From 505c19580e0f43fe5224431459cacb7c21edd93d Mon Sep 17 00:00:00 2001 From: Ondřej Surý Date: Fri, 6 Apr 2012 15:14:11 +0200 Subject: Imported Upstream version 1 --- src/Make.ccmd | 48 - src/Make.clib | 37 - src/Make.cmd | 50 - src/Make.common | 22 - src/Make.dist | 19 + src/Make.inc | 169 - src/Make.pkg | 249 - src/all-qemu.bash | 16 - src/all.bash | 7 +- src/all.bat | 21 + src/clean.bash | 25 +- src/clean.bat | 30 + src/cmd/5a/Makefile | 25 +- src/cmd/5a/a.y | 2 +- src/cmd/5a/lex.c | 16 +- src/cmd/5a/y.tab.c | 2949 +++++++ src/cmd/5a/y.tab.h | 166 + src/cmd/5c/Makefile | 33 +- src/cmd/5c/gc.h | 5 +- src/cmd/5c/swt.c | 37 +- src/cmd/5c/txt.c | 7 +- src/cmd/5g/Makefile | 35 +- src/cmd/5g/cgen.c | 68 +- src/cmd/5g/cgen64.c | 12 +- src/cmd/5g/doc.go | 2 - src/cmd/5g/galign.c | 2 + src/cmd/5g/gg.h | 21 +- src/cmd/5g/ggen.c | 9 +- src/cmd/5g/gobj.c | 63 +- src/cmd/5g/gsubr.c | 94 +- src/cmd/5g/list.c | 4 +- src/cmd/5g/peep.c | 10 +- src/cmd/5g/reg.c | 177 +- src/cmd/5l/5.out.h | 3 +- src/cmd/5l/Makefile | 42 +- src/cmd/5l/asm.c | 54 +- src/cmd/5l/doc.go | 32 +- src/cmd/5l/l.h | 1 + src/cmd/5l/list.c | 2 +- src/cmd/5l/noop.c | 3 +- src/cmd/5l/obj.c | 30 +- src/cmd/5l/pass.c | 5 +- src/cmd/5l/softfloat.c | 1 - src/cmd/5l/span.c | 2 + src/cmd/6a/Makefile | 25 +- src/cmd/6a/a.y | 6 + src/cmd/6a/lex.c | 21 +- src/cmd/6a/y.tab.c | 2730 ++++++ src/cmd/6a/y.tab.h | 135 + src/cmd/6c/Makefile | 35 +- src/cmd/6c/cgen.c | 48 +- src/cmd/6c/gc.h | 3 +- src/cmd/6c/swt.c | 31 +- src/cmd/6c/txt.c | 4 +- src/cmd/6g/Makefile | 34 +- src/cmd/6g/cgen.c | 46 +- src/cmd/6g/galign.c | 2 + src/cmd/6g/gg.h | 20 +- src/cmd/6g/ggen.c | 29 +- src/cmd/6g/gobj.c | 67 +- src/cmd/6g/gsubr.c | 99 +- src/cmd/6g/list.c | 4 +- src/cmd/6g/peep.c | 5 +- src/cmd/6g/reg.c | 190 +- src/cmd/6l/6.out.h | 5 +- src/cmd/6l/Makefile | 47 +- src/cmd/6l/asm.c | 75 +- src/cmd/6l/doc.go | 46 +- src/cmd/6l/l.h | 3 +- src/cmd/6l/obj.c | 47 +- src/cmd/6l/optab.c | 16 +- src/cmd/6l/pass.c | 25 +- src/cmd/6l/span.c | 33 +- src/cmd/8a/Makefile | 25 +- src/cmd/8a/a.y | 13 + src/cmd/8a/lex.c | 28 +- src/cmd/8a/y.tab.c | 2852 +++++++ src/cmd/8a/y.tab.h | 135 + src/cmd/8c/Makefile | 36 +- src/cmd/8c/cgen.c | 35 +- src/cmd/8c/gc.h | 3 +- src/cmd/8c/swt.c | 36 +- src/cmd/8c/txt.c | 4 +- src/cmd/8g/Makefile | 35 +- src/cmd/8g/cgen.c | 42 +- src/cmd/8g/cgen64.c | 2 + src/cmd/8g/doc.go | 2 - src/cmd/8g/galign.c | 2 + src/cmd/8g/gg.h | 24 +- src/cmd/8g/ggen.c | 26 +- src/cmd/8g/gobj.c | 66 +- src/cmd/8g/gsubr.c | 83 +- src/cmd/8g/list.c | 4 +- src/cmd/8g/opt.h | 2 + src/cmd/8g/peep.c | 5 +- src/cmd/8g/reg.c | 188 +- src/cmd/8l/8.out.h | 14 +- src/cmd/8l/Makefile | 48 +- src/cmd/8l/asm.c | 85 +- src/cmd/8l/doc.go | 43 +- src/cmd/8l/l.h | 2 + src/cmd/8l/obj.c | 52 +- src/cmd/8l/optab.c | 14 +- src/cmd/8l/pass.c | 27 +- src/cmd/8l/span.c | 39 +- src/cmd/Makefile | 69 - src/cmd/addr2line/main.c | 68 + src/cmd/api/goapi.go | 1005 +++ src/cmd/api/goapi_test.go | 75 + src/cmd/api/testdata/src/pkg/p1/golden.txt | 73 + src/cmd/api/testdata/src/pkg/p1/p1.go | 153 + src/cmd/api/testdata/src/pkg/p2/golden.txt | 5 + src/cmd/api/testdata/src/pkg/p2/p2.go | 9 + src/cmd/cc/Makefile | 36 +- src/cmd/cc/cc.h | 9 +- src/cmd/cc/com64.c | 2 +- src/cmd/cc/dcl.c | 4 + src/cmd/cc/dpchk.c | 18 +- src/cmd/cc/funct.c | 4 +- src/cmd/cc/godefs.c | 6 +- src/cmd/cc/lex.c | 2 +- src/cmd/cc/lexbody | 7 + src/cmd/cc/macbody | 4 + src/cmd/cc/pgen.c | 18 +- src/cmd/cc/pswt.c | 2 +- src/cmd/cc/sub.c | 53 +- src/cmd/cc/y.tab.c | 3811 +++++++++ src/cmd/cc/y.tab.h | 228 + src/cmd/cgo/Makefile | 15 - src/cmd/cgo/ast.go | 33 +- src/cmd/cgo/doc.go | 45 +- src/cmd/cgo/gcc.go | 391 +- src/cmd/cgo/godefs.go | 289 + src/cmd/cgo/main.go | 125 +- src/cmd/cgo/out.go | 324 +- src/cmd/cgo/util.go | 21 +- src/cmd/cov/Makefile | 39 - src/cmd/cov/doc.go | 33 - src/cmd/cov/main.c | 480 -- src/cmd/cov/tree.c | 246 - src/cmd/cov/tree.h | 47 - src/cmd/dist/README | 45 + src/cmd/dist/a.h | 149 + src/cmd/dist/arg.h | 50 + src/cmd/dist/buf.c | 279 + src/cmd/dist/build.c | 1530 ++++ src/cmd/dist/buildgc.c | 106 + src/cmd/dist/buildruntime.c | 346 + src/cmd/dist/goc2c.c | 735 ++ src/cmd/dist/main.c | 41 + src/cmd/dist/unix.c | 720 ++ src/cmd/dist/windows.c | 910 ++ src/cmd/ebnflint/Makefile | 15 - src/cmd/ebnflint/doc.go | 22 - src/cmd/ebnflint/ebnflint.go | 109 - src/cmd/fix/doc.go | 36 + 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/fix.go | 848 ++ 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/import_test.go | 458 + src/cmd/fix/iocopyn.go | 41 + src/cmd/fix/iocopyn_test.go | 37 + src/cmd/fix/main.go | 271 + src/cmd/fix/main_test.go | 129 + 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/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/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.asn1.go.in | 814 ++ src/cmd/fix/testdata/reflect.asn1.go.out | 814 ++ src/cmd/fix/testdata/reflect.datafmt.go.in | 710 ++ src/cmd/fix/testdata/reflect.datafmt.go.out | 710 ++ src/cmd/fix/testdata/reflect.decode.go.in | 905 ++ src/cmd/fix/testdata/reflect.decode.go.out | 908 ++ src/cmd/fix/testdata/reflect.decoder.go.in | 196 + src/cmd/fix/testdata/reflect.decoder.go.out | 196 + src/cmd/fix/testdata/reflect.dnsmsg.go.in | 777 ++ src/cmd/fix/testdata/reflect.dnsmsg.go.out | 777 ++ src/cmd/fix/testdata/reflect.encode.go.in | 367 + src/cmd/fix/testdata/reflect.encode.go.out | 367 + src/cmd/fix/testdata/reflect.encoder.go.in | 240 + src/cmd/fix/testdata/reflect.encoder.go.out | 240 + src/cmd/fix/testdata/reflect.export.go.in | 400 + src/cmd/fix/testdata/reflect.export.go.out | 400 + src/cmd/fix/testdata/reflect.print.go.in | 944 +++ src/cmd/fix/testdata/reflect.print.go.out | 944 +++ src/cmd/fix/testdata/reflect.quick.go.in | 364 + src/cmd/fix/testdata/reflect.quick.go.out | 365 + src/cmd/fix/testdata/reflect.read.go.in | 620 ++ src/cmd/fix/testdata/reflect.read.go.out | 620 ++ src/cmd/fix/testdata/reflect.scan.go.in | 1082 +++ src/cmd/fix/testdata/reflect.scan.go.out | 1082 +++ src/cmd/fix/testdata/reflect.script.go.in | 359 + src/cmd/fix/testdata/reflect.script.go.out | 359 + src/cmd/fix/testdata/reflect.template.go.in | 1043 +++ src/cmd/fix/testdata/reflect.template.go.out | 1044 +++ src/cmd/fix/testdata/reflect.type.go.in | 790 ++ src/cmd/fix/testdata/reflect.type.go.out | 790 ++ src/cmd/fix/timefileinfo.go | 298 + src/cmd/fix/timefileinfo_test.go | 187 + src/cmd/fix/typecheck.go | 673 ++ 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/Makefile | 72 +- src/cmd/gc/align.c | 12 +- src/cmd/gc/bisonerrors | 29 +- src/cmd/gc/bits.c | 10 +- src/cmd/gc/builtin.c | 118 + src/cmd/gc/builtin.c.boot | 114 - src/cmd/gc/closure.c | 9 +- src/cmd/gc/const.c | 123 +- src/cmd/gc/cplx.c | 7 + src/cmd/gc/dcl.c | 567 +- src/cmd/gc/doc.go | 9 +- src/cmd/gc/esc.c | 805 ++ src/cmd/gc/export.c | 345 +- src/cmd/gc/fmt.c | 1639 ++++ src/cmd/gc/gen.c | 171 +- src/cmd/gc/go.errors | 3 + src/cmd/gc/go.h | 301 +- src/cmd/gc/go.y | 448 +- src/cmd/gc/init.c | 18 +- src/cmd/gc/inl.c | 842 ++ src/cmd/gc/lex.c | 427 +- src/cmd/gc/md5.c | 2 + src/cmd/gc/mkbuiltin | 22 +- src/cmd/gc/mkbuiltin1.c | 34 +- src/cmd/gc/mkopnames | 4 +- src/cmd/gc/mparith1.c | 12 +- src/cmd/gc/mparith2.c | 81 +- src/cmd/gc/mparith3.c | 12 +- src/cmd/gc/obj.c | 51 +- src/cmd/gc/order.c | 358 + src/cmd/gc/pgen.c | 27 +- src/cmd/gc/print.c | 480 -- src/cmd/gc/range.c | 50 +- src/cmd/gc/reflect.c | 217 +- src/cmd/gc/runtime.go | 25 +- src/cmd/gc/select.c | 14 +- src/cmd/gc/sinit.c | 465 +- src/cmd/gc/subr.c | 1899 ++--- src/cmd/gc/swt.c | 63 +- src/cmd/gc/typecheck.c | 770 +- src/cmd/gc/unsafe.c | 25 +- src/cmd/gc/unsafe.go | 8 +- src/cmd/gc/walk.c | 812 +- src/cmd/gc/y.tab.c | 5540 ++++++++++++ src/cmd/gc/y.tab.h | 171 + src/cmd/gc/yerr.h | 73 + src/cmd/go/bootstrap.go | 30 + src/cmd/go/build.go | 1566 ++++ src/cmd/go/clean.go | 199 + src/cmd/go/discovery.go | 63 + src/cmd/go/doc.go | 769 ++ src/cmd/go/env.go | 89 + src/cmd/go/fix.go | 30 + src/cmd/go/fmt.go | 58 + src/cmd/go/get.go | 428 + src/cmd/go/help.go | 238 + src/cmd/go/http.go | 87 + src/cmd/go/list.go | 168 + src/cmd/go/main.go | 541 ++ src/cmd/go/match_test.go | 36 + src/cmd/go/mkdoc.sh | 8 + src/cmd/go/pkg.go | 679 ++ src/cmd/go/run.go | 85 + src/cmd/go/script | 23 + src/cmd/go/script.txt | 352 + src/cmd/go/tag_test.go | 97 + src/cmd/go/test.bash | 127 + src/cmd/go/test.go | 815 ++ src/cmd/go/testdata/errmsg/x.go | 3 + src/cmd/go/testdata/errmsg/x1_test.go | 3 + src/cmd/go/testdata/errmsg/x_test.go | 3 + src/cmd/go/testdata/local/easy.go | 7 + src/cmd/go/testdata/local/easysub/easysub.go | 7 + src/cmd/go/testdata/local/easysub/main.go | 9 + src/cmd/go/testdata/local/hard.go | 7 + src/cmd/go/testdata/local/sub/sub.go | 12 + src/cmd/go/testdata/local/sub/sub/subsub.go | 7 + src/cmd/go/testdata/src/go-cmd-test/helloworld.go | 5 + src/cmd/go/testdata/testimport/p.go | 3 + src/cmd/go/testdata/testimport/p1/p1.go | 3 + src/cmd/go/testdata/testimport/p2/p2.go | 3 + src/cmd/go/testdata/testimport/p_test.go | 13 + src/cmd/go/testdata/testimport/x_test.go | 15 + src/cmd/go/testflag.go | 235 + src/cmd/go/tool.go | 125 + src/cmd/go/vcs.go | 674 ++ src/cmd/go/version.go | 25 + src/cmd/go/vet.go | 30 + src/cmd/godefs/Makefile | 19 - src/cmd/godefs/a.h | 104 - src/cmd/godefs/doc.go | 99 - src/cmd/godefs/main.c | 606 -- src/cmd/godefs/stabs.c | 456 - src/cmd/godefs/test.sh | 45 - src/cmd/godefs/testdata.c | 41 - src/cmd/godefs/testdata_darwin_386.golden | 31 - src/cmd/godefs/testdata_darwin_amd64.golden | 31 - src/cmd/godefs/util.c | 36 - src/cmd/godoc/Makefile | 24 - src/cmd/godoc/README.godoc-app | 80 + src/cmd/godoc/appconfig.go | 19 - src/cmd/godoc/appinit.go | 74 +- src/cmd/godoc/codewalk.go | 74 +- src/cmd/godoc/dirtrees.go | 133 +- src/cmd/godoc/doc.go | 83 +- src/cmd/godoc/filesystem.go | 560 +- src/cmd/godoc/format.go | 4 +- src/cmd/godoc/godoc.go | 1042 ++- src/cmd/godoc/httpzip.go | 181 - src/cmd/godoc/index.go | 403 +- src/cmd/godoc/main.go | 328 +- src/cmd/godoc/mapping.go | 200 - src/cmd/godoc/parser.go | 28 +- src/cmd/godoc/setup-godoc-app.bash | 121 + src/cmd/godoc/snippet.go | 18 +- src/cmd/godoc/spec.go | 135 +- src/cmd/godoc/template.go | 182 + src/cmd/godoc/throttle.go | 88 + src/cmd/godoc/utils.go | 87 +- src/cmd/godoc/zip.go | 87 +- src/cmd/gofix/Makefile | 34 - src/cmd/gofix/doc.go | 36 - src/cmd/gofix/filepath.go | 53 - src/cmd/gofix/filepath_test.go | 33 - src/cmd/gofix/fix.go | 582 -- src/cmd/gofix/httpfinalurl.go | 56 - src/cmd/gofix/httpfinalurl_test.go | 37 - src/cmd/gofix/httpfs.go | 63 - src/cmd/gofix/httpfs_test.go | 47 - src/cmd/gofix/httpheaders.go | 66 - src/cmd/gofix/httpheaders_test.go | 73 - src/cmd/gofix/httpserver.go | 140 - src/cmd/gofix/httpserver_test.go | 53 - src/cmd/gofix/main.go | 258 - src/cmd/gofix/main_test.go | 126 - src/cmd/gofix/netdial.go | 114 - src/cmd/gofix/netdial_test.go | 51 - src/cmd/gofix/oserrorstring.go | 74 - src/cmd/gofix/oserrorstring_test.go | 57 - src/cmd/gofix/osopen.go | 123 - src/cmd/gofix/osopen_test.go | 82 - src/cmd/gofix/procattr.go | 61 - src/cmd/gofix/procattr_test.go | 74 - src/cmd/gofix/reflect.go | 861 -- src/cmd/gofix/reflect_test.go | 31 - src/cmd/gofix/signal.go | 49 - src/cmd/gofix/signal_test.go | 94 - src/cmd/gofix/sorthelpers.go | 46 - src/cmd/gofix/sorthelpers_test.go | 45 - src/cmd/gofix/sortslice.go | 49 - src/cmd/gofix/sortslice_test.go | 35 - src/cmd/gofix/stringssplit.go | 71 - src/cmd/gofix/stringssplit_test.go | 51 - src/cmd/gofix/testdata/reflect.asn1.go.in | 814 -- src/cmd/gofix/testdata/reflect.asn1.go.out | 814 -- src/cmd/gofix/testdata/reflect.datafmt.go.in | 710 -- src/cmd/gofix/testdata/reflect.datafmt.go.out | 710 -- src/cmd/gofix/testdata/reflect.decode.go.in | 905 -- src/cmd/gofix/testdata/reflect.decode.go.out | 908 -- src/cmd/gofix/testdata/reflect.decoder.go.in | 196 - src/cmd/gofix/testdata/reflect.decoder.go.out | 196 - src/cmd/gofix/testdata/reflect.dnsmsg.go.in | 777 -- src/cmd/gofix/testdata/reflect.dnsmsg.go.out | 777 -- src/cmd/gofix/testdata/reflect.encode.go.in | 367 - src/cmd/gofix/testdata/reflect.encode.go.out | 367 - src/cmd/gofix/testdata/reflect.encoder.go.in | 240 - src/cmd/gofix/testdata/reflect.encoder.go.out | 240 - src/cmd/gofix/testdata/reflect.export.go.in | 400 - src/cmd/gofix/testdata/reflect.export.go.out | 400 - src/cmd/gofix/testdata/reflect.print.go.in | 944 --- src/cmd/gofix/testdata/reflect.print.go.out | 944 --- src/cmd/gofix/testdata/reflect.quick.go.in | 364 - src/cmd/gofix/testdata/reflect.quick.go.out | 365 - src/cmd/gofix/testdata/reflect.read.go.in | 620 -- src/cmd/gofix/testdata/reflect.read.go.out | 620 -- src/cmd/gofix/testdata/reflect.scan.go.in | 1082 --- src/cmd/gofix/testdata/reflect.scan.go.out | 1082 --- src/cmd/gofix/testdata/reflect.script.go.in | 359 - src/cmd/gofix/testdata/reflect.script.go.out | 359 - src/cmd/gofix/testdata/reflect.template.go.in | 1043 --- src/cmd/gofix/testdata/reflect.template.go.out | 1044 --- src/cmd/gofix/testdata/reflect.type.go.in | 789 -- src/cmd/gofix/testdata/reflect.type.go.out | 789 -- src/cmd/gofix/typecheck.go | 584 -- src/cmd/gofix/url.go | 118 - src/cmd/gofix/url_test.go | 159 - src/cmd/gofmt/Makefile | 19 - src/cmd/gofmt/doc.go | 12 +- src/cmd/gofmt/gofmt.go | 194 +- src/cmd/gofmt/gofmt_test.go | 50 +- src/cmd/gofmt/long_test.go | 159 + src/cmd/gofmt/rewrite.go | 13 +- src/cmd/gofmt/simplify.go | 18 +- src/cmd/gofmt/test.sh | 162 - src/cmd/gofmt/testdata/comments.golden | 9 + src/cmd/gofmt/testdata/comments.input | 9 + src/cmd/gofmt/testdata/composites.golden | 98 + src/cmd/gofmt/testdata/composites.input | 98 + src/cmd/gofmt/testdata/import.golden | 108 + src/cmd/gofmt/testdata/import.input | 108 + src/cmd/gofmt/testdata/old.golden | 9 + src/cmd/gofmt/testdata/old.input | 8 + src/cmd/gofmt/testdata/rewrite3.golden | 12 + src/cmd/gofmt/testdata/rewrite3.input | 12 + src/cmd/gofmt/testdata/rewrite4.golden | 74 + src/cmd/gofmt/testdata/rewrite4.input | 74 + src/cmd/gofmt/testdata/stdin1.golden | 3 + src/cmd/gofmt/testdata/stdin1.golden.gofmt | 3 + src/cmd/gofmt/testdata/stdin1.input | 3 + src/cmd/gofmt/testdata/stdin1.input.gofmt | 3 + src/cmd/gofmt/testdata/stdin2.golden | 11 + src/cmd/gofmt/testdata/stdin2.golden.gofmt | 10 + src/cmd/gofmt/testdata/stdin2.input | 11 + src/cmd/gofmt/testdata/stdin2.input.gofmt | 11 + src/cmd/gofmt/testdata/stdin3.golden | 6 + src/cmd/gofmt/testdata/stdin3.golden.gofmt | 7 + src/cmd/gofmt/testdata/stdin3.input | 4 + src/cmd/gofmt/testdata/stdin3.input.gofmt | 7 + src/cmd/gofmt/testdata/stdin4.golden | 3 + src/cmd/gofmt/testdata/stdin4.golden.gofmt | 3 + src/cmd/gofmt/testdata/stdin4.input | 3 + src/cmd/gofmt/testdata/stdin4.input.gofmt | 3 + src/cmd/goinstall/Makefile | 19 - src/cmd/goinstall/doc.go | 196 - src/cmd/goinstall/download.go | 353 - src/cmd/goinstall/main.go | 334 - src/cmd/goinstall/make.go | 175 - src/cmd/goinstall/tag_test.go | 73 - src/cmd/gomake/doc.go | 36 - src/cmd/gopack/Makefile | 12 - src/cmd/gopack/ar.c | 1717 ---- src/cmd/gopack/doc.go | 26 - src/cmd/gotest/Makefile | 12 - src/cmd/gotest/doc.go | 113 - src/cmd/gotest/flag.go | 159 - src/cmd/gotest/gotest.go | 435 - src/cmd/gotry/Makefile | 18 - src/cmd/gotry/gotry | 168 - src/cmd/gotype/Makefile | 17 - src/cmd/gotype/doc.go | 61 - src/cmd/gotype/gotype.go | 192 - src/cmd/gotype/gotype_test.go | 49 - src/cmd/gotype/testdata/test1.go | 6 - src/cmd/govet/Makefile | 14 - src/cmd/govet/doc.go | 38 - src/cmd/govet/govet.go | 404 - src/cmd/goyacc/Makefile | 17 - src/cmd/goyacc/doc.go | 46 - src/cmd/goyacc/goyacc.go | 3311 -------- src/cmd/goyacc/units.txt | 576 -- src/cmd/goyacc/units.y | 765 -- src/cmd/hgpatch/Makefile | 11 - src/cmd/hgpatch/doc.go | 18 - src/cmd/hgpatch/main.go | 358 - src/cmd/ld/data.c | 147 +- src/cmd/ld/doc.go | 53 +- src/cmd/ld/dwarf.c | 8 +- src/cmd/ld/elf.c | 85 +- src/cmd/ld/elf.h | 6 +- src/cmd/ld/go.c | 8 +- src/cmd/ld/ldelf.c | 1 + src/cmd/ld/ldpe.c | 26 +- src/cmd/ld/lib.c | 95 +- src/cmd/ld/lib.h | 8 +- src/cmd/ld/macho.c | 45 +- src/cmd/ld/pe.c | 104 +- src/cmd/ld/symtab.c | 17 +- src/cmd/nm/Makefile | 14 +- src/cmd/nm/doc.go | 4 +- src/cmd/objdump/main.c | 68 + src/cmd/pack/Makefile | 5 + src/cmd/pack/ar.c | 1768 ++++ src/cmd/pack/doc.go | 27 + src/cmd/prof/Makefile | 38 - src/cmd/prof/doc.go | 48 - src/cmd/prof/gopprof | 4975 ----------- src/cmd/prof/main.c | 895 -- src/cmd/vet/Makefile | 7 + src/cmd/vet/doc.go | 62 + src/cmd/vet/main.go | 239 + src/cmd/vet/method.go | 161 + src/cmd/vet/print.go | 278 + src/cmd/vet/structtag.go | 34 + src/cmd/vet/taglit.go | 121 + src/cmd/yacc/Makefile | 10 + src/cmd/yacc/doc.go | 51 + src/cmd/yacc/units.txt | 576 ++ src/cmd/yacc/units.y | 763 ++ src/cmd/yacc/yacc.go | 3324 ++++++++ src/env.bash | 106 - src/lib9/Makefile | 120 +- src/lib9/await.c | 2 + src/lib9/ctime.c | 28 + src/lib9/exitcode.c | 1 + src/lib9/fmt/errfmt.c | 30 - src/lib9/fmt/fmtprint.c | 26 +- src/lib9/fmt/fmtvprint.c | 28 +- src/lib9/fork.c | 46 - src/lib9/getuser.c | 2 + src/lib9/jmp.c | 2 + src/lib9/notify.c | 2 + src/lib9/rfork.c | 2 + src/lib9/utf/Makefile | 14 +- src/lib9/utf/mkrunetype.c | 2 + src/lib9/utf/runetype.c | 2 +- src/lib9/utf/runetypebody-5.0.0.c | 1361 --- src/lib9/utf/runetypebody-5.2.0.c | 1541 ---- src/lib9/utf/runetypebody-6.0.0.c | 1565 ---- src/lib9/utf/runetypebody-6.0.0.h | 1565 ++++ src/lib9/win32.c | 26 - src/lib9/windows.c | 26 + src/libbio/Makefile | 54 +- src/libmach/5db.c | 2 +- src/libmach/8db.c | 36 +- src/libmach/Makefile | 67 +- src/libmach/executable.c | 203 +- src/libmach/fakeobj.c | 28 +- src/libmach/linux.c | 4 + src/libmach/machdata.c | 10 +- src/libmach/map.c | 2 + src/libmach/netbsd.c | 46 + src/libmach/plan9.c | 72 + src/libmach/sym.c | 24 +- src/make.bash | 193 +- src/make.bat | 111 + src/pkg/Makefile | 311 - src/pkg/archive/tar/Makefile | 13 - src/pkg/archive/tar/common.go | 52 +- src/pkg/archive/tar/reader.go | 36 +- src/pkg/archive/tar/reader_test.go | 109 +- src/pkg/archive/tar/testdata/writer.tar | Bin 3072 -> 3584 bytes src/pkg/archive/tar/writer.go | 59 +- src/pkg/archive/tar/writer_test.go | 45 +- src/pkg/archive/zip/Makefile | 13 - src/pkg/archive/zip/example_test.go | 75 + src/pkg/archive/zip/reader.go | 252 +- src/pkg/archive/zip/reader_test.go | 339 +- src/pkg/archive/zip/struct.go | 230 +- .../archive/zip/testdata/crc32-not-streamed.zip | Bin 0 -> 314 bytes .../archive/zip/testdata/go-no-datadesc-sig.zip | Bin 0 -> 330 bytes .../archive/zip/testdata/go-with-datadesc-sig.zip | Bin 0 -> 242 bytes src/pkg/archive/zip/testdata/r.zip | Bin 440 -> 0 bytes src/pkg/archive/zip/testdata/symlink.zip | Bin 0 -> 173 bytes src/pkg/archive/zip/testdata/unix.zip | Bin 0 -> 620 bytes src/pkg/archive/zip/testdata/winxp.zip | Bin 0 -> 412 bytes src/pkg/archive/zip/writer.go | 202 +- src/pkg/archive/zip/writer_test.go | 82 +- src/pkg/archive/zip/zip_test.go | 50 +- src/pkg/asn1/Makefile | 13 - src/pkg/asn1/asn1.go | 840 -- src/pkg/asn1/asn1_test.go | 689 -- src/pkg/asn1/common.go | 158 - src/pkg/asn1/marshal.go | 544 -- src/pkg/asn1/marshal_test.go | 129 - src/pkg/big/Makefile | 18 - src/pkg/big/arith.go | 231 - src/pkg/big/arith_386.s | 265 - src/pkg/big/arith_amd64.s | 263 - src/pkg/big/arith_arm.s | 312 - src/pkg/big/arith_decl.go | 18 - src/pkg/big/arith_test.go | 335 - src/pkg/big/calibrate_test.go | 88 - src/pkg/big/hilbert_test.go | 160 - src/pkg/big/int.go | 868 -- src/pkg/big/int_test.go | 1391 --- src/pkg/big/nat.go | 1272 --- src/pkg/big/nat_test.go | 669 -- src/pkg/big/rat.go | 371 - src/pkg/big/rat_test.go | 332 - src/pkg/bufio/Makefile | 11 - src/pkg/bufio/bufio.go | 191 +- src/pkg/bufio/bufio_test.go | 267 +- src/pkg/builtin/builtin.go | 104 +- src/pkg/bytes/Makefile | 16 - src/pkg/bytes/asm_386.s | 16 + src/pkg/bytes/asm_amd64.s | 16 + src/pkg/bytes/asm_arm.s | 3 + src/pkg/bytes/buffer.go | 140 +- src/pkg/bytes/buffer_test.go | 36 +- src/pkg/bytes/bytes.go | 249 +- src/pkg/bytes/bytes_test.go | 342 +- src/pkg/bytes/example_test.go | 28 + src/pkg/bytes/export_test.go | 1 + src/pkg/bytes/reader.go | 125 + src/pkg/bytes/reader_test.go | 88 + src/pkg/cmath/Makefile | 25 - src/pkg/cmath/abs.go | 12 - src/pkg/cmath/asin.go | 170 - src/pkg/cmath/cmath_test.go | 853 -- src/pkg/cmath/conj.go | 8 - src/pkg/cmath/exp.go | 55 - src/pkg/cmath/isinf.go | 21 - src/pkg/cmath/isnan.go | 25 - src/pkg/cmath/log.go | 64 - src/pkg/cmath/phase.go | 11 - src/pkg/cmath/polar.go | 12 - src/pkg/cmath/pow.go | 60 - src/pkg/cmath/rect.go | 13 - src/pkg/cmath/sin.go | 132 - src/pkg/cmath/sqrt.go | 103 - src/pkg/cmath/tan.go | 184 - src/pkg/compress/bzip2/Makefile | 14 - src/pkg/compress/bzip2/bit_reader.go | 15 +- src/pkg/compress/bzip2/bzip2.go | 23 +- src/pkg/compress/bzip2/bzip2_test.go | 3 +- src/pkg/compress/bzip2/huffman.go | 9 +- src/pkg/compress/flate/Makefile | 17 - src/pkg/compress/flate/deflate.go | 165 +- src/pkg/compress/flate/deflate_test.go | 173 +- src/pkg/compress/flate/flate_test.go | 12 +- src/pkg/compress/flate/huffman_bit_writer.go | 42 +- src/pkg/compress/flate/huffman_code.go | 59 +- src/pkg/compress/flate/inflate.go | 43 +- src/pkg/compress/flate/util.go | 72 - src/pkg/compress/gzip/Makefile | 12 - src/pkg/compress/gzip/gunzip.go | 89 +- src/pkg/compress/gzip/gunzip_test.go | 9 +- src/pkg/compress/gzip/gzip.go | 100 +- src/pkg/compress/gzip/gzip_test.go | 207 +- src/pkg/compress/lzw/Makefile | 12 - src/pkg/compress/lzw/reader.go | 28 +- src/pkg/compress/lzw/reader_test.go | 9 +- src/pkg/compress/lzw/writer.go | 48 +- src/pkg/compress/lzw/writer_test.go | 8 +- .../compress/testdata/Mark.Twain-Tom.Sawyer.txt | 8858 ++++++++++++++++++++ src/pkg/compress/zlib/Makefile | 12 - src/pkg/compress/zlib/reader.go | 31 +- src/pkg/compress/zlib/reader_test.go | 9 +- src/pkg/compress/zlib/writer.go | 111 +- src/pkg/compress/zlib/writer_test.go | 12 +- src/pkg/container/heap/Makefile | 11 - src/pkg/container/heap/example_test.go | 105 + src/pkg/container/heap/heap.go | 18 +- src/pkg/container/heap/heap_test.go | 34 +- src/pkg/container/list/Makefile | 11 - src/pkg/container/ring/Makefile | 11 - src/pkg/container/vector/Makefile | 69 - src/pkg/container/vector/defs.go | 43 - src/pkg/container/vector/intvector.go | 188 - src/pkg/container/vector/intvector_test.go | 331 - src/pkg/container/vector/nogen_test.go | 67 - src/pkg/container/vector/numbers_test.go | 123 - src/pkg/container/vector/stringvector.go | 188 - src/pkg/container/vector/stringvector_test.go | 331 - src/pkg/container/vector/vector.go | 188 - src/pkg/container/vector/vector_test.go | 331 - src/pkg/crypto/Makefile | 11 - src/pkg/crypto/aes/Makefile | 13 - src/pkg/crypto/aes/aes_test.go | 16 + src/pkg/crypto/aes/block.go | 18 +- src/pkg/crypto/aes/cipher.go | 41 +- src/pkg/crypto/aes/const.go | 544 +- src/pkg/crypto/blowfish/Makefile | 13 - src/pkg/crypto/blowfish/block.go | 101 - src/pkg/crypto/blowfish/blowfish_test.go | 192 - src/pkg/crypto/blowfish/cipher.go | 79 - src/pkg/crypto/blowfish/const.go | 199 - src/pkg/crypto/cast5/Makefile | 11 - src/pkg/crypto/cast5/cast5.go | 536 -- src/pkg/crypto/cast5/cast5_test.go | 104 - src/pkg/crypto/cipher/Makefile | 17 - src/pkg/crypto/cipher/cbc.go | 2 +- src/pkg/crypto/cipher/cbc_aes_test.go | 7 +- src/pkg/crypto/cipher/cfb_test.go | 7 +- src/pkg/crypto/cipher/common_test.go | 2 +- src/pkg/crypto/cipher/ctr_aes_test.go | 7 +- src/pkg/crypto/cipher/io.go | 17 +- src/pkg/crypto/cipher/ocfb.go | 138 - src/pkg/crypto/cipher/ocfb_test.go | 44 - src/pkg/crypto/cipher/ofb_test.go | 7 +- src/pkg/crypto/crypto.go | 30 +- src/pkg/crypto/des/Makefile | 13 - src/pkg/crypto/des/block.go | 2 +- src/pkg/crypto/des/cipher.go | 68 +- src/pkg/crypto/des/des_test.go | 16 +- src/pkg/crypto/dsa/Makefile | 11 - src/pkg/crypto/dsa/dsa.go | 54 +- src/pkg/crypto/dsa/dsa_test.go | 2 +- src/pkg/crypto/ecdsa/Makefile | 11 - src/pkg/crypto/ecdsa/ecdsa.go | 36 +- src/pkg/crypto/ecdsa/ecdsa_test.go | 10 +- src/pkg/crypto/elliptic/Makefile | 11 - src/pkg/crypto/elliptic/elliptic.go | 110 +- src/pkg/crypto/elliptic/elliptic_test.go | 41 +- src/pkg/crypto/elliptic/p224.go | 718 ++ src/pkg/crypto/elliptic/p224_test.go | 47 + src/pkg/crypto/hmac/Makefile | 11 - src/pkg/crypto/hmac/hmac.go | 58 +- src/pkg/crypto/hmac/hmac_test.go | 316 +- src/pkg/crypto/md4/Makefile | 12 - src/pkg/crypto/md4/md4.go | 117 - src/pkg/crypto/md4/md4_test.go | 71 - src/pkg/crypto/md4/md4block.go | 89 - src/pkg/crypto/md5/Makefile | 12 - src/pkg/crypto/md5/md5.go | 30 +- src/pkg/crypto/md5/md5_test.go | 17 +- src/pkg/crypto/ocsp/Makefile | 11 - src/pkg/crypto/ocsp/ocsp.go | 192 - src/pkg/crypto/ocsp/ocsp_test.go | 97 - src/pkg/crypto/openpgp/Makefile | 14 - src/pkg/crypto/openpgp/armor/Makefile | 12 - src/pkg/crypto/openpgp/armor/armor.go | 220 - src/pkg/crypto/openpgp/armor/armor_test.go | 95 - src/pkg/crypto/openpgp/armor/encode.go | 161 - src/pkg/crypto/openpgp/canonical_text.go | 58 - src/pkg/crypto/openpgp/canonical_text_test.go | 49 - src/pkg/crypto/openpgp/elgamal/Makefile | 11 - src/pkg/crypto/openpgp/elgamal/elgamal.go | 122 - src/pkg/crypto/openpgp/elgamal/elgamal_test.go | 49 - src/pkg/crypto/openpgp/error/Makefile | 11 - src/pkg/crypto/openpgp/error/error.go | 64 - src/pkg/crypto/openpgp/keys.go | 545 -- src/pkg/crypto/openpgp/packet/Makefile | 22 - src/pkg/crypto/openpgp/packet/compressed.go | 39 - src/pkg/crypto/openpgp/packet/compressed_test.go | 41 - src/pkg/crypto/openpgp/packet/encrypted_key.go | 168 - .../crypto/openpgp/packet/encrypted_key_test.go | 126 - src/pkg/crypto/openpgp/packet/literal.go | 90 - .../crypto/openpgp/packet/one_pass_signature.go | 74 - src/pkg/crypto/openpgp/packet/packet.go | 483 -- src/pkg/crypto/openpgp/packet/packet_test.go | 256 - src/pkg/crypto/openpgp/packet/private_key.go | 301 - src/pkg/crypto/openpgp/packet/private_key_test.go | 57 - src/pkg/crypto/openpgp/packet/public_key.go | 393 - src/pkg/crypto/openpgp/packet/public_key_test.go | 98 - src/pkg/crypto/openpgp/packet/reader.go | 63 - src/pkg/crypto/openpgp/packet/signature.go | 558 -- src/pkg/crypto/openpgp/packet/signature_test.go | 42 - .../openpgp/packet/symmetric_key_encrypted.go | 162 - .../openpgp/packet/symmetric_key_encrypted_test.go | 101 - .../openpgp/packet/symmetrically_encrypted.go | 291 - .../openpgp/packet/symmetrically_encrypted_test.go | 124 - src/pkg/crypto/openpgp/packet/userid.go | 161 - src/pkg/crypto/openpgp/packet/userid_test.go | 87 - src/pkg/crypto/openpgp/read.go | 415 - src/pkg/crypto/openpgp/read_test.go | 361 - src/pkg/crypto/openpgp/s2k/Makefile | 11 - src/pkg/crypto/openpgp/s2k/s2k.go | 180 - src/pkg/crypto/openpgp/s2k/s2k_test.go | 118 - src/pkg/crypto/openpgp/write.go | 308 - src/pkg/crypto/openpgp/write_test.go | 233 - src/pkg/crypto/rand/Makefile | 30 - src/pkg/crypto/rand/rand.go | 7 +- src/pkg/crypto/rand/rand_test.go | 2 +- src/pkg/crypto/rand/rand_unix.go | 11 +- src/pkg/crypto/rand/rand_windows.go | 14 +- src/pkg/crypto/rand/util.go | 12 +- src/pkg/crypto/rc4/Makefile | 11 - src/pkg/crypto/rc4/rc4.go | 9 +- src/pkg/crypto/ripemd160/Makefile | 12 - src/pkg/crypto/ripemd160/ripemd160.go | 118 - src/pkg/crypto/ripemd160/ripemd160_test.go | 64 - src/pkg/crypto/ripemd160/ripemd160block.go | 161 - src/pkg/crypto/rsa/Makefile | 12 - src/pkg/crypto/rsa/pkcs1v15.go | 40 +- src/pkg/crypto/rsa/pkcs1v15_test.go | 6 +- src/pkg/crypto/rsa/rsa.go | 86 +- src/pkg/crypto/rsa/rsa_test.go | 2 +- src/pkg/crypto/sha1/Makefile | 12 - src/pkg/crypto/sha1/sha1.go | 30 +- src/pkg/crypto/sha1/sha1_test.go | 16 +- src/pkg/crypto/sha256/Makefile | 12 - src/pkg/crypto/sha256/sha256.go | 38 +- src/pkg/crypto/sha256/sha256_test.go | 8 +- src/pkg/crypto/sha512/Makefile | 12 - src/pkg/crypto/sha512/sha512.go | 43 +- src/pkg/crypto/sha512/sha512_test.go | 8 +- src/pkg/crypto/subtle/Makefile | 11 - src/pkg/crypto/tls/Makefile | 20 - src/pkg/crypto/tls/alert.go | 4 + src/pkg/crypto/tls/cipher_suites.go | 128 +- src/pkg/crypto/tls/common.go | 180 +- src/pkg/crypto/tls/conn.go | 191 +- src/pkg/crypto/tls/conn_test.go | 54 + src/pkg/crypto/tls/generate_cert.go | 12 +- src/pkg/crypto/tls/handshake_client.go | 136 +- src/pkg/crypto/tls/handshake_client_test.go | 3 +- src/pkg/crypto/tls/handshake_messages.go | 207 +- src/pkg/crypto/tls/handshake_messages_test.go | 19 +- src/pkg/crypto/tls/handshake_server.go | 157 +- src/pkg/crypto/tls/handshake_server_test.go | 1165 ++- src/pkg/crypto/tls/key_agreement.go | 82 +- src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py | 2 + src/pkg/crypto/tls/prf.go | 124 +- src/pkg/crypto/tls/prf_test.go | 21 +- src/pkg/crypto/tls/root_test.go | 61 + src/pkg/crypto/tls/tls.go | 70 +- src/pkg/crypto/twofish/Makefile | 11 - src/pkg/crypto/twofish/twofish.go | 358 - src/pkg/crypto/twofish/twofish_test.go | 129 - src/pkg/crypto/x509/Makefile | 13 - src/pkg/crypto/x509/cert_pool.go | 33 +- src/pkg/crypto/x509/pkcs1.go | 122 + src/pkg/crypto/x509/pkcs8.go | 42 + src/pkg/crypto/x509/pkcs8_test.go | 20 + src/pkg/crypto/x509/pkix/Makefile | 11 - src/pkg/crypto/x509/pkix/pkix.go | 22 +- src/pkg/crypto/x509/root.go | 17 + src/pkg/crypto/x509/root_darwin.go | 80 + src/pkg/crypto/x509/root_stub.go | 15 + src/pkg/crypto/x509/root_unix.go | 35 + src/pkg/crypto/x509/root_windows.go | 226 + src/pkg/crypto/x509/verify.go | 104 +- src/pkg/crypto/x509/verify_test.go | 84 +- src/pkg/crypto/x509/x509.go | 307 +- src/pkg/crypto/x509/x509_test.go | 62 +- src/pkg/crypto/xtea/Makefile | 12 - src/pkg/crypto/xtea/block.go | 66 - src/pkg/crypto/xtea/cipher.go | 92 - src/pkg/crypto/xtea/xtea_test.go | 246 - src/pkg/csv/Makefile | 12 - src/pkg/csv/reader.go | 372 - src/pkg/csv/reader_test.go | 265 - src/pkg/csv/writer.go | 122 - src/pkg/csv/writer_test.go | 44 - src/pkg/database/sql/convert.go | 158 + src/pkg/database/sql/convert_test.go | 250 + src/pkg/database/sql/doc.txt | 46 + src/pkg/database/sql/driver/driver.go | 215 + src/pkg/database/sql/driver/types.go | 252 + src/pkg/database/sql/driver/types_test.go | 65 + src/pkg/database/sql/fakedb_test.go | 629 ++ src/pkg/database/sql/sql.go | 1063 +++ src/pkg/database/sql/sql_test.go | 614 ++ src/pkg/debug/dwarf/Makefile | 16 - src/pkg/debug/dwarf/buf.go | 9 +- src/pkg/debug/dwarf/const.go | 4 +- src/pkg/debug/dwarf/entry.go | 10 +- src/pkg/debug/dwarf/open.go | 12 +- src/pkg/debug/dwarf/testdata/typedef.c | 8 +- src/pkg/debug/dwarf/testdata/typedef.elf | Bin 10837 -> 12448 bytes src/pkg/debug/dwarf/testdata/typedef.macho | Bin 5256 -> 5024 bytes src/pkg/debug/dwarf/type.go | 55 +- src/pkg/debug/dwarf/type_test.go | 19 +- src/pkg/debug/dwarf/unit.go | 7 +- src/pkg/debug/elf/Makefile | 12 - src/pkg/debug/elf/elf.go | 8 +- src/pkg/debug/elf/file.go | 57 +- src/pkg/debug/elf/file_test.go | 3 + src/pkg/debug/gosym/Makefile | 19 - src/pkg/debug/gosym/pclinetest.asm | 58 + src/pkg/debug/gosym/pclinetest.h | 2 + src/pkg/debug/gosym/pclinetest.s | 58 - src/pkg/debug/gosym/pclntab_test.go | 36 +- src/pkg/debug/gosym/symtab.go | 19 +- src/pkg/debug/macho/Makefile | 12 - src/pkg/debug/macho/file.go | 28 +- src/pkg/debug/macho/file_test.go | 80 +- src/pkg/debug/macho/macho.go | 6 +- src/pkg/debug/pe/Makefile | 12 - src/pkg/debug/pe/file.go | 23 +- src/pkg/debug/pe/file_test.go | 54 +- src/pkg/deps.bash | 49 - src/pkg/ebnf/Makefile | 12 - src/pkg/ebnf/ebnf.go | 245 - src/pkg/ebnf/ebnf_test.go | 87 - src/pkg/ebnf/parser.go | 197 - src/pkg/encoding/ascii85/Makefile | 11 - src/pkg/encoding/ascii85/ascii85.go | 19 +- src/pkg/encoding/ascii85/ascii85_test.go | 12 +- src/pkg/encoding/asn1/asn1.go | 846 ++ src/pkg/encoding/asn1/asn1_test.go | 706 ++ src/pkg/encoding/asn1/common.go | 161 + src/pkg/encoding/asn1/marshal.go | 557 ++ src/pkg/encoding/asn1/marshal_test.go | 139 + src/pkg/encoding/base32/Makefile | 11 - src/pkg/encoding/base32/base32.go | 78 +- src/pkg/encoding/base32/base32_test.go | 51 +- src/pkg/encoding/base64/Makefile | 11 - src/pkg/encoding/base64/base64.go | 60 +- src/pkg/encoding/base64/base64_test.go | 44 +- src/pkg/encoding/binary/Makefile | 11 - src/pkg/encoding/binary/binary.go | 71 +- src/pkg/encoding/binary/binary_test.go | 65 +- src/pkg/encoding/binary/example_test.go | 52 + src/pkg/encoding/binary/varint.go | 134 + src/pkg/encoding/binary/varint_test.go | 168 + src/pkg/encoding/csv/reader.go | 376 + src/pkg/encoding/csv/reader_test.go | 281 + src/pkg/encoding/csv/writer.go | 121 + src/pkg/encoding/csv/writer_test.go | 44 + src/pkg/encoding/git85/Makefile | 11 - src/pkg/encoding/git85/git.go | 277 - src/pkg/encoding/git85/git_test.go | 194 - src/pkg/encoding/gob/codec_test.go | 1471 ++++ src/pkg/encoding/gob/debug.go | 695 ++ src/pkg/encoding/gob/decode.go | 1300 +++ src/pkg/encoding/gob/decoder.go | 216 + src/pkg/encoding/gob/doc.go | 366 + src/pkg/encoding/gob/dump.go | 29 + src/pkg/encoding/gob/encode.go | 731 ++ src/pkg/encoding/gob/encoder.go | 253 + src/pkg/encoding/gob/encoder_test.go | 738 ++ src/pkg/encoding/gob/error.go | 43 + src/pkg/encoding/gob/gobencdec_test.go | 594 ++ src/pkg/encoding/gob/timing_test.go | 96 + src/pkg/encoding/gob/type.go | 808 ++ src/pkg/encoding/gob/type_test.go | 161 + src/pkg/encoding/hex/Makefile | 11 - src/pkg/encoding/hex/hex.go | 39 +- src/pkg/encoding/hex/hex_test.go | 171 +- src/pkg/encoding/json/bench_test.go | 157 + src/pkg/encoding/json/decode.go | 979 +++ src/pkg/encoding/json/decode_test.go | 640 ++ src/pkg/encoding/json/encode.go | 551 ++ src/pkg/encoding/json/encode_test.go | 188 + src/pkg/encoding/json/example_test.go | 83 + src/pkg/encoding/json/indent.go | 127 + src/pkg/encoding/json/scanner.go | 623 ++ src/pkg/encoding/json/scanner_test.go | 303 + src/pkg/encoding/json/stream.go | 188 + src/pkg/encoding/json/stream_test.go | 147 + src/pkg/encoding/json/tagkey_test.go | 100 + src/pkg/encoding/json/tags.go | 44 + src/pkg/encoding/json/tags_test.go | 28 + src/pkg/encoding/json/testdata/code.json.gz | Bin 0 -> 120432 bytes src/pkg/encoding/pem/Makefile | 11 - src/pkg/encoding/pem/pem.go | 11 +- src/pkg/encoding/pem/pem_test.go | 4 +- src/pkg/encoding/xml/atom_test.go | 56 + src/pkg/encoding/xml/example_test.go | 111 + src/pkg/encoding/xml/marshal.go | 447 + src/pkg/encoding/xml/marshal_test.go | 793 ++ src/pkg/encoding/xml/read.go | 531 ++ src/pkg/encoding/xml/read_test.go | 357 + src/pkg/encoding/xml/typeinfo.go | 329 + src/pkg/encoding/xml/xml.go | 1697 ++++ src/pkg/encoding/xml/xml_test.go | 578 ++ src/pkg/errors/errors.go | 20 + src/pkg/errors/errors_test.go | 53 + src/pkg/errors/example_test.go | 34 + src/pkg/exec/Makefile | 31 - src/pkg/exec/exec.go | 377 - src/pkg/exec/exec_test.go | 214 - src/pkg/exec/lp_plan9.go | 51 - src/pkg/exec/lp_test.go | 33 - src/pkg/exec/lp_unix.go | 52 - src/pkg/exec/lp_windows.go | 77 - src/pkg/exp/README | 3 - src/pkg/exp/gui/Makefile | 11 - src/pkg/exp/gui/gui.go | 58 - src/pkg/exp/gui/x11/Makefile | 12 - src/pkg/exp/gui/x11/auth.go | 93 - src/pkg/exp/gui/x11/conn.go | 627 -- src/pkg/exp/norm/Makefile | 44 - src/pkg/exp/norm/composition.go | 344 - src/pkg/exp/norm/composition_test.go | 138 - src/pkg/exp/norm/forminfo.go | 188 - src/pkg/exp/norm/maketables.go | 855 -- src/pkg/exp/norm/maketesttables.go | 42 - src/pkg/exp/norm/norm_test.go | 14 - src/pkg/exp/norm/normalize.go | 99 - src/pkg/exp/norm/tables.go | 6580 --------------- src/pkg/exp/norm/trie.go | 234 - src/pkg/exp/norm/trie_test.go | 107 - src/pkg/exp/norm/triedata_test.go | 63 - src/pkg/exp/norm/triegen.go | 211 - src/pkg/exp/regexp/Makefile | 12 - src/pkg/exp/regexp/all_test.go | 429 - src/pkg/exp/regexp/exec.go | 295 - src/pkg/exp/regexp/find_test.go | 472 -- src/pkg/exp/regexp/regexp.go | 795 -- src/pkg/exp/regexp/syntax/Makefile | 16 - src/pkg/exp/regexp/syntax/compile.go | 269 - src/pkg/exp/regexp/syntax/make_perl_groups.pl | 103 - src/pkg/exp/regexp/syntax/parse.go | 1797 ---- src/pkg/exp/regexp/syntax/parse_test.go | 350 - src/pkg/exp/regexp/syntax/perl_groups.go | 130 - src/pkg/exp/regexp/syntax/prog.go | 237 - src/pkg/exp/regexp/syntax/prog_test.go | 91 - src/pkg/exp/regexp/syntax/regexp.go | 284 - src/pkg/exp/regexp/syntax/simplify.go | 151 - src/pkg/exp/regexp/syntax/simplify_test.go | 151 - src/pkg/exp/template/html/Makefile | 11 - src/pkg/exp/template/html/context.go | 98 - src/pkg/exp/template/html/escape.go | 105 - src/pkg/exp/template/html/escape_test.go | 75 - src/pkg/exp/wingui/Makefile | 29 - src/pkg/exp/wingui/gui.go | 153 - src/pkg/exp/wingui/winapi.go | 131 - src/pkg/exp/wingui/zwinapi.go | 211 - src/pkg/expvar/Makefile | 11 - src/pkg/expvar/expvar.go | 147 +- src/pkg/expvar/expvar_test.go | 10 +- src/pkg/flag/Makefile | 11 - src/pkg/flag/example_test.go | 83 + src/pkg/flag/flag.go | 207 +- src/pkg/flag/flag_test.go | 54 +- src/pkg/fmt/Makefile | 14 - src/pkg/fmt/doc.go | 35 +- src/pkg/fmt/export_test.go | 7 + src/pkg/fmt/fmt_test.go | 185 +- src/pkg/fmt/format.go | 112 +- src/pkg/fmt/print.go | 414 +- src/pkg/fmt/scan.go | 316 +- src/pkg/fmt/scan_test.go | 83 +- src/pkg/go/ast/Makefile | 16 - src/pkg/go/ast/ast.go | 180 +- src/pkg/go/ast/example_test.go | 136 + src/pkg/go/ast/filter.go | 348 +- src/pkg/go/ast/import.go | 134 + src/pkg/go/ast/print.go | 38 +- src/pkg/go/ast/print_test.go | 17 +- src/pkg/go/ast/resolve.go | 16 +- src/pkg/go/ast/scope.go | 10 +- src/pkg/go/build/Makefile | 22 - src/pkg/go/build/build.go | 1254 ++- src/pkg/go/build/build_test.go | 100 +- src/pkg/go/build/cgotest/cgotest.c | 9 - src/pkg/go/build/cgotest/cgotest.go | 19 - src/pkg/go/build/cgotest/cgotest.h | 5 - src/pkg/go/build/cmdtest/main.go | 12 - src/pkg/go/build/deps_test.go | 424 + src/pkg/go/build/dir.go | 172 - src/pkg/go/build/doc.go | 109 + src/pkg/go/build/path.go | 182 - src/pkg/go/build/pkgtest/pkgtest.go | 9 - src/pkg/go/build/pkgtest/sqrt_386.s | 10 - src/pkg/go/build/pkgtest/sqrt_amd64.s | 9 - src/pkg/go/build/pkgtest/sqrt_arm.s | 10 - src/pkg/go/build/syslist.go | 8 + src/pkg/go/build/syslist_test.go | 4 +- src/pkg/go/build/testdata/other/file/file.go | 5 + src/pkg/go/build/testdata/other/main.go | 11 + src/pkg/go/doc/Makefile | 11 +- src/pkg/go/doc/comment.go | 376 +- src/pkg/go/doc/comment_test.go | 109 + src/pkg/go/doc/doc.go | 668 +- src/pkg/go/doc/doc_test.go | 136 + src/pkg/go/doc/example.go | 117 + src/pkg/go/doc/exports.go | 199 + src/pkg/go/doc/filter.go | 105 + src/pkg/go/doc/headscan.go | 113 + src/pkg/go/doc/reader.go | 774 ++ src/pkg/go/doc/synopsis.go | 52 + src/pkg/go/doc/synopsis_test.go | 44 + src/pkg/go/doc/testdata/a.0.golden | 13 + src/pkg/go/doc/testdata/a.1.golden | 13 + src/pkg/go/doc/testdata/a.2.golden | 13 + src/pkg/go/doc/testdata/a0.go | 8 + src/pkg/go/doc/testdata/a1.go | 8 + src/pkg/go/doc/testdata/b.0.golden | 71 + src/pkg/go/doc/testdata/b.1.golden | 83 + src/pkg/go/doc/testdata/b.2.golden | 71 + src/pkg/go/doc/testdata/b.go | 58 + src/pkg/go/doc/testdata/benchmark.go | 293 + src/pkg/go/doc/testdata/c.0.golden | 48 + src/pkg/go/doc/testdata/c.1.golden | 48 + src/pkg/go/doc/testdata/c.2.golden | 48 + src/pkg/go/doc/testdata/c.go | 62 + src/pkg/go/doc/testdata/d.0.golden | 104 + src/pkg/go/doc/testdata/d.1.golden | 104 + src/pkg/go/doc/testdata/d.2.golden | 104 + src/pkg/go/doc/testdata/d1.go | 57 + src/pkg/go/doc/testdata/d2.go | 45 + src/pkg/go/doc/testdata/e.0.golden | 109 + src/pkg/go/doc/testdata/e.1.golden | 144 + src/pkg/go/doc/testdata/e.2.golden | 130 + src/pkg/go/doc/testdata/e.go | 147 + src/pkg/go/doc/testdata/error1.0.golden | 30 + src/pkg/go/doc/testdata/error1.1.golden | 32 + src/pkg/go/doc/testdata/error1.2.golden | 30 + src/pkg/go/doc/testdata/error1.go | 24 + src/pkg/go/doc/testdata/error2.0.golden | 27 + src/pkg/go/doc/testdata/error2.1.golden | 37 + src/pkg/go/doc/testdata/error2.2.golden | 27 + src/pkg/go/doc/testdata/error2.go | 29 + src/pkg/go/doc/testdata/example.go | 81 + src/pkg/go/doc/testdata/f.0.golden | 13 + src/pkg/go/doc/testdata/f.1.golden | 16 + src/pkg/go/doc/testdata/f.2.golden | 13 + src/pkg/go/doc/testdata/f.go | 14 + src/pkg/go/doc/testdata/template.txt | 65 + src/pkg/go/doc/testdata/testing.0.golden | 156 + src/pkg/go/doc/testdata/testing.1.golden | 298 + src/pkg/go/doc/testdata/testing.2.golden | 156 + src/pkg/go/doc/testdata/testing.go | 404 + src/pkg/go/parser/Makefile | 12 - src/pkg/go/parser/error_test.go | 166 + src/pkg/go/parser/example_test.go | 34 + src/pkg/go/parser/interface.go | 183 +- src/pkg/go/parser/parser.go | 628 +- src/pkg/go/parser/parser_test.go | 193 +- src/pkg/go/parser/short_test.go | 75 + src/pkg/go/parser/testdata/commas.src | 19 + src/pkg/go/parser/testdata/issue3106.src | 46 + src/pkg/go/printer/Makefile | 12 - src/pkg/go/printer/example_test.go | 67 + src/pkg/go/printer/nodes.go | 600 +- src/pkg/go/printer/performance_test.go | 4 +- src/pkg/go/printer/printer.go | 926 +- src/pkg/go/printer/printer_test.go | 242 +- src/pkg/go/printer/testdata/comments.golden | 174 +- src/pkg/go/printer/testdata/comments.input | 163 +- src/pkg/go/printer/testdata/declarations.golden | 167 +- src/pkg/go/printer/testdata/declarations.input | 118 +- src/pkg/go/printer/testdata/expressions.golden | 28 +- src/pkg/go/printer/testdata/expressions.input | 22 + src/pkg/go/printer/testdata/expressions.raw | 28 +- src/pkg/go/printer/testdata/linebreaks.golden | 52 + src/pkg/go/printer/testdata/linebreaks.input | 48 + src/pkg/go/printer/testdata/parser.go | 8 +- src/pkg/go/printer/testdata/statements.golden | 121 +- src/pkg/go/printer/testdata/statements.input | 119 + src/pkg/go/scanner/Makefile | 12 - src/pkg/go/scanner/errors.go | 142 +- src/pkg/go/scanner/example_test.go | 46 + src/pkg/go/scanner/scanner.go | 578 +- src/pkg/go/scanner/scanner_test.go | 155 +- src/pkg/go/token/Makefile | 12 - src/pkg/go/token/position.go | 277 +- src/pkg/go/token/position_test.go | 9 +- src/pkg/go/token/serialize.go | 56 + src/pkg/go/token/serialize_test.go | 111 + src/pkg/go/token/token.go | 12 +- src/pkg/go/typechecker/Makefile | 14 - src/pkg/go/typechecker/scope.go | 69 - src/pkg/go/typechecker/testdata/test0.src | 94 - src/pkg/go/typechecker/testdata/test1.src | 13 - src/pkg/go/typechecker/testdata/test3.src | 41 - src/pkg/go/typechecker/testdata/test4.src | 11 - src/pkg/go/typechecker/type.go | 118 - src/pkg/go/typechecker/typechecker.go | 468 -- src/pkg/go/typechecker/typechecker_test.go | 163 - src/pkg/go/typechecker/universe.go | 36 - src/pkg/go/types/Makefile | 16 - src/pkg/go/types/check.go | 226 - src/pkg/go/types/check_test.go | 215 - src/pkg/go/types/const.go | 332 - src/pkg/go/types/exportdata.go | 132 - src/pkg/go/types/gcimporter.go | 799 -- src/pkg/go/types/gcimporter_test.go | 101 - src/pkg/go/types/testdata/exports.go | 84 - src/pkg/go/types/testdata/test0.src | 154 - src/pkg/go/types/types.go | 255 - src/pkg/go/types/universe.go | 108 - src/pkg/gob/Makefile | 25 - src/pkg/gob/codec_test.go | 1406 ---- src/pkg/gob/debug.go | 686 -- src/pkg/gob/decode.go | 1275 --- src/pkg/gob/decoder.go | 202 - src/pkg/gob/doc.go | 363 - src/pkg/gob/dump.go | 22 - src/pkg/gob/encode.go | 717 -- src/pkg/gob/encoder.go | 243 - src/pkg/gob/encoder_test.go | 580 -- src/pkg/gob/error.go | 42 - src/pkg/gob/gobencdec_test.go | 490 -- src/pkg/gob/timing_test.go | 94 - src/pkg/gob/type.go | 786 -- src/pkg/gob/type_test.go | 153 - src/pkg/hash/Makefile | 11 - src/pkg/hash/adler32/Makefile | 11 - src/pkg/hash/adler32/adler32.go | 22 +- src/pkg/hash/crc32/Makefile | 20 - src/pkg/hash/crc32/crc32.go | 18 +- src/pkg/hash/crc32/crc32_generic.go | 2 + src/pkg/hash/crc64/Makefile | 11 - src/pkg/hash/crc64/crc64.go | 30 +- src/pkg/hash/fnv/Makefile | 11 - src/pkg/hash/fnv/fnv.go | 67 +- src/pkg/hash/fnv/fnv_test.go | 12 +- src/pkg/hash/hash.go | 12 +- src/pkg/html/Makefile | 17 - src/pkg/html/const.go | 90 - src/pkg/html/doc.go | 110 - src/pkg/html/entity.go | 4 +- src/pkg/html/entity_test.go | 2 +- src/pkg/html/escape.go | 46 +- src/pkg/html/node.go | 147 - src/pkg/html/parse.go | 800 -- src/pkg/html/parse_test.go | 156 - src/pkg/html/template/attr.go | 175 + src/pkg/html/template/clone_test.go | 148 + src/pkg/html/template/content.go | 129 + src/pkg/html/template/content_test.go | 261 + src/pkg/html/template/context.go | 339 + src/pkg/html/template/css.go | 268 + src/pkg/html/template/css_test.go | 281 + src/pkg/html/template/doc.go | 191 + src/pkg/html/template/error.go | 197 + src/pkg/html/template/escape.go | 795 ++ src/pkg/html/template/escape_test.go | 1657 ++++ src/pkg/html/template/html.go | 257 + src/pkg/html/template/html_test.go | 94 + src/pkg/html/template/js.go | 362 + src/pkg/html/template/js_test.go | 401 + src/pkg/html/template/template.go | 359 + src/pkg/html/template/transition.go | 553 ++ src/pkg/html/template/url.go | 105 + src/pkg/html/template/url_test.go | 112 + src/pkg/html/testdata/webkit/README | 28 - src/pkg/html/testdata/webkit/adoption01.dat | 194 - src/pkg/html/testdata/webkit/adoption02.dat | 31 - src/pkg/html/testdata/webkit/comments01.dat | 135 - src/pkg/html/testdata/webkit/doctype01.dat | 370 - src/pkg/html/testdata/webkit/entities01.dat | 603 -- src/pkg/html/testdata/webkit/entities02.dat | 249 - src/pkg/html/testdata/webkit/html5test-com.dat | 246 - src/pkg/html/testdata/webkit/inbody01.dat | 43 - src/pkg/html/testdata/webkit/isindex.dat | 40 - .../pending-spec-changes-plain-text-unsafe.dat | Bin 115 -> 0 bytes .../html/testdata/webkit/pending-spec-changes.dat | 28 - src/pkg/html/testdata/webkit/plain-text-unsafe.dat | 8 - src/pkg/html/testdata/webkit/scriptdata01.dat | 308 - .../html/testdata/webkit/scripted/adoption01.dat | 15 - src/pkg/html/testdata/webkit/scripted/webkit01.dat | 28 - src/pkg/html/testdata/webkit/tables01.dat | 197 - src/pkg/html/testdata/webkit/tests1.dat | 1952 ----- src/pkg/html/testdata/webkit/tests10.dat | 799 -- src/pkg/html/testdata/webkit/tests11.dat | 482 -- src/pkg/html/testdata/webkit/tests12.dat | 62 - src/pkg/html/testdata/webkit/tests14.dat | 74 - src/pkg/html/testdata/webkit/tests15.dat | 208 - src/pkg/html/testdata/webkit/tests16.dat | 2277 ----- src/pkg/html/testdata/webkit/tests17.dat | 153 - src/pkg/html/testdata/webkit/tests18.dat | 269 - src/pkg/html/testdata/webkit/tests19.dat | 1220 --- src/pkg/html/testdata/webkit/tests2.dat | 763 -- src/pkg/html/testdata/webkit/tests20.dat | 455 - src/pkg/html/testdata/webkit/tests21.dat | 221 - src/pkg/html/testdata/webkit/tests22.dat | 157 - src/pkg/html/testdata/webkit/tests23.dat | 155 - src/pkg/html/testdata/webkit/tests24.dat | 79 - src/pkg/html/testdata/webkit/tests25.dat | 219 - src/pkg/html/testdata/webkit/tests26.dat | 195 - src/pkg/html/testdata/webkit/tests3.dat | 305 - src/pkg/html/testdata/webkit/tests4.dat | 59 - src/pkg/html/testdata/webkit/tests5.dat | 191 - src/pkg/html/testdata/webkit/tests6.dat | 663 -- src/pkg/html/testdata/webkit/tests7.dat | 390 - src/pkg/html/testdata/webkit/tests8.dat | 148 - src/pkg/html/testdata/webkit/tests9.dat | 457 - src/pkg/html/testdata/webkit/tests_innerHTML_1.dat | 733 -- src/pkg/html/testdata/webkit/tricky01.dat | 261 - src/pkg/html/testdata/webkit/webkit01.dat | 609 -- src/pkg/html/testdata/webkit/webkit02.dat | 104 - src/pkg/html/token.go | 575 -- src/pkg/html/token_test.go | 340 - src/pkg/http/Makefile | 26 - src/pkg/http/cgi/Makefile | 12 - src/pkg/http/cgi/child.go | 191 - src/pkg/http/cgi/child_test.go | 93 - src/pkg/http/cgi/host.go | 323 - src/pkg/http/cgi/host_test.go | 449 - src/pkg/http/cgi/matryoshka_test.go | 74 - src/pkg/http/cgi/testdata/test.cgi | 96 - src/pkg/http/chunked.go | 77 - src/pkg/http/client.go | 290 - src/pkg/http/client_test.go | 292 - src/pkg/http/cookie.go | 268 - src/pkg/http/cookie_test.go | 201 - src/pkg/http/dump.go | 78 - src/pkg/http/export_test.go | 41 - src/pkg/http/fcgi/Makefile | 12 - src/pkg/http/fcgi/child.go | 258 - src/pkg/http/fcgi/fcgi.go | 271 - src/pkg/http/fcgi/fcgi_test.go | 114 - src/pkg/http/fs.go | 323 - src/pkg/http/fs_test.go | 308 - src/pkg/http/header.go | 78 - src/pkg/http/header_test.go | 81 - src/pkg/http/httptest/Makefile | 12 - src/pkg/http/httptest/recorder.go | 59 - src/pkg/http/httptest/server.go | 151 - src/pkg/http/lex.go | 144 - src/pkg/http/lex_test.go | 70 - src/pkg/http/persist.go | 420 - src/pkg/http/pprof/Makefile | 11 - src/pkg/http/pprof/pprof.go | 132 - src/pkg/http/proxy_test.go | 48 - src/pkg/http/range_test.go | 57 - src/pkg/http/readrequest_test.go | 183 - src/pkg/http/request.go | 745 -- src/pkg/http/request_test.go | 336 - src/pkg/http/requestwrite_test.go | 370 - src/pkg/http/response.go | 214 - src/pkg/http/response_test.go | 397 - src/pkg/http/responsewrite_test.go | 109 - src/pkg/http/reverseproxy.go | 159 - src/pkg/http/reverseproxy_test.go | 66 - src/pkg/http/serve_test.go | 959 --- src/pkg/http/server.go | 1183 --- src/pkg/http/sniff.go | 214 - src/pkg/http/sniff_test.go | 80 - src/pkg/http/spdy/Makefile | 13 - src/pkg/http/spdy/read.go | 313 - src/pkg/http/spdy/spdy_test.go | 497 -- src/pkg/http/spdy/types.go | 370 - src/pkg/http/spdy/write.go | 286 - src/pkg/http/status.go | 106 - src/pkg/http/testdata/file | 1 - src/pkg/http/testdata/index.html | 1 - src/pkg/http/testdata/style.css | 1 - src/pkg/http/transfer.go | 520 -- src/pkg/http/transport.go | 717 -- src/pkg/http/transport_test.go | 662 -- src/pkg/http/triv.go | 149 - src/pkg/image/Makefile | 15 - src/pkg/image/bmp/Makefile | 11 - src/pkg/image/bmp/reader.go | 151 - src/pkg/image/color.go | 251 - src/pkg/image/color/color.go | 303 + src/pkg/image/color/ycbcr.go | 99 + src/pkg/image/color/ycbcr_test.go | 33 + src/pkg/image/decode_example_test.go | 79 + src/pkg/image/decode_test.go | 18 +- src/pkg/image/draw/Makefile | 11 - src/pkg/image/draw/bench_test.go | 82 +- src/pkg/image/draw/clip_test.go | 6 +- src/pkg/image/draw/draw.go | 90 +- src/pkg/image/draw/draw_test.go | 134 +- src/pkg/image/format.go | 22 +- src/pkg/image/geom.go | 2 +- src/pkg/image/gif/Makefile | 11 - src/pkg/image/gif/reader.go | 52 +- src/pkg/image/image.go | 409 +- src/pkg/image/image_test.go | 45 +- src/pkg/image/jpeg/Makefile | 15 - src/pkg/image/jpeg/huffman.go | 13 +- src/pkg/image/jpeg/reader.go | 64 +- src/pkg/image/jpeg/writer.go | 22 +- src/pkg/image/jpeg/writer_test.go | 17 +- src/pkg/image/names.go | 71 +- src/pkg/image/png/Makefile | 12 - src/pkg/image/png/reader.go | 362 +- src/pkg/image/png/reader_test.go | 70 +- src/pkg/image/png/testdata/invalid-crc32.png | Bin 0 -> 1289 bytes src/pkg/image/png/testdata/invalid-noend.png | Bin 0 -> 1277 bytes src/pkg/image/png/testdata/invalid-trunc.png | Bin 0 -> 1288 bytes src/pkg/image/png/testdata/invalid-zlib.png | Bin 0 -> 1289 bytes src/pkg/image/png/writer.go | 71 +- src/pkg/image/png/writer_test.go | 39 +- src/pkg/image/testdata/video-001.bmp | Bin 46610 -> 0 bytes src/pkg/image/testdata/video-001.tiff | Bin 30810 -> 0 bytes src/pkg/image/tiff/Makefile | 13 - src/pkg/image/tiff/buffer.go | 57 - src/pkg/image/tiff/buffer_test.go | 36 - src/pkg/image/tiff/consts.go | 103 - src/pkg/image/tiff/reader.go | 423 - src/pkg/image/tiff/reader_test.go | 25 - src/pkg/image/tiff/testdata/no_rps.tiff | Bin 1294 -> 0 bytes src/pkg/image/ycbcr.go | 144 + src/pkg/image/ycbcr/Makefile | 11 - src/pkg/image/ycbcr/ycbcr.go | 183 - src/pkg/image/ycbcr/ycbcr_test.go | 33 - src/pkg/image/ycbcr_test.go | 106 + src/pkg/index/suffixarray/Makefile | 12 - src/pkg/index/suffixarray/qsufsort.go | 10 +- src/pkg/index/suffixarray/suffixarray.go | 130 +- src/pkg/index/suffixarray/suffixarray_test.go | 76 +- src/pkg/io/Makefile | 13 - src/pkg/io/io.go | 150 +- src/pkg/io/io_test.go | 82 +- src/pkg/io/ioutil/Makefile | 12 - src/pkg/io/ioutil/ioutil.go | 70 +- src/pkg/io/ioutil/ioutil_test.go | 33 +- src/pkg/io/ioutil/tempfile.go | 12 +- src/pkg/io/multi.go | 14 +- src/pkg/io/multi_test.go | 11 +- src/pkg/io/pipe.go | 51 +- src/pkg/io/pipe_test.go | 27 +- src/pkg/json/Makefile | 16 - src/pkg/json/decode.go | 934 --- src/pkg/json/decode_test.go | 561 -- src/pkg/json/encode.go | 454 - src/pkg/json/encode_test.go | 82 - src/pkg/json/indent.go | 116 - src/pkg/json/scanner.go | 624 -- src/pkg/json/scanner_test.go | 278 - src/pkg/json/stream.go | 185 - src/pkg/json/stream_test.go | 122 - src/pkg/json/tagkey_test.go | 95 - src/pkg/json/tags.go | 44 - src/pkg/json/tags_test.go | 28 - src/pkg/log/Makefile | 11 - src/pkg/log/log.go | 81 +- src/pkg/log/syslog/syslog.go | 170 + src/pkg/log/syslog/syslog_test.go | 117 + src/pkg/log/syslog/syslog_unix.go | 33 + src/pkg/log/syslog/syslog_windows.go | 8 + src/pkg/mail/Makefile | 11 - src/pkg/mail/message.go | 524 -- src/pkg/mail/message_test.go | 278 - src/pkg/math/Makefile | 102 - src/pkg/math/abs.go | 22 + src/pkg/math/abs_386.s | 10 + src/pkg/math/abs_amd64.s | 12 + src/pkg/math/abs_arm.s | 6 + src/pkg/math/acosh.go | 5 +- src/pkg/math/all_test.go | 314 +- src/pkg/math/asin.go | 10 +- src/pkg/math/asin_amd64.s | 9 + src/pkg/math/asin_arm.s | 9 + src/pkg/math/asin_decl.go | 8 - src/pkg/math/asinh.go | 8 +- src/pkg/math/atan.go | 4 +- src/pkg/math/atan2.go | 18 +- src/pkg/math/atan2_386.s | 0 src/pkg/math/atan2_amd64.s | 6 + src/pkg/math/atan2_arm.s | 6 + src/pkg/math/atan2_decl.go | 7 - src/pkg/math/atan_amd64.s | 6 + src/pkg/math/atan_arm.s | 6 + src/pkg/math/atan_decl.go | 7 - src/pkg/math/atanh.go | 7 +- src/pkg/math/big/arith.go | 242 + src/pkg/math/big/arith_386.s | 276 + src/pkg/math/big/arith_amd64.s | 274 + src/pkg/math/big/arith_arm.s | 321 + src/pkg/math/big/arith_decl.go | 19 + src/pkg/math/big/arith_test.go | 372 + src/pkg/math/big/calibrate_test.go | 88 + src/pkg/math/big/example_test.go | 51 + src/pkg/math/big/hilbert_test.go | 160 + src/pkg/math/big/int.go | 896 ++ src/pkg/math/big/int_test.go | 1414 ++++ src/pkg/math/big/nat.go | 1422 ++++ src/pkg/math/big/nat_test.go | 663 ++ src/pkg/math/big/rat.go | 432 + src/pkg/math/big/rat_test.go | 456 + src/pkg/math/bits.go | 2 +- src/pkg/math/cbrt.go | 17 +- src/pkg/math/cmplx/abs.go | 12 + src/pkg/math/cmplx/asin.go | 170 + src/pkg/math/cmplx/cmath_test.go | 853 ++ src/pkg/math/cmplx/conj.go | 8 + src/pkg/math/cmplx/exp.go | 55 + src/pkg/math/cmplx/isinf.go | 21 + src/pkg/math/cmplx/isnan.go | 25 + src/pkg/math/cmplx/log.go | 64 + src/pkg/math/cmplx/phase.go | 11 + src/pkg/math/cmplx/polar.go | 12 + src/pkg/math/cmplx/pow.go | 60 + src/pkg/math/cmplx/rect.go | 13 + src/pkg/math/cmplx/sin.go | 132 + src/pkg/math/cmplx/sqrt.go | 103 + src/pkg/math/cmplx/tan.go | 184 + src/pkg/math/const.go | 12 +- src/pkg/math/dim.go | 72 + src/pkg/math/dim_386.s | 12 + src/pkg/math/dim_amd64.s | 142 + src/pkg/math/dim_arm.s | 12 + src/pkg/math/erf.go | 20 +- src/pkg/math/exp.go | 179 +- src/pkg/math/exp2.go | 10 - src/pkg/math/exp2_amd64.s | 6 + src/pkg/math/exp2_arm.s | 6 + src/pkg/math/exp2_decl.go | 7 - src/pkg/math/exp_arm.s | 6 + src/pkg/math/exp_decl.go | 7 - src/pkg/math/exp_port.go | 191 - src/pkg/math/exp_test.go | 10 - src/pkg/math/expm1.go | 10 +- src/pkg/math/expm1_amd64.s | 6 + src/pkg/math/expm1_arm.s | 6 + src/pkg/math/expm1_decl.go | 7 - src/pkg/math/export_test.go | 11 + src/pkg/math/fabs.go | 21 - src/pkg/math/fabs_386.s | 10 - src/pkg/math/fabs_amd64.s | 12 - src/pkg/math/fabs_decl.go | 7 - src/pkg/math/fdim.go | 29 - src/pkg/math/fdim_amd64.s | 26 - src/pkg/math/fdim_decl.go | 9 - src/pkg/math/floor.go | 34 +- src/pkg/math/floor_amd64.s | 12 + src/pkg/math/floor_arm.s | 12 + src/pkg/math/floor_decl.go | 9 - src/pkg/math/fmod.go | 47 - src/pkg/math/fmod_386.s | 15 - src/pkg/math/fmod_decl.go | 7 - src/pkg/math/frexp.go | 8 +- src/pkg/math/frexp_amd64.s | 6 + src/pkg/math/frexp_arm.s | 6 + src/pkg/math/frexp_decl.go | 7 - src/pkg/math/gamma.go | 23 +- src/pkg/math/hypot.go | 10 +- src/pkg/math/hypot_arm.s | 6 + src/pkg/math/hypot_decl.go | 7 - src/pkg/math/hypot_port.go | 63 - src/pkg/math/hypot_test.go | 9 - src/pkg/math/j0.go | 12 +- src/pkg/math/j1.go | 12 +- src/pkg/math/jn.go | 16 +- src/pkg/math/ldexp.go | 8 +- src/pkg/math/ldexp_amd64.s | 6 + src/pkg/math/ldexp_arm.s | 6 + src/pkg/math/ldexp_decl.go | 7 - src/pkg/math/lgamma.go | 165 +- src/pkg/math/log.go | 8 +- src/pkg/math/log10.go | 12 +- src/pkg/math/log10_amd64.s | 9 + src/pkg/math/log10_arm.s | 9 + src/pkg/math/log10_decl.go | 8 - src/pkg/math/log1p.go | 13 +- src/pkg/math/log1p_amd64.s | 6 + src/pkg/math/log1p_arm.s | 6 + src/pkg/math/log1p_decl.go | 7 - src/pkg/math/log_arm.s | 6 + src/pkg/math/log_decl.go | 7 - src/pkg/math/logb.go | 12 +- src/pkg/math/mod.go | 50 + src/pkg/math/mod_386.s | 15 + src/pkg/math/mod_amd64.s | 6 + src/pkg/math/mod_arm.s | 6 + src/pkg/math/modf.go | 7 +- src/pkg/math/modf_amd64.s | 6 + src/pkg/math/modf_arm.s | 6 + src/pkg/math/modf_decl.go | 7 - src/pkg/math/nextafter.go | 10 +- src/pkg/math/pow.go | 10 +- src/pkg/math/pow10.go | 10 + src/pkg/math/rand/exp.go | 223 + src/pkg/math/rand/normal.go | 158 + src/pkg/math/rand/rand.go | 190 + src/pkg/math/rand/rand_test.go | 362 + src/pkg/math/rand/rng.go | 246 + src/pkg/math/rand/zipf.go | 73 + src/pkg/math/remainder.go | 20 +- src/pkg/math/remainder_amd64.s | 6 + src/pkg/math/remainder_arm.s | 6 + src/pkg/math/remainder_decl.go | 7 - src/pkg/math/sin.go | 239 +- src/pkg/math/sin_amd64.s | 9 + src/pkg/math/sin_arm.s | 9 + src/pkg/math/sin_decl.go | 8 - src/pkg/math/sincos.go | 64 +- src/pkg/math/sincos_arm.s | 6 + src/pkg/math/sincos_decl.go | 7 - src/pkg/math/sinh.go | 10 + src/pkg/math/sqrt.go | 138 +- src/pkg/math/sqrt_decl.go | 7 - src/pkg/math/sqrt_port.go | 147 - src/pkg/math/sqrt_test.go | 9 - src/pkg/math/tan.go | 142 +- src/pkg/math/tan_amd64.s | 6 + src/pkg/math/tan_arm.s | 6 + src/pkg/math/tan_decl.go | 7 - src/pkg/math/tanh.go | 5 + src/pkg/mime/Makefile | 13 - src/pkg/mime/grammar.go | 27 +- src/pkg/mime/mediatype.go | 107 +- src/pkg/mime/mediatype_test.go | 40 +- src/pkg/mime/mime_test.go | 27 - src/pkg/mime/multipart/Makefile | 13 - src/pkg/mime/multipart/formdata.go | 35 +- src/pkg/mime/multipart/multipart.go | 60 +- src/pkg/mime/multipart/multipart_test.go | 17 +- src/pkg/mime/multipart/writer.go | 26 +- src/pkg/mime/type.go | 71 +- src/pkg/mime/type_test.go | 29 + src/pkg/mime/type_unix.go | 61 + src/pkg/mime/type_windows.go | 61 + src/pkg/net/Makefile | 157 - src/pkg/net/cgo_bsd.go | 2 + src/pkg/net/cgo_stub.go | 12 +- src/pkg/net/cgo_unix.go | 17 +- src/pkg/net/dial.go | 249 +- src/pkg/net/dial_test.go | 224 + src/pkg/net/dialgoogle_test.go | 21 +- src/pkg/net/dict/Makefile | 7 - src/pkg/net/dict/dict.go | 211 - src/pkg/net/dnsclient.go | 43 +- src/pkg/net/dnsclient_unix.go | 53 +- src/pkg/net/dnsconfig.go | 19 +- src/pkg/net/dnsmsg.go | 352 +- src/pkg/net/dnsmsg_test.go | 13 + src/pkg/net/doc.go | 59 + src/pkg/net/example_test.go | 35 + src/pkg/net/fd.go | 530 +- src/pkg/net/fd_darwin.go | 37 +- src/pkg/net/fd_freebsd.go | 36 +- src/pkg/net/fd_linux.go | 51 +- src/pkg/net/fd_netbsd.go | 116 + src/pkg/net/fd_openbsd.go | 36 +- src/pkg/net/fd_windows.go | 315 +- src/pkg/net/file.go | 41 +- src/pkg/net/file_plan9.go | 13 +- src/pkg/net/file_test.go | 130 +- src/pkg/net/file_windows.go | 6 +- src/pkg/net/hosts.go | 12 +- src/pkg/net/hosts_test.go | 2 +- src/pkg/net/hosts_testdata | 12 - src/pkg/net/http/cgi/child.go | 193 + src/pkg/net/http/cgi/child_test.go | 87 + src/pkg/net/http/cgi/host.go | 350 + src/pkg/net/http/cgi/host_test.go | 479 ++ src/pkg/net/http/cgi/matryoshka_test.go | 93 + src/pkg/net/http/cgi/testdata/test.cgi | 96 + src/pkg/net/http/chunked.go | 170 + src/pkg/net/http/chunked_test.go | 39 + src/pkg/net/http/client.go | 334 + src/pkg/net/http/client_test.go | 442 + src/pkg/net/http/cookie.go | 267 + src/pkg/net/http/cookie_test.go | 228 + src/pkg/net/http/doc.go | 80 + src/pkg/net/http/example_test.go | 51 + src/pkg/net/http/export_test.go | 43 + src/pkg/net/http/fcgi/child.go | 271 + src/pkg/net/http/fcgi/fcgi.go | 274 + src/pkg/net/http/fcgi/fcgi_test.go | 150 + src/pkg/net/http/filetransport.go | 123 + src/pkg/net/http/filetransport_test.go | 65 + src/pkg/net/http/fs.go | 367 + src/pkg/net/http/fs_test.go | 478 ++ src/pkg/net/http/header.go | 78 + src/pkg/net/http/header_test.go | 81 + src/pkg/net/http/httptest/recorder.go | 58 + src/pkg/net/http/httptest/server.go | 207 + src/pkg/net/http/httptest/server_test.go | 29 + src/pkg/net/http/httputil/chunked.go | 172 + src/pkg/net/http/httputil/chunked_test.go | 41 + src/pkg/net/http/httputil/dump.go | 229 + src/pkg/net/http/httputil/dump_test.go | 152 + src/pkg/net/http/httputil/persist.go | 422 + src/pkg/net/http/httputil/reverseproxy.go | 172 + src/pkg/net/http/httputil/reverseproxy_test.go | 109 + src/pkg/net/http/jar.go | 30 + src/pkg/net/http/lex.go | 136 + src/pkg/net/http/lex_test.go | 70 + src/pkg/net/http/pprof/pprof.go | 194 + src/pkg/net/http/proxy_test.go | 48 + src/pkg/net/http/range_test.go | 57 + src/pkg/net/http/readrequest_test.go | 283 + src/pkg/net/http/request.go | 743 ++ src/pkg/net/http/request_test.go | 302 + src/pkg/net/http/requestwrite_test.go | 438 + src/pkg/net/http/response.go | 236 + src/pkg/net/http/response_test.go | 446 + src/pkg/net/http/responsewrite_test.go | 109 + src/pkg/net/http/serve_test.go | 1186 +++ src/pkg/net/http/server.go | 1232 +++ src/pkg/net/http/sniff.go | 214 + src/pkg/net/http/sniff_test.go | 138 + src/pkg/net/http/status.go | 108 + src/pkg/net/http/testdata/file | 1 + src/pkg/net/http/testdata/index.html | 1 + src/pkg/net/http/testdata/style.css | 1 + src/pkg/net/http/transfer.go | 630 ++ src/pkg/net/http/transport.go | 749 ++ src/pkg/net/http/transport_test.go | 821 ++ src/pkg/net/http/triv.go | 141 + src/pkg/net/interface.go | 52 +- src/pkg/net/interface_bsd.go | 112 +- src/pkg/net/interface_darwin.go | 35 +- src/pkg/net/interface_freebsd.go | 35 +- src/pkg/net/interface_linux.go | 164 +- src/pkg/net/interface_linux_test.go | 54 + src/pkg/net/interface_netbsd.go | 14 + src/pkg/net/interface_openbsd.go | 4 +- src/pkg/net/interface_stub.go | 12 +- src/pkg/net/interface_test.go | 65 +- src/pkg/net/interface_windows.go | 54 +- src/pkg/net/ip.go | 259 +- src/pkg/net/ip_test.go | 243 +- src/pkg/net/ipraw_test.go | 246 +- src/pkg/net/iprawsock.go | 8 +- src/pkg/net/iprawsock_plan9.go | 94 +- src/pkg/net/iprawsock_posix.go | 193 +- src/pkg/net/ipsock.go | 41 +- src/pkg/net/ipsock_plan9.go | 92 +- src/pkg/net/ipsock_posix.go | 114 +- src/pkg/net/lookup_plan9.go | 74 +- src/pkg/net/lookup_test.go | 70 +- src/pkg/net/lookup_unix.go | 104 +- src/pkg/net/lookup_windows.go | 89 +- src/pkg/net/mac.go | 86 + src/pkg/net/mac_test.go | 66 + src/pkg/net/mail/message.go | 523 ++ src/pkg/net/mail/message_test.go | 261 + src/pkg/net/multicast_test.go | 241 +- src/pkg/net/net.go | 190 +- src/pkg/net/net_test.go | 189 +- src/pkg/net/newpollserver.go | 17 +- src/pkg/net/parse.go | 49 +- src/pkg/net/parse_test.go | 6 +- src/pkg/net/pipe.go | 21 +- src/pkg/net/pipe_test.go | 5 +- src/pkg/net/port.go | 11 +- src/pkg/net/rpc/client.go | 293 + src/pkg/net/rpc/debug.go | 90 + src/pkg/net/rpc/jsonrpc/all_test.go | 221 + src/pkg/net/rpc/jsonrpc/client.go | 123 + src/pkg/net/rpc/jsonrpc/server.go | 136 + src/pkg/net/rpc/server.go | 651 ++ src/pkg/net/rpc/server_test.go | 597 ++ src/pkg/net/sendfile_linux.go | 32 +- src/pkg/net/sendfile_stub.go | 9 +- src/pkg/net/sendfile_windows.go | 12 +- src/pkg/net/server_test.go | 556 +- src/pkg/net/smtp/auth.go | 98 + src/pkg/net/smtp/smtp.go | 292 + src/pkg/net/smtp/smtp_test.go | 271 + src/pkg/net/sock.go | 141 +- src/pkg/net/sock_bsd.go | 65 +- src/pkg/net/sock_linux.go | 59 +- src/pkg/net/sock_windows.go | 46 +- src/pkg/net/sockopt.go | 193 + src/pkg/net/sockopt_bsd.go | 61 + src/pkg/net/sockopt_linux.go | 51 + src/pkg/net/sockopt_windows.go | 49 + src/pkg/net/sockoptip.go | 219 + src/pkg/net/sockoptip_bsd.go | 62 + src/pkg/net/sockoptip_darwin.go | 90 + src/pkg/net/sockoptip_freebsd.go | 92 + src/pkg/net/sockoptip_linux.go | 140 + src/pkg/net/sockoptip_netbsd.go | 39 + src/pkg/net/sockoptip_openbsd.go | 90 + src/pkg/net/sockoptip_windows.go | 91 + src/pkg/net/tcpsock.go | 6 +- src/pkg/net/tcpsock_plan9.go | 44 +- src/pkg/net/tcpsock_posix.go | 188 +- src/pkg/net/testdata/hosts | 12 + src/pkg/net/testdata/igmp | 24 + src/pkg/net/testdata/igmp6 | 18 + src/pkg/net/textproto/Makefile | 15 - src/pkg/net/textproto/header.go | 2 +- src/pkg/net/textproto/pipeline.go | 2 +- src/pkg/net/textproto/reader.go | 172 +- src/pkg/net/textproto/reader_test.go | 109 +- src/pkg/net/textproto/textproto.go | 14 +- src/pkg/net/textproto/writer.go | 7 +- src/pkg/net/timeout_test.go | 112 +- src/pkg/net/udp_test.go | 89 + src/pkg/net/udpsock.go | 6 +- src/pkg/net/udpsock_plan9.go | 75 +- src/pkg/net/udpsock_posix.go | 246 +- src/pkg/net/unicast_test.go | 538 ++ src/pkg/net/unixsock.go | 6 +- src/pkg/net/unixsock_plan9.go | 65 +- src/pkg/net/unixsock_posix.go | 198 +- src/pkg/net/url/example_test.go | 41 + src/pkg/net/url/url.go | 657 ++ src/pkg/net/url/url_test.go | 746 ++ src/pkg/netchan/Makefile | 13 - src/pkg/netchan/common.go | 336 - src/pkg/netchan/export.go | 400 - src/pkg/netchan/import.go | 287 - src/pkg/netchan/netchan_test.go | 435 - src/pkg/old/template/Makefile | 14 - src/pkg/old/template/doc.go | 91 - src/pkg/old/template/execute.go | 346 - src/pkg/old/template/format.go | 77 - src/pkg/old/template/parse.go | 743 -- src/pkg/old/template/template_test.go | 804 -- src/pkg/os/Makefile | 100 - src/pkg/os/dir_plan9.go | 64 +- src/pkg/os/dir_unix.go | 23 +- src/pkg/os/dir_windows.go | 4 +- src/pkg/os/doc.go | 124 + src/pkg/os/env.go | 40 +- src/pkg/os/env_plan9.go | 96 - src/pkg/os/env_test.go | 11 + src/pkg/os/env_unix.go | 109 - src/pkg/os/env_windows.go | 127 - src/pkg/os/error.go | 65 +- src/pkg/os/error_plan9.go | 73 +- src/pkg/os/error_posix.go | 94 +- src/pkg/os/error_test.go | 81 + src/pkg/os/error_windows.go | 30 + src/pkg/os/exec.go | 19 +- src/pkg/os/exec/example_test.go | 75 + src/pkg/os/exec/exec.go | 401 + src/pkg/os/exec/exec_test.go | 325 + src/pkg/os/exec/lp_plan9.go | 53 + src/pkg/os/exec/lp_test.go | 33 + src/pkg/os/exec/lp_unix.go | 56 + src/pkg/os/exec/lp_windows.go | 82 + src/pkg/os/exec_plan9.go | 125 +- src/pkg/os/exec_posix.go | 122 +- src/pkg/os/exec_unix.go | 75 +- src/pkg/os/exec_windows.go | 76 +- src/pkg/os/export_test.go | 9 + src/pkg/os/file.go | 207 +- src/pkg/os/file_plan9.go | 191 +- src/pkg/os/file_posix.go | 198 +- src/pkg/os/file_unix.go | 216 +- src/pkg/os/file_windows.go | 248 +- src/pkg/os/getwd.go | 16 +- src/pkg/os/inotify/Makefile | 14 - src/pkg/os/inotify/inotify_linux.go | 288 - src/pkg/os/inotify/inotify_linux_test.go | 96 - src/pkg/os/mkunixsignals.sh | 24 - src/pkg/os/os_test.go | 328 +- src/pkg/os/os_unix_test.go | 76 + src/pkg/os/path.go | 21 +- src/pkg/os/path_test.go | 37 +- src/pkg/os/path_unix.go | 2 + src/pkg/os/proc.go | 6 +- src/pkg/os/signal/Makefile | 11 - src/pkg/os/signal/sig.s | 16 + src/pkg/os/signal/signal.go | 73 +- src/pkg/os/signal/signal_stub.go | 11 + src/pkg/os/signal/signal_test.go | 50 +- src/pkg/os/signal/signal_unix.go | 38 + src/pkg/os/signal/signal_windows_test.go | 51 + src/pkg/os/stat_darwin.go | 71 +- src/pkg/os/stat_freebsd.go | 71 +- src/pkg/os/stat_linux.go | 71 +- src/pkg/os/stat_netbsd.go | 61 + src/pkg/os/stat_openbsd.go | 71 +- src/pkg/os/stat_plan9.go | 126 +- src/pkg/os/stat_windows.go | 258 +- src/pkg/os/str.go | 2 + src/pkg/os/sys_bsd.go | 11 +- src/pkg/os/sys_linux.go | 3 +- src/pkg/os/sys_plan9.go | 2 +- src/pkg/os/sys_windows.go | 4 +- src/pkg/os/time.go | 19 - src/pkg/os/types.go | 143 +- src/pkg/os/user/Makefile | 26 - src/pkg/os/user/lookup_stubs.go | 15 +- src/pkg/os/user/lookup_unix.go | 31 +- src/pkg/os/user/lookup_windows.go | 117 + src/pkg/os/user/user.go | 14 +- src/pkg/os/user/user_test.go | 76 +- src/pkg/patch/Makefile | 14 - src/pkg/patch/apply.go | 54 - src/pkg/patch/git.go | 121 - src/pkg/patch/patch.go | 322 - src/pkg/patch/patch_test.go | 390 - src/pkg/patch/textdiff.go | 175 - src/pkg/path/Makefile | 14 - src/pkg/path/example_test.go | 63 + src/pkg/path/filepath/Makefile | 32 - src/pkg/path/filepath/match.go | 51 +- src/pkg/path/filepath/match_test.go | 57 +- src/pkg/path/filepath/path.go | 289 +- src/pkg/path/filepath/path_plan9.go | 2 +- src/pkg/path/filepath/path_test.go | 515 +- src/pkg/path/filepath/path_unix.go | 4 +- src/pkg/path/filepath/path_windows.go | 42 +- src/pkg/path/filepath/symlink.go | 67 + src/pkg/path/filepath/symlink_windows.go | 63 + src/pkg/path/match.go | 18 +- src/pkg/path/match_test.go | 7 +- src/pkg/path/path.go | 31 +- src/pkg/path/path_test.go | 39 +- src/pkg/rand/Makefile | 15 - src/pkg/rand/exp.go | 223 - src/pkg/rand/normal.go | 158 - src/pkg/rand/rand.go | 179 - src/pkg/rand/rand_test.go | 350 - src/pkg/rand/rng.go | 246 - src/pkg/rand/zipf.go | 73 - src/pkg/reflect/Makefile | 13 - src/pkg/reflect/all_test.go | 198 +- src/pkg/reflect/deepequal.go | 16 +- src/pkg/reflect/tostring_test.go | 10 +- src/pkg/reflect/type.go | 253 +- src/pkg/reflect/value.go | 1476 ++-- src/pkg/regexp/Makefile | 11 - src/pkg/regexp/all_test.go | 165 +- src/pkg/regexp/exec.go | 345 + src/pkg/regexp/exec_test.go | 709 ++ src/pkg/regexp/find_test.go | 32 +- src/pkg/regexp/regexp.go | 1343 +-- src/pkg/regexp/syntax/compile.go | 289 + src/pkg/regexp/syntax/make_perl_groups.pl | 103 + src/pkg/regexp/syntax/parse.go | 1867 +++++ src/pkg/regexp/syntax/parse_test.go | 551 ++ src/pkg/regexp/syntax/perl_groups.go | 130 + src/pkg/regexp/syntax/prog.go | 310 + src/pkg/regexp/syntax/prog_test.go | 105 + src/pkg/regexp/syntax/regexp.go | 319 + src/pkg/regexp/syntax/simplify.go | 151 + src/pkg/regexp/syntax/simplify_test.go | 151 + src/pkg/regexp/testdata/README | 23 + src/pkg/regexp/testdata/basic.dat | 221 + src/pkg/regexp/testdata/nullsubexpr.dat | 79 + src/pkg/regexp/testdata/re2-exhaustive.txt.bz2 | Bin 0 -> 394016 bytes src/pkg/regexp/testdata/re2-search.txt | 3667 ++++++++ src/pkg/regexp/testdata/repetition.dat | 163 + src/pkg/regexp/testdata/testregex.c | 2286 +++++ src/pkg/rpc/Makefile | 13 - src/pkg/rpc/client.go | 286 - src/pkg/rpc/debug.go | 90 - src/pkg/rpc/jsonrpc/Makefile | 12 - src/pkg/rpc/jsonrpc/all_test.go | 156 - src/pkg/rpc/jsonrpc/client.go | 124 - src/pkg/rpc/jsonrpc/server.go | 136 - src/pkg/rpc/server.go | 637 -- src/pkg/rpc/server_test.go | 501 -- src/pkg/runtime/386/arch.h | 3 - src/pkg/runtime/386/asm.s | 549 -- src/pkg/runtime/386/atomic.c | 19 - src/pkg/runtime/386/closure.c | 105 - src/pkg/runtime/386/memmove.s | 86 - src/pkg/runtime/386/vlop.s | 48 - src/pkg/runtime/386/vlrt.c | 815 -- src/pkg/runtime/Makefile | 165 +- src/pkg/runtime/alg.c | 484 ++ src/pkg/runtime/amd64/arch.h | 3 - src/pkg/runtime/amd64/asm.s | 577 -- src/pkg/runtime/amd64/atomic.c | 19 - src/pkg/runtime/amd64/closure.c | 123 - src/pkg/runtime/amd64/memmove.s | 88 - src/pkg/runtime/amd64/traceback.c | 295 - src/pkg/runtime/arch_386.h | 4 + src/pkg/runtime/arch_amd64.h | 4 + src/pkg/runtime/arch_arm.h | 4 + src/pkg/runtime/arm/arch.h | 3 - src/pkg/runtime/arm/asm.s | 316 - src/pkg/runtime/arm/atomic.c | 83 - src/pkg/runtime/arm/closure.c | 129 - src/pkg/runtime/arm/memmove.s | 255 - src/pkg/runtime/arm/memset.s | 94 - src/pkg/runtime/arm/softfloat.c | 525 -- src/pkg/runtime/arm/traceback.c | 213 - src/pkg/runtime/arm/vlop.s | 190 - src/pkg/runtime/arm/vlrt.c | 816 -- src/pkg/runtime/asm_386.s | 572 ++ src/pkg/runtime/asm_amd64.s | 599 ++ src/pkg/runtime/asm_arm.s | 322 + src/pkg/runtime/atomic_386.c | 19 + src/pkg/runtime/atomic_amd64.c | 19 + src/pkg/runtime/atomic_arm.c | 83 + src/pkg/runtime/callback_windows_386.c | 107 + src/pkg/runtime/callback_windows_amd64.c | 104 + src/pkg/runtime/cgo/386.S | 67 - src/pkg/runtime/cgo/Makefile | 60 - src/pkg/runtime/cgo/amd64.S | 73 - src/pkg/runtime/cgo/arm.S | 1 - src/pkg/runtime/cgo/cgo.go | 12 + src/pkg/runtime/cgo/darwin_386.c | 149 - src/pkg/runtime/cgo/darwin_amd64.c | 119 - src/pkg/runtime/cgo/freebsd_386.c | 64 - src/pkg/runtime/cgo/freebsd_amd64.c | 63 - src/pkg/runtime/cgo/gcc_386.S | 67 + src/pkg/runtime/cgo/gcc_amd64.S | 92 + src/pkg/runtime/cgo/gcc_arm.S | 1 + src/pkg/runtime/cgo/gcc_darwin_386.c | 166 + src/pkg/runtime/cgo/gcc_darwin_amd64.c | 136 + src/pkg/runtime/cgo/gcc_freebsd_386.c | 81 + src/pkg/runtime/cgo/gcc_freebsd_amd64.c | 81 + src/pkg/runtime/cgo/gcc_linux_386.c | 88 + src/pkg/runtime/cgo/gcc_linux_amd64.c | 79 + src/pkg/runtime/cgo/gcc_linux_arm.c | 20 + src/pkg/runtime/cgo/gcc_setenv.c | 18 + src/pkg/runtime/cgo/gcc_util.c | 51 + src/pkg/runtime/cgo/gcc_windows_386.c | 59 + src/pkg/runtime/cgo/gcc_windows_amd64.c | 57 + src/pkg/runtime/cgo/libcgo.h | 2 +- src/pkg/runtime/cgo/linux_386.c | 73 - src/pkg/runtime/cgo/linux_amd64.c | 63 - src/pkg/runtime/cgo/linux_arm.c | 19 - src/pkg/runtime/cgo/setenv.c | 16 - src/pkg/runtime/cgo/util.c | 51 - src/pkg/runtime/cgo/windows_386.c | 62 - src/pkg/runtime/cgo/windows_amd64.c | 60 - src/pkg/runtime/cgocall.c | 54 +- src/pkg/runtime/chan.c | 41 +- src/pkg/runtime/chan_test.go | 60 + src/pkg/runtime/closure_386.c | 105 + src/pkg/runtime/closure_amd64.c | 123 + src/pkg/runtime/closure_arm.c | 129 + src/pkg/runtime/compiler.go | 13 + src/pkg/runtime/cpuprof.c | 1 + src/pkg/runtime/darwin/386/defs.h | 289 - src/pkg/runtime/darwin/386/rt0.s | 8 - src/pkg/runtime/darwin/386/signal.c | 194 - src/pkg/runtime/darwin/386/sys.s | 311 - src/pkg/runtime/darwin/amd64/defs.h | 305 - src/pkg/runtime/darwin/amd64/rt0.s | 10 - src/pkg/runtime/darwin/amd64/signal.c | 204 - src/pkg/runtime/darwin/amd64/sys.s | 295 - src/pkg/runtime/darwin/defs.c | 159 - src/pkg/runtime/darwin/mem.c | 55 - src/pkg/runtime/darwin/os.h | 31 - src/pkg/runtime/darwin/signals.h | 51 - src/pkg/runtime/darwin/thread.c | 484 -- src/pkg/runtime/debug.go | 62 +- src/pkg/runtime/debug/Makefile | 11 - src/pkg/runtime/debug/stack_test.go | 21 +- src/pkg/runtime/defs1_linux.go | 37 + src/pkg/runtime/defs2_linux.go | 126 + src/pkg/runtime/defs_arm_linux.go | 124 + src/pkg/runtime/defs_darwin.go | 163 + src/pkg/runtime/defs_darwin_386.h | 366 + src/pkg/runtime/defs_darwin_amd64.h | 369 + src/pkg/runtime/defs_freebsd.go | 117 + src/pkg/runtime/defs_freebsd_386.h | 193 + src/pkg/runtime/defs_freebsd_amd64.h | 204 + src/pkg/runtime/defs_linux.go | 104 + src/pkg/runtime/defs_linux_386.h | 191 + src/pkg/runtime/defs_linux_amd64.h | 234 + src/pkg/runtime/defs_linux_arm.h | 148 + src/pkg/runtime/defs_netbsd.go | 111 + src/pkg/runtime/defs_netbsd_386.h | 146 + src/pkg/runtime/defs_netbsd_amd64.h | 158 + src/pkg/runtime/defs_openbsd.go | 111 + src/pkg/runtime/defs_openbsd_386.h | 146 + src/pkg/runtime/defs_openbsd_amd64.h | 158 + src/pkg/runtime/defs_plan9_386.h | 2 + src/pkg/runtime/defs_windows.go | 66 + src/pkg/runtime/defs_windows_386.h | 99 + src/pkg/runtime/defs_windows_amd64.h | 114 + src/pkg/runtime/error.go | 49 +- src/pkg/runtime/export_test.go | 4 + src/pkg/runtime/extern.go | 75 +- src/pkg/runtime/freebsd/386/defs.h | 187 - src/pkg/runtime/freebsd/386/rt0.s | 9 - src/pkg/runtime/freebsd/386/signal.c | 193 - src/pkg/runtime/freebsd/386/sys.s | 239 - src/pkg/runtime/freebsd/amd64/defs.h | 198 - src/pkg/runtime/freebsd/amd64/rt0.s | 9 - src/pkg/runtime/freebsd/amd64/signal.c | 201 - src/pkg/runtime/freebsd/amd64/sys.s | 182 - src/pkg/runtime/freebsd/defs.c | 108 - src/pkg/runtime/freebsd/mem.c | 74 - src/pkg/runtime/freebsd/os.h | 12 - src/pkg/runtime/freebsd/signals.h | 52 - src/pkg/runtime/freebsd/thread.c | 201 - src/pkg/runtime/gc_test.go | 41 + src/pkg/runtime/goc2c.c | 727 -- src/pkg/runtime/hashmap.c | 370 +- src/pkg/runtime/hashmap.h | 5 +- src/pkg/runtime/iface.c | 178 +- src/pkg/runtime/linux/386/defs.h | 191 - src/pkg/runtime/linux/386/rt0.s | 9 - src/pkg/runtime/linux/386/signal.c | 184 - src/pkg/runtime/linux/386/sys.s | 344 - src/pkg/runtime/linux/amd64/defs.h | 236 - src/pkg/runtime/linux/amd64/rt0.s | 10 - src/pkg/runtime/linux/amd64/signal.c | 194 - src/pkg/runtime/linux/amd64/sys.s | 252 - src/pkg/runtime/linux/arm/defs.h | 149 - src/pkg/runtime/linux/arm/rt0.s | 6 - src/pkg/runtime/linux/arm/signal.c | 189 - src/pkg/runtime/linux/arm/sys.s | 319 - src/pkg/runtime/linux/defs.c | 95 - src/pkg/runtime/linux/defs1.c | 24 - src/pkg/runtime/linux/defs2.c | 120 - src/pkg/runtime/linux/defs_arm.c | 122 - src/pkg/runtime/linux/mem.c | 113 - src/pkg/runtime/linux/os.h | 19 - src/pkg/runtime/linux/signals.h | 51 - src/pkg/runtime/linux/thread.c | 320 - src/pkg/runtime/lock_futex.c | 156 + src/pkg/runtime/lock_sema.c | 230 + src/pkg/runtime/malloc.goc | 125 +- src/pkg/runtime/malloc.h | 59 +- src/pkg/runtime/malloc1.go | 26 + src/pkg/runtime/mallocrand.go | 93 + src/pkg/runtime/mallocrep.go | 72 + src/pkg/runtime/mallocrep1.go | 143 + src/pkg/runtime/mcache.c | 1 + src/pkg/runtime/mcentral.c | 1 + src/pkg/runtime/mem.go | 35 +- src/pkg/runtime/mem_darwin.c | 59 + src/pkg/runtime/mem_freebsd.c | 79 + src/pkg/runtime/mem_linux.c | 131 + src/pkg/runtime/mem_netbsd.c | 85 + src/pkg/runtime/mem_openbsd.c | 85 + src/pkg/runtime/mem_plan9.c | 68 + src/pkg/runtime/mem_windows.c | 71 + src/pkg/runtime/memmove_386.s | 86 + src/pkg/runtime/memmove_amd64.s | 88 + src/pkg/runtime/memmove_arm.s | 255 + src/pkg/runtime/memset_arm.s | 94 + src/pkg/runtime/mfinal.c | 193 +- src/pkg/runtime/mfinal_test.go | 64 + src/pkg/runtime/mfixalloc.c | 1 + src/pkg/runtime/mgc0.c | 737 +- src/pkg/runtime/mheap.c | 89 +- src/pkg/runtime/mkasmh.sh | 44 +- src/pkg/runtime/mkgodefs.sh | 39 - src/pkg/runtime/mkversion.c | 8 +- src/pkg/runtime/mprof.goc | 146 +- src/pkg/runtime/msize.c | 1 + src/pkg/runtime/openbsd/amd64/defs.h | 149 - src/pkg/runtime/openbsd/amd64/rt0.s | 8 - src/pkg/runtime/openbsd/amd64/signal.c | 199 - src/pkg/runtime/openbsd/amd64/sys.s | 221 - src/pkg/runtime/openbsd/defs.c | 103 - src/pkg/runtime/openbsd/mem.c | 74 - src/pkg/runtime/openbsd/os.h | 12 - src/pkg/runtime/openbsd/signals.h | 52 - src/pkg/runtime/openbsd/thread.c | 156 - src/pkg/runtime/os_darwin.h | 44 + src/pkg/runtime/os_freebsd.h | 26 + src/pkg/runtime/os_linux.h | 43 + src/pkg/runtime/os_netbsd.h | 21 + src/pkg/runtime/os_openbsd.h | 21 + src/pkg/runtime/os_plan9.h | 69 + src/pkg/runtime/os_windows.h | 32 + src/pkg/runtime/plan9/386/defs.h | 2 - src/pkg/runtime/plan9/386/rt0.s | 32 - src/pkg/runtime/plan9/386/signal.c | 24 - src/pkg/runtime/plan9/386/sys.s | 82 - src/pkg/runtime/plan9/mem.c | 67 - src/pkg/runtime/plan9/os.h | 57 - src/pkg/runtime/plan9/signals.h | 1 - src/pkg/runtime/plan9/thread.c | 174 - src/pkg/runtime/pprof/Makefile | 11 - src/pkg/runtime/pprof/pprof.go | 507 +- src/pkg/runtime/pprof/pprof_test.go | 17 +- src/pkg/runtime/print.c | 62 +- src/pkg/runtime/proc.c | 481 +- src/pkg/runtime/rt0_darwin_386.s | 8 + src/pkg/runtime/rt0_darwin_amd64.s | 10 + src/pkg/runtime/rt0_freebsd_386.s | 9 + src/pkg/runtime/rt0_freebsd_amd64.s | 9 + src/pkg/runtime/rt0_linux_386.s | 17 + src/pkg/runtime/rt0_linux_amd64.s | 10 + src/pkg/runtime/rt0_linux_arm.s | 60 + src/pkg/runtime/rt0_netbsd_386.s | 6 + src/pkg/runtime/rt0_netbsd_amd64.s | 8 + src/pkg/runtime/rt0_openbsd_386.s | 6 + src/pkg/runtime/rt0_openbsd_amd64.s | 8 + src/pkg/runtime/rt0_plan9_386.s | 32 + src/pkg/runtime/rt0_windows_386.s | 14 + src/pkg/runtime/rt0_windows_amd64.s | 13 + src/pkg/runtime/runtime-gdb.py | 83 +- src/pkg/runtime/runtime.c | 401 +- src/pkg/runtime/runtime.h | 247 +- src/pkg/runtime/runtime1.goc | 4 + src/pkg/runtime/runtime_linux_test.go | 29 + src/pkg/runtime/runtime_test.go | 40 + src/pkg/runtime/sema.goc | 14 +- src/pkg/runtime/sema_test.go | 100 - src/pkg/runtime/sig.go | 16 - src/pkg/runtime/signal_darwin_386.c | 140 + src/pkg/runtime/signal_darwin_amd64.c | 150 + src/pkg/runtime/signal_freebsd_386.c | 136 + src/pkg/runtime/signal_freebsd_amd64.c | 144 + src/pkg/runtime/signal_linux_386.c | 158 + src/pkg/runtime/signal_linux_amd64.c | 143 + src/pkg/runtime/signal_linux_arm.c | 140 + src/pkg/runtime/signal_netbsd_386.c | 132 + src/pkg/runtime/signal_netbsd_amd64.c | 141 + src/pkg/runtime/signal_openbsd_386.c | 132 + src/pkg/runtime/signal_openbsd_amd64.c | 141 + src/pkg/runtime/signal_plan9_386.c | 19 + src/pkg/runtime/signal_unix.c | 70 + src/pkg/runtime/signal_windows_386.c | 93 + src/pkg/runtime/signal_windows_amd64.c | 100 + src/pkg/runtime/signals_darwin.h | 50 + src/pkg/runtime/signals_freebsd.h | 51 + src/pkg/runtime/signals_linux.h | 83 + src/pkg/runtime/signals_netbsd.h | 51 + src/pkg/runtime/signals_openbsd.h | 51 + src/pkg/runtime/signals_plan9.h | 1 + src/pkg/runtime/signals_windows.h | 3 + src/pkg/runtime/sigqueue.goc | 112 +- src/pkg/runtime/slice.c | 65 +- src/pkg/runtime/softfloat64.go | 2 +- src/pkg/runtime/softfloat64_test.go | 2 +- src/pkg/runtime/softfloat_arm.c | 525 ++ src/pkg/runtime/stack.h | 9 +- src/pkg/runtime/stack_test.go | 1528 ++++ src/pkg/runtime/string.goc | 58 +- src/pkg/runtime/symtab.c | 71 +- src/pkg/runtime/sys_darwin_386.s | 378 + src/pkg/runtime/sys_darwin_amd64.s | 363 + src/pkg/runtime/sys_freebsd_386.s | 324 + src/pkg/runtime/sys_freebsd_amd64.s | 250 + src/pkg/runtime/sys_linux_386.s | 425 + src/pkg/runtime/sys_linux_amd64.s | 312 + src/pkg/runtime/sys_linux_arm.s | 387 + src/pkg/runtime/sys_netbsd_386.s | 325 + src/pkg/runtime/sys_netbsd_amd64.s | 268 + src/pkg/runtime/sys_openbsd_386.s | 325 + src/pkg/runtime/sys_openbsd_amd64.s | 268 + src/pkg/runtime/sys_plan9_386.s | 97 + src/pkg/runtime/sys_windows_386.s | 298 + src/pkg/runtime/sys_windows_amd64.s | 342 + src/pkg/runtime/syscall_windows.goc | 145 + src/pkg/runtime/syscall_windows_test.go | 243 + src/pkg/runtime/thread_darwin.c | 499 ++ src/pkg/runtime/thread_freebsd.c | 217 + src/pkg/runtime/thread_linux.c | 277 + src/pkg/runtime/thread_netbsd.c | 235 + src/pkg/runtime/thread_openbsd.c | 235 + src/pkg/runtime/thread_plan9.c | 269 + src/pkg/runtime/thread_windows.c | 430 + src/pkg/runtime/time.goc | 253 + src/pkg/runtime/traceback_arm.c | 217 + src/pkg/runtime/traceback_x86.c | 303 + src/pkg/runtime/type.go | 218 +- src/pkg/runtime/type.h | 3 +- src/pkg/runtime/vlop_386.s | 48 + src/pkg/runtime/vlop_arm.s | 190 + src/pkg/runtime/vlrt_386.c | 815 ++ src/pkg/runtime/vlrt_arm.c | 816 ++ src/pkg/runtime/windows/386/defs.h | 81 - src/pkg/runtime/windows/386/rt0.s | 14 - src/pkg/runtime/windows/386/signal.c | 98 - src/pkg/runtime/windows/386/sys.s | 256 - src/pkg/runtime/windows/amd64/defs.h | 40 - src/pkg/runtime/windows/amd64/rt0.s | 13 - src/pkg/runtime/windows/amd64/signal.c | 20 - src/pkg/runtime/windows/amd64/sys.s | 130 - src/pkg/runtime/windows/defs.c | 37 - src/pkg/runtime/windows/mem.c | 70 - src/pkg/runtime/windows/os.h | 30 - src/pkg/runtime/windows/signals.h | 3 - src/pkg/runtime/windows/syscall.goc | 67 - src/pkg/runtime/windows/thread.c | 432 - src/pkg/scanner/Makefile | 11 - src/pkg/scanner/scanner.go | 660 -- src/pkg/scanner/scanner_test.go | 566 -- src/pkg/smtp/Makefile | 12 - src/pkg/smtp/auth.go | 69 - src/pkg/smtp/smtp.go | 294 - src/pkg/smtp/smtp_test.go | 182 - src/pkg/sort/Makefile | 12 - src/pkg/sort/example_interface_test.go | 77 + src/pkg/sort/example_reverse_test.go | 30 + src/pkg/sort/example_test.go | 17 + src/pkg/sort/export_test.go | 9 + src/pkg/sort/sort.go | 69 +- src/pkg/sort/sort_test.go | 70 +- src/pkg/strconv/Makefile | 17 - src/pkg/strconv/atob.go | 21 +- src/pkg/strconv/atob_test.go | 13 +- src/pkg/strconv/atof.go | 155 +- src/pkg/strconv/atof_test.go | 204 +- src/pkg/strconv/atoi.go | 151 +- src/pkg/strconv/atoi_test.go | 121 +- src/pkg/strconv/decimal.go | 71 +- src/pkg/strconv/decimal_test.go | 20 +- src/pkg/strconv/extfloat.go | 501 ++ src/pkg/strconv/fp_test.go | 22 +- src/pkg/strconv/ftoa.go | 263 +- src/pkg/strconv/ftoa_test.go | 126 +- src/pkg/strconv/internal_test.go | 6 +- src/pkg/strconv/isprint.go | 521 ++ src/pkg/strconv/itoa.go | 146 +- src/pkg/strconv/itoa_test.go | 154 +- src/pkg/strconv/makeisprint.go | 162 + src/pkg/strconv/quote.go | 293 +- src/pkg/strconv/quote_test.go | 56 +- src/pkg/strings/Makefile | 12 - src/pkg/strings/example_test.go | 182 + src/pkg/strings/export_test.go | 9 + src/pkg/strings/reader.go | 88 +- src/pkg/strings/reader_test.go | 88 + src/pkg/strings/replace.go | 312 + src/pkg/strings/replace_test.go | 174 + src/pkg/strings/strings.go | 158 +- src/pkg/strings/strings_test.go | 313 +- src/pkg/sync/Makefile | 15 - src/pkg/sync/atomic/Makefile | 18 - src/pkg/sync/atomic/asm_386.s | 55 + src/pkg/sync/atomic/asm_amd64.s | 47 + src/pkg/sync/atomic/asm_arm.s | 24 + src/pkg/sync/atomic/asm_linux_arm.s | 39 + src/pkg/sync/atomic/atomic_test.go | 639 +- src/pkg/sync/atomic/doc.go | 39 +- src/pkg/sync/cond.go | 15 +- src/pkg/sync/example_test.go | 54 + src/pkg/sync/export_test.go | 9 + src/pkg/sync/mutex.go | 11 +- src/pkg/sync/mutex_test.go | 6 +- src/pkg/sync/once_test.go | 2 +- src/pkg/sync/runtime.go | 18 + src/pkg/sync/runtime_sema_test.go | 101 + src/pkg/sync/rwmutex.go | 13 +- src/pkg/sync/rwmutex_test.go | 2 +- src/pkg/sync/waitgroup.go | 24 +- src/pkg/syscall/Makefile | 58 - src/pkg/syscall/asm_linux_386.s | 14 +- src/pkg/syscall/asm_netbsd_386.s | 137 + src/pkg/syscall/asm_netbsd_amd64.s | 130 + src/pkg/syscall/asm_openbsd_386.s | 137 + src/pkg/syscall/asm_openbsd_amd64.s | 130 + src/pkg/syscall/bpf_bsd.go | 162 +- src/pkg/syscall/dll_windows.go | 249 + src/pkg/syscall/env_plan9.go | 128 + src/pkg/syscall/env_unix.go | 109 + src/pkg/syscall/env_windows.go | 77 + src/pkg/syscall/exec_bsd.go | 223 + src/pkg/syscall/exec_linux.go | 234 + src/pkg/syscall/exec_plan9.go | 37 +- src/pkg/syscall/exec_unix.go | 252 +- src/pkg/syscall/exec_windows.go | 50 +- src/pkg/syscall/lsf_linux.go | 26 +- src/pkg/syscall/mkall.sh | 98 +- src/pkg/syscall/mkerrors.sh | 177 +- src/pkg/syscall/mkerrors_windows.sh | 6 +- src/pkg/syscall/mksyscall.pl | 51 +- src/pkg/syscall/mksyscall_windows.pl | 58 +- src/pkg/syscall/mksysctl_openbsd.pl | 257 + src/pkg/syscall/mksysnum_linux.pl | 2 +- src/pkg/syscall/mksysnum_netbsd.pl | 51 + src/pkg/syscall/mksysnum_openbsd.pl | 43 + src/pkg/syscall/netlink_linux.go | 49 +- src/pkg/syscall/route_bsd.go | 99 +- src/pkg/syscall/route_darwin.go | 2 +- src/pkg/syscall/route_freebsd.go | 2 +- src/pkg/syscall/route_netbsd.go | 35 + src/pkg/syscall/route_openbsd.go | 35 + src/pkg/syscall/security_windows.go | 359 + src/pkg/syscall/sockcmsg_linux.go | 4 +- src/pkg/syscall/sockcmsg_unix.go | 16 +- src/pkg/syscall/syscall.go | 23 +- src/pkg/syscall/syscall_386.go | 7 - src/pkg/syscall/syscall_amd64.go | 7 - src/pkg/syscall/syscall_arm.go | 7 - src/pkg/syscall/syscall_bsd.go | 329 +- src/pkg/syscall/syscall_darwin.go | 191 +- src/pkg/syscall/syscall_darwin_386.go | 6 +- src/pkg/syscall/syscall_darwin_amd64.go | 4 +- src/pkg/syscall/syscall_freebsd.go | 194 +- src/pkg/syscall/syscall_freebsd_386.go | 2 +- src/pkg/syscall/syscall_linux.go | 469 +- src/pkg/syscall/syscall_linux_386.go | 189 +- src/pkg/syscall/syscall_linux_amd64.go | 92 +- src/pkg/syscall/syscall_linux_arm.go | 129 +- src/pkg/syscall/syscall_netbsd.go | 434 + src/pkg/syscall/syscall_netbsd_386.go | 42 + src/pkg/syscall/syscall_netbsd_amd64.go | 42 + src/pkg/syscall/syscall_openbsd.go | 281 + src/pkg/syscall/syscall_openbsd_386.go | 42 + src/pkg/syscall/syscall_openbsd_amd64.go | 42 + src/pkg/syscall/syscall_plan9.go | 106 +- src/pkg/syscall/syscall_unix.go | 81 +- src/pkg/syscall/syscall_windows.go | 563 +- src/pkg/syscall/types_darwin.c | 180 - src/pkg/syscall/types_darwin.go | 228 + src/pkg/syscall/types_freebsd.c | 190 - src/pkg/syscall/types_freebsd.go | 240 + src/pkg/syscall/types_linux.c | 266 - src/pkg/syscall/types_linux.go | 452 + src/pkg/syscall/types_netbsd.go | 229 + src/pkg/syscall/types_openbsd.go | 229 + src/pkg/syscall/types_plan9.c | 2 + src/pkg/syscall/zerrors_darwin_386.go | 2289 ++--- src/pkg/syscall/zerrors_darwin_amd64.go | 2289 ++--- src/pkg/syscall/zerrors_freebsd_386.go | 393 +- src/pkg/syscall/zerrors_freebsd_amd64.go | 393 +- src/pkg/syscall/zerrors_linux_386.go | 651 +- src/pkg/syscall/zerrors_linux_amd64.go | 653 +- src/pkg/syscall/zerrors_linux_arm.go | 639 +- src/pkg/syscall/zerrors_netbsd_386.go | 1368 +++ src/pkg/syscall/zerrors_netbsd_amd64.go | 1368 +++ src/pkg/syscall/zerrors_openbsd_386.go | 1441 ++++ src/pkg/syscall/zerrors_openbsd_amd64.go | 1441 ++++ src/pkg/syscall/zerrors_plan9_386.go | 21 +- src/pkg/syscall/zerrors_windows.go | 8 +- src/pkg/syscall/zsyscall_darwin_386.go | 594 +- src/pkg/syscall/zsyscall_darwin_amd64.go | 594 +- src/pkg/syscall/zsyscall_freebsd_386.go | 582 +- src/pkg/syscall/zsyscall_freebsd_amd64.go | 582 +- src/pkg/syscall/zsyscall_linux_386.go | 710 +- src/pkg/syscall/zsyscall_linux_amd64.go | 806 +- src/pkg/syscall/zsyscall_linux_arm.go | 830 +- src/pkg/syscall/zsyscall_netbsd_386.go | 1086 +++ src/pkg/syscall/zsyscall_netbsd_amd64.go | 1086 +++ src/pkg/syscall/zsyscall_openbsd_386.go | 1133 +++ src/pkg/syscall/zsyscall_openbsd_amd64.go | 1133 +++ src/pkg/syscall/zsyscall_plan9_386.go | 96 +- src/pkg/syscall/zsyscall_windows_386.go | 1275 +-- src/pkg/syscall/zsyscall_windows_amd64.go | 1275 +-- src/pkg/syscall/zsysctl_openbsd.go | 292 + src/pkg/syscall/zsysnum_darwin_386.go | 701 +- src/pkg/syscall/zsysnum_darwin_amd64.go | 701 +- src/pkg/syscall/zsysnum_freebsd_386.go | 27 +- src/pkg/syscall/zsysnum_freebsd_amd64.go | 27 +- src/pkg/syscall/zsysnum_linux_386.go | 4 + src/pkg/syscall/zsysnum_linux_amd64.go | 4 + src/pkg/syscall/zsysnum_linux_arm.go | 680 +- src/pkg/syscall/zsysnum_netbsd_386.go | 274 + src/pkg/syscall/zsysnum_netbsd_amd64.go | 274 + src/pkg/syscall/zsysnum_openbsd_386.go | 207 + src/pkg/syscall/zsysnum_openbsd_amd64.go | 207 + src/pkg/syscall/ztypes_darwin_386.go | 216 +- src/pkg/syscall/ztypes_darwin_amd64.go | 252 +- src/pkg/syscall/ztypes_freebsd_386.go | 231 +- src/pkg/syscall/ztypes_freebsd_amd64.go | 268 +- src/pkg/syscall/ztypes_linux_386.go | 476 +- src/pkg/syscall/ztypes_linux_amd64.go | 508 +- src/pkg/syscall/ztypes_linux_arm.go | 507 +- src/pkg/syscall/ztypes_netbsd_386.go | 406 + src/pkg/syscall/ztypes_netbsd_amd64.go | 412 + src/pkg/syscall/ztypes_openbsd_386.go | 405 + src/pkg/syscall/ztypes_openbsd_amd64.go | 412 + src/pkg/syscall/ztypes_windows.go | 364 +- src/pkg/syslog/Makefile | 12 - src/pkg/syslog/syslog.go | 149 - src/pkg/syslog/syslog_test.go | 113 - src/pkg/syslog/syslog_unix.go | 31 - src/pkg/tabwriter/Makefile | 11 - src/pkg/tabwriter/tabwriter.go | 561 -- src/pkg/tabwriter/tabwriter_test.go | 615 -- src/pkg/template/Makefile | 15 - src/pkg/template/doc.go | 313 - src/pkg/template/exec.go | 674 -- src/pkg/template/exec_test.go | 613 -- src/pkg/template/funcs.go | 368 - src/pkg/template/helper.go | 238 - src/pkg/template/parse.go | 85 - src/pkg/template/parse/Makefile | 14 - src/pkg/template/parse/lex.go | 472 -- src/pkg/template/parse/lex_test.go | 223 - src/pkg/template/parse/node.go | 470 -- src/pkg/template/parse/parse.go | 436 - src/pkg/template/parse/parse_test.go | 259 - src/pkg/template/parse/set.go | 50 - src/pkg/template/set.go | 108 - src/pkg/template/set_test.go | 239 - src/pkg/template/testdata/file1.tmpl | 2 - src/pkg/template/testdata/file2.tmpl | 2 - src/pkg/template/testdata/tmpl1.tmpl | 1 - src/pkg/template/testdata/tmpl2.tmpl | 1 - src/pkg/testing/Makefile | 12 - src/pkg/testing/benchmark.go | 127 +- src/pkg/testing/example.go | 82 + src/pkg/testing/iotest/Makefile | 13 - src/pkg/testing/iotest/logger.go | 5 +- src/pkg/testing/iotest/reader.go | 14 +- src/pkg/testing/iotest/writer.go | 7 +- src/pkg/testing/quick/Makefile | 11 - src/pkg/testing/quick/quick.go | 28 +- src/pkg/testing/quick/quick_test.go | 5 +- src/pkg/testing/script/Makefile | 11 - src/pkg/testing/script/script.go | 359 - src/pkg/testing/script/script_test.go | 75 - src/pkg/testing/testing.go | 324 +- src/pkg/text/scanner/scanner.go | 669 ++ src/pkg/text/scanner/scanner_test.go | 563 ++ src/pkg/text/tabwriter/example_test.go | 38 + src/pkg/text/tabwriter/tabwriter.go | 554 ++ src/pkg/text/tabwriter/tabwriter_test.go | 615 ++ src/pkg/text/template/doc.go | 358 + src/pkg/text/template/example_test.go | 71 + src/pkg/text/template/examplefiles_test.go | 182 + src/pkg/text/template/examplefunc_test.go | 54 + src/pkg/text/template/exec.go | 722 ++ src/pkg/text/template/exec_test.go | 722 ++ src/pkg/text/template/funcs.go | 410 + src/pkg/text/template/helper.go | 108 + src/pkg/text/template/multi_test.go | 280 + src/pkg/text/template/parse/lex.go | 507 ++ src/pkg/text/template/parse/lex_test.go | 257 + src/pkg/text/template/parse/node.go | 608 ++ src/pkg/text/template/parse/parse.go | 530 ++ src/pkg/text/template/parse/parse_test.go | 318 + src/pkg/text/template/template.go | 214 + src/pkg/text/template/testdata/file1.tmpl | 2 + src/pkg/text/template/testdata/file2.tmpl | 2 + src/pkg/text/template/testdata/tmpl1.tmpl | 3 + src/pkg/text/template/testdata/tmpl2.tmpl | 3 + src/pkg/time/Makefile | 46 - src/pkg/time/example_test.go | 58 + src/pkg/time/format.go | 712 +- src/pkg/time/internal_test.go | 13 + src/pkg/time/sleep.go | 212 +- src/pkg/time/sleep_test.go | 130 +- src/pkg/time/sys.go | 51 - src/pkg/time/sys_plan9.go | 66 +- src/pkg/time/sys_posix.go | 18 - src/pkg/time/sys_unix.go | 76 + src/pkg/time/sys_windows.go | 73 + src/pkg/time/tick.go | 188 +- src/pkg/time/tick_test.go | 24 +- src/pkg/time/time.go | 1069 ++- src/pkg/time/time_test.go | 695 +- src/pkg/time/zoneinfo.go | 204 + src/pkg/time/zoneinfo_plan9.go | 145 +- src/pkg/time/zoneinfo_posix.go | 62 - src/pkg/time/zoneinfo_read.go | 341 + src/pkg/time/zoneinfo_unix.go | 227 +- src/pkg/time/zoneinfo_windows.go | 275 +- src/pkg/try/Makefile | 11 - src/pkg/try/try.go | 174 - src/pkg/try/try_test.go | 60 - src/pkg/unicode/Makefile | 24 +- src/pkg/unicode/casetables.go | 4 +- src/pkg/unicode/digit.go | 8 +- src/pkg/unicode/digit_test.go | 6 +- src/pkg/unicode/graphic.go | 77 +- src/pkg/unicode/graphic_test.go | 20 +- src/pkg/unicode/letter.go | 170 +- src/pkg/unicode/letter_test.go | 29 +- src/pkg/unicode/maketables.go | 197 +- src/pkg/unicode/script_test.go | 8 +- src/pkg/unicode/tables.go | 6981 +++++++-------- src/pkg/unicode/utf16/export_test.go | 11 + src/pkg/unicode/utf16/utf16.go | 108 + src/pkg/unicode/utf16/utf16_test.go | 101 + src/pkg/unicode/utf8/utf8.go | 397 + src/pkg/unicode/utf8/utf8_test.go | 365 + src/pkg/unsafe/unsafe.go | 34 +- src/pkg/url/Makefile | 11 - src/pkg/url/url.go | 677 -- src/pkg/url/url_test.go | 698 -- src/pkg/utf16/Makefile | 11 - src/pkg/utf16/utf16.go | 101 - src/pkg/utf16/utf16_test.go | 118 - src/pkg/utf8/Makefile | 12 - src/pkg/utf8/string.go | 211 - src/pkg/utf8/string_test.go | 114 - src/pkg/utf8/utf8.go | 356 - src/pkg/utf8/utf8_test.go | 315 - src/pkg/websocket/Makefile | 9 - src/pkg/websocket/client.go | 323 - src/pkg/websocket/server.go | 219 - src/pkg/websocket/websocket.go | 192 - src/pkg/websocket/websocket_test.go | 271 - src/pkg/xml/Makefile | 14 - src/pkg/xml/atom_test.go | 50 - src/pkg/xml/embed_test.go | 124 - src/pkg/xml/marshal.go | 228 - src/pkg/xml/marshal_test.go | 305 - src/pkg/xml/read.go | 631 -- src/pkg/xml/read_test.go | 371 - src/pkg/xml/xml.go | 1694 ---- src/pkg/xml/xml_test.go | 609 -- src/quietgcc.bash | 44 - src/run.bash | 103 +- src/run.bat | 76 + src/sudo.bash | 17 +- src/version.bash | 50 - 2577 files changed, 283739 insertions(+), 222628 deletions(-) delete mode 100644 src/Make.ccmd delete mode 100644 src/Make.clib delete mode 100644 src/Make.cmd delete mode 100644 src/Make.common create mode 100644 src/Make.dist delete mode 100644 src/Make.inc delete mode 100644 src/Make.pkg delete mode 100755 src/all-qemu.bash create mode 100644 src/all.bat create mode 100644 src/clean.bat create mode 100644 src/cmd/5a/y.tab.c create mode 100644 src/cmd/5a/y.tab.h create mode 100644 src/cmd/6a/y.tab.c create mode 100644 src/cmd/6a/y.tab.h create mode 100644 src/cmd/8a/y.tab.c create mode 100644 src/cmd/8a/y.tab.h delete mode 100644 src/cmd/Makefile create mode 100644 src/cmd/addr2line/main.c create mode 100644 src/cmd/api/goapi.go create mode 100644 src/cmd/api/goapi_test.go create mode 100644 src/cmd/api/testdata/src/pkg/p1/golden.txt create mode 100644 src/cmd/api/testdata/src/pkg/p1/p1.go create mode 100644 src/cmd/api/testdata/src/pkg/p2/golden.txt create mode 100644 src/cmd/api/testdata/src/pkg/p2/p2.go create mode 100644 src/cmd/cc/y.tab.c create mode 100644 src/cmd/cc/y.tab.h delete mode 100644 src/cmd/cgo/Makefile create mode 100644 src/cmd/cgo/godefs.go delete mode 100644 src/cmd/cov/Makefile delete mode 100644 src/cmd/cov/doc.go delete mode 100644 src/cmd/cov/main.c delete mode 100644 src/cmd/cov/tree.c delete mode 100644 src/cmd/cov/tree.h create mode 100644 src/cmd/dist/README create mode 100644 src/cmd/dist/a.h create mode 100644 src/cmd/dist/arg.h create mode 100644 src/cmd/dist/buf.c create mode 100644 src/cmd/dist/build.c create mode 100644 src/cmd/dist/buildgc.c create mode 100644 src/cmd/dist/buildruntime.c create mode 100644 src/cmd/dist/goc2c.c create mode 100644 src/cmd/dist/main.c create mode 100644 src/cmd/dist/unix.c create mode 100644 src/cmd/dist/windows.c delete mode 100644 src/cmd/ebnflint/Makefile delete mode 100644 src/cmd/ebnflint/doc.go delete mode 100644 src/cmd/ebnflint/ebnflint.go create mode 100644 src/cmd/fix/doc.go create mode 100644 src/cmd/fix/error.go create mode 100644 src/cmd/fix/error_test.go create mode 100644 src/cmd/fix/filepath.go create mode 100644 src/cmd/fix/filepath_test.go create mode 100644 src/cmd/fix/fix.go create mode 100644 src/cmd/fix/go1pkgrename.go create mode 100644 src/cmd/fix/go1pkgrename_test.go create mode 100644 src/cmd/fix/go1rename.go create mode 100644 src/cmd/fix/go1rename_test.go create mode 100644 src/cmd/fix/googlecode.go create mode 100644 src/cmd/fix/googlecode_test.go create mode 100644 src/cmd/fix/hashsum.go create mode 100644 src/cmd/fix/hashsum_test.go create mode 100644 src/cmd/fix/hmacnew.go create mode 100644 src/cmd/fix/hmacnew_test.go create mode 100644 src/cmd/fix/htmlerr.go create mode 100644 src/cmd/fix/htmlerr_test.go create mode 100644 src/cmd/fix/httpfinalurl.go create mode 100644 src/cmd/fix/httpfinalurl_test.go create mode 100644 src/cmd/fix/httpfs.go create mode 100644 src/cmd/fix/httpfs_test.go create mode 100644 src/cmd/fix/httpheaders.go create mode 100644 src/cmd/fix/httpheaders_test.go create mode 100644 src/cmd/fix/httpserver.go create mode 100644 src/cmd/fix/httpserver_test.go create mode 100644 src/cmd/fix/imagecolor.go create mode 100644 src/cmd/fix/imagecolor_test.go create mode 100644 src/cmd/fix/imagenew.go create mode 100644 src/cmd/fix/imagenew_test.go create mode 100644 src/cmd/fix/imageycbcr.go create mode 100644 src/cmd/fix/imageycbcr_test.go create mode 100644 src/cmd/fix/import_test.go create mode 100644 src/cmd/fix/iocopyn.go create mode 100644 src/cmd/fix/iocopyn_test.go create mode 100644 src/cmd/fix/main.go create mode 100644 src/cmd/fix/main_test.go create mode 100644 src/cmd/fix/mapdelete.go create mode 100644 src/cmd/fix/mapdelete_test.go create mode 100644 src/cmd/fix/math.go create mode 100644 src/cmd/fix/math_test.go create mode 100644 src/cmd/fix/netdial.go create mode 100644 src/cmd/fix/netdial_test.go create mode 100644 src/cmd/fix/netudpgroup.go create mode 100644 src/cmd/fix/netudpgroup_test.go create mode 100644 src/cmd/fix/newwriter.go create mode 100644 src/cmd/fix/newwriter_test.go create mode 100644 src/cmd/fix/oserrorstring.go create mode 100644 src/cmd/fix/oserrorstring_test.go create mode 100644 src/cmd/fix/osopen.go create mode 100644 src/cmd/fix/osopen_test.go create mode 100644 src/cmd/fix/procattr.go create mode 100644 src/cmd/fix/procattr_test.go create mode 100644 src/cmd/fix/reflect.go create mode 100644 src/cmd/fix/reflect_test.go create mode 100644 src/cmd/fix/signal.go create mode 100644 src/cmd/fix/signal_test.go create mode 100644 src/cmd/fix/sorthelpers.go create mode 100644 src/cmd/fix/sorthelpers_test.go create mode 100644 src/cmd/fix/sortslice.go create mode 100644 src/cmd/fix/sortslice_test.go create mode 100644 src/cmd/fix/strconv.go create mode 100644 src/cmd/fix/strconv_test.go create mode 100644 src/cmd/fix/stringssplit.go create mode 100644 src/cmd/fix/stringssplit_test.go create mode 100644 src/cmd/fix/template.go create mode 100644 src/cmd/fix/template_test.go create mode 100644 src/cmd/fix/testdata/reflect.asn1.go.in create mode 100644 src/cmd/fix/testdata/reflect.asn1.go.out create mode 100644 src/cmd/fix/testdata/reflect.datafmt.go.in create mode 100644 src/cmd/fix/testdata/reflect.datafmt.go.out create mode 100644 src/cmd/fix/testdata/reflect.decode.go.in create mode 100644 src/cmd/fix/testdata/reflect.decode.go.out create mode 100644 src/cmd/fix/testdata/reflect.decoder.go.in create mode 100644 src/cmd/fix/testdata/reflect.decoder.go.out create mode 100644 src/cmd/fix/testdata/reflect.dnsmsg.go.in create mode 100644 src/cmd/fix/testdata/reflect.dnsmsg.go.out create mode 100644 src/cmd/fix/testdata/reflect.encode.go.in create mode 100644 src/cmd/fix/testdata/reflect.encode.go.out create mode 100644 src/cmd/fix/testdata/reflect.encoder.go.in create mode 100644 src/cmd/fix/testdata/reflect.encoder.go.out create mode 100644 src/cmd/fix/testdata/reflect.export.go.in create mode 100644 src/cmd/fix/testdata/reflect.export.go.out create mode 100644 src/cmd/fix/testdata/reflect.print.go.in create mode 100644 src/cmd/fix/testdata/reflect.print.go.out create mode 100644 src/cmd/fix/testdata/reflect.quick.go.in create mode 100644 src/cmd/fix/testdata/reflect.quick.go.out create mode 100644 src/cmd/fix/testdata/reflect.read.go.in create mode 100644 src/cmd/fix/testdata/reflect.read.go.out create mode 100644 src/cmd/fix/testdata/reflect.scan.go.in create mode 100644 src/cmd/fix/testdata/reflect.scan.go.out create mode 100644 src/cmd/fix/testdata/reflect.script.go.in create mode 100644 src/cmd/fix/testdata/reflect.script.go.out create mode 100644 src/cmd/fix/testdata/reflect.template.go.in create mode 100644 src/cmd/fix/testdata/reflect.template.go.out create mode 100644 src/cmd/fix/testdata/reflect.type.go.in create mode 100644 src/cmd/fix/testdata/reflect.type.go.out create mode 100644 src/cmd/fix/timefileinfo.go create mode 100644 src/cmd/fix/timefileinfo_test.go create mode 100644 src/cmd/fix/typecheck.go create mode 100644 src/cmd/fix/url.go create mode 100644 src/cmd/fix/url2.go create mode 100644 src/cmd/fix/url2_test.go create mode 100644 src/cmd/fix/url_test.go create mode 100644 src/cmd/fix/xmlapi.go create mode 100644 src/cmd/fix/xmlapi_test.go create mode 100644 src/cmd/gc/builtin.c delete mode 100644 src/cmd/gc/builtin.c.boot create mode 100644 src/cmd/gc/esc.c create mode 100644 src/cmd/gc/fmt.c create mode 100644 src/cmd/gc/inl.c create mode 100644 src/cmd/gc/order.c delete mode 100644 src/cmd/gc/print.c create mode 100644 src/cmd/gc/y.tab.c create mode 100644 src/cmd/gc/y.tab.h create mode 100644 src/cmd/gc/yerr.h create mode 100644 src/cmd/go/bootstrap.go create mode 100644 src/cmd/go/build.go create mode 100644 src/cmd/go/clean.go create mode 100644 src/cmd/go/discovery.go create mode 100644 src/cmd/go/doc.go create mode 100644 src/cmd/go/env.go create mode 100644 src/cmd/go/fix.go create mode 100644 src/cmd/go/fmt.go create mode 100644 src/cmd/go/get.go create mode 100644 src/cmd/go/help.go create mode 100644 src/cmd/go/http.go create mode 100644 src/cmd/go/list.go create mode 100644 src/cmd/go/main.go create mode 100644 src/cmd/go/match_test.go create mode 100755 src/cmd/go/mkdoc.sh create mode 100644 src/cmd/go/pkg.go create mode 100644 src/cmd/go/run.go create mode 100755 src/cmd/go/script create mode 100644 src/cmd/go/script.txt create mode 100644 src/cmd/go/tag_test.go create mode 100755 src/cmd/go/test.bash create mode 100644 src/cmd/go/test.go create mode 100644 src/cmd/go/testdata/errmsg/x.go create mode 100644 src/cmd/go/testdata/errmsg/x1_test.go create mode 100644 src/cmd/go/testdata/errmsg/x_test.go create mode 100644 src/cmd/go/testdata/local/easy.go create mode 100644 src/cmd/go/testdata/local/easysub/easysub.go create mode 100644 src/cmd/go/testdata/local/easysub/main.go create mode 100644 src/cmd/go/testdata/local/hard.go create mode 100644 src/cmd/go/testdata/local/sub/sub.go create mode 100644 src/cmd/go/testdata/local/sub/sub/subsub.go create mode 100644 src/cmd/go/testdata/src/go-cmd-test/helloworld.go create mode 100644 src/cmd/go/testdata/testimport/p.go create mode 100644 src/cmd/go/testdata/testimport/p1/p1.go create mode 100644 src/cmd/go/testdata/testimport/p2/p2.go create mode 100644 src/cmd/go/testdata/testimport/p_test.go create mode 100644 src/cmd/go/testdata/testimport/x_test.go create mode 100644 src/cmd/go/testflag.go create mode 100644 src/cmd/go/tool.go create mode 100644 src/cmd/go/vcs.go create mode 100644 src/cmd/go/version.go create mode 100644 src/cmd/go/vet.go delete mode 100644 src/cmd/godefs/Makefile delete mode 100644 src/cmd/godefs/a.h delete mode 100644 src/cmd/godefs/doc.go delete mode 100644 src/cmd/godefs/main.c delete mode 100644 src/cmd/godefs/stabs.c delete mode 100755 src/cmd/godefs/test.sh delete mode 100644 src/cmd/godefs/testdata.c delete mode 100644 src/cmd/godefs/testdata_darwin_386.golden delete mode 100644 src/cmd/godefs/testdata_darwin_amd64.golden delete mode 100644 src/cmd/godefs/util.c delete mode 100644 src/cmd/godoc/Makefile create mode 100644 src/cmd/godoc/README.godoc-app delete mode 100644 src/cmd/godoc/appconfig.go delete mode 100644 src/cmd/godoc/httpzip.go delete mode 100644 src/cmd/godoc/mapping.go create mode 100644 src/cmd/godoc/setup-godoc-app.bash mode change 100755 => 100644 src/cmd/godoc/snippet.go create mode 100644 src/cmd/godoc/template.go create mode 100644 src/cmd/godoc/throttle.go delete mode 100644 src/cmd/gofix/Makefile delete mode 100644 src/cmd/gofix/doc.go delete mode 100644 src/cmd/gofix/filepath.go delete mode 100644 src/cmd/gofix/filepath_test.go delete mode 100644 src/cmd/gofix/fix.go delete mode 100644 src/cmd/gofix/httpfinalurl.go delete mode 100644 src/cmd/gofix/httpfinalurl_test.go delete mode 100644 src/cmd/gofix/httpfs.go delete mode 100644 src/cmd/gofix/httpfs_test.go delete mode 100644 src/cmd/gofix/httpheaders.go delete mode 100644 src/cmd/gofix/httpheaders_test.go delete mode 100644 src/cmd/gofix/httpserver.go delete mode 100644 src/cmd/gofix/httpserver_test.go delete mode 100644 src/cmd/gofix/main.go delete mode 100644 src/cmd/gofix/main_test.go delete mode 100644 src/cmd/gofix/netdial.go delete mode 100644 src/cmd/gofix/netdial_test.go delete mode 100644 src/cmd/gofix/oserrorstring.go delete mode 100644 src/cmd/gofix/oserrorstring_test.go delete mode 100644 src/cmd/gofix/osopen.go delete mode 100644 src/cmd/gofix/osopen_test.go delete mode 100644 src/cmd/gofix/procattr.go delete mode 100644 src/cmd/gofix/procattr_test.go delete mode 100644 src/cmd/gofix/reflect.go delete mode 100644 src/cmd/gofix/reflect_test.go delete mode 100644 src/cmd/gofix/signal.go delete mode 100644 src/cmd/gofix/signal_test.go delete mode 100644 src/cmd/gofix/sorthelpers.go delete mode 100644 src/cmd/gofix/sorthelpers_test.go delete mode 100644 src/cmd/gofix/sortslice.go delete mode 100644 src/cmd/gofix/sortslice_test.go delete mode 100644 src/cmd/gofix/stringssplit.go delete mode 100644 src/cmd/gofix/stringssplit_test.go delete mode 100644 src/cmd/gofix/testdata/reflect.asn1.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.asn1.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.datafmt.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.datafmt.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.decode.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.decode.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.decoder.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.decoder.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.dnsmsg.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.dnsmsg.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.encode.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.encode.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.encoder.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.encoder.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.export.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.export.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.print.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.print.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.quick.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.quick.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.read.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.read.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.scan.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.scan.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.script.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.script.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.template.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.template.go.out delete mode 100644 src/cmd/gofix/testdata/reflect.type.go.in delete mode 100644 src/cmd/gofix/testdata/reflect.type.go.out delete mode 100644 src/cmd/gofix/typecheck.go delete mode 100644 src/cmd/gofix/url.go delete mode 100644 src/cmd/gofix/url_test.go delete mode 100644 src/cmd/gofmt/Makefile create mode 100644 src/cmd/gofmt/long_test.go delete mode 100755 src/cmd/gofmt/test.sh create mode 100644 src/cmd/gofmt/testdata/comments.golden create mode 100644 src/cmd/gofmt/testdata/comments.input create mode 100644 src/cmd/gofmt/testdata/import.golden create mode 100644 src/cmd/gofmt/testdata/import.input create mode 100644 src/cmd/gofmt/testdata/old.golden create mode 100644 src/cmd/gofmt/testdata/old.input create mode 100644 src/cmd/gofmt/testdata/rewrite3.golden create mode 100644 src/cmd/gofmt/testdata/rewrite3.input create mode 100644 src/cmd/gofmt/testdata/rewrite4.golden create mode 100644 src/cmd/gofmt/testdata/rewrite4.input create mode 100644 src/cmd/gofmt/testdata/stdin1.golden create mode 100644 src/cmd/gofmt/testdata/stdin1.golden.gofmt create mode 100644 src/cmd/gofmt/testdata/stdin1.input create mode 100644 src/cmd/gofmt/testdata/stdin1.input.gofmt create mode 100644 src/cmd/gofmt/testdata/stdin2.golden create mode 100644 src/cmd/gofmt/testdata/stdin2.golden.gofmt create mode 100644 src/cmd/gofmt/testdata/stdin2.input create mode 100644 src/cmd/gofmt/testdata/stdin2.input.gofmt create mode 100644 src/cmd/gofmt/testdata/stdin3.golden create mode 100644 src/cmd/gofmt/testdata/stdin3.golden.gofmt create mode 100644 src/cmd/gofmt/testdata/stdin3.input create mode 100644 src/cmd/gofmt/testdata/stdin3.input.gofmt create mode 100644 src/cmd/gofmt/testdata/stdin4.golden create mode 100644 src/cmd/gofmt/testdata/stdin4.golden.gofmt create mode 100644 src/cmd/gofmt/testdata/stdin4.input create mode 100644 src/cmd/gofmt/testdata/stdin4.input.gofmt delete mode 100644 src/cmd/goinstall/Makefile delete mode 100644 src/cmd/goinstall/doc.go delete mode 100644 src/cmd/goinstall/download.go delete mode 100644 src/cmd/goinstall/main.go delete mode 100644 src/cmd/goinstall/make.go delete mode 100644 src/cmd/goinstall/tag_test.go delete mode 100644 src/cmd/gomake/doc.go delete mode 100644 src/cmd/gopack/Makefile delete mode 100644 src/cmd/gopack/ar.c delete mode 100644 src/cmd/gopack/doc.go delete mode 100644 src/cmd/gotest/Makefile delete mode 100644 src/cmd/gotest/doc.go delete mode 100644 src/cmd/gotest/flag.go delete mode 100644 src/cmd/gotest/gotest.go delete mode 100644 src/cmd/gotry/Makefile delete mode 100755 src/cmd/gotry/gotry delete mode 100644 src/cmd/gotype/Makefile delete mode 100644 src/cmd/gotype/doc.go delete mode 100644 src/cmd/gotype/gotype.go delete mode 100644 src/cmd/gotype/gotype_test.go delete mode 100644 src/cmd/gotype/testdata/test1.go delete mode 100644 src/cmd/govet/Makefile delete mode 100644 src/cmd/govet/doc.go delete mode 100644 src/cmd/govet/govet.go delete mode 100644 src/cmd/goyacc/Makefile delete mode 100644 src/cmd/goyacc/doc.go delete mode 100644 src/cmd/goyacc/goyacc.go delete mode 100644 src/cmd/goyacc/units.txt delete mode 100644 src/cmd/goyacc/units.y delete mode 100644 src/cmd/hgpatch/Makefile delete mode 100644 src/cmd/hgpatch/doc.go delete mode 100644 src/cmd/hgpatch/main.go create mode 100644 src/cmd/objdump/main.c create mode 100644 src/cmd/pack/Makefile create mode 100644 src/cmd/pack/ar.c create mode 100644 src/cmd/pack/doc.go delete mode 100644 src/cmd/prof/Makefile delete mode 100644 src/cmd/prof/doc.go delete mode 100755 src/cmd/prof/gopprof delete mode 100644 src/cmd/prof/main.c create mode 100644 src/cmd/vet/Makefile create mode 100644 src/cmd/vet/doc.go create mode 100644 src/cmd/vet/main.go create mode 100644 src/cmd/vet/method.go create mode 100644 src/cmd/vet/print.go create mode 100644 src/cmd/vet/structtag.go create mode 100644 src/cmd/vet/taglit.go create mode 100644 src/cmd/yacc/Makefile create mode 100644 src/cmd/yacc/doc.go create mode 100644 src/cmd/yacc/units.txt create mode 100644 src/cmd/yacc/units.y create mode 100644 src/cmd/yacc/yacc.go delete mode 100644 src/env.bash create mode 100644 src/lib9/ctime.c delete mode 100644 src/lib9/fmt/errfmt.c delete mode 100644 src/lib9/fork.c delete mode 100644 src/lib9/utf/runetypebody-5.0.0.c delete mode 100644 src/lib9/utf/runetypebody-5.2.0.c delete mode 100644 src/lib9/utf/runetypebody-6.0.0.c create mode 100644 src/lib9/utf/runetypebody-6.0.0.h delete mode 100644 src/lib9/win32.c create mode 100644 src/lib9/windows.c create mode 100644 src/libmach/netbsd.c create mode 100644 src/libmach/plan9.c create mode 100644 src/make.bat delete mode 100644 src/pkg/Makefile delete mode 100644 src/pkg/archive/tar/Makefile delete mode 100644 src/pkg/archive/zip/Makefile create mode 100644 src/pkg/archive/zip/example_test.go create mode 100644 src/pkg/archive/zip/testdata/crc32-not-streamed.zip create mode 100644 src/pkg/archive/zip/testdata/go-no-datadesc-sig.zip create mode 100644 src/pkg/archive/zip/testdata/go-with-datadesc-sig.zip delete mode 100644 src/pkg/archive/zip/testdata/r.zip create mode 100644 src/pkg/archive/zip/testdata/symlink.zip create mode 100644 src/pkg/archive/zip/testdata/unix.zip create mode 100644 src/pkg/archive/zip/testdata/winxp.zip delete mode 100644 src/pkg/asn1/Makefile delete mode 100644 src/pkg/asn1/asn1.go delete mode 100644 src/pkg/asn1/asn1_test.go delete mode 100644 src/pkg/asn1/common.go delete mode 100644 src/pkg/asn1/marshal.go delete mode 100644 src/pkg/asn1/marshal_test.go delete mode 100644 src/pkg/big/Makefile delete mode 100644 src/pkg/big/arith.go delete mode 100644 src/pkg/big/arith_386.s delete mode 100644 src/pkg/big/arith_amd64.s delete mode 100644 src/pkg/big/arith_arm.s delete mode 100644 src/pkg/big/arith_decl.go delete mode 100644 src/pkg/big/arith_test.go delete mode 100644 src/pkg/big/calibrate_test.go delete mode 100644 src/pkg/big/hilbert_test.go delete mode 100755 src/pkg/big/int.go delete mode 100755 src/pkg/big/int_test.go delete mode 100755 src/pkg/big/nat.go delete mode 100755 src/pkg/big/nat_test.go delete mode 100644 src/pkg/big/rat.go delete mode 100644 src/pkg/big/rat_test.go delete mode 100644 src/pkg/bufio/Makefile delete mode 100644 src/pkg/bytes/Makefile create mode 100644 src/pkg/bytes/example_test.go create mode 100644 src/pkg/bytes/reader.go create mode 100644 src/pkg/bytes/reader_test.go delete mode 100644 src/pkg/cmath/Makefile delete mode 100644 src/pkg/cmath/abs.go delete mode 100644 src/pkg/cmath/asin.go delete mode 100644 src/pkg/cmath/cmath_test.go delete mode 100644 src/pkg/cmath/conj.go delete mode 100644 src/pkg/cmath/exp.go delete mode 100644 src/pkg/cmath/isinf.go delete mode 100644 src/pkg/cmath/isnan.go delete mode 100644 src/pkg/cmath/log.go delete mode 100644 src/pkg/cmath/phase.go delete mode 100644 src/pkg/cmath/polar.go delete mode 100644 src/pkg/cmath/pow.go delete mode 100644 src/pkg/cmath/rect.go delete mode 100644 src/pkg/cmath/sin.go delete mode 100644 src/pkg/cmath/sqrt.go delete mode 100644 src/pkg/cmath/tan.go delete mode 100644 src/pkg/compress/bzip2/Makefile delete mode 100644 src/pkg/compress/flate/Makefile delete mode 100644 src/pkg/compress/flate/util.go delete mode 100644 src/pkg/compress/gzip/Makefile delete mode 100644 src/pkg/compress/lzw/Makefile create mode 100644 src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt delete mode 100644 src/pkg/compress/zlib/Makefile delete mode 100644 src/pkg/container/heap/Makefile create mode 100644 src/pkg/container/heap/example_test.go delete mode 100644 src/pkg/container/list/Makefile delete mode 100644 src/pkg/container/ring/Makefile delete mode 100644 src/pkg/container/vector/Makefile delete mode 100644 src/pkg/container/vector/defs.go delete mode 100644 src/pkg/container/vector/intvector.go delete mode 100644 src/pkg/container/vector/intvector_test.go delete mode 100644 src/pkg/container/vector/nogen_test.go delete mode 100644 src/pkg/container/vector/numbers_test.go delete mode 100644 src/pkg/container/vector/stringvector.go delete mode 100644 src/pkg/container/vector/stringvector_test.go delete mode 100644 src/pkg/container/vector/vector.go delete mode 100644 src/pkg/container/vector/vector_test.go delete mode 100644 src/pkg/crypto/Makefile delete mode 100644 src/pkg/crypto/aes/Makefile delete mode 100644 src/pkg/crypto/blowfish/Makefile delete mode 100644 src/pkg/crypto/blowfish/block.go delete mode 100644 src/pkg/crypto/blowfish/blowfish_test.go delete mode 100644 src/pkg/crypto/blowfish/cipher.go delete mode 100644 src/pkg/crypto/blowfish/const.go delete mode 100644 src/pkg/crypto/cast5/Makefile delete mode 100644 src/pkg/crypto/cast5/cast5.go delete mode 100644 src/pkg/crypto/cast5/cast5_test.go delete mode 100644 src/pkg/crypto/cipher/Makefile delete mode 100644 src/pkg/crypto/cipher/ocfb.go delete mode 100644 src/pkg/crypto/cipher/ocfb_test.go delete mode 100644 src/pkg/crypto/des/Makefile delete mode 100644 src/pkg/crypto/dsa/Makefile delete mode 100644 src/pkg/crypto/ecdsa/Makefile delete mode 100644 src/pkg/crypto/elliptic/Makefile create mode 100644 src/pkg/crypto/elliptic/p224.go create mode 100644 src/pkg/crypto/elliptic/p224_test.go delete mode 100644 src/pkg/crypto/hmac/Makefile delete mode 100644 src/pkg/crypto/md4/Makefile delete mode 100644 src/pkg/crypto/md4/md4.go delete mode 100644 src/pkg/crypto/md4/md4_test.go delete mode 100644 src/pkg/crypto/md4/md4block.go delete mode 100644 src/pkg/crypto/md5/Makefile delete mode 100644 src/pkg/crypto/ocsp/Makefile delete mode 100644 src/pkg/crypto/ocsp/ocsp.go delete mode 100644 src/pkg/crypto/ocsp/ocsp_test.go delete mode 100644 src/pkg/crypto/openpgp/Makefile delete mode 100644 src/pkg/crypto/openpgp/armor/Makefile delete mode 100644 src/pkg/crypto/openpgp/armor/armor.go delete mode 100644 src/pkg/crypto/openpgp/armor/armor_test.go delete mode 100644 src/pkg/crypto/openpgp/armor/encode.go delete mode 100644 src/pkg/crypto/openpgp/canonical_text.go delete mode 100644 src/pkg/crypto/openpgp/canonical_text_test.go delete mode 100644 src/pkg/crypto/openpgp/elgamal/Makefile delete mode 100644 src/pkg/crypto/openpgp/elgamal/elgamal.go delete mode 100644 src/pkg/crypto/openpgp/elgamal/elgamal_test.go delete mode 100644 src/pkg/crypto/openpgp/error/Makefile delete mode 100644 src/pkg/crypto/openpgp/error/error.go delete mode 100644 src/pkg/crypto/openpgp/keys.go delete mode 100644 src/pkg/crypto/openpgp/packet/Makefile delete mode 100644 src/pkg/crypto/openpgp/packet/compressed.go delete mode 100644 src/pkg/crypto/openpgp/packet/compressed_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/encrypted_key.go delete mode 100644 src/pkg/crypto/openpgp/packet/encrypted_key_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/literal.go delete mode 100644 src/pkg/crypto/openpgp/packet/one_pass_signature.go delete mode 100644 src/pkg/crypto/openpgp/packet/packet.go delete mode 100644 src/pkg/crypto/openpgp/packet/packet_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/private_key.go delete mode 100644 src/pkg/crypto/openpgp/packet/private_key_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/public_key.go delete mode 100644 src/pkg/crypto/openpgp/packet/public_key_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/reader.go delete mode 100644 src/pkg/crypto/openpgp/packet/signature.go delete mode 100644 src/pkg/crypto/openpgp/packet/signature_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go delete mode 100644 src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go delete mode 100644 src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/userid.go delete mode 100644 src/pkg/crypto/openpgp/packet/userid_test.go delete mode 100644 src/pkg/crypto/openpgp/read.go delete mode 100644 src/pkg/crypto/openpgp/read_test.go delete mode 100644 src/pkg/crypto/openpgp/s2k/Makefile delete mode 100644 src/pkg/crypto/openpgp/s2k/s2k.go delete mode 100644 src/pkg/crypto/openpgp/s2k/s2k_test.go delete mode 100644 src/pkg/crypto/openpgp/write.go delete mode 100644 src/pkg/crypto/openpgp/write_test.go delete mode 100644 src/pkg/crypto/rand/Makefile mode change 100755 => 100644 src/pkg/crypto/rand/rand_windows.go delete mode 100644 src/pkg/crypto/rc4/Makefile delete mode 100644 src/pkg/crypto/ripemd160/Makefile delete mode 100644 src/pkg/crypto/ripemd160/ripemd160.go delete mode 100644 src/pkg/crypto/ripemd160/ripemd160_test.go delete mode 100644 src/pkg/crypto/ripemd160/ripemd160block.go delete mode 100644 src/pkg/crypto/rsa/Makefile delete mode 100644 src/pkg/crypto/sha1/Makefile delete mode 100644 src/pkg/crypto/sha256/Makefile delete mode 100644 src/pkg/crypto/sha512/Makefile delete mode 100644 src/pkg/crypto/subtle/Makefile delete mode 100644 src/pkg/crypto/tls/Makefile create mode 100644 src/pkg/crypto/tls/root_test.go delete mode 100644 src/pkg/crypto/twofish/Makefile delete mode 100644 src/pkg/crypto/twofish/twofish.go delete mode 100644 src/pkg/crypto/twofish/twofish_test.go delete mode 100644 src/pkg/crypto/x509/Makefile create mode 100644 src/pkg/crypto/x509/pkcs1.go create mode 100644 src/pkg/crypto/x509/pkcs8.go create mode 100644 src/pkg/crypto/x509/pkcs8_test.go delete mode 100644 src/pkg/crypto/x509/pkix/Makefile create mode 100644 src/pkg/crypto/x509/root.go create mode 100644 src/pkg/crypto/x509/root_darwin.go create mode 100644 src/pkg/crypto/x509/root_stub.go create mode 100644 src/pkg/crypto/x509/root_unix.go create mode 100644 src/pkg/crypto/x509/root_windows.go delete mode 100644 src/pkg/crypto/xtea/Makefile delete mode 100644 src/pkg/crypto/xtea/block.go delete mode 100644 src/pkg/crypto/xtea/cipher.go delete mode 100644 src/pkg/crypto/xtea/xtea_test.go delete mode 100644 src/pkg/csv/Makefile delete mode 100644 src/pkg/csv/reader.go delete mode 100644 src/pkg/csv/reader_test.go delete mode 100644 src/pkg/csv/writer.go delete mode 100644 src/pkg/csv/writer_test.go create mode 100644 src/pkg/database/sql/convert.go create mode 100644 src/pkg/database/sql/convert_test.go create mode 100644 src/pkg/database/sql/doc.txt create mode 100644 src/pkg/database/sql/driver/driver.go create mode 100644 src/pkg/database/sql/driver/types.go create mode 100644 src/pkg/database/sql/driver/types_test.go create mode 100644 src/pkg/database/sql/fakedb_test.go create mode 100644 src/pkg/database/sql/sql.go create mode 100644 src/pkg/database/sql/sql_test.go delete mode 100644 src/pkg/debug/dwarf/Makefile delete mode 100644 src/pkg/debug/elf/Makefile delete mode 100644 src/pkg/debug/gosym/Makefile create mode 100644 src/pkg/debug/gosym/pclinetest.asm delete mode 100644 src/pkg/debug/gosym/pclinetest.s delete mode 100644 src/pkg/debug/macho/Makefile delete mode 100644 src/pkg/debug/pe/Makefile delete mode 100755 src/pkg/deps.bash delete mode 100644 src/pkg/ebnf/Makefile delete mode 100644 src/pkg/ebnf/ebnf.go delete mode 100644 src/pkg/ebnf/ebnf_test.go delete mode 100644 src/pkg/ebnf/parser.go delete mode 100644 src/pkg/encoding/ascii85/Makefile create mode 100644 src/pkg/encoding/asn1/asn1.go create mode 100644 src/pkg/encoding/asn1/asn1_test.go create mode 100644 src/pkg/encoding/asn1/common.go create mode 100644 src/pkg/encoding/asn1/marshal.go create mode 100644 src/pkg/encoding/asn1/marshal_test.go delete mode 100644 src/pkg/encoding/base32/Makefile delete mode 100644 src/pkg/encoding/base64/Makefile delete mode 100644 src/pkg/encoding/binary/Makefile create mode 100644 src/pkg/encoding/binary/example_test.go create mode 100644 src/pkg/encoding/binary/varint.go create mode 100644 src/pkg/encoding/binary/varint_test.go create mode 100644 src/pkg/encoding/csv/reader.go create mode 100644 src/pkg/encoding/csv/reader_test.go create mode 100644 src/pkg/encoding/csv/writer.go create mode 100644 src/pkg/encoding/csv/writer_test.go delete mode 100644 src/pkg/encoding/git85/Makefile delete mode 100644 src/pkg/encoding/git85/git.go delete mode 100644 src/pkg/encoding/git85/git_test.go create mode 100644 src/pkg/encoding/gob/codec_test.go create mode 100644 src/pkg/encoding/gob/debug.go create mode 100644 src/pkg/encoding/gob/decode.go create mode 100644 src/pkg/encoding/gob/decoder.go create mode 100644 src/pkg/encoding/gob/doc.go create mode 100644 src/pkg/encoding/gob/dump.go create mode 100644 src/pkg/encoding/gob/encode.go create mode 100644 src/pkg/encoding/gob/encoder.go create mode 100644 src/pkg/encoding/gob/encoder_test.go create mode 100644 src/pkg/encoding/gob/error.go create mode 100644 src/pkg/encoding/gob/gobencdec_test.go create mode 100644 src/pkg/encoding/gob/timing_test.go create mode 100644 src/pkg/encoding/gob/type.go create mode 100644 src/pkg/encoding/gob/type_test.go delete mode 100644 src/pkg/encoding/hex/Makefile create mode 100644 src/pkg/encoding/json/bench_test.go create mode 100644 src/pkg/encoding/json/decode.go create mode 100644 src/pkg/encoding/json/decode_test.go create mode 100644 src/pkg/encoding/json/encode.go create mode 100644 src/pkg/encoding/json/encode_test.go create mode 100644 src/pkg/encoding/json/example_test.go create mode 100644 src/pkg/encoding/json/indent.go create mode 100644 src/pkg/encoding/json/scanner.go create mode 100644 src/pkg/encoding/json/scanner_test.go create mode 100644 src/pkg/encoding/json/stream.go create mode 100644 src/pkg/encoding/json/stream_test.go create mode 100644 src/pkg/encoding/json/tagkey_test.go create mode 100644 src/pkg/encoding/json/tags.go create mode 100644 src/pkg/encoding/json/tags_test.go create mode 100644 src/pkg/encoding/json/testdata/code.json.gz delete mode 100644 src/pkg/encoding/pem/Makefile create mode 100644 src/pkg/encoding/xml/atom_test.go create mode 100644 src/pkg/encoding/xml/example_test.go create mode 100644 src/pkg/encoding/xml/marshal.go create mode 100644 src/pkg/encoding/xml/marshal_test.go create mode 100644 src/pkg/encoding/xml/read.go create mode 100644 src/pkg/encoding/xml/read_test.go create mode 100644 src/pkg/encoding/xml/typeinfo.go create mode 100644 src/pkg/encoding/xml/xml.go create mode 100644 src/pkg/encoding/xml/xml_test.go create mode 100644 src/pkg/errors/errors.go create mode 100644 src/pkg/errors/errors_test.go create mode 100644 src/pkg/errors/example_test.go delete mode 100644 src/pkg/exec/Makefile delete mode 100644 src/pkg/exec/exec.go delete mode 100644 src/pkg/exec/exec_test.go delete mode 100644 src/pkg/exec/lp_plan9.go delete mode 100644 src/pkg/exec/lp_test.go delete mode 100644 src/pkg/exec/lp_unix.go delete mode 100644 src/pkg/exec/lp_windows.go delete mode 100644 src/pkg/exp/README delete mode 100644 src/pkg/exp/gui/Makefile delete mode 100644 src/pkg/exp/gui/gui.go delete mode 100644 src/pkg/exp/gui/x11/Makefile delete mode 100644 src/pkg/exp/gui/x11/auth.go delete mode 100644 src/pkg/exp/gui/x11/conn.go delete mode 100644 src/pkg/exp/norm/Makefile delete mode 100644 src/pkg/exp/norm/composition.go delete mode 100644 src/pkg/exp/norm/composition_test.go delete mode 100644 src/pkg/exp/norm/forminfo.go delete mode 100644 src/pkg/exp/norm/maketables.go delete mode 100644 src/pkg/exp/norm/maketesttables.go delete mode 100644 src/pkg/exp/norm/norm_test.go delete mode 100644 src/pkg/exp/norm/normalize.go delete mode 100644 src/pkg/exp/norm/tables.go delete mode 100644 src/pkg/exp/norm/trie.go delete mode 100644 src/pkg/exp/norm/trie_test.go delete mode 100644 src/pkg/exp/norm/triedata_test.go delete mode 100644 src/pkg/exp/norm/triegen.go delete mode 100644 src/pkg/exp/regexp/Makefile delete mode 100644 src/pkg/exp/regexp/all_test.go delete mode 100644 src/pkg/exp/regexp/exec.go delete mode 100644 src/pkg/exp/regexp/find_test.go delete mode 100644 src/pkg/exp/regexp/regexp.go delete mode 100644 src/pkg/exp/regexp/syntax/Makefile delete mode 100644 src/pkg/exp/regexp/syntax/compile.go delete mode 100755 src/pkg/exp/regexp/syntax/make_perl_groups.pl delete mode 100644 src/pkg/exp/regexp/syntax/parse.go delete mode 100644 src/pkg/exp/regexp/syntax/parse_test.go delete mode 100644 src/pkg/exp/regexp/syntax/perl_groups.go delete mode 100644 src/pkg/exp/regexp/syntax/prog.go delete mode 100644 src/pkg/exp/regexp/syntax/prog_test.go delete mode 100644 src/pkg/exp/regexp/syntax/regexp.go delete mode 100644 src/pkg/exp/regexp/syntax/simplify.go delete mode 100644 src/pkg/exp/regexp/syntax/simplify_test.go delete mode 100644 src/pkg/exp/template/html/Makefile delete mode 100644 src/pkg/exp/template/html/context.go delete mode 100644 src/pkg/exp/template/html/escape.go delete mode 100644 src/pkg/exp/template/html/escape_test.go delete mode 100644 src/pkg/exp/wingui/Makefile delete mode 100644 src/pkg/exp/wingui/gui.go delete mode 100644 src/pkg/exp/wingui/winapi.go delete mode 100644 src/pkg/exp/wingui/zwinapi.go delete mode 100644 src/pkg/expvar/Makefile delete mode 100644 src/pkg/flag/Makefile create mode 100644 src/pkg/flag/example_test.go delete mode 100644 src/pkg/fmt/Makefile create mode 100644 src/pkg/fmt/export_test.go delete mode 100644 src/pkg/go/ast/Makefile create mode 100644 src/pkg/go/ast/example_test.go create mode 100644 src/pkg/go/ast/import.go delete mode 100644 src/pkg/go/build/Makefile delete mode 100644 src/pkg/go/build/cgotest/cgotest.c delete mode 100644 src/pkg/go/build/cgotest/cgotest.go delete mode 100644 src/pkg/go/build/cgotest/cgotest.h delete mode 100644 src/pkg/go/build/cmdtest/main.go create mode 100644 src/pkg/go/build/deps_test.go delete mode 100644 src/pkg/go/build/dir.go create mode 100644 src/pkg/go/build/doc.go delete mode 100644 src/pkg/go/build/path.go delete mode 100644 src/pkg/go/build/pkgtest/pkgtest.go delete mode 100644 src/pkg/go/build/pkgtest/sqrt_386.s delete mode 100644 src/pkg/go/build/pkgtest/sqrt_amd64.s delete mode 100644 src/pkg/go/build/pkgtest/sqrt_arm.s create mode 100644 src/pkg/go/build/syslist.go create mode 100644 src/pkg/go/build/testdata/other/file/file.go create mode 100644 src/pkg/go/build/testdata/other/main.go create mode 100644 src/pkg/go/doc/comment_test.go create mode 100644 src/pkg/go/doc/doc_test.go create mode 100644 src/pkg/go/doc/example.go create mode 100644 src/pkg/go/doc/exports.go create mode 100644 src/pkg/go/doc/filter.go create mode 100644 src/pkg/go/doc/headscan.go create mode 100644 src/pkg/go/doc/reader.go create mode 100644 src/pkg/go/doc/synopsis.go create mode 100644 src/pkg/go/doc/synopsis_test.go create mode 100644 src/pkg/go/doc/testdata/a.0.golden create mode 100644 src/pkg/go/doc/testdata/a.1.golden create mode 100644 src/pkg/go/doc/testdata/a.2.golden create mode 100644 src/pkg/go/doc/testdata/a0.go create mode 100644 src/pkg/go/doc/testdata/a1.go create mode 100644 src/pkg/go/doc/testdata/b.0.golden create mode 100644 src/pkg/go/doc/testdata/b.1.golden create mode 100644 src/pkg/go/doc/testdata/b.2.golden create mode 100644 src/pkg/go/doc/testdata/b.go create mode 100644 src/pkg/go/doc/testdata/benchmark.go create mode 100644 src/pkg/go/doc/testdata/c.0.golden create mode 100644 src/pkg/go/doc/testdata/c.1.golden create mode 100644 src/pkg/go/doc/testdata/c.2.golden create mode 100644 src/pkg/go/doc/testdata/c.go create mode 100644 src/pkg/go/doc/testdata/d.0.golden create mode 100644 src/pkg/go/doc/testdata/d.1.golden create mode 100644 src/pkg/go/doc/testdata/d.2.golden create mode 100644 src/pkg/go/doc/testdata/d1.go create mode 100644 src/pkg/go/doc/testdata/d2.go create mode 100644 src/pkg/go/doc/testdata/e.0.golden create mode 100644 src/pkg/go/doc/testdata/e.1.golden create mode 100644 src/pkg/go/doc/testdata/e.2.golden create mode 100644 src/pkg/go/doc/testdata/e.go create mode 100644 src/pkg/go/doc/testdata/error1.0.golden create mode 100644 src/pkg/go/doc/testdata/error1.1.golden create mode 100644 src/pkg/go/doc/testdata/error1.2.golden create mode 100644 src/pkg/go/doc/testdata/error1.go create mode 100644 src/pkg/go/doc/testdata/error2.0.golden create mode 100644 src/pkg/go/doc/testdata/error2.1.golden create mode 100644 src/pkg/go/doc/testdata/error2.2.golden create mode 100644 src/pkg/go/doc/testdata/error2.go create mode 100644 src/pkg/go/doc/testdata/example.go create mode 100644 src/pkg/go/doc/testdata/f.0.golden create mode 100644 src/pkg/go/doc/testdata/f.1.golden create mode 100644 src/pkg/go/doc/testdata/f.2.golden create mode 100644 src/pkg/go/doc/testdata/f.go create mode 100644 src/pkg/go/doc/testdata/template.txt create mode 100644 src/pkg/go/doc/testdata/testing.0.golden create mode 100644 src/pkg/go/doc/testdata/testing.1.golden create mode 100644 src/pkg/go/doc/testdata/testing.2.golden create mode 100644 src/pkg/go/doc/testdata/testing.go delete mode 100644 src/pkg/go/parser/Makefile create mode 100644 src/pkg/go/parser/error_test.go create mode 100644 src/pkg/go/parser/example_test.go create mode 100644 src/pkg/go/parser/short_test.go create mode 100644 src/pkg/go/parser/testdata/commas.src create mode 100644 src/pkg/go/parser/testdata/issue3106.src delete mode 100644 src/pkg/go/printer/Makefile create mode 100644 src/pkg/go/printer/example_test.go delete mode 100644 src/pkg/go/scanner/Makefile create mode 100644 src/pkg/go/scanner/example_test.go delete mode 100644 src/pkg/go/token/Makefile create mode 100644 src/pkg/go/token/serialize.go create mode 100644 src/pkg/go/token/serialize_test.go delete mode 100644 src/pkg/go/typechecker/Makefile delete mode 100644 src/pkg/go/typechecker/scope.go delete mode 100644 src/pkg/go/typechecker/testdata/test0.src delete mode 100644 src/pkg/go/typechecker/testdata/test1.src delete mode 100644 src/pkg/go/typechecker/testdata/test3.src delete mode 100644 src/pkg/go/typechecker/testdata/test4.src delete mode 100644 src/pkg/go/typechecker/type.go delete mode 100644 src/pkg/go/typechecker/typechecker.go delete mode 100644 src/pkg/go/typechecker/typechecker_test.go delete mode 100644 src/pkg/go/typechecker/universe.go delete mode 100644 src/pkg/go/types/Makefile delete mode 100644 src/pkg/go/types/check.go delete mode 100644 src/pkg/go/types/check_test.go delete mode 100644 src/pkg/go/types/const.go delete mode 100644 src/pkg/go/types/exportdata.go delete mode 100644 src/pkg/go/types/gcimporter.go delete mode 100644 src/pkg/go/types/gcimporter_test.go delete mode 100644 src/pkg/go/types/testdata/exports.go delete mode 100644 src/pkg/go/types/testdata/test0.src delete mode 100644 src/pkg/go/types/types.go delete mode 100644 src/pkg/go/types/universe.go delete mode 100644 src/pkg/gob/Makefile delete mode 100644 src/pkg/gob/codec_test.go delete mode 100644 src/pkg/gob/debug.go delete mode 100644 src/pkg/gob/decode.go delete mode 100644 src/pkg/gob/decoder.go delete mode 100644 src/pkg/gob/doc.go delete mode 100644 src/pkg/gob/dump.go delete mode 100644 src/pkg/gob/encode.go delete mode 100644 src/pkg/gob/encoder.go delete mode 100644 src/pkg/gob/encoder_test.go delete mode 100644 src/pkg/gob/error.go delete mode 100644 src/pkg/gob/gobencdec_test.go delete mode 100644 src/pkg/gob/timing_test.go delete mode 100644 src/pkg/gob/type.go delete mode 100644 src/pkg/gob/type_test.go delete mode 100644 src/pkg/hash/Makefile delete mode 100644 src/pkg/hash/adler32/Makefile delete mode 100644 src/pkg/hash/crc32/Makefile delete mode 100644 src/pkg/hash/crc64/Makefile delete mode 100644 src/pkg/hash/fnv/Makefile delete mode 100644 src/pkg/html/Makefile delete mode 100644 src/pkg/html/const.go delete mode 100644 src/pkg/html/doc.go delete mode 100644 src/pkg/html/node.go delete mode 100644 src/pkg/html/parse.go delete mode 100644 src/pkg/html/parse_test.go create mode 100644 src/pkg/html/template/attr.go create mode 100644 src/pkg/html/template/clone_test.go create mode 100644 src/pkg/html/template/content.go create mode 100644 src/pkg/html/template/content_test.go create mode 100644 src/pkg/html/template/context.go create mode 100644 src/pkg/html/template/css.go create mode 100644 src/pkg/html/template/css_test.go create mode 100644 src/pkg/html/template/doc.go create mode 100644 src/pkg/html/template/error.go create mode 100644 src/pkg/html/template/escape.go create mode 100644 src/pkg/html/template/escape_test.go create mode 100644 src/pkg/html/template/html.go create mode 100644 src/pkg/html/template/html_test.go create mode 100644 src/pkg/html/template/js.go create mode 100644 src/pkg/html/template/js_test.go create mode 100644 src/pkg/html/template/template.go create mode 100644 src/pkg/html/template/transition.go create mode 100644 src/pkg/html/template/url.go create mode 100644 src/pkg/html/template/url_test.go delete mode 100644 src/pkg/html/testdata/webkit/README delete mode 100644 src/pkg/html/testdata/webkit/adoption01.dat delete mode 100644 src/pkg/html/testdata/webkit/adoption02.dat delete mode 100644 src/pkg/html/testdata/webkit/comments01.dat delete mode 100644 src/pkg/html/testdata/webkit/doctype01.dat delete mode 100644 src/pkg/html/testdata/webkit/entities01.dat delete mode 100644 src/pkg/html/testdata/webkit/entities02.dat delete mode 100644 src/pkg/html/testdata/webkit/html5test-com.dat delete mode 100644 src/pkg/html/testdata/webkit/inbody01.dat delete mode 100644 src/pkg/html/testdata/webkit/isindex.dat delete mode 100644 src/pkg/html/testdata/webkit/pending-spec-changes-plain-text-unsafe.dat delete mode 100644 src/pkg/html/testdata/webkit/pending-spec-changes.dat delete mode 100644 src/pkg/html/testdata/webkit/plain-text-unsafe.dat delete mode 100644 src/pkg/html/testdata/webkit/scriptdata01.dat delete mode 100644 src/pkg/html/testdata/webkit/scripted/adoption01.dat delete mode 100644 src/pkg/html/testdata/webkit/scripted/webkit01.dat delete mode 100644 src/pkg/html/testdata/webkit/tables01.dat delete mode 100644 src/pkg/html/testdata/webkit/tests1.dat delete mode 100644 src/pkg/html/testdata/webkit/tests10.dat delete mode 100644 src/pkg/html/testdata/webkit/tests11.dat delete mode 100644 src/pkg/html/testdata/webkit/tests12.dat delete mode 100644 src/pkg/html/testdata/webkit/tests14.dat delete mode 100644 src/pkg/html/testdata/webkit/tests15.dat delete mode 100644 src/pkg/html/testdata/webkit/tests16.dat delete mode 100644 src/pkg/html/testdata/webkit/tests17.dat delete mode 100644 src/pkg/html/testdata/webkit/tests18.dat delete mode 100644 src/pkg/html/testdata/webkit/tests19.dat delete mode 100644 src/pkg/html/testdata/webkit/tests2.dat delete mode 100644 src/pkg/html/testdata/webkit/tests20.dat delete mode 100644 src/pkg/html/testdata/webkit/tests21.dat delete mode 100644 src/pkg/html/testdata/webkit/tests22.dat delete mode 100644 src/pkg/html/testdata/webkit/tests23.dat delete mode 100644 src/pkg/html/testdata/webkit/tests24.dat delete mode 100644 src/pkg/html/testdata/webkit/tests25.dat delete mode 100644 src/pkg/html/testdata/webkit/tests26.dat delete mode 100644 src/pkg/html/testdata/webkit/tests3.dat delete mode 100644 src/pkg/html/testdata/webkit/tests4.dat delete mode 100644 src/pkg/html/testdata/webkit/tests5.dat delete mode 100644 src/pkg/html/testdata/webkit/tests6.dat delete mode 100644 src/pkg/html/testdata/webkit/tests7.dat delete mode 100644 src/pkg/html/testdata/webkit/tests8.dat delete mode 100644 src/pkg/html/testdata/webkit/tests9.dat delete mode 100644 src/pkg/html/testdata/webkit/tests_innerHTML_1.dat delete mode 100644 src/pkg/html/testdata/webkit/tricky01.dat delete mode 100644 src/pkg/html/testdata/webkit/webkit01.dat delete mode 100644 src/pkg/html/testdata/webkit/webkit02.dat delete mode 100644 src/pkg/html/token.go delete mode 100644 src/pkg/html/token_test.go delete mode 100644 src/pkg/http/Makefile delete mode 100644 src/pkg/http/cgi/Makefile delete mode 100644 src/pkg/http/cgi/child.go delete mode 100644 src/pkg/http/cgi/child_test.go delete mode 100644 src/pkg/http/cgi/host.go delete mode 100644 src/pkg/http/cgi/host_test.go delete mode 100644 src/pkg/http/cgi/matryoshka_test.go delete mode 100755 src/pkg/http/cgi/testdata/test.cgi delete mode 100644 src/pkg/http/chunked.go delete mode 100644 src/pkg/http/client.go delete mode 100644 src/pkg/http/client_test.go delete mode 100644 src/pkg/http/cookie.go delete mode 100644 src/pkg/http/cookie_test.go delete mode 100644 src/pkg/http/dump.go delete mode 100644 src/pkg/http/export_test.go delete mode 100644 src/pkg/http/fcgi/Makefile delete mode 100644 src/pkg/http/fcgi/child.go delete mode 100644 src/pkg/http/fcgi/fcgi.go delete mode 100644 src/pkg/http/fcgi/fcgi_test.go delete mode 100644 src/pkg/http/fs.go delete mode 100644 src/pkg/http/fs_test.go delete mode 100644 src/pkg/http/header.go delete mode 100644 src/pkg/http/header_test.go delete mode 100644 src/pkg/http/httptest/Makefile delete mode 100644 src/pkg/http/httptest/recorder.go delete mode 100644 src/pkg/http/httptest/server.go delete mode 100644 src/pkg/http/lex.go delete mode 100644 src/pkg/http/lex_test.go delete mode 100644 src/pkg/http/persist.go delete mode 100644 src/pkg/http/pprof/Makefile delete mode 100644 src/pkg/http/pprof/pprof.go delete mode 100644 src/pkg/http/proxy_test.go delete mode 100644 src/pkg/http/range_test.go delete mode 100644 src/pkg/http/readrequest_test.go delete mode 100644 src/pkg/http/request.go delete mode 100644 src/pkg/http/request_test.go delete mode 100644 src/pkg/http/requestwrite_test.go delete mode 100644 src/pkg/http/response.go delete mode 100644 src/pkg/http/response_test.go delete mode 100644 src/pkg/http/responsewrite_test.go delete mode 100644 src/pkg/http/reverseproxy.go delete mode 100644 src/pkg/http/reverseproxy_test.go delete mode 100644 src/pkg/http/serve_test.go delete mode 100644 src/pkg/http/server.go delete mode 100644 src/pkg/http/sniff.go delete mode 100644 src/pkg/http/sniff_test.go delete mode 100644 src/pkg/http/spdy/Makefile delete mode 100644 src/pkg/http/spdy/read.go delete mode 100644 src/pkg/http/spdy/spdy_test.go delete mode 100644 src/pkg/http/spdy/types.go delete mode 100644 src/pkg/http/spdy/write.go delete mode 100644 src/pkg/http/status.go delete mode 100644 src/pkg/http/testdata/file delete mode 100644 src/pkg/http/testdata/index.html delete mode 100644 src/pkg/http/testdata/style.css delete mode 100644 src/pkg/http/transfer.go delete mode 100644 src/pkg/http/transport.go delete mode 100644 src/pkg/http/transport_test.go delete mode 100644 src/pkg/http/triv.go delete mode 100644 src/pkg/image/Makefile delete mode 100644 src/pkg/image/bmp/Makefile delete mode 100644 src/pkg/image/bmp/reader.go delete mode 100644 src/pkg/image/color.go create mode 100644 src/pkg/image/color/color.go create mode 100644 src/pkg/image/color/ycbcr.go create mode 100644 src/pkg/image/color/ycbcr_test.go create mode 100644 src/pkg/image/decode_example_test.go delete mode 100644 src/pkg/image/draw/Makefile delete mode 100644 src/pkg/image/gif/Makefile delete mode 100644 src/pkg/image/jpeg/Makefile delete mode 100644 src/pkg/image/png/Makefile create mode 100644 src/pkg/image/png/testdata/invalid-crc32.png create mode 100644 src/pkg/image/png/testdata/invalid-noend.png create mode 100644 src/pkg/image/png/testdata/invalid-trunc.png create mode 100644 src/pkg/image/png/testdata/invalid-zlib.png delete mode 100644 src/pkg/image/testdata/video-001.bmp delete mode 100644 src/pkg/image/testdata/video-001.tiff delete mode 100644 src/pkg/image/tiff/Makefile delete mode 100644 src/pkg/image/tiff/buffer.go delete mode 100644 src/pkg/image/tiff/buffer_test.go delete mode 100644 src/pkg/image/tiff/consts.go delete mode 100644 src/pkg/image/tiff/reader.go delete mode 100644 src/pkg/image/tiff/reader_test.go delete mode 100644 src/pkg/image/tiff/testdata/no_rps.tiff create mode 100644 src/pkg/image/ycbcr.go delete mode 100644 src/pkg/image/ycbcr/Makefile delete mode 100644 src/pkg/image/ycbcr/ycbcr.go delete mode 100644 src/pkg/image/ycbcr/ycbcr_test.go create mode 100644 src/pkg/image/ycbcr_test.go delete mode 100644 src/pkg/index/suffixarray/Makefile delete mode 100644 src/pkg/io/Makefile delete mode 100644 src/pkg/io/ioutil/Makefile delete mode 100644 src/pkg/json/Makefile delete mode 100644 src/pkg/json/decode.go delete mode 100644 src/pkg/json/decode_test.go delete mode 100644 src/pkg/json/encode.go delete mode 100644 src/pkg/json/encode_test.go delete mode 100644 src/pkg/json/indent.go delete mode 100644 src/pkg/json/scanner.go delete mode 100644 src/pkg/json/scanner_test.go delete mode 100644 src/pkg/json/stream.go delete mode 100644 src/pkg/json/stream_test.go delete mode 100644 src/pkg/json/tagkey_test.go delete mode 100644 src/pkg/json/tags.go delete mode 100644 src/pkg/json/tags_test.go delete mode 100644 src/pkg/log/Makefile create mode 100644 src/pkg/log/syslog/syslog.go create mode 100644 src/pkg/log/syslog/syslog_test.go create mode 100644 src/pkg/log/syslog/syslog_unix.go create mode 100644 src/pkg/log/syslog/syslog_windows.go delete mode 100644 src/pkg/mail/Makefile delete mode 100644 src/pkg/mail/message.go delete mode 100644 src/pkg/mail/message_test.go delete mode 100644 src/pkg/math/Makefile create mode 100644 src/pkg/math/abs.go create mode 100644 src/pkg/math/abs_386.s create mode 100644 src/pkg/math/abs_amd64.s create mode 100644 src/pkg/math/abs_arm.s create mode 100644 src/pkg/math/asin_amd64.s create mode 100644 src/pkg/math/asin_arm.s delete mode 100644 src/pkg/math/asin_decl.go mode change 100755 => 100644 src/pkg/math/atan2_386.s create mode 100644 src/pkg/math/atan2_amd64.s create mode 100644 src/pkg/math/atan2_arm.s delete mode 100755 src/pkg/math/atan2_decl.go create mode 100644 src/pkg/math/atan_amd64.s create mode 100644 src/pkg/math/atan_arm.s delete mode 100644 src/pkg/math/atan_decl.go create mode 100644 src/pkg/math/big/arith.go create mode 100644 src/pkg/math/big/arith_386.s create mode 100644 src/pkg/math/big/arith_amd64.s create mode 100644 src/pkg/math/big/arith_arm.s create mode 100644 src/pkg/math/big/arith_decl.go create mode 100644 src/pkg/math/big/arith_test.go create mode 100644 src/pkg/math/big/calibrate_test.go create mode 100644 src/pkg/math/big/example_test.go create mode 100644 src/pkg/math/big/hilbert_test.go create mode 100644 src/pkg/math/big/int.go create mode 100644 src/pkg/math/big/int_test.go create mode 100644 src/pkg/math/big/nat.go create mode 100644 src/pkg/math/big/nat_test.go create mode 100644 src/pkg/math/big/rat.go create mode 100644 src/pkg/math/big/rat_test.go create mode 100644 src/pkg/math/cmplx/abs.go create mode 100644 src/pkg/math/cmplx/asin.go create mode 100644 src/pkg/math/cmplx/cmath_test.go create mode 100644 src/pkg/math/cmplx/conj.go create mode 100644 src/pkg/math/cmplx/exp.go create mode 100644 src/pkg/math/cmplx/isinf.go create mode 100644 src/pkg/math/cmplx/isnan.go create mode 100644 src/pkg/math/cmplx/log.go create mode 100644 src/pkg/math/cmplx/phase.go create mode 100644 src/pkg/math/cmplx/polar.go create mode 100644 src/pkg/math/cmplx/pow.go create mode 100644 src/pkg/math/cmplx/rect.go create mode 100644 src/pkg/math/cmplx/sin.go create mode 100644 src/pkg/math/cmplx/sqrt.go create mode 100644 src/pkg/math/cmplx/tan.go create mode 100644 src/pkg/math/dim.go create mode 100644 src/pkg/math/dim_386.s create mode 100644 src/pkg/math/dim_amd64.s create mode 100644 src/pkg/math/dim_arm.s delete mode 100644 src/pkg/math/exp2.go create mode 100644 src/pkg/math/exp2_amd64.s create mode 100644 src/pkg/math/exp2_arm.s delete mode 100644 src/pkg/math/exp2_decl.go create mode 100644 src/pkg/math/exp_arm.s delete mode 100644 src/pkg/math/exp_decl.go delete mode 100644 src/pkg/math/exp_port.go delete mode 100644 src/pkg/math/exp_test.go create mode 100644 src/pkg/math/expm1_amd64.s create mode 100644 src/pkg/math/expm1_arm.s delete mode 100644 src/pkg/math/expm1_decl.go create mode 100644 src/pkg/math/export_test.go delete mode 100644 src/pkg/math/fabs.go delete mode 100644 src/pkg/math/fabs_386.s delete mode 100644 src/pkg/math/fabs_amd64.s delete mode 100644 src/pkg/math/fabs_decl.go delete mode 100644 src/pkg/math/fdim.go delete mode 100644 src/pkg/math/fdim_amd64.s delete mode 100644 src/pkg/math/fdim_decl.go create mode 100644 src/pkg/math/floor_amd64.s create mode 100644 src/pkg/math/floor_arm.s delete mode 100644 src/pkg/math/floor_decl.go delete mode 100644 src/pkg/math/fmod.go delete mode 100644 src/pkg/math/fmod_386.s delete mode 100644 src/pkg/math/fmod_decl.go create mode 100644 src/pkg/math/frexp_amd64.s create mode 100644 src/pkg/math/frexp_arm.s delete mode 100644 src/pkg/math/frexp_decl.go create mode 100644 src/pkg/math/hypot_arm.s delete mode 100644 src/pkg/math/hypot_decl.go delete mode 100644 src/pkg/math/hypot_port.go delete mode 100644 src/pkg/math/hypot_test.go create mode 100644 src/pkg/math/ldexp_amd64.s create mode 100644 src/pkg/math/ldexp_arm.s delete mode 100644 src/pkg/math/ldexp_decl.go create mode 100644 src/pkg/math/log10_amd64.s create mode 100644 src/pkg/math/log10_arm.s delete mode 100644 src/pkg/math/log10_decl.go create mode 100644 src/pkg/math/log1p_amd64.s create mode 100644 src/pkg/math/log1p_arm.s delete mode 100644 src/pkg/math/log1p_decl.go create mode 100644 src/pkg/math/log_arm.s delete mode 100644 src/pkg/math/log_decl.go create mode 100644 src/pkg/math/mod.go create mode 100644 src/pkg/math/mod_386.s create mode 100644 src/pkg/math/mod_amd64.s create mode 100644 src/pkg/math/mod_arm.s create mode 100644 src/pkg/math/modf_amd64.s create mode 100644 src/pkg/math/modf_arm.s delete mode 100644 src/pkg/math/modf_decl.go create mode 100644 src/pkg/math/rand/exp.go create mode 100644 src/pkg/math/rand/normal.go create mode 100644 src/pkg/math/rand/rand.go create mode 100644 src/pkg/math/rand/rand_test.go create mode 100644 src/pkg/math/rand/rng.go create mode 100644 src/pkg/math/rand/zipf.go create mode 100644 src/pkg/math/remainder_amd64.s create mode 100644 src/pkg/math/remainder_arm.s delete mode 100644 src/pkg/math/remainder_decl.go create mode 100644 src/pkg/math/sin_amd64.s create mode 100644 src/pkg/math/sin_arm.s delete mode 100644 src/pkg/math/sin_decl.go create mode 100644 src/pkg/math/sincos_arm.s delete mode 100644 src/pkg/math/sincos_decl.go delete mode 100644 src/pkg/math/sqrt_decl.go delete mode 100644 src/pkg/math/sqrt_port.go delete mode 100644 src/pkg/math/sqrt_test.go create mode 100644 src/pkg/math/tan_amd64.s create mode 100644 src/pkg/math/tan_arm.s delete mode 100644 src/pkg/math/tan_decl.go delete mode 100644 src/pkg/mime/Makefile delete mode 100644 src/pkg/mime/mime_test.go delete mode 100644 src/pkg/mime/multipart/Makefile create mode 100644 src/pkg/mime/type_test.go create mode 100644 src/pkg/mime/type_unix.go create mode 100644 src/pkg/mime/type_windows.go delete mode 100644 src/pkg/net/Makefile create mode 100644 src/pkg/net/dial_test.go delete mode 100644 src/pkg/net/dict/Makefile delete mode 100644 src/pkg/net/dict/dict.go create mode 100644 src/pkg/net/doc.go create mode 100644 src/pkg/net/example_test.go create mode 100644 src/pkg/net/fd_netbsd.go delete mode 100644 src/pkg/net/hosts_testdata create mode 100644 src/pkg/net/http/cgi/child.go create mode 100644 src/pkg/net/http/cgi/child_test.go create mode 100644 src/pkg/net/http/cgi/host.go create mode 100644 src/pkg/net/http/cgi/host_test.go create mode 100644 src/pkg/net/http/cgi/matryoshka_test.go create mode 100755 src/pkg/net/http/cgi/testdata/test.cgi create mode 100644 src/pkg/net/http/chunked.go create mode 100644 src/pkg/net/http/chunked_test.go create mode 100644 src/pkg/net/http/client.go create mode 100644 src/pkg/net/http/client_test.go create mode 100644 src/pkg/net/http/cookie.go create mode 100644 src/pkg/net/http/cookie_test.go create mode 100644 src/pkg/net/http/doc.go create mode 100644 src/pkg/net/http/example_test.go create mode 100644 src/pkg/net/http/export_test.go create mode 100644 src/pkg/net/http/fcgi/child.go create mode 100644 src/pkg/net/http/fcgi/fcgi.go create mode 100644 src/pkg/net/http/fcgi/fcgi_test.go create mode 100644 src/pkg/net/http/filetransport.go create mode 100644 src/pkg/net/http/filetransport_test.go create mode 100644 src/pkg/net/http/fs.go create mode 100644 src/pkg/net/http/fs_test.go create mode 100644 src/pkg/net/http/header.go create mode 100644 src/pkg/net/http/header_test.go create mode 100644 src/pkg/net/http/httptest/recorder.go create mode 100644 src/pkg/net/http/httptest/server.go create mode 100644 src/pkg/net/http/httptest/server_test.go create mode 100644 src/pkg/net/http/httputil/chunked.go create mode 100644 src/pkg/net/http/httputil/chunked_test.go create mode 100644 src/pkg/net/http/httputil/dump.go create mode 100644 src/pkg/net/http/httputil/dump_test.go create mode 100644 src/pkg/net/http/httputil/persist.go create mode 100644 src/pkg/net/http/httputil/reverseproxy.go create mode 100644 src/pkg/net/http/httputil/reverseproxy_test.go create mode 100644 src/pkg/net/http/jar.go create mode 100644 src/pkg/net/http/lex.go create mode 100644 src/pkg/net/http/lex_test.go create mode 100644 src/pkg/net/http/pprof/pprof.go create mode 100644 src/pkg/net/http/proxy_test.go create mode 100644 src/pkg/net/http/range_test.go create mode 100644 src/pkg/net/http/readrequest_test.go create mode 100644 src/pkg/net/http/request.go create mode 100644 src/pkg/net/http/request_test.go create mode 100644 src/pkg/net/http/requestwrite_test.go create mode 100644 src/pkg/net/http/response.go create mode 100644 src/pkg/net/http/response_test.go create mode 100644 src/pkg/net/http/responsewrite_test.go create mode 100644 src/pkg/net/http/serve_test.go create mode 100644 src/pkg/net/http/server.go create mode 100644 src/pkg/net/http/sniff.go create mode 100644 src/pkg/net/http/sniff_test.go create mode 100644 src/pkg/net/http/status.go create mode 100644 src/pkg/net/http/testdata/file create mode 100644 src/pkg/net/http/testdata/index.html create mode 100644 src/pkg/net/http/testdata/style.css create mode 100644 src/pkg/net/http/transfer.go create mode 100644 src/pkg/net/http/transport.go create mode 100644 src/pkg/net/http/transport_test.go create mode 100644 src/pkg/net/http/triv.go create mode 100644 src/pkg/net/interface_linux_test.go create mode 100644 src/pkg/net/interface_netbsd.go create mode 100644 src/pkg/net/mac.go create mode 100644 src/pkg/net/mac_test.go create mode 100644 src/pkg/net/mail/message.go create mode 100644 src/pkg/net/mail/message_test.go create mode 100644 src/pkg/net/rpc/client.go create mode 100644 src/pkg/net/rpc/debug.go create mode 100644 src/pkg/net/rpc/jsonrpc/all_test.go create mode 100644 src/pkg/net/rpc/jsonrpc/client.go create mode 100644 src/pkg/net/rpc/jsonrpc/server.go create mode 100644 src/pkg/net/rpc/server.go create mode 100644 src/pkg/net/rpc/server_test.go create mode 100644 src/pkg/net/smtp/auth.go create mode 100644 src/pkg/net/smtp/smtp.go create mode 100644 src/pkg/net/smtp/smtp_test.go create mode 100644 src/pkg/net/sockopt.go create mode 100644 src/pkg/net/sockopt_bsd.go create mode 100644 src/pkg/net/sockopt_linux.go create mode 100644 src/pkg/net/sockopt_windows.go create mode 100644 src/pkg/net/sockoptip.go create mode 100644 src/pkg/net/sockoptip_bsd.go create mode 100644 src/pkg/net/sockoptip_darwin.go create mode 100644 src/pkg/net/sockoptip_freebsd.go create mode 100644 src/pkg/net/sockoptip_linux.go create mode 100644 src/pkg/net/sockoptip_netbsd.go create mode 100644 src/pkg/net/sockoptip_openbsd.go create mode 100644 src/pkg/net/sockoptip_windows.go create mode 100644 src/pkg/net/testdata/hosts create mode 100644 src/pkg/net/testdata/igmp create mode 100644 src/pkg/net/testdata/igmp6 delete mode 100644 src/pkg/net/textproto/Makefile create mode 100644 src/pkg/net/udp_test.go create mode 100644 src/pkg/net/unicast_test.go create mode 100644 src/pkg/net/url/example_test.go create mode 100644 src/pkg/net/url/url.go create mode 100644 src/pkg/net/url/url_test.go delete mode 100644 src/pkg/netchan/Makefile delete mode 100644 src/pkg/netchan/common.go delete mode 100644 src/pkg/netchan/export.go delete mode 100644 src/pkg/netchan/import.go delete mode 100644 src/pkg/netchan/netchan_test.go delete mode 100644 src/pkg/old/template/Makefile delete mode 100644 src/pkg/old/template/doc.go delete mode 100644 src/pkg/old/template/execute.go delete mode 100644 src/pkg/old/template/format.go delete mode 100644 src/pkg/old/template/parse.go delete mode 100644 src/pkg/old/template/template_test.go delete mode 100644 src/pkg/os/Makefile create mode 100644 src/pkg/os/doc.go delete mode 100644 src/pkg/os/env_plan9.go delete mode 100644 src/pkg/os/env_unix.go delete mode 100644 src/pkg/os/env_windows.go create mode 100644 src/pkg/os/error_test.go create mode 100644 src/pkg/os/error_windows.go create mode 100644 src/pkg/os/exec/example_test.go create mode 100644 src/pkg/os/exec/exec.go create mode 100644 src/pkg/os/exec/exec_test.go create mode 100644 src/pkg/os/exec/lp_plan9.go create mode 100644 src/pkg/os/exec/lp_test.go create mode 100644 src/pkg/os/exec/lp_unix.go create mode 100644 src/pkg/os/exec/lp_windows.go create mode 100644 src/pkg/os/export_test.go delete mode 100644 src/pkg/os/inotify/Makefile delete mode 100644 src/pkg/os/inotify/inotify_linux.go delete mode 100644 src/pkg/os/inotify/inotify_linux_test.go delete mode 100755 src/pkg/os/mkunixsignals.sh create mode 100644 src/pkg/os/os_unix_test.go delete mode 100644 src/pkg/os/signal/Makefile create mode 100644 src/pkg/os/signal/sig.s create mode 100644 src/pkg/os/signal/signal_stub.go create mode 100644 src/pkg/os/signal/signal_unix.go create mode 100644 src/pkg/os/signal/signal_windows_test.go create mode 100644 src/pkg/os/stat_netbsd.go delete mode 100644 src/pkg/os/time.go delete mode 100644 src/pkg/os/user/Makefile create mode 100644 src/pkg/os/user/lookup_windows.go delete mode 100644 src/pkg/patch/Makefile delete mode 100644 src/pkg/patch/apply.go delete mode 100644 src/pkg/patch/git.go delete mode 100644 src/pkg/patch/patch.go delete mode 100644 src/pkg/patch/patch_test.go delete mode 100644 src/pkg/patch/textdiff.go delete mode 100644 src/pkg/path/Makefile create mode 100644 src/pkg/path/example_test.go delete mode 100644 src/pkg/path/filepath/Makefile create mode 100644 src/pkg/path/filepath/symlink.go create mode 100644 src/pkg/path/filepath/symlink_windows.go delete mode 100644 src/pkg/rand/Makefile delete mode 100644 src/pkg/rand/exp.go delete mode 100644 src/pkg/rand/normal.go delete mode 100644 src/pkg/rand/rand.go delete mode 100644 src/pkg/rand/rand_test.go delete mode 100644 src/pkg/rand/rng.go delete mode 100644 src/pkg/rand/zipf.go delete mode 100644 src/pkg/reflect/Makefile delete mode 100644 src/pkg/regexp/Makefile create mode 100644 src/pkg/regexp/exec.go create mode 100644 src/pkg/regexp/exec_test.go create mode 100644 src/pkg/regexp/syntax/compile.go create mode 100755 src/pkg/regexp/syntax/make_perl_groups.pl create mode 100644 src/pkg/regexp/syntax/parse.go create mode 100644 src/pkg/regexp/syntax/parse_test.go create mode 100644 src/pkg/regexp/syntax/perl_groups.go create mode 100644 src/pkg/regexp/syntax/prog.go create mode 100644 src/pkg/regexp/syntax/prog_test.go create mode 100644 src/pkg/regexp/syntax/regexp.go create mode 100644 src/pkg/regexp/syntax/simplify.go create mode 100644 src/pkg/regexp/syntax/simplify_test.go create mode 100644 src/pkg/regexp/testdata/README create mode 100644 src/pkg/regexp/testdata/basic.dat create mode 100644 src/pkg/regexp/testdata/nullsubexpr.dat create mode 100644 src/pkg/regexp/testdata/re2-exhaustive.txt.bz2 create mode 100644 src/pkg/regexp/testdata/re2-search.txt create mode 100644 src/pkg/regexp/testdata/repetition.dat create mode 100644 src/pkg/regexp/testdata/testregex.c delete mode 100644 src/pkg/rpc/Makefile delete mode 100644 src/pkg/rpc/client.go delete mode 100644 src/pkg/rpc/debug.go delete mode 100644 src/pkg/rpc/jsonrpc/Makefile delete mode 100644 src/pkg/rpc/jsonrpc/all_test.go delete mode 100644 src/pkg/rpc/jsonrpc/client.go delete mode 100644 src/pkg/rpc/jsonrpc/server.go delete mode 100644 src/pkg/rpc/server.go delete mode 100644 src/pkg/rpc/server_test.go delete mode 100644 src/pkg/runtime/386/arch.h delete mode 100644 src/pkg/runtime/386/asm.s delete mode 100644 src/pkg/runtime/386/atomic.c delete mode 100644 src/pkg/runtime/386/closure.c delete mode 100644 src/pkg/runtime/386/memmove.s delete mode 100644 src/pkg/runtime/386/vlop.s delete mode 100644 src/pkg/runtime/386/vlrt.c create mode 100644 src/pkg/runtime/alg.c delete mode 100644 src/pkg/runtime/amd64/arch.h delete mode 100644 src/pkg/runtime/amd64/asm.s delete mode 100644 src/pkg/runtime/amd64/atomic.c delete mode 100644 src/pkg/runtime/amd64/closure.c delete mode 100644 src/pkg/runtime/amd64/memmove.s delete mode 100644 src/pkg/runtime/amd64/traceback.c create mode 100644 src/pkg/runtime/arch_386.h create mode 100644 src/pkg/runtime/arch_amd64.h create mode 100644 src/pkg/runtime/arch_arm.h delete mode 100644 src/pkg/runtime/arm/arch.h delete mode 100644 src/pkg/runtime/arm/asm.s delete mode 100644 src/pkg/runtime/arm/atomic.c delete mode 100644 src/pkg/runtime/arm/closure.c delete mode 100644 src/pkg/runtime/arm/memmove.s delete mode 100644 src/pkg/runtime/arm/memset.s delete mode 100644 src/pkg/runtime/arm/softfloat.c delete mode 100644 src/pkg/runtime/arm/traceback.c delete mode 100644 src/pkg/runtime/arm/vlop.s delete mode 100644 src/pkg/runtime/arm/vlrt.c create mode 100644 src/pkg/runtime/asm_386.s create mode 100644 src/pkg/runtime/asm_amd64.s create mode 100644 src/pkg/runtime/asm_arm.s create mode 100644 src/pkg/runtime/atomic_386.c create mode 100644 src/pkg/runtime/atomic_amd64.c create mode 100644 src/pkg/runtime/atomic_arm.c create mode 100644 src/pkg/runtime/callback_windows_386.c create mode 100644 src/pkg/runtime/callback_windows_amd64.c delete mode 100755 src/pkg/runtime/cgo/386.S delete mode 100644 src/pkg/runtime/cgo/Makefile delete mode 100644 src/pkg/runtime/cgo/amd64.S delete mode 100644 src/pkg/runtime/cgo/arm.S delete mode 100644 src/pkg/runtime/cgo/darwin_386.c delete mode 100644 src/pkg/runtime/cgo/darwin_amd64.c delete mode 100644 src/pkg/runtime/cgo/freebsd_386.c delete mode 100644 src/pkg/runtime/cgo/freebsd_amd64.c create mode 100644 src/pkg/runtime/cgo/gcc_386.S create mode 100644 src/pkg/runtime/cgo/gcc_amd64.S create mode 100644 src/pkg/runtime/cgo/gcc_arm.S create mode 100644 src/pkg/runtime/cgo/gcc_darwin_386.c create mode 100644 src/pkg/runtime/cgo/gcc_darwin_amd64.c create mode 100644 src/pkg/runtime/cgo/gcc_freebsd_386.c create mode 100644 src/pkg/runtime/cgo/gcc_freebsd_amd64.c create mode 100644 src/pkg/runtime/cgo/gcc_linux_386.c create mode 100644 src/pkg/runtime/cgo/gcc_linux_amd64.c create mode 100644 src/pkg/runtime/cgo/gcc_linux_arm.c create mode 100644 src/pkg/runtime/cgo/gcc_setenv.c create mode 100644 src/pkg/runtime/cgo/gcc_util.c create mode 100644 src/pkg/runtime/cgo/gcc_windows_386.c create mode 100644 src/pkg/runtime/cgo/gcc_windows_amd64.c delete mode 100644 src/pkg/runtime/cgo/linux_386.c delete mode 100644 src/pkg/runtime/cgo/linux_amd64.c delete mode 100644 src/pkg/runtime/cgo/linux_arm.c delete mode 100644 src/pkg/runtime/cgo/setenv.c delete mode 100644 src/pkg/runtime/cgo/util.c delete mode 100755 src/pkg/runtime/cgo/windows_386.c delete mode 100755 src/pkg/runtime/cgo/windows_amd64.c create mode 100644 src/pkg/runtime/closure_386.c create mode 100644 src/pkg/runtime/closure_amd64.c create mode 100644 src/pkg/runtime/closure_arm.c create mode 100644 src/pkg/runtime/compiler.go delete mode 100644 src/pkg/runtime/darwin/386/defs.h delete mode 100644 src/pkg/runtime/darwin/386/rt0.s delete mode 100644 src/pkg/runtime/darwin/386/signal.c delete mode 100644 src/pkg/runtime/darwin/386/sys.s delete mode 100644 src/pkg/runtime/darwin/amd64/defs.h delete mode 100644 src/pkg/runtime/darwin/amd64/rt0.s delete mode 100644 src/pkg/runtime/darwin/amd64/signal.c delete mode 100644 src/pkg/runtime/darwin/amd64/sys.s delete mode 100644 src/pkg/runtime/darwin/defs.c delete mode 100644 src/pkg/runtime/darwin/mem.c delete mode 100644 src/pkg/runtime/darwin/os.h delete mode 100644 src/pkg/runtime/darwin/signals.h delete mode 100644 src/pkg/runtime/darwin/thread.c delete mode 100644 src/pkg/runtime/debug/Makefile create mode 100644 src/pkg/runtime/defs1_linux.go create mode 100644 src/pkg/runtime/defs2_linux.go create mode 100644 src/pkg/runtime/defs_arm_linux.go create mode 100644 src/pkg/runtime/defs_darwin.go create mode 100644 src/pkg/runtime/defs_darwin_386.h create mode 100644 src/pkg/runtime/defs_darwin_amd64.h create mode 100644 src/pkg/runtime/defs_freebsd.go create mode 100644 src/pkg/runtime/defs_freebsd_386.h create mode 100644 src/pkg/runtime/defs_freebsd_amd64.h create mode 100644 src/pkg/runtime/defs_linux.go create mode 100644 src/pkg/runtime/defs_linux_386.h create mode 100644 src/pkg/runtime/defs_linux_amd64.h create mode 100644 src/pkg/runtime/defs_linux_arm.h create mode 100644 src/pkg/runtime/defs_netbsd.go create mode 100644 src/pkg/runtime/defs_netbsd_386.h create mode 100644 src/pkg/runtime/defs_netbsd_amd64.h create mode 100644 src/pkg/runtime/defs_openbsd.go create mode 100644 src/pkg/runtime/defs_openbsd_386.h create mode 100644 src/pkg/runtime/defs_openbsd_amd64.h create mode 100644 src/pkg/runtime/defs_plan9_386.h create mode 100644 src/pkg/runtime/defs_windows.go create mode 100644 src/pkg/runtime/defs_windows_386.h create mode 100644 src/pkg/runtime/defs_windows_amd64.h delete mode 100644 src/pkg/runtime/freebsd/386/defs.h delete mode 100644 src/pkg/runtime/freebsd/386/rt0.s delete mode 100644 src/pkg/runtime/freebsd/386/signal.c delete mode 100644 src/pkg/runtime/freebsd/386/sys.s delete mode 100644 src/pkg/runtime/freebsd/amd64/defs.h delete mode 100644 src/pkg/runtime/freebsd/amd64/rt0.s delete mode 100644 src/pkg/runtime/freebsd/amd64/signal.c delete mode 100644 src/pkg/runtime/freebsd/amd64/sys.s delete mode 100644 src/pkg/runtime/freebsd/defs.c delete mode 100644 src/pkg/runtime/freebsd/mem.c delete mode 100644 src/pkg/runtime/freebsd/os.h delete mode 100644 src/pkg/runtime/freebsd/signals.h delete mode 100644 src/pkg/runtime/freebsd/thread.c create mode 100644 src/pkg/runtime/gc_test.go delete mode 100644 src/pkg/runtime/goc2c.c delete mode 100644 src/pkg/runtime/linux/386/defs.h delete mode 100644 src/pkg/runtime/linux/386/rt0.s delete mode 100644 src/pkg/runtime/linux/386/signal.c delete mode 100644 src/pkg/runtime/linux/386/sys.s delete mode 100644 src/pkg/runtime/linux/amd64/defs.h delete mode 100644 src/pkg/runtime/linux/amd64/rt0.s delete mode 100644 src/pkg/runtime/linux/amd64/signal.c delete mode 100644 src/pkg/runtime/linux/amd64/sys.s delete mode 100644 src/pkg/runtime/linux/arm/defs.h delete mode 100644 src/pkg/runtime/linux/arm/rt0.s delete mode 100644 src/pkg/runtime/linux/arm/signal.c delete mode 100644 src/pkg/runtime/linux/arm/sys.s delete mode 100644 src/pkg/runtime/linux/defs.c delete mode 100644 src/pkg/runtime/linux/defs1.c delete mode 100644 src/pkg/runtime/linux/defs2.c delete mode 100644 src/pkg/runtime/linux/defs_arm.c delete mode 100644 src/pkg/runtime/linux/mem.c delete mode 100644 src/pkg/runtime/linux/os.h delete mode 100644 src/pkg/runtime/linux/signals.h delete mode 100644 src/pkg/runtime/linux/thread.c create mode 100644 src/pkg/runtime/lock_futex.c create mode 100644 src/pkg/runtime/lock_sema.c create mode 100644 src/pkg/runtime/malloc1.go create mode 100644 src/pkg/runtime/mallocrand.go create mode 100644 src/pkg/runtime/mallocrep.go create mode 100644 src/pkg/runtime/mallocrep1.go create mode 100644 src/pkg/runtime/mem_darwin.c create mode 100644 src/pkg/runtime/mem_freebsd.c create mode 100644 src/pkg/runtime/mem_linux.c create mode 100644 src/pkg/runtime/mem_netbsd.c create mode 100644 src/pkg/runtime/mem_openbsd.c create mode 100644 src/pkg/runtime/mem_plan9.c create mode 100644 src/pkg/runtime/mem_windows.c create mode 100644 src/pkg/runtime/memmove_386.s create mode 100644 src/pkg/runtime/memmove_amd64.s create mode 100644 src/pkg/runtime/memmove_arm.s create mode 100644 src/pkg/runtime/memset_arm.s create mode 100644 src/pkg/runtime/mfinal_test.go delete mode 100755 src/pkg/runtime/mkgodefs.sh delete mode 100644 src/pkg/runtime/openbsd/amd64/defs.h delete mode 100644 src/pkg/runtime/openbsd/amd64/rt0.s delete mode 100644 src/pkg/runtime/openbsd/amd64/signal.c delete mode 100644 src/pkg/runtime/openbsd/amd64/sys.s delete mode 100644 src/pkg/runtime/openbsd/defs.c delete mode 100644 src/pkg/runtime/openbsd/mem.c delete mode 100644 src/pkg/runtime/openbsd/os.h delete mode 100644 src/pkg/runtime/openbsd/signals.h delete mode 100644 src/pkg/runtime/openbsd/thread.c create mode 100644 src/pkg/runtime/os_darwin.h create mode 100644 src/pkg/runtime/os_freebsd.h create mode 100644 src/pkg/runtime/os_linux.h create mode 100644 src/pkg/runtime/os_netbsd.h create mode 100644 src/pkg/runtime/os_openbsd.h create mode 100644 src/pkg/runtime/os_plan9.h create mode 100644 src/pkg/runtime/os_windows.h delete mode 100644 src/pkg/runtime/plan9/386/defs.h delete mode 100644 src/pkg/runtime/plan9/386/rt0.s delete mode 100644 src/pkg/runtime/plan9/386/signal.c delete mode 100644 src/pkg/runtime/plan9/386/sys.s delete mode 100644 src/pkg/runtime/plan9/mem.c delete mode 100644 src/pkg/runtime/plan9/os.h delete mode 100644 src/pkg/runtime/plan9/signals.h delete mode 100644 src/pkg/runtime/plan9/thread.c delete mode 100644 src/pkg/runtime/pprof/Makefile create mode 100644 src/pkg/runtime/rt0_darwin_386.s create mode 100644 src/pkg/runtime/rt0_darwin_amd64.s create mode 100644 src/pkg/runtime/rt0_freebsd_386.s create mode 100644 src/pkg/runtime/rt0_freebsd_amd64.s create mode 100644 src/pkg/runtime/rt0_linux_386.s create mode 100644 src/pkg/runtime/rt0_linux_amd64.s create mode 100644 src/pkg/runtime/rt0_linux_arm.s create mode 100644 src/pkg/runtime/rt0_netbsd_386.s create mode 100644 src/pkg/runtime/rt0_netbsd_amd64.s create mode 100644 src/pkg/runtime/rt0_openbsd_386.s create mode 100644 src/pkg/runtime/rt0_openbsd_amd64.s create mode 100644 src/pkg/runtime/rt0_plan9_386.s create mode 100644 src/pkg/runtime/rt0_windows_386.s create mode 100644 src/pkg/runtime/rt0_windows_amd64.s create mode 100644 src/pkg/runtime/runtime_linux_test.go create mode 100644 src/pkg/runtime/runtime_test.go delete mode 100644 src/pkg/runtime/sema_test.go delete mode 100644 src/pkg/runtime/sig.go create mode 100644 src/pkg/runtime/signal_darwin_386.c create mode 100644 src/pkg/runtime/signal_darwin_amd64.c create mode 100644 src/pkg/runtime/signal_freebsd_386.c create mode 100644 src/pkg/runtime/signal_freebsd_amd64.c create mode 100644 src/pkg/runtime/signal_linux_386.c create mode 100644 src/pkg/runtime/signal_linux_amd64.c create mode 100644 src/pkg/runtime/signal_linux_arm.c create mode 100644 src/pkg/runtime/signal_netbsd_386.c create mode 100644 src/pkg/runtime/signal_netbsd_amd64.c create mode 100644 src/pkg/runtime/signal_openbsd_386.c create mode 100644 src/pkg/runtime/signal_openbsd_amd64.c create mode 100644 src/pkg/runtime/signal_plan9_386.c create mode 100644 src/pkg/runtime/signal_unix.c create mode 100644 src/pkg/runtime/signal_windows_386.c create mode 100644 src/pkg/runtime/signal_windows_amd64.c create mode 100644 src/pkg/runtime/signals_darwin.h create mode 100644 src/pkg/runtime/signals_freebsd.h create mode 100644 src/pkg/runtime/signals_linux.h create mode 100644 src/pkg/runtime/signals_netbsd.h create mode 100644 src/pkg/runtime/signals_openbsd.h create mode 100644 src/pkg/runtime/signals_plan9.h create mode 100644 src/pkg/runtime/signals_windows.h create mode 100644 src/pkg/runtime/softfloat_arm.c create mode 100644 src/pkg/runtime/stack_test.go create mode 100644 src/pkg/runtime/sys_darwin_386.s create mode 100644 src/pkg/runtime/sys_darwin_amd64.s create mode 100644 src/pkg/runtime/sys_freebsd_386.s create mode 100644 src/pkg/runtime/sys_freebsd_amd64.s create mode 100644 src/pkg/runtime/sys_linux_386.s create mode 100644 src/pkg/runtime/sys_linux_amd64.s create mode 100644 src/pkg/runtime/sys_linux_arm.s create mode 100644 src/pkg/runtime/sys_netbsd_386.s create mode 100644 src/pkg/runtime/sys_netbsd_amd64.s create mode 100644 src/pkg/runtime/sys_openbsd_386.s create mode 100644 src/pkg/runtime/sys_openbsd_amd64.s create mode 100644 src/pkg/runtime/sys_plan9_386.s create mode 100644 src/pkg/runtime/sys_windows_386.s create mode 100644 src/pkg/runtime/sys_windows_amd64.s create mode 100644 src/pkg/runtime/syscall_windows.goc create mode 100644 src/pkg/runtime/syscall_windows_test.go create mode 100644 src/pkg/runtime/thread_darwin.c create mode 100644 src/pkg/runtime/thread_freebsd.c create mode 100644 src/pkg/runtime/thread_linux.c create mode 100644 src/pkg/runtime/thread_netbsd.c create mode 100644 src/pkg/runtime/thread_openbsd.c create mode 100644 src/pkg/runtime/thread_plan9.c create mode 100644 src/pkg/runtime/thread_windows.c create mode 100644 src/pkg/runtime/time.goc create mode 100644 src/pkg/runtime/traceback_arm.c create mode 100644 src/pkg/runtime/traceback_x86.c create mode 100644 src/pkg/runtime/vlop_386.s create mode 100644 src/pkg/runtime/vlop_arm.s create mode 100644 src/pkg/runtime/vlrt_386.c create mode 100644 src/pkg/runtime/vlrt_arm.c delete mode 100644 src/pkg/runtime/windows/386/defs.h delete mode 100644 src/pkg/runtime/windows/386/rt0.s delete mode 100644 src/pkg/runtime/windows/386/signal.c delete mode 100644 src/pkg/runtime/windows/386/sys.s delete mode 100644 src/pkg/runtime/windows/amd64/defs.h delete mode 100644 src/pkg/runtime/windows/amd64/rt0.s delete mode 100644 src/pkg/runtime/windows/amd64/signal.c delete mode 100644 src/pkg/runtime/windows/amd64/sys.s delete mode 100644 src/pkg/runtime/windows/defs.c delete mode 100644 src/pkg/runtime/windows/mem.c delete mode 100644 src/pkg/runtime/windows/os.h delete mode 100644 src/pkg/runtime/windows/signals.h delete mode 100644 src/pkg/runtime/windows/syscall.goc delete mode 100644 src/pkg/runtime/windows/thread.c delete mode 100644 src/pkg/scanner/Makefile delete mode 100644 src/pkg/scanner/scanner.go delete mode 100644 src/pkg/scanner/scanner_test.go delete mode 100644 src/pkg/smtp/Makefile delete mode 100644 src/pkg/smtp/auth.go delete mode 100644 src/pkg/smtp/smtp.go delete mode 100644 src/pkg/smtp/smtp_test.go delete mode 100644 src/pkg/sort/Makefile create mode 100644 src/pkg/sort/example_interface_test.go create mode 100644 src/pkg/sort/example_reverse_test.go create mode 100644 src/pkg/sort/example_test.go create mode 100644 src/pkg/sort/export_test.go delete mode 100644 src/pkg/strconv/Makefile create mode 100644 src/pkg/strconv/extfloat.go create mode 100644 src/pkg/strconv/isprint.go create mode 100644 src/pkg/strconv/makeisprint.go delete mode 100644 src/pkg/strings/Makefile create mode 100644 src/pkg/strings/example_test.go create mode 100644 src/pkg/strings/export_test.go create mode 100644 src/pkg/strings/reader_test.go create mode 100644 src/pkg/strings/replace.go create mode 100644 src/pkg/strings/replace_test.go delete mode 100644 src/pkg/sync/Makefile delete mode 100644 src/pkg/sync/atomic/Makefile create mode 100644 src/pkg/sync/example_test.go create mode 100644 src/pkg/sync/export_test.go create mode 100644 src/pkg/sync/runtime.go create mode 100644 src/pkg/sync/runtime_sema_test.go delete mode 100644 src/pkg/syscall/Makefile create mode 100644 src/pkg/syscall/asm_netbsd_386.s create mode 100644 src/pkg/syscall/asm_netbsd_amd64.s create mode 100644 src/pkg/syscall/asm_openbsd_386.s create mode 100644 src/pkg/syscall/asm_openbsd_amd64.s create mode 100644 src/pkg/syscall/dll_windows.go create mode 100644 src/pkg/syscall/env_plan9.go create mode 100644 src/pkg/syscall/env_unix.go create mode 100644 src/pkg/syscall/env_windows.go create mode 100644 src/pkg/syscall/exec_bsd.go create mode 100644 src/pkg/syscall/exec_linux.go create mode 100755 src/pkg/syscall/mksysctl_openbsd.pl create mode 100755 src/pkg/syscall/mksysnum_netbsd.pl create mode 100755 src/pkg/syscall/mksysnum_openbsd.pl create mode 100644 src/pkg/syscall/route_netbsd.go create mode 100644 src/pkg/syscall/route_openbsd.go create mode 100644 src/pkg/syscall/security_windows.go delete mode 100644 src/pkg/syscall/syscall_386.go delete mode 100644 src/pkg/syscall/syscall_amd64.go delete mode 100644 src/pkg/syscall/syscall_arm.go create mode 100644 src/pkg/syscall/syscall_netbsd.go create mode 100644 src/pkg/syscall/syscall_netbsd_386.go create mode 100644 src/pkg/syscall/syscall_netbsd_amd64.go create mode 100644 src/pkg/syscall/syscall_openbsd.go create mode 100644 src/pkg/syscall/syscall_openbsd_386.go create mode 100644 src/pkg/syscall/syscall_openbsd_amd64.go delete mode 100644 src/pkg/syscall/types_darwin.c create mode 100644 src/pkg/syscall/types_darwin.go delete mode 100644 src/pkg/syscall/types_freebsd.c create mode 100644 src/pkg/syscall/types_freebsd.go delete mode 100644 src/pkg/syscall/types_linux.c create mode 100644 src/pkg/syscall/types_linux.go create mode 100644 src/pkg/syscall/types_netbsd.go create mode 100644 src/pkg/syscall/types_openbsd.go create mode 100644 src/pkg/syscall/zerrors_netbsd_386.go create mode 100644 src/pkg/syscall/zerrors_netbsd_amd64.go create mode 100644 src/pkg/syscall/zerrors_openbsd_386.go create mode 100644 src/pkg/syscall/zerrors_openbsd_amd64.go create mode 100644 src/pkg/syscall/zsyscall_netbsd_386.go create mode 100644 src/pkg/syscall/zsyscall_netbsd_amd64.go create mode 100644 src/pkg/syscall/zsyscall_openbsd_386.go create mode 100644 src/pkg/syscall/zsyscall_openbsd_amd64.go create mode 100644 src/pkg/syscall/zsysctl_openbsd.go create mode 100644 src/pkg/syscall/zsysnum_netbsd_386.go create mode 100644 src/pkg/syscall/zsysnum_netbsd_amd64.go create mode 100644 src/pkg/syscall/zsysnum_openbsd_386.go create mode 100644 src/pkg/syscall/zsysnum_openbsd_amd64.go create mode 100644 src/pkg/syscall/ztypes_netbsd_386.go create mode 100644 src/pkg/syscall/ztypes_netbsd_amd64.go create mode 100644 src/pkg/syscall/ztypes_openbsd_386.go create mode 100644 src/pkg/syscall/ztypes_openbsd_amd64.go delete mode 100644 src/pkg/syslog/Makefile delete mode 100644 src/pkg/syslog/syslog.go delete mode 100644 src/pkg/syslog/syslog_test.go delete mode 100644 src/pkg/syslog/syslog_unix.go delete mode 100644 src/pkg/tabwriter/Makefile delete mode 100644 src/pkg/tabwriter/tabwriter.go delete mode 100644 src/pkg/tabwriter/tabwriter_test.go delete mode 100644 src/pkg/template/Makefile delete mode 100644 src/pkg/template/doc.go delete mode 100644 src/pkg/template/exec.go delete mode 100644 src/pkg/template/exec_test.go delete mode 100644 src/pkg/template/funcs.go delete mode 100644 src/pkg/template/helper.go delete mode 100644 src/pkg/template/parse.go delete mode 100644 src/pkg/template/parse/Makefile delete mode 100644 src/pkg/template/parse/lex.go delete mode 100644 src/pkg/template/parse/lex_test.go delete mode 100644 src/pkg/template/parse/node.go delete mode 100644 src/pkg/template/parse/parse.go delete mode 100644 src/pkg/template/parse/parse_test.go delete mode 100644 src/pkg/template/parse/set.go delete mode 100644 src/pkg/template/set.go delete mode 100644 src/pkg/template/set_test.go delete mode 100644 src/pkg/template/testdata/file1.tmpl delete mode 100644 src/pkg/template/testdata/file2.tmpl delete mode 100644 src/pkg/template/testdata/tmpl1.tmpl delete mode 100644 src/pkg/template/testdata/tmpl2.tmpl delete mode 100644 src/pkg/testing/Makefile create mode 100644 src/pkg/testing/example.go delete mode 100644 src/pkg/testing/iotest/Makefile delete mode 100644 src/pkg/testing/quick/Makefile delete mode 100644 src/pkg/testing/script/Makefile delete mode 100644 src/pkg/testing/script/script.go delete mode 100644 src/pkg/testing/script/script_test.go create mode 100644 src/pkg/text/scanner/scanner.go create mode 100644 src/pkg/text/scanner/scanner_test.go create mode 100644 src/pkg/text/tabwriter/example_test.go create mode 100644 src/pkg/text/tabwriter/tabwriter.go create mode 100644 src/pkg/text/tabwriter/tabwriter_test.go create mode 100644 src/pkg/text/template/doc.go create mode 100644 src/pkg/text/template/example_test.go create mode 100644 src/pkg/text/template/examplefiles_test.go create mode 100644 src/pkg/text/template/examplefunc_test.go create mode 100644 src/pkg/text/template/exec.go create mode 100644 src/pkg/text/template/exec_test.go create mode 100644 src/pkg/text/template/funcs.go create mode 100644 src/pkg/text/template/helper.go create mode 100644 src/pkg/text/template/multi_test.go create mode 100644 src/pkg/text/template/parse/lex.go create mode 100644 src/pkg/text/template/parse/lex_test.go create mode 100644 src/pkg/text/template/parse/node.go create mode 100644 src/pkg/text/template/parse/parse.go create mode 100644 src/pkg/text/template/parse/parse_test.go create mode 100644 src/pkg/text/template/template.go create mode 100644 src/pkg/text/template/testdata/file1.tmpl create mode 100644 src/pkg/text/template/testdata/file2.tmpl create mode 100644 src/pkg/text/template/testdata/tmpl1.tmpl create mode 100644 src/pkg/text/template/testdata/tmpl2.tmpl delete mode 100644 src/pkg/time/Makefile create mode 100644 src/pkg/time/example_test.go create mode 100644 src/pkg/time/internal_test.go delete mode 100644 src/pkg/time/sys.go delete mode 100644 src/pkg/time/sys_posix.go create mode 100644 src/pkg/time/sys_unix.go create mode 100644 src/pkg/time/sys_windows.go create mode 100644 src/pkg/time/zoneinfo.go delete mode 100644 src/pkg/time/zoneinfo_posix.go create mode 100644 src/pkg/time/zoneinfo_read.go delete mode 100644 src/pkg/try/Makefile delete mode 100644 src/pkg/try/try.go delete mode 100644 src/pkg/try/try_test.go create mode 100644 src/pkg/unicode/utf16/export_test.go create mode 100644 src/pkg/unicode/utf16/utf16.go create mode 100644 src/pkg/unicode/utf16/utf16_test.go create mode 100644 src/pkg/unicode/utf8/utf8.go create mode 100644 src/pkg/unicode/utf8/utf8_test.go delete mode 100644 src/pkg/url/Makefile delete mode 100644 src/pkg/url/url.go delete mode 100644 src/pkg/url/url_test.go delete mode 100644 src/pkg/utf16/Makefile delete mode 100644 src/pkg/utf16/utf16.go delete mode 100644 src/pkg/utf16/utf16_test.go delete mode 100644 src/pkg/utf8/Makefile delete mode 100644 src/pkg/utf8/string.go delete mode 100644 src/pkg/utf8/string_test.go delete mode 100644 src/pkg/utf8/utf8.go delete mode 100644 src/pkg/utf8/utf8_test.go delete mode 100644 src/pkg/websocket/Makefile delete mode 100644 src/pkg/websocket/client.go delete mode 100644 src/pkg/websocket/server.go delete mode 100644 src/pkg/websocket/websocket.go delete mode 100644 src/pkg/websocket/websocket_test.go delete mode 100644 src/pkg/xml/Makefile delete mode 100644 src/pkg/xml/atom_test.go delete mode 100644 src/pkg/xml/embed_test.go delete mode 100644 src/pkg/xml/marshal.go delete mode 100644 src/pkg/xml/marshal_test.go delete mode 100644 src/pkg/xml/read.go delete mode 100644 src/pkg/xml/read_test.go delete mode 100644 src/pkg/xml/xml.go delete mode 100644 src/pkg/xml/xml_test.go delete mode 100755 src/quietgcc.bash create mode 100644 src/run.bat delete mode 100755 src/version.bash (limited to 'src') diff --git a/src/Make.ccmd b/src/Make.ccmd deleted file mode 100644 index 40cc3a0e8..000000000 --- a/src/Make.ccmd +++ /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. - -# Makefile for commands written in C. - -ifeq (windows,$(findstring windows, $(shell uname | tr A-Z a-z | sed 's/mingw/windows/'))) -TARG:=$(TARG).exe -endif - -$(TARG): $(OFILES) $(LIB) - $(HOST_LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lmach -lbio -l9 -lm $(HOST_LDFLAGS) - -$(OFILES): $(HFILES) - -CLEANFILES+=y.tab.[ch] - -clean: - rm -f *.$(HOST_O) $(TARG) $(CLEANFILES) - -nuke: clean - rm -f "$(GOBIN)/$(TARG)" - -ifneq ($(NOINSTALL),1) -install: $(QUOTED_GOBIN)/$(TARG) -endif - -$(QUOTED_GOBIN)/$(TARG): $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) - -y.tab.h: $(YFILES) - bison -y $(HOST_YFLAGS) $(YFILES) - -y.tab.c: y.tab.h - test -f y.tab.c && touch y.tab.c - -all: $(TARG) - -# Use $(PWD)/$*.c so that gdb shows full path in stack traces. -%.$(HOST_O): %.c - $(HOST_CC) $(HOST_CFLAGS) -c "$(PWD)/$*.c" - -# These are used by enough different Makefiles to be -# worth writing down in one place, even if they don't -# apply to every command that builds with Make.ccmd -../%l/enam.o: - cd ../$*l; $(MAKE) enam.o - diff --git a/src/Make.clib b/src/Make.clib deleted file mode 100644 index 4a7ea02d9..000000000 --- a/src/Make.clib +++ /dev/null @@ -1,37 +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. - -# Makefile included for C libraries - -all: $(LIB) - -# Use $(PWD)/$*.c so that gdb shows full path in stack traces. -%.$(HOST_O): %.c - $(HOST_CC) $(HOST_CFLAGS) -c "$(PWD)/$*.c" - -$(OFILES): $(HFILES) - -ifneq ($(NOINSTALL),1) -install: $(QUOTED_GOROOT)/lib/$(LIB) -endif - -$(QUOTED_GOROOT)/lib/$(LIB): $(LIB) - cp $(LIB) "$(GOROOT)/lib/$(LIB)" - -$(LIB): $(OFILES) - $(HOST_AR) rsc $(LIB) $(OFILES) - -CLEANFILES+=y.tab.[ch] y.output a.out $(LIB) - -clean: - rm -f *.$(HOST_O) $(CLEANFILES) - -nuke: clean - rm -f "$(GOROOT)/lib/$(LIB)" - -y.tab.h: $(YFILES) - LANG=C LANGUAGE="en_US.UTF8" bison -v -y $(HOST_YFLAGS) $(YFILES) - -y.tab.c: y.tab.h - test -f y.tab.c && touch y.tab.c diff --git a/src/Make.cmd b/src/Make.cmd deleted file mode 100644 index 27c6a2e13..000000000 --- a/src/Make.cmd +++ /dev/null @@ -1,50 +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. - -ifeq ($(GOOS),windows) -TARG:=$(TARG).exe -endif - -ifeq ($(TARGDIR),) -TARGDIR:=$(QUOTED_GOBIN) -endif - -all: $(TARG) - -include $(QUOTED_GOROOT)/src/Make.common - -PREREQ+=$(patsubst %,%.make,$(DEPS)) - -$(TARG): _go_.$O - $(LD) $(LDIMPORTS) -o $@ _go_.$O - -_go_.$O: $(GOFILES) $(PREREQ) - $(GC) $(GCIMPORTS) -o $@ $(GOFILES) - -install: $(TARGDIR)/$(TARG) - -$(TARGDIR)/$(TARG): $(TARG) - mkdir -p $(TARGDIR) && cp -f $(TARG) $(TARGDIR) - -CLEANFILES+=$(TARG) _test _testmain.go test.out build.out - -nuke: clean - rm -f $(TARGDIR)/$(TARG) - -# for gotest -testpackage: _test/main.a - -testpackage-clean: - rm -f _test/main.a _gotest_.$O - -_test/main.a: _gotest_.$O - @mkdir -p _test - rm -f $@ - gopack grc $@ _gotest_.$O - -_gotest_.$O: $(GOFILES) $(GOTESTFILES) - $(GC) $(GCIMPORTS) -o $@ $(GOFILES) $(GOTESTFILES) - -importpath: - echo main diff --git a/src/Make.common b/src/Make.common deleted file mode 100644 index 0b27d07f9..000000000 --- a/src/Make.common +++ /dev/null @@ -1,22 +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. - -clean: - rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES) - -install.clean: install - rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES) || true - -test.clean: test - rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES) || true - -testshort.clean: testshort - rm -rf *.o *.a *.[$(OS)] [$(OS)].out $(CLEANFILES) || true - -%.make: - $(MAKE) -C $* install - -.PHONY: all clean nuke install coverage test bench testpackage-clean\ - importpath dir - diff --git a/src/Make.dist b/src/Make.dist new file mode 100644 index 000000000..cae892de9 --- /dev/null +++ b/src/Make.dist @@ -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. + +# Run go tool dist to install a command. +# The -v causes dist to print the name of each directory as it runs. +# The -vv causes dist to print each build command as it runs. +# go tool dist clean cleans all directories, not just this one, +# but it's as close as we can get. + +# Default target (first). +install: + go tool dist install -v + +verbose: + go tool dist install -vv + +clean: + go tool dist clean diff --git a/src/Make.inc b/src/Make.inc deleted file mode 100644 index 8f549f624..000000000 --- a/src/Make.inc +++ /dev/null @@ -1,169 +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. - -# Makefile included by all other Go makefiles. - -# Clear variables that must come from Makefiles, -# not the environment. -LIB:= -TARG:= -GOFILES:= -HFILES:= -OFILES:= -YFILES:= - -# GOROOT must be set. -ifeq ($(GOROOT),) -$(error $$GOROOT is not set; use gomake or set $$GOROOT in your environment) -endif - -# Set up GOROOT_FINAL, GOARCH, GOOS if needed. -GOROOT_FINAL?=$(GOROOT) - -ifeq ($(GOHOSTOS),) -GOHOSTOS:=$(shell uname | tr A-Z a-z | sed 's/mingw/windows/; s/.*windows.*/windows/') -endif - -ifeq ($(GOOS),) -GOOS:=$(GOHOSTOS) -endif - -GOOS_LIST=\ - darwin\ - freebsd\ - linux\ - plan9\ - windows\ - -GOARCH_LIST=\ - 386\ - amd64\ - arm\ - -ifeq ($(filter $(GOOS),$(GOOS_LIST)),) -$(error Invalid $$GOOS '$(GOOS)'; must be one of: $(GOOS_LIST)) -endif - -ifeq ($(GOHOSTARCH),) -ifeq ($(GOHOSTOS),darwin) -# Even on 64-bit platform, darwin uname -m prints i386. -# Check for amd64 with sysctl instead. -GOHOSTARCH:=${shell if sysctl machdep.cpu.extfeatures | grep EM64T >/dev/null; then echo amd64; else uname -m | sed 's/i386/386/'; fi} -else -# Ask uname -m for the processor. -GOHOSTARCH:=${shell uname -m | sed 's/^..86$$/386/; s/^.86$$/386/; s/x86_64/amd64/; s/arm.*/arm/'} -endif -endif - -ifeq ($(GOARCH),) -GOARCH:=$(GOHOSTARCH) -endif - -# darwin requires GOHOSTARCH match GOARCH -ifeq ($(GOOS),darwin) -GOHOSTARCH:=$(GOARCH) -endif - -ifeq ($(filter $(GOARCH),$(GOARCH_LIST)),) -$(error Invalid $$GOARCH '$(GOARCH)'; must be one of: $(GOARCH_LIST)) -endif - -ifeq ($(GOARCH),386) -O:=8 -else ifeq ($(GOARCH),amd64) -O:=6 -else ifeq ($(GOARCH),arm) -O:=5 -ifneq ($(GOOS),linux) -$(error Invalid $$GOOS '$(GOOS)' for GOARCH=arm; must be linux) -endif -else -$(error Missing $$O for '$(GOARCH)') -endif - -# Save for recursive make to avoid recomputing. -export GOARCH GOOS GOHOSTARCH GOHOSTOS GOARCH_LIST GOOS_LIST - -# ugly hack to deal with whitespaces in $GOROOT -nullstring := -space := $(nullstring) # a space at the end -QUOTED_GOROOT:=$(subst $(space),\ ,$(GOROOT)) - -# default GOBIN -ifndef GOBIN -GOBIN=$(QUOTED_GOROOT)/bin -endif -QUOTED_GOBIN=$(subst $(space),\ ,$(GOBIN)) - -AS=${O}a -CC=${O}c -GC=${O}g -LD=${O}l -OS=568vq -CFLAGS=-FVw - -HOST_CC=quietgcc -HOST_LD=quietgcc -HOST_O=o -HOST_YFLAGS=-d -HOST_AR?=ar - -# These two variables can be overridden in the environment -# to build with other flags. They are like $CFLAGS and $LDFLAGS -# in a more typical GNU build. We are more explicit about the names -# here because there are different compilers being run during the -# build (both gcc and 6c, for example). -HOST_EXTRA_CFLAGS?=-ggdb -O2 -HOST_EXTRA_LDFLAGS?= - -HOST_CFLAGS=-I"$(GOROOT)/include" $(HOST_EXTRA_CFLAGS) -HOST_LDFLAGS=$(HOST_EXTRA_LDFLAGS) -PWD=$(shell pwd) - -# Decide whether use of cgo is okay. -ifeq ($(CGO_ENABLED),) -# Default on... -CGO_ENABLED:=1 -ifeq ($(GOARCH),arm) # ... but not on ARM -CGO_ENABLED:=0 -endif -ifeq ($(GOOS),plan9) # ... and not on Plan 9 -CGO_ENABLED:=0 -endif -ifeq ($(GOOS),openbsd) # ... and not on OpenBSD -CGO_ENABLED:=0 -endif -endif - -# Make environment more standard. -LANG:= -LC_ALL:=C -LC_CTYPE:=C -GREP_OPTIONS:= -GREP_COLORS:= -export LANG LC_ALL LC_CTYPE GREP_OPTIONS GREP_COLORS - -go-env: - @echo export GOARCH="$(GOARCH)" - @echo export GOOS="$(GOOS)" - @echo export GOHOSTARCH="$(GOHOSTARCH)" - @echo export GOHOSTOS="$(GOHOSTOS)" - @echo export CGO_ENABLED="$(CGO_ENABLED)" - @echo export O="$O" - @echo export AS="$(AS)" - @echo export CC="$(CC)" - @echo export GC="$(GC)" - @echo export LD="$(LD)" - @echo export OS="$(OS)" - @echo export CFLAGS="$(CFLAGS)" - @echo export LANG="$(LANG)" - @echo export LC_ALL="$(LC_ALL)" - @echo export LC_CTYPE="$(LC_CTYPE)" - @echo export GREP_OPTIONS="$(GREP_OPTIONS)" - @echo export GREP_COLORS="$(GREP_COLORS)" - @echo MAKE_GO_ENV_WORKED=1 - -# Don't let the targets in this file be used -# as the default make target. -.DEFAULT_GOAL:= diff --git a/src/Make.pkg b/src/Make.pkg deleted file mode 100644 index fc80cf6e6..000000000 --- a/src/Make.pkg +++ /dev/null @@ -1,249 +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. - -all: package -package: _obj/$(TARG).a -testpackage: _test/$(TARG).a - -include $(QUOTED_GOROOT)/src/Make.common - -# The quietgcc wrapper is for our own source code -# while building the libraries, not arbitrary source code -# as encountered by cgo. -ifeq ($(HOST_CC),quietgcc) -HOST_CC:=gcc -endif -ifeq ($(HOST_LD),quietgcc) -HOST_LD:=gcc -endif - -# GNU Make 3.80 has a bug in lastword -# elem=$(lastword $(subst /, ,$(TARG))) -TARG_words=$(subst /, ,$(TARG)) -elem=$(word $(words $(TARG_words)),$(TARG_words)) - -ifeq ($(elem),$(TARG)) -dir= -else -dir=$(patsubst %/$(elem),%,$(TARG)) -endif - -pkgdir=$(QUOTED_GOROOT)/pkg/$(GOOS)_$(GOARCH) - -ifeq ($(TARGDIR),) -TARGDIR:=$(pkgdir) -endif - -INSTALLFILES+=$(TARGDIR)/$(TARG).a - -# The rest of the cgo rules are below, but these variable updates -# must be done here so they apply to the main rules. -ifdef CGOFILES -GOFILES+=$(patsubst %.go,_obj/%.cgo1.go,$(CGOFILES)) _obj/_cgo_gotypes.go -CGO_OFILES+=$(patsubst %.go,%.cgo2.o,$(CGOFILES)) _cgo_export.o -OFILES+=_cgo_defun.$O _cgo_import.$O $(CGO_OFILES) -endif - -ifdef SWIGFILES -GOFILES+=$(patsubst %.swig,_obj/%.go,$(patsubst %.swigcxx,%.swig,$(SWIGFILES))) -OFILES+=$(patsubst %.swig,_obj/%_gc.$O,$(patsubst %.swigcxx,%.swig,$(SWIGFILES))) -SWIG_PREFIX=$(subst /,-,$(TARG)) -SWIG_SOS+=$(patsubst %.swig,_obj/$(SWIG_PREFIX)-%.so,$(patsubst %.swigcxx,%.swig,$(SWIGFILES))) -INSTALLFILES+=$(patsubst %.swig,$(TARGDIR)/swig/$(SWIG_PREFIX)-%.so,$(patsubst %.swigcxx,%.swig,$(SWIGFILES))) -endif - -PREREQ+=$(patsubst %,%.make,$(DEPS)) - -coverage: - gotest - 6cov -g $(shell pwd) $O.out | grep -v '_test\.go:' - -CLEANFILES+=*.so _obj _test _testmain.go *.exe _cgo* test.out build.out - -test: - gotest - -testshort: - gotest -test.short -test.timeout=120 - -bench: - gotest -test.bench=. -test.run="Do not run tests" - -nuke: clean - rm -f $(TARGDIR)/$(TARG).a - -testpackage-clean: - rm -f _test/$(TARG).a - -install: $(INSTALLFILES) - -$(TARGDIR)/$(TARG).a: _obj/$(TARG).a - @mkdir -p $(TARGDIR)/$(dir) - cp _obj/$(TARG).a "$@" - -_go_.$O: $(GOFILES) $(PREREQ) - $(GC) $(GCIMPORTS) -o $@ $(GOFILES) - -_gotest_.$O: $(GOFILES) $(GOTESTFILES) $(PREREQ) - $(GC) $(GCIMPORTS) -o $@ $(GOFILES) $(GOTESTFILES) - -_obj/$(TARG).a: _go_.$O $(OFILES) - @mkdir -p _obj/$(dir) - rm -f _obj/$(TARG).a - gopack grc $@ _go_.$O $(OFILES) - -_test/$(TARG).a: _gotest_.$O $(OFILES) - @mkdir -p _test/$(dir) - rm -f _test/$(TARG).a - gopack grc $@ _gotest_.$O $(OFILES) - -importpath: - @echo $(TARG) - -dir: - @echo $(dir) - -# To use cgo in a Go package, add a line -# -# CGOFILES=x.go y.go -# -# to the main Makefile. This signals that cgo should process x.go -# and y.go when building the package. -# There are three optional variables to set, CGO_CFLAGS, CGO_LDFLAGS, -# and CGO_DEPS, which specify compiler flags, linker flags, and linker -# dependencies to use when compiling (using gcc) the C support for -# x.go and y.go. - -# Cgo translates each x.go file listed in $(CGOFILES) into a basic -# translation of x.go, called _obj/x.cgo1.go. Additionally, three other -# files are created: -# -# _obj/_cgo_gotypes.go - declarations needed for all .go files in the package; imports "unsafe" -# _obj/_cgo_defun.c - C trampoline code to be compiled with 6c and linked into the package -# _obj/x.cgo2.c - C implementations compiled with gcc to create a dynamic library -# - -ifdef CGOFILES -_obj/_cgo_run: $(CGOFILES) - @mkdir -p _obj - CGOPKGPATH=$(dir) cgo -- $(CGO_CFLAGS) $(CGOFILES) - touch _obj/_cgo_run - -# _CGO_CFLAGS and _CGO_LDFLAGS are defined via the evaluation of _cgo_flags. -# The include happens before the commands in the recipe run, -# so it cannot be done in the same recipe that runs cgo. -_obj/_load_cgo_flags: _obj/_cgo_run - $(eval include _obj/_cgo_flags) - -# Include any previous flags in case cgo files are up to date. --include _obj/_cgo_flags - -# Ugly but necessary - cgo writes these files too. -_obj/_cgo_gotypes.go _obj/_cgo_export.c _obj/_cgo_export.h _obj/_cgo_main.c _obj/_cgo_defun.c: _obj/_load_cgo_flags - @true - -_obj/%.cgo1.go _obj/%.cgo2.c: _obj/_cgo_defun.c - @true -endif - -# Compile rules for gcc source files. -%.o: %.c - $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -g -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $(_CGO_CFLAGS) $*.c - -%.o: _obj/%.c - $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -I . -g -fPIC -O2 -o $@ -c $(CGO_CFLAGS) $(_CGO_CFLAGS) $^ - -# To find out which symbols are needed from external libraries -# and which libraries are needed, we build a simple a.out that -# links all the objects we just created and then use cgo -dynimport -# to inspect it. That is, we make gcc tell us which dynamic symbols -# and libraries are involved, instead of duplicating gcc's logic ourselves. -# After main we have to define all the symbols that will be provided -# by Go code. That's crosscall2 and any exported symbols. - -_cgo1_.o: _cgo_main.o $(CGO_OFILES) - $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -g -fPIC -O2 -o $@ $^ $(CGO_LDFLAGS) $(_CGO_LDFLAGS) - -_obj/_cgo_import.c: _cgo1_.o - @mkdir -p _obj - cgo -dynimport _cgo1_.o >$@_ && mv -f $@_ $@ - -# The rules above added x.cgo1.go and _cgo_gotypes.go to $(GOFILES), -# added _cgo_defun.$O to $OFILES, and added the installed copy of -# package_x.so (built from x.cgo2.c) to $(INSTALLFILES). - -# Have to run gcc with the right size argument on hybrid 32/64 machines. -_CGO_CFLAGS_386=-m32 -_CGO_CFLAGS_amd64=-m64 -_CGO_LDFLAGS_freebsd=-shared -lpthread -lm -_CGO_LDFLAGS_linux=-shared -lpthread -lm -_CGO_LDFLAGS_darwin=-dynamiclib -Wl,-undefined,dynamic_lookup -_CGO_LDFLAGS_windows=-shared -lm -mthreads - -# Have to compile the runtime header. -RUNTIME_CFLAGS=-I$(pkgdir) - -# Compile _cgo_defun.c with 6c; needs access to the runtime headers. -_cgo_defun.$O: _obj/_cgo_defun.c - $(CC) $(CFLAGS) $(RUNTIME_CFLAGS) -I . -o "$@" _obj/_cgo_defun.c - -# To use swig in a Go package, add a line -# -# SWIGFILES=x.swig -# -# to the main Makefile. This signals that SWIG should process the -#.swig file when building the package. -# -# To wrap C code, use an extension of .swig. To wrap C++ code, use an -# extension of .swigcxx. -# -# SWIGFILES=myclib.swig mycxxlib.swigcxx - -ifdef SWIGFILES -_obj/%._swig_run _obj/%.go _obj/%_gc.c _obj/%_wrap.c: %.swig - @mkdir -p _obj - swig -go -module $* -soname $(SWIG_PREFIX)-$*.so -o _obj/$*_wrap.c -outdir _obj $< - -_obj/%._swig_run _obj/%.go _obj/%_gc.c _obj/%_wrap.cxx: %.swigcxx - @mkdir -p _obj - swig -go -c++ -module $* -soname $(SWIG_PREFIX)-$*.so -o _obj/$*_wrap.cxx -outdir _obj $< - -_obj/%_gc.$O: _obj/%_gc.c - $(CC) $(CFLAGS) -I . -I$(pkgdir) -o "$@" _obj/$*_gc.c - -_obj/%_wrap.o: _obj/%_wrap.c - $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -I . -g -fPIC -O2 -o $@ -c $^ $(SWIG_CFLAGS) - -HOST_CXX=g++ - -_obj/%_wrapcxx.o: _obj/%_wrap.cxx - $(HOST_CXX) $(_CGO_CFLAGS_$(GOARCH)) -I . -g -fPIC -O2 -o $@ -c $^ $(SWIG_CXXFLAGS) - -_obj/$(SWIG_PREFIX)-%.so: _obj/%_wrap.o - $(HOST_CC) $(_CGO_CFLAGS_$(GOARCH)) -o $@ $^ $(SWIG_LDFLAGS) $(_CGO_LDFLAGS_$(GOOS)) $(_SWIG_LDFLAGS_$(GOOS)) - -_obj/$(SWIG_PREFIX)-%.so: _obj/%_wrapcxx.o - $(HOST_CXX) $(_CGO_CFLAGS_$(GOARCH)) -o $@ $^ $(SWIG_LDFLAGS) $(_CGO_LDFLAGS_$(GOOS)) $(_SWIG_LDFLAGS_$(GOOS)) - -$(TARGDIR)/swig/$(SWIG_PREFIX)-%.so: _obj/$(SWIG_PREFIX)-%.so - @mkdir -p $(TARGDIR)/swig - cp $< "$@" - -all: $(SWIG_SOS) - -SWIG_RPATH=-r $(TARGDIR)/swig - -endif - -# Generic build rules. -# These come last so that the rules above can override them -# for more specific file names. -%.$O: %.c $(HFILES) - $(CC) $(CFLAGS) -o "$@" $*.c - -%.$O: _obj/%.c $(HFILES) - $(CC) $(CFLAGS) -I . -o "$@" _obj/$*.c - -%.$O: %.s - $(AS) $*.s diff --git a/src/all-qemu.bash b/src/all-qemu.bash deleted file mode 100755 index 6a659d6d4..000000000 --- a/src/all-qemu.bash +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Run all.bash but exclude tests that depend on functionality -# missing in QEMU's system call emulation. - -export GOARCH=arm -export NOTEST="" -NOTEST="$NOTEST big" # just slow -NOTEST="$NOTEST go/build" # wants to run cgo -NOTEST="$NOTEST http http/cgi net rpc syslog websocket" # no localhost network -NOTEST="$NOTEST os" # 64-bit seek fails - -./all.bash diff --git a/src/all.bash b/src/all.bash index 00110d2da..932b65dc0 100755 --- a/src/all.bash +++ b/src/all.bash @@ -8,7 +8,6 @@ if [ ! -f make.bash ]; then echo 'all.bash must be run from $GOROOT/src' 1>&2 exit 1 fi -. ./make.bash -bash run.bash --no-env --no-rebuild -installed # function defined by make.bash - +. ./make.bash --no-banner +bash run.bash --no-rebuild --banner +$GOTOOLDIR/dist banner # print build info diff --git a/src/all.bat b/src/all.bat new file mode 100644 index 000000000..e3b61c012 --- /dev/null +++ b/src/all.bat @@ -0,0 +1,21 @@ +:: 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. +@echo off + +setlocal + +if exist make.bat goto ok +echo all.bat must be run from go\src +:: cannot exit: would kill parent command interpreter +goto end +:ok + +call make.bat --no-banner --no-local +if %GOBUILDFAIL%==1 goto end +call run.bat --no-rebuild --no-local +if %GOBUILDFAIL%==1 goto end +go tool dist banner + +:end +if x%GOBUILDEXIT%==x1 exit %GOBUILDFAIL% diff --git a/src/clean.bash b/src/clean.bash index 1955b583b..f36260240 100755 --- a/src/clean.bash +++ b/src/clean.bash @@ -5,25 +5,12 @@ set -e -if [ ! -f env.bash ]; then - echo 'clean.bash must be run from $GOROOT/src' 1>&2 +eval $(go tool dist env) + +if [ ! -x $GOTOOLDIR/dist ]; then + echo 'cannot find $GOTOOLDIR/dist; nothing to clean' >&2 exit 1 fi -. ./env.bash -if [ ! -f Make.inc ] ; then - GOROOT_FINAL=${GOROOT_FINAL:-$GOROOT} - sed 's!@@GOROOT@@!'"$GOROOT_FINAL"'!' Make.inc.in >Make.inc -fi -if [ "$1" != "--nopkg" ]; then - rm -rf "$GOROOT"/pkg/${GOOS}_$GOARCH -fi -rm -f "$GOROOT"/lib/*.a -for i in lib9 libbio libmach cmd pkg \ - ../misc/cgo/gmp ../misc/cgo/stdio \ - ../misc/cgo/life ../misc/cgo/test \ - ../test/bench ../test/garbage -do - # Do not use gomake here. It may not be available. - $MAKE -C "$GOROOT/src/$i" clean -done +"$GOBIN/go" clean -i std +$GOTOOLDIR/dist clean diff --git a/src/clean.bat b/src/clean.bat new file mode 100644 index 000000000..dcf54ea3a --- /dev/null +++ b/src/clean.bat @@ -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. +@echo off + +setlocal + +set GOBUILDFAIL=0 + +go tool dist env -wp >env.bat +if errorlevel 1 goto fail +call env.bat +del env.bat +echo. + +if exist %GOTOOLDIR%\dist.exe goto distok +echo cannot find %GOTOOLDIR%\dist; nothing to clean +goto fail +:distok + +"%GOBIN%\go" clean -i std +%GOTOOLDIR%\dist clean + +goto end + +:fail +set GOBUILDFAIL=1 + +:end +if x%GOBUILDEXIT%==x1 exit %GOBUILDFAIL% diff --git a/src/cmd/5a/Makefile b/src/cmd/5a/Makefile index f4463c97b..27290ddd7 100644 --- a/src/cmd/5a/Makefile +++ b/src/cmd/5a/Makefile @@ -1,25 +1,10 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) +include ../../Make.dist -TARG=5a +install: y.tab.h -HFILES=\ - a.h\ - y.tab.h\ - ../5l/5.out.h\ - -OFILES=\ - y.tab.$O\ - lex.$O\ - ../5l/enam.$O\ - -YFILES=\ - a.y\ - -include ../../Make.ccmd - -lex.$O: ../cc/macbody ../cc/lexbody +y.tab.h: a.y + LANG=C LANGUAGE=en_US.UTF8 bison -d -v -y a.y diff --git a/src/cmd/5a/a.y b/src/cmd/5a/a.y index 9a0efd5e0..512fb5a95 100644 --- a/src/cmd/5a/a.y +++ b/src/cmd/5a/a.y @@ -217,7 +217,7 @@ inst: */ | LTYPEB name ',' imm { - outcode($1, Always, &$2, NREG, &$4); + outcode($1, Always, &$2, 0, &$4); } | LTYPEB name ',' con ',' imm { diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c index ad7ed05dd..4bef0219a 100644 --- a/src/cmd/5a/lex.c +++ b/src/cmd/5a/lex.c @@ -44,7 +44,11 @@ enum int systemtype(int sys) { +#ifdef _WIN32 + return sys&Windows; +#else return sys&Plan9; +#endif } void @@ -643,17 +647,13 @@ outhist(void) for(h = hist; h != H; h = h->link) { p = h->name; op = 0; - /* on windows skip drive specifier in pathname */ if(systemtype(Windows) && p && p[1] == ':'){ - p += 2; - c = *p; - } - if(p && p[0] != c && h->offset == 0 && pathname){ - /* on windows skip drive specifier in pathname */ + c = p[2]; + } else if(p && p[0] != c && h->offset == 0 && pathname){ if(systemtype(Windows) && pathname[1] == ':') { op = p; - p = pathname+2; - c = *p; + p = pathname; + c = p[2]; } else if(pathname[0] == c){ op = p; p = pathname; diff --git a/src/cmd/5a/y.tab.c b/src/cmd/5a/y.tab.c new file mode 100644 index 000000000..a2fba60b3 --- /dev/null +++ b/src/cmd/5a/y.tab.c @@ -0,0 +1,2949 @@ + +/* A Bison parser, made by GNU Bison 2.4.1. */ + +/* 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. + + 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + 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. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.4.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* 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 189 of yacc.c */ +#line 31 "a.y" + +#include +#include /* if we don't, bison will, and a.h re-#defines getc */ +#include +#include "a.h" + + +/* Line 189 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 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + LTYPE1 = 258, + LTYPE2 = 259, + LTYPE3 = 260, + LTYPE4 = 261, + LTYPE5 = 262, + LTYPE6 = 263, + LTYPE7 = 264, + LTYPE8 = 265, + LTYPE9 = 266, + LTYPEA = 267, + LTYPEB = 268, + LTYPEC = 269, + LTYPED = 270, + LTYPEE = 271, + LTYPEF = 272, + LTYPEG = 273, + LTYPEH = 274, + LTYPEI = 275, + LTYPEJ = 276, + LTYPEK = 277, + LTYPEL = 278, + 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 + }; +#endif +/* Tokens. */ +#define LTYPE1 258 +#define LTYPE2 259 +#define LTYPE3 260 +#define LTYPE4 261 +#define LTYPE5 262 +#define LTYPE6 263 +#define LTYPE7 264 +#define LTYPE8 265 +#define LTYPE9 266 +#define LTYPEA 267 +#define LTYPEB 268 +#define LTYPEC 269 +#define LTYPED 270 +#define LTYPEE 271 +#define LTYPEF 272 +#define LTYPEG 273 +#define LTYPEH 274 +#define LTYPEI 275 +#define LTYPEJ 276 +#define LTYPEK 277 +#define LTYPEL 278 +#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 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 214 of yacc.c */ +#line 38 "a.y" + + Sym *sym; + int32 lval; + double dval; + char sval[8]; + Gen gen; + + + +/* Line 214 of yacc.c */ +#line 223 "y.tab.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 264 of yacc.c */ +#line 235 "y.tab.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# 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 _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((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 + +/* 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 + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 643 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 69 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 35 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 129 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 327 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 303 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +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, 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, 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, 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, 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, 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, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 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, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint16 yyprhs[] = +{ + 0, 0, 3, 4, 5, 9, 10, 15, 16, 21, + 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 +}; + +/* 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 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 67, 67, 69, 68, 76, 75, 83, 82, 88, + 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 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "'|'", "'^'", "'&'", "'<'", "'>'", "'+'", + "'-'", "'*'", "'/'", "'%'", "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", + "shift", "rcon", "sreg", "spreg", "creg", "frcon", "freg", "name", + "offset", "pointer", "con", "oexpr", "expr", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 124, 94, 38, 60, 62, 43, 45, + 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, 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 +}; +# 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 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 0, 0, 3, 0, 4, 0, 4, 4, + 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 +}; + +/* 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_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 +}; + +/* 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 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -147 +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 +}; + +/* 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 +}; + +/* 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 +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 +}; + +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 +}; + +/* 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, + 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 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* 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. */ + +#define YYFAIL goto yyerrlab + +#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 \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#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. */ + +#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 +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# 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]; + + 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; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + 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. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + +/* 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; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*-------------------------. +| yyparse or yypush_parse. | +`-------------------------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + + int yystate; + /* 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; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* 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. */ + + /* 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; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + 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 + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 3: + +/* Line 1455 of yacc.c */ +#line 69 "a.y" + { + stmtline = lineno; + } + break; + + case 5: + +/* Line 1455 of yacc.c */ +#line 76 "a.y" + { + if((yyvsp[(1) - (2)].sym)->value != pc) + yyerror("redeclaration of %s", (yyvsp[(1) - (2)].sym)->name); + (yyvsp[(1) - (2)].sym)->value = pc; + } + break; + + case 7: + +/* Line 1455 of yacc.c */ +#line 83 "a.y" + { + (yyvsp[(1) - (2)].sym)->type = LLAB; + (yyvsp[(1) - (2)].sym)->value = pc; + } + break; + + case 9: + +/* Line 1455 of yacc.c */ +#line 89 "a.y" + { + (yyvsp[(1) - (4)].sym)->type = LVAR; + (yyvsp[(1) - (4)].sym)->value = (yyvsp[(3) - (4)].lval); + } + break; + + case 10: + +/* Line 1455 of yacc.c */ +#line 94 "a.y" + { + if((yyvsp[(1) - (4)].sym)->value != (yyvsp[(3) - (4)].lval)) + yyerror("redeclaration of %s", (yyvsp[(1) - (4)].sym)->name); + (yyvsp[(1) - (4)].sym)->value = (yyvsp[(3) - (4)].lval); + } + break; + + case 14: + +/* Line 1455 of yacc.c */ +#line 108 "a.y" + { + outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].lval), &(yyvsp[(7) - (7)].gen)); + } + break; + + case 15: + +/* Line 1455 of yacc.c */ +#line 112 "a.y" + { + outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].lval), &nullgen); + } + break; + + case 16: + +/* Line 1455 of yacc.c */ +#line 116 "a.y" + { + outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen)); + } + break; + + case 17: + +/* Line 1455 of yacc.c */ +#line 123 "a.y" + { + outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen)); + } + break; + + case 18: + +/* Line 1455 of yacc.c */ +#line 130 "a.y" + { + outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen)); + } + break; + + case 19: + +/* Line 1455 of yacc.c */ +#line 137 "a.y" + { + outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &nullgen, NREG, &(yyvsp[(4) - (4)].gen)); + } + break; + + case 20: + +/* Line 1455 of yacc.c */ +#line 141 "a.y" + { + outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &nullgen, NREG, &(yyvsp[(4) - (4)].gen)); + } + break; + + case 21: + +/* Line 1455 of yacc.c */ +#line 148 "a.y" + { + outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen)); + } + break; + + case 22: + +/* Line 1455 of yacc.c */ +#line 155 "a.y" + { + outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen)); + } + break; + + case 23: + +/* Line 1455 of yacc.c */ +#line 162 "a.y" + { + outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &nullgen, NREG, &(yyvsp[(4) - (4)].gen)); + } + break; + + case 24: + +/* Line 1455 of yacc.c */ +#line 169 "a.y" + { + outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].lval), &nullgen); + } + break; + + case 25: + +/* Line 1455 of yacc.c */ +#line 176 "a.y" + { + Gen g; + + g = nullgen; + g.type = D_CONST; + g.offset = (yyvsp[(6) - (7)].lval); + outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), NREG, &g); + } + break; + + case 26: + +/* Line 1455 of yacc.c */ +#line 185 "a.y" + { + Gen g; + + g = nullgen; + g.type = D_CONST; + g.offset = (yyvsp[(4) - (7)].lval); + outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &g, NREG, &(yyvsp[(7) - (7)].gen)); + } + break; + + case 27: + +/* Line 1455 of yacc.c */ +#line 197 "a.y" + { + outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(5) - (7)].gen), (yyvsp[(3) - (7)].gen).reg, &(yyvsp[(7) - (7)].gen)); + } + break; + + case 28: + +/* Line 1455 of yacc.c */ +#line 201 "a.y" + { + outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(5) - (6)].gen), (yyvsp[(3) - (6)].gen).reg, &(yyvsp[(3) - (6)].gen)); + } + break; + + case 29: + +/* Line 1455 of yacc.c */ +#line 205 "a.y" + { + outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(4) - (6)].gen), (yyvsp[(6) - (6)].gen).reg, &(yyvsp[(6) - (6)].gen)); + } + break; + + case 30: + +/* Line 1455 of yacc.c */ +#line 212 "a.y" + { + outcode((yyvsp[(1) - (3)].lval), (yyvsp[(2) - (3)].lval), &nullgen, NREG, &nullgen); + } + break; + + case 31: + +/* Line 1455 of yacc.c */ +#line 219 "a.y" + { + outcode((yyvsp[(1) - (4)].lval), Always, &(yyvsp[(2) - (4)].gen), NREG, &(yyvsp[(4) - (4)].gen)); + } + break; + + case 32: + +/* Line 1455 of yacc.c */ +#line 223 "a.y" + { + outcode((yyvsp[(1) - (6)].lval), Always, &(yyvsp[(2) - (6)].gen), (yyvsp[(4) - (6)].lval), &(yyvsp[(6) - (6)].gen)); + } + break; + + case 33: + +/* Line 1455 of yacc.c */ +#line 230 "a.y" + { + outcode((yyvsp[(1) - (6)].lval), Always, &(yyvsp[(2) - (6)].gen), (yyvsp[(4) - (6)].lval), &(yyvsp[(6) - (6)].gen)); + } + break; + + case 34: + +/* Line 1455 of yacc.c */ +#line 237 "a.y" + { + outcode((yyvsp[(1) - (4)].lval), (yyvsp[(2) - (4)].lval), &(yyvsp[(3) - (4)].gen), NREG, &nullgen); + } + break; + + case 35: + +/* Line 1455 of yacc.c */ +#line 244 "a.y" + { + outcode((yyvsp[(1) - (3)].lval), Always, &nullgen, NREG, &(yyvsp[(3) - (3)].gen)); + } + break; + + case 36: + +/* Line 1455 of yacc.c */ +#line 251 "a.y" + { + outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen)); + } + break; + + case 37: + +/* Line 1455 of yacc.c */ +#line 255 "a.y" + { + outcode((yyvsp[(1) - (5)].lval), (yyvsp[(2) - (5)].lval), &(yyvsp[(3) - (5)].gen), NREG, &(yyvsp[(5) - (5)].gen)); + } + break; + + case 38: + +/* Line 1455 of yacc.c */ +#line 259 "a.y" + { + outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].lval), &(yyvsp[(7) - (7)].gen)); + } + break; + + case 39: + +/* Line 1455 of yacc.c */ +#line 263 "a.y" + { + outcode((yyvsp[(1) - (6)].lval), (yyvsp[(2) - (6)].lval), &(yyvsp[(3) - (6)].gen), (yyvsp[(5) - (6)].gen).reg, &nullgen); + } + break; + + case 40: + +/* Line 1455 of yacc.c */ +#line 270 "a.y" + { + Gen g; + + g = nullgen; + g.type = D_CONST; + g.offset = + (0xe << 24) | /* opcode */ + ((yyvsp[(1) - (12)].lval) << 20) | /* MCR/MRC */ + ((yyvsp[(2) - (12)].lval) << 28) | /* scond */ + (((yyvsp[(3) - (12)].lval) & 15) << 8) | /* coprocessor number */ + (((yyvsp[(5) - (12)].lval) & 7) << 21) | /* coprocessor operation */ + (((yyvsp[(7) - (12)].lval) & 15) << 12) | /* arm register */ + (((yyvsp[(9) - (12)].lval) & 15) << 16) | /* Crn */ + (((yyvsp[(11) - (12)].lval) & 15) << 0) | /* Crm */ + (((yyvsp[(12) - (12)].lval) & 7) << 5) | /* coprocessor information */ + (1<<4); /* must be set */ + outcode(AWORD, Always, &nullgen, NREG, &g); + } + break; + + case 41: + +/* Line 1455 of yacc.c */ +#line 292 "a.y" + { + outcode((yyvsp[(1) - (7)].lval), (yyvsp[(2) - (7)].lval), &(yyvsp[(3) - (7)].gen), (yyvsp[(5) - (7)].gen).reg, &(yyvsp[(7) - (7)].gen)); + } + break; + + case 42: + +/* Line 1455 of yacc.c */ +#line 299 "a.y" + { + (yyvsp[(7) - (9)].gen).type = D_REGREG; + (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)); + } + break; + + case 43: + +/* Line 1455 of yacc.c */ +#line 308 "a.y" + { + outcode((yyvsp[(1) - (2)].lval), Always, &nullgen, NREG, &nullgen); + } + break; + + case 44: + +/* Line 1455 of yacc.c */ +#line 313 "a.y" + { + (yyval.lval) = Always; + } + break; + + case 45: + +/* Line 1455 of yacc.c */ +#line 317 "a.y" + { + (yyval.lval) = ((yyvsp[(1) - (2)].lval) & ~C_SCOND) | (yyvsp[(2) - (2)].lval); + } + break; + + case 46: + +/* Line 1455 of yacc.c */ +#line 321 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (2)].lval) | (yyvsp[(2) - (2)].lval); + } + break; + + case 49: + +/* Line 1455 of yacc.c */ +#line 330 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_BRANCH; + (yyval.gen).offset = (yyvsp[(1) - (4)].lval) + pc; + } + break; + + case 50: + +/* Line 1455 of yacc.c */ +#line 336 "a.y" + { + (yyval.gen) = nullgen; + if(pass == 2) + yyerror("undefined label: %s", (yyvsp[(1) - (2)].sym)->name); + (yyval.gen).type = D_BRANCH; + (yyval.gen).sym = (yyvsp[(1) - (2)].sym); + (yyval.gen).offset = (yyvsp[(2) - (2)].lval); + } + break; + + case 51: + +/* Line 1455 of yacc.c */ +#line 345 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_BRANCH; + (yyval.gen).sym = (yyvsp[(1) - (2)].sym); + (yyval.gen).offset = (yyvsp[(1) - (2)].sym)->value + (yyvsp[(2) - (2)].lval); + } + break; + + case 52: + +/* Line 1455 of yacc.c */ +#line 353 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_CONST; + (yyval.gen).offset = (yyvsp[(2) - (2)].lval); + } + break; + + case 53: + +/* Line 1455 of yacc.c */ +#line 359 "a.y" + { + (yyval.gen) = (yyvsp[(2) - (2)].gen); + (yyval.gen).type = D_CONST; + } + break; + + case 54: + +/* Line 1455 of yacc.c */ +#line 364 "a.y" + { + (yyval.gen) = (yyvsp[(4) - (4)].gen); + (yyval.gen).type = D_OCONST; + } + break; + + case 55: + +/* Line 1455 of yacc.c */ +#line 369 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_SCONST; + memcpy((yyval.gen).sval, (yyvsp[(2) - (2)].sval), sizeof((yyval.gen).sval)); + } + break; + + case 57: + +/* Line 1455 of yacc.c */ +#line 378 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_FCONST; + (yyval.gen).dval = (yyvsp[(2) - (2)].dval); + } + break; + + case 58: + +/* Line 1455 of yacc.c */ +#line 384 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_FCONST; + (yyval.gen).dval = -(yyvsp[(3) - (3)].dval); + } + break; + + case 59: + +/* Line 1455 of yacc.c */ +#line 392 "a.y" + { + (yyval.lval) = 1 << (yyvsp[(1) - (1)].lval); + } + break; + + case 60: + +/* Line 1455 of yacc.c */ +#line 396 "a.y" + { + int i; + (yyval.lval)=0; + for(i=(yyvsp[(1) - (3)].lval); i<=(yyvsp[(3) - (3)].lval); i++) + (yyval.lval) |= 1<= 16) + print("register value out of range\n"); + (yyval.lval) = (((yyvsp[(1) - (1)].lval)&15) << 8) | (1 << 4); + } + break; + + case 90: + +/* Line 1455 of yacc.c */ +#line 540 "a.y" + { + if((yyval.lval) < 0 || (yyval.lval) >= 32) + print("shift value out of range\n"); + (yyval.lval) = ((yyvsp[(1) - (1)].lval)&31) << 7; + } + break; + + case 92: + +/* Line 1455 of yacc.c */ +#line 549 "a.y" + { + (yyval.lval) = REGPC; + } + break; + + case 93: + +/* Line 1455 of yacc.c */ +#line 553 "a.y" + { + if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG) + print("register value out of range\n"); + (yyval.lval) = (yyvsp[(3) - (4)].lval); + } + break; + + case 95: + +/* Line 1455 of yacc.c */ +#line 562 "a.y" + { + (yyval.lval) = REGSP; + } + break; + + case 97: + +/* Line 1455 of yacc.c */ +#line 569 "a.y" + { + if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG) + print("register value out of range\n"); + (yyval.lval) = (yyvsp[(3) - (4)].lval); + } + break; + + case 100: + +/* Line 1455 of yacc.c */ +#line 581 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_FREG; + (yyval.gen).reg = (yyvsp[(1) - (1)].lval); + } + break; + + case 101: + +/* Line 1455 of yacc.c */ +#line 587 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_FREG; + (yyval.gen).reg = (yyvsp[(3) - (4)].lval); + } + break; + + case 102: + +/* Line 1455 of yacc.c */ +#line 595 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_OREG; + (yyval.gen).name = (yyvsp[(3) - (4)].lval); + (yyval.gen).sym = S; + (yyval.gen).offset = (yyvsp[(1) - (4)].lval); + } + break; + + case 103: + +/* Line 1455 of yacc.c */ +#line 603 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_OREG; + (yyval.gen).name = (yyvsp[(4) - (5)].lval); + (yyval.gen).sym = (yyvsp[(1) - (5)].sym); + (yyval.gen).offset = (yyvsp[(2) - (5)].lval); + } + break; + + case 104: + +/* Line 1455 of yacc.c */ +#line 611 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_OREG; + (yyval.gen).name = D_STATIC; + (yyval.gen).sym = (yyvsp[(1) - (7)].sym); + (yyval.gen).offset = (yyvsp[(4) - (7)].lval); + } + break; + + case 105: + +/* Line 1455 of yacc.c */ +#line 620 "a.y" + { + (yyval.lval) = 0; + } + break; + + case 106: + +/* Line 1455 of yacc.c */ +#line 624 "a.y" + { + (yyval.lval) = (yyvsp[(2) - (2)].lval); + } + break; + + case 107: + +/* Line 1455 of yacc.c */ +#line 628 "a.y" + { + (yyval.lval) = -(yyvsp[(2) - (2)].lval); + } + break; + + case 112: + +/* Line 1455 of yacc.c */ +#line 640 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; + } + break; + + case 113: + +/* Line 1455 of yacc.c */ +#line 644 "a.y" + { + (yyval.lval) = -(yyvsp[(2) - (2)].lval); + } + break; + + case 114: + +/* Line 1455 of yacc.c */ +#line 648 "a.y" + { + (yyval.lval) = (yyvsp[(2) - (2)].lval); + } + break; + + case 115: + +/* Line 1455 of yacc.c */ +#line 652 "a.y" + { + (yyval.lval) = ~(yyvsp[(2) - (2)].lval); + } + break; + + case 116: + +/* Line 1455 of yacc.c */ +#line 656 "a.y" + { + (yyval.lval) = (yyvsp[(2) - (3)].lval); + } + break; + + case 117: + +/* Line 1455 of yacc.c */ +#line 661 "a.y" + { + (yyval.lval) = 0; + } + break; + + case 118: + +/* Line 1455 of yacc.c */ +#line 665 "a.y" + { + (yyval.lval) = (yyvsp[(2) - (2)].lval); + } + break; + + case 120: + +/* Line 1455 of yacc.c */ +#line 672 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); + } + break; + + case 121: + +/* Line 1455 of yacc.c */ +#line 676 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); + } + break; + + case 122: + +/* Line 1455 of yacc.c */ +#line 680 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); + } + break; + + case 123: + +/* Line 1455 of yacc.c */ +#line 684 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); + } + break; + + case 124: + +/* Line 1455 of yacc.c */ +#line 688 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); + } + break; + + case 125: + +/* Line 1455 of yacc.c */ +#line 692 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); + } + break; + + case 126: + +/* Line 1455 of yacc.c */ +#line 696 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); + } + break; + + case 127: + +/* Line 1455 of yacc.c */ +#line 700 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); + } + break; + + case 128: + +/* Line 1455 of yacc.c */ +#line 704 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); + } + break; + + case 129: + +/* Line 1455 of yacc.c */ +#line 708 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); + } + break; + + + +/* Line 1455 of yacc.c */ +#line 2740 "y.tab.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++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. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + 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; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined(yyoverflow) || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (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); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + diff --git a/src/cmd/5a/y.tab.h b/src/cmd/5a/y.tab.h new file mode 100644 index 000000000..28039992c --- /dev/null +++ b/src/cmd/5a/y.tab.h @@ -0,0 +1,166 @@ + +/* A Bison parser, made by GNU Bison 2.4.1. */ + +/* 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. + + 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + 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 + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + LTYPE1 = 258, + LTYPE2 = 259, + LTYPE3 = 260, + LTYPE4 = 261, + LTYPE5 = 262, + LTYPE6 = 263, + LTYPE7 = 264, + LTYPE8 = 265, + LTYPE9 = 266, + LTYPEA = 267, + LTYPEB = 268, + LTYPEC = 269, + LTYPED = 270, + LTYPEE = 271, + LTYPEF = 272, + LTYPEG = 273, + LTYPEH = 274, + LTYPEI = 275, + LTYPEJ = 276, + LTYPEK = 277, + LTYPEL = 278, + 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 + }; +#endif +/* Tokens. */ +#define LTYPE1 258 +#define LTYPE2 259 +#define LTYPE3 260 +#define LTYPE4 261 +#define LTYPE5 262 +#define LTYPE6 263 +#define LTYPE7 264 +#define LTYPE8 265 +#define LTYPE9 266 +#define LTYPEA 267 +#define LTYPEB 268 +#define LTYPEC 269 +#define LTYPED 270 +#define LTYPEE 271 +#define LTYPEF 272 +#define LTYPEG 273 +#define LTYPEH 274 +#define LTYPEI 275 +#define LTYPEJ 276 +#define LTYPEK 277 +#define LTYPEL 278 +#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 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 1676 of yacc.c */ +#line 38 "a.y" + + Sym *sym; + int32 lval; + double dval; + char sval[8]; + Gen gen; + + + +/* Line 1676 of yacc.c */ +#line 158 "y.tab.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +extern YYSTYPE yylval; + + diff --git a/src/cmd/5c/Makefile b/src/cmd/5c/Makefile index 70b614e8a..3f528d751 100644 --- a/src/cmd/5c/Makefile +++ b/src/cmd/5c/Makefile @@ -1,34 +1,5 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) - -TARG=5c - -HFILES=\ - gc.h\ - ../5l/5.out.h\ - ../cc/cc.h\ - -OFILES=\ - cgen.$O\ - list.$O\ - sgen.$O\ - swt.$O\ - txt.$O\ - mul.$O\ - reg.$O\ - peep.$O\ - pgen.$O\ - pswt.$O\ - ../5l/enam.$O\ - -LIB=\ - ../cc/cc.a\ - -include ../../Make.ccmd - -%.$O: ../cc/%.c - $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../cc/$*.c +include ../../Make.dist diff --git a/src/cmd/5c/gc.h b/src/cmd/5c/gc.h index ff6d51916..20652682b 100644 --- a/src/cmd/5c/gc.h +++ b/src/cmd/5c/gc.h @@ -181,7 +181,7 @@ EXTERN int32 maxargsafe; EXTERN int mnstring; EXTERN Multab multab[20]; EXTERN int retok; -EXTERN int hintabsize; +extern int hintabsize; EXTERN Node* nodrat; EXTERN Node* nodret; EXTERN Node* nodsafe; @@ -304,7 +304,8 @@ void gpseudo(int, Sym*, Node*); int swcmp(const void*, const void*); void doswit(Node*); void swit1(C1*, int, int32, Node*); -void cas(void); +void swit2(C1*, int, int32, Node*); +void newcase(void); void bitload(Node*, Node*, Node*, Node*, Node*); void bitstore(Node*, Node*, Node*, Node*, Node*); int mulcon(Node*, Node*); diff --git a/src/cmd/5c/swt.c b/src/cmd/5c/swt.c index 7cbaadba9..7268f9af2 100644 --- a/src/cmd/5c/swt.c +++ b/src/cmd/5c/swt.c @@ -28,11 +28,30 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - #include "gc.h" void swit1(C1 *q, int nc, int32 def, Node *n) +{ + Node nreg; + + if(typev[n->type->etype]) { + regsalloc(&nreg, n); + nreg.type = types[TVLONG]; + cgen(n, &nreg); + swit2(q, nc, def, &nreg); + return; + } + + regalloc(&nreg, n, Z); + nreg.type = types[TLONG]; + cgen(n, &nreg); + swit2(q, nc, def, &nreg); + regfree(&nreg); +} + +void +swit2(C1 *q, int nc, int32 def, Node *n) { C1 *r; int i; @@ -65,12 +84,12 @@ swit1(C1 *q, int nc, int32 def, Node *n) sp = p; gopcode(OEQ, nodconst(r->val), n, Z); /* just gen the B.EQ */ patch(p, r->label); - swit1(q, i, def, n); + swit2(q, i, def, n); if(debug['W']) print("case < %.8ux\n", r->val); patch(sp, pc); - swit1(r+1, nc-i-1, def, n); + swit2(r+1, nc-i-1, def, n); return; direct: @@ -460,17 +479,13 @@ outhist(Biobuf *b) for(h = hist; h != H; h = h->link) { p = h->name; op = 0; - /* on windows skip drive specifier in pathname */ if(systemtype(Windows) && p && p[1] == ':'){ - p += 2; - c = *p; - } - if(p && p[0] != c && h->offset == 0 && pathname){ - /* on windows skip drive specifier in pathname */ + c = p[2]; + } else if(p && p[0] != c && h->offset == 0 && pathname){ if(systemtype(Windows) && pathname[1] == ':') { op = p; - p = pathname+2; - c = *p; + p = pathname; + c = p[2]; } else if(pathname[0] == c){ op = p; p = pathname; diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c index a32387bc1..1a189e3af 100644 --- a/src/cmd/5c/txt.c +++ b/src/cmd/5c/txt.c @@ -139,7 +139,9 @@ gclean(void) continue; if(s->type == types[TENUM]) continue; + textflag = s->dataflag; gpseudo(AGLOBL, s, nodconst(s->type->width)); + textflag = 0; } nextpc(); p->as = AEND; @@ -357,7 +359,7 @@ regfree(Node *n) if(n->op != OREGISTER && n->op != OINDREG) goto err; i = n->reg; - if(i < 0 || i >= sizeof(reg)) + if(i < 0 || i >= nelem(reg)) goto err; if(reg[i] <= 0) goto err; @@ -1183,7 +1185,8 @@ gpseudo(int a, Sym *s, Node *n) if(a == ATEXT) { 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); diff --git a/src/cmd/5g/Makefile b/src/cmd/5g/Makefile index b47014a4e..3f528d751 100644 --- a/src/cmd/5g/Makefile +++ b/src/cmd/5g/Makefile @@ -1,36 +1,5 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) - -TARG=5g - -HFILES=\ - ../gc/go.h\ - ../5l/5.out.h\ - gg.h\ - opt.h\ - -OFILES=\ - ../5l/enam.$O\ - cgen.$O\ - cgen64.$O\ - cplx.$O\ - galign.$O\ - ggen.$O\ - gobj.$O\ - gsubr.$O\ - list.$O\ - peep.$O\ - pgen.$O\ - reg.$O\ - -LIB=\ - ../gc/gc.a\ - -include ../../Make.ccmd - -%.$O: ../gc/%.c - $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c +include ../../Make.dist diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c index 6e2fbe20f..cccef94c9 100644 --- a/src/cmd/5g/cgen.c +++ b/src/cmd/5g/cgen.c @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include +#include #include "gg.h" /* @@ -62,6 +64,9 @@ cgen(Node *n, Node *res) if(isslice(n->left->type)) n->addable = n->left->addable; break; + case OITAB: + n->addable = n->left->addable; + break; } // if both are addressable, move @@ -211,11 +216,11 @@ cgen(Node *n, Node *res) goto ret; case OMINUS: + regalloc(&n1, nl->type, N); + cgen(nl, &n1); nodconst(&n3, nl->type, 0); regalloc(&n2, nl->type, res); - regalloc(&n1, nl->type, N); gmove(&n3, &n2); - cgen(nl, &n1); gins(optoas(OSUB, nl->type), &n1, &n2); gmove(&n2, res); regfree(&n1); @@ -278,6 +283,20 @@ cgen(Node *n, Node *res) regfree(&n1); break; + case OITAB: + // itable 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); + regfree(&n1); + regfree(&n2); + break; + case OLEN: if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { // map has len in the first 32-bit word. @@ -400,9 +419,9 @@ abop: // asymmetric binary regalloc(&n2, nr->type, N); cgen(nr, &n2); } else { - regalloc(&n2, nr->type, N); + regalloc(&n2, nr->type, res); cgen(nr, &n2); - regalloc(&n1, nl->type, res); + regalloc(&n1, nl->type, N); cgen(nl, &n1); } gins(a, &n2, &n1); @@ -848,8 +867,10 @@ bgen(Node *n, int true, Prog *to) int et, a; Node *nl, *nr, *r; Node n1, n2, n3, n4, tmp; + NodeList *ll; Prog *p1, *p2; + USED(n4); // in unreachable code below if(debug['g']) { dump("\nbgen", n); } @@ -860,9 +881,6 @@ bgen(Node *n, int true, Prog *to) if(n->ninit != nil) genlist(n->ninit); - nl = n->left; - nr = n->right; - if(n->type == T) { convlit(&n, types[TBOOL]); if(n->type == T) @@ -875,7 +893,6 @@ bgen(Node *n, int true, Prog *to) patch(gins(AEND, N, N), to); goto ret; } - nl = N; nr = N; switch(n->op) { @@ -951,7 +968,10 @@ bgen(Node *n, int true, Prog *to) p1 = gbranch(AB, T); p2 = gbranch(AB, T); patch(p1, pc); + ll = n->ninit; + n->ninit = nil; bgen(n, 1, p2); + n->ninit = ll; patch(gbranch(AB, T), to); patch(p2, pc); goto ret; @@ -984,6 +1004,7 @@ bgen(Node *n, int true, Prog *to) regfree(&n1); break; +#ifdef NOTDEF a = optoas(a, types[tptr]); regalloc(&n1, types[tptr], N); regalloc(&n3, types[tptr], N); @@ -1001,6 +1022,7 @@ bgen(Node *n, int true, Prog *to) regfree(&n3); regfree(&n1); break; +#endif } if(isinter(nl->type)) { @@ -1019,6 +1041,7 @@ bgen(Node *n, int true, Prog *to) regfree(&n1); break; +#ifdef NOTDEF a = optoas(a, types[tptr]); regalloc(&n1, types[tptr], N); regalloc(&n3, types[tptr], N); @@ -1036,6 +1059,7 @@ bgen(Node *n, int true, Prog *to) regfree(&n3); regfree(&n4); break; +#endif } if(iscomplex[nl->type->etype]) { @@ -1059,7 +1083,7 @@ bgen(Node *n, int true, Prog *to) } if(nr->op == OLITERAL) { - if(nr->val.ctype == CTINT && mpgetfix(nr->val.u.xval) == 0) { + if(isconst(nr, CTINT) && mpgetfix(nr->val.u.xval) == 0) { gencmp0(nl, nl->type, a, to); break; } @@ -1186,7 +1210,7 @@ stkof(Node *n) * NB: character copy assumed little endian architecture */ void -sgen(Node *n, Node *res, int32 w) +sgen(Node *n, Node *res, int64 w) { Node dst, src, tmp, nend; int32 c, odst, osrc; @@ -1194,25 +1218,34 @@ sgen(Node *n, Node *res, int32 w) Prog *p, *ploop; if(debug['g']) { - print("\nsgen w=%d\n", w); + print("\nsgen w=%lld\n", w); dump("r", n); dump("res", res); } - if(w == 0) - return; - if(w < 0) - fatal("sgen copy %d", w); + if(n->ullman >= UINF && res->ullman >= UINF) fatal("sgen UINF"); + + if(w < 0 || (int32)w != w) + fatal("sgen copy %lld", w); + if(n->type == T) fatal("sgen: missing type"); + if(w == 0) { + // evaluate side effects only. + regalloc(&dst, types[tptr], N); + agen(res, &dst); + agen(n, &dst); + regfree(&dst); + return; + } + // determine alignment. // want to avoid unaligned access, so have to use // smaller operations for less aligned types. // for example moving [4]byte must use 4 MOVB not 1 MOVW. align = n->type->align; - op = 0; switch(align) { default: fatal("sgen: invalid alignment %d for %T", align, n->type); @@ -1227,7 +1260,7 @@ sgen(Node *n, Node *res, int32 w) break; } if(w%align) - fatal("sgen: unaligned size %d (align=%d) for %T", w, align, n->type); + fatal("sgen: unaligned size %lld (align=%d) for %T", w, align, n->type); c = w / align; // offset on the stack @@ -1313,7 +1346,6 @@ sgen(Node *n, Node *res, int32 w) p->from.type = D_OREG; p->from.offset = dir; p->scond |= C_PBIT; - ploop = p; p = gins(op, &tmp, &dst); p->to.type = D_OREG; diff --git a/src/cmd/5g/cgen64.c b/src/cmd/5g/cgen64.c index b56df765b..1235d1ace 100644 --- a/src/cmd/5g/cgen64.c +++ b/src/cmd/5g/cgen64.c @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include +#include #include "gg.h" /* @@ -240,7 +242,7 @@ cgen64(Node *n, Node *res) // shift is >= 1<<32 split64(r, &cl, &ch); gmove(&ch, &s); - p1 = gins(ATST, &s, N); + gins(ATST, &s, N); p6 = gbranch(ABNE, T); gmove(&cl, &s); splitclean(); @@ -248,7 +250,7 @@ cgen64(Node *n, Node *res) gmove(r, &s); p6 = P; } - p1 = gins(ATST, &s, N); + gins(ATST, &s, N); // shift == 0 p1 = gins(AMOVW, &bl, &al); @@ -411,7 +413,7 @@ olsh_break: gmove(r, &s); p6 = P; } - p1 = gins(ATST, &s, N); + gins(ATST, &s, N); // shift == 0 p1 = gins(AMOVW, &bl, &al); @@ -453,9 +455,9 @@ olsh_break: p1 = gins(AMOVW, &bh, &al); p1->scond = C_SCOND_EQ; if(bh.type->etype == TINT32) - p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); + gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); else - p1 = gins(AEOR, &ah, &ah); + gins(AEOR, &ah, &ah); p4 = gbranch(ABEQ, T); // check if shift is < 64 diff --git a/src/cmd/5g/doc.go b/src/cmd/5g/doc.go index e86013bdd..5a4a772fb 100644 --- a/src/cmd/5g/doc.go +++ b/src/cmd/5g/doc.go @@ -9,7 +9,5 @@ The $GOARCH for these tools is arm. It reads .go files and outputs .5 files. The flags are documented in ../gc/doc.go. -There is no instruction optimizer, so the -N flag is a no-op. - */ package documentation diff --git a/src/cmd/5g/galign.c b/src/cmd/5g/galign.c index 12766102f..070804217 100644 --- a/src/cmd/5g/galign.c +++ b/src/cmd/5g/galign.c @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include +#include #include "gg.h" int thechar = '5'; diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h index ce4558e21..7dbf3beec 100644 --- a/src/cmd/5g/gg.h +++ b/src/cmd/5g/gg.h @@ -2,16 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include -#include +#ifndef EXTERN +#define EXTERN extern +#endif #include "../gc/go.h" #include "../5l/5.out.h" -#ifndef EXTERN -#define EXTERN extern -#endif - typedef struct Addr Addr; struct Addr @@ -46,27 +43,26 @@ struct Prog uchar scond; }; +#define TEXTFLAG reg + #define REGALLOC_R0 0 #define REGALLOC_RMAX REGEXT #define REGALLOC_F0 (REGALLOC_RMAX+1) #define REGALLOC_FMAX (REGALLOC_F0 + FREGEXT) -EXTERN Biobuf* bout; EXTERN int32 dynloc; EXTERN uchar reg[REGALLOC_FMAX+1]; EXTERN int32 pcloc; // instruction counter EXTERN Strlit emptystring; extern char* anames[]; -EXTERN Hist* hist; EXTERN Prog zprog; -EXTERN Node* curfn; EXTERN Node* newproc; EXTERN Node* deferproc; EXTERN Node* deferreturn; EXTERN Node* panicindex; EXTERN Node* panicslice; EXTERN Node* throwreturn; -EXTERN long unmappedzero; +extern long unmappedzero; EXTERN int maxstksize; /* @@ -98,7 +94,7 @@ 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*, int32); +void sgen(Node*, Node*, int64); void gmove(Node*, Node*); Prog* gins(int, Node*, Node*); int samaddr(Node*, Node*); @@ -169,3 +165,6 @@ int Yconv(Fmt*); void listinit(void); void zaddr(Biobuf*, Addr*, 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 3f5f47e7b..de100620b 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -4,6 +4,8 @@ #undef EXTERN #define EXTERN +#include +#include #include "gg.h" #include "opt.h" @@ -12,7 +14,6 @@ defframe(Prog *ptxt) { // fill in argument size ptxt->to.type = D_CONST2; - ptxt->reg = 0; // flags ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr); // fill in final stack size @@ -28,10 +29,10 @@ markautoused(Prog* p) { for (; p; p = p->link) { if (p->from.name == D_AUTO && p->from.node) - p->from.node->used++; + p->from.node->used = 1; if (p->to.name == D_AUTO && p->to.node) - p->to.node->used++; + p->to.node->used = 1; } } @@ -544,7 +545,7 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) } // test for shift being 0 - p1 = gins(ATST, &n1, N); + gins(ATST, &n1, N); p3 = gbranch(ABEQ, T); // test and fix up large shifts diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c index 27c8be67d..b562ba888 100644 --- a/src/cmd/5g/gobj.c +++ b/src/cmd/5g/gobj.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include "gg.h" void @@ -266,54 +268,6 @@ dumpfuncs(void) } } -/* deferred DATA output */ -static Prog *strdat; -static Prog *estrdat; -static int gflag; -static Prog *savepc; - -void -data(void) -{ - gflag = debug['g']; - debug['g'] = 0; - - if(estrdat == nil) { - strdat = mal(sizeof(*pc)); - clearp(strdat); - estrdat = strdat; - } - if(savepc) - fatal("data phase error"); - savepc = pc; - pc = estrdat; -} - -void -text(void) -{ - if(!savepc) - fatal("text phase error"); - debug['g'] = gflag; - estrdat = pc; - pc = savepc; - savepc = nil; -} - -void -dumpdata(void) -{ - Prog *p; - - if(estrdat == nil) - return; - *pc = *strdat; - if(gflag) - for(p=pc; p!=estrdat; p=p->link) - print("%P\n", p); - pc = estrdat; -} - int dsname(Sym *sym, int off, char *t, int n) { @@ -353,6 +307,7 @@ datastring(char *s, int len, Addr *a) a->offset = widthptr+4; // skip header a->reg = NREG; a->sym = sym; + a->node = sym->def; } /* @@ -371,6 +326,7 @@ datagostring(Strlit *sval, Addr *a) a->offset = 0; // header a->reg = NREG; a->sym = sym; + a->node = sym->def; } void @@ -379,6 +335,17 @@ gdata(Node *nam, Node *nr, int wid) Prog *p; vlong v; + if(nr->op == OLITERAL) { + switch(nr->val.ctype) { + case CTCPLX: + gdatacomplex(nam, nr->val.u.cval); + return; + case CTSTR: + gdatastring(nam, nr->val.u.sval); + return; + } + } + if(wid == 8 && is64(nr->type)) { v = mpgetfix(nr->val.u.xval); p = gins(ADATA, nam, nodintconst(v)); diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index ddaf52a88..9acf93670 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include "gg.h" // TODO(kaib): Can make this bigger if we move @@ -50,6 +52,10 @@ clearp(Prog *p) pcloc++; } +static int ddumped; +static Prog *dfirst; +static Prog *dpc; + /* * generate and return proc with p->as = as, * linked into program. pc is next instruction. @@ -59,10 +65,23 @@ prog(int as) { Prog *p; - p = pc; - pc = mal(sizeof(*pc)); - - clearp(pc); + if(as == ADATA || as == AGLOBL) { + if(ddumped) + fatal("already dumped data"); + if(dpc == nil) { + dpc = mal(sizeof(*dpc)); + dfirst = dpc; + } + p = dpc; + dpc = mal(sizeof(*dpc)); + p->link = dpc; + p->reg = 0; // used for flags + } else { + p = pc; + pc = mal(sizeof(*pc)); + clearp(pc); + p->link = pc; + } if(lineno == 0) { if(debug['K']) @@ -71,10 +90,21 @@ prog(int as) p->as = as; p->lineno = lineno; - p->link = pc; return p; } +void +dumpdata(void) +{ + ddumped = 1; + if(dfirst == nil) + return; + newplist(); + *pc = *dfirst; + pc = dpc; + clearp(pc); +} + /* * generate a branch. * t is ignored. @@ -84,6 +114,8 @@ gbranch(int as, Type *t) { Prog *p; + USED(t); + p = prog(as); p->to.type = D_BRANCH; p->to.branch = P; @@ -222,6 +254,10 @@ ggloblnod(Node *nam, int32 width) p->to.sym = S; p->to.type = D_CONST; p->to.offset = width; + if(nam->readonly) + p->reg = RODATA; + if(nam->type != T && !haspointers(nam->type)) + p->reg |= NOPTR; } void @@ -238,6 +274,7 @@ ggloblsym(Sym *s, int32 width, int dupok) p->to.offset = width; if(dupok) p->reg = DUPOK; + p->reg |= RODATA; } int @@ -315,6 +352,8 @@ anyregalloc(void) return 0; } +uintptr regpc[REGALLOC_FMAX+1]; + /* * allocate register of type t, leave in n. * if o != N, o is desired fixed register. @@ -325,7 +364,7 @@ regalloc(Node *n, Type *t, Node *o) { int i, et, fixfree, floatfree; - if(debug['r']) { + if(0 && debug['r']) { fixfree = 0; for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) if(reg[i] == 0) @@ -358,9 +397,12 @@ regalloc(Node *n, Type *t, Node *o) goto out; } for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) - if(reg[i] == 0) + if(reg[i] == 0) { + regpc[i] = (uintptr)getcallerpc(&n); goto out; - + } + for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) + print("%d %p\n", i, regpc[i]); yyerror("out of fixed registers"); goto err; @@ -398,7 +440,7 @@ regfree(Node *n) { int i, fixfree, floatfree; - if(debug['r']) { + if(0 && debug['r']) { fixfree = 0; for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) if(reg[i] == 0) @@ -415,11 +457,13 @@ regfree(Node *n) if(n->op != OREGISTER && n->op != OINDREG) fatal("regfree: not a register"); i = n->val.u.reg; - if(i < 0 || i >= sizeof(reg)) + if(i < 0 || i >= nelem(reg) || i >= nelem(regpc)) fatal("regfree: reg out of range"); if(reg[i] <= 0) fatal("regfree: reg not allocated"); reg[i]--; + if(reg[i] == 0) + regpc[i] = 0; } /* @@ -481,8 +525,15 @@ nodarg(Type *t, int fp) fatal("nodarg: offset not computed for %T", t); n->xoffset = t->width; n->addable = 1; + n->orig = t->nname; fp: + // Rewrite argument named _ to __, + // or else the assignment to _ will be + // discarded during code generation. + if(isblank(n)) + n->sym = lookup("__"); + switch(fp) { default: fatal("nodarg %T %d", t, fp); @@ -1066,7 +1117,8 @@ gins(int as, Node *f, Node *t) if(f != N) naddr(f, &af, 1); if(t != N) - naddr(t, &at, 1); p = prog(as); + naddr(t, &at, 1); + p = prog(as); if(f != N) p->from = af; if(t != N) @@ -1232,6 +1284,7 @@ naddr(Node *n, Addr *a, int canemitcode) a->sym = n->left->sym; a->type = D_OREG; a->name = D_PARAM; + a->node = n->left->orig; break; case ONAME: @@ -1242,9 +1295,11 @@ naddr(Node *n, Addr *a, int canemitcode) a->etype = simtype[n->type->etype]; a->width = n->type->width; } - a->pun = n->pun; a->offset = n->xoffset; a->sym = n->sym; + a->node = n->orig; + //if(a->node >= (Node*)&n) + // fatal("stack node"); if(a->sym == S) a->sym = lookup(".noname"); if(n->method) { @@ -1263,8 +1318,6 @@ naddr(Node *n, Addr *a, int canemitcode) break; case PAUTO: a->name = D_AUTO; - if (n->sym) - a->node = n->orig; break; case PPARAM: case PPARAMOUT: @@ -1287,6 +1340,7 @@ naddr(Node *n, Addr *a, int canemitcode) a->dval = mpgetflt(n->val.u.fval); break; case CTINT: + case CTRUNE: a->sym = S; a->type = D_CONST; a->offset = mpgetfix(n->val.u.xval); @@ -1307,6 +1361,16 @@ naddr(Node *n, Addr *a, int canemitcode) } break; + case OITAB: + // itable of interface value + naddr(n->left, a, canemitcode); + a->etype = TINT32; + if(a->type == D_CONST && a->offset == 0) + break; // len(nil) + if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) + checkoffset(a, canemitcode); + break; + case OLEN: // len of string or slice naddr(n->left, a, canemitcode); @@ -1744,7 +1808,7 @@ sudoaddable(int as, Node *n, Addr *a, int *w) switch(n->op) { case OLITERAL: - if(n->val.ctype != CTINT) + if(!isconst(n, CTINT)) break; v = mpgetfix(n->val.u.xval); if(v >= 32000 || v <= -32000) diff --git a/src/cmd/5g/list.c b/src/cmd/5g/list.c index 0c6dbbf71..9bc3a9a9a 100644 --- a/src/cmd/5g/list.c +++ b/src/cmd/5g/list.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include "gg.h" // TODO(kaib): make 5g/list.c congruent with 5l/list.c @@ -57,7 +59,7 @@ Pconv(Fmt *fp) switch(p->as) { default: snprint(str1, sizeof(str1), "%A%C", p->as, p->scond); - if(p->reg == NREG) + if(p->reg == NREG && p->as != AGLOBL) snprint(str, sizeof(str), "%.4d (%L) %-7s %D,%D", p->loc, p->lineno, str1, &p->from, &p->to); else diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c index 6cc93db12..e87f5d697 100644 --- a/src/cmd/5g/peep.c +++ b/src/cmd/5g/peep.c @@ -29,6 +29,8 @@ // THE SOFTWARE. +#include +#include #include "gg.h" #include "opt.h" @@ -45,6 +47,9 @@ peep(void) Reg *r, *r1, *r2; Prog *p, *p1; int t; + + p1 = nil; + USED(p1); // ... in unreachable code... /* * complete R structure */ @@ -115,12 +120,14 @@ loop1: } break; +#ifdef NOTDEF if(p->scond == C_SCOND_NONE) if(regtyp(&p->to)) if(isdconst(&p->from)) { constprop(&p->from, &p->to, r->s1); } break; +#endif } } if(t) @@ -128,6 +135,7 @@ loop1: return; +#ifdef NOTDEF for(r=firstr; r!=R; r=r->link) { p = r->prog; switch(p->as) { @@ -255,6 +263,7 @@ return; // } predicate(); +#endif } Reg* @@ -1159,7 +1168,6 @@ copyu(Prog *p, Adr *v, Adr *s) return 3; return 0; } - return 0; } /* diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c index 2d2a6d01a..93724d032 100644 --- a/src/cmd/5g/reg.c +++ b/src/cmd/5g/reg.c @@ -29,6 +29,8 @@ // THE SOFTWARE. +#include +#include #include "gg.h" #include "opt.h" @@ -40,6 +42,9 @@ int noreturn(Prog *p); static int first = 0; +static void fixjmp(Prog*); + + Reg* rega(void) { @@ -90,8 +95,8 @@ setoutvar(void) ovar.b[z] |= bit.b[z]; t = structnext(&save); } -//if(bany(b)) -//print("ovars = %Q\n", &ovar); +//if(bany(ovar)) +//print("ovar = %Q\n", ovar); } void @@ -112,19 +117,19 @@ setaddrs(Bits bit) { int i, n; Var *v; - Sym *s; + Node *node; while(bany(&bit)) { // convert each bit to a variable i = bnum(bit); - s = var[i].sym; + node = var[i].node; n = var[i].name; bit.b[i/32] &= ~(1L<<(i%32)); // disable all pieces of that variable for(i=0; isym == s && v->name == n) + if(v->node == node && v->name == n) v->addr = 2; } } @@ -169,6 +174,8 @@ regopt(Prog *firstp) if(first == 0) { fmtinstall('Q', Qconv); } + + fixjmp(firstp); first++; if(debug['K']) { @@ -202,7 +209,7 @@ regopt(Prog *firstp) nvar = NREGVAR; memset(var, 0, NREGVAR*sizeof var[0]); for(i=0; ito; - a->sym = v->sym; a->name = v->name; a->node = v->node; + a->sym = v->node->sym; a->offset = v->offset; a->etype = v->etype; a->type = D_OREG; @@ -838,11 +845,10 @@ mkvar(Reg *r, Adr *a) int i, t, n, et, z, w, flag; int32 o; Bits bit; - Sym *s; + Node *node; // mark registers used t = a->type; - n = D_NONE; flag = 0; switch(t) { @@ -909,10 +915,13 @@ mkvar(Reg *r, Adr *a) break; } - s = a->sym; - if(s == S) + node = a->node; + if(node == N || node->op != ONAME || node->orig == N) goto none; - if(s->name[0] == '.') + node = node->orig; + if(node->orig != node) + fatal("%D: bad node", a); + if(node->sym == S || node->sym->name[0] == '.') goto none; et = a->etype; o = a->offset; @@ -920,7 +929,7 @@ mkvar(Reg *r, Adr *a) for(i=0; isym == s && v->name == n) { + if(v->node == node && v->name == n) { if(v->offset == o) if(v->etype == et) if(v->width == w) @@ -944,7 +953,7 @@ mkvar(Reg *r, Adr *a) } if(nvar >= NVAR) { - if(debug['w'] > 1 && s) + if(debug['w'] > 1 && node) fatal("variable not optimized: %D", a); goto none; } @@ -953,17 +962,16 @@ mkvar(Reg *r, Adr *a) nvar++; //print("var %d %E %D %S\n", i, et, a, s); v = var+i; - v->sym = s; v->offset = o; v->name = n; // v->gotype = a->gotype; v->etype = et; v->width = w; v->addr = flag; // funny punning - v->node = a->node; + v->node = node; if(debug['R']) - print("bit=%2d et=%2d w=%d+%d %S %D flag=%d\n", i, et, o, w, s, a, v->addr); + print("bit=%2d et=%2d 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) @@ -1021,6 +1029,13 @@ prop(Reg *r, Bits ref, Bits cal) ref.b[z] = 0; } break; + + default: + // Work around for issue 1304: + // flush modified globals before each instruction. + for(z=0; zset.b[z]) | @@ -1156,10 +1171,12 @@ loopit(Reg *r, int32 nr) r1 = rpo2r[i]; me = r1->rpo; d = -1; - if(r1->p1 != R && r1->p1->rpo < me) + // rpo2r[r->rpo] == r protects against considering dead code, + // which has r->rpo == 0. + if(r1->p1 != R && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me) d = r1->p1->rpo; for(r1 = r1->p2; r1 != nil; r1 = r1->p2link) - if(r1->rpo < me) + if(rpo2r[r1->rpo] == r1 && r1->rpo < me) d = rpolca(idom, d, r1->rpo); idom[i] = d; } @@ -1570,7 +1587,7 @@ dumpone(Reg *r) if(bany(&r->refahead)) print(" ra:%Q ", r->refahead); if(bany(&r->calbehind)) - print("cb:%Q ", r->calbehind); + print(" cb:%Q ", r->calbehind); if(bany(&r->calahead)) print(" ca:%Q ", r->calahead); if(bany(&r->regdiff)) @@ -1605,3 +1622,123 @@ dumpit(char *str, Reg *r0) // } } } + +/* + * the code generator depends on being able to write out JMP (B) + * 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. + */ + +/* what instruction does a JMP to p eventually land on? */ +static Prog* +chasejmp(Prog *p, int *jmploop) +{ + int n; + + n = 0; + while(p != P && p->as == AB && p->to.type == D_BRANCH) { + if(++n > 10) { + *jmploop = 1; + break; + } + p = p->to.branch; + } + return p; +} + +/* + * reuse reg pointer for mark/sweep state. + * leave reg==nil at end because alive==nil. + */ +#define alive ((void*)0) +#define dead ((void*)1) + +/* mark all code reachable from firstp as alive */ +static void +mark(Prog *firstp) +{ + Prog *p; + + for(p=firstp; p; p=p->link) { + 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 == AB || p->as == ARET || (p->as == ABL && noreturn(p))) + break; + } +} + +static void +fixjmp(Prog *firstp) +{ + int jmploop; + Prog *p, *last; + + if(debug['R'] && debug['v']) + print("\nfixjmp\n"); + + // pass 1: resolve jump to B, mark all code as dead. + jmploop = 0; + 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(debug['R'] && debug['v']) + print("->%P\n", p); + } + p->regp = dead; + } + if(debug['R'] && debug['v']) + print("\n"); + + // pass 2: mark all reachable code alive + mark(firstp); + + // pass 3: delete dead code (mostly JMPs). + last = nil; + for(p=firstp; p; p=p->link) { + if(p->regp == dead) { + if(p->link == P && p->as == ARET && last && last->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 %P\n", p); + continue; + } + } + if(last) + last->link = p; + last = p; + } + last->link = P; + + // pass 4: elide JMP to next instruction. + // only safe if there are no jumps to JMPs anymore. + 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(debug['R'] && debug['v']) + print("del %P\n", p); + continue; + } + if(last) + last->link = p; + last = p; + } + last->link = P; + } + + if(debug['R'] && debug['v']) { + print("\n"); + for(p=firstp; p; p=p->link) + print("%P\n", p); + print("\n"); + } +} diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h index cf86ae48b..569536ebd 100644 --- a/src/cmd/5l/5.out.h +++ b/src/cmd/5l/5.out.h @@ -35,7 +35,8 @@ #define NOPROF (1<<0) #define DUPOK (1<<1) #define NOSPLIT (1<<2) -#define ALLTHUMBS (1<<3) +#define RODATA (1<<3) +#define NOPTR (1<<4) #define REGRET 0 /* -1 disables use of REGARG */ diff --git a/src/cmd/5l/Makefile b/src/cmd/5l/Makefile index 8489abc64..3f528d751 100644 --- a/src/cmd/5l/Makefile +++ b/src/cmd/5l/Makefile @@ -1,43 +1,5 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) - -TARG=5l - -OFILES=\ - asm.$O\ - data.$O\ - elf.$O\ - enam.$O\ - ldelf.$O\ - ldmacho.$O\ - ldpe.$O\ - lib.$O\ - list.$O\ - noop.$O\ - obj.$O\ - optab.$O\ - pass.$O\ - prof.$O\ - softfloat.$O\ - span.$O\ - symtab.$O\ - go.$O\ - -HFILES=\ - l.h\ - 5.out.h\ - ../ld/elf.h\ - -include ../../Make.ccmd - -enam.c: 5.out.h - sh mkenam - -CLEANFILES+=enam.c - -%.$O: ../ld/%.c - $(HOST_CC) $(HOST_CFLAGS) -c -I. ../ld/$*.c +include ../../Make.dist diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index 5b7f6f111..b36a982d1 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -73,6 +73,9 @@ enum { ElfStrShstrtab, ElfStrRelPlt, ElfStrPlt, + ElfStrNoteNetbsdIdent, + ElfStrNoPtrData, + ElfStrNoPtrBss, NElfStr }; @@ -90,6 +93,7 @@ needlib(char *name) /* reuse hash code in symbol table */ p = smprint(".dynlib.%s", name); s = lookup(p, 0); + free(p); if(s->type == 0) { s->type = 100; // avoid SDATA, etc. return 1; @@ -162,8 +166,12 @@ doelf(void) 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"); @@ -232,7 +240,7 @@ doelf(void) /* define dynamic elf table */ s = lookup(".dynamic", 0); s->reachable = 1; - s->type = SELFROSECT; + s->type = SELFSECT; // writable /* * .dynamic table @@ -251,6 +259,7 @@ doelf(void) 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); } } @@ -293,7 +302,7 @@ asmb(void) { int32 t; int a, dynsym; - uint32 fo, symo, startva; + uint32 fo, symo, startva, resoff; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; @@ -321,11 +330,6 @@ asmb(void) cseek(segdata.fileoff); datblk(segdata.vaddr, segdata.filelen); - /* output read-only data in text segment */ - sect = segtext.sect->next; - cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); - datblk(sect->vaddr, sect->len); - if(iself) { /* index of elf text section; needed by asmelfsym, double-checked below */ /* !debug['d'] causes extra sections before the .text section */ @@ -335,6 +339,8 @@ asmb(void) if(elfverneed) elftextsh += 2; } + if(HEADTYPE == Hnetbsd) + elftextsh += 1; } /* output symbol table */ @@ -370,7 +376,7 @@ asmb(void) cseek(symo); if(iself) { if(debug['v']) - Bprint(&bso, "%5.2f elfsym\n", cputime()); + Bprint(&bso, "%5.2f elfsym\n", cputime()); asmelfsym(); cflush(); cwrite(elfstrdat, elfstrsize); @@ -454,6 +460,7 @@ asmb(void) 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]); @@ -486,7 +493,7 @@ asmb(void) sh->addralign = 1; if(interpreter == nil) interpreter = linuxdynld; - elfinterp(sh, startva, interpreter); + resoff -= elfinterp(sh, startva, resoff, interpreter); ph = newElfPhdr(); ph->type = PT_INTERP; @@ -494,11 +501,24 @@ asmb(void) 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 */ + if(!debug['d']) { /* -d suppresses dynamic loader format */ /* S headers for dynamic linking */ sh = newElfShdr(elfstr[ElfStrGot]); sh->type = SHT_PROGBITS; @@ -589,7 +609,7 @@ asmb(void) for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshbits(sect); - if (!debug['s']) { + if(!debug['s']) { sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; sh->off = symo; @@ -631,8 +651,10 @@ asmb(void) a += elfwritehdr(); a += elfwritephdrs(); a += elfwriteshdrs(); - cflush(); - if(a+elfwriteinterp() > ELFRESERVE) + a += elfwriteinterp(elfstr[ElfStrInterp]); + if(HEADTYPE == Hnetbsd) + a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]); + if(a > ELFRESERVE) diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); break; } @@ -1827,14 +1849,20 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) 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; diff --git a/src/cmd/5l/doc.go b/src/cmd/5l/doc.go index aa7ccebfc..969f502a7 100644 --- a/src/cmd/5l/doc.go +++ b/src/cmd/5l/doc.go @@ -4,36 +4,10 @@ /* -5l is a modified version of the Plan 9 linker. The original is documented at +5l is the linker for the ARM. +The $GOARCH for these tools is arm. - http://plan9.bell-labs.com/magic/man2html/1/2l - -Its target architecture is the ARM, referred to by these tools as arm. -It reads files in .5 format generated by 5g, 5c, and 5a and emits -a binary called 5.out by default. - -Major changes include: - - support for segmented stacks (this feature is implemented here, not in the compilers). - - -Original options are listed in the link above. - -Options new in this version: - --F - Force use of software floating point. - Also implied by setting GOARM=5 in the environment. --Hlinux - Write Linux ELF binaries (default when $GOOS is linux) --I interpreter - Set the ELF dynamic linker to use. --L dir1 -L dir2 - Search for libraries (package files) in dir1, dir2, etc. - The default is the single location $GOROOT/pkg/$GOOS_arm. --r dir1:dir2:... - Set the dynamic linker search path when using ELF. --V - Print the linker version. +The flags are documented in ../ld/doc.go. */ package documentation diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index dabe93d37..b1a48ded8 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -143,6 +143,7 @@ struct Sym int32 value; int32 sig; int32 size; + int32 align; // if non-zero, required alignment in bytes uchar special; uchar fnptr; // used as fn ptr Sym* hash; // in hash table diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c index fa838215b..7b623d78a 100644 --- a/src/cmd/5l/list.c +++ b/src/cmd/5l/list.c @@ -65,7 +65,7 @@ Pconv(Fmt *fp) switch(a) { default: fmtprint(fp, "(%d)", p->line); - if(p->reg == NREG) + if(p->reg == NREG && p->as != AGLOBL) fmtprint(fp, " %A%C %D,%D", a, p->scond, &p->from, &p->to); else diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c index eb44344f4..004f9f2fa 100644 --- a/src/cmd/5l/noop.c +++ b/src/cmd/5l/noop.c @@ -226,8 +226,7 @@ noops(void) p->as = AMOVW; p->scond = C_SCOND_LO; p->from.type = D_CONST; - /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */ - p->from.offset = autosize+160; + p->from.offset = autosize; p->to.type = D_REG; p->to.reg = 1; diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c index fc5806aac..a3f816160 100644 --- a/src/cmd/5l/obj.c +++ b/src/cmd/5l/obj.c @@ -81,7 +81,7 @@ void main(int argc, char *argv[]) { int c, i; - char *p; + char *p, *name, *val; Binit(&bso, 1, OWRITE); listinit(); @@ -92,6 +92,7 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; + nuxiinit(); p = getenv("GOARM"); if(p != nil && strcmp(p, "5") == 0) @@ -136,6 +137,11 @@ main(int argc, char *argv[]) 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); @@ -235,7 +241,6 @@ main(int argc, char *argv[]) histgen = 0; pc = 0; dtype = 4; - nuxiinit(); version = 0; cbp = buf.cbuf; @@ -535,7 +540,7 @@ loop: s->type = SBSS; s->value = 0; } - if(s->type != SBSS) { + if(s->type != SBSS && s->type != SNOPTRBSS && !s->dupok) { diag("redefinition: %s\n%P", s->name, p); s->type = SBSS; s->value = 0; @@ -544,6 +549,10 @@ loop: s->size = p->to.offset; if(p->reg & DUPOK) s->dupok = 1; + if(p->reg & RODATA) + s->type = SRODATA; + else if(p->reg & NOPTR) + s->type = SNOPTRBSS; break; case ADATA: @@ -553,8 +562,8 @@ loop: // redefinitions. s = p->from.sym; if(s->dupok) { - if(debug['v']) - Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn); +// if(debug['v']) +// Bprint(&bso, "skipping %s in %s: dupok\n", s->name, pn); goto loop; } if(s->file == nil) @@ -585,6 +594,10 @@ loop: errorexit(); } cursym = s; + if(s->type != 0 && s->type != SXREF && (p->reg & DUPOK)) { + skip = 1; + goto casedef; + } if(ntext++ == 0 && s->type != 0 && s->type != SXREF) { /* redefinition, so file has probably been seen before */ if(debug['v']) @@ -592,13 +605,8 @@ loop: return; } skip = 0; - if(s->type != 0 && s->type != SXREF) { - if(p->reg & DUPOK) { - skip = 1; - goto casedef; - } + if(s->type != 0 && s->type != SXREF) diag("redefinition: %s\n%P", s->name, p); - } if(etextp) etextp->next = s; else diff --git a/src/cmd/5l/pass.c b/src/cmd/5l/pass.c index c43049459..0f2afbd85 100644 --- a/src/cmd/5l/pass.c +++ b/src/cmd/5l/pass.c @@ -213,6 +213,8 @@ patch(void) if((a == ABL || a == ABX || a == AB || a == ARET) && p->to.type != D_BRANCH && p->to.sym != S) { s = p->to.sym; + if(s->text == nil) + continue; switch(s->type) { default: diag("undefined: %s", s->name); @@ -222,7 +224,8 @@ patch(void) case STEXT: p->to.offset = s->value; p->to.type = D_BRANCH; - break; + p->cond = s->text; + continue; } } if(p->to.type != D_BRANCH) diff --git a/src/cmd/5l/softfloat.c b/src/cmd/5l/softfloat.c index 4f799d17e..401107178 100644 --- a/src/cmd/5l/softfloat.c +++ b/src/cmd/5l/softfloat.c @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#define EXTERN #include "l.h" #include "../ld/lib.h" diff --git a/src/cmd/5l/span.c b/src/cmd/5l/span.c index 2e1232a1a..13e1848e1 100644 --- a/src/cmd/5l/span.c +++ b/src/cmd/5l/span.c @@ -421,6 +421,8 @@ symaddr(Sym *s) case SDATA: case SBSS: case SCONST: + case SNOPTRDATA: + case SNOPTRBSS: break; } return v; diff --git a/src/cmd/6a/Makefile b/src/cmd/6a/Makefile index 30180bd24..27290ddd7 100644 --- a/src/cmd/6a/Makefile +++ b/src/cmd/6a/Makefile @@ -1,25 +1,10 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) +include ../../Make.dist -TARG=6a +install: y.tab.h -HFILES=\ - a.h\ - y.tab.h\ - ../6l/6.out.h\ - -OFILES=\ - y.tab.$O\ - lex.$O\ - ../6l/enam.$O\ - -YFILES=\ - a.y\ - -include ../../Make.ccmd - -lex.$O: ../cc/macbody ../cc/lexbody +y.tab.h: a.y + LANG=C LANGUAGE=en_US.UTF8 bison -d -v -y a.y diff --git a/src/cmd/6a/a.y b/src/cmd/6a/a.y index c0fa4106e..8459ff323 100644 --- a/src/cmd/6a/a.y +++ b/src/cmd/6a/a.y @@ -429,6 +429,12 @@ imm: $$.type = D_FCONST; $$.dval = $3; } +| '$' '(' '-' LFCONST ')' + { + $$ = nullgen; + $$.type = D_FCONST; + $$.dval = -$4; + } | '$' '-' LFCONST { $$ = nullgen; diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c index 42f4b1d11..e013bec2a 100644 --- a/src/cmd/6a/lex.c +++ b/src/cmd/6a/lex.c @@ -44,7 +44,11 @@ enum int systemtype(int sys) { +#ifdef _WIN32 + return sys&Windows; +#else return sys&Plan9; +#endif } int @@ -392,6 +396,7 @@ struct "IMULB", LTYPEI, AIMULB, "IMULL", LTYPEI, AIMULL, "IMULQ", LTYPEI, AIMULQ, + "IMUL3Q", LTYPEX, AIMUL3Q, "IMULW", LTYPEI, AIMULW, "INB", LTYPE0, AINB, "INL", LTYPE0, AINL, @@ -456,8 +461,8 @@ struct "JGT", LTYPER, AJGT, "JG", LTYPER, AJGT, /* alternate */ "JNLE", LTYPER, AJGT, /* alternate */ - - "JCXZ", LTYPER, AJCXZ, + "JCXZL", LTYPER, AJCXZL, + "JCXZQ", LTYPER, AJCXZQ, "JMP", LTYPEC, AJMP, "LAHF", LTYPE0, ALAHF, "LARL", LTYPE3, ALARL, @@ -1251,17 +1256,13 @@ outhist(void) for(h = hist; h != H; h = h->link) { p = h->name; op = 0; - /* on windows skip drive specifier in pathname */ if(systemtype(Windows) && p && p[1] == ':'){ - p += 2; - c = *p; - } - if(p && p[0] != c && h->offset == 0 && pathname){ - /* on windows skip drive specifier in pathname */ + c = p[2]; + } else if(p && p[0] != c && h->offset == 0 && pathname){ if(systemtype(Windows) && pathname[1] == ':') { op = p; - p = pathname+2; - c = *p; + p = pathname; + c = p[2]; } else if(pathname[0] == c){ op = p; p = pathname; diff --git a/src/cmd/6a/y.tab.c b/src/cmd/6a/y.tab.c new file mode 100644 index 000000000..2da8a1fac --- /dev/null +++ b/src/cmd/6a/y.tab.c @@ -0,0 +1,2730 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* 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 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, 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 + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + 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. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + LTYPE0 = 258, + LTYPE1 = 259, + LTYPE2 = 260, + LTYPE3 = 261, + LTYPE4 = 262, + LTYPEC = 263, + LTYPED = 264, + LTYPEN = 265, + LTYPER = 266, + LTYPET = 267, + LTYPEG = 268, + LTYPES = 269, + LTYPEM = 270, + LTYPEI = 271, + LTYPEXC = 272, + LTYPEX = 273, + LTYPERT = 274, + LCONST = 275, + LFP = 276, + LPC = 277, + LSB = 278, + LBREG = 279, + LLREG = 280, + LSREG = 281, + LFREG = 282, + LMREG = 283, + LXREG = 284, + LFCONST = 285, + LSCONST = 286, + LSP = 287, + LNAME = 288, + LLAB = 289, + LVAR = 290 + }; +#endif +/* Tokens. */ +#define LTYPE0 258 +#define LTYPE1 259 +#define LTYPE2 260 +#define LTYPE3 261 +#define LTYPE4 262 +#define LTYPEC 263 +#define LTYPED 264 +#define LTYPEN 265 +#define LTYPER 266 +#define LTYPET 267 +#define LTYPEG 268 +#define LTYPES 269 +#define LTYPEM 270 +#define LTYPEI 271 +#define LTYPEXC 272 +#define LTYPEX 273 +#define LTYPERT 274 +#define LCONST 275 +#define LFP 276 +#define LPC 277 +#define LSB 278 +#define LBREG 279 +#define LLREG 280 +#define LSREG 281 +#define LFREG 282 +#define LMREG 283 +#define LXREG 284 +#define LFCONST 285 +#define LSCONST 286 +#define LSP 287 +#define LNAME 288 +#define LLAB 289 +#define LVAR 290 + + + + +/* Copy the first part of user declarations. */ +#line 31 "a.y" + +#include +#include /* if we don't, bison will, and a.h re-#defines getc */ +#include +#include "a.h" + + +/* 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 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 37 "a.y" +{ + Sym *sym; + vlong lval; + double dval; + char sval[8]; + Gen gen; + Gen2 gen2; +} +/* Line 193 of yacc.c. */ +#line 182 "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 216 of yacc.c. */ +#line 195 "y.tab.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# 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 _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + 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) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((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 + +/* 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 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 548 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 54 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 40 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 131 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 260 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 290 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +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, 52, 12, 5, 2, + 50, 51, 10, 8, 49, 9, 2, 11, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 46, 47, + 6, 48, 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, 53, 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, 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, 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, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 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, 41, 42, 43, 44, + 45 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +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, 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 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 55, 0, -1, -1, -1, 55, 56, 57, -1, -1, + 44, 46, 58, 57, -1, -1, 43, 46, 59, 57, + -1, 47, -1, 60, 47, -1, 1, 47, -1, 43, + 48, 93, -1, 45, 48, 93, -1, 13, 61, -1, + 14, 65, -1, 15, 64, -1, 16, 62, -1, 17, + 63, -1, 21, 66, -1, 19, 67, -1, 22, 68, + -1, 18, 69, -1, 20, 70, -1, 24, 71, -1, + 25, 72, -1, 26, 73, -1, 27, 74, -1, 28, + 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 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 64, 64, 66, 65, 73, 72, 80, 79, 85, + 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 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "'|'", "'^'", "'&'", "'<'", "'>'", "'+'", + "'-'", "'*'", "'/'", "'%'", "LTYPE0", "LTYPE1", "LTYPE2", "LTYPE3", + "LTYPE4", "LTYPEC", "LTYPED", "LTYPEN", "LTYPER", "LTYPET", "LTYPEG", + "LTYPES", "LTYPEM", "LTYPEI", "LTYPEXC", "LTYPEX", "LTYPERT", "LCONST", + "LFP", "LPC", "LSB", "LBREG", "LLREG", "LSREG", "LFREG", "LMREG", + "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", "spec9", "spec10", "spec11", "rem", "rom", "rim", + "rel", "reg", "imm2", "imm", "mem", "omem", "nmem", "nam", "offset", + "pointer", "con", "con2", "expr", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 124, 94, 38, 60, 62, 43, 45, + 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, 286, 287, 288, 289, 290, 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, 54, 55, 56, 55, 58, 57, 59, 57, 57, + 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 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +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, 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 +}; + +/* 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_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 +}; + +/* 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 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#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 +}; + +/* 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 +}; + +/* 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 -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 +}; + +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 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 55, 0, 56, 1, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 43, 44, 45, 47, 57, 60, 47, 49, + 61, 8, 9, 30, 34, 35, 36, 37, 38, 39, + 42, 43, 45, 49, 50, 53, 65, 78, 82, 85, + 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 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* 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. */ + +#define YYFAIL goto yyerrlab + +#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 \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#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. */ + +#ifndef YY_LOCATION_PRINT +# 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 + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# 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]; + + 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; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + 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. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* 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 look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* 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]; + char *yymsg = yymsgbuf; + 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; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + 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; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + 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 + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + 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 look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 3: +#line 66 "a.y" + { + stmtline = lineno; + } + break; + + case 5: +#line 73 "a.y" + { + if((yyvsp[(1) - (2)].sym)->value != pc) + yyerror("redeclaration of %s", (yyvsp[(1) - (2)].sym)->name); + (yyvsp[(1) - (2)].sym)->value = pc; + } + break; + + case 7: +#line 80 "a.y" + { + (yyvsp[(1) - (2)].sym)->type = LLAB; + (yyvsp[(1) - (2)].sym)->value = pc; + } + break; + + case 12: +#line 91 "a.y" + { + (yyvsp[(1) - (3)].sym)->type = LVAR; + (yyvsp[(1) - (3)].sym)->value = (yyvsp[(3) - (3)].lval); + } + break; + + case 13: +#line 96 "a.y" + { + if((yyvsp[(1) - (3)].sym)->value != (yyvsp[(3) - (3)].lval)) + yyerror("redeclaration of %s", (yyvsp[(1) - (3)].sym)->name); + (yyvsp[(1) - (3)].sym)->value = (yyvsp[(3) - (3)].lval); + } + break; + + case 14: +#line 101 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 15: +#line 102 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 16: +#line 103 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 17: +#line 104 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 18: +#line 105 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 19: +#line 106 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 20: +#line 107 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 21: +#line 108 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 22: +#line 109 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 23: +#line 110 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 24: +#line 111 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 25: +#line 112 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 26: +#line 113 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 27: +#line 114 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 28: +#line 115 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 29: +#line 116 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 30: +#line 117 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 31: +#line 120 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = nullgen; + } + break; + + case 32: +#line 125 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = nullgen; + } + break; + + case 33: +#line 132 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 34: +#line 139 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 35: +#line 146 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (2)].gen); + (yyval.gen2).to = nullgen; + } + break; + + case 36: +#line 151 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (1)].gen); + (yyval.gen2).to = nullgen; + } + break; + + case 37: +#line 158 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = (yyvsp[(2) - (2)].gen); + } + break; + + case 38: +#line 163 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = (yyvsp[(1) - (1)].gen); + } + break; + + case 39: +#line 170 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = (yyvsp[(2) - (2)].gen); + } + break; + + case 40: +#line 175 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = (yyvsp[(1) - (1)].gen); + } + break; + + case 41: +#line 182 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); + (yyval.gen2).to = (yyvsp[(5) - (5)].gen); + } + break; + + case 42: +#line 190 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 43: +#line 195 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); + (yyval.gen2).to = (yyvsp[(5) - (5)].gen); + } + break; + + case 44: +#line 203 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = (yyvsp[(2) - (2)].gen); + } + break; + + case 45: +#line 208 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = (yyvsp[(1) - (1)].gen); + } + break; + + case 48: +#line 219 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 49: +#line 224 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).to = (yyvsp[(3) - (5)].gen); + if((yyval.gen2).from.index != D_NONE) + yyerror("dp shift with lhs index"); + (yyval.gen2).from.index = (yyvsp[(5) - (5)].lval); + } + break; + + case 50: +#line 234 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 51: +#line 239 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).to = (yyvsp[(3) - (5)].gen); + if((yyval.gen2).to.index != D_NONE) + yyerror("dp move with lhs index"); + (yyval.gen2).to.index = (yyvsp[(5) - (5)].lval); + } + break; + + case 52: +#line 249 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (2)].gen); + (yyval.gen2).to = nullgen; + } + break; + + case 53: +#line 254 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (1)].gen); + (yyval.gen2).to = nullgen; + } + break; + + case 54: +#line 259 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 55: +#line 266 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).to = (yyvsp[(3) - (5)].gen); + (yyval.gen2).to.offset = (yyvsp[(5) - (5)].lval); + } + break; + + case 56: +#line 274 "a.y" + { + (yyval.gen2).from = (yyvsp[(3) - (5)].gen); + (yyval.gen2).to = (yyvsp[(5) - (5)].gen); + if((yyvsp[(1) - (5)].gen).type != D_CONST) + yyerror("illegal constant"); + (yyval.gen2).to.offset = (yyvsp[(1) - (5)].gen).offset; + } + break; + + case 57: +#line 283 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = nullgen; + } + break; + + case 58: +#line 288 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (1)].gen); + (yyval.gen2).to = nullgen; + } + break; + + case 59: +#line 295 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 60: +#line 300 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); + (yyval.gen2).to = (yyvsp[(5) - (5)].gen); + } + break; + + case 65: +#line 314 "a.y" + { + (yyval.gen) = (yyvsp[(2) - (2)].gen); + } + break; + + case 66: +#line 318 "a.y" + { + (yyval.gen) = (yyvsp[(2) - (2)].gen); + } + break; + + case 71: +#line 330 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_BRANCH; + (yyval.gen).offset = (yyvsp[(1) - (4)].lval) + pc; + } + break; + + case 72: +#line 336 "a.y" + { + (yyval.gen) = nullgen; + if(pass == 2) + yyerror("undefined label: %s", (yyvsp[(1) - (2)].sym)->name); + (yyval.gen).type = D_BRANCH; + (yyval.gen).sym = (yyvsp[(1) - (2)].sym); + (yyval.gen).offset = (yyvsp[(2) - (2)].lval); + } + break; + + case 73: +#line 345 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_BRANCH; + (yyval.gen).sym = (yyvsp[(1) - (2)].sym); + (yyval.gen).offset = (yyvsp[(1) - (2)].sym)->value + (yyvsp[(2) - (2)].lval); + } + break; + + case 74: +#line 354 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); + } + break; + + case 75: +#line 359 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); + } + break; + + case 76: +#line 364 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); + } + break; + + case 77: +#line 369 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); + } + break; + + case 78: +#line 374 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_SP; + } + break; + + case 79: +#line 379 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); + } + break; + + case 80: +#line 384 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); + } + break; + + case 81: +#line 390 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_CONST; + (yyval.gen).offset = (yyvsp[(2) - (2)].lval); + } + break; + + case 82: +#line 398 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_CONST; + (yyval.gen).offset = (yyvsp[(2) - (2)].lval); + } + break; + + case 83: +#line 404 "a.y" + { + (yyval.gen) = (yyvsp[(2) - (2)].gen); + (yyval.gen).index = (yyvsp[(2) - (2)].gen).type; + (yyval.gen).type = D_ADDR; + /* + if($2.type == D_AUTO || $2.type == D_PARAM) + yyerror("constant cannot be automatic: %s", + $2.sym->name); + */ + } + break; + + case 84: +#line 415 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_SCONST; + memcpy((yyval.gen).sval, (yyvsp[(2) - (2)].sval), sizeof((yyval.gen).sval)); + } + break; + + case 85: +#line 421 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_FCONST; + (yyval.gen).dval = (yyvsp[(2) - (2)].dval); + } + break; + + case 86: +#line 427 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_FCONST; + (yyval.gen).dval = (yyvsp[(3) - (4)].dval); + } + break; + + case 87: +#line 433 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_FCONST; + (yyval.gen).dval = -(yyvsp[(4) - (5)].dval); + } + break; + + case 88: +#line 439 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_FCONST; + (yyval.gen).dval = -(yyvsp[(3) - (3)].dval); + } + break; + + case 91: +#line 451 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+D_NONE; + (yyval.gen).offset = (yyvsp[(1) - (1)].lval); + } + break; + + case 92: +#line 457 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); + (yyval.gen).offset = (yyvsp[(1) - (4)].lval); + } + break; + + case 93: +#line 463 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+D_SP; + (yyval.gen).offset = (yyvsp[(1) - (4)].lval); + } + break; + + case 94: +#line 469 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); + (yyval.gen).offset = (yyvsp[(1) - (4)].lval); + } + break; + + case 95: +#line 475 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+D_NONE; + (yyval.gen).offset = (yyvsp[(1) - (6)].lval); + (yyval.gen).index = (yyvsp[(3) - (6)].lval); + (yyval.gen).scale = (yyvsp[(5) - (6)].lval); + checkscale((yyval.gen).scale); + } + break; + + case 96: +#line 484 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval); + (yyval.gen).offset = (yyvsp[(1) - (9)].lval); + (yyval.gen).index = (yyvsp[(6) - (9)].lval); + (yyval.gen).scale = (yyvsp[(8) - (9)].lval); + checkscale((yyval.gen).scale); + } + break; + + case 97: +#line 493 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval); + } + break; + + case 98: +#line 498 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+D_SP; + } + break; + + case 99: +#line 503 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+D_NONE; + (yyval.gen).index = (yyvsp[(2) - (5)].lval); + (yyval.gen).scale = (yyvsp[(4) - (5)].lval); + checkscale((yyval.gen).scale); + } + break; + + case 100: +#line 511 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval); + (yyval.gen).index = (yyvsp[(5) - (8)].lval); + (yyval.gen).scale = (yyvsp[(7) - (8)].lval); + checkscale((yyval.gen).scale); + } + break; + + case 101: +#line 521 "a.y" + { + (yyval.gen) = (yyvsp[(1) - (1)].gen); + } + break; + + case 102: +#line 525 "a.y" + { + (yyval.gen) = (yyvsp[(1) - (6)].gen); + (yyval.gen).index = (yyvsp[(3) - (6)].lval); + (yyval.gen).scale = (yyvsp[(5) - (6)].lval); + checkscale((yyval.gen).scale); + } + break; + + case 103: +#line 534 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(4) - (5)].lval); + (yyval.gen).sym = (yyvsp[(1) - (5)].sym); + (yyval.gen).offset = (yyvsp[(2) - (5)].lval); + } + break; + + case 104: +#line 541 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_STATIC; + (yyval.gen).sym = (yyvsp[(1) - (7)].sym); + (yyval.gen).offset = (yyvsp[(4) - (7)].lval); + } + break; + + case 105: +#line 549 "a.y" + { + (yyval.lval) = 0; + } + break; + + case 106: +#line 553 "a.y" + { + (yyval.lval) = (yyvsp[(2) - (2)].lval); + } + break; + + case 107: +#line 557 "a.y" + { + (yyval.lval) = -(yyvsp[(2) - (2)].lval); + } + break; + + case 109: +#line 564 "a.y" + { + (yyval.lval) = D_AUTO; + } + break; + + case 112: +#line 572 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; + } + break; + + case 113: +#line 576 "a.y" + { + (yyval.lval) = -(yyvsp[(2) - (2)].lval); + } + break; + + case 114: +#line 580 "a.y" + { + (yyval.lval) = (yyvsp[(2) - (2)].lval); + } + break; + + case 115: +#line 584 "a.y" + { + (yyval.lval) = ~(yyvsp[(2) - (2)].lval); + } + break; + + case 116: +#line 588 "a.y" + { + (yyval.lval) = (yyvsp[(2) - (3)].lval); + } + break; + + case 117: +#line 594 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (1)].lval) & 0xffffffffLL; + } + break; + + case 118: +#line 598 "a.y" + { + (yyval.lval) = -(yyvsp[(2) - (2)].lval) & 0xffffffffLL; + } + break; + + case 119: +#line 602 "a.y" + { + (yyval.lval) = ((yyvsp[(1) - (3)].lval) & 0xffffffffLL) + + (((yyvsp[(3) - (3)].lval) & 0xffffLL) << 32); + } + break; + + case 120: +#line 607 "a.y" + { + (yyval.lval) = (-(yyvsp[(2) - (4)].lval) & 0xffffffffLL) + + (((yyvsp[(4) - (4)].lval) & 0xffffLL) << 32); + } + break; + + case 122: +#line 615 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); + } + break; + + case 123: +#line 619 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); + } + break; + + case 124: +#line 623 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); + } + break; + + case 125: +#line 627 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); + } + break; + + case 126: +#line 631 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); + } + break; + + case 127: +#line 635 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); + } + break; + + case 128: +#line 639 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); + } + break; + + case 129: +#line 643 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); + } + break; + + case 130: +#line 647 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); + } + break; + + case 131: +#line 651 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); + } + break; + + +/* Line 1267 of yacc.c. */ +#line 2517 "y.tab.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++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. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + 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; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + 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); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + diff --git a/src/cmd/6a/y.tab.h b/src/cmd/6a/y.tab.h new file mode 100644 index 000000000..3cca766d3 --- /dev/null +++ b/src/cmd/6a/y.tab.h @@ -0,0 +1,135 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* 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 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, 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 + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + 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 + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + LTYPE0 = 258, + LTYPE1 = 259, + LTYPE2 = 260, + LTYPE3 = 261, + LTYPE4 = 262, + LTYPEC = 263, + LTYPED = 264, + LTYPEN = 265, + LTYPER = 266, + LTYPET = 267, + LTYPEG = 268, + LTYPES = 269, + LTYPEM = 270, + LTYPEI = 271, + LTYPEXC = 272, + LTYPEX = 273, + LTYPERT = 274, + LCONST = 275, + LFP = 276, + LPC = 277, + LSB = 278, + LBREG = 279, + LLREG = 280, + LSREG = 281, + LFREG = 282, + LMREG = 283, + LXREG = 284, + LFCONST = 285, + LSCONST = 286, + LSP = 287, + LNAME = 288, + LLAB = 289, + LVAR = 290 + }; +#endif +/* Tokens. */ +#define LTYPE0 258 +#define LTYPE1 259 +#define LTYPE2 260 +#define LTYPE3 261 +#define LTYPE4 262 +#define LTYPEC 263 +#define LTYPED 264 +#define LTYPEN 265 +#define LTYPER 266 +#define LTYPET 267 +#define LTYPEG 268 +#define LTYPES 269 +#define LTYPEM 270 +#define LTYPEI 271 +#define LTYPEXC 272 +#define LTYPEX 273 +#define LTYPERT 274 +#define LCONST 275 +#define LFP 276 +#define LPC 277 +#define LSB 278 +#define LBREG 279 +#define LLREG 280 +#define LSREG 281 +#define LFREG 282 +#define LMREG 283 +#define LXREG 284 +#define LFCONST 285 +#define LSCONST 286 +#define LSP 287 +#define LNAME 288 +#define LLAB 289 +#define LVAR 290 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 37 "a.y" +{ + Sym *sym; + vlong lval; + double dval; + char sval[8]; + Gen gen; + Gen2 gen2; +} +/* Line 1529 of yacc.c. */ +#line 128 "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/6c/Makefile b/src/cmd/6c/Makefile index 484e16def..3f528d751 100644 --- a/src/cmd/6c/Makefile +++ b/src/cmd/6c/Makefile @@ -1,36 +1,5 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) - -TARG=6c - -HFILES=\ - gc.h\ - ../6l/6.out.h\ - ../cc/cc.h\ - -OFILES=\ - cgen.$O\ - list.$O\ - sgen.$O\ - swt.$O\ - txt.$O\ - pgen.$O\ - pswt.$O\ - div.$O\ - mul.$O\ - reg.$O\ - peep.$O\ - machcap.$O\ - ../6l/enam.$O\ - -LIB=\ - ../cc/cc.a\ - -include ../../Make.ccmd - -%.$O: ../cc/%.c - $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../cc/$*.c +include ../../Make.dist diff --git a/src/cmd/6c/cgen.c b/src/cmd/6c/cgen.c index 7f717dcbb..71822586c 100644 --- a/src/cmd/6c/cgen.c +++ b/src/cmd/6c/cgen.c @@ -1237,11 +1237,12 @@ void boolgen(Node *n, int true, Node *nn) { int o; - Prog *p1, *p2; + Prog *p1, *p2, *p3; Node *l, *r, nod, nod1; int32 curs; if(debug['g']) { + print("boolgen %d\n", true); prtree(nn, "boolgen lhs"); prtree(n, "boolgen"); } @@ -1353,6 +1354,15 @@ boolgen(Node *n, int true, Node *nn) case OLO: case OLS: o = n->op; + if(true && typefd[l->type->etype] && (o == OEQ || o == ONE)) { + // Cannot rewrite !(l == r) into l != r with float64; it breaks NaNs. + // Jump around instead. + boolgen(n, 0, Z); + p1 = p; + gbranch(OGOTO); + patch(p1, pc); + goto com; + } if(true) o = comrel[relindex(o)]; if(l->complex >= FNX && r->complex >= FNX) { @@ -1367,6 +1377,10 @@ boolgen(Node *n, int true, Node *nn) break; } if(immconst(l)) { + // NOTE: Reversing the comparison here is wrong + // for floating point ordering comparisons involving NaN, + // but we don't have any of those yet so we don't + // bother worrying about it. o = invrel[relindex(o)]; /* bad, 13 is address of external that becomes constant */ if(r->addable < INDEXED || r->addable == 13) { @@ -1388,10 +1402,11 @@ boolgen(Node *n, int true, Node *nn) cgen(r, &nod1); gopcode(o, l->type, &nod, &nod1); regfree(&nod1); - } else + } else { gopcode(o, l->type, &nod, r); + } regfree(&nod); - goto com; + goto fixfloat; } regalloc(&nod, r, nn); cgen(r, &nod); @@ -1406,6 +1421,33 @@ boolgen(Node *n, int true, Node *nn) } else gopcode(o, l->type, l, &nod); regfree(&nod); + fixfloat: + if(typefd[l->type->etype]) { + switch(o) { + case OEQ: + // Already emitted AJEQ; want AJEQ and AJPC. + p1 = p; + gbranch(OGOTO); + p2 = p; + patch(p1, pc); + gins(AJPC, Z, Z); + patch(p2, pc); + break; + + case ONE: + // Already emitted AJNE; want AJNE or AJPS. + p1 = p; + gins(AJPS, Z, Z); + p2 = p; + gbranch(OGOTO); + p3 = p; + patch(p1, pc); + patch(p2, pc); + gbranch(OGOTO); + patch(p3, pc); + break; + } + } com: if(nn != Z) { diff --git a/src/cmd/6c/gc.h b/src/cmd/6c/gc.h index 0c23b115c..b0081abb5 100644 --- a/src/cmd/6c/gc.h +++ b/src/cmd/6c/gc.h @@ -299,7 +299,8 @@ void gpseudo(int, Sym*, Node*); int swcmp(const void*, const void*); void doswit(Node*); void swit1(C1*, int, int32, Node*); -void cas(void); +void swit2(C1*, int, int32, Node*); +void newcase(void); void bitload(Node*, Node*, Node*, Node*, Node*); void bitstore(Node*, Node*, Node*, Node*, Node*); int32 outstring(char*, int32); diff --git a/src/cmd/6c/swt.c b/src/cmd/6c/swt.c index d7a917043..f16d0f78a 100644 --- a/src/cmd/6c/swt.c +++ b/src/cmd/6c/swt.c @@ -32,6 +32,21 @@ void swit1(C1 *q, int nc, int32 def, Node *n) +{ + Node nreg; + + regalloc(&nreg, n, Z); + if(typev[n->type->etype]) + nreg.type = types[TVLONG]; + else + nreg.type = types[TLONG]; + cgen(n, &nreg); + swit2(q, nc, def, &nreg); + regfree(&nreg); +} + +void +swit2(C1 *q, int nc, int32 def, Node *n) { C1 *r; int i; @@ -58,12 +73,12 @@ swit1(C1 *q, int nc, int32 def, Node *n) gbranch(OGOTO); p->as = AJEQ; patch(p, r->label); - swit1(q, i, def, n); + swit2(q, i, def, n); if(debug['W']) print("case < %.8llux\n", r->val); patch(sp, pc); - swit1(r+1, nc-i-1, def, n); + swit2(r+1, nc-i-1, def, n); } void @@ -331,17 +346,13 @@ outhist(Biobuf *b) for(h = hist; h != H; h = h->link) { p = h->name; op = 0; - /* on windows skip drive specifier in pathname */ if(systemtype(Windows) && p && p[1] == ':'){ - p += 2; - c = *p; - } - if(p && p[0] != c && h->offset == 0 && pathname){ - /* on windows skip drive specifier in pathname */ + c = p[2]; + } else if(p && p[0] != c && h->offset == 0 && pathname){ if(systemtype(Windows) && pathname[1] == ':') { op = p; - p = pathname+2; - c = *p; + p = pathname; + c = p[2]; } else if(pathname[0] == c){ op = p; p = pathname; diff --git a/src/cmd/6c/txt.c b/src/cmd/6c/txt.c index 12fc5b498..2cb8c1585 100644 --- a/src/cmd/6c/txt.c +++ b/src/cmd/6c/txt.c @@ -158,7 +158,9 @@ gclean(void) continue; if(s->type == types[TENUM]) continue; + textflag = s->dataflag; gpseudo(AGLOBL, s, nodconst(s->type->width)); + textflag = 0; } nextpc(); p->as = AEND; @@ -411,7 +413,7 @@ regfree(Node *n) if(n->op != OREGISTER && n->op != OINDREG) goto err; i = n->reg; - if(i < 0 || i >= sizeof(reg)) + if(i < 0 || i >= nelem(reg)) goto err; if(reg[i] <= 0) goto err; diff --git a/src/cmd/6g/Makefile b/src/cmd/6g/Makefile index 64fa15399..3f528d751 100644 --- a/src/cmd/6g/Makefile +++ b/src/cmd/6g/Makefile @@ -1,35 +1,5 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) - -TARG=6g - -HFILES=\ - ../gc/go.h\ - ../6l/6.out.h\ - gg.h\ - opt.h\ - -OFILES=\ - ../6l/enam.$O\ - cgen.$O\ - cplx.$O\ - galign.$O\ - ggen.$O\ - gobj.$O\ - gsubr.$O\ - list.$O\ - peep.$O\ - pgen.$O\ - reg.$O\ - -LIB=\ - ../gc/gc.a\ - -include ../../Make.ccmd - -%.$O: ../gc/%.c - $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c +include ../../Make.dist diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c index 24f88a416..00334e71b 100644 --- a/src/cmd/6g/cgen.c +++ b/src/cmd/6g/cgen.c @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include +#include #include "gg.h" /* @@ -123,6 +125,9 @@ cgen(Node *n, Node *res) if(isslice(n->left->type)) n->addable = n->left->addable; break; + case OITAB: + n->addable = n->left->addable; + break; } if(complexop(n, res)) { @@ -257,6 +262,14 @@ cgen(Node *n, Node *res) gmove(&n1, res); regfree(&n1); break; + + case OITAB: + // interface table is first word of interface value + igen(nl, &n1, res); + n1.type = n->type; + gmove(&n1, res); + regfree(&n1); + break; case OLEN: if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { @@ -385,9 +398,9 @@ abop: // asymmetric binary regalloc(&n2, nr->type, N); cgen(nr, &n2); } else { - regalloc(&n2, nr->type, N); + regalloc(&n2, nr->type, res); cgen(nr, &n2); - regalloc(&n1, nl->type, res); + regalloc(&n1, nl->type, N); cgen(nl, &n1); } gins(a, &n2, &n1); @@ -715,6 +728,7 @@ bgen(Node *n, int true, Prog *to) int et, a; Node *nl, *nr, *l, *r; Node n1, n2, tmp; + NodeList *ll; Prog *p1, *p2; if(debug['g']) { @@ -727,9 +741,6 @@ bgen(Node *n, int true, Prog *to) if(n->ninit != nil) genlist(n->ninit); - nl = n->left; - nr = n->right; - if(n->type == T) { convlit(&n, types[TBOOL]); if(n->type == T) @@ -742,7 +753,6 @@ bgen(Node *n, int true, Prog *to) patch(gins(AEND, N, N), to); goto ret; } - nl = N; nr = N; switch(n->op) { @@ -836,7 +846,10 @@ bgen(Node *n, int true, Prog *to) 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); goto ret; @@ -1021,28 +1034,35 @@ stkof(Node *n) * memmove(&ns, &n, w); */ void -sgen(Node *n, Node *ns, int32 w) +sgen(Node *n, Node *ns, int64 w) { Node nodl, nodr, oldl, oldr, cx, oldcx, tmp; int32 c, q, odst, osrc; if(debug['g']) { - print("\nsgen w=%d\n", w); + print("\nsgen w=%lld\n", w); dump("r", n); dump("res", ns); } - if(w == 0) - return; - if(n->ullman >= UINF && ns->ullman >= UINF) { + + if(n->ullman >= UINF && ns->ullman >= UINF) fatal("sgen UINF"); - } if(w < 0) - fatal("sgen copy %d", w); + fatal("sgen copy %lld", w); if(w == 16) if(componentgen(n, ns)) return; + + if(w == 0) { + // evaluate side effects only + regalloc(&nodr, types[tptr], N); + agen(ns, &nodr); + agen(n, &nodr); + regfree(&nodr); + return; + } // offset on the stack osrc = stkof(n); diff --git a/src/cmd/6g/galign.c b/src/cmd/6g/galign.c index e366362b3..b03ac1ed6 100644 --- a/src/cmd/6g/galign.c +++ b/src/cmd/6g/galign.c @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include +#include #include "gg.h" int thechar = '6'; diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h index 2493771a0..47a540082 100644 --- a/src/cmd/6g/gg.h +++ b/src/cmd/6g/gg.h @@ -2,16 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include -#include +#ifndef EXTERN +#define EXTERN extern +#endif #include "../gc/go.h" #include "../6l/6.out.h" -#ifndef EXTERN -#define EXTERN extern -#endif - typedef struct Addr Addr; struct Addr @@ -44,22 +41,21 @@ struct Prog void* reg; // pointer to containing Reg struct }; -EXTERN Biobuf* bout; +#define TEXTFLAG from.scale + EXTERN int32 dynloc; EXTERN uchar reg[D_NONE]; EXTERN int32 pcloc; // instruction counter EXTERN Strlit emptystring; extern char* anames[]; -EXTERN Hist* hist; EXTERN Prog zprog; -EXTERN Node* curfn; EXTERN Node* newproc; EXTERN Node* deferproc; EXTERN Node* deferreturn; EXTERN Node* panicindex; EXTERN Node* panicslice; EXTERN Node* throwreturn; -EXTERN vlong unmappedzero; +extern vlong unmappedzero; /* * gen.c @@ -91,7 +87,7 @@ void agen(Node*, Node*); void igen(Node*, Node*, Node*); vlong fieldoffset(Type*, Node*); void bgen(Node*, int, Prog*); -void sgen(Node*, Node*, int32); +void sgen(Node*, Node*, int64); void gmove(Node*, Node*); Prog* gins(int, Node*, Node*); int samaddr(Node*, Node*); @@ -159,3 +155,5 @@ void listinit(void); void zaddr(Biobuf*, Addr*, int, int); +#pragma varargck type "D" Addr* +#pragma varargck type "lD" Addr* diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index a5f278384..02e67d6d4 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -4,6 +4,8 @@ #undef EXTERN #define EXTERN +#include +#include #include "gg.h" #include "opt.h" @@ -24,10 +26,10 @@ markautoused(Prog* p) { for (; p; p = p->link) { if (p->from.type == D_AUTO && p->from.node) - p->from.node->used++; + p->from.node->used = 1; if (p->to.type == D_AUTO && p->to.node) - p->to.node->used++; + p->to.node->used = 1; } } @@ -446,8 +448,8 @@ dodiv(int op, Node *nl, Node *nr, Node *res) { int a, check; Node n3, n4, n5; - Type *t; - Node ax, dx, oldax, olddx; + Type *t, *t0; + Node ax, dx, ax1, n31, oldax, olddx; Prog *p1, *p2, *p3; // Have to be careful about handling @@ -459,6 +461,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res) // For int32 and int64, use explicit test. // Could use int64 hw for int32. t = nl->type; + t0 = t; check = 0; if(issigned[t->etype]) { check = 1; @@ -476,18 +479,28 @@ dodiv(int op, Node *nl, Node *nr, Node *res) } a = optoas(op, t); - regalloc(&n3, t, N); + regalloc(&n3, t0, N); if(nl->ullman >= nr->ullman) { - savex(D_AX, &ax, &oldax, res, t); + savex(D_AX, &ax, &oldax, res, t0); cgen(nl, &ax); - regalloc(&ax, t, &ax); // mark ax live during cgen + regalloc(&ax, t0, &ax); // mark ax live during cgen cgen(nr, &n3); regfree(&ax); } else { cgen(nr, &n3); - savex(D_AX, &ax, &oldax, res, t); + savex(D_AX, &ax, &oldax, res, t0); cgen(nl, &ax); } + if(t != t0) { + // Convert + ax1 = ax; + n31 = n3; + ax.type = t; + n3.type = t; + gmove(&ax1, &ax); + gmove(&n31, &n3); + } + p3 = P; if(check) { nodconst(&n4, t, -1); diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c index ba8a4870e..80de2f750 100644 --- a/src/cmd/6g/gobj.c +++ b/src/cmd/6g/gobj.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include "gg.h" void @@ -278,54 +280,6 @@ dumpfuncs(void) } } -/* deferred DATA output */ -static Prog *strdat; -static Prog *estrdat; -static int gflag; -static Prog *savepc; - -void -data(void) -{ - gflag = debug['g']; - debug['g'] = 0; - - if(estrdat == nil) { - strdat = mal(sizeof(*pc)); - clearp(strdat); - estrdat = strdat; - } - if(savepc) - fatal("data phase error"); - savepc = pc; - pc = estrdat; -} - -void -text(void) -{ - if(!savepc) - fatal("text phase error"); - debug['g'] = gflag; - estrdat = pc; - pc = savepc; - savepc = nil; -} - -void -dumpdata(void) -{ - Prog *p; - - if(estrdat == nil) - return; - *pc = *strdat; - if(gflag) - for(p=pc; p!=estrdat; p=p->link) - print("%P\n", p); - pc = estrdat; -} - int dsname(Sym *s, int off, char *t, int n) { @@ -356,6 +310,7 @@ datastring(char *s, int len, Addr *a) sym = stringsym(s, len); a->type = D_EXTERN; a->sym = sym; + a->node = sym->def; a->offset = widthptr+4; // skip header a->etype = TINT32; } @@ -372,6 +327,7 @@ datagostring(Strlit *sval, Addr *a) sym = stringsym(sval->s, sval->len); a->type = D_EXTERN; a->sym = sym; + a->node = sym->def; a->offset = 0; // header a->etype = TINT32; } @@ -381,6 +337,16 @@ gdata(Node *nam, Node *nr, int wid) { Prog *p; + if(nr->op == OLITERAL) { + switch(nr->val.ctype) { + case CTCPLX: + gdatacomplex(nam, nr->val.u.cval); + return; + case CTSTR: + gdatastring(nam, nr->val.u.sval); + return; + } + } p = gins(ADATA, nam, nr); p->from.scale = wid; } @@ -537,8 +503,10 @@ genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) int c, d, o, mov, add, loaded; Prog *p; Type *f; + + USED(iface); - if(debug['r']) + if(0 && debug['r']) print("genembedtramp %T %T %S\n", rcvr, method, newnam); e = method->sym; @@ -619,7 +587,6 @@ out: // but 6l has a bug, and it can't handle // JMP instructions too close to the top of // a new function. - p = pc; gins(ANOP, N, N); } diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index d0d6d0c96..ededcf673 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include "gg.h" // TODO(rsc): Can make this bigger if we move @@ -46,6 +48,10 @@ clearp(Prog *p) pcloc++; } +static int ddumped; +static Prog *dfirst; +static Prog *dpc; + /* * generate and return proc with p->as = as, * linked into program. pc is next instruction. @@ -55,10 +61,22 @@ prog(int as) { Prog *p; - p = pc; - pc = mal(sizeof(*pc)); - - clearp(pc); + if(as == ADATA || as == AGLOBL) { + if(ddumped) + fatal("already dumped data"); + if(dpc == nil) { + dpc = mal(sizeof(*dpc)); + dfirst = dpc; + } + p = dpc; + dpc = mal(sizeof(*dpc)); + p->link = dpc; + } else { + p = pc; + pc = mal(sizeof(*pc)); + clearp(pc); + p->link = pc; + } if(lineno == 0) { if(debug['K']) @@ -67,10 +85,21 @@ prog(int as) p->as = as; p->lineno = lineno; - p->link = pc; return p; } +void +dumpdata(void) +{ + ddumped = 1; + if(dfirst == nil) + return; + newplist(); + *pc = *dfirst; + pc = dpc; + clearp(pc); +} + /* * generate a branch. * t is ignored. @@ -79,6 +108,8 @@ Prog* gbranch(int as, Type *t) { Prog *p; + + USED(t); p = prog(as); p->to.type = D_BRANCH; @@ -200,6 +231,8 @@ ggloblnod(Node *nam, int32 width) p->to.offset = width; if(nam->readonly) p->from.scale = RODATA; + if(nam->type != T && !haspointers(nam->type)) + p->from.scale |= NOPTR; } void @@ -256,8 +289,6 @@ static int resvd[] = D_CX, // for shift D_DX, // for divide D_SP, // for stack - D_R14, // reserved for m - D_R15, // reserved for u }; void @@ -309,6 +340,8 @@ anyregalloc(void) return 0; } +static uintptr regpc[D_R15+1 - D_AX]; + /* * allocate register of type t, leave in n. * if o != N, o is desired fixed register. @@ -341,11 +374,15 @@ regalloc(Node *n, Type *t, Node *o) goto out; } for(i=D_AX; i<=D_R15; i++) - if(reg[i] == 0) + if(reg[i] == 0) { + regpc[i-D_AX] = (uintptr)getcallerpc(&n); goto out; + } - yyerror("out of fixed registers"); - goto err; + flusherrors(); + for(i=0; i+D_AX<=D_R15; i++) + print("%d %p\n", i, regpc[i]); + fatal("out of fixed registers"); case TFLOAT32: case TFLOAT64: @@ -357,18 +394,14 @@ regalloc(Node *n, Type *t, Node *o) for(i=D_X0; i<=D_X7; i++) if(reg[i] == 0) goto out; - yyerror("out of floating registers"); - goto err; + fatal("out of floating registers"); case TCOMPLEX64: case TCOMPLEX128: tempname(n, t); return; } - yyerror("regalloc: unknown type %T", t); - -err: - nodreg(n, t, 0); + fatal("regalloc: unknown type %T", t); return; out: @@ -388,11 +421,13 @@ regfree(Node *n) i = n->val.u.reg; if(i == D_SP) return; - if(i < 0 || i >= sizeof(reg)) + if(i < 0 || i >= nelem(reg)) fatal("regfree: reg out of range"); if(reg[i] <= 0) fatal("regfree: reg not allocated"); reg[i]--; + if(reg[i] == 0 && D_AX <= i && i <= D_R15) + regpc[i - D_AX] = 0; } /* @@ -450,12 +485,20 @@ nodarg(Type *t, int fp) n = nod(ONAME, N, N); n->type = t->type; n->sym = t->sym; + if(t->width == BADWIDTH) fatal("nodarg: offset not computed for %T", t); n->xoffset = t->width; n->addable = 1; + n->orig = t->nname; fp: + // Rewrite argument named _ to __, + // or else the assignment to _ will be + // discarded during code generation. + if(isblank(n)) + n->sym = lookup("__"); + switch(fp) { case 0: // output arg n->op = OINDREG; @@ -522,6 +565,7 @@ int ismem(Node *n) { switch(n->op) { + case OITAB: case OLEN: case OCAP: case OINDREG: @@ -1088,6 +1132,7 @@ naddr(Node *n, Addr *a, int canemitcode) a->offset = n->xoffset; a->sym = n->left->sym; a->type = D_PARAM; + a->node = n->left->orig; break; case ONAME: @@ -1098,9 +1143,11 @@ naddr(Node *n, Addr *a, int canemitcode) a->width = n->type->width; a->gotype = ngotype(n); } - a->pun = n->pun; a->offset = n->xoffset; a->sym = n->sym; + a->node = n->orig; + //if(a->node >= (Node*)&n) + // fatal("stack node"); if(a->sym == S) a->sym = lookup(".noname"); if(n->method) { @@ -1118,8 +1165,6 @@ naddr(Node *n, Addr *a, int canemitcode) break; case PAUTO: a->type = D_AUTO; - if (n->sym) - a->node = n->orig; break; case PPARAM: case PPARAMOUT: @@ -1142,6 +1187,7 @@ naddr(Node *n, Addr *a, int canemitcode) a->dval = mpgetflt(n->val.u.fval); break; case CTINT: + case CTRUNE: a->sym = S; a->type = D_CONST; a->offset = mpgetfix(n->val.u.xval); @@ -1176,6 +1222,17 @@ naddr(Node *n, Addr *a, int canemitcode) break; } fatal("naddr: OADDR\n"); + + case OITAB: + // itable of interface value + naddr(n->left, a, canemitcode); + if(a->type == D_CONST && a->offset == 0) + break; // itab(nil) + a->etype = tptr; + a->width = widthptr; + if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) + checkoffset(a, canemitcode); + break; case OLEN: // len of string or slice @@ -1845,7 +1902,7 @@ sudoaddable(int as, Node *n, Addr *a) switch(n->op) { case OLITERAL: - if(n->val.ctype != CTINT) + if(!isconst(n, CTINT)) break; v = mpgetfix(n->val.u.xval); if(v >= 32000 || v <= -32000) diff --git a/src/cmd/6g/list.c b/src/cmd/6g/list.c index c8077c97a..ad63f7d29 100644 --- a/src/cmd/6g/list.c +++ b/src/cmd/6g/list.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include "gg.h" static int sconsize; @@ -131,7 +133,7 @@ Dconv(Fmt *fp) if(fp->flags & FmtLong) { d1 = a->offset & 0xffffffffLL; d2 = (a->offset>>32) & 0xffffffffLL; - snprint(str, sizeof(str), "$%ud-%ud", (ulong)d1, (ulong)d2); + snprint(str, sizeof(str), "$%lud-%lud", (ulong)d1, (ulong)d2); break; } snprint(str, sizeof(str), "$%lld", a->offset); diff --git a/src/cmd/6g/peep.c b/src/cmd/6g/peep.c index 4432203f2..3710033b2 100644 --- a/src/cmd/6g/peep.c +++ b/src/cmd/6g/peep.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include "gg.h" #include "opt.h" @@ -985,13 +987,12 @@ loop: case 3: // set if(p->as == p0->as) if(p->from.type == p0->from.type) - if(p->from.sym == p0->from.sym) + 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.index == p0->from.index) { excise(r); - t++; goto loop; } break; diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c index 4d4263047..bed9f8da6 100644 --- a/src/cmd/6g/reg.c +++ b/src/cmd/6g/reg.c @@ -28,9 +28,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include "gg.h" -#undef EXTERN -#define EXTERN #include "opt.h" #define NREGVAR 32 /* 16 general + 16 floating */ @@ -89,8 +89,8 @@ setoutvar(void) ovar.b[z] |= bit.b[z]; t = structnext(&save); } -//if(bany(b)) -//print("ovars = %Q\n", &ovar); +//if(bany(&ovar)) +//print("ovars = %Q\n", ovar); } static void @@ -98,19 +98,19 @@ setaddrs(Bits bit) { int i, n; Var *v; - Sym *s; + Node *node; while(bany(&bit)) { // convert each bit to a variable i = bnum(bit); - s = var[i].sym; + node = var[i].node; n = var[i].name; bit.b[i/32] &= ~(1L<<(i%32)); // disable all pieces of that variable for(i=0; isym == s && v->name == n) + if(v->node == node && v->name == n) v->addr = 2; } } @@ -151,6 +151,8 @@ static char* regname[] = { ".X15", }; +static void fixjmp(Prog*); + void regopt(Prog *firstp) { @@ -166,6 +168,8 @@ regopt(Prog *firstp) first = 0; } + fixjmp(firstp); + // count instructions nr = 0; for(p=firstp; p!=P; p=p->link) @@ -179,7 +183,6 @@ regopt(Prog *firstp) r1 = R; firstr = R; lastr = R; - nvar = 0; /* * control flow is more complicated in generated go code @@ -189,7 +192,7 @@ regopt(Prog *firstp) nvar = NREGVAR; memset(var, 0, NREGVAR*sizeof var[0]); for(i=0; ito; - a->sym = v->sym; 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; // need to clean this up with wptr and // some of the defaults @@ -933,7 +936,7 @@ mkvar(Reg *r, Adr *a) uint32 regu; int32 o; Bits bit; - Sym *s; + Node *node; /* * mark registers used @@ -969,10 +972,14 @@ mkvar(Reg *r, Adr *a) n = t; break; } - s = a->sym; - if(s == S) + + node = a->node; + if(node == N || node->op != ONAME || node->orig == N) goto none; - if(s->name[0] == '.') + node = node->orig; + if(node->orig != node) + fatal("%D: bad node", a); + if(node->sym == S || node->sym->name[0] == '.') goto none; et = a->etype; o = a->offset; @@ -981,7 +988,7 @@ mkvar(Reg *r, Adr *a) flag = 0; for(i=0; isym == s && v->name == n) { + if(v->node == node && v->name == n) { if(v->offset == o) if(v->etype == et) if(v->width == w) @@ -995,11 +1002,6 @@ mkvar(Reg *r, Adr *a) } } } - if(a->pun) { -// print("disable pun %s\n", s->name); - flag = 1; - - } switch(et) { case 0: case TFUNC: @@ -1007,25 +1009,24 @@ mkvar(Reg *r, Adr *a) } if(nvar >= NVAR) { - if(debug['w'] > 1 && s) - fatal("variable not optimized: %D", a); + if(debug['w'] > 1 && node != N) + fatal("variable not optimized: %#N", node); goto none; } i = nvar; nvar++; v = var+i; - v->sym = s; v->offset = o; v->name = n; v->gotype = a->gotype; v->etype = et; v->width = w; v->addr = flag; // funny punning - v->node = a->node; + v->node = node; if(debug['R']) - print("bit=%2d et=%2d w=%d %S %D\n", i, et, w, s, a); + print("bit=%2d et=%2d w=%d %#N %D\n", i, et, w, node, a); ostats.nvar++; bit = blsh(i); @@ -1084,6 +1085,13 @@ prop(Reg *r, Bits ref, Bits cal) ref.b[z] = 0; } break; + + default: + // Work around for issue 1304: + // flush modified globals before each instruction. + for(z=0; zset.b[z]) | @@ -1220,10 +1228,12 @@ loopit(Reg *r, int32 nr) r1 = rpo2r[i]; me = r1->rpo; d = -1; - if(r1->p1 != R && r1->p1->rpo < me) + // rpo2r[r->rpo] == r protects against considering dead code, + // which has r->rpo == 0. + if(r1->p1 != R && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me) d = r1->p1->rpo; for(r1 = r1->p2; r1 != nil; r1 = r1->p2link) - if(r1->rpo < me) + if(rpo2r[r1->rpo] == r1 && r1->rpo < me) d = rpolca(idom, d, r1->rpo); idom[i] = d; } @@ -1628,7 +1638,7 @@ dumpone(Reg *r) if(bany(&r->refahead)) print(" ra:%Q ", r->refahead); if(bany(&r->calbehind)) - print("cb:%Q ", r->calbehind); + print(" cb:%Q ", r->calbehind); if(bany(&r->calahead)) print(" ca:%Q ", r->calahead); if(bany(&r->regdiff)) @@ -1688,3 +1698,123 @@ noreturn(Prog *p) return 1; return 0; } + +/* + * 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. + */ + +/* what instruction does a JMP to p eventually land on? */ +static Prog* +chasejmp(Prog *p, int *jmploop) +{ + int n; + + n = 0; + while(p != P && p->as == AJMP && p->to.type == D_BRANCH) { + if(++n > 10) { + *jmploop = 1; + break; + } + p = p->to.branch; + } + return p; +} + +/* + * reuse reg pointer for mark/sweep state. + * leave reg==nil at end because alive==nil. + */ +#define alive ((void*)0) +#define dead ((void*)1) + +/* mark all code reachable from firstp as alive */ +static void +mark(Prog *firstp) +{ + Prog *p; + + for(p=firstp; p; p=p->link) { + 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))) + break; + } +} + +static void +fixjmp(Prog *firstp) +{ + int jmploop; + Prog *p, *last; + + if(debug['R'] && debug['v']) + print("\nfixjmp\n"); + + // pass 1: resolve jump to AJMP, mark all code as dead. + jmploop = 0; + 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(debug['R'] && debug['v']) + print("->%P\n", p); + } + p->reg = dead; + } + if(debug['R'] && debug['v']) + print("\n"); + + // pass 2: mark all reachable code alive + mark(firstp); + + // pass 3: delete dead code (mostly JMPs). + last = nil; + for(p=firstp; p; p=p->link) { + if(p->reg == dead) { + if(p->link == P && p->as == ARET && last && last->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 %P\n", p); + continue; + } + } + if(last) + last->link = p; + last = p; + } + last->link = P; + + // pass 4: elide JMP to next instruction. + // only safe if there are no jumps to JMPs anymore. + 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(debug['R'] && debug['v']) + print("del %P\n", p); + continue; + } + if(last) + last->link = p; + last = p; + } + last->link = P; + } + + if(debug['R'] && debug['v']) { + print("\n"); + for(p=firstp; p; p=p->link) + print("%P\n", p); + print("\n"); + } +} diff --git a/src/cmd/6l/6.out.h b/src/cmd/6l/6.out.h index 262da02ab..849915954 100644 --- a/src/cmd/6l/6.out.h +++ b/src/cmd/6l/6.out.h @@ -34,6 +34,7 @@ #define DUPOK (1<<1) #define NOSPLIT (1<<2) #define RODATA (1<<3) +#define NOPTR (1<<4) /* * amd64 @@ -121,7 +122,7 @@ enum as AIRETW, AJCC, AJCS, - AJCXZ, + AJCXZL, AJEQ, AJGE, AJGT, @@ -487,6 +488,7 @@ enum as AIDIVQ, AIMULQ, AIRETQ, + AJCXZQ, ALEAQ, ALEAVEQ, ALODSQ, @@ -733,6 +735,7 @@ enum as AMODE, ACRC32B, ACRC32Q, + AIMUL3Q, ALAST }; diff --git a/src/cmd/6l/Makefile b/src/cmd/6l/Makefile index 8ed3e1411..3f528d751 100644 --- a/src/cmd/6l/Makefile +++ b/src/cmd/6l/Makefile @@ -1,48 +1,5 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) - -TARG=6l - -OFILES=\ - asm.$O\ - data.$O\ - dwarf.$O\ - elf.$O\ - enam.$O\ - go.$O\ - ldelf.$O\ - ldmacho.$O\ - ldpe.$O\ - lib.$O\ - list.$O\ - macho.$O\ - obj.$O\ - optab.$O\ - pass.$O\ - pe.$O\ - prof.$O\ - span.$O\ - symtab.$O\ - -HFILES=\ - l.h\ - 6.out.h\ - ../ld/lib.h\ - ../ld/elf.h\ - ../ld/macho.h\ - ../ld/dwarf.h\ - ../ld/pe.h\ - -include ../../Make.ccmd - -enam.c: 6.out.h - sh mkenam - -CLEANFILES+=enam.c - -%.$O: ../ld/%.c - $(HOST_CC) $(HOST_CFLAGS) -c -I. ../ld/$*.c +include ../../Make.dist diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index 3a8223e65..ee31a05cd 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -37,13 +37,12 @@ #include "../ld/macho.h" #include "../ld/pe.h" -#define Dbufslop 100 - #define PADDR(a) ((uint32)(a) & ~0x80000000) char linuxdynld[] = "/lib64/ld-linux-x86-64.so.2"; char freebsddynld[] = "/libexec/ld-elf.so.1"; char openbsddynld[] = "/usr/libexec/ld.so"; +char netbsddynld[] = "/libexec/ld.elf_so"; char zeroes[32]; @@ -95,6 +94,9 @@ enum { ElfStrPlt, ElfStrGnuVersion, ElfStrGnuVersionR, + ElfStrNoteNetbsdIdent, + ElfStrNoPtrData, + ElfStrNoPtrBss, NElfStr }; @@ -112,6 +114,7 @@ needlib(char *name) /* reuse hash code in symbol table */ p = smprint(".elfload.%s", name); s = lookup(p, 0); + free(p); if(s->type == 0) { s->type = 100; // avoid SDATA, etc. return 1; @@ -558,7 +561,7 @@ doelf(void) { Sym *s, *shstrtab, *dynstr; - if(HEADTYPE != Hlinux && HEADTYPE != Hfreebsd && HEADTYPE != Hopenbsd) + if(HEADTYPE != Hlinux && HEADTYPE != Hfreebsd && HEADTYPE != Hopenbsd && HEADTYPE != Hnetbsd) return; /* predefine strings we need for section headers */ @@ -568,8 +571,12 @@ doelf(void) 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"); @@ -649,7 +656,7 @@ doelf(void) /* define dynamic elf table */ s = lookup(".dynamic", 0); s->reachable = 1; - s->type = SELFROSECT; + s->type = SELFSECT; // writable /* * .dynamic table @@ -670,6 +677,8 @@ doelf(void) 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. } } @@ -701,7 +710,7 @@ asmb(void) { int32 magic; int a, dynsym; - vlong vl, startva, symo, machlink; + vlong vl, startva, symo, dwarfoff, machlink, resoff; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; @@ -736,8 +745,19 @@ asmb(void) datblk(segdata.vaddr, segdata.filelen); machlink = 0; - if(HEADTYPE == Hdarwin) + if(HEADTYPE == Hdarwin) { + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + + dwarfoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND); + cseek(dwarfoff); + + segdwarf.fileoff = cpos(); + dwarfemitdebugsections(); + segdwarf.filelen = cpos() - segdwarf.fileoff; + machlink = domacholink(); + } switch(HEADTYPE) { default: @@ -750,6 +770,7 @@ asmb(void) break; case Hlinux: case Hfreebsd: + case Hnetbsd: case Hopenbsd: debug['8'] = 1; /* 64-bit addresses */ /* index of elf text section; needed by asmelfsym, double-checked below */ @@ -760,6 +781,8 @@ asmb(void) if(elfverneed) elftextsh += 2; } + if(HEADTYPE == Hnetbsd) + elftextsh += 1; break; case Hwindows: break; @@ -785,6 +808,7 @@ asmb(void) break; case Hlinux: case Hfreebsd: + case Hnetbsd: case Hopenbsd: symo = rnd(HEADR+segtext.len, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); @@ -809,7 +833,6 @@ asmb(void) dwarfemitdebugsections(); } break; - case Hdarwin: case Hwindows: if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); @@ -855,11 +878,13 @@ asmb(void) break; case Hlinux: 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]); @@ -898,12 +923,15 @@ asmb(void) case Hfreebsd: interpreter = freebsddynld; break; + case Hnetbsd: + interpreter = netbsddynld; + break; case Hopenbsd: interpreter = openbsddynld; break; } } - elfinterp(sh, startva, interpreter); + resoff -= elfinterp(sh, startva, resoff, interpreter); ph = newElfPhdr(); ph->type = PT_INTERP; @@ -911,11 +939,24 @@ asmb(void) 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 */ + if(!debug['d']) { /* -d suppresses dynamic loader format */ /* S headers for dynamic linking */ sh = newElfShdr(elfstr[ElfStrGot]); sh->type = SHT_PROGBITS; @@ -1039,7 +1080,7 @@ asmb(void) for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshbits(sect); - if (!debug['s']) { + if(!debug['s']) { sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; sh->off = symo; @@ -1064,6 +1105,8 @@ asmb(void) 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; @@ -1083,8 +1126,10 @@ asmb(void) a += elfwritehdr(); a += elfwritephdrs(); a += elfwriteshdrs(); - cflush(); - if(a+elfwriteinterp() > ELFRESERVE) + a += elfwriteinterp(elfstr[ElfStrInterp]); + if(HEADTYPE == Hnetbsd) + a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]); + if(a > ELFRESERVE) diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); break; case Hwindows: @@ -1125,7 +1170,10 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) switch(s->type&~SSUB) { case SCONST: case SRODATA: + case SSYMTAB: + case SPCLNTAB: case SDATA: + case SNOPTRDATA: case SELFROSECT: case SMACHOGOT: case STYPE: @@ -1138,8 +1186,11 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) 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; diff --git a/src/cmd/6l/doc.go b/src/cmd/6l/doc.go index b8a6013d6..4d94b209b 100644 --- a/src/cmd/6l/doc.go +++ b/src/cmd/6l/doc.go @@ -4,50 +4,10 @@ /* -6l is a modified version of the Plan 9 linker. The original is documented at - - http://plan9.bell-labs.com/magic/man2html/1/2l - -Its target architecture is the x86-64, referred to by these tools as amd64. -It reads files in .6 format generated by 6g, 6c, and 6a and emits -a binary called 6.out by default. - -Major changes include: - - support for ELF and Mach-O binary files - - support for segmented stacks (this feature is implemented here, not in the compilers). - - -Original options are listed in the link above. - -Options new in this version: - --d - Elide the dynamic linking header. With this option, the binary - is statically linked and does not refer to dynld. Without this option - (the default), the binary's contents are identical but it is loaded with dynld. --e - Emit an extra ELF-compatible symbol table useful with tools such as - nm, gdb, and oprofile. This option makes the binary file considerably larger. --Hdarwin - Write Apple Mach-O binaries (default when $GOOS is darwin) --Hlinux - Write Linux ELF binaries (default when $GOOS is linux) --Hfreebsd - Write FreeBSD ELF binaries (default when $GOOS is freebsd) --Hopenbsd - Write OpenBSD ELF binaries (default when $GOOS is openbsd) --Hwindows - Write Windows PE32+ binaries (default when $GOOS is windows) --I interpreter - Set the ELF dynamic linker to use. --L dir1 -L dir2 - Search for libraries (package files) in dir1, dir2, etc. - The default is the single location $GOROOT/pkg/$GOOS_amd64. --r dir1:dir2:... - Set the dynamic linker search path when using ELF. --V - Print the linker version. +6l is the linker for the x86-64. +The $GOARCH for these tools is amd64. +The flags are documented in ../ld/doc.go. */ package documentation diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index b291d5f3d..5f62239a1 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -133,6 +133,7 @@ struct Sym int32 sig; int32 plt; int32 got; + int32 align; // if non-zero, required alignment in bytes Sym* hash; // in hash table Sym* allsym; // in all symbol list Sym* next; // in text or data list @@ -163,7 +164,7 @@ struct Optab short as; uchar* ytab; uchar prefix; - uchar op[20]; + uchar op[22]; }; struct Movtab { diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c index a7ef58db4..692cab7b8 100644 --- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -44,16 +44,17 @@ char* thestring = "amd64"; char* paramspace = "FP"; Header headers[] = { - "plan9x32", Hplan9x32, - "plan9", Hplan9x64, - "elf", Helf, - "darwin", Hdarwin, - "linux", Hlinux, - "freebsd", Hfreebsd, - "openbsd", Hopenbsd, - "windows", Hwindows, - "windowsgui", Hwindows, - 0, 0 + "plan9x32", Hplan9x32, + "plan9", Hplan9x64, + "elf", Helf, + "darwin", Hdarwin, + "linux", Hlinux, + "freebsd", Hfreebsd, + "netbsd", Hnetbsd, + "openbsd", Hopenbsd, + "windows", Hwindows, + "windowsgui", Hwindows, + 0, 0 }; /* @@ -63,6 +64,7 @@ Header headers[] = { * -Hdarwin -Tx -Rx is apple MH-exec * -Hlinux -Tx -Rx is linux elf-exec * -Hfreebsd -Tx -Rx is FreeBSD elf-exec + * -Hnetbsd -Tx -Rx is NetBSD elf-exec * -Hopenbsd -Tx -Rx is OpenBSD elf-exec * -Hwindows -Tx -Rx is MS Windows PE32+ * @@ -80,6 +82,7 @@ void main(int argc, char *argv[]) { int c; + char *name, *val; Binit(&bso, 1, OWRITE); listinit(); @@ -91,6 +94,7 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; + nuxiinit(); ARGBEGIN { default: @@ -130,6 +134,11 @@ main(int argc, char *argv[]) case 'V': print("%cl version %s\n", thechar, getgoversion()); errorexit(); + case 'X': + name = EARGF(usage()); + val = EARGF(usage()); + addstrdata(name, val); + break; } ARGEND if(argc != 1) @@ -183,7 +192,7 @@ main(int argc, char *argv[]) case Hdarwin: /* apple MACH */ /* * OS X system constant - offset from 0(GS) to our TLS. - * Explained in ../../libcgo/darwin_amd64.c. + * Explained in ../../pkg/runtime/cgo/gcc_darwin_amd64.c. */ tlsoffset = 0x8a0; machoinit(); @@ -197,12 +206,13 @@ main(int argc, char *argv[]) break; case Hlinux: /* elf64 executable */ case Hfreebsd: /* freebsd */ + case Hnetbsd: /* netbsd */ case Hopenbsd: /* openbsd */ /* * ELF uses TLS offset negative from FS. * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS). - * Also known to ../../pkg/runtime/linux/amd64/sys.s - * and ../../libcgo/linux_amd64.s. + * Also known to ../../pkg/runtime/sys_linux_amd64.s + * and ../../pkg/runtime/cgo/gcc_linux_amd64.c. */ tlsoffset = -16; elfinit(); @@ -245,7 +255,6 @@ main(int argc, char *argv[]) zprg.mode = 64; pcstr = "%.6llux "; - nuxiinit(); histgen = 0; pc = 0; dtype = 4; @@ -462,7 +471,7 @@ loop: sig = 1729; if(sig != 0){ if(s->sig != 0 && s->sig != sig) - diag("incompatible type signatures" + diag("incompatible type signatures " "%ux(%s) and %ux(%s) for %s", s->sig, s->file, sig, pn, s->name); s->sig = sig; @@ -546,7 +555,7 @@ loop: s->type = SBSS; s->size = 0; } - if(s->type != SBSS && !s->dupok) { + if(s->type != SBSS && s->type != SNOPTRBSS && !s->dupok) { diag("%s: redefinition: %s in %s", pn, s->name, TNAME); s->type = SBSS; @@ -558,6 +567,8 @@ loop: s->dupok = 1; if(p->from.scale & RODATA) s->type = SRODATA; + else if(p->from.scale & NOPTR) + s->type = SNOPTRBSS; goto loop; case ADATA: @@ -589,6 +600,10 @@ loop: case ATEXT: s = p->from.sym; if(s->text != nil) { + if(p->from.scale & DUPOK) { + skip = 1; + goto casdef; + } diag("%s: %s: redefinition", pn, s->name); return; } diff --git a/src/cmd/6l/optab.c b/src/cmd/6l/optab.c index 36806ec4b..5746ded19 100644 --- a/src/cmd/6l/optab.c +++ b/src/cmd/6l/optab.c @@ -200,7 +200,8 @@ uchar ymovq[] = Ymm, Ymr, Zm_r_xm, 1, // MMX MOVD Ymr, Ymm, Zr_m_xm, 1, // MMX MOVD Yxr, Ymr, Zm_r_xm_nr, 2, // MOVDQ2Q - Yxr, Ym, Zr_m_xm_nr, 2, // MOVQ xmm store + Yxm, Yxr, Zm_r_xm_nr, 2, // MOVQ xmm1/m64 -> xmm2 + Yxr, Yxm, Zr_m_xm_nr, 2, // MOVQ xmm1 -> xmm2/m64 Yml, Yxr, Zm_r_xm, 2, // MOVD xmm load Yxr, Yml, Zr_m_xm, 2, // MOVD xmm store Yiauto, Yrl, Zaut_r, 2, // built-in LEAQ @@ -266,6 +267,11 @@ uchar yimul[] = Yml, Yrl, Zm_r, 2, 0 }; +uchar yimul3[] = +{ + Yml, Yrl, Zibm_r, 1, + 0 +}; uchar ybyte[] = { Yi64, Ynone, Zbyte, 1, @@ -771,6 +777,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 }, { AINB, yin, Pb, 0xe4,0xec }, { AINCB, yincb, Pb, 0xfe,(00) }, { AINCL, yincl, Px, 0xff,(00) }, @@ -788,7 +795,8 @@ Optab optab[] = { AIRETW, ynone, Pe, 0xcf }, { AJCC, yjcond, Px, 0x73,0x83,(00) }, { AJCS, yjcond, Px, 0x72,0x82 }, - { AJCXZ, yloop, Px, 0xe3 }, + { AJCXZL, yloop, Px, 0xe3 }, + { AJCXZQ, yloop, Px, 0xe3 }, { AJEQ, yjcond, Px, 0x74,0x84 }, { AJGE, yjcond, Px, 0x7d,0x8d }, { AJGT, yjcond, Px, 0x7f,0x8f }, @@ -861,7 +869,7 @@ Optab optab[] = { AMOVNTPD, yxr_ml, Pe, 0x2b }, { AMOVNTPS, yxr_ml, Pm, 0x2b }, { AMOVNTQ, ymr_ml, Pm, 0xe7 }, - { AMOVQ, ymovq, Pw, 0x89,0x8b,0x31,0xc7,(00),0xb8,0xc7,(00),0x6f,0x7f,0x6e,0x7e,Pf2,0xd6,Pe,0xd6,Pe,0x6e,Pe,0x7e }, + { AMOVQ, ymovq, Pw, 0x89, 0x8b, 0x31, 0xc7,(00), 0xb8, 0xc7,(00), 0x6f, 0x7f, 0x6e, 0x7e, Pf2,0xd6, Pf3,0x7e, Pe,0xd6, Pe,0x6e, Pe,0x7e }, { AMOVQOZX, ymrxr, Pf3, 0xd6,0x7e }, { AMOVSB, ynone, Pb, 0xa4 }, { AMOVSD, yxmov, Pf2, 0x10,0x11 }, @@ -977,7 +985,7 @@ Optab optab[] = { APSHUFW, ymshuf, Pm, 0x70 }, { 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,0x7e,(06) }, + { APSLLQ, yps, Py, 0xf3, 0x73,(06), Pe,0xf3, Pe,0x73,(06) }, { APSLLW, yps, Py, 0xf1, 0x71,(06), Pe,0xf1, Pe,0x71,(06) }, { APSRAL, yps, Py, 0xe2, 0x72,(04), Pe,0xe2, Pe,0x72,(04) }, { APSRAW, yps, Py, 0xe1, 0x71,(04), Pe,0xe1, Pe,0x71,(04) }, diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c index d9e0b2fc1..c9b477627 100644 --- a/src/cmd/6l/pass.c +++ b/src/cmd/6l/pass.c @@ -276,7 +276,7 @@ patch(void) // Convert // op n(GS), reg // to - // MOVL 0x58(GS), reg + // MOVL 0x28(GS), reg // op n(reg), reg // The purpose of this patch is to fix some accesses // to extern register variables (TLS) on Windows, as @@ -291,11 +291,11 @@ patch(void) q->as = p->as; p->as = AMOVQ; p->from.type = D_INDIR+D_GS; - p->from.offset = 0x58; + p->from.offset = 0x28; } } if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd - || HEADTYPE == Hopenbsd) { + || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd) { // ELF uses FS instead of GS. if(p->from.type == D_INDIR+D_GS) p->from.type = D_INDIR+D_FS; @@ -421,18 +421,18 @@ dostkoff(void) p = appendp(p); // load g into CX p->as = AMOVQ; if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd - || HEADTYPE == Hopenbsd) // ELF uses FS + || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd) // ELF uses FS p->from.type = D_INDIR+D_FS; else p->from.type = D_INDIR+D_GS; p->from.offset = tlsoffset+0; p->to.type = D_CX; if(HEADTYPE == Hwindows) { - // movq %gs:0x58, %rcx + // movq %gs:0x28, %rcx // movq (%rcx), %rcx p->as = AMOVQ; p->from.type = D_INDIR+D_GS; - p->from.offset = 0x58; + p->from.offset = 0x28; p->to.type = D_CX; @@ -501,10 +501,17 @@ dostkoff(void) q = p; } - /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */ + // If we ask for more stack, we'll get a minimum of StackMin bytes. + // We need a stack frame large enough to hold the top-of-stack data, + // the function arguments+results, our caller's PC, our frame, + // a word for the return PC of the next call, and then the StackLimit bytes + // that must be available on entry to any function called from a function + // that did a stack check. If StackMin is enough, don't ask for a specific + // amount: then we can use the custom functions and save a few + // instructions. moreconst1 = 0; - if(autoffset+160+textarg > 4096) - moreconst1 = (autoffset+160) & ~7LL; + if(StackTop + textarg + PtrSize + autoffset + PtrSize + StackLimit >= StackMin) + moreconst1 = autoffset; moreconst2 = textarg; // 4 varieties varieties (const1==0 cross const2==0) diff --git a/src/cmd/6l/span.c b/src/cmd/6l/span.c index 5d13ad44b..28eb38f40 100644 --- a/src/cmd/6l/span.c +++ b/src/cmd/6l/span.c @@ -88,7 +88,10 @@ span1(Sym *s) loop++; q->back ^= 2; } - s->p[q->pc+1] = v; + if(q->as == AJCXZL) + s->p[q->pc+2] = v; + else + s->p[q->pc+1] = v; } else { bp = s->p + q->pc + q->mark - 4; *bp++ = v; @@ -263,10 +266,6 @@ instinit(void) ycover[Ym*Ymax + Ymm] = 1; ycover[Ymr*Ymax + Ymm] = 1; - ycover[Yax*Ymax + Yxm] = 1; - ycover[Ycx*Ymax + Yxm] = 1; - ycover[Yrx*Ymax + Yxm] = 1; - ycover[Yrl*Ymax + Yxm] = 1; ycover[Ym*Ymax + Yxm] = 1; ycover[Yxr*Ymax + Yxm] = 1; @@ -1439,10 +1438,11 @@ found: case Zbr: case Zjmp: + case Zloop: // TODO: jump across functions needs reloc q = p->pcond; if(q == nil) { - diag("jmp/branch without target"); + diag("jmp/branch/loop without target"); errorexit(); } if(q->as == ATEXT) { @@ -1466,8 +1466,12 @@ found: if(p->back & 1) { v = q->pc - (p->pc + 2); if(v >= -128) { + if(p->as == AJCXZL) + *andptr++ = 0x67; *andptr++ = op; *andptr++ = v; + } else if(t[2] == Zloop) { + diag("loop too far: %P", p); } else { v -= 5-2; if(t[2] == Zbr) { @@ -1487,8 +1491,12 @@ found: p->forwd = q->comefrom; q->comefrom = p; if(p->back & 2) { // short + if(p->as == AJCXZL) + *andptr++ = 0x67; *andptr++ = op; *andptr++ = 0; + } else if(t[2] == Zloop) { + diag("loop too far: %P", p); } else { if(t[2] == Zbr) *andptr++ = 0x0f; @@ -1520,19 +1528,6 @@ found: */ break; - case Zloop: - q = p->pcond; - if(q == nil) { - diag("loop without target"); - errorexit(); - } - v = q->pc - p->pc - 2; - if(v < -128 && v > 127) - diag("loop too far: %P", p); - *andptr++ = op; - *andptr++ = v; - break; - case Zbyte: v = vaddr(&p->from, &rel); if(rel.siz != 0) { diff --git a/src/cmd/8a/Makefile b/src/cmd/8a/Makefile index 78d361dbd..27290ddd7 100644 --- a/src/cmd/8a/Makefile +++ b/src/cmd/8a/Makefile @@ -1,25 +1,10 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) +include ../../Make.dist -TARG=8a +install: y.tab.h -HFILES=\ - a.h\ - y.tab.h\ - ../8l/8.out.h\ - -OFILES=\ - y.tab.$O\ - lex.$O\ - ../8l/enam.$O\ - -YFILES=\ - a.y\ - -include ../../Make.ccmd - -lex.$O: ../cc/macbody ../cc/lexbody +y.tab.h: a.y + LANG=C LANGUAGE=en_US.UTF8 bison -d -v -y a.y diff --git a/src/cmd/8a/a.y b/src/cmd/8a/a.y index a8ac773da..f1881808f 100644 --- a/src/cmd/8a/a.y +++ b/src/cmd/8a/a.y @@ -210,6 +210,13 @@ spec3: /* JMP/CALL */ $$.from = nullgen; $$.to = $1; } +| '*' nam + { + $$.from = nullgen; + $$.to = $2; + $$.to.index = $2.type; + $$.to.type = D_INDIR+D_ADDR; + } spec4: /* NOP */ nonnon @@ -385,6 +392,12 @@ imm: $$.type = D_FCONST; $$.dval = $3; } +| '$' '(' '-' LFCONST ')' + { + $$ = nullgen; + $$.type = D_FCONST; + $$.dval = -$4; + } | '$' '-' LFCONST { $$ = nullgen; diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c index e56460e4b..1cc6e591d 100644 --- a/src/cmd/8a/lex.c +++ b/src/cmd/8a/lex.c @@ -44,7 +44,11 @@ enum int systemtype(int sys) { +#ifdef _WIN32 + return sys&Windows; +#else return sys&Plan9; +#endif } int @@ -309,8 +313,8 @@ struct "IDIVL", LTYPE2, AIDIVL, "IDIVW", LTYPE2, AIDIVW, "IMULB", LTYPE2, AIMULB, - "IMULL", LTYPE2, AIMULL, - "IMULW", LTYPE2, AIMULW, + "IMULL", LTYPEI, AIMULL, + "IMULW", LTYPEI, AIMULW, "INB", LTYPE0, AINB, "INL", LTYPE0, AINL, "INW", LTYPE0, AINW, @@ -371,7 +375,8 @@ struct "JG", LTYPER, AJGT, /* alternate */ "JNLE", LTYPER, AJGT, /* alternate */ - "JCXZ", LTYPER, AJCXZ, + "JCXZL", LTYPER, AJCXZL, + "JCXZW", LTYPER, AJCXZW, "JMP", LTYPEC, AJMP, "LAHF", LTYPE0, ALAHF, "LARL", LTYPE3, ALARL, @@ -440,6 +445,7 @@ struct "RCRB", LTYPE3, ARCRB, "RCRL", LTYPE3, ARCRL, "RCRW", LTYPE3, ARCRW, + "RDTSC", LTYPE0, ARDTSC, "REP", LTYPE0, AREP, "REPN", LTYPE0, AREPN, "RET", LTYPE0, ARET, @@ -657,6 +663,10 @@ struct "FXTRACT", LTYPE0, AFXTRACT, "FYL2X", LTYPE0, AFYL2X, "FYL2XP1", LTYPE0, AFYL2XP1, + "LFENCE", LTYPE0, ALFENCE, + "MFENCE", LTYPE0, AMFENCE, + "SFENCE", LTYPE0, ASFENCE, + "EMMS", LTYPE0, AEMMS, 0 }; @@ -911,17 +921,13 @@ outhist(void) for(h = hist; h != H; h = h->link) { p = h->name; op = 0; - /* on windows skip drive specifier in pathname */ if(systemtype(Windows) && p && p[1] == ':'){ - p += 2; - c = *p; - } - if(p && p[0] != c && h->offset == 0 && pathname){ - /* on windows skip drive specifier in pathname */ + c = p[2]; + } else if(p && p[0] != c && h->offset == 0 && pathname){ if(systemtype(Windows) && pathname[1] == ':') { op = p; - p = pathname+2; - c = *p; + p = pathname; + c = p[2]; } else if(pathname[0] == c){ op = p; p = pathname; diff --git a/src/cmd/8a/y.tab.c b/src/cmd/8a/y.tab.c new file mode 100644 index 000000000..ccd3a2563 --- /dev/null +++ b/src/cmd/8a/y.tab.c @@ -0,0 +1,2852 @@ + +/* A Bison parser, made by GNU Bison 2.4.1. */ + +/* 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. + + 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + 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. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.4.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* 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 189 of yacc.c */ +#line 31 "a.y" + +#include +#include /* if we don't, bison will, and a.h re-#defines getc */ +#include +#include "a.h" + + +/* Line 189 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 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + LTYPE0 = 258, + LTYPE1 = 259, + LTYPE2 = 260, + LTYPE3 = 261, + LTYPE4 = 262, + LTYPEC = 263, + LTYPED = 264, + LTYPEN = 265, + LTYPER = 266, + LTYPET = 267, + LTYPES = 268, + 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 + }; +#endif +/* Tokens. */ +#define LTYPE0 258 +#define LTYPE1 259 +#define LTYPE2 260 +#define LTYPE3 261 +#define LTYPE4 262 +#define LTYPEC 263 +#define LTYPED 264 +#define LTYPEN 265 +#define LTYPER 266 +#define LTYPET 267 +#define LTYPES 268 +#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 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 214 of yacc.c */ +#line 37 "a.y" + + Sym *sym; + int32 lval; + struct { + int32 v1; + int32 v2; + } con2; + double dval; + char sval[8]; + Gen gen; + Gen2 gen2; + + + +/* Line 214 of yacc.c */ +#line 192 "y.tab.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 264 of yacc.c */ +#line 204 "y.tab.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# 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 _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((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 + +/* 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 + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 483 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 49 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 37 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 124 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 244 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 285 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +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, 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, 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, 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, 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, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 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 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +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 +}; + +/* 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 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +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 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +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", + "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 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 124, 94, 38, 60, 62, 43, 45, + 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 +}; +# 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 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +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 +}; + +/* 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_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 +}; + +/* 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 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -107 +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 +}; + +/* 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 +}; + +/* 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 -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 +}; + +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 +}; + +/* 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 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* 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. */ + +#define YYFAIL goto yyerrlab + +#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 \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#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. */ + +#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 +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# 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]; + + 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; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + 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. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + +/* 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; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*-------------------------. +| yyparse or yypush_parse. | +`-------------------------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + + int yystate; + /* 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; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* 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. */ + + /* 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; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + 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 + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 3: + +/* Line 1455 of yacc.c */ +#line 70 "a.y" + { + stmtline = lineno; + } + break; + + case 5: + +/* Line 1455 of yacc.c */ +#line 77 "a.y" + { + if((yyvsp[(1) - (2)].sym)->value != pc) + yyerror("redeclaration of %s", (yyvsp[(1) - (2)].sym)->name); + (yyvsp[(1) - (2)].sym)->value = pc; + } + break; + + case 7: + +/* Line 1455 of yacc.c */ +#line 84 "a.y" + { + (yyvsp[(1) - (2)].sym)->type = LLAB; + (yyvsp[(1) - (2)].sym)->value = pc; + } + break; + + case 12: + +/* Line 1455 of yacc.c */ +#line 95 "a.y" + { + (yyvsp[(1) - (3)].sym)->type = LVAR; + (yyvsp[(1) - (3)].sym)->value = (yyvsp[(3) - (3)].lval); + } + break; + + case 13: + +/* Line 1455 of yacc.c */ +#line 100 "a.y" + { + if((yyvsp[(1) - (3)].sym)->value != (yyvsp[(3) - (3)].lval)) + yyerror("redeclaration of %s", (yyvsp[(1) - (3)].sym)->name); + (yyvsp[(1) - (3)].sym)->value = (yyvsp[(3) - (3)].lval); + } + break; + + case 14: + +/* Line 1455 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 106 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 16: + +/* Line 1455 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 108 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 18: + +/* Line 1455 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 110 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 20: + +/* Line 1455 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 112 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 22: + +/* Line 1455 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 114 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 24: + +/* Line 1455 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 116 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 26: + +/* Line 1455 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 118 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; + + case 28: + +/* Line 1455 of yacc.c */ +#line 121 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = nullgen; + } + break; + + case 29: + +/* Line 1455 of yacc.c */ +#line 126 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = nullgen; + } + break; + + case 30: + +/* Line 1455 of yacc.c */ +#line 133 "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" + { + (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" + { + (yyval.gen2).from = (yyvsp[(1) - (2)].gen); + (yyval.gen2).to = nullgen; + } + break; + + case 33: + +/* Line 1455 of yacc.c */ +#line 152 "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" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = (yyvsp[(2) - (2)].gen); + } + break; + + case 35: + +/* Line 1455 of yacc.c */ +#line 164 "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" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = (yyvsp[(2) - (2)].gen); + } + break; + + case 37: + +/* Line 1455 of yacc.c */ +#line 176 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = (yyvsp[(1) - (1)].gen); + } + break; + + case 38: + +/* Line 1455 of yacc.c */ +#line 183 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); + (yyval.gen2).to = (yyvsp[(5) - (5)].gen); + } + break; + + case 39: + +/* Line 1455 of yacc.c */ +#line 191 "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" + { + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); + (yyval.gen2).to = (yyvsp[(5) - (5)].gen); + } + break; + + case 41: + +/* Line 1455 of yacc.c */ +#line 204 "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" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = (yyvsp[(1) - (1)].gen); + } + break; + + case 43: + +/* Line 1455 of yacc.c */ +#line 214 "a.y" + { + (yyval.gen2).from = nullgen; + (yyval.gen2).to = (yyvsp[(2) - (2)].gen); + (yyval.gen2).to.index = (yyvsp[(2) - (2)].gen).type; + (yyval.gen2).to.type = D_INDIR+D_ADDR; + } + break; + + case 46: + +/* Line 1455 of yacc.c */ +#line 227 "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" + { + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).to = (yyvsp[(3) - (5)].gen); + if((yyval.gen2).from.index != D_NONE) + yyerror("dp shift with lhs index"); + (yyval.gen2).from.index = (yyvsp[(5) - (5)].lval); + } + break; + + case 48: + +/* Line 1455 of yacc.c */ +#line 242 "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" + { + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).to = (yyvsp[(3) - (5)].gen); + if((yyval.gen2).to.index != D_NONE) + yyerror("dp move with lhs index"); + (yyval.gen2).to.index = (yyvsp[(5) - (5)].lval); + } + break; + + case 50: + +/* Line 1455 of yacc.c */ +#line 257 "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" + { + (yyval.gen2).from = (yyvsp[(1) - (1)].gen); + (yyval.gen2).to = nullgen; + } + break; + + case 52: + +/* Line 1455 of yacc.c */ +#line 267 "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" + { + (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" + { + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); + (yyval.gen2).to = (yyvsp[(5) - (5)].gen); + } + break; + + case 59: + +/* Line 1455 of yacc.c */ +#line 293 "a.y" + { + (yyval.gen) = (yyvsp[(2) - (2)].gen); + } + break; + + case 60: + +/* Line 1455 of yacc.c */ +#line 297 "a.y" + { + (yyval.gen) = (yyvsp[(2) - (2)].gen); + } + break; + + case 66: + +/* Line 1455 of yacc.c */ +#line 310 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_BRANCH; + (yyval.gen).offset = (yyvsp[(1) - (4)].lval) + pc; + } + break; + + case 67: + +/* Line 1455 of yacc.c */ +#line 316 "a.y" + { + (yyval.gen) = nullgen; + if(pass == 2) + yyerror("undefined label: %s", (yyvsp[(1) - (2)].sym)->name); + (yyval.gen).type = D_BRANCH; + (yyval.gen).sym = (yyvsp[(1) - (2)].sym); + (yyval.gen).offset = (yyvsp[(2) - (2)].lval); + } + break; + + case 68: + +/* Line 1455 of yacc.c */ +#line 325 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_BRANCH; + (yyval.gen).sym = (yyvsp[(1) - (2)].sym); + (yyval.gen).offset = (yyvsp[(1) - (2)].sym)->value + (yyvsp[(2) - (2)].lval); + } + break; + + case 69: + +/* Line 1455 of yacc.c */ +#line 334 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); + } + break; + + case 70: + +/* Line 1455 of yacc.c */ +#line 339 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); + } + break; + + case 71: + +/* Line 1455 of yacc.c */ +#line 344 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); + } + break; + + case 72: + +/* Line 1455 of yacc.c */ +#line 349 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_SP; + } + break; + + case 73: + +/* Line 1455 of yacc.c */ +#line 354 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); + } + break; + + case 74: + +/* Line 1455 of yacc.c */ +#line 361 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_CONST; + (yyval.gen).offset = (yyvsp[(2) - (2)].lval); + } + break; + + case 75: + +/* Line 1455 of yacc.c */ +#line 367 "a.y" + { + (yyval.gen) = (yyvsp[(2) - (2)].gen); + (yyval.gen).index = (yyvsp[(2) - (2)].gen).type; + (yyval.gen).type = D_ADDR; + /* + if($2.type == D_AUTO || $2.type == D_PARAM) + yyerror("constant cannot be automatic: %s", + $2.sym->name); + */ + } + break; + + case 76: + +/* Line 1455 of yacc.c */ +#line 378 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_SCONST; + memcpy((yyval.gen).sval, (yyvsp[(2) - (2)].sval), sizeof((yyval.gen).sval)); + } + break; + + case 77: + +/* Line 1455 of yacc.c */ +#line 384 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_FCONST; + (yyval.gen).dval = (yyvsp[(2) - (2)].dval); + } + break; + + case 78: + +/* Line 1455 of yacc.c */ +#line 390 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_FCONST; + (yyval.gen).dval = (yyvsp[(3) - (4)].dval); + } + break; + + case 79: + +/* Line 1455 of yacc.c */ +#line 396 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_FCONST; + (yyval.gen).dval = -(yyvsp[(4) - (5)].dval); + } + break; + + case 80: + +/* Line 1455 of yacc.c */ +#line 402 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_FCONST; + (yyval.gen).dval = -(yyvsp[(3) - (3)].dval); + } + break; + + case 81: + +/* Line 1455 of yacc.c */ +#line 410 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_CONST2; + (yyval.gen).offset = (yyvsp[(2) - (2)].con2).v1; + (yyval.gen).offset2 = (yyvsp[(2) - (2)].con2).v2; + } + break; + + case 82: + +/* Line 1455 of yacc.c */ +#line 419 "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" + { + (yyval.con2).v1 = -(yyvsp[(2) - (2)].lval); + (yyval.con2).v2 = 0; + } + break; + + case 84: + +/* Line 1455 of yacc.c */ +#line 429 "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" + { + (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" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+D_NONE; + (yyval.gen).offset = (yyvsp[(1) - (1)].lval); + } + break; + + case 89: + +/* Line 1455 of yacc.c */ +#line 451 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); + (yyval.gen).offset = (yyvsp[(1) - (4)].lval); + } + break; + + case 90: + +/* Line 1455 of yacc.c */ +#line 457 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+D_SP; + (yyval.gen).offset = (yyvsp[(1) - (4)].lval); + } + break; + + case 91: + +/* Line 1455 of yacc.c */ +#line 463 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+D_NONE; + (yyval.gen).offset = (yyvsp[(1) - (6)].lval); + (yyval.gen).index = (yyvsp[(3) - (6)].lval); + (yyval.gen).scale = (yyvsp[(5) - (6)].lval); + checkscale((yyval.gen).scale); + } + break; + + case 92: + +/* Line 1455 of yacc.c */ +#line 472 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval); + (yyval.gen).offset = (yyvsp[(1) - (9)].lval); + (yyval.gen).index = (yyvsp[(6) - (9)].lval); + (yyval.gen).scale = (yyvsp[(8) - (9)].lval); + checkscale((yyval.gen).scale); + } + break; + + case 93: + +/* Line 1455 of yacc.c */ +#line 481 "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" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+D_SP; + } + break; + + case 95: + +/* Line 1455 of yacc.c */ +#line 491 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); + (yyval.gen).offset = (yyvsp[(1) - (4)].lval); + } + break; + + case 96: + +/* Line 1455 of yacc.c */ +#line 497 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+D_NONE; + (yyval.gen).index = (yyvsp[(2) - (5)].lval); + (yyval.gen).scale = (yyvsp[(4) - (5)].lval); + checkscale((yyval.gen).scale); + } + break; + + case 97: + +/* Line 1455 of yacc.c */ +#line 505 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval); + (yyval.gen).index = (yyvsp[(5) - (8)].lval); + (yyval.gen).scale = (yyvsp[(7) - (8)].lval); + checkscale((yyval.gen).scale); + } + break; + + case 98: + +/* Line 1455 of yacc.c */ +#line 515 "a.y" + { + (yyval.gen) = (yyvsp[(1) - (1)].gen); + } + break; + + case 99: + +/* Line 1455 of yacc.c */ +#line 519 "a.y" + { + (yyval.gen) = (yyvsp[(1) - (6)].gen); + (yyval.gen).index = (yyvsp[(3) - (6)].lval); + (yyval.gen).scale = (yyvsp[(5) - (6)].lval); + checkscale((yyval.gen).scale); + } + break; + + case 100: + +/* Line 1455 of yacc.c */ +#line 528 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(4) - (5)].lval); + (yyval.gen).sym = (yyvsp[(1) - (5)].sym); + (yyval.gen).offset = (yyvsp[(2) - (5)].lval); + } + break; + + case 101: + +/* Line 1455 of yacc.c */ +#line 535 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_STATIC; + (yyval.gen).sym = (yyvsp[(1) - (7)].sym); + (yyval.gen).offset = (yyvsp[(4) - (7)].lval); + } + break; + + case 102: + +/* Line 1455 of yacc.c */ +#line 543 "a.y" + { + (yyval.lval) = 0; + } + break; + + case 103: + +/* Line 1455 of yacc.c */ +#line 547 "a.y" + { + (yyval.lval) = (yyvsp[(2) - (2)].lval); + } + break; + + case 104: + +/* Line 1455 of yacc.c */ +#line 551 "a.y" + { + (yyval.lval) = -(yyvsp[(2) - (2)].lval); + } + break; + + case 106: + +/* Line 1455 of yacc.c */ +#line 558 "a.y" + { + (yyval.lval) = D_AUTO; + } + break; + + case 109: + +/* Line 1455 of yacc.c */ +#line 566 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; + } + break; + + case 110: + +/* Line 1455 of yacc.c */ +#line 570 "a.y" + { + (yyval.lval) = -(yyvsp[(2) - (2)].lval); + } + break; + + case 111: + +/* Line 1455 of yacc.c */ +#line 574 "a.y" + { + (yyval.lval) = (yyvsp[(2) - (2)].lval); + } + break; + + case 112: + +/* Line 1455 of yacc.c */ +#line 578 "a.y" + { + (yyval.lval) = ~(yyvsp[(2) - (2)].lval); + } + break; + + case 113: + +/* Line 1455 of yacc.c */ +#line 582 "a.y" + { + (yyval.lval) = (yyvsp[(2) - (3)].lval); + } + break; + + case 115: + +/* Line 1455 of yacc.c */ +#line 589 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); + } + break; + + case 116: + +/* Line 1455 of yacc.c */ +#line 593 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); + } + break; + + case 117: + +/* Line 1455 of yacc.c */ +#line 597 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); + } + break; + + case 118: + +/* Line 1455 of yacc.c */ +#line 601 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); + } + break; + + case 119: + +/* Line 1455 of yacc.c */ +#line 605 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); + } + break; + + case 120: + +/* Line 1455 of yacc.c */ +#line 609 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); + } + break; + + case 121: + +/* Line 1455 of yacc.c */ +#line 613 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); + } + break; + + case 122: + +/* Line 1455 of yacc.c */ +#line 617 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); + } + break; + + case 123: + +/* Line 1455 of yacc.c */ +#line 621 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); + } + break; + + case 124: + +/* Line 1455 of yacc.c */ +#line 625 "a.y" + { + (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); + } + break; + + + +/* Line 1455 of yacc.c */ +#line 2643 "y.tab.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++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. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + 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; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined(yyoverflow) || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (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); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + diff --git a/src/cmd/8a/y.tab.h b/src/cmd/8a/y.tab.h new file mode 100644 index 000000000..69a966a4b --- /dev/null +++ b/src/cmd/8a/y.tab.h @@ -0,0 +1,135 @@ + +/* A Bison parser, made by GNU Bison 2.4.1. */ + +/* 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. + + 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + 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 + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + LTYPE0 = 258, + LTYPE1 = 259, + LTYPE2 = 260, + LTYPE3 = 261, + LTYPE4 = 262, + LTYPEC = 263, + LTYPED = 264, + LTYPEN = 265, + LTYPER = 266, + LTYPET = 267, + LTYPES = 268, + 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 + }; +#endif +/* Tokens. */ +#define LTYPE0 258 +#define LTYPE1 259 +#define LTYPE2 260 +#define LTYPE3 261 +#define LTYPE4 262 +#define LTYPEC 263 +#define LTYPED 264 +#define LTYPEN 265 +#define LTYPER 266 +#define LTYPET 267 +#define LTYPES 268 +#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 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 1676 of yacc.c */ +#line 37 "a.y" + + Sym *sym; + int32 lval; + struct { + int32 v1; + int32 v2; + } con2; + double dval; + char sval[8]; + Gen gen; + Gen2 gen2; + + + +/* Line 1676 of yacc.c */ +#line 127 "y.tab.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +extern YYSTYPE yylval; + + diff --git a/src/cmd/8c/Makefile b/src/cmd/8c/Makefile index 60f46d3c9..3f528d751 100644 --- a/src/cmd/8c/Makefile +++ b/src/cmd/8c/Makefile @@ -1,37 +1,5 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) - -TARG=8c - -HFILES=\ - gc.h\ - ../8l/8.out.h\ - ../cc/cc.h\ - -OFILES=\ - cgen.$O\ - cgen64.$O\ - div.$O\ - list.$O\ - machcap.$O\ - mul.$O\ - pgen.$O\ - pswt.$O\ - peep.$O\ - reg.$O\ - sgen.$O\ - swt.$O\ - txt.$O\ - ../8l/enam.$O\ - -LIB=\ - ../cc/cc.a\ - -include ../../Make.ccmd - -%.$O: ../cc/%.c - $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../cc/$*.c +include ../../Make.dist diff --git a/src/cmd/8c/cgen.c b/src/cmd/8c/cgen.c index 7f02bd96e..869d31ace 100644 --- a/src/cmd/8c/cgen.c +++ b/src/cmd/8c/cgen.c @@ -1221,7 +1221,7 @@ void boolgen(Node *n, int true, Node *nn) { int o; - Prog *p1, *p2; + Prog *p1, *p2, *p3; Node *l, *r, nod, nod1; int32 curs; @@ -1346,6 +1346,15 @@ boolgen(Node *n, int true, Node *nn) cgen64(n, Z); goto com; } + if(true && typefd[l->type->etype] && (o == OEQ || o == ONE)) { + // Cannot rewrite !(l == r) into l != r with float64; it breaks NaNs. + // Jump around instead. + boolgen(n, 0, Z); + p1 = p; + gbranch(OGOTO); + patch(p1, pc); + goto com; + } if(true) o = comrel[relindex(o)]; if(l->complex >= FNX && r->complex >= FNX) { @@ -1378,6 +1387,30 @@ boolgen(Node *n, int true, Node *nn) } else fgopcode(o, l, &fregnode0, 0, 1); } + switch(o) { + case OEQ: + // Already emitted AJEQ; want AJEQ and AJPC. + p1 = p; + gbranch(OGOTO); + p2 = p; + patch(p1, pc); + gins(AJPC, Z, Z); + patch(p2, pc); + break; + + case ONE: + // Already emitted AJNE; want AJNE or AJPS. + p1 = p; + gins(AJPS, Z, Z); + p2 = p; + gbranch(OGOTO); + p3 = p; + patch(p1, pc); + patch(p2, pc); + gbranch(OGOTO); + patch(p3, pc); + break; + } goto com; } if(l->op == OCONST) { diff --git a/src/cmd/8c/gc.h b/src/cmd/8c/gc.h index 32b80e995..4a57f5d3c 100644 --- a/src/cmd/8c/gc.h +++ b/src/cmd/8c/gc.h @@ -304,7 +304,8 @@ void gpseudo(int, Sym*, Node*); int swcmp(const void*, const void*); void doswit(Node*); void swit1(C1*, int, int32, Node*); -void cas(void); +void swit2(C1*, int, int32, Node*); +void newcase(void); void bitload(Node*, Node*, Node*, Node*, Node*); void bitstore(Node*, Node*, Node*, Node*, Node*); int32 outstring(char*, int32); diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c index 769ef2c66..f1ca4c25f 100644 --- a/src/cmd/8c/swt.c +++ b/src/cmd/8c/swt.c @@ -32,6 +32,26 @@ void swit1(C1 *q, int nc, int32 def, Node *n) +{ + Node nreg; + + if(typev[n->type->etype]) { + regsalloc(&nreg, n); + nreg.type = types[TVLONG]; + cgen(n, &nreg); + swit2(q, nc, def, &nreg); + return; + } + + regalloc(&nreg, n, Z); + nreg.type = types[TLONG]; + cgen(n, &nreg); + swit2(q, nc, def, &nreg); + regfree(&nreg); +} + +void +swit2(C1 *q, int nc, int32 def, Node *n) { C1 *r; int i; @@ -58,12 +78,12 @@ swit1(C1 *q, int nc, int32 def, Node *n) gbranch(OGOTO); p->as = AJEQ; patch(p, r->label); - swit1(q, i, def, n); + swit2(q, i, def, n); if(debug['W']) print("case < %.8ux\n", r->val); patch(sp, pc); - swit1(r+1, nc-i-1, def, n); + swit2(r+1, nc-i-1, def, n); } void @@ -330,17 +350,13 @@ outhist(Biobuf *b) for(h = hist; h != H; h = h->link) { p = h->name; op = 0; - /* on windows skip drive specifier in pathname */ if(systemtype(Windows) && p && p[1] == ':'){ - p += 2; - c = *p; - } - if(p && p[0] != c && h->offset == 0 && pathname){ - /* on windows skip drive specifier in pathname */ + c = p[2]; + } else if(p && p[0] != c && h->offset == 0 && pathname){ if(systemtype(Windows) && pathname[1] == ':') { op = p; - p = pathname+2; - c = *p; + p = pathname; + c = p[2]; } else if(pathname[0] == c){ op = p; p = pathname; diff --git a/src/cmd/8c/txt.c b/src/cmd/8c/txt.c index b2e0148a0..3a08da7cd 100644 --- a/src/cmd/8c/txt.c +++ b/src/cmd/8c/txt.c @@ -146,7 +146,9 @@ gclean(void) continue; if(s->type == types[TENUM]) continue; + textflag = s->dataflag; gpseudo(AGLOBL, s, nodconst(s->type->width)); + textflag = 0; } nextpc(); p->as = AEND; @@ -372,7 +374,7 @@ regfree(Node *n) if(n->op != OREGISTER && n->op != OINDREG) goto err; i = n->reg; - if(i < 0 || i >= sizeof(reg)) + if(i < 0 || i >= nelem(reg)) goto err; if(reg[i] <= 0) goto err; diff --git a/src/cmd/8g/Makefile b/src/cmd/8g/Makefile index b459782a3..3f528d751 100644 --- a/src/cmd/8g/Makefile +++ b/src/cmd/8g/Makefile @@ -1,36 +1,5 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) - -TARG=8g - -HFILES=\ - ../gc/go.h\ - ../8l/8.out.h\ - gg.h\ - opt.h\ - -OFILES=\ - ../8l/enam.$O\ - cgen.$O\ - cgen64.$O\ - cplx.$O\ - galign.$O\ - ggen.$O\ - gobj.$O\ - gsubr.$O\ - list.$O\ - peep.$O\ - pgen.$O\ - reg.$O\ - -LIB=\ - ../gc/gc.a\ - -include ../../Make.ccmd - -%.$O: ../gc/%.c - $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c +include ../../Make.dist diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c index b316e6e34..48619ac73 100644 --- a/src/cmd/8g/cgen.c +++ b/src/cmd/8g/cgen.c @@ -5,6 +5,8 @@ // TODO(rsc): // assume CLD? +#include +#include #include "gg.h" void @@ -96,6 +98,9 @@ cgen(Node *n, Node *res) if(isslice(n->left->type)) n->addable = n->left->addable; break; + case OITAB: + n->addable = n->left->addable; + break; } // if both are addressable, move @@ -250,6 +255,13 @@ cgen(Node *n, Node *res) regfree(&n1); break; + case OITAB: + igen(nl, &n1, res); + n1.type = ptrto(types[TUINTPTR]); + gmove(&n1, res); + regfree(&n1); + break; + case OLEN: if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { // map has len in the first 32-bit word. @@ -680,7 +692,6 @@ agen(Node *n, Node *res) break; case ODOT: - t = nl->type; agen(nl, res); if(n->xoffset != 0) { nodconst(&n1, types[tptr], n->xoffset); @@ -786,6 +797,7 @@ bgen(Node *n, int true, Prog *to) int et, a; Node *nl, *nr, *r; Node n1, n2, tmp, t1, t2, ax; + NodeList *ll; Prog *p1, *p2; if(debug['g']) { @@ -798,9 +810,6 @@ bgen(Node *n, int true, Prog *to) if(n->ninit != nil) genlist(n->ninit); - nl = n->left; - nr = n->right; - if(n->type == T) { convlit(&n, types[TBOOL]); if(n->type == T) @@ -813,7 +822,6 @@ bgen(Node *n, int true, Prog *to) patch(gins(AEND, N, N), to); return; } - nl = N; nr = N; switch(n->op) { @@ -905,7 +913,10 @@ bgen(Node *n, int true, Prog *to) 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; @@ -1129,24 +1140,29 @@ stkof(Node *n) * memmove(&res, &n, w); */ void -sgen(Node *n, Node *res, int32 w) +sgen(Node *n, Node *res, int64 w) { Node dst, src, tdst, tsrc; int32 c, q, odst, osrc; if(debug['g']) { - print("\nsgen w=%d\n", w); + print("\nsgen w=%lld\n", w); dump("r", n); dump("res", res); } - if(w == 0) - return; - if(n->ullman >= UINF && res->ullman >= UINF) { + if(n->ullman >= UINF && res->ullman >= UINF) fatal("sgen UINF"); - } - if(w < 0) - fatal("sgen copy %d", w); + if(w < 0 || (int32)w != w) + fatal("sgen copy %lld", w); + + if(w == 0) { + // evaluate side effects only. + tempname(&tdst, types[tptr]); + agen(res, &tdst); + agen(n, &tdst); + return; + } // offset on the stack osrc = stkof(n); diff --git a/src/cmd/8g/cgen64.c b/src/cmd/8g/cgen64.c index ba99cec74..8e568a0f9 100644 --- a/src/cmd/8g/cgen64.c +++ b/src/cmd/8g/cgen64.c @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include +#include #include "gg.h" /* diff --git a/src/cmd/8g/doc.go b/src/cmd/8g/doc.go index 2d9ff9a42..6d678eac8 100644 --- a/src/cmd/8g/doc.go +++ b/src/cmd/8g/doc.go @@ -9,7 +9,5 @@ The $GOARCH for these tools is 386. It reads .go files and outputs .8 files. The flags are documented in ../gc/doc.go. -There is no instruction optimizer, so the -N flag is a no-op. - */ package documentation diff --git a/src/cmd/8g/galign.c b/src/cmd/8g/galign.c index 7734603c4..4526a2efb 100644 --- a/src/cmd/8g/galign.c +++ b/src/cmd/8g/galign.c @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include +#include #include "gg.h" int thechar = '8'; diff --git a/src/cmd/8g/gg.h b/src/cmd/8g/gg.h index 9f7a66a29..0a4f0ad2d 100644 --- a/src/cmd/8g/gg.h +++ b/src/cmd/8g/gg.h @@ -2,16 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include -#include - -#include "../gc/go.h" -#include "../8l/8.out.h" - #ifndef EXTERN #define EXTERN extern #endif +#include "../gc/go.h" +#include "../8l/8.out.h" + typedef struct Addr Addr; struct Addr @@ -46,6 +43,8 @@ struct Prog void* reg; // pointer to containing Reg struct }; +#define TEXTFLAG from.scale + // foptoas flags enum { @@ -54,15 +53,12 @@ enum Fpop2 = 1<<2, }; -EXTERN Biobuf* bout; EXTERN int32 dynloc; EXTERN uchar reg[D_NONE]; EXTERN int32 pcloc; // instruction counter EXTERN Strlit emptystring; extern char* anames[]; -EXTERN Hist* hist; EXTERN Prog zprog; -EXTERN Node* curfn; EXTERN Node* newproc; EXTERN Node* deferproc; EXTERN Node* deferreturn; @@ -103,7 +99,7 @@ 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*, int32); +void sgen(Node*, Node*, int64); void gmove(Node*, Node*); Prog* gins(int, Node*, Node*); int samaddr(Node*, Node*); @@ -167,12 +163,6 @@ void complexmove(Node*, Node*); void complexgen(Node*, Node*); void complexbool(int, Node*, Node*, int, Prog*); -/* - * gobj.c - */ -void data(void); -void text(void); - /* * list.c */ @@ -185,3 +175,5 @@ void listinit(void); void zaddr(Biobuf*, Addr*, int, int); +#pragma varargck type "D" Addr* +#pragma varargck type "lD" Addr* diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index 108c493aa..6a4570199 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -4,6 +4,8 @@ #undef EXTERN #define EXTERN +#include +#include #include "gg.h" #include "opt.h" @@ -26,10 +28,10 @@ markautoused(Prog* p) { for (; p; p = p->link) { if (p->from.type == D_AUTO && p->from.node) - p->from.node->used++; + p->from.node->used = 1; if (p->to.type == D_AUTO && p->to.node) - p->to.node->used++; + p->to.node->used = 1; } } @@ -429,7 +431,6 @@ hard: if(nr->ullman >= nl->ullman || nl->addable) { mgen(nr, &n2, N); nr = &n2; - nr = &n2; } else { tempname(&n2, nr->type); cgen(nr, &n2); @@ -483,8 +484,8 @@ void dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx) { int check; - Node n1, t1, t2, n4, nz; - Type *t; + Node n1, t1, t2, t3, t4, n4, nz; + Type *t, *t0; Prog *p1, *p2, *p3; // Have to be careful about handling @@ -496,6 +497,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx) // For int32 and int64, use explicit test. // Could use int64 hw for int32. t = nl->type; + t0 = t; check = 0; if(issigned[t->etype]) { check = 1; @@ -514,8 +516,18 @@ dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx) tempname(&t1, t); tempname(&t2, t); - cgen(nl, &t1); - cgen(nr, &t2); + if(t0 != t) { + tempname(&t3, t0); + tempname(&t4, t0); + cgen(nl, &t3); + cgen(nr, &t4); + // Convert. + gmove(&t3, &t1); + gmove(&t4, &t2); + } else { + cgen(nl, &t1); + cgen(nr, &t2); + } if(!samereg(ax, res) && !samereg(dx, res)) regalloc(&n1, t, res); diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c index 31c42a3f2..d8c8f5ab9 100644 --- a/src/cmd/8g/gobj.c +++ b/src/cmd/8g/gobj.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include "gg.h" void @@ -276,54 +278,6 @@ dumpfuncs(void) } } -/* deferred DATA output */ -static Prog *strdat; -static Prog *estrdat; -static int gflag; -static Prog *savepc; - -void -data(void) -{ - gflag = debug['g']; - debug['g'] = 0; - - if(estrdat == nil) { - strdat = mal(sizeof(*pc)); - clearp(strdat); - estrdat = strdat; - } - if(savepc) - fatal("data phase error"); - savepc = pc; - pc = estrdat; -} - -void -text(void) -{ - if(!savepc) - fatal("text phase error"); - debug['g'] = gflag; - estrdat = pc; - pc = savepc; - savepc = nil; -} - -void -dumpdata(void) -{ - Prog *p; - - if(estrdat == nil) - return; - *pc = *strdat; - if(gflag) - for(p=pc; p!=estrdat; p=p->link) - print("%P\n", p); - pc = estrdat; -} - int dsname(Sym *s, int off, char *t, int n) { @@ -354,6 +308,7 @@ datastring(char *s, int len, Addr *a) sym = stringsym(s, len); a->type = D_EXTERN; a->sym = sym; + a->node = sym->def; a->offset = widthptr+4; // skip header a->etype = TINT32; } @@ -370,6 +325,7 @@ datagostring(Strlit *sval, Addr *a) sym = stringsym(sval->s, sval->len); a->type = D_EXTERN; a->sym = sym; + a->node = sym->def; a->offset = 0; // header a->etype = TINT32; } @@ -380,6 +336,17 @@ gdata(Node *nam, Node *nr, int wid) Prog *p; vlong v; + if(nr->op == OLITERAL) { + switch(nr->val.ctype) { + case CTCPLX: + gdatacomplex(nam, nr->val.u.cval); + return; + case CTSTR: + gdatastring(nam, nr->val.u.sval); + return; + } + } + if(wid == 8 && is64(nr->type)) { v = mpgetfix(nr->val.u.xval); p = gins(ADATA, nam, nodintconst(v)); @@ -547,6 +514,8 @@ genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) Prog *p; Type *f; + USED(iface); + e = method->sym; for(d=0; d +#include #include "gg.h" // TODO(rsc): Can make this bigger if we move @@ -48,6 +50,10 @@ clearp(Prog *p) pcloc++; } +static int ddumped; +static Prog *dfirst; +static Prog *dpc; + /* * generate and return proc with p->as = as, * linked into program. pc is next instruction. @@ -57,10 +63,22 @@ prog(int as) { Prog *p; - p = pc; - pc = mal(sizeof(*pc)); - - clearp(pc); + if(as == ADATA || as == AGLOBL) { + if(ddumped) + fatal("already dumped data"); + if(dpc == nil) { + dpc = mal(sizeof(*dpc)); + dfirst = dpc; + } + p = dpc; + dpc = mal(sizeof(*dpc)); + p->link = dpc; + } else { + p = pc; + pc = mal(sizeof(*pc)); + clearp(pc); + p->link = pc; + } if(lineno == 0) { if(debug['K']) @@ -69,10 +87,21 @@ prog(int as) p->as = as; p->lineno = lineno; - p->link = pc; return p; } +void +dumpdata(void) +{ + ddumped = 1; + if(dfirst == nil) + return; + newplist(); + *pc = *dfirst; + pc = dpc; + clearp(pc); +} + /* * generate a branch. * t is ignored. @@ -82,6 +111,7 @@ gbranch(int as, Type *t) { Prog *p; + USED(t); p = prog(as); p->to.type = D_BRANCH; p->to.branch = P; @@ -202,6 +232,8 @@ ggloblnod(Node *nam, int32 width) p->to.offset = width; if(nam->readonly) p->from.scale = RODATA; + if(nam->type != T && !haspointers(nam->type)) + p->from.scale |= NOPTR; } void @@ -753,7 +785,7 @@ ginit(void) reg[resvd[i]]++; } -ulong regpc[D_NONE]; +uintptr regpc[D_NONE]; void gclean(void) @@ -822,7 +854,7 @@ regalloc(Node *n, Type *t, Node *o) fprint(2, "registers allocated at\n"); for(i=D_AX; i<=D_DI; i++) - fprint(2, "\t%R\t%#ux\n", i, regpc[i]); + fprint(2, "\t%R\t%#lux\n", i, regpc[i]); yyerror("out of fixed registers"); goto err; @@ -832,7 +864,6 @@ regalloc(Node *n, Type *t, Node *o) goto out; } yyerror("regalloc: unknown type %T", t); - i = 0; err: nodreg(n, t, 0); @@ -842,7 +873,7 @@ out: if (i == D_SP) print("alloc SP\n"); if(reg[i] == 0) { - regpc[i] = (ulong)__builtin_return_address(0); + regpc[i] = (uintptr)getcallerpc(&n); if(i == D_AX || i == D_CX || i == D_DX || i == D_SP) { dump("regalloc-o", o); fatal("regalloc %R", i); @@ -864,7 +895,7 @@ regfree(Node *n) i = n->val.u.reg; if(i == D_SP) return; - if(i < 0 || i >= sizeof(reg)) + if(i < 0 || i >= nelem(reg)) fatal("regfree: reg out of range"); if(reg[i] <= 0) fatal("regfree: reg not allocated"); @@ -935,8 +966,15 @@ nodarg(Type *t, int fp) fatal("nodarg: offset not computed for %T", t); n->xoffset = t->width; n->addable = 1; + n->orig = t->nname; break; } + + // Rewrite argument named _ to __, + // or else the assignment to _ will be + // discarded during code generation. + if(isblank(n)) + n->sym = lookup("__"); switch(fp) { default: @@ -1005,6 +1043,7 @@ int ismem(Node *n) { switch(n->op) { + case OITAB: case OLEN: case OCAP: case OINDREG: @@ -1123,6 +1162,7 @@ memname(Node *n, Type *t) strcpy(namebuf, n->sym->name); namebuf[0] = '.'; // keep optimizer from registerizing n->sym = lookup(namebuf); + n->orig->sym = n->sym; } void @@ -1799,6 +1839,7 @@ naddr(Node *n, Addr *a, int canemitcode) a->offset = n->xoffset; a->sym = n->left->sym; a->type = D_PARAM; + a->node = n->left->orig; break; case ONAME: @@ -1809,9 +1850,11 @@ naddr(Node *n, Addr *a, int canemitcode) a->width = n->type->width; a->gotype = ngotype(n); } - a->pun = n->pun; a->offset = n->xoffset; a->sym = n->sym; + a->node = n->orig; + //if(a->node >= (Node*)&n) + // fatal("stack node"); if(a->sym == S) a->sym = lookup(".noname"); if(n->method) { @@ -1829,8 +1872,6 @@ naddr(Node *n, Addr *a, int canemitcode) break; case PAUTO: a->type = D_AUTO; - if (n->sym) - a->node = n->orig; break; case PPARAM: case PPARAMOUT: @@ -1853,6 +1894,7 @@ naddr(Node *n, Addr *a, int canemitcode) a->dval = mpgetflt(n->val.u.fval); break; case CTINT: + case CTRUNE: a->sym = S; a->type = D_CONST; a->offset = mpgetfix(n->val.u.xval); @@ -1887,6 +1929,17 @@ naddr(Node *n, Addr *a, int canemitcode) break; } fatal("naddr: OADDR\n"); + + case OITAB: + // itable of interface value + naddr(n->left, a, canemitcode); + if(a->type == D_CONST && a->offset == 0) + break; // len(nil) + a->etype = tptr; + a->width = widthptr; + if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) + checkoffset(a, canemitcode); + break; case OLEN: // len of string or slice @@ -1955,5 +2008,9 @@ sudoclean(void) int sudoaddable(int as, Node *n, Addr *a) { + USED(as); + USED(n); + USED(a); + return 0; } diff --git a/src/cmd/8g/list.c b/src/cmd/8g/list.c index edb1ece84..88d3d5f7e 100644 --- a/src/cmd/8g/list.c +++ b/src/cmd/8g/list.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include "gg.h" static int sconsize; @@ -128,7 +130,7 @@ Dconv(Fmt *fp) if(fp->flags & FmtLong) { d1 = a->offset; d2 = a->offset2; - snprint(str, sizeof(str), "$%ud-%ud", (ulong)d1, (ulong)d2); + snprint(str, sizeof(str), "$%lud-%lud", (ulong)d1, (ulong)d2); break; } snprint(str, sizeof(str), "$%d", a->offset); diff --git a/src/cmd/8g/opt.h b/src/cmd/8g/opt.h index 8f31dec3b..ed6eb15ab 100644 --- a/src/cmd/8g/opt.h +++ b/src/cmd/8g/opt.h @@ -162,3 +162,5 @@ int32 RtoB(int); int32 FtoB(int); int BtoR(int32); int BtoF(int32); + +#pragma varargck type "D" Adr* diff --git a/src/cmd/8g/peep.c b/src/cmd/8g/peep.c index 5ad29e1b2..b8a2825e5 100644 --- a/src/cmd/8g/peep.c +++ b/src/cmd/8g/peep.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include "gg.h" #include "opt.h" @@ -876,13 +878,12 @@ loop: case 3: // set if(p->as == p0->as) if(p->from.type == p0->from.type) - if(p->from.sym == p0->from.sym) + 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.index == p0->from.index) { excise(r); - t++; goto loop; } break; diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c index 2b878f62a..29270c820 100644 --- a/src/cmd/8g/reg.c +++ b/src/cmd/8g/reg.c @@ -28,9 +28,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include +#include #include "gg.h" -#undef EXTERN -#define EXTERN #include "opt.h" #define NREGVAR 8 @@ -39,6 +39,8 @@ static int first = 1; +static void fixjmp(Prog*); + Reg* rega(void) { @@ -89,8 +91,8 @@ setoutvar(void) ovar.b[z] |= bit.b[z]; t = structnext(&save); } -//if(bany(b)) -//print("ovars = %Q\n", &ovar); +//if(bany(ovar)) +//print("ovars = %Q\n", ovar); } static void @@ -98,19 +100,19 @@ setaddrs(Bits bit) { int i, n; Var *v; - Sym *s; + Node *node; while(bany(&bit)) { // convert each bit to a variable i = bnum(bit); - s = var[i].sym; + node = var[i].node; n = var[i].name; bit.b[i/32] &= ~(1L<<(i%32)); // disable all pieces of that variable for(i=0; isym == s && v->name == n) + if(v->node == node && v->name == n) v->addr = 2; } } @@ -132,6 +134,8 @@ regopt(Prog *firstp) exregoffset = D_DI; // no externals first = 0; } + + fixjmp(firstp); // count instructions nr = 0; @@ -155,7 +159,7 @@ regopt(Prog *firstp) nvar = NREGVAR; memset(var, 0, NREGVAR*sizeof var[0]); for(i=0; ito; - a->sym = v->sym; 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; // need to clean this up with wptr and // some of the defaults p1->as = AMOVL; switch(v->etype) { default: - fatal("unknown type\n"); + fatal("unknown type %E", v->etype); case TINT8: case TUINT8: case TBOOL: @@ -810,7 +817,7 @@ mkvar(Reg *r, Adr *a) int i, t, n, et, z, w, flag, regu; int32 o; Bits bit; - Sym *s; + Node *node; /* * mark registers used @@ -847,10 +854,13 @@ mkvar(Reg *r, Adr *a) break; } - s = a->sym; - if(s == S) + node = a->node; + if(node == N || node->op != ONAME || node->orig == N) goto none; - if(s->name[0] == '.') + node = node->orig; + if(node->orig != node) + fatal("%D: bad node", a); + if(node->sym == S || node->sym->name[0] == '.') goto none; et = a->etype; o = a->offset; @@ -859,7 +869,7 @@ mkvar(Reg *r, Adr *a) flag = 0; for(i=0; isym == s && v->name == n) { + if(v->node == node && v->name == n) { if(v->offset == o) if(v->etype == et) if(v->width == w) @@ -868,7 +878,7 @@ mkvar(Reg *r, Adr *a) // if they overlap, disable both if(overlap(v->offset, v->width, o, w)) { if(debug['R']) - print("disable %s\n", v->sym->name); + print("disable %s\n", node->sym->name); v->addr = 1; flag = 1; } @@ -882,7 +892,7 @@ mkvar(Reg *r, Adr *a) } if(nvar >= NVAR) { - if(debug['w'] > 1 && s) + if(debug['w'] > 1 && node != N) fatal("variable not optimized: %D", a); goto none; } @@ -890,17 +900,16 @@ mkvar(Reg *r, Adr *a) i = nvar; nvar++; v = var+i; - v->sym = s; v->offset = o; v->name = n; v->gotype = a->gotype; v->etype = et; v->width = w; v->addr = flag; // funny punning - v->node = a->node; + v->node = node; if(debug['R']) - print("bit=%2d et=%2d w=%d+%d %S %D flag=%d\n", i, et, o, w, s, a, v->addr); + print("bit=%2d et=%2d w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); ostats.nvar++; bit = blsh(i); @@ -959,6 +968,13 @@ prop(Reg *r, Bits ref, Bits cal) ref.b[z] = 0; } break; + + default: + // Work around for issue 1304: + // flush modified globals before each instruction. + for(z=0; zset.b[z]) | @@ -1095,10 +1111,12 @@ loopit(Reg *r, int32 nr) r1 = rpo2r[i]; me = r1->rpo; d = -1; - if(r1->p1 != R && r1->p1->rpo < me) + // rpo2r[r->rpo] == r protects against considering dead code, + // which has r->rpo == 0. + if(r1->p1 != R && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me) d = r1->p1->rpo; for(r1 = r1->p2; r1 != nil; r1 = r1->p2link) - if(r1->rpo < me) + if(rpo2r[r1->rpo] == r1 && r1->rpo < me) d = rpolca(idom, d, r1->rpo); idom[i] = d; } @@ -1482,7 +1500,7 @@ dumpone(Reg *r) if(bany(&r->refahead)) print(" ra:%Q ", r->refahead); if(bany(&r->calbehind)) - print("cb:%Q ", r->calbehind); + print(" cb:%Q ", r->calbehind); if(bany(&r->calahead)) print(" ca:%Q ", r->calahead); if(bany(&r->regdiff)) @@ -1542,3 +1560,123 @@ noreturn(Prog *p) return 1; return 0; } + +/* + * 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. + */ + +/* what instruction does a JMP to p eventually land on? */ +static Prog* +chasejmp(Prog *p, int *jmploop) +{ + int n; + + n = 0; + while(p != P && p->as == AJMP && p->to.type == D_BRANCH) { + if(++n > 10) { + *jmploop = 1; + break; + } + p = p->to.branch; + } + return p; +} + +/* + * reuse reg pointer for mark/sweep state. + * leave reg==nil at end because alive==nil. + */ +#define alive ((void*)0) +#define dead ((void*)1) + +/* mark all code reachable from firstp as alive */ +static void +mark(Prog *firstp) +{ + Prog *p; + + for(p=firstp; p; p=p->link) { + 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))) + break; + } +} + +static void +fixjmp(Prog *firstp) +{ + int jmploop; + Prog *p, *last; + + if(debug['R'] && debug['v']) + print("\nfixjmp\n"); + + // pass 1: resolve jump to AJMP, mark all code as dead. + jmploop = 0; + 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(debug['R'] && debug['v']) + print("->%P\n", p); + } + p->reg = dead; + } + if(debug['R'] && debug['v']) + print("\n"); + + // pass 2: mark all reachable code alive + mark(firstp); + + // pass 3: delete dead code (mostly JMPs). + last = nil; + for(p=firstp; p; p=p->link) { + if(p->reg == dead) { + if(p->link == P && p->as == ARET && last && last->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 %P\n", p); + continue; + } + } + if(last) + last->link = p; + last = p; + } + last->link = P; + + // pass 4: elide JMP to next instruction. + // only safe if there are no jumps to JMPs anymore. + 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(debug['R'] && debug['v']) + print("del %P\n", p); + continue; + } + if(last) + last->link = p; + last = p; + } + last->link = P; + } + + if(debug['R'] && debug['v']) { + print("\n"); + for(p=firstp; p; p=p->link) + print("%P\n", p); + print("\n"); + } +} diff --git a/src/cmd/8l/8.out.h b/src/cmd/8l/8.out.h index 9a8483aaf..9d2751cf0 100644 --- a/src/cmd/8l/8.out.h +++ b/src/cmd/8l/8.out.h @@ -34,6 +34,7 @@ #define DUPOK (1<<1) #define NOSPLIT (1<<2) #define RODATA (1<<3) +#define NOPTR (1<<4) enum as { @@ -115,7 +116,8 @@ enum as AIRETW, AJCC, AJCS, - AJCXZ, + AJCXZL, + AJCXZW, AJEQ, AJGE, AJGT, @@ -394,7 +396,9 @@ enum as ACMPXCHGL, ACMPXCHGW, ACMPXCHG8B, - + + ARDTSC, + AXADDB, AXADDL, AXADDW, @@ -442,6 +446,12 @@ enum as AFCMOVNU, AFCMOVUN, + ALFENCE, + AMFENCE, + ASFENCE, + + AEMMS, + ALAST }; diff --git a/src/cmd/8l/Makefile b/src/cmd/8l/Makefile index 7d34e1704..3f528d751 100644 --- a/src/cmd/8l/Makefile +++ b/src/cmd/8l/Makefile @@ -1,49 +1,5 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) - -TARG=8l - -OFILES=\ - asm.$O\ - data.$O\ - dwarf.$O\ - elf.$O\ - enam.$O\ - go.$O\ - ldelf.$O\ - ldmacho.$O\ - ldpe.$O\ - lib.$O\ - list.$O\ - macho.$O\ - obj.$O\ - optab.$O\ - pass.$O\ - pe.$O\ - prof.$O\ - span.$O\ - symtab.$O\ - - -HFILES=\ - l.h\ - 8.out.h\ - ../ld/dwarf.h\ - ../ld/elf.h\ - ../ld/macho.h\ - ../ld/pe.h\ - -include ../../Make.ccmd - -enam.c: 8.out.h - sh mkenam - -CLEANFILES+=enam.c - - -%.$O: ../ld/%.c - $(HOST_CC) $(HOST_CFLAGS) -c -I. ../ld/$*.c +include ../../Make.dist diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index 22abd8049..25ffc786f 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -37,10 +37,10 @@ #include "../ld/macho.h" #include "../ld/pe.h" -#define Dbufslop 100 - char linuxdynld[] = "/lib/ld-linux.so.2"; char freebsddynld[] = "/usr/libexec/ld-elf.so.1"; +char openbsddynld[] = "/usr/libexec/ld.so"; +char netbsddynld[] = "/usr/libexec/ld.elf_so"; int32 entryvalue(void) @@ -90,6 +90,9 @@ enum { ElfStrPlt, ElfStrGnuVersion, ElfStrGnuVersionR, + ElfStrNoteNetbsdIdent, + ElfStrNoPtrData, + ElfStrNoPtrBss, NElfStr }; @@ -107,6 +110,7 @@ needlib(char *name) /* reuse hash code in symbol table */ p = smprint(".dynlib.%s", name); s = lookup(p, 0); + free(p); if(s->type == 0) { s->type = 100; // avoid SDATA, etc. return 1; @@ -416,7 +420,7 @@ adddynsym(Sym *s) return; if(s->dynimpname == nil) - diag("adddynsym: no dynamic name for %s", s->name, *(int32*)0); + diag("adddynsym: no dynamic name for %s", s->name); if(iself) { s->dynid = nelfsym++; @@ -524,8 +528,12 @@ doelf(void) 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"); @@ -606,7 +614,7 @@ doelf(void) /* define dynamic elf table */ s = lookup(".dynamic", 0); s->reachable = 1; - s->type = SELFROSECT; + s->type = SELFSECT; // writable /* * .dynamic table @@ -626,6 +634,8 @@ doelf(void) 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. } } @@ -657,7 +667,7 @@ asmb(void) { int32 v, magic; int a, dynsym; - uint32 symo, startva, machlink; + uint32 symo, startva, dwarfoff, machlink, resoff; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; @@ -688,8 +698,19 @@ asmb(void) datblk(segdata.vaddr, segdata.filelen); machlink = 0; - if(HEADTYPE == Hdarwin) + if(HEADTYPE == Hdarwin) { + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + + dwarfoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND); + cseek(dwarfoff); + + segdwarf.fileoff = cpos(); + dwarfemitdebugsections(); + segdwarf.filelen = cpos() - segdwarf.fileoff; + machlink = domacholink(); + } if(iself) { /* index of elf text section; needed by asmelfsym, double-checked below */ @@ -700,6 +721,8 @@ asmb(void) if(elfverneed) elftextsh += 2; } + if(HEADTYPE == Hnetbsd) + elftextsh += 1; } symsize = 0; @@ -746,7 +769,7 @@ asmb(void) default: if(iself) { if(debug['v']) - Bprint(&bso, "%5.2f elfsym\n", cputime()); + Bprint(&bso, "%5.2f elfsym\n", cputime()); asmelfsym(); cflush(); cwrite(elfstrdat, elfstrsize); @@ -769,7 +792,6 @@ asmb(void) cflush(); } break; - case Hdarwin: case Hwindows: if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); @@ -918,6 +940,7 @@ asmb(void) Elfput: eh = getElfEhdr(); startva = INITTEXT - HEADR; + resoff = ELFRESERVE; /* This null SHdr must appear before all others */ newElfShdr(elfstr[ElfStrEmpty]); @@ -956,9 +979,15 @@ asmb(void) case Hfreebsd: interpreter = freebsddynld; break; + case Hnetbsd: + interpreter = netbsddynld; + break; + case Hopenbsd: + interpreter = openbsddynld; + break; } } - elfinterp(sh, startva, interpreter); + resoff -= elfinterp(sh, startva, resoff, interpreter); ph = newElfPhdr(); ph->type = PT_INTERP; @@ -966,11 +995,27 @@ asmb(void) 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 */ + if(!debug['d']) { /* -d suppresses dynamic loader format */ /* S headers for dynamic linking */ sh = newElfShdr(elfstr[ElfStrGot]); sh->type = SHT_PROGBITS; @@ -1094,7 +1139,7 @@ asmb(void) for(sect=segdata.sect; sect!=nil; sect=sect->next) elfshbits(sect); - if (!debug['s']) { + if(!debug['s']) { sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; sh->off = symo; @@ -1122,7 +1167,13 @@ asmb(void) eh->ident[EI_VERSION] = EV_CURRENT; switch(HEADTYPE) { case Hfreebsd: - eh->ident[EI_OSABI] = 9; + 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; } @@ -1141,8 +1192,10 @@ asmb(void) a += elfwritehdr(); a += elfwritephdrs(); a += elfwriteshdrs(); - cflush(); - if(a+elfwriteinterp() > ELFRESERVE) + a += elfwriteinterp(elfstr[ElfStrInterp]); + if(HEADTYPE == Hnetbsd) + a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]); + if(a > ELFRESERVE) diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); break; @@ -1205,12 +1258,16 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) 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); diff --git a/src/cmd/8l/doc.go b/src/cmd/8l/doc.go index b70888907..12301d4f2 100644 --- a/src/cmd/8l/doc.go +++ b/src/cmd/8l/doc.go @@ -4,47 +4,10 @@ /* -8l is a modified version of the Plan 9 linker. The original is documented at - - http://plan9.bell-labs.com/magic/man2html/1/2l - -Its target architecture is the x86, referred to by these tools for historical reasons as 386. -It reads files in .8 format generated by 8g, 8c, and 8a and emits -a binary called 8.out by default. - -Major changes include: - - support for ELF and Mach-O binary files - - support for segmented stacks (this feature is implemented here, not in the compilers). - - -Original options are listed in the link above. - -Options new in this version: - --d - Elide the dynamic linking header. With this option, the binary - is statically linked and does not refer to dynld. Without this option - (the default), the binary's contents are identical but it is loaded with dynld. --Hplan9 - Write Plan 9 32-bit format binaries (default when $GOOS is plan9) --Hdarwin - Write Apple Mach-O binaries (default when $GOOS is darwin) --Hlinux - Write Linux ELF binaries (default when $GOOS is linux) --Hfreebsd - Write FreeBSD ELF binaries (default when $GOOS is freebsd) --Hwindows - Write Windows PE32 binaries (default when $GOOS is windows) --I interpreter - Set the ELF dynamic linker to use. --L dir1 -L dir2 - Search for libraries (package files) in dir1, dir2, etc. - The default is the single location $GOROOT/pkg/$GOOS_386. --r dir1:dir2:... - Set the dynamic linker search path when using ELF. --V - Print the linker version. +8l is the linker for the 32-bit x86. +The $GOARCH for these tools is 386. +The flags are documented in ../ld/doc.go. */ package documentation diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index 4ee0db967..b974f464b 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -134,6 +134,7 @@ struct Sym int32 dynid; int32 plt; int32 got; + int32 align; // if non-zero, required alignment in bytes Sym* hash; // in hash table Sym* allsym; // in all symbol list Sym* next; // in text or data list @@ -209,6 +210,7 @@ enum Zbr, Zcall, Zcallcon, + Zcallind, Zib_, Zib_rp, Zibo_m, diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index a8e1c34a5..af4bc844f 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -47,17 +47,19 @@ char *noname = ""; char *thestring = "386"; Header headers[] = { - "garbunix", Hgarbunix, - "unixcoff", Hunixcoff, - "plan9", Hplan9x32, - "msdoscom", Hmsdoscom, - "msdosexe", Hmsdosexe, - "darwin", Hdarwin, - "linux", Hlinux, - "freebsd", Hfreebsd, - "windows", Hwindows, - "windowsgui", Hwindows, - 0, 0 + "garbunix", Hgarbunix, + "unixcoff", Hunixcoff, + "plan9", Hplan9x32, + "msdoscom", Hmsdoscom, + "msdosexe", Hmsdosexe, + "darwin", Hdarwin, + "linux", Hlinux, + "freebsd", Hfreebsd, + "netbsd", Hnetbsd, + "openbsd", Hopenbsd, + "windows", Hwindows, + "windowsgui", Hwindows, + 0, 0 }; /* @@ -69,6 +71,8 @@ Header headers[] = { * -Hdarwin -Tx -Rx is Apple Mach-O * -Hlinux -Tx -Rx is Linux ELF32 * -Hfreebsd -Tx -Rx is FreeBSD ELF32 + * -Hnetbsd -Tx -Rx is NetBSD ELF32 + * -Hopenbsd -Tx -Rx is OpenBSD ELF32 * -Hwindows -Tx -Rx is MS Windows PE32 */ @@ -83,6 +87,7 @@ void main(int argc, char *argv[]) { int c; + char *name, *val; Binit(&bso, 1, OWRITE); listinit(); @@ -94,6 +99,7 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; + nuxiinit(); ARGBEGIN { default: @@ -133,6 +139,11 @@ main(int argc, char *argv[]) case 'V': print("%cl version %s\n", thechar, getgoversion()); errorexit(); + case 'X': + name = EARGF(usage()); + val = EARGF(usage()); + addstrdata(name, val); + break; } ARGEND if(argc != 1) @@ -209,7 +220,7 @@ main(int argc, char *argv[]) case Hdarwin: /* apple MACH */ /* * OS X system constant - offset from %gs to our TLS. - * Explained in ../../libcgo/darwin_386.c. + * Explained in ../../pkg/runtime/cgo/gcc_darwin_386.c. */ tlsoffset = 0x468; machoinit(); @@ -223,11 +234,13 @@ main(int argc, char *argv[]) break; case Hlinux: /* elf32 executable */ case Hfreebsd: + case Hnetbsd: + case Hopenbsd: /* * ELF uses TLS offsets negative from %gs. * Translate 0(GS) and 4(GS) into -8(GS) and -4(GS). - * Also known to ../../pkg/runtime/linux/386/sys.s - * and ../../libcgo/linux_386.c. + * Also known to ../../pkg/runtime/sys_linux_386.s + * and ../../pkg/runtime/cgo/gcc_linux_386.c. */ tlsoffset = -8; elfinit(); @@ -269,7 +282,6 @@ main(int argc, char *argv[]) zprg.to = zprg.from; pcstr = "%.6ux "; - nuxiinit(); histgen = 0; pc = 0; dtype = 4; @@ -477,7 +489,7 @@ loop: sig = 1729; if(sig != 0){ if(s->sig != 0 && s->sig != sig) - diag("incompatible type signatures" + diag("incompatible type signatures " "%ux(%s) and %ux(%s) for %s", s->sig, s->file, sig, pn, s->name); s->sig = sig; @@ -551,7 +563,7 @@ loop: s->type = SBSS; s->size = 0; } - if(s->type != SBSS && !s->dupok) { + if(s->type != SBSS && s->type != SNOPTRBSS && !s->dupok) { diag("%s: redefinition: %s in %s", pn, s->name, TNAME); s->type = SBSS; @@ -563,6 +575,8 @@ loop: s->dupok = 1; if(p->from.scale & RODATA) s->type = SRODATA; + else if(p->from.scale & NOPTR) + s->type = SNOPTRBSS; goto loop; case ADATA: @@ -594,6 +608,10 @@ loop: case ATEXT: s = p->from.sym; if(s->text != nil) { + if(p->from.scale & DUPOK) { + skip = 1; + goto casdef; + } diag("%s: %s: redefinition", pn, s->name); return; } diff --git a/src/cmd/8l/optab.c b/src/cmd/8l/optab.c index f5c195d75..856482290 100644 --- a/src/cmd/8l/optab.c +++ b/src/cmd/8l/optab.c @@ -260,6 +260,7 @@ uchar yloop[] = uchar ycall[] = { Ynone, Yml, Zo_m, 2, + Ynone, Ycol, Zcallind, 2, Ynone, Ybr, Zcall, 0, Ynone, Yi32, Zcallcon, 1, 0 @@ -383,7 +384,7 @@ Optab optab[] = { ABTSL, yml_rl, Pm, 0xab }, { ABTSW, yml_rl, Pq, 0xab }, { ABYTE, ybyte, Px, 1 }, - { ACALL, ycall, Px, 0xff,(02),0xe8 }, + { ACALL, ycall, Px, 0xff,(02),0xff,(0x15),0xe8 }, { ACLC, ynone, Px, 0xf8 }, { ACLD, ynone, Px, 0xfc }, { ACLI, ynone, Px, 0xfa }, @@ -430,7 +431,8 @@ Optab optab[] = { AIRETW, ynone, Pe, 0xcf }, { AJCC, yjcond, Px, 0x73,0x83,(00) }, { AJCS, yjcond, Px, 0x72,0x82 }, - { AJCXZ, yloop, Px, 0xe3 }, + { AJCXZL, yloop, Px, 0xe3 }, + { AJCXZW, yloop, Px, 0xe3 }, { AJEQ, yjcond, Px, 0x74,0x84 }, { AJGE, yjcond, Px, 0x7d,0x8d }, { AJGT, yjcond, Px, 0x7f,0x8f }, @@ -705,6 +707,8 @@ Optab optab[] = { ACMPXCHGW, yrl_ml, Pm, 0xb1 }, { ACMPXCHG8B, yscond, Pm, 0xc7,(01) }, + { ARDTSC, ynone, Pm, 0x31 }, + { AXADDB, yrb_mb, Pb, 0x0f,0xc0 }, { AXADDL, yrl_ml, Pm, 0xc1 }, { AXADDW, yrl_ml, Pe, 0x0f,0xc1 }, @@ -751,5 +755,11 @@ Optab optab[] = { AFCMOVNU, yfcmv, Px, 0xdb,(03) }, { AFCMOVUN, yfcmv, Px, 0xda,(03) }, + { ALFENCE, ynone, Pm, 0xae,0xe8 }, + { AMFENCE, ynone, Pm, 0xae,0xf0 }, + { ASFENCE, ynone, Pm, 0xae,0xf8 }, + + { AEMMS, ynone, Pm, 0x77 }, + 0 }; diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c index 2e0990c5a..9034fdf3a 100644 --- a/src/cmd/8l/pass.c +++ b/src/cmd/8l/pass.c @@ -259,7 +259,7 @@ patch(void) // Convert // op n(GS), reg // to - // MOVL 0x2C(FS), reg + // MOVL 0x14(FS), reg // op n(reg), reg // The purpose of this patch is to fix some accesses // to extern register variables (TLS) on Windows, as @@ -273,7 +273,7 @@ patch(void) q->as = p->as; p->as = AMOVL; p->from.type = D_INDIR+D_FS; - p->from.offset = 0x2C; + p->from.offset = 0x14; } } if(HEADTYPE == Hlinux) { @@ -307,9 +307,12 @@ patch(void) p->from.offset = 0; } } - if(p->as == ACALL || (p->as == AJMP && p->to.type != D_BRANCH)) { + if((p->as == ACALL && p->to.type != D_BRANCH) || (p->as == AJMP && p->to.type != D_BRANCH)) { s = p->to.sym; - if(s) { + if(p->to.type == D_INDIR+D_ADDR) { + /* skip check if this is an indirect call (CALL *symbol(SB)) */ + continue; + } else if(s) { if(debug['c']) Bprint(&bso, "%s calls %s\n", TNAME, s->name); if((s->type&~SSUB) != STEXT) { @@ -421,7 +424,7 @@ dostkoff(void) case Hwindows: p->as = AMOVL; p->from.type = D_INDIR+D_FS; - p->from.offset = 0x2c; + p->from.offset = 0x14; p->to.type = D_CX; p = appendp(p); @@ -524,10 +527,18 @@ dostkoff(void) p = appendp(p); // save frame size in DX p->as = AMOVL; p->to.type = D_DX; - /* 160 comes from 3 calls (3*8) 4 safes (4*8) and 104 guard */ p->from.type = D_CONST; - if(autoffset+160+cursym->text->to.offset2 > 4096) - p->from.offset = (autoffset+160) & ~7LL; + + // If we ask for more stack, we'll get a minimum of StackMin bytes. + // We need a stack frame large enough to hold the top-of-stack data, + // the function arguments+results, our caller's PC, our frame, + // a word for the return PC of the next call, and then the StackLimit bytes + // that must be available on entry to any function called from a function + // that did a stack check. If StackMin is enough, don't ask for a specific + // amount: then we can use the custom functions and save a few + // instructions. + if(StackTop + cursym->text->to.offset2 + PtrSize + autoffset + PtrSize + StackLimit >= StackMin) + p->from.offset = (autoffset+7) & ~7LL; p = appendp(p); // save arg size in AX p->as = AMOVL; diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c index a4cba1257..81c1d37eb 100644 --- a/src/cmd/8l/span.c +++ b/src/cmd/8l/span.c @@ -83,7 +83,10 @@ span1(Sym *s) loop++; q->back ^= 2; } - s->p[q->pc+1] = v; + if(q->as == AJCXZW) + s->p[q->pc+2] = v; + else + s->p[q->pc+1] = v; } else { bp = s->p + q->pc + q->mark - 4; *bp++ = v; @@ -282,6 +285,8 @@ oclass(Adr *a) } return Yxxx; } + //if(a->type == D_INDIR+D_ADDR) + // print("*Ycol\n"); return Ycol; } return Ym; @@ -1056,9 +1061,10 @@ found: case Zbr: case Zjmp: + case Zloop: q = p->pcond; if(q == nil) { - diag("jmp/branch without target"); + diag("jmp/branch/loop without target"); errorexit(); } if(q->as == ATEXT) { @@ -1084,8 +1090,12 @@ found: if(p->back & 1) { v = q->pc - (p->pc + 2); if(v >= -128) { + if(p->as == AJCXZW) + *andptr++ = 0x67; *andptr++ = op; *andptr++ = v; + } else if(t[2] == Zloop) { + diag("loop too far: %P", p); } else { v -= 5-2; if(t[2] == Zbr) { @@ -1105,8 +1115,12 @@ found: p->forwd = q->comefrom; q->comefrom = p; if(p->back & 2) { // short + if(p->as == AJCXZW) + *andptr++ = 0x67; *andptr++ = op; *andptr++ = 0; + } else if(t[2] == Zloop) { + diag("loop too far: %P", p); } else { if(t[2] == Zbr) *andptr++ = 0x0f; @@ -1131,18 +1145,17 @@ found: r->add = p->to.offset; put4(0); break; - - case Zloop: - q = p->pcond; - if(q == nil) { - diag("loop without target"); - errorexit(); - } - v = q->pc - p->pc - 2; - if(v < -128 && v > 127) - diag("loop too far: %P", p); + + case Zcallind: *andptr++ = op; - *andptr++ = v; + *andptr++ = o->op[z+1]; + r = addrel(cursym); + r->off = p->pc + andptr - and; + r->type = D_ADDR; + r->siz = 4; + r->add = p->to.offset; + r->sym = p->to.sym; + put4(0); break; case Zbyte: diff --git a/src/cmd/Makefile b/src/cmd/Makefile deleted file mode 100644 index 5a37733de..000000000 --- a/src/cmd/Makefile +++ /dev/null @@ -1,69 +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. - -include ../Make.inc - -all: install - -# Only build tools for current architecture, and only tools written in C. -# The tools written in Go are managed by ../pkg/Makefile. -DIRS=\ - $(O)a\ - $(O)c\ - $(O)g\ - $(O)l\ - cc\ - cov\ - gc\ - godefs\ - gopack\ - gotry\ - nm\ - prof\ - -# Clean applies to all directories, even for other architectures or -# written in Go. -CLEANDIRS=\ - $(DIRS)\ - 5a\ - 5c\ - 5g\ - 5l\ - 6a\ - 6c\ - 6g\ - 6l\ - 8a\ - 8c\ - 8g\ - 8l\ - cgo\ - ebnflint\ - godoc\ - gofix\ - gofmt\ - goinstall\ - gotest\ - gotype\ - goyacc\ - hgpatch\ - -install: $(patsubst %,%.install,$(DIRS)) -clean: $(patsubst %,%.clean,$(CLEANDIRS)) - -%.install: - @echo - @echo %%%% making $* %%%% - @echo - $(MAKE) -C $* install - -gc.install $(O)c.install: cc.install -$(O)g.install: gc.install -$(O)a.install $(O)c.install $(O)g.install: $(O)l.install - -%.clean: - $(MAKE) -C $* clean - -echo-dirs: - @echo $(DIRS) diff --git a/src/cmd/addr2line/main.c b/src/cmd/addr2line/main.c new file mode 100644 index 000000000..6b2fe5dfe --- /dev/null +++ b/src/cmd/addr2line/main.c @@ -0,0 +1,68 @@ +// 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. + +/* + * addr2line simulation - only enough to make pprof work on Macs + */ + +#include +#include +#include +#include + +void +usage(void) +{ + fprint(2, "usage: addr2line binary\n"); + fprint(2, "reads addresses from standard input and writes two lines for each:\n"); + fprint(2, "\tfunction name\n"); + fprint(2, "\tfile:line\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int fd; + char *p; + uvlong pc; + Symbol s; + Fhdr fhdr; + Biobuf bin, bout; + char file[1024]; + + ARGBEGIN{ + default: + usage(); + }ARGEND + + if(argc != 1) + usage(); + + 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"); + + Binit(&bin, 0, OREAD); + Binit(&bout, 1, OWRITE); + for(;;) { + p = Brdline(&bin, '\n'); + if(p == nil) + break; + p[Blinelen(&bin)-1] = '\0'; + pc = strtoull(p, 0, 16); + if(!findsym(pc, CTEXT, &s)) + s.name = "??"; + if(!fileline(file, sizeof file, pc)) + strcpy(file, "??:0"); + Bprint(&bout, "%s\n%s\n", s.name, file); + } + Bflush(&bout); + exits(0); +} diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go new file mode 100644 index 000000000..7363f6d82 --- /dev/null +++ b/src/cmd/api/goapi.go @@ -0,0 +1,1005 @@ +// 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. + +// Api computes the exported API of a set of Go packages. +// +// BUG(bradfitz): Note that this tool is only currently suitable +// for use on the Go standard library, not arbitrary packages. +// Once the Go AST has type information, this tool will be more +// reliable without hard-coded hacks throughout. +package main + +import ( + "bufio" + "bytes" + "errors" + "flag" + "fmt" + "go/ast" + "go/build" + "go/doc" + "go/parser" + "go/printer" + "go/token" + "io/ioutil" + "log" + "os" + "os/exec" + "path" + "path/filepath" + "sort" + "strconv" + "strings" +) + +// Flags +var ( + checkFile = flag.String("c", "", "optional filename to check API against") + verbose = flag.Bool("v", false, "Verbose debugging") +) + +var contexts = []*build.Context{ + {GOOS: "linux", GOARCH: "386", CgoEnabled: true}, + {GOOS: "linux", GOARCH: "386"}, + {GOOS: "linux", GOARCH: "amd64", CgoEnabled: true}, + {GOOS: "linux", GOARCH: "amd64"}, + {GOOS: "darwin", GOARCH: "386", CgoEnabled: true}, + {GOOS: "darwin", GOARCH: "386"}, + {GOOS: "darwin", GOARCH: "amd64", CgoEnabled: true}, + {GOOS: "darwin", GOARCH: "amd64"}, + {GOOS: "windows", GOARCH: "amd64"}, + {GOOS: "windows", GOARCH: "386"}, +} + +func init() { + for _, c := range contexts { + c.Compiler = build.Default.Compiler + } +} + +func contextName(c *build.Context) string { + s := c.GOOS + "-" + c.GOARCH + if c.CgoEnabled { + return s + "-cgo" + } + return s +} + +func main() { + flag.Parse() + + var pkgs []string + if flag.NArg() > 0 { + pkgs = flag.Args() + } else { + stds, err := exec.Command("go", "list", "std").Output() + if err != nil { + log.Fatal(err) + } + pkgs = strings.Fields(string(stds)) + } + + var featureCtx = make(map[string]map[string]bool) // feature -> context name -> true + for _, context := range contexts { + w := NewWalker() + w.context = context + + for _, pkg := range pkgs { + w.wantedPkg[pkg] = true + } + + for _, pkg := range pkgs { + if strings.HasPrefix(pkg, "cmd/") || + strings.HasPrefix(pkg, "exp/") || + strings.HasPrefix(pkg, "old/") { + continue + } + if fi, err := os.Stat(filepath.Join(w.root, pkg)); err != nil || !fi.IsDir() { + log.Fatalf("no source in tree for package %q", pkg) + } + w.WalkPackage(pkg) + } + ctxName := contextName(context) + for _, f := range w.Features() { + if featureCtx[f] == nil { + featureCtx[f] = make(map[string]bool) + } + featureCtx[f][ctxName] = true + } + } + + var features []string + for f, cmap := range featureCtx { + if len(cmap) == len(contexts) { + features = append(features, f) + continue + } + comma := strings.Index(f, ",") + for cname := range cmap { + f2 := fmt.Sprintf("%s (%s)%s", f[:comma], cname, f[comma:]) + features = append(features, f2) + } + } + sort.Strings(features) + + bw := bufio.NewWriter(os.Stdout) + defer bw.Flush() + + if *checkFile != "" { + bs, err := ioutil.ReadFile(*checkFile) + if err != nil { + log.Fatalf("Error reading file %s: %v", *checkFile, err) + } + v1 := strings.Split(strings.TrimSpace(string(bs)), "\n") + sort.Strings(v1) + v2 := features + take := func(sl *[]string) string { + s := (*sl)[0] + *sl = (*sl)[1:] + return s + } + changes := false + for len(v1) > 0 || len(v2) > 0 { + switch { + case len(v2) == 0 || v1[0] < v2[0]: + fmt.Fprintf(bw, "-%s\n", take(&v1)) + changes = true + case len(v1) == 0 || v1[0] > v2[0]: + fmt.Fprintf(bw, "+%s\n", take(&v2)) + changes = true + default: + take(&v1) + take(&v2) + } + } + if changes { + bw.Flush() + os.Exit(1) + } + } else { + for _, f := range features { + fmt.Fprintf(bw, "%s\n", f) + } + } +} + +// pkgSymbol represents a symbol in a package +type pkgSymbol struct { + pkg string // "net/http" + symbol string // "RoundTripper" +} + +type Walker struct { + context *build.Context + root string + fset *token.FileSet + scope []string + features map[string]bool // set + lastConstType string + curPackageName string + curPackage *ast.Package + prevConstType map[pkgSymbol]string + constDep map[string]string // key's const identifier has type of future value const identifier + packageState map[string]loadState + interfaces map[pkgSymbol]*ast.InterfaceType + functionTypes map[pkgSymbol]string // symbol => return type + selectorFullPkg map[string]string // "http" => "net/http", updated by imports + wantedPkg map[string]bool // packages requested on the command line +} + +func NewWalker() *Walker { + return &Walker{ + fset: token.NewFileSet(), + features: make(map[string]bool), + packageState: make(map[string]loadState), + interfaces: make(map[pkgSymbol]*ast.InterfaceType), + functionTypes: make(map[pkgSymbol]string), + selectorFullPkg: make(map[string]string), + wantedPkg: make(map[string]bool), + prevConstType: make(map[pkgSymbol]string), + root: filepath.Join(build.Default.GOROOT, "src/pkg"), + } +} + +// loadState is the state of a package's parsing. +type loadState int + +const ( + notLoaded loadState = iota + loading + 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) + } + sort.Strings(fs) + return +} + +// fileDeps returns the imports in a file. +func fileDeps(f *ast.File) (pkgs []string) { + for _, is := range f.Imports { + fpkg, err := strconv.Unquote(is.Path.Value) + if err != nil { + log.Fatalf("error unquoting import string %q: %v", is.Path.Value, err) + } + if fpkg != "C" { + pkgs = append(pkgs, fpkg) + } + } + return +} + +// WalkPackage walks all files in package `name'. +// WalkPackage does nothing if the package has already been loaded. +func (w *Walker) WalkPackage(name string) { + switch w.packageState[name] { + case loading: + log.Fatalf("import cycle loading package %q?", name) + case loaded: + return + } + w.packageState[name] = loading + defer func() { + w.packageState[name] = loaded + }() + dir := filepath.Join(w.root, filepath.FromSlash(name)) + + ctxt := w.context + if ctxt == nil { + ctxt = &build.Default + } + info, err := ctxt.ImportDir(dir, 0) + if err != nil { + if strings.Contains(err.Error(), "no Go source files") { + return + } + log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err) + } + + apkg := &ast.Package{ + Files: make(map[string]*ast.File), + } + + files := append(append([]string{}, info.GoFiles...), info.CgoFiles...) + for _, file := range files { + f, err := parser.ParseFile(w.fset, filepath.Join(dir, file), nil, 0) + if err != nil { + log.Fatalf("error parsing package %s, file %s: %v", name, file, err) + } + apkg.Files[file] = f + + for _, dep := range fileDeps(f) { + w.WalkPackage(dep) + } + } + + if *verbose { + log.Printf("package %s", name) + } + pop := w.pushScope("pkg " + name) + defer pop() + + w.curPackageName = name + w.curPackage = apkg + w.constDep = map[string]string{} + + for _, afile := range apkg.Files { + w.recordTypes(afile) + } + + // Register all function declarations first. + for _, afile := range apkg.Files { + for _, di := range afile.Decls { + if d, ok := di.(*ast.FuncDecl); ok { + w.peekFuncDecl(d) + } + } + } + + for _, afile := range apkg.Files { + w.walkFile(afile) + } + + w.resolveConstantDeps() + + // Now that we're done walking types, vars and consts + // in the *ast.Package, use go/doc to do the rest + // (functions and methods). This is done here because + // go/doc is destructive. We can't use the + // *ast.Package after this. + dpkg := doc.New(apkg, name, doc.AllMethods) + + for _, t := range dpkg.Types { + // Move funcs up to the top-level, not hiding in the Types. + dpkg.Funcs = append(dpkg.Funcs, t.Funcs...) + + for _, m := range t.Methods { + w.walkFuncDecl(m.Decl) + } + } + + for _, f := range dpkg.Funcs { + w.walkFuncDecl(f.Decl) + } +} + +// pushScope enters a new scope (walking a package, type, node, etc) +// and returns a function that will leave the scope (with sanity checking +// for mismatched pushes & pops) +func (w *Walker) pushScope(name string) (popFunc func()) { + w.scope = append(w.scope, name) + return func() { + if len(w.scope) == 0 { + log.Fatalf("attempt to leave scope %q with empty scope list", name) + } + if w.scope[len(w.scope)-1] != name { + log.Fatalf("attempt to leave scope %q, but scope is currently %#v", name, w.scope) + } + w.scope = w.scope[:len(w.scope)-1] + } +} + +func (w *Walker) recordTypes(file *ast.File) { + for _, di := range file.Decls { + switch d := di.(type) { + case *ast.GenDecl: + switch d.Tok { + case token.TYPE: + for _, sp := range d.Specs { + ts := sp.(*ast.TypeSpec) + name := ts.Name.Name + if ast.IsExported(name) { + if it, ok := ts.Type.(*ast.InterfaceType); ok { + w.noteInterface(name, it) + } + } + } + } + } + } +} + +func (w *Walker) walkFile(file *ast.File) { + // Not entering a scope here; file boundaries aren't interesting. + for _, di := range file.Decls { + switch d := di.(type) { + case *ast.GenDecl: + switch d.Tok { + case token.IMPORT: + for _, sp := range d.Specs { + is := sp.(*ast.ImportSpec) + fpath, err := strconv.Unquote(is.Path.Value) + if err != nil { + log.Fatal(err) + } + name := path.Base(fpath) + if is.Name != nil { + name = is.Name.Name + } + w.selectorFullPkg[name] = fpath + } + case token.CONST: + for _, sp := range d.Specs { + w.walkConst(sp.(*ast.ValueSpec)) + } + case token.TYPE: + for _, sp := range d.Specs { + w.walkTypeSpec(sp.(*ast.TypeSpec)) + } + case token.VAR: + for _, sp := range d.Specs { + w.walkVar(sp.(*ast.ValueSpec)) + } + default: + log.Fatalf("unknown token type %d in GenDecl", d.Tok) + } + case *ast.FuncDecl: + // Ignore. Handled in subsequent pass, by go/doc. + default: + log.Printf("unhandled %T, %#v\n", di, di) + printer.Fprint(os.Stderr, w.fset, di) + os.Stderr.Write([]byte("\n")) + } + } +} + +var constType = map[token.Token]string{ + token.INT: "ideal-int", + token.FLOAT: "ideal-float", + token.STRING: "ideal-string", + token.CHAR: "ideal-char", + token.IMAG: "ideal-imag", +} + +var varType = map[token.Token]string{ + token.INT: "int", + token.FLOAT: "float64", + token.STRING: "string", + token.CHAR: "rune", + token.IMAG: "complex128", +} + +var errTODO = errors.New("TODO") + +func (w *Walker) constValueType(vi interface{}) (string, error) { + switch v := vi.(type) { + case *ast.BasicLit: + litType, ok := constType[v.Kind] + if !ok { + return "", fmt.Errorf("unknown basic literal kind %#v", v) + } + return litType, nil + case *ast.UnaryExpr: + return w.constValueType(v.X) + case *ast.SelectorExpr: + lhs := w.nodeString(v.X) + rhs := w.nodeString(v.Sel) + pkg, ok := w.selectorFullPkg[lhs] + if !ok { + return "", fmt.Errorf("unknown constant reference; unknown package in expression %s.%s", lhs, rhs) + } + if t, ok := w.prevConstType[pkgSymbol{pkg, rhs}]; ok { + return t, nil + } + return "", fmt.Errorf("unknown constant reference to %s.%s", lhs, rhs) + case *ast.Ident: + if v.Name == "iota" { + return "ideal-int", nil // hack. + } + if v.Name == "false" || v.Name == "true" { + return "bool", nil + } + if v.Name == "intSize" && w.curPackageName == "strconv" { + // Hack. + return "ideal-int", nil + } + if t, ok := w.prevConstType[pkgSymbol{w.curPackageName, v.Name}]; ok { + return t, nil + } + return constDepPrefix + v.Name, nil + case *ast.BinaryExpr: + left, err := w.constValueType(v.X) + if err != nil { + return "", err + } + right, err := w.constValueType(v.Y) + if err != nil { + return "", err + } + if left != right { + // TODO(bradfitz): encode the real rules here, + // rather than this mess. + if left == "ideal-int" && right == "ideal-float" { + return "ideal-float", nil // math.Log2E + } + if left == "ideal-char" && right == "ideal-int" { + return "ideal-int", nil // math/big.MaxBase + } + if left == "ideal-int" && right == "ideal-char" { + return "ideal-int", nil // text/scanner.GoWhitespace + } + if left == "ideal-int" && right == "Duration" { + // Hack, for package time. + return "Duration", nil + } + if left == "ideal-int" && !strings.HasPrefix(right, "ideal-") { + return right, nil + } + if right == "ideal-int" && !strings.HasPrefix(left, "ideal-") { + return left, nil + } + if strings.HasPrefix(left, constDepPrefix) && strings.HasPrefix(right, constDepPrefix) { + // Just pick one. + // e.g. text/scanner GoTokens const-dependency:ScanIdents, const-dependency:ScanFloats + return left, nil + } + return "", fmt.Errorf("in BinaryExpr, unhandled type mismatch; left=%q, right=%q", left, right) + } + return left, nil + case *ast.CallExpr: + // Not a call, but a type conversion. + return w.nodeString(v.Fun), nil + case *ast.ParenExpr: + return w.constValueType(v.X) + } + return "", fmt.Errorf("unknown const value type %T", vi) +} + +func (w *Walker) varValueType(vi interface{}) (string, error) { + switch v := vi.(type) { + case *ast.BasicLit: + litType, ok := varType[v.Kind] + if !ok { + return "", fmt.Errorf("unknown basic literal kind %#v", v) + } + return litType, nil + case *ast.CompositeLit: + return w.nodeString(v.Type), nil + case *ast.FuncLit: + return w.nodeString(w.namelessType(v.Type)), nil + case *ast.UnaryExpr: + if v.Op == token.AND { + typ, err := w.varValueType(v.X) + return "*" + typ, err + } + return "", fmt.Errorf("unknown unary expr: %#v", v) + case *ast.SelectorExpr: + return "", errTODO + case *ast.Ident: + node, _, ok := w.resolveName(v.Name) + if !ok { + return "", fmt.Errorf("unresolved identifier: %q", v.Name) + } + return w.varValueType(node) + case *ast.BinaryExpr: + left, err := w.varValueType(v.X) + if err != nil { + return "", err + } + right, err := w.varValueType(v.Y) + if err != nil { + return "", err + } + if left != right { + return "", fmt.Errorf("in BinaryExpr, unhandled type mismatch; left=%q, right=%q", left, right) + } + return left, nil + case *ast.ParenExpr: + return w.varValueType(v.X) + case *ast.CallExpr: + var funSym pkgSymbol + if selnode, ok := v.Fun.(*ast.SelectorExpr); ok { + // assume it is not a method. + pkg, ok := w.selectorFullPkg[w.nodeString(selnode.X)] + if !ok { + return "", fmt.Errorf("not a package: %s", w.nodeString(selnode.X)) + } + funSym = pkgSymbol{pkg, selnode.Sel.Name} + if retType, ok := w.functionTypes[funSym]; ok { + if ast.IsExported(retType) && pkg != w.curPackageName { + // otherpkg.F returning an exported type from otherpkg. + return pkg + "." + retType, nil + } else { + return retType, nil + } + } + } else { + funSym = pkgSymbol{w.curPackageName, w.nodeString(v.Fun)} + if retType, ok := w.functionTypes[funSym]; ok { + return retType, nil + } + } + // maybe a function call; maybe a conversion. Need to lookup type. + // TODO(bradfitz): this is a hack, but arguably most of this tool is, + // until the Go AST has type information. + nodeStr := w.nodeString(v.Fun) + switch nodeStr { + case "string", "[]byte": + return nodeStr, nil + } + return "", fmt.Errorf("not a known function %q", nodeStr) + default: + return "", fmt.Errorf("unknown const value type %T", vi) + } + panic("unreachable") +} + +// resolveName finds a top-level node named name and returns the node +// v and its type t, if known. +func (w *Walker) resolveName(name string) (v interface{}, t interface{}, ok bool) { + for _, file := range w.curPackage.Files { + for _, di := range file.Decls { + switch d := di.(type) { + case *ast.GenDecl: + switch d.Tok { + case token.VAR: + for _, sp := range d.Specs { + vs := sp.(*ast.ValueSpec) + for i, vname := range vs.Names { + if vname.Name == name { + if len(vs.Values) > i { + return vs.Values[i], vs.Type, true + } + return nil, vs.Type, true + } + } + } + } + } + } + } + return nil, nil, false +} + +// constDepPrefix is a magic prefix that is used by constValueType +// and walkConst to signal that a type isn't known yet. These are +// resolved at the end of walking of a package's files. +const constDepPrefix = "const-dependency:" + +func (w *Walker) walkConst(vs *ast.ValueSpec) { + for _, ident := range vs.Names { + litType := "" + if vs.Type != nil { + litType = w.nodeString(vs.Type) + } else { + litType = w.lastConstType + if vs.Values != nil { + if len(vs.Values) != 1 { + log.Fatalf("const %q, values: %#v", ident.Name, vs.Values) + } + 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) + } + } + } + } + if strings.HasPrefix(litType, constDepPrefix) { + dep := litType[len(constDepPrefix):] + w.constDep[ident.Name] = dep + continue + } + if litType == "" { + log.Fatalf("unknown kind in const %q", ident.Name) + } + w.lastConstType = litType + + w.prevConstType[pkgSymbol{w.curPackageName, ident.Name}] = litType + + if ast.IsExported(ident.Name) { + w.emitFeature(fmt.Sprintf("const %s %s", ident, litType)) + } + } +} + +func (w *Walker) resolveConstantDeps() { + var findConstType func(string) string + findConstType = func(ident string) string { + if dep, ok := w.constDep[ident]; ok { + return findConstType(dep) + } + if t, ok := w.prevConstType[pkgSymbol{w.curPackageName, ident}]; ok { + return t + } + return "" + } + for ident := range w.constDep { + if !ast.IsExported(ident) { + continue + } + t := findConstType(ident) + if t == "" { + log.Fatalf("failed to resolve constant %q", ident) + } + w.emitFeature(fmt.Sprintf("const %s %s", ident, t)) + } +} + +func (w *Walker) walkVar(vs *ast.ValueSpec) { + for i, ident := range vs.Names { + if !ast.IsExported(ident.Name) { + continue + } + + typ := "" + if vs.Type != nil { + typ = w.nodeString(vs.Type) + } else { + if len(vs.Values) == 0 { + log.Fatalf("no values for var %q", ident.Name) + } + if len(vs.Values) > 1 { + log.Fatalf("more than 1 values in ValueSpec not handled, var %q", ident.Name) + } + var err error + typ, err = w.varValueType(vs.Values[i]) + if err != nil { + log.Fatalf("unknown type of variable %q, type %T, error = %v\ncode: %s", + ident.Name, vs.Values[i], err, w.nodeString(vs.Values[i])) + } + } + w.emitFeature(fmt.Sprintf("var %s %s", ident, typ)) + } +} + +func (w *Walker) nodeString(node interface{}) string { + if node == nil { + return "" + } + var b bytes.Buffer + printer.Fprint(&b, w.fset, node) + return b.String() +} + +func (w *Walker) nodeDebug(node interface{}) string { + if node == nil { + return "" + } + var b bytes.Buffer + ast.Fprint(&b, w.fset, node, nil) + return b.String() +} + +func (w *Walker) noteInterface(name string, it *ast.InterfaceType) { + w.interfaces[pkgSymbol{w.curPackageName, name}] = it +} + +func (w *Walker) walkTypeSpec(ts *ast.TypeSpec) { + name := ts.Name.Name + if !ast.IsExported(name) { + return + } + switch t := ts.Type.(type) { + case *ast.StructType: + w.walkStructType(name, t) + case *ast.InterfaceType: + w.walkInterfaceType(name, t) + default: + w.emitFeature(fmt.Sprintf("type %s %s", name, w.nodeString(ts.Type))) + } +} + +func (w *Walker) walkStructType(name string, t *ast.StructType) { + typeStruct := fmt.Sprintf("type %s struct", name) + w.emitFeature(typeStruct) + pop := w.pushScope(typeStruct) + defer pop() + for _, f := range t.Fields.List { + typ := f.Type + for _, name := range f.Names { + if ast.IsExported(name.Name) { + w.emitFeature(fmt.Sprintf("%s %s", name, w.nodeString(w.namelessType(typ)))) + } + } + if f.Names == nil { + switch v := typ.(type) { + case *ast.Ident: + if ast.IsExported(v.Name) { + w.emitFeature(fmt.Sprintf("embedded %s", v.Name)) + } + case *ast.StarExpr: + switch vv := v.X.(type) { + case *ast.Ident: + if ast.IsExported(vv.Name) { + w.emitFeature(fmt.Sprintf("embedded *%s", vv.Name)) + } + case *ast.SelectorExpr: + w.emitFeature(fmt.Sprintf("embedded %s", w.nodeString(typ))) + default: + log.Fatalf("unable to handle embedded starexpr before %T", typ) + } + case *ast.SelectorExpr: + w.emitFeature(fmt.Sprintf("embedded %s", w.nodeString(typ))) + default: + log.Fatalf("unable to handle embedded %T", typ) + } + } + } +} + +// method is a method of an interface. +type method struct { + name string // "Read" + sig string // "([]byte) (int, error)", from funcSigString +} + +// interfaceMethods returns the expanded list of methods for an interface. +// pkg is the complete package name ("net/http") +// iname is the interface name. +func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { + t, ok := w.interfaces[pkgSymbol{pkg, iname}] + if !ok { + log.Fatalf("failed to find interface %s.%s", pkg, iname) + } + + for _, f := range t.Methods.List { + typ := f.Type + switch tv := typ.(type) { + case *ast.FuncType: + for _, mname := range f.Names { + if ast.IsExported(mname.Name) { + ft := typ.(*ast.FuncType) + methods = append(methods, method{ + name: mname.Name, + sig: w.funcSigString(ft), + }) + } + } + case *ast.Ident: + embedded := typ.(*ast.Ident).Name + if embedded == "error" { + methods = append(methods, method{ + name: "Error", + sig: "() string", + }) + continue + } + if !ast.IsExported(embedded) { + log.Fatalf("unexported embedded interface %q in exported interface %s.%s; confused", + embedded, pkg, iname) + } + methods = append(methods, w.interfaceMethods(pkg, embedded)...) + case *ast.SelectorExpr: + lhs := w.nodeString(tv.X) + rhs := w.nodeString(tv.Sel) + fpkg, ok := w.selectorFullPkg[lhs] + if !ok { + log.Fatalf("can't resolve selector %q in interface %s.%s", lhs, pkg, iname) + } + methods = append(methods, w.interfaceMethods(fpkg, rhs)...) + default: + log.Fatalf("unknown type %T in interface field", typ) + } + } + return +} + +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) { + methNames = append(methNames, m.name) + w.emitFeature(fmt.Sprintf("%s%s", m.name, m.sig)) + } + pop() + + sort.Strings(methNames) + if len(methNames) == 0 { + w.emitFeature(fmt.Sprintf("type %s interface {}", name)) + } else { + w.emitFeature(fmt.Sprintf("type %s interface { %s }", name, strings.Join(methNames, ", "))) + } +} + +func (w *Walker) peekFuncDecl(f *ast.FuncDecl) { + if f.Recv != nil { + return + } + // Record return type for later use. + if f.Type.Results != nil && len(f.Type.Results.List) == 1 { + retType := w.nodeString(w.namelessType(f.Type.Results.List[0].Type)) + w.functionTypes[pkgSymbol{w.curPackageName, f.Name.Name}] = retType + } +} + +func (w *Walker) walkFuncDecl(f *ast.FuncDecl) { + if !ast.IsExported(f.Name.Name) { + return + } + if f.Recv != nil { + // Method. + recvType := w.nodeString(f.Recv.List[0].Type) + keep := ast.IsExported(recvType) || + (strings.HasPrefix(recvType, "*") && + ast.IsExported(recvType[1:])) + if !keep { + return + } + w.emitFeature(fmt.Sprintf("method (%s) %s%s", recvType, f.Name.Name, w.funcSigString(f.Type))) + return + } + // Else, a function + w.emitFeature(fmt.Sprintf("func %s%s", f.Name.Name, w.funcSigString(f.Type))) +} + +func (w *Walker) funcSigString(ft *ast.FuncType) string { + var b bytes.Buffer + 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))) + } + } + b.WriteByte(')') + if ft.Results != nil { + if nr := len(ft.Results.List); nr > 0 { + b.WriteByte(' ') + if nr > 1 { + b.WriteByte('(') + } + for i, f := range ft.Results.List { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(w.nodeString(w.namelessType(f.Type))) + } + if nr > 1 { + b.WriteByte(')') + } + } + } + return b.String() +} + +// namelessType returns a type node that lacks any variable names. +func (w *Walker) namelessType(t interface{}) interface{} { + ft, ok := t.(*ast.FuncType) + if !ok { + return t + } + return &ast.FuncType{ + Params: w.namelessFieldList(ft.Params), + Results: w.namelessFieldList(ft.Results), + } +} + +// namelessFieldList returns a deep clone of fl, with the cloned fields +// lacking names. +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)) + } + } + return fl2 +} + +// namelessField clones f, but not preserving the names of fields. +// (comments and tags are also ignored) +func (w *Walker) namelessField(f *ast.Field) *ast.Field { + return &ast.Field{ + Type: f.Type, + } +} + +func (w *Walker) emitFeature(feature string) { + if !w.wantedPkg[w.curPackageName] { + return + } + f := strings.Join(w.scope, ", ") + ", " + feature + if _, dup := w.features[f]; dup { + panic("duplicate feature inserted: " + f) + } + + if strings.Contains(f, "\n") { + // TODO: for now, just skip over the + // runtime.MemStatsType.BySize type, which this tool + // doesn't properly handle. It's pretty low-level, + // though, so not super important to protect against. + if strings.HasPrefix(f, "pkg runtime") && strings.Contains(f, "BySize [61]struct") { + return + } + panic("feature contains newlines: " + f) + } + w.features[f] = true + if *verbose { + log.Printf("feature: %s", f) + } +} + +func strListContains(l []string, s string) bool { + for _, v := range l { + if v == s { + return true + } + } + return false +} diff --git a/src/cmd/api/goapi_test.go b/src/cmd/api/goapi_test.go new file mode 100644 index 000000000..c7cc601b1 --- /dev/null +++ b/src/cmd/api/goapi_test.go @@ -0,0 +1,75 @@ +// 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 ( + "flag" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + "testing" +) + +var ( + updateGolden = flag.Bool("updategolden", false, "update golden files") +) + +func TestGolden(t *testing.T) { + td, err := os.Open("testdata/src/pkg") + if err != nil { + t.Fatal(err) + } + fis, err := td.Readdir(0) + if err != nil { + t.Fatal(err) + } + for _, fi := range fis { + if !fi.IsDir() { + continue + } + w := NewWalker() + w.wantedPkg[fi.Name()] = true + + w.root = "testdata/src/pkg" + goldenFile := filepath.Join("testdata", "src", "pkg", fi.Name(), "golden.txt") + w.WalkPackage(fi.Name()) + + if *updateGolden { + os.Remove(goldenFile) + f, err := os.Create(goldenFile) + if err != nil { + t.Fatal(err) + } + for _, feat := range w.Features() { + fmt.Fprintf(f, "%s\n", feat) + } + f.Close() + } + + bs, err := ioutil.ReadFile(goldenFile) + if err != nil { + t.Fatalf("opening golden.txt for package %q: %v", fi.Name(), err) + } + wanted := strings.Split(string(bs), "\n") + sort.Strings(wanted) + for _, feature := range wanted { + if feature == "" { + continue + } + _, ok := w.features[feature] + if !ok { + t.Errorf("package %s: missing feature %q", fi.Name(), feature) + } + delete(w.features, feature) + } + + for _, feature := range w.Features() { + t.Errorf("package %s: extra feature not in golden file: %q", fi.Name(), feature) + } + } +} diff --git a/src/cmd/api/testdata/src/pkg/p1/golden.txt b/src/cmd/api/testdata/src/pkg/p1/golden.txt new file mode 100644 index 000000000..e334e5776 --- /dev/null +++ b/src/cmd/api/testdata/src/pkg/p1/golden.txt @@ -0,0 +1,73 @@ +pkg p1, const A ideal-int +pkg p1, const A64 int64 +pkg p1, const AIsLowerA ideal-int +pkg p1, const B ideal-int +pkg p1, const ConstChase2 ideal-int +pkg p1, const ConversionConst MyInt +pkg p1, const FloatConst ideal-float +pkg p1, const StrConst ideal-string +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 TakesFunc(func(int) int) +pkg p1, method (*B) JustOnB() +pkg p1, method (*B) OnBothTandBPtr() +pkg p1, method (*Embedded) OnEmbedded() +pkg p1, method (*S2) SMethod(int8, int16, int64) +pkg p1, method (*T) JustOnT() +pkg p1, method (*T) OnBothTandBPtr() +pkg p1, method (B) OnBothTandBVal() +pkg p1, method (S) StructValueMethod() +pkg p1, method (S) StructValueMethodNamedRecv() +pkg p1, method (S2) StructValueMethod() +pkg p1, method (S2) StructValueMethodNamedRecv() +pkg p1, method (T) OnBothTandBVal() +pkg p1, method (TPtrExported) OnEmbedded() +pkg p1, method (TPtrUnexported) OnBothTandBPtr() +pkg p1, method (TPtrUnexported) OnBothTandBVal() +pkg p1, type B struct +pkg p1, type Codec struct +pkg p1, type Codec struct, Func func(int, int) int +pkg p1, type EmbedSelector struct +pkg p1, type EmbedSelector struct, embedded time.Time +pkg p1, type EmbedURLPtr struct +pkg p1, type EmbedURLPtr struct, embedded *url.URL +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 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 MyInt int +pkg p1, type Namer interface { Name } +pkg p1, type Namer interface, Name() string +pkg p1, type S struct +pkg p1, type S struct, Public *int +pkg p1, type S struct, PublicTime time.Time +pkg p1, type S2 struct +pkg p1, type S2 struct, Extra bool +pkg p1, type S2 struct, embedded S +pkg p1, type SI struct +pkg p1, type SI struct, I int +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 ChecksumError error +pkg p1, var SIPtr *SI +pkg p1, var SIPtr2 *SI +pkg p1, var SIVal SI +pkg p1, var StrConv string +pkg p1, var V string +pkg p1, var V1 uint64 +pkg p1, var V2 p2.Twoer +pkg p1, var VError Error +pkg p1, var X I +pkg p1, var X int64 +pkg p1, var Y int diff --git a/src/cmd/api/testdata/src/pkg/p1/p1.go b/src/cmd/api/testdata/src/pkg/p1/p1.go new file mode 100644 index 000000000..d965bb75e --- /dev/null +++ b/src/cmd/api/testdata/src/pkg/p1/p1.go @@ -0,0 +1,153 @@ +package p1 + +import ( + ptwo "p2" +) + +const ( + ConstChase2 = constChase // forward declaration to unexported ident + constChase = AIsLowerA // forward declaration to exported ident + + A = 1 + a = 11 + A64 int64 = 1 + + AIsLowerA = a // previously declared +) + +const ( + ConversionConst = MyInt(5) +) + +// Variables from function calls. +var ( + V = ptwo.F() + VError = BarE() + V1 = Bar1(1, 2, 3) + V2 = ptwo.G() +) + +// Variables with conversions: +var ( + StrConv = string("foo") + ByteConv = []byte("foo") +) + +var ChecksumError = ptwo.NewError("gzip checksum error") + +const B = 2 +const StrConst = "foo" +const FloatConst = 1.5 + +type myInt int + +type MyInt int + +type S struct { + Public *int + private *int + PublicTime time.Time +} + +type EmbedURLPtr struct { + *url.URL +} + +type S2 struct { + S + Extra bool +} + +var X int64 + +var ( + Y int + X I +) + +type Namer interface { + Name() string +} + +type I interface { + Namer + ptwo.Twoer + Set(name string, balance int64) + Get(string) int64 + GetNamed(string) (balance int64) + private() +} + +type Error interface { + error + Temporary() bool +} + +func (myInt) privateTypeMethod() {} +func (myInt) CapitalMethodUnexportedType() {} + +func (s *S2) SMethod(x int8, y int16, z int64) {} + +type s struct{} + +func (s) method() +func (s) Method() + +func (S) StructValueMethod() +func (ignored S) StructValueMethodNamedRecv() + +func (s *S2) unexported(x int8, y int16, z int64) {} + +func Bar(x int8, y int16, z int64) {} +func Bar1(x int8, y int16, z int64) uint64 {} +func Bar2(x int8, y int16, z int64) (uint8, uint64) {} +func BarE() Error {} + +func unexported(x int8, y int16, z int64) {} + +func TakesFunc(f func(dontWantName int) int) + +type Codec struct { + Func func(x int, y int) (z int) +} + +type SI struct { + I int +} + +var SIVal = SI{} +var SIPtr = &SI{} +var SIPtr2 *SI + +type T struct { + common +} + +type B struct { + common +} + +type common struct { + i int +} + +type TPtrUnexported struct { + *common +} + +type TPtrExported struct { + *Embedded +} + +type Embedded struct{} + +func (*Embedded) OnEmbedded() {} + +func (*T) JustOnT() {} +func (*B) JustOnB() {} +func (*common) OnBothTandBPtr() {} +func (common) OnBothTandBVal() {} + +type EmbedSelector struct { + time.Time +} diff --git a/src/cmd/api/testdata/src/pkg/p2/golden.txt b/src/cmd/api/testdata/src/pkg/p2/golden.txt new file mode 100644 index 000000000..4271620c7 --- /dev/null +++ b/src/cmd/api/testdata/src/pkg/p2/golden.txt @@ -0,0 +1,5 @@ +pkg p2, func F() string +pkg p2, func G() Twoer +pkg p2, func NewError(string) error +pkg p2, type Twoer interface { PackageTwoMeth } +pkg p2, type Twoer interface, PackageTwoMeth() diff --git a/src/cmd/api/testdata/src/pkg/p2/p2.go b/src/cmd/api/testdata/src/pkg/p2/p2.go new file mode 100644 index 000000000..6b107b507 --- /dev/null +++ b/src/cmd/api/testdata/src/pkg/p2/p2.go @@ -0,0 +1,9 @@ +package p2 + +type Twoer interface { + PackageTwoMeth() +} + +func F() string {} +func G() Twoer {} +func NewError(s string) error {} diff --git a/src/cmd/cc/Makefile b/src/cmd/cc/Makefile index 8327d9516..109578297 100644 --- a/src/cmd/cc/Makefile +++ b/src/cmd/cc/Makefile @@ -1,36 +1,10 @@ -# 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. -include ../../Make.inc -O:=$(HOST_O) +include ../../Make.dist -LIB=cc.a +install: y.tab.h -HFILES=\ - cc.h\ - y.tab.h\ - -YFILES=\ - cc.y\ - -OFILES=\ - y.tab.$O\ - lex.$O\ - mac.$O\ - dcl.$O\ - acid.$O\ - godefs.$O\ - bits.$O\ - com.$O\ - scon.$O\ - funct.$O\ - sub.$O\ - com64.$O\ - dpchk.$O\ - omachcap.$O\ - -NOINSTALL=1 -include ../../Make.clib - -install: $(LIB) +y.tab.h: cc.y + LANG=C LANGUAGE=en_US.UTF8 bison -d -v -y a.y diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h index a38e658ce..4c527a2b3 100644 --- a/src/cmd/cc/cc.h +++ b/src/cmd/cc/cc.h @@ -31,8 +31,6 @@ #include #include -#pragma lib "../cc/cc.a$O" - #ifndef EXTERN #define EXTERN extern #endif @@ -124,6 +122,7 @@ struct Sym uchar sym; uchar aused; uchar sig; + uchar dataflag; }; #define S ((Sym*)0) @@ -518,14 +517,15 @@ EXTERN int thechar; EXTERN char* thestring; EXTERN Type* thisfn; EXTERN int32 thunk; -EXTERN Type* types[NTYPE]; -EXTERN Type* fntypes[NTYPE]; +EXTERN Type* types[NALLTYPES]; +EXTERN Type* fntypes[NALLTYPES]; EXTERN Node* initlist; EXTERN Term term[NTERM]; EXTERN int nterm; EXTERN int packflg; EXTERN int fproundflg; EXTERN int textflag; +EXTERN int dataflag; EXTERN int ncontin; EXTERN int canreach; EXTERN int warnreach; @@ -768,6 +768,7 @@ void arginit(void); void pragvararg(void); void pragpack(void); void pragfpround(void); +void pragdataflag(void); void pragtextflag(void); void pragincomplete(void); void pragdynimport(void); diff --git a/src/cmd/cc/com64.c b/src/cmd/cc/com64.c index fb7a3f750..f46fedc16 100644 --- a/src/cmd/cc/com64.c +++ b/src/cmd/cc/com64.c @@ -96,7 +96,7 @@ Node* nodmmv; Node* nodvasop; -char etconv[NTYPE]; /* for _vasop */ +char etconv[NALLTYPES]; /* for _vasop */ Init initetconv[] = { TCHAR, 1, 0, diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c index d624bf247..a3ed9772d 100644 --- a/src/cmd/cc/dcl.c +++ b/src/cmd/cc/dcl.c @@ -120,6 +120,10 @@ loop: (*f)(c, t, s); if(s->class == CLOCAL) s = mkstatic(s); + if(dataflag) { + s->dataflag = dataflag; + dataflag = 0; + } firstbit = 0; n->sym = s; n->type = s->type; diff --git a/src/cmd/cc/dpchk.c b/src/cmd/cc/dpchk.c index 084aa0484..c579e20d9 100644 --- a/src/cmd/cc/dpchk.c +++ b/src/cmd/cc/dpchk.c @@ -567,19 +567,23 @@ pragfpround(void) void pragtextflag(void) { - Sym *s; - - textflag = 0; - s = getsym(); - textflag = 7; - if(s) - textflag = atoi(s->name+1); + textflag = getnsn(); while(getnsc() != '\n') ; if(debug['f']) print("%4d: textflag %d\n", lineno, textflag); } +void +pragdataflag(void) +{ + dataflag = getnsn(); + while(getnsc() != '\n') + ; + if(debug['f']) + print("%4d: dataflag %d\n", lineno, dataflag); +} + void pragincomplete(void) { diff --git a/src/cmd/cc/funct.c b/src/cmd/cc/funct.c index 99477b2b2..057151987 100644 --- a/src/cmd/cc/funct.c +++ b/src/cmd/cc/funct.c @@ -46,7 +46,7 @@ struct Gtab }; Ftab ftabinit[OEND]; -Gtab gtabinit[NTYPE]; +Gtab gtabinit[NALLTYPES]; int isfunct(Node *n) @@ -350,7 +350,7 @@ bad: diag(Z, "dclfunct bad %T %s\n", t, s->name); } -Gtab gtabinit[NTYPE] = +Gtab gtabinit[NALLTYPES] = { TCHAR, "c", TUCHAR, "uc", diff --git a/src/cmd/cc/godefs.c b/src/cmd/cc/godefs.c index 3ba979c8a..4274c5626 100644 --- a/src/cmd/cc/godefs.c +++ b/src/cmd/cc/godefs.c @@ -124,11 +124,11 @@ Uconv(Fmt *fp) if(s && *s) { if(upper) - str[0] = toupper(*s); + str[0] = toupper((uchar)*s); else - str[0] = tolower(*s); + str[0] = tolower((uchar)*s); for(i = 1; i < STRINGSZ && s[i] != 0; i++) - str[i] = tolower(s[i]); + str[i] = tolower((uchar)s[i]); str[i] = 0; } diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c index 9fb2f9e4d..8aeb1a334 100644 --- a/src/cmd/cc/lex.c +++ b/src/cmd/cc/lex.c @@ -112,7 +112,7 @@ main(int argc, char *argv[]) case 'l': /* for little-endian mips */ if(thechar != 'v'){ - print("can only use -l with vc"); + print("can only use -l with vc\n"); errorexit(); } thechar = '0'; diff --git a/src/cmd/cc/lexbody b/src/cmd/cc/lexbody index f4cc19c2e..d339cf9a2 100644 --- a/src/cmd/cc/lexbody +++ b/src/cmd/cc/lexbody @@ -74,6 +74,13 @@ pragtextflag(void) ; } +void +pragdataflag(void) +{ + while(getnsc() != '\n') + ; +} + void pragprofile(void) { diff --git a/src/cmd/cc/macbody b/src/cmd/cc/macbody index ed66361f1..874e82d25 100644 --- a/src/cmd/cc/macbody +++ b/src/cmd/cc/macbody @@ -731,6 +731,10 @@ macprag(void) pragtextflag(); return; } + if(s && strcmp(s->name, "dataflag") == 0) { + pragdataflag(); + return; + } if(s && strcmp(s->name, "varargck") == 0) { pragvararg(); return; diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c index 0e5e8c059..3a686102f 100644 --- a/src/cmd/cc/pgen.c +++ b/src/cmd/cc/pgen.c @@ -266,7 +266,7 @@ loop: if(cases == C) diag(n, "case/default outside a switch"); if(l == Z) { - cas(); + newcase(); cases->val = 0; cases->def = 1; cases->label = pc; @@ -278,7 +278,7 @@ loop: goto rloop; if(l->op == OCONST) if(typeword[l->type->etype] && l->type->etype != TIND) { - cas(); + newcase(); cases->val = l->vconst; cases->def = 0; cases->label = pc; @@ -293,7 +293,7 @@ loop: complex(l); if(l->type == T) break; - if(!typeword[l->type->etype] || l->type->etype == TIND) { + if(!typechlvp[l->type->etype] || l->type->etype == TIND) { diag(n, "switch expression must be integer"); break; } @@ -303,7 +303,7 @@ loop: cn = cases; cases = C; - cas(); + newcase(); sbc = breakpc; breakpc = pc; @@ -320,15 +320,7 @@ loop: } patch(sp, pc); - regalloc(&nod, l, Z); - /* always signed */ - if(typev[l->type->etype]) - nod.type = types[TVLONG]; - else - nod.type = types[TLONG]; - cgen(l, &nod); - doswit(&nod); - regfree(&nod); + doswit(l); patch(spb, pc); cases = cn; diff --git a/src/cmd/cc/pswt.c b/src/cmd/cc/pswt.c index 0e402dea7..b94035faa 100644 --- a/src/cmd/cc/pswt.c +++ b/src/cmd/cc/pswt.c @@ -92,7 +92,7 @@ doswit(Node *n) } void -cas(void) +newcase(void) { Case *c; diff --git a/src/cmd/cc/sub.c b/src/cmd/cc/sub.c index e5992e213..72d671b2f 100644 --- a/src/cmd/cc/sub.c +++ b/src/cmd/cc/sub.c @@ -156,7 +156,10 @@ typ(int et, Type *d) t->link = d; t->down = T; t->sym = S; - t->width = ewidth[et]; + if(et < NTYPE) + t->width = ewidth[et]; + else + t->width = -1; // for TDOT or TOLD in prototype t->offset = 0; t->shift = 0; t->nbits = 0; @@ -1535,92 +1538,92 @@ uchar logrel[12] = OEQ, ONE, OLS, OLS, OLO, OLO, OHS, OHS, OHI, OHI, }; -uchar typei[NTYPE]; +uchar typei[NALLTYPES]; int typeiinit[] = { TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TVLONG, TUVLONG, -1, }; -uchar typeu[NTYPE]; +uchar typeu[NALLTYPES]; int typeuinit[] = { TUCHAR, TUSHORT, TUINT, TULONG, TUVLONG, TIND, -1, }; -uchar typesuv[NTYPE]; +uchar typesuv[NALLTYPES]; int typesuvinit[] = { TVLONG, TUVLONG, TSTRUCT, TUNION, -1, }; -uchar typeilp[NTYPE]; +uchar typeilp[NALLTYPES]; int typeilpinit[] = { TINT, TUINT, TLONG, TULONG, TIND, -1 }; -uchar typechl[NTYPE]; -uchar typechlv[NTYPE]; -uchar typechlvp[NTYPE]; +uchar typechl[NALLTYPES]; +uchar typechlv[NALLTYPES]; +uchar typechlvp[NALLTYPES]; int typechlinit[] = { TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, -1, }; -uchar typechlp[NTYPE]; +uchar typechlp[NALLTYPES]; int typechlpinit[] = { TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TIND, -1, }; -uchar typechlpfd[NTYPE]; +uchar typechlpfd[NALLTYPES]; int typechlpfdinit[] = { TCHAR, TUCHAR, TSHORT, TUSHORT, TINT, TUINT, TLONG, TULONG, TFLOAT, TDOUBLE, TIND, -1, }; -uchar typec[NTYPE]; +uchar typec[NALLTYPES]; int typecinit[] = { TCHAR, TUCHAR, -1 }; -uchar typeh[NTYPE]; +uchar typeh[NALLTYPES]; int typehinit[] = { TSHORT, TUSHORT, -1, }; -uchar typeil[NTYPE]; +uchar typeil[NALLTYPES]; int typeilinit[] = { TINT, TUINT, TLONG, TULONG, -1, }; -uchar typev[NTYPE]; +uchar typev[NALLTYPES]; int typevinit[] = { TVLONG, TUVLONG, -1, }; -uchar typefd[NTYPE]; +uchar typefd[NALLTYPES]; int typefdinit[] = { TFLOAT, TDOUBLE, -1, }; -uchar typeaf[NTYPE]; +uchar typeaf[NALLTYPES]; int typeafinit[] = { TFUNC, TARRAY, -1, }; -uchar typesu[NTYPE]; +uchar typesu[NALLTYPES]; int typesuinit[] = { TSTRUCT, TUNION, -1, }; -int32 tasign[NTYPE]; +int32 tasign[NALLTYPES]; Init tasigninit[] = { TCHAR, BNUMBER, 0, @@ -1641,7 +1644,7 @@ Init tasigninit[] = -1, 0, 0, }; -int32 tasadd[NTYPE]; +int32 tasadd[NALLTYPES]; Init tasaddinit[] = { TCHAR, BNUMBER, 0, @@ -1660,7 +1663,7 @@ Init tasaddinit[] = -1, 0, 0, }; -int32 tcast[NTYPE]; +int32 tcast[NALLTYPES]; Init tcastinit[] = { TCHAR, BNUMBER|BIND|BVOID, 0, @@ -1682,7 +1685,7 @@ Init tcastinit[] = -1, 0, 0, }; -int32 tadd[NTYPE]; +int32 tadd[NALLTYPES]; Init taddinit[] = { TCHAR, BNUMBER|BIND, 0, @@ -1701,7 +1704,7 @@ Init taddinit[] = -1, 0, 0, }; -int32 tsub[NTYPE]; +int32 tsub[NALLTYPES]; Init tsubinit[] = { TCHAR, BNUMBER, 0, @@ -1720,7 +1723,7 @@ Init tsubinit[] = -1, 0, 0, }; -int32 tmul[NTYPE]; +int32 tmul[NALLTYPES]; Init tmulinit[] = { TCHAR, BNUMBER, 0, @@ -1738,7 +1741,7 @@ Init tmulinit[] = -1, 0, 0, }; -int32 tand[NTYPE]; +int32 tand[NALLTYPES]; Init tandinit[] = { TCHAR, BINTEGER, 0, @@ -1754,7 +1757,7 @@ Init tandinit[] = -1, 0, 0, }; -int32 trel[NTYPE]; +int32 trel[NALLTYPES]; Init trelinit[] = { TCHAR, BNUMBER, 0, diff --git a/src/cmd/cc/y.tab.c b/src/cmd/cc/y.tab.c new file mode 100644 index 000000000..af8328abd --- /dev/null +++ b/src/cmd/cc/y.tab.c @@ -0,0 +1,3811 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* 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 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, 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 + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + 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. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + LORE = 258, + LXORE = 259, + LANDE = 260, + LLSHE = 261, + LRSHE = 262, + LMDE = 263, + LDVE = 264, + LMLE = 265, + LME = 266, + LPE = 267, + LOROR = 268, + LANDAND = 269, + LNE = 270, + LEQ = 271, + LGE = 272, + LLE = 273, + LRSH = 274, + LLSH = 275, + LMG = 276, + LPP = 277, + LMM = 278, + LNAME = 279, + LTYPE = 280, + LFCONST = 281, + LDCONST = 282, + LCONST = 283, + LLCONST = 284, + LUCONST = 285, + LULCONST = 286, + LVLCONST = 287, + LUVLCONST = 288, + LSTRING = 289, + LLSTRING = 290, + LAUTO = 291, + LBREAK = 292, + LCASE = 293, + LCHAR = 294, + LCONTINUE = 295, + LDEFAULT = 296, + LDO = 297, + LDOUBLE = 298, + LELSE = 299, + LEXTERN = 300, + LFLOAT = 301, + LFOR = 302, + LGOTO = 303, + 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 + }; +#endif +/* Tokens. */ +#define LORE 258 +#define LXORE 259 +#define LANDE 260 +#define LLSHE 261 +#define LRSHE 262 +#define LMDE 263 +#define LDVE 264 +#define LMLE 265 +#define LME 266 +#define LPE 267 +#define LOROR 268 +#define LANDAND 269 +#define LNE 270 +#define LEQ 271 +#define LGE 272 +#define LLE 273 +#define LRSH 274 +#define LLSH 275 +#define LMG 276 +#define LPP 277 +#define LMM 278 +#define LNAME 279 +#define LTYPE 280 +#define LFCONST 281 +#define LDCONST 282 +#define LCONST 283 +#define LLCONST 284 +#define LUCONST 285 +#define LULCONST 286 +#define LVLCONST 287 +#define LUVLCONST 288 +#define LSTRING 289 +#define LLSTRING 290 +#define LAUTO 291 +#define LBREAK 292 +#define LCASE 293 +#define LCHAR 294 +#define LCONTINUE 295 +#define LDEFAULT 296 +#define LDO 297 +#define LDOUBLE 298 +#define LELSE 299 +#define LEXTERN 300 +#define LFLOAT 301 +#define LFOR 302 +#define LGOTO 303 +#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 + + + + +/* Copy the first part of user declarations. */ +#line 31 "cc.y" + +#include +#include /* if we don't, bison will, and cc.h re-#defines getc */ +#include "cc.h" + + +/* 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 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 36 "cc.y" +{ + Node* node; + Sym* sym; + Type* type; + struct + { + Type* t; + uchar c; + } tycl; + struct + { + Type* t1; + Type* t2; + Type* t3; + uchar c; + } tyty; + struct + { + char* s; + int32 l; + } sval; + int32 lval; + double dval; + vlong vval; +} +/* Line 193 of yacc.c. */ +#line 274 "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 216 of yacc.c. */ +#line 287 "y.tab.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# 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 _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + 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) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((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 + +/* 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 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 1188 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 98 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 75 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 246 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 412 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 328 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +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, 96, 2, 2, 2, 35, 22, 2, + 38, 92, 33, 31, 4, 32, 36, 34, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 17, 3, + 25, 5, 26, 16, 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, 37, 2, 93, 21, 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, 94, 20, 95, 97, 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, 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, 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, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 18, 19, + 23, 24, 27, 28, 29, 30, 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 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint16 yyprhs[] = +{ + 0, 0, 3, 4, 7, 10, 14, 15, 16, 23, + 25, 26, 31, 35, 37, 41, 43, 47, 52, 57, + 60, 64, 66, 67, 72, 76, 77, 82, 84, 88, + 89, 94, 95, 101, 102, 104, 106, 110, 112, 116, + 119, 120, 122, 125, 129, 131, 133, 138, 143, 146, + 150, 154, 156, 160, 164, 167, 170, 173, 177, 179, + 182, 184, 186, 189, 190, 192, 194, 197, 200, 204, + 208, 212, 213, 216, 219, 221, 224, 228, 231, 234, + 237, 239, 242, 244, 247, 250, 251, 254, 260, 268, + 269, 280, 286, 294, 298, 304, 307, 310, 314, 320, + 326, 327, 329, 330, 332, 334, 336, 340, 342, 346, + 350, 354, 358, 362, 366, 370, 374, 378, 382, 386, + 390, 394, 398, 402, 406, 410, 414, 420, 424, 428, + 432, 436, 440, 444, 448, 452, 456, 460, 464, 466, + 472, 480, 482, 485, 488, 491, 494, 497, 500, 503, + 506, 509, 512, 516, 522, 528, 533, 538, 542, 546, + 549, 552, 554, 556, 558, 560, 562, 564, 566, 568, + 570, 572, 574, 576, 579, 581, 584, 585, 587, 589, + 593, 594, 599, 600, 602, 604, 606, 608, 611, 614, + 618, 621, 625, 627, 629, 632, 633, 638, 641, 644, + 645, 650, 653, 656, 657, 658, 666, 667, 673, 675, + 677, 680, 681, 684, 686, 688, 690, 692, 695, 697, + 699, 701, 705, 708, 712, 714, 716, 718, 720, 722, + 724, 726, 728, 730, 732, 734, 736, 738, 740, 742, + 744, 746, 748, 750, 752, 754, 756 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int16 yyrhs[] = +{ + 99, 0, -1, -1, 99, 100, -1, 151, 3, -1, + 151, 103, 3, -1, -1, -1, 151, 105, 101, 110, + 102, 128, -1, 105, -1, -1, 105, 104, 5, 122, + -1, 103, 4, 103, -1, 106, -1, 33, 162, 105, + -1, 171, -1, 38, 105, 92, -1, 106, 38, 126, + 92, -1, 106, 37, 138, 93, -1, 154, 3, -1, + 154, 108, 3, -1, 105, -1, -1, 105, 109, 5, + 122, -1, 108, 4, 108, -1, -1, 110, 154, 111, + 3, -1, 105, -1, 111, 4, 111, -1, -1, 153, + 113, 115, 3, -1, -1, 112, 153, 114, 115, 3, + -1, -1, 116, -1, 117, -1, 116, 4, 116, -1, + 105, -1, 171, 17, 139, -1, 17, 139, -1, -1, + 119, -1, 33, 162, -1, 33, 162, 119, -1, 120, + -1, 121, -1, 120, 38, 126, 92, -1, 120, 37, + 138, 93, -1, 38, 92, -1, 37, 138, 93, -1, + 38, 119, 92, -1, 141, -1, 94, 125, 95, -1, + 37, 139, 93, -1, 36, 172, -1, 123, 5, -1, + 122, 4, -1, 124, 122, 4, -1, 123, -1, 124, + 123, -1, 124, -1, 122, -1, 124, 122, -1, -1, + 127, -1, 170, -1, 153, 118, -1, 153, 105, -1, + 36, 36, 36, -1, 127, 4, 127, -1, 94, 129, + 95, -1, -1, 129, 107, -1, 129, 132, -1, 131, + -1, 130, 131, -1, 56, 141, 17, -1, 59, 17, + -1, 42, 17, -1, 1, 3, -1, 134, -1, 130, + 134, -1, 137, -1, 154, 108, -1, 137, 3, -1, + -1, 135, 128, -1, 67, 38, 140, 92, 132, -1, + 67, 38, 140, 92, 132, 62, 132, -1, -1, 136, + 65, 38, 133, 3, 137, 3, 137, 92, 132, -1, + 82, 38, 140, 92, 132, -1, 60, 132, 82, 38, + 140, 92, 3, -1, 71, 137, 3, -1, 77, 38, + 140, 92, 132, -1, 55, 3, -1, 58, 3, -1, + 66, 172, 3, -1, 74, 38, 147, 92, 3, -1, + 88, 38, 147, 92, 3, -1, -1, 140, -1, -1, + 139, -1, 141, -1, 141, -1, 140, 4, 140, -1, + 142, -1, 141, 33, 141, -1, 141, 34, 141, -1, + 141, 35, 141, -1, 141, 31, 141, -1, 141, 32, + 141, -1, 141, 29, 141, -1, 141, 30, 141, -1, + 141, 25, 141, -1, 141, 26, 141, -1, 141, 28, + 141, -1, 141, 27, 141, -1, 141, 24, 141, -1, + 141, 23, 141, -1, 141, 22, 141, -1, 141, 21, + 141, -1, 141, 20, 141, -1, 141, 19, 141, -1, + 141, 18, 141, -1, 141, 16, 140, 17, 141, -1, + 141, 5, 141, -1, 141, 15, 141, -1, 141, 14, + 141, -1, 141, 13, 141, -1, 141, 12, 141, -1, + 141, 11, 141, -1, 141, 9, 141, -1, 141, 10, + 141, -1, 141, 8, 141, -1, 141, 7, 141, -1, + 141, 6, 141, -1, 143, -1, 38, 153, 118, 92, + 142, -1, 38, 153, 118, 92, 94, 125, 95, -1, + 144, -1, 33, 142, -1, 22, 142, -1, 31, 142, + -1, 32, 142, -1, 96, 142, -1, 97, 142, -1, + 40, 142, -1, 41, 142, -1, 73, 143, -1, 89, + 143, -1, 38, 140, 92, -1, 73, 38, 153, 118, + 92, -1, 89, 38, 153, 118, 92, -1, 144, 38, + 147, 92, -1, 144, 37, 140, 93, -1, 144, 39, + 172, -1, 144, 36, 172, -1, 144, 40, -1, 144, + 41, -1, 170, -1, 46, -1, 47, -1, 48, -1, + 49, -1, 45, -1, 44, -1, 50, -1, 51, -1, + 145, -1, 146, -1, 52, -1, 145, 52, -1, 53, + -1, 146, 53, -1, -1, 148, -1, 141, -1, 148, + 4, 148, -1, -1, 94, 150, 112, 95, -1, -1, + 154, -1, 155, -1, 167, -1, 164, -1, 155, 161, + -1, 167, 161, -1, 164, 155, 162, -1, 164, 167, + -1, 164, 167, 161, -1, 152, -1, 152, -1, 76, + 172, -1, -1, 76, 172, 156, 149, -1, 76, 149, + -1, 80, 172, -1, -1, 80, 172, 157, 149, -1, + 80, 149, -1, 84, 172, -1, -1, -1, 84, 172, + 158, 94, 159, 166, 95, -1, -1, 84, 94, 160, + 166, 95, -1, 43, -1, 163, -1, 161, 163, -1, + -1, 162, 169, -1, 167, -1, 169, -1, 168, -1, + 165, -1, 164, 165, -1, 169, -1, 168, -1, 42, + -1, 42, 5, 141, -1, 166, 4, -1, 166, 4, + 166, -1, 57, -1, 72, -1, 68, -1, 69, -1, + 85, -1, 81, -1, 64, -1, 61, -1, 83, -1, + 54, -1, 75, -1, 63, -1, 78, -1, 79, -1, + 70, -1, 91, -1, 86, -1, 87, -1, 90, -1, + 42, -1, 172, -1, 42, -1, 43, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 101, 101, 102, 108, 112, 114, 128, 113, 143, + 148, 147, 155, 158, 159, 166, 167, 171, 175, 184, + 188, 194, 200, 199, 211, 224, 225, 228, 232, 239, + 238, 244, 243, 250, 254, 257, 261, 264, 269, 273, + 282, 285, 288, 293, 298, 301, 302, 306, 312, 316, + 320, 326, 327, 333, 337, 342, 345, 346, 350, 351, + 357, 358, 359, 365, 368, 375, 376, 381, 386, 390, + 396, 406, 409, 413, 419, 420, 426, 430, 434, 440, + 444, 445, 451, 452, 458, 459, 459, 470, 476, 484, + 484, 495, 499, 503, 508, 522, 526, 530, 534, 538, + 544, 547, 550, 553, 556, 563, 564, 570, 571, 575, + 579, 583, 587, 591, 595, 599, 603, 607, 611, 615, + 619, 623, 627, 631, 635, 639, 643, 647, 651, 655, + 659, 663, 667, 671, 675, 679, 683, 687, 693, 694, + 701, 709, 710, 714, 718, 722, 726, 730, 734, 738, + 742, 746, 752, 756, 762, 768, 776, 780, 785, 790, + 794, 798, 799, 806, 813, 820, 827, 834, 841, 848, + 855, 856, 859, 869, 887, 897, 915, 918, 921, 922, + 929, 928, 951, 955, 958, 963, 968, 974, 982, 988, + 994, 1000, 1008, 1016, 1023, 1029, 1028, 1040, 1048, 1054, + 1053, 1065, 1073, 1082, 1086, 1081, 1103, 1102, 1111, 1117, + 1118, 1124, 1127, 1133, 1134, 1135, 1138, 1139, 1145, 1146, + 1149, 1153, 1157, 1158, 1161, 1162, 1163, 1164, 1165, 1166, + 1167, 1168, 1169, 1172, 1173, 1174, 1175, 1176, 1177, 1178, + 1181, 1182, 1183, 1186, 1201, 1213, 1214 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "';'", "','", "'='", "LORE", "LXORE", + "LANDE", "LLSHE", "LRSHE", "LMDE", "LDVE", "LMLE", "LME", "LPE", "'?'", + "':'", "LOROR", "LANDAND", "'|'", "'^'", "'&'", "LNE", "LEQ", "'<'", + "'>'", "LGE", "LLE", "LRSH", "LLSH", "'+'", "'-'", "'*'", "'/'", "'%'", + "'.'", "'['", "'('", "LMG", "LPP", "LMM", "LNAME", "LTYPE", "LFCONST", + "LDCONST", "LCONST", "LLCONST", "LUCONST", "LULCONST", "LVLCONST", + "LUVLCONST", "LSTRING", "LLSTRING", "LAUTO", "LBREAK", "LCASE", "LCHAR", + "LCONTINUE", "LDEFAULT", "LDO", "LDOUBLE", "LELSE", "LEXTERN", "LFLOAT", + "LFOR", "LGOTO", "LIF", "LINT", "LLONG", "LREGISTER", "LRETURN", + "LSHORT", "LSIZEOF", "LUSED", "LSTATIC", "LSTRUCT", "LSWITCH", + "LTYPEDEF", "LTYPESTR", "LUNION", "LUNSIGNED", "LWHILE", "LVOID", + "LENUM", "LSIGNED", "LCONSTNT", "LVOLATILE", "LSET", "LSIGNOF", + "LRESTRICT", "LINLINE", "')'", "']'", "'{'", "'}'", "'!'", "'~'", + "$accept", "prog", "xdecl", "@1", "@2", "xdlist", "@3", "xdecor", + "xdecor2", "adecl", "adlist", "@4", "pdecl", "pdlist", "edecl", "@5", + "@6", "zedlist", "edlist", "edecor", "abdecor", "abdecor1", "abdecor2", + "abdecor3", "init", "qual", "qlist", "ilist", "zarglist", "arglist", + "block", "slist", "labels", "label", "stmnt", "forexpr", "ulstmnt", "@7", + "@8", "zcexpr", "zexpr", "lexpr", "cexpr", "expr", "xuexpr", "uexpr", + "pexpr", "string", "lstring", "zelist", "elist", "sbody", "@9", + "zctlist", "types", "tlist", "ctlist", "complex", "@10", "@11", "@12", + "@13", "@14", "gctnlist", "zgnlist", "gctname", "gcnlist", "gcname", + "enum", "tname", "cname", "gname", "name", "tag", "ltag", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 59, 44, 61, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 63, 58, 268, 269, + 124, 94, 38, 270, 271, 60, 62, 272, 273, 274, + 275, 43, 45, 42, 47, 37, 46, 91, 40, 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, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, + 327, 328, 41, 93, 123, 125, 33, 126 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 98, 99, 99, 100, 100, 101, 102, 100, 103, + 104, 103, 103, 105, 105, 106, 106, 106, 106, 107, + 107, 108, 109, 108, 108, 110, 110, 111, 111, 113, + 112, 114, 112, 115, 115, 116, 116, 117, 117, 117, + 118, 118, 119, 119, 119, 120, 120, 120, 121, 121, + 121, 122, 122, 123, 123, 123, 124, 124, 124, 124, + 125, 125, 125, 126, 126, 127, 127, 127, 127, 127, + 128, 129, 129, 129, 130, 130, 131, 131, 131, 132, + 132, 132, 133, 133, 134, 135, 134, 134, 134, 136, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, + 137, 137, 138, 138, 139, 140, 140, 141, 141, 141, + 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, + 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, + 141, 141, 141, 141, 141, 141, 141, 141, 142, 142, + 142, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, + 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, + 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, + 150, 149, 151, 151, 152, 152, 152, 152, 152, 152, + 152, 152, 153, 154, 155, 156, 155, 155, 155, 157, + 155, 155, 155, 158, 159, 155, 160, 155, 155, 161, + 161, 162, 162, 163, 163, 163, 164, 164, 165, 165, + 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, + 167, 167, 167, 168, 168, 168, 168, 168, 168, 168, + 169, 169, 169, 170, 171, 172, 172 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 0, 2, 2, 3, 0, 0, 6, 1, + 0, 4, 3, 1, 3, 1, 3, 4, 4, 2, + 3, 1, 0, 4, 3, 0, 4, 1, 3, 0, + 4, 0, 5, 0, 1, 1, 3, 1, 3, 2, + 0, 1, 2, 3, 1, 1, 4, 4, 2, 3, + 3, 1, 3, 3, 2, 2, 2, 3, 1, 2, + 1, 1, 2, 0, 1, 1, 2, 2, 3, 3, + 3, 0, 2, 2, 1, 2, 3, 2, 2, 2, + 1, 2, 1, 2, 2, 0, 2, 5, 7, 0, + 10, 5, 7, 3, 5, 2, 2, 3, 5, 5, + 0, 1, 0, 1, 1, 1, 3, 1, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 5, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 1, 5, + 7, 1, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 3, 5, 5, 4, 4, 3, 3, 2, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 1, 2, 0, 1, 1, 3, + 0, 4, 0, 1, 1, 1, 1, 2, 2, 3, + 2, 3, 1, 1, 2, 0, 4, 2, 2, 0, + 4, 2, 2, 0, 0, 7, 0, 5, 1, 1, + 2, 0, 2, 1, 1, 1, 1, 2, 1, 1, + 1, 3, 2, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1 +}; + +/* 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_uint8 yydefact[] = +{ + 2, 182, 1, 208, 233, 224, 231, 235, 230, 226, + 227, 238, 225, 234, 0, 236, 237, 0, 229, 232, + 0, 228, 240, 241, 242, 239, 3, 0, 193, 183, + 184, 186, 216, 185, 219, 218, 245, 246, 180, 197, + 194, 201, 198, 206, 202, 4, 211, 0, 0, 6, + 13, 15, 244, 187, 209, 213, 215, 214, 211, 217, + 190, 188, 0, 0, 0, 0, 0, 0, 0, 5, + 0, 25, 0, 102, 63, 210, 189, 191, 0, 192, + 29, 196, 200, 220, 0, 204, 14, 212, 16, 12, + 9, 7, 0, 0, 0, 0, 0, 0, 0, 0, + 243, 167, 166, 162, 163, 164, 165, 168, 169, 172, + 174, 0, 0, 0, 0, 0, 103, 104, 107, 138, + 141, 170, 171, 161, 0, 0, 64, 40, 65, 181, + 31, 33, 0, 222, 207, 0, 0, 0, 0, 11, + 51, 143, 144, 145, 142, 0, 105, 40, 148, 149, + 0, 150, 0, 151, 146, 147, 18, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, + 0, 159, 160, 173, 175, 0, 17, 0, 211, 102, + 0, 67, 66, 41, 44, 45, 33, 0, 37, 0, + 34, 35, 15, 221, 223, 0, 71, 8, 27, 0, + 0, 0, 61, 58, 60, 0, 0, 152, 211, 0, + 0, 40, 40, 127, 137, 136, 135, 133, 134, 132, + 131, 130, 129, 128, 0, 125, 124, 123, 122, 121, + 120, 119, 115, 116, 118, 117, 113, 114, 111, 112, + 108, 109, 110, 158, 0, 178, 0, 177, 157, 68, + 69, 42, 0, 48, 0, 102, 63, 0, 39, 30, + 0, 0, 205, 0, 26, 0, 54, 0, 56, 55, + 62, 59, 52, 106, 42, 0, 0, 0, 0, 156, + 155, 0, 43, 49, 50, 0, 0, 32, 36, 38, + 0, 243, 0, 0, 0, 0, 0, 0, 0, 100, + 0, 0, 0, 0, 70, 72, 85, 74, 73, 80, + 0, 0, 0, 101, 0, 28, 53, 57, 0, 139, + 153, 154, 126, 179, 47, 46, 79, 78, 95, 0, + 96, 77, 0, 0, 0, 0, 176, 0, 0, 176, + 75, 81, 86, 0, 84, 19, 21, 0, 0, 76, + 0, 97, 0, 93, 0, 0, 0, 0, 100, 0, + 20, 0, 140, 0, 0, 0, 0, 0, 0, 0, + 82, 0, 0, 24, 0, 87, 98, 94, 91, 99, + 100, 83, 23, 0, 0, 0, 92, 88, 100, 0, + 0, 90 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 1, 26, 71, 136, 48, 72, 208, 50, 325, + 367, 379, 91, 219, 78, 131, 206, 209, 210, 211, + 202, 203, 204, 205, 222, 223, 224, 225, 125, 126, + 217, 283, 326, 327, 328, 389, 329, 330, 331, 332, + 115, 116, 333, 146, 118, 119, 120, 121, 122, 266, + 267, 39, 62, 27, 79, 127, 29, 30, 63, 64, + 66, 135, 65, 53, 67, 54, 31, 32, 84, 33, + 34, 35, 123, 51, 52 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -366 +static const yytype_int16 yypact[] = +{ + -366, 541, -366, -366, -366, -366, -366, -366, -366, -366, + -366, -366, -366, -366, -3, -366, -366, -3, -366, -366, + 140, -366, -366, -366, -366, -366, -366, 161, -366, -366, + 949, 914, -366, 949, -366, -366, -366, -366, -366, -366, + -64, -366, -60, -366, -17, -366, -366, 21, 70, 316, + 51, -366, -366, 949, -366, -366, -366, -366, -366, -366, + 949, 949, 914, -13, -13, 57, 9, 154, 54, -366, + 21, -366, 164, 745, 836, -366, -41, 949, 875, -366, + -366, -366, -366, 167, 7, -366, -366, -366, -366, -366, + 181, 914, 677, 745, 745, 745, 745, 607, 745, 745, + -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, + -366, 779, 813, 745, 745, 95, -366, 1067, -366, -366, + 422, 141, 145, -366, 176, 139, 225, 128, -366, -366, + -366, 289, 745, 57, -366, 57, 149, 21, 169, -366, + 1067, -366, -366, -366, -366, 31, 1067, 130, -366, -366, + 607, -366, 607, -366, -366, -366, -366, 745, 745, 745, + 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, + 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, + 745, 745, 745, 745, 745, 745, 745, 53, 745, 745, + 53, -366, -366, -366, -366, 199, -366, 836, -366, 745, + 147, -366, -366, -366, 111, -366, 289, 745, -366, 243, + 247, -366, 235, 1067, -366, 13, -366, -366, -366, 222, + 53, 745, 260, 255, 169, 172, 745, -366, -366, -6, + 180, 130, 130, 1067, 1067, 1067, 1067, 1067, 1067, 1067, + 1067, 1067, 1067, 1067, 16, 1084, 1100, 1115, 1129, 1142, + 1153, 1153, 319, 319, 319, 319, 303, 303, 295, 295, + -366, -366, -366, -366, 11, 1067, 186, 269, -366, -366, + -366, 190, 188, -366, 187, 745, 836, 280, -366, -366, + 289, 745, -366, 338, -366, 21, -366, 191, -366, -366, + 284, 255, -366, -366, 217, 711, 202, 204, 745, -366, + -366, 745, -366, -366, -366, 198, 207, -366, -366, -366, + 309, 296, 312, 745, 320, 307, 435, 53, 302, 745, + 305, 306, 308, 318, -366, -366, 504, -366, -366, -366, + 149, 292, 342, 354, 259, -366, -366, -366, 169, -366, + -366, -366, 421, -366, -366, -366, -366, -366, -366, 1036, + -366, -366, 277, 358, 745, 359, 745, 745, 745, 745, + -366, -366, -366, 325, -366, -366, 361, 234, 272, -366, + 326, -366, 64, -366, 276, 66, 67, 281, 607, 367, + -366, 21, -366, 745, 435, 371, 435, 435, 372, 397, + -366, 21, 677, -366, 68, 368, -366, -366, -366, -366, + 745, 427, -366, 461, 435, 462, -366, -366, 745, 377, + 435, -366 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -366, -366, -366, -366, -366, 400, -366, -26, -366, -366, + -365, -366, -366, 189, -366, -366, -366, 266, 209, -366, + -122, -187, -366, -366, -82, 254, -366, 133, 216, 299, + 168, -366, -366, 171, -304, -366, 173, -366, -366, -227, + -181, -183, -83, -45, -38, 137, -366, -366, -366, -308, + 203, 2, -366, -366, -1, 0, -88, 472, -366, -366, + -366, -366, -366, -10, -51, 208, -366, 474, 22, 256, + 265, -24, -52, -127, -12 +}; + +/* 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 -204 +static const yytype_int16 yytable[] = +{ + 28, 49, 40, 137, 212, 42, 57, 76, 44, 57, + 139, 133, 352, 274, 145, 226, 393, 133, 272, 41, + 226, 68, 128, 61, 278, 230, 401, 228, 117, 57, + -195, 199, 229, 298, -199, 226, 57, 57, 287, 36, + 37, 86, 274, 87, 90, 22, 23, 140, 374, 24, + 77, 377, 87, 57, 46, 141, 142, 143, 144, 47, + 148, 149, 80, 36, 37, 81, 82, 145, 226, 145, + 226, 226, 226, 69, 70, 154, 155, -203, 130, 212, + 395, 38, 397, 398, 302, 244, 273, 213, 73, 74, + 28, 38, 355, 140, 305, 36, 37, 147, 309, 83, + 407, 201, 134, 85, 299, 264, 411, 302, 282, 296, + 297, 218, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 227, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, + 261, 262, 290, 293, 265, 128, 88, 271, 275, 276, + 231, 390, 232, 212, 117, 214, 384, 215, 386, 387, + 403, 198, 117, 228, 45, 199, 200, 199, 229, 92, + 36, 37, 132, 405, 68, 263, 117, 294, 268, 140, + 198, 409, 36, 37, 199, 200, -10, 46, 156, 36, + 37, 93, 47, 193, 46, 334, 36, 37, 194, 47, + 94, 95, 96, 36, 37, 220, 221, 97, 286, 98, + 99, 100, 195, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 198, 128, 284, 285, 199, 200, 197, + 117, 196, 36, 37, 43, 269, 117, 380, 381, 273, + 22, 23, 111, 216, 24, 86, 279, 87, 151, 153, + 228, 280, 281, 342, 199, 229, 265, 339, 112, 218, + 289, 75, 365, 138, 288, 113, 114, 292, 349, 75, + 87, 372, 295, 301, 375, 376, 22, 23, 300, 304, + 24, 303, 28, 307, 336, 75, 55, 60, 337, 55, + 391, 344, 46, 140, 340, 56, 341, 47, 56, 345, + 394, 36, 37, 22, 23, 353, 207, 24, 366, 55, + 402, 265, 346, 347, 265, 348, 55, 55, 56, -9, + -9, -10, 46, 350, 351, 56, 56, 47, 184, 185, + 186, 36, 37, 55, 182, 183, 184, 185, 186, 310, + 354, -100, 56, 356, 357, 364, 358, 140, 180, 181, + 182, 183, 184, 185, 186, 366, 359, 363, 226, 370, + 93, 371, 373, 378, 383, 366, -22, 382, 385, 94, + 95, 96, 392, 388, 396, 399, 97, 28, 98, 99, + 311, 3, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 4, 312, 313, 5, 314, 315, 316, 6, + 400, 7, 8, -89, 317, 318, 9, 10, 11, 319, + 12, 111, 320, 13, 14, 321, 15, 16, 17, 18, + 322, 19, 20, 21, 22, 23, 323, 112, 24, 25, + 404, 381, -85, 324, 113, 114, 310, 168, -100, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 93, 187, 188, + 189, 190, 191, 192, 406, 408, 94, 95, 96, 410, + 89, 368, 277, 97, 335, 98, 99, 311, 291, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 308, + 312, 313, 306, 314, 315, 316, 270, 360, 362, 361, + -89, 317, 318, 58, 343, 59, 319, -100, 111, 320, + 0, 0, 321, 0, 0, 0, 0, 322, 0, 0, + 0, 0, 0, 323, 112, 0, 93, 0, 0, -85, + 0, 113, 114, 0, 0, 94, 95, 96, 0, 0, + 0, 2, 97, 0, 98, 99, 311, 0, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 0, 312, + 313, 0, 314, 315, 316, 0, 0, 0, 0, -89, + 317, 318, 0, 0, 0, 319, 0, 111, 320, 0, + 0, 321, 0, 0, 3, 0, 322, 0, 0, 0, + 0, 0, 323, 112, 0, 4, 0, 0, 5, 0, + 113, 114, 6, 0, 7, 8, 0, 0, 0, 9, + 10, 11, 0, 12, 0, 0, 13, 14, 0, 15, + 16, 17, 18, 0, 19, 20, 21, 22, 23, 93, + 0, 24, 25, 0, 0, 0, 0, 0, 94, 95, + 96, 0, 0, 0, 0, 97, 0, 98, 99, 100, + 3, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 4, 0, 0, 5, 0, 0, 0, 6, 0, + 7, 8, 0, 0, 0, 9, 10, 11, 0, 12, + 111, 0, 13, 14, 0, 15, 16, 17, 18, 0, + 19, 20, 21, 22, 23, 0, 112, 24, 25, 93, + 0, 0, 0, 113, 114, 0, 0, 0, 94, 95, + 96, 0, 0, 0, 0, 97, 0, 98, 99, 100, + 0, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 0, 0, 93, 0, 0, 0, 0, 0, 0, + 0, 0, 94, 95, 96, 0, 0, 0, 0, 97, + 111, 98, 99, 100, 0, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 0, 112, 93, 0, 0, + 0, 138, 0, 113, 114, 0, 94, 95, 96, 0, + 0, 0, 0, 97, 111, 98, 99, 100, 0, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 0, + 112, 93, 0, 0, 0, 338, 0, 113, 114, 0, + 94, 95, 96, 0, 0, 0, 0, 150, 111, 98, + 99, 100, 0, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 0, 112, 93, 0, 0, 0, 0, + 0, 113, 114, 0, 94, 95, 96, 0, 0, 0, + 0, 152, 111, 98, 99, 100, 0, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 0, 112, 0, + 0, 0, 124, 0, 0, 113, 114, 0, 100, 3, + 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, + 4, 0, 0, 5, 0, 0, 0, 6, 0, 7, + 8, 0, 112, 0, 9, 10, 11, 0, 12, 113, + 114, 13, 14, 0, 15, 16, 17, 18, 3, 19, + 20, 21, 22, 23, 0, 0, 24, 25, 0, 4, + 0, 0, 5, 0, 0, 0, 6, 0, 7, 8, + 0, 0, 0, 9, 10, 11, 0, 12, 0, 0, + 13, 14, 0, 15, 16, 17, 18, 3, 19, 20, + 21, 22, 23, 0, 0, 24, 25, 0, 4, 0, + 129, 5, 0, 0, 0, 6, 0, 7, 8, 0, + 0, 0, 9, 10, 11, 0, 12, 0, 0, 13, + 14, 0, 15, 16, 17, 18, 0, 19, 20, 21, + 22, 23, 0, 4, 24, 25, 5, 0, 0, 0, + 6, 0, 7, 8, 0, 0, 0, 9, 10, 11, + 0, 12, 0, 0, 13, 0, 0, 15, 16, 0, + 18, 0, 19, 0, 21, 22, 23, 0, 0, 24, + 25, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 369, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 0, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, + 181, 182, 183, 184, 185, 186, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186 +}; + +static const yytype_int16 yycheck[] = +{ + 1, 27, 14, 91, 131, 17, 30, 58, 20, 33, + 92, 4, 316, 200, 97, 4, 381, 4, 199, 17, + 4, 47, 74, 33, 207, 147, 391, 33, 73, 53, + 94, 37, 38, 17, 94, 4, 60, 61, 221, 42, + 43, 67, 229, 67, 70, 86, 87, 92, 356, 90, + 60, 359, 76, 77, 33, 93, 94, 95, 96, 38, + 98, 99, 62, 42, 43, 63, 64, 150, 4, 152, + 4, 4, 4, 3, 4, 113, 114, 94, 78, 206, + 384, 94, 386, 387, 271, 168, 92, 132, 37, 38, + 91, 94, 319, 138, 275, 42, 43, 97, 281, 42, + 404, 127, 95, 94, 93, 188, 410, 294, 95, 231, + 232, 137, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 92, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 224, 226, 189, 197, 92, 198, 37, 38, + 150, 378, 152, 280, 199, 133, 92, 135, 92, 92, + 92, 33, 207, 33, 3, 37, 38, 37, 38, 5, + 42, 43, 5, 400, 200, 187, 221, 228, 190, 224, + 33, 408, 42, 43, 37, 38, 5, 33, 93, 42, + 43, 22, 38, 52, 33, 283, 42, 43, 53, 38, + 31, 32, 33, 42, 43, 36, 37, 38, 220, 40, + 41, 42, 36, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 33, 276, 3, 4, 37, 38, 4, + 275, 92, 42, 43, 94, 36, 281, 3, 4, 92, + 86, 87, 73, 94, 90, 271, 3, 271, 111, 112, + 33, 4, 17, 298, 37, 38, 301, 295, 89, 285, + 5, 53, 3, 94, 4, 96, 97, 95, 313, 61, + 294, 354, 92, 4, 357, 358, 86, 87, 92, 92, + 90, 93, 283, 3, 93, 77, 30, 31, 4, 33, + 378, 93, 33, 338, 92, 30, 92, 38, 33, 92, + 383, 42, 43, 86, 87, 317, 17, 90, 334, 53, + 392, 356, 3, 17, 359, 3, 60, 61, 53, 3, + 4, 5, 33, 3, 17, 60, 61, 38, 33, 34, + 35, 42, 43, 77, 31, 32, 33, 34, 35, 1, + 38, 3, 77, 38, 38, 3, 38, 392, 29, 30, + 31, 32, 33, 34, 35, 381, 38, 65, 4, 82, + 22, 3, 3, 38, 38, 391, 5, 95, 92, 31, + 32, 33, 5, 92, 3, 3, 38, 378, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 3, 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, + 62, 4, 94, 95, 96, 97, 1, 16, 3, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 22, 36, 37, + 38, 39, 40, 41, 3, 3, 31, 32, 33, 92, + 70, 338, 206, 38, 285, 40, 41, 42, 224, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 280, + 55, 56, 276, 58, 59, 60, 197, 326, 330, 326, + 65, 66, 67, 31, 301, 31, 71, 3, 73, 74, + -1, -1, 77, -1, -1, -1, -1, 82, -1, -1, + -1, -1, -1, 88, 89, -1, 22, -1, -1, 94, + -1, 96, 97, -1, -1, 31, 32, 33, -1, -1, + -1, 0, 38, -1, 40, 41, 42, -1, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, -1, 55, + 56, -1, 58, 59, 60, -1, -1, -1, -1, 65, + 66, 67, -1, -1, -1, 71, -1, 73, 74, -1, + -1, 77, -1, -1, 43, -1, 82, -1, -1, -1, + -1, -1, 88, 89, -1, 54, -1, -1, 57, -1, + 96, 97, 61, -1, 63, 64, -1, -1, -1, 68, + 69, 70, -1, 72, -1, -1, 75, 76, -1, 78, + 79, 80, 81, -1, 83, 84, 85, 86, 87, 22, + -1, 90, 91, -1, -1, -1, -1, -1, 31, 32, + 33, -1, -1, -1, -1, 38, -1, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, -1, -1, 57, -1, -1, -1, 61, -1, + 63, 64, -1, -1, -1, 68, 69, 70, -1, 72, + 73, -1, 75, 76, -1, 78, 79, 80, 81, -1, + 83, 84, 85, 86, 87, -1, 89, 90, 91, 22, + -1, -1, -1, 96, 97, -1, -1, -1, 31, 32, + 33, -1, -1, -1, -1, 38, -1, 40, 41, 42, + -1, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, -1, -1, 22, -1, -1, -1, -1, -1, -1, + -1, -1, 31, 32, 33, -1, -1, -1, -1, 38, + 73, 40, 41, 42, -1, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, -1, 89, 22, -1, -1, + -1, 94, -1, 96, 97, -1, 31, 32, 33, -1, + -1, -1, -1, 38, 73, 40, 41, 42, -1, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, + 89, 22, -1, -1, -1, 94, -1, 96, 97, -1, + 31, 32, 33, -1, -1, -1, -1, 38, 73, 40, + 41, 42, -1, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, -1, 89, 22, -1, -1, -1, -1, + -1, 96, 97, -1, 31, 32, 33, -1, -1, -1, + -1, 38, 73, 40, 41, 42, -1, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, -1, 89, -1, + -1, -1, 36, -1, -1, 96, 97, -1, 42, 43, + -1, -1, -1, -1, -1, -1, 73, -1, -1, -1, + 54, -1, -1, 57, -1, -1, -1, 61, -1, 63, + 64, -1, 89, -1, 68, 69, 70, -1, 72, 96, + 97, 75, 76, -1, 78, 79, 80, 81, 43, 83, + 84, 85, 86, 87, -1, -1, 90, 91, -1, 54, + -1, -1, 57, -1, -1, -1, 61, -1, 63, 64, + -1, -1, -1, 68, 69, 70, -1, 72, -1, -1, + 75, 76, -1, 78, 79, 80, 81, 43, 83, 84, + 85, 86, 87, -1, -1, 90, 91, -1, 54, -1, + 95, 57, -1, -1, -1, 61, -1, 63, 64, -1, + -1, -1, 68, 69, 70, -1, 72, -1, -1, 75, + 76, -1, 78, 79, 80, 81, -1, 83, 84, 85, + 86, 87, -1, 54, 90, 91, 57, -1, -1, -1, + 61, -1, 63, 64, -1, -1, -1, 68, 69, 70, + -1, 72, -1, -1, 75, -1, -1, 78, 79, -1, + 81, -1, 83, -1, 85, 86, 87, -1, -1, 90, + 91, 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, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, -1, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 99, 0, 43, 54, 57, 61, 63, 64, 68, + 69, 70, 72, 75, 76, 78, 79, 80, 81, 83, + 84, 85, 86, 87, 90, 91, 100, 151, 152, 154, + 155, 164, 165, 167, 168, 169, 42, 43, 94, 149, + 172, 149, 172, 94, 172, 3, 33, 38, 103, 105, + 106, 171, 172, 161, 163, 167, 168, 169, 155, 165, + 167, 161, 150, 156, 157, 160, 158, 162, 105, 3, + 4, 101, 104, 37, 38, 163, 162, 161, 112, 152, + 153, 149, 149, 42, 166, 94, 105, 169, 92, 103, + 105, 110, 5, 22, 31, 32, 33, 38, 40, 41, + 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 73, 89, 96, 97, 138, 139, 141, 142, 143, + 144, 145, 146, 170, 36, 126, 127, 153, 170, 95, + 153, 113, 5, 4, 95, 159, 102, 154, 94, 122, + 141, 142, 142, 142, 142, 140, 141, 153, 142, 142, + 38, 143, 38, 143, 142, 142, 93, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 52, 53, 36, 92, 4, 33, 37, + 38, 105, 118, 119, 120, 121, 114, 17, 105, 115, + 116, 117, 171, 141, 166, 166, 94, 128, 105, 111, + 36, 37, 122, 123, 124, 125, 4, 92, 33, 38, + 118, 153, 153, 141, 141, 141, 141, 141, 141, 141, + 141, 141, 141, 141, 140, 141, 141, 141, 141, 141, + 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, + 141, 141, 141, 172, 140, 141, 147, 148, 172, 36, + 127, 162, 138, 92, 119, 37, 38, 115, 139, 3, + 4, 17, 95, 129, 3, 4, 172, 139, 4, 5, + 122, 123, 95, 140, 162, 92, 118, 118, 17, 93, + 92, 4, 119, 93, 92, 138, 126, 3, 116, 139, + 1, 42, 55, 56, 58, 59, 60, 66, 67, 71, + 74, 77, 82, 88, 95, 107, 130, 131, 132, 134, + 135, 136, 137, 140, 154, 111, 93, 4, 94, 142, + 92, 92, 141, 148, 93, 92, 3, 17, 3, 141, + 3, 17, 132, 172, 38, 137, 38, 38, 38, 38, + 131, 134, 128, 65, 3, 3, 105, 108, 125, 17, + 82, 3, 140, 3, 147, 140, 140, 147, 38, 109, + 3, 4, 95, 38, 92, 92, 92, 92, 92, 133, + 137, 154, 5, 108, 140, 132, 3, 132, 132, 3, + 3, 108, 122, 92, 62, 137, 3, 132, 3, 137, + 92, 132 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* 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. */ + +#define YYFAIL goto yyerrlab + +#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 \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#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. */ + +#ifndef YY_LOCATION_PRINT +# 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 + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# 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]; + + 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; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + 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. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* 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 look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* 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]; + char *yymsg = yymsgbuf; + 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; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + 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; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + 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 + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + 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 look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 4: +#line 109 "cc.y" + { + dodecl(xdecl, lastclass, lasttype, Z); + } + break; + + case 6: +#line 114 "cc.y" + { + lastdcl = T; + firstarg = S; + dodecl(xdecl, lastclass, lasttype, (yyvsp[(2) - (2)].node)); + if(lastdcl == T || lastdcl->etype != TFUNC) { + diag((yyvsp[(2) - (2)].node), "not a function"); + lastdcl = types[TFUNC]; + } + thisfn = lastdcl; + markdcl(); + firstdcl = dclstack; + argmark((yyvsp[(2) - (2)].node), 0); + } + break; + + case 7: +#line 128 "cc.y" + { + argmark((yyvsp[(2) - (4)].node), 1); + } + break; + + case 8: +#line 132 "cc.y" + { + Node *n; + + n = revertdcl(); + if(n) + (yyvsp[(6) - (6)].node) = new(OLIST, n, (yyvsp[(6) - (6)].node)); + if(!debug['a'] && !debug['Z']) + codgen((yyvsp[(6) - (6)].node), (yyvsp[(2) - (6)].node)); + } + break; + + case 9: +#line 144 "cc.y" + { + dodecl(xdecl, lastclass, lasttype, (yyvsp[(1) - (1)].node)); + } + break; + + case 10: +#line 148 "cc.y" + { + (yyvsp[(1) - (1)].node) = dodecl(xdecl, lastclass, lasttype, (yyvsp[(1) - (1)].node)); + } + break; + + case 11: +#line 152 "cc.y" + { + doinit((yyvsp[(1) - (4)].node)->sym, (yyvsp[(1) - (4)].node)->type, 0L, (yyvsp[(4) - (4)].node)); + } + break; + + case 14: +#line 160 "cc.y" + { + (yyval.node) = new(OIND, (yyvsp[(3) - (3)].node), Z); + (yyval.node)->garb = simpleg((yyvsp[(2) - (3)].lval)); + } + break; + + case 16: +#line 168 "cc.y" + { + (yyval.node) = (yyvsp[(2) - (3)].node); + } + break; + + case 17: +#line 172 "cc.y" + { + (yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); + } + break; + + case 18: +#line 176 "cc.y" + { + (yyval.node) = new(OARRAY, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); + } + break; + + case 19: +#line 185 "cc.y" + { + (yyval.node) = dodecl(adecl, lastclass, lasttype, Z); + } + break; + + case 20: +#line 189 "cc.y" + { + (yyval.node) = (yyvsp[(2) - (3)].node); + } + break; + + case 21: +#line 195 "cc.y" + { + dodecl(adecl, lastclass, lasttype, (yyvsp[(1) - (1)].node)); + (yyval.node) = Z; + } + break; + + case 22: +#line 200 "cc.y" + { + (yyvsp[(1) - (1)].node) = dodecl(adecl, lastclass, lasttype, (yyvsp[(1) - (1)].node)); + } + break; + + case 23: +#line 204 "cc.y" + { + int32 w; + + w = (yyvsp[(1) - (4)].node)->sym->type->width; + (yyval.node) = doinit((yyvsp[(1) - (4)].node)->sym, (yyvsp[(1) - (4)].node)->type, 0L, (yyvsp[(4) - (4)].node)); + (yyval.node) = contig((yyvsp[(1) - (4)].node)->sym, (yyval.node), w); + } + break; + + case 24: +#line 212 "cc.y" + { + (yyval.node) = (yyvsp[(1) - (3)].node); + if((yyvsp[(3) - (3)].node) != Z) { + (yyval.node) = (yyvsp[(3) - (3)].node); + if((yyvsp[(1) - (3)].node) != Z) + (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + } + break; + + case 27: +#line 229 "cc.y" + { + dodecl(pdecl, lastclass, lasttype, (yyvsp[(1) - (1)].node)); + } + break; + + case 29: +#line 239 "cc.y" + { + lasttype = (yyvsp[(1) - (1)].type); + } + break; + + case 31: +#line 244 "cc.y" + { + lasttype = (yyvsp[(2) - (2)].type); + } + break; + + case 33: +#line 250 "cc.y" + { + lastfield = 0; + edecl(CXXX, lasttype, S); + } + break; + + case 35: +#line 258 "cc.y" + { + dodecl(edecl, CXXX, lasttype, (yyvsp[(1) - (1)].node)); + } + break; + + case 37: +#line 265 "cc.y" + { + lastbit = 0; + firstbit = 1; + } + break; + + case 38: +#line 270 "cc.y" + { + (yyval.node) = new(OBIT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 39: +#line 274 "cc.y" + { + (yyval.node) = new(OBIT, Z, (yyvsp[(2) - (2)].node)); + } + break; + + case 40: +#line 282 "cc.y" + { + (yyval.node) = (Z); + } + break; + + case 42: +#line 289 "cc.y" + { + (yyval.node) = new(OIND, (Z), Z); + (yyval.node)->garb = simpleg((yyvsp[(2) - (2)].lval)); + } + break; + + case 43: +#line 294 "cc.y" + { + (yyval.node) = new(OIND, (yyvsp[(3) - (3)].node), Z); + (yyval.node)->garb = simpleg((yyvsp[(2) - (3)].lval)); + } + break; + + case 46: +#line 303 "cc.y" + { + (yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); + } + break; + + case 47: +#line 307 "cc.y" + { + (yyval.node) = new(OARRAY, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); + } + break; + + case 48: +#line 313 "cc.y" + { + (yyval.node) = new(OFUNC, (Z), Z); + } + break; + + case 49: +#line 317 "cc.y" + { + (yyval.node) = new(OARRAY, (Z), (yyvsp[(2) - (3)].node)); + } + break; + + case 50: +#line 321 "cc.y" + { + (yyval.node) = (yyvsp[(2) - (3)].node); + } + break; + + case 52: +#line 328 "cc.y" + { + (yyval.node) = new(OINIT, invert((yyvsp[(2) - (3)].node)), Z); + } + break; + + case 53: +#line 334 "cc.y" + { + (yyval.node) = new(OARRAY, (yyvsp[(2) - (3)].node), Z); + } + break; + + case 54: +#line 338 "cc.y" + { + (yyval.node) = new(OELEM, Z, Z); + (yyval.node)->sym = (yyvsp[(2) - (2)].sym); + } + break; + + case 57: +#line 347 "cc.y" + { + (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(2) - (3)].node)); + } + break; + + case 59: +#line 352 "cc.y" + { + (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); + } + break; + + case 62: +#line 360 "cc.y" + { + (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); + } + break; + + case 63: +#line 365 "cc.y" + { + (yyval.node) = Z; + } + break; + + case 64: +#line 369 "cc.y" + { + (yyval.node) = invert((yyvsp[(1) - (1)].node)); + } + break; + + case 66: +#line 377 "cc.y" + { + (yyval.node) = new(OPROTO, (yyvsp[(2) - (2)].node), Z); + (yyval.node)->type = (yyvsp[(1) - (2)].type); + } + break; + + case 67: +#line 382 "cc.y" + { + (yyval.node) = new(OPROTO, (yyvsp[(2) - (2)].node), Z); + (yyval.node)->type = (yyvsp[(1) - (2)].type); + } + break; + + case 68: +#line 387 "cc.y" + { + (yyval.node) = new(ODOTDOT, Z, Z); + } + break; + + case 69: +#line 391 "cc.y" + { + (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 70: +#line 397 "cc.y" + { + (yyval.node) = invert((yyvsp[(2) - (3)].node)); + // if($2 != Z) + // $$ = new(OLIST, $2, $$); + if((yyval.node) == Z) + (yyval.node) = new(OLIST, Z, Z); + } + break; + + case 71: +#line 406 "cc.y" + { + (yyval.node) = Z; + } + break; + + case 72: +#line 410 "cc.y" + { + (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); + } + break; + + case 73: +#line 414 "cc.y" + { + (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); + } + break; + + case 75: +#line 421 "cc.y" + { + (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); + } + break; + + case 76: +#line 427 "cc.y" + { + (yyval.node) = new(OCASE, (yyvsp[(2) - (3)].node), Z); + } + break; + + case 77: +#line 431 "cc.y" + { + (yyval.node) = new(OCASE, Z, Z); + } + break; + + case 78: +#line 435 "cc.y" + { + (yyval.node) = new(OLABEL, dcllabel((yyvsp[(1) - (2)].sym), 1), Z); + } + break; + + case 79: +#line 441 "cc.y" + { + (yyval.node) = Z; + } + break; + + case 81: +#line 446 "cc.y" + { + (yyval.node) = new(OLIST, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); + } + break; + + case 83: +#line 453 "cc.y" + { + (yyval.node) = (yyvsp[(2) - (2)].node); + } + break; + + case 85: +#line 459 "cc.y" + { + markdcl(); + } + break; + + case 86: +#line 463 "cc.y" + { + (yyval.node) = revertdcl(); + if((yyval.node)) + (yyval.node) = new(OLIST, (yyval.node), (yyvsp[(2) - (2)].node)); + else + (yyval.node) = (yyvsp[(2) - (2)].node); + } + break; + + case 87: +#line 471 "cc.y" + { + (yyval.node) = new(OIF, (yyvsp[(3) - (5)].node), new(OLIST, (yyvsp[(5) - (5)].node), Z)); + if((yyvsp[(5) - (5)].node) == Z) + warn((yyvsp[(3) - (5)].node), "empty if body"); + } + break; + + case 88: +#line 477 "cc.y" + { + (yyval.node) = new(OIF, (yyvsp[(3) - (7)].node), new(OLIST, (yyvsp[(5) - (7)].node), (yyvsp[(7) - (7)].node))); + if((yyvsp[(5) - (7)].node) == Z) + warn((yyvsp[(3) - (7)].node), "empty if body"); + if((yyvsp[(7) - (7)].node) == Z) + warn((yyvsp[(3) - (7)].node), "empty else body"); + } + break; + + case 89: +#line 484 "cc.y" + { markdcl(); } + break; + + case 90: +#line 485 "cc.y" + { + (yyval.node) = revertdcl(); + if((yyval.node)){ + if((yyvsp[(4) - (10)].node)) + (yyvsp[(4) - (10)].node) = new(OLIST, (yyval.node), (yyvsp[(4) - (10)].node)); + else + (yyvsp[(4) - (10)].node) = (yyval.node); + } + (yyval.node) = new(OFOR, new(OLIST, (yyvsp[(6) - (10)].node), new(OLIST, (yyvsp[(4) - (10)].node), (yyvsp[(8) - (10)].node))), (yyvsp[(10) - (10)].node)); + } + break; + + case 91: +#line 496 "cc.y" + { + (yyval.node) = new(OWHILE, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node)); + } + break; + + case 92: +#line 500 "cc.y" + { + (yyval.node) = new(ODWHILE, (yyvsp[(5) - (7)].node), (yyvsp[(2) - (7)].node)); + } + break; + + case 93: +#line 504 "cc.y" + { + (yyval.node) = new(ORETURN, (yyvsp[(2) - (3)].node), Z); + (yyval.node)->type = thisfn->link; + } + break; + + case 94: +#line 509 "cc.y" + { + (yyval.node) = new(OCONST, Z, Z); + (yyval.node)->vconst = 0; + (yyval.node)->type = types[TINT]; + (yyvsp[(3) - (5)].node) = new(OSUB, (yyval.node), (yyvsp[(3) - (5)].node)); + + (yyval.node) = new(OCONST, Z, Z); + (yyval.node)->vconst = 0; + (yyval.node)->type = types[TINT]; + (yyvsp[(3) - (5)].node) = new(OSUB, (yyval.node), (yyvsp[(3) - (5)].node)); + + (yyval.node) = new(OSWITCH, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node)); + } + break; + + case 95: +#line 523 "cc.y" + { + (yyval.node) = new(OBREAK, Z, Z); + } + break; + + case 96: +#line 527 "cc.y" + { + (yyval.node) = new(OCONTINUE, Z, Z); + } + break; + + case 97: +#line 531 "cc.y" + { + (yyval.node) = new(OGOTO, dcllabel((yyvsp[(2) - (3)].sym), 0), Z); + } + break; + + case 98: +#line 535 "cc.y" + { + (yyval.node) = new(OUSED, (yyvsp[(3) - (5)].node), Z); + } + break; + + case 99: +#line 539 "cc.y" + { + (yyval.node) = new(OSET, (yyvsp[(3) - (5)].node), Z); + } + break; + + case 100: +#line 544 "cc.y" + { + (yyval.node) = Z; + } + break; + + case 102: +#line 550 "cc.y" + { + (yyval.node) = Z; + } + break; + + case 104: +#line 557 "cc.y" + { + (yyval.node) = new(OCAST, (yyvsp[(1) - (1)].node), Z); + (yyval.node)->type = types[TLONG]; + } + break; + + case 106: +#line 565 "cc.y" + { + (yyval.node) = new(OCOMMA, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 108: +#line 572 "cc.y" + { + (yyval.node) = new(OMUL, (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)); + } + break; + + case 110: +#line 580 "cc.y" + { + (yyval.node) = new(OMOD, (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)); + } + break; + + case 112: +#line 588 "cc.y" + { + (yyval.node) = new(OSUB, (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)); + } + break; + + case 114: +#line 596 "cc.y" + { + (yyval.node) = new(OASHL, (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)); + } + break; + + case 116: +#line 604 "cc.y" + { + (yyval.node) = new(OGT, (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)); + } + break; + + case 118: +#line 612 "cc.y" + { + (yyval.node) = new(OGE, (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)); + } + break; + + case 120: +#line 620 "cc.y" + { + (yyval.node) = new(ONE, (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)); + } + break; + + case 122: +#line 628 "cc.y" + { + (yyval.node) = new(OXOR, (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)); + } + break; + + case 124: +#line 636 "cc.y" + { + (yyval.node) = new(OANDAND, (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)); + } + 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))); + } + break; + + case 127: +#line 648 "cc.y" + { + (yyval.node) = new(OAS, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 128: +#line 652 "cc.y" + { + (yyval.node) = new(OASADD, (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)); + } + break; + + case 130: +#line 660 "cc.y" + { + (yyval.node) = new(OASMUL, (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)); + } + break; + + case 132: +#line 668 "cc.y" + { + (yyval.node) = new(OASMOD, (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)); + } + break; + + case 134: +#line 676 "cc.y" + { + (yyval.node) = new(OASASHR, (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)); + } + break; + + case 136: +#line 684 "cc.y" + { + (yyval.node) = new(OASXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 137: +#line 688 "cc.y" + { + (yyval.node) = new(OASOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 139: +#line 695 "cc.y" + { + (yyval.node) = new(OCAST, (yyvsp[(5) - (5)].node), Z); + dodecl(NODECL, CXXX, (yyvsp[(2) - (5)].type), (yyvsp[(3) - (5)].node)); + (yyval.node)->type = lastdcl; + (yyval.node)->xcast = 1; + } + break; + + case 140: +#line 702 "cc.y" + { + (yyval.node) = new(OSTRUCT, (yyvsp[(6) - (7)].node), Z); + dodecl(NODECL, CXXX, (yyvsp[(2) - (7)].type), (yyvsp[(3) - (7)].node)); + (yyval.node)->type = lastdcl; + } + 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); + } + break; + + case 144: +#line 719 "cc.y" + { + (yyval.node) = new(OPOS, (yyvsp[(2) - (2)].node), Z); + } + break; + + case 145: +#line 723 "cc.y" + { + (yyval.node) = new(ONEG, (yyvsp[(2) - (2)].node), Z); + } + break; + + case 146: +#line 727 "cc.y" + { + (yyval.node) = new(ONOT, (yyvsp[(2) - (2)].node), Z); + } + break; + + case 147: +#line 731 "cc.y" + { + (yyval.node) = new(OCOM, (yyvsp[(2) - (2)].node), Z); + } + break; + + case 148: +#line 735 "cc.y" + { + (yyval.node) = new(OPREINC, (yyvsp[(2) - (2)].node), Z); + } + break; + + case 149: +#line 739 "cc.y" + { + (yyval.node) = new(OPREDEC, (yyvsp[(2) - (2)].node), Z); + } + break; + + case 150: +#line 743 "cc.y" + { + (yyval.node) = new(OSIZE, (yyvsp[(2) - (2)].node), Z); + } + break; + + case 151: +#line 747 "cc.y" + { + (yyval.node) = new(OSIGN, (yyvsp[(2) - (2)].node), Z); + } + break; + + case 152: +#line 753 "cc.y" + { + (yyval.node) = (yyvsp[(2) - (3)].node); + } + break; + + case 153: +#line 757 "cc.y" + { + (yyval.node) = new(OSIZE, Z, Z); + dodecl(NODECL, CXXX, (yyvsp[(3) - (5)].type), (yyvsp[(4) - (5)].node)); + (yyval.node)->type = lastdcl; + } + break; + + case 154: +#line 763 "cc.y" + { + (yyval.node) = new(OSIGN, Z, Z); + dodecl(NODECL, CXXX, (yyvsp[(3) - (5)].type), (yyvsp[(4) - (5)].node)); + (yyval.node)->type = lastdcl; + } + break; + + case 155: +#line 769 "cc.y" + { + (yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), Z); + if((yyvsp[(1) - (4)].node)->op == ONAME) + if((yyvsp[(1) - (4)].node)->type == T) + dodecl(xdecl, CXXX, types[TINT], (yyval.node)); + (yyval.node)->right = invert((yyvsp[(3) - (4)].node)); + } + break; + + case 156: +#line 777 "cc.y" + { + (yyval.node) = new(OIND, new(OADD, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)), Z); + } + break; + + case 157: +#line 781 "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" + { + (yyval.node) = new(ODOT, (yyvsp[(1) - (3)].node), Z); + (yyval.node)->sym = (yyvsp[(3) - (3)].sym); + } + break; + + case 159: +#line 791 "cc.y" + { + (yyval.node) = new(OPOSTINC, (yyvsp[(1) - (2)].node), Z); + } + break; + + case 160: +#line 795 "cc.y" + { + (yyval.node) = new(OPOSTDEC, (yyvsp[(1) - (2)].node), Z); + } + break; + + case 162: +#line 800 "cc.y" + { + (yyval.node) = new(OCONST, Z, Z); + (yyval.node)->type = types[TINT]; + (yyval.node)->vconst = (yyvsp[(1) - (1)].vval); + (yyval.node)->cstring = strdup(symb); + } + break; + + case 163: +#line 807 "cc.y" + { + (yyval.node) = new(OCONST, Z, Z); + (yyval.node)->type = types[TLONG]; + (yyval.node)->vconst = (yyvsp[(1) - (1)].vval); + (yyval.node)->cstring = strdup(symb); + } + break; + + case 164: +#line 814 "cc.y" + { + (yyval.node) = new(OCONST, Z, Z); + (yyval.node)->type = types[TUINT]; + (yyval.node)->vconst = (yyvsp[(1) - (1)].vval); + (yyval.node)->cstring = strdup(symb); + } + break; + + case 165: +#line 821 "cc.y" + { + (yyval.node) = new(OCONST, Z, Z); + (yyval.node)->type = types[TULONG]; + (yyval.node)->vconst = (yyvsp[(1) - (1)].vval); + (yyval.node)->cstring = strdup(symb); + } + break; + + case 166: +#line 828 "cc.y" + { + (yyval.node) = new(OCONST, Z, Z); + (yyval.node)->type = types[TDOUBLE]; + (yyval.node)->fconst = (yyvsp[(1) - (1)].dval); + (yyval.node)->cstring = strdup(symb); + } + break; + + case 167: +#line 835 "cc.y" + { + (yyval.node) = new(OCONST, Z, Z); + (yyval.node)->type = types[TFLOAT]; + (yyval.node)->fconst = (yyvsp[(1) - (1)].dval); + (yyval.node)->cstring = strdup(symb); + } + break; + + case 168: +#line 842 "cc.y" + { + (yyval.node) = new(OCONST, Z, Z); + (yyval.node)->type = types[TVLONG]; + (yyval.node)->vconst = (yyvsp[(1) - (1)].vval); + (yyval.node)->cstring = strdup(symb); + } + break; + + case 169: +#line 849 "cc.y" + { + (yyval.node) = new(OCONST, Z, Z); + (yyval.node)->type = types[TUVLONG]; + (yyval.node)->vconst = (yyvsp[(1) - (1)].vval); + (yyval.node)->cstring = strdup(symb); + } + break; + + case 172: +#line 860 "cc.y" + { + (yyval.node) = new(OSTRING, Z, Z); + (yyval.node)->type = typ(TARRAY, types[TCHAR]); + (yyval.node)->type->width = (yyvsp[(1) - (1)].sval).l + 1; + (yyval.node)->cstring = (yyvsp[(1) - (1)].sval).s; + (yyval.node)->sym = symstring; + (yyval.node)->etype = TARRAY; + (yyval.node)->class = CSTATIC; + } + break; + + case 173: +#line 870 "cc.y" + { + char *s; + int n; + + n = (yyvsp[(1) - (2)].node)->type->width - 1; + s = alloc(n+(yyvsp[(2) - (2)].sval).l+MAXALIGN); + + memcpy(s, (yyvsp[(1) - (2)].node)->cstring, n); + memcpy(s+n, (yyvsp[(2) - (2)].sval).s, (yyvsp[(2) - (2)].sval).l); + s[n+(yyvsp[(2) - (2)].sval).l] = 0; + + (yyval.node) = (yyvsp[(1) - (2)].node); + (yyval.node)->type->width += (yyvsp[(2) - (2)].sval).l; + (yyval.node)->cstring = s; + } + break; + + case 174: +#line 888 "cc.y" + { + (yyval.node) = new(OLSTRING, Z, Z); + (yyval.node)->type = typ(TARRAY, types[TUSHORT]); + (yyval.node)->type->width = (yyvsp[(1) - (1)].sval).l + sizeof(ushort); + (yyval.node)->rstring = (ushort*)(yyvsp[(1) - (1)].sval).s; + (yyval.node)->sym = symstring; + (yyval.node)->etype = TARRAY; + (yyval.node)->class = CSTATIC; + } + break; + + case 175: +#line 898 "cc.y" + { + char *s; + int n; + + n = (yyvsp[(1) - (2)].node)->type->width - sizeof(ushort); + s = alloc(n+(yyvsp[(2) - (2)].sval).l+MAXALIGN); + + memcpy(s, (yyvsp[(1) - (2)].node)->rstring, n); + memcpy(s+n, (yyvsp[(2) - (2)].sval).s, (yyvsp[(2) - (2)].sval).l); + *(ushort*)(s+n+(yyvsp[(2) - (2)].sval).l) = 0; + + (yyval.node) = (yyvsp[(1) - (2)].node); + (yyval.node)->type->width += (yyvsp[(2) - (2)].sval).l; + (yyval.node)->rstring = (ushort*)s; + } + break; + + case 176: +#line 915 "cc.y" + { + (yyval.node) = Z; + } + break; + + case 179: +#line 923 "cc.y" + { + (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 180: +#line 929 "cc.y" + { + (yyval.tyty).t1 = strf; + (yyval.tyty).t2 = strl; + (yyval.tyty).t3 = lasttype; + (yyval.tyty).c = lastclass; + strf = T; + strl = T; + lastbit = 0; + firstbit = 1; + lastclass = CXXX; + lasttype = T; + } + break; + + case 181: +#line 942 "cc.y" + { + (yyval.type) = strf; + strf = (yyvsp[(2) - (4)].tyty).t1; + strl = (yyvsp[(2) - (4)].tyty).t2; + lasttype = (yyvsp[(2) - (4)].tyty).t3; + lastclass = (yyvsp[(2) - (4)].tyty).c; + } + break; + + case 182: +#line 951 "cc.y" + { + lastclass = CXXX; + lasttype = types[TINT]; + } + break; + + case 184: +#line 959 "cc.y" + { + (yyval.tycl).t = (yyvsp[(1) - (1)].type); + (yyval.tycl).c = CXXX; + } + break; + + case 185: +#line 964 "cc.y" + { + (yyval.tycl).t = simplet((yyvsp[(1) - (1)].lval)); + (yyval.tycl).c = CXXX; + } + break; + + case 186: +#line 969 "cc.y" + { + (yyval.tycl).t = simplet((yyvsp[(1) - (1)].lval)); + (yyval.tycl).c = simplec((yyvsp[(1) - (1)].lval)); + (yyval.tycl).t = garbt((yyval.tycl).t, (yyvsp[(1) - (1)].lval)); + } + break; + + case 187: +#line 975 "cc.y" + { + (yyval.tycl).t = (yyvsp[(1) - (2)].type); + (yyval.tycl).c = simplec((yyvsp[(2) - (2)].lval)); + (yyval.tycl).t = garbt((yyval.tycl).t, (yyvsp[(2) - (2)].lval)); + if((yyvsp[(2) - (2)].lval) & ~BCLASS & ~BGARB) + diag(Z, "duplicate types given: %T and %Q", (yyvsp[(1) - (2)].type), (yyvsp[(2) - (2)].lval)); + } + break; + + case 188: +#line 983 "cc.y" + { + (yyval.tycl).t = simplet(typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval))); + (yyval.tycl).c = simplec((yyvsp[(2) - (2)].lval)); + (yyval.tycl).t = garbt((yyval.tycl).t, (yyvsp[(2) - (2)].lval)); + } + break; + + case 189: +#line 989 "cc.y" + { + (yyval.tycl).t = (yyvsp[(2) - (3)].type); + (yyval.tycl).c = simplec((yyvsp[(1) - (3)].lval)); + (yyval.tycl).t = garbt((yyval.tycl).t, (yyvsp[(1) - (3)].lval)|(yyvsp[(3) - (3)].lval)); + } + break; + + case 190: +#line 995 "cc.y" + { + (yyval.tycl).t = simplet((yyvsp[(2) - (2)].lval)); + (yyval.tycl).c = simplec((yyvsp[(1) - (2)].lval)); + (yyval.tycl).t = garbt((yyval.tycl).t, (yyvsp[(1) - (2)].lval)); + } + break; + + case 191: +#line 1001 "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)); + (yyval.tycl).t = garbt((yyval.tycl).t, (yyvsp[(1) - (3)].lval)|(yyvsp[(3) - (3)].lval)); + } + break; + + case 192: +#line 1009 "cc.y" + { + (yyval.type) = (yyvsp[(1) - (1)].tycl).t; + if((yyvsp[(1) - (1)].tycl).c != CXXX) + diag(Z, "illegal combination of class 4: %s", cnames[(yyvsp[(1) - (1)].tycl).c]); + } + break; + + case 193: +#line 1017 "cc.y" + { + lasttype = (yyvsp[(1) - (1)].tycl).t; + lastclass = (yyvsp[(1) - (1)].tycl).c; + } + break; + + case 194: +#line 1024 "cc.y" + { + dotag((yyvsp[(2) - (2)].sym), TSTRUCT, 0); + (yyval.type) = (yyvsp[(2) - (2)].sym)->suetag; + } + break; + + case 195: +#line 1029 "cc.y" + { + dotag((yyvsp[(2) - (2)].sym), TSTRUCT, autobn); + } + break; + + case 196: +#line 1033 "cc.y" + { + (yyval.type) = (yyvsp[(2) - (4)].sym)->suetag; + if((yyval.type)->link != T) + diag(Z, "redeclare tag: %s", (yyvsp[(2) - (4)].sym)->name); + (yyval.type)->link = (yyvsp[(4) - (4)].type); + sualign((yyval.type)); + } + break; + + case 197: +#line 1041 "cc.y" + { + taggen++; + sprint(symb, "_%d_", taggen); + (yyval.type) = dotag(lookup(), TSTRUCT, autobn); + (yyval.type)->link = (yyvsp[(2) - (2)].type); + sualign((yyval.type)); + } + break; + + case 198: +#line 1049 "cc.y" + { + dotag((yyvsp[(2) - (2)].sym), TUNION, 0); + (yyval.type) = (yyvsp[(2) - (2)].sym)->suetag; + } + break; + + case 199: +#line 1054 "cc.y" + { + dotag((yyvsp[(2) - (2)].sym), TUNION, autobn); + } + break; + + case 200: +#line 1058 "cc.y" + { + (yyval.type) = (yyvsp[(2) - (4)].sym)->suetag; + if((yyval.type)->link != T) + diag(Z, "redeclare tag: %s", (yyvsp[(2) - (4)].sym)->name); + (yyval.type)->link = (yyvsp[(4) - (4)].type); + sualign((yyval.type)); + } + break; + + case 201: +#line 1066 "cc.y" + { + taggen++; + sprint(symb, "_%d_", taggen); + (yyval.type) = dotag(lookup(), TUNION, autobn); + (yyval.type)->link = (yyvsp[(2) - (2)].type); + sualign((yyval.type)); + } + break; + + case 202: +#line 1074 "cc.y" + { + dotag((yyvsp[(2) - (2)].sym), TENUM, 0); + (yyval.type) = (yyvsp[(2) - (2)].sym)->suetag; + if((yyval.type)->link == T) + (yyval.type)->link = types[TINT]; + (yyval.type) = (yyval.type)->link; + } + break; + + case 203: +#line 1082 "cc.y" + { + dotag((yyvsp[(2) - (2)].sym), TENUM, autobn); + } + break; + + case 204: +#line 1086 "cc.y" + { + en.tenum = T; + en.cenum = T; + } + break; + + case 205: +#line 1091 "cc.y" + { + (yyval.type) = (yyvsp[(2) - (7)].sym)->suetag; + if((yyval.type)->link != T) + diag(Z, "redeclare tag: %s", (yyvsp[(2) - (7)].sym)->name); + if(en.tenum == T) { + diag(Z, "enum type ambiguous: %s", (yyvsp[(2) - (7)].sym)->name); + en.tenum = types[TINT]; + } + (yyval.type)->link = en.tenum; + (yyval.type) = en.tenum; + } + break; + + case 206: +#line 1103 "cc.y" + { + en.tenum = T; + en.cenum = T; + } + break; + + case 207: +#line 1108 "cc.y" + { + (yyval.type) = en.tenum; + } + break; + + case 208: +#line 1112 "cc.y" + { + (yyval.type) = tcopy((yyvsp[(1) - (1)].sym)->type); + } + break; + + case 210: +#line 1119 "cc.y" + { + (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)); + } + break; + + case 211: +#line 1124 "cc.y" + { + (yyval.lval) = 0; + } + break; + + case 212: +#line 1128 "cc.y" + { + (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)); + } + break; + + case 217: +#line 1140 "cc.y" + { + (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)); + } + break; + + case 220: +#line 1150 "cc.y" + { + doenum((yyvsp[(1) - (1)].sym), Z); + } + break; + + case 221: +#line 1154 "cc.y" + { + doenum((yyvsp[(1) - (3)].sym), (yyvsp[(3) - (3)].node)); + } + break; + + case 224: +#line 1161 "cc.y" + { (yyval.lval) = BCHAR; } + break; + + case 225: +#line 1162 "cc.y" + { (yyval.lval) = BSHORT; } + break; + + case 226: +#line 1163 "cc.y" + { (yyval.lval) = BINT; } + break; + + case 227: +#line 1164 "cc.y" + { (yyval.lval) = BLONG; } + break; + + case 228: +#line 1165 "cc.y" + { (yyval.lval) = BSIGNED; } + break; + + case 229: +#line 1166 "cc.y" + { (yyval.lval) = BUNSIGNED; } + break; + + case 230: +#line 1167 "cc.y" + { (yyval.lval) = BFLOAT; } + break; + + case 231: +#line 1168 "cc.y" + { (yyval.lval) = BDOUBLE; } + break; + + case 232: +#line 1169 "cc.y" + { (yyval.lval) = BVOID; } + break; + + case 233: +#line 1172 "cc.y" + { (yyval.lval) = BAUTO; } + break; + + case 234: +#line 1173 "cc.y" + { (yyval.lval) = BSTATIC; } + break; + + case 235: +#line 1174 "cc.y" + { (yyval.lval) = BEXTERN; } + break; + + case 236: +#line 1175 "cc.y" + { (yyval.lval) = BTYPEDEF; } + break; + + case 237: +#line 1176 "cc.y" + { (yyval.lval) = BTYPESTR; } + break; + + case 238: +#line 1177 "cc.y" + { (yyval.lval) = BREGISTER; } + break; + + case 239: +#line 1178 "cc.y" + { (yyval.lval) = 0; } + break; + + case 240: +#line 1181 "cc.y" + { (yyval.lval) = BCONSTNT; } + break; + + case 241: +#line 1182 "cc.y" + { (yyval.lval) = BVOLATILE; } + break; + + case 242: +#line 1183 "cc.y" + { (yyval.lval) = 0; } + break; + + case 243: +#line 1187 "cc.y" + { + (yyval.node) = new(ONAME, Z, Z); + if((yyvsp[(1) - (1)].sym)->class == CLOCAL) + (yyvsp[(1) - (1)].sym) = mkstatic((yyvsp[(1) - (1)].sym)); + (yyval.node)->sym = (yyvsp[(1) - (1)].sym); + (yyval.node)->type = (yyvsp[(1) - (1)].sym)->type; + (yyval.node)->etype = TVOID; + if((yyval.node)->type != T) + (yyval.node)->etype = (yyval.node)->type->etype; + (yyval.node)->xoffset = (yyvsp[(1) - (1)].sym)->offset; + (yyval.node)->class = (yyvsp[(1) - (1)].sym)->class; + (yyvsp[(1) - (1)].sym)->aused = 1; + } + break; + + case 244: +#line 1202 "cc.y" + { + (yyval.node) = new(ONAME, Z, Z); + (yyval.node)->sym = (yyvsp[(1) - (1)].sym); + (yyval.node)->type = (yyvsp[(1) - (1)].sym)->type; + (yyval.node)->etype = TVOID; + if((yyval.node)->type != T) + (yyval.node)->etype = (yyval.node)->type->etype; + (yyval.node)->xoffset = (yyvsp[(1) - (1)].sym)->offset; + (yyval.node)->class = (yyvsp[(1) - (1)].sym)->class; + } + break; + + +/* Line 1267 of yacc.c. */ +#line 3596 "y.tab.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++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. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + 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; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + 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); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + +#line 1215 "cc.y" + + diff --git a/src/cmd/cc/y.tab.h b/src/cmd/cc/y.tab.h new file mode 100644 index 000000000..c56a1d85a --- /dev/null +++ b/src/cmd/cc/y.tab.h @@ -0,0 +1,228 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* 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 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, 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 + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + 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 + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + LORE = 258, + LXORE = 259, + LANDE = 260, + LLSHE = 261, + LRSHE = 262, + LMDE = 263, + LDVE = 264, + LMLE = 265, + LME = 266, + LPE = 267, + LOROR = 268, + LANDAND = 269, + LNE = 270, + LEQ = 271, + LGE = 272, + LLE = 273, + LRSH = 274, + LLSH = 275, + LMG = 276, + LPP = 277, + LMM = 278, + LNAME = 279, + LTYPE = 280, + LFCONST = 281, + LDCONST = 282, + LCONST = 283, + LLCONST = 284, + LUCONST = 285, + LULCONST = 286, + LVLCONST = 287, + LUVLCONST = 288, + LSTRING = 289, + LLSTRING = 290, + LAUTO = 291, + LBREAK = 292, + LCASE = 293, + LCHAR = 294, + LCONTINUE = 295, + LDEFAULT = 296, + LDO = 297, + LDOUBLE = 298, + LELSE = 299, + LEXTERN = 300, + LFLOAT = 301, + LFOR = 302, + LGOTO = 303, + 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 + }; +#endif +/* Tokens. */ +#define LORE 258 +#define LXORE 259 +#define LANDE 260 +#define LLSHE 261 +#define LRSHE 262 +#define LMDE 263 +#define LDVE 264 +#define LMLE 265 +#define LME 266 +#define LPE 267 +#define LOROR 268 +#define LANDAND 269 +#define LNE 270 +#define LEQ 271 +#define LGE 272 +#define LLE 273 +#define LRSH 274 +#define LLSH 275 +#define LMG 276 +#define LPP 277 +#define LMM 278 +#define LNAME 279 +#define LTYPE 280 +#define LFCONST 281 +#define LDCONST 282 +#define LCONST 283 +#define LLCONST 284 +#define LUCONST 285 +#define LULCONST 286 +#define LVLCONST 287 +#define LUVLCONST 288 +#define LSTRING 289 +#define LLSTRING 290 +#define LAUTO 291 +#define LBREAK 292 +#define LCASE 293 +#define LCHAR 294 +#define LCONTINUE 295 +#define LDEFAULT 296 +#define LDO 297 +#define LDOUBLE 298 +#define LELSE 299 +#define LEXTERN 300 +#define LFLOAT 301 +#define LFOR 302 +#define LGOTO 303 +#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 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 36 "cc.y" +{ + Node* node; + Sym* sym; + Type* type; + struct + { + Type* t; + uchar c; + } tycl; + struct + { + Type* t1; + Type* t2; + Type* t3; + uchar c; + } tyty; + struct + { + char* s; + int32 l; + } sval; + int32 lval; + double dval; + vlong vval; +} +/* Line 1529 of yacc.c. */ +#line 221 "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/cgo/Makefile b/src/cmd/cgo/Makefile deleted file mode 100644 index 5458c3e4f..000000000 --- a/src/cmd/cgo/Makefile +++ /dev/null @@ -1,15 +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 ../../Make.inc - -TARG=cgo -GOFILES=\ - ast.go\ - gcc.go\ - main.go\ - out.go\ - util.go\ - -include ../../Make.cmd diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index 73b7313d6..381e606ef 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -9,7 +9,6 @@ package main import ( "fmt" "go/ast" - "go/doc" "go/parser" "go/scanner" "go/token" @@ -17,7 +16,7 @@ import ( "strings" ) -func parse(name string, flags uint) *ast.File { +func parse(name string, flags parser.Mode) *ast.File { ast1, err := parser.ParseFile(fset, name, nil, flags) if err != nil { if list, ok := err.(scanner.ErrorList); ok { @@ -71,7 +70,7 @@ func (f *File) ReadGo(name string) { } sawC = true if s.Name != nil { - error(s.Path.Pos(), `cannot rename import "C"`) + error_(s.Path.Pos(), `cannot rename import "C"`) } cg := s.Doc if cg == nil && len(d.Specs) == 1 { @@ -79,12 +78,12 @@ func (f *File) ReadGo(name string) { } if cg != nil { f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name) - f.Preamble += doc.CommentText(cg) + "\n" + f.Preamble += cg.Text() + "\n" } } } if !sawC { - error(token.NoPos, `cannot find import "C"`) + error_(token.NoPos, `cannot find import "C"`) } // In ast2, strip the import "C" line. @@ -128,6 +127,7 @@ func (f *File) ReadGo(name string) { f.walk(ast1, "prog", (*File).saveExport) f.walk(ast2, "prog", (*File).saveExport2) + f.Comments = ast1.Comments f.AST = ast2 } @@ -147,9 +147,12 @@ func (f *File) saveRef(x interface{}, context string) { if context == "as2" { context = "expr" } + if context == "embed-type" { + error_(sel.Pos(), "cannot embed C type") + } goname := sel.Sel.Name if goname == "errno" { - error(sel.Pos(), "cannot refer to errno directly; see documentation") + error_(sel.Pos(), "cannot refer to errno directly; see documentation") return } name := f.Name[goname] @@ -186,11 +189,11 @@ func (f *File) saveExport(x interface{}, context string) { name := strings.TrimSpace(string(c.Text[9:])) if name == "" { - error(c.Pos(), "export missing name") + error_(c.Pos(), "export missing name") } if name != n.Name.Name { - error(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name) + error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name) } f.ExpFunc = append(f.ExpFunc, &ExpFunc{ @@ -225,14 +228,18 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{} // everything else just recurs default: - error(token.NoPos, "unexpected type %T in walk", x, visit) + error_(token.NoPos, "unexpected type %T in walk", x, visit) panic("unexpected type") case nil: // These are ordered and grouped to match ../../pkg/go/ast/ast.go case *ast.Field: - f.walk(&n.Type, "type", visit) + if len(n.Names) == 0 && context == "field" { + f.walk(&n.Type, "embed-type", visit) + } else { + f.walk(&n.Type, "type", visit) + } case *ast.FieldList: for _, field := range n.List { f.walk(field, context, visit) @@ -289,9 +296,9 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{} case *ast.StructType: f.walk(n.Fields, "field", visit) case *ast.FuncType: - f.walk(n.Params, "field", visit) + f.walk(n.Params, "param", visit) if n.Results != nil { - f.walk(n.Results, "field", visit) + f.walk(n.Results, "param", visit) } case *ast.InterfaceType: f.walk(n.Methods, "field", visit) @@ -379,7 +386,7 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{} f.walk(n.Specs, "spec", visit) case *ast.FuncDecl: if n.Recv != nil { - f.walk(n.Recv, "field", visit) + f.walk(n.Recv, "param", visit) } f.walk(n.Type, "type", visit) if n.Body != nil { diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index a4219867c..1bb48f44e 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -6,7 +6,8 @@ Cgo enables the creation of Go packages that call C code. -Usage: cgo [compiler options] file.go +Usage: + go tool cgo [compiler options] file.go The compiler options are passed through uninterpreted when invoking gcc to compile the C parts of the package. @@ -16,8 +17,8 @@ the pseudo-package "C" and then refers to types such as C.size_t, variables such as C.stdout, or functions such as C.putchar. If the import of "C" is immediately preceded by a comment, that -comment is used as a header when compiling the C parts of -the package. For example: +comment, called the preamble, is used as a header when compiling +the C parts of the package. For example: // #include // #include @@ -43,6 +44,11 @@ For example: // #include import "C" +The CGO_CFLAGS and CGO_LDFLAGS environment variables are added +to the flags derived from these directives. Package-specific flags should +be set using the directives, not the environment variables, so that builds +work in unmodified environments. + Within the Go file, C identifiers or field names that are keywords in Go can be accessed by prefixing them with an underscore: if x points at a C struct with a field named "type", x._type accesses the field. @@ -57,9 +63,11 @@ The C type void* is represented by Go's unsafe.Pointer. To access a struct, union, or enum type directly, prefix it with 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 os.Error. For example: +C errno variable as an error. For example: n, err := C.atoi("abc") @@ -73,6 +81,9 @@ A few special functions convert between Go and C types 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. func C.CString(string) *C.char // C string to Go string @@ -84,16 +95,34 @@ by making copies of the data. In pseudo-Go definitions: // C pointer, length to Go []byte func C.GoBytes(unsafe.Pointer, C.int) []byte +Go functions can be exported for use by C code in the following way: + + //export MyFunction + func MyFunction(arg1, arg2 int, arg3 string) int64 {...} + + //export MyFunction2 + func MyFunction2(arg1, arg2 int, arg3 string) (int64, *C.char) {...} + +They will be available in the C code as: + + extern int64 MyFunction(int arg1, int arg2, GoString arg3); + extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3); + +found in _cgo_export.h generated header, after any preambles +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. + 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. -The standard package makefile rules in Make.pkg automate the -process of using cgo. See $GOROOT/misc/cgo/stdio and -$GOROOT/misc/cgo/gmp for examples. +The standard package construction rules of the go command +automate the process of using cgo. See $GOROOT/misc/cgo/stdio +and $GOROOT/misc/cgo/gmp for examples. Cgo does not yet work with gccgo. See "C? Go? Cgo!" for an introduction to using cgo: -http://blog.golang.org/2011/03/c-go-cgo.html +http://golang.org/doc/articles/c_go_cgo.html */ package documentation diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 7ec4d8ccf..98a847e6f 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -14,16 +14,17 @@ import ( "debug/macho" "debug/pe" "encoding/binary" + "errors" "flag" "fmt" "go/ast" "go/parser" "go/token" "os" - "runtime" "strconv" "strings" "unicode" + "unicode/utf8" ) var debugDefine = flag.Bool("debug-define", false, "print relevant #defines") @@ -59,6 +60,9 @@ func cname(s string) string { if strings.HasPrefix(s, "enum_") { return "enum " + s[len("enum_"):] } + if strings.HasPrefix(s, "sizeof_") { + return "sizeof(" + cname(s[len("sizeof_"):]) + ")" + } return s } @@ -72,7 +76,7 @@ func (p *Package) ParseFlags(f *File, srcfile string) { NextLine: for _, line := range linesIn { l := strings.TrimSpace(line) - if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(int(l[4])) { + if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(rune(l[4])) { linesOut = append(linesOut, line) continue } @@ -91,9 +95,9 @@ NextLine: case 2: k = kf[1] switch kf[0] { - case runtime.GOOS: - case runtime.GOARCH: - case runtime.GOOS + "/" + runtime.GOARCH: + case goos: + case goarch: + case goos + "/" + goarch: default: continue NextLine } @@ -148,10 +152,10 @@ func (p *Package) addToFlag(flag string, args []string) { // pkgConfig runs pkg-config and extracts --libs and --cflags information // for packages. -func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) { +func pkgConfig(packages []string) (cflags, ldflags []string, err error) { for _, name := range packages { if len(name) == 0 || name[0] == '-' { - return nil, nil, os.NewError(fmt.Sprintf("invalid name: %q", name)) + return nil, nil, errors.New(fmt.Sprintf("invalid name: %q", name)) } } @@ -159,7 +163,7 @@ func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) { stdout, stderr, ok := run(nil, args) if !ok { os.Stderr.Write(stderr) - return nil, nil, os.NewError("pkg-config failed") + return nil, nil, errors.New("pkg-config failed") } cflags, err = splitQuoted(string(stdout)) if err != nil { @@ -170,7 +174,7 @@ func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) { stdout, stderr, ok = run(nil, args) if !ok { os.Stderr.Write(stderr) - return nil, nil, os.NewError("pkg-config failed") + return nil, nil, errors.New("pkg-config failed") } ldflags, err = splitQuoted(string(stdout)) return @@ -192,30 +196,30 @@ func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) { // // []string{"a", "b:c d", "ef", `g"`} // -func splitQuoted(s string) (r []string, err os.Error) { +func splitQuoted(s string) (r []string, err error) { var args []string - arg := make([]int, len(s)) + arg := make([]rune, len(s)) escaped := false quoted := false - quote := 0 + quote := '\x00' i := 0 - for _, rune := range s { + for _, r := range s { switch { case escaped: escaped = false - case rune == '\\': + case r == '\\': escaped = true continue case quote != 0: - if rune == quote { + if r == quote { quote = 0 continue } - case rune == '"' || rune == '\'': + case r == '"' || r == '\'': quoted = true - quote = rune + quote = r continue - case unicode.IsSpace(rune): + case unicode.IsSpace(r): if quoted || i > 0 { quoted = false args = append(args, string(arg[:i])) @@ -223,21 +227,21 @@ func splitQuoted(s string) (r []string, err os.Error) { } continue } - arg[i] = rune + arg[i] = r i++ } if quoted || i > 0 { args = append(args, string(arg[:i])) } if quote != 0 { - err = os.NewError("unclosed quote") + err = errors.New("unclosed quote") } else if escaped { - err = os.NewError("unfinished escaping") + err = errors.New("unfinished escaping") } return args, err } -var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz") +var safeBytes = []byte(`+-.,/0123456789:=ABCDEFGHIJKLMNOPQRSTUVWXYZ\_abcdefghijklmnopqrstuvwxyz`) func safeName(s string) bool { if s == "" { @@ -340,14 +344,22 @@ func (p *Package) guessKinds(f *File) []*Name { if _, err := strconv.Atoi(n.Define); err == nil { ok = true } else if n.Define[0] == '"' || n.Define[0] == '\'' { - _, err := parser.ParseExpr(fset, "", n.Define) - if err == nil { + if _, err := parser.ParseExpr(n.Define); err == nil { ok = true } } if ok { n.Kind = "const" - n.Const = n.Define + // Turn decimal into hex, just for consistency + // with enum-derived constants. Otherwise + // in the cgo -godefs output half the constants + // are in hex and half are in whatever the #define used. + i, err := strconv.ParseInt(n.Define, 0, 64) + if err == nil { + n.Const = fmt.Sprintf("%#x", i) + } else { + n.Const = n.Define + } continue } @@ -421,7 +433,7 @@ func (p *Package) guessKinds(f *File) []*Name { case strings.Contains(line, ": statement with no effect"): what = "not-type" // const or func or var case strings.Contains(line, "undeclared"): - error(token.NoPos, "%s", strings.TrimSpace(line[colon+1:])) + error_(token.NoPos, "%s", strings.TrimSpace(line[colon+1:])) case strings.Contains(line, "is not an integer constant"): isConst[i] = false continue @@ -440,6 +452,11 @@ func (p *Package) guessKinds(f *File) []*Name { for i, b := range isConst { if b { names[i].Kind = "const" + if toSniff[i] != nil && names[i].Const == "" { + j := len(needType) + needType = needType[0 : j+1] + needType[j] = names[i] + } } } for _, n := range toSniff { @@ -449,7 +466,7 @@ func (p *Package) guessKinds(f *File) []*Name { if n.Kind != "" { continue } - error(token.NoPos, "could not determine kind of name for C.%s", n.Go) + error_(token.NoPos, "could not determine kind of name for C.%s", n.Go) } if nerrors > 0 { fatalf("unresolved names") @@ -507,6 +524,10 @@ func (p *Package) loadDWARF(f *File, names []*Name) { for i, n := range names { nameToIndex[n] = i } + nameToRef := make(map[*Name]*Ref) + for _, ref := range f.Ref { + nameToRef[ref.Name] = ref + } r := d.Reader() for { e, err := r.Next() @@ -577,21 +598,28 @@ func (p *Package) loadDWARF(f *File, names []*Name) { var conv typeConv conv.Init(p.PtrSize) for i, n := range names { + if types[i] == nil { + continue + } + pos := token.NoPos + if ref, ok := nameToRef[n]; ok { + pos = ref.Pos() + } f, fok := types[i].(*dwarf.FuncType) if n.Kind != "type" && fok { n.Kind = "func" - n.FuncType = conv.FuncType(f) + n.FuncType = conv.FuncType(f, pos) } else { - n.Type = conv.Type(types[i]) + n.Type = conv.Type(types[i], pos) if enums[i] != 0 && n.Type.EnumValues != nil { k := fmt.Sprintf("__cgo_enum__%d", i) n.Kind = "const" - n.Const = strconv.Itoa64(n.Type.EnumValues[k]) + n.Const = fmt.Sprintf("%#x", n.Type.EnumValues[k]) // Remove injected enum to ensure the value will deep-compare // equally in future loads of the same constant. - n.Type.EnumValues[k] = 0, false + delete(n.Type.EnumValues, k) } else if n.Kind == "const" && i < len(enumVal) { - n.Const = strconv.Itoa64(enumVal[i]) + n.Const = fmt.Sprintf("%#x", enumVal[i]) } } } @@ -600,7 +628,8 @@ func (p *Package) loadDWARF(f *File, names []*Name) { // rewriteRef rewrites all the C.xxx references in f.AST to refer to the // Go equivalents, now that we have figured out the meaning of all -// the xxx. +// the xxx. In *godefs or *cdefs mode, rewriteRef replaces the names +// with full definitions instead of mangled names. func (p *Package) rewriteRef(f *File) { // Assign mangled names. for _, n := range f.Name { @@ -618,7 +647,7 @@ func (p *Package) rewriteRef(f *File) { // functions are only used in calls. for _, r := range f.Ref { if r.Name.Kind == "const" && r.Name.Const == "" { - error(r.Pos(), "unable to find value of constant C.%s", r.Name.Go) + error_(r.Pos(), "unable to find value of constant C.%s", r.Name.Go) } var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default switch r.Context { @@ -629,12 +658,12 @@ func (p *Package) rewriteRef(f *File) { expr = r.Name.Type.Go break } - error(r.Pos(), "call of non-function C.%s", r.Name.Go) + error_(r.Pos(), "call of non-function C.%s", r.Name.Go) break } if r.Context == "call2" { if r.Name.FuncType.Result == nil { - error(r.Pos(), "assignment count mismatch: 2 = 0") + error_(r.Pos(), "assignment count mismatch: 2 = 0") } // Invent new Name for the two-result function. n := f.Name["2"+r.Name.Go] @@ -651,7 +680,7 @@ func (p *Package) rewriteRef(f *File) { } case "expr": if r.Name.Kind == "func" { - error(r.Pos(), "must call C.%s", r.Name.Go) + error_(r.Pos(), "must call C.%s", r.Name.Go) } if r.Name.Kind == "type" { // Okay - might be new(T) @@ -663,13 +692,28 @@ func (p *Package) rewriteRef(f *File) { case "type": if r.Name.Kind != "type" { - error(r.Pos(), "expression C.%s used as type", r.Name.Go) + error_(r.Pos(), "expression C.%s used as type", r.Name.Go) + } else if r.Name.Type == nil { + // Use of C.enum_x, C.struct_x or C.union_x without C definition. + // GCC won't raise an error when using pointers to such unknown types. + error_(r.Pos(), "type C.%s: undefined C type '%s'", r.Name.Go, r.Name.C) } else { expr = r.Name.Type.Go } default: if r.Name.Kind == "func" { - error(r.Pos(), "must call C.%s", r.Name.Go) + error_(r.Pos(), "must call C.%s", r.Name.Go) + } + } + if *godefs || *cdefs { + // Substitute definition for mangled type name. + if id, ok := expr.(*ast.Ident); ok { + if t := typedef[id.Name]; t != nil { + expr = t.Go + } + if id.Name == r.Name.Mangle && r.Name.Const != "" { + expr = ast.NewIdent(r.Name.Const) + } } } *r.Expr = expr @@ -688,7 +732,7 @@ func (p *Package) gccName() (ret string) { // gccMachine returns the gcc -m flag to use, either "-m32" or "-m64". func (p *Package) gccMachine() []string { - switch runtime.GOARCH { + switch goarch { case "amd64": return []string{"-m64"} case "386": @@ -697,7 +741,9 @@ func (p *Package) gccMachine() []string { return nil } -var gccTmp = objDir + "_cgo_.o" +func gccTmp() string { + return *objDir + "_cgo_.o" +} // gccCmd returns the gcc command line to use for compiling // the input. @@ -706,7 +752,7 @@ func (p *Package) gccCmd() []string { p.gccName(), "-Wall", // many warnings "-Werror", // warnings are errors - "-o" + gccTmp, // write object to tmp + "-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 @@ -723,10 +769,10 @@ func (p *Package) gccCmd() []string { func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) { runGcc(stdin, p.gccCmd()) - if f, err := macho.Open(gccTmp); err == nil { + if f, err := macho.Open(gccTmp()); err == nil { d, err := f.DWARF() if err != nil { - fatalf("cannot load DWARF output from %s: %v", gccTmp, err) + fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } var data []byte if f.Symtab != nil { @@ -752,23 +798,23 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) // Can skip debug data block in ELF and PE for now. // The DWARF information is complete. - if f, err := elf.Open(gccTmp); err == nil { + 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) + fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } return d, f.ByteOrder, nil } - if f, err := pe.Open(gccTmp); err == nil { + if f, err := pe.Open(gccTmp()); err == nil { d, err := f.DWARF() if err != nil { - fatalf("cannot load DWARF output from %s: %v", gccTmp, err) + fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } return d, binary.LittleEndian, nil } - fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp) + fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp()) panic("not reached") } @@ -848,7 +894,8 @@ type typeConv struct { } var tagGen int -var typedef = make(map[string]ast.Expr) +var typedef = make(map[string]*Type) +var goIdent = make(map[string]*ast.Ident) func (c *typeConv) Init(ptrSize int64) { c.ptrSize = ptrSize @@ -933,10 +980,10 @@ func (tr *TypeRepr) Set(repr string, fargs ...interface{}) { // Type returns a *Type with the same memory layout as // dtype when used as the type of a variable or a struct field. -func (c *typeConv) Type(dtype dwarf.Type) *Type { +func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { if t, ok := c.m[dtype]; ok { if t.Go == nil { - fatalf("type conversion loop at %s", dtype) + fatalf("%s: type conversion loop at %s", lineno(pos), dtype) } return t } @@ -959,11 +1006,11 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { switch dt := dtype.(type) { default: - fatalf("unexpected type: %s", dtype) + fatalf("%s: unexpected type: %s", lineno(pos), dtype) case *dwarf.AddrType: if t.Size != c.ptrSize { - fatalf("unexpected: %d-byte address type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte address type - %s", lineno(pos), t.Size, dtype) } t.Go = c.uintptr t.Align = t.Size @@ -978,7 +1025,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { Len: c.intExpr(dt.Count), } t.Go = gt // publish before recursive call - sub := c.Type(dt.Type) + sub := c.Type(dt.Type, pos) t.Align = sub.Align gt.Elt = sub.Go t.C.Set("typeof(%s[%d])", sub.C, dt.Count) @@ -989,7 +1036,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.CharType: if t.Size != 1 { - fatalf("unexpected: %d-byte char type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte char type - %s", lineno(pos), t.Size, dtype) } t.Go = c.int8 t.Align = 1 @@ -1009,7 +1056,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { } switch t.Size + int64(signed) { default: - fatalf("unexpected: %d-byte enum type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte enum type - %s", lineno(pos), t.Size, dtype) case 1: t.Go = c.uint8 case 2: @@ -1031,7 +1078,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.FloatType: switch t.Size { default: - fatalf("unexpected: %d-byte float type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte float type - %s", lineno(pos), t.Size, dtype) case 4: t.Go = c.float32 case 8: @@ -1044,7 +1091,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.ComplexType: switch t.Size { default: - fatalf("unexpected: %d-byte complex type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte complex type - %s", lineno(pos), t.Size, dtype) case 8: t.Go = c.complex64 case 16: @@ -1062,11 +1109,11 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { case *dwarf.IntType: if dt.BitSize > 0 { - fatalf("unexpected: %d-bit int type - %s", dt.BitSize, dtype) + fatalf("%s: unexpected: %d-bit int type - %s", lineno(pos), dt.BitSize, dtype) } switch t.Size { default: - fatalf("unexpected: %d-byte int type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte int type - %s", lineno(pos), t.Size, dtype) case 1: t.Go = c.int8 case 2: @@ -1092,13 +1139,13 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { gt := &ast.StarExpr{} t.Go = gt // publish before recursive call - sub := c.Type(dt.Type) + sub := c.Type(dt.Type, pos) gt.X = sub.Go t.C.Set("%s*", sub.C) case *dwarf.QualType: // Ignore qualifier. - t = c.Type(dt.Type) + t = c.Type(dt.Type, pos) c.m[dtype] = t return t @@ -1114,19 +1161,26 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { } name := c.Ident("_Ctype_" + dt.Kind + "_" + tag) t.Go = name // publish before recursive calls + goIdent[name.Name] = name switch dt.Kind { case "union", "class": - typedef[name.Name] = c.Opaque(t.Size) + t.Go = c.Opaque(t.Size) if t.C.Empty() { t.C.Set("typeof(unsigned char[%d])", t.Size) } + typedef[name.Name] = t case "struct": - g, csyntax, align := c.Struct(dt) + g, csyntax, align := c.Struct(dt, pos) if t.C.Empty() { t.C.Set(csyntax) } t.Align = align - typedef[name.Name] = g + tt := *t + if tag != "" { + tt.C = &TypeRepr{"struct %s", []interface{}{tag}} + } + tt.Go = g + typedef[name.Name] = &tt } case *dwarf.TypedefType: @@ -1148,29 +1202,35 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { t.Align = c.ptrSize break } - name := c.Ident("_Ctypedef_" + dt.Name) + name := c.Ident("_Ctype_" + dt.Name) + goIdent[name.Name] = name t.Go = name // publish before recursive call - sub := c.Type(dt.Type) + sub := c.Type(dt.Type, pos) t.Size = sub.Size t.Align = sub.Align if _, ok := typedef[name.Name]; !ok { - typedef[name.Name] = sub.Go + tt := *t + tt.Go = sub.Go + typedef[name.Name] = &tt + } + if *godefs || *cdefs { + t.Go = sub.Go } case *dwarf.UcharType: if t.Size != 1 { - fatalf("unexpected: %d-byte uchar type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte uchar type - %s", lineno(pos), t.Size, dtype) } t.Go = c.uint8 t.Align = 1 case *dwarf.UintType: if dt.BitSize > 0 { - fatalf("unexpected: %d-bit uint type - %s", dt.BitSize, dtype) + fatalf("%s: unexpected: %d-bit uint type - %s", lineno(pos), dt.BitSize, dtype) } switch t.Size { default: - fatalf("unexpected: %d-byte uint type - %s", t.Size, dtype) + fatalf("%s: unexpected: %d-byte uint type - %s", lineno(pos), t.Size, dtype) case 1: t.Go = c.uint8 case 2: @@ -1198,13 +1258,16 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { } s = strings.Join(strings.Split(s, " "), "") // strip spaces name := c.Ident("_Ctype_" + s) - typedef[name.Name] = t.Go - t.Go = name + tt := *t + typedef[name.Name] = &tt + if !*godefs && !*cdefs { + t.Go = name + } } } if t.C.Empty() { - fatalf("internal error: did not create C name for %s", dtype) + fatalf("%s: internal error: did not create C name for %s", lineno(pos), dtype) } return t @@ -1212,8 +1275,8 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { // FuncArg returns a Go type with the same memory layout as // dtype when used as the type of a C function argument. -func (c *typeConv) FuncArg(dtype dwarf.Type) *Type { - t := c.Type(dtype) +func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type { + t := c.Type(dtype, pos) switch dt := dtype.(type) { case *dwarf.ArrayType: // Arrays are passed implicitly as pointers in C. @@ -1234,9 +1297,18 @@ func (c *typeConv) FuncArg(dtype dwarf.Type) *Type { if ptr, ok := base(dt.Type).(*dwarf.PtrType); ok { // Unless the typedef happens to point to void* since // Go has special rules around using unsafe.Pointer. - if _, void := base(ptr.Type).(*dwarf.VoidType); !void { - return c.Type(ptr) + if _, void := base(ptr.Type).(*dwarf.VoidType); void { + break + } + + t = c.Type(ptr, pos) + if t == nil { + return nil } + + // Remember the C spelling, in case the struct + // has __attribute__((unavailable)) on it. See issue 2888. + t.Typedef = dt.Name } } return t @@ -1244,7 +1316,7 @@ func (c *typeConv) FuncArg(dtype dwarf.Type) *Type { // FuncType returns the Go type analogous to dtype. // There is no guarantee about matching memory layout. -func (c *typeConv) FuncType(dtype *dwarf.FuncType) *FuncType { +func (c *typeConv) FuncType(dtype *dwarf.FuncType, pos token.Pos) *FuncType { p := make([]*Type, len(dtype.ParamType)) gp := make([]*ast.Field, len(dtype.ParamType)) for i, f := range dtype.ParamType { @@ -1257,14 +1329,14 @@ func (c *typeConv) FuncType(dtype *dwarf.FuncType) *FuncType { p, gp = nil, nil break } - p[i] = c.FuncArg(f) + p[i] = c.FuncArg(f, pos) gp[i] = &ast.Field{Type: p[i].Go} } var r *Type var gr []*ast.Field if _, ok := dtype.ReturnType.(*dwarf.VoidType); !ok && dtype.ReturnType != nil { - r = c.Type(dtype.ReturnType) - gr = []*ast.Field{&ast.Field{Type: r.Go}} + r = c.Type(dtype.ReturnType, pos) + gr = []*ast.Field{{Type: r.Go}} } return &FuncType{ Params: p, @@ -1293,7 +1365,7 @@ func (c *typeConv) Opaque(n int64) ast.Expr { func (c *typeConv) intExpr(n int64) ast.Expr { return &ast.BasicLit{ Kind: token.INT, - Value: strconv.Itoa64(n), + Value: strconv.FormatInt(n, 10), } } @@ -1306,7 +1378,7 @@ func (c *typeConv) pad(fld []*ast.Field, size int64) []*ast.Field { } // Struct conversion: return Go and (6g) C syntax for type. -func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax string, align int64) { +func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.StructType, csyntax string, align int64) { var buf bytes.Buffer buf.WriteString("struct {") fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field @@ -1324,38 +1396,61 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s ident[f.Name] = f.Name used[f.Name] = true } - for cid, goid := range ident { - if token.Lookup([]byte(goid)).IsKeyword() { - // Avoid keyword - goid = "_" + goid - // Also avoid existing fields - for _, exist := used[goid]; exist; _, exist = used[goid] { + if !*godefs && !*cdefs { + for cid, goid := range ident { + if token.Lookup(goid).IsKeyword() { + // Avoid keyword goid = "_" + goid - } - used[goid] = true - ident[cid] = goid + // Also avoid existing fields + for _, exist := used[goid]; exist; _, exist = used[goid] { + goid = "_" + goid + } + + used[goid] = true + ident[cid] = goid + } } } + anon := 0 for _, f := range dt.Field { - if f.BitSize > 0 && f.BitSize != f.ByteSize*8 { - continue - } if f.ByteOffset > off { fld = c.pad(fld, f.ByteOffset-off) off = f.ByteOffset } - t := c.Type(f.Type) + t := c.Type(f.Type, pos) + tgo := t.Go + size := t.Size + + if f.BitSize > 0 { + if f.BitSize%8 != 0 { + continue + } + size = f.BitSize / 8 + name := tgo.(*ast.Ident).String() + if strings.HasPrefix(name, "int") { + name = "int" + } else { + name = "uint" + } + tgo = ast.NewIdent(name + fmt.Sprint(f.BitSize)) + } + n := len(fld) fld = fld[0 : n+1] - - fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[f.Name])}, Type: t.Go} - off += t.Size + name := f.Name + if name == "" { + name = fmt.Sprintf("anon%d", anon) + anon++ + ident[name] = name + } + fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[name])}, Type: tgo} + off += size buf.WriteString(t.C.String()) buf.WriteString(" ") - buf.WriteString(f.Name) + buf.WriteString(name) buf.WriteString("; ") if t.Align > align { align = t.Align @@ -1366,10 +1461,100 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s off = dt.ByteSize } if off != dt.ByteSize { - fatalf("struct size calculation error") + fatalf("%s: struct size calculation error off=%d bytesize=%d", lineno(pos), off, dt.ByteSize) } buf.WriteString("}") csyntax = buf.String() + + if *godefs || *cdefs { + godefsFields(fld) + } expr = &ast.StructType{Fields: &ast.FieldList{List: fld}} return } + +func upper(s string) string { + if s == "" { + return "" + } + r, size := utf8.DecodeRuneInString(s) + if r == '_' { + return "X" + s + } + return string(unicode.ToUpper(r)) + s[size:] +} + +// godefsFields rewrites field names for use in Go or C definitions. +// It strips leading common prefixes (like tv_ in tv_sec, tv_usec) +// converts names to upper case, and rewrites _ into Pad_godefs_n, +// so that all fields are exported. +func godefsFields(fld []*ast.Field) { + prefix := fieldPrefix(fld) + 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 == "_" { + // Use exported name instead. + n.Name = "Pad_cgo_" + strconv.Itoa(npad) + npad++ + } + if !*cdefs { + n.Name = upper(n.Name) + } + } + p := &f.Type + t := *p + if star, ok := t.(*ast.StarExpr); ok { + star = &ast.StarExpr{X: star.X} + *p = star + p = &star.X + t = *p + } + if id, ok := t.(*ast.Ident); ok { + if id.Name == "unsafe.Pointer" { + *p = ast.NewIdent("*byte") + } + } + } +} + +// fieldPrefix returns the prefix that should be removed from all the +// 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 +// (so sec, usec, which will get turned into Sec, Usec for exporting). +func fieldPrefix(fld []*ast.Field) string { + if *cdefs { + return "" + } + prefix := "" + for _, f := range fld { + for _, n := range f.Names { + // Ignore field names that don't have the prefix we're + // looking for. It is common in C headers to have fields + // 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 + // x86 ptrace register sets, which otherwise have all fields + // with reg_ prefixes. + if strings.HasPrefix(n.Name, "orig_") || strings.HasPrefix(n.Name, "_") { + continue + } + i := strings.Index(n.Name, "_") + if i < 0 { + continue + } + if prefix == "" { + prefix = n.Name[:i+1] + } else if prefix != n.Name[:i+1] { + return "" + } + } + } + return prefix +} diff --git a/src/cmd/cgo/godefs.go b/src/cmd/cgo/godefs.go new file mode 100644 index 000000000..fec70a334 --- /dev/null +++ b/src/cmd/cgo/godefs.go @@ -0,0 +1,289 @@ +// 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 ( + "bytes" + "fmt" + "go/ast" + "go/printer" + "go/token" + "os" + "strings" +) + +// godefs returns the output for -godefs mode. +func (p *Package) godefs(f *File, srcfile string) string { + var buf bytes.Buffer + + fmt.Fprintf(&buf, "// Created by cgo -godefs - DO NOT EDIT\n") + fmt.Fprintf(&buf, "// %s\n", strings.Join(os.Args, " ")) + fmt.Fprintf(&buf, "\n") + + override := make(map[string]string) + + // Allow source file to specify override mappings. + // For example, the socket data structures refer + // to in_addr and in_addr6 structs but we want to be + // able to treat them as byte arrays, so the godefs + // inputs in package syscall say + // + // // +godefs map struct_in_addr [4]byte + // // +godefs map struct_in_addr6 [16]byte + // + for _, g := range f.Comments { + for _, c := range g.List { + i := strings.Index(c.Text, "+godefs map") + if i < 0 { + continue + } + s := strings.TrimSpace(c.Text[i+len("+godefs map"):]) + i = strings.Index(s, " ") + if i < 0 { + fmt.Fprintf(os.Stderr, "invalid +godefs map comment: %s\n", c.Text) + continue + } + override["_Ctype_"+strings.TrimSpace(s[:i])] = strings.TrimSpace(s[i:]) + } + } + for _, n := range f.Name { + if s := override[n.Go]; s != "" { + override[n.Mangle] = s + } + } + + // Otherwise, if the source file says type T C.whatever, + // use "T" as the mangling of C.whatever, + // except in the definition (handled at end of function). + refName := make(map[*ast.Expr]*Name) + for _, r := range f.Ref { + refName[r.Expr] = r.Name + } + for _, d := range f.AST.Decls { + d, ok := d.(*ast.GenDecl) + if !ok || d.Tok != token.TYPE { + continue + } + for _, s := range d.Specs { + s := s.(*ast.TypeSpec) + n := refName[&s.Type] + if n != nil && n.Mangle != "" { + override[n.Mangle] = s.Name.Name + } + } + } + + // Extend overrides using typedefs: + // If we know that C.xxx should format as T + // and xxx is a typedef for yyy, make C.yyy format as T. + for typ, def := range typedef { + if new := override[typ]; new != "" { + if id, ok := def.Go.(*ast.Ident); ok { + override[id.Name] = new + } + } + } + + // Apply overrides. + for old, new := range override { + if id := goIdent[old]; id != nil { + id.Name = new + } + } + + // Any names still using the _C syntax are not going to compile, + // although in general we don't know whether they all made it + // into the file, so we can't warn here. + // + // The most common case is union types, which begin with + // _Ctype_union and for which typedef[name] is a Go byte + // array of the appropriate size (such as [4]byte). + // Substitute those union types with byte arrays. + for name, id := range goIdent { + if id.Name == name && strings.Contains(name, "_Ctype_union") { + if def := typedef[name]; def != nil { + id.Name = gofmt(def) + } + } + } + + conf.Fprint(&buf, fset, f.AST) + + return buf.String() +} + +// cdefs returns the output for -cdefs mode. +// The easiest way to do this is to translate the godefs Go to C. +func (p *Package) cdefs(f *File, srcfile string) string { + godefsOutput := p.godefs(f, srcfile) + + lines := strings.Split(godefsOutput, "\n") + lines[0] = "// Created by cgo -cdefs - DO NOT EDIT" + + for i, line := range lines { + lines[i] = strings.TrimSpace(line) + } + + var out bytes.Buffer + printf := func(format string, args ...interface{}) { fmt.Fprintf(&out, format, args...) } + + didTypedef := false + for i := 0; i < len(lines); i++ { + line := lines[i] + + // Delete + // package x + if strings.HasPrefix(line, "package ") { + continue + } + + // Convert + // const ( + // A = 1 + // B = 2 + // ) + // + // to + // + // enum { + // A = 1, + // B = 2, + // }; + if line == "const (" { + printf("enum {\n") + for i++; i < len(lines) && lines[i] != ")"; i++ { + line = lines[i] + if line != "" { + printf("\t%s,", line) + } + printf("\n") + } + printf("};\n") + continue + } + + // Convert + // const A = 1 + // to + // enum { A = 1 }; + if strings.HasPrefix(line, "const ") { + printf("enum { %s };\n", line[len("const "):]) + continue + } + + // On first type definition, typedef all the structs + // in case there are dependencies between them. + if !didTypedef && strings.HasPrefix(line, "type ") { + didTypedef = true + 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 {")] + printf("typedef struct %s %s;\n", s, s) + } + } + printf("\n") + printf("#pragma pack on\n") + printf("\n") + } + + // Convert + // type T struct { + // X int64 + // Y *int32 + // Z [4]byte + // } + // + // to + // + // struct T { + // int64 X; + // int32 *Y; + // byte Z[4]; + // } + if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") { + s := line[len("type ") : len(line)-len(" struct {")] + printf("struct %s {\n", s) + for i++; i < len(lines) && lines[i] != "}"; i++ { + line := lines[i] + if line != "" { + f := strings.Fields(line) + if len(f) != 2 { + fmt.Fprintf(os.Stderr, "cgo: cannot parse struct field: %s\n", line) + nerrors++ + continue + } + printf("\t%s;", cdecl(f[0], f[1])) + } + printf("\n") + } + printf("};\n") + continue + } + + // Convert + // type T int + // to + // typedef int T; + if strings.HasPrefix(line, "type ") { + f := strings.Fields(line[len("type "):]) + if len(f) != 2 { + fmt.Fprintf(os.Stderr, "cgo: cannot parse type definition: %s\n", line) + nerrors++ + continue + } + printf("typedef\t%s;\n", cdecl(f[0], f[1])) + continue + } + + printf("%s\n", line) + } + + if didTypedef { + printf("\n") + printf("#pragma pack off\n") + } + + return out.String() +} + +// cdecl returns the C declaration for the given Go name and type. +// It only handles the specific cases necessary for converting godefs output. +func cdecl(name, typ string) string { + // X *[0]byte -> X *void + if strings.HasPrefix(typ, "*[0]") { + typ = "*void" + } + // X *byte -> *X byte + if strings.HasPrefix(typ, "*") { + name = "*" + name + typ = typ[1:] + } + // X [4]byte -> X[4] byte + if strings.HasPrefix(typ, "[") { + i := strings.Index(typ, "]") + 1 + name = name + typ[:i] + typ = typ[i:] + } + // X T -> T X + // Handle the special case: 'unsafe.Pointer' is 'void *' + if typ == "unsafe.Pointer" { + typ = "void" + name = "*" + name + } + return typ + "\t" + name +} + +var gofmtBuf bytes.Buffer + +// gofmt returns the gofmt-formatted string for an AST node. +func gofmt(n interface{}) string { + gofmtBuf.Reset() + err := printer.Fprint(&gofmtBuf, fset, n) + if err != nil { + return "<" + err.Error() + ">" + } + return gofmtBuf.String() +} diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index be9c2bc4f..7449f04c4 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -15,11 +15,13 @@ import ( "flag" "fmt" "go/ast" + "go/printer" "go/token" "io" "os" "path/filepath" "reflect" + "runtime" "strings" ) @@ -37,11 +39,13 @@ type Package struct { Decl []ast.Decl GoFiles []string // list of Go files GccFiles []string // list of gcc output files + Preamble string // collected preamble for _cgo_export.h } // A File collects information about a single Go input file. type File struct { AST *ast.File // parsed AST + Comments []*ast.CommentGroup // comments from file Package string // Package name Preamble string // C preamble (doc comment on import "C") Ref []*Ref // all references to C.xxx in AST @@ -95,6 +99,7 @@ type Type struct { C *TypeRepr Go ast.Expr EnumValues map[string]int64 + Typedef string } // A FuncType collects information about a function type in both the C and Go worlds. @@ -121,6 +126,19 @@ 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") + +// These flags are for bootstrapping a new Go implementation, +// to generate Go and C headers that match the data layout and +// constant values used in the host's C libraries and system calls. +var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output") +var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C file to standard output") +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 importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code") +var goarch, goos string func main() { flag.Usage = usage @@ -130,7 +148,7 @@ func main() { // cgo -dynimport is essentially a separate helper command // built into the cgo binary. It scans a gcc-produced executable // and dumps information about the imported symbols and the - // imported libraries. The Make.pkg rules for cgo prepare an + // imported libraries. The 'go build' rules for cgo prepare an // appropriate executable and then use its import information // instead of needing to make the linkers duplicate all the // specialized knowledge gcc has about where to look for imported @@ -139,6 +157,18 @@ func main() { return } + if *godefs && *cdefs { + fmt.Fprintf(os.Stderr, "cgo: cannot use -cdefs and -godefs together\n") + os.Exit(2) + } + + if *godefs || *cdefs { + // Generating definitions pulled from header files, + // to be checked into Go repositories. + // Line numbers are just noise. + conf.Mode &^= printer.SourcePos + } + args := flag.Args() if len(args) < 1 { usage() @@ -156,32 +186,9 @@ func main() { usage() } - // Copy it to a new slice so it can grow. - gccOptions := make([]string, i) - copy(gccOptions, args[0:i]) - goFiles := args[i:] - arch := os.Getenv("GOARCH") - if arch == "" { - fatalf("$GOARCH is not set") - } - ptrSize := ptrSizeMap[arch] - if ptrSize == 0 { - fatalf("unknown $GOARCH %q", arch) - } - - // Clear locale variables so gcc emits English errors [sic]. - os.Setenv("LANG", "en_US.UTF-8") - os.Setenv("LC_ALL", "C") - os.Setenv("LC_CTYPE", "C") - - p := &Package{ - PtrSize: ptrSize, - GccOptions: gccOptions, - CgoFlags: make(map[string]string), - Written: make(map[string]bool), - } + p := newPackage(args[:i]) // Need a unique prefix for the global C symbols that // we use to coordinate between gcc and ourselves. @@ -197,7 +204,7 @@ func main() { io.Copy(h, f) f.Close() } - cPrefix = fmt.Sprintf("_%x", h.Sum()[0:6]) + cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6]) fs := make([]*File, len(goFiles)) for i, input := range goFiles { @@ -208,9 +215,13 @@ func main() { fs[i] = f } - // make sure that _obj directory exists, so that we can write - // all the output files there. - os.Mkdir("_obj", 0777) + if *objDir == "" { + // make sure that _obj directory exists, so that we can write + // all the output files there. + os.Mkdir("_obj", 0777) + *objDir = "_obj" + } + *objDir += string(filepath.Separator) for i, input := range goFiles { f := fs[i] @@ -232,23 +243,64 @@ func main() { pkg = filepath.Join(dir, pkg) } p.PackagePath = pkg - p.writeOutput(f, input) - p.Record(f) + if *godefs { + os.Stdout.WriteString(p.godefs(f, input)) + } else if *cdefs { + os.Stdout.WriteString(p.cdefs(f, input)) + } else { + p.writeOutput(f, input) + } } - p.writeDefs() + if !*godefs && !*cdefs { + p.writeDefs() + } if nerrors > 0 { os.Exit(2) } } +// newPackage returns a new Package that will invoke +// gcc with the additional arguments specified in args. +func newPackage(args []string) *Package { + // Copy the gcc options to a new slice so the list + // can grow without overwriting the slice that args is in. + gccOptions := make([]string, len(args)) + copy(gccOptions, args) + + goarch = runtime.GOARCH + if s := os.Getenv("GOARCH"); s != "" { + goarch = s + } + goos = runtime.GOOS + if s := os.Getenv("GOOS"); s != "" { + goos = s + } + ptrSize := ptrSizeMap[goarch] + if ptrSize == 0 { + fatalf("unknown $GOARCH %q", goarch) + } + + // Reset locale variables so gcc emits English errors [sic]. + os.Setenv("LANG", "en_US.UTF-8") + os.Setenv("LC_ALL", "C") + + p := &Package{ + PtrSize: ptrSize, + GccOptions: gccOptions, + CgoFlags: make(map[string]string), + Written: make(map[string]bool), + } + return p +} + // Record what needs to be recorded about f. func (p *Package) Record(f *File) { if p.PackageName == "" { p.PackageName = f.Package } else if p.PackageName != f.Package { - error(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package) + error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package) } if p.Name == nil { @@ -258,11 +310,14 @@ func (p *Package) Record(f *File) { if p.Name[k] == nil { p.Name[k] = v } else if !reflect.DeepEqual(p.Name[k], v) { - error(token.NoPos, "inconsistent definitions for C.%s", k) + error_(token.NoPos, "inconsistent definitions for C.%s", k) } } } - p.ExpFunc = append(p.ExpFunc, f.ExpFunc...) + if f.ExpFunc != nil { + p.ExpFunc = append(p.ExpFunc, f.ExpFunc...) + p.Preamble += "\n" + f.Preamble + } p.Decl = append(p.Decl, f.AST.Decls...) } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 498ab1566..814250c2e 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -14,20 +14,19 @@ import ( "go/printer" "go/token" "os" - "path/filepath" "strings" ) -var objDir = "_obj" + string(filepath.Separator) +var conf = printer.Config{Mode: printer.SourcePos, Tabwidth: 8} // writeDefs creates output files to be compiled by 6g, 6c, and gcc. // (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.) func (p *Package) writeDefs() { - fgo2 := creat(objDir + "_cgo_gotypes.go") - fc := creat(objDir + "_cgo_defun.c") - fm := creat(objDir + "_cgo_main.c") + fgo2 := creat(*objDir + "_cgo_gotypes.go") + fc := creat(*objDir + "_cgo_defun.c") + fm := creat(*objDir + "_cgo_main.c") - fflg := creat(objDir + "_cgo_flags") + fflg := creat(*objDir + "_cgo_flags") for k, v := range p.CgoFlags { fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v) } @@ -35,7 +34,13 @@ func (p *Package) writeDefs() { // Write C main file for using gcc to resolve imports. fmt.Fprintf(fm, "int main() { return 0; }\n") - fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") + if *importRuntimeCgo { + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") + } else { + // If we're not importing runtime/cgo, we *are* runtime/cgo, + // which provides crosscall2. We just need a prototype. + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c);") + } fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n") fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n") @@ -45,19 +50,25 @@ 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 \"os\"\n\n") - fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") + 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 *os.Error, x int) { *dst = os.Errno(x) }\n") + fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int) { *dst = syscall.Errno(x) }\n") for name, def := range typedef { fmt.Fprintf(fgo2, "type %s ", name) - printer.Fprint(fgo2, fset, def) - fmt.Fprintf(fgo2, "\n") + conf.Fprint(fgo2, fset, def.Go) + fmt.Fprintf(fgo2, "\n\n") } fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n") - fmt.Fprintf(fc, cProlog) + if *gccgo { + fmt.Fprintf(fc, cPrologGccgo) + } else { + fmt.Fprintf(fc, cProlog) + } cVars := make(map[string]bool) for _, n := range p.Name { @@ -78,7 +89,7 @@ func (p *Package) writeDefs() { fmt.Fprintf(fc, "\n") fmt.Fprintf(fgo2, "var %s ", n.Mangle) - printer.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go}) + conf.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go}) fmt.Fprintf(fgo2, "\n") } fmt.Fprintf(fc, "\n") @@ -96,13 +107,26 @@ func (p *Package) writeDefs() { } } - p.writeExports(fgo2, fc, fm) + if *gccgo { + p.writeGccgoExports(fgo2, fc, fm) + } else { + p.writeExports(fgo2, fc, fm) + } fgo2.Close() fc.Close() } func dynimport(obj string) { + stdout := os.Stdout + if *dynout != "" { + f, err := os.Create(*dynout) + if err != nil { + fatalf("%s", err) + } + stdout = f + } + if f, err := elf.Open(obj); err == nil { sym, err := f.ImportedSymbols() if err != nil { @@ -113,14 +137,14 @@ func dynimport(obj string) { if s.Version != "" { targ += "@" + s.Version } - fmt.Printf("#pragma dynimport %s %s %q\n", s.Name, targ, s.Library) + fmt.Fprintf(stdout, "#pragma dynimport %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.Printf("#pragma dynimport _ _ %q\n", l) + fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l) } return } @@ -134,14 +158,14 @@ func dynimport(obj string) { if len(s) > 0 && s[0] == '_' { s = s[1:] } - fmt.Printf("#pragma dynimport %s %s %q\n", s, s, "") + fmt.Fprintf(stdout, "#pragma dynimport %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.Printf("#pragma dynimport _ _ %q\n", l) + fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l) } return } @@ -153,7 +177,7 @@ func dynimport(obj string) { } for _, s := range sym { ss := strings.Split(s, ":") - fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) + fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) } return } @@ -176,7 +200,11 @@ func (p *Package) structType(n *Name) (string, int64) { fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) off += pad } - fmt.Fprintf(&buf, "\t\t%s p%d;\n", t.C, i) + c := t.Typedef + if c == "" { + c = t.C.String() + } + fmt.Fprintf(&buf, "\t\t%s p%d;\n", c, i) off += t.Size } if off%p.PtrSize != 0 { @@ -203,7 +231,7 @@ func (p *Package) structType(n *Name) (string, int64) { off += pad } if n.AddError { - fmt.Fprint(&buf, "\t\tvoid *e[2]; /* os.Error */\n") + fmt.Fprint(&buf, "\t\tvoid *e[2]; /* error */\n") off += 2 * p.PtrSize } if off == 0 { @@ -217,9 +245,9 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { name := n.Go gtype := n.FuncType.Go if n.AddError { - // Add "os.Error" to return type list. + // Add "error" to return type list. // Type list is known to be 0 or 1 element - it's a C function. - err := &ast.Field{Type: ast.NewIdent("os.Error")} + err := &ast.Field{Type: ast.NewIdent("error")} l := gtype.Results.List if len(l) == 0 { l = []*ast.Field{err} @@ -237,8 +265,45 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { Name: ast.NewIdent(n.Mangle), Type: gtype, } - printer.Fprint(fgo2, fset, d) - fmt.Fprintf(fgo2, "\n") + + 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) + } + 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) + l := d.Type.Results.List + d.Type.Results.List = l[:len(l)-1] + conf.Fprint(fgo2, fset, d) + fmt.Fprint(fgo2, "\n") + } + return + } + conf.Fprint(fgo2, fset, d) + fmt.Fprint(fgo2, "\n") if name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes" { // The builtins are already defined in the C prolog. @@ -276,7 +341,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { v[0] = 0; v[1] = 0; } else { - ·_Cerrno(v, e); /* fill in v as os.Error for errno e */ + ·_Cerrno(v, e); /* fill in v as error for errno e */ } }`) } @@ -292,16 +357,15 @@ func (p *Package) writeOutput(f *File, srcfile string) { base = base[0 : len(base)-3] } base = strings.Map(slashToUnderscore, base) - fgo1 := creat(objDir + base + ".cgo1.go") - fgcc := creat(objDir + base + ".cgo2.c") + fgo1 := creat(*objDir + base + ".cgo1.go") + fgcc := creat(*objDir + base + ".cgo2.c") p.GoFiles = append(p.GoFiles, base+".cgo1.go") p.GccFiles = append(p.GccFiles, base+".cgo2.c") // Write Go output: Go input with rewrites of C.xxx to _C_xxx. fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n\n") - fmt.Fprintf(fgo1, "//line %s:1\n", srcfile) - printer.Fprint(fgo1, fset, f.AST) + conf.Fprint(fgo1, fset, f.AST) // While we process the vars and funcs, also write 6c and gcc output. // Gcc output starts with the preamble. @@ -327,6 +391,11 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { } p.Written[name] = true + if *gccgo { + // we don't use wrappers with gccgo. + return + } + ctype, _ := p.structType(n) // Gcc wrapper unpacks the C argument struct @@ -351,10 +420,20 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { } } fmt.Fprintf(fgcc, "%s(", n.C) - for i := range n.FuncType.Params { + for i, t := range n.FuncType.Params { if i > 0 { fmt.Fprintf(fgcc, ", ") } + // We know the type params are correct, because + // the Go equivalents had good type params. + // However, our version of the type omits the magic + // words const and volatile, which can provoke + // C compiler warnings. Silence them by casting + // all pointers to void*. (Eventually that will produce + // other warnings.) + if c := t.C.String(); c[len(c)-1] == '*' { + fmt.Fprintf(fgcc, "(void*)") + } fmt.Fprintf(fgcc, "a->p%d", i) } fmt.Fprintf(fgcc, ");\n") @@ -368,10 +447,11 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { // Write out the various stubs we need to support functions exported // from Go so that they are callable from C. func (p *Package) writeExports(fgo2, fc, fm *os.File) { - fgcc := creat(objDir + "_cgo_export.c") - fgcch := creat("_cgo_export.h") + fgcc := creat(*objDir + "_cgo_export.c") + fgcch := creat(*objDir + "_cgo_export.h") 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(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") @@ -501,8 +581,10 @@ 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, "extern void ·%s();\n", goname) - fmt.Fprintf(fc, "\nvoid\n") + fmt.Fprintf(fc, "#pragma dynexport %s %s\n", goname, 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") fmt.Fprintf(fc, "_cgoexp%s_%s(void *a, int32 n)\n", cPrefix, exp.ExpName) fmt.Fprintf(fc, "{\n") fmt.Fprintf(fc, "\truntime·cgocallback(·%s, a, n);\n", goname) @@ -514,11 +596,11 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { // a Go wrapper function. if fn.Recv != nil { fmt.Fprintf(fgo2, "func %s(recv ", goname) - printer.Fprint(fgo2, fset, fn.Recv.List[0].Type) + conf.Fprint(fgo2, fset, fn.Recv.List[0].Type) forFieldList(fntype.Params, func(i int, atype ast.Expr) { fmt.Fprintf(fgo2, ", p%d ", i) - printer.Fprint(fgo2, fset, atype) + conf.Fprint(fgo2, fset, atype) }) fmt.Fprintf(fgo2, ")") if gccResult != "void" { @@ -528,7 +610,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { if i > 0 { fmt.Fprint(fgo2, ", ") } - printer.Fprint(fgo2, fset, atype) + conf.Fprint(fgo2, fset, atype) }) fmt.Fprint(fgo2, ")") } @@ -551,6 +633,83 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { } } +// Write out the C header allowing C code to call exported gccgo functions. +func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) { + fgcc := creat(*objDir + "_cgo_export.c") + fgcch := creat(*objDir + "_cgo_export.h") + _ = fgcc + + 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") + + 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) + + 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, + func(i int, atype ast.Expr) { resultCount++ }) + switch resultCount { + case 0: + fmt.Fprintf(cdeclBuf, "void") + case 1: + forFieldList(fntype.Results, + func(i int, atype ast.Expr) { + t := p.cgoType(atype) + fmt.Fprintf(cdeclBuf, "%s", t.C) + }) + default: + // Declare a result struct. + fmt.Fprintf(fgcch, "struct %s_result {\n", exp.ExpName) + forFieldList(fntype.Results, + func(i int, atype ast.Expr) { + t := p.cgoType(atype) + fmt.Fprintf(fgcch, "\t%s r%d;\n", t.C, i) + }) + fmt.Fprintf(fgcch, "};\n") + 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, " (") + // Function parameters. + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 { + fmt.Fprintf(cdeclBuf, ", ") + } + t := p.cgoType(atype) + fmt.Fprintf(cdeclBuf, "%s p%d", t.C, i) + }) + fmt.Fprintf(cdeclBuf, ")") + cdecl := cdeclBuf.String() + + fmt.Fprintf(fgcch, "extern %s __asm__(\"%s\");\n", cdecl, gccgoSymbol) + // Dummy declaration for _cgo_main.c + fmt.Fprintf(fm, "%s {}\n", cdecl) + } +} + // 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)) { @@ -577,22 +736,25 @@ func c(repr string, args ...interface{}) *TypeRepr { // Map predeclared Go types to Type. var goTypes = map[string]*Type{ - "int": &Type{Size: 4, Align: 4, C: c("int")}, - "uint": &Type{Size: 4, Align: 4, C: c("uint")}, - "int8": &Type{Size: 1, Align: 1, C: c("schar")}, - "uint8": &Type{Size: 1, Align: 1, C: c("uchar")}, - "int16": &Type{Size: 2, Align: 2, C: c("short")}, - "uint16": &Type{Size: 2, Align: 2, C: c("ushort")}, - "int32": &Type{Size: 4, Align: 4, C: c("int")}, - "uint32": &Type{Size: 4, Align: 4, C: c("uint")}, - "int64": &Type{Size: 8, Align: 8, C: c("int64")}, - "uint64": &Type{Size: 8, Align: 8, C: c("uint64")}, - "float": &Type{Size: 4, Align: 4, C: c("float")}, - "float32": &Type{Size: 4, Align: 4, C: c("float")}, - "float64": &Type{Size: 8, Align: 8, C: c("double")}, - "complex": &Type{Size: 8, Align: 8, C: c("__complex float")}, - "complex64": &Type{Size: 8, Align: 8, C: c("__complex float")}, - "complex128": &Type{Size: 16, Align: 16, C: c("__complex double")}, + "bool": {Size: 1, Align: 1, C: c("uchar")}, + "byte": {Size: 1, Align: 1, C: c("uchar")}, + "int": {Size: 4, Align: 4, C: c("int")}, + "uint": {Size: 4, Align: 4, C: c("uint")}, + "rune": {Size: 4, Align: 4, C: c("int")}, + "int8": {Size: 1, Align: 1, C: c("schar")}, + "uint8": {Size: 1, Align: 1, C: c("uchar")}, + "int16": {Size: 2, Align: 2, C: c("short")}, + "uint16": {Size: 2, Align: 2, C: c("ushort")}, + "int32": {Size: 4, Align: 4, C: c("int")}, + "uint32": {Size: 4, Align: 4, C: c("uint")}, + "int64": {Size: 8, Align: 8, C: c("int64")}, + "uint64": {Size: 8, Align: 8, C: c("uint64")}, + "float": {Size: 4, Align: 4, C: c("float")}, + "float32": {Size: 4, Align: 4, C: c("float")}, + "float64": {Size: 8, Align: 8, C: c("double")}, + "complex": {Size: 8, Align: 8, C: c("__complex float")}, + "complex64": {Size: 8, Align: 8, C: c("__complex float")}, + "complex128": {Size: 16, Align: 16, C: c("__complex double")}, } // Map an ast type to a Type. @@ -610,7 +772,7 @@ func (p *Package) cgoType(e ast.Expr) *Type { case *ast.FuncType: return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")} case *ast.InterfaceType: - return &Type{Size: 3 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} + return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} case *ast.MapType: return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoMap")} case *ast.ChanType: @@ -633,10 +795,8 @@ func (p *Package) cgoType(e ast.Expr) *Type { } } } - for name, def := range typedef { - if name == t.Name { - return p.cgoType(def) - } + if def := typedef[t.Name]; def != nil { + return def } if t.Name == "uintptr" { return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("uintptr")} @@ -644,13 +804,16 @@ func (p *Package) cgoType(e ast.Expr) *Type { if t.Name == "string" { return &Type{Size: p.PtrSize + 4, 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.Align > p.PtrSize { r.Align = p.PtrSize } return r } - error(e.Pos(), "unrecognized Go type %s", t.Name) + error_(e.Pos(), "unrecognized Go type %s", t.Name) return &Type{Size: 4, Align: 4, C: c("int")} case *ast.SelectorExpr: id, ok := t.X.(*ast.Ident) @@ -658,7 +821,7 @@ func (p *Package) cgoType(e ast.Expr) *Type { return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")} } } - error(e.Pos(), "unrecognized Go type %T", e) + error_(e.Pos(), "Go type not supported in export: %s", gofmt(e)) return &Type{Size: 4, Align: 4, C: c("int")} } @@ -729,6 +892,43 @@ void } ` +const cPrologGccgo = ` +#include +#include + +struct __go_string { + const unsigned char *__data; + int __length; +}; + +typedef struct __go_open_array { + void* __values; + int __count; + int __capacity; +} Slice; + +struct __go_string __go_byte_array_to_string(const void* p, int len); +struct __go_open_array __go_string_to_byte_array (struct __go_string str); + +const char *CString(struct __go_string s) { + return strndup((const char*)s.__data, s.__length); +} + +struct __go_string GoString(char *p) { + int len = (p != NULL) ? strlen(p) : 0; + return __go_byte_array_to_string(p, len); +} + +struct __go_string GoStringN(char *p, int n) { + return __go_byte_array_to_string(p, n); +} + +Slice GoBytes(char *p, int n) { + struct __go_string s = { (const unsigned char *)p, n }; + return __go_string_to_byte_array(s); +} +` + const gccExportHeaderProlog = ` typedef unsigned int uint; typedef signed char schar; diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go index e79b0e1bf..a0f216614 100644 --- a/src/cmd/cgo/util.go +++ b/src/cmd/cgo/util.go @@ -5,11 +5,11 @@ package main import ( - "exec" "fmt" "go/token" "io/ioutil" "os" + "os/exec" ) // run runs the command argv, feeding in stdin on standard input. @@ -36,7 +36,6 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { if err != nil { fatalf("%s", err) } - defer p.Release() r0.Close() w1.Close() w2.Close() @@ -56,23 +55,31 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { <-c <-c - w, err := p.Wait(0) + state, err := p.Wait() if err != nil { fatalf("%s", err) } - ok = w.Exited() && w.ExitStatus() == 0 + ok = state.Success() return } +func lineno(pos token.Pos) string { + return fset.Position(pos).String() +} + // Die with an error message. func fatalf(msg string, args ...interface{}) { - fmt.Fprintf(os.Stderr, msg+"\n", args...) + // If we've already printed other errors, they might have + // caused the fatal condition. Assume they're enough. + if nerrors == 0 { + fmt.Fprintf(os.Stderr, msg+"\n", args...) + } os.Exit(2) } var nerrors int -func error(pos token.Pos, msg string, args ...interface{}) { +func error_(pos token.Pos, msg string, args ...interface{}) { nerrors++ if pos.IsValid() { fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String()) @@ -102,7 +109,7 @@ func creat(name string) *os.File { return f } -func slashToUnderscore(c int) int { +func slashToUnderscore(c rune) rune { if c == '/' || c == '\\' || c == ':' { c = '_' } diff --git a/src/cmd/cov/Makefile b/src/cmd/cov/Makefile deleted file mode 100644 index 95dba9c60..000000000 --- a/src/cmd/cov/Makefile +++ /dev/null @@ -1,39 +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 ../../Make.inc -O:=$(HOST_O) - -# The directory is cov because the source is portable and general. -# We call the binary 6cov to avoid confusion and because this binary -# is linked only with amd64 and x86 support. - -TARG=6cov -OFILES=\ - main.$O\ - tree.$O\ - -HFILES=\ - tree.h\ - -NOINSTALL=1 -include ../../Make.ccmd - -ifeq ($(GOOS),windows) -NAME=windows -else -NAME=$(shell uname | tr A-Z a-z) -endif - -install: install-$(NAME) -install-linux: install-default -install-freebsd: install-default -install-windows: install-default - -# on Darwin, have to install and setgid; see $GOROOT/src/sudo.bash -install-darwin: $(TARG) - @true - -install-default: $(TARG) - cp $(TARG) "$(GOBIN)"/$(TARG) diff --git a/src/cmd/cov/doc.go b/src/cmd/cov/doc.go deleted file mode 100644 index 5de00e19c..000000000 --- a/src/cmd/cov/doc.go +++ /dev/null @@ -1,33 +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. - -/* - -Cov is a rudimentary code coverage tool. - -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". - -Usage: cov [-lsv] [-g substring] [-m minlines] [6.out args] - -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 - -For reasons of disambiguation it is installed as 6cov although it also serves -as an 8cov and a 5cov. - -*/ -package documentation diff --git a/src/cmd/cov/main.c b/src/cmd/cov/main.c deleted file mode 100644 index 5ff22c00a..000000000 --- a/src/cmd/cov/main.c +++ /dev/null @@ -1,480 +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. - -/* - * code coverage - */ - -#include -#include -#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); - 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); - 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) -{ - 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"); - execv(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"; - argc++; - } - 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 deleted file mode 100644 index 116772e42..000000000 --- a/src/cmd/cov/tree.c +++ /dev/null @@ -1,246 +0,0 @@ -// 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" - -#define TreeNode TreeNode -#define Tree Tree - -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); - 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 deleted file mode 100644 index a716d83ad..000000000 --- a/src/cmd/cov/tree.h +++ /dev/null @@ -1,47 +0,0 @@ -// 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 new file mode 100644 index 000000000..cf194faf0 --- /dev/null +++ b/src/cmd/dist/README @@ -0,0 +1,45 @@ +This program, dist, is the bootstrapping tool for the Go distribution. +It takes care of building the C programs (like the Go compiler) and +the initial bootstrap copy of the go tool. It also serves as a catch-all +to replace odd jobs previously done with shell scripts. + +Dist is itself written in very simple C. All interaction with C libraries, +even standard C libraries, is confined to a single system-specific file +(plan9.c, unix.c, windows.c), to aid portability. Functionality needed +by other files should be exposed via the portability layer. Functions +in the portability layer begin with an x prefix when they would otherwise +use the same name as or be confused for an existing function. +For example, xprintf is the portable printf. + +By far the most common data types in dist are strings and arrays of +strings. Instead of using char* and char**, though, dist uses two named +data structures, Buf and Vec, which own all the data they point at. +The Buf operations are functions beginning with b; the Vec operations +are functions beginning with v. The basic form of any function declaring +Bufs or Vecs on the stack should be + + void + myfunc(void) + { + Buf b1, b2; + Vec v1; + + binit(&b1); + binit(&b2); + vinit(&v1); + + ... 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 + + bfree(&b1); + bfree(&b2); + vfree(&v1); + } + +The binit/vinit calls prepare a buffer or vector for use, initializing the +data structures, and the bfree/vfree calls free any memory they are still +holding onto. Use of this idiom gives us lexically scoped allocations. + diff --git a/src/cmd/dist/a.h b/src/cmd/dist/a.h new file mode 100644 index 000000000..c19b1f468 --- /dev/null +++ b/src/cmd/dist/a.h @@ -0,0 +1,149 @@ +// 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. + +typedef int bool; + +// The Time unit is unspecified; we just need to +// be able to compare whether t1 is older than t2 with t1 < t2. +typedef long long Time; + +#define nil ((void*)0) +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define USED(x) ((void)(x)) + +// A Buf is a byte buffer, like Go's []byte. +typedef struct Buf Buf; +struct Buf +{ + char *p; + int len; + int cap; +}; + +// A Vec is a string vector, like Go's []string. +typedef struct Vec Vec; +struct Vec +{ + char **p; + int len; + int cap; +}; + +// Modes for run. +enum { + CheckExit = 1, +}; + +// buf.c +bool bequal(Buf *s, Buf *t); +void bsubst(Buf *b, char *x, char *y); +void bfree(Buf *b); +void bgrow(Buf *b, int n); +void binit(Buf *b); +char* bpathf(Buf *b, char *fmt, ...); +char* bprintf(Buf *b, char *fmt, ...); +void bwritef(Buf *b, char *fmt, ...); +void breset(Buf *b); +char* bstr(Buf *b); +char* btake(Buf *b); +void bwrite(Buf *b, void *v, int n); +void bwriteb(Buf *dst, Buf *src); +void bwritestr(Buf *b, char *p); +void bswap(Buf *b, Buf *b1); +void vadd(Vec *v, char *p); +void vcopy(Vec *dst, char **src, int n); +void vfree(Vec *v); +void vgrow(Vec *v, int n); +void vinit(Vec *v); +void vreset(Vec *v); +void vuniq(Vec *v); +void splitlines(Vec*, char*); +void splitfields(Vec*, char*); + +// build.c +extern char *goarch; +extern char *gobin; +extern char *gochar; +extern char *gohostarch; +extern char *gohostos; +extern char *goos; +extern char *goroot; +extern char *goroot_final; +extern char *goversion; +extern char *workdir; +extern char *tooldir; +extern char *slash; +extern bool rebuildall; + +int find(char*, char**, int); +void init(void); +void cmdbanner(int, char**); +void cmdbootstrap(int, char**); +void cmdclean(int, char**); +void cmdenv(int, char**); +void cmdinstall(int, char**); +void cmdversion(int, char**); + +// buildgc.c +void gcopnames(char*, char*); +void mkenam(char*, char*); + +// buildruntime.c +void mkzasm(char*, char*); +void mkzgoarch(char*, char*); +void mkzgoos(char*, char*); +void mkzruntimedefs(char*, char*); +void mkzversion(char*, char*); + +// goc2c.c +void goc2c(char*, char*); + +// main.c +extern int vflag; +void usage(void); +void xmain(int argc, char **argv); + +// portability layer (plan9.c, unix.c, windows.c) +bool contains(char *p, char *sep); +void fatal(char *msg, ...); +bool hasprefix(char *p, char *prefix); +bool hassuffix(char *p, char *suffix); +bool isabs(char*); +bool isdir(char *p); +bool isfile(char *p); +char* lastelem(char*); +Time mtime(char*); +void readfile(Buf*, char*); +void run(Buf *b, char *dir, int mode, char *cmd, ...); +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*); +void writefile(Buf*, char*, int); +void xatexit(void (*f)(void)); +void xexit(int); +void xfree(void*); +void xgetenv(Buf *b, char *name); +void xgetwd(Buf *b); +void* xmalloc(int n); +void* xmalloc(int); +int xmemcmp(void*, void*, int); +void xmemmove(void*, void*, int); +void xmkdir(char *p); +void xmkdirall(char*); +Time xmtime(char *p); +void xprintf(char*, ...); +void xqsort(void*, int, int, int(*)(const void*, const void*)); +void xreaddir(Vec *dst, char *dir); +void* xrealloc(void*, int); +void xrealwd(Buf *b, char *path); +void xremove(char *p); +void xremoveall(char *p); +void xsetenv(char*, char*); +int xstrcmp(char*, char*); +char* xstrdup(char *p); +int xstrlen(char*); +char* xstrrchr(char*, int); +char* xstrstr(char*, char*); +char* xworkdir(void); diff --git a/src/cmd/dist/arg.h b/src/cmd/dist/arg.h new file mode 100644 index 000000000..6eef0353b --- /dev/null +++ b/src/cmd/dist/arg.h @@ -0,0 +1,50 @@ +/* +Derived from Inferno include/kern.h. + +http://code.google.com/p/inferno-os/source/browse/include/kern.h + + 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. +*/ + +/* command line */ +extern char *argv0; +#define ARGBEGIN for((argv0?0:(argv0=(*argv))),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + char _argc;\ + _args = &argv[0][1];\ + 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); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), fatal("usage"), (char*)0))) + +#define ARGC() _argc + diff --git a/src/cmd/dist/buf.c b/src/cmd/dist/buf.c new file mode 100644 index 000000000..45fb1954d --- /dev/null +++ b/src/cmd/dist/buf.c @@ -0,0 +1,279 @@ +// 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. + +// Byte buffers and string vectors. + +#include "a.h" + +// binit prepares an uninitialized buffer for use. +void +binit(Buf *b) +{ + b->p = nil; + b->len = 0; + b->cap = 0; +} + +// breset truncates the buffer back to zero length. +void +breset(Buf *b) +{ + b->len = 0; +} + +// bfree frees the storage associated with a buffer. +void +bfree(Buf *b) +{ + xfree(b->p); + binit(b); +} + +// bgrow ensures that the buffer has at least n more bytes +// between its len and cap. +void +bgrow(Buf *b, int n) +{ + int want; + + want = b->len+n; + if(want > b->cap) { + b->cap = 2*want; + if(b->cap < 64) + b->cap = 64; + b->p = xrealloc(b->p, b->cap); + } +} + +// bwrite appends the n bytes at v to the buffer. +void +bwrite(Buf *b, void *v, int n) +{ + bgrow(b, n); + xmemmove(b->p+b->len, v, n); + b->len += n; +} + +// bwritestr appends the string p to the buffer. +void +bwritestr(Buf *b, char *p) +{ + bwrite(b, p, xstrlen(p)); +} + +// bstr returns a pointer to a NUL-terminated string of the +// buffer contents. The pointer points into the buffer. +char* +bstr(Buf *b) +{ + bgrow(b, 1); + b->p[b->len] = '\0'; + return b->p; +} + +// btake takes ownership of the string form of the buffer. +// After this call, the buffer has zero length and does not +// refer to the memory that btake returned. +char* +btake(Buf *b) +{ + char *p; + + p = bstr(b); + binit(b); + return p; +} + +// bwriteb appends the src buffer to the dst buffer. +void +bwriteb(Buf *dst, Buf *src) +{ + bwrite(dst, src->p, src->len); +} + +// bequal reports whether the buffers have the same content. +bool +bequal(Buf *s, Buf *t) +{ + return s->len == t->len && xmemcmp(s->p, t->p, s->len) == 0; +} + +// bsubst rewites b to replace all occurrences of x with y. +void +bsubst(Buf *b, char *x, char *y) +{ + char *p; + int nx, ny, pos; + + nx = xstrlen(x); + ny = xstrlen(y); + + pos = 0; + for(;;) { + p = xstrstr(bstr(b)+pos, x); + if(p == nil) + break; + if(nx != ny) { + if(nx < ny) { + pos = p - b->p; + bgrow(b, ny-nx); + p = b->p + pos; + } + xmemmove(p+ny, p+nx, (b->p+b->len)-(p+nx)); + } + xmemmove(p, y, ny); + pos = p+ny - b->p; + b->len += ny - nx; + } +} + +// The invariant with the vectors is that v->p[0:v->len] is allocated +// strings that are owned by the vector. The data beyond v->len may +// be garbage. + +// vinit prepares an uninitialized vector for use. +void +vinit(Vec *v) +{ + v->p = nil; + v->len = 0; + v->cap = 0; +} + +// vreset truncates the vector back to zero length. +void +vreset(Vec *v) +{ + int i; + + for(i=0; ilen; i++) { + xfree(v->p[i]); + v->p[i] = nil; + } + v->len = 0; +} + +// vfree frees the storage associated with the vector. +void +vfree(Vec *v) +{ + vreset(v); + xfree(v->p); + vinit(v); +} + + +// vgrow ensures that the vector has room for at least +// n more entries between len and cap. +void +vgrow(Vec *v, int n) +{ + int want; + + want = v->len+n; + if(want > v->cap) { + v->cap = 2*want; + if(v->cap < 64) + v->cap = 64; + v->p = xrealloc(v->p, v->cap*sizeof v->p[0]); + } +} + +// vcopy copies the srclen strings at src into the vector. +void +vcopy(Vec *dst, char **src, int srclen) +{ + int i; + + // use vadd, to make copies of strings + for(i=0; ip[v->len++] = p; +} + +// vaddn adds a string consisting of the n bytes at p to the vector. +void +vaddn(Vec *v, char *p, int n) +{ + char *q; + + vgrow(v, 1); + q = xmalloc(n+1); + xmemmove(q, p, n); + q[n] = '\0'; + v->p[v->len++] = q; +} + +static int +strpcmp(const void *a, const void *b) +{ + return xstrcmp(*(char**)a, *(char**)b); +} + +// vuniq sorts the vector and then discards duplicates, +// in the manner of sort | uniq. +void +vuniq(Vec *v) +{ + int i, n; + + xqsort(v->p, v->len, sizeof(v->p[0]), strpcmp); + n = 0; + for(i=0; ilen; i++) { + if(n>0 && streq(v->p[i], v->p[n-1])) + xfree(v->p[i]); + else + v->p[n++] = v->p[i]; + } + v->len = n; +} + +// splitlines replaces the vector v with the result of splitting +// the input p after each \n. +void +splitlines(Vec *v, char *p) +{ + int i; + char *start; + + vreset(v); + start = p; + for(i=0; p[i]; i++) { + if(p[i] == '\n') { + vaddn(v, start, (p+i+1)-start); + start = p+i+1; + } + } + if(*start != '\0') + vadd(v, start); +} + +// splitfields replaces the vector v with the result of splitting +// the input p into non-empty fields containing no spaces. +void +splitfields(Vec *v, char *p) +{ + char *start; + + vreset(v); + for(;;) { + while(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') + p++; + if(*p == '\0') + break; + start = p; + while(*p != ' ' && *p != '\t' && *p != '\r' && *p != '\n' && *p != '\0') + p++; + vaddn(v, start, p-start); + } +} diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c new file mode 100644 index 000000000..567c9f336 --- /dev/null +++ b/src/cmd/dist/build.c @@ -0,0 +1,1530 @@ +// 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" +#include "arg.h" + +/* + * Initialization for any invocation. + */ + +// The usual variables. +char *goarch; +char *gobin; +char *gohostarch; +char *gohostchar; +char *gohostos; +char *goos; +char *goroot = GOROOT_FINAL; +char *goroot_final = GOROOT_FINAL; +char *workdir; +char *tooldir; +char *gochar; +char *goversion; +char *slash; // / for unix, \ for windows + +bool rebuildall = 0; + +static bool shouldbuild(char*, char*); +static void copy(char*, char*, int); +static char *findgoversion(void); + +// The known architecture letters. +static char *gochars = "568"; + +// The known architectures. +static char *okgoarch[] = { + // same order as gochars + "arm", + "amd64", + "386", +}; + +// The known operating systems. +static char *okgoos[] = { + "darwin", + "linux", + "freebsd", + "netbsd", + "openbsd", + "plan9", + "windows", +}; + +static void rmworkdir(void); + +// find reports the first index of p in l[0:n], or else -1. +int +find(char *p, char **l, int n) +{ + int i; + + for(i=0; i 0) { + // if not "/", then strip trailing path separator + if(b.len >= 2 && b.p[b.len - 1] == slash[0]) + b.len--; + goroot = btake(&b); + } + + xgetenv(&b, "GOBIN"); + if(b.len == 0) + bprintf(&b, "%s%sbin", goroot, slash); + gobin = btake(&b); + + xgetenv(&b, "GOOS"); + if(b.len == 0) + bwritestr(&b, gohostos); + goos = btake(&b); + if(find(goos, okgoos, nelem(okgoos)) < 0) + fatal("unknown $GOOS %s", goos); + + p = bpathf(&b, "%s/include/u.h", goroot); + if(!isfile(p)) { + fatal("$GOROOT is not set correctly or not exported\n" + "\tGOROOT=%s\n" + "\t%s does not exist", goroot, p); + } + + xgetenv(&b, "GOHOSTARCH"); + if(b.len > 0) + gohostarch = btake(&b); + + i = find(gohostarch, okgoarch, nelem(okgoarch)); + if(i < 0) + fatal("unknown $GOHOSTARCH %s", gohostarch); + bprintf(&b, "%c", gochars[i]); + gohostchar = btake(&b); + + xgetenv(&b, "GOARCH"); + if(b.len == 0) + bwritestr(&b, gohostarch); + goarch = btake(&b); + i = find(goarch, okgoarch, nelem(okgoarch)); + if(i < 0) + fatal("unknown $GOARCH %s", goarch); + bprintf(&b, "%c", gochars[i]); + gochar = btake(&b); + + xsetenv("GOROOT", goroot); + xsetenv("GOARCH", goarch); + xsetenv("GOOS", goos); + + // Make the environment more predictable. + xsetenv("LANG", "C"); + xsetenv("LANGUAGE", "en_US.UTF8"); + + goversion = findgoversion(); + + workdir = xworkdir(); + xatexit(rmworkdir); + + bpathf(&b, "%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch); + tooldir = btake(&b); + + bfree(&b); +} + +// rmworkdir deletes the work directory. +static void +rmworkdir(void) +{ + if(vflag > 1) + xprintf("rm -rf %s\n", workdir); + xremoveall(workdir); +} + +// Remove trailing spaces. +static void +chomp(Buf *b) +{ + int c; + + while(b->len > 0 && ((c=b->p[b->len-1]) == ' ' || c == '\t' || c == '\r' || c == '\n')) + b->len--; +} + + +// findgoversion determines the Go version to use in the version string. +static char* +findgoversion(void) +{ + char *tag, *rev, *p; + int i, nrev; + Buf b, path, bmore, branch; + Vec tags; + + binit(&b); + binit(&path); + binit(&bmore); + binit(&branch); + vinit(&tags); + + // The $GOROOT/VERSION file takes priority, for distributions + // without the Mercurial repo. + bpathf(&path, "%s/VERSION", goroot); + if(isfile(bstr(&path))) { + readfile(&b, bstr(&path)); + chomp(&b); + // Commands such as "dist version > VERSION" will cause + // the shell to create an empty VERSION file and set dist's + // stdout to its fd. dist in turn looks at VERSION and uses + // its content if available, which is empty at this point. + if(b.len > 0) + goto done; + } + + // The $GOROOT/VERSION.cache file is a cache to avoid invoking + // hg every time we run this command. Unlike VERSION, it gets + // deleted by the clean command. + bpathf(&path, "%s/VERSION.cache", goroot); + if(isfile(bstr(&path))) { + readfile(&b, bstr(&path)); + chomp(&b); + goto done; + } + + // Otherwise, use Mercurial. + // What is the current branch? + run(&branch, goroot, CheckExit, "hg", "identify", "-b", nil); + chomp(&branch); + + // What are the tags along the current branch? + tag = ""; + rev = "."; + run(&b, goroot, CheckExit, "hg", "log", "-b", bstr(&branch), "-r", ".:0", "--template", "{tags} + ", nil); + splitfields(&tags, bstr(&b)); + nrev = 0; + for(i=0; i 0) + bwriteb(&b, &bmore); + + // Cache version. + writefile(&b, bstr(&path), 0); + +done: + p = btake(&b); + + + bfree(&b); + bfree(&path); + bfree(&bmore); + bfree(&branch); + vfree(&tags); + + return p; +} + +/* + * Initial tree setup. + */ + +// The old tools that no longer live in $GOBIN or $GOROOT/bin. +static char *oldtool[] = { + "5a", "5c", "5g", "5l", + "6a", "6c", "6g", "6l", + "8a", "8c", "8g", "8l", + "6cov", + "6nm", + "6prof", + "cgo", + "ebnflint", + "goapi", + "gofix", + "goinstall", + "gomake", + "gopack", + "gopprof", + "gotest", + "gotype", + "govet", + "goyacc", + "quietgcc", +}; + +// Unreleased directories (relative to $GOROOT) that should +// not be in release branches. +static char *unreleased[] = { + "src/cmd/cov", + "src/cmd/prof", + "src/pkg/old", + "src/pkg/exp", +}; + +// setup sets up the tree for the initial build. +static void +setup(void) +{ + int i; + Buf b; + char *p; + + binit(&b); + + // Create bin directory. + p = bpathf(&b, "%s/bin", goroot); + if(!isdir(p)) + xmkdir(p); + + // Create package directory. + p = bpathf(&b, "%s/pkg", goroot); + if(!isdir(p)) + xmkdir(p); + p = bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch); + if(rebuildall) + xremoveall(p); + xmkdirall(p); + if(!streq(goos, gohostos) || !streq(goarch, gohostarch)) { + p = bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch); + if(rebuildall) + xremoveall(p); + xmkdirall(p); + } + + // Create object directory. + // We keep it in pkg/ so that all the generated binaries + // are in one tree. If pkg/obj/libgc.a exists, it is a dreg from + // before we used subdirectories of obj. Delete all of obj + // to clean up. + bpathf(&b, "%s/pkg/obj/libgc.a", goroot); + if(isfile(bstr(&b))) + xremoveall(bpathf(&b, "%s/pkg/obj", goroot)); + p = bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch); + if(rebuildall) + xremoveall(p); + xmkdirall(p); + + // Create tool directory. + // We keep it in pkg/, just like the object directory above. + if(rebuildall) + xremoveall(tooldir); + xmkdirall(tooldir); + + // Remove tool binaries from before the tool/gohostos_gohostarch + xremoveall(bpathf(&b, "%s/bin/tool", goroot)); + + // Remove old pre-tool binaries. + for(i=0; i 1) + xprintf("skipping %s - does not exist\n", dir); + goto out; + } + + // set up gcc command line on first run. + if(gccargs.len == 0) { + xgetenv(&b, "CC"); + if(b.len == 0) + bprintf(&b, "gcc"); + splitfields(&gccargs, bstr(&b)); + for(i=0; i ttarg) + stale = 1; + if(t == 0) { + vadd(&missing, p); + files.p[n++] = files.p[i]; + continue; + } + files.p[n++] = files.p[i]; + } + files.len = n; + + for(i=0; i ttarg) + stale = 1; + + if(!stale) + goto out; + + // For package runtime, copy some files into the work space. + if(streq(dir, "pkg/runtime")) { + copy(bpathf(&b, "%s/arch_GOARCH.h", workdir), + bpathf(&b1, "%s/arch_%s.h", bstr(&path), goarch), 0); + copy(bpathf(&b, "%s/defs_GOOS_GOARCH.h", workdir), + bpathf(&b1, "%s/defs_%s_%s.h", bstr(&path), goos, goarch), 0); + copy(bpathf(&b, "%s/os_GOOS.h", workdir), + bpathf(&b1, "%s/os_%s.h", bstr(&path), goos), 0); + copy(bpathf(&b, "%s/signals_GOOS.h", workdir), + bpathf(&b1, "%s/signals_%s.h", bstr(&path), goos), 0); + } + + // Generate any missing files; regenerate existing ones. + for(i=0; i 1) + xprintf("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 + // build the package with the go tool, + // and it assumes these generated files already + // exist (it does not know how to build them). + // The 'clean' command can remove + // the generated files. + goto built; + } + } + // Did not rebuild p. + if(find(p, missing.p, missing.len) >= 0) + fatal("missing file %s", p); + built:; + } + + // One more copy for package runtime. + // The last batch was required for the generators. + // This one is generated. + if(streq(dir, "pkg/runtime")) { + copy(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), + bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch), 0); + } + + // Generate .c files from .goc files. + if(streq(dir, "pkg/runtime")) { + for(i=0; i 1) + xprintf("skip build for cross-compile %s\n", dir); + goto nobuild; + } + + // Compile the files. + for(i=0; i 1) + xprintf("cp %s %s\n", src, dst); + + binit(&b); + readfile(&b, src); + writefile(&b, dst, exec); + bfree(&b); +} + +// buildorder records the order of builds for the 'go bootstrap' command. +static char *buildorder[] = { + "lib9", + "libbio", + "libmach", + + "misc/pprof", + + "cmd/addr2line", + "cmd/cov", + "cmd/nm", + "cmd/objdump", + "cmd/pack", + "cmd/prof", + + "cmd/cc", // must be before c + "cmd/gc", // must be before g + "cmd/%sl", // must be before a, c, g + "cmd/%sa", + "cmd/%sc", + "cmd/%sg", + + // The dependency order here was copied from a buildscript + // back when there were build scripts. Will have to + // be maintained by hand, but shouldn't change very + // often. + "pkg/runtime", + "pkg/errors", + "pkg/sync/atomic", + "pkg/sync", + "pkg/io", + "pkg/unicode", + "pkg/unicode/utf8", + "pkg/unicode/utf16", + "pkg/bytes", + "pkg/math", + "pkg/strings", + "pkg/strconv", + "pkg/bufio", + "pkg/sort", + "pkg/container/heap", + "pkg/encoding/base64", + "pkg/syscall", + "pkg/time", + "pkg/os", + "pkg/reflect", + "pkg/fmt", + "pkg/encoding/json", + "pkg/flag", + "pkg/path/filepath", + "pkg/path", + "pkg/io/ioutil", + "pkg/log", + "pkg/regexp/syntax", + "pkg/regexp", + "pkg/go/token", + "pkg/go/scanner", + "pkg/go/ast", + "pkg/go/parser", + "pkg/os/exec", + "pkg/net/url", + "pkg/text/template/parse", + "pkg/text/template", + "pkg/go/doc", + "pkg/go/build", + "cmd/go", +}; + +// cleantab records the directories to clean in 'go clean'. +// It is bigger than the buildorder because we clean all the +// compilers but build only the $GOARCH ones. +static char *cleantab[] = { + "cmd/5a", + "cmd/5c", + "cmd/5g", + "cmd/5l", + "cmd/6a", + "cmd/6c", + "cmd/6g", + "cmd/6l", + "cmd/8a", + "cmd/8c", + "cmd/8g", + "cmd/8l", + "cmd/addr2line", + "cmd/cc", + "cmd/cov", + "cmd/gc", + "cmd/go", + "cmd/nm", + "cmd/objdump", + "cmd/pack", + "cmd/prof", + "lib9", + "libbio", + "libmach", + "pkg/bufio", + "pkg/bytes", + "pkg/container/heap", + "pkg/encoding/base64", + "pkg/encoding/json", + "pkg/errors", + "pkg/flag", + "pkg/fmt", + "pkg/go/ast", + "pkg/go/build", + "pkg/go/doc", + "pkg/go/parser", + "pkg/go/scanner", + "pkg/go/token", + "pkg/io", + "pkg/io/ioutil", + "pkg/log", + "pkg/math", + "pkg/net/url", + "pkg/os", + "pkg/os/exec", + "pkg/path", + "pkg/path/filepath", + "pkg/reflect", + "pkg/regexp", + "pkg/regexp/syntax", + "pkg/runtime", + "pkg/sort", + "pkg/strconv", + "pkg/strings", + "pkg/sync", + "pkg/sync/atomic", + "pkg/syscall", + "pkg/text/template", + "pkg/text/template/parse", + "pkg/time", + "pkg/unicode", + "pkg/unicode/utf16", + "pkg/unicode/utf8", +}; + +static void +clean(void) +{ + int i, j, k; + Buf b, path; + Vec dir; + + binit(&b); + binit(&path); + vinit(&dir); + + for(i=0; i 0) + usage(); + + xprintf(format, "GOROOT", goroot); + xprintf(format, "GOBIN", gobin); + xprintf(format, "GOARCH", goarch); + xprintf(format, "GOOS", goos); + xprintf(format, "GOHOSTARCH", gohostarch); + xprintf(format, "GOHOSTOS", gohostos); + xprintf(format, "GOTOOLDIR", tooldir); + xprintf(format, "GOCHAR", gochar); + + if(pflag) { + sep = ":"; + if(streq(gohostos, "windows")) + sep = ";"; + xgetenv(&b, "PATH"); + bprintf(&b1, "%s%s%s", gobin, sep, bstr(&b)); + xprintf(format, "PATH", bstr(&b1)); + } + + bfree(&b); + bfree(&b1); +} + +// The bootstrap command runs a build from scratch, +// stopping at having installed the go_bootstrap command. +void +cmdbootstrap(int argc, char **argv) +{ + int i; + Buf b; + char *oldgoos, *oldgoarch, *oldgochar; + + binit(&b); + + ARGBEGIN{ + case 'a': + rebuildall = 1; + break; + case 'v': + vflag++; + break; + default: + usage(); + }ARGEND + + if(argc > 0) + usage(); + + if(rebuildall) + clean(); + goversion = findgoversion(); + setup(); + + xsetenv("GOROOT", goroot); + xsetenv("GOROOT_FINAL", goroot_final); + + // For the main bootstrap, building for host os/arch. + oldgoos = goos; + oldgoarch = goarch; + oldgochar = gochar; + goos = gohostos; + goarch = gohostarch; + gochar = gohostchar; + xsetenv("GOARCH", goarch); + xsetenv("GOOS", goos); + + for(i=0; i 0) + usage(); + + clean(); +} + +// Banner prints the 'now you've installed Go' banner. +void +cmdbanner(int argc, char **argv) +{ + char *pathsep; + Buf b, b1, search; + + ARGBEGIN{ + case 'v': + vflag++; + break; + default: + usage(); + }ARGEND + + if(argc > 0) + usage(); + + binit(&b); + binit(&b1); + binit(&search); + + xprintf("\n"); + xprintf("---\n"); + xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot); + xprintf("Installed commands in %s\n", gobin); + + // Check that gobin appears in $PATH. + xgetenv(&b, "PATH"); + pathsep = ":"; + if(streq(gohostos, "windows")) + pathsep = ";"; + bprintf(&b1, "%s%s%s", pathsep, bstr(&b), pathsep); + bprintf(&search, "%s%s%s", pathsep, gobin, pathsep); + if(xstrstr(bstr(&b1), bstr(&search)) == nil) + xprintf("*** You need to add %s to your PATH.\n", gobin); + + if(streq(gohostos, "darwin")) { + xprintf("\n" + "On OS X the debuggers must be installed setgid procmod.\n" + "Read and run ./sudo.bash to install the debuggers.\n"); + } + + if(!streq(goroot_final, goroot)) { + xprintf("\n" + "The binaries expect %s to be copied or moved to %s\n", + goroot, goroot_final); + } + + bfree(&b); + bfree(&b1); + bfree(&search); +} + +// Version prints the Go version. +void +cmdversion(int argc, char **argv) +{ + ARGBEGIN{ + case 'v': + vflag++; + break; + default: + usage(); + }ARGEND + + if(argc > 0) + usage(); + + xprintf("%s\n", goversion); +} diff --git a/src/cmd/dist/buildgc.c b/src/cmd/dist/buildgc.c new file mode 100644 index 000000000..da38760c6 --- /dev/null +++ b/src/cmd/dist/buildgc.c @@ -0,0 +1,106 @@ +// 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" +#include + +/* + * Helpers for building cmd/gc. + */ + +// gcopnames creates opnames.h from go.h. +// It finds the OXXX enum, pulls out all the constants +// from OXXX to OEND, and writes a table mapping +// op to string. +void +gcopnames(char *dir, char *file) +{ + char *p, *q; + int i, j, end; + Buf in, b, out; + Vec lines, fields; + + binit(&in); + binit(&b); + binit(&out); + vinit(&lines); + vinit(&fields); + + bwritestr(&out, bprintf(&b, "// auto generated by go tool dist\n")); + bwritestr(&out, bprintf(&b, "static char *opnames[] = {\n")); + + readfile(&in, bprintf(&b, "%s/go.h", dir)); + splitlines(&lines, bstr(&in)); + i = 0; + while(i + +/* + * Helpers for building pkg/runtime. + */ + +// mkzversion writes zversion.go: +// +// package runtime +// const defaultGoroot = +// const theVersion = +// +void +mkzversion(char *dir, char *file) +{ + Buf b, out; + + binit(&b); + binit(&out); + + bwritestr(&out, bprintf(&b, + "// auto generated by go tool dist\n" + "\n" + "package runtime\n" + "\n" + "const defaultGoroot = `%s`\n" + "const theVersion = `%s`\n", goroot_final, goversion)); + + writefile(&out, file, 0); + + bfree(&b); + bfree(&out); +} + +// mkzgoarch writes zgoarch_$GOARCH.go: +// +// package runtime +// const theGoarch = +// +void +mkzgoarch(char *dir, char *file) +{ + Buf b, out; + + binit(&b); + binit(&out); + + bwritestr(&out, bprintf(&b, + "// auto generated by go tool dist\n" + "\n" + "package runtime\n" + "\n" + "const theGoarch = `%s`\n", goarch)); + + writefile(&out, file, 0); + + bfree(&b); + bfree(&out); +} + +// mkzgoos writes zgoos_$GOOS.go: +// +// package runtime +// const theGoos = +// +void +mkzgoos(char *dir, char *file) +{ + Buf b, out; + + binit(&b); + binit(&out); + + bwritestr(&out, bprintf(&b, + "// auto generated by go tool dist\n" + "\n" + "package runtime\n" + "\n" + "const theGoos = `%s`\n", goos)); + + writefile(&out, file, 0); + + bfree(&b); + bfree(&out); +} + +static struct { + char *goarch; + char *goos; + char *hdr; +} zasmhdr[] = { + {"386", "windows", + "#define get_tls(r) MOVL 0x14(FS), r\n" + "#define g(r) 0(r)\n" + "#define m(r) 4(r)\n" + }, + {"386", "plan9", + "#define get_tls(r) MOVL _tos(SB), r \n" + "#define g(r) -8(r)\n" + "#define m(r) -4(r)\n" + }, + {"386", "linux", + "// On Linux systems, what we call 0(GS) and 4(GS) for g and m\n" + "// turn into %gs:-8 and %gs:-4 (using gcc syntax to denote\n" + "// what the machine sees as opposed to 8l input).\n" + "// 8l rewrites 0(GS) and 4(GS) into these.\n" + "//\n" + "// On Linux Xen, it is not allowed to use %gs:-8 and %gs:-4\n" + "// directly. Instead, we have to store %gs:0 into a temporary\n" + "// register and then use -8(%reg) and -4(%reg). This kind\n" + "// of addressing is correct even when not running Xen.\n" + "//\n" + "// 8l can rewrite MOVL 0(GS), CX into the appropriate pair\n" + "// of mov instructions, using CX as the intermediate register\n" + "// (safe because CX is about to be written to anyway).\n" + "// But 8l cannot handle other instructions, like storing into 0(GS),\n" + "// which is where these macros come into play.\n" + "// get_tls sets up the temporary and then g and r use it.\n" + "//\n" + "// The final wrinkle is that get_tls needs to read from %gs:0,\n" + "// but in 8l input it's called 8(GS), because 8l is going to\n" + "// subtract 8 from all the offsets, as described above.\n" + "#define get_tls(r) MOVL 8(GS), r\n" + "#define g(r) -8(r)\n" + "#define m(r) -4(r)\n" + }, + {"386", "", + "#define get_tls(r)\n" + "#define g(r) 0(GS)\n" + "#define m(r) 4(GS)\n" + }, + + {"amd64", "windows", + "#define get_tls(r) MOVQ 0x28(GS), r\n" + "#define g(r) 0(r)\n" + "#define m(r) 8(r)\n" + }, + {"amd64", "", + "// The offsets 0 and 8 are known to:\n" + "// ../../cmd/6l/pass.c:/D_GS\n" + "// cgo/gcc_linux_amd64.c:/^threadentry\n" + "// cgo/gcc_darwin_amd64.c:/^threadentry\n" + "//\n" + "#define get_tls(r)\n" + "#define g(r) 0(GS)\n" + "#define m(r) 8(GS)\n" + }, + + {"arm", "", + "#define g R10\n" + "#define m R9\n" + "#define LR R14\n" + }, +}; + +// mkzasm writes zasm_$GOOS_$GOARCH.h, +// which contains struct offsets for use by +// assembly files. It also writes a copy to the work space +// under the name zasm_GOOS_GOARCH.h (no expansion). +// +void +mkzasm(char *dir, char *file) +{ + int i, n; + char *aggr, *p; + Buf in, b, out; + Vec argv, lines, fields; + + binit(&in); + binit(&b); + binit(&out); + vinit(&argv); + vinit(&lines); + vinit(&fields); + + bwritestr(&out, "// auto generated by go tool dist\n\n"); + for(i=0; i= 2) { + n = fields.len; + p = fields.p[n-1]; + if(p[xstrlen(p)-1] == ';') + p[xstrlen(p)-1] = '\0'; + bwritestr(&out, bprintf(&b, "#define %s_%s %s\n", aggr, fields.p[n-1], fields.p[n-2])); + } + } + + // Write both to file and to workdir/zasm_GOOS_GOARCH.h. + writefile(&out, file, 0); + writefile(&out, bprintf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), 0); + + bfree(&in); + bfree(&b); + bfree(&out); + vfree(&argv); + vfree(&lines); + vfree(&fields); +} + +static char *runtimedefs[] = { + "proc.c", + "iface.c", + "hashmap.c", + "chan.c", +}; + +// mkzruntimedefs writes zruntime_defs_$GOOS_$GOARCH.h, +// which contains Go struct definitions equivalent to the C ones. +// Mostly we just write the output of 6c -q to the file. +// However, we run it on multiple files, so we have to delete +// the duplicated definitions, and we don't care about the funcs +// and consts, so we delete those too. +// +void +mkzruntimedefs(char *dir, char *file) +{ + int i, skip; + char *p; + Buf in, b, out; + Vec argv, lines, fields, seen; + + binit(&in); + binit(&b); + binit(&out); + vinit(&argv); + vinit(&lines); + vinit(&fields); + vinit(&seen); + + bwritestr(&out, "// auto generated by go tool dist\n" + "\n" + "package runtime\n" + "import \"unsafe\"\n" + "var _ unsafe.Pointer\n" + "\n" + ); + + + // Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -q + // 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, "-q"); + vadd(&argv, ""); + p = argv.p[argv.len-1]; + for(i=0; i= 0) { + if(streq(fields.p[fields.len-1], "{")) + skip = 1; // skip until } + continue; + } + vadd(&seen, fields.p[1]); + } + if(skip) { + if(hasprefix(p, "}")) + skip = 0; + continue; + } + + bwritestr(&out, p); + } + + writefile(&out, file, 0); + + bfree(&in); + bfree(&b); + bfree(&out); + vfree(&argv); + vfree(&lines); + vfree(&fields); + vfree(&seen); +} diff --git a/src/cmd/dist/goc2c.c b/src/cmd/dist/goc2c.c new file mode 100644 index 000000000..22f72f8b5 --- /dev/null +++ b/src/cmd/dist/goc2c.c @@ -0,0 +1,735 @@ +// 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 "a.h" + +/* + * Translate a .goc file into a .c file. A .goc file is a combination + * of a limited form of Go with C. + */ + +/* + package PACKAGENAME + {# line} + func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{ + C code with proper brace nesting + \} +*/ + +/* + * We generate C code which implements the function such that it can + * be called from Go and executes the C code. + */ + +static char *input; +static Buf *output; +#define EOF -1 + +static int +xgetchar(void) +{ + int c; + + c = *input; + if(c == 0) + return EOF; + input++; + return c; +} + +static void +xungetc(void) +{ + input--; +} + +static void +xputchar(char c) +{ + bwrite(output, &c, 1); +} + +static int +xisspace(int c) +{ + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +} + +/* Whether we're emitting for gcc */ +static int gcc; + +/* File and line number */ +static const char *file; +static unsigned int lineno = 1; + +/* List of names and types. */ +struct params { + struct params *next; + char *name; + char *type; +}; + +/* index into type_table */ +enum { + Bool, + Float, + Int, + Uint, + Uintptr, + String, + Slice, + Eface, +}; + +static struct { + char *name; + int size; +} type_table[] = { + /* variable sized first, for easy replacement */ + /* order matches enum above */ + /* default is 32-bit architecture sizes */ + {"bool", 1}, + {"float", 4}, + {"int", 4}, + {"uint", 4}, + {"uintptr", 4}, + {"String", 8}, + {"Slice", 12}, + {"Eface", 8}, + + /* fixed size */ + {"float32", 4}, + {"float64", 8}, + {"byte", 1}, + {"int8", 1}, + {"uint8", 1}, + {"int16", 2}, + {"uint16", 2}, + {"int32", 4}, + {"uint32", 4}, + {"int64", 8}, + {"uint64", 8}, + + {nil}, +}; + +/* Fixed structure alignment (non-gcc only) */ +int structround = 4; + +/* Unexpected EOF. */ +static void +bad_eof(void) +{ + fatal("%s:%ud: unexpected EOF\n", file, lineno); +} + +/* Free a list of parameters. */ +static void +free_params(struct params *p) +{ + while (p != nil) { + struct params *next; + + next = p->next; + xfree(p->name); + xfree(p->type); + xfree(p); + p = next; + } +} + +/* Read a character, tracking lineno. */ +static int +getchar_update_lineno(void) +{ + int c; + + c = xgetchar(); + if (c == '\n') + ++lineno; + return c; +} + +/* Read a character, giving an error on EOF, tracking lineno. */ +static int +getchar_no_eof(void) +{ + int c; + + c = getchar_update_lineno(); + if (c == EOF) + bad_eof(); + return c; +} + +/* Read a character, skipping comments. */ +static int +getchar_skipping_comments(void) +{ + int c; + + while (1) { + c = getchar_update_lineno(); + if (c != '/') + return c; + + c = xgetchar(); + if (c == '/') { + do { + c = getchar_update_lineno(); + } while (c != EOF && c != '\n'); + return c; + } else if (c == '*') { + while (1) { + c = getchar_update_lineno(); + if (c == EOF) + return EOF; + if (c == '*') { + do { + c = getchar_update_lineno(); + } while (c == '*'); + if (c == '/') + break; + } + } + } else { + xungetc(); + return '/'; + } + } +} + +/* + * Read and return a token. Tokens are string or character literals + * or else delimited by whitespace or by [(),{}]. + * The latter are all returned as single characters. + */ +static char * +read_token(void) +{ + int c, q; + char *buf; + unsigned int alc, off; + char* delims = "(),{}"; + + while (1) { + c = getchar_skipping_comments(); + if (c == EOF) + return nil; + if (!xisspace(c)) + break; + } + alc = 16; + buf = xmalloc(alc + 1); + off = 0; + if(c == '"' || c == '\'') { + q = c; + buf[off] = c; + ++off; + while (1) { + if (off+2 >= alc) { // room for c and maybe next char + alc *= 2; + buf = xrealloc(buf, alc + 1); + } + c = getchar_no_eof(); + buf[off] = c; + ++off; + if(c == q) + break; + if(c == '\\') { + buf[off] = getchar_no_eof(); + ++off; + } + } + } else if (xstrrchr(delims, c) != nil) { + buf[off] = c; + ++off; + } else { + while (1) { + if (off >= alc) { + alc *= 2; + buf = xrealloc(buf, alc + 1); + } + buf[off] = c; + ++off; + c = getchar_skipping_comments(); + if (c == EOF) + break; + if (xisspace(c) || xstrrchr(delims, c) != nil) { + if (c == '\n') + lineno--; + xungetc(); + break; + } + } + } + buf[off] = '\0'; + return buf; +} + +/* Read a token, giving an error on EOF. */ +static char * +read_token_no_eof(void) +{ + char *token = read_token(); + if (token == nil) + bad_eof(); + return token; +} + +/* Read the package clause, and return the package name. */ +static char * +read_package(void) +{ + char *token; + + token = read_token_no_eof(); + if (token == nil) + fatal("%s:%ud: no token\n", file, lineno); + if (!streq(token, "package")) { + fatal("%s:%ud: expected \"package\", got \"%s\"\n", + file, lineno, token); + } + return read_token_no_eof(); +} + +/* Read and copy preprocessor lines. */ +static void +read_preprocessor_lines(void) +{ + while (1) { + int c; + + do { + c = getchar_skipping_comments(); + } while (xisspace(c)); + if (c != '#') { + xungetc(); + break; + } + xputchar(c); + do { + c = getchar_update_lineno(); + xputchar(c); + } while (c != '\n'); + } +} + +/* + * Read a type in Go syntax and return a type in C syntax. We only + * permit basic types and pointers. + */ +static char * +read_type(void) +{ + char *p, *op, *q; + int pointer_count; + unsigned int len; + + p = read_token_no_eof(); + if (*p != '*') + return p; + op = p; + pointer_count = 0; + while (*p == '*') { + ++pointer_count; + ++p; + } + len = xstrlen(p); + q = xmalloc(len + pointer_count + 1); + xmemmove(q, p, len); + while (pointer_count > 0) { + q[len] = '*'; + ++len; + --pointer_count; + } + q[len] = '\0'; + xfree(op); + return q; +} + +/* Return the size of the given type. */ +static int +type_size(char *p) +{ + int i; + + if(p[xstrlen(p)-1] == '*') + return type_table[Uintptr].size; + + for(i=0; type_table[i].name; i++) + if(streq(type_table[i].name, p)) + return type_table[i].size; + fatal("%s:%ud: unknown type %s\n", file, lineno, p); + return 0; +} + +/* + * Read a list of parameters. Each parameter is a name and a type. + * The list ends with a ')'. We have already read the '('. + */ +static struct params * +read_params(int *poffset) +{ + char *token; + struct params *ret, **pp, *p; + int offset, size, rnd; + + ret = nil; + pp = &ret; + token = read_token_no_eof(); + offset = 0; + if (!streq(token, ")")) { + while (1) { + p = xmalloc(sizeof(struct params)); + p->name = token; + p->type = read_type(); + p->next = nil; + *pp = p; + pp = &p->next; + + size = type_size(p->type); + rnd = size; + if(rnd > structround) + rnd = structround; + if(offset%rnd) + offset += rnd - offset%rnd; + offset += size; + + token = read_token_no_eof(); + if (!streq(token, ",")) + break; + token = read_token_no_eof(); + } + } + if (!streq(token, ")")) { + fatal("%s:%ud: expected '('\n", + file, lineno); + } + if (poffset != nil) + *poffset = offset; + return ret; +} + +/* + * Read a function header. This reads up to and including the initial + * '{' character. Returns 1 if it read a header, 0 at EOF. + */ +static int +read_func_header(char **name, struct params **params, int *paramwid, struct params **rets) +{ + int lastline; + char *token; + + lastline = -1; + while (1) { + token = read_token(); + if (token == nil) + return 0; + if (streq(token, "func")) { + if(lastline != -1) + bwritef(output, "\n"); + break; + } + if (lastline != lineno) { + if (lastline == lineno-1) + bwritef(output, "\n"); + else + bwritef(output, "\n#line %d \"%s\"\n", lineno, file); + lastline = lineno; + } + bwritef(output, "%s ", token); + } + + *name = read_token_no_eof(); + + token = read_token(); + if (token == nil || !streq(token, "(")) { + fatal("%s:%ud: expected \"(\"\n", + file, lineno); + } + *params = read_params(paramwid); + + token = read_token(); + if (token == nil || !streq(token, "(")) + *rets = nil; + else { + *rets = read_params(nil); + token = read_token(); + } + if (token == nil || !streq(token, "{")) { + fatal("%s:%ud: expected \"{\"\n", + file, lineno); + } + return 1; +} + +/* Write out parameters. */ +static void +write_params(struct params *params, int *first) +{ + struct params *p; + + for (p = params; p != nil; p = p->next) { + if (*first) + *first = 0; + else + bwritef(output, ", "); + bwritef(output, "%s %s", p->type, p->name); + } +} + +/* Write a 6g function header. */ +static void +write_6g_func_header(char *package, char *name, struct params *params, + int paramwid, struct params *rets) +{ + int first, n; + + bwritef(output, "void\n%s·%s(", package, name); + first = 1; + write_params(params, &first); + + /* insert padding to align output struct */ + if(rets != nil && paramwid%structround != 0) { + n = structround - paramwid%structround; + if(n & 1) + bwritef(output, ", uint8"); + if(n & 2) + bwritef(output, ", uint16"); + if(n & 4) + bwritef(output, ", uint32"); + } + + write_params(rets, &first); + bwritef(output, ")\n{\n"); +} + +/* Write a 6g function trailer. */ +static void +write_6g_func_trailer(struct params *rets) +{ + struct params *p; + + for (p = rets; p != nil; p = p->next) + bwritef(output, "\tFLUSH(&%s);\n", p->name); + bwritef(output, "}\n"); +} + +/* Define the gcc function return type if necessary. */ +static void +define_gcc_return_type(char *package, char *name, struct params *rets) +{ + struct params *p; + + if (rets == nil || rets->next == nil) + return; + bwritef(output, "struct %s_%s_ret {\n", package, name); + for (p = rets; p != nil; p = p->next) + bwritef(output, " %s %s;\n", p->type, p->name); + bwritef(output, "};\n"); +} + +/* Write out the gcc function return type. */ +static void +write_gcc_return_type(char *package, char *name, struct params *rets) +{ + if (rets == nil) + bwritef(output, "void"); + else if (rets->next == nil) + bwritef(output, "%s", rets->type); + else + bwritef(output, "struct %s_%s_ret", package, name); +} + +/* Write out a gcc function header. */ +static void +write_gcc_func_header(char *package, char *name, struct params *params, + struct params *rets) +{ + int first; + struct params *p; + + define_gcc_return_type(package, name, rets); + write_gcc_return_type(package, name, rets); + bwritef(output, " %s_%s(", package, name); + first = 1; + write_params(params, &first); + bwritef(output, ") asm (\"%s.%s\");\n", package, name); + write_gcc_return_type(package, name, rets); + bwritef(output, " %s_%s(", package, name); + first = 1; + write_params(params, &first); + bwritef(output, ")\n{\n"); + for (p = rets; p != nil; p = p->next) + bwritef(output, " %s %s;\n", p->type, p->name); +} + +/* Write out a gcc function trailer. */ +static void +write_gcc_func_trailer(char *package, char *name, struct params *rets) +{ + if (rets == nil) + ; + else if (rets->next == nil) + bwritef(output, "return %s;\n", rets->name); + else { + struct params *p; + + bwritef(output, " {\n struct %s_%s_ret __ret;\n", package, name); + for (p = rets; p != nil; p = p->next) + bwritef(output, " __ret.%s = %s;\n", p->name, p->name); + bwritef(output, " return __ret;\n }\n"); + } + bwritef(output, "}\n"); +} + +/* Write out a function header. */ +static void +write_func_header(char *package, char *name, + struct params *params, int paramwid, + struct params *rets) +{ + if (gcc) + write_gcc_func_header(package, name, params, rets); + else + write_6g_func_header(package, name, params, paramwid, rets); + bwritef(output, "#line %d \"%s\"\n", lineno, file); +} + +/* Write out a function trailer. */ +static void +write_func_trailer(char *package, char *name, + struct params *rets) +{ + if (gcc) + write_gcc_func_trailer(package, name, rets); + else + write_6g_func_trailer(rets); +} + +/* + * Read and write the body of the function, ending in an unnested } + * (which is read but not written). + */ +static void +copy_body(void) +{ + int nesting = 0; + while (1) { + int c; + + c = getchar_no_eof(); + if (c == '}' && nesting == 0) + return; + xputchar(c); + switch (c) { + default: + break; + case '{': + ++nesting; + break; + case '}': + --nesting; + break; + case '/': + c = getchar_update_lineno(); + xputchar(c); + if (c == '/') { + do { + c = getchar_no_eof(); + xputchar(c); + } while (c != '\n'); + } else if (c == '*') { + while (1) { + c = getchar_no_eof(); + xputchar(c); + if (c == '*') { + do { + c = getchar_no_eof(); + xputchar(c); + } while (c == '*'); + if (c == '/') + break; + } + } + } + break; + case '"': + case '\'': + { + int delim = c; + do { + c = getchar_no_eof(); + xputchar(c); + if (c == '\\') { + c = getchar_no_eof(); + xputchar(c); + c = '\0'; + } + } while (c != delim); + } + break; + } + } +} + +/* Process the entire file. */ +static void +process_file(void) +{ + char *package, *name; + struct params *params, *rets; + int paramwid; + + package = read_package(); + read_preprocessor_lines(); + while (read_func_header(&name, ¶ms, ¶mwid, &rets)) { + write_func_header(package, name, params, paramwid, rets); + copy_body(); + write_func_trailer(package, name, rets); + xfree(name); + free_params(params); + free_params(rets); + } + xfree(package); +} + +void +goc2c(char *goc, char *c) +{ + Buf in, out; + + binit(&in); + binit(&out); + + file = goc; + readfile(&in, goc); + + // TODO: set gcc=1 when using gcc + + 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; + structround = 8; + } else { + type_table[Uintptr].size = 4; + type_table[String].size = 8; + type_table[Slice].size = 16; + type_table[Eface].size = 4+4; + structround = 4; + } + } + + bprintf(&out, "// auto generated by go tool dist\n// goos=%s goarch=%s\n\n", goos, goarch); + input = bstr(&in); + output = &out; + + process_file(); + + writefile(&out, c, 0); +} diff --git a/src/cmd/dist/main.c b/src/cmd/dist/main.c new file mode 100644 index 000000000..72a7579d1 --- /dev/null +++ b/src/cmd/dist/main.c @@ -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. + +#include "a.h" + +int vflag; +char *argv0; + +// cmdtab records the available commands. +static struct { + char *name; + void (*f)(int, char**); +} cmdtab[] = { + {"banner", cmdbanner}, + {"bootstrap", cmdbootstrap}, + {"clean", cmdclean}, + {"env", cmdenv}, + {"install", cmdinstall}, + {"version", cmdversion}, +}; + +// The OS-specific main calls into the portable code here. +void +xmain(int argc, char **argv) +{ + int i; + + if(argc <= 1) + usage(); + + for(i=0; i +#include +#include +#include +#include +#include +#include +#include +#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. +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: %s", strerror(errno)); + 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 != NULL) + 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 cmd; + char *q; + + while(nbg >= maxnbg) + bgwait1(); + + // Generate a copy of the command to show in a log. + // Substitute $WORK for the work directory. + binit(&cmd); + 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) + xprintf("%s\n", bstr(&cmd)); + + if(b != nil) { + breset(b); + if(pipe(p) < 0) + fatal("pipe: %s", strerror(errno)); + } + + switch(pid = fork()) { + case -1: + fatal("fork: %s", strerror(errno)); + case 0: + if(b != nil) { + close(0); + close(p[0]); + dup2(p[1], 1); + dup2(p[1], 2); + if(p[1] > 2) + close(p[1]); + } + if(dir != nil) { + if(chdir(dir) < 0) { + fprintf(stderr, "chdir %s: %s\n", dir, strerror(errno)); + _exit(1); + } + } + vadd(argv, nil); + execvp(argv->p[0], argv->p); + fprintf(stderr, "%s\n", bstr(&cmd)); + fprintf(stderr, "exec %s: %s\n", argv->p[0], strerror(errno)); + _exit(1); + } + 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); +} + +// bgwait1 waits for a single background job. +static void +bgwait1(void) +{ + int i, pid, status, mode; + char *cmd; + Buf *b; + + errno = 0; + while((pid = wait(&status)) < 0) { + if(errno != EINTR) + fatal("waitpid: %s", strerror(errno)); + } + for(i=0; i 0) + bgwait1(); +} + +// xgetwd replaces b with the current directory. +void +xgetwd(Buf *b) +{ + char buf[MAXPATHLEN]; + + breset(b); + if(getcwd(buf, MAXPATHLEN) == nil) + fatal("getcwd: %s", strerror(errno)); + 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) +{ + int fd; + + fd = open(".", 0); + if(fd < 0) + fatal("open .: %s", strerror(errno)); + if(chdir(path) < 0) + fatal("chdir %s: %s", path, strerror(errno)); + xgetwd(b); + if(fchdir(fd) < 0) + fatal("fchdir: %s", strerror(errno)); + close(fd); +} + +// isdir reports whether p names an existing directory. +bool +isdir(char *p) +{ + struct stat st; + + return stat(p, &st) >= 0 && S_ISDIR(st.st_mode); +} + +// isfile reports whether p names an existing file. +bool +isfile(char *p) +{ + struct stat st; + + return stat(p, &st) >= 0 && S_ISREG(st.st_mode); +} + +// mtime returns the modification time of the file p. +Time +mtime(char *p) +{ + struct stat st; + + if(stat(p, &st) < 0) + return 0; + return (Time)st.st_mtime*1000000000LL; +} + +// 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, 0); + if(fd < 0) + fatal("open %s: %s", file, strerror(errno)); + breadfrom(b, fd); + close(fd); +} + +// writefile writes b to the named file, creating it if needed. if +// exec is non-zero, marks the file as executable. +void +writefile(Buf *b, char *file, int exec) +{ + int fd; + + fd = creat(file, 0666); + if(fd < 0) + fatal("create %s: %s", file, strerror(errno)); + if(write(fd, b->p, b->len) != b->len) + fatal("short write: %s", strerror(errno)); + if(exec) + fchmod(fd, 0755); + close(fd); +} + +// xmkdir creates the directory p. +void +xmkdir(char *p) +{ + if(mkdir(p, 0777) < 0) + fatal("mkdir %s: %s", p, strerror(errno)); +} + +// 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) + xprintf("rm %s\n", p); + unlink(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) + xprintf("rm %s\n", p); + rmdir(p); + } else { + if(vflag > 2) + xprintf("rm %s\n", p); + unlink(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; + struct dirent *dp; + + vreset(dst); + d = opendir(dir); + if(d == nil) + fatal("opendir %s: %s", dir, strerror(errno)); + while((dp = readdir(d)) != nil) { + if(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) + continue; + vadd(dst, dp->d_name); + } + closedir(d); +} + +// xworkdir creates a new temporary directory to hold object files +// and returns the name of that directory. +char* +xworkdir(void) +{ + Buf b; + char *p; + + binit(&b); + + xgetenv(&b, "TMPDIR"); + if(b.len == 0) + bwritestr(&b, "/var/tmp"); + bwritestr(&b, "/go-cbuild-XXXXXX"); + if(mkdtemp(bstr(&b)) == nil) + fatal("mkdtemp: %s", strerror(errno)); + p = btake(&b); + + bfree(&b); + + return p; +} + +// fatal prints an error message to standard error and exits. +void +fatal(char *msg, ...) +{ + va_list arg; + + fflush(stdout); + fprintf(stderr, "go tool dist: "); + va_start(arg, msg); + vfprintf(stderr, msg, arg); + va_end(arg); + fprintf(stderr, "\n"); + + bgwait(); + exit(1); +} + +// 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) +{ + exit(n); +} + +// 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); +} + +// xsetenv sets the environment variable $name to the given value. +void +xsetenv(char *name, char *value) +{ + setenv(name, value, 1); +} + +// main takes care of OS-specific startup and dispatches to xmain. +int +main(int argc, char **argv) +{ + Buf b; + struct utsname u; + + setvbuf(stdout, nil, _IOLBF, 0); + setvbuf(stderr, nil, _IOLBF, 0); + + binit(&b); + + slash = "/"; + +#if defined(__APPLE__) + gohostos = "darwin"; + // Even on 64-bit platform, darwin uname -m prints i386. + run(&b, nil, 0, "sysctl", "machdep.cpu.extfeatures", nil); + if(contains(bstr(&b), "EM64T")) + gohostarch = "amd64"; +#elif defined(__linux__) + gohostos = "linux"; +#elif defined(__FreeBSD__) + gohostos = "freebsd"; +#elif defined(__OpenBSD__) + gohostos = "openbsd"; +#elif defined(__NetBSD__) + gohostos = "netbsd"; +#else + fatal("unknown operating system"); +#endif + + if(gohostarch == nil) { + if(uname(&u) < 0) + fatal("uname: %s", strerror(errno)); + if(contains(u.machine, "x86_64") || contains(u.machine, "amd64")) + gohostarch = "amd64"; + else if(hassuffix(u.machine, "86")) + gohostarch = "386"; + else if(contains(u.machine, "arm")) + gohostarch = "arm"; + else + fatal("unknown architecture: %s", u.machine); + } + + if(strcmp(gohostarch, "arm") == 0) + maxnbg = 1; + + init(); + xmain(argc, argv); + bfree(&b); + return 0; +} + +// 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); +} + +#endif // PLAN9 +#endif // __WINDOWS__ diff --git a/src/cmd/dist/windows.c b/src/cmd/dist/windows.c new file mode 100644 index 000000000..557e4b003 --- /dev/null +++ b/src/cmd/dist/windows.c @@ -0,0 +1,910 @@ +// 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 WIN32 + +// Portability layer implemented for Windows. +// See unix.c for doc comments about exported functions. + +#include "a.h" +#include +#include +#include +#include + +/* + * Windows uses 16-bit rune strings in the APIs. + * Define conversions between Rune* and UTF-8 char*. + */ + +typedef unsigned char uchar; +typedef unsigned short Rune; // same as Windows + +// encoderune encodes the rune r into buf and returns +// the number of bytes used. +static int +encoderune(char *buf, Rune r) +{ + if(r < 0x80) { // 7 bits + buf[0] = r; + return 1; + } + if(r < 0x800) { // 5+6 bits + buf[0] = 0xc0 | (r>>6); + buf[1] = 0x80 | (r&0x3f); + return 2; + } + buf[0] = 0xe0 | (r>>12); + buf[1] = 0x80 | ((r>>6)&0x3f); + buf[2] = 0x80 | (r&0x3f); + return 3; +} + +// decoderune decodes the rune encoding at sbuf into r +// and returns the number of bytes used. +static int +decoderune(Rune *r, char *sbuf) +{ + uchar *buf; + + buf = (uchar*)sbuf; + if(buf[0] < 0x80) { + *r = buf[0]; + return 1; + } + if((buf[0]&0xe0) == 0xc0 && (buf[1]&0xc0) == 0x80) { + *r = (buf[0]&~0xc0)<<6 | (buf[1]&~0x80); + if(*r < 0x80) + goto err; + return 2; + } + if((buf[0]&0xf0) == 0xe0 && (buf[1]&0xc0) == 0x80 && (buf[2]&0xc0) == 0x80) { + *r = (buf[0]&~0xc0)<<12 | (buf[1]&~0x80)<<6 | (buf[2]&~0x80); + if(*r < 0x800) + goto err; + return 3; + } +err: + *r = 0xfffd; + return 1; +} + +// toutf replaces b with the UTF-8 encoding of the rune string r. +static void +toutf(Buf *b, Rune *r) +{ + int i, n; + char buf[4]; + + breset(b); + for(i=0; r[i]; i++) { + n = encoderune(buf, r[i]); + bwrite(b, buf, n); + } +} + +// torune replaces *rp with a pointer to a newly allocated +// rune string equivalent of the UTF-8 string p. +static void +torune(Rune **rp, char *p) +{ + Rune *r, *w; + + r = xmalloc((strlen(p)+1) * sizeof r[0]); + w = r; + while(*p) + p += decoderune(w++, p); + *w = 0; + *rp = r; +} + +// errstr returns the most recent Windows error, in string form. +static char* +errstr(void) +{ + DWORD code; + Rune *r; + Buf b; + + binit(&b); + code = GetLastError(); + r = nil; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + nil, code, 0, (Rune*)&r, 0, nil); + toutf(&b, r); + return bstr(&b); // leak but we're dying anyway +} + +void +xgetenv(Buf *b, char *name) +{ + Rune *buf; + int n; + Rune *r; + + breset(b); + torune(&r, name); + n = GetEnvironmentVariableW(r, NULL, 0); + if(n > 0) { + buf = xmalloc((n+1)*sizeof buf[0]); + GetEnvironmentVariableW(r, buf, n+1); + buf[n] = '\0'; + toutf(b, buf); + xfree(buf); + } + xfree(r); +} + +void +xsetenv(char *name, char *value) +{ + Rune *rname, *rvalue; + + torune(&rname, name); + torune(&rvalue, value); + SetEnvironmentVariableW(rname, rvalue); + xfree(rname); + xfree(rvalue); +} + +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); +} + +void +bwritef(Buf *b, char *fmt, ...) +{ + va_list arg; + char buf[4096]; + + // no reset + va_start(arg, fmt); + vsnprintf(buf, sizeof buf, fmt, arg); + va_end(arg); + bwritestr(b, buf); +} + +// bpathf is like bprintf but replaces / with \ in the result, +// to make it a canonical windows file path. +char* +bpathf(Buf *b, char *fmt, ...) +{ + int i; + va_list arg; + char buf[4096]; + + breset(b); + va_start(arg, fmt); + vsnprintf(buf, sizeof buf, fmt, arg); + va_end(arg); + bwritestr(b, buf); + + for(i=0; ilen; i++) + if(b->p[i] == '/') + b->p[i] = '\\'; + + return bstr(b); +} + + +static void +breadfrom(Buf *b, HANDLE h) +{ + DWORD n; + + for(;;) { + if(b->len > 1<<22) + fatal("unlikely file size in readfrom"); + bgrow(b, 4096); + n = 0; + if(!ReadFile(h, b->p+b->len, 4096, &n, nil)) { + // Happens for pipe reads. + break; + } + if(n == 0) + break; + b->len += n; + } +} + +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); +} + +static void genrun(Buf*, char*, int, Vec*, int); + +void +runv(Buf *b, char *dir, int mode, Vec *argv) +{ + genrun(b, dir, mode, argv, 1); +} + +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 { + PROCESS_INFORMATION pi; + int mode; + char *cmd; +} bg[MAXBG]; + +static int nbg; + +static void bgwait1(void); + +static void +genrun(Buf *b, char *dir, int mode, Vec *argv, int wait) +{ + int i, j, nslash; + Buf cmd; + char *q; + Rune *rcmd, *rexe, *rdir; + STARTUPINFOW si; + PROCESS_INFORMATION pi; + HANDLE p[2]; + + while(nbg >= nelem(bg)) + bgwait1(); + + binit(&cmd); + + for(i=0; ilen; i++) { + 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; + for(; *q; q++) { + if(*q == '\\') { + nslash++; + continue; + } + if(*q == '"') { + for(j=0; j<2*nslash+1; j++) + bwritestr(&cmd, "\\"); + nslash = 0; + } + for(j=0; j 1) + xprintf("%s\n", bstr(&cmd)); + + torune(&rcmd, bstr(&cmd)); + rexe = nil; + rdir = nil; + if(dir != nil) + torune(&rdir, dir); + + memset(&si, 0, sizeof si); + si.cb = sizeof si; + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = INVALID_HANDLE_VALUE; + if(b == nil) { + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + } else { + SECURITY_ATTRIBUTES seci; + + memset(&seci, 0, sizeof seci); + seci.nLength = sizeof seci; + seci.bInheritHandle = 1; + breset(b); + if(!CreatePipe(&p[0], &p[1], &seci, 0)) + fatal("CreatePipe: %s", errstr()); + si.hStdOutput = p[1]; + si.hStdError = p[1]; + } + + if(!CreateProcessW(rexe, rcmd, nil, nil, TRUE, 0, nil, rdir, &si, &pi)) { + if(mode!=CheckExit) + return; + fatal("%s: %s", argv->p[0], errstr()); + } + if(rexe != nil) + xfree(rexe); + xfree(rcmd); + if(rdir != nil) + xfree(rdir); + if(b != nil) { + CloseHandle(p[1]); + breadfrom(b, p[0]); + CloseHandle(p[0]); + } + + if(nbg < 0) + fatal("bad bookkeeping"); + bg[nbg].pi = pi; + bg[nbg].mode = mode; + bg[nbg].cmd = btake(&cmd); + nbg++; + + if(wait) + bgwait(); + + bfree(&cmd); +} + +// closes the background job for bgwait1 +static void +bgwaitclose(int i) +{ + if(i < 0 || i >= nbg) + return; + + CloseHandle(bg[i].pi.hProcess); + CloseHandle(bg[i].pi.hThread); + + bg[i] = bg[--nbg]; +} + +// bgwait1 waits for a single background job +static void +bgwait1(void) +{ + int i, mode; + char *cmd; + HANDLE bgh[MAXBG]; + DWORD code; + + if(nbg == 0) + fatal("bgwait1: nothing left"); + + for(i=0; i= nbg) + fatal("WaitForMultipleObjects: %s", errstr()); + + cmd = bg[i].cmd; + mode = bg[i].mode; + if(!GetExitCodeProcess(bg[i].pi.hProcess, &code)) { + bgwaitclose(i); + fatal("GetExitCodeProcess: %s", errstr()); + return; + } + + if(mode==CheckExit && code != 0) { + bgwaitclose(i); + fatal("FAILED: %s", cmd); + return; + } + + bgwaitclose(i); +} + +void +bgwait(void) +{ + while(nbg > 0) + bgwait1(); +} + +// rgetwd returns a rune string form of the current directory's path. +static Rune* +rgetwd(void) +{ + int n; + Rune *r; + + n = GetCurrentDirectoryW(0, nil); + r = xmalloc((n+1)*sizeof r[0]); + GetCurrentDirectoryW(n+1, r); + r[n] = '\0'; + return r; +} + +void +xgetwd(Buf *b) +{ + Rune *r; + + r = rgetwd(); + breset(b); + toutf(b, r); + xfree(r); +} + +void +xrealwd(Buf *b, char *path) +{ + Rune *old; + Rune *rnew; + + old = rgetwd(); + torune(&rnew, path); + if(!SetCurrentDirectoryW(rnew)) + fatal("chdir %s: %s", path, errstr()); + free(rnew); + xgetwd(b); + if(!SetCurrentDirectoryW(old)) { + breset(b); + toutf(b, old); + fatal("chdir %s: %s", bstr(b), errstr()); + } +} + +bool +isdir(char *p) +{ + DWORD attr; + Rune *r; + + torune(&r, p); + attr = GetFileAttributesW(r); + xfree(r); + return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY); +} + +bool +isfile(char *p) +{ + DWORD attr; + Rune *r; + + torune(&r, p); + attr = GetFileAttributesW(r); + xfree(r); + return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY); +} + +Time +mtime(char *p) +{ + HANDLE h; + WIN32_FIND_DATAW data; + Rune *r; + FILETIME *ft; + + torune(&r, p); + h = FindFirstFileW(r, &data); + xfree(r); + if(h == INVALID_HANDLE_VALUE) + return 0; + FindClose(h); + ft = &data.ftLastWriteTime; + return (Time)ft->dwLowDateTime + ((Time)ft->dwHighDateTime<<32); +} + +bool +isabs(char *p) +{ + // c:/ or c:\ at beginning + if(('A' <= p[0] && p[0] <= 'Z') || ('a' <= p[0] && p[0] <= 'z')) + return p[1] == ':' && (p[2] == '/' || p[2] == '\\'); + // / or \ at beginning + return p[0] == '/' || p[0] == '\\'; +} + +void +readfile(Buf *b, char *file) +{ + HANDLE h; + Rune *r; + + if(vflag > 2) + xprintf("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) + fatal("open %s: %s", file, errstr()); + breadfrom(b, h); + CloseHandle(h); +} + +void +writefile(Buf *b, char *file, int exec) +{ + HANDLE h; + Rune *r; + DWORD n; + + USED(exec); + + if(vflag > 2) + xprintf("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) + fatal("create %s: %s", file, errstr()); + n = 0; + if(!WriteFile(h, b->p, b->len, &n, 0)) + fatal("write %s: %s", file, errstr()); + CloseHandle(h); +} + + +void +xmkdir(char *p) +{ + Rune *r; + + torune(&r, p); + if(!CreateDirectoryW(r, nil)) + fatal("mkdir %s: %s", p, errstr()); + xfree(r); +} + +void +xmkdirall(char *p) +{ + int c; + char *q, *q2; + + if(isdir(p)) + return; + q = strrchr(p, '/'); + q2 = strrchr(p, '\\'); + if(q2 != nil && (q == nil || q < q2)) + q = q2; + if(q != nil) { + c = *q; + *q = '\0'; + xmkdirall(p); + *q = c; + } + xmkdir(p); +} + +void +xremove(char *p) +{ + int attr; + Rune *r; + + torune(&r, p); + attr = GetFileAttributesW(r); + if(attr >= 0) { + if(attr & FILE_ATTRIBUTE_DIRECTORY) + RemoveDirectoryW(r); + else + DeleteFileW(r); + } + xfree(r); +} + +void +xreaddir(Vec *dst, char *dir) +{ + Rune *r; + Buf b; + HANDLE h; + WIN32_FIND_DATAW data; + char *p, *q; + + binit(&b); + vreset(dst); + + bwritestr(&b, dir); + bwritestr(&b, "\\*"); + torune(&r, bstr(&b)); + + h = FindFirstFileW(r, &data); + xfree(r); + if(h == INVALID_HANDLE_VALUE) + goto out; + do{ + toutf(&b, data.cFileName); + p = bstr(&b); + q = xstrrchr(p, '\\'); + if(q != nil) + p = q+1; + if(!streq(p, ".") && !streq(p, "..")) + vadd(dst, p); + }while(FindNextFileW(h, &data)); + FindClose(h); + +out: + bfree(&b); +} + +char* +xworkdir(void) +{ + Rune buf[1024]; + Rune tmp[MAX_PATH]; + Rune go[3] = {'g', 'o', '\0'}; + int n; + Buf b; + + n = GetTempPathW(nelem(buf), buf); + if(n <= 0) + fatal("GetTempPath: %s", errstr()); + buf[n] = '\0'; + + if(GetTempFileNameW(buf, go, 0, tmp) == 0) + fatal("GetTempFileName: %s", errstr()); + DeleteFileW(tmp); + if(!CreateDirectoryW(tmp, nil)) + fatal("create tempdir: %s", errstr()); + + binit(&b); + toutf(&b, tmp); + return btake(&b); +} + +void +xremoveall(char *p) +{ + int i; + Buf b; + Vec dir; + Rune *r; + + binit(&b); + vinit(&dir); + + torune(&r, p); + if(isdir(p)) { + xreaddir(&dir, p); + for(i=0; i= ns && strcmp(p+np-ns, suffix) == 0; +} + +bool +hasprefix(char *p, char *prefix) +{ + return strncmp(p, prefix, strlen(prefix)) == 0; +} + +bool +contains(char *p, char *sep) +{ + return strstr(p, sep) != nil; +} + +bool +streq(char *p, char *q) +{ + return strcmp(p, q) == 0; +} + +char* +lastelem(char *p) +{ + char *out; + + out = p; + for(; *p; p++) + if(*p == '/' || *p == '\\') + out = p+1; + return out; +} + +void +xmemmove(void *dst, void *src, int n) +{ + memmove(dst, src, n); +} + +int +xmemcmp(void *a, void *b, int n) +{ + return memcmp(a, b, n); +} + +int +xstrlen(char *p) +{ + return strlen(p); +} + +void +xexit(int n) +{ + ExitProcess(n); +} + +void +xatexit(void (*f)(void)) +{ + atexit(f); +} + +void +xprintf(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_OUTPUT_HANDLE), p, n, &w, 0); + xfree(p); +} + +int +main(int argc, char **argv) +{ + SYSTEM_INFO si; + + setvbuf(stdout, nil, _IOLBF, 0); + setvbuf(stderr, nil, _IOLBF, 0); + + slash = "\\"; + gohostos = "windows"; + + GetSystemInfo(&si); + switch(si.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_AMD64: + gohostarch = "amd64"; + break; + case PROCESSOR_ARCHITECTURE_INTEL: + gohostarch = "386"; + break; + default: + fatal("unknown processor architecture"); + } + + init(); + + xmain(argc, argv); + return 0; +} + +void +xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*)) +{ + qsort(data, n, elemsize, cmp); +} + +int +xstrcmp(char *a, char *b) +{ + return strcmp(a, b); +} + +char* +xstrstr(char *a, char *b) +{ + return strstr(a, b); +} + +char* +xstrrchr(char *p, int c) +{ + char *ep; + + ep = p+strlen(p); + for(ep=p+strlen(p); ep >= p; ep--) + if(*ep == c) + return ep; + return nil; +} + +#endif // __WINDOWS__ diff --git a/src/cmd/ebnflint/Makefile b/src/cmd/ebnflint/Makefile deleted file mode 100644 index 8f030aaef..000000000 --- a/src/cmd/ebnflint/Makefile +++ /dev/null @@ -1,15 +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 ../../Make.inc - -TARG=ebnflint -GOFILES=\ - ebnflint.go\ - -include ../../Make.cmd - -test: $(TARG) - $(TARG) -start="SourceFile" "$(GOROOT)"/doc/go_spec.html - diff --git a/src/cmd/ebnflint/doc.go b/src/cmd/ebnflint/doc.go deleted file mode 100644 index f35976eea..000000000 --- a/src/cmd/ebnflint/doc.go +++ /dev/null @@ -1,22 +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. - -/* - -Ebnflint verifies that EBNF productions are consistent and gramatically correct. -It reads them from an HTML document such as the Go specification. - -Grammar productions are grouped in boxes demarcated by the HTML elements -
-	
- - -Usage: - ebnflint [--start production] [file] - -The --start flag specifies the name of the start production for -the grammar; it defaults to "Start". - -*/ -package documentation diff --git a/src/cmd/ebnflint/ebnflint.go b/src/cmd/ebnflint/ebnflint.go deleted file mode 100644 index 009b336f3..000000000 --- a/src/cmd/ebnflint/ebnflint.go +++ /dev/null @@ -1,109 +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. - -package main - -import ( - "bytes" - "ebnf" - "flag" - "fmt" - "go/scanner" - "go/token" - "io/ioutil" - "os" - "path/filepath" -) - -var fset = token.NewFileSet() -var start = flag.String("start", "Start", "name of start production") - -func usage() { - fmt.Fprintf(os.Stderr, "usage: ebnflint [flags] [filename]\n") - flag.PrintDefaults() - os.Exit(1) -} - -// Markers around EBNF sections in .html files -var ( - open = []byte(`
`)
-	close = []byte(`
`) -) - -func report(err os.Error) { - scanner.PrintError(os.Stderr, err) - os.Exit(1) -} - -func extractEBNF(src []byte) []byte { - var buf bytes.Buffer - - for { - // i = beginning of EBNF text - i := bytes.Index(src, open) - if i < 0 { - break // no EBNF found - we are done - } - i += len(open) - - // write as many newlines as found in the excluded text - // to maintain correct line numbers in error messages - for _, ch := range src[0:i] { - if ch == '\n' { - buf.WriteByte('\n') - } - } - - // j = end of EBNF text (or end of source) - j := bytes.Index(src[i:], close) // close marker - if j < 0 { - j = len(src) - i - } - j += i - - // copy EBNF text - buf.Write(src[i:j]) - - // advance - src = src[j:] - } - - return buf.Bytes() -} - -func main() { - flag.Parse() - - var ( - filename string - src []byte - err os.Error - ) - switch flag.NArg() { - case 0: - filename = "" - src, err = ioutil.ReadAll(os.Stdin) - case 1: - filename = flag.Arg(0) - src, err = ioutil.ReadFile(filename) - default: - usage() - } - if err != nil { - report(err) - } - - if filepath.Ext(filename) == ".html" || bytes.Index(src, open) >= 0 { - src = extractEBNF(src) - } - - grammar, err := ebnf.Parse(fset, filename, src) - if err != nil { - report(err) - } - - if err = ebnf.Verify(fset, grammar, *start); err != nil { - report(err) - } -} diff --git a/src/cmd/fix/doc.go b/src/cmd/fix/doc.go new file mode 100644 index 000000000..a92e0fc06 --- /dev/null +++ b/src/cmd/fix/doc.go @@ -0,0 +1,36 @@ +// 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. + +/* +Fix finds Go programs that use old APIs and rewrites them to use +newer ones. After you update to a new Go release, fix helps make +the necessary changes to your programs. + +Usage: + go tool fix [-r name,...] [path ...] + +Without an explicit path, fix reads standard input and writes the +result to standard output. + +If the named path is a file, fix rewrites the named files in place. +If the named path is a directory, fix rewrites all .go files in that +directory tree. When fix rewrites a file, it prints a line to standard +error giving the name of the file and the rewrite applied. + +If the -diff flag is set, no files are rewritten. Instead fix prints +the differences a rewrite would introduce. + +The -r flag restricts the set of rewrites considered to those in the +named list. By default fix considers all known rewrites. Fix's +rewrites are idempotent, so that it is safe to apply fix to updated +or partially updated code even without using the -r flag. + +Fix prints the full list of fixes it can apply in its help output; +to see them, run go tool fix -?. + +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 diff --git a/src/cmd/fix/error.go b/src/cmd/fix/error.go new file mode 100644 index 000000000..55613210a --- /dev/null +++ b/src/cmd/fix/error.go @@ -0,0 +1,353 @@ +// 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 new file mode 100644 index 000000000..027eed24f --- /dev/null +++ b/src/cmd/fix/error_test.go @@ -0,0 +1,240 @@ +// 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 new file mode 100644 index 000000000..f31226018 --- /dev/null +++ b/src/cmd/fix/filepath.go @@ -0,0 +1,56 @@ +// 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 new file mode 100644 index 000000000..37a2f5d9f --- /dev/null +++ b/src/cmd/fix/filepath_test.go @@ -0,0 +1,33 @@ +// 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/fix.go b/src/cmd/fix/fix.go new file mode 100644 index 000000000..a100be794 --- /dev/null +++ b/src/cmd/fix/fix.go @@ -0,0 +1,848 @@ +// 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 ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "path" + "reflect" + "strconv" + "strings" +) + +type fix struct { + name string + date string // date that fix was introduced, in YYYY-MM-DD format + f func(*ast.File) bool + desc string +} + +// main runs sort.Sort(byName(fixes)) before printing list of fixes. +type byName []fix + +func (f byName) Len() int { return len(f) } +func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } +func (f byName) Less(i, j int) bool { return f[i].name < f[j].name } + +// main runs sort.Sort(byDate(fixes)) before applying fixes. +type byDate []fix + +func (f byDate) Len() int { return len(f) } +func (f byDate) Swap(i, j int) { f[i], f[j] = f[j], f[i] } +func (f byDate) Less(i, j int) bool { return f[i].date < f[j].date } + +var fixes []fix + +func register(f fix) { + fixes = append(fixes, f) +} + +// walk traverses the AST x, calling visit(y) for each node y in the tree but +// also with a pointer to each ast.Expr, ast.Stmt, and *ast.BlockStmt, +// in a bottom-up traversal. +func walk(x interface{}, visit func(interface{})) { + walkBeforeAfter(x, nop, visit) +} + +func nop(interface{}) {} + +// walkBeforeAfter is like walk but calls before(x) before traversing +// x's children and after(x) afterward. +func walkBeforeAfter(x interface{}, before, after func(interface{})) { + before(x) + + switch n := x.(type) { + default: + panic(fmt.Errorf("unexpected type %T in walkBeforeAfter", x)) + + case nil: + + // pointers to interfaces + case *ast.Decl: + walkBeforeAfter(*n, before, after) + case *ast.Expr: + walkBeforeAfter(*n, before, after) + case *ast.Spec: + walkBeforeAfter(*n, before, after) + case *ast.Stmt: + walkBeforeAfter(*n, before, after) + + // pointers to struct pointers + case **ast.BlockStmt: + walkBeforeAfter(*n, before, after) + case **ast.CallExpr: + walkBeforeAfter(*n, before, after) + case **ast.FieldList: + walkBeforeAfter(*n, before, after) + case **ast.FuncType: + walkBeforeAfter(*n, before, after) + case **ast.Ident: + walkBeforeAfter(*n, before, after) + case **ast.BasicLit: + walkBeforeAfter(*n, before, after) + + // pointers to slices + case *[]ast.Decl: + walkBeforeAfter(*n, before, after) + case *[]ast.Expr: + walkBeforeAfter(*n, before, after) + case *[]*ast.File: + walkBeforeAfter(*n, before, after) + case *[]*ast.Ident: + walkBeforeAfter(*n, before, after) + case *[]ast.Spec: + walkBeforeAfter(*n, before, after) + case *[]ast.Stmt: + walkBeforeAfter(*n, before, after) + + // These are ordered and grouped to match ../../pkg/go/ast/ast.go + case *ast.Field: + walkBeforeAfter(&n.Names, before, after) + walkBeforeAfter(&n.Type, before, after) + walkBeforeAfter(&n.Tag, before, after) + case *ast.FieldList: + for _, field := range n.List { + walkBeforeAfter(field, before, after) + } + case *ast.BadExpr: + case *ast.Ident: + case *ast.Ellipsis: + walkBeforeAfter(&n.Elt, before, after) + case *ast.BasicLit: + case *ast.FuncLit: + walkBeforeAfter(&n.Type, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.CompositeLit: + walkBeforeAfter(&n.Type, before, after) + walkBeforeAfter(&n.Elts, before, after) + case *ast.ParenExpr: + walkBeforeAfter(&n.X, before, after) + case *ast.SelectorExpr: + walkBeforeAfter(&n.X, before, after) + case *ast.IndexExpr: + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Index, before, after) + case *ast.SliceExpr: + walkBeforeAfter(&n.X, before, after) + if n.Low != nil { + walkBeforeAfter(&n.Low, before, after) + } + if n.High != nil { + walkBeforeAfter(&n.High, before, after) + } + case *ast.TypeAssertExpr: + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Type, before, after) + case *ast.CallExpr: + walkBeforeAfter(&n.Fun, before, after) + walkBeforeAfter(&n.Args, before, after) + case *ast.StarExpr: + walkBeforeAfter(&n.X, before, after) + case *ast.UnaryExpr: + walkBeforeAfter(&n.X, before, after) + case *ast.BinaryExpr: + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Y, before, after) + case *ast.KeyValueExpr: + walkBeforeAfter(&n.Key, before, after) + walkBeforeAfter(&n.Value, before, after) + + case *ast.ArrayType: + walkBeforeAfter(&n.Len, before, after) + walkBeforeAfter(&n.Elt, before, after) + case *ast.StructType: + walkBeforeAfter(&n.Fields, before, after) + case *ast.FuncType: + walkBeforeAfter(&n.Params, before, after) + if n.Results != nil { + walkBeforeAfter(&n.Results, before, after) + } + case *ast.InterfaceType: + walkBeforeAfter(&n.Methods, before, after) + case *ast.MapType: + walkBeforeAfter(&n.Key, before, after) + walkBeforeAfter(&n.Value, before, after) + case *ast.ChanType: + walkBeforeAfter(&n.Value, before, after) + + case *ast.BadStmt: + case *ast.DeclStmt: + walkBeforeAfter(&n.Decl, before, after) + case *ast.EmptyStmt: + case *ast.LabeledStmt: + walkBeforeAfter(&n.Stmt, before, after) + case *ast.ExprStmt: + walkBeforeAfter(&n.X, before, after) + case *ast.SendStmt: + walkBeforeAfter(&n.Chan, before, after) + walkBeforeAfter(&n.Value, before, after) + case *ast.IncDecStmt: + walkBeforeAfter(&n.X, before, after) + case *ast.AssignStmt: + walkBeforeAfter(&n.Lhs, before, after) + walkBeforeAfter(&n.Rhs, before, after) + case *ast.GoStmt: + walkBeforeAfter(&n.Call, before, after) + case *ast.DeferStmt: + walkBeforeAfter(&n.Call, before, after) + case *ast.ReturnStmt: + walkBeforeAfter(&n.Results, before, after) + case *ast.BranchStmt: + case *ast.BlockStmt: + walkBeforeAfter(&n.List, before, after) + case *ast.IfStmt: + walkBeforeAfter(&n.Init, before, after) + walkBeforeAfter(&n.Cond, before, after) + walkBeforeAfter(&n.Body, before, after) + walkBeforeAfter(&n.Else, before, after) + case *ast.CaseClause: + walkBeforeAfter(&n.List, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.SwitchStmt: + walkBeforeAfter(&n.Init, before, after) + walkBeforeAfter(&n.Tag, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.TypeSwitchStmt: + walkBeforeAfter(&n.Init, before, after) + walkBeforeAfter(&n.Assign, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.CommClause: + walkBeforeAfter(&n.Comm, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.SelectStmt: + walkBeforeAfter(&n.Body, before, after) + case *ast.ForStmt: + walkBeforeAfter(&n.Init, before, after) + walkBeforeAfter(&n.Cond, before, after) + walkBeforeAfter(&n.Post, before, after) + walkBeforeAfter(&n.Body, before, after) + case *ast.RangeStmt: + walkBeforeAfter(&n.Key, before, after) + walkBeforeAfter(&n.Value, before, after) + walkBeforeAfter(&n.X, before, after) + walkBeforeAfter(&n.Body, before, after) + + case *ast.ImportSpec: + case *ast.ValueSpec: + walkBeforeAfter(&n.Type, before, after) + walkBeforeAfter(&n.Values, before, after) + walkBeforeAfter(&n.Names, before, after) + case *ast.TypeSpec: + walkBeforeAfter(&n.Type, before, after) + + case *ast.BadDecl: + case *ast.GenDecl: + walkBeforeAfter(&n.Specs, before, after) + case *ast.FuncDecl: + if n.Recv != nil { + walkBeforeAfter(&n.Recv, before, after) + } + walkBeforeAfter(&n.Type, before, after) + if n.Body != nil { + walkBeforeAfter(&n.Body, before, after) + } + + case *ast.File: + walkBeforeAfter(&n.Decls, before, after) + + case *ast.Package: + walkBeforeAfter(&n.Files, before, after) + + case []*ast.File: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []ast.Decl: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []ast.Expr: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []*ast.Ident: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []ast.Stmt: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + case []ast.Spec: + for i := range n { + walkBeforeAfter(&n[i], before, after) + } + } + after(x) +} + +// imports returns true if f imports path. +func imports(f *ast.File, path string) bool { + return importSpec(f, path) != nil +} + +// importSpec returns the import spec if f imports path, +// or nil otherwise. +func importSpec(f *ast.File, path string) *ast.ImportSpec { + for _, s := range f.Imports { + if importPath(s) == path { + return s + } + } + return nil +} + +// importPath returns the unquoted import path of s, +// or "" if the path is not properly quoted. +func importPath(s *ast.ImportSpec) string { + t, err := strconv.Unquote(s.Path.Value) + if err == nil { + return t + } + return "" +} + +// declImports reports whether gen contains an import of path. +func declImports(gen *ast.GenDecl, path string) bool { + if gen.Tok != token.IMPORT { + return false + } + for _, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + if importPath(impspec) == path { + return true + } + } + return false +} + +// isPkgDot returns true if t is the expression "pkg.name" +// where pkg is an imported identifier. +func isPkgDot(t ast.Expr, pkg, name string) bool { + sel, ok := t.(*ast.SelectorExpr) + return ok && isTopName(sel.X, pkg) && sel.Sel.String() == name +} + +// isPtrPkgDot returns true if f is the expression "*pkg.name" +// where pkg is an imported identifier. +func isPtrPkgDot(t ast.Expr, pkg, name string) bool { + ptr, ok := t.(*ast.StarExpr) + return ok && isPkgDot(ptr.X, pkg, name) +} + +// isTopName returns true if n is a top-level unresolved identifier with the given name. +func isTopName(n ast.Expr, name string) bool { + id, ok := n.(*ast.Ident) + return ok && id.Name == name && id.Obj == nil +} + +// isName returns true if n is an identifier with the given name. +func isName(n ast.Expr, name string) bool { + id, ok := n.(*ast.Ident) + return ok && id.String() == name +} + +// isCall returns true if t is a call to pkg.name. +func isCall(t ast.Expr, pkg, name string) bool { + call, ok := t.(*ast.CallExpr) + return ok && isPkgDot(call.Fun, pkg, name) +} + +// If n is an *ast.Ident, isIdent returns it; otherwise isIdent returns nil. +func isIdent(n interface{}) *ast.Ident { + id, _ := n.(*ast.Ident) + return id +} + +// refersTo returns true if n is a reference to the same object as x. +func refersTo(n ast.Node, x *ast.Ident) bool { + id, ok := n.(*ast.Ident) + // The test of id.Name == x.Name handles top-level unresolved + // identifiers, which all have Obj == nil. + return ok && id.Obj == x.Obj && id.Name == x.Name +} + +// isBlank returns true if n is the blank identifier. +func isBlank(n ast.Expr) bool { + return isName(n, "_") +} + +// isEmptyString returns true if n is an empty string literal. +func isEmptyString(n ast.Expr) bool { + lit, ok := n.(*ast.BasicLit) + return ok && lit.Kind == token.STRING && len(lit.Value) == 2 +} + +func warn(pos token.Pos, msg string, args ...interface{}) { + if pos.IsValid() { + msg = "%s: " + msg + arg1 := []interface{}{fset.Position(pos).String()} + args = append(arg1, args...) + } + fmt.Fprintf(os.Stderr, msg+"\n", args...) +} + +// countUses returns the number of uses of the identifier x in scope. +func countUses(x *ast.Ident, scope []ast.Stmt) int { + count := 0 + ff := func(n interface{}) { + if n, ok := n.(ast.Node); ok && refersTo(n, x) { + count++ + } + } + for _, n := range scope { + walk(n, ff) + } + return count +} + +// rewriteUses replaces all uses of the identifier x and !x in scope +// with f(x.Pos()) and fnot(x.Pos()). +func rewriteUses(x *ast.Ident, f, fnot func(token.Pos) ast.Expr, scope []ast.Stmt) { + var lastF ast.Expr + ff := func(n interface{}) { + ptr, ok := n.(*ast.Expr) + if !ok { + return + } + nn := *ptr + + // The child node was just walked and possibly replaced. + // If it was replaced and this is a negation, replace with fnot(p). + not, ok := nn.(*ast.UnaryExpr) + if ok && not.Op == token.NOT && not.X == lastF { + *ptr = fnot(nn.Pos()) + return + } + if refersTo(nn, x) { + lastF = f(nn.Pos()) + *ptr = lastF + } + } + for _, n := range scope { + walk(n, ff) + } +} + +// assignsTo returns true if any of the code in scope assigns to or takes the address of x. +func assignsTo(x *ast.Ident, scope []ast.Stmt) bool { + assigned := false + ff := func(n interface{}) { + if assigned { + return + } + switch n := n.(type) { + case *ast.UnaryExpr: + // use of &x + if n.Op == token.AND && refersTo(n.X, x) { + assigned = true + return + } + case *ast.AssignStmt: + for _, l := range n.Lhs { + if refersTo(l, x) { + assigned = true + return + } + } + } + } + for _, n := range scope { + if assigned { + break + } + walk(n, ff) + } + return assigned +} + +// newPkgDot returns an ast.Expr referring to "pkg.name" at position pos. +func newPkgDot(pos token.Pos, pkg, name string) ast.Expr { + return &ast.SelectorExpr{ + X: &ast.Ident{ + NamePos: pos, + Name: pkg, + }, + Sel: &ast.Ident{ + NamePos: pos, + Name: name, + }, + } +} + +// renameTop renames all references to the top-level name old. +// It returns true if it makes any changes. +func renameTop(f *ast.File, old, new string) bool { + var fixed bool + + // Rename any conflicting imports + // (assuming package name is last element of path). + for _, s := range f.Imports { + if s.Name != nil { + if s.Name.Name == old { + s.Name.Name = new + fixed = true + } + } else { + _, thisName := path.Split(importPath(s)) + if thisName == old { + s.Name = ast.NewIdent(new) + fixed = true + } + } + } + + // Rename any top-level declarations. + for _, d := range f.Decls { + switch d := d.(type) { + case *ast.FuncDecl: + if d.Recv == nil && d.Name.Name == old { + d.Name.Name = new + d.Name.Obj.Name = new + fixed = true + } + case *ast.GenDecl: + for _, s := range d.Specs { + switch s := s.(type) { + case *ast.TypeSpec: + if s.Name.Name == old { + s.Name.Name = new + s.Name.Obj.Name = new + fixed = true + } + case *ast.ValueSpec: + for _, n := range s.Names { + if n.Name == old { + n.Name = new + n.Obj.Name = new + fixed = true + } + } + } + } + } + } + + // Rename top-level old to new, both unresolved names + // (probably defined in another file) and names that resolve + // to a declaration we renamed. + walk(f, func(n interface{}) { + id, ok := n.(*ast.Ident) + if ok && isTopName(id, old) { + id.Name = new + fixed = true + } + if ok && id.Obj != nil && id.Name == old && id.Obj.Name == new { + id.Name = id.Obj.Name + fixed = true + } + }) + + return fixed +} + +// matchLen returns the length of the longest prefix shared by x and y. +func matchLen(x, y string) int { + i := 0 + for i < len(x) && i < len(y) && x[i] == y[i] { + i++ + } + return i +} + +// addImport adds the import path to the file f, if absent. +func addImport(f *ast.File, ipath string) (added bool) { + if imports(f, ipath) { + return false + } + + // Determine name of import. + // Assume added imports follow convention of using last element. + _, name := path.Split(ipath) + + // Rename any conflicting top-level references from name to name_. + renameTop(f, name, name+"_") + + newImport := &ast.ImportSpec{ + Path: &ast.BasicLit{ + Kind: token.STRING, + Value: strconv.Quote(ipath), + }, + } + + // Find an import decl to add to. + var ( + bestMatch = -1 + lastImport = -1 + impDecl *ast.GenDecl + impIndex = -1 + ) + for i, decl := range f.Decls { + gen, ok := decl.(*ast.GenDecl) + if ok && gen.Tok == token.IMPORT { + lastImport = i + // Do not add to import "C", to avoid disrupting the + // association with its doc comment, breaking cgo. + if declImports(gen, "C") { + continue + } + + // Compute longest shared prefix with imports in this block. + for j, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + n := matchLen(importPath(impspec), ipath) + if n > bestMatch { + bestMatch = n + impDecl = gen + impIndex = j + } + } + } + } + + // If no import decl found, add one after the last import. + if impDecl == nil { + impDecl = &ast.GenDecl{ + Tok: token.IMPORT, + } + f.Decls = append(f.Decls, nil) + copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:]) + f.Decls[lastImport+1] = impDecl + } + + // Ensure the import decl has parentheses, if needed. + if len(impDecl.Specs) > 0 && !impDecl.Lparen.IsValid() { + impDecl.Lparen = impDecl.Pos() + } + + insertAt := impIndex + 1 + if insertAt == 0 { + insertAt = len(impDecl.Specs) + } + impDecl.Specs = append(impDecl.Specs, nil) + copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:]) + impDecl.Specs[insertAt] = newImport + if insertAt > 0 { + // Assign same position as the previous import, + // so that the sorter sees it as being in the same block. + prev := impDecl.Specs[insertAt-1] + newImport.Path.ValuePos = prev.Pos() + newImport.EndPos = prev.Pos() + } + + f.Imports = append(f.Imports, newImport) + return true +} + +// deleteImport deletes the import path from the file f, if present. +func deleteImport(f *ast.File, path string) (deleted bool) { + oldImport := importSpec(f, path) + + // Find the import node that imports path, if any. + for i, decl := range f.Decls { + gen, ok := decl.(*ast.GenDecl) + if !ok || gen.Tok != token.IMPORT { + continue + } + for j, spec := range gen.Specs { + impspec := spec.(*ast.ImportSpec) + if oldImport != impspec { + continue + } + + // We found an import spec that imports path. + // Delete it. + deleted = true + copy(gen.Specs[j:], gen.Specs[j+1:]) + gen.Specs = gen.Specs[:len(gen.Specs)-1] + + // If this was the last import spec in this decl, + // delete the decl, too. + if len(gen.Specs) == 0 { + copy(f.Decls[i:], f.Decls[i+1:]) + f.Decls = f.Decls[:len(f.Decls)-1] + } else if len(gen.Specs) == 1 { + gen.Lparen = token.NoPos // drop parens + } + if j > 0 { + // We deleted an entry but now there will be + // a blank line-sized hole where the import was. + // Close the hole by making the previous + // import appear to "end" where this one did. + gen.Specs[j-1].(*ast.ImportSpec).EndPos = impspec.End() + } + break + } + } + + // Delete it from f.Imports. + for i, imp := range f.Imports { + if imp == oldImport { + copy(f.Imports[i:], f.Imports[i+1:]) + f.Imports = f.Imports[:len(f.Imports)-1] + break + } + } + + return +} + +// rewriteImport rewrites any import of path oldPath to path newPath. +func rewriteImport(f *ast.File, oldPath, newPath string) (rewrote bool) { + for _, imp := range f.Imports { + if importPath(imp) == oldPath { + rewrote = true + // record old End, because the default is to compute + // it using the length of imp.Path.Value. + imp.EndPos = imp.End() + imp.Path.Value = strconv.Quote(newPath) + } + } + return +} + +func usesImport(f *ast.File, path string) (used bool) { + spec := importSpec(f, path) + if spec == nil { + return + } + + name := spec.Name.String() + switch name { + case "": + // If the package name is not explicitly specified, + // make an educated guess. This is not guaranteed to be correct. + lastSlash := strings.LastIndex(path, "/") + if lastSlash == -1 { + name = path + } else { + name = path[lastSlash+1:] + } + case "_", ".": + // Not sure if this import is used - err on the side of caution. + return true + } + + walk(f, func(n interface{}) { + sel, ok := n.(*ast.SelectorExpr) + if ok && isTopName(sel.X, name) { + used = true + } + }) + + return +} + +func expr(s string) ast.Expr { + x, err := parser.ParseExpr(s) + if err != nil { + panic("parsing " + s + ": " + err.Error()) + } + // Remove position information to avoid spurious newlines. + killPos(reflect.ValueOf(x)) + return x +} + +var posType = reflect.TypeOf(token.Pos(0)) + +func killPos(v reflect.Value) { + switch v.Kind() { + case reflect.Ptr, reflect.Interface: + if !v.IsNil() { + killPos(v.Elem()) + } + case reflect.Slice: + n := v.Len() + for i := 0; i < n; i++ { + killPos(v.Index(i)) + } + case reflect.Struct: + n := v.NumField() + for i := 0; i < n; i++ { + f := v.Field(i) + if f.Type() == posType { + f.SetInt(0) + continue + } + killPos(f) + } + } +} + +// A Rename describes a single renaming. +type rename struct { + OldImport string // only apply rename if this import is present + NewImport string // add this import during rewrite + Old string // old name: p.T or *p.T + New string // new name: p.T or *p.T +} + +func renameFix(tab []rename) func(*ast.File) bool { + return func(f *ast.File) bool { + return renameFixTab(f, tab) + } +} + +func parseName(s string) (ptr bool, pkg, nam string) { + i := strings.Index(s, ".") + if i < 0 { + panic("parseName: invalid name " + s) + } + if strings.HasPrefix(s, "*") { + ptr = true + s = s[1:] + i-- + } + pkg = s[:i] + nam = s[i+1:] + return +} + +func renameFixTab(f *ast.File, tab []rename) bool { + fixed := false + added := map[string]bool{} + check := map[string]bool{} + for _, t := range tab { + if !imports(f, t.OldImport) { + continue + } + optr, opkg, onam := parseName(t.Old) + walk(f, func(n interface{}) { + np, ok := n.(*ast.Expr) + if !ok { + return + } + x := *np + if optr { + p, ok := x.(*ast.StarExpr) + if !ok { + return + } + x = p.X + } + if !isPkgDot(x, opkg, onam) { + return + } + if t.NewImport != "" && !added[t.NewImport] { + addImport(f, t.NewImport) + added[t.NewImport] = true + } + *np = expr(t.New) + check[t.OldImport] = true + fixed = true + }) + } + + for ipath := range check { + if !usesImport(f, ipath) { + deleteImport(f, ipath) + } + } + return fixed +} diff --git a/src/cmd/fix/go1pkgrename.go b/src/cmd/fix/go1pkgrename.go new file mode 100644 index 000000000..f701f62f0 --- /dev/null +++ b/src/cmd/fix/go1pkgrename.go @@ -0,0 +1,146 @@ +// 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 new file mode 100644 index 000000000..840e443b0 --- /dev/null +++ b/src/cmd/fix/go1pkgrename_test.go @@ -0,0 +1,139 @@ +// 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 new file mode 100644 index 000000000..9266c749c --- /dev/null +++ b/src/cmd/fix/go1rename.go @@ -0,0 +1,167 @@ +// 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 new file mode 100644 index 000000000..90219ba71 --- /dev/null +++ b/src/cmd/fix/go1rename_test.go @@ -0,0 +1,195 @@ +// 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 new file mode 100644 index 000000000..143781a74 --- /dev/null +++ b/src/cmd/fix/googlecode.go @@ -0,0 +1,41 @@ +// 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 new file mode 100644 index 000000000..c62ee4f32 --- /dev/null +++ b/src/cmd/fix/googlecode_test.go @@ -0,0 +1,31 @@ +// 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 new file mode 100644 index 000000000..0df6ad749 --- /dev/null +++ b/src/cmd/fix/hashsum.go @@ -0,0 +1,94 @@ +// 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 new file mode 100644 index 000000000..241af2020 --- /dev/null +++ b/src/cmd/fix/hashsum_test.go @@ -0,0 +1,99 @@ +// 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 new file mode 100644 index 000000000..c0c44ef3e --- /dev/null +++ b/src/cmd/fix/hmacnew.go @@ -0,0 +1,61 @@ +// 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 new file mode 100644 index 000000000..5aeee8573 --- /dev/null +++ b/src/cmd/fix/hmacnew_test.go @@ -0,0 +1,107 @@ +// 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 new file mode 100644 index 000000000..b5105c822 --- /dev/null +++ b/src/cmd/fix/htmlerr.go @@ -0,0 +1,47 @@ +// 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 new file mode 100644 index 000000000..043abc42a --- /dev/null +++ b/src/cmd/fix/htmlerr_test.go @@ -0,0 +1,39 @@ +// 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 new file mode 100644 index 000000000..49b9f1c51 --- /dev/null +++ b/src/cmd/fix/httpfinalurl.go @@ -0,0 +1,57 @@ +// 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 new file mode 100644 index 000000000..9249f7e18 --- /dev/null +++ b/src/cmd/fix/httpfinalurl_test.go @@ -0,0 +1,37 @@ +// 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 new file mode 100644 index 000000000..d87b30f9d --- /dev/null +++ b/src/cmd/fix/httpfs.go @@ -0,0 +1,70 @@ +// 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 new file mode 100644 index 000000000..dd8ef2cfd --- /dev/null +++ b/src/cmd/fix/httpfs_test.go @@ -0,0 +1,47 @@ +// 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 new file mode 100644 index 000000000..15c21ac86 --- /dev/null +++ b/src/cmd/fix/httpheaders.go @@ -0,0 +1,67 @@ +// 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 new file mode 100644 index 000000000..37506b82d --- /dev/null +++ b/src/cmd/fix/httpheaders_test.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. + +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 new file mode 100644 index 000000000..7aa651786 --- /dev/null +++ b/src/cmd/fix/httpserver.go @@ -0,0 +1,141 @@ +// 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 new file mode 100644 index 000000000..b6ddff27e --- /dev/null +++ b/src/cmd/fix/httpserver_test.go @@ -0,0 +1,53 @@ +// 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 new file mode 100644 index 000000000..1aac40a6f --- /dev/null +++ b/src/cmd/fix/imagecolor.go @@ -0,0 +1,85 @@ +// 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 new file mode 100644 index 000000000..c62365481 --- /dev/null +++ b/src/cmd/fix/imagecolor_test.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 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 new file mode 100644 index 000000000..b4e36d4f0 --- /dev/null +++ b/src/cmd/fix/imagenew.go @@ -0,0 +1,83 @@ +// 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 new file mode 100644 index 000000000..30abed23c --- /dev/null +++ b/src/cmd/fix/imagenew_test.go @@ -0,0 +1,51 @@ +// 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 new file mode 100644 index 000000000..41b96d18d --- /dev/null +++ b/src/cmd/fix/imageycbcr.go @@ -0,0 +1,64 @@ +// 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 new file mode 100644 index 000000000..23b599dcd --- /dev/null +++ b/src/cmd/fix/imageycbcr_test.go @@ -0,0 +1,54 @@ +// 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/import_test.go b/src/cmd/fix/import_test.go new file mode 100644 index 000000000..730119205 --- /dev/null +++ b/src/cmd/fix/import_test.go @@ -0,0 +1,458 @@ +// 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() { + addTestCases(importTests, nil) +} + +var importTests = []testCase{ + { + Name: "import.0", + Fn: addImportFn("os"), + In: `package main + +import ( + "os" +) +`, + Out: `package main + +import ( + "os" +) +`, + }, + { + Name: "import.1", + Fn: addImportFn("os"), + In: `package main +`, + Out: `package main + +import "os" +`, + }, + { + Name: "import.2", + Fn: addImportFn("os"), + In: `package main + +// Comment +import "C" +`, + Out: `package main + +// Comment +import "C" +import "os" +`, + }, + { + Name: "import.3", + Fn: addImportFn("os"), + In: `package main + +// Comment +import "C" + +import ( + "io" + "utf8" +) +`, + Out: `package main + +// Comment +import "C" + +import ( + "io" + "os" + "utf8" +) +`, + }, + { + Name: "import.4", + Fn: deleteImportFn("os"), + In: `package main + +import ( + "os" +) +`, + Out: `package main +`, + }, + { + Name: "import.5", + Fn: deleteImportFn("os"), + In: `package main + +// Comment +import "C" +import "os" +`, + Out: `package main + +// Comment +import "C" +`, + }, + { + Name: "import.6", + Fn: deleteImportFn("os"), + In: `package main + +// Comment +import "C" + +import ( + "io" + "os" + "utf8" +) +`, + Out: `package main + +// Comment +import "C" + +import ( + "io" + "utf8" +) +`, + }, + { + Name: "import.7", + Fn: deleteImportFn("io"), + In: `package main + +import ( + "io" // a + "os" // b + "utf8" // c +) +`, + Out: `package main + +import ( + // a + "os" // b + "utf8" // c +) +`, + }, + { + Name: "import.8", + Fn: deleteImportFn("os"), + In: `package main + +import ( + "io" // a + "os" // b + "utf8" // c +) +`, + Out: `package main + +import ( + "io" // a + // b + "utf8" // c +) +`, + }, + { + Name: "import.9", + Fn: deleteImportFn("utf8"), + In: `package main + +import ( + "io" // a + "os" // b + "utf8" // c +) +`, + Out: `package main + +import ( + "io" // a + "os" // b + // c +) +`, + }, + { + Name: "import.10", + Fn: deleteImportFn("io"), + In: `package main + +import ( + "io" + "os" + "utf8" +) +`, + Out: `package main + +import ( + "os" + "utf8" +) +`, + }, + { + Name: "import.11", + Fn: deleteImportFn("os"), + In: `package main + +import ( + "io" + "os" + "utf8" +) +`, + Out: `package main + +import ( + "io" + "utf8" +) +`, + }, + { + Name: "import.12", + Fn: deleteImportFn("utf8"), + In: `package main + +import ( + "io" + "os" + "utf8" +) +`, + Out: `package main + +import ( + "io" + "os" +) +`, + }, + { + Name: "import.13", + Fn: rewriteImportFn("utf8", "encoding/utf8"), + In: `package main + +import ( + "io" + "os" + "utf8" // thanks ken +) +`, + Out: `package main + +import ( + "encoding/utf8" // thanks ken + "io" + "os" +) +`, + }, + { + Name: "import.14", + Fn: rewriteImportFn("asn1", "encoding/asn1"), + In: `package main + +import ( + "asn1" + "crypto" + "crypto/rsa" + _ "crypto/sha1" + "crypto/x509" + "crypto/x509/pkix" + "time" +) + +var x = 1 +`, + Out: `package main + +import ( + "crypto" + "crypto/rsa" + _ "crypto/sha1" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "time" +) + +var x = 1 +`, + }, + { + Name: "import.15", + Fn: rewriteImportFn("url", "net/url"), + In: `package main + +import ( + "bufio" + "net" + "path" + "url" +) + +var x = 1 // comment on x, not on url +`, + Out: `package main + +import ( + "bufio" + "net" + "net/url" + "path" +) + +var x = 1 // comment on x, not on url +`, + }, + { + Name: "import.16", + Fn: rewriteImportFn("http", "net/http", "template", "text/template"), + In: `package main + +import ( + "flag" + "http" + "log" + "template" +) + +var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 +`, + Out: `package main + +import ( + "flag" + "log" + "net/http" + "text/template" +) + +var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 +`, + }, + { + Name: "import.17", + Fn: addImportFn("x/y/z", "x/a/c"), + In: `package main + +// Comment +import "C" + +import ( + "a" + "b" + + "x/w" + + "d/f" +) +`, + Out: `package main + +// Comment +import "C" + +import ( + "a" + "b" + + "x/a/c" + "x/w" + "x/y/z" + + "d/f" +) +`, + }, + { + Name: "import.18", + Fn: addDelImportFn("e", "o"), + In: `package main + +import ( + "f" + "o" + "z" +) +`, + Out: `package main + +import ( + "e" + "f" + "z" +) +`, + }, +} + +func addImportFn(path ...string) func(*ast.File) bool { + return func(f *ast.File) bool { + fixed := false + for _, p := range path { + if !imports(f, p) { + addImport(f, p) + fixed = true + } + } + return fixed + } +} + +func deleteImportFn(path string) func(*ast.File) bool { + return func(f *ast.File) bool { + if imports(f, path) { + deleteImport(f, path) + return true + } + return false + } +} + +func addDelImportFn(p1 string, p2 string) func(*ast.File) bool { + return func(f *ast.File) bool { + fixed := false + if !imports(f, p1) { + addImport(f, p1) + fixed = true + } + if imports(f, p2) { + deleteImport(f, p2) + fixed = true + } + return fixed + } +} + +func rewriteImportFn(oldnew ...string) func(*ast.File) bool { + return func(f *ast.File) bool { + fixed := false + for i := 0; i < len(oldnew); i += 2 { + if imports(f, oldnew[i]) { + rewriteImport(f, oldnew[i], oldnew[i+1]) + fixed = true + } + } + return fixed + } +} diff --git a/src/cmd/fix/iocopyn.go b/src/cmd/fix/iocopyn.go new file mode 100644 index 000000000..720f3c689 --- /dev/null +++ b/src/cmd/fix/iocopyn.go @@ -0,0 +1,41 @@ +// 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 new file mode 100644 index 000000000..f86fad763 --- /dev/null +++ b/src/cmd/fix/iocopyn_test.go @@ -0,0 +1,37 @@ +// 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 new file mode 100644 index 000000000..b151408d7 --- /dev/null +++ b/src/cmd/fix/main.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 main + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/scanner" + "go/token" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "sort" + "strings" +) + +var ( + fset = token.NewFileSet() + exitCode = 0 +) + +var allowedRewrites = flag.String("r", "", + "restrict the rewrites to this comma-separated list") + +var forceRewrites = flag.String("force", "", + "force these fixes to run even if the code looks updated") + +var allowed, force map[string]bool + +var doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files") + +// enable for debugging fix failures +const debug = false // display incorrectly reformatted source and exit + +func usage() { + fmt.Fprintf(os.Stderr, "usage: go tool fix [-diff] [-r fixname,...] [-force fixname,...] [path ...]\n") + flag.PrintDefaults() + fmt.Fprintf(os.Stderr, "\nAvailable rewrites are:\n") + sort.Sort(byName(fixes)) + for _, f := range fixes { + fmt.Fprintf(os.Stderr, "\n%s\n", f.name) + desc := strings.TrimSpace(f.desc) + desc = strings.Replace(desc, "\n", "\n\t", -1) + fmt.Fprintf(os.Stderr, "\t%s\n", desc) + } + os.Exit(2) +} + +func main() { + flag.Usage = usage + flag.Parse() + + sort.Sort(byDate(fixes)) + + if *allowedRewrites != "" { + allowed = make(map[string]bool) + for _, f := range strings.Split(*allowedRewrites, ",") { + allowed[f] = true + } + } + + if *forceRewrites != "" { + force = make(map[string]bool) + for _, f := range strings.Split(*forceRewrites, ",") { + force[f] = true + } + } + + if flag.NArg() == 0 { + if err := processFile("standard input", true); err != nil { + report(err) + } + os.Exit(exitCode) + } + + for i := 0; i < flag.NArg(); i++ { + path := flag.Arg(i) + switch dir, err := os.Stat(path); { + case err != nil: + report(err) + case dir.IsDir(): + walkDir(path) + default: + if err := processFile(path, false); err != nil { + report(err) + } + } + } + + os.Exit(exitCode) +} + +const ( + tabWidth = 8 + parserMode = parser.ParseComments + printerMode = printer.TabIndent | printer.UseSpaces +) + +var printConfig = &printer.Config{ + Mode: printerMode, + Tabwidth: tabWidth, +} + +func gofmtFile(f *ast.File) ([]byte, error) { + var buf bytes.Buffer + + ast.SortImports(fset, f) + err := printConfig.Fprint(&buf, fset, f) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func processFile(filename string, useStdin bool) error { + var f *os.File + var err error + var fixlog bytes.Buffer + + if useStdin { + f = os.Stdin + } else { + f, err = os.Open(filename) + if err != nil { + return err + } + defer f.Close() + } + + src, err := ioutil.ReadAll(f) + if err != nil { + return err + } + + file, err := parser.ParseFile(fset, filename, src, parserMode) + if err != nil { + return err + } + + // Apply all fixes to file. + newFile := file + fixed := false + for _, fix := range fixes { + if allowed != nil && !allowed[fix.name] { + continue + } + if fix.f(newFile) { + fixed = true + fmt.Fprintf(&fixlog, " %s", fix.name) + + // AST changed. + // Print and parse, to update any missing scoping + // or position information for subsequent fixers. + newSrc, err := gofmtFile(newFile) + if err != nil { + return err + } + newFile, err = parser.ParseFile(fset, filename, newSrc, parserMode) + if err != nil { + if debug { + fmt.Printf("%s", newSrc) + report(err) + os.Exit(exitCode) + } + return err + } + } + } + if !fixed { + return nil + } + fmt.Fprintf(os.Stderr, "%s: fixed %s\n", filename, fixlog.String()[1:]) + + // Print AST. We did that after each fix, so this appears + // redundant, but it is necessary to generate gofmt-compatible + // source code in a few cases. The official gofmt style is the + // output of the printer run on a standard AST generated by the parser, + // but the source we generated inside the loop above is the + // output of the printer run on a mangled AST generated by a fixer. + newSrc, err := gofmtFile(newFile) + if err != nil { + return err + } + + if *doDiff { + data, err := diff(src, newSrc) + if err != nil { + return fmt.Errorf("computing diff: %s", err) + } + fmt.Printf("diff %s fixed/%s\n", filename, filename) + os.Stdout.Write(data) + return nil + } + + if useStdin { + os.Stdout.Write(newSrc) + return nil + } + + return ioutil.WriteFile(f.Name(), newSrc, 0) +} + +var gofmtBuf bytes.Buffer + +func gofmt(n interface{}) string { + gofmtBuf.Reset() + err := printConfig.Fprint(&gofmtBuf, fset, n) + if err != nil { + return "<" + err.Error() + ">" + } + return gofmtBuf.String() +} + +func report(err error) { + scanner.PrintError(os.Stderr, err) + exitCode = 2 +} + +func walkDir(path string) { + filepath.Walk(path, visitFile) +} + +func visitFile(path string, f os.FileInfo, err error) error { + if err == nil && isGoFile(f) { + err = processFile(path, false) + } + if err != nil { + report(err) + } + return nil +} + +func isGoFile(f os.FileInfo) bool { + // ignore non-Go files + name := f.Name() + return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") +} + +func diff(b1, b2 []byte) (data []byte, err error) { + f1, err := ioutil.TempFile("", "go-fix") + if err != nil { + return nil, err + } + defer os.Remove(f1.Name()) + defer f1.Close() + + f2, err := ioutil.TempFile("", "go-fix") + if err != nil { + return nil, err + } + defer os.Remove(f2.Name()) + defer f2.Close() + + f1.Write(b1) + f2.Write(b2) + + data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() + if len(data) > 0 { + // diff exits with a non-zero status when the files don't match. + // Ignore that failure as long as we get output. + err = nil + } + return +} diff --git a/src/cmd/fix/main_test.go b/src/cmd/fix/main_test.go new file mode 100644 index 000000000..2151bf29e --- /dev/null +++ b/src/cmd/fix/main_test.go @@ -0,0 +1,129 @@ +// 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/parser" + "strings" + "testing" +) + +type testCase struct { + Name string + Fn func(*ast.File) bool + In string + Out string +} + +var testCases []testCase + +func addTestCases(t []testCase, fn func(*ast.File) bool) { + // Fill in fn to avoid repetition in definitions. + if fn != nil { + for i := range t { + if t[i].Fn == nil { + t[i].Fn = fn + } + } + } + testCases = append(testCases, t...) +} + +func fnop(*ast.File) bool { return false } + +func parseFixPrint(t *testing.T, fn func(*ast.File) bool, desc, in string, mustBeGofmt bool) (out string, fixed, ok bool) { + file, err := parser.ParseFile(fset, desc, in, parserMode) + if err != nil { + t.Errorf("%s: parsing: %v", desc, err) + return + } + + outb, err := gofmtFile(file) + if err != nil { + t.Errorf("%s: printing: %v", desc, err) + return + } + if s := string(outb); in != s && mustBeGofmt { + t.Errorf("%s: not gofmt-formatted.\n--- %s\n%s\n--- %s | gofmt\n%s", + desc, desc, in, desc, s) + tdiff(t, in, s) + return + } + + if fn == nil { + for _, fix := range fixes { + if fix.f(file) { + fixed = true + } + } + } else { + fixed = fn(file) + } + + outb, err = gofmtFile(file) + if err != nil { + t.Errorf("%s: printing: %v", desc, err) + return + } + + return string(outb), fixed, true +} + +func TestRewrite(t *testing.T) { + for _, tt := range testCases { + // Apply fix: should get tt.Out. + out, fixed, ok := parseFixPrint(t, tt.Fn, tt.Name, tt.In, true) + if !ok { + continue + } + + // reformat to get printing right + out, _, ok = parseFixPrint(t, fnop, tt.Name, out, false) + if !ok { + continue + } + + if out != tt.Out { + t.Errorf("%s: incorrect output.\n", tt.Name) + if !strings.HasPrefix(tt.Name, "testdata/") { + t.Errorf("--- have\n%s\n--- want\n%s", out, tt.Out) + } + tdiff(t, out, tt.Out) + continue + } + + if changed := out != tt.In; changed != fixed { + t.Errorf("%s: changed=%v != fixed=%v", tt.Name, changed, fixed) + continue + } + + // Should not change if run again. + out2, fixed2, ok := parseFixPrint(t, tt.Fn, tt.Name+" output", out, true) + if !ok { + continue + } + + if fixed2 { + t.Errorf("%s: applied fixes during second round", tt.Name) + continue + } + + if out2 != out { + t.Errorf("%s: changed output after second round of fixes.\n--- output after first round\n%s\n--- output after second round\n%s", + tt.Name, out, out2) + tdiff(t, out, out2) + } + } +} + +func tdiff(t *testing.T, a, b string) { + data, err := diff([]byte(a), []byte(b)) + if err != nil { + t.Error(err) + return + } + t.Error(string(data)) +} diff --git a/src/cmd/fix/mapdelete.go b/src/cmd/fix/mapdelete.go new file mode 100644 index 000000000..db89c7bf4 --- /dev/null +++ b/src/cmd/fix/mapdelete.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. + +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 new file mode 100644 index 000000000..8ed50328e --- /dev/null +++ b/src/cmd/fix/mapdelete_test.go @@ -0,0 +1,43 @@ +// 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 new file mode 100644 index 000000000..2ec837eb0 --- /dev/null +++ b/src/cmd/fix/math.go @@ -0,0 +1,51 @@ +// 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 new file mode 100644 index 000000000..b8d69d2f2 --- /dev/null +++ b/src/cmd/fix/math_test.go @@ -0,0 +1,47 @@ +// 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 new file mode 100644 index 000000000..2de994cff --- /dev/null +++ b/src/cmd/fix/netdial.go @@ -0,0 +1,117 @@ +// 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 new file mode 100644 index 000000000..fff00b4ad --- /dev/null +++ b/src/cmd/fix/netdial_test.go @@ -0,0 +1,57 @@ +// 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/netudpgroup.go b/src/cmd/fix/netudpgroup.go new file mode 100644 index 000000000..b54beb0de --- /dev/null +++ b/src/cmd/fix/netudpgroup.go @@ -0,0 +1,58 @@ +// 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 new file mode 100644 index 000000000..88c0e093f --- /dev/null +++ b/src/cmd/fix/netudpgroup_test.go @@ -0,0 +1,53 @@ +// 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 new file mode 100644 index 000000000..4befe24fb --- /dev/null +++ b/src/cmd/fix/newwriter.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 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 new file mode 100644 index 000000000..1f59628a0 --- /dev/null +++ b/src/cmd/fix/newwriter_test.go @@ -0,0 +1,83 @@ +// 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 new file mode 100644 index 000000000..a75a2c12d --- /dev/null +++ b/src/cmd/fix/oserrorstring.go @@ -0,0 +1,75 @@ +// 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 new file mode 100644 index 000000000..75551480c --- /dev/null +++ b/src/cmd/fix/oserrorstring_test.go @@ -0,0 +1,57 @@ +// 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 new file mode 100644 index 000000000..af2796ac2 --- /dev/null +++ b/src/cmd/fix/osopen.go @@ -0,0 +1,124 @@ +// 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 new file mode 100644 index 000000000..5797adb7b --- /dev/null +++ b/src/cmd/fix/osopen_test.go @@ -0,0 +1,82 @@ +// 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/procattr.go b/src/cmd/fix/procattr.go new file mode 100644 index 000000000..ea375ec9d --- /dev/null +++ b/src/cmd/fix/procattr.go @@ -0,0 +1,62 @@ +// 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 new file mode 100644 index 000000000..9e2b86e74 --- /dev/null +++ b/src/cmd/fix/procattr_test.go @@ -0,0 +1,74 @@ +// 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 new file mode 100644 index 000000000..151da569d --- /dev/null +++ b/src/cmd/fix/reflect.go @@ -0,0 +1,862 @@ +// 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 new file mode 100644 index 000000000..032cbc745 --- /dev/null +++ b/src/cmd/fix/reflect_test.go @@ -0,0 +1,35 @@ +// 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 new file mode 100644 index 000000000..5a583d41e --- /dev/null +++ b/src/cmd/fix/signal.go @@ -0,0 +1,50 @@ +// 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 new file mode 100644 index 000000000..7bca7d5c4 --- /dev/null +++ b/src/cmd/fix/signal_test.go @@ -0,0 +1,94 @@ +// 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 new file mode 100644 index 000000000..fa549313e --- /dev/null +++ b/src/cmd/fix/sorthelpers.go @@ -0,0 +1,49 @@ +// 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 new file mode 100644 index 000000000..dd6b58e03 --- /dev/null +++ b/src/cmd/fix/sorthelpers_test.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. + +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 new file mode 100644 index 000000000..89267b847 --- /dev/null +++ b/src/cmd/fix/sortslice.go @@ -0,0 +1,52 @@ +// 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 new file mode 100644 index 000000000..7b745a232 --- /dev/null +++ b/src/cmd/fix/sortslice_test.go @@ -0,0 +1,35 @@ +// 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 new file mode 100644 index 000000000..6cd69020b --- /dev/null +++ b/src/cmd/fix/strconv.go @@ -0,0 +1,127 @@ +// 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 new file mode 100644 index 000000000..7fbd4e42e --- /dev/null +++ b/src/cmd/fix/strconv_test.go @@ -0,0 +1,93 @@ +// 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 new file mode 100644 index 000000000..d89ecf039 --- /dev/null +++ b/src/cmd/fix/stringssplit.go @@ -0,0 +1,72 @@ +// 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 new file mode 100644 index 000000000..fa42b1bea --- /dev/null +++ b/src/cmd/fix/stringssplit_test.go @@ -0,0 +1,51 @@ +// 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 new file mode 100644 index 000000000..a3dd1440b --- /dev/null +++ b/src/cmd/fix/template.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. + +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 new file mode 100644 index 000000000..f713a2901 --- /dev/null +++ b/src/cmd/fix/template_test.go @@ -0,0 +1,55 @@ +// 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.asn1.go.in b/src/cmd/fix/testdata/reflect.asn1.go.in new file mode 100644 index 000000000..43128f6b2 --- /dev/null +++ b/src/cmd/fix/testdata/reflect.asn1.go.in @@ -0,0 +1,814 @@ +// 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. + +// The asn1 package implements parsing of DER-encoded ASN.1 data structures, +// as defined in ITU-T Rec X.690. +// +// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' +// http://luca.ntop.org/Teaching/Appunti/asn1.html. +package asn1 + +// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc +// are different encoding formats for those objects. Here, we'll be dealing +// with DER, the Distinguished Encoding Rules. DER is used in X.509 because +// it's fast to parse and, unlike BER, has a unique encoding for every object. +// When calculating hashes over objects, it's important that the resulting +// bytes be the same at both ends and DER removes this margin of error. +// +// ASN.1 is very complex and this package doesn't attempt to implement +// everything by any means. + +import ( + "fmt" + "os" + "reflect" + "time" +) + +// A StructuralError suggests that the ASN.1 data is valid, but the Go type +// which is receiving it doesn't match. +type StructuralError struct { + Msg string +} + +func (e StructuralError) String() string { return "ASN.1 structure error: " + e.Msg } + +// A SyntaxError suggests that the ASN.1 data is invalid. +type SyntaxError struct { + Msg string +} + +func (e SyntaxError) String() string { return "ASN.1 syntax error: " + e.Msg } + +// We start by dealing with each of the primitive types in turn. + +// BOOLEAN + +func parseBool(bytes []byte) (ret bool, err os.Error) { + if len(bytes) != 1 { + err = SyntaxError{"invalid boolean"} + return + } + + return bytes[0] != 0, nil +} + +// INTEGER + +// parseInt64 treats the given bytes as a big-endian, signed integer and +// returns the result. +func parseInt64(bytes []byte) (ret int64, err os.Error) { + if len(bytes) > 8 { + // We'll overflow an int64 in this case. + err = StructuralError{"integer too large"} + return + } + for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { + ret <<= 8 + ret |= int64(bytes[bytesRead]) + } + + // Shift up and down in order to sign extend the result. + ret <<= 64 - uint8(len(bytes))*8 + ret >>= 64 - uint8(len(bytes))*8 + return +} + +// parseInt treats the given bytes as a big-endian, signed integer and returns +// the result. +func parseInt(bytes []byte) (int, os.Error) { + ret64, err := parseInt64(bytes) + if err != nil { + return 0, err + } + if ret64 != int64(int(ret64)) { + return 0, StructuralError{"integer too large"} + } + return int(ret64), nil +} + +// BIT STRING + +// BitString is the structure to use when you want an ASN.1 BIT STRING type. A +// bit string is padded up to the nearest byte in memory and the number of +// valid bits is recorded. Padding bits will be zero. +type BitString struct { + Bytes []byte // bits packed into bytes. + BitLength int // length in bits. +} + +// At returns the bit at the given index. If the index is out of range it +// returns false. +func (b BitString) At(i int) int { + if i < 0 || i >= b.BitLength { + return 0 + } + x := i / 8 + y := 7 - uint(i%8) + return int(b.Bytes[x]>>y) & 1 +} + +// RightAlign returns a slice where the padding bits are at the beginning. The +// slice may share memory with the BitString. +func (b BitString) RightAlign() []byte { + shift := uint(8 - (b.BitLength % 8)) + if shift == 8 || len(b.Bytes) == 0 { + return b.Bytes + } + + a := make([]byte, len(b.Bytes)) + a[0] = b.Bytes[0] >> shift + for i := 1; i < len(b.Bytes); i++ { + a[i] = b.Bytes[i-1] << (8 - shift) + a[i] |= b.Bytes[i] >> shift + } + + return a +} + +// parseBitString parses an ASN.1 bit string from the given byte array and returns it. +func parseBitString(bytes []byte) (ret BitString, err os.Error) { + if len(bytes) == 0 { + err = SyntaxError{"zero length BIT STRING"} + return + } + paddingBits := int(bytes[0]) + if paddingBits > 7 || + len(bytes) == 1 && paddingBits > 0 || + bytes[len(bytes)-1]&((1< 4 { + err = StructuralError{"base 128 integer too large"} + return + } + ret <<= 7 + b := bytes[offset] + ret |= int(b & 0x7f) + offset++ + if b&0x80 == 0 { + return + } + } + err = SyntaxError{"truncated base 128 integer"} + return +} + +// UTCTime + +func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) { + s := string(bytes) + ret, err = time.Parse("0601021504Z0700", s) + if err == nil { + return + } + ret, err = time.Parse("060102150405Z0700", s) + return +} + +// parseGeneralizedTime parses the GeneralizedTime from the given byte array +// and returns the resulting time. +func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) { + return time.Parse("20060102150405Z0700", string(bytes)) +} + +// PrintableString + +// parsePrintableString parses a ASN.1 PrintableString from the given byte +// array and returns it. +func parsePrintableString(bytes []byte) (ret string, err os.Error) { + for _, b := range bytes { + if !isPrintable(b) { + err = SyntaxError{"PrintableString contains invalid character"} + return + } + } + ret = string(bytes) + return +} + +// isPrintable returns true iff the given b is in the ASN.1 PrintableString set. +func isPrintable(b byte) bool { + return 'a' <= b && b <= 'z' || + 'A' <= b && b <= 'Z' || + '0' <= b && b <= '9' || + '\'' <= b && b <= ')' || + '+' <= b && b <= '/' || + b == ' ' || + b == ':' || + b == '=' || + b == '?' || + // This is techincally not allowed in a PrintableString. + // However, x509 certificates with wildcard strings don't + // always use the correct string type so we permit it. + b == '*' +} + +// IA5String + +// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given +// byte array and returns it. +func parseIA5String(bytes []byte) (ret string, err os.Error) { + for _, b := range bytes { + if b >= 0x80 { + err = SyntaxError{"IA5String contains invalid character"} + return + } + } + ret = string(bytes) + return +} + +// T61String + +// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given +// byte array and returns it. +func parseT61String(bytes []byte) (ret string, err os.Error) { + return string(bytes), nil +} + +// A RawValue represents an undecoded ASN.1 object. +type RawValue struct { + Class, Tag int + IsCompound bool + Bytes []byte + FullBytes []byte // includes the tag and length +} + +// RawContent is used to signal that the undecoded, DER data needs to be +// preserved for a struct. To use it, the first field of the struct must have +// this type. It's an error for any of the other fields to have this type. +type RawContent []byte + +// Tagging + +// parseTagAndLength parses an ASN.1 tag and length pair from the given offset +// into a byte array. It returns the parsed data and the new offset. SET and +// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we +// don't distinguish between ordered and unordered objects in this code. +func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err os.Error) { + offset = initOffset + b := bytes[offset] + offset++ + ret.class = int(b >> 6) + ret.isCompound = b&0x20 == 0x20 + ret.tag = int(b & 0x1f) + + // If the bottom five bits are set, then the tag number is actually base 128 + // encoded afterwards + if ret.tag == 0x1f { + ret.tag, offset, err = parseBase128Int(bytes, offset) + if err != nil { + return + } + } + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + if b&0x80 == 0 { + // The length is encoded in the bottom 7 bits. + ret.length = int(b & 0x7f) + } else { + // Bottom 7 bits give the number of length bytes to follow. + numBytes := int(b & 0x7f) + // We risk overflowing a signed 32-bit number if we accept more than 3 bytes. + if numBytes > 3 { + err = StructuralError{"length too large"} + return + } + if numBytes == 0 { + err = SyntaxError{"indefinite length found (not DER)"} + return + } + ret.length = 0 + for i := 0; i < numBytes; i++ { + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + ret.length <<= 8 + ret.length |= int(b) + } + } + + return +} + +// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse +// a number of ASN.1 values from the given byte array and returns them as a +// slice of Go values of the given type. +func parseSequenceOf(bytes []byte, sliceType *reflect.SliceType, elemType reflect.Type) (ret *reflect.SliceValue, err os.Error) { + expectedTag, compoundType, ok := getUniversalType(elemType) + if !ok { + err = StructuralError{"unknown Go type for slice"} + return + } + + // First we iterate over the input and count the number of elements, + // checking that the types are correct in each case. + numElements := 0 + for offset := 0; offset < len(bytes); { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so + // that a sequence of them can be parsed into a []string. + if t.tag == tagGeneralString { + t.tag = tagPrintableString + } + if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag { + err = StructuralError{"sequence tag mismatch"} + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"truncated sequence"} + return + } + offset += t.length + numElements++ + } + ret = reflect.MakeSlice(sliceType, numElements, numElements) + params := fieldParameters{} + offset := 0 + for i := 0; i < numElements; i++ { + offset, err = parseField(ret.Elem(i), bytes, offset, params) + if err != nil { + return + } + } + return +} + +var ( + bitStringType = reflect.Typeof(BitString{}) + objectIdentifierType = reflect.Typeof(ObjectIdentifier{}) + enumeratedType = reflect.Typeof(Enumerated(0)) + flagType = reflect.Typeof(Flag(false)) + timeType = reflect.Typeof(&time.Time{}) + rawValueType = reflect.Typeof(RawValue{}) + rawContentsType = reflect.Typeof(RawContent(nil)) +) + +// invalidLength returns true iff offset + length > sliceLength, or if the +// addition would overflow. +func invalidLength(offset, length, sliceLength int) bool { + return offset+length < offset || offset+length > sliceLength +} + +// parseField is the main parsing function. Given a byte array and an offset +// into the array, it will try to parse a suitable ASN.1 value out and store it +// in the given Value. +func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err os.Error) { + offset = initOffset + fieldType := v.Type() + + // If we have run out of data, it may be that there are optional elements at the end. + if offset == len(bytes) { + if !setDefaultValue(v, params) { + err = SyntaxError{"sequence truncated"} + } + return + } + + // Deal with raw values. + if fieldType == rawValueType { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} + offset += t.length + v.(*reflect.StructValue).Set(reflect.NewValue(result).(*reflect.StructValue)) + return + } + + // Deal with the ANY type. + if ifaceType, ok := fieldType.(*reflect.InterfaceType); ok && ifaceType.NumMethod() == 0 { + ifaceValue := v.(*reflect.InterfaceValue) + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + var result interface{} + if !t.isCompound && t.class == classUniversal { + innerBytes := bytes[offset : offset+t.length] + switch t.tag { + case tagPrintableString: + result, err = parsePrintableString(innerBytes) + case tagIA5String: + result, err = parseIA5String(innerBytes) + case tagT61String: + result, err = parseT61String(innerBytes) + case tagInteger: + result, err = parseInt64(innerBytes) + case tagBitString: + result, err = parseBitString(innerBytes) + case tagOID: + result, err = parseObjectIdentifier(innerBytes) + case tagUTCTime: + result, err = parseUTCTime(innerBytes) + case tagOctetString: + result = innerBytes + default: + // If we don't know how to handle the type, we just leave Value as nil. + } + } + offset += t.length + if err != nil { + return + } + if result != nil { + ifaceValue.Set(reflect.NewValue(result)) + } + return + } + universalTag, compoundType, ok1 := getUniversalType(fieldType) + if !ok1 { + err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)} + return + } + + t, offset, err := parseTagAndLength(bytes, offset) + if err != nil { + return + } + if params.explicit { + expectedClass := classContextSpecific + if params.application { + expectedClass = classApplication + } + if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) { + if t.length > 0 { + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + } else { + if fieldType != flagType { + err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} + return + } + + flagValue := v.(*reflect.BoolValue) + flagValue.Set(true) + return + } + } else { + // The tags didn't match, it might be an optional element. + ok := setDefaultValue(v, params) + if ok { + offset = initOffset + } else { + err = StructuralError{"explicitly tagged member didn't match"} + } + return + } + } + + // Special case for strings: PrintableString and IA5String both map to + // the Go type string. getUniversalType returns the tag for + // PrintableString when it sees a string so, if we see an IA5String on + // the wire, we change the universal type to match. + if universalTag == tagPrintableString && t.tag == tagIA5String { + universalTag = tagIA5String + } + // Likewise for GeneralString + if universalTag == tagPrintableString && t.tag == tagGeneralString { + universalTag = tagGeneralString + } + + // Special case for time: UTCTime and GeneralizedTime both map to the + // Go type time.Time. + if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { + universalTag = tagGeneralizedTime + } + + expectedClass := classUniversal + expectedTag := universalTag + + if !params.explicit && params.tag != nil { + expectedClass = classContextSpecific + expectedTag = *params.tag + } + + if !params.explicit && params.application && params.tag != nil { + expectedClass = classApplication + expectedTag = *params.tag + } + + // We have unwrapped any explicit tagging at this point. + if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType { + // Tags don't match. Again, it could be an optional element. + ok := setDefaultValue(v, params) + if ok { + offset = initOffset + } else { + err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)} + } + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + innerBytes := bytes[offset : offset+t.length] + offset += t.length + + // We deal with the structures defined in this package first. + switch fieldType { + case objectIdentifierType: + newSlice, err1 := parseObjectIdentifier(innerBytes) + sliceValue := v.(*reflect.SliceValue) + sliceValue.Set(reflect.MakeSlice(sliceValue.Type().(*reflect.SliceType), len(newSlice), len(newSlice))) + if err1 == nil { + reflect.Copy(sliceValue, reflect.NewValue(newSlice).(reflect.ArrayOrSliceValue)) + } + err = err1 + return + case bitStringType: + structValue := v.(*reflect.StructValue) + bs, err1 := parseBitString(innerBytes) + if err1 == nil { + structValue.Set(reflect.NewValue(bs).(*reflect.StructValue)) + } + err = err1 + return + case timeType: + ptrValue := v.(*reflect.PtrValue) + var time *time.Time + var err1 os.Error + if universalTag == tagUTCTime { + time, err1 = parseUTCTime(innerBytes) + } else { + time, err1 = parseGeneralizedTime(innerBytes) + } + if err1 == nil { + ptrValue.Set(reflect.NewValue(time).(*reflect.PtrValue)) + } + err = err1 + return + case enumeratedType: + parsedInt, err1 := parseInt(innerBytes) + enumValue := v.(*reflect.IntValue) + if err1 == nil { + enumValue.Set(int64(parsedInt)) + } + err = err1 + return + case flagType: + flagValue := v.(*reflect.BoolValue) + flagValue.Set(true) + return + } + switch val := v.(type) { + case *reflect.BoolValue: + parsedBool, err1 := parseBool(innerBytes) + if err1 == nil { + val.Set(parsedBool) + } + err = err1 + return + case *reflect.IntValue: + switch val.Type().Kind() { + case reflect.Int: + parsedInt, err1 := parseInt(innerBytes) + if err1 == nil { + val.Set(int64(parsedInt)) + } + err = err1 + return + case reflect.Int64: + parsedInt, err1 := parseInt64(innerBytes) + if err1 == nil { + val.Set(parsedInt) + } + err = err1 + return + } + case *reflect.StructValue: + structType := fieldType.(*reflect.StructType) + + if structType.NumField() > 0 && + structType.Field(0).Type == rawContentsType { + bytes := bytes[initOffset:offset] + val.Field(0).SetValue(reflect.NewValue(RawContent(bytes))) + } + + innerOffset := 0 + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if i == 0 && field.Type == rawContentsType { + continue + } + innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag)) + if err != nil { + return + } + } + // We allow extra bytes at the end of the SEQUENCE because + // adding elements to the end has been used in X.509 as the + // version numbers have increased. + return + case *reflect.SliceValue: + sliceType := fieldType.(*reflect.SliceType) + if sliceType.Elem().Kind() == reflect.Uint8 { + val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes))) + reflect.Copy(val, reflect.NewValue(innerBytes).(reflect.ArrayOrSliceValue)) + return + } + newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) + if err1 == nil { + val.Set(newSlice) + } + err = err1 + return + case *reflect.StringValue: + var v string + switch universalTag { + case tagPrintableString: + v, err = parsePrintableString(innerBytes) + case tagIA5String: + v, err = parseIA5String(innerBytes) + case tagT61String: + v, err = parseT61String(innerBytes) + case tagGeneralString: + // GeneralString is specified in ISO-2022/ECMA-35, + // A brief review suggests that it includes structures + // that allow the encoding to change midstring and + // such. We give up and pass it as an 8-bit string. + v, err = parseT61String(innerBytes) + default: + err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} + } + if err == nil { + val.Set(v) + } + return + } + err = StructuralError{"unknown Go type"} + return +} + +// setDefaultValue is used to install a default value, from a tag string, into +// a Value. It is successful is the field was optional, even if a default value +// wasn't provided or it failed to install it into the Value. +func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { + if !params.optional { + return + } + ok = true + if params.defaultValue == nil { + return + } + switch val := v.(type) { + case *reflect.IntValue: + val.Set(*params.defaultValue) + } + return +} + +// Unmarshal parses the DER-encoded ASN.1 data structure b +// and uses the reflect package to fill in an arbitrary value pointed at by val. +// Because Unmarshal uses the reflect package, the structs +// being written to must use upper case field names. +// +// An ASN.1 INTEGER can be written to an int or int64. +// If the encoded value does not fit in the Go type, +// Unmarshal returns a parse error. +// +// An ASN.1 BIT STRING can be written to a BitString. +// +// An ASN.1 OCTET STRING can be written to a []byte. +// +// An ASN.1 OBJECT IDENTIFIER can be written to an +// ObjectIdentifier. +// +// An ASN.1 ENUMERATED can be written to an Enumerated. +// +// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time. +// +// An ASN.1 PrintableString or IA5String can be written to a string. +// +// Any of the above ASN.1 values can be written to an interface{}. +// The value stored in the interface has the corresponding Go type. +// For integers, that type is int64. +// +// An ASN.1 SEQUENCE OF x or SET OF x can be written +// to a slice if an x can be written to the slice's element type. +// +// An ASN.1 SEQUENCE or SET can be written to a struct +// if each of the elements in the sequence can be +// written to the corresponding element in the struct. +// +// The following tags on struct fields have special meaning to Unmarshal: +// +// optional marks the field as ASN.1 OPTIONAL +// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC +// default:x sets the default value for optional integer fields +// +// If the type of the first field of a structure is RawContent then the raw +// ASN1 contents of the struct will be stored in it. +// +// Other ASN.1 types are not supported; if it encounters them, +// Unmarshal returns a parse error. +func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) { + return UnmarshalWithParams(b, val, "") +} + +// UnmarshalWithParams allows field parameters to be specified for the +// top-level element. The form of the params is the same as the field tags. +func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err os.Error) { + v := reflect.NewValue(val).(*reflect.PtrValue).Elem() + offset, err := parseField(v, b, 0, parseFieldParameters(params)) + if err != nil { + return nil, err + } + return b[offset:], nil +} diff --git a/src/cmd/fix/testdata/reflect.asn1.go.out b/src/cmd/fix/testdata/reflect.asn1.go.out new file mode 100644 index 000000000..ba6224e6d --- /dev/null +++ b/src/cmd/fix/testdata/reflect.asn1.go.out @@ -0,0 +1,814 @@ +// 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. + +// The asn1 package implements parsing of DER-encoded ASN.1 data structures, +// as defined in ITU-T Rec X.690. +// +// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' +// http://luca.ntop.org/Teaching/Appunti/asn1.html. +package asn1 + +// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc +// are different encoding formats for those objects. Here, we'll be dealing +// with DER, the Distinguished Encoding Rules. DER is used in X.509 because +// it's fast to parse and, unlike BER, has a unique encoding for every object. +// When calculating hashes over objects, it's important that the resulting +// bytes be the same at both ends and DER removes this margin of error. +// +// ASN.1 is very complex and this package doesn't attempt to implement +// everything by any means. + +import ( + "fmt" + "os" + "reflect" + "time" +) + +// A StructuralError suggests that the ASN.1 data is valid, but the Go type +// which is receiving it doesn't match. +type StructuralError struct { + Msg string +} + +func (e StructuralError) String() string { return "ASN.1 structure error: " + e.Msg } + +// A SyntaxError suggests that the ASN.1 data is invalid. +type SyntaxError struct { + Msg string +} + +func (e SyntaxError) String() string { return "ASN.1 syntax error: " + e.Msg } + +// We start by dealing with each of the primitive types in turn. + +// BOOLEAN + +func parseBool(bytes []byte) (ret bool, err os.Error) { + if len(bytes) != 1 { + err = SyntaxError{"invalid boolean"} + return + } + + return bytes[0] != 0, nil +} + +// INTEGER + +// parseInt64 treats the given bytes as a big-endian, signed integer and +// returns the result. +func parseInt64(bytes []byte) (ret int64, err os.Error) { + if len(bytes) > 8 { + // We'll overflow an int64 in this case. + err = StructuralError{"integer too large"} + return + } + for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { + ret <<= 8 + ret |= int64(bytes[bytesRead]) + } + + // Shift up and down in order to sign extend the result. + ret <<= 64 - uint8(len(bytes))*8 + ret >>= 64 - uint8(len(bytes))*8 + return +} + +// parseInt treats the given bytes as a big-endian, signed integer and returns +// the result. +func parseInt(bytes []byte) (int, os.Error) { + ret64, err := parseInt64(bytes) + if err != nil { + return 0, err + } + if ret64 != int64(int(ret64)) { + return 0, StructuralError{"integer too large"} + } + return int(ret64), nil +} + +// BIT STRING + +// BitString is the structure to use when you want an ASN.1 BIT STRING type. A +// bit string is padded up to the nearest byte in memory and the number of +// valid bits is recorded. Padding bits will be zero. +type BitString struct { + Bytes []byte // bits packed into bytes. + BitLength int // length in bits. +} + +// At returns the bit at the given index. If the index is out of range it +// returns false. +func (b BitString) At(i int) int { + if i < 0 || i >= b.BitLength { + return 0 + } + x := i / 8 + y := 7 - uint(i%8) + return int(b.Bytes[x]>>y) & 1 +} + +// RightAlign returns a slice where the padding bits are at the beginning. The +// slice may share memory with the BitString. +func (b BitString) RightAlign() []byte { + shift := uint(8 - (b.BitLength % 8)) + if shift == 8 || len(b.Bytes) == 0 { + return b.Bytes + } + + a := make([]byte, len(b.Bytes)) + a[0] = b.Bytes[0] >> shift + for i := 1; i < len(b.Bytes); i++ { + a[i] = b.Bytes[i-1] << (8 - shift) + a[i] |= b.Bytes[i] >> shift + } + + return a +} + +// parseBitString parses an ASN.1 bit string from the given byte array and returns it. +func parseBitString(bytes []byte) (ret BitString, err os.Error) { + if len(bytes) == 0 { + err = SyntaxError{"zero length BIT STRING"} + return + } + paddingBits := int(bytes[0]) + if paddingBits > 7 || + len(bytes) == 1 && paddingBits > 0 || + bytes[len(bytes)-1]&((1< 4 { + err = StructuralError{"base 128 integer too large"} + return + } + ret <<= 7 + b := bytes[offset] + ret |= int(b & 0x7f) + offset++ + if b&0x80 == 0 { + return + } + } + err = SyntaxError{"truncated base 128 integer"} + return +} + +// UTCTime + +func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) { + s := string(bytes) + ret, err = time.Parse("0601021504Z0700", s) + if err == nil { + return + } + ret, err = time.Parse("060102150405Z0700", s) + return +} + +// parseGeneralizedTime parses the GeneralizedTime from the given byte array +// and returns the resulting time. +func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) { + return time.Parse("20060102150405Z0700", string(bytes)) +} + +// PrintableString + +// parsePrintableString parses a ASN.1 PrintableString from the given byte +// array and returns it. +func parsePrintableString(bytes []byte) (ret string, err os.Error) { + for _, b := range bytes { + if !isPrintable(b) { + err = SyntaxError{"PrintableString contains invalid character"} + return + } + } + ret = string(bytes) + return +} + +// isPrintable returns true iff the given b is in the ASN.1 PrintableString set. +func isPrintable(b byte) bool { + return 'a' <= b && b <= 'z' || + 'A' <= b && b <= 'Z' || + '0' <= b && b <= '9' || + '\'' <= b && b <= ')' || + '+' <= b && b <= '/' || + b == ' ' || + b == ':' || + b == '=' || + b == '?' || + // This is techincally not allowed in a PrintableString. + // However, x509 certificates with wildcard strings don't + // always use the correct string type so we permit it. + b == '*' +} + +// IA5String + +// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given +// byte array and returns it. +func parseIA5String(bytes []byte) (ret string, err os.Error) { + for _, b := range bytes { + if b >= 0x80 { + err = SyntaxError{"IA5String contains invalid character"} + return + } + } + ret = string(bytes) + return +} + +// T61String + +// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given +// byte array and returns it. +func parseT61String(bytes []byte) (ret string, err os.Error) { + return string(bytes), nil +} + +// A RawValue represents an undecoded ASN.1 object. +type RawValue struct { + Class, Tag int + IsCompound bool + Bytes []byte + FullBytes []byte // includes the tag and length +} + +// RawContent is used to signal that the undecoded, DER data needs to be +// preserved for a struct. To use it, the first field of the struct must have +// this type. It's an error for any of the other fields to have this type. +type RawContent []byte + +// Tagging + +// parseTagAndLength parses an ASN.1 tag and length pair from the given offset +// into a byte array. It returns the parsed data and the new offset. SET and +// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we +// don't distinguish between ordered and unordered objects in this code. +func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err os.Error) { + offset = initOffset + b := bytes[offset] + offset++ + ret.class = int(b >> 6) + ret.isCompound = b&0x20 == 0x20 + ret.tag = int(b & 0x1f) + + // If the bottom five bits are set, then the tag number is actually base 128 + // encoded afterwards + if ret.tag == 0x1f { + ret.tag, offset, err = parseBase128Int(bytes, offset) + if err != nil { + return + } + } + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + if b&0x80 == 0 { + // The length is encoded in the bottom 7 bits. + ret.length = int(b & 0x7f) + } else { + // Bottom 7 bits give the number of length bytes to follow. + numBytes := int(b & 0x7f) + // We risk overflowing a signed 32-bit number if we accept more than 3 bytes. + if numBytes > 3 { + err = StructuralError{"length too large"} + return + } + if numBytes == 0 { + err = SyntaxError{"indefinite length found (not DER)"} + return + } + ret.length = 0 + for i := 0; i < numBytes; i++ { + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + ret.length <<= 8 + ret.length |= int(b) + } + } + + return +} + +// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse +// a number of ASN.1 values from the given byte array and returns them as a +// slice of Go values of the given type. +func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err os.Error) { + expectedTag, compoundType, ok := getUniversalType(elemType) + if !ok { + err = StructuralError{"unknown Go type for slice"} + return + } + + // First we iterate over the input and count the number of elements, + // checking that the types are correct in each case. + numElements := 0 + for offset := 0; offset < len(bytes); { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so + // that a sequence of them can be parsed into a []string. + if t.tag == tagGeneralString { + t.tag = tagPrintableString + } + if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag { + err = StructuralError{"sequence tag mismatch"} + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"truncated sequence"} + return + } + offset += t.length + numElements++ + } + ret = reflect.MakeSlice(sliceType, numElements, numElements) + params := fieldParameters{} + offset := 0 + for i := 0; i < numElements; i++ { + offset, err = parseField(ret.Index(i), bytes, offset, params) + if err != nil { + return + } + } + return +} + +var ( + bitStringType = reflect.TypeOf(BitString{}) + objectIdentifierType = reflect.TypeOf(ObjectIdentifier{}) + enumeratedType = reflect.TypeOf(Enumerated(0)) + flagType = reflect.TypeOf(Flag(false)) + timeType = reflect.TypeOf(&time.Time{}) + rawValueType = reflect.TypeOf(RawValue{}) + rawContentsType = reflect.TypeOf(RawContent(nil)) +) + +// invalidLength returns true iff offset + length > sliceLength, or if the +// addition would overflow. +func invalidLength(offset, length, sliceLength int) bool { + return offset+length < offset || offset+length > sliceLength +} + +// parseField is the main parsing function. Given a byte array and an offset +// into the array, it will try to parse a suitable ASN.1 value out and store it +// in the given Value. +func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err os.Error) { + offset = initOffset + fieldType := v.Type() + + // If we have run out of data, it may be that there are optional elements at the end. + if offset == len(bytes) { + if !setDefaultValue(v, params) { + err = SyntaxError{"sequence truncated"} + } + return + } + + // Deal with raw values. + if fieldType == rawValueType { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} + offset += t.length + v.Set(reflect.ValueOf(result)) + return + } + + // Deal with the ANY type. + if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 { + ifaceValue := v + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + var result interface{} + if !t.isCompound && t.class == classUniversal { + innerBytes := bytes[offset : offset+t.length] + switch t.tag { + case tagPrintableString: + result, err = parsePrintableString(innerBytes) + case tagIA5String: + result, err = parseIA5String(innerBytes) + case tagT61String: + result, err = parseT61String(innerBytes) + case tagInteger: + result, err = parseInt64(innerBytes) + case tagBitString: + result, err = parseBitString(innerBytes) + case tagOID: + result, err = parseObjectIdentifier(innerBytes) + case tagUTCTime: + result, err = parseUTCTime(innerBytes) + case tagOctetString: + result = innerBytes + default: + // If we don't know how to handle the type, we just leave Value as nil. + } + } + offset += t.length + if err != nil { + return + } + if result != nil { + ifaceValue.Set(reflect.ValueOf(result)) + } + return + } + universalTag, compoundType, ok1 := getUniversalType(fieldType) + if !ok1 { + err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)} + return + } + + t, offset, err := parseTagAndLength(bytes, offset) + if err != nil { + return + } + if params.explicit { + expectedClass := classContextSpecific + if params.application { + expectedClass = classApplication + } + if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) { + if t.length > 0 { + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + } else { + if fieldType != flagType { + err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} + return + } + + flagValue := v + flagValue.SetBool(true) + return + } + } else { + // The tags didn't match, it might be an optional element. + ok := setDefaultValue(v, params) + if ok { + offset = initOffset + } else { + err = StructuralError{"explicitly tagged member didn't match"} + } + return + } + } + + // Special case for strings: PrintableString and IA5String both map to + // the Go type string. getUniversalType returns the tag for + // PrintableString when it sees a string so, if we see an IA5String on + // the wire, we change the universal type to match. + if universalTag == tagPrintableString && t.tag == tagIA5String { + universalTag = tagIA5String + } + // Likewise for GeneralString + if universalTag == tagPrintableString && t.tag == tagGeneralString { + universalTag = tagGeneralString + } + + // Special case for time: UTCTime and GeneralizedTime both map to the + // Go type time.Time. + if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { + universalTag = tagGeneralizedTime + } + + expectedClass := classUniversal + expectedTag := universalTag + + if !params.explicit && params.tag != nil { + expectedClass = classContextSpecific + expectedTag = *params.tag + } + + if !params.explicit && params.application && params.tag != nil { + expectedClass = classApplication + expectedTag = *params.tag + } + + // We have unwrapped any explicit tagging at this point. + if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType { + // Tags don't match. Again, it could be an optional element. + ok := setDefaultValue(v, params) + if ok { + offset = initOffset + } else { + err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)} + } + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + innerBytes := bytes[offset : offset+t.length] + offset += t.length + + // We deal with the structures defined in this package first. + switch fieldType { + case objectIdentifierType: + newSlice, err1 := parseObjectIdentifier(innerBytes) + sliceValue := v + sliceValue.Set(reflect.MakeSlice(sliceValue.Type(), len(newSlice), len(newSlice))) + if err1 == nil { + reflect.Copy(sliceValue, reflect.ValueOf(newSlice)) + } + err = err1 + return + case bitStringType: + structValue := v + bs, err1 := parseBitString(innerBytes) + if err1 == nil { + structValue.Set(reflect.ValueOf(bs)) + } + err = err1 + return + case timeType: + ptrValue := v + var time *time.Time + var err1 os.Error + if universalTag == tagUTCTime { + time, err1 = parseUTCTime(innerBytes) + } else { + time, err1 = parseGeneralizedTime(innerBytes) + } + if err1 == nil { + ptrValue.Set(reflect.ValueOf(time)) + } + err = err1 + return + case enumeratedType: + parsedInt, err1 := parseInt(innerBytes) + enumValue := v + if err1 == nil { + enumValue.SetInt(int64(parsedInt)) + } + err = err1 + return + case flagType: + flagValue := v + flagValue.SetBool(true) + return + } + switch val := v; val.Kind() { + case reflect.Bool: + parsedBool, err1 := parseBool(innerBytes) + if err1 == nil { + val.SetBool(parsedBool) + } + err = err1 + return + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch val.Type().Kind() { + case reflect.Int: + 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) + } + err = err1 + return + } + case reflect.Struct: + structType := fieldType + + if structType.NumField() > 0 && + structType.Field(0).Type == rawContentsType { + bytes := bytes[initOffset:offset] + val.Field(0).Set(reflect.ValueOf(RawContent(bytes))) + } + + innerOffset := 0 + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if i == 0 && field.Type == rawContentsType { + continue + } + innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag)) + if err != nil { + return + } + } + // We allow extra bytes at the end of the SEQUENCE because + // adding elements to the end has been used in X.509 as the + // version numbers have increased. + return + case reflect.Slice: + sliceType := fieldType + if sliceType.Elem().Kind() == reflect.Uint8 { + val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes))) + reflect.Copy(val, reflect.ValueOf(innerBytes)) + return + } + newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) + if err1 == nil { + val.Set(newSlice) + } + err = err1 + return + case reflect.String: + var v string + switch universalTag { + case tagPrintableString: + v, err = parsePrintableString(innerBytes) + case tagIA5String: + v, err = parseIA5String(innerBytes) + case tagT61String: + v, err = parseT61String(innerBytes) + case tagGeneralString: + // GeneralString is specified in ISO-2022/ECMA-35, + // A brief review suggests that it includes structures + // that allow the encoding to change midstring and + // such. We give up and pass it as an 8-bit string. + v, err = parseT61String(innerBytes) + default: + err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} + } + if err == nil { + val.SetString(v) + } + return + } + err = StructuralError{"unknown Go type"} + return +} + +// setDefaultValue is used to install a default value, from a tag string, into +// a Value. It is successful is the field was optional, even if a default value +// wasn't provided or it failed to install it into the Value. +func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { + if !params.optional { + return + } + ok = true + if params.defaultValue == nil { + return + } + switch val := v; val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + val.SetInt(*params.defaultValue) + } + return +} + +// Unmarshal parses the DER-encoded ASN.1 data structure b +// and uses the reflect package to fill in an arbitrary value pointed at by val. +// Because Unmarshal uses the reflect package, the structs +// being written to must use upper case field names. +// +// An ASN.1 INTEGER can be written to an int or int64. +// If the encoded value does not fit in the Go type, +// Unmarshal returns a parse error. +// +// An ASN.1 BIT STRING can be written to a BitString. +// +// An ASN.1 OCTET STRING can be written to a []byte. +// +// An ASN.1 OBJECT IDENTIFIER can be written to an +// ObjectIdentifier. +// +// An ASN.1 ENUMERATED can be written to an Enumerated. +// +// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time. +// +// An ASN.1 PrintableString or IA5String can be written to a string. +// +// Any of the above ASN.1 values can be written to an interface{}. +// The value stored in the interface has the corresponding Go type. +// For integers, that type is int64. +// +// An ASN.1 SEQUENCE OF x or SET OF x can be written +// to a slice if an x can be written to the slice's element type. +// +// An ASN.1 SEQUENCE or SET can be written to a struct +// if each of the elements in the sequence can be +// written to the corresponding element in the struct. +// +// The following tags on struct fields have special meaning to Unmarshal: +// +// optional marks the field as ASN.1 OPTIONAL +// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC +// default:x sets the default value for optional integer fields +// +// If the type of the first field of a structure is RawContent then the raw +// ASN1 contents of the struct will be stored in it. +// +// Other ASN.1 types are not supported; if it encounters them, +// Unmarshal returns a parse error. +func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) { + return UnmarshalWithParams(b, val, "") +} + +// UnmarshalWithParams allows field parameters to be specified for the +// top-level element. The form of the params is the same as the field tags. +func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err os.Error) { + v := reflect.ValueOf(val).Elem() + offset, err := parseField(v, b, 0, parseFieldParameters(params)) + if err != nil { + return nil, err + } + return b[offset:], nil +} diff --git a/src/cmd/fix/testdata/reflect.datafmt.go.in b/src/cmd/fix/testdata/reflect.datafmt.go.in new file mode 100644 index 000000000..91f885f9a --- /dev/null +++ b/src/cmd/fix/testdata/reflect.datafmt.go.in @@ -0,0 +1,710 @@ +// 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. + +/* The datafmt package implements syntax-directed, type-driven formatting + of arbitrary data structures. Formatting a data structure consists of + two phases: first, a parser reads a format specification and builds a + "compiled" format. Then, the format can be applied repeatedly to + arbitrary values. Applying a format to a value evaluates to a []byte + containing the formatted value bytes, or nil. + + A format specification is a set of package declarations and format rules: + + Format = [ Entry { ";" Entry } [ ";" ] ] . + Entry = PackageDecl | FormatRule . + + (The syntax of a format specification is presented in the same EBNF + notation as used in the Go language specification. The syntax of white + space, comments, identifiers, and string literals is the same as in Go.) + + A package declaration binds a package name (such as 'ast') to a + package import path (such as '"go/ast"'). Each package used (in + a type name, see below) must be declared once before use. + + PackageDecl = PackageName ImportPath . + PackageName = identifier . + ImportPath = string . + + A format rule binds a rule name to a format expression. A rule name + may be a type name or one of the special names 'default' or '/'. + A type name may be the name of a predeclared type (for example, 'int', + 'float32', etc.), the package-qualified name of a user-defined type + (for example, 'ast.MapType'), or an identifier indicating the structure + of unnamed composite types ('array', 'chan', 'func', 'interface', 'map', + or 'ptr'). Each rule must have a unique name; rules can be declared in + any order. + + FormatRule = RuleName "=" Expression . + RuleName = TypeName | "default" | "/" . + TypeName = [ PackageName "." ] identifier . + + To format a value, the value's type name is used to select the format rule + (there is an override mechanism, see below). The format expression of the + selected rule specifies how the value is formatted. Each format expression, + when applied to a value, evaluates to a byte sequence or nil. + + In its most general form, a format expression is a list of alternatives, + each of which is a sequence of operands: + + Expression = [ Sequence ] { "|" [ Sequence ] } . + Sequence = Operand { Operand } . + + The formatted result produced by an expression is the result of the first + alternative sequence that evaluates to a non-nil result; if there is no + such alternative, the expression evaluates to nil. The result produced by + an operand sequence is the concatenation of the results of its operands. + If any operand in the sequence evaluates to nil, the entire sequence + evaluates to nil. + + There are five kinds of operands: + + Operand = Literal | Field | Group | Option | Repetition . + + Literals evaluate to themselves, with two substitutions. First, + %-formats expand in the manner of fmt.Printf, with the current value + passed as the parameter. Second, the current indentation (see below) + is inserted after every newline or form feed character. + + Literal = string . + + This table shows string literals applied to the value 42 and the + corresponding formatted result: + + "foo" foo + "%x" 2a + "x = %d" x = 42 + "%#x = %d" 0x2a = 42 + + A field operand is a field name optionally followed by an alternate + rule name. The field name may be an identifier or one of the special + names @ or *. + + Field = FieldName [ ":" RuleName ] . + FieldName = identifier | "@" | "*" . + + If the field name is an identifier, the current value must be a struct, + and there must be a field with that name in the struct. The same lookup + rules apply as in the Go language (for instance, the name of an anonymous + field is the unqualified type name). The field name denotes the field + value in the struct. If the field is not found, formatting is aborted + and an error message is returned. (TODO consider changing the semantics + such that if a field is not found, it evaluates to nil). + + The special name '@' denotes the current value. + + The meaning of the special name '*' depends on the type of the current + value: + + array, slice types array, slice element (inside {} only, see below) + interfaces value stored in interface + pointers value pointed to by pointer + + (Implementation restriction: channel, function and map types are not + supported due to missing reflection support). + + Fields are evaluated as follows: If the field value is nil, or an array + or slice element does not exist, the result is nil (see below for details + on array/slice elements). If the value is not nil the field value is + formatted (recursively) using the rule corresponding to its type name, + or the alternate rule name, if given. + + The following example shows a complete format specification for a + struct 'myPackage.Point'. Assume the package + + package myPackage // in directory myDir/myPackage + type Point struct { + name string; + x, y int; + } + + Applying the format specification + + myPackage "myDir/myPackage"; + int = "%d"; + hexInt = "0x%x"; + string = "---%s---"; + myPackage.Point = name "{" x ", " y:hexInt "}"; + + to the value myPackage.Point{"foo", 3, 15} results in + + ---foo---{3, 0xf} + + Finally, an operand may be a grouped, optional, or repeated expression. + A grouped expression ("group") groups a more complex expression (body) + so that it can be used in place of a single operand: + + Group = "(" [ Indentation ">>" ] Body ")" . + Indentation = Expression . + Body = Expression . + + A group body may be prefixed by an indentation expression followed by '>>'. + The indentation expression is applied to the current value like any other + expression and the result, if not nil, is appended to the current indentation + during the evaluation of the body (see also formatting state, below). + + An optional expression ("option") is enclosed in '[]' brackets. + + Option = "[" Body "]" . + + An option evaluates to its body, except that if the body evaluates to nil, + the option expression evaluates to an empty []byte. Thus an option's purpose + is to protect the expression containing the option from a nil operand. + + A repeated expression ("repetition") is enclosed in '{}' braces. + + Repetition = "{" Body [ "/" Separator ] "}" . + Separator = Expression . + + A repeated expression is evaluated as follows: The body is evaluated + repeatedly and its results are concatenated until the body evaluates + to nil. The result of the repetition is the (possibly empty) concatenation, + but it is never nil. An implicit index is supplied for the evaluation of + the body: that index is used to address elements of arrays or slices. If + the corresponding elements do not exist, the field denoting the element + evaluates to nil (which in turn may terminate the repetition). + + The body of a repetition may be followed by a '/' and a "separator" + expression. If the separator is present, it is invoked between repetitions + of the body. + + The following example shows a complete format specification for formatting + a slice of unnamed type. Applying the specification + + int = "%b"; + array = { * / ", " }; // array is the type name for an unnamed slice + + to the value '[]int{2, 3, 5, 7}' results in + + 10, 11, 101, 111 + + Default rule: If a format rule named 'default' is present, it is used for + formatting a value if no other rule was found. A common default rule is + + default = "%v" + + to provide default formatting for basic types without having to specify + a specific rule for each basic type. + + Global separator rule: If a format rule named '/' is present, it is + invoked with the current value between literals. If the separator + expression evaluates to nil, it is ignored. + + For instance, a global separator rule may be used to punctuate a sequence + of values with commas. The rules: + + default = "%v"; + / = ", "; + + will format an argument list by printing each one in its default format, + separated by a comma and a space. +*/ +package datafmt + +import ( + "bytes" + "fmt" + "go/token" + "io" + "os" + "reflect" + "runtime" +) + +// ---------------------------------------------------------------------------- +// Format representation + +// Custom formatters implement the Formatter function type. +// A formatter is invoked with the current formatting state, the +// value to format, and the rule name under which the formatter +// was installed (the same formatter function may be installed +// under different names). The formatter may access the current state +// to guide formatting and use State.Write to append to the state's +// output. +// +// A formatter must return a boolean value indicating if it evaluated +// to a non-nil value (true), or a nil value (false). +// +type Formatter func(state *State, value interface{}, ruleName string) bool + +// A FormatterMap is a set of custom formatters. +// It maps a rule name to a formatter function. +// +type FormatterMap map[string]Formatter + +// A parsed format expression is built from the following nodes. +// +type ( + expr interface{} + + alternatives []expr // x | y | z + + sequence []expr // x y z + + literal [][]byte // a list of string segments, possibly starting with '%' + + field struct { + fieldName string // including "@", "*" + ruleName string // "" if no rule name specified + } + + group struct { + indent, body expr // (indent >> body) + } + + option struct { + body expr // [body] + } + + repetition struct { + body, separator expr // {body / separator} + } + + custom struct { + ruleName string + fun Formatter + } +) + +// A Format is the result of parsing a format specification. +// The format may be applied repeatedly to format values. +// +type Format map[string]expr + +// ---------------------------------------------------------------------------- +// Formatting + +// An application-specific environment may be provided to Format.Apply; +// the environment is available inside custom formatters via State.Env(). +// Environments must implement copying; the Copy method must return an +// complete copy of the receiver. This is necessary so that the formatter +// can save and restore an environment (in case of an absent expression). +// +// If the Environment doesn't change during formatting (this is under +// control of the custom formatters), the Copy function can simply return +// the receiver, and thus can be very light-weight. +// +type Environment interface { + Copy() Environment +} + +// State represents the current formatting state. +// It is provided as argument to custom formatters. +// +type State struct { + fmt Format // format in use + env Environment // user-supplied environment + errors chan os.Error // not chan *Error (errors <- nil would be wrong!) + hasOutput bool // true after the first literal has been written + indent bytes.Buffer // current indentation + output bytes.Buffer // format output + linePos token.Position // position of line beginning (Column == 0) + default_ expr // possibly nil + separator expr // possibly nil +} + +func newState(fmt Format, env Environment, errors chan os.Error) *State { + s := new(State) + s.fmt = fmt + s.env = env + s.errors = errors + s.linePos = token.Position{Line: 1} + + // if we have a default rule, cache it's expression for fast access + if x, found := fmt["default"]; found { + s.default_ = x + } + + // if we have a global separator rule, cache it's expression for fast access + if x, found := fmt["/"]; found { + s.separator = x + } + + return s +} + +// Env returns the environment passed to Format.Apply. +func (s *State) Env() interface{} { return s.env } + +// LinePos returns the position of the current line beginning +// in the state's output buffer. Line numbers start at 1. +// +func (s *State) LinePos() token.Position { return s.linePos } + +// Pos returns the position of the next byte to be written to the +// output buffer. Line numbers start at 1. +// +func (s *State) Pos() token.Position { + offs := s.output.Len() + return token.Position{Line: s.linePos.Line, Column: offs - s.linePos.Offset, Offset: offs} +} + +// Write writes data to the output buffer, inserting the indentation +// string after each newline or form feed character. It cannot return an error. +// +func (s *State) Write(data []byte) (int, os.Error) { + n := 0 + i0 := 0 + for i, ch := range data { + if ch == '\n' || ch == '\f' { + // write text segment and indentation + n1, _ := s.output.Write(data[i0 : i+1]) + n2, _ := s.output.Write(s.indent.Bytes()) + n += n1 + n2 + i0 = i + 1 + s.linePos.Offset = s.output.Len() + s.linePos.Line++ + } + } + n3, _ := s.output.Write(data[i0:]) + return n + n3, nil +} + +type checkpoint struct { + env Environment + hasOutput bool + outputLen int + linePos token.Position +} + +func (s *State) save() checkpoint { + saved := checkpoint{nil, s.hasOutput, s.output.Len(), s.linePos} + if s.env != nil { + saved.env = s.env.Copy() + } + return saved +} + +func (s *State) restore(m checkpoint) { + s.env = m.env + s.output.Truncate(m.outputLen) +} + +func (s *State) error(msg string) { + s.errors <- os.NewError(msg) + runtime.Goexit() +} + +// TODO At the moment, unnamed types are simply mapped to the default +// names below. For instance, all unnamed arrays are mapped to +// 'array' which is not really sufficient. Eventually one may want +// to be able to specify rules for say an unnamed slice of T. +// + +func typename(typ reflect.Type) string { + switch typ.(type) { + case *reflect.ArrayType: + return "array" + case *reflect.SliceType: + return "array" + case *reflect.ChanType: + return "chan" + case *reflect.FuncType: + return "func" + case *reflect.InterfaceType: + return "interface" + case *reflect.MapType: + return "map" + case *reflect.PtrType: + return "ptr" + } + return typ.String() +} + +func (s *State) getFormat(name string) expr { + if fexpr, found := s.fmt[name]; found { + return fexpr + } + + if s.default_ != nil { + return s.default_ + } + + s.error(fmt.Sprintf("no format rule for type: '%s'", name)) + return nil +} + +// eval applies a format expression fexpr to a value. If the expression +// evaluates internally to a non-nil []byte, that slice is appended to +// the state's output buffer and eval returns true. Otherwise, eval +// returns false and the state remains unchanged. +// +func (s *State) eval(fexpr expr, value reflect.Value, index int) bool { + // an empty format expression always evaluates + // to a non-nil (but empty) []byte + if fexpr == nil { + return true + } + + switch t := fexpr.(type) { + case alternatives: + // append the result of the first alternative that evaluates to + // a non-nil []byte to the state's output + mark := s.save() + for _, x := range t { + if s.eval(x, value, index) { + return true + } + s.restore(mark) + } + return false + + case sequence: + // append the result of all operands to the state's output + // unless a nil result is encountered + mark := s.save() + for _, x := range t { + if !s.eval(x, value, index) { + s.restore(mark) + return false + } + } + return true + + case literal: + // write separator, if any + if s.hasOutput { + // not the first literal + if s.separator != nil { + sep := s.separator // save current separator + s.separator = nil // and disable it (avoid recursion) + mark := s.save() + if !s.eval(sep, value, index) { + s.restore(mark) + } + s.separator = sep // enable it again + } + } + s.hasOutput = true + // write literal segments + for _, lit := range t { + if len(lit) > 1 && lit[0] == '%' { + // segment contains a %-format at the beginning + if lit[1] == '%' { + // "%%" is printed as a single "%" + s.Write(lit[1:]) + } else { + // use s instead of s.output to get indentation right + fmt.Fprintf(s, string(lit), value.Interface()) + } + } else { + // segment contains no %-formats + s.Write(lit) + } + } + return true // a literal never evaluates to nil + + case *field: + // determine field value + switch t.fieldName { + case "@": + // field value is current value + + case "*": + // indirection: operation is type-specific + switch v := value.(type) { + case *reflect.ArrayValue: + if v.Len() <= index { + return false + } + value = v.Elem(index) + + case *reflect.SliceValue: + if v.IsNil() || v.Len() <= index { + return false + } + value = v.Elem(index) + + case *reflect.MapValue: + s.error("reflection support for maps incomplete") + + case *reflect.PtrValue: + if v.IsNil() { + return false + } + value = v.Elem() + + case *reflect.InterfaceValue: + if v.IsNil() { + return false + } + value = v.Elem() + + case *reflect.ChanValue: + s.error("reflection support for chans incomplete") + + case *reflect.FuncValue: + s.error("reflection support for funcs incomplete") + + default: + s.error(fmt.Sprintf("error: * does not apply to `%s`", value.Type())) + } + + default: + // value is value of named field + var field reflect.Value + if sval, ok := value.(*reflect.StructValue); ok { + field = sval.FieldByName(t.fieldName) + if field == nil { + // TODO consider just returning false in this case + s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type())) + } + } + value = field + } + + // determine rule + ruleName := t.ruleName + if ruleName == "" { + // no alternate rule name, value type determines rule + ruleName = typename(value.Type()) + } + fexpr = s.getFormat(ruleName) + + mark := s.save() + if !s.eval(fexpr, value, index) { + s.restore(mark) + return false + } + return true + + case *group: + // remember current indentation + indentLen := s.indent.Len() + + // update current indentation + mark := s.save() + s.eval(t.indent, value, index) + // if the indentation evaluates to nil, the state's output buffer + // didn't change - either way it's ok to append the difference to + // the current identation + s.indent.Write(s.output.Bytes()[mark.outputLen:s.output.Len()]) + s.restore(mark) + + // format group body + mark = s.save() + b := true + if !s.eval(t.body, value, index) { + s.restore(mark) + b = false + } + + // reset indentation + s.indent.Truncate(indentLen) + return b + + case *option: + // evaluate the body and append the result to the state's output + // buffer unless the result is nil + mark := s.save() + if !s.eval(t.body, value, 0) { // TODO is 0 index correct? + s.restore(mark) + } + return true // an option never evaluates to nil + + case *repetition: + // evaluate the body and append the result to the state's output + // buffer until a result is nil + for i := 0; ; i++ { + mark := s.save() + // write separator, if any + if i > 0 && t.separator != nil { + // nil result from separator is ignored + mark := s.save() + if !s.eval(t.separator, value, i) { + s.restore(mark) + } + } + if !s.eval(t.body, value, i) { + s.restore(mark) + break + } + } + return true // a repetition never evaluates to nil + + case *custom: + // invoke the custom formatter to obtain the result + mark := s.save() + if !t.fun(s, value.Interface(), t.ruleName) { + s.restore(mark) + return false + } + return true + } + + panic("unreachable") + return false +} + +// Eval formats each argument according to the format +// f and returns the resulting []byte and os.Error. If +// an error occurred, the []byte contains the partially +// formatted result. An environment env may be passed +// in which is available in custom formatters through +// the state parameter. +// +func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error) { + if f == nil { + return nil, os.NewError("format is nil") + } + + errors := make(chan os.Error) + s := newState(f, env, errors) + + go func() { + for _, v := range args { + fld := reflect.NewValue(v) + if fld == nil { + errors <- os.NewError("nil argument") + return + } + mark := s.save() + if !s.eval(s.getFormat(typename(fld.Type())), fld, 0) { // TODO is 0 index correct? + s.restore(mark) + } + } + errors <- nil // no errors + }() + + err := <-errors + return s.output.Bytes(), err +} + +// ---------------------------------------------------------------------------- +// Convenience functions + +// Fprint formats each argument according to the format f +// and writes to w. The result is the total number of bytes +// written and an os.Error, if any. +// +func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, os.Error) { + data, err := f.Eval(env, args...) + if err != nil { + // TODO should we print partial result in case of error? + return 0, err + } + return w.Write(data) +} + +// Print formats each argument according to the format f +// and writes to standard output. The result is the total +// number of bytes written and an os.Error, if any. +// +func (f Format) Print(args ...interface{}) (int, os.Error) { + return f.Fprint(os.Stdout, nil, args...) +} + +// Sprint formats each argument according to the format f +// and returns the resulting string. If an error occurs +// during formatting, the result string contains the +// partially formatted result followed by an error message. +// +func (f Format) Sprint(args ...interface{}) string { + var buf bytes.Buffer + _, err := f.Fprint(&buf, nil, args...) + if err != nil { + var i interface{} = args + fmt.Fprintf(&buf, "--- Sprint(%s) failed: %v", fmt.Sprint(i), err) + } + return buf.String() +} diff --git a/src/cmd/fix/testdata/reflect.datafmt.go.out b/src/cmd/fix/testdata/reflect.datafmt.go.out new file mode 100644 index 000000000..fd447588b --- /dev/null +++ b/src/cmd/fix/testdata/reflect.datafmt.go.out @@ -0,0 +1,710 @@ +// 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. + +/* The datafmt package implements syntax-directed, type-driven formatting + of arbitrary data structures. Formatting a data structure consists of + two phases: first, a parser reads a format specification and builds a + "compiled" format. Then, the format can be applied repeatedly to + arbitrary values. Applying a format to a value evaluates to a []byte + containing the formatted value bytes, or nil. + + A format specification is a set of package declarations and format rules: + + Format = [ Entry { ";" Entry } [ ";" ] ] . + Entry = PackageDecl | FormatRule . + + (The syntax of a format specification is presented in the same EBNF + notation as used in the Go language specification. The syntax of white + space, comments, identifiers, and string literals is the same as in Go.) + + A package declaration binds a package name (such as 'ast') to a + package import path (such as '"go/ast"'). Each package used (in + a type name, see below) must be declared once before use. + + PackageDecl = PackageName ImportPath . + PackageName = identifier . + ImportPath = string . + + A format rule binds a rule name to a format expression. A rule name + may be a type name or one of the special names 'default' or '/'. + A type name may be the name of a predeclared type (for example, 'int', + 'float32', etc.), the package-qualified name of a user-defined type + (for example, 'ast.MapType'), or an identifier indicating the structure + of unnamed composite types ('array', 'chan', 'func', 'interface', 'map', + or 'ptr'). Each rule must have a unique name; rules can be declared in + any order. + + FormatRule = RuleName "=" Expression . + RuleName = TypeName | "default" | "/" . + TypeName = [ PackageName "." ] identifier . + + To format a value, the value's type name is used to select the format rule + (there is an override mechanism, see below). The format expression of the + selected rule specifies how the value is formatted. Each format expression, + when applied to a value, evaluates to a byte sequence or nil. + + In its most general form, a format expression is a list of alternatives, + each of which is a sequence of operands: + + Expression = [ Sequence ] { "|" [ Sequence ] } . + Sequence = Operand { Operand } . + + The formatted result produced by an expression is the result of the first + alternative sequence that evaluates to a non-nil result; if there is no + such alternative, the expression evaluates to nil. The result produced by + an operand sequence is the concatenation of the results of its operands. + If any operand in the sequence evaluates to nil, the entire sequence + evaluates to nil. + + There are five kinds of operands: + + Operand = Literal | Field | Group | Option | Repetition . + + Literals evaluate to themselves, with two substitutions. First, + %-formats expand in the manner of fmt.Printf, with the current value + passed as the parameter. Second, the current indentation (see below) + is inserted after every newline or form feed character. + + Literal = string . + + This table shows string literals applied to the value 42 and the + corresponding formatted result: + + "foo" foo + "%x" 2a + "x = %d" x = 42 + "%#x = %d" 0x2a = 42 + + A field operand is a field name optionally followed by an alternate + rule name. The field name may be an identifier or one of the special + names @ or *. + + Field = FieldName [ ":" RuleName ] . + FieldName = identifier | "@" | "*" . + + If the field name is an identifier, the current value must be a struct, + and there must be a field with that name in the struct. The same lookup + rules apply as in the Go language (for instance, the name of an anonymous + field is the unqualified type name). The field name denotes the field + value in the struct. If the field is not found, formatting is aborted + and an error message is returned. (TODO consider changing the semantics + such that if a field is not found, it evaluates to nil). + + The special name '@' denotes the current value. + + The meaning of the special name '*' depends on the type of the current + value: + + array, slice types array, slice element (inside {} only, see below) + interfaces value stored in interface + pointers value pointed to by pointer + + (Implementation restriction: channel, function and map types are not + supported due to missing reflection support). + + Fields are evaluated as follows: If the field value is nil, or an array + or slice element does not exist, the result is nil (see below for details + on array/slice elements). If the value is not nil the field value is + formatted (recursively) using the rule corresponding to its type name, + or the alternate rule name, if given. + + The following example shows a complete format specification for a + struct 'myPackage.Point'. Assume the package + + package myPackage // in directory myDir/myPackage + type Point struct { + name string; + x, y int; + } + + Applying the format specification + + myPackage "myDir/myPackage"; + int = "%d"; + hexInt = "0x%x"; + string = "---%s---"; + myPackage.Point = name "{" x ", " y:hexInt "}"; + + to the value myPackage.Point{"foo", 3, 15} results in + + ---foo---{3, 0xf} + + Finally, an operand may be a grouped, optional, or repeated expression. + A grouped expression ("group") groups a more complex expression (body) + so that it can be used in place of a single operand: + + Group = "(" [ Indentation ">>" ] Body ")" . + Indentation = Expression . + Body = Expression . + + A group body may be prefixed by an indentation expression followed by '>>'. + The indentation expression is applied to the current value like any other + expression and the result, if not nil, is appended to the current indentation + during the evaluation of the body (see also formatting state, below). + + An optional expression ("option") is enclosed in '[]' brackets. + + Option = "[" Body "]" . + + An option evaluates to its body, except that if the body evaluates to nil, + the option expression evaluates to an empty []byte. Thus an option's purpose + is to protect the expression containing the option from a nil operand. + + A repeated expression ("repetition") is enclosed in '{}' braces. + + Repetition = "{" Body [ "/" Separator ] "}" . + Separator = Expression . + + A repeated expression is evaluated as follows: The body is evaluated + repeatedly and its results are concatenated until the body evaluates + to nil. The result of the repetition is the (possibly empty) concatenation, + but it is never nil. An implicit index is supplied for the evaluation of + the body: that index is used to address elements of arrays or slices. If + the corresponding elements do not exist, the field denoting the element + evaluates to nil (which in turn may terminate the repetition). + + The body of a repetition may be followed by a '/' and a "separator" + expression. If the separator is present, it is invoked between repetitions + of the body. + + The following example shows a complete format specification for formatting + a slice of unnamed type. Applying the specification + + int = "%b"; + array = { * / ", " }; // array is the type name for an unnamed slice + + to the value '[]int{2, 3, 5, 7}' results in + + 10, 11, 101, 111 + + Default rule: If a format rule named 'default' is present, it is used for + formatting a value if no other rule was found. A common default rule is + + default = "%v" + + to provide default formatting for basic types without having to specify + a specific rule for each basic type. + + Global separator rule: If a format rule named '/' is present, it is + invoked with the current value between literals. If the separator + expression evaluates to nil, it is ignored. + + For instance, a global separator rule may be used to punctuate a sequence + of values with commas. The rules: + + default = "%v"; + / = ", "; + + will format an argument list by printing each one in its default format, + separated by a comma and a space. +*/ +package datafmt + +import ( + "bytes" + "fmt" + "go/token" + "io" + "os" + "reflect" + "runtime" +) + +// ---------------------------------------------------------------------------- +// Format representation + +// Custom formatters implement the Formatter function type. +// A formatter is invoked with the current formatting state, the +// value to format, and the rule name under which the formatter +// was installed (the same formatter function may be installed +// under different names). The formatter may access the current state +// to guide formatting and use State.Write to append to the state's +// output. +// +// A formatter must return a boolean value indicating if it evaluated +// to a non-nil value (true), or a nil value (false). +// +type Formatter func(state *State, value interface{}, ruleName string) bool + +// A FormatterMap is a set of custom formatters. +// It maps a rule name to a formatter function. +// +type FormatterMap map[string]Formatter + +// A parsed format expression is built from the following nodes. +// +type ( + expr interface{} + + alternatives []expr // x | y | z + + sequence []expr // x y z + + literal [][]byte // a list of string segments, possibly starting with '%' + + field struct { + fieldName string // including "@", "*" + ruleName string // "" if no rule name specified + } + + group struct { + indent, body expr // (indent >> body) + } + + option struct { + body expr // [body] + } + + repetition struct { + body, separator expr // {body / separator} + } + + custom struct { + ruleName string + fun Formatter + } +) + +// A Format is the result of parsing a format specification. +// The format may be applied repeatedly to format values. +// +type Format map[string]expr + +// ---------------------------------------------------------------------------- +// Formatting + +// An application-specific environment may be provided to Format.Apply; +// the environment is available inside custom formatters via State.Env(). +// Environments must implement copying; the Copy method must return an +// complete copy of the receiver. This is necessary so that the formatter +// can save and restore an environment (in case of an absent expression). +// +// If the Environment doesn't change during formatting (this is under +// control of the custom formatters), the Copy function can simply return +// the receiver, and thus can be very light-weight. +// +type Environment interface { + Copy() Environment +} + +// State represents the current formatting state. +// It is provided as argument to custom formatters. +// +type State struct { + fmt Format // format in use + env Environment // user-supplied environment + errors chan os.Error // not chan *Error (errors <- nil would be wrong!) + hasOutput bool // true after the first literal has been written + indent bytes.Buffer // current indentation + output bytes.Buffer // format output + linePos token.Position // position of line beginning (Column == 0) + default_ expr // possibly nil + separator expr // possibly nil +} + +func newState(fmt Format, env Environment, errors chan os.Error) *State { + s := new(State) + s.fmt = fmt + s.env = env + s.errors = errors + s.linePos = token.Position{Line: 1} + + // if we have a default rule, cache it's expression for fast access + if x, found := fmt["default"]; found { + s.default_ = x + } + + // if we have a global separator rule, cache it's expression for fast access + if x, found := fmt["/"]; found { + s.separator = x + } + + return s +} + +// Env returns the environment passed to Format.Apply. +func (s *State) Env() interface{} { return s.env } + +// LinePos returns the position of the current line beginning +// in the state's output buffer. Line numbers start at 1. +// +func (s *State) LinePos() token.Position { return s.linePos } + +// Pos returns the position of the next byte to be written to the +// output buffer. Line numbers start at 1. +// +func (s *State) Pos() token.Position { + offs := s.output.Len() + return token.Position{Line: s.linePos.Line, Column: offs - s.linePos.Offset, Offset: offs} +} + +// Write writes data to the output buffer, inserting the indentation +// string after each newline or form feed character. It cannot return an error. +// +func (s *State) Write(data []byte) (int, os.Error) { + n := 0 + i0 := 0 + for i, ch := range data { + if ch == '\n' || ch == '\f' { + // write text segment and indentation + n1, _ := s.output.Write(data[i0 : i+1]) + n2, _ := s.output.Write(s.indent.Bytes()) + n += n1 + n2 + i0 = i + 1 + s.linePos.Offset = s.output.Len() + s.linePos.Line++ + } + } + n3, _ := s.output.Write(data[i0:]) + return n + n3, nil +} + +type checkpoint struct { + env Environment + hasOutput bool + outputLen int + linePos token.Position +} + +func (s *State) save() checkpoint { + saved := checkpoint{nil, s.hasOutput, s.output.Len(), s.linePos} + if s.env != nil { + saved.env = s.env.Copy() + } + return saved +} + +func (s *State) restore(m checkpoint) { + s.env = m.env + s.output.Truncate(m.outputLen) +} + +func (s *State) error(msg string) { + s.errors <- os.NewError(msg) + runtime.Goexit() +} + +// TODO At the moment, unnamed types are simply mapped to the default +// names below. For instance, all unnamed arrays are mapped to +// 'array' which is not really sufficient. Eventually one may want +// to be able to specify rules for say an unnamed slice of T. +// + +func typename(typ reflect.Type) string { + switch typ.Kind() { + case reflect.Array: + return "array" + case reflect.Slice: + return "array" + case reflect.Chan: + return "chan" + case reflect.Func: + return "func" + case reflect.Interface: + return "interface" + case reflect.Map: + return "map" + case reflect.Ptr: + return "ptr" + } + return typ.String() +} + +func (s *State) getFormat(name string) expr { + if fexpr, found := s.fmt[name]; found { + return fexpr + } + + if s.default_ != nil { + return s.default_ + } + + s.error(fmt.Sprintf("no format rule for type: '%s'", name)) + return nil +} + +// eval applies a format expression fexpr to a value. If the expression +// evaluates internally to a non-nil []byte, that slice is appended to +// the state's output buffer and eval returns true. Otherwise, eval +// returns false and the state remains unchanged. +// +func (s *State) eval(fexpr expr, value reflect.Value, index int) bool { + // an empty format expression always evaluates + // to a non-nil (but empty) []byte + if fexpr == nil { + return true + } + + switch t := fexpr.(type) { + case alternatives: + // append the result of the first alternative that evaluates to + // a non-nil []byte to the state's output + mark := s.save() + for _, x := range t { + if s.eval(x, value, index) { + return true + } + s.restore(mark) + } + return false + + case sequence: + // append the result of all operands to the state's output + // unless a nil result is encountered + mark := s.save() + for _, x := range t { + if !s.eval(x, value, index) { + s.restore(mark) + return false + } + } + return true + + case literal: + // write separator, if any + if s.hasOutput { + // not the first literal + if s.separator != nil { + sep := s.separator // save current separator + s.separator = nil // and disable it (avoid recursion) + mark := s.save() + if !s.eval(sep, value, index) { + s.restore(mark) + } + s.separator = sep // enable it again + } + } + s.hasOutput = true + // write literal segments + for _, lit := range t { + if len(lit) > 1 && lit[0] == '%' { + // segment contains a %-format at the beginning + if lit[1] == '%' { + // "%%" is printed as a single "%" + s.Write(lit[1:]) + } else { + // use s instead of s.output to get indentation right + fmt.Fprintf(s, string(lit), value.Interface()) + } + } else { + // segment contains no %-formats + s.Write(lit) + } + } + return true // a literal never evaluates to nil + + case *field: + // determine field value + switch t.fieldName { + case "@": + // field value is current value + + case "*": + // indirection: operation is type-specific + switch v := value; v.Kind() { + case reflect.Array: + if v.Len() <= index { + return false + } + value = v.Index(index) + + case reflect.Slice: + if v.IsNil() || v.Len() <= index { + return false + } + value = v.Index(index) + + case reflect.Map: + s.error("reflection support for maps incomplete") + + case reflect.Ptr: + if v.IsNil() { + return false + } + value = v.Elem() + + case reflect.Interface: + if v.IsNil() { + return false + } + value = v.Elem() + + case reflect.Chan: + s.error("reflection support for chans incomplete") + + case reflect.Func: + s.error("reflection support for funcs incomplete") + + default: + s.error(fmt.Sprintf("error: * does not apply to `%s`", value.Type())) + } + + default: + // value is value of named field + var field reflect.Value + if sval := value; sval.Kind() == reflect.Struct { + field = sval.FieldByName(t.fieldName) + if !field.IsValid() { + // TODO consider just returning false in this case + s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type())) + } + } + value = field + } + + // determine rule + ruleName := t.ruleName + if ruleName == "" { + // no alternate rule name, value type determines rule + ruleName = typename(value.Type()) + } + fexpr = s.getFormat(ruleName) + + mark := s.save() + if !s.eval(fexpr, value, index) { + s.restore(mark) + return false + } + return true + + case *group: + // remember current indentation + indentLen := s.indent.Len() + + // update current indentation + mark := s.save() + s.eval(t.indent, value, index) + // if the indentation evaluates to nil, the state's output buffer + // didn't change - either way it's ok to append the difference to + // the current identation + s.indent.Write(s.output.Bytes()[mark.outputLen:s.output.Len()]) + s.restore(mark) + + // format group body + mark = s.save() + b := true + if !s.eval(t.body, value, index) { + s.restore(mark) + b = false + } + + // reset indentation + s.indent.Truncate(indentLen) + return b + + case *option: + // evaluate the body and append the result to the state's output + // buffer unless the result is nil + mark := s.save() + if !s.eval(t.body, value, 0) { // TODO is 0 index correct? + s.restore(mark) + } + return true // an option never evaluates to nil + + case *repetition: + // evaluate the body and append the result to the state's output + // buffer until a result is nil + for i := 0; ; i++ { + mark := s.save() + // write separator, if any + if i > 0 && t.separator != nil { + // nil result from separator is ignored + mark := s.save() + if !s.eval(t.separator, value, i) { + s.restore(mark) + } + } + if !s.eval(t.body, value, i) { + s.restore(mark) + break + } + } + return true // a repetition never evaluates to nil + + case *custom: + // invoke the custom formatter to obtain the result + mark := s.save() + if !t.fun(s, value.Interface(), t.ruleName) { + s.restore(mark) + return false + } + return true + } + + panic("unreachable") + return false +} + +// Eval formats each argument according to the format +// f and returns the resulting []byte and os.Error. If +// an error occurred, the []byte contains the partially +// formatted result. An environment env may be passed +// in which is available in custom formatters through +// the state parameter. +// +func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error) { + if f == nil { + return nil, os.NewError("format is nil") + } + + errors := make(chan os.Error) + s := newState(f, env, errors) + + go func() { + for _, v := range args { + fld := reflect.ValueOf(v) + if !fld.IsValid() { + errors <- os.NewError("nil argument") + return + } + mark := s.save() + if !s.eval(s.getFormat(typename(fld.Type())), fld, 0) { // TODO is 0 index correct? + s.restore(mark) + } + } + errors <- nil // no errors + }() + + err := <-errors + return s.output.Bytes(), err +} + +// ---------------------------------------------------------------------------- +// Convenience functions + +// Fprint formats each argument according to the format f +// and writes to w. The result is the total number of bytes +// written and an os.Error, if any. +// +func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, os.Error) { + data, err := f.Eval(env, args...) + if err != nil { + // TODO should we print partial result in case of error? + return 0, err + } + return w.Write(data) +} + +// Print formats each argument according to the format f +// and writes to standard output. The result is the total +// number of bytes written and an os.Error, if any. +// +func (f Format) Print(args ...interface{}) (int, os.Error) { + return f.Fprint(os.Stdout, nil, args...) +} + +// Sprint formats each argument according to the format f +// and returns the resulting string. If an error occurs +// during formatting, the result string contains the +// partially formatted result followed by an error message. +// +func (f Format) Sprint(args ...interface{}) string { + var buf bytes.Buffer + _, err := f.Fprint(&buf, nil, args...) + if err != nil { + var i interface{} = args + fmt.Fprintf(&buf, "--- Sprint(%s) failed: %v", fmt.Sprint(i), err) + } + return buf.String() +} diff --git a/src/cmd/fix/testdata/reflect.decode.go.in b/src/cmd/fix/testdata/reflect.decode.go.in new file mode 100644 index 000000000..f831abee3 --- /dev/null +++ b/src/cmd/fix/testdata/reflect.decode.go.in @@ -0,0 +1,905 @@ +// 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. + +// Represents JSON data structure using native Go types: booleans, floats, +// strings, arrays, and maps. + +package json + +import ( + "container/vector" + "encoding/base64" + "os" + "reflect" + "runtime" + "strconv" + "strings" + "unicode" + "utf16" + "utf8" +) + +// Unmarshal parses the JSON-encoded data and stores the result +// in the value pointed to by v. +// +// Unmarshal traverses the value v recursively. +// If an encountered value implements the Unmarshaler interface, +// Unmarshal calls its UnmarshalJSON method with a well-formed +// JSON encoding. +// +// Otherwise, Unmarshal uses the inverse of the encodings that +// Marshal uses, allocating maps, slices, and pointers as necessary, +// with the following additional rules: +// +// To unmarshal a JSON value into a nil interface value, the +// type stored in the interface value is one of: +// +// bool, for JSON booleans +// float64, for JSON numbers +// string, for JSON strings +// []interface{}, for JSON arrays +// map[string]interface{}, for JSON objects +// nil for JSON null +// +// If a JSON value is not appropriate for a given target type, +// or if a JSON number overflows the target type, Unmarshal +// skips that field and completes the unmarshalling as best it can. +// If no more serious errors are encountered, Unmarshal returns +// an UnmarshalTypeError describing the earliest such error. +// +func Unmarshal(data []byte, v interface{}) os.Error { + d := new(decodeState).init(data) + + // Quick check for well-formedness. + // Avoids filling out half a data structure + // before discovering a JSON syntax error. + err := checkValid(data, &d.scan) + if err != nil { + return err + } + + 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 +// if it wishes to retain the data after returning. +type Unmarshaler interface { + UnmarshalJSON([]byte) os.Error +} + +// An UnmarshalTypeError describes a JSON value that was +// not appropriate for a value of a specific Go type. +type UnmarshalTypeError struct { + Value string // description of JSON value - "bool", "array", "number -5" + Type reflect.Type // type of Go value it could not be assigned to +} + +func (e *UnmarshalTypeError) String() string { + return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() +} + +// An UnmarshalFieldError describes a JSON object key that +// led to an unexported (and therefore unwritable) struct field. +type UnmarshalFieldError struct { + Key string + Type *reflect.StructType + Field reflect.StructField +} + +func (e *UnmarshalFieldError) String() string { + return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() +} + +// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. +// (The argument to Unmarshal must be a non-nil pointer.) +type InvalidUnmarshalError struct { + Type reflect.Type +} + +func (e *InvalidUnmarshalError) String() string { + if e.Type == nil { + return "json: Unmarshal(nil)" + } + + if _, ok := e.Type.(*reflect.PtrType); !ok { + return "json: Unmarshal(non-pointer " + e.Type.String() + ")" + } + return "json: Unmarshal(nil " + e.Type.String() + ")" +} + +func (d *decodeState) unmarshal(v interface{}) (err os.Error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + err = r.(os.Error) + } + }() + + rv := reflect.NewValue(v) + pv, ok := rv.(*reflect.PtrValue) + if !ok || pv.IsNil() { + return &InvalidUnmarshalError{reflect.Typeof(v)} + } + + d.scan.reset() + // We decode rv not pv.Elem because the Unmarshaler interface + // test must be applied at the top level of the value. + d.value(rv) + return d.savedError +} + +// decodeState represents the state while decoding a JSON value. +type decodeState struct { + data []byte + off int // read offset in data + scan scanner + nextscan scanner // for calls to nextValue + savedError os.Error +} + +// errPhase is used for errors that should not happen unless +// there is a bug in the JSON decoder or something is editing +// the data slice while the decoder executes. +var errPhase = os.NewError("JSON decoder out of sync - data changing underfoot?") + +func (d *decodeState) init(data []byte) *decodeState { + d.data = data + d.off = 0 + d.savedError = nil + return d +} + +// error aborts the decoding by panicking with err. +func (d *decodeState) error(err os.Error) { + panic(err) +} + +// saveError saves the first err it is called with, +// for reporting at the end of the unmarshal. +func (d *decodeState) saveError(err os.Error) { + if d.savedError == nil { + d.savedError = err + } +} + +// next cuts off and returns the next full JSON value in d.data[d.off:]. +// The next value is known to be an object or array, not a literal. +func (d *decodeState) next() []byte { + c := d.data[d.off] + item, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // Our scanner has seen the opening brace/bracket + // and thinks we're still in the middle of the object. + // invent a closing brace/bracket to get it out. + if c == '{' { + d.scan.step(&d.scan, '}') + } else { + d.scan.step(&d.scan, ']') + } + + return item +} + +// scanWhile processes bytes in d.data[d.off:] until it +// receives a scan code not equal to op. +// It updates d.off and returns the new scan code. +func (d *decodeState) scanWhile(op int) int { + var newOp int + for { + if d.off >= len(d.data) { + newOp = d.scan.eof() + d.off = len(d.data) + 1 // mark processed EOF with len+1 + } else { + c := int(d.data[d.off]) + d.off++ + newOp = d.scan.step(&d.scan, c) + } + if newOp != op { + break + } + } + return newOp +} + +// value decodes a JSON value from d.data[d.off:] into the value. +// it updates d.off to point past the decoded value. +func (d *decodeState) value(v reflect.Value) { + if v == nil { + _, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // d.scan thinks we're still at the beginning of the item. + // Feed in an empty string - the shortest, simplest value - + // so that it knows we got to the end of the value. + if d.scan.step == stateRedo { + panic("redo") + } + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + return + } + + switch op := d.scanWhile(scanSkipSpace); op { + default: + d.error(errPhase) + + case scanBeginArray: + d.array(v) + + case scanBeginObject: + d.object(v) + + case scanBeginLiteral: + d.literal(v) + } +} + +// indirect walks down v allocating pointers as needed, +// until it gets to a non-pointer. +// if it encounters an Unmarshaler, indirect stops and returns that. +// if wantptr is true, indirect stops at the last pointer. +func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, reflect.Value) { + 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) + } + + if iv, ok := v.(*reflect.InterfaceValue); ok && !iv.IsNil() { + v = iv.Elem() + continue + } + pv, ok := v.(*reflect.PtrValue) + if !ok { + break + } + _, isptrptr := pv.Elem().(*reflect.PtrValue) + if !isptrptr && wantptr && !isUnmarshaler { + return nil, pv + } + if pv.IsNil() { + pv.PointTo(reflect.MakeZero(pv.Type().(*reflect.PtrType).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), nil + } + v = pv.Elem() + } + return nil, v +} + +// array consumes an array from d.data[d.off-1:], decoding into the value v. +// the first byte of the array ('[') has been read already. +func (d *decodeState) array(v reflect.Value) { + // Check for unmarshaler. + unmarshaler, pv := d.indirect(v, false) + if unmarshaler != nil { + d.off-- + err := unmarshaler.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + iv, ok := v.(*reflect.InterfaceValue) + if ok { + iv.Set(reflect.NewValue(d.arrayInterface())) + return + } + + // Check type of target. + av, ok := v.(reflect.ArrayOrSliceValue) + if !ok { + d.saveError(&UnmarshalTypeError{"array", v.Type()}) + d.off-- + d.next() + return + } + + sv, _ := v.(*reflect.SliceValue) + + i := 0 + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + // Get element of array, growing if necessary. + if i >= av.Cap() && sv != nil { + newcap := sv.Cap() + sv.Cap()/2 + if newcap < 4 { + newcap = 4 + } + newv := reflect.MakeSlice(sv.Type().(*reflect.SliceType), sv.Len(), newcap) + reflect.Copy(newv, sv) + sv.Set(newv) + } + if i >= av.Len() && sv != nil { + // Must be slice; gave up on array during i >= av.Cap(). + sv.SetLen(i + 1) + } + + // Decode into element. + if i < av.Len() { + d.value(av.Elem(i)) + } else { + // Ran out of fixed array: skip. + d.value(nil) + } + i++ + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + if i < av.Len() { + if sv == nil { + // Array. Zero the rest. + z := reflect.MakeZero(av.Type().(*reflect.ArrayType).Elem()) + for ; i < av.Len(); i++ { + av.Elem(i).SetValue(z) + } + } else { + sv.SetLen(i) + } + } +} + +// matchName returns true if key should be written to a field named name. +func matchName(key, name string) bool { + return strings.ToLower(key) == strings.ToLower(name) +} + +// object consumes an object from d.data[d.off-1:], decoding into the value v. +// the first byte of the object ('{') has been read already. +func (d *decodeState) object(v reflect.Value) { + // Check for unmarshaler. + unmarshaler, pv := d.indirect(v, false) + if unmarshaler != nil { + d.off-- + err := unmarshaler.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + iv, ok := v.(*reflect.InterfaceValue) + if ok { + iv.Set(reflect.NewValue(d.objectInterface())) + return + } + + // Check type of target: struct or map[string]T + var ( + mv *reflect.MapValue + sv *reflect.StructValue + ) + switch v := v.(type) { + case *reflect.MapValue: + // map must have string type + t := v.Type().(*reflect.MapType) + if t.Key() != reflect.Typeof("") { + d.saveError(&UnmarshalTypeError{"object", v.Type()}) + break + } + mv = v + if mv.IsNil() { + mv.SetValue(reflect.MakeMap(t)) + } + case *reflect.StructValue: + sv = v + default: + d.saveError(&UnmarshalTypeError{"object", v.Type()}) + } + + if mv == nil && sv == nil { + d.off-- + d.next() // skip over { } in input + return + } + + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Figure out field corresponding to key. + var subv reflect.Value + if mv != nil { + subv = reflect.MakeZero(mv.Type().(*reflect.MapType).Elem()) + } else { + var f reflect.StructField + var ok bool + st := sv.Type().(*reflect.StructType) + // First try for field with that tag. + if isValidTag(key) { + for i := 0; i < sv.NumField(); i++ { + f = st.Field(i) + if f.Tag == key { + ok = true + break + } + } + } + if !ok { + // Second, exact match. + f, ok = st.FieldByName(key) + } + if !ok { + // Third, case-insensitive match. + f, ok = st.FieldByNameFunc(func(s string) bool { return matchName(key, s) }) + } + + // Extract value; name must be exported. + if ok { + if f.PkgPath != "" { + d.saveError(&UnmarshalFieldError{key, st, f}) + } else { + subv = sv.FieldByIndex(f.Index) + } + } + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + d.value(subv) + + // Write value back to map; + // if using struct, subv points into struct already. + if mv != nil { + mv.SetElem(reflect.NewValue(key), subv) + } + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } +} + +// literal consumes a literal from d.data[d.off-1:], decoding into the value v. +// The first byte of the literal has been read already +// (that's how the caller knows it's a literal). +func (d *decodeState) literal(v reflect.Value) { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + // Check for unmarshaler. + wantptr := item[0] == 'n' // null + unmarshaler, pv := d.indirect(v, wantptr) + if unmarshaler != nil { + err := unmarshaler.UnmarshalJSON(item) + if err != nil { + d.error(err) + } + return + } + v = pv + + switch c := item[0]; c { + case 'n': // null + switch v.(type) { + default: + d.saveError(&UnmarshalTypeError{"null", v.Type()}) + case *reflect.InterfaceValue, *reflect.PtrValue, *reflect.MapValue: + v.SetValue(nil) + } + + case 't', 'f': // true, false + value := c == 't' + switch v := v.(type) { + default: + d.saveError(&UnmarshalTypeError{"bool", v.Type()}) + case *reflect.BoolValue: + v.Set(value) + case *reflect.InterfaceValue: + v.Set(reflect.NewValue(value)) + } + + case '"': // string + s, ok := unquoteBytes(item) + if !ok { + d.error(errPhase) + } + switch v := v.(type) { + default: + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + case *reflect.SliceValue: + if v.Type() != byteSliceType { + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + break + } + b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) + n, err := base64.StdEncoding.Decode(b, s) + if err != nil { + d.saveError(err) + break + } + v.Set(reflect.NewValue(b[0:n]).(*reflect.SliceValue)) + case *reflect.StringValue: + v.Set(string(s)) + case *reflect.InterfaceValue: + v.Set(reflect.NewValue(string(s))) + } + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + s := string(item) + switch v := v.(type) { + default: + d.error(&UnmarshalTypeError{"number", v.Type()}) + case *reflect.InterfaceValue: + n, err := strconv.Atof64(s) + if err != nil { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.Set(reflect.NewValue(n)) + + case *reflect.IntValue: + n, err := strconv.Atoi64(s) + if err != nil || v.Overflow(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.Set(n) + + case *reflect.UintValue: + n, err := strconv.Atoui64(s) + if err != nil || v.Overflow(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.Set(n) + + case *reflect.FloatValue: + n, err := strconv.AtofN(s, v.Type().Bits()) + if err != nil || v.Overflow(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.Set(n) + } + } +} + +// The xxxInterface routines build up a value to be stored +// in an empty interface. They are not strictly necessary, +// but they avoid the weight of reflection in this common case. + +// valueInterface is like value but returns interface{} +func (d *decodeState) valueInterface() interface{} { + switch d.scanWhile(scanSkipSpace) { + default: + d.error(errPhase) + case scanBeginArray: + return d.arrayInterface() + case scanBeginObject: + return d.objectInterface() + case scanBeginLiteral: + return d.literalInterface() + } + panic("unreachable") +} + +// arrayInterface is like array but returns []interface{}. +func (d *decodeState) arrayInterface() []interface{} { + var v vector.Vector + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + v.Push(d.valueInterface()) + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + return v +} + +// objectInterface is like object but returns map[string]interface{}. +func (d *decodeState) objectInterface() map[string]interface{} { + m := make(map[string]interface{}) + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + m[key] = d.valueInterface() + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } + return m +} + +// literalInterface is like literal but returns an interface value. +func (d *decodeState) literalInterface() interface{} { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + switch c := item[0]; c { + case 'n': // null + return nil + + case 't', 'f': // true, false + return c == 't' + + case '"': // string + s, ok := unquote(item) + if !ok { + d.error(errPhase) + } + return s + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + n, err := strconv.Atof64(string(item)) + if err != nil { + d.saveError(&UnmarshalTypeError{"number " + string(item), reflect.Typeof(0.0)}) + } + return n + } + panic("unreachable") +} + +// getu4 decodes \uXXXX from the beginning of s, returning the hex value, +// or it returns -1. +func getu4(s []byte) int { + if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { + return -1 + } + rune, err := strconv.Btoui64(string(s[2:6]), 16) + if err != nil { + return -1 + } + return int(rune) +} + +// unquote converts a quoted JSON string literal s into an actual string t. +// The rules are different than for Go, so cannot use strconv.Unquote. +func unquote(s []byte) (t string, ok bool) { + s, ok = unquoteBytes(s) + t = string(s) + return +} + +func unquoteBytes(s []byte) (t []byte, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return + } + s = s[1 : len(s)-1] + + // Check for unusual characters. If there are none, + // then no unquoting is needed, so return a slice of the + // original bytes. + r := 0 + for r < len(s) { + c := s[r] + if c == '\\' || c == '"' || c < ' ' { + break + } + if c < utf8.RuneSelf { + r++ + continue + } + rune, size := utf8.DecodeRune(s[r:]) + if rune == utf8.RuneError && size == 1 { + break + } + r += size + } + if r == len(s) { + return s, true + } + + b := make([]byte, len(s)+2*utf8.UTFMax) + w := copy(b, s[0:r]) + for r < len(s) { + // Out of room? Can only happen if s is full of + // malformed UTF-8 and we're replacing each + // byte with RuneError. + if w >= len(b)-2*utf8.UTFMax { + nb := make([]byte, (len(b)+utf8.UTFMax)*2) + copy(nb, b[0:w]) + b = nb + } + switch c := s[r]; { + case c == '\\': + r++ + if r >= len(s) { + return + } + switch s[r] { + default: + return + case '"', '\\', '/', '\'': + b[w] = s[r] + r++ + w++ + case 'b': + b[w] = '\b' + r++ + w++ + case 'f': + b[w] = '\f' + r++ + w++ + case 'n': + b[w] = '\n' + r++ + w++ + case 'r': + b[w] = '\r' + r++ + w++ + case 't': + b[w] = '\t' + r++ + w++ + case 'u': + r-- + rune := getu4(s[r:]) + if rune < 0 { + return + } + r += 6 + if utf16.IsSurrogate(rune) { + rune1 := getu4(s[r:]) + if dec := utf16.DecodeRune(rune, rune1); dec != unicode.ReplacementChar { + // A valid pair; consume. + r += 6 + w += utf8.EncodeRune(b[w:], dec) + break + } + // Invalid surrogate; fall back to replacement rune. + rune = unicode.ReplacementChar + } + w += utf8.EncodeRune(b[w:], rune) + } + + // Quote, control characters are invalid. + case c == '"', c < ' ': + return + + // ASCII + case c < utf8.RuneSelf: + b[w] = c + r++ + w++ + + // Coerce to well-formed UTF-8. + default: + rune, size := utf8.DecodeRune(s[r:]) + r += size + w += utf8.EncodeRune(b[w:], rune) + } + } + return b[0:w], true +} diff --git a/src/cmd/fix/testdata/reflect.decode.go.out b/src/cmd/fix/testdata/reflect.decode.go.out new file mode 100644 index 000000000..fb7910ee3 --- /dev/null +++ b/src/cmd/fix/testdata/reflect.decode.go.out @@ -0,0 +1,908 @@ +// 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. + +// Represents JSON data structure using native Go types: booleans, floats, +// strings, arrays, and maps. + +package json + +import ( + "container/vector" + "encoding/base64" + "os" + "reflect" + "runtime" + "strconv" + "strings" + "unicode" + "utf16" + "utf8" +) + +// Unmarshal parses the JSON-encoded data and stores the result +// in the value pointed to by v. +// +// Unmarshal traverses the value v recursively. +// If an encountered value implements the Unmarshaler interface, +// Unmarshal calls its UnmarshalJSON method with a well-formed +// JSON encoding. +// +// Otherwise, Unmarshal uses the inverse of the encodings that +// Marshal uses, allocating maps, slices, and pointers as necessary, +// with the following additional rules: +// +// To unmarshal a JSON value into a nil interface value, the +// type stored in the interface value is one of: +// +// bool, for JSON booleans +// float64, for JSON numbers +// string, for JSON strings +// []interface{}, for JSON arrays +// map[string]interface{}, for JSON objects +// nil for JSON null +// +// If a JSON value is not appropriate for a given target type, +// or if a JSON number overflows the target type, Unmarshal +// skips that field and completes the unmarshalling as best it can. +// If no more serious errors are encountered, Unmarshal returns +// an UnmarshalTypeError describing the earliest such error. +// +func Unmarshal(data []byte, v interface{}) os.Error { + d := new(decodeState).init(data) + + // Quick check for well-formedness. + // Avoids filling out half a data structure + // before discovering a JSON syntax error. + err := checkValid(data, &d.scan) + if err != nil { + return err + } + + 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 +// if it wishes to retain the data after returning. +type Unmarshaler interface { + UnmarshalJSON([]byte) os.Error +} + +// An UnmarshalTypeError describes a JSON value that was +// not appropriate for a value of a specific Go type. +type UnmarshalTypeError struct { + Value string // description of JSON value - "bool", "array", "number -5" + Type reflect.Type // type of Go value it could not be assigned to +} + +func (e *UnmarshalTypeError) String() string { + return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() +} + +// An UnmarshalFieldError describes a JSON object key that +// led to an unexported (and therefore unwritable) struct field. +type UnmarshalFieldError struct { + Key string + Type reflect.Type + Field reflect.StructField +} + +func (e *UnmarshalFieldError) String() string { + return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() +} + +// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. +// (The argument to Unmarshal must be a non-nil pointer.) +type InvalidUnmarshalError struct { + Type reflect.Type +} + +func (e *InvalidUnmarshalError) String() string { + if e.Type == nil { + return "json: Unmarshal(nil)" + } + + if e.Type.Kind() != reflect.Ptr { + return "json: Unmarshal(non-pointer " + e.Type.String() + ")" + } + return "json: Unmarshal(nil " + e.Type.String() + ")" +} + +func (d *decodeState) unmarshal(v interface{}) (err os.Error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + err = r.(os.Error) + } + }() + + rv := reflect.ValueOf(v) + pv := rv + if pv.Kind() != reflect.Ptr || + pv.IsNil() { + return &InvalidUnmarshalError{reflect.TypeOf(v)} + } + + d.scan.reset() + // We decode rv not pv.Elem because the Unmarshaler interface + // test must be applied at the top level of the value. + d.value(rv) + return d.savedError +} + +// decodeState represents the state while decoding a JSON value. +type decodeState struct { + data []byte + off int // read offset in data + scan scanner + nextscan scanner // for calls to nextValue + savedError os.Error +} + +// errPhase is used for errors that should not happen unless +// there is a bug in the JSON decoder or something is editing +// the data slice while the decoder executes. +var errPhase = os.NewError("JSON decoder out of sync - data changing underfoot?") + +func (d *decodeState) init(data []byte) *decodeState { + d.data = data + d.off = 0 + d.savedError = nil + return d +} + +// error aborts the decoding by panicking with err. +func (d *decodeState) error(err os.Error) { + panic(err) +} + +// saveError saves the first err it is called with, +// for reporting at the end of the unmarshal. +func (d *decodeState) saveError(err os.Error) { + if d.savedError == nil { + d.savedError = err + } +} + +// next cuts off and returns the next full JSON value in d.data[d.off:]. +// The next value is known to be an object or array, not a literal. +func (d *decodeState) next() []byte { + c := d.data[d.off] + item, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // Our scanner has seen the opening brace/bracket + // and thinks we're still in the middle of the object. + // invent a closing brace/bracket to get it out. + if c == '{' { + d.scan.step(&d.scan, '}') + } else { + d.scan.step(&d.scan, ']') + } + + return item +} + +// scanWhile processes bytes in d.data[d.off:] until it +// receives a scan code not equal to op. +// It updates d.off and returns the new scan code. +func (d *decodeState) scanWhile(op int) int { + var newOp int + for { + if d.off >= len(d.data) { + newOp = d.scan.eof() + d.off = len(d.data) + 1 // mark processed EOF with len+1 + } else { + c := int(d.data[d.off]) + d.off++ + newOp = d.scan.step(&d.scan, c) + } + if newOp != op { + break + } + } + return newOp +} + +// value decodes a JSON value from d.data[d.off:] into the value. +// it updates d.off to point past the decoded value. +func (d *decodeState) value(v reflect.Value) { + if !v.IsValid() { + _, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // d.scan thinks we're still at the beginning of the item. + // Feed in an empty string - the shortest, simplest value - + // so that it knows we got to the end of the value. + if d.scan.step == stateRedo { + panic("redo") + } + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + return + } + + switch op := d.scanWhile(scanSkipSpace); op { + default: + d.error(errPhase) + + case scanBeginArray: + d.array(v) + + case scanBeginObject: + d.object(v) + + case scanBeginLiteral: + d.literal(v) + } +} + +// indirect walks down v allocating pointers as needed, +// until it gets to a non-pointer. +// if it encounters an Unmarshaler, indirect stops and returns that. +// if wantptr is true, indirect stops at the last pointer. +func (d *decodeState) indirect(v reflect.Value, wantptr bool) (Unmarshaler, reflect.Value) { + 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) + } + + if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() { + v = iv.Elem() + continue + } + pv := v + if pv.Kind() != reflect.Ptr { + break + } + + if pv.Elem().Kind() != reflect.Ptr && + wantptr && !isUnmarshaler { + return nil, pv + } + if pv.IsNil() { + pv.Set(reflect.Zero(pv.Type().Elem()).Addr()) + } + 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{} + } + v = pv.Elem() + } + return nil, v +} + +// array consumes an array from d.data[d.off-1:], decoding into the value v. +// the first byte of the array ('[') has been read already. +func (d *decodeState) array(v reflect.Value) { + // Check for unmarshaler. + unmarshaler, pv := d.indirect(v, false) + if unmarshaler != nil { + d.off-- + err := unmarshaler.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + iv := v + ok := iv.Kind() == reflect.Interface + if ok { + iv.Set(reflect.ValueOf(d.arrayInterface())) + return + } + + // Check type of target. + av := v + if av.Kind() != reflect.Array && av.Kind() != reflect.Slice { + d.saveError(&UnmarshalTypeError{"array", v.Type()}) + d.off-- + d.next() + return + } + + sv := v + + i := 0 + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + // Get element of array, growing if necessary. + if i >= av.Cap() && sv.IsValid() { + newcap := sv.Cap() + sv.Cap()/2 + if newcap < 4 { + newcap = 4 + } + newv := reflect.MakeSlice(sv.Type(), sv.Len(), newcap) + reflect.Copy(newv, sv) + sv.Set(newv) + } + if i >= av.Len() && sv.IsValid() { + // Must be slice; gave up on array during i >= av.Cap(). + sv.SetLen(i + 1) + } + + // Decode into element. + if i < av.Len() { + d.value(av.Index(i)) + } else { + // Ran out of fixed array: skip. + d.value(reflect.Value{}) + } + i++ + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + if i < av.Len() { + if !sv.IsValid() { + // Array. Zero the rest. + z := reflect.Zero(av.Type().Elem()) + for ; i < av.Len(); i++ { + av.Index(i).Set(z) + } + } else { + sv.SetLen(i) + } + } +} + +// matchName returns true if key should be written to a field named name. +func matchName(key, name string) bool { + return strings.ToLower(key) == strings.ToLower(name) +} + +// object consumes an object from d.data[d.off-1:], decoding into the value v. +// the first byte of the object ('{') has been read already. +func (d *decodeState) object(v reflect.Value) { + // Check for unmarshaler. + unmarshaler, pv := d.indirect(v, false) + if unmarshaler != nil { + d.off-- + err := unmarshaler.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + iv := v + if iv.Kind() == reflect.Interface { + iv.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 + t := v.Type() + if t.Key() != reflect.TypeOf("") { + d.saveError(&UnmarshalTypeError{"object", v.Type()}) + break + } + mv = v + if mv.IsNil() { + mv.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 + } + + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Figure out field corresponding to key. + var subv reflect.Value + if mv.IsValid() { + subv = reflect.Zero(mv.Type().Elem()) + } else { + var f reflect.StructField + var ok bool + st := sv.Type() + // First try for field with that tag. + if isValidTag(key) { + for i := 0; i < sv.NumField(); i++ { + f = st.Field(i) + if f.Tag == key { + ok = true + break + } + } + } + if !ok { + // Second, exact match. + f, ok = st.FieldByName(key) + } + if !ok { + // Third, case-insensitive match. + f, ok = st.FieldByNameFunc(func(s string) bool { return matchName(key, s) }) + } + + // Extract value; name must be exported. + if ok { + if f.PkgPath != "" { + d.saveError(&UnmarshalFieldError{key, st, f}) + } else { + subv = sv.FieldByIndex(f.Index) + } + } + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + 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) + } + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } +} + +// literal consumes a literal from d.data[d.off-1:], decoding into the value v. +// The first byte of the literal has been read already +// (that's how the caller knows it's a literal). +func (d *decodeState) literal(v reflect.Value) { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + // Check for unmarshaler. + wantptr := item[0] == 'n' // null + unmarshaler, pv := d.indirect(v, wantptr) + if unmarshaler != nil { + err := unmarshaler.UnmarshalJSON(item) + if err != nil { + d.error(err) + } + return + } + v = pv + + 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: + v.Set(reflect.Zero(v.Type())) + } + + case 't', 'f': // true, false + value := c == 't' + switch v.Kind() { + default: + d.saveError(&UnmarshalTypeError{"bool", v.Type()}) + case reflect.Bool: + v.SetBool(value) + case reflect.Interface: + v.Set(reflect.ValueOf(value)) + } + + case '"': // string + s, ok := unquoteBytes(item) + if !ok { + d.error(errPhase) + } + switch v.Kind() { + default: + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + case reflect.Slice: + if v.Type() != byteSliceType { + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + break + } + b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) + n, err := base64.StdEncoding.Decode(b, s) + if err != nil { + d.saveError(err) + break + } + v.Set(reflect.ValueOf(b[0:n])) + case reflect.String: + v.SetString(string(s)) + case reflect.Interface: + v.Set(reflect.ValueOf(string(s))) + } + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + s := string(item) + switch v.Kind() { + default: + d.error(&UnmarshalTypeError{"number", v.Type()}) + case reflect.Interface: + n, err := strconv.Atof64(s) + if err != nil { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.Set(reflect.ValueOf(n)) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n, err := strconv.Atoi64(s) + if err != nil || v.OverflowInt(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.SetInt(n) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n, err := strconv.Atoui64(s) + if err != nil || v.OverflowUint(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.SetUint(n) + + case reflect.Float32, reflect.Float64: + n, err := strconv.AtofN(s, v.Type().Bits()) + if err != nil || v.OverflowFloat(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.SetFloat(n) + } + } +} + +// The xxxInterface routines build up a value to be stored +// in an empty interface. They are not strictly necessary, +// but they avoid the weight of reflection in this common case. + +// valueInterface is like value but returns interface{} +func (d *decodeState) valueInterface() interface{} { + switch d.scanWhile(scanSkipSpace) { + default: + d.error(errPhase) + case scanBeginArray: + return d.arrayInterface() + case scanBeginObject: + return d.objectInterface() + case scanBeginLiteral: + return d.literalInterface() + } + panic("unreachable") +} + +// arrayInterface is like array but returns []interface{}. +func (d *decodeState) arrayInterface() []interface{} { + var v vector.Vector + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + v.Push(d.valueInterface()) + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + return v +} + +// objectInterface is like object but returns map[string]interface{}. +func (d *decodeState) objectInterface() map[string]interface{} { + m := make(map[string]interface{}) + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + m[key] = d.valueInterface() + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } + return m +} + +// literalInterface is like literal but returns an interface value. +func (d *decodeState) literalInterface() interface{} { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + switch c := item[0]; c { + case 'n': // null + return nil + + case 't', 'f': // true, false + return c == 't' + + case '"': // string + s, ok := unquote(item) + if !ok { + d.error(errPhase) + } + return s + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + n, err := strconv.Atof64(string(item)) + if err != nil { + d.saveError(&UnmarshalTypeError{"number " + string(item), reflect.TypeOf(0.0)}) + } + return n + } + panic("unreachable") +} + +// getu4 decodes \uXXXX from the beginning of s, returning the hex value, +// or it returns -1. +func getu4(s []byte) int { + if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { + return -1 + } + rune, err := strconv.Btoui64(string(s[2:6]), 16) + if err != nil { + return -1 + } + return int(rune) +} + +// unquote converts a quoted JSON string literal s into an actual string t. +// The rules are different than for Go, so cannot use strconv.Unquote. +func unquote(s []byte) (t string, ok bool) { + s, ok = unquoteBytes(s) + t = string(s) + return +} + +func unquoteBytes(s []byte) (t []byte, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return + } + s = s[1 : len(s)-1] + + // Check for unusual characters. If there are none, + // then no unquoting is needed, so return a slice of the + // original bytes. + r := 0 + for r < len(s) { + c := s[r] + if c == '\\' || c == '"' || c < ' ' { + break + } + if c < utf8.RuneSelf { + r++ + continue + } + rune, size := utf8.DecodeRune(s[r:]) + if rune == utf8.RuneError && size == 1 { + break + } + r += size + } + if r == len(s) { + return s, true + } + + b := make([]byte, len(s)+2*utf8.UTFMax) + w := copy(b, s[0:r]) + for r < len(s) { + // Out of room? Can only happen if s is full of + // malformed UTF-8 and we're replacing each + // byte with RuneError. + if w >= len(b)-2*utf8.UTFMax { + nb := make([]byte, (len(b)+utf8.UTFMax)*2) + copy(nb, b[0:w]) + b = nb + } + switch c := s[r]; { + case c == '\\': + r++ + if r >= len(s) { + return + } + switch s[r] { + default: + return + case '"', '\\', '/', '\'': + b[w] = s[r] + r++ + w++ + case 'b': + b[w] = '\b' + r++ + w++ + case 'f': + b[w] = '\f' + r++ + w++ + case 'n': + b[w] = '\n' + r++ + w++ + case 'r': + b[w] = '\r' + r++ + w++ + case 't': + b[w] = '\t' + r++ + w++ + case 'u': + r-- + rune := getu4(s[r:]) + if rune < 0 { + return + } + r += 6 + if utf16.IsSurrogate(rune) { + rune1 := getu4(s[r:]) + if dec := utf16.DecodeRune(rune, rune1); dec != unicode.ReplacementChar { + // A valid pair; consume. + r += 6 + w += utf8.EncodeRune(b[w:], dec) + break + } + // Invalid surrogate; fall back to replacement rune. + rune = unicode.ReplacementChar + } + w += utf8.EncodeRune(b[w:], rune) + } + + // Quote, control characters are invalid. + case c == '"', c < ' ': + return + + // ASCII + case c < utf8.RuneSelf: + b[w] = c + r++ + w++ + + // Coerce to well-formed UTF-8. + default: + rune, size := utf8.DecodeRune(s[r:]) + r += size + w += utf8.EncodeRune(b[w:], rune) + } + } + return b[0:w], true +} diff --git a/src/cmd/fix/testdata/reflect.decoder.go.in b/src/cmd/fix/testdata/reflect.decoder.go.in new file mode 100644 index 000000000..0ce9b06fd --- /dev/null +++ b/src/cmd/fix/testdata/reflect.decoder.go.in @@ -0,0 +1,196 @@ +// 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 gob + +import ( + "bufio" + "bytes" + "io" + "os" + "reflect" + "sync" +) + +// A Decoder manages the receipt of type and data information read from the +// remote side of a connection. +type Decoder struct { + mutex sync.Mutex // each item must be received atomically + r io.Reader // source of the data + buf bytes.Buffer // buffer for more efficient i/o from r + wireType map[typeId]*wireType // map from remote ID to local description + decoderCache map[reflect.Type]map[typeId]**decEngine // cache of compiled engines + ignorerCache map[typeId]**decEngine // ditto for ignored objects + freeList *decoderState // list of free decoderStates; avoids reallocation + countBuf []byte // used for decoding integers while parsing messages + tmp []byte // temporary storage for i/o; saves reallocating + err os.Error +} + +// NewDecoder returns a new decoder that reads from the io.Reader. +func NewDecoder(r io.Reader) *Decoder { + dec := new(Decoder) + dec.r = bufio.NewReader(r) + dec.wireType = make(map[typeId]*wireType) + dec.decoderCache = make(map[reflect.Type]map[typeId]**decEngine) + dec.ignorerCache = make(map[typeId]**decEngine) + dec.countBuf = make([]byte, 9) // counts may be uint64s (unlikely!), require 9 bytes + + return dec +} + +// recvType loads the definition of a type. +func (dec *Decoder) recvType(id typeId) { + // Have we already seen this type? That's an error + if id < firstUserId || dec.wireType[id] != nil { + dec.err = os.NewError("gob: duplicate type received") + return + } + + // Type: + wire := new(wireType) + dec.decodeValue(tWireType, reflect.NewValue(wire)) + if dec.err != nil { + return + } + // Remember we've seen this type. + dec.wireType[id] = wire +} + +// recvMessage reads the next count-delimited item from the input. It is the converse +// of Encoder.writeMessage. It returns false on EOF or other error reading the message. +func (dec *Decoder) recvMessage() bool { + // Read a count. + nbytes, _, err := decodeUintReader(dec.r, dec.countBuf) + if err != nil { + dec.err = err + return false + } + dec.readMessage(int(nbytes)) + return dec.err == nil +} + +// 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 + } + dec.tmp = dec.tmp[:nbytes] + + // Read the data + _, dec.err = io.ReadFull(dec.r, dec.tmp) + if dec.err != nil { + if dec.err == os.EOF { + dec.err = io.ErrUnexpectedEOF + } + return + } + dec.buf.Write(dec.tmp) +} + +// toInt turns an encoded uint64 into an int, according to the marshaling rules. +func toInt(x uint64) int64 { + i := int64(x >> 1) + if x&1 != 0 { + i = ^i + } + return i +} + +func (dec *Decoder) nextInt() int64 { + n, _, err := decodeUintReader(&dec.buf, dec.countBuf) + if err != nil { + dec.err = err + } + return toInt(n) +} + +func (dec *Decoder) nextUint() uint64 { + n, _, err := decodeUintReader(&dec.buf, dec.countBuf) + if err != nil { + dec.err = err + } + return n +} + +// decodeTypeSequence parses: +// TypeSequence +// (TypeDefinition DelimitedTypeDefinition*)? +// and returns the type id of the next value. It returns -1 at +// EOF. Upon return, the remainder of dec.buf is the value to be +// decoded. If this is an interface value, it can be ignored by +// simply resetting that buffer. +func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId { + for dec.err == nil { + if dec.buf.Len() == 0 { + if !dec.recvMessage() { + break + } + } + // Receive a type id. + id := typeId(dec.nextInt()) + if id >= 0 { + // Value follows. + return id + } + // Type definition for (-id) follows. + dec.recvType(-id) + // When decoding an interface, after a type there may be a + // DelimitedValue still in the buffer. Skip its count. + // (Alternatively, the buffer is empty and the byte count + // will be absorbed by recvMessage.) + if dec.buf.Len() > 0 { + if !isInterface { + dec.err = os.NewError("extra data in buffer") + break + } + dec.nextUint() + } + } + return -1 +} + +// Decode reads the next value from the connection and stores +// it in the data represented by the empty interface value. +// If e is nil, the value will be discarded. Otherwise, +// the value underlying e must either be the correct type for the next +// data item received, and must be a pointer. +func (dec *Decoder) Decode(e interface{}) os.Error { + if e == nil { + return dec.DecodeValue(nil) + } + value := reflect.NewValue(e) + // If e represents a value as opposed to a pointer, the answer won't + // get back to the caller. Make sure it's a pointer. + if value.Type().Kind() != reflect.Ptr { + dec.err = os.NewError("gob: attempt to decode into a non-pointer") + return dec.err + } + return dec.DecodeValue(value) +} + +// DecodeValue reads the next value from the connection and stores +// it in the data represented by the reflection value. +// The value must be the correct type for the next +// data item received, or it may be nil, which means the +// value will be discarded. +func (dec *Decoder) DecodeValue(value reflect.Value) os.Error { + // Make sure we're single-threaded through here. + dec.mutex.Lock() + defer dec.mutex.Unlock() + + dec.buf.Reset() // In case data lingers from previous invocation. + dec.err = nil + id := dec.decodeTypeSequence(false) + if dec.err == nil { + dec.decodeValue(id, value) + } + return dec.err +} + +// If debug.go is compiled into the program , debugFunc prints a human-readable +// representation of the gob data read from r by calling that file's Debug function. +// Otherwise it is nil. +var debugFunc func(io.Reader) diff --git a/src/cmd/fix/testdata/reflect.decoder.go.out b/src/cmd/fix/testdata/reflect.decoder.go.out new file mode 100644 index 000000000..ece88ecbe --- /dev/null +++ b/src/cmd/fix/testdata/reflect.decoder.go.out @@ -0,0 +1,196 @@ +// 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 gob + +import ( + "bufio" + "bytes" + "io" + "os" + "reflect" + "sync" +) + +// A Decoder manages the receipt of type and data information read from the +// remote side of a connection. +type Decoder struct { + mutex sync.Mutex // each item must be received atomically + r io.Reader // source of the data + buf bytes.Buffer // buffer for more efficient i/o from r + wireType map[typeId]*wireType // map from remote ID to local description + decoderCache map[reflect.Type]map[typeId]**decEngine // cache of compiled engines + ignorerCache map[typeId]**decEngine // ditto for ignored objects + freeList *decoderState // list of free decoderStates; avoids reallocation + countBuf []byte // used for decoding integers while parsing messages + tmp []byte // temporary storage for i/o; saves reallocating + err os.Error +} + +// NewDecoder returns a new decoder that reads from the io.Reader. +func NewDecoder(r io.Reader) *Decoder { + dec := new(Decoder) + dec.r = bufio.NewReader(r) + dec.wireType = make(map[typeId]*wireType) + dec.decoderCache = make(map[reflect.Type]map[typeId]**decEngine) + dec.ignorerCache = make(map[typeId]**decEngine) + dec.countBuf = make([]byte, 9) // counts may be uint64s (unlikely!), require 9 bytes + + return dec +} + +// recvType loads the definition of a type. +func (dec *Decoder) recvType(id typeId) { + // Have we already seen this type? That's an error + if id < firstUserId || dec.wireType[id] != nil { + dec.err = os.NewError("gob: duplicate type received") + return + } + + // Type: + wire := new(wireType) + dec.decodeValue(tWireType, reflect.ValueOf(wire)) + if dec.err != nil { + return + } + // Remember we've seen this type. + dec.wireType[id] = wire +} + +// recvMessage reads the next count-delimited item from the input. It is the converse +// of Encoder.writeMessage. It returns false on EOF or other error reading the message. +func (dec *Decoder) recvMessage() bool { + // Read a count. + nbytes, _, err := decodeUintReader(dec.r, dec.countBuf) + if err != nil { + dec.err = err + return false + } + dec.readMessage(int(nbytes)) + return dec.err == nil +} + +// 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 + } + dec.tmp = dec.tmp[:nbytes] + + // Read the data + _, dec.err = io.ReadFull(dec.r, dec.tmp) + if dec.err != nil { + if dec.err == os.EOF { + dec.err = io.ErrUnexpectedEOF + } + return + } + dec.buf.Write(dec.tmp) +} + +// toInt turns an encoded uint64 into an int, according to the marshaling rules. +func toInt(x uint64) int64 { + i := int64(x >> 1) + if x&1 != 0 { + i = ^i + } + return i +} + +func (dec *Decoder) nextInt() int64 { + n, _, err := decodeUintReader(&dec.buf, dec.countBuf) + if err != nil { + dec.err = err + } + return toInt(n) +} + +func (dec *Decoder) nextUint() uint64 { + n, _, err := decodeUintReader(&dec.buf, dec.countBuf) + if err != nil { + dec.err = err + } + return n +} + +// decodeTypeSequence parses: +// TypeSequence +// (TypeDefinition DelimitedTypeDefinition*)? +// and returns the type id of the next value. It returns -1 at +// EOF. Upon return, the remainder of dec.buf is the value to be +// decoded. If this is an interface value, it can be ignored by +// simply resetting that buffer. +func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId { + for dec.err == nil { + if dec.buf.Len() == 0 { + if !dec.recvMessage() { + break + } + } + // Receive a type id. + id := typeId(dec.nextInt()) + if id >= 0 { + // Value follows. + return id + } + // Type definition for (-id) follows. + dec.recvType(-id) + // When decoding an interface, after a type there may be a + // DelimitedValue still in the buffer. Skip its count. + // (Alternatively, the buffer is empty and the byte count + // will be absorbed by recvMessage.) + if dec.buf.Len() > 0 { + if !isInterface { + dec.err = os.NewError("extra data in buffer") + break + } + dec.nextUint() + } + } + return -1 +} + +// Decode reads the next value from the connection and stores +// it in the data represented by the empty interface value. +// If e is nil, the value will be discarded. Otherwise, +// the value underlying e must either be the correct type for the next +// data item received, and must be a pointer. +func (dec *Decoder) Decode(e interface{}) os.Error { + if e == nil { + return dec.DecodeValue(reflect.Value{}) + } + value := reflect.ValueOf(e) + // If e represents a value as opposed to a pointer, the answer won't + // get back to the caller. Make sure it's a pointer. + if value.Type().Kind() != reflect.Ptr { + dec.err = os.NewError("gob: attempt to decode into a non-pointer") + return dec.err + } + return dec.DecodeValue(value) +} + +// DecodeValue reads the next value from the connection and stores +// it in the data represented by the reflection value. +// The value must be the correct type for the next +// data item received, or it may be nil, which means the +// value will be discarded. +func (dec *Decoder) DecodeValue(value reflect.Value) os.Error { + // Make sure we're single-threaded through here. + dec.mutex.Lock() + defer dec.mutex.Unlock() + + dec.buf.Reset() // In case data lingers from previous invocation. + dec.err = nil + id := dec.decodeTypeSequence(false) + if dec.err == nil { + dec.decodeValue(id, value) + } + return dec.err +} + +// If debug.go is compiled into the program , debugFunc prints a human-readable +// representation of the gob data read from r by calling that file's Debug function. +// Otherwise it is nil. +var debugFunc func(io.Reader) diff --git a/src/cmd/fix/testdata/reflect.dnsmsg.go.in b/src/cmd/fix/testdata/reflect.dnsmsg.go.in new file mode 100644 index 000000000..3d9c312f2 --- /dev/null +++ b/src/cmd/fix/testdata/reflect.dnsmsg.go.in @@ -0,0 +1,777 @@ +// 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. + +// DNS packet assembly. See RFC 1035. +// +// This is intended to support name resolution during net.Dial. +// It doesn't have to be blazing fast. +// +// Rather than write the usual handful of routines to pack and +// unpack every message that can appear on the wire, we use +// reflection to write a generic pack/unpack for structs and then +// use it. Thus, if in the future we need to define new message +// structs, no new pack/unpack/printing code needs to be written. +// +// The first half of this file defines the DNS message formats. +// The second half implements the conversion to and from wire format. +// A few of the structure elements have string tags to aid the +// generic pack/unpack routines. +// +// TODO(rsc): There are enough names defined in this file that they're all +// prefixed with dns. Perhaps put this in its own package later. + +package net + +import ( + "fmt" + "os" + "reflect" +) + +// Packet formats + +// Wire constants. +const ( + // valid dnsRR_Header.Rrtype and dnsQuestion.qtype + dnsTypeA = 1 + dnsTypeNS = 2 + dnsTypeMD = 3 + dnsTypeMF = 4 + dnsTypeCNAME = 5 + dnsTypeSOA = 6 + dnsTypeMB = 7 + dnsTypeMG = 8 + dnsTypeMR = 9 + dnsTypeNULL = 10 + dnsTypeWKS = 11 + dnsTypePTR = 12 + dnsTypeHINFO = 13 + dnsTypeMINFO = 14 + dnsTypeMX = 15 + dnsTypeTXT = 16 + dnsTypeAAAA = 28 + dnsTypeSRV = 33 + + // valid dnsQuestion.qtype only + dnsTypeAXFR = 252 + dnsTypeMAILB = 253 + dnsTypeMAILA = 254 + dnsTypeALL = 255 + + // valid dnsQuestion.qclass + dnsClassINET = 1 + dnsClassCSNET = 2 + dnsClassCHAOS = 3 + dnsClassHESIOD = 4 + dnsClassANY = 255 + + // dnsMsg.rcode + dnsRcodeSuccess = 0 + dnsRcodeFormatError = 1 + dnsRcodeServerFailure = 2 + dnsRcodeNameError = 3 + dnsRcodeNotImplemented = 4 + dnsRcodeRefused = 5 +) + +// The wire format for the DNS packet header. +type dnsHeader struct { + Id uint16 + Bits uint16 + Qdcount, Ancount, Nscount, Arcount uint16 +} + +const ( + // dnsHeader.Bits + _QR = 1 << 15 // query/response (response=1) + _AA = 1 << 10 // authoritative + _TC = 1 << 9 // truncated + _RD = 1 << 8 // recursion desired + _RA = 1 << 7 // recursion available +) + +// DNS queries. +type dnsQuestion struct { + Name string "domain-name" // "domain-name" specifies encoding; see packers below + Qtype uint16 + Qclass uint16 +} + +// DNS responses (resource records). +// There are many types of messages, +// but they all share the same header. +type dnsRR_Header struct { + Name string "domain-name" + Rrtype uint16 + Class uint16 + Ttl uint32 + Rdlength uint16 // length of data after header +} + +func (h *dnsRR_Header) Header() *dnsRR_Header { + return h +} + +type dnsRR interface { + Header() *dnsRR_Header +} + +// Specific DNS RR formats for each query type. + +type dnsRR_CNAME struct { + Hdr dnsRR_Header + Cname string "domain-name" +} + +func (rr *dnsRR_CNAME) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_HINFO struct { + Hdr dnsRR_Header + Cpu string + Os string +} + +func (rr *dnsRR_HINFO) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MB struct { + Hdr dnsRR_Header + Mb string "domain-name" +} + +func (rr *dnsRR_MB) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MG struct { + Hdr dnsRR_Header + Mg string "domain-name" +} + +func (rr *dnsRR_MG) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MINFO struct { + Hdr dnsRR_Header + Rmail string "domain-name" + Email string "domain-name" +} + +func (rr *dnsRR_MINFO) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MR struct { + Hdr dnsRR_Header + Mr string "domain-name" +} + +func (rr *dnsRR_MR) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MX struct { + Hdr dnsRR_Header + Pref uint16 + Mx string "domain-name" +} + +func (rr *dnsRR_MX) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_NS struct { + Hdr dnsRR_Header + Ns string "domain-name" +} + +func (rr *dnsRR_NS) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_PTR struct { + Hdr dnsRR_Header + Ptr string "domain-name" +} + +func (rr *dnsRR_PTR) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_SOA struct { + Hdr dnsRR_Header + Ns string "domain-name" + Mbox string "domain-name" + Serial uint32 + Refresh uint32 + Retry uint32 + Expire uint32 + Minttl uint32 +} + +func (rr *dnsRR_SOA) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_TXT struct { + Hdr dnsRR_Header + Txt string // not domain name +} + +func (rr *dnsRR_TXT) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_SRV struct { + Hdr dnsRR_Header + Priority uint16 + Weight uint16 + Port uint16 + Target string "domain-name" +} + +func (rr *dnsRR_SRV) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_A struct { + Hdr dnsRR_Header + A uint32 "ipv4" +} + +func (rr *dnsRR_A) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_AAAA struct { + Hdr dnsRR_Header + AAAA [16]byte "ipv6" +} + +func (rr *dnsRR_AAAA) Header() *dnsRR_Header { + return &rr.Hdr +} + +// Packing and unpacking. +// +// All the packers and unpackers take a (msg []byte, off int) +// and return (off1 int, ok bool). If they return ok==false, they +// also return off1==len(msg), so that the next unpacker will +// also fail. This lets us avoid checks of ok until the end of a +// packing sequence. + +// Map of constructors for each RR wire type. +var rr_mk = map[int]func() dnsRR{ + dnsTypeCNAME: func() dnsRR { return new(dnsRR_CNAME) }, + dnsTypeHINFO: func() dnsRR { return new(dnsRR_HINFO) }, + dnsTypeMB: func() dnsRR { return new(dnsRR_MB) }, + dnsTypeMG: func() dnsRR { return new(dnsRR_MG) }, + dnsTypeMINFO: func() dnsRR { return new(dnsRR_MINFO) }, + dnsTypeMR: func() dnsRR { return new(dnsRR_MR) }, + dnsTypeMX: func() dnsRR { return new(dnsRR_MX) }, + dnsTypeNS: func() dnsRR { return new(dnsRR_NS) }, + dnsTypePTR: func() dnsRR { return new(dnsRR_PTR) }, + dnsTypeSOA: func() dnsRR { return new(dnsRR_SOA) }, + dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) }, + dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) }, + dnsTypeA: func() dnsRR { return new(dnsRR_A) }, + dnsTypeAAAA: func() dnsRR { return new(dnsRR_AAAA) }, +} + +// Pack a domain name s into msg[off:]. +// Domain names are a sequence of counted strings +// split at the dots. They end with a zero-length string. +func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { + // Add trailing dot to canonicalize name. + if n := len(s); n == 0 || s[n-1] != '.' { + s += "." + } + + // Each dot ends a segment of the name. + // We trade each dot byte for a length byte. + // There is also a trailing zero. + // Check that we have all the space we need. + tot := len(s) + 1 + if off+tot > len(msg) { + return len(msg), false + } + + // Emit sequence of counted strings, chopping at dots. + begin := 0 + for i := 0; i < len(s); i++ { + if s[i] == '.' { + if i-begin >= 1<<6 { // top two bits of length must be clear + return len(msg), false + } + msg[off] = byte(i - begin) + off++ + for j := begin; j < i; j++ { + msg[off] = s[j] + off++ + } + begin = i + 1 + } + } + msg[off] = 0 + off++ + return off, true +} + +// Unpack a domain name. +// In addition to the simple sequences of counted strings above, +// domain names are allowed to refer to strings elsewhere in the +// packet, to avoid repeating common suffixes when returning +// many entries in a single domain. The pointers are marked +// by a length byte with the top two bits set. Ignoring those +// two bits, that byte and the next give a 14 bit offset from msg[0] +// where we should pick up the trail. +// Note that if we jump elsewhere in the packet, +// we return off1 == the offset after the first pointer we found, +// which is where the next record will start. +// In theory, the pointers are only allowed to jump backward. +// We let them jump anywhere and stop jumping after a while. +func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { + s = "" + ptr := 0 // number of pointers followed +Loop: + for { + if off >= len(msg) { + return "", len(msg), false + } + c := int(msg[off]) + off++ + switch c & 0xC0 { + case 0x00: + if c == 0x00 { + // end of name + break Loop + } + // literal string + if off+c > len(msg) { + return "", len(msg), false + } + s += string(msg[off:off+c]) + "." + off += c + case 0xC0: + // pointer to somewhere else in msg. + // remember location after first ptr, + // since that's how many bytes we consumed. + // also, don't follow too many pointers -- + // maybe there's a loop. + if off >= len(msg) { + return "", len(msg), false + } + c1 := msg[off] + off++ + if ptr == 0 { + off1 = off + } + if ptr++; ptr > 10 { + return "", len(msg), false + } + off = (c^0xC0)<<8 | int(c1) + default: + // 0x80 and 0x40 are reserved + return "", len(msg), false + } + } + if ptr == 0 { + off1 = off + } + return s, off1, true +} + +// TODO(rsc): Move into generic library? +// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string, +// [n]byte, and other (often anonymous) structs. +func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { + for i := 0; i < val.NumField(); i++ { + f := val.Type().(*reflect.StructType).Field(i) + switch fv := val.Field(i).(type) { + default: + BadType: + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) + return len(msg), false + case *reflect.StructValue: + off, ok = packStructValue(fv, msg, off) + case *reflect.UintValue: + i := fv.Get() + switch fv.Type().Kind() { + default: + goto BadType + case reflect.Uint16: + if off+2 > len(msg) { + return len(msg), false + } + msg[off] = byte(i >> 8) + msg[off+1] = byte(i) + off += 2 + case reflect.Uint32: + if off+4 > len(msg) { + return len(msg), false + } + msg[off] = byte(i >> 24) + msg[off+1] = byte(i >> 16) + msg[off+2] = byte(i >> 8) + msg[off+3] = byte(i) + off += 4 + } + case *reflect.ArrayValue: + if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 { + goto BadType + } + n := fv.Len() + if off+n > len(msg) { + return len(msg), false + } + reflect.Copy(reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue), fv) + off += n + case *reflect.StringValue: + // There are multiple string encodings. + // The tag distinguishes ordinary strings from domain names. + s := fv.Get() + switch f.Tag { + default: + fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) + return len(msg), false + case "domain-name": + off, ok = packDomainName(s, msg, off) + if !ok { + return len(msg), false + } + case "": + // Counted string: 1 byte length. + if len(s) > 255 || off+1+len(s) > len(msg) { + return len(msg), false + } + msg[off] = byte(len(s)) + off++ + off += copy(msg[off:], s) + } + } + } + return off, true +} + +func structValue(any interface{}) *reflect.StructValue { + return reflect.NewValue(any).(*reflect.PtrValue).Elem().(*reflect.StructValue) +} + +func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { + off, ok = packStructValue(structValue(any), msg, off) + return off, ok +} + +// TODO(rsc): Move into generic library? +// Unpack a reflect.StructValue from msg. +// Same restrictions as packStructValue. +func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { + for i := 0; i < val.NumField(); i++ { + f := val.Type().(*reflect.StructType).Field(i) + switch fv := val.Field(i).(type) { + default: + BadType: + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) + return len(msg), false + case *reflect.StructValue: + off, ok = unpackStructValue(fv, msg, off) + case *reflect.UintValue: + switch fv.Type().Kind() { + default: + goto BadType + case reflect.Uint16: + if off+2 > len(msg) { + return len(msg), false + } + i := uint16(msg[off])<<8 | uint16(msg[off+1]) + fv.Set(uint64(i)) + off += 2 + case reflect.Uint32: + if off+4 > len(msg) { + return len(msg), false + } + i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]) + fv.Set(uint64(i)) + off += 4 + } + case *reflect.ArrayValue: + if fv.Type().(*reflect.ArrayType).Elem().Kind() != reflect.Uint8 { + goto BadType + } + n := fv.Len() + if off+n > len(msg) { + return len(msg), false + } + reflect.Copy(fv, reflect.NewValue(msg[off:off+n]).(*reflect.SliceValue)) + off += n + case *reflect.StringValue: + var s string + switch f.Tag { + default: + fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) + return len(msg), false + case "domain-name": + s, off, ok = unpackDomainName(msg, off) + if !ok { + return len(msg), false + } + case "": + if off >= len(msg) || off+1+int(msg[off]) > len(msg) { + return len(msg), false + } + n := int(msg[off]) + off++ + b := make([]byte, n) + for i := 0; i < n; i++ { + b[i] = msg[off+i] + } + off += n + s = string(b) + } + fv.Set(s) + } + } + return off, true +} + +func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { + off, ok = unpackStructValue(structValue(any), msg, off) + return off, ok +} + +// Generic struct printer. +// Doesn't care about the string tag "domain-name", +// but does look for an "ipv4" tag on uint32 variables +// and the "ipv6" tag on array variables, +// printing them as IP addresses. +func printStructValue(val *reflect.StructValue) string { + s := "{" + for i := 0; i < val.NumField(); i++ { + if i > 0 { + s += ", " + } + f := val.Type().(*reflect.StructType).Field(i) + if !f.Anonymous { + s += f.Name + "=" + } + fval := val.Field(i) + if fv, ok := fval.(*reflect.StructValue); ok { + s += printStructValue(fv) + } else if fv, ok := fval.(*reflect.UintValue); ok && f.Tag == "ipv4" { + i := fv.Get() + s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() + } else if fv, ok := fval.(*reflect.ArrayValue); ok && f.Tag == "ipv6" { + i := fv.Interface().([]byte) + s += IP(i).String() + } else { + s += fmt.Sprint(fval.Interface()) + } + } + s += "}" + return s +} + +func printStruct(any interface{}) string { return printStructValue(structValue(any)) } + +// Resource record packer. +func packRR(rr dnsRR, msg []byte, off int) (off2 int, ok bool) { + var off1 int + // pack twice, once to find end of header + // and again to find end of packet. + // a bit inefficient but this doesn't need to be fast. + // off1 is end of header + // off2 is end of rr + off1, ok = packStruct(rr.Header(), msg, off) + off2, ok = packStruct(rr, msg, off) + if !ok { + return len(msg), false + } + // pack a third time; redo header with correct data length + rr.Header().Rdlength = uint16(off2 - off1) + packStruct(rr.Header(), msg, off) + return off2, true +} + +// Resource record unpacker. +func unpackRR(msg []byte, off int) (rr dnsRR, off1 int, ok bool) { + // unpack just the header, to find the rr type and length + var h dnsRR_Header + off0 := off + if off, ok = unpackStruct(&h, msg, off); !ok { + return nil, len(msg), false + } + end := off + int(h.Rdlength) + + // make an rr of that type and re-unpack. + // again inefficient but doesn't need to be fast. + mk, known := rr_mk[int(h.Rrtype)] + if !known { + return &h, end, true + } + rr = mk() + off, ok = unpackStruct(rr, msg, off0) + if off != end { + return &h, end, true + } + return rr, off, ok +} + +// Usable representation of a DNS packet. + +// A manually-unpacked version of (id, bits). +// This is in its own struct for easy printing. +type dnsMsgHdr struct { + id uint16 + response bool + opcode int + authoritative bool + truncated bool + recursion_desired bool + recursion_available bool + rcode int +} + +type dnsMsg struct { + dnsMsgHdr + question []dnsQuestion + answer []dnsRR + ns []dnsRR + extra []dnsRR +} + +func (dns *dnsMsg) Pack() (msg []byte, ok bool) { + var dh dnsHeader + + // Convert convenient dnsMsg into wire-like dnsHeader. + dh.Id = dns.id + dh.Bits = uint16(dns.opcode)<<11 | uint16(dns.rcode) + if dns.recursion_available { + dh.Bits |= _RA + } + if dns.recursion_desired { + dh.Bits |= _RD + } + if dns.truncated { + dh.Bits |= _TC + } + if dns.authoritative { + dh.Bits |= _AA + } + if dns.response { + dh.Bits |= _QR + } + + // Prepare variable sized arrays. + question := dns.question + answer := dns.answer + ns := dns.ns + extra := dns.extra + + dh.Qdcount = uint16(len(question)) + dh.Ancount = uint16(len(answer)) + dh.Nscount = uint16(len(ns)) + dh.Arcount = uint16(len(extra)) + + // Could work harder to calculate message size, + // but this is far more than we need and not + // big enough to hurt the allocator. + msg = make([]byte, 2000) + + // Pack it in: header and then the pieces. + off := 0 + off, ok = packStruct(&dh, msg, off) + for i := 0; i < len(question); i++ { + off, ok = packStruct(&question[i], msg, off) + } + for i := 0; i < len(answer); i++ { + off, ok = packRR(answer[i], msg, off) + } + for i := 0; i < len(ns); i++ { + off, ok = packRR(ns[i], msg, off) + } + for i := 0; i < len(extra); i++ { + off, ok = packRR(extra[i], msg, off) + } + if !ok { + return nil, false + } + return msg[0:off], true +} + +func (dns *dnsMsg) Unpack(msg []byte) bool { + // Header. + var dh dnsHeader + off := 0 + var ok bool + if off, ok = unpackStruct(&dh, msg, off); !ok { + return false + } + dns.id = dh.Id + dns.response = (dh.Bits & _QR) != 0 + dns.opcode = int(dh.Bits>>11) & 0xF + dns.authoritative = (dh.Bits & _AA) != 0 + dns.truncated = (dh.Bits & _TC) != 0 + dns.recursion_desired = (dh.Bits & _RD) != 0 + dns.recursion_available = (dh.Bits & _RA) != 0 + dns.rcode = int(dh.Bits & 0xF) + + // Arrays. + dns.question = make([]dnsQuestion, dh.Qdcount) + dns.answer = make([]dnsRR, dh.Ancount) + dns.ns = make([]dnsRR, dh.Nscount) + dns.extra = make([]dnsRR, dh.Arcount) + + for i := 0; i < len(dns.question); i++ { + off, ok = unpackStruct(&dns.question[i], msg, off) + } + for i := 0; i < len(dns.answer); i++ { + dns.answer[i], off, ok = unpackRR(msg, off) + } + for i := 0; i < len(dns.ns); i++ { + dns.ns[i], off, ok = unpackRR(msg, off) + } + for i := 0; i < len(dns.extra); i++ { + dns.extra[i], off, ok = unpackRR(msg, off) + } + if !ok { + return false + } + // if off != len(msg) { + // println("extra bytes in dns packet", off, "<", len(msg)); + // } + return true +} + +func (dns *dnsMsg) String() string { + s := "DNS: " + printStruct(&dns.dnsMsgHdr) + "\n" + if len(dns.question) > 0 { + s += "-- Questions\n" + for i := 0; i < len(dns.question); i++ { + s += printStruct(&dns.question[i]) + "\n" + } + } + if len(dns.answer) > 0 { + s += "-- Answers\n" + for i := 0; i < len(dns.answer); i++ { + s += printStruct(dns.answer[i]) + "\n" + } + } + if len(dns.ns) > 0 { + s += "-- Name servers\n" + for i := 0; i < len(dns.ns); i++ { + s += printStruct(dns.ns[i]) + "\n" + } + } + if len(dns.extra) > 0 { + s += "-- Extra\n" + for i := 0; i < len(dns.extra); i++ { + s += printStruct(dns.extra[i]) + "\n" + } + } + return s +} diff --git a/src/cmd/fix/testdata/reflect.dnsmsg.go.out b/src/cmd/fix/testdata/reflect.dnsmsg.go.out new file mode 100644 index 000000000..c777fe27c --- /dev/null +++ b/src/cmd/fix/testdata/reflect.dnsmsg.go.out @@ -0,0 +1,777 @@ +// 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. + +// DNS packet assembly. See RFC 1035. +// +// This is intended to support name resolution during net.Dial. +// It doesn't have to be blazing fast. +// +// Rather than write the usual handful of routines to pack and +// unpack every message that can appear on the wire, we use +// reflection to write a generic pack/unpack for structs and then +// use it. Thus, if in the future we need to define new message +// structs, no new pack/unpack/printing code needs to be written. +// +// The first half of this file defines the DNS message formats. +// The second half implements the conversion to and from wire format. +// A few of the structure elements have string tags to aid the +// generic pack/unpack routines. +// +// TODO(rsc): There are enough names defined in this file that they're all +// prefixed with dns. Perhaps put this in its own package later. + +package net + +import ( + "fmt" + "os" + "reflect" +) + +// Packet formats + +// Wire constants. +const ( + // valid dnsRR_Header.Rrtype and dnsQuestion.qtype + dnsTypeA = 1 + dnsTypeNS = 2 + dnsTypeMD = 3 + dnsTypeMF = 4 + dnsTypeCNAME = 5 + dnsTypeSOA = 6 + dnsTypeMB = 7 + dnsTypeMG = 8 + dnsTypeMR = 9 + dnsTypeNULL = 10 + dnsTypeWKS = 11 + dnsTypePTR = 12 + dnsTypeHINFO = 13 + dnsTypeMINFO = 14 + dnsTypeMX = 15 + dnsTypeTXT = 16 + dnsTypeAAAA = 28 + dnsTypeSRV = 33 + + // valid dnsQuestion.qtype only + dnsTypeAXFR = 252 + dnsTypeMAILB = 253 + dnsTypeMAILA = 254 + dnsTypeALL = 255 + + // valid dnsQuestion.qclass + dnsClassINET = 1 + dnsClassCSNET = 2 + dnsClassCHAOS = 3 + dnsClassHESIOD = 4 + dnsClassANY = 255 + + // dnsMsg.rcode + dnsRcodeSuccess = 0 + dnsRcodeFormatError = 1 + dnsRcodeServerFailure = 2 + dnsRcodeNameError = 3 + dnsRcodeNotImplemented = 4 + dnsRcodeRefused = 5 +) + +// The wire format for the DNS packet header. +type dnsHeader struct { + Id uint16 + Bits uint16 + Qdcount, Ancount, Nscount, Arcount uint16 +} + +const ( + // dnsHeader.Bits + _QR = 1 << 15 // query/response (response=1) + _AA = 1 << 10 // authoritative + _TC = 1 << 9 // truncated + _RD = 1 << 8 // recursion desired + _RA = 1 << 7 // recursion available +) + +// DNS queries. +type dnsQuestion struct { + Name string "domain-name" // "domain-name" specifies encoding; see packers below + Qtype uint16 + Qclass uint16 +} + +// DNS responses (resource records). +// There are many types of messages, +// but they all share the same header. +type dnsRR_Header struct { + Name string "domain-name" + Rrtype uint16 + Class uint16 + Ttl uint32 + Rdlength uint16 // length of data after header +} + +func (h *dnsRR_Header) Header() *dnsRR_Header { + return h +} + +type dnsRR interface { + Header() *dnsRR_Header +} + +// Specific DNS RR formats for each query type. + +type dnsRR_CNAME struct { + Hdr dnsRR_Header + Cname string "domain-name" +} + +func (rr *dnsRR_CNAME) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_HINFO struct { + Hdr dnsRR_Header + Cpu string + Os string +} + +func (rr *dnsRR_HINFO) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MB struct { + Hdr dnsRR_Header + Mb string "domain-name" +} + +func (rr *dnsRR_MB) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MG struct { + Hdr dnsRR_Header + Mg string "domain-name" +} + +func (rr *dnsRR_MG) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MINFO struct { + Hdr dnsRR_Header + Rmail string "domain-name" + Email string "domain-name" +} + +func (rr *dnsRR_MINFO) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MR struct { + Hdr dnsRR_Header + Mr string "domain-name" +} + +func (rr *dnsRR_MR) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_MX struct { + Hdr dnsRR_Header + Pref uint16 + Mx string "domain-name" +} + +func (rr *dnsRR_MX) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_NS struct { + Hdr dnsRR_Header + Ns string "domain-name" +} + +func (rr *dnsRR_NS) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_PTR struct { + Hdr dnsRR_Header + Ptr string "domain-name" +} + +func (rr *dnsRR_PTR) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_SOA struct { + Hdr dnsRR_Header + Ns string "domain-name" + Mbox string "domain-name" + Serial uint32 + Refresh uint32 + Retry uint32 + Expire uint32 + Minttl uint32 +} + +func (rr *dnsRR_SOA) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_TXT struct { + Hdr dnsRR_Header + Txt string // not domain name +} + +func (rr *dnsRR_TXT) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_SRV struct { + Hdr dnsRR_Header + Priority uint16 + Weight uint16 + Port uint16 + Target string "domain-name" +} + +func (rr *dnsRR_SRV) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_A struct { + Hdr dnsRR_Header + A uint32 "ipv4" +} + +func (rr *dnsRR_A) Header() *dnsRR_Header { + return &rr.Hdr +} + +type dnsRR_AAAA struct { + Hdr dnsRR_Header + AAAA [16]byte "ipv6" +} + +func (rr *dnsRR_AAAA) Header() *dnsRR_Header { + return &rr.Hdr +} + +// Packing and unpacking. +// +// All the packers and unpackers take a (msg []byte, off int) +// and return (off1 int, ok bool). If they return ok==false, they +// also return off1==len(msg), so that the next unpacker will +// also fail. This lets us avoid checks of ok until the end of a +// packing sequence. + +// Map of constructors for each RR wire type. +var rr_mk = map[int]func() dnsRR{ + dnsTypeCNAME: func() dnsRR { return new(dnsRR_CNAME) }, + dnsTypeHINFO: func() dnsRR { return new(dnsRR_HINFO) }, + dnsTypeMB: func() dnsRR { return new(dnsRR_MB) }, + dnsTypeMG: func() dnsRR { return new(dnsRR_MG) }, + dnsTypeMINFO: func() dnsRR { return new(dnsRR_MINFO) }, + dnsTypeMR: func() dnsRR { return new(dnsRR_MR) }, + dnsTypeMX: func() dnsRR { return new(dnsRR_MX) }, + dnsTypeNS: func() dnsRR { return new(dnsRR_NS) }, + dnsTypePTR: func() dnsRR { return new(dnsRR_PTR) }, + dnsTypeSOA: func() dnsRR { return new(dnsRR_SOA) }, + dnsTypeTXT: func() dnsRR { return new(dnsRR_TXT) }, + dnsTypeSRV: func() dnsRR { return new(dnsRR_SRV) }, + dnsTypeA: func() dnsRR { return new(dnsRR_A) }, + dnsTypeAAAA: func() dnsRR { return new(dnsRR_AAAA) }, +} + +// Pack a domain name s into msg[off:]. +// Domain names are a sequence of counted strings +// split at the dots. They end with a zero-length string. +func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { + // Add trailing dot to canonicalize name. + if n := len(s); n == 0 || s[n-1] != '.' { + s += "." + } + + // Each dot ends a segment of the name. + // We trade each dot byte for a length byte. + // There is also a trailing zero. + // Check that we have all the space we need. + tot := len(s) + 1 + if off+tot > len(msg) { + return len(msg), false + } + + // Emit sequence of counted strings, chopping at dots. + begin := 0 + for i := 0; i < len(s); i++ { + if s[i] == '.' { + if i-begin >= 1<<6 { // top two bits of length must be clear + return len(msg), false + } + msg[off] = byte(i - begin) + off++ + for j := begin; j < i; j++ { + msg[off] = s[j] + off++ + } + begin = i + 1 + } + } + msg[off] = 0 + off++ + return off, true +} + +// Unpack a domain name. +// In addition to the simple sequences of counted strings above, +// domain names are allowed to refer to strings elsewhere in the +// packet, to avoid repeating common suffixes when returning +// many entries in a single domain. The pointers are marked +// by a length byte with the top two bits set. Ignoring those +// two bits, that byte and the next give a 14 bit offset from msg[0] +// where we should pick up the trail. +// Note that if we jump elsewhere in the packet, +// we return off1 == the offset after the first pointer we found, +// which is where the next record will start. +// In theory, the pointers are only allowed to jump backward. +// We let them jump anywhere and stop jumping after a while. +func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { + s = "" + ptr := 0 // number of pointers followed +Loop: + for { + if off >= len(msg) { + return "", len(msg), false + } + c := int(msg[off]) + off++ + switch c & 0xC0 { + case 0x00: + if c == 0x00 { + // end of name + break Loop + } + // literal string + if off+c > len(msg) { + return "", len(msg), false + } + s += string(msg[off:off+c]) + "." + off += c + case 0xC0: + // pointer to somewhere else in msg. + // remember location after first ptr, + // since that's how many bytes we consumed. + // also, don't follow too many pointers -- + // maybe there's a loop. + if off >= len(msg) { + return "", len(msg), false + } + c1 := msg[off] + off++ + if ptr == 0 { + off1 = off + } + if ptr++; ptr > 10 { + return "", len(msg), false + } + off = (c^0xC0)<<8 | int(c1) + default: + // 0x80 and 0x40 are reserved + return "", len(msg), false + } + } + if ptr == 0 { + off1 = off + } + return s, off1, true +} + +// TODO(rsc): Move into generic library? +// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string, +// [n]byte, and other (often anonymous) structs. +func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) { + for i := 0; i < val.NumField(); i++ { + f := val.Type().Field(i) + switch fv := val.Field(i); fv.Kind() { + default: + BadType: + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) + return len(msg), false + case reflect.Struct: + off, ok = packStructValue(fv, msg, off) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + i := fv.Uint() + switch fv.Type().Kind() { + default: + goto BadType + case reflect.Uint16: + if off+2 > len(msg) { + return len(msg), false + } + msg[off] = byte(i >> 8) + msg[off+1] = byte(i) + off += 2 + case reflect.Uint32: + if off+4 > len(msg) { + return len(msg), false + } + msg[off] = byte(i >> 24) + msg[off+1] = byte(i >> 16) + msg[off+2] = byte(i >> 8) + msg[off+3] = byte(i) + off += 4 + } + case reflect.Array: + if fv.Type().Elem().Kind() != reflect.Uint8 { + goto BadType + } + n := fv.Len() + if off+n > len(msg) { + return len(msg), false + } + reflect.Copy(reflect.ValueOf(msg[off:off+n]), fv) + off += n + case reflect.String: + // There are multiple string encodings. + // The tag distinguishes ordinary strings from domain names. + s := fv.String() + switch f.Tag { + default: + fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) + return len(msg), false + case "domain-name": + off, ok = packDomainName(s, msg, off) + if !ok { + return len(msg), false + } + case "": + // Counted string: 1 byte length. + if len(s) > 255 || off+1+len(s) > len(msg) { + return len(msg), false + } + msg[off] = byte(len(s)) + off++ + off += copy(msg[off:], s) + } + } + } + return off, true +} + +func structValue(any interface{}) reflect.Value { + return reflect.ValueOf(any).Elem() +} + +func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { + off, ok = packStructValue(structValue(any), msg, off) + return off, ok +} + +// TODO(rsc): Move into generic library? +// Unpack a reflect.StructValue from msg. +// Same restrictions as packStructValue. +func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) { + for i := 0; i < val.NumField(); i++ { + f := val.Type().Field(i) + switch fv := val.Field(i); fv.Kind() { + default: + BadType: + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) + return len(msg), false + case reflect.Struct: + off, ok = unpackStructValue(fv, msg, off) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch fv.Type().Kind() { + default: + goto BadType + case reflect.Uint16: + if off+2 > len(msg) { + return len(msg), false + } + i := uint16(msg[off])<<8 | uint16(msg[off+1]) + fv.SetUint(uint64(i)) + off += 2 + case reflect.Uint32: + if off+4 > len(msg) { + return len(msg), false + } + i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]) + fv.SetUint(uint64(i)) + off += 4 + } + case reflect.Array: + if fv.Type().Elem().Kind() != reflect.Uint8 { + goto BadType + } + n := fv.Len() + if off+n > len(msg) { + return len(msg), false + } + reflect.Copy(fv, reflect.ValueOf(msg[off:off+n])) + off += n + case reflect.String: + var s string + switch f.Tag { + default: + fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) + return len(msg), false + case "domain-name": + s, off, ok = unpackDomainName(msg, off) + if !ok { + return len(msg), false + } + case "": + if off >= len(msg) || off+1+int(msg[off]) > len(msg) { + return len(msg), false + } + n := int(msg[off]) + off++ + b := make([]byte, n) + for i := 0; i < n; i++ { + b[i] = msg[off+i] + } + off += n + s = string(b) + } + fv.SetString(s) + } + } + return off, true +} + +func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { + off, ok = unpackStructValue(structValue(any), msg, off) + return off, ok +} + +// Generic struct printer. +// Doesn't care about the string tag "domain-name", +// but does look for an "ipv4" tag on uint32 variables +// and the "ipv6" tag on array variables, +// printing them as IP addresses. +func printStructValue(val reflect.Value) string { + s := "{" + for i := 0; i < val.NumField(); i++ { + if i > 0 { + s += ", " + } + f := val.Type().Field(i) + if !f.Anonymous { + s += f.Name + "=" + } + fval := val.Field(i) + if fv := fval; fv.Kind() == reflect.Struct { + s += printStructValue(fv) + } else if fv := fval; (fv.Kind() == reflect.Uint || fv.Kind() == reflect.Uint8 || fv.Kind() == reflect.Uint16 || fv.Kind() == reflect.Uint32 || fv.Kind() == reflect.Uint64 || fv.Kind() == reflect.Uintptr) && f.Tag == "ipv4" { + i := fv.Uint() + s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() + } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == "ipv6" { + i := fv.Interface().([]byte) + s += IP(i).String() + } else { + s += fmt.Sprint(fval.Interface()) + } + } + s += "}" + return s +} + +func printStruct(any interface{}) string { return printStructValue(structValue(any)) } + +// Resource record packer. +func packRR(rr dnsRR, msg []byte, off int) (off2 int, ok bool) { + var off1 int + // pack twice, once to find end of header + // and again to find end of packet. + // a bit inefficient but this doesn't need to be fast. + // off1 is end of header + // off2 is end of rr + off1, ok = packStruct(rr.Header(), msg, off) + off2, ok = packStruct(rr, msg, off) + if !ok { + return len(msg), false + } + // pack a third time; redo header with correct data length + rr.Header().Rdlength = uint16(off2 - off1) + packStruct(rr.Header(), msg, off) + return off2, true +} + +// Resource record unpacker. +func unpackRR(msg []byte, off int) (rr dnsRR, off1 int, ok bool) { + // unpack just the header, to find the rr type and length + var h dnsRR_Header + off0 := off + if off, ok = unpackStruct(&h, msg, off); !ok { + return nil, len(msg), false + } + end := off + int(h.Rdlength) + + // make an rr of that type and re-unpack. + // again inefficient but doesn't need to be fast. + mk, known := rr_mk[int(h.Rrtype)] + if !known { + return &h, end, true + } + rr = mk() + off, ok = unpackStruct(rr, msg, off0) + if off != end { + return &h, end, true + } + return rr, off, ok +} + +// Usable representation of a DNS packet. + +// A manually-unpacked version of (id, bits). +// This is in its own struct for easy printing. +type dnsMsgHdr struct { + id uint16 + response bool + opcode int + authoritative bool + truncated bool + recursion_desired bool + recursion_available bool + rcode int +} + +type dnsMsg struct { + dnsMsgHdr + question []dnsQuestion + answer []dnsRR + ns []dnsRR + extra []dnsRR +} + +func (dns *dnsMsg) Pack() (msg []byte, ok bool) { + var dh dnsHeader + + // Convert convenient dnsMsg into wire-like dnsHeader. + dh.Id = dns.id + dh.Bits = uint16(dns.opcode)<<11 | uint16(dns.rcode) + if dns.recursion_available { + dh.Bits |= _RA + } + if dns.recursion_desired { + dh.Bits |= _RD + } + if dns.truncated { + dh.Bits |= _TC + } + if dns.authoritative { + dh.Bits |= _AA + } + if dns.response { + dh.Bits |= _QR + } + + // Prepare variable sized arrays. + question := dns.question + answer := dns.answer + ns := dns.ns + extra := dns.extra + + dh.Qdcount = uint16(len(question)) + dh.Ancount = uint16(len(answer)) + dh.Nscount = uint16(len(ns)) + dh.Arcount = uint16(len(extra)) + + // Could work harder to calculate message size, + // but this is far more than we need and not + // big enough to hurt the allocator. + msg = make([]byte, 2000) + + // Pack it in: header and then the pieces. + off := 0 + off, ok = packStruct(&dh, msg, off) + for i := 0; i < len(question); i++ { + off, ok = packStruct(&question[i], msg, off) + } + for i := 0; i < len(answer); i++ { + off, ok = packRR(answer[i], msg, off) + } + for i := 0; i < len(ns); i++ { + off, ok = packRR(ns[i], msg, off) + } + for i := 0; i < len(extra); i++ { + off, ok = packRR(extra[i], msg, off) + } + if !ok { + return nil, false + } + return msg[0:off], true +} + +func (dns *dnsMsg) Unpack(msg []byte) bool { + // Header. + var dh dnsHeader + off := 0 + var ok bool + if off, ok = unpackStruct(&dh, msg, off); !ok { + return false + } + dns.id = dh.Id + dns.response = (dh.Bits & _QR) != 0 + dns.opcode = int(dh.Bits>>11) & 0xF + dns.authoritative = (dh.Bits & _AA) != 0 + dns.truncated = (dh.Bits & _TC) != 0 + dns.recursion_desired = (dh.Bits & _RD) != 0 + dns.recursion_available = (dh.Bits & _RA) != 0 + dns.rcode = int(dh.Bits & 0xF) + + // Arrays. + dns.question = make([]dnsQuestion, dh.Qdcount) + dns.answer = make([]dnsRR, dh.Ancount) + dns.ns = make([]dnsRR, dh.Nscount) + dns.extra = make([]dnsRR, dh.Arcount) + + for i := 0; i < len(dns.question); i++ { + off, ok = unpackStruct(&dns.question[i], msg, off) + } + for i := 0; i < len(dns.answer); i++ { + dns.answer[i], off, ok = unpackRR(msg, off) + } + for i := 0; i < len(dns.ns); i++ { + dns.ns[i], off, ok = unpackRR(msg, off) + } + for i := 0; i < len(dns.extra); i++ { + dns.extra[i], off, ok = unpackRR(msg, off) + } + if !ok { + return false + } + // if off != len(msg) { + // println("extra bytes in dns packet", off, "<", len(msg)); + // } + return true +} + +func (dns *dnsMsg) String() string { + s := "DNS: " + printStruct(&dns.dnsMsgHdr) + "\n" + if len(dns.question) > 0 { + s += "-- Questions\n" + for i := 0; i < len(dns.question); i++ { + s += printStruct(&dns.question[i]) + "\n" + } + } + if len(dns.answer) > 0 { + s += "-- Answers\n" + for i := 0; i < len(dns.answer); i++ { + s += printStruct(dns.answer[i]) + "\n" + } + } + if len(dns.ns) > 0 { + s += "-- Name servers\n" + for i := 0; i < len(dns.ns); i++ { + s += printStruct(dns.ns[i]) + "\n" + } + } + if len(dns.extra) > 0 { + s += "-- Extra\n" + for i := 0; i < len(dns.extra); i++ { + s += printStruct(dns.extra[i]) + "\n" + } + } + return s +} diff --git a/src/cmd/fix/testdata/reflect.encode.go.in b/src/cmd/fix/testdata/reflect.encode.go.in new file mode 100644 index 000000000..26ce47039 --- /dev/null +++ b/src/cmd/fix/testdata/reflect.encode.go.in @@ -0,0 +1,367 @@ +// 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. + +// The json package implements encoding and decoding of JSON objects as +// defined in RFC 4627. +package json + +import ( + "bytes" + "encoding/base64" + "os" + "reflect" + "runtime" + "sort" + "strconv" + "unicode" + "utf8" +) + +// Marshal returns the JSON encoding of v. +// +// Marshal traverses the value v recursively. +// If an encountered value implements the Marshaler interface, +// Marshal calls its MarshalJSON method to produce JSON. +// +// Otherwise, Marshal uses the following type-dependent default encodings: +// +// Boolean values encode as JSON booleans. +// +// Floating point and integer 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. +// +// Array and slice values encode as JSON arrays, except that +// []byte encodes as a base64-encoded string. +// +// Struct values encode as JSON objects. Each struct field becomes +// a member of the object. By default the object's key name is the +// struct field name. If the struct field has a non-empty tag consisting +// of only Unicode letters, digits, and underscores, that tag will be used +// as the name instead. Only exported fields will be encoded. +// +// Map values encode as JSON objects. +// The map's key type must be string; the object keys are used directly +// as map keys. +// +// Pointer values encode as the value pointed to. +// A nil pointer encodes as the null JSON object. +// +// Interface values encode as the value contained in the interface. +// A nil interface value encodes as the null JSON object. +// +// Channel, complex, and function values cannot be encoded in JSON. +// Attempting to encode such a value causes Marshal to return +// an InvalidTypeError. +// +// JSON cannot represent cyclic data structures and Marshal does not +// handle them. Passing cyclic structures to Marshal will result in +// an infinite recursion. +// +func Marshal(v interface{}) ([]byte, os.Error) { + e := &encodeState{} + err := e.marshal(v) + if err != nil { + return nil, err + } + return e.Bytes(), nil +} + +// MarshalIndent is like Marshal but applies Indent to format the output. +func MarshalIndent(v interface{}, prefix, indent string) ([]byte, os.Error) { + b, err := Marshal(v) + if err != nil { + return nil, err + } + var buf bytes.Buffer + err = Indent(&buf, b, prefix, indent) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// MarshalForHTML is like Marshal but applies HTMLEscape to the output. +func MarshalForHTML(v interface{}) ([]byte, os.Error) { + b, err := Marshal(v) + if err != nil { + return nil, err + } + var buf bytes.Buffer + HTMLEscape(&buf, b) + return buf.Bytes(), nil +} + +// HTMLEscape appends to dst the JSON-encoded src with <, >, and & +// characters inside string literals changed to \u003c, \u003e, \u0026 +// so that the JSON will be safe to embed inside HTML - - -EOF -} - -sub HtmlListingFooter { - return <<'EOF'; - - -EOF -} - -sub HtmlEscape { - my $text = shift; - $text =~ s/&/&/g; - $text =~ s//>/g; - return $text; -} - -# Returns the indentation of the line, if it has any non-whitespace -# characters. Otherwise, returns -1. -sub Indentation { - my $line = shift; - if (m/^(\s*)\S/) { - return length($1); - } else { - return -1; - } -} - -# Print source-listing for one routine -sub PrintSource { - my $prog = shift; - my $offset = shift; - my $routine = shift; - my $flat = shift; - my $cumulative = shift; - my $start_addr = shift; - my $end_addr = shift; - my $html = shift; - my $output = shift; - - # Disassemble all instructions (just to get line numbers) - my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr); - - # Hack 1: assume that the first source file encountered in the - # disassembly contains the routine - my $filename = undef; - for (my $i = 0; $i <= $#instructions; $i++) { - if ($instructions[$i]->[2] >= 0) { - $filename = $instructions[$i]->[1]; - last; - } - } - if (!defined($filename)) { - print STDERR "no filename found in $routine\n"; - return 0; - } - - # Hack 2: assume that the largest line number from $filename is the - # end of the procedure. This is typically safe since if P1 contains - # an inlined call to P2, then P2 usually occurs earlier in the - # source file. If this does not work, we might have to compute a - # density profile or just print all regions we find. - my $lastline = 0; - for (my $i = 0; $i <= $#instructions; $i++) { - my $f = $instructions[$i]->[1]; - my $l = $instructions[$i]->[2]; - if (($f eq $filename) && ($l > $lastline)) { - $lastline = $l; - } - } - - # Hack 3: assume the first source location from "filename" is the start of - # the source code. - my $firstline = 1; - for (my $i = 0; $i <= $#instructions; $i++) { - if ($instructions[$i]->[1] eq $filename) { - $firstline = $instructions[$i]->[2]; - last; - } - } - - # Hack 4: Extend last line forward until its indentation is less than - # the indentation we saw on $firstline - my $oldlastline = $lastline; - { - if (!open(FILE, "<$filename")) { - print STDERR "$filename: $!\n"; - return 0; - } - my $l = 0; - my $first_indentation = -1; - while () { - s/\r//g; # turn windows-looking lines into unix-looking lines - $l++; - my $indent = Indentation($_); - if ($l >= $firstline) { - if ($first_indentation < 0 && $indent >= 0) { - $first_indentation = $indent; - last if ($first_indentation == 0); - } - } - if ($l >= $lastline && $indent >= 0) { - if ($indent >= $first_indentation) { - $lastline = $l+1; - } else { - last; - } - } - } - close(FILE); - } - - # Assign all samples to the range $firstline,$lastline, - # Hack 4: If an instruction does not occur in the range, its samples - # are moved to the next instruction that occurs in the range. - my $samples1 = {}; # Map from line number to flat count - my $samples2 = {}; # Map from line number to cumulative count - my $running1 = 0; # Unassigned flat counts - my $running2 = 0; # Unassigned cumulative counts - my $total1 = 0; # Total flat counts - my $total2 = 0; # Total cumulative counts - my %disasm = (); # Map from line number to disassembly - my $running_disasm = ""; # Unassigned disassembly - my $skip_marker = "---\n"; - if ($html) { - $skip_marker = ""; - for (my $l = $firstline; $l <= $lastline; $l++) { - $disasm{$l} = ""; - } - } - foreach my $e (@instructions) { - # Add up counts for all address that fall inside this instruction - my $c1 = 0; - my $c2 = 0; - for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) { - $c1 += GetEntry($flat, $a); - $c2 += GetEntry($cumulative, $a); - } - - if ($html) { - $running_disasm .= sprintf(" %6s %6s \t\t%8s: %s\n", - HtmlPrintNumber($c1), - HtmlPrintNumber($c2), - $e->[0], - CleanDisassembly($e->[3])); - } - - $running1 += $c1; - $running2 += $c2; - $total1 += $c1; - $total2 += $c2; - my $file = $e->[1]; - my $line = $e->[2]; - if (($file eq $filename) && - ($line >= $firstline) && - ($line <= $lastline)) { - # Assign all accumulated samples to this line - AddEntry($samples1, $line, $running1); - AddEntry($samples2, $line, $running2); - $running1 = 0; - $running2 = 0; - if ($html) { - $disasm{$line} .= $running_disasm; - $running_disasm = ''; - } - } - } - - # Assign any leftover samples to $lastline - AddEntry($samples1, $lastline, $running1); - AddEntry($samples2, $lastline, $running2); - - if ($html) { - printf $output ( - "

%s

%s\n
\n" .
-      "Total:%6s %6s (flat / cumulative %s)\n",
-      HtmlEscape(ShortFunctionName($routine)),
-      HtmlEscape($filename),
-      Unparse($total1),
-      Unparse($total2),
-      Units());
-  } else {
-    printf $output (
-      "ROUTINE ====================== %s in %s\n" .
-      "%6s %6s Total %s (flat / cumulative)\n",
-      ShortFunctionName($routine),
-      $filename,
-      Unparse($total1),
-      Unparse($total2),
-      Units());
-  }
-  if (!open(FILE, "<$filename")) {
-    print STDERR "$filename: $!\n";
-    return 0;
-  }
-  my $l = 0;
-  while () {
-    s/\r//g;         # turn windows-looking lines into unix-looking lines
-    $l++;
-    if ($l >= $firstline - 5 &&
-        (($l <= $oldlastline + 5) || ($l <= $lastline))) {
-      chop;
-      my $text = $_;
-      if ($l == $firstline) { print $output $skip_marker; }
-      my $n1 = GetEntry($samples1, $l);
-      my $n2 = GetEntry($samples2, $l);
-      if ($html) {
-        my $dis = $disasm{$l};
-        if (!defined($dis) || $n1 + $n2 == 0) {
-          # No samples/disassembly for this source line
-          printf $output (
-            "%5d " .
-            "%6s %6s %s\n",
-            $l,
-            HtmlPrintNumber($n1),
-            HtmlPrintNumber($n2),
-            HtmlEscape($text));
-        } else {
-          printf $output (
-            "%5d " .
-            "%6s %6s %s" .
-            "%s\n",
-            $l,
-            HtmlPrintNumber($n1),
-            HtmlPrintNumber($n2),
-            HtmlEscape($text),
-            HtmlEscape($dis));
-        }
-      } else {
-        printf $output(
-          "%6s %6s %4d: %s\n",
-          UnparseAlt($n1),
-          UnparseAlt($n2),
-          $l,
-          $text);
-      }
-      if ($l == $lastline)  { print $output $skip_marker; }
-    };
-  }
-  close(FILE);
-  if ($html) {
-    print $output "
\n"; - } - return 1; -} - -# Return the source line for the specified file/linenumber. -# Returns undef if not found. -sub SourceLine { - my $file = shift; - my $line = shift; - - # Look in cache - if (!defined($main::source_cache{$file})) { - if (100 < scalar keys(%main::source_cache)) { - # Clear the cache when it gets too big - $main::source_cache = (); - } - - # Read all lines from the file - if (!open(FILE, "<$file")) { - print STDERR "$file: $!\n"; - $main::source_cache{$file} = []; # Cache the negative result - return undef; - } - my $lines = []; - push(@{$lines}, ""); # So we can use 1-based line numbers as indices - while () { - push(@{$lines}, $_); - } - close(FILE); - - # Save the lines in the cache - $main::source_cache{$file} = $lines; - } - - my $lines = $main::source_cache{$file}; - if (($line < 0) || ($line > $#{$lines})) { - return undef; - } else { - return $lines->[$line]; - } -} - -# Print disassembly for one routine with interspersed source if available -sub PrintDisassembledFunction { - my $prog = shift; - my $offset = shift; - my $routine = shift; - my $flat = shift; - my $cumulative = shift; - my $start_addr = shift; - my $end_addr = shift; - my $total = shift; - - # Disassemble all instructions - my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr); - - # Make array of counts per instruction - my @flat_count = (); - my @cum_count = (); - my $flat_total = 0; - my $cum_total = 0; - foreach my $e (@instructions) { - # Add up counts for all address that fall inside this instruction - my $c1 = 0; - my $c2 = 0; - for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) { - $c1 += GetEntry($flat, $a); - $c2 += GetEntry($cumulative, $a); - } - push(@flat_count, $c1); - push(@cum_count, $c2); - $flat_total += $c1; - $cum_total += $c2; - } - - # Print header with total counts - printf("ROUTINE ====================== %s\n" . - "%6s %6s %s (flat, cumulative) %.1f%% of total\n", - ShortFunctionName($routine), - Unparse($flat_total), - Unparse($cum_total), - Units(), - ($cum_total * 100.0) / $total); - - # Process instructions in order - my $current_file = ""; - for (my $i = 0; $i <= $#instructions; ) { - my $e = $instructions[$i]; - - # Print the new file name whenever we switch files - if ($e->[1] ne $current_file) { - $current_file = $e->[1]; - my $fname = $current_file; - $fname =~ s|^\./||; # Trim leading "./" - - # Shorten long file names - if (length($fname) >= 58) { - $fname = "..." . substr($fname, -55); - } - printf("-------------------- %s\n", $fname); - } - - # TODO: Compute range of lines to print together to deal with - # small reorderings. - my $first_line = $e->[2]; - my $last_line = $first_line; - my %flat_sum = (); - my %cum_sum = (); - for (my $l = $first_line; $l <= $last_line; $l++) { - $flat_sum{$l} = 0; - $cum_sum{$l} = 0; - } - - # Find run of instructions for this range of source lines - my $first_inst = $i; - while (($i <= $#instructions) && - ($instructions[$i]->[2] >= $first_line) && - ($instructions[$i]->[2] <= $last_line)) { - $e = $instructions[$i]; - $flat_sum{$e->[2]} += $flat_count[$i]; - $cum_sum{$e->[2]} += $cum_count[$i]; - $i++; - } - my $last_inst = $i - 1; - - # Print source lines - for (my $l = $first_line; $l <= $last_line; $l++) { - my $line = SourceLine($current_file, $l); - if (!defined($line)) { - $line = "?\n"; - next; - } else { - $line =~ s/^\s+//; - } - printf("%6s %6s %5d: %s", - UnparseAlt($flat_sum{$l}), - UnparseAlt($cum_sum{$l}), - $l, - $line); - } - - # Print disassembly - for (my $x = $first_inst; $x <= $last_inst; $x++) { - my $e = $instructions[$x]; - my $address = $e->[0]; - $address = AddressSub($address, $offset); # Make relative to section - $address =~ s/^0x//; - $address =~ s/^0*//; - - printf("%6s %6s %8s: %6s\n", - UnparseAlt($flat_count[$x]), - UnparseAlt($cum_count[$x]), - $address, - CleanDisassembly($e->[3])); - } - } -} - -# Print DOT graph -sub PrintDot { - my $prog = shift; - my $symbols = shift; - my $raw = shift; - my $flat = shift; - my $cumulative = shift; - my $overall_total = shift; - - # Get total - my $local_total = TotalProfile($flat); - my $nodelimit = int($main::opt_nodefraction * $local_total); - my $edgelimit = int($main::opt_edgefraction * $local_total); - my $nodecount = $main::opt_nodecount; - - # Find nodes to include - my @list = (sort { abs(GetEntry($cumulative, $b)) <=> - abs(GetEntry($cumulative, $a)) - || $a cmp $b } - keys(%{$cumulative})); - my $last = $nodecount - 1; - if ($last > $#list) { - $last = $#list; - } - while (($last >= 0) && - (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) { - $last--; - } - if ($last < 0) { - print STDERR "No nodes to print\n"; - cleanup(); - return 0; - } - - if ($nodelimit > 0 || $edgelimit > 0) { - printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n", - Unparse($nodelimit), Units(), - Unparse($edgelimit), Units()); - } - - # Open DOT output file - my $output; - if ($main::opt_gv) { - $output = "| $DOT -Tps2 >" . TempName($main::next_tmpfile, "ps"); - } elsif ($main::opt_ps) { - $output = "| $DOT -Tps2"; - } elsif ($main::opt_pdf) { - $output = "| $DOT -Tps2 | $PS2PDF - -"; - } elsif ($main::opt_web || $main::opt_svg) { - # We need to post-process the SVG, so write to a temporary file always. - $output = "| $DOT -Tsvg >" . TempName($main::next_tmpfile, "svg"); - } elsif ($main::opt_gif) { - $output = "| $DOT -Tgif"; - } else { - $output = ">&STDOUT"; - } - open(DOT, $output) || error("$output: $!\n"); - - # Title - printf DOT ("digraph \"%s; %s %s\" {\n", - $prog, - Unparse($overall_total), - Units()); - if ($main::opt_pdf) { - # The output is more printable if we set the page size for dot. - printf DOT ("size=\"8,11\"\n"); - } - printf DOT ("node [width=0.375,height=0.25];\n"); - - # Print legend - printf DOT ("Legend [shape=box,fontsize=24,shape=plaintext," . - "label=\"%s\\l%s\\l%s\\l%s\\l%s\\l\"];\n", - $prog, - sprintf("Total %s: %s", Units(), Unparse($overall_total)), - sprintf("Focusing on: %s", Unparse($local_total)), - sprintf("Dropped nodes with <= %s abs(%s)", - Unparse($nodelimit), Units()), - sprintf("Dropped edges with <= %s %s", - Unparse($edgelimit), Units()) - ); - - # Print nodes - my %node = (); - my $nextnode = 1; - foreach my $a (@list[0..$last]) { - # Pick font size - my $f = GetEntry($flat, $a); - my $c = GetEntry($cumulative, $a); - - my $fs = 8; - if ($local_total > 0) { - $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total))); - } - - $node{$a} = $nextnode++; - my $sym = $a; - $sym =~ s/\s+/\\n/g; - $sym =~ s/::/\\n/g; - - # Extra cumulative info to print for non-leaves - my $extra = ""; - if ($f != $c) { - $extra = sprintf("\\rof %s (%s)", - Unparse($c), - Percent($c, $overall_total)); - } - my $style = ""; - if ($main::opt_heapcheck) { - if ($f > 0) { - # make leak-causing nodes more visible (add a background) - $style = ",style=filled,fillcolor=gray" - } elsif ($f < 0) { - # make anti-leak-causing nodes (which almost never occur) - # stand out as well (triple border) - $style = ",peripheries=3" - } - } - - printf DOT ("N%d [label=\"%s\\n%s (%s)%s\\r" . - "\",shape=box,fontsize=%.1f%s];\n", - $node{$a}, - $sym, - Unparse($f), - Percent($f, $overall_total), - $extra, - $fs, - $style, - ); - } - - # Get edges and counts per edge - my %edge = (); - my $n; - foreach my $k (keys(%{$raw})) { - # TODO: omit low %age edges - $n = $raw->{$k}; - my @translated = TranslateStack($symbols, $k); - for (my $i = 1; $i <= $#translated; $i++) { - my $src = $translated[$i]; - my $dst = $translated[$i-1]; - #next if ($src eq $dst); # Avoid self-edges? - if (exists($node{$src}) && exists($node{$dst})) { - my $edge_label = "$src\001$dst"; - if (!exists($edge{$edge_label})) { - $edge{$edge_label} = 0; - } - $edge{$edge_label} += $n; - } - } - } - - # Print edges - foreach my $e (keys(%edge)) { - my @x = split(/\001/, $e); - $n = $edge{$e}; - - if (abs($n) > $edgelimit) { - # Compute line width based on edge count - my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0); - if ($fraction > 1) { $fraction = 1; } - my $w = $fraction * 2; - if ($w < 1 && ($main::opt_web || $main::opt_svg)) { - # SVG output treats line widths < 1 poorly. - $w = 1; - } - - # Dot sometimes segfaults if given edge weights that are too large, so - # we cap the weights at a large value - my $edgeweight = abs($n) ** 0.7; - if ($edgeweight > 100000) { $edgeweight = 100000; } - $edgeweight = int($edgeweight); - - my $style = sprintf("setlinewidth(%f)", $w); - if ($x[1] =~ m/\(inline\)/) { - $style .= ",dashed"; - } - - # Use a slightly squashed function of the edge count as the weight - printf DOT ("N%s -> N%s [label=%s, weight=%d, style=\"%s\"];\n", - $node{$x[0]}, - $node{$x[1]}, - Unparse($n), - $edgeweight, - $style); - } - } - - print DOT ("}\n"); - close(DOT); - - if ($main::opt_web || $main::opt_svg) { - # Rewrite SVG to be more usable inside web browser. - RewriteSvg(TempName($main::next_tmpfile, "svg")); - } - - return 1; -} - -sub RewriteSvg { - my $svgfile = shift; - - open(SVG, $svgfile) || die "open temp svg: $!"; - my @svg = ; - close(SVG); - unlink $svgfile; - my $svg = join('', @svg); - - # Dot's SVG output is - # - # - # - # ... - # - # - # - # Change it to - # - # - # $svg_javascript - # - # - # ... - # - # - # - - # Fix width, height; drop viewBox. - $svg =~ s/(?s) above first - my $svg_javascript = SvgJavascript(); - my $viewport = "\n"; - $svg =~ s/ above . - $svg =~ s/(.*)(<\/svg>)/$1<\/g>$2/; - $svg =~ s/$svgfile") || die "open $svgfile: $!"; - print SVG $svg; - close(SVG); - } -} - -sub SvgJavascript { - return <<'EOF'; - -EOF -} - -# Translate a stack of addresses into a stack of symbols -sub TranslateStack { - my $symbols = shift; - my $k = shift; - - my @addrs = split(/\n/, $k); - my @result = (); - for (my $i = 0; $i <= $#addrs; $i++) { - my $a = $addrs[$i]; - - # Skip large addresses since they sometimes show up as fake entries on RH9 - if (length($a) > 8 && $a gt "7fffffffffffffff") { - next; - } - - if ($main::opt_disasm || $main::opt_list) { - # We want just the address for the key - push(@result, $a); - next; - } - - my $symlist = $symbols->{$a}; - if (!defined($symlist)) { - $symlist = [$a, "", $a]; - } - - # We can have a sequence of symbols for a particular entry - # (more than one symbol in the case of inlining). Callers - # come before callees in symlist, so walk backwards since - # the translated stack should contain callees before callers. - for (my $j = $#{$symlist}; $j >= 2; $j -= 3) { - my $func = $symlist->[$j-2]; - my $fileline = $symlist->[$j-1]; - my $fullfunc = $symlist->[$j]; - if ($j > 2) { - $func = "$func (inline)"; - } - if ($main::opt_addresses) { - push(@result, "$a $func $fileline"); - } elsif ($main::opt_lines) { - if ($func eq '??' && $fileline eq '??:0') { - push(@result, "$a"); - } else { - push(@result, "$func $fileline"); - } - } elsif ($main::opt_functions) { - if ($func eq '??') { - push(@result, "$a"); - } else { - push(@result, $func); - } - } elsif ($main::opt_files) { - if ($fileline eq '??:0' || $fileline eq '') { - push(@result, "$a"); - } else { - my $f = $fileline; - $f =~ s/:\d+$//; - push(@result, $f); - } - } else { - push(@result, $a); - last; # Do not print inlined info - } - } - } - - # print join(",", @addrs), " => ", join(",", @result), "\n"; - return @result; -} - -# Generate percent string for a number and a total -sub Percent { - my $num = shift; - my $tot = shift; - if ($tot != 0) { - return sprintf("%.1f%%", $num * 100.0 / $tot); - } else { - return ($num == 0) ? "nan" : (($num > 0) ? "+inf" : "-inf"); - } -} - -# Generate pretty-printed form of number -sub Unparse { - my $num = shift; - if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') { - if ($main::opt_inuse_objects || $main::opt_alloc_objects) { - return sprintf("%d", $num); - } else { - if ($main::opt_show_bytes) { - return sprintf("%d", $num); - } else { - return sprintf("%.1f", $num / 1048576.0); - } - } - } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) { - return sprintf("%.3f", $num / 1e9); # Convert nanoseconds to seconds - } else { - return sprintf("%d", $num); - } -} - -# Alternate pretty-printed form: 0 maps to "." -sub UnparseAlt { - my $num = shift; - if ($num == 0) { - return "."; - } else { - return Unparse($num); - } -} - -# Alternate pretty-printed form: 0 maps to "" -sub HtmlPrintNumber { - my $num = shift; - if ($num == 0) { - return ""; - } else { - return Unparse($num); - } -} - -# Return output units -sub Units { - if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') { - if ($main::opt_inuse_objects || $main::opt_alloc_objects) { - return "objects"; - } else { - if ($main::opt_show_bytes) { - return "B"; - } else { - return "MB"; - } - } - } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) { - return "seconds"; - } else { - return "samples"; - } -} - -##### Profile manipulation code ##### - -# Generate flattened profile: -# If count is charged to stack [a,b,c,d], in generated profile, -# it will be charged to [a] -sub FlatProfile { - my $profile = shift; - my $result = {}; - foreach my $k (keys(%{$profile})) { - my $count = $profile->{$k}; - my @addrs = split(/\n/, $k); - if ($#addrs >= 0) { - AddEntry($result, $addrs[0], $count); - } - } - return $result; -} - -# Generate cumulative profile: -# If count is charged to stack [a,b,c,d], in generated profile, -# it will be charged to [a], [b], [c], [d] -sub CumulativeProfile { - my $profile = shift; - my $result = {}; - foreach my $k (keys(%{$profile})) { - my $count = $profile->{$k}; - my @addrs = split(/\n/, $k); - foreach my $a (@addrs) { - AddEntry($result, $a, $count); - } - } - return $result; -} - -# If the second-youngest PC on the stack is always the same, returns -# that pc. Otherwise, returns undef. -sub IsSecondPcAlwaysTheSame { - my $profile = shift; - - my $second_pc = undef; - foreach my $k (keys(%{$profile})) { - my @addrs = split(/\n/, $k); - if ($#addrs < 1) { - return undef; - } - if (not defined $second_pc) { - $second_pc = $addrs[1]; - } else { - if ($second_pc ne $addrs[1]) { - return undef; - } - } - } - return $second_pc; -} - -sub ExtractSymbolLocation { - my $symbols = shift; - my $address = shift; - # 'addr2line' outputs "??:0" for unknown locations; we do the - # same to be consistent. - my $location = "??:0:unknown"; - if (exists $symbols->{$address}) { - my $file = $symbols->{$address}->[1]; - if ($file eq "?") { - $file = "??:0" - } - $location = $file . ":" . $symbols->{$address}->[0]; - } - return $location; -} - -# Extracts a graph of calls. -sub ExtractCalls { - my $symbols = shift; - my $profile = shift; - - my $calls = {}; - while( my ($stack_trace, $count) = each %$profile ) { - my @address = split(/\n/, $stack_trace); - my $destination = ExtractSymbolLocation($symbols, $address[0]); - AddEntry($calls, $destination, $count); - for (my $i = 1; $i <= $#address; $i++) { - my $source = ExtractSymbolLocation($symbols, $address[$i]); - my $call = "$source -> $destination"; - AddEntry($calls, $call, $count); - $destination = $source; - } - } - - return $calls; -} - -sub RemoveUninterestingFrames { - my $symbols = shift; - my $profile = shift; - - # List of function names to skip - my %skip = (); - my $skip_regexp = 'NOMATCH'; - if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') { - foreach my $name ('calloc', - 'cfree', - 'malloc', - 'free', - 'memalign', - 'posix_memalign', - 'pvalloc', - 'valloc', - 'realloc', - 'tc_calloc', - 'tc_cfree', - 'tc_malloc', - 'tc_free', - 'tc_memalign', - 'tc_posix_memalign', - 'tc_pvalloc', - 'tc_valloc', - 'tc_realloc', - 'tc_new', - 'tc_delete', - 'tc_newarray', - 'tc_deletearray', - 'tc_new_nothrow', - 'tc_newarray_nothrow', - 'do_malloc', - '::do_malloc', # new name -- got moved to an unnamed ns - '::do_malloc_or_cpp_alloc', - 'DoSampledAllocation', - 'simple_alloc::allocate', - '__malloc_alloc_template::allocate', - '__builtin_delete', - '__builtin_new', - '__builtin_vec_delete', - '__builtin_vec_new', - 'operator new', - 'operator new[]', - # Go - 'catstring', - 'copyin', - 'gostring', - 'gostringsize', - 'growslice1', - 'appendslice1', - 'hash_init', - 'hash_subtable_new', - 'hash_conv', - 'hash_grow', - 'hash_insert_internal', - 'hash_insert', - 'mapassign', - 'runtime.mapassign', - 'runtime.appendslice', - 'runtime.mapassign1', - 'makechan', - 'makemap', - 'mal', - 'runtime.new', - 'makeslice1', - 'runtime.gostringsize', - 'runtime.malloc', - 'unsafe.New', - 'runtime.mallocgc', - 'runtime.catstring', - 'runtime.growslice', - 'runtime.ifaceT2E', - 'runtime.ifaceT2I', - 'runtime.makechan', - 'runtime.makechan_c', - 'runtime.makemap', - 'runtime.makemap_c', - 'runtime.makeslice', - 'runtime.mal', - 'runtime.slicebytetostring', - 'runtime.sliceinttostring', - 'runtime.stringtoslicebyte', - 'runtime.stringtosliceint', - # These mark the beginning/end of our custom sections - '__start_google_malloc', - '__stop_google_malloc', - '__start_malloc_hook', - '__stop_malloc_hook') { - $skip{$name} = 1; - $skip{"_" . $name} = 1; # Mach (OS X) adds a _ prefix to everything - } - # TODO: Remove TCMalloc once everything has been - # moved into the tcmalloc:: namespace and we have flushed - # old code out of the system. - $skip_regexp = "TCMalloc|^tcmalloc::"; - } elsif ($main::profile_type eq 'contention') { - foreach my $vname ('Mutex::Unlock', 'Mutex::UnlockSlow') { - $skip{$vname} = 1; - } - } elsif ($main::profile_type eq 'cpu') { - # Drop signal handlers used for CPU profile collection - # TODO(dpeng): this should not be necessary; it's taken - # care of by the general 2nd-pc mechanism below. - foreach my $name ('ProfileData::Add', # historical - 'ProfileData::prof_handler', # historical - 'CpuProfiler::prof_handler', - '__FRAME_END__', - '__pthread_sighandler', - '__restore') { - $skip{$name} = 1; - } - } else { - # Nothing skipped for unknown types - } - - # Go doesn't have the problem that this heuristic tries to fix. Disable. - if (0 && $main::profile_type eq 'cpu') { - # If all the second-youngest program counters are the same, - # this STRONGLY suggests that it is an artifact of measurement, - # i.e., stack frames pushed by the CPU profiler signal handler. - # Hence, we delete them. - # (The topmost PC is read from the signal structure, not from - # the stack, so it does not get involved.) - while (my $second_pc = IsSecondPcAlwaysTheSame($profile)) { - my $result = {}; - my $func = ''; - if (exists($symbols->{$second_pc})) { - $second_pc = $symbols->{$second_pc}->[0]; - } - print STDERR "Removing $second_pc from all stack traces.\n"; - foreach my $k (keys(%{$profile})) { - my $count = $profile->{$k}; - my @addrs = split(/\n/, $k); - splice @addrs, 1, 1; - my $reduced_path = join("\n", @addrs); - AddEntry($result, $reduced_path, $count); - } - $profile = $result; - } - } - - my $result = {}; - foreach my $k (keys(%{$profile})) { - my $count = $profile->{$k}; - my @addrs = split(/\n/, $k); - my @path = (); - foreach my $a (@addrs) { - if (exists($symbols->{$a})) { - my $func = $symbols->{$a}->[0]; - if ($skip{$func} || ($func =~ m/$skip_regexp/)) { - next; - } - } - push(@path, $a); - } - my $reduced_path = join("\n", @path); - AddEntry($result, $reduced_path, $count); - } - return $result; -} - -# Reduce profile to granularity given by user -sub ReduceProfile { - my $symbols = shift; - my $profile = shift; - my $result = {}; - foreach my $k (keys(%{$profile})) { - my $count = $profile->{$k}; - my @translated = TranslateStack($symbols, $k); - my @path = (); - my %seen = (); - $seen{''} = 1; # So that empty keys are skipped - foreach my $e (@translated) { - # To avoid double-counting due to recursion, skip a stack-trace - # entry if it has already been seen - if (!$seen{$e}) { - $seen{$e} = 1; - push(@path, $e); - } - } - my $reduced_path = join("\n", @path); - AddEntry($result, $reduced_path, $count); - } - return $result; -} - -# Does the specified symbol array match the regexp? -sub SymbolMatches { - my $sym = shift; - my $re = shift; - if (defined($sym)) { - for (my $i = 0; $i < $#{$sym}; $i += 3) { - if ($sym->[$i] =~ m/$re/ || $sym->[$i+1] =~ m/$re/) { - return 1; - } - } - } - return 0; -} - -# Focus only on paths involving specified regexps -sub FocusProfile { - my $symbols = shift; - my $profile = shift; - my $focus = shift; - my $result = {}; - foreach my $k (keys(%{$profile})) { - my $count = $profile->{$k}; - my @addrs = split(/\n/, $k); - foreach my $a (@addrs) { - # Reply if it matches either the address/shortname/fileline - if (($a =~ m/$focus/) || SymbolMatches($symbols->{$a}, $focus)) { - AddEntry($result, $k, $count); - last; - } - } - } - return $result; -} - -# Focus only on paths not involving specified regexps -sub IgnoreProfile { - my $symbols = shift; - my $profile = shift; - my $ignore = shift; - my $result = {}; - foreach my $k (keys(%{$profile})) { - my $count = $profile->{$k}; - my @addrs = split(/\n/, $k); - my $matched = 0; - foreach my $a (@addrs) { - # Reply if it matches either the address/shortname/fileline - if (($a =~ m/$ignore/) || SymbolMatches($symbols->{$a}, $ignore)) { - $matched = 1; - last; - } - } - if (!$matched) { - AddEntry($result, $k, $count); - } - } - return $result; -} - -# Get total count in profile -sub TotalProfile { - my $profile = shift; - my $result = 0; - foreach my $k (keys(%{$profile})) { - $result += $profile->{$k}; - } - return $result; -} - -# Add A to B -sub AddProfile { - my $A = shift; - my $B = shift; - - my $R = {}; - # add all keys in A - foreach my $k (keys(%{$A})) { - my $v = $A->{$k}; - AddEntry($R, $k, $v); - } - # add all keys in B - foreach my $k (keys(%{$B})) { - my $v = $B->{$k}; - AddEntry($R, $k, $v); - } - return $R; -} - -# Merges symbol maps -sub MergeSymbols { - my $A = shift; - my $B = shift; - - my $R = {}; - foreach my $k (keys(%{$A})) { - $R->{$k} = $A->{$k}; - } - if (defined($B)) { - foreach my $k (keys(%{$B})) { - $R->{$k} = $B->{$k}; - } - } - return $R; -} - - -# Add A to B -sub AddPcs { - my $A = shift; - my $B = shift; - - my $R = {}; - # add all keys in A - foreach my $k (keys(%{$A})) { - $R->{$k} = 1 - } - # add all keys in B - foreach my $k (keys(%{$B})) { - $R->{$k} = 1 - } - return $R; -} - -# Subtract B from A -sub SubtractProfile { - my $A = shift; - my $B = shift; - - my $R = {}; - foreach my $k (keys(%{$A})) { - my $v = $A->{$k} - GetEntry($B, $k); - if ($v < 0 && $main::opt_drop_negative) { - $v = 0; - } - AddEntry($R, $k, $v); - } - if (!$main::opt_drop_negative) { - # Take care of when subtracted profile has more entries - foreach my $k (keys(%{$B})) { - if (!exists($A->{$k})) { - AddEntry($R, $k, 0 - $B->{$k}); - } - } - } - return $R; -} - -# Get entry from profile; zero if not present -sub GetEntry { - my $profile = shift; - my $k = shift; - if (exists($profile->{$k})) { - return $profile->{$k}; - } else { - return 0; - } -} - -# Add entry to specified profile -sub AddEntry { - my $profile = shift; - my $k = shift; - my $n = shift; - if (!exists($profile->{$k})) { - $profile->{$k} = 0; - } - $profile->{$k} += $n; -} - -# Add a stack of entries to specified profile, and add them to the $pcs -# list. -sub AddEntries { - my $profile = shift; - my $pcs = shift; - my $stack = shift; - my $count = shift; - my @k = (); - - foreach my $e (split(/\s+/, $stack)) { - my $pc = HexExtend($e); - $pcs->{$pc} = 1; - push @k, $pc; - } - AddEntry($profile, (join "\n", @k), $count); -} - -sub IsSymbolizedProfileFile { - my $file_name = shift; - - if (!(-e $file_name) || !(-r $file_name)) { - return 0; - } - - $SYMBOL_PAGE =~ m,[^/]+$,; # matches everything after the last slash - my $symbol_marker = $&; - # Check if the file contains a symbol-section marker. - open(TFILE, "<$file_name"); - my @lines = ; - my $result = grep(/^--- *$symbol_marker/, @lines); - close(TFILE); - return $result > 0; -} - -##### Code to profile a server dynamically ##### - -sub CheckSymbolPage { - my $url = SymbolPageURL(); -print STDERR "Read $url\n"; - open(SYMBOL, "$CURL -s '$url' |"); - my $line = ; - $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines - close(SYMBOL); - unless (defined($line)) { - error("$url doesn't exist\n"); - } - - if ($line =~ /^num_symbols:\s+(\d+)$/) { - if ($1 == 0) { - error("Stripped binary. No symbols available.\n"); - } - } else { - error("Failed to get the number of symbols from $url\n"); - } -} - -sub IsProfileURL { - my $profile_name = shift; - my ($host, $port, $prefix, $path) = ParseProfileURL($profile_name); - return defined($host) and defined($port) and defined($path); -} - -sub ParseProfileURL { - my $profile_name = shift; - if (defined($profile_name) && - $profile_name =~ m,^(http://|)([^/:]+):(\d+)(|\@\d+)(|/|(.*?)($PROFILE_PAGE|$PMUPROFILE_PAGE|$HEAP_PAGE|$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|$FILTEREDPROFILE_PAGE))$,o) { - # $7 is $PROFILE_PAGE/$HEAP_PAGE/etc. $5 is *everything* after - # the hostname, as long as that everything is the empty string, - # a slash, or something ending in $PROFILE_PAGE/$HEAP_PAGE/etc. - # So "$7 || $5" is $PROFILE_PAGE/etc if there, or else it's "/" or "". - return ($2, $3, $6, $7 || $5); - } - return (); -} - -# We fetch symbols from the first profile argument. -sub SymbolPageURL { - my ($host, $port, $prefix, $path) = ParseProfileURL($main::pfile_args[0]); - return "http://$host:$port$prefix$SYMBOL_PAGE"; -} - -sub FetchProgramName() { - my ($host, $port, $prefix, $path) = ParseProfileURL($main::pfile_args[0]); - my $url = "http://$host:$port$prefix$PROGRAM_NAME_PAGE"; - my $command_line = "$CURL -s '$url'"; - open(CMDLINE, "$command_line |") or error($command_line); - my $cmdline = ; - $cmdline =~ s/\r//g; # turn windows-looking lines into unix-looking lines - close(CMDLINE); - error("Failed to get program name from $url\n") unless defined($cmdline); - $cmdline =~ s/\x00.+//; # Remove argv[1] and latters. - $cmdline =~ s!\n!!g; # Remove LFs. - return $cmdline; -} - -# Gee, curl's -L (--location) option isn't reliable at least -# with its 7.12.3 version. Curl will forget to post data if -# there is a redirection. This function is a workaround for -# curl. Redirection happens on borg hosts. -sub ResolveRedirectionForCurl { - my $url = shift; - my $command_line = "$CURL -s --head '$url'"; - open(CMDLINE, "$command_line |") or error($command_line); - while () { - s/\r//g; # turn windows-looking lines into unix-looking lines - if (/^Location: (.*)/) { - $url = $1; - } - } - close(CMDLINE); - return $url; -} - -# Reads a symbol map from the file handle name given as $1, returning -# the resulting symbol map. Also processes variables relating to symbols. -# Currently, the only variable processed is 'binary=' which updates -# $main::prog to have the correct program name. -sub ReadSymbols { - my $in = shift; - my $map = shift; - while (<$in>) { - s/\r//g; # turn windows-looking lines into unix-looking lines - # Removes all the leading zeroes from the symbols, see comment below. - if (m/^0x0*([0-9a-f]+)\s+(.+)/) { - $map->{$1} = $2; - } elsif (m/^---/) { - last; - } elsif (m/^([a-z][^=]*)=(.*)$/ ) { - my ($variable, $value) = ($1, $2); - for ($variable, $value) { - s/^\s+//; - s/\s+$//; - } - if ($variable eq "binary") { - if ($main::prog ne $UNKNOWN_BINARY && $main::prog ne $value) { - printf STDERR ("Warning: Mismatched binary name '%s', using '%s'.\n", - $main::prog, $value); - } - $main::prog = $value; - } else { - printf STDERR ("Ignoring unknown variable in symbols list: " . - "'%s' = '%s'\n", $variable, $value); - } - } - } - return $map; -} - -# Fetches and processes symbols to prepare them for use in the profile output -# code. If the optional 'symbol_map' arg is not given, fetches symbols from -# $SYMBOL_PAGE for all PC values found in profile. Otherwise, the raw symbols -# are assumed to have already been fetched into 'symbol_map' and are simply -# extracted and processed. -sub FetchSymbols { - my $pcset = shift; - my $symbol_map = shift; - - my %seen = (); - my @pcs = grep { !$seen{$_}++ } keys(%$pcset); # uniq - - if (!defined($symbol_map)) { - $symbol_map = {}; - my @toask = @pcs; - while (@toask > 0) { - my $n = @toask; - # NOTE(rsc): Limiting the number of PCs requested per round - # used to be necessary, but I think it was a bug in - # debug/pprof/symbol's implementation. Leaving here - # in case I am wrong. - # if ($n > 49) { $n = 49; } - my @thisround = @toask[0..$n]; - @toask = @toask[($n+1)..(@toask-1)]; - my $post_data = join("+", sort((map {"0x" . "$_"} @thisround))); - open(POSTFILE, ">$main::tmpfile_sym"); - print POSTFILE $post_data; - close(POSTFILE); - - my $url = SymbolPageURL(); - $url = ResolveRedirectionForCurl($url); - my $command_line = "$CURL -sd '\@$main::tmpfile_sym' '$url'"; - # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols. - my $cppfilt = $obj_tool_map{"c++filt"}; - open(SYMBOL, "$command_line | $cppfilt |") or error($command_line); - ReadSymbols(*SYMBOL{IO}, $symbol_map); - close(SYMBOL); - } - } - - my $symbols = {}; - foreach my $pc (@pcs) { - my $fullname; - # For 64 bits binaries, symbols are extracted with 8 leading zeroes. - # Then /symbol reads the long symbols in as uint64, and outputs - # the result with a "0x%08llx" format which get rid of the zeroes. - # By removing all the leading zeroes in both $pc and the symbols from - # /symbol, the symbols match and are retrievable from the map. - my $shortpc = $pc; - $shortpc =~ s/^0*//; - # Each line may have a list of names, which includes the function - # and also other functions it has inlined. They are separated - # (in PrintSymbolizedFile), by --, which is illegal in function names. - my $fullnames; - if (defined($symbol_map->{$shortpc})) { - $fullnames = $symbol_map->{$shortpc}; - } else { - $fullnames = "0x" . $pc; # Just use addresses - } - my $sym = []; - $symbols->{$pc} = $sym; - foreach my $fullname (split("--", $fullnames)) { - my $name = ShortFunctionName($fullname); - push(@{$sym}, $name, "?", $fullname); - } - } - return $symbols; -} - -sub BaseName { - my $file_name = shift; - $file_name =~ s!^.*/!!; # Remove directory name - return $file_name; -} - -sub MakeProfileBaseName { - my ($binary_name, $profile_name) = @_; - my ($host, $port, $prefix, $path) = ParseProfileURL($profile_name); - my $binary_shortname = BaseName($binary_name); - return sprintf("%s.%s.%s-port%s", - $binary_shortname, $main::op_time, $host, $port); -} - -sub FetchDynamicProfile { - my $binary_name = shift; - my $profile_name = shift; - my $fetch_name_only = shift; - my $encourage_patience = shift; - - if (!IsProfileURL($profile_name)) { - return $profile_name; - } else { - my ($host, $port, $prefix, $path) = ParseProfileURL($profile_name); - if ($path eq "" || $path eq "/") { - # Missing type specifier defaults to cpu-profile - $path = $PROFILE_PAGE; - } - - my $profile_file = MakeProfileBaseName($binary_name, $profile_name); - - my $url; - my $curl_timeout; - if (($path =~ m/$PROFILE_PAGE/) || ($path =~ m/$PMUPROFILE_PAGE/)) { - if ($path =~ m/$PROFILE_PAGE/) { - $url = sprintf("http://$host:$port$prefix$path?seconds=%d", - $main::opt_seconds); - } else { - if ($profile_name =~ m/[?]/) { - $profile_name .= "&" - } else { - $profile_name .= "?" - } - $url = sprintf("http://$profile_name" . "seconds=%d", - $main::opt_seconds); - } - $curl_timeout = sprintf("--max-time %d", - int($main::opt_seconds * 1.01 + 60)); - } else { - # For non-CPU profiles, we add a type-extension to - # the target profile file name. - my $suffix = $path; - $suffix =~ s,/,.,g; - $profile_file .= "$suffix"; - $url = "http://$host:$port$prefix$path"; - $curl_timeout = ""; - } - - my $profile_dir = $ENV{"PPROF_TMPDIR"} || ($ENV{HOME} . "/pprof"); - if (!(-d $profile_dir)) { - mkdir($profile_dir) - || die("Unable to create profile directory $profile_dir: $!\n"); - } - my $tmp_profile = "$profile_dir/.tmp.$profile_file"; - my $real_profile = "$profile_dir/$profile_file"; - - if ($fetch_name_only > 0) { - return $real_profile; - } - - my $cmd = "$CURL $curl_timeout -s -o $tmp_profile '$url'"; - if (($path =~ m/$PROFILE_PAGE/) || ($path =~ m/$PMUPROFILE_PAGE/)){ - print STDERR "Gathering CPU profile from $url for $main::opt_seconds seconds to\n ${real_profile}\n"; - if ($encourage_patience) { - print STDERR "Be patient...\n"; - } - } else { - print STDERR "Fetching $path profile from $host:$port to\n ${real_profile}\n"; - } - - (system($cmd) == 0) || error("Failed to get profile: $cmd: $!\n"); - (system("mv $tmp_profile $real_profile") == 0) || error("Unable to rename profile\n"); - print STDERR "Wrote profile to $real_profile\n"; - $main::collected_profile = $real_profile; - return $main::collected_profile; - } -} - -# Collect profiles in parallel -sub FetchDynamicProfiles { - my $items = scalar(@main::pfile_args); - my $levels = log($items) / log(2); - - if ($items == 1) { - $main::profile_files[0] = FetchDynamicProfile($main::prog, $main::pfile_args[0], 0, 1); - } else { - # math rounding issues - if ((2 ** $levels) < $items) { - $levels++; - } - my $count = scalar(@main::pfile_args); - for (my $i = 0; $i < $count; $i++) { - $main::profile_files[$i] = FetchDynamicProfile($main::prog, $main::pfile_args[$i], 1, 0); - } - print STDERR "Fetching $count profiles, Be patient...\n"; - FetchDynamicProfilesRecurse($levels, 0, 0); - $main::collected_profile = join(" \\\n ", @main::profile_files); - } -} - -# Recursively fork a process to get enough processes -# collecting profiles -sub FetchDynamicProfilesRecurse { - my $maxlevel = shift; - my $level = shift; - my $position = shift; - - if (my $pid = fork()) { - $position = 0 | ($position << 1); - TryCollectProfile($maxlevel, $level, $position); - wait; - } else { - $position = 1 | ($position << 1); - TryCollectProfile($maxlevel, $level, $position); - exit(0); - } -} - -# Collect a single profile -sub TryCollectProfile { - my $maxlevel = shift; - my $level = shift; - my $position = shift; - - if ($level >= ($maxlevel - 1)) { - if ($position < scalar(@main::pfile_args)) { - FetchDynamicProfile($main::prog, $main::pfile_args[$position], 0, 0); - } - } else { - FetchDynamicProfilesRecurse($maxlevel, $level+1, $position); - } -} - -##### Parsing code ##### - -# Provide a small streaming-read module to handle very large -# cpu-profile files. Stream in chunks along a sliding window. -# Provides an interface to get one 'slot', correctly handling -# endian-ness differences. A slot is one 32-bit or 64-bit word -# (depending on the input profile). We tell endianness and bit-size -# for the profile by looking at the first 8 bytes: in cpu profiles, -# the second slot is always 3 (we'll accept anything that's not 0). -BEGIN { - package CpuProfileStream; - - sub new { - my ($class, $file, $fname) = @_; - my $self = { file => $file, - base => 0, - stride => 512 * 1024, # must be a multiple of bitsize/8 - slots => [], - unpack_code => "", # N for big-endian, V for little - }; - bless $self, $class; - # Let unittests adjust the stride - if ($main::opt_test_stride > 0) { - $self->{stride} = $main::opt_test_stride; - } - # Read the first two slots to figure out bitsize and endianness. - my $slots = $self->{slots}; - my $str; - read($self->{file}, $str, 8); - # Set the global $address_length based on what we see here. - # 8 is 32-bit (8 hexadecimal chars); 16 is 64-bit (16 hexadecimal chars). - $address_length = ($str eq (chr(0)x8)) ? 16 : 8; - if ($address_length == 8) { - if (substr($str, 6, 2) eq chr(0)x2) { - $self->{unpack_code} = 'V'; # Little-endian. - } elsif (substr($str, 4, 2) eq chr(0)x2) { - $self->{unpack_code} = 'N'; # Big-endian - } else { - ::error("$fname: header size >= 2**16\n"); - } - @$slots = unpack($self->{unpack_code} . "*", $str); - } else { - # If we're a 64-bit profile, make sure we're a 64-bit-capable - # perl. Otherwise, each slot will be represented as a float - # instead of an int64, losing precision and making all the - # 64-bit addresses right. We *could* try to handle this with - # software emulation of 64-bit ints, but that's added complexity - # for no clear benefit (yet). We use 'Q' to test for 64-bit-ness; - # perl docs say it's only available on 64-bit perl systems. - my $has_q = 0; - eval { $has_q = pack("Q", "1") ? 1 : 1; }; - if (!$has_q) { - ::error("$fname: need a 64-bit perl to process this 64-bit profile.\n"); - } - read($self->{file}, $str, 8); - if (substr($str, 4, 4) eq chr(0)x4) { - # We'd love to use 'Q', but it's a) not universal, b) not endian-proof. - $self->{unpack_code} = 'V'; # Little-endian. - } elsif (substr($str, 0, 4) eq chr(0)x4) { - $self->{unpack_code} = 'N'; # Big-endian - } else { - ::error("$fname: header size >= 2**32\n"); - } - my @pair = unpack($self->{unpack_code} . "*", $str); - # Since we know one of the pair is 0, it's fine to just add them. - @$slots = (0, $pair[0] + $pair[1]); - } - return $self; - } - - # Load more data when we access slots->get(X) which is not yet in memory. - sub overflow { - my ($self) = @_; - my $slots = $self->{slots}; - $self->{base} += $#$slots + 1; # skip over data we're replacing - my $str; - read($self->{file}, $str, $self->{stride}); - if ($address_length == 8) { # the 32-bit case - # This is the easy case: unpack provides 32-bit unpacking primitives. - @$slots = unpack($self->{unpack_code} . "*", $str); - } else { - # We need to unpack 32 bits at a time and combine. - my @b32_values = unpack($self->{unpack_code} . "*", $str); - my @b64_values = (); - for (my $i = 0; $i < $#b32_values; $i += 2) { - # TODO(csilvers): if this is a 32-bit perl, the math below - # could end up in a too-large int, which perl will promote - # to a double, losing necessary precision. Deal with that. - if ($self->{unpack_code} eq 'V') { # little-endian - push(@b64_values, $b32_values[$i] + $b32_values[$i+1] * (2**32)); - } else { - push(@b64_values, $b32_values[$i] * (2**32) + $b32_values[$i+1]); - } - } - @$slots = @b64_values; - } - } - - # Access the i-th long in the file (logically), or -1 at EOF. - sub get { - my ($self, $idx) = @_; - my $slots = $self->{slots}; - while ($#$slots >= 0) { - if ($idx < $self->{base}) { - # The only time we expect a reference to $slots[$i - something] - # after referencing $slots[$i] is reading the very first header. - # Since $stride > |header|, that shouldn't cause any lookback - # errors. And everything after the header is sequential. - print STDERR "Unexpected look-back reading CPU profile"; - return -1; # shrug, don't know what better to return - } elsif ($idx > $self->{base} + $#$slots) { - $self->overflow(); - } else { - return $slots->[$idx - $self->{base}]; - } - } - # If we get here, $slots is [], which means we've reached EOF - return -1; # unique since slots is supposed to hold unsigned numbers - } -} - -# Parse profile generated by common/profiler.cc and return a reference -# to a map: -# $result->{version} Version number of profile file -# $result->{period} Sampling period (in microseconds) -# $result->{profile} Profile object -# $result->{map} Memory map info from profile -# $result->{pcs} Hash of all PC values seen, key is hex address -sub ReadProfile { - my $prog = shift; - my $fname = shift; - - if (IsSymbolizedProfileFile($fname) && !$main::use_symbolized_profile) { - # we have both a binary and symbolized profiles, abort - usage("Symbolized profile '$fname' cannot be used with a binary arg. " . - "Try again without passing '$prog'."); - } - - $main::profile_type = ''; - - $CONTENTION_PAGE =~ m,[^/]+$,; # matches everything after the last slash - my $contention_marker = $&; - $GROWTH_PAGE =~ m,[^/]+$,; # matches everything after the last slash - my $growth_marker = $&; - $SYMBOL_PAGE =~ m,[^/]+$,; # matches everything after the last slash - my $symbol_marker = $&; - $PROFILE_PAGE =~ m,[^/]+$,; # matches everything after the last slash - my $profile_marker = $&; - - # Look at first line to see if it is a heap or a CPU profile. - # CPU profile may start with no header at all, and just binary data - # (starting with \0\0\0\0) -- in that case, don't try to read the - # whole firstline, since it may be gigabytes(!) of data. - open(PROFILE, "<$fname") || error("$fname: $!\n"); - binmode PROFILE; # New perls do UTF-8 processing - my $firstchar = ""; - my $header = ""; - read(PROFILE, $firstchar, 1); - seek(PROFILE, -1, 1); # unread the firstchar - if ($firstchar ne "\0") { - $header = ; - if (!defined($header)) { - error("Profile is empty.\n"); - } - $header =~ s/\r//g; # turn windows-looking lines into unix-looking lines - } - - my $symbols; - if ($header =~ m/^--- *$symbol_marker/o) { - # read the symbol section of the symbolized profile file - $symbols = ReadSymbols(*PROFILE{IO}); - - # read the next line to get the header for the remaining profile - $header = ""; - read(PROFILE, $firstchar, 1); - seek(PROFILE, -1, 1); # unread the firstchar - if ($firstchar ne "\0") { - $header = ; - $header =~ s/\r//g; - } - } - - my $result; - - if ($header =~ m/^heap profile:.*$growth_marker/o) { - $main::profile_type = 'growth'; - $result = ReadHeapProfile($prog, $fname, $header); - } elsif ($header =~ m/^heap profile:/) { - $main::profile_type = 'heap'; - $result = ReadHeapProfile($prog, $fname, $header); - } elsif ($header =~ m/^--- *$contention_marker/o) { - $main::profile_type = 'contention'; - $result = ReadSynchProfile($prog, $fname); - } elsif ($header =~ m/^--- *Stacks:/) { - print STDERR - "Old format contention profile: mistakenly reports " . - "condition variable signals as lock contentions.\n"; - $main::profile_type = 'contention'; - $result = ReadSynchProfile($prog, $fname); - } elsif ($header =~ m/^--- *$profile_marker/) { - # the binary cpu profile data starts immediately after this line - $main::profile_type = 'cpu'; - $result = ReadCPUProfile($prog, $fname); - } else { - if (defined($symbols)) { - # a symbolized profile contains a format we don't recognize, bail out - error("$fname: Cannot recognize profile section after symbols.\n"); - } - # no ascii header present -- must be a CPU profile - $main::profile_type = 'cpu'; - $result = ReadCPUProfile($prog, $fname); - } - - # if we got symbols along with the profile, return those as well - if (defined($symbols)) { - $result->{symbols} = $symbols; - } - - return $result; -} - -# Subtract one from caller pc so we map back to call instr. -# However, don't do this if we're reading a symbolized profile -# file, in which case the subtract-one was done when the file -# was written. -# -# We apply the same logic to all readers, though ReadCPUProfile uses an -# independent implementation. -sub FixCallerAddresses { - my $stack = shift; - if ($main::use_symbolized_profile) { - return $stack; - } else { - $stack =~ /(\s)/; - my $delimiter = $1; - my @addrs = split(' ', $stack); - my @fixedaddrs; - $#fixedaddrs = $#addrs; - if ($#addrs >= 0) { - $fixedaddrs[0] = $addrs[0]; - } - for (my $i = 1; $i <= $#addrs; $i++) { - $fixedaddrs[$i] = AddressSub($addrs[$i], "0x1"); - } - return join $delimiter, @fixedaddrs; - } -} - -# CPU profile reader -sub ReadCPUProfile { - my $prog = shift; - my $fname = shift; - my $version; - my $period; - my $i; - my $profile = {}; - my $pcs = {}; - - # Parse string into array of slots. - my $slots = CpuProfileStream->new(*PROFILE, $fname); - - # Read header. The current header version is a 5-element structure - # containing: - # 0: header count (always 0) - # 1: header "words" (after this one: 3) - # 2: format version (0) - # 3: sampling period (usec) - # 4: unused padding (always 0) - if ($slots->get(0) != 0 ) { - error("$fname: not a profile file, or old format profile file\n"); - } - $i = 2 + $slots->get(1); - $version = $slots->get(2); - $period = $slots->get(3); - # Do some sanity checking on these header values. - if ($version > (2**32) || $period > (2**32) || $i > (2**32) || $i < 5) { - error("$fname: not a profile file, or corrupted profile file\n"); - } - - # Parse profile - while ($slots->get($i) != -1) { - my $n = $slots->get($i++); - my $d = $slots->get($i++); - if ($d > (2**16)) { # TODO(csilvers): what's a reasonable max-stack-depth? - my $addr = sprintf("0%o", $i * ($address_length == 8 ? 4 : 8)); - print STDERR "At index $i (address $addr):\n"; - error("$fname: stack trace depth >= 2**32\n"); - } - if ($slots->get($i) == 0) { - # End of profile data marker - $i += $d; - last; - } - - # Make key out of the stack entries - my @k = (); - for (my $j = 0; $j < $d; $j++) { - my $pc = $slots->get($i+$j); - # Subtract one from caller pc so we map back to call instr. - # However, don't do this if we're reading a symbolized profile - # file, in which case the subtract-one was done when the file - # was written. - if ($j > 0 && !$main::use_symbolized_profile) { - $pc--; - } - $pc = sprintf("%0*x", $address_length, $pc); - $pcs->{$pc} = 1; - push @k, $pc; - } - - AddEntry($profile, (join "\n", @k), $n); - $i += $d; - } - - # Parse map - my $map = ''; - seek(PROFILE, $i * 4, 0); - read(PROFILE, $map, (stat PROFILE)[7]); - close(PROFILE); - - my $r = {}; - $r->{version} = $version; - $r->{period} = $period; - $r->{profile} = $profile; - $r->{libs} = ParseLibraries($prog, $map, $pcs); - $r->{pcs} = $pcs; - - return $r; -} - -sub ReadHeapProfile { - my $prog = shift; - my $fname = shift; - my $header = shift; - - my $index = 1; - if ($main::opt_inuse_space) { - $index = 1; - } elsif ($main::opt_inuse_objects) { - $index = 0; - } elsif ($main::opt_alloc_space) { - $index = 3; - } elsif ($main::opt_alloc_objects) { - $index = 2; - } - - # Find the type of this profile. The header line looks like: - # heap profile: 1246: 8800744 [ 1246: 8800744] @ /266053 - # There are two pairs , the first inuse objects/space, and the - # second allocated objects/space. This is followed optionally by a profile - # type, and if that is present, optionally by a sampling frequency. - # For remote heap profiles (v1): - # The interpretation of the sampling frequency is that the profiler, for - # each sample, calculates a uniformly distributed random integer less than - # the given value, and records the next sample after that many bytes have - # been allocated. Therefore, the expected sample interval is half of the - # given frequency. By default, if not specified, the expected sample - # interval is 128KB. Only remote-heap-page profiles are adjusted for - # sample size. - # For remote heap profiles (v2): - # The sampling frequency is the rate of a Poisson process. This means that - # the probability of sampling an allocation of size X with sampling rate Y - # is 1 - exp(-X/Y) - # For version 2, a typical header line might look like this: - # heap profile: 1922: 127792360 [ 1922: 127792360] @ _v2/524288 - # the trailing number (524288) is the sampling rate. (Version 1 showed - # double the 'rate' here) - my $sampling_algorithm = 0; - my $sample_adjustment = 0; - chomp($header); - my $type = "unknown"; - if ($header =~ m"^heap profile:\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\](\s*@\s*([^/]*)(/(\d+))?)?") { - if (defined($6) && ($6 ne '')) { - $type = $6; - my $sample_period = $8; - # $type is "heapprofile" for profiles generated by the - # heap-profiler, and either "heap" or "heap_v2" for profiles - # generated by sampling directly within tcmalloc. It can also - # be "growth" for heap-growth profiles. The first is typically - # found for profiles generated locally, and the others for - # remote profiles. - if (($type eq "heapprofile") || ($type !~ /heap/) ) { - # No need to adjust for the sampling rate with heap-profiler-derived data - $sampling_algorithm = 0; - } elsif ($type =~ /_v2/) { - $sampling_algorithm = 2; # version 2 sampling - if (defined($sample_period) && ($sample_period ne '')) { - $sample_adjustment = int($sample_period); - } - } else { - $sampling_algorithm = 1; # version 1 sampling - if (defined($sample_period) && ($sample_period ne '')) { - $sample_adjustment = int($sample_period)/2; - } - } - } else { - # We detect whether or not this is a remote-heap profile by checking - # that the total-allocated stats ($n2,$s2) are exactly the - # same as the in-use stats ($n1,$s1). It is remotely conceivable - # that a non-remote-heap profile may pass this check, but it is hard - # to imagine how that could happen. - # In this case it's so old it's guaranteed to be remote-heap version 1. - my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4); - if (($n1 == $n2) && ($s1 == $s2)) { - # This is likely to be a remote-heap based sample profile - $sampling_algorithm = 1; - } - } - } - - if ($sampling_algorithm > 0) { - # For remote-heap generated profiles, adjust the counts and sizes to - # account for the sample rate (we sample once every 128KB by default). - if ($sample_adjustment == 0) { - # Turn on profile adjustment. - $sample_adjustment = 128*1024; - print STDERR "Adjusting heap profiles for 1-in-128KB sampling rate\n"; - } else { - printf STDERR ("Adjusting heap profiles for 1-in-%d sampling rate\n", - $sample_adjustment); - } - if ($sampling_algorithm > 1) { - # We don't bother printing anything for the original version (version 1) - printf STDERR "Heap version $sampling_algorithm\n"; - } - } - - my $profile = {}; - my $pcs = {}; - my $map = ""; - - while () { - s/\r//g; # turn windows-looking lines into unix-looking lines - if (/^MAPPED_LIBRARIES:/) { - # Read the /proc/self/maps data - while () { - s/\r//g; # turn windows-looking lines into unix-looking lines - $map .= $_; - } - last; - } - - if (/^--- Memory map:/) { - # Read /proc/self/maps data as formatted by DumpAddressMap() - my $buildvar = ""; - while () { - s/\r//g; # turn windows-looking lines into unix-looking lines - # Parse "build=" specification if supplied - if (m/^\s*build=(.*)\n/) { - $buildvar = $1; - } - - # Expand "$build" variable if available - $_ =~ s/\$build\b/$buildvar/g; - - $map .= $_; - } - last; - } - - # Read entry of the form: - # : [: ] @ a1 a2 a3 ... an - s/^\s*//; - s/\s*$//; - if (m/^\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]\s+@\s+(.*)$/) { - my $stack = $5; - my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4); - - if ($sample_adjustment) { - if ($sampling_algorithm == 2) { - # Remote-heap version 2 - # The sampling frequency is the rate of a Poisson process. - # This means that the probability of sampling an allocation of - # size X with sampling rate Y is 1 - exp(-X/Y) - my $ratio; - $ratio = (($s1*1.0)/$n1)/($sample_adjustment); - my $scale_factor; - $scale_factor = 1/(1 - exp(-$ratio)); - $n1 *= $scale_factor; - $s1 *= $scale_factor; - $ratio = (($s2*1.0)/$n2)/($sample_adjustment); - $scale_factor = 1/(1 - exp(-$ratio)); - $n2 *= $scale_factor; - $s2 *= $scale_factor; - } else { - # Remote-heap version 1 - my $ratio; - $ratio = (($s1*1.0)/$n1)/($sample_adjustment); - if ($ratio < 1) { - $n1 /= $ratio; - $s1 /= $ratio; - } - $ratio = (($s2*1.0)/$n2)/($sample_adjustment); - if ($ratio < 1) { - $n2 /= $ratio; - $s2 /= $ratio; - } - } - } - - my @counts = ($n1, $s1, $n2, $s2); - AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]); - } - } - - my $r = {}; - $r->{version} = "heap"; - $r->{period} = 1; - $r->{profile} = $profile; - $r->{libs} = ParseLibraries($prog, $map, $pcs); - $r->{pcs} = $pcs; - return $r; -} - -sub ReadSynchProfile { - my ($prog, $fname, $header) = @_; - - my $map = ''; - my $profile = {}; - my $pcs = {}; - my $sampling_period = 1; - my $cyclespernanosec = 2.8; # Default assumption for old binaries - my $seen_clockrate = 0; - my $line; - - my $index = 0; - if ($main::opt_total_delay) { - $index = 0; - } elsif ($main::opt_contentions) { - $index = 1; - } elsif ($main::opt_mean_delay) { - $index = 2; - } - - while ( $line = ) { - $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines - if ( $line =~ /^\s*(\d+)\s+(\d+) \@\s*(.*?)\s*$/ ) { - my ($cycles, $count, $stack) = ($1, $2, $3); - - # Convert cycles to nanoseconds - $cycles /= $cyclespernanosec; - - # Adjust for sampling done by application - $cycles *= $sampling_period; - $count *= $sampling_period; - - my @values = ($cycles, $count, $cycles / $count); - AddEntries($profile, $pcs, FixCallerAddresses($stack), $values[$index]); - - } elsif ( $line =~ /^(slow release).*thread \d+ \@\s*(.*?)\s*$/ || - $line =~ /^\s*(\d+) \@\s*(.*?)\s*$/ ) { - my ($cycles, $stack) = ($1, $2); - if ($cycles !~ /^\d+$/) { - next; - } - - # Convert cycles to nanoseconds - $cycles /= $cyclespernanosec; - - # Adjust for sampling done by application - $cycles *= $sampling_period; - - AddEntries($profile, $pcs, FixCallerAddresses($stack), $cycles); - - } elsif ( $line =~ m/^([a-z][^=]*)=(.*)$/ ) { - my ($variable, $value) = ($1,$2); - for ($variable, $value) { - s/^\s+//; - s/\s+$//; - } - if ($variable eq "cycles/second") { - $cyclespernanosec = $value / 1e9; - $seen_clockrate = 1; - } elsif ($variable eq "sampling period") { - $sampling_period = $value; - } elsif ($variable eq "ms since reset") { - # Currently nothing is done with this value in pprof - # So we just silently ignore it for now - } elsif ($variable eq "discarded samples") { - # Currently nothing is done with this value in pprof - # So we just silently ignore it for now - } else { - printf STDERR ("Ignoring unnknown variable in /contention output: " . - "'%s' = '%s'\n",$variable,$value); - } - } else { - # Memory map entry - $map .= $line; - } - } - close PROFILE; - - if (!$seen_clockrate) { - printf STDERR ("No cycles/second entry in profile; Guessing %.1f GHz\n", - $cyclespernanosec); - } - - my $r = {}; - $r->{version} = 0; - $r->{period} = $sampling_period; - $r->{profile} = $profile; - $r->{libs} = ParseLibraries($prog, $map, $pcs); - $r->{pcs} = $pcs; - return $r; -} - -# Given a hex value in the form "0x1abcd" return "0001abcd" or -# "000000000001abcd", depending on the current address length. -# There's probably a more idiomatic (or faster) way to do this... -sub HexExtend { - my $addr = shift; - - $addr =~ s/^0x//; - - if (length $addr > $address_length) { - printf STDERR "Warning: address $addr is longer than address length $address_length\n"; - } - - return substr("000000000000000".$addr, -$address_length); -} - -##### Symbol extraction ##### - -# Aggressively search the lib_prefix values for the given library -# If all else fails, just return the name of the library unmodified. -# If the lib_prefix is "/my/path,/other/path" and $file is "/lib/dir/mylib.so" -# it will search the following locations in this order, until it finds a file: -# /my/path/lib/dir/mylib.so -# /other/path/lib/dir/mylib.so -# /my/path/dir/mylib.so -# /other/path/dir/mylib.so -# /my/path/mylib.so -# /other/path/mylib.so -# /lib/dir/mylib.so (returned as last resort) -sub FindLibrary { - my $file = shift; - my $suffix = $file; - - # Search for the library as described above - do { - foreach my $prefix (@prefix_list) { - my $fullpath = $prefix . $suffix; - if (-e $fullpath) { - return $fullpath; - } - } - } while ($suffix =~ s|^/[^/]+/|/|); - return $file; -} - -# Return path to library with debugging symbols. -# For libc libraries, the copy in /usr/lib/debug contains debugging symbols -sub DebuggingLibrary { - my $file = shift; - if ($file =~ m|^/| && -f "/usr/lib/debug$file") { - return "/usr/lib/debug$file"; - } - return undef; -} - -# Parse text section header of a library using objdump -sub ParseTextSectionHeaderFromObjdump { - my $lib = shift; - - my $size = undef; - my $vma; - my $file_offset; - # Get objdump output from the library file to figure out how to - # map between mapped addresses and addresses in the library. - my $objdump = $obj_tool_map{"objdump"}; - open(OBJDUMP, "$objdump -h $lib |") - || error("$objdump $lib: $!\n"); - while () { - s/\r//g; # turn windows-looking lines into unix-looking lines - # Idx Name Size VMA LMA File off Algn - # 10 .text 00104b2c 420156f0 420156f0 000156f0 2**4 - # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file - # offset may still be 8. But AddressSub below will still handle that. - my @x = split; - if (($#x >= 6) && ($x[1] eq '.text')) { - $size = $x[2]; - $vma = $x[3]; - $file_offset = $x[5]; - last; - } - } - close(OBJDUMP); - - if (!defined($size)) { - return undef; - } - - my $r = {}; - $r->{size} = $size; - $r->{vma} = $vma; - $r->{file_offset} = $file_offset; - - return $r; -} - -# Parse text section header of a library using otool (on OS X) -sub ParseTextSectionHeaderFromOtool { - my $lib = shift; - - my $size = undef; - my $vma = undef; - my $file_offset = undef; - # Get otool output from the library file to figure out how to - # map between mapped addresses and addresses in the library. - my $otool = $obj_tool_map{"otool"}; - open(OTOOL, "$otool -l $lib |") - || error("$otool $lib: $!\n"); - my $cmd = ""; - my $sectname = ""; - my $segname = ""; - foreach my $line () { - $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines - # Load command <#> - # cmd LC_SEGMENT - # [...] - # Section - # sectname __text - # segname __TEXT - # addr 0x000009f8 - # size 0x00018b9e - # offset 2552 - # align 2^2 (4) - # We will need to strip off the leading 0x from the hex addresses, - # and convert the offset into hex. - if ($line =~ /Load command/) { - $cmd = ""; - $sectname = ""; - $segname = ""; - } elsif ($line =~ /Section/) { - $sectname = ""; - $segname = ""; - } elsif ($line =~ /cmd (\w+)/) { - $cmd = $1; - } elsif ($line =~ /sectname (\w+)/) { - $sectname = $1; - } elsif ($line =~ /segname (\w+)/) { - $segname = $1; - } elsif (!(($cmd eq "LC_SEGMENT" || $cmd eq "LC_SEGMENT_64") && - $sectname eq "__text" && - $segname eq "__TEXT")) { - next; - } elsif ($line =~ /\baddr 0x([0-9a-fA-F]+)/) { - $vma = $1; - } elsif ($line =~ /\bsize 0x([0-9a-fA-F]+)/) { - $size = $1; - } elsif ($line =~ /\boffset ([0-9]+)/) { - $file_offset = sprintf("%016x", $1); - } - if (defined($vma) && defined($size) && defined($file_offset)) { - last; - } - } - close(OTOOL); - - if (!defined($vma) || !defined($size) || !defined($file_offset)) { - return undef; - } - - my $r = {}; - $r->{size} = $size; - $r->{vma} = $vma; - $r->{file_offset} = $file_offset; - - return $r; -} - -sub ParseTextSectionHeader { - # obj_tool_map("otool") is only defined if we're in a Mach-O environment - if (defined($obj_tool_map{"otool"})) { - my $r = ParseTextSectionHeaderFromOtool(@_); - if (defined($r)){ - return $r; - } - } - # If otool doesn't work, or we don't have it, fall back to objdump - return ParseTextSectionHeaderFromObjdump(@_); -} - -# Split /proc/pid/maps dump into a list of libraries -sub ParseLibraries { - return if $main::use_symbol_page; # We don't need libraries info. - my $prog = shift; - my $map = shift; - my $pcs = shift; - - my $result = []; - my $h = "[a-f0-9]+"; - my $zero_offset = HexExtend("0"); - - my $buildvar = ""; - foreach my $l (split("\n", $map)) { - if ($l =~ m/^\s*build=(.*)$/) { - $buildvar = $1; - } - - my $start; - my $finish; - my $offset; - my $lib; - if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?)$/i) { - # Full line from /proc/self/maps. Example: - # 40000000-40015000 r-xp 00000000 03:01 12845071 /lib/ld-2.3.2.so - $start = HexExtend($1); - $finish = HexExtend($2); - $offset = HexExtend($3); - $lib = $4; - $lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths - } elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) { - # Cooked line from DumpAddressMap. Example: - # 40000000-40015000: /lib/ld-2.3.2.so - $start = HexExtend($1); - $finish = HexExtend($2); - $offset = $zero_offset; - $lib = $3; - } else { - next; - } - - # Expand "$build" variable if available - $lib =~ s/\$build\b/$buildvar/g; - - $lib = FindLibrary($lib); - - # Check for pre-relocated libraries, which use pre-relocated symbol tables - # and thus require adjusting the offset that we'll use to translate - # VM addresses into symbol table addresses. - # Only do this if we're not going to fetch the symbol table from a - # debugging copy of the library. - if (!DebuggingLibrary($lib)) { - my $text = ParseTextSectionHeader($lib); - if (defined($text)) { - my $vma_offset = AddressSub($text->{vma}, $text->{file_offset}); - $offset = AddressAdd($offset, $vma_offset); - } - } - - push(@{$result}, [$lib, $start, $finish, $offset]); - } - - # Append special entry for additional library (not relocated) - if ($main::opt_lib ne "") { - my $text = ParseTextSectionHeader($main::opt_lib); - if (defined($text)) { - my $start = $text->{vma}; - my $finish = AddressAdd($start, $text->{size}); - - push(@{$result}, [$main::opt_lib, $start, $finish, $start]); - } - } - - # Append special entry for the main program. This covers - # 0..max_pc_value_seen, so that we assume pc values not found in one - # of the library ranges will be treated as coming from the main - # program binary. - my $min_pc = HexExtend("0"); - my $max_pc = $min_pc; # find the maximal PC value in any sample - foreach my $pc (keys(%{$pcs})) { - if (HexExtend($pc) gt $max_pc) { $max_pc = HexExtend($pc); } - } - push(@{$result}, [$prog, $min_pc, $max_pc, $zero_offset]); - - return $result; -} - -# Add two hex addresses of length $address_length. -# Run pprof --test for unit test if this is changed. -sub AddressAdd { - my $addr1 = shift; - my $addr2 = shift; - my $sum; - - if ($address_length == 8) { - # Perl doesn't cope with wraparound arithmetic, so do it explicitly: - $sum = (hex($addr1)+hex($addr2)) % (0x10000000 * 16); - return sprintf("%08x", $sum); - - } else { - # Do the addition in 7-nibble chunks to trivialize carry handling. - - if ($main::opt_debug and $main::opt_test) { - print STDERR "AddressAdd $addr1 + $addr2 = "; - } - - my $a1 = substr($addr1,-7); - $addr1 = substr($addr1,0,-7); - my $a2 = substr($addr2,-7); - $addr2 = substr($addr2,0,-7); - $sum = hex($a1) + hex($a2); - my $c = 0; - if ($sum > 0xfffffff) { - $c = 1; - $sum -= 0x10000000; - } - my $r = sprintf("%07x", $sum); - - $a1 = substr($addr1,-7); - $addr1 = substr($addr1,0,-7); - $a2 = substr($addr2,-7); - $addr2 = substr($addr2,0,-7); - $sum = hex($a1) + hex($a2) + $c; - $c = 0; - if ($sum > 0xfffffff) { - $c = 1; - $sum -= 0x10000000; - } - $r = sprintf("%07x", $sum) . $r; - - $sum = hex($addr1) + hex($addr2) + $c; - if ($sum > 0xff) { $sum -= 0x100; } - $r = sprintf("%02x", $sum) . $r; - - if ($main::opt_debug and $main::opt_test) { print STDERR "$r\n"; } - - return $r; - } -} - - -# Subtract two hex addresses of length $address_length. -# Run pprof --test for unit test if this is changed. -sub AddressSub { - my $addr1 = shift; - my $addr2 = shift; - my $diff; - - if ($address_length == 8) { - # Perl doesn't cope with wraparound arithmetic, so do it explicitly: - $diff = (hex($addr1)-hex($addr2)) % (0x10000000 * 16); - return sprintf("%08x", $diff); - - } else { - # Do the addition in 7-nibble chunks to trivialize borrow handling. - # if ($main::opt_debug) { print STDERR "AddressSub $addr1 - $addr2 = "; } - - my $a1 = hex(substr($addr1,-7)); - $addr1 = substr($addr1,0,-7); - my $a2 = hex(substr($addr2,-7)); - $addr2 = substr($addr2,0,-7); - my $b = 0; - if ($a2 > $a1) { - $b = 1; - $a1 += 0x10000000; - } - $diff = $a1 - $a2; - my $r = sprintf("%07x", $diff); - - $a1 = hex(substr($addr1,-7)); - $addr1 = substr($addr1,0,-7); - $a2 = hex(substr($addr2,-7)) + $b; - $addr2 = substr($addr2,0,-7); - $b = 0; - if ($a2 > $a1) { - $b = 1; - $a1 += 0x10000000; - } - $diff = $a1 - $a2; - $r = sprintf("%07x", $diff) . $r; - - $a1 = hex($addr1); - $a2 = hex($addr2) + $b; - if ($a2 > $a1) { $a1 += 0x100; } - $diff = $a1 - $a2; - $r = sprintf("%02x", $diff) . $r; - - # if ($main::opt_debug) { print STDERR "$r\n"; } - - return $r; - } -} - -# Increment a hex addresses of length $address_length. -# Run pprof --test for unit test if this is changed. -sub AddressInc { - my $addr = shift; - my $sum; - - if ($address_length == 8) { - # Perl doesn't cope with wraparound arithmetic, so do it explicitly: - $sum = (hex($addr)+1) % (0x10000000 * 16); - return sprintf("%08x", $sum); - - } else { - # Do the addition in 7-nibble chunks to trivialize carry handling. - # We are always doing this to step through the addresses in a function, - # and will almost never overflow the first chunk, so we check for this - # case and exit early. - - # if ($main::opt_debug) { print STDERR "AddressInc $addr1 = "; } - - my $a1 = substr($addr,-7); - $addr = substr($addr,0,-7); - $sum = hex($a1) + 1; - my $r = sprintf("%07x", $sum); - if ($sum <= 0xfffffff) { - $r = $addr . $r; - # if ($main::opt_debug) { print STDERR "$r\n"; } - return HexExtend($r); - } else { - $r = "0000000"; - } - - $a1 = substr($addr,-7); - $addr = substr($addr,0,-7); - $sum = hex($a1) + 1; - $r = sprintf("%07x", $sum) . $r; - if ($sum <= 0xfffffff) { - $r = $addr . $r; - # if ($main::opt_debug) { print STDERR "$r\n"; } - return HexExtend($r); - } else { - $r = "00000000000000"; - } - - $sum = hex($addr) + 1; - if ($sum > 0xff) { $sum -= 0x100; } - $r = sprintf("%02x", $sum) . $r; - - # if ($main::opt_debug) { print STDERR "$r\n"; } - return $r; - } -} - -# Extract symbols for all PC values found in profile -sub ExtractSymbols { - my $libs = shift; - my $pcset = shift; - - my $symbols = {}; - - # Map each PC value to the containing library - my %seen = (); - foreach my $lib (@{$libs}) { - my $libname = $lib->[0]; - my $start = $lib->[1]; - my $finish = $lib->[2]; - my $offset = $lib->[3]; - - # Get list of pcs that belong in this library. - my $contained = []; - foreach my $pc (keys(%{$pcset})) { - if (!$seen{$pc} && ($pc ge $start) && ($pc le $finish)) { - $seen{$pc} = 1; - push(@{$contained}, $pc); - } - } - # Map to symbols - MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols); - } - - return $symbols; -} - -# Map list of PC values to symbols for a given image -sub MapToSymbols { - my $image = shift; - my $offset = shift; - my $pclist = shift; - my $symbols = shift; - - my $debug = 0; - - # Ignore empty binaries - if ($#{$pclist} < 0) { return; } - - # Figure out the addr2line command to use - my $addr2line = $obj_tool_map{"addr2line"}; - my $cmd = "$addr2line -f -C -e $image"; - if (exists $obj_tool_map{"addr2line_pdb"}) { - $addr2line = $obj_tool_map{"addr2line_pdb"}; - $cmd = "$addr2line --demangle -f -C -e $image"; - } - - # If "addr2line" isn't installed on the system at all, just use - # nm to get what info we can (function names, but not line numbers). - if (system("$addr2line --help >/dev/null 2>&1") != 0) { - MapSymbolsWithNM($image, $offset, $pclist, $symbols); - return; - } - - # "addr2line -i" can produce a variable number of lines per input - # address, with no separator that allows us to tell when data for - # the next address starts. So we find the address for a special - # symbol (_fini) and interleave this address between all real - # addresses passed to addr2line. The name of this special symbol - # can then be used as a separator. - $sep_address = undef; # May be filled in by MapSymbolsWithNM() - my $nm_symbols = {}; - MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols); - # TODO(csilvers): only add '-i' if addr2line supports it. - if (defined($sep_address)) { - # Only add " -i" to addr2line if the binary supports it. - # addr2line --help returns 0, but not if it sees an unknown flag first. - if (system("$cmd -i --help >/dev/null 2>&1") == 0) { - $cmd .= " -i"; - } else { - $sep_address = undef; # no need for sep_address if we don't support -i - } - } - - # Make file with all PC values with intervening 'sep_address' so - # that we can reliably detect the end of inlined function list - open(ADDRESSES, ">$main::tmpfile_sym") || error("$main::tmpfile_sym: $!\n"); - if ($debug) { print("---- $image ---\n"); } - for (my $i = 0; $i <= $#{$pclist}; $i++) { - # addr2line always reads hex addresses, and does not need '0x' prefix. - if ($debug) { printf STDERR ("%s\n", $pclist->[$i]); } - printf ADDRESSES ("%s\n", AddressSub($pclist->[$i], $offset)); - if (defined($sep_address)) { - printf ADDRESSES ("%s\n", $sep_address); - } - } - close(ADDRESSES); - if ($debug) { - print("----\n"); - system("cat $main::tmpfile_sym"); - print("----\n"); - system("$cmd <$main::tmpfile_sym"); - print("----\n"); - } - - open(SYMBOLS, "$cmd <$main::tmpfile_sym |") || error("$cmd: $!\n"); - my $count = 0; # Index in pclist - while () { - # Read fullfunction and filelineinfo from next pair of lines - s/\r?\n$//g; - my $fullfunction = $_; - $_ = ; - s/\r?\n$//g; - my $filelinenum = $_; - - if (defined($sep_address) && $fullfunction eq $sep_symbol) { - # Terminating marker for data for this address - $count++; - next; - } - - $filelinenum =~ s|\\|/|g; # turn windows-style paths into unix-style paths - - my $pcstr = $pclist->[$count]; - my $function = ShortFunctionName($fullfunction); - if ($fullfunction eq '??') { - # See if nm found a symbol - my $nms = $nm_symbols->{$pcstr}; - if (defined($nms)) { - $function = $nms->[0]; - $fullfunction = $nms->[2]; - } - } - - # Prepend to accumulated symbols for pcstr - # (so that caller comes before callee) - my $sym = $symbols->{$pcstr}; - if (!defined($sym)) { - $sym = []; - $symbols->{$pcstr} = $sym; - } - unshift(@{$sym}, $function, $filelinenum, $fullfunction); - if ($debug) { printf STDERR ("%s => [%s]\n", $pcstr, join(" ", @{$sym})); } - if (!defined($sep_address)) { - # Inlining is off, se this entry ends immediately - $count++; - } - } - close(SYMBOLS); -} - -# Use nm to map the list of referenced PCs to symbols. Return true iff we -# are able to read procedure information via nm. -sub MapSymbolsWithNM { - my $image = shift; - my $offset = shift; - my $pclist = shift; - my $symbols = shift; - - # Get nm output sorted by increasing address - my $symbol_table = GetProcedureBoundaries($image, "."); - if (!%{$symbol_table}) { - return 0; - } - # Start addresses are already the right length (8 or 16 hex digits). - my @names = sort { $symbol_table->{$a}->[0] cmp $symbol_table->{$b}->[0] } - keys(%{$symbol_table}); - - if ($#names < 0) { - # No symbols: just use addresses - foreach my $pc (@{$pclist}) { - my $pcstr = "0x" . $pc; - $symbols->{$pc} = [$pcstr, "?", $pcstr]; - } - return 0; - } - - # Sort addresses so we can do a join against nm output - my $index = 0; - my $fullname = $names[0]; - my $name = ShortFunctionName($fullname); - foreach my $pc (sort { $a cmp $b } @{$pclist}) { - # Adjust for mapped offset - my $mpc = AddressSub($pc, $offset); - while (($index < $#names) && ($mpc ge $symbol_table->{$fullname}->[1])){ - $index++; - $fullname = $names[$index]; - $name = ShortFunctionName($fullname); - } - if ($mpc lt $symbol_table->{$fullname}->[1]) { - $symbols->{$pc} = [$name, "?", $fullname]; - } else { - my $pcstr = "0x" . $pc; - $symbols->{$pc} = [$pcstr, "?", $pcstr]; - } - } - return 1; -} - -sub ShortFunctionName { - my $function = shift; - while ($function =~ s/\([^()]*\)(\s*const)?//g) { } # Argument types - while ($function =~ s/<[^<>]*>//g) { } # Remove template arguments - $function =~ s/^.*\s+(\w+::)/$1/; # Remove leading type - return $function; -} - -# Trim overly long symbols found in disassembler output -sub CleanDisassembly { - my $d = shift; - while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax) - while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments - return $d; -} - -##### Miscellaneous ##### - -# Find the right versions of the above object tools to use. The -# argument is the program file being analyzed, and should be an ELF -# 32-bit or ELF 64-bit executable file. The location of the tools -# is determined by considering the following options in this order: -# 1) --tools option, if set -# 2) PPROF_TOOLS environment variable, if set -# 3) the environment -sub ConfigureObjTools { - my $prog_file = shift; - - # Check for the existence of $prog_file because /usr/bin/file does not - # predictably return error status in prod. - (-e $prog_file) || error("$prog_file does not exist.\n"); - - # Follow symlinks (at least for systems where "file" supports that) - my $file_type = `/usr/bin/file -L $prog_file 2>/dev/null || /usr/bin/file $prog_file`; - if ($file_type =~ /64-bit/) { - # Change $address_length to 16 if the program file is ELF 64-bit. - # We can't detect this from many (most?) heap or lock contention - # profiles, since the actual addresses referenced are generally in low - # memory even for 64-bit programs. - $address_length = 16; - } - - if ($file_type =~ /MS Windows/) { - # For windows, we provide a version of nm and addr2line as part of - # the opensource release, which is capable of parsing - # Windows-style PDB executables. It should live in the path, or - # in the same directory as pprof. - $obj_tool_map{"nm_pdb"} = "nm-pdb"; - $obj_tool_map{"addr2line_pdb"} = "addr2line-pdb"; - } - - if ($file_type =~ /Mach-O/) { - # OS X uses otool to examine Mach-O files, rather than objdump. - $obj_tool_map{"otool"} = "otool"; - $obj_tool_map{"addr2line"} = "false"; # no addr2line - $obj_tool_map{"objdump"} = "false"; # no objdump - } - - # Go fill in %obj_tool_map with the pathnames to use: - foreach my $tool (keys %obj_tool_map) { - $obj_tool_map{$tool} = ConfigureTool($obj_tool_map{$tool}); - } -} - -# Returns the path of a caller-specified object tool. If --tools or -# PPROF_TOOLS are specified, then returns the full path to the tool -# with that prefix. Otherwise, returns the path unmodified (which -# means we will look for it on PATH). -sub ConfigureTool { - my $tool = shift; - my $path; - - if ($main::opt_tools ne "") { - # Use a prefix specified by the --tools option... - $path = $main::opt_tools . $tool; - if (!-x $path) { - error("No '$tool' found with prefix specified by --tools $main::opt_tools\n"); - } - } elsif (exists $ENV{"PPROF_TOOLS"} && - $ENV{"PPROF_TOOLS"} ne "") { - #... or specified with the PPROF_TOOLS environment variable... - $path = $ENV{"PPROF_TOOLS"} . $tool; - if (!-x $path) { - error("No '$tool' found with prefix specified by PPROF_TOOLS=$ENV{PPROF_TOOLS}\n"); - } - } else { - # ... otherwise use the version that exists in the same directory as - # pprof. If there's nothing there, use $PATH. - $0 =~ m,[^/]*$,; # this is everything after the last slash - my $dirname = $`; # this is everything up to and including the last slash - if (-x "$dirname$tool") { - $path = "$dirname$tool"; - } else { - $path = $tool; - } - } - if ($main::opt_debug) { print STDERR "Using '$path' for '$tool'.\n"; } - return $path; -} - -sub cleanup { - unlink($main::tmpfile_sym); - unlink(keys %main::tempnames); - - # We leave any collected profiles in $HOME/pprof in case the user wants - # to look at them later. We print a message informing them of this. - if ((scalar(@main::profile_files) > 0) && - defined($main::collected_profile)) { - if (scalar(@main::profile_files) == 1) { - print STDERR "Dynamically gathered profile is in $main::collected_profile\n"; - } - print STDERR "If you want to investigate this profile further, you can do:\n"; - print STDERR "\n"; - print STDERR " pprof \\\n"; - print STDERR " $main::prog \\\n"; - print STDERR " $main::collected_profile\n"; - print STDERR "\n"; - } -} - -sub sighandler { - cleanup(); - exit(1); -} - -sub error { - my $msg = shift; - print STDERR $msg; - cleanup(); - exit(1); -} - - -# Run $nm_command and get all the resulting procedure boundaries whose -# names match "$regexp" and returns them in a hashtable mapping from -# procedure name to a two-element vector of [start address, end address] -sub GetProcedureBoundariesViaNm { - my $nm_command = shift; - my $regexp = shift; - - my $symbol_table = {}; - open(NM, "$nm_command |") || error("$nm_command: $!\n"); - my $last_start = "0"; - my $routine = ""; - while () { - s/\r//g; # turn windows-looking lines into unix-looking lines - if (m/^\s*([0-9a-f]+) (.) (..*)/) { - my $start_val = $1; - my $type = $2; - my $this_routine = $3; - - # It's possible for two symbols to share the same address, if - # one is a zero-length variable (like __start_google_malloc) or - # one symbol is a weak alias to another (like __libc_malloc). - # In such cases, we want to ignore all values except for the - # actual symbol, which in nm-speak has type "T". The logic - # below does this, though it's a bit tricky: what happens when - # we have a series of lines with the same address, is the first - # one gets queued up to be processed. However, it won't - # *actually* be processed until later, when we read a line with - # a different address. That means that as long as we're reading - # lines with the same address, we have a chance to replace that - # item in the queue, which we do whenever we see a 'T' entry -- - # that is, a line with type 'T'. If we never see a 'T' entry, - # we'll just go ahead and process the first entry (which never - # got touched in the queue), and ignore the others. - if ($start_val eq $last_start && $type =~ /t/i) { - # We are the 'T' symbol at this address, replace previous symbol. - $routine = $this_routine; - next; - } elsif ($start_val eq $last_start) { - # We're not the 'T' symbol at this address, so ignore us. - next; - } - - if ($this_routine eq $sep_symbol) { - $sep_address = HexExtend($start_val); - } - - # Tag this routine with the starting address in case the image - # has multiple occurrences of this routine. We use a syntax - # that resembles template paramters that are automatically - # stripped out by ShortFunctionName() - $this_routine .= "<$start_val>"; - - if (defined($routine) && $routine =~ m/$regexp/) { - $symbol_table->{$routine} = [HexExtend($last_start), - HexExtend($start_val)]; - } - $last_start = $start_val; - $routine = $this_routine; - } elsif (m/^Loaded image name: (.+)/) { - # The win32 nm workalike emits information about the binary it is using. - if ($main::opt_debug) { print STDERR "Using Image $1\n"; } - } elsif (m/^PDB file name: (.+)/) { - # The win32 nm workalike emits information about the pdb it is using. - if ($main::opt_debug) { print STDERR "Using PDB $1\n"; } - } - } - close(NM); - # Handle the last line in the nm output. Unfortunately, we don't know - # how big this last symbol is, because we don't know how big the file - # is. For now, we just give it a size of 0. - # TODO(csilvers): do better here. - if (defined($routine) && $routine =~ m/$regexp/) { - $symbol_table->{$routine} = [HexExtend($last_start), - HexExtend($last_start)]; - } - return $symbol_table; -} - -# Gets the procedure boundaries for all routines in "$image" whose names -# match "$regexp" and returns them in a hashtable mapping from procedure -# name to a two-element vector of [start address, end address]. -# Will return an empty map if nm is not installed or not working properly. -sub GetProcedureBoundaries { - my $image = shift; - my $regexp = shift; - - # For libc libraries, the copy in /usr/lib/debug contains debugging symbols - my $debugging = DebuggingLibrary($image); - if ($debugging) { - $image = $debugging; - } - - my $nm = $obj_tool_map{"nm"}; - my $cppfilt = $obj_tool_map{"c++filt"}; - - # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm - # binary doesn't support --demangle. In addition, for OS X we need - # to use the -f flag to get 'flat' nm output (otherwise we don't sort - # properly and get incorrect results). Unfortunately, GNU nm uses -f - # in an incompatible way. So first we test whether our nm supports - # --demangle and -f. - my $demangle_flag = ""; - my $cppfilt_flag = ""; - if (system("$nm --demangle $image >/dev/null 2>&1") == 0) { - # In this mode, we do "nm --demangle " - $demangle_flag = "--demangle"; - $cppfilt_flag = ""; - } elsif (system("$cppfilt $image >/dev/null 2>&1") == 0) { - # In this mode, we do "nm | c++filt" - $cppfilt_flag = " | $cppfilt"; - }; - my $flatten_flag = ""; - if (system("$nm -f $image >/dev/null 2>&1") == 0) { - $flatten_flag = "-f"; - } - - # Finally, in the case $imagie isn't a debug library, we try again with - # -D to at least get *exported* symbols. If we can't use --demangle, - # we use c++filt instead, if it exists on this system. - my @nm_commands = ("$nm -n $flatten_flag $demangle_flag" . - " $image 2>/dev/null $cppfilt_flag", - "$nm -D -n $flatten_flag $demangle_flag" . - " $image 2>/dev/null $cppfilt_flag", - # 6nm is for Go binaries - "6nm $image 2>/dev/null | sort"); - - # If the executable is an MS Windows PDB-format executable, we'll - # have set up obj_tool_map("nm_pdb"). In this case, we actually - # want to use both unix nm and windows-specific nm_pdb, since - # PDB-format executables can apparently include dwarf .o files. - if (exists $obj_tool_map{"nm_pdb"}) { - my $nm_pdb = $obj_tool_map{"nm_pdb"}; - push(@nm_commands, "$nm_pdb --demangle $image 2>/dev/null"); - } - - foreach my $nm_command (@nm_commands) { - my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp); - return $symbol_table if (%{$symbol_table}); - } - my $symbol_table = {}; - return $symbol_table; -} - - -# The test vectors for AddressAdd/Sub/Inc are 8-16-nibble hex strings. -# To make them more readable, we add underscores at interesting places. -# This routine removes the underscores, producing the canonical representation -# used by pprof to represent addresses, particularly in the tested routines. -sub CanonicalHex { - my $arg = shift; - return join '', (split '_',$arg); -} - - -# Unit test for AddressAdd: -sub AddressAddUnitTest { - my $test_data_8 = shift; - my $test_data_16 = shift; - my $error_count = 0; - my $fail_count = 0; - my $pass_count = 0; - # print STDERR "AddressAddUnitTest: ", 1+$#{$test_data_8}, " tests\n"; - - # First a few 8-nibble addresses. Note that this implementation uses - # plain old arithmetic, so a quick sanity check along with verifying what - # happens to overflow (we want it to wrap): - $address_length = 8; - foreach my $row (@{$test_data_8}) { - if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; } - my $sum = AddressAdd ($row->[0], $row->[1]); - if ($sum ne $row->[2]) { - printf STDERR "ERROR: %s != %s + %s = %s\n", $sum, - $row->[0], $row->[1], $row->[2]; - ++$fail_count; - } else { - ++$pass_count; - } - } - printf STDERR "AddressAdd 32-bit tests: %d passes, %d failures\n", - $pass_count, $fail_count; - $error_count = $fail_count; - $fail_count = 0; - $pass_count = 0; - - # Now 16-nibble addresses. - $address_length = 16; - foreach my $row (@{$test_data_16}) { - if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; } - my $sum = AddressAdd (CanonicalHex($row->[0]), CanonicalHex($row->[1])); - my $expected = join '', (split '_',$row->[2]); - if ($sum ne CanonicalHex($row->[2])) { - printf STDERR "ERROR: %s != %s + %s = %s\n", $sum, - $row->[0], $row->[1], $row->[2]; - ++$fail_count; - } else { - ++$pass_count; - } - } - printf STDERR "AddressAdd 64-bit tests: %d passes, %d failures\n", - $pass_count, $fail_count; - $error_count += $fail_count; - - return $error_count; -} - - -# Unit test for AddressSub: -sub AddressSubUnitTest { - my $test_data_8 = shift; - my $test_data_16 = shift; - my $error_count = 0; - my $fail_count = 0; - my $pass_count = 0; - # print STDERR "AddressSubUnitTest: ", 1+$#{$test_data_8}, " tests\n"; - - # First a few 8-nibble addresses. Note that this implementation uses - # plain old arithmetic, so a quick sanity check along with verifying what - # happens to overflow (we want it to wrap): - $address_length = 8; - foreach my $row (@{$test_data_8}) { - if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; } - my $sum = AddressSub ($row->[0], $row->[1]); - if ($sum ne $row->[3]) { - printf STDERR "ERROR: %s != %s - %s = %s\n", $sum, - $row->[0], $row->[1], $row->[3]; - ++$fail_count; - } else { - ++$pass_count; - } - } - printf STDERR "AddressSub 32-bit tests: %d passes, %d failures\n", - $pass_count, $fail_count; - $error_count = $fail_count; - $fail_count = 0; - $pass_count = 0; - - # Now 16-nibble addresses. - $address_length = 16; - foreach my $row (@{$test_data_16}) { - if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; } - my $sum = AddressSub (CanonicalHex($row->[0]), CanonicalHex($row->[1])); - if ($sum ne CanonicalHex($row->[3])) { - printf STDERR "ERROR: %s != %s - %s = %s\n", $sum, - $row->[0], $row->[1], $row->[3]; - ++$fail_count; - } else { - ++$pass_count; - } - } - printf STDERR "AddressSub 64-bit tests: %d passes, %d failures\n", - $pass_count, $fail_count; - $error_count += $fail_count; - - return $error_count; -} - - -# Unit test for AddressInc: -sub AddressIncUnitTest { - my $test_data_8 = shift; - my $test_data_16 = shift; - my $error_count = 0; - my $fail_count = 0; - my $pass_count = 0; - # print STDERR "AddressIncUnitTest: ", 1+$#{$test_data_8}, " tests\n"; - - # First a few 8-nibble addresses. Note that this implementation uses - # plain old arithmetic, so a quick sanity check along with verifying what - # happens to overflow (we want it to wrap): - $address_length = 8; - foreach my $row (@{$test_data_8}) { - if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; } - my $sum = AddressInc ($row->[0]); - if ($sum ne $row->[4]) { - printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum, - $row->[0], $row->[4]; - ++$fail_count; - } else { - ++$pass_count; - } - } - printf STDERR "AddressInc 32-bit tests: %d passes, %d failures\n", - $pass_count, $fail_count; - $error_count = $fail_count; - $fail_count = 0; - $pass_count = 0; - - # Now 16-nibble addresses. - $address_length = 16; - foreach my $row (@{$test_data_16}) { - if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; } - my $sum = AddressInc (CanonicalHex($row->[0])); - if ($sum ne CanonicalHex($row->[4])) { - printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum, - $row->[0], $row->[4]; - ++$fail_count; - } else { - ++$pass_count; - } - } - printf STDERR "AddressInc 64-bit tests: %d passes, %d failures\n", - $pass_count, $fail_count; - $error_count += $fail_count; - - return $error_count; -} - - -# Driver for unit tests. -# Currently just the address add/subtract/increment routines for 64-bit. -sub RunUnitTests { - my $error_count = 0; - - # This is a list of tuples [a, b, a+b, a-b, a+1] - my $unit_test_data_8 = [ - [qw(aaaaaaaa 50505050 fafafafa 5a5a5a5a aaaaaaab)], - [qw(50505050 aaaaaaaa fafafafa a5a5a5a6 50505051)], - [qw(ffffffff aaaaaaaa aaaaaaa9 55555555 00000000)], - [qw(00000001 ffffffff 00000000 00000002 00000002)], - [qw(00000001 fffffff0 fffffff1 00000011 00000002)], - ]; - my $unit_test_data_16 = [ - # The implementation handles data in 7-nibble chunks, so those are the - # interesting boundaries. - [qw(aaaaaaaa 50505050 - 00_000000f_afafafa 00_0000005_a5a5a5a 00_000000a_aaaaaab)], - [qw(50505050 aaaaaaaa - 00_000000f_afafafa ff_ffffffa_5a5a5a6 00_0000005_0505051)], - [qw(ffffffff aaaaaaaa - 00_000001a_aaaaaa9 00_0000005_5555555 00_0000010_0000000)], - [qw(00000001 ffffffff - 00_0000010_0000000 ff_ffffff0_0000002 00_0000000_0000002)], - [qw(00000001 fffffff0 - 00_000000f_ffffff1 ff_ffffff0_0000011 00_0000000_0000002)], - - [qw(00_a00000a_aaaaaaa 50505050 - 00_a00000f_afafafa 00_a000005_a5a5a5a 00_a00000a_aaaaaab)], - [qw(0f_fff0005_0505050 aaaaaaaa - 0f_fff000f_afafafa 0f_ffefffa_5a5a5a6 0f_fff0005_0505051)], - [qw(00_000000f_fffffff 01_800000a_aaaaaaa - 01_800001a_aaaaaa9 fe_8000005_5555555 00_0000010_0000000)], - [qw(00_0000000_0000001 ff_fffffff_fffffff - 00_0000000_0000000 00_0000000_0000002 00_0000000_0000002)], - [qw(00_0000000_0000001 ff_fffffff_ffffff0 - ff_fffffff_ffffff1 00_0000000_0000011 00_0000000_0000002)], - ]; - - $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16); - $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16); - $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16); - if ($error_count > 0) { - print STDERR $error_count, " errors: FAILED\n"; - } else { - print STDERR "PASS\n"; - } - exit ($error_count); -} diff --git a/src/cmd/prof/main.c b/src/cmd/prof/main.c deleted file mode 100644 index f36759cd3..000000000 --- a/src/cmd/prof/main.c +++ /dev/null @@ -1,895 +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 -#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; - - 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)); - 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() -{ - ppstart = nppdata; - addppword(~0); -} - -void -endpptrace() -{ - ppdata[ppstart] = nppdata-ppstart-1; -} - -uvlong nextpc; - -void -xptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym) -{ - 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); - 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() -{ - 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]); - 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() -{ - 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)); - 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 new file mode 100644 index 000000000..2a35d1ae3 --- /dev/null +++ b/src/cmd/vet/Makefile @@ -0,0 +1,7 @@ +# 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. + +test testshort: + go build + ../../../test/errchk ./vet -printfuncs='Warn:1,Warnf:1' print.go diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go new file mode 100644 index 000000000..620964aaf --- /dev/null +++ b/src/cmd/vet/doc.go @@ -0,0 +1,62 @@ +// 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. + +/* + +Vet examines Go source code and reports suspicious constructs, such as Printf +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. + +Available checks: + +1. Printf family + +Suspicious calls to functions in the Printf family, including any functions +with these names: + Print Printf Println + Fprint Fprintf Fprintln + Sprint Sprintf Sprintln + Error Errorf + Fatal Fatalf + Panic Panicf Panicln +If the function name ends with an 'f', the function is assumed to take +a format descriptor string in the manner of fmt.Printf. If not, vet +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 + +Non-standard signatures for methods with familiar names, including: + Format GobEncode GobDecode MarshalJSON MarshalXML + Peek ReadByte ReadFrom ReadRune Scan Seek + UnmarshalJSON UnreadByte UnreadRune WriteByte + WriteTo + +3. Struct tags + +Struct tags that do not follow the format understood by reflect.StructTag.Get. + +Usage: + + go tool vet [flag] [file.go ...] + go tool vet [flag] [directory ...] # Scan all .go files under directory, recursively + +The flags are: + -v + Verbose mode + -printfuncs + A comma-separated list of print-like functions to supplement + the standard list. Each entry is in the form Name:N where N + is the zero-based argument position of the first argument + involved in the print: either the format or the first print + argument for non-formatted prints. For example, + if you have Warn and Warnf functions that take an + io.Writer as their first argument, like Fprintf, + -printfuncs=Warn:1,Warnf:1 + +*/ +package documentation diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go new file mode 100644 index 000000000..625133315 --- /dev/null +++ b/src/cmd/vet/main.go @@ -0,0 +1,239 @@ +// 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. + +// Vet is a simple checker for static errors in Go source code. +// See doc.go for more information. +package main + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/parser" + "go/token" + "io" + "os" + "path/filepath" + "strconv" + "strings" +) + +var verbose = flag.Bool("v", false, "verbose") +var exitCode = 0 + +// setExit sets the value for os.Exit when it is called, later. It +// remembers the highest value. +func setExit(err int) { + if err > exitCode { + exitCode = err + } +} + +// Usage is a replacement usage function for the flags package. +func Usage() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + flag.PrintDefaults() + os.Exit(2) +} + +// 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 { + fset *token.FileSet + file *ast.File + b bytes.Buffer // for use by methods +} + +func main() { + flag.Usage = Usage + flag.Parse() + + if *printfuncs != "" { + for _, name := range strings.Split(*printfuncs, ",") { + if len(name) == 0 { + flag.Usage() + } + skip := 0 + if colon := strings.LastIndex(name, ":"); colon > 0 { + var err error + skip, err = strconv.Atoi(name[colon+1:]) + if err != nil { + errorf(`illegal format for "Func:N" argument %q; %s`, name, err) + } + name = name[:colon] + } + name = strings.ToLower(name) + if name[len(name)-1] == 'f' { + printfList[name] = skip + } else { + printList[name] = skip + } + } + } + + if flag.NArg() == 0 { + doFile("stdin", os.Stdin) + } else { + 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) + } + } + } + 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) + if err != nil { + errorf("%s: %s", name, err) + return + } + file := &File{fset: fs, file: parsedFile} + file.walkFile(name, parsedFile) +} + +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) + } + return nil +} + +// walkDir recursively walks the tree looking for .go files. +func walkDir(root string) { + filepath.Walk(root, visit) +} + +// error formats the error to standard error, adding program +// identification and a newline +func errorf(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) + setExit(2) +} + +// Println is fmt.Println guarded by -v. +func Println(args ...interface{}) { + if !*verbose { + return + } + fmt.Println(args...) +} + +// Printf is fmt.Printf guarded by -v. +func Printf(format string, args ...interface{}) { + if !*verbose { + return + } + fmt.Printf(format+"\n", args...) +} + +// Bad reports an error and sets the exit code.. +func (f *File) Bad(pos token.Pos, args ...interface{}) { + f.Warn(pos, args...) + setExit(1) +} + +// Badf reports a formatted error and sets the exit code. +func (f *File) Badf(pos token.Pos, format string, args ...interface{}) { + f.Warnf(pos, format, args...) + setExit(1) +} + +// 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...)) +} + +// 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...) +} + +// walkFile walks the file's tree. +func (f *File) walkFile(name string, file *ast.File) { + Println("Checking file", name) + ast.Walk(f, file) +} + +// Visit implements the ast.Visitor interface. +func (f *File) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.CallExpr: + f.walkCallExpr(n) + case *ast.CompositeLit: + f.walkCompositeLit(n) + case *ast.Field: + f.walkFieldTag(n) + case *ast.FuncDecl: + f.walkMethodDecl(n) + case *ast.InterfaceType: + f.walkInterfaceType(n) + } + return f +} + +// walkCall walks a call expression. +func (f *File) walkCall(call *ast.CallExpr, name string) { + f.checkFmtPrintfCall(call, name) +} + +// walkCompositeLit walks a composite literal. +func (f *File) walkCompositeLit(c *ast.CompositeLit) { + f.checkUntaggedLiteral(c) +} + +// walkFieldTag walks a struct field tag. +func (f *File) walkFieldTag(field *ast.Field) { + if field.Tag == nil { + return + } + f.checkCanonicalFieldTag(field) +} + +// walkMethodDecl walks the method's signature. +func (f *File) walkMethod(id *ast.Ident, t *ast.FuncType) { + f.checkCanonicalMethod(id, t) +} + +// walkMethodDecl walks the method signature in the declaration. +func (f *File) walkMethodDecl(d *ast.FuncDecl) { + if d.Recv == nil { + // not a method + return + } + f.walkMethod(d.Name, d.Type) +} + +// walkInterfaceType walks the method signatures of an interface. +func (f *File) walkInterfaceType(t *ast.InterfaceType) { + for _, field := range t.Methods.List { + for _, id := range field.Names { + f.walkMethod(id, field.Type.(*ast.FuncType)) + } + } +} + +// 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) + } +} diff --git a/src/cmd/vet/method.go b/src/cmd/vet/method.go new file mode 100644 index 000000000..41cb40ff9 --- /dev/null +++ b/src/cmd/vet/method.go @@ -0,0 +1,161 @@ +// 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 the code to check canonical methods. + +package main + +import ( + "fmt" + "go/ast" + "go/printer" + "strings" +) + +type MethodSig struct { + args []string + results []string +} + +// canonicalMethods lists the input and output types for Go methods +// that are checked using dynamic interface checks. Because the +// checks are dynamic, such methods would not cause a compile error +// if they have the wrong signature: instead the dynamic check would +// fail, sometimes mysteriously. If a method is found with a name listed +// here but not the input/output types listed here, vet complains. +// +// A few of the canonical methods have very common names. +// For example, a type might implement a Scan method that +// has nothing to do with fmt.Scanner, but we still want to check +// the methods that are intended to implement fmt.Scanner. +// To do that, the arguments that have a = prefix are treated as +// signals that the canonical meaning is intended: if a Scan +// method doesn't have a fmt.ScanState as its first argument, +// we let it go. But if it does have a fmt.ScanState, then the +// rest has to match. +var canonicalMethods = map[string]MethodSig{ + // "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict + "Format": {[]string{"=fmt.State", "rune"}, []string{}}, // fmt.Formatter + "GobDecode": {[]string{"[]byte"}, []string{"error"}}, // gob.GobDecoder + "GobEncode": {[]string{}, []string{"[]byte", "error"}}, // gob.GobEncoder + "MarshalJSON": {[]string{}, []string{"[]byte", "error"}}, // json.Marshaler + "MarshalXML": {[]string{}, []string{"[]byte", "error"}}, // xml.Marshaler + "Peek": {[]string{"=int"}, []string{"[]byte", "error"}}, // image.reader (matching bufio.Reader) + "ReadByte": {[]string{}, []string{"byte", "error"}}, // io.ByteReader + "ReadFrom": {[]string{"=io.Reader"}, []string{"int64", "error"}}, // io.ReaderFrom + "ReadRune": {[]string{}, []string{"rune", "int", "error"}}, // io.RuneReader + "Scan": {[]string{"=fmt.ScanState", "rune"}, []string{"error"}}, // fmt.Scanner + "Seek": {[]string{"=int64", "int"}, []string{"int64", "error"}}, // io.Seeker + "UnmarshalJSON": {[]string{"[]byte"}, []string{"error"}}, // json.Unmarshaler + "UnreadByte": {[]string{}, []string{"error"}}, + "UnreadRune": {[]string{}, []string{"error"}}, + "WriteByte": {[]string{"byte"}, []string{"error"}}, // jpeg.writer (matching bufio.Writer) + "WriteTo": {[]string{"=io.Writer"}, []string{"int64", "error"}}, // io.WriterTo +} + +func (f *File) checkCanonicalMethod(id *ast.Ident, t *ast.FuncType) { + // Expected input/output. + expect, ok := canonicalMethods[id.Name] + if !ok { + return + } + + // Actual input/output + args := typeFlatten(t.Params.List) + var results []ast.Expr + if t.Results != nil { + results = typeFlatten(t.Results.List) + } + + // Do the =s (if any) all match? + if !f.matchParams(expect.args, args, "=") || !f.matchParams(expect.results, results, "=") { + return + } + + // Everything must match. + if !f.matchParams(expect.args, args, "") || !f.matchParams(expect.results, results, "") { + expectFmt := id.Name + "(" + argjoin(expect.args) + ")" + if len(expect.results) == 1 { + expectFmt += " " + argjoin(expect.results) + } else if len(expect.results) > 1 { + expectFmt += " (" + argjoin(expect.results) + ")" + } + + f.b.Reset() + if err := printer.Fprint(&f.b, f.fset, t); err != nil { + fmt.Fprintf(&f.b, "<%s>", err) + } + actual := f.b.String() + if strings.HasPrefix(actual, "func(") { + actual = actual[4:] + } + actual = id.Name + actual + + f.Warnf(id.Pos(), "method %s should have signature %s", actual, expectFmt) + } +} + +func argjoin(x []string) string { + y := make([]string, len(x)) + for i, s := range x { + if s[0] == '=' { + s = s[1:] + } + y[i] = s + } + return strings.Join(y, ", ") +} + +// Turn parameter list into slice of types +// (in the ast, types are Exprs). +// Have to handle f(int, bool) and f(x, y, z int) +// so not a simple 1-to-1 conversion. +func typeFlatten(l []*ast.Field) []ast.Expr { + var t []ast.Expr + for _, f := range l { + if len(f.Names) == 0 { + t = append(t, f.Type) + continue + } + for _ = range f.Names { + t = append(t, f.Type) + } + } + return t +} + +// Does each type in expect with the given prefix match the corresponding type in actual? +func (f *File) matchParams(expect []string, actual []ast.Expr, prefix string) bool { + for i, x := range expect { + if !strings.HasPrefix(x, prefix) { + continue + } + if i >= len(actual) { + return false + } + if !f.matchParamType(x, actual[i]) { + return false + } + } + if prefix == "" && len(actual) > len(expect) { + return false + } + return true +} + +// Does this one type match? +func (f *File) matchParamType(expect string, actual ast.Expr) bool { + if strings.HasPrefix(expect, "=") { + expect = expect[1:] + } + // Strip package name if we're in that package. + if n := len(f.file.Name.Name); len(expect) > n && expect[:n] == f.file.Name.Name && expect[n] == '.' { + expect = expect[n+1:] + } + + // Overkill but easy. + f.b.Reset() + printer.Fprint(&f.b, f.fset, actual) + return f.b.String() == expect +} diff --git a/src/cmd/vet/print.go b/src/cmd/vet/print.go new file mode 100644 index 000000000..ee9a33c70 --- /dev/null +++ b/src/cmd/vet/print.go @@ -0,0 +1,278 @@ +// 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 the printf-checker. + +package main + +import ( + "flag" + "fmt" + "go/ast" + "go/token" + "strings" + "unicode/utf8" +) + +var printfuncs = flag.String("printfuncs", "", "comma-separated list of print function names to check") + +// printfList records the formatted-print functions. The value is the location +// of the format parameter. Names are lower-cased so the lookup is +// case insensitive. +var printfList = map[string]int{ + "errorf": 0, + "fatalf": 0, + "fprintf": 1, + "panicf": 0, + "printf": 0, + "sprintf": 0, +} + +// printList records the unformatted-print functions. The value is the location +// of the first parameter to be printed. Names are lower-cased so the lookup is +// case insensitive. +var printList = map[string]int{ + "error": 0, + "fatal": 0, + "fprint": 1, "fprintln": 1, + "panic": 0, "panicln": 0, + "print": 0, "println": 0, + "sprint": 0, "sprintln": 0, +} + +// checkCall triggers the print-specific checks if the call invokes a print function. +func (f *File) checkFmtPrintfCall(call *ast.CallExpr, Name string) { + name := strings.ToLower(Name) + if skip, ok := printfList[name]; ok { + f.checkPrintf(call, Name, skip) + return + } + if skip, ok := printList[name]; ok { + f.checkPrint(call, Name, skip) + return + } +} + +// 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 { + return + } + // Common case: literal is first argument. + arg := call.Args[skip] + lit, ok := arg.(*ast.BasicLit) + if !ok { + // Too hard to check. + 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 + } + } + // Hard part: check formats against args. + // Trivial but useful test: count. + numArgs := 0 + for i, w := 0, 0; i < len(lit.Value); i += w { + w = 1 + if lit.Value[i] == '%' { + nbytes, nargs := f.parsePrintfVerb(call, lit.Value[i:]) + w = nbytes + numArgs += 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) + } +} + +// 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) { + // There's guaranteed a percent sign. + flags := make([]byte, 0, 5) + nbytes = 1 + end := len(s) + // There may be flags. +FlagLoop: + for nbytes < end { + switch s[nbytes] { + case '#', '0', '+', '-', ' ': + flags = append(flags, s[nbytes]) + nbytes++ + default: + break FlagLoop + } + } + getNum := func() { + if nbytes < end && s[nbytes] == '*' { + nbytes++ + nargs++ + } else { + for nbytes < end && '0' <= s[nbytes] && s[nbytes] <= '9' { + nbytes++ + } + } + } + // There may be a width. + getNum() + // If there's a period, there may be a precision. + if nbytes < end && s[nbytes] == '.' { + flags = append(flags, '.') // Treat precision as a flag. + nbytes++ + getNum() + } + // Now a verb. + c, w := utf8.DecodeRuneInString(s[nbytes:]) + nbytes += w + if c != '%' { + nargs++ + f.checkPrintfVerb(call, c, flags) + } + return +} + +type printVerb struct { + verb rune + flags string // known flags are all ASCII +} + +// Common flag sets for printf verbs. +const ( + numFlag = " -+.0" + sharpNumFlag = " -+.0#" + allFlags = " -+.0#" +) + +// printVerbs identifies which flags are known to printf for each verb. +// TODO: A type that implements Formatter may do what it wants, and vet +// will complain incorrectly. +var printVerbs = []printVerb{ + // '-' is a width modifier, always valid. + // '.' is a precision for float, max width for strings. + // '+' 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}, +} + +const printfVerbs = "bcdeEfFgGopqstTvxUX" + +func (f *File) checkPrintfVerb(call *ast.CallExpr, verb rune, flags []byte) { + // 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 + } + } + f.Badf(call.Pos(), "unrecognized printf verb %q", verb) +} + +// 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) { + 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 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") { + f.Warnf(call.Pos(), "first argument to %s is %s.%s", name, x.Name, sel.Sel.Name) + } + } + } + } + if len(args) <= skip { + if *verbose && !isLn { + f.Badf(call.Pos(), "no args in %s call", name) + } + return + } + arg := args[skip] + 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) + } + } + if isLn { + // The last item, if a string, should not have a newline. + arg = args[len(call.Args)-1] + if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { + if strings.HasSuffix(lit.Value, `\n"`) { + f.Badf(call.Pos(), "%s call ends with newline", name) + } + } + } +} + +// 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[)]" +} + +// printf is used by the test. +func printf(format string, args ...interface{}) { + panic("don't call - testing only") +} diff --git a/src/cmd/vet/structtag.go b/src/cmd/vet/structtag.go new file mode 100644 index 000000000..ea2a9d863 --- /dev/null +++ b/src/cmd/vet/structtag.go @@ -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. + +// This file contains the test for canonical struct tags. + +package main + +import ( + "go/ast" + "reflect" + "strconv" +) + +// checkField checks a struct field tag. +func (f *File) checkCanonicalFieldTag(field *ast.Field) { + if field.Tag == nil { + return + } + + tag, err := strconv.Unquote(field.Tag.Value) + if err != nil { + f.Warnf(field.Pos(), "unable to read struct tag %s", field.Tag.Value) + return + } + + // Check tag for validity by appending + // new key:value to end and checking that + // the tag parsing code can find it. + if reflect.StructTag(tag+` _gofix:"_magic"`).Get("_gofix") != "_magic" { + f.Warnf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get", field.Tag.Value) + return + } +} diff --git a/src/cmd/vet/taglit.go b/src/cmd/vet/taglit.go new file mode 100644 index 000000000..c3c4f3234 --- /dev/null +++ b/src/cmd/vet/taglit.go @@ -0,0 +1,121 @@ +// 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 test for untagged struct literals. + +package main + +import ( + "go/ast" + "strings" +) + +// checkUntaggedLiteral checks if a composite literal is an struct literal with +// untagged fields. +func (f *File) checkUntaggedLiteral(c *ast.CompositeLit) { + // Check if the CompositeLit contains an untagged field. + allKeyValue := true + for _, e := range c.Elts { + if _, ok := e.(*ast.KeyValueExpr); !ok { + allKeyValue = false + break + } + } + if allKeyValue { + return + } + + // Check that the CompositeLit's type has the form pkg.Typ. + s, ok := c.Type.(*ast.SelectorExpr) + if !ok { + return + } + pkg, ok := s.X.(*ast.Ident) + if !ok { + return + } + + // Convert the package name to an import path, and compare to a whitelist. + path := pkgPath(f, pkg.Name) + if path == "" { + f.Warnf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name) + return + } + typ := path + "." + s.Sel.Name + if untaggedLiteralWhitelist[typ] { + return + } + + f.Warnf(c.Pos(), "%s struct literal uses untagged fields", typ) +} + +// pkgPath returns the import path "image/png" for the package name "png". +// +// This is based purely on syntax and convention, and not on the imported +// package's contents. It will be incorrect if a package name differs from the +// leaf element of the import path, or if the package was a dot import. +func pkgPath(f *File, pkgName string) (path string) { + for _, x := range f.file.Imports { + s := strings.Trim(x.Path.Value, `"`) + if x.Name != nil { + // Catch `import pkgName "foo/bar"`. + if x.Name.Name == pkgName { + return s + } + } else { + // Catch `import "pkgName"` or `import "foo/bar/pkgName"`. + if s == pkgName || strings.HasSuffix(s, "/"+pkgName) { + return s + } + } + } + return "" +} + +var untaggedLiteralWhitelist = map[string]bool{ + /* + These types are actually slices. Syntactically, we cannot tell + whether the Typ in pkg.Typ{1, 2, 3} is a slice or a struct, so we + whitelist all the standard package library's exported slice types. + + find $GOROOT/src/pkg -type f | grep -v _test.go | xargs grep '^type.*\[\]' | \ + grep -v ' map\[' | sed 's,/[^/]*go.type,,' | sed 's,.*src/pkg/,,' | \ + sed 's, ,.,' | sed 's, .*,,' | grep -v '\.[a-z]' | \ + sort | awk '{ print "\"" $0 "\": true," }' + */ + "crypto/x509/pkix.RDNSequence": true, + "crypto/x509/pkix.RelativeDistinguishedNameSET": true, + "database/sql.RawBytes": true, + "debug/macho.LoadBytes": true, + "encoding/asn1.ObjectIdentifier": true, + "encoding/asn1.RawContent": true, + "encoding/json.RawMessage": true, + "encoding/xml.CharData": true, + "encoding/xml.Comment": true, + "encoding/xml.Directive": true, + "exp/norm.Decomposition": true, + "exp/types.ObjList": true, + "go/scanner.ErrorList": true, + "image/color.Palette": true, + "net.HardwareAddr": true, + "net.IP": true, + "net.IPMask": true, + "sort.Float64Slice": true, + "sort.IntSlice": true, + "sort.StringSlice": true, + "unicode.SpecialCase": true, + + // These image and image/color struct types are frozen. We will never add fields to them. + "image/color.Alpha16": true, + "image/color.Alpha": true, + "image/color.Gray16": true, + "image/color.Gray": true, + "image/color.NRGBA64": true, + "image/color.NRGBA": true, + "image/color.RGBA64": true, + "image/color.RGBA": true, + "image/color.YCbCr": true, + "image.Point": true, + "image.Rectangle": true, +} diff --git a/src/cmd/yacc/Makefile b/src/cmd/yacc/Makefile new file mode 100644 index 000000000..56e954289 --- /dev/null +++ b/src/cmd/yacc/Makefile @@ -0,0 +1,10 @@ +# 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. + +units: yacc.go units.y + go run yacc.go -p units_ units.y + go build -o units y.go + +clean: + rm -f y.go y.output units diff --git a/src/cmd/yacc/doc.go b/src/cmd/yacc/doc.go new file mode 100644 index 000000000..4a2c2a314 --- /dev/null +++ b/src/cmd/yacc/doc.go @@ -0,0 +1,51 @@ +// 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. + +/* + +Yacc is a version of yacc for Go. +It is written in Go and generates parsers written in Go. + +Usage: + + go tool yacc args... + +It is largely transliterated from the Inferno version written in Limbo +which in turn was largely transliterated from the Plan 9 version +written in C and documented at + + http://plan9.bell-labs.com/magic/man2html/1/yacc + +Adepts of the original yacc will have no trouble adapting to this +form of the tool. + +The file units.y in this directory is a yacc grammar for a version of +the Unix tool units, also written in Go and largely transliterated +from the Plan 9 C version. It needs the flag "-p units_" (see +below). + +The generated parser is reentrant. Parse expects to be given an +argument that conforms to the following interface: + + type yyLexer interface { + Lex(lval *yySymType) int + Error(e string) + } + +Lex should return the token identifier, and place other token +information in lval (which replaces the usual yylval). +Error is equivalent to yyerror in the original yacc. + +Code inside the parser may refer to the variable yylex, +which holds the yyLexer passed to Parse. + +Multiple grammars compiled into a single program should be placed in +distinct packages. If that is impossible, the "-p prefix" flag to +yacc sets the prefix, by default yy, that begins the names of +symbols, including types, the parser, and the lexer, generated and +referenced by yacc's generated code. Setting it to distinct values +allows multiple grammars to be placed in a single package. + +*/ +package documentation diff --git a/src/cmd/yacc/units.txt b/src/cmd/yacc/units.txt new file mode 100644 index 000000000..ddb2bc294 --- /dev/null +++ b/src/cmd/yacc/units.txt @@ -0,0 +1,576 @@ +/ Plan 9's /lib/units +/ http://plan9.bell-labs.com/sources/plan9/lib/units +/ +/ Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved. +/ Distributed under the terms of the Lucent Public License Version 1.02 +/ See http://plan9.bell-labs.com/plan9/license.html +/ +/order of evaluation +/ + - +/ * / +/ juxtaposition (meaning *) +/ ¹ ² ³ ^ +/ | (meaning /) +/ name number () + +/dimensions +m # +kg # +sec # +coul # +candela # +$ # +radian # +bit # +erlang # +°K # +°C # +°F # + +/constants + +π 3.14159265358979323846 +pi π +c 2.997925e+8 m/sec +g 9.80665 m/sec² +au 1.49597871e+11 m +mole 6.022169e+23 +e 1.6021917e-19 coul +energy c² +force g +mercury 1.33322e+5 kg/m²sec² +hg mercury +h 6.62620e-34 m²kg/sec +ℏ h/2 π +hbar ℏ +nonillion 1e30 +octillion 1e27 +septillion 1e24 +sextillion 1e21 +pentillion 1e18 +quadrillion 1e15 +trillion 1e12 +billion 1e9 +million 1e6 +thousand 1e3 +hundred 1e2 + +/dimensionless + +° 1|180 π radian +degree ° +circle 2 π radian +turn 2 π radian +grad .9 ° +arcdeg 1 ° +arcmin 1|60 ° +arcsec 1|3600 ° +ccs 1|36 erlang + +steradian radian² +sphere 4 π steradian +sr steradian +giga 1024 1024 1024 + +/Time + +second sec +s sec +minute 60 sec +min minute +hour 60 min +hr hour +day 24 hr +da day +week 7 day +year 365.24219879 day +yr year +month 1|12 year +ms millisec +us microsec + +/Mass + +gram millikg +gm gram +mg milligram +metricton kilokg + +/Avoirdupois + +lb .45359237 kg +lbf lb g +pound lb +ounce 1|16 lb +oz ounce +dram 1|16 oz +dr dram +grain 1|7000 lb +gr grain +shortton 2000 lb +ton shortton +longton 2240 lb + +/Apothecary + +scruple 20 grain +apdram 60 grain +apounce 480 grain +troyounce apounce +appound 5760 grain +troypound appound + +/Length + +meter m +cm centimeter +mm millimeter +km kilometer +nm nanometer +micron micrometer +µ micrometer +Å decinanometer +angstrom Å + +inch 2.54 cm +" inch +in inch +inches inch +' 12" +foot 12 in +feet foot +ft foot +yard 3 ft +yd yard +rod 5.5 yd +rd rod +mile 5280 ft +mi mile + +british 1200|3937 m/ft +nmile 1852 m + +acre 4840 yd² + +cc cm³ +liter kilocc +ml milliliter + +/US Liquid + +gallon 231 in³ +imperial 1.20095 +epa 0.8 +gal gallon +quart 1|4 gal +qt quart +pint 1|2 qt +pt pint + +floz 1|16 pt +fldr 1|8 floz + +/US Dry + +dry 268.8025 in³/gallon +peck 8 dry quart +pk peck +bushel 4 peck +bu bushel + +/British + +brgallon 277.420 in³ +brquart 1|4 brgallon +brpint 1|2 brquart +brfloz 1|20 brpint +brpeck 554.84 in³ +brbushel 4 brpeck + +/Energy Work + +newton kg m/sec² +nt newton +joule nt m +cal 4.1868 joule + +/Electrical + +coulomb coul +ampere coul/sec +amp ampere +watt joule/sec +volt watt/amp +Ω volt/amp +ohm Ω +mho 1/Ω +farad coul/volt +henry sec²/farad +weber volt sec + +/Light + +cd candela +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 $ + +/ END MONEY + +€ europeuro +£ britainpound +¥ japanyen +dollar $ + +baht thailandbaht +brpound britainpound +dirham uaedirham +euro europeuro +forint hungaryforint +krona swedenkrona +peso mexicopeso +rand southafricarand +real brazilreal +yuan chinayuan +ringgit malaysiaringgit +riyal saudiarabiariyal +ruble russiaruble +rupee indiarupee +rupiah indonesiarupiah +shekel israelshekel +sol perunewsol +won southkoreawon +yen japanyen +zloty polandzloty + +usdollar dollar +sterling britainpound | pound +poundsterling britainpound + +/bits + +baud bit/sec +byte 8 bit +short 2 byte +long 4 byte +vlong 8 bytes +frame 2352 byte + +/Australian liquid measure + +pony 7 brfloz +midie 10 brfloz +pot midie +handle midie +schooner 15 brfloz +jug 40 brfloz +resch midie +alf midie +tinny 13 brfloz +stubby tinny +twisty 250 ml +longneck 2 tinny +slab 24 tinny +sixpack 6 tinny +nip brfloz + +/wine +winebottle 750 ml +balthazar 16 winebottle +jeroboam 4 winebottle +magnum 2 winebottle +mathusalem 8 winebottle +methuselah 8 winebottle +nebuchadnezzar 20 winebottle +rehoboam 6 winebottle +salmanazar 12 winebottle +split 0.25 winebottle +jigger 1.5 floz + +/Trivia + +% 1|100 +admiraltyknot 6080 ft/hr +ε₀ (1e-9/36π) farad/m +α (1/4π ε₀) e²/ℏ c +alpha α +apostilb cd/π m² +are 1e+2 m² +arpentcan 27.52 mi +arpentlin 191.835 ft +astronomicalunit au +atmosphere 1.01325e+5 nt/m² +atm atmosphere +atomicmassunit 1.66044e-27 kg +amu atomicmassunit +bag 94 lb +bakersdozen 13 +bar 1e+5 nt/m² +barie 1e-1 nt/m² +barleycorn 1|3 in +barn 1e-28 m² +barrel 42 gal +barye 1e-1 nt/m² +bev 1e+9 e volt +biot 10 amp +blondel cd/π m² +boardfoot 144 in³ +bolt 40 yd +bottommeasure 1|40 in +britishthermalunit 1.05506e+3 joule +btu britishthermalunit +quad 1.0e+15 btu +refrigeration 12000 btu/ton hour +buck dollar +cable 720 ft +caliber 1e-2 in +calorie cal +carat 205 mg +cent centidollar +cental 100 lb +centesimalminute 1e-2 grad +centesimalsecond 1e-4 grad +century 100 year +cfs ft³/sec +chain 66 ft +circularinch 1|4 π in² +circularmil 1e-6|4 π in² +clusec 1e-8 mm hg m³/s +coomb 4 bu +cord 128 ft³ +cordfoot cord +crith 9.06e-2 gm +cubit 18 in +cup 1|2 pt +curie 3.7e+10/sec +cusec ft³/sec +dalton amu +decade 10 yr +degK °K +degC °C +degF °F +dipotre 1/m +displacementton 35 ft³ +doppelzentner 100 kg +dozen 12 +drop .03 cm³ +dyne cm gm/sec² +electronvolt e volt +ell 45 in +engineerschain 100 ft +engineerslink 100|100 ft +equivalentfootcandle lumen/π ft² +equivalentlux lumen/π m² +equivalentphot cd/π cm² +erg cm²gm/sec² +ev e volt +faraday 9.652e+4 coul +fathom 6 ft +fermi 1e-15 m +fifth 4|5 qt +fin 5 dollar +finger 7|8 in +firkin 9 gal +footcandle lumen/ft² +footlambert cd/π ft² +fortnight 14 da +franklin 3.33564e-10 coul +frigorie kilocal +furlong 220 yd +galileo 1e-2 m/sec² +gamma 1e-9 weber/m² +gauss 1e-4 weber/m² +geodeticfoot british ft +geographicalmile 1852 m +gilbert 7.95775e-1 amp +gill 1|4 pt +gross 144 +gunterschain 22 yd +hand 4 in +hectare 1e+4 m² +hefnercandle .92 cd +hertz 1/sec +hogshead 2 barrel +hd hogshead +homestead 1|4 mi² +horsepower 550 ft lb g/sec +hp horsepower +hyl gm force sec²/m +hz 1/sec +imaginarycubicfoot 1.4 ft³ +karat 1|24 +kcal kilocal +kcalorie kilocal +kev 1e+3 e volt +key kg +khz 1e+3/sec +kilderkin 18 gal +knot nmile/hr +kwh kilowatt hour +lambert cd/π cm² +langley cal/cm² +last 80 bu +league 3 mi +lightyear c yr +ly lightyear +lightsecond c sec +line 1|12 in +link 66|100 ft +longhundredweight 112 lb +longquarter 28 lb +lusec 1e-6 mm hg m³/s +mach 331.46 m/sec +marineleague 3 nmile +maxwell 1e-8 weber +metriccarat 200 mg +mev 1e+6 e volt +mgd megagal/day +mh millihenry +mhz 1e+6/sec +mil 1e-3 in +millenium 1000 year +minersinch 1.5 ft³/min +minim 1|60 fldr +mo month +mpg mile/gal +mph mile/hr +nail 1|16 yd +nauticalmile nmile +nit cd/m² +noggin 1|8 qt +nox 1e-3 lux +ns nanosec +oersted 2.5e+2 amp/m π +oe oersted +pace 36 in +palm 3 in +parasang 3.5 mi +parsec au radian/arcsec +pascal nt/m² +pc parsec +pennyweight 1|20 oz +percent % +perch rd +pf picofarad +phot lumen/cm² +pica 1|6 in +pieze 1e+3 nt/m² +pipe 4 barrel +point 1|72 in +poise gm/cm sec +pole rd +poundal ft lb/sec² +pdl poundal +proof 1/200 +psi lb g/in² +quarter 9 in +quartersection 1|4 mi² +quintal 100 kg +quire 25 +rad 100 erg/gm +ream 500 +registerton 100 ft³ +rhe 10 m²/nt sec +rontgen 2.58e-4 curie/kg +rood 1.21e+3 yd +rope 20 ft +rutherford 1e+6/sec +rydberg 1.36054e+1 ev +sabin 1 ft² +sack 3 bu +seam 8 bu +section mi² +shippington 40 ft³ +shorthundredweight 100 lb +shortquarter 25 lb +siemens 1/Ω +σ 5.66956e-5 erg/cm² °K^4 sec +sigma σ +skein 120 yd +skot 1e-3 apostilb +slug lb g sec²/ft +span 9 in +spat 4 π sr +spindle 14400 yd +square 100 ft² +squidge 1|972 inch +catsquidge 1|432 inch +stere m³ +sthene 1e+3 nt +stilb cd/cm² +stoke 1e-4 m²/sec +stone 14 lb +strike 2 bu +surveyfoot british ft +surveyorschain 66 ft +surveyorslink 66|100 ft +tablespoon 4 fldr +teaspoon 4|3 fldr +tesla weber/m² +therm 1e+5 btu +thermie 1e+6 cal +timberfoot ft³ +tnt 4.6e+6 m²/sec² +tonne 1e+6 gm +torr mm hg +township 36 mi² +tun 8 barrel +water .22491|2.54 kg/m²sec² +wey 40 bu +weymass 252 lb +Xunit 1.00202e-13 m +k 1.38047e-16 erg/°K +foal 9223372036854775807 diff --git a/src/cmd/yacc/units.y b/src/cmd/yacc/units.y new file mode 100644 index 000000000..7258e3e59 --- /dev/null +++ b/src/cmd/yacc/units.y @@ -0,0 +1,763 @@ +// Derived from Plan 9's /sys/src/cmd/units.y +// http://plan9.bell-labs.com/sources/plan9/sys/src/cmd/units.y +// +// Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved. +// Portions Copyright 2009 The Go Authors. All Rights Reserved. +// Distributed under the terms of the Lucent Public License Version 1.02 +// See http://plan9.bell-labs.com/plan9/license.html + +// Generate parser with prefix "units_": +// go tool yacc -p "units_" + +%{ + +// units.y +// example of a Go yacc program +// usage is +// go tool yacc -p "units_" units.y (produces y.go) +// go build -o units y.go +// ./units $GOROOT/src/cmd/yacc/units.txt +// you have: c +// you want: furlongs/fortnight +// * 1.8026178e+12 +// / 5.5474878e-13 +// you have: + +package main + +import ( + "flag" + "fmt" + "bufio" + "os" + "math" + "strconv" + "unicode/utf8" +) + +const ( + Ndim = 15 // number of dimensions + Maxe = 695 // log of largest number +) + +type Node struct { + vval float64 + dim [Ndim]int8 +} + +type Var struct { + name string + node Node +} + +var fi *bufio.Reader // input +var fund [Ndim]*Var // names of fundamental units +var line string // current input line +var lineno int // current input line number +var linep int // index to next rune in unput +var nerrors int // error count +var one Node // constant one +var peekrune rune // backup runt from input +var retnode1 Node +var retnode2 Node +var retnode Node +var sym string +var vflag bool +%} + +%union { + node Node + vvar *Var + numb int + vval float64 +} + +%type prog expr expr0 expr1 expr2 expr3 expr4 + +%token VAL +%token VAR +%token SUP +%% +prog: + ':' VAR expr + { + var f int + f = int($2.node.dim[0]) + $2.node = $3 + $2.node.dim[0] = 1 + if f != 0 { + Errorf("redefinition of %v", $2.name) + } else if vflag { + fmt.Printf("%v\t%v\n", $2.name, &$2.node) + } + } +| ':' VAR '#' + { + var f, i int + for i = 1; i < Ndim; i++ { + if fund[i] == nil { + break + } + } + if i >= Ndim { + Error("too many dimensions") + i = Ndim - 1 + } + fund[i] = $2 + f = int($2.node.dim[0]) + $2.node = one + $2.node.dim[0] = 1 + $2.node.dim[i] = 1 + if f != 0 { + Errorf("redefinition of %v", $2.name) + } else if vflag { + fmt.Printf("%v\t#\n", $2.name) + } + } +| ':' + { + } +| '?' expr + { + retnode1 = $2 + } +| '?' + { + retnode1 = one + } + +expr: + expr4 +| expr '+' expr4 + { + add(&$$, &$1, &$3) + } +| expr '-' expr4 + { + sub(&$$, &$1, &$3) + } + +expr4: + expr3 +| expr4 '*' expr3 + { + mul(&$$, &$1, &$3) + } +| expr4 '/' expr3 + { + div(&$$, &$1, &$3) + } + +expr3: + expr2 +| expr3 expr2 + { + mul(&$$, &$1, &$2) + } + +expr2: + expr1 +| expr2 SUP + { + xpn(&$$, &$1, $2) + } +| expr2 '^' expr1 + { + var i int + for i = 1; i < Ndim; i++ { + if $3.dim[i] != 0 { + Error("exponent has units") + $$ = $1 + break + } + } + if i >= Ndim { + i = int($3.vval) + if float64(i) != $3.vval { + Error("exponent not integral") + } + xpn(&$$, &$1, i) + } + } + +expr1: + expr0 +| expr1 '|' expr0 + { + div(&$$, &$1, &$3) + } + +expr0: + VAR + { + if $1.node.dim[0] == 0 { + Errorf("undefined %v", $1.name) + $$ = one + } else { + $$ = $1.node + } + } +| VAL + { + $$ = one + $$.vval = $1 + } +| '(' expr ')' + { + $$ = $2 + } +%% + +type UnitsLex int + +func (UnitsLex) Lex(yylval *units_SymType) int { + var c rune + var i int + + c = peekrune + peekrune = ' ' + +loop: + if (c >= '0' && c <= '9') || c == '.' { + goto numb + } + if ralpha(c) { + goto alpha + } + switch c { + case ' ', '\t': + c = getrune() + goto loop + case '×': + return '*' + case '÷': + return '/' + case '¹', 'ⁱ': + yylval.numb = 1 + return SUP + case '²', '⁲': + yylval.numb = 2 + return SUP + case '³', '⁳': + yylval.numb = 3 + return SUP + } + return int(c) + +alpha: + sym = "" + for i = 0; ; i++ { + sym += string(c) + c = getrune() + if !ralpha(c) { + break + } + } + peekrune = c + yylval.vvar = lookup(0) + return VAR + +numb: + sym = "" + for i = 0; ; i++ { + sym += string(c) + c = getrune() + if !rdigit(c) { + break + } + } + peekrune = c + f, err := strconv.ParseFloat(sym, 64) + if err != nil { + fmt.Printf("error converting %v\n", sym) + f = 0 + } + yylval.vval = f + return VAL +} + +func (UnitsLex) Error(s string) { + Errorf("syntax error, last name: %v", sym) +} + +func main() { + var file string + + flag.BoolVar(&vflag, "v", false, "verbose") + + flag.Parse() + + if dir := os.Getenv("GOROOT"); dir != "" { + file = dir + "/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") + os.Exit(1) + } + + f, err := os.Open(file) + if err != nil { + fmt.Fprintf(os.Stderr, "error opening %v: %v\n", file, err) + os.Exit(1) + } + fi = bufio.NewReader(f) + + one.vval = 1 + + /* + * read the 'units' file to + * develope a database + */ + lineno = 0 + for { + lineno++ + if readline() { + break + } + if len(line) == 0 || line[0] == '/' { + continue + } + peekrune = ':' + units_Parse(UnitsLex(0)) + } + + /* + * read the console to + * print ratio of pairs + */ + fi = bufio.NewReader(os.NewFile(0, "stdin")) + + lineno = 0 + for { + if (lineno & 1) != 0 { + fmt.Printf("you want: ") + } else { + fmt.Printf("you have: ") + } + if readline() { + break + } + peekrune = '?' + nerrors = 0 + units_Parse(UnitsLex(0)) + if nerrors != 0 { + continue + } + if (lineno & 1) != 0 { + if specialcase(&retnode, &retnode2, &retnode1) { + fmt.Printf("\tis %v\n", &retnode) + } else { + div(&retnode, &retnode2, &retnode1) + fmt.Printf("\t* %v\n", &retnode) + div(&retnode, &retnode1, &retnode2) + fmt.Printf("\t/ %v\n", &retnode) + } + } else { + retnode2 = retnode1 + } + lineno++ + } + fmt.Printf("\n") + os.Exit(0) +} + +/* + * all characters that have some + * meaning. rest are usable as names + */ +func ralpha(c rune) bool { + switch c { + case 0, '+', '-', '*', '/', '[', ']', '(', ')', + '^', ':', '?', ' ', '\t', '.', '|', '#', + '×', '÷', '¹', 'ⁱ', '²', '⁲', '³', '⁳': + return false + } + return true +} + +/* + * number forming character + */ +func rdigit(c rune) bool { + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '.', 'e', '+', '-': + return true + } + return false +} + +func Errorf(s string, v ...interface{}) { + fmt.Printf("%v: %v\n\t", lineno, line) + fmt.Printf(s, v...) + fmt.Printf("\n") + + nerrors++ + if nerrors > 5 { + fmt.Printf("too many errors\n") + os.Exit(1) + } +} + +func Error(s string) { + Errorf("%s", s) +} + +func add(c, a, b *Node) { + var i int + var d int8 + + for i = 0; i < Ndim; i++ { + d = a.dim[i] + c.dim[i] = d + if d != b.dim[i] { + Error("add must be like units") + } + } + c.vval = fadd(a.vval, b.vval) +} + +func sub(c, a, b *Node) { + var i int + var d int8 + + for i = 0; i < Ndim; i++ { + d = a.dim[i] + c.dim[i] = d + if d != b.dim[i] { + Error("sub must be like units") + } + } + c.vval = fadd(a.vval, -b.vval) +} + +func mul(c, a, b *Node) { + var i int + + for i = 0; i < Ndim; i++ { + c.dim[i] = a.dim[i] + b.dim[i] + } + c.vval = fmul(a.vval, b.vval) +} + +func div(c, a, b *Node) { + var i int + + for i = 0; i < Ndim; i++ { + c.dim[i] = a.dim[i] - b.dim[i] + } + c.vval = fdiv(a.vval, b.vval) +} + +func xpn(c, a *Node, b int) { + var i int + + *c = one + if b < 0 { + b = -b + for i = 0; i < b; i++ { + div(c, c, a) + } + } else { + for i = 0; i < b; i++ { + mul(c, c, a) + } + } +} + +func specialcase(c, a, b *Node) bool { + var i int + var d, d1, d2 int8 + + d1 = 0 + d2 = 0 + for i = 1; i < Ndim; i++ { + d = a.dim[i] + if d != 0 { + if d != 1 || d1 != 0 { + return false + } + d1 = int8(i) + } + d = b.dim[i] + if d != 0 { + if d != 1 || d2 != 0 { + return false + } + d2 = int8(i) + } + } + if d1 == 0 || d2 == 0 { + return false + } + + if fund[d1].name == "°C" && fund[d2].name == "°F" && + b.vval == 1 { + for ll := 0; ll < len(c.dim); ll++ { + c.dim[ll] = b.dim[ll] + } + c.vval = a.vval*9./5. + 32. + return true + } + + if fund[d1].name == "°F" && fund[d2].name == "°C" && + b.vval == 1 { + for ll := 0; ll < len(c.dim); ll++ { + c.dim[ll] = b.dim[ll] + } + c.vval = (a.vval - 32.) * 5. / 9. + return true + } + return false +} + +func printdim(str string, d, n int) string { + var v *Var + + if n != 0 { + v = fund[d] + if v != nil { + str += fmt.Sprintf("%v", v.name) + } else { + str += fmt.Sprintf("[%d]", d) + } + switch n { + case 1: + break + case 2: + str += "²" + case 3: + str += "³" + default: + str += fmt.Sprintf("^%d", n) + } + } + return str +} + +func (n Node) String() string { + var str string + var f, i, d int + + str = fmt.Sprintf("%.7e ", n.vval) + + f = 0 + for i = 1; i < Ndim; i++ { + d = int(n.dim[i]) + if d > 0 { + str = printdim(str, i, d) + } else if d < 0 { + f = 1 + } + } + + if f != 0 { + str += " /" + for i = 1; i < Ndim; i++ { + d = int(n.dim[i]) + if d < 0 { + str = printdim(str, i, -d) + } + } + } + + return str +} + +func (v *Var) String() string { + var str string + str = fmt.Sprintf("%v %v", v.name, v.node) + return str +} + +func readline() bool { + s, err := fi.ReadString('\n') + if err != nil { + return true + } + line = s + linep = 0 + return false +} + +func getrune() rune { + var c rune + var n int + + if linep >= len(line) { + return 0 + } + c, n = utf8.DecodeRuneInString(line[linep:len(line)]) + linep += n + if c == '\n' { + c = 0 + } + return c +} + +var symmap = make(map[string]*Var) // symbol table + +func lookup(f int) *Var { + var p float64 + var w *Var + + v, ok := symmap[sym] + if ok { + return v + } + if f != 0 { + return nil + } + v = new(Var) + v.name = sym + symmap[sym] = v + + p = 1 + for { + p = fmul(p, pname()) + if p == 0 { + break + } + w = lookup(1) + if w != nil { + v.node = w.node + v.node.vval = fmul(v.node.vval, p) + break + } + } + return v +} + +type Prefix struct { + vval float64 + name string +} + +var prefix = []Prefix{ // prefix table + {1e-24, "yocto"}, + {1e-21, "zepto"}, + {1e-18, "atto"}, + {1e-15, "femto"}, + {1e-12, "pico"}, + {1e-9, "nano"}, + {1e-6, "micro"}, + {1e-6, "μ"}, + {1e-3, "milli"}, + {1e-2, "centi"}, + {1e-1, "deci"}, + {1e1, "deka"}, + {1e2, "hecta"}, + {1e2, "hecto"}, + {1e3, "kilo"}, + {1e6, "mega"}, + {1e6, "meg"}, + {1e9, "giga"}, + {1e12, "tera"}, + {1e15, "peta"}, + {1e18, "exa"}, + {1e21, "zetta"}, + {1e24, "yotta"}, +} + +func pname() float64 { + var i, j, n int + var s string + + /* + * rip off normal prefixs + */ + n = len(sym) + for i = 0; i < len(prefix); i++ { + s = prefix[i].name + j = len(s) + if j < n && sym[0:j] == s { + sym = sym[j:n] + return prefix[i].vval + } + } + + /* + * rip off 's' suffixes + */ + if n > 2 && sym[n-1] == 's' { + sym = sym[0 : n-1] + return 1 + } + + return 0 +} + +// careful multiplication +// exponents (log) are checked before multiply +func fmul(a, b float64) float64 { + var l float64 + + if b <= 0 { + if b == 0 { + return 0 + } + l = math.Log(-b) + } else { + l = math.Log(b) + } + + if a <= 0 { + if a == 0 { + return 0 + } + l += math.Log(-a) + } else { + l += math.Log(a) + } + + if l > Maxe { + Error("overflow in multiply") + return 1 + } + if l < -Maxe { + Error("underflow in multiply") + return 0 + } + return a * b +} + +// careful division +// exponents (log) are checked before divide +func fdiv(a, b float64) float64 { + var l float64 + + if b <= 0 { + if b == 0 { + Errorf("division by zero: %v %v", a, b) + return 1 + } + l = math.Log(-b) + } else { + l = math.Log(b) + } + + if a <= 0 { + if a == 0 { + return 0 + } + l -= math.Log(-a) + } else { + l -= math.Log(a) + } + + if l < -Maxe { + Error("overflow in divide") + return 1 + } + if l > Maxe { + Error("underflow in divide") + return 0 + } + return a / b +} + +func fadd(a, b float64) float64 { + return a + b +} diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go new file mode 100644 index 000000000..e94228152 --- /dev/null +++ b/src/cmd/yacc/yacc.go @@ -0,0 +1,3324 @@ +/* +Derived from Inferno's utils/iyacc/yacc.c +http://code.google.com/p/inferno-os/source/browse/utils/iyacc/yacc.c + +This copyright NOTICE applies to all files in this directory and +subdirectories, unless another copyright notice appears in a given +file or subdirectory. If you take substantial code from this software to use in +other programs, you must somehow include with it an appropriate +copyright notice that includes the copyright notice and the other +notices below. It is fine (and often tidier) to do that in a separate +file such as NOTICE, LICENCE or COPYING. + + Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. + Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) + Portions Copyright © 1997-1999 Vita Nuova Limited + Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) + Portions Copyright © 2004,2006 Bruce Ellis + Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) + Revisions Copyright © 2000-2007 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 +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. +*/ + +package main + +// yacc +// major difference is lack of stem ("y" variable) +// + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "os" + "strings" +) + +// the following are adjustable +// according to memory size +const ( + ACTSIZE = 30000 + NSTATES = 2000 + TEMPSIZE = 2000 + + SYMINC = 50 // increase for non-term or term + RULEINC = 50 // increase for max rule length prodptr[i] + PRODINC = 100 // increase for productions prodptr + WSETINC = 50 // increase for working sets wsets + STATEINC = 200 // increase for states statemem + + NAMESIZE = 50 + NTYPES = 63 + ISIZE = 400 + + PRIVATE = 0xE000 // unicode private use + + // relationships which must hold: + // TEMPSIZE >= NTERMS + NNONTERM + 1; + // TEMPSIZE >= NSTATES; + // + + NTBASE = 010000 + ERRCODE = 8190 + ACCEPTCODE = 8191 + YYLEXUNK = 3 + TOKSTART = 4 //index of first defined token +) + +// no, left, right, binary assoc. +const ( + NOASC = iota + LASC + RASC + BASC +) + +// flags for state generation +const ( + DONE = iota + MUSTDO + MUSTLOOKAHEAD +) + +// flags for a rule having an action, and being reduced +const ( + ACTFLAG = 1 << (iota + 2) + REDFLAG +) + +// output parser flags +const yyFlag = -1000 + +// parse tokens +const ( + IDENTIFIER = PRIVATE + iota + MARK + TERM + LEFT + RIGHT + BINARY + PREC + LCURLY + IDENTCOLON + NUMBER + START + TYPEDEF + TYPENAME + UNION +) + +const ENDFILE = 0 +const EMPTY = 1 +const WHOKNOWS = 0 +const OK = 1 +const NOMORE = -1000 + +// macros for getting associativity and precedence levels +func ASSOC(i int) int { return i & 3 } + +func PLEVEL(i int) int { return (i >> 4) & 077 } + +func TYPE(i int) int { return (i >> 10) & 077 } + +// macros for setting associativity and precedence levels +func SETASC(i, j int) int { return i | j } + +func SETPLEV(i, j int) int { return i | (j << 4) } + +func SETTYPE(i, j int) int { return i | (j << 10) } + +// I/O descriptors +var finput *bufio.Reader // input file +var stderr *bufio.Writer +var ftable *bufio.Writer // y.go file +var fcode = &bytes.Buffer{} // saved code +var foutput *bufio.Writer // y.output file + +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 +var prefix string // name prefix for identifiers, default yy + +func init() { + flag.StringVar(&oflag, "o", "y.go", "parser output") + flag.StringVar(&prefix, "p", "yy", "name prefix to use in generated code") + flag.StringVar(&vflag, "v", "y.output", "create parsing tables") + flag.BoolVar(&lflag, "l", false, "disable line directives") +} + +var stacksize = 200 + +// communication variables between various I/O routines +var infile string // input file name +var numbval int // value of an input number +var tokname string // input token name, slop for runes and 0 +var tokflag = false + +// structure declarations +type Lkset []int + +type Pitem struct { + prod []int + off int // offset within the production + first int // first term or non-term in item + prodno int // production number for sorting +} + +type Item struct { + pitem Pitem + look Lkset +} + +type Symb struct { + name string + value int +} + +type Wset struct { + pitem Pitem + flag int + ws Lkset +} + +// storage of types +var ntypes int // number of types defined +var typeset [NTYPES]string // pointers to type tags + +// token information + +var ntokens = 0 // number of tokens +var tokset []Symb +var toklev []int // vector with the precedence of the terminals + +// nonterminal information + +var nnonter = -1 // the number of nonterminals +var nontrst []Symb +var start int // start symbol + +// state information + +var nstate = 0 // number of states +var pstate = make([]int, NSTATES+2) // index into statemem to the descriptions of the states +var statemem []Item +var tystate = make([]int, NSTATES) // contains type information about the states +var tstates []int // states generated by terminal gotos +var ntstates []int // states generated by nonterminal gotos +var mstates = make([]int, NSTATES) // chain of overflows of term/nonterm generation lists +var lastred int // number of last reduction of a state +var defact = make([]int, NSTATES) // default actions of states + +// lookahead set information + +var lkst []Lkset +var nolook = 0 // flag to turn off lookahead computations +var tbitset = 0 // size of lookahead sets +var clset Lkset // temporary storage for lookahead computations + +// working set information + +var wsets []Wset +var cwp int + +// storage for action table + +var amem []int // action table storage +var memp int // next free action table position +var indgo = make([]int, NSTATES) // index to the stored goto table + +// temporary vector, indexable by states, terms, or ntokens + +var temp1 = make([]int, TEMPSIZE) // temporary storage, indexed by terms + ntokens or states +var lineno = 1 // current input line number +var fatfl = 1 // if on, error is fatal +var nerrors = 0 // number of errors + +// assigned token type values + +var extval = 0 + +// grammar rule information + +var nprod = 1 // number of productions +var prdptr [][]int // pointers to descriptions of productions +var levprd []int // precedence levels for the productions +var rlines []int // line number for this rule + +// statistics collection variables + +var zzgoent = 0 +var zzgobest = 0 +var zzacent = 0 +var zzexcp = 0 +var zzclose = 0 +var zzrrconf = 0 +var zzsrconf = 0 +var zzstate = 0 + +// optimizer arrays + +var yypgo [][]int +var optst [][]int +var ggreed []int +var pgo []int + +var maxspr int // maximum spread of any entry +var maxoff int // maximum offset into a array +var maxa int + +// storage for information about the nonterminals + +var pres [][][]int // vector of pointers to productions yielding each nonterminal +var pfirst []Lkset +var pempty []int // vector of nonterminals nontrivially deriving e + +// random stuff picked out from between functions + +var indebug = 0 // debugging flag for cpfir +var pidebug = 0 // debugging flag for putitem +var gsdebug = 0 // debugging flag for stagen +var cldebug = 0 // debugging flag for closure +var pkdebug = 0 // debugging flag for apack +var g2debug = 0 // debugging for go2gen +var adb = 0 // debugging for callopt + +type Resrv struct { + name string + value int +} + +var resrv = []Resrv{ + {"binary", BINARY}, + {"left", LEFT}, + {"nonassoc", BINARY}, + {"prec", PREC}, + {"right", RIGHT}, + {"start", START}, + {"term", TERM}, + {"token", TERM}, + {"type", TYPEDEF}, + {"union", UNION}, + {"struct", UNION}, +} + +var zznewstate = 0 + +const EOF = -1 +const UTFmax = 0x3f + +func main() { + + setup() // initialize and read productions + + tbitset = (ntokens + 32) / 32 + cpres() // make table of which productions yield a given nonterminal + cempty() // make a table of which nonterminals can match the empty string + cpfir() // make a table of firsts of nonterminals + + stagen() // generate the states + + yypgo = make([][]int, nnonter+1) + optst = make([][]int, nstate) + output() // write the states and the tables + go2out() + + hideprod() + summary() + + callopt() + + others() + + exit(0) +} + +func setup() { + var j, ty int + + stderr = bufio.NewWriter(os.NewFile(2, "stderr")) + foutput = nil + + flag.Parse() + if flag.NArg() != 1 { + usage() + } + if stacksize < 1 { + // never set so cannot happen + fmt.Fprintf(stderr, "yacc: stack size too small\n") + usage() + } + yaccpar = strings.Replace(yaccpartext, "$$", prefix, -1) + openup() + + defin(0, "$end") + extval = PRIVATE // tokens start in unicode 'private use' + defin(0, "error") + defin(1, "$accept") + defin(0, "$unk") + i := 0 + + t := gettok() + +outer: + for { + switch t { + default: + errorf("syntax error tok=%v", t-PRIVATE) + + case MARK, ENDFILE: + break outer + + case ';': + + case START: + t = gettok() + if t != IDENTIFIER { + errorf("bad %%start construction") + } + start = chfind(1, tokname) + + case TYPEDEF: + t = gettok() + if t != TYPENAME { + errorf("bad syntax in %%type") + } + ty = numbval + for { + t = gettok() + switch t { + case IDENTIFIER: + t = chfind(1, tokname) + if t < NTBASE { + j = TYPE(toklev[t]) + if j != 0 && j != ty { + errorf("type redeclaration of token %s", + tokset[t].name) + } else { + toklev[t] = SETTYPE(toklev[t], ty) + } + } else { + j = nontrst[t-NTBASE].value + if j != 0 && j != ty { + errorf("type redeclaration of nonterminal %v", + nontrst[t-NTBASE].name) + } else { + nontrst[t-NTBASE].value = ty + } + } + continue + + case ',': + continue + } + break + } + continue + + case UNION: + cpyunion() + + case LEFT, BINARY, RIGHT, TERM: + // nonzero means new prec. and assoc. + lev := t - TERM + if lev != 0 { + i++ + } + ty = 0 + + // get identifiers so defined + t = gettok() + + // there is a type defined + if t == TYPENAME { + ty = numbval + t = gettok() + } + for { + switch t { + case ',': + t = gettok() + continue + + case ';': + break + + case IDENTIFIER: + j = chfind(0, tokname) + if j >= NTBASE { + errorf("%v defined earlier as nonterminal", tokname) + } + if lev != 0 { + if ASSOC(toklev[j]) != 0 { + errorf("redeclaration of precedence of %v", tokname) + } + toklev[j] = SETASC(toklev[j], lev) + toklev[j] = SETPLEV(toklev[j], i) + } + if ty != 0 { + if TYPE(toklev[j]) != 0 { + errorf("redeclaration of type of %v", tokname) + } + toklev[j] = SETTYPE(toklev[j], ty) + } + t = gettok() + if t == NUMBER { + tokset[j].value = numbval + t = gettok() + } + + continue + } + break + } + continue + + case LCURLY: + cpycode() + } + t = gettok() + } + + if t == ENDFILE { + errorf("unexpected EOF before %%") + } + + // put out non-literal terminals + for i := TOKSTART; i <= ntokens; i++ { + // non-literals + c := tokset[i].name[0] + if c != ' ' && c != '$' { + fmt.Fprintf(ftable, "const %v = %v\n", tokset[i].name, tokset[i].value) + } + } + + // put out names of token names + ftable.WriteRune('\n') + fmt.Fprintf(ftable, "var %sToknames = []string{\n", prefix) + for i := TOKSTART; i <= ntokens; i++ { + fmt.Fprintf(ftable, "\t\"%v\",\n", tokset[i].name) + } + fmt.Fprintf(ftable, "}\n") + + // put out names of state names + fmt.Fprintf(ftable, "var %sStatenames = []string{", prefix) + // for i:=TOKSTART; i<=ntokens; i++ { + // fmt.Fprintf(ftable, "\t\"%v\",\n", tokset[i].name); + // } + fmt.Fprintf(ftable, "}\n") + + fmt.Fprintf(fcode, "switch %snt {\n", prefix) + + moreprod() + prdptr[0] = []int{NTBASE, start, 1, 0} + + nprod = 1 + curprod := make([]int, RULEINC) + t = gettok() + if t != IDENTCOLON { + errorf("bad syntax on first rule") + } + + if start == 0 { + prdptr[0][1] = chfind(1, tokname) + } + + // read rules + // put into prdptr array in the format + // target + // followed by id's of terminals and non-terminals + // followed by -nprod + + for t != MARK && t != ENDFILE { + mem := 0 + + // process a rule + rlines[nprod] = lineno + if t == '|' { + curprod[mem] = prdptr[nprod-1][0] + mem++ + } else if t == IDENTCOLON { + curprod[mem] = chfind(1, tokname) + if curprod[mem] < NTBASE { + errorf("token illegal on LHS of grammar rule") + } + mem++ + } else { + errorf("illegal rule: missing semicolon or | ?") + } + + // read rule body + t = gettok() + for { + for t == IDENTIFIER { + curprod[mem] = chfind(1, tokname) + if curprod[mem] < NTBASE { + levprd[nprod] = toklev[curprod[mem]] + } + mem++ + if mem >= len(curprod) { + ncurprod := make([]int, mem+RULEINC) + copy(ncurprod, curprod) + curprod = ncurprod + } + t = gettok() + } + if t == PREC { + if gettok() != IDENTIFIER { + errorf("illegal %%prec syntax") + } + j = chfind(2, tokname) + if j >= NTBASE { + errorf("nonterminal " + nontrst[j-NTBASE].name + " illegal after %%prec") + } + levprd[nprod] = toklev[j] + t = gettok() + } + if t != '=' { + break + } + levprd[nprod] |= ACTFLAG + fmt.Fprintf(fcode, "\n\tcase %v:", nprod) + cpyact(curprod, mem) + + // action within rule... + t = gettok() + if t == IDENTIFIER { + // make it a nonterminal + j = chfind(1, fmt.Sprintf("$$%v", nprod)) + + // + // the current rule will become rule number nprod+1 + // enter null production for action + // + prdptr[nprod] = make([]int, 2) + prdptr[nprod][0] = j + prdptr[nprod][1] = -nprod + + // update the production information + nprod++ + moreprod() + levprd[nprod] = levprd[nprod-1] & ^ACTFLAG + levprd[nprod-1] = ACTFLAG + rlines[nprod] = lineno + + // make the action appear in the original rule + curprod[mem] = j + mem++ + if mem >= len(curprod) { + ncurprod := make([]int, mem+RULEINC) + copy(ncurprod, curprod) + curprod = ncurprod + } + } + } + + for t == ';' { + t = gettok() + } + curprod[mem] = -nprod + mem++ + + // check that default action is reasonable + if ntypes != 0 && (levprd[nprod]&ACTFLAG) == 0 && + nontrst[curprod[0]-NTBASE].value != 0 { + // no explicit action, LHS has value + tempty := curprod[1] + if tempty < 0 { + errorf("must return a value, since LHS has a type") + } + if tempty >= NTBASE { + tempty = nontrst[tempty-NTBASE].value + } else { + tempty = TYPE(toklev[tempty]) + } + if tempty != nontrst[curprod[0]-NTBASE].value { + errorf("default action causes potential type clash") + } + fmt.Fprintf(fcode, "\n\tcase %v:", nprod) + fmt.Fprintf(fcode, "\n\t\t%sVAL.%v = %sS[%spt-0].%v", + prefix, typeset[tempty], prefix, prefix, typeset[tempty]) + } + moreprod() + prdptr[nprod] = make([]int, mem) + copy(prdptr[nprod], curprod) + nprod++ + moreprod() + levprd[nprod] = 0 + } + + // + // end of all rules + // dump out the prefix code + // + + fmt.Fprintf(fcode, "\n\t}") + + ftable.WriteRune('\n') + fmt.Fprintf(ftable, "const %sEofCode = 1\n", prefix) + fmt.Fprintf(ftable, "const %sErrCode = 2\n", prefix) + fmt.Fprintf(ftable, "const %sMaxDepth = %v\n", prefix, stacksize) + + // + // copy any postfix code + // + if t == MARK { + if !lflag { + fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno) + } + for { + c := getrune(finput) + if c == EOF { + break + } + ftable.WriteRune(c) + } + } +} + +// +// allocate enough room to hold another production +// +func moreprod() { + n := len(prdptr) + if nprod >= n { + nn := n + PRODINC + aprod := make([][]int, nn) + alevprd := make([]int, nn) + arlines := make([]int, nn) + + copy(aprod, prdptr) + copy(alevprd, levprd) + copy(arlines, rlines) + + prdptr = aprod + levprd = alevprd + rlines = arlines + } +} + +// +// define s to be a terminal if t=0 +// or a nonterminal if t=1 +// +func defin(nt int, s string) int { + val := 0 + if nt != 0 { + nnonter++ + if nnonter >= len(nontrst) { + anontrst := make([]Symb, nnonter+SYMINC) + copy(anontrst, nontrst) + nontrst = anontrst + } + nontrst[nnonter] = Symb{s, 0} + return NTBASE + nnonter + } + + // must be a token + ntokens++ + if ntokens >= len(tokset) { + nn := ntokens + SYMINC + atokset := make([]Symb, nn) + atoklev := make([]int, nn) + + copy(atoklev, toklev) + copy(atokset, tokset) + + tokset = atokset + toklev = atoklev + } + tokset[ntokens].name = s + toklev[ntokens] = 0 + + // 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 + default: + errorf("illegal \\unnnn construction") + } + val = val*16 + c + s = s[1:] + } + if val == 0 { + errorf("'\\u0000' is illegal") + } + } else { + errorf("unknown escape") + } + } else { + val = extval + extval++ + } + + tokset[ntokens].value = val + return ntokens +} + +var peekline = 0 + +func gettok() int { + var i int + var match, c rune + + tokname = "" + for { + lineno += peekline + peekline = 0 + c = getrune(finput) + for c == ' ' || c == '\n' || c == '\t' || c == '\v' || c == '\r' { + if c == '\n' { + lineno++ + } + c = getrune(finput) + } + + // skip comment -- fix + if c != '/' { + break + } + lineno += skipcom() + } + + switch c { + case EOF: + if tokflag { + fmt.Printf(">>> ENDFILE %v\n", lineno) + } + return ENDFILE + + case '{': + ungetrune(finput, c) + if tokflag { + fmt.Printf(">>> ={ %v\n", lineno) + } + return '=' + + case '<': + // get, and look up, a type name (union member name) + c = getrune(finput) + for c != '>' && c != EOF && c != '\n' { + tokname += string(c) + c = getrune(finput) + } + + if c != '>' { + errorf("unterminated < ... > clause") + } + + for i = 1; i <= ntypes; i++ { + if typeset[i] == tokname { + numbval = i + if tokflag { + fmt.Printf(">>> TYPENAME old <%v> %v\n", tokname, lineno) + } + return TYPENAME + } + } + ntypes++ + numbval = ntypes + typeset[numbval] = tokname + if tokflag { + fmt.Printf(">>> TYPENAME new <%v> %v\n", tokname, lineno) + } + return TYPENAME + + case '"', '\'': + match = c + tokname = " " + for { + c = getrune(finput) + if c == '\n' || c == EOF { + errorf("illegal or missing ' or \"") + } + if c == '\\' { + tokname += string('\\') + c = getrune(finput) + } else if c == match { + if tokflag { + fmt.Printf(">>> IDENTIFIER \"%v\" %v\n", tokname, lineno) + } + return IDENTIFIER + } + tokname += string(c) + } + + case '%': + c = getrune(finput) + switch c { + case '%': + if tokflag { + fmt.Printf(">>> MARK %%%% %v\n", lineno) + } + return MARK + case '=': + if tokflag { + fmt.Printf(">>> PREC %%= %v\n", lineno) + } + return PREC + case '{': + if tokflag { + fmt.Printf(">>> LCURLY %%{ %v\n", lineno) + } + return LCURLY + } + + getword(c) + // find a reserved word + for i := range resrv { + if tokname == resrv[i].name { + if tokflag { + fmt.Printf(">>> %%%v %v %v\n", tokname, + resrv[i].value-PRIVATE, lineno) + } + return resrv[i].value + } + } + errorf("invalid escape, or illegal reserved word: %v", tokname) + + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + numbval = int(c - '0') + for { + c = getrune(finput) + if !isdigit(c) { + break + } + numbval = numbval*10 + int(c-'0') + } + ungetrune(finput, c) + if tokflag { + fmt.Printf(">>> NUMBER %v %v\n", numbval, lineno) + } + return NUMBER + + default: + if isword(c) || c == '.' || c == '$' { + getword(c) + break + } + if tokflag { + fmt.Printf(">>> OPERATOR %v %v\n", string(c), lineno) + } + return int(c) + } + + // look ahead to distinguish IDENTIFIER from IDENTCOLON + c = getrune(finput) + for c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\r' || c == '/' { + if c == '\n' { + peekline++ + } + // look for comments + if c == '/' { + peekline += skipcom() + } + c = getrune(finput) + } + if c == ':' { + if tokflag { + fmt.Printf(">>> IDENTCOLON %v: %v\n", tokname, lineno) + } + return IDENTCOLON + } + + ungetrune(finput, c) + if tokflag { + fmt.Printf(">>> IDENTIFIER %v %v\n", tokname, lineno) + } + return IDENTIFIER +} + +func getword(c rune) { + tokname = "" + for isword(c) || isdigit(c) || c == '_' || c == '.' || c == '$' { + tokname += string(c) + c = getrune(finput) + } + ungetrune(finput, c) +} + +// +// determine the type of a symbol +// +func fdtype(t int) int { + var v int + var s string + + if t >= NTBASE { + v = nontrst[t-NTBASE].value + s = nontrst[t-NTBASE].name + } else { + v = TYPE(toklev[t]) + s = tokset[t].name + } + if v <= 0 { + errorf("must specify type for %v", s) + } + return v +} + +func chfind(t int, s string) int { + if s[0] == ' ' { + t = 0 + } + for i := 0; i <= ntokens; i++ { + if s == tokset[i].name { + return i + } + } + for i := 0; i <= nnonter; i++ { + if s == nontrst[i].name { + return NTBASE + i + } + } + + // cannot find name + if t > 1 { + errorf("%v should have been defined earlier", s) + } + return defin(t, s) +} + +// +// copy the union declaration to the output, and the define file if present +// +func cpyunion() { + + if !lflag { + fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno) + } + fmt.Fprintf(ftable, "type %sSymType struct", prefix) + + level := 0 + +out: + for { + c := getrune(finput) + if c == EOF { + errorf("EOF encountered while processing %%union") + } + ftable.WriteRune(c) + switch c { + case '\n': + lineno++ + case '{': + if level == 0 { + fmt.Fprintf(ftable, "\n\tyys int") + } + level++ + case '}': + level-- + if level == 0 { + break out + } + } + } + fmt.Fprintf(ftable, "\n\n") +} + +// +// saves code between %{ and %} +// +func cpycode() { + lno := lineno + + c := getrune(finput) + if c == '\n' { + c = getrune(finput) + lineno++ + } + if !lflag { + fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno) + } + for c != EOF { + if c == '%' { + c = getrune(finput) + if c == '}' { + return + } + ftable.WriteRune('%') + } + ftable.WriteRune(c) + if c == '\n' { + lineno++ + } + c = getrune(finput) + } + lineno = lno + errorf("eof before %%}") +} + +// +// skip over comments +// skipcom is called after reading a '/' +// +func skipcom() int { + var c rune + + c = getrune(finput) + if c == '/' { + for c != EOF { + if c == '\n' { + return 1 + } + c = getrune(finput) + } + errorf("EOF inside comment") + return 0 + } + if c != '*' { + errorf("illegal comment") + } + + nl := 0 // lines skipped + c = getrune(finput) + +l1: + switch c { + case '*': + c = getrune(finput) + if c == '/' { + break + } + goto l1 + + case '\n': + nl++ + fallthrough + + default: + c = getrune(finput) + goto l1 + } + return nl +} + +func dumpprod(curprod []int, max int) { + fmt.Printf("\n") + for i := 0; i < max; i++ { + p := curprod[i] + if p < 0 { + fmt.Printf("[%v] %v\n", i, p) + } else { + fmt.Printf("[%v] %v\n", i, symnam(p)) + } + } +} + +// +// copy action to the next ; or closing } +// +func cpyact(curprod []int, max int) { + + if !lflag { + fmt.Fprintf(fcode, "\n\t\t//line %v:%v\n\t\t", infile, lineno) + } + + lno := lineno + brac := 0 + +loop: + for { + c := getrune(finput) + + swt: + switch c { + case ';': + if brac == 0 { + fcode.WriteRune(c) + return + } + + case '{': + if brac == 0 { + } + brac++ + + case '$': + s := 1 + tok := -1 + c = getrune(finput) + + // type description + if c == '<' { + ungetrune(finput, c) + if gettok() != TYPENAME { + errorf("bad syntax on $ clause") + } + tok = numbval + c = getrune(finput) + } + if c == '$' { + fmt.Fprintf(fcode, "%sVAL", prefix) + + // put out the proper tag... + if ntypes != 0 { + if tok < 0 { + tok = fdtype(curprod[0]) + } + fmt.Fprintf(fcode, ".%v", typeset[tok]) + } + continue loop + } + if c == '-' { + s = -s + c = getrune(finput) + } + j := 0 + if isdigit(c) { + for isdigit(c) { + j = j*10 + int(c-'0') + c = getrune(finput) + } + ungetrune(finput, c) + j = j * s + if j >= max { + errorf("Illegal use of $%v", j) + } + } else if isword(c) || c == '_' || c == '.' { + // look for $name + ungetrune(finput, c) + if gettok() != IDENTIFIER { + errorf("$ must be followed by an identifier") + } + tokn := chfind(2, tokname) + fnd := -1 + c = getrune(finput) + if c != '@' { + ungetrune(finput, c) + } else if gettok() != NUMBER { + errorf("@ must be followed by number") + } else { + fnd = numbval + } + for j = 1; j < max; j++ { + if tokn == curprod[j] { + fnd-- + if fnd <= 0 { + break + } + } + } + if j >= max { + errorf("$name or $name@number not found") + } + } else { + fcode.WriteRune('$') + if s < 0 { + fcode.WriteRune('-') + } + ungetrune(finput, c) + continue loop + } + fmt.Fprintf(fcode, "%sS[%spt-%v]", prefix, prefix, max-j-1) + + // put out the proper tag + if ntypes != 0 { + if j <= 0 && tok < 0 { + errorf("must specify type of $%v", j) + } + if tok < 0 { + tok = fdtype(curprod[j]) + } + fmt.Fprintf(fcode, ".%v", typeset[tok]) + } + continue loop + + case '}': + brac-- + if brac != 0 { + break + } + fcode.WriteRune(c) + return + + case '/': + nc := getrune(finput) + if nc != '/' && nc != '*' { + ungetrune(finput, nc) + break + } + // a comment + fcode.WriteRune(c) + fcode.WriteRune(nc) + c = getrune(finput) + for c != EOF { + switch { + case c == '\n': + lineno++ + if nc == '/' { // end of // comment + break swt + } + case c == '*' && nc == '*': // end of /* comment? + nnc := getrune(finput) + if nnc == '/' { + fcode.WriteRune('*') + fcode.WriteRune('/') + c = getrune(finput) + break swt + } + ungetrune(finput, nnc) + } + fcode.WriteRune(c) + c = getrune(finput) + } + errorf("EOF inside comment") + + case '\'', '"': + // character string or constant + match := c + fcode.WriteRune(c) + c = getrune(finput) + for c != EOF { + if c == '\\' { + fcode.WriteRune(c) + c = getrune(finput) + if c == '\n' { + lineno++ + } + } else if c == match { + break swt + } + if c == '\n' { + errorf("newline in string or char const") + } + fcode.WriteRune(c) + c = getrune(finput) + } + errorf("EOF in string or character constant") + + case EOF: + lineno = lno + errorf("action does not terminate") + + case '\n': + fmt.Fprint(fcode, "\n\t") + lineno++ + continue loop + } + + fcode.WriteRune(c) + } +} + +func openup() { + infile = flag.Arg(0) + finput = open(infile) + if finput == nil { + errorf("cannot open %v", infile) + } + + foutput = nil + if vflag != "" { + foutput = create(vflag) + if foutput == nil { + errorf("can't create file %v", vflag) + } + } + + ftable = nil + if oflag == "" { + oflag = "y.go" + } + ftable = create(oflag) + if ftable == nil { + errorf("can't create file %v", oflag) + } + +} + +// +// return a pointer to the name of symbol i +// +func symnam(i int) string { + var s string + + if i >= NTBASE { + s = nontrst[i-NTBASE].name + } else { + s = tokset[i].name + } + if s[0] == ' ' { + s = s[1:] + } + return s +} + +// +// set elements 0 through n-1 to c +// +func aryfil(v []int, n, c int) { + for i := 0; i < n; i++ { + v[i] = c + } +} + +// +// compute an array with the beginnings of productions yielding given nonterminals +// The array pres points to these lists +// the array pyield has the lists: the total size is only NPROD+1 +// +func cpres() { + pres = make([][][]int, nnonter+1) + curres := make([][]int, nprod) + + if false { + for j := 0; j <= nnonter; j++ { + fmt.Printf("nnonter[%v] = %v\n", j, nontrst[j].name) + } + for j := 0; j < nprod; j++ { + fmt.Printf("prdptr[%v][0] = %v+NTBASE\n", j, prdptr[j][0]-NTBASE) + } + } + + fatfl = 0 // make undefined symbols nonfatal + for i := 0; i <= nnonter; i++ { + n := 0 + c := i + NTBASE + for j := 0; j < nprod; j++ { + if prdptr[j][0] == c { + curres[n] = prdptr[j][1:] + n++ + } + } + if n == 0 { + errorf("nonterminal %v not defined", nontrst[i].name) + continue + } + pres[i] = make([][]int, n) + copy(pres[i], curres) + } + fatfl = 1 + if nerrors != 0 { + summary() + exit(1) + } +} + +func dumppres() { + for i := 0; i <= nnonter; i++ { + fmt.Printf("nonterm %d\n", i) + curres := pres[i] + for j := 0; j < len(curres); j++ { + fmt.Printf("\tproduction %d:", j) + prd := curres[j] + for k := 0; k < len(prd); k++ { + fmt.Printf(" %d", prd[k]) + } + fmt.Print("\n") + } + } +} + +// +// mark nonterminals which derive the empty string +// also, look for nonterminals which don't derive any token strings +// +func cempty() { + var i, p, np int + var prd []int + + pempty = make([]int, nnonter+1) + + // first, use the array pempty to detect productions that can never be reduced + // set pempty to WHONOWS + aryfil(pempty, nnonter+1, WHOKNOWS) + + // now, look at productions, marking nonterminals which derive something +more: + for { + for i = 0; i < nprod; i++ { + prd = prdptr[i] + if pempty[prd[0]-NTBASE] != 0 { + continue + } + np = len(prd) - 1 + for p = 1; p < np; p++ { + if prd[p] >= NTBASE && pempty[prd[p]-NTBASE] == WHOKNOWS { + break + } + } + // production can be derived + if p == np { + pempty[prd[0]-NTBASE] = OK + continue more + } + } + break + } + + // now, look at the nonterminals, to see if they are all OK + for i = 0; i <= nnonter; i++ { + // the added production rises or falls as the start symbol ... + if i == 0 { + continue + } + if pempty[i] != OK { + fatfl = 0 + errorf("nonterminal " + nontrst[i].name + " never derives any token string") + } + } + + if nerrors != 0 { + summary() + exit(1) + } + + // now, compute the pempty array, to see which nonterminals derive the empty string + // set pempty to WHOKNOWS + aryfil(pempty, nnonter+1, WHOKNOWS) + + // loop as long as we keep finding empty nonterminals + +again: + for { + next: + for i = 1; i < nprod; i++ { + // not known to be empty + prd = prdptr[i] + if pempty[prd[0]-NTBASE] != WHOKNOWS { + continue + } + np = len(prd) - 1 + for p = 1; p < np; p++ { + if prd[p] < NTBASE || pempty[prd[p]-NTBASE] != EMPTY { + continue next + } + } + + // we have a nontrivially empty nonterminal + pempty[prd[0]-NTBASE] = EMPTY + + // got one ... try for another + continue again + } + return + } +} + +func dumpempty() { + for i := 0; i <= nnonter; i++ { + if pempty[i] == EMPTY { + fmt.Printf("non-term %d %s matches empty\n", i, symnam(i+NTBASE)) + } + } +} + +// +// compute an array with the first of nonterminals +// +func cpfir() { + var s, n, p, np, ch, i int + var curres [][]int + var prd []int + + wsets = make([]Wset, nnonter+WSETINC) + pfirst = make([]Lkset, nnonter+1) + for i = 0; i <= nnonter; i++ { + wsets[i].ws = mkset() + pfirst[i] = mkset() + curres = pres[i] + n = len(curres) + + // initially fill the sets + for s = 0; s < n; s++ { + prd = curres[s] + np = len(prd) - 1 + for p = 0; p < np; p++ { + ch = prd[p] + if ch < NTBASE { + setbit(pfirst[i], ch) + break + } + if pempty[ch-NTBASE] == 0 { + break + } + } + } + } + + // now, reflect transitivity + changes := 1 + for changes != 0 { + changes = 0 + for i = 0; i <= nnonter; i++ { + curres = pres[i] + n = len(curres) + for s = 0; s < n; s++ { + prd = curres[s] + np = len(prd) - 1 + for p = 0; p < np; p++ { + ch = prd[p] - NTBASE + if ch < 0 { + break + } + changes |= setunion(pfirst[i], pfirst[ch]) + if pempty[ch] == 0 { + break + } + } + } + } + } + + if indebug == 0 { + return + } + if foutput != nil { + for i = 0; i <= nnonter; i++ { + fmt.Fprintf(foutput, "\n%v: %v %v\n", + nontrst[i].name, pfirst[i], pempty[i]) + } + } +} + +// +// generate the states +// +func stagen() { + // initialize + nstate = 0 + tstates = make([]int, ntokens+1) // states generated by terminal gotos + ntstates = make([]int, nnonter+1) // states generated by nonterminal gotos + amem = make([]int, ACTSIZE) + memp = 0 + + clset = mkset() + pstate[0] = 0 + pstate[1] = 0 + aryfil(clset, tbitset, 0) + putitem(Pitem{prdptr[0], 0, 0, 0}, clset) + tystate[0] = MUSTDO + nstate = 1 + pstate[2] = pstate[1] + + // + // now, the main state generation loop + // first pass generates all of the states + // later passes fix up lookahead + // could be sped up a lot by remembering + // results of the first pass rather than recomputing + // + first := 1 + for more := 1; more != 0; first = 0 { + more = 0 + for i := 0; i < nstate; i++ { + if tystate[i] != MUSTDO { + continue + } + + tystate[i] = DONE + aryfil(temp1, nnonter+1, 0) + + // take state i, close it, and do gotos + closure(i) + + // generate goto's + for p := 0; p < cwp; p++ { + pi := wsets[p] + if pi.flag != 0 { + continue + } + wsets[p].flag = 1 + c := pi.pitem.first + if c <= 1 { + if pstate[i+1]-pstate[i] <= p { + tystate[i] = MUSTLOOKAHEAD + } + continue + } + + // do a goto on c + putitem(wsets[p].pitem, wsets[p].ws) + for q := p + 1; q < cwp; q++ { + // this item contributes to the goto + if c == wsets[q].pitem.first { + putitem(wsets[q].pitem, wsets[q].ws) + wsets[q].flag = 1 + } + } + + if c < NTBASE { + state(c) // register new state + } else { + temp1[c-NTBASE] = state(c) + } + } + + if gsdebug != 0 && foutput != nil { + fmt.Fprintf(foutput, "%v: ", i) + for j := 0; j <= nnonter; j++ { + if temp1[j] != 0 { + fmt.Fprintf(foutput, "%v %v,", nontrst[j].name, temp1[j]) + } + } + fmt.Fprintf(foutput, "\n") + } + + if first != 0 { + indgo[i] = apack(temp1[1:], nnonter-1) - 1 + } + + more++ + } + } +} + +// +// generate the closure of state i +// +func closure(i int) { + zzclose++ + + // first, copy kernel of state i to wsets + cwp = 0 + q := pstate[i+1] + for p := pstate[i]; p < q; p++ { + wsets[cwp].pitem = statemem[p].pitem + wsets[cwp].flag = 1 // this item must get closed + copy(wsets[cwp].ws, statemem[p].look) + cwp++ + } + + // now, go through the loop, closing each item + work := 1 + for work != 0 { + work = 0 + for u := 0; u < cwp; u++ { + if wsets[u].flag == 0 { + continue + } + + // dot is before c + c := wsets[u].pitem.first + if c < NTBASE { + wsets[u].flag = 0 + // only interesting case is where . is before nonterminal + continue + } + + // compute the lookahead + aryfil(clset, tbitset, 0) + + // find items involving c + for v := u; v < cwp; v++ { + if wsets[v].flag != 1 || wsets[v].pitem.first != c { + continue + } + pi := wsets[v].pitem.prod + ipi := wsets[v].pitem.off + 1 + + wsets[v].flag = 0 + if nolook != 0 { + continue + } + + ch := pi[ipi] + ipi++ + for ch > 0 { + // terminal symbol + if ch < NTBASE { + setbit(clset, ch) + break + } + + // nonterminal symbol + setunion(clset, pfirst[ch-NTBASE]) + if pempty[ch-NTBASE] == 0 { + break + } + ch = pi[ipi] + ipi++ + } + if ch <= 0 { + setunion(clset, wsets[v].ws) + } + } + + // + // now loop over productions derived from c + // + curres := pres[c-NTBASE] + n := len(curres) + + nexts: + // initially fill the sets + for s := 0; s < n; s++ { + prd := curres[s] + + // + // put these items into the closure + // is the item there + // + for v := 0; v < cwp; v++ { + // yes, it is there + if wsets[v].pitem.off == 0 && + aryeq(wsets[v].pitem.prod, prd) != 0 { + if nolook == 0 && + setunion(wsets[v].ws, clset) != 0 { + wsets[v].flag = 1 + work = 1 + } + continue nexts + } + } + + // not there; make a new entry + if cwp >= len(wsets) { + awsets := make([]Wset, cwp+WSETINC) + copy(awsets, wsets) + wsets = awsets + } + wsets[cwp].pitem = Pitem{prd, 0, prd[0], -prd[len(prd)-1]} + wsets[cwp].flag = 1 + wsets[cwp].ws = mkset() + if nolook == 0 { + work = 1 + copy(wsets[cwp].ws, clset) + } + cwp++ + } + } + } + + // have computed closure; flags are reset; return + if cldebug != 0 && foutput != nil { + fmt.Fprintf(foutput, "\nState %v, nolook = %v\n", i, nolook) + for u := 0; u < cwp; u++ { + if wsets[u].flag != 0 { + fmt.Fprintf(foutput, "flag set\n") + } + wsets[u].flag = 0 + fmt.Fprintf(foutput, "\t%v", writem(wsets[u].pitem)) + prlook(wsets[u].ws) + fmt.Fprintf(foutput, "\n") + } + } +} + +// +// sorts last state,and sees if it equals earlier ones. returns state number +// +func state(c int) int { + zzstate++ + p1 := pstate[nstate] + p2 := pstate[nstate+1] + if p1 == p2 { + return 0 // null state + } + + // sort the items + var k, l int + for k = p1 + 1; k < p2; k++ { // make k the biggest + for l = k; l > p1; l-- { + if statemem[l].pitem.prodno < statemem[l-1].pitem.prodno || + statemem[l].pitem.prodno == statemem[l-1].pitem.prodno && + statemem[l].pitem.off < statemem[l-1].pitem.off { + s := statemem[l] + statemem[l] = statemem[l-1] + statemem[l-1] = s + } else { + break + } + } + } + + size1 := p2 - p1 // size of state + + var i int + if c >= NTBASE { + i = ntstates[c-NTBASE] + } else { + i = tstates[c] + } + +look: + for ; i != 0; i = mstates[i] { + // get ith state + q1 := pstate[i] + q2 := pstate[i+1] + size2 := q2 - q1 + if size1 != size2 { + continue + } + k = p1 + for l = q1; l < q2; l++ { + if aryeq(statemem[l].pitem.prod, statemem[k].pitem.prod) == 0 || + statemem[l].pitem.off != statemem[k].pitem.off { + continue look + } + k++ + } + + // found it + pstate[nstate+1] = pstate[nstate] // delete last state + + // fix up lookaheads + if nolook != 0 { + return i + } + k = p1 + for l = q1; l < q2; l++ { + if setunion(statemem[l].look, statemem[k].look) != 0 { + tystate[i] = MUSTDO + } + k++ + } + return i + } + + // state is new + zznewstate++ + if nolook != 0 { + errorf("yacc state/nolook error") + } + pstate[nstate+2] = p2 + if nstate+1 >= NSTATES { + errorf("too many states") + } + if c >= NTBASE { + mstates[nstate] = ntstates[c-NTBASE] + ntstates[c-NTBASE] = nstate + } else { + mstates[nstate] = tstates[c] + tstates[c] = nstate + } + tystate[nstate] = MUSTDO + nstate++ + return nstate - 1 +} + +func putitem(p Pitem, set Lkset) { + p.off++ + p.first = p.prod[p.off] + + if pidebug != 0 && foutput != nil { + fmt.Fprintf(foutput, "putitem(%v), state %v\n", writem(p), nstate) + } + j := pstate[nstate+1] + if j >= len(statemem) { + asm := make([]Item, j+STATEINC) + copy(asm, statemem) + statemem = asm + } + statemem[j].pitem = p + if nolook == 0 { + s := mkset() + copy(s, set) + statemem[j].look = s + } + j++ + pstate[nstate+1] = j +} + +// +// creates output string for item pointed to by pp +// +func writem(pp Pitem) string { + var i int + + p := pp.prod + q := chcopy(nontrst[prdptr[pp.prodno][0]-NTBASE].name) + ": " + npi := pp.off + + pi := aryeq(p, prdptr[pp.prodno]) + + for { + c := ' ' + if pi == npi { + c = '.' + } + q += string(c) + + i = p[pi] + pi++ + if i <= 0 { + break + } + q += chcopy(symnam(i)) + } + + // an item calling for a reduction + i = p[npi] + if i < 0 { + q += fmt.Sprintf(" (%v)", -i) + } + + return q +} + +// +// pack state i from temp1 into amem +// +func apack(p []int, n int) int { + // + // we don't need to worry about checking because + // we will only look at entries known to be there... + // eliminate leading and trailing 0's + // + off := 0 + pp := 0 + for ; pp <= n && p[pp] == 0; pp++ { + off-- + } + + // no actions + if pp > n { + return 0 + } + for ; n > pp && p[n] == 0; n-- { + } + p = p[pp : n+1] + + // now, find a place for the elements from p to q, inclusive + r := len(amem) - len(p) + +nextk: + for rr := 0; rr <= r; rr++ { + qq := rr + for pp = 0; pp < len(p); pp++ { + if p[pp] != 0 { + if p[pp] != amem[qq] && amem[qq] != 0 { + continue nextk + } + } + qq++ + } + + // we have found an acceptable k + if pkdebug != 0 && foutput != nil { + fmt.Fprintf(foutput, "off = %v, k = %v\n", off+rr, rr) + } + qq = rr + for pp = 0; pp < len(p); pp++ { + if p[pp] != 0 { + if qq > memp { + memp = qq + } + amem[qq] = p[pp] + } + qq++ + } + if pkdebug != 0 && foutput != nil { + for pp = 0; pp <= memp; pp += 10 { + fmt.Fprintf(foutput, "\n") + for qq = pp; qq <= pp+9; qq++ { + fmt.Fprintf(foutput, "%v ", amem[qq]) + } + fmt.Fprintf(foutput, "\n") + } + } + return off + rr + } + errorf("no space in action table") + return 0 +} + +// +// print the output for the states +// +func output() { + var c, u, v int + + fmt.Fprintf(ftable, "\n//line yacctab:1\n") + fmt.Fprintf(ftable, "var %sExca = []int{\n", prefix) + + noset := mkset() + + // output the stuff for state i + for i := 0; i < nstate; i++ { + nolook = 0 + if tystate[i] != MUSTLOOKAHEAD { + nolook = 1 + } + closure(i) + + // output actions + nolook = 1 + aryfil(temp1, ntokens+nnonter+1, 0) + for u = 0; u < cwp; u++ { + c = wsets[u].pitem.first + if c > 1 && c < NTBASE && temp1[c] == 0 { + for v = u; v < cwp; v++ { + if c == wsets[v].pitem.first { + putitem(wsets[v].pitem, noset) + } + } + temp1[c] = state(c) + } else if c > NTBASE { + c -= NTBASE + if temp1[c+ntokens] == 0 { + temp1[c+ntokens] = amem[indgo[i]+c] + } + } + } + if i == 1 { + temp1[1] = ACCEPTCODE + } + + // now, we have the shifts; look at the reductions + lastred = 0 + for u = 0; u < cwp; u++ { + c = wsets[u].pitem.first + + // reduction + if c > 0 { + continue + } + lastred = -c + us := wsets[u].ws + for k := 0; k <= ntokens; k++ { + if bitset(us, k) == 0 { + continue + } + if temp1[k] == 0 { + temp1[k] = c + } else if temp1[k] < 0 { // reduce/reduce conflict + if foutput != nil { + fmt.Fprintf(foutput, + "\n %v: reduce/reduce conflict (red'ns "+ + "%v and %v) on %v", + i, -temp1[k], lastred, symnam(k)) + } + if -temp1[k] > lastred { + temp1[k] = -lastred + } + zzrrconf++ + } else { + // potential shift/reduce conflict + precftn(lastred, k, i) + } + } + } + wract(i) + } + + fmt.Fprintf(ftable, "}\n") + ftable.WriteRune('\n') + fmt.Fprintf(ftable, "const %sNprod = %v\n", prefix, nprod) + fmt.Fprintf(ftable, "const %sPrivate = %v\n", prefix, PRIVATE) + ftable.WriteRune('\n') + fmt.Fprintf(ftable, "var %sTokenNames []string\n", prefix) + fmt.Fprintf(ftable, "var %sStates []string\n", prefix) +} + +// +// decide a shift/reduce conflict by precedence. +// r is a rule number, t a token number +// the conflict is in state s +// temp1[t] is changed to reflect the action +// +func precftn(r, t, s int) { + var action int + + lp := levprd[r] + lt := toklev[t] + if PLEVEL(lt) == 0 || PLEVEL(lp) == 0 { + // conflict + if foutput != nil { + fmt.Fprintf(foutput, + "\n%v: shift/reduce conflict (shift %v(%v), red'n %v(%v)) on %v", + s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t)) + } + zzsrconf++ + return + } + if PLEVEL(lt) == PLEVEL(lp) { + action = ASSOC(lt) + } else if PLEVEL(lt) > PLEVEL(lp) { + action = RASC // shift + } else { + action = LASC + } // reduce + switch action { + case BASC: // error action + temp1[t] = ERRCODE + case LASC: // reduce + temp1[t] = -r + } +} + +// +// output state i +// temp1 has the actions, lastred the default +// +func wract(i int) { + var p, p1 int + + // find the best choice for lastred + lastred = 0 + ntimes := 0 + for j := 0; j <= ntokens; j++ { + if temp1[j] >= 0 { + continue + } + if temp1[j]+lastred == 0 { + continue + } + // count the number of appearances of temp1[j] + count := 0 + tred := -temp1[j] + levprd[tred] |= REDFLAG + for p = 0; p <= ntokens; p++ { + if temp1[p]+tred == 0 { + count++ + } + } + if count > ntimes { + lastred = tred + ntimes = count + } + } + + // + // for error recovery, arrange that, if there is a shift on the + // error recovery token, `error', that the default be the error action + // + if temp1[2] > 0 { + lastred = 0 + } + + // clear out entries in temp1 which equal lastred + // count entries in optst table + n := 0 + for p = 0; p <= ntokens; p++ { + p1 = temp1[p] + if p1+lastred == 0 { + temp1[p] = 0 + p1 = 0 + } + if p1 > 0 && p1 != ACCEPTCODE && p1 != ERRCODE { + n++ + } + } + + wrstate(i) + defact[i] = lastred + flag := 0 + os := make([]int, n*2) + n = 0 + for p = 0; p <= ntokens; p++ { + p1 = temp1[p] + if p1 != 0 { + if p1 < 0 { + p1 = -p1 + } else if p1 == ACCEPTCODE { + p1 = -1 + } else if p1 == ERRCODE { + p1 = 0 + } else { + os[n] = p + n++ + os[n] = p1 + n++ + zzacent++ + continue + } + if flag == 0 { + fmt.Fprintf(ftable, "\t-1, %v,\n", i) + } + flag++ + fmt.Fprintf(ftable, "\t%v, %v,\n", p, p1) + zzexcp++ + } + } + if flag != 0 { + defact[i] = -2 + fmt.Fprintf(ftable, "\t-2, %v,\n", lastred) + } + optst[i] = os +} + +// +// writes state i +// +func wrstate(i int) { + var j0, j1, u int + var pp, qq int + + if foutput == nil { + return + } + fmt.Fprintf(foutput, "\nstate %v\n", i) + qq = pstate[i+1] + for pp = pstate[i]; pp < qq; pp++ { + fmt.Fprintf(foutput, "\t%v\n", writem(statemem[pp].pitem)) + } + if tystate[i] == MUSTLOOKAHEAD { + // print out empty productions in closure + for u = pstate[i+1] - pstate[i]; u < cwp; u++ { + if wsets[u].pitem.first < 0 { + fmt.Fprintf(foutput, "\t%v\n", writem(wsets[u].pitem)) + } + } + } + + // check for state equal to another + for j0 = 0; j0 <= ntokens; j0++ { + j1 = temp1[j0] + if j1 != 0 { + fmt.Fprintf(foutput, "\n\t%v ", symnam(j0)) + + // shift, error, or accept + if j1 > 0 { + if j1 == ACCEPTCODE { + fmt.Fprintf(foutput, "accept") + } else if j1 == ERRCODE { + fmt.Fprintf(foutput, "error") + } else { + fmt.Fprintf(foutput, "shift %v", j1) + } + } else { + fmt.Fprintf(foutput, "reduce %v (src line %v)", -j1, rlines[-j1]) + } + } + } + + // output the final production + if lastred != 0 { + fmt.Fprintf(foutput, "\n\t. reduce %v (src line %v)\n\n", + lastred, rlines[lastred]) + } else { + fmt.Fprintf(foutput, "\n\t. error\n\n") + } + + // now, output nonterminal actions + j1 = ntokens + for j0 = 1; j0 <= nnonter; j0++ { + j1++ + if temp1[j1] != 0 { + fmt.Fprintf(foutput, "\t%v goto %v\n", symnam(j0+NTBASE), temp1[j1]) + } + } +} + +// +// output the gotos for the nontermninals +// +func go2out() { + for i := 1; i <= nnonter; i++ { + go2gen(i) + + // find the best one to make default + best := -1 + times := 0 + + // is j the most frequent + for j := 0; j < nstate; j++ { + if tystate[j] == 0 { + continue + } + if tystate[j] == best { + continue + } + + // is tystate[j] the most frequent + count := 0 + cbest := tystate[j] + for k := j; k < nstate; k++ { + if tystate[k] == cbest { + count++ + } + } + if count > times { + best = cbest + times = count + } + } + + // best is now the default entry + zzgobest += times - 1 + n := 0 + for j := 0; j < nstate; j++ { + if tystate[j] != 0 && tystate[j] != best { + n++ + } + } + goent := make([]int, 2*n+1) + n = 0 + for j := 0; j < nstate; j++ { + if tystate[j] != 0 && tystate[j] != best { + goent[n] = j + n++ + goent[n] = tystate[j] + n++ + zzgoent++ + } + } + + // now, the default + if best == -1 { + best = 0 + } + + zzgoent++ + goent[n] = best + yypgo[i] = goent + } +} + +// +// output the gotos for nonterminal c +// +func go2gen(c int) { + var i, cc, p, q int + + // first, find nonterminals with gotos on c + aryfil(temp1, nnonter+1, 0) + temp1[c] = 1 + work := 1 + for work != 0 { + work = 0 + for i = 0; i < nprod; i++ { + // cc is a nonterminal with a goto on c + cc = prdptr[i][1] - NTBASE + if cc >= 0 && temp1[cc] != 0 { + // thus, the left side of production i does too + cc = prdptr[i][0] - NTBASE + if temp1[cc] == 0 { + work = 1 + temp1[cc] = 1 + } + } + } + } + + // now, we have temp1[c] = 1 if a goto on c in closure of cc + if g2debug != 0 && foutput != nil { + fmt.Fprintf(foutput, "%v: gotos on ", nontrst[c].name) + for i = 0; i <= nnonter; i++ { + if temp1[i] != 0 { + fmt.Fprintf(foutput, "%v ", nontrst[i].name) + } + } + fmt.Fprintf(foutput, "\n") + } + + // now, go through and put gotos into tystate + aryfil(tystate, nstate, 0) + for i = 0; i < nstate; i++ { + q = pstate[i+1] + for p = pstate[i]; p < q; p++ { + cc = statemem[p].pitem.first + if cc >= NTBASE { + // goto on c is possible + if temp1[cc-NTBASE] != 0 { + tystate[i] = amem[indgo[i]+c] + break + } + } + } + } +} + +// +// in order to free up the mem and amem arrays for the optimizer, +// and still be able to output yyr1, etc., after the sizes of +// the action array is known, we hide the nonterminals +// derived by productions in levprd. +// +func hideprod() { + nred := 0 + levprd[0] = 0 + for i := 1; i < nprod; i++ { + if (levprd[i] & REDFLAG) == 0 { + if foutput != nil { + fmt.Fprintf(foutput, "Rule not reduced: %v\n", + writem(Pitem{prdptr[i], 0, 0, i})) + } + fmt.Printf("rule %v never reduced\n", writem(Pitem{prdptr[i], 0, 0, i})) + nred++ + } + levprd[i] = prdptr[i][0] - NTBASE + } + if nred != 0 { + fmt.Printf("%v rules never reduced\n", nred) + } +} + +func callopt() { + var j, k, p, q, i int + var v []int + + pgo = make([]int, nnonter+1) + pgo[0] = 0 + maxoff = 0 + maxspr = 0 + for i = 0; i < nstate; i++ { + k = 32000 + j = 0 + v = optst[i] + q = len(v) + for p = 0; p < q; p += 2 { + if v[p] > j { + j = v[p] + } + if v[p] < k { + k = v[p] + } + } + + // nontrivial situation + if k <= j { + // j is now the range + // j -= k; // call scj + if k > maxoff { + maxoff = k + } + } + tystate[i] = q + 2*j + if j > maxspr { + maxspr = j + } + } + + // initialize ggreed table + ggreed = make([]int, nnonter+1) + for i = 1; i <= nnonter; i++ { + ggreed[i] = 1 + j = 0 + + // minimum entry index is always 0 + v = yypgo[i] + q = len(v) - 1 + for p = 0; p < q; p += 2 { + ggreed[i] += 2 + if v[p] > j { + j = v[p] + } + } + ggreed[i] = ggreed[i] + 2*j + if j > maxoff { + maxoff = j + } + } + + // now, prepare to put the shift actions into the amem array + for i = 0; i < ACTSIZE; i++ { + amem[i] = 0 + } + maxa = 0 + for i = 0; i < nstate; i++ { + if tystate[i] == 0 && adb > 1 { + fmt.Fprintf(ftable, "State %v: null\n", i) + } + indgo[i] = yyFlag + } + + i = nxti() + for i != NOMORE { + if i >= 0 { + stin(i) + } else { + gin(-i) + } + i = nxti() + } + + // print amem array + if adb > 2 { + for p = 0; p <= maxa; p += 10 { + fmt.Fprintf(ftable, "%v ", p) + for i = 0; i < 10; i++ { + fmt.Fprintf(ftable, "%v ", amem[p+i]) + } + ftable.WriteRune('\n') + } + } + + aoutput() + osummary() +} + +// +// finds the next i +// +func nxti() int { + max := 0 + maxi := 0 + for i := 1; i <= nnonter; i++ { + if ggreed[i] >= max { + max = ggreed[i] + maxi = -i + } + } + for i := 0; i < nstate; i++ { + if tystate[i] >= max { + max = tystate[i] + maxi = i + } + } + if max == 0 { + return NOMORE + } + return maxi +} + +func gin(i int) { + var s int + + // enter gotos on nonterminal i into array amem + ggreed[i] = 0 + + q := yypgo[i] + nq := len(q) - 1 + + // now, find amem place for it +nextgp: + for p := 0; p < ACTSIZE; p++ { + if amem[p] != 0 { + continue + } + for r := 0; r < nq; r += 2 { + s = p + q[r] + 1 + if s > maxa { + maxa = s + if maxa >= ACTSIZE { + errorf("a array overflow") + } + } + if amem[s] != 0 { + continue nextgp + } + } + + // we have found amem spot + amem[p] = q[nq] + if p > maxa { + maxa = p + } + for r := 0; r < nq; r += 2 { + s = p + q[r] + 1 + amem[s] = q[r+1] + } + pgo[i] = p + if adb > 1 { + fmt.Fprintf(ftable, "Nonterminal %v, entry at %v\n", i, pgo[i]) + } + return + } + errorf("cannot place goto %v\n", i) +} + +func stin(i int) { + var s int + + tystate[i] = 0 + + // enter state i into the amem array + q := optst[i] + nq := len(q) + +nextn: + // find an acceptable place + for n := -maxoff; n < ACTSIZE; n++ { + flag := 0 + for r := 0; r < nq; r += 2 { + s = q[r] + n + if s < 0 || s > ACTSIZE { + continue nextn + } + if amem[s] == 0 { + flag++ + } else if amem[s] != q[r+1] { + continue nextn + } + } + + // check the position equals another only if the states are identical + for j := 0; j < nstate; j++ { + if indgo[j] == n { + + // we have some disagreement + if flag != 0 { + continue nextn + } + if nq == len(optst[j]) { + + // states are equal + indgo[i] = n + if adb > 1 { + fmt.Fprintf(ftable, "State %v: entry at"+ + "%v equals state %v\n", + i, n, j) + } + return + } + + // we have some disagreement + continue nextn + } + } + + for r := 0; r < nq; r += 2 { + s = q[r] + n + if s > maxa { + maxa = s + } + if amem[s] != 0 && amem[s] != q[r+1] { + errorf("clobber of a array, pos'n %v, by %v", s, q[r+1]) + } + amem[s] = q[r+1] + } + indgo[i] = n + if adb > 1 { + fmt.Fprintf(ftable, "State %v: entry at %v\n", i, indgo[i]) + } + return + } + errorf("Error; failure to place state %v", i) +} + +// +// this version is for limbo +// write out the optimized parser +// +func aoutput() { + ftable.WriteRune('\n') + fmt.Fprintf(ftable, "const %sLast = %v\n\n", prefix, maxa+1) + arout("Act", amem, maxa+1) + arout("Pact", indgo, nstate) + arout("Pgo", pgo, nnonter+1) +} + +// +// put out other arrays, copy the parsers +// +func others() { + var i, j int + + arout("R1", levprd, nprod) + aryfil(temp1, nprod, 0) + + // + //yyr2 is the number of rules for each production + // + for i = 1; i < nprod; i++ { + temp1[i] = len(prdptr[i]) - 2 + } + arout("R2", temp1, nprod) + + aryfil(temp1, nstate, -1000) + for i = 0; i <= ntokens; i++ { + for j := tstates[i]; j != 0; j = mstates[j] { + temp1[j] = i + } + } + for i = 0; i <= nnonter; i++ { + for j = ntstates[i]; j != 0; j = mstates[j] { + temp1[j] = -i + } + } + arout("Chk", temp1, nstate) + arout("Def", defact, nstate) + + // put out token translation tables + // table 1 has 0-256 + aryfil(temp1, 256, 0) + c := 0 + for i = 1; i <= ntokens; i++ { + 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.Printf(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name) + nerrors++ + } + temp1[j] = i + if j > c { + c = j + } + } + } + for i = 0; i <= c; i++ { + if temp1[i] == 0 { + temp1[i] = YYLEXUNK + } + } + arout("Tok1", temp1, c+1) + + // table 2 has PRIVATE-PRIVATE+256 + aryfil(temp1, 256, 0) + c = 0 + for i = 1; i <= ntokens; i++ { + 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.Printf(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name) + nerrors++ + } + temp1[j] = i + if j > c { + c = j + } + } + } + arout("Tok2", temp1, c+1) + + // table 3 has everything else + fmt.Fprintf(ftable, "var %sTok3 = []int{\n\t", prefix) + c = 0 + for i = 1; i <= ntokens; i++ { + j = tokset[i].value + if j >= 0 && j < 256 { + continue + } + if j >= PRIVATE && j < 256+PRIVATE { + continue + } + + if c%5 != 0 { + ftable.WriteRune(' ') + } + fmt.Fprintf(ftable, "%d, %d,", j, i) + c++ + if c%5 == 0 { + fmt.Fprint(ftable, "\n\t") + } + } + if c%5 != 0 { + ftable.WriteRune(' ') + } + fmt.Fprintf(ftable, "%d,\n}\n", 0) + + // copy parser text + ch := getrune(finput) + for ch != EOF { + ftable.WriteRune(ch) + ch = getrune(finput) + } + + // copy yaccpar + fmt.Fprintf(ftable, "\n//line yaccpar:1\n") + + parts := strings.SplitN(yaccpar, prefix+"run()", 2) + fmt.Fprintf(ftable, "%v", parts[0]) + ftable.Write(fcode.Bytes()) + fmt.Fprintf(ftable, "%v", parts[1]) +} + +func arout(s string, v []int, n int) { + s = prefix + s + fmt.Fprintf(ftable, "var %v = []int{\n", s) + for i := 0; i < n; i++ { + if i%10 == 0 { + fmt.Fprintf(ftable, "\n\t") + } else { + ftable.WriteRune(' ') + } + fmt.Fprintf(ftable, "%d,", v[i]) + } + fmt.Fprintf(ftable, "\n}\n") +} + +// +// output the summary on y.output +// +func summary() { + if foutput != nil { + fmt.Fprintf(foutput, "\n%v terminals, %v nonterminals\n", ntokens, nnonter+1) + fmt.Fprintf(foutput, "%v grammar rules, %v/%v states\n", nprod, nstate, NSTATES) + fmt.Fprintf(foutput, "%v shift/reduce, %v reduce/reduce conflicts reported\n", zzsrconf, zzrrconf) + fmt.Fprintf(foutput, "%v working sets used\n", len(wsets)) + fmt.Fprintf(foutput, "memory: parser %v/%v\n", memp, ACTSIZE) + fmt.Fprintf(foutput, "%v extra closures\n", zzclose-2*nstate) + fmt.Fprintf(foutput, "%v shift entries, %v exceptions\n", zzacent, zzexcp) + fmt.Fprintf(foutput, "%v goto entries\n", zzgoent) + fmt.Fprintf(foutput, "%v entries saved by goto default\n", zzgobest) + } + if zzsrconf != 0 || zzrrconf != 0 { + fmt.Printf("\nconflicts: ") + if zzsrconf != 0 { + fmt.Printf("%v shift/reduce", zzsrconf) + } + if zzsrconf != 0 && zzrrconf != 0 { + fmt.Printf(", ") + } + if zzrrconf != 0 { + fmt.Printf("%v reduce/reduce", zzrrconf) + } + fmt.Printf("\n") + } +} + +// +// write optimizer summary +// +func osummary() { + if foutput == nil { + return + } + i := 0 + for p := maxa; p >= 0; p-- { + if amem[p] == 0 { + i++ + } + } + + fmt.Fprintf(foutput, "Optimizer space used: output %v/%v\n", maxa+1, ACTSIZE) + fmt.Fprintf(foutput, "%v table entries, %v zero\n", maxa+1, i) + fmt.Fprintf(foutput, "maximum spread: %v, maximum offset: %v\n", maxspr, maxoff) +} + +// +// copies and protects "'s in q +// +func chcopy(q string) string { + s := "" + i := 0 + j := 0 + for i = 0; i < len(q); i++ { + if q[i] == '"' { + s += q[j:i] + "\\" + j = i + } + } + return s + q[j:i] +} + +func usage() { + fmt.Fprintf(stderr, "usage: yacc [-o output] [-v parsetable] input\n") + exit(1) +} + +func bitset(set Lkset, bit int) int { return set[bit>>5] & (1 << uint(bit&31)) } + +func setbit(set Lkset, bit int) { set[bit>>5] |= (1 << uint(bit&31)) } + +func mkset() Lkset { return make([]int, tbitset) } + +// +// set a to the union of a and b +// return 1 if b is not a subset of a, 0 otherwise +// +func setunion(a, b []int) int { + sub := 0 + for i := 0; i < tbitset; i++ { + x := a[i] + y := x | b[i] + a[i] = y + if y != x { + sub = 1 + } + } + return sub +} + +func prlook(p Lkset) { + if p == nil { + fmt.Fprintf(foutput, "\tNULL") + return + } + fmt.Fprintf(foutput, " { ") + for j := 0; j <= ntokens; j++ { + if bitset(p, j) != 0 { + fmt.Fprintf(foutput, "%v ", symnam(j)) + } + } + fmt.Fprintf(foutput, "}") +} + +// +// utility routines +// +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') +} + +func mktemp(t string) string { return t } + +// +// return 1 if 2 arrays are equal +// return 0 if not equal +// +func aryeq(a []int, b []int) int { + n := len(a) + if len(b) != n { + return 0 + } + for ll := 0; ll < n; ll++ { + if a[ll] != b[ll] { + return 0 + } + } + return 1 +} + +func putrune(f *bufio.Writer, c int) { + s := string(c) + for i := 0; i < len(s); i++ { + f.WriteByte(s[i]) + } +} + +func getrune(f *bufio.Reader) rune { + var r rune + + if peekrune != 0 { + if peekrune == EOF { + return EOF + } + r = peekrune + peekrune = 0 + return r + } + + c, n, err := f.ReadRune() + if n == 0 { + return EOF + } + if err != nil { + errorf("read error: %v", err) + } + //fmt.Printf("rune = %v n=%v\n", string(c), n); + return c +} + +func ungetrune(f *bufio.Reader, c rune) { + if f != finput { + panic("ungetc - not finput") + } + if peekrune != 0 { + panic("ungetc - 2nd unget") + } + peekrune = c +} + +func write(f *bufio.Writer, b []byte, n int) int { + panic("write") + return 0 +} + +func open(s string) *bufio.Reader { + fi, err := os.Open(s) + if err != nil { + errorf("error opening %v: %v", s, err) + } + //fmt.Printf("open %v\n", s); + return bufio.NewReader(fi) +} + +func create(s string) *bufio.Writer { + fo, err := os.Create(s) + if err != nil { + errorf("error creating %v: %v", s, err) + } + //fmt.Printf("create %v mode %v\n", s); + return bufio.NewWriter(fo) +} + +// +// write out error comment +// +func errorf(s string, v ...interface{}) { + nerrors++ + fmt.Fprintf(stderr, s, v...) + fmt.Fprintf(stderr, ": %v:%v\n", infile, lineno) + if fatfl != 0 { + summary() + exit(1) + } +} + +func exit(status int) { + if ftable != nil { + ftable.Flush() + ftable = nil + } + if foutput != nil { + foutput.Flush() + foutput = nil + } + if stderr != nil { + stderr.Flush() + stderr = nil + } + os.Exit(status) +} + +var yaccpar string // will be processed version of yaccpartext: s/$$/prefix/g +var yaccpartext = ` +/* parser for yacc output */ + +var $$Debug = 0 + +type $$Lexer interface { + Lex(lval *$$SymType) int + Error(s string) +} + +const $$Flag = -1000 + +func $$Tokname(c int) string { + if c > 0 && c <= len($$Toknames) { + if $$Toknames[c-1] != "" { + return $$Toknames[c-1] + } + } + return fmt.Sprintf("tok-%v", c) +} + +func $$Statname(s int) string { + if s >= 0 && s < len($$Statenames) { + if $$Statenames[s] != "" { + return $$Statenames[s] + } + } + return fmt.Sprintf("state-%v", s) +} + +func $$lex1(lex $$Lexer, lval *$$SymType) int { + c := 0 + char := lex.Lex(lval) + if char <= 0 { + c = $$Tok1[0] + goto out + } + if char < len($$Tok1) { + c = $$Tok1[char] + goto out + } + if char >= $$Private { + if char < $$Private+len($$Tok2) { + c = $$Tok2[char-$$Private] + goto out + } + } + for i := 0; i < len($$Tok3); i += 2 { + c = $$Tok3[i+0] + if c == char { + c = $$Tok3[i+1] + goto out + } + } + +out: + if c == 0 { + c = $$Tok2[1] /* unknown char */ + } + if $$Debug >= 3 { + fmt.Printf("lex %U %s\n", uint(char), $$Tokname(c)) + } + return c +} + +func $$Parse($$lex $$Lexer) int { + var $$n int + var $$lval $$SymType + var $$VAL $$SymType + $$S := make([]$$SymType, $$MaxDepth) + + Nerrs := 0 /* number of errors */ + Errflag := 0 /* error recovery flag */ + $$state := 0 + $$char := -1 + $$p := -1 + goto $$stack + +ret0: + return 0 + +ret1: + return 1 + +$$stack: + /* put a state and value onto the stack */ + if $$Debug >= 4 { + fmt.Printf("char %v in %v\n", $$Tokname($$char), $$Statname($$state)) + } + + $$p++ + if $$p >= len($$S) { + nyys := make([]$$SymType, len($$S)*2) + copy(nyys, $$S) + $$S = nyys + } + $$S[$$p] = $$VAL + $$S[$$p].yys = $$state + +$$newstate: + $$n = $$Pact[$$state] + if $$n <= $$Flag { + goto $$default /* simple state */ + } + if $$char < 0 { + $$char = $$lex1($$lex, &$$lval) + } + $$n += $$char + if $$n < 0 || $$n >= $$Last { + goto $$default + } + $$n = $$Act[$$n] + if $$Chk[$$n] == $$char { /* valid shift */ + $$char = -1 + $$VAL = $$lval + $$state = $$n + if Errflag > 0 { + Errflag-- + } + goto $$stack + } + +$$default: + /* default state action */ + $$n = $$Def[$$state] + if $$n == -2 { + if $$char < 0 { + $$char = $$lex1($$lex, &$$lval) + } + + /* look through exception table */ + xi := 0 + for { + if $$Exca[xi+0] == -1 && $$Exca[xi+1] == $$state { + break + } + xi += 2 + } + for xi += 2; ; xi += 2 { + $$n = $$Exca[xi+0] + if $$n < 0 || $$n == $$char { + break + } + } + $$n = $$Exca[xi+1] + if $$n < 0 { + goto ret0 + } + } + if $$n == 0 { + /* error ... attempt to resume parsing */ + switch Errflag { + case 0: /* brand new error */ + $$lex.Error("syntax error") + Nerrs++ + if $$Debug >= 1 { + fmt.Printf("%s", $$Statname($$state)) + fmt.Printf("saw %s\n", $$Tokname($$char)) + } + fallthrough + + case 1, 2: /* incompletely recovered error ... try again */ + Errflag = 3 + + /* find a state where "error" is a legal shift action */ + for $$p >= 0 { + $$n = $$Pact[$$S[$$p].yys] + $$ErrCode + if $$n >= 0 && $$n < $$Last { + $$state = $$Act[$$n] /* simulate a shift of "error" */ + if $$Chk[$$state] == $$ErrCode { + goto $$stack + } + } + + /* the current p has no shift on "error", pop stack */ + if $$Debug >= 2 { + fmt.Printf("error recovery pops state %d\n", $$S[$$p].yys) + } + $$p-- + } + /* there is no state on the stack with an error shift ... abort */ + goto ret1 + + case 3: /* no shift yet; clobber input char */ + if $$Debug >= 2 { + fmt.Printf("error recovery discards %s\n", $$Tokname($$char)) + } + if $$char == $$EofCode { + goto ret1 + } + $$char = -1 + goto $$newstate /* try again in the same state */ + } + } + + /* reduction by production $$n */ + if $$Debug >= 2 { + fmt.Printf("reduce %v in:\n\t%v\n", $$n, $$Statname($$state)) + } + + $$nt := $$n + $$pt := $$p + _ = $$pt // guard against "declared and not used" + + $$p -= $$R2[$$n] + $$VAL = $$S[$$p+1] + + /* consult goto table to find next state */ + $$n = $$R1[$$n] + $$g := $$Pgo[$$n] + $$j := $$g + $$S[$$p].yys + 1 + + if $$j >= $$Last { + $$state = $$Act[$$g] + } else { + $$state = $$Act[$$j] + if $$Chk[$$state] != -$$n { + $$state = $$Act[$$g] + } + } + // dummy call; replaced with literal code + $$run() + goto $$stack /* stack new state and value */ +} +` diff --git a/src/env.bash b/src/env.bash deleted file mode 100644 index de446bf47..000000000 --- a/src/env.bash +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# If set to a Windows-style path convert to an MSYS-Unix -# one using the built-in shell commands. -if [[ "$GOROOT" == *:* ]]; then - GOROOT=$(cd "$GOROOT"; pwd) -fi - -if [[ "$GOBIN" == *:* ]]; then - GOBIN=$(cd "$GOBIN"; pwd) -fi - -export GOROOT=${GOROOT:-$(cd ..; pwd)} - -if ! test -f "$GOROOT"/include/u.h -then - echo '$GOROOT is not set correctly or not exported: '$GOROOT 1>&2 - exit 1 -fi - -# Double-check that we're in $GOROOT, for people with multiple Go trees. -# Various aspects of the build cd into $GOROOT-rooted paths, -# making it easy to jump to a different tree and get confused. -DIR1=$(cd ..; pwd) -DIR2=$(cd "$GOROOT"; pwd) -if [ "$DIR1" != "$DIR2" ]; then - echo 'Suspicious $GOROOT '"$GOROOT"': does not match current directory.' 1>&2 - exit 1 -fi - -export GOBIN=${GOBIN:-"$GOROOT/bin"} -if [ ! -d "$GOBIN" -a "$GOBIN" != "$GOROOT/bin" ]; then - echo '$GOBIN is not a directory or does not exist' 1>&2 - echo 'create it or set $GOBIN differently' 1>&2 - exit 1 -fi - -export OLDPATH=$PATH -export PATH="$GOBIN":$PATH - -MAKE=make -if ! make --version 2>/dev/null | grep 'GNU Make' >/dev/null; then - MAKE=gmake -fi - -PROGS=" - ar - awk - bash - bison - chmod - cp - cut - echo - egrep - gcc - grep - ls - mkdir - mv - pwd - rm - sed - sort - tee - touch - tr - true - uname - uniq -" - -for i in $PROGS; do - if ! which $i >/dev/null 2>&1; then - echo "Cannot find '$i' on search path." 1>&2 - echo "See http://golang.org/doc/install.html#ctools" 1>&2 - exit 1 - fi -done - -if bison --version 2>&1 | grep 'bison++' >/dev/null 2>&1; then - echo "Your system's 'bison' is bison++." - echo "Go needs the original bison instead." 1>&2 - echo "See http://golang.org/doc/install.html#ctools" 1>&2 - exit 1 -fi - -# Issue 2020: some users configure bash to default to -# set -o noclobber -# which makes >x fail if x already exists. Restore sanity. -set +o noclobber - -# Tried to use . <($MAKE ...) here, but it cannot set environment -# variables in the version of bash that ships with OS X. Amazing. -eval $($MAKE --no-print-directory -f Make.inc go-env | egrep 'GOARCH|GOOS|GOHOSTARCH|GOHOSTOS|GO_ENV') - -# Shell doesn't tell us whether make succeeded, -# so Make.inc generates a fake variable name. -if [ "$MAKE_GO_ENV_WORKED" != 1 ]; then - echo 'Did not find Go environment variables.' 1>&2 - exit 1 -fi -unset MAKE_GO_ENV_WORKED diff --git a/src/lib9/Makefile b/src/lib9/Makefile index 28c97c9b4..62aba5dca 100644 --- a/src/lib9/Makefile +++ b/src/lib9/Makefile @@ -1,121 +1,5 @@ -# 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. -include ../Make.inc -O:=$(HOST_O) - -LIB=lib9.a - -NUM=\ - charstod.$O\ - pow10.$O\ - -# Could add fmt/errfmt, but we want to pick it up from ./errstr.c instead. -FMTOFILES=\ - dofmt.$O\ - fltfmt.$O\ - fmt.$O\ - fmtfd.$O\ - fmtfdflush.$O\ - fmtlocale.$O\ - fmtlock2.$O\ - fmtnull.$O\ - fmtprint.$O\ - fmtquote.$O\ - fmtrune.$O\ - fmtstr.$O\ - fmtvprint.$O\ - fprint.$O\ - nan64.$O\ - print.$O\ - seprint.$O\ - smprint.$O\ - snprint.$O\ - sprint.$O\ - strtod.$O\ - vfprint.$O\ - vseprint.$O\ - vsmprint.$O\ - vsnprint.$O\ - $(NUM)\ - -UTFOFILES=\ - rune.$O\ - utfecpy.$O\ - utflen.$O\ - utfnlen.$O\ - utfrrune.$O\ - utfrune.$O\ - utfutf.$O\ - runetype.$O\ - -LIB9OFILES=\ - _p9dir.$O\ - _exits.$O\ - argv0.$O\ - atoi.$O\ - cleanname.$O\ - create.$O\ - dirfstat.$O\ - dirfwstat.$O\ - dirstat.$O\ - dirwstat.$O\ - dup.$O\ - errstr.$O\ - exec.$O\ - execl.$O\ - exitcode.$O\ - exits.$O\ - getenv.$O\ - getfields.$O\ - getwd.$O\ - goos.$O\ - main.$O\ - nan.$O\ - nulldir.$O\ - open.$O\ - readn.$O\ - seek.$O\ - strecpy.$O\ - sysfatal.$O\ - time.$O\ - tokenize.$O\ - -ifeq ($(GOHOSTOS),windows) -LIB9OFILES+=\ - win32.$O\ - -else -LIB9OFILES+=\ - await.$O\ - getuser.$O\ - jmp.$O\ - notify.$O\ - rfork.$O\ - -endif - -OFILES=\ - $(LIB9OFILES)\ - $(FMTOFILES)\ - $(UTFOFILES)\ - -HFILES=\ - $(QUOTED_GOROOT)/include/u.h\ - $(QUOTED_GOROOT)/include/libc.h\ - -include ../Make.clib - -GOROOT_FINAL?=$(GOROOT) - -%.$O: fmt/%.c - $(HOST_CC) -c $(HOST_CFLAGS) -DPLAN9PORT -Ifmt $< - -%.$O: utf/%.c - $(HOST_CC) -c $(HOST_CFLAGS) $< - -goos.$O: goos.c - GOVERSION=`../version.bash` && \ - $(HOST_CC) -c $(HOST_CFLAGS) -DGOOS='"$(GOOS)"' -DGOARCH='"$(GOARCH)"' -DGOROOT='"$(GOROOT_FINAL)"' -DGOVERSION='"'"$$GOVERSION"'"' $< - +include ../Make.dist diff --git a/src/lib9/await.c b/src/lib9/await.c index 90be598a1..0f00a94bd 100644 --- a/src/lib9/await.c +++ b/src/lib9/await.c @@ -1,3 +1,5 @@ +// +build !windows + /* Plan 9 from User Space src/lib9/await.c http://code.swtch.com/plan9port/src/tip/src/lib9/await.c diff --git a/src/lib9/ctime.c b/src/lib9/ctime.c new file mode 100644 index 000000000..1bc29fbf9 --- /dev/null +++ b/src/lib9/ctime.c @@ -0,0 +1,28 @@ +// 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. + +#define NOPLAN9DEFINES +#include +#include + +char* +p9ctime(long t) +{ + static char buf[100]; + time_t tt; + struct tm *tm; + + tt = t; + tm = localtime(&tt); + snprint(buf, sizeof buf, "%3.3s %3.3s %02d %02d:%02d:%02d %3.3s %d\n", + "SunMonTueWedThuFriSat"+(tm->tm_wday*3), + "JanFebMarAprMayJunJulAugSepOctNovDec"+(tm->tm_mon*3), + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec, + "XXX", // tm_zone is unavailable on windows, and no one cares + tm->tm_year + 1900); + return buf; +} diff --git a/src/lib9/exitcode.c b/src/lib9/exitcode.c index 234492acf..a952b2da2 100644 --- a/src/lib9/exitcode.c +++ b/src/lib9/exitcode.c @@ -29,6 +29,7 @@ THE SOFTWARE. int exitcode(char *s) { + USED(s); return 1; } diff --git a/src/lib9/fmt/errfmt.c b/src/lib9/fmt/errfmt.c deleted file mode 100644 index 66c9600f0..000000000 --- a/src/lib9/fmt/errfmt.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson, - * with contributions from Mike Burrows and Sean Dorward. - * - * Copyright (c) 2002-2006 by Lucent Technologies. - * Portions Copyright (c) 2004 Google Inc. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES - * NOR GOOGLE INC MAKE ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING - * THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ - -#include -#include -#include "fmtdef.h" - -int -__errfmt(Fmt *f) -{ - char *s; - - s = strerror(errno); - return fmtstrcpy(f, s); -} diff --git a/src/lib9/fmt/fmtprint.c b/src/lib9/fmt/fmtprint.c index 8a29e6faf..6848ab4eb 100644 --- a/src/lib9/fmt/fmtprint.c +++ b/src/lib9/fmt/fmtprint.c @@ -21,31 +21,19 @@ #include "fmtdef.h" /* - * format a string into the output buffer - * designed for formats which themselves call fmt, - * but ignore any width flags + * Format a string into the output buffer. + * Designed for formats which themselves call fmt. + * Flags, precision and width are preserved. */ int fmtprint(Fmt *f, char *fmt, ...) { - va_list va; int n; + va_list va; - f->flags = 0; - f->width = 0; - f->prec = 0; - VA_COPY(va, f->args); - VA_END(f->args); - va_start(f->args, fmt); - n = dofmt(f, fmt); - va_end(f->args); - f->flags = 0; - f->width = 0; - f->prec = 0; - VA_COPY(f->args,va); - VA_END(va); - if(n >= 0) - return 0; + va_start(va, fmt); + n = fmtvprint(f, fmt, va); + va_end(va); return n; } diff --git a/src/lib9/fmt/fmtvprint.c b/src/lib9/fmt/fmtvprint.c index 6acd37a51..f18d27bba 100644 --- a/src/lib9/fmt/fmtvprint.c +++ b/src/lib9/fmt/fmtvprint.c @@ -22,31 +22,31 @@ /* - * format a string into the output buffer - * designed for formats which themselves call fmt, - * but ignore any width flags + * Format a string into the output buffer. + * Designed for formats which themselves call fmt. + * Flags, precision and width are preserved. */ int fmtvprint(Fmt *f, char *fmt, va_list args) { va_list va; - int n; + int n, w, p; + unsigned long fl; - f->flags = 0; - f->width = 0; - f->prec = 0; - VA_COPY(va,f->args); + w = f->width; + p = f->prec; + fl = f->flags; + VA_COPY(va, f->args); VA_END(f->args); - VA_COPY(f->args,args); + VA_COPY(f->args, args); n = dofmt(f, fmt); - f->flags = 0; - f->width = 0; - f->prec = 0; VA_END(f->args); - VA_COPY(f->args,va); + VA_COPY(f->args, va); VA_END(va); + f->width = w; + f->prec = p; + f->flags = fl; if(n >= 0) return 0; return n; } - diff --git a/src/lib9/fork.c b/src/lib9/fork.c deleted file mode 100644 index 0dd79dfb8..000000000 --- a/src/lib9/fork.c +++ /dev/null @@ -1,46 +0,0 @@ -/* -Plan 9 from User Space src/lib9/fork.c -http://code.swtch.com/plan9port/src/tip/src/lib9/fork.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 -#include "9proc.h" -#undef fork - -int -p9fork(void) -{ - int pid; - sigset_t all, old; - - sigfillset(&all); - sigprocmask(SIG_SETMASK, &all, &old); - pid = fork(); - if(pid == 0){ - _clearuproc(); - _p9uproc(0); - } - sigprocmask(SIG_SETMASK, &old, nil); - return pid; -} diff --git a/src/lib9/getuser.c b/src/lib9/getuser.c index f70b35c87..d611f4467 100644 --- a/src/lib9/getuser.c +++ b/src/lib9/getuser.c @@ -1,3 +1,5 @@ +// +build !windows + /* Plan 9 from User Space src/lib9/getuser.c http://code.swtch.com/plan9port/src/tip/src/lib9/getuser.c diff --git a/src/lib9/jmp.c b/src/lib9/jmp.c index a606fb07b..c44e040d2 100644 --- a/src/lib9/jmp.c +++ b/src/lib9/jmp.c @@ -1,3 +1,5 @@ +// +build !windows + /* Plan 9 from User Space src/lib9/jmp.c http://code.swtch.com/plan9port/src/tip/src/lib9/jmp.c diff --git a/src/lib9/notify.c b/src/lib9/notify.c index 84999b887..c424aed54 100644 --- a/src/lib9/notify.c +++ b/src/lib9/notify.c @@ -1,3 +1,5 @@ +// +build !windows + /* Plan 9 from User Space src/lib9/notify.c http://code.swtch.com/plan9port/src/tip/src/lib9/notify.c diff --git a/src/lib9/rfork.c b/src/lib9/rfork.c index c9d632189..5a6eaeb94 100644 --- a/src/lib9/rfork.c +++ b/src/lib9/rfork.c @@ -1,3 +1,5 @@ +// +build !windows + /* Plan 9 from User Space src/lib9/rfork.c http://code.swtch.com/plan9port/src/tip/src/lib9/rfork.c diff --git a/src/lib9/utf/Makefile b/src/lib9/utf/Makefile index c3b9ec5d0..bbb2da6a9 100644 --- a/src/lib9/utf/Makefile +++ b/src/lib9/utf/Makefile @@ -4,21 +4,17 @@ # The library is built by the Makefile in the parent directory. # This Makefile only builds mkrunetype. - -include ../../Make.inc -O:=$(HOST_O) +# GOROOT, GOOS, and GOARCH must be set explicitly. TARG=mkrunetype -OFILES=\ - mkrunetype.$O\ - -include ../../Make.ccmd - UnicodeData-%.txt: curl http://www.unicode.org/Public/$*/ucd/UnicodeData.txt >_$@ mv _$@ $@ +mkrunetype: mkrunetype.c + cc -I../../../include -o mkrunetype -L$(GOROOT)/pkg/obj/$(GOOS)_$(GOARCH)/ mkrunetype.c -l9 + runetypebody-%.c: mkrunetype UnicodeData-%.txt mkrunetype -p UnicodeData-$*.txt >_$@ mv _$@ $@ @@ -30,3 +26,5 @@ UNICODE_VERSION=6.0.0 test: mkrunetype UnicodeData-$(UNICODE_VERSION).txt mkrunetype -c UnicodeData-$(UNICODE_VERSION).txt +clean: + rm -f UnicodeData.txt mkrunetype \ No newline at end of file diff --git a/src/lib9/utf/mkrunetype.c b/src/lib9/utf/mkrunetype.c index 06d52b572..01eb6b6ec 100644 --- a/src/lib9/utf/mkrunetype.c +++ b/src/lib9/utf/mkrunetype.c @@ -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 + /* * make is(upper|lower|title|space|alpha)rune and * to(upper|lower|title)rune from a UnicodeData.txt file. diff --git a/src/lib9/utf/runetype.c b/src/lib9/utf/runetype.c index 27867430b..51729fb01 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.c" +#include "runetypebody-6.0.0.h" diff --git a/src/lib9/utf/runetypebody-5.0.0.c b/src/lib9/utf/runetypebody-5.0.0.c deleted file mode 100644 index 67a645d60..000000000 --- a/src/lib9/utf/runetypebody-5.0.0.c +++ /dev/null @@ -1,1361 +0,0 @@ -/* generated automatically by mkrunetype.c from UnicodeData-5.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, - 0x17e0, 0x17e9, - 0x1810, 0x1819, - 0x1946, 0x194f, - 0x19d0, 0x19d9, - 0x1b50, 0x1b59, - 0xff10, 0xff19, - 0x104a0, 0x104a9, - 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, - 0x037a, 0x037d, - 0x0388, 0x038a, - 0x038e, 0x03a1, - 0x03a3, 0x03ce, - 0x03d0, 0x03f5, - 0x03f7, 0x0481, - 0x048a, 0x0513, - 0x0531, 0x0556, - 0x0561, 0x0587, - 0x05d0, 0x05ea, - 0x05f0, 0x05f2, - 0x0621, 0x063a, - 0x0640, 0x064a, - 0x066e, 0x066f, - 0x0671, 0x06d3, - 0x06e5, 0x06e6, - 0x06ee, 0x06ef, - 0x06fa, 0x06fc, - 0x0712, 0x072f, - 0x074d, 0x076d, - 0x0780, 0x07a5, - 0x07ca, 0x07ea, - 0x07f4, 0x07f5, - 0x0904, 0x0939, - 0x0958, 0x0961, - 0x097b, 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, - 0x0c60, 0x0c61, - 0x0c85, 0x0c8c, - 0x0c8e, 0x0c90, - 0x0c92, 0x0ca8, - 0x0caa, 0x0cb3, - 0x0cb5, 0x0cb9, - 0x0ce0, 0x0ce1, - 0x0d05, 0x0d0c, - 0x0d0e, 0x0d10, - 0x0d12, 0x0d28, - 0x0d2a, 0x0d39, - 0x0d60, 0x0d61, - 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, 0x0f6a, - 0x0f88, 0x0f8b, - 0x1000, 0x1021, - 0x1023, 0x1027, - 0x1029, 0x102a, - 0x1050, 0x1055, - 0x10a0, 0x10c5, - 0x10d0, 0x10fa, - 0x1100, 0x1159, - 0x115f, 0x11a2, - 0x11a8, 0x11f9, - 0x1200, 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, 0x1676, - 0x1681, 0x169a, - 0x16a0, 0x16ea, - 0x1700, 0x170c, - 0x170e, 0x1711, - 0x1720, 0x1731, - 0x1740, 0x1751, - 0x1760, 0x176c, - 0x176e, 0x1770, - 0x1780, 0x17b3, - 0x1820, 0x1877, - 0x1880, 0x18a8, - 0x1900, 0x191c, - 0x1950, 0x196d, - 0x1970, 0x1974, - 0x1980, 0x19a9, - 0x19c1, 0x19c7, - 0x1a00, 0x1a16, - 0x1b05, 0x1b33, - 0x1b45, 0x1b4b, - 0x1d00, 0x1dbf, - 0x1e00, 0x1e9b, - 0x1ea0, 0x1ef9, - 0x1f00, 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, 0x2094, - 0x210a, 0x2113, - 0x2119, 0x211d, - 0x212a, 0x212d, - 0x212f, 0x2139, - 0x213c, 0x213f, - 0x2145, 0x2149, - 0x2183, 0x2184, - 0x2c00, 0x2c2e, - 0x2c30, 0x2c5e, - 0x2c60, 0x2c6c, - 0x2c74, 0x2c77, - 0x2c80, 0x2ce4, - 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, 0x312c, - 0x3131, 0x318e, - 0x31a0, 0x31b7, - 0x31f0, 0x31ff, - 0x3400, 0x4db5, - 0x4e00, 0x9fbb, - 0xa000, 0xa48c, - 0xa717, 0xa71a, - 0xa800, 0xa801, - 0xa803, 0xa805, - 0xa807, 0xa80a, - 0xa80c, 0xa822, - 0xa840, 0xa873, - 0xac00, 0xd7a3, - 0xf900, 0xfa2d, - 0xfa30, 0xfa6a, - 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, - 0x10300, 0x1031e, - 0x10330, 0x10340, - 0x10342, 0x10349, - 0x10380, 0x1039d, - 0x103a0, 0x103c3, - 0x103c8, 0x103cf, - 0x10400, 0x1049d, - 0x10800, 0x10805, - 0x1080a, 0x10835, - 0x10837, 0x10838, - 0x10900, 0x10915, - 0x10a10, 0x10a13, - 0x10a15, 0x10a17, - 0x10a19, 0x10a33, - 0x12000, 0x1236e, - 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, - 0x2f800, 0x2fa1d, -}; - -static Rune __isalphas[] = { - 0x00aa, - 0x00b5, - 0x00ba, - 0x02ee, - 0x0386, - 0x038c, - 0x0559, - 0x06d5, - 0x06ff, - 0x0710, - 0x07b1, - 0x07fa, - 0x093d, - 0x0950, - 0x09b2, - 0x09bd, - 0x09ce, - 0x0a5e, - 0x0abd, - 0x0ad0, - 0x0b3d, - 0x0b71, - 0x0b83, - 0x0b9c, - 0x0cbd, - 0x0cde, - 0x0dbd, - 0x0e84, - 0x0e8a, - 0x0e8d, - 0x0ea5, - 0x0ea7, - 0x0ebd, - 0x0ec6, - 0x0f00, - 0x10fc, - 0x1258, - 0x12c0, - 0x17d7, - 0x17dc, - 0x1f59, - 0x1f5b, - 0x1f5d, - 0x1fbe, - 0x2071, - 0x207f, - 0x2102, - 0x2107, - 0x2115, - 0x2124, - 0x2126, - 0x2128, - 0x214e, - 0x2d6f, - 0xfb1d, - 0xfb3e, - 0x10808, - 0x1083c, - 0x1083f, - 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, - 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, - 0x03d8, 0x03ee, - 0x0460, 0x0480, - 0x048a, 0x04be, - 0x04c3, 0x04cd, - 0x04d0, 0x0512, - 0x1e00, 0x1e94, - 0x1ea0, 0x1ef8, - 0x1f59, 0x1f5f, - 0x2124, 0x2128, - 0x2c67, 0x2c6b, - 0x2c80, 0x2ce2, -}; - -static Rune __isuppers[] = { - 0x0184, - 0x01a9, - 0x01ac, - 0x01b5, - 0x01bc, - 0x01c4, - 0x01c7, - 0x01ca, - 0x01f1, - 0x01f4, - 0x0241, - 0x0386, - 0x038c, - 0x03f4, - 0x03f7, - 0x2102, - 0x2107, - 0x2115, - 0x2145, - 0x2183, - 0x2c60, - 0x2c75, - 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, 0x1e9b, - 0x1f00, 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, - 0x2c76, 0x2c77, - 0x2ce3, 0x2ce4, - 0x2d00, 0x2d25, - 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, - 0x03d9, 0x03ed, - 0x0461, 0x0481, - 0x048b, 0x04bf, - 0x04c2, 0x04cc, - 0x04d1, 0x0513, - 0x1e01, 0x1e93, - 0x1ea1, 0x1ef9, - 0x2c68, 0x2c6c, - 0x2c81, 0x2ce1, -}; - -static Rune __islowers[] = { - 0x00aa, - 0x00b5, - 0x00ba, - 0x0188, - 0x0192, - 0x0195, - 0x019e, - 0x01a8, - 0x01ad, - 0x01b0, - 0x01c6, - 0x01c9, - 0x023c, - 0x0242, - 0x0390, - 0x03f5, - 0x03f8, - 0x1fbe, - 0x2071, - 0x207f, - 0x210a, - 0x2113, - 0x212f, - 0x2134, - 0x2139, - 0x214e, - 0x2184, - 0x2c61, - 0x2c74, - 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, - 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, - 0x03d8, 0x03ee, - 0x0460, 0x0480, - 0x048a, 0x04be, - 0x04c3, 0x04cd, - 0x04d0, 0x0512, - 0x1e00, 0x1e94, - 0x1ea0, 0x1ef8, - 0x1f59, 0x1f5f, - 0x2c67, 0x2c6b, - 0x2c80, 0x2ce2, -}; - -static Rune __istitles[] = { - 0x0184, - 0x01a9, - 0x01ac, - 0x01b5, - 0x01bc, - 0x01c5, - 0x01c8, - 0x0241, - 0x0386, - 0x038c, - 0x03f7, - 0x2132, - 0x2183, - 0x2c60, - 0x2c75, -}; - -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, - 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, - 0x03d9, 0x03ef, 1048575, - 0x0461, 0x0481, 1048575, - 0x048b, 0x04bf, 1048575, - 0x04c2, 0x04ce, 1048575, - 0x04d1, 0x0513, 1048575, - 0x1e01, 0x1e95, 1048575, - 0x1ea1, 0x1ef9, 1048575, - 0x1f51, 0x1f57, 1048584, - 0x2c68, 0x2c6c, 1048575, - 0x2c81, 0x2ce3, 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, - 0x0253, 1048366, - 0x0254, 1048370, - 0x0259, 1048374, - 0x025b, 1048373, - 0x0260, 1048371, - 0x0263, 1048369, - 0x0268, 1048367, - 0x0269, 1048365, - 0x026b, 1059319, - 0x026f, 1048365, - 0x0272, 1048363, - 0x0275, 1048362, - 0x027d, 1059303, - 0x0280, 1048358, - 0x0283, 1048358, - 0x0288, 1048358, - 0x0289, 1048507, - 0x028c, 1048505, - 0x0292, 1048357, - 0x0345, 1048660, - 0x03ac, 1048538, - 0x03c2, 1048545, - 0x03cc, 1048512, - 0x03d0, 1048514, - 0x03d1, 1048519, - 0x03d5, 1048529, - 0x03d6, 1048522, - 0x03f0, 1048490, - 0x03f1, 1048496, - 0x03f2, 1048583, - 0x03f5, 1048480, - 0x03f8, 1048575, - 0x03fb, 1048575, - 0x04cf, 1048561, - 0x1d7d, 1052390, - 0x1e9b, 1048517, - 0x1fb3, 1048585, - 0x1fbe, 1041371, - 0x1fc3, 1048585, - 0x1fe5, 1048583, - 0x1ff3, 1048585, - 0x214e, 1048548, - 0x2184, 1048575, - 0x2c61, 1048575, - 0x2c65, 1037781, - 0x2c66, 1037784, - 0x2c76, 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, - 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, - 0x03d8, 0x03ee, 1048577, - 0x0460, 0x0480, 1048577, - 0x048a, 0x04be, 1048577, - 0x04c3, 0x04cd, 1048577, - 0x04d0, 0x0512, 1048577, - 0x1e00, 0x1e94, 1048577, - 0x1ea0, 0x1ef8, 1048577, - 0x1f59, 0x1f5f, 1048568, - 0x2c67, 0x2c6b, 1048577, - 0x2c80, 0x2ce2, 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, - 0x0386, 1048614, - 0x038c, 1048640, - 0x03f4, 1048516, - 0x03f7, 1048577, - 0x03f9, 1048569, - 0x03fa, 1048577, - 0x04c0, 1048591, - 0x04c1, 1048577, - 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, - 0x2c75, 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, - 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, - 0x03d9, 0x03ef, 1048575, - 0x0461, 0x0481, 1048575, - 0x048b, 0x04bf, 1048575, - 0x04c2, 0x04ce, 1048575, - 0x04d1, 0x0513, 1048575, - 0x1e01, 0x1e95, 1048575, - 0x1ea1, 0x1ef9, 1048575, - 0x1f51, 0x1f57, 1048584, - 0x2c68, 0x2c6c, 1048575, - 0x2c81, 0x2ce3, 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, - 0x0253, 1048366, - 0x0254, 1048370, - 0x0259, 1048374, - 0x025b, 1048373, - 0x0260, 1048371, - 0x0263, 1048369, - 0x0268, 1048367, - 0x0269, 1048365, - 0x026b, 1059319, - 0x026f, 1048365, - 0x0272, 1048363, - 0x0275, 1048362, - 0x027d, 1059303, - 0x0280, 1048358, - 0x0283, 1048358, - 0x0288, 1048358, - 0x0289, 1048507, - 0x028c, 1048505, - 0x0292, 1048357, - 0x0345, 1048660, - 0x03ac, 1048538, - 0x03c2, 1048545, - 0x03cc, 1048512, - 0x03d0, 1048514, - 0x03d1, 1048519, - 0x03d5, 1048529, - 0x03d6, 1048522, - 0x03f0, 1048490, - 0x03f1, 1048496, - 0x03f2, 1048583, - 0x03f5, 1048480, - 0x03f8, 1048575, - 0x03fb, 1048575, - 0x04cf, 1048561, - 0x1d7d, 1052390, - 0x1e9b, 1048517, - 0x1fb3, 1048585, - 0x1fbe, 1041371, - 0x1fc3, 1048585, - 0x1fe5, 1048583, - 0x1ff3, 1048585, - 0x214e, 1048548, - 0x2184, 1048575, - 0x2c61, 1048575, - 0x2c65, 1037781, - 0x2c66, 1037784, - 0x2c76, 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-5.2.0.c b/src/lib9/utf/runetypebody-5.2.0.c deleted file mode 100644 index 4ff66b9d9..000000000 --- a/src/lib9/utf/runetypebody-5.2.0.c +++ /dev/null @@ -1,1541 +0,0 @@ -/* generated automatically by mkrunetype.c from UnicodeData-5.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, 0x19da, - 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, - 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, 0x0525, - 0x0531, 0x0556, - 0x0561, 0x0587, - 0x05d0, 0x05ea, - 0x05f0, 0x05f2, - 0x0621, 0x064a, - 0x066e, 0x066f, - 0x0671, 0x06d3, - 0x06e5, 0x06e6, - 0x06ee, 0x06ef, - 0x06fa, 0x06fc, - 0x0712, 0x072f, - 0x074d, 0x07a5, - 0x07ca, 0x07ea, - 0x07f4, 0x07f5, - 0x0800, 0x0815, - 0x0904, 0x0939, - 0x0958, 0x0961, - 0x0971, 0x0972, - 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, - 0x0d05, 0x0d0c, - 0x0d0e, 0x0d10, - 0x0d12, 0x0d28, - 0x0d2a, 0x0d39, - 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, 0x0f8b, - 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, - 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, 0x2094, - 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, 0x31b7, - 0x31f0, 0x31ff, - 0x3400, 0x4db5, - 0x4e00, 0x9fcb, - 0xa000, 0xa48c, - 0xa4d0, 0xa4fd, - 0xa500, 0xa60c, - 0xa610, 0xa61f, - 0xa62a, 0xa62b, - 0xa640, 0xa65f, - 0xa662, 0xa66e, - 0xa67f, 0xa697, - 0xa6a0, 0xa6e5, - 0xa717, 0xa71f, - 0xa722, 0xa788, - 0xa78b, 0xa78c, - 0xa7fb, 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, - 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, - 0x11083, 0x110af, - 0x12000, 0x1236e, - 0x13000, 0x1342e, - 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, - 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, - 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, 0x0524, - 0x1e00, 0x1e94, - 0x1e9e, 0x1efe, - 0x1f59, 0x1f5f, - 0x2124, 0x2128, - 0x2c67, 0x2c6b, - 0x2c82, 0x2ce2, - 0x2ceb, 0x2ced, - 0xa640, 0xa65e, - 0xa662, 0xa66c, - 0xa680, 0xa696, - 0xa722, 0xa72e, - 0xa732, 0xa76e, - 0xa779, 0xa77b, - 0xa780, 0xa786, -}; - -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, - 0xa78b, - 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, 0x0525, - 0x1e01, 0x1e93, - 0x1e9f, 0x1efd, - 0x2c68, 0x2c6c, - 0x2c81, 0x2ce1, - 0x2cec, 0x2cee, - 0xa641, 0xa65f, - 0xa663, 0xa66d, - 0xa681, 0xa697, - 0xa723, 0xa72d, - 0xa733, 0xa76f, - 0xa77a, 0xa77c, - 0xa77f, 0xa787, -}; - -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, - 0xa78c, - 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, 0x0524, - 0x1e00, 0x1e94, - 0x1ea0, 0x1efe, - 0x1f59, 0x1f5f, - 0x2c67, 0x2c6b, - 0x2c82, 0x2ce2, - 0x2ceb, 0x2ced, - 0xa640, 0xa65e, - 0xa662, 0xa66c, - 0xa680, 0xa696, - 0xa722, 0xa72e, - 0xa732, 0xa76e, - 0xa779, 0xa77b, - 0xa780, 0xa786, -}; - -static Rune __istitles[] = { - 0x0184, - 0x01a9, - 0x01ac, - 0x01b5, - 0x01bc, - 0x01c5, - 0x01c8, - 0x0241, - 0x0376, - 0x0386, - 0x038c, - 0x03cf, - 0x03f7, - 0x2132, - 0x2183, - 0x2c60, - 0x2c72, - 0x2c75, - 0xa78b, -}; - -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, 0x0525, 1048575, - 0x1e01, 0x1e95, 1048575, - 0x1ea1, 0x1eff, 1048575, - 0x1f51, 0x1f57, 1048584, - 0x2c68, 0x2c6c, 1048575, - 0x2c81, 0x2ce3, 1048575, - 0x2cec, 0x2cee, 1048575, - 0xa641, 0xa65f, 1048575, - 0xa663, 0xa66d, 1048575, - 0xa681, 0xa697, 1048575, - 0xa723, 0xa72f, 1048575, - 0xa733, 0xa76f, 1048575, - 0xa77a, 0xa77c, 1048575, - 0xa77f, 0xa787, 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, - 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, -}; - -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, 0x0524, 1048577, - 0x1e00, 0x1e94, 1048577, - 0x1ea0, 0x1efe, 1048577, - 0x1f59, 0x1f5f, 1048568, - 0x2c67, 0x2c6b, 1048577, - 0x2c80, 0x2ce2, 1048577, - 0x2ceb, 0x2ced, 1048577, - 0xa640, 0xa65e, 1048577, - 0xa662, 0xa66c, 1048577, - 0xa680, 0xa696, 1048577, - 0xa722, 0xa72e, 1048577, - 0xa732, 0xa76e, 1048577, - 0xa779, 0xa77b, 1048577, - 0xa780, 0xa786, 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, -}; - -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, 0x0525, 1048575, - 0x1e01, 0x1e95, 1048575, - 0x1ea1, 0x1eff, 1048575, - 0x1f51, 0x1f57, 1048584, - 0x2c68, 0x2c6c, 1048575, - 0x2c81, 0x2ce3, 1048575, - 0x2cec, 0x2cee, 1048575, - 0xa641, 0xa65f, 1048575, - 0xa663, 0xa66d, 1048575, - 0xa681, 0xa697, 1048575, - 0xa723, 0xa72f, 1048575, - 0xa733, 0xa76f, 1048575, - 0xa77a, 0xa77c, 1048575, - 0xa77f, 0xa787, 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, - 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, -}; - -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.0.0.c b/src/lib9/utf/runetypebody-6.0.0.c deleted file mode 100644 index 47c0faf73..000000000 --- a/src/lib9/utf/runetypebody-6.0.0.c +++ /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.0.0.h b/src/lib9/utf/runetypebody-6.0.0.h new file mode 100644 index 000000000..47c0faf73 --- /dev/null +++ b/src/lib9/utf/runetypebody-6.0.0.h @@ -0,0 +1,1565 @@ +/* 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/win32.c b/src/lib9/win32.c deleted file mode 100644 index 90753bb8d..000000000 --- a/src/lib9/win32.c +++ /dev/null @@ -1,26 +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 -#include - -int fork() -{ - return -1; -} - -int p9rfork(int flags) -{ - return -1; -} - -Waitmsg *p9wait() -{ - return 0; -} - -int p9waitpid() -{ - return -1; -} diff --git a/src/lib9/windows.c b/src/lib9/windows.c new file mode 100644 index 000000000..90753bb8d --- /dev/null +++ b/src/lib9/windows.c @@ -0,0 +1,26 @@ +// 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 + +int fork() +{ + return -1; +} + +int p9rfork(int flags) +{ + return -1; +} + +Waitmsg *p9wait() +{ + return 0; +} + +int p9waitpid() +{ + return -1; +} diff --git a/src/libbio/Makefile b/src/libbio/Makefile index 4340b0eae..62aba5dca 100644 --- a/src/libbio/Makefile +++ b/src/libbio/Makefile @@ -1,51 +1,5 @@ -# Derived from http://code.google.com/p/inferno-os/source/browse/libbio/mkfile -# -# 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. +# 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.inc -O:=$(HOST_O) - -LIB=libbio.a - -OFILES=\ - bbuffered.$O\ - bfildes.$O\ - bflush.$O\ - bgetc.$O\ - bgetrune.$O\ - bgetd.$O\ - binit.$O\ - boffset.$O\ - bprint.$O\ - bputc.$O\ - bputrune.$O\ - brdline.$O\ - brdstr.$O\ - bread.$O\ - bseek.$O\ - bwrite.$O\ - -HFILES=\ - ../../include/bio.h - -include ../Make.clib +include ../Make.dist diff --git a/src/libmach/5db.c b/src/libmach/5db.c index aea391edf..ae71dd90d 100644 --- a/src/libmach/5db.c +++ b/src/libmach/5db.c @@ -307,7 +307,7 @@ gsymoff(char *buf, int n, long v, int space) if (!delta) return snprint(buf, n, "%s", s.name); if (s.type != 't' && s.type != 'T') - return snprint(buf, n, "%s+%lux", s.name, v-s.value); + return snprint(buf, n, "%s+%llux", s.name, v-s.value); else return snprint(buf, n, "#%lux", v); } diff --git a/src/libmach/8db.c b/src/libmach/8db.c index 5b3de69a5..ce1b4ddd7 100644 --- a/src/libmach/8db.c +++ b/src/libmach/8db.c @@ -1,11 +1,11 @@ // Inferno libmach/8db.c // http://code.google.com/p/inferno-os/source/browse/utils/libmach/8db.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 @@ -125,7 +125,7 @@ i386excep(Map *map, Rgetter rget) if (memcmp(buf, machdata->bpinst, machdata->bpsize) == 0) return "breakpoint"; } - snprint(buf, sizeof(buf), "exception %ld", c); + snprint(buf, sizeof(buf), "exception %d", c); return buf; } else return excname[c]; @@ -688,6 +688,7 @@ static Optable optab0F[256]= [0x74] = { RM,0, "PCMPEQB %m,%M" }, [0x75] = { RM,0, "PCMPEQW %m,%M" }, [0x76] = { RM,0, "PCMPEQL %m,%M" }, +[0x77] = { 0,0, "EMMS" }, [0x7E] = { RM,0, "MOV%S %M,%e" }, [0x7F] = { RM,0, "MOVQ %M,%m" }, [0xAE] = { RMOP,0, optab0FAE }, @@ -1971,7 +1972,7 @@ plocal(Instr *ip) offset = ip->disp; if (!findsym(ip->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s)) { - bprint(ip, "%lux(SP)", offset); + bprint(ip, "%ux(SP)", offset); return; } @@ -1987,7 +1988,7 @@ plocal(Instr *ip) bprint(ip, "%s+", s.name); else offset = ip->disp; - bprint(ip, "%lux%s", offset, reg); + bprint(ip, "%ux%s", offset, reg); } static int @@ -2061,7 +2062,7 @@ immediate(Instr *ip, vlong val) w = -w; if (issymref(ip, &s, w, val)) { if (w) - bprint(ip, "%s+%#lux(SB)", s.name, w); + bprint(ip, "%s+%#ux(SB)", s.name, w); else bprint(ip, "%s(SB)", s.name); return; @@ -2087,24 +2088,29 @@ immediate(Instr *ip, vlong val) static void pea(Instr *ip) { + int base; + + base = ip->base; + if(base >= 0 && (ip->rex & REXB)) + base += 8; + if (ip->mod == 3) { if (ip->osize == 'B') bprint(ip, (ip->rex & REXB? breg64: breg)[(uchar)ip->base]); - else if(ip->rex & REXB) - bprint(ip, "%s%s", ANAME(ip), reg[ip->base+8]); else - bprint(ip, "%s%s", ANAME(ip), reg[(uchar)ip->base]); + bprint(ip, "%s%s", ANAME(ip), reg[base]); return; } + if (ip->segment) bprint(ip, ip->segment); - if (ip->asize == 'E' && ip->base == SP) + if (ip->asize == 'E' && base == SP) plocal(ip); else { if (ip->base < 0) immediate(ip, ip->disp); else { - bprint(ip, "%lux", ip->disp); + bprint(ip, "%ux", ip->disp); if(ip->rip) bprint(ip, "(RIP)"); bprint(ip,"(%s%s)", ANAME(ip), reg[ip->rex&REXB? ip->base+8: ip->base]); @@ -2197,7 +2203,7 @@ prinstr(Instr *ip, char *fmt) bprint(ip, "CBW"); break; case 'd': - bprint(ip,"%ux:%lux",ip->seg,ip->disp); + bprint(ip,"%ux:%ux", ip->seg, ip->disp); break; case 'm': if (ip->mod == 3 && ip->osize != 'B') { diff --git a/src/libmach/Makefile b/src/libmach/Makefile index 2a53749c8..62aba5dca 100644 --- a/src/libmach/Makefile +++ b/src/libmach/Makefile @@ -1,64 +1,5 @@ -# Derived from Inferno libmach/mkfile -# http://code.google.com/p/inferno-os/source/browse/utils/libmach/mkfile -# -# 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 -# 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. +# 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.inc -O:=$(HOST_O) - -LIB=libmach.a -OFILES=\ - executable.$O\ - fakeobj.$O\ - map.$O\ - obj.$O\ - swap.$O\ - sym.$O\ - access.$O\ - machdata.$O\ - setmach.$O\ - 5.$O\ - 6.$O\ - 8.$O\ - 5db.$O\ - 8db.$O\ - 5obj.$O\ - 6obj.$O\ - 8obj.$O\ - -ifneq ($(GOHOSTOS),windows) -OFILES+=\ - $(shell uname | tr A-Z a-z).$O\ - -else -OFILES+=\ - windows.$O\ - -endif - -HFILES=../../include/mach.h elf.h macho.h obj.h - -include ../Make.clib +include ../Make.dist diff --git a/src/libmach/executable.c b/src/libmach/executable.c index e90334438..3db3e7da4 100644 --- a/src/libmach/executable.c +++ b/src/libmach/executable.c @@ -66,6 +66,7 @@ static int adotout(int, Fhdr*, ExecHdr*); static int elfdotout(int, Fhdr*, ExecHdr*); static int machdotout(int, Fhdr*, ExecHdr*); static int armdotout(int, Fhdr*, ExecHdr*); +static int pedotout(int, Fhdr*, ExecHdr*); static void setsym(Fhdr*, vlong, int32, vlong, int32, vlong, int32); static void setdata(Fhdr*, uvlong, int32, vlong, int32); static void settext(Fhdr*, uvlong, uvlong, int32, vlong); @@ -312,6 +313,15 @@ ExecTable exectab[] = sizeof(Exec), beswal, common }, + { 0x4d5a9000, /* see dosstub[] in pe.c */ + "windows PE executable", + nil, + FWINPE, + 0, + &mi386, + sizeof(Exec), /* TODO */ + nil, + pedotout }, { 0 }, }; @@ -502,6 +512,8 @@ commonllp64(int unused, Fhdr *fp, ExecHdr *hp) int32 pgsize; uvlong entry; + USED(unused); + hswal(&hp->e, sizeof(Exec)/sizeof(int32), beswal); if(!(hp->e.exechdr.magic & HDR_MAGIC)) return 0; @@ -542,6 +554,10 @@ commonllp64(int unused, Fhdr *fp, ExecHdr *hp) static int mipsboot(int fd, Fhdr *fp, ExecHdr *hp) { + USED(fd); + USED(fp); + USED(hp); + abort(); #ifdef unused USED(fd); @@ -573,6 +589,10 @@ abort(); static int mips4kboot(int fd, Fhdr *fp, ExecHdr *hp) { + USED(fd); + USED(fp); + USED(hp); + abort(); #ifdef unused USED(fd); @@ -604,6 +624,10 @@ abort(); static int sparcboot(int fd, Fhdr *fp, ExecHdr *hp) { + USED(fd); + USED(fp); + USED(hp); + abort(); #ifdef unused USED(fd); @@ -624,6 +648,10 @@ abort(); static int nextboot(int fd, Fhdr *fp, ExecHdr *hp) { + USED(fd); + USED(fp); + USED(hp); + abort(); #ifdef unused USED(fd); @@ -645,12 +673,11 @@ abort(); static int elf64dotout(int fd, Fhdr *fp, ExecHdr *hp) { - uvlong (*swav)(uvlong); uint32 (*swal)(uint32); ushort (*swab)(ushort); Ehdr64 *ep; - Phdr64 *ph; + Phdr64 *ph, *pph; Shdr64 *sh; int i, it, id, is, phsz, shsz; @@ -770,7 +797,8 @@ elf64dotout(int fd, Fhdr *fp, ExecHdr *hp) } settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset); - setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - ph[id].filesz); + pph = ph + id; + setdata(fp, pph->vaddr, pph->filesz, pph->offset, pph->memsz - pph->filesz); if(is != -1) setsym(fp, ph[is].offset, ph[is].filesz, 0, 0, 0, ph[is].memsz); else if(sh != 0){ @@ -784,7 +812,7 @@ elf64dotout(int fd, Fhdr *fp, ExecHdr *hp) buf = malloc(sh[ep->shstrndx].size); if (buf == 0) goto done; - memset(buf, 0, sizeof buf); + memset(buf, 0, sh[ep->shstrndx].size); seek(fd, sh[ep->shstrndx].offset, 0); i = read(fd, buf, sh[ep->shstrndx].size); USED(i); // shut up ubuntu gcc @@ -962,7 +990,7 @@ elfdotout(int fd, Fhdr *fp, ExecHdr *hp) buf = malloc(sh[ep->shstrndx].size); if (buf == 0) goto done; - memset(buf, 0, sizeof buf); + memset(buf, 0, sh[ep->shstrndx].size); seek(fd, sh[ep->shstrndx].offset, 0); i = read(fd, buf, sh[ep->shstrndx].size); USED(i); // shut up ubuntu gcc @@ -1022,7 +1050,6 @@ machdotout(int fd, Fhdr *fp, ExecHdr *hp) mp->sizeofcmds = swal(mp->sizeofcmds); mp->flags = swal(mp->flags); mp->reserved = swal(mp->reserved); - hdrsize = 0; switch(mp->magic) { case 0xFEEDFACE: // 32-bit mach @@ -1077,7 +1104,9 @@ machdotout(int fd, Fhdr *fp, ExecHdr *hp) datava = 0; symtab = 0; pclntab = 0; - textsize = datasize = bsssize = 0; + textsize = 0; + datasize = 0; + bsssize = 0; for (i = 0; i < mp->ncmds; i++) { MachCmd *c; @@ -1232,6 +1261,165 @@ armdotout(int fd, Fhdr *fp, ExecHdr *hp) return 1; } +/* + * Structures needed to parse PE image. + */ +typedef struct { + uint16 Machine; + uint16 NumberOfSections; + uint32 TimeDateStamp; + uint32 PointerToSymbolTable; + uint32 NumberOfSymbols; + uint16 SizeOfOptionalHeader; + uint16 Characteristics; +} IMAGE_FILE_HEADER; + +typedef struct { + uint8 Name[8]; + uint32 VirtualSize; + uint32 VirtualAddress; + uint32 SizeOfRawData; + uint32 PointerToRawData; + uint32 PointerToRelocations; + uint32 PointerToLineNumbers; + uint16 NumberOfRelocations; + uint16 NumberOfLineNumbers; + uint32 Characteristics; +} IMAGE_SECTION_HEADER; + +typedef struct { + uint32 VirtualAddress; + uint32 Size; +} IMAGE_DATA_DIRECTORY; + +typedef struct { + uint16 Magic; + uint8 MajorLinkerVersion; + uint8 MinorLinkerVersion; + uint32 SizeOfCode; + uint32 SizeOfInitializedData; + uint32 SizeOfUninitializedData; + uint32 AddressOfEntryPoint; + uint32 BaseOfCode; + uint32 BaseOfData; + uint32 ImageBase; + uint32 SectionAlignment; + uint32 FileAlignment; + uint16 MajorOperatingSystemVersion; + uint16 MinorOperatingSystemVersion; + uint16 MajorImageVersion; + uint16 MinorImageVersion; + uint16 MajorSubsystemVersion; + uint16 MinorSubsystemVersion; + uint32 Win32VersionValue; + uint32 SizeOfImage; + uint32 SizeOfHeaders; + uint32 CheckSum; + uint16 Subsystem; + uint16 DllCharacteristics; + uint32 SizeOfStackReserve; + uint32 SizeOfStackCommit; + uint32 SizeOfHeapReserve; + uint32 SizeOfHeapCommit; + uint32 LoaderFlags; + uint32 NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; +} IMAGE_OPTIONAL_HEADER; + +static int +match8(void *buf, char *cmp) +{ + return strncmp((char*)buf, cmp, 8) == 0; +} + +/* TODO(czaplinski): 64b windows? */ +/* + * Read from Windows PE/COFF .exe file image. + */ +static int +pedotout(int fd, Fhdr *fp, ExecHdr *hp) +{ + uint32 start, magic; + uint32 symtab, esymtab; + IMAGE_FILE_HEADER fh; + IMAGE_SECTION_HEADER sh; + IMAGE_OPTIONAL_HEADER oh; + uint8 sym[18]; + uint32 *valp; + int i; + + USED(hp); + seek(fd, 0x3c, 0); + if (readn(fd, &start, sizeof(start)) != sizeof(start)) { + werrstr("crippled PE MSDOS header"); + return 0; + } + start = leswal(start); + + seek(fd, start, 0); + if (readn(fd, &magic, sizeof(magic)) != sizeof(magic)) { + werrstr("no PE magic number found"); + return 0; + } + if (beswal(magic) != 0x50450000) { /* "PE\0\0" */ + werrstr("incorrect PE magic number"); + return 0; + } + + if (readn(fd, &fh, sizeof(fh)) != sizeof(fh)) { + werrstr("crippled PE File Header"); + return 0; + } + if (fh.PointerToSymbolTable == 0) { + werrstr("zero pointer to COFF symbol table"); + return 0; + } + + if (readn(fd, &oh, sizeof(oh)) != sizeof(oh)) { + werrstr("crippled PE Optional Header"); + return 0; + } + + seek(fd, start+sizeof(magic)+sizeof(fh)+leswab(fh.SizeOfOptionalHeader), 0); + fp->txtaddr = 0; + fp->dataddr = 0; + for (i=0; itxtaddr==0 || fp->dataddr==0) { + werrstr("no .text or .data"); + return 0; + } + + seek(fd, leswal(fh.PointerToSymbolTable), 0); + symtab = esymtab = 0; + for (i=0; ilnpcsz = lnpcsz; } - static uvlong _round(uvlong a, uint32 b) { diff --git a/src/libmach/fakeobj.c b/src/libmach/fakeobj.c index ea7ef012c..a4a897cfe 100644 --- a/src/libmach/fakeobj.c +++ b/src/libmach/fakeobj.c @@ -13,17 +13,17 @@ #include #include "obj.h" -int _is2(char* x) { return 0; } -int _is7(char* x) { return 0; } -int _is9(char* x) { return 0; } -int _isk(char* x) { return 0; } -int _isq(char* x) { return 0; } -int _isv(char* x) { return 0; } -int _isu(char* x) { return 0; } -int _read2(Biobuf* b, Prog* p) { return 0; } -int _read7(Biobuf* b, Prog* p) { return 0; } -int _read9(Biobuf* b, Prog* p) { return 0; } -int _readk(Biobuf* b, Prog* p) { return 0; } -int _readq(Biobuf* b, Prog* p) { return 0; } -int _readv(Biobuf* b, Prog* p) { return 0; } -int _readu(Biobuf* b, Prog* p) { return 0; } +int _is2(char* x) { USED(x); return 0; } +int _is7(char* x) { USED(x); return 0; } +int _is9(char* x) { USED(x); return 0; } +int _isk(char* x) { USED(x); return 0; } +int _isq(char* x) { USED(x); return 0; } +int _isv(char* x) { USED(x); return 0; } +int _isu(char* x) { USED(x); return 0; } +int _read2(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; } +int _read7(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; } +int _read9(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; } +int _readk(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; } +int _readq(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; } +int _readv(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; } +int _readu(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; } diff --git a/src/libmach/linux.c b/src/libmach/linux.c index 6ce18957f..2c143266a 100644 --- a/src/libmach/linux.c +++ b/src/libmach/linux.c @@ -807,6 +807,8 @@ ptraceerr: static int ptracesegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr) { + USED(seg); + return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA, isr, map->pid, addr, v, n); } @@ -937,6 +939,8 @@ ptraceregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr) { int laddr; uvlong u; + + USED(seg); if((laddr = go2linux(addr)) < 0){ if(isr){ diff --git a/src/libmach/machdata.c b/src/libmach/machdata.c index 425a92179..66c19f953 100644 --- a/src/libmach/machdata.c +++ b/src/libmach/machdata.c @@ -113,7 +113,7 @@ symoff(char *buf, int n, uvlong v, int space) if (s.type != 't' && s.type != 'T' && delta >= 4096) return snprint(buf, n, "%llux", v); else if (delta) - return snprint(buf, n, "%s+%#lux", s.name, delta); + return snprint(buf, n, "%s+%#ux", s.name, delta); else return snprint(buf, n, "%s", s.name); } @@ -139,7 +139,7 @@ fpformat(Map *map, Reglist *rp, char *buf, int n, int modif) case 'X': if (get4(map, rp->roffs, &r) < 0) return -1; - snprint(buf, n, "%lux", r); + snprint(buf, n, "%ux", r); break; case 'F': /* first reg of double reg pair */ if (modif == 'F') @@ -219,12 +219,12 @@ ieeedftos(char *buf, int n, uint32 h, uint32 l) return snprint(buf, n, "0."); exp = (h>>20) & ((1L<<11)-1L); if(exp == 0) - return snprint(buf, n, "DeN(%.8lux%.8lux)", h, l); + return snprint(buf, n, "DeN(%.8ux%.8ux)", h, l); if(exp == ((1L<<11)-1L)){ if(l==0 && (h&((1L<<20)-1L)) == 0) return snprint(buf, n, "Inf"); else - return snprint(buf, n, "NaN(%.8lux%.8lux)", h&((1L<<20)-1L), l); + return snprint(buf, n, "NaN(%.8ux%.8ux)", h&((1<<20)-1), l); } exp -= (1L<<10) - 2L; fr = l & ((1L<<16)-1L); @@ -256,7 +256,7 @@ ieeesftos(char *buf, int n, uint32 h) return snprint(buf, n, "0."); exp = (h>>23) & ((1L<<8)-1L); if(exp == 0) - return snprint(buf, n, "DeN(%.8lux)", h); + return snprint(buf, n, "DeN(%.8ux)", h); if(exp == ((1L<<8)-1L)){ if((h&((1L<<23)-1L)) == 0) return snprint(buf, n, "Inf"); diff --git a/src/libmach/map.c b/src/libmach/map.c index ebfe03702..cd5ef0985 100644 --- a/src/libmach/map.c +++ b/src/libmach/map.c @@ -137,6 +137,8 @@ int fdrw(Map *map, Seg *s, uvlong addr, void *v, uint n, int isread) { int tot, m; + + USED(map); for(tot=0; tot +#include +#include +#include + +int +ctlproc(int pid, char *msg) +{ + sysfatal("ctlproc unimplemented in NetBSD"); + return -1; +} + +char* +proctextfile(int pid) +{ + sysfatal("proctextfile unimplemented in NetBSD"); + return nil; +} + +char* +procstatus(int pid) +{ + sysfatal("procstatus unimplemented in NetBSD"); + return nil; +} + +Map* +attachproc(int pid, Fhdr *fp) +{ + sysfatal("attachproc unimplemented in NetBSD"); + return nil; +} + +void +detachproc(Map *m) +{ + sysfatal("detachproc unimplemented in NetBSD"); +} + +int +procthreadpids(int pid, int *p, int np) +{ + sysfatal("procthreadpids unimplemented in NetBSD"); + return -1; +} diff --git a/src/libmach/plan9.c b/src/libmach/plan9.c new file mode 100644 index 000000000..59e2649d0 --- /dev/null +++ b/src/libmach/plan9.c @@ -0,0 +1,72 @@ +// This is stubbed out for the moment. Will revisit when the time comes. +#include +#include +#include +#include + +int +ctlproc(int pid, char *msg) +{ + USED(pid); + USED(msg); + + sysfatal("ctlproc unimplemented on Plan 9"); + return -1; +} + +char* +proctextfile(int pid) +{ + USED(pid); + + sysfatal("proctextfile unimplemented on Plan 9"); + return nil; +} + +char* +procstatus(int pid) +{ + USED(pid); + + sysfatal("procstatus unimplemented on Plan 9"); + return nil; +} + +Map* +attachproc(int pid, Fhdr *fp) +{ + USED(pid); + USED(fp); + + sysfatal("attachproc unimplemented on Plan 9"); + return nil; +} + +void +detachproc(Map *m) +{ + USED(m); + + sysfatal("detachproc unimplemented on Plan 9"); +} + +int +procthreadpids(int pid, int *p, int np) +{ + USED(pid); + USED(p); + USED(np); + + sysfatal("procthreadpids unimplemented on Plan 9"); + return -1; +} + +int +nanosleep(const struct timespec *rqtp, struct timespec *rmtp) +{ + USED(rqtp); + USED(rmtp); + + sysfatal("nanosleep unimplemented on Plan 9"); + return -1; +} diff --git a/src/libmach/sym.c b/src/libmach/sym.c index 5e4fdd867..1512d7a4f 100644 --- a/src/libmach/sym.c +++ b/src/libmach/sym.c @@ -124,7 +124,7 @@ syminit(int fd, Fhdr *fp) /* minimum symbol record size = 4+1+2 bytes */ symbols = malloc((fp->symsz/(4+1+2)+1)*sizeof(Sym)); if(symbols == 0) { - werrstr("can't malloc %ld bytes", fp->symsz); + werrstr("can't malloc %d bytes", fp->symsz); return -1; } Binit(&b, fd, OREAD); @@ -203,11 +203,11 @@ syminit(int fd, Fhdr *fp) } } if (debug) - print("NG: %ld NT: %d NF: %d\n", nglob, ntxt, fmaxi); + print("NG: %d NT: %d NF: %d\n", nglob, ntxt, fmaxi); if (fp->sppcsz) { /* pc-sp offset table */ spoff = (uchar *)malloc(fp->sppcsz); if(spoff == 0) { - werrstr("can't malloc %ld bytes", fp->sppcsz); + werrstr("can't malloc %d bytes", fp->sppcsz); return -1; } Bseek(&b, fp->sppcoff, 0); @@ -220,7 +220,7 @@ syminit(int fd, Fhdr *fp) if (fp->lnpcsz) { /* pc-line number table */ pcline = (uchar *)malloc(fp->lnpcsz); if(pcline == 0) { - werrstr("can't malloc %ld bytes", fp->lnpcsz); + werrstr("can't malloc %d bytes", fp->lnpcsz); return -1; } Bseek(&b, fp->lnpcoff, 0); @@ -280,12 +280,12 @@ decodename(Biobuf *bp, Sym *p) n = Bseek(bp, 0, 1)-o; p->name = malloc(n); if(p->name == 0) { - werrstr("can't malloc %ld bytes", n); + werrstr("can't malloc %d bytes", n); return -1; } Bseek(bp, -n, 1); if(Bread(bp, p->name, n) != n) { - werrstr("can't read %ld bytes of symbol name", n); + werrstr("can't read %d bytes of symbol name", n); return -1; } } else { @@ -297,7 +297,7 @@ decodename(Biobuf *bp, Sym *p) n = Blinelen(bp); p->name = malloc(n); if(p->name == 0) { - werrstr("can't malloc %ld bytes", n); + werrstr("can't malloc %d bytes", n); return -1; } strcpy(p->name, cp); @@ -913,7 +913,7 @@ file2pc(char *file, int32 line) break; free(name); if(i >= nfiles) { - werrstr("line %ld in file %s not found", line, file); + werrstr("line %d in file %s not found", line, file); return ~0; } start = fp->addr; /* first text addr this file */ @@ -926,10 +926,10 @@ file2pc(char *file, int32 line) * run the state machine to locate the pc closest to that value. */ if(debug) - print("find pc for %ld - between: %llux and %llux\n", line, start, end); + print("find pc for %d - between: %llux and %llux\n", line, start, end); pc = line2addr(line, start, end); if(pc == ~0) { - werrstr("line %ld not in file %s", line, file); + werrstr("line %d not in file %s", line, file); return ~0; } return pc; @@ -1146,7 +1146,7 @@ fline(char *str, int n, int32 line, Hist *base, Hist **ret) else { k = fileelem(fnames, (uchar*)start->name, str, n); if(k+8 < n) - sprint(str+k, ":%ld", line); + sprint(str+k, ":%d", line); } /**********Remove comments for complete back-trace of include sequence * if(start != base) { @@ -1404,7 +1404,7 @@ printhist(char *msg, Hist *hp, int count) while(hp->name) { if(count && ++i > count) break; - print("%s Line: %lx (%ld) Offset: %lx (%ld) Name: ", msg, + print("%s Line: %x (%d) Offset: %x (%d) Name: ", msg, hp->line, hp->line, hp->offset, hp->offset); for(cp = (uchar *)hp->name+1; (*cp<<8)|cp[1]; cp += 2) { if (cp != (uchar *)hp->name+1) diff --git a/src/make.bash b/src/make.bash index 84b9908f4..b2de37b72 100755 --- a/src/make.bash +++ b/src/make.bash @@ -3,14 +3,48 @@ # 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.bash: +# +# 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. +# +# 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. + set -e -if [ ! -f env.bash ]; then +if [ ! -f run.bash ]; then echo 'make.bash must be run from $GOROOT/src' 1>&2 exit 1 fi -. ./env.bash -if ld --version 2>&1 | grep 'gold.*2\.20' >/dev/null; then +# Test for Windows. +case "$(uname)" in +*MINGW* | *WIN32* | *CYGWIN*) + echo 'ERROR: Do not use make.bash to build on Windows.' + echo 'Use make.bat instead.' + echo + exit 1 + ;; +esac + +# Test for bad ld. +if ld --version 2>&1 | grep 'gold.* 2\.20' >/dev/null; then echo 'ERROR: Your system has gold 2.20 installed.' echo 'This version is shipped by Ubuntu even though' echo 'it is known not to work on Ubuntu.' @@ -21,90 +55,83 @@ if ld --version 2>&1 | grep 'gold.*2\.20' >/dev/null; then exit 1 fi -# Create target directories -if [ "$GOBIN" = "$GOROOT/bin" ]; then - mkdir -p "$GOROOT/bin" -fi -mkdir -p "$GOROOT/pkg" - -GOROOT_FINAL=${GOROOT_FINAL:-$GOROOT} - -MAKEFLAGS=${MAKEFLAGS:-"-j4"} -export MAKEFLAGS -unset CDPATH # in case user has it set - -rm -f "$GOBIN"/quietgcc -CC=${CC:-gcc} -export CC -sed -e "s|@CC@|$CC|" < "$GOROOT"/src/quietgcc.bash > "$GOBIN"/quietgcc -chmod +x "$GOBIN"/quietgcc - -rm -f "$GOBIN"/gomake -( - echo '#!/bin/sh' - echo 'export GOROOT=${GOROOT:-'$GOROOT_FINAL'}' - echo 'exec '$MAKE' "$@"' -) >"$GOBIN"/gomake -chmod +x "$GOBIN"/gomake - -# TODO(brainman): delete this after 01/01/2012. -rm -f "$GOBIN"/gotest # remove old bash version of gotest on Windows - -if [ -d /selinux -a -f /selinux/booleans/allow_execstack -a -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then - if ! cat /selinux/booleans/allow_execstack | grep -c '^1 1$' >> /dev/null ; then - echo "WARNING: the default SELinux policy on, at least, Fedora 12 breaks " - echo "Go. You can enable the features that Go needs via the following " - echo "command (as root):" - echo " # setsebool -P allow_execstack 1" - echo - echo "Note that this affects your system globally! " - echo - echo "The build will continue in five seconds in case we " - echo "misdiagnosed the issue..." - - sleep 5 +# Test for bad SELinux. +# On Fedora 16 the selinux filesystem is mounted at /sys/fs/selinux, +# so loop through the possible selinux mount points. +for se_mount in /selinux /sys/fs/selinux +do + if [ -d $se_mount -a -f $se_mount/booleans/allow_execstack -a -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then + if ! cat $se_mount/booleans/allow_execstack | grep -c '^1 1$' >> /dev/null ; then + echo "WARNING: the default SELinux policy on, at least, Fedora 12 breaks " + echo "Go. You can enable the features that Go needs via the following " + echo "command (as root):" + echo " # setsebool -P allow_execstack 1" + echo + echo "Note that this affects your system globally! " + echo + echo "The build will continue in five seconds in case we " + echo "misdiagnosed the issue..." + + sleep 5 + fi fi -fi +done -( - cd "$GOROOT"/src/pkg; - bash deps.bash # do this here so clean.bash will work in the pkg directory -) -bash "$GOROOT"/src/clean.bash +# Clean old generated file that will cause problems in the build. +rm -f ./pkg/runtime/runtime_defs.go -# pkg builds libcgo and the Go programs in cmd. -for i in lib9 libbio libmach cmd pkg -do - echo; echo; echo %%%% making $i %%%%; echo - gomake -C $i install -done +# Finally! Run the build. -# Print post-install messages. -# Implemented as a function so that all.bash can repeat the output -# after run.bash finishes running all the tests. -installed() { - eval $(gomake --no-print-directory -f Make.inc go-env) - echo - echo --- - echo Installed Go for $GOOS/$GOARCH in "$GOROOT". - echo Installed commands in "$GOBIN". - case "$OLDPATH" in - "$GOBIN:"* | *":$GOBIN" | *":$GOBIN:"*) - ;; - *) - echo '***' "You need to add $GOBIN to your "'$PATH.' '***' - esac - echo The compiler is $GC. - if [ "$(uname)" = "Darwin" ]; then - echo - echo On OS X the debuggers must be installed setgrp procmod. - echo Read and run ./sudo.bash to install the debuggers. - fi - if [ "$GOROOT_FINAL" != "$GOROOT" ]; then - echo - echo The binaries expect "$GOROOT" to be copied or moved to "$GOROOT_FINAL". +echo '# Building C bootstrap tool.' +echo cmd/dist +export GOROOT="$(cd .. && pwd)" +GOROOT_FINAL="${GOROOT_FINAL:-$GOROOT}" +DEFGOROOT='-DGOROOT_FINAL="'"$GOROOT_FINAL"'"' + +mflag="" +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 + +eval $(./cmd/dist/dist env -p) +echo + +if [ "$1" = "--dist-tool" ]; then + # Stop after building dist tool. + mkdir -p "$GOTOOLDIR" + if [ "$2" != "" ]; then + cp cmd/dist/dist "$2" fi -} + mv cmd/dist/dist "$GOTOOLDIR"/dist + exit 0 +fi + +echo "# Building compilers and Go bootstrap tool for host, $GOHOSTOS/$GOHOSTARCH." +buildall="-a" +if [ "$1" = "--no-clean" ]; then + buildall="" +fi +./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 + +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 + echo +fi + +echo "# Building packages and commands for $GOOS/$GOARCH." +"$GOTOOLDIR"/go_bootstrap install -gcflags "$GO_GCFLAGS" -ldflags "$GO_LDFLAGS" -v std +echo -(installed) # run in sub-shell to avoid polluting environment +rm -f "$GOTOOLDIR"/go_bootstrap +if [ "$1" != "--no-banner" ]; then + "$GOTOOLDIR"/dist banner +fi diff --git a/src/make.bat b/src/make.bat new file mode 100644 index 000000000..ec39392dd --- /dev/null +++ b/src/make.bat @@ -0,0 +1,111 @@ +:: 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.bat: +:: +:: 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. +:: +:: 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. + +@echo off + +:: Keep environment variables within this script +:: unless invoked with --no-local. +if x%1==x--no-local goto nolocal +if x%2==x--no-local goto nolocal +setlocal +:nolocal + +set GOBUILDFAIL=0 + +if exist make.bat goto ok +echo Must run make.bat from Go src directory. +goto fail +:ok + +:: Clean old generated file that will cause problems in the build. +del /F ".\pkg\runtime\runtime_defs.go" 2>NUL + +:: Grab default GOROOT_FINAL and set GOROOT for build. +:: The expression %VAR:\=\\% means to take %VAR% +:: and apply the substitution \ = \\, escaping the +:: backslashes. Then we wrap that in quotes to create +:: a C string. +cd .. +set GOROOT=%CD% +cd src +if "x%GOROOT_FINAL%"=="x" set GOROOT_FINAL=%GOROOT% +set DEFGOROOT=-DGOROOT_FINAL="\"%GOROOT_FINAL:\=\\%\"" + +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 +if errorlevel 1 goto fail +.\cmd\dist\dist env -wp >env.bat +if errorlevel 1 goto fail +call env.bat +del env.bat +echo. + +echo # Building compilers and Go bootstrap tool. +set buildall=-a +if x%1==x--no-clean set buildall= +.\cmd\dist\dist bootstrap %buildall% -v +if errorlevel 1 goto fail +:: Delay move of dist tool to now, because bootstrap cleared tool directory. +move .\cmd\dist\dist.exe "%GOTOOLDIR%\dist.exe" +"%GOTOOLDIR%\go_bootstrap" clean -i std +echo. + +if not %GOHOSTARCH% == %GOARCH% goto localbuild +if not %GOHOSTOS% == %GOOS% goto localbuild +goto mainbuild + +:localbuild +echo # Building tools for local system. %GOHOSTOS%/%GOHOSTARCH% +setlocal +set GOOS=%GOHOSTOS% +set GOARCH=%GOHOSTARCH% +"%GOTOOLDIR%\go_bootstrap" install -gcflags "%GO_GCFLAGS%" -ldflags "%GO_LDFLAGS%" -v std +endlocal +if errorlevel 1 goto fail +echo. + +:mainbuild +echo # Building packages and commands. +"%GOTOOLDIR%\go_bootstrap" install -gcflags "%GO_GCFLAGS%" -ldflags "%GO_LDFLAGS%" -a -v std +if errorlevel 1 goto fail +del "%GOTOOLDIR%\go_bootstrap.exe" +echo. + +if x%1==x--no-banner goto nobanner +"%GOTOOLDIR%\dist" banner +:nobanner + +goto end + +:fail +set GOBUILDFAIL=1 + +:end diff --git a/src/pkg/Makefile b/src/pkg/Makefile deleted file mode 100644 index 44d7f4367..000000000 --- a/src/pkg/Makefile +++ /dev/null @@ -1,311 +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. - -# After editing the DIRS= list or adding imports to any Go files -# in any of those directories, run: -# -# ./deps.bash -# -# to rebuild the dependency information in Make.deps. - -include ../Make.inc - -all: install - -DIRS=\ - archive/tar\ - archive/zip\ - asn1\ - big\ - bufio\ - bytes\ - cmath\ - compress/bzip2\ - compress/flate\ - compress/gzip\ - compress/lzw \ - compress/zlib\ - container/heap\ - container/list\ - container/ring\ - container/vector\ - crypto\ - crypto/aes\ - crypto/blowfish\ - crypto/cast5\ - crypto/cipher\ - crypto/des\ - crypto/dsa\ - crypto/ecdsa\ - crypto/elliptic\ - crypto/hmac\ - crypto/md4\ - crypto/md5\ - crypto/ocsp\ - crypto/openpgp\ - crypto/openpgp/armor\ - crypto/openpgp/elgamal\ - crypto/openpgp/error\ - crypto/openpgp/packet\ - crypto/openpgp/s2k\ - crypto/rand\ - crypto/rc4\ - crypto/ripemd160\ - crypto/rsa\ - crypto/sha1\ - crypto/sha256\ - crypto/sha512\ - crypto/subtle\ - crypto/tls\ - crypto/twofish\ - crypto/x509\ - crypto/x509/pkix\ - crypto/xtea\ - csv\ - debug/dwarf\ - debug/macho\ - debug/elf\ - debug/gosym\ - debug/pe\ - ebnf\ - encoding/ascii85\ - encoding/base32\ - encoding/base64\ - encoding/binary\ - encoding/git85\ - encoding/hex\ - encoding/pem\ - exec\ - exp/gui\ - exp/gui/x11\ - exp/norm\ - exp/regexp/syntax\ - exp/template/html\ - expvar\ - flag\ - fmt\ - go/ast\ - go/build\ - go/doc\ - go/parser\ - go/printer\ - go/scanner\ - go/token\ - go/typechecker\ - go/types\ - gob\ - hash\ - hash/adler32\ - hash/crc32\ - hash/crc64\ - hash/fnv\ - html\ - http\ - http/cgi\ - http/fcgi\ - http/pprof\ - http/httptest\ - http/spdy\ - image\ - image/bmp\ - image/draw\ - image/gif\ - image/jpeg\ - image/png\ - image/tiff\ - image/ycbcr\ - index/suffixarray\ - io\ - io/ioutil\ - json\ - log\ - mail\ - math\ - mime\ - mime/multipart\ - net\ - net/dict\ - net/textproto\ - netchan\ - old/template\ - os\ - os/signal\ - os/user\ - patch\ - path\ - path/filepath\ - rand\ - reflect\ - regexp\ - rpc\ - rpc/jsonrpc\ - runtime\ - runtime/cgo\ - runtime/debug\ - runtime/pprof\ - scanner\ - smtp\ - sort\ - strconv\ - strings\ - sync\ - sync/atomic\ - syscall\ - syslog\ - tabwriter\ - template\ - template/parse\ - testing\ - testing/iotest\ - testing/quick\ - testing/script\ - time\ - try\ - unicode\ - url\ - utf16\ - utf8\ - websocket\ - xml\ - ../cmd/cgo\ - ../cmd/ebnflint\ - ../cmd/godoc\ - ../cmd/gofix\ - ../cmd/gofmt\ - ../cmd/goinstall\ - ../cmd/gotest\ - ../cmd/gotype\ - ../cmd/govet\ - ../cmd/goyacc\ - ../cmd/hgpatch\ - -ifeq ($(GOOS),linux) -DIRS+=\ - os/inotify\ - -endif - -ifeq ($(GOOS),plan9) -NOPLAN9BUILD=\ - crypto/tls\ - exp/gui/x11\ - expvar\ - http\ - http/cgi\ - http/fcgi\ - http/httptest\ - http/pprof\ - http/spdy\ - mail\ - mime/multipart\ - net\ - net/dict\ - net/textproto\ - netchan\ - os/signal\ - rpc\ - rpc/jsonrpc\ - smtp\ - syslog\ - websocket\ - ../cmd/godoc\ - ../cmd/goinstall\ - -DIRS:=$(filter-out $(NOPLAN9BUILD),$(DIRS)) -endif - -NOTEST+=\ - crypto\ - crypto/openpgp/error\ - crypto/x509/pkix\ - exp/gui\ - exp/gui/x11\ - go/ast\ - go/doc\ - go/token\ - hash\ - http/pprof\ - http/httptest\ - image/bmp\ - image/gif\ - net/dict\ - rand\ - runtime/cgo\ - syscall\ - testing\ - testing/iotest\ - try\ - ../cmd/cgo\ - ../cmd/ebnflint\ - ../cmd/godoc\ - ../cmd/gotest\ - ../cmd/goyacc\ - ../cmd/hgpatch\ - -NOBENCH+=\ - container/vector\ - -# Disable tests that windows cannot run yet. -ifeq ($(GOOS),windows) -NOTEST+=os/signal # no signals -NOTEST+=syslog # no network -NOTEST+=time # no syscall.Kill, syscall.SIGCHLD for sleep tests -endif - -TEST=\ - $(filter-out $(NOTEST),$(DIRS)) - -BENCH=\ - $(filter-out $(NOBENCH),$(TEST)) - -clean.dirs: $(addsuffix .clean, $(DIRS)) -install.dirs: $(addsuffix .install, $(DIRS)) -nuke.dirs: $(addsuffix .nuke, $(DIRS)) -test.dirs: $(addsuffix .test, $(TEST)) -testshort.dirs: $(addsuffix .testshort, $(TEST)) -bench.dirs: $(addsuffix .bench, $(BENCH)) - -%.clean: - +$(MAKE) -C $* clean - -%.install: - +@echo install $* - +@$(MAKE) -C $* install.clean >$*/build.out 2>&1 || (echo INSTALL FAIL $*; cat $*/build.out; exit 1) - -%.nuke: - +$(MAKE) -C $* nuke - -%.test: - +@echo test $* - +@$(MAKE) -C $* test.clean >$*/test.out 2>&1 || (echo TEST FAIL $*; cat $*/test.out; exit 1) - -%.testshort: - +@echo test $* - +@$(MAKE) -C $* testshort.clean >$*/test.out 2>&1 || (echo TEST FAIL $*; cat $*/test.out; exit 1) - -%.bench: - +$(MAKE) -C $* bench - -clean: clean.dirs - -install: install.dirs - -test: test.dirs - -testshort: testshort.dirs - -bench: bench.dirs ../../test/garbage.bench - -nuke: nuke.dirs - rm -rf "$(GOROOT)"/pkg/* - -deps: - ./deps.bash - -echo-dirs: - @echo $(DIRS) - --include Make.deps - -runtime/cgo.install: ../cmd/cgo.install diff --git a/src/pkg/archive/tar/Makefile b/src/pkg/archive/tar/Makefile deleted file mode 100644 index 8897e883e..000000000 --- a/src/pkg/archive/tar/Makefile +++ /dev/null @@ -1,13 +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 ../../../Make.inc - -TARG=archive/tar -GOFILES=\ - common.go\ - reader.go\ - writer.go\ - -include ../../../Make.pkg diff --git a/src/pkg/archive/tar/common.go b/src/pkg/archive/tar/common.go index 528858765..fc7a40923 100644 --- a/src/pkg/archive/tar/common.go +++ b/src/pkg/archive/tar/common.go @@ -11,40 +11,42 @@ // http://www.gnu.org/software/tar/manual/html_node/Standard.html package tar +import "time" + const ( blockSize = 512 // Types - TypeReg = '0' - TypeRegA = '\x00' - TypeLink = '1' - TypeSymlink = '2' - TypeChar = '3' - TypeBlock = '4' - TypeDir = '5' - TypeFifo = '6' - TypeCont = '7' - TypeXHeader = 'x' - TypeXGlobalHeader = 'g' + TypeReg = '0' // regular file + TypeRegA = '\x00' // regular file + TypeLink = '1' // hard link + TypeSymlink = '2' // symbolic link + TypeChar = '3' // character device node + TypeBlock = '4' // block device node + TypeDir = '5' // directory + TypeFifo = '6' // fifo node + TypeCont = '7' // reserved + TypeXHeader = 'x' // extended header + TypeXGlobalHeader = 'g' // global extended header ) // A Header represents a single header in a tar archive. // Some fields may not be populated. type Header struct { - Name string - Mode int64 - Uid int - Gid int - Size int64 - Mtime int64 - Typeflag byte - Linkname string - Uname string - Gname string - Devmajor int64 - Devminor int64 - Atime int64 - Ctime int64 + Name string // name of header file entry + Mode int64 // permission and mode bits + Uid int // user id of owner + Gid int // group id of owner + Size int64 // length in bytes + ModTime time.Time // modified time + Typeflag byte // type of header entry + Linkname string // target name of link + Uname string // user name of owner + Gname string // group name of owner + Devmajor int64 // major number of character or block device + Devminor int64 // minor number of character or block device + AccessTime time.Time // access time + ChangeTime time.Time // status change time } var zeroBlock = make([]byte, blockSize) diff --git a/src/pkg/archive/tar/reader.go b/src/pkg/archive/tar/reader.go index 45d95c3df..1b40af812 100644 --- a/src/pkg/archive/tar/reader.go +++ b/src/pkg/archive/tar/reader.go @@ -9,14 +9,16 @@ package tar import ( "bytes" + "errors" "io" "io/ioutil" "os" "strconv" + "time" ) var ( - HeaderError = os.NewError("invalid tar header") + ErrHeader = errors.New("archive/tar: invalid tar header") ) // A Reader provides sequential access to the contents of a tar archive. @@ -28,7 +30,7 @@ var ( // tr := tar.NewReader(r) // for { // hdr, err := tr.Next() -// if err == os.EOF { +// if err == io.EOF { // // end of tar archive // break // } @@ -39,7 +41,7 @@ var ( // } type Reader struct { r io.Reader - err os.Error + err error nb int64 // number of unread bytes for current file entry pad int64 // amount of padding (ignored) after current file entry } @@ -48,7 +50,7 @@ type Reader struct { func NewReader(r io.Reader) *Reader { return &Reader{r: r} } // Next advances to the next entry in the tar archive. -func (tr *Reader) Next() (*Header, os.Error) { +func (tr *Reader) Next() (*Header, error) { var hdr *Header if tr.err == nil { tr.skipUnread() @@ -78,7 +80,7 @@ func (tr *Reader) octal(b []byte) int64 { for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\x00') { b = b[0 : len(b)-1] } - x, err := strconv.Btoui64(cString(b), 8) + x, err := strconv.ParseUint(cString(b), 8, 64) if err != nil { tr.err = err } @@ -94,7 +96,7 @@ func (tr *Reader) skipUnread() { return } } - _, tr.err = io.Copyn(ioutil.Discard, tr.r, nr) + _, tr.err = io.CopyN(ioutil.Discard, tr.r, nr) } func (tr *Reader) verifyChecksum(header []byte) bool { @@ -119,15 +121,15 @@ func (tr *Reader) readHeader() *Header { return nil } if bytes.Equal(header, zeroBlock[0:blockSize]) { - tr.err = os.EOF + tr.err = io.EOF } else { - tr.err = HeaderError // zero block and then non-zero block + tr.err = ErrHeader // zero block and then non-zero block } return nil } if !tr.verifyChecksum(header) { - tr.err = HeaderError + tr.err = ErrHeader return nil } @@ -140,7 +142,7 @@ func (tr *Reader) readHeader() *Header { hdr.Uid = int(tr.octal(s.next(8))) hdr.Gid = int(tr.octal(s.next(8))) hdr.Size = tr.octal(s.next(12)) - hdr.Mtime = tr.octal(s.next(12)) + hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0) s.next(8) // chksum hdr.Typeflag = s.next(1)[0] hdr.Linkname = cString(s.next(100)) @@ -177,8 +179,8 @@ func (tr *Reader) readHeader() *Header { prefix = cString(s.next(155)) case "star": prefix = cString(s.next(131)) - hdr.Atime = tr.octal(s.next(12)) - hdr.Ctime = tr.octal(s.next(12)) + hdr.AccessTime = time.Unix(tr.octal(s.next(12)), 0) + hdr.ChangeTime = time.Unix(tr.octal(s.next(12)), 0) } if len(prefix) > 0 { hdr.Name = prefix + "/" + hdr.Name @@ -186,7 +188,7 @@ func (tr *Reader) readHeader() *Header { } if tr.err != nil { - tr.err = HeaderError + tr.err = ErrHeader return nil } @@ -199,12 +201,12 @@ func (tr *Reader) readHeader() *Header { } // Read reads from the current entry in the tar archive. -// It returns 0, os.EOF when it reaches the end of that entry, +// It returns 0, io.EOF when it reaches the end of that entry, // until Next is called to advance to the next entry. -func (tr *Reader) Read(b []byte) (n int, err os.Error) { +func (tr *Reader) Read(b []byte) (n int, err error) { if tr.nb == 0 { // file consumed - return 0, os.EOF + return 0, io.EOF } if int64(len(b)) > tr.nb { @@ -213,7 +215,7 @@ func (tr *Reader) Read(b []byte) (n int, err os.Error) { n, err = tr.r.Read(b) tr.nb -= int64(n) - if err == os.EOF && tr.nb > 0 { + if err == io.EOF && tr.nb > 0 { err = io.ErrUnexpectedEOF } tr.err = err diff --git a/src/pkg/archive/tar/reader_test.go b/src/pkg/archive/tar/reader_test.go index f473c900f..0a8646c39 100644 --- a/src/pkg/archive/tar/reader_test.go +++ b/src/pkg/archive/tar/reader_test.go @@ -10,8 +10,8 @@ import ( "fmt" "io" "os" - "reflect" "testing" + "time" ) type untarTest struct { @@ -23,24 +23,24 @@ type untarTest struct { var gnuTarTest = &untarTest{ file: "testdata/gnu.tar", headers: []*Header{ - &Header{ + { Name: "small.txt", Mode: 0640, Uid: 73025, Gid: 5000, Size: 5, - Mtime: 1244428340, + ModTime: time.Unix(1244428340, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", }, - &Header{ + { Name: "small2.txt", Mode: 0640, Uid: 73025, Gid: 5000, Size: 11, - Mtime: 1244436044, + ModTime: time.Unix(1244436044, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", @@ -54,56 +54,56 @@ var gnuTarTest = &untarTest{ var untarTests = []*untarTest{ gnuTarTest, - &untarTest{ + { file: "testdata/star.tar", headers: []*Header{ - &Header{ - Name: "small.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 5, - Mtime: 1244592783, - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - Atime: 1244592783, - Ctime: 1244592783, + { + Name: "small.txt", + Mode: 0640, + Uid: 73025, + Gid: 5000, + Size: 5, + ModTime: time.Unix(1244592783, 0), + Typeflag: '0', + Uname: "dsymonds", + Gname: "eng", + AccessTime: time.Unix(1244592783, 0), + ChangeTime: time.Unix(1244592783, 0), }, - &Header{ - Name: "small2.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 11, - Mtime: 1244592783, - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - Atime: 1244592783, - Ctime: 1244592783, + { + Name: "small2.txt", + Mode: 0640, + Uid: 73025, + Gid: 5000, + Size: 11, + ModTime: time.Unix(1244592783, 0), + Typeflag: '0', + Uname: "dsymonds", + Gname: "eng", + AccessTime: time.Unix(1244592783, 0), + ChangeTime: time.Unix(1244592783, 0), }, }, }, - &untarTest{ + { file: "testdata/v7.tar", headers: []*Header{ - &Header{ + { Name: "small.txt", Mode: 0444, Uid: 73025, Gid: 5000, Size: 5, - Mtime: 1244593104, + ModTime: time.Unix(1244593104, 0), Typeflag: '\x00', }, - &Header{ + { Name: "small2.txt", Mode: 0444, Uid: 73025, Gid: 5000, Size: 11, - Mtime: 1244593104, + ModTime: time.Unix(1244593104, 0), Typeflag: '\x00', }, }, @@ -126,13 +126,13 @@ testLoop: f.Close() continue testLoop } - if !reflect.DeepEqual(hdr, header) { + if *hdr != *header { t.Errorf("test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v", i, j, *hdr, *header) } } hdr, err := tr.Next() - if err == os.EOF { + if err == io.EOF { break } if hdr != nil || err != nil { @@ -195,12 +195,12 @@ func TestIncrementalRead(t *testing.T) { // loop over all files for ; ; nread++ { hdr, err := tr.Next() - if hdr == nil || err == os.EOF { + if hdr == nil || err == io.EOF { break } // check the header - if !reflect.DeepEqual(hdr, headers[nread]) { + if *hdr != *headers[nread] { t.Errorf("Incorrect header:\nhave %+v\nwant %+v", *hdr, headers[nread]) } @@ -211,7 +211,7 @@ func TestIncrementalRead(t *testing.T) { rdbuf := make([]uint8, 8) for { nr, err := tr.Read(rdbuf) - if err == os.EOF { + if err == io.EOF { break } if err != nil { @@ -221,7 +221,7 @@ func TestIncrementalRead(t *testing.T) { h.Write(rdbuf[0:nr]) } // verify checksum - have := fmt.Sprintf("%x", h.Sum()) + have := fmt.Sprintf("%x", h.Sum(nil)) want := cksums[nread] if want != have { t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want) @@ -240,31 +240,20 @@ func TestNonSeekable(t *testing.T) { } defer f.Close() - // pipe the data in - r, w, err := os.Pipe() - if err != nil { - t.Fatalf("Unexpected error %s", err) + type readerOnly struct { + io.Reader } - go func() { - rdbuf := make([]uint8, 1<<16) - for { - nr, err := f.Read(rdbuf) - w.Write(rdbuf[0:nr]) - if err == os.EOF { - break - } - } - w.Close() - }() - - tr := NewReader(r) + tr := NewReader(readerOnly{f}) nread := 0 for ; ; nread++ { - hdr, err := tr.Next() - if hdr == nil || err == os.EOF { + _, err := tr.Next() + if err == io.EOF { break } + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } } if nread != len(test.headers) { diff --git a/src/pkg/archive/tar/testdata/writer.tar b/src/pkg/archive/tar/testdata/writer.tar index 0358f91b9..e6d816ad0 100644 Binary files a/src/pkg/archive/tar/testdata/writer.tar and b/src/pkg/archive/tar/testdata/writer.tar differ diff --git a/src/pkg/archive/tar/writer.go b/src/pkg/archive/tar/writer.go index 8673bad31..b2b7a58a1 100644 --- a/src/pkg/archive/tar/writer.go +++ b/src/pkg/archive/tar/writer.go @@ -5,18 +5,19 @@ package tar // TODO(dsymonds): -// - catch more errors (no first header, write after close, etc.) +// - catch more errors (no first header, etc.) import ( + "errors" + "fmt" "io" - "os" "strconv" ) var ( - ErrWriteTooLong = os.NewError("write too long") - ErrFieldTooLong = os.NewError("header field too long") - ErrWriteAfterClose = os.NewError("write after close") + 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") ) // A Writer provides sequential writing of a tar archive in POSIX.1 format. @@ -36,7 +37,7 @@ var ( // tw.Close() type Writer struct { w io.Writer - err os.Error + err error nb int64 // number of unwritten bytes for current file entry pad int64 // amount of padding to write after current file entry closed bool @@ -47,7 +48,12 @@ type Writer struct { func NewWriter(w io.Writer) *Writer { return &Writer{w: w} } // Flush finishes writing the current file (optional). -func (tw *Writer) Flush() os.Error { +func (tw *Writer) Flush() error { + if tw.nb > 0 { + tw.err = fmt.Errorf("archive/tar: missed writing %d bytes", tw.nb) + return tw.err + } + n := tw.nb + tw.pad for n > 0 && tw.err == nil { nr := n @@ -79,7 +85,7 @@ func (tw *Writer) cString(b []byte, s string) { // Encode x as an octal ASCII string and write it into b with leading zeros. func (tw *Writer) octal(b []byte, x int64) { - s := strconv.Itob64(x, 8) + s := strconv.FormatInt(x, 8) // leading zeros, but leave room for a NUL. for len(s)+1 < len(b) { s = "0" + s @@ -90,7 +96,7 @@ func (tw *Writer) octal(b []byte, x int64) { // Write x into b, either as octal or as binary (GNUtar/star extension). func (tw *Writer) numeric(b []byte, x int64) { // Try octal first. - s := strconv.Itob64(x, 8) + s := strconv.FormatInt(x, 8) if len(s) < len(b) { tw.octal(b, x) return @@ -107,7 +113,7 @@ func (tw *Writer) numeric(b []byte, x int64) { // 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. -func (tw *Writer) WriteHeader(hdr *Header) os.Error { +func (tw *Writer) WriteHeader(hdr *Header) error { if tw.closed { return ErrWriteAfterClose } @@ -127,19 +133,19 @@ func (tw *Writer) WriteHeader(hdr *Header) os.Error { // 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.Mtime) // 136:148 - s.next(8) // chksum (148:156) - s.next(1)[0] = hdr.Typeflag // 156:157 - s.next(100) // 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.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 // Use the GNU magic instead of POSIX magic if we used any GNU extensions. if tw.usedBinary { @@ -165,7 +171,7 @@ func (tw *Writer) WriteHeader(hdr *Header) os.Error { // 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. -func (tw *Writer) Write(b []byte) (n int, err os.Error) { +func (tw *Writer) Write(b []byte) (n int, err error) { if tw.closed { err = ErrWriteTooLong return @@ -187,12 +193,15 @@ func (tw *Writer) Write(b []byte) (n int, err os.Error) { // Close closes the tar archive, flushing any unwritten // data to the underlying writer. -func (tw *Writer) Close() os.Error { +func (tw *Writer) Close() error { if tw.err != nil || tw.closed { return tw.err } tw.Flush() tw.closed = true + if tw.err != nil { + return tw.err + } // trailer: two zero blocks for i := 0; i < 2; i++ { diff --git a/src/pkg/archive/tar/writer_test.go b/src/pkg/archive/tar/writer_test.go index 838cb7e1f..a214e57b9 100644 --- a/src/pkg/archive/tar/writer_test.go +++ b/src/pkg/archive/tar/writer_test.go @@ -9,8 +9,10 @@ import ( "fmt" "io" "io/ioutil" + "strings" "testing" "testing/iotest" + "time" ) type writerTestEntry struct { @@ -24,58 +26,78 @@ type writerTest struct { } var writerTests = []*writerTest{ - &writerTest{ + // The writer test file was produced with this command: + // tar (GNU tar) 1.26 + // ln -s small.txt link.txt + // tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt + { file: "testdata/writer.tar", entries: []*writerTestEntry{ - &writerTestEntry{ + { header: &Header{ Name: "small.txt", Mode: 0640, Uid: 73025, Gid: 5000, Size: 5, - Mtime: 1246508266, + ModTime: time.Unix(1246508266, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", }, contents: "Kilts", }, - &writerTestEntry{ + { header: &Header{ Name: "small2.txt", Mode: 0640, Uid: 73025, Gid: 5000, Size: 11, - Mtime: 1245217492, + ModTime: time.Unix(1245217492, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", }, contents: "Google.com\n", }, + { + header: &Header{ + Name: "link.txt", + Mode: 0777, + Uid: 1000, + Gid: 1000, + Size: 0, + ModTime: time.Unix(1314603082, 0), + Typeflag: '2', + Linkname: "small.txt", + Uname: "strings", + Gname: "strings", + }, + // no contents + }, }, }, // The truncated test file was produced using these commands: // dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt // tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar - &writerTest{ + { file: "testdata/writer-big.tar", entries: []*writerTestEntry{ - &writerTestEntry{ + { header: &Header{ Name: "tmp/16gig.txt", Mode: 0640, Uid: 73025, Gid: 5000, Size: 16 << 30, - Mtime: 1254699560, + ModTime: time.Unix(1254699560, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", }, - // no contents + // fake contents + contents: strings.Repeat("\x00", 4<<10), }, }, }, @@ -130,7 +152,9 @@ testLoop: buf := new(bytes.Buffer) tw := NewWriter(iotest.TruncateWriter(buf, 4<<10)) // only catch the first 4 KB + big := false for j, entry := range test.entries { + big = big || entry.header.Size > 1<<10 if err := tw.WriteHeader(entry.header); err != nil { t.Errorf("test %d, entry %d: Failed writing header: %v", i, j, err) continue testLoop @@ -140,7 +164,8 @@ testLoop: continue testLoop } } - if err := tw.Close(); err != nil { + // Only interested in Close failures for the small tests. + if err := tw.Close(); err != nil && !big { t.Errorf("test %d: Failed closing archive: %v", i, err) continue testLoop } diff --git a/src/pkg/archive/zip/Makefile b/src/pkg/archive/zip/Makefile deleted file mode 100644 index 9071690f0..000000000 --- a/src/pkg/archive/zip/Makefile +++ /dev/null @@ -1,13 +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. - -include ../../../Make.inc - -TARG=archive/zip -GOFILES=\ - reader.go\ - struct.go\ - writer.go\ - -include ../../../Make.pkg diff --git a/src/pkg/archive/zip/example_test.go b/src/pkg/archive/zip/example_test.go new file mode 100644 index 000000000..c2ed9e79c --- /dev/null +++ b/src/pkg/archive/zip/example_test.go @@ -0,0 +1,75 @@ +// 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 zip_test + +import ( + "archive/zip" + "bytes" + "fmt" + "io" + "log" + "os" +) + +func ExampleWriter() { + // Create a buffer to write our archive to. + buf := new(bytes.Buffer) + + // Create a new zip archive. + w := zip.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.\nWrite more examples."}, + } + for _, file := range files { + f, err := w.Create(file.Name) + if err != nil { + log.Fatal(err) + } + _, err = f.Write([]byte(file.Body)) + if err != nil { + log.Fatal(err) + } + } + + // Make sure to check the error on Close. + err := w.Close() + if err != nil { + log.Fatal(err) + } +} + +func ExampleReader() { + // Open a zip archive for reading. + r, err := zip.OpenReader("testdata/readme.zip") + if err != nil { + log.Fatal(err) + } + defer r.Close() + + // Iterate through the files in the archive, + // printing some of their contents. + for _, f := range r.File { + fmt.Printf("Contents of %s:\n", f.Name) + rc, err := f.Open() + if err != nil { + log.Fatal(err) + } + _, err = io.CopyN(os.Stdout, rc, 68) + if err != nil { + log.Fatal(err) + } + rc.Close() + fmt.Println() + } + // Output: + // Contents of README: + // This is the source code repository for the Go programming language. +} diff --git a/src/pkg/archive/zip/reader.go b/src/pkg/archive/zip/reader.go index f92f9297a..ddd507538 100644 --- a/src/pkg/archive/zip/reader.go +++ b/src/pkg/archive/zip/reader.go @@ -7,18 +7,19 @@ package zip import ( "bufio" "compress/flate" + "encoding/binary" + "errors" "hash" "hash/crc32" - "encoding/binary" "io" "io/ioutil" "os" ) var ( - FormatError = os.NewError("zip: not a valid zip file") - UnsupportedMethod = os.NewError("zip: unsupported compression algorithm") - ChecksumError = os.NewError("zip: checksum error") + ErrFormat = errors.New("zip: not a valid zip file") + ErrAlgorithm = errors.New("zip: unsupported compression algorithm") + ErrChecksum = errors.New("zip: checksum error") ) type Reader struct { @@ -44,7 +45,7 @@ func (f *File) hasDataDescriptor() bool { } // OpenReader will open the Zip file specified by name and return a ReadCloser. -func OpenReader(name string) (*ReadCloser, os.Error) { +func OpenReader(name string) (*ReadCloser, error) { f, err := os.Open(name) if err != nil { return nil, err @@ -55,16 +56,17 @@ func OpenReader(name string) (*ReadCloser, os.Error) { return nil, err } r := new(ReadCloser) - if err := r.init(f, fi.Size); err != nil { + if err := r.init(f, fi.Size()); err != nil { f.Close() return nil, err } + r.f = f return r, nil } // NewReader returns a new Reader reading from r, which is assumed to // have the given size in bytes. -func NewReader(r io.ReaderAt, size int64) (*Reader, os.Error) { +func NewReader(r io.ReaderAt, size int64) (*Reader, error) { zr := new(Reader) if err := zr.init(r, size); err != nil { return nil, err @@ -72,7 +74,7 @@ func NewReader(r io.ReaderAt, size int64) (*Reader, os.Error) { return zr, nil } -func (z *Reader) init(r io.ReaderAt, size int64) os.Error { +func (z *Reader) init(r io.ReaderAt, size int64) error { end, err := readDirectoryEnd(r, size) if err != nil { return err @@ -88,12 +90,12 @@ func (z *Reader) init(r io.ReaderAt, size int64) os.Error { // The count of files inside a zip is truncated to fit in a uint16. // Gloss over this by reading headers until we encounter - // a bad one, and then only report a FormatError or UnexpectedEOF if + // a bad one, and then only report a ErrFormat or UnexpectedEOF if // the file count modulo 65536 is incorrect. for { f := &File{zipr: r, zipsize: size} err = readDirectoryHeader(f, buf) - if err == FormatError || err == io.ErrUnexpectedEOF { + if err == ErrFormat || err == io.ErrUnexpectedEOF { break } if err != nil { @@ -110,22 +112,18 @@ func (z *Reader) init(r io.ReaderAt, size int64) os.Error { } // Close closes the Zip file, rendering it unusable for I/O. -func (rc *ReadCloser) Close() os.Error { +func (rc *ReadCloser) Close() error { return rc.f.Close() } // Open returns a ReadCloser that provides access to the File's contents. -// It is safe to Open and Read from files concurrently. -func (f *File) Open() (rc io.ReadCloser, err os.Error) { +// Multiple files may be read concurrently. +func (f *File) Open() (rc io.ReadCloser, err error) { bodyOffset, err := f.findBodyOffset() if err != nil { return } size := int64(f.CompressedSize) - if size == 0 && f.hasDataDescriptor() { - // permit SectionReader to see the rest of the file - size = f.zipsize - (f.headerOffset + bodyOffset) - } r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size) switch f.Method { case Store: // (no compression) @@ -133,11 +131,14 @@ func (f *File) Open() (rc io.ReadCloser, err os.Error) { case Deflate: rc = flate.NewReader(r) default: - err = UnsupportedMethod + err = ErrAlgorithm + return } - if rc != nil { - rc = &checksumReader{rc, crc32.NewIEEE(), f, r} + var desr io.Reader + if f.hasDataDescriptor() { + desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen) } + rc = &checksumReader{rc, crc32.NewIEEE(), f, desr, nil} return } @@ -145,101 +146,86 @@ type checksumReader struct { rc io.ReadCloser hash hash.Hash32 f *File - zipr io.Reader // for reading the data descriptor + desr io.Reader // if non-nil, where to read the data descriptor + err error // sticky error } -func (r *checksumReader) Read(b []byte) (n int, err os.Error) { +func (r *checksumReader) Read(b []byte) (n int, err error) { + if r.err != nil { + return 0, r.err + } n, err = r.rc.Read(b) r.hash.Write(b[:n]) - if err != os.EOF { + if err == nil { return } - if r.f.hasDataDescriptor() { - if err = readDataDescriptor(r.zipr, r.f); err != nil { - return + if err == io.EOF { + if r.desr != nil { + if err1 := readDataDescriptor(r.desr, r.f); err1 != nil { + err = err1 + } else if r.hash.Sum32() != r.f.CRC32 { + err = ErrChecksum + } + } else { + // If there's not a data descriptor, we still compare + // the CRC32 of what we've read against the file header + // or TOC's CRC32, if it seems like it was set. + if r.f.CRC32 != 0 && r.hash.Sum32() != r.f.CRC32 { + err = ErrChecksum + } } } - if r.hash.Sum32() != r.f.CRC32 { - err = ChecksumError - } + r.err = err return } -func (r *checksumReader) Close() os.Error { return r.rc.Close() } - -func readFileHeader(f *File, r io.Reader) os.Error { - var b [fileHeaderLen]byte - if _, err := io.ReadFull(r, b[:]); err != nil { - return err - } - c := binary.LittleEndian - if sig := c.Uint32(b[:4]); sig != fileHeaderSignature { - return FormatError - } - f.ReaderVersion = c.Uint16(b[4:6]) - f.Flags = c.Uint16(b[6:8]) - f.Method = c.Uint16(b[8:10]) - f.ModifiedTime = c.Uint16(b[10:12]) - f.ModifiedDate = c.Uint16(b[12:14]) - f.CRC32 = c.Uint32(b[14:18]) - f.CompressedSize = c.Uint32(b[18:22]) - f.UncompressedSize = c.Uint32(b[22:26]) - filenameLen := int(c.Uint16(b[26:28])) - extraLen := int(c.Uint16(b[28:30])) - d := make([]byte, filenameLen+extraLen) - if _, err := io.ReadFull(r, d); err != nil { - return err - } - f.Name = string(d[:filenameLen]) - f.Extra = d[filenameLen:] - return nil -} +func (r *checksumReader) Close() error { return r.rc.Close() } // findBodyOffset does the minimum work to verify the file has a header // and returns the file body offset. -func (f *File) findBodyOffset() (int64, os.Error) { +func (f *File) findBodyOffset() (int64, error) { r := io.NewSectionReader(f.zipr, f.headerOffset, f.zipsize-f.headerOffset) - var b [fileHeaderLen]byte - if _, err := io.ReadFull(r, b[:]); err != nil { + var buf [fileHeaderLen]byte + if _, err := io.ReadFull(r, buf[:]); err != nil { return 0, err } - c := binary.LittleEndian - if sig := c.Uint32(b[:4]); sig != fileHeaderSignature { - return 0, FormatError + b := readBuf(buf[:]) + if sig := b.uint32(); sig != fileHeaderSignature { + return 0, ErrFormat } - filenameLen := int(c.Uint16(b[26:28])) - extraLen := int(c.Uint16(b[28:30])) + b = b[22:] // skip over most of the header + filenameLen := int(b.uint16()) + extraLen := int(b.uint16()) return int64(fileHeaderLen + filenameLen + extraLen), nil } // readDirectoryHeader attempts to read a directory header from r. // It returns io.ErrUnexpectedEOF if it cannot read a complete header, -// and FormatError if it doesn't find a valid header signature. -func readDirectoryHeader(f *File, r io.Reader) os.Error { - var b [directoryHeaderLen]byte - if _, err := io.ReadFull(r, b[:]); err != nil { +// and ErrFormat if it doesn't find a valid header signature. +func readDirectoryHeader(f *File, r io.Reader) error { + var buf [directoryHeaderLen]byte + if _, err := io.ReadFull(r, buf[:]); err != nil { return err } - c := binary.LittleEndian - if sig := c.Uint32(b[:4]); sig != directoryHeaderSignature { - return FormatError + b := readBuf(buf[:]) + if sig := b.uint32(); sig != directoryHeaderSignature { + return ErrFormat } - f.CreatorVersion = c.Uint16(b[4:6]) - f.ReaderVersion = c.Uint16(b[6:8]) - f.Flags = c.Uint16(b[8:10]) - f.Method = c.Uint16(b[10:12]) - f.ModifiedTime = c.Uint16(b[12:14]) - f.ModifiedDate = c.Uint16(b[14:16]) - f.CRC32 = c.Uint32(b[16:20]) - f.CompressedSize = c.Uint32(b[20:24]) - f.UncompressedSize = c.Uint32(b[24:28]) - filenameLen := int(c.Uint16(b[28:30])) - extraLen := int(c.Uint16(b[30:32])) - commentLen := int(c.Uint16(b[32:34])) - // startDiskNumber := c.Uint16(b[34:36]) // Unused - // internalAttributes := c.Uint16(b[36:38]) // Unused - // externalAttributes := c.Uint32(b[38:42]) // Unused - f.headerOffset = int64(c.Uint32(b[42:46])) + f.CreatorVersion = b.uint16() + f.ReaderVersion = b.uint16() + f.Flags = b.uint16() + f.Method = b.uint16() + f.ModifiedTime = b.uint16() + f.ModifiedDate = b.uint16() + f.CRC32 = b.uint32() + f.CompressedSize = b.uint32() + f.UncompressedSize = b.uint32() + filenameLen := int(b.uint16()) + extraLen := int(b.uint16()) + commentLen := int(b.uint16()) + b = b[4:] // skipped start disk number and internal attributes (2x uint16) + f.ExternalAttrs = b.uint32() + f.headerOffset = int64(b.uint32()) d := make([]byte, filenameLen+extraLen+commentLen) if _, err := io.ReadFull(r, d); err != nil { return err @@ -250,49 +236,75 @@ func readDirectoryHeader(f *File, r io.Reader) os.Error { return nil } -func readDataDescriptor(r io.Reader, f *File) os.Error { - var b [dataDescriptorLen]byte - if _, err := io.ReadFull(r, b[:]); err != nil { +func readDataDescriptor(r io.Reader, f *File) error { + var buf [dataDescriptorLen]byte + + // The spec says: "Although not originally assigned a + // signature, the value 0x08074b50 has commonly been adopted + // as a signature value for the data descriptor record. + // Implementers should be aware that ZIP files may be + // encountered with or without this signature marking data + // descriptors and should account for either case when reading + // ZIP files to ensure compatibility." + // + // dataDescriptorLen includes the size of the signature but + // first read just those 4 bytes to see if it exists. + if _, err := io.ReadFull(r, buf[:4]); err != nil { + return err + } + off := 0 + maybeSig := readBuf(buf[:4]) + if maybeSig.uint32() != dataDescriptorSignature { + // No data descriptor signature. Keep these four + // bytes. + off += 4 + } + if _, err := io.ReadFull(r, buf[off:12]); err != nil { return err } - c := binary.LittleEndian - f.CRC32 = c.Uint32(b[:4]) - f.CompressedSize = c.Uint32(b[4:8]) - f.UncompressedSize = c.Uint32(b[8:12]) + b := readBuf(buf[:12]) + f.CRC32 = b.uint32() + f.CompressedSize = b.uint32() + f.UncompressedSize = b.uint32() return nil } -func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err os.Error) { +func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) { // look for directoryEndSignature in the last 1k, then in the last 65k - var b []byte + var buf []byte for i, bLen := range []int64{1024, 65 * 1024} { if bLen > size { bLen = size } - b = make([]byte, int(bLen)) - if _, err := r.ReadAt(b, size-bLen); err != nil && err != os.EOF { + buf = make([]byte, int(bLen)) + if _, err := r.ReadAt(buf, size-bLen); err != nil && err != io.EOF { return nil, err } - if p := findSignatureInBlock(b); p >= 0 { - b = b[p:] + if p := findSignatureInBlock(buf); p >= 0 { + buf = buf[p:] break } if i == 1 || bLen == size { - return nil, FormatError + return nil, ErrFormat } } // read header into struct - c := binary.LittleEndian - d := new(directoryEnd) - d.diskNbr = c.Uint16(b[4:6]) - d.dirDiskNbr = c.Uint16(b[6:8]) - d.dirRecordsThisDisk = c.Uint16(b[8:10]) - d.directoryRecords = c.Uint16(b[10:12]) - d.directorySize = c.Uint32(b[12:16]) - d.directoryOffset = c.Uint32(b[16:20]) - d.commentLen = c.Uint16(b[20:22]) - d.comment = string(b[22 : 22+int(d.commentLen)]) + 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(), + commentLen: b.uint16(), + } + l := int(d.commentLen) + if l > len(b) { + return nil, errors.New("zip: invalid comment length") + } + d.comment = string(b[:l]) return d, nil } @@ -309,3 +321,17 @@ func findSignatureInBlock(b []byte) int { } return -1 } + +type readBuf []byte + +func (b *readBuf) uint16() uint16 { + v := binary.LittleEndian.Uint16(*b) + *b = (*b)[2:] + return v +} + +func (b *readBuf) uint32() uint32 { + v := binary.LittleEndian.Uint32(*b) + *b = (*b)[4:] + return v +} diff --git a/src/pkg/archive/zip/reader_test.go b/src/pkg/archive/zip/reader_test.go index fd5fed2af..5f1d1b28a 100644 --- a/src/pkg/archive/zip/reader_test.go +++ b/src/pkg/archive/zip/reader_test.go @@ -7,25 +7,31 @@ package zip import ( "bytes" "encoding/binary" + "encoding/hex" "io" "io/ioutil" "os" + "path/filepath" + "regexp" "testing" "time" ) type ZipTest struct { Name string + Source func() (r io.ReaderAt, size int64) // if non-nil, used instead of testdata/ file Comment string File []ZipTestFile - Error os.Error // the error that Opening this file should return + Error error // the error that Opening this file should return } type ZipTestFile struct { - Name string - Content []byte // if blank, will attempt to compare against File - File string // name of file to compare to (relative to testdata/) - Mtime string // modified time in format "mm-dd-yy hh:mm:ss" + Name string + Content []byte // if blank, will attempt to compare against File + ContentErr error + File string // name of file to compare to (relative to testdata/) + Mtime string // modified time in format "mm-dd-yy hh:mm:ss" + Mode os.FileMode } // Caution: The Mtime values found for the test files should correspond to @@ -47,26 +53,45 @@ var tests = []ZipTest{ Name: "test.txt", Content: []byte("This is a test text file.\n"), Mtime: "09-05-10 12:12:02", + Mode: 0644, }, { Name: "gophercolor16x16.png", File: "gophercolor16x16.png", Mtime: "09-05-10 15:52:58", + Mode: 0644, + }, + }, + }, + { + Name: "r.zip", + Source: returnRecursiveZip, + File: []ZipTestFile{ + { + Name: "r/r.zip", + Content: rZipBytes(), + Mtime: "03-04-10 00:24:16", + Mode: 0666, }, }, }, { - Name: "r.zip", + Name: "symlink.zip", File: []ZipTestFile{ { - Name: "r/r.zip", - File: "r.zip", - Mtime: "03-04-10 00:24:16", + Name: "symlink", + Content: []byte("../target"), + Mode: 0777 | os.ModeSymlink, }, }, }, - {Name: "readme.zip"}, - {Name: "readme.notzip", Error: FormatError}, + { + Name: "readme.zip", + }, + { + Name: "readme.notzip", + Error: ErrFormat, + }, { Name: "dd.zip", File: []ZipTestFile{ @@ -74,9 +99,136 @@ var tests = []ZipTest{ Name: "filename", Content: []byte("This is a test textfile.\n"), Mtime: "02-02-11 13:06:20", + Mode: 0666, }, }, }, + { + // created in windows XP file manager. + Name: "winxp.zip", + File: crossPlatform, + }, + { + // created by Zip 3.0 under Linux + Name: "unix.zip", + File: crossPlatform, + }, + { + // created by Go, before we wrote the "optional" data + // descriptor signatures (which are required by OS X) + Name: "go-no-datadesc-sig.zip", + File: []ZipTestFile{ + { + Name: "foo.txt", + Content: []byte("foo\n"), + Mtime: "03-08-12 16:59:10", + Mode: 0644, + }, + { + Name: "bar.txt", + Content: []byte("bar\n"), + Mtime: "03-08-12 16:59:12", + Mode: 0644, + }, + }, + }, + { + // created by Go, after we wrote the "optional" data + // descriptor signatures (which are required by OS X) + Name: "go-with-datadesc-sig.zip", + File: []ZipTestFile{ + { + Name: "foo.txt", + Content: []byte("foo\n"), + Mode: 0666, + }, + { + Name: "bar.txt", + Content: []byte("bar\n"), + Mode: 0666, + }, + }, + }, + { + Name: "Bad-CRC32-in-data-descriptor", + Source: returnCorruptCRC32Zip, + File: []ZipTestFile{ + { + Name: "foo.txt", + Content: []byte("foo\n"), + Mode: 0666, + ContentErr: ErrChecksum, + }, + { + Name: "bar.txt", + Content: []byte("bar\n"), + Mode: 0666, + }, + }, + }, + // Tests that we verify (and accept valid) crc32s on files + // with crc32s in their file header (not in data descriptors) + { + Name: "crc32-not-streamed.zip", + File: []ZipTestFile{ + { + Name: "foo.txt", + Content: []byte("foo\n"), + Mtime: "03-08-12 16:59:10", + Mode: 0644, + }, + { + Name: "bar.txt", + Content: []byte("bar\n"), + Mtime: "03-08-12 16:59:12", + Mode: 0644, + }, + }, + }, + // Tests that we verify (and reject invalid) crc32s on files + // with crc32s in their file header (not in data descriptors) + { + Name: "crc32-not-streamed.zip", + Source: returnCorruptNotStreamedZip, + File: []ZipTestFile{ + { + Name: "foo.txt", + Content: []byte("foo\n"), + Mtime: "03-08-12 16:59:10", + Mode: 0644, + ContentErr: ErrChecksum, + }, + { + Name: "bar.txt", + Content: []byte("bar\n"), + Mtime: "03-08-12 16:59:12", + Mode: 0644, + }, + }, + }, +} + +var crossPlatform = []ZipTestFile{ + { + Name: "hello", + Content: []byte("world \r\n"), + Mode: 0666, + }, + { + Name: "dir/bar", + Content: []byte("foo \r\n"), + Mode: 0666, + }, + { + Name: "dir/empty/", + Content: []byte{}, + Mode: os.ModeDir | 0777, + }, + { + Name: "readonly", + Content: []byte("important \r\n"), + Mode: 0444, + }, } func TestReader(t *testing.T) { @@ -86,17 +238,27 @@ func TestReader(t *testing.T) { } func readTestZip(t *testing.T, zt ZipTest) { - z, err := OpenReader("testdata/" + zt.Name) + var z *Reader + var err error + if zt.Source != nil { + rat, size := zt.Source() + z, err = NewReader(rat, size) + } else { + var rc *ReadCloser + rc, err = OpenReader(filepath.Join("testdata", zt.Name)) + if err == nil { + z = &rc.Reader + } + } if err != zt.Error { t.Errorf("error=%v, want %v", err, zt.Error) return } // bail if file is not zip - if err == FormatError { + if err == ErrFormat { return } - defer z.Close() // bail here if no Files expected to be tested // (there may actually be files in the zip, but we don't care) @@ -108,12 +270,12 @@ func readTestZip(t *testing.T, zt ZipTest) { t.Errorf("%s: comment=%q, want %q", zt.Name, z.Comment, zt.Comment) } if len(z.File) != len(zt.File) { - t.Errorf("%s: file count=%d, want %d", zt.Name, len(z.File), len(zt.File)) + t.Fatalf("%s: file count=%d, want %d", zt.Name, len(z.File), len(zt.File)) } // test read of each file for i, ft := range zt.File { - readTestFile(t, ft, z.File[i]) + readTestFile(t, zt, ft, z.File[i]) } // test simultaneous reads @@ -121,46 +283,35 @@ func readTestZip(t *testing.T, zt ZipTest) { done := make(chan bool) for i := 0; i < 5; i++ { for j, ft := range zt.File { - go func() { - readTestFile(t, ft, z.File[j]) + go func(j int, ft ZipTestFile) { + readTestFile(t, zt, ft, z.File[j]) done <- true - }() + }(j, ft) n++ } } for ; n > 0; n-- { <-done } +} + +func readTestFile(t *testing.T, zt ZipTest, ft ZipTestFile, f *File) { + if f.Name != ft.Name { + t.Errorf("%s: name=%q, want %q", zt.Name, f.Name, ft.Name) + } - // test invalid checksum - if !z.File[0].hasDataDescriptor() { // skip test when crc32 in dd - z.File[0].CRC32++ // invalidate - r, err := z.File[0].Open() + if ft.Mtime != "" { + mtime, err := time.Parse("01-02-06 15:04:05", ft.Mtime) if err != nil { t.Error(err) return } - var b bytes.Buffer - _, err = io.Copy(&b, r) - if err != ChecksumError { - t.Errorf("%s: copy error=%v, want %v", z.File[0].Name, err, ChecksumError) + if ft := f.ModTime(); !ft.Equal(mtime) { + t.Errorf("%s: %s: mtime=%s, want %s", zt.Name, f.Name, ft, mtime) } } -} - -func readTestFile(t *testing.T, ft ZipTestFile, f *File) { - if f.Name != ft.Name { - t.Errorf("name=%q, want %q", f.Name, ft.Name) - } - mtime, err := time.Parse("01-02-06 15:04:05", ft.Mtime) - if err != nil { - t.Error(err) - return - } - if got, want := f.Mtime_ns()/1e9, mtime.Seconds(); got != want { - t.Errorf("%s: mtime=%s (%d); want %s (%d)", f.Name, time.SecondsToUTC(got), got, mtime, want) - } + testFileMode(t, zt.Name, f, ft.Mode) size0 := f.UncompressedSize @@ -176,14 +327,16 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) { } _, err = io.Copy(&b, r) + if err != ft.ContentErr { + t.Errorf("%s: copying contents: %v (want %v)", zt.Name, err, ft.ContentErr) + } if err != nil { - t.Error(err) return } r.Close() var c []byte - if len(ft.Content) != 0 { + if ft.Content != nil { c = ft.Content } else if c, err = ioutil.ReadFile("testdata/" + ft.File); err != nil { t.Error(err) @@ -203,14 +356,23 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) { } } +func testFileMode(t *testing.T, zipName string, f *File, want os.FileMode) { + mode := f.Mode() + if want == 0 { + t.Errorf("%s: %s mode: got %v, want none", zipName, f.Name, mode) + } else if mode != want { + t.Errorf("%s: %s mode: want %v, got %v", zipName, f.Name, want, mode) + } +} + func TestInvalidFiles(t *testing.T) { const size = 1024 * 70 // 70kb b := make([]byte, size) // zeroes - _, err := NewReader(sliceReaderAt(b), size) - if err != FormatError { - t.Errorf("zeroes: error=%v, want %v", err, FormatError) + _, err := NewReader(bytes.NewReader(b), size) + if err != ErrFormat { + t.Errorf("zeroes: error=%v, want %v", err, ErrFormat) } // repeated directoryEndSignatures @@ -219,15 +381,86 @@ func TestInvalidFiles(t *testing.T) { for i := 0; i < size-4; i += 4 { copy(b[i:i+4], sig) } - _, err = NewReader(sliceReaderAt(b), size) - if err != FormatError { - t.Errorf("sigs: error=%v, want %v", err, FormatError) + _, err = NewReader(bytes.NewReader(b), size) + if err != ErrFormat { + t.Errorf("sigs: error=%v, want %v", err, ErrFormat) } } -type sliceReaderAt []byte +func messWith(fileName string, corrupter func(b []byte)) (r io.ReaderAt, size int64) { + data, err := ioutil.ReadFile(filepath.Join("testdata", fileName)) + if err != nil { + panic("Error reading " + fileName + ": " + err.Error()) + } + corrupter(data) + return bytes.NewReader(data), int64(len(data)) +} + +func returnCorruptCRC32Zip() (r io.ReaderAt, size int64) { + return messWith("go-with-datadesc-sig.zip", func(b []byte) { + // Corrupt one of the CRC32s in the data descriptor: + b[0x2d]++ + }) +} + +func returnCorruptNotStreamedZip() (r io.ReaderAt, size int64) { + return messWith("crc32-not-streamed.zip", func(b []byte) { + // Corrupt foo.txt's final crc32 byte, in both + // the file header and TOC. (0x7e -> 0x7f) + b[0x11]++ + b[0x9d]++ + + // TODO(bradfitz): add a new test that only corrupts + // one of these values, and verify that that's also an + // error. Currently, the reader code doesn't verify the + // fileheader and TOC's crc32 match if they're both + // non-zero and only the second line above, the TOC, + // is what matters. + }) +} + +// rZipBytes returns the bytes of a recursive zip file, without +// putting it on disk and triggering certain virus scanners. +func rZipBytes() []byte { + s := ` +0000000 50 4b 03 04 14 00 00 00 08 00 08 03 64 3c f9 f4 +0000010 89 64 48 01 00 00 b8 01 00 00 07 00 00 00 72 2f +0000020 72 2e 7a 69 70 00 25 00 da ff 50 4b 03 04 14 00 +0000030 00 00 08 00 08 03 64 3c f9 f4 89 64 48 01 00 00 +0000040 b8 01 00 00 07 00 00 00 72 2f 72 2e 7a 69 70 00 +0000050 2f 00 d0 ff 00 25 00 da ff 50 4b 03 04 14 00 00 +0000060 00 08 00 08 03 64 3c f9 f4 89 64 48 01 00 00 b8 +0000070 01 00 00 07 00 00 00 72 2f 72 2e 7a 69 70 00 2f +0000080 00 d0 ff c2 54 8e 57 39 00 05 00 fa ff c2 54 8e +0000090 57 39 00 05 00 fa ff 00 05 00 fa ff 00 14 00 eb +00000a0 ff c2 54 8e 57 39 00 05 00 fa ff 00 05 00 fa ff +00000b0 00 14 00 eb ff 42 88 21 c4 00 00 14 00 eb ff 42 +00000c0 88 21 c4 00 00 14 00 eb ff 42 88 21 c4 00 00 14 +00000d0 00 eb ff 42 88 21 c4 00 00 14 00 eb ff 42 88 21 +00000e0 c4 00 00 00 00 ff ff 00 00 00 ff ff 00 34 00 cb +00000f0 ff 42 88 21 c4 00 00 00 00 ff ff 00 00 00 ff ff +0000100 00 34 00 cb ff 42 e8 21 5e 0f 00 00 00 ff ff 0a +0000110 f0 66 64 12 61 c0 15 dc e8 a0 48 bf 48 af 2a b3 +0000120 20 c0 9b 95 0d c4 67 04 42 53 06 06 06 40 00 06 +0000130 00 f9 ff 6d 01 00 00 00 00 42 e8 21 5e 0f 00 00 +0000140 00 ff ff 0a f0 66 64 12 61 c0 15 dc e8 a0 48 bf +0000150 48 af 2a b3 20 c0 9b 95 0d c4 67 04 42 53 06 06 +0000160 06 40 00 06 00 f9 ff 6d 01 00 00 00 00 50 4b 01 +0000170 02 14 00 14 00 00 00 08 00 08 03 64 3c f9 f4 89 +0000180 64 48 01 00 00 b8 01 00 00 07 00 00 00 00 00 00 +0000190 00 00 00 00 00 00 00 00 00 00 00 72 2f 72 2e 7a +00001a0 69 70 50 4b 05 06 00 00 00 00 01 00 01 00 35 00 +00001b0 00 00 6d 01 00 00 00 00` + s = regexp.MustCompile(`[0-9a-f]{7}`).ReplaceAllString(s, "") + s = regexp.MustCompile(`\s+`).ReplaceAllString(s, "") + b, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return b +} -func (r sliceReaderAt) ReadAt(b []byte, off int64) (int, os.Error) { - copy(b, r[int(off):int(off)+len(b)]) - return len(b), nil +func returnRecursiveZip() (r io.ReaderAt, size int64) { + b := rZipBytes() + return bytes.NewReader(b), int64(len(b)) } diff --git a/src/pkg/archive/zip/struct.go b/src/pkg/archive/zip/struct.go index 1d6e70f10..55f3dcfb8 100644 --- a/src/pkg/archive/zip/struct.go +++ b/src/pkg/archive/zip/struct.go @@ -11,8 +11,11 @@ This package does not support ZIP64 or disk spanning. */ package zip -import "os" -import "time" +import ( + "errors" + "os" + "time" +) // Compression methods. const ( @@ -24,10 +27,18 @@ const ( fileHeaderSignature = 0x04034b50 directoryHeaderSignature = 0x02014b50 directoryEndSignature = 0x06054b50 - fileHeaderLen = 30 // + filename + extra - directoryHeaderLen = 46 // + filename + extra + comment - directoryEndLen = 22 // + comment - dataDescriptorLen = 12 + 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 + + // Constants for the first byte in CreatorVersion + creatorFAT = 0 + creatorUnix = 3 + creatorNTFS = 11 + creatorVFAT = 14 + creatorMacOSX = 19 ) type FileHeader struct { @@ -42,9 +53,43 @@ type FileHeader struct { CompressedSize uint32 UncompressedSize uint32 Extra []byte + ExternalAttrs uint32 // Meaning depends on CreatorVersion Comment string } +// FileInfo returns an os.FileInfo for the FileHeader. +func (h *FileHeader) FileInfo() os.FileInfo { + return headerFileInfo{h} +} + +// headerFileInfo implements os.FileInfo. +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) 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() } +func (fi headerFileInfo) Sys() interface{} { return fi.fh } + +// FileInfoHeader creates a partially-populated FileHeader from an +// 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), + } + fh.SetModTime(fi.ModTime()) + fh.SetMode(fi.Mode()) + return fh, nil +} + type directoryEnd struct { diskNbr uint16 // unused dirDiskNbr uint16 // unused @@ -56,36 +101,165 @@ type directoryEnd struct { comment string } -func recoverError(err *os.Error) { - if e := recover(); e != nil { - if osErr, ok := e.(os.Error); ok { - *err = osErr - return - } - panic(e) - } -} - // msDosTimeToTime converts an MS-DOS date and time into a time.Time. // The resolution is 2s. // See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx func msDosTimeToTime(dosDate, dosTime uint16) time.Time { - return time.Time{ + return time.Date( // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980 - Year: int64(dosDate>>9 + 1980), - Month: int(dosDate >> 5 & 0xf), - Day: int(dosDate & 0x1f), + int(dosDate>>9+1980), + time.Month(dosDate>>5&0xf), + int(dosDate&0x1f), // time bits 0-4: second/2; 5-10: minute; 11-15: hour - Hour: int(dosTime >> 11), - Minute: int(dosTime >> 5 & 0x3f), - Second: int(dosTime & 0x1f * 2), - } + int(dosTime>>11), + int(dosTime>>5&0x3f), + int(dosTime&0x1f*2), + 0, // nanoseconds + + time.UTC, + ) } -// Mtime_ns returns the modified time in ns since epoch. +// timeToMsDosTime converts a time.Time to an MS-DOS date and time. // The resolution is 2s. -func (h *FileHeader) Mtime_ns() int64 { - t := msDosTimeToTime(h.ModifiedDate, h.ModifiedTime) - return t.Seconds() * 1e9 +// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx +func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) { + t = t.In(time.UTC) + fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9) + fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11) + return +} + +// ModTime returns the modification time. +// The resolution is 2s. +func (h *FileHeader) ModTime() time.Time { + return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime) +} + +// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time. +// The resolution is 2s. +func (h *FileHeader) SetModTime(t time.Time) { + h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t) +} + +const ( + // Unix constants. The specification doesn't mention them, + // but these seem to be the values agreed on by tools. + s_IFMT = 0xf000 + s_IFSOCK = 0xc000 + s_IFLNK = 0xa000 + s_IFREG = 0x8000 + s_IFBLK = 0x6000 + s_IFDIR = 0x4000 + s_IFCHR = 0x2000 + s_IFIFO = 0x1000 + s_ISUID = 0x800 + s_ISGID = 0x400 + s_ISVTX = 0x200 + + msdosDir = 0x10 + msdosReadOnly = 0x01 +) + +// Mode returns the permission and mode bits for the FileHeader. +func (h *FileHeader) Mode() (mode os.FileMode) { + switch h.CreatorVersion >> 8 { + case creatorUnix, creatorMacOSX: + mode = unixModeToFileMode(h.ExternalAttrs >> 16) + case creatorNTFS, creatorVFAT, creatorFAT: + mode = msdosModeToFileMode(h.ExternalAttrs) + } + if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' { + mode |= os.ModeDir + } + return mode +} + +// SetMode changes the permission and mode bits for the FileHeader. +func (h *FileHeader) SetMode(mode os.FileMode) { + h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8 + h.ExternalAttrs = fileModeToUnixMode(mode) << 16 + + // set MSDOS attributes too, as the original zip does. + if mode&os.ModeDir != 0 { + h.ExternalAttrs |= msdosDir + } + if mode&0200 == 0 { + h.ExternalAttrs |= msdosReadOnly + } +} + +func msdosModeToFileMode(m uint32) (mode os.FileMode) { + if m&msdosDir != 0 { + mode = os.ModeDir | 0777 + } else { + mode = 0666 + } + if m&msdosReadOnly != 0 { + mode &^= 0222 + } + return mode +} + +func fileModeToUnixMode(mode os.FileMode) uint32 { + var m uint32 + switch mode & os.ModeType { + default: + m = s_IFREG + case os.ModeDir: + m = s_IFDIR + case os.ModeSymlink: + m = s_IFLNK + case os.ModeNamedPipe: + m = s_IFIFO + case os.ModeSocket: + m = s_IFSOCK + case os.ModeDevice: + if mode&os.ModeCharDevice != 0 { + m = s_IFCHR + } else { + m = s_IFBLK + } + } + if mode&os.ModeSetuid != 0 { + m |= s_ISUID + } + if mode&os.ModeSetgid != 0 { + m |= s_ISGID + } + if mode&os.ModeSticky != 0 { + m |= s_ISVTX + } + return m | uint32(mode&0777) +} + +func unixModeToFileMode(m uint32) os.FileMode { + mode := os.FileMode(m & 0777) + switch m & s_IFMT { + case s_IFBLK: + mode |= os.ModeDevice + case s_IFCHR: + mode |= os.ModeDevice | os.ModeCharDevice + case s_IFDIR: + mode |= os.ModeDir + case s_IFIFO: + mode |= os.ModeNamedPipe + case s_IFLNK: + mode |= os.ModeSymlink + case s_IFREG: + // nothing to do + case s_IFSOCK: + mode |= os.ModeSocket + } + if m&s_ISGID != 0 { + mode |= os.ModeSetgid + } + if m&s_ISUID != 0 { + mode |= os.ModeSetuid + } + if m&s_ISVTX != 0 { + mode |= os.ModeSticky + } + return mode } diff --git a/src/pkg/archive/zip/testdata/crc32-not-streamed.zip b/src/pkg/archive/zip/testdata/crc32-not-streamed.zip new file mode 100644 index 000000000..f268d8873 Binary files /dev/null and b/src/pkg/archive/zip/testdata/crc32-not-streamed.zip differ diff --git a/src/pkg/archive/zip/testdata/go-no-datadesc-sig.zip b/src/pkg/archive/zip/testdata/go-no-datadesc-sig.zip new file mode 100644 index 000000000..c3d593f44 Binary files /dev/null and b/src/pkg/archive/zip/testdata/go-no-datadesc-sig.zip differ diff --git a/src/pkg/archive/zip/testdata/go-with-datadesc-sig.zip b/src/pkg/archive/zip/testdata/go-with-datadesc-sig.zip new file mode 100644 index 000000000..bcfe121bb Binary files /dev/null and b/src/pkg/archive/zip/testdata/go-with-datadesc-sig.zip differ diff --git a/src/pkg/archive/zip/testdata/r.zip b/src/pkg/archive/zip/testdata/r.zip deleted file mode 100644 index ea0fa2ffc..000000000 Binary files a/src/pkg/archive/zip/testdata/r.zip and /dev/null differ diff --git a/src/pkg/archive/zip/testdata/symlink.zip b/src/pkg/archive/zip/testdata/symlink.zip new file mode 100644 index 000000000..af846938c Binary files /dev/null and b/src/pkg/archive/zip/testdata/symlink.zip differ diff --git a/src/pkg/archive/zip/testdata/unix.zip b/src/pkg/archive/zip/testdata/unix.zip new file mode 100644 index 000000000..ce1a981b2 Binary files /dev/null and b/src/pkg/archive/zip/testdata/unix.zip differ diff --git a/src/pkg/archive/zip/testdata/winxp.zip b/src/pkg/archive/zip/testdata/winxp.zip new file mode 100644 index 000000000..3919322f0 Binary files /dev/null and b/src/pkg/archive/zip/testdata/winxp.zip differ diff --git a/src/pkg/archive/zip/writer.go b/src/pkg/archive/zip/writer.go index 2065b06da..45eb6bd73 100644 --- a/src/pkg/archive/zip/writer.go +++ b/src/pkg/archive/zip/writer.go @@ -8,10 +8,10 @@ import ( "bufio" "compress/flate" "encoding/binary" + "errors" "hash" "hash/crc32" "io" - "os" ) // TODO(adg): support zip file comments @@ -19,7 +19,7 @@ import ( // Writer implements a zip file writer. type Writer struct { - *countWriter + cw *countWriter dir []*header last *fileWriter closed bool @@ -32,69 +32,81 @@ type header struct { // NewWriter returns a new Writer writing a zip file to w. func NewWriter(w io.Writer) *Writer { - return &Writer{countWriter: &countWriter{w: bufio.NewWriter(w)}} + return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}} } // Close finishes writing the zip file by writing the central directory. // It does not (and can not) close the underlying writer. -func (w *Writer) Close() (err os.Error) { +func (w *Writer) Close() error { if w.last != nil && !w.last.closed { - if err = w.last.close(); err != nil { - return + if err := w.last.close(); err != nil { + return err } w.last = nil } if w.closed { - return os.NewError("zip: writer closed twice") + return errors.New("zip: writer closed twice") } w.closed = true - defer recoverError(&err) - // write central directory - start := w.count + start := w.cw.count for _, h := range w.dir { - write(w, uint32(directoryHeaderSignature)) - write(w, h.CreatorVersion) - write(w, h.ReaderVersion) - write(w, h.Flags) - write(w, h.Method) - write(w, h.ModifiedTime) - write(w, h.ModifiedDate) - write(w, h.CRC32) - write(w, h.CompressedSize) - write(w, h.UncompressedSize) - write(w, uint16(len(h.Name))) - write(w, uint16(len(h.Extra))) - write(w, uint16(len(h.Comment))) - write(w, uint16(0)) // disk number start - write(w, uint16(0)) // internal file attributes - write(w, uint32(0)) // external file attributes - write(w, h.offset) - writeBytes(w, []byte(h.Name)) - writeBytes(w, h.Extra) - writeBytes(w, []byte(h.Comment)) + var buf [directoryHeaderLen]byte + b := writeBuf(buf[:]) + b.uint32(uint32(directoryHeaderSignature)) + b.uint16(h.CreatorVersion) + b.uint16(h.ReaderVersion) + b.uint16(h.Flags) + 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.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 _, err := w.cw.Write(buf[:]); err != nil { + return err + } + if _, err := io.WriteString(w.cw, h.Name); err != nil { + return err + } + if _, err := w.cw.Write(h.Extra); err != nil { + return err + } + if _, err := io.WriteString(w.cw, h.Comment); err != nil { + return err + } } - end := w.count + end := w.cw.count // write end record - write(w, uint32(directoryEndSignature)) - write(w, uint16(0)) // disk number - write(w, uint16(0)) // disk number where directory starts - write(w, uint16(len(w.dir))) // number of entries this disk - write(w, uint16(len(w.dir))) // number of entries total - write(w, uint32(end-start)) // size of directory - write(w, uint32(start)) // start of directory - write(w, uint16(0)) // size of comment + 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 + // skipped size of comment (always zero) + if _, err := w.cw.Write(buf[:]); err != nil { + return err + } - return w.w.(*bufio.Writer).Flush() + return w.cw.w.(*bufio.Writer).Flush() } // Create adds a file to the zip file using the provided name. // 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. -func (w *Writer) Create(name string) (io.Writer, os.Error) { +func (w *Writer) Create(name string) (io.Writer, error) { header := &FileHeader{ Name: name, Method: Deflate, @@ -107,7 +119,7 @@ func (w *Writer) Create(name string) (io.Writer, os.Error) { // 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. -func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, os.Error) { +func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { if w.last != nil && !w.last.closed { if err := w.last.close(); err != nil { return nil, err @@ -115,32 +127,36 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, os.Error) { } fh.Flags |= 0x8 // we will write a data descriptor - fh.CreatorVersion = 0x14 + fh.CreatorVersion = fh.CreatorVersion&0xff00 | 0x14 fh.ReaderVersion = 0x14 fw := &fileWriter{ - zipw: w, - compCount: &countWriter{w: w}, + zipw: w.cw, + compCount: &countWriter{w: w.cw}, crc32: crc32.NewIEEE(), } switch fh.Method { case Store: fw.comp = nopCloser{fw.compCount} case Deflate: - fw.comp = flate.NewWriter(fw.compCount, 5) + var err error + fw.comp, err = flate.NewWriter(fw.compCount, 5) + if err != nil { + return nil, err + } default: - return nil, UnsupportedMethod + return nil, ErrAlgorithm } fw.rawCount = &countWriter{w: fw.comp} h := &header{ FileHeader: fh, - offset: uint32(w.count), + offset: uint32(w.cw.count), } w.dir = append(w.dir, h) fw.header = h - if err := writeHeader(w, fh); err != nil { + if err := writeHeader(w.cw, fh); err != nil { return nil, err } @@ -148,22 +164,28 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, os.Error) { return fw, nil } -func writeHeader(w io.Writer, h *FileHeader) (err os.Error) { - defer recoverError(&err) - write(w, uint32(fileHeaderSignature)) - write(w, h.ReaderVersion) - write(w, h.Flags) - write(w, h.Method) - write(w, h.ModifiedTime) - write(w, h.ModifiedDate) - write(w, h.CRC32) - write(w, h.CompressedSize) - write(w, h.UncompressedSize) - write(w, uint16(len(h.Name))) - write(w, uint16(len(h.Extra))) - writeBytes(w, []byte(h.Name)) - writeBytes(w, h.Extra) - return nil +func writeHeader(w io.Writer, h *FileHeader) error { + var buf [fileHeaderLen]byte + b := writeBuf(buf[:]) + b.uint32(uint32(fileHeaderSignature)) + b.uint16(h.ReaderVersion) + b.uint16(h.Flags) + 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.uint16(uint16(len(h.Name))) + b.uint16(uint16(len(h.Extra))) + if _, err := w.Write(buf[:]); err != nil { + return err + } + if _, err := io.WriteString(w, h.Name); err != nil { + return err + } + _, err := w.Write(h.Extra) + return err } type fileWriter struct { @@ -176,21 +198,21 @@ type fileWriter struct { closed bool } -func (w *fileWriter) Write(p []byte) (int, os.Error) { +func (w *fileWriter) Write(p []byte) (int, error) { if w.closed { - return 0, os.NewError("zip: write to closed file") + return 0, errors.New("zip: write to closed file") } w.crc32.Write(p) return w.rawCount.Write(p) } -func (w *fileWriter) close() (err os.Error) { +func (w *fileWriter) close() error { if w.closed { - return os.NewError("zip: file closed twice") + return errors.New("zip: file closed twice") } w.closed = true - if err = w.comp.Close(); err != nil { - return + if err := w.comp.Close(); err != nil { + return err } // update FileHeader @@ -200,12 +222,14 @@ func (w *fileWriter) close() (err os.Error) { fh.UncompressedSize = uint32(w.rawCount.count) // write data descriptor - defer recoverError(&err) - write(w.zipw, fh.CRC32) - write(w.zipw, fh.CompressedSize) - write(w.zipw, fh.UncompressedSize) - - return nil + var buf [dataDescriptorLen]byte + 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[:]) + return err } type countWriter struct { @@ -213,7 +237,7 @@ type countWriter struct { count int64 } -func (w *countWriter) Write(p []byte) (int, os.Error) { +func (w *countWriter) Write(p []byte) (int, error) { n, err := w.w.Write(p) w.count += int64(n) return n, err @@ -223,22 +247,18 @@ type nopCloser struct { io.Writer } -func (w nopCloser) Close() os.Error { +func (w nopCloser) Close() error { return nil } -func write(w io.Writer, data interface{}) { - if err := binary.Write(w, binary.LittleEndian, data); err != nil { - panic(err) - } +type writeBuf []byte + +func (b *writeBuf) uint16(v uint16) { + binary.LittleEndian.PutUint16(*b, v) + *b = (*b)[2:] } -func writeBytes(w io.Writer, b []byte) { - n, err := w.Write(b) - if err != nil { - panic(err) - } - if n != len(b) { - panic(io.ErrShortWrite) - } +func (b *writeBuf) uint32(v uint32) { + binary.LittleEndian.PutUint32(*b, v) + *b = (*b)[4:] } diff --git a/src/pkg/archive/zip/writer_test.go b/src/pkg/archive/zip/writer_test.go index eb2a80c3f..8b1c4dfd2 100644 --- a/src/pkg/archive/zip/writer_test.go +++ b/src/pkg/archive/zip/writer_test.go @@ -7,54 +7,108 @@ package zip import ( "bytes" "io/ioutil" - "rand" + "math/rand" + "os" "testing" ) // TODO(adg): a more sophisticated test suite -const testString = "Rabbits, guinea pigs, gophers, marsupial rats, and quolls." +type WriteTest struct { + Name string + Data []byte + Method uint16 + Mode os.FileMode +} + +var writeTests = []WriteTest{ + { + Name: "foo", + Data: []byte("Rabbits, guinea pigs, gophers, marsupial rats, and quolls."), + Method: Store, + Mode: 0666, + }, + { + Name: "bar", + Data: nil, // large data set in the test + Method: Deflate, + Mode: 0644, + }, + { + Name: "setuid", + Data: []byte("setuid file"), + Method: Deflate, + Mode: 0755 | os.ModeSetuid, + }, + { + Name: "setgid", + Data: []byte("setgid file"), + Method: Deflate, + Mode: 0755 | os.ModeSetgid, + }, + { + Name: "symlink", + Data: []byte("../link/target"), + Method: Deflate, + Mode: 0755 | os.ModeSymlink, + }, +} func TestWriter(t *testing.T) { largeData := make([]byte, 1<<17) for i := range largeData { largeData[i] = byte(rand.Int()) } + writeTests[1].Data = largeData + defer func() { + writeTests[1].Data = nil + }() // write a zip file buf := new(bytes.Buffer) w := NewWriter(buf) - testCreate(t, w, "foo", []byte(testString), Store) - testCreate(t, w, "bar", largeData, Deflate) + + for _, wt := range writeTests { + testCreate(t, w, &wt) + } + if err := w.Close(); err != nil { t.Fatal(err) } // read it back - r, err := NewReader(sliceReaderAt(buf.Bytes()), int64(buf.Len())) + r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len())) if err != nil { t.Fatal(err) } - testReadFile(t, r.File[0], []byte(testString)) - testReadFile(t, r.File[1], largeData) + for i, wt := range writeTests { + testReadFile(t, r.File[i], &wt) + } } -func testCreate(t *testing.T, w *Writer, name string, data []byte, method uint16) { +func testCreate(t *testing.T, w *Writer, wt *WriteTest) { header := &FileHeader{ - Name: name, - Method: method, + Name: wt.Name, + Method: wt.Method, + } + if wt.Mode != 0 { + header.SetMode(wt.Mode) } f, err := w.CreateHeader(header) if err != nil { t.Fatal(err) } - _, err = f.Write(data) + _, err = f.Write(wt.Data) if err != nil { t.Fatal(err) } } -func testReadFile(t *testing.T, f *File, data []byte) { +func testReadFile(t *testing.T, f *File, wt *WriteTest) { + if f.Name != wt.Name { + t.Fatalf("File name: got %q, want %q", f.Name, wt.Name) + } + testFileMode(t, wt.Name, f, wt.Mode) rc, err := f.Open() if err != nil { t.Fatal("opening:", err) @@ -67,7 +121,7 @@ func testReadFile(t *testing.T, f *File, data []byte) { if err != nil { t.Fatal("closing:", err) } - if !bytes.Equal(b, data) { - t.Errorf("File contents %q, want %q", b, data) + if !bytes.Equal(b, wt.Data) { + t.Errorf("File contents %q, want %q", b, wt.Data) } } diff --git a/src/pkg/archive/zip/zip_test.go b/src/pkg/archive/zip/zip_test.go index 0f71fdfac..d6490c4cb 100644 --- a/src/pkg/archive/zip/zip_test.go +++ b/src/pkg/archive/zip/zip_test.go @@ -9,20 +9,12 @@ package zip import ( "bytes" "fmt" - "os" + "reflect" + "strings" "testing" + "time" ) -type stringReaderAt string - -func (s stringReaderAt) ReadAt(p []byte, off int64) (n int, err os.Error) { - if off >= int64(len(s)) { - return 0, os.EOF - } - n = copy(p, s[off:]) - return -} - func TestOver65kFiles(t *testing.T) { if testing.Short() { t.Logf("slow test; skipping") @@ -40,8 +32,8 @@ func TestOver65kFiles(t *testing.T) { if err := w.Close(); err != nil { t.Fatalf("Writer.Close: %v", err) } - rat := stringReaderAt(buf.String()) - zr, err := NewReader(rat, int64(len(rat))) + s := buf.String() + zr, err := NewReader(strings.NewReader(s), int64(len(s))) if err != nil { t.Fatalf("NewReader: %v", err) } @@ -55,3 +47,35 @@ func TestOver65kFiles(t *testing.T) { } } } + +func TestModTime(t *testing.T) { + var testTime = time.Date(2009, time.November, 10, 23, 45, 58, 0, time.UTC) + fh := new(FileHeader) + fh.SetModTime(testTime) + outTime := fh.ModTime() + if !outTime.Equal(testTime) { + t.Errorf("times don't match: got %s, want %s", outTime, testTime) + } +} + +func TestFileHeaderRoundTrip(t *testing.T) { + fh := &FileHeader{ + Name: "foo.txt", + UncompressedSize: 987654321, + ModifiedTime: 1234, + ModifiedDate: 5678, + } + fi := fh.FileInfo() + fh2, err := FileInfoHeader(fi) + + // Ignore these fields: + fh2.CreatorVersion = 0 + fh2.ExternalAttrs = 0 + + if !reflect.DeepEqual(fh, fh2) { + t.Errorf("mismatch\n input=%#v\noutput=%#v\nerr=%v", fh, fh2, err) + } + if sysfh, ok := fi.Sys().(*FileHeader); !ok && sysfh != fh { + t.Errorf("Sys didn't return original *FileHeader") + } +} diff --git a/src/pkg/asn1/Makefile b/src/pkg/asn1/Makefile deleted file mode 100644 index 6b7770e82..000000000 --- a/src/pkg/asn1/Makefile +++ /dev/null @@ -1,13 +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 ../../Make.inc - -TARG=asn1 -GOFILES=\ - asn1.go\ - common.go\ - marshal.go\ - -include ../../Make.pkg diff --git a/src/pkg/asn1/asn1.go b/src/pkg/asn1/asn1.go deleted file mode 100644 index 39b676b41..000000000 --- a/src/pkg/asn1/asn1.go +++ /dev/null @@ -1,840 +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. - -// Package asn1 implements parsing of DER-encoded ASN.1 data structures, -// as defined in ITU-T Rec X.690. -// -// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' -// http://luca.ntop.org/Teaching/Appunti/asn1.html. -package asn1 - -// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc -// are different encoding formats for those objects. Here, we'll be dealing -// with DER, the Distinguished Encoding Rules. DER is used in X.509 because -// it's fast to parse and, unlike BER, has a unique encoding for every object. -// When calculating hashes over objects, it's important that the resulting -// bytes be the same at both ends and DER removes this margin of error. -// -// ASN.1 is very complex and this package doesn't attempt to implement -// everything by any means. - -import ( - "big" - "fmt" - "os" - "reflect" - "time" -) - -// A StructuralError suggests that the ASN.1 data is valid, but the Go type -// which is receiving it doesn't match. -type StructuralError struct { - Msg string -} - -func (e StructuralError) String() string { return "ASN.1 structure error: " + e.Msg } - -// A SyntaxError suggests that the ASN.1 data is invalid. -type SyntaxError struct { - Msg string -} - -func (e SyntaxError) String() string { return "ASN.1 syntax error: " + e.Msg } - -// We start by dealing with each of the primitive types in turn. - -// BOOLEAN - -func parseBool(bytes []byte) (ret bool, err os.Error) { - if len(bytes) != 1 { - err = SyntaxError{"invalid boolean"} - return - } - - return bytes[0] != 0, nil -} - -// INTEGER - -// parseInt64 treats the given bytes as a big-endian, signed integer and -// returns the result. -func parseInt64(bytes []byte) (ret int64, err os.Error) { - if len(bytes) > 8 { - // We'll overflow an int64 in this case. - err = StructuralError{"integer too large"} - return - } - for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { - ret <<= 8 - ret |= int64(bytes[bytesRead]) - } - - // Shift up and down in order to sign extend the result. - ret <<= 64 - uint8(len(bytes))*8 - ret >>= 64 - uint8(len(bytes))*8 - return -} - -// parseInt treats the given bytes as a big-endian, signed integer and returns -// the result. -func parseInt(bytes []byte) (int, os.Error) { - ret64, err := parseInt64(bytes) - if err != nil { - return 0, err - } - if ret64 != int64(int(ret64)) { - return 0, StructuralError{"integer too large"} - } - return int(ret64), nil -} - -var bigOne = big.NewInt(1) - -// parseBigInt treats the given bytes as a big-endian, signed integer and returns -// the result. -func parseBigInt(bytes []byte) *big.Int { - ret := new(big.Int) - if len(bytes) > 0 && bytes[0]&0x80 == 0x80 { - // This is a negative number. - notBytes := make([]byte, len(bytes)) - for i := range notBytes { - notBytes[i] = ^bytes[i] - } - ret.SetBytes(notBytes) - ret.Add(ret, bigOne) - ret.Neg(ret) - return ret - } - ret.SetBytes(bytes) - return ret -} - -// BIT STRING - -// BitString is the structure to use when you want an ASN.1 BIT STRING type. A -// bit string is padded up to the nearest byte in memory and the number of -// valid bits is recorded. Padding bits will be zero. -type BitString struct { - Bytes []byte // bits packed into bytes. - BitLength int // length in bits. -} - -// At returns the bit at the given index. If the index is out of range it -// returns false. -func (b BitString) At(i int) int { - if i < 0 || i >= b.BitLength { - return 0 - } - x := i / 8 - y := 7 - uint(i%8) - return int(b.Bytes[x]>>y) & 1 -} - -// RightAlign returns a slice where the padding bits are at the beginning. The -// slice may share memory with the BitString. -func (b BitString) RightAlign() []byte { - shift := uint(8 - (b.BitLength % 8)) - if shift == 8 || len(b.Bytes) == 0 { - return b.Bytes - } - - a := make([]byte, len(b.Bytes)) - a[0] = b.Bytes[0] >> shift - for i := 1; i < len(b.Bytes); i++ { - a[i] = b.Bytes[i-1] << (8 - shift) - a[i] |= b.Bytes[i] >> shift - } - - return a -} - -// parseBitString parses an ASN.1 bit string from the given byte slice and returns it. -func parseBitString(bytes []byte) (ret BitString, err os.Error) { - if len(bytes) == 0 { - err = SyntaxError{"zero length BIT STRING"} - return - } - paddingBits := int(bytes[0]) - if paddingBits > 7 || - len(bytes) == 1 && paddingBits > 0 || - bytes[len(bytes)-1]&((1< 4 { - err = StructuralError{"base 128 integer too large"} - return - } - ret <<= 7 - b := bytes[offset] - ret |= int(b & 0x7f) - offset++ - if b&0x80 == 0 { - return - } - } - err = SyntaxError{"truncated base 128 integer"} - return -} - -// UTCTime - -func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) { - s := string(bytes) - ret, err = time.Parse("0601021504Z0700", s) - if err == nil { - return - } - ret, err = time.Parse("060102150405Z0700", s) - return -} - -// parseGeneralizedTime parses the GeneralizedTime from the given byte slice -// and returns the resulting time. -func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) { - return time.Parse("20060102150405Z0700", string(bytes)) -} - -// PrintableString - -// parsePrintableString parses a ASN.1 PrintableString from the given byte -// array and returns it. -func parsePrintableString(bytes []byte) (ret string, err os.Error) { - for _, b := range bytes { - if !isPrintable(b) { - err = SyntaxError{"PrintableString contains invalid character"} - return - } - } - ret = string(bytes) - return -} - -// isPrintable returns true iff the given b is in the ASN.1 PrintableString set. -func isPrintable(b byte) bool { - return 'a' <= b && b <= 'z' || - 'A' <= b && b <= 'Z' || - '0' <= b && b <= '9' || - '\'' <= b && b <= ')' || - '+' <= b && b <= '/' || - b == ' ' || - b == ':' || - b == '=' || - b == '?' || - // This is technically not allowed in a PrintableString. - // However, x509 certificates with wildcard strings don't - // always use the correct string type so we permit it. - b == '*' -} - -// IA5String - -// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given -// byte slice and returns it. -func parseIA5String(bytes []byte) (ret string, err os.Error) { - for _, b := range bytes { - if b >= 0x80 { - err = SyntaxError{"IA5String contains invalid character"} - return - } - } - ret = string(bytes) - return -} - -// T61String - -// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given -// byte slice and returns it. -func parseT61String(bytes []byte) (ret string, err os.Error) { - return string(bytes), nil -} - -// UTF8String - -// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte -// array and returns it. -func parseUTF8String(bytes []byte) (ret string, err os.Error) { - return string(bytes), nil -} - -// A RawValue represents an undecoded ASN.1 object. -type RawValue struct { - Class, Tag int - IsCompound bool - Bytes []byte - FullBytes []byte // includes the tag and length -} - -// RawContent is used to signal that the undecoded, DER data needs to be -// preserved for a struct. To use it, the first field of the struct must have -// this type. It's an error for any of the other fields to have this type. -type RawContent []byte - -// Tagging - -// parseTagAndLength parses an ASN.1 tag and length pair from the given offset -// into a byte slice. It returns the parsed data and the new offset. SET and -// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we -// don't distinguish between ordered and unordered objects in this code. -func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err os.Error) { - offset = initOffset - b := bytes[offset] - offset++ - ret.class = int(b >> 6) - ret.isCompound = b&0x20 == 0x20 - ret.tag = int(b & 0x1f) - - // If the bottom five bits are set, then the tag number is actually base 128 - // encoded afterwards - if ret.tag == 0x1f { - ret.tag, offset, err = parseBase128Int(bytes, offset) - if err != nil { - return - } - } - if offset >= len(bytes) { - err = SyntaxError{"truncated tag or length"} - return - } - b = bytes[offset] - offset++ - if b&0x80 == 0 { - // The length is encoded in the bottom 7 bits. - ret.length = int(b & 0x7f) - } else { - // Bottom 7 bits give the number of length bytes to follow. - numBytes := int(b & 0x7f) - // We risk overflowing a signed 32-bit number if we accept more than 3 bytes. - if numBytes > 3 { - err = StructuralError{"length too large"} - return - } - if numBytes == 0 { - err = SyntaxError{"indefinite length found (not DER)"} - return - } - ret.length = 0 - for i := 0; i < numBytes; i++ { - if offset >= len(bytes) { - err = SyntaxError{"truncated tag or length"} - return - } - b = bytes[offset] - offset++ - ret.length <<= 8 - ret.length |= int(b) - } - } - - return -} - -// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse -// a number of ASN.1 values from the given byte slice and returns them as a -// slice of Go values of the given type. -func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err os.Error) { - expectedTag, compoundType, ok := getUniversalType(elemType) - if !ok { - err = StructuralError{"unknown Go type for slice"} - return - } - - // First we iterate over the input and count the number of elements, - // checking that the types are correct in each case. - numElements := 0 - for offset := 0; offset < len(bytes); { - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so - // that a sequence of them can be parsed into a []string. - if t.tag == tagGeneralString { - t.tag = tagPrintableString - } - if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag { - err = StructuralError{"sequence tag mismatch"} - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"truncated sequence"} - return - } - offset += t.length - numElements++ - } - ret = reflect.MakeSlice(sliceType, numElements, numElements) - params := fieldParameters{} - offset := 0 - for i := 0; i < numElements; i++ { - offset, err = parseField(ret.Index(i), bytes, offset, params) - if err != nil { - return - } - } - return -} - -var ( - bitStringType = reflect.TypeOf(BitString{}) - objectIdentifierType = reflect.TypeOf(ObjectIdentifier{}) - enumeratedType = reflect.TypeOf(Enumerated(0)) - flagType = reflect.TypeOf(Flag(false)) - timeType = reflect.TypeOf(&time.Time{}) - rawValueType = reflect.TypeOf(RawValue{}) - rawContentsType = reflect.TypeOf(RawContent(nil)) - bigIntType = reflect.TypeOf(new(big.Int)) -) - -// invalidLength returns true iff offset + length > sliceLength, or if the -// addition would overflow. -func invalidLength(offset, length, sliceLength int) bool { - return offset+length < offset || offset+length > sliceLength -} - -// parseField is the main parsing function. Given a byte slice and an offset -// into the array, it will try to parse a suitable ASN.1 value out and store it -// in the given Value. -func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err os.Error) { - offset = initOffset - fieldType := v.Type() - - // If we have run out of data, it may be that there are optional elements at the end. - if offset == len(bytes) { - if !setDefaultValue(v, params) { - err = SyntaxError{"sequence truncated"} - } - return - } - - // Deal with raw values. - if fieldType == rawValueType { - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return - } - result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} - offset += t.length - v.Set(reflect.ValueOf(result)) - return - } - - // Deal with the ANY type. - if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 { - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return - } - var result interface{} - if !t.isCompound && t.class == classUniversal { - innerBytes := bytes[offset : offset+t.length] - switch t.tag { - case tagPrintableString: - result, err = parsePrintableString(innerBytes) - case tagIA5String: - result, err = parseIA5String(innerBytes) - case tagT61String: - result, err = parseT61String(innerBytes) - case tagInteger: - result, err = parseInt64(innerBytes) - case tagBitString: - result, err = parseBitString(innerBytes) - case tagOID: - result, err = parseObjectIdentifier(innerBytes) - case tagUTCTime: - result, err = parseUTCTime(innerBytes) - case tagOctetString: - result = innerBytes - default: - // If we don't know how to handle the type, we just leave Value as nil. - } - } - offset += t.length - if err != nil { - return - } - if result != nil { - v.Set(reflect.ValueOf(result)) - } - return - } - universalTag, compoundType, ok1 := getUniversalType(fieldType) - if !ok1 { - err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)} - return - } - - t, offset, err := parseTagAndLength(bytes, offset) - if err != nil { - return - } - if params.explicit { - expectedClass := classContextSpecific - if params.application { - expectedClass = classApplication - } - if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) { - if t.length > 0 { - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - } else { - if fieldType != flagType { - err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} - return - } - v.SetBool(true) - return - } - } else { - // The tags didn't match, it might be an optional element. - ok := setDefaultValue(v, params) - if ok { - offset = initOffset - } else { - err = StructuralError{"explicitly tagged member didn't match"} - } - return - } - } - - // Special case for strings: all the ASN.1 string types map to the Go - // type string. getUniversalType returns the tag for PrintableString - // when it sees a string, so if we see a different string type on the - // wire, we change the universal type to match. - if universalTag == tagPrintableString { - switch t.tag { - case tagIA5String, tagGeneralString, tagT61String, tagUTF8String: - universalTag = t.tag - } - } - - // Special case for time: UTCTime and GeneralizedTime both map to the - // Go type time.Time. - if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { - universalTag = tagGeneralizedTime - } - - expectedClass := classUniversal - expectedTag := universalTag - - if !params.explicit && params.tag != nil { - expectedClass = classContextSpecific - expectedTag = *params.tag - } - - if !params.explicit && params.application && params.tag != nil { - expectedClass = classApplication - expectedTag = *params.tag - } - - // We have unwrapped any explicit tagging at this point. - if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType { - // Tags don't match. Again, it could be an optional element. - ok := setDefaultValue(v, params) - if ok { - offset = initOffset - } else { - err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)} - } - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return - } - innerBytes := bytes[offset : offset+t.length] - offset += t.length - - // We deal with the structures defined in this package first. - switch fieldType { - case objectIdentifierType: - newSlice, err1 := parseObjectIdentifier(innerBytes) - v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice))) - if err1 == nil { - reflect.Copy(v, reflect.ValueOf(newSlice)) - } - err = err1 - return - case bitStringType: - bs, err1 := parseBitString(innerBytes) - if err1 == nil { - v.Set(reflect.ValueOf(bs)) - } - err = err1 - return - case timeType: - var time *time.Time - var err1 os.Error - if universalTag == tagUTCTime { - time, err1 = parseUTCTime(innerBytes) - } else { - time, err1 = parseGeneralizedTime(innerBytes) - } - if err1 == nil { - v.Set(reflect.ValueOf(time)) - } - err = err1 - return - case enumeratedType: - parsedInt, err1 := parseInt(innerBytes) - if err1 == nil { - v.SetInt(int64(parsedInt)) - } - err = err1 - return - case flagType: - v.SetBool(true) - return - case bigIntType: - parsedInt := parseBigInt(innerBytes) - v.Set(reflect.ValueOf(parsedInt)) - return - } - switch val := v; val.Kind() { - case reflect.Bool: - parsedBool, err1 := parseBool(innerBytes) - if err1 == nil { - val.SetBool(parsedBool) - } - 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) - } - err = err1 - return - // TODO(dfc) Add support for the remaining integer types - case reflect.Struct: - structType := fieldType - - if structType.NumField() > 0 && - structType.Field(0).Type == rawContentsType { - bytes := bytes[initOffset:offset] - val.Field(0).Set(reflect.ValueOf(RawContent(bytes))) - } - - innerOffset := 0 - for i := 0; i < structType.NumField(); i++ { - field := structType.Field(i) - if i == 0 && field.Type == rawContentsType { - continue - } - innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1"))) - if err != nil { - return - } - } - // We allow extra bytes at the end of the SEQUENCE because - // adding elements to the end has been used in X.509 as the - // version numbers have increased. - return - case reflect.Slice: - sliceType := fieldType - if sliceType.Elem().Kind() == reflect.Uint8 { - val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes))) - reflect.Copy(val, reflect.ValueOf(innerBytes)) - return - } - newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) - if err1 == nil { - val.Set(newSlice) - } - err = err1 - return - case reflect.String: - var v string - switch universalTag { - case tagPrintableString: - v, err = parsePrintableString(innerBytes) - case tagIA5String: - v, err = parseIA5String(innerBytes) - case tagT61String: - v, err = parseT61String(innerBytes) - case tagUTF8String: - v, err = parseUTF8String(innerBytes) - case tagGeneralString: - // GeneralString is specified in ISO-2022/ECMA-35, - // A brief review suggests that it includes structures - // that allow the encoding to change midstring and - // such. We give up and pass it as an 8-bit string. - v, err = parseT61String(innerBytes) - default: - err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} - } - if err == nil { - val.SetString(v) - } - return - } - err = StructuralError{"unsupported: " + v.Type().String()} - return -} - -// setDefaultValue is used to install a default value, from a tag string, into -// a Value. It is successful is the field was optional, even if a default value -// wasn't provided or it failed to install it into the Value. -func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { - if !params.optional { - return - } - ok = true - if params.defaultValue == nil { - return - } - switch val := v; val.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - val.SetInt(*params.defaultValue) - } - return -} - -// Unmarshal parses the DER-encoded ASN.1 data structure b -// and uses the reflect package to fill in an arbitrary value pointed at by val. -// Because Unmarshal uses the reflect package, the structs -// being written to must use upper case field names. -// -// An ASN.1 INTEGER can be written to an int, int32 or int64. -// If the encoded value does not fit in the Go type, -// Unmarshal returns a parse error. -// -// An ASN.1 BIT STRING can be written to a BitString. -// -// An ASN.1 OCTET STRING can be written to a []byte. -// -// An ASN.1 OBJECT IDENTIFIER can be written to an -// ObjectIdentifier. -// -// An ASN.1 ENUMERATED can be written to an Enumerated. -// -// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time. -// -// An ASN.1 PrintableString or IA5String can be written to a string. -// -// Any of the above ASN.1 values can be written to an interface{}. -// The value stored in the interface has the corresponding Go type. -// For integers, that type is int64. -// -// An ASN.1 SEQUENCE OF x or SET OF x can be written -// to a slice if an x can be written to the slice's element type. -// -// An ASN.1 SEQUENCE or SET can be written to a struct -// if each of the elements in the sequence can be -// written to the corresponding element in the struct. -// -// The following tags on struct fields have special meaning to Unmarshal: -// -// optional marks the field as ASN.1 OPTIONAL -// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC -// default:x sets the default value for optional integer fields -// -// If the type of the first field of a structure is RawContent then the raw -// ASN1 contents of the struct will be stored in it. -// -// Other ASN.1 types are not supported; if it encounters them, -// Unmarshal returns a parse error. -func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) { - return UnmarshalWithParams(b, val, "") -} - -// UnmarshalWithParams allows field parameters to be specified for the -// top-level element. The form of the params is the same as the field tags. -func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err os.Error) { - v := reflect.ValueOf(val).Elem() - offset, err := parseField(v, b, 0, parseFieldParameters(params)) - if err != nil { - return nil, err - } - return b[offset:], nil -} diff --git a/src/pkg/asn1/asn1_test.go b/src/pkg/asn1/asn1_test.go deleted file mode 100644 index 9f48f7bdd..000000000 --- a/src/pkg/asn1/asn1_test.go +++ /dev/null @@ -1,689 +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. - -package asn1 - -import ( - "bytes" - "reflect" - "testing" - "time" -) - -type int64Test struct { - in []byte - ok bool - out int64 -} - -var int64TestData = []int64Test{ - {[]byte{0x00}, true, 0}, - {[]byte{0x7f}, true, 127}, - {[]byte{0x00, 0x80}, true, 128}, - {[]byte{0x01, 0x00}, true, 256}, - {[]byte{0x80}, true, -128}, - {[]byte{0xff, 0x7f}, true, -129}, - {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, true, -1}, - {[]byte{0xff}, true, -1}, - {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, true, -9223372036854775808}, - {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, false, 0}, -} - -func TestParseInt64(t *testing.T) { - for i, test := range int64TestData { - ret, err := parseInt64(test.in) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) - } - if test.ok && ret != test.out { - t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) - } - } -} - -type int32Test struct { - in []byte - ok bool - out int32 -} - -var int32TestData = []int32Test{ - {[]byte{0x00}, true, 0}, - {[]byte{0x7f}, true, 127}, - {[]byte{0x00, 0x80}, true, 128}, - {[]byte{0x01, 0x00}, true, 256}, - {[]byte{0x80}, true, -128}, - {[]byte{0xff, 0x7f}, true, -129}, - {[]byte{0xff, 0xff, 0xff, 0xff}, true, -1}, - {[]byte{0xff}, true, -1}, - {[]byte{0x80, 0x00, 0x00, 0x00}, true, -2147483648}, - {[]byte{0x80, 0x00, 0x00, 0x00, 0x00}, false, 0}, -} - -func TestParseInt32(t *testing.T) { - for i, test := range int32TestData { - ret, err := parseInt(test.in) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) - } - if test.ok && int32(ret) != test.out { - t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) - } - } -} - -var bigIntTests = []struct { - in []byte - base10 string -}{ - {[]byte{0xff}, "-1"}, - {[]byte{0x00}, "0"}, - {[]byte{0x01}, "1"}, - {[]byte{0x00, 0xff}, "255"}, - {[]byte{0xff, 0x00}, "-256"}, - {[]byte{0x01, 0x00}, "256"}, -} - -func TestParseBigInt(t *testing.T) { - for i, test := range bigIntTests { - ret := parseBigInt(test.in) - if ret.String() != test.base10 { - t.Errorf("#%d: bad result from %x, got %s want %s", i, test.in, ret.String(), test.base10) - } - fw := newForkableWriter() - marshalBigInt(fw, ret) - result := fw.Bytes() - if !bytes.Equal(result, test.in) { - t.Errorf("#%d: got %x from marshaling %s, want %x", i, result, ret, test.in) - } - } -} - -type bitStringTest struct { - in []byte - ok bool - out []byte - bitLength int -} - -var bitStringTestData = []bitStringTest{ - {[]byte{}, false, []byte{}, 0}, - {[]byte{0x00}, true, []byte{}, 0}, - {[]byte{0x07, 0x00}, true, []byte{0x00}, 1}, - {[]byte{0x07, 0x01}, false, []byte{}, 0}, - {[]byte{0x07, 0x40}, false, []byte{}, 0}, - {[]byte{0x08, 0x00}, false, []byte{}, 0}, -} - -func TestBitString(t *testing.T) { - for i, test := range bitStringTestData { - ret, err := parseBitString(test.in) - if (err == nil) != test.ok { - 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 { - t.Errorf("#%d: Bad result: %v (expected %v %v)", i, ret, test.out, test.bitLength) - } - } - } -} - -func TestBitStringAt(t *testing.T) { - bs := BitString{[]byte{0x82, 0x40}, 16} - if bs.At(0) != 1 { - t.Error("#1: Failed") - } - if bs.At(1) != 0 { - t.Error("#2: Failed") - } - if bs.At(6) != 1 { - t.Error("#3: Failed") - } - if bs.At(9) != 1 { - t.Error("#4: Failed") - } -} - -type bitStringRightAlignTest struct { - in []byte - inlen int - out []byte -} - -var bitStringRightAlignTests = []bitStringRightAlignTest{ - {[]byte{0x80}, 1, []byte{0x01}}, - {[]byte{0x80, 0x80}, 9, []byte{0x01, 0x01}}, - {[]byte{}, 0, []byte{}}, - {[]byte{0xce}, 8, []byte{0xce}}, - {[]byte{0xce, 0x47}, 16, []byte{0xce, 0x47}}, - {[]byte{0x34, 0x50}, 12, []byte{0x03, 0x45}}, -} - -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 { - t.Errorf("#%d got: %x want: %x", i, out, test.out) - } - } -} - -type objectIdentifierTest struct { - in []byte - ok bool - out []int -} - -var objectIdentifierTestData = []objectIdentifierTest{ - {[]byte{}, false, []int{}}, - {[]byte{85}, true, []int{2, 5}}, - {[]byte{85, 0x02}, true, []int{2, 5, 2}}, - {[]byte{85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}}, - {[]byte{85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}}, -} - -func TestObjectIdentifier(t *testing.T) { - for i, test := range objectIdentifierTestData { - ret, err := parseObjectIdentifier(test.in) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) - } - if err == nil { - if !reflect.DeepEqual(test.out, ret) { - t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) - } - } - } -} - -type timeTest struct { - in string - ok bool - out *time.Time -} - -var utcTestData = []timeTest{ - {"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 0, -7 * 60 * 60, ""}}, - {"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 0, 7*60*60 + 30*60, ""}}, - {"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, 0, "UTC"}}, - {"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, 0, "UTC"}}, - {"a10506234540Z", false, nil}, - {"91a506234540Z", false, nil}, - {"9105a6234540Z", false, nil}, - {"910506a34540Z", false, nil}, - {"910506334a40Z", false, nil}, - {"91050633444aZ", false, nil}, - {"910506334461Z", false, nil}, - {"910506334400Za", false, nil}, -} - -func TestUTCTime(t *testing.T) { - for i, test := range utcTestData { - ret, err := parseUTCTime([]byte(test.in)) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) - } - if err == nil { - if !reflect.DeepEqual(test.out, ret) { - t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) - } - } - } -} - -var generalizedTimeTestData = []timeTest{ - {"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, 0, "UTC"}}, - {"20100102030405", false, nil}, - {"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, 6*60*60 + 7*60, ""}}, - {"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, -6*60*60 - 7*60, ""}}, -} - -func TestGeneralizedTime(t *testing.T) { - for i, test := range generalizedTimeTestData { - ret, err := parseGeneralizedTime([]byte(test.in)) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) - } - if err == nil { - if !reflect.DeepEqual(test.out, ret) { - t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) - } - } - } -} - -type tagAndLengthTest struct { - in []byte - ok bool - out tagAndLength -} - -var tagAndLengthData = []tagAndLengthTest{ - {[]byte{0x80, 0x01}, true, tagAndLength{2, 0, 1, false}}, - {[]byte{0xa0, 0x01}, true, tagAndLength{2, 0, 1, true}}, - {[]byte{0x02, 0x00}, true, tagAndLength{0, 2, 0, false}}, - {[]byte{0xfe, 0x00}, true, tagAndLength{3, 30, 0, true}}, - {[]byte{0x1f, 0x01, 0x00}, true, tagAndLength{0, 1, 0, false}}, - {[]byte{0x1f, 0x81, 0x00, 0x00}, true, tagAndLength{0, 128, 0, false}}, - {[]byte{0x1f, 0x81, 0x80, 0x01, 0x00}, true, tagAndLength{0, 0x4001, 0, false}}, - {[]byte{0x00, 0x81, 0x01}, true, tagAndLength{0, 0, 1, false}}, - {[]byte{0x00, 0x82, 0x01, 0x00}, true, tagAndLength{0, 0, 256, false}}, - {[]byte{0x00, 0x83, 0x01, 0x00}, false, tagAndLength{}}, - {[]byte{0x1f, 0x85}, false, tagAndLength{}}, - {[]byte{0x30, 0x80}, false, tagAndLength{}}, -} - -func TestParseTagAndLength(t *testing.T) { - for i, test := range tagAndLengthData { - tagAndLength, _, err := parseTagAndLength(test.in, 0) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did pass? %v, expected: %v)", i, err == nil, test.ok) - } - if err == nil && !reflect.DeepEqual(test.out, tagAndLength) { - t.Errorf("#%d: Bad result: %v (expected %v)", i, tagAndLength, test.out) - } - } -} - -type parseFieldParametersTest struct { - in string - out fieldParameters -} - -func newInt(n int) *int { return &n } - -func newInt64(n int64) *int64 { return &n } - -func newString(s string) *string { return &s } - -func newBool(b bool) *bool { return &b } - -var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParametersTest{ - {"", fieldParameters{}}, - {"ia5", fieldParameters{stringType: tagIA5String}}, - {"printable", fieldParameters{stringType: tagPrintableString}}, - {"optional", fieldParameters{optional: true}}, - {"explicit", fieldParameters{explicit: true, tag: new(int)}}, - {"application", fieldParameters{application: true, tag: new(int)}}, - {"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}}, - {"default:42", fieldParameters{defaultValue: newInt64(42)}}, - {"tag:17", fieldParameters{tag: newInt(17)}}, - {"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}}, - {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, false}}, - {"set", fieldParameters{set: true}}, -} - -func TestParseFieldParameters(t *testing.T) { - for i, test := range parseFieldParametersTestData { - f := parseFieldParameters(test.in) - if !reflect.DeepEqual(f, test.out) { - t.Errorf("#%d: Bad result: %v (expected %v)", i, f, test.out) - } - } -} - -type TestObjectIdentifierStruct struct { - OID ObjectIdentifier -} - -type TestContextSpecificTags struct { - A int `asn1:"tag:1"` -} - -type TestContextSpecificTags2 struct { - A int `asn1:"explicit,tag:1"` - B int -} - -type TestElementsAfterString struct { - S string - A, B int -} - -var unmarshalTestData = []struct { - in []byte - out interface{} -}{ - {[]byte{0x02, 0x01, 0x42}, newInt(0x42)}, - {[]byte{0x30, 0x08, 0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d}, &TestObjectIdentifierStruct{[]int{1, 2, 840, 113549}}}, - {[]byte{0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0}, &BitString{[]byte{110, 93, 192}, 18}}, - {[]byte{0x30, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &[]int{1, 2, 3}}, - {[]byte{0x02, 0x01, 0x10}, newInt(16)}, - {[]byte{0x13, 0x04, 't', 'e', 's', 't'}, newString("test")}, - {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, newString("test")}, - {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, &RawValue{0, 22, false, []byte("test"), []byte("\x16\x04test")}}, - {[]byte{0x04, 0x04, 1, 2, 3, 4}, &RawValue{0, 4, false, []byte{1, 2, 3, 4}, []byte{4, 4, 1, 2, 3, 4}}}, - {[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}}, - {[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}}, - {[]byte{0x01, 0x01, 0x00}, newBool(false)}, - {[]byte{0x01, 0x01, 0x01}, newBool(true)}, - {[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}}, -} - -func TestUnmarshal(t *testing.T) { - for i, test := range unmarshalTestData { - pv := reflect.New(reflect.TypeOf(test.out).Elem()) - val := pv.Interface() - _, err := Unmarshal(test.in, val) - if err != nil { - t.Errorf("Unmarshal failed at index %d %v", i, err) - } - if !reflect.DeepEqual(val, test.out) { - t.Errorf("#%d:\nhave %#v\nwant %#v", i, val, test.out) - } - } -} - -type Certificate struct { - TBSCertificate TBSCertificate - SignatureAlgorithm AlgorithmIdentifier - SignatureValue BitString -} - -type TBSCertificate struct { - Version int `asn1:"optional,explicit,default:0,tag:0"` - SerialNumber RawValue - SignatureAlgorithm AlgorithmIdentifier - Issuer RDNSequence - Validity Validity - Subject RDNSequence - PublicKey PublicKeyInfo -} - -type AlgorithmIdentifier struct { - Algorithm ObjectIdentifier -} - -type RDNSequence []RelativeDistinguishedNameSET - -type RelativeDistinguishedNameSET []AttributeTypeAndValue - -type AttributeTypeAndValue struct { - Type ObjectIdentifier - Value interface{} -} - -type Validity struct { - NotBefore, NotAfter *time.Time -} - -type PublicKeyInfo struct { - Algorithm AlgorithmIdentifier - PublicKey BitString -} - -func TestCertificate(t *testing.T) { - // This is a minimal, self-signed certificate that should parse correctly. - var cert Certificate - if _, err := Unmarshal(derEncodedSelfSignedCertBytes, &cert); err != nil { - t.Errorf("Unmarshal failed: %v", err) - } - if !reflect.DeepEqual(cert, derEncodedSelfSignedCert) { - t.Errorf("Bad result:\ngot: %+v\nwant: %+v", cert, derEncodedSelfSignedCert) - } -} - -func TestCertificateWithNUL(t *testing.T) { - // This is the paypal NUL-hack certificate. It should fail to parse because - // NUL isn't a permitted character in a PrintableString. - - var cert Certificate - if _, err := Unmarshal(derEncodedPaypalNULCertBytes, &cert); err == nil { - t.Error("Unmarshal succeeded, should not have") - } -} - -type rawStructTest struct { - Raw RawContent - A int -} - -func TestRawStructs(t *testing.T) { - var s rawStructTest - input := []byte{0x30, 0x03, 0x02, 0x01, 0x50} - - rest, err := Unmarshal(input, &s) - if len(rest) != 0 { - t.Errorf("incomplete parse: %x", rest) - return - } - if err != nil { - t.Error(err) - return - } - 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 { - t.Errorf("bad value for Raw: got %x want %x", s.Raw, input) - } -} - -var derEncodedSelfSignedCert = Certificate{ - TBSCertificate: TBSCertificate{ - Version: 0, - SerialNumber: RawValue{Class: 0, Tag: 2, IsCompound: false, Bytes: []uint8{0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}, FullBytes: []byte{2, 9, 0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}}, - SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}}, - Issuer: RDNSequence{ - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 7}, Value: "City"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 10}, Value: "Internet Widgits Pty Ltd"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, - }, - Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}}, - Subject: RDNSequence{ - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 7}, Value: "City"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 10}, Value: "Internet Widgits Pty Ltd"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, - }, - PublicKey: PublicKeyInfo{ - Algorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}}, - PublicKey: BitString{ - Bytes: []uint8{ - 0x30, 0x48, 0x2, 0x41, 0x0, 0xcd, 0xb7, - 0x63, 0x9c, 0x32, 0x78, 0xf0, 0x6, 0xaa, 0x27, 0x7f, 0x6e, 0xaf, 0x42, - 0x90, 0x2b, 0x59, 0x2d, 0x8c, 0xbc, 0xbe, 0x38, 0xa1, 0xc9, 0x2b, 0xa4, - 0x69, 0x5a, 0x33, 0x1b, 0x1d, 0xea, 0xde, 0xad, 0xd8, 0xe9, 0xa5, 0xc2, - 0x7e, 0x8c, 0x4c, 0x2f, 0xd0, 0xa8, 0x88, 0x96, 0x57, 0x72, 0x2a, 0x4f, - 0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45, 0xdc, 0x8f, 0xde, 0xec, - 0x35, 0x7d, 0x2, 0x3, 0x1, 0x0, 0x1, - }, - BitLength: 592, - }, - }, - }, - SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}}, - SignatureValue: BitString{ - Bytes: []uint8{ - 0xa6, 0x7b, 0x6, 0xec, 0x5e, 0xce, - 0x92, 0x77, 0x2c, 0xa4, 0x13, 0xcb, 0xa3, 0xca, 0x12, 0x56, 0x8f, 0xdc, 0x6c, - 0x7b, 0x45, 0x11, 0xcd, 0x40, 0xa7, 0xf6, 0x59, 0x98, 0x4, 0x2, 0xdf, 0x2b, - 0x99, 0x8b, 0xb9, 0xa4, 0xa8, 0xcb, 0xeb, 0x34, 0xc0, 0xf0, 0xa7, 0x8c, 0xf8, - 0xd9, 0x1e, 0xde, 0x14, 0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa, - 0xfa, 0x88, 0x21, 0x49, 0x4, 0x35, - }, - BitLength: 512, - }, -} - -var derEncodedSelfSignedCertBytes = []byte{ - 0x30, 0x82, 0x02, 0x18, 0x30, - 0x82, 0x01, 0xc2, 0x02, 0x09, 0x00, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, - 0x98, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, - 0x05, 0x05, 0x00, 0x30, 0x81, 0x92, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x58, 0x58, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, - 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x04, 0x43, - 0x69, 0x74, 0x79, 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, 0x31, - 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x66, 0x61, 0x6c, - 0x73, 0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, - 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x40, 0x65, 0x78, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, - 0x30, 0x39, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x35, 0x35, 0x33, 0x5a, - 0x17, 0x0d, 0x31, 0x30, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x35, 0x35, - 0x33, 0x5a, 0x30, 0x81, 0x92, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, - 0x06, 0x13, 0x02, 0x58, 0x58, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, - 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x04, 0x43, 0x69, - 0x74, 0x79, 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, 0x31, 0x1a, - 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x66, 0x61, 0x6c, 0x73, - 0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, - 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, - 0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x40, 0x65, 0x78, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 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, 0xcd, 0xb7, 0x63, 0x9c, 0x32, 0x78, - 0xf0, 0x06, 0xaa, 0x27, 0x7f, 0x6e, 0xaf, 0x42, 0x90, 0x2b, 0x59, 0x2d, 0x8c, - 0xbc, 0xbe, 0x38, 0xa1, 0xc9, 0x2b, 0xa4, 0x69, 0x5a, 0x33, 0x1b, 0x1d, 0xea, - 0xde, 0xad, 0xd8, 0xe9, 0xa5, 0xc2, 0x7e, 0x8c, 0x4c, 0x2f, 0xd0, 0xa8, 0x88, - 0x96, 0x57, 0x72, 0x2a, 0x4f, 0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45, - 0xdc, 0x8f, 0xde, 0xec, 0x35, 0x7d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, - 0x03, 0x41, 0x00, 0xa6, 0x7b, 0x06, 0xec, 0x5e, 0xce, 0x92, 0x77, 0x2c, 0xa4, - 0x13, 0xcb, 0xa3, 0xca, 0x12, 0x56, 0x8f, 0xdc, 0x6c, 0x7b, 0x45, 0x11, 0xcd, - 0x40, 0xa7, 0xf6, 0x59, 0x98, 0x04, 0x02, 0xdf, 0x2b, 0x99, 0x8b, 0xb9, 0xa4, - 0xa8, 0xcb, 0xeb, 0x34, 0xc0, 0xf0, 0xa7, 0x8c, 0xf8, 0xd9, 0x1e, 0xde, 0x14, - 0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa, 0xfa, 0x88, 0x21, 0x49, - 0x04, 0x35, -} - -var derEncodedPaypalNULCertBytes = []byte{ - 0x30, 0x82, 0x06, 0x44, 0x30, - 0x82, 0x05, 0xad, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x00, 0xf0, 0x9b, - 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, - 0x05, 0x00, 0x30, 0x82, 0x01, 0x12, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x45, 0x53, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, - 0x04, 0x08, 0x13, 0x09, 0x42, 0x61, 0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61, - 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x42, 0x61, - 0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, - 0x55, 0x04, 0x0a, 0x13, 0x20, 0x49, 0x50, 0x53, 0x20, 0x43, 0x65, 0x72, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x73, 0x2e, 0x6c, 0x2e, 0x31, 0x2e, - 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x14, 0x25, 0x67, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, - 0x20, 0x43, 0x2e, 0x49, 0x2e, 0x46, 0x2e, 0x20, 0x20, 0x42, 0x2d, 0x42, 0x36, - 0x32, 0x32, 0x31, 0x30, 0x36, 0x39, 0x35, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, - 0x55, 0x04, 0x0b, 0x13, 0x25, 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c, - 0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, - 0x25, 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, - 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, - 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, - 0x01, 0x16, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70, - 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, - 0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a, 0x17, 0x0d, - 0x31, 0x31, 0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a, - 0x30, 0x81, 0x94, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, - 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16, - 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0d, 0x53, 0x61, 0x6e, 0x20, - 0x46, 0x72, 0x61, 0x6e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x31, 0x11, 0x30, 0x0f, - 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, - 0x74, 0x79, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0b, - 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x55, 0x6e, 0x69, 0x74, 0x31, 0x2f, - 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x77, 0x77, 0x77, 0x2e, - 0x70, 0x61, 0x79, 0x70, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x73, 0x73, - 0x6c, 0x2e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x63, 0x63, 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, 0xd2, 0x69, - 0xfa, 0x6f, 0x3a, 0x00, 0xb4, 0x21, 0x1b, 0xc8, 0xb1, 0x02, 0xd7, 0x3f, 0x19, - 0xb2, 0xc4, 0x6d, 0xb4, 0x54, 0xf8, 0x8b, 0x8a, 0xcc, 0xdb, 0x72, 0xc2, 0x9e, - 0x3c, 0x60, 0xb9, 0xc6, 0x91, 0x3d, 0x82, 0xb7, 0x7d, 0x99, 0xff, 0xd1, 0x29, - 0x84, 0xc1, 0x73, 0x53, 0x9c, 0x82, 0xdd, 0xfc, 0x24, 0x8c, 0x77, 0xd5, 0x41, - 0xf3, 0xe8, 0x1e, 0x42, 0xa1, 0xad, 0x2d, 0x9e, 0xff, 0x5b, 0x10, 0x26, 0xce, - 0x9d, 0x57, 0x17, 0x73, 0x16, 0x23, 0x38, 0xc8, 0xd6, 0xf1, 0xba, 0xa3, 0x96, - 0x5b, 0x16, 0x67, 0x4a, 0x4f, 0x73, 0x97, 0x3a, 0x4d, 0x14, 0xa4, 0xf4, 0xe2, - 0x3f, 0x8b, 0x05, 0x83, 0x42, 0xd1, 0xd0, 0xdc, 0x2f, 0x7a, 0xe5, 0xb6, 0x10, - 0xb2, 0x11, 0xc0, 0xdc, 0x21, 0x2a, 0x90, 0xff, 0xae, 0x97, 0x71, 0x5a, 0x49, - 0x81, 0xac, 0x40, 0xf3, 0x3b, 0xb8, 0x59, 0xb2, 0x4f, 0x02, 0x03, 0x01, 0x00, - 0x01, 0xa3, 0x82, 0x03, 0x21, 0x30, 0x82, 0x03, 0x1d, 0x30, 0x09, 0x06, 0x03, - 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x11, 0x06, 0x09, 0x60, 0x86, - 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, 0x06, 0x40, - 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xf8, - 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08, - 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x1d, 0x06, 0x03, 0x55, - 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x61, 0x8f, 0x61, 0x34, 0x43, 0x55, 0x14, - 0x7f, 0x27, 0x09, 0xce, 0x4c, 0x8b, 0xea, 0x9b, 0x7b, 0x19, 0x25, 0xbc, 0x6e, - 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, - 0x0e, 0x07, 0x60, 0xd4, 0x39, 0xc9, 0x1b, 0x5b, 0x5d, 0x90, 0x7b, 0x23, 0xc8, - 0xd2, 0x34, 0x9d, 0x4a, 0x9a, 0x46, 0x39, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, - 0x11, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, - 0x15, 0x30, 0x13, 0x81, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40, - 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x72, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d, 0x04, 0x65, 0x16, 0x63, - 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e, - 0x4f, 0x54, 0x20, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, 0x44, 0x2e, - 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x68, - 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, - 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x2f, 0x06, 0x09, 0x60, - 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x02, 0x04, 0x22, 0x16, 0x20, 0x68, - 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, - 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, - 0x32, 0x30, 0x30, 0x32, 0x2f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, - 0x86, 0xf8, 0x42, 0x01, 0x04, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70, - 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, - 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x43, 0x4c, - 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x03, 0x04, 0x39, 0x16, 0x37, - 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, - 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, - 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x72, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74, - 0x6d, 0x6c, 0x3f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, - 0x42, 0x01, 0x07, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, - 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, - 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, - 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x3f, 0x30, 0x41, 0x06, 0x09, 0x60, 0x86, - 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x08, 0x04, 0x34, 0x16, 0x32, 0x68, 0x74, - 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, - 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, - 0x30, 0x30, 0x32, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x4c, 0x41, - 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x30, 0x81, 0x83, 0x06, - 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x7c, 0x30, 0x7a, 0x30, 0x39, 0xa0, 0x37, 0xa0, - 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, - 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, - 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, - 0x32, 0x30, 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, - 0x72, 0x6c, 0x30, 0x3d, 0xa0, 0x3b, 0xa0, 0x39, 0x86, 0x37, 0x68, 0x74, 0x74, - 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x69, - 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, - 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, - 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c, - 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, - 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, - 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, - 0x73, 0x70, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, - 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x68, 0xee, 0x79, 0x97, 0x97, 0xdd, 0x3b, - 0xef, 0x16, 0x6a, 0x06, 0xf2, 0x14, 0x9a, 0x6e, 0xcd, 0x9e, 0x12, 0xf7, 0xaa, - 0x83, 0x10, 0xbd, 0xd1, 0x7c, 0x98, 0xfa, 0xc7, 0xae, 0xd4, 0x0e, 0x2c, 0x9e, - 0x38, 0x05, 0x9d, 0x52, 0x60, 0xa9, 0x99, 0x0a, 0x81, 0xb4, 0x98, 0x90, 0x1d, - 0xae, 0xbb, 0x4a, 0xd7, 0xb9, 0xdc, 0x88, 0x9e, 0x37, 0x78, 0x41, 0x5b, 0xf7, - 0x82, 0xa5, 0xf2, 0xba, 0x41, 0x25, 0x5a, 0x90, 0x1a, 0x1e, 0x45, 0x38, 0xa1, - 0x52, 0x58, 0x75, 0x94, 0x26, 0x44, 0xfb, 0x20, 0x07, 0xba, 0x44, 0xcc, 0xe5, - 0x4a, 0x2d, 0x72, 0x3f, 0x98, 0x47, 0xf6, 0x26, 0xdc, 0x05, 0x46, 0x05, 0x07, - 0x63, 0x21, 0xab, 0x46, 0x9b, 0x9c, 0x78, 0xd5, 0x54, 0x5b, 0x3d, 0x0c, 0x1e, - 0xc8, 0x64, 0x8c, 0xb5, 0x50, 0x23, 0x82, 0x6f, 0xdb, 0xb8, 0x22, 0x1c, 0x43, - 0x96, 0x07, 0xa8, 0xbb, -} diff --git a/src/pkg/asn1/common.go b/src/pkg/asn1/common.go deleted file mode 100644 index 01f4f7b6e..000000000 --- a/src/pkg/asn1/common.go +++ /dev/null @@ -1,158 +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. - -package asn1 - -import ( - "reflect" - "strconv" - "strings" -) - -// ASN.1 objects have metadata preceding them: -// the tag: the type of the object -// a flag denoting if this object is compound or not -// the class type: the namespace of the tag -// the length of the object, in bytes - -// Here are some standard tags and classes - -const ( - tagBoolean = 1 - tagInteger = 2 - tagBitString = 3 - tagOctetString = 4 - tagOID = 6 - tagEnum = 10 - tagUTF8String = 12 - tagSequence = 16 - tagSet = 17 - tagPrintableString = 19 - tagT61String = 20 - tagIA5String = 22 - tagUTCTime = 23 - tagGeneralizedTime = 24 - tagGeneralString = 27 -) - -const ( - classUniversal = 0 - classApplication = 1 - classContextSpecific = 2 - classPrivate = 3 -) - -type tagAndLength struct { - class, tag, length int - isCompound bool -} - -// ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead -// of" and "in addition to". When not specified, every primitive type has a -// default tag in the UNIVERSAL class. -// -// For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1 -// doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT -// CONTEXT-SPECIFIC 42], that means that the tag is replaced by another. -// -// On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an -// /additional/ tag would wrap the default tag. This explicit tag will have the -// compound flag set. -// -// (This is used in order to remove ambiguity with optional elements.) -// -// You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we -// don't support that here. We support a single layer of EXPLICIT or IMPLICIT -// tagging with tag strings on the fields of a structure. - -// fieldParameters is the parsed representation of tag string from a structure field. -type fieldParameters struct { - optional bool // true iff the field is OPTIONAL - explicit bool // true iff an EXPLICIT tag is in use. - application bool // true iff an APPLICATION tag is in use. - defaultValue *int64 // a default value for INTEGER typed fields (maybe nil). - tag *int // the EXPLICIT or IMPLICIT tag (maybe nil). - stringType int // the string tag to use when marshaling. - set bool // true iff this should be encoded as a SET - - // Invariants: - // if explicit is set, tag is non-nil. -} - -// Given a tag string with the format specified in the package comment, -// parseFieldParameters will parse it into a fieldParameters structure, -// ignoring unknown parts of the string. -func parseFieldParameters(str string) (ret fieldParameters) { - for _, part := range strings.Split(str, ",") { - switch { - case part == "optional": - ret.optional = true - case part == "explicit": - ret.explicit = true - if ret.tag == nil { - ret.tag = new(int) - } - case part == "ia5": - ret.stringType = tagIA5String - case part == "printable": - ret.stringType = tagPrintableString - case strings.HasPrefix(part, "default:"): - i, err := strconv.Atoi64(part[8:]) - if err == nil { - ret.defaultValue = new(int64) - *ret.defaultValue = i - } - case strings.HasPrefix(part, "tag:"): - i, err := strconv.Atoi(part[4:]) - if err == nil { - ret.tag = new(int) - *ret.tag = i - } - case part == "set": - ret.set = true - case part == "application": - ret.application = true - if ret.tag == nil { - ret.tag = new(int) - } - } - } - return -} - -// Given a reflected Go type, getUniversalType returns the default tag number -// and expected compound flag. -func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) { - switch t { - case objectIdentifierType: - return tagOID, false, true - case bitStringType: - return tagBitString, false, true - case timeType: - return tagUTCTime, false, true - case enumeratedType: - return tagEnum, false, true - case bigIntType: - return tagInteger, false, true - } - switch t.Kind() { - case reflect.Bool: - return tagBoolean, false, true - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return tagInteger, false, true - case reflect.Struct: - return tagSequence, true, true - case reflect.Slice: - if t.Elem().Kind() == reflect.Uint8 { - return tagOctetString, false, true - } - if strings.HasSuffix(t.Name(), "SET") { - return tagSet, true, true - } - return tagSequence, true, true - case reflect.String: - return tagPrintableString, false, true - } - return 0, false, false -} diff --git a/src/pkg/asn1/marshal.go b/src/pkg/asn1/marshal.go deleted file mode 100644 index d7eb63bf8..000000000 --- a/src/pkg/asn1/marshal.go +++ /dev/null @@ -1,544 +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. - -package asn1 - -import ( - "big" - "bytes" - "fmt" - "io" - "os" - "reflect" - "time" -) - -// A forkableWriter is an in-memory buffer that can be -// 'forked' to create new forkableWriters that bracket the -// original. After -// pre, post := w.fork(); -// the overall sequence of bytes represented is logically w+pre+post. -type forkableWriter struct { - *bytes.Buffer - pre, post *forkableWriter -} - -func newForkableWriter() *forkableWriter { - return &forkableWriter{bytes.NewBuffer(nil), nil, nil} -} - -func (f *forkableWriter) fork() (pre, post *forkableWriter) { - if f.pre != nil || f.post != nil { - panic("have already forked") - } - f.pre = newForkableWriter() - f.post = newForkableWriter() - return f.pre, f.post -} - -func (f *forkableWriter) Len() (l int) { - l += f.Buffer.Len() - if f.pre != nil { - l += f.pre.Len() - } - if f.post != nil { - l += f.post.Len() - } - return -} - -func (f *forkableWriter) writeTo(out io.Writer) (n int, err os.Error) { - n, err = out.Write(f.Bytes()) - if err != nil { - return - } - - var nn int - - if f.pre != nil { - nn, err = f.pre.writeTo(out) - n += nn - if err != nil { - return - } - } - - if f.post != nil { - nn, err = f.post.writeTo(out) - n += nn - } - return -} - -func marshalBase128Int(out *forkableWriter, n int64) (err os.Error) { - if n == 0 { - err = out.WriteByte(0) - return - } - - l := 0 - for i := n; i > 0; i >>= 7 { - l++ - } - - for i := l - 1; i >= 0; i-- { - o := byte(n >> uint(i*7)) - o &= 0x7f - if i != 0 { - o |= 0x80 - } - err = out.WriteByte(o) - if err != nil { - return - } - } - - return nil -} - -func marshalInt64(out *forkableWriter, i int64) (err os.Error) { - n := int64Length(i) - - for ; n > 0; n-- { - err = out.WriteByte(byte(i >> uint((n-1)*8))) - if err != nil { - return - } - } - - return nil -} - -func int64Length(i int64) (numBytes int) { - numBytes = 1 - - for i > 127 { - numBytes++ - i >>= 8 - } - - for i < -128 { - numBytes++ - i >>= 8 - } - - return -} - -func marshalBigInt(out *forkableWriter, n *big.Int) (err os.Error) { - if n.Sign() < 0 { - // A negative number has to be converted to two's-complement - // form. So we'll subtract 1 and invert. If the - // most-significant-bit isn't set then we'll need to pad the - // beginning with 0xff in order to keep the number negative. - nMinus1 := new(big.Int).Neg(n) - nMinus1.Sub(nMinus1, bigOne) - bytes := nMinus1.Bytes() - for i := range bytes { - bytes[i] ^= 0xff - } - if len(bytes) == 0 || bytes[0]&0x80 == 0 { - err = out.WriteByte(0xff) - if err != nil { - return - } - } - _, err = out.Write(bytes) - } else if n.Sign() == 0 { - // Zero is written as a single 0 zero rather than no bytes. - err = out.WriteByte(0x00) - } else { - bytes := n.Bytes() - if len(bytes) > 0 && bytes[0]&0x80 != 0 { - // We'll have to pad this with 0x00 in order to stop it - // looking like a negative number. - err = out.WriteByte(0) - if err != nil { - return - } - } - _, err = out.Write(bytes) - } - return -} - -func marshalLength(out *forkableWriter, i int) (err os.Error) { - n := lengthLength(i) - - for ; n > 0; n-- { - err = out.WriteByte(byte(i >> uint((n-1)*8))) - if err != nil { - return - } - } - - return nil -} - -func lengthLength(i int) (numBytes int) { - numBytes = 1 - for i > 255 { - numBytes++ - i >>= 8 - } - return -} - -func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err os.Error) { - b := uint8(t.class) << 6 - if t.isCompound { - b |= 0x20 - } - if t.tag >= 31 { - b |= 0x1f - err = out.WriteByte(b) - if err != nil { - return - } - err = marshalBase128Int(out, int64(t.tag)) - if err != nil { - return - } - } else { - b |= uint8(t.tag) - err = out.WriteByte(b) - if err != nil { - return - } - } - - if t.length >= 128 { - l := lengthLength(t.length) - err = out.WriteByte(0x80 | byte(l)) - if err != nil { - return - } - err = marshalLength(out, t.length) - if err != nil { - return - } - } else { - err = out.WriteByte(byte(t.length)) - if err != nil { - return - } - } - - return nil -} - -func marshalBitString(out *forkableWriter, b BitString) (err os.Error) { - paddingBits := byte((8 - b.BitLength%8) % 8) - err = out.WriteByte(paddingBits) - if err != nil { - return - } - _, err = out.Write(b.Bytes) - return -} - -func marshalObjectIdentifier(out *forkableWriter, oid []int) (err os.Error) { - if len(oid) < 2 || oid[0] > 6 || oid[1] >= 40 { - return StructuralError{"invalid object identifier"} - } - - err = out.WriteByte(byte(oid[0]*40 + oid[1])) - if err != nil { - return - } - for i := 2; i < len(oid); i++ { - err = marshalBase128Int(out, int64(oid[i])) - if err != nil { - return - } - } - - return -} - -func marshalPrintableString(out *forkableWriter, s string) (err os.Error) { - b := []byte(s) - for _, c := range b { - if !isPrintable(c) { - return StructuralError{"PrintableString contains invalid character"} - } - } - - _, err = out.Write(b) - return -} - -func marshalIA5String(out *forkableWriter, s string) (err os.Error) { - b := []byte(s) - for _, c := range b { - if c > 127 { - return StructuralError{"IA5String contains invalid character"} - } - } - - _, err = out.Write(b) - return -} - -func marshalTwoDigits(out *forkableWriter, v int) (err os.Error) { - err = out.WriteByte(byte('0' + (v/10)%10)) - if err != nil { - return - } - return out.WriteByte(byte('0' + v%10)) -} - -func marshalUTCTime(out *forkableWriter, t *time.Time) (err os.Error) { - switch { - case 1950 <= t.Year && t.Year < 2000: - err = marshalTwoDigits(out, int(t.Year-1900)) - case 2000 <= t.Year && t.Year < 2050: - err = marshalTwoDigits(out, int(t.Year-2000)) - default: - return StructuralError{"Cannot represent time as UTCTime"} - } - - if err != nil { - return - } - - err = marshalTwoDigits(out, t.Month) - if err != nil { - return - } - - err = marshalTwoDigits(out, t.Day) - if err != nil { - return - } - - err = marshalTwoDigits(out, t.Hour) - if err != nil { - return - } - - err = marshalTwoDigits(out, t.Minute) - if err != nil { - return - } - - err = marshalTwoDigits(out, t.Second) - if err != nil { - return - } - - switch { - case t.ZoneOffset/60 == 0: - err = out.WriteByte('Z') - return - case t.ZoneOffset > 0: - err = out.WriteByte('+') - case t.ZoneOffset < 0: - err = out.WriteByte('-') - } - - if err != nil { - return - } - - offsetMinutes := t.ZoneOffset / 60 - if offsetMinutes < 0 { - offsetMinutes = -offsetMinutes - } - - err = marshalTwoDigits(out, offsetMinutes/60) - if err != nil { - return - } - - err = marshalTwoDigits(out, offsetMinutes%60) - return -} - -func stripTagAndLength(in []byte) []byte { - _, offset, err := parseTagAndLength(in, 0) - if err != nil { - return in - } - return in[offset:] -} - -func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err os.Error) { - switch value.Type() { - case timeType: - return marshalUTCTime(out, value.Interface().(*time.Time)) - case bitStringType: - return marshalBitString(out, value.Interface().(BitString)) - case objectIdentifierType: - return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier)) - case bigIntType: - return marshalBigInt(out, value.Interface().(*big.Int)) - } - - switch v := value; v.Kind() { - case reflect.Bool: - if v.Bool() { - return out.WriteByte(255) - } else { - return out.WriteByte(0) - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return marshalInt64(out, int64(v.Int())) - case reflect.Struct: - t := v.Type() - - startingField := 0 - - // If the first element of the structure is a non-empty - // RawContents, then we don't bother serializing the rest. - if t.NumField() > 0 && t.Field(0).Type == rawContentsType { - s := v.Field(0) - if s.Len() > 0 { - bytes := make([]byte, s.Len()) - for i := 0; i < s.Len(); i++ { - bytes[i] = uint8(s.Index(i).Uint()) - } - /* The RawContents will contain the tag and - * length fields but we'll also be writing - * those ourselves, so we strip them out of - * bytes */ - _, err = out.Write(stripTagAndLength(bytes)) - return - } else { - startingField = 1 - } - } - - for i := startingField; i < t.NumField(); i++ { - var pre *forkableWriter - pre, out = out.fork() - err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1"))) - if err != nil { - return - } - } - return - case reflect.Slice: - sliceType := v.Type() - if sliceType.Elem().Kind() == reflect.Uint8 { - bytes := make([]byte, v.Len()) - for i := 0; i < v.Len(); i++ { - bytes[i] = uint8(v.Index(i).Uint()) - } - _, err = out.Write(bytes) - return - } - - var params fieldParameters - for i := 0; i < v.Len(); i++ { - var pre *forkableWriter - pre, out = out.fork() - err = marshalField(pre, v.Index(i), params) - if err != nil { - return - } - } - return - case reflect.String: - if params.stringType == tagIA5String { - return marshalIA5String(out, v.String()) - } else { - return marshalPrintableString(out, v.String()) - } - return - } - - return StructuralError{"unknown Go type"} -} - -func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err os.Error) { - // If the field is an interface{} then recurse into it. - if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 { - return marshalField(out, v.Elem(), params) - } - - if params.optional && reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) { - return - } - - if v.Type() == rawValueType { - rv := v.Interface().(RawValue) - err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound}) - if err != nil { - return - } - _, err = out.Write(rv.Bytes) - return - } - - tag, isCompound, ok := getUniversalType(v.Type()) - if !ok { - err = StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())} - return - } - class := classUniversal - - if params.stringType != 0 { - if tag != tagPrintableString { - return StructuralError{"Explicit string type given to non-string member"} - } - tag = params.stringType - } - - if params.set { - if tag != tagSequence { - return StructuralError{"Non sequence tagged as set"} - } - tag = tagSet - } - - tags, body := out.fork() - - err = marshalBody(body, v, params) - if err != nil { - return - } - - bodyLen := body.Len() - - var explicitTag *forkableWriter - if params.explicit { - explicitTag, tags = tags.fork() - } - - if !params.explicit && params.tag != nil { - // implicit tag. - tag = *params.tag - class = classContextSpecific - } - - err = marshalTagAndLength(tags, tagAndLength{class, tag, bodyLen, isCompound}) - if err != nil { - return - } - - if params.explicit { - err = marshalTagAndLength(explicitTag, tagAndLength{ - class: classContextSpecific, - tag: *params.tag, - length: bodyLen + tags.Len(), - isCompound: true, - }) - } - - return nil -} - -// Marshal returns the ASN.1 encoding of val. -func Marshal(val interface{}) ([]byte, os.Error) { - var out bytes.Buffer - v := reflect.ValueOf(val) - f := newForkableWriter() - err := marshalField(f, v, fieldParameters{}) - if err != nil { - return nil, err - } - _, err = f.writeTo(&out) - return out.Bytes(), nil -} diff --git a/src/pkg/asn1/marshal_test.go b/src/pkg/asn1/marshal_test.go deleted file mode 100644 index 03df5f1e1..000000000 --- a/src/pkg/asn1/marshal_test.go +++ /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. - -package asn1 - -import ( - "bytes" - "encoding/hex" - "testing" - "time" -) - -type intStruct struct { - A int -} - -type twoIntStruct struct { - A int - B int -} - -type nestedStruct struct { - A intStruct -} - -type rawContentsStruct struct { - Raw RawContent - A int -} - -type implicitTagTest struct { - A int `asn1:"implicit,tag:5"` -} - -type explicitTagTest struct { - A int `asn1:"explicit,tag:5"` -} - -type ia5StringTest struct { - A string `asn1:"ia5"` -} - -type printableStringTest struct { - A string `asn1:"printable"` -} - -type optionalRawValueTest struct { - A RawValue `asn1:"optional"` -} - -type testSET []int - -func setPST(t *time.Time) *time.Time { - t.ZoneOffset = -28800 - return t -} - -type marshalTest struct { - in interface{} - out string // hex encoded -} - -var marshalTests = []marshalTest{ - {10, "02010a"}, - {127, "02017f"}, - {128, "02020080"}, - {-128, "020180"}, - {-129, "0202ff7f"}, - {intStruct{64}, "3003020140"}, - {twoIntStruct{64, 65}, "3006020140020141"}, - {nestedStruct{intStruct{127}}, "3005300302017f"}, - {[]byte{1, 2, 3}, "0403010203"}, - {implicitTagTest{64}, "3003850140"}, - {explicitTagTest{64}, "3005a503020140"}, - {time.SecondsToUTC(0), "170d3730303130313030303030305a"}, - {time.SecondsToUTC(1258325776), "170d3039313131353232353631365a"}, - {setPST(time.SecondsToUTC(1258325776)), "17113039313131353232353631362d30383030"}, - {BitString{[]byte{0x80}, 1}, "03020780"}, - {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"}, - {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"}, - {ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"}, - {"test", "130474657374"}, - { - "" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 127 times 'x' - "137f" + - "7878787878787878787878787878787878787878787878787878787878787878" + - "7878787878787878787878787878787878787878787878787878787878787878" + - "7878787878787878787878787878787878787878787878787878787878787878" + - "78787878787878787878787878787878787878787878787878787878787878", - }, - { - "" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 128 times 'x' - "138180" + - "7878787878787878787878787878787878787878787878787878787878787878" + - "7878787878787878787878787878787878787878787878787878787878787878" + - "7878787878787878787878787878787878787878787878787878787878787878" + - "7878787878787878787878787878787878787878787878787878787878787878", - }, - {ia5StringTest{"test"}, "3006160474657374"}, - {optionalRawValueTest{}, "3000"}, - {printableStringTest{"test"}, "3006130474657374"}, - {printableStringTest{"test*"}, "30071305746573742a"}, - {rawContentsStruct{nil, 64}, "3003020140"}, - {rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"}, - {RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"}, - {testSET([]int{10}), "310302010a"}, -} - -func TestMarshal(t *testing.T) { - for i, test := range marshalTests { - data, err := Marshal(test.in) - if err != nil { - t.Errorf("#%d failed: %s", i, err) - } - out, _ := hex.DecodeString(test.out) - if bytes.Compare(out, data) != 0 { - t.Errorf("#%d got: %x want %x", i, data, out) - } - } -} diff --git a/src/pkg/big/Makefile b/src/pkg/big/Makefile deleted file mode 100644 index 3d4b56d78..000000000 --- a/src/pkg/big/Makefile +++ /dev/null @@ -1,18 +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 ../../Make.inc - -TARG=big -GOFILES=\ - arith.go\ - arith_decl.go\ - int.go\ - nat.go\ - rat.go\ - -OFILES=\ - arith_$(GOARCH).$O\ - -include ../../Make.pkg diff --git a/src/pkg/big/arith.go b/src/pkg/big/arith.go deleted file mode 100644 index 0a02a4ef5..000000000 --- a/src/pkg/big/arith.go +++ /dev/null @@ -1,231 +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. - -// This file provides Go implementations of elementary multi-precision -// arithmetic operations on word vectors. Needed for platforms without -// assembly implementations of these routines. - -package big - -// TODO(gri) Decide if Word needs to remain exported. - -type Word uintptr - -const ( - // Compute the size _S of a Word in bytes. - _m = ^Word(0) - _logS = _m>>8&1 + _m>>16&1 + _m>>32&1 - _S = 1 << _logS - - _W = _S << 3 // word size in bits - _B = 1 << _W // digit base - _M = _B - 1 // digit mask - - _W2 = _W / 2 // half word size in bits - _B2 = 1 << _W2 // half digit base - _M2 = _B2 - 1 // half digit mask -) - -// ---------------------------------------------------------------------------- -// Elementary operations on words -// -// These operations are used by the vector operations below. - -// z1<<_W + z0 = x+y+c, with c == 0 or 1 -func addWW_g(x, y, c Word) (z1, z0 Word) { - yc := y + c - z0 = x + yc - if z0 < x || yc < y { - z1 = 1 - } - return -} - -// z1<<_W + z0 = x-y-c, with c == 0 or 1 -func subWW_g(x, y, c Word) (z1, z0 Word) { - yc := y + c - z0 = x - yc - if z0 > x || yc < y { - z1 = 1 - } - return -} - -// z1<<_W + z0 = x*y -// Adapted from Warren, Hacker's Delight, p. 132. -func mulWW_g(x, y Word) (z1, z0 Word) { - x0 := x & _M2 - x1 := x >> _W2 - y0 := y & _M2 - y1 := y >> _W2 - w0 := x0 * y0 - t := x1*y0 + w0>>_W2 - w1 := t & _M2 - w2 := t >> _W2 - w1 += x0 * y1 - z1 = x1*y1 + w2 + w1>>_W2 - z0 = x * y - return -} - -// z1<<_W + z0 = x*y + c -func mulAddWWW_g(x, y, c Word) (z1, z0 Word) { - z1, zz0 := mulWW(x, y) - if z0 = zz0 + c; z0 < zz0 { - z1++ - } - return -} - -// Length of x in bits. -func bitLen(x Word) (n int) { - for ; x >= 0x100; x >>= 8 { - n += 8 - } - for ; x > 0; x >>= 1 { - n++ - } - return -} - -// log2 computes the integer binary logarithm of x. -// The result is the integer n for which 2^n <= x < 2^(n+1). -// If x == 0, the result is -1. -func log2(x Word) int { - return bitLen(x) - 1 -} - -// Number of leading zeros in x. -func leadingZeros(x Word) uint { - return uint(_W - bitLen(x)) -} - -// q = (u1<<_W + u0 - r)/y -// Adapted from Warren, Hacker's Delight, p. 152. -func divWW_g(u1, u0, v Word) (q, r Word) { - if u1 >= v { - return 1<<_W - 1, 1<<_W - 1 - } - - s := leadingZeros(v) - v <<= s - - vn1 := v >> _W2 - vn0 := v & _M2 - un32 := u1<>(_W-s) - un10 := u0 << s - un1 := un10 >> _W2 - un0 := un10 & _M2 - q1 := un32 / vn1 - rhat := un32 - q1*vn1 - -again1: - if q1 >= _B2 || q1*vn0 > _B2*rhat+un1 { - q1-- - rhat += vn1 - if rhat < _B2 { - goto again1 - } - } - - un21 := un32*_B2 + un1 - q1*v - q0 := un21 / vn1 - rhat = un21 - q0*vn1 - -again2: - if q0 >= _B2 || q0*vn0 > _B2*rhat+un0 { - q0-- - rhat += vn1 - if rhat < _B2 { - goto again2 - } - } - - return q1*_B2 + q0, (un21*_B2 + un0 - q0*v) >> s -} - -func addVV_g(z, x, y []Word) (c Word) { - for i := range z { - c, z[i] = addWW_g(x[i], y[i], c) - } - return -} - -func subVV_g(z, x, y []Word) (c Word) { - for i := range z { - c, z[i] = subWW_g(x[i], y[i], c) - } - return -} - -func addVW_g(z, x []Word, y Word) (c Word) { - c = y - for i := range z { - c, z[i] = addWW_g(x[i], c, 0) - } - return -} - -func subVW_g(z, x []Word, y Word) (c Word) { - c = y - for i := range z { - c, z[i] = subWW_g(x[i], c, 0) - } - return -} - -func shlVU_g(z, x []Word, s uint) (c Word) { - if n := len(z); n > 0 { - ŝ := _W - s - w1 := x[n-1] - c = w1 >> ŝ - for i := n - 1; i > 0; i-- { - w := w1 - w1 = x[i-1] - z[i] = w<>ŝ - } - z[0] = w1 << s - } - return -} - -func shrVU_g(z, x []Word, s uint) (c Word) { - if n := len(z); n > 0 { - ŝ := _W - s - w1 := x[0] - c = w1 << ŝ - for i := 0; i < n-1; i++ { - w := w1 - w1 = x[i+1] - z[i] = w>>s | w1<<ŝ - } - z[n-1] = w1 >> s - } - return -} - -func mulAddVWW_g(z, x []Word, y, r Word) (c Word) { - c = r - for i := range z { - c, z[i] = mulAddWWW_g(x[i], y, c) - } - return -} - -func addMulVVW_g(z, x []Word, y Word) (c Word) { - for i := range z { - z1, z0 := mulAddWWW_g(x[i], y, z[i]) - c, z[i] = addWW_g(z0, c, 0) - c += z1 - } - return -} - -func divWVW_g(z []Word, xn Word, x []Word, y Word) (r Word) { - r = xn - for i := len(z) - 1; i >= 0; i-- { - z[i], r = divWW_g(r, x[i], y) - } - return -} diff --git a/src/pkg/big/arith_386.s b/src/pkg/big/arith_386.s deleted file mode 100644 index 07c07b02c..000000000 --- a/src/pkg/big/arith_386.s +++ /dev/null @@ -1,265 +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. - -// This file provides fast assembly versions for the elementary -// arithmetic operations on vectors implemented in arith.go. - -// func mulWW(x, y Word) (z1, z0 Word) -TEXT ·mulWW(SB),7,$0 - MOVL x+0(FP), AX - MULL y+4(FP) - MOVL DX, z1+8(FP) - MOVL AX, z0+12(FP) - RET - - -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB),7,$0 - MOVL x1+0(FP), DX - MOVL x0+4(FP), AX - DIVL y+8(FP) - MOVL AX, q+12(FP) - MOVL DX, r+16(FP) - RET - - -// func addVV(z, x, y []Word) (c Word) -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 $0, BX // i = 0 - MOVL $0, DX // c = 0 - JMP E1 - -L1: MOVL (SI)(BX*4), AX - RCRL $1, DX - ADCL (CX)(BX*4), AX - RCLL $1, DX - MOVL AX, (DI)(BX*4) - ADDL $1, BX // i++ - -E1: CMPL BX, BP // i < n - JL L1 - - MOVL DX, c+36(FP) - RET - - -// func subVV(z, x, y []Word) (c Word) -// (same as addVV except for SBBL instead of ADCL and label names) -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 $0, BX // i = 0 - MOVL $0, DX // c = 0 - JMP E2 - -L2: MOVL (SI)(BX*4), AX - RCRL $1, DX - SBBL (CX)(BX*4), AX - RCLL $1, DX - MOVL AX, (DI)(BX*4) - ADDL $1, BX // i++ - -E2: CMPL BX, BP // i < n - JL L2 - - MOVL DX, c+36(FP) - RET - - -// func addVW(z, x []Word, y Word) (c Word) -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 $0, BX // i = 0 - JMP E3 - -L3: ADDL (SI)(BX*4), AX - MOVL AX, (DI)(BX*4) - RCLL $1, AX - ANDL $1, AX - ADDL $1, BX // i++ - -E3: CMPL BX, BP // i < n - JL L3 - - MOVL AX, c+28(FP) - RET - - -// func subVW(z, x []Word, y Word) (c Word) -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 $0, BX // i = 0 - JMP E4 - -L4: MOVL (SI)(BX*4), DX // TODO(gri) is there a reverse SUBL? - SUBL AX, DX - MOVL DX, (DI)(BX*4) - RCLL $1, AX - ANDL $1, AX - ADDL $1, BX // i++ - -E4: CMPL BX, BP // i < n - JL L4 - - MOVL AX, c+28(FP) - RET - - -// func shlVU(z, x []Word, s uint) (c Word) -TEXT ·shlVU(SB),7,$0 - MOVL n+4(FP), BX // i = n - SUBL $1, BX // i-- - JL X8b // i < 0 (n <= 0) - - // n > 0 - MOVL z+0(FP), DI - MOVL x+12(FP), SI - MOVL s+24(FP), CX - MOVL (SI)(BX*4), AX // w1 = x[n-1] - MOVL $0, DX - SHLL CX, DX:AX // w1>>ŝ - MOVL DX, c+28(FP) - - CMPL BX, $0 - JLE X8a // i <= 0 - - // i > 0 -L8: MOVL AX, DX // w = w1 - MOVL -4(SI)(BX*4), AX // w1 = x[i-1] - SHLL CX, DX:AX // w<>ŝ - MOVL DX, (DI)(BX*4) // z[i] = w<>ŝ - SUBL $1, BX // i-- - JG L8 // i > 0 - - // i <= 0 -X8a: SHLL CX, AX // w1< 0 - MOVL z+0(FP), DI - MOVL x+12(FP), SI - MOVL s+24(FP), CX - MOVL (SI), AX // w1 = x[0] - MOVL $0, DX - SHRL CX, DX:AX // w1<<ŝ - MOVL DX, c+28(FP) - - MOVL $0, BX // i = 0 - JMP E9 - - // i < n-1 -L9: MOVL AX, DX // w = w1 - MOVL 4(SI)(BX*4), AX // w1 = x[i+1] - SHRL CX, DX:AX // w>>s | w1<<ŝ - MOVL DX, (DI)(BX*4) // z[i] = w>>s | w1<<ŝ - ADDL $1, BX // i++ - -E9: CMPL BX, BP - JL L9 // i < n-1 - - // i >= n-1 -X9a: SHRL CX, AX // w1>>s - MOVL AX, (DI)(BP*4) // z[n-1] = w1>>s - RET - -X9b: MOVL $0, c+28(FP) - RET - - -// func mulAddVWW(z, x []Word, y, r Word) (c Word) -TEXT ·mulAddVWW(SB),7,$0 - MOVL z+0(FP), DI - MOVL x+12(FP), SI - MOVL y+24(FP), BP - MOVL r+28(FP), CX // c = r - MOVL n+4(FP), BX - LEAL (DI)(BX*4), DI - LEAL (SI)(BX*4), SI - NEGL BX // i = -n - JMP E5 - -L5: MOVL (SI)(BX*4), AX - MULL BP - ADDL CX, AX - ADCL $0, DX - MOVL AX, (DI)(BX*4) - MOVL DX, CX - ADDL $1, BX // i++ - -E5: CMPL BX, $0 // i < 0 - JL L5 - - MOVL CX, c+32(FP) - RET - - -// func addMulVVW(z, x []Word, y Word) (c Word) -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 - LEAL (DI)(BX*4), DI - LEAL (SI)(BX*4), SI - NEGL BX // i = -n - MOVL $0, CX // c = 0 - JMP E6 - -L6: MOVL (SI)(BX*4), AX - MULL BP - ADDL CX, AX - ADCL $0, DX - ADDL AX, (DI)(BX*4) - ADCL $0, DX - MOVL DX, CX - ADDL $1, BX // i++ - -E6: CMPL BX, $0 // i < 0 - JL L6 - - MOVL CX, c+28(FP) - RET - - -// divWVW(z* Word, xn Word, x []Word, y Word) (r Word) -TEXT ·divWVW(SB),7,$0 - MOVL z+0(FP), DI - MOVL xn+12(FP), DX // r = xn - MOVL x+16(FP), SI - MOVL y+28(FP), CX - MOVL n+4(FP), BX // i = n - JMP E7 - -L7: MOVL (SI)(BX*4), AX - DIVL CX - MOVL AX, (DI)(BX*4) - -E7: SUBL $1, BX // i-- - JGE L7 // i >= 0 - - MOVL DX, r+32(FP) - RET diff --git a/src/pkg/big/arith_amd64.s b/src/pkg/big/arith_amd64.s deleted file mode 100644 index 89b65f38a..000000000 --- a/src/pkg/big/arith_amd64.s +++ /dev/null @@ -1,263 +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. - -// 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 - -// func mulWW(x, y Word) (z1, z0 Word) -TEXT ·mulWW(SB),7,$0 - MOVQ x+0(FP), AX - MULQ y+8(FP) - MOVQ DX, z1+16(FP) - MOVQ AX, z0+24(FP) - RET - - -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB),7,$0 - MOVQ x1+0(FP), DX - MOVQ x0+8(FP), AX - DIVQ y+16(FP) - MOVQ AX, q+24(FP) - MOVQ DX, r+32(FP) - RET - - -// func addVV(z, x, y []Word) (c Word) -TEXT ·addVV(SB),7,$0 - 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) - RET - - -// func subVV(z, x, y []Word) (c Word) -// (same as addVV_s except for SBBQ instead of ADCQ and label names) -TEXT ·subVV(SB),7,$0 - 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) - RET - - -// func addVW(z, x []Word, y Word) (c Word) -TEXT ·addVW(SB),7,$0 - 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) - RET - - -// func subVW(z, x []Word, y Word) (c Word) -TEXT ·subVW(SB),7,$0 - 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) - 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-- - JL X8b // i < 0 (n <= 0) - - // n > 0 - MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVL s+32(FP), CX - MOVQ (R8)(BX*8), AX // w1 = x[n-1] - MOVQ $0, DX - SHLQ CX, DX:AX // w1>>ŝ - MOVQ DX, c+40(FP) - - CMPL BX, $0 - JLE X8a // i <= 0 - - // i > 0 -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-- - JG L8 // i > 0 - - // i <= 0 -X8a: SHLQ CX, AX // w1< 0 - MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVL s+32(FP), CX - MOVQ (R8), AX // w1 = x[0] - MOVQ $0, DX - SHRQ CX, DX:AX // w1<<ŝ - MOVQ DX, c+40(FP) - - MOVQ $0, BX // i = 0 - JMP E9 - - // i < n-1 -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++ - -E9: CMPQ BX, R11 - JL L9 // i < n-1 - - // i >= n-1 -X9a: SHRQ CX, AX // w1>>s - MOVQ AX, (R10)(R11*8) // z[n-1] = w1>>s - RET - -X9b: MOVQ $0, c+40(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 $0, BX // i = 0 - JMP E5 - -L5: MOVQ (R8)(BX*8), AX - MULQ R9 - ADDQ CX, AX - ADCQ $0, DX - MOVQ AX, (R10)(BX*8) - MOVQ DX, CX - ADDL $1, BX // i++ - -E5: CMPQ BX, R11 // i < n - JL L5 - - MOVQ CX, c+48(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 $0, BX // i = 0 - MOVQ $0, CX // c = 0 - JMP E6 - -L6: MOVQ (R8)(BX*8), AX - MULQ R9 - ADDQ CX, AX - ADCQ $0, DX - ADDQ AX, (R10)(BX*8) - ADCQ $0, DX - MOVQ DX, CX - ADDL $1, BX // i++ - -E6: CMPQ BX, R11 // i < n - JL L6 - - MOVQ CX, c+40(FP) - RET - - -// 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 - JMP E7 - -L7: MOVQ (R8)(BX*8), AX - DIVQ R9 - MOVQ AX, (R10)(BX*8) - -E7: SUBL $1, BX // i-- - JGE L7 // i >= 0 - - MOVQ DX, r+48(FP) - RET diff --git a/src/pkg/big/arith_arm.s b/src/pkg/big/arith_arm.s deleted file mode 100644 index 60abe6eaa..000000000 --- a/src/pkg/big/arith_arm.s +++ /dev/null @@ -1,312 +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. - -// This file provides fast assembly versions for the elementary -// arithmetic operations on vectors implemented in arith.go. - -#define CFLAG 29 // bit position of carry flag - -// func addVV(z, x, y []Word) (c Word) -TEXT ·addVV(SB),7,$0 - MOVW $0, R0 - MOVW z+0(FP), R1 - MOVW x+12(FP), R2 - MOVW y+24(FP), R3 - MOVW n+4(FP), R4 - MOVW R4<<2, R4 - ADD R1, R4 - B E1 -L1: - MOVW.P 4(R2), R5 - MOVW.P 4(R3), R6 - MOVW R0, CPSR - ADC.S R6, R5 - MOVW.P R5, 4(R1) - MOVW CPSR, R0 -E1: - CMP R1, R4 - BNE L1 - - MOVW R0>>CFLAG, R0 - AND $1, R0 - MOVW R0, c+36(FP) - RET - - -// func subVV(z, x, y []Word) (c Word) -// (same as addVV except for SBC instead of ADC and label names) -TEXT ·subVV(SB),7,$0 - MOVW $(1<>CFLAG, R0 - AND $1, R0 - EOR $1, R0 - MOVW R0, c+36(FP) - RET - - -// func addVW(z, x []Word, y Word) (c Word) -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 R4<<2, R4 - ADD R1, R4 - CMP R1, R4 - BNE L3a - MOVW R3, c+28(FP) - RET -L3a: - MOVW.P 4(R2), R5 - ADD.S R3, R5 - MOVW.P R5, 4(R1) - MOVW CPSR, R0 - B E3 -L3: - MOVW.P 4(R2), R5 - MOVW R0, CPSR - ADC.S $0, R5 - MOVW.P R5, 4(R1) - MOVW CPSR, R0 -E3: - CMP R1, R4 - BNE L3 - - MOVW R0>>CFLAG, R0 - AND $1, R0 - MOVW R0, c+28(FP) - RET - - -// func subVW(z, x []Word, y Word) (c Word) -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 R4<<2, R4 - ADD R1, R4 - CMP R1, R4 - BNE L4a - MOVW R3, c+28(FP) - RET -L4a: - MOVW.P 4(R2), R5 - SUB.S R3, R5 - MOVW.P R5, 4(R1) - MOVW CPSR, R0 - B E4 -L4: - MOVW.P 4(R2), R5 - MOVW R0, CPSR - SBC.S $0, R5 - MOVW.P R5, 4(R1) - MOVW CPSR, R0 -E4: - CMP R1, R4 - BNE L4 - - MOVW R0>>CFLAG, R0 - AND $1, R0 - EOR $1, R0 - MOVW R0, c+28(FP) - RET - - -// func shlVU(z, x []Word, s uint) (c Word) -TEXT ·shlVU(SB),7,$0 - MOVW n+4(FP), R5 - CMP $0, R5 - BEQ X7 - - MOVW z+0(FP), R1 - MOVW x+12(FP), R2 - MOVW R5<<2, R5 - ADD R5, R2 - ADD R1, R5 - MOVW s+24(FP), R3 - CMP $0, R3 // shift 0 is special - BEQ Y7 - ADD $4, R1 // stop one word early - MOVW $32, R4 - SUB R3, R4 - MOVW $0, R7 - - MOVW.W -4(R2), R6 - MOVW R6<>R4, R6 - MOVW R6, c+28(FP) - B E7 - -L7: - MOVW.W -4(R2), R6 - ORR R6>>R4, R7 - MOVW.W R7, -4(R5) - MOVW R6<>R3, R7 - MOVW R6<>R3, R7 -E6: - CMP R1, R5 - BNE L6 - - MOVW R7, 0(R1) - RET - -Y6: // copy loop, because shift 0 == shift 32 - MOVW.P 4(R2), R6 - MOVW.P R6, 4(R1) - CMP R1, R5 - BNE Y6 - -X6: - MOVW $0, R1 - MOVW R1, c+28(FP) - RET - - -// func mulAddVWW(z, x []Word, y, r Word) (c Word) -TEXT ·mulAddVWW(SB),7,$0 - MOVW $0, R0 - MOVW z+0(FP), R1 - MOVW x+12(FP), R2 - MOVW y+24(FP), R3 - MOVW r+28(FP), R4 - MOVW n+4(FP), R5 - MOVW R5<<2, R5 - ADD R1, R5 - B E8 - - // word loop -L8: - MOVW.P 4(R2), R6 - MULLU R6, R3, (R7, R6) - ADD.S R4, R6 - ADC R0, R7 - MOVW.P R6, 4(R1) - MOVW R7, R4 -E8: - CMP R1, R5 - BNE L8 - - MOVW R4, c+32(FP) - RET - - -// func addMulVVW(z, x []Word, y Word) (c Word) -TEXT ·addMulVVW(SB),7,$0 - MOVW $0, R0 - MOVW z+0(FP), R1 - MOVW x+12(FP), R2 - MOVW y+24(FP), R3 - MOVW n+4(FP), R5 - MOVW R5<<2, R5 - ADD R1, R5 - MOVW $0, R4 - B E9 - - // word loop -L9: - MOVW.P 4(R2), R6 - MULLU R6, R3, (R7, R6) - ADD.S R4, R6 - ADC R0, R7 - MOVW 0(R1), R4 - ADD.S R4, R6 - ADC R0, R7 - MOVW.P R6, 4(R1) - MOVW R7, R4 -E9: - CMP R1, R5 - BNE L9 - - MOVW R4, c+28(FP) - RET - - -// divWVW(z* Word, xn Word, x []Word, y Word) (r Word) -TEXT ·divWVW(SB),7,$0 - // ARM has no multiword division, so use portable code. - B ·divWVW_g(SB) - - -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB),7,$0 - // ARM has no multiword division, so use portable code. - B ·divWW_g(SB) - - -// func mulWW(x, y Word) (z1, z0 Word) -TEXT ·mulWW(SB),7,$0 - MOVW x+0(FP), R1 - MOVW y+4(FP), R2 - MULLU R1, R2, (R4, R3) - MOVW R4, z1+8(FP) - MOVW R3, z0+12(FP) - RET diff --git a/src/pkg/big/arith_decl.go b/src/pkg/big/arith_decl.go deleted file mode 100644 index 95fcd8b94..000000000 --- a/src/pkg/big/arith_decl.go +++ /dev/null @@ -1,18 +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. - -package big - -// implemented in arith_$GOARCH.s -func mulWW(x, y Word) (z1, z0 Word) -func divWW(x1, x0, y Word) (q, r Word) -func addVV(z, x, y []Word) (c Word) -func subVV(z, x, y []Word) (c Word) -func addVW(z, x []Word, y Word) (c Word) -func subVW(z, x []Word, y Word) (c Word) -func shlVU(z, x []Word, s uint) (c Word) -func shrVU(z, x []Word, s uint) (c Word) -func mulAddVWW(z, x []Word, y, r Word) (c Word) -func addMulVVW(z, x []Word, y Word) (c Word) -func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) diff --git a/src/pkg/big/arith_test.go b/src/pkg/big/arith_test.go deleted file mode 100644 index b6c56c39e..000000000 --- a/src/pkg/big/arith_test.go +++ /dev/null @@ -1,335 +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. - -package big - -import "testing" - -type funWW func(x, y, c Word) (z1, z0 Word) -type argWW struct { - x, y, c, z1, z0 Word -} - -var sumWW = []argWW{ - {0, 0, 0, 0, 0}, - {0, 1, 0, 0, 1}, - {0, 0, 1, 0, 1}, - {0, 1, 1, 0, 2}, - {12345, 67890, 0, 0, 80235}, - {12345, 67890, 1, 0, 80236}, - {_M, 1, 0, 1, 0}, - {_M, 0, 1, 1, 0}, - {_M, 1, 1, 1, 1}, - {_M, _M, 0, 1, _M - 1}, - {_M, _M, 1, 1, _M}, -} - -func testFunWW(t *testing.T, msg string, f funWW, a argWW) { - z1, z0 := f(a.x, a.y, a.c) - if z1 != a.z1 || z0 != a.z0 { - t.Errorf("%s%+v\n\tgot z1:z0 = %#x:%#x; want %#x:%#x", msg, a, z1, z0, a.z1, a.z0) - } -} - -func TestFunWW(t *testing.T) { - for _, a := range sumWW { - arg := a - testFunWW(t, "addWW_g", addWW_g, arg) - - arg = argWW{a.y, a.x, a.c, a.z1, a.z0} - testFunWW(t, "addWW_g symmetric", addWW_g, arg) - - arg = argWW{a.z0, a.x, a.c, a.z1, a.y} - testFunWW(t, "subWW_g", subWW_g, arg) - - arg = argWW{a.z0, a.y, a.c, a.z1, a.x} - testFunWW(t, "subWW_g symmetric", subWW_g, arg) - } -} - -type funVV func(z, x, y []Word) (c Word) -type argVV struct { - z, x, y nat - c Word -} - -var sumVV = []argVV{ - {}, - {nat{0}, nat{0}, nat{0}, 0}, - {nat{1}, nat{1}, nat{0}, 0}, - {nat{0}, nat{_M}, nat{1}, 1}, - {nat{80235}, nat{12345}, nat{67890}, 0}, - {nat{_M - 1}, nat{_M}, nat{_M}, 1}, - {nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, nat{1, 0, 0, 0}, 1}, - {nat{0, 0, 0, _M}, nat{_M, _M, _M, _M - 1}, nat{1, 0, 0, 0}, 0}, - {nat{0, 0, 0, 0}, nat{_M, 0, _M, 0}, nat{1, _M, 0, _M}, 1}, -} - -func testFunVV(t *testing.T, msg string, f funVV, a argVV) { - z := make(nat, len(a.z)) - c := f(z, a.x, a.y) - for i, zi := range z { - if zi != a.z[i] { - t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) - break - } - } - if c != a.c { - t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c) - } -} - -func TestFunVV(t *testing.T) { - for _, a := range sumVV { - arg := a - testFunVV(t, "addVV_g", addVV_g, arg) - testFunVV(t, "addVV", addVV, arg) - - arg = argVV{a.z, a.y, a.x, a.c} - testFunVV(t, "addVV_g symmetric", addVV_g, arg) - testFunVV(t, "addVV symmetric", addVV, arg) - - arg = argVV{a.x, a.z, a.y, a.c} - testFunVV(t, "subVV_g", subVV_g, arg) - testFunVV(t, "subVV", subVV, arg) - - arg = argVV{a.y, a.z, a.x, a.c} - testFunVV(t, "subVV_g symmetric", subVV_g, arg) - testFunVV(t, "subVV symmetric", subVV, arg) - } -} - -type funVW func(z, x []Word, y Word) (c Word) -type argVW struct { - z, x nat - y Word - c Word -} - -var sumVW = []argVW{ - {}, - {nil, nil, 2, 2}, - {nat{0}, nat{0}, 0, 0}, - {nat{1}, nat{0}, 1, 0}, - {nat{1}, nat{1}, 0, 0}, - {nat{0}, nat{_M}, 1, 1}, - {nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, 1, 1}, -} - -var prodVW = []argVW{ - {}, - {nat{0}, nat{0}, 0, 0}, - {nat{0}, nat{_M}, 0, 0}, - {nat{0}, nat{0}, _M, 0}, - {nat{1}, nat{1}, 1, 0}, - {nat{22793}, nat{991}, 23, 0}, - {nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0}, - {nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0}, - {nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0}, - {nat{_M << 1 & _M}, nat{_M}, 1 << 1, _M >> (_W - 1)}, - {nat{_M << 7 & _M}, nat{_M}, 1 << 7, _M >> (_W - 7)}, - {nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, _M >> (_W - 7)}, -} - -var lshVW = []argVW{ - {}, - {nat{0}, nat{0}, 0, 0}, - {nat{0}, nat{0}, 1, 0}, - {nat{0}, nat{0}, 20, 0}, - - {nat{_M}, nat{_M}, 0, 0}, - {nat{_M << 1 & _M}, nat{_M}, 1, 1}, - {nat{_M << 20 & _M}, nat{_M}, 20, _M >> (_W - 20)}, - - {nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0}, - {nat{_M << 1 & _M, _M, _M}, nat{_M, _M, _M}, 1, 1}, - {nat{_M << 20 & _M, _M, _M}, nat{_M, _M, _M}, 20, _M >> (_W - 20)}, -} - -var rshVW = []argVW{ - {}, - {nat{0}, nat{0}, 0, 0}, - {nat{0}, nat{0}, 1, 0}, - {nat{0}, nat{0}, 20, 0}, - - {nat{_M}, nat{_M}, 0, 0}, - {nat{_M >> 1}, nat{_M}, 1, _M << (_W - 1) & _M}, - {nat{_M >> 20}, nat{_M}, 20, _M << (_W - 20) & _M}, - - {nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0}, - {nat{_M, _M, _M >> 1}, nat{_M, _M, _M}, 1, _M << (_W - 1) & _M}, - {nat{_M, _M, _M >> 20}, nat{_M, _M, _M}, 20, _M << (_W - 20) & _M}, -} - -func testFunVW(t *testing.T, msg string, f funVW, a argVW) { - z := make(nat, len(a.z)) - c := f(z, a.x, a.y) - for i, zi := range z { - if zi != a.z[i] { - t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) - break - } - } - if c != a.c { - t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c) - } -} - -func makeFunVW(f func(z, x []Word, s uint) (c Word)) funVW { - return func(z, x []Word, s Word) (c Word) { - return f(z, x, uint(s)) - } -} - -func TestFunVW(t *testing.T) { - for _, a := range sumVW { - arg := a - testFunVW(t, "addVW_g", addVW_g, arg) - testFunVW(t, "addVW", addVW, arg) - - arg = argVW{a.x, a.z, a.y, a.c} - testFunVW(t, "subVW_g", subVW_g, arg) - testFunVW(t, "subVW", subVW, arg) - } - - shlVW_g := makeFunVW(shlVU_g) - shlVW := makeFunVW(shlVU) - for _, a := range lshVW { - arg := a - testFunVW(t, "shlVU_g", shlVW_g, arg) - testFunVW(t, "shlVU", shlVW, arg) - } - - shrVW_g := makeFunVW(shrVU_g) - shrVW := makeFunVW(shrVU) - for _, a := range rshVW { - arg := a - testFunVW(t, "shrVU_g", shrVW_g, arg) - testFunVW(t, "shrVU", shrVW, arg) - } -} - -type funVWW func(z, x []Word, y, r Word) (c Word) -type argVWW struct { - z, x nat - y, r Word - c Word -} - -var prodVWW = []argVWW{ - {}, - {nat{0}, nat{0}, 0, 0, 0}, - {nat{991}, nat{0}, 0, 991, 0}, - {nat{0}, nat{_M}, 0, 0, 0}, - {nat{991}, nat{_M}, 0, 991, 0}, - {nat{0}, nat{0}, _M, 0, 0}, - {nat{991}, nat{0}, _M, 991, 0}, - {nat{1}, nat{1}, 1, 0, 0}, - {nat{992}, nat{1}, 1, 991, 0}, - {nat{22793}, nat{991}, 23, 0, 0}, - {nat{22800}, nat{991}, 23, 7, 0}, - {nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0, 0}, - {nat{7, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 7, 0}, - {nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0, 0}, - {nat{991, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 991, 0}, - {nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0, 0}, - {nat{991, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 991, 0}, - {nat{_M << 1 & _M}, nat{_M}, 1 << 1, 0, _M >> (_W - 1)}, - {nat{_M<<1&_M + 1}, nat{_M}, 1 << 1, 1, _M >> (_W - 1)}, - {nat{_M << 7 & _M}, nat{_M}, 1 << 7, 0, _M >> (_W - 7)}, - {nat{_M<<7&_M + 1<<6}, nat{_M}, 1 << 7, 1 << 6, _M >> (_W - 7)}, - {nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 0, _M >> (_W - 7)}, - {nat{_M<<7&_M + 1<<6, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 1 << 6, _M >> (_W - 7)}, -} - -func testFunVWW(t *testing.T, msg string, f funVWW, a argVWW) { - z := make(nat, len(a.z)) - c := f(z, a.x, a.y, a.r) - for i, zi := range z { - if zi != a.z[i] { - t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) - break - } - } - if c != a.c { - t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c) - } -} - -// TODO(gri) mulAddVWW and divWVW are symmetric operations but -// their signature is not symmetric. Try to unify. - -type funWVW func(z []Word, xn Word, x []Word, y Word) (r Word) -type argWVW struct { - z nat - xn Word - x nat - y Word - r Word -} - -func testFunWVW(t *testing.T, msg string, f funWVW, a argWVW) { - z := make(nat, len(a.z)) - r := f(z, a.xn, a.x, a.y) - for i, zi := range z { - if zi != a.z[i] { - t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) - break - } - } - if r != a.r { - t.Errorf("%s%+v\n\tgot r = %#x; want %#x", msg, a, r, a.r) - } -} - -func TestFunVWW(t *testing.T) { - for _, a := range prodVWW { - arg := a - testFunVWW(t, "mulAddVWW_g", mulAddVWW_g, arg) - testFunVWW(t, "mulAddVWW", mulAddVWW, arg) - - if a.y != 0 && a.r < a.y { - arg := argWVW{a.x, a.c, a.z, a.y, a.r} - testFunWVW(t, "divWVW_g", divWVW_g, arg) - testFunWVW(t, "divWVW", divWVW, arg) - } - } -} - -var mulWWTests = []struct { - x, y Word - q, r Word -}{ - {_M, _M, _M - 1, 1}, - // 32 bit only: {0xc47dfa8c, 50911, 0x98a4, 0x998587f4}, -} - -func TestMulWW(t *testing.T) { - for i, test := range mulWWTests { - q, r := mulWW_g(test.x, test.y) - if q != test.q || r != test.r { - t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r) - } - } -} - -var mulAddWWWTests = []struct { - x, y, c Word - q, r Word -}{ - // TODO(agl): These will only work on 64-bit platforms. - // {15064310297182388543, 0xe7df04d2d35d5d80, 13537600649892366549, 13644450054494335067, 10832252001440893781}, - // {15064310297182388543, 0xdab2f18048baa68d, 13644450054494335067, 12869334219691522700, 14233854684711418382}, - {_M, _M, 0, _M - 1, 1}, - {_M, _M, _M, _M, 0}, -} - -func TestMulAddWWW(t *testing.T) { - for i, test := range mulAddWWWTests { - q, r := mulAddWWW_g(test.x, test.y, test.c) - if q != test.q || r != test.r { - t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r) - } - } -} diff --git a/src/pkg/big/calibrate_test.go b/src/pkg/big/calibrate_test.go deleted file mode 100644 index 1cd93b105..000000000 --- a/src/pkg/big/calibrate_test.go +++ /dev/null @@ -1,88 +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. - -// This file prints execution times for the Mul benchmark -// given different Karatsuba thresholds. The result may be -// used to manually fine-tune the threshold constant. The -// results are somewhat fragile; use repeated runs to get -// a clear picture. - -// Usage: gotest -calibrate - -package big - -import ( - "flag" - "fmt" - "testing" - "time" -) - -var calibrate = flag.Bool("calibrate", false, "run calibration test") - -// measure returns the time to run f -func measure(f func()) int64 { - const N = 100 - start := time.Nanoseconds() - for i := N; i > 0; i-- { - f() - } - stop := time.Nanoseconds() - return (stop - start) / N -} - -func computeThresholds() { - fmt.Printf("Multiplication times for varying Karatsuba thresholds\n") - 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) - - // thresholds - n := 8 // any lower values for the threshold lead to very slow multiplies - th1 := -1 - th2 := -1 - - var deltaOld int64 - for count := -1; count != 0; count-- { - // determine Tk, the work load execution time using Karatsuba multiplication - karatsubaThreshold = n // enable karatsuba - Tk := measure(benchmarkMulLoad) - - // improvement over Tb - delta := (Tb - Tk) * 100 / Tb - - fmt.Printf("n = %3d Tk = %8dns %4d%%", n, Tk, delta) - - // determine break-even point - if Tk < Tb && th1 < 0 { - th1 = n - fmt.Print(" break-even point") - } - - // determine diminishing return - if 0 < delta && delta < deltaOld && th2 < 0 { - th2 = n - fmt.Print(" diminishing return") - } - deltaOld = delta - - fmt.Println() - - // trigger counter - if th1 >= 0 && th2 >= 0 && count < 0 { - count = 20 // this many extra measurements after we got both thresholds - } - - n++ - } -} - -func TestCalibrate(t *testing.T) { - if *calibrate { - computeThresholds() - } -} diff --git a/src/pkg/big/hilbert_test.go b/src/pkg/big/hilbert_test.go deleted file mode 100644 index 1a84341b3..000000000 --- a/src/pkg/big/hilbert_test.go +++ /dev/null @@ -1,160 +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. - -// A little test program and benchmark for rational arithmetics. -// Computes a Hilbert matrix, its inverse, multiplies them -// and verifies that the product is the identity matrix. - -package big - -import ( - "fmt" - "testing" -) - -type matrix struct { - n, m int - a []*Rat -} - -func (a *matrix) at(i, j int) *Rat { - if !(0 <= i && i < a.n && 0 <= j && j < a.m) { - panic("index out of range") - } - return a.a[i*a.m+j] -} - -func (a *matrix) set(i, j int, x *Rat) { - if !(0 <= i && i < a.n && 0 <= j && j < a.m) { - panic("index out of range") - } - a.a[i*a.m+j] = x -} - -func newMatrix(n, m int) *matrix { - if !(0 <= n && 0 <= m) { - panic("illegal matrix") - } - a := new(matrix) - a.n = n - a.m = m - a.a = make([]*Rat, n*m) - return a -} - -func newUnit(n int) *matrix { - a := newMatrix(n, n) - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - x := NewRat(0, 1) - if i == j { - x.SetInt64(1) - } - a.set(i, j, x) - } - } - return a -} - -func newHilbert(n int) *matrix { - a := newMatrix(n, n) - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - a.set(i, j, NewRat(1, int64(i+j+1))) - } - } - return a -} - -func newInverseHilbert(n int) *matrix { - a := newMatrix(n, n) - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - x1 := new(Rat).SetInt64(int64(i + j + 1)) - x2 := new(Rat).SetInt(new(Int).Binomial(int64(n+i), int64(n-j-1))) - x3 := new(Rat).SetInt(new(Int).Binomial(int64(n+j), int64(n-i-1))) - x4 := new(Rat).SetInt(new(Int).Binomial(int64(i+j), int64(i))) - - x1.Mul(x1, x2) - x1.Mul(x1, x3) - x1.Mul(x1, x4) - x1.Mul(x1, x4) - - if (i+j)&1 != 0 { - x1.Neg(x1) - } - - a.set(i, j, x1) - } - } - return a -} - -func (a *matrix) mul(b *matrix) *matrix { - if a.m != b.n { - panic("illegal matrix multiply") - } - c := newMatrix(a.n, b.m) - for i := 0; i < c.n; i++ { - for j := 0; j < c.m; j++ { - x := NewRat(0, 1) - for k := 0; k < a.m; k++ { - x.Add(x, new(Rat).Mul(a.at(i, k), b.at(k, j))) - } - c.set(i, j, x) - } - } - return c -} - -func (a *matrix) eql(b *matrix) bool { - if a.n != b.n || a.m != b.m { - return false - } - for i := 0; i < a.n; i++ { - for j := 0; j < a.m; j++ { - if a.at(i, j).Cmp(b.at(i, j)) != 0 { - return false - } - } - } - return true -} - -func (a *matrix) String() string { - s := "" - for i := 0; i < a.n; i++ { - for j := 0; j < a.m; j++ { - s += fmt.Sprintf("\t%s", a.at(i, j)) - } - s += "\n" - } - return s -} - -func doHilbert(t *testing.T, n int) { - a := newHilbert(n) - b := newInverseHilbert(n) - I := newUnit(n) - ab := a.mul(b) - if !ab.eql(I) { - if t == nil { - panic("Hilbert failed") - } - t.Errorf("a = %s\n", a) - t.Errorf("b = %s\n", b) - t.Errorf("a*b = %s\n", ab) - t.Errorf("I = %s\n", I) - } -} - -func TestHilbert(t *testing.T) { - doHilbert(t, 10) -} - -func BenchmarkHilbert(b *testing.B) { - for i := 0; i < b.N; i++ { - doHilbert(nil, 10) - } -} diff --git a/src/pkg/big/int.go b/src/pkg/big/int.go deleted file mode 100755 index 701b69715..000000000 --- a/src/pkg/big/int.go +++ /dev/null @@ -1,868 +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. - -// This file implements signed multi-precision integers. - -package big - -import ( - "fmt" - "io" - "os" - "rand" - "strings" -) - -// An Int represents a signed multi-precision integer. -// The zero value for an Int represents the value 0. -type Int struct { - neg bool // sign - abs nat // absolute value of the integer -} - -var intOne = &Int{false, natOne} - -// Sign returns: -// -// -1 if x < 0 -// 0 if x == 0 -// +1 if x > 0 -// -func (x *Int) Sign() int { - if len(x.abs) == 0 { - return 0 - } - if x.neg { - return -1 - } - return 1 -} - -// SetInt64 sets z to x and returns z. -func (z *Int) SetInt64(x int64) *Int { - neg := false - if x < 0 { - neg = true - x = -x - } - z.abs = z.abs.setUint64(uint64(x)) - z.neg = neg - return z -} - -// NewInt allocates and returns a new Int set to x. -func NewInt(x int64) *Int { - return new(Int).SetInt64(x) -} - -// Set sets z to x and returns z. -func (z *Int) Set(x *Int) *Int { - z.abs = z.abs.set(x.abs) - z.neg = x.neg - return z -} - -// Abs sets z to |x| (the absolute value of x) and returns z. -func (z *Int) Abs(x *Int) *Int { - z.abs = z.abs.set(x.abs) - z.neg = false - return z -} - -// Neg sets z to -x and returns z. -func (z *Int) Neg(x *Int) *Int { - z.abs = z.abs.set(x.abs) - z.neg = len(z.abs) > 0 && !x.neg // 0 has no sign - return z -} - -// Add sets z to the sum x+y and returns z. -func (z *Int) Add(x, y *Int) *Int { - neg := x.neg - if x.neg == y.neg { - // x + y == x + y - // (-x) + (-y) == -(x + y) - z.abs = z.abs.add(x.abs, y.abs) - } else { - // x + (-y) == x - y == -(y - x) - // (-x) + y == y - x == -(x - y) - if x.abs.cmp(y.abs) >= 0 { - z.abs = z.abs.sub(x.abs, y.abs) - } else { - neg = !neg - z.abs = z.abs.sub(y.abs, x.abs) - } - } - z.neg = len(z.abs) > 0 && neg // 0 has no sign - return z -} - -// Sub sets z to the difference x-y and returns z. -func (z *Int) Sub(x, y *Int) *Int { - neg := x.neg - if x.neg != y.neg { - // x - (-y) == x + y - // (-x) - y == -(x + y) - z.abs = z.abs.add(x.abs, y.abs) - } else { - // x - y == x - y == -(y - x) - // (-x) - (-y) == y - x == -(x - y) - if x.abs.cmp(y.abs) >= 0 { - z.abs = z.abs.sub(x.abs, y.abs) - } else { - neg = !neg - z.abs = z.abs.sub(y.abs, x.abs) - } - } - z.neg = len(z.abs) > 0 && neg // 0 has no sign - return z -} - -// Mul sets z to the product x*y and returns z. -func (z *Int) Mul(x, y *Int) *Int { - // x * y == x * y - // x * (-y) == -(x * y) - // (-x) * y == -(x * y) - // (-x) * (-y) == x * y - z.abs = z.abs.mul(x.abs, y.abs) - z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign - return z -} - -// MulRange sets z to the product of all integers -// in the range [a, b] inclusively and returns z. -// If a > b (empty range), the result is 1. -func (z *Int) MulRange(a, b int64) *Int { - switch { - case a > b: - return z.SetInt64(1) // empty range - case a <= 0 && b >= 0: - return z.SetInt64(0) // range includes 0 - } - // a <= b && (b < 0 || a > 0) - - neg := false - if a < 0 { - neg = (b-a)&1 == 0 - a, b = -b, -a - } - - z.abs = z.abs.mulRange(uint64(a), uint64(b)) - z.neg = neg - return z -} - -// Binomial sets z to the binomial coefficient of (n, k) and returns z. -func (z *Int) Binomial(n, k int64) *Int { - var a, b Int - a.MulRange(n-k+1, n) - b.MulRange(1, k) - return z.Quo(&a, &b) -} - -// Quo sets z to the quotient x/y for y != 0 and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -// See QuoRem for more details. -func (z *Int) Quo(x, y *Int) *Int { - z.abs, _ = z.abs.div(nil, x.abs, y.abs) - z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign - return z -} - -// Rem sets z to the remainder x%y for y != 0 and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -// See QuoRem for more details. -func (z *Int) Rem(x, y *Int) *Int { - _, z.abs = nat(nil).div(z.abs, x.abs, y.abs) - z.neg = len(z.abs) > 0 && x.neg // 0 has no sign - return z -} - -// QuoRem sets z to the quotient x/y and r to the remainder x%y -// and returns the pair (z, r) for y != 0. -// If y == 0, a division-by-zero run-time panic occurs. -// -// QuoRem implements T-division and modulus (like Go): -// -// q = x/y with the result truncated to zero -// r = x - y*q -// -// (See Daan Leijen, ``Division and Modulus for Computer Scientists''.) -// -func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) { - z.abs, r.abs = z.abs.div(r.abs, x.abs, y.abs) - z.neg, r.neg = len(z.abs) > 0 && x.neg != y.neg, len(r.abs) > 0 && x.neg // 0 has no sign - return z, r -} - -// Div sets z to the quotient x/y for y != 0 and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -// See DivMod for more details. -func (z *Int) Div(x, y *Int) *Int { - y_neg := y.neg // z may be an alias for y - var r Int - z.QuoRem(x, y, &r) - if r.neg { - if y_neg { - z.Add(z, intOne) - } else { - z.Sub(z, intOne) - } - } - return z -} - -// Mod sets z to the modulus x%y for y != 0 and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -// See DivMod for more details. -func (z *Int) Mod(x, y *Int) *Int { - y0 := y // save y - if z == y || alias(z.abs, y.abs) { - y0 = new(Int).Set(y) - } - var q Int - q.QuoRem(x, y, z) - if z.neg { - if y0.neg { - z.Sub(z, y0) - } else { - z.Add(z, y0) - } - } - return z -} - -// DivMod sets z to the quotient x div y and m to the modulus x mod y -// and returns the pair (z, m) for y != 0. -// If y == 0, a division-by-zero run-time panic occurs. -// -// DivMod implements Euclidean division and modulus (unlike Go): -// -// q = x div y such that -// m = x - y*q with 0 <= m < |q| -// -// (See Raymond T. Boute, ``The Euclidean definition of the functions -// div and mod''. ACM Transactions on Programming Languages and -// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992. -// ACM press.) -// -func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) { - y0 := y // save y - if z == y || alias(z.abs, y.abs) { - y0 = new(Int).Set(y) - } - z.QuoRem(x, y, m) - if m.neg { - if y0.neg { - z.Add(z, intOne) - m.Sub(m, y0) - } else { - z.Sub(z, intOne) - m.Add(m, y0) - } - } - return z, m -} - -// Cmp compares x and y and returns: -// -// -1 if x < y -// 0 if x == y -// +1 if x > y -// -func (x *Int) Cmp(y *Int) (r int) { - // x cmp y == x cmp y - // x cmp (-y) == x - // (-x) cmp y == y - // (-x) cmp (-y) == -(x cmp y) - switch { - case x.neg == y.neg: - r = x.abs.cmp(y.abs) - if x.neg { - r = -r - } - case x.neg: - r = -1 - default: - r = 1 - } - return -} - -func (x *Int) String() string { - switch { - case x == nil: - return "" - case x.neg: - return "-" + x.abs.decimalString() - } - return x.abs.decimalString() -} - -func charset(ch int) string { - switch ch { - case 'b': - return lowercaseDigits[0:2] - case 'o': - return lowercaseDigits[0:8] - case 'd', 's', 'v': - return lowercaseDigits[0:10] - case 'x': - return lowercaseDigits[0:16] - case 'X': - return uppercaseDigits[0:16] - } - return "" // unknown format -} - -// write count copies of text to s -func writeMultiple(s fmt.State, text string, count int) { - if len(text) > 0 { - b := []byte(text) - for ; count > 0; count-- { - s.Write(b) - } - } -} - -// Format is a support routine for fmt.Formatter. It accepts -// the formats 'b' (binary), 'o' (octal), 'd' (decimal), 'x' -// (lowercase hexadecimal), and 'X' (uppercase hexadecimal). -// Also supported are the full suite of package fmt's format -// verbs for integral types, including '+', '-', and ' ' -// for sign control, '#' for leading zero in octal and for -// hexadecimal, a leading "0x" or "0X" for "%#x" and "%#X" -// respectively, specification of minimum digits precision, -// output field width, space or zero padding, and left or -// right justification. -// -func (x *Int) Format(s fmt.State, ch int) { - cs := charset(ch) - - // special cases - switch { - case cs == "": - // unknown format - fmt.Fprintf(s, "%%!%c(big.Int=%s)", ch, x.String()) - return - case x == nil: - fmt.Fprint(s, "") - return - } - - // determine sign character - sign := "" - switch { - case x.neg: - sign = "-" - case s.Flag('+'): // supersedes ' ' when both specified - sign = "+" - case s.Flag(' '): - sign = " " - } - - // determine prefix characters for indicating output base - prefix := "" - if s.Flag('#') { - switch ch { - case 'o': // octal - prefix = "0" - case 'x': // hexadecimal - prefix = "0x" - case 'X': - prefix = "0X" - } - } - - // determine digits with base set by len(cs) and digit characters from cs - digits := x.abs.string(cs) - - // number of characters for the three classes of number padding - var left int // space characters to left of digits for right justification ("%8d") - var zeroes int // zero characters (actually cs[0]) as left-most digits ("%.8d") - var right int // space characters to right of digits for left justification ("%-8d") - - // determine number padding from precision: the least number of digits to output - precision, precisionSet := s.Precision() - if precisionSet { - switch { - case len(digits) < precision: - 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") - } - } - - // determine field pad from width: the least number of characters to output - length := len(sign) + len(prefix) + zeroes + len(digits) - if width, widthSet := s.Width(); widthSet && length < width { // pad as specified - switch d := width - length; { - case s.Flag('-'): - // pad on the right with spaces; supersedes '0' when both specified - right = d - case s.Flag('0') && !precisionSet: - // pad with zeroes unless precision also specified - zeroes = d - default: - // pad on the left with spaces - left = d - } - } - - // print number as [left pad][sign][prefix][zero pad][digits][right pad] - writeMultiple(s, " ", left) - writeMultiple(s, sign, 1) - writeMultiple(s, prefix, 1) - writeMultiple(s, "0", zeroes) - writeMultiple(s, digits, 1) - writeMultiple(s, " ", right) -} - -// scan sets z to the integer value corresponding to the longest possible prefix -// read from r representing a signed integer number in a given conversion base. -// It returns z, the actual conversion base used, and an error, if any. In the -// error case, the value of z is undefined. The syntax follows the syntax of -// integer literals in Go. -// -// The base argument must be 0 or a value from 2 through MaxBase. If the base -// is 0, the string prefix determines the actual conversion base. A prefix of -// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a -// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. -// -func (z *Int) scan(r io.RuneScanner, base int) (*Int, int, os.Error) { - // determine sign - ch, _, err := r.ReadRune() - if err != nil { - return z, 0, err - } - neg := false - switch ch { - case '-': - neg = true - case '+': // nothing to do - default: - r.UnreadRune() - } - - // determine mantissa - z.abs, base, err = z.abs.scan(r, base) - if err != nil { - return z, base, err - } - z.neg = len(z.abs) > 0 && neg // 0 has no sign - - return z, base, nil -} - -// Scan is a support routine for fmt.Scanner; it sets z to the value of -// the scanned number. It accepts the formats 'b' (binary), 'o' (octal), -// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal). -func (z *Int) Scan(s fmt.ScanState, ch int) os.Error { - s.SkipSpace() // skip leading space characters - base := 0 - switch ch { - case 'b': - base = 2 - case 'o': - base = 8 - case 'd': - base = 10 - case 'x', 'X': - base = 16 - case 's', 'v': - // let scan determine the base - default: - return os.NewError("Int.Scan: invalid verb") - } - _, _, err := z.scan(s, base) - return err -} - -// Int64 returns the int64 representation of x. -// If x cannot be represented in an int64, the result is undefined. -func (x *Int) Int64() int64 { - if len(x.abs) == 0 { - return 0 - } - v := int64(x.abs[0]) - if _W == 32 && len(x.abs) > 1 { - v |= int64(x.abs[1]) << 32 - } - if x.neg { - v = -v - } - 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. -// -// The base argument must be 0 or a value from 2 through MaxBase. If the base -// is 0, the string prefix determines the actual conversion base. A prefix of -// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a -// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. -// -func (z *Int) SetString(s string, base int) (*Int, bool) { - r := strings.NewReader(s) - _, _, err := z.scan(r, base) - if err != nil { - return z, false - } - _, _, err = r.ReadRune() - return z, err == os.EOF // err == os.EOF => scan consumed all of s -} - -// SetBytes interprets buf as the bytes of a big-endian unsigned -// integer, sets z to that value, and returns z. -func (z *Int) SetBytes(buf []byte) *Int { - z.abs = z.abs.setBytes(buf) - z.neg = false - return z -} - -// Bytes returns the absolute value of z as a big-endian byte slice. -func (z *Int) Bytes() []byte { - buf := make([]byte, len(z.abs)*_S) - return buf[z.abs.bytes(buf):] -} - -// BitLen returns the length of the absolute value of z in bits. -// The bit length of 0 is 0. -func (z *Int) BitLen() int { - return z.abs.bitLen() -} - -// Exp sets z = x**y mod m. If m is nil, 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 - } - - var mWords nat - if m != nil { - mWords = m.abs - } - - z.abs = z.abs.expNN(x.abs, y.abs, mWords) - z.neg = len(z.abs) > 0 && x.neg && y.abs[0]&1 == 1 // 0 has no sign - return z -} - -// GcdInt sets d to the greatest common divisor of a and b, which must be -// positive numbers. -// If x and y are not nil, GcdInt sets x and y such that d = a*x + b*y. -// If either a or b is not positive, GcdInt sets d = x = y = 0. -func GcdInt(d, x, y, a, b *Int) { - if a.neg || b.neg { - d.SetInt64(0) - if x != nil { - x.SetInt64(0) - } - if y != nil { - y.SetInt64(0) - } - return - } - - A := new(Int).Set(a) - B := new(Int).Set(b) - - X := new(Int) - Y := new(Int).SetInt64(1) - - lastX := new(Int).SetInt64(1) - lastY := new(Int) - - q := new(Int) - temp := new(Int) - - for len(B.abs) > 0 { - r := new(Int) - q, r = q.QuoRem(A, B, r) - - A, B = B, r - - temp.Set(X) - X.Mul(X, q) - X.neg = !X.neg - X.Add(X, lastX) - lastX.Set(temp) - - temp.Set(Y) - Y.Mul(Y, q) - Y.neg = !Y.neg - Y.Add(Y, lastY) - lastY.Set(temp) - } - - if x != nil { - *x = *lastX - } - - if y != nil { - *y = *lastY - } - - *d = *A -} - -// ProbablyPrime performs n Miller-Rabin tests to check whether z is prime. -// If it returns true, z is prime with probability 1 - 1/4^n. -// If it returns false, z is not prime. -func ProbablyPrime(z *Int, n int) bool { - return !z.neg && z.abs.probablyPrime(n) -} - -// Rand sets z to a pseudo-random number in [0, n) and returns z. -func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int { - z.neg = false - if n.neg == true || len(n.abs) == 0 { - z.abs = nil - return z - } - z.abs = z.abs.random(rnd, n.abs, n.abs.bitLen()) - return z -} - -// ModInverse sets z to the multiplicative inverse of g in the group ℤ/pℤ (where -// p is a prime) and returns z. -func (z *Int) ModInverse(g, p *Int) *Int { - var d Int - GcdInt(&d, z, nil, g, p) - // x and y are such that g*x + p*y = d. Since p is prime, d = 1. Taking - // that modulo p results in g*x = 1, therefore x is the inverse element. - if z.neg { - z.Add(z, p) - } - return z -} - -// Lsh sets z = x << n and returns z. -func (z *Int) Lsh(x *Int, n uint) *Int { - z.abs = z.abs.shl(x.abs, n) - z.neg = x.neg - return z -} - -// Rsh sets z = x >> n and returns z. -func (z *Int) Rsh(x *Int, n uint) *Int { - if x.neg { - // (-x) >> s == ^(x-1) >> s == ^((x-1) >> s) == -(((x-1) >> s) + 1) - t := z.abs.sub(x.abs, natOne) // no underflow because |x| > 0 - t = t.shr(t, n) - z.abs = t.add(t, natOne) - z.neg = true // z cannot be zero if x is negative - return z - } - - z.abs = z.abs.shr(x.abs, n) - z.neg = false - return z -} - -// Bit returns the value of the i'th bit of z. That is, it -// returns (z>>i)&1. The bit index i must be >= 0. -func (z *Int) Bit(i int) uint { - if i < 0 { - panic("negative bit index") - } - if z.neg { - t := nat{}.sub(z.abs, natOne) - return t.bit(uint(i)) ^ 1 - } - - return z.abs.bit(uint(i)) -} - -// SetBit sets the i'th bit of z to bit and returns z. -// That is, if bit is 1 SetBit sets z = x | (1 << i); -// if bit is 0 it sets z = x &^ (1 << i). If bit is not 0 or 1, -// SetBit will panic. -func (z *Int) SetBit(x *Int, i int, b uint) *Int { - if i < 0 { - panic("negative bit index") - } - if x.neg { - t := z.abs.sub(x.abs, natOne) - t = t.setBit(t, uint(i), b^1) - z.abs = t.add(t, natOne) - z.neg = len(z.abs) > 0 - return z - } - z.abs = z.abs.setBit(x.abs, uint(i), b) - z.neg = false - return z -} - -// And sets z = x & y and returns z. -func (z *Int) And(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.add(z.abs.or(x1, y1), natOne) - z.neg = true // z cannot be zero if x and y are negative - return z - } - - // x & y == x & y - z.abs = z.abs.and(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - if x.neg { - x, y = y, x // & is symmetric - } - - // x & (-y) == x & ^(y-1) == x &^ (y-1) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.andNot(x.abs, y1) - z.neg = false - return z -} - -// AndNot sets z = x &^ y and returns z. -func (z *Int) AndNot(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) &^ (-y) == ^(x-1) &^ ^(y-1) == ^(x-1) & (y-1) == (y-1) &^ (x-1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.andNot(y1, x1) - z.neg = false - return z - } - - // x &^ y == x &^ y - z.abs = z.abs.andNot(x.abs, y.abs) - z.neg = false - return z - } - - if x.neg { - // (-x) &^ y == ^(x-1) &^ y == ^(x-1) & ^y == ^((x-1) | y) == -(((x-1) | y) + 1) - x1 := nat{}.sub(x.abs, natOne) - z.abs = z.abs.add(z.abs.or(x1, y.abs), natOne) - z.neg = true // z cannot be zero if x is negative and y is positive - return z - } - - // x &^ (-y) == x &^ ^(y-1) == x & (y-1) - y1 := nat{}.add(y.abs, natOne) - z.abs = z.abs.and(x.abs, y1) - z.neg = false - return z -} - -// Or sets z = x | y and returns z. -func (z *Int) Or(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.add(z.abs.and(x1, y1), natOne) - z.neg = true // z cannot be zero if x and y are negative - return z - } - - // x | y == x | y - z.abs = z.abs.or(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - if x.neg { - x, y = y, x // | is symmetric - } - - // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.add(z.abs.andNot(y1, x.abs), natOne) - z.neg = true // z cannot be zero if one of x or y is negative - return z -} - -// Xor sets z = x ^ y and returns z. -func (z *Int) Xor(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.xor(x1, y1) - z.neg = false - return z - } - - // x ^ y == x ^ y - z.abs = z.abs.xor(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - if x.neg { - x, y = y, x // ^ is symmetric - } - - // x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.add(z.abs.xor(x.abs, y1), natOne) - z.neg = true // z cannot be zero if only one of x or y is negative - return z -} - -// Not sets z = ^x and returns z. -func (z *Int) Not(x *Int) *Int { - if x.neg { - // ^(-x) == ^(^(x-1)) == x-1 - z.abs = z.abs.sub(x.abs, natOne) - z.neg = false - return z - } - - // ^x == -x-1 == -(x+1) - z.abs = z.abs.add(x.abs, natOne) - z.neg = true // z cannot be zero if x is positive - return z -} - -// Gob codec version. Permits backward-compatible changes to the encoding. -const intGobVersion byte = 1 - -// GobEncode implements the gob.GobEncoder interface. -func (z *Int) GobEncode() ([]byte, os.Error) { - buf := make([]byte, 1+len(z.abs)*_S) // extra byte for version and sign bit - i := z.abs.bytes(buf) - 1 // i >= 0 - b := intGobVersion << 1 // make space for sign bit - if z.neg { - b |= 1 - } - buf[i] = b - return buf[i:], nil -} - -// GobDecode implements the gob.GobDecoder interface. -func (z *Int) GobDecode(buf []byte) os.Error { - if len(buf) == 0 { - return os.NewError("Int.GobDecode: no data") - } - b := buf[0] - if b>>1 != intGobVersion { - return os.NewError(fmt.Sprintf("Int.GobDecode: encoding version %d not supported", b>>1)) - } - z.neg = b&1 != 0 - z.abs = z.abs.setBytes(buf[1:]) - return nil -} diff --git a/src/pkg/big/int_test.go b/src/pkg/big/int_test.go deleted file mode 100755 index 03446d6ae..000000000 --- a/src/pkg/big/int_test.go +++ /dev/null @@ -1,1391 +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. - -package big - -import ( - "bytes" - "encoding/hex" - "fmt" - "gob" - "testing" - "testing/quick" -) - -func isNormalized(x *Int) bool { - if len(x.abs) == 0 { - return !x.neg - } - // len(x.abs) > 0 - return x.abs[len(x.abs)-1] != 0 -} - -type funZZ func(z, x, y *Int) *Int -type argZZ struct { - z, x, y *Int -} - -var sumZZ = []argZZ{ - {NewInt(0), NewInt(0), NewInt(0)}, - {NewInt(1), NewInt(1), NewInt(0)}, - {NewInt(1111111110), NewInt(123456789), NewInt(987654321)}, - {NewInt(-1), NewInt(-1), NewInt(0)}, - {NewInt(864197532), NewInt(-123456789), NewInt(987654321)}, - {NewInt(-1111111110), NewInt(-123456789), NewInt(-987654321)}, -} - -var prodZZ = []argZZ{ - {NewInt(0), NewInt(0), NewInt(0)}, - {NewInt(0), NewInt(1), NewInt(0)}, - {NewInt(1), NewInt(1), NewInt(1)}, - {NewInt(-991 * 991), NewInt(991), NewInt(-991)}, - // TODO(gri) add larger products -} - -func TestSignZ(t *testing.T) { - var zero Int - for _, a := range sumZZ { - s := a.z.Sign() - e := a.z.Cmp(&zero) - if s != e { - t.Errorf("got %d; want %d for z = %v", s, e, a.z) - } - } -} - -func TestSetZ(t *testing.T) { - for _, a := range sumZZ { - var z Int - z.Set(a.z) - if !isNormalized(&z) { - t.Errorf("%v is not normalized", z) - } - if (&z).Cmp(a.z) != 0 { - t.Errorf("got z = %v; want %v", z, a.z) - } - } -} - -func TestAbsZ(t *testing.T) { - var zero Int - for _, a := range sumZZ { - var z Int - z.Abs(a.z) - var e Int - e.Set(a.z) - if e.Cmp(&zero) < 0 { - e.Sub(&zero, &e) - } - if z.Cmp(&e) != 0 { - t.Errorf("got z = %v; want %v", z, e) - } - } -} - -func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) { - var z Int - f(&z, a.x, a.y) - if !isNormalized(&z) { - t.Errorf("%s%v is not normalized", z, msg) - } - if (&z).Cmp(a.z) != 0 { - t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z) - } -} - -func TestSumZZ(t *testing.T) { - AddZZ := func(z, x, y *Int) *Int { return z.Add(x, y) } - SubZZ := func(z, x, y *Int) *Int { return z.Sub(x, y) } - for _, a := range sumZZ { - arg := a - testFunZZ(t, "AddZZ", AddZZ, arg) - - arg = argZZ{a.z, a.y, a.x} - testFunZZ(t, "AddZZ symmetric", AddZZ, arg) - - arg = argZZ{a.x, a.z, a.y} - testFunZZ(t, "SubZZ", SubZZ, arg) - - arg = argZZ{a.y, a.z, a.x} - testFunZZ(t, "SubZZ symmetric", SubZZ, arg) - } -} - -func TestProdZZ(t *testing.T) { - MulZZ := func(z, x, y *Int) *Int { return z.Mul(x, y) } - for _, a := range prodZZ { - arg := a - testFunZZ(t, "MulZZ", MulZZ, arg) - - arg = argZZ{a.z, a.y, a.x} - testFunZZ(t, "MulZZ symmetric", MulZZ, arg) - } -} - -// mulBytes returns x*y via grade school multiplication. Both inputs -// and the result are assumed to be in big-endian representation (to -// match the semantics of Int.Bytes and Int.SetBytes). -func mulBytes(x, y []byte) []byte { - z := make([]byte, len(x)+len(y)) - - // multiply - k0 := len(z) - 1 - for j := len(y) - 1; j >= 0; j-- { - d := int(y[j]) - if d != 0 { - k := k0 - carry := 0 - for i := len(x) - 1; i >= 0; i-- { - t := int(z[k]) + int(x[i])*d + carry - z[k], carry = byte(t), t>>8 - k-- - } - z[k] = byte(carry) - } - k0-- - } - - // normalize (remove leading 0's) - i := 0 - for i < len(z) && z[i] == 0 { - i++ - } - - return z[i:] -} - -func checkMul(a, b []byte) bool { - var x, y, z1 Int - x.SetBytes(a) - y.SetBytes(b) - z1.Mul(&x, &y) - - var z2 Int - z2.SetBytes(mulBytes(a, b)) - - return z1.Cmp(&z2) == 0 -} - -func TestMul(t *testing.T) { - if err := quick.Check(checkMul, nil); err != nil { - t.Error(err) - } -} - -var mulRangesZ = []struct { - a, b int64 - prod string -}{ - // entirely positive ranges are covered by mulRangesN - {-1, 1, "0"}, - {-2, -1, "2"}, - {-3, -2, "6"}, - {-3, -1, "-6"}, - {1, 3, "6"}, - {-10, -10, "-10"}, - {0, -1, "1"}, // empty range - {-1, -100, "1"}, // empty range - {-1, 1, "0"}, // range includes 0 - {-1e9, 0, "0"}, // range includes 0 - {-1e9, 1e9, "0"}, // range includes 0 - {-10, -1, "3628800"}, // 10! - {-20, -2, "-2432902008176640000"}, // -20! - {-99, -1, - "-933262154439441526816992388562667004907159682643816214685929" + - "638952175999932299156089414639761565182862536979208272237582" + - "511852109168640000000000000000000000", // -99! - }, -} - -func TestMulRangeZ(t *testing.T) { - var tmp Int - // test entirely positive ranges - for i, r := range mulRangesN { - prod := tmp.MulRange(int64(r.a), int64(r.b)).String() - if prod != r.prod { - t.Errorf("#%da: got %s; want %s", i, prod, r.prod) - } - } - // test other ranges - for i, r := range mulRangesZ { - prod := tmp.MulRange(r.a, r.b).String() - if prod != r.prod { - t.Errorf("#%db: got %s; want %s", i, prod, r.prod) - } - } -} - -var stringTests = []struct { - in string - out string - base int - val int64 - ok bool -}{ - {in: "", ok: false}, - {in: "a", ok: false}, - {in: "z", ok: false}, - {in: "+", ok: false}, - {in: "-", ok: false}, - {in: "0b", ok: false}, - {in: "0x", ok: false}, - {in: "2", base: 2, ok: false}, - {in: "0b2", base: 0, ok: false}, - {in: "08", ok: false}, - {in: "8", base: 8, ok: false}, - {in: "0xg", base: 0, ok: false}, - {in: "g", base: 16, ok: false}, - {"0", "0", 0, 0, true}, - {"0", "0", 10, 0, true}, - {"0", "0", 16, 0, true}, - {"+0", "0", 0, 0, true}, - {"-0", "0", 0, 0, true}, - {"10", "10", 0, 10, true}, - {"10", "10", 10, 10, true}, - {"10", "10", 16, 16, true}, - {"-10", "-10", 16, -16, true}, - {"+10", "10", 16, 16, true}, - {"0x10", "16", 0, 16, true}, - {in: "0x10", base: 16, ok: false}, - {"-0x10", "-16", 0, -16, true}, - {"+0x10", "16", 0, 16, true}, - {"00", "0", 0, 0, true}, - {"0", "0", 8, 0, true}, - {"07", "7", 0, 7, true}, - {"7", "7", 8, 7, true}, - {"023", "19", 0, 19, true}, - {"23", "23", 8, 19, true}, - {"cafebabe", "cafebabe", 16, 0xcafebabe, true}, - {"0b0", "0", 0, 0, true}, - {"-111", "-111", 2, -7, true}, - {"-0b111", "-7", 0, -7, true}, - {"0b1001010111", "599", 0, 0x257, true}, - {"1001010111", "1001010111", 2, 0x257, true}, -} - -func format(base int) string { - switch base { - case 2: - return "%b" - case 8: - return "%o" - case 16: - return "%x" - } - return "%d" -} - -func TestGetString(t *testing.T) { - z := new(Int) - for i, test := range stringTests { - if !test.ok { - continue - } - z.SetInt64(test.val) - - if test.base == 10 { - s := z.String() - if s != test.out { - t.Errorf("#%da got %s; want %s", i, s, test.out) - } - } - - s := fmt.Sprintf(format(test.base), z) - if s != test.out { - t.Errorf("#%db got %s; want %s", i, s, test.out) - } - } -} - -func TestSetString(t *testing.T) { - tmp := new(Int) - for i, test := range stringTests { - n1, ok1 := new(Int).SetString(test.in, test.base) - n2, ok2 := tmp.SetString(test.in, test.base) - expected := NewInt(test.val) - if ok1 != test.ok || ok2 != test.ok { - t.Errorf("#%d (input '%s') ok incorrect (should be %t)", i, test.in, test.ok) - continue - } - if !ok1 || !ok2 { - continue - } - - if ok1 && !isNormalized(n1) { - t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n1) - } - if ok2 && !isNormalized(n2) { - t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n2) - } - - if n1.Cmp(expected) != 0 { - t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n1, test.val) - } - if n2.Cmp(expected) != 0 { - t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n2, test.val) - } - } -} - -var formatTests = []struct { - input string - format string - output string -}{ - {"", "%x", ""}, - {"", "%#x", ""}, - {"", "%#y", "%!y(big.Int=)"}, - - {"10", "%b", "1010"}, - {"10", "%o", "12"}, - {"10", "%d", "10"}, - {"10", "%v", "10"}, - {"10", "%x", "a"}, - {"10", "%X", "A"}, - {"-10", "%X", "-A"}, - {"10", "%y", "%!y(big.Int=10)"}, - {"-10", "%y", "%!y(big.Int=-10)"}, - - {"10", "%#b", "1010"}, - {"10", "%#o", "012"}, - {"10", "%#d", "10"}, - {"10", "%#v", "10"}, - {"10", "%#x", "0xa"}, - {"10", "%#X", "0XA"}, - {"-10", "%#X", "-0XA"}, - {"10", "%#y", "%!y(big.Int=10)"}, - {"-10", "%#y", "%!y(big.Int=-10)"}, - - {"1234", "%d", "1234"}, - {"1234", "%3d", "1234"}, - {"1234", "%4d", "1234"}, - {"-1234", "%d", "-1234"}, - {"1234", "% 5d", " 1234"}, - {"1234", "%+5d", "+1234"}, - {"1234", "%-5d", "1234 "}, - {"1234", "%x", "4d2"}, - {"1234", "%X", "4D2"}, - {"-1234", "%3x", "-4d2"}, - {"-1234", "%4x", "-4d2"}, - {"-1234", "%5x", " -4d2"}, - {"-1234", "%-5x", "-4d2 "}, - {"1234", "%03d", "1234"}, - {"1234", "%04d", "1234"}, - {"1234", "%05d", "01234"}, - {"1234", "%06d", "001234"}, - {"-1234", "%06d", "-01234"}, - {"1234", "%+06d", "+01234"}, - {"1234", "% 06d", " 01234"}, - {"1234", "%-6d", "1234 "}, - {"1234", "%-06d", "1234 "}, - {"-1234", "%-06d", "-1234 "}, - - {"1234", "%.3d", "1234"}, - {"1234", "%.4d", "1234"}, - {"1234", "%.5d", "01234"}, - {"1234", "%.6d", "001234"}, - {"-1234", "%.3d", "-1234"}, - {"-1234", "%.4d", "-1234"}, - {"-1234", "%.5d", "-01234"}, - {"-1234", "%.6d", "-001234"}, - - {"1234", "%8.3d", " 1234"}, - {"1234", "%8.4d", " 1234"}, - {"1234", "%8.5d", " 01234"}, - {"1234", "%8.6d", " 001234"}, - {"-1234", "%8.3d", " -1234"}, - {"-1234", "%8.4d", " -1234"}, - {"-1234", "%8.5d", " -01234"}, - {"-1234", "%8.6d", " -001234"}, - - {"1234", "%+8.3d", " +1234"}, - {"1234", "%+8.4d", " +1234"}, - {"1234", "%+8.5d", " +01234"}, - {"1234", "%+8.6d", " +001234"}, - {"-1234", "%+8.3d", " -1234"}, - {"-1234", "%+8.4d", " -1234"}, - {"-1234", "%+8.5d", " -01234"}, - {"-1234", "%+8.6d", " -001234"}, - - {"1234", "% 8.3d", " 1234"}, - {"1234", "% 8.4d", " 1234"}, - {"1234", "% 8.5d", " 01234"}, - {"1234", "% 8.6d", " 001234"}, - {"-1234", "% 8.3d", " -1234"}, - {"-1234", "% 8.4d", " -1234"}, - {"-1234", "% 8.5d", " -01234"}, - {"-1234", "% 8.6d", " -001234"}, - - {"1234", "%.3x", "4d2"}, - {"1234", "%.4x", "04d2"}, - {"1234", "%.5x", "004d2"}, - {"1234", "%.6x", "0004d2"}, - {"-1234", "%.3x", "-4d2"}, - {"-1234", "%.4x", "-04d2"}, - {"-1234", "%.5x", "-004d2"}, - {"-1234", "%.6x", "-0004d2"}, - - {"1234", "%8.3x", " 4d2"}, - {"1234", "%8.4x", " 04d2"}, - {"1234", "%8.5x", " 004d2"}, - {"1234", "%8.6x", " 0004d2"}, - {"-1234", "%8.3x", " -4d2"}, - {"-1234", "%8.4x", " -04d2"}, - {"-1234", "%8.5x", " -004d2"}, - {"-1234", "%8.6x", " -0004d2"}, - - {"1234", "%+8.3x", " +4d2"}, - {"1234", "%+8.4x", " +04d2"}, - {"1234", "%+8.5x", " +004d2"}, - {"1234", "%+8.6x", " +0004d2"}, - {"-1234", "%+8.3x", " -4d2"}, - {"-1234", "%+8.4x", " -04d2"}, - {"-1234", "%+8.5x", " -004d2"}, - {"-1234", "%+8.6x", " -0004d2"}, - - {"1234", "% 8.3x", " 4d2"}, - {"1234", "% 8.4x", " 04d2"}, - {"1234", "% 8.5x", " 004d2"}, - {"1234", "% 8.6x", " 0004d2"}, - {"1234", "% 8.7x", " 00004d2"}, - {"1234", "% 8.8x", " 000004d2"}, - {"-1234", "% 8.3x", " -4d2"}, - {"-1234", "% 8.4x", " -04d2"}, - {"-1234", "% 8.5x", " -004d2"}, - {"-1234", "% 8.6x", " -0004d2"}, - {"-1234", "% 8.7x", "-00004d2"}, - {"-1234", "% 8.8x", "-000004d2"}, - - {"1234", "%-8.3d", "1234 "}, - {"1234", "%-8.4d", "1234 "}, - {"1234", "%-8.5d", "01234 "}, - {"1234", "%-8.6d", "001234 "}, - {"1234", "%-8.7d", "0001234 "}, - {"1234", "%-8.8d", "00001234"}, - {"-1234", "%-8.3d", "-1234 "}, - {"-1234", "%-8.4d", "-1234 "}, - {"-1234", "%-8.5d", "-01234 "}, - {"-1234", "%-8.6d", "-001234 "}, - {"-1234", "%-8.7d", "-0001234"}, - {"-1234", "%-8.8d", "-00001234"}, - - {"16777215", "%b", "111111111111111111111111"}, // 2**24 - 1 - - {"0", "%.d", ""}, - {"0", "%.0d", ""}, - {"0", "%3.d", ""}, -} - -func TestFormat(t *testing.T) { - for i, test := range formatTests { - var x *Int - if test.input != "" { - var ok bool - x, ok = new(Int).SetString(test.input, 0) - if !ok { - t.Errorf("#%d failed reading input %s", i, test.input) - } - } - output := fmt.Sprintf(test.format, x) - if output != test.output { - t.Errorf("#%d got %q; want %q, {%q, %q, %q}", i, output, test.output, test.input, test.format, test.output) - } - } -} - -var scanTests = []struct { - input string - format string - output string - remaining int -}{ - {"1010", "%b", "10", 0}, - {"0b1010", "%v", "10", 0}, - {"12", "%o", "10", 0}, - {"012", "%v", "10", 0}, - {"10", "%d", "10", 0}, - {"10", "%v", "10", 0}, - {"a", "%x", "10", 0}, - {"0xa", "%v", "10", 0}, - {"A", "%X", "10", 0}, - {"-A", "%X", "-10", 0}, - {"+0b1011001", "%v", "89", 0}, - {"0xA", "%v", "10", 0}, - {"0 ", "%v", "0", 1}, - {"2+3", "%v", "2", 2}, - {"0XABC 12", "%v", "2748", 3}, -} - -func TestScan(t *testing.T) { - var buf bytes.Buffer - for i, test := range scanTests { - x := new(Int) - buf.Reset() - buf.WriteString(test.input) - if _, err := fmt.Fscanf(&buf, test.format, x); err != nil { - t.Errorf("#%d error: %s", i, err.String()) - } - if x.String() != test.output { - t.Errorf("#%d got %s; want %s", i, x.String(), test.output) - } - if buf.Len() != test.remaining { - t.Errorf("#%d got %d bytes remaining; want %d", i, buf.Len(), test.remaining) - } - } -} - -// Examples from the Go Language Spec, section "Arithmetic operators" -var divisionSignsTests = []struct { - x, y int64 - q, r int64 // T-division - d, m int64 // Euclidian division -}{ - {5, 3, 1, 2, 1, 2}, - {-5, 3, -1, -2, -2, 1}, - {5, -3, -1, 2, -1, 2}, - {-5, -3, 1, -2, 2, 1}, - {1, 2, 0, 1, 0, 1}, - {8, 4, 2, 0, 2, 0}, -} - -func TestDivisionSigns(t *testing.T) { - for i, test := range divisionSignsTests { - x := NewInt(test.x) - y := NewInt(test.y) - q := NewInt(test.q) - r := NewInt(test.r) - d := NewInt(test.d) - m := NewInt(test.m) - - q1 := new(Int).Quo(x, y) - r1 := new(Int).Rem(x, y) - if !isNormalized(q1) { - t.Errorf("#%d Quo: %v is not normalized", i, *q1) - } - if !isNormalized(r1) { - t.Errorf("#%d Rem: %v is not normalized", i, *r1) - } - if q1.Cmp(q) != 0 || r1.Cmp(r) != 0 { - t.Errorf("#%d QuoRem: got (%s, %s), want (%s, %s)", i, q1, r1, q, r) - } - - q2, r2 := new(Int).QuoRem(x, y, new(Int)) - if !isNormalized(q2) { - t.Errorf("#%d Quo: %v is not normalized", i, *q2) - } - if !isNormalized(r2) { - t.Errorf("#%d Rem: %v is not normalized", i, *r2) - } - if q2.Cmp(q) != 0 || r2.Cmp(r) != 0 { - t.Errorf("#%d QuoRem: got (%s, %s), want (%s, %s)", i, q2, r2, q, r) - } - - d1 := new(Int).Div(x, y) - m1 := new(Int).Mod(x, y) - if !isNormalized(d1) { - t.Errorf("#%d Div: %v is not normalized", i, *d1) - } - if !isNormalized(m1) { - t.Errorf("#%d Mod: %v is not normalized", i, *m1) - } - if d1.Cmp(d) != 0 || m1.Cmp(m) != 0 { - t.Errorf("#%d DivMod: got (%s, %s), want (%s, %s)", i, d1, m1, d, m) - } - - d2, m2 := new(Int).DivMod(x, y, new(Int)) - if !isNormalized(d2) { - t.Errorf("#%d Div: %v is not normalized", i, *d2) - } - if !isNormalized(m2) { - t.Errorf("#%d Mod: %v is not normalized", i, *m2) - } - if d2.Cmp(d) != 0 || m2.Cmp(m) != 0 { - t.Errorf("#%d DivMod: got (%s, %s), want (%s, %s)", i, d2, m2, d, m) - } - } -} - -func checkSetBytes(b []byte) bool { - hex1 := hex.EncodeToString(new(Int).SetBytes(b).Bytes()) - hex2 := hex.EncodeToString(b) - - for len(hex1) < len(hex2) { - hex1 = "0" + hex1 - } - - for len(hex1) > len(hex2) { - hex2 = "0" + hex2 - } - - return hex1 == hex2 -} - -func TestSetBytes(t *testing.T) { - if err := quick.Check(checkSetBytes, nil); err != nil { - t.Error(err) - } -} - -func checkBytes(b []byte) bool { - b2 := new(Int).SetBytes(b).Bytes() - return bytes.Compare(b, b2) == 0 -} - -func TestBytes(t *testing.T) { - if err := quick.Check(checkSetBytes, nil); err != nil { - t.Error(err) - } -} - -func checkQuo(x, y []byte) bool { - u := new(Int).SetBytes(x) - v := new(Int).SetBytes(y) - - if len(v.abs) == 0 { - return true - } - - r := new(Int) - q, r := new(Int).QuoRem(u, v, r) - - if r.Cmp(v) >= 0 { - return false - } - - uprime := new(Int).Set(q) - uprime.Mul(uprime, v) - uprime.Add(uprime, r) - - return uprime.Cmp(u) == 0 -} - -var quoTests = []struct { - x, y string - q, r string -}{ - { - "476217953993950760840509444250624797097991362735329973741718102894495832294430498335824897858659711275234906400899559094370964723884706254265559534144986498357", - "9353930466774385905609975137998169297361893554149986716853295022578535724979483772383667534691121982974895531435241089241440253066816724367338287092081996", - "50911", - "1", - }, - { - "11510768301994997771168", - "1328165573307167369775", - "8", - "885443715537658812968", - }, -} - -func TestQuo(t *testing.T) { - if err := quick.Check(checkQuo, nil); err != nil { - t.Error(err) - } - - for i, test := range quoTests { - x, _ := new(Int).SetString(test.x, 10) - y, _ := new(Int).SetString(test.y, 10) - expectedQ, _ := new(Int).SetString(test.q, 10) - expectedR, _ := new(Int).SetString(test.r, 10) - - r := new(Int) - q, r := new(Int).QuoRem(x, y, r) - - if q.Cmp(expectedQ) != 0 || r.Cmp(expectedR) != 0 { - t.Errorf("#%d got (%s, %s) want (%s, %s)", i, q, r, expectedQ, expectedR) - } - } -} - -func TestQuoStepD6(t *testing.T) { - // See Knuth, Volume 2, section 4.3.1, exercise 21. This code exercises - // a code path which only triggers 1 in 10^{-19} cases. - - u := &Int{false, nat{0, 0, 1 + 1<<(_W-1), _M ^ (1 << (_W - 1))}} - v := &Int{false, nat{5, 2 + 1<<(_W-1), 1 << (_W - 1)}} - - r := new(Int) - q, r := new(Int).QuoRem(u, v, r) - const expectedQ64 = "18446744073709551613" - const expectedR64 = "3138550867693340382088035895064302439801311770021610913807" - const expectedQ32 = "4294967293" - const expectedR32 = "39614081266355540837921718287" - if q.String() != expectedQ64 && q.String() != expectedQ32 || - r.String() != expectedR64 && r.String() != expectedR32 { - t.Errorf("got (%s, %s) want (%s, %s) or (%s, %s)", q, r, expectedQ64, expectedR64, expectedQ32, expectedR32) - } -} - -var bitLenTests = []struct { - in string - out int -}{ - {"-1", 1}, - {"0", 0}, - {"1", 1}, - {"2", 2}, - {"4", 3}, - {"0xabc", 12}, - {"0x8000", 16}, - {"0x80000000", 32}, - {"0x800000000000", 48}, - {"0x8000000000000000", 64}, - {"0x80000000000000000000", 80}, - {"-0x4000000000000000000000", 87}, -} - -func TestBitLen(t *testing.T) { - for i, test := range bitLenTests { - x, ok := new(Int).SetString(test.in, 0) - if !ok { - t.Errorf("#%d test input invalid: %s", i, test.in) - continue - } - - if n := x.BitLen(); n != test.out { - t.Errorf("#%d got %d want %d", i, n, test.out) - } - } -} - -var expTests = []struct { - x, y, m string - out string -}{ - {"5", "0", "", "1"}, - {"-5", "0", "", "-1"}, - {"5", "1", "", "5"}, - {"-5", "1", "", "-5"}, - {"-2", "3", "2", "0"}, - {"5", "2", "", "25"}, - {"1", "65537", "2", "1"}, - {"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"}, - {"0x8000000000000000", "2", "6719", "4944"}, - {"0x8000000000000000", "3", "6719", "5447"}, - {"0x8000000000000000", "1000", "6719", "1603"}, - {"0x8000000000000000", "1000000", "6719", "3199"}, - { - "2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347", - "298472983472983471903246121093472394872319615612417471234712061", - "29834729834729834729347290846729561262544958723956495615629569234729836259263598127342374289365912465901365498236492183464", - "23537740700184054162508175125554701713153216681790245129157191391322321508055833908509185839069455749219131480588829346291", - }, -} - -func TestExp(t *testing.T) { - for i, test := range expTests { - x, ok1 := new(Int).SetString(test.x, 0) - y, ok2 := new(Int).SetString(test.y, 0) - out, ok3 := new(Int).SetString(test.out, 0) - - var ok4 bool - var m *Int - - if len(test.m) == 0 { - m, ok4 = nil, true - } else { - m, ok4 = new(Int).SetString(test.m, 0) - } - - if !ok1 || !ok2 || !ok3 || !ok4 { - t.Errorf("#%d: error in input", i) - continue - } - - z := y.Exp(x, y, m) - if !isNormalized(z) { - t.Errorf("#%d: %v is not normalized", i, *z) - } - if z.Cmp(out) != 0 { - t.Errorf("#%d: got %s want %s", i, z, out) - } - } -} - -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) - - GcdInt(d, x, y, a, b) - x.Mul(x, a) - y.Mul(y, b) - x.Add(x, y) - - return x.Cmp(d) == 0 -} - -var gcdTests = []struct { - a, b int64 - d, x, y int64 -}{ - {120, 23, 1, -9, 47}, -} - -func TestGcd(t *testing.T) { - for i, test := range gcdTests { - a := NewInt(test.a) - b := NewInt(test.b) - - x := new(Int) - y := new(Int) - d := new(Int) - - expectedX := NewInt(test.x) - expectedY := NewInt(test.y) - expectedD := NewInt(test.d) - - GcdInt(d, x, y, a, b) - - 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) - } - } - - quick.Check(checkGcd, nil) -} - -var primes = []string{ - "2", - "3", - "5", - "7", - "11", - - "13756265695458089029", - "13496181268022124907", - "10953742525620032441", - "17908251027575790097", - - // http://code.google.com/p/go/issues/detail?id=638 - "18699199384836356663", - - "98920366548084643601728869055592650835572950932266967461790948584315647051443", - "94560208308847015747498523884063394671606671904944666360068158221458669711639", - - // http://primes.utm.edu/lists/small/small3.html - "449417999055441493994709297093108513015373787049558499205492347871729927573118262811508386655998299074566974373711472560655026288668094291699357843464363003144674940345912431129144354948751003607115263071543163", - "230975859993204150666423538988557839555560243929065415434980904258310530753006723857139742334640122533598517597674807096648905501653461687601339782814316124971547968912893214002992086353183070342498989426570593", - "5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993", - "203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123", -} - -var composites = []string{ - "21284175091214687912771199898307297748211672914763848041968395774954376176754", - "6084766654921918907427900243509372380954290099172559290432744450051395395951", - "84594350493221918389213352992032324280367711247940675652888030554255915464401", - "82793403787388584738507275144194252681", -} - -func TestProbablyPrime(t *testing.T) { - nreps := 20 - if testing.Short() { - nreps = 1 - } - for i, s := range primes { - p, _ := new(Int).SetString(s, 10) - if !ProbablyPrime(p, nreps) { - t.Errorf("#%d prime found to be non-prime (%s)", i, s) - } - } - - for i, s := range composites { - c, _ := new(Int).SetString(s, 10) - if ProbablyPrime(c, nreps) { - t.Errorf("#%d composite found to be prime (%s)", i, s) - } - if testing.Short() { - break - } - } -} - -type intShiftTest struct { - in string - shift uint - out string -} - -var rshTests = []intShiftTest{ - {"0", 0, "0"}, - {"-0", 0, "0"}, - {"0", 1, "0"}, - {"0", 2, "0"}, - {"1", 0, "1"}, - {"1", 1, "0"}, - {"1", 2, "0"}, - {"2", 0, "2"}, - {"2", 1, "1"}, - {"-1", 0, "-1"}, - {"-1", 1, "-1"}, - {"-1", 10, "-1"}, - {"-100", 2, "-25"}, - {"-100", 3, "-13"}, - {"-100", 100, "-1"}, - {"4294967296", 0, "4294967296"}, - {"4294967296", 1, "2147483648"}, - {"4294967296", 2, "1073741824"}, - {"18446744073709551616", 0, "18446744073709551616"}, - {"18446744073709551616", 1, "9223372036854775808"}, - {"18446744073709551616", 2, "4611686018427387904"}, - {"18446744073709551616", 64, "1"}, - {"340282366920938463463374607431768211456", 64, "18446744073709551616"}, - {"340282366920938463463374607431768211456", 128, "1"}, -} - -func TestRsh(t *testing.T) { - for i, test := range rshTests { - in, _ := new(Int).SetString(test.in, 10) - expected, _ := new(Int).SetString(test.out, 10) - out := new(Int).Rsh(in, test.shift) - - if !isNormalized(out) { - t.Errorf("#%d: %v is not normalized", i, *out) - } - if out.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, out, expected) - } - } -} - -func TestRshSelf(t *testing.T) { - for i, test := range rshTests { - z, _ := new(Int).SetString(test.in, 10) - expected, _ := new(Int).SetString(test.out, 10) - z.Rsh(z, test.shift) - - if !isNormalized(z) { - t.Errorf("#%d: %v is not normalized", i, *z) - } - if z.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, z, expected) - } - } -} - -var lshTests = []intShiftTest{ - {"0", 0, "0"}, - {"0", 1, "0"}, - {"0", 2, "0"}, - {"1", 0, "1"}, - {"1", 1, "2"}, - {"1", 2, "4"}, - {"2", 0, "2"}, - {"2", 1, "4"}, - {"2", 2, "8"}, - {"-87", 1, "-174"}, - {"4294967296", 0, "4294967296"}, - {"4294967296", 1, "8589934592"}, - {"4294967296", 2, "17179869184"}, - {"18446744073709551616", 0, "18446744073709551616"}, - {"9223372036854775808", 1, "18446744073709551616"}, - {"4611686018427387904", 2, "18446744073709551616"}, - {"1", 64, "18446744073709551616"}, - {"18446744073709551616", 64, "340282366920938463463374607431768211456"}, - {"1", 128, "340282366920938463463374607431768211456"}, -} - -func TestLsh(t *testing.T) { - for i, test := range lshTests { - in, _ := new(Int).SetString(test.in, 10) - expected, _ := new(Int).SetString(test.out, 10) - out := new(Int).Lsh(in, test.shift) - - if !isNormalized(out) { - t.Errorf("#%d: %v is not normalized", i, *out) - } - if out.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, out, expected) - } - } -} - -func TestLshSelf(t *testing.T) { - for i, test := range lshTests { - z, _ := new(Int).SetString(test.in, 10) - expected, _ := new(Int).SetString(test.out, 10) - z.Lsh(z, test.shift) - - if !isNormalized(z) { - t.Errorf("#%d: %v is not normalized", i, *z) - } - if z.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, z, expected) - } - } -} - -func TestLshRsh(t *testing.T) { - for i, test := range rshTests { - in, _ := new(Int).SetString(test.in, 10) - out := new(Int).Lsh(in, test.shift) - out = out.Rsh(out, test.shift) - - if !isNormalized(out) { - t.Errorf("#%d: %v is not normalized", i, *out) - } - if in.Cmp(out) != 0 { - t.Errorf("#%d: got %s want %s", i, out, in) - } - } - for i, test := range lshTests { - in, _ := new(Int).SetString(test.in, 10) - out := new(Int).Lsh(in, test.shift) - out.Rsh(out, test.shift) - - if !isNormalized(out) { - t.Errorf("#%d: %v is not normalized", i, *out) - } - if in.Cmp(out) != 0 { - t.Errorf("#%d: got %s want %s", i, out, in) - } - } -} - -var int64Tests = []int64{ - 0, - 1, - -1, - 4294967295, - -4294967295, - 4294967296, - -4294967296, - 9223372036854775807, - -9223372036854775807, - -9223372036854775808, -} - -func TestInt64(t *testing.T) { - for i, testVal := range int64Tests { - in := NewInt(testVal) - out := in.Int64() - - if out != testVal { - t.Errorf("#%d got %d want %d", i, out, testVal) - } - } -} - -var bitwiseTests = []struct { - x, y string - and, or, xor, andNot string -}{ - {"0x00", "0x00", "0x00", "0x00", "0x00", "0x00"}, - {"0x00", "0x01", "0x00", "0x01", "0x01", "0x00"}, - {"0x01", "0x00", "0x00", "0x01", "0x01", "0x01"}, - {"-0x01", "0x00", "0x00", "-0x01", "-0x01", "-0x01"}, - {"-0xaf", "-0x50", "-0xf0", "-0x0f", "0xe1", "0x41"}, - {"0x00", "-0x01", "0x00", "-0x01", "-0x01", "0x00"}, - {"0x01", "0x01", "0x01", "0x01", "0x00", "0x00"}, - {"-0x01", "-0x01", "-0x01", "-0x01", "0x00", "0x00"}, - {"0x07", "0x08", "0x00", "0x0f", "0x0f", "0x07"}, - {"0x05", "0x0f", "0x05", "0x0f", "0x0a", "0x00"}, - {"0x013ff6", "0x9a4e", "0x1a46", "0x01bffe", "0x01a5b8", "0x0125b0"}, - {"-0x013ff6", "0x9a4e", "0x800a", "-0x0125b2", "-0x01a5bc", "-0x01c000"}, - {"-0x013ff6", "-0x9a4e", "-0x01bffe", "-0x1a46", "0x01a5b8", "0x8008"}, - { - "0x1000009dc6e3d9822cba04129bcbe3401", - "0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", - "0x1000001186210100001000009048c2001", - "0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd", - "0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc", - "0x8c40c2d8822caa04120b8321400", - }, - { - "0x1000009dc6e3d9822cba04129bcbe3401", - "-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", - "0x8c40c2d8822caa04120b8321401", - "-0xb9bd7d543685789d57ca918e82229142459020483cd2014001fd", - "-0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fe", - "0x1000001186210100001000009048c2000", - }, - { - "-0x1000009dc6e3d9822cba04129bcbe3401", - "-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", - "-0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd", - "-0x1000001186210100001000009048c2001", - "0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc", - "0xb9bd7d543685789d57ca918e82229142459020483cd2014001fc", - }, -} - -type bitFun func(z, x, y *Int) *Int - -func testBitFun(t *testing.T, msg string, f bitFun, x, y *Int, exp string) { - expected := new(Int) - expected.SetString(exp, 0) - - out := f(new(Int), x, y) - if out.Cmp(expected) != 0 { - t.Errorf("%s: got %s want %s", msg, out, expected) - } -} - -func testBitFunSelf(t *testing.T, msg string, f bitFun, x, y *Int, exp string) { - self := new(Int) - self.Set(x) - expected := new(Int) - expected.SetString(exp, 0) - - self = f(self, self, y) - if self.Cmp(expected) != 0 { - t.Errorf("%s: got %s want %s", msg, self, expected) - } -} - -func altBit(x *Int, i int) uint { - z := new(Int).Rsh(x, uint(i)) - z = z.And(z, NewInt(1)) - if z.Cmp(new(Int)) != 0 { - return 1 - } - return 0 -} - -func altSetBit(z *Int, x *Int, i int, b uint) *Int { - one := NewInt(1) - m := one.Lsh(one, uint(i)) - switch b { - case 1: - return z.Or(x, m) - case 0: - return z.AndNot(x, m) - } - panic("set bit is not 0 or 1") -} - -func testBitset(t *testing.T, x *Int) { - n := x.BitLen() - z := new(Int).Set(x) - z1 := new(Int).Set(x) - for i := 0; i < n+10; i++ { - old := z.Bit(i) - old1 := altBit(z1, i) - if old != old1 { - t.Errorf("bitset: inconsistent value for Bit(%s, %d), got %v want %v", z1, i, old, old1) - } - z := new(Int).SetBit(z, i, 1) - z1 := altSetBit(new(Int), z1, i, 1) - if z.Bit(i) == 0 { - t.Errorf("bitset: bit %d of %s got 0 want 1", i, x) - } - if z.Cmp(z1) != 0 { - t.Errorf("bitset: inconsistent value after SetBit 1, got %s want %s", z, z1) - } - z.SetBit(z, i, 0) - altSetBit(z1, z1, i, 0) - if z.Bit(i) != 0 { - t.Errorf("bitset: bit %d of %s got 1 want 0", i, x) - } - if z.Cmp(z1) != 0 { - t.Errorf("bitset: inconsistent value after SetBit 0, got %s want %s", z, z1) - } - altSetBit(z1, z1, i, old) - z.SetBit(z, i, old) - if z.Cmp(z1) != 0 { - t.Errorf("bitset: inconsistent value after SetBit old, got %s want %s", z, z1) - } - } - if z.Cmp(x) != 0 { - t.Errorf("bitset: got %s want %s", z, x) - } -} - -var bitsetTests = []struct { - x string - i int - b uint -}{ - {"0", 0, 0}, - {"0", 200, 0}, - {"1", 0, 1}, - {"1", 1, 0}, - {"-1", 0, 1}, - {"-1", 200, 1}, - {"0x2000000000000000000000000000", 108, 0}, - {"0x2000000000000000000000000000", 109, 1}, - {"0x2000000000000000000000000000", 110, 0}, - {"-0x2000000000000000000000000001", 108, 1}, - {"-0x2000000000000000000000000001", 109, 0}, - {"-0x2000000000000000000000000001", 110, 1}, -} - -func TestBitSet(t *testing.T) { - for _, test := range bitwiseTests { - x := new(Int) - x.SetString(test.x, 0) - testBitset(t, x) - x = new(Int) - x.SetString(test.y, 0) - testBitset(t, x) - } - for i, test := range bitsetTests { - x := new(Int) - x.SetString(test.x, 0) - b := x.Bit(test.i) - if b != test.b { - - t.Errorf("#%d want %v got %v", i, test.b, b) - } - } -} - -func BenchmarkBitset(b *testing.B) { - z := new(Int) - z.SetBit(z, 512, 1) - b.ResetTimer() - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - z.SetBit(z, i&512, 1) - } -} - -func BenchmarkBitsetNeg(b *testing.B) { - z := NewInt(-1) - z.SetBit(z, 512, 0) - b.ResetTimer() - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - z.SetBit(z, i&512, 0) - } -} - -func BenchmarkBitsetOrig(b *testing.B) { - z := new(Int) - altSetBit(z, z, 512, 1) - b.ResetTimer() - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - altSetBit(z, z, i&512, 1) - } -} - -func BenchmarkBitsetNegOrig(b *testing.B) { - z := NewInt(-1) - altSetBit(z, z, 512, 0) - b.ResetTimer() - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - altSetBit(z, z, i&512, 0) - } -} - -func TestBitwise(t *testing.T) { - x := new(Int) - y := new(Int) - for _, test := range bitwiseTests { - x.SetString(test.x, 0) - y.SetString(test.y, 0) - - testBitFun(t, "and", (*Int).And, x, y, test.and) - testBitFunSelf(t, "and", (*Int).And, x, y, test.and) - testBitFun(t, "andNot", (*Int).AndNot, x, y, test.andNot) - testBitFunSelf(t, "andNot", (*Int).AndNot, x, y, test.andNot) - testBitFun(t, "or", (*Int).Or, x, y, test.or) - testBitFunSelf(t, "or", (*Int).Or, x, y, test.or) - testBitFun(t, "xor", (*Int).Xor, x, y, test.xor) - testBitFunSelf(t, "xor", (*Int).Xor, x, y, test.xor) - } -} - -var notTests = []struct { - in string - out string -}{ - {"0", "-1"}, - {"1", "-2"}, - {"7", "-8"}, - {"0", "-1"}, - {"-81910", "81909"}, - { - "298472983472983471903246121093472394872319615612417471234712061", - "-298472983472983471903246121093472394872319615612417471234712062", - }, -} - -func TestNot(t *testing.T) { - in := new(Int) - out := new(Int) - expected := new(Int) - for i, test := range notTests { - in.SetString(test.in, 10) - expected.SetString(test.out, 10) - out = out.Not(in) - if out.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, out, expected) - } - out = out.Not(out) - if out.Cmp(in) != 0 { - t.Errorf("#%d: got %s want %s", i, out, in) - } - } -} - -var modInverseTests = []struct { - element string - prime string -}{ - {"1", "7"}, - {"1", "13"}, - {"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"}, -} - -func TestModInverse(t *testing.T) { - var element, prime Int - one := NewInt(1) - for i, test := range modInverseTests { - (&element).SetString(test.element, 10) - (&prime).SetString(test.prime, 10) - inverse := new(Int).ModInverse(&element, &prime) - inverse.Mul(inverse, &element) - inverse.Mod(inverse, &prime) - if inverse.Cmp(one) != 0 { - t.Errorf("#%d: failed (e·e^(-1)=%s)", i, inverse) - } - } -} - -// used by TestIntGobEncoding and TestRatGobEncoding -var gobEncodingTests = []string{ - "0", - "1", - "2", - "10", - "42", - "1234567890", - "298472983472983471903246121093472394872319615612417471234712061", -} - -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) - } - } - } -} diff --git a/src/pkg/big/nat.go b/src/pkg/big/nat.go deleted file mode 100755 index be3aff29d..000000000 --- a/src/pkg/big/nat.go +++ /dev/null @@ -1,1272 +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. - -// Package big implements multi-precision arithmetic (big numbers). -// The following numeric types are supported: -// -// - Int signed integers -// - Rat rational numbers -// -// All methods on Int take the result as the receiver; if it is one -// of the operands it may be overwritten (and its memory reused). -// To enable chaining of operations, the result is also returned. -// -package big - -// This file contains operations on unsigned multi-precision integers. -// These are the building blocks for the operations on signed integers -// and rationals. - -import ( - "io" - "os" - "rand" -) - -// An unsigned integer x of the form -// -// x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0] -// -// with 0 <= x[i] < _B and 0 <= i < n is stored in a slice of length n, -// with the digits x[i] as the slice elements. -// -// A number is normalized if the slice contains no leading 0 digits. -// During arithmetic operations, denormalized values may occur but are -// always normalized before returning the final result. The normalized -// representation of 0 is the empty or nil slice (length = 0). - -type nat []Word - -var ( - natOne = nat{1} - natTwo = nat{2} - natTen = nat{10} -) - -func (z nat) clear() { - for i := range z { - z[i] = 0 - } -} - -func (z nat) norm() nat { - i := len(z) - for i > 0 && z[i-1] == 0 { - i-- - } - return z[0:i] -} - -func (z nat) make(n int) nat { - if n <= cap(z) { - return z[0:n] // reuse z - } - // Choosing a good value for e has significant performance impact - // because it increases the chance that a value can be reused. - const e = 4 // extra capacity - return make(nat, n, n+e) -} - -func (z nat) setWord(x Word) nat { - if x == 0 { - return z.make(0) - } - z = z.make(1) - z[0] = x - return z -} - -func (z nat) setUint64(x uint64) nat { - // single-digit values - if w := Word(x); uint64(w) == x { - return z.setWord(w) - } - - // compute number of words n required to represent x - n := 0 - for t := x; t > 0; t >>= _W { - n++ - } - - // split x into n words - z = z.make(n) - for i := range z { - z[i] = Word(x & _M) - x >>= _W - } - - return z -} - -func (z nat) set(x nat) nat { - z = z.make(len(x)) - copy(z, x) - return z -} - -func (z nat) add(x, y nat) nat { - m := len(x) - n := len(y) - - switch { - case m < n: - return z.add(y, x) - case m == 0: - // n == 0 because m >= n; result is 0 - return z.make(0) - case n == 0: - // result is x - return z.set(x) - } - // m > 0 - - z = z.make(m + 1) - c := addVV(z[0:n], x, y) - if m > n { - c = addVW(z[n:m], x[n:], c) - } - z[m] = c - - return z.norm() -} - -func (z nat) sub(x, y nat) nat { - m := len(x) - n := len(y) - - switch { - case m < n: - panic("underflow") - case m == 0: - // n == 0 because m >= n; result is 0 - return z.make(0) - case n == 0: - // result is x - return z.set(x) - } - // m > 0 - - z = z.make(m) - c := subVV(z[0:n], x, y) - if m > n { - c = subVW(z[n:], x[n:], c) - } - if c != 0 { - panic("underflow") - } - - return z.norm() -} - -func (x nat) cmp(y nat) (r int) { - m := len(x) - n := len(y) - if m != n || m == 0 { - switch { - case m < n: - r = -1 - case m > n: - r = 1 - } - return - } - - i := m - 1 - for i > 0 && x[i] == y[i] { - i-- - } - - switch { - case x[i] < y[i]: - r = -1 - case x[i] > y[i]: - r = 1 - } - return -} - -func (z nat) mulAddWW(x nat, y, r Word) nat { - m := len(x) - if m == 0 || y == 0 { - return z.setWord(r) // result is r - } - // m > 0 - - z = z.make(m + 1) - z[m] = mulAddVWW(z[0:m], x, y, r) - - return z.norm() -} - -// basicMul multiplies x and y and leaves the result in z. -// The (non-normalized) result is placed in z[0 : len(x) + len(y)]. -func basicMul(z, x, y nat) { - z[0 : len(x)+len(y)].clear() // initialize z - for i, d := range y { - if d != 0 { - z[len(x)+i] = addMulVVW(z[i:i+len(x)], x, d) - } - } -} - -// Fast version of z[0:n+n>>1].add(z[0:n+n>>1], x[0:n]) w/o bounds checks. -// Factored out for readability - do not use outside karatsuba. -func karatsubaAdd(z, x nat, n int) { - if c := addVV(z[0:n], z, x); c != 0 { - addVW(z[n:n+n>>1], z[n:], c) - } -} - -// Like karatsubaAdd, but does subtract. -func karatsubaSub(z, x nat, n int) { - if c := subVV(z[0:n], z, x); c != 0 { - subVW(z[n:n+n>>1], z[n:], c) - } -} - -// 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 - -// 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 -// power of 2. The result vector z must have len(z) >= 6*n. -// The (non-normalized) result is placed in z[0 : 2*n]. -func karatsuba(z, x, y nat) { - n := len(y) - - // Switch to basic multiplication if numbers are odd or small. - // (n is always even if karatsubaThreshold is even, but be - // conservative) - if n&1 != 0 || n < karatsubaThreshold || n < 2 { - basicMul(z, x, y) - return - } - // n&1 == 0 && n >= karatsubaThreshold && n >= 2 - - // Karatsuba multiplication is based on the observation that - // for two numbers x and y with: - // - // x = x1*b + x0 - // y = y1*b + y0 - // - // the product x*y can be obtained with 3 products z2, z1, z0 - // instead of 4: - // - // x*y = x1*y1*b*b + (x1*y0 + x0*y1)*b + x0*y0 - // = z2*b*b + z1*b + z0 - // - // with: - // - // xd = x1 - x0 - // yd = y0 - y1 - // - // z1 = xd*yd + z1 + z0 - // = (x1-x0)*(y0 - y1) + z1 + z0 - // = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z1 + z0 - // = x1*y0 - z1 - z0 + x0*y1 + z1 + z0 - // = x1*y0 + x0*y1 - - // split x, y into "digits" - n2 := n >> 1 // n2 >= 1 - x1, x0 := x[n2:], x[0:n2] // x = x1*b + y0 - y1, y0 := y[n2:], y[0:n2] // y = y1*b + y0 - - // z is used for the result and temporary storage: - // - // 6*n 5*n 4*n 3*n 2*n 1*n 0*n - // z = [z2 copy|z0 copy| xd*yd | yd:xd | x1*y1 | x0*y0 ] - // - // For each recursive call of karatsuba, an unused slice of - // z is passed in that has (at least) half the length of the - // caller's z. - - // compute z0 and z2 with the result "in place" in z - karatsuba(z, x0, y0) // z0 = x0*y0 - karatsuba(z[n:], x1, y1) // z2 = x1*y1 - - // compute xd (or the negative value if underflow occurs) - s := 1 // sign of product xd*yd - xd := z[2*n : 2*n+n2] - if subVV(xd, x1, x0) != 0 { // x1-x0 - s = -s - subVV(xd, x0, x1) // x0-x1 - } - - // compute yd (or the negative value if underflow occurs) - yd := z[2*n+n2 : 3*n] - if subVV(yd, y0, y1) != 0 { // y0-y1 - s = -s - subVV(yd, y1, y0) // y1-y0 - } - - // p = (x1-x0)*(y0-y1) == x1*y0 - x1*y1 - x0*y0 + x0*y1 for s > 0 - // p = (x0-x1)*(y0-y1) == x0*y0 - x0*y1 - x1*y0 + x1*y1 for s < 0 - p := z[n*3:] - karatsuba(p, xd, yd) - - // save original z2:z0 - // (ok to use upper half of z since we're done recursing) - r := z[n*4:] - copy(r, z) - - // add up all partial products - // - // 2*n n 0 - // z = [ z2 | z0 ] - // + [ z0 ] - // + [ z2 ] - // + [ p ] - // - karatsubaAdd(z[n2:], r, n) - karatsubaAdd(z[n2:], r[n:], n) - if s > 0 { - karatsubaAdd(z[n2:], p, n) - } else { - karatsubaSub(z[n2:], p, n) - } -} - -// alias returns true if x and y share the same base array. -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. -// (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) { - if n := len(x); n > 0 { - if c := addVV(z[i:i+n], z[i:], x); c != 0 { - j := i + n - if j < len(z) { - addVW(z[j:], z[j:], c) - } - } - } -} - -func max(x, y int) int { - if x > y { - return x - } - return y -} - -// karatsubaLen computes an approximation to the maximum k <= n such that -// k = p<= 0. Thus, the -// result is the largest number that can be divided repeatedly by 2 before -// becoming about the value of karatsubaThreshold. -func karatsubaLen(n int) int { - i := uint(0) - for n > karatsubaThreshold { - n >>= 1 - i++ - } - return n << i -} - -func (z nat) mul(x, y nat) nat { - m := len(x) - n := len(y) - - switch { - case m < n: - return z.mul(y, x) - case m == 0 || n == 0: - return z.make(0) - case n == 1: - return z.mulAddWW(x, y[0], 0) - } - // m >= n > 1 - - // determine if z can be reused - if alias(z, x) || alias(z, y) { - z = nil // z is an alias for x or y - cannot reuse - } - - // use basic multiplication if the numbers are small - if n < karatsubaThreshold || n < 2 { - z = z.make(m + n) - basicMul(z, x, y) - return z.norm() - } - // m >= n && n >= karatsubaThreshold && n >= 2 - - // determine Karatsuba length k such that - // - // x = x1*b + x0 - // y = y1*b + y0 (and k <= len(y), which implies k <= len(x)) - // b = 1<<(_W*k) ("base" of digits xi, yi) - // - k := karatsubaLen(n) - // k <= n - - // multiply x0 and y0 via Karatsuba - x0 := x[0:k] // x0 is not normalized - 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 - - // If x1 and/or y1 are not 0, add missing terms to z explicitly: - // - // m+n 2*k 0 - // z = [ ... | x0*y0 ] - // + [ x1*y1 ] - // + [ x1*y0 ] - // + [ x0*y1 ] - // - 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) - addAt(z, t, k) - } - - return z.norm() -} - -// mulRange computes the product of all the unsigned integers in the -// range [a, b] inclusively. If a > b (empty range), the result is 1. -func (z nat) mulRange(a, b uint64) nat { - switch { - case a == 0: - // cut long ranges short (optimization) - return z.setUint64(0) - case a > b: - return z.setUint64(1) - case a == b: - return z.setUint64(a) - case a+1 == b: - return z.mul(nat(nil).setUint64(a), nat(nil).setUint64(b)) - } - m := (a + b) / 2 - return z.mul(nat(nil).mulRange(a, m), nat(nil).mulRange(m+1, b)) -} - -// q = (x-r)/y, with 0 <= r < y -func (z nat) divW(x nat, y Word) (q nat, r Word) { - m := len(x) - switch { - case y == 0: - panic("division by zero") - case y == 1: - q = z.set(x) // result is x - return - case m == 0: - q = z.make(0) // result is 0 - return - } - // m > 0 - z = z.make(m) - r = divWVW(z, 0, x, y) - q = z.norm() - return -} - -func (z nat) div(z2, u, v nat) (q, r nat) { - if len(v) == 0 { - panic("division by zero") - } - - if u.cmp(v) < 0 { - q = z.make(0) - r = z2.set(u) - return - } - - 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) - } - return - } - - q, r = z.divLarge(z2, u, v) - return -} - -// q = (uIn-r)/v, with 0 <= r < y -// Uses z as storage for q, and u as storage for r if possible. -// See Knuth, Volume 2, section 4.3.1, Algorithm D. -// Preconditions: -// len(v) >= 2 -// len(uIn) >= len(v) -func (z nat) divLarge(u, uIn, v nat) (q, r nat) { - n := len(v) - m := len(uIn) - n - - // determine if z can be reused - // TODO(gri) should find a better solution - this if statement - // is very costly (see e.g. time pidigits -s -n 10000) - if alias(z, uIn) || alias(z, v) { - z = nil // z is an alias for uIn or v - cannot reuse - } - q = z.make(m + 1) - - qhatv := make(nat, n+1) - if alias(u, uIn) || alias(u, v) { - u = nil // u is an alias for uIn or v - cannot reuse - } - u = u.make(len(uIn) + 1) - u.clear() - - // D1. - shift := leadingZeros(v[n-1]) - if shift > 0 { - // do not modify v, it may be used by another goroutine simultaneously - v1 := make(nat, n) - shlVU(v1, v, shift) - v = v1 - } - u[len(uIn)] = shlVU(u[0:len(uIn)], uIn, shift) - - // D2. - for j := m; j >= 0; j-- { - // D3. - qhat := Word(_M) - if u[j+n] != v[n-1] { - var rhat Word - qhat, rhat = divWW(u[j+n], u[j+n-1], v[n-1]) - - // x1 | x2 = q̂v_{n-2} - x1, x2 := mulWW(qhat, v[n-2]) - // test if q̂v_{n-2} > br̂ + u_{j+n-2} - for greaterThan(x1, x2, rhat, u[j+n-2]) { - qhat-- - prevRhat := rhat - rhat += v[n-1] - // v[n-1] >= 0, so this tests for overflow. - if rhat < prevRhat { - break - } - x1, x2 = mulWW(qhat, v[n-2]) - } - } - - // D4. - qhatv[n] = mulAddVWW(qhatv[0:n], v, qhat, 0) - - c := subVV(u[j:j+len(qhatv)], u[j:], qhatv) - if c != 0 { - c := addVV(u[j:j+n], u[j:], v) - u[j+n] += c - qhat-- - } - - q[j] = qhat - } - - q = q.norm() - shrVU(u, u, shift) - r = u.norm() - - return q, r -} - -// Length of x in bits. x must be normalized. -func (x nat) bitLen() int { - if i := len(x) - 1; i >= 0 { - return i*_W + bitLen(x[i]) - } - return 0 -} - -// MaxBase is the largest number base accepted for string conversions. -const MaxBase = 'z' - 'a' + 10 + 1 // = hexValue('z') + 1 - - -func hexValue(ch int) Word { - d := MaxBase + 1 // illegal base - switch { - case '0' <= ch && ch <= '9': - d = ch - '0' - case 'a' <= ch && ch <= 'z': - d = ch - 'a' + 10 - case 'A' <= ch && ch <= 'Z': - d = ch - 'A' + 10 - } - return Word(d) -} - -// scan sets z to the natural number corresponding to the longest possible prefix -// read from r representing an unsigned integer in a given conversion base. -// It returns z, the actual conversion base used, and an error, if any. In the -// error case, the value of z is undefined. The syntax follows the syntax of -// unsigned integer literals in Go. -// -// The base argument must be 0 or a value from 2 through MaxBase. If the base -// is 0, the string prefix determines the actual conversion base. A prefix of -// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a -// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. -// -func (z nat) scan(r io.RuneScanner, base int) (nat, int, os.Error) { - // reject illegal bases - if base < 0 || base == 1 || MaxBase < base { - return z, 0, os.NewError("illegal number base") - } - - // one char look-ahead - ch, _, err := r.ReadRune() - if err != nil { - return z, 0, err - } - - // determine base if necessary - b := Word(base) - if base == 0 { - b = 10 - if ch == '0' { - switch ch, _, err = r.ReadRune(); err { - case nil: - b = 8 - switch ch { - case 'x', 'X': - b = 16 - case 'b', 'B': - b = 2 - } - if b == 2 || b == 16 { - if ch, _, err = r.ReadRune(); err != nil { - return z, 0, err - } - } - case os.EOF: - return z, 10, nil - default: - return z, 10, err - } - } - } - - // convert string - // - group as many digits d as possible together into a "super-digit" dd with "super-base" bb - // - only when bb does not fit into a word anymore, do a full number mulAddWW using bb and dd - z = z.make(0) - bb := Word(1) - dd := Word(0) - for max := _M / b; ; { - d := hexValue(ch) - if d >= b { - r.UnreadRune() // ch does not belong to number anymore - break - } - - if bb <= max { - bb *= b - dd = dd*b + d - } else { - // bb * b would overflow - z = z.mulAddWW(z, bb, dd) - bb = b - dd = d - } - - if ch, _, err = r.ReadRune(); err != nil { - if err != os.EOF { - return z, int(b), err - } - break - } - } - - switch { - case bb > 1: - // there was at least one mantissa digit - z = z.mulAddWW(z, bb, dd) - case base == 0 && b == 8: - // there was only the octal prefix 0 (possibly followed by digits > 7); - // return base 10, not 8 - return z, 10, nil - case base != 0 || b != 8: - // there was neither a mantissa digit nor the octal prefix 0 - return z, int(b), os.NewError("syntax error scanning number") - } - - return z.norm(), int(b), nil -} - -// Character sets for string conversion. -const ( - lowercaseDigits = "0123456789abcdefghijklmnopqrstuvwxyz" - uppercaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" -) - -// decimalString returns a decimal representation of x. -// It calls x.string with the charset "0123456789". -func (x nat) decimalString() string { - return x.string(lowercaseDigits[0:10]) -} - -// string converts x to a string using digits from a charset; a digit with -// value d is represented by charset[d]. The conversion base is determined -// by len(charset), which must be >= 2. -func (x nat) string(charset string) string { - b := Word(len(charset)) - - // special cases - switch { - case b < 2 || b > 256: - panic("illegal base") - case len(x) == 0: - return string(charset[0]) - } - - // allocate buffer for conversion - i := x.bitLen()/log2(b) + 1 // +1: round up - s := make([]byte, i) - - // special case: power of two bases can avoid divisions completely - if b == b&-b { - // shift is base-b digit size in bits - shift := uint(trailingZeroBits(b)) // shift > 0 because b >= 2 - mask := Word(1)<= shift { - i-- - s[i] = charset[w&mask] - w >>= shift - nbits -= shift - } - - // convert any partial leading digit and advance to next word - if nbits == 0 { - // no partial digit remaining, just advance - w = x[k] - nbits = _W - } else { - // partial digit in current (k-1) and next (k) word - w |= x[k] << nbits - i-- - s[i] = charset[w&mask] - - // advance - w = x[k] >> (shift - nbits) - nbits = _W - (shift - nbits) - } - } - - // convert digits of most-significant word (omit leading zeros) - for nbits >= 0 && w != 0 { - i-- - s[i] = charset[w&mask] - w >>= shift - nbits -= shift - } - - return string(s[i:]) - } - - // general case: extract groups of digits by multiprecision division - - // maximize ndigits where b**ndigits < 2^_W; bb (big base) is b**ndigits - bb := Word(1) - ndigits := 0 - for max := Word(_M / b); bb <= max; bb *= b { - ndigits++ - } - - // preserve x, create local copy for use in repeated divisions - q := nat(nil).set(x) - var r Word - - // convert - if b == 10 { // hard-coding for 10 here speeds this up by 1.25x - for len(q) > 0 { - // extract least significant, base bb "digit" - q, r = q.divW(q, bb) // N.B. >82% of time is here. Optimize divW - if len(q) == 0 { - // skip leading zeros in most-significant group of digits - for j := 0; j < ndigits && r != 0; j++ { - i-- - s[i] = charset[r%10] - r /= 10 - } - } else { - for j := 0; j < ndigits; j++ { - i-- - s[i] = charset[r%10] - r /= 10 - } - } - } - } else { - for len(q) > 0 { - // extract least significant group of digits - q, r = q.divW(q, bb) // N.B. >82% of time is here. Optimize divW - if len(q) == 0 { - // skip leading zeros in most-significant group of digits - for j := 0; j < ndigits && r != 0; j++ { - i-- - s[i] = charset[r%b] - r /= b - } - } else { - for j := 0; j < ndigits; j++ { - i-- - s[i] = charset[r%b] - r /= b - } - } - } - } - - return string(s[i:]) -} - -const deBruijn32 = 0x077CB531 - -var deBruijn32Lookup = []byte{ - 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, - 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9, -} - -const deBruijn64 = 0x03f79d71b4ca8b09 - -var deBruijn64Lookup = []byte{ - 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, - 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, - 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, - 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 { - // 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 - // left shifting, in this case by k bits. The de Bruijn constant is - // such that all six bit, consecutive substrings are distinct. - // 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. - switch _W { - case 32: - return int(deBruijn32Lookup[((x&-x)*deBruijn32)>>27]) - case 64: - return int(deBruijn64Lookup[((x&-x)*(deBruijn64&_M))>>58]) - default: - panic("Unknown word size") - } - - return 0 -} - -// z = x << s -func (z nat) shl(x nat, s uint) nat { - m := len(x) - if m == 0 { - return z.make(0) - } - // m > 0 - - n := m + int(s/_W) - z = z.make(n + 1) - z[n] = shlVU(z[n-m:n], x, s%_W) - z[0 : n-m].clear() - - return z.norm() -} - -// z = x >> s -func (z nat) shr(x nat, s uint) nat { - m := len(x) - n := m - int(s/_W) - if n <= 0 { - return z.make(0) - } - // n > 0 - - z = z.make(n) - shrVU(z, x[m-n:], s%_W) - - return z.norm() -} - -func (z nat) setBit(x nat, i uint, b uint) nat { - j := int(i / _W) - m := Word(1) << (i % _W) - n := len(x) - switch b { - case 0: - z = z.make(n) - copy(z, x) - if j >= n { - // no need to grow - return z - } - z[j] &^= m - return z.norm() - case 1: - if j >= n { - n = j + 1 - } - z = z.make(n) - copy(z, x) - z[j] |= m - // no need to normalize - return z - } - panic("set bit is not 0 or 1") -} - -func (z nat) bit(i uint) uint { - j := int(i / _W) - if j >= len(z) { - return 0 - } - return uint(z[j] >> (i % _W) & 1) -} - -func (z nat) and(x, y nat) nat { - m := len(x) - n := len(y) - if m > n { - m = n - } - // m <= n - - z = z.make(m) - for i := 0; i < m; i++ { - z[i] = x[i] & y[i] - } - - return z.norm() -} - -func (z nat) andNot(x, y nat) nat { - m := len(x) - n := len(y) - if n > m { - n = m - } - // m >= n - - z = z.make(m) - for i := 0; i < n; i++ { - z[i] = x[i] &^ y[i] - } - copy(z[n:m], x[n:m]) - - return z.norm() -} - -func (z nat) or(x, y nat) nat { - m := len(x) - n := len(y) - s := x - if m < n { - n, m = m, n - s = y - } - // m >= n - - z = z.make(m) - for i := 0; i < n; i++ { - z[i] = x[i] | y[i] - } - copy(z[n:m], s[n:m]) - - return z.norm() -} - -func (z nat) xor(x, y nat) nat { - m := len(x) - n := len(y) - s := x - if m < n { - n, m = m, n - s = y - } - // m >= n - - z = z.make(m) - for i := 0; i < n; i++ { - z[i] = x[i] ^ y[i] - } - copy(z[n:m], s[n:m]) - - return z.norm() -} - -// greaterThan returns true iff (x1<<_W + x2) > (y1<<_W + y2) -func greaterThan(x1, x2, y1, y2 Word) bool { - return x1 > y1 || x1 == y1 && x2 > y2 -} - -// modW returns x % d. -func (x nat) modW(d Word) (r Word) { - // TODO(agl): we don't actually need to store the q value. - var q nat - q = q.make(len(x)) - return divWVW(q, 0, x, d) -} - -// powersOfTwoDecompose finds q and k with x = q * 1<= 0; i-- { - v = y[i] - - for j := 0; j < _W; j++ { - z = z.mul(z, z) - - if v&mask != 0 { - z = z.mul(z, x) - } - - if m != nil { - q, z = q.div(z, z, m) - } - - v <<= 1 - } - } - - return z -} - -// 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. -func (n nat) probablyPrime(reps int) bool { - if len(n) == 0 { - return false - } - - if len(n) == 1 { - if n[0] < 2 { - return false - } - - if n[0]%2 == 0 { - return n[0] == 2 - } - - // We have to exclude these cases because we reject all - // multiples of these numbers below. - switch n[0] { - case 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53: - return true - } - } - - const primesProduct32 = 0xC0CFD797 // Π {p ∈ primes, 2 < p <= 29} - const primesProduct64 = 0xE221F97C30E94E1D // Π {p ∈ primes, 2 < p <= 53} - - var r Word - switch _W { - case 32: - r = n.modW(primesProduct32) - case 64: - r = n.modW(primesProduct64 & _M) - default: - panic("Unknown word size") - } - - if r%3 == 0 || r%5 == 0 || r%7 == 0 || r%11 == 0 || - r%13 == 0 || r%17 == 0 || r%19 == 0 || r%23 == 0 || r%29 == 0 { - return false - } - - if _W == 64 && (r%31 == 0 || r%37 == 0 || r%41 == 0 || - r%43 == 0 || r%47 == 0 || r%53 == 0) { - return false - } - - nm1 := nat(nil).sub(n, natOne) - // 1<= len(z)*_S. The value of z is encoded in the -// slice buf[i:]. The number i of unused bytes at the beginning of -// buf is returned as result. -func (z nat) bytes(buf []byte) (i int) { - i = len(buf) - for _, d := range z { - for j := 0; j < _S; j++ { - i-- - buf[i] = byte(d) - d >>= 8 - } - } - - for i < len(buf) && buf[i] == 0 { - i++ - } - - return -} - -// setBytes interprets buf as the bytes of a big-endian unsigned -// integer, sets z to that value, and returns z. -func (z nat) setBytes(buf []byte) nat { - z = z.make((len(buf) + _S - 1) / _S) - - k := 0 - s := uint(0) - var d Word - for i := len(buf); i > 0; i-- { - d |= Word(buf[i-1]) << s - if s += 8; s == _S*8 { - z[k] = d - k++ - s = 0 - d = 0 - } - } - if k < len(z) { - z[k] = d - } - - return z.norm() -} diff --git a/src/pkg/big/nat_test.go b/src/pkg/big/nat_test.go deleted file mode 100755 index 71d086087..000000000 --- a/src/pkg/big/nat_test.go +++ /dev/null @@ -1,669 +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. - -package big - -import ( - "fmt" - "os" - "strings" - "testing" -) - -var cmpTests = []struct { - x, y nat - r int -}{ - {nil, nil, 0}, - {nil, nat{}, 0}, - {nat{}, nil, 0}, - {nat{}, nat{}, 0}, - {nat{0}, nat{0}, 0}, - {nat{0}, nat{1}, -1}, - {nat{1}, nat{0}, 1}, - {nat{1}, nat{1}, 0}, - {nat{0, _M}, nat{1}, 1}, - {nat{1}, nat{0, _M}, -1}, - {nat{1, _M}, nat{0, _M}, 1}, - {nat{0, _M}, nat{1, _M}, -1}, - {nat{16, 571956, 8794, 68}, nat{837, 9146, 1, 754489}, -1}, - {nat{34986, 41, 105, 1957}, nat{56, 7458, 104, 1957}, 1}, -} - -func TestCmp(t *testing.T) { - for i, a := range cmpTests { - r := a.x.cmp(a.y) - if r != a.r { - t.Errorf("#%d got r = %v; want %v", i, r, a.r) - } - } -} - -type funNN func(z, x, y nat) nat -type argNN struct { - z, x, y nat -} - -var sumNN = []argNN{ - {}, - {nat{1}, nil, nat{1}}, - {nat{1111111110}, nat{123456789}, nat{987654321}}, - {nat{0, 0, 0, 1}, nil, nat{0, 0, 0, 1}}, - {nat{0, 0, 0, 1111111110}, nat{0, 0, 0, 123456789}, nat{0, 0, 0, 987654321}}, - {nat{0, 0, 0, 1}, nat{0, 0, _M}, nat{0, 0, 1}}, -} - -var prodNN = []argNN{ - {}, - {nil, nil, nil}, - {nil, nat{991}, nil}, - {nat{991}, nat{991}, nat{1}}, - {nat{991 * 991}, nat{991}, nat{991}}, - {nat{0, 0, 991 * 991}, nat{0, 991}, nat{0, 991}}, - {nat{1 * 991, 2 * 991, 3 * 991, 4 * 991}, nat{1, 2, 3, 4}, nat{991}}, - {nat{4, 11, 20, 30, 20, 11, 4}, nat{1, 2, 3, 4}, nat{4, 3, 2, 1}}, -} - -func TestSet(t *testing.T) { - for _, a := range sumNN { - z := nat(nil).set(a.z) - if z.cmp(a.z) != 0 { - t.Errorf("got z = %v; want %v", z, a.z) - } - } -} - -func testFunNN(t *testing.T, msg string, f funNN, a argNN) { - z := f(nil, a.x, a.y) - if z.cmp(a.z) != 0 { - t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, z, a.z) - } -} - -func TestFunNN(t *testing.T) { - for _, a := range sumNN { - arg := a - testFunNN(t, "add", nat.add, arg) - - arg = argNN{a.z, a.y, a.x} - testFunNN(t, "add symmetric", nat.add, arg) - - arg = argNN{a.x, a.z, a.y} - testFunNN(t, "sub", nat.sub, arg) - - arg = argNN{a.y, a.z, a.x} - testFunNN(t, "sub symmetric", nat.sub, arg) - } - - for _, a := range prodNN { - arg := a - testFunNN(t, "mul", nat.mul, arg) - - arg = argNN{a.z, a.y, a.x} - testFunNN(t, "mul symmetric", nat.mul, arg) - } -} - -var mulRangesN = []struct { - a, b uint64 - prod string -}{ - {0, 0, "0"}, - {1, 1, "1"}, - {1, 2, "2"}, - {1, 3, "6"}, - {10, 10, "10"}, - {0, 100, "0"}, - {0, 1e9, "0"}, - {1, 0, "1"}, // empty range - {100, 1, "1"}, // empty range - {1, 10, "3628800"}, // 10! - {1, 20, "2432902008176640000"}, // 20! - {1, 100, - "933262154439441526816992388562667004907159682643816214685929" + - "638952175999932299156089414639761565182862536979208272237582" + - "51185210916864000000000000000000000000", // 100! - }, -} - -func TestMulRangeN(t *testing.T) { - for i, r := range mulRangesN { - prod := nat(nil).mulRange(r.a, r.b).decimalString() - if prod != r.prod { - t.Errorf("#%d: got %s; want %s", i, prod, r.prod) - } - } -} - -var mulArg, mulTmp nat - -func init() { - const n = 1000 - mulArg = make(nat, n) - for i := 0; i < n; i++ { - mulArg[i] = _M - } -} - -func benchmarkMulLoad() { - for j := 1; j <= 10; j++ { - x := mulArg[0 : j*100] - mulTmp.mul(x, x) - } -} - -func BenchmarkMul(b *testing.B) { - for i := 0; i < b.N; i++ { - benchmarkMulLoad() - } -} - -func toString(x nat, charset string) string { - base := len(charset) - - // special cases - switch { - case base < 2: - panic("illegal base") - case len(x) == 0: - return string(charset[0]) - } - - // allocate buffer for conversion - i := x.bitLen()/log2(Word(base)) + 1 // +1: round up - s := make([]byte, i) - - // don't destroy x - q := nat(nil).set(x) - - // convert - for len(q) > 0 { - i-- - var r Word - q, r = q.divW(q, Word(base)) - s[i] = charset[r] - } - - return string(s[i:]) -} - -var strTests = []struct { - x nat // nat value to be converted - c string // conversion charset - s string // expected result -}{ - {nil, "01", "0"}, - {nat{1}, "01", "1"}, - {nat{0xc5}, "01", "11000101"}, - {nat{03271}, lowercaseDigits[0:8], "3271"}, - {nat{10}, lowercaseDigits[0:10], "10"}, - {nat{1234567890}, uppercaseDigits[0:10], "1234567890"}, - {nat{0xdeadbeef}, lowercaseDigits[0:16], "deadbeef"}, - {nat{0xdeadbeef}, uppercaseDigits[0:16], "DEADBEEF"}, - {nat{0x229be7}, lowercaseDigits[0:17], "1a2b3c"}, - {nat{0x309663e6}, uppercaseDigits[0:32], "O9COV6"}, -} - -func TestString(t *testing.T) { - for _, a := range strTests { - s := a.x.string(a.c) - if s != a.s { - t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s) - } - - x, b, err := nat(nil).scan(strings.NewReader(a.s), len(a.c)) - if x.cmp(a.x) != 0 { - t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) - } - if b != len(a.c) { - t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, len(a.c)) - } - if err != nil { - t.Errorf("scan%+v\n\tgot error = %s", a, err) - } - } -} - -var natScanTests = []struct { - s string // string to be scanned - base int // input base - x nat // expected nat - b int // expected base - ok bool // expected success - next int // next character (or 0, if at EOF) -}{ - // error: illegal base - {base: -1}, - {base: 1}, - {base: 37}, - - // error: no mantissa - {}, - {s: "?"}, - {base: 10}, - {base: 36}, - {s: "?", base: 10}, - {s: "0x"}, - {s: "345", base: 2}, - - // no errors - {"0", 0, nil, 10, true, 0}, - {"0", 10, nil, 10, true, 0}, - {"0", 36, nil, 36, true, 0}, - {"1", 0, nat{1}, 10, true, 0}, - {"1", 10, nat{1}, 10, true, 0}, - {"0 ", 0, nil, 10, true, ' '}, - {"08", 0, nil, 10, true, '8'}, - {"018", 0, nat{1}, 8, true, '8'}, - {"0b1", 0, nat{1}, 2, true, 0}, - {"0b11000101", 0, nat{0xc5}, 2, true, 0}, - {"03271", 0, nat{03271}, 8, true, 0}, - {"10ab", 0, nat{10}, 10, true, 'a'}, - {"1234567890", 0, nat{1234567890}, 10, true, 0}, - {"xyz", 36, nat{(33*36+34)*36 + 35}, 36, true, 0}, - {"xyz?", 36, nat{(33*36+34)*36 + 35}, 36, true, '?'}, - {"0x", 16, nil, 16, true, 'x'}, - {"0xdeadbeef", 0, nat{0xdeadbeef}, 16, true, 0}, - {"0XDEADBEEF", 0, nat{0xdeadbeef}, 16, true, 0}, -} - -func TestScanBase(t *testing.T) { - for _, a := range natScanTests { - r := strings.NewReader(a.s) - x, b, err := nat(nil).scan(r, a.base) - if err == nil && !a.ok { - t.Errorf("scan%+v\n\texpected error", a) - } - if err != nil { - if a.ok { - t.Errorf("scan%+v\n\tgot error = %s", a, err) - } - continue - } - if x.cmp(a.x) != 0 { - t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) - } - if b != a.b { - t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.base) - } - next, _, err := r.ReadRune() - if err == os.EOF { - next = 0 - err = nil - } - if err == nil && next != a.next { - t.Errorf("scan%+v\n\tgot next = %q; want %q", a, next, a.next) - } - } -} - -var pi = "3" + - "14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651" + - "32823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461" + - "28475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920" + - "96282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179" + - "31051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798" + - "60943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901" + - "22495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837" + - "29780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083" + - "81420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909" + - "21642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151" + - "55748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035" + - "63707660104710181942955596198946767837449448255379774726847104047534646208046684259069491293313677028989152104" + - "75216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992" + - "45863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818" + - "34797753566369807426542527862551818417574672890977772793800081647060016145249192173217214772350141441973568548" + - "16136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179" + - "04946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886" + - "26945604241965285022210661186306744278622039194945047123713786960956364371917287467764657573962413890865832645" + - "99581339047802759009946576407895126946839835259570982582262052248940772671947826848260147699090264013639443745" + - "53050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382" + - "68683868942774155991855925245953959431049972524680845987273644695848653836736222626099124608051243884390451244" + - "13654976278079771569143599770012961608944169486855584840635342207222582848864815845602850601684273945226746767" + - "88952521385225499546667278239864565961163548862305774564980355936345681743241125150760694794510965960940252288" + - "79710893145669136867228748940560101503308617928680920874760917824938589009714909675985261365549781893129784821" + - "68299894872265880485756401427047755513237964145152374623436454285844479526586782105114135473573952311342716610" + - "21359695362314429524849371871101457654035902799344037420073105785390621983874478084784896833214457138687519435" + - "06430218453191048481005370614680674919278191197939952061419663428754440643745123718192179998391015919561814675" + - "14269123974894090718649423196156794520809514655022523160388193014209376213785595663893778708303906979207734672" + - "21825625996615014215030680384477345492026054146659252014974428507325186660021324340881907104863317346496514539" + - "05796268561005508106658796998163574736384052571459102897064140110971206280439039759515677157700420337869936007" + - "23055876317635942187312514712053292819182618612586732157919841484882916447060957527069572209175671167229109816" + - "90915280173506712748583222871835209353965725121083579151369882091444210067510334671103141267111369908658516398" + - "31501970165151168517143765761835155650884909989859982387345528331635507647918535893226185489632132933089857064" + - "20467525907091548141654985946163718027098199430992448895757128289059232332609729971208443357326548938239119325" + - "97463667305836041428138830320382490375898524374417029132765618093773444030707469211201913020330380197621101100" + - "44929321516084244485963766983895228684783123552658213144957685726243344189303968642624341077322697802807318915" + - "44110104468232527162010526522721116603966655730925471105578537634668206531098965269186205647693125705863566201" + - "85581007293606598764861179104533488503461136576867532494416680396265797877185560845529654126654085306143444318" + - "58676975145661406800700237877659134401712749470420562230538994561314071127000407854733269939081454664645880797" + - "27082668306343285878569830523580893306575740679545716377525420211495576158140025012622859413021647155097925923" + - "09907965473761255176567513575178296664547791745011299614890304639947132962107340437518957359614589019389713111" + - "79042978285647503203198691514028708085990480109412147221317947647772622414254854540332157185306142288137585043" + - "06332175182979866223717215916077166925474873898665494945011465406284336639379003976926567214638530673609657120" + - "91807638327166416274888800786925602902284721040317211860820419000422966171196377921337575114959501566049631862" + - "94726547364252308177036751590673502350728354056704038674351362222477158915049530984448933309634087807693259939" + - "78054193414473774418426312986080998886874132604721569516239658645730216315981931951673538129741677294786724229" + - "24654366800980676928238280689964004824354037014163149658979409243237896907069779422362508221688957383798623001" + - "59377647165122893578601588161755782973523344604281512627203734314653197777416031990665541876397929334419521541" + - "34189948544473456738316249934191318148092777710386387734317720754565453220777092120190516609628049092636019759" + - "88281613323166636528619326686336062735676303544776280350450777235547105859548702790814356240145171806246436267" + - "94561275318134078330336254232783944975382437205835311477119926063813346776879695970309833913077109870408591337" - -// Test case for BenchmarkScanPi. -func TestScanPi(t *testing.T) { - var x nat - z, _, err := x.scan(strings.NewReader(pi), 10) - if err != nil { - t.Errorf("scanning pi: %s", err) - } - if s := z.decimalString(); s != pi { - t.Errorf("scanning pi: got %s", s) - } -} - -func BenchmarkScanPi(b *testing.B) { - for i := 0; i < b.N; i++ { - var x nat - x.scan(strings.NewReader(pi), 10) - } -} - -const ( - // 314**271 - // base 2: 2249 digits - // base 8: 751 digits - // base 10: 678 digits - // base 16: 563 digits - shortBase = 314 - shortExponent = 271 - - // 3141**2178 - // base 2: 31577 digits - // base 8: 10527 digits - // base 10: 9507 digits - // base 16: 7895 digits - mediumBase = 3141 - mediumExponent = 2718 - - // 3141**2178 - // base 2: 406078 digits - // base 8: 135360 digits - // base 10: 122243 digits - // base 16: 101521 digits - longBase = 31415 - longExponent = 27182 -) - -func BenchmarkScanShort2(b *testing.B) { - ScanHelper(b, 2, shortBase, shortExponent) -} - -func BenchmarkScanShort8(b *testing.B) { - ScanHelper(b, 8, shortBase, shortExponent) -} - -func BenchmarkScanSort10(b *testing.B) { - ScanHelper(b, 10, shortBase, shortExponent) -} - -func BenchmarkScanShort16(b *testing.B) { - ScanHelper(b, 16, shortBase, shortExponent) -} - -func BenchmarkScanMedium2(b *testing.B) { - ScanHelper(b, 2, mediumBase, mediumExponent) -} - -func BenchmarkScanMedium8(b *testing.B) { - ScanHelper(b, 8, mediumBase, mediumExponent) -} - -func BenchmarkScanMedium10(b *testing.B) { - ScanHelper(b, 10, mediumBase, mediumExponent) -} - -func BenchmarkScanMedium16(b *testing.B) { - ScanHelper(b, 16, mediumBase, mediumExponent) -} - -func BenchmarkScanLong2(b *testing.B) { - ScanHelper(b, 2, longBase, longExponent) -} - -func BenchmarkScanLong8(b *testing.B) { - ScanHelper(b, 8, longBase, longExponent) -} - -func BenchmarkScanLong10(b *testing.B) { - ScanHelper(b, 10, longBase, longExponent) -} - -func BenchmarkScanLong16(b *testing.B) { - ScanHelper(b, 16, longBase, longExponent) -} - -func ScanHelper(b *testing.B, base int, xv, yv Word) { - b.StopTimer() - var x, y, z nat - x = x.setWord(xv) - y = y.setWord(yv) - z = z.expNN(x, y, nil) - - var s string - s = z.string(lowercaseDigits[0:base]) - if t := toString(z, lowercaseDigits[0:base]); t != s { - panic(fmt.Sprintf("scanning: got %s; want %s", s, t)) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - x.scan(strings.NewReader(s), base) - } -} - -func BenchmarkStringShort2(b *testing.B) { - StringHelper(b, 2, shortBase, shortExponent) -} - -func BenchmarkStringShort8(b *testing.B) { - StringHelper(b, 8, shortBase, shortExponent) -} - -func BenchmarkStringShort10(b *testing.B) { - StringHelper(b, 10, shortBase, shortExponent) -} - -func BenchmarkStringShort16(b *testing.B) { - StringHelper(b, 16, shortBase, shortExponent) -} - -func BenchmarkStringMedium2(b *testing.B) { - StringHelper(b, 2, mediumBase, mediumExponent) -} - -func BenchmarkStringMedium8(b *testing.B) { - StringHelper(b, 8, mediumBase, mediumExponent) -} - -func BenchmarkStringMedium10(b *testing.B) { - StringHelper(b, 10, mediumBase, mediumExponent) -} - -func BenchmarkStringMedium16(b *testing.B) { - StringHelper(b, 16, mediumBase, mediumExponent) -} - -func BenchmarkStringLong2(b *testing.B) { - StringHelper(b, 2, longBase, longExponent) -} - -func BenchmarkStringLong8(b *testing.B) { - StringHelper(b, 8, longBase, longExponent) -} - -func BenchmarkStringLong10(b *testing.B) { - StringHelper(b, 10, longBase, longExponent) -} - -func BenchmarkStringLong16(b *testing.B) { - StringHelper(b, 16, longBase, longExponent) -} - -func StringHelper(b *testing.B, base int, xv, yv Word) { - b.StopTimer() - var x, y, z nat - x = x.setWord(xv) - y = y.setWord(yv) - z = z.expNN(x, y, nil) - b.StartTimer() - - for i := 0; i < b.N; i++ { - z.string(lowercaseDigits[0:base]) - } -} - -func TestLeadingZeros(t *testing.T) { - var x Word = _B >> 1 - for i := 0; i <= _W; i++ { - if int(leadingZeros(x)) != i { - t.Errorf("failed at %x: got %d want %d", x, leadingZeros(x), i) - } - x >>= 1 - } -} - -type shiftTest struct { - in nat - shift uint - out nat -} - -var leftShiftTests = []shiftTest{ - {nil, 0, nil}, - {nil, 1, nil}, - {natOne, 0, natOne}, - {natOne, 1, natTwo}, - {nat{1 << (_W - 1)}, 1, nat{0}}, - {nat{1 << (_W - 1), 0}, 1, nat{0, 1}}, -} - -func TestShiftLeft(t *testing.T) { - for i, test := range leftShiftTests { - var z nat - z = z.shl(test.in, test.shift) - for j, d := range test.out { - if j >= len(z) || z[j] != d { - t.Errorf("#%d: got: %v want: %v", i, z, test.out) - break - } - } - } -} - -var rightShiftTests = []shiftTest{ - {nil, 0, nil}, - {nil, 1, nil}, - {natOne, 0, natOne}, - {natOne, 1, nil}, - {natTwo, 1, natOne}, - {nat{0, 1}, 1, nat{1 << (_W - 1)}}, - {nat{2, 1, 1}, 1, nat{1<<(_W-1) + 1, 1 << (_W - 1)}}, -} - -func TestShiftRight(t *testing.T) { - for i, test := range rightShiftTests { - var z nat - z = z.shr(test.in, test.shift) - for j, d := range test.out { - if j >= len(z) || z[j] != d { - t.Errorf("#%d: got: %v want: %v", i, z, test.out) - break - } - } - } -} - -type modWTest struct { - in string - dividend string - out string -} - -var modWTests32 = []modWTest{ - {"23492635982634928349238759823742", "252341", "220170"}, -} - -var modWTests64 = []modWTest{ - {"6527895462947293856291561095690465243862946", "524326975699234", "375066989628668"}, -} - -func runModWTests(t *testing.T, tests []modWTest) { - for i, test := range tests { - in, _ := new(Int).SetString(test.in, 10) - d, _ := new(Int).SetString(test.dividend, 10) - out, _ := new(Int).SetString(test.out, 10) - - r := in.abs.modW(d.abs[0]) - if r != out.abs[0] { - t.Errorf("#%d failed: got %s want %s", i, r, out) - } - } -} - -func TestModW(t *testing.T) { - if _W >= 32 { - runModWTests(t, modWTests32) - } - if _W >= 64 { - runModWTests(t, modWTests64) - } -} - -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 <<= 1 - } -} - -var expNNTests = []struct { - x, y, m string - out string -}{ - {"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"}, - {"0x8000000000000000", "2", "6719", "4944"}, - {"0x8000000000000000", "3", "6719", "5447"}, - {"0x8000000000000000", "1000", "6719", "1603"}, - {"0x8000000000000000", "1000000", "6719", "3199"}, - { - "2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347", - "298472983472983471903246121093472394872319615612417471234712061", - "29834729834729834729347290846729561262544958723956495615629569234729836259263598127342374289365912465901365498236492183464", - "23537740700184054162508175125554701713153216681790245129157191391322321508055833908509185839069455749219131480588829346291", - }, -} - -func TestExpNN(t *testing.T) { - for i, test := range expNNTests { - x, _, _ := nat(nil).scan(strings.NewReader(test.x), 0) - y, _, _ := nat(nil).scan(strings.NewReader(test.y), 0) - out, _, _ := nat(nil).scan(strings.NewReader(test.out), 0) - - var m nat - - if len(test.m) > 0 { - m, _, _ = nat(nil).scan(strings.NewReader(test.m), 0) - } - - z := nat(nil).expNN(x, y, m) - if z.cmp(out) != 0 { - t.Errorf("#%d got %v want %v", i, z, out) - } - } -} diff --git a/src/pkg/big/rat.go b/src/pkg/big/rat.go deleted file mode 100644 index 327b9bd9c..000000000 --- a/src/pkg/big/rat.go +++ /dev/null @@ -1,371 +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 file implements multi-precision rational numbers. - -package big - -import ( - "encoding/binary" - "fmt" - "os" - "strings" -) - -// A Rat represents a quotient a/b of arbitrary precision. The zero value for -// a Rat, 0/0, is not a legal Rat. -type Rat struct { - a Int - b nat -} - -// NewRat creates a new Rat with numerator a and denominator b. -func NewRat(a, b int64) *Rat { - return new(Rat).SetFrac64(a, b) -} - -// SetFrac sets z to a/b and returns z. -func (z *Rat) SetFrac(a, b *Int) *Rat { - z.a.Set(a) - z.a.neg = a.neg != b.neg - z.b = z.b.set(b.abs) - return z.norm() -} - -// SetFrac64 sets z to a/b and returns z. -func (z *Rat) SetFrac64(a, b int64) *Rat { - z.a.SetInt64(a) - if b < 0 { - b = -b - z.a.neg = !z.a.neg - } - z.b = z.b.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.setWord(1) - 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.setWord(1) - return z -} - -// Sign returns: -// -// -1 if x < 0 -// 0 if x == 0 -// +1 if x > 0 -// -func (x *Rat) Sign() int { - return x.a.Sign() -} - -// IsInt returns true if the denominator of x is 1. -func (x *Rat) IsInt() bool { - return len(x.b) == 1 && x.b[0] == 1 -} - -// Num returns the numerator of z; it may be <= 0. -// The result is a reference to z's numerator; it -// may change if a new value is assigned to z. -func (z *Rat) Num() *Int { - return &z.a -} - -// Denom returns the denominator of z; it is always > 0. -// The result is a reference to z's denominator; it -// may change if a new value is assigned to z. -func (z *Rat) Denom() *Int { - return &Int{false, z.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 -} - -func (z *Rat) norm() *Rat { - f := gcd(z.a.abs, z.b) - if len(z.a.abs) == 0 { - // z == 0 - z.a.neg = false // normalize sign - z.b = z.b.setWord(1) - return z - } - if 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) - } - return z -} - -func mulNat(x *Int, y nat) *Int { - var z Int - z.abs = z.abs.mul(x.abs, y) - z.neg = len(z.abs) > 0 && x.neg - return &z -} - -// Cmp compares x and y and returns: -// -// -1 if x < y -// 0 if x == y -// +1 if x > y -// -func (x *Rat) Cmp(y *Rat) (r int) { - return mulNat(&x.a, y.b).Cmp(mulNat(&y.a, x.b)) -} - -// Abs sets z to |x| (the absolute value of x) and returns z. -func (z *Rat) Abs(x *Rat) *Rat { - z.a.Abs(&x.a) - z.b = z.b.set(x.b) - return z -} - -// Add sets z to the sum x+y and returns z. -func (z *Rat) Add(x, y *Rat) *Rat { - a1 := mulNat(&x.a, y.b) - a2 := mulNat(&y.a, x.b) - z.a.Add(a1, a2) - z.b = z.b.mul(x.b, y.b) - return z.norm() -} - -// Sub sets z to the difference x-y and returns z. -func (z *Rat) Sub(x, y *Rat) *Rat { - a1 := mulNat(&x.a, y.b) - a2 := mulNat(&y.a, x.b) - z.a.Sub(a1, a2) - z.b = z.b.mul(x.b, y.b) - 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 = z.b.mul(x.b, y.b) - return z.norm() -} - -// Quo sets z to the quotient x/y and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -func (z *Rat) Quo(x, y *Rat) *Rat { - if len(y.a.abs) == 0 { - panic("division by zero") - } - a := mulNat(&x.a, y.b) - b := mulNat(&y.a, x.b) - z.a.abs = a.abs - z.b = b.abs - z.a.neg = a.neg != b.neg - return z.norm() -} - -// Neg sets z to -x (by making a copy of x if necessary) and returns z. -func (z *Rat) Neg(x *Rat) *Rat { - z.a.Neg(&x.a) - z.b = z.b.set(x.b) - return z -} - -// Set sets z to x (by making a copy of x if necessary) and returns z. -func (z *Rat) Set(x *Rat) *Rat { - z.a.Set(&x.a) - z.b = z.b.set(x.b) - return z -} - -func ratTok(ch int) bool { - return strings.IndexRune("+-/0123456789.eE", ch) >= 0 -} - -// Scan is a support routine for fmt.Scanner. It accepts the formats -// 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent. -func (z *Rat) Scan(s fmt.ScanState, ch int) os.Error { - tok, err := s.Token(true, ratTok) - if err != nil { - return err - } - if strings.IndexRune("efgEFGv", ch) < 0 { - return os.NewError("Rat.Scan: invalid verb") - } - if _, ok := z.SetString(string(tok)); !ok { - return os.NewError("Rat.Scan: invalid syntax") - } - return nil -} - -// SetString sets z to the value of s and returns z and a boolean indicating -// success. s can be given as a fraction "a/b" or as a floating-point number -// optionally followed by an exponent. If the operation failed, the value of z -// is undefined. -func (z *Rat) SetString(s string) (*Rat, bool) { - if len(s) == 0 { - return z, false - } - - // check for a quotient - sep := strings.Index(s, "/") - if sep >= 0 { - if _, ok := z.a.SetString(s[0:sep], 10); !ok { - return z, false - } - s = s[sep+1:] - var err os.Error - if z.b, _, err = z.b.scan(strings.NewReader(s), 10); err != nil { - return z, false - } - return z.norm(), true - } - - // check for a decimal point - sep = strings.Index(s, ".") - // check for an exponent - e := strings.IndexAny(s, "eE") - var exp Int - if e >= 0 { - if e < sep { - // The E must come after the decimal point. - return z, false - } - if _, ok := exp.SetString(s[e+1:], 10); !ok { - return z, false - } - s = s[0:e] - } - if sep >= 0 { - s = s[0:sep] + s[sep+1:] - exp.Sub(&exp, NewInt(int64(len(s)-sep))) - } - - if _, ok := z.a.SetString(s, 10); !ok { - return z, false - } - powTen := nat{}.expNN(natTen, exp.abs, nil) - if exp.neg { - z.b = powTen - z.norm() - } else { - z.a.abs = z.a.abs.mul(z.a.abs, powTen) - z.b = z.b.setWord(1) - } - - return z, true -} - -// String returns a string representation of z in the form "a/b" (even if b == 1). -func (z *Rat) String() string { - return z.a.String() + "/" + z.b.decimalString() -} - -// RatString returns a string representation of z in the form "a/b" if b != 1, -// and in the form "a" if b == 1. -func (z *Rat) RatString() string { - if z.IsInt() { - return z.a.String() - } - return z.String() -} - -// FloatString returns a string representation of z in decimal form with prec -// digits of precision after the decimal point and the last digit rounded. -func (z *Rat) FloatString(prec int) string { - if z.IsInt() { - s := z.a.String() - if prec > 0 { - s += "." + strings.Repeat("0", prec) - } - return s - } - - q, r := nat{}.div(nat{}, z.a.abs, z.b) - - p := natOne - if prec > 0 { - p = nat{}.expNN(natTen, nat{}.setUint64(uint64(prec)), nil) - } - - r = r.mul(r, p) - r, r2 := r.div(nat{}, r, z.b) - - // see if we need to round up - r2 = r2.add(r2, r2) - if z.b.cmp(r2) <= 0 { - r = r.add(r, natOne) - if r.cmp(p) >= 0 { - q = nat{}.add(q, natOne) - r = nat{}.sub(r, p) - } - } - - s := q.decimalString() - if z.a.neg { - s = "-" + s - } - - if prec > 0 { - rs := r.decimalString() - leadingZeros := prec - len(rs) - s += "." + strings.Repeat("0", leadingZeros) + rs - } - - return s -} - -// Gob codec version. Permits backward-compatible changes to the encoding. -const ratGobVersion byte = 1 - -// GobEncode implements the gob.GobEncoder interface. -func (z *Rat) GobEncode() ([]byte, os.Error) { - buf := make([]byte, 1+4+(len(z.a.abs)+len(z.b))*_S) // extra bytes for version and sign bit (1), and numerator length (4) - i := z.b.bytes(buf) - j := z.a.abs.bytes(buf[0:i]) - n := i - j - if int(uint32(n)) != n { - // this should never happen - return nil, os.NewError("Rat.GobEncode: numerator too large") - } - binary.BigEndian.PutUint32(buf[j-4:j], uint32(n)) - j -= 1 + 4 - b := ratGobVersion << 1 // make space for sign bit - if z.a.neg { - b |= 1 - } - buf[j] = b - return buf[j:], nil -} - -// GobDecode implements the gob.GobDecoder interface. -func (z *Rat) GobDecode(buf []byte) os.Error { - if len(buf) == 0 { - return os.NewError("Rat.GobDecode: no data") - } - b := buf[0] - if b>>1 != ratGobVersion { - return os.NewError(fmt.Sprintf("Rat.GobDecode: encoding version %d not supported", b>>1)) - } - const j = 1 + 4 - 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:]) - return nil -} diff --git a/src/pkg/big/rat_test.go b/src/pkg/big/rat_test.go deleted file mode 100644 index dbc5bb6cc..000000000 --- a/src/pkg/big/rat_test.go +++ /dev/null @@ -1,332 +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. - -package big - -import ( - "bytes" - "fmt" - "gob" - "testing" -) - -var setStringTests = []struct { - in, out string - ok bool -}{ - {"0", "0", true}, - {"-0", "0", true}, - {"1", "1", true}, - {"-1", "-1", true}, - {"1.", "1", true}, - {"1e0", "1", true}, - {"1.e1", "10", true}, - {in: "1e", ok: false}, - {in: "1.e", ok: false}, - {in: "1e+14e-5", ok: false}, - {in: "1e4.5", ok: false}, - {in: "r", ok: false}, - {in: "a/b", ok: false}, - {in: "a.b", ok: false}, - {"-0.1", "-1/10", true}, - {"-.1", "-1/10", true}, - {"2/4", "1/2", true}, - {".25", "1/4", true}, - {"-1/5", "-1/5", true}, - {"8129567.7690E14", "812956776900000000000", true}, - {"78189e+4", "781890000", true}, - {"553019.8935e+8", "55301989350000", true}, - {"98765432109876543210987654321e-10", "98765432109876543210987654321/10000000000", true}, - {"9877861857500000E-7", "3951144743/4", true}, - {"2169378.417e-3", "2169378417/1000000", true}, - {"884243222337379604041632732738665534", "884243222337379604041632732738665534", true}, - {"53/70893980658822810696", "53/70893980658822810696", true}, - {"106/141787961317645621392", "53/70893980658822810696", true}, - {"204211327800791583.81095", "4084226556015831676219/20000", true}, -} - -func TestRatSetString(t *testing.T) { - for i, test := range setStringTests { - x, ok := new(Rat).SetString(test.in) - - if ok != test.ok || ok && x.RatString() != test.out { - t.Errorf("#%d got %s want %s", i, x.RatString(), test.out) - } - } -} - -func TestRatScan(t *testing.T) { - var buf bytes.Buffer - for i, test := range setStringTests { - x := new(Rat) - buf.Reset() - buf.WriteString(test.in) - - _, err := fmt.Fscanf(&buf, "%v", x) - if err == nil != test.ok { - if test.ok { - t.Errorf("#%d error: %s", i, err.String()) - } else { - t.Errorf("#%d expected error", i) - } - continue - } - if err == nil && x.RatString() != test.out { - t.Errorf("#%d got %s want %s", i, x.RatString(), test.out) - } - } -} - -var floatStringTests = []struct { - in string - prec int - out string -}{ - {"0", 0, "0"}, - {"0", 4, "0.0000"}, - {"1", 0, "1"}, - {"1", 2, "1.00"}, - {"-1", 0, "-1"}, - {".25", 2, "0.25"}, - {".25", 1, "0.3"}, - {".25", 3, "0.250"}, - {"-1/3", 3, "-0.333"}, - {"-2/3", 4, "-0.6667"}, - {"0.96", 1, "1.0"}, - {"0.999", 2, "1.00"}, - {"0.9", 0, "1"}, - {".25", -1, "0"}, - {".55", -1, "1"}, -} - -func TestFloatString(t *testing.T) { - for i, test := range floatStringTests { - x, _ := new(Rat).SetString(test.in) - - if x.FloatString(test.prec) != test.out { - t.Errorf("#%d got %s want %s", i, x.FloatString(test.prec), test.out) - } - } -} - -func TestRatSign(t *testing.T) { - zero := NewRat(0, 1) - for _, a := range setStringTests { - var x Rat - x.SetString(a.in) - s := x.Sign() - e := x.Cmp(zero) - if s != e { - t.Errorf("got %d; want %d for z = %v", s, e, &x) - } - } -} - -var ratCmpTests = []struct { - rat1, rat2 string - out int -}{ - {"0", "0/1", 0}, - {"1/1", "1", 0}, - {"-1", "-2/2", 0}, - {"1", "0", 1}, - {"0/1", "1/1", -1}, - {"-5/1434770811533343057144", "-5/1434770811533343057145", -1}, - {"49832350382626108453/8964749413", "49832350382626108454/8964749413", -1}, - {"-37414950961700930/7204075375675961", "37414950961700930/7204075375675961", -1}, - {"37414950961700930/7204075375675961", "74829901923401860/14408150751351922", 0}, -} - -func TestRatCmp(t *testing.T) { - for i, test := range ratCmpTests { - x, _ := new(Rat).SetString(test.rat1) - y, _ := new(Rat).SetString(test.rat2) - - out := x.Cmp(y) - if out != test.out { - t.Errorf("#%d got out = %v; want %v", i, out, test.out) - } - } -} - -func TestIsInt(t *testing.T) { - one := NewInt(1) - for _, a := range setStringTests { - var x Rat - x.SetString(a.in) - i := x.IsInt() - e := x.Denom().Cmp(one) == 0 - if i != e { - t.Errorf("got %v; want %v for z = %v", i, e, &x) - } - } -} - -func TestRatAbs(t *testing.T) { - zero := NewRat(0, 1) - for _, a := range setStringTests { - var z Rat - z.SetString(a.in) - var e Rat - e.Set(&z) - if e.Cmp(zero) < 0 { - e.Sub(zero, &e) - } - z.Abs(&z) - if z.Cmp(&e) != 0 { - t.Errorf("got z = %v; want %v", &z, &e) - } - } -} - -type ratBinFun func(z, x, y *Rat) *Rat -type ratBinArg struct { - x, y, z string -} - -func testRatBin(t *testing.T, i int, name string, f ratBinFun, a ratBinArg) { - x, _ := NewRat(0, 1).SetString(a.x) - y, _ := NewRat(0, 1).SetString(a.y) - z, _ := NewRat(0, 1).SetString(a.z) - out := f(NewRat(0, 1), x, y) - - if out.Cmp(z) != 0 { - t.Errorf("%s #%d got %s want %s", name, i, out, z) - } -} - -var ratBinTests = []struct { - x, y string - sum, prod string -}{ - {"0", "0", "0", "0"}, - {"0", "1", "1", "0"}, - {"-1", "0", "-1", "0"}, - {"-1", "1", "0", "-1"}, - {"1", "1", "2", "1"}, - {"1/2", "1/2", "1", "1/4"}, - {"1/4", "1/3", "7/12", "1/12"}, - {"2/5", "-14/3", "-64/15", "-28/15"}, - {"4707/49292519774798173060", "-3367/70976135186689855734", "84058377121001851123459/1749296273614329067191168098769082663020", "-1760941/388732505247628681598037355282018369560"}, - {"-61204110018146728334/3", "-31052192278051565633/2", "-215564796870448153567/6", "950260896245257153059642991192710872711/3"}, - {"-854857841473707320655/4237645934602118692642972629634714039", "-18/31750379913563777419", "-27/133467566250814981", "15387441146526731771790/134546868362786310073779084329032722548987800600710485341"}, - {"618575745270541348005638912139/19198433543745179392300736", "-19948846211000086/637313996471", "27674141753240653/30123979153216", "-6169936206128396568797607742807090270137721977/6117715203873571641674006593837351328"}, - {"-3/26206484091896184128", "5/2848423294177090248", "15310893822118706237/9330894968229805033368778458685147968", "-5/24882386581946146755650075889827061248"}, - {"26946729/330400702820", "41563965/225583428284", "1238218672302860271/4658307703098666660055", "224002580204097/14906584649915733312176"}, - {"-8259900599013409474/7", "-84829337473700364773/56707961321161574960", "-468402123685491748914621885145127724451/396955729248131024720", "350340947706464153265156004876107029701/198477864624065512360"}, - {"575775209696864/1320203974639986246357", "29/712593081308", "410331716733912717985762465/940768218243776489278275419794956", "808/45524274987585732633"}, - {"1786597389946320496771/2066653520653241", "6269770/1992362624741777", "3559549865190272133656109052308126637/4117523232840525481453983149257", "8967230/3296219033"}, - {"-36459180403360509753/32150500941194292113930", "9381566963714/9633539", "301622077145533298008420642898530153/309723104686531919656937098270", "-3784609207827/3426986245"}, -} - -func TestRatBin(t *testing.T) { - for i, test := range ratBinTests { - arg := ratBinArg{test.x, test.y, test.sum} - testRatBin(t, i, "Add", (*Rat).Add, arg) - - arg = ratBinArg{test.y, test.x, test.sum} - testRatBin(t, i, "Add symmetric", (*Rat).Add, arg) - - arg = ratBinArg{test.sum, test.x, test.y} - testRatBin(t, i, "Sub", (*Rat).Sub, arg) - - arg = ratBinArg{test.sum, test.y, test.x} - testRatBin(t, i, "Sub symmetric", (*Rat).Sub, arg) - - arg = ratBinArg{test.x, test.y, test.prod} - testRatBin(t, i, "Mul", (*Rat).Mul, arg) - - arg = ratBinArg{test.y, test.x, test.prod} - testRatBin(t, i, "Mul symmetric", (*Rat).Mul, arg) - - if test.x != "0" { - arg = ratBinArg{test.prod, test.x, test.y} - testRatBin(t, i, "Quo", (*Rat).Quo, arg) - } - - if test.y != "0" { - arg = ratBinArg{test.prod, test.y, test.x} - testRatBin(t, i, "Quo symmetric", (*Rat).Quo, arg) - } - } -} - -func TestIssue820(t *testing.T) { - x := NewRat(3, 1) - y := NewRat(2, 1) - z := y.Quo(x, y) - q := NewRat(3, 2) - if z.Cmp(q) != 0 { - t.Errorf("got %s want %s", z, q) - } - - y = NewRat(3, 1) - x = NewRat(2, 1) - z = y.Quo(x, y) - q = NewRat(2, 3) - if z.Cmp(q) != 0 { - t.Errorf("got %s want %s", z, q) - } - - x = NewRat(3, 1) - z = x.Quo(x, x) - q = NewRat(3, 3) - if z.Cmp(q) != 0 { - t.Errorf("got %s want %s", z, q) - } -} - -var setFrac64Tests = []struct { - a, b int64 - out string -}{ - {0, 1, "0"}, - {0, -1, "0"}, - {1, 1, "1"}, - {-1, 1, "-1"}, - {1, -1, "-1"}, - {-1, -1, "1"}, - {-9223372036854775808, -9223372036854775808, "1"}, -} - -func TestRatSetFrac64Rat(t *testing.T) { - for i, test := range setFrac64Tests { - x := new(Rat).SetFrac64(test.a, test.b) - if x.RatString() != test.out { - t.Errorf("#%d got %s want %s", i, x.RatString(), test.out) - } - } -} - -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) - } - } - } -} diff --git a/src/pkg/bufio/Makefile b/src/pkg/bufio/Makefile deleted file mode 100644 index 85430e8e8..000000000 --- a/src/pkg/bufio/Makefile +++ /dev/null @@ -1,11 +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 ../../Make.inc - -TARG=bufio -GOFILES=\ - bufio.go\ - -include ../../Make.pkg diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go index 727ebfdbb..b44d0e7d1 100644 --- a/src/pkg/bufio/bufio.go +++ b/src/pkg/bufio/bufio.go @@ -9,38 +9,22 @@ package bufio import ( "bytes" + "errors" "io" - "os" - "strconv" - "utf8" + "unicode/utf8" ) const ( defaultBufSize = 4096 ) -// Errors introduced by this package. -type Error struct { - ErrorString string -} - -func (err *Error) String() string { return err.ErrorString } - var ( - ErrInvalidUnreadByte os.Error = &Error{"bufio: invalid use of UnreadByte"} - ErrInvalidUnreadRune os.Error = &Error{"bufio: invalid use of UnreadRune"} - ErrBufferFull os.Error = &Error{"bufio: buffer full"} - ErrNegativeCount os.Error = &Error{"bufio: negative count"} - errInternal os.Error = &Error{"bufio: internal error"} + ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte") + ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune") + ErrBufferFull = errors.New("bufio: buffer full") + ErrNegativeCount = errors.New("bufio: negative count") ) -// BufSizeError is the error representing an invalid buffer size. -type BufSizeError int - -func (b BufSizeError) String() string { - return "bufio: bad buffer size " + strconv.Itoa(int(b)) -} - // Buffered input. // Reader implements buffering for an io.Reader object. @@ -48,40 +32,36 @@ type Reader struct { buf []byte rd io.Reader r, w int - err os.Error + err error lastByte int lastRuneSize int } -// NewReaderSize creates a new Reader whose buffer has the specified size, -// which must be greater than zero. If the argument io.Reader is already a -// Reader with large enough size, it returns the underlying Reader. -// It returns the Reader and any error. -func NewReaderSize(rd io.Reader, size int) (*Reader, os.Error) { - if size <= 0 { - return nil, BufSizeError(size) - } +const minReadBufferSize = 16 + +// NewReaderSize returns a new Reader whose buffer has at least the specified +// size. If the argument io.Reader is already a Reader with large enough +// size, it returns the underlying Reader. +func NewReaderSize(rd io.Reader, size int) *Reader { // Is it already a Reader? b, ok := rd.(*Reader) if ok && len(b.buf) >= size { - return b, nil + return b + } + if size < minReadBufferSize { + size = minReadBufferSize + } + return &Reader{ + buf: make([]byte, size), + rd: rd, + lastByte: -1, + lastRuneSize: -1, } - b = new(Reader) - b.buf = make([]byte, size) - b.rd = rd - b.lastByte = -1 - b.lastRuneSize = -1 - return b, nil } // NewReader returns a new Reader whose buffer has the default size. func NewReader(rd io.Reader) *Reader { - b, err := NewReaderSize(rd, defaultBufSize) - if err != nil { - // cannot happen - defaultBufSize is a valid size - panic(err) - } - return b + return NewReaderSize(rd, defaultBufSize) } // fill reads a new chunk into the buffer. @@ -101,7 +81,7 @@ func (b *Reader) fill() { } } -func (b *Reader) readErr() os.Error { +func (b *Reader) readErr() error { err := b.err b.err = nil return err @@ -111,7 +91,7 @@ func (b *Reader) readErr() os.Error { // being valid at the next read call. If Peek returns fewer than n bytes, it // also returns an error explaining why the read is short. The error is // ErrBufferFull if n is larger than b's buffer size. -func (b *Reader) Peek(n int) ([]byte, os.Error) { +func (b *Reader) Peek(n int) ([]byte, error) { if n < 0 { return nil, ErrNegativeCount } @@ -125,9 +105,12 @@ func (b *Reader) Peek(n int) ([]byte, os.Error) { if m > n { m = n } - err := b.readErr() - if m < n && err == nil { - err = ErrBufferFull + var err error + if m < n { + err = b.readErr() + if err == nil { + err = ErrBufferFull + } } return b.buf[b.r : b.r+m], err } @@ -136,8 +119,8 @@ func (b *Reader) Peek(n int) ([]byte, os.Error) { // It returns the number of bytes read into p. // It calls Read at most once on the underlying Reader, // hence n may be less than len(p). -// At EOF, the count will be zero and err will be os.EOF. -func (b *Reader) Read(p []byte) (n int, err os.Error) { +// At EOF, the count will be zero and err will be io.EOF. +func (b *Reader) Read(p []byte) (n int, err error) { n = len(p) if n == 0 { return 0, b.readErr() @@ -174,7 +157,7 @@ func (b *Reader) Read(p []byte) (n int, err os.Error) { // ReadByte reads and returns a single byte. // If no byte is available, returns an error. -func (b *Reader) ReadByte() (c byte, err os.Error) { +func (b *Reader) ReadByte() (c byte, err error) { b.lastRuneSize = -1 for b.w == b.r { if b.err != nil { @@ -189,7 +172,7 @@ func (b *Reader) ReadByte() (c byte, err os.Error) { } // UnreadByte unreads the last byte. Only the most recently read byte can be unread. -func (b *Reader) UnreadByte() os.Error { +func (b *Reader) UnreadByte() error { b.lastRuneSize = -1 if b.r == b.w && b.lastByte >= 0 { b.w = 1 @@ -207,8 +190,9 @@ func (b *Reader) UnreadByte() os.Error { } // ReadRune reads a single UTF-8 encoded Unicode character and returns the -// rune and its size in bytes. -func (b *Reader) ReadRune() (rune int, size int, err os.Error) { +// rune and its size in bytes. If the encoded rune is invalid, it consumes one byte +// and returns unicode.ReplacementChar (U+FFFD) with a size of 1. +func (b *Reader) ReadRune() (r rune, size int, err error) { for b.r+utf8.UTFMax > b.w && !utf8.FullRune(b.buf[b.r:b.w]) && b.err == nil { b.fill() } @@ -216,21 +200,21 @@ func (b *Reader) ReadRune() (rune int, size int, err os.Error) { if b.r == b.w { return 0, 0, b.readErr() } - rune, size = int(b.buf[b.r]), 1 - if rune >= 0x80 { - rune, size = utf8.DecodeRune(b.buf[b.r:b.w]) + r, size = rune(b.buf[b.r]), 1 + if r >= 0x80 { + r, size = utf8.DecodeRune(b.buf[b.r:b.w]) } b.r += size b.lastByte = int(b.buf[b.r-1]) b.lastRuneSize = size - return rune, size, nil + return r, size, nil } // UnreadRune unreads the last rune. If the most recent read operation on // the buffer was not a ReadRune, UnreadRune returns an error. (In this // regard it is stricter than UnreadByte, which will unread the last byte // from any read operation.) -func (b *Reader) UnreadRune() os.Error { +func (b *Reader) UnreadRune() error { if b.lastRuneSize < 0 || b.r == 0 { return ErrInvalidUnreadRune } @@ -247,13 +231,13 @@ func (b *Reader) Buffered() int { return b.w - b.r } // returning a slice pointing at the bytes in the buffer. // The bytes stop being valid at the next read call. // If ReadSlice encounters an error before finding a delimiter, -// it returns all the data in the buffer and the error itself (often os.EOF). +// it returns all the data in the buffer and the error itself (often io.EOF). // ReadSlice fails with error ErrBufferFull if the buffer fills without a delim. // Because the data returned from ReadSlice will be overwritten // by the next I/O operation, most clients should use // ReadBytes or ReadString instead. // ReadSlice returns err != nil if and only if line does not end in delim. -func (b *Reader) ReadSlice(delim byte) (line []byte, err os.Error) { +func (b *Reader) ReadSlice(delim byte) (line []byte, err error) { // Look in buffer. if i := bytes.IndexByte(b.buf[b.r:b.w], delim); i >= 0 { line1 := b.buf[b.r : b.r+i+1] @@ -295,22 +279,37 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err os.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. -func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) { +func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) { line, err = b.ReadSlice('\n') if err == ErrBufferFull { + // Handle the case where "\r\n" straddles the buffer. + if len(line) > 0 && line[len(line)-1] == '\r' { + // Put the '\r' back on buf and drop it from line. + // Let the next call to ReadLine check for "\r\n". + if b.r == 0 { + // should be unreachable + panic("bufio: tried to rewind past start of buffer") + } + b.r-- + line = line[:len(line)-1] + } return line, true, nil } if len(line) == 0 { + if err != nil { + line = nil + } return } err = nil if line[len(line)-1] == '\n' { - line = line[:len(line)-1] - } - if len(line) > 0 && line[len(line)-1] == '\r' { - line = line[:len(line)-1] + drop := 1 + if len(line) > 1 && line[len(line)-2] == '\r' { + drop = 2 + } + line = line[:len(line)-drop] } return } @@ -318,10 +317,10 @@ func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) { // ReadBytes reads until the first occurrence of delim in the input, // returning a slice containing the data up to and including the delimiter. // If ReadBytes encounters an error before finding a delimiter, -// it returns the data read before the error and the error itself (often os.EOF). +// 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. -func (b *Reader) ReadBytes(delim byte) (line []byte, err os.Error) { +func (b *Reader) ReadBytes(delim byte) (line []byte, err error) { // Use ReadSlice to look for array, // accumulating full buffers. var frag []byte @@ -329,7 +328,7 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err os.Error) { err = nil for { - var e os.Error + var e error frag, e = b.ReadSlice(delim) if e == nil { // got final fragment break @@ -365,10 +364,10 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err os.Error) { // ReadString reads until the first occurrence of delim in the input, // returning a string containing the data up to and including the delimiter. // If ReadString encounters an error before finding a delimiter, -// it returns the data read before the error and the error itself (often os.EOF). +// 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. -func (b *Reader) ReadString(delim byte) (line string, err os.Error) { +func (b *Reader) ReadString(delim byte) (line string, err error) { bytes, e := b.ReadBytes(delim) return string(bytes), e } @@ -376,44 +375,40 @@ func (b *Reader) ReadString(delim byte) (line string, err os.Error) { // buffered output // Writer implements buffering for an io.Writer object. +// If an error occurs writing to a Writer, no more data will be +// accepted and all subsequent writes will return the error. type Writer struct { - err os.Error + err error buf []byte n int wr io.Writer } -// NewWriterSize creates a new Writer whose buffer has the specified size, -// which must be greater than zero. If the argument io.Writer is already a -// Writer with large enough size, it returns the underlying Writer. -// It returns the Writer and any error. -func NewWriterSize(wr io.Writer, size int) (*Writer, os.Error) { - if size <= 0 { - return nil, BufSizeError(size) - } +// NewWriterSize returns a new Writer whose buffer has at least the specified +// size. If the argument io.Writer is already a Writer with large enough +// size, it returns the underlying Writer. +func NewWriterSize(wr io.Writer, size int) *Writer { // Is it already a Writer? b, ok := wr.(*Writer) if ok && len(b.buf) >= size { - return b, nil + return b + } + if size <= 0 { + size = defaultBufSize } b = new(Writer) b.buf = make([]byte, size) b.wr = wr - return b, nil + return b } // NewWriter returns a new Writer whose buffer has the default size. func NewWriter(wr io.Writer) *Writer { - b, err := NewWriterSize(wr, defaultBufSize) - if err != nil { - // cannot happen - defaultBufSize is valid size - panic(err) - } - return b + return NewWriterSize(wr, defaultBufSize) } // Flush writes any buffered data to the underlying io.Writer. -func (b *Writer) Flush() os.Error { +func (b *Writer) Flush() error { if b.err != nil { return b.err } @@ -446,7 +441,7 @@ func (b *Writer) Buffered() int { return b.n } // It returns the number of bytes written. // If nn < len(p), it also returns an error explaining // why the write is short. -func (b *Writer) Write(p []byte) (nn int, err os.Error) { +func (b *Writer) Write(p []byte) (nn int, err error) { for len(p) > b.Available() && b.err == nil { var n int if b.Buffered() == 0 { @@ -471,7 +466,7 @@ func (b *Writer) Write(p []byte) (nn int, err os.Error) { } // WriteByte writes a single byte. -func (b *Writer) WriteByte(c byte) os.Error { +func (b *Writer) WriteByte(c byte) error { if b.err != nil { return b.err } @@ -485,9 +480,9 @@ func (b *Writer) WriteByte(c byte) os.Error { // WriteRune writes a single Unicode code point, returning // the number of bytes written and any error. -func (b *Writer) WriteRune(rune int) (size int, err os.Error) { - if rune < utf8.RuneSelf { - err = b.WriteByte(byte(rune)) +func (b *Writer) WriteRune(r rune) (size int, err error) { + if r < utf8.RuneSelf { + err = b.WriteByte(byte(r)) if err != nil { return 0, err } @@ -504,10 +499,10 @@ func (b *Writer) WriteRune(rune int) (size int, err os.Error) { n = b.Available() if n < utf8.UTFMax { // Can only happen if buffer is silly small. - return b.WriteString(string(rune)) + return b.WriteString(string(r)) } } - size = utf8.EncodeRune(b.buf[b.n:], rune) + size = utf8.EncodeRune(b.buf[b.n:], r) b.n += size return size, nil } @@ -516,7 +511,7 @@ func (b *Writer) WriteRune(rune int) (size int, err os.Error) { // It returns the number of bytes written. // If the count is less than len(s), it also returns an error explaining // why the write is short. -func (b *Writer) WriteString(s string) (int, os.Error) { +func (b *Writer) WriteString(s string) (int, error) { nn := 0 for len(s) > b.Available() && b.err == nil { n := copy(b.buf[b.n:], s) diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go index 82c73d36a..a43cbd23a 100644 --- a/src/pkg/bufio/bufio_test.go +++ b/src/pkg/bufio/bufio_test.go @@ -10,11 +10,10 @@ import ( "fmt" "io" "io/ioutil" - "os" "strings" "testing" "testing/iotest" - "utf8" + "unicode/utf8" ) // Reads from a reader and rot13s the result. @@ -28,7 +27,7 @@ func newRot13Reader(r io.Reader) *rot13Reader { return r13 } -func (r13 *rot13Reader) Read(p []byte) (int, os.Error) { +func (r13 *rot13Reader) Read(p []byte) (int, error) { n, e := r13.r.Read(p) if e != nil { return n, e @@ -50,14 +49,14 @@ func readBytes(buf *Reader) string { nb := 0 for { c, e := buf.ReadByte() - if e == os.EOF { + if e == io.EOF { break } if e == nil { b[nb] = c nb++ } else if e != iotest.ErrTimeout { - panic("Data: " + e.String()) + panic("Data: " + e.Error()) } } return string(b[0:nb]) @@ -95,11 +94,11 @@ func readLines(b *Reader) string { s := "" for { s1, e := b.ReadString('\n') - if e == os.EOF { + if e == io.EOF { break } if e != nil && e != iotest.ErrTimeout { - panic("GetLines: " + e.String()) + panic("GetLines: " + e.Error()) } s += s1 } @@ -113,7 +112,7 @@ func reads(buf *Reader, m int) string { for { n, e := buf.Read(b[nb : nb+m]) nb += n - if e == os.EOF { + if e == io.EOF { break } } @@ -136,9 +135,10 @@ var bufreaders = []bufReader{ {"lines", readLines}, } +const minReadBufferSize = 16 + var bufsizes = []int{ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 23, 32, 46, 64, 93, 128, 1024, 4096, + minReadBufferSize, 23, 32, 46, 64, 93, 128, 1024, 4096, } func TestReader(t *testing.T) { @@ -161,7 +161,7 @@ func TestReader(t *testing.T) { bufreader := bufreaders[j] bufsize := bufsizes[k] read := readmaker.fn(bytes.NewBufferString(text)) - buf, _ := NewReaderSize(read, bufsize) + buf := NewReaderSize(read, bufsize) s := bufreader.fn(buf) if s != text { t.Errorf("reader=%s fn=%s bufsize=%d want=%q got=%q", @@ -179,13 +179,13 @@ type StringReader struct { step int } -func (r *StringReader) Read(p []byte) (n int, err os.Error) { +func (r *StringReader) Read(p []byte) (n int, err error) { if r.step < len(r.data) { s := r.data[r.step] n = copy(p, s) r.step++ } else { - err = os.EOF + err = io.EOF } return } @@ -195,14 +195,14 @@ func readRuneSegments(t *testing.T, segments []string) { want := strings.Join(segments, "") r := NewReader(&StringReader{data: segments}) for { - rune, _, err := r.ReadRune() + r, _, err := r.ReadRune() if err != nil { - if err != os.EOF { + if err != io.EOF { return } break } - got += string(rune) + got += string(r) } if got != want { t.Errorf("segments=%v got=%s want=%s", segments, got, want) @@ -233,24 +233,24 @@ func TestUnreadRune(t *testing.T) { r := NewReader(&StringReader{data: segments}) // Normal execution. for { - rune, _, err := r.ReadRune() + r1, _, err := r.ReadRune() if err != nil { - if err != os.EOF { + if err != io.EOF { t.Error("unexpected EOF") } break } - got += string(rune) + got += string(r1) // Put it back and read it again if err = r.UnreadRune(); err != nil { t.Error("unexpected error on UnreadRune:", err) } - rune1, _, err := r.ReadRune() + r2, _, err := r.ReadRune() if err != nil { t.Error("unexpected error reading after unreading:", err) } - if rune != rune1 { - t.Errorf("incorrect rune after unread: got %c wanted %c", rune1, rune) + if r1 != r2 { + t.Errorf("incorrect rune after unread: got %c wanted %c", r1, r2) } } if got != data { @@ -328,7 +328,7 @@ func TestUnreadRuneAtEOF(t *testing.T) { _, _, err := r.ReadRune() if err == nil { t.Error("expected error at EOF") - } else if err != os.EOF { + } else if err != io.EOF { t.Error("expected EOF; got", err) } } @@ -339,25 +339,25 @@ func TestReadWriteRune(t *testing.T) { w := NewWriter(byteBuf) // Write the runes out using WriteRune buf := make([]byte, utf8.UTFMax) - for rune := 0; rune < NRune; rune++ { - size := utf8.EncodeRune(buf, rune) - nbytes, err := w.WriteRune(rune) + for r := rune(0); r < NRune; r++ { + size := utf8.EncodeRune(buf, r) + nbytes, err := w.WriteRune(r) if err != nil { - t.Fatalf("WriteRune(0x%x) error: %s", rune, err) + t.Fatalf("WriteRune(0x%x) error: %s", r, err) } if nbytes != size { - t.Fatalf("WriteRune(0x%x) expected %d, got %d", rune, size, nbytes) + t.Fatalf("WriteRune(0x%x) expected %d, got %d", r, size, nbytes) } } w.Flush() r := NewReader(byteBuf) // Read them back with ReadRune - for rune := 0; rune < NRune; rune++ { - size := utf8.EncodeRune(buf, rune) + for r1 := rune(0); r1 < NRune; r1++ { + size := utf8.EncodeRune(buf, r1) nr, nbytes, err := r.ReadRune() - if nr != rune || nbytes != size || err != nil { - t.Fatalf("ReadRune(0x%x) got 0x%x,%d not 0x%x,%d (err=%s)", r, nr, nbytes, r, size, err) + if nr != r1 || nbytes != size || err != nil { + t.Fatalf("ReadRune(0x%x) got 0x%x,%d not 0x%x,%d (err=%s)", r1, nr, nbytes, r1, size, err) } } } @@ -379,18 +379,14 @@ func TestWriter(t *testing.T) { // and that the data is correct. w.Reset() - buf, e := NewWriterSize(w, bs) + buf := NewWriterSize(w, bs) context := fmt.Sprintf("nwrite=%d bufsize=%d", nwrite, bs) - if e != nil { - t.Errorf("%s: NewWriterSize %d: %v", context, bs, e) - continue - } n, e1 := buf.Write(data[0:nwrite]) if e1 != nil || n != nwrite { t.Errorf("%s: buf.Write %d = %d, %v", context, nwrite, n, e1) continue } - if e = buf.Flush(); e != nil { + if e := buf.Flush(); e != nil { t.Errorf("%s: buf.Flush = %v", context, e) } @@ -413,11 +409,11 @@ func TestWriter(t *testing.T) { type errorWriterTest struct { n, m int - err os.Error - expect os.Error + err error + expect error } -func (w errorWriterTest) Write(p []byte) (int, os.Error) { +func (w errorWriterTest) Write(p []byte) (int, error) { return len(p) * w.n / w.m, w.err } @@ -425,9 +421,9 @@ var errorWriterTests = []errorWriterTest{ {0, 1, nil, io.ErrShortWrite}, {1, 2, nil, io.ErrShortWrite}, {1, 1, nil, nil}, - {0, 1, os.EPIPE, os.EPIPE}, - {1, 2, os.EPIPE, os.EPIPE}, - {1, 1, os.EPIPE, os.EPIPE}, + {0, 1, io.ErrClosedPipe, io.ErrClosedPipe}, + {1, 2, io.ErrClosedPipe, io.ErrClosedPipe}, + {1, 1, io.ErrClosedPipe, io.ErrClosedPipe}, } func TestWriteErrors(t *testing.T) { @@ -447,23 +443,14 @@ func TestWriteErrors(t *testing.T) { func TestNewReaderSizeIdempotent(t *testing.T) { const BufSize = 1000 - b, err := NewReaderSize(bytes.NewBufferString("hello world"), BufSize) - if err != nil { - t.Error("NewReaderSize create fail", err) - } + b := NewReaderSize(bytes.NewBufferString("hello world"), BufSize) // Does it recognize itself? - b1, err2 := NewReaderSize(b, BufSize) - if err2 != nil { - t.Error("NewReaderSize #2 create fail", err2) - } + b1 := NewReaderSize(b, BufSize) if b1 != b { t.Error("NewReaderSize did not detect underlying Reader") } // Does it wrap if existing buffer is too small? - b2, err3 := NewReaderSize(b, 2*BufSize) - if err3 != nil { - t.Error("NewReaderSize #3 create fail", err3) - } + b2 := NewReaderSize(b, 2*BufSize) if b2 == b { t.Error("NewReaderSize did not enlarge buffer") } @@ -471,23 +458,14 @@ func TestNewReaderSizeIdempotent(t *testing.T) { func TestNewWriterSizeIdempotent(t *testing.T) { const BufSize = 1000 - b, err := NewWriterSize(new(bytes.Buffer), BufSize) - if err != nil { - t.Error("NewWriterSize create fail", err) - } + b := NewWriterSize(new(bytes.Buffer), BufSize) // Does it recognize itself? - b1, err2 := NewWriterSize(b, BufSize) - if err2 != nil { - t.Error("NewWriterSize #2 create fail", err2) - } + b1 := NewWriterSize(b, BufSize) if b1 != b { t.Error("NewWriterSize did not detect underlying Writer") } // Does it wrap if existing buffer is too small? - b2, err3 := NewWriterSize(b, 2*BufSize) - if err3 != nil { - t.Error("NewWriterSize #3 create fail", err3) - } + b2 := NewWriterSize(b, 2*BufSize) if b2 == b { t.Error("NewWriterSize did not enlarge buffer") } @@ -496,10 +474,7 @@ func TestNewWriterSizeIdempotent(t *testing.T) { func TestWriteString(t *testing.T) { const BufSize = 8 buf := new(bytes.Buffer) - b, err := NewWriterSize(buf, BufSize) - if err != nil { - t.Error("NewWriterSize create fail", err) - } + b := NewWriterSize(buf, BufSize) b.WriteString("0") // easy b.WriteString("123456") // still easy b.WriteString("7890") // easy after flush @@ -515,27 +490,29 @@ func TestWriteString(t *testing.T) { } func TestBufferFull(t *testing.T) { - buf, _ := NewReaderSize(strings.NewReader("hello, world"), 5) - line, err := buf.ReadSlice(',') - if string(line) != "hello" || err != ErrBufferFull { + const longString = "And now, hello, world! It is the time for all good men to come to the aid of their party" + buf := NewReaderSize(strings.NewReader(longString), minReadBufferSize) + line, err := buf.ReadSlice('!') + if string(line) != "And now, hello, " || err != ErrBufferFull { t.Errorf("first ReadSlice(,) = %q, %v", line, err) } - line, err = buf.ReadSlice(',') - if string(line) != "," || err != nil { + line, err = buf.ReadSlice('!') + if string(line) != "world!" || err != nil { t.Errorf("second ReadSlice(,) = %q, %v", line, err) } } func TestPeek(t *testing.T) { p := make([]byte, 10) - buf, _ := NewReaderSize(strings.NewReader("abcdefghij"), 4) + // string is 16 (minReadBufferSize) long. + buf := NewReaderSize(strings.NewReader("abcdefghijklmnop"), minReadBufferSize) if s, err := buf.Peek(1); string(s) != "a" || err != nil { t.Fatalf("want %q got %q, err=%v", "a", string(s), err) } if s, err := buf.Peek(4); string(s) != "abcd" || err != nil { t.Fatalf("want %q got %q, err=%v", "abcd", string(s), err) } - if _, err := buf.Peek(5); err != ErrBufferFull { + if _, err := buf.Peek(32); err != ErrBufferFull { t.Fatalf("want ErrBufFull got %v", err) } if _, err := buf.Read(p[0:3]); string(p[0:3]) != "abc" || err != nil { @@ -553,15 +530,36 @@ func TestPeek(t *testing.T) { if s, err := buf.Peek(4); string(s) != "ghij" || err != nil { t.Fatalf("want %q got %q, err=%v", "ghij", string(s), err) } - if _, err := buf.Read(p[0:4]); string(p[0:4]) != "ghij" || err != nil { - t.Fatalf("want %q got %q, err=%v", "ghij", string(p[0:3]), err) + if _, err := buf.Read(p[0:]); string(p[0:]) != "ghijklmnop" || err != nil { + t.Fatalf("want %q got %q, err=%v", "ghijklmnop", string(p[0:minReadBufferSize]), err) } if s, err := buf.Peek(0); string(s) != "" || err != nil { t.Fatalf("want %q got %q, err=%v", "", string(s), err) } - if _, err := buf.Peek(1); err != os.EOF { + if _, err := buf.Peek(1); err != io.EOF { t.Fatalf("want EOF got %v", err) } + + // Test for issue 3022, not exposing a reader's error on a successful Peek. + buf = NewReaderSize(dataAndEOFReader("abcd"), 32) + if s, err := buf.Peek(2); string(s) != "ab" || err != nil { + t.Errorf(`Peek(2) on "abcd", EOF = %q, %v; want "ab", nil`, string(s), err) + } + if s, err := buf.Peek(4); string(s) != "abcd" || err != nil { + t.Errorf(`Peek(4) on "abcd", EOF = %q, %v; want "abcd", nil`, string(s), err) + } + if n, err := buf.Read(p[0:5]); string(p[0:n]) != "abcd" || err != nil { + t.Fatalf("Read after peek = %q, %v; want abcd, EOF", p[0:n], err) + } + if n, err := buf.Read(p[0:1]); string(p[0:n]) != "" || err != io.EOF { + t.Fatalf(`second Read after peek = %q, %v; want "", EOF`, p[0:n], err) + } +} + +type dataAndEOFReader string + +func (r dataAndEOFReader) Read(p []byte) (int, error) { + return copy(p, r), io.EOF } func TestPeekThenUnreadRune(t *testing.T) { @@ -583,7 +581,7 @@ type testReader struct { stride int } -func (t *testReader) Read(buf []byte) (n int, err os.Error) { +func (t *testReader) Read(buf []byte) (n int, err error) { n = t.stride if n > len(t.data) { n = len(t.data) @@ -594,7 +592,7 @@ func (t *testReader) Read(buf []byte) (n int, err os.Error) { copy(buf, t.data) t.data = t.data[n:] if len(t.data) == 0 { - err = os.EOF + err = io.EOF } return } @@ -604,7 +602,7 @@ func testReadLine(t *testing.T, input []byte) { for stride := 1; stride < 2; stride++ { done := 0 reader := testReader{input, stride} - l, _ := NewReaderSize(&reader, len(input)+1) + l := NewReaderSize(&reader, len(input)+1) for { line, isPrefix, err := l.ReadLine() if len(line) > 0 && err != nil { @@ -614,7 +612,7 @@ func testReadLine(t *testing.T, input []byte) { t.Errorf("ReadLine returned prefix") } if err != nil { - if err != os.EOF { + if err != io.EOF { t.Fatalf("Got unknown error: %s", err) } break @@ -636,19 +634,25 @@ func TestReadLine(t *testing.T) { } func TestLineTooLong(t *testing.T) { - buf := bytes.NewBuffer([]byte("aaabbbcc\n")) - l, _ := NewReaderSize(buf, 3) + data := make([]byte, 0) + for i := 0; i < minReadBufferSize*5/2; i++ { + data = append(data, '0'+byte(i%10)) + } + buf := bytes.NewBuffer(data) + l := NewReaderSize(buf, minReadBufferSize) line, isPrefix, err := l.ReadLine() - if !isPrefix || !bytes.Equal(line, []byte("aaa")) || err != nil { - t.Errorf("bad result for first line: %x %s", line, err) + if !isPrefix || !bytes.Equal(line, data[:minReadBufferSize]) || err != nil { + t.Errorf("bad result for first line: got %q want %q %v", line, data[:minReadBufferSize], err) } + data = data[len(line):] line, isPrefix, err = l.ReadLine() - if !isPrefix || !bytes.Equal(line, []byte("bbb")) || err != nil { - t.Errorf("bad result for second line: %x", line) + if !isPrefix || !bytes.Equal(line, data[:minReadBufferSize]) || err != nil { + t.Errorf("bad result for second line: got %q want %q %v", line, data[:minReadBufferSize], err) } + data = data[len(line):] line, isPrefix, err = l.ReadLine() - if isPrefix || !bytes.Equal(line, []byte("cc")) || err != nil { - t.Errorf("bad result for third line: %x", line) + if isPrefix || !bytes.Equal(line, data[:minReadBufferSize/2]) || err != nil { + t.Errorf("bad result for third line: got %q want %q %v", line, data[:minReadBufferSize/2], err) } line, isPrefix, err = l.ReadLine() if isPrefix || err == nil { @@ -657,12 +661,12 @@ func TestLineTooLong(t *testing.T) { } func TestReadAfterLines(t *testing.T) { - line1 := "line1" - restData := "line2\nline 3\n" + line1 := "this is line1" + restData := "this is line2\nthis is line 3\n" inbuf := bytes.NewBuffer([]byte(line1 + "\n" + restData)) outbuf := new(bytes.Buffer) maxLineLength := len(line1) + len(restData)/2 - l, _ := NewReaderSize(inbuf, maxLineLength) + l := NewReaderSize(inbuf, maxLineLength) line, isPrefix, err := l.ReadLine() if isPrefix || err != nil || string(line) != line1 { t.Errorf("bad result for first line: isPrefix=%v err=%v line=%q", isPrefix, err, string(line)) @@ -677,15 +681,15 @@ func TestReadAfterLines(t *testing.T) { } func TestReadEmptyBuffer(t *testing.T) { - l, _ := NewReaderSize(bytes.NewBuffer(nil), 10) + l := NewReaderSize(new(bytes.Buffer), minReadBufferSize) line, isPrefix, err := l.ReadLine() - if err != os.EOF { + if err != io.EOF { t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err) } } func TestLinesAfterRead(t *testing.T) { - l, _ := NewReaderSize(bytes.NewBuffer([]byte("foo")), 10) + l := NewReaderSize(bytes.NewBuffer([]byte("foo")), minReadBufferSize) _, err := ioutil.ReadAll(l) if err != nil { t.Error(err) @@ -693,7 +697,68 @@ func TestLinesAfterRead(t *testing.T) { } line, isPrefix, err := l.ReadLine() - if err != os.EOF { + if err != io.EOF { t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err) } } + +func TestReadLineNonNilLineOrError(t *testing.T) { + r := NewReader(strings.NewReader("line 1\n")) + for i := 0; i < 2; i++ { + l, _, err := r.ReadLine() + if l != nil && err != nil { + t.Fatalf("on line %d/2; ReadLine=%#v, %v; want non-nil line or Error, but not both", + i+1, l, err) + } + } +} + +type readLineResult struct { + line []byte + isPrefix bool + err error +} + +var readLineNewlinesTests = []struct { + input string + expect []readLineResult +}{ + {"012345678901234\r\n012345678901234\r\n", []readLineResult{ + {[]byte("012345678901234"), true, nil}, + {nil, false, nil}, + {[]byte("012345678901234"), true, nil}, + {nil, false, nil}, + {nil, false, io.EOF}, + }}, + {"0123456789012345\r012345678901234\r", []readLineResult{ + {[]byte("0123456789012345"), true, nil}, + {[]byte("\r012345678901234"), true, nil}, + {[]byte("\r"), false, nil}, + {nil, false, io.EOF}, + }}, +} + +func TestReadLineNewlines(t *testing.T) { + for _, e := range readLineNewlinesTests { + testReadLineNewlines(t, e.input, e.expect) + } +} + +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 { + t.Errorf("%q call %d, line == %q, want %q", input, i, line, e.line) + return + } + if isPrefix != e.isPrefix { + t.Errorf("%q call %d, isPrefix == %v, want %v", input, i, isPrefix, e.isPrefix) + return + } + if err != e.err { + t.Errorf("%q call %d, err == %v, want %v", input, i, err, e.err) + return + } + } +} diff --git a/src/pkg/builtin/builtin.go b/src/pkg/builtin/builtin.go index 07acce4f7..e81616ca4 100644 --- a/src/pkg/builtin/builtin.go +++ b/src/pkg/builtin/builtin.go @@ -3,29 +3,110 @@ // license that can be found in the LICENSE file. /* - Package builtin provides documentation for Go's built-in functions. - The functions documented here are not actually in package builtin + Package builtin provides documentation for Go's predeclared identifiers. + The items documented here are not actually in package builtin but their descriptions here allow godoc to present documentation - for the language's special functions. + for the language's special identifiers. */ package builtin +// bool is the set of boolean values, true and false. +type bool bool + +// uint8 is the set of all unsigned 8-bit integers. +// Range: 0 through 255. +type uint8 uint8 + +// uint16 is the set of all unsigned 16-bit integers. +// Range: 0 through 65535. +type uint16 uint16 + +// uint32 is the set of all unsigned 32-bit integers. +// Range: 0 through 4294967295. +type uint32 uint32 + +// uint64 is the set of all unsigned 64-bit integers. +// Range: 0 through 18446744073709551615. +type uint64 uint64 + +// int8 is the set of all signed 8-bit integers. +// Range: -128 through 127. +type int8 int8 + +// int16 is the set of all signed 16-bit integers. +// Range: -32768 through 32767. +type int16 int16 + +// int32 is the set of all signed 32-bit integers. +// Range: -2147483648 through 2147483647. +type int32 int32 + +// int64 is the set of all signed 64-bit integers. +// Range: -9223372036854775808 through 9223372036854775807. +type int64 int64 + +// float32 is the set of all IEEE-754 32-bit floating-point numbers. +type float32 float32 + +// float64 is the set of all IEEE-754 64-bit floating-point numbers. +type float64 float64 + +// complex64 is the set of all complex numbers with float32 real and +// imaginary parts. +type complex64 complex64 + +// complex128 is the set of all complex numbers with float64 real and +// imaginary parts. +type complex128 complex128 + +// string is the set of all strings of 8-bit bytes, conventionally but not +// necessarily representing UTF-8-encoded text. A string may be empty, but +// not nil. Values of string type are immutable. +type string string + +// int is a signed integer type that is at least 32 bits in size. It is a +// distinct type, however, and not an alias for, say, int32. +type int int + +// uint is an unsigned integer type that is at least 32 bits in size. It is a +// distinct type, however, and not an alias for, say, uint32. +type uint uint + +// uintptr is an integer type that is large enough to hold the bit pattern of +// any pointer. +type uintptr uintptr + +// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is +// used, by convention, to distinguish byte values from 8-bit unsigned +// integer values. +type byte byte + +// rune is an alias for int and is equivalent to int 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 // for any Go type, but represents the same type for any given function // invocation. type Type int +// Type1 is here for the purposes of documentation only. It is a stand-in +// for any Go type, but represents the same type for any given function +// invocation. +type Type1 int + // IntegerType is here for the purposes of documentation only. It is a stand-in // for any integer type: int, uint, int8 etc. type IntegerType int // FloatType is here for the purposes of documentation only. It is a stand-in // for either float type: float32 or float64. -type FloatType int +type FloatType float32 // ComplexType is here for the purposes of documentation only. It is a // stand-in for either complex type: complex64 or complex128. -type ComplexType int +type ComplexType complex64 // The append built-in function appends elements to the end of a slice. If // it has sufficient capacity, the destination is resliced to accommodate the @@ -43,6 +124,11 @@ func append(slice []Type, elems ...Type) []Type // len(src) and len(dst). 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. +func delete(m map[Type]Type1, key Type) + // The len built-in function returns the length of v, according to its type: // Array: the number of elements in v. // Pointer to array: the number of elements in *v (even if v is nil). @@ -95,7 +181,7 @@ func complex(r, i FloatType) ComplexType // The return value will be floating point type corresponding to the type of c. func real(c ComplexType) FloatType -// The imaginary built-in function returns the imaginary part of the complex +// The imag built-in function returns the imaginary part of the complex // number c. The return value will be floating point type corresponding to // the type of c. func imag(c ComplexType) FloatType @@ -133,3 +219,9 @@ func panic(v interface{}) // nil. Thus the return value from recover reports whether the goroutine is // panicking. func recover() interface{} + +// The error built-in interface type is the conventional interface for +// representing an error condition, with the nil value representing no error. +type error interface { + Error() string +} diff --git a/src/pkg/bytes/Makefile b/src/pkg/bytes/Makefile deleted file mode 100644 index 03395c7a4..000000000 --- a/src/pkg/bytes/Makefile +++ /dev/null @@ -1,16 +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 ../../Make.inc - -TARG=bytes -GOFILES=\ - buffer.go\ - bytes.go\ - bytes_decl.go\ - -OFILES=\ - asm_$(GOARCH).$O\ - -include ../../Make.pkg diff --git a/src/pkg/bytes/asm_386.s b/src/pkg/bytes/asm_386.s index f3391740b..e7833de0c 100644 --- a/src/pkg/bytes/asm_386.s +++ b/src/pkg/bytes/asm_386.s @@ -15,3 +15,19 @@ TEXT ·IndexByte(SB),7,$0 SUBL $1, DI MOVL DI, ret+16(FP) RET + +TEXT ·Equal(SB),7,$0 + MOVL len+4(FP), BX + MOVL len1+16(FP), CX + MOVL $0, AX + CMPL BX, CX + JNE eqret + MOVL p+0(FP), SI + MOVL q+12(FP), DI + CLD + REP; CMPSB + JNE eqret + MOVL $1, AX +eqret: + MOVB AX, ret+24(FP) + RET diff --git a/src/pkg/bytes/asm_amd64.s b/src/pkg/bytes/asm_amd64.s index c6793cbdc..5caea5c52 100644 --- a/src/pkg/bytes/asm_amd64.s +++ b/src/pkg/bytes/asm_amd64.s @@ -90,3 +90,19 @@ success: MOVL DI, ret+24(FP) RET +TEXT ·Equal(SB),7,$0 + MOVL len+8(FP), BX + MOVL len1+24(FP), CX + MOVL $0, AX + CMPL BX, CX + JNE eqret + MOVQ p+0(FP), SI + MOVQ q+16(FP), DI + CLD + REP; CMPSB + MOVL $1, DX + CMOVLEQ DX, AX +eqret: + MOVB AX, ret+32(FP) + RET + diff --git a/src/pkg/bytes/asm_arm.s b/src/pkg/bytes/asm_arm.s index f32fca136..4ed0c1580 100644 --- a/src/pkg/bytes/asm_arm.s +++ b/src/pkg/bytes/asm_arm.s @@ -6,3 +6,6 @@ TEXT ·IndexByte(SB),7,$0 B ·indexBytePortable(SB) +// no memcmp implementation on arm yet +TEXT ·Equal(SB),7,$0 + B ·equalPortable(SB) diff --git a/src/pkg/bytes/buffer.go b/src/pkg/bytes/buffer.go index 5de86105d..afdf22055 100644 --- a/src/pkg/bytes/buffer.go +++ b/src/pkg/bytes/buffer.go @@ -7,9 +7,9 @@ package bytes // Simple byte buffer for marshaling data. import ( + "errors" "io" - "os" - "utf8" + "unicode/utf8" ) // A Buffer is a variable-sized buffer of bytes with Read and Write methods. @@ -33,6 +33,9 @@ const ( opRead // Any other read operation. ) +// ErrTooLarge is passed to panic if memory cannot be allocated to store data in a buffer. +var ErrTooLarge = errors.New("bytes.Buffer: too large") + // Bytes returns a slice of the contents of the unread portion of the buffer; // len(b.Bytes()) == b.Len(). If the caller changes the contents of the // returned slice, the contents of the buffer will change provided there @@ -54,10 +57,13 @@ func (b *Buffer) String() string { func (b *Buffer) Len() int { return len(b.buf) - b.off } // Truncate discards all but the first n unread bytes from the buffer. -// It is an error to call b.Truncate(n) with n > b.Len(). +// It panics if n is negative or greater than the length of the buffer. func (b *Buffer) Truncate(n int) { b.lastRead = opInvalid - if n == 0 { + switch { + case n < 0 || n > b.Len(): + panic("bytes.Buffer: truncation out of range") + case n == 0: // Reuse buffer space. b.off = 0 } @@ -68,8 +74,9 @@ func (b *Buffer) Truncate(n int) { // b.Reset() is the same as b.Truncate(0). func (b *Buffer) Reset() { b.Truncate(0) } -// Grow buffer to guarantee space for n more bytes. -// Return index where bytes should be written. +// grow grows the buffer to guarantee space for n more bytes. +// It returns the index where bytes should be written. +// If the buffer can't grow it will panic with ErrTooLarge. func (b *Buffer) grow(n int) int { m := b.Len() // If buffer is empty, reset to recover space. @@ -82,7 +89,7 @@ func (b *Buffer) grow(n int) int { buf = b.bootstrap[0:] } else { // not enough space anywhere - buf = make([]byte, 2*cap(b.buf)+n) + buf = makeSlice(2*cap(b.buf) + n) copy(buf, b.buf[b.off:]) } b.buf = buf @@ -94,16 +101,19 @@ func (b *Buffer) grow(n int) int { // Write appends the contents of p to the buffer. The return // value n is the length of p; err is always nil. -func (b *Buffer) Write(p []byte) (n int, err os.Error) { +// If the buffer becomes too large, Write will panic with +// ErrTooLarge. +func (b *Buffer) Write(p []byte) (n int, err error) { b.lastRead = opInvalid m := b.grow(len(p)) - copy(b.buf[m:], p) - return len(p), nil + return copy(b.buf[m:], p), nil } // WriteString appends the contents of s to the buffer. The return // value n is the length of s; err is always nil. -func (b *Buffer) WriteString(s string) (n int, err os.Error) { +// If the buffer becomes too large, WriteString will panic with +// ErrTooLarge. +func (b *Buffer) WriteString(s string) (n int, err error) { b.lastRead = opInvalid m := b.grow(len(s)) return copy(b.buf[m:], s), nil @@ -117,33 +127,33 @@ const MinRead = 512 // ReadFrom reads data from r until EOF and appends it to the buffer. // The return value n is the number of bytes read. -// Any error except os.EOF encountered during the read +// Any error except io.EOF encountered during the read // is also returned. -func (b *Buffer) ReadFrom(r io.Reader) (n int64, err os.Error) { +// If the buffer becomes too large, ReadFrom will panic with +// ErrTooLarge. +func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) { b.lastRead = opInvalid // If buffer is empty, reset to recover space. if b.off >= len(b.buf) { b.Truncate(0) } for { - if cap(b.buf)-len(b.buf) < MinRead { - var newBuf []byte - // can we get space without allocation? - if b.off+cap(b.buf)-len(b.buf) >= MinRead { - // reuse beginning of buffer - newBuf = b.buf[0 : len(b.buf)-b.off] - } else { - // not enough space at end; put space on end - newBuf = make([]byte, len(b.buf)-b.off, 2*(cap(b.buf)-b.off)+MinRead) + if free := cap(b.buf) - len(b.buf); free < MinRead { + // not enough space at end + newBuf := b.buf + if b.off+free < MinRead { + // not enough space using beginning of buffer; + // double buffer capacity + newBuf = makeSlice(2*cap(b.buf) + MinRead) } copy(newBuf, b.buf[b.off:]) - b.buf = newBuf + b.buf = newBuf[:len(b.buf)-b.off] b.off = 0 } m, e := r.Read(b.buf[len(b.buf):cap(b.buf)]) b.buf = b.buf[0 : len(b.buf)+m] n += int64(m) - if e == os.EOF { + if e == io.EOF { break } if e != nil { @@ -153,21 +163,40 @@ func (b *Buffer) ReadFrom(r io.Reader) (n int64, err os.Error) { return n, nil // err is EOF, so return nil explicitly } +// makeSlice allocates a slice of size n. If the allocation fails, it panics +// with ErrTooLarge. +func makeSlice(n int) []byte { + // If the make fails, give a known error. + defer func() { + if recover() != nil { + panic(ErrTooLarge) + } + }() + return make([]byte, n) +} + // WriteTo writes data to w until the buffer is drained or an error // occurs. The return value n is the number of bytes written; it always // fits into an int, but it is int64 to match the io.WriterTo interface. // Any error encountered during the write is also returned. -func (b *Buffer) WriteTo(w io.Writer) (n int64, err os.Error) { +func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) { b.lastRead = opInvalid if b.off < len(b.buf) { + nBytes := b.Len() m, e := w.Write(b.buf[b.off:]) + if m > nBytes { + panic("bytes.Buffer.WriteTo: invalid Write count") + } b.off += m n = int64(m) if e != nil { return n, e } - // otherwise all bytes were written, by definition of + // all bytes should have been written, by definition of // Write method in io.Writer + if m != nBytes { + return n, io.ErrShortWrite + } } // Buffer is now empty; reset. b.Truncate(0) @@ -177,7 +206,9 @@ func (b *Buffer) WriteTo(w io.Writer) (n int64, err os.Error) { // WriteByte appends the byte c to the buffer. // The returned error is always nil, but is included // to match bufio.Writer's WriteByte. -func (b *Buffer) WriteByte(c byte) os.Error { +// If the buffer becomes too large, WriteByte will panic with +// ErrTooLarge. +func (b *Buffer) WriteByte(c byte) error { b.lastRead = opInvalid m := b.grow(1) b.buf[m] = c @@ -188,7 +219,9 @@ func (b *Buffer) WriteByte(c byte) os.Error { // code point r to the buffer, returning its length and // an error, which is always nil but is included // to match bufio.Writer's WriteRune. -func (b *Buffer) WriteRune(r int) (n int, err os.Error) { +// If the buffer becomes too large, WriteRune will panic with +// ErrTooLarge. +func (b *Buffer) WriteRune(r rune) (n int, err error) { if r < utf8.RuneSelf { b.WriteByte(byte(r)) return 1, nil @@ -200,14 +233,17 @@ func (b *Buffer) WriteRune(r int) (n int, err os.Error) { // Read reads the next len(p) bytes from the buffer or until the buffer // is drained. The return value n is the number of bytes read. If the -// buffer has no data to return, err is os.EOF even if len(p) is zero; +// buffer has no data to return, err is io.EOF (unless len(p) is zero); // otherwise it is nil. -func (b *Buffer) Read(p []byte) (n int, err os.Error) { +func (b *Buffer) Read(p []byte) (n int, err error) { b.lastRead = opInvalid if b.off >= len(b.buf) { // Buffer is empty, reset to recover space. b.Truncate(0) - return 0, os.EOF + if len(p) == 0 { + return + } + return 0, io.EOF } n = copy(p, b.buf[b.off:]) b.off += n @@ -236,13 +272,13 @@ func (b *Buffer) Next(n int) []byte { } // ReadByte reads and returns the next byte from the buffer. -// If no byte is available, it returns error os.EOF. -func (b *Buffer) ReadByte() (c byte, err os.Error) { +// If no byte is available, it returns error io.EOF. +func (b *Buffer) ReadByte() (c byte, err error) { b.lastRead = opInvalid if b.off >= len(b.buf) { // Buffer is empty, reset to recover space. b.Truncate(0) - return 0, os.EOF + return 0, io.EOF } c = b.buf[b.off] b.off++ @@ -252,21 +288,21 @@ func (b *Buffer) ReadByte() (c byte, err os.Error) { // ReadRune reads and returns the next UTF-8-encoded // Unicode code point from the buffer. -// If no bytes are available, the error returned is os.EOF. +// If no bytes are available, the error returned is io.EOF. // If the bytes are an erroneous UTF-8 encoding, it // consumes one byte and returns U+FFFD, 1. -func (b *Buffer) ReadRune() (r int, size int, err os.Error) { +func (b *Buffer) ReadRune() (r rune, size int, err error) { b.lastRead = opInvalid if b.off >= len(b.buf) { // Buffer is empty, reset to recover space. b.Truncate(0) - return 0, 0, os.EOF + return 0, 0, io.EOF } b.lastRead = opReadRune c := b.buf[b.off] if c < utf8.RuneSelf { b.off++ - return int(c), 1, nil + return rune(c), 1, nil } r, n := utf8.DecodeRune(b.buf[b.off:]) b.off += n @@ -278,9 +314,9 @@ func (b *Buffer) ReadRune() (r int, size int, err os.Error) { // not a ReadRune, UnreadRune returns an error. (In this regard // it is stricter than UnreadByte, which will unread the last byte // from any read operation.) -func (b *Buffer) UnreadRune() os.Error { +func (b *Buffer) UnreadRune() error { if b.lastRead != opReadRune { - return os.NewError("bytes.Buffer: UnreadRune: previous operation was not ReadRune") + return errors.New("bytes.Buffer: UnreadRune: previous operation was not ReadRune") } b.lastRead = opInvalid if b.off > 0 { @@ -293,9 +329,9 @@ func (b *Buffer) UnreadRune() os.Error { // UnreadByte unreads the last byte returned by the most recent // read operation. If write has happened since the last read, UnreadByte // returns an error. -func (b *Buffer) UnreadByte() os.Error { +func (b *Buffer) UnreadByte() error { if b.lastRead != opReadRune && b.lastRead != opRead { - return os.NewError("bytes.Buffer: UnreadByte: previous operation was not a read") + return errors.New("bytes.Buffer: UnreadByte: previous operation was not a read") } b.lastRead = opInvalid if b.off > 0 { @@ -307,15 +343,15 @@ func (b *Buffer) UnreadByte() os.Error { // ReadBytes reads until the first occurrence of delim in the input, // returning a slice containing the data up to and including the delimiter. // If ReadBytes encounters an error before finding a delimiter, -// it returns the data read before the error and the error itself (often os.EOF). +// 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. -func (b *Buffer) ReadBytes(delim byte) (line []byte, err os.Error) { +func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) { i := IndexByte(b.buf[b.off:], delim) size := i + 1 if i < 0 { size = len(b.buf) - b.off - err = os.EOF + err = io.EOF } line = make([]byte, size) copy(line, b.buf[b.off:]) @@ -326,23 +362,29 @@ func (b *Buffer) ReadBytes(delim byte) (line []byte, err os.Error) { // ReadString reads until the first occurrence of delim in the input, // returning a string containing the data up to and including the delimiter. // If ReadString encounters an error before finding a delimiter, -// it returns the data read before the error and the error itself (often os.EOF). +// 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. -func (b *Buffer) ReadString(delim byte) (line string, err os.Error) { +func (b *Buffer) ReadString(delim byte) (line string, err error) { bytes, err := b.ReadBytes(delim) return string(bytes), err } // NewBuffer creates and initializes a new Buffer using buf as its initial // contents. It is intended to prepare a Buffer to read existing data. It -// can also be used to size the internal buffer for writing. To do that, +// can also be used to size the internal buffer for writing. To do that, // buf should have the desired capacity but a length of zero. +// +// In most cases, new(Buffer) (or just declaring a Buffer variable) is +// sufficient to initialize a Buffer. func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} } // NewBufferString creates and initializes a new Buffer using string s as its -// initial contents. It is intended to prepare a buffer to read an existing +// initial contents. It is intended to prepare a buffer to read an existing // string. +// +// In most cases, new(Buffer) (or just declaring a Buffer variable) is +// sufficient to initialize a Buffer. func NewBufferString(s string) *Buffer { return &Buffer{buf: []byte(s)} } diff --git a/src/pkg/bytes/buffer_test.go b/src/pkg/bytes/buffer_test.go index 06d2a65c6..d0af11f10 100644 --- a/src/pkg/bytes/buffer_test.go +++ b/src/pkg/bytes/buffer_test.go @@ -6,17 +6,16 @@ package bytes_test import ( . "bytes" - "os" - "rand" + "io" + "math/rand" "testing" - "utf8" + "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. - func init() { bytes = make([]byte, N) for i := 0; i < N; i++ { @@ -264,7 +263,7 @@ func TestRuneIO(t *testing.T) { b := make([]byte, utf8.UTFMax*NRune) var buf Buffer n := 0 - for r := 0; r < NRune; r++ { + for r := rune(0); r < NRune; r++ { size := utf8.EncodeRune(b[n:], r) nbytes, err := buf.WriteRune(r) if err != nil { @@ -284,7 +283,7 @@ func TestRuneIO(t *testing.T) { p := make([]byte, utf8.UTFMax) // Read it back with ReadRune - for r := 0; r < NRune; r++ { + for r := rune(0); r < NRune; r++ { size := utf8.EncodeRune(p, r) nr, nbytes, err := buf.ReadRune() if nr != r || nbytes != size || err != nil { @@ -295,7 +294,7 @@ func TestRuneIO(t *testing.T) { // Check that UnreadRune works buf.Reset() buf.Write(b) - for r := 0; r < NRune; r++ { + for r := rune(0); r < NRune; r++ { r1, size, _ := buf.ReadRune() if err := buf.UnreadRune(); err != nil { t.Fatalf("UnreadRune(%U) got error %q", r, err) @@ -344,21 +343,21 @@ var readBytesTests = []struct { buffer string delim byte expected []string - err os.Error + err error }{ - {"", 0, []string{""}, os.EOF}, + {"", 0, []string{""}, io.EOF}, {"a\x00", 0, []string{"a\x00"}, nil}, {"abbbaaaba", 'b', []string{"ab", "b", "b", "aaab"}, nil}, {"hello\x01world", 1, []string{"hello\x01"}, nil}, - {"foo\nbar", 0, []string{"foo\nbar"}, os.EOF}, + {"foo\nbar", 0, []string{"foo\nbar"}, io.EOF}, {"alpha\nbeta\ngamma\n", '\n', []string{"alpha\n", "beta\n", "gamma\n"}, nil}, - {"alpha\nbeta\ngamma", '\n', []string{"alpha\n", "beta\n", "gamma"}, os.EOF}, + {"alpha\nbeta\ngamma", '\n', []string{"alpha\n", "beta\n", "gamma"}, io.EOF}, } func TestReadBytes(t *testing.T) { for _, test := range readBytesTests { buf := NewBufferString(test.buffer) - var err os.Error + var err error for _, expected := range test.expected { var bytes []byte bytes, err = buf.ReadBytes(test.delim) @@ -374,3 +373,16 @@ func TestReadBytes(t *testing.T) { } } } + +// Was a bug: used to give EOF reading empty slice at EOF. +func TestReadEmptyAtEOF(t *testing.T) { + b := new(Buffer) + slice := make([]byte, 0) + n, err := b.Read(slice) + if err != nil { + t.Errorf("read error: %v", err) + } + if n != 0 { + t.Errorf("wrong count; got %d want 0", n) + } +} diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go index 5119fce94..7d1426fb4 100644 --- a/src/pkg/bytes/bytes.go +++ b/src/pkg/bytes/bytes.go @@ -8,11 +8,12 @@ package bytes import ( "unicode" - "utf8" + "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 +// A nil argument is equivalent to an empty slice. func Compare(a, b []byte) int { m := len(a) if m > len(b) { @@ -37,7 +38,10 @@ func Compare(a, b []byte) int { } // Equal returns a boolean reporting whether a == b. -func Equal(a, b []byte) bool { +// 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 } @@ -74,18 +78,38 @@ func explode(s []byte, n int) [][]byte { // Count counts the number of non-overlapping instances of sep in s. func Count(s, sep []byte) int { - if len(sep) == 0 { + n := len(sep) + if n == 0 { return utf8.RuneCount(s) + 1 } + if n > len(s) { + return 0 + } + count := 0 c := sep[0] - n := 0 - for i := 0; i+len(sep) <= len(s); i++ { - if s[i] == c && (len(sep) == 1 || Equal(s[i:i+len(sep)], sep)) { - n++ - i += len(sep) - 1 + i := 0 + t := s[:len(s)-n+1] + for i < len(t) { + if t[i] != c { + o := IndexByte(t[i:], c) + if o < 0 { + break + } + i += o + } + if n == 1 || Equal(s[i:i+n], sep) { + count++ + i += n + continue } + i++ } - return n + return count +} + +// Contains returns whether subslice is within b. +func Contains(b, subslice []byte) bool { + return Index(b, subslice) != -1 } // Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. @@ -94,11 +118,27 @@ func Index(s, sep []byte) int { if n == 0 { return 0 } + if n > len(s) { + return -1 + } c := sep[0] - for i := 0; i+n <= len(s); i++ { - if s[i] == c && (n == 1 || Equal(s[i:i+n], sep)) { + if n == 1 { + return IndexByte(s, c) + } + i := 0 + t := s[:len(s)-n+1] + for i < len(t) { + if t[i] != c { + o := IndexByte(t[i:], c) + if o < 0 { + break + } + i += o + } + if Equal(s[i:i+n], sep) { return i } + i++ } return -1 } @@ -130,10 +170,10 @@ func LastIndex(s, sep []byte) int { // IndexRune interprets s as a sequence of UTF-8-encoded Unicode code points. // It returns the byte index of the first occurrence in s of the given rune. // It returns -1 if rune is not present in s. -func IndexRune(s []byte, rune int) int { +func IndexRune(s []byte, r rune) int { for i := 0; i < len(s); { - r, size := utf8.DecodeRune(s[i:]) - if r == rune { + r1, size := utf8.DecodeRune(s[i:]) + if r == r1 { return i } i += size @@ -147,16 +187,17 @@ func IndexRune(s []byte, rune int) int { // point in common. func IndexAny(s []byte, chars string) int { if len(chars) > 0 { - var rune, width int + var r rune + var width int for i := 0; i < len(s); i += width { - rune = int(s[i]) - if rune < utf8.RuneSelf { + r = rune(s[i]) + if r < utf8.RuneSelf { width = 1 } else { - rune, width = utf8.DecodeRune(s[i:]) + r, width = utf8.DecodeRune(s[i:]) } - for _, r := range chars { - if rune == r { + for _, ch := range chars { + if r == ch { return i } } @@ -172,10 +213,10 @@ func IndexAny(s []byte, chars string) int { func LastIndexAny(s []byte, chars string) int { if len(chars) > 0 { for i := len(s); i > 0; { - rune, size := utf8.DecodeLastRune(s[0:i]) + r, size := utf8.DecodeLastRune(s[0:i]) i -= size - for _, m := range chars { - if rune == m { + for _, ch := range chars { + if r == ch { return i } } @@ -256,13 +297,13 @@ func Fields(s []byte) [][]byte { // 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 // empty slice is returned. -func FieldsFunc(s []byte, f func(int) bool) [][]byte { +func FieldsFunc(s []byte, f func(rune) bool) [][]byte { n := 0 inField := false for i := 0; i < len(s); { - rune, size := utf8.DecodeRune(s[i:]) + r, size := utf8.DecodeRune(s[i:]) wasInField := inField - inField = !f(rune) + inField = !f(r) if inField && !wasInField { n++ } @@ -273,13 +314,13 @@ func FieldsFunc(s []byte, f func(int) bool) [][]byte { na := 0 fieldStart := -1 for i := 0; i <= len(s) && na < n; { - rune, size := utf8.DecodeRune(s[i:]) - if fieldStart < 0 && size > 0 && !f(rune) { + r, size := utf8.DecodeRune(s[i:]) + if fieldStart < 0 && size > 0 && !f(r) { fieldStart = i i += size continue } - if fieldStart >= 0 && (size == 0 || f(rune)) { + if fieldStart >= 0 && (size == 0 || f(r)) { a[na] = s[fieldStart:i] na++ fieldStart = -1 @@ -329,7 +370,7 @@ func HasSuffix(s, suffix []byte) bool { // 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(rune int) int, s []byte) []byte { +func Map(mapping func(r rune) rune, s []byte) []byte { // In the worst case, the array 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. @@ -338,20 +379,20 @@ func Map(mapping func(rune int) int, s []byte) []byte { b := make([]byte, maxbytes) for i := 0; i < len(s); { wid := 1 - rune := int(s[i]) - if rune >= utf8.RuneSelf { - rune, wid = utf8.DecodeRune(s[i:]) + r := rune(s[i]) + if r >= utf8.RuneSelf { + r, wid = utf8.DecodeRune(s[i:]) } - rune = mapping(rune) - if rune >= 0 { - if nbytes+utf8.RuneLen(rune) > maxbytes { + r = mapping(r) + if r >= 0 { + if nbytes+utf8.RuneLen(r) > maxbytes { // Grow the buffer. maxbytes = maxbytes*2 + utf8.UTFMax nb := make([]byte, maxbytes) copy(nb, b[0:nbytes]) b = nb } - nbytes += utf8.EncodeRune(b[nbytes:maxbytes], rune) + nbytes += utf8.EncodeRune(b[nbytes:maxbytes], r) } i += wid } @@ -383,44 +424,44 @@ 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 // upper case, giving priority to the special casing rules. func ToUpperSpecial(_case unicode.SpecialCase, s []byte) []byte { - return Map(func(r int) int { return _case.ToUpper(r) }, s) + 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 // lower case, giving priority to the special casing rules. func ToLowerSpecial(_case unicode.SpecialCase, s []byte) []byte { - return Map(func(r int) int { return _case.ToLower(r) }, s) + 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 // title case, giving priority to the special casing rules. func ToTitleSpecial(_case unicode.SpecialCase, s []byte) []byte { - return Map(func(r int) int { return _case.ToTitle(r) }, s) + return Map(func(r rune) rune { return _case.ToTitle(r) }, s) } // isSeparator reports whether the rune could mark a word boundary. // TODO: update when package unicode captures more of the properties. -func isSeparator(rune int) bool { +func isSeparator(r rune) bool { // ASCII alphanumerics and underscore are not separators - if rune <= 0x7F { + if r <= 0x7F { switch { - case '0' <= rune && rune <= '9': + case '0' <= r && r <= '9': return false - case 'a' <= rune && rune <= 'z': + case 'a' <= r && r <= 'z': return false - case 'A' <= rune && rune <= 'Z': + case 'A' <= r && r <= 'Z': return false - case rune == '_': + case r == '_': return false } return true } // Letters and digits are not separators - if unicode.IsLetter(rune) || unicode.IsDigit(rune) { + if unicode.IsLetter(r) || unicode.IsDigit(r) { return false } // Otherwise, all we can do for now is treat spaces as separators. - return unicode.IsSpace(rune) + return unicode.IsSpace(r) } // BUG(r): The rule Title uses for word boundaries does not handle Unicode punctuation properly. @@ -433,7 +474,7 @@ func Title(s []byte) []byte { // the closure once per rune. prev := ' ' return Map( - func(r int) int { + func(r rune) rune { if isSeparator(prev) { prev = r return unicode.ToTitle(r) @@ -446,7 +487,7 @@ func Title(s []byte) []byte { // TrimLeftFunc returns a subslice of s by slicing off all leading UTF-8-encoded // Unicode code points c that satisfy f(c). -func TrimLeftFunc(s []byte, f func(r int) bool) []byte { +func TrimLeftFunc(s []byte, f func(r rune) bool) []byte { i := indexFunc(s, f, false) if i == -1 { return nil @@ -456,7 +497,7 @@ func TrimLeftFunc(s []byte, f func(r int) bool) []byte { // TrimRightFunc returns a subslice of s by slicing off all trailing UTF-8 // encoded Unicode code points c that satisfy f(c). -func TrimRightFunc(s []byte, f func(r int) bool) []byte { +func TrimRightFunc(s []byte, f func(r rune) bool) []byte { i := lastIndexFunc(s, f, false) if i >= 0 && s[i] >= utf8.RuneSelf { _, wid := utf8.DecodeRune(s[i:]) @@ -469,36 +510,36 @@ func TrimRightFunc(s []byte, f func(r int) bool) []byte { // TrimFunc returns a subslice of s by slicing off all leading and trailing // UTF-8-encoded Unicode code points c that satisfy f(c). -func TrimFunc(s []byte, f func(r int) bool) []byte { +func TrimFunc(s []byte, f func(r rune) bool) []byte { return TrimRightFunc(TrimLeftFunc(s, f), f) } // 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. -func IndexFunc(s []byte, f func(r int) bool) int { +func IndexFunc(s []byte, f func(r rune) bool) int { return indexFunc(s, f, true) } // LastIndexFunc interprets s as a sequence of UTF-8-encoded Unicode code points. // It returns the byte index in s of the last Unicode // code point satisfying f(c), or -1 if none do. -func LastIndexFunc(s []byte, f func(r int) bool) int { +func LastIndexFunc(s []byte, f func(r rune) bool) int { return lastIndexFunc(s, f, true) } // indexFunc is the same as IndexFunc except that if // truth==false, the sense of the predicate function is // inverted. -func indexFunc(s []byte, f func(r int) bool, truth bool) int { +func indexFunc(s []byte, f func(r rune) bool, truth bool) int { start := 0 for start < len(s) { wid := 1 - rune := int(s[start]) - if rune >= utf8.RuneSelf { - rune, wid = utf8.DecodeRune(s[start:]) + r := rune(s[start]) + if r >= utf8.RuneSelf { + r, wid = utf8.DecodeRune(s[start:]) } - if f(rune) == truth { + if f(r) == truth { return start } start += wid @@ -509,21 +550,21 @@ func indexFunc(s []byte, f func(r int) bool, truth bool) int { // lastIndexFunc is the same as LastIndexFunc except that if // truth==false, the sense of the predicate function is // inverted. -func lastIndexFunc(s []byte, f func(r int) bool, truth bool) int { +func lastIndexFunc(s []byte, f func(r rune) bool, truth bool) int { for i := len(s); i > 0; { - rune, size := utf8.DecodeLastRune(s[0:i]) + r, size := utf8.DecodeLastRune(s[0:i]) i -= size - if f(rune) == truth { + if f(r) == truth { return i } } return -1 } -func makeCutsetFunc(cutset string) func(rune int) bool { - return func(rune int) bool { +func makeCutsetFunc(cutset string) func(r rune) bool { + return func(r rune) bool { for _, c := range cutset { - if c == rune { + if c == r { return true } } @@ -556,8 +597,8 @@ func TrimSpace(s []byte) []byte { } // Runes returns a slice of runes (Unicode code points) equivalent to s. -func Runes(s []byte) []int { - t := make([]int, utf8.RuneCount(s)) +func Runes(s []byte) []rune { + t := make([]rune, utf8.RuneCount(s)) i := 0 for len(s) > 0 { r, l := utf8.DecodeRune(s) @@ -572,13 +613,18 @@ func Runes(s []byte) []int { // non-overlapping instances of old replaced by new. // If n < 0, there is no limit on the number of replacements. func Replace(s, old, new []byte, n int) []byte { - if n == 0 { - return s // avoid allocation - } - // Compute number of replacements. - if m := Count(s, old); m == 0 { - return s // avoid allocation - } else if n <= 0 || m < n { + m := 0 + if n != 0 { + // Compute number of replacements. + m = Count(s, old) + } + if m == 0 { + // Nothing to do. Just copy. + t := make([]byte, len(s)) + copy(t, s) + return t + } + if n < 0 || m < n { n = m } @@ -603,3 +649,58 @@ func Replace(s, old, new []byte, n int) []byte { w += copy(t[w:], s[start:]) return t[0:w] } + +// EqualFold reports whether s and t, interpreted as UTF-8 strings, +// are equal under Unicode case-folding. +func EqualFold(s, t []byte) bool { + for len(s) != 0 && len(t) != 0 { + // Extract first rune from each. + var sr, tr rune + if s[0] < utf8.RuneSelf { + sr, s = rune(s[0]), s[1:] + } else { + r, size := utf8.DecodeRune(s) + sr, s = r, s[size:] + } + if t[0] < utf8.RuneSelf { + tr, t = rune(t[0]), t[1:] + } else { + r, size := utf8.DecodeRune(t) + tr, t = r, t[size:] + } + + // If they match, keep going; if not, return false. + + // Easy case. + if tr == sr { + continue + } + + // Make sr < tr to simplify what follows. + if tr < sr { + tr, sr = sr, tr + } + // Fast check for ASCII. + if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' { + // ASCII, and sr is upper case. tr must be lower case. + if tr == sr+'a'-'A' { + continue + } + return false + } + + // General case. SimpleFold(x) returns the next equivalent rune > x + // or wraps around to smaller values. + r := unicode.SimpleFold(sr) + for r != sr && r < tr { + r = unicode.SimpleFold(r) + } + if r == tr { + continue + } + return false + } + + // One string is empty. Are both? + return len(s) == len(t) +} diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go index 9444358a8..000f23517 100644 --- a/src/pkg/bytes/bytes_test.go +++ b/src/pkg/bytes/bytes_test.go @@ -9,7 +9,7 @@ import ( "reflect" "testing" "unicode" - "utf8" + "unicode/utf8" ) func eq(a, b []string) bool { @@ -46,31 +46,42 @@ type BinOpTest struct { i int } -var comparetests = []BinOpTest{ - {"", "", 0}, - {"a", "", 1}, - {"", "a", -1}, - {"abc", "abc", 0}, - {"ab", "abc", -1}, - {"abc", "ab", 1}, - {"x", "ab", 1}, - {"ab", "x", -1}, - {"x", "a", 1}, - {"b", "x", -1}, +var compareTests = []struct { + a, b []byte + i int +}{ + {[]byte(""), []byte(""), 0}, + {[]byte("a"), []byte(""), 1}, + {[]byte(""), []byte("a"), -1}, + {[]byte("abc"), []byte("abc"), 0}, + {[]byte("ab"), []byte("abc"), -1}, + {[]byte("abc"), []byte("ab"), 1}, + {[]byte("x"), []byte("ab"), 1}, + {[]byte("ab"), []byte("x"), -1}, + {[]byte("x"), []byte("a"), 1}, + {[]byte("b"), []byte("x"), -1}, + // nil tests + {nil, nil, 0}, + {[]byte(""), nil, 0}, + {nil, []byte(""), 0}, + {[]byte("a"), nil, 1}, + {nil, []byte("a"), -1}, } func TestCompare(t *testing.T) { - for _, tt := range comparetests { - a := []byte(tt.a) - b := []byte(tt.b) - cmp := Compare(a, b) - eql := Equal(a, b) + for _, tt := range compareTests { + cmp := Compare(tt.a, tt.b) if cmp != tt.i { t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp) } + eql := Equal(tt.a, tt.b) if eql != (tt.i == 0) { t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql) } + eql = EqualPortable(tt.a, tt.b) + if eql != (tt.i == 0) { + t.Errorf(`EqualPortable(%q, %q) = %v`, tt.a, tt.b, eql) + } } } @@ -264,41 +275,147 @@ func TestIndexRune(t *testing.T) { } } -func BenchmarkIndexByte4K(b *testing.B) { bmIndex(b, IndexByte, 4<<10) } +var bmbuf []byte + +func BenchmarkIndexByte32(b *testing.B) { bmIndexByte(b, IndexByte, 32) } +func BenchmarkIndexByte4K(b *testing.B) { bmIndexByte(b, IndexByte, 4<<10) } +func BenchmarkIndexByte4M(b *testing.B) { bmIndexByte(b, IndexByte, 4<<20) } +func BenchmarkIndexByte64M(b *testing.B) { bmIndexByte(b, IndexByte, 64<<20) } +func BenchmarkIndexBytePortable32(b *testing.B) { bmIndexByte(b, IndexBytePortable, 32) } +func BenchmarkIndexBytePortable4K(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<10) } +func BenchmarkIndexBytePortable4M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<20) } +func BenchmarkIndexBytePortable64M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 64<<20) } + +func bmIndexByte(b *testing.B, index func([]byte, byte) int, n int) { + if len(bmbuf) < n { + bmbuf = make([]byte, n) + } + b.SetBytes(int64(n)) + buf := bmbuf[0:n] + buf[n-1] = 'x' + for i := 0; i < b.N; i++ { + j := index(buf, 'x') + if j != n-1 { + b.Fatal("bad index", j) + } + } + buf[n-1] = '\x00' +} + +func BenchmarkEqual32(b *testing.B) { bmEqual(b, Equal, 32) } +func BenchmarkEqual4K(b *testing.B) { bmEqual(b, Equal, 4<<10) } +func BenchmarkEqual4M(b *testing.B) { bmEqual(b, Equal, 4<<20) } +func BenchmarkEqual64M(b *testing.B) { bmEqual(b, Equal, 64<<20) } +func BenchmarkEqualPort32(b *testing.B) { bmEqual(b, EqualPortable, 32) } +func BenchmarkEqualPort4K(b *testing.B) { bmEqual(b, EqualPortable, 4<<10) } +func BenchmarkEqualPortable4M(b *testing.B) { bmEqual(b, EqualPortable, 4<<20) } +func BenchmarkEqualPortable64M(b *testing.B) { bmEqual(b, EqualPortable, 64<<20) } -func BenchmarkIndexByte4M(b *testing.B) { bmIndex(b, IndexByte, 4<<20) } +func bmEqual(b *testing.B, equal func([]byte, []byte) bool, n int) { + if len(bmbuf) < 2*n { + bmbuf = make([]byte, 2*n) + } + b.SetBytes(int64(n)) + buf1 := bmbuf[0:n] + buf2 := bmbuf[n : 2*n] + buf1[n-1] = 'x' + buf2[n-1] = 'x' + for i := 0; i < b.N; i++ { + eq := equal(buf1, buf2) + if !eq { + b.Fatal("bad equal") + } + } + buf1[n-1] = '\x00' + buf2[n-1] = '\x00' +} -func BenchmarkIndexByte64M(b *testing.B) { bmIndex(b, IndexByte, 64<<20) } +func BenchmarkIndex32(b *testing.B) { bmIndex(b, Index, 32) } +func BenchmarkIndex4K(b *testing.B) { bmIndex(b, Index, 4<<10) } +func BenchmarkIndex4M(b *testing.B) { bmIndex(b, Index, 4<<20) } +func BenchmarkIndex64M(b *testing.B) { bmIndex(b, Index, 64<<20) } -func BenchmarkIndexBytePortable4K(b *testing.B) { - bmIndex(b, IndexBytePortable, 4<<10) +func bmIndex(b *testing.B, index func([]byte, []byte) int, n int) { + if len(bmbuf) < n { + bmbuf = make([]byte, n) + } + b.SetBytes(int64(n)) + buf := bmbuf[0:n] + buf[n-1] = 'x' + for i := 0; i < b.N; i++ { + j := index(buf, buf[n-7:]) + if j != n-7 { + b.Fatal("bad index", j) + } + } + buf[n-1] = '\x00' } -func BenchmarkIndexBytePortable4M(b *testing.B) { - bmIndex(b, IndexBytePortable, 4<<20) +func BenchmarkIndexEasy32(b *testing.B) { bmIndexEasy(b, Index, 32) } +func BenchmarkIndexEasy4K(b *testing.B) { bmIndexEasy(b, Index, 4<<10) } +func BenchmarkIndexEasy4M(b *testing.B) { bmIndexEasy(b, Index, 4<<20) } +func BenchmarkIndexEasy64M(b *testing.B) { bmIndexEasy(b, Index, 64<<20) } + +func bmIndexEasy(b *testing.B, index func([]byte, []byte) int, n int) { + if len(bmbuf) < n { + bmbuf = make([]byte, n) + } + b.SetBytes(int64(n)) + buf := bmbuf[0:n] + buf[n-1] = 'x' + buf[n-7] = 'x' + for i := 0; i < b.N; i++ { + j := index(buf, buf[n-7:]) + if j != n-7 { + b.Fatal("bad index", j) + } + } + buf[n-1] = '\x00' + buf[n-7] = '\x00' } -func BenchmarkIndexBytePortable64M(b *testing.B) { - bmIndex(b, IndexBytePortable, 64<<20) +func BenchmarkCount32(b *testing.B) { bmCount(b, Count, 32) } +func BenchmarkCount4K(b *testing.B) { bmCount(b, Count, 4<<10) } +func BenchmarkCount4M(b *testing.B) { bmCount(b, Count, 4<<20) } +func BenchmarkCount64M(b *testing.B) { bmCount(b, Count, 64<<20) } + +func bmCount(b *testing.B, count func([]byte, []byte) int, n int) { + if len(bmbuf) < n { + bmbuf = make([]byte, n) + } + b.SetBytes(int64(n)) + buf := bmbuf[0:n] + buf[n-1] = 'x' + for i := 0; i < b.N; i++ { + j := count(buf, buf[n-7:]) + if j != 1 { + b.Fatal("bad count", j) + } + } + buf[n-1] = '\x00' } -var bmbuf []byte +func BenchmarkCountEasy32(b *testing.B) { bmCountEasy(b, Count, 32) } +func BenchmarkCountEasy4K(b *testing.B) { bmCountEasy(b, Count, 4<<10) } +func BenchmarkCountEasy4M(b *testing.B) { bmCountEasy(b, Count, 4<<20) } +func BenchmarkCountEasy64M(b *testing.B) { bmCountEasy(b, Count, 64<<20) } -func bmIndex(b *testing.B, index func([]byte, byte) int, n int) { +func bmCountEasy(b *testing.B, count func([]byte, []byte) int, n int) { if len(bmbuf) < n { bmbuf = make([]byte, n) } b.SetBytes(int64(n)) buf := bmbuf[0:n] buf[n-1] = 'x' + buf[n-7] = 'x' for i := 0; i < b.N; i++ { - j := index(buf, 'x') - if j != n-1 { - println("bad index", j) - panic("bad index") + j := count(buf, buf[n-7:]) + if j != 1 { + b.Fatal("bad count", j) } } - buf[n-1] = '0' + buf[n-1] = '\x00' + buf[n-7] = '\x00' } type ExplodeTest struct { @@ -444,7 +561,7 @@ func TestFields(t *testing.T) { } func TestFieldsFunc(t *testing.T) { - pred := func(c int) bool { return c == 'X' } + pred := func(c rune) bool { return c == 'X' } var fieldsFuncTests = []FieldsTest{ {"", []string{}}, {"XX", []string{}}, @@ -514,24 +631,24 @@ func runStringTests(t *testing.T, f func([]byte) []byte, funcName string, testCa } } -func tenRunes(rune int) string { - r := make([]int, 10) - for i := range r { - r[i] = rune +func tenRunes(r rune) string { + runes := make([]rune, 10) + for i := range runes { + runes[i] = r } - return string(r) + return string(runes) } // User-defined self-inverse mapping function -func rot13(rune int) int { - step := 13 - if rune >= 'a' && rune <= 'z' { - return ((rune - 'a' + step) % 26) + 'a' +func rot13(r rune) rune { + const step = 13 + if r >= 'a' && r <= 'z' { + return ((r - 'a' + step) % 26) + 'a' } - if rune >= 'A' && rune <= 'Z' { - return ((rune - 'A' + step) % 26) + 'A' + if r >= 'A' && r <= 'Z' { + return ((r - 'A' + step) % 26) + 'A' } - return rune + return r } func TestMap(t *testing.T) { @@ -539,7 +656,7 @@ func TestMap(t *testing.T) { a := tenRunes('a') // 1. Grow. This triggers two reallocations in Map. - maxRune := func(rune int) int { return unicode.MaxRune } + maxRune := func(r rune) rune { return unicode.MaxRune } m := Map(maxRune, []byte(a)) expect := tenRunes(unicode.MaxRune) if string(m) != expect { @@ -547,7 +664,7 @@ func TestMap(t *testing.T) { } // 2. Shrink - minRune := func(rune int) int { return 'a' } + minRune := func(r rune) rune { return 'a' } m = Map(minRune, []byte(tenRunes(unicode.MaxRune))) expect = a if string(m) != expect { @@ -569,9 +686,9 @@ func TestMap(t *testing.T) { } // 5. Drop - dropNotLatin := func(rune int) int { - if unicode.Is(unicode.Latin, rune) { - return rune + dropNotLatin := func(r rune) rune { + if unicode.Is(unicode.Latin, r) { + return r } return -1 } @@ -615,7 +732,7 @@ func TestRepeat(t *testing.T) { } } -func runesEqual(a, b []int) bool { +func runesEqual(a, b []rune) bool { if len(a) != len(b) { return false } @@ -629,18 +746,18 @@ func runesEqual(a, b []int) bool { type RunesTest struct { in string - out []int + out []rune lossy bool } var RunesTests = []RunesTest{ - {"", []int{}, false}, - {" ", []int{32}, false}, - {"ABC", []int{65, 66, 67}, false}, - {"abc", []int{97, 98, 99}, false}, - {"\u65e5\u672c\u8a9e", []int{26085, 26412, 35486}, false}, - {"ab\x80c", []int{97, 98, 0xFFFD, 99}, true}, - {"ab\xc0c", []int{97, 98, 0xFFFD, 99}, true}, + {"", []rune{}, false}, + {" ", []rune{32}, false}, + {"ABC", []rune{65, 66, 67}, false}, + {"abc", []rune{97, 98, 99}, false}, + {"\u65e5\u672c\u8a9e", []rune{26085, 26412, 35486}, false}, + {"ab\x80c", []rune{97, 98, 0xFFFD, 99}, true}, + {"ab\xc0c", []rune{97, 98, 0xFFFD, 99}, true}, } func TestRunes(t *testing.T) { @@ -662,48 +779,49 @@ func TestRunes(t *testing.T) { } type TrimTest struct { - f func([]byte, string) []byte + f string in, cutset, out string } var trimTests = []TrimTest{ - {Trim, "abba", "a", "bb"}, - {Trim, "abba", "ab", ""}, - {TrimLeft, "abba", "ab", ""}, - {TrimRight, "abba", "ab", ""}, - {TrimLeft, "abba", "a", "bba"}, - {TrimRight, "abba", "a", "abb"}, - {Trim, "", "<>", "tag"}, - {Trim, "* listitem", " *", "listitem"}, - {Trim, `"quote"`, `"`, "quote"}, - {Trim, "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, + {"Trim", "abba", "a", "bb"}, + {"Trim", "abba", "ab", ""}, + {"TrimLeft", "abba", "ab", ""}, + {"TrimRight", "abba", "ab", ""}, + {"TrimLeft", "abba", "a", "bba"}, + {"TrimRight", "abba", "a", "abb"}, + {"Trim", "", "<>", "tag"}, + {"Trim", "* listitem", " *", "listitem"}, + {"Trim", `"quote"`, `"`, "quote"}, + {"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, //empty string tests - {Trim, "abba", "", "abba"}, - {Trim, "", "123", ""}, - {Trim, "", "", ""}, - {TrimLeft, "abba", "", "abba"}, - {TrimLeft, "", "123", ""}, - {TrimLeft, "", "", ""}, - {TrimRight, "abba", "", "abba"}, - {TrimRight, "", "123", ""}, - {TrimRight, "", "", ""}, - {TrimRight, "☺\xc0", "☺", "☺\xc0"}, + {"Trim", "abba", "", "abba"}, + {"Trim", "", "123", ""}, + {"Trim", "", "", ""}, + {"TrimLeft", "abba", "", "abba"}, + {"TrimLeft", "", "123", ""}, + {"TrimLeft", "", "", ""}, + {"TrimRight", "abba", "", "abba"}, + {"TrimRight", "", "123", ""}, + {"TrimRight", "", "", ""}, + {"TrimRight", "☺\xc0", "☺", "☺\xc0"}, } func TestTrim(t *testing.T) { for _, tc := range trimTests { - actual := string(tc.f([]byte(tc.in), tc.cutset)) - var name string - switch tc.f { - case Trim: - name = "Trim" - case TrimLeft: - name = "TrimLeft" - case TrimRight: - name = "TrimRight" + name := tc.f + var f func([]byte, string) []byte + switch name { + case "Trim": + f = Trim + case "TrimLeft": + f = TrimLeft + case "TrimRight": + f = TrimRight default: - t.Error("Undefined trim function") + t.Errorf("Undefined trim function %s", name) } + actual := string(f([]byte(tc.in), tc.cutset)) if actual != tc.out { t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out) } @@ -711,7 +829,7 @@ func TestTrim(t *testing.T) { } type predicate struct { - f func(r int) bool + f func(r rune) bool name string } @@ -719,7 +837,7 @@ var isSpace = predicate{unicode.IsSpace, "IsSpace"} var isDigit = predicate{unicode.IsDigit, "IsDigit"} var isUpper = predicate{unicode.IsUpper, "IsUpper"} var isValidRune = predicate{ - func(r int) bool { + func(r rune) bool { return r != utf8.RuneError }, "IsValidRune", @@ -732,7 +850,7 @@ type TrimFuncTest struct { func not(p predicate) predicate { return predicate{ - func(r int) bool { + func(r rune) bool { return !p.f(r) }, "not " + p.name, @@ -829,9 +947,15 @@ var ReplaceTests = []ReplaceTest{ func TestReplace(t *testing.T) { for _, tt := range ReplaceTests { - if s := string(Replace([]byte(tt.in), []byte(tt.old), []byte(tt.new), tt.n)); s != tt.out { + in := append([]byte(tt.in), ""...) + in = in[:len(tt.in)] + out := Replace(in, []byte(tt.old), []byte(tt.new), tt.n) + if s := string(out); s != tt.out { t.Errorf("Replace(%q, %q, %q, %d) = %q, want %q", tt.in, tt.old, tt.new, tt.n, s, tt.out) } + if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] { + t.Errorf("Replace(%q, %q, %q, %d) didn't copy", tt.in, tt.old, tt.new, tt.n) + } } } @@ -856,3 +980,31 @@ func TestTitle(t *testing.T) { } } } + +var EqualFoldTests = []struct { + s, t string + out bool +}{ + {"abc", "abc", true}, + {"ABcd", "ABcd", true}, + {"123abc", "123ABC", true}, + {"αβδ", "ΑΒΔ", true}, + {"abc", "xyz", false}, + {"abc", "XYZ", false}, + {"abcdefghijk", "abcdefghijX", false}, + {"abcdefghijk", "abcdefghij\u212A", true}, + {"abcdefghijK", "abcdefghij\u212A", true}, + {"abcdefghijkz", "abcdefghij\u212Ay", false}, + {"abcdefghijKz", "abcdefghij\u212Ay", false}, +} + +func TestEqualFold(t *testing.T) { + for _, tt := range EqualFoldTests { + if out := EqualFold([]byte(tt.s), []byte(tt.t)); out != tt.out { + t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.s, tt.t, out, tt.out) + } + if out := EqualFold([]byte(tt.t), []byte(tt.s)); out != tt.out { + t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.t, tt.s, out, tt.out) + } + } +} diff --git a/src/pkg/bytes/example_test.go b/src/pkg/bytes/example_test.go new file mode 100644 index 000000000..6fe8cd5a9 --- /dev/null +++ b/src/pkg/bytes/example_test.go @@ -0,0 +1,28 @@ +// 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 bytes_test + +import ( + . "bytes" + "encoding/base64" + "io" + "os" +) + +func ExampleBuffer() { + var b Buffer // A Buffer needs no initialization. + b.Write([]byte("Hello ")) + b.Write([]byte("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==") + dec := base64.NewDecoder(base64.StdEncoding, buf) + io.Copy(os.Stdout, dec) + // Output: Gophers rule! +} diff --git a/src/pkg/bytes/export_test.go b/src/pkg/bytes/export_test.go index b65428d9c..f61523e60 100644 --- a/src/pkg/bytes/export_test.go +++ b/src/pkg/bytes/export_test.go @@ -6,3 +6,4 @@ package bytes // Export func for testing var IndexBytePortable = indexBytePortable +var EqualPortable = equalPortable diff --git a/src/pkg/bytes/reader.go b/src/pkg/bytes/reader.go new file mode 100644 index 000000000..a062e54ba --- /dev/null +++ b/src/pkg/bytes/reader.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 bytes + +import ( + "errors" + "io" + "unicode/utf8" +) + +// A Reader implements the io.Reader, io.ReaderAt, 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. +type Reader struct { + s []byte + i int // current reading index + prevRune int // index of previous rune; or < 0 +} + +// Len returns the number of bytes of the unread portion of the +// slice. +func (r *Reader) Len() int { + if r.i >= len(r.s) { + return 0 + } + return len(r.s) - r.i +} + +func (r *Reader) Read(b []byte) (n int, err error) { + if len(b) == 0 { + return 0, nil + } + if r.i >= len(r.s) { + return 0, io.EOF + } + n = copy(b, r.s[r.i:]) + r.i += n + r.prevRune = -1 + return +} + +func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) { + if off < 0 { + return 0, errors.New("bytes: invalid offset") + } + if off >= int64(len(r.s)) { + return 0, io.EOF + } + n = copy(b, r.s[int(off):]) + if n < len(b) { + err = io.EOF + } + return +} + +func (r *Reader) ReadByte() (b byte, err error) { + if r.i >= len(r.s) { + return 0, io.EOF + } + b = r.s[r.i] + r.i++ + r.prevRune = -1 + return +} + +func (r *Reader) UnreadByte() error { + if r.i <= 0 { + return errors.New("bytes.Reader: at beginning of slice") + } + r.i-- + r.prevRune = -1 + return nil +} + +func (r *Reader) ReadRune() (ch rune, size int, err error) { + if r.i >= len(r.s) { + return 0, 0, io.EOF + } + r.prevRune = r.i + if c := r.s[r.i]; c < utf8.RuneSelf { + r.i++ + return rune(c), 1, nil + } + ch, size = utf8.DecodeRune(r.s[r.i:]) + r.i += size + return +} + +func (r *Reader) UnreadRune() error { + if r.prevRune < 0 { + return errors.New("bytes.Reader: previous operation was not ReadRune") + } + r.i = r.prevRune + r.prevRune = -1 + return nil +} + +// Seek implements the io.Seeker interface. +func (r *Reader) Seek(offset int64, whence int) (int64, error) { + var abs int64 + switch whence { + case 0: + abs = offset + case 1: + abs = int64(r.i) + offset + case 2: + abs = int64(len(r.s)) + offset + default: + return 0, errors.New("bytes: invalid whence") + } + if abs < 0 { + return 0, errors.New("bytes: negative position") + } + if abs >= 1<<31 { + return 0, errors.New("bytes: position out of range") + } + r.i = int(abs) + return abs, nil +} + +// 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 new file mode 100644 index 000000000..2e4b1f26e --- /dev/null +++ b/src/pkg/bytes/reader_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. + +package bytes_test + +import ( + . "bytes" + "fmt" + "io" + "os" + "testing" +) + +func TestReader(t *testing.T) { + r := NewReader([]byte("0123456789")) + tests := []struct { + off int64 + seek int + n int + want string + wantpos int64 + seekerr string + }{ + {seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"}, + {seek: os.SEEK_SET, off: 1, n: 1, want: "1"}, + {seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"}, + {seek: os.SEEK_SET, off: -1, seekerr: "bytes: negative position"}, + {seek: os.SEEK_SET, off: 1<<31 - 1}, + {seek: os.SEEK_CUR, off: 1, seekerr: "bytes: position out of range"}, + {seek: os.SEEK_SET, n: 5, want: "01234"}, + {seek: os.SEEK_CUR, n: 5, want: "56789"}, + {seek: os.SEEK_END, off: -1, n: 1, wantpos: 9, want: "9"}, + } + + for i, tt := range tests { + pos, err := r.Seek(tt.off, tt.seek) + if err == nil && tt.seekerr != "" { + t.Errorf("%d. want seek error %q", i, tt.seekerr) + continue + } + if err != nil && err.Error() != tt.seekerr { + t.Errorf("%d. seek error = %q; want %q", i, err.Error(), tt.seekerr) + continue + } + if tt.wantpos != 0 && tt.wantpos != pos { + t.Errorf("%d. pos = %d, want %d", i, pos, tt.wantpos) + } + buf := make([]byte, tt.n) + n, err := r.Read(buf) + if err != nil { + t.Errorf("%d. read = %v", i, err) + continue + } + got := string(buf[:n]) + if got != tt.want { + t.Errorf("%d. got %q; want %q", i, got, tt.want) + } + } +} + +func TestReaderAt(t *testing.T) { + r := NewReader([]byte("0123456789")) + tests := []struct { + off int64 + n int + want string + wanterr interface{} + }{ + {0, 10, "0123456789", nil}, + {1, 10, "123456789", io.EOF}, + {1, 9, "123456789", nil}, + {11, 10, "", io.EOF}, + {0, 0, "", nil}, + {-1, 0, "", "bytes: invalid offset"}, + } + for i, tt := range tests { + b := make([]byte, tt.n) + rn, err := r.ReadAt(b, tt.off) + got := string(b[:rn]) + if got != tt.want { + t.Errorf("%d. got %q; want %q", i, got, tt.want) + } + if fmt.Sprintf("%v", err) != fmt.Sprintf("%v", tt.wanterr) { + t.Errorf("%d. got error = %v; want %v", i, err, tt.wanterr) + } + } +} diff --git a/src/pkg/cmath/Makefile b/src/pkg/cmath/Makefile deleted file mode 100644 index 486caace4..000000000 --- a/src/pkg/cmath/Makefile +++ /dev/null @@ -1,25 +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. - -include ../../Make.inc - -TARG=cmath - -GOFILES=\ - abs.go\ - asin.go\ - conj.go\ - exp.go\ - isinf.go\ - isnan.go\ - log.go\ - phase.go\ - polar.go\ - pow.go\ - rect.go\ - sin.go\ - sqrt.go\ - tan.go\ - -include ../../Make.pkg diff --git a/src/pkg/cmath/abs.go b/src/pkg/cmath/abs.go deleted file mode 100644 index f3199cad5..000000000 --- a/src/pkg/cmath/abs.go +++ /dev/null @@ -1,12 +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. - -// Package cmath provides basic constants and mathematical functions for -// complex numbers. -package cmath - -import "math" - -// Abs returns the absolute value (also called the modulus) of x. -func Abs(x complex128) float64 { return math.Hypot(real(x), imag(x)) } diff --git a/src/pkg/cmath/asin.go b/src/pkg/cmath/asin.go deleted file mode 100644 index d6a3ca480..000000000 --- a/src/pkg/cmath/asin.go +++ /dev/null @@ -1,170 +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. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// 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 - -// Complex circular arc sine -// -// DESCRIPTION: -// -// Inverse complex sine: -// 2 -// w = -i clog( iz + csqrt( 1 - z ) ). -// -// casin(z) = -i casinh(iz) -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 10100 2.1e-15 3.4e-16 -// IEEE -10,+10 30000 2.2e-14 2.7e-15 -// Larger relative error can be observed for z near zero. -// Also tested by csin(casin(z)) = z. - -// Asin returns the inverse sine of x. -func Asin(x complex128) complex128 { - if imag(x) == 0 { - if math.Fabs(real(x)) > 1 { - return complex(math.Pi/2, 0) // DOMAIN error - } - return complex(math.Asin(real(x)), 0) - } - ct := complex(-imag(x), real(x)) // i * x - xx := x * x - x1 := complex(1-real(xx), -imag(xx)) // 1 - x*x - x2 := Sqrt(x1) // x2 = sqrt(1 - x*x) - w := Log(ct + x2) - return complex(imag(w), -real(w)) // -i * w -} - -// Asinh returns the inverse hyperbolic sine of x. -func Asinh(x complex128) complex128 { - // TODO check range - if imag(x) == 0 { - if math.Fabs(real(x)) > 1 { - return complex(math.Pi/2, 0) // DOMAIN error - } - return complex(math.Asinh(real(x)), 0) - } - xx := x * x - x1 := complex(1+real(xx), imag(xx)) // 1 + x*x - return Log(x + Sqrt(x1)) // log(x + sqrt(1 + x*x)) -} - -// Complex circular arc cosine -// -// DESCRIPTION: -// -// w = arccos z = PI/2 - arcsin z. -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 5200 1.6e-15 2.8e-16 -// IEEE -10,+10 30000 1.8e-14 2.2e-15 - -// Acos returns the inverse cosine of x. -func Acos(x complex128) complex128 { - w := Asin(x) - return complex(math.Pi/2-real(w), -imag(w)) -} - -// Acosh returns the inverse hyperbolic cosine of x. -func Acosh(x complex128) complex128 { - w := Acos(x) - if imag(w) <= 0 { - return complex(-imag(w), real(w)) // i * w - } - return complex(imag(w), -real(w)) // -i * w -} - -// Complex circular arc tangent -// -// DESCRIPTION: -// -// If -// z = x + iy, -// -// then -// 1 ( 2x ) -// Re w = - arctan(-----------) + k PI -// 2 ( 2 2) -// (1 - x - y ) -// -// ( 2 2) -// 1 (x + (y+1) ) -// Im w = - log(------------) -// 4 ( 2 2) -// (x + (y-1) ) -// -// Where k is an arbitrary integer. -// -// catan(z) = -i catanh(iz). -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 5900 1.3e-16 7.8e-18 -// IEEE -10,+10 30000 2.3e-15 8.5e-17 -// The check catan( ctan(z) ) = z, with |x| and |y| < PI/2, -// had peak relative error 1.5e-16, rms relative error -// 2.9e-17. See also clog(). - -// Atan returns the inverse tangent of x. -func Atan(x complex128) complex128 { - if real(x) == 0 && imag(x) > 1 { - return NaN() - } - - x2 := real(x) * real(x) - a := 1 - x2 - imag(x)*imag(x) - if a == 0 { - return NaN() - } - t := 0.5 * math.Atan2(2*real(x), a) - w := reducePi(t) - - t = imag(x) - 1 - b := x2 + t*t - if b == 0 { - return NaN() - } - t = imag(x) + 1 - c := (x2 + t*t) / b - return complex(w, 0.25*math.Log(c)) -} - -// Atanh returns the inverse hyperbolic tangent of x. -func Atanh(x complex128) complex128 { - z := complex(-imag(x), real(x)) // z = i * x - z = Atan(z) - return complex(imag(z), -real(z)) // z = -i * z -} diff --git a/src/pkg/cmath/cmath_test.go b/src/pkg/cmath/cmath_test.go deleted file mode 100644 index 6a595b0a6..000000000 --- a/src/pkg/cmath/cmath_test.go +++ /dev/null @@ -1,853 +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. - -package cmath - -import ( - "math" - "testing" -) - -var vc26 = []complex128{ - (4.97901192488367350108546816 + 7.73887247457810456552351752i), - (7.73887247457810456552351752 - 0.27688005719200159404635997i), - (-0.27688005719200159404635997 - 5.01060361827107492160848778i), - (-5.01060361827107492160848778 + 9.63629370719841737980004837i), - (9.63629370719841737980004837 + 2.92637723924396464525443662i), - (2.92637723924396464525443662 + 5.22908343145930665230025625i), - (5.22908343145930665230025625 + 2.72793991043601025126008608i), - (2.72793991043601025126008608 + 1.82530809168085506044576505i), - (1.82530809168085506044576505 - 8.68592476857560136238589621i), - (-8.68592476857560136238589621 + 4.97901192488367350108546816i), -} -var vc = []complex128{ - (4.9790119248836735e+00 + 7.7388724745781045e+00i), - (7.7388724745781045e+00 - 2.7688005719200159e-01i), - (-2.7688005719200159e-01 - 5.0106036182710749e+00i), - (-5.0106036182710749e+00 + 9.6362937071984173e+00i), - (9.6362937071984173e+00 + 2.9263772392439646e+00i), - (2.9263772392439646e+00 + 5.2290834314593066e+00i), - (5.2290834314593066e+00 + 2.7279399104360102e+00i), - (2.7279399104360102e+00 + 1.8253080916808550e+00i), - (1.8253080916808550e+00 - 8.6859247685756013e+00i), - (-8.6859247685756013e+00 + 4.9790119248836735e+00i), -} - -// The expected results below were computed by the high precision calculators -// at http://keisan.casio.com/. More exact input values (array vc[], above) -// were obtained by printing them with "%.26f". The answers were calculated -// to 26 digits (by using the "Digit number" drop-down control of each -// calculator). - -var abs = []float64{ - 9.2022120669932650313380972e+00, - 7.7438239742296106616261394e+00, - 5.0182478202557746902556648e+00, - 1.0861137372799545160704002e+01, - 1.0070841084922199607011905e+01, - 5.9922447613166942183705192e+00, - 5.8978784056736762299945176e+00, - 3.2822866700678709020367184e+00, - 8.8756430028990417290744307e+00, - 1.0011785496777731986390856e+01, -} - -var acos = []complex128{ - (1.0017679804707456328694569 - 2.9138232718554953784519807i), - (0.03606427612041407369636057 + 2.7358584434576260925091256i), - (1.6249365462333796703711823 + 2.3159537454335901187730929i), - (2.0485650849650740120660391 - 3.0795576791204117911123886i), - (0.29621132089073067282488147 - 3.0007392508200622519398814i), - (1.0664555914934156601503632 - 2.4872865024796011364747111i), - (0.48681307452231387690013905 - 2.463655912283054555225301i), - (0.6116977071277574248407752 - 1.8734458851737055262693056i), - (1.3649311280370181331184214 + 2.8793528632328795424123832i), - (2.6189310485682988308904501 - 2.9956543302898767795858704i), -} -var acosh = []complex128{ - (2.9138232718554953784519807 + 1.0017679804707456328694569i), - (2.7358584434576260925091256 - 0.03606427612041407369636057i), - (2.3159537454335901187730929 - 1.6249365462333796703711823i), - (3.0795576791204117911123886 + 2.0485650849650740120660391i), - (3.0007392508200622519398814 + 0.29621132089073067282488147i), - (2.4872865024796011364747111 + 1.0664555914934156601503632i), - (2.463655912283054555225301 + 0.48681307452231387690013905i), - (1.8734458851737055262693056 + 0.6116977071277574248407752i), - (2.8793528632328795424123832 - 1.3649311280370181331184214i), - (2.9956543302898767795858704 + 2.6189310485682988308904501i), -} -var asin = []complex128{ - (0.56902834632415098636186476 + 2.9138232718554953784519807i), - (1.5347320506744825455349611 - 2.7358584434576260925091256i), - (-0.054140219438483051139860579 - 2.3159537454335901187730929i), - (-0.47776875817017739283471738 + 3.0795576791204117911123886i), - (1.2745850059041659464064402 + 3.0007392508200622519398814i), - (0.50434073530148095908095852 + 2.4872865024796011364747111i), - (1.0839832522725827423311826 + 2.463655912283054555225301i), - (0.9590986196671391943905465 + 1.8734458851737055262693056i), - (0.20586519875787848611290031 - 2.8793528632328795424123832i), - (-1.0481347217734022116591284 + 2.9956543302898767795858704i), -} -var asinh = []complex128{ - (2.9113760469415295679342185 + 0.99639459545704326759805893i), - (2.7441755423994259061579029 - 0.035468308789000500601119392i), - (-2.2962136462520690506126678 - 1.5144663565690151885726707i), - (-3.0771233459295725965402455 + 1.0895577967194013849422294i), - (3.0048366100923647417557027 + 0.29346979169819220036454168i), - (2.4800059370795363157364643 + 1.0545868606049165710424232i), - (2.4718773838309585611141821 + 0.47502344364250803363708842i), - (1.8910743588080159144378396 + 0.56882925572563602341139174i), - (2.8735426423367341878069406 - 1.362376149648891420997548i), - (-2.9981750586172477217567878 + 0.5183571985225367505624207i), -} -var atan = []complex128{ - (1.5115747079332741358607654 + 0.091324403603954494382276776i), - (1.4424504323482602560806727 - 0.0045416132642803911503770933i), - (-1.5593488703630532674484026 - 0.20163295409248362456446431i), - (-1.5280619472445889867794105 + 0.081721556230672003746956324i), - (1.4759909163240799678221039 + 0.028602969320691644358773586i), - (1.4877353772046548932715555 + 0.14566877153207281663773599i), - (1.4206983927779191889826 + 0.076830486127880702249439993i), - (1.3162236060498933364869556 + 0.16031313000467530644933363i), - (1.5473450684303703578810093 - 0.11064907507939082484935782i), - (-1.4841462340185253987375812 + 0.049341850305024399493142411i), -} -var atanh = []complex128{ - (0.058375027938968509064640438 + 1.4793488495105334458167782i), - (0.12977343497790381229915667 - 1.5661009410463561327262499i), - (-0.010576456067347252072200088 - 1.3743698658402284549750563i), - (-0.042218595678688358882784918 + 1.4891433968166405606692604i), - (0.095218997991316722061828397 + 1.5416884098777110330499698i), - (0.079965459366890323857556487 + 1.4252510353873192700350435i), - (0.15051245471980726221708301 + 1.4907432533016303804884461i), - (0.25082072933993987714470373 + 1.392057665392187516442986i), - (0.022896108815797135846276662 - 1.4609224989282864208963021i), - (-0.08665624101841876130537396 + 1.5207902036935093480142159i), -} -var conj = []complex128{ - (4.9790119248836735e+00 - 7.7388724745781045e+00i), - (7.7388724745781045e+00 + 2.7688005719200159e-01i), - (-2.7688005719200159e-01 + 5.0106036182710749e+00i), - (-5.0106036182710749e+00 - 9.6362937071984173e+00i), - (9.6362937071984173e+00 - 2.9263772392439646e+00i), - (2.9263772392439646e+00 - 5.2290834314593066e+00i), - (5.2290834314593066e+00 - 2.7279399104360102e+00i), - (2.7279399104360102e+00 - 1.8253080916808550e+00i), - (1.8253080916808550e+00 + 8.6859247685756013e+00i), - (-8.6859247685756013e+00 - 4.9790119248836735e+00i), -} -var cos = []complex128{ - (3.024540920601483938336569e+02 + 1.1073797572517071650045357e+03i), - (1.192858682649064973252758e-01 + 2.7857554122333065540970207e-01i), - (7.2144394304528306603857962e+01 - 2.0500129667076044169954205e+01i), - (2.24921952538403984190541e+03 - 7.317363745602773587049329e+03i), - (-9.148222970032421760015498e+00 + 1.953124661113563541862227e+00i), - (-9.116081175857732248227078e+01 - 1.992669213569952232487371e+01i), - (3.795639179042704640002918e+00 + 6.623513350981458399309662e+00i), - (-2.9144840732498869560679084e+00 - 1.214620271628002917638748e+00i), - (-7.45123482501299743872481e+02 + 2.8641692314488080814066734e+03i), - (-5.371977967039319076416747e+01 + 4.893348341339375830564624e+01i), -} -var cosh = []complex128{ - (8.34638383523018249366948e+00 + 7.2181057886425846415112064e+01i), - (1.10421967379919366952251e+03 - 3.1379638689277575379469861e+02i), - (3.051485206773701584738512e-01 - 2.6805384730105297848044485e-01i), - (-7.33294728684187933370938e+01 + 1.574445942284918251038144e+01i), - (-7.478643293945957535757355e+03 + 1.6348382209913353929473321e+03i), - (4.622316522966235701630926e+00 - 8.088695185566375256093098e+00i), - (-8.544333183278877406197712e+01 + 3.7505836120128166455231717e+01i), - (-1.934457815021493925115198e+00 + 7.3725859611767228178358673e+00i), - (-2.352958770061749348353548e+00 - 2.034982010440878358915409e+00i), - (7.79756457532134748165069e+02 + 2.8549350716819176560377717e+03i), -} -var exp = []complex128{ - (1.669197736864670815125146e+01 + 1.4436895109507663689174096e+02i), - (2.2084389286252583447276212e+03 - 6.2759289284909211238261917e+02i), - (2.227538273122775173434327e-01 + 7.2468284028334191250470034e-01i), - (-6.5182985958153548997881627e-03 - 1.39965837915193860879044e-03i), - (-1.4957286524084015746110777e+04 + 3.269676455931135688988042e+03i), - (9.218158701983105935659273e+00 - 1.6223985291084956009304582e+01i), - (-1.7088175716853040841444505e+02 + 7.501382609870410713795546e+01i), - (-3.852461315830959613132505e+00 + 1.4808420423156073221970892e+01i), - (-4.586775503301407379786695e+00 - 4.178501081246873415144744e+00i), - (4.451337963005453491095747e-05 - 1.62977574205442915935263e-04i), -} -var log = []complex128{ - (2.2194438972179194425697051e+00 + 9.9909115046919291062461269e-01i), - (2.0468956191154167256337289e+00 - 3.5762575021856971295156489e-02i), - (1.6130808329853860438751244e+00 - 1.6259990074019058442232221e+00i), - (2.3851910394823008710032651e+00 + 2.0502936359659111755031062e+00i), - (2.3096442270679923004800651e+00 + 2.9483213155446756211881774e-01i), - (1.7904660933974656106951860e+00 + 1.0605860367252556281902109e+00i), - (1.7745926939841751666177512e+00 + 4.8084556083358307819310911e-01i), - (1.1885403350045342425648780e+00 + 5.8969634164776659423195222e-01i), - (2.1833107837679082586772505e+00 - 1.3636647724582455028314573e+00i), - (2.3037629487273259170991671e+00 + 2.6210913895386013290915234e+00i), -} -var log10 = []complex128{ - (9.6389223745559042474184943e-01 + 4.338997735671419492599631e-01i), - (8.8895547241376579493490892e-01 - 1.5531488990643548254864806e-02i), - (7.0055210462945412305244578e-01 - 7.0616239649481243222248404e-01i), - (1.0358753067322445311676952e+00 + 8.9043121238134980156490909e-01i), - (1.003065742975330237172029e+00 + 1.2804396782187887479857811e-01i), - (7.7758954439739162532085157e-01 + 4.6060666333341810869055108e-01i), - (7.7069581462315327037689152e-01 + 2.0882857371769952195512475e-01i), - (5.1617650901191156135137239e-01 + 2.5610186717615977620363299e-01i), - (9.4819982567026639742663212e-01 - 5.9223208584446952284914289e-01i), - (1.0005115362454417135973429e+00 + 1.1383255270407412817250921e+00i), -} - -type ff struct { - r, theta float64 -} - -var polar = []ff{ - {9.2022120669932650313380972e+00, 9.9909115046919291062461269e-01}, - {7.7438239742296106616261394e+00, -3.5762575021856971295156489e-02}, - {5.0182478202557746902556648e+00, -1.6259990074019058442232221e+00}, - {1.0861137372799545160704002e+01, 2.0502936359659111755031062e+00}, - {1.0070841084922199607011905e+01, 2.9483213155446756211881774e-01}, - {5.9922447613166942183705192e+00, 1.0605860367252556281902109e+00}, - {5.8978784056736762299945176e+00, 4.8084556083358307819310911e-01}, - {3.2822866700678709020367184e+00, 5.8969634164776659423195222e-01}, - {8.8756430028990417290744307e+00, -1.3636647724582455028314573e+00}, - {1.0011785496777731986390856e+01, 2.6210913895386013290915234e+00}, -} -var pow = []complex128{ - (-2.499956739197529585028819e+00 + 1.759751724335650228957144e+00i), - (7.357094338218116311191939e+04 - 5.089973412479151648145882e+04i), - (1.320777296067768517259592e+01 - 3.165621914333901498921986e+01i), - (-3.123287828297300934072149e-07 - 1.9849567521490553032502223E-7i), - (8.0622651468477229614813e+04 - 7.80028727944573092944363e+04i), - (-1.0268824572103165858577141e+00 - 4.716844738244989776610672e-01i), - (-4.35953819012244175753187e+01 + 2.2036445974645306917648585e+02i), - (8.3556092283250594950239e-01 - 1.2261571947167240272593282e+01i), - (1.582292972120769306069625e+03 + 1.273564263524278244782512e+04i), - (6.592208301642122149025369e-08 + 2.584887236651661903526389e-08i), -} -var sin = []complex128{ - (-1.1073801774240233539648544e+03 + 3.024539773002502192425231e+02i), - (1.0317037521400759359744682e+00 - 3.2208979799929570242818e-02i), - (-2.0501952097271429804261058e+01 - 7.2137981348240798841800967e+01i), - (7.3173638080346338642193078e+03 + 2.249219506193664342566248e+03i), - (-1.964375633631808177565226e+00 - 9.0958264713870404464159683e+00i), - (1.992783647158514838337674e+01 - 9.11555769410191350416942e+01i), - (-6.680335650741921444300349e+00 + 3.763353833142432513086117e+00i), - (1.2794028166657459148245993e+00 - 2.7669092099795781155109602e+00i), - (2.8641693949535259594188879e+03 + 7.451234399649871202841615e+02i), - (-4.893811726244659135553033e+01 - 5.371469305562194635957655e+01i), -} -var sinh = []complex128{ - (8.34559353341652565758198e+00 + 7.2187893208650790476628899e+01i), - (1.1042192548260646752051112e+03 - 3.1379650595631635858792056e+02i), - (-8.239469336509264113041849e-02 + 9.9273668758439489098514519e-01i), - (7.332295456982297798219401e+01 - 1.574585908122833444899023e+01i), - (-7.4786432301380582103534216e+03 + 1.63483823493980029604071e+03i), - (4.595842179016870234028347e+00 - 8.135290105518580753211484e+00i), - (-8.543842533574163435246793e+01 + 3.750798997857594068272375e+01i), - (-1.918003500809465688017307e+00 + 7.4358344619793504041350251e+00i), - (-2.233816733239658031433147e+00 - 2.143519070805995056229335e+00i), - (-7.797564130187551181105341e+02 - 2.8549352346594918614806877e+03i), -} -var sqrt = []complex128{ - (2.6628203086086130543813948e+00 + 1.4531345674282185229796902e+00i), - (2.7823278427251986247149295e+00 - 4.9756907317005224529115567e-02i), - (1.5397025302089642757361015e+00 - 1.6271336573016637535695727e+00i), - (1.7103411581506875260277898e+00 + 2.8170677122737589676157029e+00i), - (3.1390392472953103383607947e+00 + 4.6612625849858653248980849e-01i), - (2.1117080764822417640789287e+00 + 1.2381170223514273234967850e+00i), - (2.3587032281672256703926939e+00 + 5.7827111903257349935720172e-01i), - (1.7335262588873410476661577e+00 + 5.2647258220721269141550382e-01i), - (2.3131094974708716531499282e+00 - 1.8775429304303785570775490e+00i), - (8.1420535745048086240947359e-01 + 3.0575897587277248522656113e+00i), -} -var tan = []complex128{ - (-1.928757919086441129134525e-07 + 1.0000003267499169073251826e+00i), - (1.242412685364183792138948e+00 - 3.17149693883133370106696e+00i), - (-4.6745126251587795225571826e-05 - 9.9992439225263959286114298e-01i), - (4.792363401193648192887116e-09 + 1.0000000070589333451557723e+00i), - (2.345740824080089140287315e-03 + 9.947733046570988661022763e-01i), - (-2.396030789494815566088809e-05 + 9.9994781345418591429826779e-01i), - (-7.370204836644931340905303e-03 + 1.0043553413417138987717748e+00i), - (-3.691803847992048527007457e-02 + 9.6475071993469548066328894e-01i), - (-2.781955256713729368401878e-08 - 1.000000049848910609006646e+00i), - (9.4281590064030478879791249e-05 + 9.9999119340863718183758545e-01i), -} -var tanh = []complex128{ - (1.0000921981225144748819918e+00 + 2.160986245871518020231507e-05i), - (9.9999967727531993209562591e-01 - 1.9953763222959658873657676e-07i), - (-1.765485739548037260789686e+00 + 1.7024216325552852445168471e+00i), - (-9.999189442732736452807108e-01 + 3.64906070494473701938098e-05i), - (9.9999999224622333738729767e-01 - 3.560088949517914774813046e-09i), - (1.0029324933367326862499343e+00 - 4.948790309797102353137528e-03i), - (9.9996113064788012488693567e-01 - 4.226995742097032481451259e-05i), - (1.0074784189316340029873945e+00 - 4.194050814891697808029407e-03i), - (9.9385534229718327109131502e-01 + 5.144217985914355502713437e-02i), - (-1.0000000491604982429364892e+00 - 2.901873195374433112227349e-08i), -} - -// special cases -var vcAbsSC = []complex128{ - NaN(), -} -var absSC = []float64{ - math.NaN(), -} -var vcAcosSC = []complex128{ - NaN(), -} -var acosSC = []complex128{ - NaN(), -} -var vcAcoshSC = []complex128{ - NaN(), -} -var acoshSC = []complex128{ - NaN(), -} -var vcAsinSC = []complex128{ - NaN(), -} -var asinSC = []complex128{ - NaN(), -} -var vcAsinhSC = []complex128{ - NaN(), -} -var asinhSC = []complex128{ - NaN(), -} -var vcAtanSC = []complex128{ - NaN(), -} -var atanSC = []complex128{ - NaN(), -} -var vcAtanhSC = []complex128{ - NaN(), -} -var atanhSC = []complex128{ - NaN(), -} -var vcConjSC = []complex128{ - NaN(), -} -var conjSC = []complex128{ - NaN(), -} -var vcCosSC = []complex128{ - NaN(), -} -var cosSC = []complex128{ - NaN(), -} -var vcCoshSC = []complex128{ - NaN(), -} -var coshSC = []complex128{ - NaN(), -} -var vcExpSC = []complex128{ - NaN(), -} -var expSC = []complex128{ - NaN(), -} -var vcIsNaNSC = []complex128{ - complex(math.Inf(-1), math.Inf(-1)), - complex(math.Inf(-1), math.NaN()), - complex(math.NaN(), math.Inf(-1)), - complex(0, math.NaN()), - complex(math.NaN(), 0), - complex(math.Inf(1), math.Inf(1)), - complex(math.Inf(1), math.NaN()), - complex(math.NaN(), math.Inf(1)), - complex(math.NaN(), math.NaN()), -} -var isNaNSC = []bool{ - false, - false, - false, - true, - true, - false, - false, - false, - true, -} -var vcLogSC = []complex128{ - NaN(), -} -var logSC = []complex128{ - NaN(), -} -var vcLog10SC = []complex128{ - NaN(), -} -var log10SC = []complex128{ - NaN(), -} -var vcPolarSC = []complex128{ - NaN(), -} -var polarSC = []ff{ - {math.NaN(), math.NaN()}, -} -var vcPowSC = [][2]complex128{ - {NaN(), NaN()}, -} -var powSC = []complex128{ - NaN(), -} -var vcSinSC = []complex128{ - NaN(), -} -var sinSC = []complex128{ - NaN(), -} -var vcSinhSC = []complex128{ - NaN(), -} -var sinhSC = []complex128{ - NaN(), -} -var vcSqrtSC = []complex128{ - NaN(), -} -var sqrtSC = []complex128{ - NaN(), -} -var vcTanSC = []complex128{ - NaN(), -} -var tanSC = []complex128{ - NaN(), -} -var vcTanhSC = []complex128{ - NaN(), -} -var tanhSC = []complex128{ - NaN(), -} - -// functions borrowed from pkg/math/all_test.go -func tolerance(a, b, e float64) bool { - d := a - b - if d < 0 { - d = -d - } - - if a != 0 { - e = e * a - if e < 0 { - e = -e - } - } - return d < e -} -func soclose(a, b, e float64) bool { return tolerance(a, b, e) } -func veryclose(a, b float64) bool { return tolerance(a, b, 4e-16) } -func alike(a, b float64) bool { - switch { - case a != a && b != b: // math.IsNaN(a) && math.IsNaN(b): - return true - case a == b: - return math.Signbit(a) == math.Signbit(b) - } - return false -} - -func cTolerance(a, b complex128, e float64) bool { - d := Abs(a - b) - if a != 0 { - e = e * Abs(a) - if e < 0 { - e = -e - } - } - return d < e -} -func cSoclose(a, b complex128, e float64) bool { return cTolerance(a, b, e) } -func cVeryclose(a, b complex128) bool { return cTolerance(a, b, 4e-16) } -func cAlike(a, b complex128) bool { - switch { - case IsNaN(a) && IsNaN(b): - return true - case a == b: - return math.Signbit(real(a)) == math.Signbit(real(b)) && math.Signbit(imag(a)) == math.Signbit(imag(b)) - } - return false -} - -func TestAbs(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Abs(vc[i]); !veryclose(abs[i], f) { - t.Errorf("Abs(%g) = %g, want %g", vc[i], f, abs[i]) - } - } - for i := 0; i < len(vcAbsSC); i++ { - if f := Abs(vcAbsSC[i]); !alike(absSC[i], f) { - t.Errorf("Abs(%g) = %g, want %g", vcAbsSC[i], f, absSC[i]) - } - } -} -func TestAcos(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Acos(vc[i]); !cSoclose(acos[i], f, 1e-14) { - t.Errorf("Acos(%g) = %g, want %g", vc[i], f, acos[i]) - } - } - for i := 0; i < len(vcAcosSC); i++ { - if f := Acos(vcAcosSC[i]); !cAlike(acosSC[i], f) { - t.Errorf("Acos(%g) = %g, want %g", vcAcosSC[i], f, acosSC[i]) - } - } -} -func TestAcosh(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Acosh(vc[i]); !cSoclose(acosh[i], f, 1e-14) { - t.Errorf("Acosh(%g) = %g, want %g", vc[i], f, acosh[i]) - } - } - for i := 0; i < len(vcAcoshSC); i++ { - if f := Acosh(vcAcoshSC[i]); !cAlike(acoshSC[i], f) { - t.Errorf("Acosh(%g) = %g, want %g", vcAcoshSC[i], f, acoshSC[i]) - } - } -} -func TestAsin(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Asin(vc[i]); !cSoclose(asin[i], f, 1e-14) { - t.Errorf("Asin(%g) = %g, want %g", vc[i], f, asin[i]) - } - } - for i := 0; i < len(vcAsinSC); i++ { - if f := Asin(vcAsinSC[i]); !cAlike(asinSC[i], f) { - t.Errorf("Asin(%g) = %g, want %g", vcAsinSC[i], f, asinSC[i]) - } - } -} -func TestAsinh(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Asinh(vc[i]); !cSoclose(asinh[i], f, 4e-15) { - t.Errorf("Asinh(%g) = %g, want %g", vc[i], f, asinh[i]) - } - } - for i := 0; i < len(vcAsinhSC); i++ { - if f := Asinh(vcAsinhSC[i]); !cAlike(asinhSC[i], f) { - t.Errorf("Asinh(%g) = %g, want %g", vcAsinhSC[i], f, asinhSC[i]) - } - } -} -func TestAtan(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Atan(vc[i]); !cVeryclose(atan[i], f) { - t.Errorf("Atan(%g) = %g, want %g", vc[i], f, atan[i]) - } - } - for i := 0; i < len(vcAtanSC); i++ { - if f := Atan(vcAtanSC[i]); !cAlike(atanSC[i], f) { - t.Errorf("Atan(%g) = %g, want %g", vcAtanSC[i], f, atanSC[i]) - } - } -} -func TestAtanh(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Atanh(vc[i]); !cVeryclose(atanh[i], f) { - t.Errorf("Atanh(%g) = %g, want %g", vc[i], f, atanh[i]) - } - } - for i := 0; i < len(vcAtanhSC); i++ { - if f := Atanh(vcAtanhSC[i]); !cAlike(atanhSC[i], f) { - t.Errorf("Atanh(%g) = %g, want %g", vcAtanhSC[i], f, atanhSC[i]) - } - } -} -func TestConj(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Conj(vc[i]); !cVeryclose(conj[i], f) { - t.Errorf("Conj(%g) = %g, want %g", vc[i], f, conj[i]) - } - } - for i := 0; i < len(vcConjSC); i++ { - if f := Conj(vcConjSC[i]); !cAlike(conjSC[i], f) { - t.Errorf("Conj(%g) = %g, want %g", vcConjSC[i], f, conjSC[i]) - } - } -} -func TestCos(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Cos(vc[i]); !cSoclose(cos[i], f, 3e-15) { - t.Errorf("Cos(%g) = %g, want %g", vc[i], f, cos[i]) - } - } - for i := 0; i < len(vcCosSC); i++ { - if f := Cos(vcCosSC[i]); !cAlike(cosSC[i], f) { - t.Errorf("Cos(%g) = %g, want %g", vcCosSC[i], f, cosSC[i]) - } - } -} -func TestCosh(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Cosh(vc[i]); !cSoclose(cosh[i], f, 2e-15) { - t.Errorf("Cosh(%g) = %g, want %g", vc[i], f, cosh[i]) - } - } - for i := 0; i < len(vcCoshSC); i++ { - if f := Cosh(vcCoshSC[i]); !cAlike(coshSC[i], f) { - t.Errorf("Cosh(%g) = %g, want %g", vcCoshSC[i], f, coshSC[i]) - } - } -} -func TestExp(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Exp(vc[i]); !cSoclose(exp[i], f, 1e-15) { - t.Errorf("Exp(%g) = %g, want %g", vc[i], f, exp[i]) - } - } - for i := 0; i < len(vcExpSC); i++ { - if f := Exp(vcExpSC[i]); !cAlike(expSC[i], f) { - t.Errorf("Exp(%g) = %g, want %g", vcExpSC[i], f, expSC[i]) - } - } -} -func TestIsNaN(t *testing.T) { - for i := 0; i < len(vcIsNaNSC); i++ { - if f := IsNaN(vcIsNaNSC[i]); isNaNSC[i] != f { - t.Errorf("IsNaN(%v) = %v, want %v", vcIsNaNSC[i], f, isNaNSC[i]) - } - } -} -func TestLog(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Log(vc[i]); !cVeryclose(log[i], f) { - t.Errorf("Log(%g) = %g, want %g", vc[i], f, log[i]) - } - } - for i := 0; i < len(vcLogSC); i++ { - if f := Log(vcLogSC[i]); !cAlike(logSC[i], f) { - t.Errorf("Log(%g) = %g, want %g", vcLogSC[i], f, logSC[i]) - } - } -} -func TestLog10(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Log10(vc[i]); !cVeryclose(log10[i], f) { - t.Errorf("Log10(%g) = %g, want %g", vc[i], f, log10[i]) - } - } - for i := 0; i < len(vcLog10SC); i++ { - if f := Log10(vcLog10SC[i]); !cAlike(log10SC[i], f) { - t.Errorf("Log10(%g) = %g, want %g", vcLog10SC[i], f, log10SC[i]) - } - } -} -func TestPolar(t *testing.T) { - for i := 0; i < len(vc); i++ { - if r, theta := Polar(vc[i]); !veryclose(polar[i].r, r) && !veryclose(polar[i].theta, theta) { - t.Errorf("Polar(%g) = %g, %g want %g, %g", vc[i], r, theta, polar[i].r, polar[i].theta) - } - } - for i := 0; i < len(vcPolarSC); i++ { - if r, theta := Polar(vcPolarSC[i]); !alike(polarSC[i].r, r) && !alike(polarSC[i].theta, theta) { - t.Errorf("Polar(%g) = %g, %g, want %g, %g", vcPolarSC[i], r, theta, polarSC[i].r, polarSC[i].theta) - } - } -} -func TestPow(t *testing.T) { - var a = complex(3.0, 3.0) - for i := 0; i < len(vc); i++ { - if f := Pow(a, vc[i]); !cSoclose(pow[i], f, 4e-15) { - t.Errorf("Pow(%g, %g) = %g, want %g", a, vc[i], f, pow[i]) - } - } - for i := 0; i < len(vcPowSC); i++ { - if f := Pow(vcPowSC[i][0], vcPowSC[i][0]); !cAlike(powSC[i], f) { - t.Errorf("Pow(%g, %g) = %g, want %g", vcPowSC[i][0], vcPowSC[i][0], f, powSC[i]) - } - } -} -func TestRect(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Rect(polar[i].r, polar[i].theta); !cVeryclose(vc[i], f) { - t.Errorf("Rect(%g, %g) = %g want %g", polar[i].r, polar[i].theta, f, vc[i]) - } - } - for i := 0; i < len(vcPolarSC); i++ { - if f := Rect(polarSC[i].r, polarSC[i].theta); !cAlike(vcPolarSC[i], f) { - t.Errorf("Rect(%g, %g) = %g, want %g", polarSC[i].r, polarSC[i].theta, f, vcPolarSC[i]) - } - } -} -func TestSin(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Sin(vc[i]); !cSoclose(sin[i], f, 2e-15) { - t.Errorf("Sin(%g) = %g, want %g", vc[i], f, sin[i]) - } - } - for i := 0; i < len(vcSinSC); i++ { - if f := Sin(vcSinSC[i]); !cAlike(sinSC[i], f) { - t.Errorf("Sin(%g) = %g, want %g", vcSinSC[i], f, sinSC[i]) - } - } -} -func TestSinh(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Sinh(vc[i]); !cSoclose(sinh[i], f, 2e-15) { - t.Errorf("Sinh(%g) = %g, want %g", vc[i], f, sinh[i]) - } - } - for i := 0; i < len(vcSinhSC); i++ { - if f := Sinh(vcSinhSC[i]); !cAlike(sinhSC[i], f) { - t.Errorf("Sinh(%g) = %g, want %g", vcSinhSC[i], f, sinhSC[i]) - } - } -} -func TestSqrt(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Sqrt(vc[i]); !cVeryclose(sqrt[i], f) { - t.Errorf("Sqrt(%g) = %g, want %g", vc[i], f, sqrt[i]) - } - } - for i := 0; i < len(vcSqrtSC); i++ { - if f := Sqrt(vcSqrtSC[i]); !cAlike(sqrtSC[i], f) { - t.Errorf("Sqrt(%g) = %g, want %g", vcSqrtSC[i], f, sqrtSC[i]) - } - } -} -func TestTan(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Tan(vc[i]); !cSoclose(tan[i], f, 3e-15) { - t.Errorf("Tan(%g) = %g, want %g", vc[i], f, tan[i]) - } - } - for i := 0; i < len(vcTanSC); i++ { - if f := Tan(vcTanSC[i]); !cAlike(tanSC[i], f) { - t.Errorf("Tan(%g) = %g, want %g", vcTanSC[i], f, tanSC[i]) - } - } -} -func TestTanh(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Tanh(vc[i]); !cSoclose(tanh[i], f, 2e-15) { - t.Errorf("Tanh(%g) = %g, want %g", vc[i], f, tanh[i]) - } - } - for i := 0; i < len(vcTanhSC); i++ { - if f := Tanh(vcTanhSC[i]); !cAlike(tanhSC[i], f) { - t.Errorf("Tanh(%g) = %g, want %g", vcTanhSC[i], f, tanhSC[i]) - } - } -} - -func BenchmarkAbs(b *testing.B) { - for i := 0; i < b.N; i++ { - Abs(complex(2.5, 3.5)) - } -} -func BenchmarkAcos(b *testing.B) { - for i := 0; i < b.N; i++ { - Acos(complex(2.5, 3.5)) - } -} -func BenchmarkAcosh(b *testing.B) { - for i := 0; i < b.N; i++ { - Acosh(complex(2.5, 3.5)) - } -} -func BenchmarkAsin(b *testing.B) { - for i := 0; i < b.N; i++ { - Asin(complex(2.5, 3.5)) - } -} -func BenchmarkAsinh(b *testing.B) { - for i := 0; i < b.N; i++ { - Asinh(complex(2.5, 3.5)) - } -} -func BenchmarkAtan(b *testing.B) { - for i := 0; i < b.N; i++ { - Atan(complex(2.5, 3.5)) - } -} -func BenchmarkAtanh(b *testing.B) { - for i := 0; i < b.N; i++ { - Atanh(complex(2.5, 3.5)) - } -} -func BenchmarkConj(b *testing.B) { - for i := 0; i < b.N; i++ { - Conj(complex(2.5, 3.5)) - } -} -func BenchmarkCos(b *testing.B) { - for i := 0; i < b.N; i++ { - Cos(complex(2.5, 3.5)) - } -} -func BenchmarkCosh(b *testing.B) { - for i := 0; i < b.N; i++ { - Cosh(complex(2.5, 3.5)) - } -} -func BenchmarkExp(b *testing.B) { - for i := 0; i < b.N; i++ { - Exp(complex(2.5, 3.5)) - } -} -func BenchmarkLog(b *testing.B) { - for i := 0; i < b.N; i++ { - Log(complex(2.5, 3.5)) - } -} -func BenchmarkLog10(b *testing.B) { - for i := 0; i < b.N; i++ { - Log10(complex(2.5, 3.5)) - } -} -func BenchmarkPhase(b *testing.B) { - for i := 0; i < b.N; i++ { - Phase(complex(2.5, 3.5)) - } -} -func BenchmarkPolar(b *testing.B) { - for i := 0; i < b.N; i++ { - Polar(complex(2.5, 3.5)) - } -} -func BenchmarkPow(b *testing.B) { - for i := 0; i < b.N; i++ { - Pow(complex(2.5, 3.5), complex(2.5, 3.5)) - } -} -func BenchmarkRect(b *testing.B) { - for i := 0; i < b.N; i++ { - Rect(2.5, 1.5) - } -} -func BenchmarkSin(b *testing.B) { - for i := 0; i < b.N; i++ { - Sin(complex(2.5, 3.5)) - } -} -func BenchmarkSinh(b *testing.B) { - for i := 0; i < b.N; i++ { - Sinh(complex(2.5, 3.5)) - } -} -func BenchmarkSqrt(b *testing.B) { - for i := 0; i < b.N; i++ { - Sqrt(complex(2.5, 3.5)) - } -} -func BenchmarkTan(b *testing.B) { - for i := 0; i < b.N; i++ { - Tan(complex(2.5, 3.5)) - } -} -func BenchmarkTanh(b *testing.B) { - for i := 0; i < b.N; i++ { - Tanh(complex(2.5, 3.5)) - } -} diff --git a/src/pkg/cmath/conj.go b/src/pkg/cmath/conj.go deleted file mode 100644 index 776b57da7..000000000 --- a/src/pkg/cmath/conj.go +++ /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. - -package cmath - -// Conj returns the complex conjugate of x. -func Conj(x complex128) complex128 { return complex(real(x), -imag(x)) } diff --git a/src/pkg/cmath/exp.go b/src/pkg/cmath/exp.go deleted file mode 100644 index 64c1ef409..000000000 --- a/src/pkg/cmath/exp.go +++ /dev/null @@ -1,55 +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. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// 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 - -// Complex exponential function -// -// DESCRIPTION: -// -// Returns the complex exponential of the complex argument z. -// -// If -// z = x + iy, -// r = exp(x), -// then -// w = r cos y + i r sin y. -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 8700 3.7e-17 1.1e-17 -// IEEE -10,+10 30000 3.0e-16 8.7e-17 - -// Exp returns e**x, the base-e exponential of x. -func Exp(x complex128) complex128 { - r := math.Exp(real(x)) - s, c := math.Sincos(imag(x)) - return complex(r*c, r*s) -} diff --git a/src/pkg/cmath/isinf.go b/src/pkg/cmath/isinf.go deleted file mode 100644 index f23d2dea7..000000000 --- a/src/pkg/cmath/isinf.go +++ /dev/null @@ -1,21 +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. - -package cmath - -import "math" - -// IsInf returns true if either real(x) or imag(x) is an infinity. -func IsInf(x complex128) bool { - if math.IsInf(real(x), 0) || math.IsInf(imag(x), 0) { - return true - } - return false -} - -// Inf returns a complex infinity, complex(+Inf, +Inf). -func Inf() complex128 { - inf := math.Inf(1) - return complex(inf, inf) -} diff --git a/src/pkg/cmath/isnan.go b/src/pkg/cmath/isnan.go deleted file mode 100644 index 2063bb835..000000000 --- a/src/pkg/cmath/isnan.go +++ /dev/null @@ -1,25 +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. - -package cmath - -import "math" - -// IsNaN returns true if either real(x) or imag(x) is NaN -// and neither is an infinity. -func IsNaN(x complex128) bool { - switch { - case math.IsInf(real(x), 0) || math.IsInf(imag(x), 0): - return false - case math.IsNaN(real(x)) || math.IsNaN(imag(x)): - return true - } - return false -} - -// NaN returns a complex ``not-a-number'' value. -func NaN() complex128 { - nan := math.NaN() - return complex(nan, nan) -} diff --git a/src/pkg/cmath/log.go b/src/pkg/cmath/log.go deleted file mode 100644 index 8e6964fee..000000000 --- a/src/pkg/cmath/log.go +++ /dev/null @@ -1,64 +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. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// 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 - -// Complex natural logarithm -// -// DESCRIPTION: -// -// Returns complex logarithm to the base e (2.718...) of -// the complex argument z. -// -// If -// z = x + iy, r = sqrt( x**2 + y**2 ), -// then -// w = log(r) + i arctan(y/x). -// -// The arctangent ranges from -PI to +PI. -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 7000 8.5e-17 1.9e-17 -// IEEE -10,+10 30000 5.0e-15 1.1e-16 -// -// Larger relative error can be observed for z near 1 +i0. -// In IEEE arithmetic the peak absolute error is 5.2e-16, rms -// absolute error 1.0e-16. - -// Log returns the natural logarithm of x. -func Log(x complex128) complex128 { - return complex(math.Log(Abs(x)), Phase(x)) -} - -// Log10 returns the decimal logarithm of x. -func Log10(x complex128) complex128 { - return math.Log10E * Log(x) -} diff --git a/src/pkg/cmath/phase.go b/src/pkg/cmath/phase.go deleted file mode 100644 index 2d67aa34c..000000000 --- a/src/pkg/cmath/phase.go +++ /dev/null @@ -1,11 +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. - -package cmath - -import "math" - -// Phase returns the phase (also called the argument) of x. -// The returned value is in the range [-Pi, Pi]. -func Phase(x complex128) float64 { return math.Atan2(imag(x), real(x)) } diff --git a/src/pkg/cmath/polar.go b/src/pkg/cmath/polar.go deleted file mode 100644 index 033676acc..000000000 --- a/src/pkg/cmath/polar.go +++ /dev/null @@ -1,12 +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. - -package cmath - -// Polar returns the absolute value r and phase θ of x, -// such that x = r * e**θi. -// The phase is in the range [-Pi, Pi]. -func Polar(x complex128) (r, θ float64) { - return Abs(x), Phase(x) -} diff --git a/src/pkg/cmath/pow.go b/src/pkg/cmath/pow.go deleted file mode 100644 index 68e1207c6..000000000 --- a/src/pkg/cmath/pow.go +++ /dev/null @@ -1,60 +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. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// 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 - -// Complex power function -// -// DESCRIPTION: -// -// Raises complex A to the complex Zth power. -// Definition is per AMS55 # 4.2.8, -// analytically equivalent to cpow(a,z) = cexp(z clog(a)). -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// IEEE -10,+10 30000 9.4e-15 1.5e-15 - -// Pow returns x**y, the base-x exponential of y. -func Pow(x, y complex128) complex128 { - modulus := Abs(x) - if modulus == 0 { - return complex(0, 0) - } - r := math.Pow(modulus, real(y)) - arg := Phase(x) - theta := real(y) * arg - if imag(y) != 0 { - r *= math.Exp(-imag(y) * arg) - theta += imag(y) * math.Log(modulus) - } - s, c := math.Sincos(theta) - return complex(r*c, r*s) -} diff --git a/src/pkg/cmath/rect.go b/src/pkg/cmath/rect.go deleted file mode 100644 index b955f0bf7..000000000 --- a/src/pkg/cmath/rect.go +++ /dev/null @@ -1,13 +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. - -package cmath - -import "math" - -// Rect returns the complex number x with polar coordinates r, θ. -func Rect(r, θ float64) complex128 { - s, c := math.Sincos(θ) - return complex(r*c, r*s) -} diff --git a/src/pkg/cmath/sin.go b/src/pkg/cmath/sin.go deleted file mode 100644 index 8900ecdde..000000000 --- a/src/pkg/cmath/sin.go +++ /dev/null @@ -1,132 +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. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// 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 - -// Complex circular sine -// -// DESCRIPTION: -// -// If -// z = x + iy, -// -// then -// -// w = sin x cosh y + i cos x sinh y. -// -// csin(z) = -i csinh(iz). -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 8400 5.3e-17 1.3e-17 -// IEEE -10,+10 30000 3.8e-16 1.0e-16 -// Also tested by csin(casin(z)) = z. - -// Sin returns the sine of x. -func Sin(x complex128) complex128 { - s, c := math.Sincos(real(x)) - sh, ch := sinhcosh(imag(x)) - return complex(s*ch, c*sh) -} - -// Complex hyperbolic sine -// -// DESCRIPTION: -// -// csinh z = (cexp(z) - cexp(-z))/2 -// = sinh x * cos y + i cosh x * sin y . -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// IEEE -10,+10 30000 3.1e-16 8.2e-17 - -// Sinh returns the hyperbolic sine of x. -func Sinh(x complex128) complex128 { - s, c := math.Sincos(imag(x)) - sh, ch := sinhcosh(real(x)) - return complex(c*sh, s*ch) -} - -// Complex circular cosine -// -// DESCRIPTION: -// -// If -// z = x + iy, -// -// then -// -// w = cos x cosh y - i sin x sinh y. -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 8400 4.5e-17 1.3e-17 -// IEEE -10,+10 30000 3.8e-16 1.0e-16 - -// Cos returns the cosine of x. -func Cos(x complex128) complex128 { - s, c := math.Sincos(real(x)) - sh, ch := sinhcosh(imag(x)) - return complex(c*ch, -s*sh) -} - -// Complex hyperbolic cosine -// -// DESCRIPTION: -// -// ccosh(z) = cosh x cos y + i sinh x sin y . -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// IEEE -10,+10 30000 2.9e-16 8.1e-17 - -// Cosh returns the hyperbolic cosine of x. -func Cosh(x complex128) complex128 { - s, c := math.Sincos(imag(x)) - sh, ch := sinhcosh(real(x)) - return complex(c*ch, s*sh) -} - -// calculate sinh and cosh -func sinhcosh(x float64) (sh, ch float64) { - if math.Fabs(x) <= 0.5 { - return math.Sinh(x), math.Cosh(x) - } - e := math.Exp(x) - ei := 0.5 / e - e *= 0.5 - return e - ei, e + ei -} diff --git a/src/pkg/cmath/sqrt.go b/src/pkg/cmath/sqrt.go deleted file mode 100644 index e77a9b9df..000000000 --- a/src/pkg/cmath/sqrt.go +++ /dev/null @@ -1,103 +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. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// 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 - -// Complex square root -// -// DESCRIPTION: -// -// If z = x + iy, r = |z|, then -// -// 1/2 -// Re w = [ (r + x)/2 ] , -// -// 1/2 -// Im w = [ (r - x)/2 ] . -// -// Cancellation error in r-x or r+x is avoided by using the -// identity 2 Re w Im w = y. -// -// Note that -w is also a square root of z. The root chosen -// is always in the right half plane and Im w has the same sign as y. -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 25000 3.2e-17 9.6e-18 -// IEEE -10,+10 1,000,000 2.9e-16 6.1e-17 - -// Sqrt returns the square root of x. -func Sqrt(x complex128) complex128 { - if imag(x) == 0 { - if real(x) == 0 { - return complex(0, 0) - } - if real(x) < 0 { - return complex(0, math.Sqrt(-real(x))) - } - return complex(math.Sqrt(real(x)), 0) - } - if real(x) == 0 { - if imag(x) < 0 { - r := math.Sqrt(-0.5 * imag(x)) - return complex(r, -r) - } - r := math.Sqrt(0.5 * imag(x)) - return complex(r, r) - } - a := real(x) - b := imag(x) - var scale float64 - // Rescale to avoid internal overflow or underflow. - if math.Fabs(a) > 4 || math.Fabs(b) > 4 { - a *= 0.25 - b *= 0.25 - scale = 2 - } else { - a *= 1.8014398509481984e16 // 2**54 - b *= 1.8014398509481984e16 - scale = 7.450580596923828125e-9 // 2**-27 - } - r := math.Hypot(a, b) - var t float64 - if a > 0 { - t = math.Sqrt(0.5*r + 0.5*a) - r = scale * math.Fabs((0.5*b)/t) - t *= scale - } else { - r = math.Sqrt(0.5*r - 0.5*a) - t = scale * math.Fabs((0.5*b)/r) - r *= scale - } - if b < 0 { - return complex(t, -r) - } - return complex(t, r) -} diff --git a/src/pkg/cmath/tan.go b/src/pkg/cmath/tan.go deleted file mode 100644 index 94b517521..000000000 --- a/src/pkg/cmath/tan.go +++ /dev/null @@ -1,184 +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. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// 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 - -// Complex circular tangent -// -// DESCRIPTION: -// -// If -// z = x + iy, -// -// then -// -// sin 2x + i sinh 2y -// w = --------------------. -// cos 2x + cosh 2y -// -// On the real axis the denominator is zero at odd multiples -// of PI/2. The denominator is evaluated by its Taylor -// series near these points. -// -// ctan(z) = -i ctanh(iz). -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 5200 7.1e-17 1.6e-17 -// IEEE -10,+10 30000 7.2e-16 1.2e-16 -// Also tested by ctan * ccot = 1 and catan(ctan(z)) = z. - -// Tan returns the tangent of x. -func Tan(x complex128) complex128 { - d := math.Cos(2*real(x)) + math.Cosh(2*imag(x)) - if math.Fabs(d) < 0.25 { - d = tanSeries(x) - } - if d == 0 { - return Inf() - } - return complex(math.Sin(2*real(x))/d, math.Sinh(2*imag(x))/d) -} - -// Complex hyperbolic tangent -// -// DESCRIPTION: -// -// tanh z = (sinh 2x + i sin 2y) / (cosh 2x + cos 2y) . -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// IEEE -10,+10 30000 1.7e-14 2.4e-16 - -// Tanh returns the hyperbolic tangent of x. -func Tanh(x complex128) complex128 { - d := math.Cosh(2*real(x)) + math.Cos(2*imag(x)) - if d == 0 { - return Inf() - } - return complex(math.Sinh(2*real(x))/d, math.Sin(2*imag(x))/d) -} - -// Program to subtract nearest integer multiple of PI -func reducePi(x float64) float64 { - const ( - // extended precision value of PI: - DP1 = 3.14159265160560607910E0 // ?? 0x400921fb54000000 - DP2 = 1.98418714791870343106E-9 // ?? 0x3e210b4610000000 - DP3 = 1.14423774522196636802E-17 // ?? 0x3c6a62633145c06e - ) - t := x / math.Pi - if t >= 0 { - t += 0.5 - } else { - t -= 0.5 - } - t = float64(int64(t)) // int64(t) = the multiple - return ((x - t*DP1) - t*DP2) - t*DP3 -} - -// Taylor series expansion for cosh(2y) - cos(2x) -func tanSeries(z complex128) float64 { - const MACHEP = 1.0 / (1 << 53) - x := math.Fabs(2 * real(z)) - y := math.Fabs(2 * imag(z)) - x = reducePi(x) - x = x * x - y = y * y - x2 := 1.0 - y2 := 1.0 - f := 1.0 - rn := 0.0 - d := 0.0 - for { - rn += 1 - f *= rn - rn += 1 - f *= rn - x2 *= x - y2 *= y - t := y2 + x2 - t /= f - d += t - - rn += 1 - f *= rn - rn += 1 - f *= rn - x2 *= x - y2 *= y - t = y2 - x2 - t /= f - d += t - if math.Fabs(t/d) <= MACHEP { - break - } - } - return d -} - -// Complex circular cotangent -// -// DESCRIPTION: -// -// If -// z = x + iy, -// -// then -// -// sin 2x - i sinh 2y -// w = --------------------. -// cosh 2y - cos 2x -// -// On the real axis, the denominator has zeros at even -// multiples of PI/2. Near these points it is evaluated -// by a Taylor series. -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 3000 6.5e-17 1.6e-17 -// IEEE -10,+10 30000 9.2e-16 1.2e-16 -// Also tested by ctan * ccot = 1 + i0. - -// Cot returns the cotangent of x. -func Cot(x complex128) complex128 { - d := math.Cosh(2*imag(x)) - math.Cos(2*real(x)) - if math.Fabs(d) < 0.25 { - d = tanSeries(x) - } - if d == 0 { - return Inf() - } - return complex(math.Sin(2*real(x))/d, -math.Sinh(2*imag(x))/d) -} diff --git a/src/pkg/compress/bzip2/Makefile b/src/pkg/compress/bzip2/Makefile deleted file mode 100644 index a4bceef16..000000000 --- a/src/pkg/compress/bzip2/Makefile +++ /dev/null @@ -1,14 +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. - -include ../../../Make.inc - -TARG=compress/bzip2 -GOFILES=\ - bit_reader.go\ - bzip2.go\ - huffman.go\ - move_to_front.go\ - -include ../../../Make.pkg diff --git a/src/pkg/compress/bzip2/bit_reader.go b/src/pkg/compress/bzip2/bit_reader.go index 50f0ec836..b35c69a1c 100644 --- a/src/pkg/compress/bzip2/bit_reader.go +++ b/src/pkg/compress/bzip2/bit_reader.go @@ -7,25 +7,24 @@ package bzip2 import ( "bufio" "io" - "os" ) // bitReader wraps an io.Reader and provides the ability to read values, -// bit-by-bit, from it. Its Read* methods don't return the usual os.Error +// bit-by-bit, from it. Its Read* methods don't return the usual error // because the error handling was verbose. Instead, any error is kept and can // be checked afterwards. type bitReader struct { r byteReader n uint64 bits uint - err os.Error + err error } -// bitReader needs to read bytes from an io.Reader. We attempt to cast the +// 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, os.Error) + ReadByte() (byte, error) } func newBitReader(r io.Reader) bitReader { @@ -38,11 +37,11 @@ func newBitReader(r io.Reader) bitReader { // ReadBits64 reads the given number of bits and returns them in the // least-significant part of a uint64. In the event of an error, it returns 0 -// and the error can be obtained by calling Error(). +// and the error can be obtained by calling Err(). func (br *bitReader) ReadBits64(bits uint) (n uint64) { for bits > br.bits { b, err := br.r.ReadByte() - if err == os.EOF { + if err == io.EOF { err = io.ErrUnexpectedEOF } if err != nil { @@ -83,6 +82,6 @@ func (br *bitReader) ReadBit() bool { return n != 0 } -func (br *bitReader) Error() os.Error { +func (br *bitReader) Err() error { return br.err } diff --git a/src/pkg/compress/bzip2/bzip2.go b/src/pkg/compress/bzip2/bzip2.go index 8b4572306..3dc8c6206 100644 --- a/src/pkg/compress/bzip2/bzip2.go +++ b/src/pkg/compress/bzip2/bzip2.go @@ -5,10 +5,7 @@ // Package bzip2 implements bzip2 decompression. package bzip2 -import ( - "io" - "os" -) +import "io" // There's no RFC for bzip2. I used the Wikipedia page for reference and a lot // of guessing: http://en.wikipedia.org/wiki/Bzip2 @@ -19,7 +16,7 @@ import ( // syntactically invalid. type StructuralError string -func (s StructuralError) String() string { +func (s StructuralError) Error() string { return "bzip2 data invalid: " + string(s) } @@ -53,7 +50,7 @@ const bzip2BlockMagic = 0x314159265359 const bzip2FinalMagic = 0x177245385090 // setup parses the bzip2 header. -func (bz2 *reader) setup() os.Error { +func (bz2 *reader) setup() error { br := &bz2.br magic := br.ReadBits(16) @@ -76,14 +73,14 @@ func (bz2 *reader) setup() os.Error { return nil } -func (bz2 *reader) Read(buf []byte) (n int, err os.Error) { +func (bz2 *reader) Read(buf []byte) (n int, err error) { if bz2.eof { - return 0, os.EOF + return 0, io.EOF } if !bz2.setupDone { err = bz2.setup() - brErr := bz2.br.Error() + brErr := bz2.br.Err() if brErr != nil { err = brErr } @@ -94,14 +91,14 @@ func (bz2 *reader) Read(buf []byte) (n int, err os.Error) { } n, err = bz2.read(buf) - brErr := bz2.br.Error() + brErr := bz2.br.Err() if brErr != nil { err = brErr } return } -func (bz2 *reader) read(buf []byte) (n int, err os.Error) { +func (bz2 *reader) read(buf []byte) (n int, err error) { // bzip2 is a block based compressor, except that it has a run-length // preprocessing step. The block based nature means that we can // preallocate fixed-size buffers and reuse them. However, the RLE @@ -162,7 +159,7 @@ func (bz2 *reader) read(buf []byte) (n int, err os.Error) { if magic == bzip2FinalMagic { br.ReadBits64(32) // ignored CRC bz2.eof = true - return 0, os.EOF + return 0, io.EOF } else if magic != bzip2BlockMagic { return 0, StructuralError("bad magic value found") } @@ -176,7 +173,7 @@ func (bz2 *reader) read(buf []byte) (n int, err os.Error) { } // readBlock reads a bzip2 block. The magic number should already have been consumed. -func (bz2 *reader) readBlock() (err os.Error) { +func (bz2 *reader) readBlock() (err error) { br := &bz2.br br.ReadBits64(32) // skip checksum. TODO: check it if we can figure out what it is. randomized := br.ReadBits(1) diff --git a/src/pkg/compress/bzip2/bzip2_test.go b/src/pkg/compress/bzip2/bzip2_test.go index 156eea83f..7b227ac9f 100644 --- a/src/pkg/compress/bzip2/bzip2_test.go +++ b/src/pkg/compress/bzip2/bzip2_test.go @@ -9,7 +9,6 @@ import ( "encoding/hex" "io" "io/ioutil" - "os" "testing" ) @@ -46,7 +45,7 @@ func readerFromHex(s string) io.Reader { return bytes.NewBuffer(data) } -func decompressHex(s string) (out []byte, err os.Error) { +func decompressHex(s string) (out []byte, err error) { r := NewReader(readerFromHex(s)) return ioutil.ReadAll(r) } diff --git a/src/pkg/compress/bzip2/huffman.go b/src/pkg/compress/bzip2/huffman.go index dc05739c7..078c1cb89 100644 --- a/src/pkg/compress/bzip2/huffman.go +++ b/src/pkg/compress/bzip2/huffman.go @@ -4,10 +4,7 @@ package bzip2 -import ( - "os" - "sort" -) +import "sort" // A huffmanTree is a binary tree which is navigated, bit-by-bit to reach a // symbol. @@ -63,7 +60,7 @@ func (t huffmanTree) Decode(br *bitReader) (v uint16) { // newHuffmanTree builds a Huffman tree from a slice containing the code // lengths of each symbol. The maximum code length is 32 bits. -func newHuffmanTree(lengths []uint8) (huffmanTree, os.Error) { +func newHuffmanTree(lengths []uint8) (huffmanTree, error) { // There are many possible trees that assign the same code length to // each symbol (consider reflecting a tree down the middle, for // example). Since the code length assignments determine the @@ -176,7 +173,7 @@ func (n huffmanCodes) Swap(i, j int) { // buildHuffmanNode takes a slice of sorted huffmanCodes and builds a node in // the Huffman tree at the given level. It returns the index of the newly // constructed node. -func buildHuffmanNode(t *huffmanTree, codes []huffmanCode, level uint32) (nodeIndex uint16, err os.Error) { +func buildHuffmanNode(t *huffmanTree, codes []huffmanCode, level uint32) (nodeIndex uint16, err error) { test := uint32(1) << (31 - level) // We have to search the list of codes to find the divide between the left and right sides. diff --git a/src/pkg/compress/flate/Makefile b/src/pkg/compress/flate/Makefile deleted file mode 100644 index 197828a92..000000000 --- a/src/pkg/compress/flate/Makefile +++ /dev/null @@ -1,17 +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 ../../../Make.inc - -TARG=compress/flate -GOFILES=\ - deflate.go\ - huffman_bit_writer.go\ - huffman_code.go\ - inflate.go\ - reverse_bits.go\ - token.go\ - util.go\ - -include ../../../Make.pkg diff --git a/src/pkg/compress/flate/deflate.go b/src/pkg/compress/flate/deflate.go index b1cee0b2f..20408409c 100644 --- a/src/pkg/compress/flate/deflate.go +++ b/src/pkg/compress/flate/deflate.go @@ -5,9 +5,9 @@ package flate import ( + "fmt" "io" "math" - "os" ) const ( @@ -28,10 +28,12 @@ const ( // stop things from getting too large. maxFlateBlockTokens = 1 << 14 maxStoreBlockSize = 65535 - hashBits = 15 + hashBits = 17 hashSize = 1 << hashBits hashMask = (1 << hashBits) - 1 hashShift = (hashBits + minMatchLength - 1) / minMatchLength + + skipNever = math.MaxInt32 ) type compressionLevel struct { @@ -46,12 +48,12 @@ var levels = []compressionLevel{ {3, 0, 32, 32, 6}, // Levels 4-9 use increasingly more lazy matching // and increasingly stringent conditions for "good enough". - {4, 4, 16, 16, math.MaxInt32}, - {8, 16, 32, 32, math.MaxInt32}, - {8, 16, 128, 128, math.MaxInt32}, - {8, 32, 128, 256, math.MaxInt32}, - {32, 128, 258, 1024, math.MaxInt32}, - {32, 258, 258, 4096, math.MaxInt32}, + {4, 4, 16, 16, skipNever}, + {8, 16, 32, 32, skipNever}, + {8, 16, 128, 128, skipNever}, + {8, 32, 128, 256, skipNever}, + {32, 128, 258, 1024, skipNever}, + {32, 258, 258, 4096, skipNever}, } type compressor struct { @@ -69,9 +71,10 @@ type compressor struct { // If hashHead[hashValue] is within the current window, then // hashPrev[hashHead[hashValue] & windowMask] contains the previous index // with the same hash value. - chainHead int - hashHead []int - hashPrev []int + chainHead int + hashHead []int + hashPrev []int + hashOffset int // input window: unprocessed data is window[index:windowEnd] index int @@ -80,16 +83,15 @@ type compressor struct { blockStart int // window index where current tokens start byteAvailable bool // if true, still need to process window[index-1]. - // queued output tokens: tokens[:ti] + // queued output tokens tokens []token - ti int // deflate state length int offset int hash int maxInsertIndex int - err os.Error + err error } func (d *compressor) fillDeflate(b []byte) int { @@ -103,27 +105,14 @@ func (d *compressor) fillDeflate(b []byte) int { } else { d.blockStart = math.MaxInt32 } - for i, h := range d.hashHead { - v := h - windowSize - if v < -1 { - v = -1 - } - d.hashHead[i] = v - } - for i, h := range d.hashPrev { - v := -h - windowSize - if v < -1 { - v = -1 - } - d.hashPrev[i] = v - } + d.hashOffset += windowSize } n := copy(d.window[d.windowEnd:], b) d.windowEnd += n return n } -func (d *compressor) writeBlock(tokens []token, index int, eof bool) os.Error { +func (d *compressor) writeBlock(tokens []token, index int, eof bool) error { if index > 0 || eof { var window []byte if d.blockStart <= index { @@ -187,14 +176,14 @@ func (d *compressor) findMatch(pos int, prevHead int, prevLength int, lookahead // hashPrev[i & windowMask] has already been overwritten, so stop now. break } - if i = d.hashPrev[i&windowMask]; i < minIndex || i < 0 { + if i = d.hashPrev[i&windowMask] - d.hashOffset; i < minIndex || i < 0 { break } } return } -func (d *compressor) writeStoredBlock(buf []byte) os.Error { +func (d *compressor) writeStoredBlock(buf []byte) error { if d.w.writeStoredHeader(len(buf), false); d.w.err != nil { return d.w.err } @@ -206,13 +195,12 @@ func (d *compressor) initDeflate() { d.hashHead = make([]int, hashSize) d.hashPrev = make([]int, windowSize) d.window = make([]byte, 2*windowSize) - fillInts(d.hashHead, -1) - d.tokens = make([]token, maxFlateBlockTokens, maxFlateBlockTokens+1) + d.hashOffset = 1 + d.tokens = make([]token, 0, maxFlateBlockTokens+1) d.length = minMatchLength - 1 d.offset = 0 d.byteAvailable = false d.index = 0 - d.ti = 0 d.hash = 0 d.chainHead = -1 } @@ -244,15 +232,14 @@ Loop: // Flush current output block if any. if d.byteAvailable { // There is still one pending token that needs to be flushed - d.tokens[d.ti] = literalToken(uint32(d.window[d.index-1])) - d.ti++ + d.tokens = append(d.tokens, literalToken(uint32(d.window[d.index-1]))) d.byteAvailable = false } - if d.ti > 0 { - if d.err = d.writeBlock(d.tokens[0:d.ti], d.index, false); d.err != nil { + if len(d.tokens) > 0 { + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { return } - d.ti = 0 + d.tokens = d.tokens[:0] } break Loop } @@ -262,7 +249,7 @@ Loop: d.hash = (d.hash<= minIndex && - (d.fastSkipHashing != 0 && lookahead > minMatchLength-1 || - d.fastSkipHashing == 0 && lookahead > prevLength && prevLength < d.lazy) { - if newLength, newOffset, ok := d.findMatch(d.index, d.chainHead, minMatchLength-1, lookahead); ok { + if d.chainHead-d.hashOffset >= minIndex && + (d.fastSkipHashing != skipNever && lookahead > minMatchLength-1 || + d.fastSkipHashing == skipNever && lookahead > prevLength && prevLength < d.lazy) { + if newLength, newOffset, ok := d.findMatch(d.index, d.chainHead-d.hashOffset, minMatchLength-1, lookahead); ok { d.length = newLength d.offset = newOffset } } - if d.fastSkipHashing != 0 && d.length >= minMatchLength || - d.fastSkipHashing == 0 && prevLength >= minMatchLength && d.length <= prevLength { + if d.fastSkipHashing != skipNever && d.length >= minMatchLength || + d.fastSkipHashing == skipNever && prevLength >= minMatchLength && d.length <= prevLength { // There was a match at the previous step, and the current match is // not better. Output the previous match. - if d.fastSkipHashing != 0 { - d.tokens[d.ti] = matchToken(uint32(d.length-minMatchLength), uint32(d.offset-minOffsetSize)) + if d.fastSkipHashing != skipNever { + d.tokens = append(d.tokens, matchToken(uint32(d.length-minMatchLength), uint32(d.offset-minOffsetSize))) } else { - d.tokens[d.ti] = matchToken(uint32(prevLength-minMatchLength), uint32(prevOffset-minOffsetSize)) + d.tokens = append(d.tokens, matchToken(uint32(prevLength-minMatchLength), uint32(prevOffset-minOffsetSize))) } - d.ti++ // Insert in the hash table all strings up to the end of the match. // index and index-1 are already inserted. If there is not enough // lookahead, the last two strings are not inserted into the hash // table. if d.length <= d.fastSkipHashing { var newIndex int - if d.fastSkipHashing != 0 { + if d.fastSkipHashing != skipNever { newIndex = d.index + d.length } else { - newIndex = prevLength - 1 + newIndex = d.index + prevLength - 1 } for d.index++; d.index < newIndex; d.index++ { if d.index < d.maxInsertIndex { @@ -309,10 +295,10 @@ Loop: // Our chain should point to the previous value. d.hashPrev[d.index&windowMask] = d.hashHead[d.hash] // Set the head of the hash chain to us. - d.hashHead[d.hash] = d.index + d.hashHead[d.hash] = d.index + d.hashOffset } } - if d.fastSkipHashing == 0 { + if d.fastSkipHashing == skipNever { d.byteAvailable = false d.length = minMatchLength - 1 } @@ -320,32 +306,33 @@ Loop: // For matches this long, we don't bother inserting each individual // item into the table. d.index += d.length - d.hash = (int(d.window[d.index])< 0 { @@ -375,7 +362,7 @@ func (d *compressor) write(b []byte) (n int, err os.Error) { return n, d.err } -func (d *compressor) syncFlush() os.Error { +func (d *compressor) syncFlush() error { d.sync = true d.step(d) if d.err == nil { @@ -387,7 +374,7 @@ func (d *compressor) syncFlush() os.Error { return d.err } -func (d *compressor) init(w io.Writer, level int) (err os.Error) { +func (d *compressor) init(w io.Writer, level int) (err error) { d.w = newHuffmanBitWriter(w) switch { @@ -404,12 +391,12 @@ func (d *compressor) init(w io.Writer, level int) (err os.Error) { d.fill = (*compressor).fillDeflate d.step = (*compressor).deflate default: - return WrongValueError{"level", 0, 9, int32(level)} + return fmt.Errorf("flate: invalid compression level %d: want value in range [-1, 9]", level) } return nil } -func (d *compressor) close() os.Error { +func (d *compressor) close() error { d.sync = true d.step(d) if d.err != nil { @@ -422,17 +409,22 @@ func (d *compressor) close() os.Error { return d.w.err } -// NewWriter returns a new Writer compressing -// data at the given level. Following zlib, levels -// range from 1 (BestSpeed) to 9 (BestCompression); -// higher levels typically run slower but compress more. -// Level 0 (NoCompression) does not attempt any -// compression; it only adds the necessary DEFLATE framing. -func NewWriter(w io.Writer, level int) *Writer { +// NewWriter returns a new Writer compressing data at the given level. +// Following zlib, levels range from 1 (BestSpeed) to 9 (BestCompression); +// higher levels typically run slower but compress more. Level 0 +// (NoCompression) does not attempt any compression; it only adds the +// necessary DEFLATE framing. Level -1 (DefaultCompression) uses the default +// compression level. +// +// If level is in the range [-1, 9] then the error returned will be nil. +// Otherwise the error returned will be non-nil. +func NewWriter(w io.Writer, level int) (*Writer, error) { const logWindowSize = logMaxOffsetSize var dw Writer - dw.d.init(w, level) - return &dw + if err := dw.d.init(w, level); err != nil { + return nil, err + } + return &dw, nil } // NewWriterDict is like NewWriter but initializes the new @@ -441,13 +433,16 @@ func NewWriter(w io.Writer, level int) *Writer { // any compressed output. The compressed data written to w // can only be decompressed by a Reader initialized with the // same dictionary. -func NewWriterDict(w io.Writer, level int, dict []byte) *Writer { +func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) { dw := &dictWriter{w, false} - zw := NewWriter(dw, level) + zw, err := NewWriter(dw, level) + if err != nil { + return nil, err + } zw.Write(dict) zw.Flush() dw.enabled = true - return zw + return zw, err } type dictWriter struct { @@ -455,7 +450,7 @@ type dictWriter struct { enabled bool } -func (w *dictWriter) Write(b []byte) (n int, err os.Error) { +func (w *dictWriter) Write(b []byte) (n int, err error) { if w.enabled { return w.w.Write(b) } @@ -470,7 +465,7 @@ type Writer struct { // Write writes data to w, which will eventually write the // compressed form of data to its underlying writer. -func (w *Writer) Write(data []byte) (n int, err os.Error) { +func (w *Writer) Write(data []byte) (n int, err error) { return w.d.write(data) } @@ -481,13 +476,13 @@ func (w *Writer) Write(data []byte) (n int, err os.Error) { // If the underlying writer returns an error, Flush returns that error. // // In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH. -func (w *Writer) Flush() os.Error { +func (w *Writer) Flush() error { // For more about flushing: // http://www.bolet.org/~pornin/deflate-flush.html return w.d.syncFlush() } // Close flushes and closes the writer. -func (w *Writer) Close() os.Error { +func (w *Writer) Close() error { return w.d.close() } diff --git a/src/pkg/compress/flate/deflate_test.go b/src/pkg/compress/flate/deflate_test.go index 930823685..543c59505 100644 --- a/src/pkg/compress/flate/deflate_test.go +++ b/src/pkg/compress/flate/deflate_test.go @@ -9,7 +9,6 @@ import ( "fmt" "io" "io/ioutil" - "os" "sync" "testing" ) @@ -31,44 +30,44 @@ type reverseBitsTest struct { } var deflateTests = []*deflateTest{ - &deflateTest{[]byte{}, 0, []byte{1, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11}, -1, []byte{18, 4, 4, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11}, DefaultCompression, []byte{18, 4, 4, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11}, 4, []byte{18, 4, 4, 0, 0, 255, 255}}, - - &deflateTest{[]byte{0x11}, 0, []byte{0, 1, 0, 254, 255, 17, 1, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11, 0x12}, 0, []byte{0, 2, 0, 253, 255, 17, 18, 1, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 0, + {[]byte{}, 0, []byte{1, 0, 0, 255, 255}}, + {[]byte{0x11}, -1, []byte{18, 4, 4, 0, 0, 255, 255}}, + {[]byte{0x11}, DefaultCompression, []byte{18, 4, 4, 0, 0, 255, 255}}, + {[]byte{0x11}, 4, []byte{18, 4, 4, 0, 0, 255, 255}}, + + {[]byte{0x11}, 0, []byte{0, 1, 0, 254, 255, 17, 1, 0, 0, 255, 255}}, + {[]byte{0x11, 0x12}, 0, []byte{0, 2, 0, 253, 255, 17, 18, 1, 0, 0, 255, 255}}, + {[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 0, []byte{0, 8, 0, 247, 255, 17, 17, 17, 17, 17, 17, 17, 17, 1, 0, 0, 255, 255}, }, - &deflateTest{[]byte{}, 1, []byte{1, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11}, 1, []byte{18, 4, 4, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11, 0x12}, 1, []byte{18, 20, 2, 4, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 1, []byte{18, 132, 2, 64, 0, 0, 0, 255, 255}}, - &deflateTest{[]byte{}, 9, []byte{1, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11}, 9, []byte{18, 4, 4, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11, 0x12}, 9, []byte{18, 20, 2, 4, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 9, []byte{18, 132, 2, 64, 0, 0, 0, 255, 255}}, + {[]byte{}, 1, []byte{1, 0, 0, 255, 255}}, + {[]byte{0x11}, 1, []byte{18, 4, 4, 0, 0, 255, 255}}, + {[]byte{0x11, 0x12}, 1, []byte{18, 20, 2, 4, 0, 0, 255, 255}}, + {[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 1, []byte{18, 132, 2, 64, 0, 0, 0, 255, 255}}, + {[]byte{}, 9, []byte{1, 0, 0, 255, 255}}, + {[]byte{0x11}, 9, []byte{18, 4, 4, 0, 0, 255, 255}}, + {[]byte{0x11, 0x12}, 9, []byte{18, 20, 2, 4, 0, 0, 255, 255}}, + {[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 9, []byte{18, 132, 2, 64, 0, 0, 0, 255, 255}}, } var deflateInflateTests = []*deflateInflateTest{ - &deflateInflateTest{[]byte{}}, - &deflateInflateTest{[]byte{0x11}}, - &deflateInflateTest{[]byte{0x11, 0x12}}, - &deflateInflateTest{[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}}, - &deflateInflateTest{[]byte{0x11, 0x10, 0x13, 0x41, 0x21, 0x21, 0x41, 0x13, 0x87, 0x78, 0x13}}, - &deflateInflateTest{largeDataChunk()}, + {[]byte{}}, + {[]byte{0x11}}, + {[]byte{0x11, 0x12}}, + {[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}}, + {[]byte{0x11, 0x10, 0x13, 0x41, 0x21, 0x21, 0x41, 0x13, 0x87, 0x78, 0x13}}, + {largeDataChunk()}, } var reverseBitsTests = []*reverseBitsTest{ - &reverseBitsTest{1, 1, 1}, - &reverseBitsTest{1, 2, 2}, - &reverseBitsTest{1, 3, 4}, - &reverseBitsTest{1, 4, 8}, - &reverseBitsTest{1, 5, 16}, - &reverseBitsTest{17, 5, 17}, - &reverseBitsTest{257, 9, 257}, - &reverseBitsTest{29, 5, 23}, + {1, 1, 1}, + {1, 2, 2}, + {1, 3, 4}, + {1, 4, 8}, + {1, 5, 16}, + {17, 5, 17}, + {257, 9, 257}, + {29, 5, 23}, } func largeDataChunk() []byte { @@ -82,7 +81,11 @@ func largeDataChunk() []byte { func TestDeflate(t *testing.T) { for _, h := range deflateTests { var buf bytes.Buffer - w := NewWriter(&buf, h.level) + w, err := NewWriter(&buf, h.level) + if err != nil { + t.Errorf("NewWriter: %v", err) + continue + } w.Write(h.in) w.Close() if !bytes.Equal(buf.Bytes(), h.out) { @@ -102,7 +105,7 @@ func newSyncBuffer() *syncBuffer { return &syncBuffer{ready: make(chan bool, 1)} } -func (b *syncBuffer) Read(p []byte) (n int, err os.Error) { +func (b *syncBuffer) Read(p []byte) (n int, err error) { for { b.mu.RLock() n, err = b.buf.Read(p) @@ -122,7 +125,7 @@ func (b *syncBuffer) signal() { } } -func (b *syncBuffer) Write(p []byte) (n int, err os.Error) { +func (b *syncBuffer) Write(p []byte) (n int, err error) { n, err = b.buf.Write(p) b.signal() return @@ -137,7 +140,7 @@ func (b *syncBuffer) ReadMode() { b.signal() } -func (b *syncBuffer) Close() os.Error { +func (b *syncBuffer) Close() error { b.closed = true b.signal() return nil @@ -152,7 +155,11 @@ func testSync(t *testing.T, level int, input []byte, name string) { buf := newSyncBuffer() buf1 := new(bytes.Buffer) buf.WriteMode() - w := NewWriter(io.MultiWriter(buf, buf1), level) + w, err := NewWriter(io.MultiWriter(buf, buf1), level) + if err != nil { + t.Errorf("NewWriter: %v", err) + return + } r := NewReader(buf) // Write half the input and read back. @@ -204,7 +211,7 @@ func testSync(t *testing.T, level int, input []byte, name string) { } buf.ReadMode() out := make([]byte, 10) - if n, err := r.Read(out); n > 0 || err != os.EOF { + if n, err := r.Read(out); n > 0 || err != io.EOF { t.Errorf("testSync (%d, %d, %s): final Read: %d, %v (hex: %x)", level, len(input), name, n, err, out[0:n]) } if buf.buf.Len() != 0 { @@ -214,7 +221,7 @@ func testSync(t *testing.T, level int, input []byte, name string) { // stream should work for ordinary reader too r = NewReader(buf1) - out, err := ioutil.ReadAll(r) + out, err = ioutil.ReadAll(r) if err != nil { t.Errorf("testSync: read: %s", err) return @@ -225,35 +232,42 @@ func testSync(t *testing.T, level int, input []byte, name string) { } } -func testToFromWithLevel(t *testing.T, level int, input []byte, name string) os.Error { - buffer := bytes.NewBuffer(nil) - w := NewWriter(buffer, level) +func testToFromWithLevelAndLimit(t *testing.T, level int, input []byte, name string, limit int) { + var buffer bytes.Buffer + w, err := NewWriter(&buffer, level) + if err != nil { + t.Errorf("NewWriter: %v", err) + return + } w.Write(input) w.Close() - r := NewReader(buffer) + if limit > 0 && buffer.Len() > limit { + t.Errorf("level: %d, len(compress(data)) = %d > limit = %d", level, buffer.Len(), limit) + return + } + r := NewReader(&buffer) out, err := ioutil.ReadAll(r) if err != nil { t.Errorf("read: %s", err) - return err + return } r.Close() if !bytes.Equal(input, out) { t.Errorf("decompress(compress(data)) != data: level=%d input=%s", level, name) + return } - testSync(t, level, input, name) - return nil } -func testToFrom(t *testing.T, input []byte, name string) { +func testToFromWithLimit(t *testing.T, input []byte, name string, limit [10]int) { for i := 0; i < 10; i++ { - testToFromWithLevel(t, i, input, name) + testToFromWithLevelAndLimit(t, i, input, name, limit[i]) } } func TestDeflateInflate(t *testing.T) { for i, h := range deflateInflateTests { - testToFrom(t, h.in, fmt.Sprintf("#%d", i)) + testToFromWithLimit(t, h.in, fmt.Sprintf("#%d", i), [10]int{}) } } @@ -266,12 +280,36 @@ func TestReverseBits(t *testing.T) { } } +type deflateInflateStringTest struct { + filename string + label string + limit [10]int +} + +var deflateInflateStringTests = []deflateInflateStringTest{ + { + "../testdata/e.txt", + "2.718281828...", + [...]int{10013, 5065, 5096, 5115, 5093, 5079, 5079, 5079, 5079, 5079}, + }, + { + "../testdata/Mark.Twain-Tom.Sawyer.txt", + "Mark.Twain-Tom.Sawyer", + [...]int{407330, 187598, 180361, 172974, 169160, 163476, 160936, 160506, 160295, 160295}, + }, +} + func TestDeflateInflateString(t *testing.T) { - gold, err := ioutil.ReadFile("../testdata/e.txt") - if err != nil { - t.Error(err) + for _, test := range deflateInflateStringTests { + gold, err := ioutil.ReadFile(test.filename) + if err != nil { + t.Error(err) + } + testToFromWithLimit(t, gold, test.label, test.limit) + if testing.Short() { + break + } } - testToFromWithLevel(t, 1, gold, "2.718281828...") } func TestReaderDict(t *testing.T) { @@ -280,7 +318,10 @@ func TestReaderDict(t *testing.T) { text = "hello again world" ) var b bytes.Buffer - w := NewWriter(&b, 5) + w, err := NewWriter(&b, 5) + if err != nil { + t.Fatalf("NewWriter: %v", err) + } w.Write([]byte(dict)) w.Flush() b.Reset() @@ -303,7 +344,10 @@ func TestWriterDict(t *testing.T) { text = "hello again world" ) var b bytes.Buffer - w := NewWriter(&b, 5) + w, err := NewWriter(&b, 5) + if err != nil { + t.Fatalf("NewWriter: %v", err) + } w.Write([]byte(dict)) w.Flush() b.Reset() @@ -311,7 +355,7 @@ func TestWriterDict(t *testing.T) { w.Close() var b1 bytes.Buffer - w = NewWriterDict(&b1, 5, []byte(dict)) + w, _ = NewWriterDict(&b1, 5, []byte(dict)) w.Write([]byte(text)) w.Close() @@ -319,3 +363,22 @@ func TestWriterDict(t *testing.T) { t.Fatalf("writer wrote %q want %q", b1.Bytes(), b.Bytes()) } } + +// See http://code.google.com/p/go/issues/detail?id=2508 +func TestRegression2508(t *testing.T) { + if testing.Short() { + t.Logf("test disabled with -short") + return + } + w, err := NewWriter(ioutil.Discard, 1) + if err != nil { + t.Fatalf("NewWriter: %v", err) + } + buf := make([]byte, 1024) + for i := 0; i < 131072; i++ { + if _, err := w.Write(buf); err != nil { + t.Fatalf("writer failed: %v", err) + } + } + w.Close() +} diff --git a/src/pkg/compress/flate/flate_test.go b/src/pkg/compress/flate/flate_test.go index bfd3b8381..94efc90ac 100644 --- a/src/pkg/compress/flate/flate_test.go +++ b/src/pkg/compress/flate/flate_test.go @@ -52,7 +52,7 @@ type InitDecoderTest struct { var initDecoderTests = []*InitDecoderTest{ // Example from Connell 1973, - &InitDecoderTest{ + { []int{3, 5, 2, 4, 3, 5, 5, 4, 4, 3, 4, 5}, huffmanDecoder{ 2, 5, @@ -68,7 +68,7 @@ var initDecoderTests = []*InitDecoderTest{ }, // Example from RFC 1951 section 3.2.2 - &InitDecoderTest{ + { []int{2, 1, 3, 3}, huffmanDecoder{ 1, 3, @@ -80,7 +80,7 @@ var initDecoderTests = []*InitDecoderTest{ }, // Second example from RFC 1951 section 3.2.2 - &InitDecoderTest{ + { []int{3, 3, 3, 3, 3, 2, 4, 4}, huffmanDecoder{ 2, 4, @@ -92,21 +92,21 @@ var initDecoderTests = []*InitDecoderTest{ }, // Static Huffman codes (RFC 1951 section 3.2.6) - &InitDecoderTest{ + { fixedHuffmanBits[0:], fixedHuffmanDecoder, true, }, // Illegal input. - &InitDecoderTest{ + { []int{}, huffmanDecoder{}, false, }, // Illegal input. - &InitDecoderTest{ + { []int{0, 0, 0, 0, 0, 0, 0}, huffmanDecoder{}, false, diff --git a/src/pkg/compress/flate/huffman_bit_writer.go b/src/pkg/compress/flate/huffman_bit_writer.go index 3981df5cb..25e1da336 100644 --- a/src/pkg/compress/flate/huffman_bit_writer.go +++ b/src/pkg/compress/flate/huffman_bit_writer.go @@ -7,8 +7,6 @@ package flate import ( "io" "math" - "os" - "strconv" ) const ( @@ -83,14 +81,7 @@ type huffmanBitWriter struct { literalEncoding *huffmanEncoder offsetEncoding *huffmanEncoder codegenEncoding *huffmanEncoder - err os.Error -} - -type WrongValueError struct { - name string - from int32 - to int32 - value int32 + err error } func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter { @@ -106,11 +97,6 @@ func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter { } } -func (err WrongValueError) String() string { - return "huffmanBitWriter: " + err.name + " should belong to [" + strconv.Itoa64(int64(err.from)) + ";" + - strconv.Itoa64(int64(err.to)) + "] but actual value is " + strconv.Itoa64(int64(err.value)) -} - func (w *huffmanBitWriter) flushBits() { if w.err != nil { w.nbits = 0 @@ -194,15 +180,17 @@ func (w *huffmanBitWriter) writeBytes(bytes []byte) { // numLiterals The number of literals in literalEncoding // numOffsets The number of offsets in offsetEncoding func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int) { - fillInt32s(w.codegenFreq, 0) + for i := range w.codegenFreq { + w.codegenFreq[i] = 0 + } // Note that we are using codegen both as a temporary variable for holding // a copy of the frequencies, and as the place where we put the result. // This is fine because the output is always shorter than the input used // so far. codegen := w.codegen // cache // Copy the concatenated code sizes to codegen. Put a marker at the end. - copyUint8s(codegen[0:numLiterals], w.literalEncoding.codeBits) - copyUint8s(codegen[numLiterals:numLiterals+numOffsets], w.offsetEncoding.codeBits) + copy(codegen[0:numLiterals], w.literalEncoding.codeBits) + copy(codegen[numLiterals:numLiterals+numOffsets], w.offsetEncoding.codeBits) codegen[numLiterals+numOffsets] = badCode size := codegen[0] @@ -223,7 +211,10 @@ func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int) { w.codegenFreq[size]++ count-- for count >= 3 { - n := min(count, 6) + n := 6 + if n > count { + n = count + } codegen[outIndex] = 16 outIndex++ codegen[outIndex] = uint8(n - 3) @@ -233,7 +224,10 @@ func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int) { } } else { for count >= 11 { - n := min(count, 138) + n := 138 + if n > count { + n = count + } codegen[outIndex] = 18 outIndex++ codegen[outIndex] = uint8(n - 11) @@ -352,8 +346,12 @@ func (w *huffmanBitWriter) writeBlock(tokens []token, eof bool, input []byte) { if w.err != nil { return } - fillInt32s(w.literalFreq, 0) - fillInt32s(w.offsetFreq, 0) + for i := range w.literalFreq { + w.literalFreq[i] = 0 + } + for i := range w.offsetFreq { + w.offsetFreq[i] = 0 + } n := len(tokens) tokens = tokens[0 : n+1] diff --git a/src/pkg/compress/flate/huffman_code.go b/src/pkg/compress/flate/huffman_code.go index 7ed603a4f..009cce626 100644 --- a/src/pkg/compress/flate/huffman_code.go +++ b/src/pkg/compress/flate/huffman_code.go @@ -121,61 +121,6 @@ func (h *huffmanEncoder) bitLength(freq []int32) int64 { return total } -// Generate elements in the chain using an iterative algorithm. -func (h *huffmanEncoder) generateChains(top *levelInfo, list []literalNode) { - n := len(list) - list = list[0 : n+1] - list[n] = maxNode() - - l := top - for { - if l.nextPairFreq == math.MaxInt32 && l.nextCharFreq == math.MaxInt32 { - // We've run out of both leafs and pairs. - // End all calculations for this level. - // To m sure we never come back to this level or any lower level, - // set nextPairFreq impossibly large. - l.lastChain = nil - l.needed = 0 - l = l.up - l.nextPairFreq = math.MaxInt32 - continue - } - - prevFreq := l.lastChain.freq - if l.nextCharFreq < l.nextPairFreq { - // The next item on this row is a leaf node. - n := l.lastChain.leafCount + 1 - l.lastChain = &chain{l.nextCharFreq, n, l.lastChain.up} - l.nextCharFreq = list[n].freq - } else { - // The next item on this row is a pair from the previous row. - // nextPairFreq isn't valid until we generate two - // more values in the level below - l.lastChain = &chain{l.nextPairFreq, l.lastChain.leafCount, l.down.lastChain} - l.down.needed = 2 - } - - if l.needed--; l.needed == 0 { - // We've done everything we need to do for this level. - // Continue calculating one level up. Fill in nextPairFreq - // of that level with the sum of the two nodes we've just calculated on - // this level. - up := l.up - if up == nil { - // All done! - return - } - up.nextPairFreq = prevFreq + l.lastChain.freq - l = up - } else { - // If we stole from below, move down temporarily to replenish it. - for l.down.needed > 0 { - l = l.down - } - } - } -} - // Return the number of literals assigned to each bit size in the Huffman encoding // // This method is only called when list.length >= 3 @@ -195,7 +140,9 @@ func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 { // The tree can't have greater depth than n - 1, no matter what. This // saves a little bit of work in some small cases - maxBits = minInt32(maxBits, n-1) + if maxBits > n-1 { + maxBits = n - 1 + } // Create information about each of the levels. // A bogus "Level 0" whose sole purpose is so that diff --git a/src/pkg/compress/flate/inflate.go b/src/pkg/compress/flate/inflate.go index 3845f1204..3f2042bfe 100644 --- a/src/pkg/compress/flate/inflate.go +++ b/src/pkg/compress/flate/inflate.go @@ -10,7 +10,6 @@ package flate import ( "bufio" "io" - "os" "strconv" ) @@ -25,33 +24,33 @@ const ( // A CorruptInputError reports the presence of corrupt input at a given offset. type CorruptInputError int64 -func (e CorruptInputError) String() string { - return "flate: corrupt input before offset " + strconv.Itoa64(int64(e)) +func (e CorruptInputError) Error() string { + return "flate: corrupt input before offset " + strconv.FormatInt(int64(e), 10) } // An InternalError reports an error in the flate code itself. type InternalError string -func (e InternalError) String() string { return "flate: internal error: " + string(e) } +func (e InternalError) Error() string { return "flate: internal error: " + string(e) } // A ReadError reports an error encountered while reading input. type ReadError struct { - Offset int64 // byte offset where error occurred - Error os.Error // error returned by underlying Read + Offset int64 // byte offset where error occurred + Err error // error returned by underlying Read } -func (e *ReadError) String() string { - return "flate: read error at offset " + strconv.Itoa64(e.Offset) + ": " + e.Error.String() +func (e *ReadError) Error() string { + return "flate: read error at offset " + strconv.FormatInt(e.Offset, 10) + ": " + e.Err.Error() } // A WriteError reports an error encountered while writing output. type WriteError struct { - Offset int64 // byte offset where error occurred - Error os.Error // error returned by underlying Write + Offset int64 // byte offset where error occurred + Err error // error returned by underlying Write } -func (e *WriteError) String() string { - return "flate: write error at offset " + strconv.Itoa64(e.Offset) + ": " + e.Error.String() +func (e *WriteError) Error() string { + return "flate: write error at offset " + strconv.FormatInt(e.Offset, 10) + ": " + e.Err.Error() } // Huffman decoder is based on @@ -190,7 +189,7 @@ var fixedHuffmanDecoder = huffmanDecoder{ // the NewReader will introduce its own buffering. type Reader interface { io.Reader - ReadByte() (c byte, err os.Error) + ReadByte() (c byte, err error) } // Decompress state. @@ -224,7 +223,7 @@ type decompressor struct { // and decompression state. step func(*decompressor) final bool - err os.Error + err error toRead []byte hl, hd *huffmanDecoder copyLen int @@ -237,7 +236,7 @@ func (f *decompressor) nextBlock() { f.flush((*decompressor).nextBlock) return } - f.err = os.EOF + f.err = io.EOF return } for f.nb < 1+2 { @@ -272,7 +271,7 @@ func (f *decompressor) nextBlock() { } } -func (f *decompressor) Read(b []byte) (int, os.Error) { +func (f *decompressor) Read(b []byte) (int, error) { for { if len(f.toRead) > 0 { n := copy(b, f.toRead) @@ -287,8 +286,8 @@ func (f *decompressor) Read(b []byte) (int, os.Error) { panic("unreachable") } -func (f *decompressor) Close() os.Error { - if f.err == os.EOF { +func (f *decompressor) Close() error { + if f.err == io.EOF { return nil } return f.err @@ -299,7 +298,7 @@ func (f *decompressor) Close() os.Error { var codeOrder = [...]int{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} -func (f *decompressor) readHuffman() os.Error { +func (f *decompressor) readHuffman() error { // HLIT[5], HDIST[5], HCLEN[4]. for f.nb < 5+5+4 { if err := f.moreBits(); err != nil { @@ -625,10 +624,10 @@ func (f *decompressor) setDict(dict []byte) { f.hw = f.hp } -func (f *decompressor) moreBits() os.Error { +func (f *decompressor) moreBits() error { c, err := f.r.ReadByte() if err != nil { - if err == os.EOF { + if err == io.EOF { err = io.ErrUnexpectedEOF } return err @@ -640,7 +639,7 @@ func (f *decompressor) moreBits() os.Error { } // Read the next Huffman-encoded symbol from f according to h. -func (f *decompressor) huffSym(h *huffmanDecoder) (int, os.Error) { +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 { diff --git a/src/pkg/compress/flate/util.go b/src/pkg/compress/flate/util.go deleted file mode 100644 index aca5c78b2..000000000 --- a/src/pkg/compress/flate/util.go +++ /dev/null @@ -1,72 +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. - -package flate - -func min(left int, right int) int { - if left < right { - return left - } - return right -} - -func minInt32(left int32, right int32) int32 { - if left < right { - return left - } - return right -} - -func max(left int, right int) int { - if left > right { - return left - } - return right -} - -func fillInts(a []int, value int) { - for i := range a { - a[i] = value - } -} - -func fillInt32s(a []int32, value int32) { - for i := range a { - a[i] = value - } -} - -func fillBytes(a []byte, value byte) { - for i := range a { - a[i] = value - } -} - -func fillInt8s(a []int8, value int8) { - for i := range a { - a[i] = value - } -} - -func fillUint8s(a []uint8, value uint8) { - for i := range a { - a[i] = value - } -} - -func copyInt8s(dst []int8, src []int8) int { - cnt := min(len(dst), len(src)) - for i := 0; i < cnt; i++ { - dst[i] = src[i] - } - return cnt -} - -func copyUint8s(dst []uint8, src []uint8) int { - cnt := min(len(dst), len(src)) - for i := 0; i < cnt; i++ { - dst[i] = src[i] - } - return cnt -} diff --git a/src/pkg/compress/gzip/Makefile b/src/pkg/compress/gzip/Makefile deleted file mode 100644 index b671fc72c..000000000 --- a/src/pkg/compress/gzip/Makefile +++ /dev/null @@ -1,12 +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 ../../../Make.inc - -TARG=compress/gzip -GOFILES=\ - gunzip.go\ - gzip.go\ - -include ../../../Make.pkg diff --git a/src/pkg/compress/gzip/gunzip.go b/src/pkg/compress/gzip/gunzip.go index 6ac9293d7..33736f635 100644 --- a/src/pkg/compress/gzip/gunzip.go +++ b/src/pkg/compress/gzip/gunzip.go @@ -9,15 +9,13 @@ package gzip import ( "bufio" "compress/flate" + "errors" "hash" "hash/crc32" "io" - "os" + "time" ) -// BUG(nigeltao): Comments and Names don't properly map UTF-8 character codes outside of -// the 0x00-0x7f range to ISO 8859-1 (Latin-1). - const ( gzipID1 = 0x1f gzipID2 = 0x8b @@ -36,34 +34,38 @@ func makeReader(r io.Reader) flate.Reader { return bufio.NewReader(r) } -var HeaderError = os.NewError("invalid gzip header") -var ChecksumError = os.NewError("gzip checksum error") +var ( + // ErrChecksum is returned when reading GZIP data that has an invalid checksum. + ErrChecksum = errors.New("gzip: invalid checksum") + // ErrHeader is returned when reading GZIP data that has an invalid header. + ErrHeader = errors.New("gzip: invalid header") +) // The gzip file stores a header giving metadata about the compressed file. -// That header is exposed as the fields of the Compressor and Decompressor structs. +// That header is exposed as the fields of the Writer and Reader structs. type Header struct { - Comment string // comment - Extra []byte // "extra data" - Mtime uint32 // modification time (seconds since January 1, 1970) - Name string // file name - OS byte // operating system type + Comment string // comment + Extra []byte // "extra data" + ModTime time.Time // modification time + Name string // file name + OS byte // operating system type } -// An Decompressor is an io.Reader that can be read to retrieve +// A Reader is an io.Reader that can be read to retrieve // uncompressed data from a gzip-format compressed file. // // In general, a gzip file can be a concatenation of gzip files, -// each with its own header. Reads from the Decompressor +// each with its own header. Reads from the Reader // return the concatenation of the uncompressed data of each. -// Only the first header is recorded in the Decompressor fields. +// Only the first header is recorded in the Reader fields. // // Gzip files store a length and checksum of the uncompressed data. -// The Decompressor will return a ChecksumError when Read +// The Reader will return a ErrChecksum when Read // reaches the end of the uncompressed data if it does not // have the expected length or checksum. Clients should treat data -// returned by Read as tentative until they receive the successful -// (zero length, nil error) Read marking the end of the data. -type Decompressor struct { +// returned by Read as tentative until they receive the io.EOF +// marking the end of the data. +type Reader struct { Header r flate.Reader decompressor io.ReadCloser @@ -71,18 +73,17 @@ type Decompressor struct { size uint32 flg byte buf [512]byte - err os.Error + err error } -// NewReader creates a new Decompressor reading the given reader. +// NewReader creates a new Reader reading the given reader. // The implementation buffers input and may read more data than necessary from r. -// It is the caller's responsibility to call Close on the Decompressor when done. -func NewReader(r io.Reader) (*Decompressor, os.Error) { - z := new(Decompressor) +// It is the caller's responsibility to call Close on the Reader when done. +func NewReader(r io.Reader) (*Reader, error) { + z := new(Reader) z.r = makeReader(r) z.digest = crc32.NewIEEE() if err := z.readHeader(true); err != nil { - z.err = err return nil, err } return z, nil @@ -93,26 +94,36 @@ func get4(p []byte) uint32 { return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 } -func (z *Decompressor) readString() (string, os.Error) { - var err os.Error +func (z *Reader) readString() (string, error) { + var err error + needconv := false for i := 0; ; i++ { if i >= len(z.buf) { - return "", HeaderError + return "", ErrHeader } z.buf[i], err = z.r.ReadByte() if err != nil { return "", err } + if z.buf[i] > 0x7f { + needconv = true + } if z.buf[i] == 0 { // GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1). - // TODO(nigeltao): Convert from ISO 8859-1 (Latin-1) to UTF-8. + if needconv { + s := make([]rune, 0, i) + for _, v := range z.buf[0:i] { + s = append(s, rune(v)) + } + return string(s), nil + } return string(z.buf[0:i]), nil } } panic("not reached") } -func (z *Decompressor) read2() (uint32, os.Error) { +func (z *Reader) read2() (uint32, error) { _, err := io.ReadFull(z.r, z.buf[0:2]) if err != nil { return 0, err @@ -120,17 +131,17 @@ func (z *Decompressor) read2() (uint32, os.Error) { return uint32(z.buf[0]) | uint32(z.buf[1])<<8, nil } -func (z *Decompressor) readHeader(save bool) os.Error { +func (z *Reader) readHeader(save bool) error { _, err := io.ReadFull(z.r, z.buf[0:10]) if err != nil { return err } if z.buf[0] != gzipID1 || z.buf[1] != gzipID2 || z.buf[2] != gzipDeflate { - return HeaderError + return ErrHeader } z.flg = z.buf[3] if save { - z.Mtime = get4(z.buf[4:8]) + z.ModTime = time.Unix(int64(get4(z.buf[4:8])), 0) // z.buf[8] is xfl, ignored z.OS = z.buf[9] } @@ -177,7 +188,7 @@ func (z *Decompressor) readHeader(save bool) os.Error { } sum := z.digest.Sum32() & 0xFFFF if n != sum { - return HeaderError + return ErrHeader } } @@ -186,7 +197,7 @@ func (z *Decompressor) readHeader(save bool) os.Error { return nil } -func (z *Decompressor) Read(p []byte) (n int, err os.Error) { +func (z *Reader) Read(p []byte) (n int, err error) { if z.err != nil { return 0, z.err } @@ -197,7 +208,7 @@ func (z *Decompressor) Read(p []byte) (n int, err os.Error) { n, err = z.decompressor.Read(p) z.digest.Write(p[0:n]) z.size += uint32(n) - if n != 0 || err != os.EOF { + if n != 0 || err != io.EOF { z.err = err return } @@ -210,7 +221,7 @@ func (z *Decompressor) Read(p []byte) (n int, err os.Error) { crc32, isize := get4(z.buf[0:4]), get4(z.buf[4:8]) sum := z.digest.Sum32() if sum != crc32 || isize != z.size { - z.err = ChecksumError + z.err = ErrChecksum return 0, z.err } @@ -226,5 +237,5 @@ func (z *Decompressor) Read(p []byte) (n int, err os.Error) { return z.Read(p) } -// Calling Close does not close the wrapped io.Reader originally passed to NewReader. -func (z *Decompressor) Close() os.Error { return z.decompressor.Close() } +// Close closes the Reader. It does not close the underlying io.Reader. +func (z *Reader) Close() error { return z.decompressor.Close() } diff --git a/src/pkg/compress/gzip/gunzip_test.go b/src/pkg/compress/gzip/gunzip_test.go index 1c08c7374..a1333580d 100644 --- a/src/pkg/compress/gzip/gunzip_test.go +++ b/src/pkg/compress/gzip/gunzip_test.go @@ -7,7 +7,6 @@ package gzip import ( "bytes" "io" - "os" "testing" ) @@ -16,7 +15,7 @@ type gunzipTest struct { desc string raw string gzip []byte - err os.Error + err error } var gunzipTests = []gunzipTest{ @@ -233,7 +232,7 @@ var gunzipTests = []gunzipTest{ 0x02, 0x00, 0x2d, 0x3b, 0x08, 0xaf, 0x0c, 0x00, 0x00, 0x00, 'g', 'a', 'r', 'b', 'a', 'g', 'e', '!', '!', '!', }, - HeaderError, + ErrHeader, }, { // has 1 non-empty fixed huffman block not enough header "hello.txt", @@ -261,7 +260,7 @@ var gunzipTests = []gunzipTest{ 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, }, - ChecksumError, + ErrChecksum, }, { // has 1 non-empty fixed huffman block but corrupt size "hello.txt", @@ -275,7 +274,7 @@ var gunzipTests = []gunzipTest{ 0x02, 0x00, 0x2d, 0x3b, 0x08, 0xaf, 0xff, 0x00, 0x00, 0x00, }, - ChecksumError, + ErrChecksum, }, } diff --git a/src/pkg/compress/gzip/gzip.go b/src/pkg/compress/gzip/gzip.go index 8860d10af..3035dfffc 100644 --- a/src/pkg/compress/gzip/gzip.go +++ b/src/pkg/compress/gzip/gzip.go @@ -6,10 +6,11 @@ package gzip import ( "compress/flate" + "errors" + "fmt" "hash" "hash/crc32" "io" - "os" ) // These constants are copied from the flate package, so that code that imports @@ -21,9 +22,9 @@ const ( DefaultCompression = flate.DefaultCompression ) -// A Compressor is an io.WriteCloser that satisfies writes by compressing data written +// A Writer is an io.WriteCloser that satisfies writes by compressing data written // to its wrapped io.Writer. -type Compressor struct { +type Writer struct { Header w io.Writer level int @@ -32,28 +33,43 @@ type Compressor struct { size uint32 closed bool buf [10]byte - err os.Error + err error } -// NewWriter calls NewWriterLevel with the default compression level. -func NewWriter(w io.Writer) (*Compressor, os.Error) { - return NewWriterLevel(w, DefaultCompression) +// NewWriter creates a new Writer that satisfies writes by compressing data +// written to w. +// +// It is the caller's responsibility to call Close on the WriteCloser when done. +// Writes may be buffered and not flushed until Close. +// +// Callers that wish to set the fields in Writer.Header must do so before +// the first call to Write or Close. The Comment and Name header fields are +// UTF-8 strings in Go, but the underlying format requires NUL-terminated ISO +// 8859-1 (Latin-1). NUL or non-Latin-1 runes in those strings will lead to an +// error on Write. +func NewWriter(w io.Writer) *Writer { + z, _ := NewWriterLevel(w, DefaultCompression) + return z } -// NewWriterLevel creates a new Compressor writing to the given writer. -// Writes may be buffered and not flushed until Close. -// Callers that wish to set the fields in Compressor.Header must -// do so before the first call to Write or Close. -// It is the caller's responsibility to call Close on the WriteCloser when done. -// level is the compression level, which can be DefaultCompression, NoCompression, -// or any integer value between BestSpeed and BestCompression (inclusive). -func NewWriterLevel(w io.Writer, level int) (*Compressor, os.Error) { - z := new(Compressor) - z.OS = 255 // unknown - z.w = w - z.level = level - z.digest = crc32.NewIEEE() - return z, nil +// NewWriterLevel is like NewWriter but specifies the compression level instead +// of assuming DefaultCompression. +// +// The compression level can be DefaultCompression, NoCompression, or any +// integer value between BestSpeed and BestCompression inclusive. The error +// returned will be nil if the level is valid. +func NewWriterLevel(w io.Writer, level int) (*Writer, error) { + if level < DefaultCompression || level > BestCompression { + return nil, fmt.Errorf("gzip: invalid compression level: %d", level) + } + return &Writer{ + Header: Header{ + OS: 255, // unknown + }, + w: w, + level: level, + digest: crc32.NewIEEE(), + }, nil } // GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950). @@ -70,9 +86,9 @@ func put4(p []byte, v uint32) { } // writeBytes writes a length-prefixed byte slice to z.w. -func (z *Compressor) writeBytes(b []byte) os.Error { +func (z *Writer) writeBytes(b []byte) error { if len(b) > 0xffff { - return os.NewError("gzip.Write: Extra data is too large") + return errors.New("gzip.Write: Extra data is too large") } put2(z.buf[0:2], uint16(len(b))) _, err := z.w.Write(z.buf[0:2]) @@ -83,16 +99,28 @@ func (z *Compressor) writeBytes(b []byte) os.Error { return err } -// writeString writes a string (in ISO 8859-1 (Latin-1) format) to z.w. -func (z *Compressor) writeString(s string) os.Error { - // GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1). - // TODO(nigeltao): Convert from UTF-8 to ISO 8859-1 (Latin-1). +// writeString writes a UTF-8 string s in GZIP's format to z.w. +// GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1). +func (z *Writer) writeString(s string) (err error) { + // GZIP stores Latin-1 strings; error if non-Latin-1; convert if non-ASCII. + needconv := false for _, v := range s { - if v == 0 || v > 0x7f { - return os.NewError("gzip.Write: non-ASCII header string") + if v == 0 || v > 0xff { + return errors.New("gzip.Write: non-Latin-1 header string") + } + if v > 0x7f { + needconv = true + } + } + if needconv { + b := make([]byte, 0, len(s)) + for _, v := range s { + b = append(b, byte(v)) } + _, err = z.w.Write(b) + } else { + _, err = io.WriteString(z.w, s) } - _, err := io.WriteString(z.w, s) if err != nil { return err } @@ -102,7 +130,9 @@ func (z *Compressor) writeString(s string) os.Error { return err } -func (z *Compressor) Write(p []byte) (int, os.Error) { +// Write writes a compressed form of p to the underlying io.Writer. The +// compressed bytes are not necessarily flushed until the Writer is closed. +func (z *Writer) Write(p []byte) (int, error) { if z.err != nil { return 0, z.err } @@ -122,7 +152,7 @@ func (z *Compressor) Write(p []byte) (int, os.Error) { if z.Comment != "" { z.buf[3] |= 0x10 } - put4(z.buf[4:8], z.Mtime) + put4(z.buf[4:8], uint32(z.ModTime.Unix())) if z.level == BestCompression { z.buf[8] = 2 } else if z.level == BestSpeed { @@ -153,7 +183,7 @@ func (z *Compressor) Write(p []byte) (int, os.Error) { return n, z.err } } - z.compressor = flate.NewWriter(z.w, z.level) + z.compressor, _ = flate.NewWriter(z.w, z.level) } z.size += uint32(len(p)) z.digest.Write(p) @@ -161,8 +191,8 @@ func (z *Compressor) Write(p []byte) (int, os.Error) { return n, z.err } -// Calling Close does not close the wrapped io.Writer originally passed to NewWriter. -func (z *Compressor) Close() os.Error { +// Close closes the Writer. It does not close the underlying io.Writer. +func (z *Writer) Close() error { if z.err != nil { return z.err } diff --git a/src/pkg/compress/gzip/gzip_test.go b/src/pkg/compress/gzip/gzip_test.go index 121e627e6..6f7b59364 100644 --- a/src/pkg/compress/gzip/gzip_test.go +++ b/src/pkg/compress/gzip/gzip_test.go @@ -5,80 +5,155 @@ package gzip import ( - "io" + "bufio" + "bytes" "io/ioutil" "testing" + "time" ) -// pipe creates two ends of a pipe that gzip and gunzip, and runs dfunc at the -// writer end and cfunc at the reader end. -func pipe(t *testing.T, dfunc func(*Compressor), cfunc func(*Decompressor)) { - piper, pipew := io.Pipe() - defer piper.Close() - go func() { - defer pipew.Close() - compressor, err := NewWriter(pipew) - if err != nil { - t.Fatalf("%v", err) - } - defer compressor.Close() - dfunc(compressor) - }() - decompressor, err := NewReader(piper) +// TestEmpty tests that an empty payload still forms a valid GZIP stream. +func TestEmpty(t *testing.T) { + buf := new(bytes.Buffer) + + if err := NewWriter(buf).Close(); err != nil { + t.Fatalf("Writer.Close: %v", err) + } + + r, err := NewReader(buf) if err != nil { - t.Fatalf("%v", err) + t.Fatalf("NewReader: %v", err) + } + b, err := ioutil.ReadAll(r) + if err != nil { + t.Fatalf("ReadAll: %v", err) + } + if len(b) != 0 { + t.Fatalf("got %d bytes, want 0", len(b)) + } + if err := r.Close(); err != nil { + t.Fatalf("Reader.Close: %v", err) } - defer decompressor.Close() - cfunc(decompressor) } -// Tests that an empty payload still forms a valid GZIP stream. -func TestEmpty(t *testing.T) { - pipe(t, - func(compressor *Compressor) {}, - func(decompressor *Decompressor) { - b, err := ioutil.ReadAll(decompressor) - if err != nil { - t.Fatalf("%v", err) - } - if len(b) != 0 { - t.Fatalf("did not read an empty slice") - } - }) +// TestRoundTrip tests that gzipping and then gunzipping is the identity +// function. +func TestRoundTrip(t *testing.T) { + buf := new(bytes.Buffer) + + w := NewWriter(buf) + w.Comment = "comment" + w.Extra = []byte("extra") + w.ModTime = time.Unix(1e8, 0) + w.Name = "name" + if _, err := w.Write([]byte("payload")); err != nil { + t.Fatalf("Write: %v", err) + } + if err := w.Close(); err != nil { + t.Fatalf("Writer.Close: %v", err) + } + + r, err := NewReader(buf) + if err != nil { + t.Fatalf("NewReader: %v", err) + } + b, err := ioutil.ReadAll(r) + if err != nil { + t.Fatalf("ReadAll: %v", err) + } + if string(b) != "payload" { + t.Fatalf("payload is %q, want %q", string(b), "payload") + } + if r.Comment != "comment" { + t.Fatalf("comment is %q, want %q", r.Comment, "comment") + } + if string(r.Extra) != "extra" { + t.Fatalf("extra is %q, want %q", r.Extra, "extra") + } + if r.ModTime.Unix() != 1e8 { + t.Fatalf("mtime is %d, want %d", r.ModTime.Unix(), uint32(1e8)) + } + if r.Name != "name" { + t.Fatalf("name is %q, want %q", r.Name, "name") + } + if err := r.Close(); err != nil { + t.Fatalf("Reader.Close: %v", err) + } } -// Tests that gzipping and then gunzipping is the identity function. -func TestWriter(t *testing.T) { - pipe(t, - func(compressor *Compressor) { - compressor.Comment = "comment" - compressor.Extra = []byte("extra") - compressor.Mtime = 1e8 - compressor.Name = "name" - _, err := compressor.Write([]byte("payload")) - if err != nil { - t.Fatalf("%v", err) - } - }, - func(decompressor *Decompressor) { - b, err := ioutil.ReadAll(decompressor) - if err != nil { - t.Fatalf("%v", err) - } - if string(b) != "payload" { - t.Fatalf("payload is %q, want %q", string(b), "payload") - } - if decompressor.Comment != "comment" { - t.Fatalf("comment is %q, want %q", decompressor.Comment, "comment") - } - if string(decompressor.Extra) != "extra" { - t.Fatalf("extra is %q, want %q", decompressor.Extra, "extra") - } - if decompressor.Mtime != 1e8 { - t.Fatalf("mtime is %d, want %d", decompressor.Mtime, uint32(1e8)) - } - if decompressor.Name != "name" { - t.Fatalf("name is %q, want %q", decompressor.Name, "name") - } - }) +// TestLatin1 tests the internal functions for converting to and from Latin-1. +func TestLatin1(t *testing.T) { + latin1 := []byte{0xc4, 'u', 0xdf, 'e', 'r', 'u', 'n', 'g', 0} + utf8 := "Äußerung" + z := Reader{r: bufio.NewReader(bytes.NewBuffer(latin1))} + s, err := z.readString() + if err != nil { + t.Fatalf("readString: %v", err) + } + if s != utf8 { + t.Fatalf("read latin-1: got %q, want %q", s, utf8) + } + + buf := bytes.NewBuffer(make([]byte, 0, len(latin1))) + c := Writer{w: buf} + if err = c.writeString(utf8); err != nil { + t.Fatalf("writeString: %v", err) + } + s = buf.String() + if s != string(latin1) { + t.Fatalf("write utf-8: got %q, want %q", s, string(latin1)) + } +} + +// TestLatin1RoundTrip tests that metadata that is representable in Latin-1 +// survives a round trip. +func TestLatin1RoundTrip(t *testing.T) { + testCases := []struct { + name string + ok bool + }{ + {"", true}, + {"ASCII is OK", true}, + {"unless it contains a NUL\x00", false}, + {"no matter where \x00 occurs", false}, + {"\x00\x00\x00", false}, + {"Látin-1 also passes (U+00E1)", true}, + {"but LĀtin Extended-A (U+0100) does not", false}, + {"neither does 日本語", false}, + {"invalid UTF-8 also \xffails", false}, + {"\x00 as does Látin-1 with NUL", false}, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + + w := NewWriter(buf) + w.Name = tc.name + err := w.Close() + if (err == nil) != tc.ok { + t.Errorf("Writer.Close: name = %q, err = %v", tc.name, err) + continue + } + if !tc.ok { + continue + } + + r, err := NewReader(buf) + if err != nil { + t.Errorf("NewReader: %v", err) + continue + } + _, err = ioutil.ReadAll(r) + if err != nil { + t.Errorf("ReadAll: %v", err) + continue + } + if r.Name != tc.name { + t.Errorf("name is %q, want %q", r.Name, tc.name) + continue + } + if err := r.Close(); err != nil { + t.Errorf("Reader.Close: %v", err) + continue + } + } } diff --git a/src/pkg/compress/lzw/Makefile b/src/pkg/compress/lzw/Makefile deleted file mode 100644 index 28f5e6abc..000000000 --- a/src/pkg/compress/lzw/Makefile +++ /dev/null @@ -1,12 +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. - -include ../../../Make.inc - -TARG=compress/lzw -GOFILES=\ - reader.go\ - writer.go\ - -include ../../../Make.pkg diff --git a/src/pkg/compress/lzw/reader.go b/src/pkg/compress/lzw/reader.go index 21231c8e5..0ed742c89 100644 --- a/src/pkg/compress/lzw/reader.go +++ b/src/pkg/compress/lzw/reader.go @@ -16,9 +16,9 @@ package lzw import ( "bufio" + "errors" "fmt" "io" - "os" ) // Order specifies the bit ordering in an LZW data stream. @@ -45,9 +45,9 @@ type decoder struct { bits uint32 nBits uint width uint - read func(*decoder) (uint16, os.Error) // readLSB or readMSB - litWidth int // width in bits of literal codes - err os.Error + read func(*decoder) (uint16, error) // readLSB or readMSB + litWidth int // width in bits of literal codes + err error // The first 1< 0 { n := copy(b, d.toRead) @@ -132,7 +132,7 @@ func (d *decoder) decode() { for { code, err := d.read(d) if err != nil { - if err == os.EOF { + if err == io.EOF { err = io.ErrUnexpectedEOF } d.err = err @@ -156,7 +156,7 @@ func (d *decoder) decode() { continue case code == d.eof: d.flush() - d.err = os.EOF + d.err = io.EOF return case code <= d.hi: c, i := code, len(d.output)-1 @@ -186,7 +186,7 @@ func (d *decoder) decode() { d.prefix[d.hi] = d.last } default: - d.err = os.NewError("lzw: invalid code") + d.err = errors.New("lzw: invalid code") return } d.last, d.hi = code, d.hi+1 @@ -211,8 +211,10 @@ func (d *decoder) flush() { d.o = 0 } -func (d *decoder) Close() os.Error { - d.err = os.EINVAL // in case any Reads come along +var errClosed = errors.New("compress/lzw: reader/writer is closed") + +func (d *decoder) Close() error { + d.err = errClosed // in case any Reads come along return nil } @@ -230,7 +232,7 @@ func NewReader(r io.Reader, order Order, litWidth int) io.ReadCloser { case MSB: d.read = (*decoder).readMSB default: - d.err = os.NewError("lzw: unknown order") + d.err = errors.New("lzw: unknown order") return d } if litWidth < 2 || 8 < litWidth { diff --git a/src/pkg/compress/lzw/reader_test.go b/src/pkg/compress/lzw/reader_test.go index f8042b0d1..e5be12f54 100644 --- a/src/pkg/compress/lzw/reader_test.go +++ b/src/pkg/compress/lzw/reader_test.go @@ -8,7 +8,6 @@ import ( "bytes" "io" "io/ioutil" - "os" "runtime" "strconv" "strings" @@ -19,7 +18,7 @@ type lzwTest struct { desc string raw string compressed string - err os.Error + err error } var lzwTests = []lzwTest{ @@ -82,7 +81,7 @@ var lzwTests = []lzwTest{ } func TestReader(t *testing.T) { - b := bytes.NewBuffer(nil) + var b bytes.Buffer for _, tt := range lzwTests { d := strings.Split(tt.desc, ";") var order Order @@ -98,7 +97,7 @@ func TestReader(t *testing.T) { rc := NewReader(strings.NewReader(tt.compressed), order, litWidth) defer rc.Close() b.Reset() - n, err := io.Copy(b, rc) + n, err := io.Copy(&b, rc) if err != nil { if err != tt.err { t.Errorf("%s: io.Copy: %v want %v", tt.desc, err, tt.err) @@ -117,7 +116,7 @@ func benchmarkDecoder(b *testing.B, n int) { b.SetBytes(int64(n)) buf0, _ := ioutil.ReadFile("../testdata/e.txt") buf0 = buf0[:10000] - compressed := bytes.NewBuffer(nil) + compressed := new(bytes.Buffer) w := NewWriter(compressed, LSB, 8) for i := 0; i < n; i += len(buf0) { io.Copy(w, bytes.NewBuffer(buf0)) diff --git a/src/pkg/compress/lzw/writer.go b/src/pkg/compress/lzw/writer.go index 87143b7aa..488ba6428 100644 --- a/src/pkg/compress/lzw/writer.go +++ b/src/pkg/compress/lzw/writer.go @@ -6,27 +6,27 @@ package lzw import ( "bufio" + "errors" "fmt" "io" - "os" ) // A writer is a buffered, flushable writer. type writer interface { - WriteByte(byte) os.Error - Flush() os.Error + WriteByte(byte) error + Flush() error } // An errWriteCloser is an io.WriteCloser that always returns a given error. type errWriteCloser struct { - err os.Error + err error } -func (e *errWriteCloser) Write([]byte) (int, os.Error) { +func (e *errWriteCloser) Write([]byte) (int, error) { return 0, e.err } -func (e *errWriteCloser) Close() os.Error { +func (e *errWriteCloser) Close() error { return e.err } @@ -48,9 +48,10 @@ const ( type encoder struct { // w is the writer that compressed bytes are written to. w writer - // write, bits, nBits and width are the state for converting a code stream - // into a byte stream. - write func(*encoder, uint32) os.Error + // order, write, bits, nBits and width are the state for + // converting a code stream into a byte stream. + order Order + write func(*encoder, uint32) error bits uint32 nBits uint width uint @@ -63,8 +64,8 @@ type encoder struct { // call. It is equal to invalidCode if there was no such call. savedCode uint32 // err is the first error encountered during writing. Closing the encoder - // will make any future Write calls return os.EINVAL. - err os.Error + // will make any future Write calls return errClosed + err error // table is the hash table from 20-bit keys to 12-bit values. Each table // entry contains key<<12|val and collisions resolve by linear probing. // The keys consist of a 12-bit code prefix and an 8-bit byte suffix. @@ -73,7 +74,7 @@ type encoder struct { } // writeLSB writes the code c for "Least Significant Bits first" data. -func (e *encoder) writeLSB(c uint32) os.Error { +func (e *encoder) writeLSB(c uint32) error { e.bits |= c << e.nBits e.nBits += e.width for e.nBits >= 8 { @@ -87,7 +88,7 @@ func (e *encoder) writeLSB(c uint32) os.Error { } // writeMSB writes the code c for "Most Significant Bits first" data. -func (e *encoder) writeMSB(c uint32) os.Error { +func (e *encoder) writeMSB(c uint32) error { e.bits |= c << (32 - e.width - e.nBits) e.nBits += e.width for e.nBits >= 8 { @@ -102,12 +103,12 @@ func (e *encoder) writeMSB(c uint32) os.Error { // errOutOfCodes is an internal error that means that the encoder has run out // of unused codes and a clear code needs to be sent next. -var errOutOfCodes = os.NewError("lzw: out of codes") +var errOutOfCodes = errors.New("lzw: out of codes") // incHi increments e.hi and checks for both overflow and running out of // unused codes. In the latter case, incHi sends a clear code, resets the // encoder state and returns errOutOfCodes. -func (e *encoder) incHi() os.Error { +func (e *encoder) incHi() error { e.hi++ if e.hi == e.overflow { e.width++ @@ -130,7 +131,7 @@ func (e *encoder) incHi() os.Error { } // Write writes a compressed representation of p to e's underlying writer. -func (e *encoder) Write(p []byte) (int, os.Error) { +func (e *encoder) Write(p []byte) (int, error) { if e.err != nil { return 0, e.err } @@ -188,15 +189,15 @@ loop: // Close closes the encoder, flushing any pending output. It does not close or // flush e's underlying writer. -func (e *encoder) Close() os.Error { +func (e *encoder) Close() error { if e.err != nil { - if e.err == os.EINVAL { + if e.err == errClosed { return nil } return e.err } - // Make any future calls to Write return os.EINVAL. - e.err = os.EINVAL + // Make any future calls to Write return errClosed. + e.err = errClosed // Write the savedCode if valid. if e.savedCode != invalidCode { if err := e.write(e, e.savedCode); err != nil { @@ -213,7 +214,7 @@ func (e *encoder) Close() os.Error { } // Write the final bits. if e.nBits > 0 { - if e.write == (*encoder).writeMSB { + if e.order == MSB { e.bits >>= 24 } if err := e.w.WriteByte(uint8(e.bits)); err != nil { @@ -230,14 +231,14 @@ func (e *encoder) Close() os.Error { // The number of bits to use for literal codes, litWidth, must be in the // range [2,8] and is typically 8. func NewWriter(w io.Writer, order Order, litWidth int) io.WriteCloser { - var write func(*encoder, uint32) os.Error + var write func(*encoder, uint32) error switch order { case LSB: write = (*encoder).writeLSB case MSB: write = (*encoder).writeMSB default: - return &errWriteCloser{os.NewError("lzw: unknown order")} + return &errWriteCloser{errors.New("lzw: unknown order")} } if litWidth < 2 || 8 < litWidth { return &errWriteCloser{fmt.Errorf("lzw: litWidth %d out of range", litWidth)} @@ -249,6 +250,7 @@ func NewWriter(w io.Writer, order Order, litWidth int) io.WriteCloser { lw := uint(litWidth) return &encoder{ w: bw, + order: order, write: write, width: 1 + lw, litWidth: lw, diff --git a/src/pkg/compress/lzw/writer_test.go b/src/pkg/compress/lzw/writer_test.go index 4c5e522f9..d249a09b2 100644 --- a/src/pkg/compress/lzw/writer_test.go +++ b/src/pkg/compress/lzw/writer_test.go @@ -45,20 +45,16 @@ func testFile(t *testing.T, fn string, order Order, litWidth int) { var b [4096]byte for { n, err0 := raw.Read(b[:]) - if err0 != nil && err0 != os.EOF { + if err0 != nil && err0 != io.EOF { t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err0) return } _, err1 := lzww.Write(b[:n]) - if err1 == os.EPIPE { - // Fail, but do not report the error, as some other (presumably reportable) error broke the pipe. - return - } if err1 != nil { t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err1) return } - if err0 == os.EOF { + if err0 == io.EOF { break } } diff --git a/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt b/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt new file mode 100644 index 000000000..8d0ff4e65 --- /dev/null +++ b/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt @@ -0,0 +1,8858 @@ +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 +almost no restrictions whatsoever. You may copy it, give it away or +re-use it under the terms of the Project Gutenberg License included +with this eBook or online at www.gutenberg.net + + +Title: The Adventures of Tom Sawyer, Complete + +Author: Mark Twain (Samuel Clemens) + +Release Date: August 20, 2006 [EBook #74] +[Last updated: May 3, 2011] + +Language: English + + +*** START OF THIS PROJECT GUTENBERG EBOOK TOM SAWYER *** + + + + +Produced by David Widger. The previous edition was updated by Jose +Menendez. + + + + + + THE ADVENTURES OF TOM SAWYER + BY + MARK TWAIN + (Samuel Langhorne Clemens) + + + + + P R E F A C E + +MOST of the adventures recorded in this book really occurred; one or +two were experiences of my own, the rest those of boys who were +schoolmates of mine. Huck Finn is drawn from life; Tom Sawyer also, but +not from an individual--he is a combination of the characteristics of +three boys whom I knew, and therefore belongs to the composite order of +architecture. + +The odd superstitions touched upon were all prevalent among children +and slaves in the West at the period of this story--that is to say, +thirty or forty years ago. + +Although my book is intended mainly for the entertainment of boys and +girls, I hope it will not be shunned by men and women on that account, +for part of my plan has been to try to pleasantly remind adults of what +they once were themselves, and of how they felt and thought and talked, +and what queer enterprises they sometimes engaged in. + + THE AUTHOR. + +HARTFORD, 1876. + + + + T O M S A W Y E R + + + +CHAPTER I + +"TOM!" + +No answer. + +"TOM!" + +No answer. + +"What's gone with that boy, I wonder? You TOM!" + +No answer. + +The old lady pulled her spectacles down and looked over them about the +room; then she put them up and looked out under them. She seldom or +never looked THROUGH them for so small a thing as a boy; they were her +state pair, the pride of her heart, and were built for "style," not +service--she could have seen through a pair of stove-lids just as well. +She looked perplexed for a moment, and then said, not fiercely, but +still loud enough for the furniture to hear: + +"Well, I lay if I get hold of you I'll--" + +She did not finish, for by this time she was bending down and punching +under the bed with the broom, and so she needed breath to punctuate the +punches with. She resurrected nothing but the cat. + +"I never did see the beat of that boy!" + +She went to the open door and stood in it and looked out among the +tomato vines and "jimpson" weeds that constituted the garden. No Tom. +So she lifted up her voice at an angle calculated for distance and +shouted: + +"Y-o-u-u TOM!" + +There was a slight noise behind her and she turned just in time to +seize a small boy by the slack of his roundabout and arrest his flight. + +"There! I might 'a' thought of that closet. What you been doing in +there?" + +"Nothing." + +"Nothing! Look at your hands. And look at your mouth. What IS that +truck?" + +"I don't know, aunt." + +"Well, I know. It's jam--that's what it is. Forty times I've said if +you didn't let that jam alone I'd skin you. Hand me that switch." + +The switch hovered in the air--the peril was desperate-- + +"My! Look behind you, aunt!" + +The old lady whirled round, and snatched her skirts out of danger. The +lad fled on the instant, scrambled up the high board-fence, and +disappeared over it. + +His aunt Polly stood surprised a moment, and then broke into a gentle +laugh. + +"Hang the boy, can't I never learn anything? Ain't he played me tricks +enough like that for me to be looking out for him by this time? But old +fools is the biggest fools there is. Can't learn an old dog new tricks, +as the saying is. But my goodness, he never plays them alike, two days, +and how is a body to know what's coming? He 'pears to know just how +long he can torment me before I get my dander up, and he knows if he +can make out to put me off for a minute or make me laugh, it's all down +again and I can't hit him a lick. I ain't doing my duty by that boy, +and that's the Lord's truth, goodness knows. Spare the rod and spile +the child, as the Good Book says. I'm a laying up sin and suffering for +us both, I know. He's full of the Old Scratch, but laws-a-me! he's my +own dead sister's boy, poor thing, and I ain't got the heart to lash +him, somehow. Every time I let him off, my conscience does hurt me so, +and every time I hit him my old heart most breaks. Well-a-well, man +that is born of woman is of few days and full of trouble, as the +Scripture says, and I reckon it's so. He'll play hookey this evening, * +and [* Southwestern for "afternoon"] I'll just be obleeged to make him +work, to-morrow, to punish him. It's mighty hard to make him work +Saturdays, when all the boys is having holiday, but he hates work more +than he hates anything else, and I've GOT to do some of my duty by him, +or I'll be the ruination of the child." + +Tom did play hookey, and he had a very good time. He got back home +barely in season to help Jim, the small colored boy, saw next-day's +wood and split the kindlings before supper--at least he was there in +time to tell his adventures to Jim while Jim did three-fourths of the +work. Tom's younger brother (or rather half-brother) Sid was already +through with his part of the work (picking up chips), for he was a +quiet boy, and had no adventurous, troublesome ways. + +While Tom was eating his supper, and stealing sugar as opportunity +offered, Aunt Polly asked him questions that were full of guile, and +very deep--for she wanted to trap him into damaging revealments. Like +many other simple-hearted souls, it was her pet vanity to believe she +was endowed with a talent for dark and mysterious diplomacy, and she +loved to contemplate her most transparent devices as marvels of low +cunning. Said she: + +"Tom, it was middling warm in school, warn't it?" + +"Yes'm." + +"Powerful warm, warn't it?" + +"Yes'm." + +"Didn't you want to go in a-swimming, Tom?" + +A bit of a scare shot through Tom--a touch of uncomfortable suspicion. +He searched Aunt Polly's face, but it told him nothing. So he said: + +"No'm--well, not very much." + +The old lady reached out her hand and felt Tom's shirt, and said: + +"But you ain't too warm now, though." And it flattered her to reflect +that she had discovered that the shirt was dry without anybody knowing +that that was what she had in her mind. But in spite of her, Tom knew +where the wind lay, now. So he forestalled what might be the next move: + +"Some of us pumped on our heads--mine's damp yet. See?" + +Aunt Polly was vexed to think she had overlooked that bit of +circumstantial evidence, and missed a trick. Then she had a new +inspiration: + +"Tom, you didn't have to undo your shirt collar where I sewed it, to +pump on your head, did you? Unbutton your jacket!" + +The trouble vanished out of Tom's face. He opened his jacket. His +shirt collar was securely sewed. + +"Bother! Well, go 'long with you. I'd made sure you'd played hookey +and been a-swimming. But I forgive ye, Tom. I reckon you're a kind of a +singed cat, as the saying is--better'n you look. THIS time." + +She was half sorry her sagacity had miscarried, and half glad that Tom +had stumbled into obedient conduct for once. + +But Sidney said: + +"Well, now, if I didn't think you sewed his collar with white thread, +but it's black." + +"Why, I did sew it with white! Tom!" + +But Tom did not wait for the rest. As he went out at the door he said: + +"Siddy, I'll lick you for that." + +In a safe place Tom examined two large needles which were thrust into +the lapels of his jacket, and had thread bound about them--one needle +carried white thread and the other black. He said: + +"She'd never noticed if it hadn't been for Sid. Confound it! sometimes +she sews it with white, and sometimes she sews it with black. I wish to +geeminy she'd stick to one or t'other--I can't keep the run of 'em. But +I bet you I'll lam Sid for that. I'll learn him!" + +He was not the Model Boy of the village. He knew the model boy very +well though--and loathed him. + +Within two minutes, or even less, he had forgotten all his troubles. +Not because his troubles were one whit less heavy and bitter to him +than a man's are to a man, but because a new and powerful interest bore +them down and drove them out of his mind for the time--just as men's +misfortunes are forgotten in the excitement of new enterprises. This +new interest was a valued novelty in whistling, which he had just +acquired from a negro, and he was suffering to practise it undisturbed. +It consisted in a peculiar bird-like turn, a sort of liquid warble, +produced by touching the tongue to the roof of the mouth at short +intervals in the midst of the music--the reader probably remembers how +to do it, if he has ever been a boy. Diligence and attention soon gave +him the knack of it, and he strode down the street with his mouth full +of harmony and his soul full of gratitude. He felt much as an +astronomer feels who has discovered a new planet--no doubt, as far as +strong, deep, unalloyed pleasure is concerned, the advantage was with +the boy, not the astronomer. + +The summer evenings were long. It was not dark, yet. Presently Tom +checked his whistle. A stranger was before him--a boy a shade larger +than himself. A new-comer of any age or either sex was an impressive +curiosity in the poor little shabby village of St. Petersburg. This boy +was well dressed, too--well dressed on a week-day. This was simply +astounding. His cap was a dainty thing, his close-buttoned blue cloth +roundabout was new and natty, and so were his pantaloons. He had shoes +on--and it was only Friday. He even wore a necktie, a bright bit of +ribbon. He had a citified air about him that ate into Tom's vitals. The +more Tom stared at the splendid marvel, the higher he turned up his +nose at his finery and the shabbier and shabbier his own outfit seemed +to him to grow. Neither boy spoke. If one moved, the other moved--but +only sidewise, in a circle; they kept face to face and eye to eye all +the time. Finally Tom said: + +"I can lick you!" + +"I'd like to see you try it." + +"Well, I can do it." + +"No you can't, either." + +"Yes I can." + +"No you can't." + +"I can." + +"You can't." + +"Can!" + +"Can't!" + +An uncomfortable pause. Then Tom said: + +"What's your name?" + +"'Tisn't any of your business, maybe." + +"Well I 'low I'll MAKE it my business." + +"Well why don't you?" + +"If you say much, I will." + +"Much--much--MUCH. There now." + +"Oh, you think you're mighty smart, DON'T you? I could lick you with +one hand tied behind me, if I wanted to." + +"Well why don't you DO it? You SAY you can do it." + +"Well I WILL, if you fool with me." + +"Oh yes--I've seen whole families in the same fix." + +"Smarty! You think you're SOME, now, DON'T you? Oh, what a hat!" + +"You can lump that hat if you don't like it. I dare you to knock it +off--and anybody that'll take a dare will suck eggs." + +"You're a liar!" + +"You're another." + +"You're a fighting liar and dasn't take it up." + +"Aw--take a walk!" + +"Say--if you give me much more of your sass I'll take and bounce a +rock off'n your head." + +"Oh, of COURSE you will." + +"Well I WILL." + +"Well why don't you DO it then? What do you keep SAYING you will for? +Why don't you DO it? It's because you're afraid." + +"I AIN'T afraid." + +"You are." + +"I ain't." + +"You are." + +Another pause, and more eying and sidling around each other. Presently +they were shoulder to shoulder. Tom said: + +"Get away from here!" + +"Go away yourself!" + +"I won't." + +"I won't either." + +So they stood, each with a foot placed at an angle as a brace, and +both shoving with might and main, and glowering at each other with +hate. But neither could get an advantage. After struggling till both +were hot and flushed, each relaxed his strain with watchful caution, +and Tom said: + +"You're a coward and a pup. I'll tell my big brother on you, and he +can thrash you with his little finger, and I'll make him do it, too." + +"What do I care for your big brother? I've got a brother that's bigger +than he is--and what's more, he can throw him over that fence, too." +[Both brothers were imaginary.] + +"That's a lie." + +"YOUR saying so don't make it so." + +Tom drew a line in the dust with his big toe, and said: + +"I dare you to step over that, and I'll lick you till you can't stand +up. Anybody that'll take a dare will steal sheep." + +The new boy stepped over promptly, and said: + +"Now you said you'd do it, now let's see you do it." + +"Don't you crowd me now; you better look out." + +"Well, you SAID you'd do it--why don't you do it?" + +"By jingo! for two cents I WILL do it." + +The new boy took two broad coppers out of his pocket and held them out +with derision. Tom struck them to the ground. In an instant both boys +were rolling and tumbling in the dirt, gripped together like cats; and +for the space of a minute they tugged and tore at each other's hair and +clothes, punched and scratched each other's nose, and covered +themselves with dust and glory. Presently the confusion took form, and +through the fog of battle Tom appeared, seated astride the new boy, and +pounding him with his fists. "Holler 'nuff!" said he. + +The boy only struggled to free himself. He was crying--mainly from rage. + +"Holler 'nuff!"--and the pounding went on. + +At last the stranger got out a smothered "'Nuff!" and Tom let him up +and said: + +"Now that'll learn you. Better look out who you're fooling with next +time." + +The new boy went off brushing the dust from his clothes, sobbing, +snuffling, and occasionally looking back and shaking his head and +threatening what he would do to Tom the "next time he caught him out." +To which Tom responded with jeers, and started off in high feather, and +as soon as his back was turned the new boy snatched up a stone, threw +it and hit him between the shoulders and then turned tail and ran like +an antelope. Tom chased the traitor home, and thus found out where he +lived. He then held a position at the gate for some time, daring the +enemy to come outside, but the enemy only made faces at him through the +window and declined. At last the enemy's mother appeared, and called +Tom a bad, vicious, vulgar child, and ordered him away. So he went +away; but he said he "'lowed" to "lay" for that boy. + +He got home pretty late that night, and when he climbed cautiously in +at the window, he uncovered an ambuscade, in the person of his aunt; +and when she saw the state his clothes were in her resolution to turn +his Saturday holiday into captivity at hard labor became adamantine in +its firmness. + + + +CHAPTER II + +SATURDAY morning was come, and all the summer world was bright and +fresh, and brimming with life. There was a song in every heart; and if +the heart was young the music issued at the lips. There was cheer in +every face and a spring in every step. The locust-trees were in bloom +and the fragrance of the blossoms filled the air. Cardiff Hill, beyond +the village and above it, was green with vegetation and it lay just far +enough away to seem a Delectable Land, dreamy, reposeful, and inviting. + +Tom appeared on the sidewalk with a bucket of whitewash and a +long-handled brush. He surveyed the fence, and all gladness left him and +a deep melancholy settled down upon his spirit. Thirty yards of board +fence nine feet high. Life to him seemed hollow, and existence but a +burden. Sighing, he dipped his brush and passed it along the topmost +plank; repeated the operation; did it again; compared the insignificant +whitewashed streak with the far-reaching continent of unwhitewashed +fence, and sat down on a tree-box discouraged. Jim came skipping out at +the gate with a tin pail, and singing Buffalo Gals. Bringing water from +the town pump had always been hateful work in Tom's eyes, before, but +now it did not strike him so. He remembered that there was company at +the pump. White, mulatto, and negro boys and girls were always there +waiting their turns, resting, trading playthings, quarrelling, +fighting, skylarking. And he remembered that although the pump was only +a hundred and fifty yards off, Jim never got back with a bucket of +water under an hour--and even then somebody generally had to go after +him. Tom said: + +"Say, Jim, I'll fetch the water if you'll whitewash some." + +Jim shook his head and said: + +"Can't, Mars Tom. Ole missis, she tole me I got to go an' git dis +water an' not stop foolin' roun' wid anybody. She say she spec' Mars +Tom gwine to ax me to whitewash, an' so she tole me go 'long an' 'tend +to my own business--she 'lowed SHE'D 'tend to de whitewashin'." + +"Oh, never you mind what she said, Jim. That's the way she always +talks. Gimme the bucket--I won't be gone only a a minute. SHE won't +ever know." + +"Oh, I dasn't, Mars Tom. Ole missis she'd take an' tar de head off'n +me. 'Deed she would." + +"SHE! She never licks anybody--whacks 'em over the head with her +thimble--and who cares for that, I'd like to know. She talks awful, but +talk don't hurt--anyways it don't if she don't cry. Jim, I'll give you +a marvel. I'll give you a white alley!" + +Jim began to waver. + +"White alley, Jim! And it's a bully taw." + +"My! Dat's a mighty gay marvel, I tell you! But Mars Tom I's powerful +'fraid ole missis--" + +"And besides, if you will I'll show you my sore toe." + +Jim was only human--this attraction was too much for him. He put down +his pail, took the white alley, and bent over the toe with absorbing +interest while the bandage was being unwound. In another moment he was +flying down the street with his pail and a tingling rear, Tom was +whitewashing with vigor, and Aunt Polly was retiring from the field +with a slipper in her hand and triumph in her eye. + +But Tom's energy did not last. He began to think of the fun he had +planned for this day, and his sorrows multiplied. Soon the free boys +would come tripping along on all sorts of delicious expeditions, and +they would make a world of fun of him for having to work--the very +thought of it burnt him like fire. He got out his worldly wealth and +examined it--bits of toys, marbles, and trash; enough to buy an +exchange of WORK, maybe, but not half enough to buy so much as half an +hour of pure freedom. So he returned his straitened means to his +pocket, and gave up the idea of trying to buy the boys. At this dark +and hopeless moment an inspiration burst upon him! Nothing less than a +great, magnificent inspiration. + +He took up his brush and went tranquilly to work. Ben Rogers hove in +sight presently--the very boy, of all boys, whose ridicule he had been +dreading. Ben's gait was the hop-skip-and-jump--proof enough that his +heart was light and his anticipations high. He was eating an apple, and +giving a long, melodious whoop, at intervals, followed by a deep-toned +ding-dong-dong, ding-dong-dong, for he was personating a steamboat. As +he drew near, he slackened speed, took the middle of the street, leaned +far over to starboard and rounded to ponderously and with laborious +pomp and circumstance--for he was personating the Big Missouri, and +considered himself to be drawing nine feet of water. He was boat and +captain and engine-bells combined, so he had to imagine himself +standing on his own hurricane-deck giving the orders and executing them: + +"Stop her, sir! Ting-a-ling-ling!" The headway ran almost out, and he +drew up slowly toward the sidewalk. + +"Ship up to back! Ting-a-ling-ling!" His arms straightened and +stiffened down his sides. + +"Set her back on the stabboard! Ting-a-ling-ling! Chow! ch-chow-wow! +Chow!" His right hand, meantime, describing stately circles--for it was +representing a forty-foot wheel. + +"Let her go back on the labboard! Ting-a-lingling! Chow-ch-chow-chow!" +The left hand began to describe circles. + +"Stop the stabboard! Ting-a-ling-ling! Stop the labboard! Come ahead +on the stabboard! Stop her! Let your outside turn over slow! +Ting-a-ling-ling! Chow-ow-ow! Get out that head-line! LIVELY now! +Come--out with your spring-line--what're you about there! Take a turn +round that stump with the bight of it! Stand by that stage, now--let her +go! Done with the engines, sir! Ting-a-ling-ling! SH'T! S'H'T! SH'T!" +(trying the gauge-cocks). + +Tom went on whitewashing--paid no attention to the steamboat. Ben +stared a moment and then said: "Hi-YI! YOU'RE up a stump, ain't you!" + +No answer. Tom surveyed his last touch with the eye of an artist, then +he gave his brush another gentle sweep and surveyed the result, as +before. Ben ranged up alongside of him. Tom's mouth watered for the +apple, but he stuck to his work. Ben said: + +"Hello, old chap, you got to work, hey?" + +Tom wheeled suddenly and said: + +"Why, it's you, Ben! I warn't noticing." + +"Say--I'm going in a-swimming, I am. Don't you wish you could? But of +course you'd druther WORK--wouldn't you? Course you would!" + +Tom contemplated the boy a bit, and said: + +"What do you call work?" + +"Why, ain't THAT work?" + +Tom resumed his whitewashing, and answered carelessly: + +"Well, maybe it is, and maybe it ain't. All I know, is, it suits Tom +Sawyer." + +"Oh come, now, you don't mean to let on that you LIKE it?" + +The brush continued to move. + +"Like it? Well, I don't see why I oughtn't to like it. Does a boy get +a chance to whitewash a fence every day?" + +That put the thing in a new light. Ben stopped nibbling his apple. Tom +swept his brush daintily back and forth--stepped back to note the +effect--added a touch here and there--criticised the effect again--Ben +watching every move and getting more and more interested, more and more +absorbed. Presently he said: + +"Say, Tom, let ME whitewash a little." + +Tom considered, was about to consent; but he altered his mind: + +"No--no--I reckon it wouldn't hardly do, Ben. You see, Aunt Polly's +awful particular about this fence--right here on the street, you know +--but if it was the back fence I wouldn't mind and SHE wouldn't. Yes, +she's awful particular about this fence; it's got to be done very +careful; I reckon there ain't one boy in a thousand, maybe two +thousand, that can do it the way it's got to be done." + +"No--is that so? Oh come, now--lemme just try. Only just a little--I'd +let YOU, if you was me, Tom." + +"Ben, I'd like to, honest injun; but Aunt Polly--well, Jim wanted to +do it, but she wouldn't let him; Sid wanted to do it, and she wouldn't +let Sid. Now don't you see how I'm fixed? If you was to tackle this +fence and anything was to happen to it--" + +"Oh, shucks, I'll be just as careful. Now lemme try. Say--I'll give +you the core of my apple." + +"Well, here--No, Ben, now don't. I'm afeard--" + +"I'll give you ALL of it!" + +Tom gave up the brush with reluctance in his face, but alacrity in his +heart. And while the late steamer Big Missouri worked and sweated in +the sun, the retired artist sat on a barrel in the shade close by, +dangled his legs, munched his apple, and planned the slaughter of more +innocents. There was no lack of material; boys happened along every +little while; they came to jeer, but remained to whitewash. By the time +Ben was fagged out, Tom had traded the next chance to Billy Fisher for +a kite, in good repair; and when he played out, Johnny Miller bought in +for a dead rat and a string to swing it with--and so on, and so on, +hour after hour. And when the middle of the afternoon came, from being +a poor poverty-stricken boy in the morning, Tom was literally rolling +in wealth. He had besides the things before mentioned, twelve marbles, +part of a jews-harp, a piece of blue bottle-glass to look through, a +spool cannon, a key that wouldn't unlock anything, a fragment of chalk, +a glass stopper of a decanter, a tin soldier, a couple of tadpoles, six +fire-crackers, a kitten with only one eye, a brass doorknob, a +dog-collar--but no dog--the handle of a knife, four pieces of +orange-peel, and a dilapidated old window sash. + +He had had a nice, good, idle time all the while--plenty of company +--and the fence had three coats of whitewash on it! If he hadn't run out +of whitewash he would have bankrupted every boy in the village. + +Tom said to himself that it was not such a hollow world, after all. He +had discovered a great law of human action, without knowing it--namely, +that in order to make a man or a boy covet a thing, it is only +necessary to make the thing difficult to attain. If he had been a great +and wise philosopher, like the writer of this book, he would now have +comprehended that Work consists of whatever a body is OBLIGED to do, +and that Play consists of whatever a body is not obliged to do. And +this would help him to understand why constructing artificial flowers +or performing on a tread-mill is work, while rolling ten-pins or +climbing Mont Blanc is only amusement. There are wealthy gentlemen in +England who drive four-horse passenger-coaches twenty or thirty miles +on a daily line, in the summer, because the privilege costs them +considerable money; but if they were offered wages for the service, +that would turn it into work and then they would resign. + +The boy mused awhile over the substantial change which had taken place +in his worldly circumstances, and then wended toward headquarters to +report. + + + +CHAPTER III + +TOM presented himself before Aunt Polly, who was sitting by an open +window in a pleasant rearward apartment, which was bedroom, +breakfast-room, dining-room, and library, combined. The balmy summer +air, the restful quiet, the odor of the flowers, and the drowsing murmur +of the bees had had their effect, and she was nodding over her knitting +--for she had no company but the cat, and it was asleep in her lap. Her +spectacles were propped up on her gray head for safety. She had thought +that of course Tom had deserted long ago, and she wondered at seeing him +place himself in her power again in this intrepid way. He said: "Mayn't +I go and play now, aunt?" + +"What, a'ready? How much have you done?" + +"It's all done, aunt." + +"Tom, don't lie to me--I can't bear it." + +"I ain't, aunt; it IS all done." + +Aunt Polly placed small trust in such evidence. She went out to see +for herself; and she would have been content to find twenty per cent. +of Tom's statement true. When she found the entire fence whitewashed, +and not only whitewashed but elaborately coated and recoated, and even +a streak added to the ground, her astonishment was almost unspeakable. +She said: + +"Well, I never! There's no getting round it, you can work when you're +a mind to, Tom." And then she diluted the compliment by adding, "But +it's powerful seldom you're a mind to, I'm bound to say. Well, go 'long +and play; but mind you get back some time in a week, or I'll tan you." + +She was so overcome by the splendor of his achievement that she took +him into the closet and selected a choice apple and delivered it to +him, along with an improving lecture upon the added value and flavor a +treat took to itself when it came without sin through virtuous effort. +And while she closed with a happy Scriptural flourish, he "hooked" a +doughnut. + +Then he skipped out, and saw Sid just starting up the outside stairway +that led to the back rooms on the second floor. Clods were handy and +the air was full of them in a twinkling. They raged around Sid like a +hail-storm; and before Aunt Polly could collect her surprised faculties +and sally to the rescue, six or seven clods had taken personal effect, +and Tom was over the fence and gone. There was a gate, but as a general +thing he was too crowded for time to make use of it. His soul was at +peace, now that he had settled with Sid for calling attention to his +black thread and getting him into trouble. + +Tom skirted the block, and came round into a muddy alley that led by +the back of his aunt's cow-stable. He presently got safely beyond the +reach of capture and punishment, and hastened toward the public square +of the village, where two "military" companies of boys had met for +conflict, according to previous appointment. Tom was General of one of +these armies, Joe Harper (a bosom friend) General of the other. These +two great commanders did not condescend to fight in person--that being +better suited to the still smaller fry--but sat together on an eminence +and conducted the field operations by orders delivered through +aides-de-camp. Tom's army won a great victory, after a long and +hard-fought battle. Then the dead were counted, prisoners exchanged, +the terms of the next disagreement agreed upon, and the day for the +necessary battle appointed; after which the armies fell into line and +marched away, and Tom turned homeward alone. + +As he was passing by the house where Jeff Thatcher lived, he saw a new +girl in the garden--a lovely little blue-eyed creature with yellow hair +plaited into two long-tails, white summer frock and embroidered +pantalettes. The fresh-crowned hero fell without firing a shot. A +certain Amy Lawrence vanished out of his heart and left not even a +memory of herself behind. He had thought he loved her to distraction; +he had regarded his passion as adoration; and behold it was only a poor +little evanescent partiality. He had been months winning her; she had +confessed hardly a week ago; he had been the happiest and the proudest +boy in the world only seven short days, and here in one instant of time +she had gone out of his heart like a casual stranger whose visit is +done. + +He worshipped this new angel with furtive eye, till he saw that she +had discovered him; then he pretended he did not know she was present, +and began to "show off" in all sorts of absurd boyish ways, in order to +win her admiration. He kept up this grotesque foolishness for some +time; but by-and-by, while he was in the midst of some dangerous +gymnastic performances, he glanced aside and saw that the little girl +was wending her way toward the house. Tom came up to the fence and +leaned on it, grieving, and hoping she would tarry yet awhile longer. +She halted a moment on the steps and then moved toward the door. Tom +heaved a great sigh as she put her foot on the threshold. But his face +lit up, right away, for she tossed a pansy over the fence a moment +before she disappeared. + +The boy ran around and stopped within a foot or two of the flower, and +then shaded his eyes with his hand and began to look down street as if +he had discovered something of interest going on in that direction. +Presently he picked up a straw and began trying to balance it on his +nose, with his head tilted far back; and as he moved from side to side, +in his efforts, he edged nearer and nearer toward the pansy; finally +his bare foot rested upon it, his pliant toes closed upon it, and he +hopped away with the treasure and disappeared round the corner. But +only for a minute--only while he could button the flower inside his +jacket, next his heart--or next his stomach, possibly, for he was not +much posted in anatomy, and not hypercritical, anyway. + +He returned, now, and hung about the fence till nightfall, "showing +off," as before; but the girl never exhibited herself again, though Tom +comforted himself a little with the hope that she had been near some +window, meantime, and been aware of his attentions. Finally he strode +home reluctantly, with his poor head full of visions. + +All through supper his spirits were so high that his aunt wondered +"what had got into the child." He took a good scolding about clodding +Sid, and did not seem to mind it in the least. He tried to steal sugar +under his aunt's very nose, and got his knuckles rapped for it. He said: + +"Aunt, you don't whack Sid when he takes it." + +"Well, Sid don't torment a body the way you do. You'd be always into +that sugar if I warn't watching you." + +Presently she stepped into the kitchen, and Sid, happy in his +immunity, reached for the sugar-bowl--a sort of glorying over Tom which +was wellnigh unbearable. But Sid's fingers slipped and the bowl dropped +and broke. Tom was in ecstasies. In such ecstasies that he even +controlled his tongue and was silent. He said to himself that he would +not speak a word, even when his aunt came in, but would sit perfectly +still till she asked who did the mischief; and then he would tell, and +there would be nothing so good in the world as to see that pet model +"catch it." He was so brimful of exultation that he could hardly hold +himself when the old lady came back and stood above the wreck +discharging lightnings of wrath from over her spectacles. He said to +himself, "Now it's coming!" And the next instant he was sprawling on +the floor! The potent palm was uplifted to strike again when Tom cried +out: + +"Hold on, now, what 'er you belting ME for?--Sid broke it!" + +Aunt Polly paused, perplexed, and Tom looked for healing pity. But +when she got her tongue again, she only said: + +"Umf! Well, you didn't get a lick amiss, I reckon. You been into some +other audacious mischief when I wasn't around, like enough." + +Then her conscience reproached her, and she yearned to say something +kind and loving; but she judged that this would be construed into a +confession that she had been in the wrong, and discipline forbade that. +So she kept silence, and went about her affairs with a troubled heart. +Tom sulked in a corner and exalted his woes. He knew that in her heart +his aunt was on her knees to him, and he was morosely gratified by the +consciousness of it. He would hang out no signals, he would take notice +of none. He knew that a yearning glance fell upon him, now and then, +through a film of tears, but he refused recognition of it. He pictured +himself lying sick unto death and his aunt bending over him beseeching +one little forgiving word, but he would turn his face to the wall, and +die with that word unsaid. Ah, how would she feel then? And he pictured +himself brought home from the river, dead, with his curls all wet, and +his sore heart at rest. How she would throw herself upon him, and how +her tears would fall like rain, and her lips pray God to give her back +her boy and she would never, never abuse him any more! But he would lie +there cold and white and make no sign--a poor little sufferer, whose +griefs were at an end. He so worked upon his feelings with the pathos +of these dreams, that he had to keep swallowing, he was so like to +choke; and his eyes swam in a blur of water, which overflowed when he +winked, and ran down and trickled from the end of his nose. And such a +luxury to him was this petting of his sorrows, that he could not bear +to have any worldly cheeriness or any grating delight intrude upon it; +it was too sacred for such contact; and so, presently, when his cousin +Mary danced in, all alive with the joy of seeing home again after an +age-long visit of one week to the country, he got up and moved in +clouds and darkness out at one door as she brought song and sunshine in +at the other. + +He wandered far from the accustomed haunts of boys, and sought +desolate places that were in harmony with his spirit. A log raft in the +river invited him, and he seated himself on its outer edge and +contemplated the dreary vastness of the stream, wishing, the while, +that he could only be drowned, all at once and unconsciously, without +undergoing the uncomfortable routine devised by nature. Then he thought +of his flower. He got it out, rumpled and wilted, and it mightily +increased his dismal felicity. He wondered if she would pity him if she +knew? Would she cry, and wish that she had a right to put her arms +around his neck and comfort him? Or would she turn coldly away like all +the hollow world? This picture brought such an agony of pleasurable +suffering that he worked it over and over again in his mind and set it +up in new and varied lights, till he wore it threadbare. At last he +rose up sighing and departed in the darkness. + +About half-past nine or ten o'clock he came along the deserted street +to where the Adored Unknown lived; he paused a moment; no sound fell +upon his listening ear; a candle was casting a dull glow upon the +curtain of a second-story window. Was the sacred presence there? He +climbed the fence, threaded his stealthy way through the plants, till +he stood under that window; he looked up at it long, and with emotion; +then he laid him down on the ground under it, disposing himself upon +his back, with his hands clasped upon his breast and holding his poor +wilted flower. And thus he would die--out in the cold world, with no +shelter over his homeless head, no friendly hand to wipe the +death-damps from his brow, no loving face to bend pityingly over him +when the great agony came. And thus SHE would see him when she looked +out upon the glad morning, and oh! would she drop one little tear upon +his poor, lifeless form, would she heave one little sigh to see a bright +young life so rudely blighted, so untimely cut down? + +The window went up, a maid-servant's discordant voice profaned the +holy calm, and a deluge of water drenched the prone martyr's remains! + +The strangling hero sprang up with a relieving snort. There was a whiz +as of a missile in the air, mingled with the murmur of a curse, a sound +as of shivering glass followed, and a small, vague form went over the +fence and shot away in the gloom. + +Not long after, as Tom, all undressed for bed, was surveying his +drenched garments by the light of a tallow dip, Sid woke up; but if he +had any dim idea of making any "references to allusions," he thought +better of it and held his peace, for there was danger in Tom's eye. + +Tom turned in without the added vexation of prayers, and Sid made +mental note of the omission. + + + +CHAPTER IV + +THE sun rose upon a tranquil world, and beamed down upon the peaceful +village like a benediction. Breakfast over, Aunt Polly had family +worship: it began with a prayer built from the ground up of solid +courses of Scriptural quotations, welded together with a thin mortar of +originality; and from the summit of this she delivered a grim chapter +of the Mosaic Law, as from Sinai. + +Then Tom girded up his loins, so to speak, and went to work to "get +his verses." Sid had learned his lesson days before. Tom bent all his +energies to the memorizing of five verses, and he chose part of the +Sermon on the Mount, because he could find no verses that were shorter. +At the end of half an hour Tom had a vague general idea of his lesson, +but no more, for his mind was traversing the whole field of human +thought, and his hands were busy with distracting recreations. Mary +took his book to hear him recite, and he tried to find his way through +the fog: + +"Blessed are the--a--a--" + +"Poor"-- + +"Yes--poor; blessed are the poor--a--a--" + +"In spirit--" + +"In spirit; blessed are the poor in spirit, for they--they--" + +"THEIRS--" + +"For THEIRS. Blessed are the poor in spirit, for theirs is the kingdom +of heaven. Blessed are they that mourn, for they--they--" + +"Sh--" + +"For they--a--" + +"S, H, A--" + +"For they S, H--Oh, I don't know what it is!" + +"SHALL!" + +"Oh, SHALL! for they shall--for they shall--a--a--shall mourn--a--a-- +blessed are they that shall--they that--a--they that shall mourn, for +they shall--a--shall WHAT? Why don't you tell me, Mary?--what do you +want to be so mean for?" + +"Oh, Tom, you poor thick-headed thing, I'm not teasing you. I wouldn't +do that. You must go and learn it again. Don't you be discouraged, Tom, +you'll manage it--and if you do, I'll give you something ever so nice. +There, now, that's a good boy." + +"All right! What is it, Mary, tell me what it is." + +"Never you mind, Tom. You know if I say it's nice, it is nice." + +"You bet you that's so, Mary. All right, I'll tackle it again." + +And he did "tackle it again"--and under the double pressure of +curiosity and prospective gain he did it with such spirit that he +accomplished a shining success. Mary gave him a brand-new "Barlow" +knife worth twelve and a half cents; and the convulsion of delight that +swept his system shook him to his foundations. True, the knife would +not cut anything, but it was a "sure-enough" Barlow, and there was +inconceivable grandeur in that--though where the Western boys ever got +the idea that such a weapon could possibly be counterfeited to its +injury is an imposing mystery and will always remain so, perhaps. Tom +contrived to scarify the cupboard with it, and was arranging to begin +on the bureau, when he was called off to dress for Sunday-school. + +Mary gave him a tin basin of water and a piece of soap, and he went +outside the door and set the basin on a little bench there; then he +dipped the soap in the water and laid it down; turned up his sleeves; +poured out the water on the ground, gently, and then entered the +kitchen and began to wipe his face diligently on the towel behind the +door. But Mary removed the towel and said: + +"Now ain't you ashamed, Tom. You mustn't be so bad. Water won't hurt +you." + +Tom was a trifle disconcerted. The basin was refilled, and this time +he stood over it a little while, gathering resolution; took in a big +breath and began. When he entered the kitchen presently, with both eyes +shut and groping for the towel with his hands, an honorable testimony +of suds and water was dripping from his face. But when he emerged from +the towel, he was not yet satisfactory, for the clean territory stopped +short at his chin and his jaws, like a mask; below and beyond this line +there was a dark expanse of unirrigated soil that spread downward in +front and backward around his neck. Mary took him in hand, and when she +was done with him he was a man and a brother, without distinction of +color, and his saturated hair was neatly brushed, and its short curls +wrought into a dainty and symmetrical general effect. [He privately +smoothed out the curls, with labor and difficulty, and plastered his +hair close down to his head; for he held curls to be effeminate, and +his own filled his life with bitterness.] Then Mary got out a suit of +his clothing that had been used only on Sundays during two years--they +were simply called his "other clothes"--and so by that we know the +size of his wardrobe. The girl "put him to rights" after he had dressed +himself; she buttoned his neat roundabout up to his chin, turned his +vast shirt collar down over his shoulders, brushed him off and crowned +him with his speckled straw hat. He now looked exceedingly improved and +uncomfortable. He was fully as uncomfortable as he looked; for there +was a restraint about whole clothes and cleanliness that galled him. He +hoped that Mary would forget his shoes, but the hope was blighted; she +coated them thoroughly with tallow, as was the custom, and brought them +out. He lost his temper and said he was always being made to do +everything he didn't want to do. But Mary said, persuasively: + +"Please, Tom--that's a good boy." + +So he got into the shoes snarling. Mary was soon ready, and the three +children set out for Sunday-school--a place that Tom hated with his +whole heart; but Sid and Mary were fond of it. + +Sabbath-school hours were from nine to half-past ten; and then church +service. Two of the children always remained for the sermon +voluntarily, and the other always remained too--for stronger reasons. +The church's high-backed, uncushioned pews would seat about three +hundred persons; the edifice was but a small, plain affair, with a sort +of pine board tree-box on top of it for a steeple. At the door Tom +dropped back a step and accosted a Sunday-dressed comrade: + +"Say, Billy, got a yaller ticket?" + +"Yes." + +"What'll you take for her?" + +"What'll you give?" + +"Piece of lickrish and a fish-hook." + +"Less see 'em." + +Tom exhibited. They were satisfactory, and the property changed hands. +Then Tom traded a couple of white alleys for three red tickets, and +some small trifle or other for a couple of blue ones. He waylaid other +boys as they came, and went on buying tickets of various colors ten or +fifteen minutes longer. He entered the church, now, with a swarm of +clean and noisy boys and girls, proceeded to his seat and started a +quarrel with the first boy that came handy. The teacher, a grave, +elderly man, interfered; then turned his back a moment and Tom pulled a +boy's hair in the next bench, and was absorbed in his book when the boy +turned around; stuck a pin in another boy, presently, in order to hear +him say "Ouch!" and got a new reprimand from his teacher. Tom's whole +class were of a pattern--restless, noisy, and troublesome. When they +came to recite their lessons, not one of them knew his verses +perfectly, but had to be prompted all along. However, they worried +through, and each got his reward--in small blue tickets, each with a +passage of Scripture on it; each blue ticket was pay for two verses of +the recitation. Ten blue tickets equalled a red one, and could be +exchanged for it; ten red tickets equalled a yellow one; for ten yellow +tickets the superintendent gave a very plainly bound Bible (worth forty +cents in those easy times) to the pupil. How many of my readers would +have the industry and application to memorize two thousand verses, even +for a Dore Bible? And yet Mary had acquired two Bibles in this way--it +was the patient work of two years--and a boy of German parentage had +won four or five. He once recited three thousand verses without +stopping; but the strain upon his mental faculties was too great, and +he was little better than an idiot from that day forth--a grievous +misfortune for the school, for on great occasions, before company, the +superintendent (as Tom expressed it) had always made this boy come out +and "spread himself." Only the older pupils managed to keep their +tickets and stick to their tedious work long enough to get a Bible, and +so the delivery of one of these prizes was a rare and noteworthy +circumstance; the successful pupil was so great and conspicuous for +that day that on the spot every scholar's heart was fired with a fresh +ambition that often lasted a couple of weeks. It is possible that Tom's +mental stomach had never really hungered for one of those prizes, but +unquestionably his entire being had for many a day longed for the glory +and the eclat that came with it. + +In due course the superintendent stood up in front of the pulpit, with +a closed hymn-book in his hand and his forefinger inserted between its +leaves, and commanded attention. When a Sunday-school superintendent +makes his customary little speech, a hymn-book in the hand is as +necessary as is the inevitable sheet of music in the hand of a singer +who stands forward on the platform and sings a solo at a concert +--though why, is a mystery: for neither the hymn-book nor the sheet of +music is ever referred to by the sufferer. This superintendent was a +slim creature of thirty-five, with a sandy goatee and short sandy hair; +he wore a stiff standing-collar whose upper edge almost reached his +ears and whose sharp points curved forward abreast the corners of his +mouth--a fence that compelled a straight lookout ahead, and a turning +of the whole body when a side view was required; his chin was propped +on a spreading cravat which was as broad and as long as a bank-note, +and had fringed ends; his boot toes were turned sharply up, in the +fashion of the day, like sleigh-runners--an effect patiently and +laboriously produced by the young men by sitting with their toes +pressed against a wall for hours together. Mr. Walters was very earnest +of mien, and very sincere and honest at heart; and he held sacred +things and places in such reverence, and so separated them from worldly +matters, that unconsciously to himself his Sunday-school voice had +acquired a peculiar intonation which was wholly absent on week-days. He +began after this fashion: + +"Now, children, I want you all to sit up just as straight and pretty +as you can and give me all your attention for a minute or two. There +--that is it. That is the way good little boys and girls should do. I see +one little girl who is looking out of the window--I am afraid she +thinks I am out there somewhere--perhaps up in one of the trees making +a speech to the little birds. [Applausive titter.] I want to tell you +how good it makes me feel to see so many bright, clean little faces +assembled in a place like this, learning to do right and be good." And +so forth and so on. It is not necessary to set down the rest of the +oration. It was of a pattern which does not vary, and so it is familiar +to us all. + +The latter third of the speech was marred by the resumption of fights +and other recreations among certain of the bad boys, and by fidgetings +and whisperings that extended far and wide, washing even to the bases +of isolated and incorruptible rocks like Sid and Mary. But now every +sound ceased suddenly, with the subsidence of Mr. Walters' voice, and +the conclusion of the speech was received with a burst of silent +gratitude. + +A good part of the whispering had been occasioned by an event which +was more or less rare--the entrance of visitors: lawyer Thatcher, +accompanied by a very feeble and aged man; a fine, portly, middle-aged +gentleman with iron-gray hair; and a dignified lady who was doubtless +the latter's wife. The lady was leading a child. Tom had been restless +and full of chafings and repinings; conscience-smitten, too--he could +not meet Amy Lawrence's eye, he could not brook her loving gaze. But +when he saw this small new-comer his soul was all ablaze with bliss in +a moment. The next moment he was "showing off" with all his might +--cuffing boys, pulling hair, making faces--in a word, using every art +that seemed likely to fascinate a girl and win her applause. His +exaltation had but one alloy--the memory of his humiliation in this +angel's garden--and that record in sand was fast washing out, under +the waves of happiness that were sweeping over it now. + +The visitors were given the highest seat of honor, and as soon as Mr. +Walters' speech was finished, he introduced them to the school. The +middle-aged man turned out to be a prodigious personage--no less a one +than the county judge--altogether the most august creation these +children had ever looked upon--and they wondered what kind of material +he was made of--and they half wanted to hear him roar, and were half +afraid he might, too. He was from Constantinople, twelve miles away--so +he had travelled, and seen the world--these very eyes had looked upon +the county court-house--which was said to have a tin roof. The awe +which these reflections inspired was attested by the impressive silence +and the ranks of staring eyes. This was the great Judge Thatcher, +brother of their own lawyer. Jeff Thatcher immediately went forward, to +be familiar with the great man and be envied by the school. It would +have been music to his soul to hear the whisperings: + +"Look at him, Jim! He's a going up there. Say--look! he's a going to +shake hands with him--he IS shaking hands with him! By jings, don't you +wish you was Jeff?" + +Mr. Walters fell to "showing off," with all sorts of official +bustlings and activities, giving orders, delivering judgments, +discharging directions here, there, everywhere that he could find a +target. The librarian "showed off"--running hither and thither with his +arms full of books and making a deal of the splutter and fuss that +insect authority delights in. The young lady teachers "showed off" +--bending sweetly over pupils that were lately being boxed, lifting +pretty warning fingers at bad little boys and patting good ones +lovingly. The young gentlemen teachers "showed off" with small +scoldings and other little displays of authority and fine attention to +discipline--and most of the teachers, of both sexes, found business up +at the library, by the pulpit; and it was business that frequently had +to be done over again two or three times (with much seeming vexation). +The little girls "showed off" in various ways, and the little boys +"showed off" with such diligence that the air was thick with paper wads +and the murmur of scufflings. And above it all the great man sat and +beamed a majestic judicial smile upon all the house, and warmed himself +in the sun of his own grandeur--for he was "showing off," too. + +There was only one thing wanting to make Mr. Walters' ecstasy +complete, and that was a chance to deliver a Bible-prize and exhibit a +prodigy. Several pupils had a few yellow tickets, but none had enough +--he had been around among the star pupils inquiring. He would have given +worlds, now, to have that German lad back again with a sound mind. + +And now at this moment, when hope was dead, Tom Sawyer came forward +with nine yellow tickets, nine red tickets, and ten blue ones, and +demanded a Bible. This was a thunderbolt out of a clear sky. Walters +was not expecting an application from this source for the next ten +years. But there was no getting around it--here were the certified +checks, and they were good for their face. Tom was therefore elevated +to a place with the Judge and the other elect, and the great news was +announced from headquarters. It was the most stunning surprise of the +decade, and so profound was the sensation that it lifted the new hero +up to the judicial one's altitude, and the school had two marvels to +gaze upon in place of one. The boys were all eaten up with envy--but +those that suffered the bitterest pangs were those who perceived too +late that they themselves had contributed to this hated splendor by +trading tickets to Tom for the wealth he had amassed in selling +whitewashing privileges. These despised themselves, as being the dupes +of a wily fraud, a guileful snake in the grass. + +The prize was delivered to Tom with as much effusion as the +superintendent could pump up under the circumstances; but it lacked +somewhat of the true gush, for the poor fellow's instinct taught him +that there was a mystery here that could not well bear the light, +perhaps; it was simply preposterous that this boy had warehoused two +thousand sheaves of Scriptural wisdom on his premises--a dozen would +strain his capacity, without a doubt. + +Amy Lawrence was proud and glad, and she tried to make Tom see it in +her face--but he wouldn't look. She wondered; then she was just a grain +troubled; next a dim suspicion came and went--came again; she watched; +a furtive glance told her worlds--and then her heart broke, and she was +jealous, and angry, and the tears came and she hated everybody. Tom +most of all (she thought). + +Tom was introduced to the Judge; but his tongue was tied, his breath +would hardly come, his heart quaked--partly because of the awful +greatness of the man, but mainly because he was her parent. He would +have liked to fall down and worship him, if it were in the dark. The +Judge put his hand on Tom's head and called him a fine little man, and +asked him what his name was. The boy stammered, gasped, and got it out: + +"Tom." + +"Oh, no, not Tom--it is--" + +"Thomas." + +"Ah, that's it. I thought there was more to it, maybe. That's very +well. But you've another one I daresay, and you'll tell it to me, won't +you?" + +"Tell the gentleman your other name, Thomas," said Walters, "and say +sir. You mustn't forget your manners." + +"Thomas Sawyer--sir." + +"That's it! That's a good boy. Fine boy. Fine, manly little fellow. +Two thousand verses is a great many--very, very great many. And you +never can be sorry for the trouble you took to learn them; for +knowledge is worth more than anything there is in the world; it's what +makes great men and good men; you'll be a great man and a good man +yourself, some day, Thomas, and then you'll look back and say, It's all +owing to the precious Sunday-school privileges of my boyhood--it's all +owing to my dear teachers that taught me to learn--it's all owing to +the good superintendent, who encouraged me, and watched over me, and +gave me a beautiful Bible--a splendid elegant Bible--to keep and have +it all for my own, always--it's all owing to right bringing up! That is +what you will say, Thomas--and you wouldn't take any money for those +two thousand verses--no indeed you wouldn't. And now you wouldn't mind +telling me and this lady some of the things you've learned--no, I know +you wouldn't--for we are proud of little boys that learn. Now, no +doubt you know the names of all the twelve disciples. Won't you tell us +the names of the first two that were appointed?" + +Tom was tugging at a button-hole and looking sheepish. He blushed, +now, and his eyes fell. Mr. Walters' heart sank within him. He said to +himself, it is not possible that the boy can answer the simplest +question--why DID the Judge ask him? Yet he felt obliged to speak up +and say: + +"Answer the gentleman, Thomas--don't be afraid." + +Tom still hung fire. + +"Now I know you'll tell me," said the lady. "The names of the first +two disciples were--" + +"DAVID AND GOLIAH!" + +Let us draw the curtain of charity over the rest of the scene. + + + +CHAPTER V + +ABOUT half-past ten the cracked bell of the small church began to +ring, and presently the people began to gather for the morning sermon. +The Sunday-school children distributed themselves about the house and +occupied pews with their parents, so as to be under supervision. Aunt +Polly came, and Tom and Sid and Mary sat with her--Tom being placed +next the aisle, in order that he might be as far away from the open +window and the seductive outside summer scenes as possible. The crowd +filed up the aisles: the aged and needy postmaster, who had seen better +days; the mayor and his wife--for they had a mayor there, among other +unnecessaries; the justice of the peace; the widow Douglass, fair, +smart, and forty, a generous, good-hearted soul and well-to-do, her +hill mansion the only palace in the town, and the most hospitable and +much the most lavish in the matter of festivities that St. Petersburg +could boast; the bent and venerable Major and Mrs. Ward; lawyer +Riverson, the new notable from a distance; next the belle of the +village, followed by a troop of lawn-clad and ribbon-decked young +heart-breakers; then all the young clerks in town in a body--for they +had stood in the vestibule sucking their cane-heads, a circling wall of +oiled and simpering admirers, till the last girl had run their gantlet; +and last of all came the Model Boy, Willie Mufferson, taking as heedful +care of his mother as if she were cut glass. He always brought his +mother to church, and was the pride of all the matrons. The boys all +hated him, he was so good. And besides, he had been "thrown up to them" +so much. His white handkerchief was hanging out of his pocket behind, as +usual on Sundays--accidentally. Tom had no handkerchief, and he looked +upon boys who had as snobs. + +The congregation being fully assembled, now, the bell rang once more, +to warn laggards and stragglers, and then a solemn hush fell upon the +church which was only broken by the tittering and whispering of the +choir in the gallery. The choir always tittered and whispered all +through service. There was once a church choir that was not ill-bred, +but I have forgotten where it was, now. It was a great many years ago, +and I can scarcely remember anything about it, but I think it was in +some foreign country. + +The minister gave out the hymn, and read it through with a relish, in +a peculiar style which was much admired in that part of the country. +His voice began on a medium key and climbed steadily up till it reached +a certain point, where it bore with strong emphasis upon the topmost +word and then plunged down as if from a spring-board: + + Shall I be car-ri-ed toe the skies, on flow'ry BEDS of ease, + + Whilst others fight to win the prize, and sail thro' BLOODY seas? + +He was regarded as a wonderful reader. At church "sociables" he was +always called upon to read poetry; and when he was through, the ladies +would lift up their hands and let them fall helplessly in their laps, +and "wall" their eyes, and shake their heads, as much as to say, "Words +cannot express it; it is too beautiful, TOO beautiful for this mortal +earth." + +After the hymn had been sung, the Rev. Mr. Sprague turned himself into +a bulletin-board, and read off "notices" of meetings and societies and +things till it seemed that the list would stretch out to the crack of +doom--a queer custom which is still kept up in America, even in cities, +away here in this age of abundant newspapers. Often, the less there is +to justify a traditional custom, the harder it is to get rid of it. + +And now the minister prayed. A good, generous prayer it was, and went +into details: it pleaded for the church, and the little children of the +church; for the other churches of the village; for the village itself; +for the county; for the State; for the State officers; for the United +States; for the churches of the United States; for Congress; for the +President; for the officers of the Government; for poor sailors, tossed +by stormy seas; for the oppressed millions groaning under the heel of +European monarchies and Oriental despotisms; for such as have the light +and the good tidings, and yet have not eyes to see nor ears to hear +withal; for the heathen in the far islands of the sea; and closed with +a supplication that the words he was about to speak might find grace +and favor, and be as seed sown in fertile ground, yielding in time a +grateful harvest of good. Amen. + +There was a rustling of dresses, and the standing congregation sat +down. The boy whose history this book relates did not enjoy the prayer, +he only endured it--if he even did that much. He was restive all +through it; he kept tally of the details of the prayer, unconsciously +--for he was not listening, but he knew the ground of old, and the +clergyman's regular route over it--and when a little trifle of new +matter was interlarded, his ear detected it and his whole nature +resented it; he considered additions unfair, and scoundrelly. In the +midst of the prayer a fly had lit on the back of the pew in front of +him and tortured his spirit by calmly rubbing its hands together, +embracing its head with its arms, and polishing it so vigorously that +it seemed to almost part company with the body, and the slender thread +of a neck was exposed to view; scraping its wings with its hind legs +and smoothing them to its body as if they had been coat-tails; going +through its whole toilet as tranquilly as if it knew it was perfectly +safe. As indeed it was; for as sorely as Tom's hands itched to grab for +it they did not dare--he believed his soul would be instantly destroyed +if he did such a thing while the prayer was going on. But with the +closing sentence his hand began to curve and steal forward; and the +instant the "Amen" was out the fly was a prisoner of war. His aunt +detected the act and made him let it go. + +The minister gave out his text and droned along monotonously through +an argument that was so prosy that many a head by and by began to nod +--and yet it was an argument that dealt in limitless fire and brimstone +and thinned the predestined elect down to a company so small as to be +hardly worth the saving. Tom counted the pages of the sermon; after +church he always knew how many pages there had been, but he seldom knew +anything else about the discourse. However, this time he was really +interested for a little while. The minister made a grand and moving +picture of the assembling together of the world's hosts at the +millennium when the lion and the lamb should lie down together and a +little child should lead them. But the pathos, the lesson, the moral of +the great spectacle were lost upon the boy; he only thought of the +conspicuousness of the principal character before the on-looking +nations; his face lit with the thought, and he said to himself that he +wished he could be that child, if it was a tame lion. + +Now he lapsed into suffering again, as the dry argument was resumed. +Presently he bethought him of a treasure he had and got it out. It was +a large black beetle with formidable jaws--a "pinchbug," he called it. +It was in a percussion-cap box. The first thing the beetle did was to +take him by the finger. A natural fillip followed, the beetle went +floundering into the aisle and lit on its back, and the hurt finger +went into the boy's mouth. The beetle lay there working its helpless +legs, unable to turn over. Tom eyed it, and longed for it; but it was +safe out of his reach. Other people uninterested in the sermon found +relief in the beetle, and they eyed it too. Presently a vagrant poodle +dog came idling along, sad at heart, lazy with the summer softness and +the quiet, weary of captivity, sighing for change. He spied the beetle; +the drooping tail lifted and wagged. He surveyed the prize; walked +around it; smelt at it from a safe distance; walked around it again; +grew bolder, and took a closer smell; then lifted his lip and made a +gingerly snatch at it, just missing it; made another, and another; +began to enjoy the diversion; subsided to his stomach with the beetle +between his paws, and continued his experiments; grew weary at last, +and then indifferent and absent-minded. His head nodded, and little by +little his chin descended and touched the enemy, who seized it. There +was a sharp yelp, a flirt of the poodle's head, and the beetle fell a +couple of yards away, and lit on its back once more. The neighboring +spectators shook with a gentle inward joy, several faces went behind +fans and handkerchiefs, and Tom was entirely happy. The dog looked +foolish, and probably felt so; but there was resentment in his heart, +too, and a craving for revenge. So he went to the beetle and began a +wary attack on it again; jumping at it from every point of a circle, +lighting with his fore-paws within an inch of the creature, making even +closer snatches at it with his teeth, and jerking his head till his +ears flapped again. But he grew tired once more, after a while; tried +to amuse himself with a fly but found no relief; followed an ant +around, with his nose close to the floor, and quickly wearied of that; +yawned, sighed, forgot the beetle entirely, and sat down on it. Then +there was a wild yelp of agony and the poodle went sailing up the +aisle; the yelps continued, and so did the dog; he crossed the house in +front of the altar; he flew down the other aisle; he crossed before the +doors; he clamored up the home-stretch; his anguish grew with his +progress, till presently he was but a woolly comet moving in its orbit +with the gleam and the speed of light. At last the frantic sufferer +sheered from its course, and sprang into its master's lap; he flung it +out of the window, and the voice of distress quickly thinned away and +died in the distance. + +By this time the whole church was red-faced and suffocating with +suppressed laughter, and the sermon had come to a dead standstill. The +discourse was resumed presently, but it went lame and halting, all +possibility of impressiveness being at an end; for even the gravest +sentiments were constantly being received with a smothered burst of +unholy mirth, under cover of some remote pew-back, as if the poor +parson had said a rarely facetious thing. It was a genuine relief to +the whole congregation when the ordeal was over and the benediction +pronounced. + +Tom Sawyer went home quite cheerful, thinking to himself that there +was some satisfaction about divine service when there was a bit of +variety in it. He had but one marring thought; he was willing that the +dog should play with his pinchbug, but he did not think it was upright +in him to carry it off. + + + +CHAPTER VI + +MONDAY morning found Tom Sawyer miserable. Monday morning always found +him so--because it began another week's slow suffering in school. He +generally began that day with wishing he had had no intervening +holiday, it made the going into captivity and fetters again so much +more odious. + +Tom lay thinking. Presently it occurred to him that he wished he was +sick; then he could stay home from school. Here was a vague +possibility. He canvassed his system. No ailment was found, and he +investigated again. This time he thought he could detect colicky +symptoms, and he began to encourage them with considerable hope. But +they soon grew feeble, and presently died wholly away. He reflected +further. Suddenly he discovered something. One of his upper front teeth +was loose. This was lucky; he was about to begin to groan, as a +"starter," as he called it, when it occurred to him that if he came +into court with that argument, his aunt would pull it out, and that +would hurt. So he thought he would hold the tooth in reserve for the +present, and seek further. Nothing offered for some little time, and +then he remembered hearing the doctor tell about a certain thing that +laid up a patient for two or three weeks and threatened to make him +lose a finger. So the boy eagerly drew his sore toe from under the +sheet and held it up for inspection. But now he did not know the +necessary symptoms. However, it seemed well worth while to chance it, +so he fell to groaning with considerable spirit. + +But Sid slept on unconscious. + +Tom groaned louder, and fancied that he began to feel pain in the toe. + +No result from Sid. + +Tom was panting with his exertions by this time. He took a rest and +then swelled himself up and fetched a succession of admirable groans. + +Sid snored on. + +Tom was aggravated. He said, "Sid, Sid!" and shook him. This course +worked well, and Tom began to groan again. Sid yawned, stretched, then +brought himself up on his elbow with a snort, and began to stare at +Tom. Tom went on groaning. Sid said: + +"Tom! Say, Tom!" [No response.] "Here, Tom! TOM! What is the matter, +Tom?" And he shook him and looked in his face anxiously. + +Tom moaned out: + +"Oh, don't, Sid. Don't joggle me." + +"Why, what's the matter, Tom? I must call auntie." + +"No--never mind. It'll be over by and by, maybe. Don't call anybody." + +"But I must! DON'T groan so, Tom, it's awful. How long you been this +way?" + +"Hours. Ouch! Oh, don't stir so, Sid, you'll kill me." + +"Tom, why didn't you wake me sooner? Oh, Tom, DON'T! It makes my +flesh crawl to hear you. Tom, what is the matter?" + +"I forgive you everything, Sid. [Groan.] Everything you've ever done +to me. When I'm gone--" + +"Oh, Tom, you ain't dying, are you? Don't, Tom--oh, don't. Maybe--" + +"I forgive everybody, Sid. [Groan.] Tell 'em so, Sid. And Sid, you +give my window-sash and my cat with one eye to that new girl that's +come to town, and tell her--" + +But Sid had snatched his clothes and gone. Tom was suffering in +reality, now, so handsomely was his imagination working, and so his +groans had gathered quite a genuine tone. + +Sid flew down-stairs and said: + +"Oh, Aunt Polly, come! Tom's dying!" + +"Dying!" + +"Yes'm. Don't wait--come quick!" + +"Rubbage! I don't believe it!" + +But she fled up-stairs, nevertheless, with Sid and Mary at her heels. +And her face grew white, too, and her lip trembled. When she reached +the bedside she gasped out: + +"You, Tom! Tom, what's the matter with you?" + +"Oh, auntie, I'm--" + +"What's the matter with you--what is the matter with you, child?" + +"Oh, auntie, my sore toe's mortified!" + +The old lady sank down into a chair and laughed a little, then cried a +little, then did both together. This restored her and she said: + +"Tom, what a turn you did give me. Now you shut up that nonsense and +climb out of this." + +The groans ceased and the pain vanished from the toe. The boy felt a +little foolish, and he said: + +"Aunt Polly, it SEEMED mortified, and it hurt so I never minded my +tooth at all." + +"Your tooth, indeed! What's the matter with your tooth?" + +"One of them's loose, and it aches perfectly awful." + +"There, there, now, don't begin that groaning again. Open your mouth. +Well--your tooth IS loose, but you're not going to die about that. +Mary, get me a silk thread, and a chunk of fire out of the kitchen." + +Tom said: + +"Oh, please, auntie, don't pull it out. It don't hurt any more. I wish +I may never stir if it does. Please don't, auntie. I don't want to stay +home from school." + +"Oh, you don't, don't you? So all this row was because you thought +you'd get to stay home from school and go a-fishing? Tom, Tom, I love +you so, and you seem to try every way you can to break my old heart +with your outrageousness." By this time the dental instruments were +ready. The old lady made one end of the silk thread fast to Tom's tooth +with a loop and tied the other to the bedpost. Then she seized the +chunk of fire and suddenly thrust it almost into the boy's face. The +tooth hung dangling by the bedpost, now. + +But all trials bring their compensations. As Tom wended to school +after breakfast, he was the envy of every boy he met because the gap in +his upper row of teeth enabled him to expectorate in a new and +admirable way. He gathered quite a following of lads interested in the +exhibition; and one that had cut his finger and had been a centre of +fascination and homage up to this time, now found himself suddenly +without an adherent, and shorn of his glory. His heart was heavy, and +he said with a disdain which he did not feel that it wasn't anything to +spit like Tom Sawyer; but another boy said, "Sour grapes!" and he +wandered away a dismantled hero. + +Shortly Tom came upon the juvenile pariah of the village, Huckleberry +Finn, son of the town drunkard. Huckleberry was cordially hated and +dreaded by all the mothers of the town, because he was idle and lawless +and vulgar and bad--and because all their children admired him so, and +delighted in his forbidden society, and wished they dared to be like +him. Tom was like the rest of the respectable boys, in that he envied +Huckleberry his gaudy outcast condition, and was under strict orders +not to play with him. So he played with him every time he got a chance. +Huckleberry was always dressed in the cast-off clothes of full-grown +men, and they were in perennial bloom and fluttering with rags. His hat +was a vast ruin with a wide crescent lopped out of its brim; his coat, +when he wore one, hung nearly to his heels and had the rearward buttons +far down the back; but one suspender supported his trousers; the seat +of the trousers bagged low and contained nothing, the fringed legs +dragged in the dirt when not rolled up. + +Huckleberry came and went, at his own free will. He slept on doorsteps +in fine weather and in empty hogsheads in wet; he did not have to go to +school or to church, or call any being master or obey anybody; he could +go fishing or swimming when and where he chose, and stay as long as it +suited him; nobody forbade him to fight; he could sit up as late as he +pleased; he was always the first boy that went barefoot in the spring +and the last to resume leather in the fall; he never had to wash, nor +put on clean clothes; he could swear wonderfully. In a word, everything +that goes to make life precious that boy had. So thought every +harassed, hampered, respectable boy in St. Petersburg. + +Tom hailed the romantic outcast: + +"Hello, Huckleberry!" + +"Hello yourself, and see how you like it." + +"What's that you got?" + +"Dead cat." + +"Lemme see him, Huck. My, he's pretty stiff. Where'd you get him?" + +"Bought him off'n a boy." + +"What did you give?" + +"I give a blue ticket and a bladder that I got at the slaughter-house." + +"Where'd you get the blue ticket?" + +"Bought it off'n Ben Rogers two weeks ago for a hoop-stick." + +"Say--what is dead cats good for, Huck?" + +"Good for? Cure warts with." + +"No! Is that so? I know something that's better." + +"I bet you don't. What is it?" + +"Why, spunk-water." + +"Spunk-water! I wouldn't give a dern for spunk-water." + +"You wouldn't, wouldn't you? D'you ever try it?" + +"No, I hain't. But Bob Tanner did." + +"Who told you so!" + +"Why, he told Jeff Thatcher, and Jeff told Johnny Baker, and Johnny +told Jim Hollis, and Jim told Ben Rogers, and Ben told a nigger, and +the nigger told me. There now!" + +"Well, what of it? They'll all lie. Leastways all but the nigger. I +don't know HIM. But I never see a nigger that WOULDN'T lie. Shucks! Now +you tell me how Bob Tanner done it, Huck." + +"Why, he took and dipped his hand in a rotten stump where the +rain-water was." + +"In the daytime?" + +"Certainly." + +"With his face to the stump?" + +"Yes. Least I reckon so." + +"Did he say anything?" + +"I don't reckon he did. I don't know." + +"Aha! Talk about trying to cure warts with spunk-water such a blame +fool way as that! Why, that ain't a-going to do any good. You got to go +all by yourself, to the middle of the woods, where you know there's a +spunk-water stump, and just as it's midnight you back up against the +stump and jam your hand in and say: + + 'Barley-corn, barley-corn, injun-meal shorts, + Spunk-water, spunk-water, swaller these warts,' + +and then walk away quick, eleven steps, with your eyes shut, and then +turn around three times and walk home without speaking to anybody. +Because if you speak the charm's busted." + +"Well, that sounds like a good way; but that ain't the way Bob Tanner +done." + +"No, sir, you can bet he didn't, becuz he's the wartiest boy in this +town; and he wouldn't have a wart on him if he'd knowed how to work +spunk-water. I've took off thousands of warts off of my hands that way, +Huck. I play with frogs so much that I've always got considerable many +warts. Sometimes I take 'em off with a bean." + +"Yes, bean's good. I've done that." + +"Have you? What's your way?" + +"You take and split the bean, and cut the wart so as to get some +blood, and then you put the blood on one piece of the bean and take and +dig a hole and bury it 'bout midnight at the crossroads in the dark of +the moon, and then you burn up the rest of the bean. You see that piece +that's got the blood on it will keep drawing and drawing, trying to +fetch the other piece to it, and so that helps the blood to draw the +wart, and pretty soon off she comes." + +"Yes, that's it, Huck--that's it; though when you're burying it if you +say 'Down bean; off wart; come no more to bother me!' it's better. +That's the way Joe Harper does, and he's been nearly to Coonville and +most everywheres. But say--how do you cure 'em with dead cats?" + +"Why, you take your cat and go and get in the graveyard 'long about +midnight when somebody that was wicked has been buried; and when it's +midnight a devil will come, or maybe two or three, but you can't see +'em, you can only hear something like the wind, or maybe hear 'em talk; +and when they're taking that feller away, you heave your cat after 'em +and say, 'Devil follow corpse, cat follow devil, warts follow cat, I'm +done with ye!' That'll fetch ANY wart." + +"Sounds right. D'you ever try it, Huck?" + +"No, but old Mother Hopkins told me." + +"Well, I reckon it's so, then. Becuz they say she's a witch." + +"Say! Why, Tom, I KNOW she is. She witched pap. Pap says so his own +self. He come along one day, and he see she was a-witching him, so he +took up a rock, and if she hadn't dodged, he'd a got her. Well, that +very night he rolled off'n a shed wher' he was a layin drunk, and broke +his arm." + +"Why, that's awful. How did he know she was a-witching him?" + +"Lord, pap can tell, easy. Pap says when they keep looking at you +right stiddy, they're a-witching you. Specially if they mumble. Becuz +when they mumble they're saying the Lord's Prayer backards." + +"Say, Hucky, when you going to try the cat?" + +"To-night. I reckon they'll come after old Hoss Williams to-night." + +"But they buried him Saturday. Didn't they get him Saturday night?" + +"Why, how you talk! How could their charms work till midnight?--and +THEN it's Sunday. Devils don't slosh around much of a Sunday, I don't +reckon." + +"I never thought of that. That's so. Lemme go with you?" + +"Of course--if you ain't afeard." + +"Afeard! 'Tain't likely. Will you meow?" + +"Yes--and you meow back, if you get a chance. Last time, you kep' me +a-meowing around till old Hays went to throwing rocks at me and says +'Dern that cat!' and so I hove a brick through his window--but don't +you tell." + +"I won't. I couldn't meow that night, becuz auntie was watching me, +but I'll meow this time. Say--what's that?" + +"Nothing but a tick." + +"Where'd you get him?" + +"Out in the woods." + +"What'll you take for him?" + +"I don't know. I don't want to sell him." + +"All right. It's a mighty small tick, anyway." + +"Oh, anybody can run a tick down that don't belong to them. I'm +satisfied with it. It's a good enough tick for me." + +"Sho, there's ticks a plenty. I could have a thousand of 'em if I +wanted to." + +"Well, why don't you? Becuz you know mighty well you can't. This is a +pretty early tick, I reckon. It's the first one I've seen this year." + +"Say, Huck--I'll give you my tooth for him." + +"Less see it." + +Tom got out a bit of paper and carefully unrolled it. Huckleberry +viewed it wistfully. The temptation was very strong. At last he said: + +"Is it genuwyne?" + +Tom lifted his lip and showed the vacancy. + +"Well, all right," said Huckleberry, "it's a trade." + +Tom enclosed the tick in the percussion-cap box that had lately been +the pinchbug's prison, and the boys separated, each feeling wealthier +than before. + +When Tom reached the little isolated frame schoolhouse, he strode in +briskly, with the manner of one who had come with all honest speed. +He hung his hat on a peg and flung himself into his seat with +business-like alacrity. The master, throned on high in his great +splint-bottom arm-chair, was dozing, lulled by the drowsy hum of study. +The interruption roused him. + +"Thomas Sawyer!" + +Tom knew that when his name was pronounced in full, it meant trouble. + +"Sir!" + +"Come up here. Now, sir, why are you late again, as usual?" + +Tom was about to take refuge in a lie, when he saw two long tails of +yellow hair hanging down a back that he recognized by the electric +sympathy of love; and by that form was THE ONLY VACANT PLACE on the +girls' side of the schoolhouse. He instantly said: + +"I STOPPED TO TALK WITH HUCKLEBERRY FINN!" + +The master's pulse stood still, and he stared helplessly. The buzz of +study ceased. The pupils wondered if this foolhardy boy had lost his +mind. The master said: + +"You--you did what?" + +"Stopped to talk with Huckleberry Finn." + +There was no mistaking the words. + +"Thomas Sawyer, this is the most astounding confession I have ever +listened to. No mere ferule will answer for this offence. Take off your +jacket." + +The master's arm performed until it was tired and the stock of +switches notably diminished. Then the order followed: + +"Now, sir, go and sit with the girls! And let this be a warning to you." + +The titter that rippled around the room appeared to abash the boy, but +in reality that result was caused rather more by his worshipful awe of +his unknown idol and the dread pleasure that lay in his high good +fortune. He sat down upon the end of the pine bench and the girl +hitched herself away from him with a toss of her head. Nudges and winks +and whispers traversed the room, but Tom sat still, with his arms upon +the long, low desk before him, and seemed to study his book. + +By and by attention ceased from him, and the accustomed school murmur +rose upon the dull air once more. Presently the boy began to steal +furtive glances at the girl. She observed it, "made a mouth" at him and +gave him the back of her head for the space of a minute. When she +cautiously faced around again, a peach lay before her. She thrust it +away. Tom gently put it back. She thrust it away again, but with less +animosity. Tom patiently returned it to its place. Then she let it +remain. Tom scrawled on his slate, "Please take it--I got more." The +girl glanced at the words, but made no sign. Now the boy began to draw +something on the slate, hiding his work with his left hand. For a time +the girl refused to notice; but her human curiosity presently began to +manifest itself by hardly perceptible signs. The boy worked on, +apparently unconscious. The girl made a sort of noncommittal attempt to +see, but the boy did not betray that he was aware of it. At last she +gave in and hesitatingly whispered: + +"Let me see it." + +Tom partly uncovered a dismal caricature of a house with two gable +ends to it and a corkscrew of smoke issuing from the chimney. Then the +girl's interest began to fasten itself upon the work and she forgot +everything else. When it was finished, she gazed a moment, then +whispered: + +"It's nice--make a man." + +The artist erected a man in the front yard, that resembled a derrick. +He could have stepped over the house; but the girl was not +hypercritical; she was satisfied with the monster, and whispered: + +"It's a beautiful man--now make me coming along." + +Tom drew an hour-glass with a full moon and straw limbs to it and +armed the spreading fingers with a portentous fan. The girl said: + +"It's ever so nice--I wish I could draw." + +"It's easy," whispered Tom, "I'll learn you." + +"Oh, will you? When?" + +"At noon. Do you go home to dinner?" + +"I'll stay if you will." + +"Good--that's a whack. What's your name?" + +"Becky Thatcher. What's yours? Oh, I know. It's Thomas Sawyer." + +"That's the name they lick me by. I'm Tom when I'm good. You call me +Tom, will you?" + +"Yes." + +Now Tom began to scrawl something on the slate, hiding the words from +the girl. But she was not backward this time. She begged to see. Tom +said: + +"Oh, it ain't anything." + +"Yes it is." + +"No it ain't. You don't want to see." + +"Yes I do, indeed I do. Please let me." + +"You'll tell." + +"No I won't--deed and deed and double deed won't." + +"You won't tell anybody at all? Ever, as long as you live?" + +"No, I won't ever tell ANYbody. Now let me." + +"Oh, YOU don't want to see!" + +"Now that you treat me so, I WILL see." And she put her small hand +upon his and a little scuffle ensued, Tom pretending to resist in +earnest but letting his hand slip by degrees till these words were +revealed: "I LOVE YOU." + +"Oh, you bad thing!" And she hit his hand a smart rap, but reddened +and looked pleased, nevertheless. + +Just at this juncture the boy felt a slow, fateful grip closing on his +ear, and a steady lifting impulse. In that wise he was borne across the +house and deposited in his own seat, under a peppering fire of giggles +from the whole school. Then the master stood over him during a few +awful moments, and finally moved away to his throne without saying a +word. But although Tom's ear tingled, his heart was jubilant. + +As the school quieted down Tom made an honest effort to study, but the +turmoil within him was too great. In turn he took his place in the +reading class and made a botch of it; then in the geography class and +turned lakes into mountains, mountains into rivers, and rivers into +continents, till chaos was come again; then in the spelling class, and +got "turned down," by a succession of mere baby words, till he brought +up at the foot and yielded up the pewter medal which he had worn with +ostentation for months. + + + +CHAPTER VII + +THE harder Tom tried to fasten his mind on his book, the more his +ideas wandered. So at last, with a sigh and a yawn, he gave it up. It +seemed to him that the noon recess would never come. The air was +utterly dead. There was not a breath stirring. It was the sleepiest of +sleepy days. The drowsing murmur of the five and twenty studying +scholars soothed the soul like the spell that is in the murmur of bees. +Away off in the flaming sunshine, Cardiff Hill lifted its soft green +sides through a shimmering veil of heat, tinted with the purple of +distance; a few birds floated on lazy wing high in the air; no other +living thing was visible but some cows, and they were asleep. Tom's +heart ached to be free, or else to have something of interest to do to +pass the dreary time. His hand wandered into his pocket and his face +lit up with a glow of gratitude that was prayer, though he did not know +it. Then furtively the percussion-cap box came out. He released the +tick and put him on the long flat desk. The creature probably glowed +with a gratitude that amounted to prayer, too, at this moment, but it +was premature: for when he started thankfully to travel off, Tom turned +him aside with a pin and made him take a new direction. + +Tom's bosom friend sat next him, suffering just as Tom had been, and +now he was deeply and gratefully interested in this entertainment in an +instant. This bosom friend was Joe Harper. The two boys were sworn +friends all the week, and embattled enemies on Saturdays. Joe took a +pin out of his lapel and began to assist in exercising the prisoner. +The sport grew in interest momently. Soon Tom said that they were +interfering with each other, and neither getting the fullest benefit of +the tick. So he put Joe's slate on the desk and drew a line down the +middle of it from top to bottom. + +"Now," said he, "as long as he is on your side you can stir him up and +I'll let him alone; but if you let him get away and get on my side, +you're to leave him alone as long as I can keep him from crossing over." + +"All right, go ahead; start him up." + +The tick escaped from Tom, presently, and crossed the equator. Joe +harassed him awhile, and then he got away and crossed back again. This +change of base occurred often. While one boy was worrying the tick with +absorbing interest, the other would look on with interest as strong, +the two heads bowed together over the slate, and the two souls dead to +all things else. At last luck seemed to settle and abide with Joe. The +tick tried this, that, and the other course, and got as excited and as +anxious as the boys themselves, but time and again just as he would +have victory in his very grasp, so to speak, and Tom's fingers would be +twitching to begin, Joe's pin would deftly head him off, and keep +possession. At last Tom could stand it no longer. The temptation was +too strong. So he reached out and lent a hand with his pin. Joe was +angry in a moment. Said he: + +"Tom, you let him alone." + +"I only just want to stir him up a little, Joe." + +"No, sir, it ain't fair; you just let him alone." + +"Blame it, I ain't going to stir him much." + +"Let him alone, I tell you." + +"I won't!" + +"You shall--he's on my side of the line." + +"Look here, Joe Harper, whose is that tick?" + +"I don't care whose tick he is--he's on my side of the line, and you +sha'n't touch him." + +"Well, I'll just bet I will, though. He's my tick and I'll do what I +blame please with him, or die!" + +A tremendous whack came down on Tom's shoulders, and its duplicate on +Joe's; and for the space of two minutes the dust continued to fly from +the two jackets and the whole school to enjoy it. The boys had been too +absorbed to notice the hush that had stolen upon the school awhile +before when the master came tiptoeing down the room and stood over +them. He had contemplated a good part of the performance before he +contributed his bit of variety to it. + +When school broke up at noon, Tom flew to Becky Thatcher, and +whispered in her ear: + +"Put on your bonnet and let on you're going home; and when you get to +the corner, give the rest of 'em the slip, and turn down through the +lane and come back. I'll go the other way and come it over 'em the same +way." + +So the one went off with one group of scholars, and the other with +another. In a little while the two met at the bottom of the lane, and +when they reached the school they had it all to themselves. Then they +sat together, with a slate before them, and Tom gave Becky the pencil +and held her hand in his, guiding it, and so created another surprising +house. When the interest in art began to wane, the two fell to talking. +Tom was swimming in bliss. He said: + +"Do you love rats?" + +"No! I hate them!" + +"Well, I do, too--LIVE ones. But I mean dead ones, to swing round your +head with a string." + +"No, I don't care for rats much, anyway. What I like is chewing-gum." + +"Oh, I should say so! I wish I had some now." + +"Do you? I've got some. I'll let you chew it awhile, but you must give +it back to me." + +That was agreeable, so they chewed it turn about, and dangled their +legs against the bench in excess of contentment. + +"Was you ever at a circus?" said Tom. + +"Yes, and my pa's going to take me again some time, if I'm good." + +"I been to the circus three or four times--lots of times. Church ain't +shucks to a circus. There's things going on at a circus all the time. +I'm going to be a clown in a circus when I grow up." + +"Oh, are you! That will be nice. They're so lovely, all spotted up." + +"Yes, that's so. And they get slathers of money--most a dollar a day, +Ben Rogers says. Say, Becky, was you ever engaged?" + +"What's that?" + +"Why, engaged to be married." + +"No." + +"Would you like to?" + +"I reckon so. I don't know. What is it like?" + +"Like? Why it ain't like anything. You only just tell a boy you won't +ever have anybody but him, ever ever ever, and then you kiss and that's +all. Anybody can do it." + +"Kiss? What do you kiss for?" + +"Why, that, you know, is to--well, they always do that." + +"Everybody?" + +"Why, yes, everybody that's in love with each other. Do you remember +what I wrote on the slate?" + +"Ye--yes." + +"What was it?" + +"I sha'n't tell you." + +"Shall I tell YOU?" + +"Ye--yes--but some other time." + +"No, now." + +"No, not now--to-morrow." + +"Oh, no, NOW. Please, Becky--I'll whisper it, I'll whisper it ever so +easy." + +Becky hesitating, Tom took silence for consent, and passed his arm +about her waist and whispered the tale ever so softly, with his mouth +close to her ear. And then he added: + +"Now you whisper it to me--just the same." + +She resisted, for a while, and then said: + +"You turn your face away so you can't see, and then I will. But you +mustn't ever tell anybody--WILL you, Tom? Now you won't, WILL you?" + +"No, indeed, indeed I won't. Now, Becky." + +He turned his face away. She bent timidly around till her breath +stirred his curls and whispered, "I--love--you!" + +Then she sprang away and ran around and around the desks and benches, +with Tom after her, and took refuge in a corner at last, with her +little white apron to her face. Tom clasped her about her neck and +pleaded: + +"Now, Becky, it's all done--all over but the kiss. Don't you be afraid +of that--it ain't anything at all. Please, Becky." And he tugged at her +apron and the hands. + +By and by she gave up, and let her hands drop; her face, all glowing +with the struggle, came up and submitted. Tom kissed the red lips and +said: + +"Now it's all done, Becky. And always after this, you know, you ain't +ever to love anybody but me, and you ain't ever to marry anybody but +me, ever never and forever. Will you?" + +"No, I'll never love anybody but you, Tom, and I'll never marry +anybody but you--and you ain't to ever marry anybody but me, either." + +"Certainly. Of course. That's PART of it. And always coming to school +or when we're going home, you're to walk with me, when there ain't +anybody looking--and you choose me and I choose you at parties, because +that's the way you do when you're engaged." + +"It's so nice. I never heard of it before." + +"Oh, it's ever so gay! Why, me and Amy Lawrence--" + +The big eyes told Tom his blunder and he stopped, confused. + +"Oh, Tom! Then I ain't the first you've ever been engaged to!" + +The child began to cry. Tom said: + +"Oh, don't cry, Becky, I don't care for her any more." + +"Yes, you do, Tom--you know you do." + +Tom tried to put his arm about her neck, but she pushed him away and +turned her face to the wall, and went on crying. Tom tried again, with +soothing words in his mouth, and was repulsed again. Then his pride was +up, and he strode away and went outside. He stood about, restless and +uneasy, for a while, glancing at the door, every now and then, hoping +she would repent and come to find him. But she did not. Then he began +to feel badly and fear that he was in the wrong. It was a hard struggle +with him to make new advances, now, but he nerved himself to it and +entered. She was still standing back there in the corner, sobbing, with +her face to the wall. Tom's heart smote him. He went to her and stood a +moment, not knowing exactly how to proceed. Then he said hesitatingly: + +"Becky, I--I don't care for anybody but you." + +No reply--but sobs. + +"Becky"--pleadingly. "Becky, won't you say something?" + +More sobs. + +Tom got out his chiefest jewel, a brass knob from the top of an +andiron, and passed it around her so that she could see it, and said: + +"Please, Becky, won't you take it?" + +She struck it to the floor. Then Tom marched out of the house and over +the hills and far away, to return to school no more that day. Presently +Becky began to suspect. She ran to the door; he was not in sight; she +flew around to the play-yard; he was not there. Then she called: + +"Tom! Come back, Tom!" + +She listened intently, but there was no answer. She had no companions +but silence and loneliness. So she sat down to cry again and upbraid +herself; and by this time the scholars began to gather again, and she +had to hide her griefs and still her broken heart and take up the cross +of a long, dreary, aching afternoon, with none among the strangers +about her to exchange sorrows with. + + + +CHAPTER VIII + +TOM dodged hither and thither through lanes until he was well out of +the track of returning scholars, and then fell into a moody jog. He +crossed a small "branch" two or three times, because of a prevailing +juvenile superstition that to cross water baffled pursuit. Half an hour +later he was disappearing behind the Douglas mansion on the summit of +Cardiff Hill, and the schoolhouse was hardly distinguishable away off +in the valley behind him. He entered a dense wood, picked his pathless +way to the centre of it, and sat down on a mossy spot under a spreading +oak. There was not even a zephyr stirring; the dead noonday heat had +even stilled the songs of the birds; nature lay in a trance that was +broken by no sound but the occasional far-off hammering of a +woodpecker, and this seemed to render the pervading silence and sense +of loneliness the more profound. The boy's soul was steeped in +melancholy; his feelings were in happy accord with his surroundings. He +sat long with his elbows on his knees and his chin in his hands, +meditating. It seemed to him that life was but a trouble, at best, and +he more than half envied Jimmy Hodges, so lately released; it must be +very peaceful, he thought, to lie and slumber and dream forever and +ever, with the wind whispering through the trees and caressing the +grass and the flowers over the grave, and nothing to bother and grieve +about, ever any more. If he only had a clean Sunday-school record he +could be willing to go, and be done with it all. Now as to this girl. +What had he done? Nothing. He had meant the best in the world, and been +treated like a dog--like a very dog. She would be sorry some day--maybe +when it was too late. Ah, if he could only die TEMPORARILY! + +But the elastic heart of youth cannot be compressed into one +constrained shape long at a time. Tom presently began to drift +insensibly back into the concerns of this life again. What if he turned +his back, now, and disappeared mysteriously? What if he went away--ever +so far away, into unknown countries beyond the seas--and never came +back any more! How would she feel then! The idea of being a clown +recurred to him now, only to fill him with disgust. For frivolity and +jokes and spotted tights were an offense, when they intruded themselves +upon a spirit that was exalted into the vague august realm of the +romantic. No, he would be a soldier, and return after long years, all +war-worn and illustrious. No--better still, he would join the Indians, +and hunt buffaloes and go on the warpath in the mountain ranges and the +trackless great plains of the Far West, and away in the future come +back a great chief, bristling with feathers, hideous with paint, and +prance into Sunday-school, some drowsy summer morning, with a +bloodcurdling war-whoop, and sear the eyeballs of all his companions +with unappeasable envy. But no, there was something gaudier even than +this. He would be a pirate! That was it! NOW his future lay plain +before him, and glowing with unimaginable splendor. How his name would +fill the world, and make people shudder! How gloriously he would go +plowing the dancing seas, in his long, low, black-hulled racer, the +Spirit of the Storm, with his grisly flag flying at the fore! And at +the zenith of his fame, how he would suddenly appear at the old village +and stalk into church, brown and weather-beaten, in his black velvet +doublet and trunks, his great jack-boots, his crimson sash, his belt +bristling with horse-pistols, his crime-rusted cutlass at his side, his +slouch hat with waving plumes, his black flag unfurled, with the skull +and crossbones on it, and hear with swelling ecstasy the whisperings, +"It's Tom Sawyer the Pirate!--the Black Avenger of the Spanish Main!" + +Yes, it was settled; his career was determined. He would run away from +home and enter upon it. He would start the very next morning. Therefore +he must now begin to get ready. He would collect his resources +together. He went to a rotten log near at hand and began to dig under +one end of it with his Barlow knife. He soon struck wood that sounded +hollow. He put his hand there and uttered this incantation impressively: + +"What hasn't come here, come! What's here, stay here!" + +Then he scraped away the dirt, and exposed a pine shingle. He took it +up and disclosed a shapely little treasure-house whose bottom and sides +were of shingles. In it lay a marble. Tom's astonishment was boundless! +He scratched his head with a perplexed air, and said: + +"Well, that beats anything!" + +Then he tossed the marble away pettishly, and stood cogitating. The +truth was, that a superstition of his had failed, here, which he and +all his comrades had always looked upon as infallible. If you buried a +marble with certain necessary incantations, and left it alone a +fortnight, and then opened the place with the incantation he had just +used, you would find that all the marbles you had ever lost had +gathered themselves together there, meantime, no matter how widely they +had been separated. But now, this thing had actually and unquestionably +failed. Tom's whole structure of faith was shaken to its foundations. +He had many a time heard of this thing succeeding but never of its +failing before. It did not occur to him that he had tried it several +times before, himself, but could never find the hiding-places +afterward. He puzzled over the matter some time, and finally decided +that some witch had interfered and broken the charm. He thought he +would satisfy himself on that point; so he searched around till he +found a small sandy spot with a little funnel-shaped depression in it. +He laid himself down and put his mouth close to this depression and +called-- + +"Doodle-bug, doodle-bug, tell me what I want to know! Doodle-bug, +doodle-bug, tell me what I want to know!" + +The sand began to work, and presently a small black bug appeared for a +second and then darted under again in a fright. + +"He dasn't tell! So it WAS a witch that done it. I just knowed it." + +He well knew the futility of trying to contend against witches, so he +gave up discouraged. But it occurred to him that he might as well have +the marble he had just thrown away, and therefore he went and made a +patient search for it. But he could not find it. Now he went back to +his treasure-house and carefully placed himself just as he had been +standing when he tossed the marble away; then he took another marble +from his pocket and tossed it in the same way, saying: + +"Brother, go find your brother!" + +He watched where it stopped, and went there and looked. But it must +have fallen short or gone too far; so he tried twice more. The last +repetition was successful. The two marbles lay within a foot of each +other. + +Just here the blast of a toy tin trumpet came faintly down the green +aisles of the forest. Tom flung off his jacket and trousers, turned a +suspender into a belt, raked away some brush behind the rotten log, +disclosing a rude bow and arrow, a lath sword and a tin trumpet, and in +a moment had seized these things and bounded away, barelegged, with +fluttering shirt. He presently halted under a great elm, blew an +answering blast, and then began to tiptoe and look warily out, this way +and that. He said cautiously--to an imaginary company: + +"Hold, my merry men! Keep hid till I blow." + +Now appeared Joe Harper, as airily clad and elaborately armed as Tom. +Tom called: + +"Hold! Who comes here into Sherwood Forest without my pass?" + +"Guy of Guisborne wants no man's pass. Who art thou that--that--" + +"Dares to hold such language," said Tom, prompting--for they talked +"by the book," from memory. + +"Who art thou that dares to hold such language?" + +"I, indeed! I am Robin Hood, as thy caitiff carcase soon shall know." + +"Then art thou indeed that famous outlaw? Right gladly will I dispute +with thee the passes of the merry wood. Have at thee!" + +They took their lath swords, dumped their other traps on the ground, +struck a fencing attitude, foot to foot, and began a grave, careful +combat, "two up and two down." Presently Tom said: + +"Now, if you've got the hang, go it lively!" + +So they "went it lively," panting and perspiring with the work. By and +by Tom shouted: + +"Fall! fall! Why don't you fall?" + +"I sha'n't! Why don't you fall yourself? You're getting the worst of +it." + +"Why, that ain't anything. I can't fall; that ain't the way it is in +the book. The book says, 'Then with one back-handed stroke he slew poor +Guy of Guisborne.' You're to turn around and let me hit you in the +back." + +There was no getting around the authorities, so Joe turned, received +the whack and fell. + +"Now," said Joe, getting up, "you got to let me kill YOU. That's fair." + +"Why, I can't do that, it ain't in the book." + +"Well, it's blamed mean--that's all." + +"Well, say, Joe, you can be Friar Tuck or Much the miller's son, and +lam me with a quarter-staff; or I'll be the Sheriff of Nottingham and +you be Robin Hood a little while and kill me." + +This was satisfactory, and so these adventures were carried out. Then +Tom became Robin Hood again, and was allowed by the treacherous nun to +bleed his strength away through his neglected wound. And at last Joe, +representing a whole tribe of weeping outlaws, dragged him sadly forth, +gave his bow into his feeble hands, and Tom said, "Where this arrow +falls, there bury poor Robin Hood under the greenwood tree." Then he +shot the arrow and fell back and would have died, but he lit on a +nettle and sprang up too gaily for a corpse. + +The boys dressed themselves, hid their accoutrements, and went off +grieving that there were no outlaws any more, and wondering what modern +civilization could claim to have done to compensate for their loss. +They said they would rather be outlaws a year in Sherwood Forest than +President of the United States forever. + + + +CHAPTER IX + +AT half-past nine, that night, Tom and Sid were sent to bed, as usual. +They said their prayers, and Sid was soon asleep. Tom lay awake and +waited, in restless impatience. When it seemed to him that it must be +nearly daylight, he heard the clock strike ten! This was despair. He +would have tossed and fidgeted, as his nerves demanded, but he was +afraid he might wake Sid. So he lay still, and stared up into the dark. +Everything was dismally still. By and by, out of the stillness, little, +scarcely perceptible noises began to emphasize themselves. The ticking +of the clock began to bring itself into notice. Old beams began to +crack mysteriously. The stairs creaked faintly. Evidently spirits were +abroad. A measured, muffled snore issued from Aunt Polly's chamber. And +now the tiresome chirping of a cricket that no human ingenuity could +locate, began. Next the ghastly ticking of a deathwatch in the wall at +the bed's head made Tom shudder--it meant that somebody's days were +numbered. Then the howl of a far-off dog rose on the night air, and was +answered by a fainter howl from a remoter distance. Tom was in an +agony. At last he was satisfied that time had ceased and eternity +begun; he began to doze, in spite of himself; the clock chimed eleven, +but he did not hear it. And then there came, mingling with his +half-formed dreams, a most melancholy caterwauling. The raising of a +neighboring window disturbed him. A cry of "Scat! you devil!" and the +crash of an empty bottle against the back of his aunt's woodshed +brought him wide awake, and a single minute later he was dressed and +out of the window and creeping along the roof of the "ell" on all +fours. He "meow'd" with caution once or twice, as he went; then jumped +to the roof of the woodshed and thence to the ground. Huckleberry Finn +was there, with his dead cat. The boys moved off and disappeared in the +gloom. At the end of half an hour they were wading through the tall +grass of the graveyard. + +It was a graveyard of the old-fashioned Western kind. It was on a +hill, about a mile and a half from the village. It had a crazy board +fence around it, which leaned inward in places, and outward the rest of +the time, but stood upright nowhere. Grass and weeds grew rank over the +whole cemetery. All the old graves were sunken in, there was not a +tombstone on the place; round-topped, worm-eaten boards staggered over +the graves, leaning for support and finding none. "Sacred to the memory +of" So-and-So had been painted on them once, but it could no longer +have been read, on the most of them, now, even if there had been light. + +A faint wind moaned through the trees, and Tom feared it might be the +spirits of the dead, complaining at being disturbed. The boys talked +little, and only under their breath, for the time and the place and the +pervading solemnity and silence oppressed their spirits. They found the +sharp new heap they were seeking, and ensconced themselves within the +protection of three great elms that grew in a bunch within a few feet +of the grave. + +Then they waited in silence for what seemed a long time. The hooting +of a distant owl was all the sound that troubled the dead stillness. +Tom's reflections grew oppressive. He must force some talk. So he said +in a whisper: + +"Hucky, do you believe the dead people like it for us to be here?" + +Huckleberry whispered: + +"I wisht I knowed. It's awful solemn like, AIN'T it?" + +"I bet it is." + +There was a considerable pause, while the boys canvassed this matter +inwardly. Then Tom whispered: + +"Say, Hucky--do you reckon Hoss Williams hears us talking?" + +"O' course he does. Least his sperrit does." + +Tom, after a pause: + +"I wish I'd said Mister Williams. But I never meant any harm. +Everybody calls him Hoss." + +"A body can't be too partic'lar how they talk 'bout these-yer dead +people, Tom." + +This was a damper, and conversation died again. + +Presently Tom seized his comrade's arm and said: + +"Sh!" + +"What is it, Tom?" And the two clung together with beating hearts. + +"Sh! There 'tis again! Didn't you hear it?" + +"I--" + +"There! Now you hear it." + +"Lord, Tom, they're coming! They're coming, sure. What'll we do?" + +"I dono. Think they'll see us?" + +"Oh, Tom, they can see in the dark, same as cats. I wisht I hadn't +come." + +"Oh, don't be afeard. I don't believe they'll bother us. We ain't +doing any harm. If we keep perfectly still, maybe they won't notice us +at all." + +"I'll try to, Tom, but, Lord, I'm all of a shiver." + +"Listen!" + +The boys bent their heads together and scarcely breathed. A muffled +sound of voices floated up from the far end of the graveyard. + +"Look! See there!" whispered Tom. "What is it?" + +"It's devil-fire. Oh, Tom, this is awful." + +Some vague figures approached through the gloom, swinging an +old-fashioned tin lantern that freckled the ground with innumerable +little spangles of light. Presently Huckleberry whispered with a +shudder: + +"It's the devils sure enough. Three of 'em! Lordy, Tom, we're goners! +Can you pray?" + +"I'll try, but don't you be afeard. They ain't going to hurt us. 'Now +I lay me down to sleep, I--'" + +"Sh!" + +"What is it, Huck?" + +"They're HUMANS! One of 'em is, anyway. One of 'em's old Muff Potter's +voice." + +"No--'tain't so, is it?" + +"I bet I know it. Don't you stir nor budge. He ain't sharp enough to +notice us. Drunk, the same as usual, likely--blamed old rip!" + +"All right, I'll keep still. Now they're stuck. Can't find it. Here +they come again. Now they're hot. Cold again. Hot again. Red hot! +They're p'inted right, this time. Say, Huck, I know another o' them +voices; it's Injun Joe." + +"That's so--that murderin' half-breed! I'd druther they was devils a +dern sight. What kin they be up to?" + +The whisper died wholly out, now, for the three men had reached the +grave and stood within a few feet of the boys' hiding-place. + +"Here it is," said the third voice; and the owner of it held the +lantern up and revealed the face of young Doctor Robinson. + +Potter and Injun Joe were carrying a handbarrow with a rope and a +couple of shovels on it. They cast down their load and began to open +the grave. The doctor put the lantern at the head of the grave and came +and sat down with his back against one of the elm trees. He was so +close the boys could have touched him. + +"Hurry, men!" he said, in a low voice; "the moon might come out at any +moment." + +They growled a response and went on digging. For some time there was +no noise but the grating sound of the spades discharging their freight +of mould and gravel. It was very monotonous. Finally a spade struck +upon the coffin with a dull woody accent, and within another minute or +two the men had hoisted it out on the ground. They pried off the lid +with their shovels, got out the body and dumped it rudely on the +ground. The moon drifted from behind the clouds and exposed the pallid +face. The barrow was got ready and the corpse placed on it, covered +with a blanket, and bound to its place with the rope. Potter took out a +large spring-knife and cut off the dangling end of the rope and then +said: + +"Now the cussed thing's ready, Sawbones, and you'll just out with +another five, or here she stays." + +"That's the talk!" said Injun Joe. + +"Look here, what does this mean?" said the doctor. "You required your +pay in advance, and I've paid you." + +"Yes, and you done more than that," said Injun Joe, approaching the +doctor, who was now standing. "Five years ago you drove me away from +your father's kitchen one night, when I come to ask for something to +eat, and you said I warn't there for any good; and when I swore I'd get +even with you if it took a hundred years, your father had me jailed for +a vagrant. Did you think I'd forget? The Injun blood ain't in me for +nothing. And now I've GOT you, and you got to SETTLE, you know!" + +He was threatening the doctor, with his fist in his face, by this +time. The doctor struck out suddenly and stretched the ruffian on the +ground. Potter dropped his knife, and exclaimed: + +"Here, now, don't you hit my pard!" and the next moment he had +grappled with the doctor and the two were struggling with might and +main, trampling the grass and tearing the ground with their heels. +Injun Joe sprang to his feet, his eyes flaming with passion, snatched +up Potter's knife, and went creeping, catlike and stooping, round and +round about the combatants, seeking an opportunity. All at once the +doctor flung himself free, seized the heavy headboard of Williams' +grave and felled Potter to the earth with it--and in the same instant +the half-breed saw his chance and drove the knife to the hilt in the +young man's breast. He reeled and fell partly upon Potter, flooding him +with his blood, and in the same moment the clouds blotted out the +dreadful spectacle and the two frightened boys went speeding away in +the dark. + +Presently, when the moon emerged again, Injun Joe was standing over +the two forms, contemplating them. The doctor murmured inarticulately, +gave a long gasp or two and was still. The half-breed muttered: + +"THAT score is settled--damn you." + +Then he robbed the body. After which he put the fatal knife in +Potter's open right hand, and sat down on the dismantled coffin. Three +--four--five minutes passed, and then Potter began to stir and moan. His +hand closed upon the knife; he raised it, glanced at it, and let it +fall, with a shudder. Then he sat up, pushing the body from him, and +gazed at it, and then around him, confusedly. His eyes met Joe's. + +"Lord, how is this, Joe?" he said. + +"It's a dirty business," said Joe, without moving. + +"What did you do it for?" + +"I! I never done it!" + +"Look here! That kind of talk won't wash." + +Potter trembled and grew white. + +"I thought I'd got sober. I'd no business to drink to-night. But it's +in my head yet--worse'n when we started here. I'm all in a muddle; +can't recollect anything of it, hardly. Tell me, Joe--HONEST, now, old +feller--did I do it? Joe, I never meant to--'pon my soul and honor, I +never meant to, Joe. Tell me how it was, Joe. Oh, it's awful--and him +so young and promising." + +"Why, you two was scuffling, and he fetched you one with the headboard +and you fell flat; and then up you come, all reeling and staggering +like, and snatched the knife and jammed it into him, just as he fetched +you another awful clip--and here you've laid, as dead as a wedge til +now." + +"Oh, I didn't know what I was a-doing. I wish I may die this minute if +I did. It was all on account of the whiskey and the excitement, I +reckon. I never used a weepon in my life before, Joe. I've fought, but +never with weepons. They'll all say that. Joe, don't tell! Say you +won't tell, Joe--that's a good feller. I always liked you, Joe, and +stood up for you, too. Don't you remember? You WON'T tell, WILL you, +Joe?" And the poor creature dropped on his knees before the stolid +murderer, and clasped his appealing hands. + +"No, you've always been fair and square with me, Muff Potter, and I +won't go back on you. There, now, that's as fair as a man can say." + +"Oh, Joe, you're an angel. I'll bless you for this the longest day I +live." And Potter began to cry. + +"Come, now, that's enough of that. This ain't any time for blubbering. +You be off yonder way and I'll go this. Move, now, and don't leave any +tracks behind you." + +Potter started on a trot that quickly increased to a run. The +half-breed stood looking after him. He muttered: + +"If he's as much stunned with the lick and fuddled with the rum as he +had the look of being, he won't think of the knife till he's gone so +far he'll be afraid to come back after it to such a place by himself +--chicken-heart!" + +Two or three minutes later the murdered man, the blanketed corpse, the +lidless coffin, and the open grave were under no inspection but the +moon's. The stillness was complete again, too. + + + +CHAPTER X + +THE two boys flew on and on, toward the village, speechless with +horror. They glanced backward over their shoulders from time to time, +apprehensively, as if they feared they might be followed. Every stump +that started up in their path seemed a man and an enemy, and made them +catch their breath; and as they sped by some outlying cottages that lay +near the village, the barking of the aroused watch-dogs seemed to give +wings to their feet. + +"If we can only get to the old tannery before we break down!" +whispered Tom, in short catches between breaths. "I can't stand it much +longer." + +Huckleberry's hard pantings were his only reply, and the boys fixed +their eyes on the goal of their hopes and bent to their work to win it. +They gained steadily on it, and at last, breast to breast, they burst +through the open door and fell grateful and exhausted in the sheltering +shadows beyond. By and by their pulses slowed down, and Tom whispered: + +"Huckleberry, what do you reckon'll come of this?" + +"If Doctor Robinson dies, I reckon hanging'll come of it." + +"Do you though?" + +"Why, I KNOW it, Tom." + +Tom thought a while, then he said: + +"Who'll tell? We?" + +"What are you talking about? S'pose something happened and Injun Joe +DIDN'T hang? Why, he'd kill us some time or other, just as dead sure as +we're a laying here." + +"That's just what I was thinking to myself, Huck." + +"If anybody tells, let Muff Potter do it, if he's fool enough. He's +generally drunk enough." + +Tom said nothing--went on thinking. Presently he whispered: + +"Huck, Muff Potter don't know it. How can he tell?" + +"What's the reason he don't know it?" + +"Because he'd just got that whack when Injun Joe done it. D'you reckon +he could see anything? D'you reckon he knowed anything?" + +"By hokey, that's so, Tom!" + +"And besides, look-a-here--maybe that whack done for HIM!" + +"No, 'taint likely, Tom. He had liquor in him; I could see that; and +besides, he always has. Well, when pap's full, you might take and belt +him over the head with a church and you couldn't phase him. He says so, +his own self. So it's the same with Muff Potter, of course. But if a +man was dead sober, I reckon maybe that whack might fetch him; I dono." + +After another reflective silence, Tom said: + +"Hucky, you sure you can keep mum?" + +"Tom, we GOT to keep mum. You know that. That Injun devil wouldn't +make any more of drownding us than a couple of cats, if we was to +squeak 'bout this and they didn't hang him. Now, look-a-here, Tom, less +take and swear to one another--that's what we got to do--swear to keep +mum." + +"I'm agreed. It's the best thing. Would you just hold hands and swear +that we--" + +"Oh no, that wouldn't do for this. That's good enough for little +rubbishy common things--specially with gals, cuz THEY go back on you +anyway, and blab if they get in a huff--but there orter be writing +'bout a big thing like this. And blood." + +Tom's whole being applauded this idea. It was deep, and dark, and +awful; the hour, the circumstances, the surroundings, were in keeping +with it. He picked up a clean pine shingle that lay in the moonlight, +took a little fragment of "red keel" out of his pocket, got the moon on +his work, and painfully scrawled these lines, emphasizing each slow +down-stroke by clamping his tongue between his teeth, and letting up +the pressure on the up-strokes. [See next page.] + + "Huck Finn and + Tom Sawyer swears + they will keep mum + about This and They + wish They may Drop + down dead in Their + Tracks if They ever + Tell and Rot." + +Huckleberry was filled with admiration of Tom's facility in writing, +and the sublimity of his language. He at once took a pin from his lapel +and was going to prick his flesh, but Tom said: + +"Hold on! Don't do that. A pin's brass. It might have verdigrease on +it." + +"What's verdigrease?" + +"It's p'ison. That's what it is. You just swaller some of it once +--you'll see." + +So Tom unwound the thread from one of his needles, and each boy +pricked the ball of his thumb and squeezed out a drop of blood. In +time, after many squeezes, Tom managed to sign his initials, using the +ball of his little finger for a pen. Then he showed Huckleberry how to +make an H and an F, and the oath was complete. They buried the shingle +close to the wall, with some dismal ceremonies and incantations, and +the fetters that bound their tongues were considered to be locked and +the key thrown away. + +A figure crept stealthily through a break in the other end of the +ruined building, now, but they did not notice it. + +"Tom," whispered Huckleberry, "does this keep us from EVER telling +--ALWAYS?" + +"Of course it does. It don't make any difference WHAT happens, we got +to keep mum. We'd drop down dead--don't YOU know that?" + +"Yes, I reckon that's so." + +They continued to whisper for some little time. Presently a dog set up +a long, lugubrious howl just outside--within ten feet of them. The boys +clasped each other suddenly, in an agony of fright. + +"Which of us does he mean?" gasped Huckleberry. + +"I dono--peep through the crack. Quick!" + +"No, YOU, Tom!" + +"I can't--I can't DO it, Huck!" + +"Please, Tom. There 'tis again!" + +"Oh, lordy, I'm thankful!" whispered Tom. "I know his voice. It's Bull +Harbison." * + +[* If Mr. Harbison owned a slave named Bull, Tom would have spoken of +him as "Harbison's Bull," but a son or a dog of that name was "Bull +Harbison."] + +"Oh, that's good--I tell you, Tom, I was most scared to death; I'd a +bet anything it was a STRAY dog." + +The dog howled again. The boys' hearts sank once more. + +"Oh, my! that ain't no Bull Harbison!" whispered Huckleberry. "DO, Tom!" + +Tom, quaking with fear, yielded, and put his eye to the crack. His +whisper was hardly audible when he said: + +"Oh, Huck, IT S A STRAY DOG!" + +"Quick, Tom, quick! Who does he mean?" + +"Huck, he must mean us both--we're right together." + +"Oh, Tom, I reckon we're goners. I reckon there ain't no mistake 'bout +where I'LL go to. I been so wicked." + +"Dad fetch it! This comes of playing hookey and doing everything a +feller's told NOT to do. I might a been good, like Sid, if I'd a tried +--but no, I wouldn't, of course. But if ever I get off this time, I lay +I'll just WALLER in Sunday-schools!" And Tom began to snuffle a little. + +"YOU bad!" and Huckleberry began to snuffle too. "Consound it, Tom +Sawyer, you're just old pie, 'longside o' what I am. Oh, LORDY, lordy, +lordy, I wisht I only had half your chance." + +Tom choked off and whispered: + +"Look, Hucky, look! He's got his BACK to us!" + +Hucky looked, with joy in his heart. + +"Well, he has, by jingoes! Did he before?" + +"Yes, he did. But I, like a fool, never thought. Oh, this is bully, +you know. NOW who can he mean?" + +The howling stopped. Tom pricked up his ears. + +"Sh! What's that?" he whispered. + +"Sounds like--like hogs grunting. No--it's somebody snoring, Tom." + +"That IS it! Where 'bouts is it, Huck?" + +"I bleeve it's down at 'tother end. Sounds so, anyway. Pap used to +sleep there, sometimes, 'long with the hogs, but laws bless you, he +just lifts things when HE snores. Besides, I reckon he ain't ever +coming back to this town any more." + +The spirit of adventure rose in the boys' souls once more. + +"Hucky, do you das't to go if I lead?" + +"I don't like to, much. Tom, s'pose it's Injun Joe!" + +Tom quailed. But presently the temptation rose up strong again and the +boys agreed to try, with the understanding that they would take to +their heels if the snoring stopped. So they went tiptoeing stealthily +down, the one behind the other. When they had got to within five steps +of the snorer, Tom stepped on a stick, and it broke with a sharp snap. +The man moaned, writhed a little, and his face came into the moonlight. +It was Muff Potter. The boys' hearts had stood still, and their hopes +too, when the man moved, but their fears passed away now. They tiptoed +out, through the broken weather-boarding, and stopped at a little +distance to exchange a parting word. That long, lugubrious howl rose on +the night air again! They turned and saw the strange dog standing +within a few feet of where Potter was lying, and FACING Potter, with +his nose pointing heavenward. + +"Oh, geeminy, it's HIM!" exclaimed both boys, in a breath. + +"Say, Tom--they say a stray dog come howling around Johnny Miller's +house, 'bout midnight, as much as two weeks ago; and a whippoorwill +come in and lit on the banisters and sung, the very same evening; and +there ain't anybody dead there yet." + +"Well, I know that. And suppose there ain't. Didn't Gracie Miller fall +in the kitchen fire and burn herself terrible the very next Saturday?" + +"Yes, but she ain't DEAD. And what's more, she's getting better, too." + +"All right, you wait and see. She's a goner, just as dead sure as Muff +Potter's a goner. That's what the niggers say, and they know all about +these kind of things, Huck." + +Then they separated, cogitating. When Tom crept in at his bedroom +window the night was almost spent. He undressed with excessive caution, +and fell asleep congratulating himself that nobody knew of his +escapade. He was not aware that the gently-snoring Sid was awake, and +had been so for an hour. + +When Tom awoke, Sid was dressed and gone. There was a late look in the +light, a late sense in the atmosphere. He was startled. Why had he not +been called--persecuted till he was up, as usual? The thought filled +him with bodings. Within five minutes he was dressed and down-stairs, +feeling sore and drowsy. The family were still at table, but they had +finished breakfast. There was no voice of rebuke; but there were +averted eyes; there was a silence and an air of solemnity that struck a +chill to the culprit's heart. He sat down and tried to seem gay, but it +was up-hill work; it roused no smile, no response, and he lapsed into +silence and let his heart sink down to the depths. + +After breakfast his aunt took him aside, and Tom almost brightened in +the hope that he was going to be flogged; but it was not so. His aunt +wept over him and asked him how he could go and break her old heart so; +and finally told him to go on, and ruin himself and bring her gray +hairs with sorrow to the grave, for it was no use for her to try any +more. This was worse than a thousand whippings, and Tom's heart was +sorer now than his body. He cried, he pleaded for forgiveness, promised +to reform over and over again, and then received his dismissal, feeling +that he had won but an imperfect forgiveness and established but a +feeble confidence. + +He left the presence too miserable to even feel revengeful toward Sid; +and so the latter's prompt retreat through the back gate was +unnecessary. He moped to school gloomy and sad, and took his flogging, +along with Joe Harper, for playing hookey the day before, with the air +of one whose heart was busy with heavier woes and wholly dead to +trifles. Then he betook himself to his seat, rested his elbows on his +desk and his jaws in his hands, and stared at the wall with the stony +stare of suffering that has reached the limit and can no further go. +His elbow was pressing against some hard substance. After a long time +he slowly and sadly changed his position, and took up this object with +a sigh. It was in a paper. He unrolled it. A long, lingering, colossal +sigh followed, and his heart broke. It was his brass andiron knob! + +This final feather broke the camel's back. + + + +CHAPTER XI + +CLOSE upon the hour of noon the whole village was suddenly electrified +with the ghastly news. No need of the as yet undreamed-of telegraph; +the tale flew from man to man, from group to group, from house to +house, with little less than telegraphic speed. Of course the +schoolmaster gave holiday for that afternoon; the town would have +thought strangely of him if he had not. + +A gory knife had been found close to the murdered man, and it had been +recognized by somebody as belonging to Muff Potter--so the story ran. +And it was said that a belated citizen had come upon Potter washing +himself in the "branch" about one or two o'clock in the morning, and +that Potter had at once sneaked off--suspicious circumstances, +especially the washing which was not a habit with Potter. It was also +said that the town had been ransacked for this "murderer" (the public +are not slow in the matter of sifting evidence and arriving at a +verdict), but that he could not be found. Horsemen had departed down +all the roads in every direction, and the Sheriff "was confident" that +he would be captured before night. + +All the town was drifting toward the graveyard. Tom's heartbreak +vanished and he joined the procession, not because he would not a +thousand times rather go anywhere else, but because an awful, +unaccountable fascination drew him on. Arrived at the dreadful place, +he wormed his small body through the crowd and saw the dismal +spectacle. It seemed to him an age since he was there before. Somebody +pinched his arm. He turned, and his eyes met Huckleberry's. Then both +looked elsewhere at once, and wondered if anybody had noticed anything +in their mutual glance. But everybody was talking, and intent upon the +grisly spectacle before them. + +"Poor fellow!" "Poor young fellow!" "This ought to be a lesson to +grave robbers!" "Muff Potter'll hang for this if they catch him!" This +was the drift of remark; and the minister said, "It was a judgment; His +hand is here." + +Now Tom shivered from head to heel; for his eye fell upon the stolid +face of Injun Joe. At this moment the crowd began to sway and struggle, +and voices shouted, "It's him! it's him! he's coming himself!" + +"Who? Who?" from twenty voices. + +"Muff Potter!" + +"Hallo, he's stopped!--Look out, he's turning! Don't let him get away!" + +People in the branches of the trees over Tom's head said he wasn't +trying to get away--he only looked doubtful and perplexed. + +"Infernal impudence!" said a bystander; "wanted to come and take a +quiet look at his work, I reckon--didn't expect any company." + +The crowd fell apart, now, and the Sheriff came through, +ostentatiously leading Potter by the arm. The poor fellow's face was +haggard, and his eyes showed the fear that was upon him. When he stood +before the murdered man, he shook as with a palsy, and he put his face +in his hands and burst into tears. + +"I didn't do it, friends," he sobbed; "'pon my word and honor I never +done it." + +"Who's accused you?" shouted a voice. + +This shot seemed to carry home. Potter lifted his face and looked +around him with a pathetic hopelessness in his eyes. He saw Injun Joe, +and exclaimed: + +"Oh, Injun Joe, you promised me you'd never--" + +"Is that your knife?" and it was thrust before him by the Sheriff. + +Potter would have fallen if they had not caught him and eased him to +the ground. Then he said: + +"Something told me 't if I didn't come back and get--" He shuddered; +then waved his nerveless hand with a vanquished gesture and said, "Tell +'em, Joe, tell 'em--it ain't any use any more." + +Then Huckleberry and Tom stood dumb and staring, and heard the +stony-hearted liar reel off his serene statement, they expecting every +moment that the clear sky would deliver God's lightnings upon his head, +and wondering to see how long the stroke was delayed. And when he had +finished and still stood alive and whole, their wavering impulse to +break their oath and save the poor betrayed prisoner's life faded and +vanished away, for plainly this miscreant had sold himself to Satan and +it would be fatal to meddle with the property of such a power as that. + +"Why didn't you leave? What did you want to come here for?" somebody +said. + +"I couldn't help it--I couldn't help it," Potter moaned. "I wanted to +run away, but I couldn't seem to come anywhere but here." And he fell +to sobbing again. + +Injun Joe repeated his statement, just as calmly, a few minutes +afterward on the inquest, under oath; and the boys, seeing that the +lightnings were still withheld, were confirmed in their belief that Joe +had sold himself to the devil. He was now become, to them, the most +balefully interesting object they had ever looked upon, and they could +not take their fascinated eyes from his face. + +They inwardly resolved to watch him nights, when opportunity should +offer, in the hope of getting a glimpse of his dread master. + +Injun Joe helped to raise the body of the murdered man and put it in a +wagon for removal; and it was whispered through the shuddering crowd +that the wound bled a little! The boys thought that this happy +circumstance would turn suspicion in the right direction; but they were +disappointed, for more than one villager remarked: + +"It was within three feet of Muff Potter when it done it." + +Tom's fearful secret and gnawing conscience disturbed his sleep for as +much as a week after this; and at breakfast one morning Sid said: + +"Tom, you pitch around and talk in your sleep so much that you keep me +awake half the time." + +Tom blanched and dropped his eyes. + +"It's a bad sign," said Aunt Polly, gravely. "What you got on your +mind, Tom?" + +"Nothing. Nothing 't I know of." But the boy's hand shook so that he +spilled his coffee. + +"And you do talk such stuff," Sid said. "Last night you said, 'It's +blood, it's blood, that's what it is!' You said that over and over. And +you said, 'Don't torment me so--I'll tell!' Tell WHAT? What is it +you'll tell?" + +Everything was swimming before Tom. There is no telling what might +have happened, now, but luckily the concern passed out of Aunt Polly's +face and she came to Tom's relief without knowing it. She said: + +"Sho! It's that dreadful murder. I dream about it most every night +myself. Sometimes I dream it's me that done it." + +Mary said she had been affected much the same way. Sid seemed +satisfied. Tom got out of the presence as quick as he plausibly could, +and after that he complained of toothache for a week, and tied up his +jaws every night. He never knew that Sid lay nightly watching, and +frequently slipped the bandage free and then leaned on his elbow +listening a good while at a time, and afterward slipped the bandage +back to its place again. Tom's distress of mind wore off gradually and +the toothache grew irksome and was discarded. If Sid really managed to +make anything out of Tom's disjointed mutterings, he kept it to himself. + +It seemed to Tom that his schoolmates never would get done holding +inquests on dead cats, and thus keeping his trouble present to his +mind. Sid noticed that Tom never was coroner at one of these inquiries, +though it had been his habit to take the lead in all new enterprises; +he noticed, too, that Tom never acted as a witness--and that was +strange; and Sid did not overlook the fact that Tom even showed a +marked aversion to these inquests, and always avoided them when he +could. Sid marvelled, but said nothing. However, even inquests went out +of vogue at last, and ceased to torture Tom's conscience. + +Every day or two, during this time of sorrow, Tom watched his +opportunity and went to the little grated jail-window and smuggled such +small comforts through to the "murderer" as he could get hold of. The +jail was a trifling little brick den that stood in a marsh at the edge +of the village, and no guards were afforded for it; indeed, it was +seldom occupied. These offerings greatly helped to ease Tom's +conscience. + +The villagers had a strong desire to tar-and-feather Injun Joe and +ride him on a rail, for body-snatching, but so formidable was his +character that nobody could be found who was willing to take the lead +in the matter, so it was dropped. He had been careful to begin both of +his inquest-statements with the fight, without confessing the +grave-robbery that preceded it; therefore it was deemed wisest not +to try the case in the courts at present. + + + +CHAPTER XII + +ONE of the reasons why Tom's mind had drifted away from its secret +troubles was, that it had found a new and weighty matter to interest +itself about. Becky Thatcher had stopped coming to school. Tom had +struggled with his pride a few days, and tried to "whistle her down the +wind," but failed. He began to find himself hanging around her father's +house, nights, and feeling very miserable. She was ill. What if she +should die! There was distraction in the thought. He no longer took an +interest in war, nor even in piracy. The charm of life was gone; there +was nothing but dreariness left. He put his hoop away, and his bat; +there was no joy in them any more. His aunt was concerned. She began to +try all manner of remedies on him. She was one of those people who are +infatuated with patent medicines and all new-fangled methods of +producing health or mending it. She was an inveterate experimenter in +these things. When something fresh in this line came out she was in a +fever, right away, to try it; not on herself, for she was never ailing, +but on anybody else that came handy. She was a subscriber for all the +"Health" periodicals and phrenological frauds; and the solemn ignorance +they were inflated with was breath to her nostrils. All the "rot" they +contained about ventilation, and how to go to bed, and how to get up, +and what to eat, and what to drink, and how much exercise to take, and +what frame of mind to keep one's self in, and what sort of clothing to +wear, was all gospel to her, and she never observed that her +health-journals of the current month customarily upset everything they +had recommended the month before. She was as simple-hearted and honest +as the day was long, and so she was an easy victim. She gathered +together her quack periodicals and her quack medicines, and thus armed +with death, went about on her pale horse, metaphorically speaking, with +"hell following after." But she never suspected that she was not an +angel of healing and the balm of Gilead in disguise, to the suffering +neighbors. + +The water treatment was new, now, and Tom's low condition was a +windfall to her. She had him out at daylight every morning, stood him +up in the woodshed and drowned him with a deluge of cold water; then +she scrubbed him down with a towel like a file, and so brought him to; +then she rolled him up in a wet sheet and put him away under blankets +till she sweated his soul clean and "the yellow stains of it came +through his pores"--as Tom said. + +Yet notwithstanding all this, the boy grew more and more melancholy +and pale and dejected. She added hot baths, sitz baths, shower baths, +and plunges. The boy remained as dismal as a hearse. She began to +assist the water with a slim oatmeal diet and blister-plasters. She +calculated his capacity as she would a jug's, and filled him up every +day with quack cure-alls. + +Tom had become indifferent to persecution by this time. This phase +filled the old lady's heart with consternation. This indifference must +be broken up at any cost. Now she heard of Pain-killer for the first +time. She ordered a lot at once. She tasted it and was filled with +gratitude. It was simply fire in a liquid form. She dropped the water +treatment and everything else, and pinned her faith to Pain-killer. She +gave Tom a teaspoonful and watched with the deepest anxiety for the +result. Her troubles were instantly at rest, her soul at peace again; +for the "indifference" was broken up. The boy could not have shown a +wilder, heartier interest, if she had built a fire under him. + +Tom felt that it was time to wake up; this sort of life might be +romantic enough, in his blighted condition, but it was getting to have +too little sentiment and too much distracting variety about it. So he +thought over various plans for relief, and finally hit pon that of +professing to be fond of Pain-killer. He asked for it so often that he +became a nuisance, and his aunt ended by telling him to help himself +and quit bothering her. If it had been Sid, she would have had no +misgivings to alloy her delight; but since it was Tom, she watched the +bottle clandestinely. She found that the medicine did really diminish, +but it did not occur to her that the boy was mending the health of a +crack in the sitting-room floor with it. + +One day Tom was in the act of dosing the crack when his aunt's yellow +cat came along, purring, eying the teaspoon avariciously, and begging +for a taste. Tom said: + +"Don't ask for it unless you want it, Peter." + +But Peter signified that he did want it. + +"You better make sure." + +Peter was sure. + +"Now you've asked for it, and I'll give it to you, because there ain't +anything mean about me; but if you find you don't like it, you mustn't +blame anybody but your own self." + +Peter was agreeable. So Tom pried his mouth open and poured down the +Pain-killer. Peter sprang a couple of yards in the air, and then +delivered a war-whoop and set off round and round the room, banging +against furniture, upsetting flower-pots, and making general havoc. +Next he rose on his hind feet and pranced around, in a frenzy of +enjoyment, with his head over his shoulder and his voice proclaiming +his unappeasable happiness. Then he went tearing around the house again +spreading chaos and destruction in his path. Aunt Polly entered in time +to see him throw a few double summersets, deliver a final mighty +hurrah, and sail through the open window, carrying the rest of the +flower-pots with him. The old lady stood petrified with astonishment, +peering over her glasses; Tom lay on the floor expiring with laughter. + +"Tom, what on earth ails that cat?" + +"I don't know, aunt," gasped the boy. + +"Why, I never see anything like it. What did make him act so?" + +"Deed I don't know, Aunt Polly; cats always act so when they're having +a good time." + +"They do, do they?" There was something in the tone that made Tom +apprehensive. + +"Yes'm. That is, I believe they do." + +"You DO?" + +"Yes'm." + +The old lady was bending down, Tom watching, with interest emphasized +by anxiety. Too late he divined her "drift." The handle of the telltale +teaspoon was visible under the bed-valance. Aunt Polly took it, held it +up. Tom winced, and dropped his eyes. Aunt Polly raised him by the +usual handle--his ear--and cracked his head soundly with her thimble. + +"Now, sir, what did you want to treat that poor dumb beast so, for?" + +"I done it out of pity for him--because he hadn't any aunt." + +"Hadn't any aunt!--you numskull. What has that got to do with it?" + +"Heaps. Because if he'd had one she'd a burnt him out herself! She'd a +roasted his bowels out of him 'thout any more feeling than if he was a +human!" + +Aunt Polly felt a sudden pang of remorse. This was putting the thing +in a new light; what was cruelty to a cat MIGHT be cruelty to a boy, +too. She began to soften; she felt sorry. Her eyes watered a little, +and she put her hand on Tom's head and said gently: + +"I was meaning for the best, Tom. And, Tom, it DID do you good." + +Tom looked up in her face with just a perceptible twinkle peeping +through his gravity. + +"I know you was meaning for the best, aunty, and so was I with Peter. +It done HIM good, too. I never see him get around so since--" + +"Oh, go 'long with you, Tom, before you aggravate me again. And you +try and see if you can't be a good boy, for once, and you needn't take +any more medicine." + +Tom reached school ahead of time. It was noticed that this strange +thing had been occurring every day latterly. And now, as usual of late, +he hung about the gate of the schoolyard instead of playing with his +comrades. He was sick, he said, and he looked it. He tried to seem to +be looking everywhere but whither he really was looking--down the road. +Presently Jeff Thatcher hove in sight, and Tom's face lighted; he gazed +a moment, and then turned sorrowfully away. When Jeff arrived, Tom +accosted him; and "led up" warily to opportunities for remark about +Becky, but the giddy lad never could see the bait. Tom watched and +watched, hoping whenever a frisking frock came in sight, and hating the +owner of it as soon as he saw she was not the right one. At last frocks +ceased to appear, and he dropped hopelessly into the dumps; he entered +the empty schoolhouse and sat down to suffer. Then one more frock +passed in at the gate, and Tom's heart gave a great bound. The next +instant he was out, and "going on" like an Indian; yelling, laughing, +chasing boys, jumping over the fence at risk of life and limb, throwing +handsprings, standing on his head--doing all the heroic things he could +conceive of, and keeping a furtive eye out, all the while, to see if +Becky Thatcher was noticing. But she seemed to be unconscious of it +all; she never looked. Could it be possible that she was not aware that +he was there? He carried his exploits to her immediate vicinity; came +war-whooping around, snatched a boy's cap, hurled it to the roof of the +schoolhouse, broke through a group of boys, tumbling them in every +direction, and fell sprawling, himself, under Becky's nose, almost +upsetting her--and she turned, with her nose in the air, and he heard +her say: "Mf! some people think they're mighty smart--always showing +off!" + +Tom's cheeks burned. He gathered himself up and sneaked off, crushed +and crestfallen. + + + +CHAPTER XIII + +TOM'S mind was made up now. He was gloomy and desperate. He was a +forsaken, friendless boy, he said; nobody loved him; when they found +out what they had driven him to, perhaps they would be sorry; he had +tried to do right and get along, but they would not let him; since +nothing would do them but to be rid of him, let it be so; and let them +blame HIM for the consequences--why shouldn't they? What right had the +friendless to complain? Yes, they had forced him to it at last: he +would lead a life of crime. There was no choice. + +By this time he was far down Meadow Lane, and the bell for school to +"take up" tinkled faintly upon his ear. He sobbed, now, to think he +should never, never hear that old familiar sound any more--it was very +hard, but it was forced on him; since he was driven out into the cold +world, he must submit--but he forgave them. Then the sobs came thick +and fast. + +Just at this point he met his soul's sworn comrade, Joe Harper +--hard-eyed, and with evidently a great and dismal purpose in his heart. +Plainly here were "two souls with but a single thought." Tom, wiping +his eyes with his sleeve, began to blubber out something about a +resolution to escape from hard usage and lack of sympathy at home by +roaming abroad into the great world never to return; and ended by +hoping that Joe would not forget him. + +But it transpired that this was a request which Joe had just been +going to make of Tom, and had come to hunt him up for that purpose. His +mother had whipped him for drinking some cream which he had never +tasted and knew nothing about; it was plain that she was tired of him +and wished him to go; if she felt that way, there was nothing for him +to do but succumb; he hoped she would be happy, and never regret having +driven her poor boy out into the unfeeling world to suffer and die. + +As the two boys walked sorrowing along, they made a new compact to +stand by each other and be brothers and never separate till death +relieved them of their troubles. Then they began to lay their plans. +Joe was for being a hermit, and living on crusts in a remote cave, and +dying, some time, of cold and want and grief; but after listening to +Tom, he conceded that there were some conspicuous advantages about a +life of crime, and so he consented to be a pirate. + +Three miles below St. Petersburg, at a point where the Mississippi +River was a trifle over a mile wide, there was a long, narrow, wooded +island, with a shallow bar at the head of it, and this offered well as +a rendezvous. It was not inhabited; it lay far over toward the further +shore, abreast a dense and almost wholly unpeopled forest. So Jackson's +Island was chosen. Who were to be the subjects of their piracies was a +matter that did not occur to them. Then they hunted up Huckleberry +Finn, and he joined them promptly, for all careers were one to him; he +was indifferent. They presently separated to meet at a lonely spot on +the river-bank two miles above the village at the favorite hour--which +was midnight. There was a small log raft there which they meant to +capture. Each would bring hooks and lines, and such provision as he +could steal in the most dark and mysterious way--as became outlaws. And +before the afternoon was done, they had all managed to enjoy the sweet +glory of spreading the fact that pretty soon the town would "hear +something." All who got this vague hint were cautioned to "be mum and +wait." + +About midnight Tom arrived with a boiled ham and a few trifles, +and stopped in a dense undergrowth on a small bluff overlooking the +meeting-place. It was starlight, and very still. The mighty river lay +like an ocean at rest. Tom listened a moment, but no sound disturbed the +quiet. Then he gave a low, distinct whistle. It was answered from under +the bluff. Tom whistled twice more; these signals were answered in the +same way. Then a guarded voice said: + +"Who goes there?" + +"Tom Sawyer, the Black Avenger of the Spanish Main. Name your names." + +"Huck Finn the Red-Handed, and Joe Harper the Terror of the Seas." Tom +had furnished these titles, from his favorite literature. + +"'Tis well. Give the countersign." + +Two hoarse whispers delivered the same awful word simultaneously to +the brooding night: + +"BLOOD!" + +Then Tom tumbled his ham over the bluff and let himself down after it, +tearing both skin and clothes to some extent in the effort. There was +an easy, comfortable path along the shore under the bluff, but it +lacked the advantages of difficulty and danger so valued by a pirate. + +The Terror of the Seas had brought a side of bacon, and had about worn +himself out with getting it there. Finn the Red-Handed had stolen a +skillet and a quantity of half-cured leaf tobacco, and had also brought +a few corn-cobs to make pipes with. But none of the pirates smoked or +"chewed" but himself. The Black Avenger of the Spanish Main said it +would never do to start without some fire. That was a wise thought; +matches were hardly known there in that day. They saw a fire +smouldering upon a great raft a hundred yards above, and they went +stealthily thither and helped themselves to a chunk. They made an +imposing adventure of it, saying, "Hist!" every now and then, and +suddenly halting with finger on lip; moving with hands on imaginary +dagger-hilts; and giving orders in dismal whispers that if "the foe" +stirred, to "let him have it to the hilt," because "dead men tell no +tales." They knew well enough that the raftsmen were all down at the +village laying in stores or having a spree, but still that was no +excuse for their conducting this thing in an unpiratical way. + +They shoved off, presently, Tom in command, Huck at the after oar and +Joe at the forward. Tom stood amidships, gloomy-browed, and with folded +arms, and gave his orders in a low, stern whisper: + +"Luff, and bring her to the wind!" + +"Aye-aye, sir!" + +"Steady, steady-y-y-y!" + +"Steady it is, sir!" + +"Let her go off a point!" + +"Point it is, sir!" + +As the boys steadily and monotonously drove the raft toward mid-stream +it was no doubt understood that these orders were given only for +"style," and were not intended to mean anything in particular. + +"What sail's she carrying?" + +"Courses, tops'ls, and flying-jib, sir." + +"Send the r'yals up! Lay out aloft, there, half a dozen of ye +--foretopmaststuns'l! Lively, now!" + +"Aye-aye, sir!" + +"Shake out that maintogalans'l! Sheets and braces! NOW my hearties!" + +"Aye-aye, sir!" + +"Hellum-a-lee--hard a port! Stand by to meet her when she comes! Port, +port! NOW, men! With a will! Stead-y-y-y!" + +"Steady it is, sir!" + +The raft drew beyond the middle of the river; the boys pointed her +head right, and then lay on their oars. The river was not high, so +there was not more than a two or three mile current. Hardly a word was +said during the next three-quarters of an hour. Now the raft was +passing before the distant town. Two or three glimmering lights showed +where it lay, peacefully sleeping, beyond the vague vast sweep of +star-gemmed water, unconscious of the tremendous event that was happening. +The Black Avenger stood still with folded arms, "looking his last" upon +the scene of his former joys and his later sufferings, and wishing +"she" could see him now, abroad on the wild sea, facing peril and death +with dauntless heart, going to his doom with a grim smile on his lips. +It was but a small strain on his imagination to remove Jackson's Island +beyond eyeshot of the village, and so he "looked his last" with a +broken and satisfied heart. The other pirates were looking their last, +too; and they all looked so long that they came near letting the +current drift them out of the range of the island. But they discovered +the danger in time, and made shift to avert it. About two o'clock in +the morning the raft grounded on the bar two hundred yards above the +head of the island, and they waded back and forth until they had landed +their freight. Part of the little raft's belongings consisted of an old +sail, and this they spread over a nook in the bushes for a tent to +shelter their provisions; but they themselves would sleep in the open +air in good weather, as became outlaws. + +They built a fire against the side of a great log twenty or thirty +steps within the sombre depths of the forest, and then cooked some +bacon in the frying-pan for supper, and used up half of the corn "pone" +stock they had brought. It seemed glorious sport to be feasting in that +wild, free way in the virgin forest of an unexplored and uninhabited +island, far from the haunts of men, and they said they never would +return to civilization. The climbing fire lit up their faces and threw +its ruddy glare upon the pillared tree-trunks of their forest temple, +and upon the varnished foliage and festooning vines. + +When the last crisp slice of bacon was gone, and the last allowance of +corn pone devoured, the boys stretched themselves out on the grass, +filled with contentment. They could have found a cooler place, but they +would not deny themselves such a romantic feature as the roasting +camp-fire. + +"AIN'T it gay?" said Joe. + +"It's NUTS!" said Tom. "What would the boys say if they could see us?" + +"Say? Well, they'd just die to be here--hey, Hucky!" + +"I reckon so," said Huckleberry; "anyways, I'm suited. I don't want +nothing better'n this. I don't ever get enough to eat, gen'ally--and +here they can't come and pick at a feller and bullyrag him so." + +"It's just the life for me," said Tom. "You don't have to get up, +mornings, and you don't have to go to school, and wash, and all that +blame foolishness. You see a pirate don't have to do ANYTHING, Joe, +when he's ashore, but a hermit HE has to be praying considerable, and +then he don't have any fun, anyway, all by himself that way." + +"Oh yes, that's so," said Joe, "but I hadn't thought much about it, +you know. I'd a good deal rather be a pirate, now that I've tried it." + +"You see," said Tom, "people don't go much on hermits, nowadays, like +they used to in old times, but a pirate's always respected. And a +hermit's got to sleep on the hardest place he can find, and put +sackcloth and ashes on his head, and stand out in the rain, and--" + +"What does he put sackcloth and ashes on his head for?" inquired Huck. + +"I dono. But they've GOT to do it. Hermits always do. You'd have to do +that if you was a hermit." + +"Dern'd if I would," said Huck. + +"Well, what would you do?" + +"I dono. But I wouldn't do that." + +"Why, Huck, you'd HAVE to. How'd you get around it?" + +"Why, I just wouldn't stand it. I'd run away." + +"Run away! Well, you WOULD be a nice old slouch of a hermit. You'd be +a disgrace." + +The Red-Handed made no response, being better employed. He had +finished gouging out a cob, and now he fitted a weed stem to it, loaded +it with tobacco, and was pressing a coal to the charge and blowing a +cloud of fragrant smoke--he was in the full bloom of luxurious +contentment. The other pirates envied him this majestic vice, and +secretly resolved to acquire it shortly. Presently Huck said: + +"What does pirates have to do?" + +Tom said: + +"Oh, they have just a bully time--take ships and burn them, and get +the money and bury it in awful places in their island where there's +ghosts and things to watch it, and kill everybody in the ships--make +'em walk a plank." + +"And they carry the women to the island," said Joe; "they don't kill +the women." + +"No," assented Tom, "they don't kill the women--they're too noble. And +the women's always beautiful, too. + +"And don't they wear the bulliest clothes! Oh no! All gold and silver +and di'monds," said Joe, with enthusiasm. + +"Who?" said Huck. + +"Why, the pirates." + +Huck scanned his own clothing forlornly. + +"I reckon I ain't dressed fitten for a pirate," said he, with a +regretful pathos in his voice; "but I ain't got none but these." + +But the other boys told him the fine clothes would come fast enough, +after they should have begun their adventures. They made him understand +that his poor rags would do to begin with, though it was customary for +wealthy pirates to start with a proper wardrobe. + +Gradually their talk died out and drowsiness began to steal upon the +eyelids of the little waifs. The pipe dropped from the fingers of the +Red-Handed, and he slept the sleep of the conscience-free and the +weary. The Terror of the Seas and the Black Avenger of the Spanish Main +had more difficulty in getting to sleep. They said their prayers +inwardly, and lying down, since there was nobody there with authority +to make them kneel and recite aloud; in truth, they had a mind not to +say them at all, but they were afraid to proceed to such lengths as +that, lest they might call down a sudden and special thunderbolt from +heaven. Then at once they reached and hovered upon the imminent verge +of sleep--but an intruder came, now, that would not "down." It was +conscience. They began to feel a vague fear that they had been doing +wrong to run away; and next they thought of the stolen meat, and then +the real torture came. They tried to argue it away by reminding +conscience that they had purloined sweetmeats and apples scores of +times; but conscience was not to be appeased by such thin +plausibilities; it seemed to them, in the end, that there was no +getting around the stubborn fact that taking sweetmeats was only +"hooking," while taking bacon and hams and such valuables was plain +simple stealing--and there was a command against that in the Bible. So +they inwardly resolved that so long as they remained in the business, +their piracies should not again be sullied with the crime of stealing. +Then conscience granted a truce, and these curiously inconsistent +pirates fell peacefully to sleep. + + + +CHAPTER XIV + +WHEN Tom awoke in the morning, he wondered where he was. He sat up and +rubbed his eyes and looked around. Then he comprehended. It was the +cool gray dawn, and there was a delicious sense of repose and peace in +the deep pervading calm and silence of the woods. Not a leaf stirred; +not a sound obtruded upon great Nature's meditation. Beaded dewdrops +stood upon the leaves and grasses. A white layer of ashes covered the +fire, and a thin blue breath of smoke rose straight into the air. Joe +and Huck still slept. + +Now, far away in the woods a bird called; another answered; presently +the hammering of a woodpecker was heard. Gradually the cool dim gray of +the morning whitened, and as gradually sounds multiplied and life +manifested itself. The marvel of Nature shaking off sleep and going to +work unfolded itself to the musing boy. A little green worm came +crawling over a dewy leaf, lifting two-thirds of his body into the air +from time to time and "sniffing around," then proceeding again--for he +was measuring, Tom said; and when the worm approached him, of its own +accord, he sat as still as a stone, with his hopes rising and falling, +by turns, as the creature still came toward him or seemed inclined to +go elsewhere; and when at last it considered a painful moment with its +curved body in the air and then came decisively down upon Tom's leg and +began a journey over him, his whole heart was glad--for that meant that +he was going to have a new suit of clothes--without the shadow of a +doubt a gaudy piratical uniform. Now a procession of ants appeared, +from nowhere in particular, and went about their labors; one struggled +manfully by with a dead spider five times as big as itself in its arms, +and lugged it straight up a tree-trunk. A brown spotted lady-bug +climbed the dizzy height of a grass blade, and Tom bent down close to +it and said, "Lady-bug, lady-bug, fly away home, your house is on fire, +your children's alone," and she took wing and went off to see about it +--which did not surprise the boy, for he knew of old that this insect was +credulous about conflagrations, and he had practised upon its +simplicity more than once. A tumblebug came next, heaving sturdily at +its ball, and Tom touched the creature, to see it shut its legs against +its body and pretend to be dead. The birds were fairly rioting by this +time. A catbird, the Northern mocker, lit in a tree over Tom's head, +and trilled out her imitations of her neighbors in a rapture of +enjoyment; then a shrill jay swept down, a flash of blue flame, and +stopped on a twig almost within the boy's reach, cocked his head to one +side and eyed the strangers with a consuming curiosity; a gray squirrel +and a big fellow of the "fox" kind came skurrying along, sitting up at +intervals to inspect and chatter at the boys, for the wild things had +probably never seen a human being before and scarcely knew whether to +be afraid or not. All Nature was wide awake and stirring, now; long +lances of sunlight pierced down through the dense foliage far and near, +and a few butterflies came fluttering upon the scene. + +Tom stirred up the other pirates and they all clattered away with a +shout, and in a minute or two were stripped and chasing after and +tumbling over each other in the shallow limpid water of the white +sandbar. They felt no longing for the little village sleeping in the +distance beyond the majestic waste of water. A vagrant current or a +slight rise in the river had carried off their raft, but this only +gratified them, since its going was something like burning the bridge +between them and civilization. + +They came back to camp wonderfully refreshed, glad-hearted, and +ravenous; and they soon had the camp-fire blazing up again. Huck found +a spring of clear cold water close by, and the boys made cups of broad +oak or hickory leaves, and felt that water, sweetened with such a +wildwood charm as that, would be a good enough substitute for coffee. +While Joe was slicing bacon for breakfast, Tom and Huck asked him to +hold on a minute; they stepped to a promising nook in the river-bank +and threw in their lines; almost immediately they had reward. Joe had +not had time to get impatient before they were back again with some +handsome bass, a couple of sun-perch and a small catfish--provisions +enough for quite a family. They fried the fish with the bacon, and were +astonished; for no fish had ever seemed so delicious before. They did +not know that the quicker a fresh-water fish is on the fire after he is +caught the better he is; and they reflected little upon what a sauce +open-air sleeping, open-air exercise, bathing, and a large ingredient +of hunger make, too. + +They lay around in the shade, after breakfast, while Huck had a smoke, +and then went off through the woods on an exploring expedition. They +tramped gayly along, over decaying logs, through tangled underbrush, +among solemn monarchs of the forest, hung from their crowns to the +ground with a drooping regalia of grape-vines. Now and then they came +upon snug nooks carpeted with grass and jeweled with flowers. + +They found plenty of things to be delighted with, but nothing to be +astonished at. They discovered that the island was about three miles +long and a quarter of a mile wide, and that the shore it lay closest to +was only separated from it by a narrow channel hardly two hundred yards +wide. They took a swim about every hour, so it was close upon the +middle of the afternoon when they got back to camp. They were too +hungry to stop to fish, but they fared sumptuously upon cold ham, and +then threw themselves down in the shade to talk. But the talk soon +began to drag, and then died. The stillness, the solemnity that brooded +in the woods, and the sense of loneliness, began to tell upon the +spirits of the boys. They fell to thinking. A sort of undefined longing +crept upon them. This took dim shape, presently--it was budding +homesickness. Even Finn the Red-Handed was dreaming of his doorsteps +and empty hogsheads. But they were all ashamed of their weakness, and +none was brave enough to speak his thought. + +For some time, now, the boys had been dully conscious of a peculiar +sound in the distance, just as one sometimes is of the ticking of a +clock which he takes no distinct note of. But now this mysterious sound +became more pronounced, and forced a recognition. The boys started, +glanced at each other, and then each assumed a listening attitude. +There was a long silence, profound and unbroken; then a deep, sullen +boom came floating down out of the distance. + +"What is it!" exclaimed Joe, under his breath. + +"I wonder," said Tom in a whisper. + +"'Tain't thunder," said Huckleberry, in an awed tone, "becuz thunder--" + +"Hark!" said Tom. "Listen--don't talk." + +They waited a time that seemed an age, and then the same muffled boom +troubled the solemn hush. + +"Let's go and see." + +They sprang to their feet and hurried to the shore toward the town. +They parted the bushes on the bank and peered out over the water. The +little steam ferryboat was about a mile below the village, drifting +with the current. Her broad deck seemed crowded with people. There were +a great many skiffs rowing about or floating with the stream in the +neighborhood of the ferryboat, but the boys could not determine what +the men in them were doing. Presently a great jet of white smoke burst +from the ferryboat's side, and as it expanded and rose in a lazy cloud, +that same dull throb of sound was borne to the listeners again. + +"I know now!" exclaimed Tom; "somebody's drownded!" + +"That's it!" said Huck; "they done that last summer, when Bill Turner +got drownded; they shoot a cannon over the water, and that makes him +come up to the top. Yes, and they take loaves of bread and put +quicksilver in 'em and set 'em afloat, and wherever there's anybody +that's drownded, they'll float right there and stop." + +"Yes, I've heard about that," said Joe. "I wonder what makes the bread +do that." + +"Oh, it ain't the bread, so much," said Tom; "I reckon it's mostly +what they SAY over it before they start it out." + +"But they don't say anything over it," said Huck. "I've seen 'em and +they don't." + +"Well, that's funny," said Tom. "But maybe they say it to themselves. +Of COURSE they do. Anybody might know that." + +The other boys agreed that there was reason in what Tom said, because +an ignorant lump of bread, uninstructed by an incantation, could not be +expected to act very intelligently when set upon an errand of such +gravity. + +"By jings, I wish I was over there, now," said Joe. + +"I do too" said Huck "I'd give heaps to know who it is." + +The boys still listened and watched. Presently a revealing thought +flashed through Tom's mind, and he exclaimed: + +"Boys, I know who's drownded--it's us!" + +They felt like heroes in an instant. Here was a gorgeous triumph; they +were missed; they were mourned; hearts were breaking on their account; +tears were being shed; accusing memories of unkindness to these poor +lost lads were rising up, and unavailing regrets and remorse were being +indulged; and best of all, the departed were the talk of the whole +town, and the envy of all the boys, as far as this dazzling notoriety +was concerned. This was fine. It was worth while to be a pirate, after +all. + +As twilight drew on, the ferryboat went back to her accustomed +business and the skiffs disappeared. The pirates returned to camp. They +were jubilant with vanity over their new grandeur and the illustrious +trouble they were making. They caught fish, cooked supper and ate it, +and then fell to guessing at what the village was thinking and saying +about them; and the pictures they drew of the public distress on their +account were gratifying to look upon--from their point of view. But +when the shadows of night closed them in, they gradually ceased to +talk, and sat gazing into the fire, with their minds evidently +wandering elsewhere. The excitement was gone, now, and Tom and Joe +could not keep back thoughts of certain persons at home who were not +enjoying this fine frolic as much as they were. Misgivings came; they +grew troubled and unhappy; a sigh or two escaped, unawares. By and by +Joe timidly ventured upon a roundabout "feeler" as to how the others +might look upon a return to civilization--not right now, but-- + +Tom withered him with derision! Huck, being uncommitted as yet, joined +in with Tom, and the waverer quickly "explained," and was glad to get +out of the scrape with as little taint of chicken-hearted homesickness +clinging to his garments as he could. Mutiny was effectually laid to +rest for the moment. + +As the night deepened, Huck began to nod, and presently to snore. Joe +followed next. Tom lay upon his elbow motionless, for some time, +watching the two intently. At last he got up cautiously, on his knees, +and went searching among the grass and the flickering reflections flung +by the camp-fire. He picked up and inspected several large +semi-cylinders of the thin white bark of a sycamore, and finally chose +two which seemed to suit him. Then he knelt by the fire and painfully +wrote something upon each of these with his "red keel"; one he rolled up +and put in his jacket pocket, and the other he put in Joe's hat and +removed it to a little distance from the owner. And he also put into the +hat certain schoolboy treasures of almost inestimable value--among them +a lump of chalk, an India-rubber ball, three fishhooks, and one of that +kind of marbles known as a "sure 'nough crystal." Then he tiptoed his +way cautiously among the trees till he felt that he was out of hearing, +and straightway broke into a keen run in the direction of the sandbar. + + + +CHAPTER XV + +A FEW minutes later Tom was in the shoal water of the bar, wading +toward the Illinois shore. Before the depth reached his middle he was +half-way over; the current would permit no more wading, now, so he +struck out confidently to swim the remaining hundred yards. He swam +quartering upstream, but still was swept downward rather faster than he +had expected. However, he reached the shore finally, and drifted along +till he found a low place and drew himself out. He put his hand on his +jacket pocket, found his piece of bark safe, and then struck through +the woods, following the shore, with streaming garments. Shortly before +ten o'clock he came out into an open place opposite the village, and +saw the ferryboat lying in the shadow of the trees and the high bank. +Everything was quiet under the blinking stars. He crept down the bank, +watching with all his eyes, slipped into the water, swam three or four +strokes and climbed into the skiff that did "yawl" duty at the boat's +stern. He laid himself down under the thwarts and waited, panting. + +Presently the cracked bell tapped and a voice gave the order to "cast +off." A minute or two later the skiff's head was standing high up, +against the boat's swell, and the voyage was begun. Tom felt happy in +his success, for he knew it was the boat's last trip for the night. At +the end of a long twelve or fifteen minutes the wheels stopped, and Tom +slipped overboard and swam ashore in the dusk, landing fifty yards +downstream, out of danger of possible stragglers. + +He flew along unfrequented alleys, and shortly found himself at his +aunt's back fence. He climbed over, approached the "ell," and looked in +at the sitting-room window, for a light was burning there. There sat +Aunt Polly, Sid, Mary, and Joe Harper's mother, grouped together, +talking. They were by the bed, and the bed was between them and the +door. Tom went to the door and began to softly lift the latch; then he +pressed gently and the door yielded a crack; he continued pushing +cautiously, and quaking every time it creaked, till he judged he might +squeeze through on his knees; so he put his head through and began, +warily. + +"What makes the candle blow so?" said Aunt Polly. Tom hurried up. +"Why, that door's open, I believe. Why, of course it is. No end of +strange things now. Go 'long and shut it, Sid." + +Tom disappeared under the bed just in time. He lay and "breathed" +himself for a time, and then crept to where he could almost touch his +aunt's foot. + +"But as I was saying," said Aunt Polly, "he warn't BAD, so to say +--only mischEEvous. Only just giddy, and harum-scarum, you know. He +warn't any more responsible than a colt. HE never meant any harm, and +he was the best-hearted boy that ever was"--and she began to cry. + +"It was just so with my Joe--always full of his devilment, and up to +every kind of mischief, but he was just as unselfish and kind as he +could be--and laws bless me, to think I went and whipped him for taking +that cream, never once recollecting that I throwed it out myself +because it was sour, and I never to see him again in this world, never, +never, never, poor abused boy!" And Mrs. Harper sobbed as if her heart +would break. + +"I hope Tom's better off where he is," said Sid, "but if he'd been +better in some ways--" + +"SID!" Tom felt the glare of the old lady's eye, though he could not +see it. "Not a word against my Tom, now that he's gone! God'll take +care of HIM--never you trouble YOURself, sir! Oh, Mrs. Harper, I don't +know how to give him up! I don't know how to give him up! He was such a +comfort to me, although he tormented my old heart out of me, 'most." + +"The Lord giveth and the Lord hath taken away--Blessed be the name of +the Lord! But it's so hard--Oh, it's so hard! Only last Saturday my +Joe busted a firecracker right under my nose and I knocked him +sprawling. Little did I know then, how soon--Oh, if it was to do over +again I'd hug him and bless him for it." + +"Yes, yes, yes, I know just how you feel, Mrs. Harper, I know just +exactly how you feel. No longer ago than yesterday noon, my Tom took +and filled the cat full of Pain-killer, and I did think the cretur +would tear the house down. And God forgive me, I cracked Tom's head +with my thimble, poor boy, poor dead boy. But he's out of all his +troubles now. And the last words I ever heard him say was to reproach--" + +But this memory was too much for the old lady, and she broke entirely +down. Tom was snuffling, now, himself--and more in pity of himself than +anybody else. He could hear Mary crying, and putting in a kindly word +for him from time to time. He began to have a nobler opinion of himself +than ever before. Still, he was sufficiently touched by his aunt's +grief to long to rush out from under the bed and overwhelm her with +joy--and the theatrical gorgeousness of the thing appealed strongly to +his nature, too, but he resisted and lay still. + +He went on listening, and gathered by odds and ends that it was +conjectured at first that the boys had got drowned while taking a swim; +then the small raft had been missed; next, certain boys said the +missing lads had promised that the village should "hear something" +soon; the wise-heads had "put this and that together" and decided that +the lads had gone off on that raft and would turn up at the next town +below, presently; but toward noon the raft had been found, lodged +against the Missouri shore some five or six miles below the village +--and then hope perished; they must be drowned, else hunger would have +driven them home by nightfall if not sooner. It was believed that the +search for the bodies had been a fruitless effort merely because the +drowning must have occurred in mid-channel, since the boys, being good +swimmers, would otherwise have escaped to shore. This was Wednesday +night. If the bodies continued missing until Sunday, all hope would be +given over, and the funerals would be preached on that morning. Tom +shuddered. + +Mrs. Harper gave a sobbing good-night and turned to go. Then with a +mutual impulse the two bereaved women flung themselves into each +other's arms and had a good, consoling cry, and then parted. Aunt Polly +was tender far beyond her wont, in her good-night to Sid and Mary. Sid +snuffled a bit and Mary went off crying with all her heart. + +Aunt Polly knelt down and prayed for Tom so touchingly, so +appealingly, and with such measureless love in her words and her old +trembling voice, that he was weltering in tears again, long before she +was through. + +He had to keep still long after she went to bed, for she kept making +broken-hearted ejaculations from time to time, tossing unrestfully, and +turning over. But at last she was still, only moaning a little in her +sleep. Now the boy stole out, rose gradually by the bedside, shaded the +candle-light with his hand, and stood regarding her. His heart was full +of pity for her. He took out his sycamore scroll and placed it by the +candle. But something occurred to him, and he lingered considering. His +face lighted with a happy solution of his thought; he put the bark +hastily in his pocket. Then he bent over and kissed the faded lips, and +straightway made his stealthy exit, latching the door behind him. + +He threaded his way back to the ferry landing, found nobody at large +there, and walked boldly on board the boat, for he knew she was +tenantless except that there was a watchman, who always turned in and +slept like a graven image. He untied the skiff at the stern, slipped +into it, and was soon rowing cautiously upstream. When he had pulled a +mile above the village, he started quartering across and bent himself +stoutly to his work. He hit the landing on the other side neatly, for +this was a familiar bit of work to him. He was moved to capture the +skiff, arguing that it might be considered a ship and therefore +legitimate prey for a pirate, but he knew a thorough search would be +made for it and that might end in revelations. So he stepped ashore and +entered the woods. + +He sat down and took a long rest, torturing himself meanwhile to keep +awake, and then started warily down the home-stretch. The night was far +spent. It was broad daylight before he found himself fairly abreast the +island bar. He rested again until the sun was well up and gilding the +great river with its splendor, and then he plunged into the stream. A +little later he paused, dripping, upon the threshold of the camp, and +heard Joe say: + +"No, Tom's true-blue, Huck, and he'll come back. He won't desert. He +knows that would be a disgrace to a pirate, and Tom's too proud for +that sort of thing. He's up to something or other. Now I wonder what?" + +"Well, the things is ours, anyway, ain't they?" + +"Pretty near, but not yet, Huck. The writing says they are if he ain't +back here to breakfast." + +"Which he is!" exclaimed Tom, with fine dramatic effect, stepping +grandly into camp. + +A sumptuous breakfast of bacon and fish was shortly provided, and as +the boys set to work upon it, Tom recounted (and adorned) his +adventures. They were a vain and boastful company of heroes when the +tale was done. Then Tom hid himself away in a shady nook to sleep till +noon, and the other pirates got ready to fish and explore. + + + +CHAPTER XVI + +AFTER dinner all the gang turned out to hunt for turtle eggs on the +bar. They went about poking sticks into the sand, and when they found a +soft place they went down on their knees and dug with their hands. +Sometimes they would take fifty or sixty eggs out of one hole. They +were perfectly round white things a trifle smaller than an English +walnut. They had a famous fried-egg feast that night, and another on +Friday morning. + +After breakfast they went whooping and prancing out on the bar, and +chased each other round and round, shedding clothes as they went, until +they were naked, and then continued the frolic far away up the shoal +water of the bar, against the stiff current, which latter tripped their +legs from under them from time to time and greatly increased the fun. +And now and then they stooped in a group and splashed water in each +other's faces with their palms, gradually approaching each other, with +averted faces to avoid the strangling sprays, and finally gripping and +struggling till the best man ducked his neighbor, and then they all +went under in a tangle of white legs and arms and came up blowing, +sputtering, laughing, and gasping for breath at one and the same time. + +When they were well exhausted, they would run out and sprawl on the +dry, hot sand, and lie there and cover themselves up with it, and by +and by break for the water again and go through the original +performance once more. Finally it occurred to them that their naked +skin represented flesh-colored "tights" very fairly; so they drew a +ring in the sand and had a circus--with three clowns in it, for none +would yield this proudest post to his neighbor. + +Next they got their marbles and played "knucks" and "ring-taw" and +"keeps" till that amusement grew stale. Then Joe and Huck had another +swim, but Tom would not venture, because he found that in kicking off +his trousers he had kicked his string of rattlesnake rattles off his +ankle, and he wondered how he had escaped cramp so long without the +protection of this mysterious charm. He did not venture again until he +had found it, and by that time the other boys were tired and ready to +rest. They gradually wandered apart, dropped into the "dumps," and fell +to gazing longingly across the wide river to where the village lay +drowsing in the sun. Tom found himself writing "BECKY" in the sand with +his big toe; he scratched it out, and was angry with himself for his +weakness. But he wrote it again, nevertheless; he could not help it. He +erased it once more and then took himself out of temptation by driving +the other boys together and joining them. + +But Joe's spirits had gone down almost beyond resurrection. He was so +homesick that he could hardly endure the misery of it. The tears lay +very near the surface. Huck was melancholy, too. Tom was downhearted, +but tried hard not to show it. He had a secret which he was not ready +to tell, yet, but if this mutinous depression was not broken up soon, +he would have to bring it out. He said, with a great show of +cheerfulness: + +"I bet there's been pirates on this island before, boys. We'll explore +it again. They've hid treasures here somewhere. How'd you feel to light +on a rotten chest full of gold and silver--hey?" + +But it roused only faint enthusiasm, which faded out, with no reply. +Tom tried one or two other seductions; but they failed, too. It was +discouraging work. Joe sat poking up the sand with a stick and looking +very gloomy. Finally he said: + +"Oh, boys, let's give it up. I want to go home. It's so lonesome." + +"Oh no, Joe, you'll feel better by and by," said Tom. "Just think of +the fishing that's here." + +"I don't care for fishing. I want to go home." + +"But, Joe, there ain't such another swimming-place anywhere." + +"Swimming's no good. I don't seem to care for it, somehow, when there +ain't anybody to say I sha'n't go in. I mean to go home." + +"Oh, shucks! Baby! You want to see your mother, I reckon." + +"Yes, I DO want to see my mother--and you would, too, if you had one. +I ain't any more baby than you are." And Joe snuffled a little. + +"Well, we'll let the cry-baby go home to his mother, won't we, Huck? +Poor thing--does it want to see its mother? And so it shall. You like +it here, don't you, Huck? We'll stay, won't we?" + +Huck said, "Y-e-s"--without any heart in it. + +"I'll never speak to you again as long as I live," said Joe, rising. +"There now!" And he moved moodily away and began to dress himself. + +"Who cares!" said Tom. "Nobody wants you to. Go 'long home and get +laughed at. Oh, you're a nice pirate. Huck and me ain't cry-babies. +We'll stay, won't we, Huck? Let him go if he wants to. I reckon we can +get along without him, per'aps." + +But Tom was uneasy, nevertheless, and was alarmed to see Joe go +sullenly on with his dressing. And then it was discomforting to see +Huck eying Joe's preparations so wistfully, and keeping up such an +ominous silence. Presently, without a parting word, Joe began to wade +off toward the Illinois shore. Tom's heart began to sink. He glanced at +Huck. Huck could not bear the look, and dropped his eyes. Then he said: + +"I want to go, too, Tom. It was getting so lonesome anyway, and now +it'll be worse. Let's us go, too, Tom." + +"I won't! You can all go, if you want to. I mean to stay." + +"Tom, I better go." + +"Well, go 'long--who's hendering you." + +Huck began to pick up his scattered clothes. He said: + +"Tom, I wisht you'd come, too. Now you think it over. We'll wait for +you when we get to shore." + +"Well, you'll wait a blame long time, that's all." + +Huck started sorrowfully away, and Tom stood looking after him, with a +strong desire tugging at his heart to yield his pride and go along too. +He hoped the boys would stop, but they still waded slowly on. It +suddenly dawned on Tom that it was become very lonely and still. He +made one final struggle with his pride, and then darted after his +comrades, yelling: + +"Wait! Wait! I want to tell you something!" + +They presently stopped and turned around. When he got to where they +were, he began unfolding his secret, and they listened moodily till at +last they saw the "point" he was driving at, and then they set up a +war-whoop of applause and said it was "splendid!" and said if he had +told them at first, they wouldn't have started away. He made a plausible +excuse; but his real reason had been the fear that not even the secret +would keep them with him any very great length of time, and so he had +meant to hold it in reserve as a last seduction. + +The lads came gayly back and went at their sports again with a will, +chattering all the time about Tom's stupendous plan and admiring the +genius of it. After a dainty egg and fish dinner, Tom said he wanted to +learn to smoke, now. Joe caught at the idea and said he would like to +try, too. So Huck made pipes and filled them. These novices had never +smoked anything before but cigars made of grape-vine, and they "bit" +the tongue, and were not considered manly anyway. + +Now they stretched themselves out on their elbows and began to puff, +charily, and with slender confidence. The smoke had an unpleasant +taste, and they gagged a little, but Tom said: + +"Why, it's just as easy! If I'd a knowed this was all, I'd a learnt +long ago." + +"So would I," said Joe. "It's just nothing." + +"Why, many a time I've looked at people smoking, and thought well I +wish I could do that; but I never thought I could," said Tom. + +"That's just the way with me, hain't it, Huck? You've heard me talk +just that way--haven't you, Huck? I'll leave it to Huck if I haven't." + +"Yes--heaps of times," said Huck. + +"Well, I have too," said Tom; "oh, hundreds of times. Once down by the +slaughter-house. Don't you remember, Huck? Bob Tanner was there, and +Johnny Miller, and Jeff Thatcher, when I said it. Don't you remember, +Huck, 'bout me saying that?" + +"Yes, that's so," said Huck. "That was the day after I lost a white +alley. No, 'twas the day before." + +"There--I told you so," said Tom. "Huck recollects it." + +"I bleeve I could smoke this pipe all day," said Joe. "I don't feel +sick." + +"Neither do I," said Tom. "I could smoke it all day. But I bet you +Jeff Thatcher couldn't." + +"Jeff Thatcher! Why, he'd keel over just with two draws. Just let him +try it once. HE'D see!" + +"I bet he would. And Johnny Miller--I wish could see Johnny Miller +tackle it once." + +"Oh, don't I!" said Joe. "Why, I bet you Johnny Miller couldn't any +more do this than nothing. Just one little snifter would fetch HIM." + +"'Deed it would, Joe. Say--I wish the boys could see us now." + +"So do I." + +"Say--boys, don't say anything about it, and some time when they're +around, I'll come up to you and say, 'Joe, got a pipe? I want a smoke.' +And you'll say, kind of careless like, as if it warn't anything, you'll +say, 'Yes, I got my OLD pipe, and another one, but my tobacker ain't +very good.' And I'll say, 'Oh, that's all right, if it's STRONG +enough.' And then you'll out with the pipes, and we'll light up just as +ca'm, and then just see 'em look!" + +"By jings, that'll be gay, Tom! I wish it was NOW!" + +"So do I! And when we tell 'em we learned when we was off pirating, +won't they wish they'd been along?" + +"Oh, I reckon not! I'll just BET they will!" + +So the talk ran on. But presently it began to flag a trifle, and grow +disjointed. The silences widened; the expectoration marvellously +increased. Every pore inside the boys' cheeks became a spouting +fountain; they could scarcely bail out the cellars under their tongues +fast enough to prevent an inundation; little overflowings down their +throats occurred in spite of all they could do, and sudden retchings +followed every time. Both boys were looking very pale and miserable, +now. Joe's pipe dropped from his nerveless fingers. Tom's followed. +Both fountains were going furiously and both pumps bailing with might +and main. Joe said feebly: + +"I've lost my knife. I reckon I better go and find it." + +Tom said, with quivering lips and halting utterance: + +"I'll help you. You go over that way and I'll hunt around by the +spring. No, you needn't come, Huck--we can find it." + +So Huck sat down again, and waited an hour. Then he found it lonesome, +and went to find his comrades. They were wide apart in the woods, both +very pale, both fast asleep. But something informed him that if they +had had any trouble they had got rid of it. + +They were not talkative at supper that night. They had a humble look, +and when Huck prepared his pipe after the meal and was going to prepare +theirs, they said no, they were not feeling very well--something they +ate at dinner had disagreed with them. + +About midnight Joe awoke, and called the boys. There was a brooding +oppressiveness in the air that seemed to bode something. The boys +huddled themselves together and sought the friendly companionship of +the fire, though the dull dead heat of the breathless atmosphere was +stifling. They sat still, intent and waiting. The solemn hush +continued. Beyond the light of the fire everything was swallowed up in +the blackness of darkness. Presently there came a quivering glow that +vaguely revealed the foliage for a moment and then vanished. By and by +another came, a little stronger. Then another. Then a faint moan came +sighing through the branches of the forest and the boys felt a fleeting +breath upon their cheeks, and shuddered with the fancy that the Spirit +of the Night had gone by. There was a pause. Now a weird flash turned +night into day and showed every little grass-blade, separate and +distinct, that grew about their feet. And it showed three white, +startled faces, too. A deep peal of thunder went rolling and tumbling +down the heavens and lost itself in sullen rumblings in the distance. A +sweep of chilly air passed by, rustling all the leaves and snowing the +flaky ashes broadcast about the fire. Another fierce glare lit up the +forest and an instant crash followed that seemed to rend the tree-tops +right over the boys' heads. They clung together in terror, in the thick +gloom that followed. A few big rain-drops fell pattering upon the +leaves. + +"Quick! boys, go for the tent!" exclaimed Tom. + +They sprang away, stumbling over roots and among vines in the dark, no +two plunging in the same direction. A furious blast roared through the +trees, making everything sing as it went. One blinding flash after +another came, and peal on peal of deafening thunder. And now a +drenching rain poured down and the rising hurricane drove it in sheets +along the ground. The boys cried out to each other, but the roaring +wind and the booming thunder-blasts drowned their voices utterly. +However, one by one they straggled in at last and took shelter under +the tent, cold, scared, and streaming with water; but to have company +in misery seemed something to be grateful for. They could not talk, the +old sail flapped so furiously, even if the other noises would have +allowed them. The tempest rose higher and higher, and presently the +sail tore loose from its fastenings and went winging away on the blast. +The boys seized each others' hands and fled, with many tumblings and +bruises, to the shelter of a great oak that stood upon the river-bank. +Now the battle was at its highest. Under the ceaseless conflagration of +lightning that flamed in the skies, everything below stood out in +clean-cut and shadowless distinctness: the bending trees, the billowy +river, white with foam, the driving spray of spume-flakes, the dim +outlines of the high bluffs on the other side, glimpsed through the +drifting cloud-rack and the slanting veil of rain. Every little while +some giant tree yielded the fight and fell crashing through the younger +growth; and the unflagging thunder-peals came now in ear-splitting +explosive bursts, keen and sharp, and unspeakably appalling. The storm +culminated in one matchless effort that seemed likely to tear the island +to pieces, burn it up, drown it to the tree-tops, blow it away, and +deafen every creature in it, all at one and the same moment. It was a +wild night for homeless young heads to be out in. + +But at last the battle was done, and the forces retired with weaker +and weaker threatenings and grumblings, and peace resumed her sway. The +boys went back to camp, a good deal awed; but they found there was +still something to be thankful for, because the great sycamore, the +shelter of their beds, was a ruin, now, blasted by the lightnings, and +they were not under it when the catastrophe happened. + +Everything in camp was drenched, the camp-fire as well; for they were +but heedless lads, like their generation, and had made no provision +against rain. Here was matter for dismay, for they were soaked through +and chilled. They were eloquent in their distress; but they presently +discovered that the fire had eaten so far up under the great log it had +been built against (where it curved upward and separated itself from +the ground), that a handbreadth or so of it had escaped wetting; so +they patiently wrought until, with shreds and bark gathered from the +under sides of sheltered logs, they coaxed the fire to burn again. Then +they piled on great dead boughs till they had a roaring furnace, and +were glad-hearted once more. They dried their boiled ham and had a +feast, and after that they sat by the fire and expanded and glorified +their midnight adventure until morning, for there was not a dry spot to +sleep on, anywhere around. + +As the sun began to steal in upon the boys, drowsiness came over them, +and they went out on the sandbar and lay down to sleep. They got +scorched out by and by, and drearily set about getting breakfast. After +the meal they felt rusty, and stiff-jointed, and a little homesick once +more. Tom saw the signs, and fell to cheering up the pirates as well as +he could. But they cared nothing for marbles, or circus, or swimming, +or anything. He reminded them of the imposing secret, and raised a ray +of cheer. While it lasted, he got them interested in a new device. This +was to knock off being pirates, for a while, and be Indians for a +change. They were attracted by this idea; so it was not long before +they were stripped, and striped from head to heel with black mud, like +so many zebras--all of them chiefs, of course--and then they went +tearing through the woods to attack an English settlement. + +By and by they separated into three hostile tribes, and darted upon +each other from ambush with dreadful war-whoops, and killed and scalped +each other by thousands. It was a gory day. Consequently it was an +extremely satisfactory one. + +They assembled in camp toward supper-time, hungry and happy; but now a +difficulty arose--hostile Indians could not break the bread of +hospitality together without first making peace, and this was a simple +impossibility without smoking a pipe of peace. There was no other +process that ever they had heard of. Two of the savages almost wished +they had remained pirates. However, there was no other way; so with +such show of cheerfulness as they could muster they called for the pipe +and took their whiff as it passed, in due form. + +And behold, they were glad they had gone into savagery, for they had +gained something; they found that they could now smoke a little without +having to go and hunt for a lost knife; they did not get sick enough to +be seriously uncomfortable. They were not likely to fool away this high +promise for lack of effort. No, they practised cautiously, after +supper, with right fair success, and so they spent a jubilant evening. +They were prouder and happier in their new acquirement than they would +have been in the scalping and skinning of the Six Nations. We will +leave them to smoke and chatter and brag, since we have no further use +for them at present. + + + +CHAPTER XVII + +BUT there was no hilarity in the little town that same tranquil +Saturday afternoon. The Harpers, and Aunt Polly's family, were being +put into mourning, with great grief and many tears. An unusual quiet +possessed the village, although it was ordinarily quiet enough, in all +conscience. The villagers conducted their concerns with an absent air, +and talked little; but they sighed often. The Saturday holiday seemed a +burden to the children. They had no heart in their sports, and +gradually gave them up. + +In the afternoon Becky Thatcher found herself moping about the +deserted schoolhouse yard, and feeling very melancholy. But she found +nothing there to comfort her. She soliloquized: + +"Oh, if I only had a brass andiron-knob again! But I haven't got +anything now to remember him by." And she choked back a little sob. + +Presently she stopped, and said to herself: + +"It was right here. Oh, if it was to do over again, I wouldn't say +that--I wouldn't say it for the whole world. But he's gone now; I'll +never, never, never see him any more." + +This thought broke her down, and she wandered away, with tears rolling +down her cheeks. Then quite a group of boys and girls--playmates of +Tom's and Joe's--came by, and stood looking over the paling fence and +talking in reverent tones of how Tom did so-and-so the last time they +saw him, and how Joe said this and that small trifle (pregnant with +awful prophecy, as they could easily see now!)--and each speaker +pointed out the exact spot where the lost lads stood at the time, and +then added something like "and I was a-standing just so--just as I am +now, and as if you was him--I was as close as that--and he smiled, just +this way--and then something seemed to go all over me, like--awful, you +know--and I never thought what it meant, of course, but I can see now!" + +Then there was a dispute about who saw the dead boys last in life, and +many claimed that dismal distinction, and offered evidences, more or +less tampered with by the witness; and when it was ultimately decided +who DID see the departed last, and exchanged the last words with them, +the lucky parties took upon themselves a sort of sacred importance, and +were gaped at and envied by all the rest. One poor chap, who had no +other grandeur to offer, said with tolerably manifest pride in the +remembrance: + +"Well, Tom Sawyer he licked me once." + +But that bid for glory was a failure. Most of the boys could say that, +and so that cheapened the distinction too much. The group loitered +away, still recalling memories of the lost heroes, in awed voices. + +When the Sunday-school hour was finished, the next morning, the bell +began to toll, instead of ringing in the usual way. It was a very still +Sabbath, and the mournful sound seemed in keeping with the musing hush +that lay upon nature. The villagers began to gather, loitering a moment +in the vestibule to converse in whispers about the sad event. But there +was no whispering in the house; only the funereal rustling of dresses +as the women gathered to their seats disturbed the silence there. None +could remember when the little church had been so full before. There +was finally a waiting pause, an expectant dumbness, and then Aunt Polly +entered, followed by Sid and Mary, and they by the Harper family, all +in deep black, and the whole congregation, the old minister as well, +rose reverently and stood until the mourners were seated in the front +pew. There was another communing silence, broken at intervals by +muffled sobs, and then the minister spread his hands abroad and prayed. +A moving hymn was sung, and the text followed: "I am the Resurrection +and the Life." + +As the service proceeded, the clergyman drew such pictures of the +graces, the winning ways, and the rare promise of the lost lads that +every soul there, thinking he recognized these pictures, felt a pang in +remembering that he had persistently blinded himself to them always +before, and had as persistently seen only faults and flaws in the poor +boys. The minister related many a touching incident in the lives of the +departed, too, which illustrated their sweet, generous natures, and the +people could easily see, now, how noble and beautiful those episodes +were, and remembered with grief that at the time they occurred they had +seemed rank rascalities, well deserving of the cowhide. The +congregation became more and more moved, as the pathetic tale went on, +till at last the whole company broke down and joined the weeping +mourners in a chorus of anguished sobs, the preacher himself giving way +to his feelings, and crying in the pulpit. + +There was a rustle in the gallery, which nobody noticed; a moment +later the church door creaked; the minister raised his streaming eyes +above his handkerchief, and stood transfixed! First one and then +another pair of eyes followed the minister's, and then almost with one +impulse the congregation rose and stared while the three dead boys came +marching up the aisle, Tom in the lead, Joe next, and Huck, a ruin of +drooping rags, sneaking sheepishly in the rear! They had been hid in +the unused gallery listening to their own funeral sermon! + +Aunt Polly, Mary, and the Harpers threw themselves upon their restored +ones, smothered them with kisses and poured out thanksgivings, while +poor Huck stood abashed and uncomfortable, not knowing exactly what to +do or where to hide from so many unwelcoming eyes. He wavered, and +started to slink away, but Tom seized him and said: + +"Aunt Polly, it ain't fair. Somebody's got to be glad to see Huck." + +"And so they shall. I'm glad to see him, poor motherless thing!" And +the loving attentions Aunt Polly lavished upon him were the one thing +capable of making him more uncomfortable than he was before. + +Suddenly the minister shouted at the top of his voice: "Praise God +from whom all blessings flow--SING!--and put your hearts in it!" + +And they did. Old Hundred swelled up with a triumphant burst, and +while it shook the rafters Tom Sawyer the Pirate looked around upon the +envying juveniles about him and confessed in his heart that this was +the proudest moment of his life. + +As the "sold" congregation trooped out they said they would almost be +willing to be made ridiculous again to hear Old Hundred sung like that +once more. + +Tom got more cuffs and kisses that day--according to Aunt Polly's +varying moods--than he had earned before in a year; and he hardly knew +which expressed the most gratefulness to God and affection for himself. + + + +CHAPTER XVIII + +THAT was Tom's great secret--the scheme to return home with his +brother pirates and attend their own funerals. They had paddled over to +the Missouri shore on a log, at dusk on Saturday, landing five or six +miles below the village; they had slept in the woods at the edge of the +town till nearly daylight, and had then crept through back lanes and +alleys and finished their sleep in the gallery of the church among a +chaos of invalided benches. + +At breakfast, Monday morning, Aunt Polly and Mary were very loving to +Tom, and very attentive to his wants. There was an unusual amount of +talk. In the course of it Aunt Polly said: + +"Well, I don't say it wasn't a fine joke, Tom, to keep everybody +suffering 'most a week so you boys had a good time, but it is a pity +you could be so hard-hearted as to let me suffer so. If you could come +over on a log to go to your funeral, you could have come over and give +me a hint some way that you warn't dead, but only run off." + +"Yes, you could have done that, Tom," said Mary; "and I believe you +would if you had thought of it." + +"Would you, Tom?" said Aunt Polly, her face lighting wistfully. "Say, +now, would you, if you'd thought of it?" + +"I--well, I don't know. 'Twould 'a' spoiled everything." + +"Tom, I hoped you loved me that much," said Aunt Polly, with a grieved +tone that discomforted the boy. "It would have been something if you'd +cared enough to THINK of it, even if you didn't DO it." + +"Now, auntie, that ain't any harm," pleaded Mary; "it's only Tom's +giddy way--he is always in such a rush that he never thinks of +anything." + +"More's the pity. Sid would have thought. And Sid would have come and +DONE it, too. Tom, you'll look back, some day, when it's too late, and +wish you'd cared a little more for me when it would have cost you so +little." + +"Now, auntie, you know I do care for you," said Tom. + +"I'd know it better if you acted more like it." + +"I wish now I'd thought," said Tom, with a repentant tone; "but I +dreamt about you, anyway. That's something, ain't it?" + +"It ain't much--a cat does that much--but it's better than nothing. +What did you dream?" + +"Why, Wednesday night I dreamt that you was sitting over there by the +bed, and Sid was sitting by the woodbox, and Mary next to him." + +"Well, so we did. So we always do. I'm glad your dreams could take +even that much trouble about us." + +"And I dreamt that Joe Harper's mother was here." + +"Why, she was here! Did you dream any more?" + +"Oh, lots. But it's so dim, now." + +"Well, try to recollect--can't you?" + +"Somehow it seems to me that the wind--the wind blowed the--the--" + +"Try harder, Tom! The wind did blow something. Come!" + +Tom pressed his fingers on his forehead an anxious minute, and then +said: + +"I've got it now! I've got it now! It blowed the candle!" + +"Mercy on us! Go on, Tom--go on!" + +"And it seems to me that you said, 'Why, I believe that that door--'" + +"Go ON, Tom!" + +"Just let me study a moment--just a moment. Oh, yes--you said you +believed the door was open." + +"As I'm sitting here, I did! Didn't I, Mary! Go on!" + +"And then--and then--well I won't be certain, but it seems like as if +you made Sid go and--and--" + +"Well? Well? What did I make him do, Tom? What did I make him do?" + +"You made him--you--Oh, you made him shut it." + +"Well, for the land's sake! I never heard the beat of that in all my +days! Don't tell ME there ain't anything in dreams, any more. Sereny +Harper shall know of this before I'm an hour older. I'd like to see her +get around THIS with her rubbage 'bout superstition. Go on, Tom!" + +"Oh, it's all getting just as bright as day, now. Next you said I +warn't BAD, only mischeevous and harum-scarum, and not any more +responsible than--than--I think it was a colt, or something." + +"And so it was! Well, goodness gracious! Go on, Tom!" + +"And then you began to cry." + +"So I did. So I did. Not the first time, neither. And then--" + +"Then Mrs. Harper she began to cry, and said Joe was just the same, +and she wished she hadn't whipped him for taking cream when she'd +throwed it out her own self--" + +"Tom! The sperrit was upon you! You was a prophesying--that's what you +was doing! Land alive, go on, Tom!" + +"Then Sid he said--he said--" + +"I don't think I said anything," said Sid. + +"Yes you did, Sid," said Mary. + +"Shut your heads and let Tom go on! What did he say, Tom?" + +"He said--I THINK he said he hoped I was better off where I was gone +to, but if I'd been better sometimes--" + +"THERE, d'you hear that! It was his very words!" + +"And you shut him up sharp." + +"I lay I did! There must 'a' been an angel there. There WAS an angel +there, somewheres!" + +"And Mrs. Harper told about Joe scaring her with a firecracker, and +you told about Peter and the Painkiller--" + +"Just as true as I live!" + +"And then there was a whole lot of talk 'bout dragging the river for +us, and 'bout having the funeral Sunday, and then you and old Miss +Harper hugged and cried, and she went." + +"It happened just so! It happened just so, as sure as I'm a-sitting in +these very tracks. Tom, you couldn't told it more like if you'd 'a' +seen it! And then what? Go on, Tom!" + +"Then I thought you prayed for me--and I could see you and hear every +word you said. And you went to bed, and I was so sorry that I took and +wrote on a piece of sycamore bark, 'We ain't dead--we are only off +being pirates,' and put it on the table by the candle; and then you +looked so good, laying there asleep, that I thought I went and leaned +over and kissed you on the lips." + +"Did you, Tom, DID you! I just forgive you everything for that!" And +she seized the boy in a crushing embrace that made him feel like the +guiltiest of villains. + +"It was very kind, even though it was only a--dream," Sid soliloquized +just audibly. + +"Shut up, Sid! A body does just the same in a dream as he'd do if he +was awake. Here's a big Milum apple I've been saving for you, Tom, if +you was ever found again--now go 'long to school. I'm thankful to the +good God and Father of us all I've got you back, that's long-suffering +and merciful to them that believe on Him and keep His word, though +goodness knows I'm unworthy of it, but if only the worthy ones got His +blessings and had His hand to help them over the rough places, there's +few enough would smile here or ever enter into His rest when the long +night comes. Go 'long Sid, Mary, Tom--take yourselves off--you've +hendered me long enough." + +The children left for school, and the old lady to call on Mrs. Harper +and vanquish her realism with Tom's marvellous dream. Sid had better +judgment than to utter the thought that was in his mind as he left the +house. It was this: "Pretty thin--as long a dream as that, without any +mistakes in it!" + +What a hero Tom was become, now! He did not go skipping and prancing, +but moved with a dignified swagger as became a pirate who felt that the +public eye was on him. And indeed it was; he tried not to seem to see +the looks or hear the remarks as he passed along, but they were food +and drink to him. Smaller boys than himself flocked at his heels, as +proud to be seen with him, and tolerated by him, as if he had been the +drummer at the head of a procession or the elephant leading a menagerie +into town. Boys of his own size pretended not to know he had been away +at all; but they were consuming with envy, nevertheless. They would +have given anything to have that swarthy suntanned skin of his, and his +glittering notoriety; and Tom would not have parted with either for a +circus. + +At school the children made so much of him and of Joe, and delivered +such eloquent admiration from their eyes, that the two heroes were not +long in becoming insufferably "stuck-up." They began to tell their +adventures to hungry listeners--but they only began; it was not a thing +likely to have an end, with imaginations like theirs to furnish +material. And finally, when they got out their pipes and went serenely +puffing around, the very summit of glory was reached. + +Tom decided that he could be independent of Becky Thatcher now. Glory +was sufficient. He would live for glory. Now that he was distinguished, +maybe she would be wanting to "make up." Well, let her--she should see +that he could be as indifferent as some other people. Presently she +arrived. Tom pretended not to see her. He moved away and joined a group +of boys and girls and began to talk. Soon he observed that she was +tripping gayly back and forth with flushed face and dancing eyes, +pretending to be busy chasing schoolmates, and screaming with laughter +when she made a capture; but he noticed that she always made her +captures in his vicinity, and that she seemed to cast a conscious eye +in his direction at such times, too. It gratified all the vicious +vanity that was in him; and so, instead of winning him, it only "set +him up" the more and made him the more diligent to avoid betraying that +he knew she was about. Presently she gave over skylarking, and moved +irresolutely about, sighing once or twice and glancing furtively and +wistfully toward Tom. Then she observed that now Tom was talking more +particularly to Amy Lawrence than to any one else. She felt a sharp +pang and grew disturbed and uneasy at once. She tried to go away, but +her feet were treacherous, and carried her to the group instead. She +said to a girl almost at Tom's elbow--with sham vivacity: + +"Why, Mary Austin! you bad girl, why didn't you come to Sunday-school?" + +"I did come--didn't you see me?" + +"Why, no! Did you? Where did you sit?" + +"I was in Miss Peters' class, where I always go. I saw YOU." + +"Did you? Why, it's funny I didn't see you. I wanted to tell you about +the picnic." + +"Oh, that's jolly. Who's going to give it?" + +"My ma's going to let me have one." + +"Oh, goody; I hope she'll let ME come." + +"Well, she will. The picnic's for me. She'll let anybody come that I +want, and I want you." + +"That's ever so nice. When is it going to be?" + +"By and by. Maybe about vacation." + +"Oh, won't it be fun! You going to have all the girls and boys?" + +"Yes, every one that's friends to me--or wants to be"; and she glanced +ever so furtively at Tom, but he talked right along to Amy Lawrence +about the terrible storm on the island, and how the lightning tore the +great sycamore tree "all to flinders" while he was "standing within +three feet of it." + +"Oh, may I come?" said Grace Miller. + +"Yes." + +"And me?" said Sally Rogers. + +"Yes." + +"And me, too?" said Susy Harper. "And Joe?" + +"Yes." + +And so on, with clapping of joyful hands till all the group had begged +for invitations but Tom and Amy. Then Tom turned coolly away, still +talking, and took Amy with him. Becky's lips trembled and the tears +came to her eyes; she hid these signs with a forced gayety and went on +chattering, but the life had gone out of the picnic, now, and out of +everything else; she got away as soon as she could and hid herself and +had what her sex call "a good cry." Then she sat moody, with wounded +pride, till the bell rang. She roused up, now, with a vindictive cast +in her eye, and gave her plaited tails a shake and said she knew what +SHE'D do. + +At recess Tom continued his flirtation with Amy with jubilant +self-satisfaction. And he kept drifting about to find Becky and lacerate +her with the performance. At last he spied her, but there was a sudden +falling of his mercury. She was sitting cosily on a little bench behind +the schoolhouse looking at a picture-book with Alfred Temple--and so +absorbed were they, and their heads so close together over the book, +that they did not seem to be conscious of anything in the world besides. +Jealousy ran red-hot through Tom's veins. He began to hate himself for +throwing away the chance Becky had offered for a reconciliation. He +called himself a fool, and all the hard names he could think of. He +wanted to cry with vexation. Amy chatted happily along, as they walked, +for her heart was singing, but Tom's tongue had lost its function. He +did not hear what Amy was saying, and whenever she paused expectantly he +could only stammer an awkward assent, which was as often misplaced as +otherwise. He kept drifting to the rear of the schoolhouse, again and +again, to sear his eyeballs with the hateful spectacle there. He could +not help it. And it maddened him to see, as he thought he saw, that +Becky Thatcher never once suspected that he was even in the land of the +living. But she did see, nevertheless; and she knew she was winning her +fight, too, and was glad to see him suffer as she had suffered. + +Amy's happy prattle became intolerable. Tom hinted at things he had to +attend to; things that must be done; and time was fleeting. But in +vain--the girl chirped on. Tom thought, "Oh, hang her, ain't I ever +going to get rid of her?" At last he must be attending to those +things--and she said artlessly that she would be "around" when school +let out. And he hastened away, hating her for it. + +"Any other boy!" Tom thought, grating his teeth. "Any boy in the whole +town but that Saint Louis smarty that thinks he dresses so fine and is +aristocracy! Oh, all right, I licked you the first day you ever saw +this town, mister, and I'll lick you again! You just wait till I catch +you out! I'll just take and--" + +And he went through the motions of thrashing an imaginary boy +--pummelling the air, and kicking and gouging. "Oh, you do, do you? You +holler 'nough, do you? Now, then, let that learn you!" And so the +imaginary flogging was finished to his satisfaction. + +Tom fled home at noon. His conscience could not endure any more of +Amy's grateful happiness, and his jealousy could bear no more of the +other distress. Becky resumed her picture inspections with Alfred, but +as the minutes dragged along and no Tom came to suffer, her triumph +began to cloud and she lost interest; gravity and absent-mindedness +followed, and then melancholy; two or three times she pricked up her +ear at a footstep, but it was a false hope; no Tom came. At last she +grew entirely miserable and wished she hadn't carried it so far. When +poor Alfred, seeing that he was losing her, he did not know how, kept +exclaiming: "Oh, here's a jolly one! look at this!" she lost patience +at last, and said, "Oh, don't bother me! I don't care for them!" and +burst into tears, and got up and walked away. + +Alfred dropped alongside and was going to try to comfort her, but she +said: + +"Go away and leave me alone, can't you! I hate you!" + +So the boy halted, wondering what he could have done--for she had said +she would look at pictures all through the nooning--and she walked on, +crying. Then Alfred went musing into the deserted schoolhouse. He was +humiliated and angry. He easily guessed his way to the truth--the girl +had simply made a convenience of him to vent her spite upon Tom Sawyer. +He was far from hating Tom the less when this thought occurred to him. +He wished there was some way to get that boy into trouble without much +risk to himself. Tom's spelling-book fell under his eye. Here was his +opportunity. He gratefully opened to the lesson for the afternoon and +poured ink upon the page. + +Becky, glancing in at a window behind him at the moment, saw the act, +and moved on, without discovering herself. She started homeward, now, +intending to find Tom and tell him; Tom would be thankful and their +troubles would be healed. Before she was half way home, however, she +had changed her mind. The thought of Tom's treatment of her when she +was talking about her picnic came scorching back and filled her with +shame. She resolved to let him get whipped on the damaged +spelling-book's account, and to hate him forever, into the bargain. + + + +CHAPTER XIX + +TOM arrived at home in a dreary mood, and the first thing his aunt +said to him showed him that he had brought his sorrows to an +unpromising market: + +"Tom, I've a notion to skin you alive!" + +"Auntie, what have I done?" + +"Well, you've done enough. Here I go over to Sereny Harper, like an +old softy, expecting I'm going to make her believe all that rubbage +about that dream, when lo and behold you she'd found out from Joe that +you was over here and heard all the talk we had that night. Tom, I +don't know what is to become of a boy that will act like that. It makes +me feel so bad to think you could let me go to Sereny Harper and make +such a fool of myself and never say a word." + +This was a new aspect of the thing. His smartness of the morning had +seemed to Tom a good joke before, and very ingenious. It merely looked +mean and shabby now. He hung his head and could not think of anything +to say for a moment. Then he said: + +"Auntie, I wish I hadn't done it--but I didn't think." + +"Oh, child, you never think. You never think of anything but your own +selfishness. You could think to come all the way over here from +Jackson's Island in the night to laugh at our troubles, and you could +think to fool me with a lie about a dream; but you couldn't ever think +to pity us and save us from sorrow." + +"Auntie, I know now it was mean, but I didn't mean to be mean. I +didn't, honest. And besides, I didn't come over here to laugh at you +that night." + +"What did you come for, then?" + +"It was to tell you not to be uneasy about us, because we hadn't got +drownded." + +"Tom, Tom, I would be the thankfullest soul in this world if I could +believe you ever had as good a thought as that, but you know you never +did--and I know it, Tom." + +"Indeed and 'deed I did, auntie--I wish I may never stir if I didn't." + +"Oh, Tom, don't lie--don't do it. It only makes things a hundred times +worse." + +"It ain't a lie, auntie; it's the truth. I wanted to keep you from +grieving--that was all that made me come." + +"I'd give the whole world to believe that--it would cover up a power +of sins, Tom. I'd 'most be glad you'd run off and acted so bad. But it +ain't reasonable; because, why didn't you tell me, child?" + +"Why, you see, when you got to talking about the funeral, I just got +all full of the idea of our coming and hiding in the church, and I +couldn't somehow bear to spoil it. So I just put the bark back in my +pocket and kept mum." + +"What bark?" + +"The bark I had wrote on to tell you we'd gone pirating. I wish, now, +you'd waked up when I kissed you--I do, honest." + +The hard lines in his aunt's face relaxed and a sudden tenderness +dawned in her eyes. + +"DID you kiss me, Tom?" + +"Why, yes, I did." + +"Are you sure you did, Tom?" + +"Why, yes, I did, auntie--certain sure." + +"What did you kiss me for, Tom?" + +"Because I loved you so, and you laid there moaning and I was so sorry." + +The words sounded like truth. The old lady could not hide a tremor in +her voice when she said: + +"Kiss me again, Tom!--and be off with you to school, now, and don't +bother me any more." + +The moment he was gone, she ran to a closet and got out the ruin of a +jacket which Tom had gone pirating in. Then she stopped, with it in her +hand, and said to herself: + +"No, I don't dare. Poor boy, I reckon he's lied about it--but it's a +blessed, blessed lie, there's such a comfort come from it. I hope the +Lord--I KNOW the Lord will forgive him, because it was such +goodheartedness in him to tell it. But I don't want to find out it's a +lie. I won't look." + +She put the jacket away, and stood by musing a minute. Twice she put +out her hand to take the garment again, and twice she refrained. Once +more she ventured, and this time she fortified herself with the +thought: "It's a good lie--it's a good lie--I won't let it grieve me." +So she sought the jacket pocket. A moment later she was reading Tom's +piece of bark through flowing tears and saying: "I could forgive the +boy, now, if he'd committed a million sins!" + + + +CHAPTER XX + +THERE was something about Aunt Polly's manner, when she kissed Tom, +that swept away his low spirits and made him lighthearted and happy +again. He started to school and had the luck of coming upon Becky +Thatcher at the head of Meadow Lane. His mood always determined his +manner. Without a moment's hesitation he ran to her and said: + +"I acted mighty mean to-day, Becky, and I'm so sorry. I won't ever, +ever do that way again, as long as ever I live--please make up, won't +you?" + +The girl stopped and looked him scornfully in the face: + +"I'll thank you to keep yourself TO yourself, Mr. Thomas Sawyer. I'll +never speak to you again." + +She tossed her head and passed on. Tom was so stunned that he had not +even presence of mind enough to say "Who cares, Miss Smarty?" until the +right time to say it had gone by. So he said nothing. But he was in a +fine rage, nevertheless. He moped into the schoolyard wishing she were +a boy, and imagining how he would trounce her if she were. He presently +encountered her and delivered a stinging remark as he passed. She +hurled one in return, and the angry breach was complete. It seemed to +Becky, in her hot resentment, that she could hardly wait for school to +"take in," she was so impatient to see Tom flogged for the injured +spelling-book. If she had had any lingering notion of exposing Alfred +Temple, Tom's offensive fling had driven it entirely away. + +Poor girl, she did not know how fast she was nearing trouble herself. +The master, Mr. Dobbins, had reached middle age with an unsatisfied +ambition. The darling of his desires was, to be a doctor, but poverty +had decreed that he should be nothing higher than a village +schoolmaster. Every day he took a mysterious book out of his desk and +absorbed himself in it at times when no classes were reciting. He kept +that book under lock and key. There was not an urchin in school but was +perishing to have a glimpse of it, but the chance never came. Every boy +and girl had a theory about the nature of that book; but no two +theories were alike, and there was no way of getting at the facts in +the case. Now, as Becky was passing by the desk, which stood near the +door, she noticed that the key was in the lock! It was a precious +moment. She glanced around; found herself alone, and the next instant +she had the book in her hands. The title-page--Professor Somebody's +ANATOMY--carried no information to her mind; so she began to turn the +leaves. She came at once upon a handsomely engraved and colored +frontispiece--a human figure, stark naked. At that moment a shadow fell +on the page and Tom Sawyer stepped in at the door and caught a glimpse +of the picture. Becky snatched at the book to close it, and had the +hard luck to tear the pictured page half down the middle. She thrust +the volume into the desk, turned the key, and burst out crying with +shame and vexation. + +"Tom Sawyer, you are just as mean as you can be, to sneak up on a +person and look at what they're looking at." + +"How could I know you was looking at anything?" + +"You ought to be ashamed of yourself, Tom Sawyer; you know you're +going to tell on me, and oh, what shall I do, what shall I do! I'll be +whipped, and I never was whipped in school." + +Then she stamped her little foot and said: + +"BE so mean if you want to! I know something that's going to happen. +You just wait and you'll see! Hateful, hateful, hateful!"--and she +flung out of the house with a new explosion of crying. + +Tom stood still, rather flustered by this onslaught. Presently he said +to himself: + +"What a curious kind of a fool a girl is! Never been licked in school! +Shucks! What's a licking! That's just like a girl--they're so +thin-skinned and chicken-hearted. Well, of course I ain't going to tell +old Dobbins on this little fool, because there's other ways of getting +even on her, that ain't so mean; but what of it? Old Dobbins will ask +who it was tore his book. Nobody'll answer. Then he'll do just the way +he always does--ask first one and then t'other, and when he comes to the +right girl he'll know it, without any telling. Girls' faces always tell +on them. They ain't got any backbone. She'll get licked. Well, it's a +kind of a tight place for Becky Thatcher, because there ain't any way +out of it." Tom conned the thing a moment longer, and then added: "All +right, though; she'd like to see me in just such a fix--let her sweat it +out!" + +Tom joined the mob of skylarking scholars outside. In a few moments +the master arrived and school "took in." Tom did not feel a strong +interest in his studies. Every time he stole a glance at the girls' +side of the room Becky's face troubled him. Considering all things, he +did not want to pity her, and yet it was all he could do to help it. He +could get up no exultation that was really worthy the name. Presently +the spelling-book discovery was made, and Tom's mind was entirely full +of his own matters for a while after that. Becky roused up from her +lethargy of distress and showed good interest in the proceedings. She +did not expect that Tom could get out of his trouble by denying that he +spilt the ink on the book himself; and she was right. The denial only +seemed to make the thing worse for Tom. Becky supposed she would be +glad of that, and she tried to believe she was glad of it, but she +found she was not certain. When the worst came to the worst, she had an +impulse to get up and tell on Alfred Temple, but she made an effort and +forced herself to keep still--because, said she to herself, "he'll tell +about me tearing the picture sure. I wouldn't say a word, not to save +his life!" + +Tom took his whipping and went back to his seat not at all +broken-hearted, for he thought it was possible that he had unknowingly +upset the ink on the spelling-book himself, in some skylarking bout--he +had denied it for form's sake and because it was custom, and had stuck +to the denial from principle. + +A whole hour drifted by, the master sat nodding in his throne, the air +was drowsy with the hum of study. By and by, Mr. Dobbins straightened +himself up, yawned, then unlocked his desk, and reached for his book, +but seemed undecided whether to take it out or leave it. Most of the +pupils glanced up languidly, but there were two among them that watched +his movements with intent eyes. Mr. Dobbins fingered his book absently +for a while, then took it out and settled himself in his chair to read! +Tom shot a glance at Becky. He had seen a hunted and helpless rabbit +look as she did, with a gun levelled at its head. Instantly he forgot +his quarrel with her. Quick--something must be done! done in a flash, +too! But the very imminence of the emergency paralyzed his invention. +Good!--he had an inspiration! He would run and snatch the book, spring +through the door and fly. But his resolution shook for one little +instant, and the chance was lost--the master opened the volume. If Tom +only had the wasted opportunity back again! Too late. There was no help +for Becky now, he said. The next moment the master faced the school. +Every eye sank under his gaze. There was that in it which smote even +the innocent with fear. There was silence while one might count ten +--the master was gathering his wrath. Then he spoke: "Who tore this book?" + +There was not a sound. One could have heard a pin drop. The stillness +continued; the master searched face after face for signs of guilt. + +"Benjamin Rogers, did you tear this book?" + +A denial. Another pause. + +"Joseph Harper, did you?" + +Another denial. Tom's uneasiness grew more and more intense under the +slow torture of these proceedings. The master scanned the ranks of +boys--considered a while, then turned to the girls: + +"Amy Lawrence?" + +A shake of the head. + +"Gracie Miller?" + +The same sign. + +"Susan Harper, did you do this?" + +Another negative. The next girl was Becky Thatcher. Tom was trembling +from head to foot with excitement and a sense of the hopelessness of +the situation. + +"Rebecca Thatcher" [Tom glanced at her face--it was white with terror] +--"did you tear--no, look me in the face" [her hands rose in appeal] +--"did you tear this book?" + +A thought shot like lightning through Tom's brain. He sprang to his +feet and shouted--"I done it!" + +The school stared in perplexity at this incredible folly. Tom stood a +moment, to gather his dismembered faculties; and when he stepped +forward to go to his punishment the surprise, the gratitude, the +adoration that shone upon him out of poor Becky's eyes seemed pay +enough for a hundred floggings. Inspired by the splendor of his own +act, he took without an outcry the most merciless flaying that even Mr. +Dobbins had ever administered; and also received with indifference the +added cruelty of a command to remain two hours after school should be +dismissed--for he knew who would wait for him outside till his +captivity was done, and not count the tedious time as loss, either. + +Tom went to bed that night planning vengeance against Alfred Temple; +for with shame and repentance Becky had told him all, not forgetting +her own treachery; but even the longing for vengeance had to give way, +soon, to pleasanter musings, and he fell asleep at last with Becky's +latest words lingering dreamily in his ear-- + +"Tom, how COULD you be so noble!" + + + +CHAPTER XXI + +VACATION was approaching. The schoolmaster, always severe, grew +severer and more exacting than ever, for he wanted the school to make a +good showing on "Examination" day. His rod and his ferule were seldom +idle now--at least among the smaller pupils. Only the biggest boys, and +young ladies of eighteen and twenty, escaped lashing. Mr. Dobbins' +lashings were very vigorous ones, too; for although he carried, under +his wig, a perfectly bald and shiny head, he had only reached middle +age, and there was no sign of feebleness in his muscle. As the great +day approached, all the tyranny that was in him came to the surface; he +seemed to take a vindictive pleasure in punishing the least +shortcomings. The consequence was, that the smaller boys spent their +days in terror and suffering and their nights in plotting revenge. They +threw away no opportunity to do the master a mischief. But he kept +ahead all the time. The retribution that followed every vengeful +success was so sweeping and majestic that the boys always retired from +the field badly worsted. At last they conspired together and hit upon a +plan that promised a dazzling victory. They swore in the sign-painter's +boy, told him the scheme, and asked his help. He had his own reasons +for being delighted, for the master boarded in his father's family and +had given the boy ample cause to hate him. The master's wife would go +on a visit to the country in a few days, and there would be nothing to +interfere with the plan; the master always prepared himself for great +occasions by getting pretty well fuddled, and the sign-painter's boy +said that when the dominie had reached the proper condition on +Examination Evening he would "manage the thing" while he napped in his +chair; then he would have him awakened at the right time and hurried +away to school. + +In the fulness of time the interesting occasion arrived. At eight in +the evening the schoolhouse was brilliantly lighted, and adorned with +wreaths and festoons of foliage and flowers. The master sat throned in +his great chair upon a raised platform, with his blackboard behind him. +He was looking tolerably mellow. Three rows of benches on each side and +six rows in front of him were occupied by the dignitaries of the town +and by the parents of the pupils. To his left, back of the rows of +citizens, was a spacious temporary platform upon which were seated the +scholars who were to take part in the exercises of the evening; rows of +small boys, washed and dressed to an intolerable state of discomfort; +rows of gawky big boys; snowbanks of girls and young ladies clad in +lawn and muslin and conspicuously conscious of their bare arms, their +grandmothers' ancient trinkets, their bits of pink and blue ribbon and +the flowers in their hair. All the rest of the house was filled with +non-participating scholars. + +The exercises began. A very little boy stood up and sheepishly +recited, "You'd scarce expect one of my age to speak in public on the +stage," etc.--accompanying himself with the painfully exact and +spasmodic gestures which a machine might have used--supposing the +machine to be a trifle out of order. But he got through safely, though +cruelly scared, and got a fine round of applause when he made his +manufactured bow and retired. + +A little shamefaced girl lisped, "Mary had a little lamb," etc., +performed a compassion-inspiring curtsy, got her meed of applause, and +sat down flushed and happy. + +Tom Sawyer stepped forward with conceited confidence and soared into +the unquenchable and indestructible "Give me liberty or give me death" +speech, with fine fury and frantic gesticulation, and broke down in the +middle of it. A ghastly stage-fright seized him, his legs quaked under +him and he was like to choke. True, he had the manifest sympathy of the +house but he had the house's silence, too, which was even worse than +its sympathy. The master frowned, and this completed the disaster. Tom +struggled awhile and then retired, utterly defeated. There was a weak +attempt at applause, but it died early. + +"The Boy Stood on the Burning Deck" followed; also "The Assyrian Came +Down," and other declamatory gems. Then there were reading exercises, +and a spelling fight. The meagre Latin class recited with honor. The +prime feature of the evening was in order, now--original "compositions" +by the young ladies. Each in her turn stepped forward to the edge of +the platform, cleared her throat, held up her manuscript (tied with +dainty ribbon), and proceeded to read, with labored attention to +"expression" and punctuation. The themes were the same that had been +illuminated upon similar occasions by their mothers before them, their +grandmothers, and doubtless all their ancestors in the female line +clear back to the Crusades. "Friendship" was one; "Memories of Other +Days"; "Religion in History"; "Dream Land"; "The Advantages of +Culture"; "Forms of Political Government Compared and Contrasted"; +"Melancholy"; "Filial Love"; "Heart Longings," etc., etc. + +A prevalent feature in these compositions was a nursed and petted +melancholy; another was a wasteful and opulent gush of "fine language"; +another was a tendency to lug in by the ears particularly prized words +and phrases until they were worn entirely out; and a peculiarity that +conspicuously marked and marred them was the inveterate and intolerable +sermon that wagged its crippled tail at the end of each and every one +of them. No matter what the subject might be, a brain-racking effort +was made to squirm it into some aspect or other that the moral and +religious mind could contemplate with edification. The glaring +insincerity of these sermons was not sufficient to compass the +banishment of the fashion from the schools, and it is not sufficient +to-day; it never will be sufficient while the world stands, perhaps. +There is no school in all our land where the young ladies do not feel +obliged to close their compositions with a sermon; and you will find +that the sermon of the most frivolous and the least religious girl in +the school is always the longest and the most relentlessly pious. But +enough of this. Homely truth is unpalatable. + +Let us return to the "Examination." The first composition that was +read was one entitled "Is this, then, Life?" Perhaps the reader can +endure an extract from it: + + "In the common walks of life, with what delightful + emotions does the youthful mind look forward to some + anticipated scene of festivity! Imagination is busy + sketching rose-tinted pictures of joy. In fancy, the + voluptuous votary of fashion sees herself amid the + festive throng, 'the observed of all observers.' Her + graceful form, arrayed in snowy robes, is whirling + through the mazes of the joyous dance; her eye is + brightest, her step is lightest in the gay assembly. + + "In such delicious fancies time quickly glides by, + and the welcome hour arrives for her entrance into + the Elysian world, of which she has had such bright + dreams. How fairy-like does everything appear to + her enchanted vision! Each new scene is more charming + than the last. But after a while she finds that + beneath this goodly exterior, all is vanity, the + flattery which once charmed her soul, now grates + harshly upon her ear; the ball-room has lost its + charms; and with wasted health and imbittered heart, + she turns away with the conviction that earthly + pleasures cannot satisfy the longings of the soul!" + +And so forth and so on. There was a buzz of gratification from time to +time during the reading, accompanied by whispered ejaculations of "How +sweet!" "How eloquent!" "So true!" etc., and after the thing had closed +with a peculiarly afflicting sermon the applause was enthusiastic. + +Then arose a slim, melancholy girl, whose face had the "interesting" +paleness that comes of pills and indigestion, and read a "poem." Two +stanzas of it will do: + + "A MISSOURI MAIDEN'S FAREWELL TO ALABAMA + + "Alabama, good-bye! I love thee well! + But yet for a while do I leave thee now! + Sad, yes, sad thoughts of thee my heart doth swell, + And burning recollections throng my brow! + For I have wandered through thy flowery woods; + Have roamed and read near Tallapoosa's stream; + Have listened to Tallassee's warring floods, + And wooed on Coosa's side Aurora's beam. + + "Yet shame I not to bear an o'er-full heart, + Nor blush to turn behind my tearful eyes; + 'Tis from no stranger land I now must part, + 'Tis to no strangers left I yield these sighs. + Welcome and home were mine within this State, + Whose vales I leave--whose spires fade fast from me + And cold must be mine eyes, and heart, and tete, + When, dear Alabama! they turn cold on thee!" + +There were very few there who knew what "tete" meant, but the poem was +very satisfactory, nevertheless. + +Next appeared a dark-complexioned, black-eyed, black-haired young +lady, who paused an impressive moment, assumed a tragic expression, and +began to read in a measured, solemn tone: + + "A VISION + + "Dark and tempestuous was night. Around the + throne on high not a single star quivered; but + the deep intonations of the heavy thunder + constantly vibrated upon the ear; whilst the + terrific lightning revelled in angry mood + through the cloudy chambers of heaven, seeming + to scorn the power exerted over its terror by + the illustrious Franklin! Even the boisterous + winds unanimously came forth from their mystic + homes, and blustered about as if to enhance by + their aid the wildness of the scene. + + "At such a time, so dark, so dreary, for human + sympathy my very spirit sighed; but instead thereof, + + "'My dearest friend, my counsellor, my comforter + and guide--My joy in grief, my second bliss + in joy,' came to my side. She moved like one of + those bright beings pictured in the sunny walks + of fancy's Eden by the romantic and young, a + queen of beauty unadorned save by her own + transcendent loveliness. So soft was her step, it + failed to make even a sound, and but for the + magical thrill imparted by her genial touch, as + other unobtrusive beauties, she would have glided + away un-perceived--unsought. A strange sadness + rested upon her features, like icy tears upon + the robe of December, as she pointed to the + contending elements without, and bade me contemplate + the two beings presented." + +This nightmare occupied some ten pages of manuscript and wound up with +a sermon so destructive of all hope to non-Presbyterians that it took +the first prize. This composition was considered to be the very finest +effort of the evening. The mayor of the village, in delivering the +prize to the author of it, made a warm speech in which he said that it +was by far the most "eloquent" thing he had ever listened to, and that +Daniel Webster himself might well be proud of it. + +It may be remarked, in passing, that the number of compositions in +which the word "beauteous" was over-fondled, and human experience +referred to as "life's page," was up to the usual average. + +Now the master, mellow almost to the verge of geniality, put his chair +aside, turned his back to the audience, and began to draw a map of +America on the blackboard, to exercise the geography class upon. But he +made a sad business of it with his unsteady hand, and a smothered +titter rippled over the house. He knew what the matter was, and set +himself to right it. He sponged out lines and remade them; but he only +distorted them more than ever, and the tittering was more pronounced. +He threw his entire attention upon his work, now, as if determined not +to be put down by the mirth. He felt that all eyes were fastened upon +him; he imagined he was succeeding, and yet the tittering continued; it +even manifestly increased. And well it might. There was a garret above, +pierced with a scuttle over his head; and down through this scuttle +came a cat, suspended around the haunches by a string; she had a rag +tied about her head and jaws to keep her from mewing; as she slowly +descended she curved upward and clawed at the string, she swung +downward and clawed at the intangible air. The tittering rose higher +and higher--the cat was within six inches of the absorbed teacher's +head--down, down, a little lower, and she grabbed his wig with her +desperate claws, clung to it, and was snatched up into the garret in an +instant with her trophy still in her possession! And how the light did +blaze abroad from the master's bald pate--for the sign-painter's boy +had GILDED it! + +That broke up the meeting. The boys were avenged. Vacation had come. + + NOTE:--The pretended "compositions" quoted in + this chapter are taken without alteration from a + volume entitled "Prose and Poetry, by a Western + Lady"--but they are exactly and precisely after + the schoolgirl pattern, and hence are much + happier than any mere imitations could be. + + + +CHAPTER XXII + +TOM joined the new order of Cadets of Temperance, being attracted by +the showy character of their "regalia." He promised to abstain from +smoking, chewing, and profanity as long as he remained a member. Now he +found out a new thing--namely, that to promise not to do a thing is the +surest way in the world to make a body want to go and do that very +thing. Tom soon found himself tormented with a desire to drink and +swear; the desire grew to be so intense that nothing but the hope of a +chance to display himself in his red sash kept him from withdrawing +from the order. Fourth of July was coming; but he soon gave that up +--gave it up before he had worn his shackles over forty-eight hours--and +fixed his hopes upon old Judge Frazer, justice of the peace, who was +apparently on his deathbed and would have a big public funeral, since +he was so high an official. During three days Tom was deeply concerned +about the Judge's condition and hungry for news of it. Sometimes his +hopes ran high--so high that he would venture to get out his regalia +and practise before the looking-glass. But the Judge had a most +discouraging way of fluctuating. At last he was pronounced upon the +mend--and then convalescent. Tom was disgusted; and felt a sense of +injury, too. He handed in his resignation at once--and that night the +Judge suffered a relapse and died. Tom resolved that he would never +trust a man like that again. + +The funeral was a fine thing. The Cadets paraded in a style calculated +to kill the late member with envy. Tom was a free boy again, however +--there was something in that. He could drink and swear, now--but found +to his surprise that he did not want to. The simple fact that he could, +took the desire away, and the charm of it. + +Tom presently wondered to find that his coveted vacation was beginning +to hang a little heavily on his hands. + +He attempted a diary--but nothing happened during three days, and so +he abandoned it. + +The first of all the negro minstrel shows came to town, and made a +sensation. Tom and Joe Harper got up a band of performers and were +happy for two days. + +Even the Glorious Fourth was in some sense a failure, for it rained +hard, there was no procession in consequence, and the greatest man in +the world (as Tom supposed), Mr. Benton, an actual United States +Senator, proved an overwhelming disappointment--for he was not +twenty-five feet high, nor even anywhere in the neighborhood of it. + +A circus came. The boys played circus for three days afterward in +tents made of rag carpeting--admission, three pins for boys, two for +girls--and then circusing was abandoned. + +A phrenologist and a mesmerizer came--and went again and left the +village duller and drearier than ever. + +There were some boys-and-girls' parties, but they were so few and so +delightful that they only made the aching voids between ache the harder. + +Becky Thatcher was gone to her Constantinople home to stay with her +parents during vacation--so there was no bright side to life anywhere. + +The dreadful secret of the murder was a chronic misery. It was a very +cancer for permanency and pain. + +Then came the measles. + +During two long weeks Tom lay a prisoner, dead to the world and its +happenings. He was very ill, he was interested in nothing. When he got +upon his feet at last and moved feebly down-town, a melancholy change +had come over everything and every creature. There had been a +"revival," and everybody had "got religion," not only the adults, but +even the boys and girls. Tom went about, hoping against hope for the +sight of one blessed sinful face, but disappointment crossed him +everywhere. He found Joe Harper studying a Testament, and turned sadly +away from the depressing spectacle. He sought Ben Rogers, and found him +visiting the poor with a basket of tracts. He hunted up Jim Hollis, who +called his attention to the precious blessing of his late measles as a +warning. Every boy he encountered added another ton to his depression; +and when, in desperation, he flew for refuge at last to the bosom of +Huckleberry Finn and was received with a Scriptural quotation, his +heart broke and he crept home and to bed realizing that he alone of all +the town was lost, forever and forever. + +And that night there came on a terrific storm, with driving rain, +awful claps of thunder and blinding sheets of lightning. He covered his +head with the bedclothes and waited in a horror of suspense for his +doom; for he had not the shadow of a doubt that all this hubbub was +about him. He believed he had taxed the forbearance of the powers above +to the extremity of endurance and that this was the result. It might +have seemed to him a waste of pomp and ammunition to kill a bug with a +battery of artillery, but there seemed nothing incongruous about the +getting up such an expensive thunderstorm as this to knock the turf +from under an insect like himself. + +By and by the tempest spent itself and died without accomplishing its +object. The boy's first impulse was to be grateful, and reform. His +second was to wait--for there might not be any more storms. + +The next day the doctors were back; Tom had relapsed. The three weeks +he spent on his back this time seemed an entire age. When he got abroad +at last he was hardly grateful that he had been spared, remembering how +lonely was his estate, how companionless and forlorn he was. He drifted +listlessly down the street and found Jim Hollis acting as judge in a +juvenile court that was trying a cat for murder, in the presence of her +victim, a bird. He found Joe Harper and Huck Finn up an alley eating a +stolen melon. Poor lads! they--like Tom--had suffered a relapse. + + + +CHAPTER XXIII + +AT last the sleepy atmosphere was stirred--and vigorously: the murder +trial came on in the court. It became the absorbing topic of village +talk immediately. Tom could not get away from it. Every reference to +the murder sent a shudder to his heart, for his troubled conscience and +fears almost persuaded him that these remarks were put forth in his +hearing as "feelers"; he did not see how he could be suspected of +knowing anything about the murder, but still he could not be +comfortable in the midst of this gossip. It kept him in a cold shiver +all the time. He took Huck to a lonely place to have a talk with him. +It would be some relief to unseal his tongue for a little while; to +divide his burden of distress with another sufferer. Moreover, he +wanted to assure himself that Huck had remained discreet. + +"Huck, have you ever told anybody about--that?" + +"'Bout what?" + +"You know what." + +"Oh--'course I haven't." + +"Never a word?" + +"Never a solitary word, so help me. What makes you ask?" + +"Well, I was afeard." + +"Why, Tom Sawyer, we wouldn't be alive two days if that got found out. +YOU know that." + +Tom felt more comfortable. After a pause: + +"Huck, they couldn't anybody get you to tell, could they?" + +"Get me to tell? Why, if I wanted that half-breed devil to drownd me +they could get me to tell. They ain't no different way." + +"Well, that's all right, then. I reckon we're safe as long as we keep +mum. But let's swear again, anyway. It's more surer." + +"I'm agreed." + +So they swore again with dread solemnities. + +"What is the talk around, Huck? I've heard a power of it." + +"Talk? Well, it's just Muff Potter, Muff Potter, Muff Potter all the +time. It keeps me in a sweat, constant, so's I want to hide som'ers." + +"That's just the same way they go on round me. I reckon he's a goner. +Don't you feel sorry for him, sometimes?" + +"Most always--most always. He ain't no account; but then he hain't +ever done anything to hurt anybody. Just fishes a little, to get money +to get drunk on--and loafs around considerable; but lord, we all do +that--leastways most of us--preachers and such like. But he's kind of +good--he give me half a fish, once, when there warn't enough for two; +and lots of times he's kind of stood by me when I was out of luck." + +"Well, he's mended kites for me, Huck, and knitted hooks on to my +line. I wish we could get him out of there." + +"My! we couldn't get him out, Tom. And besides, 'twouldn't do any +good; they'd ketch him again." + +"Yes--so they would. But I hate to hear 'em abuse him so like the +dickens when he never done--that." + +"I do too, Tom. Lord, I hear 'em say he's the bloodiest looking +villain in this country, and they wonder he wasn't ever hung before." + +"Yes, they talk like that, all the time. I've heard 'em say that if he +was to get free they'd lynch him." + +"And they'd do it, too." + +The boys had a long talk, but it brought them little comfort. As the +twilight drew on, they found themselves hanging about the neighborhood +of the little isolated jail, perhaps with an undefined hope that +something would happen that might clear away their difficulties. But +nothing happened; there seemed to be no angels or fairies interested in +this luckless captive. + +The boys did as they had often done before--went to the cell grating +and gave Potter some tobacco and matches. He was on the ground floor +and there were no guards. + +His gratitude for their gifts had always smote their consciences +before--it cut deeper than ever, this time. They felt cowardly and +treacherous to the last degree when Potter said: + +"You've been mighty good to me, boys--better'n anybody else in this +town. And I don't forget it, I don't. Often I says to myself, says I, +'I used to mend all the boys' kites and things, and show 'em where the +good fishin' places was, and befriend 'em what I could, and now they've +all forgot old Muff when he's in trouble; but Tom don't, and Huck +don't--THEY don't forget him, says I, 'and I don't forget them.' Well, +boys, I done an awful thing--drunk and crazy at the time--that's the +only way I account for it--and now I got to swing for it, and it's +right. Right, and BEST, too, I reckon--hope so, anyway. Well, we won't +talk about that. I don't want to make YOU feel bad; you've befriended +me. But what I want to say, is, don't YOU ever get drunk--then you won't +ever get here. Stand a litter furder west--so--that's it; it's a prime +comfort to see faces that's friendly when a body's in such a muck of +trouble, and there don't none come here but yourn. Good friendly +faces--good friendly faces. Git up on one another's backs and let me +touch 'em. That's it. Shake hands--yourn'll come through the bars, but +mine's too big. Little hands, and weak--but they've helped Muff Potter +a power, and they'd help him more if they could." + +Tom went home miserable, and his dreams that night were full of +horrors. The next day and the day after, he hung about the court-room, +drawn by an almost irresistible impulse to go in, but forcing himself +to stay out. Huck was having the same experience. They studiously +avoided each other. Each wandered away, from time to time, but the same +dismal fascination always brought them back presently. Tom kept his +ears open when idlers sauntered out of the court-room, but invariably +heard distressing news--the toils were closing more and more +relentlessly around poor Potter. At the end of the second day the +village talk was to the effect that Injun Joe's evidence stood firm and +unshaken, and that there was not the slightest question as to what the +jury's verdict would be. + +Tom was out late, that night, and came to bed through the window. He +was in a tremendous state of excitement. It was hours before he got to +sleep. All the village flocked to the court-house the next morning, for +this was to be the great day. Both sexes were about equally represented +in the packed audience. After a long wait the jury filed in and took +their places; shortly afterward, Potter, pale and haggard, timid and +hopeless, was brought in, with chains upon him, and seated where all +the curious eyes could stare at him; no less conspicuous was Injun Joe, +stolid as ever. There was another pause, and then the judge arrived and +the sheriff proclaimed the opening of the court. The usual whisperings +among the lawyers and gathering together of papers followed. These +details and accompanying delays worked up an atmosphere of preparation +that was as impressive as it was fascinating. + +Now a witness was called who testified that he found Muff Potter +washing in the brook, at an early hour of the morning that the murder +was discovered, and that he immediately sneaked away. After some +further questioning, counsel for the prosecution said: + +"Take the witness." + +The prisoner raised his eyes for a moment, but dropped them again when +his own counsel said: + +"I have no questions to ask him." + +The next witness proved the finding of the knife near the corpse. +Counsel for the prosecution said: + +"Take the witness." + +"I have no questions to ask him," Potter's lawyer replied. + +A third witness swore he had often seen the knife in Potter's +possession. + +"Take the witness." + +Counsel for Potter declined to question him. The faces of the audience +began to betray annoyance. Did this attorney mean to throw away his +client's life without an effort? + +Several witnesses deposed concerning Potter's guilty behavior when +brought to the scene of the murder. They were allowed to leave the +stand without being cross-questioned. + +Every detail of the damaging circumstances that occurred in the +graveyard upon that morning which all present remembered so well was +brought out by credible witnesses, but none of them were cross-examined +by Potter's lawyer. The perplexity and dissatisfaction of the house +expressed itself in murmurs and provoked a reproof from the bench. +Counsel for the prosecution now said: + +"By the oaths of citizens whose simple word is above suspicion, we +have fastened this awful crime, beyond all possibility of question, +upon the unhappy prisoner at the bar. We rest our case here." + +A groan escaped from poor Potter, and he put his face in his hands and +rocked his body softly to and fro, while a painful silence reigned in +the court-room. Many men were moved, and many women's compassion +testified itself in tears. Counsel for the defence rose and said: + +"Your honor, in our remarks at the opening of this trial, we +foreshadowed our purpose to prove that our client did this fearful deed +while under the influence of a blind and irresponsible delirium +produced by drink. We have changed our mind. We shall not offer that +plea." [Then to the clerk:] "Call Thomas Sawyer!" + +A puzzled amazement awoke in every face in the house, not even +excepting Potter's. Every eye fastened itself with wondering interest +upon Tom as he rose and took his place upon the stand. The boy looked +wild enough, for he was badly scared. The oath was administered. + +"Thomas Sawyer, where were you on the seventeenth of June, about the +hour of midnight?" + +Tom glanced at Injun Joe's iron face and his tongue failed him. The +audience listened breathless, but the words refused to come. After a +few moments, however, the boy got a little of his strength back, and +managed to put enough of it into his voice to make part of the house +hear: + +"In the graveyard!" + +"A little bit louder, please. Don't be afraid. You were--" + +"In the graveyard." + +A contemptuous smile flitted across Injun Joe's face. + +"Were you anywhere near Horse Williams' grave?" + +"Yes, sir." + +"Speak up--just a trifle louder. How near were you?" + +"Near as I am to you." + +"Were you hidden, or not?" + +"I was hid." + +"Where?" + +"Behind the elms that's on the edge of the grave." + +Injun Joe gave a barely perceptible start. + +"Any one with you?" + +"Yes, sir. I went there with--" + +"Wait--wait a moment. Never mind mentioning your companion's name. We +will produce him at the proper time. Did you carry anything there with +you." + +Tom hesitated and looked confused. + +"Speak out, my boy--don't be diffident. The truth is always +respectable. What did you take there?" + +"Only a--a--dead cat." + +There was a ripple of mirth, which the court checked. + +"We will produce the skeleton of that cat. Now, my boy, tell us +everything that occurred--tell it in your own way--don't skip anything, +and don't be afraid." + +Tom began--hesitatingly at first, but as he warmed to his subject his +words flowed more and more easily; in a little while every sound ceased +but his own voice; every eye fixed itself upon him; with parted lips +and bated breath the audience hung upon his words, taking no note of +time, rapt in the ghastly fascinations of the tale. The strain upon +pent emotion reached its climax when the boy said: + +"--and as the doctor fetched the board around and Muff Potter fell, +Injun Joe jumped with the knife and--" + +Crash! Quick as lightning the half-breed sprang for a window, tore his +way through all opposers, and was gone! + + + +CHAPTER XXIV + +TOM was a glittering hero once more--the pet of the old, the envy of +the young. His name even went into immortal print, for the village +paper magnified him. There were some that believed he would be +President, yet, if he escaped hanging. + +As usual, the fickle, unreasoning world took Muff Potter to its bosom +and fondled him as lavishly as it had abused him before. But that sort +of conduct is to the world's credit; therefore it is not well to find +fault with it. + +Tom's days were days of splendor and exultation to him, but his nights +were seasons of horror. Injun Joe infested all his dreams, and always +with doom in his eye. Hardly any temptation could persuade the boy to +stir abroad after nightfall. Poor Huck was in the same state of +wretchedness and terror, for Tom had told the whole story to the lawyer +the night before the great day of the trial, and Huck was sore afraid +that his share in the business might leak out, yet, notwithstanding +Injun Joe's flight had saved him the suffering of testifying in court. +The poor fellow had got the attorney to promise secrecy, but what of +that? Since Tom's harassed conscience had managed to drive him to the +lawyer's house by night and wring a dread tale from lips that had been +sealed with the dismalest and most formidable of oaths, Huck's +confidence in the human race was well-nigh obliterated. + +Daily Muff Potter's gratitude made Tom glad he had spoken; but nightly +he wished he had sealed up his tongue. + +Half the time Tom was afraid Injun Joe would never be captured; the +other half he was afraid he would be. He felt sure he never could draw +a safe breath again until that man was dead and he had seen the corpse. + +Rewards had been offered, the country had been scoured, but no Injun +Joe was found. One of those omniscient and awe-inspiring marvels, a +detective, came up from St. Louis, moused around, shook his head, +looked wise, and made that sort of astounding success which members of +that craft usually achieve. That is to say, he "found a clew." But you +can't hang a "clew" for murder, and so after that detective had got +through and gone home, Tom felt just as insecure as he was before. + +The slow days drifted on, and each left behind it a slightly lightened +weight of apprehension. + + + +CHAPTER XXV + +THERE comes a time in every rightly-constructed boy's life when he has +a raging desire to go somewhere and dig for hidden treasure. This +desire suddenly came upon Tom one day. He sallied out to find Joe +Harper, but failed of success. Next he sought Ben Rogers; he had gone +fishing. Presently he stumbled upon Huck Finn the Red-Handed. Huck +would answer. Tom took him to a private place and opened the matter to +him confidentially. Huck was willing. Huck was always willing to take a +hand in any enterprise that offered entertainment and required no +capital, for he had a troublesome superabundance of that sort of time +which is not money. "Where'll we dig?" said Huck. + +"Oh, most anywhere." + +"Why, is it hid all around?" + +"No, indeed it ain't. It's hid in mighty particular places, Huck +--sometimes on islands, sometimes in rotten chests under the end of a +limb of an old dead tree, just where the shadow falls at midnight; but +mostly under the floor in ha'nted houses." + +"Who hides it?" + +"Why, robbers, of course--who'd you reckon? Sunday-school +sup'rintendents?" + +"I don't know. If 'twas mine I wouldn't hide it; I'd spend it and have +a good time." + +"So would I. But robbers don't do that way. They always hide it and +leave it there." + +"Don't they come after it any more?" + +"No, they think they will, but they generally forget the marks, or +else they die. Anyway, it lays there a long time and gets rusty; and by +and by somebody finds an old yellow paper that tells how to find the +marks--a paper that's got to be ciphered over about a week because it's +mostly signs and hy'roglyphics." + +"Hyro--which?" + +"Hy'roglyphics--pictures and things, you know, that don't seem to mean +anything." + +"Have you got one of them papers, Tom?" + +"No." + +"Well then, how you going to find the marks?" + +"I don't want any marks. They always bury it under a ha'nted house or +on an island, or under a dead tree that's got one limb sticking out. +Well, we've tried Jackson's Island a little, and we can try it again +some time; and there's the old ha'nted house up the Still-House branch, +and there's lots of dead-limb trees--dead loads of 'em." + +"Is it under all of them?" + +"How you talk! No!" + +"Then how you going to know which one to go for?" + +"Go for all of 'em!" + +"Why, Tom, it'll take all summer." + +"Well, what of that? Suppose you find a brass pot with a hundred +dollars in it, all rusty and gray, or rotten chest full of di'monds. +How's that?" + +Huck's eyes glowed. + +"That's bully. Plenty bully enough for me. Just you gimme the hundred +dollars and I don't want no di'monds." + +"All right. But I bet you I ain't going to throw off on di'monds. Some +of 'em's worth twenty dollars apiece--there ain't any, hardly, but's +worth six bits or a dollar." + +"No! Is that so?" + +"Cert'nly--anybody'll tell you so. Hain't you ever seen one, Huck?" + +"Not as I remember." + +"Oh, kings have slathers of them." + +"Well, I don' know no kings, Tom." + +"I reckon you don't. But if you was to go to Europe you'd see a raft +of 'em hopping around." + +"Do they hop?" + +"Hop?--your granny! No!" + +"Well, what did you say they did, for?" + +"Shucks, I only meant you'd SEE 'em--not hopping, of course--what do +they want to hop for?--but I mean you'd just see 'em--scattered around, +you know, in a kind of a general way. Like that old humpbacked Richard." + +"Richard? What's his other name?" + +"He didn't have any other name. Kings don't have any but a given name." + +"No?" + +"But they don't." + +"Well, if they like it, Tom, all right; but I don't want to be a king +and have only just a given name, like a nigger. But say--where you +going to dig first?" + +"Well, I don't know. S'pose we tackle that old dead-limb tree on the +hill t'other side of Still-House branch?" + +"I'm agreed." + +So they got a crippled pick and a shovel, and set out on their +three-mile tramp. They arrived hot and panting, and threw themselves +down in the shade of a neighboring elm to rest and have a smoke. + +"I like this," said Tom. + +"So do I." + +"Say, Huck, if we find a treasure here, what you going to do with your +share?" + +"Well, I'll have pie and a glass of soda every day, and I'll go to +every circus that comes along. I bet I'll have a gay time." + +"Well, ain't you going to save any of it?" + +"Save it? What for?" + +"Why, so as to have something to live on, by and by." + +"Oh, that ain't any use. Pap would come back to thish-yer town some +day and get his claws on it if I didn't hurry up, and I tell you he'd +clean it out pretty quick. What you going to do with yourn, Tom?" + +"I'm going to buy a new drum, and a sure-'nough sword, and a red +necktie and a bull pup, and get married." + +"Married!" + +"That's it." + +"Tom, you--why, you ain't in your right mind." + +"Wait--you'll see." + +"Well, that's the foolishest thing you could do. Look at pap and my +mother. Fight! Why, they used to fight all the time. I remember, mighty +well." + +"That ain't anything. The girl I'm going to marry won't fight." + +"Tom, I reckon they're all alike. They'll all comb a body. Now you +better think 'bout this awhile. I tell you you better. What's the name +of the gal?" + +"It ain't a gal at all--it's a girl." + +"It's all the same, I reckon; some says gal, some says girl--both's +right, like enough. Anyway, what's her name, Tom?" + +"I'll tell you some time--not now." + +"All right--that'll do. Only if you get married I'll be more lonesomer +than ever." + +"No you won't. You'll come and live with me. Now stir out of this and +we'll go to digging." + +They worked and sweated for half an hour. No result. They toiled +another half-hour. Still no result. Huck said: + +"Do they always bury it as deep as this?" + +"Sometimes--not always. Not generally. I reckon we haven't got the +right place." + +So they chose a new spot and began again. The labor dragged a little, +but still they made progress. They pegged away in silence for some +time. Finally Huck leaned on his shovel, swabbed the beaded drops from +his brow with his sleeve, and said: + +"Where you going to dig next, after we get this one?" + +"I reckon maybe we'll tackle the old tree that's over yonder on +Cardiff Hill back of the widow's." + +"I reckon that'll be a good one. But won't the widow take it away from +us, Tom? It's on her land." + +"SHE take it away! Maybe she'd like to try it once. Whoever finds one +of these hid treasures, it belongs to him. It don't make any difference +whose land it's on." + +That was satisfactory. The work went on. By and by Huck said: + +"Blame it, we must be in the wrong place again. What do you think?" + +"It is mighty curious, Huck. I don't understand it. Sometimes witches +interfere. I reckon maybe that's what's the trouble now." + +"Shucks! Witches ain't got no power in the daytime." + +"Well, that's so. I didn't think of that. Oh, I know what the matter +is! What a blamed lot of fools we are! You got to find out where the +shadow of the limb falls at midnight, and that's where you dig!" + +"Then consound it, we've fooled away all this work for nothing. Now +hang it all, we got to come back in the night. It's an awful long way. +Can you get out?" + +"I bet I will. We've got to do it to-night, too, because if somebody +sees these holes they'll know in a minute what's here and they'll go +for it." + +"Well, I'll come around and maow to-night." + +"All right. Let's hide the tools in the bushes." + +The boys were there that night, about the appointed time. They sat in +the shadow waiting. It was a lonely place, and an hour made solemn by +old traditions. Spirits whispered in the rustling leaves, ghosts lurked +in the murky nooks, the deep baying of a hound floated up out of the +distance, an owl answered with his sepulchral note. The boys were +subdued by these solemnities, and talked little. By and by they judged +that twelve had come; they marked where the shadow fell, and began to +dig. Their hopes commenced to rise. Their interest grew stronger, and +their industry kept pace with it. The hole deepened and still deepened, +but every time their hearts jumped to hear the pick strike upon +something, they only suffered a new disappointment. It was only a stone +or a chunk. At last Tom said: + +"It ain't any use, Huck, we're wrong again." + +"Well, but we CAN'T be wrong. We spotted the shadder to a dot." + +"I know it, but then there's another thing." + +"What's that?". + +"Why, we only guessed at the time. Like enough it was too late or too +early." + +Huck dropped his shovel. + +"That's it," said he. "That's the very trouble. We got to give this +one up. We can't ever tell the right time, and besides this kind of +thing's too awful, here this time of night with witches and ghosts +a-fluttering around so. I feel as if something's behind me all the time; +and I'm afeard to turn around, becuz maybe there's others in front +a-waiting for a chance. I been creeping all over, ever since I got here." + +"Well, I've been pretty much so, too, Huck. They most always put in a +dead man when they bury a treasure under a tree, to look out for it." + +"Lordy!" + +"Yes, they do. I've always heard that." + +"Tom, I don't like to fool around much where there's dead people. A +body's bound to get into trouble with 'em, sure." + +"I don't like to stir 'em up, either. S'pose this one here was to +stick his skull out and say something!" + +"Don't Tom! It's awful." + +"Well, it just is. Huck, I don't feel comfortable a bit." + +"Say, Tom, let's give this place up, and try somewheres else." + +"All right, I reckon we better." + +"What'll it be?" + +Tom considered awhile; and then said: + +"The ha'nted house. That's it!" + +"Blame it, I don't like ha'nted houses, Tom. Why, they're a dern sight +worse'n dead people. Dead people might talk, maybe, but they don't come +sliding around in a shroud, when you ain't noticing, and peep over your +shoulder all of a sudden and grit their teeth, the way a ghost does. I +couldn't stand such a thing as that, Tom--nobody could." + +"Yes, but, Huck, ghosts don't travel around only at night. They won't +hender us from digging there in the daytime." + +"Well, that's so. But you know mighty well people don't go about that +ha'nted house in the day nor the night." + +"Well, that's mostly because they don't like to go where a man's been +murdered, anyway--but nothing's ever been seen around that house except +in the night--just some blue lights slipping by the windows--no regular +ghosts." + +"Well, where you see one of them blue lights flickering around, Tom, +you can bet there's a ghost mighty close behind it. It stands to +reason. Becuz you know that they don't anybody but ghosts use 'em." + +"Yes, that's so. But anyway they don't come around in the daytime, so +what's the use of our being afeard?" + +"Well, all right. We'll tackle the ha'nted house if you say so--but I +reckon it's taking chances." + +They had started down the hill by this time. There in the middle of +the moonlit valley below them stood the "ha'nted" house, utterly +isolated, its fences gone long ago, rank weeds smothering the very +doorsteps, the chimney crumbled to ruin, the window-sashes vacant, a +corner of the roof caved in. The boys gazed awhile, half expecting to +see a blue light flit past a window; then talking in a low tone, as +befitted the time and the circumstances, they struck far off to the +right, to give the haunted house a wide berth, and took their way +homeward through the woods that adorned the rearward side of Cardiff +Hill. + + + +CHAPTER XXVI + +ABOUT noon the next day the boys arrived at the dead tree; they had +come for their tools. Tom was impatient to go to the haunted house; +Huck was measurably so, also--but suddenly said: + +"Lookyhere, Tom, do you know what day it is?" + +Tom mentally ran over the days of the week, and then quickly lifted +his eyes with a startled look in them-- + +"My! I never once thought of it, Huck!" + +"Well, I didn't neither, but all at once it popped onto me that it was +Friday." + +"Blame it, a body can't be too careful, Huck. We might 'a' got into an +awful scrape, tackling such a thing on a Friday." + +"MIGHT! Better say we WOULD! There's some lucky days, maybe, but +Friday ain't." + +"Any fool knows that. I don't reckon YOU was the first that found it +out, Huck." + +"Well, I never said I was, did I? And Friday ain't all, neither. I had +a rotten bad dream last night--dreampt about rats." + +"No! Sure sign of trouble. Did they fight?" + +"No." + +"Well, that's good, Huck. When they don't fight it's only a sign that +there's trouble around, you know. All we got to do is to look mighty +sharp and keep out of it. We'll drop this thing for to-day, and play. +Do you know Robin Hood, Huck?" + +"No. Who's Robin Hood?" + +"Why, he was one of the greatest men that was ever in England--and the +best. He was a robber." + +"Cracky, I wisht I was. Who did he rob?" + +"Only sheriffs and bishops and rich people and kings, and such like. +But he never bothered the poor. He loved 'em. He always divided up with +'em perfectly square." + +"Well, he must 'a' been a brick." + +"I bet you he was, Huck. Oh, he was the noblest man that ever was. +They ain't any such men now, I can tell you. He could lick any man in +England, with one hand tied behind him; and he could take his yew bow +and plug a ten-cent piece every time, a mile and a half." + +"What's a YEW bow?" + +"I don't know. It's some kind of a bow, of course. And if he hit that +dime only on the edge he would set down and cry--and curse. But we'll +play Robin Hood--it's nobby fun. I'll learn you." + +"I'm agreed." + +So they played Robin Hood all the afternoon, now and then casting a +yearning eye down upon the haunted house and passing a remark about the +morrow's prospects and possibilities there. As the sun began to sink +into the west they took their way homeward athwart the long shadows of +the trees and soon were buried from sight in the forests of Cardiff +Hill. + +On Saturday, shortly after noon, the boys were at the dead tree again. +They had a smoke and a chat in the shade, and then dug a little in +their last hole, not with great hope, but merely because Tom said there +were so many cases where people had given up a treasure after getting +down within six inches of it, and then somebody else had come along and +turned it up with a single thrust of a shovel. The thing failed this +time, however, so the boys shouldered their tools and went away feeling +that they had not trifled with fortune, but had fulfilled all the +requirements that belong to the business of treasure-hunting. + +When they reached the haunted house there was something so weird and +grisly about the dead silence that reigned there under the baking sun, +and something so depressing about the loneliness and desolation of the +place, that they were afraid, for a moment, to venture in. Then they +crept to the door and took a trembling peep. They saw a weed-grown, +floorless room, unplastered, an ancient fireplace, vacant windows, a +ruinous staircase; and here, there, and everywhere hung ragged and +abandoned cobwebs. They presently entered, softly, with quickened +pulses, talking in whispers, ears alert to catch the slightest sound, +and muscles tense and ready for instant retreat. + +In a little while familiarity modified their fears and they gave the +place a critical and interested examination, rather admiring their own +boldness, and wondering at it, too. Next they wanted to look up-stairs. +This was something like cutting off retreat, but they got to daring +each other, and of course there could be but one result--they threw +their tools into a corner and made the ascent. Up there were the same +signs of decay. In one corner they found a closet that promised +mystery, but the promise was a fraud--there was nothing in it. Their +courage was up now and well in hand. They were about to go down and +begin work when-- + +"Sh!" said Tom. + +"What is it?" whispered Huck, blanching with fright. + +"Sh!... There!... Hear it?" + +"Yes!... Oh, my! Let's run!" + +"Keep still! Don't you budge! They're coming right toward the door." + +The boys stretched themselves upon the floor with their eyes to +knot-holes in the planking, and lay waiting, in a misery of fear. + +"They've stopped.... No--coming.... Here they are. Don't whisper +another word, Huck. My goodness, I wish I was out of this!" + +Two men entered. Each boy said to himself: "There's the old deaf and +dumb Spaniard that's been about town once or twice lately--never saw +t'other man before." + +"T'other" was a ragged, unkempt creature, with nothing very pleasant +in his face. The Spaniard was wrapped in a serape; he had bushy white +whiskers; long white hair flowed from under his sombrero, and he wore +green goggles. When they came in, "t'other" was talking in a low voice; +they sat down on the ground, facing the door, with their backs to the +wall, and the speaker continued his remarks. His manner became less +guarded and his words more distinct as he proceeded: + +"No," said he, "I've thought it all over, and I don't like it. It's +dangerous." + +"Dangerous!" grunted the "deaf and dumb" Spaniard--to the vast +surprise of the boys. "Milksop!" + +This voice made the boys gasp and quake. It was Injun Joe's! There was +silence for some time. Then Joe said: + +"What's any more dangerous than that job up yonder--but nothing's come +of it." + +"That's different. Away up the river so, and not another house about. +'Twon't ever be known that we tried, anyway, long as we didn't succeed." + +"Well, what's more dangerous than coming here in the daytime!--anybody +would suspicion us that saw us." + +"I know that. But there warn't any other place as handy after that +fool of a job. I want to quit this shanty. I wanted to yesterday, only +it warn't any use trying to stir out of here, with those infernal boys +playing over there on the hill right in full view." + +"Those infernal boys" quaked again under the inspiration of this +remark, and thought how lucky it was that they had remembered it was +Friday and concluded to wait a day. They wished in their hearts they +had waited a year. + +The two men got out some food and made a luncheon. After a long and +thoughtful silence, Injun Joe said: + +"Look here, lad--you go back up the river where you belong. Wait there +till you hear from me. I'll take the chances on dropping into this town +just once more, for a look. We'll do that 'dangerous' job after I've +spied around a little and think things look well for it. Then for +Texas! We'll leg it together!" + +This was satisfactory. Both men presently fell to yawning, and Injun +Joe said: + +"I'm dead for sleep! It's your turn to watch." + +He curled down in the weeds and soon began to snore. His comrade +stirred him once or twice and he became quiet. Presently the watcher +began to nod; his head drooped lower and lower, both men began to snore +now. + +The boys drew a long, grateful breath. Tom whispered: + +"Now's our chance--come!" + +Huck said: + +"I can't--I'd die if they was to wake." + +Tom urged--Huck held back. At last Tom rose slowly and softly, and +started alone. But the first step he made wrung such a hideous creak +from the crazy floor that he sank down almost dead with fright. He +never made a second attempt. The boys lay there counting the dragging +moments till it seemed to them that time must be done and eternity +growing gray; and then they were grateful to note that at last the sun +was setting. + +Now one snore ceased. Injun Joe sat up, stared around--smiled grimly +upon his comrade, whose head was drooping upon his knees--stirred him +up with his foot and said: + +"Here! YOU'RE a watchman, ain't you! All right, though--nothing's +happened." + +"My! have I been asleep?" + +"Oh, partly, partly. Nearly time for us to be moving, pard. What'll we +do with what little swag we've got left?" + +"I don't know--leave it here as we've always done, I reckon. No use to +take it away till we start south. Six hundred and fifty in silver's +something to carry." + +"Well--all right--it won't matter to come here once more." + +"No--but I'd say come in the night as we used to do--it's better." + +"Yes: but look here; it may be a good while before I get the right +chance at that job; accidents might happen; 'tain't in such a very good +place; we'll just regularly bury it--and bury it deep." + +"Good idea," said the comrade, who walked across the room, knelt down, +raised one of the rearward hearth-stones and took out a bag that +jingled pleasantly. He subtracted from it twenty or thirty dollars for +himself and as much for Injun Joe, and passed the bag to the latter, +who was on his knees in the corner, now, digging with his bowie-knife. + +The boys forgot all their fears, all their miseries in an instant. +With gloating eyes they watched every movement. Luck!--the splendor of +it was beyond all imagination! Six hundred dollars was money enough to +make half a dozen boys rich! Here was treasure-hunting under the +happiest auspices--there would not be any bothersome uncertainty as to +where to dig. They nudged each other every moment--eloquent nudges and +easily understood, for they simply meant--"Oh, but ain't you glad NOW +we're here!" + +Joe's knife struck upon something. + +"Hello!" said he. + +"What is it?" said his comrade. + +"Half-rotten plank--no, it's a box, I believe. Here--bear a hand and +we'll see what it's here for. Never mind, I've broke a hole." + +He reached his hand in and drew it out-- + +"Man, it's money!" + +The two men examined the handful of coins. They were gold. The boys +above were as excited as themselves, and as delighted. + +Joe's comrade said: + +"We'll make quick work of this. There's an old rusty pick over amongst +the weeds in the corner the other side of the fireplace--I saw it a +minute ago." + +He ran and brought the boys' pick and shovel. Injun Joe took the pick, +looked it over critically, shook his head, muttered something to +himself, and then began to use it. The box was soon unearthed. It was +not very large; it was iron bound and had been very strong before the +slow years had injured it. The men contemplated the treasure awhile in +blissful silence. + +"Pard, there's thousands of dollars here," said Injun Joe. + +"'Twas always said that Murrel's gang used to be around here one +summer," the stranger observed. + +"I know it," said Injun Joe; "and this looks like it, I should say." + +"Now you won't need to do that job." + +The half-breed frowned. Said he: + +"You don't know me. Least you don't know all about that thing. 'Tain't +robbery altogether--it's REVENGE!" and a wicked light flamed in his +eyes. "I'll need your help in it. When it's finished--then Texas. Go +home to your Nance and your kids, and stand by till you hear from me." + +"Well--if you say so; what'll we do with this--bury it again?" + +"Yes. [Ravishing delight overhead.] NO! by the great Sachem, no! +[Profound distress overhead.] I'd nearly forgot. That pick had fresh +earth on it! [The boys were sick with terror in a moment.] What +business has a pick and a shovel here? What business with fresh earth +on them? Who brought them here--and where are they gone? Have you heard +anybody?--seen anybody? What! bury it again and leave them to come and +see the ground disturbed? Not exactly--not exactly. We'll take it to my +den." + +"Why, of course! Might have thought of that before. You mean Number +One?" + +"No--Number Two--under the cross. The other place is bad--too common." + +"All right. It's nearly dark enough to start." + +Injun Joe got up and went about from window to window cautiously +peeping out. Presently he said: + +"Who could have brought those tools here? Do you reckon they can be +up-stairs?" + +The boys' breath forsook them. Injun Joe put his hand on his knife, +halted a moment, undecided, and then turned toward the stairway. The +boys thought of the closet, but their strength was gone. The steps came +creaking up the stairs--the intolerable distress of the situation woke +the stricken resolution of the lads--they were about to spring for the +closet, when there was a crash of rotten timbers and Injun Joe landed +on the ground amid the debris of the ruined stairway. He gathered +himself up cursing, and his comrade said: + +"Now what's the use of all that? If it's anybody, and they're up +there, let them STAY there--who cares? If they want to jump down, now, +and get into trouble, who objects? It will be dark in fifteen minutes +--and then let them follow us if they want to. I'm willing. In my +opinion, whoever hove those things in here caught a sight of us and +took us for ghosts or devils or something. I'll bet they're running +yet." + +Joe grumbled awhile; then he agreed with his friend that what daylight +was left ought to be economized in getting things ready for leaving. +Shortly afterward they slipped out of the house in the deepening +twilight, and moved toward the river with their precious box. + +Tom and Huck rose up, weak but vastly relieved, and stared after them +through the chinks between the logs of the house. Follow? Not they. +They were content to reach ground again without broken necks, and take +the townward track over the hill. They did not talk much. They were too +much absorbed in hating themselves--hating the ill luck that made them +take the spade and the pick there. But for that, Injun Joe never would +have suspected. He would have hidden the silver with the gold to wait +there till his "revenge" was satisfied, and then he would have had the +misfortune to find that money turn up missing. Bitter, bitter luck that +the tools were ever brought there! + +They resolved to keep a lookout for that Spaniard when he should come +to town spying out for chances to do his revengeful job, and follow him +to "Number Two," wherever that might be. Then a ghastly thought +occurred to Tom. + +"Revenge? What if he means US, Huck!" + +"Oh, don't!" said Huck, nearly fainting. + +They talked it all over, and as they entered town they agreed to +believe that he might possibly mean somebody else--at least that he +might at least mean nobody but Tom, since only Tom had testified. + +Very, very small comfort it was to Tom to be alone in danger! Company +would be a palpable improvement, he thought. + + + +CHAPTER XXVII + +THE adventure of the day mightily tormented Tom's dreams that night. +Four times he had his hands on that rich treasure and four times it +wasted to nothingness in his fingers as sleep forsook him and +wakefulness brought back the hard reality of his misfortune. As he lay +in the early morning recalling the incidents of his great adventure, he +noticed that they seemed curiously subdued and far away--somewhat as if +they had happened in another world, or in a time long gone by. Then it +occurred to him that the great adventure itself must be a dream! There +was one very strong argument in favor of this idea--namely, that the +quantity of coin he had seen was too vast to be real. He had never seen +as much as fifty dollars in one mass before, and he was like all boys +of his age and station in life, in that he imagined that all references +to "hundreds" and "thousands" were mere fanciful forms of speech, and +that no such sums really existed in the world. He never had supposed +for a moment that so large a sum as a hundred dollars was to be found +in actual money in any one's possession. If his notions of hidden +treasure had been analyzed, they would have been found to consist of a +handful of real dimes and a bushel of vague, splendid, ungraspable +dollars. + +But the incidents of his adventure grew sensibly sharper and clearer +under the attrition of thinking them over, and so he presently found +himself leaning to the impression that the thing might not have been a +dream, after all. This uncertainty must be swept away. He would snatch +a hurried breakfast and go and find Huck. Huck was sitting on the +gunwale of a flatboat, listlessly dangling his feet in the water and +looking very melancholy. Tom concluded to let Huck lead up to the +subject. If he did not do it, then the adventure would be proved to +have been only a dream. + +"Hello, Huck!" + +"Hello, yourself." + +Silence, for a minute. + +"Tom, if we'd 'a' left the blame tools at the dead tree, we'd 'a' got +the money. Oh, ain't it awful!" + +"'Tain't a dream, then, 'tain't a dream! Somehow I most wish it was. +Dog'd if I don't, Huck." + +"What ain't a dream?" + +"Oh, that thing yesterday. I been half thinking it was." + +"Dream! If them stairs hadn't broke down you'd 'a' seen how much dream +it was! I've had dreams enough all night--with that patch-eyed Spanish +devil going for me all through 'em--rot him!" + +"No, not rot him. FIND him! Track the money!" + +"Tom, we'll never find him. A feller don't have only one chance for +such a pile--and that one's lost. I'd feel mighty shaky if I was to see +him, anyway." + +"Well, so'd I; but I'd like to see him, anyway--and track him out--to +his Number Two." + +"Number Two--yes, that's it. I been thinking 'bout that. But I can't +make nothing out of it. What do you reckon it is?" + +"I dono. It's too deep. Say, Huck--maybe it's the number of a house!" + +"Goody!... No, Tom, that ain't it. If it is, it ain't in this +one-horse town. They ain't no numbers here." + +"Well, that's so. Lemme think a minute. Here--it's the number of a +room--in a tavern, you know!" + +"Oh, that's the trick! They ain't only two taverns. We can find out +quick." + +"You stay here, Huck, till I come." + +Tom was off at once. He did not care to have Huck's company in public +places. He was gone half an hour. He found that in the best tavern, No. +2 had long been occupied by a young lawyer, and was still so occupied. +In the less ostentatious house, No. 2 was a mystery. The +tavern-keeper's young son said it was kept locked all the time, and he +never saw anybody go into it or come out of it except at night; he did +not know any particular reason for this state of things; had had some +little curiosity, but it was rather feeble; had made the most of the +mystery by entertaining himself with the idea that that room was +"ha'nted"; had noticed that there was a light in there the night before. + +"That's what I've found out, Huck. I reckon that's the very No. 2 +we're after." + +"I reckon it is, Tom. Now what you going to do?" + +"Lemme think." + +Tom thought a long time. Then he said: + +"I'll tell you. The back door of that No. 2 is the door that comes out +into that little close alley between the tavern and the old rattle trap +of a brick store. Now you get hold of all the door-keys you can find, +and I'll nip all of auntie's, and the first dark night we'll go there +and try 'em. And mind you, keep a lookout for Injun Joe, because he +said he was going to drop into town and spy around once more for a +chance to get his revenge. If you see him, you just follow him; and if +he don't go to that No. 2, that ain't the place." + +"Lordy, I don't want to foller him by myself!" + +"Why, it'll be night, sure. He mightn't ever see you--and if he did, +maybe he'd never think anything." + +"Well, if it's pretty dark I reckon I'll track him. I dono--I dono. +I'll try." + +"You bet I'll follow him, if it's dark, Huck. Why, he might 'a' found +out he couldn't get his revenge, and be going right after that money." + +"It's so, Tom, it's so. I'll foller him; I will, by jingoes!" + +"Now you're TALKING! Don't you ever weaken, Huck, and I won't." + + + +CHAPTER XXVIII + +THAT night Tom and Huck were ready for their adventure. They hung +about the neighborhood of the tavern until after nine, one watching the +alley at a distance and the other the tavern door. Nobody entered the +alley or left it; nobody resembling the Spaniard entered or left the +tavern door. The night promised to be a fair one; so Tom went home with +the understanding that if a considerable degree of darkness came on, +Huck was to come and "maow," whereupon he would slip out and try the +keys. But the night remained clear, and Huck closed his watch and +retired to bed in an empty sugar hogshead about twelve. + +Tuesday the boys had the same ill luck. Also Wednesday. But Thursday +night promised better. Tom slipped out in good season with his aunt's +old tin lantern, and a large towel to blindfold it with. He hid the +lantern in Huck's sugar hogshead and the watch began. An hour before +midnight the tavern closed up and its lights (the only ones +thereabouts) were put out. No Spaniard had been seen. Nobody had +entered or left the alley. Everything was auspicious. The blackness of +darkness reigned, the perfect stillness was interrupted only by +occasional mutterings of distant thunder. + +Tom got his lantern, lit it in the hogshead, wrapped it closely in the +towel, and the two adventurers crept in the gloom toward the tavern. +Huck stood sentry and Tom felt his way into the alley. Then there was a +season of waiting anxiety that weighed upon Huck's spirits like a +mountain. He began to wish he could see a flash from the lantern--it +would frighten him, but it would at least tell him that Tom was alive +yet. It seemed hours since Tom had disappeared. Surely he must have +fainted; maybe he was dead; maybe his heart had burst under terror and +excitement. In his uneasiness Huck found himself drawing closer and +closer to the alley; fearing all sorts of dreadful things, and +momentarily expecting some catastrophe to happen that would take away +his breath. There was not much to take away, for he seemed only able to +inhale it by thimblefuls, and his heart would soon wear itself out, the +way it was beating. Suddenly there was a flash of light and Tom came +tearing by him: "Run!" said he; "run, for your life!" + +He needn't have repeated it; once was enough; Huck was making thirty +or forty miles an hour before the repetition was uttered. The boys +never stopped till they reached the shed of a deserted slaughter-house +at the lower end of the village. Just as they got within its shelter +the storm burst and the rain poured down. As soon as Tom got his breath +he said: + +"Huck, it was awful! I tried two of the keys, just as soft as I could; +but they seemed to make such a power of racket that I couldn't hardly +get my breath I was so scared. They wouldn't turn in the lock, either. +Well, without noticing what I was doing, I took hold of the knob, and +open comes the door! It warn't locked! I hopped in, and shook off the +towel, and, GREAT CAESAR'S GHOST!" + +"What!--what'd you see, Tom?" + +"Huck, I most stepped onto Injun Joe's hand!" + +"No!" + +"Yes! He was lying there, sound asleep on the floor, with his old +patch on his eye and his arms spread out." + +"Lordy, what did you do? Did he wake up?" + +"No, never budged. Drunk, I reckon. I just grabbed that towel and +started!" + +"I'd never 'a' thought of the towel, I bet!" + +"Well, I would. My aunt would make me mighty sick if I lost it." + +"Say, Tom, did you see that box?" + +"Huck, I didn't wait to look around. I didn't see the box, I didn't +see the cross. I didn't see anything but a bottle and a tin cup on the +floor by Injun Joe; yes, I saw two barrels and lots more bottles in the +room. Don't you see, now, what's the matter with that ha'nted room?" + +"How?" + +"Why, it's ha'nted with whiskey! Maybe ALL the Temperance Taverns have +got a ha'nted room, hey, Huck?" + +"Well, I reckon maybe that's so. Who'd 'a' thought such a thing? But +say, Tom, now's a mighty good time to get that box, if Injun Joe's +drunk." + +"It is, that! You try it!" + +Huck shuddered. + +"Well, no--I reckon not." + +"And I reckon not, Huck. Only one bottle alongside of Injun Joe ain't +enough. If there'd been three, he'd be drunk enough and I'd do it." + +There was a long pause for reflection, and then Tom said: + +"Lookyhere, Huck, less not try that thing any more till we know Injun +Joe's not in there. It's too scary. Now, if we watch every night, we'll +be dead sure to see him go out, some time or other, and then we'll +snatch that box quicker'n lightning." + +"Well, I'm agreed. I'll watch the whole night long, and I'll do it +every night, too, if you'll do the other part of the job." + +"All right, I will. All you got to do is to trot up Hooper Street a +block and maow--and if I'm asleep, you throw some gravel at the window +and that'll fetch me." + +"Agreed, and good as wheat!" + +"Now, Huck, the storm's over, and I'll go home. It'll begin to be +daylight in a couple of hours. You go back and watch that long, will +you?" + +"I said I would, Tom, and I will. I'll ha'nt that tavern every night +for a year! I'll sleep all day and I'll stand watch all night." + +"That's all right. Now, where you going to sleep?" + +"In Ben Rogers' hayloft. He lets me, and so does his pap's nigger man, +Uncle Jake. I tote water for Uncle Jake whenever he wants me to, and +any time I ask him he gives me a little something to eat if he can +spare it. That's a mighty good nigger, Tom. He likes me, becuz I don't +ever act as if I was above him. Sometime I've set right down and eat +WITH him. But you needn't tell that. A body's got to do things when +he's awful hungry he wouldn't want to do as a steady thing." + +"Well, if I don't want you in the daytime, I'll let you sleep. I won't +come bothering around. Any time you see something's up, in the night, +just skip right around and maow." + + + +CHAPTER XXIX + +THE first thing Tom heard on Friday morning was a glad piece of news +--Judge Thatcher's family had come back to town the night before. Both +Injun Joe and the treasure sunk into secondary importance for a moment, +and Becky took the chief place in the boy's interest. He saw her and +they had an exhausting good time playing "hi-spy" and "gully-keeper" +with a crowd of their school-mates. The day was completed and crowned +in a peculiarly satisfactory way: Becky teased her mother to appoint +the next day for the long-promised and long-delayed picnic, and she +consented. The child's delight was boundless; and Tom's not more +moderate. The invitations were sent out before sunset, and straightway +the young folks of the village were thrown into a fever of preparation +and pleasurable anticipation. Tom's excitement enabled him to keep +awake until a pretty late hour, and he had good hopes of hearing Huck's +"maow," and of having his treasure to astonish Becky and the picnickers +with, next day; but he was disappointed. No signal came that night. + +Morning came, eventually, and by ten or eleven o'clock a giddy and +rollicking company were gathered at Judge Thatcher's, and everything +was ready for a start. It was not the custom for elderly people to mar +the picnics with their presence. The children were considered safe +enough under the wings of a few young ladies of eighteen and a few +young gentlemen of twenty-three or thereabouts. The old steam ferryboat +was chartered for the occasion; presently the gay throng filed up the +main street laden with provision-baskets. Sid was sick and had to miss +the fun; Mary remained at home to entertain him. The last thing Mrs. +Thatcher said to Becky, was: + +"You'll not get back till late. Perhaps you'd better stay all night +with some of the girls that live near the ferry-landing, child." + +"Then I'll stay with Susy Harper, mamma." + +"Very well. And mind and behave yourself and don't be any trouble." + +Presently, as they tripped along, Tom said to Becky: + +"Say--I'll tell you what we'll do. 'Stead of going to Joe Harper's +we'll climb right up the hill and stop at the Widow Douglas'. She'll +have ice-cream! She has it most every day--dead loads of it. And she'll +be awful glad to have us." + +"Oh, that will be fun!" + +Then Becky reflected a moment and said: + +"But what will mamma say?" + +"How'll she ever know?" + +The girl turned the idea over in her mind, and said reluctantly: + +"I reckon it's wrong--but--" + +"But shucks! Your mother won't know, and so what's the harm? All she +wants is that you'll be safe; and I bet you she'd 'a' said go there if +she'd 'a' thought of it. I know she would!" + +The Widow Douglas' splendid hospitality was a tempting bait. It and +Tom's persuasions presently carried the day. So it was decided to say +nothing anybody about the night's programme. Presently it occurred to +Tom that maybe Huck might come this very night and give the signal. The +thought took a deal of the spirit out of his anticipations. Still he +could not bear to give up the fun at Widow Douglas'. And why should he +give it up, he reasoned--the signal did not come the night before, so +why should it be any more likely to come to-night? The sure fun of the +evening outweighed the uncertain treasure; and, boy-like, he determined +to yield to the stronger inclination and not allow himself to think of +the box of money another time that day. + +Three miles below town the ferryboat stopped at the mouth of a woody +hollow and tied up. The crowd swarmed ashore and soon the forest +distances and craggy heights echoed far and near with shoutings and +laughter. All the different ways of getting hot and tired were gone +through with, and by-and-by the rovers straggled back to camp fortified +with responsible appetites, and then the destruction of the good things +began. After the feast there was a refreshing season of rest and chat +in the shade of spreading oaks. By-and-by somebody shouted: + +"Who's ready for the cave?" + +Everybody was. Bundles of candles were procured, and straightway there +was a general scamper up the hill. The mouth of the cave was up the +hillside--an opening shaped like a letter A. Its massive oaken door +stood unbarred. Within was a small chamber, chilly as an ice-house, and +walled by Nature with solid limestone that was dewy with a cold sweat. +It was romantic and mysterious to stand here in the deep gloom and look +out upon the green valley shining in the sun. But the impressiveness of +the situation quickly wore off, and the romping began again. The moment +a candle was lighted there was a general rush upon the owner of it; a +struggle and a gallant defence followed, but the candle was soon +knocked down or blown out, and then there was a glad clamor of laughter +and a new chase. But all things have an end. By-and-by the procession +went filing down the steep descent of the main avenue, the flickering +rank of lights dimly revealing the lofty walls of rock almost to their +point of junction sixty feet overhead. This main avenue was not more +than eight or ten feet wide. Every few steps other lofty and still +narrower crevices branched from it on either hand--for McDougal's cave +was but a vast labyrinth of crooked aisles that ran into each other and +out again and led nowhere. It was said that one might wander days and +nights together through its intricate tangle of rifts and chasms, and +never find the end of the cave; and that he might go down, and down, +and still down, into the earth, and it was just the same--labyrinth +under labyrinth, and no end to any of them. No man "knew" the cave. +That was an impossible thing. Most of the young men knew a portion of +it, and it was not customary to venture much beyond this known portion. +Tom Sawyer knew as much of the cave as any one. + +The procession moved along the main avenue some three-quarters of a +mile, and then groups and couples began to slip aside into branch +avenues, fly along the dismal corridors, and take each other by +surprise at points where the corridors joined again. Parties were able +to elude each other for the space of half an hour without going beyond +the "known" ground. + +By-and-by, one group after another came straggling back to the mouth +of the cave, panting, hilarious, smeared from head to foot with tallow +drippings, daubed with clay, and entirely delighted with the success of +the day. Then they were astonished to find that they had been taking no +note of time and that night was about at hand. The clanging bell had +been calling for half an hour. However, this sort of close to the day's +adventures was romantic and therefore satisfactory. When the ferryboat +with her wild freight pushed into the stream, nobody cared sixpence for +the wasted time but the captain of the craft. + +Huck was already upon his watch when the ferryboat's lights went +glinting past the wharf. He heard no noise on board, for the young +people were as subdued and still as people usually are who are nearly +tired to death. He wondered what boat it was, and why she did not stop +at the wharf--and then he dropped her out of his mind and put his +attention upon his business. The night was growing cloudy and dark. Ten +o'clock came, and the noise of vehicles ceased, scattered lights began +to wink out, all straggling foot-passengers disappeared, the village +betook itself to its slumbers and left the small watcher alone with the +silence and the ghosts. Eleven o'clock came, and the tavern lights were +put out; darkness everywhere, now. Huck waited what seemed a weary long +time, but nothing happened. His faith was weakening. Was there any use? +Was there really any use? Why not give it up and turn in? + +A noise fell upon his ear. He was all attention in an instant. The +alley door closed softly. He sprang to the corner of the brick store. +The next moment two men brushed by him, and one seemed to have +something under his arm. It must be that box! So they were going to +remove the treasure. Why call Tom now? It would be absurd--the men +would get away with the box and never be found again. No, he would +stick to their wake and follow them; he would trust to the darkness for +security from discovery. So communing with himself, Huck stepped out +and glided along behind the men, cat-like, with bare feet, allowing +them to keep just far enough ahead not to be invisible. + +They moved up the river street three blocks, then turned to the left +up a cross-street. They went straight ahead, then, until they came to +the path that led up Cardiff Hill; this they took. They passed by the +old Welshman's house, half-way up the hill, without hesitating, and +still climbed upward. Good, thought Huck, they will bury it in the old +quarry. But they never stopped at the quarry. They passed on, up the +summit. They plunged into the narrow path between the tall sumach +bushes, and were at once hidden in the gloom. Huck closed up and +shortened his distance, now, for they would never be able to see him. +He trotted along awhile; then slackened his pace, fearing he was +gaining too fast; moved on a piece, then stopped altogether; listened; +no sound; none, save that he seemed to hear the beating of his own +heart. The hooting of an owl came over the hill--ominous sound! But no +footsteps. Heavens, was everything lost! He was about to spring with +winged feet, when a man cleared his throat not four feet from him! +Huck's heart shot into his throat, but he swallowed it again; and then +he stood there shaking as if a dozen agues had taken charge of him at +once, and so weak that he thought he must surely fall to the ground. He +knew where he was. He knew he was within five steps of the stile +leading into Widow Douglas' grounds. Very well, he thought, let them +bury it there; it won't be hard to find. + +Now there was a voice--a very low voice--Injun Joe's: + +"Damn her, maybe she's got company--there's lights, late as it is." + +"I can't see any." + +This was that stranger's voice--the stranger of the haunted house. A +deadly chill went to Huck's heart--this, then, was the "revenge" job! +His thought was, to fly. Then he remembered that the Widow Douglas had +been kind to him more than once, and maybe these men were going to +murder her. He wished he dared venture to warn her; but he knew he +didn't dare--they might come and catch him. He thought all this and +more in the moment that elapsed between the stranger's remark and Injun +Joe's next--which was-- + +"Because the bush is in your way. Now--this way--now you see, don't +you?" + +"Yes. Well, there IS company there, I reckon. Better give it up." + +"Give it up, and I just leaving this country forever! Give it up and +maybe never have another chance. I tell you again, as I've told you +before, I don't care for her swag--you may have it. But her husband was +rough on me--many times he was rough on me--and mainly he was the +justice of the peace that jugged me for a vagrant. And that ain't all. +It ain't a millionth part of it! He had me HORSEWHIPPED!--horsewhipped +in front of the jail, like a nigger!--with all the town looking on! +HORSEWHIPPED!--do you understand? He took advantage of me and died. But +I'll take it out of HER." + +"Oh, don't kill her! Don't do that!" + +"Kill? Who said anything about killing? I would kill HIM if he was +here; but not her. When you want to get revenge on a woman you don't +kill her--bosh! you go for her looks. You slit her nostrils--you notch +her ears like a sow!" + +"By God, that's--" + +"Keep your opinion to yourself! It will be safest for you. I'll tie +her to the bed. If she bleeds to death, is that my fault? I'll not cry, +if she does. My friend, you'll help me in this thing--for MY sake +--that's why you're here--I mightn't be able alone. If you flinch, I'll +kill you. Do you understand that? And if I have to kill you, I'll kill +her--and then I reckon nobody'll ever know much about who done this +business." + +"Well, if it's got to be done, let's get at it. The quicker the +better--I'm all in a shiver." + +"Do it NOW? And company there? Look here--I'll get suspicious of you, +first thing you know. No--we'll wait till the lights are out--there's +no hurry." + +Huck felt that a silence was going to ensue--a thing still more awful +than any amount of murderous talk; so he held his breath and stepped +gingerly back; planted his foot carefully and firmly, after balancing, +one-legged, in a precarious way and almost toppling over, first on one +side and then on the other. He took another step back, with the same +elaboration and the same risks; then another and another, and--a twig +snapped under his foot! His breath stopped and he listened. There was +no sound--the stillness was perfect. His gratitude was measureless. Now +he turned in his tracks, between the walls of sumach bushes--turned +himself as carefully as if he were a ship--and then stepped quickly but +cautiously along. When he emerged at the quarry he felt secure, and so +he picked up his nimble heels and flew. Down, down he sped, till he +reached the Welshman's. He banged at the door, and presently the heads +of the old man and his two stalwart sons were thrust from windows. + +"What's the row there? Who's banging? What do you want?" + +"Let me in--quick! I'll tell everything." + +"Why, who are you?" + +"Huckleberry Finn--quick, let me in!" + +"Huckleberry Finn, indeed! It ain't a name to open many doors, I +judge! But let him in, lads, and let's see what's the trouble." + +"Please don't ever tell I told you," were Huck's first words when he +got in. "Please don't--I'd be killed, sure--but the widow's been good +friends to me sometimes, and I want to tell--I WILL tell if you'll +promise you won't ever say it was me." + +"By George, he HAS got something to tell, or he wouldn't act so!" +exclaimed the old man; "out with it and nobody here'll ever tell, lad." + +Three minutes later the old man and his sons, well armed, were up the +hill, and just entering the sumach path on tiptoe, their weapons in +their hands. Huck accompanied them no further. He hid behind a great +bowlder and fell to listening. There was a lagging, anxious silence, +and then all of a sudden there was an explosion of firearms and a cry. + +Huck waited for no particulars. He sprang away and sped down the hill +as fast as his legs could carry him. + + + +CHAPTER XXX + +AS the earliest suspicion of dawn appeared on Sunday morning, Huck +came groping up the hill and rapped gently at the old Welshman's door. +The inmates were asleep, but it was a sleep that was set on a +hair-trigger, on account of the exciting episode of the night. A call +came from a window: + +"Who's there!" + +Huck's scared voice answered in a low tone: + +"Please let me in! It's only Huck Finn!" + +"It's a name that can open this door night or day, lad!--and welcome!" + +These were strange words to the vagabond boy's ears, and the +pleasantest he had ever heard. He could not recollect that the closing +word had ever been applied in his case before. The door was quickly +unlocked, and he entered. Huck was given a seat and the old man and his +brace of tall sons speedily dressed themselves. + +"Now, my boy, I hope you're good and hungry, because breakfast will be +ready as soon as the sun's up, and we'll have a piping hot one, too +--make yourself easy about that! I and the boys hoped you'd turn up and +stop here last night." + +"I was awful scared," said Huck, "and I run. I took out when the +pistols went off, and I didn't stop for three mile. I've come now becuz +I wanted to know about it, you know; and I come before daylight becuz I +didn't want to run across them devils, even if they was dead." + +"Well, poor chap, you do look as if you'd had a hard night of it--but +there's a bed here for you when you've had your breakfast. No, they +ain't dead, lad--we are sorry enough for that. You see we knew right +where to put our hands on them, by your description; so we crept along +on tiptoe till we got within fifteen feet of them--dark as a cellar +that sumach path was--and just then I found I was going to sneeze. It +was the meanest kind of luck! I tried to keep it back, but no use +--'twas bound to come, and it did come! I was in the lead with my pistol +raised, and when the sneeze started those scoundrels a-rustling to get +out of the path, I sung out, 'Fire boys!' and blazed away at the place +where the rustling was. So did the boys. But they were off in a jiffy, +those villains, and we after them, down through the woods. I judge we +never touched them. They fired a shot apiece as they started, but their +bullets whizzed by and didn't do us any harm. As soon as we lost the +sound of their feet we quit chasing, and went down and stirred up the +constables. They got a posse together, and went off to guard the river +bank, and as soon as it is light the sheriff and a gang are going to +beat up the woods. My boys will be with them presently. I wish we had +some sort of description of those rascals--'twould help a good deal. +But you couldn't see what they were like, in the dark, lad, I suppose?" + +"Oh yes; I saw them down-town and follered them." + +"Splendid! Describe them--describe them, my boy!" + +"One's the old deaf and dumb Spaniard that's ben around here once or +twice, and t'other's a mean-looking, ragged--" + +"That's enough, lad, we know the men! Happened on them in the woods +back of the widow's one day, and they slunk away. Off with you, boys, +and tell the sheriff--get your breakfast to-morrow morning!" + +The Welshman's sons departed at once. As they were leaving the room +Huck sprang up and exclaimed: + +"Oh, please don't tell ANYbody it was me that blowed on them! Oh, +please!" + +"All right if you say it, Huck, but you ought to have the credit of +what you did." + +"Oh no, no! Please don't tell!" + +When the young men were gone, the old Welshman said: + +"They won't tell--and I won't. But why don't you want it known?" + +Huck would not explain, further than to say that he already knew too +much about one of those men and would not have the man know that he +knew anything against him for the whole world--he would be killed for +knowing it, sure. + +The old man promised secrecy once more, and said: + +"How did you come to follow these fellows, lad? Were they looking +suspicious?" + +Huck was silent while he framed a duly cautious reply. Then he said: + +"Well, you see, I'm a kind of a hard lot,--least everybody says so, +and I don't see nothing agin it--and sometimes I can't sleep much, on +account of thinking about it and sort of trying to strike out a new way +of doing. That was the way of it last night. I couldn't sleep, and so I +come along up-street 'bout midnight, a-turning it all over, and when I +got to that old shackly brick store by the Temperance Tavern, I backed +up agin the wall to have another think. Well, just then along comes +these two chaps slipping along close by me, with something under their +arm, and I reckoned they'd stole it. One was a-smoking, and t'other one +wanted a light; so they stopped right before me and the cigars lit up +their faces and I see that the big one was the deaf and dumb Spaniard, +by his white whiskers and the patch on his eye, and t'other one was a +rusty, ragged-looking devil." + +"Could you see the rags by the light of the cigars?" + +This staggered Huck for a moment. Then he said: + +"Well, I don't know--but somehow it seems as if I did." + +"Then they went on, and you--" + +"Follered 'em--yes. That was it. I wanted to see what was up--they +sneaked along so. I dogged 'em to the widder's stile, and stood in the +dark and heard the ragged one beg for the widder, and the Spaniard +swear he'd spile her looks just as I told you and your two--" + +"What! The DEAF AND DUMB man said all that!" + +Huck had made another terrible mistake! He was trying his best to keep +the old man from getting the faintest hint of who the Spaniard might +be, and yet his tongue seemed determined to get him into trouble in +spite of all he could do. He made several efforts to creep out of his +scrape, but the old man's eye was upon him and he made blunder after +blunder. Presently the Welshman said: + +"My boy, don't be afraid of me. I wouldn't hurt a hair of your head +for all the world. No--I'd protect you--I'd protect you. This Spaniard +is not deaf and dumb; you've let that slip without intending it; you +can't cover that up now. You know something about that Spaniard that +you want to keep dark. Now trust me--tell me what it is, and trust me +--I won't betray you." + +Huck looked into the old man's honest eyes a moment, then bent over +and whispered in his ear: + +"'Tain't a Spaniard--it's Injun Joe!" + +The Welshman almost jumped out of his chair. In a moment he said: + +"It's all plain enough, now. When you talked about notching ears and +slitting noses I judged that that was your own embellishment, because +white men don't take that sort of revenge. But an Injun! That's a +different matter altogether." + +During breakfast the talk went on, and in the course of it the old man +said that the last thing which he and his sons had done, before going +to bed, was to get a lantern and examine the stile and its vicinity for +marks of blood. They found none, but captured a bulky bundle of-- + +"Of WHAT?" + +If the words had been lightning they could not have leaped with a more +stunning suddenness from Huck's blanched lips. His eyes were staring +wide, now, and his breath suspended--waiting for the answer. The +Welshman started--stared in return--three seconds--five seconds--ten +--then replied: + +"Of burglar's tools. Why, what's the MATTER with you?" + +Huck sank back, panting gently, but deeply, unutterably grateful. The +Welshman eyed him gravely, curiously--and presently said: + +"Yes, burglar's tools. That appears to relieve you a good deal. But +what did give you that turn? What were YOU expecting we'd found?" + +Huck was in a close place--the inquiring eye was upon him--he would +have given anything for material for a plausible answer--nothing +suggested itself--the inquiring eye was boring deeper and deeper--a +senseless reply offered--there was no time to weigh it, so at a venture +he uttered it--feebly: + +"Sunday-school books, maybe." + +Poor Huck was too distressed to smile, but the old man laughed loud +and joyously, shook up the details of his anatomy from head to foot, +and ended by saying that such a laugh was money in a-man's pocket, +because it cut down the doctor's bill like everything. Then he added: + +"Poor old chap, you're white and jaded--you ain't well a bit--no +wonder you're a little flighty and off your balance. But you'll come +out of it. Rest and sleep will fetch you out all right, I hope." + +Huck was irritated to think he had been such a goose and betrayed such +a suspicious excitement, for he had dropped the idea that the parcel +brought from the tavern was the treasure, as soon as he had heard the +talk at the widow's stile. He had only thought it was not the treasure, +however--he had not known that it wasn't--and so the suggestion of a +captured bundle was too much for his self-possession. But on the whole +he felt glad the little episode had happened, for now he knew beyond +all question that that bundle was not THE bundle, and so his mind was +at rest and exceedingly comfortable. In fact, everything seemed to be +drifting just in the right direction, now; the treasure must be still +in No. 2, the men would be captured and jailed that day, and he and Tom +could seize the gold that night without any trouble or any fear of +interruption. + +Just as breakfast was completed there was a knock at the door. Huck +jumped for a hiding-place, for he had no mind to be connected even +remotely with the late event. The Welshman admitted several ladies and +gentlemen, among them the Widow Douglas, and noticed that groups of +citizens were climbing up the hill--to stare at the stile. So the news +had spread. The Welshman had to tell the story of the night to the +visitors. The widow's gratitude for her preservation was outspoken. + +"Don't say a word about it, madam. There's another that you're more +beholden to than you are to me and my boys, maybe, but he don't allow +me to tell his name. We wouldn't have been there but for him." + +Of course this excited a curiosity so vast that it almost belittled +the main matter--but the Welshman allowed it to eat into the vitals of +his visitors, and through them be transmitted to the whole town, for he +refused to part with his secret. When all else had been learned, the +widow said: + +"I went to sleep reading in bed and slept straight through all that +noise. Why didn't you come and wake me?" + +"We judged it warn't worth while. Those fellows warn't likely to come +again--they hadn't any tools left to work with, and what was the use of +waking you up and scaring you to death? My three negro men stood guard +at your house all the rest of the night. They've just come back." + +More visitors came, and the story had to be told and retold for a +couple of hours more. + +There was no Sabbath-school during day-school vacation, but everybody +was early at church. The stirring event was well canvassed. News came +that not a sign of the two villains had been yet discovered. When the +sermon was finished, Judge Thatcher's wife dropped alongside of Mrs. +Harper as she moved down the aisle with the crowd and said: + +"Is my Becky going to sleep all day? I just expected she would be +tired to death." + +"Your Becky?" + +"Yes," with a startled look--"didn't she stay with you last night?" + +"Why, no." + +Mrs. Thatcher turned pale, and sank into a pew, just as Aunt Polly, +talking briskly with a friend, passed by. Aunt Polly said: + +"Good-morning, Mrs. Thatcher. Good-morning, Mrs. Harper. I've got a +boy that's turned up missing. I reckon my Tom stayed at your house last +night--one of you. And now he's afraid to come to church. I've got to +settle with him." + +Mrs. Thatcher shook her head feebly and turned paler than ever. + +"He didn't stay with us," said Mrs. Harper, beginning to look uneasy. +A marked anxiety came into Aunt Polly's face. + +"Joe Harper, have you seen my Tom this morning?" + +"No'm." + +"When did you see him last?" + +Joe tried to remember, but was not sure he could say. The people had +stopped moving out of church. Whispers passed along, and a boding +uneasiness took possession of every countenance. Children were +anxiously questioned, and young teachers. They all said they had not +noticed whether Tom and Becky were on board the ferryboat on the +homeward trip; it was dark; no one thought of inquiring if any one was +missing. One young man finally blurted out his fear that they were +still in the cave! Mrs. Thatcher swooned away. Aunt Polly fell to +crying and wringing her hands. + +The alarm swept from lip to lip, from group to group, from street to +street, and within five minutes the bells were wildly clanging and the +whole town was up! The Cardiff Hill episode sank into instant +insignificance, the burglars were forgotten, horses were saddled, +skiffs were manned, the ferryboat ordered out, and before the horror +was half an hour old, two hundred men were pouring down highroad and +river toward the cave. + +All the long afternoon the village seemed empty and dead. Many women +visited Aunt Polly and Mrs. Thatcher and tried to comfort them. They +cried with them, too, and that was still better than words. All the +tedious night the town waited for news; but when the morning dawned at +last, all the word that came was, "Send more candles--and send food." +Mrs. Thatcher was almost crazed; and Aunt Polly, also. Judge Thatcher +sent messages of hope and encouragement from the cave, but they +conveyed no real cheer. + +The old Welshman came home toward daylight, spattered with +candle-grease, smeared with clay, and almost worn out. He found Huck +still in the bed that had been provided for him, and delirious with +fever. The physicians were all at the cave, so the Widow Douglas came +and took charge of the patient. She said she would do her best by him, +because, whether he was good, bad, or indifferent, he was the Lord's, +and nothing that was the Lord's was a thing to be neglected. The +Welshman said Huck had good spots in him, and the widow said: + +"You can depend on it. That's the Lord's mark. He don't leave it off. +He never does. Puts it somewhere on every creature that comes from his +hands." + +Early in the forenoon parties of jaded men began to straggle into the +village, but the strongest of the citizens continued searching. All the +news that could be gained was that remotenesses of the cavern were +being ransacked that had never been visited before; that every corner +and crevice was going to be thoroughly searched; that wherever one +wandered through the maze of passages, lights were to be seen flitting +hither and thither in the distance, and shoutings and pistol-shots sent +their hollow reverberations to the ear down the sombre aisles. In one +place, far from the section usually traversed by tourists, the names +"BECKY & TOM" had been found traced upon the rocky wall with +candle-smoke, and near at hand a grease-soiled bit of ribbon. Mrs. +Thatcher recognized the ribbon and cried over it. She said it was the +last relic she should ever have of her child; and that no other memorial +of her could ever be so precious, because this one parted latest from +the living body before the awful death came. Some said that now and +then, in the cave, a far-away speck of light would glimmer, and then a +glorious shout would burst forth and a score of men go trooping down the +echoing aisle--and then a sickening disappointment always followed; the +children were not there; it was only a searcher's light. + +Three dreadful days and nights dragged their tedious hours along, and +the village sank into a hopeless stupor. No one had heart for anything. +The accidental discovery, just made, that the proprietor of the +Temperance Tavern kept liquor on his premises, scarcely fluttered the +public pulse, tremendous as the fact was. In a lucid interval, Huck +feebly led up to the subject of taverns, and finally asked--dimly +dreading the worst--if anything had been discovered at the Temperance +Tavern since he had been ill. + +"Yes," said the widow. + +Huck started up in bed, wild-eyed: + +"What? What was it?" + +"Liquor!--and the place has been shut up. Lie down, child--what a turn +you did give me!" + +"Only tell me just one thing--only just one--please! Was it Tom Sawyer +that found it?" + +The widow burst into tears. "Hush, hush, child, hush! I've told you +before, you must NOT talk. You are very, very sick!" + +Then nothing but liquor had been found; there would have been a great +powwow if it had been the gold. So the treasure was gone forever--gone +forever! But what could she be crying about? Curious that she should +cry. + +These thoughts worked their dim way through Huck's mind, and under the +weariness they gave him he fell asleep. The widow said to herself: + +"There--he's asleep, poor wreck. Tom Sawyer find it! Pity but somebody +could find Tom Sawyer! Ah, there ain't many left, now, that's got hope +enough, or strength enough, either, to go on searching." + + + +CHAPTER XXXI + +NOW to return to Tom and Becky's share in the picnic. They tripped +along the murky aisles with the rest of the company, visiting the +familiar wonders of the cave--wonders dubbed with rather +over-descriptive names, such as "The Drawing-Room," "The Cathedral," +"Aladdin's Palace," and so on. Presently the hide-and-seek frolicking +began, and Tom and Becky engaged in it with zeal until the exertion +began to grow a trifle wearisome; then they wandered down a sinuous +avenue holding their candles aloft and reading the tangled web-work of +names, dates, post-office addresses, and mottoes with which the rocky +walls had been frescoed (in candle-smoke). Still drifting along and +talking, they scarcely noticed that they were now in a part of the cave +whose walls were not frescoed. They smoked their own names under an +overhanging shelf and moved on. Presently they came to a place where a +little stream of water, trickling over a ledge and carrying a limestone +sediment with it, had, in the slow-dragging ages, formed a laced and +ruffled Niagara in gleaming and imperishable stone. Tom squeezed his +small body behind it in order to illuminate it for Becky's +gratification. He found that it curtained a sort of steep natural +stairway which was enclosed between narrow walls, and at once the +ambition to be a discoverer seized him. Becky responded to his call, +and they made a smoke-mark for future guidance, and started upon their +quest. They wound this way and that, far down into the secret depths of +the cave, made another mark, and branched off in search of novelties to +tell the upper world about. In one place they found a spacious cavern, +from whose ceiling depended a multitude of shining stalactites of the +length and circumference of a man's leg; they walked all about it, +wondering and admiring, and presently left it by one of the numerous +passages that opened into it. This shortly brought them to a bewitching +spring, whose basin was incrusted with a frostwork of glittering +crystals; it was in the midst of a cavern whose walls were supported by +many fantastic pillars which had been formed by the joining of great +stalactites and stalagmites together, the result of the ceaseless +water-drip of centuries. Under the roof vast knots of bats had packed +themselves together, thousands in a bunch; the lights disturbed the +creatures and they came flocking down by hundreds, squeaking and +darting furiously at the candles. Tom knew their ways and the danger of +this sort of conduct. He seized Becky's hand and hurried her into the +first corridor that offered; and none too soon, for a bat struck +Becky's light out with its wing while she was passing out of the +cavern. The bats chased the children a good distance; but the fugitives +plunged into every new passage that offered, and at last got rid of the +perilous things. Tom found a subterranean lake, shortly, which +stretched its dim length away until its shape was lost in the shadows. +He wanted to explore its borders, but concluded that it would be best +to sit down and rest awhile, first. Now, for the first time, the deep +stillness of the place laid a clammy hand upon the spirits of the +children. Becky said: + +"Why, I didn't notice, but it seems ever so long since I heard any of +the others." + +"Come to think, Becky, we are away down below them--and I don't know +how far away north, or south, or east, or whichever it is. We couldn't +hear them here." + +Becky grew apprehensive. + +"I wonder how long we've been down here, Tom? We better start back." + +"Yes, I reckon we better. P'raps we better." + +"Can you find the way, Tom? It's all a mixed-up crookedness to me." + +"I reckon I could find it--but then the bats. If they put our candles +out it will be an awful fix. Let's try some other way, so as not to go +through there." + +"Well. But I hope we won't get lost. It would be so awful!" and the +girl shuddered at the thought of the dreadful possibilities. + +They started through a corridor, and traversed it in silence a long +way, glancing at each new opening, to see if there was anything +familiar about the look of it; but they were all strange. Every time +Tom made an examination, Becky would watch his face for an encouraging +sign, and he would say cheerily: + +"Oh, it's all right. This ain't the one, but we'll come to it right +away!" + +But he felt less and less hopeful with each failure, and presently +began to turn off into diverging avenues at sheer random, in desperate +hope of finding the one that was wanted. He still said it was "all +right," but there was such a leaden dread at his heart that the words +had lost their ring and sounded just as if he had said, "All is lost!" +Becky clung to his side in an anguish of fear, and tried hard to keep +back the tears, but they would come. At last she said: + +"Oh, Tom, never mind the bats, let's go back that way! We seem to get +worse and worse off all the time." + +"Listen!" said he. + +Profound silence; silence so deep that even their breathings were +conspicuous in the hush. Tom shouted. The call went echoing down the +empty aisles and died out in the distance in a faint sound that +resembled a ripple of mocking laughter. + +"Oh, don't do it again, Tom, it is too horrid," said Becky. + +"It is horrid, but I better, Becky; they might hear us, you know," and +he shouted again. + +The "might" was even a chillier horror than the ghostly laughter, it +so confessed a perishing hope. The children stood still and listened; +but there was no result. Tom turned upon the back track at once, and +hurried his steps. It was but a little while before a certain +indecision in his manner revealed another fearful fact to Becky--he +could not find his way back! + +"Oh, Tom, you didn't make any marks!" + +"Becky, I was such a fool! Such a fool! I never thought we might want +to come back! No--I can't find the way. It's all mixed up." + +"Tom, Tom, we're lost! we're lost! We never can get out of this awful +place! Oh, why DID we ever leave the others!" + +She sank to the ground and burst into such a frenzy of crying that Tom +was appalled with the idea that she might die, or lose her reason. He +sat down by her and put his arms around her; she buried her face in his +bosom, she clung to him, she poured out her terrors, her unavailing +regrets, and the far echoes turned them all to jeering laughter. Tom +begged her to pluck up hope again, and she said she could not. He fell +to blaming and abusing himself for getting her into this miserable +situation; this had a better effect. She said she would try to hope +again, she would get up and follow wherever he might lead if only he +would not talk like that any more. For he was no more to blame than +she, she said. + +So they moved on again--aimlessly--simply at random--all they could do +was to move, keep moving. For a little while, hope made a show of +reviving--not with any reason to back it, but only because it is its +nature to revive when the spring has not been taken out of it by age +and familiarity with failure. + +By-and-by Tom took Becky's candle and blew it out. This economy meant +so much! Words were not needed. Becky understood, and her hope died +again. She knew that Tom had a whole candle and three or four pieces in +his pockets--yet he must economize. + +By-and-by, fatigue began to assert its claims; the children tried to +pay attention, for it was dreadful to think of sitting down when time +was grown to be so precious, moving, in some direction, in any +direction, was at least progress and might bear fruit; but to sit down +was to invite death and shorten its pursuit. + +At last Becky's frail limbs refused to carry her farther. She sat +down. Tom rested with her, and they talked of home, and the friends +there, and the comfortable beds and, above all, the light! Becky cried, +and Tom tried to think of some way of comforting her, but all his +encouragements were grown threadbare with use, and sounded like +sarcasms. Fatigue bore so heavily upon Becky that she drowsed off to +sleep. Tom was grateful. He sat looking into her drawn face and saw it +grow smooth and natural under the influence of pleasant dreams; and +by-and-by a smile dawned and rested there. The peaceful face reflected +somewhat of peace and healing into his own spirit, and his thoughts +wandered away to bygone times and dreamy memories. While he was deep in +his musings, Becky woke up with a breezy little laugh--but it was +stricken dead upon her lips, and a groan followed it. + +"Oh, how COULD I sleep! I wish I never, never had waked! No! No, I +don't, Tom! Don't look so! I won't say it again." + +"I'm glad you've slept, Becky; you'll feel rested, now, and we'll find +the way out." + +"We can try, Tom; but I've seen such a beautiful country in my dream. +I reckon we are going there." + +"Maybe not, maybe not. Cheer up, Becky, and let's go on trying." + +They rose up and wandered along, hand in hand and hopeless. They tried +to estimate how long they had been in the cave, but all they knew was +that it seemed days and weeks, and yet it was plain that this could not +be, for their candles were not gone yet. A long time after this--they +could not tell how long--Tom said they must go softly and listen for +dripping water--they must find a spring. They found one presently, and +Tom said it was time to rest again. Both were cruelly tired, yet Becky +said she thought she could go a little farther. She was surprised to +hear Tom dissent. She could not understand it. They sat down, and Tom +fastened his candle to the wall in front of them with some clay. +Thought was soon busy; nothing was said for some time. Then Becky broke +the silence: + +"Tom, I am so hungry!" + +Tom took something out of his pocket. + +"Do you remember this?" said he. + +Becky almost smiled. + +"It's our wedding-cake, Tom." + +"Yes--I wish it was as big as a barrel, for it's all we've got." + +"I saved it from the picnic for us to dream on, Tom, the way grown-up +people do with wedding-cake--but it'll be our--" + +She dropped the sentence where it was. Tom divided the cake and Becky +ate with good appetite, while Tom nibbled at his moiety. There was +abundance of cold water to finish the feast with. By-and-by Becky +suggested that they move on again. Tom was silent a moment. Then he +said: + +"Becky, can you bear it if I tell you something?" + +Becky's face paled, but she thought she could. + +"Well, then, Becky, we must stay here, where there's water to drink. +That little piece is our last candle!" + +Becky gave loose to tears and wailings. Tom did what he could to +comfort her, but with little effect. At length Becky said: + +"Tom!" + +"Well, Becky?" + +"They'll miss us and hunt for us!" + +"Yes, they will! Certainly they will!" + +"Maybe they're hunting for us now, Tom." + +"Why, I reckon maybe they are. I hope they are." + +"When would they miss us, Tom?" + +"When they get back to the boat, I reckon." + +"Tom, it might be dark then--would they notice we hadn't come?" + +"I don't know. But anyway, your mother would miss you as soon as they +got home." + +A frightened look in Becky's face brought Tom to his senses and he saw +that he had made a blunder. Becky was not to have gone home that night! +The children became silent and thoughtful. In a moment a new burst of +grief from Becky showed Tom that the thing in his mind had struck hers +also--that the Sabbath morning might be half spent before Mrs. Thatcher +discovered that Becky was not at Mrs. Harper's. + +The children fastened their eyes upon their bit of candle and watched +it melt slowly and pitilessly away; saw the half inch of wick stand +alone at last; saw the feeble flame rise and fall, climb the thin +column of smoke, linger at its top a moment, and then--the horror of +utter darkness reigned! + +How long afterward it was that Becky came to a slow consciousness that +she was crying in Tom's arms, neither could tell. All that they knew +was, that after what seemed a mighty stretch of time, both awoke out of +a dead stupor of sleep and resumed their miseries once more. Tom said +it might be Sunday, now--maybe Monday. He tried to get Becky to talk, +but her sorrows were too oppressive, all her hopes were gone. Tom said +that they must have been missed long ago, and no doubt the search was +going on. He would shout and maybe some one would come. He tried it; +but in the darkness the distant echoes sounded so hideously that he +tried it no more. + +The hours wasted away, and hunger came to torment the captives again. +A portion of Tom's half of the cake was left; they divided and ate it. +But they seemed hungrier than before. The poor morsel of food only +whetted desire. + +By-and-by Tom said: + +"SH! Did you hear that?" + +Both held their breath and listened. There was a sound like the +faintest, far-off shout. Instantly Tom answered it, and leading Becky +by the hand, started groping down the corridor in its direction. +Presently he listened again; again the sound was heard, and apparently +a little nearer. + +"It's them!" said Tom; "they're coming! Come along, Becky--we're all +right now!" + +The joy of the prisoners was almost overwhelming. Their speed was +slow, however, because pitfalls were somewhat common, and had to be +guarded against. They shortly came to one and had to stop. It might be +three feet deep, it might be a hundred--there was no passing it at any +rate. Tom got down on his breast and reached as far down as he could. +No bottom. They must stay there and wait until the searchers came. They +listened; evidently the distant shoutings were growing more distant! a +moment or two more and they had gone altogether. The heart-sinking +misery of it! Tom whooped until he was hoarse, but it was of no use. He +talked hopefully to Becky; but an age of anxious waiting passed and no +sounds came again. + +The children groped their way back to the spring. The weary time +dragged on; they slept again, and awoke famished and woe-stricken. Tom +believed it must be Tuesday by this time. + +Now an idea struck him. There were some side passages near at hand. It +would be better to explore some of these than bear the weight of the +heavy time in idleness. He took a kite-line from his pocket, tied it to +a projection, and he and Becky started, Tom in the lead, unwinding the +line as he groped along. At the end of twenty steps the corridor ended +in a "jumping-off place." Tom got down on his knees and felt below, and +then as far around the corner as he could reach with his hands +conveniently; he made an effort to stretch yet a little farther to the +right, and at that moment, not twenty yards away, a human hand, holding +a candle, appeared from behind a rock! Tom lifted up a glorious shout, +and instantly that hand was followed by the body it belonged to--Injun +Joe's! Tom was paralyzed; he could not move. He was vastly gratified +the next moment, to see the "Spaniard" take to his heels and get +himself out of sight. Tom wondered that Joe had not recognized his +voice and come over and killed him for testifying in court. But the +echoes must have disguised the voice. Without doubt, that was it, he +reasoned. Tom's fright weakened every muscle in his body. He said to +himself that if he had strength enough to get back to the spring he +would stay there, and nothing should tempt him to run the risk of +meeting Injun Joe again. He was careful to keep from Becky what it was +he had seen. He told her he had only shouted "for luck." + +But hunger and wretchedness rise superior to fears in the long run. +Another tedious wait at the spring and another long sleep brought +changes. The children awoke tortured with a raging hunger. Tom believed +that it must be Wednesday or Thursday or even Friday or Saturday, now, +and that the search had been given over. He proposed to explore another +passage. He felt willing to risk Injun Joe and all other terrors. But +Becky was very weak. She had sunk into a dreary apathy and would not be +roused. She said she would wait, now, where she was, and die--it would +not be long. She told Tom to go with the kite-line and explore if he +chose; but she implored him to come back every little while and speak +to her; and she made him promise that when the awful time came, he +would stay by her and hold her hand until all was over. + +Tom kissed her, with a choking sensation in his throat, and made a +show of being confident of finding the searchers or an escape from the +cave; then he took the kite-line in his hand and went groping down one +of the passages on his hands and knees, distressed with hunger and sick +with bodings of coming doom. + + + +CHAPTER XXXII + +TUESDAY afternoon came, and waned to the twilight. The village of St. +Petersburg still mourned. The lost children had not been found. Public +prayers had been offered up for them, and many and many a private +prayer that had the petitioner's whole heart in it; but still no good +news came from the cave. The majority of the searchers had given up the +quest and gone back to their daily avocations, saying that it was plain +the children could never be found. Mrs. Thatcher was very ill, and a +great part of the time delirious. People said it was heartbreaking to +hear her call her child, and raise her head and listen a whole minute +at a time, then lay it wearily down again with a moan. Aunt Polly had +drooped into a settled melancholy, and her gray hair had grown almost +white. The village went to its rest on Tuesday night, sad and forlorn. + +Away in the middle of the night a wild peal burst from the village +bells, and in a moment the streets were swarming with frantic half-clad +people, who shouted, "Turn out! turn out! they're found! they're +found!" Tin pans and horns were added to the din, the population massed +itself and moved toward the river, met the children coming in an open +carriage drawn by shouting citizens, thronged around it, joined its +homeward march, and swept magnificently up the main street roaring +huzzah after huzzah! + +The village was illuminated; nobody went to bed again; it was the +greatest night the little town had ever seen. During the first half-hour +a procession of villagers filed through Judge Thatcher's house, seized +the saved ones and kissed them, squeezed Mrs. Thatcher's hand, tried to +speak but couldn't--and drifted out raining tears all over the place. + +Aunt Polly's happiness was complete, and Mrs. Thatcher's nearly so. It +would be complete, however, as soon as the messenger dispatched with +the great news to the cave should get the word to her husband. Tom lay +upon a sofa with an eager auditory about him and told the history of +the wonderful adventure, putting in many striking additions to adorn it +withal; and closed with a description of how he left Becky and went on +an exploring expedition; how he followed two avenues as far as his +kite-line would reach; how he followed a third to the fullest stretch of +the kite-line, and was about to turn back when he glimpsed a far-off +speck that looked like daylight; dropped the line and groped toward it, +pushed his head and shoulders through a small hole, and saw the broad +Mississippi rolling by! And if it had only happened to be night he would +not have seen that speck of daylight and would not have explored that +passage any more! He told how he went back for Becky and broke the good +news and she told him not to fret her with such stuff, for she was +tired, and knew she was going to die, and wanted to. He described how he +labored with her and convinced her; and how she almost died for joy when +she had groped to where she actually saw the blue speck of daylight; how +he pushed his way out at the hole and then helped her out; how they sat +there and cried for gladness; how some men came along in a skiff and Tom +hailed them and told them their situation and their famished condition; +how the men didn't believe the wild tale at first, "because," said they, +"you are five miles down the river below the valley the cave is in" +--then took them aboard, rowed to a house, gave them supper, made them +rest till two or three hours after dark and then brought them home. + +Before day-dawn, Judge Thatcher and the handful of searchers with him +were tracked out, in the cave, by the twine clews they had strung +behind them, and informed of the great news. + +Three days and nights of toil and hunger in the cave were not to be +shaken off at once, as Tom and Becky soon discovered. They were +bedridden all of Wednesday and Thursday, and seemed to grow more and +more tired and worn, all the time. Tom got about, a little, on +Thursday, was down-town Friday, and nearly as whole as ever Saturday; +but Becky did not leave her room until Sunday, and then she looked as +if she had passed through a wasting illness. + +Tom learned of Huck's sickness and went to see him on Friday, but +could not be admitted to the bedroom; neither could he on Saturday or +Sunday. He was admitted daily after that, but was warned to keep still +about his adventure and introduce no exciting topic. The Widow Douglas +stayed by to see that he obeyed. At home Tom learned of the Cardiff +Hill event; also that the "ragged man's" body had eventually been found +in the river near the ferry-landing; he had been drowned while trying +to escape, perhaps. + +About a fortnight after Tom's rescue from the cave, he started off to +visit Huck, who had grown plenty strong enough, now, to hear exciting +talk, and Tom had some that would interest him, he thought. Judge +Thatcher's house was on Tom's way, and he stopped to see Becky. The +Judge and some friends set Tom to talking, and some one asked him +ironically if he wouldn't like to go to the cave again. Tom said he +thought he wouldn't mind it. The Judge said: + +"Well, there are others just like you, Tom, I've not the least doubt. +But we have taken care of that. Nobody will get lost in that cave any +more." + +"Why?" + +"Because I had its big door sheathed with boiler iron two weeks ago, +and triple-locked--and I've got the keys." + +Tom turned as white as a sheet. + +"What's the matter, boy! Here, run, somebody! Fetch a glass of water!" + +The water was brought and thrown into Tom's face. + +"Ah, now you're all right. What was the matter with you, Tom?" + +"Oh, Judge, Injun Joe's in the cave!" + + + +CHAPTER XXXIII + +WITHIN a few minutes the news had spread, and a dozen skiff-loads of +men were on their way to McDougal's cave, and the ferryboat, well +filled with passengers, soon followed. Tom Sawyer was in the skiff that +bore Judge Thatcher. + +When the cave door was unlocked, a sorrowful sight presented itself in +the dim twilight of the place. Injun Joe lay stretched upon the ground, +dead, with his face close to the crack of the door, as if his longing +eyes had been fixed, to the latest moment, upon the light and the cheer +of the free world outside. Tom was touched, for he knew by his own +experience how this wretch had suffered. His pity was moved, but +nevertheless he felt an abounding sense of relief and security, now, +which revealed to him in a degree which he had not fully appreciated +before how vast a weight of dread had been lying upon him since the day +he lifted his voice against this bloody-minded outcast. + +Injun Joe's bowie-knife lay close by, its blade broken in two. The +great foundation-beam of the door had been chipped and hacked through, +with tedious labor; useless labor, too, it was, for the native rock +formed a sill outside it, and upon that stubborn material the knife had +wrought no effect; the only damage done was to the knife itself. But if +there had been no stony obstruction there the labor would have been +useless still, for if the beam had been wholly cut away Injun Joe could +not have squeezed his body under the door, and he knew it. So he had +only hacked that place in order to be doing something--in order to pass +the weary time--in order to employ his tortured faculties. Ordinarily +one could find half a dozen bits of candle stuck around in the crevices +of this vestibule, left there by tourists; but there were none now. The +prisoner had searched them out and eaten them. He had also contrived to +catch a few bats, and these, also, he had eaten, leaving only their +claws. The poor unfortunate had starved to death. In one place, near at +hand, a stalagmite had been slowly growing up from the ground for ages, +builded by the water-drip from a stalactite overhead. The captive had +broken off the stalagmite, and upon the stump had placed a stone, +wherein he had scooped a shallow hollow to catch the precious drop +that fell once in every three minutes with the dreary regularity of a +clock-tick--a dessertspoonful once in four and twenty hours. That drop +was falling when the Pyramids were new; when Troy fell; when the +foundations of Rome were laid; when Christ was crucified; when the +Conqueror created the British empire; when Columbus sailed; when the +massacre at Lexington was "news." It is falling now; it will still be +falling when all these things shall have sunk down the afternoon of +history, and the twilight of tradition, and been swallowed up in the +thick night of oblivion. Has everything a purpose and a mission? Did +this drop fall patiently during five thousand years to be ready for +this flitting human insect's need? and has it another important object +to accomplish ten thousand years to come? No matter. It is many and +many a year since the hapless half-breed scooped out the stone to catch +the priceless drops, but to this day the tourist stares longest at that +pathetic stone and that slow-dropping water when he comes to see the +wonders of McDougal's cave. Injun Joe's cup stands first in the list of +the cavern's marvels; even "Aladdin's Palace" cannot rival it. + +Injun Joe was buried near the mouth of the cave; and people flocked +there in boats and wagons from the towns and from all the farms and +hamlets for seven miles around; they brought their children, and all +sorts of provisions, and confessed that they had had almost as +satisfactory a time at the funeral as they could have had at the +hanging. + +This funeral stopped the further growth of one thing--the petition to +the governor for Injun Joe's pardon. The petition had been largely +signed; many tearful and eloquent meetings had been held, and a +committee of sappy women been appointed to go in deep mourning and wail +around the governor, and implore him to be a merciful ass and trample +his duty under foot. Injun Joe was believed to have killed five +citizens of the village, but what of that? If he had been Satan himself +there would have been plenty of weaklings ready to scribble their names +to a pardon-petition, and drip a tear on it from their permanently +impaired and leaky water-works. + +The morning after the funeral Tom took Huck to a private place to have +an important talk. Huck had learned all about Tom's adventure from the +Welshman and the Widow Douglas, by this time, but Tom said he reckoned +there was one thing they had not told him; that thing was what he +wanted to talk about now. Huck's face saddened. He said: + +"I know what it is. You got into No. 2 and never found anything but +whiskey. Nobody told me it was you; but I just knowed it must 'a' ben +you, soon as I heard 'bout that whiskey business; and I knowed you +hadn't got the money becuz you'd 'a' got at me some way or other and +told me even if you was mum to everybody else. Tom, something's always +told me we'd never get holt of that swag." + +"Why, Huck, I never told on that tavern-keeper. YOU know his tavern +was all right the Saturday I went to the picnic. Don't you remember you +was to watch there that night?" + +"Oh yes! Why, it seems 'bout a year ago. It was that very night that I +follered Injun Joe to the widder's." + +"YOU followed him?" + +"Yes--but you keep mum. I reckon Injun Joe's left friends behind him, +and I don't want 'em souring on me and doing me mean tricks. If it +hadn't ben for me he'd be down in Texas now, all right." + +Then Huck told his entire adventure in confidence to Tom, who had only +heard of the Welshman's part of it before. + +"Well," said Huck, presently, coming back to the main question, +"whoever nipped the whiskey in No. 2, nipped the money, too, I reckon +--anyways it's a goner for us, Tom." + +"Huck, that money wasn't ever in No. 2!" + +"What!" Huck searched his comrade's face keenly. "Tom, have you got on +the track of that money again?" + +"Huck, it's in the cave!" + +Huck's eyes blazed. + +"Say it again, Tom." + +"The money's in the cave!" + +"Tom--honest injun, now--is it fun, or earnest?" + +"Earnest, Huck--just as earnest as ever I was in my life. Will you go +in there with me and help get it out?" + +"I bet I will! I will if it's where we can blaze our way to it and not +get lost." + +"Huck, we can do that without the least little bit of trouble in the +world." + +"Good as wheat! What makes you think the money's--" + +"Huck, you just wait till we get in there. If we don't find it I'll +agree to give you my drum and every thing I've got in the world. I +will, by jings." + +"All right--it's a whiz. When do you say?" + +"Right now, if you say it. Are you strong enough?" + +"Is it far in the cave? I ben on my pins a little, three or four days, +now, but I can't walk more'n a mile, Tom--least I don't think I could." + +"It's about five mile into there the way anybody but me would go, +Huck, but there's a mighty short cut that they don't anybody but me +know about. Huck, I'll take you right to it in a skiff. I'll float the +skiff down there, and I'll pull it back again all by myself. You +needn't ever turn your hand over." + +"Less start right off, Tom." + +"All right. We want some bread and meat, and our pipes, and a little +bag or two, and two or three kite-strings, and some of these +new-fangled things they call lucifer matches. I tell you, many's +the time I wished I had some when I was in there before." + +A trifle after noon the boys borrowed a small skiff from a citizen who +was absent, and got under way at once. When they were several miles +below "Cave Hollow," Tom said: + +"Now you see this bluff here looks all alike all the way down from the +cave hollow--no houses, no wood-yards, bushes all alike. But do you see +that white place up yonder where there's been a landslide? Well, that's +one of my marks. We'll get ashore, now." + +They landed. + +"Now, Huck, where we're a-standing you could touch that hole I got out +of with a fishing-pole. See if you can find it." + +Huck searched all the place about, and found nothing. Tom proudly +marched into a thick clump of sumach bushes and said: + +"Here you are! Look at it, Huck; it's the snuggest hole in this +country. You just keep mum about it. All along I've been wanting to be +a robber, but I knew I'd got to have a thing like this, and where to +run across it was the bother. We've got it now, and we'll keep it +quiet, only we'll let Joe Harper and Ben Rogers in--because of course +there's got to be a Gang, or else there wouldn't be any style about it. +Tom Sawyer's Gang--it sounds splendid, don't it, Huck?" + +"Well, it just does, Tom. And who'll we rob?" + +"Oh, most anybody. Waylay people--that's mostly the way." + +"And kill them?" + +"No, not always. Hive them in the cave till they raise a ransom." + +"What's a ransom?" + +"Money. You make them raise all they can, off'n their friends; and +after you've kept them a year, if it ain't raised then you kill them. +That's the general way. Only you don't kill the women. You shut up the +women, but you don't kill them. They're always beautiful and rich, and +awfully scared. You take their watches and things, but you always take +your hat off and talk polite. They ain't anybody as polite as robbers +--you'll see that in any book. Well, the women get to loving you, and +after they've been in the cave a week or two weeks they stop crying and +after that you couldn't get them to leave. If you drove them out they'd +turn right around and come back. It's so in all the books." + +"Why, it's real bully, Tom. I believe it's better'n to be a pirate." + +"Yes, it's better in some ways, because it's close to home and +circuses and all that." + +By this time everything was ready and the boys entered the hole, Tom +in the lead. They toiled their way to the farther end of the tunnel, +then made their spliced kite-strings fast and moved on. A few steps +brought them to the spring, and Tom felt a shudder quiver all through +him. He showed Huck the fragment of candle-wick perched on a lump of +clay against the wall, and described how he and Becky had watched the +flame struggle and expire. + +The boys began to quiet down to whispers, now, for the stillness and +gloom of the place oppressed their spirits. They went on, and presently +entered and followed Tom's other corridor until they reached the +"jumping-off place." The candles revealed the fact that it was not +really a precipice, but only a steep clay hill twenty or thirty feet +high. Tom whispered: + +"Now I'll show you something, Huck." + +He held his candle aloft and said: + +"Look as far around the corner as you can. Do you see that? There--on +the big rock over yonder--done with candle-smoke." + +"Tom, it's a CROSS!" + +"NOW where's your Number Two? 'UNDER THE CROSS,' hey? Right yonder's +where I saw Injun Joe poke up his candle, Huck!" + +Huck stared at the mystic sign awhile, and then said with a shaky voice: + +"Tom, less git out of here!" + +"What! and leave the treasure?" + +"Yes--leave it. Injun Joe's ghost is round about there, certain." + +"No it ain't, Huck, no it ain't. It would ha'nt the place where he +died--away out at the mouth of the cave--five mile from here." + +"No, Tom, it wouldn't. It would hang round the money. I know the ways +of ghosts, and so do you." + +Tom began to fear that Huck was right. Misgivings gathered in his +mind. But presently an idea occurred to him-- + +"Lookyhere, Huck, what fools we're making of ourselves! Injun Joe's +ghost ain't a going to come around where there's a cross!" + +The point was well taken. It had its effect. + +"Tom, I didn't think of that. But that's so. It's luck for us, that +cross is. I reckon we'll climb down there and have a hunt for that box." + +Tom went first, cutting rude steps in the clay hill as he descended. +Huck followed. Four avenues opened out of the small cavern which the +great rock stood in. The boys examined three of them with no result. +They found a small recess in the one nearest the base of the rock, with +a pallet of blankets spread down in it; also an old suspender, some +bacon rind, and the well-gnawed bones of two or three fowls. But there +was no money-box. The lads searched and researched this place, but in +vain. Tom said: + +"He said UNDER the cross. Well, this comes nearest to being under the +cross. It can't be under the rock itself, because that sets solid on +the ground." + +They searched everywhere once more, and then sat down discouraged. +Huck could suggest nothing. By-and-by Tom said: + +"Lookyhere, Huck, there's footprints and some candle-grease on the +clay about one side of this rock, but not on the other sides. Now, +what's that for? I bet you the money IS under the rock. I'm going to +dig in the clay." + +"That ain't no bad notion, Tom!" said Huck with animation. + +Tom's "real Barlow" was out at once, and he had not dug four inches +before he struck wood. + +"Hey, Huck!--you hear that?" + +Huck began to dig and scratch now. Some boards were soon uncovered and +removed. They had concealed a natural chasm which led under the rock. +Tom got into this and held his candle as far under the rock as he +could, but said he could not see to the end of the rift. He proposed to +explore. He stooped and passed under; the narrow way descended +gradually. He followed its winding course, first to the right, then to +the left, Huck at his heels. Tom turned a short curve, by-and-by, and +exclaimed: + +"My goodness, Huck, lookyhere!" + +It was the treasure-box, sure enough, occupying a snug little cavern, +along with an empty powder-keg, a couple of guns in leather cases, two +or three pairs of old moccasins, a leather belt, and some other rubbish +well soaked with the water-drip. + +"Got it at last!" said Huck, ploughing among the tarnished coins with +his hand. "My, but we're rich, Tom!" + +"Huck, I always reckoned we'd get it. It's just too good to believe, +but we HAVE got it, sure! Say--let's not fool around here. Let's snake +it out. Lemme see if I can lift the box." + +It weighed about fifty pounds. Tom could lift it, after an awkward +fashion, but could not carry it conveniently. + +"I thought so," he said; "THEY carried it like it was heavy, that day +at the ha'nted house. I noticed that. I reckon I was right to think of +fetching the little bags along." + +The money was soon in the bags and the boys took it up to the cross +rock. + +"Now less fetch the guns and things," said Huck. + +"No, Huck--leave them there. They're just the tricks to have when we +go to robbing. We'll keep them there all the time, and we'll hold our +orgies there, too. It's an awful snug place for orgies." + +"What orgies?" + +"I dono. But robbers always have orgies, and of course we've got to +have them, too. Come along, Huck, we've been in here a long time. It's +getting late, I reckon. I'm hungry, too. We'll eat and smoke when we +get to the skiff." + +They presently emerged into the clump of sumach bushes, looked warily +out, found the coast clear, and were soon lunching and smoking in the +skiff. As the sun dipped toward the horizon they pushed out and got +under way. Tom skimmed up the shore through the long twilight, chatting +cheerily with Huck, and landed shortly after dark. + +"Now, Huck," said Tom, "we'll hide the money in the loft of the +widow's woodshed, and I'll come up in the morning and we'll count it +and divide, and then we'll hunt up a place out in the woods for it +where it will be safe. Just you lay quiet here and watch the stuff till +I run and hook Benny Taylor's little wagon; I won't be gone a minute." + +He disappeared, and presently returned with the wagon, put the two +small sacks into it, threw some old rags on top of them, and started +off, dragging his cargo behind him. When the boys reached the +Welshman's house, they stopped to rest. Just as they were about to move +on, the Welshman stepped out and said: + +"Hallo, who's that?" + +"Huck and Tom Sawyer." + +"Good! Come along with me, boys, you are keeping everybody waiting. +Here--hurry up, trot ahead--I'll haul the wagon for you. Why, it's not +as light as it might be. Got bricks in it?--or old metal?" + +"Old metal," said Tom. + +"I judged so; the boys in this town will take more trouble and fool +away more time hunting up six bits' worth of old iron to sell to the +foundry than they would to make twice the money at regular work. But +that's human nature--hurry along, hurry along!" + +The boys wanted to know what the hurry was about. + +"Never mind; you'll see, when we get to the Widow Douglas'." + +Huck said with some apprehension--for he was long used to being +falsely accused: + +"Mr. Jones, we haven't been doing nothing." + +The Welshman laughed. + +"Well, I don't know, Huck, my boy. I don't know about that. Ain't you +and the widow good friends?" + +"Yes. Well, she's ben good friends to me, anyway." + +"All right, then. What do you want to be afraid for?" + +This question was not entirely answered in Huck's slow mind before he +found himself pushed, along with Tom, into Mrs. Douglas' drawing-room. +Mr. Jones left the wagon near the door and followed. + +The place was grandly lighted, and everybody that was of any +consequence in the village was there. The Thatchers were there, the +Harpers, the Rogerses, Aunt Polly, Sid, Mary, the minister, the editor, +and a great many more, and all dressed in their best. The widow +received the boys as heartily as any one could well receive two such +looking beings. They were covered with clay and candle-grease. Aunt +Polly blushed crimson with humiliation, and frowned and shook her head +at Tom. Nobody suffered half as much as the two boys did, however. Mr. +Jones said: + +"Tom wasn't at home, yet, so I gave him up; but I stumbled on him and +Huck right at my door, and so I just brought them along in a hurry." + +"And you did just right," said the widow. "Come with me, boys." + +She took them to a bedchamber and said: + +"Now wash and dress yourselves. Here are two new suits of clothes +--shirts, socks, everything complete. They're Huck's--no, no thanks, +Huck--Mr. Jones bought one and I the other. But they'll fit both of you. +Get into them. We'll wait--come down when you are slicked up enough." + +Then she left. + + + +CHAPTER XXXIV + +HUCK said: "Tom, we can slope, if we can find a rope. The window ain't +high from the ground." + +"Shucks! what do you want to slope for?" + +"Well, I ain't used to that kind of a crowd. I can't stand it. I ain't +going down there, Tom." + +"Oh, bother! It ain't anything. I don't mind it a bit. I'll take care +of you." + +Sid appeared. + +"Tom," said he, "auntie has been waiting for you all the afternoon. +Mary got your Sunday clothes ready, and everybody's been fretting about +you. Say--ain't this grease and clay, on your clothes?" + +"Now, Mr. Siddy, you jist 'tend to your own business. What's all this +blow-out about, anyway?" + +"It's one of the widow's parties that she's always having. This time +it's for the Welshman and his sons, on account of that scrape they +helped her out of the other night. And say--I can tell you something, +if you want to know." + +"Well, what?" + +"Why, old Mr. Jones is going to try to spring something on the people +here to-night, but I overheard him tell auntie to-day about it, as a +secret, but I reckon it's not much of a secret now. Everybody knows +--the widow, too, for all she tries to let on she don't. Mr. Jones was +bound Huck should be here--couldn't get along with his grand secret +without Huck, you know!" + +"Secret about what, Sid?" + +"About Huck tracking the robbers to the widow's. I reckon Mr. Jones +was going to make a grand time over his surprise, but I bet you it will +drop pretty flat." + +Sid chuckled in a very contented and satisfied way. + +"Sid, was it you that told?" + +"Oh, never mind who it was. SOMEBODY told--that's enough." + +"Sid, there's only one person in this town mean enough to do that, and +that's you. If you had been in Huck's place you'd 'a' sneaked down the +hill and never told anybody on the robbers. You can't do any but mean +things, and you can't bear to see anybody praised for doing good ones. +There--no thanks, as the widow says"--and Tom cuffed Sid's ears and +helped him to the door with several kicks. "Now go and tell auntie if +you dare--and to-morrow you'll catch it!" + +Some minutes later the widow's guests were at the supper-table, and a +dozen children were propped up at little side-tables in the same room, +after the fashion of that country and that day. At the proper time Mr. +Jones made his little speech, in which he thanked the widow for the +honor she was doing himself and his sons, but said that there was +another person whose modesty-- + +And so forth and so on. He sprung his secret about Huck's share in the +adventure in the finest dramatic manner he was master of, but the +surprise it occasioned was largely counterfeit and not as clamorous and +effusive as it might have been under happier circumstances. However, +the widow made a pretty fair show of astonishment, and heaped so many +compliments and so much gratitude upon Huck that he almost forgot the +nearly intolerable discomfort of his new clothes in the entirely +intolerable discomfort of being set up as a target for everybody's gaze +and everybody's laudations. + +The widow said she meant to give Huck a home under her roof and have +him educated; and that when she could spare the money she would start +him in business in a modest way. Tom's chance was come. He said: + +"Huck don't need it. Huck's rich." + +Nothing but a heavy strain upon the good manners of the company kept +back the due and proper complimentary laugh at this pleasant joke. But +the silence was a little awkward. Tom broke it: + +"Huck's got money. Maybe you don't believe it, but he's got lots of +it. Oh, you needn't smile--I reckon I can show you. You just wait a +minute." + +Tom ran out of doors. The company looked at each other with a +perplexed interest--and inquiringly at Huck, who was tongue-tied. + +"Sid, what ails Tom?" said Aunt Polly. "He--well, there ain't ever any +making of that boy out. I never--" + +Tom entered, struggling with the weight of his sacks, and Aunt Polly +did not finish her sentence. Tom poured the mass of yellow coin upon +the table and said: + +"There--what did I tell you? Half of it's Huck's and half of it's mine!" + +The spectacle took the general breath away. All gazed, nobody spoke +for a moment. Then there was a unanimous call for an explanation. Tom +said he could furnish it, and he did. The tale was long, but brimful of +interest. There was scarcely an interruption from any one to break the +charm of its flow. When he had finished, Mr. Jones said: + +"I thought I had fixed up a little surprise for this occasion, but it +don't amount to anything now. This one makes it sing mighty small, I'm +willing to allow." + +The money was counted. The sum amounted to a little over twelve +thousand dollars. It was more than any one present had ever seen at one +time before, though several persons were there who were worth +considerably more than that in property. + + + +CHAPTER XXXV + +THE reader may rest satisfied that Tom's and Huck's windfall made a +mighty stir in the poor little village of St. Petersburg. So vast a +sum, all in actual cash, seemed next to incredible. It was talked +about, gloated over, glorified, until the reason of many of the +citizens tottered under the strain of the unhealthy excitement. Every +"haunted" house in St. Petersburg and the neighboring villages was +dissected, plank by plank, and its foundations dug up and ransacked for +hidden treasure--and not by boys, but men--pretty grave, unromantic +men, too, some of them. Wherever Tom and Huck appeared they were +courted, admired, stared at. The boys were not able to remember that +their remarks had possessed weight before; but now their sayings were +treasured and repeated; everything they did seemed somehow to be +regarded as remarkable; they had evidently lost the power of doing and +saying commonplace things; moreover, their past history was raked up +and discovered to bear marks of conspicuous originality. The village +paper published biographical sketches of the boys. + +The Widow Douglas put Huck's money out at six per cent., and Judge +Thatcher did the same with Tom's at Aunt Polly's request. Each lad had +an income, now, that was simply prodigious--a dollar for every week-day +in the year and half of the Sundays. It was just what the minister got +--no, it was what he was promised--he generally couldn't collect it. A +dollar and a quarter a week would board, lodge, and school a boy in +those old simple days--and clothe him and wash him, too, for that +matter. + +Judge Thatcher had conceived a great opinion of Tom. He said that no +commonplace boy would ever have got his daughter out of the cave. When +Becky told her father, in strict confidence, how Tom had taken her +whipping at school, the Judge was visibly moved; and when she pleaded +grace for the mighty lie which Tom had told in order to shift that +whipping from her shoulders to his own, the Judge said with a fine +outburst that it was a noble, a generous, a magnanimous lie--a lie that +was worthy to hold up its head and march down through history breast to +breast with George Washington's lauded Truth about the hatchet! Becky +thought her father had never looked so tall and so superb as when he +walked the floor and stamped his foot and said that. She went straight +off and told Tom about it. + +Judge Thatcher hoped to see Tom a great lawyer or a great soldier some +day. He said he meant to look to it that Tom should be admitted to the +National Military Academy and afterward trained in the best law school +in the country, in order that he might be ready for either career or +both. + +Huck Finn's wealth and the fact that he was now under the Widow +Douglas' protection introduced him into society--no, dragged him into +it, hurled him into it--and his sufferings were almost more than he +could bear. The widow's servants kept him clean and neat, combed and +brushed, and they bedded him nightly in unsympathetic sheets that had +not one little spot or stain which he could press to his heart and know +for a friend. He had to eat with a knife and fork; he had to use +napkin, cup, and plate; he had to learn his book, he had to go to +church; he had to talk so properly that speech was become insipid in +his mouth; whithersoever he turned, the bars and shackles of +civilization shut him in and bound him hand and foot. + +He bravely bore his miseries three weeks, and then one day turned up +missing. For forty-eight hours the widow hunted for him everywhere in +great distress. The public were profoundly concerned; they searched +high and low, they dragged the river for his body. Early the third +morning Tom Sawyer wisely went poking among some old empty hogsheads +down behind the abandoned slaughter-house, and in one of them he found +the refugee. Huck had slept there; he had just breakfasted upon some +stolen odds and ends of food, and was lying off, now, in comfort, with +his pipe. He was unkempt, uncombed, and clad in the same old ruin of +rags that had made him picturesque in the days when he was free and +happy. Tom routed him out, told him the trouble he had been causing, +and urged him to go home. Huck's face lost its tranquil content, and +took a melancholy cast. He said: + +"Don't talk about it, Tom. I've tried it, and it don't work; it don't +work, Tom. It ain't for me; I ain't used to it. The widder's good to +me, and friendly; but I can't stand them ways. She makes me get up just +at the same time every morning; she makes me wash, they comb me all to +thunder; she won't let me sleep in the woodshed; I got to wear them +blamed clothes that just smothers me, Tom; they don't seem to any air +git through 'em, somehow; and they're so rotten nice that I can't set +down, nor lay down, nor roll around anywher's; I hain't slid on a +cellar-door for--well, it 'pears to be years; I got to go to church and +sweat and sweat--I hate them ornery sermons! I can't ketch a fly in +there, I can't chaw. I got to wear shoes all Sunday. The widder eats by +a bell; she goes to bed by a bell; she gits up by a bell--everything's +so awful reg'lar a body can't stand it." + +"Well, everybody does that way, Huck." + +"Tom, it don't make no difference. I ain't everybody, and I can't +STAND it. It's awful to be tied up so. And grub comes too easy--I don't +take no interest in vittles, that way. I got to ask to go a-fishing; I +got to ask to go in a-swimming--dern'd if I hain't got to ask to do +everything. Well, I'd got to talk so nice it wasn't no comfort--I'd got +to go up in the attic and rip out awhile, every day, to git a taste in +my mouth, or I'd a died, Tom. The widder wouldn't let me smoke; she +wouldn't let me yell, she wouldn't let me gape, nor stretch, nor +scratch, before folks--" [Then with a spasm of special irritation and +injury]--"And dad fetch it, she prayed all the time! I never see such a +woman! I HAD to shove, Tom--I just had to. And besides, that school's +going to open, and I'd a had to go to it--well, I wouldn't stand THAT, +Tom. Looky here, Tom, being rich ain't what it's cracked up to be. It's +just worry and worry, and sweat and sweat, and a-wishing you was dead +all the time. Now these clothes suits me, and this bar'l suits me, and +I ain't ever going to shake 'em any more. Tom, I wouldn't ever got into +all this trouble if it hadn't 'a' ben for that money; now you just take +my sheer of it along with your'n, and gimme a ten-center sometimes--not +many times, becuz I don't give a dern for a thing 'thout it's tollable +hard to git--and you go and beg off for me with the widder." + +"Oh, Huck, you know I can't do that. 'Tain't fair; and besides if +you'll try this thing just a while longer you'll come to like it." + +"Like it! Yes--the way I'd like a hot stove if I was to set on it long +enough. No, Tom, I won't be rich, and I won't live in them cussed +smothery houses. I like the woods, and the river, and hogsheads, and +I'll stick to 'em, too. Blame it all! just as we'd got guns, and a +cave, and all just fixed to rob, here this dern foolishness has got to +come up and spile it all!" + +Tom saw his opportunity-- + +"Lookyhere, Huck, being rich ain't going to keep me back from turning +robber." + +"No! Oh, good-licks; are you in real dead-wood earnest, Tom?" + +"Just as dead earnest as I'm sitting here. But Huck, we can't let you +into the gang if you ain't respectable, you know." + +Huck's joy was quenched. + +"Can't let me in, Tom? Didn't you let me go for a pirate?" + +"Yes, but that's different. A robber is more high-toned than what a +pirate is--as a general thing. In most countries they're awful high up +in the nobility--dukes and such." + +"Now, Tom, hain't you always ben friendly to me? You wouldn't shet me +out, would you, Tom? You wouldn't do that, now, WOULD you, Tom?" + +"Huck, I wouldn't want to, and I DON'T want to--but what would people +say? Why, they'd say, 'Mph! Tom Sawyer's Gang! pretty low characters in +it!' They'd mean you, Huck. You wouldn't like that, and I wouldn't." + +Huck was silent for some time, engaged in a mental struggle. Finally +he said: + +"Well, I'll go back to the widder for a month and tackle it and see if +I can come to stand it, if you'll let me b'long to the gang, Tom." + +"All right, Huck, it's a whiz! Come along, old chap, and I'll ask the +widow to let up on you a little, Huck." + +"Will you, Tom--now will you? That's good. If she'll let up on some of +the roughest things, I'll smoke private and cuss private, and crowd +through or bust. When you going to start the gang and turn robbers?" + +"Oh, right off. We'll get the boys together and have the initiation +to-night, maybe." + +"Have the which?" + +"Have the initiation." + +"What's that?" + +"It's to swear to stand by one another, and never tell the gang's +secrets, even if you're chopped all to flinders, and kill anybody and +all his family that hurts one of the gang." + +"That's gay--that's mighty gay, Tom, I tell you." + +"Well, I bet it is. And all that swearing's got to be done at +midnight, in the lonesomest, awfulest place you can find--a ha'nted +house is the best, but they're all ripped up now." + +"Well, midnight's good, anyway, Tom." + +"Yes, so it is. And you've got to swear on a coffin, and sign it with +blood." + +"Now, that's something LIKE! Why, it's a million times bullier than +pirating. I'll stick to the widder till I rot, Tom; and if I git to be +a reg'lar ripper of a robber, and everybody talking 'bout it, I reckon +she'll be proud she snaked me in out of the wet." + + + +CONCLUSION + +SO endeth this chronicle. It being strictly a history of a BOY, it +must stop here; the story could not go much further without becoming +the history of a MAN. When one writes a novel about grown people, he +knows exactly where to stop--that is, with a marriage; but when he +writes of juveniles, he must stop where he best can. + +Most of the characters that perform in this book still live, and are +prosperous and happy. Some day it may seem worth while to take up the +story of the younger ones again and see what sort of men and women they +turned out to be; therefore it will be wisest not to reveal any of that +part of their lives at present. + + + + + +End of the Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete +by Mark Twain (Samuel Clemens) + +*** END OF THIS PROJECT GUTENBERG EBOOK TOM SAWYER *** + +***** This file should be named 74.txt or 74.zip ***** +This and all associated files of various formats will be found in: + http://www.gutenberg.net/7/74/ + +Produced by David Widger. The previous edition was update by Jose +Menendez. + + +Updated editions will replace the previous one--the old editions +will be renamed. + +Creating the works from public domain print editions means that no +one owns a United States copyright in these works, so the Foundation +(and you!) can copy and distribute it in the United States without +permission and without paying copyright royalties. Special rules, +set forth in the General Terms of Use part of this license, apply to +copying and distributing Project Gutenberg-tm electronic works to +protect the PROJECT GUTENBERG-tm concept and trademark. Project +Gutenberg is a registered trademark, and may not be used if you +charge for the eBooks, unless you receive specific permission. If you +do not charge anything for copies of this eBook, complying with the +rules is very easy. You may use this eBook for nearly any purpose +such as creation of derivative works, reports, performances and +research. They may be modified and printed and given away--you may do +practically ANYTHING with public domain eBooks. Redistribution is +subject to the trademark license, especially commercial +redistribution. + + + +*** START: FULL LICENSE *** + +THE FULL PROJECT GUTENBERG LICENSE +PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK + +To protect the Project Gutenberg-tm mission of promoting the free +distribution of electronic works, by using or distributing this work +(or any other work associated in any way with the phrase "Project +Gutenberg"), you agree to comply with all the terms of the Full Project +Gutenberg-tm License (available with this file or online at +http://gutenberg.net/license). + + +Section 1. General Terms of Use and Redistributing Project Gutenberg-tm +electronic works + +1.A. By reading or using any part of this Project Gutenberg-tm +electronic work, you indicate that you have read, understand, agree to +and accept all the terms of this license and intellectual property +(trademark/copyright) agreement. If you do not agree to abide by all +the terms of this agreement, you must cease using and return or destroy +all copies of Project Gutenberg-tm electronic works in your possession. +If you paid a fee for obtaining a copy of or access to a Project +Gutenberg-tm electronic work and you do not agree to be bound by the +terms of this agreement, you may obtain a refund from the person or +entity to whom you paid the fee as set forth in paragraph 1.E.8. + +1.B. "Project Gutenberg" is a registered trademark. It may only be +used on or associated in any way with an electronic work by people who +agree to be bound by the terms of this agreement. There are a few +things that you can do with most Project Gutenberg-tm electronic works +even without complying with the full terms of this agreement. See +paragraph 1.C below. There are a lot of things you can do with Project +Gutenberg-tm electronic works if you follow the terms of this agreement +and help preserve free future access to Project Gutenberg-tm electronic +works. See paragraph 1.E below. + +1.C. The Project Gutenberg Literary Archive Foundation ("the Foundation" +or PGLAF), owns a compilation copyright in the collection of Project +Gutenberg-tm electronic works. Nearly all the individual works in the +collection are in the public domain in the United States. If an +individual work is in the public domain in the United States and you are +located in the United States, we do not claim a right to prevent you from +copying, distributing, performing, displaying or creating derivative +works based on the work as long as all references to Project Gutenberg +are removed. Of course, we hope that you will support the Project +Gutenberg-tm mission of promoting free access to electronic works by +freely sharing Project Gutenberg-tm works in compliance with the terms of +this agreement for keeping the Project Gutenberg-tm name associated with +the work. You can easily comply with the terms of this agreement by +keeping this work in the same format with its attached full Project +Gutenberg-tm License when you share it without charge with others. + +1.D. The copyright laws of the place where you are located also govern +what you can do with this work. Copyright laws in most countries are in +a constant state of change. If you are outside the United States, check +the laws of your country in addition to the terms of this agreement +before downloading, copying, displaying, performing, distributing or +creating derivative works based on this work or any other Project +Gutenberg-tm work. The Foundation makes no representations concerning +the copyright status of any work in any country outside the United +States. + +1.E. Unless you have removed all references to Project Gutenberg: + +1.E.1. The following sentence, with active links to, or other immediate +access to, the full Project Gutenberg-tm License must appear prominently +whenever any copy of a Project Gutenberg-tm work (any work on which the +phrase "Project Gutenberg" appears, or with which the phrase "Project +Gutenberg" is associated) is accessed, displayed, performed, viewed, +copied or distributed: + +This eBook is for the use of anyone anywhere at no cost and with +almost no restrictions whatsoever. You may copy it, give it away or +re-use it under the terms of the Project Gutenberg License included +with this eBook or online at www.gutenberg.net + +1.E.2. If an individual Project Gutenberg-tm electronic work is derived +from the public domain (does not contain a notice indicating that it is +posted with permission of the copyright holder), the work can be copied +and distributed to anyone in the United States without paying any fees +or charges. If you are redistributing or providing access to a work +with the phrase "Project Gutenberg" associated with or appearing on the +work, you must comply either with the requirements of paragraphs 1.E.1 +through 1.E.7 or obtain permission for the use of the work and the +Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or +1.E.9. + +1.E.3. If an individual Project Gutenberg-tm electronic work is posted +with the permission of the copyright holder, your use and distribution +must comply with both paragraphs 1.E.1 through 1.E.7 and any additional +terms imposed by the copyright holder. Additional terms will be linked +to the Project Gutenberg-tm License for all works posted with the +permission of the copyright holder found at the beginning of this work. + +1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm +License terms from this work, or any files containing a part of this +work or any other work associated with Project Gutenberg-tm. + +1.E.5. Do not copy, display, perform, distribute or redistribute this +electronic work, or any part of this electronic work, without +prominently displaying the sentence set forth in paragraph 1.E.1 with +active links or immediate access to the full terms of the Project +Gutenberg-tm License. + +1.E.6. You may convert to and distribute this work in any binary, +compressed, marked up, nonproprietary or proprietary form, including any +word processing or hypertext form. However, if you provide access to or +distribute copies of a Project Gutenberg-tm work in a format other than +"Plain Vanilla ASCII" or other format used in the official version +posted on the official Project Gutenberg-tm web site (www.gutenberg.net), +you must, at no additional cost, fee or expense to the user, provide a +copy, a means of exporting a copy, or a means of obtaining a copy upon +request, of the work in its original "Plain Vanilla ASCII" or other +form. Any alternate format must include the full Project Gutenberg-tm +License as specified in paragraph 1.E.1. + +1.E.7. Do not charge a fee for access to, viewing, displaying, +performing, copying or distributing any Project Gutenberg-tm works +unless you comply with paragraph 1.E.8 or 1.E.9. + +1.E.8. You may charge a reasonable fee for copies of or providing +access to or distributing Project Gutenberg-tm electronic works provided +that + +- You pay a royalty fee of 20% of the gross profits you derive from + the use of Project Gutenberg-tm works calculated using the method + you already use to calculate your applicable taxes. The fee is + owed to the owner of the Project Gutenberg-tm trademark, but he + has agreed to donate royalties under this paragraph to the + Project Gutenberg Literary Archive Foundation. Royalty payments + must be paid within 60 days following each date on which you + prepare (or are legally required to prepare) your periodic tax + returns. Royalty payments should be clearly marked as such and + sent to the Project Gutenberg Literary Archive Foundation at the + address specified in Section 4, "Information about donations to + the Project Gutenberg Literary Archive Foundation." + +- You provide a full refund of any money paid by a user who notifies + you in writing (or by e-mail) within 30 days of receipt that s/he + does not agree to the terms of the full Project Gutenberg-tm + License. You must require such a user to return or + destroy all copies of the works possessed in a physical medium + and discontinue all use of and all access to other copies of + Project Gutenberg-tm works. + +- You provide, in accordance with paragraph 1.F.3, a full refund of any + money paid for a work or a replacement copy, if a defect in the + electronic work is discovered and reported to you within 90 days + of receipt of the work. + +- You comply with all other terms of this agreement for free + distribution of Project Gutenberg-tm works. + +1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm +electronic work or group of works on different terms than are set +forth in this agreement, you must obtain permission in writing from +both the Project Gutenberg Literary Archive Foundation and Michael +Hart, the owner of the Project Gutenberg-tm trademark. Contact the +Foundation as set forth in Section 3 below. + +1.F. + +1.F.1. Project Gutenberg volunteers and employees expend considerable +effort to identify, do copyright research on, transcribe and proofread +public domain works in creating the Project Gutenberg-tm +collection. Despite these efforts, Project Gutenberg-tm electronic +works, and the medium on which they may be stored, may contain +"Defects," such as, but not limited to, incomplete, inaccurate or +corrupt data, transcription errors, a copyright or other intellectual +property infringement, a defective or damaged disk or other medium, a +computer virus, or computer codes that damage or cannot be read by +your equipment. + +1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the "Right +of Replacement or Refund" described in paragraph 1.F.3, the Project +Gutenberg Literary Archive Foundation, the owner of the Project +Gutenberg-tm trademark, and any other party distributing a Project +Gutenberg-tm electronic work under this agreement, disclaim all +liability to you for damages, costs and expenses, including legal +fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT +LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE +PROVIDED IN PARAGRAPH F3. YOU AGREE THAT THE FOUNDATION, THE +TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE +LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR +INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH +DAMAGE. + +1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a +defect in this electronic work within 90 days of receiving it, you can +receive a refund of the money (if any) you paid for it by sending a +written explanation to the person you received the work from. If you +received the work on a physical medium, you must return the medium with +your written explanation. The person or entity that provided you with +the defective work may elect to provide a replacement copy in lieu of a +refund. If you received the work electronically, the person or entity +providing it to you may choose to give you a second opportunity to +receive the work electronically in lieu of a refund. If the second copy +is also defective, you may demand a refund in writing without further +opportunities to fix the problem. + +1.F.4. Except for the limited right of replacement or refund set forth +in paragraph 1.F.3, this work is provided to you 'AS-IS' WITH NO OTHER +WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR ANY PURPOSE. + +1.F.5. Some states do not allow disclaimers of certain implied +warranties or the exclusion or limitation of certain types of damages. +If any disclaimer or limitation set forth in this agreement violates the +law of the state applicable to this agreement, the agreement shall be +interpreted to make the maximum disclaimer or limitation permitted by +the applicable state law. The invalidity or unenforceability of any +provision of this agreement shall not void the remaining provisions. + +1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the +trademark owner, any agent or employee of the Foundation, anyone +providing copies of Project Gutenberg-tm electronic works in accordance +with this agreement, and any volunteers associated with the production, +promotion and distribution of Project Gutenberg-tm electronic works, +harmless from all liability, costs and expenses, including legal fees, +that arise directly or indirectly from any of the following which you do +or cause to occur: (a) distribution of this or any Project Gutenberg-tm +work, (b) alteration, modification, or additions or deletions to any +Project Gutenberg-tm work, and (c) any Defect you cause. + + +Section 2. Information about the Mission of Project Gutenberg-tm + +Project Gutenberg-tm is synonymous with the free distribution of +electronic works in formats readable by the widest variety of computers +including obsolete, old, middle-aged and new computers. It exists +because of the efforts of hundreds of volunteers and donations from +people in all walks of life. + +Volunteers and financial support to provide volunteers with the +assistance they need, is critical to reaching Project Gutenberg-tm's +goals and ensuring that the Project Gutenberg-tm collection will +remain freely available for generations to come. In 2001, the Project +Gutenberg Literary Archive Foundation was created to provide a secure +and permanent future for Project Gutenberg-tm and future generations. +To learn more about the Project Gutenberg Literary Archive Foundation +and how your efforts and donations can help, see Sections 3 and 4 +and the Foundation web page at http://www.pglaf.org. + + +Section 3. Information about the Project Gutenberg Literary Archive +Foundation + +The Project Gutenberg Literary Archive Foundation is a non profit +501(c)(3) educational corporation organized under the laws of the +state of Mississippi and granted tax exempt status by the Internal +Revenue Service. The Foundation's EIN or federal tax identification +number is 64-6221541. Its 501(c)(3) letter is posted at +http://pglaf.org/fundraising. Contributions to the Project Gutenberg +Literary Archive Foundation are tax deductible to the full extent +permitted by U.S. federal laws and your state's laws. + +The Foundation's principal office is located at 4557 Melan Dr. S. +Fairbanks, AK, 99712., but its volunteers and employees are scattered +throughout numerous locations. Its business office is located at +809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887, email +business@pglaf.org. Email contact links and up to date contact +information can be found at the Foundation's web site and official +page at http://pglaf.org + +For additional contact information: + Dr. Gregory B. Newby + Chief Executive and Director + gbnewby@pglaf.org + + +Section 4. Information about Donations to the Project Gutenberg +Literary Archive Foundation + +Project Gutenberg-tm depends upon and cannot survive without wide +spread public support and donations to carry out its mission of +increasing the number of public domain and licensed works that can be +freely distributed in machine readable form accessible by the widest +array of equipment including outdated equipment. Many small donations +($1 to $5,000) are particularly important to maintaining tax exempt +status with the IRS. + +The Foundation is committed to complying with the laws regulating +charities and charitable donations in all 50 states of the United +States. Compliance requirements are not uniform and it takes a +considerable effort, much paperwork and many fees to meet and keep up +with these requirements. We do not solicit donations in locations +where we have not received written confirmation of compliance. To +SEND DONATIONS or determine the status of compliance for any +particular state visit http://pglaf.org + +While we cannot and do not solicit contributions from states where we +have not met the solicitation requirements, we know of no prohibition +against accepting unsolicited donations from donors in such states who +approach us with offers to donate. + +International donations are gratefully accepted, but we cannot make +any statements concerning tax treatment of donations received from +outside the United States. U.S. laws alone swamp our small staff. + +Please check the Project Gutenberg Web pages for current donation +methods and addresses. Donations are accepted in a number of other +ways including including checks, online payments and credit card +donations. To donate, please visit: http://pglaf.org/donate + + +Section 5. General Information About Project Gutenberg-tm electronic +works. + +Professor Michael S. Hart is the originator of the Project Gutenberg-tm +concept of a library of electronic works that could be freely shared +with anyone. For thirty years, he produced and distributed Project +Gutenberg-tm eBooks with only a loose network of volunteer support. + + +Project Gutenberg-tm eBooks are often created from several printed +editions, all of which are confirmed as Public Domain in the U.S. +unless a copyright notice is included. Thus, we do not necessarily +keep eBooks in compliance with any particular paper edition. + + +Most people start at our Web site which has the main PG search facility: + + http://www.gutenberg.net + +This Web site includes information about Project Gutenberg-tm, +including how to make donations to the Project Gutenberg Literary +Archive Foundation, how to help produce our new eBooks, and how to +subscribe to our email newsletter to hear about new eBooks. diff --git a/src/pkg/compress/zlib/Makefile b/src/pkg/compress/zlib/Makefile deleted file mode 100644 index 791072d34..000000000 --- a/src/pkg/compress/zlib/Makefile +++ /dev/null @@ -1,12 +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 ../../../Make.inc - -TARG=compress/zlib -GOFILES=\ - reader.go\ - writer.go\ - -include ../../../Make.pkg diff --git a/src/pkg/compress/zlib/reader.go b/src/pkg/compress/zlib/reader.go index 78dabdf4d..f38ef5a88 100644 --- a/src/pkg/compress/zlib/reader.go +++ b/src/pkg/compress/zlib/reader.go @@ -26,36 +26,41 @@ package zlib import ( "bufio" "compress/flate" + "errors" "hash" "hash/adler32" "io" - "os" ) const zlibDeflate = 8 -var ChecksumError = os.NewError("zlib checksum error") -var HeaderError = os.NewError("invalid zlib header") -var DictionaryError = os.NewError("invalid zlib dictionary") +var ( + // ErrChecksum is returned when reading ZLIB data that has an invalid checksum. + ErrChecksum = errors.New("zlib: invalid checksum") + // ErrDictionary is returned when reading ZLIB data that has an invalid dictionary. + ErrDictionary = errors.New("zlib: invalid dictionary") + // ErrHeader is returned when reading ZLIB data that has an invalid header. + ErrHeader = errors.New("zlib: invalid header") +) type reader struct { r flate.Reader decompressor io.ReadCloser digest hash.Hash32 - err os.Error + err error scratch [4]byte } // NewReader creates a new io.ReadCloser that satisfies reads by decompressing data read from r. // The implementation buffers input and may read more data than necessary from r. // It is the caller's responsibility to call Close on the ReadCloser when done. -func NewReader(r io.Reader) (io.ReadCloser, os.Error) { +func NewReader(r io.Reader) (io.ReadCloser, error) { return NewReaderDict(r, nil) } // NewReaderDict is like NewReader but uses a preset dictionary. // NewReaderDict ignores the dictionary if the compressed data does not refer to it. -func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, os.Error) { +func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, error) { z := new(reader) if fr, ok := r.(flate.Reader); ok { z.r = fr @@ -68,7 +73,7 @@ func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, os.Error) { } h := uint(z.scratch[0])<<8 | uint(z.scratch[1]) if (z.scratch[0]&0x0f != zlibDeflate) || (h%31 != 0) { - return nil, HeaderError + return nil, ErrHeader } if z.scratch[1]&0x20 != 0 { _, err = io.ReadFull(z.r, z.scratch[0:4]) @@ -77,7 +82,7 @@ func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, os.Error) { } checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3]) if checksum != adler32.Checksum(dict) { - return nil, DictionaryError + return nil, ErrDictionary } z.decompressor = flate.NewReaderDict(z.r, dict) } else { @@ -87,7 +92,7 @@ func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, os.Error) { return z, nil } -func (z *reader) Read(p []byte) (n int, err os.Error) { +func (z *reader) Read(p []byte) (n int, err error) { if z.err != nil { return 0, z.err } @@ -97,7 +102,7 @@ func (z *reader) Read(p []byte) (n int, err os.Error) { n, err = z.decompressor.Read(p) z.digest.Write(p[0:n]) - if n != 0 || err != os.EOF { + if n != 0 || err != io.EOF { z.err = err return } @@ -110,14 +115,14 @@ func (z *reader) Read(p []byte) (n int, err os.Error) { // ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952). checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3]) if checksum != z.digest.Sum32() { - z.err = ChecksumError + z.err = ErrChecksum return 0, z.err } return } // Calling Close does not close the wrapped io.Reader originally passed to NewReader. -func (z *reader) Close() os.Error { +func (z *reader) Close() error { if z.err != nil { return z.err } diff --git a/src/pkg/compress/zlib/reader_test.go b/src/pkg/compress/zlib/reader_test.go index 195db446c..3b02a0868 100644 --- a/src/pkg/compress/zlib/reader_test.go +++ b/src/pkg/compress/zlib/reader_test.go @@ -7,7 +7,6 @@ package zlib import ( "bytes" "io" - "os" "testing" ) @@ -16,7 +15,7 @@ type zlibTest struct { raw string compressed []byte dict []byte - err os.Error + err error } // Compare-to-golden test data was generated by the ZLIB example program at @@ -46,14 +45,14 @@ var zlibTests = []zlibTest{ "", []byte{0x78, 0x9f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01}, nil, - HeaderError, + ErrHeader, }, { "bad checksum", "", []byte{0x78, 0x9c, 0x03, 0x00, 0x00, 0x00, 0x00, 0xff}, nil, - ChecksumError, + ErrChecksum, }, { "not enough data", @@ -96,7 +95,7 @@ var zlibTests = []zlibTest{ []byte{ 0x48, 0x65, 0x6c, 0x6c, }, - DictionaryError, + ErrDictionary, }, } diff --git a/src/pkg/compress/zlib/writer.go b/src/pkg/compress/zlib/writer.go index 8f86e9c4c..cd8dea460 100644 --- a/src/pkg/compress/zlib/writer.go +++ b/src/pkg/compress/zlib/writer.go @@ -6,10 +6,10 @@ package zlib import ( "compress/flate" + "fmt" "hash" "hash/adler32" "io" - "os" ) // These constants are copied from the flate package, so that code that imports @@ -24,30 +24,55 @@ const ( // A Writer takes data written to it and writes the compressed // form of that data to an underlying writer (see NewWriter). type Writer struct { - w io.Writer - compressor *flate.Writer - digest hash.Hash32 - err os.Error - scratch [4]byte + w io.Writer + level int + dict []byte + compressor *flate.Writer + digest hash.Hash32 + err error + scratch [4]byte + wroteHeader bool } -// NewWriter calls NewWriterLevel with the default compression level. -func NewWriter(w io.Writer) (*Writer, os.Error) { - return NewWriterLevel(w, DefaultCompression) +// NewWriter creates a new Writer that satisfies writes by compressing data +// written to w. +// +// It is the caller's responsibility to call Close on the WriteCloser when done. +// Writes may be buffered and not flushed until Close. +func NewWriter(w io.Writer) *Writer { + z, _ := NewWriterLevelDict(w, DefaultCompression, nil) + return z } -// NewWriterLevel calls NewWriterDict with no dictionary. -func NewWriterLevel(w io.Writer, level int) (*Writer, os.Error) { - return NewWriterDict(w, level, nil) +// NewWriterLevel is like NewWriter but specifies the compression level instead +// of assuming DefaultCompression. +// +// The compression level can be DefaultCompression, NoCompression, or any +// integer value between BestSpeed and BestCompression inclusive. The error +// returned will be nil if the level is valid. +func NewWriterLevel(w io.Writer, level int) (*Writer, error) { + return NewWriterLevelDict(w, level, nil) } -// NewWriterDict creates a new io.WriteCloser that satisfies writes by compressing data written to w. -// It is the caller's responsibility to call Close on the WriteCloser when done. -// level is the compression level, which can be DefaultCompression, NoCompression, -// or any integer value between BestSpeed and BestCompression (inclusive). -// dict is the preset dictionary to compress with, or nil to use no dictionary. -func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, os.Error) { - z := new(Writer) +// NewWriterLevelDict is like NewWriterLevel but specifies a dictionary to +// compress with. +// +// The dictionary may be nil. If not, its contents should not be modified until +// the Writer is closed. +func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error) { + if level < DefaultCompression || level > BestCompression { + return nil, fmt.Errorf("zlib: invalid compression level: %d", level) + } + return &Writer{ + w: w, + level: level, + dict: dict, + }, nil +} + +// writeHeader writes the ZLIB header. +func (z *Writer) writeHeader() (err error) { + z.wroteHeader = true // ZLIB has a two-byte header (as documented in RFC 1950). // The first four bits is the CINFO (compression info), which is 7 for the default deflate window size. // The next four bits is the CM (compression method), which is 8 for deflate. @@ -56,7 +81,7 @@ func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, os.Error) { // 0=fastest, 1=fast, 2=default, 3=best. // The next bit, FDICT, is set if a dictionary is given. // The final five FCHECK bits form a mod-31 checksum. - switch level { + switch z.level { case 0, 1: z.scratch[1] = 0 << 6 case 2, 3, 4, 5: @@ -66,35 +91,41 @@ func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, os.Error) { case 7, 8, 9: z.scratch[1] = 3 << 6 default: - return nil, os.NewError("level out of range") + panic("unreachable") } - if dict != nil { + if z.dict != nil { z.scratch[1] |= 1 << 5 } z.scratch[1] += uint8(31 - (uint16(z.scratch[0])<<8+uint16(z.scratch[1]))%31) - _, err := w.Write(z.scratch[0:2]) - if err != nil { - return nil, err + if _, err = z.w.Write(z.scratch[0:2]); err != nil { + return err } - if dict != nil { + if z.dict != nil { // The next four bytes are the Adler-32 checksum of the dictionary. - checksum := adler32.Checksum(dict) + checksum := adler32.Checksum(z.dict) z.scratch[0] = uint8(checksum >> 24) z.scratch[1] = uint8(checksum >> 16) z.scratch[2] = uint8(checksum >> 8) z.scratch[3] = uint8(checksum >> 0) - _, err = w.Write(z.scratch[0:4]) - if err != nil { - return nil, err + if _, err = z.w.Write(z.scratch[0:4]); err != nil { + return err } } - z.w = w - z.compressor = flate.NewWriterDict(w, level, dict) + z.compressor, err = flate.NewWriterDict(z.w, z.level, z.dict) + if err != nil { + return err + } z.digest = adler32.New() - return z, nil + return nil } -func (z *Writer) Write(p []byte) (n int, err os.Error) { +// Write writes a compressed form of p to the underlying io.Writer. The +// compressed bytes are not necessarily flushed until the Writer is closed or +// explicitly flushed. +func (z *Writer) Write(p []byte) (n int, err error) { + if !z.wroteHeader { + z.err = z.writeHeader() + } if z.err != nil { return 0, z.err } @@ -110,8 +141,11 @@ func (z *Writer) Write(p []byte) (n int, err os.Error) { return } -// Flush flushes the underlying compressor. -func (z *Writer) Flush() os.Error { +// Flush flushes the Writer to its underlying io.Writer. +func (z *Writer) Flush() error { + if !z.wroteHeader { + z.err = z.writeHeader() + } if z.err != nil { return z.err } @@ -120,7 +154,10 @@ func (z *Writer) Flush() os.Error { } // Calling Close does not close the wrapped io.Writer originally passed to NewWriter. -func (z *Writer) Close() os.Error { +func (z *Writer) Close() error { + if !z.wroteHeader { + z.err = z.writeHeader() + } if z.err != nil { return z.err } diff --git a/src/pkg/compress/zlib/writer_test.go b/src/pkg/compress/zlib/writer_test.go index 32f05ab68..aee1a5c2f 100644 --- a/src/pkg/compress/zlib/writer_test.go +++ b/src/pkg/compress/zlib/writer_test.go @@ -52,17 +52,13 @@ func testLevelDict(t *testing.T, fn string, b0 []byte, level int, d string) { defer piper.Close() go func() { defer pipew.Close() - zlibw, err := NewWriterDict(pipew, level, dict) + zlibw, err := NewWriterLevelDict(pipew, level, dict) if err != nil { t.Errorf("%s (level=%d, dict=%q): %v", fn, level, d, err) return } defer zlibw.Close() _, err = zlibw.Write(b0) - if err == os.EPIPE { - // Fail, but do not report the error, as some other (presumably reported) error broke the pipe. - return - } if err != nil { t.Errorf("%s (level=%d, dict=%q): %v", fn, level, d, err) return @@ -128,10 +124,10 @@ func TestWriterDict(t *testing.T) { func TestWriterDictIsUsed(t *testing.T) { var input = []byte("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") - buf := bytes.NewBuffer(nil) - compressor, err := NewWriterDict(buf, BestCompression, input) + var buf bytes.Buffer + compressor, err := NewWriterLevelDict(&buf, BestCompression, input) if err != nil { - t.Errorf("error in NewWriterDict: %s", err) + t.Errorf("error in NewWriterLevelDict: %s", err) return } compressor.Write(input) diff --git a/src/pkg/container/heap/Makefile b/src/pkg/container/heap/Makefile deleted file mode 100644 index 4291d1122..000000000 --- a/src/pkg/container/heap/Makefile +++ /dev/null @@ -1,11 +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 ../../../Make.inc - -TARG=container/heap -GOFILES=\ - heap.go\ - -include ../../../Make.pkg diff --git a/src/pkg/container/heap/example_test.go b/src/pkg/container/heap/example_test.go new file mode 100644 index 000000000..2050bc835 --- /dev/null +++ b/src/pkg/container/heap/example_test.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. + +// 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 2dfe5b43c..67018e6ba 100644 --- a/src/pkg/container/heap/heap.go +++ b/src/pkg/container/heap/heap.go @@ -3,7 +3,14 @@ // license that can be found in the LICENSE file. // Package heap provides heap operations for any type that implements -// heap.Interface. +// heap.Interface. A heap is a tree with the property that each node is the +// highest-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. // package heap @@ -11,14 +18,17 @@ import "sort" // Any type that implements heap.Interface may be used as a // min-heap with the following invariants (established after -// Init has been called): +// Init has been called or if the data is empty or sorted): // // !h.Less(j, i) for 0 <= i < h.Len() and j = 2*i+1 or 2*i+2 and j < h.Len() // +// Note that Push and Pop in this interface are for package heap's +// implementation to call. To add and remove things from the heap, +// use heap.Push and heap.Pop. type Interface interface { sort.Interface - Push(x interface{}) - Pop() interface{} + Push(x interface{}) // add x as element Len() + Pop() interface{} // remove and return element Len() - 1. } // A heap must be initialized before any of the heap operations diff --git a/src/pkg/container/heap/heap_test.go b/src/pkg/container/heap/heap_test.go index c5c1f76e1..cb31ef6d3 100644 --- a/src/pkg/container/heap/heap_test.go +++ b/src/pkg/container/heap/heap_test.go @@ -5,33 +5,47 @@ package heap_test import ( - "testing" - "container/vector" . "container/heap" + "testing" ) -type myHeap struct { - // A vector.Vector implements sort.Interface except for Less, - // and it implements Push and Pop as required for heap.Interface. - vector.Vector +type myHeap []int + +func (h *myHeap) Less(i, j int) bool { + return (*h)[i] < (*h)[j] +} + +func (h *myHeap) Swap(i, j int) { + (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } -func (h *myHeap) Less(i, j int) bool { return h.At(i).(int) < h.At(j).(int) } +func (h *myHeap) Len() int { + return len(*h) +} + +func (h *myHeap) Pop() (v interface{}) { + *h, v = (*h)[:h.Len()-1], (*h)[h.Len()-1] + return +} + +func (h *myHeap) Push(v interface{}) { + *h = append(*h, v.(int)) +} -func (h *myHeap) verify(t *testing.T, i int) { +func (h myHeap) verify(t *testing.T, i int) { n := h.Len() j1 := 2*i + 1 j2 := 2*i + 2 if j1 < n { if h.Less(j1, i) { - t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h.At(i), j1, h.At(j1)) + t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h[i], j1, h[j1]) return } h.verify(t, j1) } if j2 < n { if h.Less(j2, i) { - t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h.At(i), j1, h.At(j2)) + t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h[i], j1, h[j2]) return } h.verify(t, j2) diff --git a/src/pkg/container/list/Makefile b/src/pkg/container/list/Makefile deleted file mode 100644 index 7fcd5f99a..000000000 --- a/src/pkg/container/list/Makefile +++ /dev/null @@ -1,11 +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 ../../../Make.inc - -TARG=container/list -GOFILES=\ - list.go\ - -include ../../../Make.pkg diff --git a/src/pkg/container/ring/Makefile b/src/pkg/container/ring/Makefile deleted file mode 100644 index fb0900774..000000000 --- a/src/pkg/container/ring/Makefile +++ /dev/null @@ -1,11 +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 ../../../Make.inc - -TARG=container/ring -GOFILES=\ - ring.go\ - -include ../../../Make.pkg diff --git a/src/pkg/container/vector/Makefile b/src/pkg/container/vector/Makefile deleted file mode 100644 index f6b50156f..000000000 --- a/src/pkg/container/vector/Makefile +++ /dev/null @@ -1,69 +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 ../../../Make.inc - -TARG=container/vector -GOFILES=\ - defs.go\ - intvector.go\ - stringvector.go\ - vector.go\ - -include ../../../Make.pkg - -generate: vector.go vector_test.go - < vector.go cat\ - | gofmt -r='Vector -> IntVector'\ - | gofmt -r='interface{} -> int'\ - > intvector.go\ - - < vector.go cat\ - | gofmt -r='Vector -> StringVector'\ - | gofmt -r='interface{} -> string'\ - > stringvector.go\ - - < vector_test.go cat\ - | gofmt -r='Vector -> IntVector'\ - | gofmt -r='zero -> intzero'\ - | gofmt -r='elem2Value -> elem2IntValue'\ - | gofmt -r='intf2Value -> intf2IntValue'\ - | gofmt -r='int2Value -> int2IntValue'\ - | gofmt -r='TestZeroLen -> TestIntZeroLen'\ - | gofmt -r='TestResize -> TestIntResize'\ - | gofmt -r='TestResize2 -> TestIntResize2'\ - | gofmt -r='checkZero -> checkIntZero'\ - | gofmt -r='TestTrailingElements -> TestIntTrailingElements'\ - | gofmt -r='TestAccess -> TestIntAccess'\ - | gofmt -r='TestInsertDeleteClear -> TestIntInsertDeleteClear'\ - | gofmt -r='verify_slice -> verify_sliceInt'\ - | gofmt -r='verify_pattern -> verify_patternInt'\ - | gofmt -r='make_vector -> make_vectorInt'\ - | gofmt -r='TestInsertVector -> TestIntInsertVector'\ - | gofmt -r='TestDo -> TestIntDo'\ - | gofmt -r='TestVectorCopy -> TestIntVectorCopy'\ - | gofmt -r='interface{} -> int'\ - > intvector_test.go\ - - < vector_test.go cat\ - | gofmt -r='Vector -> StringVector'\ - | gofmt -r='zero -> strzero'\ - | gofmt -r='int2Value -> int2StrValue'\ - | gofmt -r='intf2Value -> intf2StrValue'\ - | gofmt -r='elem2Value -> elem2StrValue'\ - | gofmt -r='TestZeroLen -> TestStrZeroLen'\ - | gofmt -r='TestResize -> TestStrResize'\ - | gofmt -r='TestResize2 -> TestStrResize2'\ - | gofmt -r='checkZero -> checkStrZero'\ - | gofmt -r='TestTrailingElements -> TestStrTrailingElements'\ - | gofmt -r='TestAccess -> TestStrAccess'\ - | gofmt -r='TestInsertDeleteClear -> TestStrInsertDeleteClear'\ - | gofmt -r='verify_slice -> verify_sliceStr'\ - | gofmt -r='verify_pattern -> verify_patternStr'\ - | gofmt -r='make_vector -> make_vectorStr'\ - | gofmt -r='TestInsertVector -> TestStrInsertVector'\ - | gofmt -r='TestDo -> TestStrDo'\ - | gofmt -r='TestVectorCopy -> TestStrVectorCopy'\ - | gofmt -r='interface{} -> string'\ - > stringvector_test.go diff --git a/src/pkg/container/vector/defs.go b/src/pkg/container/vector/defs.go deleted file mode 100644 index 6d6b2ac81..000000000 --- a/src/pkg/container/vector/defs.go +++ /dev/null @@ -1,43 +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. - -// Package vector implements containers for managing sequences of elements. -// Vectors grow and shrink dynamically as necessary. -package vector - -// Vector is a container for numbered sequences of elements of type interface{}. -// A vector's length and capacity adjusts automatically as necessary. -// The zero value for Vector is an empty vector ready to use. -type Vector []interface{} - -// IntVector is a container for numbered sequences of elements of type int. -// A vector's length and capacity adjusts automatically as necessary. -// The zero value for IntVector is an empty vector ready to use. -type IntVector []int - -// StringVector is a container for numbered sequences of elements of type string. -// A vector's length and capacity adjusts automatically as necessary. -// The zero value for StringVector is an empty vector ready to use. -type StringVector []string - -// Initial underlying array size -const initialSize = 8 - -// Partial sort.Interface support - -// LessInterface provides partial support of the sort.Interface. -type LessInterface interface { - Less(y interface{}) bool -} - -// Less returns a boolean denoting whether the i'th element is less than the j'th element. -func (p *Vector) Less(i, j int) bool { return (*p)[i].(LessInterface).Less((*p)[j]) } - -// sort.Interface support - -// Less returns a boolean denoting whether the i'th element is less than the j'th element. -func (p *IntVector) Less(i, j int) bool { return (*p)[i] < (*p)[j] } - -// Less returns a boolean denoting whether the i'th element is less than the j'th element. -func (p *StringVector) Less(i, j int) bool { return (*p)[i] < (*p)[j] } diff --git a/src/pkg/container/vector/intvector.go b/src/pkg/container/vector/intvector.go deleted file mode 100644 index aa88cfeb3..000000000 --- a/src/pkg/container/vector/intvector.go +++ /dev/null @@ -1,188 +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. - -// CAUTION: If this file is not vector.go, it was generated -// automatically from vector.go - DO NOT EDIT in that case! - -package vector - -func (p *IntVector) realloc(length, capacity int) (b []int) { - if capacity < initialSize { - capacity = initialSize - } - if capacity < length { - capacity = length - } - b = make(IntVector, length, capacity) - copy(b, *p) - *p = b - return -} - -// Insert n elements at position i. -func (p *IntVector) Expand(i, n int) { - a := *p - - // make sure we have enough space - len0 := len(a) - len1 := len0 + n - if len1 <= cap(a) { - // enough space - just expand - a = a[0:len1] - } else { - // not enough space - double capacity - capb := cap(a) * 2 - if capb < len1 { - // still not enough - use required length - capb = len1 - } - // capb >= len1 - a = p.realloc(len1, capb) - } - - // make a hole - for j := len0 - 1; j >= i; j-- { - a[j+n] = a[j] - } - - *p = a -} - -// Insert n elements at the end of a vector. -func (p *IntVector) Extend(n int) { p.Expand(len(*p), n) } - -// Resize changes the length and capacity of a vector. -// If the new length is shorter than the current length, Resize discards -// trailing elements. If the new length is longer than the current length, -// Resize adds the respective zero values for the additional elements. The capacity -// parameter is ignored unless the new length or capacity is longer than the current -// capacity. The resized vector's capacity may be larger than the requested capacity. -func (p *IntVector) Resize(length, capacity int) *IntVector { - a := *p - - if length > cap(a) || capacity > cap(a) { - // not enough space or larger capacity requested explicitly - a = p.realloc(length, capacity) - } else if length < len(a) { - // clear trailing elements - for i := range a[length:] { - var zero int - a[length+i] = zero - } - } - - *p = a[0:length] - return p -} - -// Len returns the number of elements in the vector. -// Same as len(*p). -func (p *IntVector) Len() int { return len(*p) } - -// Cap returns the capacity of the vector; that is, the -// maximum length the vector can grow without resizing. -// Same as cap(*p). -func (p *IntVector) Cap() int { return cap(*p) } - -// At returns the i'th element of the vector. -func (p *IntVector) At(i int) int { return (*p)[i] } - -// Set sets the i'th element of the vector to value x. -func (p *IntVector) Set(i int, x int) { (*p)[i] = x } - -// Last returns the element in the vector of highest index. -func (p *IntVector) Last() int { return (*p)[len(*p)-1] } - -// Copy makes a copy of the vector and returns it. -func (p *IntVector) Copy() IntVector { - arr := make(IntVector, len(*p)) - copy(arr, *p) - return arr -} - -// Insert inserts into the vector an element of value x before -// the current element at index i. -func (p *IntVector) Insert(i int, x int) { - p.Expand(i, 1) - (*p)[i] = x -} - -// Delete deletes the i'th element of the vector. The gap is closed so the old -// element at index i+1 has index i afterwards. -func (p *IntVector) Delete(i int) { - a := *p - n := len(a) - - copy(a[i:n-1], a[i+1:n]) - var zero int - a[n-1] = zero // support GC, zero out entry - *p = a[0 : n-1] -} - -// InsertVector inserts into the vector the contents of the vector -// x such that the 0th element of x appears at index i after insertion. -func (p *IntVector) InsertVector(i int, x *IntVector) { - b := *x - - p.Expand(i, len(b)) - copy((*p)[i:i+len(b)], b) -} - -// Cut deletes elements i through j-1, inclusive. -func (p *IntVector) Cut(i, j int) { - a := *p - n := len(a) - m := n - (j - i) - - copy(a[i:m], a[j:n]) - for k := m; k < n; k++ { //TODO(bflm) don't zero out the elements unless it's a Vector. - var zero int - a[k] = zero // support GC, zero out entries - } - - *p = a[0:m] -} - -// Slice returns a new sub-vector by slicing the old one to extract slice [i:j]. -// The elements are copied. The original vector is unchanged. -func (p *IntVector) Slice(i, j int) *IntVector { - var s IntVector - s.realloc(j-i, 0) // will fail in Init() if j < i - copy(s, (*p)[i:j]) - return &s -} - -// Convenience wrappers - -// Push appends x to the end of the vector. -func (p *IntVector) Push(x int) { p.Insert(len(*p), x) } - -// Pop deletes the last element of the vector. -func (p *IntVector) Pop() int { - a := *p - - i := len(a) - 1 - x := a[i] - var zero int - a[i] = zero // support GC, zero out entry - *p = a[0:i] - return x -} - -// AppendVector appends the entire vector x to the end of this vector. -func (p *IntVector) AppendVector(x *IntVector) { p.InsertVector(len(*p), x) } - -// Swap exchanges the elements at indexes i and j. -func (p *IntVector) Swap(i, j int) { - a := *p - a[i], a[j] = a[j], a[i] -} - -// Do calls function f for each element of the vector, in order. -// The behavior of Do is undefined if f changes *p. -func (p *IntVector) Do(f func(elem int)) { - for _, e := range *p { - f(e) - } -} diff --git a/src/pkg/container/vector/intvector_test.go b/src/pkg/container/vector/intvector_test.go deleted file mode 100644 index b825af912..000000000 --- a/src/pkg/container/vector/intvector_test.go +++ /dev/null @@ -1,331 +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. - -// CAUTION: If this file is not vector_test.go, it was generated -// automatically from vector_test.go - DO NOT EDIT in that case! - -package vector - -import "testing" - -func TestIntZeroLen(t *testing.T) { - a := new(IntVector) - if a.Len() != 0 { - t.Errorf("%T: B1) expected 0, got %d", a, a.Len()) - } - if len(*a) != 0 { - t.Errorf("%T: B2) expected 0, got %d", a, len(*a)) - } - var b IntVector - if b.Len() != 0 { - t.Errorf("%T: B3) expected 0, got %d", b, b.Len()) - } - if len(b) != 0 { - t.Errorf("%T: B4) expected 0, got %d", b, len(b)) - } -} - -func TestIntResize(t *testing.T) { - var a IntVector - checkSize(t, &a, 0, 0) - checkSize(t, a.Resize(0, 5), 0, 5) - checkSize(t, a.Resize(1, 0), 1, 5) - checkSize(t, a.Resize(10, 0), 10, 10) - checkSize(t, a.Resize(5, 0), 5, 10) - checkSize(t, a.Resize(3, 8), 3, 10) - checkSize(t, a.Resize(0, 100), 0, 100) - checkSize(t, a.Resize(11, 100), 11, 100) -} - -func TestIntResize2(t *testing.T) { - var a IntVector - checkSize(t, &a, 0, 0) - a.Push(int2IntValue(1)) - a.Push(int2IntValue(2)) - a.Push(int2IntValue(3)) - a.Push(int2IntValue(4)) - checkSize(t, &a, 4, 4) - checkSize(t, a.Resize(10, 0), 10, 10) - for i := 4; i < a.Len(); i++ { - if a.At(i) != intzero { - t.Errorf("%T: expected a.At(%d) == %v; found %v!", a, i, intzero, a.At(i)) - } - } - for i := 4; i < len(a); i++ { - if a[i] != intzero { - t.Errorf("%T: expected a[%d] == %v; found %v", a, i, intzero, a[i]) - } - } -} - -func checkIntZero(t *testing.T, a *IntVector, i int) { - for j := 0; j < i; j++ { - if a.At(j) == intzero { - t.Errorf("%T: 1 expected a.At(%d) == %d; found %v", a, j, j, a.At(j)) - } - if (*a)[j] == intzero { - t.Errorf("%T: 2 expected (*a)[%d] == %d; found %v", a, j, j, (*a)[j]) - } - } - for ; i < a.Len(); i++ { - if a.At(i) != intzero { - t.Errorf("%T: 3 expected a.At(%d) == %v; found %v", a, i, intzero, a.At(i)) - } - if (*a)[i] != intzero { - t.Errorf("%T: 4 expected (*a)[%d] == %v; found %v", a, i, intzero, (*a)[i]) - } - } -} - -func TestIntTrailingElements(t *testing.T) { - var a IntVector - for i := 0; i < 10; i++ { - a.Push(int2IntValue(i + 1)) - } - checkIntZero(t, &a, 10) - checkSize(t, &a, 10, 16) - checkSize(t, a.Resize(5, 0), 5, 16) - checkSize(t, a.Resize(10, 0), 10, 16) - checkIntZero(t, &a, 5) -} - -func TestIntAccess(t *testing.T) { - const n = 100 - var a IntVector - a.Resize(n, 0) - for i := 0; i < n; i++ { - a.Set(i, int2IntValue(val(i))) - } - for i := 0; i < n; i++ { - if elem2IntValue(a.At(i)) != int2IntValue(val(i)) { - t.Error(i) - } - } - var b IntVector - b.Resize(n, 0) - for i := 0; i < n; i++ { - b[i] = int2IntValue(val(i)) - } - for i := 0; i < n; i++ { - if elem2IntValue(b[i]) != int2IntValue(val(i)) { - t.Error(i) - } - } -} - -func TestIntInsertDeleteClear(t *testing.T) { - const n = 100 - var a IntVector - - for i := 0; i < n; i++ { - if a.Len() != i { - t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i) - } - if len(a) != i { - t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i) - } - a.Insert(0, int2IntValue(val(i))) - if elem2IntValue(a.Last()) != int2IntValue(val(0)) { - t.Errorf("%T: B", a) - } - } - for i := n - 1; i >= 0; i-- { - if elem2IntValue(a.Last()) != int2IntValue(val(0)) { - t.Errorf("%T: C", a) - } - if elem2IntValue(a.At(0)) != int2IntValue(val(i)) { - t.Errorf("%T: D", a) - } - if elem2IntValue(a[0]) != int2IntValue(val(i)) { - t.Errorf("%T: D2", a) - } - a.Delete(0) - if a.Len() != i { - t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i) - } - if len(a) != i { - t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i) - } - } - - if a.Len() != 0 { - t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len()) - } - if len(a) != 0 { - t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a)) - } - for i := 0; i < n; i++ { - a.Push(int2IntValue(val(i))) - if a.Len() != i+1 { - t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1) - } - if len(a) != i+1 { - t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1) - } - if elem2IntValue(a.Last()) != int2IntValue(val(i)) { - t.Errorf("%T: H", a) - } - } - a.Resize(0, 0) - if a.Len() != 0 { - t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len()) - } - if len(a) != 0 { - t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a)) - } - - const m = 5 - for j := 0; j < m; j++ { - a.Push(int2IntValue(j)) - for i := 0; i < n; i++ { - x := val(i) - a.Push(int2IntValue(x)) - if elem2IntValue(a.Pop()) != int2IntValue(x) { - t.Errorf("%T: J", a) - } - if a.Len() != j+1 { - t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1) - } - if len(a) != j+1 { - t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1) - } - } - } - if a.Len() != m { - t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m) - } - if len(a) != m { - t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m) - } -} - -func verify_sliceInt(t *testing.T, x *IntVector, elt, i, j int) { - for k := i; k < j; k++ { - if elem2IntValue(x.At(k)) != int2IntValue(elt) { - t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2IntValue(x.At(k)), int2IntValue(elt)) - } - } - - s := x.Slice(i, j) - for k, n := 0, j-i; k < n; k++ { - if elem2IntValue(s.At(k)) != int2IntValue(elt) { - t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2IntValue(x.At(k)), int2IntValue(elt)) - } - } -} - -func verify_patternInt(t *testing.T, x *IntVector, a, b, c int) { - n := a + b + c - if x.Len() != n { - t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n) - } - if len(*x) != n { - t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n) - } - verify_sliceInt(t, x, 0, 0, a) - verify_sliceInt(t, x, 1, a, a+b) - verify_sliceInt(t, x, 0, a+b, n) -} - -func make_vectorInt(elt, len int) *IntVector { - x := new(IntVector).Resize(len, 0) - for i := 0; i < len; i++ { - x.Set(i, int2IntValue(elt)) - } - return x -} - -func TestIntInsertVector(t *testing.T) { - // 1 - a := make_vectorInt(0, 0) - b := make_vectorInt(1, 10) - a.InsertVector(0, b) - verify_patternInt(t, a, 0, 10, 0) - // 2 - a = make_vectorInt(0, 10) - b = make_vectorInt(1, 0) - a.InsertVector(5, b) - verify_patternInt(t, a, 5, 0, 5) - // 3 - a = make_vectorInt(0, 10) - b = make_vectorInt(1, 3) - a.InsertVector(3, b) - verify_patternInt(t, a, 3, 3, 7) - // 4 - a = make_vectorInt(0, 10) - b = make_vectorInt(1, 1000) - a.InsertVector(8, b) - verify_patternInt(t, a, 8, 1000, 2) -} - -func TestIntDo(t *testing.T) { - const n = 25 - const salt = 17 - a := new(IntVector).Resize(n, 0) - for i := 0; i < n; i++ { - a.Set(i, int2IntValue(salt*i)) - } - count := 0 - a.Do(func(e int) { - i := intf2IntValue(e) - if i != int2IntValue(count*salt) { - t.Error(tname(a), "value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(a), "should visit", n, "values; did visit", count) - } - - b := new(IntVector).Resize(n, 0) - for i := 0; i < n; i++ { - (*b)[i] = int2IntValue(salt * i) - } - count = 0 - b.Do(func(e int) { - i := intf2IntValue(e) - if i != int2IntValue(count*salt) { - t.Error(tname(b), "b) value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(b), "b) should visit", n, "values; did visit", count) - } - - var c IntVector - c.Resize(n, 0) - for i := 0; i < n; i++ { - c[i] = int2IntValue(salt * i) - } - count = 0 - c.Do(func(e int) { - i := intf2IntValue(e) - if i != int2IntValue(count*salt) { - t.Error(tname(c), "c) value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(c), "c) should visit", n, "values; did visit", count) - } - -} - -func TestIntVectorCopy(t *testing.T) { - // verify Copy() returns a copy, not simply a slice of the original vector - const Len = 10 - var src IntVector - for i := 0; i < Len; i++ { - src.Push(int2IntValue(i * i)) - } - dest := src.Copy() - for i := 0; i < Len; i++ { - src[i] = int2IntValue(-1) - v := elem2IntValue(dest[i]) - if v != int2IntValue(i*i) { - t.Error(tname(src), "expected", i*i, "got", v) - } - } -} diff --git a/src/pkg/container/vector/nogen_test.go b/src/pkg/container/vector/nogen_test.go deleted file mode 100644 index 7b6a25952..000000000 --- a/src/pkg/container/vector/nogen_test.go +++ /dev/null @@ -1,67 +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. - -package vector - -import ( - "fmt" - "sort" - "testing" -) - -var ( - zero interface{} - intzero int - strzero string -) - -func int2Value(x int) int { return x } -func int2IntValue(x int) int { return x } -func int2StrValue(x int) string { return string(x) } - -func elem2Value(x interface{}) int { return x.(int) } -func elem2IntValue(x int) int { return x } -func elem2StrValue(x string) string { return x } - -func intf2Value(x interface{}) int { return x.(int) } -func intf2IntValue(x interface{}) int { return x.(int) } -func intf2StrValue(x interface{}) string { return x.(string) } - -type VectorInterface interface { - Len() int - Cap() int -} - -func checkSize(t *testing.T, v VectorInterface, len, cap int) { - if v.Len() != len { - t.Errorf("%T expected len = %d; found %d", v, len, v.Len()) - } - if v.Cap() < cap { - t.Errorf("%T expected cap >= %d; found %d", v, cap, v.Cap()) - } -} - -func val(i int) int { return i*991 - 1234 } - -func TestSorting(t *testing.T) { - const n = 100 - - a := new(IntVector).Resize(n, 0) - for i := n - 1; i >= 0; i-- { - a.Set(i, n-1-i) - } - if sort.IsSorted(a) { - t.Error("int vector not sorted") - } - - b := new(StringVector).Resize(n, 0) - for i := n - 1; i >= 0; i-- { - b.Set(i, fmt.Sprint(n-1-i)) - } - if sort.IsSorted(b) { - t.Error("string vector not sorted") - } -} - -func tname(x interface{}) string { return fmt.Sprintf("%T: ", x) } diff --git a/src/pkg/container/vector/numbers_test.go b/src/pkg/container/vector/numbers_test.go deleted file mode 100644 index abe01a8fb..000000000 --- a/src/pkg/container/vector/numbers_test.go +++ /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. - -package vector - -import ( - "fmt" - "runtime" - "strings" - "testing" -) - -const memTestN = 1000000 - -func s(n uint64) string { - str := fmt.Sprintf("%d", n) - lens := len(str) - a := make([]string, (lens+2)/3) - start := lens - for i := range a { - start -= 3 - if start < 0 { - start = 0 - } - a[len(a)-i-1] = str[start:lens] - lens -= 3 - } - return strings.Join(a, " ") -} - -func TestVectorNums(t *testing.T) { - if testing.Short() { - return - } - var v Vector - c := int(0) - runtime.GC() - m0 := runtime.MemStats - v.Resize(memTestN, memTestN) - for i := 0; i < memTestN; i++ { - v.Set(i, c) - } - runtime.GC() - m := runtime.MemStats - v.Resize(0, 0) - runtime.GC() - n := m.Alloc - m0.Alloc - t.Logf("%T.Push(%#v), n = %s: Alloc/n = %.2f\n", v, c, s(memTestN), float64(n)/memTestN) -} - -func TestIntVectorNums(t *testing.T) { - if testing.Short() { - return - } - var v IntVector - c := int(0) - runtime.GC() - m0 := runtime.MemStats - v.Resize(memTestN, memTestN) - for i := 0; i < memTestN; i++ { - v.Set(i, c) - } - runtime.GC() - m := runtime.MemStats - v.Resize(0, 0) - runtime.GC() - n := m.Alloc - m0.Alloc - t.Logf("%T.Push(%#v), n = %s: Alloc/n = %.2f\n", v, c, s(memTestN), float64(n)/memTestN) -} - -func TestStringVectorNums(t *testing.T) { - if testing.Short() { - return - } - var v StringVector - c := "" - runtime.GC() - m0 := runtime.MemStats - v.Resize(memTestN, memTestN) - for i := 0; i < memTestN; i++ { - v.Set(i, c) - } - runtime.GC() - m := runtime.MemStats - v.Resize(0, 0) - runtime.GC() - n := m.Alloc - m0.Alloc - t.Logf("%T.Push(%#v), n = %s: Alloc/n = %.2f\n", v, c, s(memTestN), float64(n)/memTestN) -} - -func BenchmarkVectorNums(b *testing.B) { - c := int(0) - var v Vector - b.StopTimer() - runtime.GC() - b.StartTimer() - for i := 0; i < b.N; i++ { - v.Push(c) - } -} - -func BenchmarkIntVectorNums(b *testing.B) { - c := int(0) - var v IntVector - b.StopTimer() - runtime.GC() - b.StartTimer() - for i := 0; i < b.N; i++ { - v.Push(c) - } -} - -func BenchmarkStringVectorNums(b *testing.B) { - c := "" - var v StringVector - b.StopTimer() - runtime.GC() - b.StartTimer() - for i := 0; i < b.N; i++ { - v.Push(c) - } -} diff --git a/src/pkg/container/vector/stringvector.go b/src/pkg/container/vector/stringvector.go deleted file mode 100644 index dc81f06b7..000000000 --- a/src/pkg/container/vector/stringvector.go +++ /dev/null @@ -1,188 +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. - -// CAUTION: If this file is not vector.go, it was generated -// automatically from vector.go - DO NOT EDIT in that case! - -package vector - -func (p *StringVector) realloc(length, capacity int) (b []string) { - if capacity < initialSize { - capacity = initialSize - } - if capacity < length { - capacity = length - } - b = make(StringVector, length, capacity) - copy(b, *p) - *p = b - return -} - -// Insert n elements at position i. -func (p *StringVector) Expand(i, n int) { - a := *p - - // make sure we have enough space - len0 := len(a) - len1 := len0 + n - if len1 <= cap(a) { - // enough space - just expand - a = a[0:len1] - } else { - // not enough space - double capacity - capb := cap(a) * 2 - if capb < len1 { - // still not enough - use required length - capb = len1 - } - // capb >= len1 - a = p.realloc(len1, capb) - } - - // make a hole - for j := len0 - 1; j >= i; j-- { - a[j+n] = a[j] - } - - *p = a -} - -// Insert n elements at the end of a vector. -func (p *StringVector) Extend(n int) { p.Expand(len(*p), n) } - -// Resize changes the length and capacity of a vector. -// If the new length is shorter than the current length, Resize discards -// trailing elements. If the new length is longer than the current length, -// Resize adds the respective zero values for the additional elements. The capacity -// parameter is ignored unless the new length or capacity is longer than the current -// capacity. The resized vector's capacity may be larger than the requested capacity. -func (p *StringVector) Resize(length, capacity int) *StringVector { - a := *p - - if length > cap(a) || capacity > cap(a) { - // not enough space or larger capacity requested explicitly - a = p.realloc(length, capacity) - } else if length < len(a) { - // clear trailing elements - for i := range a[length:] { - var zero string - a[length+i] = zero - } - } - - *p = a[0:length] - return p -} - -// Len returns the number of elements in the vector. -// Same as len(*p). -func (p *StringVector) Len() int { return len(*p) } - -// Cap returns the capacity of the vector; that is, the -// maximum length the vector can grow without resizing. -// Same as cap(*p). -func (p *StringVector) Cap() int { return cap(*p) } - -// At returns the i'th element of the vector. -func (p *StringVector) At(i int) string { return (*p)[i] } - -// Set sets the i'th element of the vector to value x. -func (p *StringVector) Set(i int, x string) { (*p)[i] = x } - -// Last returns the element in the vector of highest index. -func (p *StringVector) Last() string { return (*p)[len(*p)-1] } - -// Copy makes a copy of the vector and returns it. -func (p *StringVector) Copy() StringVector { - arr := make(StringVector, len(*p)) - copy(arr, *p) - return arr -} - -// Insert inserts into the vector an element of value x before -// the current element at index i. -func (p *StringVector) Insert(i int, x string) { - p.Expand(i, 1) - (*p)[i] = x -} - -// Delete deletes the i'th element of the vector. The gap is closed so the old -// element at index i+1 has index i afterwards. -func (p *StringVector) Delete(i int) { - a := *p - n := len(a) - - copy(a[i:n-1], a[i+1:n]) - var zero string - a[n-1] = zero // support GC, zero out entry - *p = a[0 : n-1] -} - -// InsertVector inserts into the vector the contents of the vector -// x such that the 0th element of x appears at index i after insertion. -func (p *StringVector) InsertVector(i int, x *StringVector) { - b := *x - - p.Expand(i, len(b)) - copy((*p)[i:i+len(b)], b) -} - -// Cut deletes elements i through j-1, inclusive. -func (p *StringVector) Cut(i, j int) { - a := *p - n := len(a) - m := n - (j - i) - - copy(a[i:m], a[j:n]) - for k := m; k < n; k++ { //TODO(bflm) don't zero out the elements unless it's a Vector. - var zero string - a[k] = zero // support GC, zero out entries - } - - *p = a[0:m] -} - -// Slice returns a new sub-vector by slicing the old one to extract slice [i:j]. -// The elements are copied. The original vector is unchanged. -func (p *StringVector) Slice(i, j int) *StringVector { - var s StringVector - s.realloc(j-i, 0) // will fail in Init() if j < i - copy(s, (*p)[i:j]) - return &s -} - -// Convenience wrappers - -// Push appends x to the end of the vector. -func (p *StringVector) Push(x string) { p.Insert(len(*p), x) } - -// Pop deletes the last element of the vector. -func (p *StringVector) Pop() string { - a := *p - - i := len(a) - 1 - x := a[i] - var zero string - a[i] = zero // support GC, zero out entry - *p = a[0:i] - return x -} - -// AppendVector appends the entire vector x to the end of this vector. -func (p *StringVector) AppendVector(x *StringVector) { p.InsertVector(len(*p), x) } - -// Swap exchanges the elements at indexes i and j. -func (p *StringVector) Swap(i, j int) { - a := *p - a[i], a[j] = a[j], a[i] -} - -// Do calls function f for each element of the vector, in order. -// The behavior of Do is undefined if f changes *p. -func (p *StringVector) Do(f func(elem string)) { - for _, e := range *p { - f(e) - } -} diff --git a/src/pkg/container/vector/stringvector_test.go b/src/pkg/container/vector/stringvector_test.go deleted file mode 100644 index c75676f78..000000000 --- a/src/pkg/container/vector/stringvector_test.go +++ /dev/null @@ -1,331 +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. - -// CAUTION: If this file is not vector_test.go, it was generated -// automatically from vector_test.go - DO NOT EDIT in that case! - -package vector - -import "testing" - -func TestStrZeroLen(t *testing.T) { - a := new(StringVector) - if a.Len() != 0 { - t.Errorf("%T: B1) expected 0, got %d", a, a.Len()) - } - if len(*a) != 0 { - t.Errorf("%T: B2) expected 0, got %d", a, len(*a)) - } - var b StringVector - if b.Len() != 0 { - t.Errorf("%T: B3) expected 0, got %d", b, b.Len()) - } - if len(b) != 0 { - t.Errorf("%T: B4) expected 0, got %d", b, len(b)) - } -} - -func TestStrResize(t *testing.T) { - var a StringVector - checkSize(t, &a, 0, 0) - checkSize(t, a.Resize(0, 5), 0, 5) - checkSize(t, a.Resize(1, 0), 1, 5) - checkSize(t, a.Resize(10, 0), 10, 10) - checkSize(t, a.Resize(5, 0), 5, 10) - checkSize(t, a.Resize(3, 8), 3, 10) - checkSize(t, a.Resize(0, 100), 0, 100) - checkSize(t, a.Resize(11, 100), 11, 100) -} - -func TestStrResize2(t *testing.T) { - var a StringVector - checkSize(t, &a, 0, 0) - a.Push(int2StrValue(1)) - a.Push(int2StrValue(2)) - a.Push(int2StrValue(3)) - a.Push(int2StrValue(4)) - checkSize(t, &a, 4, 4) - checkSize(t, a.Resize(10, 0), 10, 10) - for i := 4; i < a.Len(); i++ { - if a.At(i) != strzero { - t.Errorf("%T: expected a.At(%d) == %v; found %v!", a, i, strzero, a.At(i)) - } - } - for i := 4; i < len(a); i++ { - if a[i] != strzero { - t.Errorf("%T: expected a[%d] == %v; found %v", a, i, strzero, a[i]) - } - } -} - -func checkStrZero(t *testing.T, a *StringVector, i int) { - for j := 0; j < i; j++ { - if a.At(j) == strzero { - t.Errorf("%T: 1 expected a.At(%d) == %d; found %v", a, j, j, a.At(j)) - } - if (*a)[j] == strzero { - t.Errorf("%T: 2 expected (*a)[%d] == %d; found %v", a, j, j, (*a)[j]) - } - } - for ; i < a.Len(); i++ { - if a.At(i) != strzero { - t.Errorf("%T: 3 expected a.At(%d) == %v; found %v", a, i, strzero, a.At(i)) - } - if (*a)[i] != strzero { - t.Errorf("%T: 4 expected (*a)[%d] == %v; found %v", a, i, strzero, (*a)[i]) - } - } -} - -func TestStrTrailingElements(t *testing.T) { - var a StringVector - for i := 0; i < 10; i++ { - a.Push(int2StrValue(i + 1)) - } - checkStrZero(t, &a, 10) - checkSize(t, &a, 10, 16) - checkSize(t, a.Resize(5, 0), 5, 16) - checkSize(t, a.Resize(10, 0), 10, 16) - checkStrZero(t, &a, 5) -} - -func TestStrAccess(t *testing.T) { - const n = 100 - var a StringVector - a.Resize(n, 0) - for i := 0; i < n; i++ { - a.Set(i, int2StrValue(val(i))) - } - for i := 0; i < n; i++ { - if elem2StrValue(a.At(i)) != int2StrValue(val(i)) { - t.Error(i) - } - } - var b StringVector - b.Resize(n, 0) - for i := 0; i < n; i++ { - b[i] = int2StrValue(val(i)) - } - for i := 0; i < n; i++ { - if elem2StrValue(b[i]) != int2StrValue(val(i)) { - t.Error(i) - } - } -} - -func TestStrInsertDeleteClear(t *testing.T) { - const n = 100 - var a StringVector - - for i := 0; i < n; i++ { - if a.Len() != i { - t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i) - } - if len(a) != i { - t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i) - } - a.Insert(0, int2StrValue(val(i))) - if elem2StrValue(a.Last()) != int2StrValue(val(0)) { - t.Errorf("%T: B", a) - } - } - for i := n - 1; i >= 0; i-- { - if elem2StrValue(a.Last()) != int2StrValue(val(0)) { - t.Errorf("%T: C", a) - } - if elem2StrValue(a.At(0)) != int2StrValue(val(i)) { - t.Errorf("%T: D", a) - } - if elem2StrValue(a[0]) != int2StrValue(val(i)) { - t.Errorf("%T: D2", a) - } - a.Delete(0) - if a.Len() != i { - t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i) - } - if len(a) != i { - t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i) - } - } - - if a.Len() != 0 { - t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len()) - } - if len(a) != 0 { - t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a)) - } - for i := 0; i < n; i++ { - a.Push(int2StrValue(val(i))) - if a.Len() != i+1 { - t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1) - } - if len(a) != i+1 { - t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1) - } - if elem2StrValue(a.Last()) != int2StrValue(val(i)) { - t.Errorf("%T: H", a) - } - } - a.Resize(0, 0) - if a.Len() != 0 { - t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len()) - } - if len(a) != 0 { - t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a)) - } - - const m = 5 - for j := 0; j < m; j++ { - a.Push(int2StrValue(j)) - for i := 0; i < n; i++ { - x := val(i) - a.Push(int2StrValue(x)) - if elem2StrValue(a.Pop()) != int2StrValue(x) { - t.Errorf("%T: J", a) - } - if a.Len() != j+1 { - t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1) - } - if len(a) != j+1 { - t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1) - } - } - } - if a.Len() != m { - t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m) - } - if len(a) != m { - t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m) - } -} - -func verify_sliceStr(t *testing.T, x *StringVector, elt, i, j int) { - for k := i; k < j; k++ { - if elem2StrValue(x.At(k)) != int2StrValue(elt) { - t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2StrValue(x.At(k)), int2StrValue(elt)) - } - } - - s := x.Slice(i, j) - for k, n := 0, j-i; k < n; k++ { - if elem2StrValue(s.At(k)) != int2StrValue(elt) { - t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2StrValue(x.At(k)), int2StrValue(elt)) - } - } -} - -func verify_patternStr(t *testing.T, x *StringVector, a, b, c int) { - n := a + b + c - if x.Len() != n { - t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n) - } - if len(*x) != n { - t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n) - } - verify_sliceStr(t, x, 0, 0, a) - verify_sliceStr(t, x, 1, a, a+b) - verify_sliceStr(t, x, 0, a+b, n) -} - -func make_vectorStr(elt, len int) *StringVector { - x := new(StringVector).Resize(len, 0) - for i := 0; i < len; i++ { - x.Set(i, int2StrValue(elt)) - } - return x -} - -func TestStrInsertVector(t *testing.T) { - // 1 - a := make_vectorStr(0, 0) - b := make_vectorStr(1, 10) - a.InsertVector(0, b) - verify_patternStr(t, a, 0, 10, 0) - // 2 - a = make_vectorStr(0, 10) - b = make_vectorStr(1, 0) - a.InsertVector(5, b) - verify_patternStr(t, a, 5, 0, 5) - // 3 - a = make_vectorStr(0, 10) - b = make_vectorStr(1, 3) - a.InsertVector(3, b) - verify_patternStr(t, a, 3, 3, 7) - // 4 - a = make_vectorStr(0, 10) - b = make_vectorStr(1, 1000) - a.InsertVector(8, b) - verify_patternStr(t, a, 8, 1000, 2) -} - -func TestStrDo(t *testing.T) { - const n = 25 - const salt = 17 - a := new(StringVector).Resize(n, 0) - for i := 0; i < n; i++ { - a.Set(i, int2StrValue(salt*i)) - } - count := 0 - a.Do(func(e string) { - i := intf2StrValue(e) - if i != int2StrValue(count*salt) { - t.Error(tname(a), "value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(a), "should visit", n, "values; did visit", count) - } - - b := new(StringVector).Resize(n, 0) - for i := 0; i < n; i++ { - (*b)[i] = int2StrValue(salt * i) - } - count = 0 - b.Do(func(e string) { - i := intf2StrValue(e) - if i != int2StrValue(count*salt) { - t.Error(tname(b), "b) value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(b), "b) should visit", n, "values; did visit", count) - } - - var c StringVector - c.Resize(n, 0) - for i := 0; i < n; i++ { - c[i] = int2StrValue(salt * i) - } - count = 0 - c.Do(func(e string) { - i := intf2StrValue(e) - if i != int2StrValue(count*salt) { - t.Error(tname(c), "c) value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(c), "c) should visit", n, "values; did visit", count) - } - -} - -func TestStrVectorCopy(t *testing.T) { - // verify Copy() returns a copy, not simply a slice of the original vector - const Len = 10 - var src StringVector - for i := 0; i < Len; i++ { - src.Push(int2StrValue(i * i)) - } - dest := src.Copy() - for i := 0; i < Len; i++ { - src[i] = int2StrValue(-1) - v := elem2StrValue(dest[i]) - if v != int2StrValue(i*i) { - t.Error(tname(src), "expected", i*i, "got", v) - } - } -} diff --git a/src/pkg/container/vector/vector.go b/src/pkg/container/vector/vector.go deleted file mode 100644 index 8470ec067..000000000 --- a/src/pkg/container/vector/vector.go +++ /dev/null @@ -1,188 +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. - -// CAUTION: If this file is not vector.go, it was generated -// automatically from vector.go - DO NOT EDIT in that case! - -package vector - -func (p *Vector) realloc(length, capacity int) (b []interface{}) { - if capacity < initialSize { - capacity = initialSize - } - if capacity < length { - capacity = length - } - b = make(Vector, length, capacity) - copy(b, *p) - *p = b - return -} - -// Insert n elements at position i. -func (p *Vector) Expand(i, n int) { - a := *p - - // make sure we have enough space - len0 := len(a) - len1 := len0 + n - if len1 <= cap(a) { - // enough space - just expand - a = a[0:len1] - } else { - // not enough space - double capacity - capb := cap(a) * 2 - if capb < len1 { - // still not enough - use required length - capb = len1 - } - // capb >= len1 - a = p.realloc(len1, capb) - } - - // make a hole - for j := len0 - 1; j >= i; j-- { - a[j+n] = a[j] - } - - *p = a -} - -// Insert n elements at the end of a vector. -func (p *Vector) Extend(n int) { p.Expand(len(*p), n) } - -// Resize changes the length and capacity of a vector. -// If the new length is shorter than the current length, Resize discards -// trailing elements. If the new length is longer than the current length, -// Resize adds the respective zero values for the additional elements. The capacity -// parameter is ignored unless the new length or capacity is longer than the current -// capacity. The resized vector's capacity may be larger than the requested capacity. -func (p *Vector) Resize(length, capacity int) *Vector { - a := *p - - if length > cap(a) || capacity > cap(a) { - // not enough space or larger capacity requested explicitly - a = p.realloc(length, capacity) - } else if length < len(a) { - // clear trailing elements - for i := range a[length:] { - var zero interface{} - a[length+i] = zero - } - } - - *p = a[0:length] - return p -} - -// Len returns the number of elements in the vector. -// Same as len(*p). -func (p *Vector) Len() int { return len(*p) } - -// Cap returns the capacity of the vector; that is, the -// maximum length the vector can grow without resizing. -// Same as cap(*p). -func (p *Vector) Cap() int { return cap(*p) } - -// At returns the i'th element of the vector. -func (p *Vector) At(i int) interface{} { return (*p)[i] } - -// Set sets the i'th element of the vector to value x. -func (p *Vector) Set(i int, x interface{}) { (*p)[i] = x } - -// Last returns the element in the vector of highest index. -func (p *Vector) Last() interface{} { return (*p)[len(*p)-1] } - -// Copy makes a copy of the vector and returns it. -func (p *Vector) Copy() Vector { - arr := make(Vector, len(*p)) - copy(arr, *p) - return arr -} - -// Insert inserts into the vector an element of value x before -// the current element at index i. -func (p *Vector) Insert(i int, x interface{}) { - p.Expand(i, 1) - (*p)[i] = x -} - -// Delete deletes the i'th element of the vector. The gap is closed so the old -// element at index i+1 has index i afterwards. -func (p *Vector) Delete(i int) { - a := *p - n := len(a) - - copy(a[i:n-1], a[i+1:n]) - var zero interface{} - a[n-1] = zero // support GC, zero out entry - *p = a[0 : n-1] -} - -// InsertVector inserts into the vector the contents of the vector -// x such that the 0th element of x appears at index i after insertion. -func (p *Vector) InsertVector(i int, x *Vector) { - b := *x - - p.Expand(i, len(b)) - copy((*p)[i:i+len(b)], b) -} - -// Cut deletes elements i through j-1, inclusive. -func (p *Vector) Cut(i, j int) { - a := *p - n := len(a) - m := n - (j - i) - - copy(a[i:m], a[j:n]) - for k := m; k < n; k++ { //TODO(bflm) don't zero out the elements unless it's a Vector. - var zero interface{} - a[k] = zero // support GC, zero out entries - } - - *p = a[0:m] -} - -// Slice returns a new sub-vector by slicing the old one to extract slice [i:j]. -// The elements are copied. The original vector is unchanged. -func (p *Vector) Slice(i, j int) *Vector { - var s Vector - s.realloc(j-i, 0) // will fail in Init() if j < i - copy(s, (*p)[i:j]) - return &s -} - -// Convenience wrappers - -// Push appends x to the end of the vector. -func (p *Vector) Push(x interface{}) { p.Insert(len(*p), x) } - -// Pop deletes the last element of the vector. -func (p *Vector) Pop() interface{} { - a := *p - - i := len(a) - 1 - x := a[i] - var zero interface{} - a[i] = zero // support GC, zero out entry - *p = a[0:i] - return x -} - -// AppendVector appends the entire vector x to the end of this vector. -func (p *Vector) AppendVector(x *Vector) { p.InsertVector(len(*p), x) } - -// Swap exchanges the elements at indexes i and j. -func (p *Vector) Swap(i, j int) { - a := *p - a[i], a[j] = a[j], a[i] -} - -// Do calls function f for each element of the vector, in order. -// The behavior of Do is undefined if f changes *p. -func (p *Vector) Do(f func(elem interface{})) { - for _, e := range *p { - f(e) - } -} diff --git a/src/pkg/container/vector/vector_test.go b/src/pkg/container/vector/vector_test.go deleted file mode 100644 index a7f47b8c2..000000000 --- a/src/pkg/container/vector/vector_test.go +++ /dev/null @@ -1,331 +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. - -// CAUTION: If this file is not vector_test.go, it was generated -// automatically from vector_test.go - DO NOT EDIT in that case! - -package vector - -import "testing" - -func TestZeroLen(t *testing.T) { - a := new(Vector) - if a.Len() != 0 { - t.Errorf("%T: B1) expected 0, got %d", a, a.Len()) - } - if len(*a) != 0 { - t.Errorf("%T: B2) expected 0, got %d", a, len(*a)) - } - var b Vector - if b.Len() != 0 { - t.Errorf("%T: B3) expected 0, got %d", b, b.Len()) - } - if len(b) != 0 { - t.Errorf("%T: B4) expected 0, got %d", b, len(b)) - } -} - -func TestResize(t *testing.T) { - var a Vector - checkSize(t, &a, 0, 0) - checkSize(t, a.Resize(0, 5), 0, 5) - checkSize(t, a.Resize(1, 0), 1, 5) - checkSize(t, a.Resize(10, 0), 10, 10) - checkSize(t, a.Resize(5, 0), 5, 10) - checkSize(t, a.Resize(3, 8), 3, 10) - checkSize(t, a.Resize(0, 100), 0, 100) - checkSize(t, a.Resize(11, 100), 11, 100) -} - -func TestResize2(t *testing.T) { - var a Vector - checkSize(t, &a, 0, 0) - a.Push(int2Value(1)) - a.Push(int2Value(2)) - a.Push(int2Value(3)) - a.Push(int2Value(4)) - checkSize(t, &a, 4, 4) - checkSize(t, a.Resize(10, 0), 10, 10) - for i := 4; i < a.Len(); i++ { - if a.At(i) != zero { - t.Errorf("%T: expected a.At(%d) == %v; found %v!", a, i, zero, a.At(i)) - } - } - for i := 4; i < len(a); i++ { - if a[i] != zero { - t.Errorf("%T: expected a[%d] == %v; found %v", a, i, zero, a[i]) - } - } -} - -func checkZero(t *testing.T, a *Vector, i int) { - for j := 0; j < i; j++ { - if a.At(j) == zero { - t.Errorf("%T: 1 expected a.At(%d) == %d; found %v", a, j, j, a.At(j)) - } - if (*a)[j] == zero { - t.Errorf("%T: 2 expected (*a)[%d] == %d; found %v", a, j, j, (*a)[j]) - } - } - for ; i < a.Len(); i++ { - if a.At(i) != zero { - t.Errorf("%T: 3 expected a.At(%d) == %v; found %v", a, i, zero, a.At(i)) - } - if (*a)[i] != zero { - t.Errorf("%T: 4 expected (*a)[%d] == %v; found %v", a, i, zero, (*a)[i]) - } - } -} - -func TestTrailingElements(t *testing.T) { - var a Vector - for i := 0; i < 10; i++ { - a.Push(int2Value(i + 1)) - } - checkZero(t, &a, 10) - checkSize(t, &a, 10, 16) - checkSize(t, a.Resize(5, 0), 5, 16) - checkSize(t, a.Resize(10, 0), 10, 16) - checkZero(t, &a, 5) -} - -func TestAccess(t *testing.T) { - const n = 100 - var a Vector - a.Resize(n, 0) - for i := 0; i < n; i++ { - a.Set(i, int2Value(val(i))) - } - for i := 0; i < n; i++ { - if elem2Value(a.At(i)) != int2Value(val(i)) { - t.Error(i) - } - } - var b Vector - b.Resize(n, 0) - for i := 0; i < n; i++ { - b[i] = int2Value(val(i)) - } - for i := 0; i < n; i++ { - if elem2Value(b[i]) != int2Value(val(i)) { - t.Error(i) - } - } -} - -func TestInsertDeleteClear(t *testing.T) { - const n = 100 - var a Vector - - for i := 0; i < n; i++ { - if a.Len() != i { - t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i) - } - if len(a) != i { - t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i) - } - a.Insert(0, int2Value(val(i))) - if elem2Value(a.Last()) != int2Value(val(0)) { - t.Errorf("%T: B", a) - } - } - for i := n - 1; i >= 0; i-- { - if elem2Value(a.Last()) != int2Value(val(0)) { - t.Errorf("%T: C", a) - } - if elem2Value(a.At(0)) != int2Value(val(i)) { - t.Errorf("%T: D", a) - } - if elem2Value(a[0]) != int2Value(val(i)) { - t.Errorf("%T: D2", a) - } - a.Delete(0) - if a.Len() != i { - t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i) - } - if len(a) != i { - t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i) - } - } - - if a.Len() != 0 { - t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len()) - } - if len(a) != 0 { - t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a)) - } - for i := 0; i < n; i++ { - a.Push(int2Value(val(i))) - if a.Len() != i+1 { - t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1) - } - if len(a) != i+1 { - t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1) - } - if elem2Value(a.Last()) != int2Value(val(i)) { - t.Errorf("%T: H", a) - } - } - a.Resize(0, 0) - if a.Len() != 0 { - t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len()) - } - if len(a) != 0 { - t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a)) - } - - const m = 5 - for j := 0; j < m; j++ { - a.Push(int2Value(j)) - for i := 0; i < n; i++ { - x := val(i) - a.Push(int2Value(x)) - if elem2Value(a.Pop()) != int2Value(x) { - t.Errorf("%T: J", a) - } - if a.Len() != j+1 { - t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1) - } - if len(a) != j+1 { - t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1) - } - } - } - if a.Len() != m { - t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m) - } - if len(a) != m { - t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m) - } -} - -func verify_slice(t *testing.T, x *Vector, elt, i, j int) { - for k := i; k < j; k++ { - if elem2Value(x.At(k)) != int2Value(elt) { - t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2Value(x.At(k)), int2Value(elt)) - } - } - - s := x.Slice(i, j) - for k, n := 0, j-i; k < n; k++ { - if elem2Value(s.At(k)) != int2Value(elt) { - t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2Value(x.At(k)), int2Value(elt)) - } - } -} - -func verify_pattern(t *testing.T, x *Vector, a, b, c int) { - n := a + b + c - if x.Len() != n { - t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n) - } - if len(*x) != n { - t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n) - } - verify_slice(t, x, 0, 0, a) - verify_slice(t, x, 1, a, a+b) - verify_slice(t, x, 0, a+b, n) -} - -func make_vector(elt, len int) *Vector { - x := new(Vector).Resize(len, 0) - for i := 0; i < len; i++ { - x.Set(i, int2Value(elt)) - } - return x -} - -func TestInsertVector(t *testing.T) { - // 1 - a := make_vector(0, 0) - b := make_vector(1, 10) - a.InsertVector(0, b) - verify_pattern(t, a, 0, 10, 0) - // 2 - a = make_vector(0, 10) - b = make_vector(1, 0) - a.InsertVector(5, b) - verify_pattern(t, a, 5, 0, 5) - // 3 - a = make_vector(0, 10) - b = make_vector(1, 3) - a.InsertVector(3, b) - verify_pattern(t, a, 3, 3, 7) - // 4 - a = make_vector(0, 10) - b = make_vector(1, 1000) - a.InsertVector(8, b) - verify_pattern(t, a, 8, 1000, 2) -} - -func TestDo(t *testing.T) { - const n = 25 - const salt = 17 - a := new(Vector).Resize(n, 0) - for i := 0; i < n; i++ { - a.Set(i, int2Value(salt*i)) - } - count := 0 - a.Do(func(e interface{}) { - i := intf2Value(e) - if i != int2Value(count*salt) { - t.Error(tname(a), "value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(a), "should visit", n, "values; did visit", count) - } - - b := new(Vector).Resize(n, 0) - for i := 0; i < n; i++ { - (*b)[i] = int2Value(salt * i) - } - count = 0 - b.Do(func(e interface{}) { - i := intf2Value(e) - if i != int2Value(count*salt) { - t.Error(tname(b), "b) value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(b), "b) should visit", n, "values; did visit", count) - } - - var c Vector - c.Resize(n, 0) - for i := 0; i < n; i++ { - c[i] = int2Value(salt * i) - } - count = 0 - c.Do(func(e interface{}) { - i := intf2Value(e) - if i != int2Value(count*salt) { - t.Error(tname(c), "c) value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(c), "c) should visit", n, "values; did visit", count) - } - -} - -func TestVectorCopy(t *testing.T) { - // verify Copy() returns a copy, not simply a slice of the original vector - const Len = 10 - var src Vector - for i := 0; i < Len; i++ { - src.Push(int2Value(i * i)) - } - dest := src.Copy() - for i := 0; i < Len; i++ { - src[i] = int2Value(-1) - v := elem2Value(dest[i]) - if v != int2Value(i*i) { - t.Error(tname(src), "expected", i*i, "got", v) - } - } -} diff --git a/src/pkg/crypto/Makefile b/src/pkg/crypto/Makefile deleted file mode 100644 index 738a52062..000000000 --- a/src/pkg/crypto/Makefile +++ /dev/null @@ -1,11 +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. - -include ../../Make.inc - -TARG=crypto -GOFILES=\ - crypto.go\ - -include ../../Make.pkg diff --git a/src/pkg/crypto/aes/Makefile b/src/pkg/crypto/aes/Makefile deleted file mode 100644 index 9dc846ee3..000000000 --- a/src/pkg/crypto/aes/Makefile +++ /dev/null @@ -1,13 +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 ../../../Make.inc - -TARG=crypto/aes -GOFILES=\ - block.go\ - cipher.go\ - const.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/aes/aes_test.go b/src/pkg/crypto/aes/aes_test.go index 2136d447d..e500c666d 100644 --- a/src/pkg/crypto/aes/aes_test.go +++ b/src/pkg/crypto/aes/aes_test.go @@ -91,6 +91,7 @@ func TestTe(t *testing.T) { s2 := mul(s, 2) s3 := mul(s, 3) w := s2<<24 | s<<16 | s<<8 | s3 + te := [][256]uint32{te0, te1, te2, te3} for j := 0; j < 4; j++ { if x := te[j][i]; x != w { t.Fatalf("te[%d][%d] = %#x, want %#x", j, i, x, w) @@ -110,6 +111,7 @@ func TestTd(t *testing.T) { sd := mul(s, 0xd) se := mul(s, 0xe) w := se<<24 | s9<<16 | sd<<8 | sb + td := [][256]uint32{td0, td1, td2, td3} for j := 0; j < 4; j++ { if x := td[j][i]; x != w { t.Fatalf("td[%d][%d] = %#x, want %#x", j, i, x, w) @@ -348,3 +350,17 @@ 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() + for i := 0; i < b.N; i++ { + c.Encrypt(out, tt.in) + } +} diff --git a/src/pkg/crypto/aes/block.go b/src/pkg/crypto/aes/block.go index 130cd011c..b930787ce 100644 --- a/src/pkg/crypto/aes/block.go +++ b/src/pkg/crypto/aes/block.go @@ -56,10 +56,10 @@ func encryptBlock(xk []uint32, dst, src []byte) { nr := len(xk)/4 - 2 // - 2: one above, one more below k := 4 for r := 0; r < nr; r++ { - t0 = xk[k+0] ^ te[0][s0>>24] ^ te[1][s1>>16&0xff] ^ te[2][s2>>8&0xff] ^ te[3][s3&0xff] - t1 = xk[k+1] ^ te[0][s1>>24] ^ te[1][s2>>16&0xff] ^ te[2][s3>>8&0xff] ^ te[3][s0&0xff] - t2 = xk[k+2] ^ te[0][s2>>24] ^ te[1][s3>>16&0xff] ^ te[2][s0>>8&0xff] ^ te[3][s1&0xff] - t3 = xk[k+3] ^ te[0][s3>>24] ^ te[1][s0>>16&0xff] ^ te[2][s1>>8&0xff] ^ te[3][s2&0xff] + t0 = xk[k+0] ^ te0[uint8(s0>>24)] ^ te1[uint8(s1>>16)] ^ te2[uint8(s2>>8)] ^ te3[uint8(s3)] + t1 = xk[k+1] ^ te0[uint8(s1>>24)] ^ te1[uint8(s2>>16)] ^ te2[uint8(s3>>8)] ^ te3[uint8(s0)] + t2 = xk[k+2] ^ te0[uint8(s2>>24)] ^ te1[uint8(s3>>16)] ^ te2[uint8(s0>>8)] ^ te3[uint8(s1)] + t3 = xk[k+3] ^ te0[uint8(s3>>24)] ^ te1[uint8(s0>>16)] ^ te2[uint8(s1>>8)] ^ te3[uint8(s2)] k += 4 s0, s1, s2, s3 = t0, t1, t2, t3 } @@ -101,10 +101,10 @@ func decryptBlock(xk []uint32, dst, src []byte) { nr := len(xk)/4 - 2 // - 2: one above, one more below k := 4 for r := 0; r < nr; r++ { - t0 = xk[k+0] ^ td[0][s0>>24] ^ td[1][s3>>16&0xff] ^ td[2][s2>>8&0xff] ^ td[3][s1&0xff] - t1 = xk[k+1] ^ td[0][s1>>24] ^ td[1][s0>>16&0xff] ^ td[2][s3>>8&0xff] ^ td[3][s2&0xff] - t2 = xk[k+2] ^ td[0][s2>>24] ^ td[1][s1>>16&0xff] ^ td[2][s0>>8&0xff] ^ td[3][s3&0xff] - t3 = xk[k+3] ^ td[0][s3>>24] ^ td[1][s2>>16&0xff] ^ td[2][s1>>8&0xff] ^ td[3][s0&0xff] + t0 = xk[k+0] ^ td0[uint8(s0>>24)] ^ td1[uint8(s3>>16)] ^ td2[uint8(s2>>8)] ^ td3[uint8(s1)] + t1 = xk[k+1] ^ td0[uint8(s1>>24)] ^ td1[uint8(s0>>16)] ^ td2[uint8(s3>>8)] ^ td3[uint8(s2)] + t2 = xk[k+2] ^ td0[uint8(s2>>24)] ^ td1[uint8(s1>>16)] ^ td2[uint8(s0>>8)] ^ td3[uint8(s3)] + t3 = xk[k+3] ^ td0[uint8(s3>>24)] ^ td1[uint8(s2>>16)] ^ td2[uint8(s1>>8)] ^ td3[uint8(s0)] k += 4 s0, s1, s2, s3 = t0, t1, t2, t3 } @@ -168,7 +168,7 @@ func expandKey(key []byte, enc, dec []uint32) { for j := 0; j < 4; j++ { x := enc[ei+j] if i > 0 && i+4 < n { - x = td[0][sbox0[x>>24]] ^ td[1][sbox0[x>>16&0xff]] ^ td[2][sbox0[x>>8&0xff]] ^ td[3][sbox0[x&0xff]] + x = td0[sbox0[x>>24]] ^ td1[sbox0[x>>16&0xff]] ^ td2[sbox0[x>>8&0xff]] ^ td3[sbox0[x&0xff]] } dec[i+j] = x } diff --git a/src/pkg/crypto/aes/cipher.go b/src/pkg/crypto/aes/cipher.go index 73223531e..7d307c93a 100644 --- a/src/pkg/crypto/aes/cipher.go +++ b/src/pkg/crypto/aes/cipher.go @@ -5,30 +5,30 @@ package aes import ( - "os" + "crypto/cipher" "strconv" ) // The AES block size in bytes. const BlockSize = 16 -// A Cipher is an instance of AES encryption using a particular key. -type Cipher struct { +// A cipher is an instance of AES encryption using a particular key. +type aesCipher struct { enc []uint32 dec []uint32 } type KeySizeError int -func (k KeySizeError) String() string { +func (k KeySizeError) Error() string { return "crypto/aes: invalid key size " + strconv.Itoa(int(k)) } -// NewCipher creates and returns a new Cipher. +// NewCipher creates and returns a new cipher.Block. // The key argument should be the AES key, // either 16, 24, or 32 bytes to select // AES-128, AES-192, or AES-256. -func NewCipher(key []byte) (*Cipher, os.Error) { +func NewCipher(key []byte) (cipher.Block, error) { k := len(key) switch k { default: @@ -38,34 +38,13 @@ func NewCipher(key []byte) (*Cipher, os.Error) { } n := k + 28 - c := &Cipher{make([]uint32, n), make([]uint32, n)} + c := &aesCipher{make([]uint32, n), make([]uint32, n)} expandKey(key, c.enc, c.dec) return c, nil } -// BlockSize returns the AES block size, 16 bytes. -// It is necessary to satisfy the Cipher interface in the -// package "crypto/cipher". -func (c *Cipher) BlockSize() int { return BlockSize } +func (c *aesCipher) BlockSize() int { return BlockSize } -// Encrypt encrypts the 16-byte buffer src using the key k -// and stores the result in dst. -// Note that for amounts of data larger than a block, -// it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). -func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c.enc, dst, src) } +func (c *aesCipher) Encrypt(dst, src []byte) { encryptBlock(c.enc, dst, src) } -// Decrypt decrypts the 16-byte buffer src using the key k -// and stores the result in dst. -func (c *Cipher) Decrypt(dst, src []byte) { decryptBlock(c.dec, dst, src) } - -// Reset zeros the key data, so that it will no longer -// appear in the process's memory. -func (c *Cipher) Reset() { - for i := 0; i < len(c.enc); i++ { - c.enc[i] = 0 - } - for i := 0; i < len(c.dec); i++ { - c.dec[i] = 0 - } -} +func (c *aesCipher) Decrypt(dst, src []byte) { decryptBlock(c.dec, dst, src) } diff --git a/src/pkg/crypto/aes/const.go b/src/pkg/crypto/aes/const.go index 25acd0d17..f0b4eabf6 100644 --- a/src/pkg/crypto/aes/const.go +++ b/src/pkg/crypto/aes/const.go @@ -80,283 +80,279 @@ var sbox1 = [256]byte{ // Lookup tables for encryption. // These can be recomputed by adapting the tests in aes_test.go. -var te = [4][256]uint32{ - { - 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, - 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, - 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, - 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, - 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, - 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, - 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, - 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, - 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, - 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, - 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, - 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, - 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, - 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, - 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, - 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, - 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, - 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, - 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, - 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, - 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, - 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, - 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, - 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, - 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, - 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, - 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, - 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, - 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, - 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, - 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, - 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, - }, - { - 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, - 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, - 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, - 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, - 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, - 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, - 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, - 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, - 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, - 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, - 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, - 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, - 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, - 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, - 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, - 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, - 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, - 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, - 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, - 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, - 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, - 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, - 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, - 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, - 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, - 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, - 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, - 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, - 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, - 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, - 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, - 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, - }, - { - 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, - 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, - 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, - 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, - 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, - 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, - 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, - 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, - 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, - 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, - 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, - 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, - 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, - 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, - 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, - 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, - 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, - 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, - 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, - 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, - 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, - 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, - 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, - 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, - 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, - 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, - 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, - 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, - 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, - 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, - 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, - 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, - }, - { - 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, - 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, - 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, - 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, - 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, - 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, - 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, - 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, - 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, - 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, - 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, - 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, - 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, - 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, - 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, - 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, - 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, - 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, - 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, - 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, - 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, - 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, - 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, - 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, - 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, - 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, - 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, - 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, - 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, - 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, - 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, - 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, - }, +var te0 = [256]uint32{ + 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, + 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, + 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, + 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, + 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, + 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, + 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, + 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, + 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, + 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, + 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, + 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, + 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, + 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, + 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, + 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, + 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, + 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, + 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, + 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, + 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, + 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, + 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, + 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, + 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, + 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, + 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, + 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, + 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, + 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, + 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, + 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, +} +var te1 = [256]uint32{ + 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, + 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, + 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, + 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, + 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, + 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, + 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, + 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, + 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, + 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, + 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, + 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, + 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, + 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, + 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, + 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, + 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, + 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, + 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, + 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, + 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, + 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, + 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, + 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, + 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, + 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, + 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, + 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, + 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, + 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, + 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, + 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, +} +var te2 = [256]uint32{ + 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, + 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, + 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, + 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, + 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, + 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, + 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, + 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, + 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, + 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, + 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, + 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, + 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, + 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, + 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, + 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, + 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, + 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, + 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, + 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, + 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, + 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, + 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, + 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, + 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, + 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, + 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, + 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, + 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, + 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, + 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, + 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, +} +var te3 = [256]uint32{ + 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, + 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, + 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, + 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, + 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, + 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, + 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, + 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, + 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, + 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, + 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, + 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, + 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, + 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, + 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, + 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, + 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, + 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, + 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, + 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, + 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, + 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, + 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, + 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, + 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, + 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, + 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, + 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, + 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, + 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, + 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, + 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, } // Lookup tables for decryption. // These can be recomputed by adapting the tests in aes_test.go. -var td = [4][256]uint32{ - { - 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, - 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, - 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, - 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, - 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, - 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, - 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, - 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, - 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, - 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, - 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, - 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, - 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, - 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, - 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, - 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, - 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, - 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, - 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, - 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, - 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, - 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, - 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, - 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, - 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, - 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, - 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, - 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, - 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, - 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, - 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, - 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742, - }, - { - 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, - 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, - 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, - 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, - 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, - 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, - 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, - 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, - 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, - 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, - 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, - 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, - 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, - 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, - 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, - 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, - 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, - 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, - 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, - 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, - 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, - 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, - 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, - 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, - 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, - 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, - 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, - 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, - 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, - 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, - 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, - 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, - }, - { - 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, - 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, - 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, - 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, - 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, - 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, - 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, - 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, - 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, - 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, - 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, - 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, - 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, - 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, - 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, - 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, - 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, - 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, - 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, - 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, - 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, - 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, - 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, - 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, - 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, - 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, - 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, - 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, - 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, - 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, - 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, - 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8, - }, - { - 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, - 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, - 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, - 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, - 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, - 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, - 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, - 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, - 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, - 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, - 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, - 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, - 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, - 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, - 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, - 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, - 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, - 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, - 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, - 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, - 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, - 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, - 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, - 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, - 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, - 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, - 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, - 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, - 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, - 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, - 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, - 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0, - }, +var td0 = [256]uint32{ + 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, + 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, + 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, + 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, + 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, + 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, + 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, + 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, + 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, + 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, + 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, + 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, + 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, + 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, + 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, + 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, + 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, + 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, + 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, + 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, + 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, + 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, + 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, + 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, + 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, + 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, + 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, + 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, + 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, + 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, + 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, + 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742, +} +var td1 = [256]uint32{ + 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, + 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, + 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, + 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, + 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, + 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, + 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, + 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, + 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, + 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, + 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, + 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, + 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, + 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, + 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, + 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, + 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, + 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, + 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, + 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, + 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, + 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, + 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, + 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, + 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, + 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, + 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, + 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, + 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, + 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, + 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, + 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, +} +var td2 = [256]uint32{ + 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, + 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, + 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, + 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, + 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, + 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, + 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, + 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, + 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, + 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, + 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, + 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, + 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, + 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, + 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, + 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, + 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, + 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, + 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, + 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, + 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, + 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, + 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, + 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, + 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, + 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, + 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, + 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, + 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, + 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, + 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, + 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8, +} +var td3 = [256]uint32{ + 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, + 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, + 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, + 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, + 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, + 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, + 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, + 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, + 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, + 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, + 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, + 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, + 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, + 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, + 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, + 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, + 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, + 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, + 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, + 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, + 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, + 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, + 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, + 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, + 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, + 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, + 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, + 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, + 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, + 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, + 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, + 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0, } diff --git a/src/pkg/crypto/blowfish/Makefile b/src/pkg/crypto/blowfish/Makefile deleted file mode 100644 index f370ab28b..000000000 --- a/src/pkg/crypto/blowfish/Makefile +++ /dev/null @@ -1,13 +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. - -include ../../../Make.inc - -TARG=crypto/blowfish -GOFILES=\ - block.go\ - cipher.go\ - const.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/blowfish/block.go b/src/pkg/crypto/blowfish/block.go deleted file mode 100644 index 7fbe7eefb..000000000 --- a/src/pkg/crypto/blowfish/block.go +++ /dev/null @@ -1,101 +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. - -package blowfish - -func expandKey(key []byte, c *Cipher) { - copy(c.p[0:], p[0:]) - copy(c.s0[0:], s0[0:]) - copy(c.s1[0:], s1[0:]) - copy(c.s2[0:], s2[0:]) - copy(c.s3[0:], s3[0:]) - - j := 0 - for i := 0; i < 18; i++ { - var d uint32 - for k := 0; k < 4; k++ { - d = d<<8 | uint32(key[j])&0x000000FF - j++ - if j >= len(key) { - j = 0 - } - } - c.p[i] ^= d - } - - var l, r uint32 - for i := 0; i < 18; i += 2 { - l, r = encryptBlock(l, r, c) - c.p[i], c.p[i+1] = l, r - } - - for i := 0; i < 256; i += 2 { - l, r = encryptBlock(l, r, c) - c.s0[i], c.s0[i+1] = l, r - } - for i := 0; i < 256; i += 2 { - l, r = encryptBlock(l, r, c) - c.s1[i], c.s1[i+1] = l, r - } - for i := 0; i < 256; i += 2 { - l, r = encryptBlock(l, r, c) - c.s2[i], c.s2[i+1] = l, r - } - for i := 0; i < 256; i += 2 { - l, r = encryptBlock(l, r, c) - c.s3[i], c.s3[i+1] = l, r - } -} - -func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { - xl, xr := l, r - xl ^= c.p[0] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16] - xr ^= c.p[17] - return xr, xl -} - -func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { - xl, xr := l, r - xl ^= c.p[17] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1] - xr ^= c.p[0] - return xr, xl -} - -func zero(x []uint32) { - for i := range x { - x[i] = 0 - } -} diff --git a/src/pkg/crypto/blowfish/blowfish_test.go b/src/pkg/crypto/blowfish/blowfish_test.go deleted file mode 100644 index 3a7ab6c2a..000000000 --- a/src/pkg/crypto/blowfish/blowfish_test.go +++ /dev/null @@ -1,192 +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. - -package blowfish - -import ( - "testing" -) - -type CryptTest struct { - key []byte - in []byte - out []byte -} - -// Test vector values are from http://www.schneier.com/code/vectors.txt. -var encryptTests = []CryptTest{ - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}}, - { - []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - []byte{0x51, 0x86, 0x6F, 0xD5, 0xB8, 0x5E, 0xCB, 0x8A}}, - { - []byte{0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, - []byte{0x7D, 0x85, 0x6F, 0x9A, 0x61, 0x30, 0x63, 0xF2}}, - { - []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, - []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, - []byte{0x24, 0x66, 0xDD, 0x87, 0x8B, 0x96, 0x3C, 0x9D}}, - - { - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, - []byte{0x61, 0xF9, 0xC3, 0x80, 0x22, 0x81, 0xB0, 0x96}}, - { - []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0x7D, 0x0C, 0xC6, 0x30, 0xAF, 0xDA, 0x1E, 0xC7}}, - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}}, - { - []byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0x0A, 0xCE, 0xAB, 0x0F, 0xC6, 0xA0, 0xA2, 0x8D}}, - { - []byte{0x7C, 0xA1, 0x10, 0x45, 0x4A, 0x1A, 0x6E, 0x57}, - []byte{0x01, 0xA1, 0xD6, 0xD0, 0x39, 0x77, 0x67, 0x42}, - []byte{0x59, 0xC6, 0x82, 0x45, 0xEB, 0x05, 0x28, 0x2B}}, - { - []byte{0x01, 0x31, 0xD9, 0x61, 0x9D, 0xC1, 0x37, 0x6E}, - []byte{0x5C, 0xD5, 0x4C, 0xA8, 0x3D, 0xEF, 0x57, 0xDA}, - []byte{0xB1, 0xB8, 0xCC, 0x0B, 0x25, 0x0F, 0x09, 0xA0}}, - { - []byte{0x07, 0xA1, 0x13, 0x3E, 0x4A, 0x0B, 0x26, 0x86}, - []byte{0x02, 0x48, 0xD4, 0x38, 0x06, 0xF6, 0x71, 0x72}, - []byte{0x17, 0x30, 0xE5, 0x77, 0x8B, 0xEA, 0x1D, 0xA4}}, - { - []byte{0x38, 0x49, 0x67, 0x4C, 0x26, 0x02, 0x31, 0x9E}, - []byte{0x51, 0x45, 0x4B, 0x58, 0x2D, 0xDF, 0x44, 0x0A}, - []byte{0xA2, 0x5E, 0x78, 0x56, 0xCF, 0x26, 0x51, 0xEB}}, - { - []byte{0x04, 0xB9, 0x15, 0xBA, 0x43, 0xFE, 0xB5, 0xB6}, - []byte{0x42, 0xFD, 0x44, 0x30, 0x59, 0x57, 0x7F, 0xA2}, - []byte{0x35, 0x38, 0x82, 0xB1, 0x09, 0xCE, 0x8F, 0x1A}}, - { - []byte{0x01, 0x13, 0xB9, 0x70, 0xFD, 0x34, 0xF2, 0xCE}, - []byte{0x05, 0x9B, 0x5E, 0x08, 0x51, 0xCF, 0x14, 0x3A}, - []byte{0x48, 0xF4, 0xD0, 0x88, 0x4C, 0x37, 0x99, 0x18}}, - { - []byte{0x01, 0x70, 0xF1, 0x75, 0x46, 0x8F, 0xB5, 0xE6}, - []byte{0x07, 0x56, 0xD8, 0xE0, 0x77, 0x47, 0x61, 0xD2}, - []byte{0x43, 0x21, 0x93, 0xB7, 0x89, 0x51, 0xFC, 0x98}}, - { - []byte{0x43, 0x29, 0x7F, 0xAD, 0x38, 0xE3, 0x73, 0xFE}, - []byte{0x76, 0x25, 0x14, 0xB8, 0x29, 0xBF, 0x48, 0x6A}, - []byte{0x13, 0xF0, 0x41, 0x54, 0xD6, 0x9D, 0x1A, 0xE5}}, - { - []byte{0x07, 0xA7, 0x13, 0x70, 0x45, 0xDA, 0x2A, 0x16}, - []byte{0x3B, 0xDD, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02}, - []byte{0x2E, 0xED, 0xDA, 0x93, 0xFF, 0xD3, 0x9C, 0x79}}, - { - []byte{0x04, 0x68, 0x91, 0x04, 0xC2, 0xFD, 0x3B, 0x2F}, - []byte{0x26, 0x95, 0x5F, 0x68, 0x35, 0xAF, 0x60, 0x9A}, - []byte{0xD8, 0x87, 0xE0, 0x39, 0x3C, 0x2D, 0xA6, 0xE3}}, - { - []byte{0x37, 0xD0, 0x6B, 0xB5, 0x16, 0xCB, 0x75, 0x46}, - []byte{0x16, 0x4D, 0x5E, 0x40, 0x4F, 0x27, 0x52, 0x32}, - []byte{0x5F, 0x99, 0xD0, 0x4F, 0x5B, 0x16, 0x39, 0x69}}, - { - []byte{0x1F, 0x08, 0x26, 0x0D, 0x1A, 0xC2, 0x46, 0x5E}, - []byte{0x6B, 0x05, 0x6E, 0x18, 0x75, 0x9F, 0x5C, 0xCA}, - []byte{0x4A, 0x05, 0x7A, 0x3B, 0x24, 0xD3, 0x97, 0x7B}}, - { - []byte{0x58, 0x40, 0x23, 0x64, 0x1A, 0xBA, 0x61, 0x76}, - []byte{0x00, 0x4B, 0xD6, 0xEF, 0x09, 0x17, 0x60, 0x62}, - []byte{0x45, 0x20, 0x31, 0xC1, 0xE4, 0xFA, 0xDA, 0x8E}}, - { - []byte{0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xB0, 0x07}, - []byte{0x48, 0x0D, 0x39, 0x00, 0x6E, 0xE7, 0x62, 0xF2}, - []byte{0x75, 0x55, 0xAE, 0x39, 0xF5, 0x9B, 0x87, 0xBD}}, - { - []byte{0x49, 0x79, 0x3E, 0xBC, 0x79, 0xB3, 0x25, 0x8F}, - []byte{0x43, 0x75, 0x40, 0xC8, 0x69, 0x8F, 0x3C, 0xFA}, - []byte{0x53, 0xC5, 0x5F, 0x9C, 0xB4, 0x9F, 0xC0, 0x19}}, - { - []byte{0x4F, 0xB0, 0x5E, 0x15, 0x15, 0xAB, 0x73, 0xA7}, - []byte{0x07, 0x2D, 0x43, 0xA0, 0x77, 0x07, 0x52, 0x92}, - []byte{0x7A, 0x8E, 0x7B, 0xFA, 0x93, 0x7E, 0x89, 0xA3}}, - { - []byte{0x49, 0xE9, 0x5D, 0x6D, 0x4C, 0xA2, 0x29, 0xBF}, - []byte{0x02, 0xFE, 0x55, 0x77, 0x81, 0x17, 0xF1, 0x2A}, - []byte{0xCF, 0x9C, 0x5D, 0x7A, 0x49, 0x86, 0xAD, 0xB5}}, - { - []byte{0x01, 0x83, 0x10, 0xDC, 0x40, 0x9B, 0x26, 0xD6}, - []byte{0x1D, 0x9D, 0x5C, 0x50, 0x18, 0xF7, 0x28, 0xC2}, - []byte{0xD1, 0xAB, 0xB2, 0x90, 0x65, 0x8B, 0xC7, 0x78}}, - { - []byte{0x1C, 0x58, 0x7F, 0x1C, 0x13, 0x92, 0x4F, 0xEF}, - []byte{0x30, 0x55, 0x32, 0x28, 0x6D, 0x6F, 0x29, 0x5A}, - []byte{0x55, 0xCB, 0x37, 0x74, 0xD1, 0x3E, 0xF2, 0x01}}, - { - []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0xFA, 0x34, 0xEC, 0x48, 0x47, 0xB2, 0x68, 0xB2}}, - { - []byte{0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E}, - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0xA7, 0x90, 0x79, 0x51, 0x08, 0xEA, 0x3C, 0xAE}}, - { - []byte{0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE}, - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0xC3, 0x9E, 0x07, 0x2D, 0x9F, 0xAC, 0x63, 0x1D}}, - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - []byte{0x01, 0x49, 0x33, 0xE0, 0xCD, 0xAF, 0xF6, 0xE4}}, - { - []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0xF2, 0x1E, 0x9A, 0x77, 0xB7, 0x1C, 0x49, 0xBC}}, - { - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x24, 0x59, 0x46, 0x88, 0x57, 0x54, 0x36, 0x9A}}, - { - []byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, - []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - []byte{0x6B, 0x5C, 0x5A, 0x9C, 0x5D, 0x9E, 0x0A, 0x5A}}, -} - -func TestCipherEncrypt(t *testing.T) { - for i, tt := range encryptTests { - c, err := NewCipher(tt.key) - if err != nil { - t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err) - continue - } - ct := make([]byte, len(tt.out)) - c.Encrypt(ct, tt.in) - for j, v := range ct { - if v != tt.out[j] { - t.Errorf("Cipher.Encrypt, test vector #%d: cipher-text[%d] = %#x, expected %#x", i, j, v, tt.out[j]) - break - } - } - } -} - -func TestCipherDecrypt(t *testing.T) { - for i, tt := range encryptTests { - c, err := NewCipher(tt.key) - if err != nil { - t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err) - continue - } - pt := make([]byte, len(tt.in)) - c.Decrypt(pt, tt.out) - for j, v := range pt { - if v != tt.in[j] { - t.Errorf("Cipher.Decrypt, test vector #%d: plain-text[%d] = %#x, expected %#x", i, j, v, tt.in[j]) - break - } - } - } -} diff --git a/src/pkg/crypto/blowfish/cipher.go b/src/pkg/crypto/blowfish/cipher.go deleted file mode 100644 index 6c37dfe94..000000000 --- a/src/pkg/crypto/blowfish/cipher.go +++ /dev/null @@ -1,79 +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. - -// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm. -package blowfish - -// The code is a port of Bruce Schneier's C implementation. -// See http://www.schneier.com/blowfish.html. - -import ( - "os" - "strconv" -) - -// The Blowfish block size in bytes. -const BlockSize = 8 - -// A Cipher is an instance of Blowfish encryption using a particular key. -type Cipher struct { - p [18]uint32 - s0, s1, s2, s3 [256]uint32 -} - -type KeySizeError int - -func (k KeySizeError) String() string { - return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k)) -} - -// NewCipher creates and returns a Cipher. -// The key argument should be the Blowfish key, 4 to 56 bytes. -func NewCipher(key []byte) (*Cipher, os.Error) { - k := len(key) - if k < 4 || k > 56 { - return nil, KeySizeError(k) - } - var result Cipher - expandKey(key, &result) - return &result, nil -} - -// BlockSize returns the Blowfish block size, 8 bytes. -// It is necessary to satisfy the Cipher interface in the -// package "crypto/cipher". -func (c *Cipher) BlockSize() int { return BlockSize } - -// Encrypt encrypts the 8-byte buffer src using the key k -// and stores the result in dst. -// Note that for amounts of data larger than a block, -// it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). -func (c *Cipher) Encrypt(dst, src []byte) { - l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) - r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) - l, r = encryptBlock(l, r, c) - dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) - dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) -} - -// Decrypt decrypts the 8-byte buffer src using the key k -// and stores the result in dst. -func (c *Cipher) Decrypt(dst, src []byte) { - l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) - r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) - l, r = decryptBlock(l, r, c) - dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) - dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) -} - -// Reset zeros the key data, so that it will no longer -// appear in the process's memory. -func (c *Cipher) Reset() { - zero(c.p[0:]) - zero(c.s0[0:]) - zero(c.s1[0:]) - zero(c.s2[0:]) - zero(c.s3[0:]) -} diff --git a/src/pkg/crypto/blowfish/const.go b/src/pkg/crypto/blowfish/const.go deleted file mode 100644 index 8c5ee4cb0..000000000 --- a/src/pkg/crypto/blowfish/const.go +++ /dev/null @@ -1,199 +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. - -// The startup permutation array and substitution boxes. -// They are the hexadecimal digits of PI; see: -// http://www.schneier.com/code/constants.txt. - -package blowfish - -var s0 = [256]uint32{ - 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, - 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, - 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, - 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, - 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, - 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, - 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, - 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, - 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, - 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, - 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, - 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, - 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, - 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, - 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, - 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, - 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, - 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, - 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, - 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, - 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, - 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, - 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, - 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, - 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, - 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, - 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, - 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, - 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, - 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, - 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, - 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, - 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, - 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, - 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, - 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, - 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, - 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, - 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, - 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, - 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, - 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, - 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, -} - -var s1 = [256]uint32{ - 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, - 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, - 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, - 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, - 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, - 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, - 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, - 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, - 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, - 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, - 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, - 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, - 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, - 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, - 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, - 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, - 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, - 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, - 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, - 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, - 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, - 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, - 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, - 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, - 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, - 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, - 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, - 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, - 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, - 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, - 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, - 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, - 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, - 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, - 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, - 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, - 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, - 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, - 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, - 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, - 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, - 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, - 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, -} - -var s2 = [256]uint32{ - 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, - 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, - 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, - 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, - 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, - 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, - 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, - 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, - 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, - 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, - 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, - 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, - 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, - 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, - 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, - 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, - 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, - 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, - 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, - 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, - 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, - 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, - 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, - 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, - 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, - 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, - 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, - 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, - 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, - 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, - 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, - 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, - 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, - 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, - 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, - 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, - 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, - 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, - 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, - 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, - 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, - 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, - 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, -} - -var s3 = [256]uint32{ - 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, - 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, - 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, - 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, - 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, - 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, - 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, - 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, - 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, - 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, - 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, - 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, - 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, - 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, - 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, - 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, - 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, - 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, - 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, - 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, - 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, - 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, - 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, - 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, - 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, - 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, - 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, - 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, - 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, - 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, - 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, - 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, - 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, - 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, - 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, - 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, - 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, - 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, - 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, - 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, - 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, - 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, - 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, -} - -var p = [18]uint32{ - 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, - 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, - 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b, -} diff --git a/src/pkg/crypto/cast5/Makefile b/src/pkg/crypto/cast5/Makefile deleted file mode 100644 index 346fadd94..000000000 --- a/src/pkg/crypto/cast5/Makefile +++ /dev/null @@ -1,11 +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. - -include ../../../Make.inc - -TARG=crypto/cast5 -GOFILES=\ - cast5.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/cast5/cast5.go b/src/pkg/crypto/cast5/cast5.go deleted file mode 100644 index e9d4a24e2..000000000 --- a/src/pkg/crypto/cast5/cast5.go +++ /dev/null @@ -1,536 +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. - -// Package cast5 implements CAST5, as defined in RFC 2144. CAST5 is a common -// OpenPGP cipher. -package cast5 - -import ( - "os" -) - -const BlockSize = 8 -const KeySize = 16 - -type Cipher struct { - masking [16]uint32 - rotate [16]uint8 -} - -func NewCipher(key []byte) (c *Cipher, err os.Error) { - if len(key) != KeySize { - return nil, os.NewError("CAST5: keys must be 16 bytes") - } - - c = new(Cipher) - c.keySchedule(key) - return -} - -func (c *Cipher) BlockSize() int { - return BlockSize -} - -// Reset zeros the key material in memory. -func (c *Cipher) Reset() { - for i := 0; i < 16; i++ { - c.masking[i] = 0 - c.rotate[i] = 0 - } -} - -func (c *Cipher) Encrypt(dst, src []byte) { - l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) - r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) - - l, r = r, l^f1(r, c.masking[0], c.rotate[0]) - l, r = r, l^f2(r, c.masking[1], c.rotate[1]) - l, r = r, l^f3(r, c.masking[2], c.rotate[2]) - l, r = r, l^f1(r, c.masking[3], c.rotate[3]) - - l, r = r, l^f2(r, c.masking[4], c.rotate[4]) - l, r = r, l^f3(r, c.masking[5], c.rotate[5]) - l, r = r, l^f1(r, c.masking[6], c.rotate[6]) - l, r = r, l^f2(r, c.masking[7], c.rotate[7]) - - l, r = r, l^f3(r, c.masking[8], c.rotate[8]) - l, r = r, l^f1(r, c.masking[9], c.rotate[9]) - l, r = r, l^f2(r, c.masking[10], c.rotate[10]) - l, r = r, l^f3(r, c.masking[11], c.rotate[11]) - - l, r = r, l^f1(r, c.masking[12], c.rotate[12]) - l, r = r, l^f2(r, c.masking[13], c.rotate[13]) - l, r = r, l^f3(r, c.masking[14], c.rotate[14]) - l, r = r, l^f1(r, c.masking[15], c.rotate[15]) - - dst[0] = uint8(r >> 24) - dst[1] = uint8(r >> 16) - dst[2] = uint8(r >> 8) - dst[3] = uint8(r) - dst[4] = uint8(l >> 24) - dst[5] = uint8(l >> 16) - dst[6] = uint8(l >> 8) - dst[7] = uint8(l) -} - -func (c *Cipher) Decrypt(dst, src []byte) { - l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) - r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) - - l, r = r, l^f1(r, c.masking[15], c.rotate[15]) - l, r = r, l^f3(r, c.masking[14], c.rotate[14]) - l, r = r, l^f2(r, c.masking[13], c.rotate[13]) - l, r = r, l^f1(r, c.masking[12], c.rotate[12]) - - l, r = r, l^f3(r, c.masking[11], c.rotate[11]) - l, r = r, l^f2(r, c.masking[10], c.rotate[10]) - l, r = r, l^f1(r, c.masking[9], c.rotate[9]) - l, r = r, l^f3(r, c.masking[8], c.rotate[8]) - - l, r = r, l^f2(r, c.masking[7], c.rotate[7]) - l, r = r, l^f1(r, c.masking[6], c.rotate[6]) - l, r = r, l^f3(r, c.masking[5], c.rotate[5]) - l, r = r, l^f2(r, c.masking[4], c.rotate[4]) - - l, r = r, l^f1(r, c.masking[3], c.rotate[3]) - l, r = r, l^f3(r, c.masking[2], c.rotate[2]) - l, r = r, l^f2(r, c.masking[1], c.rotate[1]) - l, r = r, l^f1(r, c.masking[0], c.rotate[0]) - - dst[0] = uint8(r >> 24) - dst[1] = uint8(r >> 16) - dst[2] = uint8(r >> 8) - dst[3] = uint8(r) - dst[4] = uint8(l >> 24) - dst[5] = uint8(l >> 16) - dst[6] = uint8(l >> 8) - dst[7] = uint8(l) -} - -type keyScheduleA [4][7]uint8 -type keyScheduleB [4][5]uint8 - -// keyScheduleRound contains the magic values for a round of the key schedule. -// The keyScheduleA deals with the lines like: -// z0z1z2z3 = x0x1x2x3 ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8] -// Conceptually, both x and z are in the same array, x first. The first -// element describes which word of this array gets written to and the -// second, which word gets read. So, for the line above, it's "4, 0", because -// it's writing to the first word of z, which, being after x, is word 4, and -// reading from the first word of x: word 0. -// -// Next are the indexes into the S-boxes. Now the array is treated as bytes. So -// "xD" is 0xd. The first byte of z is written as "16 + 0", just to be clear -// that it's z that we're indexing. -// -// keyScheduleB deals with lines like: -// K1 = S5[z8] ^ S6[z9] ^ S7[z7] ^ S8[z6] ^ S5[z2] -// "K1" is ignored because key words are always written in order. So the five -// elements are the S-box indexes. They use the same form as in keyScheduleA, -// above. - -type keyScheduleRound struct{} -type keySchedule []keyScheduleRound - -var schedule = []struct { - a keyScheduleA - b keyScheduleB -}{ - { - keyScheduleA{ - {4, 0, 0xd, 0xf, 0xc, 0xe, 0x8}, - {5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa}, - {6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9}, - {7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb}, - }, - keyScheduleB{ - {16 + 8, 16 + 9, 16 + 7, 16 + 6, 16 + 2}, - {16 + 0xa, 16 + 0xb, 16 + 5, 16 + 4, 16 + 6}, - {16 + 0xc, 16 + 0xd, 16 + 3, 16 + 2, 16 + 9}, - {16 + 0xe, 16 + 0xf, 16 + 1, 16 + 0, 16 + 0xc}, - }, - }, - { - keyScheduleA{ - {0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0}, - {1, 4, 0, 2, 1, 3, 16 + 2}, - {2, 5, 7, 6, 5, 4, 16 + 1}, - {3, 7, 0xa, 9, 0xb, 8, 16 + 3}, - }, - keyScheduleB{ - {3, 2, 0xc, 0xd, 8}, - {1, 0, 0xe, 0xf, 0xd}, - {7, 6, 8, 9, 3}, - {5, 4, 0xa, 0xb, 7}, - }, - }, - { - keyScheduleA{ - {4, 0, 0xd, 0xf, 0xc, 0xe, 8}, - {5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa}, - {6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9}, - {7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb}, - }, - keyScheduleB{ - {16 + 3, 16 + 2, 16 + 0xc, 16 + 0xd, 16 + 9}, - {16 + 1, 16 + 0, 16 + 0xe, 16 + 0xf, 16 + 0xc}, - {16 + 7, 16 + 6, 16 + 8, 16 + 9, 16 + 2}, - {16 + 5, 16 + 4, 16 + 0xa, 16 + 0xb, 16 + 6}, - }, - }, - { - keyScheduleA{ - {0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0}, - {1, 4, 0, 2, 1, 3, 16 + 2}, - {2, 5, 7, 6, 5, 4, 16 + 1}, - {3, 7, 0xa, 9, 0xb, 8, 16 + 3}, - }, - keyScheduleB{ - {8, 9, 7, 6, 3}, - {0xa, 0xb, 5, 4, 7}, - {0xc, 0xd, 3, 2, 8}, - {0xe, 0xf, 1, 0, 0xd}, - }, - }, -} - -func (c *Cipher) keySchedule(in []byte) { - var t [8]uint32 - var k [32]uint32 - - for i := 0; i < 4; i++ { - j := i * 4 - t[i] = uint32(in[j])<<24 | uint32(in[j+1])<<16 | uint32(in[j+2])<<8 | uint32(in[j+3]) - } - - x := []byte{6, 7, 4, 5} - ki := 0 - - for half := 0; half < 2; half++ { - for _, round := range schedule { - for j := 0; j < 4; j++ { - var a [7]uint8 - copy(a[:], round.a[j][:]) - w := t[a[1]] - w ^= sBox[4][(t[a[2]>>2]>>(24-8*(a[2]&3)))&0xff] - w ^= sBox[5][(t[a[3]>>2]>>(24-8*(a[3]&3)))&0xff] - w ^= sBox[6][(t[a[4]>>2]>>(24-8*(a[4]&3)))&0xff] - w ^= sBox[7][(t[a[5]>>2]>>(24-8*(a[5]&3)))&0xff] - w ^= sBox[x[j]][(t[a[6]>>2]>>(24-8*(a[6]&3)))&0xff] - t[a[0]] = w - } - - for j := 0; j < 4; j++ { - var b [5]uint8 - copy(b[:], round.b[j][:]) - w := sBox[4][(t[b[0]>>2]>>(24-8*(b[0]&3)))&0xff] - w ^= sBox[5][(t[b[1]>>2]>>(24-8*(b[1]&3)))&0xff] - w ^= sBox[6][(t[b[2]>>2]>>(24-8*(b[2]&3)))&0xff] - w ^= sBox[7][(t[b[3]>>2]>>(24-8*(b[3]&3)))&0xff] - w ^= sBox[4+j][(t[b[4]>>2]>>(24-8*(b[4]&3)))&0xff] - k[ki] = w - ki++ - } - } - } - - for i := 0; i < 16; i++ { - c.masking[i] = k[i] - c.rotate[i] = uint8(k[16+i] & 0x1f) - } -} - -// These are the three 'f' functions. See RFC 2144, section 2.2. -func f1(d, m uint32, r uint8) uint32 { - t := m + d - I := (t << r) | (t >> (32 - r)) - return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff] -} - -func f2(d, m uint32, r uint8) uint32 { - t := m ^ d - I := (t << r) | (t >> (32 - r)) - return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff] -} - -func f3(d, m uint32, r uint8) uint32 { - t := m - d - I := (t << r) | (t >> (32 - r)) - return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff] -} - -var sBox = [8][256]uint32{ - { - 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, - 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, - 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, - 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, - 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, - 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, - 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, - 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, - 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, - 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, - 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, - 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, - 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, - 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, - 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, - 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, - 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, - 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, - 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, - 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, - 0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, - 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, - 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, - 0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, - 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, - 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, - 0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, - 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, - 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c, - 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, - 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, - 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf, - }, - { - 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651, - 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, - 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, - 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806, - 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, - 0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359, - 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b, - 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, - 0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34, - 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, - 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, - 0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860, - 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, - 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, - 0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, - 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, - 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, - 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, - 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f, - 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, - 0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6, - 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58, - 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, - 0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d, - 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, - 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, - 0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6, - 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, - 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, - 0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, - 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9, - 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1, - }, - { - 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90, - 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, - 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, - 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240, - 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, - 0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, - 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71, - 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, - 0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82, - 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, - 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, - 0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176, - 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, - 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, - 0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, - 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e, - 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, - 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, - 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a, - 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, - 0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, - 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5, - 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, - 0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536, - 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, - 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, - 0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69, - 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, - 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, - 0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, - 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a, - 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783, - }, - { - 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1, - 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, - 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, - 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121, - 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, - 0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, - 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb, - 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, - 0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d, - 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, - 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, - 0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003, - 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, - 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, - 0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, - 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a, - 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, - 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, - 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26, - 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, - 0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7, - 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417, - 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, - 0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2, - 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, - 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, - 0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef, - 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, - 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, - 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, - 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282, - 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2, - }, - { - 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f, - 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, - 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, - 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02, - 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, - 0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7, - 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9, - 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, - 0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774, - 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, - 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, - 0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910, - 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, - 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, - 0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, - 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f, - 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, - 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, - 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3, - 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, - 0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4, - 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2, - 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, - 0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5, - 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, - 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, - 0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801, - 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, - 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, - 0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, - 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8, - 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4, - }, - { - 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac, - 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, - 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, - 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98, - 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, - 0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3, - 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd, - 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, - 0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9, - 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, - 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, - 0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc, - 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, - 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, - 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, - 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289, - 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, - 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, - 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b, - 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, - 0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13, - 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976, - 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, - 0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891, - 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, - 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, - 0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084, - 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, - 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, - 0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, - 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd, - 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f, - }, - { - 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f, - 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, - 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, - 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19, - 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, - 0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516, - 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88, - 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, - 0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756, - 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, - 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, - 0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688, - 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, - 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, - 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, - 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, - 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, - 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, - 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566, - 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, - 0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962, - 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e, - 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, - 0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c, - 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, - 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, - 0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be, - 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, - 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, - 0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, - 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c, - 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3, - }, - { - 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5, - 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, - 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, - 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d, - 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, - 0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862, - 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc, - 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, - 0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e, - 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, - 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, - 0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42, - 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, - 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, - 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, - 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c, - 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, - 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, - 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70, - 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, - 0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c, - 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3, - 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, - 0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101, - 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, - 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, - 0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a, - 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, - 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, - 0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, - 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82, - 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e, - }, -} diff --git a/src/pkg/crypto/cast5/cast5_test.go b/src/pkg/crypto/cast5/cast5_test.go deleted file mode 100644 index 5f7025ff2..000000000 --- a/src/pkg/crypto/cast5/cast5_test.go +++ /dev/null @@ -1,104 +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. - -package cast5 - -import ( - "bytes" - "encoding/hex" - "testing" -) - -// This test vector is taken from RFC 2144, App B.1. -// Since the other two test vectors are for reduced-round variants, we can't -// use them. -var basicTests = []struct { - key, plainText, cipherText string -}{ - { - "0123456712345678234567893456789a", - "0123456789abcdef", - "238b4fe5847e44b2", - }, -} - -func TestBasic(t *testing.T) { - for i, test := range basicTests { - key, _ := hex.DecodeString(test.key) - plainText, _ := hex.DecodeString(test.plainText) - expected, _ := hex.DecodeString(test.cipherText) - - c, err := NewCipher(key) - if err != nil { - t.Errorf("#%d: failed to create Cipher: %s", i, err) - continue - } - var cipherText [BlockSize]byte - c.Encrypt(cipherText[:], plainText) - if !bytes.Equal(cipherText[:], expected) { - t.Errorf("#%d: got:%x want:%x", i, cipherText, expected) - } - - var plainTextAgain [BlockSize]byte - c.Decrypt(plainTextAgain[:], cipherText[:]) - if !bytes.Equal(plainTextAgain[:], plainText) { - t.Errorf("#%d: got:%x want:%x", i, plainTextAgain, plainText) - } - } -} - -// TestFull performs the test specified in RFC 2144, App B.2. -// However, due to the length of time taken, it's disabled here and a more -// limited version is included, below. -func TestFull(t *testing.T) { - // This is too slow for normal testing - return - - a, b := iterate(1000000) - - const expectedA = "eea9d0a249fd3ba6b3436fb89d6dca92" - const expectedB = "b2c95eb00c31ad7180ac05b8e83d696e" - - if hex.EncodeToString(a) != expectedA { - t.Errorf("a: got:%x want:%s", a, expectedA) - } - if hex.EncodeToString(b) != expectedB { - t.Errorf("b: got:%x want:%s", b, expectedB) - } -} - -func iterate(iterations int) ([]byte, []byte) { - const initValueHex = "0123456712345678234567893456789a" - - initValue, _ := hex.DecodeString(initValueHex) - - var a, b [16]byte - copy(a[:], initValue) - copy(b[:], initValue) - - for i := 0; i < iterations; i++ { - c, _ := NewCipher(b[:]) - c.Encrypt(a[:8], a[:8]) - c.Encrypt(a[8:], a[8:]) - c, _ = NewCipher(a[:]) - c.Encrypt(b[:8], b[:8]) - c.Encrypt(b[8:], b[8:]) - } - - return a[:], b[:] -} - -func TestLimited(t *testing.T) { - a, b := iterate(1000) - - const expectedA = "23f73b14b02a2ad7dfb9f2c35644798d" - const expectedB = "e5bf37eff14c456a40b21ce369370a9f" - - if hex.EncodeToString(a) != expectedA { - t.Errorf("a: got:%x want:%s", a, expectedA) - } - if hex.EncodeToString(b) != expectedB { - t.Errorf("b: got:%x want:%s", b, expectedB) - } -} diff --git a/src/pkg/crypto/cipher/Makefile b/src/pkg/crypto/cipher/Makefile deleted file mode 100644 index 8f61cf20b..000000000 --- a/src/pkg/crypto/cipher/Makefile +++ /dev/null @@ -1,17 +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. - -include ../../../Make.inc - -TARG=crypto/cipher -GOFILES=\ - cbc.go\ - cfb.go\ - cipher.go\ - ctr.go\ - io.go\ - ocfb.go\ - ofb.go - -include ../../../Make.pkg diff --git a/src/pkg/crypto/cipher/cbc.go b/src/pkg/crypto/cipher/cbc.go index 4632f882a..a48929cf5 100644 --- a/src/pkg/crypto/cipher/cbc.go +++ b/src/pkg/crypto/cipher/cbc.go @@ -56,7 +56,7 @@ type cbcDecrypter cbc // NewCBCDecrypter returns a BlockMode which decrypts in cipher block chaining // mode, using the given Block. The length of iv must be the same as the -// Block's block size as must match the iv used to encrypt the data. +// Block's block size and must match the iv used to encrypt the data. func NewCBCDecrypter(b Block, iv []byte) BlockMode { return (*cbcDecrypter)(newCBC(b, iv)) } diff --git a/src/pkg/crypto/cipher/cbc_aes_test.go b/src/pkg/crypto/cipher/cbc_aes_test.go index 944ca1ba8..cee3a784b 100644 --- a/src/pkg/crypto/cipher/cbc_aes_test.go +++ b/src/pkg/crypto/cipher/cbc_aes_test.go @@ -8,11 +8,12 @@ // Special Publication 800-38A, ``Recommendation for Block Cipher // Modes of Operation,'' 2001 Edition, pp. 24-29. -package cipher +package cipher_test import ( "bytes" "crypto/aes" + "crypto/cipher" "testing" ) @@ -72,14 +73,14 @@ func TestCBC_AES(t *testing.T) { continue } - encrypter := NewCBCEncrypter(c, tt.iv) + encrypter := cipher.NewCBCEncrypter(c, tt.iv) d := make([]byte, len(tt.in)) encrypter.CryptBlocks(d, tt.in) if !bytes.Equal(tt.out, d) { t.Errorf("%s: CBCEncrypter\nhave %x\nwant %x", test, d, tt.out) } - decrypter := NewCBCDecrypter(c, tt.iv) + decrypter := cipher.NewCBCDecrypter(c, tt.iv) p := make([]byte, len(d)) decrypter.CryptBlocks(p, d) if !bytes.Equal(tt.in, p) { diff --git a/src/pkg/crypto/cipher/cfb_test.go b/src/pkg/crypto/cipher/cfb_test.go index 9547bfceb..f704b337e 100644 --- a/src/pkg/crypto/cipher/cfb_test.go +++ b/src/pkg/crypto/cipher/cfb_test.go @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package cipher +package cipher_test import ( "bytes" "crypto/aes" + "crypto/cipher" "crypto/rand" "testing" ) @@ -21,11 +22,11 @@ func TestCFB(t *testing.T) { plaintext := []byte("this is the plaintext") iv := make([]byte, block.BlockSize()) rand.Reader.Read(iv) - cfb := NewCFBEncrypter(block, iv) + cfb := cipher.NewCFBEncrypter(block, iv) ciphertext := make([]byte, len(plaintext)) cfb.XORKeyStream(ciphertext, plaintext) - cfbdec := NewCFBDecrypter(block, iv) + cfbdec := cipher.NewCFBDecrypter(block, iv) plaintextCopy := make([]byte, len(plaintext)) cfbdec.XORKeyStream(plaintextCopy, ciphertext) diff --git a/src/pkg/crypto/cipher/common_test.go b/src/pkg/crypto/cipher/common_test.go index fb755757c..c75c919d1 100644 --- a/src/pkg/crypto/cipher/common_test.go +++ b/src/pkg/crypto/cipher/common_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. -package cipher +package cipher_test // Common values for tests. diff --git a/src/pkg/crypto/cipher/ctr_aes_test.go b/src/pkg/crypto/cipher/ctr_aes_test.go index 8dca9968c..d019ae0d0 100644 --- a/src/pkg/crypto/cipher/ctr_aes_test.go +++ b/src/pkg/crypto/cipher/ctr_aes_test.go @@ -8,11 +8,12 @@ // Special Publication 800-38A, ``Recommendation for Block Cipher // Modes of Operation,'' 2001 Edition, pp. 55-58. -package cipher +package cipher_test import ( "bytes" "crypto/aes" + "crypto/cipher" "testing" ) @@ -76,7 +77,7 @@ func TestCTR_AES(t *testing.T) { for j := 0; j <= 5; j += 5 { in := tt.in[0 : len(tt.in)-j] - ctr := NewCTR(c, tt.iv) + ctr := cipher.NewCTR(c, tt.iv) encrypted := make([]byte, len(in)) ctr.XORKeyStream(encrypted, in) if out := tt.out[0:len(in)]; !bytes.Equal(out, encrypted) { @@ -86,7 +87,7 @@ func TestCTR_AES(t *testing.T) { for j := 0; j <= 7; j += 7 { in := tt.out[0 : len(tt.out)-j] - ctr := NewCTR(c, tt.iv) + ctr := cipher.NewCTR(c, tt.iv) plain := make([]byte, len(in)) ctr.XORKeyStream(plain, in) if out := tt.in[0:len(in)]; !bytes.Equal(out, plain) { diff --git a/src/pkg/crypto/cipher/io.go b/src/pkg/crypto/cipher/io.go index 97f40b8e7..76048fbf3 100644 --- a/src/pkg/crypto/cipher/io.go +++ b/src/pkg/crypto/cipher/io.go @@ -4,37 +4,34 @@ package cipher -import ( - "os" - "io" -) +import "io" // The Stream* objects are so simple that all their members are public. Users // can create them themselves. -// StreamReader wraps a Stream into an io.Reader. It simply calls XORKeyStream +// StreamReader wraps a Stream into an io.Reader. It calls XORKeyStream // to process each slice of data which passes through. type StreamReader struct { S Stream R io.Reader } -func (r StreamReader) Read(dst []byte) (n int, err os.Error) { +func (r StreamReader) Read(dst []byte) (n int, err error) { n, err = r.R.Read(dst) r.S.XORKeyStream(dst[:n], dst[:n]) return } -// StreamWriter wraps a Stream into an io.Writer. It simply calls XORKeyStream +// StreamWriter wraps a Stream into an io.Writer. It calls XORKeyStream // to process each slice of data which passes through. If any Write call // returns short then the StreamWriter is out of sync and must be discarded. type StreamWriter struct { S Stream W io.Writer - Err os.Error + Err error } -func (w StreamWriter) Write(src []byte) (n int, err os.Error) { +func (w StreamWriter) Write(src []byte) (n int, err error) { if w.Err != nil { return 0, w.Err } @@ -50,7 +47,7 @@ func (w StreamWriter) Write(src []byte) (n int, err os.Error) { return } -func (w StreamWriter) Close() os.Error { +func (w StreamWriter) Close() error { // This saves us from either requiring a WriteCloser or having a // StreamWriterCloser. return w.W.(io.Closer).Close() diff --git a/src/pkg/crypto/cipher/ocfb.go b/src/pkg/crypto/cipher/ocfb.go deleted file mode 100644 index 031e74a9d..000000000 --- a/src/pkg/crypto/cipher/ocfb.go +++ /dev/null @@ -1,138 +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. - -// OpenPGP CFB Mode. http://tools.ietf.org/html/rfc4880#section-13.9 - -package cipher - -type ocfbEncrypter struct { - b Block - fre []byte - outUsed int -} - -// An OCFBResyncOption determines if the "resynchronization step" of OCFB is -// performed. -type OCFBResyncOption bool - -const ( - OCFBResync OCFBResyncOption = true - OCFBNoResync OCFBResyncOption = false -) - -// NewOCFBEncrypter returns a Stream which encrypts data with OpenPGP's cipher -// feedback mode using the given Block, and an initial amount of ciphertext. -// randData must be random bytes and be the same length as the Block's block -// size. Resync determines if the "resynchronization step" from RFC 4880, 13.9 -// step 7 is performed. Different parts of OpenPGP vary on this point. -func NewOCFBEncrypter(block Block, randData []byte, resync OCFBResyncOption) (Stream, []byte) { - blockSize := block.BlockSize() - if len(randData) != blockSize { - return nil, nil - } - - x := &ocfbEncrypter{ - b: block, - fre: make([]byte, blockSize), - outUsed: 0, - } - prefix := make([]byte, blockSize+2) - - block.Encrypt(x.fre, x.fre) - for i := 0; i < blockSize; i++ { - prefix[i] = randData[i] ^ x.fre[i] - } - - block.Encrypt(x.fre, prefix[:blockSize]) - prefix[blockSize] = x.fre[0] ^ randData[blockSize-2] - prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1] - - if resync { - block.Encrypt(x.fre, prefix[2:]) - } else { - x.fre[0] = prefix[blockSize] - x.fre[1] = prefix[blockSize+1] - x.outUsed = 2 - } - return x, prefix -} - -func (x *ocfbEncrypter) XORKeyStream(dst, src []byte) { - for i := 0; i < len(src); i++ { - if x.outUsed == len(x.fre) { - x.b.Encrypt(x.fre, x.fre) - x.outUsed = 0 - } - - x.fre[x.outUsed] ^= src[i] - dst[i] = x.fre[x.outUsed] - x.outUsed++ - } -} - -type ocfbDecrypter struct { - b Block - fre []byte - outUsed int -} - -// NewOCFBDecrypter returns a Stream which decrypts data with OpenPGP's cipher -// feedback mode using the given Block. Prefix must be the first blockSize + 2 -// bytes of the ciphertext, where blockSize is the Block's block size. If an -// incorrect key is detected then nil is returned. On successful exit, -// blockSize+2 bytes of decrypted data are written into prefix. Resync -// determines if the "resynchronization step" from RFC 4880, 13.9 step 7 is -// performed. Different parts of OpenPGP vary on this point. -func NewOCFBDecrypter(block Block, prefix []byte, resync OCFBResyncOption) Stream { - blockSize := block.BlockSize() - if len(prefix) != blockSize+2 { - return nil - } - - x := &ocfbDecrypter{ - b: block, - fre: make([]byte, blockSize), - outUsed: 0, - } - prefixCopy := make([]byte, len(prefix)) - copy(prefixCopy, prefix) - - block.Encrypt(x.fre, x.fre) - for i := 0; i < blockSize; i++ { - prefixCopy[i] ^= x.fre[i] - } - - block.Encrypt(x.fre, prefix[:blockSize]) - prefixCopy[blockSize] ^= x.fre[0] - prefixCopy[blockSize+1] ^= x.fre[1] - - if prefixCopy[blockSize-2] != prefixCopy[blockSize] || - prefixCopy[blockSize-1] != prefixCopy[blockSize+1] { - return nil - } - - if resync { - block.Encrypt(x.fre, prefix[2:]) - } else { - x.fre[0] = prefix[blockSize] - x.fre[1] = prefix[blockSize+1] - x.outUsed = 2 - } - copy(prefix, prefixCopy) - return x -} - -func (x *ocfbDecrypter) XORKeyStream(dst, src []byte) { - for i := 0; i < len(src); i++ { - if x.outUsed == len(x.fre) { - x.b.Encrypt(x.fre, x.fre) - x.outUsed = 0 - } - - c := src[i] - dst[i] = x.fre[x.outUsed] ^ src[i] - x.fre[x.outUsed] = c - x.outUsed++ - } -} diff --git a/src/pkg/crypto/cipher/ocfb_test.go b/src/pkg/crypto/cipher/ocfb_test.go deleted file mode 100644 index 40938b589..000000000 --- a/src/pkg/crypto/cipher/ocfb_test.go +++ /dev/null @@ -1,44 +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. - -package cipher - -import ( - "bytes" - "crypto/aes" - "crypto/rand" - "testing" -) - -func testOCFB(t *testing.T, resync OCFBResyncOption) { - block, err := aes.NewCipher(commonKey128) - if err != nil { - t.Error(err) - return - } - - plaintext := []byte("this is the plaintext, which is long enough to span several blocks.") - randData := make([]byte, block.BlockSize()) - rand.Reader.Read(randData) - ocfb, prefix := NewOCFBEncrypter(block, randData, resync) - ciphertext := make([]byte, len(plaintext)) - ocfb.XORKeyStream(ciphertext, plaintext) - - ocfbdec := NewOCFBDecrypter(block, prefix, resync) - if ocfbdec == nil { - t.Errorf("NewOCFBDecrypter failed (resync: %t)", resync) - return - } - plaintextCopy := make([]byte, len(plaintext)) - ocfbdec.XORKeyStream(plaintextCopy, ciphertext) - - if !bytes.Equal(plaintextCopy, plaintext) { - t.Errorf("got: %x, want: %x (resync: %t)", plaintextCopy, plaintext, resync) - } -} - -func TestOCFB(t *testing.T) { - testOCFB(t, OCFBNoResync) - testOCFB(t, OCFBResync) -} diff --git a/src/pkg/crypto/cipher/ofb_test.go b/src/pkg/crypto/cipher/ofb_test.go index 9b4495c88..8d3c5d3a3 100644 --- a/src/pkg/crypto/cipher/ofb_test.go +++ b/src/pkg/crypto/cipher/ofb_test.go @@ -8,11 +8,12 @@ // Special Publication 800-38A, ``Recommendation for Block Cipher // Modes of Operation,'' 2001 Edition, pp. 52-55. -package cipher +package cipher_test import ( "bytes" "crypto/aes" + "crypto/cipher" "testing" ) @@ -76,7 +77,7 @@ func TestOFB(t *testing.T) { for j := 0; j <= 5; j += 5 { plaintext := tt.in[0 : len(tt.in)-j] - ofb := NewOFB(c, tt.iv) + ofb := cipher.NewOFB(c, tt.iv) ciphertext := make([]byte, len(plaintext)) ofb.XORKeyStream(ciphertext, plaintext) if !bytes.Equal(ciphertext, tt.out[:len(plaintext)]) { @@ -86,7 +87,7 @@ func TestOFB(t *testing.T) { for j := 0; j <= 5; j += 5 { ciphertext := tt.out[0 : len(tt.in)-j] - ofb := NewOFB(c, tt.iv) + ofb := cipher.NewOFB(c, tt.iv) plaintext := make([]byte, len(ciphertext)) ofb.XORKeyStream(plaintext, ciphertext) if !bytes.Equal(plaintext, tt.in[:len(ciphertext)]) { diff --git a/src/pkg/crypto/crypto.go b/src/pkg/crypto/crypto.go index 53672a4da..ecefc6572 100644 --- a/src/pkg/crypto/crypto.go +++ b/src/pkg/crypto/crypto.go @@ -14,15 +14,15 @@ import ( type Hash uint const ( - MD4 Hash = 1 + iota // in package crypto/md4 - MD5 // in package crypto/md5 - SHA1 // in package crypto/sha1 - SHA224 // in package crypto/sha256 - SHA256 // in package crypto/sha256 - SHA384 // in package crypto/sha512 - SHA512 // in package crypto/sha512 + MD4 Hash = 1 + iota // import code.google.com/p/go.crypto/md4 + MD5 // import crypto/md5 + SHA1 // import crypto/sha1 + SHA224 // import crypto/sha256 + SHA256 // import crypto/sha256 + SHA384 // import crypto/sha512 + SHA512 // import crypto/sha512 MD5SHA1 // no implementation; MD5+SHA1 used for TLS RSA - RIPEMD160 // in package crypto/ripemd160 + RIPEMD160 // import code.google.com/p/go.crypto/ripemd160 maxHash ) @@ -50,8 +50,8 @@ func (h Hash) Size() int { var hashes = make([]func() hash.Hash, maxHash) -// New returns a new hash.Hash calculating the given hash function. If the -// hash function is not linked into the binary, New returns nil. +// New returns a new hash.Hash calculating the given hash function. New panics +// if the hash function is not linked into the binary. func (h Hash) New() hash.Hash { if h > 0 && h < maxHash { f := hashes[h] @@ -59,7 +59,12 @@ func (h Hash) New() hash.Hash { return f() } } - return nil + panic("crypto: requested hash function is unavailable") +} + +// Available reports whether the given hash function is linked into the binary. +func (h Hash) Available() bool { + return h < maxHash && hashes[h] != nil } // RegisterHash registers a function that returns a new instance of the given @@ -71,3 +76,6 @@ func RegisterHash(h Hash, f func() hash.Hash) { } hashes[h] = f } + +// PrivateKey represents a private key using an unspecified algorithm. +type PrivateKey interface{} diff --git a/src/pkg/crypto/des/Makefile b/src/pkg/crypto/des/Makefile deleted file mode 100644 index 94b0fc0fa..000000000 --- a/src/pkg/crypto/des/Makefile +++ /dev/null @@ -1,13 +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. - -include ../../../Make.inc - -TARG=crypto/des -GOFILES=\ - block.go\ - cipher.go\ - const.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/des/block.go b/src/pkg/crypto/des/block.go index e18eaedf5..c11c62cd7 100644 --- a/src/pkg/crypto/des/block.go +++ b/src/pkg/crypto/des/block.go @@ -79,7 +79,7 @@ func ksRotate(in uint32) (out []uint32) { } // creates 16 56-bit subkeys from the original key -func (c *Cipher) generateSubkeys(keyBytes []byte) { +func (c *desCipher) generateSubkeys(keyBytes []byte) { // apply PC1 permutation to key key := binary.BigEndian.Uint64(keyBytes) permutedKey := permuteBlock(key, permutedChoice1[:]) diff --git a/src/pkg/crypto/des/cipher.go b/src/pkg/crypto/des/cipher.go index d17a1a783..2f929ca7b 100644 --- a/src/pkg/crypto/des/cipher.go +++ b/src/pkg/crypto/des/cipher.go @@ -5,7 +5,7 @@ package des import ( - "os" + "crypto/cipher" "strconv" ) @@ -14,90 +14,60 @@ const BlockSize = 8 type KeySizeError int -func (k KeySizeError) String() string { +func (k KeySizeError) Error() string { return "crypto/des: invalid key size " + strconv.Itoa(int(k)) } -// Cipher is an instance of DES encryption. -type Cipher struct { +// desCipher is an instance of DES encryption. +type desCipher struct { subkeys [16]uint64 } -// NewCipher creates and returns a new Cipher. -func NewCipher(key []byte) (*Cipher, os.Error) { +// NewCipher creates and returns a new cipher.Block. +func NewCipher(key []byte) (cipher.Block, error) { if len(key) != 8 { return nil, KeySizeError(len(key)) } - c := new(Cipher) + c := new(desCipher) c.generateSubkeys(key) return c, nil } -// BlockSize returns the DES block size, 8 bytes. -func (c *Cipher) BlockSize() int { return BlockSize } +func (c *desCipher) BlockSize() int { return BlockSize } -// Encrypts the 8-byte buffer src and stores the result in dst. -// Note that for amounts of data larger than a block, -// it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). -func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c.subkeys[:], dst, src) } +func (c *desCipher) Encrypt(dst, src []byte) { encryptBlock(c.subkeys[:], dst, src) } -// Decrypts the 8-byte buffer src and stores the result in dst. -func (c *Cipher) Decrypt(dst, src []byte) { decryptBlock(c.subkeys[:], dst, src) } +func (c *desCipher) Decrypt(dst, src []byte) { decryptBlock(c.subkeys[:], dst, src) } -// Reset zeros the key data, so that it will no longer -// appear in the process's memory. -func (c *Cipher) Reset() { - for i := 0; i < len(c.subkeys); i++ { - c.subkeys[i] = 0 - } -} - -// A TripleDESCipher is an instance of TripleDES encryption. -type TripleDESCipher struct { - cipher1, cipher2, cipher3 Cipher +// A tripleDESCipher is an instance of TripleDES encryption. +type tripleDESCipher struct { + cipher1, cipher2, cipher3 desCipher } -// NewCipher creates and returns a new Cipher. -func NewTripleDESCipher(key []byte) (*TripleDESCipher, os.Error) { +// NewTripleDESCipher creates and returns a new cipher.Block. +func NewTripleDESCipher(key []byte) (cipher.Block, error) { if len(key) != 24 { return nil, KeySizeError(len(key)) } - c := new(TripleDESCipher) + c := new(tripleDESCipher) c.cipher1.generateSubkeys(key[:8]) c.cipher2.generateSubkeys(key[8:16]) c.cipher3.generateSubkeys(key[16:]) return c, nil } -// BlockSize returns the TripleDES block size, 8 bytes. -// It is necessary to satisfy the Block interface in the -// package "crypto/cipher". -func (c *TripleDESCipher) BlockSize() int { return BlockSize } +func (c *tripleDESCipher) BlockSize() int { return BlockSize } -// Encrypts the 8-byte buffer src and stores the result in dst. -// Note that for amounts of data larger than a block, -// it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). -func (c *TripleDESCipher) Encrypt(dst, src []byte) { +func (c *tripleDESCipher) Encrypt(dst, src []byte) { c.cipher1.Encrypt(dst, src) c.cipher2.Decrypt(dst, dst) c.cipher3.Encrypt(dst, dst) } -// Decrypts the 8-byte buffer src and stores the result in dst. -func (c *TripleDESCipher) Decrypt(dst, src []byte) { +func (c *tripleDESCipher) Decrypt(dst, src []byte) { c.cipher3.Decrypt(dst, src) c.cipher2.Encrypt(dst, dst) c.cipher1.Decrypt(dst, dst) } - -// Reset zeros the key data, so that it will no longer -// appear in the process's memory. -func (c *TripleDESCipher) Reset() { - c.cipher1.Reset() - c.cipher2.Reset() - c.cipher3.Reset() -} diff --git a/src/pkg/crypto/des/des_test.go b/src/pkg/crypto/des/des_test.go index d1f3aa71a..e9fc23629 100644 --- a/src/pkg/crypto/des/des_test.go +++ b/src/pkg/crypto/des/des_test.go @@ -1260,11 +1260,19 @@ var tableA4Tests = []CryptTest{ []byte{0x63, 0xfa, 0xc0, 0xd0, 0x34, 0xd9, 0xf7, 0x93}}, } +func newCipher(key []byte) *desCipher { + c, err := NewCipher(key) + if err != nil { + panic("NewCipher failed: " + err.Error()) + } + return c.(*desCipher) +} + // Use the known weak keys to test DES implementation func TestWeakKeys(t *testing.T) { for i, tt := range weakKeyTests { var encrypt = func(in []byte) (out []byte) { - c, _ := NewCipher(tt.key) + c := newCipher(tt.key) out = make([]byte, len(in)) encryptBlock(c.subkeys[:], out, in) return @@ -1285,7 +1293,7 @@ func TestWeakKeys(t *testing.T) { func TestSemiWeakKeyPairs(t *testing.T) { for i, tt := range semiWeakKeyTests { var encrypt = func(key, in []byte) (out []byte) { - c, _ := NewCipher(key) + c := newCipher(key) out = make([]byte, len(in)) encryptBlock(c.subkeys[:], out, in) return @@ -1305,7 +1313,7 @@ func TestSemiWeakKeyPairs(t *testing.T) { func TestDESEncryptBlock(t *testing.T) { for i, tt := range encryptDESTests { - c, _ := NewCipher(tt.key) + c := newCipher(tt.key) out := make([]byte, len(tt.in)) encryptBlock(c.subkeys[:], out, tt.in) @@ -1317,7 +1325,7 @@ func TestDESEncryptBlock(t *testing.T) { func TestDESDecryptBlock(t *testing.T) { for i, tt := range encryptDESTests { - c, _ := NewCipher(tt.key) + c := newCipher(tt.key) plain := make([]byte, len(tt.in)) decryptBlock(c.subkeys[:], plain, tt.out) diff --git a/src/pkg/crypto/dsa/Makefile b/src/pkg/crypto/dsa/Makefile deleted file mode 100644 index fa89d4ab2..000000000 --- a/src/pkg/crypto/dsa/Makefile +++ /dev/null @@ -1,11 +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. - -include ../../../Make.inc - -TARG=crypto/dsa -GOFILES=\ - dsa.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/dsa/dsa.go b/src/pkg/crypto/dsa/dsa.go index a5f96fe94..05766a2f1 100644 --- a/src/pkg/crypto/dsa/dsa.go +++ b/src/pkg/crypto/dsa/dsa.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 dsa implements the Digital Signature Algorithm, as defined in FIPS 186-3 +// Package dsa implements the Digital Signature Algorithm, as defined in FIPS 186-3. package dsa import ( - "big" + "errors" "io" - "os" + "math/big" ) // Parameters represents the domain parameters for a key. These parameters can @@ -29,17 +29,11 @@ type PrivateKey struct { X *big.Int } -type invalidPublicKeyError int - -func (invalidPublicKeyError) String() string { - return "crypto/dsa: invalid public key" -} - -// InvalidPublicKeyError results when a public key is not usable by this code. +// ErrInvalidPublicKey results when a public key is not usable by this code. // FIPS is quite strict about the format of DSA keys, but other code may be // less so. Thus, when using keys which may have been generated by other code, // this error must be handled. -var InvalidPublicKeyError = invalidPublicKeyError(0) +var ErrInvalidPublicKey = errors.New("crypto/dsa: invalid public key") // ParameterSizes is a enumeration of the acceptable bit lengths of the primes // in a set of DSA parameters. See FIPS 186-3, section 4.2. @@ -58,7 +52,7 @@ const numMRTests = 64 // GenerateParameters puts a random, valid set of DSA parameters into params. // This function takes many seconds, even on fast machines. -func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes) (err os.Error) { +func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes) (err error) { // This function doesn't follow FIPS 186-3 exactly in that it doesn't // use a verification seed to generate the primes. The verification // seed doesn't appear to be exported or used by other code and @@ -79,7 +73,7 @@ func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes L = 3072 N = 256 default: - return os.NewError("crypto/dsa: invalid ParameterSizes") + return errors.New("crypto/dsa: invalid ParameterSizes") } qBytes := make([]byte, N/8) @@ -102,7 +96,7 @@ GeneratePrimes: qBytes[0] |= 0x80 q.SetBytes(qBytes) - if !big.ProbablyPrime(q, numMRTests) { + if !q.ProbablyPrime(numMRTests) { continue } @@ -123,7 +117,7 @@ GeneratePrimes: continue } - if !big.ProbablyPrime(p, numMRTests) { + if !p.ProbablyPrime(numMRTests) { continue } @@ -156,9 +150,9 @@ GeneratePrimes: // GenerateKey generates a public&private key pair. The Parameters of the // PrivateKey must already be valid (see GenerateParameters). -func GenerateKey(priv *PrivateKey, rand io.Reader) os.Error { +func GenerateKey(priv *PrivateKey, rand io.Reader) error { if priv.P == nil || priv.Q == nil || priv.G == nil { - return os.NewError("crypto/dsa: parameters not set up before generating key") + return errors.New("crypto/dsa: parameters not set up before generating key") } x := new(big.Int) @@ -185,12 +179,16 @@ func GenerateKey(priv *PrivateKey, rand io.Reader) os.Error { // larger message) using the private key, priv. It returns the signature as a // pair of integers. The security of the private key depends on the entropy of // rand. -func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os.Error) { +// +// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated +// to the byte-length of the subgroup. This function does not perform that +// truncation itself. +func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) { // FIPS 186-3, section 4.6 n := priv.Q.BitLen() if n&7 != 0 { - err = InvalidPublicKeyError + err = ErrInvalidPublicKey return } n >>= 3 @@ -218,10 +216,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os. continue } - if n > len(hash) { - n = len(hash) - } - z := k.SetBytes(hash[:n]) + z := k.SetBytes(hash) s = new(big.Int).Mul(priv.X, r) s.Add(s, z) @@ -238,7 +233,11 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os. } // Verify verifies the signature in r, s of hash using the public key, pub. It -// returns true iff the signature is valid. +// reports whether the signature is valid. +// +// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated +// to the byte-length of the subgroup. This function does not perform that +// truncation itself. func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { // FIPS 186-3, section 4.7 @@ -255,12 +254,7 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { if n&7 != 0 { return false } - n >>= 3 - - if n > len(hash) { - n = len(hash) - } - z := new(big.Int).SetBytes(hash[:n]) + z := new(big.Int).SetBytes(hash) u1 := new(big.Int).Mul(z, w) u1.Mod(u1, pub.Q) diff --git a/src/pkg/crypto/dsa/dsa_test.go b/src/pkg/crypto/dsa/dsa_test.go index deec08dfd..177aa444d 100644 --- a/src/pkg/crypto/dsa/dsa_test.go +++ b/src/pkg/crypto/dsa/dsa_test.go @@ -5,8 +5,8 @@ package dsa import ( - "big" "crypto/rand" + "math/big" "testing" ) diff --git a/src/pkg/crypto/ecdsa/Makefile b/src/pkg/crypto/ecdsa/Makefile deleted file mode 100644 index 0af24c2f2..000000000 --- a/src/pkg/crypto/ecdsa/Makefile +++ /dev/null @@ -1,11 +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. - -include ../../../Make.inc - -TARG=crypto/ecdsa -GOFILES=\ - ecdsa.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/ecdsa/ecdsa.go b/src/pkg/crypto/ecdsa/ecdsa.go index 7bce1bc96..b28239b78 100644 --- a/src/pkg/crypto/ecdsa/ecdsa.go +++ b/src/pkg/crypto/ecdsa/ecdsa.go @@ -7,21 +7,20 @@ package ecdsa // References: -// [NSA]: Suite B implementor's guide to FIPS 186-3, +// [NSA]: Suite B implementer's guide to FIPS 186-3, // http://www.nsa.gov/ia/_files/ecdsa.pdf // [SECG]: SECG, SEC1 // http://www.secg.org/download/aid-780/sec1-v2.pdf import ( - "big" "crypto/elliptic" "io" - "os" + "math/big" ) // PublicKey represents an ECDSA public key. type PublicKey struct { - *elliptic.Curve + elliptic.Curve X, Y *big.Int } @@ -35,22 +34,23 @@ var one = new(big.Int).SetInt64(1) // randFieldElement returns a random element of the field underlying the given // curve using the procedure given in [NSA] A.2.1. -func randFieldElement(c *elliptic.Curve, rand io.Reader) (k *big.Int, err os.Error) { - b := make([]byte, c.BitSize/8+8) +func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) { + params := c.Params() + b := make([]byte, params.BitSize/8+8) _, err = io.ReadFull(rand, b) if err != nil { return } k = new(big.Int).SetBytes(b) - n := new(big.Int).Sub(c.N, one) + n := new(big.Int).Sub(params.N, one) k.Mod(k, n) k.Add(k, one) return } // GenerateKey generates a public&private key pair. -func GenerateKey(c *elliptic.Curve, rand io.Reader) (priv *PrivateKey, err os.Error) { +func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error) { k, err := randFieldElement(c, rand) if err != nil { return @@ -67,8 +67,8 @@ func GenerateKey(c *elliptic.Curve, rand io.Reader) (priv *PrivateKey, err os.Er // about how this is done. [NSA] suggests that this is done in the obvious // manner, but [SECG] truncates the hash to the bit-length of the curve order // first. We follow [SECG] because that's what OpenSSL does. -func hashToInt(hash []byte, c *elliptic.Curve) *big.Int { - orderBits := c.N.BitLen() +func hashToInt(hash []byte, c elliptic.Curve) *big.Int { + orderBits := c.Params().N.BitLen() orderBytes := (orderBits + 7) / 8 if len(hash) > orderBytes { hash = hash[:orderBytes] @@ -86,9 +86,10 @@ func hashToInt(hash []byte, c *elliptic.Curve) *big.Int { // larger message) using the private key, priv. It returns the signature as a // pair of integers. The security of the private key depends on the entropy of // rand. -func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os.Error) { +func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) { // See [NSA] 3.4.1 c := priv.PublicKey.Curve + N := c.Params().N var k, kInv *big.Int for { @@ -99,9 +100,9 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os. return } - kInv = new(big.Int).ModInverse(k, c.N) + kInv = new(big.Int).ModInverse(k, N) r, _ = priv.Curve.ScalarBaseMult(k.Bytes()) - r.Mod(r, priv.Curve.N) + r.Mod(r, N) if r.Sign() != 0 { break } @@ -111,7 +112,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os. s = new(big.Int).Mul(priv.D, r) s.Add(s, e) s.Mul(s, kInv) - s.Mod(s, priv.PublicKey.Curve.N) + s.Mod(s, N) if s.Sign() != 0 { break } @@ -125,15 +126,16 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os. func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { // See [NSA] 3.4.2 c := pub.Curve + N := c.Params().N if r.Sign() == 0 || s.Sign() == 0 { return false } - if r.Cmp(c.N) >= 0 || s.Cmp(c.N) >= 0 { + if r.Cmp(N) >= 0 || s.Cmp(N) >= 0 { return false } e := hashToInt(hash, c) - w := new(big.Int).ModInverse(s, c.N) + w := new(big.Int).ModInverse(s, N) u1 := e.Mul(e, w) u2 := w.Mul(r, w) @@ -144,6 +146,6 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { return false } x, _ := c.Add(x1, y1, x2, y2) - x.Mod(x, c.N) + 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 d6b403914..3a2b3efba 100644 --- a/src/pkg/crypto/ecdsa/ecdsa_test.go +++ b/src/pkg/crypto/ecdsa/ecdsa_test.go @@ -5,15 +5,15 @@ package ecdsa import ( - "big" "crypto/elliptic" - "crypto/sha1" "crypto/rand" + "crypto/sha1" "encoding/hex" + "math/big" "testing" ) -func testKeyGeneration(t *testing.T, c *elliptic.Curve, tag string) { +func testKeyGeneration(t *testing.T, c elliptic.Curve, tag string) { priv, err := GenerateKey(c, rand.Reader) if err != nil { t.Errorf("%s: error: %s", tag, err) @@ -34,7 +34,7 @@ func TestKeyGeneration(t *testing.T) { testKeyGeneration(t, elliptic.P521(), "p521") } -func testSignAndVerify(t *testing.T, c *elliptic.Curve, tag string) { +func testSignAndVerify(t *testing.T, c elliptic.Curve, tag string) { priv, _ := GenerateKey(c, rand.Reader) hashed := []byte("testing") @@ -214,7 +214,7 @@ func TestVectors(t *testing.T) { msg, _ := hex.DecodeString(test.msg) sha.Reset() sha.Write(msg) - hashed := sha.Sum() + hashed := sha.Sum(nil) r := fromHex(test.r) s := fromHex(test.s) if Verify(&pub, hashed, r, s) != test.ok { diff --git a/src/pkg/crypto/elliptic/Makefile b/src/pkg/crypto/elliptic/Makefile deleted file mode 100644 index 4db5d7de5..000000000 --- a/src/pkg/crypto/elliptic/Makefile +++ /dev/null @@ -1,11 +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. - -include ../../../Make.inc - -TARG=crypto/elliptic -GOFILES=\ - elliptic.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/elliptic/elliptic.go b/src/pkg/crypto/elliptic/elliptic.go index 41835f1a9..30835a90b 100644 --- a/src/pkg/crypto/elliptic/elliptic.go +++ b/src/pkg/crypto/elliptic/elliptic.go @@ -14,15 +14,32 @@ package elliptic // reverse the transform than to operate in affine coordinates. import ( - "big" "io" - "os" + "math/big" "sync" ) // A Curve represents a short-form Weierstrass curve with a=-3. // See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html -type Curve struct { +type Curve interface { + // Params returns the parameters for the curve. + Params() *CurveParams + // IsOnCurve returns true if the given (x,y) lies on the curve. + IsOnCurve(x, y *big.Int) bool + // Add returns the sum of (x1,y1) and (x2,y2) + Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) + // 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) +} + +// CurveParams contains the parameters of an elliptic curve and also provides +// a generic, non-constant time implementation of Curve. +type CurveParams struct { P *big.Int // the order of the underlying field N *big.Int // the order of the base point B *big.Int // the constant of the curve equation @@ -30,8 +47,11 @@ type Curve struct { BitSize int // the size of the underlying field } -// IsOnCurve returns true if the given (x,y) lies on the curve. -func (curve *Curve) IsOnCurve(x, y *big.Int) bool { +func (curve *CurveParams) Params() *CurveParams { + return curve +} + +func (curve *CurveParams) IsOnCurve(x, y *big.Int) bool { // y² = x³ - 3x + b y2 := new(big.Int).Mul(y, y) y2.Mod(y2, curve.P) @@ -51,7 +71,7 @@ func (curve *Curve) IsOnCurve(x, y *big.Int) bool { // affineFromJacobian reverses the Jacobian transform. See the comment at the // top of the file. -func (curve *Curve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { +func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { zinv := new(big.Int).ModInverse(z, curve.P) zinvsq := new(big.Int).Mul(zinv, zinv) @@ -63,15 +83,14 @@ func (curve *Curve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { return } -// Add returns the sum of (x1,y1) and (x2,y2) -func (curve *Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { +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)) } // addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and // (x2, y2, z2) and returns their sum, also in Jacobian form. -func (curve *Curve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { +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 z1z1 := new(big.Int).Mul(z1, z1) z1z1.Mod(z1z1, curve.P) @@ -134,15 +153,14 @@ func (curve *Curve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big return x3, y3, z3 } -// Double returns 2*(x,y) -func (curve *Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { +func (curve *CurveParams) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { z1 := new(big.Int).SetInt64(1) return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1)) } // doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and // returns its double, also in Jacobian form. -func (curve *Curve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { +func (curve *CurveParams) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b delta := new(big.Int).Mul(z, z) delta.Mod(delta, curve.P) @@ -200,8 +218,7 @@ func (curve *Curve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.I return x3, y3, z3 } -// ScalarMult returns k*(Bx,By) where k is a number in big-endian form. -func (curve *Curve) ScalarMult(Bx, By *big.Int, k []byte) (*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 @@ -239,18 +256,17 @@ func (curve *Curve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { return curve.affineFromJacobian(x, y, z) } -// ScalarBaseMult returns k*G, where G is the base point of the group and k is -// an integer in big-endian form. -func (curve *Curve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { +func (curve *CurveParams) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { return curve.ScalarMult(curve.Gx, curve.Gy, k) } var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f} -// GenerateKey returns a public/private key pair. The private key is generated -// using the given reader, which must return random data. -func (curve *Curve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err os.Error) { - byteLen := (curve.BitSize + 7) >> 3 +// GenerateKey returns a public/private key pair. The private key is +// generated using the given reader, which must return random data. +func GenerateKey(curve Curve, rand io.Reader) (priv []byte, x, y *big.Int, err error) { + bitSize := curve.Params().BitSize + byteLen := (bitSize + 7) >> 3 priv = make([]byte, byteLen) for x == nil { @@ -260,7 +276,7 @@ func (curve *Curve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err } // We have to mask off any excess bits in the case that the size of the // underlying field is not a whole number of bytes. - priv[0] &= mask[curve.BitSize%8] + priv[0] &= mask[bitSize%8] // This is because, in tests, rand will return all zeros and we don't // want to get the point at infinity and loop forever. priv[1] ^= 0x42 @@ -269,10 +285,9 @@ func (curve *Curve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err return } -// Marshal converts a point into the form specified in section 4.3.6 of ANSI -// X9.62. -func (curve *Curve) Marshal(x, y *big.Int) []byte { - byteLen := (curve.BitSize + 7) >> 3 +// Marshal converts a point into the form specified in section 4.3.6 of ANSI X9.62. +func Marshal(curve Curve, x, y *big.Int) []byte { + byteLen := (curve.Params().BitSize + 7) >> 3 ret := make([]byte, 1+2*byteLen) ret[0] = 4 // uncompressed point @@ -284,10 +299,9 @@ func (curve *Curve) Marshal(x, y *big.Int) []byte { return ret } -// Unmarshal converts a point, serialized by Marshal, into an x, y pair. On -// error, x = nil. -func (curve *Curve) Unmarshal(data []byte) (x, y *big.Int) { - byteLen := (curve.BitSize + 7) >> 3 +// Unmarshal converts a point, serialized by Marshal, into an x, y pair. On error, x = nil. +func Unmarshal(curve Curve, data []byte) (x, y *big.Int) { + byteLen := (curve.Params().BitSize + 7) >> 3 if len(data) != 1+2*byteLen { return } @@ -300,10 +314,9 @@ func (curve *Curve) Unmarshal(data []byte) (x, y *big.Int) { } var initonce sync.Once -var p224 *Curve -var p256 *Curve -var p384 *Curve -var p521 *Curve +var p256 *CurveParams +var p384 *CurveParams +var p521 *CurveParams func initAll() { initP224() @@ -312,20 +325,9 @@ func initAll() { initP521() } -func initP224() { - // See FIPS 186-3, section D.2.2 - p224 = new(Curve) - p224.P, _ = new(big.Int).SetString("26959946667150639794667015087019630673557916260026308143510066298881", 10) - p224.N, _ = new(big.Int).SetString("26959946667150639794667015087019625940457807714424391721682722368061", 10) - p224.B, _ = new(big.Int).SetString("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", 16) - p224.Gx, _ = new(big.Int).SetString("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", 16) - p224.Gy, _ = new(big.Int).SetString("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", 16) - p224.BitSize = 224 -} - func initP256() { // See FIPS 186-3, section D.2.3 - p256 = new(Curve) + p256 = new(CurveParams) p256.P, _ = new(big.Int).SetString("115792089210356248762697446949407573530086143415290314195533631308867097853951", 10) p256.N, _ = new(big.Int).SetString("115792089210356248762697446949407573529996955224135760342422259061068512044369", 10) p256.B, _ = new(big.Int).SetString("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16) @@ -336,7 +338,7 @@ func initP256() { func initP384() { // See FIPS 186-3, section D.2.4 - p384 = new(Curve) + p384 = new(CurveParams) p384.P, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319", 10) p384.N, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643", 10) p384.B, _ = new(big.Int).SetString("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", 16) @@ -347,7 +349,7 @@ func initP384() { func initP521() { // See FIPS 186-3, section D.2.5 - p521 = new(Curve) + p521 = new(CurveParams) p521.P, _ = new(big.Int).SetString("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", 10) p521.N, _ = new(big.Int).SetString("6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449", 10) p521.B, _ = new(big.Int).SetString("051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00", 16) @@ -356,26 +358,20 @@ func initP521() { p521.BitSize = 521 } -// P224 returns a Curve which implements P-224 (see FIPS 186-3, section D.2.2) -func P224() *Curve { - initonce.Do(initAll) - return p224 -} - // P256 returns a Curve which implements P-256 (see FIPS 186-3, section D.2.3) -func P256() *Curve { +func P256() Curve { initonce.Do(initAll) return p256 } // P384 returns a Curve which implements P-384 (see FIPS 186-3, section D.2.4) -func P384() *Curve { +func P384() Curve { initonce.Do(initAll) return p384 } // P256 returns a Curve which implements P-521 (see FIPS 186-3, section D.2.5) -func P521() *Curve { +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 b7e7f035f..1e3407ee0 100644 --- a/src/pkg/crypto/elliptic/elliptic_test.go +++ b/src/pkg/crypto/elliptic/elliptic_test.go @@ -5,15 +5,16 @@ package elliptic import ( - "big" "crypto/rand" + "encoding/hex" "fmt" + "math/big" "testing" ) func TestOnCurve(t *testing.T) { p224 := P224() - if !p224.IsOnCurve(p224.Gx, p224.Gy) { + if !p224.IsOnCurve(p224.Params().Gx, p224.Params().Gy) { t.Errorf("FAIL") } } @@ -295,7 +296,25 @@ func TestBaseMult(t *testing.T) { } x, y := p224.ScalarBaseMult(k.Bytes()) if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y { - t.Errorf("%d: bad output for k=%s: got (%x, %s), want (%s, %s)", i, e.k, x, y, e.x, e.y) + t.Errorf("%d: bad output for k=%s: got (%x, %x), want (%s, %s)", i, e.k, x, y, e.x, e.y) + } + if testing.Short() && i > 5 { + break + } + } +} + +func TestGenericBaseMult(t *testing.T) { + // We use the P224 CurveParams directly in order to test the generic implementation. + p224 := P224().Params() + for i, e := range p224BaseMultTests { + k, ok := new(big.Int).SetString(e.k, 10) + if !ok { + t.Errorf("%d: bad value for k: %s", i, e.k) + } + x, y := p224.ScalarBaseMult(k.Bytes()) + if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y { + t.Errorf("%d: bad output for k=%s: got (%x, %x), want (%s, %s)", i, e.k, x, y, e.x, e.y) } if testing.Short() && i > 5 { break @@ -316,13 +335,13 @@ func BenchmarkBaseMult(b *testing.B) { func TestMarshal(t *testing.T) { p224 := P224() - _, x, y, err := p224.GenerateKey(rand.Reader) + _, x, y, err := GenerateKey(p224, rand.Reader) if err != nil { t.Error(err) return } - serialized := p224.Marshal(x, y) - xx, yy := p224.Unmarshal(serialized) + serialized := Marshal(p224, x, y) + xx, yy := Unmarshal(p224, serialized) if xx == nil { t.Error("failed to unmarshal") return @@ -332,3 +351,13 @@ func TestMarshal(t *testing.T) { return } } + +func TestP224Overflow(t *testing.T) { + // This tests for a specific bug in the P224 implementation. + p224 := P224() + pointData, _ := hex.DecodeString("049B535B45FB0A2072398A6831834624C7E32CCFD5A4B933BCEAF77F1DD945E08BBE5178F5EDF5E733388F196D2A631D2E075BB16CBFEEA15B") + x, y := Unmarshal(p224, pointData) + if !p224.IsOnCurve(x, y) { + t.Error("P224 failed to validate a correct point") + } +} diff --git a/src/pkg/crypto/elliptic/p224.go b/src/pkg/crypto/elliptic/p224.go new file mode 100644 index 000000000..17571c252 --- /dev/null +++ b/src/pkg/crypto/elliptic/p224.go @@ -0,0 +1,718 @@ +// 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 elliptic + +// This is a constant-time, 32-bit implementation of P224. See FIPS 186-3, +// section D.2.2. +// +// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background. + +import ( + "math/big" +) + +var p224 p224Curve + +type p224Curve struct { + *CurveParams + gx, gy, b p224FieldElement +} + +func initP224() { + // See FIPS 186-3, section D.2.2 + p224.CurveParams = new(CurveParams) + p224.P, _ = new(big.Int).SetString("26959946667150639794667015087019630673557916260026308143510066298881", 10) + p224.N, _ = new(big.Int).SetString("26959946667150639794667015087019625940457807714424391721682722368061", 10) + p224.B, _ = new(big.Int).SetString("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", 16) + p224.Gx, _ = new(big.Int).SetString("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", 16) + p224.Gy, _ = new(big.Int).SetString("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", 16) + p224.BitSize = 224 + + p224FromBig(&p224.gx, p224.Gx) + p224FromBig(&p224.gy, p224.Gy) + p224FromBig(&p224.b, p224.B) +} + +// P224 returns a Curve which implements P-224 (see FIPS 186-3, section D.2.2) +func P224() Curve { + initonce.Do(initAll) + return p224 +} + +func (curve p224Curve) Params() *CurveParams { + return curve.CurveParams +} + +func (curve p224Curve) IsOnCurve(bigX, bigY *big.Int) bool { + var x, y p224FieldElement + p224FromBig(&x, bigX) + p224FromBig(&y, bigY) + + // y² = x³ - 3x + b + var tmp p224LargeFieldElement + var x3 p224FieldElement + p224Square(&x3, &x, &tmp) + p224Mul(&x3, &x3, &x, &tmp) + + for i := 0; i < 8; i++ { + x[i] *= 3 + } + p224Sub(&x3, &x3, &x) + p224Reduce(&x3) + p224Add(&x3, &x3, &curve.b) + p224Contract(&x3, &x3) + + p224Square(&y, &y, &tmp) + p224Contract(&y, &y) + + for i := 0; i < 8; i++ { + if y[i] != x3[i] { + return false + } + } + return true +} + +func (p224Curve) Add(bigX1, bigY1, bigX2, bigY2 *big.Int) (x, y *big.Int) { + var x1, y1, z1, x2, y2, z2, x3, y3, z3 p224FieldElement + + p224FromBig(&x1, bigX1) + p224FromBig(&y1, bigY1) + z1[0] = 1 + p224FromBig(&x2, bigX2) + p224FromBig(&y2, bigY2) + z2[0] = 1 + + p224AddJacobian(&x3, &y3, &z3, &x1, &y1, &z1, &x2, &y2, &z2) + return p224ToAffine(&x3, &y3, &z3) +} + +func (p224Curve) Double(bigX1, bigY1 *big.Int) (x, y *big.Int) { + var x1, y1, z1, x2, y2, z2 p224FieldElement + + p224FromBig(&x1, bigX1) + p224FromBig(&y1, bigY1) + z1[0] = 1 + + p224DoubleJacobian(&x2, &y2, &z2, &x1, &y1, &z1) + return p224ToAffine(&x2, &y2, &z2) +} + +func (p224Curve) ScalarMult(bigX1, bigY1 *big.Int, scalar []byte) (x, y *big.Int) { + var x1, y1, z1, x2, y2, z2 p224FieldElement + + p224FromBig(&x1, bigX1) + p224FromBig(&y1, bigY1) + z1[0] = 1 + + p224ScalarMult(&x2, &y2, &z2, &x1, &y1, &z1, scalar) + return p224ToAffine(&x2, &y2, &z2) +} + +func (curve p224Curve) ScalarBaseMult(scalar []byte) (x, y *big.Int) { + var z1, x2, y2, z2 p224FieldElement + + z1[0] = 1 + p224ScalarMult(&x2, &y2, &z2, &curve.gx, &curve.gy, &z1, scalar) + return p224ToAffine(&x2, &y2, &z2) +} + +// Field element functions. +// +// The field that we're dealing with is ℤ/pℤ where p = 2**224 - 2**96 + 1. +// +// Field elements are represented by a FieldElement, which is a typedef to an +// array of 8 uint32's. The value of a FieldElement, a, is: +// a[0] + 2**28·a[1] + 2**56·a[1] + ... + 2**196·a[7] +// +// Using 28-bit limbs means that there's only 4 bits of headroom, which is less +// than we would really like. But it has the useful feature that we hit 2**224 +// exactly, making the reflections during a reduce much nicer. +type p224FieldElement [8]uint32 + +// p224Add computes *out = a+b +// +// a[i] + b[i] < 2**32 +func p224Add(out, a, b *p224FieldElement) { + for i := 0; i < 8; i++ { + out[i] = a[i] + b[i] + } +} + +const two31p3 = 1<<31 + 1<<3 +const two31m3 = 1<<31 - 1<<3 +const two31m15m3 = 1<<31 - 1<<15 - 1<<3 + +// p224ZeroModP31 is 0 mod p where bit 31 is set in all limbs so that we can +// subtract smaller amounts without underflow. See the section "Subtraction" in +// [1] for reasoning. +var p224ZeroModP31 = []uint32{two31p3, two31m3, two31m3, two31m15m3, two31m3, two31m3, two31m3, two31m3} + +// p224Sub computes *out = a-b +// +// a[i], b[i] < 2**30 +// out[i] < 2**32 +func p224Sub(out, a, b *p224FieldElement) { + for i := 0; i < 8; i++ { + out[i] = a[i] + p224ZeroModP31[i] - b[i] + } +} + +// LargeFieldElement also represents an element of the field. The limbs are +// still spaced 28-bits apart and in little-endian order. So the limbs are at +// 0, 28, 56, ..., 392 bits, each 64-bits wide. +type p224LargeFieldElement [15]uint64 + +const two63p35 = 1<<63 + 1<<35 +const two63m35 = 1<<63 - 1<<35 +const two63m35m19 = 1<<63 - 1<<35 - 1<<19 + +// p224ZeroModP63 is 0 mod p where bit 63 is set in all limbs. See the section +// "Subtraction" in [1] for why. +var p224ZeroModP63 = [8]uint64{two63p35, two63m35, two63m35, two63m35, two63m35m19, two63m35, two63m35, two63m35} + +const bottom12Bits = 0xfff +const bottom28Bits = 0xfffffff + +// p224Mul computes *out = a*b +// +// a[i] < 2**29, b[i] < 2**30 (or vice versa) +// out[i] < 2**29 +func p224Mul(out, a, b *p224FieldElement, tmp *p224LargeFieldElement) { + for i := 0; i < 15; i++ { + tmp[i] = 0 + } + + for i := 0; i < 8; i++ { + for j := 0; j < 8; j++ { + tmp[i+j] += uint64(a[i]) * uint64(b[j]) + } + } + + p224ReduceLarge(out, tmp) +} + +// Square computes *out = a*a +// +// a[i] < 2**29 +// out[i] < 2**29 +func p224Square(out, a *p224FieldElement, tmp *p224LargeFieldElement) { + for i := 0; i < 15; i++ { + tmp[i] = 0 + } + + for i := 0; i < 8; i++ { + for j := 0; j <= i; j++ { + r := uint64(a[i]) * uint64(a[j]) + if i == j { + tmp[i+j] += r + } else { + tmp[i+j] += r << 1 + } + } + } + + p224ReduceLarge(out, tmp) +} + +// ReduceLarge converts a p224LargeFieldElement to a p224FieldElement. +// +// in[i] < 2**62 +func p224ReduceLarge(out *p224FieldElement, in *p224LargeFieldElement) { + for i := 0; i < 8; i++ { + in[i] += p224ZeroModP63[i] + } + + // Eliminate the coefficients at 2**224 and greater. + for i := 14; i >= 8; i-- { + in[i-8] -= in[i] + in[i-5] += (in[i] & 0xffff) << 12 + in[i-4] += in[i] >> 16 + } + in[8] = 0 + // in[0..8] < 2**64 + + // As the values become small enough, we start to store them in |out| + // and use 32-bit operations. + for i := 1; i < 8; i++ { + in[i+1] += in[i] >> 28 + out[i] = uint32(in[i] & bottom28Bits) + } + in[0] -= in[8] + out[3] += uint32(in[8]&0xffff) << 12 + out[4] += uint32(in[8] >> 16) + // in[0] < 2**64 + // out[3] < 2**29 + // out[4] < 2**29 + // out[1,2,5..7] < 2**28 + + out[0] = uint32(in[0] & bottom28Bits) + out[1] += uint32((in[0] >> 28) & bottom28Bits) + out[2] += uint32(in[0] >> 56) + // out[0] < 2**28 + // out[1..4] < 2**29 + // out[5..7] < 2**28 +} + +// Reduce reduces the coefficients of a to smaller bounds. +// +// On entry: a[i] < 2**31 + 2**30 +// On exit: a[i] < 2**29 +func p224Reduce(a *p224FieldElement) { + for i := 0; i < 7; i++ { + a[i+1] += a[i] >> 28 + a[i] &= bottom28Bits + } + top := a[7] >> 28 + a[7] &= bottom28Bits + + // top < 2**4 + mask := top + mask |= mask >> 2 + mask |= mask >> 1 + mask <<= 31 + mask = uint32(int32(mask) >> 31) + // Mask is all ones if top != 0, all zero otherwise + + a[0] -= top + a[3] += top << 12 + + // We may have just made a[0] negative but, if we did, then we must + // have added something to a[3], this it's > 2**12. Therefore we can + // carry down to a[0]. + a[3] -= 1 & mask + a[2] += mask & (1<<28 - 1) + a[1] += mask & (1<<28 - 1) + a[0] += mask & (1 << 28) +} + +// p224Invert calculates *out = in**-1 by computing in**(2**224 - 2**96 - 1), +// i.e. Fermat's little theorem. +func p224Invert(out, in *p224FieldElement) { + var f1, f2, f3, f4 p224FieldElement + var c p224LargeFieldElement + + p224Square(&f1, in, &c) // 2 + p224Mul(&f1, &f1, in, &c) // 2**2 - 1 + p224Square(&f1, &f1, &c) // 2**3 - 2 + p224Mul(&f1, &f1, in, &c) // 2**3 - 1 + p224Square(&f2, &f1, &c) // 2**4 - 2 + p224Square(&f2, &f2, &c) // 2**5 - 4 + p224Square(&f2, &f2, &c) // 2**6 - 8 + p224Mul(&f1, &f1, &f2, &c) // 2**6 - 1 + p224Square(&f2, &f1, &c) // 2**7 - 2 + for i := 0; i < 5; i++ { // 2**12 - 2**6 + p224Square(&f2, &f2, &c) + } + p224Mul(&f2, &f2, &f1, &c) // 2**12 - 1 + p224Square(&f3, &f2, &c) // 2**13 - 2 + for i := 0; i < 11; i++ { // 2**24 - 2**12 + p224Square(&f3, &f3, &c) + } + p224Mul(&f2, &f3, &f2, &c) // 2**24 - 1 + p224Square(&f3, &f2, &c) // 2**25 - 2 + for i := 0; i < 23; i++ { // 2**48 - 2**24 + p224Square(&f3, &f3, &c) + } + p224Mul(&f3, &f3, &f2, &c) // 2**48 - 1 + p224Square(&f4, &f3, &c) // 2**49 - 2 + for i := 0; i < 47; i++ { // 2**96 - 2**48 + p224Square(&f4, &f4, &c) + } + p224Mul(&f3, &f3, &f4, &c) // 2**96 - 1 + p224Square(&f4, &f3, &c) // 2**97 - 2 + for i := 0; i < 23; i++ { // 2**120 - 2**24 + p224Square(&f4, &f4, &c) + } + p224Mul(&f2, &f4, &f2, &c) // 2**120 - 1 + for i := 0; i < 6; i++ { // 2**126 - 2**6 + p224Square(&f2, &f2, &c) + } + p224Mul(&f1, &f1, &f2, &c) // 2**126 - 1 + p224Square(&f1, &f1, &c) // 2**127 - 2 + p224Mul(&f1, &f1, in, &c) // 2**127 - 1 + for i := 0; i < 97; i++ { // 2**224 - 2**97 + p224Square(&f1, &f1, &c) + } + p224Mul(out, &f1, &f3, &c) // 2**224 - 2**96 - 1 +} + +// p224Contract converts a FieldElement to its unique, minimal form. +// +// On entry, in[i] < 2**29 +// On exit, in[i] < 2**28 +func p224Contract(out, in *p224FieldElement) { + copy(out[:], in[:]) + + for i := 0; i < 7; i++ { + out[i+1] += out[i] >> 28 + out[i] &= bottom28Bits + } + top := out[7] >> 28 + out[7] &= bottom28Bits + + out[0] -= top + out[3] += top << 12 + + // We may just have made out[i] negative. So we carry down. If we made + // out[0] negative then we know that out[3] is sufficiently positive + // because we just added to it. + for i := 0; i < 3; i++ { + mask := uint32(int32(out[i]) >> 31) + out[i] += (1 << 28) & mask + out[i+1] -= 1 & mask + } + + // We might have pushed out[3] over 2**28 so we perform another, partial, + // carry chain. + for i := 3; i < 7; i++ { + out[i+1] += out[i] >> 28 + out[i] &= bottom28Bits + } + top = out[7] >> 28 + out[7] &= bottom28Bits + + // Eliminate top while maintaining the same value mod p. + out[0] -= top + out[3] += top << 12 + + // There are two cases to consider for out[3]: + // 1) The first time that we eliminated top, we didn't push out[3] over + // 2**28. In this case, the partial carry chain didn't change any values + // and top is zero. + // 2) We did push out[3] over 2**28 the first time that we eliminated top. + // The first value of top was in [0..16), therefore, prior to eliminating + // the first top, 0xfff1000 <= out[3] <= 0xfffffff. Therefore, after + // overflowing and being reduced by the second carry chain, out[3] <= + // 0xf000. Thus it cannot have overflowed when we eliminated top for the + // second time. + + // Again, we may just have made out[0] negative, so do the same carry down. + // As before, if we made out[0] negative then we know that out[3] is + // sufficiently positive. + for i := 0; i < 3; i++ { + mask := uint32(int32(out[i]) >> 31) + out[i] += (1 << 28) & mask + out[i+1] -= 1 & mask + } + + // Now we see if the value is >= p and, if so, subtract p. + + // First we build a mask from the top four limbs, which must all be + // equal to bottom28Bits if the whole value is >= p. If top4AllOnes + // ends up with any zero bits in the bottom 28 bits, then this wasn't + // true. + top4AllOnes := uint32(0xffffffff) + for i := 4; i < 8; i++ { + top4AllOnes &= (out[i] & bottom28Bits) - 1 + } + top4AllOnes |= 0xf0000000 + // Now we replicate any zero bits to all the bits in top4AllOnes. + top4AllOnes &= top4AllOnes >> 16 + top4AllOnes &= top4AllOnes >> 8 + top4AllOnes &= top4AllOnes >> 4 + top4AllOnes &= top4AllOnes >> 2 + top4AllOnes &= top4AllOnes >> 1 + top4AllOnes = uint32(int32(top4AllOnes<<31) >> 31) + + // Now we test whether the bottom three limbs are non-zero. + bottom3NonZero := out[0] | out[1] | out[2] + bottom3NonZero |= bottom3NonZero >> 16 + bottom3NonZero |= bottom3NonZero >> 8 + bottom3NonZero |= bottom3NonZero >> 4 + bottom3NonZero |= bottom3NonZero >> 2 + bottom3NonZero |= bottom3NonZero >> 1 + bottom3NonZero = uint32(int32(bottom3NonZero<<31) >> 31) + + // Everything depends on the value of out[3]. + // If it's > 0xffff000 and top4AllOnes != 0 then the whole value is >= p + // If it's = 0xffff000 and top4AllOnes != 0 and bottom3NonZero != 0, + // then the whole value is >= p + // If it's < 0xffff000, then the whole value is < p + n := out[3] - 0xffff000 + out3Equal := n + out3Equal |= out3Equal >> 16 + out3Equal |= out3Equal >> 8 + out3Equal |= out3Equal >> 4 + out3Equal |= out3Equal >> 2 + out3Equal |= out3Equal >> 1 + out3Equal = ^uint32(int32(out3Equal<<31) >> 31) + + // If out[3] > 0xffff000 then n's MSB will be zero. + out3GT := ^uint32(int32(n<<31) >> 31) + + mask := top4AllOnes & ((out3Equal & bottom3NonZero) | out3GT) + out[0] -= 1 & mask + out[3] -= 0xffff000 & mask + out[4] -= 0xfffffff & mask + out[5] -= 0xfffffff & mask + out[6] -= 0xfffffff & mask + out[7] -= 0xfffffff & mask +} + +// Group element functions. +// +// These functions deal with group elements. The group is an elliptic curve +// group with a = -3 defined in FIPS 186-3, section D.2.2. + +// p224AddJacobian computes *out = a+b where a != b. +func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-p224Add-2007-bl + var z1z1, z2z2, u1, u2, s1, s2, h, i, j, r, v p224FieldElement + var c p224LargeFieldElement + + // Z1Z1 = Z1² + p224Square(&z1z1, z1, &c) + // Z2Z2 = Z2² + p224Square(&z2z2, z2, &c) + // U1 = X1*Z2Z2 + p224Mul(&u1, x1, &z2z2, &c) + // U2 = X2*Z1Z1 + p224Mul(&u2, x2, &z1z1, &c) + // S1 = Y1*Z2*Z2Z2 + p224Mul(&s1, z2, &z2z2, &c) + p224Mul(&s1, y1, &s1, &c) + // S2 = Y2*Z1*Z1Z1 + p224Mul(&s2, z1, &z1z1, &c) + p224Mul(&s2, y2, &s2, &c) + // H = U2-U1 + p224Sub(&h, &u2, &u1) + p224Reduce(&h) + // I = (2*H)² + for j := 0; j < 8; j++ { + i[j] = h[j] << 1 + } + p224Reduce(&i) + p224Square(&i, &i, &c) + // J = H*I + p224Mul(&j, &h, &i, &c) + // r = 2*(S2-S1) + p224Sub(&r, &s2, &s1) + p224Reduce(&r) + for i := 0; i < 8; i++ { + r[i] <<= 1 + } + p224Reduce(&r) + // V = U1*I + p224Mul(&v, &u1, &i, &c) + // Z3 = ((Z1+Z2)²-Z1Z1-Z2Z2)*H + p224Add(&z1z1, &z1z1, &z2z2) + p224Add(&z2z2, z1, z2) + p224Reduce(&z2z2) + p224Square(&z2z2, &z2z2, &c) + p224Sub(z3, &z2z2, &z1z1) + p224Reduce(z3) + p224Mul(z3, z3, &h, &c) + // X3 = r²-J-2*V + for i := 0; i < 8; i++ { + z1z1[i] = v[i] << 1 + } + p224Add(&z1z1, &j, &z1z1) + p224Reduce(&z1z1) + p224Square(x3, &r, &c) + p224Sub(x3, x3, &z1z1) + p224Reduce(x3) + // Y3 = r*(V-X3)-2*S1*J + for i := 0; i < 8; i++ { + s1[i] <<= 1 + } + p224Mul(&s1, &s1, &j, &c) + p224Sub(&z1z1, &v, x3) + p224Reduce(&z1z1) + p224Mul(&z1z1, &z1z1, &r, &c) + p224Sub(y3, &z1z1, &s1) + p224Reduce(y3) +} + +// p224DoubleJacobian computes *out = a+a. +func p224DoubleJacobian(x3, y3, z3, x1, y1, z1 *p224FieldElement) { + var delta, gamma, beta, alpha, t p224FieldElement + var c p224LargeFieldElement + + p224Square(&delta, z1, &c) + p224Square(&gamma, y1, &c) + p224Mul(&beta, x1, &gamma, &c) + + // alpha = 3*(X1-delta)*(X1+delta) + p224Add(&t, x1, &delta) + for i := 0; i < 8; i++ { + t[i] += t[i] << 1 + } + p224Reduce(&t) + p224Sub(&alpha, x1, &delta) + p224Reduce(&alpha) + p224Mul(&alpha, &alpha, &t, &c) + + // Z3 = (Y1+Z1)²-gamma-delta + p224Add(z3, y1, z1) + p224Reduce(z3) + p224Square(z3, z3, &c) + p224Sub(z3, z3, &gamma) + p224Reduce(z3) + p224Sub(z3, z3, &delta) + p224Reduce(z3) + + // X3 = alpha²-8*beta + for i := 0; i < 8; i++ { + delta[i] = beta[i] << 3 + } + p224Reduce(&delta) + p224Square(x3, &alpha, &c) + p224Sub(x3, x3, &delta) + p224Reduce(x3) + + // Y3 = alpha*(4*beta-X3)-8*gamma² + for i := 0; i < 8; i++ { + beta[i] <<= 2 + } + p224Sub(&beta, &beta, x3) + p224Reduce(&beta) + p224Square(&gamma, &gamma, &c) + for i := 0; i < 8; i++ { + gamma[i] <<= 3 + } + p224Reduce(&gamma) + p224Mul(y3, &alpha, &beta, &c) + p224Sub(y3, y3, &gamma) + p224Reduce(y3) +} + +// p224CopyConditional sets *out = *in iff the least-significant-bit of control +// is true, and it runs in constant time. +func p224CopyConditional(out, in *p224FieldElement, control uint32) { + control <<= 31 + control = uint32(int32(control) >> 31) + + for i := 0; i < 8; i++ { + out[i] ^= (out[i] ^ in[i]) & control + } +} + +func p224ScalarMult(outX, outY, outZ, inX, inY, inZ *p224FieldElement, scalar []byte) { + var xx, yy, zz p224FieldElement + for i := 0; i < 8; i++ { + 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 + } + } +} + +// p224ToAffine converts from Jacobian to affine form. +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 + } + + p224Invert(&zinv, z) + p224Square(&zinvsq, &zinv, &tmp) + p224Mul(x, x, &zinvsq, &tmp) + p224Mul(&zinvsq, &zinvsq, &zinv, &tmp) + p224Mul(y, y, &zinvsq, &tmp) + + p224Contract(&outx, x) + p224Contract(&outy, y) + return p224ToBig(&outx), p224ToBig(&outy) +} + +// get28BitsFromEnd returns the least-significant 28 bits from buf>>shift, +// where buf is interpreted as a big-endian number. +func get28BitsFromEnd(buf []byte, shift uint) (uint32, []byte) { + var ret uint32 + + for i := uint(0); i < 4; i++ { + var b byte + if l := len(buf); l > 0 { + b = buf[l-1] + // We don't remove the byte if we're about to return and we're not + // reading all of it. + if i != 3 || shift == 4 { + buf = buf[:l-1] + } + } + ret |= uint32(b) << (8 * i) >> shift + } + ret &= bottom28Bits + return ret, buf +} + +// p224FromBig sets *out = *in. +func p224FromBig(out *p224FieldElement, in *big.Int) { + bytes := in.Bytes() + out[0], bytes = get28BitsFromEnd(bytes, 0) + out[1], bytes = get28BitsFromEnd(bytes, 4) + out[2], bytes = get28BitsFromEnd(bytes, 0) + out[3], bytes = get28BitsFromEnd(bytes, 4) + out[4], bytes = get28BitsFromEnd(bytes, 0) + out[5], bytes = get28BitsFromEnd(bytes, 4) + out[6], bytes = get28BitsFromEnd(bytes, 0) + out[7], bytes = get28BitsFromEnd(bytes, 4) +} + +// p224ToBig returns in as a big.Int. +func p224ToBig(in *p224FieldElement) *big.Int { + var buf [28]byte + buf[27] = byte(in[0]) + buf[26] = byte(in[0] >> 8) + buf[25] = byte(in[0] >> 16) + buf[24] = byte(((in[0] >> 24) & 0x0f) | (in[1]<<4)&0xf0) + + buf[23] = byte(in[1] >> 4) + buf[22] = byte(in[1] >> 12) + buf[21] = byte(in[1] >> 20) + + buf[20] = byte(in[2]) + buf[19] = byte(in[2] >> 8) + buf[18] = byte(in[2] >> 16) + buf[17] = byte(((in[2] >> 24) & 0x0f) | (in[3]<<4)&0xf0) + + buf[16] = byte(in[3] >> 4) + buf[15] = byte(in[3] >> 12) + buf[14] = byte(in[3] >> 20) + + buf[13] = byte(in[4]) + buf[12] = byte(in[4] >> 8) + buf[11] = byte(in[4] >> 16) + buf[10] = byte(((in[4] >> 24) & 0x0f) | (in[5]<<4)&0xf0) + + buf[9] = byte(in[5] >> 4) + buf[8] = byte(in[5] >> 12) + buf[7] = byte(in[5] >> 20) + + buf[6] = byte(in[6]) + buf[5] = byte(in[6] >> 8) + buf[4] = byte(in[6] >> 16) + buf[3] = byte(((in[6] >> 24) & 0x0f) | (in[7]<<4)&0xf0) + + buf[2] = byte(in[7] >> 4) + buf[1] = byte(in[7] >> 12) + buf[0] = byte(in[7] >> 20) + + return new(big.Int).SetBytes(buf[:]) +} diff --git a/src/pkg/crypto/elliptic/p224_test.go b/src/pkg/crypto/elliptic/p224_test.go new file mode 100644 index 000000000..4b26d1610 --- /dev/null +++ b/src/pkg/crypto/elliptic/p224_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 elliptic + +import ( + "math/big" + "testing" +) + +var toFromBigTests = []string{ + "0", + "1", + "23", + "b70e0cb46bb4bf7f321390b94a03c1d356c01122343280d6105c1d21", + "706a46d476dcb76798e6046d89474788d164c18032d268fd10704fa6", +} + +func p224AlternativeToBig(in *p224FieldElement) *big.Int { + ret := new(big.Int) + tmp := new(big.Int) + + for i := uint(0); i < 8; i++ { + tmp.SetInt64(int64(in[i])) + tmp.Lsh(tmp, 28*i) + ret.Add(ret, tmp) + } + ret.Mod(ret, p224.P) + return ret +} + +func TestToFromBig(t *testing.T) { + for i, test := range toFromBigTests { + n, _ := new(big.Int).SetString(test, 16) + var x p224FieldElement + p224FromBig(&x, n) + m := p224ToBig(&x) + if n.Cmp(m) != 0 { + t.Errorf("#%d: %x != %x", i, n, m) + } + q := p224AlternativeToBig(&x) + if n.Cmp(q) != 0 { + t.Errorf("#%d: %x != %x (alternative)", i, n, m) + } + } +} diff --git a/src/pkg/crypto/hmac/Makefile b/src/pkg/crypto/hmac/Makefile deleted file mode 100644 index cc69abf60..000000000 --- a/src/pkg/crypto/hmac/Makefile +++ /dev/null @@ -1,11 +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 ../../../Make.inc - -TARG=crypto/hmac -GOFILES=\ - hmac.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/hmac/hmac.go b/src/pkg/crypto/hmac/hmac.go index 04ec86e9a..a97ce0972 100644 --- a/src/pkg/crypto/hmac/hmac.go +++ b/src/pkg/crypto/hmac/hmac.go @@ -9,33 +9,20 @@ package hmac import ( - "crypto/md5" - "crypto/sha1" - "crypto/sha256" "hash" - "os" ) // FIPS 198: // http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf -// key is zero padded to 64 bytes -// ipad = 0x36 byte repeated to 64 bytes -// opad = 0x5c byte repeated to 64 bytes +// key is zero padded to the block size of the hash function +// ipad = 0x36 byte repeated for key length +// opad = 0x5c byte repeated for key length // hmac = H([key ^ opad] H([key ^ ipad] text)) -const ( - // NOTE(rsc): This constant is actually the - // underlying hash function's block size. - // HMAC is only conventionally used with - // MD5 and SHA1, and both use 64-byte blocks. - // The hash.Hash interface doesn't provide a - // way to find out the block size. - padSize = 64 -) - type hmac struct { size int + blocksize int key, tmp []byte outer, inner hash.Hash } @@ -44,57 +31,50 @@ func (h *hmac) tmpPad(xor byte) { for i, k := range h.key { h.tmp[i] = xor ^ k } - for i := len(h.key); i < padSize; i++ { + for i := len(h.key); i < h.blocksize; i++ { h.tmp[i] = xor } } -func (h *hmac) Sum() []byte { - sum := h.inner.Sum() +func (h *hmac) Sum(in []byte) []byte { + origLen := len(in) + in = h.inner.Sum(in) h.tmpPad(0x5c) - for i, b := range sum { - h.tmp[padSize+i] = b - } + copy(h.tmp[h.blocksize:], in[origLen:]) h.outer.Reset() h.outer.Write(h.tmp) - return h.outer.Sum() + return h.outer.Sum(in[:origLen]) } -func (h *hmac) Write(p []byte) (n int, err os.Error) { +func (h *hmac) Write(p []byte) (n int, err error) { return h.inner.Write(p) } func (h *hmac) Size() int { return h.size } +func (h *hmac) BlockSize() int { return h.blocksize } + func (h *hmac) Reset() { h.inner.Reset() h.tmpPad(0x36) - h.inner.Write(h.tmp[0:padSize]) + h.inner.Write(h.tmp[0:h.blocksize]) } -// New returns a new HMAC hash using the given hash generator and key. +// New returns a new HMAC hash using the given hash.Hash type and key. func New(h func() hash.Hash, key []byte) hash.Hash { hm := new(hmac) hm.outer = h() hm.inner = h() hm.size = hm.inner.Size() - hm.tmp = make([]byte, padSize+hm.size) - if len(key) > padSize { + hm.blocksize = hm.inner.BlockSize() + hm.tmp = make([]byte, hm.blocksize+hm.size) + if len(key) > hm.blocksize { // If key is too big, hash it. hm.outer.Write(key) - key = hm.outer.Sum() + key = hm.outer.Sum(nil) } hm.key = make([]byte, len(key)) copy(hm.key, key) hm.Reset() return hm } - -// NewMD5 returns a new HMAC-MD5 hash using the given key. -func NewMD5(key []byte) hash.Hash { return New(md5.New, key) } - -// NewSHA1 returns a new HMAC-SHA1 hash using the given key. -func NewSHA1(key []byte) hash.Hash { return New(sha1.New, key) } - -// NewSHA256 returns a new HMAC-SHA256 hash using the given key. -func NewSHA256(key []byte) hash.Hash { return New(sha256.New, key) } diff --git a/src/pkg/crypto/hmac/hmac_test.go b/src/pkg/crypto/hmac/hmac_test.go index bcae63b8a..07957414c 100644 --- a/src/pkg/crypto/hmac/hmac_test.go +++ b/src/pkg/crypto/hmac/hmac_test.go @@ -5,13 +5,17 @@ package hmac import ( - "hash" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" "fmt" + "hash" "testing" ) type hmacTest struct { - hash func([]byte) hash.Hash + hash func() hash.Hash key []byte in []byte out string @@ -21,7 +25,7 @@ var hmacTests = []hmacTest{ // Tests from US FIPS 198 // http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf { - NewSHA1, + sha1.New, []byte{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, @@ -36,7 +40,7 @@ var hmacTests = []hmacTest{ "4f4ca3d5d68ba7cc0a1208c9c61e9c5da0403c0a", }, { - NewSHA1, + sha1.New, []byte{ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, @@ -46,7 +50,7 @@ var hmacTests = []hmacTest{ "0922d3405faa3d194f82a45830737d5cc6c75d24", }, { - NewSHA1, + sha1.New, []byte{ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, @@ -68,7 +72,7 @@ var hmacTests = []hmacTest{ // Test from Plan 9. { - NewMD5, + md5.New, []byte("Jefe"), []byte("what do ya want for nothing?"), "750c783e6ab0b503eaa86e310a5db738", @@ -76,7 +80,7 @@ var hmacTests = []hmacTest{ // Tests from RFC 4231 { - NewSHA256, + sha256.New, []byte{ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, @@ -86,13 +90,13 @@ var hmacTests = []hmacTest{ "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7", }, { - NewSHA256, + sha256.New, []byte("Jefe"), []byte("what do ya want for nothing?"), "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843", }, { - NewSHA256, + sha256.New, []byte{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, @@ -110,7 +114,7 @@ var hmacTests = []hmacTest{ "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe", }, { - NewSHA256, + sha256.New, []byte{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, @@ -129,7 +133,7 @@ var hmacTests = []hmacTest{ "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b", }, { - NewSHA256, + sha256.New, []byte{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, @@ -153,7 +157,7 @@ var hmacTests = []hmacTest{ "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54", }, { - NewSHA256, + sha256.New, []byte{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, @@ -178,11 +182,295 @@ var hmacTests = []hmacTest{ "be hashed before being used by the HMAC algorithm."), "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2", }, + + // Tests from http://csrc.nist.gov/groups/ST/toolkit/examples.html + // (truncated tag tests are left out) + { + sha1.New, + []byte{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + []byte("Sample message for keylen=blocklen"), + "5fd596ee78d5553c8ff4e72d266dfd192366da29", + }, + { + sha1.New, + []byte{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + }, + []byte("Sample message for keylen 0 { - n := len(p) - 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:]) - d.nx = 0 - } - p = p[n:] - } - n := _Block(d, p) - p = p[n:] - if len(p) > 0 { - d.nx = copy(d.x[:], p) - } - return -} - -func (d0 *digest) Sum() []byte { - // Make a copy of d0, so that caller can keep writing and summing. - d := new(digest) - *d = *d0 - - // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. - len := d.len - var tmp [64]byte - tmp[0] = 0x80 - if len%64 < 56 { - d.Write(tmp[0 : 56-len%64]) - } else { - d.Write(tmp[0 : 64+56-len%64]) - } - - // Length in bits. - len <<= 3 - for i := uint(0); i < 8; i++ { - tmp[i] = byte(len >> (8 * i)) - } - d.Write(tmp[0:8]) - - if d.nx != 0 { - panic("d.nx != 0") - } - - p := make([]byte, 16) - j := 0 - for _, s := range d.s { - p[j+0] = byte(s >> 0) - p[j+1] = byte(s >> 8) - p[j+2] = byte(s >> 16) - p[j+3] = byte(s >> 24) - j += 4 - } - return p -} diff --git a/src/pkg/crypto/md4/md4_test.go b/src/pkg/crypto/md4/md4_test.go deleted file mode 100644 index 721bd4cbc..000000000 --- a/src/pkg/crypto/md4/md4_test.go +++ /dev/null @@ -1,71 +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. - -package md4 - -import ( - "fmt" - "io" - "testing" -) - -type md4Test struct { - out string - in string -} - -var golden = []md4Test{ - {"31d6cfe0d16ae931b73c59d7e0c089c0", ""}, - {"bde52cb31de33e46245e05fbdbd6fb24", "a"}, - {"ec388dd78999dfc7cf4632465693b6bf", "ab"}, - {"a448017aaf21d8525fc10ae87aa6729d", "abc"}, - {"41decd8f579255c5200f86a4bb3ba740", "abcd"}, - {"9803f4a34e8eb14f96adba49064a0c41", "abcde"}, - {"804e7f1c2586e50b49ac65db5b645131", "abcdef"}, - {"752f4adfe53d1da0241b5bc216d098fc", "abcdefg"}, - {"ad9daf8d49d81988590a6f0e745d15dd", "abcdefgh"}, - {"1e4e28b05464316b56402b3815ed2dfd", "abcdefghi"}, - {"dc959c6f5d6f9e04e4380777cc964b3d", "abcdefghij"}, - {"1b5701e265778898ef7de5623bbe7cc0", "Discard medicine more than two years old."}, - {"d7f087e090fe7ad4a01cb59dacc9a572", "He who has a shady past knows that nice guys finish last."}, - {"a6f8fd6df617c72837592fc3570595c9", "I wouldn't marry him with a ten foot pole."}, - {"c92a84a9526da8abc240c05d6b1a1ce0", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, - {"f6013160c4dcb00847069fee3bb09803", "The days of the digital watch are numbered. -Tom Stoppard"}, - {"2c3bb64f50b9107ed57640fe94bec09f", "Nepal premier won't resign."}, - {"45b7d8a32c7806f2f7f897332774d6e4", "For every action there is an equal and opposite government program."}, - {"b5b4f9026b175c62d7654bdc3a1cd438", "His money is twice tainted: 'taint yours and 'taint mine."}, - {"caf44e80f2c20ce19b5ba1cab766e7bd", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, - {"191fae6707f496aa54a6bce9f2ecf74d", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, - {"9ddc753e7a4ccee6081cd1b45b23a834", "size: a.out: bad magic"}, - {"8d050f55b1cadb9323474564be08a521", "The major problem is with sendmail. -Mark Horton"}, - {"ad6e2587f74c3e3cc19146f6127fa2e3", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, - {"1d616d60a5fabe85589c3f1566ca7fca", "If the enemy is within range, then so are you."}, - {"aec3326a4f496a2ced65a1963f84577f", "It's well we cannot hear the screams/That we create in others' dreams."}, - {"77b4fd762d6b9245e61c50bf6ebf118b", "You remind me of a TV show, but that's all right: I watch it anyway."}, - {"e8f48c726bae5e516f6ddb1a4fe62438", "C is as portable as Stonehedge!!"}, - {"a3a84366e7219e887423b01f9be7166e", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, - {"a6b7aa35157e984ef5d9b7f32e5fbb52", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, - {"75661f0545955f8f9abeeb17845f3fd6", "How can you write a big system without C++? -Paul Glick"}, -} - -func TestGolden(t *testing.T) { - for i := 0; i < len(golden); i++ { - g := golden[i] - c := New() - for j := 0; j < 3; j++ { - if j < 2 { - io.WriteString(c, g.in) - } else { - io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() - io.WriteString(c, g.in[len(g.in)/2:]) - } - s := fmt.Sprintf("%x", c.Sum()) - if s != g.out { - t.Fatalf("md4[%d](%s) = %s want %s", j, g.in, s, g.out) - } - c.Reset() - } - } -} diff --git a/src/pkg/crypto/md4/md4block.go b/src/pkg/crypto/md4/md4block.go deleted file mode 100644 index 3fed475f3..000000000 --- a/src/pkg/crypto/md4/md4block.go +++ /dev/null @@ -1,89 +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. - -// MD4 block step. -// In its own file so that a faster assembly or C version -// can be substituted easily. - -package md4 - -var shift1 = []uint{3, 7, 11, 19} -var shift2 = []uint{3, 5, 9, 13} -var shift3 = []uint{3, 9, 11, 15} - -var xIndex2 = []uint{0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15} -var xIndex3 = []uint{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15} - -func _Block(dig *digest, p []byte) int { - 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 { - 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 - } - - // 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] - a = a<>(32-s) - a, b, c, d = d, a, b, c - } - - // Round 2. - for i := uint(0); i < 16; i++ { - x := xIndex2[i] - s := shift2[i%4] - g := (b & c) | (b & d) | (c & d) - a += g + X[x] + 0x5a827999 - a = a<>(32-s) - a, b, c, d = d, a, b, c - } - - // Round 3. - for i := uint(0); i < 16; i++ { - x := xIndex3[i] - s := shift3[i%4] - h := b ^ c ^ d - a += h + X[x] + 0x6ed9eba1 - a = a<>(32-s) - a, b, c, d = d, a, b, c - } - - a += aa - b += bb - c += cc - d += dd - - p = p[_Chunk:] - n += _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/md5/Makefile b/src/pkg/crypto/md5/Makefile deleted file mode 100644 index 5cde3e6d6..000000000 --- a/src/pkg/crypto/md5/Makefile +++ /dev/null @@ -1,12 +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 ../../../Make.inc - -TARG=crypto/md5 -GOFILES=\ - md5.go\ - md5block.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/md5/md5.go b/src/pkg/crypto/md5/md5.go index 378faa6ec..cfb728c94 100644 --- a/src/pkg/crypto/md5/md5.go +++ b/src/pkg/crypto/md5/md5.go @@ -8,7 +8,6 @@ package md5 import ( "crypto" "hash" - "os" ) func init() { @@ -18,6 +17,9 @@ func init() { // The size of an MD5 checksum in bytes. const Size = 16 +// The blocksize of MD5 in bytes. +const BlockSize = 64 + const ( _Chunk = 64 _Init0 = 0x67452301 @@ -52,7 +54,9 @@ func New() hash.Hash { func (d *digest) Size() int { return Size } -func (d *digest) Write(p []byte) (nn int, err os.Error) { +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { nn = len(p) d.len += uint64(nn) if d.nx > 0 { @@ -78,10 +82,9 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. - d := new(digest) - *d = *d0 + d := *d0 // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. len := d.len @@ -104,14 +107,13 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 16) - j := 0 - for _, s := range d.s { - p[j+0] = byte(s >> 0) - p[j+1] = byte(s >> 8) - p[j+2] = byte(s >> 16) - p[j+3] = byte(s >> 24) - j += 4 + var digest [Size]byte + for i, s := range d.s { + digest[i*4] = byte(s) + digest[i*4+1] = byte(s >> 8) + digest[i*4+2] = byte(s >> 16) + digest[i*4+3] = byte(s >> 24) } - return p + + return append(in, digest[:]...) } diff --git a/src/pkg/crypto/md5/md5_test.go b/src/pkg/crypto/md5/md5_test.go index 857002b70..aae875464 100644 --- a/src/pkg/crypto/md5/md5_test.go +++ b/src/pkg/crypto/md5/md5_test.go @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package md5 +package md5_test import ( + "crypto/md5" "fmt" "io" "testing" @@ -52,16 +53,16 @@ var golden = []md5Test{ func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] - c := New() + c := md5.New() for j := 0; j < 3; j++ { if j < 2 { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("md5[%d](%s) = %s want %s", j, g.in, s, g.out) } @@ -69,3 +70,11 @@ func TestGolden(t *testing.T) { } } } + +func ExampleNew() { + h := md5.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 +} diff --git a/src/pkg/crypto/ocsp/Makefile b/src/pkg/crypto/ocsp/Makefile deleted file mode 100644 index 6e132ff9b..000000000 --- a/src/pkg/crypto/ocsp/Makefile +++ /dev/null @@ -1,11 +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 ../../../Make.inc - -TARG=crypto/ocsp -GOFILES=\ - ocsp.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/ocsp/ocsp.go b/src/pkg/crypto/ocsp/ocsp.go deleted file mode 100644 index 7ea7a1e82..000000000 --- a/src/pkg/crypto/ocsp/ocsp.go +++ /dev/null @@ -1,192 +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. - -// Package ocsp parses OCSP responses as specified in RFC 2560. OCSP responses -// are signed messages attesting to the validity of a certificate for a small -// period of time. This is used to manage revocation for X.509 certificates. -package ocsp - -import ( - "asn1" - "crypto" - "crypto/rsa" - _ "crypto/sha1" - "crypto/x509" - "crypto/x509/pkix" - "os" - "time" -) - -var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1}) -var idSHA1WithRSA = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 1, 5}) - -// These are internal structures that reflect the ASN.1 structure of an OCSP -// response. See RFC 2560, section 4.2. - -const ( - ocspSuccess = 0 - ocspMalformed = 1 - ocspInternalError = 2 - ocspTryLater = 3 - ocspSigRequired = 4 - ocspUnauthorized = 5 -) - -type certID struct { - HashAlgorithm pkix.AlgorithmIdentifier - NameHash []byte - IssuerKeyHash []byte - SerialNumber asn1.RawValue -} - -type responseASN1 struct { - Status asn1.Enumerated - Response responseBytes `asn1:"explicit,tag:0"` -} - -type responseBytes struct { - ResponseType asn1.ObjectIdentifier - Response []byte -} - -type basicResponse struct { - TBSResponseData responseData - SignatureAlgorithm pkix.AlgorithmIdentifier - Signature asn1.BitString - Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"` -} - -type responseData struct { - Raw asn1.RawContent - Version int `asn1:"optional,default:1,explicit,tag:0"` - RequestorName pkix.RDNSequence `asn1:"optional,explicit,tag:1"` - KeyHash []byte `asn1:"optional,explicit,tag:2"` - ProducedAt *time.Time - Responses []singleResponse -} - -type singleResponse struct { - CertID certID - Good asn1.Flag `asn1:"explicit,tag:0,optional"` - Revoked revokedInfo `asn1:"explicit,tag:1,optional"` - Unknown asn1.Flag `asn1:"explicit,tag:2,optional"` - ThisUpdate *time.Time - NextUpdate *time.Time `asn1:"explicit,tag:0,optional"` -} - -type revokedInfo struct { - RevocationTime *time.Time - Reason int `asn1:"explicit,tag:0,optional"` -} - -// This is the exposed reflection of the internal OCSP structures. - -const ( - // Good means that the certificate is valid. - Good = iota - // Revoked means that the certificate has been deliberately revoked. - Revoked = iota - // Unknown means that the OCSP responder doesn't know about the certificate. - Unknown = iota - // ServerFailed means that the OCSP responder failed to process the request. - ServerFailed = iota -) - -// Response represents an OCSP response. See RFC 2560. -type Response struct { - // Status is one of {Good, Revoked, Unknown, ServerFailed} - Status int - SerialNumber []byte - ProducedAt, ThisUpdate, NextUpdate, RevokedAt *time.Time - RevocationReason int - Certificate *x509.Certificate -} - -// ParseError results from an invalid OCSP response. -type ParseError string - -func (p ParseError) String() string { - return string(p) -} - -// ParseResponse parses an OCSP response in DER form. It only supports -// responses for a single certificate and only those using RSA signatures. -// Non-RSA responses will result in an x509.UnsupportedAlgorithmError. -// Signature errors or parse failures will result in a ParseError. -func ParseResponse(bytes []byte) (*Response, os.Error) { - var resp responseASN1 - rest, err := asn1.Unmarshal(bytes, &resp) - if err != nil { - return nil, err - } - if len(rest) > 0 { - return nil, ParseError("trailing data in OCSP response") - } - - ret := new(Response) - if resp.Status != ocspSuccess { - ret.Status = ServerFailed - return ret, nil - } - - if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) { - return nil, ParseError("bad OCSP response type") - } - - var basicResp basicResponse - rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp) - if err != nil { - return nil, err - } - - if len(basicResp.Certificates) != 1 { - return nil, ParseError("OCSP response contains bad number of certificates") - } - - if len(basicResp.TBSResponseData.Responses) != 1 { - return nil, ParseError("OCSP response contains bad number of responses") - } - - ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes) - if err != nil { - return nil, err - } - - if ret.Certificate.PublicKeyAlgorithm != x509.RSA || !basicResp.SignatureAlgorithm.Algorithm.Equal(idSHA1WithRSA) { - return nil, x509.UnsupportedAlgorithmError{} - } - - hashType := crypto.SHA1 - h := hashType.New() - - pub := ret.Certificate.PublicKey.(*rsa.PublicKey) - h.Write(basicResp.TBSResponseData.Raw) - digest := h.Sum() - signature := basicResp.Signature.RightAlign() - - if rsa.VerifyPKCS1v15(pub, hashType, digest, signature) != nil { - return nil, ParseError("bad OCSP signature") - } - - r := basicResp.TBSResponseData.Responses[0] - - ret.SerialNumber = r.CertID.SerialNumber.Bytes - - switch { - case bool(r.Good): - ret.Status = Good - case bool(r.Unknown): - ret.Status = Unknown - default: - ret.Status = Revoked - ret.RevokedAt = r.Revoked.RevocationTime - ret.RevocationReason = r.Revoked.Reason - } - - ret.ProducedAt = basicResp.TBSResponseData.ProducedAt - ret.ThisUpdate = r.ThisUpdate - ret.NextUpdate = r.NextUpdate - - return ret, nil -} diff --git a/src/pkg/crypto/ocsp/ocsp_test.go b/src/pkg/crypto/ocsp/ocsp_test.go deleted file mode 100644 index f9889790f..000000000 --- a/src/pkg/crypto/ocsp/ocsp_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package ocsp - -import ( - "bytes" - "encoding/hex" - "reflect" - "testing" - "time" -) - -func TestOCSPDecode(t *testing.T) { - responseBytes, _ := hex.DecodeString(ocspResponseHex) - resp, err := ParseResponse(responseBytes) - if err != nil { - t.Error(err) - } - - expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}} - - if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) { - t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate) - } - - if !reflect.DeepEqual(resp.NextUpdate, resp.NextUpdate) { - t.Errorf("resp.NextUpdate: got %d, want %d", resp.NextUpdate, expected.NextUpdate) - } - - if resp.Status != expected.Status { - t.Errorf("resp.Status: got %d, want %d", resp.Status, expected.Status) - } - - if !bytes.Equal(resp.SerialNumber, expected.SerialNumber) { - t.Errorf("resp.SerialNumber: got %x, want %x", resp.SerialNumber, expected.SerialNumber) - } - - if resp.RevocationReason != expected.RevocationReason { - t.Errorf("resp.RevocationReason: got %d, want %d", resp.RevocationReason, expected.RevocationReason) - } -} - -// This OCSP response was taken from Thawte's public OCSP responder. -// To recreate: -// $ openssl s_client -tls1 -showcerts -servername www.google.com -connect www.google.com:443 -// Copy and paste the first certificate into /tmp/cert.crt and the second into -// /tmp/intermediate.crt -// $ openssl ocsp -issuer /tmp/intermediate.crt -cert /tmp/cert.crt -url http://ocsp.thawte.com -resp_text -respout /tmp/ocsp.der -// Then hex encode the result: -// $ python -c 'print file("/tmp/ocsp.der", "r").read().encode("hex")' - -const ocspResponseHex = "308206bc0a0100a08206b5308206b106092b0601050507300101048206a23082069e3081" + - "c9a14e304c310b300906035504061302494c31163014060355040a130d5374617274436f" + - "6d204c74642e312530230603550403131c5374617274436f6d20436c6173732031204f43" + - "5350205369676e6572180f32303130303730373137333531375a30663064303c30090605" + - "2b0e03021a050004146568874f40750f016a3475625e1f5c93e5a26d580414eb4234d098" + - "b0ab9ff41b6b08f7cc642eef0e2c45020301d0fa8000180f323031303037303731353031" + - "30355aa011180f32303130303730373138333531375a300d06092a864886f70d01010505" + - "000382010100ab557ff070d1d7cebbb5f0ec91a15c3fed22eb2e1b8244f1b84545f013a4" + - "fb46214c5e3fbfbebb8a56acc2b9db19f68fd3c3201046b3824d5ba689f99864328710cb" + - "467195eb37d84f539e49f859316b32964dc3e47e36814ce94d6c56dd02733b1d0802f7ff" + - "4eebdbbd2927dcf580f16cbc290f91e81b53cb365e7223f1d6e20a88ea064104875e0145" + - "672b20fc14829d51ca122f5f5d77d3ad6c83889c55c7dc43680ba2fe3cef8b05dbcabdc0" + - "d3e09aaf9725597f8c858c2fa38c0d6aed2e6318194420dd1a1137445d13e1c97ab47896" + - "17a4e08925f46f867b72e3a4dc1f08cb870b2b0717f7207faa0ac512e628a029aba7457a" + - "e63dcf3281e2162d9349a08204ba308204b6308204b23082039aa003020102020101300d" + - "06092a864886f70d010105050030818c310b300906035504061302494c31163014060355" + - "040a130d5374617274436f6d204c74642e312b3029060355040b13225365637572652044" + - "69676974616c204365727469666963617465205369676e696e6731383036060355040313" + - "2f5374617274436f6d20436c6173732031205072696d61727920496e7465726d65646961" + - "746520536572766572204341301e170d3037313032353030323330365a170d3132313032" + - "333030323330365a304c310b300906035504061302494c31163014060355040a130d5374" + - "617274436f6d204c74642e312530230603550403131c5374617274436f6d20436c617373" + - "2031204f435350205369676e657230820122300d06092a864886f70d0101010500038201" + - "0f003082010a0282010100b9561b4c45318717178084e96e178df2255e18ed8d8ecc7c2b" + - "7b51a6c1c2e6bf0aa3603066f132fe10ae97b50e99fa24b83fc53dd2777496387d14e1c3" + - "a9b6a4933e2ac12413d085570a95b8147414a0bc007c7bcf222446ef7f1a156d7ea1c577" + - "fc5f0facdfd42eb0f5974990cb2f5cefebceef4d1bdc7ae5c1075c5a99a93171f2b0845b" + - "4ff0864e973fcfe32f9d7511ff87a3e943410c90a4493a306b6944359340a9ca96f02b66" + - "ce67f028df2980a6aaee8d5d5d452b8b0eb93f923cc1e23fcccbdbe7ffcb114d08fa7a6a" + - "3c404f825d1a0e715935cf623a8c7b59670014ed0622f6089a9447a7a19010f7fe58f841" + - "29a2765ea367824d1c3bb2fda308530203010001a382015c30820158300c0603551d1301" + - "01ff04023000300b0603551d0f0404030203a8301e0603551d250417301506082b060105" + - "0507030906092b0601050507300105301d0603551d0e0416041445e0a36695414c5dd449" + - "bc00e33cdcdbd2343e173081a80603551d230481a030819d8014eb4234d098b0ab9ff41b" + - "6b08f7cc642eef0e2c45a18181a47f307d310b300906035504061302494c311630140603" + - "55040a130d5374617274436f6d204c74642e312b3029060355040b132253656375726520" + - "4469676974616c204365727469666963617465205369676e696e67312930270603550403" + - "13205374617274436f6d2043657274696669636174696f6e20417574686f726974798201" + - "0a30230603551d12041c301a8618687474703a2f2f7777772e737461727473736c2e636f" + - "6d2f302c06096086480186f842010d041f161d5374617274436f6d205265766f63617469" + - "6f6e20417574686f72697479300d06092a864886f70d01010505000382010100182d2215" + - "8f0fc0291324fa8574c49bb8ff2835085adcbf7b7fc4191c397ab6951328253fffe1e5ec" + - "2a7da0d50fca1a404e6968481366939e666c0a6209073eca57973e2fefa9ed1718e8176f" + - "1d85527ff522c08db702e3b2b180f1cbff05d98128252cf0f450f7dd2772f4188047f19d" + - "c85317366f94bc52d60f453a550af58e308aaab00ced33040b62bf37f5b1ab2a4f7f0f80" + - "f763bf4d707bc8841d7ad9385ee2a4244469260b6f2bf085977af9074796048ecc2f9d48" + - "a1d24ce16e41a9941568fec5b42771e118f16c106a54ccc339a4b02166445a167902e75e" + - "6d8620b0825dcd18a069b90fd851d10fa8effd409deec02860d26d8d833f304b10669b42" diff --git a/src/pkg/crypto/openpgp/Makefile b/src/pkg/crypto/openpgp/Makefile deleted file mode 100644 index b46ac2bba..000000000 --- a/src/pkg/crypto/openpgp/Makefile +++ /dev/null @@ -1,14 +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. - -include ../../../Make.inc - -TARG=crypto/openpgp -GOFILES=\ - canonical_text.go\ - keys.go\ - read.go\ - write.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/armor/Makefile b/src/pkg/crypto/openpgp/armor/Makefile deleted file mode 100644 index 138e314e9..000000000 --- a/src/pkg/crypto/openpgp/armor/Makefile +++ /dev/null @@ -1,12 +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. - -include ../../../../Make.inc - -TARG=crypto/openpgp/armor -GOFILES=\ - armor.go\ - encode.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/armor/armor.go b/src/pkg/crypto/openpgp/armor/armor.go deleted file mode 100644 index 9c4180d6d..000000000 --- a/src/pkg/crypto/openpgp/armor/armor.go +++ /dev/null @@ -1,220 +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. - -// Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is -// very similar to PEM except that it has an additional CRC checksum. -package armor - -import ( - "bufio" - "bytes" - "crypto/openpgp/error" - "encoding/base64" - "io" - "os" -) - -// A Block represents an OpenPGP armored structure. -// -// The encoded form is: -// -----BEGIN Type----- -// Headers -// -// base64-encoded Bytes -// '=' base64 encoded checksum -// -----END Type----- -// where Headers is a possibly empty sequence of Key: Value lines. -// -// Since the armored data can be very large, this package presents a streaming -// interface. -type Block struct { - Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE"). - Header map[string]string // Optional headers. - Body io.Reader // A Reader from which the contents can be read - lReader lineReader - oReader openpgpReader -} - -var ArmorCorrupt os.Error = error.StructuralError("armor invalid") - -const crc24Init = 0xb704ce -const crc24Poly = 0x1864cfb -const crc24Mask = 0xffffff - -// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1 -func crc24(crc uint32, d []byte) uint32 { - for _, b := range d { - crc ^= uint32(b) << 16 - for i := 0; i < 8; i++ { - crc <<= 1 - if crc&0x1000000 != 0 { - crc ^= crc24Poly - } - } - } - return crc -} - -var armorStart = []byte("-----BEGIN ") -var armorEnd = []byte("-----END ") -var armorEndOfLine = []byte("-----") - -// lineReader wraps a line based reader. It watches for the end of an armor -// block and records the expected CRC value. -type lineReader struct { - in *bufio.Reader - buf []byte - eof bool - crc uint32 -} - -func (l *lineReader) Read(p []byte) (n int, err os.Error) { - if l.eof { - return 0, os.EOF - } - - if len(l.buf) > 0 { - n = copy(p, l.buf) - l.buf = l.buf[n:] - return - } - - line, isPrefix, err := l.in.ReadLine() - if err != nil { - return - } - if isPrefix { - return 0, ArmorCorrupt - } - - if len(line) == 5 && line[0] == '=' { - // This is the checksum line - var expectedBytes [3]byte - var m int - m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:]) - if m != 3 || err != nil { - return - } - l.crc = uint32(expectedBytes[0])<<16 | - uint32(expectedBytes[1])<<8 | - uint32(expectedBytes[2]) - - line, _, err = l.in.ReadLine() - if err != nil && err != os.EOF { - return - } - if !bytes.HasPrefix(line, armorEnd) { - return 0, ArmorCorrupt - } - - l.eof = true - return 0, os.EOF - } - - if len(line) > 64 { - return 0, ArmorCorrupt - } - - n = copy(p, line) - bytesToSave := len(line) - n - if bytesToSave > 0 { - if cap(l.buf) < bytesToSave { - l.buf = make([]byte, 0, bytesToSave) - } - l.buf = l.buf[0:bytesToSave] - copy(l.buf, line[n:]) - } - - return -} - -// openpgpReader passes Read calls to the underlying base64 decoder, but keeps -// a running CRC of the resulting data and checks the CRC against the value -// found by the lineReader at EOF. -type openpgpReader struct { - lReader *lineReader - b64Reader io.Reader - currentCRC uint32 -} - -func (r *openpgpReader) Read(p []byte) (n int, err os.Error) { - n, err = r.b64Reader.Read(p) - r.currentCRC = crc24(r.currentCRC, p[:n]) - - if err == os.EOF { - if r.lReader.crc != uint32(r.currentCRC&crc24Mask) { - return 0, ArmorCorrupt - } - } - - return -} - -// Decode reads a PGP armored block from the given Reader. It will ignore -// leading garbage. If it doesn't find a block, it will return nil, os.EOF. The -// given Reader is not usable after calling this function: an arbitrary amount -// of data may have been read past the end of the block. -func Decode(in io.Reader) (p *Block, err os.Error) { - r, _ := bufio.NewReaderSize(in, 100) - var line []byte - ignoreNext := false - -TryNextBlock: - p = nil - - // Skip leading garbage - for { - ignoreThis := ignoreNext - line, ignoreNext, err = r.ReadLine() - if err != nil { - return - } - if ignoreNext || ignoreThis { - continue - } - line = bytes.TrimSpace(line) - if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) { - break - } - } - - p = new(Block) - p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)]) - p.Header = make(map[string]string) - nextIsContinuation := false - var lastKey string - - // Read headers - for { - isContinuation := nextIsContinuation - line, nextIsContinuation, err = r.ReadLine() - if err != nil { - p = nil - return - } - if isContinuation { - p.Header[lastKey] += string(line) - continue - } - line = bytes.TrimSpace(line) - if len(line) == 0 { - break - } - - i := bytes.Index(line, []byte(": ")) - if i == -1 { - goto TryNextBlock - } - lastKey = string(line[:i]) - p.Header[lastKey] = string(line[i+2:]) - } - - p.lReader.in = r - p.oReader.currentCRC = crc24Init - p.oReader.lReader = &p.lReader - p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader) - p.Body = &p.oReader - - return -} diff --git a/src/pkg/crypto/openpgp/armor/armor_test.go b/src/pkg/crypto/openpgp/armor/armor_test.go deleted file mode 100644 index 9334e94e9..000000000 --- a/src/pkg/crypto/openpgp/armor/armor_test.go +++ /dev/null @@ -1,95 +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. - -package armor - -import ( - "bytes" - "hash/adler32" - "io/ioutil" - "testing" -) - -func TestDecodeEncode(t *testing.T) { - buf := bytes.NewBuffer([]byte(armorExample1)) - result, err := Decode(buf) - if err != nil { - t.Error(err) - } - expectedType := "PGP SIGNATURE" - if result.Type != expectedType { - t.Errorf("result.Type: got:%s want:%s", result.Type, expectedType) - } - if len(result.Header) != 1 { - t.Errorf("len(result.Header): got:%d want:1", len(result.Header)) - } - v, ok := result.Header["Version"] - if !ok || v != "GnuPG v1.4.10 (GNU/Linux)" { - t.Errorf("result.Header: got:%#v", result.Header) - } - - contents, err := ioutil.ReadAll(result.Body) - if err != nil { - t.Error(err) - } - - if adler32.Checksum(contents) != 0x27b144be { - t.Errorf("contents: got: %x", contents) - } - - buf = bytes.NewBuffer(nil) - w, err := Encode(buf, result.Type, result.Header) - if err != nil { - t.Error(err) - } - _, err = w.Write(contents) - if err != nil { - t.Error(err) - } - w.Close() - - if !bytes.Equal(buf.Bytes(), []byte(armorExample1)) { - t.Errorf("got: %s\nwant: %s", string(buf.Bytes()), armorExample1) - } -} - -func TestLongHeader(t *testing.T) { - buf := bytes.NewBuffer([]byte(armorLongLine)) - result, err := Decode(buf) - if err != nil { - t.Error(err) - return - } - value, ok := result.Header["Version"] - if !ok { - t.Errorf("missing Version header") - } - if value != longValueExpected { - t.Errorf("got: %s want: %s", value, longValueExpected) - } -} - -const armorExample1 = `-----BEGIN PGP SIGNATURE----- -Version: GnuPG v1.4.10 (GNU/Linux) - -iJwEAAECAAYFAk1Fv/0ACgkQo01+GMIMMbsYTwQAiAw+QAaNfY6WBdplZ/uMAccm -4g+81QPmTSGHnetSb6WBiY13kVzK4HQiZH8JSkmmroMLuGeJwsRTEL4wbjRyUKEt -p1xwUZDECs234F1xiG5enc5SGlRtP7foLBz9lOsjx+LEcA4sTl5/2eZR9zyFZqWW -TxRjs+fJCIFuo71xb1g= -=/teI ------END PGP SIGNATURE-----` - -const armorLongLine = `-----BEGIN PGP SIGNATURE----- -Version: 0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz - -iQEcBAABAgAGBQJMtFESAAoJEKsQXJGvOPsVj40H/1WW6jaMXv4BW+1ueDSMDwM8 -kx1fLOXbVM5/Kn5LStZNt1jWWnpxdz7eq3uiqeCQjmqUoRde3YbB2EMnnwRbAhpp -cacnAvy9ZQ78OTxUdNW1mhX5bS6q1MTEJnl+DcyigD70HG/yNNQD7sOPMdYQw0TA -byQBwmLwmTsuZsrYqB68QyLHI+DUugn+kX6Hd2WDB62DKa2suoIUIHQQCd/ofwB3 -WfCYInXQKKOSxu2YOg2Eb4kLNhSMc1i9uKUWAH+sdgJh7NBgdoE4MaNtBFkHXRvv -okWuf3+xA9ksp1npSY/mDvgHijmjvtpRDe6iUeqfCn8N9u9CBg8geANgaG8+QA4= -=wfQG ------END PGP SIGNATURE-----` - -const longValueExpected = "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz" diff --git a/src/pkg/crypto/openpgp/armor/encode.go b/src/pkg/crypto/openpgp/armor/encode.go deleted file mode 100644 index 99dee375e..000000000 --- a/src/pkg/crypto/openpgp/armor/encode.go +++ /dev/null @@ -1,161 +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. - -package armor - -import ( - "encoding/base64" - "io" - "os" -) - -var armorHeaderSep = []byte(": ") -var blockEnd = []byte("\n=") -var newline = []byte("\n") -var armorEndOfLineOut = []byte("-----\n") - -// writeSlices writes its arguments to the given Writer. -func writeSlices(out io.Writer, slices ...[]byte) (err os.Error) { - for _, s := range slices { - _, err = out.Write(s) - if err != nil { - return err - } - } - return -} - -// lineBreaker breaks data across several lines, all of the same byte length -// (except possibly the last). Lines are broken with a single '\n'. -type lineBreaker struct { - lineLength int - line []byte - used int - out io.Writer - haveWritten bool -} - -func newLineBreaker(out io.Writer, lineLength int) *lineBreaker { - return &lineBreaker{ - lineLength: lineLength, - line: make([]byte, lineLength), - used: 0, - out: out, - } -} - -func (l *lineBreaker) Write(b []byte) (n int, err os.Error) { - n = len(b) - - if n == 0 { - return - } - - if l.used == 0 && l.haveWritten { - _, err = l.out.Write([]byte{'\n'}) - if err != nil { - return - } - } - - if l.used+len(b) < l.lineLength { - l.used += copy(l.line[l.used:], b) - return - } - - l.haveWritten = true - _, err = l.out.Write(l.line[0:l.used]) - if err != nil { - return - } - excess := l.lineLength - l.used - l.used = 0 - - _, err = l.out.Write(b[0:excess]) - if err != nil { - return - } - - _, err = l.Write(b[excess:]) - return -} - -func (l *lineBreaker) Close() (err os.Error) { - if l.used > 0 { - _, err = l.out.Write(l.line[0:l.used]) - if err != nil { - return - } - } - - return -} - -// encoding keeps track of a running CRC24 over the data which has been written -// to it and outputs a OpenPGP checksum when closed, followed by an armor -// trailer. -// -// It's built into a stack of io.Writers: -// encoding -> base64 encoder -> lineBreaker -> out -type encoding struct { - out io.Writer - breaker *lineBreaker - b64 io.WriteCloser - crc uint32 - blockType []byte -} - -func (e *encoding) Write(data []byte) (n int, err os.Error) { - e.crc = crc24(e.crc, data) - return e.b64.Write(data) -} - -func (e *encoding) Close() (err os.Error) { - err = e.b64.Close() - if err != nil { - return - } - e.breaker.Close() - - var checksumBytes [3]byte - checksumBytes[0] = byte(e.crc >> 16) - checksumBytes[1] = byte(e.crc >> 8) - checksumBytes[2] = byte(e.crc) - - var b64ChecksumBytes [4]byte - base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:]) - - return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine) -} - -// Encode returns a WriteCloser which will encode the data written to it in -// OpenPGP armor. -func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err os.Error) { - bType := []byte(blockType) - err = writeSlices(out, armorStart, bType, armorEndOfLineOut) - if err != nil { - return - } - - for k, v := range headers { - err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline) - if err != nil { - return - } - } - - _, err = out.Write(newline) - if err != nil { - return - } - - e := &encoding{ - out: out, - breaker: newLineBreaker(out, 64), - crc: crc24Init, - blockType: bType, - } - e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker) - return e, nil -} diff --git a/src/pkg/crypto/openpgp/canonical_text.go b/src/pkg/crypto/openpgp/canonical_text.go deleted file mode 100644 index 293eff354..000000000 --- a/src/pkg/crypto/openpgp/canonical_text.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 openpgp - -import ( - "hash" - "os" -) - -// NewCanonicalTextHash reformats text written to it into the canonical -// form and then applies the hash h. See RFC 4880, section 5.2.1. -func NewCanonicalTextHash(h hash.Hash) hash.Hash { - return &canonicalTextHash{h, 0} -} - -type canonicalTextHash struct { - h hash.Hash - s int -} - -var newline = []byte{'\r', '\n'} - -func (cth *canonicalTextHash) Write(buf []byte) (int, os.Error) { - start := 0 - - for i, c := range buf { - switch cth.s { - case 0: - if c == '\r' { - cth.s = 1 - } else if c == '\n' { - cth.h.Write(buf[start:i]) - cth.h.Write(newline) - start = i + 1 - } - case 1: - cth.s = 0 - } - } - - cth.h.Write(buf[start:]) - return len(buf), nil -} - -func (cth *canonicalTextHash) Sum() []byte { - return cth.h.Sum() -} - -func (cth *canonicalTextHash) Reset() { - cth.h.Reset() - cth.s = 0 -} - -func (cth *canonicalTextHash) Size() int { - return cth.h.Size() -} diff --git a/src/pkg/crypto/openpgp/canonical_text_test.go b/src/pkg/crypto/openpgp/canonical_text_test.go deleted file mode 100644 index ccf2910cc..000000000 --- a/src/pkg/crypto/openpgp/canonical_text_test.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 openpgp - -import ( - "bytes" - "os" - "testing" -) - -type recordingHash struct { - buf *bytes.Buffer -} - -func (r recordingHash) Write(b []byte) (n int, err os.Error) { - return r.buf.Write(b) -} - -func (r recordingHash) Sum() []byte { - return r.buf.Bytes() -} - -func (r recordingHash) Reset() { - panic("shouldn't be called") -} - -func (r recordingHash) Size() int { - panic("shouldn't be called") -} - -func testCanonicalText(t *testing.T, input, expected string) { - r := recordingHash{bytes.NewBuffer(nil)} - c := NewCanonicalTextHash(r) - c.Write([]byte(input)) - result := c.Sum() - if expected != string(result) { - t.Errorf("input: %x got: %x want: %x", input, result, expected) - } -} - -func TestCanonicalText(t *testing.T) { - testCanonicalText(t, "foo\n", "foo\r\n") - testCanonicalText(t, "foo", "foo") - testCanonicalText(t, "foo\r\n", "foo\r\n") - testCanonicalText(t, "foo\r\nbar", "foo\r\nbar") - testCanonicalText(t, "foo\r\nbar\n\n", "foo\r\nbar\r\n\r\n") -} diff --git a/src/pkg/crypto/openpgp/elgamal/Makefile b/src/pkg/crypto/openpgp/elgamal/Makefile deleted file mode 100644 index f730255f8..000000000 --- a/src/pkg/crypto/openpgp/elgamal/Makefile +++ /dev/null @@ -1,11 +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. - -include ../../../../Make.inc - -TARG=crypto/openpgp/elgamal -GOFILES=\ - elgamal.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/elgamal/elgamal.go b/src/pkg/crypto/openpgp/elgamal/elgamal.go deleted file mode 100644 index 99a6e3e1f..000000000 --- a/src/pkg/crypto/openpgp/elgamal/elgamal.go +++ /dev/null @@ -1,122 +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 elgamal implements ElGamal encryption, suitable for OpenPGP, -// as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on -// Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31, -// n. 4, 1985, pp. 469-472. -// -// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it -// unsuitable for other protocols. RSA should be used in preference in any -// case. -package elgamal - -import ( - "big" - "crypto/rand" - "crypto/subtle" - "io" - "os" -) - -// PublicKey represents an ElGamal public key. -type PublicKey struct { - G, P, Y *big.Int -} - -// PrivateKey represents an ElGamal private key. -type PrivateKey struct { - PublicKey - X *big.Int -} - -// Encrypt encrypts the given message to the given public key. The result is a -// pair of integers. Errors can result from reading random, or because msg is -// too large to be encrypted to the public key. -func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err os.Error) { - pLen := (pub.P.BitLen() + 7) / 8 - if len(msg) > pLen-11 { - err = os.NewError("elgamal: message too long") - return - } - - // EM = 0x02 || PS || 0x00 || M - em := make([]byte, pLen-1) - em[0] = 2 - ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):] - err = nonZeroRandomBytes(ps, random) - if err != nil { - return - } - em[len(em)-len(msg)-1] = 0 - copy(mm, msg) - - m := new(big.Int).SetBytes(em) - - k, err := rand.Int(random, pub.P) - if err != nil { - return - } - - c1 = new(big.Int).Exp(pub.G, k, pub.P) - s := new(big.Int).Exp(pub.Y, k, pub.P) - c2 = s.Mul(s, m) - c2.Mod(c2, pub.P) - - return -} - -// Decrypt takes two integers, resulting from an ElGamal encryption, and -// returns the plaintext of the message. An error can result only if the -// ciphertext is invalid. Users should keep in mind that this is a padding -// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can -// be used to break the cryptosystem. See ``Chosen Ciphertext Attacks -// Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel -// Bleichenbacher, Advances in Cryptology (Crypto '98), -func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err os.Error) { - s := new(big.Int).Exp(c1, priv.X, priv.P) - s.ModInverse(s, priv.P) - s.Mul(s, c2) - s.Mod(s, priv.P) - em := s.Bytes() - - firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2) - - // The remainder of the plaintext must be a string of non-zero random - // octets, followed by a 0, followed by the message. - // lookingForIndex: 1 iff we are still looking for the zero. - // index: the offset of the first zero byte. - var lookingForIndex, index int - lookingForIndex = 1 - - for i := 1; i < len(em); i++ { - equals0 := subtle.ConstantTimeByteEq(em[i], 0) - index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index) - lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex) - } - - if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 { - return nil, os.NewError("elgamal: decryption error") - } - return em[index+1:], nil -} - -// nonZeroRandomBytes fills the given slice with non-zero random octets. -func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) { - _, err = io.ReadFull(rand, s) - if err != nil { - return - } - - for i := 0; i < len(s); i++ { - for s[i] == 0 { - _, err = io.ReadFull(rand, s[i:i+1]) - if err != nil { - return - } - } - } - - return -} diff --git a/src/pkg/crypto/openpgp/elgamal/elgamal_test.go b/src/pkg/crypto/openpgp/elgamal/elgamal_test.go deleted file mode 100644 index 101121aa6..000000000 --- a/src/pkg/crypto/openpgp/elgamal/elgamal_test.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 elgamal - -import ( - "big" - "bytes" - "crypto/rand" - "testing" -) - -// This is the 1024-bit MODP group from RFC 5114, section 2.1: -const primeHex = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371" - -const generatorHex = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5" - -func fromHex(hex string) *big.Int { - n, ok := new(big.Int).SetString(hex, 16) - if !ok { - panic("failed to parse hex number") - } - return n -} - -func TestEncryptDecrypt(t *testing.T) { - priv := &PrivateKey{ - PublicKey: PublicKey{ - G: fromHex(generatorHex), - P: fromHex(primeHex), - }, - X: fromHex("42"), - } - priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P) - - message := []byte("hello world") - c1, c2, err := Encrypt(rand.Reader, &priv.PublicKey, message) - if err != nil { - t.Errorf("error encrypting: %s", err) - } - message2, err := Decrypt(priv, c1, c2) - if err != nil { - t.Errorf("error decrypting: %s", err) - } - if !bytes.Equal(message2, message) { - t.Errorf("decryption failed, got: %x, want: %x", message2, message) - } -} diff --git a/src/pkg/crypto/openpgp/error/Makefile b/src/pkg/crypto/openpgp/error/Makefile deleted file mode 100644 index 8c370a089..000000000 --- a/src/pkg/crypto/openpgp/error/Makefile +++ /dev/null @@ -1,11 +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. - -include ../../../../Make.inc - -TARG=crypto/openpgp/error -GOFILES=\ - error.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/error/error.go b/src/pkg/crypto/openpgp/error/error.go deleted file mode 100644 index 3759ce161..000000000 --- a/src/pkg/crypto/openpgp/error/error.go +++ /dev/null @@ -1,64 +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. - -// Package error contains common error types for the OpenPGP packages. -package error - -import ( - "strconv" -) - -// A StructuralError is returned when OpenPGP data is found to be syntactically -// invalid. -type StructuralError string - -func (s StructuralError) String() string { - return "OpenPGP data invalid: " + string(s) -} - -// UnsupportedError indicates that, although the OpenPGP data is valid, it -// makes use of currently unimplemented features. -type UnsupportedError string - -func (s UnsupportedError) String() string { - return "OpenPGP feature unsupported: " + string(s) -} - -// InvalidArgumentError indicates that the caller is in error and passed an -// incorrect value. -type InvalidArgumentError string - -func (i InvalidArgumentError) String() string { - return "OpenPGP argument invalid: " + string(i) -} - -// SignatureError indicates that a syntactically valid signature failed to -// validate. -type SignatureError string - -func (b SignatureError) String() string { - return "OpenPGP signature invalid: " + string(b) -} - -type keyIncorrect int - -func (ki keyIncorrect) String() string { - return "the given key was incorrect" -} - -var KeyIncorrectError = keyIncorrect(0) - -type unknownIssuer int - -func (unknownIssuer) String() string { - return "signature make by unknown entity" -} - -var UnknownIssuerError = unknownIssuer(0) - -type UnknownPacketTypeError uint8 - -func (upte UnknownPacketTypeError) String() string { - return "unknown OpenPGP packet type: " + strconv.Itoa(int(upte)) -} diff --git a/src/pkg/crypto/openpgp/keys.go b/src/pkg/crypto/openpgp/keys.go deleted file mode 100644 index c70fb7927..000000000 --- a/src/pkg/crypto/openpgp/keys.go +++ /dev/null @@ -1,545 +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 openpgp - -import ( - "crypto" - "crypto/openpgp/armor" - "crypto/openpgp/error" - "crypto/openpgp/packet" - "crypto/rsa" - "io" - "os" - "time" -) - -// PublicKeyType is the armor type for a PGP public key. -var PublicKeyType = "PGP PUBLIC KEY BLOCK" -// PrivateKeyType is the armor type for a PGP private key. -var PrivateKeyType = "PGP PRIVATE KEY BLOCK" - -// An Entity represents the components of an OpenPGP key: a primary public key -// (which must be a signing key), one or more identities claimed by that key, -// and zero or more subkeys, which may be encryption keys. -type Entity struct { - PrimaryKey *packet.PublicKey - PrivateKey *packet.PrivateKey - Identities map[string]*Identity // indexed by Identity.Name - Subkeys []Subkey -} - -// An Identity represents an identity claimed by an Entity and zero or more -// assertions by other entities about that claim. -type Identity struct { - Name string // by convention, has the form "Full Name (comment) " - UserId *packet.UserId - SelfSignature *packet.Signature - Signatures []*packet.Signature -} - -// A Subkey is an additional public key in an Entity. Subkeys can be used for -// encryption. -type Subkey struct { - PublicKey *packet.PublicKey - PrivateKey *packet.PrivateKey - Sig *packet.Signature -} - -// A Key identifies a specific public key in an Entity. This is either the -// Entity's primary key or a subkey. -type Key struct { - Entity *Entity - PublicKey *packet.PublicKey - PrivateKey *packet.PrivateKey - SelfSignature *packet.Signature -} - -// A KeyRing provides access to public and private keys. -type KeyRing interface { - // KeysById returns the set of keys that have the given key id. - KeysById(id uint64) []Key - // DecryptionKeys returns all private keys that are valid for - // decryption. - DecryptionKeys() []Key -} - -// primaryIdentity returns the Identity marked as primary or the first identity -// if none are so marked. -func (e *Entity) primaryIdentity() *Identity { - var firstIdentity *Identity - for _, ident := range e.Identities { - if firstIdentity == nil { - firstIdentity = ident - } - if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { - return ident - } - } - return firstIdentity -} - -// encryptionKey returns the best candidate Key for encrypting a message to the -// given Entity. -func (e *Entity) encryptionKey() Key { - candidateSubkey := -1 - - for i, subkey := range e.Subkeys { - if subkey.Sig.FlagsValid && subkey.Sig.FlagEncryptCommunications && subkey.PublicKey.PubKeyAlgo.CanEncrypt() { - candidateSubkey = i - break - } - } - - i := e.primaryIdentity() - - if e.PrimaryKey.PubKeyAlgo.CanEncrypt() { - // If we don't have any candidate subkeys for encryption and - // the primary key doesn't have any usage metadata then we - // assume that the primary key is ok. Or, if the primary key is - // marked as ok to encrypt to, then we can obviously use it. - if candidateSubkey == -1 && !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && i.SelfSignature.FlagsValid { - return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature} - } - } - - if candidateSubkey != -1 { - subkey := e.Subkeys[candidateSubkey] - return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig} - } - - // This Entity appears to be signing only. - return Key{} -} - -// signingKey return the best candidate Key for signing a message with this -// Entity. -func (e *Entity) signingKey() Key { - candidateSubkey := -1 - - for i, subkey := range e.Subkeys { - if subkey.Sig.FlagsValid && subkey.Sig.FlagSign && subkey.PublicKey.PubKeyAlgo.CanSign() { - candidateSubkey = i - break - } - } - - i := e.primaryIdentity() - - // If we have no candidate subkey then we assume that it's ok to sign - // with the primary key. - if candidateSubkey == -1 || i.SelfSignature.FlagsValid && i.SelfSignature.FlagSign { - return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature} - } - - subkey := e.Subkeys[candidateSubkey] - return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig} -} - -// An EntityList contains one or more Entities. -type EntityList []*Entity - -// KeysById returns the set of keys that have the given key id. -func (el EntityList) KeysById(id uint64) (keys []Key) { - for _, e := range el { - if e.PrimaryKey.KeyId == id { - var selfSig *packet.Signature - for _, ident := range e.Identities { - if selfSig == nil { - selfSig = ident.SelfSignature - } else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { - selfSig = ident.SelfSignature - break - } - } - keys = append(keys, Key{e, e.PrimaryKey, e.PrivateKey, selfSig}) - } - - for _, subKey := range e.Subkeys { - if subKey.PublicKey.KeyId == id { - keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) - } - } - } - return -} - -// DecryptionKeys returns all private keys that are valid for decryption. -func (el EntityList) DecryptionKeys() (keys []Key) { - for _, e := range el { - for _, subKey := range e.Subkeys { - if subKey.PrivateKey != nil && (!subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications) { - keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) - } - } - } - return -} - -// ReadArmoredKeyRing reads one or more public/private keys from an armor keyring file. -func ReadArmoredKeyRing(r io.Reader) (EntityList, os.Error) { - block, err := armor.Decode(r) - if err == os.EOF { - return nil, error.InvalidArgumentError("no armored data found") - } - if err != nil { - return nil, err - } - if block.Type != PublicKeyType && block.Type != PrivateKeyType { - return nil, error.InvalidArgumentError("expected public or private key block, got: " + block.Type) - } - - return ReadKeyRing(block.Body) -} - -// ReadKeyRing reads one or more public/private keys. Unsupported keys are -// ignored as long as at least a single valid key is found. -func ReadKeyRing(r io.Reader) (el EntityList, err os.Error) { - packets := packet.NewReader(r) - var lastUnsupportedError os.Error - - for { - var e *Entity - e, err = readEntity(packets) - if err != nil { - if _, ok := err.(error.UnsupportedError); ok { - lastUnsupportedError = err - err = readToNextPublicKey(packets) - } - if err == os.EOF { - err = nil - break - } - if err != nil { - el = nil - break - } - } else { - el = append(el, e) - } - } - - if len(el) == 0 && err == nil { - err = lastUnsupportedError - } - return -} - -// readToNextPublicKey reads packets until the start of the entity and leaves -// the first packet of the new entity in the Reader. -func readToNextPublicKey(packets *packet.Reader) (err os.Error) { - var p packet.Packet - for { - p, err = packets.Next() - if err == os.EOF { - return - } else if err != nil { - if _, ok := err.(error.UnsupportedError); ok { - err = nil - continue - } - return - } - - if pk, ok := p.(*packet.PublicKey); ok && !pk.IsSubkey { - packets.Unread(p) - return - } - } - - panic("unreachable") -} - -// readEntity reads an entity (public key, identities, subkeys etc) from the -// given Reader. -func readEntity(packets *packet.Reader) (*Entity, os.Error) { - e := new(Entity) - e.Identities = make(map[string]*Identity) - - p, err := packets.Next() - if err != nil { - return nil, err - } - - var ok bool - if e.PrimaryKey, ok = p.(*packet.PublicKey); !ok { - if e.PrivateKey, ok = p.(*packet.PrivateKey); !ok { - packets.Unread(p) - return nil, error.StructuralError("first packet was not a public/private key") - } else { - e.PrimaryKey = &e.PrivateKey.PublicKey - } - } - - if !e.PrimaryKey.PubKeyAlgo.CanSign() { - return nil, error.StructuralError("primary key cannot be used for signatures") - } - - var current *Identity -EachPacket: - for { - p, err := packets.Next() - if err == os.EOF { - break - } else if err != nil { - return nil, err - } - - switch pkt := p.(type) { - case *packet.UserId: - current = new(Identity) - current.Name = pkt.Id - current.UserId = pkt - e.Identities[pkt.Id] = current - - for { - p, err = packets.Next() - if err == os.EOF { - return nil, io.ErrUnexpectedEOF - } else if err != nil { - return nil, err - } - - sig, ok := p.(*packet.Signature) - if !ok { - return nil, error.StructuralError("user ID packet not followed by self-signature") - } - - if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId { - if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, sig); err != nil { - return nil, error.StructuralError("user ID self-signature invalid: " + err.String()) - } - current.SelfSignature = sig - break - } - current.Signatures = append(current.Signatures, sig) - } - case *packet.Signature: - if current == nil { - return nil, error.StructuralError("signature packet found before user id packet") - } - current.Signatures = append(current.Signatures, pkt) - case *packet.PrivateKey: - if pkt.IsSubkey == false { - packets.Unread(p) - break EachPacket - } - err = addSubkey(e, packets, &pkt.PublicKey, pkt) - if err != nil { - return nil, err - } - case *packet.PublicKey: - if pkt.IsSubkey == false { - packets.Unread(p) - break EachPacket - } - err = addSubkey(e, packets, pkt, nil) - if err != nil { - return nil, err - } - default: - // we ignore unknown packets - } - } - - if len(e.Identities) == 0 { - return nil, error.StructuralError("entity without any identities") - } - - return e, nil -} - -func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) os.Error { - var subKey Subkey - subKey.PublicKey = pub - subKey.PrivateKey = priv - p, err := packets.Next() - if err == os.EOF { - return io.ErrUnexpectedEOF - } - if err != nil { - return error.StructuralError("subkey signature invalid: " + err.String()) - } - var ok bool - subKey.Sig, ok = p.(*packet.Signature) - if !ok { - return error.StructuralError("subkey packet not followed by signature") - } - if subKey.Sig.SigType != packet.SigTypeSubkeyBinding { - return error.StructuralError("subkey signature with wrong type") - } - err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig) - if err != nil { - return error.StructuralError("subkey signature invalid: " + err.String()) - } - e.Subkeys = append(e.Subkeys, subKey) - return nil -} - -const defaultRSAKeyBits = 2048 - -// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a -// single identity composed of the given full name, comment and email, any of -// which may be empty but must not contain any of "()<>\x00". -func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email string) (*Entity, os.Error) { - uid := packet.NewUserId(name, comment, email) - if uid == nil { - return nil, error.InvalidArgumentError("user id field contained invalid characters") - } - signingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits) - if err != nil { - return nil, err - } - encryptingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits) - if err != nil { - return nil, err - } - - t := uint32(currentTimeSecs) - - e := &Entity{ - PrimaryKey: packet.NewRSAPublicKey(t, &signingPriv.PublicKey, false /* not a subkey */ ), - PrivateKey: packet.NewRSAPrivateKey(t, signingPriv, false /* not a subkey */ ), - Identities: make(map[string]*Identity), - } - isPrimaryId := true - e.Identities[uid.Id] = &Identity{ - Name: uid.Name, - UserId: uid, - SelfSignature: &packet.Signature{ - CreationTime: t, - SigType: packet.SigTypePositiveCert, - PubKeyAlgo: packet.PubKeyAlgoRSA, - Hash: crypto.SHA256, - IsPrimaryId: &isPrimaryId, - FlagsValid: true, - FlagSign: true, - FlagCertify: true, - IssuerKeyId: &e.PrimaryKey.KeyId, - }, - } - - e.Subkeys = make([]Subkey, 1) - e.Subkeys[0] = Subkey{ - PublicKey: packet.NewRSAPublicKey(t, &encryptingPriv.PublicKey, true /* is a subkey */ ), - PrivateKey: packet.NewRSAPrivateKey(t, encryptingPriv, true /* is a subkey */ ), - Sig: &packet.Signature{ - CreationTime: t, - SigType: packet.SigTypeSubkeyBinding, - PubKeyAlgo: packet.PubKeyAlgoRSA, - Hash: crypto.SHA256, - FlagsValid: true, - FlagEncryptStorage: true, - FlagEncryptCommunications: true, - IssuerKeyId: &e.PrimaryKey.KeyId, - }, - } - - return e, nil -} - -// SerializePrivate serializes an Entity, including private key material, to -// the given Writer. For now, it must only be used on an Entity returned from -// NewEntity. -func (e *Entity) SerializePrivate(w io.Writer) (err os.Error) { - err = e.PrivateKey.Serialize(w) - if err != nil { - return - } - for _, ident := range e.Identities { - err = ident.UserId.Serialize(w) - if err != nil { - return - } - err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey) - if err != nil { - return - } - err = ident.SelfSignature.Serialize(w) - if err != nil { - return - } - } - for _, subkey := range e.Subkeys { - err = subkey.PrivateKey.Serialize(w) - if err != nil { - return - } - err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey) - if err != nil { - return - } - err = subkey.Sig.Serialize(w) - if err != nil { - return - } - } - return nil -} - -// Serialize writes the public part of the given Entity to w. (No private -// key material will be output). -func (e *Entity) Serialize(w io.Writer) os.Error { - err := e.PrimaryKey.Serialize(w) - if err != nil { - return err - } - for _, ident := range e.Identities { - err = ident.UserId.Serialize(w) - if err != nil { - return err - } - err = ident.SelfSignature.Serialize(w) - if err != nil { - return err - } - for _, sig := range ident.Signatures { - err = sig.Serialize(w) - if err != nil { - return err - } - } - } - for _, subkey := range e.Subkeys { - err = subkey.PublicKey.Serialize(w) - if err != nil { - return err - } - err = subkey.Sig.Serialize(w) - if err != nil { - return err - } - } - return nil -} - -// SignIdentity adds a signature to e, from signer, attesting that identity is -// associated with e. The provided identity must already be an element of -// e.Identities and the private key of signer must have been decrypted if -// necessary. -func (e *Entity) SignIdentity(identity string, signer *Entity) os.Error { - if signer.PrivateKey == nil { - return error.InvalidArgumentError("signing Entity must have a private key") - } - if signer.PrivateKey.Encrypted { - return error.InvalidArgumentError("signing Entity's private key must be decrypted") - } - ident, ok := e.Identities[identity] - if !ok { - return error.InvalidArgumentError("given identity string not found in Entity") - } - - sig := &packet.Signature{ - SigType: packet.SigTypeGenericCert, - PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, - Hash: crypto.SHA256, - CreationTime: uint32(time.Seconds()), - IssuerKeyId: &signer.PrivateKey.KeyId, - } - if err := sig.SignKey(e.PrimaryKey, signer.PrivateKey); err != nil { - return err - } - ident.Signatures = append(ident.Signatures, sig) - return nil -} diff --git a/src/pkg/crypto/openpgp/packet/Makefile b/src/pkg/crypto/openpgp/packet/Makefile deleted file mode 100644 index 0f0d94eb1..000000000 --- a/src/pkg/crypto/openpgp/packet/Makefile +++ /dev/null @@ -1,22 +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. - -include ../../../../Make.inc - -TARG=crypto/openpgp/packet -GOFILES=\ - compressed.go\ - encrypted_key.go\ - literal.go\ - one_pass_signature.go\ - packet.go\ - private_key.go\ - public_key.go\ - reader.go\ - signature.go\ - symmetrically_encrypted.go\ - symmetric_key_encrypted.go\ - userid.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/packet/compressed.go b/src/pkg/crypto/openpgp/packet/compressed.go deleted file mode 100644 index 1c15c24c4..000000000 --- a/src/pkg/crypto/openpgp/packet/compressed.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 packet - -import ( - "compress/flate" - "compress/zlib" - "crypto/openpgp/error" - "io" - "os" - "strconv" -) - -// Compressed represents a compressed OpenPGP packet. The decompressed contents -// will contain more OpenPGP packets. See RFC 4880, section 5.6. -type Compressed struct { - Body io.Reader -} - -func (c *Compressed) parse(r io.Reader) os.Error { - var buf [1]byte - _, err := readFull(r, buf[:]) - if err != nil { - return err - } - - switch buf[0] { - case 1: - c.Body = flate.NewReader(r) - case 2: - c.Body, err = zlib.NewReader(r) - default: - err = error.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0]))) - } - - return err -} diff --git a/src/pkg/crypto/openpgp/packet/compressed_test.go b/src/pkg/crypto/openpgp/packet/compressed_test.go deleted file mode 100644 index 24fe501ed..000000000 --- a/src/pkg/crypto/openpgp/packet/compressed_test.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 packet - -import ( - "bytes" - "encoding/hex" - "os" - "io/ioutil" - "testing" -) - -func TestCompressed(t *testing.T) { - packet, err := Read(readerFromHex(compressedHex)) - if err != nil { - t.Errorf("failed to read Compressed: %s", err) - return - } - - c, ok := packet.(*Compressed) - if !ok { - t.Error("didn't find Compressed packet") - return - } - - contents, err := ioutil.ReadAll(c.Body) - if err != nil && err != os.EOF { - t.Error(err) - return - } - - expected, _ := hex.DecodeString(compressedExpectedHex) - if !bytes.Equal(expected, contents) { - t.Errorf("got:%x want:%x", contents, expected) - } -} - -const compressedHex = "a3013b2d90c4e02b72e25f727e5e496a5e49b11e1700" -const compressedExpectedHex = "cb1062004d14c8fe636f6e74656e74732e0a" diff --git a/src/pkg/crypto/openpgp/packet/encrypted_key.go b/src/pkg/crypto/openpgp/packet/encrypted_key.go deleted file mode 100644 index b4730cbc9..000000000 --- a/src/pkg/crypto/openpgp/packet/encrypted_key.go +++ /dev/null @@ -1,168 +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 packet - -import ( - "big" - "crypto/openpgp/elgamal" - "crypto/openpgp/error" - "crypto/rand" - "crypto/rsa" - "encoding/binary" - "io" - "os" - "strconv" -) - -const encryptedKeyVersion = 3 - -// EncryptedKey represents a public-key encrypted session key. See RFC 4880, -// section 5.1. -type EncryptedKey struct { - KeyId uint64 - Algo PublicKeyAlgorithm - CipherFunc CipherFunction // only valid after a successful Decrypt - Key []byte // only valid after a successful Decrypt - - encryptedMPI1, encryptedMPI2 []byte -} - -func (e *EncryptedKey) parse(r io.Reader) (err os.Error) { - var buf [10]byte - _, err = readFull(r, buf[:]) - if err != nil { - return - } - if buf[0] != encryptedKeyVersion { - return error.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0]))) - } - e.KeyId = binary.BigEndian.Uint64(buf[1:9]) - e.Algo = PublicKeyAlgorithm(buf[9]) - switch e.Algo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - e.encryptedMPI1, _, err = readMPI(r) - case PubKeyAlgoElGamal: - e.encryptedMPI1, _, err = readMPI(r) - if err != nil { - return - } - e.encryptedMPI2, _, err = readMPI(r) - } - _, err = consumeAll(r) - return -} - -func checksumKeyMaterial(key []byte) uint16 { - var checksum uint16 - for _, v := range key { - checksum += uint16(v) - } - return checksum -} - -// Decrypt decrypts an encrypted session key with the given private key. The -// private key must have been decrypted first. -func (e *EncryptedKey) Decrypt(priv *PrivateKey) os.Error { - var err os.Error - var b []byte - - // TODO(agl): use session key decryption routines here to avoid - // padding oracle attacks. - switch priv.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - b, err = rsa.DecryptPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1) - case PubKeyAlgoElGamal: - c1 := new(big.Int).SetBytes(e.encryptedMPI1) - c2 := new(big.Int).SetBytes(e.encryptedMPI2) - b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2) - default: - err = error.InvalidArgumentError("cannot decrypted encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) - } - - if err != nil { - return err - } - - e.CipherFunc = CipherFunction(b[0]) - e.Key = b[1 : len(b)-2] - expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1]) - checksum := checksumKeyMaterial(e.Key) - if checksum != expectedChecksum { - return error.StructuralError("EncryptedKey checksum incorrect") - } - - return nil -} - -// SerializeEncryptedKey serializes an encrypted key packet to w that contains -// key, encrypted to pub. -func SerializeEncryptedKey(w io.Writer, rand io.Reader, pub *PublicKey, cipherFunc CipherFunction, key []byte) os.Error { - var buf [10]byte - buf[0] = encryptedKeyVersion - binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) - buf[9] = byte(pub.PubKeyAlgo) - - keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */ ) - keyBlock[0] = byte(cipherFunc) - copy(keyBlock[1:], key) - checksum := checksumKeyMaterial(key) - keyBlock[1+len(key)] = byte(checksum >> 8) - keyBlock[1+len(key)+1] = byte(checksum) - - switch pub.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - return serializeEncryptedKeyRSA(w, rand, buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) - case PubKeyAlgoElGamal: - return serializeEncryptedKeyElGamal(w, rand, buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) - case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: - return error.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) - } - - return error.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) -} - -func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) os.Error { - cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock) - if err != nil { - return error.InvalidArgumentError("RSA encryption failed: " + err.String()) - } - - packetLen := 10 /* header length */ + 2 /* mpi size */ + len(cipherText) - - err = serializeHeader(w, packetTypeEncryptedKey, packetLen) - if err != nil { - return err - } - _, err = w.Write(header[:]) - if err != nil { - return err - } - return writeMPI(w, 8*uint16(len(cipherText)), cipherText) -} - -func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) os.Error { - c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock) - if err != nil { - return error.InvalidArgumentError("ElGamal encryption failed: " + err.String()) - } - - packetLen := 10 /* header length */ - packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8 - packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8 - - err = serializeHeader(w, packetTypeEncryptedKey, packetLen) - if err != nil { - return err - } - _, err = w.Write(header[:]) - if err != nil { - return err - } - err = writeBig(w, c1) - if err != nil { - return err - } - return writeBig(w, c2) -} diff --git a/src/pkg/crypto/openpgp/packet/encrypted_key_test.go b/src/pkg/crypto/openpgp/packet/encrypted_key_test.go deleted file mode 100644 index b402245bd..000000000 --- a/src/pkg/crypto/openpgp/packet/encrypted_key_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 packet - -import ( - "big" - "bytes" - "crypto/rand" - "crypto/rsa" - "fmt" - "testing" -) - -func bigFromBase10(s string) *big.Int { - b, ok := new(big.Int).SetString(s, 10) - if !ok { - panic("bigFromBase10 failed") - } - return b -} - -var encryptedKeyPub = rsa.PublicKey{ - E: 65537, - N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"), -} - -var encryptedKeyRSAPriv = &rsa.PrivateKey{ - PublicKey: encryptedKeyPub, - D: bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"), -} - -var encryptedKeyPriv = &PrivateKey{ - PublicKey: PublicKey{ - PubKeyAlgo: PubKeyAlgoRSA, - }, - PrivateKey: encryptedKeyRSAPriv, -} - -func TestDecryptingEncryptedKey(t *testing.T) { - const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8" - const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b" - - p, err := Read(readerFromHex(encryptedKeyHex)) - if err != nil { - t.Errorf("error from Read: %s", err) - return - } - ek, ok := p.(*EncryptedKey) - if !ok { - t.Errorf("didn't parse an EncryptedKey, got %#v", p) - return - } - - if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA { - t.Errorf("unexpected EncryptedKey contents: %#v", ek) - return - } - - err = ek.Decrypt(encryptedKeyPriv) - if err != nil { - t.Errorf("error from Decrypt: %s", err) - return - } - - if ek.CipherFunc != CipherAES256 { - t.Errorf("unexpected EncryptedKey contents: %#v", ek) - return - } - - keyHex := fmt.Sprintf("%x", ek.Key) - if keyHex != expectedKeyHex { - t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) - } -} - -func TestEncryptingEncryptedKey(t *testing.T) { - key := []byte{1, 2, 3, 4} - const expectedKeyHex = "01020304" - const keyId = 42 - - pub := &PublicKey{ - PublicKey: &encryptedKeyPub, - KeyId: keyId, - PubKeyAlgo: PubKeyAlgoRSAEncryptOnly, - } - - buf := new(bytes.Buffer) - err := SerializeEncryptedKey(buf, rand.Reader, pub, CipherAES128, key) - if err != nil { - t.Errorf("error writing encrypted key packet: %s", err) - } - - p, err := Read(buf) - if err != nil { - t.Errorf("error from Read: %s", err) - return - } - ek, ok := p.(*EncryptedKey) - if !ok { - t.Errorf("didn't parse an EncryptedKey, got %#v", p) - return - } - - if ek.KeyId != keyId || ek.Algo != PubKeyAlgoRSAEncryptOnly { - t.Errorf("unexpected EncryptedKey contents: %#v", ek) - return - } - - err = ek.Decrypt(encryptedKeyPriv) - if err != nil { - t.Errorf("error from Decrypt: %s", err) - return - } - - if ek.CipherFunc != CipherAES128 { - t.Errorf("unexpected EncryptedKey contents: %#v", ek) - return - } - - keyHex := fmt.Sprintf("%x", ek.Key) - if keyHex != expectedKeyHex { - t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) - } -} diff --git a/src/pkg/crypto/openpgp/packet/literal.go b/src/pkg/crypto/openpgp/packet/literal.go deleted file mode 100644 index 9411572d7..000000000 --- a/src/pkg/crypto/openpgp/packet/literal.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. - -package packet - -import ( - "encoding/binary" - "io" - "os" -) - -// LiteralData represents an encrypted file. See RFC 4880, section 5.9. -type LiteralData struct { - IsBinary bool - FileName string - Time uint32 // Unix epoch time. Either creation time or modification time. 0 means undefined. - Body io.Reader -} - -// ForEyesOnly returns whether the contents of the LiteralData have been marked -// as especially sensitive. -func (l *LiteralData) ForEyesOnly() bool { - return l.FileName == "_CONSOLE" -} - -func (l *LiteralData) parse(r io.Reader) (err os.Error) { - var buf [256]byte - - _, err = readFull(r, buf[:2]) - if err != nil { - return - } - - l.IsBinary = buf[0] == 'b' - fileNameLen := int(buf[1]) - - _, err = readFull(r, buf[:fileNameLen]) - if err != nil { - return - } - - l.FileName = string(buf[:fileNameLen]) - - _, err = readFull(r, buf[:4]) - if err != nil { - return - } - - l.Time = binary.BigEndian.Uint32(buf[:4]) - l.Body = r - return -} - -// SerializeLiteral serializes a literal data packet to w and returns a -// WriteCloser to which the data itself can be written and which MUST be closed -// on completion. The fileName is truncated to 255 bytes. -func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err os.Error) { - var buf [4]byte - buf[0] = 't' - if isBinary { - buf[0] = 'b' - } - if len(fileName) > 255 { - fileName = fileName[:255] - } - buf[1] = byte(len(fileName)) - - inner, err := serializeStreamHeader(w, packetTypeLiteralData) - if err != nil { - return - } - - _, err = inner.Write(buf[:2]) - if err != nil { - return - } - _, err = inner.Write([]byte(fileName)) - if err != nil { - return - } - binary.BigEndian.PutUint32(buf[:], time) - _, err = inner.Write(buf[:]) - if err != nil { - return - } - - plaintext = inner - return -} diff --git a/src/pkg/crypto/openpgp/packet/one_pass_signature.go b/src/pkg/crypto/openpgp/packet/one_pass_signature.go deleted file mode 100644 index ca826e4f4..000000000 --- a/src/pkg/crypto/openpgp/packet/one_pass_signature.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 packet - -import ( - "crypto" - "crypto/openpgp/error" - "crypto/openpgp/s2k" - "encoding/binary" - "io" - "os" - "strconv" -) - -// OnePassSignature represents a one-pass signature packet. See RFC 4880, -// section 5.4. -type OnePassSignature struct { - SigType SignatureType - Hash crypto.Hash - PubKeyAlgo PublicKeyAlgorithm - KeyId uint64 - IsLast bool -} - -const onePassSignatureVersion = 3 - -func (ops *OnePassSignature) parse(r io.Reader) (err os.Error) { - var buf [13]byte - - _, err = readFull(r, buf[:]) - if err != nil { - return - } - if buf[0] != onePassSignatureVersion { - err = error.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) - } - - var ok bool - ops.Hash, ok = s2k.HashIdToHash(buf[2]) - if !ok { - return error.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2]))) - } - - ops.SigType = SignatureType(buf[1]) - ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3]) - ops.KeyId = binary.BigEndian.Uint64(buf[4:12]) - ops.IsLast = buf[12] != 0 - return -} - -// Serialize marshals the given OnePassSignature to w. -func (ops *OnePassSignature) Serialize(w io.Writer) os.Error { - var buf [13]byte - buf[0] = onePassSignatureVersion - buf[1] = uint8(ops.SigType) - var ok bool - buf[2], ok = s2k.HashToHashId(ops.Hash) - if !ok { - return error.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash))) - } - buf[3] = uint8(ops.PubKeyAlgo) - binary.BigEndian.PutUint64(buf[4:12], ops.KeyId) - if ops.IsLast { - buf[12] = 1 - } - - if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil { - return err - } - _, err := w.Write(buf[:]) - return err -} diff --git a/src/pkg/crypto/openpgp/packet/packet.go b/src/pkg/crypto/openpgp/packet/packet.go deleted file mode 100644 index 1d7297e38..000000000 --- a/src/pkg/crypto/openpgp/packet/packet.go +++ /dev/null @@ -1,483 +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 packet implements parsing and serialization of OpenPGP packets, as -// specified in RFC 4880. -package packet - -import ( - "big" - "crypto/aes" - "crypto/cast5" - "crypto/cipher" - "crypto/openpgp/error" - "io" - "os" -) - -// readFull is the same as io.ReadFull except that reading zero bytes returns -// ErrUnexpectedEOF rather than EOF. -func readFull(r io.Reader, buf []byte) (n int, err os.Error) { - n, err = io.ReadFull(r, buf) - if err == os.EOF { - err = io.ErrUnexpectedEOF - } - return -} - -// readLength reads an OpenPGP length from r. See RFC 4880, section 4.2.2. -func readLength(r io.Reader) (length int64, isPartial bool, err os.Error) { - var buf [4]byte - _, err = readFull(r, buf[:1]) - if err != nil { - return - } - switch { - case buf[0] < 192: - length = int64(buf[0]) - case buf[0] < 224: - length = int64(buf[0]-192) << 8 - _, err = readFull(r, buf[0:1]) - if err != nil { - return - } - length += int64(buf[0]) + 192 - case buf[0] < 255: - length = int64(1) << (buf[0] & 0x1f) - isPartial = true - default: - _, err = readFull(r, buf[0:4]) - if err != nil { - return - } - length = int64(buf[0])<<24 | - int64(buf[1])<<16 | - int64(buf[2])<<8 | - int64(buf[3]) - } - return -} - -// partialLengthReader wraps an io.Reader and handles OpenPGP partial lengths. -// The continuation lengths are parsed and removed from the stream and EOF is -// returned at the end of the packet. See RFC 4880, section 4.2.2.4. -type partialLengthReader struct { - r io.Reader - remaining int64 - isPartial bool -} - -func (r *partialLengthReader) Read(p []byte) (n int, err os.Error) { - for r.remaining == 0 { - if !r.isPartial { - return 0, os.EOF - } - r.remaining, r.isPartial, err = readLength(r.r) - if err != nil { - return 0, err - } - } - - toRead := int64(len(p)) - if toRead > r.remaining { - toRead = r.remaining - } - - n, err = r.r.Read(p[:int(toRead)]) - r.remaining -= int64(n) - if n < int(toRead) && err == os.EOF { - err = io.ErrUnexpectedEOF - } - return -} - -// partialLengthWriter writes a stream of data using OpenPGP partial lengths. -// See RFC 4880, section 4.2.2.4. -type partialLengthWriter struct { - w io.WriteCloser - lengthByte [1]byte -} - -func (w *partialLengthWriter) Write(p []byte) (n int, err os.Error) { - for len(p) > 0 { - for power := uint(14); power < 32; power-- { - l := 1 << power - if len(p) >= l { - w.lengthByte[0] = 224 + uint8(power) - _, err = w.w.Write(w.lengthByte[:]) - if err != nil { - return - } - var m int - m, err = w.w.Write(p[:l]) - n += m - if err != nil { - return - } - p = p[l:] - break - } - } - } - return -} - -func (w *partialLengthWriter) Close() os.Error { - w.lengthByte[0] = 0 - _, err := w.w.Write(w.lengthByte[:]) - if err != nil { - return err - } - return w.w.Close() -} - -// A spanReader is an io.LimitReader, but it returns ErrUnexpectedEOF if the -// underlying Reader returns EOF before the limit has been reached. -type spanReader struct { - r io.Reader - n int64 -} - -func (l *spanReader) Read(p []byte) (n int, err os.Error) { - if l.n <= 0 { - return 0, os.EOF - } - if int64(len(p)) > l.n { - p = p[0:l.n] - } - n, err = l.r.Read(p) - l.n -= int64(n) - if l.n > 0 && err == os.EOF { - err = io.ErrUnexpectedEOF - } - return -} - -// readHeader parses a packet header and returns an io.Reader which will return -// the contents of the packet. See RFC 4880, section 4.2. -func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader, err os.Error) { - var buf [4]byte - _, err = io.ReadFull(r, buf[:1]) - if err != nil { - return - } - if buf[0]&0x80 == 0 { - err = error.StructuralError("tag byte does not have MSB set") - return - } - if buf[0]&0x40 == 0 { - // Old format packet - tag = packetType((buf[0] & 0x3f) >> 2) - lengthType := buf[0] & 3 - if lengthType == 3 { - length = -1 - contents = r - return - } - lengthBytes := 1 << lengthType - _, err = readFull(r, buf[0:lengthBytes]) - if err != nil { - return - } - for i := 0; i < lengthBytes; i++ { - length <<= 8 - length |= int64(buf[i]) - } - contents = &spanReader{r, length} - return - } - - // New format packet - tag = packetType(buf[0] & 0x3f) - length, isPartial, err := readLength(r) - if err != nil { - return - } - if isPartial { - contents = &partialLengthReader{ - remaining: length, - isPartial: true, - r: r, - } - length = -1 - } else { - contents = &spanReader{r, length} - } - return -} - -// serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section -// 4.2. -func serializeHeader(w io.Writer, ptype packetType, length int) (err os.Error) { - var buf [6]byte - var n int - - buf[0] = 0x80 | 0x40 | byte(ptype) - if length < 192 { - buf[1] = byte(length) - n = 2 - } else if length < 8384 { - length -= 192 - buf[1] = 192 + byte(length>>8) - buf[2] = byte(length) - n = 3 - } else { - buf[1] = 255 - buf[2] = byte(length >> 24) - buf[3] = byte(length >> 16) - buf[4] = byte(length >> 8) - buf[5] = byte(length) - n = 6 - } - - _, err = w.Write(buf[:n]) - return -} - -// serializeStreamHeader writes an OpenPGP packet header to w where the -// length of the packet is unknown. It returns a io.WriteCloser which can be -// used to write the contents of the packet. See RFC 4880, section 4.2. -func serializeStreamHeader(w io.WriteCloser, ptype packetType) (out io.WriteCloser, err os.Error) { - var buf [1]byte - buf[0] = 0x80 | 0x40 | byte(ptype) - _, err = w.Write(buf[:]) - if err != nil { - return - } - out = &partialLengthWriter{w: w} - return -} - -// Packet represents an OpenPGP packet. Users are expected to try casting -// instances of this interface to specific packet types. -type Packet interface { - parse(io.Reader) os.Error -} - -// consumeAll reads from the given Reader until error, returning the number of -// bytes read. -func consumeAll(r io.Reader) (n int64, err os.Error) { - var m int - var buf [1024]byte - - for { - m, err = r.Read(buf[:]) - n += int64(m) - if err == os.EOF { - err = nil - return - } - if err != nil { - return - } - } - - panic("unreachable") -} - -// packetType represents the numeric ids of the different OpenPGP packet types. See -// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-2 -type packetType uint8 - -const ( - packetTypeEncryptedKey packetType = 1 - packetTypeSignature packetType = 2 - packetTypeSymmetricKeyEncrypted packetType = 3 - packetTypeOnePassSignature packetType = 4 - packetTypePrivateKey packetType = 5 - packetTypePublicKey packetType = 6 - packetTypePrivateSubkey packetType = 7 - packetTypeCompressed packetType = 8 - packetTypeSymmetricallyEncrypted packetType = 9 - packetTypeLiteralData packetType = 11 - packetTypeUserId packetType = 13 - packetTypePublicSubkey packetType = 14 - packetTypeSymmetricallyEncryptedMDC packetType = 18 -) - -// Read reads a single OpenPGP packet from the given io.Reader. If there is an -// error parsing a packet, the whole packet is consumed from the input. -func Read(r io.Reader) (p Packet, err os.Error) { - tag, _, contents, err := readHeader(r) - if err != nil { - return - } - - switch tag { - case packetTypeEncryptedKey: - p = new(EncryptedKey) - case packetTypeSignature: - p = new(Signature) - case packetTypeSymmetricKeyEncrypted: - p = new(SymmetricKeyEncrypted) - case packetTypeOnePassSignature: - p = new(OnePassSignature) - case packetTypePrivateKey, packetTypePrivateSubkey: - pk := new(PrivateKey) - if tag == packetTypePrivateSubkey { - pk.IsSubkey = true - } - p = pk - case packetTypePublicKey, packetTypePublicSubkey: - pk := new(PublicKey) - if tag == packetTypePublicSubkey { - pk.IsSubkey = true - } - p = pk - case packetTypeCompressed: - p = new(Compressed) - case packetTypeSymmetricallyEncrypted: - p = new(SymmetricallyEncrypted) - case packetTypeLiteralData: - p = new(LiteralData) - case packetTypeUserId: - p = new(UserId) - case packetTypeSymmetricallyEncryptedMDC: - se := new(SymmetricallyEncrypted) - se.MDC = true - p = se - default: - err = error.UnknownPacketTypeError(tag) - } - if p != nil { - err = p.parse(contents) - } - if err != nil { - consumeAll(contents) - } - return -} - -// SignatureType represents the different semantic meanings of an OpenPGP -// signature. See RFC 4880, section 5.2.1. -type SignatureType uint8 - -const ( - SigTypeBinary SignatureType = 0 - SigTypeText = 1 - SigTypeGenericCert = 0x10 - SigTypePersonaCert = 0x11 - SigTypeCasualCert = 0x12 - SigTypePositiveCert = 0x13 - SigTypeSubkeyBinding = 0x18 -) - -// PublicKeyAlgorithm represents the different public key system specified for -// OpenPGP. See -// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-12 -type PublicKeyAlgorithm uint8 - -const ( - PubKeyAlgoRSA PublicKeyAlgorithm = 1 - PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 - PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 - PubKeyAlgoElGamal PublicKeyAlgorithm = 16 - PubKeyAlgoDSA PublicKeyAlgorithm = 17 -) - -// CanEncrypt returns true if it's possible to encrypt a message to a public -// key of the given type. -func (pka PublicKeyAlgorithm) CanEncrypt() bool { - switch pka { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal: - return true - } - return false -} - -// CanSign returns true if it's possible for a public key of the given type to -// sign a message. -func (pka PublicKeyAlgorithm) CanSign() bool { - switch pka { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: - return true - } - return false -} - -// CipherFunction represents the different block ciphers specified for OpenPGP. See -// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13 -type CipherFunction uint8 - -const ( - CipherCAST5 CipherFunction = 3 - CipherAES128 CipherFunction = 7 - CipherAES192 CipherFunction = 8 - CipherAES256 CipherFunction = 9 -) - -// KeySize returns the key size, in bytes, of cipher. -func (cipher CipherFunction) KeySize() int { - switch cipher { - case CipherCAST5: - return cast5.KeySize - case CipherAES128: - return 16 - case CipherAES192: - return 24 - case CipherAES256: - return 32 - } - return 0 -} - -// blockSize returns the block size, in bytes, of cipher. -func (cipher CipherFunction) blockSize() int { - switch cipher { - case CipherCAST5: - return 8 - case CipherAES128, CipherAES192, CipherAES256: - return 16 - } - return 0 -} - -// new returns a fresh instance of the given cipher. -func (cipher CipherFunction) new(key []byte) (block cipher.Block) { - switch cipher { - case CipherCAST5: - block, _ = cast5.NewCipher(key) - case CipherAES128, CipherAES192, CipherAES256: - block, _ = aes.NewCipher(key) - } - return -} - -// readMPI reads a big integer from r. The bit length returned is the bit -// length that was specified in r. This is preserved so that the integer can be -// reserialized exactly. -func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err os.Error) { - var buf [2]byte - _, err = readFull(r, buf[0:]) - if err != nil { - return - } - bitLength = uint16(buf[0])<<8 | uint16(buf[1]) - numBytes := (int(bitLength) + 7) / 8 - mpi = make([]byte, numBytes) - _, err = readFull(r, mpi) - return -} - -// mpiLength returns the length of the given *big.Int when serialized as an -// MPI. -func mpiLength(n *big.Int) (mpiLengthInBytes int) { - mpiLengthInBytes = 2 /* MPI length */ - mpiLengthInBytes += (n.BitLen() + 7) / 8 - return -} - -// writeMPI serializes a big integer to w. -func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err os.Error) { - _, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)}) - if err == nil { - _, err = w.Write(mpiBytes) - } - return -} - -// writeBig serializes a *big.Int to w. -func writeBig(w io.Writer, i *big.Int) os.Error { - return writeMPI(w, uint16(i.BitLen()), i.Bytes()) -} diff --git a/src/pkg/crypto/openpgp/packet/packet_test.go b/src/pkg/crypto/openpgp/packet/packet_test.go deleted file mode 100644 index 23d9978ae..000000000 --- a/src/pkg/crypto/openpgp/packet/packet_test.go +++ /dev/null @@ -1,256 +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 packet - -import ( - "bytes" - "crypto/openpgp/error" - "encoding/hex" - "fmt" - "io" - "io/ioutil" - "os" - "testing" -) - -func TestReadFull(t *testing.T) { - var out [4]byte - - b := bytes.NewBufferString("foo") - n, err := readFull(b, out[:3]) - if n != 3 || err != nil { - t.Errorf("full read failed n:%d err:%s", n, err) - } - - b = bytes.NewBufferString("foo") - n, err = readFull(b, out[:4]) - if n != 3 || err != io.ErrUnexpectedEOF { - t.Errorf("partial read failed n:%d err:%s", n, err) - } - - b = bytes.NewBuffer(nil) - n, err = readFull(b, out[:3]) - if n != 0 || err != io.ErrUnexpectedEOF { - t.Errorf("empty read failed n:%d err:%s", n, err) - } -} - -func readerFromHex(s string) io.Reader { - data, err := hex.DecodeString(s) - if err != nil { - panic("readerFromHex: bad input") - } - return bytes.NewBuffer(data) -} - -var readLengthTests = []struct { - hexInput string - length int64 - isPartial bool - err os.Error -}{ - {"", 0, false, io.ErrUnexpectedEOF}, - {"1f", 31, false, nil}, - {"c0", 0, false, io.ErrUnexpectedEOF}, - {"c101", 256 + 1 + 192, false, nil}, - {"e0", 1, true, nil}, - {"e1", 2, true, nil}, - {"e2", 4, true, nil}, - {"ff", 0, false, io.ErrUnexpectedEOF}, - {"ff00", 0, false, io.ErrUnexpectedEOF}, - {"ff0000", 0, false, io.ErrUnexpectedEOF}, - {"ff000000", 0, false, io.ErrUnexpectedEOF}, - {"ff00000000", 0, false, nil}, - {"ff01020304", 16909060, false, nil}, -} - -func TestReadLength(t *testing.T) { - for i, test := range readLengthTests { - length, isPartial, err := readLength(readerFromHex(test.hexInput)) - if test.err != nil { - if err != test.err { - t.Errorf("%d: expected different error got:%s want:%s", i, err, test.err) - } - continue - } - if err != nil { - t.Errorf("%d: unexpected error: %s", i, err) - continue - } - if length != test.length || isPartial != test.isPartial { - t.Errorf("%d: bad result got:(%d,%t) want:(%d,%t)", i, length, isPartial, test.length, test.isPartial) - } - } -} - -var partialLengthReaderTests = []struct { - hexInput string - err os.Error - hexOutput string -}{ - {"e0", io.ErrUnexpectedEOF, ""}, - {"e001", io.ErrUnexpectedEOF, ""}, - {"e0010102", nil, "0102"}, - {"ff00000000", nil, ""}, - {"e10102e1030400", nil, "01020304"}, - {"e101", io.ErrUnexpectedEOF, ""}, -} - -func TestPartialLengthReader(t *testing.T) { - for i, test := range partialLengthReaderTests { - r := &partialLengthReader{readerFromHex(test.hexInput), 0, true} - out, err := ioutil.ReadAll(r) - if test.err != nil { - if err != test.err { - t.Errorf("%d: expected different error got:%s want:%s", i, err, test.err) - } - continue - } - if err != nil { - t.Errorf("%d: unexpected error: %s", i, err) - continue - } - - got := fmt.Sprintf("%x", out) - if got != test.hexOutput { - t.Errorf("%d: got:%s want:%s", i, test.hexOutput, got) - } - } -} - -var readHeaderTests = []struct { - hexInput string - structuralError bool - unexpectedEOF bool - tag int - length int64 - hexOutput string -}{ - {"", false, false, 0, 0, ""}, - {"7f", true, false, 0, 0, ""}, - - // Old format headers - {"80", false, true, 0, 0, ""}, - {"8001", false, true, 0, 1, ""}, - {"800102", false, false, 0, 1, "02"}, - {"81000102", false, false, 0, 1, "02"}, - {"820000000102", false, false, 0, 1, "02"}, - {"860000000102", false, false, 1, 1, "02"}, - {"83010203", false, false, 0, -1, "010203"}, - - // New format headers - {"c0", false, true, 0, 0, ""}, - {"c000", false, false, 0, 0, ""}, - {"c00102", false, false, 0, 1, "02"}, - {"c0020203", false, false, 0, 2, "0203"}, - {"c00202", false, true, 0, 2, ""}, - {"c3020203", false, false, 3, 2, "0203"}, -} - -func TestReadHeader(t *testing.T) { - for i, test := range readHeaderTests { - tag, length, contents, err := readHeader(readerFromHex(test.hexInput)) - if test.structuralError { - if _, ok := err.(error.StructuralError); ok { - continue - } - t.Errorf("%d: expected StructuralError, got:%s", i, err) - continue - } - if err != nil { - if len(test.hexInput) == 0 && err == os.EOF { - continue - } - if !test.unexpectedEOF || err != io.ErrUnexpectedEOF { - t.Errorf("%d: unexpected error from readHeader: %s", i, err) - } - continue - } - if int(tag) != test.tag || length != test.length { - t.Errorf("%d: got:(%d,%d) want:(%d,%d)", i, int(tag), length, test.tag, test.length) - continue - } - - body, err := ioutil.ReadAll(contents) - if err != nil { - if !test.unexpectedEOF || err != io.ErrUnexpectedEOF { - t.Errorf("%d: unexpected error from contents: %s", i, err) - } - continue - } - if test.unexpectedEOF { - t.Errorf("%d: expected ErrUnexpectedEOF from contents but got no error", i) - continue - } - got := fmt.Sprintf("%x", body) - if got != test.hexOutput { - t.Errorf("%d: got:%s want:%s", i, got, test.hexOutput) - } - } -} - -func TestSerializeHeader(t *testing.T) { - tag := packetTypePublicKey - lengths := []int{0, 1, 2, 64, 192, 193, 8000, 8384, 8385, 10000} - - for _, length := range lengths { - buf := bytes.NewBuffer(nil) - serializeHeader(buf, tag, length) - tag2, length2, _, err := readHeader(buf) - if err != nil { - t.Errorf("length %d, err: %s", length, err) - } - if tag2 != tag { - t.Errorf("length %d, tag incorrect (got %d, want %d)", length, tag2, tag) - } - if int(length2) != length { - t.Errorf("length %d, length incorrect (got %d)", length, length2) - } - } -} - -func TestPartialLengths(t *testing.T) { - buf := bytes.NewBuffer(nil) - w := new(partialLengthWriter) - w.w = noOpCloser{buf} - - const maxChunkSize = 64 - - var b [maxChunkSize]byte - var n uint8 - for l := 1; l <= maxChunkSize; l++ { - for i := 0; i < l; i++ { - b[i] = n - n++ - } - m, err := w.Write(b[:l]) - if m != l { - t.Errorf("short write got: %d want: %d", m, l) - } - if err != nil { - t.Errorf("error from write: %s", err) - } - } - w.Close() - - want := (maxChunkSize * (maxChunkSize + 1)) / 2 - copyBuf := bytes.NewBuffer(nil) - r := &partialLengthReader{buf, 0, true} - m, err := io.Copy(copyBuf, r) - if m != int64(want) { - t.Errorf("short copy got: %d want: %d", m, want) - } - if err != nil { - t.Errorf("error from copy: %s", err) - } - - copyBytes := copyBuf.Bytes() - for i := 0; i < want; i++ { - if copyBytes[i] != uint8(i) { - t.Errorf("bad pattern in copy at %d", i) - break - } - } -} diff --git a/src/pkg/crypto/openpgp/packet/private_key.go b/src/pkg/crypto/openpgp/packet/private_key.go deleted file mode 100644 index 6f8133d98..000000000 --- a/src/pkg/crypto/openpgp/packet/private_key.go +++ /dev/null @@ -1,301 +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 packet - -import ( - "big" - "bytes" - "crypto/cipher" - "crypto/dsa" - "crypto/openpgp/elgamal" - "crypto/openpgp/error" - "crypto/openpgp/s2k" - "crypto/rsa" - "crypto/sha1" - "io" - "io/ioutil" - "os" - "strconv" -) - -// PrivateKey represents a possibly encrypted private key. See RFC 4880, -// section 5.5.3. -type PrivateKey struct { - PublicKey - Encrypted bool // if true then the private key is unavailable until Decrypt has been called. - encryptedData []byte - cipher CipherFunction - s2k func(out, in []byte) - PrivateKey interface{} // An *rsa.PrivateKey. - sha1Checksum bool - iv []byte -} - -func NewRSAPrivateKey(currentTimeSecs uint32, priv *rsa.PrivateKey, isSubkey bool) *PrivateKey { - pk := new(PrivateKey) - pk.PublicKey = *NewRSAPublicKey(currentTimeSecs, &priv.PublicKey, isSubkey) - pk.PrivateKey = priv - return pk -} - -func (pk *PrivateKey) parse(r io.Reader) (err os.Error) { - err = (&pk.PublicKey).parse(r) - if err != nil { - return - } - var buf [1]byte - _, err = readFull(r, buf[:]) - if err != nil { - return - } - - s2kType := buf[0] - - switch s2kType { - case 0: - pk.s2k = nil - pk.Encrypted = false - case 254, 255: - _, err = readFull(r, buf[:]) - if err != nil { - return - } - pk.cipher = CipherFunction(buf[0]) - pk.Encrypted = true - pk.s2k, err = s2k.Parse(r) - if err != nil { - return - } - if s2kType == 254 { - pk.sha1Checksum = true - } - default: - return error.UnsupportedError("deprecated s2k function in private key") - } - - if pk.Encrypted { - blockSize := pk.cipher.blockSize() - if blockSize == 0 { - return error.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher))) - } - pk.iv = make([]byte, blockSize) - _, err = readFull(r, pk.iv) - if err != nil { - return - } - } - - pk.encryptedData, err = ioutil.ReadAll(r) - if err != nil { - return - } - - if !pk.Encrypted { - return pk.parsePrivateKey(pk.encryptedData) - } - - return -} - -func mod64kHash(d []byte) uint16 { - h := uint16(0) - for i := 0; i < len(d); i += 2 { - v := uint16(d[i]) << 8 - if i+1 < len(d) { - v += uint16(d[i+1]) - } - h += v - } - return h -} - -func (pk *PrivateKey) Serialize(w io.Writer) (err os.Error) { - // TODO(agl): support encrypted private keys - buf := bytes.NewBuffer(nil) - err = pk.PublicKey.serializeWithoutHeaders(buf) - if err != nil { - return - } - buf.WriteByte(0 /* no encryption */ ) - - privateKeyBuf := bytes.NewBuffer(nil) - - switch priv := pk.PrivateKey.(type) { - case *rsa.PrivateKey: - err = serializeRSAPrivateKey(privateKeyBuf, priv) - default: - err = error.InvalidArgumentError("non-RSA private key") - } - if err != nil { - return - } - - ptype := packetTypePrivateKey - contents := buf.Bytes() - privateKeyBytes := privateKeyBuf.Bytes() - if pk.IsSubkey { - ptype = packetTypePrivateSubkey - } - err = serializeHeader(w, ptype, len(contents)+len(privateKeyBytes)+2) - if err != nil { - return - } - _, err = w.Write(contents) - if err != nil { - return - } - _, err = w.Write(privateKeyBytes) - if err != nil { - return - } - - checksum := mod64kHash(privateKeyBytes) - var checksumBytes [2]byte - checksumBytes[0] = byte(checksum >> 8) - checksumBytes[1] = byte(checksum) - _, err = w.Write(checksumBytes[:]) - - return -} - -func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) os.Error { - err := writeBig(w, priv.D) - if err != nil { - return err - } - err = writeBig(w, priv.Primes[1]) - if err != nil { - return err - } - err = writeBig(w, priv.Primes[0]) - if err != nil { - return err - } - return writeBig(w, priv.Precomputed.Qinv) -} - -// Decrypt decrypts an encrypted private key using a passphrase. -func (pk *PrivateKey) Decrypt(passphrase []byte) os.Error { - if !pk.Encrypted { - return nil - } - - key := make([]byte, pk.cipher.KeySize()) - pk.s2k(key, passphrase) - block := pk.cipher.new(key) - cfb := cipher.NewCFBDecrypter(block, pk.iv) - - data := pk.encryptedData - cfb.XORKeyStream(data, data) - - if pk.sha1Checksum { - if len(data) < sha1.Size { - return error.StructuralError("truncated private key data") - } - h := sha1.New() - h.Write(data[:len(data)-sha1.Size]) - sum := h.Sum() - if !bytes.Equal(sum, data[len(data)-sha1.Size:]) { - return error.StructuralError("private key checksum failure") - } - data = data[:len(data)-sha1.Size] - } else { - if len(data) < 2 { - return error.StructuralError("truncated private key data") - } - var sum uint16 - for i := 0; i < len(data)-2; i++ { - sum += uint16(data[i]) - } - if data[len(data)-2] != uint8(sum>>8) || - data[len(data)-1] != uint8(sum) { - return error.StructuralError("private key checksum failure") - } - data = data[:len(data)-2] - } - - return pk.parsePrivateKey(data) -} - -func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) { - switch pk.PublicKey.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly: - return pk.parseRSAPrivateKey(data) - case PubKeyAlgoDSA: - return pk.parseDSAPrivateKey(data) - case PubKeyAlgoElGamal: - return pk.parseElGamalPrivateKey(data) - } - panic("impossible") -} - -func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err os.Error) { - rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey) - rsaPriv := new(rsa.PrivateKey) - rsaPriv.PublicKey = *rsaPub - - buf := bytes.NewBuffer(data) - d, _, err := readMPI(buf) - if err != nil { - return - } - p, _, err := readMPI(buf) - if err != nil { - return - } - q, _, err := readMPI(buf) - if err != nil { - return - } - - rsaPriv.D = new(big.Int).SetBytes(d) - rsaPriv.Primes = make([]*big.Int, 2) - rsaPriv.Primes[0] = new(big.Int).SetBytes(p) - rsaPriv.Primes[1] = new(big.Int).SetBytes(q) - rsaPriv.Precompute() - pk.PrivateKey = rsaPriv - pk.Encrypted = false - pk.encryptedData = nil - - return nil -} - -func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err os.Error) { - dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey) - dsaPriv := new(dsa.PrivateKey) - dsaPriv.PublicKey = *dsaPub - - buf := bytes.NewBuffer(data) - x, _, err := readMPI(buf) - if err != nil { - return - } - - dsaPriv.X = new(big.Int).SetBytes(x) - pk.PrivateKey = dsaPriv - pk.Encrypted = false - pk.encryptedData = nil - - return nil -} - -func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err os.Error) { - pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey) - priv := new(elgamal.PrivateKey) - priv.PublicKey = *pub - - buf := bytes.NewBuffer(data) - x, _, err := readMPI(buf) - if err != nil { - return - } - - priv.X = new(big.Int).SetBytes(x) - pk.PrivateKey = priv - pk.Encrypted = false - pk.encryptedData = nil - - return nil -} diff --git a/src/pkg/crypto/openpgp/packet/private_key_test.go b/src/pkg/crypto/openpgp/packet/private_key_test.go deleted file mode 100644 index 60eebaa6b..000000000 --- a/src/pkg/crypto/openpgp/packet/private_key_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 packet - -import ( - "testing" -) - -var privateKeyTests = []struct { - privateKeyHex string - creationTime uint32 -}{ - { - privKeyRSAHex, - 0x4cc349a8, - }, - { - privKeyElGamalHex, - 0x4df9ee1a, - }, -} - -func TestPrivateKeyRead(t *testing.T) { - for i, test := range privateKeyTests { - packet, err := Read(readerFromHex(test.privateKeyHex)) - if err != nil { - t.Errorf("#%d: failed to parse: %s", i, err) - continue - } - - privKey := packet.(*PrivateKey) - - if !privKey.Encrypted { - t.Errorf("#%d: private key isn't encrypted", i) - continue - } - - err = privKey.Decrypt([]byte("testing")) - if err != nil { - t.Errorf("#%d: failed to decrypt: %s", i, err) - continue - } - - if privKey.CreationTime != test.creationTime || privKey.Encrypted { - t.Errorf("#%d: bad result, got: %#v", i, privKey) - } - } -} - -// Generated with `gpg --export-secret-keys "Test Key 2"` -const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec" - -// Generated by `gpg --export-secret-keys` followed by a manual extraction of -// the ElGamal subkey from the packets. -const privKeyElGamalHex = "9d0157044df9ee1a100400eb8e136a58ec39b582629cdadf830bc64e0a94ed8103ca8bb247b27b11b46d1d25297ef4bcc3071785ba0c0bedfe89eabc5287fcc0edf81ab5896c1c8e4b20d27d79813c7aede75320b33eaeeaa586edc00fd1036c10133e6ba0ff277245d0d59d04b2b3421b7244aca5f4a8d870c6f1c1fbff9e1c26699a860b9504f35ca1d700030503fd1ededd3b840795be6d9ccbe3c51ee42e2f39233c432b831ddd9c4e72b7025a819317e47bf94f9ee316d7273b05d5fcf2999c3a681f519b1234bbfa6d359b4752bd9c3f77d6b6456cde152464763414ca130f4e91d91041432f90620fec0e6d6b5116076c2985d5aeaae13be492b9b329efcaf7ee25120159a0a30cd976b42d7afe030302dae7eb80db744d4960c4df930d57e87fe81412eaace9f900e6c839817a614ddb75ba6603b9417c33ea7b6c93967dfa2bcff3fa3c74a5ce2c962db65b03aece14c96cbd0038fc" diff --git a/src/pkg/crypto/openpgp/packet/public_key.go b/src/pkg/crypto/openpgp/packet/public_key.go deleted file mode 100644 index e6b0ae5f3..000000000 --- a/src/pkg/crypto/openpgp/packet/public_key.go +++ /dev/null @@ -1,393 +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 packet - -import ( - "big" - "crypto/dsa" - "crypto/openpgp/elgamal" - "crypto/openpgp/error" - "crypto/rsa" - "crypto/sha1" - "encoding/binary" - "fmt" - "hash" - "io" - "os" - "strconv" -) - -// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2. -type PublicKey struct { - CreationTime uint32 // seconds since the epoch - PubKeyAlgo PublicKeyAlgorithm - PublicKey interface{} // Either a *rsa.PublicKey or *dsa.PublicKey - Fingerprint [20]byte - KeyId uint64 - IsSubkey bool - - n, e, p, q, g, y parsedMPI -} - -func fromBig(n *big.Int) parsedMPI { - return parsedMPI{ - bytes: n.Bytes(), - bitLength: uint16(n.BitLen()), - } -} - -// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey. -func NewRSAPublicKey(creationTimeSecs uint32, pub *rsa.PublicKey, isSubkey bool) *PublicKey { - pk := &PublicKey{ - CreationTime: creationTimeSecs, - PubKeyAlgo: PubKeyAlgoRSA, - PublicKey: pub, - IsSubkey: isSubkey, - n: fromBig(pub.N), - e: fromBig(big.NewInt(int64(pub.E))), - } - - pk.setFingerPrintAndKeyId() - return pk -} - -func (pk *PublicKey) parse(r io.Reader) (err os.Error) { - // RFC 4880, section 5.5.2 - var buf [6]byte - _, err = readFull(r, buf[:]) - if err != nil { - return - } - if buf[0] != 4 { - return error.UnsupportedError("public key version") - } - pk.CreationTime = uint32(buf[1])<<24 | uint32(buf[2])<<16 | uint32(buf[3])<<8 | uint32(buf[4]) - pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5]) - switch pk.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: - err = pk.parseRSA(r) - case PubKeyAlgoDSA: - err = pk.parseDSA(r) - case PubKeyAlgoElGamal: - err = pk.parseElGamal(r) - default: - err = error.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) - } - if err != nil { - return - } - - pk.setFingerPrintAndKeyId() - return -} - -func (pk *PublicKey) setFingerPrintAndKeyId() { - // RFC 4880, section 12.2 - fingerPrint := sha1.New() - pk.SerializeSignaturePrefix(fingerPrint) - pk.serializeWithoutHeaders(fingerPrint) - copy(pk.Fingerprint[:], fingerPrint.Sum()) - pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20]) -} - -// parseRSA parses RSA public key material from the given Reader. See RFC 4880, -// section 5.5.2. -func (pk *PublicKey) parseRSA(r io.Reader) (err os.Error) { - pk.n.bytes, pk.n.bitLength, err = readMPI(r) - if err != nil { - return - } - pk.e.bytes, pk.e.bitLength, err = readMPI(r) - if err != nil { - return - } - - if len(pk.e.bytes) > 3 { - err = error.UnsupportedError("large public exponent") - return - } - rsa := &rsa.PublicKey{ - N: new(big.Int).SetBytes(pk.n.bytes), - E: 0, - } - for i := 0; i < len(pk.e.bytes); i++ { - rsa.E <<= 8 - rsa.E |= int(pk.e.bytes[i]) - } - pk.PublicKey = rsa - return -} - -// parseDSA parses DSA public key material from the given Reader. See RFC 4880, -// section 5.5.2. -func (pk *PublicKey) parseDSA(r io.Reader) (err os.Error) { - pk.p.bytes, pk.p.bitLength, err = readMPI(r) - if err != nil { - return - } - pk.q.bytes, pk.q.bitLength, err = readMPI(r) - if err != nil { - return - } - pk.g.bytes, pk.g.bitLength, err = readMPI(r) - if err != nil { - return - } - pk.y.bytes, pk.y.bitLength, err = readMPI(r) - if err != nil { - return - } - - dsa := new(dsa.PublicKey) - dsa.P = new(big.Int).SetBytes(pk.p.bytes) - dsa.Q = new(big.Int).SetBytes(pk.q.bytes) - dsa.G = new(big.Int).SetBytes(pk.g.bytes) - dsa.Y = new(big.Int).SetBytes(pk.y.bytes) - pk.PublicKey = dsa - return -} - -// parseElGamal parses ElGamal public key material from the given Reader. See -// RFC 4880, section 5.5.2. -func (pk *PublicKey) parseElGamal(r io.Reader) (err os.Error) { - pk.p.bytes, pk.p.bitLength, err = readMPI(r) - if err != nil { - return - } - pk.g.bytes, pk.g.bitLength, err = readMPI(r) - if err != nil { - return - } - pk.y.bytes, pk.y.bitLength, err = readMPI(r) - if err != nil { - return - } - - elgamal := new(elgamal.PublicKey) - elgamal.P = new(big.Int).SetBytes(pk.p.bytes) - elgamal.G = new(big.Int).SetBytes(pk.g.bytes) - elgamal.Y = new(big.Int).SetBytes(pk.y.bytes) - pk.PublicKey = elgamal - return -} - -// SerializeSignaturePrefix writes the prefix for this public key to the given Writer. -// The prefix is used when calculating a signature over this public key. See -// RFC 4880, section 5.2.4. -func (pk *PublicKey) SerializeSignaturePrefix(h hash.Hash) { - var pLength uint16 - switch pk.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: - pLength += 2 + uint16(len(pk.n.bytes)) - pLength += 2 + uint16(len(pk.e.bytes)) - case PubKeyAlgoDSA: - pLength += 2 + uint16(len(pk.p.bytes)) - pLength += 2 + uint16(len(pk.q.bytes)) - pLength += 2 + uint16(len(pk.g.bytes)) - pLength += 2 + uint16(len(pk.y.bytes)) - case PubKeyAlgoElGamal: - pLength += 2 + uint16(len(pk.p.bytes)) - pLength += 2 + uint16(len(pk.g.bytes)) - pLength += 2 + uint16(len(pk.y.bytes)) - default: - panic("unknown public key algorithm") - } - pLength += 6 - h.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) - return -} - -func (pk *PublicKey) Serialize(w io.Writer) (err os.Error) { - length := 6 // 6 byte header - - switch pk.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: - length += 2 + len(pk.n.bytes) - length += 2 + len(pk.e.bytes) - case PubKeyAlgoDSA: - length += 2 + len(pk.p.bytes) - length += 2 + len(pk.q.bytes) - length += 2 + len(pk.g.bytes) - length += 2 + len(pk.y.bytes) - case PubKeyAlgoElGamal: - length += 2 + len(pk.p.bytes) - length += 2 + len(pk.g.bytes) - length += 2 + len(pk.y.bytes) - default: - panic("unknown public key algorithm") - } - - packetType := packetTypePublicKey - if pk.IsSubkey { - packetType = packetTypePublicSubkey - } - err = serializeHeader(w, packetType, length) - if err != nil { - return - } - return pk.serializeWithoutHeaders(w) -} - -// serializeWithoutHeaders marshals the PublicKey to w in the form of an -// OpenPGP public key packet, not including the packet header. -func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err os.Error) { - var buf [6]byte - buf[0] = 4 - buf[1] = byte(pk.CreationTime >> 24) - buf[2] = byte(pk.CreationTime >> 16) - buf[3] = byte(pk.CreationTime >> 8) - buf[4] = byte(pk.CreationTime) - buf[5] = byte(pk.PubKeyAlgo) - - _, err = w.Write(buf[:]) - if err != nil { - return - } - - switch pk.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: - return writeMPIs(w, pk.n, pk.e) - case PubKeyAlgoDSA: - return writeMPIs(w, pk.p, pk.q, pk.g, pk.y) - case PubKeyAlgoElGamal: - return writeMPIs(w, pk.p, pk.g, pk.y) - } - return error.InvalidArgumentError("bad public-key algorithm") -} - -// CanSign returns true iff this public key can generate signatures -func (pk *PublicKey) CanSign() bool { - return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal -} - -// VerifySignature returns nil iff sig is a valid signature, made by this -// public key, of the data hashed into signed. signed is mutated by this call. -func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.Error) { - if !pk.CanSign() { - return error.InvalidArgumentError("public key cannot generate signatures") - } - - signed.Write(sig.HashSuffix) - hashBytes := signed.Sum() - - if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { - return error.SignatureError("hash tag doesn't match") - } - - if pk.PubKeyAlgo != sig.PubKeyAlgo { - return error.InvalidArgumentError("public key and signature use different algorithms") - } - - switch pk.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey) - err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes) - if err != nil { - return error.SignatureError("RSA verification failure") - } - return nil - case PubKeyAlgoDSA: - dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey) - if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) { - return error.SignatureError("DSA verification failure") - } - return nil - default: - panic("shouldn't happen") - } - panic("unreachable") -} - -// keySignatureHash returns a Hash of the message that needs to be signed for -// pk to assert a subkey relationship to signed. -func keySignatureHash(pk, signed *PublicKey, sig *Signature) (h hash.Hash, err os.Error) { - h = sig.Hash.New() - if h == nil { - return nil, error.UnsupportedError("hash function") - } - - // RFC 4880, section 5.2.4 - pk.SerializeSignaturePrefix(h) - pk.serializeWithoutHeaders(h) - signed.SerializeSignaturePrefix(h) - signed.serializeWithoutHeaders(h) - return -} - -// VerifyKeySignature returns nil iff sig is a valid signature, made by this -// public key, of signed. -func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) (err os.Error) { - h, err := keySignatureHash(pk, signed, sig) - if err != nil { - return err - } - return pk.VerifySignature(h, sig) -} - -// userIdSignatureHash returns a Hash of the message that needs to be signed -// to assert that pk is a valid key for id. -func userIdSignatureHash(id string, pk *PublicKey, sig *Signature) (h hash.Hash, err os.Error) { - h = sig.Hash.New() - if h == nil { - return nil, error.UnsupportedError("hash function") - } - - // RFC 4880, section 5.2.4 - pk.SerializeSignaturePrefix(h) - pk.serializeWithoutHeaders(h) - - var buf [5]byte - buf[0] = 0xb4 - buf[1] = byte(len(id) >> 24) - buf[2] = byte(len(id) >> 16) - buf[3] = byte(len(id) >> 8) - buf[4] = byte(len(id)) - h.Write(buf[:]) - h.Write([]byte(id)) - - return -} - -// VerifyUserIdSignature returns nil iff sig is a valid signature, made by this -// public key, of id. -func (pk *PublicKey) VerifyUserIdSignature(id string, sig *Signature) (err os.Error) { - h, err := userIdSignatureHash(id, pk, sig) - if err != nil { - return err - } - return pk.VerifySignature(h, sig) -} - -// KeyIdString returns the public key's fingerprint in capital hex -// (e.g. "6C7EE1B8621CC013"). -func (pk *PublicKey) KeyIdString() string { - return fmt.Sprintf("%X", pk.Fingerprint[12:20]) -} - -// KeyIdShortString returns the short form of public key's fingerprint -// in capital hex, as shown by gpg --list-keys (e.g. "621CC013"). -func (pk *PublicKey) KeyIdShortString() string { - return fmt.Sprintf("%X", pk.Fingerprint[16:20]) -} - -// A parsedMPI is used to store the contents of a big integer, along with the -// bit length that was specified in the original input. This allows the MPI to -// be reserialized exactly. -type parsedMPI struct { - bytes []byte - bitLength uint16 -} - -// writeMPIs is a utility function for serializing several big integers to the -// given Writer. -func writeMPIs(w io.Writer, mpis ...parsedMPI) (err os.Error) { - for _, mpi := range mpis { - err = writeMPI(w, mpi.bitLength, mpi.bytes) - if err != nil { - return - } - } - return -} diff --git a/src/pkg/crypto/openpgp/packet/public_key_test.go b/src/pkg/crypto/openpgp/packet/public_key_test.go deleted file mode 100644 index 6e8bfbce6..000000000 --- a/src/pkg/crypto/openpgp/packet/public_key_test.go +++ /dev/null @@ -1,98 +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 packet - -import ( - "bytes" - "encoding/hex" - "testing" -) - -var pubKeyTests = []struct { - hexData string - hexFingerprint string - creationTime uint32 - pubKeyAlgo PublicKeyAlgorithm - keyId uint64 - keyIdString string - keyIdShort string -}{ - {rsaPkDataHex, rsaFingerprintHex, 0x4d3c5c10, PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"}, - {dsaPkDataHex, dsaFingerprintHex, 0x4d432f89, PubKeyAlgoDSA, 0x8e8fbe54062f19ed, "8E8FBE54062F19ED", "062F19ED"}, -} - -func TestPublicKeyRead(t *testing.T) { - for i, test := range pubKeyTests { - packet, err := Read(readerFromHex(test.hexData)) - if err != nil { - t.Errorf("#%d: Read error: %s", i, err) - continue - } - pk, ok := packet.(*PublicKey) - if !ok { - t.Errorf("#%d: failed to parse, got: %#v", i, packet) - continue - } - if pk.PubKeyAlgo != test.pubKeyAlgo { - t.Errorf("#%d: bad public key algorithm got:%x want:%x", i, pk.PubKeyAlgo, test.pubKeyAlgo) - } - if pk.CreationTime != test.creationTime { - t.Errorf("#%d: bad creation time got:%x want:%x", i, pk.CreationTime, test.creationTime) - } - expectedFingerprint, _ := hex.DecodeString(test.hexFingerprint) - if !bytes.Equal(expectedFingerprint, pk.Fingerprint[:]) { - t.Errorf("#%d: bad fingerprint got:%x want:%x", i, pk.Fingerprint[:], expectedFingerprint) - } - if pk.KeyId != test.keyId { - t.Errorf("#%d: bad keyid got:%x want:%x", i, pk.KeyId, test.keyId) - } - if g, e := pk.KeyIdString(), test.keyIdString; g != e { - t.Errorf("#%d: bad KeyIdString got:%q want:%q", i, g, e) - } - if g, e := pk.KeyIdShortString(), test.keyIdShort; g != e { - t.Errorf("#%d: bad KeyIdShortString got:%q want:%q", i, g, e) - } - } -} - -func TestPublicKeySerialize(t *testing.T) { - for i, test := range pubKeyTests { - packet, err := Read(readerFromHex(test.hexData)) - if err != nil { - t.Errorf("#%d: Read error: %s", i, err) - continue - } - pk, ok := packet.(*PublicKey) - if !ok { - t.Errorf("#%d: failed to parse, got: %#v", i, packet) - continue - } - serializeBuf := bytes.NewBuffer(nil) - err = pk.Serialize(serializeBuf) - if err != nil { - t.Errorf("#%d: failed to serialize: %s", i, err) - continue - } - - packet, err = Read(serializeBuf) - if err != nil { - t.Errorf("#%d: Read error (from serialized data): %s", i, err) - continue - } - pk, ok = packet.(*PublicKey) - if !ok { - t.Errorf("#%d: failed to parse serialized data, got: %#v", i, packet) - continue - } - } -} - -const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb" - -const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001" - -const dsaFingerprintHex = "eece4c094db002103714c63c8e8fbe54062f19ed" - -const dsaPkDataHex = "9901a2044d432f89110400cd581334f0d7a1e1bdc8b9d6d8c0baf68793632735d2bb0903224cbaa1dfbf35a60ee7a13b92643421e1eb41aa8d79bea19a115a677f6b8ba3c7818ce53a6c2a24a1608bd8b8d6e55c5090cbde09dd26e356267465ae25e69ec8bdd57c7bbb2623e4d73336f73a0a9098f7f16da2e25252130fd694c0e8070c55a812a423ae7f00a0ebf50e70c2f19c3520a551bd4b08d30f23530d3d03ff7d0bf4a53a64a09dc5e6e6e35854b7d70c882b0c60293401958b1bd9e40abec3ea05ba87cf64899299d4bd6aa7f459c201d3fbbd6c82004bdc5e8a9eb8082d12054cc90fa9d4ec251a843236a588bf49552441817436c4f43326966fe85447d4e6d0acf8fa1ef0f014730770603ad7634c3088dc52501c237328417c31c89ed70400b2f1a98b0bf42f11fefc430704bebbaa41d9f355600c3facee1e490f64208e0e094ea55e3a598a219a58500bf78ac677b670a14f4e47e9cf8eab4f368cc1ddcaa18cc59309d4cc62dd4f680e73e6cc3e1ce87a84d0925efbcb26c575c093fc42eecf45135fabf6403a25c2016e1774c0484e440a18319072c617cc97ac0a3bb0" diff --git a/src/pkg/crypto/openpgp/packet/reader.go b/src/pkg/crypto/openpgp/packet/reader.go deleted file mode 100644 index 5febc3bc8..000000000 --- a/src/pkg/crypto/openpgp/packet/reader.go +++ /dev/null @@ -1,63 +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 packet - -import ( - "crypto/openpgp/error" - "io" - "os" -) - -// Reader reads packets from an io.Reader and allows packets to be 'unread' so -// that they result from the next call to Next. -type Reader struct { - q []Packet - readers []io.Reader -} - -// Next returns the most recently unread Packet, or reads another packet from -// the top-most io.Reader. Unknown packet types are skipped. -func (r *Reader) Next() (p Packet, err os.Error) { - if len(r.q) > 0 { - p = r.q[len(r.q)-1] - r.q = r.q[:len(r.q)-1] - return - } - - for len(r.readers) > 0 { - p, err = Read(r.readers[len(r.readers)-1]) - if err == nil { - return - } - if err == os.EOF { - r.readers = r.readers[:len(r.readers)-1] - continue - } - if _, ok := err.(error.UnknownPacketTypeError); !ok { - return nil, err - } - } - - return nil, os.EOF -} - -// Push causes the Reader to start reading from a new io.Reader. When an EOF -// error is seen from the new io.Reader, it is popped and the Reader continues -// to read from the next most recent io.Reader. -func (r *Reader) Push(reader io.Reader) { - r.readers = append(r.readers, reader) -} - -// Unread causes the given Packet to be returned from the next call to Next. -func (r *Reader) Unread(p Packet) { - r.q = append(r.q, p) -} - -func NewReader(r io.Reader) *Reader { - return &Reader{ - q: nil, - readers: []io.Reader{r}, - } -} diff --git a/src/pkg/crypto/openpgp/packet/signature.go b/src/pkg/crypto/openpgp/packet/signature.go deleted file mode 100644 index 7577e2875..000000000 --- a/src/pkg/crypto/openpgp/packet/signature.go +++ /dev/null @@ -1,558 +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 packet - -import ( - "crypto" - "crypto/dsa" - "crypto/openpgp/error" - "crypto/openpgp/s2k" - "crypto/rand" - "crypto/rsa" - "encoding/binary" - "hash" - "io" - "os" - "strconv" -) - -// Signature represents a signature. See RFC 4880, section 5.2. -type Signature struct { - SigType SignatureType - PubKeyAlgo PublicKeyAlgorithm - Hash crypto.Hash - - // HashSuffix is extra data that is hashed in after the signed data. - HashSuffix []byte - // HashTag contains the first two bytes of the hash for fast rejection - // of bad signed data. - HashTag [2]byte - CreationTime uint32 // Unix epoch time - - RSASignature parsedMPI - DSASigR, DSASigS parsedMPI - - // rawSubpackets contains the unparsed subpackets, in order. - rawSubpackets []outputSubpacket - - // The following are optional so are nil when not included in the - // signature. - - SigLifetimeSecs, KeyLifetimeSecs *uint32 - PreferredSymmetric, PreferredHash, PreferredCompression []uint8 - IssuerKeyId *uint64 - IsPrimaryId *bool - - // FlagsValid is set if any flags were given. See RFC 4880, section - // 5.2.3.21 for details. - FlagsValid bool - FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage bool - - outSubpackets []outputSubpacket -} - -func (sig *Signature) parse(r io.Reader) (err os.Error) { - // RFC 4880, section 5.2.3 - var buf [5]byte - _, err = readFull(r, buf[:1]) - if err != nil { - return - } - if buf[0] != 4 { - err = error.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) - return - } - - _, err = readFull(r, buf[:5]) - if err != nil { - return - } - sig.SigType = SignatureType(buf[0]) - sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) - switch sig.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: - default: - err = error.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) - return - } - - var ok bool - sig.Hash, ok = s2k.HashIdToHash(buf[2]) - if !ok { - return error.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) - } - - hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4]) - l := 6 + hashedSubpacketsLength - sig.HashSuffix = make([]byte, l+6) - sig.HashSuffix[0] = 4 - copy(sig.HashSuffix[1:], buf[:5]) - hashedSubpackets := sig.HashSuffix[6:l] - _, err = readFull(r, hashedSubpackets) - if err != nil { - return - } - // See RFC 4880, section 5.2.4 - trailer := sig.HashSuffix[l:] - trailer[0] = 4 - trailer[1] = 0xff - trailer[2] = uint8(l >> 24) - trailer[3] = uint8(l >> 16) - trailer[4] = uint8(l >> 8) - trailer[5] = uint8(l) - - err = parseSignatureSubpackets(sig, hashedSubpackets, true) - if err != nil { - return - } - - _, err = readFull(r, buf[:2]) - if err != nil { - return - } - unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1]) - unhashedSubpackets := make([]byte, unhashedSubpacketsLength) - _, err = readFull(r, unhashedSubpackets) - if err != nil { - return - } - err = parseSignatureSubpackets(sig, unhashedSubpackets, false) - if err != nil { - return - } - - _, err = readFull(r, sig.HashTag[:2]) - if err != nil { - return - } - - switch sig.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) - case PubKeyAlgoDSA: - sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r) - if err == nil { - sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) - } - default: - panic("unreachable") - } - return -} - -// parseSignatureSubpackets parses subpackets of the main signature packet. See -// RFC 4880, section 5.2.3.1. -func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err os.Error) { - for len(subpackets) > 0 { - subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed) - if err != nil { - return - } - } - - if sig.CreationTime == 0 { - err = error.StructuralError("no creation time in signature") - } - - return -} - -type signatureSubpacketType uint8 - -const ( - creationTimeSubpacket signatureSubpacketType = 2 - signatureExpirationSubpacket signatureSubpacketType = 3 - keyExpirySubpacket signatureSubpacketType = 9 - prefSymmetricAlgosSubpacket signatureSubpacketType = 11 - issuerSubpacket signatureSubpacketType = 16 - prefHashAlgosSubpacket signatureSubpacketType = 21 - prefCompressionSubpacket signatureSubpacketType = 22 - primaryUserIdSubpacket signatureSubpacketType = 25 - keyFlagsSubpacket signatureSubpacketType = 27 -) - -// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. -func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err os.Error) { - // RFC 4880, section 5.2.3.1 - var ( - length uint32 - packetType signatureSubpacketType - isCritical bool - ) - switch { - case subpacket[0] < 192: - length = uint32(subpacket[0]) - subpacket = subpacket[1:] - case subpacket[0] < 255: - if len(subpacket) < 2 { - goto Truncated - } - length = uint32(subpacket[0]-192)<<8 + uint32(subpacket[1]) + 192 - subpacket = subpacket[2:] - default: - if len(subpacket) < 5 { - goto Truncated - } - length = uint32(subpacket[1])<<24 | - uint32(subpacket[2])<<16 | - uint32(subpacket[3])<<8 | - uint32(subpacket[4]) - subpacket = subpacket[5:] - } - if length > uint32(len(subpacket)) { - goto Truncated - } - rest = subpacket[length:] - subpacket = subpacket[:length] - if len(subpacket) == 0 { - err = error.StructuralError("zero length signature subpacket") - return - } - packetType = signatureSubpacketType(subpacket[0] & 0x7f) - isCritical = subpacket[0]&0x80 == 0x80 - subpacket = subpacket[1:] - sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket}) - switch packetType { - case creationTimeSubpacket: - if !isHashed { - err = error.StructuralError("signature creation time in non-hashed area") - return - } - if len(subpacket) != 4 { - err = error.StructuralError("signature creation time not four bytes") - return - } - sig.CreationTime = binary.BigEndian.Uint32(subpacket) - case signatureExpirationSubpacket: - // Signature expiration time, section 5.2.3.10 - if !isHashed { - return - } - if len(subpacket) != 4 { - err = error.StructuralError("expiration subpacket with bad length") - return - } - sig.SigLifetimeSecs = new(uint32) - *sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket) - case keyExpirySubpacket: - // Key expiration time, section 5.2.3.6 - if !isHashed { - return - } - if len(subpacket) != 4 { - err = error.StructuralError("key expiration subpacket with bad length") - return - } - sig.KeyLifetimeSecs = new(uint32) - *sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket) - case prefSymmetricAlgosSubpacket: - // Preferred symmetric algorithms, section 5.2.3.7 - if !isHashed { - return - } - sig.PreferredSymmetric = make([]byte, len(subpacket)) - copy(sig.PreferredSymmetric, subpacket) - case issuerSubpacket: - // Issuer, section 5.2.3.5 - if len(subpacket) != 8 { - err = error.StructuralError("issuer subpacket with bad length") - return - } - sig.IssuerKeyId = new(uint64) - *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket) - case prefHashAlgosSubpacket: - // Preferred hash algorithms, section 5.2.3.8 - if !isHashed { - return - } - sig.PreferredHash = make([]byte, len(subpacket)) - copy(sig.PreferredHash, subpacket) - case prefCompressionSubpacket: - // Preferred compression algorithms, section 5.2.3.9 - if !isHashed { - return - } - sig.PreferredCompression = make([]byte, len(subpacket)) - copy(sig.PreferredCompression, subpacket) - case primaryUserIdSubpacket: - // Primary User ID, section 5.2.3.19 - if !isHashed { - return - } - if len(subpacket) != 1 { - err = error.StructuralError("primary user id subpacket with bad length") - return - } - sig.IsPrimaryId = new(bool) - if subpacket[0] > 0 { - *sig.IsPrimaryId = true - } - case keyFlagsSubpacket: - // Key flags, section 5.2.3.21 - if !isHashed { - return - } - if len(subpacket) == 0 { - err = error.StructuralError("empty key flags subpacket") - return - } - sig.FlagsValid = true - if subpacket[0]&1 != 0 { - sig.FlagCertify = true - } - if subpacket[0]&2 != 0 { - sig.FlagSign = true - } - if subpacket[0]&4 != 0 { - sig.FlagEncryptCommunications = true - } - if subpacket[0]&8 != 0 { - sig.FlagEncryptStorage = true - } - - default: - if isCritical { - err = error.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) - return - } - } - return - -Truncated: - err = error.StructuralError("signature subpacket truncated") - return -} - -// subpacketLengthLength returns the length, in bytes, of an encoded length value. -func subpacketLengthLength(length int) int { - if length < 192 { - return 1 - } - if length < 16320 { - return 2 - } - return 5 -} - -// serializeSubpacketLength marshals the given length into to. -func serializeSubpacketLength(to []byte, length int) int { - if length < 192 { - to[0] = byte(length) - return 1 - } - if length < 16320 { - length -= 192 - to[0] = byte(length >> 8) - to[1] = byte(length) - return 2 - } - to[0] = 255 - to[1] = byte(length >> 24) - to[2] = byte(length >> 16) - to[3] = byte(length >> 8) - to[4] = byte(length) - return 5 -} - -// subpacketsLength returns the serialized length, in bytes, of the given -// subpackets. -func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) { - for _, subpacket := range subpackets { - if subpacket.hashed == hashed { - length += subpacketLengthLength(len(subpacket.contents) + 1) - length += 1 // type byte - length += len(subpacket.contents) - } - } - return -} - -// serializeSubpackets marshals the given subpackets into to. -func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { - for _, subpacket := range subpackets { - if subpacket.hashed == hashed { - n := serializeSubpacketLength(to, len(subpacket.contents)+1) - to[n] = byte(subpacket.subpacketType) - to = to[1+n:] - n = copy(to, subpacket.contents) - to = to[n:] - } - } - return -} - -// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. -func (sig *Signature) buildHashSuffix() (err os.Error) { - hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) - - var ok bool - l := 6 + hashedSubpacketsLen - sig.HashSuffix = make([]byte, l+6) - sig.HashSuffix[0] = 4 - sig.HashSuffix[1] = uint8(sig.SigType) - sig.HashSuffix[2] = uint8(sig.PubKeyAlgo) - sig.HashSuffix[3], ok = s2k.HashToHashId(sig.Hash) - if !ok { - sig.HashSuffix = nil - return error.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash))) - } - sig.HashSuffix[4] = byte(hashedSubpacketsLen >> 8) - sig.HashSuffix[5] = byte(hashedSubpacketsLen) - serializeSubpackets(sig.HashSuffix[6:l], sig.outSubpackets, true) - trailer := sig.HashSuffix[l:] - trailer[0] = 4 - trailer[1] = 0xff - trailer[2] = byte(l >> 24) - trailer[3] = byte(l >> 16) - trailer[4] = byte(l >> 8) - trailer[5] = byte(l) - return -} - -func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err os.Error) { - err = sig.buildHashSuffix() - if err != nil { - return - } - - h.Write(sig.HashSuffix) - digest = h.Sum() - copy(sig.HashTag[:], digest) - return -} - -// Sign signs a message with a private key. The hash, h, must contain -// the hash of the message to be signed and will be mutated by this function. -// On success, the signature is stored in sig. Call Serialize to write it out. -func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey) (err os.Error) { - sig.outSubpackets = sig.buildSubpackets() - digest, err := sig.signPrepareHash(h) - if err != nil { - return - } - - switch priv.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sig.RSASignature.bytes, err = rsa.SignPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) - sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes)) - case PubKeyAlgoDSA: - r, s, err := dsa.Sign(rand.Reader, priv.PrivateKey.(*dsa.PrivateKey), digest) - if err == nil { - sig.DSASigR.bytes = r.Bytes() - sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) - sig.DSASigS.bytes = s.Bytes() - sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes)) - } - default: - err = error.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) - } - - return -} - -// SignUserId computes a signature from priv, asserting that pub is a valid -// key for the identity id. On success, the signature is stored in sig. Call -// Serialize to write it out. -func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey) os.Error { - h, err := userIdSignatureHash(id, pub, sig) - if err != nil { - return nil - } - return sig.Sign(h, priv) -} - -// SignKey computes a signature from priv, asserting that pub is a subkey. On -// success, the signature is stored in sig. Call Serialize to write it out. -func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey) os.Error { - h, err := keySignatureHash(&priv.PublicKey, pub, sig) - if err != nil { - return err - } - return sig.Sign(h, priv) -} - -// Serialize marshals sig to w. SignRSA or SignDSA must have been called first. -func (sig *Signature) Serialize(w io.Writer) (err os.Error) { - if len(sig.outSubpackets) == 0 { - sig.outSubpackets = sig.rawSubpackets - } - if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil { - return error.InvalidArgumentError("Signature: need to call SignRSA or SignDSA before Serialize") - } - - sigLength := 0 - switch sig.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sigLength = 2 + len(sig.RSASignature.bytes) - case PubKeyAlgoDSA: - sigLength = 2 + len(sig.DSASigR.bytes) - sigLength += 2 + len(sig.DSASigS.bytes) - default: - panic("impossible") - } - - unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) - length := len(sig.HashSuffix) - 6 /* trailer not included */ + - 2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + - 2 /* hash tag */ + sigLength - err = serializeHeader(w, packetTypeSignature, length) - if err != nil { - return - } - - _, err = w.Write(sig.HashSuffix[:len(sig.HashSuffix)-6]) - if err != nil { - return - } - - unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen) - unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8) - unhashedSubpackets[1] = byte(unhashedSubpacketsLen) - serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false) - - _, err = w.Write(unhashedSubpackets) - if err != nil { - return - } - _, err = w.Write(sig.HashTag[:]) - if err != nil { - return - } - - switch sig.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - err = writeMPIs(w, sig.RSASignature) - case PubKeyAlgoDSA: - err = writeMPIs(w, sig.DSASigR, sig.DSASigS) - default: - panic("impossible") - } - return -} - -// outputSubpacket represents a subpacket to be marshaled. -type outputSubpacket struct { - hashed bool // true if this subpacket is in the hashed area. - subpacketType signatureSubpacketType - isCritical bool - contents []byte -} - -func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) { - creationTime := make([]byte, 4) - creationTime[0] = byte(sig.CreationTime >> 24) - creationTime[1] = byte(sig.CreationTime >> 16) - creationTime[2] = byte(sig.CreationTime >> 8) - creationTime[3] = byte(sig.CreationTime) - subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) - - if sig.IssuerKeyId != nil { - keyId := make([]byte, 8) - binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) - subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId}) - } - - return -} diff --git a/src/pkg/crypto/openpgp/packet/signature_test.go b/src/pkg/crypto/openpgp/packet/signature_test.go deleted file mode 100644 index c1bbde8b0..000000000 --- a/src/pkg/crypto/openpgp/packet/signature_test.go +++ /dev/null @@ -1,42 +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 packet - -import ( - "bytes" - "crypto" - "encoding/hex" - "testing" -) - -func TestSignatureRead(t *testing.T) { - packet, err := Read(readerFromHex(signatureDataHex)) - if err != nil { - t.Error(err) - return - } - sig, ok := packet.(*Signature) - if !ok || sig.SigType != SigTypeBinary || sig.PubKeyAlgo != PubKeyAlgoRSA || sig.Hash != crypto.SHA1 { - t.Errorf("failed to parse, got: %#v", packet) - } -} - -func TestSignatureReserialize(t *testing.T) { - packet, _ := Read(readerFromHex(signatureDataHex)) - sig := packet.(*Signature) - out := new(bytes.Buffer) - err := sig.Serialize(out) - if err != nil { - t.Errorf("error reserializing: %s", err) - return - } - - expected, _ := hex.DecodeString(signatureDataHex) - if !bytes.Equal(expected, out.Bytes()) { - t.Errorf("output doesn't match input (got vs expected):\n%s\n%s", hex.Dump(out.Bytes()), hex.Dump(expected)) - } -} - -const signatureDataHex = "c2c05c04000102000605024cb45112000a0910ab105c91af38fb158f8d07ff5596ea368c5efe015bed6e78348c0f033c931d5f2ce5db54ce7f2a7e4b4ad64db758d65a7a71773edeab7ba2a9e0908e6a94a1175edd86c1d843279f045b021a6971a72702fcbd650efc393c5474d5b59a15f96d2eaad4c4c426797e0dcca2803ef41c6ff234d403eec38f31d610c344c06f2401c262f0993b2e66cad8a81ebc4322c723e0d4ba09fe917e8777658307ad8329adacba821420741009dfe87f007759f0982275d028a392c6ed983a0d846f890b36148c7358bdb8a516007fac760261ecd06076813831a36d0459075d1befa245ae7f7fb103d92ca759e9498fe60ef8078a39a3beda510deea251ea9f0a7f0df6ef42060f20780360686f3e400e" diff --git a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go deleted file mode 100644 index ad4f1d621..000000000 --- a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go +++ /dev/null @@ -1,162 +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 packet - -import ( - "bytes" - "crypto/cipher" - "crypto/openpgp/error" - "crypto/openpgp/s2k" - "io" - "os" - "strconv" -) - -// This is the largest session key that we'll support. Since no 512-bit cipher -// has even been seriously used, this is comfortably large. -const maxSessionKeySizeInBytes = 64 - -// SymmetricKeyEncrypted represents a passphrase protected session key. See RFC -// 4880, section 5.3. -type SymmetricKeyEncrypted struct { - CipherFunc CipherFunction - Encrypted bool - Key []byte // Empty unless Encrypted is false. - s2k func(out, in []byte) - encryptedKey []byte -} - -const symmetricKeyEncryptedVersion = 4 - -func (ske *SymmetricKeyEncrypted) parse(r io.Reader) (err os.Error) { - // RFC 4880, section 5.3. - var buf [2]byte - _, err = readFull(r, buf[:]) - if err != nil { - return - } - if buf[0] != symmetricKeyEncryptedVersion { - return error.UnsupportedError("SymmetricKeyEncrypted version") - } - ske.CipherFunc = CipherFunction(buf[1]) - - if ske.CipherFunc.KeySize() == 0 { - return error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1]))) - } - - ske.s2k, err = s2k.Parse(r) - if err != nil { - return - } - - encryptedKey := make([]byte, maxSessionKeySizeInBytes) - // The session key may follow. We just have to try and read to find - // out. If it exists then we limit it to maxSessionKeySizeInBytes. - n, err := readFull(r, encryptedKey) - if err != nil && err != io.ErrUnexpectedEOF { - return - } - err = nil - if n != 0 { - if n == maxSessionKeySizeInBytes { - return error.UnsupportedError("oversized encrypted session key") - } - ske.encryptedKey = encryptedKey[:n] - } - - ske.Encrypted = true - - return -} - -// Decrypt attempts to decrypt an encrypted session key. If it returns nil, -// ske.Key will contain the session key. -func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) os.Error { - if !ske.Encrypted { - return nil - } - - key := make([]byte, ske.CipherFunc.KeySize()) - ske.s2k(key, passphrase) - - if len(ske.encryptedKey) == 0 { - ske.Key = key - } else { - // the IV is all zeros - iv := make([]byte, ske.CipherFunc.blockSize()) - c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv) - c.XORKeyStream(ske.encryptedKey, ske.encryptedKey) - ske.CipherFunc = CipherFunction(ske.encryptedKey[0]) - if ske.CipherFunc.blockSize() == 0 { - return error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(ske.CipherFunc))) - } - ske.CipherFunc = CipherFunction(ske.encryptedKey[0]) - ske.Key = ske.encryptedKey[1:] - if len(ske.Key)%ske.CipherFunc.blockSize() != 0 { - ske.Key = nil - return error.StructuralError("length of decrypted key not a multiple of block size") - } - } - - ske.Encrypted = false - return nil -} - -// SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w. The -// packet contains a random session key, encrypted by a key derived from the -// given passphrase. The session key is returned and must be passed to -// SerializeSymmetricallyEncrypted. -func SerializeSymmetricKeyEncrypted(w io.Writer, rand io.Reader, passphrase []byte, cipherFunc CipherFunction) (key []byte, err os.Error) { - keySize := cipherFunc.KeySize() - if keySize == 0 { - return nil, error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) - } - - s2kBuf := new(bytes.Buffer) - keyEncryptingKey := make([]byte, keySize) - // s2k.Serialize salts and stretches the passphrase, and writes the - // resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf. - err = s2k.Serialize(s2kBuf, keyEncryptingKey, rand, passphrase) - if err != nil { - return - } - s2kBytes := s2kBuf.Bytes() - - packetLength := 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize - err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength) - if err != nil { - return - } - - var buf [2]byte - buf[0] = symmetricKeyEncryptedVersion - buf[1] = byte(cipherFunc) - _, err = w.Write(buf[:]) - if err != nil { - return - } - _, err = w.Write(s2kBytes) - if err != nil { - return - } - - sessionKey := make([]byte, keySize) - _, err = io.ReadFull(rand, sessionKey) - if err != nil { - return - } - iv := make([]byte, cipherFunc.blockSize()) - c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv) - encryptedCipherAndKey := make([]byte, keySize+1) - c.XORKeyStream(encryptedCipherAndKey, buf[1:]) - c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey) - _, err = w.Write(encryptedCipherAndKey) - if err != nil { - return - } - - key = sessionKey - return -} diff --git a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go deleted file mode 100644 index 823ec400d..000000000 --- a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.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 packet - -import ( - "bytes" - "crypto/rand" - "encoding/hex" - "io/ioutil" - "os" - "testing" -) - -func TestSymmetricKeyEncrypted(t *testing.T) { - buf := readerFromHex(symmetricallyEncryptedHex) - packet, err := Read(buf) - if err != nil { - t.Errorf("failed to read SymmetricKeyEncrypted: %s", err) - return - } - ske, ok := packet.(*SymmetricKeyEncrypted) - if !ok { - t.Error("didn't find SymmetricKeyEncrypted packet") - return - } - err = ske.Decrypt([]byte("password")) - if err != nil { - t.Error(err) - return - } - - packet, err = Read(buf) - if err != nil { - t.Errorf("failed to read SymmetricallyEncrypted: %s", err) - return - } - se, ok := packet.(*SymmetricallyEncrypted) - if !ok { - t.Error("didn't find SymmetricallyEncrypted packet") - return - } - r, err := se.Decrypt(ske.CipherFunc, ske.Key) - if err != nil { - t.Error(err) - return - } - - contents, err := ioutil.ReadAll(r) - if err != nil && err != os.EOF { - t.Error(err) - return - } - - expectedContents, _ := hex.DecodeString(symmetricallyEncryptedContentsHex) - if !bytes.Equal(expectedContents, contents) { - t.Errorf("bad contents got:%x want:%x", contents, expectedContents) - } -} - -const symmetricallyEncryptedHex = "8c0d04030302371a0b38d884f02060c91cf97c9973b8e58e028e9501708ccfe618fb92afef7fa2d80ddadd93cf" -const symmetricallyEncryptedContentsHex = "cb1062004d14c4df636f6e74656e74732e0a" - -func TestSerializeSymmetricKeyEncrypted(t *testing.T) { - buf := bytes.NewBuffer(nil) - passphrase := []byte("testing") - cipherFunc := CipherAES128 - - key, err := SerializeSymmetricKeyEncrypted(buf, rand.Reader, passphrase, cipherFunc) - if err != nil { - t.Errorf("failed to serialize: %s", err) - return - } - - p, err := Read(buf) - if err != nil { - t.Errorf("failed to reparse: %s", err) - return - } - ske, ok := p.(*SymmetricKeyEncrypted) - if !ok { - t.Errorf("parsed a different packet type: %#v", p) - return - } - - if !ske.Encrypted { - t.Errorf("SKE not encrypted but should be") - } - if ske.CipherFunc != cipherFunc { - t.Errorf("SKE cipher function is %d (expected %d)", ske.CipherFunc, cipherFunc) - } - err = ske.Decrypt(passphrase) - if err != nil { - t.Errorf("failed to decrypt reparsed SKE: %s", err) - return - } - if !bytes.Equal(key, ske.Key) { - t.Errorf("keys don't match after Decrpyt: %x (original) vs %x (parsed)", key, ske.Key) - } -} diff --git a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go deleted file mode 100644 index e33c9f3a0..000000000 --- a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go +++ /dev/null @@ -1,291 +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 packet - -import ( - "crypto/cipher" - "crypto/openpgp/error" - "crypto/rand" - "crypto/sha1" - "crypto/subtle" - "hash" - "io" - "os" - "strconv" -) - -// SymmetricallyEncrypted represents a symmetrically encrypted byte string. The -// encrypted contents will consist of more OpenPGP packets. See RFC 4880, -// sections 5.7 and 5.13. -type SymmetricallyEncrypted struct { - MDC bool // true iff this is a type 18 packet and thus has an embedded MAC. - contents io.Reader - prefix []byte -} - -const symmetricallyEncryptedVersion = 1 - -func (se *SymmetricallyEncrypted) parse(r io.Reader) os.Error { - if se.MDC { - // See RFC 4880, section 5.13. - var buf [1]byte - _, err := readFull(r, buf[:]) - if err != nil { - return err - } - if buf[0] != symmetricallyEncryptedVersion { - return error.UnsupportedError("unknown SymmetricallyEncrypted version") - } - } - se.contents = r - return nil -} - -// Decrypt returns a ReadCloser, from which the decrypted contents of the -// packet can be read. An incorrect key can, with high probability, be detected -// immediately and this will result in a KeyIncorrect error being returned. -func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, os.Error) { - keySize := c.KeySize() - if keySize == 0 { - return nil, error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c))) - } - if len(key) != keySize { - return nil, error.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length") - } - - if se.prefix == nil { - se.prefix = make([]byte, c.blockSize()+2) - _, err := readFull(se.contents, se.prefix) - if err != nil { - return nil, err - } - } else if len(se.prefix) != c.blockSize()+2 { - return nil, error.InvalidArgumentError("can't try ciphers with different block lengths") - } - - ocfbResync := cipher.OCFBResync - if se.MDC { - // MDC packets use a different form of OCFB mode. - ocfbResync = cipher.OCFBNoResync - } - - s := cipher.NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync) - if s == nil { - return nil, error.KeyIncorrectError - } - - plaintext := cipher.StreamReader{S: s, R: se.contents} - - if se.MDC { - // MDC packets have an embedded hash that we need to check. - h := sha1.New() - h.Write(se.prefix) - return &seMDCReader{in: plaintext, h: h}, nil - } - - // Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser. - return seReader{plaintext}, nil -} - -// seReader wraps an io.Reader with a no-op Close method. -type seReader struct { - in io.Reader -} - -func (ser seReader) Read(buf []byte) (int, os.Error) { - return ser.in.Read(buf) -} - -func (ser seReader) Close() os.Error { - return nil -} - -const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size - -// An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold -// of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an -// MDC packet containing a hash of the previous contents which is checked -// against the running hash. See RFC 4880, section 5.13. -type seMDCReader struct { - in io.Reader - h hash.Hash - trailer [mdcTrailerSize]byte - scratch [mdcTrailerSize]byte - trailerUsed int - error bool - eof bool -} - -func (ser *seMDCReader) Read(buf []byte) (n int, err os.Error) { - if ser.error { - err = io.ErrUnexpectedEOF - return - } - if ser.eof { - err = os.EOF - return - } - - // If we haven't yet filled the trailer buffer then we must do that - // first. - for ser.trailerUsed < mdcTrailerSize { - n, err = ser.in.Read(ser.trailer[ser.trailerUsed:]) - ser.trailerUsed += n - if err == os.EOF { - if ser.trailerUsed != mdcTrailerSize { - n = 0 - err = io.ErrUnexpectedEOF - ser.error = true - return - } - ser.eof = true - n = 0 - return - } - - if err != nil { - n = 0 - return - } - } - - // If it's a short read then we read into a temporary buffer and shift - // the data into the caller's buffer. - if len(buf) <= mdcTrailerSize { - n, err = readFull(ser.in, ser.scratch[:len(buf)]) - copy(buf, ser.trailer[:n]) - ser.h.Write(buf[:n]) - copy(ser.trailer[:], ser.trailer[n:]) - copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:]) - if n < len(buf) { - ser.eof = true - err = os.EOF - } - return - } - - n, err = ser.in.Read(buf[mdcTrailerSize:]) - copy(buf, ser.trailer[:]) - ser.h.Write(buf[:n]) - copy(ser.trailer[:], buf[n:]) - - if err == os.EOF { - ser.eof = true - } - return -} - -// This is a new-format packet tag byte for a type 19 (MDC) packet. -const mdcPacketTagByte = byte(0x80) | 0x40 | 19 - -func (ser *seMDCReader) Close() os.Error { - if ser.error { - return error.SignatureError("error during reading") - } - - for !ser.eof { - // We haven't seen EOF so we need to read to the end - var buf [1024]byte - _, err := ser.Read(buf[:]) - if err == os.EOF { - break - } - if err != nil { - return error.SignatureError("error during reading") - } - } - - if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size { - return error.SignatureError("MDC packet not found") - } - ser.h.Write(ser.trailer[:2]) - - final := ser.h.Sum() - if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 { - return error.SignatureError("hash mismatch") - } - return nil -} - -// An seMDCWriter writes through to an io.WriteCloser while maintains a running -// hash of the data written. On close, it emits an MDC packet containing the -// running hash. -type seMDCWriter struct { - w io.WriteCloser - h hash.Hash -} - -func (w *seMDCWriter) Write(buf []byte) (n int, err os.Error) { - w.h.Write(buf) - return w.w.Write(buf) -} - -func (w *seMDCWriter) Close() (err os.Error) { - var buf [mdcTrailerSize]byte - - buf[0] = mdcPacketTagByte - buf[1] = sha1.Size - w.h.Write(buf[:2]) - digest := w.h.Sum() - copy(buf[2:], digest) - - _, err = w.w.Write(buf[:]) - if err != nil { - return - } - return w.w.Close() -} - -// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. -type noOpCloser struct { - w io.Writer -} - -func (c noOpCloser) Write(data []byte) (n int, err os.Error) { - return c.w.Write(data) -} - -func (c noOpCloser) Close() os.Error { - return nil -} - -// SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet -// to w and returns a WriteCloser to which the to-be-encrypted packets can be -// written. -func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte) (contents io.WriteCloser, err os.Error) { - if c.KeySize() != len(key) { - return nil, error.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") - } - writeCloser := noOpCloser{w} - ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC) - if err != nil { - return - } - - _, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion}) - if err != nil { - return - } - - block := c.new(key) - blockSize := block.BlockSize() - iv := make([]byte, blockSize) - _, err = rand.Reader.Read(iv) - if err != nil { - return - } - s, prefix := cipher.NewOCFBEncrypter(block, iv, cipher.OCFBNoResync) - _, err = ciphertext.Write(prefix) - if err != nil { - return - } - plaintext := cipher.StreamWriter{S: s, W: ciphertext} - - h := sha1.New() - h.Write(iv) - h.Write(iv[blockSize-2:]) - contents = &seMDCWriter{w: plaintext, h: h} - return -} diff --git a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go deleted file mode 100644 index 1054fc2f9..000000000 --- a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.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 packet - -import ( - "bytes" - "crypto/openpgp/error" - "crypto/sha1" - "encoding/hex" - "io" - "io/ioutil" - "os" - "testing" -) - -// TestReader wraps a []byte and returns reads of a specific length. -type testReader struct { - data []byte - stride int -} - -func (t *testReader) Read(buf []byte) (n int, err os.Error) { - n = t.stride - if n > len(t.data) { - n = len(t.data) - } - if n > len(buf) { - n = len(buf) - } - copy(buf, t.data) - t.data = t.data[n:] - if len(t.data) == 0 { - err = os.EOF - } - return -} - -func testMDCReader(t *testing.T) { - mdcPlaintext, _ := hex.DecodeString(mdcPlaintextHex) - - for stride := 1; stride < len(mdcPlaintext)/2; stride++ { - r := &testReader{data: mdcPlaintext, stride: stride} - mdcReader := &seMDCReader{in: r, h: sha1.New()} - body, err := ioutil.ReadAll(mdcReader) - if err != nil { - t.Errorf("stride: %d, error: %s", stride, err) - continue - } - if !bytes.Equal(body, mdcPlaintext[:len(mdcPlaintext)-22]) { - t.Errorf("stride: %d: bad contents %x", stride, body) - continue - } - - err = mdcReader.Close() - if err != nil { - t.Errorf("stride: %d, error on Close: %s", stride, err) - } - } - - mdcPlaintext[15] ^= 80 - - r := &testReader{data: mdcPlaintext, stride: 2} - mdcReader := &seMDCReader{in: r, h: sha1.New()} - _, err := ioutil.ReadAll(mdcReader) - if err != nil { - t.Errorf("corruption test, error: %s", err) - return - } - err = mdcReader.Close() - if err == nil { - t.Error("corruption: no error") - } else if _, ok := err.(*error.SignatureError); !ok { - t.Errorf("corruption: expected SignatureError, got: %s", err) - } -} - -const mdcPlaintextHex = "a302789c3b2d93c4e0eb9aba22283539b3203335af44a134afb800c849cb4c4de10200aff40b45d31432c80cb384299a0655966d6939dfdeed1dddf980" - -func TestSerialize(t *testing.T) { - buf := bytes.NewBuffer(nil) - c := CipherAES128 - key := make([]byte, c.KeySize()) - - w, err := SerializeSymmetricallyEncrypted(buf, c, key) - if err != nil { - t.Errorf("error from SerializeSymmetricallyEncrypted: %s", err) - return - } - - contents := []byte("hello world\n") - - w.Write(contents) - w.Close() - - p, err := Read(buf) - if err != nil { - t.Errorf("error from Read: %s", err) - return - } - - se, ok := p.(*SymmetricallyEncrypted) - if !ok { - t.Errorf("didn't read a *SymmetricallyEncrypted") - return - } - - r, err := se.Decrypt(c, key) - if err != nil { - t.Errorf("error from Decrypt: %s", err) - return - } - - contentsCopy := bytes.NewBuffer(nil) - _, err = io.Copy(contentsCopy, r) - if err != nil { - t.Errorf("error from io.Copy: %s", err) - return - } - if !bytes.Equal(contentsCopy.Bytes(), contents) { - t.Errorf("contents not equal got: %x want: %x", contentsCopy.Bytes(), contents) - } -} diff --git a/src/pkg/crypto/openpgp/packet/userid.go b/src/pkg/crypto/openpgp/packet/userid.go deleted file mode 100644 index 0580ba3ed..000000000 --- a/src/pkg/crypto/openpgp/packet/userid.go +++ /dev/null @@ -1,161 +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 packet - -import ( - "io" - "io/ioutil" - "os" - "strings" -) - -// UserId contains text that is intended to represent the name and email -// address of the key holder. See RFC 4880, section 5.11. By convention, this -// takes the form "Full Name (Comment) " -type UserId struct { - Id string // By convention, this takes the form "Full Name (Comment) " which is split out in the fields below. - - Name, Comment, Email string -} - -func hasInvalidCharacters(s string) bool { - for _, c := range s { - switch c { - case '(', ')', '<', '>', 0: - return true - } - } - return false -} - -// NewUserId returns a UserId or nil if any of the arguments contain invalid -// characters. The invalid characters are '\x00', '(', ')', '<' and '>' -func NewUserId(name, comment, email string) *UserId { - // RFC 4880 doesn't deal with the structure of userid strings; the - // name, comment and email form is just a convention. However, there's - // no convention about escaping the metacharacters and GPG just refuses - // to create user ids where, say, the name contains a '('. We mirror - // this behaviour. - - if hasInvalidCharacters(name) || hasInvalidCharacters(comment) || hasInvalidCharacters(email) { - return nil - } - - uid := new(UserId) - uid.Name, uid.Comment, uid.Email = name, comment, email - uid.Id = name - if len(comment) > 0 { - if len(uid.Id) > 0 { - uid.Id += " " - } - uid.Id += "(" - uid.Id += comment - uid.Id += ")" - } - if len(email) > 0 { - if len(uid.Id) > 0 { - uid.Id += " " - } - uid.Id += "<" - uid.Id += email - uid.Id += ">" - } - return uid -} - -func (uid *UserId) parse(r io.Reader) (err os.Error) { - // RFC 4880, section 5.11 - b, err := ioutil.ReadAll(r) - if err != nil { - return - } - uid.Id = string(b) - uid.Name, uid.Comment, uid.Email = parseUserId(uid.Id) - return -} - -// Serialize marshals uid to w in the form of an OpenPGP packet, including -// header. -func (uid *UserId) Serialize(w io.Writer) os.Error { - err := serializeHeader(w, packetTypeUserId, len(uid.Id)) - if err != nil { - return err - } - _, err = w.Write([]byte(uid.Id)) - return err -} - -// parseUserId extracts the name, comment and email from a user id string that -// is formatted as "Full Name (Comment) ". -func parseUserId(id string) (name, comment, email string) { - var n, c, e struct { - start, end int - } - var state int - - for offset, rune := range id { - switch state { - case 0: - // Entering name - n.start = offset - state = 1 - fallthrough - case 1: - // In name - if rune == '(' { - state = 2 - n.end = offset - } else if rune == '<' { - state = 5 - n.end = offset - } - case 2: - // Entering comment - c.start = offset - state = 3 - fallthrough - case 3: - // In comment - if rune == ')' { - state = 4 - c.end = offset - } - case 4: - // Between comment and email - if rune == '<' { - state = 5 - } - case 5: - // Entering email - e.start = offset - state = 6 - fallthrough - case 6: - // In email - if rune == '>' { - state = 7 - e.end = offset - } - default: - // After email - } - } - switch state { - case 1: - // ended in the name - n.end = len(id) - case 3: - // ended in comment - c.end = len(id) - case 6: - // ended in email - e.end = len(id) - } - - name = strings.TrimSpace(id[n.start:n.end]) - comment = strings.TrimSpace(id[c.start:c.end]) - email = strings.TrimSpace(id[e.start:e.end]) - return -} diff --git a/src/pkg/crypto/openpgp/packet/userid_test.go b/src/pkg/crypto/openpgp/packet/userid_test.go deleted file mode 100644 index 296819389..000000000 --- a/src/pkg/crypto/openpgp/packet/userid_test.go +++ /dev/null @@ -1,87 +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 packet - -import ( - "testing" -) - -var userIdTests = []struct { - id string - name, comment, email string -}{ - {"", "", "", ""}, - {"John Smith", "John Smith", "", ""}, - {"John Smith ()", "John Smith", "", ""}, - {"John Smith () <>", "John Smith", "", ""}, - {"(comment", "", "comment", ""}, - {"(comment)", "", "comment", ""}, - {" sdfk", "", "", "email"}, - {" John Smith ( Comment ) asdkflj < email > lksdfj", "John Smith", "Comment", "email"}, - {" John Smith < email > lksdfj", "John Smith", "", "email"}, - {"("}, - {"foo", "bar", "", "foo (bar)"}, - {"foo", "", "baz", "foo "}, - {"", "bar", "baz", "(bar) "}, - {"foo", "bar", "baz", "foo (bar) "}, -} - -func TestNewUserId(t *testing.T) { - for i, test := range newUserIdTests { - uid := NewUserId(test.name, test.comment, test.email) - if uid == nil { - t.Errorf("#%d: returned nil", i) - continue - } - if uid.Id != test.id { - t.Errorf("#%d: got '%s', want '%s'", i, uid.Id, test.id) - } - } -} - -var invalidNewUserIdTests = []struct { - name, comment, email string -}{ - {"foo(", "", ""}, - {"foo<", "", ""}, - {"", "bar)", ""}, - {"", "bar<", ""}, - {"", "", "baz>"}, - {"", "", "baz)"}, - {"", "", "baz\x00"}, -} - -func TestNewUserIdWithInvalidInput(t *testing.T) { - for i, test := range invalidNewUserIdTests { - if uid := NewUserId(test.name, test.comment, test.email); uid != nil { - t.Errorf("#%d: returned non-nil value: %#v", i, uid) - } - } -} diff --git a/src/pkg/crypto/openpgp/read.go b/src/pkg/crypto/openpgp/read.go deleted file mode 100644 index d95f613c6..000000000 --- a/src/pkg/crypto/openpgp/read.go +++ /dev/null @@ -1,415 +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 openpgp implements high level operations on OpenPGP messages. -package openpgp - -import ( - "crypto" - "crypto/openpgp/armor" - "crypto/openpgp/error" - "crypto/openpgp/packet" - _ "crypto/sha256" - "hash" - "io" - "os" - "strconv" -) - -// SignatureType is the armor type for a PGP signature. -var SignatureType = "PGP SIGNATURE" - -// readArmored reads an armored block with the given type. -func readArmored(r io.Reader, expectedType string) (body io.Reader, err os.Error) { - block, err := armor.Decode(r) - if err != nil { - return - } - - if block.Type != expectedType { - return nil, error.InvalidArgumentError("expected '" + expectedType + "', got: " + block.Type) - } - - return block.Body, nil -} - -// MessageDetails contains the result of parsing an OpenPGP encrypted and/or -// signed message. -type MessageDetails struct { - IsEncrypted bool // true if the message was encrypted. - EncryptedToKeyIds []uint64 // the list of recipient key ids. - IsSymmetricallyEncrypted bool // true if a passphrase could have decrypted the message. - DecryptedWith Key // the private key used to decrypt the message, if any. - IsSigned bool // true if the message is signed. - SignedByKeyId uint64 // the key id of the signer, if any. - SignedBy *Key // the key of the signer, if available. - LiteralData *packet.LiteralData // the metadata of the contents - UnverifiedBody io.Reader // the contents of the message. - - // If IsSigned is true and SignedBy is non-zero then the signature will - // be verified as UnverifiedBody is read. The signature cannot be - // checked until the whole of UnverifiedBody is read so UnverifiedBody - // must be consumed until EOF before the data can trusted. Even if a - // message isn't signed (or the signer is unknown) the data may contain - // an authentication code that is only checked once UnverifiedBody has - // been consumed. Once EOF has been seen, the following fields are - // valid. (An authentication code failure is reported as a - // SignatureError error when reading from UnverifiedBody.) - SignatureError os.Error // nil if the signature is good. - Signature *packet.Signature // the signature packet itself. - - decrypted io.ReadCloser -} - -// A PromptFunction is used as a callback by functions that may need to decrypt -// a private key, or prompt for a passphrase. It is called with a list of -// acceptable, encrypted private keys and a boolean that indicates whether a -// passphrase is usable. It should either decrypt a private key or return a -// passphrase to try. If the decrypted private key or given passphrase isn't -// correct, the function will be called again, forever. Any error returned will -// be passed up. -type PromptFunction func(keys []Key, symmetric bool) ([]byte, os.Error) - -// A keyEnvelopePair is used to store a private key with the envelope that -// contains a symmetric key, encrypted with that key. -type keyEnvelopePair struct { - key Key - encryptedKey *packet.EncryptedKey -} - -// ReadMessage parses an OpenPGP message that may be signed and/or encrypted. -// The given KeyRing should contain both public keys (for signature -// verification) and, possibly encrypted, private keys for decrypting. -func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction) (md *MessageDetails, err os.Error) { - var p packet.Packet - - var symKeys []*packet.SymmetricKeyEncrypted - var pubKeys []keyEnvelopePair - var se *packet.SymmetricallyEncrypted - - packets := packet.NewReader(r) - md = new(MessageDetails) - md.IsEncrypted = true - - // The message, if encrypted, starts with a number of packets - // containing an encrypted decryption key. The decryption key is either - // encrypted to a public key, or with a passphrase. This loop - // collects these packets. -ParsePackets: - for { - p, err = packets.Next() - if err != nil { - return nil, err - } - switch p := p.(type) { - case *packet.SymmetricKeyEncrypted: - // This packet contains the decryption key encrypted with a passphrase. - md.IsSymmetricallyEncrypted = true - symKeys = append(symKeys, p) - case *packet.EncryptedKey: - // This packet contains the decryption key encrypted to a public key. - md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId) - switch p.Algo { - case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal: - break - default: - continue - } - var keys []Key - if p.KeyId == 0 { - keys = keyring.DecryptionKeys() - } else { - keys = keyring.KeysById(p.KeyId) - } - for _, k := range keys { - pubKeys = append(pubKeys, keyEnvelopePair{k, p}) - } - case *packet.SymmetricallyEncrypted: - se = p - break ParsePackets - case *packet.Compressed, *packet.LiteralData, *packet.OnePassSignature: - // This message isn't encrypted. - if len(symKeys) != 0 || len(pubKeys) != 0 { - return nil, error.StructuralError("key material not followed by encrypted message") - } - packets.Unread(p) - return readSignedMessage(packets, nil, keyring) - } - } - - var candidates []Key - var decrypted io.ReadCloser - - // Now that we have the list of encrypted keys we need to decrypt at - // least one of them or, if we cannot, we need to call the prompt - // function so that it can decrypt a key or give us a passphrase. -FindKey: - for { - // See if any of the keys already have a private key available - candidates = candidates[:0] - candidateFingerprints := make(map[string]bool) - - for _, pk := range pubKeys { - if pk.key.PrivateKey == nil { - continue - } - if !pk.key.PrivateKey.Encrypted { - if len(pk.encryptedKey.Key) == 0 { - pk.encryptedKey.Decrypt(pk.key.PrivateKey) - } - if len(pk.encryptedKey.Key) == 0 { - continue - } - decrypted, err = se.Decrypt(pk.encryptedKey.CipherFunc, pk.encryptedKey.Key) - if err != nil && err != error.KeyIncorrectError { - return nil, err - } - if decrypted != nil { - md.DecryptedWith = pk.key - break FindKey - } - } else { - fpr := string(pk.key.PublicKey.Fingerprint[:]) - if v := candidateFingerprints[fpr]; v { - continue - } - candidates = append(candidates, pk.key) - candidateFingerprints[fpr] = true - } - } - - if len(candidates) == 0 && len(symKeys) == 0 { - return nil, error.KeyIncorrectError - } - - if prompt == nil { - return nil, error.KeyIncorrectError - } - - passphrase, err := prompt(candidates, len(symKeys) != 0) - if err != nil { - return nil, err - } - - // Try the symmetric passphrase first - if len(symKeys) != 0 && passphrase != nil { - for _, s := range symKeys { - err = s.Decrypt(passphrase) - if err == nil && !s.Encrypted { - decrypted, err = se.Decrypt(s.CipherFunc, s.Key) - if err != nil && err != error.KeyIncorrectError { - return nil, err - } - if decrypted != nil { - break FindKey - } - } - - } - } - } - - md.decrypted = decrypted - packets.Push(decrypted) - return readSignedMessage(packets, md, keyring) -} - -// readSignedMessage reads a possibly signed message if mdin is non-zero then -// that structure is updated and returned. Otherwise a fresh MessageDetails is -// used. -func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring KeyRing) (md *MessageDetails, err os.Error) { - if mdin == nil { - mdin = new(MessageDetails) - } - md = mdin - - var p packet.Packet - var h hash.Hash - var wrappedHash hash.Hash -FindLiteralData: - for { - p, err = packets.Next() - if err != nil { - return nil, err - } - switch p := p.(type) { - case *packet.Compressed: - packets.Push(p.Body) - case *packet.OnePassSignature: - if !p.IsLast { - return nil, error.UnsupportedError("nested signatures") - } - - h, wrappedHash, err = hashForSignature(p.Hash, p.SigType) - if err != nil { - md = nil - return - } - - md.IsSigned = true - md.SignedByKeyId = p.KeyId - keys := keyring.KeysById(p.KeyId) - for i, key := range keys { - if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign { - continue - } - md.SignedBy = &keys[i] - break - } - case *packet.LiteralData: - md.LiteralData = p - break FindLiteralData - } - } - - if md.SignedBy != nil { - md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md} - } else if md.decrypted != nil { - md.UnverifiedBody = checkReader{md} - } else { - md.UnverifiedBody = md.LiteralData.Body - } - - return md, nil -} - -// hashForSignature returns a pair of hashes that can be used to verify a -// signature. The signature may specify that the contents of the signed message -// should be preprocessed (i.e. to normalize line endings). Thus this function -// returns two hashes. The second should be used to hash the message itself and -// performs any needed preprocessing. -func hashForSignature(hashId crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, os.Error) { - h := hashId.New() - if h == nil { - return nil, nil, error.UnsupportedError("hash not available: " + strconv.Itoa(int(hashId))) - } - - switch sigType { - case packet.SigTypeBinary: - return h, h, nil - case packet.SigTypeText: - return h, NewCanonicalTextHash(h), nil - } - - return nil, nil, error.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType))) -} - -// checkReader wraps an io.Reader from a LiteralData packet. When it sees EOF -// it closes the ReadCloser from any SymmetricallyEncrypted packet to trigger -// MDC checks. -type checkReader struct { - md *MessageDetails -} - -func (cr checkReader) Read(buf []byte) (n int, err os.Error) { - n, err = cr.md.LiteralData.Body.Read(buf) - if err == os.EOF { - mdcErr := cr.md.decrypted.Close() - if mdcErr != nil { - err = mdcErr - } - } - return -} - -// signatureCheckReader wraps an io.Reader from a LiteralData packet and hashes -// the data as it is read. When it sees an EOF from the underlying io.Reader -// it parses and checks a trailing Signature packet and triggers any MDC checks. -type signatureCheckReader struct { - packets *packet.Reader - h, wrappedHash hash.Hash - md *MessageDetails -} - -func (scr *signatureCheckReader) Read(buf []byte) (n int, err os.Error) { - n, err = scr.md.LiteralData.Body.Read(buf) - scr.wrappedHash.Write(buf[:n]) - if err == os.EOF { - var p packet.Packet - p, scr.md.SignatureError = scr.packets.Next() - if scr.md.SignatureError != nil { - return - } - - var ok bool - if scr.md.Signature, ok = p.(*packet.Signature); !ok { - scr.md.SignatureError = error.StructuralError("LiteralData not followed by Signature") - return - } - - scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature) - - // The SymmetricallyEncrypted packet, if any, might have an - // unsigned hash of its own. In order to check this we need to - // close that Reader. - if scr.md.decrypted != nil { - mdcErr := scr.md.decrypted.Close() - if mdcErr != nil { - err = mdcErr - } - } - } - return -} - -// CheckDetachedSignature takes a signed file and a detached signature and -// returns the signer if the signature is valid. If the signer isn't know, -// UnknownIssuerError is returned. -func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err os.Error) { - p, err := packet.Read(signature) - if err != nil { - return - } - - sig, ok := p.(*packet.Signature) - if !ok { - return nil, error.StructuralError("non signature packet found") - } - - if sig.IssuerKeyId == nil { - return nil, error.StructuralError("signature doesn't have an issuer") - } - - keys := keyring.KeysById(*sig.IssuerKeyId) - if len(keys) == 0 { - return nil, error.UnknownIssuerError - } - - h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) - if err != nil { - return - } - - _, err = io.Copy(wrappedHash, signed) - if err != nil && err != os.EOF { - return - } - - for _, key := range keys { - if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign { - continue - } - err = key.PublicKey.VerifySignature(h, sig) - if err == nil { - return key.Entity, nil - } - } - - if err != nil { - return - } - - return nil, error.UnknownIssuerError -} - -// CheckArmoredDetachedSignature performs the same actions as -// CheckDetachedSignature but expects the signature to be armored. -func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err os.Error) { - body, err := readArmored(signature, SignatureType) - if err != nil { - return - } - - return CheckDetachedSignature(keyring, signed, body) -} diff --git a/src/pkg/crypto/openpgp/read_test.go b/src/pkg/crypto/openpgp/read_test.go deleted file mode 100644 index 4dc290ef2..000000000 --- a/src/pkg/crypto/openpgp/read_test.go +++ /dev/null @@ -1,361 +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 openpgp - -import ( - "bytes" - "crypto/openpgp/error" - "encoding/hex" - "io" - "io/ioutil" - "os" - "testing" -) - -func readerFromHex(s string) io.Reader { - data, err := hex.DecodeString(s) - if err != nil { - panic("readerFromHex: bad input") - } - return bytes.NewBuffer(data) -} - -func TestReadKeyRing(t *testing.T) { - kring, err := ReadKeyRing(readerFromHex(testKeys1And2Hex)) - if err != nil { - t.Error(err) - return - } - if len(kring) != 2 || uint32(kring[0].PrimaryKey.KeyId) != 0xC20C31BB || uint32(kring[1].PrimaryKey.KeyId) != 0x1E35246B { - t.Errorf("bad keyring: %#v", kring) - } -} - -func TestRereadKeyRing(t *testing.T) { - kring, err := ReadKeyRing(readerFromHex(testKeys1And2Hex)) - if err != nil { - t.Errorf("error in initial parse: %s", err) - return - } - out := new(bytes.Buffer) - err = kring[0].Serialize(out) - if err != nil { - t.Errorf("error in serialization: %s", err) - return - } - kring, err = ReadKeyRing(out) - if err != nil { - t.Errorf("error in second parse: %s", err) - return - } - - if len(kring) != 1 || uint32(kring[0].PrimaryKey.KeyId) != 0xC20C31BB { - t.Errorf("bad keyring: %#v", kring) - } -} - -func TestReadPrivateKeyRing(t *testing.T) { - kring, err := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) - if err != nil { - t.Error(err) - return - } - if len(kring) != 2 || uint32(kring[0].PrimaryKey.KeyId) != 0xC20C31BB || uint32(kring[1].PrimaryKey.KeyId) != 0x1E35246B || kring[0].PrimaryKey == nil { - t.Errorf("bad keyring: %#v", kring) - } -} - -func TestReadDSAKey(t *testing.T) { - kring, err := ReadKeyRing(readerFromHex(dsaTestKeyHex)) - if err != nil { - t.Error(err) - return - } - if len(kring) != 1 || uint32(kring[0].PrimaryKey.KeyId) != 0x0CCC0360 { - t.Errorf("bad parse: %#v", kring) - } -} - -func TestGetKeyById(t *testing.T) { - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) - - keys := kring.KeysById(0xa34d7e18c20c31bb) - if len(keys) != 1 || keys[0].Entity != kring[0] { - t.Errorf("bad result for 0xa34d7e18c20c31bb: %#v", keys) - } - - keys = kring.KeysById(0xfd94408d4543314f) - if len(keys) != 1 || keys[0].Entity != kring[0] { - t.Errorf("bad result for 0xa34d7e18c20c31bb: %#v", keys) - } -} - -func checkSignedMessage(t *testing.T, signedHex, expected string) { - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) - - md, err := ReadMessage(readerFromHex(signedHex), kring, nil) - if err != nil { - t.Error(err) - return - } - - if !md.IsSigned || md.SignedByKeyId != 0xa34d7e18c20c31bb || md.SignedBy == nil || md.IsEncrypted || md.IsSymmetricallyEncrypted || len(md.EncryptedToKeyIds) != 0 || md.IsSymmetricallyEncrypted { - t.Errorf("bad MessageDetails: %#v", md) - } - - contents, err := ioutil.ReadAll(md.UnverifiedBody) - if err != nil { - t.Errorf("error reading UnverifiedBody: %s", err) - } - if string(contents) != expected { - t.Errorf("bad UnverifiedBody got:%s want:%s", string(contents), expected) - } - if md.SignatureError != nil || md.Signature == nil { - t.Errorf("failed to validate: %s", md.SignatureError) - } -} - -func TestSignedMessage(t *testing.T) { - checkSignedMessage(t, signedMessageHex, signedInput) -} - -func TestTextSignedMessage(t *testing.T) { - checkSignedMessage(t, signedTextMessageHex, signedTextInput) -} - -var signedEncryptedMessageTests = []struct { - keyRingHex string - messageHex string - signedByKeyId uint64 - encryptedToKeyId uint64 -}{ - { - testKeys1And2PrivateHex, - signedEncryptedMessageHex, - 0xa34d7e18c20c31bb, - 0x2a67d68660df41c7, - }, - { - dsaElGamalTestKeysHex, - signedEncryptedMessage2Hex, - 0x33af447ccd759b09, - 0xcf6a7abcd43e3673, - }, -} - -func TestSignedEncryptedMessage(t *testing.T) { - for i, test := range signedEncryptedMessageTests { - expected := "Signed and encrypted message\n" - kring, _ := ReadKeyRing(readerFromHex(test.keyRingHex)) - prompt := func(keys []Key, symmetric bool) ([]byte, os.Error) { - if symmetric { - t.Errorf("prompt: message was marked as symmetrically encrypted") - return nil, error.KeyIncorrectError - } - - if len(keys) == 0 { - t.Error("prompt: no keys requested") - return nil, error.KeyIncorrectError - } - - err := keys[0].PrivateKey.Decrypt([]byte("passphrase")) - if err != nil { - t.Errorf("prompt: error decrypting key: %s", err) - return nil, error.KeyIncorrectError - } - - return nil, nil - } - - md, err := ReadMessage(readerFromHex(test.messageHex), kring, prompt) - if err != nil { - t.Errorf("#%d: error reading message: %s", i, err) - return - } - - if !md.IsSigned || md.SignedByKeyId != test.signedByKeyId || md.SignedBy == nil || !md.IsEncrypted || md.IsSymmetricallyEncrypted || len(md.EncryptedToKeyIds) == 0 || md.EncryptedToKeyIds[0] != test.encryptedToKeyId { - t.Errorf("#%d: bad MessageDetails: %#v", i, md) - } - - contents, err := ioutil.ReadAll(md.UnverifiedBody) - if err != nil { - t.Errorf("#%d: error reading UnverifiedBody: %s", i, err) - } - if string(contents) != expected { - t.Errorf("#%d: bad UnverifiedBody got:%s want:%s", i, string(contents), expected) - } - - if md.SignatureError != nil || md.Signature == nil { - t.Errorf("#%d: failed to validate: %s", i, md.SignatureError) - } - } -} - -func TestUnspecifiedRecipient(t *testing.T) { - expected := "Recipient unspecified\n" - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) - - md, err := ReadMessage(readerFromHex(recipientUnspecifiedHex), kring, nil) - if err != nil { - t.Errorf("error reading message: %s", err) - return - } - - contents, err := ioutil.ReadAll(md.UnverifiedBody) - if err != nil { - t.Errorf("error reading UnverifiedBody: %s", err) - } - if string(contents) != expected { - t.Errorf("bad UnverifiedBody got:%s want:%s", string(contents), expected) - } -} - -func TestSymmetricallyEncrypted(t *testing.T) { - expected := "Symmetrically encrypted.\n" - - prompt := func(keys []Key, symmetric bool) ([]byte, os.Error) { - if len(keys) != 0 { - t.Errorf("prompt: len(keys) = %d (want 0)", len(keys)) - } - - if !symmetric { - t.Errorf("symmetric is not set") - } - - return []byte("password"), nil - } - - md, err := ReadMessage(readerFromHex(symmetricallyEncryptedCompressedHex), nil, prompt) - if err != nil { - t.Errorf("ReadMessage: %s", err) - return - } - - contents, err := ioutil.ReadAll(md.UnverifiedBody) - if err != nil { - t.Errorf("ReadAll: %s", err) - } - - expectedCreationTime := uint32(1295992998) - if md.LiteralData.Time != expectedCreationTime { - t.Errorf("LiteralData.Time is %d, want %d", md.LiteralData.Time, expectedCreationTime) - } - - if string(contents) != expected { - t.Errorf("contents got: %s want: %s", string(contents), expected) - } -} - -func testDetachedSignature(t *testing.T, kring KeyRing, signature io.Reader, sigInput, tag string, expectedSignerKeyId uint64) { - signed := bytes.NewBufferString(sigInput) - signer, err := CheckDetachedSignature(kring, signed, signature) - if err != nil { - t.Errorf("%s: signature error: %s", tag, err) - return - } - if signer == nil { - t.Errorf("%s: signer is nil", tag) - return - } - if signer.PrimaryKey.KeyId != expectedSignerKeyId { - t.Errorf("%s: wrong signer got:%x want:%x", tag, signer.PrimaryKey.KeyId, expectedSignerKeyId) - } -} - -func TestDetachedSignature(t *testing.T) { - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) - testDetachedSignature(t, kring, readerFromHex(detachedSignatureHex), signedInput, "binary", testKey1KeyId) - testDetachedSignature(t, kring, readerFromHex(detachedSignatureTextHex), signedInput, "text", testKey1KeyId) -} - -func TestDetachedSignatureDSA(t *testing.T) { - kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyHex)) - testDetachedSignature(t, kring, readerFromHex(detachedSignatureDSAHex), signedInput, "binary", testKey3KeyId) -} - -func TestReadingArmoredPrivateKey(t *testing.T) { - el, err := ReadArmoredKeyRing(bytes.NewBufferString(armoredPrivateKeyBlock)) - if err != nil { - t.Error(err) - } - if len(el) != 1 { - t.Errorf("got %d entities, wanted 1\n", len(el)) - } -} - -func TestNoArmoredData(t *testing.T) { - _, err := ReadArmoredKeyRing(bytes.NewBufferString("foo")) - if _, ok := err.(error.InvalidArgumentError); !ok { - t.Errorf("error was not an InvalidArgumentError: %s", err) - } -} - -const testKey1KeyId = 0xA34D7E18C20C31BB -const testKey3KeyId = 0x338934250CCC0360 - -const signedInput = "Signed message\nline 2\nline 3\n" -const signedTextInput = "Signed message\r\nline 2\r\nline 3\r\n" - -const recipientUnspecifiedHex = "848c0300000000000000000103ff62d4d578d03cf40c3da998dfe216c074fa6ddec5e31c197c9666ba292830d91d18716a80f699f9d897389a90e6d62d0238f5f07a5248073c0f24920e4bc4a30c2d17ee4e0cae7c3d4aaa4e8dced50e3010a80ee692175fa0385f62ecca4b56ee6e9980aa3ec51b61b077096ac9e800edaf161268593eedb6cc7027ff5cb32745d250010d407a6221ae22ef18469b444f2822478c4d190b24d36371a95cb40087cdd42d9399c3d06a53c0673349bfb607927f20d1e122bde1e2bf3aa6cae6edf489629bcaa0689539ae3b718914d88ededc3b" - -const detachedSignatureHex = "889c04000102000605024d449cd1000a0910a34d7e18c20c31bb167603ff57718d09f28a519fdc7b5a68b6a3336da04df85e38c5cd5d5bd2092fa4629848a33d85b1729402a2aab39c3ac19f9d573f773cc62c264dc924c067a79dfd8a863ae06c7c8686120760749f5fd9b1e03a64d20a7df3446ddc8f0aeadeaeba7cbaee5c1e366d65b6a0c6cc749bcb912d2f15013f812795c2e29eb7f7b77f39ce77" - -const detachedSignatureTextHex = "889c04010102000605024d449d21000a0910a34d7e18c20c31bbc8c60400a24fbef7342603a41cb1165767bd18985d015fb72fe05db42db36cfb2f1d455967f1e491194fbf6cf88146222b23bf6ffbd50d17598d976a0417d3192ff9cc0034fd00f287b02e90418bbefe609484b09231e4e7a5f3562e199bf39909ab5276c4d37382fe088f6b5c3426fc1052865da8b3ab158672d58b6264b10823dc4b39" - -const detachedSignatureDSAHex = "884604001102000605024d6c4eac000a0910338934250ccc0360f18d00a087d743d6405ed7b87755476629600b8b694a39e900a0abff8126f46faf1547c1743c37b21b4ea15b8f83" - -const testKeys1And2Hex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b0020003b88d044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f0011010001889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab0020003988d044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b0020003b88d044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020003" - -const testKeys1And2PrivateHex = "9501d8044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd00110100010003ff4d91393b9a8e3430b14d6209df42f98dc927425b881f1209f319220841273a802a97c7bdb8b3a7740b3ab5866c4d1d308ad0d3a79bd1e883aacf1ac92dfe720285d10d08752a7efe3c609b1d00f17f2805b217be53999a7da7e493bfc3e9618fd17018991b8128aea70a05dbce30e4fbe626aa45775fa255dd9177aabf4df7cf0200c1ded12566e4bc2bb590455e5becfb2e2c9796482270a943343a7835de41080582c2be3caf5981aa838140e97afa40ad652a0b544f83eb1833b0957dce26e47b0200eacd6046741e9ce2ec5beb6fb5e6335457844fb09477f83b050a96be7da043e17f3a9523567ed40e7a521f818813a8b8a72209f1442844843ccc7eb9805442570200bdafe0438d97ac36e773c7162028d65844c4d463e2420aa2228c6e50dc2743c3d6c72d0d782a5173fe7be2169c8a9f4ef8a7cf3e37165e8c61b89c346cdc6c1799d2b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b00200009d01d8044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f00110100010003fd17a7490c22a79c59281fb7b20f5e6553ec0c1637ae382e8adaea295f50241037f8997cf42c1ce26417e015091451b15424b2c59eb8d4161b0975630408e394d3b00f88d4b4e18e2cc85e8251d4753a27c639c83f5ad4a571c4f19d7cd460b9b73c25ade730c99df09637bd173d8e3e981ac64432078263bb6dc30d3e974150dd0200d0ee05be3d4604d2146fb0457f31ba17c057560785aa804e8ca5530a7cd81d3440d0f4ba6851efcfd3954b7e68908fc0ba47f7ac37bf559c6c168b70d3a7c8cd0200da1c677c4bce06a068070f2b3733b0a714e88d62aa3f9a26c6f5216d48d5c2b5624144f3807c0df30be66b3268eeeca4df1fbded58faf49fc95dc3c35f134f8b01fd1396b6c0fc1b6c4f0eb8f5e44b8eace1e6073e20d0b8bc5385f86f1cf3f050f66af789f3ef1fc107b7f4421e19e0349c730c68f0a226981f4e889054fdb4dc149e8e889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab00200009501fe044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001fe030302e9030f3c783e14856063f16938530e148bc57a7aa3f3e4f90df9dceccdc779bc0835e1ad3d006e4a8d7b36d08b8e0de5a0d947254ecfbd22037e6572b426bcfdc517796b224b0036ff90bc574b5509bede85512f2eefb520fb4b02aa523ba739bff424a6fe81c5041f253f8d757e69a503d3563a104d0d49e9e890b9d0c26f96b55b743883b472caa7050c4acfd4a21f875bdf1258d88bd61224d303dc9df77f743137d51e6d5246b88c406780528fd9a3e15bab5452e5b93970d9dcc79f48b38651b9f15bfbcf6da452837e9cc70683d1bdca94507870f743e4ad902005812488dd342f836e72869afd00ce1850eea4cfa53ce10e3608e13d3c149394ee3cbd0e23d018fcbcb6e2ec5a1a22972d1d462ca05355d0d290dd2751e550d5efb38c6c89686344df64852bf4ff86638708f644e8ec6bd4af9b50d8541cb91891a431326ab2e332faa7ae86cfb6e0540aa63160c1e5cdd5a4add518b303fff0a20117c6bc77f7cfbaf36b04c865c6c2b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b00200009d01fe044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001fe030302e9030f3c783e148560f936097339ae381d63116efcf802ff8b1c9360767db5219cc987375702a4123fd8657d3e22700f23f95020d1b261eda5257e9a72f9a918e8ef22dd5b3323ae03bbc1923dd224db988cadc16acc04b120a9f8b7e84da9716c53e0334d7b66586ddb9014df604b41be1e960dcfcbc96f4ed150a1a0dd070b9eb14276b9b6be413a769a75b519a53d3ecc0c220e85cd91ca354d57e7344517e64b43b6e29823cbd87eae26e2b2e78e6dedfbb76e3e9f77bcb844f9a8932eb3db2c3f9e44316e6f5d60e9e2a56e46b72abe6b06dc9a31cc63f10023d1f5e12d2a3ee93b675c96f504af0001220991c88db759e231b3320dcedf814dcf723fd9857e3d72d66a0f2af26950b915abdf56c1596f46a325bf17ad4810d3535fb02a259b247ac3dbd4cc3ecf9c51b6c07cebb009c1506fba0a89321ec8683e3fd009a6e551d50243e2d5092fefb3321083a4bad91320dc624bd6b5dddf93553e3d53924c05bfebec1fb4bd47e89a1a889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020000" - -const dsaElGamalTestKeysHex = "9501e1044dfcb16a110400aa3e5c1a1f43dd28c2ffae8abf5cfce555ee874134d8ba0a0f7b868ce2214beddc74e5e1e21ded354a95d18acdaf69e5e342371a71fbb9093162e0c5f3427de413a7f2c157d83f5cd2f9d791256dc4f6f0e13f13c3302af27f2384075ab3021dff7a050e14854bbde0a1094174855fc02f0bae8e00a340d94a1f22b32e48485700a0cec672ac21258fb95f61de2ce1af74b2c4fa3e6703ff698edc9be22c02ae4d916e4fa223f819d46582c0516235848a77b577ea49018dcd5e9e15cff9dbb4663a1ae6dd7580fa40946d40c05f72814b0f88481207e6c0832c3bded4853ebba0a7e3bd8e8c66df33d5a537cd4acf946d1080e7a3dcea679cb2b11a72a33a2b6a9dc85f466ad2ddf4c3db6283fa645343286971e3dd700703fc0c4e290d45767f370831a90187e74e9972aae5bff488eeff7d620af0362bfb95c1a6c3413ab5d15a2e4139e5d07a54d72583914661ed6a87cce810be28a0aa8879a2dd39e52fb6fe800f4f181ac7e328f740cde3d09a05cecf9483e4cca4253e60d4429ffd679d9996a520012aad119878c941e3cf151459873bdfc2a9563472fe0303027a728f9feb3b864260a1babe83925ce794710cfd642ee4ae0e5b9d74cee49e9c67b6cd0ea5dfbb582132195a121356a1513e1bca73e5b80c58c7ccb4164453412f456c47616d616c2054657374204b65792031886204131102002205024dfcb16a021b03060b090807030206150802090a0b0416020301021e01021780000a091033af447ccd759b09fadd00a0b8fd6f5a790bad7e9f2dbb7632046dc4493588db009c087c6a9ba9f7f49fab221587a74788c00db4889ab00200009d0157044dfcb16a1004008dec3f9291205255ccff8c532318133a6840739dd68b03ba942676f9038612071447bf07d00d559c5c0875724ea16a4c774f80d8338b55fca691a0522e530e604215b467bbc9ccfd483a1da99d7bc2648b4318fdbd27766fc8bfad3fddb37c62b8ae7ccfe9577e9b8d1e77c1d417ed2c2ef02d52f4da11600d85d3229607943700030503ff506c94c87c8cab778e963b76cf63770f0a79bf48fb49d3b4e52234620fc9f7657f9f8d56c96a2b7c7826ae6b57ebb2221a3fe154b03b6637cea7e6d98e3e45d87cf8dc432f723d3d71f89c5192ac8d7290684d2c25ce55846a80c9a7823f6acd9bb29fa6cd71f20bc90eccfca20451d0c976e460e672b000df49466408d527affe0303027a728f9feb3b864260abd761730327bca2aaa4ea0525c175e92bf240682a0e83b226f97ecb2e935b62c9a133858ce31b271fa8eb41f6a1b3cd72a63025ce1a75ee4180dcc284884904181102000905024dfcb16a021b0c000a091033af447ccd759b09dd0b009e3c3e7296092c81bee5a19929462caaf2fff3ae26009e218c437a2340e7ea628149af1ec98ec091a43992b00200009501e1044dfcb1be1104009f61faa61aa43df75d128cbe53de528c4aec49ce9360c992e70c77072ad5623de0a3a6212771b66b39a30dad6781799e92608316900518ec01184a85d872365b7d2ba4bacfb5882ea3c2473d3750dc6178cc1cf82147fb58caa28b28e9f12f6d1efcb0534abed644156c91cca4ab78834268495160b2400bc422beb37d237c2300a0cac94911b6d493bda1e1fbc6feeca7cb7421d34b03fe22cec6ccb39675bb7b94a335c2b7be888fd3906a1125f33301d8aa6ec6ee6878f46f73961c8d57a3e9544d8ef2a2cbfd4d52da665b1266928cfe4cb347a58c412815f3b2d2369dec04b41ac9a71cc9547426d5ab941cccf3b18575637ccfb42df1a802df3cfe0a999f9e7109331170e3a221991bf868543960f8c816c28097e503fe319db10fb98049f3a57d7c80c420da66d56f3644371631fad3f0ff4040a19a4fedc2d07727a1b27576f75a4d28c47d8246f27071e12d7a8de62aad216ddbae6aa02efd6b8a3e2818cda48526549791ab277e447b3a36c57cefe9b592f5eab73959743fcc8e83cbefec03a329b55018b53eec196765ae40ef9e20521a603c551efe0303020950d53a146bf9c66034d00c23130cce95576a2ff78016ca471276e8227fb30b1ffbd92e61804fb0c3eff9e30b1a826ee8f3e4730b4d86273ca977b4164453412f456c47616d616c2054657374204b65792032886204131102002205024dfcb1be021b03060b090807030206150802090a0b0416020301021e01021780000a0910a86bf526325b21b22bd9009e34511620415c974750a20df5cb56b182f3b48e6600a0a9466cb1a1305a84953445f77d461593f1d42bc1b00200009d0157044dfcb1be1004009565a951da1ee87119d600c077198f1c1bceb0f7aa54552489298e41ff788fa8f0d43a69871f0f6f77ebdfb14a4260cf9fbeb65d5844b4272a1904dd95136d06c3da745dc46327dd44a0f16f60135914368c8039a34033862261806bb2c5ce1152e2840254697872c85441ccb7321431d75a747a4bfb1d2c66362b51ce76311700030503fc0ea76601c196768070b7365a200e6ddb09307f262d5f39eec467b5f5784e22abdf1aa49226f59ab37cb49969d8f5230ea65caf56015abda62604544ed526c5c522bf92bed178a078789f6c807b6d34885688024a5bed9e9f8c58d11d4b82487b44c5f470c5606806a0443b79cadb45e0f897a561a53f724e5349b9267c75ca17fe0303020950d53a146bf9c660bc5f4ce8f072465e2d2466434320c1e712272fafc20e342fe7608101580fa1a1a367e60486a7cd1246b7ef5586cf5e10b32762b710a30144f12dd17dd4884904181102000905024dfcb1be021b0c000a0910a86bf526325b21b2904c00a0b2b66b4b39ccffda1d10f3ea8d58f827e30a8b8e009f4255b2d8112a184e40cde43a34e8655ca7809370b0020000" - -const signedMessageHex = "a3019bc0cbccc0c4b8d8b74ee2108fe16ec6d3ca490cbe362d3f8333d3f352531472538b8b13d353b97232f352158c20943157c71c16064626063656269052062e4e01987e9b6fccff4b7df3a34c534b23e679cbec3bc0f8f6e64dfb4b55fe3f8efa9ce110ddb5cd79faf1d753c51aecfa669f7e7aa043436596cccc3359cb7dd6bbe9ecaa69e5989d9e57209571edc0b2fa7f57b9b79a64ee6e99ce1371395fee92fec2796f7b15a77c386ff668ee27f6d38f0baa6c438b561657377bf6acff3c5947befd7bf4c196252f1d6e5c524d0300" - -const signedTextMessageHex = "a3019bc0cbccc8c4b8d8b74ee2108fe16ec6d36a250cbece0c178233d3f352531472538b8b13d35379b97232f352158ca0b4312f57c71c1646462606365626906a062e4e019811591798ff99bf8afee860b0d8a8c2a85c3387e3bcf0bb3b17987f2bbcfab2aa526d930cbfd3d98757184df3995c9f3e7790e36e3e9779f06089d4c64e9e47dd6202cb6e9bc73c5d11bb59fbaf89d22d8dc7cf199ddf17af96e77c5f65f9bbed56f427bd8db7af37f6c9984bf9385efaf5f184f986fb3e6adb0ecfe35bbf92d16a7aa2a344fb0bc52fb7624f0200" - -const signedEncryptedMessageHex = "848c032a67d68660df41c70103ff5789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8d2c03b018bd210b1d3791e1aba74b0f1034e122ab72e760492c192383cf5e20b5628bd043272d63df9b923f147eb6091cd897553204832aba48fec54aa447547bb16305a1024713b90e77fd0065f1918271947549205af3c74891af22ee0b56cd29bfec6d6e351901cd4ab3ece7c486f1e32a792d4e474aed98ee84b3f591c7dff37b64e0ecd68fd036d517e412dcadf85840ce184ad7921ad446c4ee28db80447aea1ca8d4f574db4d4e37688158ddd19e14ee2eab4873d46947d65d14a23e788d912cf9a19624ca7352469b72a83866b7c23cb5ace3deab3c7018061b0ba0f39ed2befe27163e5083cf9b8271e3e3d52cc7ad6e2a3bd81d4c3d7022f8d" - -const signedEncryptedMessage2Hex = "85010e03cf6a7abcd43e36731003fb057f5495b79db367e277cdbe4ab90d924ddee0c0381494112ff8c1238fb0184af35d1731573b01bc4c55ecacd2aafbe2003d36310487d1ecc9ac994f3fada7f9f7f5c3a64248ab7782906c82c6ff1303b69a84d9a9529c31ecafbcdb9ba87e05439897d87e8a2a3dec55e14df19bba7f7bd316291c002ae2efd24f83f9e3441203fc081c0c23dc3092a454ca8a082b27f631abf73aca341686982e8fbda7e0e7d863941d68f3de4a755c2964407f4b5e0477b3196b8c93d551dd23c8beef7d0f03fbb1b6066f78907faf4bf1677d8fcec72651124080e0b7feae6b476e72ab207d38d90b958759fdedfc3c6c35717c9dbfc979b3cfbbff0a76d24a5e57056bb88acbd2a901ef64bc6e4db02adc05b6250ff378de81dca18c1910ab257dff1b9771b85bb9bbe0a69f5989e6d1710a35e6dfcceb7d8fb5ccea8db3932b3d9ff3fe0d327597c68b3622aec8e3716c83a6c93f497543b459b58ba504ed6bcaa747d37d2ca746fe49ae0a6ce4a8b694234e941b5159ff8bd34b9023da2814076163b86f40eed7c9472f81b551452d5ab87004a373c0172ec87ea6ce42ccfa7dbdad66b745496c4873d8019e8c28d6b3" - -const symmetricallyEncryptedCompressedHex = "8c0d04030302eb4a03808145d0d260c92f714339e13de5a79881216431925bf67ee2898ea61815f07894cd0703c50d0a76ef64d482196f47a8bc729af9b80bb6" - -const dsaTestKeyHex = "9901a2044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794" - -const dsaTestKeyPrivateHex = "9501bb044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4d00009f592e0619d823953577d4503061706843317e4fee083db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794" - -const armoredPrivateKeyBlock = `-----BEGIN PGP PRIVATE KEY BLOCK----- -Version: GnuPG v1.4.10 (GNU/Linux) - -lQHYBE2rFNoBBADFwqWQIW/DSqcB4yCQqnAFTJ27qS5AnB46ccAdw3u4Greeu3Bp -idpoHdjULy7zSKlwR1EA873dO/k/e11Ml3dlAFUinWeejWaK2ugFP6JjiieSsrKn -vWNicdCS4HTWn0X4sjl0ZiAygw6GNhqEQ3cpLeL0g8E9hnYzJKQ0LWJa0QARAQAB -AAP/TB81EIo2VYNmTq0pK1ZXwUpxCrvAAIG3hwKjEzHcbQznsjNvPUihZ+NZQ6+X -0HCfPAdPkGDCLCb6NavcSW+iNnLTrdDnSI6+3BbIONqWWdRDYJhqZCkqmG6zqSfL -IdkJgCw94taUg5BWP/AAeQrhzjChvpMQTVKQL5mnuZbUCeMCAN5qrYMP2S9iKdnk -VANIFj7656ARKt/nf4CBzxcpHTyB8+d2CtPDKCmlJP6vL8t58Jmih+kHJMvC0dzn -gr5f5+sCAOOe5gt9e0am7AvQWhdbHVfJU0TQJx+m2OiCJAqGTB1nvtBLHdJnfdC9 -TnXXQ6ZXibqLyBies/xeY2sCKL5qtTMCAKnX9+9d/5yQxRyrQUHt1NYhaXZnJbHx -q4ytu0eWz+5i68IYUSK69jJ1NWPM0T6SkqpB3KCAIv68VFm9PxqG1KmhSrQIVGVz -dCBLZXmIuAQTAQIAIgUCTasU2gIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA -CgkQO9o98PRieSoLhgQAkLEZex02Qt7vGhZzMwuN0R22w3VwyYyjBx+fM3JFETy1 -ut4xcLJoJfIaF5ZS38UplgakHG0FQ+b49i8dMij0aZmDqGxrew1m4kBfjXw9B/v+ -eIqpODryb6cOSwyQFH0lQkXC040pjq9YqDsO5w0WYNXYKDnzRV0p4H1pweo2VDid -AdgETasU2gEEAN46UPeWRqKHvA99arOxee38fBt2CI08iiWyI8T3J6ivtFGixSqV -bRcPxYO/qLpVe5l84Nb3X71GfVXlc9hyv7CD6tcowL59hg1E/DC5ydI8K8iEpUmK -/UnHdIY5h8/kqgGxkY/T/hgp5fRQgW1ZoZxLajVlMRZ8W4tFtT0DeA+JABEBAAEA -A/0bE1jaaZKj6ndqcw86jd+QtD1SF+Cf21CWRNeLKnUds4FRRvclzTyUMuWPkUeX -TaNNsUOFqBsf6QQ2oHUBBK4VCHffHCW4ZEX2cd6umz7mpHW6XzN4DECEzOVksXtc -lUC1j4UB91DC/RNQqwX1IV2QLSwssVotPMPqhOi0ZLNY7wIA3n7DWKInxYZZ4K+6 -rQ+POsz6brEoRHwr8x6XlHenq1Oki855pSa1yXIARoTrSJkBtn5oI+f8AzrnN0BN -oyeQAwIA/7E++3HDi5aweWrViiul9cd3rcsS0dEnksPhvS0ozCJiHsq/6GFmy7J8 -QSHZPteedBnZyNp5jR+H7cIfVN3KgwH/Skq4PsuPhDq5TKK6i8Pc1WW8MA6DXTdU -nLkX7RGmMwjC0DBf7KWAlPjFaONAX3a8ndnz//fy1q7u2l9AZwrj1qa1iJ8EGAEC -AAkFAk2rFNoCGwwACgkQO9o98PRieSo2/QP/WTzr4ioINVsvN1akKuekmEMI3LAp -BfHwatufxxP1U+3Si/6YIk7kuPB9Hs+pRqCXzbvPRrI8NHZBmc8qIGthishdCYad -AHcVnXjtxrULkQFGbGvhKURLvS9WnzD/m1K2zzwxzkPTzT9/Yf06O6Mal5AdugPL -VrM0m72/jnpKo04= -=zNCn ------END PGP PRIVATE KEY BLOCK-----` diff --git a/src/pkg/crypto/openpgp/s2k/Makefile b/src/pkg/crypto/openpgp/s2k/Makefile deleted file mode 100644 index 731d53431..000000000 --- a/src/pkg/crypto/openpgp/s2k/Makefile +++ /dev/null @@ -1,11 +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. - -include ../../../../Make.inc - -TARG=crypto/openpgp/s2k -GOFILES=\ - s2k.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/s2k/s2k.go b/src/pkg/crypto/openpgp/s2k/s2k.go deleted file mode 100644 index da926a76e..000000000 --- a/src/pkg/crypto/openpgp/s2k/s2k.go +++ /dev/null @@ -1,180 +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 s2k implements the various OpenPGP string-to-key transforms as -// specified in RFC 4800 section 3.7.1. -package s2k - -import ( - "crypto" - "crypto/openpgp/error" - "hash" - "io" - "os" - "strconv" -) - -// Simple writes to out the result of computing the Simple S2K function (RFC -// 4880, section 3.7.1.1) using the given hash and input passphrase. -func Simple(out []byte, h hash.Hash, in []byte) { - Salted(out, h, in, nil) -} - -var zero [1]byte - -// Salted writes to out the result of computing the Salted S2K function (RFC -// 4880, section 3.7.1.2) using the given hash, input passphrase and salt. -func Salted(out []byte, h hash.Hash, in []byte, salt []byte) { - done := 0 - - for i := 0; done < len(out); i++ { - h.Reset() - for j := 0; j < i; j++ { - h.Write(zero[:]) - } - h.Write(salt) - h.Write(in) - n := copy(out[done:], h.Sum()) - done += n - } -} - -// Iterated writes to out the result of computing the Iterated and Salted S2K -// function (RFC 4880, section 3.7.1.3) using the given hash, input passphrase, -// salt and iteration count. -func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) { - combined := make([]byte, len(in)+len(salt)) - copy(combined, salt) - copy(combined[len(salt):], in) - - if count < len(combined) { - count = len(combined) - } - - done := 0 - for i := 0; done < len(out); i++ { - h.Reset() - for j := 0; j < i; j++ { - h.Write(zero[:]) - } - written := 0 - for written < count { - if written+len(combined) > count { - todo := count - written - h.Write(combined[:todo]) - written = count - } else { - h.Write(combined) - written += len(combined) - } - } - n := copy(out[done:], h.Sum()) - done += n - } -} - -// Parse reads a binary specification for a string-to-key transformation from r -// and returns a function which performs that transform. -func Parse(r io.Reader) (f func(out, in []byte), err os.Error) { - var buf [9]byte - - _, err = io.ReadFull(r, buf[:2]) - if err != nil { - return - } - - hash, ok := HashIdToHash(buf[1]) - if !ok { - return nil, error.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(buf[1]))) - } - h := hash.New() - if h == nil { - return nil, error.UnsupportedError("hash not available: " + strconv.Itoa(int(hash))) - } - - switch buf[0] { - case 1: - f := func(out, in []byte) { - Simple(out, h, in) - } - return f, nil - case 2: - _, err := io.ReadFull(r, buf[:8]) - if err != nil { - return - } - f := func(out, in []byte) { - Salted(out, h, in, buf[:8]) - } - return f, nil - case 3: - _, err := io.ReadFull(r, buf[:9]) - if err != nil { - return - } - count := (16 + int(buf[8]&15)) << (uint32(buf[8]>>4) + 6) - f := func(out, in []byte) { - Iterated(out, h, in, buf[:8], count) - } - return f, nil - } - - return nil, error.UnsupportedError("S2K function") -} - -// Serialize salts and stretches the given passphrase and writes the resulting -// key into key. It also serializes an S2K descriptor to w. -func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte) os.Error { - var buf [11]byte - buf[0] = 3 /* iterated and salted */ - buf[1], _ = HashToHashId(crypto.SHA1) - salt := buf[2:10] - if _, err := io.ReadFull(rand, salt); err != nil { - return err - } - const count = 65536 // this is the default in gpg - buf[10] = 96 // 65536 iterations - if _, err := w.Write(buf[:]); err != nil { - return err - } - - Iterated(key, crypto.SHA1.New(), passphrase, salt, count) - return nil -} - -// hashToHashIdMapping contains pairs relating OpenPGP's hash identifier with -// Go's crypto.Hash type. See RFC 4880, section 9.4. -var hashToHashIdMapping = []struct { - id byte - hash crypto.Hash -}{ - {1, crypto.MD5}, - {2, crypto.SHA1}, - {3, crypto.RIPEMD160}, - {8, crypto.SHA256}, - {9, crypto.SHA384}, - {10, crypto.SHA512}, - {11, crypto.SHA224}, -} - -// HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP -// hash id. -func HashIdToHash(id byte) (h crypto.Hash, ok bool) { - for _, m := range hashToHashIdMapping { - if m.id == id { - return m.hash, true - } - } - return 0, false -} - -// HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. -func HashToHashId(h crypto.Hash) (id byte, ok bool) { - for _, m := range hashToHashIdMapping { - if m.hash == h { - return m.id, true - } - } - return 0, false -} diff --git a/src/pkg/crypto/openpgp/s2k/s2k_test.go b/src/pkg/crypto/openpgp/s2k/s2k_test.go deleted file mode 100644 index ec4012c23..000000000 --- a/src/pkg/crypto/openpgp/s2k/s2k_test.go +++ /dev/null @@ -1,118 +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 s2k - -import ( - "bytes" - "crypto/sha1" - "crypto/rand" - "encoding/hex" - "testing" -) - -var saltedTests = []struct { - in, out string -}{ - {"hello", "10295ac1"}, - {"world", "ac587a5e"}, - {"foo", "4dda8077"}, - {"bar", "bd8aac6b9ea9cae04eae6a91c6133b58b5d9a61c14f355516ed9370456"}, - {"x", "f1d3f289"}, - {"xxxxxxxxxxxxxxxxxxxxxxx", "e00d7b45"}, -} - -func TestSalted(t *testing.T) { - h := sha1.New() - salt := [4]byte{1, 2, 3, 4} - - for i, test := range saltedTests { - expected, _ := hex.DecodeString(test.out) - out := make([]byte, len(expected)) - Salted(out, h, []byte(test.in), salt[:]) - if !bytes.Equal(expected, out) { - t.Errorf("#%d, got: %x want: %x", i, out, expected) - } - } -} - -var iteratedTests = []struct { - in, out string -}{ - {"hello", "83126105"}, - {"world", "6fa317f9"}, - {"foo", "8fbc35b9"}, - {"bar", "2af5a99b54f093789fd657f19bd245af7604d0f6ae06f66602a46a08ae"}, - {"x", "5a684dfe"}, - {"xxxxxxxxxxxxxxxxxxxxxxx", "18955174"}, -} - -func TestIterated(t *testing.T) { - h := sha1.New() - salt := [4]byte{4, 3, 2, 1} - - for i, test := range iteratedTests { - expected, _ := hex.DecodeString(test.out) - out := make([]byte, len(expected)) - Iterated(out, h, []byte(test.in), salt[:], 31) - if !bytes.Equal(expected, out) { - t.Errorf("#%d, got: %x want: %x", i, out, expected) - } - } -} - -var parseTests = []struct { - spec, in, out string -}{ - /* Simple with SHA1 */ - {"0102", "hello", "aaf4c61d"}, - /* Salted with SHA1 */ - {"02020102030405060708", "hello", "f4f7d67e"}, - /* Iterated with SHA1 */ - {"03020102030405060708f1", "hello", "f2a57b7c"}, -} - -func TestParse(t *testing.T) { - for i, test := range parseTests { - spec, _ := hex.DecodeString(test.spec) - buf := bytes.NewBuffer(spec) - f, err := Parse(buf) - if err != nil { - t.Errorf("%d: Parse returned error: %s", i, err) - continue - } - - expected, _ := hex.DecodeString(test.out) - out := make([]byte, len(expected)) - f(out, []byte(test.in)) - if !bytes.Equal(out, expected) { - t.Errorf("%d: output got: %x want: %x", i, out, expected) - } - if testing.Short() { - break - } - } -} - -func TestSerialize(t *testing.T) { - buf := bytes.NewBuffer(nil) - key := make([]byte, 16) - passphrase := []byte("testing") - err := Serialize(buf, key, rand.Reader, passphrase) - if err != nil { - t.Errorf("failed to serialize: %s", err) - return - } - - f, err := Parse(buf) - if err != nil { - t.Errorf("failed to reparse: %s", err) - return - } - key2 := make([]byte, len(key)) - f(key2, passphrase) - if !bytes.Equal(key2, key) { - t.Errorf("keys don't match: %x (serialied) vs %x (parsed)", key, key2) - } -} diff --git a/src/pkg/crypto/openpgp/write.go b/src/pkg/crypto/openpgp/write.go deleted file mode 100644 index 9884472ce..000000000 --- a/src/pkg/crypto/openpgp/write.go +++ /dev/null @@ -1,308 +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 openpgp - -import ( - "crypto" - "crypto/openpgp/armor" - "crypto/openpgp/error" - "crypto/openpgp/packet" - "crypto/openpgp/s2k" - "crypto/rand" - _ "crypto/sha256" - "hash" - "io" - "os" - "strconv" - "time" -) - -// DetachSign signs message with the private key from signer (which must -// already have been decrypted) and writes the signature to w. -func DetachSign(w io.Writer, signer *Entity, message io.Reader) os.Error { - return detachSign(w, signer, message, packet.SigTypeBinary) -} - -// ArmoredDetachSign signs message with the private key from signer (which -// must already have been decrypted) and writes an armored signature to w. -func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader) (err os.Error) { - return armoredDetachSign(w, signer, message, packet.SigTypeBinary) -} - -// DetachSignText signs message (after canonicalising the line endings) with -// the private key from signer (which must already have been decrypted) and -// writes the signature to w. -func DetachSignText(w io.Writer, signer *Entity, message io.Reader) os.Error { - return detachSign(w, signer, message, packet.SigTypeText) -} - -// ArmoredDetachSignText signs message (after canonicalising the line endings) -// with the private key from signer (which must already have been decrypted) -// and writes an armored signature to w. -func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader) os.Error { - return armoredDetachSign(w, signer, message, packet.SigTypeText) -} - -func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType) (err os.Error) { - out, err := armor.Encode(w, SignatureType, nil) - if err != nil { - return - } - err = detachSign(out, signer, message, sigType) - if err != nil { - return - } - return out.Close() -} - -func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType) (err os.Error) { - if signer.PrivateKey == nil { - return error.InvalidArgumentError("signing key doesn't have a private key") - } - if signer.PrivateKey.Encrypted { - return error.InvalidArgumentError("signing key is encrypted") - } - - sig := new(packet.Signature) - sig.SigType = sigType - sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo - sig.Hash = crypto.SHA256 - sig.CreationTime = uint32(time.Seconds()) - sig.IssuerKeyId = &signer.PrivateKey.KeyId - - h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) - if err != nil { - return - } - io.Copy(wrappedHash, message) - - err = sig.Sign(h, signer.PrivateKey) - if err != nil { - return - } - - return sig.Serialize(w) -} - -// FileHints contains metadata about encrypted files. This metadata is, itself, -// encrypted. -type FileHints struct { - // IsBinary can be set to hint that the contents are binary data. - IsBinary bool - // FileName hints at the name of the file that should be written. It's - // truncated to 255 bytes if longer. It may be empty to suggest that the - // file should not be written to disk. It may be equal to "_CONSOLE" to - // suggest the data should not be written to disk. - FileName string - // EpochSeconds contains the modification time of the file, or 0 if not applicable. - EpochSeconds uint32 -} - -// SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. -// The resulting WriteCloser must be closed after the contents of the file have -// been written. -func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints) (plaintext io.WriteCloser, err os.Error) { - if hints == nil { - hints = &FileHints{} - } - - key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, rand.Reader, passphrase, packet.CipherAES128) - if err != nil { - return - } - w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, packet.CipherAES128, key) - if err != nil { - return - } - return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds) -} - -// intersectPreferences mutates and returns a prefix of a that contains only -// the values in the intersection of a and b. The order of a is preserved. -func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) { - var j int - for _, v := range a { - for _, v2 := range b { - if v == v2 { - a[j] = v - j++ - break - } - } - } - - return a[:j] -} - -func hashToHashId(h crypto.Hash) uint8 { - v, ok := s2k.HashToHashId(h) - if !ok { - panic("tried to convert unknown hash") - } - return v -} - -// Encrypt encrypts a message to a number of recipients and, optionally, signs -// it. hints contains optional information, that is also encrypted, that aids -// the recipients in processing the message. The resulting WriteCloser must -// be closed after the contents of the file have been written. -func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints) (plaintext io.WriteCloser, err os.Error) { - var signer *packet.PrivateKey - if signed != nil { - signer = signed.signingKey().PrivateKey - if signer == nil || signer.Encrypted { - return nil, error.InvalidArgumentError("signing key must be decrypted") - } - } - - // These are the possible ciphers that we'll use for the message. - candidateCiphers := []uint8{ - uint8(packet.CipherAES128), - uint8(packet.CipherAES256), - uint8(packet.CipherCAST5), - } - // These are the possible hash functions that we'll use for the signature. - candidateHashes := []uint8{ - hashToHashId(crypto.SHA256), - hashToHashId(crypto.SHA512), - hashToHashId(crypto.SHA1), - hashToHashId(crypto.RIPEMD160), - } - // In the event that a recipient doesn't specify any supported ciphers - // or hash functions, these are the ones that we assume that every - // implementation supports. - defaultCiphers := candidateCiphers[len(candidateCiphers)-1:] - defaultHashes := candidateHashes[len(candidateHashes)-1:] - - encryptKeys := make([]Key, len(to)) - for i := range to { - encryptKeys[i] = to[i].encryptionKey() - if encryptKeys[i].PublicKey == nil { - return nil, error.InvalidArgumentError("cannot encrypt a message to key id " + strconv.Uitob64(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") - } - - sig := to[i].primaryIdentity().SelfSignature - - preferredSymmetric := sig.PreferredSymmetric - if len(preferredSymmetric) == 0 { - preferredSymmetric = defaultCiphers - } - preferredHashes := sig.PreferredHash - if len(preferredHashes) == 0 { - preferredHashes = defaultHashes - } - candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) - candidateHashes = intersectPreferences(candidateHashes, preferredHashes) - } - - if len(candidateCiphers) == 0 || len(candidateHashes) == 0 { - return nil, error.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") - } - - cipher := packet.CipherFunction(candidateCiphers[0]) - hash, _ := s2k.HashIdToHash(candidateHashes[0]) - symKey := make([]byte, cipher.KeySize()) - if _, err := io.ReadFull(rand.Reader, symKey); err != nil { - return nil, err - } - - for _, key := range encryptKeys { - if err := packet.SerializeEncryptedKey(ciphertext, rand.Reader, key.PublicKey, cipher, symKey); err != nil { - return nil, err - } - } - - encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey) - if err != nil { - return - } - - if signer != nil { - ops := &packet.OnePassSignature{ - SigType: packet.SigTypeBinary, - Hash: hash, - PubKeyAlgo: signer.PubKeyAlgo, - KeyId: signer.KeyId, - IsLast: true, - } - if err := ops.Serialize(encryptedData); err != nil { - return nil, err - } - } - - if hints == nil { - hints = &FileHints{} - } - - w := encryptedData - if signer != nil { - // If we need to write a signature packet after the literal - // data then we need to stop literalData from closing - // encryptedData. - w = noOpCloser{encryptedData} - - } - literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds) - if err != nil { - return nil, err - } - - if signer != nil { - return signatureWriter{encryptedData, literalData, hash, hash.New(), signer}, nil - } - return literalData, nil -} - -// signatureWriter hashes the contents of a message while passing it along to -// literalData. When closed, it closes literalData, writes a signature packet -// to encryptedData and then also closes encryptedData. -type signatureWriter struct { - encryptedData io.WriteCloser - literalData io.WriteCloser - hashType crypto.Hash - h hash.Hash - signer *packet.PrivateKey -} - -func (s signatureWriter) Write(data []byte) (int, os.Error) { - s.h.Write(data) - return s.literalData.Write(data) -} - -func (s signatureWriter) Close() os.Error { - sig := &packet.Signature{ - SigType: packet.SigTypeBinary, - PubKeyAlgo: s.signer.PubKeyAlgo, - Hash: s.hashType, - CreationTime: uint32(time.Seconds()), - IssuerKeyId: &s.signer.KeyId, - } - - if err := sig.Sign(s.h, s.signer); err != nil { - return err - } - if err := s.literalData.Close(); err != nil { - return err - } - if err := sig.Serialize(s.encryptedData); err != nil { - return err - } - return s.encryptedData.Close() -} - -// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. -// TODO: we have two of these in OpenPGP packages alone. This probably needs -// to be promoted somewhere more common. -type noOpCloser struct { - w io.Writer -} - -func (c noOpCloser) Write(data []byte) (n int, err os.Error) { - return c.w.Write(data) -} - -func (c noOpCloser) Close() os.Error { - return nil -} diff --git a/src/pkg/crypto/openpgp/write_test.go b/src/pkg/crypto/openpgp/write_test.go deleted file mode 100644 index c542dfa45..000000000 --- a/src/pkg/crypto/openpgp/write_test.go +++ /dev/null @@ -1,233 +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 openpgp - -import ( - "bytes" - "crypto/rand" - "os" - "io" - "io/ioutil" - "testing" - "time" -) - -func TestSignDetached(t *testing.T) { - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) - out := bytes.NewBuffer(nil) - message := bytes.NewBufferString(signedInput) - err := DetachSign(out, kring[0], message) - if err != nil { - t.Error(err) - } - - testDetachedSignature(t, kring, out, signedInput, "check", testKey1KeyId) -} - -func TestSignTextDetached(t *testing.T) { - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) - out := bytes.NewBuffer(nil) - message := bytes.NewBufferString(signedInput) - err := DetachSignText(out, kring[0], message) - if err != nil { - t.Error(err) - } - - testDetachedSignature(t, kring, out, signedInput, "check", testKey1KeyId) -} - -func TestSignDetachedDSA(t *testing.T) { - kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyPrivateHex)) - out := bytes.NewBuffer(nil) - message := bytes.NewBufferString(signedInput) - err := DetachSign(out, kring[0], message) - if err != nil { - t.Error(err) - } - - testDetachedSignature(t, kring, out, signedInput, "check", testKey3KeyId) -} - -func TestNewEntity(t *testing.T) { - if testing.Short() { - return - } - - e, err := NewEntity(rand.Reader, time.Seconds(), "Test User", "test", "test@example.com") - if err != nil { - t.Errorf("failed to create entity: %s", err) - return - } - - w := bytes.NewBuffer(nil) - if err := e.SerializePrivate(w); err != nil { - t.Errorf("failed to serialize entity: %s", err) - return - } - serialized := w.Bytes() - - el, err := ReadKeyRing(w) - if err != nil { - t.Errorf("failed to reparse entity: %s", err) - return - } - - if len(el) != 1 { - t.Errorf("wrong number of entities found, got %d, want 1", len(el)) - } - - w = bytes.NewBuffer(nil) - if err := e.SerializePrivate(w); err != nil { - t.Errorf("failed to serialize entity second time: %s", err) - return - } - - if !bytes.Equal(w.Bytes(), serialized) { - t.Errorf("results differed") - } -} - -func TestSymmetricEncryption(t *testing.T) { - buf := new(bytes.Buffer) - plaintext, err := SymmetricallyEncrypt(buf, []byte("testing"), nil) - if err != nil { - t.Errorf("error writing headers: %s", err) - return - } - message := []byte("hello world\n") - _, err = plaintext.Write(message) - if err != nil { - t.Errorf("error writing to plaintext writer: %s", err) - } - err = plaintext.Close() - if err != nil { - t.Errorf("error closing plaintext writer: %s", err) - } - - md, err := ReadMessage(buf, nil, func(keys []Key, symmetric bool) ([]byte, os.Error) { - return []byte("testing"), nil - }) - if err != nil { - t.Errorf("error rereading message: %s", err) - } - messageBuf := bytes.NewBuffer(nil) - _, err = io.Copy(messageBuf, md.UnverifiedBody) - if err != nil { - t.Errorf("error rereading message: %s", err) - } - if !bytes.Equal(message, messageBuf.Bytes()) { - t.Errorf("recovered message incorrect got '%s', want '%s'", messageBuf.Bytes(), message) - } -} - -var testEncryptionTests = []struct { - keyRingHex string - isSigned bool -}{ - { - testKeys1And2PrivateHex, - false, - }, - { - testKeys1And2PrivateHex, - true, - }, - { - dsaElGamalTestKeysHex, - false, - }, - { - dsaElGamalTestKeysHex, - true, - }, -} - -func TestEncryption(t *testing.T) { - for i, test := range testEncryptionTests { - kring, _ := ReadKeyRing(readerFromHex(test.keyRingHex)) - - passphrase := []byte("passphrase") - for _, entity := range kring { - if entity.PrivateKey != nil && entity.PrivateKey.Encrypted { - err := entity.PrivateKey.Decrypt(passphrase) - if err != nil { - t.Errorf("#%d: failed to decrypt key", i) - } - } - for _, subkey := range entity.Subkeys { - if subkey.PrivateKey != nil && subkey.PrivateKey.Encrypted { - err := subkey.PrivateKey.Decrypt(passphrase) - if err != nil { - t.Errorf("#%d: failed to decrypt subkey", i) - } - } - } - } - - var signed *Entity - if test.isSigned { - signed = kring[0] - } - - buf := new(bytes.Buffer) - w, err := Encrypt(buf, kring[:1], signed, nil /* no hints */ ) - if err != nil { - t.Errorf("#%d: error in Encrypt: %s", i, err) - continue - } - - const message = "testing" - _, err = w.Write([]byte(message)) - if err != nil { - t.Errorf("#%d: error writing plaintext: %s", i, err) - continue - } - err = w.Close() - if err != nil { - t.Errorf("#%d: error closing WriteCloser: %s", i, err) - continue - } - - md, err := ReadMessage(buf, kring, nil /* no prompt */ ) - if err != nil { - t.Errorf("#%d: error reading message: %s", i, err) - continue - } - - if test.isSigned { - expectedKeyId := kring[0].signingKey().PublicKey.KeyId - if md.SignedByKeyId != expectedKeyId { - t.Errorf("#%d: message signed by wrong key id, got: %d, want: %d", i, *md.SignedBy, expectedKeyId) - } - if md.SignedBy == nil { - t.Errorf("#%d: failed to find the signing Entity", i) - } - } - - plaintext, err := ioutil.ReadAll(md.UnverifiedBody) - if err != nil { - t.Errorf("#%d: error reading encrypted contents: %s", i, err) - continue - } - - expectedKeyId := kring[0].encryptionKey().PublicKey.KeyId - if len(md.EncryptedToKeyIds) != 1 || md.EncryptedToKeyIds[0] != expectedKeyId { - t.Errorf("#%d: expected message to be encrypted to %v, but got %#v", i, expectedKeyId, md.EncryptedToKeyIds) - } - - if string(plaintext) != message { - t.Errorf("#%d: got: %s, want: %s", i, string(plaintext), message) - } - - if test.isSigned { - if md.SignatureError != nil { - t.Errorf("#%d: signature error: %s", i, err) - } - if md.Signature == nil { - t.Error("signature missing") - } - } - } -} diff --git a/src/pkg/crypto/rand/Makefile b/src/pkg/crypto/rand/Makefile deleted file mode 100644 index d1a3d45e8..000000000 --- a/src/pkg/crypto/rand/Makefile +++ /dev/null @@ -1,30 +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. - -include ../../../Make.inc - -TARG=crypto/rand - -GOFILES=\ - rand.go\ - util.go\ - -GOFILES_freebsd=\ - rand_unix.go\ - -GOFILES_darwin=\ - rand_unix.go\ - -GOFILES_linux=\ - rand_unix.go\ - -GOFILES_openbsd=\ - rand_unix.go\ - -GOFILES_windows=\ - rand_windows.go\ - -GOFILES+=$(GOFILES_$(GOOS)) - -include ../../../Make.pkg diff --git a/src/pkg/crypto/rand/rand.go b/src/pkg/crypto/rand/rand.go index 42d9da0ef..59759038e 100644 --- a/src/pkg/crypto/rand/rand.go +++ b/src/pkg/crypto/rand/rand.go @@ -6,10 +6,7 @@ // pseudorandom number generator. package rand -import ( - "io" - "os" -) +import "io" // Reader is a global, shared instance of a cryptographically // strong pseudo-random generator. @@ -18,4 +15,4 @@ import ( var Reader io.Reader // Read is a helper function that calls Reader.Read. -func Read(b []byte) (n int, err os.Error) { return Reader.Read(b) } +func Read(b []byte) (n int, err error) { return Reader.Read(b) } diff --git a/src/pkg/crypto/rand/rand_test.go b/src/pkg/crypto/rand/rand_test.go index bfae7ce4f..be3a5a221 100644 --- a/src/pkg/crypto/rand/rand_test.go +++ b/src/pkg/crypto/rand/rand_test.go @@ -22,7 +22,7 @@ func TestRead(t *testing.T) { } var z bytes.Buffer - f := flate.NewWriter(&z, 5) + f, _ := flate.NewWriter(&z, 5) f.Write(b) f.Close() if z.Len() < len(b)*99/100 { diff --git a/src/pkg/crypto/rand/rand_unix.go b/src/pkg/crypto/rand/rand_unix.go index 3a06aa8b1..5eb4cda2b 100644 --- a/src/pkg/crypto/rand/rand_unix.go +++ b/src/pkg/crypto/rand/rand_unix.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 darwin freebsd linux netbsd openbsd + // Unix cryptographically secure pseudorandom number // generator. @@ -10,6 +12,7 @@ package rand import ( "bufio" "crypto/aes" + "crypto/cipher" "io" "os" "sync" @@ -28,7 +31,7 @@ type devReader struct { mu sync.Mutex } -func (r *devReader) Read(b []byte) (n int, err os.Error) { +func (r *devReader) Read(b []byte) (n int, err error) { r.mu.Lock() defer r.mu.Unlock() if r.f == nil { @@ -64,12 +67,12 @@ func newReader(entropy io.Reader) io.Reader { type reader struct { mu sync.Mutex budget int // number of bytes that can be generated - cipher *aes.Cipher + cipher cipher.Block entropy io.Reader time, seed, dst, key [aes.BlockSize]byte } -func (r *reader) Read(b []byte) (n int, err os.Error) { +func (r *reader) Read(b []byte) (n int, err error) { r.mu.Lock() defer r.mu.Unlock() n = len(b) @@ -98,7 +101,7 @@ func (r *reader) Read(b []byte) (n int, err os.Error) { // t = encrypt(time) // dst = encrypt(t^seed) // seed = encrypt(t^dst) - ns := time.Nanoseconds() + ns := time.Now().UnixNano() r.time[0] = byte(ns >> 56) r.time[1] = byte(ns >> 48) r.time[2] = byte(ns >> 40) diff --git a/src/pkg/crypto/rand/rand_windows.go b/src/pkg/crypto/rand/rand_windows.go old mode 100755 new mode 100644 index 0eab6b213..2b2bd4bba --- a/src/pkg/crypto/rand/rand_windows.go +++ b/src/pkg/crypto/rand/rand_windows.go @@ -23,21 +23,21 @@ type rngReader struct { mu sync.Mutex } -func (r *rngReader) Read(b []byte) (n int, err os.Error) { +func (r *rngReader) Read(b []byte) (n int, err error) { r.mu.Lock() if r.prov == 0 { const provType = syscall.PROV_RSA_FULL const flags = syscall.CRYPT_VERIFYCONTEXT | syscall.CRYPT_SILENT - errno := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags) - if errno != 0 { + err := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags) + if err != nil { r.mu.Unlock() - return 0, os.NewSyscallError("CryptAcquireContext", errno) + return 0, os.NewSyscallError("CryptAcquireContext", err) } } r.mu.Unlock() - errno := syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) - if errno != 0 { - return 0, os.NewSyscallError("CryptGenRandom", errno) + err = syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) + if err != nil { + return 0, os.NewSyscallError("CryptGenRandom", err) } return len(b), nil } diff --git a/src/pkg/crypto/rand/util.go b/src/pkg/crypto/rand/util.go index 77028476e..5391c1829 100644 --- a/src/pkg/crypto/rand/util.go +++ b/src/pkg/crypto/rand/util.go @@ -5,16 +5,16 @@ package rand import ( - "big" + "errors" "io" - "os" + "math/big" ) // 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 os.Error) { +func Prime(rand io.Reader, bits int) (p *big.Int, err error) { if bits < 1 { - err = os.EINVAL + err = errors.New("crypto/rand: prime size must be positive") } b := uint(bits % 8) @@ -39,7 +39,7 @@ func Prime(rand io.Reader, bits int) (p *big.Int, err os.Error) { bytes[len(bytes)-1] |= 1 p.SetBytes(bytes) - if big.ProbablyPrime(p, 20) { + if p.ProbablyPrime(20) { return } } @@ -48,7 +48,7 @@ func Prime(rand io.Reader, bits int) (p *big.Int, err os.Error) { } // Int returns a uniform random value in [0, max). -func Int(rand io.Reader, max *big.Int) (n *big.Int, err os.Error) { +func Int(rand io.Reader, max *big.Int) (n *big.Int, err error) { k := (max.BitLen() + 7) / 8 // b is the number of bits in the most significant byte of max. diff --git a/src/pkg/crypto/rc4/Makefile b/src/pkg/crypto/rc4/Makefile deleted file mode 100644 index 50a3b7972..000000000 --- a/src/pkg/crypto/rc4/Makefile +++ /dev/null @@ -1,11 +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 ../../../Make.inc - -TARG=crypto/rc4 -GOFILES=\ - rc4.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/rc4/rc4.go b/src/pkg/crypto/rc4/rc4.go index 7ee471093..1bb278f74 100644 --- a/src/pkg/crypto/rc4/rc4.go +++ b/src/pkg/crypto/rc4/rc4.go @@ -9,10 +9,7 @@ package rc4 // BUG(agl): RC4 is in common use but has design weaknesses that make // it a poor choice for new protocols. -import ( - "os" - "strconv" -) +import "strconv" // A Cipher is an instance of RC4 using a particular key. type Cipher struct { @@ -22,13 +19,13 @@ type Cipher struct { type KeySizeError int -func (k KeySizeError) String() string { +func (k KeySizeError) Error() string { return "crypto/rc4: invalid key size " + strconv.Itoa(int(k)) } // NewCipher creates and returns a new Cipher. The key argument should be the // RC4 key, at least 1 byte and at most 256 bytes. -func NewCipher(key []byte) (*Cipher, os.Error) { +func NewCipher(key []byte) (*Cipher, error) { k := len(key) if k < 1 || k > 256 { return nil, KeySizeError(k) diff --git a/src/pkg/crypto/ripemd160/Makefile b/src/pkg/crypto/ripemd160/Makefile deleted file mode 100644 index 7e529457d..000000000 --- a/src/pkg/crypto/ripemd160/Makefile +++ /dev/null @@ -1,12 +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. - -include ../../../Make.inc - -TARG=crypto/ripemd160 -GOFILES=\ - ripemd160.go\ - ripemd160block.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/ripemd160/ripemd160.go b/src/pkg/crypto/ripemd160/ripemd160.go deleted file mode 100644 index 5aaca59a3..000000000 --- a/src/pkg/crypto/ripemd160/ripemd160.go +++ /dev/null @@ -1,118 +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. - -// Package ripemd160 implements the RIPEMD-160 hash algorithm. -package ripemd160 - -// RIPEMD-160 is designed by by Hans Dobbertin, Antoon Bosselaers, and Bart -// Preneel with specifications available at: -// http://homes.esat.kuleuven.be/~cosicart/pdf/AB-9601/AB-9601.pdf. - -import ( - "crypto" - "hash" - "os" -) - -func init() { - crypto.RegisterHash(crypto.RIPEMD160, New) -} - -// The size of the checksum in bytes. -const Size = 20 - -// The block size of the hash algorithm in bytes. -const BlockSize = 64 - -const ( - _s0 = 0x67452301 - _s1 = 0xefcdab89 - _s2 = 0x98badcfe - _s3 = 0x10325476 - _s4 = 0xc3d2e1f0 -) - -// digest represents the partial evaluation of a checksum. -type digest struct { - s [5]uint32 // running context - x [BlockSize]byte // temporary buffer - nx int // index into x - tc uint64 // total count of bytes processed -} - -func (d *digest) Reset() { - d.s[0], d.s[1], d.s[2], d.s[3], d.s[4] = _s0, _s1, _s2, _s3, _s4 - d.nx = 0 - d.tc = 0 -} - -// New returns a new hash.Hash computing the checksum. -func New() hash.Hash { - result := new(digest) - result.Reset() - return result -} - -func (d *digest) Size() int { return Size } - -func (d *digest) Write(p []byte) (nn int, err os.Error) { - nn = len(p) - d.tc += uint64(nn) - if d.nx > 0 { - n := len(p) - if n > BlockSize-d.nx { - n = BlockSize - d.nx - } - for i := 0; i < n; i++ { - d.x[d.nx+i] = p[i] - } - d.nx += n - if d.nx == BlockSize { - _Block(d, d.x[0:]) - d.nx = 0 - } - p = p[n:] - } - n := _Block(d, p) - p = p[n:] - if len(p) > 0 { - d.nx = copy(d.x[:], p) - } - return -} - -func (d0 *digest) Sum() []byte { - // Make a copy of d0 so that caller can keep writing and summing. - d := new(digest) - *d = *d0 - - // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. - tc := d.tc - var tmp [64]byte - tmp[0] = 0x80 - if tc%64 < 56 { - d.Write(tmp[0 : 56-tc%64]) - } else { - d.Write(tmp[0 : 64+56-tc%64]) - } - - // Length in bits. - tc <<= 3 - for i := uint(0); i < 8; i++ { - tmp[i] = byte(tc >> (8 * i)) - } - d.Write(tmp[0:8]) - - if d.nx != 0 { - panic("d.nx != 0") - } - - p := make([]byte, 20) - j := 0 - for _, s := range d.s { - p[j], p[j+1], p[j+2], p[j+3] = byte(s), byte(s>>8), byte(s>>16), byte(s>>24) - j += 4 - } - return p -} diff --git a/src/pkg/crypto/ripemd160/ripemd160_test.go b/src/pkg/crypto/ripemd160/ripemd160_test.go deleted file mode 100644 index f4135f5cf..000000000 --- a/src/pkg/crypto/ripemd160/ripemd160_test.go +++ /dev/null @@ -1,64 +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. - -package ripemd160 - -// Test vectors are from: -// http://homes.esat.kuleuven.be/~bosselae/ripemd160.html - -import ( - "fmt" - "io" - "testing" -) - -type mdTest struct { - out string - in string -} - -var vectors = [...]mdTest{ - {"9c1185a5c5e9fc54612808977ee8f548b2258d31", ""}, - {"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", "a"}, - {"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", "abc"}, - {"5d0689ef49d2fae572b881b123a85ffa21595f36", "message digest"}, - {"f71c27109c692c1b56bbdceb5b9d2865b3708dbc", "abcdefghijklmnopqrstuvwxyz"}, - {"12a053384a9c0c88e405a06c27dcf49ada62eb2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - {"b0e20b6e3116640286ed3a87a5713079b21f5189", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - {"9b752e45573d4b39f4dbd3323cab82bf63326bfb", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, -} - -func TestVectors(t *testing.T) { - for i := 0; i < len(vectors); i++ { - tv := vectors[i] - md := New() - for j := 0; j < 3; j++ { - if j < 2 { - io.WriteString(md, tv.in) - } else { - io.WriteString(md, tv.in[0:len(tv.in)/2]) - md.Sum() - io.WriteString(md, tv.in[len(tv.in)/2:]) - } - s := fmt.Sprintf("%x", md.Sum()) - if s != tv.out { - t.Fatalf("RIPEMD-160[%d](%s) = %s, expected %s", j, tv.in, s, tv.out) - } - md.Reset() - } - } -} - -func TestMillionA(t *testing.T) { - md := New() - for i := 0; i < 100000; i++ { - io.WriteString(md, "aaaaaaaaaa") - } - out := "52783243c1697bdbe16d37f97f68f08325dc1528" - s := fmt.Sprintf("%x", md.Sum()) - if s != out { - t.Fatalf("RIPEMD-160 (1 million 'a') = %s, expected %s", s, out) - } - md.Reset() -} diff --git a/src/pkg/crypto/ripemd160/ripemd160block.go b/src/pkg/crypto/ripemd160/ripemd160block.go deleted file mode 100644 index 7bc8e6c48..000000000 --- a/src/pkg/crypto/ripemd160/ripemd160block.go +++ /dev/null @@ -1,161 +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. - -// RIPEMD-160 block step. -// In its own file so that a faster assembly or C version -// can be substituted easily. - -package ripemd160 - -// work buffer indices and roll amounts for one line -var _n = [80]uint{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, - 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, - 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, - 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13, -} - -var _r = [80]uint{ - 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, - 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, - 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, - 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, - 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6, -} - -// same for the other parallel one -var n_ = [80]uint{ - 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, - 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, - 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, - 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, - 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11, -} - -var r_ = [80]uint{ - 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, - 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, - 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, - 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, - 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11, -} - -func _Block(md *digest, p []byte) int { - n := 0 - var x [16]uint32 - var alpha, beta uint32 - for len(p) >= BlockSize { - a, b, c, d, e := md.s[0], md.s[1], md.s[2], md.s[3], md.s[4] - aa, bb, cc, dd, ee := a, b, c, d, e - 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 - } - - // round 1 - i := 0 - for i < 16 { - alpha = a + (b ^ c ^ d) + x[_n[i]] - s := _r[i] - alpha = (alpha<>(32-s)) + e - beta = c<<10 | c>>22 - a, b, c, d, e = e, alpha, b, beta, d - - // parallel line - alpha = aa + (bb ^ (cc | ^dd)) + x[n_[i]] + 0x50a28be6 - s = r_[i] - alpha = (alpha<>(32-s)) + ee - beta = cc<<10 | cc>>22 - aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd - - i++ - } - - // round 2 - for i < 32 { - alpha = a + (b&c | ^b&d) + x[_n[i]] + 0x5a827999 - s := _r[i] - alpha = (alpha<>(32-s)) + e - beta = c<<10 | c>>22 - a, b, c, d, e = e, alpha, b, beta, d - - // parallel line - alpha = aa + (bb&dd | cc&^dd) + x[n_[i]] + 0x5c4dd124 - s = r_[i] - alpha = (alpha<>(32-s)) + ee - beta = cc<<10 | cc>>22 - aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd - - i++ - } - - // round 3 - for i < 48 { - alpha = a + (b | ^c ^ d) + x[_n[i]] + 0x6ed9eba1 - s := _r[i] - alpha = (alpha<>(32-s)) + e - beta = c<<10 | c>>22 - a, b, c, d, e = e, alpha, b, beta, d - - // parallel line - alpha = aa + (bb | ^cc ^ dd) + x[n_[i]] + 0x6d703ef3 - s = r_[i] - alpha = (alpha<>(32-s)) + ee - beta = cc<<10 | cc>>22 - aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd - - i++ - } - - // round 4 - for i < 64 { - alpha = a + (b&d | c&^d) + x[_n[i]] + 0x8f1bbcdc - s := _r[i] - alpha = (alpha<>(32-s)) + e - beta = c<<10 | c>>22 - a, b, c, d, e = e, alpha, b, beta, d - - // parallel line - alpha = aa + (bb&cc | ^bb&dd) + x[n_[i]] + 0x7a6d76e9 - s = r_[i] - alpha = (alpha<>(32-s)) + ee - beta = cc<<10 | cc>>22 - aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd - - i++ - } - - // round 5 - for i < 80 { - alpha = a + (b ^ (c | ^d)) + x[_n[i]] + 0xa953fd4e - s := _r[i] - alpha = (alpha<>(32-s)) + e - beta = c<<10 | c>>22 - a, b, c, d, e = e, alpha, b, beta, d - - // parallel line - alpha = aa + (bb ^ cc ^ dd) + x[n_[i]] - s = r_[i] - alpha = (alpha<>(32-s)) + ee - beta = cc<<10 | cc>>22 - aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd - - i++ - } - - // combine results - dd += c + md.s[1] - md.s[1] = md.s[2] + d + ee - md.s[2] = md.s[3] + e + aa - md.s[3] = md.s[4] + a + bb - md.s[4] = md.s[0] + b + cc - md.s[0] = dd - - p = p[BlockSize:] - n += BlockSize - } - return n -} diff --git a/src/pkg/crypto/rsa/Makefile b/src/pkg/crypto/rsa/Makefile deleted file mode 100644 index ff26ca6f2..000000000 --- a/src/pkg/crypto/rsa/Makefile +++ /dev/null @@ -1,12 +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 ../../../Make.inc - -TARG=crypto/rsa -GOFILES=\ - rsa.go\ - pkcs1v15.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go index 600623114..254f4a3da 100644 --- a/src/pkg/crypto/rsa/pkcs1v15.go +++ b/src/pkg/crypto/rsa/pkcs1v15.go @@ -5,11 +5,11 @@ package rsa import ( - "big" "crypto" "crypto/subtle" + "errors" "io" - "os" + "math/big" ) // This file implements encryption and decryption using PKCS#1 v1.5 padding. @@ -18,10 +18,10 @@ import ( // The message must be no longer than the length of the public modulus minus 11 bytes. // 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 os.Error) { +func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, err error) { k := (pub.N.BitLen() + 7) / 8 if len(msg) > k-11 { - err = MessageTooLongError{} + err = ErrMessageTooLong return } @@ -44,10 +44,10 @@ func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, er // 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 os.Error) { +func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out []byte, err error) { valid, out, err := decryptPKCS1v15(rand, priv, ciphertext) if err == nil && valid == 0 { - err = DecryptionError{} + err = ErrDecryption } return @@ -65,11 +65,11 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out [ // about the plaintext. // See ``Chosen Ciphertext Attacks Against Protocols Based on the RSA // Encryption Standard PKCS #1'', Daniel Bleichenbacher, Advances in Cryptology -// (Crypto '98), -func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) (err os.Error) { +// (Crypto '98). +func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) (err error) { k := (priv.N.BitLen() + 7) / 8 if k-(len(key)+3+8) < 0 { - err = DecryptionError{} + err = ErrDecryption return } @@ -83,10 +83,10 @@ func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []by return } -func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, msg []byte, err os.Error) { +func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, msg []byte, err error) { k := (priv.N.BitLen() + 7) / 8 if k < 11 { - err = DecryptionError{} + err = ErrDecryption return } @@ -119,7 +119,7 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid } // nonZeroRandomBytes fills the given slice with non-zero random octets. -func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) { +func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) { _, err = io.ReadFull(rand, s) if err != nil { return @@ -161,7 +161,7 @@ var hashPrefixes = map[crypto.Hash][]byte{ // SignPKCS1v15 calculates the signature of hashed using RSASSA-PKCS1-V1_5-SIGN from RSA PKCS#1 v1.5. // Note that hashed must be the result of hashing the input message using the // given hash function. -func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) (s []byte, err os.Error) { +func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) (s []byte, err error) { hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed)) if err != nil { return @@ -170,7 +170,7 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b tLen := len(prefix) + hashLen k := (priv.N.BitLen() + 7) / 8 if k < tLen+11 { - return nil, MessageTooLongError{} + return nil, ErrMessageTooLong } // EM = 0x00 || 0x01 || PS || 0x00 || T @@ -194,7 +194,7 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b // hashed is the result of hashing the input message using the given hash // function and sig is the signature. A valid signature is indicated by // returning a nil error. -func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) (err os.Error) { +func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) (err error) { hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed)) if err != nil { return @@ -203,7 +203,7 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) tLen := len(prefix) + hashLen k := (pub.N.BitLen() + 7) / 8 if k < tLen+11 { - err = VerificationError{} + err = ErrVerification return } @@ -223,20 +223,20 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) } if ok != 1 { - return VerificationError{} + return ErrVerification } return nil } -func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err os.Error) { +func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err error) { hashLen = hash.Size() if inLen != hashLen { - return 0, nil, os.NewError("input must be hashed message") + return 0, nil, errors.New("input must be hashed message") } prefix, ok := hashPrefixes[hash] if !ok { - return 0, nil, os.NewError("unsupported hash function") + return 0, nil, errors.New("unsupported hash function") } return } diff --git a/src/pkg/crypto/rsa/pkcs1v15_test.go b/src/pkg/crypto/rsa/pkcs1v15_test.go index d69bacfd6..58d5fda19 100644 --- a/src/pkg/crypto/rsa/pkcs1v15_test.go +++ b/src/pkg/crypto/rsa/pkcs1v15_test.go @@ -5,7 +5,6 @@ package rsa import ( - "big" "bytes" "crypto" "crypto/rand" @@ -13,6 +12,7 @@ import ( "encoding/base64" "encoding/hex" "io" + "math/big" "testing" "testing/quick" ) @@ -168,7 +168,7 @@ func TestSignPKCS1v15(t *testing.T) { for i, test := range signPKCS1v15Tests { h := sha1.New() h.Write([]byte(test.in)) - digest := h.Sum() + digest := h.Sum(nil) s, err := SignPKCS1v15(nil, rsaPrivateKey, crypto.SHA1, digest) if err != nil { @@ -186,7 +186,7 @@ func TestVerifyPKCS1v15(t *testing.T) { for i, test := range signPKCS1v15Tests { h := sha1.New() h.Write([]byte(test.in)) - digest := h.Sum() + digest := h.Sum(nil) sig, _ := hex.DecodeString(test.out) diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index 6957659f2..ec77e6869 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -8,12 +8,12 @@ package rsa // TODO(agl): Add support for PSS padding. import ( - "big" "crypto/rand" "crypto/subtle" + "errors" "hash" "io" - "os" + "math/big" ) var bigZero = big.NewInt(0) @@ -55,16 +55,15 @@ type CRTValue struct { } // Validate performs basic sanity checks on the key. -// It returns nil if the key is valid, or else an os.Error describing a problem. - -func (priv *PrivateKey) Validate() os.Error { +// It returns nil if the key is valid, or else an error describing a problem. +func (priv *PrivateKey) Validate() error { // 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 !big.ProbablyPrime(prime, 20) { - return os.NewError("prime factor is composite") + if !prime.ProbablyPrime(20) { + return errors.New("prime factor is composite") } } @@ -74,7 +73,7 @@ func (priv *PrivateKey) Validate() os.Error { modulus.Mul(modulus, prime) } if modulus.Cmp(priv.N) != 0 { - return os.NewError("invalid modulus") + return errors.New("invalid modulus") } // Check that e and totient(Πprimes) are coprime. totient := new(big.Int).Set(bigOne) @@ -86,21 +85,21 @@ func (priv *PrivateKey) Validate() os.Error { gcd := new(big.Int) x := new(big.Int) y := new(big.Int) - big.GcdInt(gcd, x, y, totient, e) + gcd.GCD(x, y, totient, e) if gcd.Cmp(bigOne) != 0 { - return os.NewError("invalid public exponent E") + 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 os.NewError("invalid private exponent D") + return errors.New("invalid private exponent D") } return nil } // GenerateKey generates an RSA keypair of the given bit size. -func GenerateKey(random io.Reader, bits int) (priv *PrivateKey, err os.Error) { +func GenerateKey(random io.Reader, bits int) (priv *PrivateKey, err error) { return GenerateMultiPrimeKey(random, 2, bits) } @@ -114,20 +113,12 @@ func GenerateKey(random io.Reader, bits int) (priv *PrivateKey, err os.Error) { // // [1] US patent 4405829 (1972, expired) // [2] http://www.cacr.math.uwaterloo.ca/techreports/2006/cacr2006-16.pdf -func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *PrivateKey, err os.Error) { +func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *PrivateKey, err error) { priv = new(PrivateKey) - // Smaller public exponents lead to faster public key - // operations. Since the exponent must be coprime to - // (p-1)(q-1), the smallest possible value is 3. Some have - // suggested that a larger exponent (often 2**16+1) be used - // since previous implementation bugs[1] were avoided when this - // was the case. However, there are no current reasons not to use - // small exponents. - // [1] http://marc.info/?l=cryptography&m=115694833312008&w=2 - priv.E = 3 + priv.E = 65537 if nprimes < 2 { - return nil, os.NewError("rsa.GenerateMultiPrimeKey: nprimes must be >= 2") + return nil, errors.New("rsa.GenerateMultiPrimeKey: nprimes must be >= 2") } primes := make([]*big.Int, nprimes) @@ -165,7 +156,7 @@ NextSetOfPrimes: priv.D = new(big.Int) y := new(big.Int) e := big.NewInt(int64(priv.E)) - big.GcdInt(g, priv.D, y, e, totient) + g.GCD(priv.D, y, e, totient) if g.Cmp(bigOne) == 0 { priv.D.Add(priv.D, totient) @@ -198,12 +189,13 @@ func incCounter(c *[4]byte) { // specified in PKCS#1 v2.1. func mgf1XOR(out []byte, hash hash.Hash, seed []byte) { var counter [4]byte + var digest []byte done := 0 for done < len(out) { hash.Write(seed) hash.Write(counter[0:4]) - digest := hash.Sum() + digest = hash.Sum(digest[:0]) hash.Reset() for i := 0; i < len(digest) && done < len(out); i++ { @@ -214,13 +206,9 @@ func mgf1XOR(out []byte, hash hash.Hash, seed []byte) { } } -// MessageTooLongError is returned when attempting to encrypt a message which -// is too large for the size of the public key. -type MessageTooLongError struct{} - -func (MessageTooLongError) String() string { - return "message too long for RSA public key size" -} +// ErrMessageTooLong is returned when attempting to encrypt a message which is +// too large for the size of the public key. +var ErrMessageTooLong = errors.New("crypto/rsa: message too long for RSA public key size") func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int { e := big.NewInt(int64(pub.E)) @@ -231,16 +219,16 @@ func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int { // EncryptOAEP encrypts the given message with RSA-OAEP. // 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 os.Error) { +func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) (out []byte, err error) { hash.Reset() k := (pub.N.BitLen() + 7) / 8 if len(msg) > k-2*hash.Size()-2 { - err = MessageTooLongError{} + err = ErrMessageTooLong return } hash.Write(label) - lHash := hash.Sum() + lHash := hash.Sum(nil) hash.Reset() em := make([]byte, k) @@ -274,17 +262,13 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l return } -// A DecryptionError represents a failure to decrypt a message. +// ErrDecryption represents a failure to decrypt a message. // It is deliberately vague to avoid adaptive attacks. -type DecryptionError struct{} +var ErrDecryption = errors.New("crypto/rsa: decryption error") -func (DecryptionError) String() string { return "RSA decryption error" } - -// A VerificationError represents a failure to verify a signature. +// ErrVerification represents a failure to verify a signature. // It is deliberately vague to avoid adaptive attacks. -type VerificationError struct{} - -func (VerificationError) String() string { return "RSA verification error" } +var ErrVerification = errors.New("crypto/rsa: verification error") // modInverse returns ia, the inverse of a in the multiplicative group of prime // order n. It requires that a be a member of the group (i.e. less than n). @@ -292,7 +276,7 @@ func modInverse(a, n *big.Int) (ia *big.Int, ok bool) { g := new(big.Int) x := new(big.Int) y := new(big.Int) - big.GcdInt(g, x, y, a, n) + g.GCD(x, y, a, n) if g.Cmp(bigOne) != 0 { // In this case, a and n aren't coprime and we cannot calculate // the inverse. This happens because the values of n are nearly @@ -343,10 +327,10 @@ func (priv *PrivateKey) Precompute() { // decrypt performs an RSA decryption, resulting in a plaintext integer. If a // random source is given, RSA blinding is used. -func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os.Error) { +func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err error) { // TODO(agl): can we get away with reusing blinds? if c.Cmp(priv.N) > 0 { - err = DecryptionError{} + err = ErrDecryption return } @@ -420,12 +404,12 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os } // DecryptOAEP decrypts ciphertext using RSA-OAEP. -// If rand != 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 os.Error) { +// 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) { k := (priv.N.BitLen() + 7) / 8 if len(ciphertext) > k || k < hash.Size()*2+2 { - err = DecryptionError{} + err = ErrDecryption return } @@ -437,7 +421,7 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext } hash.Write(label) - lHash := hash.Sum() + lHash := hash.Sum(nil) hash.Reset() // Converting the plaintext number to bytes will strip any @@ -481,7 +465,7 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext } if firstByteIsZero&lHash2Good&^invalid&^lookingForIndex != 1 { - err = DecryptionError{} + err = ErrDecryption return } diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go index c36bca1cd..0fb9875d0 100644 --- a/src/pkg/crypto/rsa/rsa_test.go +++ b/src/pkg/crypto/rsa/rsa_test.go @@ -5,10 +5,10 @@ package rsa import ( - "big" "bytes" "crypto/rand" "crypto/sha1" + "math/big" "testing" ) diff --git a/src/pkg/crypto/sha1/Makefile b/src/pkg/crypto/sha1/Makefile deleted file mode 100644 index 81ac38c0b..000000000 --- a/src/pkg/crypto/sha1/Makefile +++ /dev/null @@ -1,12 +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 ../../../Make.inc - -TARG=crypto/sha1 -GOFILES=\ - sha1.go\ - sha1block.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/sha1/sha1.go b/src/pkg/crypto/sha1/sha1.go index 788d1ff55..876e7992a 100644 --- a/src/pkg/crypto/sha1/sha1.go +++ b/src/pkg/crypto/sha1/sha1.go @@ -8,7 +8,6 @@ package sha1 import ( "crypto" "hash" - "os" ) func init() { @@ -18,6 +17,9 @@ func init() { // The size of a SHA1 checksum in bytes. const Size = 20 +// The blocksize of SHA1 in bytes. +const BlockSize = 64 + const ( _Chunk = 64 _Init0 = 0x67452301 @@ -54,7 +56,9 @@ func New() hash.Hash { func (d *digest) Size() int { return Size } -func (d *digest) Write(p []byte) (nn int, err os.Error) { +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { nn = len(p) d.len += uint64(nn) if d.nx > 0 { @@ -80,10 +84,9 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. - d := new(digest) - *d = *d0 + d := *d0 // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. len := d.len @@ -106,14 +109,13 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 20) - j := 0 - for _, s := range d.h { - p[j+0] = byte(s >> 24) - p[j+1] = byte(s >> 16) - p[j+2] = byte(s >> 8) - p[j+3] = byte(s >> 0) - j += 4 + var digest [Size]byte + for i, s := range d.h { + digest[i*4] = byte(s >> 24) + digest[i*4+1] = byte(s >> 16) + digest[i*4+2] = byte(s >> 8) + digest[i*4+3] = byte(s) } - return p + + return append(in, digest[:]...) } diff --git a/src/pkg/crypto/sha1/sha1_test.go b/src/pkg/crypto/sha1/sha1_test.go index 2712fe35e..2dc14ac98 100644 --- a/src/pkg/crypto/sha1/sha1_test.go +++ b/src/pkg/crypto/sha1/sha1_test.go @@ -4,9 +4,10 @@ // SHA1 hash algorithm. See RFC 3174. -package sha1 +package sha1_test import ( + "crypto/sha1" "fmt" "io" "testing" @@ -54,16 +55,16 @@ var golden = []sha1Test{ func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] - c := New() + c := sha1.New() for j := 0; j < 3; j++ { if j < 2 { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha1[%d](%s) = %s want %s", j, g.in, s, g.out) } @@ -71,3 +72,10 @@ func TestGolden(t *testing.T) { } } } + +func ExampleNew() { + h := sha1.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 +} diff --git a/src/pkg/crypto/sha256/Makefile b/src/pkg/crypto/sha256/Makefile deleted file mode 100644 index 97fe4d8e6..000000000 --- a/src/pkg/crypto/sha256/Makefile +++ /dev/null @@ -1,12 +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 ../../../Make.inc - -TARG=crypto/sha256 -GOFILES=\ - sha256.go\ - sha256block.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/sha256/sha256.go b/src/pkg/crypto/sha256/sha256.go index a2c058d18..a61e30b42 100644 --- a/src/pkg/crypto/sha256/sha256.go +++ b/src/pkg/crypto/sha256/sha256.go @@ -9,7 +9,6 @@ package sha256 import ( "crypto" "hash" - "os" ) func init() { @@ -23,6 +22,9 @@ const Size = 32 // The size of a SHA224 checksum in bytes. const Size224 = 28 +// The blocksize of SHA256 and SHA224 in bytes. +const BlockSize = 64 + const ( _Chunk = 64 _Init0 = 0x6A09E667 @@ -98,7 +100,9 @@ func (d *digest) Size() int { return Size224 } -func (d *digest) Write(p []byte) (nn int, err os.Error) { +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { nn = len(p) d.len += uint64(nn) if d.nx > 0 { @@ -124,10 +128,9 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. - d := new(digest) - *d = *d0 + d := *d0 // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. len := d.len @@ -150,17 +153,20 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 32) - j := 0 - for _, s := range d.h { - p[j+0] = byte(s >> 24) - p[j+1] = byte(s >> 16) - p[j+2] = byte(s >> 8) - p[j+3] = byte(s >> 0) - j += 4 - } + h := d.h[:] + size := Size if d.is224 { - return p[0:28] + h = d.h[:7] + size = Size224 } - return p + + var digest [Size]byte + for i, s := range h { + digest[i*4] = byte(s >> 24) + digest[i*4+1] = byte(s >> 16) + digest[i*4+2] = byte(s >> 8) + digest[i*4+3] = byte(s) + } + + return append(in, digest[:size]...) } diff --git a/src/pkg/crypto/sha256/sha256_test.go b/src/pkg/crypto/sha256/sha256_test.go index 42a3fa7a0..a6efb3754 100644 --- a/src/pkg/crypto/sha256/sha256_test.go +++ b/src/pkg/crypto/sha256/sha256_test.go @@ -94,10 +94,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha256[%d](%s) = %s want %s", j, g.in, s, g.out) } @@ -112,10 +112,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha224[%d](%s) = %s want %s", j, g.in, s, g.out) } diff --git a/src/pkg/crypto/sha512/Makefile b/src/pkg/crypto/sha512/Makefile deleted file mode 100644 index 2f7633fa3..000000000 --- a/src/pkg/crypto/sha512/Makefile +++ /dev/null @@ -1,12 +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 ../../../Make.inc - -TARG=crypto/sha512 -GOFILES=\ - sha512.go\ - sha512block.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/sha512/sha512.go b/src/pkg/crypto/sha512/sha512.go index 78f5fe26f..a245fd68e 100644 --- a/src/pkg/crypto/sha512/sha512.go +++ b/src/pkg/crypto/sha512/sha512.go @@ -9,7 +9,6 @@ package sha512 import ( "crypto" "hash" - "os" ) func init() { @@ -23,6 +22,9 @@ const Size = 64 // The size of a SHA384 checksum in bytes. const Size384 = 48 +// The blocksize of SHA512 and SHA384 in bytes. +const BlockSize = 128 + const ( _Chunk = 128 _Init0 = 0x6a09e667f3bcc908 @@ -98,7 +100,9 @@ func (d *digest) Size() int { return Size384 } -func (d *digest) Write(p []byte) (nn int, err os.Error) { +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { nn = len(p) d.len += uint64(nn) if d.nx > 0 { @@ -124,7 +128,7 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. d := new(digest) *d = *d0 @@ -150,21 +154,24 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 64) - j := 0 - for _, s := range d.h { - p[j+0] = byte(s >> 56) - p[j+1] = byte(s >> 48) - p[j+2] = byte(s >> 40) - p[j+3] = byte(s >> 32) - p[j+4] = byte(s >> 24) - p[j+5] = byte(s >> 16) - p[j+6] = byte(s >> 8) - p[j+7] = byte(s >> 0) - j += 8 - } + h := d.h[:] + size := Size if d.is384 { - return p[0:48] + h = d.h[:6] + size = Size384 } - return p + + var digest [Size]byte + for i, s := range h { + digest[i*8] = byte(s >> 56) + digest[i*8+1] = byte(s >> 48) + digest[i*8+2] = byte(s >> 40) + digest[i*8+3] = byte(s >> 32) + digest[i*8+4] = byte(s >> 24) + digest[i*8+5] = byte(s >> 16) + digest[i*8+6] = byte(s >> 8) + digest[i*8+7] = byte(s) + } + + return append(in, digest[:size]...) } diff --git a/src/pkg/crypto/sha512/sha512_test.go b/src/pkg/crypto/sha512/sha512_test.go index dd116dc17..a70f7c54e 100644 --- a/src/pkg/crypto/sha512/sha512_test.go +++ b/src/pkg/crypto/sha512/sha512_test.go @@ -94,10 +94,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha512[%d](%s) = %s want %s", j, g.in, s, g.out) } @@ -112,10 +112,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha384[%d](%s) = %s want %s", j, g.in, s, g.out) } diff --git a/src/pkg/crypto/subtle/Makefile b/src/pkg/crypto/subtle/Makefile deleted file mode 100644 index 08d8bbfa0..000000000 --- a/src/pkg/crypto/subtle/Makefile +++ /dev/null @@ -1,11 +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 ../../../Make.inc - -TARG=crypto/subtle -GOFILES=\ - constant_time.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/tls/Makefile b/src/pkg/crypto/tls/Makefile deleted file mode 100644 index 000314be5..000000000 --- a/src/pkg/crypto/tls/Makefile +++ /dev/null @@ -1,20 +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 ../../../Make.inc - -TARG=crypto/tls -GOFILES=\ - alert.go\ - cipher_suites.go\ - common.go\ - conn.go\ - handshake_client.go\ - handshake_messages.go\ - handshake_server.go\ - key_agreement.go\ - prf.go\ - tls.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/tls/alert.go b/src/pkg/crypto/tls/alert.go index 3b9e0e241..0856311e4 100644 --- a/src/pkg/crypto/tls/alert.go +++ b/src/pkg/crypto/tls/alert.go @@ -71,3 +71,7 @@ func (e alert) String() string { } return "alert(" + strconv.Itoa(int(e)) + ")" } + +func (e alert) Error() string { + return e.String() +} diff --git a/src/pkg/crypto/tls/cipher_suites.go b/src/pkg/crypto/tls/cipher_suites.go index bc7b0d32f..00695e7d1 100644 --- a/src/pkg/crypto/tls/cipher_suites.go +++ b/src/pkg/crypto/tls/cipher_suites.go @@ -7,11 +7,12 @@ package tls import ( "crypto/aes" "crypto/cipher" + "crypto/des" "crypto/hmac" "crypto/rc4" + "crypto/sha1" "crypto/x509" "hash" - "os" ) // a keyAgreement implements the client and server side of a TLS key agreement @@ -22,20 +23,21 @@ type keyAgreement interface { // In the case that the key agreement protocol doesn't use a // ServerKeyExchange message, generateServerKeyExchange can return nil, // nil. - generateServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) - processClientKeyExchange(*Config, *clientKeyExchangeMsg) ([]byte, os.Error) + generateServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error) + processClientKeyExchange(*Config, *clientKeyExchangeMsg, uint16) ([]byte, error) // On the client side, the next two methods are called in order. // This method may not be called if the server doesn't send a // ServerKeyExchange message. - processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) os.Error - generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) + processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error + generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) } // A cipherSuite is a specific combination of key agreement, cipher and MAC // function. All cipher suites currently assume RSA key agreement. type cipherSuite struct { + id uint16 // the lengths, in bytes, of the key material needed for each component. keyLen int macLen int @@ -46,14 +48,16 @@ type cipherSuite struct { // and point format that we can handle. elliptic bool cipher func(key, iv []byte, isRead bool) interface{} - mac func(macKey []byte) hash.Hash + mac func(version uint16, macKey []byte) macFunction } -var cipherSuites = map[uint16]*cipherSuite{ - TLS_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, hmacSHA1}, - TLS_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, hmacSHA1}, - TLS_ECDHE_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, hmacSHA1}, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, hmacSHA1}, +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_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}, } func cipherRC4(key, iv []byte, isRead bool) interface{} { @@ -61,6 +65,14 @@ func cipherRC4(key, iv []byte, isRead bool) interface{} { return cipher } +func cipher3DES(key, iv []byte, isRead bool) interface{} { + block, _ := des.NewTripleDESCipher(key) + if isRead { + return cipher.NewCBCDecrypter(block, iv) + } + return cipher.NewCBCEncrypter(block, iv) +} + func cipherAES(key, iv []byte, isRead bool) interface{} { block, _ := aes.NewCipher(key) if isRead { @@ -69,8 +81,75 @@ func cipherAES(key, iv []byte, isRead bool) interface{} { return cipher.NewCBCEncrypter(block, iv) } -func hmacSHA1(key []byte) hash.Hash { - return hmac.NewSHA1(key) +// macSHA1 returns a macFunction for the given protocol version. +func macSHA1(version uint16, key []byte) macFunction { + if version == versionSSL30 { + mac := ssl30MAC{ + h: sha1.New(), + key: make([]byte, len(key)), + } + copy(mac.key, key) + return mac + } + return tls10MAC{hmac.New(sha1.New, key)} +} + +type macFunction interface { + Size() int + MAC(digestBuf, seq, data []byte) []byte +} + +// ssl30MAC implements the SSLv3 MAC function, as defined in +// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 5.2.3.1 +type ssl30MAC struct { + h hash.Hash + key []byte +} + +func (s ssl30MAC) Size() int { + return s.h.Size() +} + +var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36} + +var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c} + +func (s ssl30MAC) MAC(digestBuf, seq, record []byte) []byte { + padLength := 48 + if s.h.Size() == 20 { + padLength = 40 + } + + s.h.Reset() + s.h.Write(s.key) + s.h.Write(ssl30Pad1[:padLength]) + s.h.Write(seq) + s.h.Write(record[:1]) + s.h.Write(record[3:5]) + s.h.Write(record[recordHeaderLen:]) + digestBuf = s.h.Sum(digestBuf[:0]) + + s.h.Reset() + s.h.Write(s.key) + s.h.Write(ssl30Pad2[:padLength]) + s.h.Write(digestBuf) + return s.h.Sum(digestBuf[:0]) +} + +// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, section 6.2.3. +type tls10MAC struct { + h hash.Hash +} + +func (s tls10MAC) Size() int { + return s.h.Size() +} + +func (s tls10MAC) MAC(digestBuf, seq, record []byte) []byte { + s.h.Reset() + s.h.Write(seq) + s.h.Write(record) + return s.h.Sum(digestBuf[:0]) } func rsaKA() keyAgreement { @@ -81,22 +160,29 @@ func ecdheRSAKA() keyAgreement { return new(ecdheRSAKeyAgreement) } -// mutualCipherSuite returns a cipherSuite and its id given a list of supported +// mutualCipherSuite returns a cipherSuite given a list of supported // ciphersuites and the id requested by the peer. -func mutualCipherSuite(have []uint16, want uint16) (suite *cipherSuite, id uint16) { +func mutualCipherSuite(have []uint16, want uint16) *cipherSuite { for _, id := range have { if id == want { - return cipherSuites[id], id + for _, suite := range cipherSuites { + if suite.id == want { + return suite + } + } + return nil } } - return + return nil } // A list of the possible cipher suite ids. Taken from // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml const ( - TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 - TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f - TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 + 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_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 ) diff --git a/src/pkg/crypto/tls/common.go b/src/pkg/crypto/tls/common.go index 3efac9c13..4ba0bf874 100644 --- a/src/pkg/crypto/tls/common.go +++ b/src/pkg/crypto/tls/common.go @@ -5,11 +5,11 @@ package tls import ( + "crypto" "crypto/rand" - "crypto/rsa" "crypto/x509" "io" - "io/ioutil" + "strings" "sync" "time" ) @@ -20,8 +20,11 @@ const ( recordHeaderLen = 5 // record header length maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB) - minVersion = 0x0301 // minimum supported version - TLS 1.0 - maxVersion = 0x0301 // maximum supported version - TLS 1.0 + versionSSL30 = 0x0300 + versionTLS10 = 0x0301 + + minVersion = versionSSL30 + maxVersion = versionTLS10 ) // TLS record types. @@ -98,12 +101,28 @@ type ConnectionState struct { NegotiatedProtocol string NegotiatedProtocolIsMutual bool + // ServerName contains the server name indicated by the client, if any. + // (Only valid for server connections.) + ServerName string + // the certificate chain that was presented by the other side PeerCertificates []*x509.Certificate // the verified certificate chains built from PeerCertificates. VerifiedChains [][]*x509.Certificate } +// ClientAuthType declares the policy the server will follow for +// TLS Client Authentication. +type ClientAuthType int + +const ( + NoClientCert ClientAuthType = iota + RequestClientCert + RequireAnyClientCert + VerifyClientCertIfGiven + RequireAndVerifyClientCert +) + // A Config structure is used to configure a TLS client or server. After one // has been passed to a TLS function it must not be modified. type Config struct { @@ -113,14 +132,22 @@ type Config struct { Rand io.Reader // Time returns the current time as the number of seconds since the epoch. - // If Time is nil, TLS uses the system time.Seconds. - Time func() int64 + // If Time is nil, TLS uses time.Now. + Time func() time.Time // Certificates contains one or more certificate chains // to present to the other side of the connection. // Server configurations must include at least one certificate. Certificates []Certificate + // NameToCertificate maps from a certificate name to an element of + // Certificates. Note that a certificate name can be of the form + // '*.example.com' and so doesn't have to be a domain name as such. + // See Config.BuildNameToCertificate + // The nil value causes the first element of Certificates to be used + // for all connections. + NameToCertificate map[string]*Certificate + // RootCAs defines the set of root certificate authorities // that clients use when verifying server certificates. // If RootCAs is nil, TLS uses the host's root CA set. @@ -133,11 +160,22 @@ type Config struct { // hosting. ServerName string - // AuthenticateClient controls whether a server will request a certificate - // from the client. It does not require that the client send a - // certificate nor does it require that the certificate sent be - // anything more than self-signed. - AuthenticateClient bool + // ClientAuth determines the server's policy for + // TLS Client Authentication. The default is NoClientCert. + ClientAuth ClientAuthType + + // ClientCAs defines the set of root certificate authorities + // that servers use if required to verify a client certificate + // by the policy in ClientAuth. + ClientCAs *x509.CertPool + + // InsecureSkipVerify controls whether a client verifies the + // server's certificate chain and host name. + // If InsecureSkipVerify is true, TLS accepts any certificate + // presented by the server and any host name in that certificate. + // In this mode, TLS is susceptible to man-in-the-middle attacks. + // This should be used only for testing. + InsecureSkipVerify bool // CipherSuites is a list of supported cipher suites. If CipherSuites // is nil, TLS uses a list of suites supported by the implementation. @@ -152,22 +190,14 @@ func (c *Config) rand() io.Reader { return r } -func (c *Config) time() int64 { +func (c *Config) time() time.Time { t := c.Time if t == nil { - t = time.Seconds + t = time.Now } return t() } -func (c *Config) rootCAs() *x509.CertPool { - s := c.RootCAs - if s == nil { - s = defaultRoots() - } - return s -} - func (c *Config) cipherSuites() []uint16 { s := c.CipherSuites if s == nil { @@ -176,13 +206,71 @@ func (c *Config) cipherSuites() []uint16 { return s } +// getCertificateForName returns the best certificate for the given name, +// defaulting to the first element of c.Certificates if there are no good +// options. +func (c *Config) getCertificateForName(name string) *Certificate { + if len(c.Certificates) == 1 || c.NameToCertificate == nil { + // There's only one choice, so no point doing any work. + return &c.Certificates[0] + } + + name = strings.ToLower(name) + for len(name) > 0 && name[len(name)-1] == '.' { + name = name[:len(name)-1] + } + + if cert, ok := c.NameToCertificate[name]; ok { + return cert + } + + // try replacing labels in the name with wildcards until we get a + // match. + labels := strings.Split(name, ".") + for i := range labels { + labels[i] = "*" + candidate := strings.Join(labels, ".") + if cert, ok := c.NameToCertificate[candidate]; ok { + return cert + } + } + + // If nothing matches, return the first certificate. + return &c.Certificates[0] +} + +// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate +// from the CommonName and SubjectAlternateName fields of each of the leaf +// certificates. +func (c *Config) BuildNameToCertificate() { + c.NameToCertificate = make(map[string]*Certificate) + for i := range c.Certificates { + cert := &c.Certificates[i] + x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + continue + } + if len(x509Cert.Subject.CommonName) > 0 { + c.NameToCertificate[x509Cert.Subject.CommonName] = cert + } + for _, san := range x509Cert.DNSNames { + c.NameToCertificate[san] = cert + } + } +} + // A Certificate is a chain of one or more certificates, leaf first. type Certificate struct { Certificate [][]byte - PrivateKey *rsa.PrivateKey + PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey // OCSPStaple contains an optional OCSP response which will be served // to clients that request it. OCSPStaple []byte + // Leaf is the parsed form of the leaf certificate, which may be + // initialized using x509.ParseCertificate to reduce per-handshake + // processing for TLS clients doing client authentication. If nil, the + // leaf certificate will be parsed as needed. + Leaf *x509.Certificate } // A TLS record. @@ -215,53 +303,19 @@ func defaultConfig() *Config { return &emptyConfig } -// Possible certificate files; stop after finding one. -// On OS X we should really be using the Directory Services keychain -// but that requires a lot of Mach goo to get at. Instead we use -// the same root set that curl uses. -var certFiles = []string{ - "/etc/ssl/certs/ca-certificates.crt", // Linux etc - "/usr/share/curl/curl-ca-bundle.crt", // OS X -} - -var once sync.Once - -func defaultRoots() *x509.CertPool { - once.Do(initDefaults) - return varDefaultRoots -} +var ( + once sync.Once + varDefaultCipherSuites []uint16 +) func defaultCipherSuites() []uint16 { - once.Do(initDefaults) + once.Do(initDefaultCipherSuites) return varDefaultCipherSuites } -func initDefaults() { - initDefaultRoots() - initDefaultCipherSuites() -} - -var varDefaultRoots *x509.CertPool - -func initDefaultRoots() { - roots := x509.NewCertPool() - for _, file := range certFiles { - data, err := ioutil.ReadFile(file) - if err == nil { - roots.AppendCertsFromPEM(data) - break - } - } - varDefaultRoots = roots -} - -var varDefaultCipherSuites []uint16 - func initDefaultCipherSuites() { varDefaultCipherSuites = make([]uint16, len(cipherSuites)) - i := 0 - for id := range cipherSuites { - varDefaultCipherSuites[i] = id - i++ + for i, suite := range cipherSuites { + varDefaultCipherSuites[i] = suite.id } } diff --git a/src/pkg/crypto/tls/conn.go b/src/pkg/crypto/tls/conn.go index fac65afd9..2a5115dc6 100644 --- a/src/pkg/crypto/tls/conn.go +++ b/src/pkg/crypto/tls/conn.go @@ -11,11 +11,11 @@ import ( "crypto/cipher" "crypto/subtle" "crypto/x509" - "hash" + "errors" "io" "net" - "os" "sync" + "time" ) // A Conn represents a secured connection. @@ -37,13 +37,15 @@ type Conn struct { // verifiedChains contains the certificate chains that we built, as // opposed to the ones presented by the server. verifiedChains [][]*x509.Certificate + // serverName contains the server name indicated by the client, if any. + serverName string clientProtocol string clientProtocolFallback bool // first permanent error errMutex sync.Mutex - err os.Error + err error // input/output in, out halfConn // in.Mutex < out.Mutex @@ -54,7 +56,7 @@ type Conn struct { tmp [16]byte } -func (c *Conn) setError(err os.Error) os.Error { +func (c *Conn) setError(err error) error { c.errMutex.Lock() defer c.errMutex.Unlock() @@ -64,7 +66,7 @@ func (c *Conn) setError(err os.Error) os.Error { return err } -func (c *Conn) error() os.Error { +func (c *Conn) error() error { c.errMutex.Lock() defer c.errMutex.Unlock() @@ -85,48 +87,54 @@ func (c *Conn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() } -// SetTimeout sets the read deadline associated with the connection. -// There is no write deadline. -func (c *Conn) SetTimeout(nsec int64) os.Error { - return c.conn.SetTimeout(nsec) +// SetDeadline sets the read and write deadlines associated with the connection. +// A zero value for t means Read and Write will not time out. +// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. +func (c *Conn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) } -// SetReadTimeout sets the time (in nanoseconds) that -// Read will wait for data before returning os.EAGAIN. -// Setting nsec == 0 (the default) disables the deadline. -func (c *Conn) SetReadTimeout(nsec int64) os.Error { - return c.conn.SetReadTimeout(nsec) +// SetReadDeadline sets the read deadline on the underlying connection. +// A zero value for t means Read will not time out. +func (c *Conn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) } -// SetWriteTimeout exists to satisfy the net.Conn interface -// but is not implemented by TLS. It always returns an error. -func (c *Conn) SetWriteTimeout(nsec int64) os.Error { - return os.NewError("TLS does not support SetWriteTimeout") +// SetWriteDeadline sets the write deadline on the underlying conneciton. +// A zero value for t means Write will not time out. +// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. +func (c *Conn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) } // A halfConn represents one direction of the record layer // connection, either sending or receiving. type halfConn struct { sync.Mutex - cipher interface{} // cipher algorithm - mac hash.Hash // MAC algorithm - seq [8]byte // 64-bit sequence number - bfree *block // list of free blocks + version uint16 // protocol version + cipher interface{} // cipher algorithm + mac macFunction + seq [8]byte // 64-bit sequence number + bfree *block // list of free blocks nextCipher interface{} // next encryption state - nextMac hash.Hash // next MAC algorithm + nextMac macFunction // next MAC algorithm + + // used to save allocating a new buffer for each MAC. + inDigestBuf, outDigestBuf []byte } // prepareCipherSpec sets the encryption and MAC states // that a subsequent changeCipherSpec will use. -func (hc *halfConn) prepareCipherSpec(cipher interface{}, mac hash.Hash) { +func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction) { + hc.version = version hc.nextCipher = cipher hc.nextMac = mac } // changeCipherSpec changes the encryption and MAC states // to the ones previously passed to prepareCipherSpec. -func (hc *halfConn) changeCipherSpec() os.Error { +func (hc *halfConn) changeCipherSpec() error { if hc.nextCipher == nil { return alertInternalError } @@ -197,6 +205,22 @@ func removePadding(payload []byte) ([]byte, byte) { return payload[:len(payload)-int(toRemove)], good } +// removePaddingSSL30 is a replacement for removePadding in the case that the +// protocol version is SSLv3. In this version, the contents of the padding +// are random and cannot be checked. +func removePaddingSSL30(payload []byte) ([]byte, byte) { + if len(payload) < 1 { + return payload, 0 + } + + paddingLen := int(payload[len(payload)-1]) + 1 + if paddingLen > len(payload) { + return payload, 0 + } + + return payload[:len(payload)-paddingLen], 255 +} + func roundUp(a, b int) int { return a + (b-a%b)%b } @@ -226,7 +250,11 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { } c.CryptBlocks(payload, payload) - payload, paddingGood = removePadding(payload) + if hc.version == versionSSL30 { + payload, paddingGood = removePaddingSSL30(payload) + } else { + payload, paddingGood = removePadding(payload) + } b.resize(recordHeaderLen + len(payload)) // note that we still have a timing side-channel in the @@ -256,15 +284,13 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { b.data[4] = byte(n) b.resize(recordHeaderLen + n) remoteMAC := payload[n:] - - hc.mac.Reset() - hc.mac.Write(hc.seq[0:]) + localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data) hc.incSeq() - hc.mac.Write(b.data) - if subtle.ConstantTimeCompare(hc.mac.Sum(), remoteMAC) != 1 || paddingGood != 255 { + if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 { return false, alertBadRecordMAC } + hc.inDigestBuf = localMAC } return true, 0 @@ -291,14 +317,13 @@ func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) { func (hc *halfConn) encrypt(b *block) (bool, alert) { // mac if hc.mac != nil { - hc.mac.Reset() - hc.mac.Write(hc.seq[0:]) + mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data) hc.incSeq() - hc.mac.Write(b.data) - mac := hc.mac.Sum() + n := len(b.data) b.resize(n + len(mac)) copy(b.data[n:], mac) + hc.outDigestBuf = mac } payload := b.data[recordHeaderLen:] @@ -360,7 +385,7 @@ func (b *block) reserve(n int) { // readFromUntil reads from r into b until b contains at least n bytes // or else returns an error. -func (b *block) readFromUntil(r io.Reader, n int) os.Error { +func (b *block) readFromUntil(r io.Reader, n int) error { // quick case if len(b.data) >= n { return nil @@ -381,7 +406,7 @@ func (b *block) readFromUntil(r io.Reader, n int) os.Error { return nil } -func (b *block) Read(p []byte) (n int, err os.Error) { +func (b *block) Read(p []byte) (n int, err error) { n = copy(p, b.data[b.off:]) b.off += n return @@ -425,7 +450,7 @@ func (hc *halfConn) splitBlock(b *block, n int) (*block, *block) { // readRecord reads the next TLS record from the connection // and updates the record layer state. // c.in.Mutex <= L; c.input == nil. -func (c *Conn) readRecord(want recordType) os.Error { +func (c *Conn) readRecord(want recordType) error { // Caller must be in sync with connection: // handshake data if handshake not yet completed, // else application data. (We don't support renegotiation.) @@ -453,7 +478,7 @@ Again: // RFC suggests that EOF without an alertCloseNotify is // an error, but popular web sites seem to do this, // so we can't make it an error. - // if err == os.EOF { + // if err == io.EOF { // err = io.ErrUnexpectedEOF // } if e, ok := err.(net.Error); !ok || !e.Temporary() { @@ -470,8 +495,21 @@ Again: if n > maxCiphertext { return c.sendAlert(alertRecordOverflow) } + if !c.haveVers { + // 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. + // 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, + // it's probably not real. + if (typ != recordTypeAlert && typ != want) || vers >= 0x1000 || n >= 0x3000 { + return c.sendAlert(alertUnexpectedMessage) + } + } if err := b.readFromUntil(c.conn, recordHeaderLen+n); err != nil { - if err == os.EOF { + if err == io.EOF { err = io.ErrUnexpectedEOF } if e, ok := err.(net.Error); !ok || !e.Temporary() { @@ -503,7 +541,7 @@ Again: break } if alert(data[1]) == alertCloseNotify { - c.setError(os.EOF) + c.setError(io.EOF) break } switch data[0] { @@ -512,7 +550,7 @@ Again: c.in.freeBlock(b) goto Again case alertLevelError: - c.setError(&net.OpError{Op: "remote error", Error: alert(data[1])}) + c.setError(&net.OpError{Op: "remote error", Err: alert(data[1])}) default: c.sendAlert(alertUnexpectedMessage) } @@ -551,7 +589,7 @@ Again: // sendAlert sends a TLS alert message. // c.out.Mutex <= L. -func (c *Conn) sendAlertLocked(err alert) os.Error { +func (c *Conn) sendAlertLocked(err alert) error { c.tmp[0] = alertLevelError if err == alertNoRenegotiation { c.tmp[0] = alertLevelWarning @@ -560,14 +598,14 @@ func (c *Conn) sendAlertLocked(err alert) os.Error { c.writeRecord(recordTypeAlert, c.tmp[0:2]) // closeNotify is a special case in that it isn't an error: if err != alertCloseNotify { - return c.setError(&net.OpError{Op: "local error", Error: err}) + return c.setError(&net.OpError{Op: "local error", Err: err}) } return nil } // sendAlert sends a TLS alert message. // L < c.out.Mutex. -func (c *Conn) sendAlert(err alert) os.Error { +func (c *Conn) sendAlert(err alert) error { c.out.Lock() defer c.out.Unlock() return c.sendAlertLocked(err) @@ -576,7 +614,7 @@ func (c *Conn) sendAlert(err alert) os.Error { // writeRecord writes a TLS record with the given type and payload // to the connection and updates the record layer state. // c.out.Mutex <= L. -func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err os.Error) { +func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) { b := c.out.newBlock() for len(data) > 0 { m := len(data) @@ -612,7 +650,7 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err os.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", Error: err} + c.err = &net.OpError{Op: "local error", Err: err} return n, c.err } } @@ -622,12 +660,14 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err os.Error) { // readHandshake reads the next handshake message from // the record layer. // c.in.Mutex < L; c.out.Mutex < L. -func (c *Conn) readHandshake() (interface{}, os.Error) { +func (c *Conn) readHandshake() (interface{}, error) { for c.hand.Len() < 4 { if c.err != nil { return nil, c.err } - c.readRecord(recordTypeHandshake) + if err := c.readRecord(recordTypeHandshake); err != nil { + return nil, err + } } data := c.hand.Bytes() @@ -640,7 +680,9 @@ func (c *Conn) readHandshake() (interface{}, os.Error) { if c.err != nil { return nil, c.err } - c.readRecord(recordTypeHandshake) + if err := c.readRecord(recordTypeHandshake); err != nil { + return nil, err + } } data = c.hand.Next(4 + n) var m handshakeMessage @@ -685,9 +727,13 @@ func (c *Conn) readHandshake() (interface{}, os.Error) { } // Write writes data to the connection. -func (c *Conn) Write(b []byte) (n int, err os.Error) { - if err = c.Handshake(); err != nil { - return +func (c *Conn) Write(b []byte) (int, error) { + if c.err != nil { + return 0, c.err + } + + if c.err = c.Handshake(); c.err != nil { + return 0, c.err } c.out.Lock() @@ -696,15 +742,15 @@ func (c *Conn) Write(b []byte) (n int, err os.Error) { if !c.handshakeComplete { return 0, alertInternalError } - if c.err != nil { - return 0, c.err - } - return c.writeRecord(recordTypeApplicationData, b) + + var n int + n, c.err = c.writeRecord(recordTypeApplicationData, b) + return n, c.err } -// Read can be made to time out and return err == os.EAGAIN -// after a fixed time limit; see SetTimeout and SetReadTimeout. -func (c *Conn) Read(b []byte) (n int, err os.Error) { +// Read can be made to time out and return a net.Error with Timeout() == true +// after a fixed time limit; see SetDeadline and SetReadDeadline. +func (c *Conn) Read(b []byte) (n int, err error) { if err = c.Handshake(); err != nil { return } @@ -730,18 +776,26 @@ func (c *Conn) Read(b []byte) (n int, err os.Error) { } // Close closes the connection. -func (c *Conn) Close() os.Error { - if err := c.Handshake(); err != nil { +func (c *Conn) Close() error { + var alertErr error + + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + if c.handshakeComplete { + alertErr = c.sendAlert(alertCloseNotify) + } + + if err := c.conn.Close(); err != nil { return err } - return c.sendAlert(alertCloseNotify) + return alertErr } // Handshake runs the client or server handshake // protocol if it has not yet been run. // Most uses of this package need not call Handshake // explicitly: the first Read or Write will call it automatically. -func (c *Conn) Handshake() os.Error { +func (c *Conn) Handshake() error { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() if err := c.error(); err != nil { @@ -769,6 +823,7 @@ func (c *Conn) ConnectionState() ConnectionState { state.CipherSuite = c.cipherSuite state.PeerCertificates = c.peerCertificates state.VerifiedChains = c.verifiedChains + state.ServerName = c.serverName } return state @@ -784,16 +839,16 @@ func (c *Conn) OCSPResponse() []byte { } // VerifyHostname checks that the peer certificate chain is valid for -// connecting to host. If so, it returns nil; if not, it returns an os.Error +// connecting to host. If so, it returns nil; if not, it returns an error // describing the problem. -func (c *Conn) VerifyHostname(host string) os.Error { +func (c *Conn) VerifyHostname(host string) error { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() if !c.isClient { - return os.NewError("VerifyHostname called on TLS server connection") + return errors.New("VerifyHostname called on TLS server connection") } if !c.handshakeComplete { - return os.NewError("TLS handshake has not yet been performed") + return errors.New("TLS handshake has not yet been performed") } return c.peerCertificates[0].VerifyHostname(host) } diff --git a/src/pkg/crypto/tls/conn_test.go b/src/pkg/crypto/tls/conn_test.go index f44a50bed..5c555147c 100644 --- a/src/pkg/crypto/tls/conn_test.go +++ b/src/pkg/crypto/tls/conn_test.go @@ -50,3 +50,57 @@ func TestRemovePadding(t *testing.T) { } } } + +var certExampleCom = `308201403081eda003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313138353835325a170d3132303933303138353835325a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31a301830160603551d11040f300d820b6578616d706c652e636f6d300b06092a864886f70d0101050341001a0b419d2c74474c6450654e5f10b32bf426ffdf55cad1c52602e7a9151513a3424c70f5960dcd682db0c33769cc1daa3fcdd3db10809d2392ed4a1bf50ced18` + +var certWildcardExampleCom = `308201423081efa003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303034365a170d3132303933303139303034365a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31c301a30180603551d110411300f820d2a2e6578616d706c652e636f6d300b06092a864886f70d0101050341001676f0c9e7c33c1b656ed5a6476c4e2ee9ec8e62df7407accb1875272b2edd0a22096cb2c22598d11604104d604f810eb4b5987ca6bb319c7e6ce48725c54059` + +var certFooExampleCom = `308201443081f1a003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303131345a170d3132303933303139303131345a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31e301c301a0603551d1104133011820f666f6f2e6578616d706c652e636f6d300b06092a864886f70d010105034100646a2a51f2aa2477add854b462cf5207ba16d3213ffb5d3d0eed473fbf09935019192d1d5b8ca6a2407b424cf04d97c4cd9197c83ecf81f0eab9464a1109d09f` + +var certDoubleWildcardExampleCom = `308201443081f1a003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303134315a170d3132303933303139303134315a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31e301c301a0603551d1104133011820f2a2e2a2e6578616d706c652e636f6d300b06092a864886f70d0101050341001c3de267975f56ef57771c6218ef95ecc65102e57bd1defe6f7efea90d9b26cf40de5bd7ad75e46201c7f2a92aaa3e907451e9409f65e28ddb6db80d726290f6` + +func TestCertificateSelection(t *testing.T) { + config := Config{ + Certificates: []Certificate{ + { + Certificate: [][]byte{fromHex(certExampleCom)}, + }, + { + Certificate: [][]byte{fromHex(certWildcardExampleCom)}, + }, + { + Certificate: [][]byte{fromHex(certFooExampleCom)}, + }, + { + Certificate: [][]byte{fromHex(certDoubleWildcardExampleCom)}, + }, + }, + } + + config.BuildNameToCertificate() + + pointerToIndex := func(c *Certificate) int { + for i := range config.Certificates { + if c == &config.Certificates[i] { + return i + } + } + return -1 + } + + if n := pointerToIndex(config.getCertificateForName("example.com")); n != 0 { + t.Errorf("example.com returned certificate %d, not 0", n) + } + if n := pointerToIndex(config.getCertificateForName("bar.example.com")); n != 1 { + t.Errorf("bar.example.com returned certificate %d, not 1", n) + } + if n := pointerToIndex(config.getCertificateForName("foo.example.com")); n != 2 { + t.Errorf("foo.example.com returned certificate %d, not 2", n) + } + if n := pointerToIndex(config.getCertificateForName("foo.bar.example.com")); n != 3 { + t.Errorf("foo.bar.example.com returned certificate %d, not 3", n) + } + if n := pointerToIndex(config.getCertificateForName("foo.bar.baz.example.com")); n != 0 { + t.Errorf("foo.bar.baz.example.com returned certificate %d, not 0", n) + } +} diff --git a/src/pkg/crypto/tls/generate_cert.go b/src/pkg/crypto/tls/generate_cert.go index 41206e276..84be5bfd8 100644 --- a/src/pkg/crypto/tls/generate_cert.go +++ b/src/pkg/crypto/tls/generate_cert.go @@ -2,20 +2,22 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + // Generate a self-signed X.509 certificate for a TLS server. Outputs to // 'cert.pem' and 'key.pem' and will overwrite existing files. package main import ( - "big" - "crypto/x509/pkix" "crypto/rand" "crypto/rsa" "crypto/x509" + "crypto/x509/pkix" "encoding/pem" "flag" "log" + "math/big" "os" "time" ) @@ -31,7 +33,7 @@ func main() { return } - now := time.Seconds() + now := time.Now() template := x509.Certificate{ SerialNumber: new(big.Int).SetInt64(0), @@ -39,8 +41,8 @@ func main() { CommonName: *hostName, Organization: []string{"Acme Co"}, }, - NotBefore: time.SecondsToUTC(now - 300), - NotAfter: time.SecondsToUTC(now + 60*60*24*365), // valid for 1 year. + NotBefore: now.Add(-5 * time.Minute).UTC(), + NotAfter: now.AddDate(1, 0, 0).UTC(), // valid for 1 year. SubjectKeyId: []byte{1, 2, 3, 4}, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, diff --git a/src/pkg/crypto/tls/handshake_client.go b/src/pkg/crypto/tls/handshake_client.go index 15604cea7..2877f1738 100644 --- a/src/pkg/crypto/tls/handshake_client.go +++ b/src/pkg/crypto/tls/handshake_client.go @@ -5,16 +5,18 @@ package tls import ( + "bytes" "crypto" "crypto/rsa" "crypto/subtle" "crypto/x509" + "errors" "io" - "os" + "strconv" ) -func (c *Conn) clientHandshake() os.Error { - finishedHash := newFinishedHash() +func (c *Conn) clientHandshake() error { + finishedHash := newFinishedHash(versionTLS10) if c.config == nil { c.config = defaultConfig() @@ -32,7 +34,7 @@ func (c *Conn) clientHandshake() os.Error { nextProtoNeg: len(c.config.NextProtos) > 0, } - t := uint32(c.config.time()) + t := uint32(c.config.time().Unix()) hello.random[0] = byte(t >> 24) hello.random[1] = byte(t >> 16) hello.random[2] = byte(t >> 8) @@ -40,7 +42,7 @@ func (c *Conn) clientHandshake() os.Error { _, err := io.ReadFull(c.config.rand(), hello.random[4:]) if err != nil { c.sendAlert(alertInternalError) - return os.NewError("short read from Rand") + return errors.New("short read from Rand") } finishedHash.Write(hello.marshal()) @@ -57,7 +59,8 @@ func (c *Conn) clientHandshake() os.Error { finishedHash.Write(serverHello.marshal()) vers, ok := mutualVersion(serverHello.vers) - if !ok { + if !ok || vers < versionTLS10 { + // TLS 1.0 is the minimum version supported as a client. return c.sendAlert(alertProtocolVersion) } c.vers = vers @@ -69,10 +72,10 @@ func (c *Conn) clientHandshake() os.Error { if !hello.nextProtoNeg && serverHello.nextProtoNeg { c.sendAlert(alertHandshakeFailure) - return os.NewError("server advertised unrequested NPN") + return errors.New("server advertised unrequested NPN") } - suite, suiteId := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite) + suite := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite) if suite == nil { return c.sendAlert(alertHandshakeFailure) } @@ -92,14 +95,12 @@ func (c *Conn) clientHandshake() os.Error { cert, err := x509.ParseCertificate(asn1Data) if err != nil { c.sendAlert(alertBadCertificate) - return os.NewError("failed to parse certificate from server: " + err.String()) + return errors.New("failed to parse certificate from server: " + err.Error()) } certs[i] = cert } - // If we don't have a root CA set configured then anything is accepted. - // TODO(rsc): Find certificates for OS X 10.6. - if c.config.RootCAs != nil { + if !c.config.InsecureSkipVerify { opts := x509.VerifyOptions{ Roots: c.config.RootCAs, CurrentTime: c.config.time(), @@ -164,10 +165,26 @@ func (c *Conn) clientHandshake() os.Error { } } - transmitCert := false + var certToSend *Certificate + var certRequested bool certReq, ok := msg.(*certificateRequestMsg) if ok { - // We only accept certificates with RSA keys. + certRequested = true + + // RFC 4346 on the certificateAuthorities field: + // A list of the distinguished names of acceptable certificate + // authorities. These distinguished names may specify a desired + // distinguished name for a root CA or for a subordinate CA; + // thus, this message can be used to describe both known roots + // and a desired authorization space. If the + // certificate_authorities list is empty then the client MAY + // send any certificate of the appropriate + // ClientCertificateType, unless there is some external + // arrangement to the contrary. + + finishedHash.Write(certReq.marshal()) + + // For now, we only know how to sign challenges with RSA rsaAvail := false for _, certType := range certReq.certificateTypes { if certType == certTypeRSASign { @@ -176,23 +193,41 @@ func (c *Conn) clientHandshake() os.Error { } } - // For now, only send a certificate back if the server gives us an - // empty list of certificateAuthorities. - // - // RFC 4346 on the certificateAuthorities field: - // A list of the distinguished names of acceptable certificate - // authorities. These distinguished names may specify a desired - // distinguished name for a root CA or for a subordinate CA; thus, - // this message can be used to describe both known roots and a - // desired authorization space. If the certificate_authorities - // list is empty then the client MAY send any certificate of the - // appropriate ClientCertificateType, unless there is some - // external arrangement to the contrary. - if rsaAvail && len(certReq.certificateAuthorities) == 0 { - transmitCert = true - } + // We need to search our list of client certs for one + // where SignatureAlgorithm is RSA and the Issuer is in + // certReq.certificateAuthorities + findCert: + for i, cert := range c.config.Certificates { + if !rsaAvail { + continue + } - finishedHash.Write(certReq.marshal()) + leaf := cert.Leaf + if leaf == nil { + if leaf, err = x509.ParseCertificate(cert.Certificate[0]); err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) + } + } + + if leaf.PublicKeyAlgorithm != x509.RSA { + continue + } + + if len(certReq.certificateAuthorities) == 0 { + // they gave us an empty list, so just take the + // first RSA cert from c.config.Certificates + certToSend = &cert + break + } + + for _, ca := range certReq.certificateAuthorities { + if bytes.Equal(leaf.RawIssuer, ca) { + certToSend = &cert + break findCert + } + } + } msg, err = c.readHandshake() if err != nil { @@ -206,16 +241,13 @@ func (c *Conn) clientHandshake() os.Error { } finishedHash.Write(shd.marshal()) - var cert *x509.Certificate - if transmitCert { + // If the server requested a certificate then we have to send a + // Certificate message, even if it's empty because we don't have a + // certificate to send. + if certRequested { certMsg = new(certificateMsg) - if len(c.config.Certificates) > 0 { - cert, err = x509.ParseCertificate(c.config.Certificates[0].Certificate[0]) - if err == nil && cert.PublicKeyAlgorithm == x509.RSA { - certMsg.certificates = c.config.Certificates[0].Certificate - } else { - cert = nil - } + if certToSend != nil { + certMsg.certificates = certToSend.Certificate } finishedHash.Write(certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal()) @@ -231,12 +263,12 @@ func (c *Conn) clientHandshake() os.Error { c.writeRecord(recordTypeHandshake, ckx.marshal()) } - if cert != nil { + if certToSend != nil { certVerify := new(certificateVerifyMsg) - var digest [36]byte - copy(digest[0:16], finishedHash.serverMD5.Sum()) - copy(digest[16:36], finishedHash.serverSHA1.Sum()) - signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey, crypto.MD5SHA1, digest[0:]) + digest := make([]byte, 0, 36) + digest = finishedHash.serverMD5.Sum(digest) + digest = finishedHash.serverSHA1.Sum(digest) + signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, digest) if err != nil { return c.sendAlert(alertInternalError) } @@ -247,11 +279,11 @@ func (c *Conn) clientHandshake() os.Error { } masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := - keysFromPreMasterSecret10(preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) + keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) - clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */ ) - clientHash := suite.mac(clientMAC) - c.out.prepareCipherSpec(clientCipher, clientHash) + clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */) + clientHash := suite.mac(c.vers, clientMAC) + c.out.prepareCipherSpec(c.vers, clientCipher, clientHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) if serverHello.nextProtoNeg { @@ -270,9 +302,9 @@ func (c *Conn) clientHandshake() os.Error { finishedHash.Write(finished.marshal()) c.writeRecord(recordTypeHandshake, finished.marshal()) - serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */ ) - serverHash := suite.mac(serverMAC) - c.in.prepareCipherSpec(serverCipher, serverHash) + serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */) + serverHash := suite.mac(c.vers, serverMAC) + c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) c.readRecord(recordTypeChangeCipherSpec) if c.err != nil { return c.err @@ -294,7 +326,7 @@ func (c *Conn) clientHandshake() os.Error { } c.handshakeComplete = true - c.cipherSuite = suiteId + c.cipherSuite = suite.id return nil } diff --git a/src/pkg/crypto/tls/handshake_client_test.go b/src/pkg/crypto/tls/handshake_client_test.go index 3f91c7acf..8c56daaf6 100644 --- a/src/pkg/crypto/tls/handshake_client_test.go +++ b/src/pkg/crypto/tls/handshake_client_test.go @@ -18,6 +18,7 @@ func testClientScript(t *testing.T, name string, clientScript [][]byte, config * go func() { cli.Write([]byte("hello\n")) cli.Close() + c.Close() }() defer c.Close() @@ -61,7 +62,7 @@ func TestRunClient(t *testing.T) { // Script of interaction with gnutls implementation. // The values for this test are obtained by building and running in client mode: -// % gotest -test.run "TestRunClient" -connect +// % 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 diff --git a/src/pkg/crypto/tls/handshake_messages.go b/src/pkg/crypto/tls/handshake_messages.go index 6645adce4..e1517cc79 100644 --- a/src/pkg/crypto/tls/handshake_messages.go +++ b/src/pkg/crypto/tls/handshake_messages.go @@ -4,6 +4,8 @@ package tls +import "bytes" + type clientHelloMsg struct { raw []byte vers uint16 @@ -18,6 +20,25 @@ type clientHelloMsg struct { supportedPoints []uint8 } +func (m *clientHelloMsg) equal(i interface{}) bool { + m1, ok := i.(*clientHelloMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.vers == m1.vers && + bytes.Equal(m.random, m1.random) && + bytes.Equal(m.sessionId, m1.sessionId) && + eqUint16s(m.cipherSuites, m1.cipherSuites) && + bytes.Equal(m.compressionMethods, m1.compressionMethods) && + m.nextProtoNeg == m1.nextProtoNeg && + m.serverName == m1.serverName && + m.ocspStapling == m1.ocspStapling && + eqUint16s(m.supportedCurves, m1.supportedCurves) && + bytes.Equal(m.supportedPoints, m1.supportedPoints) +} + func (m *clientHelloMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -309,6 +330,23 @@ type serverHelloMsg struct { ocspStapling bool } +func (m *serverHelloMsg) equal(i interface{}) bool { + m1, ok := i.(*serverHelloMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.vers == m1.vers && + bytes.Equal(m.random, m1.random) && + bytes.Equal(m.sessionId, m1.sessionId) && + m.cipherSuite == m1.cipherSuite && + m.compressionMethod == m1.compressionMethod && + m.nextProtoNeg == m1.nextProtoNeg && + eqStrings(m.nextProtos, m1.nextProtos) && + m.ocspStapling == m1.ocspStapling +} + func (m *serverHelloMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -463,6 +501,16 @@ type certificateMsg struct { certificates [][]byte } +func (m *certificateMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + eqByteSlices(m.certificates, m1.certificates) +} + func (m *certificateMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -540,6 +588,16 @@ type serverKeyExchangeMsg struct { key []byte } +func (m *serverKeyExchangeMsg) equal(i interface{}) bool { + m1, ok := i.(*serverKeyExchangeMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.key, m1.key) +} + func (m *serverKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -571,6 +629,17 @@ type certificateStatusMsg struct { response []byte } +func (m *certificateStatusMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateStatusMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.statusType == m1.statusType && + bytes.Equal(m.response, m1.response) +} + func (m *certificateStatusMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -622,6 +691,11 @@ func (m *certificateStatusMsg) unmarshal(data []byte) bool { type serverHelloDoneMsg struct{} +func (m *serverHelloDoneMsg) equal(i interface{}) bool { + _, ok := i.(*serverHelloDoneMsg) + return ok +} + func (m *serverHelloDoneMsg) marshal() []byte { x := make([]byte, 4) x[0] = typeServerHelloDone @@ -637,6 +711,16 @@ type clientKeyExchangeMsg struct { ciphertext []byte } +func (m *clientKeyExchangeMsg) equal(i interface{}) bool { + m1, ok := i.(*clientKeyExchangeMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.ciphertext, m1.ciphertext) +} + func (m *clientKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -671,14 +755,24 @@ type finishedMsg struct { verifyData []byte } +func (m *finishedMsg) equal(i interface{}) bool { + m1, ok := i.(*finishedMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.verifyData, m1.verifyData) +} + func (m *finishedMsg) marshal() (x []byte) { if m.raw != nil { return m.raw } - x = make([]byte, 16) + x = make([]byte, 4+len(m.verifyData)) x[0] = typeFinished - x[3] = 12 + x[3] = byte(len(m.verifyData)) copy(x[4:], m.verifyData) m.raw = x return @@ -686,7 +780,7 @@ func (m *finishedMsg) marshal() (x []byte) { func (m *finishedMsg) unmarshal(data []byte) bool { m.raw = data - if len(data) != 4+12 { + if len(data) < 4 { return false } m.verifyData = data[4:] @@ -698,6 +792,16 @@ type nextProtoMsg struct { proto string } +func (m *nextProtoMsg) equal(i interface{}) bool { + m1, ok := i.(*nextProtoMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.proto == m1.proto +} + func (m *nextProtoMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -759,6 +863,17 @@ type certificateRequestMsg struct { certificateAuthorities [][]byte } +func (m *certificateRequestMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateRequestMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.certificateTypes, m1.certificateTypes) && + eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) +} + func (m *certificateRequestMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -766,9 +881,11 @@ func (m *certificateRequestMsg) marshal() (x []byte) { // See http://tools.ietf.org/html/rfc4346#section-7.4.4 length := 1 + len(m.certificateTypes) + 2 + casLength := 0 for _, ca := range m.certificateAuthorities { - length += 2 + len(ca) + casLength += 2 + len(ca) } + length += casLength x = make([]byte, 4+length) x[0] = typeCertificateRequest @@ -780,10 +897,8 @@ func (m *certificateRequestMsg) marshal() (x []byte) { copy(x[5:], m.certificateTypes) y := x[5+len(m.certificateTypes):] - - numCA := len(m.certificateAuthorities) - y[0] = uint8(numCA >> 8) - y[1] = uint8(numCA) + y[0] = uint8(casLength >> 8) + y[1] = uint8(casLength) y = y[2:] for _, ca := range m.certificateAuthorities { y[0] = uint8(len(ca) >> 8) @@ -794,7 +909,6 @@ func (m *certificateRequestMsg) marshal() (x []byte) { } m.raw = x - return } @@ -822,31 +936,34 @@ func (m *certificateRequestMsg) unmarshal(data []byte) bool { } data = data[numCertTypes:] + if len(data) < 2 { return false } - - numCAs := uint16(data[0])<<16 | uint16(data[1]) + casLength := uint16(data[0])<<8 | uint16(data[1]) data = data[2:] + if len(data) < int(casLength) { + return false + } + cas := make([]byte, casLength) + copy(cas, data) + data = data[casLength:] - m.certificateAuthorities = make([][]byte, numCAs) - for i := uint16(0); i < numCAs; i++ { - if len(data) < 2 { + m.certificateAuthorities = nil + for len(cas) > 0 { + if len(cas) < 2 { return false } - caLen := uint16(data[0])<<16 | uint16(data[1]) + caLen := uint16(cas[0])<<8 | uint16(cas[1]) + cas = cas[2:] - data = data[2:] - if len(data) < int(caLen) { + if len(cas) < int(caLen) { return false } - ca := make([]byte, caLen) - copy(ca, data) - m.certificateAuthorities[i] = ca - data = data[caLen:] + m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen]) + cas = cas[caLen:] } - if len(data) > 0 { return false } @@ -859,6 +976,16 @@ type certificateVerifyMsg struct { signature []byte } +func (m *certificateVerifyMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateVerifyMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.signature, m1.signature) +} + func (m *certificateVerifyMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -902,3 +1029,39 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool { return true } + +func eqUint16s(x, y []uint16) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if y[i] != v { + return false + } + } + return true +} + +func eqStrings(x, y []string) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if y[i] != v { + return false + } + } + return true +} + +func eqByteSlices(x, y [][]byte) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if !bytes.Equal(v, y[i]) { + return false + } + } + return true +} diff --git a/src/pkg/crypto/tls/handshake_messages_test.go b/src/pkg/crypto/tls/handshake_messages_test.go index 23f729dd9..e62a9d581 100644 --- a/src/pkg/crypto/tls/handshake_messages_test.go +++ b/src/pkg/crypto/tls/handshake_messages_test.go @@ -5,7 +5,7 @@ package tls import ( - "rand" + "math/rand" "reflect" "testing" "testing/quick" @@ -14,23 +14,25 @@ import ( var tests = []interface{}{ &clientHelloMsg{}, &serverHelloMsg{}, + &finishedMsg{}, &certificateMsg{}, &certificateRequestMsg{}, &certificateVerifyMsg{}, &certificateStatusMsg{}, &clientKeyExchangeMsg{}, - &finishedMsg{}, &nextProtoMsg{}, } type testMessage interface { marshal() []byte unmarshal([]byte) bool + equal(interface{}) bool } func TestMarshalUnmarshal(t *testing.T) { rand := rand.New(rand.NewSource(0)) + for i, iface := range tests { ty := reflect.ValueOf(iface).Type() @@ -54,16 +56,17 @@ func TestMarshalUnmarshal(t *testing.T) { } m2.marshal() // to fill any marshal cache in the message - if !reflect.DeepEqual(m1, m2) { + if !m1.equal(m2) { t.Errorf("#%d got:%#v want:%#v %x", i, m2, m1, marshaled) break } - if i >= 2 { - // The first two message types (ClientHello and - // ServerHello) are allowed to have parsable - // prefixes because the extension data is - // optional. + if i >= 3 { + // The first three message types (ClientHello, + // ServerHello and Finished) are allowed to + // have parsable prefixes because the extension + // data is optional and the length of the + // Finished varies across versions. for j := 0; j < len(marshaled); j++ { if m2.unmarshal(marshaled[0:j]) { t.Errorf("#%d unmarshaled a prefix of length %d of %#v", i, j, m1) diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go index 44a324041..77e56a754 100644 --- a/src/pkg/crypto/tls/handshake_server.go +++ b/src/pkg/crypto/tls/handshake_server.go @@ -9,11 +9,11 @@ import ( "crypto/rsa" "crypto/subtle" "crypto/x509" + "errors" "io" - "os" ) -func (c *Conn) serverHandshake() os.Error { +func (c *Conn) serverHandshake() error { config := c.config msg, err := c.readHandshake() if err != nil { @@ -30,7 +30,7 @@ func (c *Conn) serverHandshake() os.Error { c.vers = vers c.haveVers = true - finishedHash := newFinishedHash() + finishedHash := newFinishedHash(vers) finishedHash.Write(clientHello.marshal()) hello := new(serverHelloMsg) @@ -56,18 +56,27 @@ Curves: ellipticOk := supportedCurve && supportedPointFormat var suite *cipherSuite - var suiteId uint16 FindCipherSuite: for _, id := range clientHello.cipherSuites { for _, supported := range config.cipherSuites() { if id == supported { - suite = cipherSuites[id] + 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 suite.elliptic && !ellipticOk { + if candidate.elliptic && !ellipticOk { continue } - suiteId = id + suite = candidate break FindCipherSuite } } @@ -87,8 +96,8 @@ FindCipherSuite: } hello.vers = vers - hello.cipherSuite = suiteId - t := uint32(config.time()) + hello.cipherSuite = suite.id + t := uint32(config.time().Unix()) hello.random = make([]byte, 32) hello.random[0] = byte(t >> 24) hello.random[1] = byte(t >> 16) @@ -115,7 +124,12 @@ FindCipherSuite: } certMsg := new(certificateMsg) - certMsg.certificates = config.Certificates[0].Certificate + if len(clientHello.serverName) > 0 { + c.serverName = clientHello.serverName + certMsg.certificates = config.getCertificateForName(clientHello.serverName).Certificate + } else { + certMsg.certificates = config.Certificates[0].Certificate + } finishedHash.Write(certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal()) @@ -128,7 +142,6 @@ FindCipherSuite: } keyAgreement := suite.ka() - skx, err := keyAgreement.generateServerKeyExchange(config, clientHello, hello) if err != nil { c.sendAlert(alertHandshakeFailure) @@ -139,14 +152,19 @@ FindCipherSuite: c.writeRecord(recordTypeHandshake, skx.marshal()) } - if config.AuthenticateClient { + if config.ClientAuth >= RequestClientCert { // Request a client certificate certReq := new(certificateRequestMsg) certReq.certificateTypes = []byte{certTypeRSASign} + // An empty list of certificateAuthorities signals to // the client that it may send any certificate in response - // to our request. - + // to our request. When we know the CAs we trust, then + // we can send them down, so that the client can choose + // an appropriate certificate to give to us. + if config.ClientCAs != nil { + certReq.certificateAuthorities = config.ClientCAs.Subjects() + } finishedHash.Write(certReq.marshal()) c.writeRecord(recordTypeHandshake, certReq.marshal()) } @@ -155,52 +173,87 @@ FindCipherSuite: finishedHash.Write(helloDone.marshal()) c.writeRecord(recordTypeHandshake, helloDone.marshal()) - var pub *rsa.PublicKey - if config.AuthenticateClient { - // Get client certificate - msg, err = c.readHandshake() - if err != nil { - return err - } - certMsg, ok = msg.(*certificateMsg) - if !ok { - return c.sendAlert(alertUnexpectedMessage) + var pub *rsa.PublicKey // public key for client auth, if any + + msg, err = c.readHandshake() + if err != nil { + return err + } + + // 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()) + if len(certMsg.certificates) == 0 { + // The client didn't actually send a certificate + switch config.ClientAuth { + case RequireAnyClientCert, RequireAndVerifyClientCert: + c.sendAlert(alertBadCertificate) + return errors.New("tls: client didn't provide a certificate") + } + } + certs := make([]*x509.Certificate, len(certMsg.certificates)) for i, asn1Data := range certMsg.certificates { - cert, err := x509.ParseCertificate(asn1Data) - if err != nil { + if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { c.sendAlert(alertBadCertificate) - return os.NewError("could not parse client's certificate: " + err.String()) + return errors.New("tls: failed to parse client certificate: " + err.Error()) } - certs[i] = cert } - // TODO(agl): do better validation of certs: max path length, name restrictions etc. - for i := 1; i < len(certs); i++ { - if err := certs[i-1].CheckSignatureFrom(certs[i]); err != nil { + 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 os.NewError("could not validate certificate signature: " + err.String()) + 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 { - key, ok := certs[0].PublicKey.(*rsa.PublicKey) - if !ok { + if pub, ok = certs[0].PublicKey.(*rsa.PublicKey); !ok { return c.sendAlert(alertUnsupportedCertificate) } - pub = key c.peerCertificates = certs } + + msg, err = c.readHandshake() + if err != nil { + return err + } } // Get client key exchange - msg, err = c.readHandshake() - if err != nil { - return err - } ckx, ok := msg.(*clientKeyExchangeMsg) if !ok { return c.sendAlert(alertUnexpectedMessage) @@ -223,30 +276,30 @@ FindCipherSuite: return c.sendAlert(alertUnexpectedMessage) } - digest := make([]byte, 36) - copy(digest[0:16], finishedHash.serverMD5.Sum()) - copy(digest[16:36], finishedHash.serverSHA1.Sum()) + digest := make([]byte, 0, 36) + digest = finishedHash.serverMD5.Sum(digest) + digest = finishedHash.serverSHA1.Sum(digest) err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature) if err != nil { c.sendAlert(alertBadCertificate) - return os.NewError("could not validate signature of connection nonces: " + err.String()) + return errors.New("could not validate signature of connection nonces: " + err.Error()) } finishedHash.Write(certVerify.marshal()) } - preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx) + preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx, c.vers) if err != nil { c.sendAlert(alertHandshakeFailure) return err } masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := - keysFromPreMasterSecret10(preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) + keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) - clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */ ) - clientHash := suite.mac(clientMAC) - c.in.prepareCipherSpec(clientCipher, clientHash) + clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */) + clientHash := suite.mac(c.vers, clientMAC) + c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) c.readRecord(recordTypeChangeCipherSpec) if err := c.error(); err != nil { return err @@ -282,9 +335,9 @@ FindCipherSuite: finishedHash.Write(clientFinished.marshal()) - serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */ ) - serverHash := suite.mac(serverMAC) - c.out.prepareCipherSpec(serverCipher, serverHash) + 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) @@ -292,7 +345,7 @@ FindCipherSuite: c.writeRecord(recordTypeHandshake, finished.marshal()) c.handshakeComplete = true - c.cipherSuite = suiteId + c.cipherSuite = suite.id return nil } diff --git a/src/pkg/crypto/tls/handshake_server_test.go b/src/pkg/crypto/tls/handshake_server_test.go index b77646e43..08a0ccb09 100644 --- a/src/pkg/crypto/tls/handshake_server_test.go +++ b/src/pkg/crypto/tls/handshake_server_test.go @@ -5,20 +5,25 @@ package tls import ( - "big" "bytes" "crypto/rsa" + "crypto/x509" "encoding/hex" + "encoding/pem" "flag" "io" + "log" + "math/big" "net" - "os" + "strconv" + "strings" "testing" + "time" ) type zeroSource struct{} -func (zeroSource) Read(b []byte) (n int, err os.Error) { +func (zeroSource) Read(b []byte) (n int, err error) { for i := range b { b[i] = 0 } @@ -30,15 +35,16 @@ var testConfig *Config func init() { testConfig = new(Config) - testConfig.Time = func() int64 { return 0 } + testConfig.Time = func() time.Time { return time.Unix(0, 0) } testConfig.Rand = zeroSource{} testConfig.Certificates = make([]Certificate, 1) testConfig.Certificates[0].Certificate = [][]byte{testCertificate} testConfig.Certificates[0].PrivateKey = testPrivateKey testConfig.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA} + testConfig.InsecureSkipVerify = true } -func testClientHelloFailure(t *testing.T, m handshakeMessage, expected os.Error) { +func testClientHelloFailure(t *testing.T, m handshakeMessage, expected error) { // Create in-memory network connection, // send message to server. Should return // expected error. @@ -53,7 +59,7 @@ func testClientHelloFailure(t *testing.T, m handshakeMessage, expected os.Error) }() err := Server(s, testConfig).Handshake() s.Close() - if e, ok := err.(*net.OpError); !ok || e.Error != expected { + if e, ok := err.(*net.OpError); !ok || e.Err != expected { t.Errorf("Got error: %s; expected: %s", err, expected) } } @@ -62,7 +68,7 @@ func TestSimpleError(t *testing.T) { testClientHelloFailure(t, &serverHelloDoneMsg{}, alertUnexpectedMessage) } -var badProtocolVersions = []uint16{0x0000, 0x0005, 0x0100, 0x0105, 0x0200, 0x0205, 0x0300} +var badProtocolVersions = []uint16{0x0000, 0x0005, 0x0100, 0x0105, 0x0200, 0x0205} func TestRejectBadProtocolVersion(t *testing.T) { for _, v := range badProtocolVersions { @@ -90,7 +96,7 @@ func TestAlertForwarding(t *testing.T) { err := Server(s, testConfig).Handshake() s.Close() - if e, ok := err.(*net.OpError); !ok || e.Error != os.Error(alertUnknownCA) { + if e, ok := err.(*net.OpError); !ok || e.Err != error(alertUnknownCA) { t.Errorf("Got error: %s; expected: %s", err, alertUnknownCA) } } @@ -101,54 +107,115 @@ func TestClose(t *testing.T) { err := Server(s, testConfig).Handshake() s.Close() - if err != os.EOF { - t.Errorf("Got error: %s; expected: %s", err, os.EOF) + if err != io.EOF { + t.Errorf("Got error: %s; expected: %s", err, io.EOF) } } -func testServerScript(t *testing.T, name string, serverScript [][]byte, config *Config) { +func testServerScript(t *testing.T, name string, serverScript [][]byte, config *Config, peers []*x509.Certificate) { c, s := net.Pipe() srv := Server(s, config) + pchan := make(chan []*x509.Certificate, 1) go func() { srv.Write([]byte("hello, world\n")) srv.Close() + s.Close() + st := srv.ConnectionState() + pchan <- st.PeerCertificates }() - defer c.Close() for i, b := range serverScript { if i%2 == 0 { c.Write(b) continue } bb := make([]byte, len(b)) - _, err := io.ReadFull(c, bb) + n, err := io.ReadFull(c, bb) if err != nil { - t.Fatalf("%s #%d: %s", name, i, err) + t.Fatalf("%s #%d: %s\nRead %d, wanted %d, got %x, wanted %x\n", name, i, err, n, len(bb), bb[:n], b) } if !bytes.Equal(b, bb) { t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", name, i, bb, b) } } + c.Close() + + if peers != nil { + gotpeers := <-pchan + if len(peers) == len(gotpeers) { + for i := range peers { + if !peers[i].Equal(gotpeers[i]) { + t.Fatalf("%s: mismatch on peer cert %d", name, i) + } + } + } else { + t.Fatalf("%s: mismatch on peer list length: %d (wanted) != %d (got)", name, len(peers), len(gotpeers)) + } + } } func TestHandshakeServerRC4(t *testing.T) { - testServerScript(t, "RC4", rc4ServerScript, testConfig) + testServerScript(t, "RC4", rc4ServerScript, testConfig, nil) +} + +func TestHandshakeServer3DES(t *testing.T) { + des3Config := new(Config) + *des3Config = *testConfig + des3Config.CipherSuites = []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA} + testServerScript(t, "3DES", des3ServerScript, des3Config, nil) } func TestHandshakeServerAES(t *testing.T) { aesConfig := new(Config) *aesConfig = *testConfig aesConfig.CipherSuites = []uint16{TLS_RSA_WITH_AES_128_CBC_SHA} - testServerScript(t, "AES", aesServerScript, aesConfig) + testServerScript(t, "AES", aesServerScript, aesConfig, nil) +} + +func TestHandshakeServerSSLv3(t *testing.T) { + testServerScript(t, "SSLv3", sslv3ServerScript, testConfig, nil) +} + +type clientauthTest struct { + name string + clientauth ClientAuthType + peers []*x509.Certificate + script [][]byte +} + +func TestClientAuth(t *testing.T) { + for _, cat := range clientauthTests { + t.Log("running", cat.name) + cfg := new(Config) + *cfg = *testConfig + cfg.ClientAuth = cat.clientauth + testServerScript(t, cat.name, cat.script, cfg, cat.peers) + } } 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 } + suites := strings.Split(*testCipherSuites, ",") + testConfig.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) + } + + testConfig.ClientAuth = ClientAuthType(*testClientAuth) + l, err := Listen("tcp", ":10443", testConfig) if err != nil { t.Fatal(err) @@ -157,13 +224,23 @@ func TestRunServer(t *testing.T) { for { c, err := l.Accept() if err != nil { + log.Printf("error from TLS handshake: %s", err) break } + _, err = c.Write([]byte("hello, world\n")) if err != nil { - t.Errorf("error from TLS: %s", err) - break + log.Printf("error from TLS: %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") } + c.Close() } } @@ -193,31 +270,42 @@ var testPrivateKey = &rsa.PrivateKey{ }, } +func loadPEMCert(in string) *x509.Certificate { + block, _ := pem.Decode([]byte(in)) + if block.Type == "CERTIFICATE" && len(block.Headers) == 0 { + cert, err := x509.ParseCertificate(block.Bytes) + if err == nil { + return cert + } + panic("error parsing cert") + } + panic("error parsing PEM") +} + // Script of interaction with gnutls implementation. // The values for this test are obtained by building and running in server mode: -// % gotest -test.run "TestRunServer" -serve +// % 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 var rc4ServerScript = [][]byte{ { - 0x16, 0x03, 0x02, 0x00, 0x7f, 0x01, 0x00, 0x00, - 0x7b, 0x03, 0x02, 0x4d, 0x08, 0x1f, 0x5a, 0x7a, - 0x0a, 0x92, 0x2f, 0xf0, 0x73, 0x16, 0x3a, 0x88, - 0x14, 0x85, 0x4c, 0x98, 0x15, 0x7b, 0x65, 0xe0, - 0x78, 0xd0, 0xed, 0xd0, 0xf3, 0x65, 0x20, 0xeb, - 0x80, 0xd1, 0x0b, 0x00, 0x00, 0x34, 0x00, 0x33, + 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, 0x1e, 0x00, 0x09, + 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, 0xff, - 0x01, 0x00, 0x01, 0x00, + 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, }, { @@ -321,38 +409,219 @@ var rc4ServerScript = [][]byte{ { 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, - 0x82, 0x00, 0x80, 0x3c, 0x13, 0xd7, 0x12, 0xc1, - 0x6a, 0xf0, 0x3f, 0x8c, 0xa1, 0x35, 0x5d, 0xc5, - 0x89, 0x1e, 0x9e, 0xcd, 0x32, 0xc7, 0x9e, 0xe6, - 0xae, 0xd5, 0xf1, 0xbf, 0x70, 0xd7, 0xa9, 0xef, - 0x2c, 0x4c, 0xf4, 0x22, 0xbc, 0x17, 0x17, 0xaa, - 0x05, 0xf3, 0x9f, 0x80, 0xf2, 0xe9, 0x82, 0x2f, - 0x2a, 0x15, 0x54, 0x0d, 0x16, 0x0e, 0x77, 0x4c, - 0x28, 0x3c, 0x03, 0x2d, 0x2d, 0xd7, 0xc8, 0x64, - 0xd9, 0x59, 0x4b, 0x1c, 0xf4, 0xde, 0xff, 0x2f, - 0xbc, 0x94, 0xaf, 0x18, 0x26, 0x37, 0xce, 0x4f, - 0x84, 0x74, 0x2e, 0x45, 0x66, 0x7c, 0x0c, 0x54, - 0x46, 0x36, 0x5f, 0x65, 0x21, 0x7b, 0x83, 0x8c, - 0x6d, 0x76, 0xcd, 0x0d, 0x9f, 0xda, 0x1c, 0xa4, - 0x6e, 0xfe, 0xb1, 0xf7, 0x09, 0x0d, 0xfb, 0x74, - 0x66, 0x34, 0x99, 0x89, 0x7f, 0x5f, 0x77, 0x87, - 0x4a, 0x66, 0x4b, 0xa9, 0x59, 0x57, 0xe3, 0x56, - 0x0d, 0xdd, 0xd8, 0x14, 0x03, 0x01, 0x00, 0x01, - 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xc0, 0x4e, - 0xd3, 0x0f, 0xb5, 0xc0, 0x57, 0xa6, 0x18, 0x80, - 0x80, 0x6b, 0x49, 0xfe, 0xbd, 0x3a, 0x7a, 0x2c, - 0xef, 0x70, 0xb5, 0x1c, 0xd2, 0xdf, 0x5f, 0x78, - 0x5a, 0xd8, 0x4f, 0xa0, 0x95, 0xb4, 0xb3, 0xb5, - 0xaa, 0x3b, + 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, + }, + + { + 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, + }, +} + +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, 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, 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, + 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, 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, }, { 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x24, 0x9d, 0xc9, 0xda, 0xdf, 0xeb, - 0xc8, 0xdb, 0xf8, 0x94, 0xa5, 0xef, 0xd5, 0xfc, - 0x89, 0x01, 0x64, 0x30, 0x77, 0x5a, 0x18, 0x4b, - 0x16, 0x79, 0x9c, 0xf6, 0xf5, 0x09, 0x22, 0x12, - 0x4c, 0x3e, 0xa8, 0x8e, 0x91, 0xa5, 0x24, + 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, }, } @@ -515,3 +784,787 @@ var aesServerScript = [][]byte{ 0xcd, 0x84, 0xf0, }, } + +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, 0x2a, 0x02, 0x00, 0x00, + 0x26, 0x03, 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, 0x00, 0x05, 0x00, 0x16, + 0x03, 0x00, 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, 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, + }, + + { + 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, + }, +} + +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, + }, + + { + 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, + }}}, + // 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, + }, + + { + 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, + }}}, + // 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{clicert}, + [][]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, + }, + + { + 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, + }}}, +} + +// cert.pem and key.pem were generated with generate_cert.go +// Thus, they have no ExtKeyUsage fields and trigger an error +// when verification is turned on. + +var clicert = loadPEMCert(` +-----BEGIN CERTIFICATE----- +MIIB7TCCAVigAwIBAgIBADALBgkqhkiG9w0BAQUwJjEQMA4GA1UEChMHQWNtZSBD +bzESMBAGA1UEAxMJMTI3LjAuMC4xMB4XDTExMTIwODA3NTUxMloXDTEyMTIwNzA4 +MDAxMlowJjEQMA4GA1UEChMHQWNtZSBDbzESMBAGA1UEAxMJMTI3LjAuMC4xMIGc +MAsGCSqGSIb3DQEBAQOBjAAwgYgCgYBO0Hsx44Jk2VnAwoekXh6LczPHY1PfZpIG +hPZk1Y/kNqcdK+izIDZFI7Xjla7t4PUgnI2V339aEu+H5Fto5OkOdOwEin/ekyfE +ARl6vfLcPRSr0FTKIQzQTW6HLlzF0rtNS0/Otiz3fojsfNcCkXSmHgwa2uNKWi7e +E5xMQIhZkwIDAQABozIwMDAOBgNVHQ8BAf8EBAMCAKAwDQYDVR0OBAYEBAECAwQw +DwYDVR0jBAgwBoAEAQIDBDALBgkqhkiG9w0BAQUDgYEANh+zegx1yW43RmEr1b3A +p0vMRpqBWHyFeSnIyMZn3TJWRSt1tukkqVCavh9a+hoV2cxVlXIWg7nCto/9iIw4 +hB2rXZIxE0/9gzvGnfERYraL7KtnvshksBFQRlgXa5kc0x38BvEO5ZaoDPl4ILdE +GFGNEH5PlGffo05wc46QkYU= +-----END CERTIFICATE----- +`) + +/* corresponding key.pem for cert.pem is: +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgE7QezHjgmTZWcDCh6ReHotzM8djU99mkgaE9mTVj+Q2px0r6LMg +NkUjteOVru3g9SCcjZXff1oS74fkW2jk6Q507ASKf96TJ8QBGXq98tw9FKvQVMoh +DNBNbocuXMXSu01LT862LPd+iOx81wKRdKYeDBra40paLt4TnExAiFmTAgMBAAEC +gYBxvXd8yNteFTns8A/2yomEMC4yeosJJSpp1CsN3BJ7g8/qTnrVPxBy+RU+qr63 +t2WquaOu/cr5P8iEsa6lk20tf8pjKLNXeX0b1RTzK8rJLbS7nGzP3tvOhL096VtQ +dAo4ROEaro0TzYpHmpciSvxVIeEIAAdFDObDJPKqcJAxyQJBAJizfYgK8Gzx9fsx +hxp+VteCbVPg2euASH5Yv3K5LukRdKoSzHE2grUVQgN/LafC0eZibRanxHegYSr7 +7qaswKUCQQCEIWor/X4XTMdVj3Oj+vpiw75y/S9gh682+myZL+d/02IEkwnB098P +RkKVpenBHyrGg0oeN5La7URILWKj7CPXAkBKo6F+d+phNjwIFoN1Xb/RA32w/D1I +saG9sF+UEhRt9AxUfW/U/tIQ9V0ZHHcSg1XaCM5Nvp934brdKdvTOKnJAkBD5h/3 +Rybatlvg/fzBEaJFyq09zhngkxlZOUtBVTqzl17RVvY2orgH02U4HbCHy4phxOn7 +qTdQRYlHRftgnWK1AkANibn9PRYJ7mJyJ9Dyj2QeNcSkSTzrt0tPvUMf4+meJymN +1Ntu5+S1DLLzfxlaljWG6ylW6DNxujCyuXIV2rvAMAA= +-----END RSA PRIVATE KEY----- +*/ diff --git a/src/pkg/crypto/tls/key_agreement.go b/src/pkg/crypto/tls/key_agreement.go index a40d18fd9..a931d8fb5 100644 --- a/src/pkg/crypto/tls/key_agreement.go +++ b/src/pkg/crypto/tls/key_agreement.go @@ -5,26 +5,26 @@ package tls import ( - "big" "crypto" "crypto/elliptic" "crypto/md5" "crypto/rsa" "crypto/sha1" "crypto/x509" + "errors" "io" - "os" + "math/big" ) // rsaKeyAgreement implements the standard TLS key agreement where the client // encrypts the pre-master secret to the server's public key. type rsaKeyAgreement struct{} -func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) { +func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { return nil, nil } -func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) { +func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { preMasterSecret := make([]byte, 48) _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) if err != nil { @@ -32,15 +32,19 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe } if len(ckx.ciphertext) < 2 { - return nil, os.NewError("bad ClientKeyExchange") + return nil, errors.New("bad ClientKeyExchange") } - ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) - if ciphertextLen != len(ckx.ciphertext)-2 { - return nil, os.NewError("bad ClientKeyExchange") + + ciphertext := ckx.ciphertext + if version != versionSSL30 { + ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) + if ciphertextLen != len(ckx.ciphertext)-2 { + return nil, errors.New("bad ClientKeyExchange") + } + ciphertext = ckx.ciphertext[2:] } - ciphertext := ckx.ciphertext[2:] - err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ciphertext, preMasterSecret) + err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret) if err != nil { return nil, err } @@ -53,11 +57,11 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe return preMasterSecret, nil } -func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) os.Error { - return os.NewError("unexpected ServerKeyExchange") +func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { + return errors.New("unexpected ServerKeyExchange") } -func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) { +func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { preMasterSecret := make([]byte, 48) preMasterSecret[0] = byte(clientHello.vers >> 8) preMasterSecret[1] = byte(clientHello.vers) @@ -86,13 +90,13 @@ func md5SHA1Hash(slices ...[]byte) []byte { for _, slice := range slices { hmd5.Write(slice) } - copy(md5sha1, hmd5.Sum()) + copy(md5sha1, hmd5.Sum(nil)) hsha1 := sha1.New() for _, slice := range slices { hsha1.Write(slice) } - copy(md5sha1[md5.Size:], hsha1.Sum()) + copy(md5sha1[md5.Size:], hsha1.Sum(nil)) return md5sha1 } @@ -101,11 +105,11 @@ func md5SHA1Hash(slices ...[]byte) []byte { // pre-master secret is then calculated using ECDH. type ecdheRSAKeyAgreement struct { privateKey []byte - curve *elliptic.Curve + curve elliptic.Curve x, y *big.Int } -func (ka *ecdheRSAKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) { +func (ka *ecdheRSAKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { var curveid uint16 Curve: @@ -126,13 +130,17 @@ Curve: } } + if curveid == 0 { + return nil, errors.New("tls: no supported elliptic curves offered") + } + var x, y *big.Int - var err os.Error - ka.privateKey, x, y, err = ka.curve.GenerateKey(config.rand()) + var err error + ka.privateKey, x, y, err = elliptic.GenerateKey(ka.curve, config.rand()) if err != nil { return nil, err } - ecdhePublic := ka.curve.Marshal(x, y) + ecdhePublic := elliptic.Marshal(ka.curve, x, y) // http://tools.ietf.org/html/rfc4492#section-5.4 serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic)) @@ -143,9 +151,9 @@ Curve: copy(serverECDHParams[4:], ecdhePublic) md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams) - sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey, crypto.MD5SHA1, md5sha1) + sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, md5sha1) if err != nil { - return nil, os.NewError("failed to sign ECDHE parameters: " + err.String()) + return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) } skx := new(serverKeyExchangeMsg) @@ -159,30 +167,30 @@ Curve: return skx, nil } -func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) { +func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { - return nil, os.NewError("bad ClientKeyExchange") + return nil, errors.New("bad ClientKeyExchange") } - x, y := ka.curve.Unmarshal(ckx.ciphertext[1:]) + x, y := elliptic.Unmarshal(ka.curve, ckx.ciphertext[1:]) if x == nil { - return nil, os.NewError("bad ClientKeyExchange") + return nil, errors.New("bad ClientKeyExchange") } x, _ = ka.curve.ScalarMult(x, y, ka.privateKey) - preMasterSecret := make([]byte, (ka.curve.BitSize+7)>>3) + preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3) xBytes := x.Bytes() copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) return preMasterSecret, nil } -var errServerKeyExchange = os.NewError("invalid ServerKeyExchange") +var errServerKeyExchange = errors.New("invalid ServerKeyExchange") -func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) os.Error { +func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { if len(skx.key) < 4 { return errServerKeyExchange } if skx.key[0] != 3 { // named curve - return os.NewError("server selected unsupported curve") + return errors.New("server selected unsupported curve") } curveid := uint16(skx.key[1])<<8 | uint16(skx.key[2]) @@ -194,14 +202,14 @@ func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientH case curveP521: ka.curve = elliptic.P521() default: - return os.NewError("server selected unsupported curve") + return errors.New("server selected unsupported curve") } publicLen := int(skx.key[3]) if publicLen+4 > len(skx.key) { return errServerKeyExchange } - ka.x, ka.y = ka.curve.Unmarshal(skx.key[4 : 4+publicLen]) + ka.x, ka.y = elliptic.Unmarshal(ka.curve, skx.key[4:4+publicLen]) if ka.x == nil { return errServerKeyExchange } @@ -221,20 +229,20 @@ func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientH return rsa.VerifyPKCS1v15(cert.PublicKey.(*rsa.PublicKey), crypto.MD5SHA1, md5sha1, sig) } -func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) { +func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { if ka.curve == nil { - return nil, nil, os.NewError("missing ServerKeyExchange message") + return nil, nil, errors.New("missing ServerKeyExchange message") } - priv, mx, my, err := ka.curve.GenerateKey(config.rand()) + priv, mx, my, err := elliptic.GenerateKey(ka.curve, config.rand()) if err != nil { return nil, nil, err } x, _ := ka.curve.ScalarMult(ka.x, ka.y, priv) - preMasterSecret := make([]byte, (ka.curve.BitSize+7)>>3) + preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3) xBytes := x.Bytes() copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) - serialized := ka.curve.Marshal(mx, my) + serialized := elliptic.Marshal(ka.curve, mx, my) ckx := new(clientKeyExchangeMsg) ckx.ciphertext = make([]byte, 1+len(serialized)) diff --git a/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py b/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py index c03eaa6ea..5692bd32f 100644 --- a/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py +++ b/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py @@ -35,6 +35,8 @@ for line in sys.stdin.readlines(): 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) diff --git a/src/pkg/crypto/tls/prf.go b/src/pkg/crypto/tls/prf.go index 478cf65f9..637ef03e2 100644 --- a/src/pkg/crypto/tls/prf.go +++ b/src/pkg/crypto/tls/prf.go @@ -9,7 +9,6 @@ import ( "crypto/md5" "crypto/sha1" "hash" - "os" ) // Split a premaster secret in two as specified in RFC 4346, section 5. @@ -23,14 +22,14 @@ func splitPreMasterSecret(secret []byte) (s1, s2 []byte) { func pHash(result, secret, seed []byte, hash func() hash.Hash) { h := hmac.New(hash, secret) h.Write(seed) - a := h.Sum() + a := h.Sum(nil) j := 0 for j < len(result) { h.Reset() h.Write(a) h.Write(seed) - b := h.Sum() + b := h.Sum(nil) todo := len(b) if j+todo > len(result) { todo = len(result) - j @@ -40,7 +39,7 @@ func pHash(result, secret, seed []byte, hash func() hash.Hash) { h.Reset() h.Write(a) - a = h.Sum() + a = h.Sum(nil) } } @@ -63,6 +62,39 @@ func pRF10(result, secret, label, seed []byte) { } } +// pRF30 implements the SSL 3.0 pseudo-random function, as defined in +// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6. +func pRF30(result, secret, label, seed []byte) { + hashSHA1 := sha1.New() + hashMD5 := md5.New() + + done := 0 + i := 0 + // RFC5246 section 6.3 says that the largest PRF output needed is 128 + // bytes. Since no more ciphersuites will be added to SSLv3, this will + // remain true. Each iteration gives us 16 bytes so 10 iterations will + // be sufficient. + var b [11]byte + for done < len(result) { + for j := 0; j <= i; j++ { + b[j] = 'A' + byte(i) + } + + hashSHA1.Reset() + hashSHA1.Write(b[:i+1]) + hashSHA1.Write(secret) + hashSHA1.Write(seed) + digest := hashSHA1.Sum(nil) + + hashMD5.Reset() + hashMD5.Write(secret) + hashMD5.Write(digest) + + done += copy(result[done:], hashMD5.Sum(nil)) + i++ + } +} + const ( tlsRandomLength = 32 // Length of a random nonce in TLS 1.1. masterSecretLength = 48 // Length of a master secret in TLS 1.1. @@ -77,19 +109,24 @@ 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 keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { +func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { + prf := pRF10 + if version == versionSSL30 { + prf = pRF30 + } + var seed [tlsRandomLength * 2]byte copy(seed[0:len(clientRandom)], clientRandom) copy(seed[len(clientRandom):], serverRandom) masterSecret = make([]byte, masterSecretLength) - pRF10(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) + prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) copy(seed[0:len(clientRandom)], serverRandom) copy(seed[len(serverRandom):], clientRandom) n := 2*macLen + 2*keyLen + 2*ivLen keyMaterial := make([]byte, n) - pRF10(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) + prf(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) clientMAC = keyMaterial[:macLen] keyMaterial = keyMaterial[macLen:] serverMAC = keyMaterial[:macLen] @@ -104,6 +141,10 @@ func keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byt return } +func newFinishedHash(version uint16) finishedHash { + return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New(), version} +} + // A finishedHash calculates the hash of a set of handshake messages suitable // for including in a Finished message. type finishedHash struct { @@ -111,13 +152,10 @@ type finishedHash struct { clientSHA1 hash.Hash serverMD5 hash.Hash serverSHA1 hash.Hash + version uint16 } -func newFinishedHash() finishedHash { - return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New()} -} - -func (h finishedHash) Write(msg []byte) (n int, err os.Error) { +func (h finishedHash) Write(msg []byte) (n int, err error) { h.clientMD5.Write(msg) h.clientSHA1.Write(msg) h.serverMD5.Write(msg) @@ -125,9 +163,10 @@ func (h finishedHash) Write(msg []byte) (n int, err os.Error) { return len(msg), nil } -// finishedSum calculates the contents of the verify_data member of a Finished -// message given the MD5 and SHA1 hashes of a set of handshake messages. -func finishedSum(md5, sha1, label, masterSecret []byte) []byte { +// finishedSum10 calculates the contents of the verify_data member of a TLSv1 +// Finished message given the MD5 and SHA1 hashes of a set of handshake +// messages. +func finishedSum10(md5, sha1, label, masterSecret []byte) []byte { seed := make([]byte, len(md5)+len(sha1)) copy(seed, md5) copy(seed[len(md5):], sha1) @@ -136,18 +175,61 @@ func finishedSum(md5, sha1, label, masterSecret []byte) []byte { return out } +// finishedSum30 calculates the contents of the verify_data member of a SSLv3 +// Finished message given the MD5 and SHA1 hashes of a set of handshake +// messages. +func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic [4]byte) []byte { + md5.Write(magic[:]) + md5.Write(masterSecret) + md5.Write(ssl30Pad1[:]) + md5Digest := md5.Sum(nil) + + md5.Reset() + md5.Write(masterSecret) + md5.Write(ssl30Pad2[:]) + md5.Write(md5Digest) + md5Digest = md5.Sum(nil) + + sha1.Write(magic[:]) + sha1.Write(masterSecret) + sha1.Write(ssl30Pad1[:40]) + sha1Digest := sha1.Sum(nil) + + sha1.Reset() + sha1.Write(masterSecret) + sha1.Write(ssl30Pad2[:40]) + sha1.Write(sha1Digest) + sha1Digest = sha1.Sum(nil) + + ret := make([]byte, len(md5Digest)+len(sha1Digest)) + copy(ret, md5Digest) + copy(ret[len(md5Digest):], sha1Digest) + return ret +} + +var ssl3ClientFinishedMagic = [4]byte{0x43, 0x4c, 0x4e, 0x54} +var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52} + // clientSum returns the contents of the verify_data member of a client's // Finished message. func (h finishedHash) clientSum(masterSecret []byte) []byte { - md5 := h.clientMD5.Sum() - sha1 := h.clientSHA1.Sum() - return finishedSum(md5, sha1, clientFinishedLabel, masterSecret) + if h.version == versionSSL30 { + return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic) + } + + md5 := h.clientMD5.Sum(nil) + sha1 := h.clientSHA1.Sum(nil) + return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret) } // serverSum returns the contents of the verify_data member of a server's // Finished message. func (h finishedHash) serverSum(masterSecret []byte) []byte { - md5 := h.serverMD5.Sum() - sha1 := h.serverSHA1.Sum() - return finishedSum(md5, sha1, serverFinishedLabel, masterSecret) + if h.version == versionSSL30 { + return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic) + } + + md5 := h.serverMD5.Sum(nil) + sha1 := h.serverSHA1.Sum(nil) + return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret) } diff --git a/src/pkg/crypto/tls/prf_test.go b/src/pkg/crypto/tls/prf_test.go index f8c4acb9d..a32392cef 100644 --- a/src/pkg/crypto/tls/prf_test.go +++ b/src/pkg/crypto/tls/prf_test.go @@ -34,6 +34,7 @@ func TestSplitPreMasterSecret(t *testing.T) { } type testKeysFromTest struct { + version uint16 preMasterSecret string clientRandom, serverRandom string masterSecret string @@ -47,7 +48,7 @@ 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, _, _ := keysFromPreMasterSecret10(in, clientRandom, serverRandom, test.macLen, test.keyLen, 0) + master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret(test.version, in, clientRandom, serverRandom, test.macLen, test.keyLen, 0) masterString := hex.EncodeToString(master) clientMACString := hex.EncodeToString(clientMAC) serverMACString := hex.EncodeToString(serverMAC) @@ -58,7 +59,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { 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, serverMACString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, 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) } } } @@ -66,6 +67,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { // These test vectors were generated from GnuTLS using `gnutls-cli --insecure -d 9 ` var testKeysFromTests = []testKeysFromTest{ { + versionTLS10, "0302cac83ad4b1db3b9ab49ad05957de2a504a634a386fc600889321e1a971f57479466830ac3e6f468e87f5385fa0c5", "4ae66303755184a3917fcb44880605fcc53baa01912b22ed94473fc69cebd558", "4ae663020ec16e6bb5130be918cfcafd4d765979a3136a5d50c593446e4e44db", @@ -78,6 +80,7 @@ var testKeysFromTests = []testKeysFromTest{ 16, }, { + versionTLS10, "03023f7527316bc12cbcd69e4b9e8275d62c028f27e65c745cfcddc7ce01bd3570a111378b63848127f1c36e5f9e4890", "4ae66364b5ea56b20ce4e25555aed2d7e67f42788dd03f3fee4adae0459ab106", "4ae66363ab815cbf6a248b87d6b556184e945e9b97fbdf247858b0bdafacfa1c", @@ -90,6 +93,7 @@ var testKeysFromTests = []testKeysFromTest{ 16, }, { + versionTLS10, "832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1", "4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e", "4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e", @@ -101,4 +105,17 @@ var testKeysFromTests = []testKeysFromTest{ 20, 16, }, + { + versionSSL30, + "832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1", + "4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e", + "4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e", + "a614863e56299dcffeea2938f22c2ba023768dbe4b3f6877bc9c346c6ae529b51d9cb87ff9695ea4d01f2205584405b2", + "2c450d5b6f6e2013ac6bea6a0b32200d4e1ffb94", + "7a7a7438769536f2fb1ae49a61f0703b79b2dc53", + "f8f6b26c10f12855c9aafb1e0e839ccf", + "2b9d4b4a60cb7f396780ebff50650419", + 20, + 16, + }, } diff --git a/src/pkg/crypto/tls/root_test.go b/src/pkg/crypto/tls/root_test.go new file mode 100644 index 000000000..e61c21851 --- /dev/null +++ b/src/pkg/crypto/tls/root_test.go @@ -0,0 +1,61 @@ +// 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/tls.go b/src/pkg/crypto/tls/tls.go index 4f0859fee..09df5ad44 100644 --- a/src/pkg/crypto/tls/tls.go +++ b/src/pkg/crypto/tls/tls.go @@ -2,17 +2,16 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package tls partially implements the TLS 1.1 protocol, as specified in RFC -// 4346. +// Package tls partially implements TLS 1.0, as specified in RFC 2246. package tls import ( "crypto/rsa" "crypto/x509" "encoding/pem" + "errors" "io/ioutil" "net" - "os" "strings" ) @@ -33,16 +32,16 @@ func Client(conn net.Conn, config *Config) *Conn { return &Conn{conn: conn, config: config, isClient: true} } -// A Listener implements a network listener (net.Listener) for TLS connections. -type Listener struct { - listener net.Listener - config *Config +// A listener implements a network listener (net.Listener) for TLS connections. +type listener struct { + net.Listener + config *Config } // Accept waits for and returns the next incoming TLS connection. // The returned connection c is a *tls.Conn. -func (l *Listener) Accept() (c net.Conn, err os.Error) { - c, err = l.listener.Accept() +func (l *listener) Accept() (c net.Conn, err error) { + c, err = l.Listener.Accept() if err != nil { return } @@ -50,30 +49,24 @@ func (l *Listener) Accept() (c net.Conn, err os.Error) { return } -// Close closes the listener. -func (l *Listener) Close() os.Error { return l.listener.Close() } - -// Addr returns the listener's network address. -func (l *Listener) Addr() net.Addr { return l.listener.Addr() } - // NewListener creates a Listener which accepts connections from an inner // Listener and wraps each connection with Server. // The configuration config must be non-nil and must have // at least one certificate. -func NewListener(listener net.Listener, config *Config) (l *Listener) { - l = new(Listener) - l.listener = listener +func NewListener(inner net.Listener, config *Config) net.Listener { + l := new(listener) + l.Listener = inner l.config = config - return + return l } // Listen creates a TLS listener accepting connections on the // given network address using net.Listen. // The configuration config must be non-nil and must have // at least one certificate. -func Listen(network, laddr string, config *Config) (*Listener, os.Error) { +func Listen(network, laddr string, config *Config) (net.Listener, error) { if config == nil || len(config.Certificates) == 0 { - return nil, os.NewError("tls.Listen: no certificates in configuration") + return nil, errors.New("tls.Listen: no certificates in configuration") } l, err := net.Listen(network, laddr) if err != nil { @@ -88,7 +81,7 @@ func Listen(network, laddr string, config *Config) (*Listener, os.Error) { // Dial interprets a nil configuration as equivalent to // the zero configuration; see the documentation of Config // for the defaults. -func Dial(network, addr string, config *Config) (*Conn, os.Error) { +func Dial(network, addr string, config *Config) (*Conn, error) { raddr := addr c, err := net.Dial(network, raddr) if err != nil { @@ -104,7 +97,9 @@ func Dial(network, addr string, config *Config) (*Conn, os.Error) { if config == nil { config = defaultConfig() } - if config.ServerName != "" { + // If no ServerName is set, infer the ServerName + // from the hostname we're connecting to. + if config.ServerName == "" { // Make a copy to avoid polluting argument or default. c := *config c.ServerName = hostname @@ -120,7 +115,7 @@ func Dial(network, addr string, config *Config) (*Conn, os.Error) { // LoadX509KeyPair reads and parses a public/private key pair from a pair of // files. The files must contain PEM encoded data. -func LoadX509KeyPair(certFile string, keyFile string) (cert Certificate, err os.Error) { +func LoadX509KeyPair(certFile, keyFile string) (cert Certificate, err error) { certPEMBlock, err := ioutil.ReadFile(certFile) if err != nil { return @@ -134,7 +129,7 @@ func LoadX509KeyPair(certFile string, keyFile string) (cert Certificate, err os. // X509KeyPair parses a public/private key pair from a pair of // PEM encoded data. -func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Error) { +func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error) { var certDERBlock *pem.Block for { certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) @@ -147,20 +142,31 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Err } if len(cert.Certificate) == 0 { - err = os.NewError("crypto/tls: failed to parse certificate PEM data") + err = errors.New("crypto/tls: failed to parse certificate PEM data") return } keyDERBlock, _ := pem.Decode(keyPEMBlock) if keyDERBlock == nil { - err = os.NewError("crypto/tls: failed to parse key PEM data") + err = errors.New("crypto/tls: failed to parse key PEM data") return } - key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes) - if err != nil { - err = os.NewError("crypto/tls: failed to parse key: " + err.String()) - 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()) + 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 + } } cert.PrivateKey = key @@ -173,7 +179,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Err } if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 { - err = os.NewError("crypto/tls: private key does not match public key") + err = errors.New("crypto/tls: private key does not match public key") return } diff --git a/src/pkg/crypto/twofish/Makefile b/src/pkg/crypto/twofish/Makefile deleted file mode 100644 index aec61659d..000000000 --- a/src/pkg/crypto/twofish/Makefile +++ /dev/null @@ -1,11 +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. - -include ../../../Make.inc - -TARG=crypto/twofish -GOFILES=\ - twofish.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/twofish/twofish.go b/src/pkg/crypto/twofish/twofish.go deleted file mode 100644 index 2e537c606..000000000 --- a/src/pkg/crypto/twofish/twofish.go +++ /dev/null @@ -1,358 +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 twofish implements Bruce Schneier's Twofish encryption algorithm. -package twofish - -// Twofish is defined in http://www.schneier.com/paper-twofish-paper.pdf [TWOFISH] - -// This code is a port of the LibTom C implementation. -// See http://libtom.org/?page=features&newsitems=5&whatfile=crypt. -// LibTomCrypt is free for all purposes under the public domain. -// It was heavily inspired by the go blowfish package. - -import ( - "os" - "strconv" -) - -// BlockSize is the constant block size of Twofish. -const BlockSize = 16 - -const mdsPolynomial = 0x169 // x^8 + x^6 + x^5 + x^3 + 1, see [TWOFISH] 4.2 -const rsPolynomial = 0x14d // x^8 + x^6 + x^3 + x^2 + 1, see [TWOFISH] 4.3 - -// A Cipher is an instance of Twofish encryption using a particular key. -type Cipher struct { - s [4][256]uint32 - k [40]uint32 -} - -type KeySizeError int - -func (k KeySizeError) String() string { - return "crypto/twofish: invalid key size " + strconv.Itoa(int(k)) -} - -// NewCipher creates and returns a Cipher. -// The key argument should be the Twofish key, 16, 24 or 32 bytes. -func NewCipher(key []byte) (*Cipher, os.Error) { - keylen := len(key) - - if keylen != 16 && keylen != 24 && keylen != 32 { - return nil, KeySizeError(keylen) - } - - // k is the number of 64 bit words in key - k := keylen / 8 - - // Create the S[..] words - var S [4 * 4]byte - for i := 0; i < k; i++ { - // Computes [y0 y1 y2 y3] = rs . [x0 x1 x2 x3 x4 x5 x6 x7] - for j, rsRow := range rs { - for k, rsVal := range rsRow { - S[4*i+j] ^= gfMult(key[8*i+k], rsVal, rsPolynomial) - } - } - } - - // Calculate subkeys - c := new(Cipher) - var tmp [4]byte - for i := byte(0); i < 20; i++ { - // A = h(p * 2x, Me) - for j := range tmp { - tmp[j] = 2 * i - } - A := h(tmp[:], key, 0) - - // B = rolc(h(p * (2x + 1), Mo), 8) - for j := range tmp { - tmp[j] = 2*i + 1 - } - B := h(tmp[:], key, 1) - B = rol(B, 8) - - c.k[2*i] = A + B - - // K[2i+1] = (A + 2B) <<< 9 - c.k[2*i+1] = rol(2*B+A, 9) - } - - // Calculate sboxes - switch k { - case 2: - for i := range c.s[0] { - c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][byte(i)]^S[0]]^S[4]], 0) - c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][byte(i)]^S[1]]^S[5]], 1) - c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][byte(i)]^S[2]]^S[6]], 2) - c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][byte(i)]^S[3]]^S[7]], 3) - } - case 3: - for i := range c.s[0] { - c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][sbox[1][byte(i)]^S[0]]^S[4]]^S[8]], 0) - c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][sbox[1][byte(i)]^S[1]]^S[5]]^S[9]], 1) - c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][sbox[0][byte(i)]^S[2]]^S[6]]^S[10]], 2) - c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][sbox[0][byte(i)]^S[3]]^S[7]]^S[11]], 3) - } - default: - for i := range c.s[0] { - c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][sbox[1][sbox[1][byte(i)]^S[0]]^S[4]]^S[8]]^S[12]], 0) - c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][sbox[1][sbox[0][byte(i)]^S[1]]^S[5]]^S[9]]^S[13]], 1) - c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][sbox[0][sbox[0][byte(i)]^S[2]]^S[6]]^S[10]]^S[14]], 2) - c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][sbox[0][sbox[1][byte(i)]^S[3]]^S[7]]^S[11]]^S[15]], 3) - } - } - - return c, nil -} - -// Reset zeros the key data, so that it will no longer appear in the process's -// memory. -func (c *Cipher) Reset() { - for i := range c.k { - c.k[i] = 0 - } - for i := range c.s { - for j := 0; j < 256; j++ { - c.s[i][j] = 0 - } - } -} - -// BlockSize returns the Twofish block size, 16 bytes. -func (c *Cipher) BlockSize() int { return BlockSize } - -// store32l stores src in dst in little-endian form. -func store32l(dst []byte, src uint32) { - dst[0] = byte(src) - dst[1] = byte(src >> 8) - dst[2] = byte(src >> 16) - dst[3] = byte(src >> 24) - return -} - -// load32l reads a little-endian uint32 from src. -func load32l(src []byte) uint32 { - return uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24 -} - -// rol returns x after a left circular rotation of y bits. -func rol(x, y uint32) uint32 { - return (x << (y & 31)) | (x >> (32 - (y & 31))) -} - -// ror returns x after a right circular rotation of y bits. -func ror(x, y uint32) uint32 { - return (x >> (y & 31)) | (x << (32 - (y & 31))) -} - -// The RS matrix. See [TWOFISH] 4.3 -var rs = [4][8]byte{ - {0x01, 0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E}, - {0xA4, 0x56, 0x82, 0xF3, 0x1E, 0xC6, 0x68, 0xE5}, - {0x02, 0xA1, 0xFC, 0xC1, 0x47, 0xAE, 0x3D, 0x19}, - {0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E, 0x03}, -} - -// sbox tables -var sbox = [2][256]byte{ - { - 0xa9, 0x67, 0xb3, 0xe8, 0x04, 0xfd, 0xa3, 0x76, 0x9a, 0x92, 0x80, 0x78, 0xe4, 0xdd, 0xd1, 0x38, - 0x0d, 0xc6, 0x35, 0x98, 0x18, 0xf7, 0xec, 0x6c, 0x43, 0x75, 0x37, 0x26, 0xfa, 0x13, 0x94, 0x48, - 0xf2, 0xd0, 0x8b, 0x30, 0x84, 0x54, 0xdf, 0x23, 0x19, 0x5b, 0x3d, 0x59, 0xf3, 0xae, 0xa2, 0x82, - 0x63, 0x01, 0x83, 0x2e, 0xd9, 0x51, 0x9b, 0x7c, 0xa6, 0xeb, 0xa5, 0xbe, 0x16, 0x0c, 0xe3, 0x61, - 0xc0, 0x8c, 0x3a, 0xf5, 0x73, 0x2c, 0x25, 0x0b, 0xbb, 0x4e, 0x89, 0x6b, 0x53, 0x6a, 0xb4, 0xf1, - 0xe1, 0xe6, 0xbd, 0x45, 0xe2, 0xf4, 0xb6, 0x66, 0xcc, 0x95, 0x03, 0x56, 0xd4, 0x1c, 0x1e, 0xd7, - 0xfb, 0xc3, 0x8e, 0xb5, 0xe9, 0xcf, 0xbf, 0xba, 0xea, 0x77, 0x39, 0xaf, 0x33, 0xc9, 0x62, 0x71, - 0x81, 0x79, 0x09, 0xad, 0x24, 0xcd, 0xf9, 0xd8, 0xe5, 0xc5, 0xb9, 0x4d, 0x44, 0x08, 0x86, 0xe7, - 0xa1, 0x1d, 0xaa, 0xed, 0x06, 0x70, 0xb2, 0xd2, 0x41, 0x7b, 0xa0, 0x11, 0x31, 0xc2, 0x27, 0x90, - 0x20, 0xf6, 0x60, 0xff, 0x96, 0x5c, 0xb1, 0xab, 0x9e, 0x9c, 0x52, 0x1b, 0x5f, 0x93, 0x0a, 0xef, - 0x91, 0x85, 0x49, 0xee, 0x2d, 0x4f, 0x8f, 0x3b, 0x47, 0x87, 0x6d, 0x46, 0xd6, 0x3e, 0x69, 0x64, - 0x2a, 0xce, 0xcb, 0x2f, 0xfc, 0x97, 0x05, 0x7a, 0xac, 0x7f, 0xd5, 0x1a, 0x4b, 0x0e, 0xa7, 0x5a, - 0x28, 0x14, 0x3f, 0x29, 0x88, 0x3c, 0x4c, 0x02, 0xb8, 0xda, 0xb0, 0x17, 0x55, 0x1f, 0x8a, 0x7d, - 0x57, 0xc7, 0x8d, 0x74, 0xb7, 0xc4, 0x9f, 0x72, 0x7e, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, - 0x6e, 0x50, 0xde, 0x68, 0x65, 0xbc, 0xdb, 0xf8, 0xc8, 0xa8, 0x2b, 0x40, 0xdc, 0xfe, 0x32, 0xa4, - 0xca, 0x10, 0x21, 0xf0, 0xd3, 0x5d, 0x0f, 0x00, 0x6f, 0x9d, 0x36, 0x42, 0x4a, 0x5e, 0xc1, 0xe0, - }, - { - 0x75, 0xf3, 0xc6, 0xf4, 0xdb, 0x7b, 0xfb, 0xc8, 0x4a, 0xd3, 0xe6, 0x6b, 0x45, 0x7d, 0xe8, 0x4b, - 0xd6, 0x32, 0xd8, 0xfd, 0x37, 0x71, 0xf1, 0xe1, 0x30, 0x0f, 0xf8, 0x1b, 0x87, 0xfa, 0x06, 0x3f, - 0x5e, 0xba, 0xae, 0x5b, 0x8a, 0x00, 0xbc, 0x9d, 0x6d, 0xc1, 0xb1, 0x0e, 0x80, 0x5d, 0xd2, 0xd5, - 0xa0, 0x84, 0x07, 0x14, 0xb5, 0x90, 0x2c, 0xa3, 0xb2, 0x73, 0x4c, 0x54, 0x92, 0x74, 0x36, 0x51, - 0x38, 0xb0, 0xbd, 0x5a, 0xfc, 0x60, 0x62, 0x96, 0x6c, 0x42, 0xf7, 0x10, 0x7c, 0x28, 0x27, 0x8c, - 0x13, 0x95, 0x9c, 0xc7, 0x24, 0x46, 0x3b, 0x70, 0xca, 0xe3, 0x85, 0xcb, 0x11, 0xd0, 0x93, 0xb8, - 0xa6, 0x83, 0x20, 0xff, 0x9f, 0x77, 0xc3, 0xcc, 0x03, 0x6f, 0x08, 0xbf, 0x40, 0xe7, 0x2b, 0xe2, - 0x79, 0x0c, 0xaa, 0x82, 0x41, 0x3a, 0xea, 0xb9, 0xe4, 0x9a, 0xa4, 0x97, 0x7e, 0xda, 0x7a, 0x17, - 0x66, 0x94, 0xa1, 0x1d, 0x3d, 0xf0, 0xde, 0xb3, 0x0b, 0x72, 0xa7, 0x1c, 0xef, 0xd1, 0x53, 0x3e, - 0x8f, 0x33, 0x26, 0x5f, 0xec, 0x76, 0x2a, 0x49, 0x81, 0x88, 0xee, 0x21, 0xc4, 0x1a, 0xeb, 0xd9, - 0xc5, 0x39, 0x99, 0xcd, 0xad, 0x31, 0x8b, 0x01, 0x18, 0x23, 0xdd, 0x1f, 0x4e, 0x2d, 0xf9, 0x48, - 0x4f, 0xf2, 0x65, 0x8e, 0x78, 0x5c, 0x58, 0x19, 0x8d, 0xe5, 0x98, 0x57, 0x67, 0x7f, 0x05, 0x64, - 0xaf, 0x63, 0xb6, 0xfe, 0xf5, 0xb7, 0x3c, 0xa5, 0xce, 0xe9, 0x68, 0x44, 0xe0, 0x4d, 0x43, 0x69, - 0x29, 0x2e, 0xac, 0x15, 0x59, 0xa8, 0x0a, 0x9e, 0x6e, 0x47, 0xdf, 0x34, 0x35, 0x6a, 0xcf, 0xdc, - 0x22, 0xc9, 0xc0, 0x9b, 0x89, 0xd4, 0xed, 0xab, 0x12, 0xa2, 0x0d, 0x52, 0xbb, 0x02, 0x2f, 0xa9, - 0xd7, 0x61, 0x1e, 0xb4, 0x50, 0x04, 0xf6, 0xc2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xbe, 0x91, - }, -} - -// gfMult returns a·b in GF(2^8)/p -func gfMult(a, b byte, p uint32) byte { - B := [2]uint32{0, uint32(b)} - P := [2]uint32{0, p} - var result uint32 - - // branchless GF multiplier - for i := 0; i < 7; i++ { - result ^= B[a&1] - a >>= 1 - B[1] = P[B[1]>>7] ^ (B[1] << 1) - } - result ^= B[a&1] - return byte(result) -} - -// mdsColumnMult calculates y{col} where [y0 y1 y2 y3] = MDS · [x0] -func mdsColumnMult(in byte, col int) uint32 { - mul01 := in - mul5B := gfMult(in, 0x5B, mdsPolynomial) - mulEF := gfMult(in, 0xEF, mdsPolynomial) - - switch col { - case 0: - return uint32(mul01) | uint32(mul5B)<<8 | uint32(mulEF)<<16 | uint32(mulEF)<<24 - case 1: - return uint32(mulEF) | uint32(mulEF)<<8 | uint32(mul5B)<<16 | uint32(mul01)<<24 - case 2: - return uint32(mul5B) | uint32(mulEF)<<8 | uint32(mul01)<<16 | uint32(mulEF)<<24 - case 3: - return uint32(mul5B) | uint32(mul01)<<8 | uint32(mulEF)<<16 | uint32(mul5B)<<24 - } - - panic("unreachable") -} - -// h implements the S-box generation function. See [TWOFISH] 4.3.5 -func h(in, key []byte, offset int) uint32 { - var y [4]byte - for x := range y { - y[x] = in[x] - } - switch len(key) / 8 { - case 4: - y[0] = sbox[1][y[0]] ^ key[4*(6+offset)+0] - y[1] = sbox[0][y[1]] ^ key[4*(6+offset)+1] - y[2] = sbox[0][y[2]] ^ key[4*(6+offset)+2] - y[3] = sbox[1][y[3]] ^ key[4*(6+offset)+3] - fallthrough - case 3: - y[0] = sbox[1][y[0]] ^ key[4*(4+offset)+0] - y[1] = sbox[1][y[1]] ^ key[4*(4+offset)+1] - y[2] = sbox[0][y[2]] ^ key[4*(4+offset)+2] - y[3] = sbox[0][y[3]] ^ key[4*(4+offset)+3] - fallthrough - case 2: - y[0] = sbox[1][sbox[0][sbox[0][y[0]]^key[4*(2+offset)+0]]^key[4*(0+offset)+0]] - y[1] = sbox[0][sbox[0][sbox[1][y[1]]^key[4*(2+offset)+1]]^key[4*(0+offset)+1]] - y[2] = sbox[1][sbox[1][sbox[0][y[2]]^key[4*(2+offset)+2]]^key[4*(0+offset)+2]] - y[3] = sbox[0][sbox[1][sbox[1][y[3]]^key[4*(2+offset)+3]]^key[4*(0+offset)+3]] - } - // [y0 y1 y2 y3] = MDS . [x0 x1 x2 x3] - var mdsMult uint32 - for i := range y { - mdsMult ^= mdsColumnMult(y[i], i) - } - return mdsMult -} - -// Encrypt encrypts a 16-byte block from src to dst, which may overlap. -// Note that for amounts of data larger than a block, -// it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). -func (c *Cipher) Encrypt(dst, src []byte) { - S1 := c.s[0] - S2 := c.s[1] - S3 := c.s[2] - S4 := c.s[3] - - // Load input - ia := load32l(src[0:4]) - ib := load32l(src[4:8]) - ic := load32l(src[8:12]) - id := load32l(src[12:16]) - - // Pre-whitening - ia ^= c.k[0] - ib ^= c.k[1] - ic ^= c.k[2] - id ^= c.k[3] - - for i := 0; i < 8; i++ { - k := c.k[8+i*4 : 12+i*4] - t2 := S2[byte(ib)] ^ S3[byte(ib>>8)] ^ S4[byte(ib>>16)] ^ S1[byte(ib>>24)] - t1 := S1[byte(ia)] ^ S2[byte(ia>>8)] ^ S3[byte(ia>>16)] ^ S4[byte(ia>>24)] + t2 - ic = ror(ic^(t1+k[0]), 1) - id = rol(id, 1) ^ (t2 + t1 + k[1]) - - t2 = S2[byte(id)] ^ S3[byte(id>>8)] ^ S4[byte(id>>16)] ^ S1[byte(id>>24)] - t1 = S1[byte(ic)] ^ S2[byte(ic>>8)] ^ S3[byte(ic>>16)] ^ S4[byte(ic>>24)] + t2 - ia = ror(ia^(t1+k[2]), 1) - ib = rol(ib, 1) ^ (t2 + t1 + k[3]) - } - - // Output with "undo last swap" - ta := ic ^ c.k[4] - tb := id ^ c.k[5] - tc := ia ^ c.k[6] - td := ib ^ c.k[7] - - store32l(dst[0:4], ta) - store32l(dst[4:8], tb) - store32l(dst[8:12], tc) - store32l(dst[12:16], td) -} - -// Decrypt decrypts a 16-byte block from src to dst, which may overlap. -func (c *Cipher) Decrypt(dst, src []byte) { - S1 := c.s[0] - S2 := c.s[1] - S3 := c.s[2] - S4 := c.s[3] - - // Load input - ta := load32l(src[0:4]) - tb := load32l(src[4:8]) - tc := load32l(src[8:12]) - td := load32l(src[12:16]) - - // Undo undo final swap - ia := tc ^ c.k[6] - ib := td ^ c.k[7] - ic := ta ^ c.k[4] - id := tb ^ c.k[5] - - for i := 8; i > 0; i-- { - k := c.k[4+i*4 : 8+i*4] - t2 := S2[byte(id)] ^ S3[byte(id>>8)] ^ S4[byte(id>>16)] ^ S1[byte(id>>24)] - t1 := S1[byte(ic)] ^ S2[byte(ic>>8)] ^ S3[byte(ic>>16)] ^ S4[byte(ic>>24)] + t2 - ia = rol(ia, 1) ^ (t1 + k[2]) - ib = ror(ib^(t2+t1+k[3]), 1) - - t2 = S2[byte(ib)] ^ S3[byte(ib>>8)] ^ S4[byte(ib>>16)] ^ S1[byte(ib>>24)] - t1 = S1[byte(ia)] ^ S2[byte(ia>>8)] ^ S3[byte(ia>>16)] ^ S4[byte(ia>>24)] + t2 - ic = rol(ic, 1) ^ (t1 + k[0]) - id = ror(id^(t2+t1+k[1]), 1) - } - - // Undo pre-whitening - ia ^= c.k[0] - ib ^= c.k[1] - ic ^= c.k[2] - id ^= c.k[3] - - store32l(dst[0:4], ia) - store32l(dst[4:8], ib) - store32l(dst[8:12], ic) - store32l(dst[12:16], id) -} diff --git a/src/pkg/crypto/twofish/twofish_test.go b/src/pkg/crypto/twofish/twofish_test.go deleted file mode 100644 index 303081f3f..000000000 --- a/src/pkg/crypto/twofish/twofish_test.go +++ /dev/null @@ -1,129 +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 twofish - -import ( - "bytes" - "testing" -) - -var qbox = [2][4][16]byte{ - { - {0x8, 0x1, 0x7, 0xD, 0x6, 0xF, 0x3, 0x2, 0x0, 0xB, 0x5, 0x9, 0xE, 0xC, 0xA, 0x4}, - {0xE, 0xC, 0xB, 0x8, 0x1, 0x2, 0x3, 0x5, 0xF, 0x4, 0xA, 0x6, 0x7, 0x0, 0x9, 0xD}, - {0xB, 0xA, 0x5, 0xE, 0x6, 0xD, 0x9, 0x0, 0xC, 0x8, 0xF, 0x3, 0x2, 0x4, 0x7, 0x1}, - {0xD, 0x7, 0xF, 0x4, 0x1, 0x2, 0x6, 0xE, 0x9, 0xB, 0x3, 0x0, 0x8, 0x5, 0xC, 0xA}, - }, - { - {0x2, 0x8, 0xB, 0xD, 0xF, 0x7, 0x6, 0xE, 0x3, 0x1, 0x9, 0x4, 0x0, 0xA, 0xC, 0x5}, - {0x1, 0xE, 0x2, 0xB, 0x4, 0xC, 0x3, 0x7, 0x6, 0xD, 0xA, 0x5, 0xF, 0x9, 0x0, 0x8}, - {0x4, 0xC, 0x7, 0x5, 0x1, 0x6, 0x9, 0xA, 0x0, 0xE, 0xD, 0x8, 0x2, 0xB, 0x3, 0xF}, - {0xB, 0x9, 0x5, 0x1, 0xC, 0x3, 0xD, 0xE, 0x6, 0x4, 0x7, 0xF, 0x2, 0x0, 0x8, 0xA}, - }, -} - -// genSbox generates the variable sbox -func genSbox(qi int, x byte) byte { - a0, b0 := x/16, x%16 - for i := 0; i < 2; i++ { - a1 := a0 ^ b0 - b1 := (a0 ^ ((b0 << 3) | (b0 >> 1)) ^ (a0 << 3)) & 15 - a0 = qbox[qi][2*i][a1] - b0 = qbox[qi][2*i+1][b1] - } - return (b0 << 4) + a0 -} - -func TestSbox(t *testing.T) { - for n := range sbox { - for m := range sbox[n] { - if genSbox(n, byte(m)) != sbox[n][m] { - t.Errorf("#%d|%d: sbox value = %d want %d", n, m, sbox[n][m], genSbox(n, byte(m))) - } - } - } -} - -var testVectors = []struct { - key []byte - dec []byte - enc []byte -}{ - // These tests are extracted from LibTom - { - []byte{0x9F, 0x58, 0x9F, 0x5C, 0xF6, 0x12, 0x2C, 0x32, 0xB6, 0xBF, 0xEC, 0x2F, 0x2A, 0xE8, 0xC3, 0x5A}, - []byte{0xD4, 0x91, 0xDB, 0x16, 0xE7, 0xB1, 0xC3, 0x9E, 0x86, 0xCB, 0x08, 0x6B, 0x78, 0x9F, 0x54, 0x19}, - []byte{0x01, 0x9F, 0x98, 0x09, 0xDE, 0x17, 0x11, 0x85, 0x8F, 0xAA, 0xC3, 0xA3, 0xBA, 0x20, 0xFB, 0xC3}, - }, - { - []byte{0x88, 0xB2, 0xB2, 0x70, 0x6B, 0x10, 0x5E, 0x36, 0xB4, 0x46, 0xBB, 0x6D, 0x73, 0x1A, 0x1E, 0x88, - 0xEF, 0xA7, 0x1F, 0x78, 0x89, 0x65, 0xBD, 0x44}, - []byte{0x39, 0xDA, 0x69, 0xD6, 0xBA, 0x49, 0x97, 0xD5, 0x85, 0xB6, 0xDC, 0x07, 0x3C, 0xA3, 0x41, 0xB2}, - []byte{0x18, 0x2B, 0x02, 0xD8, 0x14, 0x97, 0xEA, 0x45, 0xF9, 0xDA, 0xAC, 0xDC, 0x29, 0x19, 0x3A, 0x65}, - }, - { - []byte{0xD4, 0x3B, 0xB7, 0x55, 0x6E, 0xA3, 0x2E, 0x46, 0xF2, 0xA2, 0x82, 0xB7, 0xD4, 0x5B, 0x4E, 0x0D, - 0x57, 0xFF, 0x73, 0x9D, 0x4D, 0xC9, 0x2C, 0x1B, 0xD7, 0xFC, 0x01, 0x70, 0x0C, 0xC8, 0x21, 0x6F}, - []byte{0x90, 0xAF, 0xE9, 0x1B, 0xB2, 0x88, 0x54, 0x4F, 0x2C, 0x32, 0xDC, 0x23, 0x9B, 0x26, 0x35, 0xE6}, - []byte{0x6C, 0xB4, 0x56, 0x1C, 0x40, 0xBF, 0x0A, 0x97, 0x05, 0x93, 0x1C, 0xB6, 0xD4, 0x08, 0xE7, 0xFA}, - }, - // These test are derived from http://www.schneier.com/code/ecb_ival.txt - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x9F, 0x58, 0x9F, 0x5C, 0xF6, 0x12, 0x2C, 0x32, 0xB6, 0xBF, 0xEC, 0x2F, 0x2A, 0xE8, 0xC3, 0x5A}, - }, - { - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - }, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0xCF, 0xD1, 0xD2, 0xE5, 0xA9, 0xBE, 0x9C, 0xDF, 0x50, 0x1F, 0x13, 0xB8, 0x92, 0xBD, 0x22, 0x48}, - }, - { - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - }, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x37, 0x52, 0x7B, 0xE0, 0x05, 0x23, 0x34, 0xB8, 0x9F, 0x0C, 0xFC, 0xCA, 0xE8, 0x7C, 0xFA, 0x20}, - }, -} - -func TestCipher(t *testing.T) { - for n, tt := range testVectors { - // Test if the plaintext (dec) is encrypts to the given - // ciphertext (enc) using the given key. Test also if enc can - // be decrypted again into dec. - c, err := NewCipher(tt.key) - if err != nil { - t.Errorf("#%d: NewCipher: %v", n, err) - return - } - - buf := make([]byte, 16) - c.Encrypt(buf, tt.dec) - if !bytes.Equal(buf, tt.enc) { - t.Errorf("#%d: encrypt = %x want %x", n, buf, tt.enc) - } - c.Decrypt(buf, tt.enc) - if !bytes.Equal(buf, tt.dec) { - t.Errorf("#%d: decrypt = %x want %x", n, buf, tt.dec) - } - - // Test that 16 zero bytes, encrypted 1000 times then decrypted - // 1000 times results in zero bytes again. - zero := make([]byte, 16) - buf = make([]byte, 16) - for i := 0; i < 1000; i++ { - c.Encrypt(buf, buf) - } - for i := 0; i < 1000; i++ { - c.Decrypt(buf, buf) - } - if !bytes.Equal(buf, zero) { - t.Errorf("#%d: encrypt/decrypt 1000: have %x want %x", n, buf, zero) - } - } -} diff --git a/src/pkg/crypto/x509/Makefile b/src/pkg/crypto/x509/Makefile deleted file mode 100644 index 14ffd095f..000000000 --- a/src/pkg/crypto/x509/Makefile +++ /dev/null @@ -1,13 +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 ../../../Make.inc - -TARG=crypto/x509 -GOFILES=\ - cert_pool.go\ - verify.go\ - x509.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/x509/cert_pool.go b/src/pkg/crypto/x509/cert_pool.go index 16cd92efc..616a0b3c1 100644 --- a/src/pkg/crypto/x509/cert_pool.go +++ b/src/pkg/crypto/x509/cert_pool.go @@ -5,12 +5,10 @@ package x509 import ( - "crypto/x509/pkix" "encoding/pem" - "strings" ) -// Roots is a set of certificates. +// CertPool is a set of certificates. type CertPool struct { bySubjectKeyId map[string][]int byName map[string][]int @@ -26,21 +24,20 @@ func NewCertPool() *CertPool { } } -func nameToKey(name *pkix.Name) string { - return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName -} - // findVerifiedParents attempts to find certificates in s which have signed the // given certificate. If no such certificate can be found or the signature // doesn't match, it returns nil. func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int) { + if s == nil { + return + } var candidates []int if len(cert.AuthorityKeyId) > 0 { candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)] } if len(candidates) == 0 { - candidates = s.byName[nameToKey(&cert.Issuer)] + candidates = s.byName[string(cert.RawIssuer)] } for _, c := range candidates { @@ -72,15 +69,15 @@ func (s *CertPool) AddCert(cert *Certificate) { keyId := string(cert.SubjectKeyId) s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], n) } - name := nameToKey(&cert.Subject) + name := string(cert.RawSubject) s.byName[name] = append(s.byName[name], n) } -// AppendCertsFromPEM attempts to parse a series of PEM encoded root -// certificates. It appends any certificates found to s and returns true if any -// certificates were successfully parsed. +// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates. +// It appends any certificates found to s and returns true if any certificates +// were successfully parsed. // -// On many Linux systems, /etc/ssl/cert.pem will contains the system wide set +// On many Linux systems, /etc/ssl/cert.pem will contain the system wide set // of root CAs in a format suitable for this function. func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) { for len(pemCerts) > 0 { @@ -104,3 +101,13 @@ func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) { return } + +// Subjects returns a list of the DER-encoded subjects of +// 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 { + res[i] = c.RawSubject + } + return +} diff --git a/src/pkg/crypto/x509/pkcs1.go b/src/pkg/crypto/x509/pkcs1.go new file mode 100644 index 000000000..873d3966e --- /dev/null +++ b/src/pkg/crypto/x509/pkcs1.go @@ -0,0 +1,122 @@ +// 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 x509 + +import ( + "crypto/rsa" + "encoding/asn1" + "errors" + "math/big" +) + +// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key. +type pkcs1PrivateKey struct { + Version int + N *big.Int + E int + D *big.Int + P *big.Int + Q *big.Int + // We ignore these values, if present, because rsa will calculate them. + Dp *big.Int `asn1:"optional"` + Dq *big.Int `asn1:"optional"` + Qinv *big.Int `asn1:"optional"` + + AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional,omitempty"` +} + +type pkcs1AdditionalRSAPrime struct { + Prime *big.Int + + // We ignore these values because rsa will calculate them. + Exp *big.Int + Coeff *big.Int +} + +// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form. +func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) { + var priv pkcs1PrivateKey + rest, err := asn1.Unmarshal(der, &priv) + if len(rest) > 0 { + err = asn1.SyntaxError{Msg: "trailing data"} + return + } + if err != nil { + return + } + + if priv.Version > 1 { + return nil, errors.New("x509: unsupported private key version") + } + + if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 { + return nil, errors.New("private key contains zero or negative value") + } + + key = new(rsa.PrivateKey) + key.PublicKey = rsa.PublicKey{ + E: priv.E, + N: priv.N, + } + + key.D = priv.D + key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes)) + key.Primes[0] = priv.P + key.Primes[1] = priv.Q + for i, a := range priv.AdditionalPrimes { + if a.Prime.Sign() <= 0 { + return nil, errors.New("private key contains zero or negative prime") + } + key.Primes[i+2] = a.Prime + // We ignore the other two values because rsa will calculate + // them as needed. + } + + err = key.Validate() + if err != nil { + return nil, err + } + key.Precompute() + + return +} + +// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form. +func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { + key.Precompute() + + version := 0 + if len(key.Primes) > 2 { + version = 1 + } + + priv := pkcs1PrivateKey{ + Version: version, + N: key.N, + E: key.PublicKey.E, + D: key.D, + P: key.Primes[0], + Q: key.Primes[1], + Dp: key.Precomputed.Dp, + Dq: key.Precomputed.Dq, + Qinv: key.Precomputed.Qinv, + } + + priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues)) + for i, values := range key.Precomputed.CRTValues { + priv.AdditionalPrimes[i].Prime = key.Primes[2+i] + priv.AdditionalPrimes[i].Exp = values.Exp + priv.AdditionalPrimes[i].Coeff = values.Coeff + } + + b, _ := asn1.Marshal(priv) + return b +} + +// rsaPublicKey reflects the ASN.1 structure of a PKCS#1 public key. +type rsaPublicKey struct { + N *big.Int + E int +} diff --git a/src/pkg/crypto/x509/pkcs8.go b/src/pkg/crypto/x509/pkcs8.go new file mode 100644 index 000000000..4d8e0518e --- /dev/null +++ b/src/pkg/crypto/x509/pkcs8.go @@ -0,0 +1,42 @@ +// 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 x509 + +import ( + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" +) + +// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See +// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn. +type pkcs8 struct { + Version int + Algo pkix.AlgorithmIdentifier + PrivateKey []byte + // optional attributes omitted. +} + +// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See +// http://www.rsa.com/rsalabs/node.asp?id=2130 +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): + 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 + default: + return nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm) + } + + panic("unreachable") +} diff --git a/src/pkg/crypto/x509/pkcs8_test.go b/src/pkg/crypto/x509/pkcs8_test.go new file mode 100644 index 000000000..372005f90 --- /dev/null +++ b/src/pkg/crypto/x509/pkcs8_test.go @@ -0,0 +1,20 @@ +// 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 x509 + +import ( + "encoding/hex" + "testing" +) + +var pkcs8PrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031` + +func TestPKCS8(t *testing.T) { + derBytes, _ := hex.DecodeString(pkcs8PrivateKeyHex) + _, err := ParsePKCS8PrivateKey(derBytes) + if err != nil { + t.Errorf("failed to decode PKCS8 key: %s", err) + } +} diff --git a/src/pkg/crypto/x509/pkix/Makefile b/src/pkg/crypto/x509/pkix/Makefile deleted file mode 100644 index e29b74c01..000000000 --- a/src/pkg/crypto/x509/pkix/Makefile +++ /dev/null @@ -1,11 +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. - -include ../../../../Make.inc - -TARG=crypto/x509/pkix -GOFILES=\ - pkix.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/crypto/x509/pkix/pkix.go b/src/pkg/crypto/x509/pkix/pkix.go index 266fd557a..738659011 100644 --- a/src/pkg/crypto/x509/pkix/pkix.go +++ b/src/pkg/crypto/x509/pkix/pkix.go @@ -7,8 +7,8 @@ package pkix import ( - "asn1" - "big" + "encoding/asn1" + "math/big" "time" ) @@ -23,6 +23,8 @@ type RDNSequence []RelativeDistinguishedNameSET type RelativeDistinguishedNameSET []AttributeTypeAndValue +// AttributeTypeAndValue mirrors the ASN.1 structure of the same name in +// http://tools.ietf.org/html/rfc5280#section-4.1.2.4 type AttributeTypeAndValue struct { Type asn1.ObjectIdentifier Value interface{} @@ -43,6 +45,8 @@ type Name struct { Locality, Province []string StreetAddress, PostalCode []string SerialNumber, CommonName string + + Names []AttributeTypeAndValue } func (n *Name) FillFromRDNSequence(rdns *RDNSequence) { @@ -51,6 +55,7 @@ func (n *Name) FillFromRDNSequence(rdns *RDNSequence) { continue } atv := rdn[0] + n.Names = append(n.Names, atv) value, ok := atv.Value.(string) if !ok { continue @@ -139,10 +144,9 @@ type CertificateList struct { SignatureValue asn1.BitString } -// HasExpired returns true iff currentTimeSeconds is past the expiry time of -// certList. -func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool { - return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds +// HasExpired returns true iff now is past the expiry time of certList. +func (certList *CertificateList) HasExpired(now time.Time) bool { + return now.After(certList.TBSCertList.NextUpdate) } // TBSCertificateList represents the ASN.1 structure of the same name. See RFC @@ -152,8 +156,8 @@ type TBSCertificateList struct { Version int `asn1:"optional,default:2"` Signature AlgorithmIdentifier Issuer RDNSequence - ThisUpdate *time.Time - NextUpdate *time.Time + ThisUpdate time.Time + NextUpdate time.Time RevokedCertificates []RevokedCertificate `asn1:"optional"` Extensions []Extension `asn1:"tag:0,optional,explicit"` } @@ -162,6 +166,6 @@ type TBSCertificateList struct { // 5280, section 5.1. type RevokedCertificate struct { SerialNumber *big.Int - RevocationTime *time.Time + RevocationTime time.Time Extensions []Extension `asn1:"optional"` } diff --git a/src/pkg/crypto/x509/root.go b/src/pkg/crypto/x509/root.go new file mode 100644 index 000000000..8aae14e09 --- /dev/null +++ b/src/pkg/crypto/x509/root.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 x509 + +import "sync" + +var ( + once sync.Once + systemRoots *CertPool +) + +func systemRootsPool() *CertPool { + once.Do(initSystemRoots) + return systemRoots +} diff --git a/src/pkg/crypto/x509/root_darwin.go b/src/pkg/crypto/x509/root_darwin.go new file mode 100644 index 000000000..0f99581e8 --- /dev/null +++ b/src/pkg/crypto/x509/root_darwin.go @@ -0,0 +1,80 @@ +// 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 x509 + +/* +#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060 +#cgo LDFLAGS: -framework CoreFoundation -framework Security + +#include +#include + +// FetchPEMRoots fetches the system's list of trusted X.509 root certificates. +// +// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root +// certificates of the system. On failure, the function returns -1. +// +// Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after +// we've consumed its content. +int FetchPEMRoots(CFDataRef *pemRoots) { + if (pemRoots == NULL) { + return -1; + } + + CFArrayRef certs = NULL; + OSStatus err = SecTrustCopyAnchorCertificates(&certs); + if (err != noErr) { + return -1; + } + + CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); + int i, ncerts = CFArrayGetCount(certs); + for (i = 0; i < ncerts; i++) { + CFDataRef data = NULL; + SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i); + if (cert == NULL) { + continue; + } + + // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. + // Once we support weak imports via cgo we should prefer that, and fall back to this + // for older systems. + err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); + if (err != noErr) { + continue; + } + + if (data != NULL) { + CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data)); + CFRelease(data); + } + } + + CFRelease(certs); + + *pemRoots = combinedData; + return 0; +} +*/ +import "C" +import "unsafe" + +func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { + return nil, nil +} + +func initSystemRoots() { + roots := NewCertPool() + + 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) + } + + systemRoots = roots +} diff --git a/src/pkg/crypto/x509/root_stub.go b/src/pkg/crypto/x509/root_stub.go new file mode 100644 index 000000000..568004108 --- /dev/null +++ b/src/pkg/crypto/x509/root_stub.go @@ -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. + +// +build plan9 darwin,!cgo + +package x509 + +func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { + return nil, nil +} + +func initSystemRoots() { + systemRoots = NewCertPool() +} diff --git a/src/pkg/crypto/x509/root_unix.go b/src/pkg/crypto/x509/root_unix.go new file mode 100644 index 000000000..76e79f494 --- /dev/null +++ b/src/pkg/crypto/x509/root_unix.go @@ -0,0 +1,35 @@ +// 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 freebsd linux openbsd netbsd + +package x509 + +import "io/ioutil" + +// Possible certificate files; stop after finding one. +var certFiles = []string{ + "/etc/ssl/certs/ca-certificates.crt", // Linux etc + "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL + "/etc/ssl/ca-bundle.pem", // OpenSUSE + "/etc/ssl/cert.pem", // OpenBSD + "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD +} + +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) + break + } + } + + systemRoots = roots +} diff --git a/src/pkg/crypto/x509/root_windows.go b/src/pkg/crypto/x509/root_windows.go new file mode 100644 index 000000000..7e8f2af4b --- /dev/null +++ b/src/pkg/crypto/x509/root_windows.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 x509 + +import ( + "errors" + "syscall" + "unsafe" +) + +// Creates a new *syscall.CertContext representing the leaf certificate in an in-memory +// certificate store containing itself and all of the intermediate certificates specified +// in the opts.Intermediates CertPool. +// +// A pointer to the in-memory store is available in the returned CertContext's Store field. +// The store is automatically freed when the CertContext is freed using +// syscall.CertFreeCertificateContext. +func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertContext, error) { + var storeCtx *syscall.CertContext + + leafCtx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &leaf.Raw[0], uint32(len(leaf.Raw))) + if err != nil { + return nil, err + } + defer syscall.CertFreeCertificateContext(leafCtx) + + handle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0) + if err != nil { + return nil, err + } + defer syscall.CertCloseStore(handle, 0) + + err = syscall.CertAddCertificateContextToStore(handle, leafCtx, syscall.CERT_STORE_ADD_ALWAYS, &storeCtx) + if err != nil { + return nil, err + } + + if opts.Intermediates != nil { + for _, intermediate := range opts.Intermediates.certs { + ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw))) + if err != nil { + return nil, err + } + + err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil) + syscall.CertFreeCertificateContext(ctx) + if err != nil { + return nil, err + } + } + } + + return storeCtx, nil +} + +// extractSimpleChain extracts the final certificate chain from a CertSimpleChain. +func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) { + if simpleChain == nil || count == 0 { + return nil, errors.New("x509: invalid simple chain") + } + + simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:] + lastChain := simpleChains[count-1] + elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:] + for i := 0; i < int(lastChain.NumElements); i++ { + // Copy the buf, since ParseCertificate does not create its own copy. + cert := elements[i].CertContext + encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:] + buf := make([]byte, cert.Length) + copy(buf, encodedCert[:]) + parsedCert, err := ParseCertificate(buf) + if err != nil { + return nil, err + } + chain = append(chain, parsedCert) + } + + return chain, nil +} + +// checkChainTrustStatus checks the trust status of the certificate chain, translating +// any errors it finds into Go errors in the process. +func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error { + if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR { + status := chainCtx.TrustStatus.ErrorStatus + switch status { + case syscall.CERT_TRUST_IS_NOT_TIME_VALID: + return CertificateInvalidError{c, Expired} + default: + return UnknownAuthorityError{c} + } + } + return nil +} + +// 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 { + sslPara := &syscall.SSLExtraCertChainPolicyPara{ + AuthType: syscall.AUTHTYPE_SERVER, + ServerName: syscall.StringToUTF16Ptr(opts.DNSName), + } + sslPara.Size = uint32(unsafe.Sizeof(*sslPara)) + + para := &syscall.CertChainPolicyPara{ + ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)), + } + para.Size = uint32(unsafe.Sizeof(*para)) + + status := syscall.CertChainPolicyStatus{} + err := syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status) + if err != nil { + return err + } + + // TODO(mkrautz): use the lChainIndex and lElementIndex fields + // of the CertChainPolicyStatus to provide proper context, instead + // using c. + if status.Error != 0 { + switch status.Error { + case syscall.CERT_E_EXPIRED: + return CertificateInvalidError{c, Expired} + case syscall.CERT_E_CN_NO_MATCH: + return HostnameError{c, opts.DNSName} + case syscall.CERT_E_UNTRUSTEDROOT: + return UnknownAuthorityError{c} + default: + return UnknownAuthorityError{c} + } + } + + return nil +} + +// systemVerify is like Verify, except that it uses CryptoAPI calls +// to build certificate chains and verify them. +func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { + hasDNSName := opts != nil && len(opts.DNSName) > 0 + + storeCtx, err := createStoreContext(c, opts) + if err != nil { + return nil, err + } + defer syscall.CertFreeCertificateContext(storeCtx) + + para := new(syscall.CertChainPara) + para.Size = uint32(unsafe.Sizeof(*para)) + + // If there's a DNSName set in opts, assume we're verifying + // a certificate from a TLS server. + if hasDNSName { + oids := []*byte{ + &syscall.OID_PKIX_KP_SERVER_AUTH[0], + // Both IE and Chrome allow certificates with + // Server Gated Crypto as well. Some certificates + // in the wild require them. + &syscall.OID_SERVER_GATED_CRYPTO[0], + &syscall.OID_SGC_NETSCAPE[0], + } + para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR + para.RequestedUsage.Usage.Length = uint32(len(oids)) + para.RequestedUsage.Usage.UsageIdentifiers = &oids[0] + } else { + para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND + para.RequestedUsage.Usage.Length = 0 + para.RequestedUsage.Usage.UsageIdentifiers = nil + } + + var verifyTime *syscall.Filetime + if opts != nil && !opts.CurrentTime.IsZero() { + ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano()) + verifyTime = &ft + } + + // CertGetCertificateChain will traverse Windows's root stores + // in an attempt to build a verified certificate chain. Once + // it has found a verified chain, it stops. MSDN docs on + // CERT_CHAIN_CONTEXT: + // + // When a CERT_CHAIN_CONTEXT is built, the first simple chain + // begins with an end certificate and ends with a self-signed + // certificate. If that self-signed certificate is not a root + // or otherwise trusted certificate, an attempt is made to + // build a new chain. CTLs are used to create the new chain + // beginning with the self-signed certificate from the original + // chain as the end certificate of the new chain. This process + // continues building additional simple chains until the first + // self-signed certificate is a trusted certificate or until + // an additional simple chain cannot be built. + // + // The result is that we'll only get a single trusted chain to + // return to our caller. + var chainCtx *syscall.CertChainContext + err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx) + if err != nil { + return nil, err + } + defer syscall.CertFreeCertificateChain(chainCtx) + + err = checkChainTrustStatus(c, chainCtx) + if err != nil { + return nil, err + } + + if hasDNSName { + err = checkChainSSLServerPolicy(c, chainCtx, opts) + if err != nil { + return nil, err + } + } + + chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount)) + if err != nil { + return nil, err + } + + chains = append(chains, chain) + + return chains, nil +} + +func initSystemRoots() { + systemRoots = NewCertPool() +} diff --git a/src/pkg/crypto/x509/verify.go b/src/pkg/crypto/x509/verify.go index 4c0fecccb..307c5ef03 100644 --- a/src/pkg/crypto/x509/verify.go +++ b/src/pkg/crypto/x509/verify.go @@ -5,9 +5,10 @@ package x509 import ( - "os" + "runtime" "strings" "time" + "unicode/utf8" ) type InvalidReason int @@ -23,6 +24,9 @@ const ( // certificate has a name constraint which doesn't include the name // being checked. CANotAuthorizedForThisName + // TooManyIntermediates results when a path length constraint is + // violated. + TooManyIntermediates ) // CertificateInvalidError results when an odd error occurs. Users of this @@ -32,7 +36,7 @@ type CertificateInvalidError struct { Reason InvalidReason } -func (e CertificateInvalidError) String() string { +func (e CertificateInvalidError) Error() string { switch e.Reason { case NotAuthorizedToSign: return "x509: certificate is not authorized to sign other other certificates" @@ -40,6 +44,8 @@ func (e CertificateInvalidError) String() string { 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" } return "x509: unknown error" } @@ -51,7 +57,7 @@ type HostnameError struct { Host string } -func (h HostnameError) String() string { +func (h HostnameError) Error() string { var valid string c := h.Certificate if len(c.DNSNames) > 0 { @@ -67,7 +73,7 @@ type UnknownAuthorityError struct { cert *Certificate } -func (e UnknownAuthorityError) String() string { +func (e UnknownAuthorityError) Error() string { return "x509: certificate signed by unknown authority" } @@ -76,8 +82,8 @@ func (e UnknownAuthorityError) String() string { type VerifyOptions struct { DNSName string Intermediates *CertPool - Roots *CertPool - CurrentTime int64 // if 0, the current system time is used. + Roots *CertPool // if nil, the system roots are used + CurrentTime time.Time // if zero, the current time is used } const ( @@ -87,9 +93,12 @@ const ( ) // isValid performs validity checks on the c. -func (c *Certificate) isValid(certType int, opts *VerifyOptions) os.Error { - if opts.CurrentTime < c.NotBefore.Seconds() || - opts.CurrentTime > c.NotAfter.Seconds() { +func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error { + now := opts.CurrentTime + if now.IsZero() { + now = time.Now() + } + if now.Before(c.NotBefore) || now.After(c.NotAfter) { return CertificateInvalidError{c, Expired} } @@ -127,29 +136,44 @@ func (c *Certificate) isValid(certType int, opts *VerifyOptions) os.Error { return CertificateInvalidError{c, NotAuthorizedToSign} } + if c.BasicConstraintsValid && c.MaxPathLen >= 0 { + numIntermediates := len(currentChain) - 1 + if numIntermediates > c.MaxPathLen { + return CertificateInvalidError{c, TooManyIntermediates} + } + } + return nil } // Verify attempts to verify c by building one or more chains from c to a -// certificate in opts.roots, using certificates in opts.Intermediates if -// needed. If successful, it returns one or chains where the first element of -// the chain is c and the last element is from opts.Roots. +// certificate in opts.Roots, using certificates in opts.Intermediates if +// needed. If successful, it returns one or more chains where the first +// element of the chain is c and the last element is from opts.Roots. // // WARNING: this doesn't do any revocation checking. -func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err os.Error) { - if opts.CurrentTime == 0 { - opts.CurrentTime = time.Seconds() +func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) { + // Use Windows's own verification and chain building. + if opts.Roots == nil && runtime.GOOS == "windows" { + return c.systemVerify(&opts) + } + + if opts.Roots == nil { + opts.Roots = systemRootsPool() } - err = c.isValid(leafCertificate, &opts) + + err = c.isValid(leafCertificate, nil, &opts) if err != nil { return } + if len(opts.DNSName) > 0 { err = c.VerifyHostname(opts.DNSName) if err != nil { return } } + return c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts) } @@ -160,10 +184,10 @@ func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate return n } -func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err os.Error) { +func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) { for _, rootNum := range opts.Roots.findVerifiedParents(c) { root := opts.Roots.certs[rootNum] - err = root.isValid(rootCertificate, opts) + err = root.isValid(rootCertificate, currentChain, opts) if err != nil { continue } @@ -178,7 +202,7 @@ nextIntermediate: continue nextIntermediate } } - err = intermediate.isValid(intermediateCertificate, opts) + err = intermediate.isValid(intermediateCertificate, currentChain, opts) if err != nil { continue } @@ -226,17 +250,51 @@ func matchHostnames(pattern, host string) bool { return true } +// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use +// an explicitly ASCII function to avoid any sharp corners resulting from +// performing Unicode operations on DNS labels. +func toLowerCaseASCII(in string) string { + // If the string is already lower-case then there's nothing to do. + isAlreadyLowerCase := true + for _, c := range in { + if c == utf8.RuneError { + // If we get a UTF-8 error then there might be + // upper-case ASCII bytes in the invalid sequence. + isAlreadyLowerCase = false + break + } + if 'A' <= c && c <= 'Z' { + isAlreadyLowerCase = false + break + } + } + + if isAlreadyLowerCase { + return in + } + + out := []byte(in) + for i, c := range out { + if 'A' <= c && c <= 'Z' { + out[i] += 'a' - 'A' + } + } + return string(out) +} + // VerifyHostname returns nil if c is a valid certificate for the named host. -// Otherwise it returns an os.Error describing the mismatch. -func (c *Certificate) VerifyHostname(h string) os.Error { +// Otherwise it returns an error describing the mismatch. +func (c *Certificate) VerifyHostname(h string) error { + lowered := toLowerCaseASCII(h) + if len(c.DNSNames) > 0 { for _, match := range c.DNSNames { - if matchHostnames(match, h) { + if matchHostnames(toLowerCaseASCII(match), lowered) { return nil } } // If Subject Alt Name is given, we ignore the common name. - } else if matchHostnames(c.Subject.CommonName, h) { + } else if matchHostnames(toLowerCaseASCII(c.Subject.CommonName), lowered) { return nil } diff --git a/src/pkg/crypto/x509/verify_test.go b/src/pkg/crypto/x509/verify_test.go index 111f60eb1..7b171b291 100644 --- a/src/pkg/crypto/x509/verify_test.go +++ b/src/pkg/crypto/x509/verify_test.go @@ -5,10 +5,13 @@ package x509 import ( + "crypto/x509/pkix" "encoding/pem" - "os" + "errors" + "runtime" "strings" "testing" + "time" ) type verifyTest struct { @@ -17,8 +20,9 @@ type verifyTest struct { roots []string currentTime int64 dnsName string + systemSkip bool - errorCallback func(*testing.T, int, os.Error) bool + errorCallback func(*testing.T, int, error) bool expectedChains [][]string } @@ -31,7 +35,18 @@ var verifyTests = []verifyTest{ dnsName: "www.google.com", expectedChains: [][]string{ - []string{"Google", "Thawte", "VeriSign"}, + {"Google", "Thawte", "VeriSign"}, + }, + }, + { + leaf: googleLeaf, + intermediates: []string{thawteIntermediate}, + roots: []string{verisignRoot}, + currentTime: 1302726541, + dnsName: "WwW.GooGLE.coM", + + expectedChains: [][]string{ + {"Google", "Thawte", "VeriSign"}, }, }, { @@ -58,6 +73,9 @@ var verifyTests = []verifyTest{ currentTime: 1302726541, dnsName: "www.google.com", + // Skip when using systemVerify, since Windows + // *will* find the missing intermediate cert. + systemSkip: true, errorCallback: expectAuthorityUnknown, }, { @@ -68,7 +86,7 @@ var verifyTests = []verifyTest{ dnsName: "www.google.com", expectedChains: [][]string{ - []string{"Google", "Thawte", "VeriSign"}, + {"Google", "Thawte", "VeriSign"}, }, }, { @@ -78,7 +96,7 @@ var verifyTests = []verifyTest{ currentTime: 1302726541, expectedChains: [][]string{ - []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, + {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, }, }, { @@ -87,14 +105,17 @@ var verifyTests = []verifyTest{ roots: []string{startComRoot}, currentTime: 1302726541, + // Skip when using systemVerify, since Windows + // can only return a single chain to us (for now). + systemSkip: true, expectedChains: [][]string{ - []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, - []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"}, + {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, + {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"}, }, }, } -func expectHostnameError(t *testing.T, i int, err os.Error) (ok bool) { +func expectHostnameError(t *testing.T, i int, err error) (ok bool) { if _, ok := err.(HostnameError); !ok { t.Errorf("#%d: error was not a HostnameError: %s", i, err) return false @@ -102,7 +123,7 @@ func expectHostnameError(t *testing.T, i int, err os.Error) (ok bool) { return true } -func expectExpired(t *testing.T, i int, err os.Error) (ok bool) { +func expectExpired(t *testing.T, i int, err error) (ok bool) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != Expired { t.Errorf("#%d: error was not Expired: %s", i, err) return false @@ -110,7 +131,7 @@ func expectExpired(t *testing.T, i int, err os.Error) (ok bool) { return true } -func expectAuthorityUnknown(t *testing.T, i int, err os.Error) (ok bool) { +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) return false @@ -118,28 +139,34 @@ func expectAuthorityUnknown(t *testing.T, i int, err os.Error) (ok bool) { return true } -func certificateFromPEM(pemBytes string) (*Certificate, os.Error) { +func certificateFromPEM(pemBytes string) (*Certificate, error) { block, _ := pem.Decode([]byte(pemBytes)) if block == nil { - return nil, os.NewError("failed to decode PEM") + return nil, errors.New("failed to decode PEM") } return ParseCertificate(block.Bytes) } -func TestVerify(t *testing.T) { +func testVerify(t *testing.T, useSystemRoots bool) { for i, test := range verifyTests { + if useSystemRoots && test.systemSkip { + continue + } + opts := VerifyOptions{ - Roots: NewCertPool(), Intermediates: NewCertPool(), DNSName: test.dnsName, - CurrentTime: test.currentTime, + CurrentTime: time.Unix(test.currentTime, 0), } - for j, root := range test.roots { - ok := opts.Roots.AppendCertsFromPEM([]byte(root)) - if !ok { - t.Errorf("#%d: failed to parse root #%d", i, j) - return + if !useSystemRoots { + opts.Roots = NewCertPool() + for j, root := range test.roots { + ok := opts.Roots.AppendCertsFromPEM([]byte(root)) + if !ok { + t.Errorf("#%d: failed to parse root #%d", i, j) + return + } } } @@ -200,6 +227,19 @@ func TestVerify(t *testing.T) { } } +func TestGoVerify(t *testing.T) { + testVerify(t, false) +} + +func TestSystemVerify(t *testing.T) { + if runtime.GOOS != "windows" { + t.Logf("skipping verify test using system APIs on %q", runtime.GOOS) + return + } + + testVerify(t, true) +} + func chainToDebugString(chain []*Certificate) string { var chainStr string for _, cert := range chain { @@ -211,6 +251,10 @@ func chainToDebugString(chain []*Certificate) string { return chainStr } +func nameToKey(name *pkix.Name) string { + return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName +} + const verisignRoot = `-----BEGIN CERTIFICATE----- MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index 8fda47159..8dae7e7fc 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -6,122 +6,73 @@ package x509 import ( - "asn1" - "big" "bytes" "crypto" "crypto/dsa" "crypto/rsa" "crypto/sha1" "crypto/x509/pkix" + "encoding/asn1" "encoding/pem" + "errors" "io" - "os" + "math/big" "time" ) -// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key. -type pkcs1PrivateKey struct { - Version int - N *big.Int - E int - D *big.Int - P *big.Int - Q *big.Int - // We ignore these values, if present, because rsa will calculate them. - Dp *big.Int `asn1:"optional"` - Dq *big.Int `asn1:"optional"` - Qinv *big.Int `asn1:"optional"` - - AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"` +// pkixPublicKey reflects a PKIX public key structure. See SubjectPublicKeyInfo +// in RFC 3280. +type pkixPublicKey struct { + Algo pkix.AlgorithmIdentifier + BitString asn1.BitString } -type pkcs1AdditionalRSAPrime struct { - Prime *big.Int - - // We ignore these values because rsa will calculate them. - Exp *big.Int - Coeff *big.Int -} - -// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form. -func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { - var priv pkcs1PrivateKey - rest, err := asn1.Unmarshal(der, &priv) - if len(rest) > 0 { - err = asn1.SyntaxError{"trailing data"} +// ParsePKIXPublicKey parses a DER encoded public key. These values are +// typically found in PEM blocks with "BEGIN PUBLIC KEY". +func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) { + var pki publicKeyInfo + if _, err = asn1.Unmarshal(derBytes, &pki); err != nil { return } - if err != nil { - return - } - - if priv.Version > 1 { - return nil, os.NewError("x509: unsupported private key version") - } - - if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 { - return nil, os.NewError("private key contains zero or negative value") - } - - key = new(rsa.PrivateKey) - key.PublicKey = rsa.PublicKey{ - E: priv.E, - N: priv.N, - } - - key.D = priv.D - key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes)) - key.Primes[0] = priv.P - key.Primes[1] = priv.Q - for i, a := range priv.AdditionalPrimes { - if a.Prime.Sign() <= 0 { - return nil, os.NewError("private key contains zero or negative prime") - } - key.Primes[i+2] = a.Prime - // We ignore the other two values because rsa will calculate - // them as needed. - } - - err = key.Validate() - if err != nil { - return nil, err + algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm) + if algo == UnknownPublicKeyAlgorithm { + return nil, errors.New("ParsePKIXPublicKey: unknown public key algorithm") } - key.Precompute() - - return + return parsePublicKey(algo, &pki) } -// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form. -func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { - key.Precompute() - - version := 0 - if len(key.Primes) > 2 { - version = 1 - } +// MarshalPKIXPublicKey serialises a public key to DER-encoded PKIX format. +func MarshalPKIXPublicKey(pub interface{}) ([]byte, error) { + var pubBytes []byte - priv := pkcs1PrivateKey{ - Version: version, - N: key.N, - E: key.PublicKey.E, - D: key.D, - P: key.Primes[0], - Q: key.Primes[1], - Dp: key.Precomputed.Dp, - Dq: key.Precomputed.Dq, - Qinv: key.Precomputed.Qinv, + switch pub := pub.(type) { + case *rsa.PublicKey: + pubBytes, _ = asn1.Marshal(rsaPublicKey{ + N: pub.N, + E: pub.E, + }) + default: + return nil, errors.New("MarshalPKIXPublicKey: unknown public key type") } - priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues)) - for i, values := range key.Precomputed.CRTValues { - priv.AdditionalPrimes[i].Prime = key.Primes[2+i] - priv.AdditionalPrimes[i].Exp = values.Exp - priv.AdditionalPrimes[i].Coeff = values.Coeff + pkix := pkixPublicKey{ + Algo: pkix.AlgorithmIdentifier{ + Algorithm: []int{1, 2, 840, 113549, 1, 1, 1}, + // This is a NULL parameters value which is technically + // superfluous, but most other code includes it and, by + // doing this, we match their public key hashes. + Parameters: asn1.RawValue{ + Tag: 5, + }, + }, + BitString: asn1.BitString{ + Bytes: pubBytes, + BitLength: 8 * len(pubBytes), + }, } - b, _ := asn1.Marshal(priv) - return b + ret, _ := asn1.Marshal(pkix) + return ret, nil } // These structures reflect the ASN.1 structure of X.509 certificates.: @@ -138,9 +89,9 @@ type tbsCertificate struct { Version int `asn1:"optional,explicit,default:1,tag:0"` SerialNumber *big.Int SignatureAlgorithm pkix.AlgorithmIdentifier - Issuer pkix.RDNSequence + Issuer asn1.RawValue Validity validity - Subject pkix.RDNSequence + Subject asn1.RawValue PublicKey publicKeyInfo UniqueId asn1.BitString `asn1:"optional,tag:1"` SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"` @@ -156,7 +107,7 @@ type dsaSignature struct { } type validity struct { - NotBefore, NotAfter *time.Time + NotBefore, NotAfter time.Time } type publicKeyInfo struct { @@ -202,7 +153,7 @@ const ( // // md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } // -// md5WithRSAEncryption OBJECT IDENTIFER ::= { pkcs-1 4 } +// md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } // // sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } // @@ -221,9 +172,9 @@ const ( // // RFC 5758 3.1 DSA Signature Algorithms // -// dsaWithSha356 OBJECT IDENTIFER ::= { +// dsaWithSha256 OBJECT IDENTIFIER ::= { // joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) -// algorithms(4) id-dsa-with-sha2(3) 2} +// csor(3) algorithms(4) id-dsa-with-sha2(3) 2} // var ( oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} @@ -339,6 +290,8 @@ type Certificate struct { Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature). RawTBSCertificate []byte // Certificate part of raw ASN.1 DER content. RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo. + RawSubject []byte // DER encoded Subject + RawIssuer []byte // DER encoded Issuer Signature []byte SignatureAlgorithm SignatureAlgorithm @@ -350,7 +303,7 @@ type Certificate struct { SerialNumber *big.Int Issuer pkix.Name Subject pkix.Name - NotBefore, NotAfter *time.Time // Validity bounds. + NotBefore, NotAfter time.Time // Validity bounds. KeyUsage KeyUsage ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages. @@ -374,21 +327,17 @@ type Certificate struct { PolicyIdentifiers []asn1.ObjectIdentifier } -// UnsupportedAlgorithmError results from attempting to perform an operation -// that involves algorithms that are not currently implemented. -type UnsupportedAlgorithmError struct{} - -func (UnsupportedAlgorithmError) String() string { - return "cannot verify signature: algorithm unimplemented" -} +// ErrUnsupportedAlgorithm results from attempting to perform an operation that +// involves algorithms that are not currently implemented. +var ErrUnsupportedAlgorithm = errors.New("crypto/x509: cannot verify signature: algorithm unimplemented") // ConstraintViolationError results when a requested usage is not permitted by // a certificate. For example: checking a signature when the public key isn't a // certificate signing key. type ConstraintViolationError struct{} -func (ConstraintViolationError) String() string { - return "invalid signature: parent certificate cannot sign this kind of certificate" +func (ConstraintViolationError) Error() string { + return "crypto/x509: invalid signature: parent certificate cannot sign this kind of certificate" } func (c *Certificate) Equal(other *Certificate) bool { @@ -397,7 +346,7 @@ func (c *Certificate) Equal(other *Certificate) bool { // CheckSignatureFrom verifies that the signature on c is a valid signature // from parent. -func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err os.Error) { +func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err error) { // RFC 5280, 4.2.1.9: // "If the basic constraints extension is not present in a version 3 // certificate, or the extension is present but the cA boolean is not @@ -413,7 +362,7 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err os.Error) { } if parent.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm { - return UnsupportedAlgorithmError{} + return ErrUnsupportedAlgorithm } // TODO(agl): don't ignore the path length constraint. @@ -423,7 +372,7 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err os.Error) { // CheckSignature verifies that signature is a valid signature over signed from // c's public key. -func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) (err os.Error) { +func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) (err error) { var hashType crypto.Hash switch algo { @@ -436,16 +385,16 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature case SHA512WithRSA: hashType = crypto.SHA512 default: - return UnsupportedAlgorithmError{} + return ErrUnsupportedAlgorithm } h := hashType.New() if h == nil { - return UnsupportedAlgorithmError{} + return ErrUnsupportedAlgorithm } h.Write(signed) - digest := h.Sum() + digest := h.Sum(nil) switch pub := c.PublicKey.(type) { case *rsa.PublicKey: @@ -456,36 +405,31 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature return err } if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { - return os.NewError("DSA signature contained zero or negative values") + return errors.New("DSA signature contained zero or negative values") } if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { - return os.NewError("DSA verification failure") + return errors.New("DSA verification failure") } return } - return UnsupportedAlgorithmError{} + return ErrUnsupportedAlgorithm } // CheckCRLSignature checks that the signature in crl is from c. -func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err os.Error) { +func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err error) { algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algorithm) return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign()) } type UnhandledCriticalExtension struct{} -func (h UnhandledCriticalExtension) String() string { +func (h UnhandledCriticalExtension) Error() string { return "unhandled critical extension" } type basicConstraints struct { IsCA bool `asn1:"optional"` - MaxPathLen int `asn1:"optional"` -} - -type rsaPublicKey struct { - N *big.Int - E int + MaxPathLen int `asn1:"optional,default:-1"` } // RFC 5280 4.2.1.4 @@ -506,7 +450,7 @@ type generalSubtree struct { Max int `asn1:"optional,tag:1"` } -func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, os.Error) { +func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) { asn1Data := keyData.PublicKey.RightAlign() switch algo { case RSA: @@ -534,7 +478,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ return nil, err } if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 { - return nil, os.NewError("zero or negative DSA parameter") + return nil, errors.New("zero or negative DSA parameter") } pub := &dsa.PublicKey{ Parameters: dsa.Parameters{ @@ -551,11 +495,13 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ panic("unreachable") } -func parseCertificate(in *certificate) (*Certificate, os.Error) { +func parseCertificate(in *certificate) (*Certificate, error) { out := new(Certificate) out.Raw = in.Raw out.RawTBSCertificate = in.TBSCertificate.Raw out.RawSubjectPublicKeyInfo = in.TBSCertificate.PublicKey.Raw + out.RawSubject = in.TBSCertificate.Subject.FullBytes + out.RawIssuer = in.TBSCertificate.Issuer.FullBytes out.Signature = in.SignatureValue.RightAlign() out.SignatureAlgorithm = @@ -563,20 +509,30 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { out.PublicKeyAlgorithm = getPublicKeyAlgorithmFromOID(in.TBSCertificate.PublicKey.Algorithm.Algorithm) - var err os.Error + var err error out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCertificate.PublicKey) if err != nil { return nil, err } if in.TBSCertificate.SerialNumber.Sign() < 0 { - return nil, os.NewError("negative serial number") + return nil, errors.New("negative serial number") } out.Version = in.TBSCertificate.Version + 1 out.SerialNumber = in.TBSCertificate.SerialNumber - out.Issuer.FillFromRDNSequence(&in.TBSCertificate.Issuer) - out.Subject.FillFromRDNSequence(&in.TBSCertificate.Subject) + + var issuer, subject pkix.RDNSequence + if _, err := asn1.Unmarshal(in.TBSCertificate.Subject.FullBytes, &subject); err != nil { + return nil, err + } + if _, err := asn1.Unmarshal(in.TBSCertificate.Issuer.FullBytes, &issuer); err != nil { + return nil, err + } + + out.Issuer.FillFromRDNSequence(&issuer) + out.Subject.FillFromRDNSequence(&subject) + out.NotBefore = in.TBSCertificate.Validity.NotBefore out.NotAfter = in.TBSCertificate.Validity.NotAfter @@ -632,7 +588,7 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { return nil, err } if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 { - return nil, asn1.StructuralError{"bad SAN sequence"} + return nil, asn1.StructuralError{Msg: "bad SAN sequence"} } parsedName := false @@ -777,14 +733,14 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { } // ParseCertificate parses a single certificate from the given ASN.1 DER data. -func ParseCertificate(asn1Data []byte) (*Certificate, os.Error) { +func ParseCertificate(asn1Data []byte) (*Certificate, error) { var cert certificate rest, err := asn1.Unmarshal(asn1Data, &cert) if err != nil { return nil, err } if len(rest) > 0 { - return nil, asn1.SyntaxError{"trailing data"} + return nil, asn1.SyntaxError{Msg: "trailing data"} } return parseCertificate(&cert) @@ -792,12 +748,12 @@ func ParseCertificate(asn1Data []byte) (*Certificate, os.Error) { // ParseCertificates parses one or more certificates from the given ASN.1 DER // data. The certificates must be concatenated with no intermediate padding. -func ParseCertificates(asn1Data []byte) ([]*Certificate, os.Error) { +func ParseCertificates(asn1Data []byte) ([]*Certificate, error) { var v []*certificate for len(asn1Data) > 0 { cert := new(certificate) - var err os.Error + var err error asn1Data, err = asn1.Unmarshal(asn1Data, cert) if err != nil { return nil, err @@ -834,8 +790,8 @@ var ( oidExtensionNameConstraints = []int{2, 5, 29, 30} ) -func buildExtensions(template *Certificate) (ret []pkix.Extension, err os.Error) { - ret = make([]pkix.Extension, 7 /* maximum number of elements. */ ) +func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { + ret = make([]pkix.Extension, 7 /* maximum number of elements. */) n := 0 if template.KeyUsage != 0 { @@ -939,21 +895,41 @@ var ( oidRSA = []int{1, 2, 840, 113549, 1, 1, 1} ) -// CreateSelfSignedCertificate 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. +func subjectBytes(cert *Certificate) ([]byte, error) { + if len(cert.RawSubject) > 0 { + return cert.RawSubject, nil + } + + return asn1.Marshal(cert.Subject.ToRDNSequence()) +} + +// 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. // // 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 // signee and priv is the private key of the signer. // // The returned slice is the certificate in DER encoding. -func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.PublicKey, priv *rsa.PrivateKey) (cert []byte, err os.Error) { +// +// The only supported key type is RSA (*rsa.PublicKey for pub, *rsa.PrivateKey +// 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") + } + + rsaPriv, ok := priv.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("x509: non-RSA private keys not supported") + } + asn1PublicKey, err := asn1.Marshal(rsaPublicKey{ - N: pub.N, - E: pub.E, + N: rsaPub.N, + E: rsaPub.E, }) if err != nil { return @@ -968,14 +944,24 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P return } + asn1Issuer, err := subjectBytes(parent) + if err != nil { + return + } + + asn1Subject, err := subjectBytes(template) + if err != nil { + return + } + encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey} c := tbsCertificate{ Version: 2, SerialNumber: template.SerialNumber, SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, - Issuer: parent.Subject.ToRDNSequence(), + Issuer: asn1.RawValue{FullBytes: asn1Issuer}, Validity: validity{template.NotBefore, template.NotAfter}, - Subject: template.Subject.ToRDNSequence(), + Subject: asn1.RawValue{FullBytes: asn1Subject}, PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey}, Extensions: extensions, } @@ -989,9 +975,9 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P h := sha1.New() h.Write(tbsCertContents) - digest := h.Sum() + digest := h.Sum(nil) - signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest) + signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest) if err != nil { return } @@ -1008,6 +994,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P // pemCRLPrefix is the magic string that indicates that we have a PEM encoded // CRL. var pemCRLPrefix = []byte("-----BEGIN X509 CRL") + // pemType is the type of a PEM encoded CRL. var pemType = "X509 CRL" @@ -1015,7 +1002,7 @@ var pemType = "X509 CRL" // encoded CRLs will appear where they should be DER encoded, so this function // will transparently handle PEM encoding as long as there isn't any leading // garbage. -func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err os.Error) { +func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err error) { if bytes.HasPrefix(crlBytes, pemCRLPrefix) { block, _ := pem.Decode(crlBytes) if block != nil && block.Type == pemType { @@ -1026,7 +1013,7 @@ func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err os.Error) { } // ParseDERCRL parses a DER encoded CRL from the given bytes. -func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err os.Error) { +func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err error) { certList = new(pkix.CertificateList) _, err = asn1.Unmarshal(derBytes, certList) if err != nil { @@ -1037,7 +1024,13 @@ func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err os.Error) // CreateCRL returns a DER encoded CRL, signed by this Certificate, that // contains the given list of revoked certificates. -func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCerts []pkix.RevokedCertificate, now, expiry *time.Time) (crlBytes []byte, err os.Error) { +// +// The only supported key type is RSA (*rsa.PrivateKey for priv). +func (c *Certificate) CreateCRL(rand io.Reader, priv interface{}, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) { + rsaPriv, ok := priv.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("x509: non-RSA private keys not supported") + } tbsCertList := pkix.TBSCertificateList{ Version: 2, Signature: pkix.AlgorithmIdentifier{ @@ -1056,9 +1049,9 @@ func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCer h := sha1.New() h.Write(tbsCertListContents) - digest := h.Sum() + digest := h.Sum(nil) - signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest) + signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest) if err != nil { return } diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go index dc216505e..f0327b012 100644 --- a/src/pkg/crypto/x509/x509_test.go +++ b/src/pkg/crypto/x509/x509_test.go @@ -5,15 +5,16 @@ package x509 import ( - "asn1" - "big" + "bytes" "crypto/dsa" "crypto/rand" "crypto/rsa" "crypto/x509/pkix" + "encoding/asn1" "encoding/base64" "encoding/hex" "encoding/pem" + "math/big" "testing" "time" ) @@ -34,6 +35,40 @@ func TestParsePKCS1PrivateKey(t *testing.T) { } } +func TestParsePKIXPublicKey(t *testing.T) { + block, _ := pem.Decode([]byte(pemPublicKey)) + pub, err := ParsePKIXPublicKey(block.Bytes) + if err != nil { + t.Errorf("Failed to parse RSA public key: %s", err) + return + } + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + t.Errorf("Value returned from ParsePKIXPublicKey was not an RSA public key") + return + } + + pubBytes2, err := MarshalPKIXPublicKey(rsaPub) + if err != nil { + t.Errorf("Failed to marshal RSA public key for the second time: %s", err) + return + } + if !bytes.Equal(pubBytes2, block.Bytes) { + t.Errorf("Reserialization of public key didn't match. got %x, want %x", pubBytes2, block.Bytes) + } +} + +var pemPublicKey = `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3VoPN9PKUjKFLMwOge6+ +wnDi8sbETGIx2FKXGgqtAKpzmem53kRGEQg8WeqRmp12wgp74TGpkEXsGae7RS1k +enJCnma4fii+noGH7R0qKgHvPrI2Bwa9hzsH8tHxpyM3qrXslOmD45EH9SxIDUBJ +FehNdaPbLP1gFyahKMsdfxFJLUvbUycuZSJ2ZnIgeVxwm4qbSvZInL9Iu4FzuPtg +fINKcbbovy1qq4KvPIrXzhbY3PWDc6btxCf3SE0JdE1MCPThntB62/bLMSQ7xdDR +FF53oIpvxe/SCOymfWq/LW849Ytv3Xwod0+wzAP8STXG4HSELS4UedPYeHJJJYcZ ++QIDAQAB +-----END PUBLIC KEY----- +` + var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0 fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu @@ -208,14 +243,15 @@ func TestCreateSelfSignedCertificate(t *testing.T) { return } + commonName := "test.example.com" template := Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ - CommonName: "test.example.com", + CommonName: commonName, Organization: []string{"Acme Co"}, }, - NotBefore: time.SecondsToUTC(1000), - NotAfter: time.SecondsToUTC(100000), + NotBefore: time.Unix(1000, 0), + NotAfter: time.Unix(100000, 0), SubjectKeyId: []byte{1, 2, 3, 4}, KeyUsage: KeyUsageCertSign, @@ -248,6 +284,14 @@ func TestCreateSelfSignedCertificate(t *testing.T) { t.Errorf("Failed to parse name constraints: %#v", cert.PermittedDNSDomains) } + if cert.Subject.CommonName != commonName { + t.Errorf("Subject wasn't correctly copied from the template. Got %s, want %s", cert.Subject.CommonName, commonName) + } + + if cert.Issuer.CommonName != commonName { + t.Errorf("Issuer wasn't correctly copied from the template. Got %s, want %s", cert.Issuer.CommonName, commonName) + } + err = cert.CheckSignatureFrom(cert) if err != nil { t.Errorf("Signature verification failed: %s", err) @@ -352,8 +396,8 @@ func TestCRLCreation(t *testing.T) { block, _ = pem.Decode([]byte(pemCertificate)) cert, _ := ParseCertificate(block.Bytes) - now := time.SecondsToUTC(1000) - expiry := time.SecondsToUTC(10000) + now := time.Unix(1000, 0) + expiry := time.Unix(10000, 0) revokedCerts := []pkix.RevokedCertificate{ { @@ -399,7 +443,7 @@ func TestParseDERCRL(t *testing.T) { t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) } - if certList.HasExpired(1302517272) { + if certList.HasExpired(time.Unix(1302517272, 0)) { t.Errorf("CRL has expired (but shouldn't have)") } @@ -419,7 +463,7 @@ func TestParsePEMCRL(t *testing.T) { t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) } - if certList.HasExpired(1302517272) { + if certList.HasExpired(time.Unix(1302517272, 0)) { t.Errorf("CRL has expired (but shouldn't have)") } diff --git a/src/pkg/crypto/xtea/Makefile b/src/pkg/crypto/xtea/Makefile deleted file mode 100644 index 301621168..000000000 --- a/src/pkg/crypto/xtea/Makefile +++ /dev/null @@ -1,12 +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 ../../../Make.inc - -TARG=crypto/xtea -GOFILES=\ - cipher.go\ - block.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/xtea/block.go b/src/pkg/crypto/xtea/block.go deleted file mode 100644 index bf5d24599..000000000 --- a/src/pkg/crypto/xtea/block.go +++ /dev/null @@ -1,66 +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. - -/* - Implementation adapted from Needham and Wheeler's paper: - http://www.cix.co.uk/~klockstone/xtea.pdf - - A precalculated look up table is used during encryption/decryption for values that are based purely on the key. -*/ - -package xtea - -// XTEA is based on 64 rounds. -const numRounds = 64 - -// blockToUint32 reads an 8 byte slice into two uint32s. -// The block is treated as big endian. -func blockToUint32(src []byte) (uint32, uint32) { - r0 := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) - r1 := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) - return r0, r1 -} - -// uint32ToBlock writes two uint32s into an 8 byte data block. -// Values are written as big endian. -func uint32ToBlock(v0, v1 uint32, dst []byte) { - dst[0] = byte(v0 >> 24) - dst[1] = byte(v0 >> 16) - dst[2] = byte(v0 >> 8) - dst[3] = byte(v0) - dst[4] = byte(v1 >> 24) - dst[5] = byte(v1 >> 16) - dst[6] = byte(v1 >> 8) - dst[7] = byte(v1 >> 0) -} - -// encryptBlock encrypts a single 8 byte block using XTEA. -func encryptBlock(c *Cipher, dst, src []byte) { - v0, v1 := blockToUint32(src) - - // Two rounds of XTEA applied per loop - for i := 0; i < numRounds; { - v0 += ((v1<<4 ^ v1>>5) + v1) ^ c.table[i] - i++ - v1 += ((v0<<4 ^ v0>>5) + v0) ^ c.table[i] - i++ - } - - uint32ToBlock(v0, v1, dst) -} - -// decryptBlock decrypt a single 8 byte block using XTEA. -func decryptBlock(c *Cipher, dst, src []byte) { - v0, v1 := blockToUint32(src) - - // Two rounds of XTEA applied per loop - for i := numRounds; i > 0; { - i-- - v1 -= ((v0<<4 ^ v0>>5) + v0) ^ c.table[i] - i-- - v0 -= ((v1<<4 ^ v1>>5) + v1) ^ c.table[i] - } - - uint32ToBlock(v0, v1, dst) -} diff --git a/src/pkg/crypto/xtea/cipher.go b/src/pkg/crypto/xtea/cipher.go deleted file mode 100644 index b3fba3c84..000000000 --- a/src/pkg/crypto/xtea/cipher.go +++ /dev/null @@ -1,92 +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. - -// Package xtea implements XTEA encryption, as defined in Needham and Wheeler's -// 1997 technical report, "Tea extensions." -package xtea - -// For details, see http://www.cix.co.uk/~klockstone/xtea.pdf - -import ( - "os" - "strconv" -) - -// The XTEA block size in bytes. -const BlockSize = 8 - -// A Cipher is an instance of an XTEA cipher using a particular key. -// table contains a series of precalculated values that are used each round. -type Cipher struct { - table [64]uint32 -} - -type KeySizeError int - -func (k KeySizeError) String() string { - return "crypto/xtea: invalid key size " + strconv.Itoa(int(k)) -} - -// NewCipher creates and returns a new Cipher. -// The key argument should be the XTEA key. -// XTEA only supports 128 bit (16 byte) keys. -func NewCipher(key []byte) (*Cipher, os.Error) { - k := len(key) - switch k { - default: - return nil, KeySizeError(k) - case 16: - break - } - - c := new(Cipher) - initCipher(c, key) - - return c, nil -} - -// BlockSize returns the XTEA block size, 8 bytes. -// It is necessary to satisfy the Cipher interface in the -// package "crypto/cipher". -func (c *Cipher) BlockSize() int { return BlockSize } - -// Encrypt encrypts the 8 byte buffer src using the key and stores the result in dst. -// Note that for amounts of data larger than a block, -// it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). -func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c, dst, src) } - -// Decrypt decrypts the 8 byte buffer src using the key k and stores the result in dst. -func (c *Cipher) Decrypt(dst, src []byte) { decryptBlock(c, dst, src) } - -// Reset zeros the table, so that it will no longer appear in the process's memory. -func (c *Cipher) Reset() { - for i := 0; i < len(c.table); i++ { - c.table[i] = 0 - } -} - -// initCipher initializes the cipher context by creating a look up table -// of precalculated values that are based on the key. -func initCipher(c *Cipher, key []byte) { - // Load the key into four uint32s - var k [4]uint32 - for i := 0; i < len(k); i++ { - j := i << 2 // Multiply by 4 - k[i] = uint32(key[j+0])<<24 | uint32(key[j+1])<<16 | uint32(key[j+2])<<8 | uint32(key[j+3]) - } - - // Precalculate the table - const delta = 0x9E3779B9 - var sum uint32 = 0 - - // Two rounds of XTEA applied per loop - for i := 0; i < numRounds; { - c.table[i] = sum + k[sum&3] - i++ - sum += delta - c.table[i] = sum + k[(sum>>11)&3] - i++ - } -} diff --git a/src/pkg/crypto/xtea/xtea_test.go b/src/pkg/crypto/xtea/xtea_test.go deleted file mode 100644 index 217d96adc..000000000 --- a/src/pkg/crypto/xtea/xtea_test.go +++ /dev/null @@ -1,246 +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. - -package xtea - -import ( - "testing" -) - -// A sample test key for when we just want to initialize a cipher -var testKey = []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF} - -// Test that the block size for XTEA is correct -func TestBlocksize(t *testing.T) { - if BlockSize != 8 { - t.Errorf("BlockSize constant - expected 8, got %d", BlockSize) - return - } - - c, err := NewCipher(testKey) - if err != nil { - t.Errorf("NewCipher(%d bytes) = %s", len(testKey), err) - return - } - - result := c.BlockSize() - if result != 8 { - t.Errorf("BlockSize function - expected 8, got %d", result) - return - } -} - -// A series of test values to confirm that the Cipher.table array was initialized correctly -var testTable = []uint32{ - 0x00112233, 0x6B1568B8, 0xE28CE030, 0xC5089E2D, 0xC5089E2D, 0x1EFBD3A2, 0xA7845C2A, 0x78EF0917, - 0x78EF0917, 0x172682D0, 0x5B6AC714, 0x822AC955, 0x3DE68511, 0xDC1DFECA, 0x2062430E, 0x3611343F, - 0xF1CCEFFB, 0x900469B4, 0xD448ADF8, 0x2E3BE36D, 0xB6C46BF5, 0x994029F2, 0x994029F2, 0xF3335F67, - 0x6AAAD6DF, 0x4D2694DC, 0x4D2694DC, 0xEB5E0E95, 0x2FA252D9, 0x4551440A, 0x121E10D6, 0xB0558A8F, - 0xE388BDC3, 0x0A48C004, 0xC6047BC0, 0x643BF579, 0xA88039BD, 0x02736F32, 0x8AFBF7BA, 0x5C66A4A7, - 0x5C66A4A7, 0xC76AEB2C, 0x3EE262A4, 0x215E20A1, 0x215E20A1, 0x7B515616, 0x03D9DE9E, 0x1988CFCF, - 0xD5448B8B, 0x737C0544, 0xB7C04988, 0xDE804BC9, 0x9A3C0785, 0x3873813E, 0x7CB7C582, 0xD6AAFAF7, - 0x4E22726F, 0x309E306C, 0x309E306C, 0x8A9165E1, 0x1319EE69, 0xF595AC66, 0xF595AC66, 0x4F88E1DB, -} - -// Test that the cipher context is initialized correctly -func TestCipherInit(t *testing.T) { - c, err := NewCipher(testKey) - if err != nil { - t.Errorf("NewCipher(%d bytes) = %s", len(testKey), err) - return - } - - for i := 0; i < len(c.table); i++ { - if c.table[i] != testTable[i] { - t.Errorf("NewCipher() failed to initialize Cipher.table[%d] correctly. Expected %08X, got %08X", i, testTable[i], c.table[i]) - break - } - } -} - -// Test that invalid key sizes return an error -func TestInvalidKeySize(t *testing.T) { - // Test a long key - key := []byte{ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - } - - _, err := NewCipher(key) - if err == nil { - t.Errorf("Invalid key size %d didn't result in an error.", len(key)) - } - - // Test a short key - key = []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77} - - _, err = NewCipher(key) - if err == nil { - t.Errorf("Invalid key size %d didn't result in an error.", len(key)) - } -} - -// Test that we can correctly decode some bytes we have encoded -func TestEncodeDecode(t *testing.T) { - original := []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF} - input := original - output := make([]byte, BlockSize) - - c, err := NewCipher(testKey) - if err != nil { - t.Errorf("NewCipher(%d bytes) = %s", len(testKey), err) - return - } - - // Encrypt the input block - c.Encrypt(output, input) - - // Check that the output does not match the input - differs := false - for i := 0; i < len(input); i++ { - if output[i] != input[i] { - differs = true - break - } - } - if differs == false { - t.Error("Cipher.Encrypt: Failed to encrypt the input block.") - return - } - - // Decrypt the block we just encrypted - input = output - output = make([]byte, BlockSize) - c.Decrypt(output, input) - - // Check that the output from decrypt matches our initial input - for i := 0; i < len(input); i++ { - if output[i] != original[i] { - t.Errorf("Decrypted byte %d differed. Expected %02X, got %02X\n", i, original[i], output[i]) - return - } - } -} - -// Test Vectors -type CryptTest struct { - key []byte - plainText []byte - cipherText []byte -} - -var CryptTests = []CryptTest{ - // These were sourced from http://www.freemedialibrary.com/index.php/XTEA_test_vectors - { - []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, - []byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}, - []byte{0x49, 0x7d, 0xf3, 0xd0, 0x72, 0x61, 0x2c, 0xb5}, - }, - { - []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, - []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, - []byte{0xe7, 0x8f, 0x2d, 0x13, 0x74, 0x43, 0x41, 0xd8}, - }, - { - []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, - []byte{0x5a, 0x5b, 0x6e, 0x27, 0x89, 0x48, 0xd7, 0x7f}, - []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, - }, - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}, - []byte{0xa0, 0x39, 0x05, 0x89, 0xf8, 0xb8, 0xef, 0xa5}, - }, - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, - []byte{0xed, 0x23, 0x37, 0x5a, 0x82, 0x1a, 0x8c, 0x2d}, - }, - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x70, 0xe1, 0x22, 0x5d, 0x6e, 0x4e, 0x76, 0x55}, - []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, - }, - - // These vectors are from http://wiki.secondlife.com/wiki/XTEA_Strong_Encryption_Implementation#Bouncy_Castle_C.23_API - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0xDE, 0xE9, 0xD4, 0xD8, 0xF7, 0x13, 0x1E, 0xD9}, - }, - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, - []byte{0x06, 0x5C, 0x1B, 0x89, 0x75, 0xC6, 0xA8, 0x16}, - }, - { - []byte{0x01, 0x23, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78, 0x23, 0x45, 0x67, 0x89, 0x34, 0x56, 0x78, 0x9A}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x1F, 0xF9, 0xA0, 0x26, 0x1A, 0xC6, 0x42, 0x64}, - }, - { - []byte{0x01, 0x23, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78, 0x23, 0x45, 0x67, 0x89, 0x34, 0x56, 0x78, 0x9A}, - []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, - []byte{0x8C, 0x67, 0x15, 0x5B, 0x2E, 0xF9, 0x1E, 0xAD}, - }, -} - -// Test encryption -func TestCipherEncrypt(t *testing.T) { - for i, tt := range CryptTests { - c, err := NewCipher(tt.key) - if err != nil { - t.Errorf("NewCipher(%d bytes), vector %d = %s", len(tt.key), i, err) - continue - } - - out := make([]byte, len(tt.plainText)) - c.Encrypt(out, tt.plainText) - - for j := 0; j < len(out); j++ { - if out[j] != tt.cipherText[j] { - t.Errorf("Cipher.Encrypt %d: out[%d] = %02X, expected %02X", i, j, out[j], tt.cipherText[j]) - break - } - } - } -} - -// Test decryption -func TestCipherDecrypt(t *testing.T) { - for i, tt := range CryptTests { - c, err := NewCipher(tt.key) - if err != nil { - t.Errorf("NewCipher(%d bytes), vector %d = %s", len(tt.key), i, err) - continue - } - - out := make([]byte, len(tt.cipherText)) - c.Decrypt(out, tt.cipherText) - - for j := 0; j < len(out); j++ { - if out[j] != tt.plainText[j] { - t.Errorf("Cipher.Decrypt %d: out[%d] = %02X, expected %02X", i, j, out[j], tt.plainText[j]) - break - } - } - } -} - -// Test resetting the cipher context -func TestReset(t *testing.T) { - c, err := NewCipher(testKey) - if err != nil { - t.Errorf("NewCipher(%d bytes) = %s", len(testKey), err) - return - } - - c.Reset() - for i := 0; i < len(c.table); i++ { - if c.table[i] != 0 { - t.Errorf("Cipher.Reset: Failed to clear Cipher.table[%d]. expected 0, got %08X", i, c.table[i]) - return - } - } -} diff --git a/src/pkg/csv/Makefile b/src/pkg/csv/Makefile deleted file mode 100644 index e364d51d2..000000000 --- a/src/pkg/csv/Makefile +++ /dev/null @@ -1,12 +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. - -include ../../Make.inc - -TARG=csv -GOFILES=\ - reader.go\ - writer.go\ - -include ../../Make.pkg diff --git a/src/pkg/csv/reader.go b/src/pkg/csv/reader.go deleted file mode 100644 index ea2c266a4..000000000 --- a/src/pkg/csv/reader.go +++ /dev/null @@ -1,372 +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 csv reads and writes comma-separated values (CSV) files. -// -// A csv file contains zero or more records of one or more fields per record. -// Each record is separated by the newline character. The final record may -// optionally be followed by a newline character. -// -// field1,field2,field3 -// -// White space is considered part of a field. -// -// Carriage returns before newline characters are silently removed. -// -// Blank lines are ignored. A line with only whitespace characters (excluding -// the ending newline character) is not considered a blank line. -// -// Fields which start and stop with the quote character " are called -// quoted-fields. The beginning and ending quote are not part of the -// field. -// -// The source: -// -// normal string,"quoted-field" -// -// results in the fields -// -// {`normal string`, `quoted-field`} -// -// Within a quoted-field a quote character followed by a second quote -// character is considered a single quote. -// -// "the ""word"" is true","a ""quoted-field""" -// -// results in -// -// {`the "word" is true`, `a "quoted-field"`} -// -// Newlines and commas may be included in a quoted-field -// -// "Multi-line -// field","comma is ," -// -// results in -// -// {`Multi-line -// field`, `comma is ,`} -package csv - -import ( - "bufio" - "bytes" - "fmt" - "io" - "os" - "unicode" -) - -// A ParseError is returned for parsing errors. -// The first line is 1. The first column is 0. -type ParseError struct { - Line int // Line where the error occurred - Column int // Column (rune index) where the error occurred - Error os.Error // The actual error -} - -func (e *ParseError) String() string { - return fmt.Sprintf("line %d, column %d: %s", e.Line, e.Column, e.Error) -} - -// These are the errors that can be returned in ParseError.Error -var ( - ErrTrailingComma = os.NewError("extra delimiter at end of line") - ErrBareQuote = os.NewError("bare \" in non-quoted-field") - ErrQuote = os.NewError("extraneous \" in field") - ErrFieldCount = os.NewError("wrong number of fields in line") -) - -// A Reader reads records from a CSV-encoded file. -// -// As returned by NewReader, a Reader expects input conforming to RFC 4180. -// The exported fields can be changed to customize the details before the -// first call to Read or ReadAll. -// -// Comma is the field delimiter. It defaults to ','. -// -// Comment, if not 0, is the comment character. Lines beginning with the -// Comment character are ignored. -// -// If FieldsPerRecord is positive, Read requires each record to -// have the given number of fields. If FieldsPerRecord is 0, Read sets it to -// the number of fields in the first record, so that future records must -// have the same field count. -// -// If LazyQuotes is true, a quote may appear in an unquoted field and a -// non-doubled quote may appear in a quoted field. -// -// If TrailingComma is true, the last field may be an unquoted empty field. -// -// If TrimLeadingSpace is true, leading white space in a field is ignored. -type Reader struct { - Comma int // Field delimiter (set to ',' by NewReader) - Comment int // Comment character for start of line - FieldsPerRecord int // Number of expected fields per record - LazyQuotes bool // Allow lazy quotes - TrailingComma bool // Allow trailing comma - TrimLeadingSpace bool // Trim leading space - line int - column int - r *bufio.Reader - field bytes.Buffer -} - -// NewReader returns a new Reader that reads from r. -func NewReader(r io.Reader) *Reader { - return &Reader{ - Comma: ',', - r: bufio.NewReader(r), - } -} - -// error creates a new ParseError based on err. -func (r *Reader) error(err os.Error) os.Error { - return &ParseError{ - Line: r.line, - Column: r.column, - Error: err, - } -} - -// Read reads one record from r. The record is a slice of strings with each -// string representing one field. -func (r *Reader) Read() (record []string, err os.Error) { - for { - record, err = r.parseRecord() - if record != nil { - break - } - if err != nil { - return nil, err - } - } - - if r.FieldsPerRecord > 0 { - if len(record) != r.FieldsPerRecord { - r.column = 0 // report at start of record - return record, r.error(ErrFieldCount) - } - } else if r.FieldsPerRecord == 0 { - r.FieldsPerRecord = len(record) - } - return record, nil -} - -// ReadAll reads all the remaining records from r. -// Each record is a slice of fields. -func (r *Reader) ReadAll() (records [][]string, err os.Error) { - for { - record, err := r.Read() - if err == os.EOF { - return records, nil - } - if err != nil { - return nil, err - } - records = append(records, record) - } - panic("unreachable") -} - -// readRune reads one rune from r, folding \r\n to \n and keeping track -// of how far into the line we have read. r.column will point to the start -// of this rune, not the end of this rune. -func (r *Reader) readRune() (int, os.Error) { - rune, _, err := r.r.ReadRune() - - // Handle \r\n here. We make the simplifying assumption that - // anytime \r is followed by \n that it can be folded to \n. - // We will not detect files which contain both \r\n and bare \n. - if rune == '\r' { - rune, _, err = r.r.ReadRune() - if err == nil { - if rune != '\n' { - r.r.UnreadRune() - rune = '\r' - } - } - } - r.column++ - return rune, err -} - -// unreadRune puts the last rune read from r back. -func (r *Reader) unreadRune() { - r.r.UnreadRune() - r.column-- -} - -// skip reads runes up to and including the rune delim or until error. -func (r *Reader) skip(delim int) os.Error { - for { - rune, err := r.readRune() - if err != nil { - return err - } - if rune == delim { - return nil - } - } - panic("unreachable") -} - -// parseRecord reads and parses a single csv record from r. -func (r *Reader) parseRecord() (fields []string, err os.Error) { - // Each record starts on a new line. We increment our line - // number (lines start at 1, not 0) and set column to -1 - // so as we increment in readRune it points to the character we read. - r.line++ - r.column = -1 - - // Peek at the first rune. If it is an error we are done. - // If we are support comments and it is the comment character - // then skip to the end of line. - - rune, _, err := r.r.ReadRune() - if err != nil { - return nil, err - } - - if r.Comment != 0 && rune == r.Comment { - return nil, r.skip('\n') - } - r.r.UnreadRune() - - // At this point we have at least one field. - for { - haveField, delim, err := r.parseField() - if haveField { - fields = append(fields, r.field.String()) - } - if delim == '\n' || err == os.EOF { - return fields, err - } else if err != nil { - return nil, err - } - } - panic("unreachable") -} - -// parseField parses the next field in the record. The read field is -// located in r.field. Delim is the first character not part of the field -// (r.Comma or '\n'). -func (r *Reader) parseField() (haveField bool, delim int, err os.Error) { - r.field.Reset() - - rune, err := r.readRune() - if err != nil { - // If we have EOF and are not at the start of a line - // then we return the empty field. We have already - // checked for trailing commas if needed. - if err == os.EOF && r.column != 0 { - return true, 0, err - } - return false, 0, err - } - - if r.TrimLeadingSpace { - for unicode.IsSpace(rune) { - rune, err = r.readRune() - if err != nil { - return false, 0, err - } - } - } - - switch rune { - case r.Comma: - // will check below - - case '\n': - // We are a trailing empty field or a blank line - if r.column == 0 { - return false, rune, nil - } - return true, rune, nil - - case '"': - // quoted field - Quoted: - for { - rune, err = r.readRune() - if err != nil { - if err == os.EOF { - if r.LazyQuotes { - return true, 0, err - } - return false, 0, r.error(ErrQuote) - } - return false, 0, err - } - switch rune { - case '"': - rune, err = r.readRune() - if err != nil || rune == r.Comma { - break Quoted - } - if rune == '\n' { - return true, rune, nil - } - if rune != '"' { - if !r.LazyQuotes { - r.column-- - return false, 0, r.error(ErrQuote) - } - // accept the bare quote - r.field.WriteRune('"') - } - case '\n': - r.line++ - r.column = -1 - } - r.field.WriteRune(rune) - } - - default: - // unquoted field - for { - r.field.WriteRune(rune) - rune, err = r.readRune() - if err != nil || rune == r.Comma { - break - } - if rune == '\n' { - return true, rune, nil - } - if !r.LazyQuotes && rune == '"' { - return false, 0, r.error(ErrBareQuote) - } - } - } - - if err != nil { - if err == os.EOF { - return true, 0, err - } - return false, 0, err - } - - if !r.TrailingComma { - // We don't allow trailing commas. See if we - // are at the end of the line (being mindful - // of trimming spaces). - c := r.column - rune, err = r.readRune() - if r.TrimLeadingSpace { - for unicode.IsSpace(rune) { - rune, err = r.readRune() - if err != nil { - break - } - } - } - if err == os.EOF || rune == '\n' { - r.column = c // report the comma - return false, 0, r.error(ErrTrailingComma) - } - r.unreadRune() - } - return true, rune, nil -} diff --git a/src/pkg/csv/reader_test.go b/src/pkg/csv/reader_test.go deleted file mode 100644 index 0068bad1d..000000000 --- a/src/pkg/csv/reader_test.go +++ /dev/null @@ -1,265 +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 csv - -import ( - "reflect" - "strings" - "testing" -) - -var readTests = []struct { - Name string - Input string - Output [][]string - UseFieldsPerRecord bool // false (default) means FieldsPerRecord is -1 - - // These fields are copied into the Reader - Comma int - Comment int - FieldsPerRecord int - LazyQuotes bool - TrailingComma bool - TrimLeadingSpace bool - - Error string - Line int // Expected error line if != 0 - Column int // Expected error column if line != 0 -}{ - { - Name: "Simple", - Input: "a,b,c\n", - Output: [][]string{{"a", "b", "c"}}, - }, - { - Name: "CRLF", - Input: "a,b\r\nc,d\r\n", - Output: [][]string{{"a", "b"}, {"c", "d"}}, - }, - { - Name: "BareCR", - Input: "a,b\rc,d\r\n", - Output: [][]string{{"a", "b\rc", "d"}}, - }, - { - Name: "RFC4180test", - UseFieldsPerRecord: true, - Input: `#field1,field2,field3 -"aaa","bb -b","ccc" -"a,a","b""bb","ccc" -zzz,yyy,xxx -`, - Output: [][]string{ - {"#field1", "field2", "field3"}, - {"aaa", "bb\nb", "ccc"}, - {"a,a", `b"bb`, "ccc"}, - {"zzz", "yyy", "xxx"}, - }, - }, - { - Name: "NoEOLTest", - Input: "a,b,c", - Output: [][]string{{"a", "b", "c"}}, - }, - { - Name: "Semicolon", - Comma: ';', - Input: "a;b;c\n", - Output: [][]string{{"a", "b", "c"}}, - }, - { - Name: "MultiLine", - Input: `"two -line","one line","three -line -field"`, - Output: [][]string{{"two\nline", "one line", "three\nline\nfield"}}, - }, - { - Name: "BlankLine", - Input: "a,b,c\n\nd,e,f\n\n", - Output: [][]string{ - {"a", "b", "c"}, - {"d", "e", "f"}, - }, - }, - { - Name: "TrimSpace", - Input: " a, b, c\n", - TrimLeadingSpace: true, - Output: [][]string{{"a", "b", "c"}}, - }, - { - Name: "LeadingSpace", - Input: " a, b, c\n", - Output: [][]string{{" a", " b", " c"}}, - }, - { - Name: "Comment", - Comment: '#', - Input: "#1,2,3\na,b,c\n#comment", - Output: [][]string{{"a", "b", "c"}}, - }, - { - Name: "NoComment", - Input: "#1,2,3\na,b,c", - Output: [][]string{{"#1", "2", "3"}, {"a", "b", "c"}}, - }, - { - Name: "LazyQuotes", - LazyQuotes: true, - Input: `a "word","1"2",a","b`, - Output: [][]string{{`a "word"`, `1"2`, `a"`, `b`}}, - }, - { - Name: "BareQuotes", - LazyQuotes: true, - Input: `a "word","1"2",a"`, - Output: [][]string{{`a "word"`, `1"2`, `a"`}}, - }, - { - Name: "BareDoubleQuotes", - LazyQuotes: true, - Input: `a""b,c`, - Output: [][]string{{`a""b`, `c`}}, - }, - { - Name: "BadDoubleQuotes", - Input: `a""b,c`, - Output: [][]string{{`a""b`, `c`}}, - Error: `bare " in non-quoted-field`, Line: 1, Column: 1, - }, - { - Name: "TrimQuote", - Input: ` "a"," b",c`, - TrimLeadingSpace: true, - Output: [][]string{{"a", " b", "c"}}, - }, - { - Name: "BadBareQuote", - Input: `a "word","b"`, - Error: `bare " in non-quoted-field`, Line: 1, Column: 2, - }, - { - Name: "BadTrailingQuote", - Input: `"a word",b"`, - Error: `bare " in non-quoted-field`, Line: 1, Column: 10, - }, - { - Name: "ExtraneousQuote", - Input: `"a "word","b"`, - Error: `extraneous " in field`, Line: 1, Column: 3, - }, - { - Name: "BadFieldCount", - UseFieldsPerRecord: true, - Input: "a,b,c\nd,e", - Error: "wrong number of fields", Line: 2, - }, - { - Name: "BadFieldCount1", - UseFieldsPerRecord: true, - FieldsPerRecord: 2, - Input: `a,b,c`, - Error: "wrong number of fields", Line: 1, - }, - { - Name: "FieldCount", - Input: "a,b,c\nd,e", - Output: [][]string{{"a", "b", "c"}, {"d", "e"}}, - }, - { - Name: "BadTrailingCommaEOF", - Input: "a,b,c,", - Error: "extra delimiter at end of line", Line: 1, Column: 5, - }, - { - Name: "BadTrailingCommaEOL", - Input: "a,b,c,\n", - Error: "extra delimiter at end of line", Line: 1, Column: 5, - }, - { - Name: "BadTrailingCommaSpaceEOF", - TrimLeadingSpace: true, - Input: "a,b,c, ", - Error: "extra delimiter at end of line", Line: 1, Column: 5, - }, - { - Name: "BadTrailingCommaSpaceEOL", - TrimLeadingSpace: true, - Input: "a,b,c, \n", - Error: "extra delimiter at end of line", Line: 1, Column: 5, - }, - { - Name: "BadTrailingCommaLine3", - TrimLeadingSpace: true, - Input: "a,b,c\nd,e,f\ng,hi,", - Error: "extra delimiter at end of line", Line: 3, Column: 4, - }, - { - Name: "NotTrailingComma3", - Input: "a,b,c, \n", - Output: [][]string{{"a", "b", "c", " "}}, - }, - { - Name: "CommaFieldTest", - TrailingComma: true, - Input: `x,y,z,w -x,y,z, -x,y,, -x,,, -,,, -"x","y","z","w" -"x","y","z","" -"x","y","","" -"x","","","" -"","","","" -`, - Output: [][]string{ - {"x", "y", "z", "w"}, - {"x", "y", "z", ""}, - {"x", "y", "", ""}, - {"x", "", "", ""}, - {"", "", "", ""}, - {"x", "y", "z", "w"}, - {"x", "y", "z", ""}, - {"x", "y", "", ""}, - {"x", "", "", ""}, - {"", "", "", ""}, - }, - }, -} - -func TestRead(t *testing.T) { - for _, tt := range readTests { - r := NewReader(strings.NewReader(tt.Input)) - r.Comment = tt.Comment - if tt.UseFieldsPerRecord { - r.FieldsPerRecord = tt.FieldsPerRecord - } else { - r.FieldsPerRecord = -1 - } - r.LazyQuotes = tt.LazyQuotes - r.TrailingComma = tt.TrailingComma - r.TrimLeadingSpace = tt.TrimLeadingSpace - if tt.Comma != 0 { - r.Comma = tt.Comma - } - out, err := r.ReadAll() - perr, _ := err.(*ParseError) - if tt.Error != "" { - if err == nil || !strings.Contains(err.String(), tt.Error) { - t.Errorf("%s: error %v, want error %q", tt.Name, err, tt.Error) - } else if tt.Line != 0 && (tt.Line != perr.Line || tt.Column != perr.Column) { - t.Errorf("%s: error at %d:%d expected %d:%d", tt.Name, perr.Line, perr.Column, tt.Line, tt.Column) - } - } else if err != nil { - t.Errorf("%s: unexpected error %v", tt.Name, err) - } else if !reflect.DeepEqual(out, tt.Output) { - t.Errorf("%s: out=%q want %q", tt.Name, out, tt.Output) - } - } -} diff --git a/src/pkg/csv/writer.go b/src/pkg/csv/writer.go deleted file mode 100644 index ccf703f0f..000000000 --- a/src/pkg/csv/writer.go +++ /dev/null @@ -1,122 +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 csv - -import ( - "bufio" - "io" - "os" - "strings" - "unicode" - "utf8" -) - -// A Writer writes records to a CSV encoded file. -// -// As returned by NewWriter, a Writer writes records terminated by a -// newline and uses ',' as the field delimiter. The exported fields can be -// changed to customize the details before the first call to Write or WriteAll. -// -// Comma is the field delimiter. -// -// If UseCRLF is true, the Writer ends each record with \r\n instead of \n. -type Writer struct { - Comma int // Field delimiter (set to to ',' by NewWriter) - UseCRLF bool // True to use \r\n as the line terminator - w *bufio.Writer -} - -// NewWriter returns a new Writer that writes to w. -func NewWriter(w io.Writer) *Writer { - return &Writer{ - Comma: ',', - w: bufio.NewWriter(w), - } -} - -// Writer writes a single CSV record to w along with any necessary quoting. -// A record is a slice of strings with each string being one field. -func (w *Writer) Write(record []string) (err os.Error) { - for n, field := range record { - if n > 0 { - if _, err = w.w.WriteRune(w.Comma); err != nil { - return - } - } - - // If we don't have to have a quoted field then just - // write out the field and continue to the next field. - if !w.fieldNeedsQuotes(field) { - if _, err = w.w.WriteString(field); err != nil { - return - } - continue - } - if err = w.w.WriteByte('"'); err != nil { - return - } - - for _, rune := range field { - switch rune { - case '"': - _, err = w.w.WriteString(`""`) - case '\r': - if !w.UseCRLF { - err = w.w.WriteByte('\r') - } - case '\n': - if w.UseCRLF { - _, err = w.w.WriteString("\r\n") - } else { - err = w.w.WriteByte('\n') - } - default: - _, err = w.w.WriteRune(rune) - } - if err != nil { - return - } - } - - if err = w.w.WriteByte('"'); err != nil { - return - } - } - if w.UseCRLF { - _, err = w.w.WriteString("\r\n") - } else { - err = w.w.WriteByte('\n') - } - return -} - -// Flush writes any buffered data to the underlying io.Writer. -func (w *Writer) Flush() { - w.w.Flush() -} - -// WriteAll writes multiple CSV records to w using Write and then calls Flush. -func (w *Writer) WriteAll(records [][]string) (err os.Error) { - for _, record := range records { - err = w.Write(record) - if err != nil { - break - } - } - w.Flush() - return nil -} - -// fieldNeedsQuotes returns true if our field must be enclosed in quotes. -// Empty fields, files with a Comma, fields with a quote or newline, and -// fields which start with a space must be enclosed in quotes. -func (w *Writer) fieldNeedsQuotes(field string) bool { - if len(field) == 0 || strings.IndexRune(field, w.Comma) >= 0 || strings.IndexAny(field, "\"\r\n") >= 0 { - return true - } - - rune, _ := utf8.DecodeRuneInString(field) - return unicode.IsSpace(rune) -} diff --git a/src/pkg/csv/writer_test.go b/src/pkg/csv/writer_test.go deleted file mode 100644 index 578959007..000000000 --- a/src/pkg/csv/writer_test.go +++ /dev/null @@ -1,44 +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 csv - -import ( - "bytes" - "testing" -) - -var writeTests = []struct { - Input [][]string - Output string - UseCRLF bool -}{ - {Input: [][]string{{"abc"}}, Output: "abc\n"}, - {Input: [][]string{{"abc"}}, Output: "abc\r\n", UseCRLF: true}, - {Input: [][]string{{`"abc"`}}, Output: `"""abc"""` + "\n"}, - {Input: [][]string{{`a"b`}}, Output: `"a""b"` + "\n"}, - {Input: [][]string{{`"a"b"`}}, Output: `"""a""b"""` + "\n"}, - {Input: [][]string{{" abc"}}, Output: `" abc"` + "\n"}, - {Input: [][]string{{"abc,def"}}, Output: `"abc,def"` + "\n"}, - {Input: [][]string{{"abc", "def"}}, Output: "abc,def\n"}, - {Input: [][]string{{"abc"}, {"def"}}, Output: "abc\ndef\n"}, - {Input: [][]string{{"abc\ndef"}}, Output: "\"abc\ndef\"\n"}, - {Input: [][]string{{"abc\ndef"}}, Output: "\"abc\r\ndef\"\r\n", UseCRLF: true}, -} - -func TestWrite(t *testing.T) { - for n, tt := range writeTests { - b := &bytes.Buffer{} - f := NewWriter(b) - f.UseCRLF = tt.UseCRLF - err := f.WriteAll(tt.Input) - if err != nil { - t.Errorf("Unexpected error: %s\n", err) - } - out := b.String() - if out != tt.Output { - t.Errorf("#%d: out=%q want %q", n, out, tt.Output) - } - } -} diff --git a/src/pkg/database/sql/convert.go b/src/pkg/database/sql/convert.go new file mode 100644 index 000000000..bfcb03ccf --- /dev/null +++ b/src/pkg/database/sql/convert.go @@ -0,0 +1,158 @@ +// 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 conversions for Scan. + +package sql + +import ( + "database/sql/driver" + "errors" + "fmt" + "reflect" + "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)) + for n, arg := range args { + var err error + out[n], err = driver.DefaultParameterConverter.ConvertValue(arg) + if err != nil { + return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n+1, err) + } + } + return out, 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. + switch s := src.(type) { + case string: + switch d := dest.(type) { + case *string: + *d = s + return nil + case *[]byte: + *d = []byte(s) + return nil + } + case []byte: + switch d := dest.(type) { + case *string: + *d = string(s) + return nil + case *interface{}: + bcopy := make([]byte, len(s)) + copy(bcopy, s) + *d = bcopy + return nil + case *[]byte: + *d = s + return nil + } + case nil: + switch d := dest.(type) { + case *[]byte: + *d = nil + return nil + } + } + + var sv reflect.Value + + switch d := dest.(type) { + case *string: + sv = reflect.ValueOf(src) + switch sv.Kind() { + case reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + *d = fmt.Sprintf("%v", src) + return nil + } + case *bool: + bv, err := driver.Bool.ConvertValue(src) + if err == nil { + *d = bv.(bool) + } + return err + case *interface{}: + *d = src + return nil + } + + if scanner, ok := dest.(Scanner); ok { + return scanner.Scan(src) + } + + dpv := reflect.ValueOf(dest) + if dpv.Kind() != reflect.Ptr { + return errors.New("destination not a pointer") + } + + if !sv.IsValid() { + sv = reflect.ValueOf(src) + } + + dv := reflect.Indirect(dpv) + if dv.Kind() == sv.Kind() { + dv.Set(sv) + return nil + } + + switch dv.Kind() { + case reflect.Ptr: + if src == nil { + dv.Set(reflect.Zero(dv.Type())) + return nil + } else { + dv.Set(reflect.New(dv.Type().Elem())) + return convertAssign(dv.Interface(), src) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + s := asString(src) + i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) + if err != nil { + return fmt.Errorf("converting string %q to a %s: %v", s, dv.Kind(), err) + } + dv.SetInt(i64) + return nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + s := asString(src) + u64, err := strconv.ParseUint(s, 10, dv.Type().Bits()) + if err != nil { + return fmt.Errorf("converting string %q to a %s: %v", s, dv.Kind(), err) + } + dv.SetUint(u64) + return nil + case reflect.Float32, reflect.Float64: + s := asString(src) + f64, err := strconv.ParseFloat(s, dv.Type().Bits()) + if err != nil { + return fmt.Errorf("converting string %q to a %s: %v", s, dv.Kind(), err) + } + dv.SetFloat(f64) + return nil + } + + return fmt.Errorf("unsupported driver -> Scan pair: %T -> %T", src, dest) +} + +func asString(src interface{}) string { + switch v := src.(type) { + case string: + return v + case []byte: + return string(v) + } + return fmt.Sprintf("%v", src) +} diff --git a/src/pkg/database/sql/convert_test.go b/src/pkg/database/sql/convert_test.go new file mode 100644 index 000000000..9c362d732 --- /dev/null +++ b/src/pkg/database/sql/convert_test.go @@ -0,0 +1,250 @@ +// 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 sql + +import ( + "database/sql/driver" + "fmt" + "reflect" + "testing" + "time" +) + +var someTime = time.Unix(123, 0) +var answer int64 = 42 + +type conversionTest struct { + s, d interface{} // source and destination + + // following are used if they're non-zero + wantint int64 + wantuint uint64 + wantstr string + wantf32 float32 + wantf64 float64 + wanttime time.Time + wantbool bool // used if d is of type *bool + wanterr string + wantiface interface{} + wantptr *int64 // if non-nil, *d's pointed value must be equal to *wantptr + wantnil bool // if true, *d must be *int64(nil) +} + +// Target variables for scanning into. +var ( + scanstr string + scanint int + scanint8 int8 + scanint16 int16 + scanint32 int32 + scanuint8 uint8 + scanuint16 uint16 + scanbool bool + scanf32 float32 + scanf64 float64 + scantime time.Time + scanptr *int64 + scaniface interface{} +) + +var conversionTests = []conversionTest{ + // Exact conversions (destination pointer type matches source type) + {s: "foo", d: &scanstr, wantstr: "foo"}, + {s: 123, d: &scanint, wantint: 123}, + {s: someTime, d: &scantime, wanttime: someTime}, + + // To strings + {s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"}, + {s: 123, d: &scanstr, wantstr: "123"}, + {s: int8(123), d: &scanstr, wantstr: "123"}, + {s: int64(123), d: &scanstr, wantstr: "123"}, + {s: uint8(123), d: &scanstr, wantstr: "123"}, + {s: uint16(123), d: &scanstr, wantstr: "123"}, + {s: uint32(123), d: &scanstr, wantstr: "123"}, + {s: uint64(123), d: &scanstr, wantstr: "123"}, + {s: 1.5, d: &scanstr, wantstr: "1.5"}, + + // Strings to integers + {s: "255", d: &scanuint8, wantuint: 255}, + {s: "256", d: &scanuint8, wanterr: `converting string "256" to a uint8: strconv.ParseUint: parsing "256": value out of range`}, + {s: "256", d: &scanuint16, wantuint: 256}, + {s: "-1", d: &scanint, wantint: -1}, + {s: "foo", d: &scanint, wanterr: `converting string "foo" to a int: strconv.ParseInt: parsing "foo": invalid syntax`}, + + // True bools + {s: true, d: &scanbool, wantbool: true}, + {s: "True", d: &scanbool, wantbool: true}, + {s: "TRUE", d: &scanbool, wantbool: true}, + {s: "1", d: &scanbool, wantbool: true}, + {s: 1, d: &scanbool, wantbool: true}, + {s: int64(1), d: &scanbool, wantbool: true}, + {s: uint16(1), d: &scanbool, wantbool: true}, + + // False bools + {s: false, d: &scanbool, wantbool: false}, + {s: "false", d: &scanbool, wantbool: false}, + {s: "FALSE", d: &scanbool, wantbool: false}, + {s: "0", d: &scanbool, wantbool: false}, + {s: 0, d: &scanbool, wantbool: false}, + {s: int64(0), d: &scanbool, wantbool: false}, + {s: uint16(0), d: &scanbool, wantbool: false}, + + // Not bools + {s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`}, + {s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`}, + + // Floats + {s: float64(1.5), d: &scanf64, wantf64: float64(1.5)}, + {s: int64(1), d: &scanf64, wantf64: float64(1)}, + {s: float64(1.5), d: &scanf32, wantf32: float32(1.5)}, + {s: "1.5", d: &scanf32, wantf32: float32(1.5)}, + {s: "1.5", d: &scanf64, wantf64: float64(1.5)}, + + // Pointers + {s: interface{}(nil), d: &scanptr, wantnil: true}, + {s: int64(42), d: &scanptr, wantptr: &answer}, + + // To interface{} + {s: float64(1.5), d: &scaniface, wantiface: float64(1.5)}, + {s: int64(1), d: &scaniface, wantiface: int64(1)}, + {s: "str", d: &scaniface, wantiface: "str"}, + {s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")}, + {s: true, d: &scaniface, wantiface: true}, + {s: nil, d: &scaniface}, +} + +func intPtrValue(intptr interface{}) interface{} { + return reflect.Indirect(reflect.Indirect(reflect.ValueOf(intptr))).Int() +} + +func intValue(intptr interface{}) int64 { + return reflect.Indirect(reflect.ValueOf(intptr)).Int() +} + +func uintValue(intptr interface{}) uint64 { + return reflect.Indirect(reflect.ValueOf(intptr)).Uint() +} + +func float64Value(ptr interface{}) float64 { + return *(ptr.(*float64)) +} + +func float32Value(ptr interface{}) float32 { + return *(ptr.(*float32)) +} + +func timeValue(ptr interface{}) time.Time { + return *(ptr.(*time.Time)) +} + +func TestConversions(t *testing.T) { + for n, ct := range conversionTests { + err := convertAssign(ct.d, ct.s) + errstr := "" + if err != nil { + errstr = err.Error() + } + errf := func(format string, args ...interface{}) { + base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d) + t.Errorf(base+format, args...) + } + if errstr != ct.wanterr { + errf("got error %q, want error %q", errstr, ct.wanterr) + } + if ct.wantstr != "" && ct.wantstr != scanstr { + errf("want string %q, got %q", ct.wantstr, scanstr) + } + if ct.wantint != 0 && ct.wantint != intValue(ct.d) { + errf("want int %d, got %d", ct.wantint, intValue(ct.d)) + } + if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) { + errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d)) + } + if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) { + errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d)) + } + if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) { + errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d)) + } + if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" { + errf("want bool %v, got %v", ct.wantbool, *bp) + } + if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) { + errf("want time %v, got %v", ct.wanttime, timeValue(ct.d)) + } + if ct.wantnil && *ct.d.(**int64) != nil { + errf("want nil, got %v", intPtrValue(ct.d)) + } + if ct.wantptr != nil { + if *ct.d.(**int64) == nil { + errf("want pointer to %v, got nil", *ct.wantptr) + } else if *ct.wantptr != intPtrValue(ct.d) { + errf("want pointer to %v, got %v", *ct.wantptr, intPtrValue(ct.d)) + } + } + if ifptr, ok := ct.d.(*interface{}); ok { + if !reflect.DeepEqual(ct.wantiface, scaniface) { + errf("want interface %#v, got %#v", ct.wantiface, scaniface) + continue + } + if srcBytes, ok := ct.s.([]byte); ok { + dstBytes := (*ifptr).([]byte) + if &dstBytes[0] == &srcBytes[0] { + errf("copy into interface{} didn't copy []byte data") + } + } + } + } +} + +func TestNullString(t *testing.T) { + var ns NullString + convertAssign(&ns, []byte("foo")) + if !ns.Valid { + t.Errorf("expecting not null") + } + if ns.String != "foo" { + t.Errorf("expecting foo; got %q", ns.String) + } + convertAssign(&ns, nil) + if ns.Valid { + t.Errorf("expecting null on nil") + } + if ns.String != "" { + t.Errorf("expecting blank on nil; got %q", ns.String) + } +} + +type valueConverterTest struct { + c driver.ValueConverter + in, out interface{} + err string +} + +var valueConverterTests = []valueConverterTest{ + {driver.DefaultParameterConverter, NullString{"hi", true}, "hi", ""}, + {driver.DefaultParameterConverter, NullString{"", false}, nil, ""}, +} + +func TestValueConverters(t *testing.T) { + for i, tt := range valueConverterTests { + out, err := tt.c.ConvertValue(tt.in) + goterr := "" + if err != nil { + goterr = err.Error() + } + if goterr != tt.err { + t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q", + i, tt.c, tt.in, tt.in, goterr, tt.err) + } + if tt.err != "" { + continue + } + if !reflect.DeepEqual(out, tt.out) { + t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)", + i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out) + } + } +} diff --git a/src/pkg/database/sql/doc.txt b/src/pkg/database/sql/doc.txt new file mode 100644 index 000000000..fb1659548 --- /dev/null +++ b/src/pkg/database/sql/doc.txt @@ -0,0 +1,46 @@ +Goals of the sql and sql/driver packages: + +* Provide a generic database API for a variety of SQL or SQL-like + databases. There currently exist Go libraries for SQLite, MySQL, + and Postgres, but all with a very different feel, and often + a non-Go-like feel. + +* Feel like Go. + +* Care mostly about the common cases. Common SQL should be portable. + SQL edge cases or db-specific extensions can be detected and + conditionally used by the application. It is a non-goal to care + about every particular db's extension or quirk. + +* Separate out the basic implementation of a database driver + (implementing the sql/driver interfaces) vs the implementation + of all the user-level types and convenience methods. + In a nutshell: + + User Code ---> sql package (concrete types) ---> sql/driver (interfaces) + 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, + not in each driver. The drivers then only have to deal with a + smaller set of types. + +* Be flexible with type conversions, but be paranoid about silent + truncation or other loss of precision. + +* Handle concurrency well. Users shouldn't need to care about the + database's per-connection thread safety issues (or lack thereof), + and shouldn't have to maintain their own free pools of connections. + The 'db' package should deal with that bookkeeping as needed. Given + an *sql.DB, it should be possible to share that instance between + multiple goroutines, without any extra synchronization. + +* Push complexity, where necessary, down into the sql+driver packages, + rather than exposing it to users. Said otherwise, the sql package + should expose an ideal database that's not finnicky about how it's + accessed, even if that's not true. + +* Provide optional interfaces in sql/driver for drivers to implement + for special cases or fastpaths. But the only party that knows about + those is the sql package. To user code, some stuff just might start + working or start working slightly faster. diff --git a/src/pkg/database/sql/driver/driver.go b/src/pkg/database/sql/driver/driver.go new file mode 100644 index 000000000..2f5280db8 --- /dev/null +++ b/src/pkg/database/sql/driver/driver.go @@ -0,0 +1,215 @@ +// 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 driver defines interfaces to be implemented by database +// drivers as used by package sql. +// +// Most code should use package sql. +package driver + +import "errors" + +// A driver Value is a value that drivers must be able to handle. +// A Value is either nil or an instance of one of these types: +// +// int64 +// float64 +// bool +// []byte +// string [*] everywhere except from Rows.Next. +// time.Time +type Value interface{} + +// Driver is the interface that must be implemented by a database +// driver. +type Driver interface { + // Open returns a new connection to the database. + // The name is a string in a driver-specific format. + // + // Open may return a cached connection (one previously + // closed), but doing so is unnecessary; the sql package + // maintains a pool of idle connections for efficient re-use. + // + // The returned connection is only used by one goroutine at a + // time. + Open(name string) (Conn, error) +} + +// ErrSkip may be returned by some optional interfaces' methods to +// indicate at runtime that the fast path is unavailable and the sql +// package should continue as if the optional interface was not +// implemented. ErrSkip is only supported where explicitly +// documented. +var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented") + +// ErrBadConn should be returned by a driver to signal to the sql +// package that a driver.Conn is in a bad state (such as the server +// having earlier closed the connection) and the sql package should +// retry on a new connection. +// +// To prevent duplicate operations, ErrBadConn should NOT be returned +// if there's a possibility that the database server might have +// performed the operation. Even if the server sends back an error, +// you shouldn't return ErrBadConn. +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 +// first prepare a query, execute the statement, and then close the +// statement. +// +// Exec may return ErrSkip. +type Execer interface { + Exec(query string, args []Value) (Result, error) +} + +// Conn is a connection to a database. It is not used concurrently +// by multiple goroutines. +// +// Conn is assumed to be stateful. +type Conn interface { + // Prepare returns a prepared statement, bound to this connection. + Prepare(query string) (Stmt, error) + + // Close invalidates and potentially stops any current + // prepared statements and transactions, marking this + // connection as no longer in use. + // + // Because the sql package maintains a free pool of + // connections and only calls Close when there's a surplus of + // idle connections, it shouldn't be necessary for drivers to + // do their own connection caching. + Close() error + + // Begin starts and returns a new transaction. + Begin() (Tx, error) +} + +// Result is the result of a query execution. +type Result interface { + // LastInsertId returns the database's auto-generated ID + // after, for example, an INSERT into a table with primary + // key. + LastInsertId() (int64, error) + + // RowsAffected returns the number of rows affected by the + // query. + RowsAffected() (int64, error) +} + +// Stmt is a prepared statement. It is bound to a Conn and not +// used by multiple goroutines concurrently. +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. + Close() error + + // NumInput returns the number of placeholder parameters. + // + // If NumInput returns >= 0, the sql package will sanity check + // argument counts from callers and return errors to the caller + // before the statement's Exec or Query methods are called. + // + // NumInput may also return -1, if the driver doesn't know + // its number of placeholders. In that case, the sql package + // will not sanity check Exec or Query argument counts. + NumInput() int + + // Exec executes a query that doesn't return rows, such + // as an INSERT or UPDATE. + Exec(args []Value) (Result, error) + + // Exec executes a query that may return rows, such as a + // SELECT. + Query(args []Value) (Rows, error) +} + +// ColumnConverter may be optionally implemented by Stmt if the +// the statement is aware of its own columns' types and can +// convert from any type to a driver Value. +type ColumnConverter interface { + // ColumnConverter returns a ValueConverter for the provided + // column index. If the type of a specific column isn't known + // or shouldn't be handled specially, DefaultValueConverter + // can be returned. + ColumnConverter(idx int) ValueConverter +} + +// Rows is an iterator over an executed query's results. +type Rows interface { + // Columns returns the names of the columns. The number of + // columns of the result is inferred from the length of the + // slice. If a particular column name isn't known, an empty + // string should be returned for that entry. + Columns() []string + + // Close closes the rows iterator. + Close() error + + // Next is called to populate the next row of data into + // the provided slice. The provided slice will be the same + // size as the Columns() are wide. + // + // The dest slice may be populated only with + // a driver Value type, but excluding string. + // All string values must be converted to []byte. + // + // Next should return io.EOF when there are no more rows. + Next(dest []Value) error +} + +// Tx is a transaction. +type Tx interface { + Commit() error + Rollback() error +} + +// RowsAffected implements Result for an INSERT or UPDATE operation +// which mutates a number of rows. +type RowsAffected int64 + +var _ Result = RowsAffected(0) + +func (RowsAffected) LastInsertId() (int64, error) { + return 0, errors.New("no LastInsertId available") +} + +func (v RowsAffected) RowsAffected() (int64, error) { + return int64(v), nil +} + +// ResultNoRows is a pre-defined Result for drivers to return when a DDL +// command (such as a CREATE TABLE) succeeds. It returns an error for both +// LastInsertId and RowsAffected. +var ResultNoRows noRows + +type noRows struct{} + +var _ Result = noRows{} + +func (noRows) LastInsertId() (int64, error) { + return 0, errors.New("no LastInsertId available after DDL statement") +} + +func (noRows) RowsAffected() (int64, error) { + return 0, errors.New("no RowsAffected available after DDL statement") +} diff --git a/src/pkg/database/sql/driver/types.go b/src/pkg/database/sql/driver/types.go new file mode 100644 index 000000000..3305354df --- /dev/null +++ b/src/pkg/database/sql/driver/types.go @@ -0,0 +1,252 @@ +// 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 driver + +import ( + "fmt" + "reflect" + "strconv" + "time" +) + +// ValueConverter is the interface providing the ConvertValue method. +// +// Various implementations of ValueConverter are provided by the +// driver package to provide consistent implementations of conversions +// between drivers. The ValueConverters have several uses: +// +// * converting from the Value types as provided by the sql package +// into a database table's specific column type and making sure it +// fits, such as making sure a particular int64 fits in a +// table's uint16 column. +// +// * converting a value as given from the database into one of the +// driver Value types. +// +// * by the sql package, for converting from a driver's Value type +// to a user's type in a scan. +type ValueConverter interface { + // ConvertValue converts a value to a driver Value. + ConvertValue(v interface{}) (Value, error) +} + +// Valuer is the interface providing the Value method. +// +// Types implementing Valuer interface are able to convert +// themselves to a driver Value. +type Valuer interface { + // Value returns a driver Value. + Value() (Value, error) +} + +// Bool is a ValueConverter that converts input values to bools. +// +// The conversion rules are: +// - booleans are returned unchanged +// - for integer types, +// 1 is true +// 0 is false, +// other integers are an error +// - for strings and []byte, same rules as strconv.ParseBool +// - all other types are an error +var Bool boolType + +type boolType struct{} + +var _ ValueConverter = boolType{} + +func (boolType) String() string { return "Bool" } + +func (boolType) ConvertValue(src interface{}) (Value, error) { + switch s := src.(type) { + case bool: + return s, nil + case string: + b, err := strconv.ParseBool(s) + if err != nil { + return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s) + } + return b, nil + case []byte: + b, err := strconv.ParseBool(string(s)) + if err != nil { + return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s) + } + return b, nil + } + + sv := reflect.ValueOf(src) + switch sv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + iv := sv.Int() + if iv == 1 || iv == 0 { + return iv == 1, nil + } + return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", iv) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + uv := sv.Uint() + if uv == 1 || uv == 0 { + return uv == 1, nil + } + return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", uv) + } + + return nil, fmt.Errorf("sql/driver: couldn't convert %v (%T) into type bool", src, src) +} + +// Int32 is a ValueConverter that converts input values to int64, +// respecting the limits of an int32 value. +var Int32 int32Type + +type int32Type struct{} + +var _ ValueConverter = int32Type{} + +func (int32Type) ConvertValue(v interface{}) (Value, error) { + rv := reflect.ValueOf(v) + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + i64 := rv.Int() + if i64 > (1<<31)-1 || i64 < -(1<<31) { + return nil, fmt.Errorf("sql/driver: value %d overflows int32", v) + } + return i64, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + u64 := rv.Uint() + if u64 > (1<<31)-1 { + return nil, fmt.Errorf("sql/driver: value %d overflows int32", v) + } + return int64(u64), nil + case reflect.String: + i, err := strconv.Atoi(rv.String()) + if err != nil { + return nil, fmt.Errorf("sql/driver: value %q can't be converted to int32", v) + } + return int64(i), nil + } + return nil, fmt.Errorf("sql/driver: unsupported value %v (type %T) converting to int32", v, v) +} + +// String is a ValueConverter that converts its input to a string. +// If the value is already a string or []byte, it's unchanged. +// If the value is of another type, conversion to string is done +// with fmt.Sprintf("%v", v). +var String stringType + +type stringType struct{} + +func (stringType) ConvertValue(v interface{}) (Value, error) { + switch v.(type) { + case string, []byte: + return v, nil + } + return fmt.Sprintf("%v", v), nil +} + +// Null is a type that implements ValueConverter by allowing nil +// values but otherwise delegating to another ValueConverter. +type Null struct { + Converter ValueConverter +} + +func (n Null) ConvertValue(v interface{}) (Value, error) { + if v == nil { + return nil, nil + } + return n.Converter.ConvertValue(v) +} + +// NotNull is a type that implements ValueConverter by disallowing nil +// values but otherwise delegating to another ValueConverter. +type NotNull struct { + Converter ValueConverter +} + +func (n NotNull) ConvertValue(v interface{}) (Value, error) { + if v == nil { + return nil, fmt.Errorf("nil value not allowed") + } + return n.Converter.ConvertValue(v) +} + +// IsValue reports whether v is a valid Value parameter type. +// Unlike IsScanValue, IsValue permits the string type. +func IsValue(v interface{}) bool { + if IsScanValue(v) { + return true + } + if _, ok := v.(string); ok { + return true + } + return false +} + +// IsScanValue reports whether v is a valid Value scan type. +// Unlike IsValue, IsScanValue does not permit the string type. +func IsScanValue(v interface{}) bool { + if v == nil { + return true + } + switch v.(type) { + case int64, float64, []byte, bool, time.Time: + return true + } + return false +} + +// DefaultParameterConverter is the default implementation of +// ValueConverter that's used when a Stmt doesn't implement +// ColumnConverter. +// +// DefaultParameterConverter returns the given value directly if +// IsValue(value). Otherwise integer type are converted to +// int64, floats to float64, and strings to []byte. Other types are +// an error. +var DefaultParameterConverter defaultConverter + +type defaultConverter struct{} + +var _ ValueConverter = defaultConverter{} + +func (defaultConverter) ConvertValue(v interface{}) (Value, error) { + if IsValue(v) { + return v, nil + } + + if svi, ok := v.(Valuer); ok { + sv, err := svi.Value() + if err != nil { + return nil, err + } + if !IsValue(sv) { + return nil, fmt.Errorf("non-Value type %T returned from Value", sv) + } + return sv, nil + } + + rv := reflect.ValueOf(v) + switch rv.Kind() { + case reflect.Ptr: + // indirect pointers + if rv.IsNil() { + return nil, nil + } else { + return defaultConverter{}.ConvertValue(rv.Elem().Interface()) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int(), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: + return int64(rv.Uint()), nil + case reflect.Uint64: + u64 := rv.Uint() + if u64 >= 1<<63 { + return nil, fmt.Errorf("uint64 values with high bit set are not supported") + } + return int64(u64), nil + case reflect.Float32, reflect.Float64: + return rv.Float(), nil + } + return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind()) +} diff --git a/src/pkg/database/sql/driver/types_test.go b/src/pkg/database/sql/driver/types_test.go new file mode 100644 index 000000000..ab82bca71 --- /dev/null +++ b/src/pkg/database/sql/driver/types_test.go @@ -0,0 +1,65 @@ +// 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 driver + +import ( + "reflect" + "testing" + "time" +) + +type valueConverterTest struct { + c ValueConverter + in interface{} + out interface{} + err string +} + +var now = time.Now() +var answer int64 = 42 + +var valueConverterTests = []valueConverterTest{ + {Bool, "true", true, ""}, + {Bool, "True", true, ""}, + {Bool, []byte("t"), true, ""}, + {Bool, true, true, ""}, + {Bool, "1", true, ""}, + {Bool, 1, true, ""}, + {Bool, int64(1), true, ""}, + {Bool, uint16(1), true, ""}, + {Bool, "false", false, ""}, + {Bool, false, false, ""}, + {Bool, "0", false, ""}, + {Bool, 0, false, ""}, + {Bool, int64(0), false, ""}, + {Bool, uint16(0), false, ""}, + {c: Bool, in: "foo", err: "sql/driver: couldn't convert \"foo\" into type bool"}, + {c: Bool, in: 2, err: "sql/driver: couldn't convert 2 into type bool"}, + {DefaultParameterConverter, now, now, ""}, + {DefaultParameterConverter, (*int64)(nil), nil, ""}, + {DefaultParameterConverter, &answer, answer, ""}, + {DefaultParameterConverter, &now, now, ""}, +} + +func TestValueConverters(t *testing.T) { + for i, tt := range valueConverterTests { + out, err := tt.c.ConvertValue(tt.in) + goterr := "" + if err != nil { + goterr = err.Error() + } + if goterr != tt.err { + t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q", + i, tt.c, tt.in, tt.in, goterr, tt.err) + } + if tt.err != "" { + continue + } + if !reflect.DeepEqual(out, tt.out) { + t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)", + i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out) + } + } +} diff --git a/src/pkg/database/sql/fakedb_test.go b/src/pkg/database/sql/fakedb_test.go new file mode 100644 index 000000000..184e7756c --- /dev/null +++ b/src/pkg/database/sql/fakedb_test.go @@ -0,0 +1,629 @@ +// 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 sql + +import ( + "database/sql/driver" + "errors" + "fmt" + "io" + "log" + "strconv" + "strings" + "sync" + "time" +) + +var _ = log.Printf + +// fakeDriver is a fake database that implements Go's driver.Driver +// interface, just for testing. +// +// It speaks a query language that's semantically similar to but +// syntantically different and simpler than SQL. The syntax is as +// follows: +// +// WIPE +// CREATE||=,=,... +// where types are: "string", [u]int{8,16,32,64}, "bool" +// INSERT||col=val,col2=val2,col3=? +// SELECT||projectcol1,projectcol2|filtercol=?,filtercol2=? +// +// When opening a 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 + openCount int + dbs map[string]*fakeDB +} + +type fakeDB struct { + name string + + mu sync.Mutex + free []*fakeConn + tables map[string]*table +} + +type table struct { + mu sync.Mutex + colname []string + coltype []string + rows []*row +} + +func (t *table) columnIndex(name string) int { + for n, nname := range t.colname { + if name == nname { + return n + } + } + return -1 +} + +type row struct { + cols []interface{} // must be same size as its table colname + coltype +} + +func (r *row) clone() *row { + nrow := &row{cols: make([]interface{}, len(r.cols))} + copy(nrow.cols, r.cols) + return nrow +} + +type fakeConn struct { + db *fakeDB // where to return ourselves to + + currTx *fakeTx + + // Stats for tests: + mu sync.Mutex + stmtsMade int + stmtsClosed int + numPrepare int +} + +func (c *fakeConn) incrStat(v *int) { + c.mu.Lock() + *v++ + c.mu.Unlock() +} + +type fakeTx struct { + c *fakeConn +} + +type fakeStmt struct { + c *fakeConn + q string // just for debugging + + cmd string + table string + + closed bool + + colName []string // used by CREATE, INSERT, SELECT (selected columns) + colType []string // used by CREATE + colValue []interface{} // used by INSERT (mix of strings and "?" for bound params) + placeholders int // used by INSERT/SELECT: number of ? params + + whereCol []string // used by SELECT (all placeholders) + + placeholderConverter []driver.ValueConverter // used by INSERT +} + +var fdriver driver.Driver = &fakeDriver{} + +func init() { + Register("test", fdriver) +} + +// Supports dsn forms: +// +// ; (no currently supported options) +func (d *fakeDriver) Open(dsn string) (driver.Conn, error) { + parts := strings.Split(dsn, ";") + if len(parts) < 1 { + return nil, errors.New("fakedb: no database name") + } + name := parts[0] + + db := d.getDB(name) + + d.mu.Lock() + d.openCount++ + d.mu.Unlock() + return &fakeConn{db: db}, nil +} + +func (d *fakeDriver) getDB(name string) *fakeDB { + d.mu.Lock() + defer d.mu.Unlock() + if d.dbs == nil { + d.dbs = make(map[string]*fakeDB) + } + db, ok := d.dbs[name] + if !ok { + db = &fakeDB{name: name} + d.dbs[name] = db + } + return db +} + +func (db *fakeDB) wipe() { + db.mu.Lock() + defer db.mu.Unlock() + db.tables = nil +} + +func (db *fakeDB) createTable(name string, columnNames, columnTypes []string) error { + db.mu.Lock() + defer db.mu.Unlock() + if db.tables == nil { + db.tables = make(map[string]*table) + } + if _, exist := db.tables[name]; exist { + return fmt.Errorf("table %q already exists", name) + } + if len(columnNames) != len(columnTypes) { + return fmt.Errorf("create table of %q len(names) != len(types): %d vs %d", + name, len(columnNames), len(columnTypes)) + } + db.tables[name] = &table{colname: columnNames, coltype: columnTypes} + return nil +} + +// must be called with db.mu lock held +func (db *fakeDB) table(table string) (*table, bool) { + if db.tables == nil { + return nil, false + } + t, ok := db.tables[table] + return t, ok +} + +func (db *fakeDB) columnType(table, column string) (typ string, ok bool) { + db.mu.Lock() + defer db.mu.Unlock() + t, ok := db.table(table) + if !ok { + return + } + for n, cname := range t.colname { + if cname == column { + return t.coltype[n], true + } + } + return "", false +} + +func (c *fakeConn) Begin() (driver.Tx, error) { + if c.currTx != nil { + return nil, errors.New("already in a transaction") + } + c.currTx = &fakeTx{c: c} + return c.currTx, nil +} + +func (c *fakeConn) Close() error { + if c.currTx != nil { + return errors.New("can't close fakeConn; in a Transaction") + } + if c.db == nil { + return errors.New("can't close fakeConn; already closed") + } + if c.stmtsMade > c.stmtsClosed { + return errors.New("can't close; dangling statement(s)") + } + c.db = nil + return nil +} + +func checkSubsetTypes(args []driver.Value) error { + for n, arg := range args { + switch arg.(type) { + case int64, float64, bool, nil, []byte, string, time.Time: + default: + return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg) + } + } + return nil +} + +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. + // 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 errf(msg string, args ...interface{}) error { + return errors.New("fakedb: " + fmt.Sprintf(msg, args...)) +} + +// parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=? +// (note that where 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 { + stmt.Close() + return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts)) + } + stmt.table = parts[0] + stmt.colName = strings.Split(parts[1], ",") + for n, colspec := range strings.Split(parts[2], ",") { + if colspec == "" { + continue + } + nameVal := strings.Split(colspec, "=") + if len(nameVal) != 2 { + stmt.Close() + return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) + } + column, value := nameVal[0], nameVal[1] + _, ok := c.db.columnType(stmt.table, column) + if !ok { + stmt.Close() + return nil, errf("SELECT on table %q references non-existent column %q", stmt.table, column) + } + if value != "?" { + stmt.Close() + return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark", + stmt.table, column) + } + stmt.whereCol = append(stmt.whereCol, column) + stmt.placeholders++ + } + return stmt, nil +} + +// parts are table|col=type,col2=type2 +func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, error) { + if len(parts) != 2 { + stmt.Close() + return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts)) + } + stmt.table = parts[0] + for n, colspec := range strings.Split(parts[1], ",") { + nameType := strings.Split(colspec, "=") + if len(nameType) != 2 { + stmt.Close() + return nil, errf("CREATE table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) + } + stmt.colName = append(stmt.colName, nameType[0]) + stmt.colType = append(stmt.colType, nameType[1]) + } + return stmt, nil +} + +// parts are table|col=?,col2=val +func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, error) { + if len(parts) != 2 { + stmt.Close() + return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts)) + } + stmt.table = parts[0] + for n, colspec := range strings.Split(parts[1], ",") { + nameVal := strings.Split(colspec, "=") + if len(nameVal) != 2 { + stmt.Close() + return nil, errf("INSERT table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) + } + column, value := nameVal[0], nameVal[1] + ctype, ok := c.db.columnType(stmt.table, column) + if !ok { + stmt.Close() + return nil, errf("INSERT table %q references non-existent column %q", stmt.table, column) + } + stmt.colName = append(stmt.colName, column) + + if value != "?" { + var subsetVal interface{} + // Convert to driver subset type + switch ctype { + case "string": + subsetVal = []byte(value) + case "blob": + subsetVal = []byte(value) + case "int32": + i, err := strconv.Atoi(value) + if err != nil { + stmt.Close() + return nil, errf("invalid conversion to int32 from %q", value) + } + subsetVal = int64(i) // int64 is a subset type, but not int32 + default: + stmt.Close() + return nil, errf("unsupported conversion for pre-bound parameter %q to type %q", value, ctype) + } + stmt.colValue = append(stmt.colValue, subsetVal) + } else { + stmt.placeholders++ + stmt.placeholderConverter = append(stmt.placeholderConverter, converterForType(ctype)) + stmt.colValue = append(stmt.colValue, "?") + } + } + return stmt, nil +} + +func (c *fakeConn) Prepare(query string) (driver.Stmt, error) { + c.numPrepare++ + if c.db == nil { + panic("nil c.db; conn = " + fmt.Sprintf("%#v", c)) + } + parts := strings.Split(query, "|") + if len(parts) < 1 { + return nil, errf("empty query") + } + cmd := parts[0] + parts = parts[1:] + stmt := &fakeStmt{q: query, c: c, cmd: cmd} + c.incrStat(&c.stmtsMade) + switch cmd { + case "WIPE": + // Nothing + case "SELECT": + return c.prepareSelect(stmt, parts) + case "CREATE": + return c.prepareCreate(stmt, parts) + case "INSERT": + return c.prepareInsert(stmt, parts) + default: + stmt.Close() + return nil, errf("unsupported command type %q", cmd) + } + return stmt, nil +} + +func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter { + return s.placeholderConverter[idx] +} + +func (s *fakeStmt) Close() error { + if !s.closed { + s.c.incrStat(&s.c.stmtsClosed) + s.closed = true + } + return nil +} + +var errClosed = errors.New("fakedb: statement has been closed") + +func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) { + if s.closed { + return nil, errClosed + } + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + + db := s.c.db + switch s.cmd { + case "WIPE": + db.wipe() + return driver.ResultNoRows, nil + case "CREATE": + if err := db.createTable(s.table, s.colName, s.colType); err != nil { + return nil, err + } + return driver.ResultNoRows, nil + case "INSERT": + return s.execInsert(args) + } + fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s) + return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd) +} + +func (s *fakeStmt) execInsert(args []driver.Value) (driver.Result, error) { + db := s.c.db + if len(args) != s.placeholders { + panic("error in pkg db; should only get here if size is correct") + } + db.mu.Lock() + t, ok := db.table(s.table) + db.mu.Unlock() + if !ok { + return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table) + } + + t.mu.Lock() + defer t.mu.Unlock() + + cols := make([]interface{}, len(t.colname)) + argPos := 0 + for n, colname := range s.colName { + colidx := t.columnIndex(colname) + if colidx == -1 { + return nil, fmt.Errorf("fakedb: column %q doesn't exist or dropped since prepared statement was created", colname) + } + var val interface{} + if strvalue, ok := s.colValue[n].(string); ok && strvalue == "?" { + val = args[argPos] + argPos++ + } else { + val = s.colValue[n] + } + cols[colidx] = val + } + + t.rows = append(t.rows, &row{cols: cols}) + return driver.RowsAffected(1), nil +} + +func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) { + if s.closed { + return nil, errClosed + } + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + + db := s.c.db + if len(args) != s.placeholders { + panic("error in pkg db; should only get here if size is correct") + } + + db.mu.Lock() + t, ok := db.table(s.table) + db.mu.Unlock() + if !ok { + return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table) + } + t.mu.Lock() + defer t.mu.Unlock() + + colIdx := make(map[string]int) // select column name -> column index in table + for _, name := range s.colName { + idx := t.columnIndex(name) + if idx == -1 { + return nil, fmt.Errorf("fakedb: unknown column name %q", name) + } + colIdx[name] = idx + } + + mrows := []*row{} +rows: + for _, trow := range t.rows { + // Process the where clause, skipping non-match rows. This is lazy + // and just uses fmt.Sprintf("%v") to test equality. Good enough + // for test code. + for widx, wcol := range s.whereCol { + idx := t.columnIndex(wcol) + if idx == -1 { + return nil, fmt.Errorf("db: invalid where clause column %q", wcol) + } + tcol := trow.cols[idx] + if bs, ok := tcol.([]byte); ok { + // lazy hack to avoid sprintf %v on a []byte + tcol = string(bs) + } + if fmt.Sprintf("%v", tcol) != fmt.Sprintf("%v", args[widx]) { + continue rows + } + } + mrow := &row{cols: make([]interface{}, len(s.colName))} + for seli, name := range s.colName { + mrow.cols[seli] = trow.cols[colIdx[name]] + } + mrows = append(mrows, mrow) + } + + cursor := &rowsCursor{ + pos: -1, + rows: mrows, + cols: s.colName, + } + return cursor, nil +} + +func (s *fakeStmt) NumInput() int { + return s.placeholders +} + +func (tx *fakeTx) Commit() error { + tx.c.currTx = nil + return nil +} + +func (tx *fakeTx) Rollback() error { + tx.c.currTx = nil + return nil +} + +type rowsCursor struct { + cols []string + pos int + rows []*row + closed bool + + // a clone of slices to give out to clients, indexed by the + // the original slice's first byte address. we clone them + // just so we're able to corrupt them on close. + bytesClone map[*byte][]byte +} + +func (rc *rowsCursor) Close() error { + if !rc.closed { + for _, bs := range rc.bytesClone { + bs[0] = 255 // first byte corrupted + } + } + rc.closed = true + return nil +} + +func (rc *rowsCursor) Columns() []string { + return rc.cols +} + +func (rc *rowsCursor) Next(dest []driver.Value) error { + if rc.closed { + return errors.New("fakedb: cursor is closed") + } + rc.pos++ + if rc.pos >= len(rc.rows) { + return io.EOF // per interface spec + } + for i, v := range rc.rows[rc.pos].cols { + // TODO(bradfitz): convert to subset types? naah, I + // think the subset types should only be input to + // driver, but the sql package should be able to handle + // a wider range of types coming out of drivers. all + // for ease of drivers, and to prevent drivers from + // messing up conversions or doing them differently. + dest[i] = v + + if bs, ok := v.([]byte); ok { + if rc.bytesClone == nil { + rc.bytesClone = make(map[*byte][]byte) + } + clone, ok := rc.bytesClone[&bs[0]] + if !ok { + clone = make([]byte, len(bs)) + copy(clone, bs) + rc.bytesClone[&bs[0]] = clone + } + dest[i] = clone + } + } + return nil +} + +func converterForType(typ string) driver.ValueConverter { + switch typ { + case "bool": + return driver.Bool + case "nullbool": + return driver.Null{Converter: driver.Bool} + case "int32": + return driver.Int32 + case "string": + return driver.NotNull{Converter: driver.String} + case "nullstring": + return driver.Null{Converter: driver.String} + case "int64": + // TODO(coopernurse): add type-specific converter + return driver.NotNull{Converter: driver.DefaultParameterConverter} + case "nullint64": + // TODO(coopernurse): add type-specific converter + return driver.Null{Converter: driver.DefaultParameterConverter} + case "float64": + // TODO(coopernurse): add type-specific converter + return driver.NotNull{Converter: driver.DefaultParameterConverter} + case "nullfloat64": + // TODO(coopernurse): add type-specific converter + return driver.Null{Converter: driver.DefaultParameterConverter} + case "datetime": + return driver.DefaultParameterConverter + } + panic("invalid fakedb column type of " + typ) +} diff --git a/src/pkg/database/sql/sql.go b/src/pkg/database/sql/sql.go new file mode 100644 index 000000000..51a357b37 --- /dev/null +++ b/src/pkg/database/sql/sql.go @@ -0,0 +1,1063 @@ +// 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 sql provides a generic interface around SQL (or SQL-like) +// databases. +package sql + +import ( + "database/sql/driver" + "errors" + "fmt" + "io" + "sync" +) + +var drivers = make(map[string]driver.Driver) + +// Register makes a database driver available by the provided name. +// If Register is called twice with the same name or if driver is nil, +// it panics. +func Register(name string, driver driver.Driver) { + if driver == nil { + panic("sql: Register driver is nil") + } + if _, dup := drivers[name]; dup { + panic("sql: Register called twice for driver " + name) + } + drivers[name] = driver +} + +// RawBytes is a byte slice that holds a reference to memory owned by +// the database itself. After a Scan into a RawBytes, the slice is only +// valid until the next call to Next, Scan, or Close. +type RawBytes []byte + +// NullString represents a string that may be null. +// NullString implements the Scanner interface so +// it can be used as a scan destination: +// +// var s NullString +// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s) +// ... +// if s.Valid { +// // use s.String +// } else { +// // NULL value +// } +// +type NullString struct { + String string + Valid bool // Valid is true if String is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullString) Scan(value interface{}) error { + if value == nil { + ns.String, ns.Valid = "", false + return nil + } + ns.Valid = true + return convertAssign(&ns.String, value) +} + +// Value implements the driver Valuer interface. +func (ns NullString) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return ns.String, nil +} + +// NullInt64 represents an int64 that may be null. +// NullInt64 implements the Scanner interface so +// it can be used as a scan destination, similar to NullString. +type NullInt64 struct { + Int64 int64 + Valid bool // Valid is true if Int64 is not NULL +} + +// Scan implements the Scanner interface. +func (n *NullInt64) Scan(value interface{}) error { + if value == nil { + n.Int64, n.Valid = 0, false + return nil + } + n.Valid = true + return convertAssign(&n.Int64, value) +} + +// Value implements the driver Valuer interface. +func (n NullInt64) Value() (driver.Value, error) { + if !n.Valid { + return nil, nil + } + return n.Int64, nil +} + +// NullFloat64 represents a float64 that may be null. +// NullFloat64 implements the Scanner interface so +// it can be used as a scan destination, similar to NullString. +type NullFloat64 struct { + Float64 float64 + Valid bool // Valid is true if Float64 is not NULL +} + +// Scan implements the Scanner interface. +func (n *NullFloat64) Scan(value interface{}) error { + if value == nil { + n.Float64, n.Valid = 0, false + return nil + } + n.Valid = true + return convertAssign(&n.Float64, value) +} + +// Value implements the driver Valuer interface. +func (n NullFloat64) Value() (driver.Value, error) { + if !n.Valid { + return nil, nil + } + return n.Float64, nil +} + +// NullBool represents a bool that may be null. +// NullBool implements the Scanner interface so +// it can be used as a scan destination, similar to NullString. +type NullBool struct { + Bool bool + Valid bool // Valid is true if Bool is not NULL +} + +// Scan implements the Scanner interface. +func (n *NullBool) Scan(value interface{}) error { + if value == nil { + n.Bool, n.Valid = false, false + return nil + } + n.Valid = true + return convertAssign(&n.Bool, value) +} + +// Value implements the driver Valuer interface. +func (n NullBool) Value() (driver.Value, error) { + if !n.Valid { + return nil, nil + } + return n.Bool, nil +} + +// Scanner is an interface used by Scan. +type Scanner interface { + // Scan assigns a value from a database driver. + // + // The src value will be of one of the following restricted + // set of types: + // + // int64 + // float64 + // bool + // []byte + // string + // time.Time + // nil - for NULL values + // + // An error should be returned if the value can not be stored + // without loss of information. + Scan(src interface{}) error +} + +// ErrNoRows is returned by Scan when QueryRow doesn't return a +// row. In such a case, QueryRow returns a placeholder *Row value that +// defers this error until a Scan. +var ErrNoRows = errors.New("sql: no rows in result set") + +// DB is a database handle. It's safe for concurrent use by multiple +// goroutines. +// +// If the underlying database driver has the concept of a connection +// and per-connection session state, the sql package manages creating +// and freeing connections automatically, including maintaining a free +// pool of idle connections. If observing session state is required, +// either do not share a *DB between multiple concurrent goroutines or +// create and observe all state only within a transaction. Once +// DB.Open is called, the returned Tx is bound to a single isolated +// connection. Once Tx.Commit or Tx.Rollback is called, that +// connection is returned to DB's idle connection pool. +type DB struct { + driver driver.Driver + dsn string + + mu sync.Mutex // protects freeConn and closed + freeConn []driver.Conn + closed bool +} + +// Open opens a database specified by its database driver name and a +// driver-specific data source name, usually consisting of at least a +// database name and connection information. +// +// 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] + if !ok { + return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName) + } + return &DB{driver: driver, dsn: dataSourceName}, nil +} + +// Close closes the database, releasing any open resources. +func (db *DB) Close() error { + db.mu.Lock() + defer db.mu.Unlock() + var err error + for _, c := range db.freeConn { + err1 := c.Close() + if err1 != nil { + err = err1 + } + } + db.freeConn = nil + db.closed = true + return err +} + +func (db *DB) maxIdleConns() int { + const defaultMaxIdleConns = 2 + // TODO(bradfitz): ask driver, if supported, for its default preference + // TODO(bradfitz): let users override? + return defaultMaxIdleConns +} + +// conn returns a newly-opened or cached driver.Conn +func (db *DB) conn() (driver.Conn, error) { + db.mu.Lock() + if db.closed { + db.mu.Unlock() + return nil, errors.New("sql: database is closed") + } + if n := len(db.freeConn); n > 0 { + conn := db.freeConn[n-1] + db.freeConn = db.freeConn[:n-1] + db.mu.Unlock() + return conn, nil + } + db.mu.Unlock() + return db.driver.Open(db.dsn) +} + +func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) { + db.mu.Lock() + defer db.mu.Unlock() + 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] + return wanted, true + } + return nil, false +} + +// putConnHook is a hook for testing. +var putConnHook func(*DB, driver.Conn) + +// putConn adds a connection to the db's free pool. +// err is optionally the last error that occured on this connection. +func (db *DB) putConn(c driver.Conn, err error) { + if err == driver.ErrBadConn { + // Don't reuse bad connections. + return + } + db.mu.Lock() + if putConnHook != nil { + putConnHook(db, c) + } + if n := len(db.freeConn); !db.closed && n < db.maxIdleConns() { + db.freeConn = append(db.freeConn, c) + db.mu.Unlock() + return + } + // TODO: check to see if we need this Conn for any prepared + // statements which are still active? + db.mu.Unlock() + c.Close() +} + +// Prepare creates a prepared statement for later execution. +func (db *DB) Prepare(query string) (*Stmt, error) { + var stmt *Stmt + var err error + for i := 0; i < 10; i++ { + stmt, err = db.prepare(query) + if err != driver.ErrBadConn { + break + } + } + return stmt, err +} + +func (db *DB) prepare(query string) (stmt *Stmt, err 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 + // to a connection, and to execute this prepared statement + // we either need to use this connection (if it's free), else + // get a new connection + re-prepare + execute on that one. + ci, err := db.conn() + if err != nil { + return nil, err + } + defer db.putConn(ci, err) + si, err := ci.Prepare(query) + if err != nil { + return nil, err + } + stmt = &Stmt{ + db: db, + query: query, + css: []connStmt{{ci, si}}, + } + return stmt, nil +} + +// Exec executes a query without returning any rows. +func (db *DB) Exec(query string, args ...interface{}) (Result, error) { + sargs, err := subsetTypeArgs(args) + var res Result + for i := 0; i < 10; i++ { + res, err = db.exec(query, sargs) + if err != driver.ErrBadConn { + break + } + } + return res, err +} + +func (db *DB) exec(query string, sargs []driver.Value) (res Result, err error) { + ci, err := db.conn() + if err != nil { + return nil, err + } + defer db.putConn(ci, err) + + if execer, ok := ci.(driver.Execer); ok { + resi, err := execer.Exec(query, sargs) + if err != driver.ErrSkip { + if err != nil { + return nil, err + } + return result{resi}, nil + } + } + + sti, err := ci.Prepare(query) + if err != nil { + return nil, err + } + defer sti.Close() + + resi, err := sti.Exec(sargs) + if err != nil { + return nil, err + } + return result{resi}, nil +} + +// 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) + if err != nil { + return nil, err + } + rows, err := stmt.Query(args...) + if err != nil { + stmt.Close() + return nil, err + } + rows.closeStmt = stmt + return rows, nil +} + +// QueryRow executes a query that is expected to return at most one row. +// QueryRow always return a non-nil value. Errors are deferred until +// Row's Scan method is called. +func (db *DB) QueryRow(query string, args ...interface{}) *Row { + rows, err := db.Query(query, args...) + return &Row{rows: rows, err: err} +} + +// Begin starts a transaction. The isolation level is dependent on +// the driver. +func (db *DB) Begin() (*Tx, error) { + var tx *Tx + var err error + for i := 0; i < 10; i++ { + tx, err = db.begin() + if err != driver.ErrBadConn { + break + } + } + return tx, err +} + +func (db *DB) begin() (tx *Tx, err error) { + ci, err := db.conn() + if err != nil { + return nil, err + } + txi, err := ci.Begin() + if err != nil { + db.putConn(ci, err) + return nil, fmt.Errorf("sql: failed to Begin transaction: %v", err) + } + return &Tx{ + db: db, + ci: ci, + txi: txi, + }, nil +} + +// Driver returns the database's underlying driver. +func (db *DB) Driver() driver.Driver { + return db.driver +} + +// Tx is an in-progress database transaction. +// +// A transaction must end with a call to Commit or Rollback. +// +// After a call to Commit or Rollback, all operations on the +// transaction fail with ErrTxDone. +type Tx struct { + db *DB + + // ci is owned exclusively until Commit or Rollback, at which point + // it's returned with putConn. + ci driver.Conn + txi driver.Tx + + // cimu is held while somebody is using ci (between grabConn + // and releaseConn) + cimu sync.Mutex + + // done transitions from false to true exactly once, on Commit + // or Rollback. once done, all operations fail with + // ErrTxDone. + done bool +} + +var ErrTxDone = errors.New("sql: Transaction has already been committed or rolled back") + +func (tx *Tx) close() { + if tx.done { + panic("double close") // internal error + } + tx.done = true + tx.db.putConn(tx.ci, nil) + tx.ci = nil + tx.txi = nil +} + +func (tx *Tx) grabConn() (driver.Conn, error) { + if tx.done { + return nil, ErrTxDone + } + tx.cimu.Lock() + return tx.ci, nil +} + +func (tx *Tx) releaseConn() { + tx.cimu.Unlock() +} + +// Commit commits the transaction. +func (tx *Tx) Commit() error { + if tx.done { + return ErrTxDone + } + defer tx.close() + return tx.txi.Commit() +} + +// Rollback aborts the transaction. +func (tx *Tx) Rollback() error { + if tx.done { + return ErrTxDone + } + defer tx.close() + return tx.txi.Rollback() +} + +// Prepare creates a prepared statement for use within a transaction. +// +// The returned statement operates within the transaction and can no longer +// be used once the transaction has been committed or rolled back. +// +// To use an existing prepared statement on this transaction, see Tx.Stmt. +func (tx *Tx) Prepare(query string) (*Stmt, error) { + // TODO(bradfitz): We could be more efficient here and either + // provide a method to take an existing Stmt (created on + // perhaps a different Conn), and re-create it on this Conn if + // necessary. Or, better: keep a map in DB of query string to + // Stmts, and have Stmt.Execute do the right thing and + // re-prepare if the Conn in use doesn't have that prepared + // statement. But we'll want to avoid caching the statement + // in the case where we only call conn.Prepare implicitly + // (such as in db.Exec or tx.Exec), but the caller package + // can't be holding a reference to the returned statement. + // Perhaps just looking at the reference count (by noting + // Stmt.Close) would be enough. We might also want a finalizer + // on Stmt to drop the reference count. + ci, err := tx.grabConn() + if err != nil { + return nil, err + } + defer tx.releaseConn() + + si, err := ci.Prepare(query) + if err != nil { + return nil, err + } + + stmt := &Stmt{ + db: tx.db, + tx: tx, + txsi: si, + query: query, + } + return stmt, nil +} + +// Stmt returns a transaction-specific prepared statement from +// an existing statement. +// +// Example: +// updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?") +// ... +// tx, err := db.Begin() +// ... +// res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203) +func (tx *Tx) Stmt(stmt *Stmt) *Stmt { + // TODO(bradfitz): optimize this. Currently this re-prepares + // each time. This is fine for now to illustrate the API but + // we should really cache already-prepared statements + // per-Conn. See also the big comment in Tx.Prepare. + + if tx.db != stmt.db { + return &Stmt{stickyErr: errors.New("sql: Tx.Stmt: statement from different database used")} + } + ci, err := tx.grabConn() + if err != nil { + return &Stmt{stickyErr: err} + } + defer tx.releaseConn() + si, err := ci.Prepare(stmt.query) + return &Stmt{ + db: tx.db, + tx: tx, + txsi: si, + query: stmt.query, + stickyErr: err, + } +} + +// Exec executes a query that doesn't return rows. +// For example: an INSERT and UPDATE. +func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { + ci, err := tx.grabConn() + if err != nil { + return nil, err + } + 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) + if err == nil { + return result{resi}, nil + } + if err != driver.ErrSkip { + return nil, err + } + } + + sti, err := ci.Prepare(query) + if err != nil { + return nil, err + } + defer sti.Close() + + resi, err := sti.Exec(sargs) + if err != nil { + return nil, err + } + return result{resi}, nil +} + +// 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...) + if err != nil { + stmt.Close() + return nil, err + } + rows.closeStmt = stmt + return rows, err +} + +// QueryRow executes a query that is expected to return at most one row. +// QueryRow always return a non-nil value. Errors are deferred until +// Row's Scan method is called. +func (tx *Tx) QueryRow(query string, args ...interface{}) *Row { + rows, err := tx.Query(query, args...) + return &Row{rows: rows, err: err} +} + +// connStmt is a prepared statement on a particular connection. +type connStmt struct { + ci driver.Conn + si driver.Stmt +} + +// Stmt is a prepared statement. Stmt is safe for concurrent use by multiple goroutines. +type Stmt struct { + // Immutable: + db *DB // where we came from + query string // that created the Stmt + stickyErr error // if non-nil, this error is returned for all operations + + // If in a transaction, else both nil: + tx *Tx + txsi driver.Stmt + + mu sync.Mutex // protects the rest of the fields + closed bool + + // css is a list of underlying driver statement interfaces + // that are valid on particular connections. This is only + // used if tx == nil and one is found that has idle + // connections. If tx != nil, txsi is always used. + css []connStmt +} + +// 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) { + _, releaseConn, si, err := s.connStmt() + if err != nil { + return nil, err + } + defer releaseConn(nil) + + // -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: 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) + } + } + } + + resi, err := si.Exec(sargs) + if err != nil { + return nil, err + } + return result{resi}, nil +} + +// connStmt returns a free driver connection on which to execute the +// statement, a function to call to release the connection, and a +// statement bound to that connection. +func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(error), si driver.Stmt, err error) { + if err = s.stickyErr; err != nil { + return + } + s.mu.Lock() + if s.closed { + s.mu.Unlock() + err = errors.New("sql: statement is closed") + return + } + + // In a transaction, we always use the connection that the + // transaction was created on. + if s.tx != nil { + s.mu.Unlock() + ci, err = s.tx.grabConn() // blocks, waiting for the connection. + if err != nil { + return + } + releaseConn = func(error) { s.tx.releaseConn() } + return ci, releaseConn, s.txsi, nil + } + + var cs connStmt + match := false + for _, v := range s.css { + // TODO(bradfitz): lazily clean up entries in this + // list with dead conns while enumerating + if _, match = s.db.connIfFree(v.ci); match { + cs = v + break + } + } + s.mu.Unlock() + + // Make a new conn if all are busy. + // TODO(bradfitz): or wait for one? make configurable later? + if !match { + for i := 0; ; i++ { + ci, err := s.db.conn() + if err != nil { + return nil, nil, nil, err + } + si, err := ci.Prepare(s.query) + if err == driver.ErrBadConn && i < 10 { + continue + } + if err != nil { + return nil, nil, nil, err + } + s.mu.Lock() + cs = connStmt{ci, si} + s.css = append(s.css, cs) + s.mu.Unlock() + break + } + } + + conn := cs.ci + releaseConn = func(err error) { s.db.putConn(conn, err) } + return conn, releaseConn, cs.si, nil +} + +// 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) { + ci, releaseConn, si, err := s.connStmt() + if err != nil { + return nil, err + } + + // -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) + if err != nil { + return nil, err + } + rowsi, err := si.Query(sargs) + 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 +} + +// QueryRow executes a prepared query statement with the given arguments. +// If an error occurs during the execution of the statement, that error will +// be returned by a call to Scan on the returned *Row, which is always non-nil. +// If the query selects no rows, the *Row's Scan will return ErrNoRows. +// Otherwise, the *Row's Scan scans the first selected row and discards +// the rest. +// +// Example usage: +// +// var name string +// err := nameByUseridStmt.QueryRow(id).Scan(&name) +func (s *Stmt) QueryRow(args ...interface{}) *Row { + rows, err := s.Query(args...) + if err != nil { + return &Row{err: err} + } + return &Row{rows: rows} +} + +// Close closes the statement. +func (s *Stmt) Close() error { + if s.stickyErr != nil { + return s.stickyErr + } + s.mu.Lock() + defer s.mu.Unlock() + if s.closed { + return nil + } + s.closed = true + + 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 +} + +// Rows is the result of a query. Its cursor starts before the first row +// of the result set. Use Next to advance through the rows: +// +// rows, err := db.Query("SELECT ...") +// ... +// for rows.Next() { +// var id int +// var name string +// err = rows.Scan(&id, &name) +// ... +// } +// err = rows.Err() // get any error encountered during iteration +// ... +type Rows struct { + db *DB + ci driver.Conn // owned; must call putconn 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 +} + +// Next prepares the next result row for reading with the Scan method. +// It returns true on success, false if there is no next result row. +// Every call to Scan, even the first one, must be preceded by a call +// to Next. +func (rs *Rows) Next() bool { + if rs.closed { + return false + } + if rs.lasterr != nil { + return false + } + if rs.lastcols == nil { + rs.lastcols = make([]driver.Value, len(rs.rowsi.Columns())) + } + rs.lasterr = rs.rowsi.Next(rs.lastcols) + if rs.lasterr == io.EOF { + rs.Close() + } + return rs.lasterr == nil +} + +// Err returns the error, if any, that was encountered during iteration. +func (rs *Rows) Err() error { + if rs.lasterr == io.EOF { + return nil + } + return rs.lasterr +} + +// Columns returns the column names. +// Columns returns an error if the rows are closed, or if the rows +// are from QueryRow and there was a deferred error. +func (rs *Rows) Columns() ([]string, error) { + if rs.closed { + return nil, errors.New("sql: Rows are closed") + } + if rs.rowsi == nil { + return nil, errors.New("sql: no Rows available") + } + return rs.rowsi.Columns(), nil +} + +// Scan copies the columns in the current row into the values pointed +// at by dest. +// +// If an argument has type *[]byte, Scan saves in that argument a copy +// of the corresponding data. The copy is owned by the caller and can +// be modified and held indefinitely. The copy can be avoided by using +// an argument of type *RawBytes instead; see the documentation for +// RawBytes for restrictions on its use. +// +// If an argument has type *interface{}, Scan copies the value +// provided by the underlying driver without conversion. If the value +// is of type []byte, a copy is made and the caller owns the result. +func (rs *Rows) Scan(dest ...interface{}) error { + if rs.closed { + return errors.New("sql: Rows closed") + } + if rs.lasterr != nil { + return rs.lasterr + } + if rs.lastcols == nil { + return errors.New("sql: Scan called without calling Next") + } + if len(dest) != len(rs.lastcols) { + return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest)) + } + for i, sv := range rs.lastcols { + err := convertAssign(dest[i], sv) + if err != nil { + return fmt.Errorf("sql: Scan error on column index %d: %v", i, err) + } + } + for _, dp := range dest { + b, ok := dp.(*[]byte) + if !ok { + continue + } + if *b == nil { + // If the []byte is now nil (for a NULL value), + // don't fall through to below which would + // turn it into a non-nil 0-length byte slice + continue + } + if _, ok = dp.(*RawBytes); ok { + continue + } + clone := make([]byte, len(*b)) + copy(clone, *b) + *b = clone + } + return nil +} + +// Close closes the Rows, preventing further enumeration. If the +// end is encountered, the Rows are closed automatically. Close +// is idempotent. +func (rs *Rows) Close() error { + if rs.closed { + return nil + } + rs.closed = true + err := rs.rowsi.Close() + rs.releaseConn(err) + if rs.closeStmt != nil { + rs.closeStmt.Close() + } + return err +} + +// Row is the result of calling QueryRow to select a single row. +type Row struct { + // One of these two will be non-nil: + err error // deferred error for easy chaining + rows *Rows +} + +// Scan copies the columns from the matched row into the values +// pointed at by dest. If more than one row matches the query, +// Scan uses the first row and discards the rest. If no row matches +// the query, Scan returns ErrNoRows. +func (r *Row) Scan(dest ...interface{}) error { + if r.err != nil { + return r.err + } + + // TODO(bradfitz): for now we need to defensively clone all + // []byte that the driver returned (not permitting + // *RawBytes in Rows.Scan), since we're about to close + // the Rows in our defer, when we return from this function. + // the contract with the driver.Next(...) interface is that it + // can return slices into read-only temporary memory that's + // only valid until the next Scan/Close. But the TODO is that + // for a lot of drivers, this copy will be unnecessary. We + // should provide an optional interface for drivers to + // implement to say, "don't worry, the []bytes that I return + // from Next will not be modified again." (for instance, if + // they were obtained from the network anyway) But for now we + // don't care. + for _, dp := range dest { + if _, ok := dp.(*RawBytes); ok { + return errors.New("sql: RawBytes isn't allowed on Row.Scan") + } + } + + defer r.rows.Close() + if !r.rows.Next() { + return ErrNoRows + } + err := r.rows.Scan(dest...) + if err != nil { + return err + } + + return nil +} + +// A Result summarizes an executed SQL command. +type Result interface { + LastInsertId() (int64, error) + RowsAffected() (int64, error) +} + +type result struct { + driver.Result +} diff --git a/src/pkg/database/sql/sql_test.go b/src/pkg/database/sql/sql_test.go new file mode 100644 index 000000000..b29670586 --- /dev/null +++ b/src/pkg/database/sql/sql_test.go @@ -0,0 +1,614 @@ +// 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 sql + +import ( + "database/sql/driver" + "fmt" + "reflect" + "runtime" + "strings" + "testing" + "time" +) + +func init() { + type dbConn struct { + db *DB + c driver.Conn + } + freedFrom := make(map[dbConn]string) + putConnHook = func(db *DB, c driver.Conn) { + for _, oc := range db.freeConn { + if oc == c { + // print before panic, as panic may get lost due to conflicting panic + // (all goroutines asleep) elsewhere, since we might not unlock + // the mutex in freeConn here. + println("double free of conn. conflicts are:\nA) " + freedFrom[dbConn{db, c}] + "\n\nand\nB) " + stack()) + panic("double free of conn.") + } + } + freedFrom[dbConn{db, c}] = stack() + } +} + +const fakeDBName = "foo" + +var chrisBirthday = time.Unix(123456789, 0) + +func newTestDB(t *testing.T, name string) *DB { + db, err := Open("test", fakeDBName) + if err != nil { + t.Fatalf("Open: %v", err) + } + if _, err := db.Exec("WIPE"); err != nil { + t.Fatalf("exec wipe: %v", err) + } + if name == "people" { + exec(t, db, "CREATE|people|name=string,age=int32,photo=blob,dead=bool,bdate=datetime") + exec(t, db, "INSERT|people|name=Alice,age=?,photo=APHOTO", 1) + exec(t, db, "INSERT|people|name=Bob,age=?,photo=BPHOTO", 2) + exec(t, db, "INSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?", 3, chrisBirthday) + } + return db +} + +func exec(t *testing.T, db *DB, query string, args ...interface{}) { + _, err := db.Exec(query, args...) + if err != nil { + t.Fatalf("Exec of %q: %v", query, err) + } +} + +func closeDB(t *testing.T, db *DB) { + err := db.Close() + if err != nil { + t.Fatalf("error closing DB: %v", err) + } +} + +// numPrepares assumes that db has exactly 1 idle conn and returns +// its count of calls to Prepare +func numPrepares(t *testing.T, db *DB) int { + if n := len(db.freeConn); n != 1 { + t.Fatalf("free conns = %d; want 1", n) + } + return db.freeConn[0].(*fakeConn).numPrepare +} + +func TestQuery(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + prepares0 := numPrepares(t, db) + rows, err := db.Query("SELECT|people|age,name|") + if err != nil { + t.Fatalf("Query: %v", err) + } + type row struct { + age int + name string + } + got := []row{} + for rows.Next() { + var r row + err = rows.Scan(&r.age, &r.name) + if err != nil { + t.Fatalf("Scan: %v", err) + } + got = append(got, r) + } + err = rows.Err() + if err != nil { + t.Fatalf("Err: %v", err) + } + want := []row{ + {age: 1, name: "Alice"}, + {age: 2, name: "Bob"}, + {age: 3, name: "Chris"}, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want) + } + + // And verify that the final rows.Next() call, which hit EOF, + // also closed the rows connection. + if n := len(db.freeConn); n != 1 { + t.Fatalf("free conns after query hitting EOF = %d; want 1", n) + } + if prepares := numPrepares(t, db) - prepares0; prepares != 1 { + t.Errorf("executed %d Prepare statements; want 1", prepares) + } +} + +func TestByteOwnership(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + rows, err := db.Query("SELECT|people|name,photo|") + if err != nil { + t.Fatalf("Query: %v", err) + } + type row struct { + name []byte + photo RawBytes + } + got := []row{} + for rows.Next() { + var r row + err = rows.Scan(&r.name, &r.photo) + if err != nil { + t.Fatalf("Scan: %v", err) + } + got = append(got, r) + } + corruptMemory := []byte("\xffPHOTO") + want := []row{ + {name: []byte("Alice"), photo: corruptMemory}, + {name: []byte("Bob"), photo: corruptMemory}, + {name: []byte("Chris"), photo: corruptMemory}, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want) + } + + var photo RawBytes + err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo) + if err == nil { + t.Error("want error scanning into RawBytes from QueryRow") + } +} + +func TestRowsColumns(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + rows, err := db.Query("SELECT|people|age,name|") + if err != nil { + t.Fatalf("Query: %v", err) + } + cols, err := rows.Columns() + if err != nil { + t.Fatalf("Columns: %v", err) + } + want := []string{"age", "name"} + if !reflect.DeepEqual(cols, want) { + t.Errorf("got %#v; want %#v", cols, want) + } +} + +func TestQueryRow(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + var name string + var age int + var birthday time.Time + + err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age) + if err == nil || !strings.Contains(err.Error(), "expected 2 destination arguments") { + t.Errorf("expected error from wrong number of arguments; actually got: %v", err) + } + + err = db.QueryRow("SELECT|people|bdate|age=?", 3).Scan(&birthday) + if err != nil || !birthday.Equal(chrisBirthday) { + t.Errorf("chris birthday = %v, err = %v; want %v", birthday, err, chrisBirthday) + } + + err = db.QueryRow("SELECT|people|age,name|age=?", 2).Scan(&age, &name) + if err != nil { + t.Fatalf("age QueryRow+Scan: %v", err) + } + if name != "Bob" { + t.Errorf("expected name Bob, got %q", name) + } + if age != 2 { + t.Errorf("expected age 2, got %d", age) + } + + err = db.QueryRow("SELECT|people|age,name|name=?", "Alice").Scan(&age, &name) + if err != nil { + t.Fatalf("name QueryRow+Scan: %v", err) + } + if name != "Alice" { + t.Errorf("expected name Alice, got %q", name) + } + if age != 1 { + t.Errorf("expected age 1, got %d", age) + } + + var photo []byte + err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo) + if err != nil { + t.Fatalf("photo QueryRow+Scan: %v", err) + } + want := []byte("APHOTO") + if !reflect.DeepEqual(photo, want) { + t.Errorf("photo = %q; want %q", photo, want) + } +} + +func TestStatementErrorAfterClose(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) + } + err = stmt.Close() + if err != nil { + t.Fatalf("Close: %v", err) + } + var name string + err = stmt.QueryRow("foo").Scan(&name) + if err == nil { + t.Errorf("expected error from QueryRow.Scan after Stmt.Close") + } +} + +func TestStatementQueryRow(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() + var age int + for n, tt := range []struct { + name string + want int + }{ + {"Alice", 1}, + {"Bob", 2}, + {"Chris", 3}, + } { + if err := stmt.QueryRow(tt.name).Scan(&age); err != nil { + t.Errorf("%d: on %q, QueryRow/Scan: %v", n, tt.name, err) + } else if age != tt.want { + t.Errorf("%d: age=%d, want %d", n, age, tt.want) + } + } + +} + +// just a test of fakedb itself +func TestBogusPreboundParameters(t *testing.T) { + db := newTestDB(t, "foo") + defer closeDB(t, db) + exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") + _, err := db.Prepare("INSERT|t1|name=?,age=bogusconversion") + if err == nil { + t.Fatalf("expected error") + } + if err.Error() != `fakedb: invalid conversion to int32 from "bogusconversion"` { + t.Errorf("unexpected error: %v", err) + } +} + +func TestExec(t *testing.T) { + db := newTestDB(t, "foo") + 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.Errorf("Stmt, err = %v, %v", stmt, err) + } + defer stmt.Close() + + type execTest struct { + args []interface{} + wantErr string + } + execTests := []execTest{ + // Okay: + {[]interface{}{"Brad", 31}, ""}, + {[]interface{}{"Brad", int64(31)}, ""}, + {[]interface{}{"Bob", "32"}, ""}, + {[]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"}, + + // Wrong number of args: + {[]interface{}{}, "sql: expected 2 arguments, got 0"}, + {[]interface{}{1, 2, 3}, "sql: expected 2 arguments, got 3"}, + } + for n, et := range execTests { + _, err := stmt.Exec(et.args...) + errStr := "" + if err != nil { + errStr = err.Error() + } + if errStr != et.wantErr { + t.Errorf("stmt.Execute #%d: for %v, got error %q, want error %q", + n, et.args, errStr, et.wantErr) + } + } +} + +func TestTxStmt(t *testing.T) { + db := newTestDB(t, "") + 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) + } +} + +// Issue: http://golang.org/issue/2784 +// This test didn't fail before because we got luckly with the fakedb driver. +// It was failing, and now not, in github.com/bradfitz/go-sql-test +func TestTxQuery(t *testing.T) { + db := newTestDB(t, "") + defer closeDB(t, db) + exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") + exec(t, db, "INSERT|t1|name=Alice") + + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer tx.Rollback() + + r, err := tx.Query("SELECT|t1|name|") + if err != nil { + t.Fatal(err) + } + defer r.Close() + + if !r.Next() { + if r.Err() != nil { + t.Fatal(r.Err()) + } + t.Fatal("expected one row") + } + + var x string + err = r.Scan(&x) + if err != nil { + t.Fatal(err) + } +} + +func TestTxQueryInvalid(t *testing.T) { + db := newTestDB(t, "") + defer closeDB(t, db) + + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer tx.Rollback() + + _, err = tx.Query("SELECT|t1|name|") + if err == nil { + t.Fatal("Error expected") + } +} + +// Tests fix for issue 2542, that we release a lock when querying on +// a closed connection. +func TestIssue2542Deadlock(t *testing.T) { + db := newTestDB(t, "people") + closeDB(t, db) + for i := 0; i < 2; i++ { + _, err := db.Query("SELECT|people|age,name|") + if err == nil { + t.Fatalf("expected error") + } + } +} + +// 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) { + db := newTestDB(t, "") + defer closeDB(t, db) + exec(t, db, "CREATE|t|id=int32,name=nullstring") + exec(t, db, "INSERT|t|id=10,name=?", nil) + + var name []byte + + err := db.QueryRow("SELECT|t|name|id=?", 10).Scan(&name) + if err != nil { + t.Fatal(err) + } + if name != nil { + t.Fatalf("name []byte should be nil for null column value, got: %#v", name) + } + + exec(t, db, "INSERT|t|id=11,name=?", "bob") + err = db.QueryRow("SELECT|t|name|id=?", 11).Scan(&name) + if err != nil { + t.Fatal(err) + } + if string(name) != "bob" { + t.Fatalf("name []byte should be bob, got: %q", string(name)) + } +} + +func TestPointerParamsAndScans(t *testing.T) { + db := newTestDB(t, "") + defer closeDB(t, db) + exec(t, db, "CREATE|t|id=int32,name=nullstring") + + bob := "bob" + var name *string + + name = &bob + exec(t, db, "INSERT|t|id=10,name=?", name) + name = nil + exec(t, db, "INSERT|t|id=20,name=?", name) + + err := db.QueryRow("SELECT|t|name|id=?", 10).Scan(&name) + if err != nil { + t.Fatalf("querying id 10: %v", err) + } + if name == nil { + t.Errorf("id 10's name = nil; want bob") + } else if *name != "bob" { + t.Errorf("id 10's name = %q; want bob", *name) + } + + err = db.QueryRow("SELECT|t|name|id=?", 20).Scan(&name) + if err != nil { + t.Fatalf("querying id 20: %v", err) + } + if name != nil { + t.Errorf("id 20 = %q; want nil", *name) + } +} + +func TestQueryRowClosingStmt(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + var name string + var age int + err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age, &name) + if err != nil { + t.Fatal(err) + } + if len(db.freeConn) != 1 { + t.Fatalf("expected 1 free conn") + } + fakeConn := db.freeConn[0].(*fakeConn) + if made, closed := fakeConn.stmtsMade, fakeConn.stmtsClosed; made != closed { + t.Errorf("statement close mismatch: made %d, closed %d", made, closed) + } +} + +type nullTestRow struct { + nullParam interface{} + notNullParam interface{} + scanNullVal interface{} +} + +type nullTestSpec struct { + nullType string + notNullType string + rows [6]nullTestRow +} + +func TestNullStringParam(t *testing.T) { + spec := nullTestSpec{"nullstring", "string", [6]nullTestRow{ + {NullString{"aqua", true}, "", NullString{"aqua", true}}, + {NullString{"brown", false}, "", NullString{"", false}}, + {"chartreuse", "", NullString{"chartreuse", true}}, + {NullString{"darkred", true}, "", NullString{"darkred", true}}, + {NullString{"eel", false}, "", NullString{"", false}}, + {"foo", NullString{"black", false}, nil}, + }} + nullTestRun(t, spec) +} + +func TestNullInt64Param(t *testing.T) { + spec := nullTestSpec{"nullint64", "int64", [6]nullTestRow{ + {NullInt64{31, true}, 1, NullInt64{31, true}}, + {NullInt64{-22, false}, 1, NullInt64{0, false}}, + {22, 1, NullInt64{22, true}}, + {NullInt64{33, true}, 1, NullInt64{33, true}}, + {NullInt64{222, false}, 1, NullInt64{0, false}}, + {0, NullInt64{31, false}, nil}, + }} + nullTestRun(t, spec) +} + +func TestNullFloat64Param(t *testing.T) { + spec := nullTestSpec{"nullfloat64", "float64", [6]nullTestRow{ + {NullFloat64{31.2, true}, 1, NullFloat64{31.2, true}}, + {NullFloat64{13.1, false}, 1, NullFloat64{0, false}}, + {-22.9, 1, NullFloat64{-22.9, true}}, + {NullFloat64{33.81, true}, 1, NullFloat64{33.81, true}}, + {NullFloat64{222, false}, 1, NullFloat64{0, false}}, + {10, NullFloat64{31.2, false}, nil}, + }} + nullTestRun(t, spec) +} + +func TestNullBoolParam(t *testing.T) { + spec := nullTestSpec{"nullbool", "bool", [6]nullTestRow{ + {NullBool{false, true}, true, NullBool{false, true}}, + {NullBool{true, false}, false, NullBool{false, false}}, + {true, true, NullBool{true, true}}, + {NullBool{true, true}, false, NullBool{true, true}}, + {NullBool{true, false}, true, NullBool{false, false}}, + {true, NullBool{true, false}, nil}, + }} + nullTestRun(t, spec) +} + +func nullTestRun(t *testing.T, spec nullTestSpec) { + db := newTestDB(t, "") + defer closeDB(t, db) + exec(t, db, fmt.Sprintf("CREATE|t|id=int32,name=string,nullf=%s,notnullf=%s", spec.nullType, spec.notNullType)) + + // Inserts with db.Exec: + exec(t, db, "INSERT|t|id=?,name=?,nullf=?,notnullf=?", 1, "alice", spec.rows[0].nullParam, spec.rows[0].notNullParam) + exec(t, db, "INSERT|t|id=?,name=?,nullf=?,notnullf=?", 2, "bob", spec.rows[1].nullParam, spec.rows[1].notNullParam) + + // Inserts with a prepared statement: + stmt, err := db.Prepare("INSERT|t|id=?,name=?,nullf=?,notnullf=?") + if err != nil { + t.Fatalf("prepare: %v", err) + } + defer stmt.Close() + if _, err := stmt.Exec(3, "chris", spec.rows[2].nullParam, spec.rows[2].notNullParam); err != nil { + t.Errorf("exec insert chris: %v", err) + } + if _, err := stmt.Exec(4, "dave", spec.rows[3].nullParam, spec.rows[3].notNullParam); err != nil { + t.Errorf("exec insert dave: %v", err) + } + if _, err := stmt.Exec(5, "eleanor", spec.rows[4].nullParam, spec.rows[4].notNullParam); err != nil { + t.Errorf("exec insert eleanor: %v", err) + } + + // Can't put null val into non-null col + if _, err := stmt.Exec(6, "bob", spec.rows[5].nullParam, spec.rows[5].notNullParam); err == nil { + t.Errorf("expected error inserting nil val with prepared statement Exec") + } + + _, err = db.Exec("INSERT|t|id=?,name=?,nullf=?", 999, nil, nil) + if err == nil { + // TODO: this test fails, but it's just because + // fakeConn implements the optional Execer interface, + // so arguably this is the correct behavior. But + // maybe I should flesh out the fakeConn.Exec + // implementation so this properly fails. + // t.Errorf("expected error inserting nil name with Exec") + } + + paramtype := reflect.TypeOf(spec.rows[0].nullParam) + bindVal := reflect.New(paramtype).Interface() + + for i := 0; i < 5; i++ { + id := i + 1 + if err := db.QueryRow("SELECT|t|nullf|id=?", id).Scan(bindVal); err != nil { + t.Errorf("id=%d Scan: %v", id, err) + } + bindValDeref := reflect.ValueOf(bindVal).Elem().Interface() + if !reflect.DeepEqual(bindValDeref, spec.rows[i].scanNullVal) { + t.Errorf("id=%d got %#v, want %#v", id, bindValDeref, spec.rows[i].scanNullVal) + } + } +} + +func stack() string { + buf := make([]byte, 1024) + return string(buf[:runtime.Stack(buf, false)]) +} diff --git a/src/pkg/debug/dwarf/Makefile b/src/pkg/debug/dwarf/Makefile deleted file mode 100644 index c4203188e..000000000 --- a/src/pkg/debug/dwarf/Makefile +++ /dev/null @@ -1,16 +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 ../../../Make.inc - -TARG=debug/dwarf -GOFILES=\ - buf.go\ - const.go\ - entry.go\ - open.go\ - type.go\ - unit.go\ - -include ../../../Make.pkg diff --git a/src/pkg/debug/dwarf/buf.go b/src/pkg/debug/dwarf/buf.go index 2d29cebdd..6dc28d256 100644 --- a/src/pkg/debug/dwarf/buf.go +++ b/src/pkg/debug/dwarf/buf.go @@ -8,7 +8,6 @@ package dwarf import ( "encoding/binary" - "os" "strconv" ) @@ -20,7 +19,7 @@ type buf struct { off Offset data []byte addrsize int - err os.Error + err error } func makeBuf(d *Data, name string, off Offset, data []byte, addrsize int) buf { @@ -146,9 +145,9 @@ func (b *buf) error(s string) { type DecodeError struct { Name string Offset Offset - Error string + Err string } -func (e DecodeError) String() string { - return "decoding dwarf section " + e.Name + " at offset 0x" + strconv.Itob64(int64(e.Offset), 16) + ": " + e.Error +func (e DecodeError) Error() string { + return "decoding dwarf section " + e.Name + " at offset 0x" + strconv.FormatInt(int64(e.Offset), 16) + ": " + e.Err } diff --git a/src/pkg/debug/dwarf/const.go b/src/pkg/debug/dwarf/const.go index 1a3fec155..918b153d0 100644 --- a/src/pkg/debug/dwarf/const.go +++ b/src/pkg/debug/dwarf/const.go @@ -178,7 +178,7 @@ func (a Attr) GoString() string { return "dwarf.Attr" + s } } - return "dwarf.Attr(" + strconv.Itoa64(int64(a)) + ")" + return "dwarf.Attr(" + strconv.FormatInt(int64(a), 10) + ")" } // A format is a DWARF data encoding format. @@ -347,7 +347,7 @@ func (t Tag) GoString() string { return "dwarf.Tag" + s } } - return "dwarf.Tag(" + strconv.Itoa64(int64(t)) + ")" + return "dwarf.Tag(" + strconv.FormatInt(int64(t), 10) + ")" } // Location expression operators. diff --git a/src/pkg/debug/dwarf/entry.go b/src/pkg/debug/dwarf/entry.go index 549e5c2cc..2885d8fa2 100644 --- a/src/pkg/debug/dwarf/entry.go +++ b/src/pkg/debug/dwarf/entry.go @@ -10,7 +10,7 @@ package dwarf -import "os" +import "errors" // a single entry's description: a sequence of attributes type abbrev struct { @@ -29,7 +29,7 @@ type abbrevTable map[uint32]abbrev // ParseAbbrev returns the abbreviation table that starts at byte off // in the .debug_abbrev section. -func (d *Data) parseAbbrev(off uint32) (abbrevTable, os.Error) { +func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) { if m, ok := d.abbrevCache[off]; ok { return m, nil } @@ -232,7 +232,7 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { type Reader struct { b buf d *Data - err os.Error + err error unit int lastChildren bool // .Children of last entry returned by Next lastSibling Offset // .Val(AttrSibling) of last entry returned by Next @@ -273,7 +273,7 @@ func (r *Reader) Seek(off Offset) { return } } - r.err = os.NewError("offset out of range") + r.err = errors.New("offset out of range") } // maybeNextUnit advances to the next unit if this one is finished. @@ -289,7 +289,7 @@ func (r *Reader) maybeNextUnit() { // It returns nil, nil when it reaches the end of the section. // It returns an error if the current offset is invalid or the data at the // offset cannot be decoded as a valid Entry. -func (r *Reader) Next() (*Entry, os.Error) { +func (r *Reader) Next() (*Entry, error) { if r.err != nil { return nil, r.err } diff --git a/src/pkg/debug/dwarf/open.go b/src/pkg/debug/dwarf/open.go index d9525f788..37a518b6d 100644 --- a/src/pkg/debug/dwarf/open.go +++ b/src/pkg/debug/dwarf/open.go @@ -7,10 +7,7 @@ // http://dwarfstd.org/doc/dwarf-2.0.0.pdf package dwarf -import ( - "encoding/binary" - "os" -) +import "encoding/binary" // Data represents the DWARF debugging information // loaded from an executable file (for example, an ELF or Mach-O executable). @@ -34,13 +31,14 @@ type Data struct { } // New returns a new Data object initialized from the given parameters. -// Clients should typically use [TODO(rsc): method to be named later] instead of calling -// New directly. +// Rather than calling this function directly, clients should typically use +// the DWARF method of the File type of the appropriate package debug/elf, +// debug/macho, or debug/pe. // // The []byte arguments are the data from the corresponding debug section // in the object file; for example, for an ELF object, abbrev is the contents of // the ".debug_abbrev" section. -func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Data, os.Error) { +func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Data, error) { d := &Data{ abbrev: abbrev, aranges: aranges, diff --git a/src/pkg/debug/dwarf/testdata/typedef.c b/src/pkg/debug/dwarf/testdata/typedef.c index 664d021ce..f05f01564 100644 --- a/src/pkg/debug/dwarf/testdata/typedef.c +++ b/src/pkg/debug/dwarf/testdata/typedef.c @@ -28,8 +28,13 @@ typedef struct my_struct { volatile int vi; char x : 1; int y : 4; + int z[0]; long long array[40]; + int zz[0]; } t_my_struct; +typedef struct my_struct1 { + int zz [1]; +} t_my_struct1; typedef union my_union { volatile int vi; char x : 1; @@ -65,7 +70,8 @@ t_func_void_of_char *a9; t_func_void_of_void *a10; t_func_void_of_ptr_char_dots *a11; t_my_struct *a12; -t_my_union *a12a; +t_my_struct1 *a12a; +t_my_union *a12b; t_my_enum *a13; t_my_list *a14; t_my_tree *a15; diff --git a/src/pkg/debug/dwarf/testdata/typedef.elf b/src/pkg/debug/dwarf/testdata/typedef.elf index 44df8da9b..b2062d2c4 100755 Binary files a/src/pkg/debug/dwarf/testdata/typedef.elf and b/src/pkg/debug/dwarf/testdata/typedef.elf differ diff --git a/src/pkg/debug/dwarf/testdata/typedef.macho b/src/pkg/debug/dwarf/testdata/typedef.macho index 41019c1e1..f75afcccb 100644 Binary files a/src/pkg/debug/dwarf/testdata/typedef.macho and b/src/pkg/debug/dwarf/testdata/typedef.macho differ diff --git a/src/pkg/debug/dwarf/type.go b/src/pkg/debug/dwarf/type.go index f35365ebe..450235502 100644 --- a/src/pkg/debug/dwarf/type.go +++ b/src/pkg/debug/dwarf/type.go @@ -8,10 +8,7 @@ package dwarf -import ( - "os" - "strconv" -) +import "strconv" // A Type conventionally represents a pointer to any of the // specific Type structures (CharType, StructType, etc.). @@ -113,7 +110,7 @@ type ArrayType struct { } func (t *ArrayType) String() string { - return "[" + strconv.Itoa64(t.Count) + "]" + t.Type.String() + return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.String() } func (t *ArrayType) Size() int64 { return t.Count * t.Type.Size() } @@ -174,10 +171,10 @@ func (t *StructType) Defn() string { s += "; " } s += f.Name + " " + f.Type.String() - s += "@" + strconv.Itoa64(f.ByteOffset) + s += "@" + strconv.FormatInt(f.ByteOffset, 10) if f.BitSize > 0 { - s += " : " + strconv.Itoa64(f.BitSize) - s += "@" + strconv.Itoa64(f.BitOffset) + s += " : " + strconv.FormatInt(f.BitSize, 10) + s += "@" + strconv.FormatInt(f.BitOffset, 10) } } s += "}" @@ -209,7 +206,7 @@ func (t *EnumType) String() string { if i > 0 { s += "; " } - s += v.Name + "=" + strconv.Itoa64(v.Val) + s += v.Name + "=" + strconv.FormatInt(v.Val, 10) } s += "}" return s @@ -254,7 +251,7 @@ func (t *TypedefType) String() string { return t.Name } func (t *TypedefType) Size() int64 { return t.Type.Size() } -func (d *Data) Type(off Offset) (Type, os.Error) { +func (d *Data) Type(off Offset) (Type, error) { if t, ok := d.typeCache[off]; ok { return t, nil } @@ -429,6 +426,8 @@ func (d *Data) Type(off Offset) (Type, os.Error) { t.StructName, _ = e.Val(AttrName).(string) t.Incomplete = e.Val(AttrDeclaration) != nil t.Field = make([]*StructField, 0, 8) + var lastFieldType Type + var lastFieldBitOffset int64 for kid := next(); kid != nil; kid = next() { if kid.Tag == TagMember { f := new(StructField) @@ -447,11 +446,32 @@ func (d *Data) Type(off Offset) (Type, os.Error) { goto Error } } + + haveBitOffset := false f.Name, _ = kid.Val(AttrName).(string) f.ByteSize, _ = kid.Val(AttrByteSize).(int64) - f.BitOffset, _ = kid.Val(AttrBitOffset).(int64) + f.BitOffset, haveBitOffset = kid.Val(AttrBitOffset).(int64) f.BitSize, _ = kid.Val(AttrBitSize).(int64) t.Field = append(t.Field, f) + + bito := f.BitOffset + if !haveBitOffset { + bito = f.ByteOffset * 8 + } + if bito == lastFieldBitOffset && t.Kind != "union" { + // Last field was zero width. Fix array length. + // (DWARF writes out 0-length arrays as if they were 1-length arrays.) + zeroArray(lastFieldType) + } + lastFieldType = f.Type + lastFieldBitOffset = bito + } + } + if t.Kind != "union" { + b, ok := e.Val(AttrByteSize).(int64) + if ok && b*8 == lastFieldBitOffset { + // Final field must be zero width. Fix array length. + zeroArray(lastFieldType) } } @@ -579,6 +599,17 @@ Error: // If the parse fails, take the type out of the cache // so that the next call with this offset doesn't hit // the cache and return success. - d.typeCache[off] = nil, false + delete(d.typeCache, off) return nil, err } + +func zeroArray(t Type) { + for { + at, ok := t.(*ArrayType) + if !ok { + break + } + at.Count = 0 + t = at.Type + } +} diff --git a/src/pkg/debug/dwarf/type_test.go b/src/pkg/debug/dwarf/type_test.go index b9470a4fc..b5b255f6f 100644 --- a/src/pkg/debug/dwarf/type_test.go +++ b/src/pkg/debug/dwarf/type_test.go @@ -25,13 +25,22 @@ var typedefTests = map[string]string{ "t_func_void_of_char": "func(char) void", "t_func_void_of_void": "func() void", "t_func_void_of_ptr_char_dots": "func(*char, ...) void", - "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; array [40]long long int@8}", + "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; z [0]int@8; array [40]long long int@8; zz [0]int@328}", + "t_my_struct1": "struct my_struct1 {zz [1]int@0}", "t_my_union": "union my_union {vi volatile int@0; x char@0 : 1@7; y int@0 : 4@28; array [40]long long int@0}", "t_my_enum": "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}", "t_my_list": "struct list {val short int@0; next *t_my_list@8}", "t_my_tree": "struct tree {left *struct tree@0; right *struct tree@8; val long long unsigned int@16}", } +// As Apple converts gcc to a clang-based front end +// they keep breaking the DWARF output. This map lists the +// conversion from real answer to Apple answer. +var machoBug = map[string]string{ + "func(*char, ...) void": "func(*char) void", + "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}": "enum my_enum {e1=1; e2=2; e3=-5; e4=-1530494976}", +} + func elfData(t *testing.T, name string) *Data { f, err := elf.Open(name) if err != nil { @@ -58,13 +67,13 @@ func machoData(t *testing.T, name string) *Data { return d } -func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf")) } +func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf"), "elf") } func TestTypedefsMachO(t *testing.T) { - testTypedefs(t, machoData(t, "testdata/typedef.macho")) + testTypedefs(t, machoData(t, "testdata/typedef.macho"), "macho") } -func testTypedefs(t *testing.T, d *Data) { +func testTypedefs(t *testing.T, d *Data, kind string) { r := d.Reader() seen := make(map[string]bool) for { @@ -93,7 +102,7 @@ func testTypedefs(t *testing.T, d *Data) { t.Errorf("multiple definitions for %s", t1.Name) } seen[t1.Name] = true - if typstr != want { + if typstr != want && (kind != "macho" || typstr != machoBug[want]) { t.Errorf("%s:\n\thave %s\n\twant %s", t1.Name, typstr, want) } } diff --git a/src/pkg/debug/dwarf/unit.go b/src/pkg/debug/dwarf/unit.go index 02cb363b4..c10d75dbd 100644 --- a/src/pkg/debug/dwarf/unit.go +++ b/src/pkg/debug/dwarf/unit.go @@ -4,10 +4,7 @@ package dwarf -import ( - "os" - "strconv" -) +import "strconv" // DWARF debug info is split into a sequence of compilation units. // Each unit has its own abbreviation table and address size. @@ -20,7 +17,7 @@ type unit struct { addrsize int } -func (d *Data) parseUnits() ([]unit, os.Error) { +func (d *Data) parseUnits() ([]unit, error) { // Count units. nunit := 0 b := makeBuf(d, "info", 0, d.info, 0) diff --git a/src/pkg/debug/elf/Makefile b/src/pkg/debug/elf/Makefile deleted file mode 100644 index dd431f653..000000000 --- a/src/pkg/debug/elf/Makefile +++ /dev/null @@ -1,12 +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 ../../../Make.inc - -TARG=debug/elf -GOFILES=\ - elf.go\ - file.go\ - -include ../../../Make.pkg diff --git a/src/pkg/debug/elf/elf.go b/src/pkg/debug/elf/elf.go index c71b230bd..03e42b034 100644 --- a/src/pkg/debug/elf/elf.go +++ b/src/pkg/debug/elf/elf.go @@ -1490,11 +1490,11 @@ func stringName(i uint32, names []intName, goSyntax bool) string { if goSyntax { s = "elf." + s } - return s + "+" + strconv.Uitoa64(uint64(i-n.i)) + return s + "+" + strconv.FormatUint(uint64(i-n.i), 10) } } - return strconv.Uitoa64(uint64(i)) + return strconv.FormatUint(uint64(i), 10) } func flagName(i uint32, names []intName, goSyntax bool) string { @@ -1512,10 +1512,10 @@ func flagName(i uint32, names []intName, goSyntax bool) string { } } if len(s) == 0 { - return "0x" + strconv.Uitob64(uint64(i), 16) + return "0x" + strconv.FormatUint(uint64(i), 16) } if i != 0 { - s += "+0x" + strconv.Uitob64(uint64(i), 16) + s += "+0x" + strconv.FormatUint(uint64(i), 16) } return s } diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go index a0ddb1fc7..184ca8375 100644 --- a/src/pkg/debug/elf/file.go +++ b/src/pkg/debug/elf/file.go @@ -9,6 +9,7 @@ import ( "bytes" "debug/dwarf" "encoding/binary" + "errors" "fmt" "io" "os" @@ -71,7 +72,7 @@ type Section struct { } // Data reads and returns the contents of the ELF section. -func (s *Section) Data() ([]byte, os.Error) { +func (s *Section) Data() ([]byte, error) { dat := make([]byte, s.sr.Size()) n, err := s.sr.ReadAt(dat, 0) return dat[0:n], err @@ -79,9 +80,9 @@ func (s *Section) Data() ([]byte, os.Error) { // stringTable reads and returns the string table given by the // specified link value. -func (f *File) stringTable(link uint32) ([]byte, os.Error) { +func (f *File) stringTable(link uint32) ([]byte, error) { if link <= 0 || link >= uint32(len(f.Sections)) { - return nil, os.NewError("section has invalid string table link") + return nil, errors.New("section has invalid string table link") } return f.Sections[link].Data() } @@ -136,7 +137,7 @@ type FormatError struct { val interface{} } -func (e *FormatError) String() string { +func (e *FormatError) Error() string { msg := e.msg if e.val != nil { msg += fmt.Sprintf(" '%v' ", e.val) @@ -146,7 +147,7 @@ func (e *FormatError) String() string { } // Open opens the named file using os.Open and prepares it for use as an ELF binary. -func Open(name string) (*File, os.Error) { +func Open(name string) (*File, error) { f, err := os.Open(name) if err != nil { return nil, err @@ -163,8 +164,8 @@ func Open(name string) (*File, os.Error) { // Close closes the File. // If the File was created using NewFile directly instead of Open, // Close has no effect. -func (f *File) Close() os.Error { - var err os.Error +func (f *File) Close() error { + var err error if f.closer != nil { err = f.closer.Close() f.closer = nil @@ -185,7 +186,7 @@ func (f *File) SectionByType(typ SectionType) *Section { // NewFile creates a new File for accessing an ELF binary in an underlying reader. // The ELF binary is expected to start at position 0 in the ReaderAt. -func NewFile(r io.ReaderAt) (*File, os.Error) { +func NewFile(r io.ReaderAt) (*File, error) { sr := io.NewSectionReader(r, 0, 1<<63-1) // Read and decode ELF identifier var ident [16]uint8 @@ -381,7 +382,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { // getSymbols returns a slice of Symbols from parsing the symbol table // with the given type, along with the associated string table. -func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, os.Error) { +func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) { switch f.Class { case ELFCLASS64: return f.getSymbols64(typ) @@ -390,27 +391,27 @@ func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, os.Error) { return f.getSymbols32(typ) } - return nil, nil, os.NewError("not implemented") + return nil, nil, errors.New("not implemented") } -func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, os.Error) { +func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) { symtabSection := f.SectionByType(typ) if symtabSection == nil { - return nil, nil, os.NewError("no symbol section") + return nil, nil, errors.New("no symbol section") } data, err := symtabSection.Data() if err != nil { - return nil, nil, os.NewError("cannot load symbol section") + return nil, nil, errors.New("cannot load symbol section") } symtab := bytes.NewBuffer(data) if symtab.Len()%Sym32Size != 0 { - return nil, nil, os.NewError("length of symbol section is not a multiple of SymSize") + return nil, nil, errors.New("length of symbol section is not a multiple of SymSize") } strdata, err := f.stringTable(symtabSection.Link) if err != nil { - return nil, nil, os.NewError("cannot load string table section") + return nil, nil, errors.New("cannot load string table section") } // The first entry is all zeros. @@ -436,24 +437,24 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, os.Error) { return symbols, strdata, nil } -func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, os.Error) { +func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) { symtabSection := f.SectionByType(typ) if symtabSection == nil { - return nil, nil, os.NewError("no symbol section") + return nil, nil, errors.New("no symbol section") } data, err := symtabSection.Data() if err != nil { - return nil, nil, os.NewError("cannot load symbol section") + return nil, nil, errors.New("cannot load symbol section") } symtab := bytes.NewBuffer(data) if symtab.Len()%Sym64Size != 0 { - return nil, nil, os.NewError("length of symbol section is not a multiple of Sym64Size") + return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size") } strdata, err := f.stringTable(symtabSection.Link) if err != nil { - return nil, nil, os.NewError("cannot load string table section") + return nil, nil, errors.New("cannot load string table section") } // The first entry is all zeros. @@ -506,17 +507,17 @@ func (f *File) Section(name string) *Section { // applyRelocations applies relocations to dst. rels is a relocations section // in RELA format. -func (f *File) applyRelocations(dst []byte, rels []byte) os.Error { +func (f *File) applyRelocations(dst []byte, rels []byte) error { if f.Class == ELFCLASS64 && f.Machine == EM_X86_64 { return f.applyRelocationsAMD64(dst, rels) } - return os.NewError("not implemented") + return errors.New("not implemented") } -func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) os.Error { +func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error { if len(rels)%Sym64Size != 0 { - return os.NewError("length of relocation section is not a multiple of Sym64Size") + return errors.New("length of relocation section is not a multiple of Sym64Size") } symbols, _, err := f.getSymbols(SHT_SYMTAB) @@ -558,7 +559,7 @@ func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) os.Error { return nil } -func (f *File) DWARF() (*dwarf.Data, os.Error) { +func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these // are the required ones, and the debug/dwarf package // does not use the others, so don't bother loading them. @@ -596,7 +597,7 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) { } // Symbols returns the symbol table for f. -func (f *File) Symbols() ([]Symbol, os.Error) { +func (f *File) Symbols() ([]Symbol, error) { sym, _, err := f.getSymbols(SHT_SYMTAB) return sym, err } @@ -611,7 +612,7 @@ type ImportedSymbol struct { // referred to by the binary f that are expected to be // satisfied by other libraries at dynamic load time. // It does not return weak symbols. -func (f *File) ImportedSymbols() ([]ImportedSymbol, os.Error) { +func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { sym, str, err := f.getSymbols(SHT_DYNSYM) if err != nil { return nil, err @@ -721,7 +722,7 @@ func (f *File) gnuVersion(i int, sym *ImportedSymbol) { // ImportedLibraries returns the names of all libraries // referred to by the binary f that are expected to be // linked with the binary at dynamic link time. -func (f *File) ImportedLibraries() ([]string, os.Error) { +func (f *File) ImportedLibraries() ([]string, error) { ds := f.SectionByType(SHT_DYNAMIC) if ds == nil { // not dynamic, so no libraries diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go index 451d3d514..98f2723c8 100644 --- a/src/pkg/debug/elf/file_test.go +++ b/src/pkg/debug/elf/file_test.go @@ -227,6 +227,9 @@ func TestNoSectionOverlaps(t *testing.T) { } for i, si := range f.Sections { sih := si.SectionHeader + if sih.Type == SHT_NOBITS { + continue + } for j, sj := range f.Sections { sjh := sj.SectionHeader if i == j || sjh.Type == SHT_NOBITS || sih.Offset == sjh.Offset && sih.Size == 0 { diff --git a/src/pkg/debug/gosym/Makefile b/src/pkg/debug/gosym/Makefile deleted file mode 100644 index 4f420e729..000000000 --- a/src/pkg/debug/gosym/Makefile +++ /dev/null @@ -1,19 +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 ../../../Make.inc - -TARG=debug/gosym -GOFILES=\ - pclntab.go\ - symtab.go\ - -include ../../../Make.pkg - -test: make-pclinetest - -testshort: make-pclinetest - -make-pclinetest: - @if [ "`uname`-`uname -m`" = Linux-x86_64 -a $(GOARCH) = amd64 ]; then mkdir -p _test && $(AS) pclinetest.s && $(LD) -E main -o _test/pclinetest pclinetest.$O; fi diff --git a/src/pkg/debug/gosym/pclinetest.asm b/src/pkg/debug/gosym/pclinetest.asm new file mode 100644 index 000000000..6305435b0 --- /dev/null +++ b/src/pkg/debug/gosym/pclinetest.asm @@ -0,0 +1,58 @@ +TEXT linefrompc(SB),7,$0 // Each byte stores its line delta +BYTE $2; +BYTE $1; +BYTE $1; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; +BYTE $1; +BYTE $1; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +#include "pclinetest.h" +BYTE $2; +#include "pclinetest.h" +BYTE $2; + +TEXT pcfromline(SB),7,$0 // Each record stores its line delta, then n, then n more bytes +BYTE $31; BYTE $0; +BYTE $1; BYTE $1; BYTE $0; +BYTE $1; BYTE $0; + +BYTE $2; BYTE $4; BYTE $0; BYTE $0; BYTE $0; BYTE $0; + + +#include "pclinetest.h" +BYTE $4; BYTE $0; + + +BYTE $3; BYTE $3; BYTE $0; BYTE $0; BYTE $0; +#include "pclinetest.h" + + +BYTE $4; BYTE $3; BYTE $0; BYTE $0; BYTE $0; + +TEXT main(SB),7,$0 + // Prevent GC of our test symbols + CALL linefrompc(SB) + CALL pcfromline(SB) + +// Keep the linker happy +TEXT main·main(SB),7,$0 + RET + +TEXT main·init(SB),7,$0 + RET diff --git a/src/pkg/debug/gosym/pclinetest.h b/src/pkg/debug/gosym/pclinetest.h index a6c40e76c..156c0b87b 100644 --- a/src/pkg/debug/gosym/pclinetest.h +++ b/src/pkg/debug/gosym/pclinetest.h @@ -1,3 +1,5 @@ +// +build ignore + // Empty include file to generate z symbols diff --git a/src/pkg/debug/gosym/pclinetest.s b/src/pkg/debug/gosym/pclinetest.s deleted file mode 100644 index 6305435b0..000000000 --- a/src/pkg/debug/gosym/pclinetest.s +++ /dev/null @@ -1,58 +0,0 @@ -TEXT linefrompc(SB),7,$0 // Each byte stores its line delta -BYTE $2; -BYTE $1; -BYTE $1; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; -BYTE $1; -BYTE $1; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -#include "pclinetest.h" -BYTE $2; -#include "pclinetest.h" -BYTE $2; - -TEXT pcfromline(SB),7,$0 // Each record stores its line delta, then n, then n more bytes -BYTE $31; BYTE $0; -BYTE $1; BYTE $1; BYTE $0; -BYTE $1; BYTE $0; - -BYTE $2; BYTE $4; BYTE $0; BYTE $0; BYTE $0; BYTE $0; - - -#include "pclinetest.h" -BYTE $4; BYTE $0; - - -BYTE $3; BYTE $3; BYTE $0; BYTE $0; BYTE $0; -#include "pclinetest.h" - - -BYTE $4; BYTE $3; BYTE $0; BYTE $0; BYTE $0; - -TEXT main(SB),7,$0 - // Prevent GC of our test symbols - CALL linefrompc(SB) - CALL pcfromline(SB) - -// Keep the linker happy -TEXT main·main(SB),7,$0 - RET - -TEXT main·init(SB),7,$0 - RET diff --git a/src/pkg/debug/gosym/pclntab_test.go b/src/pkg/debug/gosym/pclntab_test.go index c83e64eab..b2400bb3b 100644 --- a/src/pkg/debug/gosym/pclntab_test.go +++ b/src/pkg/debug/gosym/pclntab_test.go @@ -6,14 +6,37 @@ package gosym import ( "debug/elf" + "fmt" "os" + "os/exec" + "runtime" + "strings" "testing" - "syscall" ) +var pclinetestBinary string + func dotest() bool { // For now, only works on ELF platforms. - return syscall.OS == "linux" && os.Getenv("GOARCH") == "amd64" + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + return false + } + if pclinetestBinary != "" { + return true + } + // This command builds pclinetest from pclinetest.asm; + // the resulting binary looks like it was built from pclinetest.s, + // but we have renamed it to keep it away from the go tool. + pclinetestBinary = os.TempDir() + "/pclinetest" + command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6", + pclinetestBinary, pclinetestBinary, pclinetestBinary) + cmd := exec.Command("sh", "-c", command) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + panic(err) + } + return true } func getTable(t *testing.T) *Table { @@ -148,7 +171,7 @@ func TestPCLine(t *testing.T) { return } - f, tab := crack("_test/pclinetest", t) + f, tab := crack(pclinetestBinary, t) text := f.Section(".text") textdat, err := text.Data() if err != nil { @@ -162,10 +185,13 @@ func TestPCLine(t *testing.T) { file, line, fn := tab.PCToLine(pc) off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g wantLine += int(textdat[off]) + t.Logf("off is %d", off) if fn == nil { t.Errorf("failed to get line of PC %#x", pc) - } else if len(file) < 12 || file[len(file)-12:] != "pclinetest.s" || line != wantLine || fn != sym { - t.Errorf("expected %s:%d (%s) at PC %#x, got %s:%d (%s)", "pclinetest.s", wantLine, sym.Name, pc, file, line, fn.Name) + } else if !strings.HasSuffix(file, "pclinetest.asm") { + t.Errorf("expected %s (%s) at PC %#x, got %s (%s)", "pclinetest.asm", sym.Name, pc, file, fn.Name) + } else if line != wantLine || fn != sym { + t.Errorf("expected :%d (%s) at PC %#x, got :%d (%s)", wantLine, sym.Name, pc, line, fn.Name) } } diff --git a/src/pkg/debug/gosym/symtab.go b/src/pkg/debug/gosym/symtab.go index dea460d71..52d7d55a3 100644 --- a/src/pkg/debug/gosym/symtab.go +++ b/src/pkg/debug/gosym/symtab.go @@ -15,7 +15,6 @@ package gosym import ( "encoding/binary" "fmt" - "os" "strconv" "strings" ) @@ -105,7 +104,7 @@ type sym struct { name []byte } -func walksymtab(data []byte, fn func(sym) os.Error) os.Error { +func walksymtab(data []byte, fn func(sym) error) error { var s sym p := data for len(p) >= 6 { @@ -149,9 +148,9 @@ func walksymtab(data []byte, fn func(sym) os.Error) os.Error { // NewTable decodes the Go symbol table in data, // returning an in-memory representation. -func NewTable(symtab []byte, pcln *LineTable) (*Table, os.Error) { +func NewTable(symtab []byte, pcln *LineTable) (*Table, error) { var n int - err := walksymtab(symtab, func(s sym) os.Error { + err := walksymtab(symtab, func(s sym) error { n++ return nil }) @@ -165,7 +164,7 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, os.Error) { nf := 0 nz := 0 lasttyp := uint8(0) - err = walksymtab(symtab, func(s sym) os.Error { + err = walksymtab(symtab, func(s sym) error { n := len(t.Syms) t.Syms = t.Syms[0 : n+1] ts := &t.Syms[n] @@ -355,7 +354,7 @@ func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) { // LineToPC looks up the first program counter on the given line in // the named file. Returns UnknownPathError or UnknownLineError if // there is an error looking up this line. -func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err os.Error) { +func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) { obj, ok := t.Files[file] if !ok { return 0, nil, UnknownFileError(file) @@ -466,7 +465,7 @@ pathloop: return tos.path, aline - tos.start - tos.offset + 1 } -func (o *Obj) alineFromLine(path string, line int) (int, os.Error) { +func (o *Obj) alineFromLine(path string, line int) (int, error) { if line < 1 { return 0, &UnknownLineError{path, line} } @@ -516,7 +515,7 @@ func (o *Obj) alineFromLine(path string, line int) (int, os.Error) { // the symbol table. type UnknownFileError string -func (e UnknownFileError) String() string { return "unknown file: " + string(e) } +func (e UnknownFileError) Error() string { return "unknown file: " + string(e) } // UnknownLineError represents a failure to map a line to a program // counter, either because the line is beyond the bounds of the file @@ -526,7 +525,7 @@ type UnknownLineError struct { Line int } -func (e *UnknownLineError) String() string { +func (e *UnknownLineError) Error() string { return "no code at " + e.File + ":" + strconv.Itoa(e.Line) } @@ -538,7 +537,7 @@ type DecodingError struct { val interface{} } -func (e *DecodingError) String() string { +func (e *DecodingError) Error() string { msg := e.msg if e.val != nil { msg += fmt.Sprintf(" '%v'", e.val) diff --git a/src/pkg/debug/macho/Makefile b/src/pkg/debug/macho/Makefile deleted file mode 100644 index 5fbbf1efe..000000000 --- a/src/pkg/debug/macho/Makefile +++ /dev/null @@ -1,12 +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 ../../../Make.inc - -TARG=debug/macho -GOFILES=\ - macho.go\ - file.go\ - -include ../../../Make.pkg diff --git a/src/pkg/debug/macho/file.go b/src/pkg/debug/macho/file.go index 721a4c416..fa73a315c 100644 --- a/src/pkg/debug/macho/file.go +++ b/src/pkg/debug/macho/file.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package macho implements access to Mach-O object files, as defined by -// http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html. +// Package macho implements access to Mach-O object files. package macho // High level access to low level data structures. @@ -12,6 +11,7 @@ import ( "bytes" "debug/dwarf" "encoding/binary" + "errors" "fmt" "io" "os" @@ -71,7 +71,7 @@ type Segment struct { } // Data reads and returns the contents of the segment. -func (s *Segment) Data() ([]byte, os.Error) { +func (s *Segment) Data() ([]byte, error) { dat := make([]byte, s.sr.Size()) n, err := s.sr.ReadAt(dat, 0) return dat[0:n], err @@ -106,7 +106,7 @@ type Section struct { } // Data reads and returns the contents of the Mach-O section. -func (s *Section) Data() ([]byte, os.Error) { +func (s *Section) Data() ([]byte, error) { dat := make([]byte, s.sr.Size()) n, err := s.sr.ReadAt(dat, 0) return dat[0:n], err @@ -148,7 +148,7 @@ type FormatError struct { val interface{} } -func (e *FormatError) String() string { +func (e *FormatError) Error() string { msg := e.msg if e.val != nil { msg += fmt.Sprintf(" '%v'", e.val) @@ -158,7 +158,7 @@ func (e *FormatError) String() string { } // Open opens the named file using os.Open and prepares it for use as a Mach-O binary. -func Open(name string) (*File, os.Error) { +func Open(name string) (*File, error) { f, err := os.Open(name) if err != nil { return nil, err @@ -175,8 +175,8 @@ func Open(name string) (*File, os.Error) { // Close closes the File. // If the File was created using NewFile directly instead of Open, // Close has no effect. -func (f *File) Close() os.Error { - var err os.Error +func (f *File) Close() error { + var err error if f.closer != nil { err = f.closer.Close() f.closer = nil @@ -186,7 +186,7 @@ func (f *File) Close() os.Error { // NewFile creates a new File for accessing a Mach-O binary in an underlying reader. // The Mach-O binary is expected to start at position 0 in the ReaderAt. -func NewFile(r io.ReaderAt) (*File, os.Error) { +func NewFile(r io.ReaderAt) (*File, error) { f := new(File) sr := io.NewSectionReader(r, 0, 1<<63-1) @@ -391,7 +391,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { return f, nil } -func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, os.Error) { +func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) { bo := f.ByteOrder symtab := make([]Symbol, hdr.Nsyms) b := bytes.NewBuffer(symdat) @@ -463,7 +463,7 @@ func (f *File) Section(name string) *Section { } // DWARF returns the DWARF debug information for the Mach-O file. -func (f *File) DWARF() (*dwarf.Data, os.Error) { +func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these // are the required ones, and the debug/dwarf package // does not use the others, so don't bother loading them. @@ -473,7 +473,7 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) { name = "__debug_" + name s := f.Section(name) if s == nil { - return nil, os.NewError("missing Mach-O section " + name) + return nil, errors.New("missing Mach-O section " + name) } b, err := s.Data() if err != nil && uint64(len(b)) < s.Size { @@ -489,7 +489,7 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) { // ImportedSymbols returns the names of all symbols // referred to by the binary f that are expected to be // satisfied by other libraries at dynamic load time. -func (f *File) ImportedSymbols() ([]string, os.Error) { +func (f *File) ImportedSymbols() ([]string, error) { if f.Dysymtab == nil || f.Symtab == nil { return nil, &FormatError{0, "missing symbol table", nil} } @@ -506,7 +506,7 @@ func (f *File) ImportedSymbols() ([]string, os.Error) { // ImportedLibraries returns the paths of all libraries // referred to by the binary f that are expected to be // linked with the binary at dynamic link time. -func (f *File) ImportedLibraries() ([]string, os.Error) { +func (f *File) ImportedLibraries() ([]string, error) { var all []string for _, l := range f.Loads { if lib, ok := l.(*Dylib); ok { diff --git a/src/pkg/debug/macho/file_test.go b/src/pkg/debug/macho/file_test.go index 56d8a20be..640225b32 100644 --- a/src/pkg/debug/macho/file_test.go +++ b/src/pkg/debug/macho/file_test.go @@ -21,11 +21,11 @@ var fileTests = []fileTest{ "testdata/gcc-386-darwin-exec", FileHeader{0xfeedface, Cpu386, 0x3, 0x2, 0xc, 0x3c0, 0x85}, []*SegmentHeader{ - &SegmentHeader{LoadCmdSegment, 0x38, "__PAGEZERO", 0x0, 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - &SegmentHeader{LoadCmdSegment, 0xc0, "__TEXT", 0x1000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x2, 0x0}, - &SegmentHeader{LoadCmdSegment, 0xc0, "__DATA", 0x2000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x2, 0x0}, - &SegmentHeader{LoadCmdSegment, 0x7c, "__IMPORT", 0x3000, 0x1000, 0x2000, 0x1000, 0x7, 0x7, 0x1, 0x0}, - &SegmentHeader{LoadCmdSegment, 0x38, "__LINKEDIT", 0x4000, 0x1000, 0x3000, 0x12c, 0x7, 0x1, 0x0, 0x0}, + {LoadCmdSegment, 0x38, "__PAGEZERO", 0x0, 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {LoadCmdSegment, 0xc0, "__TEXT", 0x1000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x2, 0x0}, + {LoadCmdSegment, 0xc0, "__DATA", 0x2000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x2, 0x0}, + {LoadCmdSegment, 0x7c, "__IMPORT", 0x3000, 0x1000, 0x2000, 0x1000, 0x7, 0x7, 0x1, 0x0}, + {LoadCmdSegment, 0x38, "__LINKEDIT", 0x4000, 0x1000, 0x3000, 0x12c, 0x7, 0x1, 0x0, 0x0}, nil, nil, nil, @@ -35,21 +35,21 @@ var fileTests = []fileTest{ nil, }, []*SectionHeader{ - &SectionHeader{"__text", "__TEXT", 0x1f68, 0x88, 0xf68, 0x2, 0x0, 0x0, 0x80000400}, - &SectionHeader{"__cstring", "__TEXT", 0x1ff0, 0xd, 0xff0, 0x0, 0x0, 0x0, 0x2}, - &SectionHeader{"__data", "__DATA", 0x2000, 0x14, 0x1000, 0x2, 0x0, 0x0, 0x0}, - &SectionHeader{"__dyld", "__DATA", 0x2014, 0x1c, 0x1014, 0x2, 0x0, 0x0, 0x0}, - &SectionHeader{"__jump_table", "__IMPORT", 0x3000, 0xa, 0x2000, 0x6, 0x0, 0x0, 0x4000008}, + {"__text", "__TEXT", 0x1f68, 0x88, 0xf68, 0x2, 0x0, 0x0, 0x80000400}, + {"__cstring", "__TEXT", 0x1ff0, 0xd, 0xff0, 0x0, 0x0, 0x0, 0x2}, + {"__data", "__DATA", 0x2000, 0x14, 0x1000, 0x2, 0x0, 0x0, 0x0}, + {"__dyld", "__DATA", 0x2014, 0x1c, 0x1014, 0x2, 0x0, 0x0, 0x0}, + {"__jump_table", "__IMPORT", 0x3000, 0xa, 0x2000, 0x6, 0x0, 0x0, 0x4000008}, }, }, { "testdata/gcc-amd64-darwin-exec", FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0x2, 0xb, 0x568, 0x85}, []*SegmentHeader{ - &SegmentHeader{LoadCmdSegment64, 0x48, "__PAGEZERO", 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - &SegmentHeader{LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x5, 0x0}, - &SegmentHeader{LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x3, 0x0}, - &SegmentHeader{LoadCmdSegment64, 0x48, "__LINKEDIT", 0x100002000, 0x1000, 0x2000, 0x140, 0x7, 0x1, 0x0, 0x0}, + {LoadCmdSegment64, 0x48, "__PAGEZERO", 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x5, 0x0}, + {LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x3, 0x0}, + {LoadCmdSegment64, 0x48, "__LINKEDIT", 0x100002000, 0x1000, 0x2000, 0x140, 0x7, 0x1, 0x0, 0x0}, nil, nil, nil, @@ -59,14 +59,14 @@ var fileTests = []fileTest{ nil, }, []*SectionHeader{ - &SectionHeader{"__text", "__TEXT", 0x100000f14, 0x6d, 0xf14, 0x2, 0x0, 0x0, 0x80000400}, - &SectionHeader{"__symbol_stub1", "__TEXT", 0x100000f81, 0xc, 0xf81, 0x0, 0x0, 0x0, 0x80000408}, - &SectionHeader{"__stub_helper", "__TEXT", 0x100000f90, 0x18, 0xf90, 0x2, 0x0, 0x0, 0x0}, - &SectionHeader{"__cstring", "__TEXT", 0x100000fa8, 0xd, 0xfa8, 0x0, 0x0, 0x0, 0x2}, - &SectionHeader{"__eh_frame", "__TEXT", 0x100000fb8, 0x48, 0xfb8, 0x3, 0x0, 0x0, 0x6000000b}, - &SectionHeader{"__data", "__DATA", 0x100001000, 0x1c, 0x1000, 0x3, 0x0, 0x0, 0x0}, - &SectionHeader{"__dyld", "__DATA", 0x100001020, 0x38, 0x1020, 0x3, 0x0, 0x0, 0x0}, - &SectionHeader{"__la_symbol_ptr", "__DATA", 0x100001058, 0x10, 0x1058, 0x2, 0x0, 0x0, 0x7}, + {"__text", "__TEXT", 0x100000f14, 0x6d, 0xf14, 0x2, 0x0, 0x0, 0x80000400}, + {"__symbol_stub1", "__TEXT", 0x100000f81, 0xc, 0xf81, 0x0, 0x0, 0x0, 0x80000408}, + {"__stub_helper", "__TEXT", 0x100000f90, 0x18, 0xf90, 0x2, 0x0, 0x0, 0x0}, + {"__cstring", "__TEXT", 0x100000fa8, 0xd, 0xfa8, 0x0, 0x0, 0x0, 0x2}, + {"__eh_frame", "__TEXT", 0x100000fb8, 0x48, 0xfb8, 0x3, 0x0, 0x0, 0x6000000b}, + {"__data", "__DATA", 0x100001000, 0x1c, 0x1000, 0x3, 0x0, 0x0, 0x0}, + {"__dyld", "__DATA", 0x100001020, 0x38, 0x1020, 0x3, 0x0, 0x0, 0x0}, + {"__la_symbol_ptr", "__DATA", 0x100001058, 0x10, 0x1058, 0x2, 0x0, 0x0, 0x7}, }, }, { @@ -74,26 +74,26 @@ var fileTests = []fileTest{ FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0xa, 0x4, 0x5a0, 0}, []*SegmentHeader{ nil, - &SegmentHeader{LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x0, 0x7, 0x5, 0x5, 0x0}, - &SegmentHeader{LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x0, 0x0, 0x7, 0x3, 0x3, 0x0}, - &SegmentHeader{LoadCmdSegment64, 0x278, "__DWARF", 0x100002000, 0x1000, 0x1000, 0x1bc, 0x7, 0x3, 0x7, 0x0}, + {LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x0, 0x7, 0x5, 0x5, 0x0}, + {LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x0, 0x0, 0x7, 0x3, 0x3, 0x0}, + {LoadCmdSegment64, 0x278, "__DWARF", 0x100002000, 0x1000, 0x1000, 0x1bc, 0x7, 0x3, 0x7, 0x0}, }, []*SectionHeader{ - &SectionHeader{"__text", "__TEXT", 0x100000f14, 0x0, 0x0, 0x2, 0x0, 0x0, 0x80000400}, - &SectionHeader{"__symbol_stub1", "__TEXT", 0x100000f81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000408}, - &SectionHeader{"__stub_helper", "__TEXT", 0x100000f90, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0}, - &SectionHeader{"__cstring", "__TEXT", 0x100000fa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, - &SectionHeader{"__eh_frame", "__TEXT", 0x100000fb8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x6000000b}, - &SectionHeader{"__data", "__DATA", 0x100001000, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0}, - &SectionHeader{"__dyld", "__DATA", 0x100001020, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0}, - &SectionHeader{"__la_symbol_ptr", "__DATA", 0x100001058, 0x0, 0x0, 0x2, 0x0, 0x0, 0x7}, - &SectionHeader{"__debug_abbrev", "__DWARF", 0x100002000, 0x36, 0x1000, 0x0, 0x0, 0x0, 0x0}, - &SectionHeader{"__debug_aranges", "__DWARF", 0x100002036, 0x30, 0x1036, 0x0, 0x0, 0x0, 0x0}, - &SectionHeader{"__debug_frame", "__DWARF", 0x100002066, 0x40, 0x1066, 0x0, 0x0, 0x0, 0x0}, - &SectionHeader{"__debug_info", "__DWARF", 0x1000020a6, 0x54, 0x10a6, 0x0, 0x0, 0x0, 0x0}, - &SectionHeader{"__debug_line", "__DWARF", 0x1000020fa, 0x47, 0x10fa, 0x0, 0x0, 0x0, 0x0}, - &SectionHeader{"__debug_pubnames", "__DWARF", 0x100002141, 0x1b, 0x1141, 0x0, 0x0, 0x0, 0x0}, - &SectionHeader{"__debug_str", "__DWARF", 0x10000215c, 0x60, 0x115c, 0x0, 0x0, 0x0, 0x0}, + {"__text", "__TEXT", 0x100000f14, 0x0, 0x0, 0x2, 0x0, 0x0, 0x80000400}, + {"__symbol_stub1", "__TEXT", 0x100000f81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000408}, + {"__stub_helper", "__TEXT", 0x100000f90, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0}, + {"__cstring", "__TEXT", 0x100000fa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, + {"__eh_frame", "__TEXT", 0x100000fb8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x6000000b}, + {"__data", "__DATA", 0x100001000, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0}, + {"__dyld", "__DATA", 0x100001020, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0}, + {"__la_symbol_ptr", "__DATA", 0x100001058, 0x0, 0x0, 0x2, 0x0, 0x0, 0x7}, + {"__debug_abbrev", "__DWARF", 0x100002000, 0x36, 0x1000, 0x0, 0x0, 0x0, 0x0}, + {"__debug_aranges", "__DWARF", 0x100002036, 0x30, 0x1036, 0x0, 0x0, 0x0, 0x0}, + {"__debug_frame", "__DWARF", 0x100002066, 0x40, 0x1066, 0x0, 0x0, 0x0, 0x0}, + {"__debug_info", "__DWARF", 0x1000020a6, 0x54, 0x10a6, 0x0, 0x0, 0x0, 0x0}, + {"__debug_line", "__DWARF", 0x1000020fa, 0x47, 0x10fa, 0x0, 0x0, 0x0, 0x0}, + {"__debug_pubnames", "__DWARF", 0x100002141, 0x1b, 0x1141, 0x0, 0x0, 0x0, 0x0}, + {"__debug_str", "__DWARF", 0x10000215c, 0x60, 0x115c, 0x0, 0x0, 0x0, 0x0}, }, }, } diff --git a/src/pkg/debug/macho/macho.go b/src/pkg/debug/macho/macho.go index 1386f5acf..bc14226c5 100644 --- a/src/pkg/debug/macho/macho.go +++ b/src/pkg/debug/macho/macho.go @@ -278,7 +278,7 @@ func stringName(i uint32, names []intName, goSyntax bool) string { return n.s } } - return strconv.Uitoa64(uint64(i)) + return strconv.FormatUint(uint64(i), 10) } func flagName(i uint32, names []intName, goSyntax bool) string { @@ -296,10 +296,10 @@ func flagName(i uint32, names []intName, goSyntax bool) string { } } if len(s) == 0 { - return "0x" + strconv.Uitob64(uint64(i), 16) + return "0x" + strconv.FormatUint(uint64(i), 16) } if i != 0 { - s += "+0x" + strconv.Uitob64(uint64(i), 16) + s += "+0x" + strconv.FormatUint(uint64(i), 16) } return s } diff --git a/src/pkg/debug/pe/Makefile b/src/pkg/debug/pe/Makefile deleted file mode 100644 index 998e6a418..000000000 --- a/src/pkg/debug/pe/Makefile +++ /dev/null @@ -1,12 +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 ../../../Make.inc - -TARG=debug/pe -GOFILES=\ - pe.go\ - file.go\ - -include ../../../Make.pkg diff --git a/src/pkg/debug/pe/file.go b/src/pkg/debug/pe/file.go index d86d916f5..6b98a5f45 100644 --- a/src/pkg/debug/pe/file.go +++ b/src/pkg/debug/pe/file.go @@ -8,6 +8,7 @@ package pe import ( "debug/dwarf" "encoding/binary" + "errors" "fmt" "io" "os" @@ -59,7 +60,7 @@ type ImportDirectory struct { } // Data reads and returns the contents of the PE section. -func (s *Section) Data() ([]byte, os.Error) { +func (s *Section) Data() ([]byte, error) { dat := make([]byte, s.sr.Size()) n, err := s.sr.ReadAt(dat, 0) return dat[0:n], err @@ -74,7 +75,7 @@ type FormatError struct { val interface{} } -func (e *FormatError) String() string { +func (e *FormatError) Error() string { msg := e.msg if e.val != nil { msg += fmt.Sprintf(" '%v'", e.val) @@ -84,7 +85,7 @@ func (e *FormatError) String() string { } // Open opens the named file using os.Open and prepares it for use as a PE binary. -func Open(name string) (*File, os.Error) { +func Open(name string) (*File, error) { f, err := os.Open(name) if err != nil { return nil, err @@ -101,8 +102,8 @@ func Open(name string) (*File, os.Error) { // Close closes the File. // If the File was created using NewFile directly instead of Open, // Close has no effect. -func (f *File) Close() os.Error { - var err os.Error +func (f *File) Close() error { + var err error if f.closer != nil { err = f.closer.Close() f.closer = nil @@ -111,7 +112,7 @@ func (f *File) Close() os.Error { } // NewFile creates a new File for accessing a PE binary in an underlying reader. -func NewFile(r io.ReaderAt) (*File, os.Error) { +func NewFile(r io.ReaderAt) (*File, error) { f := new(File) sr := io.NewSectionReader(r, 0, 1<<63-1) @@ -124,7 +125,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { var sign [4]byte r.ReadAt(sign[0:], int64(dosheader[0x3c])) if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { - return nil, os.NewError("Invalid PE File Format.") + return nil, errors.New("Invalid PE File Format.") } base = int64(dosheader[0x3c]) + 4 } else { @@ -135,7 +136,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { return nil, err } if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 { - return nil, os.NewError("Invalid PE File Format.") + 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) @@ -215,7 +216,7 @@ func (f *File) Section(name string) *Section { return nil } -func (f *File) DWARF() (*dwarf.Data, os.Error) { +func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these // are the required ones, and the debug/dwarf package // does not use the others, so don't bother loading them. @@ -242,7 +243,7 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) { // referred to by the binary f that are expected to be // satisfied by other libraries at dynamic load time. // It does not return weak symbols. -func (f *File) ImportedSymbols() ([]string, os.Error) { +func (f *File) ImportedSymbols() ([]string, error) { pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 ds := f.Section(".idata") if ds == nil { @@ -308,7 +309,7 @@ func (f *File) ImportedSymbols() ([]string, os.Error) { // ImportedLibraries returns the names of all libraries // referred to by the binary f that are expected to be // linked with the binary at dynamic link time. -func (f *File) ImportedLibraries() ([]string, os.Error) { +func (f *File) ImportedLibraries() ([]string, error) { // TODO // cgo -dynimport don't use this for windows PE, so just return. return nil, nil diff --git a/src/pkg/debug/pe/file_test.go b/src/pkg/debug/pe/file_test.go index 2c5c25b8c..2815d720b 100644 --- a/src/pkg/debug/pe/file_test.go +++ b/src/pkg/debug/pe/file_test.go @@ -20,39 +20,39 @@ var fileTests = []fileTest{ "testdata/gcc-386-mingw-obj", FileHeader{0x014c, 0x000c, 0x0, 0x64a, 0x1e, 0x0, 0x104}, []*SectionHeader{ - &SectionHeader{".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020}, - &SectionHeader{".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264}, - &SectionHeader{".bss", 0, 0, 0, 0, 0, 0, 0, 0, 3224371328}, - &SectionHeader{".debug_abbrev", 0, 0, 137, 536, 0, 0, 0, 0, 0x42100000}, - &SectionHeader{".debug_info", 0, 0, 418, 673, 1470, 0, 7, 0, 1108344832}, - &SectionHeader{".debug_line", 0, 0, 128, 1091, 1540, 0, 1, 0, 1108344832}, - &SectionHeader{".rdata", 0, 0, 16, 1219, 0, 0, 0, 0, 1076887616}, - &SectionHeader{".debug_frame", 0, 0, 52, 1235, 1550, 0, 2, 0, 1110441984}, - &SectionHeader{".debug_loc", 0, 0, 56, 1287, 0, 0, 0, 0, 1108344832}, - &SectionHeader{".debug_pubnames", 0, 0, 27, 1343, 1570, 0, 1, 0, 1108344832}, - &SectionHeader{".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832}, - &SectionHeader{".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832}, + {".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020}, + {".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264}, + {".bss", 0, 0, 0, 0, 0, 0, 0, 0, 3224371328}, + {".debug_abbrev", 0, 0, 137, 536, 0, 0, 0, 0, 0x42100000}, + {".debug_info", 0, 0, 418, 673, 1470, 0, 7, 0, 1108344832}, + {".debug_line", 0, 0, 128, 1091, 1540, 0, 1, 0, 1108344832}, + {".rdata", 0, 0, 16, 1219, 0, 0, 0, 0, 1076887616}, + {".debug_frame", 0, 0, 52, 1235, 1550, 0, 2, 0, 1110441984}, + {".debug_loc", 0, 0, 56, 1287, 0, 0, 0, 0, 1108344832}, + {".debug_pubnames", 0, 0, 27, 1343, 1570, 0, 1, 0, 1108344832}, + {".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832}, + {".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832}, }, }, { "testdata/gcc-386-mingw-exec", FileHeader{0x014c, 0x000f, 0x4c6a1b60, 0x3c00, 0x282, 0xe0, 0x107}, []*SectionHeader{ - &SectionHeader{Name: ".text", VirtualSize: 0xcd8, VirtualAddress: 0x1000, Size: 0xe00, Offset: 0x400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x60500060}, - &SectionHeader{Name: ".data", VirtualSize: 0x10, VirtualAddress: 0x2000, Size: 0x200, Offset: 0x1200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, - &SectionHeader{Name: ".rdata", VirtualSize: 0x120, VirtualAddress: 0x3000, Size: 0x200, Offset: 0x1400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x40300040}, - &SectionHeader{Name: ".bss", VirtualSize: 0xdc, VirtualAddress: 0x4000, Size: 0x0, Offset: 0x0, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0400080}, - &SectionHeader{Name: ".idata", VirtualSize: 0x3c8, VirtualAddress: 0x5000, Size: 0x400, Offset: 0x1600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, - &SectionHeader{Name: ".CRT", VirtualSize: 0x18, VirtualAddress: 0x6000, Size: 0x200, Offset: 0x1a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, - &SectionHeader{Name: ".tls", VirtualSize: 0x20, VirtualAddress: 0x7000, Size: 0x200, Offset: 0x1c00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, - &SectionHeader{Name: ".debug_aranges", VirtualSize: 0x20, VirtualAddress: 0x8000, Size: 0x200, Offset: 0x1e00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, - &SectionHeader{Name: ".debug_pubnames", VirtualSize: 0x51, VirtualAddress: 0x9000, Size: 0x200, Offset: 0x2000, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, - &SectionHeader{Name: ".debug_pubtypes", VirtualSize: 0x91, VirtualAddress: 0xa000, Size: 0x200, Offset: 0x2200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, - &SectionHeader{Name: ".debug_info", VirtualSize: 0xe22, VirtualAddress: 0xb000, Size: 0x1000, Offset: 0x2400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, - &SectionHeader{Name: ".debug_abbrev", VirtualSize: 0x157, VirtualAddress: 0xc000, Size: 0x200, Offset: 0x3400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, - &SectionHeader{Name: ".debug_line", VirtualSize: 0x144, VirtualAddress: 0xd000, Size: 0x200, Offset: 0x3600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, - &SectionHeader{Name: ".debug_frame", VirtualSize: 0x34, VirtualAddress: 0xe000, Size: 0x200, Offset: 0x3800, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42300000}, - &SectionHeader{Name: ".debug_loc", VirtualSize: 0x38, VirtualAddress: 0xf000, Size: 0x200, Offset: 0x3a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".text", VirtualSize: 0xcd8, VirtualAddress: 0x1000, Size: 0xe00, Offset: 0x400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x60500060}, + {Name: ".data", VirtualSize: 0x10, VirtualAddress: 0x2000, Size: 0x200, Offset: 0x1200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, + {Name: ".rdata", VirtualSize: 0x120, VirtualAddress: 0x3000, Size: 0x200, Offset: 0x1400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x40300040}, + {Name: ".bss", VirtualSize: 0xdc, VirtualAddress: 0x4000, Size: 0x0, Offset: 0x0, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0400080}, + {Name: ".idata", VirtualSize: 0x3c8, VirtualAddress: 0x5000, Size: 0x400, Offset: 0x1600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, + {Name: ".CRT", VirtualSize: 0x18, VirtualAddress: 0x6000, Size: 0x200, Offset: 0x1a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, + {Name: ".tls", VirtualSize: 0x20, VirtualAddress: 0x7000, Size: 0x200, Offset: 0x1c00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, + {Name: ".debug_aranges", VirtualSize: 0x20, VirtualAddress: 0x8000, Size: 0x200, Offset: 0x1e00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".debug_pubnames", VirtualSize: 0x51, VirtualAddress: 0x9000, Size: 0x200, Offset: 0x2000, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".debug_pubtypes", VirtualSize: 0x91, VirtualAddress: 0xa000, Size: 0x200, Offset: 0x2200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".debug_info", VirtualSize: 0xe22, VirtualAddress: 0xb000, Size: 0x1000, Offset: 0x2400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".debug_abbrev", VirtualSize: 0x157, VirtualAddress: 0xc000, Size: 0x200, Offset: 0x3400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".debug_line", VirtualSize: 0x144, VirtualAddress: 0xd000, Size: 0x200, Offset: 0x3600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {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}, }, }, } diff --git a/src/pkg/deps.bash b/src/pkg/deps.bash deleted file mode 100755 index 2095ec1d8..000000000 --- a/src/pkg/deps.bash +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash -# 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. - -eval $(gomake --no-print-directory -f ../Make.inc go-env) - -OUT="Make.deps" -TMP="Make.deps.tmp" - -if [ -f $OUT ] && ! [ -w $OUT ]; then - echo "$0: $OUT is read-only; aborting." 1>&2 - exit 1 -fi - -# Get list of directories from Makefile -dirs=$(gomake --no-print-directory echo-dirs) -dirpat=$(echo $dirs C | awk '{ - for(i=1;i<=NF;i++){ - x=$i - gsub("/", "\\/", x) - printf("/^(%s)$/\n", x) - } -}') - -for dir in $dirs; do ( - cd $dir || exit 1 - - sources=$(sed -n 's/^[ ]*\([^ ]*\.go\)[ ]*\\*[ ]*$/\1/p' Makefile) - sources=$(echo $sources | sed 's/\$(GOOS)/'$GOOS'/g') - sources=$(echo $sources | sed 's/\$(GOARCH)/'$GOARCH'/g') - # /dev/null here means we get an empty dependency list if $sources is empty - # instead of listing every file in the directory. - sources=$(ls $sources /dev/null 2> /dev/null) # remove .s, .c, etc. - - deps=$( - sed -n '/^import.*"/p; /^import[ \t]*(/,/^)/p' $sources /dev/null | - cut -d '"' -f2 | - awk "$dirpat" | - grep -v "^$dir\$" | - sed 's/$/.install/' | - sed 's;^C\.install;runtime/cgo.install;' | - sort -u - ) - - echo $dir.install: $deps -) done > $TMP - -mv $TMP $OUT diff --git a/src/pkg/ebnf/Makefile b/src/pkg/ebnf/Makefile deleted file mode 100644 index f5555d272..000000000 --- a/src/pkg/ebnf/Makefile +++ /dev/null @@ -1,12 +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 ../../Make.inc - -TARG=ebnf -GOFILES=\ - ebnf.go\ - parser.go\ - -include ../../Make.pkg diff --git a/src/pkg/ebnf/ebnf.go b/src/pkg/ebnf/ebnf.go deleted file mode 100644 index 69da11767..000000000 --- a/src/pkg/ebnf/ebnf.go +++ /dev/null @@ -1,245 +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. - -// Package ebnf is a library for EBNF grammars. The input is text ([]byte) -// satisfying the following grammar (represented itself in EBNF): -// -// Production = name "=" [ Expression ] "." . -// Expression = Alternative { "|" Alternative } . -// Alternative = Term { Term } . -// Term = name | token [ "…" token ] | Group | Option | Repetition . -// Group = "(" Expression ")" . -// Option = "[" Expression "]" . -// Repetition = "{" Expression "}" . -// -// A name is a Go identifier, a token is a Go string, and comments -// and white space follow the same rules as for the Go language. -// Production names starting with an uppercase Unicode letter denote -// non-terminal productions (i.e., productions which allow white-space -// and comments between tokens); all other production names denote -// lexical productions. -// -package ebnf - -import ( - "go/scanner" - "go/token" - "os" - "unicode" - "utf8" -) - -// ---------------------------------------------------------------------------- -// Internal representation - -type ( - // An Expression node represents a production expression. - Expression interface { - // Pos is the position of the first character of the syntactic construct - Pos() token.Pos - } - - // An Alternative node represents a non-empty list of alternative expressions. - Alternative []Expression // x | y | z - - // A Sequence node represents a non-empty list of sequential expressions. - Sequence []Expression // x y z - - // A Name node represents a production name. - Name struct { - StringPos token.Pos - String string - } - - // A Token node represents a literal. - Token struct { - StringPos token.Pos - String string - } - - // A List node represents a range of characters. - Range struct { - Begin, End *Token // begin ... end - } - - // A Group node represents a grouped expression. - Group struct { - Lparen token.Pos - Body Expression // (body) - } - - // An Option node represents an optional expression. - Option struct { - Lbrack token.Pos - Body Expression // [body] - } - - // A Repetition node represents a repeated expression. - Repetition struct { - Lbrace token.Pos - Body Expression // {body} - } - - // A Bad node stands for pieces of source code that lead to a parse error. - Bad struct { - TokPos token.Pos - Error string // parser error message - } - - // A Production node represents an EBNF production. - Production struct { - Name *Name - Expr Expression - } - - // A Grammar is a set of EBNF productions. The map - // is indexed by production name. - // - Grammar map[string]*Production -) - -func (x Alternative) Pos() token.Pos { return x[0].Pos() } // the parser always generates non-empty Alternative -func (x Sequence) Pos() token.Pos { return x[0].Pos() } // the parser always generates non-empty Sequences -func (x *Name) Pos() token.Pos { return x.StringPos } -func (x *Token) Pos() token.Pos { return x.StringPos } -func (x *Range) Pos() token.Pos { return x.Begin.Pos() } -func (x *Group) Pos() token.Pos { return x.Lparen } -func (x *Option) Pos() token.Pos { return x.Lbrack } -func (x *Repetition) Pos() token.Pos { return x.Lbrace } -func (x *Bad) Pos() token.Pos { return x.TokPos } -func (x *Production) Pos() token.Pos { return x.Name.Pos() } - -// ---------------------------------------------------------------------------- -// Grammar verification - -func isLexical(name string) bool { - ch, _ := utf8.DecodeRuneInString(name) - return !unicode.IsUpper(ch) -} - -type verifier struct { - fset *token.FileSet - scanner.ErrorVector - worklist []*Production - reached Grammar // set of productions reached from (and including) the root production - grammar Grammar -} - -func (v *verifier) error(pos token.Pos, msg string) { - v.Error(v.fset.Position(pos), msg) -} - -func (v *verifier) push(prod *Production) { - name := prod.Name.String - if _, found := v.reached[name]; !found { - v.worklist = append(v.worklist, prod) - v.reached[name] = prod - } -} - -func (v *verifier) verifyChar(x *Token) int { - s := x.String - if utf8.RuneCountInString(s) != 1 { - v.error(x.Pos(), "single char expected, found "+s) - return 0 - } - ch, _ := utf8.DecodeRuneInString(s) - return ch -} - -func (v *verifier) verifyExpr(expr Expression, lexical bool) { - switch x := expr.(type) { - case nil: - // empty expression - case Alternative: - for _, e := range x { - v.verifyExpr(e, lexical) - } - case Sequence: - for _, e := range x { - v.verifyExpr(e, lexical) - } - case *Name: - // a production with this name must exist; - // add it to the worklist if not yet processed - if prod, found := v.grammar[x.String]; found { - v.push(prod) - } else { - v.error(x.Pos(), "missing production "+x.String) - } - // within a lexical production references - // to non-lexical productions are invalid - if lexical && !isLexical(x.String) { - v.error(x.Pos(), "reference to non-lexical production "+x.String) - } - case *Token: - // nothing to do for now - case *Range: - i := v.verifyChar(x.Begin) - j := v.verifyChar(x.End) - if i >= j { - v.error(x.Pos(), "decreasing character range") - } - case *Group: - v.verifyExpr(x.Body, lexical) - case *Option: - v.verifyExpr(x.Body, lexical) - case *Repetition: - v.verifyExpr(x.Body, lexical) - default: - panic("unreachable") - } -} - -func (v *verifier) verify(fset *token.FileSet, grammar Grammar, start string) { - // find root production - root, found := grammar[start] - if !found { - // token.NoPos doesn't require a file set; - // ok to set v.fset only afterwards - v.error(token.NoPos, "no start production "+start) - return - } - - // initialize verifier - v.fset = fset - v.ErrorVector.Reset() - v.worklist = v.worklist[0:0] - v.reached = make(Grammar) - v.grammar = grammar - - // work through the worklist - v.push(root) - for { - n := len(v.worklist) - 1 - if n < 0 { - break - } - prod := v.worklist[n] - v.worklist = v.worklist[0:n] - v.verifyExpr(prod.Expr, isLexical(prod.Name.String)) - } - - // check if all productions were reached - if len(v.reached) < len(v.grammar) { - for name, prod := range v.grammar { - if _, found := v.reached[name]; !found { - v.error(prod.Pos(), name+" is unreachable") - } - } - } -} - -// Verify checks that: -// - all productions used are defined -// - all productions defined are used when beginning at start -// - lexical productions refer only to other lexical productions -// -// Position information is interpreted relative to the file set fset. -// -func Verify(fset *token.FileSet, grammar Grammar, start string) os.Error { - var v verifier - v.verify(fset, grammar, start) - return v.GetError(scanner.Sorted) -} diff --git a/src/pkg/ebnf/ebnf_test.go b/src/pkg/ebnf/ebnf_test.go deleted file mode 100644 index b086facc3..000000000 --- a/src/pkg/ebnf/ebnf_test.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. - -package ebnf - -import ( - "go/token" - "io/ioutil" - "testing" -) - -var fset = token.NewFileSet() - -var goodGrammars = []string{ - `Program = .`, - - `Program = foo . - foo = "foo" .`, - - `Program = "a" | "b" "c" .`, - - `Program = "a" … "z" .`, - - `Program = Song . - Song = { Note } . - Note = Do | (Re | Mi | Fa | So | La) | Ti . - Do = "c" . - Re = "d" . - Mi = "e" . - Fa = "f" . - So = "g" . - La = "a" . - Ti = ti . - ti = "b" .`, -} - -var badGrammars = []string{ - `Program = | .`, - `Program = | b .`, - `Program = a … b .`, - `Program = "a" … .`, - `Program = … "b" .`, - `Program = () .`, - `Program = [] .`, - `Program = {} .`, -} - -func checkGood(t *testing.T, filename string, src []byte) { - grammar, err := Parse(fset, filename, src) - if err != nil { - t.Errorf("Parse(%s) failed: %v", src, err) - } - if err = Verify(fset, grammar, "Program"); err != nil { - t.Errorf("Verify(%s) failed: %v", src, err) - } -} - -func checkBad(t *testing.T, filename string, src []byte) { - _, err := Parse(fset, filename, src) - if err == nil { - t.Errorf("Parse(%s) should have failed", src) - } -} - -func TestGrammars(t *testing.T) { - for _, src := range goodGrammars { - checkGood(t, "", []byte(src)) - } - for _, src := range badGrammars { - checkBad(t, "", []byte(src)) - } -} - -var files = []string{ -// TODO(gri) add some test files -} - -func TestFiles(t *testing.T) { - for _, filename := range files { - src, err := ioutil.ReadFile(filename) - if err != nil { - t.Fatal(err) - } - checkGood(t, filename, src) - } -} diff --git a/src/pkg/ebnf/parser.go b/src/pkg/ebnf/parser.go deleted file mode 100644 index ef2fac000..000000000 --- a/src/pkg/ebnf/parser.go +++ /dev/null @@ -1,197 +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. - -package ebnf - -import ( - "go/scanner" - "go/token" - "os" - "strconv" -) - -type parser struct { - fset *token.FileSet - scanner.ErrorVector - scanner scanner.Scanner - pos token.Pos // token position - tok token.Token // one token look-ahead - lit string // token literal -} - -func (p *parser) next() { - p.pos, p.tok, p.lit = p.scanner.Scan() - if p.tok.IsKeyword() { - // TODO Should keyword mapping always happen outside scanner? - // Or should there be a flag to scanner to enable keyword mapping? - p.tok = token.IDENT - } -} - -func (p *parser) error(pos token.Pos, msg string) { - p.Error(p.fset.Position(pos), msg) -} - -func (p *parser) errorExpected(pos token.Pos, msg string) { - msg = "expected " + msg - if pos == p.pos { - // the error happened at the current position; - // make the error message more specific - msg += ", found '" + p.tok.String() + "'" - if p.tok.IsLiteral() { - msg += " " + p.lit - } - } - p.error(pos, msg) -} - -func (p *parser) expect(tok token.Token) token.Pos { - pos := p.pos - if p.tok != tok { - p.errorExpected(pos, "'"+tok.String()+"'") - } - p.next() // make progress in any case - return pos -} - -func (p *parser) parseIdentifier() *Name { - pos := p.pos - name := p.lit - p.expect(token.IDENT) - return &Name{pos, name} -} - -func (p *parser) parseToken() *Token { - pos := p.pos - value := "" - if p.tok == token.STRING { - value, _ = strconv.Unquote(p.lit) - // Unquote may fail with an error, but only if the scanner found - // an illegal string in the first place. In this case the error - // has already been reported. - p.next() - } else { - p.expect(token.STRING) - } - return &Token{pos, value} -} - -// ParseTerm returns nil if no term was found. -func (p *parser) parseTerm() (x Expression) { - pos := p.pos - - switch p.tok { - case token.IDENT: - x = p.parseIdentifier() - - case token.STRING: - tok := p.parseToken() - x = tok - const ellipsis = "…" // U+2026, the horizontal ellipsis character - if p.tok == token.ILLEGAL && p.lit == ellipsis { - p.next() - x = &Range{tok, p.parseToken()} - } - - case token.LPAREN: - p.next() - x = &Group{pos, p.parseExpression()} - p.expect(token.RPAREN) - - case token.LBRACK: - p.next() - x = &Option{pos, p.parseExpression()} - p.expect(token.RBRACK) - - case token.LBRACE: - p.next() - x = &Repetition{pos, p.parseExpression()} - p.expect(token.RBRACE) - } - - return x -} - -func (p *parser) parseSequence() Expression { - var list Sequence - - for x := p.parseTerm(); x != nil; x = p.parseTerm() { - list = append(list, x) - } - - // no need for a sequence if list.Len() < 2 - switch len(list) { - case 0: - p.errorExpected(p.pos, "term") - return &Bad{p.pos, "term expected"} - case 1: - return list[0] - } - - return list -} - -func (p *parser) parseExpression() Expression { - var list Alternative - - for { - list = append(list, p.parseSequence()) - if p.tok != token.OR { - break - } - p.next() - } - // len(list) > 0 - - // no need for an Alternative node if list.Len() < 2 - if len(list) == 1 { - return list[0] - } - - return list -} - -func (p *parser) parseProduction() *Production { - name := p.parseIdentifier() - p.expect(token.ASSIGN) - var expr Expression - if p.tok != token.PERIOD { - expr = p.parseExpression() - } - p.expect(token.PERIOD) - return &Production{name, expr} -} - -func (p *parser) parse(fset *token.FileSet, filename string, src []byte) Grammar { - // initialize parser - p.fset = fset - p.ErrorVector.Reset() - p.scanner.Init(fset.AddFile(filename, fset.Base(), len(src)), src, p, scanner.AllowIllegalChars) - p.next() // initializes pos, tok, lit - - grammar := make(Grammar) - for p.tok != token.EOF { - prod := p.parseProduction() - name := prod.Name.String - if _, found := grammar[name]; !found { - grammar[name] = prod - } else { - p.error(prod.Pos(), name+" declared already") - } - } - - return grammar -} - -// Parse parses a set of EBNF productions from source src. -// It returns a set of productions. Errors are reported -// for incorrect syntax and if a production is declared -// more than once. Position information is recorded relative -// to the file set fset. -// -func Parse(fset *token.FileSet, filename string, src []byte) (Grammar, os.Error) { - var p parser - grammar := p.parse(fset, filename, src) - return grammar, p.GetError(scanner.Sorted) -} diff --git a/src/pkg/encoding/ascii85/Makefile b/src/pkg/encoding/ascii85/Makefile deleted file mode 100644 index 412383efd..000000000 --- a/src/pkg/encoding/ascii85/Makefile +++ /dev/null @@ -1,11 +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 ../../../Make.inc - -TARG=encoding/ascii85 -GOFILES=\ - ascii85.go\ - -include ../../../Make.pkg diff --git a/src/pkg/encoding/ascii85/ascii85.go b/src/pkg/encoding/ascii85/ascii85.go index ead0c2475..7d004b5e5 100644 --- a/src/pkg/encoding/ascii85/ascii85.go +++ b/src/pkg/encoding/ascii85/ascii85.go @@ -8,7 +8,6 @@ package ascii85 import ( "io" - "os" "strconv" ) @@ -93,14 +92,14 @@ func MaxEncodedLen(n int) int { return (n + 3) / 4 * 5 } func NewEncoder(w io.Writer) io.WriteCloser { return &encoder{w: w} } type encoder struct { - err os.Error + err error w io.Writer buf [4]byte // buffered data waiting to be encoded nbuf int // number of bytes in buf out [1024]byte // output buffer } -func (e *encoder) Write(p []byte) (n int, err os.Error) { +func (e *encoder) Write(p []byte) (n int, err error) { if e.err != nil { return 0, e.err } @@ -152,7 +151,7 @@ func (e *encoder) Write(p []byte) (n int, err os.Error) { // Close flushes any pending output from the encoder. // It is an error to call Write after calling Close. -func (e *encoder) Close() os.Error { +func (e *encoder) Close() error { // If there's anything left in the buffer, flush it out if e.err == nil && e.nbuf > 0 { nout := Encode(e.out[0:], e.buf[0:e.nbuf]) @@ -168,8 +167,8 @@ func (e *encoder) Close() os.Error { type CorruptInputError int64 -func (e CorruptInputError) String() string { - return "illegal ascii85 data at input byte " + strconv.Itoa64(int64(e)) +func (e CorruptInputError) Error() string { + return "illegal ascii85 data at input byte " + strconv.FormatInt(int64(e), 10) } // Decode decodes src into dst, returning both the number @@ -186,7 +185,7 @@ func (e CorruptInputError) String() string { // // NewDecoder wraps an io.Reader interface around Decode. // -func Decode(dst, src []byte, flush bool) (ndst, nsrc int, err os.Error) { +func Decode(dst, src []byte, flush bool) (ndst, nsrc int, err error) { var v uint32 var nb int for i, b := range src { @@ -246,8 +245,8 @@ func Decode(dst, src []byte, flush bool) (ndst, nsrc int, err os.Error) { func NewDecoder(r io.Reader) io.Reader { return &decoder{r: r} } type decoder struct { - err os.Error - readErr os.Error + err error + readErr error r io.Reader end bool // saw end of message buf [1024]byte // leftover input @@ -256,7 +255,7 @@ type decoder struct { outbuf [1024]byte } -func (d *decoder) Read(p []byte) (n int, err os.Error) { +func (d *decoder) Read(p []byte) (n int, err error) { if len(p) == 0 { return 0, nil } diff --git a/src/pkg/encoding/ascii85/ascii85_test.go b/src/pkg/encoding/ascii85/ascii85_test.go index fdfeb889f..70e67d8b0 100644 --- a/src/pkg/encoding/ascii85/ascii85_test.go +++ b/src/pkg/encoding/ascii85/ascii85_test.go @@ -6,8 +6,8 @@ package ascii85 import ( "bytes" + "io" "io/ioutil" - "os" "testing" ) @@ -83,11 +83,11 @@ func TestEncoderBuffering(t *testing.T) { end = len(input) } n, err := encoder.Write(input[pos:end]) - testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, os.Error(nil)) + testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, error(nil)) testEqual(t, "Write(%q) gave length %v, want %v", input[pos:end], n, end-pos) } err := encoder.Close() - testEqual(t, "Close gave error %v, want %v", err, os.Error(nil)) + testEqual(t, "Close gave error %v, want %v", err, error(nil)) testEqual(t, "Encoding/%d of %q = %q, want %q", bs, bigtest.decoded, strip85(bb.String()), strip85(bigtest.encoded)) } } @@ -96,7 +96,7 @@ func TestDecode(t *testing.T) { for _, p := range pairs { dbuf := make([]byte, 4*len(p.encoded)) ndst, nsrc, err := Decode(dbuf, []byte(p.encoded), true) - testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, os.Error(nil)) + testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, error(nil)) testEqual(t, "Decode(%q) = nsrc %v, want %v", p.encoded, nsrc, len(p.encoded)) testEqual(t, "Decode(%q) = ndst %v, want %v", p.encoded, ndst, len(p.decoded)) testEqual(t, "Decode(%q) = %q, want %q", p.encoded, string(dbuf[0:ndst]), p.decoded) @@ -113,7 +113,7 @@ func TestDecoder(t *testing.T) { testEqual(t, "Read from %q = length %v, want %v", p.encoded, len(dbuf), len(p.decoded)) testEqual(t, "Decoding of %q = %q, want %q", p.encoded, string(dbuf), p.decoded) if err != nil { - testEqual(t, "Read from %q = %v, want %v", p.encoded, err, os.EOF) + testEqual(t, "Read from %q = %v, want %v", p.encoded, err, io.EOF) } } } @@ -125,7 +125,7 @@ func TestDecoderBuffering(t *testing.T) { var total int for total = 0; total < len(bigtest.decoded); { n, err := decoder.Read(buf[total : total+bs]) - testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, os.Error(nil)) + testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, error(nil)) total += n } testEqual(t, "Decoding/%d of %q = %q, want %q", bs, bigtest.encoded, string(buf[0:total]), bigtest.decoded) diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go new file mode 100644 index 000000000..3bf81a68c --- /dev/null +++ b/src/pkg/encoding/asn1/asn1.go @@ -0,0 +1,846 @@ +// 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 asn1 implements parsing of DER-encoded ASN.1 data structures, +// as defined in ITU-T Rec X.690. +// +// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' +// http://luca.ntop.org/Teaching/Appunti/asn1.html. +package asn1 + +// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc +// are different encoding formats for those objects. Here, we'll be dealing +// with DER, the Distinguished Encoding Rules. DER is used in X.509 because +// it's fast to parse and, unlike BER, has a unique encoding for every object. +// When calculating hashes over objects, it's important that the resulting +// bytes be the same at both ends and DER removes this margin of error. +// +// ASN.1 is very complex and this package doesn't attempt to implement +// everything by any means. + +import ( + "fmt" + "math/big" + "reflect" + "time" +) + +// A StructuralError suggests that the ASN.1 data is valid, but the Go type +// which is receiving it doesn't match. +type StructuralError struct { + Msg string +} + +func (e StructuralError) Error() string { return "ASN.1 structure error: " + e.Msg } + +// A SyntaxError suggests that the ASN.1 data is invalid. +type SyntaxError struct { + Msg string +} + +func (e SyntaxError) Error() string { return "ASN.1 syntax error: " + e.Msg } + +// We start by dealing with each of the primitive types in turn. + +// BOOLEAN + +func parseBool(bytes []byte) (ret bool, err error) { + if len(bytes) != 1 { + err = SyntaxError{"invalid boolean"} + return + } + + return bytes[0] != 0, nil +} + +// INTEGER + +// parseInt64 treats the given bytes as a big-endian, signed integer and +// returns the result. +func parseInt64(bytes []byte) (ret int64, err error) { + if len(bytes) > 8 { + // We'll overflow an int64 in this case. + err = StructuralError{"integer too large"} + return + } + for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { + ret <<= 8 + ret |= int64(bytes[bytesRead]) + } + + // Shift up and down in order to sign extend the result. + ret <<= 64 - uint8(len(bytes))*8 + ret >>= 64 - uint8(len(bytes))*8 + return +} + +// parseInt treats the given bytes as a big-endian, signed integer and returns +// the result. +func parseInt(bytes []byte) (int, error) { + ret64, err := parseInt64(bytes) + if err != nil { + return 0, err + } + if ret64 != int64(int(ret64)) { + return 0, StructuralError{"integer too large"} + } + return int(ret64), nil +} + +var bigOne = big.NewInt(1) + +// parseBigInt treats the given bytes as a big-endian, signed integer and returns +// the result. +func parseBigInt(bytes []byte) *big.Int { + ret := new(big.Int) + if len(bytes) > 0 && bytes[0]&0x80 == 0x80 { + // This is a negative number. + notBytes := make([]byte, len(bytes)) + for i := range notBytes { + notBytes[i] = ^bytes[i] + } + ret.SetBytes(notBytes) + ret.Add(ret, bigOne) + ret.Neg(ret) + return ret + } + ret.SetBytes(bytes) + return ret +} + +// BIT STRING + +// BitString is the structure to use when you want an ASN.1 BIT STRING type. A +// bit string is padded up to the nearest byte in memory and the number of +// valid bits is recorded. Padding bits will be zero. +type BitString struct { + Bytes []byte // bits packed into bytes. + BitLength int // length in bits. +} + +// At returns the bit at the given index. If the index is out of range it +// returns false. +func (b BitString) At(i int) int { + if i < 0 || i >= b.BitLength { + return 0 + } + x := i / 8 + y := 7 - uint(i%8) + return int(b.Bytes[x]>>y) & 1 +} + +// RightAlign returns a slice where the padding bits are at the beginning. The +// slice may share memory with the BitString. +func (b BitString) RightAlign() []byte { + shift := uint(8 - (b.BitLength % 8)) + if shift == 8 || len(b.Bytes) == 0 { + return b.Bytes + } + + a := make([]byte, len(b.Bytes)) + a[0] = b.Bytes[0] >> shift + for i := 1; i < len(b.Bytes); i++ { + a[i] = b.Bytes[i-1] << (8 - shift) + a[i] |= b.Bytes[i] >> shift + } + + return a +} + +// parseBitString parses an ASN.1 bit string from the given byte slice and returns it. +func parseBitString(bytes []byte) (ret BitString, err error) { + if len(bytes) == 0 { + err = SyntaxError{"zero length BIT STRING"} + return + } + paddingBits := int(bytes[0]) + if paddingBits > 7 || + len(bytes) == 1 && paddingBits > 0 || + bytes[len(bytes)-1]&((1< 4 { + err = StructuralError{"base 128 integer too large"} + return + } + ret <<= 7 + b := bytes[offset] + ret |= int(b & 0x7f) + offset++ + if b&0x80 == 0 { + return + } + } + err = SyntaxError{"truncated base 128 integer"} + return +} + +// UTCTime + +func parseUTCTime(bytes []byte) (ret time.Time, err error) { + s := string(bytes) + ret, err = time.Parse("0601021504Z0700", s) + if err != nil { + ret, err = time.Parse("060102150405Z0700", s) + } + if err == nil && ret.Year() >= 2050 { + // UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 + ret = ret.AddDate(-100, 0, 0) + } + + return +} + +// parseGeneralizedTime parses the GeneralizedTime from the given byte slice +// and returns the resulting time. +func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) { + return time.Parse("20060102150405Z0700", string(bytes)) +} + +// PrintableString + +// parsePrintableString parses a ASN.1 PrintableString from the given byte +// array and returns it. +func parsePrintableString(bytes []byte) (ret string, err error) { + for _, b := range bytes { + if !isPrintable(b) { + err = SyntaxError{"PrintableString contains invalid character"} + return + } + } + ret = string(bytes) + return +} + +// isPrintable returns true iff the given b is in the ASN.1 PrintableString set. +func isPrintable(b byte) bool { + return 'a' <= b && b <= 'z' || + 'A' <= b && b <= 'Z' || + '0' <= b && b <= '9' || + '\'' <= b && b <= ')' || + '+' <= b && b <= '/' || + b == ' ' || + b == ':' || + b == '=' || + b == '?' || + // This is technically not allowed in a PrintableString. + // However, x509 certificates with wildcard strings don't + // always use the correct string type so we permit it. + b == '*' +} + +// IA5String + +// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given +// byte slice and returns it. +func parseIA5String(bytes []byte) (ret string, err error) { + for _, b := range bytes { + if b >= 0x80 { + err = SyntaxError{"IA5String contains invalid character"} + return + } + } + ret = string(bytes) + return +} + +// T61String + +// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given +// byte slice and returns it. +func parseT61String(bytes []byte) (ret string, err error) { + return string(bytes), nil +} + +// UTF8String + +// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte +// array and returns it. +func parseUTF8String(bytes []byte) (ret string, err error) { + return string(bytes), nil +} + +// A RawValue represents an undecoded ASN.1 object. +type RawValue struct { + Class, Tag int + IsCompound bool + Bytes []byte + FullBytes []byte // includes the tag and length +} + +// RawContent is used to signal that the undecoded, DER data needs to be +// preserved for a struct. To use it, the first field of the struct must have +// this type. It's an error for any of the other fields to have this type. +type RawContent []byte + +// Tagging + +// parseTagAndLength parses an ASN.1 tag and length pair from the given offset +// into a byte slice. It returns the parsed data and the new offset. SET and +// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we +// don't distinguish between ordered and unordered objects in this code. +func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err error) { + offset = initOffset + b := bytes[offset] + offset++ + ret.class = int(b >> 6) + ret.isCompound = b&0x20 == 0x20 + ret.tag = int(b & 0x1f) + + // If the bottom five bits are set, then the tag number is actually base 128 + // encoded afterwards + if ret.tag == 0x1f { + ret.tag, offset, err = parseBase128Int(bytes, offset) + if err != nil { + return + } + } + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + if b&0x80 == 0 { + // The length is encoded in the bottom 7 bits. + ret.length = int(b & 0x7f) + } else { + // Bottom 7 bits give the number of length bytes to follow. + numBytes := int(b & 0x7f) + // We risk overflowing a signed 32-bit number if we accept more than 3 bytes. + if numBytes > 3 { + err = StructuralError{"length too large"} + return + } + if numBytes == 0 { + err = SyntaxError{"indefinite length found (not DER)"} + return + } + ret.length = 0 + for i := 0; i < numBytes; i++ { + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + ret.length <<= 8 + ret.length |= int(b) + } + } + + return +} + +// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse +// a number of ASN.1 values from the given byte slice and returns them as a +// slice of Go values of the given type. +func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err error) { + expectedTag, compoundType, ok := getUniversalType(elemType) + if !ok { + err = StructuralError{"unknown Go type for slice"} + return + } + + // First we iterate over the input and count the number of elements, + // checking that the types are correct in each case. + numElements := 0 + for offset := 0; offset < len(bytes); { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so + // that a sequence of them can be parsed into a []string. + if t.tag == tagGeneralString { + t.tag = tagPrintableString + } + if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag { + err = StructuralError{"sequence tag mismatch"} + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"truncated sequence"} + return + } + offset += t.length + numElements++ + } + ret = reflect.MakeSlice(sliceType, numElements, numElements) + params := fieldParameters{} + offset := 0 + for i := 0; i < numElements; i++ { + offset, err = parseField(ret.Index(i), bytes, offset, params) + if err != nil { + return + } + } + return +} + +var ( + bitStringType = reflect.TypeOf(BitString{}) + objectIdentifierType = reflect.TypeOf(ObjectIdentifier{}) + enumeratedType = reflect.TypeOf(Enumerated(0)) + flagType = reflect.TypeOf(Flag(false)) + timeType = reflect.TypeOf(time.Time{}) + rawValueType = reflect.TypeOf(RawValue{}) + rawContentsType = reflect.TypeOf(RawContent(nil)) + bigIntType = reflect.TypeOf(new(big.Int)) +) + +// invalidLength returns true iff offset + length > sliceLength, or if the +// addition would overflow. +func invalidLength(offset, length, sliceLength int) bool { + return offset+length < offset || offset+length > sliceLength +} + +// parseField is the main parsing function. Given a byte slice and an offset +// into the array, it will try to parse a suitable ASN.1 value out and store it +// in the given Value. +func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err error) { + offset = initOffset + fieldType := v.Type() + + // If we have run out of data, it may be that there are optional elements at the end. + if offset == len(bytes) { + if !setDefaultValue(v, params) { + err = SyntaxError{"sequence truncated"} + } + return + } + + // Deal with raw values. + if fieldType == rawValueType { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} + offset += t.length + v.Set(reflect.ValueOf(result)) + return + } + + // Deal with the ANY type. + if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + var result interface{} + if !t.isCompound && t.class == classUniversal { + innerBytes := bytes[offset : offset+t.length] + switch t.tag { + case tagPrintableString: + result, err = parsePrintableString(innerBytes) + case tagIA5String: + result, err = parseIA5String(innerBytes) + case tagT61String: + result, err = parseT61String(innerBytes) + case tagUTF8String: + result, err = parseUTF8String(innerBytes) + case tagInteger: + result, err = parseInt64(innerBytes) + case tagBitString: + result, err = parseBitString(innerBytes) + case tagOID: + result, err = parseObjectIdentifier(innerBytes) + case tagUTCTime: + result, err = parseUTCTime(innerBytes) + case tagOctetString: + result = innerBytes + default: + // If we don't know how to handle the type, we just leave Value as nil. + } + } + offset += t.length + if err != nil { + return + } + if result != nil { + v.Set(reflect.ValueOf(result)) + } + return + } + universalTag, compoundType, ok1 := getUniversalType(fieldType) + if !ok1 { + err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)} + return + } + + t, offset, err := parseTagAndLength(bytes, offset) + if err != nil { + return + } + if params.explicit { + expectedClass := classContextSpecific + if params.application { + expectedClass = classApplication + } + if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) { + if t.length > 0 { + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + } else { + if fieldType != flagType { + err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} + return + } + v.SetBool(true) + return + } + } else { + // The tags didn't match, it might be an optional element. + ok := setDefaultValue(v, params) + if ok { + offset = initOffset + } else { + err = StructuralError{"explicitly tagged member didn't match"} + } + return + } + } + + // Special case for strings: all the ASN.1 string types map to the Go + // type string. getUniversalType returns the tag for PrintableString + // when it sees a string, so if we see a different string type on the + // wire, we change the universal type to match. + if universalTag == tagPrintableString { + switch t.tag { + case tagIA5String, tagGeneralString, tagT61String, tagUTF8String: + universalTag = t.tag + } + } + + // Special case for time: UTCTime and GeneralizedTime both map to the + // Go type time.Time. + if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { + universalTag = tagGeneralizedTime + } + + expectedClass := classUniversal + expectedTag := universalTag + + if !params.explicit && params.tag != nil { + expectedClass = classContextSpecific + expectedTag = *params.tag + } + + if !params.explicit && params.application && params.tag != nil { + expectedClass = classApplication + expectedTag = *params.tag + } + + // We have unwrapped any explicit tagging at this point. + if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType { + // Tags don't match. Again, it could be an optional element. + ok := setDefaultValue(v, params) + if ok { + offset = initOffset + } else { + err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)} + } + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + innerBytes := bytes[offset : offset+t.length] + offset += t.length + + // We deal with the structures defined in this package first. + switch fieldType { + case objectIdentifierType: + newSlice, err1 := parseObjectIdentifier(innerBytes) + v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice))) + if err1 == nil { + reflect.Copy(v, reflect.ValueOf(newSlice)) + } + err = err1 + return + case bitStringType: + bs, err1 := parseBitString(innerBytes) + if err1 == nil { + v.Set(reflect.ValueOf(bs)) + } + err = err1 + return + case timeType: + var time time.Time + var err1 error + if universalTag == tagUTCTime { + time, err1 = parseUTCTime(innerBytes) + } else { + time, err1 = parseGeneralizedTime(innerBytes) + } + if err1 == nil { + v.Set(reflect.ValueOf(time)) + } + err = err1 + return + case enumeratedType: + parsedInt, err1 := parseInt(innerBytes) + if err1 == nil { + v.SetInt(int64(parsedInt)) + } + err = err1 + return + case flagType: + v.SetBool(true) + return + case bigIntType: + parsedInt := parseBigInt(innerBytes) + v.Set(reflect.ValueOf(parsedInt)) + return + } + switch val := v; val.Kind() { + case reflect.Bool: + parsedBool, err1 := parseBool(innerBytes) + if err1 == nil { + val.SetBool(parsedBool) + } + 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) + } + err = err1 + return + // TODO(dfc) Add support for the remaining integer types + case reflect.Struct: + structType := fieldType + + if structType.NumField() > 0 && + structType.Field(0).Type == rawContentsType { + bytes := bytes[initOffset:offset] + val.Field(0).Set(reflect.ValueOf(RawContent(bytes))) + } + + innerOffset := 0 + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if i == 0 && field.Type == rawContentsType { + continue + } + innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1"))) + if err != nil { + return + } + } + // We allow extra bytes at the end of the SEQUENCE because + // adding elements to the end has been used in X.509 as the + // version numbers have increased. + return + case reflect.Slice: + sliceType := fieldType + if sliceType.Elem().Kind() == reflect.Uint8 { + val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes))) + reflect.Copy(val, reflect.ValueOf(innerBytes)) + return + } + newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) + if err1 == nil { + val.Set(newSlice) + } + err = err1 + return + case reflect.String: + var v string + switch universalTag { + case tagPrintableString: + v, err = parsePrintableString(innerBytes) + case tagIA5String: + v, err = parseIA5String(innerBytes) + case tagT61String: + v, err = parseT61String(innerBytes) + case tagUTF8String: + v, err = parseUTF8String(innerBytes) + case tagGeneralString: + // GeneralString is specified in ISO-2022/ECMA-35, + // A brief review suggests that it includes structures + // that allow the encoding to change midstring and + // such. We give up and pass it as an 8-bit string. + v, err = parseT61String(innerBytes) + default: + err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} + } + if err == nil { + val.SetString(v) + } + return + } + err = StructuralError{"unsupported: " + v.Type().String()} + return +} + +// setDefaultValue is used to install a default value, from a tag string, into +// a Value. It is successful is the field was optional, even if a default value +// wasn't provided or it failed to install it into the Value. +func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { + if !params.optional { + return + } + ok = true + if params.defaultValue == nil { + return + } + switch val := v; val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + val.SetInt(*params.defaultValue) + } + return +} + +// Unmarshal parses the DER-encoded ASN.1 data structure b +// and uses the reflect package to fill in an arbitrary value pointed at by val. +// Because Unmarshal uses the reflect package, the structs +// being written to must use upper case field names. +// +// An ASN.1 INTEGER can be written to an int, int32, int64, +// or *big.Int (from the math/big package). +// If the encoded value does not fit in the Go type, +// Unmarshal returns a parse error. +// +// An ASN.1 BIT STRING can be written to a BitString. +// +// An ASN.1 OCTET STRING can be written to a []byte. +// +// An ASN.1 OBJECT IDENTIFIER can be written to an +// ObjectIdentifier. +// +// An ASN.1 ENUMERATED can be written to an Enumerated. +// +// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a time.Time. +// +// An ASN.1 PrintableString or IA5String can be written to a string. +// +// Any of the above ASN.1 values can be written to an interface{}. +// The value stored in the interface has the corresponding Go type. +// For integers, that type is int64. +// +// An ASN.1 SEQUENCE OF x or SET OF x can be written +// to a slice if an x can be written to the slice's element type. +// +// An ASN.1 SEQUENCE or SET can be written to a struct +// if each of the elements in the sequence can be +// written to the corresponding element in the struct. +// +// The following tags on struct fields have special meaning to Unmarshal: +// +// optional marks the field as ASN.1 OPTIONAL +// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC +// default:x sets the default value for optional integer fields +// +// If the type of the first field of a structure is RawContent then the raw +// ASN1 contents of the struct will be stored in it. +// +// Other ASN.1 types are not supported; if it encounters them, +// Unmarshal returns a parse error. +func Unmarshal(b []byte, val interface{}) (rest []byte, err error) { + return UnmarshalWithParams(b, val, "") +} + +// UnmarshalWithParams allows field parameters to be specified for the +// top-level element. The form of the params is the same as the field tags. +func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err error) { + v := reflect.ValueOf(val).Elem() + offset, err := parseField(v, b, 0, parseFieldParameters(params)) + if err != nil { + return nil, err + } + return b[offset:], nil +} diff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go new file mode 100644 index 000000000..93803f435 --- /dev/null +++ b/src/pkg/encoding/asn1/asn1_test.go @@ -0,0 +1,706 @@ +// 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 asn1 + +import ( + "bytes" + "math/big" + "reflect" + "testing" + "time" +) + +type int64Test struct { + in []byte + ok bool + out int64 +} + +var int64TestData = []int64Test{ + {[]byte{0x00}, true, 0}, + {[]byte{0x7f}, true, 127}, + {[]byte{0x00, 0x80}, true, 128}, + {[]byte{0x01, 0x00}, true, 256}, + {[]byte{0x80}, true, -128}, + {[]byte{0xff, 0x7f}, true, -129}, + {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, true, -1}, + {[]byte{0xff}, true, -1}, + {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, true, -9223372036854775808}, + {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, false, 0}, +} + +func TestParseInt64(t *testing.T) { + for i, test := range int64TestData { + ret, err := parseInt64(test.in) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if test.ok && ret != test.out { + t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + } + } +} + +type int32Test struct { + in []byte + ok bool + out int32 +} + +var int32TestData = []int32Test{ + {[]byte{0x00}, true, 0}, + {[]byte{0x7f}, true, 127}, + {[]byte{0x00, 0x80}, true, 128}, + {[]byte{0x01, 0x00}, true, 256}, + {[]byte{0x80}, true, -128}, + {[]byte{0xff, 0x7f}, true, -129}, + {[]byte{0xff, 0xff, 0xff, 0xff}, true, -1}, + {[]byte{0xff}, true, -1}, + {[]byte{0x80, 0x00, 0x00, 0x00}, true, -2147483648}, + {[]byte{0x80, 0x00, 0x00, 0x00, 0x00}, false, 0}, +} + +func TestParseInt32(t *testing.T) { + for i, test := range int32TestData { + ret, err := parseInt(test.in) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if test.ok && int32(ret) != test.out { + t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + } + } +} + +var bigIntTests = []struct { + in []byte + base10 string +}{ + {[]byte{0xff}, "-1"}, + {[]byte{0x00}, "0"}, + {[]byte{0x01}, "1"}, + {[]byte{0x00, 0xff}, "255"}, + {[]byte{0xff, 0x00}, "-256"}, + {[]byte{0x01, 0x00}, "256"}, +} + +func TestParseBigInt(t *testing.T) { + for i, test := range bigIntTests { + ret := parseBigInt(test.in) + if ret.String() != test.base10 { + t.Errorf("#%d: bad result from %x, got %s want %s", i, test.in, ret.String(), test.base10) + } + fw := newForkableWriter() + marshalBigInt(fw, ret) + result := fw.Bytes() + if !bytes.Equal(result, test.in) { + t.Errorf("#%d: got %x from marshaling %s, want %x", i, result, ret, test.in) + } + } +} + +type bitStringTest struct { + in []byte + ok bool + out []byte + bitLength int +} + +var bitStringTestData = []bitStringTest{ + {[]byte{}, false, []byte{}, 0}, + {[]byte{0x00}, true, []byte{}, 0}, + {[]byte{0x07, 0x00}, true, []byte{0x00}, 1}, + {[]byte{0x07, 0x01}, false, []byte{}, 0}, + {[]byte{0x07, 0x40}, false, []byte{}, 0}, + {[]byte{0x08, 0x00}, false, []byte{}, 0}, +} + +func TestBitString(t *testing.T) { + for i, test := range bitStringTestData { + ret, err := parseBitString(test.in) + if (err == nil) != test.ok { + 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 { + t.Errorf("#%d: Bad result: %v (expected %v %v)", i, ret, test.out, test.bitLength) + } + } + } +} + +func TestBitStringAt(t *testing.T) { + bs := BitString{[]byte{0x82, 0x40}, 16} + if bs.At(0) != 1 { + t.Error("#1: Failed") + } + if bs.At(1) != 0 { + t.Error("#2: Failed") + } + if bs.At(6) != 1 { + t.Error("#3: Failed") + } + if bs.At(9) != 1 { + t.Error("#4: Failed") + } +} + +type bitStringRightAlignTest struct { + in []byte + inlen int + out []byte +} + +var bitStringRightAlignTests = []bitStringRightAlignTest{ + {[]byte{0x80}, 1, []byte{0x01}}, + {[]byte{0x80, 0x80}, 9, []byte{0x01, 0x01}}, + {[]byte{}, 0, []byte{}}, + {[]byte{0xce}, 8, []byte{0xce}}, + {[]byte{0xce, 0x47}, 16, []byte{0xce, 0x47}}, + {[]byte{0x34, 0x50}, 12, []byte{0x03, 0x45}}, +} + +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 { + t.Errorf("#%d got: %x want: %x", i, out, test.out) + } + } +} + +type objectIdentifierTest struct { + in []byte + ok bool + out []int +} + +var objectIdentifierTestData = []objectIdentifierTest{ + {[]byte{}, false, []int{}}, + {[]byte{85}, true, []int{2, 5}}, + {[]byte{85, 0x02}, true, []int{2, 5, 2}}, + {[]byte{85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}}, + {[]byte{85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}}, +} + +func TestObjectIdentifier(t *testing.T) { + for i, test := range objectIdentifierTestData { + ret, err := parseObjectIdentifier(test.in) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if err == nil { + if !reflect.DeepEqual(test.out, ret) { + t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + } + } + } +} + +type timeTest struct { + in string + ok bool + out time.Time +} + +var utcTestData = []timeTest{ + {"910506164540-0700", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", -7*60*60))}, + {"910506164540+0730", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", 7*60*60+30*60))}, + {"910506234540Z", true, time.Date(1991, 05, 06, 23, 45, 40, 0, time.UTC)}, + {"9105062345Z", true, time.Date(1991, 05, 06, 23, 45, 0, 0, time.UTC)}, + {"a10506234540Z", false, time.Time{}}, + {"91a506234540Z", false, time.Time{}}, + {"9105a6234540Z", false, time.Time{}}, + {"910506a34540Z", false, time.Time{}}, + {"910506334a40Z", false, time.Time{}}, + {"91050633444aZ", false, time.Time{}}, + {"910506334461Z", false, time.Time{}}, + {"910506334400Za", false, time.Time{}}, +} + +func TestUTCTime(t *testing.T) { + for i, test := range utcTestData { + ret, err := parseUTCTime([]byte(test.in)) + if err != nil { + if test.ok { + t.Errorf("#%d: parseUTCTime(%q) = error %v", i, test.in, err) + } + continue + } + if !test.ok { + t.Errorf("#%d: parseUTCTime(%q) succeeded, should have failed", i, test.in) + continue + } + const format = "Jan _2 15:04:05 -0700 2006" // ignore zone name, just offset + have := ret.Format(format) + want := test.out.Format(format) + if have != want { + t.Errorf("#%d: parseUTCTime(%q) = %s, want %s", i, test.in, have, want) + } + } +} + +var generalizedTimeTestData = []timeTest{ + {"20100102030405Z", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.UTC)}, + {"20100102030405", false, time.Time{}}, + {"20100102030405+0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", 6*60*60+7*60))}, + {"20100102030405-0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", -6*60*60-7*60))}, +} + +func TestGeneralizedTime(t *testing.T) { + for i, test := range generalizedTimeTestData { + ret, err := parseGeneralizedTime([]byte(test.in)) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if err == nil { + if !reflect.DeepEqual(test.out, ret) { + t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + } + } + } +} + +type tagAndLengthTest struct { + in []byte + ok bool + out tagAndLength +} + +var tagAndLengthData = []tagAndLengthTest{ + {[]byte{0x80, 0x01}, true, tagAndLength{2, 0, 1, false}}, + {[]byte{0xa0, 0x01}, true, tagAndLength{2, 0, 1, true}}, + {[]byte{0x02, 0x00}, true, tagAndLength{0, 2, 0, false}}, + {[]byte{0xfe, 0x00}, true, tagAndLength{3, 30, 0, true}}, + {[]byte{0x1f, 0x01, 0x00}, true, tagAndLength{0, 1, 0, false}}, + {[]byte{0x1f, 0x81, 0x00, 0x00}, true, tagAndLength{0, 128, 0, false}}, + {[]byte{0x1f, 0x81, 0x80, 0x01, 0x00}, true, tagAndLength{0, 0x4001, 0, false}}, + {[]byte{0x00, 0x81, 0x01}, true, tagAndLength{0, 0, 1, false}}, + {[]byte{0x00, 0x82, 0x01, 0x00}, true, tagAndLength{0, 0, 256, false}}, + {[]byte{0x00, 0x83, 0x01, 0x00}, false, tagAndLength{}}, + {[]byte{0x1f, 0x85}, false, tagAndLength{}}, + {[]byte{0x30, 0x80}, false, tagAndLength{}}, +} + +func TestParseTagAndLength(t *testing.T) { + for i, test := range tagAndLengthData { + tagAndLength, _, err := parseTagAndLength(test.in, 0) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did pass? %v, expected: %v)", i, err == nil, test.ok) + } + if err == nil && !reflect.DeepEqual(test.out, tagAndLength) { + t.Errorf("#%d: Bad result: %v (expected %v)", i, tagAndLength, test.out) + } + } +} + +type parseFieldParametersTest struct { + in string + out fieldParameters +} + +func newInt(n int) *int { return &n } + +func newInt64(n int64) *int64 { return &n } + +func newString(s string) *string { return &s } + +func newBool(b bool) *bool { return &b } + +var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParametersTest{ + {"", fieldParameters{}}, + {"ia5", fieldParameters{stringType: tagIA5String}}, + {"printable", fieldParameters{stringType: tagPrintableString}}, + {"optional", fieldParameters{optional: true}}, + {"explicit", fieldParameters{explicit: true, tag: new(int)}}, + {"application", fieldParameters{application: true, tag: new(int)}}, + {"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}}, + {"default:42", fieldParameters{defaultValue: newInt64(42)}}, + {"tag:17", fieldParameters{tag: newInt(17)}}, + {"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}}, + {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, false, false}}, + {"set", fieldParameters{set: true}}, +} + +func TestParseFieldParameters(t *testing.T) { + for i, test := range parseFieldParametersTestData { + f := parseFieldParameters(test.in) + if !reflect.DeepEqual(f, test.out) { + t.Errorf("#%d: Bad result: %v (expected %v)", i, f, test.out) + } + } +} + +type TestObjectIdentifierStruct struct { + OID ObjectIdentifier +} + +type TestContextSpecificTags struct { + A int `asn1:"tag:1"` +} + +type TestContextSpecificTags2 struct { + A int `asn1:"explicit,tag:1"` + B int +} + +type TestElementsAfterString struct { + S string + A, B int +} + +type TestBigInt struct { + X *big.Int +} + +var unmarshalTestData = []struct { + in []byte + out interface{} +}{ + {[]byte{0x02, 0x01, 0x42}, newInt(0x42)}, + {[]byte{0x30, 0x08, 0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d}, &TestObjectIdentifierStruct{[]int{1, 2, 840, 113549}}}, + {[]byte{0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0}, &BitString{[]byte{110, 93, 192}, 18}}, + {[]byte{0x30, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &[]int{1, 2, 3}}, + {[]byte{0x02, 0x01, 0x10}, newInt(16)}, + {[]byte{0x13, 0x04, 't', 'e', 's', 't'}, newString("test")}, + {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, newString("test")}, + {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, &RawValue{0, 22, false, []byte("test"), []byte("\x16\x04test")}}, + {[]byte{0x04, 0x04, 1, 2, 3, 4}, &RawValue{0, 4, false, []byte{1, 2, 3, 4}, []byte{4, 4, 1, 2, 3, 4}}}, + {[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}}, + {[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}}, + {[]byte{0x01, 0x01, 0x00}, newBool(false)}, + {[]byte{0x01, 0x01, 0x01}, newBool(true)}, + {[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}}, + {[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}}, +} + +func TestUnmarshal(t *testing.T) { + for i, test := range unmarshalTestData { + pv := reflect.New(reflect.TypeOf(test.out).Elem()) + val := pv.Interface() + _, err := Unmarshal(test.in, val) + if err != nil { + t.Errorf("Unmarshal failed at index %d %v", i, err) + } + if !reflect.DeepEqual(val, test.out) { + t.Errorf("#%d:\nhave %#v\nwant %#v", i, val, test.out) + } + } +} + +type Certificate struct { + TBSCertificate TBSCertificate + SignatureAlgorithm AlgorithmIdentifier + SignatureValue BitString +} + +type TBSCertificate struct { + Version int `asn1:"optional,explicit,default:0,tag:0"` + SerialNumber RawValue + SignatureAlgorithm AlgorithmIdentifier + Issuer RDNSequence + Validity Validity + Subject RDNSequence + PublicKey PublicKeyInfo +} + +type AlgorithmIdentifier struct { + Algorithm ObjectIdentifier +} + +type RDNSequence []RelativeDistinguishedNameSET + +type RelativeDistinguishedNameSET []AttributeTypeAndValue + +type AttributeTypeAndValue struct { + Type ObjectIdentifier + Value interface{} +} + +type Validity struct { + NotBefore, NotAfter time.Time +} + +type PublicKeyInfo struct { + Algorithm AlgorithmIdentifier + PublicKey BitString +} + +func TestCertificate(t *testing.T) { + // This is a minimal, self-signed certificate that should parse correctly. + var cert Certificate + if _, err := Unmarshal(derEncodedSelfSignedCertBytes, &cert); err != nil { + t.Errorf("Unmarshal failed: %v", err) + } + if !reflect.DeepEqual(cert, derEncodedSelfSignedCert) { + t.Errorf("Bad result:\ngot: %+v\nwant: %+v", cert, derEncodedSelfSignedCert) + } +} + +func TestCertificateWithNUL(t *testing.T) { + // This is the paypal NUL-hack certificate. It should fail to parse because + // NUL isn't a permitted character in a PrintableString. + + var cert Certificate + if _, err := Unmarshal(derEncodedPaypalNULCertBytes, &cert); err == nil { + t.Error("Unmarshal succeeded, should not have") + } +} + +type rawStructTest struct { + Raw RawContent + A int +} + +func TestRawStructs(t *testing.T) { + var s rawStructTest + input := []byte{0x30, 0x03, 0x02, 0x01, 0x50} + + rest, err := Unmarshal(input, &s) + if len(rest) != 0 { + t.Errorf("incomplete parse: %x", rest) + return + } + if err != nil { + t.Error(err) + return + } + 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 { + t.Errorf("bad value for Raw: got %x want %x", s.Raw, input) + } +} + +var derEncodedSelfSignedCert = Certificate{ + TBSCertificate: TBSCertificate{ + Version: 0, + SerialNumber: RawValue{Class: 0, Tag: 2, IsCompound: false, Bytes: []uint8{0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}, FullBytes: []byte{2, 9, 0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}}, + SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}}, + Issuer: RDNSequence{ + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 7}, Value: "City"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 10}, Value: "Internet Widgits Pty Ltd"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, + }, + Validity: Validity{ + NotBefore: time.Date(2009, 10, 8, 00, 25, 53, 0, time.UTC), + NotAfter: time.Date(2010, 10, 8, 00, 25, 53, 0, time.UTC), + }, + Subject: RDNSequence{ + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 7}, Value: "City"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 10}, Value: "Internet Widgits Pty Ltd"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, + }, + PublicKey: PublicKeyInfo{ + Algorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}}, + PublicKey: BitString{ + Bytes: []uint8{ + 0x30, 0x48, 0x2, 0x41, 0x0, 0xcd, 0xb7, + 0x63, 0x9c, 0x32, 0x78, 0xf0, 0x6, 0xaa, 0x27, 0x7f, 0x6e, 0xaf, 0x42, + 0x90, 0x2b, 0x59, 0x2d, 0x8c, 0xbc, 0xbe, 0x38, 0xa1, 0xc9, 0x2b, 0xa4, + 0x69, 0x5a, 0x33, 0x1b, 0x1d, 0xea, 0xde, 0xad, 0xd8, 0xe9, 0xa5, 0xc2, + 0x7e, 0x8c, 0x4c, 0x2f, 0xd0, 0xa8, 0x88, 0x96, 0x57, 0x72, 0x2a, 0x4f, + 0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45, 0xdc, 0x8f, 0xde, 0xec, + 0x35, 0x7d, 0x2, 0x3, 0x1, 0x0, 0x1, + }, + BitLength: 592, + }, + }, + }, + SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}}, + SignatureValue: BitString{ + Bytes: []uint8{ + 0xa6, 0x7b, 0x6, 0xec, 0x5e, 0xce, + 0x92, 0x77, 0x2c, 0xa4, 0x13, 0xcb, 0xa3, 0xca, 0x12, 0x56, 0x8f, 0xdc, 0x6c, + 0x7b, 0x45, 0x11, 0xcd, 0x40, 0xa7, 0xf6, 0x59, 0x98, 0x4, 0x2, 0xdf, 0x2b, + 0x99, 0x8b, 0xb9, 0xa4, 0xa8, 0xcb, 0xeb, 0x34, 0xc0, 0xf0, 0xa7, 0x8c, 0xf8, + 0xd9, 0x1e, 0xde, 0x14, 0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa, + 0xfa, 0x88, 0x21, 0x49, 0x4, 0x35, + }, + BitLength: 512, + }, +} + +var derEncodedSelfSignedCertBytes = []byte{ + 0x30, 0x82, 0x02, 0x18, 0x30, + 0x82, 0x01, 0xc2, 0x02, 0x09, 0x00, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, + 0x98, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x30, 0x81, 0x92, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x58, 0x58, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x04, 0x43, + 0x69, 0x74, 0x79, 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, 0x31, + 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x66, 0x61, 0x6c, + 0x73, 0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x40, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, + 0x30, 0x39, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x35, 0x35, 0x33, 0x5a, + 0x17, 0x0d, 0x31, 0x30, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x35, 0x35, + 0x33, 0x5a, 0x30, 0x81, 0x92, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x58, 0x58, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x04, 0x43, 0x69, + 0x74, 0x79, 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, 0x31, 0x1a, + 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x66, 0x61, 0x6c, 0x73, + 0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, + 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x40, 0x65, 0x78, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 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, 0xcd, 0xb7, 0x63, 0x9c, 0x32, 0x78, + 0xf0, 0x06, 0xaa, 0x27, 0x7f, 0x6e, 0xaf, 0x42, 0x90, 0x2b, 0x59, 0x2d, 0x8c, + 0xbc, 0xbe, 0x38, 0xa1, 0xc9, 0x2b, 0xa4, 0x69, 0x5a, 0x33, 0x1b, 0x1d, 0xea, + 0xde, 0xad, 0xd8, 0xe9, 0xa5, 0xc2, 0x7e, 0x8c, 0x4c, 0x2f, 0xd0, 0xa8, 0x88, + 0x96, 0x57, 0x72, 0x2a, 0x4f, 0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45, + 0xdc, 0x8f, 0xde, 0xec, 0x35, 0x7d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x03, 0x41, 0x00, 0xa6, 0x7b, 0x06, 0xec, 0x5e, 0xce, 0x92, 0x77, 0x2c, 0xa4, + 0x13, 0xcb, 0xa3, 0xca, 0x12, 0x56, 0x8f, 0xdc, 0x6c, 0x7b, 0x45, 0x11, 0xcd, + 0x40, 0xa7, 0xf6, 0x59, 0x98, 0x04, 0x02, 0xdf, 0x2b, 0x99, 0x8b, 0xb9, 0xa4, + 0xa8, 0xcb, 0xeb, 0x34, 0xc0, 0xf0, 0xa7, 0x8c, 0xf8, 0xd9, 0x1e, 0xde, 0x14, + 0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa, 0xfa, 0x88, 0x21, 0x49, + 0x04, 0x35, +} + +var derEncodedPaypalNULCertBytes = []byte{ + 0x30, 0x82, 0x06, 0x44, 0x30, + 0x82, 0x05, 0xad, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x00, 0xf0, 0x9b, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, 0x30, 0x82, 0x01, 0x12, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x45, 0x53, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x09, 0x42, 0x61, 0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61, + 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x42, 0x61, + 0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x20, 0x49, 0x50, 0x53, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x73, 0x2e, 0x6c, 0x2e, 0x31, 0x2e, + 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x14, 0x25, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, + 0x20, 0x43, 0x2e, 0x49, 0x2e, 0x46, 0x2e, 0x20, 0x20, 0x42, 0x2d, 0x42, 0x36, + 0x32, 0x32, 0x31, 0x30, 0x36, 0x39, 0x35, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x13, 0x25, 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c, + 0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x25, 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, + 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, + 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, + 0x01, 0x16, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70, + 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, + 0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a, 0x17, 0x0d, + 0x31, 0x31, 0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a, + 0x30, 0x81, 0x94, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16, + 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0d, 0x53, 0x61, 0x6e, 0x20, + 0x46, 0x72, 0x61, 0x6e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x31, 0x11, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, + 0x74, 0x79, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0b, + 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x55, 0x6e, 0x69, 0x74, 0x31, 0x2f, + 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x77, 0x77, 0x77, 0x2e, + 0x70, 0x61, 0x79, 0x70, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x73, 0x73, + 0x6c, 0x2e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x63, 0x63, 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, 0xd2, 0x69, + 0xfa, 0x6f, 0x3a, 0x00, 0xb4, 0x21, 0x1b, 0xc8, 0xb1, 0x02, 0xd7, 0x3f, 0x19, + 0xb2, 0xc4, 0x6d, 0xb4, 0x54, 0xf8, 0x8b, 0x8a, 0xcc, 0xdb, 0x72, 0xc2, 0x9e, + 0x3c, 0x60, 0xb9, 0xc6, 0x91, 0x3d, 0x82, 0xb7, 0x7d, 0x99, 0xff, 0xd1, 0x29, + 0x84, 0xc1, 0x73, 0x53, 0x9c, 0x82, 0xdd, 0xfc, 0x24, 0x8c, 0x77, 0xd5, 0x41, + 0xf3, 0xe8, 0x1e, 0x42, 0xa1, 0xad, 0x2d, 0x9e, 0xff, 0x5b, 0x10, 0x26, 0xce, + 0x9d, 0x57, 0x17, 0x73, 0x16, 0x23, 0x38, 0xc8, 0xd6, 0xf1, 0xba, 0xa3, 0x96, + 0x5b, 0x16, 0x67, 0x4a, 0x4f, 0x73, 0x97, 0x3a, 0x4d, 0x14, 0xa4, 0xf4, 0xe2, + 0x3f, 0x8b, 0x05, 0x83, 0x42, 0xd1, 0xd0, 0xdc, 0x2f, 0x7a, 0xe5, 0xb6, 0x10, + 0xb2, 0x11, 0xc0, 0xdc, 0x21, 0x2a, 0x90, 0xff, 0xae, 0x97, 0x71, 0x5a, 0x49, + 0x81, 0xac, 0x40, 0xf3, 0x3b, 0xb8, 0x59, 0xb2, 0x4f, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x82, 0x03, 0x21, 0x30, 0x82, 0x03, 0x1d, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x11, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, 0x06, 0x40, + 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xf8, + 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x61, 0x8f, 0x61, 0x34, 0x43, 0x55, 0x14, + 0x7f, 0x27, 0x09, 0xce, 0x4c, 0x8b, 0xea, 0x9b, 0x7b, 0x19, 0x25, 0xbc, 0x6e, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0x0e, 0x07, 0x60, 0xd4, 0x39, 0xc9, 0x1b, 0x5b, 0x5d, 0x90, 0x7b, 0x23, 0xc8, + 0xd2, 0x34, 0x9d, 0x4a, 0x9a, 0x46, 0x39, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, + 0x11, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, + 0x15, 0x30, 0x13, 0x81, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40, + 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x72, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d, 0x04, 0x65, 0x16, 0x63, + 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e, + 0x4f, 0x54, 0x20, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, 0x44, 0x2e, + 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, + 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x2f, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x02, 0x04, 0x22, 0x16, 0x20, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, + 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, + 0x32, 0x30, 0x30, 0x32, 0x2f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x86, 0xf8, 0x42, 0x01, 0x04, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70, + 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, + 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x43, 0x4c, + 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x03, 0x04, 0x39, 0x16, 0x37, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, + 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, + 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x72, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74, + 0x6d, 0x6c, 0x3f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, + 0x42, 0x01, 0x07, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, + 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, + 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x3f, 0x30, 0x41, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x08, 0x04, 0x34, 0x16, 0x32, 0x68, 0x74, + 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, + 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, + 0x30, 0x30, 0x32, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x4c, 0x41, + 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x30, 0x81, 0x83, 0x06, + 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x7c, 0x30, 0x7a, 0x30, 0x39, 0xa0, 0x37, 0xa0, + 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, + 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, + 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, + 0x32, 0x30, 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x3d, 0xa0, 0x3b, 0xa0, 0x39, 0x86, 0x37, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x69, + 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, + 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, + 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, + 0x73, 0x70, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x68, 0xee, 0x79, 0x97, 0x97, 0xdd, 0x3b, + 0xef, 0x16, 0x6a, 0x06, 0xf2, 0x14, 0x9a, 0x6e, 0xcd, 0x9e, 0x12, 0xf7, 0xaa, + 0x83, 0x10, 0xbd, 0xd1, 0x7c, 0x98, 0xfa, 0xc7, 0xae, 0xd4, 0x0e, 0x2c, 0x9e, + 0x38, 0x05, 0x9d, 0x52, 0x60, 0xa9, 0x99, 0x0a, 0x81, 0xb4, 0x98, 0x90, 0x1d, + 0xae, 0xbb, 0x4a, 0xd7, 0xb9, 0xdc, 0x88, 0x9e, 0x37, 0x78, 0x41, 0x5b, 0xf7, + 0x82, 0xa5, 0xf2, 0xba, 0x41, 0x25, 0x5a, 0x90, 0x1a, 0x1e, 0x45, 0x38, 0xa1, + 0x52, 0x58, 0x75, 0x94, 0x26, 0x44, 0xfb, 0x20, 0x07, 0xba, 0x44, 0xcc, 0xe5, + 0x4a, 0x2d, 0x72, 0x3f, 0x98, 0x47, 0xf6, 0x26, 0xdc, 0x05, 0x46, 0x05, 0x07, + 0x63, 0x21, 0xab, 0x46, 0x9b, 0x9c, 0x78, 0xd5, 0x54, 0x5b, 0x3d, 0x0c, 0x1e, + 0xc8, 0x64, 0x8c, 0xb5, 0x50, 0x23, 0x82, 0x6f, 0xdb, 0xb8, 0x22, 0x1c, 0x43, + 0x96, 0x07, 0xa8, 0xbb, +} diff --git a/src/pkg/encoding/asn1/common.go b/src/pkg/encoding/asn1/common.go new file mode 100644 index 000000000..03856bc55 --- /dev/null +++ b/src/pkg/encoding/asn1/common.go @@ -0,0 +1,161 @@ +// 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 asn1 + +import ( + "reflect" + "strconv" + "strings" +) + +// ASN.1 objects have metadata preceding them: +// the tag: the type of the object +// a flag denoting if this object is compound or not +// the class type: the namespace of the tag +// the length of the object, in bytes + +// Here are some standard tags and classes + +const ( + tagBoolean = 1 + tagInteger = 2 + tagBitString = 3 + tagOctetString = 4 + tagOID = 6 + tagEnum = 10 + tagUTF8String = 12 + tagSequence = 16 + tagSet = 17 + tagPrintableString = 19 + tagT61String = 20 + tagIA5String = 22 + tagUTCTime = 23 + tagGeneralizedTime = 24 + tagGeneralString = 27 +) + +const ( + classUniversal = 0 + classApplication = 1 + classContextSpecific = 2 + classPrivate = 3 +) + +type tagAndLength struct { + class, tag, length int + isCompound bool +} + +// ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead +// of" and "in addition to". When not specified, every primitive type has a +// default tag in the UNIVERSAL class. +// +// For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1 +// doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT +// CONTEXT-SPECIFIC 42], that means that the tag is replaced by another. +// +// On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an +// /additional/ tag would wrap the default tag. This explicit tag will have the +// compound flag set. +// +// (This is used in order to remove ambiguity with optional elements.) +// +// You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we +// don't support that here. We support a single layer of EXPLICIT or IMPLICIT +// tagging with tag strings on the fields of a structure. + +// fieldParameters is the parsed representation of tag string from a structure field. +type fieldParameters struct { + optional bool // true iff the field is OPTIONAL + explicit bool // true iff an EXPLICIT tag is in use. + application bool // true iff an APPLICATION tag is in use. + defaultValue *int64 // a default value for INTEGER typed fields (maybe nil). + tag *int // the EXPLICIT or IMPLICIT tag (maybe nil). + stringType int // the string tag to use when marshaling. + set bool // true iff this should be encoded as a SET + omitEmpty bool // true iff this should be omitted if empty when marshaling. + + // Invariants: + // if explicit is set, tag is non-nil. +} + +// Given a tag string with the format specified in the package comment, +// parseFieldParameters will parse it into a fieldParameters structure, +// ignoring unknown parts of the string. +func parseFieldParameters(str string) (ret fieldParameters) { + for _, part := range strings.Split(str, ",") { + switch { + case part == "optional": + ret.optional = true + case part == "explicit": + ret.explicit = true + if ret.tag == nil { + ret.tag = new(int) + } + case part == "ia5": + ret.stringType = tagIA5String + case part == "printable": + ret.stringType = tagPrintableString + case strings.HasPrefix(part, "default:"): + i, err := strconv.ParseInt(part[8:], 10, 64) + if err == nil { + ret.defaultValue = new(int64) + *ret.defaultValue = i + } + case strings.HasPrefix(part, "tag:"): + i, err := strconv.Atoi(part[4:]) + if err == nil { + ret.tag = new(int) + *ret.tag = i + } + case part == "set": + ret.set = true + case part == "application": + ret.application = true + if ret.tag == nil { + ret.tag = new(int) + } + case part == "omitempty": + ret.omitEmpty = true + } + } + return +} + +// Given a reflected Go type, getUniversalType returns the default tag number +// and expected compound flag. +func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) { + switch t { + case objectIdentifierType: + return tagOID, false, true + case bitStringType: + return tagBitString, false, true + case timeType: + return tagUTCTime, false, true + case enumeratedType: + return tagEnum, false, true + case bigIntType: + return tagInteger, false, true + } + switch t.Kind() { + case reflect.Bool: + return tagBoolean, false, true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return tagInteger, false, true + case reflect.Struct: + return tagSequence, true, true + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { + return tagOctetString, false, true + } + if strings.HasSuffix(t.Name(), "SET") { + return tagSet, true, true + } + return tagSequence, true, true + case reflect.String: + return tagPrintableString, false, true + } + return 0, false, false +} diff --git a/src/pkg/encoding/asn1/marshal.go b/src/pkg/encoding/asn1/marshal.go new file mode 100644 index 000000000..163bca575 --- /dev/null +++ b/src/pkg/encoding/asn1/marshal.go @@ -0,0 +1,557 @@ +// 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 asn1 + +import ( + "bytes" + "fmt" + "io" + "math/big" + "reflect" + "time" +) + +// A forkableWriter is an in-memory buffer that can be +// 'forked' to create new forkableWriters that bracket the +// original. After +// pre, post := w.fork(); +// the overall sequence of bytes represented is logically w+pre+post. +type forkableWriter struct { + *bytes.Buffer + pre, post *forkableWriter +} + +func newForkableWriter() *forkableWriter { + return &forkableWriter{new(bytes.Buffer), nil, nil} +} + +func (f *forkableWriter) fork() (pre, post *forkableWriter) { + if f.pre != nil || f.post != nil { + panic("have already forked") + } + f.pre = newForkableWriter() + f.post = newForkableWriter() + return f.pre, f.post +} + +func (f *forkableWriter) Len() (l int) { + l += f.Buffer.Len() + if f.pre != nil { + l += f.pre.Len() + } + if f.post != nil { + l += f.post.Len() + } + return +} + +func (f *forkableWriter) writeTo(out io.Writer) (n int, err error) { + n, err = out.Write(f.Bytes()) + if err != nil { + return + } + + var nn int + + if f.pre != nil { + nn, err = f.pre.writeTo(out) + n += nn + if err != nil { + return + } + } + + if f.post != nil { + nn, err = f.post.writeTo(out) + n += nn + } + return +} + +func marshalBase128Int(out *forkableWriter, n int64) (err error) { + if n == 0 { + err = out.WriteByte(0) + return + } + + l := 0 + for i := n; i > 0; i >>= 7 { + l++ + } + + for i := l - 1; i >= 0; i-- { + o := byte(n >> uint(i*7)) + o &= 0x7f + if i != 0 { + o |= 0x80 + } + err = out.WriteByte(o) + if err != nil { + return + } + } + + return nil +} + +func marshalInt64(out *forkableWriter, i int64) (err error) { + n := int64Length(i) + + for ; n > 0; n-- { + err = out.WriteByte(byte(i >> uint((n-1)*8))) + if err != nil { + return + } + } + + return nil +} + +func int64Length(i int64) (numBytes int) { + numBytes = 1 + + for i > 127 { + numBytes++ + i >>= 8 + } + + for i < -128 { + numBytes++ + i >>= 8 + } + + return +} + +func marshalBigInt(out *forkableWriter, n *big.Int) (err error) { + if n.Sign() < 0 { + // A negative number has to be converted to two's-complement + // form. So we'll subtract 1 and invert. If the + // most-significant-bit isn't set then we'll need to pad the + // beginning with 0xff in order to keep the number negative. + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bytes := nMinus1.Bytes() + for i := range bytes { + bytes[i] ^= 0xff + } + if len(bytes) == 0 || bytes[0]&0x80 == 0 { + err = out.WriteByte(0xff) + if err != nil { + return + } + } + _, err = out.Write(bytes) + } else if n.Sign() == 0 { + // Zero is written as a single 0 zero rather than no bytes. + err = out.WriteByte(0x00) + } else { + bytes := n.Bytes() + if len(bytes) > 0 && bytes[0]&0x80 != 0 { + // We'll have to pad this with 0x00 in order to stop it + // looking like a negative number. + err = out.WriteByte(0) + if err != nil { + return + } + } + _, err = out.Write(bytes) + } + return +} + +func marshalLength(out *forkableWriter, i int) (err error) { + n := lengthLength(i) + + for ; n > 0; n-- { + err = out.WriteByte(byte(i >> uint((n-1)*8))) + if err != nil { + return + } + } + + return nil +} + +func lengthLength(i int) (numBytes int) { + numBytes = 1 + for i > 255 { + numBytes++ + i >>= 8 + } + return +} + +func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err error) { + b := uint8(t.class) << 6 + if t.isCompound { + b |= 0x20 + } + if t.tag >= 31 { + b |= 0x1f + err = out.WriteByte(b) + if err != nil { + return + } + err = marshalBase128Int(out, int64(t.tag)) + if err != nil { + return + } + } else { + b |= uint8(t.tag) + err = out.WriteByte(b) + if err != nil { + return + } + } + + if t.length >= 128 { + l := lengthLength(t.length) + err = out.WriteByte(0x80 | byte(l)) + if err != nil { + return + } + err = marshalLength(out, t.length) + if err != nil { + return + } + } else { + err = out.WriteByte(byte(t.length)) + if err != nil { + return + } + } + + return nil +} + +func marshalBitString(out *forkableWriter, b BitString) (err error) { + paddingBits := byte((8 - b.BitLength%8) % 8) + err = out.WriteByte(paddingBits) + if err != nil { + return + } + _, err = out.Write(b.Bytes) + return +} + +func marshalObjectIdentifier(out *forkableWriter, oid []int) (err error) { + if len(oid) < 2 || oid[0] > 6 || oid[1] >= 40 { + return StructuralError{"invalid object identifier"} + } + + err = out.WriteByte(byte(oid[0]*40 + oid[1])) + if err != nil { + return + } + for i := 2; i < len(oid); i++ { + err = marshalBase128Int(out, int64(oid[i])) + if err != nil { + return + } + } + + return +} + +func marshalPrintableString(out *forkableWriter, s string) (err error) { + b := []byte(s) + for _, c := range b { + if !isPrintable(c) { + return StructuralError{"PrintableString contains invalid character"} + } + } + + _, err = out.Write(b) + return +} + +func marshalIA5String(out *forkableWriter, s string) (err error) { + b := []byte(s) + for _, c := range b { + if c > 127 { + return StructuralError{"IA5String contains invalid character"} + } + } + + _, err = out.Write(b) + return +} + +func marshalTwoDigits(out *forkableWriter, v int) (err error) { + err = out.WriteByte(byte('0' + (v/10)%10)) + if err != nil { + return + } + return out.WriteByte(byte('0' + v%10)) +} + +func marshalUTCTime(out *forkableWriter, t time.Time) (err error) { + utc := t.UTC() + year, month, day := utc.Date() + + switch { + case 1950 <= year && year < 2000: + err = marshalTwoDigits(out, int(year-1900)) + case 2000 <= year && year < 2050: + err = marshalTwoDigits(out, int(year-2000)) + default: + return StructuralError{"Cannot represent time as UTCTime"} + } + if err != nil { + return + } + + err = marshalTwoDigits(out, int(month)) + if err != nil { + return + } + + err = marshalTwoDigits(out, day) + if err != nil { + return + } + + hour, min, sec := utc.Clock() + + err = marshalTwoDigits(out, hour) + if err != nil { + return + } + + err = marshalTwoDigits(out, min) + if err != nil { + return + } + + err = marshalTwoDigits(out, sec) + if err != nil { + return + } + + _, offset := t.Zone() + + switch { + case offset/60 == 0: + err = out.WriteByte('Z') + return + case offset > 0: + err = out.WriteByte('+') + case offset < 0: + err = out.WriteByte('-') + } + + if err != nil { + return + } + + offsetMinutes := offset / 60 + if offsetMinutes < 0 { + offsetMinutes = -offsetMinutes + } + + err = marshalTwoDigits(out, offsetMinutes/60) + if err != nil { + return + } + + err = marshalTwoDigits(out, offsetMinutes%60) + return +} + +func stripTagAndLength(in []byte) []byte { + _, offset, err := parseTagAndLength(in, 0) + if err != nil { + return in + } + return in[offset:] +} + +func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) { + switch value.Type() { + case timeType: + return marshalUTCTime(out, value.Interface().(time.Time)) + case bitStringType: + return marshalBitString(out, value.Interface().(BitString)) + case objectIdentifierType: + return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier)) + case bigIntType: + return marshalBigInt(out, value.Interface().(*big.Int)) + } + + switch v := value; v.Kind() { + case reflect.Bool: + if v.Bool() { + return out.WriteByte(255) + } else { + return out.WriteByte(0) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return marshalInt64(out, int64(v.Int())) + case reflect.Struct: + t := v.Type() + + startingField := 0 + + // If the first element of the structure is a non-empty + // RawContents, then we don't bother serializing the rest. + if t.NumField() > 0 && t.Field(0).Type == rawContentsType { + s := v.Field(0) + if s.Len() > 0 { + bytes := make([]byte, s.Len()) + for i := 0; i < s.Len(); i++ { + bytes[i] = uint8(s.Index(i).Uint()) + } + /* The RawContents will contain the tag and + * length fields but we'll also be writing + * those ourselves, so we strip them out of + * bytes */ + _, err = out.Write(stripTagAndLength(bytes)) + return + } else { + startingField = 1 + } + } + + for i := startingField; i < t.NumField(); i++ { + var pre *forkableWriter + pre, out = out.fork() + err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1"))) + if err != nil { + return + } + } + return + case reflect.Slice: + sliceType := v.Type() + if sliceType.Elem().Kind() == reflect.Uint8 { + bytes := make([]byte, v.Len()) + for i := 0; i < v.Len(); i++ { + bytes[i] = uint8(v.Index(i).Uint()) + } + _, err = out.Write(bytes) + return + } + + var params fieldParameters + for i := 0; i < v.Len(); i++ { + var pre *forkableWriter + pre, out = out.fork() + err = marshalField(pre, v.Index(i), params) + if err != nil { + return + } + } + return + case reflect.String: + if params.stringType == tagIA5String { + return marshalIA5String(out, v.String()) + } else { + return marshalPrintableString(out, v.String()) + } + return + } + + return StructuralError{"unknown Go type"} +} + +func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err error) { + // If the field is an interface{} then recurse into it. + if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 { + return marshalField(out, v.Elem(), params) + } + + if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty { + return + } + + if params.optional && reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) { + return + } + + if v.Type() == rawValueType { + rv := v.Interface().(RawValue) + if len(rv.FullBytes) != 0 { + _, err = out.Write(rv.FullBytes) + } else { + err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound}) + if err != nil { + return + } + _, err = out.Write(rv.Bytes) + } + return + } + + tag, isCompound, ok := getUniversalType(v.Type()) + if !ok { + err = StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())} + return + } + class := classUniversal + + if params.stringType != 0 { + if tag != tagPrintableString { + return StructuralError{"Explicit string type given to non-string member"} + } + tag = params.stringType + } + + if params.set { + if tag != tagSequence { + return StructuralError{"Non sequence tagged as set"} + } + tag = tagSet + } + + tags, body := out.fork() + + err = marshalBody(body, v, params) + if err != nil { + return + } + + bodyLen := body.Len() + + var explicitTag *forkableWriter + if params.explicit { + explicitTag, tags = tags.fork() + } + + if !params.explicit && params.tag != nil { + // implicit tag. + tag = *params.tag + class = classContextSpecific + } + + err = marshalTagAndLength(tags, tagAndLength{class, tag, bodyLen, isCompound}) + if err != nil { + return + } + + if params.explicit { + err = marshalTagAndLength(explicitTag, tagAndLength{ + class: classContextSpecific, + tag: *params.tag, + length: bodyLen + tags.Len(), + isCompound: true, + }) + } + + return nil +} + +// Marshal returns the ASN.1 encoding of val. +func Marshal(val interface{}) ([]byte, error) { + var out bytes.Buffer + v := reflect.ValueOf(val) + f := newForkableWriter() + err := marshalField(f, v, fieldParameters{}) + if err != nil { + return nil, err + } + _, err = f.writeTo(&out) + return out.Bytes(), nil +} diff --git a/src/pkg/encoding/asn1/marshal_test.go b/src/pkg/encoding/asn1/marshal_test.go new file mode 100644 index 000000000..f43bcae68 --- /dev/null +++ b/src/pkg/encoding/asn1/marshal_test.go @@ -0,0 +1,139 @@ +// 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 asn1 + +import ( + "bytes" + "encoding/hex" + "math/big" + "testing" + "time" +) + +type intStruct struct { + A int +} + +type twoIntStruct struct { + A int + B int +} + +type bigIntStruct struct { + A *big.Int +} + +type nestedStruct struct { + A intStruct +} + +type rawContentsStruct struct { + Raw RawContent + A int +} + +type implicitTagTest struct { + A int `asn1:"implicit,tag:5"` +} + +type explicitTagTest struct { + A int `asn1:"explicit,tag:5"` +} + +type ia5StringTest struct { + A string `asn1:"ia5"` +} + +type printableStringTest struct { + A string `asn1:"printable"` +} + +type optionalRawValueTest struct { + A RawValue `asn1:"optional"` +} + +type omitEmptyTest struct { + A []string `asn1:"omitempty"` +} + +type testSET []int + +var PST = time.FixedZone("PST", -8*60*60) + +type marshalTest struct { + in interface{} + out string // hex encoded +} + +var marshalTests = []marshalTest{ + {10, "02010a"}, + {127, "02017f"}, + {128, "02020080"}, + {-128, "020180"}, + {-129, "0202ff7f"}, + {intStruct{64}, "3003020140"}, + {bigIntStruct{big.NewInt(0x123456)}, "30050203123456"}, + {twoIntStruct{64, 65}, "3006020140020141"}, + {nestedStruct{intStruct{127}}, "3005300302017f"}, + {[]byte{1, 2, 3}, "0403010203"}, + {implicitTagTest{64}, "3003850140"}, + {explicitTagTest{64}, "3005a503020140"}, + {time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"}, + {time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"}, + {time.Unix(1258325776, 0).In(PST), "17113039313131353232353631362d30383030"}, + {BitString{[]byte{0x80}, 1}, "03020780"}, + {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"}, + {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"}, + {ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"}, + {"test", "130474657374"}, + { + "" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 127 times 'x' + "137f" + + "7878787878787878787878787878787878787878787878787878787878787878" + + "7878787878787878787878787878787878787878787878787878787878787878" + + "7878787878787878787878787878787878787878787878787878787878787878" + + "78787878787878787878787878787878787878787878787878787878787878", + }, + { + "" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 128 times 'x' + "138180" + + "7878787878787878787878787878787878787878787878787878787878787878" + + "7878787878787878787878787878787878787878787878787878787878787878" + + "7878787878787878787878787878787878787878787878787878787878787878" + + "7878787878787878787878787878787878787878787878787878787878787878", + }, + {ia5StringTest{"test"}, "3006160474657374"}, + {optionalRawValueTest{}, "3000"}, + {printableStringTest{"test"}, "3006130474657374"}, + {printableStringTest{"test*"}, "30071305746573742a"}, + {rawContentsStruct{nil, 64}, "3003020140"}, + {rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"}, + {RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"}, + {testSET([]int{10}), "310302010a"}, + {omitEmptyTest{[]string{}}, "3000"}, + {omitEmptyTest{[]string{"1"}}, "30053003130131"}, +} + +func TestMarshal(t *testing.T) { + for i, test := range marshalTests { + data, err := Marshal(test.in) + if err != nil { + t.Errorf("#%d failed: %s", i, err) + } + out, _ := hex.DecodeString(test.out) + if bytes.Compare(out, data) != 0 { + t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out) + + } + } +} diff --git a/src/pkg/encoding/base32/Makefile b/src/pkg/encoding/base32/Makefile deleted file mode 100644 index c0e85b644..000000000 --- a/src/pkg/encoding/base32/Makefile +++ /dev/null @@ -1,11 +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 $(GOROOT)/src/Make.inc - -TARG=encoding/base32 -GOFILES=\ - base32.go\ - -include $(GOROOT)/src/Make.pkg diff --git a/src/pkg/encoding/base32/base32.go b/src/pkg/encoding/base32/base32.go index acace30d6..71da6e22b 100644 --- a/src/pkg/encoding/base32/base32.go +++ b/src/pkg/encoding/base32/base32.go @@ -7,7 +7,6 @@ package base32 import ( "io" - "os" "strconv" ) @@ -126,8 +125,15 @@ func (enc *Encoding) Encode(dst, src []byte) { } } +// EncodeToString returns the base32 encoding of src. +func (enc *Encoding) EncodeToString(src []byte) string { + buf := make([]byte, enc.EncodedLen(len(src))) + enc.Encode(buf, src) + return string(buf) +} + type encoder struct { - err os.Error + err error enc *Encoding w io.Writer buf [5]byte // buffered data waiting to be encoded @@ -135,7 +141,7 @@ type encoder struct { out [1024]byte // output buffer } -func (e *encoder) Write(p []byte) (n int, err os.Error) { +func (e *encoder) Write(p []byte) (n int, err error) { if e.err != nil { return 0, e.err } @@ -187,7 +193,7 @@ func (e *encoder) Write(p []byte) (n int, err os.Error) { // Close flushes any pending output from the encoder. // It is an error to call Write after calling Close. -func (e *encoder) Close() os.Error { +func (e *encoder) Close() error { // If there's anything left in the buffer, flush it out if e.err == nil && e.nbuf > 0 { e.enc.Encode(e.out[0:], e.buf[0:e.nbuf]) @@ -216,30 +222,38 @@ func (enc *Encoding) EncodedLen(n int) int { return (n + 4) / 5 * 8 } type CorruptInputError int64 -func (e CorruptInputError) String() string { - return "illegal base32 data at input byte " + strconv.Itoa64(int64(e)) +func (e CorruptInputError) Error() string { + return "illegal base32 data at input byte " + strconv.FormatInt(int64(e), 10) } // decode is like Decode but returns an additional 'end' value, which // indicates if end-of-message padding was encountered and thus any -// additional data is an error. decode also assumes len(src)%8==0, -// since it is meant for internal use. -func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err os.Error) { - for i := 0; i < len(src)/8 && !end; i++ { +// additional data is an error. +func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { + osrc := src + for len(src) > 0 && !end { // Decode quantum using the base32 alphabet var dbuf [8]byte dlen := 8 // do the top bytes contain any data? dbufloop: - for j := 0; j < 8; j++ { - in := src[i*8+j] - if in == '=' && j >= 2 && i == len(src)/8-1 { + for j := 0; j < 8; { + if len(src) == 0 { + return n, false, CorruptInputError(len(osrc) - len(src) - j) + } + in := src[0] + src = src[1:] + if in == '\r' || in == '\n' { + // Ignore this character. + continue + } + if in == '=' && j >= 2 && len(src) < 8 { // We've reached the end and there's // padding, the rest should be padded - for k := j; k < 8; k++ { - if src[i*8+k] != '=' { - return n, false, CorruptInputError(i*8 + j) + for k := 0; k < 8-j-1; k++ { + if len(src) > k && src[k] != '=' { + return n, false, CorruptInputError(len(osrc) - len(src) + k - 1) } } dlen = j @@ -248,28 +262,30 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err os.Error) { } dbuf[j] = enc.decodeMap[in] if dbuf[j] == 0xFF { - return n, false, CorruptInputError(i*8 + j) + return n, false, CorruptInputError(len(osrc) - len(src) - 1) } + j++ } // Pack 8x 5-bit source blocks into 5 byte destination // quantum switch dlen { case 7, 8: - dst[i*5+4] = dbuf[6]<<5 | dbuf[7] + dst[4] = dbuf[6]<<5 | dbuf[7] fallthrough case 6, 5: - dst[i*5+3] = dbuf[4]<<7 | dbuf[5]<<2 | dbuf[6]>>3 + dst[3] = dbuf[4]<<7 | dbuf[5]<<2 | dbuf[6]>>3 fallthrough case 4: - dst[i*5+2] = dbuf[3]<<4 | dbuf[4]>>1 + dst[2] = dbuf[3]<<4 | dbuf[4]>>1 fallthrough case 3: - dst[i*5+1] = dbuf[1]<<6 | dbuf[2]<<1 | dbuf[3]>>4 + dst[1] = dbuf[1]<<6 | dbuf[2]<<1 | dbuf[3]>>4 fallthrough case 2: - dst[i*5+0] = dbuf[0]<<3 | dbuf[1]>>2 + dst[0] = dbuf[0]<<3 | dbuf[1]>>2 } + dst = dst[5:] switch dlen { case 2: n += 1 @@ -290,17 +306,21 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err os.Error) { // DecodedLen(len(src)) bytes to dst and returns the number of bytes // written. If src contains invalid base32 data, it will return the // number of bytes successfully written and CorruptInputError. -func (enc *Encoding) Decode(dst, src []byte) (n int, err os.Error) { - if len(src)%8 != 0 { - return 0, CorruptInputError(len(src) / 8 * 8) - } - +// New line characters (\r and \n) are ignored. +func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { n, _, err = enc.decode(dst, src) return } +// DecodeString returns the bytes represented by the base32 string s. +func (enc *Encoding) DecodeString(s string) ([]byte, error) { + dbuf := make([]byte, enc.DecodedLen(len(s))) + n, err := enc.Decode(dbuf, []byte(s)) + return dbuf[:n], err +} + type decoder struct { - err os.Error + err error enc *Encoding r io.Reader end bool // saw end of message @@ -310,7 +330,7 @@ type decoder struct { outbuf [1024 / 8 * 5]byte } -func (d *decoder) Read(p []byte) (n int, err os.Error) { +func (d *decoder) Read(p []byte) (n int, err error) { if d.err != nil { return 0, d.err } diff --git a/src/pkg/encoding/base32/base32_test.go b/src/pkg/encoding/base32/base32_test.go index 3fa1c2b26..98365e18c 100644 --- a/src/pkg/encoding/base32/base32_test.go +++ b/src/pkg/encoding/base32/base32_test.go @@ -6,8 +6,8 @@ package base32 import ( "bytes" + "io" "io/ioutil" - "os" "testing" ) @@ -51,9 +51,8 @@ func testEqual(t *testing.T, msg string, args ...interface{}) bool { func TestEncode(t *testing.T) { for _, p := range pairs { - buf := make([]byte, StdEncoding.EncodedLen(len(p.decoded))) - StdEncoding.Encode(buf, []byte(p.decoded)) - testEqual(t, "Encode(%q) = %q, want %q", p.decoded, string(buf), p.encoded) + got := StdEncoding.EncodeToString([]byte(p.decoded)) + testEqual(t, "Encode(%q) = %q, want %q", p.decoded, got, p.encoded) } } @@ -78,11 +77,11 @@ func TestEncoderBuffering(t *testing.T) { end = len(input) } n, err := encoder.Write(input[pos:end]) - testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, os.Error(nil)) + testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, error(nil)) testEqual(t, "Write(%q) gave length %v, want %v", input[pos:end], n, end-pos) } err := encoder.Close() - testEqual(t, "Close gave error %v, want %v", err, os.Error(nil)) + testEqual(t, "Close gave error %v, want %v", err, error(nil)) testEqual(t, "Encoding/%d of %q = %q, want %q", bs, bigtest.decoded, bb.String(), bigtest.encoded) } } @@ -91,7 +90,7 @@ func TestDecode(t *testing.T) { for _, p := range pairs { dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))) count, end, err := StdEncoding.decode(dbuf, []byte(p.encoded)) - testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, os.Error(nil)) + testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, error(nil)) testEqual(t, "Decode(%q) = length %v, want %v", p.encoded, count, len(p.decoded)) if len(p.encoded) > 0 { testEqual(t, "Decode(%q) = end %v, want %v", p.encoded, end, (p.encoded[len(p.encoded)-1] == '=')) @@ -99,6 +98,10 @@ func TestDecode(t *testing.T) { testEqual(t, "Decode(%q) = %q, want %q", p.encoded, string(dbuf[0:count]), p.decoded) + + dbuf, err = StdEncoding.DecodeString(p.encoded) + testEqual(t, "DecodeString(%q) = error %v, want %v", p.encoded, err, error(nil)) + testEqual(t, "DecodeString(%q) = %q, want %q", p.encoded, string(dbuf), p.decoded) } } @@ -107,15 +110,15 @@ func TestDecoder(t *testing.T) { decoder := NewDecoder(StdEncoding, bytes.NewBufferString(p.encoded)) dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))) count, err := decoder.Read(dbuf) - if err != nil && err != os.EOF { + if err != nil && err != io.EOF { t.Fatal("Read failed", err) } testEqual(t, "Read from %q = length %v, want %v", p.encoded, count, len(p.decoded)) testEqual(t, "Decoding of %q = %q, want %q", p.encoded, string(dbuf[0:count]), p.decoded) - if err != os.EOF { + if err != io.EOF { count, err = decoder.Read(dbuf) } - testEqual(t, "Read from %q = %v, want %v", p.encoded, err, os.EOF) + testEqual(t, "Read from %q = %v, want %v", p.encoded, err, io.EOF) } } @@ -126,7 +129,7 @@ func TestDecoderBuffering(t *testing.T) { var total int for total = 0; total < len(bigtest.decoded); { n, err := decoder.Read(buf[total : total+bs]) - testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, os.Error(nil)) + testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, error(nil)) total += n } testEqual(t, "Decoding/%d of %q = %q, want %q", bs, bigtest.encoded, string(buf[0:total]), bigtest.decoded) @@ -191,3 +194,29 @@ func TestBig(t *testing.T) { t.Errorf("Decode(Encode(%d-byte string)) failed at offset %d", n, i) } } + +func TestNewLineCharacters(t *testing.T) { + // Each of these should decode to the string "sure", without errors. + const expected = "sure" + examples := []string{ + "ON2XEZI=", + "ON2XEZI=\r", + "ON2XEZI=\n", + "ON2XEZI=\r\n", + "ON2XEZ\r\nI=", + "ON2X\rEZ\nI=", + "ON2X\nEZ\rI=", + "ON2XEZ\nI=", + "ON2XEZI\n=", + } + for _, e := range examples { + buf, err := StdEncoding.DecodeString(e) + if err != nil { + t.Errorf("Decode(%q) failed: %v", e, err) + continue + } + if s := string(buf); s != expected { + t.Errorf("Decode(%q) = %q, want %q", e, s, expected) + } + } +} diff --git a/src/pkg/encoding/base64/Makefile b/src/pkg/encoding/base64/Makefile deleted file mode 100644 index 2f54ed839..000000000 --- a/src/pkg/encoding/base64/Makefile +++ /dev/null @@ -1,11 +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 ../../../Make.inc - -TARG=encoding/base64 -GOFILES=\ - base64.go\ - -include ../../../Make.pkg diff --git a/src/pkg/encoding/base64/base64.go b/src/pkg/encoding/base64/base64.go index c6b2a13e4..55f9f67a4 100644 --- a/src/pkg/encoding/base64/base64.go +++ b/src/pkg/encoding/base64/base64.go @@ -7,7 +7,6 @@ package base64 import ( "io" - "os" "strconv" ) @@ -114,7 +113,7 @@ func (enc *Encoding) EncodeToString(src []byte) string { } type encoder struct { - err os.Error + err error enc *Encoding w io.Writer buf [3]byte // buffered data waiting to be encoded @@ -122,7 +121,7 @@ type encoder struct { out [1024]byte // output buffer } -func (e *encoder) Write(p []byte) (n int, err os.Error) { +func (e *encoder) Write(p []byte) (n int, err error) { if e.err != nil { return 0, e.err } @@ -174,7 +173,7 @@ func (e *encoder) Write(p []byte) (n int, err os.Error) { // Close flushes any pending output from the encoder. // It is an error to call Write after calling Close. -func (e *encoder) Close() os.Error { +func (e *encoder) Close() error { // If there's anything left in the buffer, flush it out if e.err == nil && e.nbuf > 0 { e.enc.Encode(e.out[0:], e.buf[0:e.nbuf]) @@ -203,28 +202,36 @@ func (enc *Encoding) EncodedLen(n int) int { return (n + 2) / 3 * 4 } type CorruptInputError int64 -func (e CorruptInputError) String() string { - return "illegal base64 data at input byte " + strconv.Itoa64(int64(e)) +func (e CorruptInputError) Error() string { + return "illegal base64 data at input byte " + strconv.FormatInt(int64(e), 10) } // decode is like Decode but returns an additional 'end' value, which // indicates if end-of-message padding was encountered and thus any -// additional data is an error. decode also assumes len(src)%4==0, -// since it is meant for internal use. -func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err os.Error) { - for i := 0; i < len(src)/4 && !end; i++ { +// additional data is an error. +func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { + osrc := src + for len(src) > 0 && !end { // Decode quantum using the base64 alphabet var dbuf [4]byte dlen := 4 dbufloop: - for j := 0; j < 4; j++ { - in := src[i*4+j] - if in == '=' && j >= 2 && i == len(src)/4-1 { + for j := 0; j < 4; { + if len(src) == 0 { + return n, false, CorruptInputError(len(osrc) - len(src) - j) + } + in := src[0] + src = src[1:] + if in == '\r' || in == '\n' { + // Ignore this character. + continue + } + if in == '=' && j >= 2 && len(src) < 4 { // We've reached the end and there's // padding - if src[i*4+3] != '=' { - return n, false, CorruptInputError(i*4 + 2) + if len(src) > 0 && src[0] != '=' { + return n, false, CorruptInputError(len(osrc) - len(src) - 1) } dlen = j end = true @@ -232,22 +239,24 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err os.Error) { } dbuf[j] = enc.decodeMap[in] if dbuf[j] == 0xFF { - return n, false, CorruptInputError(i*4 + j) + return n, false, CorruptInputError(len(osrc) - len(src) - 1) } + j++ } // Pack 4x 6-bit source blocks into 3 byte destination // quantum switch dlen { case 4: - dst[i*3+2] = dbuf[2]<<6 | dbuf[3] + dst[2] = dbuf[2]<<6 | dbuf[3] fallthrough case 3: - dst[i*3+1] = dbuf[1]<<4 | dbuf[2]>>2 + dst[1] = dbuf[1]<<4 | dbuf[2]>>2 fallthrough case 2: - dst[i*3+0] = dbuf[0]<<2 | dbuf[1]>>4 + dst[0] = dbuf[0]<<2 | dbuf[1]>>4 } + dst = dst[3:] n += dlen - 1 } @@ -258,24 +267,21 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err os.Error) { // DecodedLen(len(src)) bytes to dst and returns the number of bytes // written. If src contains invalid base64 data, it will return the // number of bytes successfully written and CorruptInputError. -func (enc *Encoding) Decode(dst, src []byte) (n int, err os.Error) { - if len(src)%4 != 0 { - return 0, CorruptInputError(len(src) / 4 * 4) - } - +// New line characters (\r and \n) are ignored. +func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { n, _, err = enc.decode(dst, src) return } // DecodeString returns the bytes represented by the base64 string s. -func (enc *Encoding) DecodeString(s string) ([]byte, os.Error) { +func (enc *Encoding) DecodeString(s string) ([]byte, error) { dbuf := make([]byte, enc.DecodedLen(len(s))) n, err := enc.Decode(dbuf, []byte(s)) return dbuf[:n], err } type decoder struct { - err os.Error + err error enc *Encoding r io.Reader end bool // saw end of message @@ -285,7 +291,7 @@ type decoder struct { outbuf [1024 / 4 * 3]byte } -func (d *decoder) Read(p []byte) (n int, err os.Error) { +func (d *decoder) Read(p []byte) (n int, err error) { if d.err != nil { return 0, d.err } diff --git a/src/pkg/encoding/base64/base64_test.go b/src/pkg/encoding/base64/base64_test.go index c163dae84..3e9a84393 100644 --- a/src/pkg/encoding/base64/base64_test.go +++ b/src/pkg/encoding/base64/base64_test.go @@ -6,8 +6,8 @@ package base64 import ( "bytes" + "io" "io/ioutil" - "os" "testing" ) @@ -82,11 +82,11 @@ func TestEncoderBuffering(t *testing.T) { end = len(input) } n, err := encoder.Write(input[pos:end]) - testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, os.Error(nil)) + testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, error(nil)) testEqual(t, "Write(%q) gave length %v, want %v", input[pos:end], n, end-pos) } err := encoder.Close() - testEqual(t, "Close gave error %v, want %v", err, os.Error(nil)) + testEqual(t, "Close gave error %v, want %v", err, error(nil)) testEqual(t, "Encoding/%d of %q = %q, want %q", bs, bigtest.decoded, bb.String(), bigtest.encoded) } } @@ -95,7 +95,7 @@ func TestDecode(t *testing.T) { for _, p := range pairs { dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))) count, end, err := StdEncoding.decode(dbuf, []byte(p.encoded)) - testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, os.Error(nil)) + testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, error(nil)) testEqual(t, "Decode(%q) = length %v, want %v", p.encoded, count, len(p.decoded)) if len(p.encoded) > 0 { testEqual(t, "Decode(%q) = end %v, want %v", p.encoded, end, (p.encoded[len(p.encoded)-1] == '=')) @@ -103,7 +103,7 @@ func TestDecode(t *testing.T) { testEqual(t, "Decode(%q) = %q, want %q", p.encoded, string(dbuf[0:count]), p.decoded) dbuf, err = StdEncoding.DecodeString(p.encoded) - testEqual(t, "DecodeString(%q) = error %v, want %v", p.encoded, err, os.Error(nil)) + testEqual(t, "DecodeString(%q) = error %v, want %v", p.encoded, err, error(nil)) testEqual(t, "DecodeString(%q) = %q, want %q", string(dbuf), p.decoded) } } @@ -113,15 +113,15 @@ func TestDecoder(t *testing.T) { decoder := NewDecoder(StdEncoding, bytes.NewBufferString(p.encoded)) dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))) count, err := decoder.Read(dbuf) - if err != nil && err != os.EOF { + if err != nil && err != io.EOF { t.Fatal("Read failed", err) } testEqual(t, "Read from %q = length %v, want %v", p.encoded, count, len(p.decoded)) testEqual(t, "Decoding of %q = %q, want %q", p.encoded, string(dbuf[0:count]), p.decoded) - if err != os.EOF { + if err != io.EOF { count, err = decoder.Read(dbuf) } - testEqual(t, "Read from %q = %v, want %v", p.encoded, err, os.EOF) + testEqual(t, "Read from %q = %v, want %v", p.encoded, err, io.EOF) } } @@ -132,7 +132,7 @@ func TestDecoderBuffering(t *testing.T) { var total int for total = 0; total < len(bigtest.decoded); { n, err := decoder.Read(buf[total : total+bs]) - testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, os.Error(nil)) + testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, error(nil)) total += n } testEqual(t, "Decoding/%d of %q = %q, want %q", bs, bigtest.encoded, string(buf[0:total]), bigtest.decoded) @@ -197,3 +197,29 @@ func TestBig(t *testing.T) { t.Errorf("Decode(Encode(%d-byte string)) failed at offset %d", n, i) } } + +func TestNewLineCharacters(t *testing.T) { + // Each of these should decode to the string "sure", without errors. + const expected = "sure" + examples := []string{ + "c3VyZQ==", + "c3VyZQ==\r", + "c3VyZQ==\n", + "c3VyZQ==\r\n", + "c3VyZ\r\nQ==", + "c3V\ryZ\nQ==", + "c3V\nyZ\rQ==", + "c3VyZ\nQ==", + "c3VyZQ\n==", + } + for _, e := range examples { + buf, err := StdEncoding.DecodeString(e) + if err != nil { + t.Errorf("Decode(%q) failed: %v", e, err) + continue + } + if s := string(buf); s != expected { + t.Errorf("Decode(%q) = %q, want %q", e, s, expected) + } + } +} diff --git a/src/pkg/encoding/binary/Makefile b/src/pkg/encoding/binary/Makefile deleted file mode 100644 index dc46abe90..000000000 --- a/src/pkg/encoding/binary/Makefile +++ /dev/null @@ -1,11 +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 ../../../Make.inc - -TARG=encoding/binary -GOFILES=\ - binary.go\ - -include ../../../Make.pkg diff --git a/src/pkg/encoding/binary/binary.go b/src/pkg/encoding/binary/binary.go index 8e55cb23b..712e490e6 100644 --- a/src/pkg/encoding/binary/binary.go +++ b/src/pkg/encoding/binary/binary.go @@ -2,41 +2,45 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package binary implements translation between -// unsigned integer values and byte sequences -// and the reading and writing of fixed-size values. +// Package binary implements translation between numbers and byte sequences +// and encoding and decoding of varints. +// +// Numbers are translated by reading and writing fixed-size values. +// A fixed-size value is either a fixed-size arithmetic +// type (int8, uint8, int16, float32, complex64, ...) +// or an array or struct containing only fixed-size values. +// +// Varints are a method of encoding integers using one or more bytes; +// numbers with smaller absolute value take a smaller number of bytes. +// For a specification, see http://code.google.com/apis/protocolbuffers/docs/encoding.html. package binary import ( - "math" + "errors" "io" - "os" + "math" "reflect" ) // A ByteOrder specifies how to convert byte sequences into // 16-, 32-, or 64-bit unsigned integers. type ByteOrder interface { - Uint16(b []byte) uint16 - Uint32(b []byte) uint32 - Uint64(b []byte) uint64 + Uint16([]byte) uint16 + Uint32([]byte) uint32 + Uint64([]byte) uint64 PutUint16([]byte, uint16) PutUint32([]byte, uint32) PutUint64([]byte, uint64) String() string } -// This is byte instead of struct{} so that it can be compared, -// allowing, e.g., order == binary.LittleEndian. -type unused byte - // LittleEndian is the little-endian implementation of ByteOrder. var LittleEndian littleEndian // BigEndian is the big-endian implementation of ByteOrder. var BigEndian bigEndian -type littleEndian unused +type littleEndian struct{} func (littleEndian) Uint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 } @@ -76,7 +80,7 @@ func (littleEndian) String() string { return "LittleEndian" } func (littleEndian) GoString() string { return "binary.LittleEndian" } -type bigEndian unused +type bigEndian struct{} func (bigEndian) Uint16(b []byte) uint16 { return uint16(b[1]) | uint16(b[0])<<8 } @@ -119,12 +123,9 @@ func (bigEndian) GoString() string { return "binary.BigEndian" } // Read reads structured binary data from r into data. // Data must be a pointer to a fixed-size value or a slice // of fixed-size values. -// A fixed-size value is either a fixed-size arithmetic -// type (int8, uint8, int16, float32, complex64, ...) -// or an array or struct containing only fixed-size values. // Bytes read from r are decoded using the specified byte order // and written to successive fields of the data. -func Read(r io.Reader, order ByteOrder, data interface{}) os.Error { +func Read(r io.Reader, order ByteOrder, data interface{}) error { // Fast path for basic types. if n := intDestSize(data); n != 0 { var b [8]byte @@ -161,11 +162,11 @@ func Read(r io.Reader, order ByteOrder, data interface{}) os.Error { case reflect.Slice: v = d default: - return os.NewError("binary.Read: invalid type " + d.Type().String()) + return errors.New("binary.Read: invalid type " + d.Type().String()) } - size := TotalSize(v) + size := dataSize(v) if size < 0 { - return os.NewError("binary.Read: invalid type " + v.Type().String()) + return errors.New("binary.Read: invalid type " + v.Type().String()) } d := &decoder{order: order, buf: make([]byte, size)} if _, err := io.ReadFull(r, d.buf); err != nil { @@ -176,14 +177,11 @@ func Read(r io.Reader, order ByteOrder, data interface{}) os.Error { } // Write writes the binary representation of data into w. -// Data must be a fixed-size value or a pointer to -// a fixed-size value. -// A fixed-size value is either a fixed-size arithmetic -// type (int8, uint8, int16, float32, complex64, ...) -// or an array or struct containing only fixed-size values. +// Data must be a fixed-size value or a slice of fixed-size +// 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. -func Write(w io.Writer, order ByteOrder, data interface{}) os.Error { +func Write(w io.Writer, order ByteOrder, data interface{}) error { // Fast path for basic types. var b [8]byte var bs []byte @@ -242,9 +240,9 @@ func Write(w io.Writer, order ByteOrder, data interface{}) os.Error { return err } v := reflect.Indirect(reflect.ValueOf(data)) - size := TotalSize(v) + size := dataSize(v) if size < 0 { - return os.NewError("binary.Write: invalid type " + v.Type().String()) + return errors.New("binary.Write: invalid type " + v.Type().String()) } buf := make([]byte, size) e := &encoder{order: order, buf: buf} @@ -253,7 +251,17 @@ func Write(w io.Writer, order ByteOrder, data interface{}) os.Error { return err } -func TotalSize(v reflect.Value) int { +// 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))) +} + +// 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 { if v.Kind() == reflect.Slice { elem := sizeof(v.Type().Elem()) if elem < 0 { @@ -369,6 +377,7 @@ func (d *decoder) value(v reflect.Value) { for i := 0; i < l; i++ { d.value(v.Index(i)) } + case reflect.Struct: l := v.NumField() for i := 0; i < l; i++ { @@ -424,11 +433,13 @@ func (e *encoder) value(v reflect.Value) { for i := 0; i < l; i++ { e.value(v.Index(i)) } + case reflect.Struct: l := v.NumField() for i := 0; i < l; i++ { e.value(v.Field(i)) } + case reflect.Slice: l := v.Len() for i := 0; i < l; i++ { diff --git a/src/pkg/encoding/binary/binary_test.go b/src/pkg/encoding/binary/binary_test.go index b266996f6..ff361b7e3 100644 --- a/src/pkg/encoding/binary/binary_test.go +++ b/src/pkg/encoding/binary/binary_test.go @@ -5,9 +5,8 @@ package binary import ( - "io" - "os" "bytes" + "io" "math" "reflect" "testing" @@ -99,7 +98,7 @@ var little = []byte{ var src = []byte{1, 2, 3, 4, 5, 6, 7, 8} var res = []int32{0x01020304, 0x05060708} -func checkResult(t *testing.T, dir string, order, err os.Error, have, want interface{}) { +func checkResult(t *testing.T, dir string, order ByteOrder, err error, have, want interface{}) { if err != nil { t.Errorf("%v %v: %v", dir, order, err) return @@ -166,17 +165,48 @@ type byteSliceReader struct { remain []byte } -func (br *byteSliceReader) Read(p []byte) (int, os.Error) { +func (br *byteSliceReader) Read(p []byte) (int, error) { n := copy(p, br.remain) br.remain = br.remain[n:] return n, nil } -func BenchmarkRead(b *testing.B) { +func BenchmarkReadSlice1000Int32s(b *testing.B) { + bsr := &byteSliceReader{} + slice := make([]int32, 1000) + buf := make([]byte, len(slice)*4) + b.SetBytes(int64(len(buf))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + bsr.remain = buf + Read(bsr, BigEndian, slice) + } +} + +func BenchmarkReadStruct(b *testing.B) { + bsr := &byteSliceReader{} + var buf bytes.Buffer + Write(&buf, BigEndian, &s) + n := dataSize(reflect.ValueOf(s)) + b.SetBytes(int64(n)) + t := s + b.ResetTimer() + for i := 0; i < b.N; i++ { + bsr.remain = buf.Bytes() + Read(bsr, BigEndian, &t) + } + b.StopTimer() + if !reflect.DeepEqual(s, t) { + b.Fatal("no match") + } +} + +func BenchmarkReadInts(b *testing.B) { var ls Struct bsr := &byteSliceReader{} var r io.Reader = bsr - + b.SetBytes(2 * (1 + 2 + 4 + 8)) + b.ResetTimer() for i := 0; i < b.N; i++ { bsr.remain = big Read(r, BigEndian, &ls.Int8) @@ -197,25 +227,19 @@ func BenchmarkRead(b *testing.B) { for i := range want.Array { want.Array[i] = 0 } + b.StopTimer() if !reflect.DeepEqual(ls, want) { panic("no match") } } -func BenchmarkWrite(b *testing.B) { +func BenchmarkWriteInts(b *testing.B) { buf := new(bytes.Buffer) var w io.Writer = buf - + b.SetBytes(2 * (1 + 2 + 4 + 8)) + b.ResetTimer() for i := 0; i < b.N; i++ { buf.Reset() - Write(w, BigEndian, &s.Int8) - Write(w, BigEndian, &s.Int16) - Write(w, BigEndian, &s.Int32) - Write(w, BigEndian, &s.Int64) - Write(w, BigEndian, &s.Uint8) - Write(w, BigEndian, &s.Uint16) - Write(w, BigEndian, &s.Uint32) - Write(w, BigEndian, &s.Uint64) Write(w, BigEndian, s.Int8) Write(w, BigEndian, s.Int16) Write(w, BigEndian, s.Int32) @@ -225,11 +249,8 @@ func BenchmarkWrite(b *testing.B) { Write(w, BigEndian, s.Uint32) Write(w, BigEndian, s.Uint64) } - - if !bytes.Equal(buf.Bytes()[:30], big[:30]) { - panic("first half doesn't match") - } - if !bytes.Equal(buf.Bytes()[30:], big[:30]) { - panic("second half doesn't match") + b.StopTimer() + if !bytes.Equal(buf.Bytes(), big[:30]) { + b.Fatalf("first half doesn't match: %x %x", buf.Bytes(), big[:30]) } } diff --git a/src/pkg/encoding/binary/example_test.go b/src/pkg/encoding/binary/example_test.go new file mode 100644 index 000000000..dec12ebf5 --- /dev/null +++ b/src/pkg/encoding/binary/example_test.go @@ -0,0 +1,52 @@ +// 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 binary_test + +import ( + "bytes" + "encoding/binary" + "fmt" + "math" +) + +func ExampleWrite() { + buf := new(bytes.Buffer) + var pi float64 = math.Pi + err := binary.Write(buf, binary.LittleEndian, pi) + if err != nil { + fmt.Println("binary.Write failed:", err) + } + fmt.Printf("% x", buf.Bytes()) + // Output: 18 2d 44 54 fb 21 09 40 +} + +func ExampleWrite_multi() { + buf := new(bytes.Buffer) + var data = []interface{}{ + uint16(61374), + int8(-54), + uint8(254), + } + for _, v := range data { + err := binary.Write(buf, binary.LittleEndian, v) + if err != nil { + fmt.Println("binary.Write failed:", err) + } + } + fmt.Printf("%x", buf.Bytes()) + // Output: beefcafe +} + +func ExampleRead() { + var pi float64 + b := []byte{0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40} + buf := bytes.NewBuffer(b) + err := binary.Read(buf, binary.LittleEndian, &pi) + if err != nil { + fmt.Println("binary.Read failed:", err) + } + fmt.Print(pi) + // Output: 3.141592653589793 +} diff --git a/src/pkg/encoding/binary/varint.go b/src/pkg/encoding/binary/varint.go new file mode 100644 index 000000000..b756afdd0 --- /dev/null +++ b/src/pkg/encoding/binary/varint.go @@ -0,0 +1,134 @@ +// 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 binary + +// This file implements "varint" encoding of 64-bit integers. +// The encoding is: +// - unsigned integers are serialized 7 bits at a time, starting with the +// least significant bits +// - the most significant bit (msb) in each output byte indicates if there +// is a continuation byte (msb = 1) +// - signed integers are mapped to unsigned integers using "zig-zag" +// encoding: Positive values x are written as 2*x + 0, negative values +// are written as 2*(^x) + 1; that is, negative numbers are complemented +// and whether to complement is encoded in bit 0. +// +// Design note: +// At most 10 bytes are needed for 64-bit values. The encoding could +// be more dense: a full 64-bit value needs an extra byte just to hold bit 63. +// Instead, the msb of the previous byte could be used to hold bit 63 since we +// know there can't be more than 64 bits. This is a trivial improvement and +// would reduce the maximum encoding length to 9 bytes. However, it breaks the +// invariant that the msb is always the "continuation bit" and thus makes the +// format incompatible with a varint encoding for larger numbers (say 128-bit). + +import ( + "errors" + "io" +) + +// MaxVarintLenN is the maximum length of a varint-encoded N-bit integer. +const ( + MaxVarintLen16 = 3 + MaxVarintLen32 = 5 + MaxVarintLen64 = 10 +) + +// PutUvarint encodes a uint64 into buf and returns the number of bytes written. +// If the buffer is too small, PutUvarint will panic. +func PutUvarint(buf []byte, x uint64) int { + i := 0 + for x >= 0x80 { + buf[i] = byte(x) | 0x80 + x >>= 7 + i++ + } + buf[i] = byte(x) + return i + 1 +} + +// Uvarint decodes a uint64 from buf and returns that value and the +// number of bytes read (> 0). If an error occurred, the value is 0 +// and the number of bytes n is <= 0 meaning: +// +// n == 0: buf too small +// n < 0: value larger than 64 bits (overflow) +// and -n is the number of bytes read +// +func Uvarint(buf []byte) (uint64, int) { + var x uint64 + var s uint + for i, b := range buf { + if b < 0x80 { + if i > 9 || i == 9 && b > 1 { + return 0, -(i + 1) // overflow + } + return x | uint64(b)< 0). If an error occurred, the value is 0 +// and the number of bytes n is <= 0 with the following meaning: +// +// n == 0: buf too small +// n < 0: value larger than 64 bits (overflow) +// and -n is the number of bytes read +// +func Varint(buf []byte) (int64, int) { + ux, n := Uvarint(buf) // ok to continue in presence of error + x := int64(ux >> 1) + if ux&1 != 0 { + x = ^x + } + return x, n +} + +var overflow = errors.New("binary: varint overflows a 64-bit integer") + +// ReadUvarint reads an encoded unsigned integer from r and returns it as a uint64. +func ReadUvarint(r io.ByteReader) (uint64, error) { + var x uint64 + var s uint + for i := 0; ; i++ { + b, err := r.ReadByte() + if err != nil { + return x, err + } + if b < 0x80 { + if i > 9 || i == 9 && b > 1 { + return x, overflow + } + return x | uint64(b)<> 1) + if ux&1 != 0 { + x = ^x + } + return x, err +} diff --git a/src/pkg/encoding/binary/varint_test.go b/src/pkg/encoding/binary/varint_test.go new file mode 100644 index 000000000..9476bd5fb --- /dev/null +++ b/src/pkg/encoding/binary/varint_test.go @@ -0,0 +1,168 @@ +// 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 binary + +import ( + "bytes" + "io" + "testing" +) + +func testConstant(t *testing.T, w uint, max int) { + buf := make([]byte, MaxVarintLen64) + n := PutUvarint(buf, 1< 0 { + if len(record) != r.FieldsPerRecord { + r.column = 0 // report at start of record + return record, r.error(ErrFieldCount) + } + } else if r.FieldsPerRecord == 0 { + r.FieldsPerRecord = len(record) + } + return record, nil +} + +// ReadAll reads all the remaining records from r. +// Each record is a slice of fields. +// A successful call returns err == nil, not err == EOF. Because ReadAll is +// defined to read until EOF, it does not treat end of file as an error to be +// reported. +func (r *Reader) ReadAll() (records [][]string, err error) { + for { + record, err := r.Read() + if err == io.EOF { + return records, nil + } + if err != nil { + return nil, err + } + records = append(records, record) + } + panic("unreachable") +} + +// readRune reads one rune from r, folding \r\n to \n and keeping track +// of how far into the line we have read. r.column will point to the start +// of this rune, not the end of this rune. +func (r *Reader) readRune() (rune, error) { + r1, _, err := r.r.ReadRune() + + // Handle \r\n here. We make the simplifying assumption that + // anytime \r is followed by \n that it can be folded to \n. + // We will not detect files which contain both \r\n and bare \n. + if r1 == '\r' { + r1, _, err = r.r.ReadRune() + if err == nil { + if r1 != '\n' { + r.r.UnreadRune() + r1 = '\r' + } + } + } + r.column++ + return r1, err +} + +// unreadRune puts the last rune read from r back. +func (r *Reader) unreadRune() { + r.r.UnreadRune() + r.column-- +} + +// skip reads runes up to and including the rune delim or until error. +func (r *Reader) skip(delim rune) error { + for { + r1, err := r.readRune() + if err != nil { + return err + } + if r1 == delim { + return nil + } + } + panic("unreachable") +} + +// parseRecord reads and parses a single csv record from r. +func (r *Reader) parseRecord() (fields []string, err error) { + // Each record starts on a new line. We increment our line + // number (lines start at 1, not 0) and set column to -1 + // so as we increment in readRune it points to the character we read. + r.line++ + r.column = -1 + + // Peek at the first rune. If it is an error we are done. + // If we are support comments and it is the comment character + // then skip to the end of line. + + r1, _, err := r.r.ReadRune() + if err != nil { + return nil, err + } + + if r.Comment != 0 && r1 == r.Comment { + return nil, r.skip('\n') + } + r.r.UnreadRune() + + // At this point we have at least one field. + for { + haveField, delim, err := r.parseField() + if haveField { + fields = append(fields, r.field.String()) + } + if delim == '\n' || err == io.EOF { + return fields, err + } else if err != nil { + return nil, err + } + } + panic("unreachable") +} + +// parseField parses the next field in the record. The read field is +// located in r.field. Delim is the first character not part of the field +// (r.Comma or '\n'). +func (r *Reader) parseField() (haveField bool, delim rune, err error) { + r.field.Reset() + + r1, err := r.readRune() + if err != nil { + // If we have EOF and are not at the start of a line + // then we return the empty field. We have already + // checked for trailing commas if needed. + if err == io.EOF && r.column != 0 { + return true, 0, err + } + return false, 0, err + } + + if r.TrimLeadingSpace { + for r1 != '\n' && unicode.IsSpace(r1) { + r1, err = r.readRune() + if err != nil { + return false, 0, err + } + } + } + + switch r1 { + case r.Comma: + // will check below + + case '\n': + // We are a trailing empty field or a blank line + if r.column == 0 { + return false, r1, nil + } + return true, r1, nil + + case '"': + // quoted field + Quoted: + for { + r1, err = r.readRune() + if err != nil { + if err == io.EOF { + if r.LazyQuotes { + return true, 0, err + } + return false, 0, r.error(ErrQuote) + } + return false, 0, err + } + switch r1 { + case '"': + r1, err = r.readRune() + if err != nil || r1 == r.Comma { + break Quoted + } + if r1 == '\n' { + return true, r1, nil + } + if r1 != '"' { + if !r.LazyQuotes { + r.column-- + return false, 0, r.error(ErrQuote) + } + // accept the bare quote + r.field.WriteRune('"') + } + case '\n': + r.line++ + r.column = -1 + } + r.field.WriteRune(r1) + } + + default: + // unquoted field + for { + r.field.WriteRune(r1) + r1, err = r.readRune() + if err != nil || r1 == r.Comma { + break + } + if r1 == '\n' { + return true, r1, nil + } + if !r.LazyQuotes && r1 == '"' { + return false, 0, r.error(ErrBareQuote) + } + } + } + + if err != nil { + if err == io.EOF { + return true, 0, err + } + return false, 0, err + } + + if !r.TrailingComma { + // We don't allow trailing commas. See if we + // are at the end of the line (being mindful + // of trimming spaces). + c := r.column + r1, err = r.readRune() + if r.TrimLeadingSpace { + for r1 != '\n' && unicode.IsSpace(r1) { + r1, err = r.readRune() + if err != nil { + break + } + } + } + if err == io.EOF || r1 == '\n' { + r.column = c // report the comma + return false, 0, r.error(ErrTrailingComma) + } + r.unreadRune() + } + return true, r1, nil +} diff --git a/src/pkg/encoding/csv/reader_test.go b/src/pkg/encoding/csv/reader_test.go new file mode 100644 index 000000000..5fd84a76b --- /dev/null +++ b/src/pkg/encoding/csv/reader_test.go @@ -0,0 +1,281 @@ +// 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 csv + +import ( + "reflect" + "strings" + "testing" +) + +var readTests = []struct { + Name string + Input string + Output [][]string + UseFieldsPerRecord bool // false (default) means FieldsPerRecord is -1 + + // These fields are copied into the Reader + Comma rune + Comment rune + FieldsPerRecord int + LazyQuotes bool + TrailingComma bool + TrimLeadingSpace bool + + Error string + Line int // Expected error line if != 0 + Column int // Expected error column if line != 0 +}{ + { + Name: "Simple", + Input: "a,b,c\n", + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "CRLF", + Input: "a,b\r\nc,d\r\n", + Output: [][]string{{"a", "b"}, {"c", "d"}}, + }, + { + Name: "BareCR", + Input: "a,b\rc,d\r\n", + Output: [][]string{{"a", "b\rc", "d"}}, + }, + { + Name: "RFC4180test", + UseFieldsPerRecord: true, + Input: `#field1,field2,field3 +"aaa","bb +b","ccc" +"a,a","b""bb","ccc" +zzz,yyy,xxx +`, + Output: [][]string{ + {"#field1", "field2", "field3"}, + {"aaa", "bb\nb", "ccc"}, + {"a,a", `b"bb`, "ccc"}, + {"zzz", "yyy", "xxx"}, + }, + }, + { + Name: "NoEOLTest", + Input: "a,b,c", + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "Semicolon", + Comma: ';', + Input: "a;b;c\n", + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "MultiLine", + Input: `"two +line","one line","three +line +field"`, + Output: [][]string{{"two\nline", "one line", "three\nline\nfield"}}, + }, + { + Name: "BlankLine", + Input: "a,b,c\n\nd,e,f\n\n", + Output: [][]string{ + {"a", "b", "c"}, + {"d", "e", "f"}, + }, + }, + { + Name: "TrimSpace", + Input: " a, b, c\n", + TrimLeadingSpace: true, + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "LeadingSpace", + Input: " a, b, c\n", + Output: [][]string{{" a", " b", " c"}}, + }, + { + Name: "Comment", + Comment: '#', + Input: "#1,2,3\na,b,c\n#comment", + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "NoComment", + Input: "#1,2,3\na,b,c", + Output: [][]string{{"#1", "2", "3"}, {"a", "b", "c"}}, + }, + { + Name: "LazyQuotes", + LazyQuotes: true, + Input: `a "word","1"2",a","b`, + Output: [][]string{{`a "word"`, `1"2`, `a"`, `b`}}, + }, + { + Name: "BareQuotes", + LazyQuotes: true, + Input: `a "word","1"2",a"`, + Output: [][]string{{`a "word"`, `1"2`, `a"`}}, + }, + { + Name: "BareDoubleQuotes", + LazyQuotes: true, + Input: `a""b,c`, + Output: [][]string{{`a""b`, `c`}}, + }, + { + Name: "BadDoubleQuotes", + Input: `a""b,c`, + Error: `bare " in non-quoted-field`, Line: 1, Column: 1, + }, + { + Name: "TrimQuote", + Input: ` "a"," b",c`, + TrimLeadingSpace: true, + Output: [][]string{{"a", " b", "c"}}, + }, + { + Name: "BadBareQuote", + Input: `a "word","b"`, + Error: `bare " in non-quoted-field`, Line: 1, Column: 2, + }, + { + Name: "BadTrailingQuote", + Input: `"a word",b"`, + Error: `bare " in non-quoted-field`, Line: 1, Column: 10, + }, + { + Name: "ExtraneousQuote", + Input: `"a "word","b"`, + Error: `extraneous " in field`, Line: 1, Column: 3, + }, + { + Name: "BadFieldCount", + UseFieldsPerRecord: true, + Input: "a,b,c\nd,e", + Error: "wrong number of fields", Line: 2, + }, + { + Name: "BadFieldCount1", + UseFieldsPerRecord: true, + FieldsPerRecord: 2, + Input: `a,b,c`, + Error: "wrong number of fields", Line: 1, + }, + { + Name: "FieldCount", + Input: "a,b,c\nd,e", + Output: [][]string{{"a", "b", "c"}, {"d", "e"}}, + }, + { + Name: "BadTrailingCommaEOF", + Input: "a,b,c,", + Error: "extra delimiter at end of line", Line: 1, Column: 5, + }, + { + Name: "BadTrailingCommaEOL", + Input: "a,b,c,\n", + Error: "extra delimiter at end of line", Line: 1, Column: 5, + }, + { + Name: "BadTrailingCommaSpaceEOF", + TrimLeadingSpace: true, + Input: "a,b,c, ", + Error: "extra delimiter at end of line", Line: 1, Column: 5, + }, + { + Name: "BadTrailingCommaSpaceEOL", + TrimLeadingSpace: true, + Input: "a,b,c, \n", + Error: "extra delimiter at end of line", Line: 1, Column: 5, + }, + { + Name: "BadTrailingCommaLine3", + TrimLeadingSpace: true, + Input: "a,b,c\nd,e,f\ng,hi,", + Error: "extra delimiter at end of line", Line: 3, Column: 4, + }, + { + Name: "NotTrailingComma3", + Input: "a,b,c, \n", + Output: [][]string{{"a", "b", "c", " "}}, + }, + { + Name: "CommaFieldTest", + TrailingComma: true, + Input: `x,y,z,w +x,y,z, +x,y,, +x,,, +,,, +"x","y","z","w" +"x","y","z","" +"x","y","","" +"x","","","" +"","","","" +`, + Output: [][]string{ + {"x", "y", "z", "w"}, + {"x", "y", "z", ""}, + {"x", "y", "", ""}, + {"x", "", "", ""}, + {"", "", "", ""}, + {"x", "y", "z", "w"}, + {"x", "y", "z", ""}, + {"x", "y", "", ""}, + {"x", "", "", ""}, + {"", "", "", ""}, + }, + }, + { + Name: "Issue 2366", + TrailingComma: true, + TrimLeadingSpace: true, + Input: "a,b,\nc,d,e", + Output: [][]string{ + {"a", "b", ""}, + {"c", "d", "e"}, + }, + }, + { + Name: "Issue 2366a", + TrailingComma: false, + TrimLeadingSpace: true, + Input: "a,b,\nc,d,e", + Error: "extra delimiter at end of line", + }, +} + +func TestRead(t *testing.T) { + for _, tt := range readTests { + r := NewReader(strings.NewReader(tt.Input)) + r.Comment = tt.Comment + if tt.UseFieldsPerRecord { + r.FieldsPerRecord = tt.FieldsPerRecord + } else { + r.FieldsPerRecord = -1 + } + r.LazyQuotes = tt.LazyQuotes + r.TrailingComma = tt.TrailingComma + r.TrimLeadingSpace = tt.TrimLeadingSpace + if tt.Comma != 0 { + r.Comma = tt.Comma + } + out, err := r.ReadAll() + perr, _ := err.(*ParseError) + if tt.Error != "" { + if err == nil || !strings.Contains(err.Error(), tt.Error) { + t.Errorf("%s: error %v, want error %q", tt.Name, err, tt.Error) + } else if tt.Line != 0 && (tt.Line != perr.Line || tt.Column != perr.Column) { + t.Errorf("%s: error at %d:%d expected %d:%d", tt.Name, perr.Line, perr.Column, tt.Line, tt.Column) + } + } else if err != nil { + t.Errorf("%s: unexpected error %v", tt.Name, err) + } else if !reflect.DeepEqual(out, tt.Output) { + t.Errorf("%s: out=%q want %q", tt.Name, out, tt.Output) + } + } +} diff --git a/src/pkg/encoding/csv/writer.go b/src/pkg/encoding/csv/writer.go new file mode 100644 index 000000000..c4dcba566 --- /dev/null +++ b/src/pkg/encoding/csv/writer.go @@ -0,0 +1,121 @@ +// 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 csv + +import ( + "bufio" + "io" + "strings" + "unicode" + "unicode/utf8" +) + +// A Writer writes records to a CSV encoded file. +// +// As returned by NewWriter, a Writer writes records terminated by a +// newline and uses ',' as the field delimiter. The exported fields can be +// changed to customize the details before the first call to Write or WriteAll. +// +// Comma is the field delimiter. +// +// 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) + UseCRLF bool // True to use \r\n as the line terminator + w *bufio.Writer +} + +// NewWriter returns a new Writer that writes to w. +func NewWriter(w io.Writer) *Writer { + return &Writer{ + Comma: ',', + w: bufio.NewWriter(w), + } +} + +// Writer writes a single CSV record to w along with any necessary quoting. +// A record is a slice of strings with each string being one field. +func (w *Writer) Write(record []string) (err error) { + for n, field := range record { + if n > 0 { + if _, err = w.w.WriteRune(w.Comma); err != nil { + return + } + } + + // If we don't have to have a quoted field then just + // write out the field and continue to the next field. + if !w.fieldNeedsQuotes(field) { + if _, err = w.w.WriteString(field); err != nil { + return + } + continue + } + if err = w.w.WriteByte('"'); err != nil { + return + } + + for _, r1 := range field { + switch r1 { + case '"': + _, err = w.w.WriteString(`""`) + case '\r': + if !w.UseCRLF { + err = w.w.WriteByte('\r') + } + case '\n': + if w.UseCRLF { + _, err = w.w.WriteString("\r\n") + } else { + err = w.w.WriteByte('\n') + } + default: + _, err = w.w.WriteRune(r1) + } + if err != nil { + return + } + } + + if err = w.w.WriteByte('"'); err != nil { + return + } + } + if w.UseCRLF { + _, err = w.w.WriteString("\r\n") + } else { + err = w.w.WriteByte('\n') + } + return +} + +// Flush writes any buffered data to the underlying io.Writer. +func (w *Writer) Flush() { + w.w.Flush() +} + +// 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 + } + } + w.Flush() + return nil +} + +// fieldNeedsQuotes returns true if our field must be enclosed in quotes. +// Empty fields, files with a Comma, fields with a quote or newline, and +// fields which start with a space must be enclosed in quotes. +func (w *Writer) fieldNeedsQuotes(field string) bool { + if len(field) == 0 || strings.IndexRune(field, w.Comma) >= 0 || strings.IndexAny(field, "\"\r\n") >= 0 { + return true + } + + r1, _ := utf8.DecodeRuneInString(field) + return unicode.IsSpace(r1) +} diff --git a/src/pkg/encoding/csv/writer_test.go b/src/pkg/encoding/csv/writer_test.go new file mode 100644 index 000000000..578959007 --- /dev/null +++ b/src/pkg/encoding/csv/writer_test.go @@ -0,0 +1,44 @@ +// 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 csv + +import ( + "bytes" + "testing" +) + +var writeTests = []struct { + Input [][]string + Output string + UseCRLF bool +}{ + {Input: [][]string{{"abc"}}, Output: "abc\n"}, + {Input: [][]string{{"abc"}}, Output: "abc\r\n", UseCRLF: true}, + {Input: [][]string{{`"abc"`}}, Output: `"""abc"""` + "\n"}, + {Input: [][]string{{`a"b`}}, Output: `"a""b"` + "\n"}, + {Input: [][]string{{`"a"b"`}}, Output: `"""a""b"""` + "\n"}, + {Input: [][]string{{" abc"}}, Output: `" abc"` + "\n"}, + {Input: [][]string{{"abc,def"}}, Output: `"abc,def"` + "\n"}, + {Input: [][]string{{"abc", "def"}}, Output: "abc,def\n"}, + {Input: [][]string{{"abc"}, {"def"}}, Output: "abc\ndef\n"}, + {Input: [][]string{{"abc\ndef"}}, Output: "\"abc\ndef\"\n"}, + {Input: [][]string{{"abc\ndef"}}, Output: "\"abc\r\ndef\"\r\n", UseCRLF: true}, +} + +func TestWrite(t *testing.T) { + for n, tt := range writeTests { + b := &bytes.Buffer{} + f := NewWriter(b) + f.UseCRLF = tt.UseCRLF + err := f.WriteAll(tt.Input) + if err != nil { + t.Errorf("Unexpected error: %s\n", err) + } + out := b.String() + if out != tt.Output { + t.Errorf("#%d: out=%q want %q", n, out, tt.Output) + } + } +} diff --git a/src/pkg/encoding/git85/Makefile b/src/pkg/encoding/git85/Makefile deleted file mode 100644 index fbd003454..000000000 --- a/src/pkg/encoding/git85/Makefile +++ /dev/null @@ -1,11 +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 ../../../Make.inc - -TARG=encoding/git85 -GOFILES=\ - git.go\ - -include ../../../Make.pkg diff --git a/src/pkg/encoding/git85/git.go b/src/pkg/encoding/git85/git.go deleted file mode 100644 index 6bb74f46c..000000000 --- a/src/pkg/encoding/git85/git.go +++ /dev/null @@ -1,277 +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. - -// Package git85 implements the radix 85 data encoding -// used in the Git version control system. -package git85 - -import ( - "bytes" - "io" - "os" - "strconv" -) - -type CorruptInputError int64 - -func (e CorruptInputError) String() string { - return "illegal git85 data at input byte " + strconv.Itoa64(int64(e)) -} - -const encode = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~" - -// The decodings are 1+ the actual value, so that the -// default zero value can be used to mean "not valid". -var decode = [256]uint8{ - '0': 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 'A': 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, - 'a': 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, -} - -// Encode encodes src into EncodedLen(len(src)) -// bytes of dst. As a convenience, it returns the number -// of bytes written to dst, but this value is always EncodedLen(len(src)). -// Encode implements the radix 85 encoding used in the -// Git version control tool. -// -// The encoding splits src into chunks of at most 52 bytes -// and encodes each chunk on its own line. -func Encode(dst, src []byte) int { - ndst := 0 - for len(src) > 0 { - n := len(src) - if n > 52 { - n = 52 - } - if n <= 27 { - dst[ndst] = byte('A' + n - 1) - } else { - dst[ndst] = byte('a' + n - 26 - 1) - } - ndst++ - for i := 0; i < n; i += 4 { - var v uint32 - for j := 0; j < 4 && i+j < n; j++ { - v |= uint32(src[i+j]) << uint(24-j*8) - } - for j := 4; j >= 0; j-- { - dst[ndst+j] = encode[v%85] - v /= 85 - } - ndst += 5 - } - dst[ndst] = '\n' - ndst++ - src = src[n:] - } - return ndst -} - -// EncodedLen returns the length of an encoding of n source bytes. -func EncodedLen(n int) int { - if n == 0 { - return 0 - } - // 5 bytes per 4 bytes of input, rounded up. - // 2 extra bytes for each line of 52 src bytes, rounded up. - return (n+3)/4*5 + (n+51)/52*2 -} - -var newline = []byte{'\n'} - -// Decode decodes src into at most MaxDecodedLen(len(src)) -// bytes, returning the actual number of bytes written to dst. -// -// If Decode encounters invalid input, it returns a CorruptInputError. -// -func Decode(dst, src []byte) (n int, err os.Error) { - ndst := 0 - nsrc := 0 - for nsrc < len(src) { - var l int - switch ch := int(src[nsrc]); { - case 'A' <= ch && ch <= 'Z': - l = ch - 'A' + 1 - case 'a' <= ch && ch <= 'z': - l = ch - 'a' + 26 + 1 - default: - return ndst, CorruptInputError(nsrc) - } - if nsrc+1+l > len(src) { - return ndst, CorruptInputError(nsrc) - } - el := (l + 3) / 4 * 5 // encoded len - if nsrc+1+el+1 > len(src) || src[nsrc+1+el] != '\n' { - return ndst, CorruptInputError(nsrc) - } - line := src[nsrc+1 : nsrc+1+el] - for i := 0; i < el; i += 5 { - var v uint32 - for j := 0; j < 5; j++ { - ch := decode[line[i+j]] - if ch == 0 { - return ndst, CorruptInputError(nsrc + 1 + i + j) - } - v = v*85 + uint32(ch-1) - } - for j := 0; j < 4; j++ { - dst[ndst] = byte(v >> 24) - v <<= 8 - ndst++ - } - } - // Last fragment may have run too far (but there was room in dst). - // Back up. - if l%4 != 0 { - ndst -= 4 - l%4 - } - nsrc += 1 + el + 1 - } - return ndst, nil -} - -func MaxDecodedLen(n int) int { return n / 5 * 4 } - -// NewEncoder returns a new Git base85 stream encoder. Data written to -// the returned writer will be encoded and then written to w. -// The Git encoding operates on 52-byte blocks; when finished -// writing, the caller must Close the returned encoder to flush any -// partially written blocks. -func NewEncoder(w io.Writer) io.WriteCloser { return &encoder{w: w} } - -type encoder struct { - w io.Writer - err os.Error - buf [52]byte - nbuf int - out [1024]byte - nout int -} - -func (e *encoder) Write(p []byte) (n int, err os.Error) { - if e.err != nil { - return 0, e.err - } - - // Leading fringe. - if e.nbuf > 0 { - var i int - for i = 0; i < len(p) && e.nbuf < 52; i++ { - e.buf[e.nbuf] = p[i] - e.nbuf++ - } - n += i - p = p[i:] - if e.nbuf < 52 { - return - } - nout := Encode(e.out[0:], e.buf[0:]) - if _, e.err = e.w.Write(e.out[0:nout]); e.err != nil { - return n, e.err - } - e.nbuf = 0 - } - - // Large interior chunks. - for len(p) >= 52 { - nn := len(e.out) / (1 + 52/4*5 + 1) * 52 - if nn > len(p) { - nn = len(p) / 52 * 52 - } - if nn > 0 { - nout := Encode(e.out[0:], p[0:nn]) - if _, e.err = e.w.Write(e.out[0:nout]); e.err != nil { - return n, e.err - } - } - n += nn - p = p[nn:] - } - - // Trailing fringe. - for i := 0; i < len(p); i++ { - e.buf[i] = p[i] - } - e.nbuf = len(p) - n += len(p) - return -} - -func (e *encoder) Close() os.Error { - // If there's anything left in the buffer, flush it out - if e.err == nil && e.nbuf > 0 { - nout := Encode(e.out[0:], e.buf[0:e.nbuf]) - e.nbuf = 0 - _, e.err = e.w.Write(e.out[0:nout]) - } - return e.err -} - -// NewDecoder returns a new Git base85 stream decoder. -func NewDecoder(r io.Reader) io.Reader { return &decoder{r: r} } - -type decoder struct { - r io.Reader - err os.Error - readErr os.Error - buf [1024]byte - nbuf int - out []byte - outbuf [1024]byte - off int64 -} - -func (d *decoder) Read(p []byte) (n int, err os.Error) { - if len(p) == 0 { - return 0, nil - } - - for { - // Copy leftover output from last decode. - if len(d.out) > 0 { - n = copy(p, d.out) - d.out = d.out[n:] - return - } - - // Out of decoded output. Check errors. - if d.err != nil { - return 0, d.err - } - if d.readErr != nil { - d.err = d.readErr - return 0, d.err - } - - // Read and decode more input. - var nn int - nn, d.readErr = d.r.Read(d.buf[d.nbuf:]) - d.nbuf += nn - - // Send complete lines to Decode. - nl := bytes.LastIndex(d.buf[0:d.nbuf], newline) - if nl < 0 { - continue - } - nn, d.err = Decode(d.outbuf[0:], d.buf[0:nl+1]) - if e, ok := d.err.(CorruptInputError); ok { - d.err = CorruptInputError(int64(e) + d.off) - } - d.out = d.outbuf[0:nn] - d.nbuf = copy(d.buf[0:], d.buf[nl+1:d.nbuf]) - d.off += int64(nl + 1) - } - panic("unreachable") -} diff --git a/src/pkg/encoding/git85/git_test.go b/src/pkg/encoding/git85/git_test.go deleted file mode 100644 index c76385c35..000000000 --- a/src/pkg/encoding/git85/git_test.go +++ /dev/null @@ -1,194 +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. - -package git85 - -import ( - "bytes" - "io/ioutil" - "os" - "testing" -) - -type testpair struct { - decoded, encoded string -} - -func testEqual(t *testing.T, msg string, args ...interface{}) bool { - if args[len(args)-2] != args[len(args)-1] { - t.Errorf(msg, args...) - return false - } - return true -} - -func TestGitTable(t *testing.T) { - var saw [256]bool - for i, c := range encode { - if decode[c] != uint8(i+1) { - t.Errorf("decode['%c'] = %d, want %d", c, decode[c], i+1) - } - saw[c] = true - } - for i, b := range saw { - if !b && decode[i] != 0 { - t.Errorf("decode[%d] = %d, want 0", i, decode[i]) - } - } -} - -var gitPairs = []testpair{ - // Wikipedia example, adapted. - { - "Man is distinguished, not only by his reason, but by this singular passion from " + - "other animals, which is a lust of the mind, that by a perseverance of delight in " + - "the continued and indefatigable generation of knowledge, exceeds the short " + - "vehemence of any carnal pleasure.", - - "zO<`^zX>%ZCX>)XGZfA9Ab7*B`EFf-gbRchTYDO_b1WctXlY|;AZc?T\n" + - "zVIXXEb95kYW*~HEWgu;7Ze%PVbZB98AYyqSVIXj2a&u*NWpZI|V`U(3W*}r`Y-wj`\n" + - "zbRcPNAarPDAY*TCbZKsNWn>^>Ze$>7Ze(RV>IZ)PBC\n" + - "zZf|#NWn^b%EFfigV`XJzb0BnRWgv5CZ*p`Xc4cT~ZDnp_Wgu^6AYpEKAY);2ZeeU7\n" + - "IaBO8^b9HiME&u=k\n", - }, -} - -var gitBigtest = gitPairs[len(gitPairs)-1] - -func TestEncode(t *testing.T) { - for _, p := range gitPairs { - buf := make([]byte, EncodedLen(len(p.decoded))) - n := Encode(buf, []byte(p.decoded)) - if n != len(buf) { - t.Errorf("EncodedLen does not agree with Encode") - } - buf = buf[0:n] - testEqual(t, "Encode(%q) = %q, want %q", p.decoded, string(buf), p.encoded) - } -} - -func TestEncoder(t *testing.T) { - for _, p := range gitPairs { - bb := &bytes.Buffer{} - encoder := NewEncoder(bb) - encoder.Write([]byte(p.decoded)) - encoder.Close() - testEqual(t, "Encode(%q) = %q, want %q", p.decoded, bb.String(), p.encoded) - } -} - -func TestEncoderBuffering(t *testing.T) { - input := []byte(gitBigtest.decoded) - for bs := 1; bs <= 12; bs++ { - bb := &bytes.Buffer{} - encoder := NewEncoder(bb) - for pos := 0; pos < len(input); pos += bs { - end := pos + bs - if end > len(input) { - end = len(input) - } - n, err := encoder.Write(input[pos:end]) - testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, os.Error(nil)) - testEqual(t, "Write(%q) gave length %v, want %v", input[pos:end], n, end-pos) - } - err := encoder.Close() - testEqual(t, "Close gave error %v, want %v", err, os.Error(nil)) - testEqual(t, "Encoding/%d of %q = %q, want %q", bs, gitBigtest.decoded, bb.String(), gitBigtest.encoded) - } -} - -func TestDecode(t *testing.T) { - for _, p := range gitPairs { - dbuf := make([]byte, 4*len(p.encoded)) - ndst, err := Decode(dbuf, []byte(p.encoded)) - testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, os.Error(nil)) - testEqual(t, "Decode(%q) = ndst %v, want %v", p.encoded, ndst, len(p.decoded)) - testEqual(t, "Decode(%q) = %q, want %q", p.encoded, string(dbuf[0:ndst]), p.decoded) - } -} - -func TestDecoder(t *testing.T) { - for _, p := range gitPairs { - decoder := NewDecoder(bytes.NewBufferString(p.encoded)) - dbuf, err := ioutil.ReadAll(decoder) - if err != nil { - t.Fatal("Read failed", err) - } - testEqual(t, "Read from %q = length %v, want %v", p.encoded, len(dbuf), len(p.decoded)) - testEqual(t, "Decoding of %q = %q, want %q", p.encoded, string(dbuf), p.decoded) - if err != nil { - testEqual(t, "Read from %q = %v, want %v", p.encoded, err, os.EOF) - } - } -} - -func TestDecoderBuffering(t *testing.T) { - for bs := 1; bs <= 12; bs++ { - decoder := NewDecoder(bytes.NewBufferString(gitBigtest.encoded)) - buf := make([]byte, len(gitBigtest.decoded)+12) - var total int - for total = 0; total < len(gitBigtest.decoded); { - n, err := decoder.Read(buf[total : total+bs]) - testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", gitBigtest.encoded, total, n, err, os.Error(nil)) - total += n - } - testEqual(t, "Decoding/%d of %q = %q, want %q", bs, gitBigtest.encoded, string(buf[0:total]), gitBigtest.decoded) - } -} - -func TestDecodeCorrupt(t *testing.T) { - type corrupt struct { - e string - p int - } - examples := []corrupt{ - {"v", 0}, - {"!z!!!!!!!!!", 0}, - } - - for _, e := range examples { - dbuf := make([]byte, 2*len(e.e)) - _, err := Decode(dbuf, []byte(e.e)) - switch err := err.(type) { - case CorruptInputError: - testEqual(t, "Corruption in %q at offset %v, want %v", e.e, int(err), e.p) - default: - t.Error("Decoder failed to detect corruption in", e) - } - } -} - -func TestGitBig(t *testing.T) { - n := 3*1000 + 1 - raw := make([]byte, n) - const alpha = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - for i := 0; i < n; i++ { - raw[i] = alpha[i%len(alpha)] - } - encoded := new(bytes.Buffer) - w := NewEncoder(encoded) - nn, err := w.Write(raw) - if nn != n || err != nil { - t.Fatalf("Encoder.Write(raw) = %d, %v want %d, nil", nn, err, n) - } - err = w.Close() - if err != nil { - t.Fatalf("Encoder.Close() = %v want nil", err) - } - decoded, err := ioutil.ReadAll(NewDecoder(encoded)) - if err != nil { - t.Fatalf("ioutil.ReadAll(NewDecoder(...)): %v", err) - } - - if !bytes.Equal(raw, decoded) { - var i int - for i = 0; i < len(decoded) && i < len(raw); i++ { - if decoded[i] != raw[i] { - break - } - } - t.Errorf("Decode(Encode(%d-byte string)) failed at offset %d", n, i) - } -} diff --git a/src/pkg/encoding/gob/codec_test.go b/src/pkg/encoding/gob/codec_test.go new file mode 100644 index 000000000..ebcbb78eb --- /dev/null +++ b/src/pkg/encoding/gob/codec_test.go @@ -0,0 +1,1471 @@ +// 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 gob + +import ( + "bytes" + "errors" + "math" + "math/rand" + "reflect" + "strings" + "testing" + "time" + "unsafe" +) + +// Guarantee encoding format by comparing some encodings to hand-written values +type EncodeT struct { + x uint64 + b []byte +} + +var encodeT = []EncodeT{ + {0x00, []byte{0x00}}, + {0x0F, []byte{0x0F}}, + {0xFF, []byte{0xFF, 0xFF}}, + {0xFFFF, []byte{0xFE, 0xFF, 0xFF}}, + {0xFFFFFF, []byte{0xFD, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFF, []byte{0xFC, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFFFF, []byte{0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFFFFFF, []byte{0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFFFFFFFF, []byte{0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFFFFFFFFFF, []byte{0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0x1111, []byte{0xFE, 0x11, 0x11}}, + {0x1111111111111111, []byte{0xF8, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}}, + {0x8888888888888888, []byte{0xF8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88}}, + {1 << 63, []byte{0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, +} + +// testError is meant to be used as a deferred function to turn a panic(gobError) into a +// plain test.Error call. +func testError(t *testing.T) { + if e := recover(); e != nil { + t.Error(e.(gobError).err) // Will re-panic if not one of our errors, such as a runtime error. + } + return +} + +// Test basic encode/decode routines for unsigned integers +func TestUintCodec(t *testing.T) { + defer testError(t) + b := new(bytes.Buffer) + encState := newEncoderState(b) + for _, tt := range encodeT { + b.Reset() + encState.encodeUint(tt.x) + if !bytes.Equal(tt.b, b.Bytes()) { + t.Errorf("encodeUint: %#x encode: expected % x got % x", tt.x, tt.b, b.Bytes()) + } + } + decState := newDecodeState(b) + for u := uint64(0); ; u = (u + 1) * 7 { + b.Reset() + encState.encodeUint(u) + v := decState.decodeUint() + if u != v { + t.Errorf("Encode/Decode: sent %#x received %#x", u, v) + } + if u&(1<<63) != 0 { + break + } + } +} + +func verifyInt(i int64, t *testing.T) { + defer testError(t) + var b = new(bytes.Buffer) + encState := newEncoderState(b) + encState.encodeInt(i) + decState := newDecodeState(b) + decState.buf = make([]byte, 8) + j := decState.decodeInt() + if i != j { + t.Errorf("Encode/Decode: sent %#x received %#x", uint64(i), uint64(j)) + } +} + +// Test basic encode/decode routines for signed integers +func TestIntCodec(t *testing.T) { + for u := uint64(0); ; u = (u + 1) * 7 { + // Do positive and negative values + i := int64(u) + verifyInt(i, t) + verifyInt(-i, t) + verifyInt(^i, t) + if u&(1<<63) != 0 { + break + } + } + verifyInt(-1<<63, t) // a tricky case +} + +// The result of encoding a true boolean with field number 7 +var boolResult = []byte{0x07, 0x01} + +// The result of encoding a number 17 with field number 7 +var signedResult = []byte{0x07, 2 * 17} +var unsignedResult = []byte{0x07, 17} +var floatResult = []byte{0x07, 0xFE, 0x31, 0x40} + +// The result of encoding a number 17+19i with field number 7 +var complexResult = []byte{0x07, 0xFE, 0x31, 0x40, 0xFE, 0x33, 0x40} + +// The result of encoding "hello" with field number 7 +var bytesResult = []byte{0x07, 0x05, 'h', 'e', 'l', 'l', 'o'} + +func newDecodeState(buf *bytes.Buffer) *decoderState { + d := new(decoderState) + d.b = buf + d.buf = make([]byte, uint64Size) + return d +} + +func newEncoderState(b *bytes.Buffer) *encoderState { + b.Reset() + state := &encoderState{enc: nil, b: b} + state.fieldnum = -1 + return state +} + +// Test instruction execution for encoding. +// Do not run the machine yet; instead do individual instructions crafted by hand. +func TestScalarEncInstructions(t *testing.T) { + var b = new(bytes.Buffer) + + // bool + { + data := struct{ a bool }{true} + instr := &encInstr{encBool, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(boolResult, b.Bytes()) { + t.Errorf("bool enc instructions: expected % x got % x", boolResult, b.Bytes()) + } + } + + // int + { + b.Reset() + data := struct{ a int }{17} + instr := &encInstr{encInt, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(signedResult, b.Bytes()) { + t.Errorf("int enc instructions: expected % x got % x", signedResult, b.Bytes()) + } + } + + // uint + { + b.Reset() + data := struct{ a uint }{17} + instr := &encInstr{encUint, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(unsignedResult, b.Bytes()) { + t.Errorf("uint enc instructions: expected % x got % x", unsignedResult, b.Bytes()) + } + } + + // int8 + { + b.Reset() + data := struct{ a int8 }{17} + instr := &encInstr{encInt8, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(signedResult, b.Bytes()) { + t.Errorf("int8 enc instructions: expected % x got % x", signedResult, b.Bytes()) + } + } + + // uint8 + { + b.Reset() + data := struct{ a uint8 }{17} + instr := &encInstr{encUint8, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(unsignedResult, b.Bytes()) { + t.Errorf("uint8 enc instructions: expected % x got % x", unsignedResult, b.Bytes()) + } + } + + // int16 + { + b.Reset() + data := struct{ a int16 }{17} + instr := &encInstr{encInt16, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(signedResult, b.Bytes()) { + t.Errorf("int16 enc instructions: expected % x got % x", signedResult, b.Bytes()) + } + } + + // uint16 + { + b.Reset() + data := struct{ a uint16 }{17} + instr := &encInstr{encUint16, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(unsignedResult, b.Bytes()) { + t.Errorf("uint16 enc instructions: expected % x got % x", unsignedResult, b.Bytes()) + } + } + + // int32 + { + b.Reset() + data := struct{ a int32 }{17} + instr := &encInstr{encInt32, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(signedResult, b.Bytes()) { + t.Errorf("int32 enc instructions: expected % x got % x", signedResult, b.Bytes()) + } + } + + // uint32 + { + b.Reset() + data := struct{ a uint32 }{17} + instr := &encInstr{encUint32, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(unsignedResult, b.Bytes()) { + t.Errorf("uint32 enc instructions: expected % x got % x", unsignedResult, b.Bytes()) + } + } + + // int64 + { + b.Reset() + data := struct{ a int64 }{17} + instr := &encInstr{encInt64, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(signedResult, b.Bytes()) { + t.Errorf("int64 enc instructions: expected % x got % x", signedResult, b.Bytes()) + } + } + + // uint64 + { + b.Reset() + data := struct{ a uint64 }{17} + instr := &encInstr{encUint64, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(unsignedResult, b.Bytes()) { + t.Errorf("uint64 enc instructions: expected % x got % x", unsignedResult, b.Bytes()) + } + } + + // float32 + { + b.Reset() + data := struct{ a float32 }{17} + instr := &encInstr{encFloat32, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(floatResult, b.Bytes()) { + t.Errorf("float32 enc instructions: expected % x got % x", floatResult, b.Bytes()) + } + } + + // float64 + { + b.Reset() + data := struct{ a float64 }{17} + instr := &encInstr{encFloat64, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(floatResult, b.Bytes()) { + t.Errorf("float64 enc instructions: expected % x got % x", floatResult, b.Bytes()) + } + } + + // bytes == []uint8 + { + b.Reset() + data := struct{ a []byte }{[]byte("hello")} + instr := &encInstr{encUint8Array, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(bytesResult, b.Bytes()) { + t.Errorf("bytes enc instructions: expected % x got % x", bytesResult, b.Bytes()) + } + } + + // string + { + b.Reset() + data := struct{ a string }{"hello"} + instr := &encInstr{encString, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(bytesResult, b.Bytes()) { + t.Errorf("string enc instructions: expected % x got % x", bytesResult, b.Bytes()) + } + } +} + +func execDec(typ string, instr *decInstr, state *decoderState, t *testing.T, p unsafe.Pointer) { + defer testError(t) + v := int(state.decodeUint()) + if v+state.fieldnum != 6 { + t.Fatalf("decoding field number %d, got %d", 6, v+state.fieldnum) + } + instr.op(instr, state, decIndirect(p, instr.indir)) + state.fieldnum = 6 +} + +func newDecodeStateFromData(data []byte) *decoderState { + b := bytes.NewBuffer(data) + state := newDecodeState(b) + state.fieldnum = -1 + return state +} + +// Test instruction execution for decoding. +// Do not run the machine yet; instead do individual instructions crafted by hand. +func TestScalarDecInstructions(t *testing.T) { + ovfl := errors.New("overflow") + + // bool + { + var data struct { + a bool + } + instr := &decInstr{decBool, 6, 0, 0, ovfl} + state := newDecodeStateFromData(boolResult) + execDec("bool", instr, state, t, unsafe.Pointer(&data)) + if data.a != true { + t.Errorf("bool a = %v not true", data.a) + } + } + // int + { + var data struct { + a int + } + instr := &decInstr{decOpTable[reflect.Int], 6, 0, 0, ovfl} + state := newDecodeStateFromData(signedResult) + execDec("int", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("int a = %v not 17", data.a) + } + } + + // uint + { + var data struct { + a uint + } + instr := &decInstr{decOpTable[reflect.Uint], 6, 0, 0, ovfl} + state := newDecodeStateFromData(unsignedResult) + execDec("uint", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("uint a = %v not 17", data.a) + } + } + + // int8 + { + var data struct { + a int8 + } + instr := &decInstr{decInt8, 6, 0, 0, ovfl} + state := newDecodeStateFromData(signedResult) + execDec("int8", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("int8 a = %v not 17", data.a) + } + } + + // uint8 + { + var data struct { + a uint8 + } + instr := &decInstr{decUint8, 6, 0, 0, ovfl} + state := newDecodeStateFromData(unsignedResult) + execDec("uint8", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("uint8 a = %v not 17", data.a) + } + } + + // int16 + { + var data struct { + a int16 + } + instr := &decInstr{decInt16, 6, 0, 0, ovfl} + state := newDecodeStateFromData(signedResult) + execDec("int16", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("int16 a = %v not 17", data.a) + } + } + + // uint16 + { + var data struct { + a uint16 + } + instr := &decInstr{decUint16, 6, 0, 0, ovfl} + state := newDecodeStateFromData(unsignedResult) + execDec("uint16", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("uint16 a = %v not 17", data.a) + } + } + + // int32 + { + var data struct { + a int32 + } + instr := &decInstr{decInt32, 6, 0, 0, ovfl} + state := newDecodeStateFromData(signedResult) + execDec("int32", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("int32 a = %v not 17", data.a) + } + } + + // uint32 + { + var data struct { + a uint32 + } + instr := &decInstr{decUint32, 6, 0, 0, ovfl} + state := newDecodeStateFromData(unsignedResult) + execDec("uint32", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("uint32 a = %v not 17", data.a) + } + } + + // uintptr + { + var data struct { + a uintptr + } + instr := &decInstr{decOpTable[reflect.Uintptr], 6, 0, 0, ovfl} + state := newDecodeStateFromData(unsignedResult) + execDec("uintptr", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("uintptr a = %v not 17", data.a) + } + } + + // int64 + { + var data struct { + a int64 + } + instr := &decInstr{decInt64, 6, 0, 0, ovfl} + state := newDecodeStateFromData(signedResult) + execDec("int64", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("int64 a = %v not 17", data.a) + } + } + + // uint64 + { + var data struct { + a uint64 + } + instr := &decInstr{decUint64, 6, 0, 0, ovfl} + state := newDecodeStateFromData(unsignedResult) + execDec("uint64", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("uint64 a = %v not 17", data.a) + } + } + + // float32 + { + var data struct { + a float32 + } + instr := &decInstr{decFloat32, 6, 0, 0, ovfl} + state := newDecodeStateFromData(floatResult) + execDec("float32", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("float32 a = %v not 17", data.a) + } + } + + // float64 + { + var data struct { + a float64 + } + instr := &decInstr{decFloat64, 6, 0, 0, ovfl} + state := newDecodeStateFromData(floatResult) + execDec("float64", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("float64 a = %v not 17", data.a) + } + } + + // complex64 + { + var data struct { + a complex64 + } + instr := &decInstr{decOpTable[reflect.Complex64], 6, 0, 0, ovfl} + state := newDecodeStateFromData(complexResult) + execDec("complex", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17+19i { + t.Errorf("complex a = %v not 17+19i", data.a) + } + } + + // complex128 + { + var data struct { + a complex128 + } + instr := &decInstr{decOpTable[reflect.Complex128], 6, 0, 0, ovfl} + state := newDecodeStateFromData(complexResult) + execDec("complex", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17+19i { + t.Errorf("complex a = %v not 17+19i", data.a) + } + } + + // bytes == []uint8 + { + var data struct { + a []byte + } + instr := &decInstr{decUint8Slice, 6, 0, 0, ovfl} + state := newDecodeStateFromData(bytesResult) + execDec("bytes", instr, state, t, unsafe.Pointer(&data)) + if string(data.a) != "hello" { + t.Errorf(`bytes a = %q not "hello"`, string(data.a)) + } + } + + // string + { + var data struct { + a string + } + instr := &decInstr{decString, 6, 0, 0, ovfl} + state := newDecodeStateFromData(bytesResult) + execDec("bytes", instr, state, t, unsafe.Pointer(&data)) + if data.a != "hello" { + t.Errorf(`bytes a = %q not "hello"`, data.a) + } + } +} + +func TestEndToEnd(t *testing.T) { + type T2 struct { + T string + } + s1 := "string1" + s2 := "string2" + type T1 struct { + A, B, C int + M map[string]*float64 + EmptyMap map[string]int // to check that we receive a non-nil map. + N *[3]float64 + Strs *[2]string + Int64s *[]int64 + RI complex64 + S string + Y []byte + T *T2 + } + pi := 3.14159 + e := 2.71828 + t1 := &T1{ + A: 17, + B: 18, + C: -5, + M: map[string]*float64{"pi": &pi, "e": &e}, + EmptyMap: make(map[string]int), + N: &[3]float64{1.5, 2.5, 3.5}, + Strs: &[2]string{s1, s2}, + Int64s: &[]int64{77, 89, 123412342134}, + RI: 17 - 23i, + S: "Now is the time", + Y: []byte("hello, sailor"), + T: &T2{"this is T2"}, + } + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(t1) + if err != nil { + t.Error("encode:", err) + } + var _t1 T1 + err = NewDecoder(b).Decode(&_t1) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(t1, &_t1) { + t.Errorf("encode expected %v got %v", *t1, _t1) + } + // Be absolutely sure the received map is non-nil. + if t1.EmptyMap == nil { + t.Errorf("nil map sent") + } + if _t1.EmptyMap == nil { + t.Errorf("nil map received") + } +} + +func TestOverflow(t *testing.T) { + type inputT struct { + Maxi int64 + Mini int64 + Maxu uint64 + Maxf float64 + Minf float64 + Maxc complex128 + Minc complex128 + } + var it inputT + var err error + b := new(bytes.Buffer) + enc := NewEncoder(b) + dec := NewDecoder(b) + + // int8 + b.Reset() + it = inputT{ + Maxi: math.MaxInt8 + 1, + } + type outi8 struct { + Maxi int8 + Mini int8 + } + var o1 outi8 + enc.Encode(it) + err = dec.Decode(&o1) + if err == nil || err.Error() != `value for "Maxi" out of range` { + t.Error("wrong overflow error for int8:", err) + } + it = inputT{ + Mini: math.MinInt8 - 1, + } + b.Reset() + enc.Encode(it) + err = dec.Decode(&o1) + if err == nil || err.Error() != `value for "Mini" out of range` { + t.Error("wrong underflow error for int8:", err) + } + + // int16 + b.Reset() + it = inputT{ + Maxi: math.MaxInt16 + 1, + } + type outi16 struct { + Maxi int16 + Mini int16 + } + var o2 outi16 + enc.Encode(it) + err = dec.Decode(&o2) + if err == nil || err.Error() != `value for "Maxi" out of range` { + t.Error("wrong overflow error for int16:", err) + } + it = inputT{ + Mini: math.MinInt16 - 1, + } + b.Reset() + enc.Encode(it) + err = dec.Decode(&o2) + if err == nil || err.Error() != `value for "Mini" out of range` { + t.Error("wrong underflow error for int16:", err) + } + + // int32 + b.Reset() + it = inputT{ + Maxi: math.MaxInt32 + 1, + } + type outi32 struct { + Maxi int32 + Mini int32 + } + var o3 outi32 + enc.Encode(it) + err = dec.Decode(&o3) + if err == nil || err.Error() != `value for "Maxi" out of range` { + t.Error("wrong overflow error for int32:", err) + } + it = inputT{ + Mini: math.MinInt32 - 1, + } + b.Reset() + enc.Encode(it) + err = dec.Decode(&o3) + if err == nil || err.Error() != `value for "Mini" out of range` { + t.Error("wrong underflow error for int32:", err) + } + + // uint8 + b.Reset() + it = inputT{ + Maxu: math.MaxUint8 + 1, + } + type outu8 struct { + Maxu uint8 + } + var o4 outu8 + enc.Encode(it) + err = dec.Decode(&o4) + if err == nil || err.Error() != `value for "Maxu" out of range` { + t.Error("wrong overflow error for uint8:", err) + } + + // uint16 + b.Reset() + it = inputT{ + Maxu: math.MaxUint16 + 1, + } + type outu16 struct { + Maxu uint16 + } + var o5 outu16 + enc.Encode(it) + err = dec.Decode(&o5) + if err == nil || err.Error() != `value for "Maxu" out of range` { + t.Error("wrong overflow error for uint16:", err) + } + + // uint32 + b.Reset() + it = inputT{ + Maxu: math.MaxUint32 + 1, + } + type outu32 struct { + Maxu uint32 + } + var o6 outu32 + enc.Encode(it) + err = dec.Decode(&o6) + if err == nil || err.Error() != `value for "Maxu" out of range` { + t.Error("wrong overflow error for uint32:", err) + } + + // float32 + b.Reset() + it = inputT{ + Maxf: math.MaxFloat32 * 2, + } + type outf32 struct { + Maxf float32 + Minf float32 + } + var o7 outf32 + enc.Encode(it) + err = dec.Decode(&o7) + if err == nil || err.Error() != `value for "Maxf" out of range` { + t.Error("wrong overflow error for float32:", err) + } + + // complex64 + b.Reset() + it = inputT{ + Maxc: complex(math.MaxFloat32*2, math.MaxFloat32*2), + } + type outc64 struct { + Maxc complex64 + Minc complex64 + } + var o8 outc64 + enc.Encode(it) + err = dec.Decode(&o8) + if err == nil || err.Error() != `value for "Maxc" out of range` { + t.Error("wrong overflow error for complex64:", err) + } +} + +func TestNesting(t *testing.T) { + type RT struct { + A string + Next *RT + } + rt := new(RT) + rt.A = "level1" + rt.Next = new(RT) + rt.Next.A = "level2" + b := new(bytes.Buffer) + NewEncoder(b).Encode(rt) + var drt RT + dec := NewDecoder(b) + err := dec.Decode(&drt) + if err != nil { + t.Fatal("decoder error:", err) + } + if drt.A != rt.A { + t.Errorf("nesting: encode expected %v got %v", *rt, drt) + } + if drt.Next == nil { + t.Errorf("nesting: recursion failed") + } + if drt.Next.A != rt.Next.A { + t.Errorf("nesting: encode expected %v got %v", *rt.Next, *drt.Next) + } +} + +// These three structures have the same data with different indirections +type T0 struct { + A int + B int + C int + D int +} +type T1 struct { + A int + B *int + C **int + D ***int +} +type T2 struct { + A ***int + B **int + C *int + D int +} + +func TestAutoIndirection(t *testing.T) { + // First transfer t1 into t0 + var t1 T1 + t1.A = 17 + t1.B = new(int) + *t1.B = 177 + t1.C = new(*int) + *t1.C = new(int) + **t1.C = 1777 + t1.D = new(**int) + *t1.D = new(*int) + **t1.D = new(int) + ***t1.D = 17777 + b := new(bytes.Buffer) + enc := NewEncoder(b) + enc.Encode(t1) + dec := NewDecoder(b) + var t0 T0 + dec.Decode(&t0) + if t0.A != 17 || t0.B != 177 || t0.C != 1777 || t0.D != 17777 { + t.Errorf("t1->t0: expected {17 177 1777 17777}; got %v", t0) + } + + // Now transfer t2 into t0 + var t2 T2 + t2.D = 17777 + t2.C = new(int) + *t2.C = 1777 + t2.B = new(*int) + *t2.B = new(int) + **t2.B = 177 + t2.A = new(**int) + *t2.A = new(*int) + **t2.A = new(int) + ***t2.A = 17 + b.Reset() + enc.Encode(t2) + t0 = T0{} + dec.Decode(&t0) + if t0.A != 17 || t0.B != 177 || t0.C != 1777 || t0.D != 17777 { + t.Errorf("t2->t0 expected {17 177 1777 17777}; got %v", t0) + } + + // Now transfer t0 into t1 + t0 = T0{17, 177, 1777, 17777} + b.Reset() + enc.Encode(t0) + t1 = T1{} + dec.Decode(&t1) + if t1.A != 17 || *t1.B != 177 || **t1.C != 1777 || ***t1.D != 17777 { + t.Errorf("t0->t1 expected {17 177 1777 17777}; got {%d %d %d %d}", t1.A, *t1.B, **t1.C, ***t1.D) + } + + // Now transfer t0 into t2 + b.Reset() + enc.Encode(t0) + t2 = T2{} + dec.Decode(&t2) + if ***t2.A != 17 || **t2.B != 177 || *t2.C != 1777 || t2.D != 17777 { + t.Errorf("t0->t2 expected {17 177 1777 17777}; got {%d %d %d %d}", ***t2.A, **t2.B, *t2.C, t2.D) + } + + // Now do t2 again but without pre-allocated pointers. + b.Reset() + enc.Encode(t0) + ***t2.A = 0 + **t2.B = 0 + *t2.C = 0 + t2.D = 0 + dec.Decode(&t2) + if ***t2.A != 17 || **t2.B != 177 || *t2.C != 1777 || t2.D != 17777 { + t.Errorf("t0->t2 expected {17 177 1777 17777}; got {%d %d %d %d}", ***t2.A, **t2.B, *t2.C, t2.D) + } +} + +type RT0 struct { + A int + B string + C float64 +} +type RT1 struct { + C float64 + B string + A int + NotSet string +} + +func TestReorderedFields(t *testing.T) { + var rt0 RT0 + rt0.A = 17 + rt0.B = "hello" + rt0.C = 3.14159 + b := new(bytes.Buffer) + NewEncoder(b).Encode(rt0) + dec := NewDecoder(b) + var rt1 RT1 + // Wire type is RT0, local type is RT1. + err := dec.Decode(&rt1) + if err != nil { + t.Fatal("decode error:", err) + } + if rt0.A != rt1.A || rt0.B != rt1.B || rt0.C != rt1.C { + t.Errorf("rt1->rt0: expected %v; got %v", rt0, rt1) + } +} + +// Like an RT0 but with fields we'll ignore on the decode side. +type IT0 struct { + A int64 + B string + Ignore_d []int + Ignore_e [3]float64 + Ignore_f bool + Ignore_g string + Ignore_h []byte + Ignore_i *RT1 + Ignore_m map[string]int + C float64 +} + +func TestIgnoredFields(t *testing.T) { + var it0 IT0 + it0.A = 17 + it0.B = "hello" + it0.C = 3.14159 + it0.Ignore_d = []int{1, 2, 3} + it0.Ignore_e[0] = 1.0 + it0.Ignore_e[1] = 2.0 + it0.Ignore_e[2] = 3.0 + it0.Ignore_f = true + it0.Ignore_g = "pay no attention" + it0.Ignore_h = []byte("to the curtain") + it0.Ignore_i = &RT1{3.1, "hi", 7, "hello"} + it0.Ignore_m = map[string]int{"one": 1, "two": 2} + + b := new(bytes.Buffer) + NewEncoder(b).Encode(it0) + dec := NewDecoder(b) + var rt1 RT1 + // Wire type is IT0, local type is RT1. + err := dec.Decode(&rt1) + if err != nil { + t.Error("error: ", err) + } + if int(it0.A) != rt1.A || it0.B != rt1.B || it0.C != rt1.C { + t.Errorf("rt0->rt1: expected %v; got %v", it0, rt1) + } +} + +func TestBadRecursiveType(t *testing.T) { + type Rec ***Rec + var rec Rec + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(&rec) + if err == nil { + t.Error("expected error; got none") + } else if strings.Index(err.Error(), "recursive") < 0 { + t.Error("expected recursive type error; got", err) + } + // Can't test decode easily because we can't encode one, so we can't pass one to a Decoder. +} + +type Bad0 struct { + CH chan int + C float64 +} + +func TestInvalidField(t *testing.T) { + var bad0 Bad0 + bad0.CH = make(chan int) + b := new(bytes.Buffer) + dummyEncoder := new(Encoder) // sufficient for this purpose. + dummyEncoder.encode(b, reflect.ValueOf(&bad0), userType(reflect.TypeOf(&bad0))) + if err := dummyEncoder.err; err == nil { + t.Error("expected error; got none") + } else if strings.Index(err.Error(), "type") < 0 { + t.Error("expected type error; got", err) + } +} + +type Indirect struct { + A ***[3]int + S ***[]int + M ****map[string]int +} + +type Direct struct { + A [3]int + S []int + M map[string]int +} + +func TestIndirectSliceMapArray(t *testing.T) { + // Marshal indirect, unmarshal to direct. + i := new(Indirect) + i.A = new(**[3]int) + *i.A = new(*[3]int) + **i.A = new([3]int) + ***i.A = [3]int{1, 2, 3} + i.S = new(**[]int) + *i.S = new(*[]int) + **i.S = new([]int) + ***i.S = []int{4, 5, 6} + i.M = new(***map[string]int) + *i.M = new(**map[string]int) + **i.M = new(*map[string]int) + ***i.M = new(map[string]int) + ****i.M = map[string]int{"one": 1, "two": 2, "three": 3} + b := new(bytes.Buffer) + NewEncoder(b).Encode(i) + dec := NewDecoder(b) + var d Direct + err := dec.Decode(&d) + if err != nil { + t.Error("error: ", err) + } + if len(d.A) != 3 || d.A[0] != 1 || d.A[1] != 2 || d.A[2] != 3 { + t.Errorf("indirect to direct: d.A is %v not %v", d.A, ***i.A) + } + if len(d.S) != 3 || d.S[0] != 4 || d.S[1] != 5 || d.S[2] != 6 { + t.Errorf("indirect to direct: d.S is %v not %v", d.S, ***i.S) + } + if len(d.M) != 3 || d.M["one"] != 1 || d.M["two"] != 2 || d.M["three"] != 3 { + t.Errorf("indirect to direct: d.M is %v not %v", d.M, ***i.M) + } + // Marshal direct, unmarshal to indirect. + d.A = [3]int{11, 22, 33} + d.S = []int{44, 55, 66} + d.M = map[string]int{"four": 4, "five": 5, "six": 6} + i = new(Indirect) + b.Reset() + NewEncoder(b).Encode(d) + dec = NewDecoder(b) + err = dec.Decode(&i) + if err != nil { + t.Fatal("error: ", err) + } + if len(***i.A) != 3 || (***i.A)[0] != 11 || (***i.A)[1] != 22 || (***i.A)[2] != 33 { + t.Errorf("direct to indirect: ***i.A is %v not %v", ***i.A, d.A) + } + if len(***i.S) != 3 || (***i.S)[0] != 44 || (***i.S)[1] != 55 || (***i.S)[2] != 66 { + t.Errorf("direct to indirect: ***i.S is %v not %v", ***i.S, ***i.S) + } + if len(****i.M) != 3 || (****i.M)["four"] != 4 || (****i.M)["five"] != 5 || (****i.M)["six"] != 6 { + t.Errorf("direct to indirect: ****i.M is %v not %v", ****i.M, d.M) + } +} + +// An interface with several implementations +type Squarer interface { + Square() int +} + +type Int int + +func (i Int) Square() int { + return int(i * i) +} + +type Float float64 + +func (f Float) Square() int { + return int(f * f) +} + +type Vector []int + +func (v Vector) Square() int { + sum := 0 + for _, x := range v { + sum += x * x + } + return sum +} + +type Point struct { + X, Y int +} + +func (p Point) Square() int { + return p.X*p.X + p.Y*p.Y +} + +// A struct with interfaces in it. +type InterfaceItem struct { + I int + Sq1, Sq2, Sq3 Squarer + F float64 + Sq []Squarer +} + +// The same struct without interfaces +type NoInterfaceItem struct { + I int + F float64 +} + +func TestInterface(t *testing.T) { + iVal := Int(3) + fVal := Float(5) + // Sending a Vector will require that the receiver define a type in the middle of + // receiving the value for item2. + vVal := Vector{1, 2, 3} + b := new(bytes.Buffer) + item1 := &InterfaceItem{1, iVal, fVal, vVal, 11.5, []Squarer{iVal, fVal, nil, vVal}} + // Register the types. + Register(Int(0)) + Register(Float(0)) + Register(Vector{}) + err := NewEncoder(b).Encode(item1) + if err != nil { + t.Error("expected no encode error; got", err) + } + + item2 := InterfaceItem{} + err = NewDecoder(b).Decode(&item2) + if err != nil { + t.Fatal("decode:", err) + } + if item2.I != item1.I { + t.Error("normal int did not decode correctly") + } + if item2.Sq1 == nil || item2.Sq1.Square() != iVal.Square() { + t.Error("Int did not decode correctly") + } + if item2.Sq2 == nil || item2.Sq2.Square() != fVal.Square() { + t.Error("Float did not decode correctly") + } + if item2.Sq3 == nil || item2.Sq3.Square() != vVal.Square() { + t.Error("Vector did not decode correctly") + } + if item2.F != item1.F { + t.Error("normal float did not decode correctly") + } + // Now check that we received a slice of Squarers correctly, including a nil element + if len(item1.Sq) != len(item2.Sq) { + t.Fatalf("[]Squarer length wrong: got %d; expected %d", len(item2.Sq), len(item1.Sq)) + } + for i, v1 := range item1.Sq { + v2 := item2.Sq[i] + if v1 == nil || v2 == nil { + if v1 != nil || v2 != nil { + t.Errorf("item %d inconsistent nils", i) + } + continue + if v1.Square() != v2.Square() { + t.Errorf("item %d inconsistent values: %v %v", i, v1, v2) + } + } + } +} + +// A struct with all basic types, stored in interfaces. +type BasicInterfaceItem struct { + Int, Int8, Int16, Int32, Int64 interface{} + Uint, Uint8, Uint16, Uint32, Uint64 interface{} + Float32, Float64 interface{} + Complex64, Complex128 interface{} + Bool interface{} + String interface{} + Bytes interface{} +} + +func TestInterfaceBasic(t *testing.T) { + b := new(bytes.Buffer) + item1 := &BasicInterfaceItem{ + int(1), int8(1), int16(1), int32(1), int64(1), + uint(1), uint8(1), uint16(1), uint32(1), uint64(1), + float32(1), 1.0, + complex64(1i), complex128(1i), + true, + "hello", + []byte("sailor"), + } + err := NewEncoder(b).Encode(item1) + if err != nil { + t.Error("expected no encode error; got", err) + } + + item2 := &BasicInterfaceItem{} + err = NewDecoder(b).Decode(&item2) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(item1, item2) { + t.Errorf("encode expected %v got %v", item1, item2) + } + // Hand check a couple for correct types. + if v, ok := item2.Bool.(bool); !ok || !v { + t.Error("boolean should be true") + } + if v, ok := item2.String.(string); !ok || v != item1.String.(string) { + t.Errorf("string should be %v is %v", item1.String, v) + } +} + +type String string + +type PtrInterfaceItem struct { + Str1 interface{} // basic + Str2 interface{} // derived +} + +// We'll send pointers; should receive values. +// Also check that we can register T but send *T. +func TestInterfacePointer(t *testing.T) { + b := new(bytes.Buffer) + str1 := "howdy" + str2 := String("kiddo") + item1 := &PtrInterfaceItem{ + &str1, + &str2, + } + // Register the type. + Register(str2) + err := NewEncoder(b).Encode(item1) + if err != nil { + t.Error("expected no encode error; got", err) + } + + item2 := &PtrInterfaceItem{} + err = NewDecoder(b).Decode(&item2) + if err != nil { + t.Fatal("decode:", err) + } + // Hand test for correct types and values. + if v, ok := item2.Str1.(string); !ok || v != str1 { + t.Errorf("basic string failed: %q should be %q", v, str1) + } + if v, ok := item2.Str2.(String); !ok || v != str2 { + t.Errorf("derived type String failed: %q should be %q", v, str2) + } +} + +func TestIgnoreInterface(t *testing.T) { + iVal := Int(3) + fVal := Float(5) + // Sending a Point will require that the receiver define a type in the middle of + // receiving the value for item2. + pVal := Point{2, 3} + b := new(bytes.Buffer) + item1 := &InterfaceItem{1, iVal, fVal, pVal, 11.5, nil} + // Register the types. + Register(Int(0)) + Register(Float(0)) + Register(Point{}) + err := NewEncoder(b).Encode(item1) + if err != nil { + t.Error("expected no encode error; got", err) + } + + item2 := NoInterfaceItem{} + err = NewDecoder(b).Decode(&item2) + if err != nil { + t.Fatal("decode:", err) + } + if item2.I != item1.I { + t.Error("normal int did not decode correctly") + } + if item2.F != item2.F { + t.Error("normal float did not decode correctly") + } +} + +type U struct { + A int + B string + c float64 + D uint +} + +func TestUnexportedFields(t *testing.T) { + var u0 U + u0.A = 17 + u0.B = "hello" + u0.c = 3.14159 + u0.D = 23 + b := new(bytes.Buffer) + NewEncoder(b).Encode(u0) + dec := NewDecoder(b) + var u1 U + u1.c = 1234. + err := dec.Decode(&u1) + if err != nil { + t.Fatal("decode error:", err) + } + if u0.A != u0.A || u0.B != u1.B || u0.D != u1.D { + t.Errorf("u1->u0: expected %v; got %v", u0, u1) + } + if u1.c != 1234. { + t.Error("u1.c modified") + } +} + +var singletons = []interface{}{ + true, + 7, + 3.2, + "hello", + [3]int{11, 22, 33}, + []float32{0.5, 0.25, 0.125}, + map[string]int{"one": 1, "two": 2}, +} + +func TestDebugSingleton(t *testing.T) { + if debugFunc == nil { + return + } + b := new(bytes.Buffer) + // Accumulate a number of values and print them out all at once. + for _, x := range singletons { + err := NewEncoder(b).Encode(x) + if err != nil { + t.Fatal("encode:", err) + } + } + debugFunc(b) +} + +// A type that won't be defined in the gob until we send it in an interface value. +type OnTheFly struct { + A int +} + +type DT struct { + // X OnTheFly + A int + B string + C float64 + I interface{} + J interface{} + I_nil interface{} + M map[string]int + T [3]int + S []string +} + +func TestDebugStruct(t *testing.T) { + if debugFunc == nil { + return + } + Register(OnTheFly{}) + var dt DT + dt.A = 17 + dt.B = "hello" + dt.C = 3.14159 + dt.I = 271828 + dt.J = OnTheFly{3} + dt.I_nil = nil + dt.M = map[string]int{"one": 1, "two": 2} + dt.T = [3]int{11, 22, 33} + dt.S = []string{"hi", "joe"} + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(dt) + if err != nil { + t.Fatal("encode:", err) + } + debugBuffer := bytes.NewBuffer(b.Bytes()) + dt2 := &DT{} + err = NewDecoder(b).Decode(&dt2) + if err != nil { + t.Error("decode:", err) + } + debugFunc(debugBuffer) +} + +func encFuzzDec(rng *rand.Rand, in interface{}) error { + buf := new(bytes.Buffer) + enc := NewEncoder(buf) + if err := enc.Encode(&in); err != nil { + return err + } + + b := buf.Bytes() + for i, bi := range b { + if rng.Intn(10) < 3 { + b[i] = bi + uint8(rng.Intn(256)) + } + } + + dec := NewDecoder(buf) + var e interface{} + if err := dec.Decode(&e); err != nil { + return err + } + return nil +} + +// This does some "fuzz testing" by attempting to decode a sequence of random bytes. +func TestFuzz(t *testing.T) { + if testing.Short() { + return + } + + // all possible inputs + input := []interface{}{ + new(int), + new(float32), + new(float64), + new(complex128), + &ByteStruct{255}, + &ArrayStruct{}, + &StringStruct{"hello"}, + &GobTest1{0, &StringStruct{"hello"}}, + } + testFuzz(t, time.Now().UnixNano(), 100, input...) +} + +func TestFuzzRegressions(t *testing.T) { + // 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)) +} + +func testFuzz(t *testing.T, seed int64, n int, input ...interface{}) { + for _, e := range input { + t.Logf("seed=%d n=%d e=%T", seed, n, e) + rng := rand.New(rand.NewSource(seed)) + for i := 0; i < n; i++ { + encFuzzDec(rng, e) + } + } +} diff --git a/src/pkg/encoding/gob/debug.go b/src/pkg/encoding/gob/debug.go new file mode 100644 index 000000000..31d1351fc --- /dev/null +++ b/src/pkg/encoding/gob/debug.go @@ -0,0 +1,695 @@ +// 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. + +// Delete the next line to include in the gob package. +// +build ignore + +package gob + +// This file is not normally included in the gob package. Used only for debugging the package itself. +// Except for reading uints, it is an implementation of a reader that is independent of +// the one implemented by Decoder. +// To enable the Debug function, delete the +build ignore line above and do +// go install + +import ( + "bytes" + "fmt" + "io" + "os" + "strings" + "sync" +) + +var dumpBytes = false // If true, print the remaining bytes in the input buffer at each item. + +// Init installs the debugging facility. If this file is not compiled in the +// package, the tests in codec_test.go are no-ops. +func init() { + debugFunc = Debug +} + +var ( + blanks = bytes.Repeat([]byte{' '}, 3*10) + empty = []byte(": \n") + tabs = strings.Repeat("\t", 100) +) + +// tab indents itself when printed. +type tab int + +func (t tab) String() string { + n := int(t) + if n > len(tabs) { + n = len(tabs) + } + return tabs[0:n] +} + +func (t tab) print() { + fmt.Fprint(os.Stderr, t) +} + +// A peekReader wraps an io.Reader, allowing one to peek ahead to see +// what's coming without stealing the data from the client of the Reader. +type peekReader struct { + r io.Reader + data []byte // read-ahead data +} + +// newPeekReader returns a peekReader that wraps r. +func newPeekReader(r io.Reader) *peekReader { + return &peekReader{r: r} +} + +// Read is the usual method. It will first take data that has been read ahead. +func (p *peekReader) Read(b []byte) (n int, err error) { + if len(p.data) == 0 { + return p.r.Read(b) + } + // Satisfy what's possible from the read-ahead data. + n = copy(b, p.data) + // Move data down to beginning of slice, to avoid endless growth + copy(p.data, p.data[n:]) + p.data = p.data[:len(p.data)-n] + return +} + +// peek returns as many bytes as possible from the unread +// portion of the stream, up to the length of b. +func (p *peekReader) peek(b []byte) (n int, err error) { + if len(p.data) > 0 { + n = copy(b, p.data) + if n == len(b) { + return + } + b = b[n:] + } + if len(b) == 0 { + return + } + m, e := io.ReadFull(p.r, b) + if m > 0 { + p.data = append(p.data, b[:m]...) + } + n += m + if e == io.ErrUnexpectedEOF { + // That means m > 0 but we reached EOF. If we got data + // we won't complain about not being able to peek enough. + if n > 0 { + e = nil + } else { + e = io.EOF + } + } + return n, e +} + +type debugger struct { + mutex sync.Mutex + remain int // the number of bytes known to remain in the input + remainingKnown bool // the value of 'remain' is valid + r *peekReader + wireType map[typeId]*wireType + tmp []byte // scratch space for decoding uints. +} + +// dump prints the next nBytes of the input. +// It arranges to print the output aligned from call to +// call, to make it easy to see what has been consumed. +func (deb *debugger) dump(format string, args ...interface{}) { + if !dumpBytes { + return + } + fmt.Fprintf(os.Stderr, format+" ", args...) + if !deb.remainingKnown { + return + } + if deb.remain < 0 { + fmt.Fprintf(os.Stderr, "remaining byte count is negative! %d\n", deb.remain) + return + } + data := make([]byte, deb.remain) + n, _ := deb.r.peek(data) + if n == 0 { + os.Stderr.Write(empty) + return + } + b := new(bytes.Buffer) + fmt.Fprintf(b, "[%d]{\n", deb.remain) + // Blanks until first byte + lineLength := 0 + if n := len(data); n%10 != 0 { + lineLength = 10 - n%10 + fmt.Fprintf(b, "\t%s", blanks[:lineLength*3]) + } + // 10 bytes per line + for len(data) > 0 { + if lineLength == 0 { + fmt.Fprint(b, "\t") + } + m := 10 - lineLength + lineLength = 0 + if m > len(data) { + m = len(data) + } + fmt.Fprintf(b, "% x\n", data[:m]) + data = data[m:] + } + fmt.Fprint(b, "}\n") + os.Stderr.Write(b.Bytes()) +} + +// Debug prints a human-readable representation of the gob data read from r. +// It is a no-op unless debugging was enabled when the package was built. +func Debug(r io.Reader) { + err := debug(r) + if err != nil { + fmt.Fprintf(os.Stderr, "gob debug: %s\n", err) + } +} + +// debug implements Debug, but catches panics and returns +// them as errors to be printed by Debug. +func debug(r io.Reader) (err error) { + defer catchError(&err) + fmt.Fprintln(os.Stderr, "Start of debugging") + deb := &debugger{ + r: newPeekReader(r), + wireType: make(map[typeId]*wireType), + tmp: make([]byte, 16), + } + if b, ok := r.(*bytes.Buffer); ok { + deb.remain = b.Len() + deb.remainingKnown = true + } + deb.gobStream() + return +} + +// note that we've consumed some bytes +func (deb *debugger) consumed(n int) { + if deb.remainingKnown { + deb.remain -= n + } +} + +// int64 decodes and returns the next integer, which must be present. +// Don't call this if you could be at EOF. +func (deb *debugger) int64() int64 { + return toInt(deb.uint64()) +} + +// uint64 returns and decodes the next unsigned integer, which must be present. +// Don't call this if you could be at EOF. +// TODO: handle errors better. +func (deb *debugger) uint64() uint64 { + n, w, err := decodeUintReader(deb.r, deb.tmp) + if err != nil { + errorf("debug: read error: %s", err) + } + deb.consumed(w) + return n +} + +// GobStream: +// DelimitedMessage* (until EOF) +func (deb *debugger) gobStream() { + // Make sure we're single-threaded through here. + deb.mutex.Lock() + defer deb.mutex.Unlock() + + for deb.delimitedMessage(0) { + } +} + +// DelimitedMessage: +// uint(lengthOfMessage) Message +func (deb *debugger) delimitedMessage(indent tab) bool { + for { + n := deb.loadBlock(true) + if n < 0 { + return false + } + deb.dump("Delimited message of length %d", n) + deb.message(indent) + } + return true +} + +// loadBlock preps us to read a message +// of the length specified next in the input. It returns +// the length of the block. The argument tells whether +// an EOF is acceptable now. If it is and one is found, +// the return value is negative. +func (deb *debugger) loadBlock(eofOK bool) int { + n64, w, err := decodeUintReader(deb.r, deb.tmp) // deb.uint64 will error at EOF + if err != nil { + if eofOK && err == io.EOF { + return -1 + } + errorf("debug: unexpected error: %s", err) + } + deb.consumed(w) + n := int(n64) + if n < 0 { + errorf("huge value for message length: %d", n64) + } + return int(n) +} + +// Message: +// TypeSequence TypedValue +// TypeSequence +// (TypeDefinition DelimitedTypeDefinition*)? +// DelimitedTypeDefinition: +// uint(lengthOfTypeDefinition) TypeDefinition +// TypedValue: +// int(typeId) Value +func (deb *debugger) message(indent tab) bool { + for { + // Convert the uint64 to a signed integer typeId + uid := deb.int64() + id := typeId(uid) + deb.dump("type id=%d", id) + if id < 0 { + deb.typeDefinition(indent, -id) + n := deb.loadBlock(false) + deb.dump("Message of length %d", n) + continue + } else { + deb.value(indent, id) + break + } + } + return true +} + +// Helper methods to make it easy to scan a type descriptor. + +// common returns the CommonType at the input point. +func (deb *debugger) common() CommonType { + fieldNum := -1 + name := "" + id := typeId(0) + for { + delta := deb.delta(-1) + if delta == 0 { + break + } + fieldNum += delta + switch fieldNum { + case 0: + name = deb.string() + case 1: + // Id typeId + id = deb.typeId() + default: + errorf("corrupted CommonType") + } + } + return CommonType{name, id} +} + +// uint returns the unsigned int at the input point, as a uint (not uint64). +func (deb *debugger) uint() uint { + return uint(deb.uint64()) +} + +// int returns the signed int at the input point, as an int (not int64). +func (deb *debugger) int() int { + return int(deb.int64()) +} + +// typeId returns the type id at the input point. +func (deb *debugger) typeId() typeId { + return typeId(deb.int64()) +} + +// string returns the string at the input point. +func (deb *debugger) string() string { + x := int(deb.uint64()) + b := make([]byte, x) + nb, _ := deb.r.Read(b) + if nb != x { + errorf("corrupted type") + } + deb.consumed(nb) + return string(b) +} + +// delta returns the field delta at the input point. The expect argument, +// if non-negative, identifies what the value should be. +func (deb *debugger) delta(expect int) int { + delta := int(deb.uint64()) + if delta < 0 || (expect >= 0 && delta != expect) { + errorf("decode: corrupted type: delta %d expected %d", delta, expect) + } + return delta +} + +// TypeDefinition: +// [int(-typeId) (already read)] encodingOfWireType +func (deb *debugger) typeDefinition(indent tab, id typeId) { + deb.dump("type definition for id %d", id) + // Encoding is of a wireType. Decode the structure as usual + fieldNum := -1 + wire := new(wireType) + // A wireType defines a single field. + delta := deb.delta(-1) + fieldNum += delta + switch fieldNum { + case 0: // array type, one field of {{Common}, elem, length} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + // Field number 1 is type Id of elem + deb.delta(1) + id := deb.typeId() + // Field number 3 is length + deb.delta(1) + length := deb.int() + wire.ArrayT = &arrayType{com, id, length} + + case 1: // slice type, one field of {{Common}, elem} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + // Field number 1 is type Id of elem + deb.delta(1) + id := deb.typeId() + wire.SliceT = &sliceType{com, id} + + case 2: // struct type, one field of {{Common}, []fieldType} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + // Field number 1 is slice of FieldType + deb.delta(1) + numField := int(deb.uint()) + field := make([]*fieldType, numField) + for i := 0; i < numField; i++ { + field[i] = new(fieldType) + deb.delta(1) // field 0 of fieldType: name + field[i].Name = deb.string() + deb.delta(1) // field 1 of fieldType: id + field[i].Id = deb.typeId() + deb.delta(0) // end of fieldType + } + wire.StructT = &structType{com, field} + + case 3: // map type, one field of {{Common}, key, elem} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + // Field number 1 is type Id of key + deb.delta(1) + keyId := deb.typeId() + // Field number 2 is type Id of elem + deb.delta(1) + elemId := deb.typeId() + wire.MapT = &mapType{com, keyId, elemId} + case 4: // GobEncoder type, one field of {{Common}} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + wire.GobEncoderT = &gobEncoderType{com} + default: + errorf("bad field in type %d", fieldNum) + } + deb.printWireType(indent, wire) + deb.delta(0) // end inner type (arrayType, etc.) + deb.delta(0) // end wireType + // Remember we've seen this type. + deb.wireType[id] = wire +} + +// Value: +// SingletonValue | StructValue +func (deb *debugger) value(indent tab, id typeId) { + wire, ok := deb.wireType[id] + if ok && wire.StructT != nil { + deb.structValue(indent, id) + } else { + deb.singletonValue(indent, id) + } +} + +// SingletonValue: +// uint(0) FieldValue +func (deb *debugger) singletonValue(indent tab, id typeId) { + deb.dump("Singleton value") + // is it a builtin type? + wire := deb.wireType[id] + _, ok := builtinIdToType[id] + if !ok && wire == nil { + errorf("type id %d not defined", id) + } + m := deb.uint64() + if m != 0 { + errorf("expected zero; got %d", m) + } + deb.fieldValue(indent, id) +} + +// InterfaceValue: +// NilInterfaceValue | NonNilInterfaceValue +func (deb *debugger) interfaceValue(indent tab) { + deb.dump("Start of interface value") + if nameLen := deb.uint64(); nameLen == 0 { + deb.nilInterfaceValue(indent) + } else { + deb.nonNilInterfaceValue(indent, int(nameLen)) + } +} + +// NilInterfaceValue: +// uint(0) [already read] +func (deb *debugger) nilInterfaceValue(indent tab) int { + fmt.Fprintf(os.Stderr, "%snil interface\n", indent) + return 0 +} + +// NonNilInterfaceValue: +// ConcreteTypeName TypeSequence InterfaceContents +// ConcreteTypeName: +// uint(lengthOfName) [already read=n] name +// InterfaceContents: +// int(concreteTypeId) DelimitedValue +// DelimitedValue: +// uint(length) Value +func (deb *debugger) nonNilInterfaceValue(indent tab, nameLen int) { + // ConcreteTypeName + b := make([]byte, nameLen) + deb.r.Read(b) // TODO: CHECK THESE READS!! + deb.consumed(nameLen) + name := string(b) + + for { + id := deb.typeId() + if id < 0 { + deb.typeDefinition(indent, -id) + n := deb.loadBlock(false) + deb.dump("Nested message of length %d", n) + } else { + // DelimitedValue + x := deb.uint64() // in case we want to ignore the value; we don't. + fmt.Fprintf(os.Stderr, "%sinterface value, type %q id=%d; valueLength %d\n", indent, name, id, x) + deb.value(indent, id) + break + } + } +} + +// printCommonType prints a common type; used by printWireType. +func (deb *debugger) printCommonType(indent tab, kind string, common *CommonType) { + indent.print() + fmt.Fprintf(os.Stderr, "%s %q id=%d\n", kind, common.Name, common.Id) +} + +// printWireType prints the contents of a wireType. +func (deb *debugger) printWireType(indent tab, wire *wireType) { + fmt.Fprintf(os.Stderr, "%stype definition {\n", indent) + indent++ + switch { + case wire.ArrayT != nil: + deb.printCommonType(indent, "array", &wire.ArrayT.CommonType) + fmt.Fprintf(os.Stderr, "%slen %d\n", indent+1, wire.ArrayT.Len) + fmt.Fprintf(os.Stderr, "%selemid %d\n", indent+1, wire.ArrayT.Elem) + case wire.MapT != nil: + deb.printCommonType(indent, "map", &wire.MapT.CommonType) + fmt.Fprintf(os.Stderr, "%skey id=%d\n", indent+1, wire.MapT.Key) + fmt.Fprintf(os.Stderr, "%selem id=%d\n", indent+1, wire.MapT.Elem) + case wire.SliceT != nil: + deb.printCommonType(indent, "slice", &wire.SliceT.CommonType) + fmt.Fprintf(os.Stderr, "%selem id=%d\n", indent+1, wire.SliceT.Elem) + case wire.StructT != nil: + deb.printCommonType(indent, "struct", &wire.StructT.CommonType) + for i, field := range wire.StructT.Field { + fmt.Fprintf(os.Stderr, "%sfield %d:\t%s\tid=%d\n", indent+1, i, field.Name, field.Id) + } + case wire.GobEncoderT != nil: + deb.printCommonType(indent, "GobEncoder", &wire.GobEncoderT.CommonType) + } + indent-- + fmt.Fprintf(os.Stderr, "%s}\n", indent) +} + +// fieldValue prints a value of any type, such as a struct field. +// FieldValue: +// builtinValue | ArrayValue | MapValue | SliceValue | StructValue | InterfaceValue +func (deb *debugger) fieldValue(indent tab, id typeId) { + _, ok := builtinIdToType[id] + if ok { + if id == tInterface { + deb.interfaceValue(indent) + } else { + deb.printBuiltin(indent, id) + } + return + } + wire, ok := deb.wireType[id] + if !ok { + errorf("type id %d not defined", id) + } + switch { + case wire.ArrayT != nil: + deb.arrayValue(indent, wire) + case wire.MapT != nil: + deb.mapValue(indent, wire) + case wire.SliceT != nil: + deb.sliceValue(indent, wire) + case wire.StructT != nil: + deb.structValue(indent, id) + case wire.GobEncoderT != nil: + deb.gobEncoderValue(indent, id) + default: + panic("bad wire type for field") + } +} + +// printBuiltin prints a value not of a fundamental type, that is, +// one whose type is known to gobs at bootstrap time. +func (deb *debugger) printBuiltin(indent tab, id typeId) { + switch id { + case tBool: + x := deb.int64() + if x == 0 { + fmt.Fprintf(os.Stderr, "%sfalse\n", indent) + } else { + fmt.Fprintf(os.Stderr, "%strue\n", indent) + } + case tInt: + x := deb.int64() + fmt.Fprintf(os.Stderr, "%s%d\n", indent, x) + case tUint: + x := deb.int64() + fmt.Fprintf(os.Stderr, "%s%d\n", indent, x) + case tFloat: + x := deb.uint64() + fmt.Fprintf(os.Stderr, "%s%g\n", indent, floatFromBits(x)) + case tComplex: + r := deb.uint64() + i := deb.uint64() + fmt.Fprintf(os.Stderr, "%s%g+%gi\n", indent, floatFromBits(r), floatFromBits(i)) + case tBytes: + x := int(deb.uint64()) + b := make([]byte, x) + deb.r.Read(b) + deb.consumed(x) + fmt.Fprintf(os.Stderr, "%s{% x}=%q\n", indent, b, b) + case tString: + x := int(deb.uint64()) + b := make([]byte, x) + deb.r.Read(b) + deb.consumed(x) + fmt.Fprintf(os.Stderr, "%s%q\n", indent, b) + default: + panic("unknown builtin") + } +} + +// ArrayValue: +// uint(n) FieldValue*n +func (deb *debugger) arrayValue(indent tab, wire *wireType) { + elemId := wire.ArrayT.Elem + u := deb.uint64() + length := int(u) + for i := 0; i < length; i++ { + deb.fieldValue(indent, elemId) + } + if length != wire.ArrayT.Len { + fmt.Fprintf(os.Stderr, "%s(wrong length for array: %d should be %d)\n", indent, length, wire.ArrayT.Len) + } +} + +// MapValue: +// uint(n) (FieldValue FieldValue)*n [n (key, value) pairs] +func (deb *debugger) mapValue(indent tab, wire *wireType) { + keyId := wire.MapT.Key + elemId := wire.MapT.Elem + u := deb.uint64() + length := int(u) + for i := 0; i < length; i++ { + deb.fieldValue(indent+1, keyId) + deb.fieldValue(indent+1, elemId) + } +} + +// SliceValue: +// uint(n) (n FieldValue) +func (deb *debugger) sliceValue(indent tab, wire *wireType) { + elemId := wire.SliceT.Elem + u := deb.uint64() + length := int(u) + deb.dump("Start of slice of length %d", length) + + for i := 0; i < length; i++ { + deb.fieldValue(indent, elemId) + } +} + +// StructValue: +// (uint(fieldDelta) FieldValue)* +func (deb *debugger) structValue(indent tab, id typeId) { + deb.dump("Start of struct value of %q id=%d\n<<\n", id.name(), id) + fmt.Fprintf(os.Stderr, "%s%s struct {\n", indent, id.name()) + wire, ok := deb.wireType[id] + if !ok { + errorf("type id %d not defined", id) + } + strct := wire.StructT + fieldNum := -1 + indent++ + for { + delta := deb.uint64() + if delta == 0 { // struct terminator is zero delta fieldnum + break + } + fieldNum += int(delta) + if fieldNum < 0 || fieldNum >= len(strct.Field) { + deb.dump("field number out of range: prevField=%d delta=%d", fieldNum-int(delta), delta) + break + } + fmt.Fprintf(os.Stderr, "%sfield %d:\t%s\n", indent, fieldNum, wire.StructT.Field[fieldNum].Name) + deb.fieldValue(indent+1, strct.Field[fieldNum].Id) + } + indent-- + fmt.Fprintf(os.Stderr, "%s} // end %s struct\n", indent, id.name()) + deb.dump(">> End of struct value of type %d %q", id, id.name()) +} + +// GobEncoderValue: +// uint(n) byte*n +func (deb *debugger) gobEncoderValue(indent tab, id typeId) { + len := deb.uint64() + deb.dump("GobEncoder value of %q id=%d, length %d\n", id.name(), id, len) + fmt.Fprintf(os.Stderr, "%s%s (implements GobEncoder)\n", indent, id.name()) + data := make([]byte, len) + _, err := deb.r.Read(data) + if err != nil { + errorf("gobEncoder data read: %s", err) + } + fmt.Fprintf(os.Stderr, "%s[% .2x]\n", indent+1, data) +} diff --git a/src/pkg/encoding/gob/decode.go b/src/pkg/encoding/gob/decode.go new file mode 100644 index 000000000..e32a178ab --- /dev/null +++ b/src/pkg/encoding/gob/decode.go @@ -0,0 +1,1300 @@ +// 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 gob + +// TODO(rsc): When garbage collector changes, revisit +// the allocations in this file that use unsafe.Pointer. + +import ( + "bytes" + "errors" + "io" + "math" + "reflect" + "unsafe" +) + +var ( + errBadUint = errors.New("gob: encoded unsigned integer out of range") + errBadType = errors.New("gob: unknown type id or corrupted data") + errRange = errors.New("gob: bad data: field numbers out of bounds") +) + +// decoderState is the execution state of an instance of the decoder. A new state +// is created for nested objects. +type decoderState struct { + dec *Decoder + // The buffer is stored with an extra indirection because it may be replaced + // if we load a type during decode (when reading an interface value). + b *bytes.Buffer + fieldnum int // the last field number read. + buf []byte + next *decoderState // for free list +} + +// We pass the bytes.Buffer separately for easier testing of the infrastructure +// without requiring a full Decoder. +func (dec *Decoder) newDecoderState(buf *bytes.Buffer) *decoderState { + d := dec.freeList + if d == nil { + d = new(decoderState) + d.dec = dec + d.buf = make([]byte, uint64Size) + } else { + dec.freeList = d.next + } + d.b = buf + return d +} + +func (dec *Decoder) freeDecoderState(d *decoderState) { + d.next = dec.freeList + dec.freeList = d +} + +func overflow(name string) error { + return errors.New(`value for "` + name + `" out of range`) +} + +// decodeUintReader reads an encoded unsigned integer from an io.Reader. +// 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 { + return + } + b := buf[0] + if b <= 0x7f { + return uint64(b), width, nil + } + n := -int(int8(b)) + if n > uint64Size { + err = errBadUint + return + } + width, err = io.ReadFull(r, buf[0:n]) + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return + } + // Could check that the high byte is zero but it's not worth it. + for _, b := range buf[0:width] { + x = x<<8 | uint64(b) + } + width++ // +1 for length byte + return +} + +// decodeUint reads an encoded unsigned integer from state.r. +// Does not check for overflow. +func (state *decoderState) decodeUint() (x uint64) { + b, err := state.b.ReadByte() + if err != nil { + error_(err) + } + if b <= 0x7f { + return uint64(b) + } + n := -int(int8(b)) + if n > uint64Size { + error_(errBadUint) + } + width, err := state.b.Read(state.buf[0:n]) + if err != nil { + error_(err) + } + // Don't need to check error; it's safe to loop regardless. + // Could check that the high byte is zero but it's not worth it. + for _, b := range state.buf[0:width] { + x = x<<8 | uint64(b) + } + return x +} + +// decodeInt reads an encoded signed integer from state.r. +// Does not check for overflow. +func (state *decoderState) decodeInt() int64 { + x := state.decodeUint() + if x&1 != 0 { + return ^int64(x >> 1) + } + return int64(x >> 1) +} + +// decOp is the signature of a decoding operator for a given type. +type decOp func(i *decInstr, state *decoderState, p unsafe.Pointer) + +// The 'instructions' of the decoding machine +type decInstr struct { + op decOp + field int // field number of the wire type + indir int // how many pointer indirections to reach the value in the struct + offset uintptr // offset in the structure of the field to encode + ovfl error // error message for overflow/underflow (for arrays, of the elements) +} + +// Since the encoder writes no zeros, if we arrive at a decoder we have +// a value to extract and store. The field number has already been read +// (it's how we knew to call this decoder). +// Each decoder is responsible for handling any indirections associated +// with the data structure. If any pointer so reached is nil, allocation must +// be done. + +// Walk the pointer hierarchy, allocating if we find a nil. Stop one before the end. +func decIndirect(p unsafe.Pointer, indir int) unsafe.Pointer { + for ; indir > 1; indir-- { + if *(*unsafe.Pointer)(p) == nil { + // Allocation required + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(unsafe.Pointer)) + } + p = *(*unsafe.Pointer)(p) + } + return p +} + +// ignoreUint discards a uint value with no destination. +func ignoreUint(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.decodeUint() +} + +// ignoreTwoUints discards a uint value with no destination. It's used to skip +// complex values. +func ignoreTwoUints(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.decodeUint() + state.decodeUint() +} + +// decBool decodes a uint and stores it as a boolean through p. +func decBool(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(bool)) + } + p = *(*unsafe.Pointer)(p) + } + *(*bool)(p) = state.decodeUint() != 0 +} + +// decInt8 decodes an integer and stores it as an int8 through p. +func decInt8(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(int8)) + } + p = *(*unsafe.Pointer)(p) + } + v := state.decodeInt() + if v < math.MinInt8 || math.MaxInt8 < v { + error_(i.ovfl) + } else { + *(*int8)(p) = int8(v) + } +} + +// decUint8 decodes an unsigned integer and stores it as a uint8 through p. +func decUint8(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(uint8)) + } + p = *(*unsafe.Pointer)(p) + } + v := state.decodeUint() + if math.MaxUint8 < v { + error_(i.ovfl) + } else { + *(*uint8)(p) = uint8(v) + } +} + +// decInt16 decodes an integer and stores it as an int16 through p. +func decInt16(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(int16)) + } + p = *(*unsafe.Pointer)(p) + } + v := state.decodeInt() + if v < math.MinInt16 || math.MaxInt16 < v { + error_(i.ovfl) + } else { + *(*int16)(p) = int16(v) + } +} + +// decUint16 decodes an unsigned integer and stores it as a uint16 through p. +func decUint16(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(uint16)) + } + p = *(*unsafe.Pointer)(p) + } + v := state.decodeUint() + if math.MaxUint16 < v { + error_(i.ovfl) + } else { + *(*uint16)(p) = uint16(v) + } +} + +// decInt32 decodes an integer and stores it as an int32 through p. +func decInt32(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(int32)) + } + p = *(*unsafe.Pointer)(p) + } + v := state.decodeInt() + if v < math.MinInt32 || math.MaxInt32 < v { + error_(i.ovfl) + } else { + *(*int32)(p) = int32(v) + } +} + +// decUint32 decodes an unsigned integer and stores it as a uint32 through p. +func decUint32(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(uint32)) + } + p = *(*unsafe.Pointer)(p) + } + v := state.decodeUint() + if math.MaxUint32 < v { + error_(i.ovfl) + } else { + *(*uint32)(p) = uint32(v) + } +} + +// decInt64 decodes an integer and stores it as an int64 through p. +func decInt64(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(int64)) + } + p = *(*unsafe.Pointer)(p) + } + *(*int64)(p) = int64(state.decodeInt()) +} + +// decUint64 decodes an unsigned integer and stores it as a uint64 through p. +func decUint64(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(uint64)) + } + p = *(*unsafe.Pointer)(p) + } + *(*uint64)(p) = uint64(state.decodeUint()) +} + +// Floating-point numbers are transmitted as uint64s holding the bits +// of the underlying representation. They are sent byte-reversed, with +// the exponent end coming out first, so integer floating point numbers +// (for example) transmit more compactly. This routine does the +// unswizzling. +func floatFromBits(u uint64) float64 { + var v uint64 + for i := 0; i < 8; i++ { + v <<= 8 + v |= u & 0xFF + u >>= 8 + } + return math.Float64frombits(v) +} + +// storeFloat32 decodes an unsigned integer, treats it as a 32-bit floating-point +// number, and stores it through p. It's a helper function for float32 and complex64. +func storeFloat32(i *decInstr, state *decoderState, p unsafe.Pointer) { + v := floatFromBits(state.decodeUint()) + av := v + if av < 0 { + av = -av + } + // +Inf is OK in both 32- and 64-bit floats. Underflow is always OK. + if math.MaxFloat32 < av && av <= math.MaxFloat64 { + error_(i.ovfl) + } else { + *(*float32)(p) = float32(v) + } +} + +// decFloat32 decodes an unsigned integer, treats it as a 32-bit floating-point +// number, and stores it through p. +func decFloat32(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(float32)) + } + p = *(*unsafe.Pointer)(p) + } + storeFloat32(i, state, p) +} + +// decFloat64 decodes an unsigned integer, treats it as a 64-bit floating-point +// number, and stores it through p. +func decFloat64(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(float64)) + } + p = *(*unsafe.Pointer)(p) + } + *(*float64)(p) = floatFromBits(uint64(state.decodeUint())) +} + +// decComplex64 decodes a pair of unsigned integers, treats them as a +// pair of floating point numbers, and stores them as a complex64 through p. +// The real part comes first. +func decComplex64(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(complex64)) + } + p = *(*unsafe.Pointer)(p) + } + storeFloat32(i, state, p) + storeFloat32(i, state, unsafe.Pointer(uintptr(p)+unsafe.Sizeof(float32(0)))) +} + +// decComplex128 decodes a pair of unsigned integers, treats them as a +// pair of floating point numbers, and stores them as a complex128 through p. +// The real part comes first. +func decComplex128(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(complex128)) + } + p = *(*unsafe.Pointer)(p) + } + real := floatFromBits(uint64(state.decodeUint())) + imag := floatFromBits(uint64(state.decodeUint())) + *(*complex128)(p) = complex(real, imag) +} + +// decUint8Slice decodes a byte slice and stores through p a slice header +// describing the data. +// uint8 slices are encoded as an unsigned count followed by the raw bytes. +func decUint8Slice(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new([]uint8)) + } + p = *(*unsafe.Pointer)(p) + } + n := state.decodeUint() + if n > uint64(state.b.Len()) { + errorf("length of []byte exceeds input size (%d bytes)", n) + } + slice := (*[]uint8)(p) + if uint64(cap(*slice)) < n { + *slice = make([]uint8, n) + } else { + *slice = (*slice)[0:n] + } + if _, err := state.b.Read(*slice); err != nil { + errorf("error decoding []byte: %s", err) + } +} + +// decString decodes byte array and stores through p a string header +// describing the data. +// Strings are encoded as an unsigned count followed by the raw bytes. +func decString(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(string)) + } + p = *(*unsafe.Pointer)(p) + } + n := state.decodeUint() + if n > uint64(state.b.Len()) { + errorf("string length exceeds input size (%d bytes)", n) + } + b := make([]byte, n) + state.b.Read(b) + // It would be a shame to do the obvious thing here, + // *(*string)(p) = string(b) + // because we've already allocated the storage and this would + // allocate again and copy. So we do this ugly hack, which is even + // even more unsafe than it looks as it depends the memory + // representation of a string matching the beginning of the memory + // representation of a byte slice (a byte slice is longer). + *(*string)(p) = *(*string)(unsafe.Pointer(&b)) +} + +// ignoreUint8Array skips over the data for a byte slice value with no destination. +func ignoreUint8Array(i *decInstr, state *decoderState, p unsafe.Pointer) { + b := make([]byte, state.decodeUint()) + state.b.Read(b) +} + +// Execution engine + +// The encoder engine is an array of instructions indexed by field number of the incoming +// decoder. It is executed with random access according to field number. +type decEngine struct { + instr []decInstr + numInstr int // the number of active instructions +} + +// allocate makes sure storage is available for an object of underlying type rtyp +// that is indir levels of indirection through p. +func allocate(rtyp reflect.Type, p uintptr, indir int) uintptr { + if indir == 0 { + return p + } + up := unsafe.Pointer(p) + if indir > 1 { + up = decIndirect(up, indir) + } + if *(*unsafe.Pointer)(up) == nil { + // Allocate object. + *(*unsafe.Pointer)(up) = unsafe.Pointer(reflect.New(rtyp).Pointer()) + } + return *(*uintptr)(up) +} + +// decodeSingle decodes a top-level value that is not a struct and stores it through p. +// Such values are preceded by a zero, making them have the memory layout of a +// struct field (although with an illegal field number). +func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uintptr) { + state := dec.newDecoderState(&dec.buf) + state.fieldnum = singletonField + delta := int(state.decodeUint()) + if delta != 0 { + errorf("decode: corrupted data: non-zero delta for singleton") + } + instr := &engine.instr[singletonField] + if instr.indir != ut.indir { + errorf("internal error: inconsistent indirection instr %d ut %d", instr.indir, ut.indir) + } + ptr := unsafe.Pointer(basep) // offset will be zero + if instr.indir > 1 { + ptr = decIndirect(ptr, instr.indir) + } + instr.op(instr, state, ptr) + dec.freeDecoderState(state) +} + +// decodeStruct decodes a top-level struct and stores it through p. +// Indir is for the value, not the type. At the time of the call it may +// differ from ut.indir, which was computed when the engine was built. +// This state cannot arise for decodeSingle, which is called directly +// from the user's value, not from the innards of an engine. +func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p uintptr, indir int) { + p = allocate(ut.base, p, indir) + state := dec.newDecoderState(&dec.buf) + state.fieldnum = -1 + basep := p + for state.b.Len() > 0 { + delta := int(state.decodeUint()) + if delta < 0 { + errorf("decode: corrupted data: negative delta") + } + if delta == 0 { // struct terminator is zero delta fieldnum + break + } + fieldnum := state.fieldnum + delta + if fieldnum >= len(engine.instr) { + error_(errRange) + break + } + instr := &engine.instr[fieldnum] + p := unsafe.Pointer(basep + instr.offset) + if instr.indir > 1 { + p = decIndirect(p, instr.indir) + } + instr.op(instr, state, p) + state.fieldnum = fieldnum + } + dec.freeDecoderState(state) +} + +// ignoreStruct discards the data for a struct with no destination. +func (dec *Decoder) ignoreStruct(engine *decEngine) { + state := dec.newDecoderState(&dec.buf) + state.fieldnum = -1 + for state.b.Len() > 0 { + delta := int(state.decodeUint()) + if delta < 0 { + errorf("ignore decode: corrupted data: negative delta") + } + if delta == 0 { // struct terminator is zero delta fieldnum + break + } + fieldnum := state.fieldnum + delta + if fieldnum >= len(engine.instr) { + error_(errRange) + } + instr := &engine.instr[fieldnum] + instr.op(instr, state, unsafe.Pointer(nil)) + state.fieldnum = fieldnum + } + dec.freeDecoderState(state) +} + +// ignoreSingle discards the data for a top-level non-struct value with no +// destination. It's used when calling Decode with a nil value. +func (dec *Decoder) ignoreSingle(engine *decEngine) { + state := dec.newDecoderState(&dec.buf) + state.fieldnum = singletonField + delta := int(state.decodeUint()) + if delta != 0 { + errorf("decode: corrupted data: non-zero delta for singleton") + } + instr := &engine.instr[singletonField] + instr.op(instr, state, unsafe.Pointer(nil)) + dec.freeDecoderState(state) +} + +// decodeArrayHelper does the work for decoding arrays and slices. +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++ { + up := unsafe.Pointer(p) + if elemIndir > 1 { + up = decIndirect(up, elemIndir) + } + elemOp(instr, state, up) + p += uintptr(elemWid) + } +} + +// decodeArray decodes an array and stores it through p, that is, p points to the zeroth element. +// The length is an unsigned integer preceding the elements. Even though the length is redundant +// (it's part of the type), it's a useful check and is included in the encoding. +func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl error) { + if indir > 0 { + p = allocate(atyp, p, 1) // All but the last level has been allocated by dec.Indirect + } + if n := state.decodeUint(); n != uint64(length) { + errorf("length mismatch in decodeArray") + } + dec.decodeArrayHelper(state, p, elemOp, elemWid, length, elemIndir, ovfl) +} + +// decodeIntoValue is a helper for map decoding. Since maps are decoded using reflection, +// unlike the other items we can't use a pointer directly. +func decodeIntoValue(state *decoderState, op decOp, indir int, v reflect.Value, ovfl error) reflect.Value { + instr := &decInstr{op, 0, indir, 0, ovfl} + up := unsafe.Pointer(unsafeAddr(v)) + if indir > 1 { + up = decIndirect(up, indir) + } + op(instr, state, up) + return v +} + +// decodeMap decodes a map and stores its header through p. +// Maps are encoded as a length followed by key:value pairs. +// Because the internals of maps are not visible to us, we must +// use reflection rather than pointer magic. +func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, p uintptr, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl error) { + if indir > 0 { + p = allocate(mtyp, p, 1) // All but the last level has been allocated by dec.Indirect + } + up := unsafe.Pointer(p) + if *(*unsafe.Pointer)(up) == nil { // maps are represented as a pointer in the runtime + // Allocate map. + *(*unsafe.Pointer)(up) = unsafe.Pointer(reflect.MakeMap(mtyp).Pointer()) + } + // Maps cannot be accessed by moving addresses around the way + // that slices etc. can. We must recover a full reflection value for + // the iteration. + v := reflect.NewAt(mtyp, unsafe.Pointer(p)).Elem() + n := int(state.decodeUint()) + for i := 0; i < n; i++ { + key := decodeIntoValue(state, keyOp, keyIndir, allocValue(mtyp.Key()), ovfl) + elem := decodeIntoValue(state, elemOp, elemIndir, allocValue(mtyp.Elem()), ovfl) + v.SetMapIndex(key, elem) + } +} + +// ignoreArrayHelper does the work for discarding arrays and slices. +func (dec *Decoder) ignoreArrayHelper(state *decoderState, elemOp decOp, length int) { + instr := &decInstr{elemOp, 0, 0, 0, errors.New("no error")} + for i := 0; i < length; i++ { + elemOp(instr, state, nil) + } +} + +// ignoreArray discards the data for an array value with no destination. +func (dec *Decoder) ignoreArray(state *decoderState, elemOp decOp, length int) { + if n := state.decodeUint(); n != uint64(length) { + errorf("length mismatch in ignoreArray") + } + dec.ignoreArrayHelper(state, elemOp, length) +} + +// ignoreMap discards the data for a map value with no destination. +func (dec *Decoder) ignoreMap(state *decoderState, keyOp, elemOp decOp) { + n := int(state.decodeUint()) + keyInstr := &decInstr{keyOp, 0, 0, 0, errors.New("no error")} + elemInstr := &decInstr{elemOp, 0, 0, 0, errors.New("no error")} + for i := 0; i < n; i++ { + keyOp(keyInstr, state, nil) + elemOp(elemInstr, state, nil) + } +} + +// decodeSlice decodes a slice and stores the slice header through p. +// 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) + if *(*unsafe.Pointer)(up) == nil { + // Allocate the slice header. + *(*unsafe.Pointer)(up) = unsafe.Pointer(new([]unsafe.Pointer)) + } + p = *(*uintptr)(up) + } + // Allocate storage for the slice elements, that is, the underlying array, + // if the existing slice does not have the capacity. + // Always write a header at p. + hdrp := (*reflect.SliceHeader)(unsafe.Pointer(p)) + if hdrp.Cap < n { + hdrp.Data = reflect.MakeSlice(atyp, n, n).Pointer() + hdrp.Cap = n + } + hdrp.Len = n + dec.decodeArrayHelper(state, hdrp.Data, elemOp, elemWid, n, elemIndir, ovfl) +} + +// ignoreSlice skips over the data for a slice value with no destination. +func (dec *Decoder) ignoreSlice(state *decoderState, elemOp decOp) { + dec.ignoreArrayHelper(state, elemOp, int(state.decodeUint())) +} + +// setInterfaceValue sets an interface value to a concrete value, +// but first it checks that the assignment will succeed. +func setInterfaceValue(ivalue reflect.Value, value reflect.Value) { + if !value.Type().AssignableTo(ivalue.Type()) { + errorf("cannot assign value of type %s to %s", value.Type(), ivalue.Type()) + } + ivalue.Set(value) +} + +// decodeInterface decodes an interface value and stores it through p. +// Interfaces are encoded as the name of a concrete type followed by a value. +// If the name is empty, the value is nil and no value is sent. +func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p uintptr, indir int) { + // Create a writable interface reflect.Value. We need one even for the nil case. + ivalue := allocValue(ityp) + // Read the name of the concrete type. + nr := state.decodeUint() + if nr < 0 || nr > 1<<31 { // zero is permissible for anonymous types + errorf("invalid type name length %d", nr) + } + b := make([]byte, nr) + state.b.Read(b) + name := string(b) + if name == "" { + // Copy the representation of the nil interface value to the target. + // This is horribly unsafe and special. + if indir > 0 { + p = allocate(ityp, p, 1) // All but the last level has been allocated by dec.Indirect + } + *(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.InterfaceData() + return + } + if len(name) > 1024 { + errorf("name too long (%d bytes): %.20q...", len(name), name) + } + // The concrete type must be registered. + typ, ok := nameToConcreteType[name] + if !ok { + errorf("name not registered for interface: %q", name) + } + // Read the type id of the concrete value. + concreteId := dec.decodeTypeSequence(true) + if concreteId < 0 { + error_(dec.err) + } + // Byte count of value is next; we don't care what it is (it's there + // in case we want to ignore the value by skipping it completely). + state.decodeUint() + // Read the concrete value. + value := allocValue(typ) + dec.decodeValue(concreteId, value) + if dec.err != nil { + error_(dec.err) + } + // Allocate the destination interface value. + if indir > 0 { + p = allocate(ityp, p, 1) // All but the last level has been allocated by dec.Indirect + } + // Assign the concrete value to the interface. + // Tread carefully; it might not satisfy the interface. + setInterfaceValue(ivalue, value) + // Copy the representation of the interface value to the target. + // This is horribly unsafe and special. + *(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.InterfaceData() +} + +// ignoreInterface discards the data for an interface value with no destination. +func (dec *Decoder) ignoreInterface(state *decoderState) { + // Read the name of the concrete type. + b := make([]byte, state.decodeUint()) + _, err := state.b.Read(b) + if err != nil { + error_(err) + } + id := dec.decodeTypeSequence(true) + if id < 0 { + error_(dec.err) + } + // At this point, the decoder buffer contains a delimited value. Just toss it. + state.b.Next(int(state.decodeUint())) +} + +// decodeGobDecoder decodes something implementing the GobDecoder interface. +// The data is encoded as a byte slice. +func (dec *Decoder) decodeGobDecoder(state *decoderState, v reflect.Value) { + // Read the bytes for the value. + b := make([]byte, state.decodeUint()) + _, err := state.b.Read(b) + if err != nil { + error_(err) + } + // We know it's a GobDecoder, so just call the method directly. + err = v.Interface().(GobDecoder).GobDecode(b) + if err != nil { + error_(err) + } +} + +// ignoreGobDecoder discards the data for a GobDecoder value with no destination. +func (dec *Decoder) ignoreGobDecoder(state *decoderState) { + // Read the bytes for the value. + b := make([]byte, state.decodeUint()) + _, err := state.b.Read(b) + if err != nil { + error_(err) + } +} + +// Index by Go types. +var decOpTable = [...]decOp{ + reflect.Bool: decBool, + reflect.Int8: decInt8, + reflect.Int16: decInt16, + reflect.Int32: decInt32, + reflect.Int64: decInt64, + reflect.Uint8: decUint8, + reflect.Uint16: decUint16, + reflect.Uint32: decUint32, + reflect.Uint64: decUint64, + reflect.Float32: decFloat32, + reflect.Float64: decFloat64, + reflect.Complex64: decComplex64, + reflect.Complex128: decComplex128, + reflect.String: decString, +} + +// Indexed by gob types. tComplex will be added during type.init(). +var decIgnoreOpMap = map[typeId]decOp{ + tBool: ignoreUint, + tInt: ignoreUint, + tUint: ignoreUint, + tFloat: ignoreUint, + tBytes: ignoreUint8Array, + tString: ignoreUint8Array, + tComplex: ignoreTwoUints, +} + +// decOpFor returns the decoding op for the base type under rt and +// the indirection count to reach it. +func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProgress map[reflect.Type]*decOp) (*decOp, int) { + ut := userType(rt) + // If the type implements GobEncoder, we handle it without further processing. + if ut.isGobDecoder { + return dec.gobDecodeOpFor(ut) + } + // If this type is already in progress, it's a recursive type (e.g. map[string]*T). + // Return the pointer to the op we're already building. + if opPtr := inProgress[rt]; opPtr != nil { + return opPtr, ut.indir + } + typ := ut.base + indir := ut.indir + var op decOp + k := typ.Kind() + if int(k) < len(decOpTable) { + op = decOpTable[k] + } + if op == nil { + inProgress[rt] = &op + // Special cases + switch t := typ; t.Kind() { + case reflect.Array: + name = "element of " + name + elemId := dec.wireType[wireId].ArrayT.Elem + elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name, inProgress) + ovfl := overflow(name) + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.decodeArray(t, state, uintptr(p), *elemOp, t.Elem().Size(), t.Len(), i.indir, elemIndir, ovfl) + } + + case reflect.Map: + keyId := dec.wireType[wireId].MapT.Key + elemId := dec.wireType[wireId].MapT.Elem + keyOp, keyIndir := dec.decOpFor(keyId, t.Key(), "key of "+name, inProgress) + elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), "element of "+name, inProgress) + ovfl := overflow(name) + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + up := unsafe.Pointer(p) + state.dec.decodeMap(t, state, uintptr(up), *keyOp, *elemOp, i.indir, keyIndir, elemIndir, ovfl) + } + + case reflect.Slice: + name = "element of " + name + if t.Elem().Kind() == reflect.Uint8 { + op = decUint8Slice + break + } + var elemId typeId + if tt, ok := builtinIdToType[wireId]; ok { + elemId = tt.(*sliceType).Elem + } else { + elemId = dec.wireType[wireId].SliceT.Elem + } + elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name, inProgress) + ovfl := overflow(name) + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.decodeSlice(t, state, uintptr(p), *elemOp, t.Elem().Size(), i.indir, elemIndir, ovfl) + } + + case reflect.Struct: + // Generate a closure that calls out to the engine for the nested type. + enginePtr, err := dec.getDecEnginePtr(wireId, userType(typ)) + if err != nil { + error_(err) + } + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + // indirect through enginePtr to delay evaluation for recursive structs. + dec.decodeStruct(*enginePtr, userType(typ), uintptr(p), i.indir) + } + case reflect.Interface: + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.decodeInterface(t, state, uintptr(p), i.indir) + } + } + } + if op == nil { + errorf("decode can't handle type %s", rt) + } + return &op, indir +} + +// decIgnoreOpFor returns the decoding op for a field that has no destination. +func (dec *Decoder) decIgnoreOpFor(wireId typeId) decOp { + op, ok := decIgnoreOpMap[wireId] + if !ok { + if wireId == tInterface { + // Special case because it's a method: the ignored item might + // define types and we need to record their state in the decoder. + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.ignoreInterface(state) + } + return op + } + // Special cases + wire := dec.wireType[wireId] + switch { + case wire == nil: + errorf("bad data: undefined type %s", wireId.string()) + case wire.ArrayT != nil: + elemId := wire.ArrayT.Elem + elemOp := dec.decIgnoreOpFor(elemId) + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.ignoreArray(state, elemOp, wire.ArrayT.Len) + } + + case wire.MapT != nil: + keyId := dec.wireType[wireId].MapT.Key + elemId := dec.wireType[wireId].MapT.Elem + keyOp := dec.decIgnoreOpFor(keyId) + elemOp := dec.decIgnoreOpFor(elemId) + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.ignoreMap(state, keyOp, elemOp) + } + + case wire.SliceT != nil: + elemId := wire.SliceT.Elem + elemOp := dec.decIgnoreOpFor(elemId) + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.ignoreSlice(state, elemOp) + } + + case wire.StructT != nil: + // Generate a closure that calls out to the engine for the nested type. + enginePtr, err := dec.getIgnoreEnginePtr(wireId) + if err != nil { + error_(err) + } + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + // indirect through enginePtr to delay evaluation for recursive structs + state.dec.ignoreStruct(*enginePtr) + } + + case wire.GobEncoderT != nil: + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.ignoreGobDecoder(state) + } + } + } + if op == nil { + errorf("bad data: ignore can't handle type %s", wireId.string()) + } + return op +} + +// gobDecodeOpFor returns the op for a type that is known to implement +// GobDecoder. +func (dec *Decoder) gobDecodeOpFor(ut *userTypeInfo) (*decOp, int) { + rcvrType := ut.user + if ut.decIndir == -1 { + rcvrType = reflect.PtrTo(rcvrType) + } else if ut.decIndir > 0 { + for i := int8(0); i < ut.decIndir; i++ { + rcvrType = rcvrType.Elem() + } + } + var op decOp + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + // Caller has gotten us to within one indirection of our value. + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(reflect.New(ut.base).Pointer()) + } + } + // Now p is a pointer to the base type. Do we need to climb out to + // get to the receiver type? + var v reflect.Value + if ut.decIndir == -1 { + v = reflect.NewAt(rcvrType, unsafe.Pointer(&p)).Elem() + } else { + v = reflect.NewAt(rcvrType, p).Elem() + } + state.dec.decodeGobDecoder(state, v) + } + return &op, int(ut.indir) + +} + +// compatibleType asks: Are these two gob Types compatible? +// Answers the question for basic types, arrays, maps and slices, plus +// GobEncoder/Decoder pairs. +// Structs are considered ok; fields will be checked later. +func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId, inProgress map[reflect.Type]typeId) bool { + if rhs, ok := inProgress[fr]; ok { + return rhs == fw + } + inProgress[fr] = fw + ut := userType(fr) + wire, ok := dec.wireType[fw] + // If fr is a GobDecoder, the wire type must be GobEncoder. + // And if fr is not a GobDecoder, the wire type must not be either. + if ut.isGobDecoder != (ok && wire.GobEncoderT != nil) { // the parentheses look odd but are correct. + return false + } + if ut.isGobDecoder { // This test trumps all others. + return true + } + switch t := ut.base; t.Kind() { + default: + // chan, etc: cannot handle. + return false + case reflect.Bool: + return fw == tBool + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return fw == tInt + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return fw == tUint + case reflect.Float32, reflect.Float64: + return fw == tFloat + case reflect.Complex64, reflect.Complex128: + return fw == tComplex + case reflect.String: + return fw == tString + case reflect.Interface: + return fw == tInterface + case reflect.Array: + if !ok || wire.ArrayT == nil { + return false + } + array := wire.ArrayT + return t.Len() == array.Len && dec.compatibleType(t.Elem(), array.Elem, inProgress) + case reflect.Map: + if !ok || wire.MapT == nil { + return false + } + MapType := wire.MapT + return dec.compatibleType(t.Key(), MapType.Key, inProgress) && dec.compatibleType(t.Elem(), MapType.Elem, inProgress) + case reflect.Slice: + // Is it an array of bytes? + if t.Elem().Kind() == reflect.Uint8 { + return fw == tBytes + } + // Extract and compare element types. + var sw *sliceType + if tt, ok := builtinIdToType[fw]; ok { + sw, _ = tt.(*sliceType) + } else if wire != nil { + sw = wire.SliceT + } + elem := userType(t.Elem()).base + return sw != nil && dec.compatibleType(elem, sw.Elem, inProgress) + case reflect.Struct: + return true + } + return true +} + +// typeString returns a human-readable description of the type identified by remoteId. +func (dec *Decoder) typeString(remoteId typeId) string { + if t := idToType[remoteId]; t != nil { + // globally known type. + return t.string() + } + return dec.wireType[remoteId].string() +} + +// compileSingle compiles the decoder engine for a non-struct top-level value, including +// GobDecoders. +func (dec *Decoder) compileSingle(remoteId typeId, ut *userTypeInfo) (engine *decEngine, err error) { + rt := ut.user + engine = new(decEngine) + engine.instr = make([]decInstr, 1) // one item + name := rt.String() // best we can do + if !dec.compatibleType(rt, remoteId, make(map[reflect.Type]typeId)) { + remoteType := dec.typeString(remoteId) + // Common confusing case: local interface type, remote concrete type. + if ut.base.Kind() == reflect.Interface && remoteId != tInterface { + return nil, errors.New("gob: local interface type " + name + " can only be decoded from remote interface type; received concrete type " + remoteType) + } + return nil, errors.New("gob: decoding into local type " + name + ", received remote type " + remoteType) + } + op, indir := dec.decOpFor(remoteId, rt, name, make(map[reflect.Type]*decOp)) + ovfl := errors.New(`value for "` + name + `" out of range`) + engine.instr[singletonField] = decInstr{*op, singletonField, indir, 0, ovfl} + engine.numInstr = 1 + return +} + +// compileIgnoreSingle compiles the decoder engine for a non-struct top-level value that will be discarded. +func (dec *Decoder) compileIgnoreSingle(remoteId typeId) (engine *decEngine, err error) { + engine = new(decEngine) + engine.instr = make([]decInstr, 1) // one item + op := dec.decIgnoreOpFor(remoteId) + ovfl := overflow(dec.typeString(remoteId)) + engine.instr[0] = decInstr{op, 0, 0, 0, ovfl} + engine.numInstr = 1 + return +} + +// compileDec compiles the decoder engine for a value. If the value is not a struct, +// it calls out to compileSingle. +func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEngine, err error) { + rt := ut.base + srt := rt + if srt.Kind() != reflect.Struct || + ut.isGobDecoder { + return dec.compileSingle(remoteId, ut) + } + var wireStruct *structType + // Builtin types can come from global pool; the rest must be defined by the decoder. + // Also we know we're decoding a struct now, so the client must have sent one. + if t, ok := builtinIdToType[remoteId]; ok { + wireStruct, _ = t.(*structType) + } else { + wire := dec.wireType[remoteId] + if wire == nil { + error_(errBadType) + } + wireStruct = wire.StructT + } + if wireStruct == nil { + errorf("type mismatch in decoder: want struct type %s; got non-struct", rt) + } + engine = new(decEngine) + engine.instr = make([]decInstr, len(wireStruct.Field)) + seen := make(map[reflect.Type]*decOp) + // Loop over the fields of the wire type. + for fieldnum := 0; fieldnum < len(wireStruct.Field); fieldnum++ { + wireField := wireStruct.Field[fieldnum] + if wireField.Name == "" { + errorf("empty name for remote field of type %s", wireStruct.Name) + } + ovfl := overflow(wireField.Name) + // Find the field of the local type with the same name. + localField, present := srt.FieldByName(wireField.Name) + // TODO(r): anonymous names + if !present || !isExported(wireField.Name) { + op := dec.decIgnoreOpFor(wireField.Id) + engine.instr[fieldnum] = decInstr{op, fieldnum, 0, 0, ovfl} + continue + } + if !dec.compatibleType(localField.Type, wireField.Id, make(map[reflect.Type]typeId)) { + errorf("wrong type (%s) for received field %s.%s", localField.Type, wireStruct.Name, wireField.Name) + } + op, indir := dec.decOpFor(wireField.Id, localField.Type, localField.Name, seen) + engine.instr[fieldnum] = decInstr{*op, fieldnum, indir, uintptr(localField.Offset), ovfl} + engine.numInstr++ + } + return +} + +// getDecEnginePtr returns the engine for the specified type. +func (dec *Decoder) getDecEnginePtr(remoteId typeId, ut *userTypeInfo) (enginePtr **decEngine, err error) { + rt := ut.user + decoderMap, ok := dec.decoderCache[rt] + if !ok { + decoderMap = make(map[typeId]**decEngine) + dec.decoderCache[rt] = decoderMap + } + if enginePtr, ok = decoderMap[remoteId]; !ok { + // To handle recursive types, mark this engine as underway before compiling. + enginePtr = new(*decEngine) + decoderMap[remoteId] = enginePtr + *enginePtr, err = dec.compileDec(remoteId, ut) + if err != nil { + delete(decoderMap, remoteId) + } + } + return +} + +// emptyStruct is the type we compile into when ignoring a struct value. +type emptyStruct struct{} + +var emptyStructType = reflect.TypeOf(emptyStruct{}) + +// getDecEnginePtr returns the engine for the specified type when the value is to be discarded. +func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, err error) { + var ok bool + if enginePtr, ok = dec.ignorerCache[wireId]; !ok { + // To handle recursive types, mark this engine as underway before compiling. + enginePtr = new(*decEngine) + dec.ignorerCache[wireId] = enginePtr + wire := dec.wireType[wireId] + if wire != nil && wire.StructT != nil { + *enginePtr, err = dec.compileDec(wireId, userType(emptyStructType)) + } else { + *enginePtr, err = dec.compileIgnoreSingle(wireId) + } + if err != nil { + delete(dec.ignorerCache, wireId) + } + } + return +} + +// decodeValue decodes the data stream representing a value and stores it in val. +func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) { + defer catchError(&dec.err) + // If the value is nil, it means we should just ignore this item. + if !val.IsValid() { + dec.decodeIgnoredValue(wireId) + return + } + // Dereference down to the underlying type. + ut := userType(val.Type()) + base := ut.base + var enginePtr **decEngine + enginePtr, dec.err = dec.getDecEnginePtr(wireId, ut) + if dec.err != nil { + return + } + engine := *enginePtr + if st := base; st.Kind() == reflect.Struct && !ut.isGobDecoder { + if engine.numInstr == 0 && st.NumField() > 0 && len(dec.wireType[wireId].StructT.Field) > 0 { + name := base.Name() + errorf("type mismatch: no fields matched compiling decoder for %s", name) + } + dec.decodeStruct(engine, ut, uintptr(unsafeAddr(val)), ut.indir) + } else { + dec.decodeSingle(engine, ut, uintptr(unsafeAddr(val))) + } +} + +// decodeIgnoredValue decodes the data stream representing a value of the specified type and discards it. +func (dec *Decoder) decodeIgnoredValue(wireId typeId) { + var enginePtr **decEngine + enginePtr, dec.err = dec.getIgnoreEnginePtr(wireId) + if dec.err != nil { + return + } + wire := dec.wireType[wireId] + if wire != nil && wire.StructT != nil { + dec.ignoreStruct(*enginePtr) + } else { + dec.ignoreSingle(*enginePtr) + } +} + +func init() { + var iop, uop decOp + switch reflect.TypeOf(int(0)).Bits() { + case 32: + iop = decInt32 + uop = decUint32 + case 64: + iop = decInt64 + uop = decUint64 + default: + panic("gob: unknown size of int/uint") + } + decOpTable[reflect.Int] = iop + decOpTable[reflect.Uint] = uop + + // Finally uintptr + switch reflect.TypeOf(uintptr(0)).Bits() { + case 32: + uop = decUint32 + case 64: + uop = decUint64 + default: + panic("gob: unknown size of uintptr") + } + decOpTable[reflect.Uintptr] = uop +} + +// Gob assumes it can call UnsafeAddr on any Value +// in order to get a pointer it can copy data from. +// Values that have just been created and do not point +// into existing structs or slices cannot be addressed, +// so simulate it by returning a pointer to a copy. +// Each call allocates once. +func unsafeAddr(v reflect.Value) uintptr { + if v.CanAddr() { + return v.UnsafeAddr() + } + x := reflect.New(v.Type()).Elem() + x.Set(v) + return x.UnsafeAddr() +} + +// Gob depends on being able to take the address +// of zeroed Values it creates, so use this wrapper instead +// of the standard reflect.Zero. +// Each call allocates once. +func allocValue(t reflect.Type) reflect.Value { + return reflect.New(t).Elem() +} diff --git a/src/pkg/encoding/gob/decoder.go b/src/pkg/encoding/gob/decoder.go new file mode 100644 index 000000000..c5c7d3fdb --- /dev/null +++ b/src/pkg/encoding/gob/decoder.go @@ -0,0 +1,216 @@ +// 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 gob + +import ( + "bufio" + "bytes" + "errors" + "io" + "reflect" + "sync" +) + +// A Decoder manages the receipt of type and data information read from the +// remote side of a connection. +type Decoder struct { + mutex sync.Mutex // each item must be received atomically + r io.Reader // source of the data + buf bytes.Buffer // buffer for more efficient i/o from r + wireType map[typeId]*wireType // map from remote ID to local description + decoderCache map[reflect.Type]map[typeId]**decEngine // cache of compiled engines + ignorerCache map[typeId]**decEngine // ditto for ignored objects + freeList *decoderState // list of free decoderStates; avoids reallocation + countBuf []byte // used for decoding integers while parsing messages + tmp []byte // temporary storage for i/o; saves reallocating + err error +} + +// NewDecoder returns a new decoder that reads from the io.Reader. +// If r does not also implement io.ByteReader, it will be wrapped in a +// bufio.Reader. +func NewDecoder(r io.Reader) *Decoder { + dec := new(Decoder) + // We use the ability to read bytes as a plausible surrogate for buffering. + if _, ok := r.(io.ByteReader); !ok { + r = bufio.NewReader(r) + } + dec.r = r + dec.wireType = make(map[typeId]*wireType) + dec.decoderCache = make(map[reflect.Type]map[typeId]**decEngine) + dec.ignorerCache = make(map[typeId]**decEngine) + dec.countBuf = make([]byte, 9) // counts may be uint64s (unlikely!), require 9 bytes + + return dec +} + +// recvType loads the definition of a type. +func (dec *Decoder) recvType(id typeId) { + // Have we already seen this type? That's an error + if id < firstUserId || dec.wireType[id] != nil { + dec.err = errors.New("gob: duplicate type received") + return + } + + // Type: + wire := new(wireType) + dec.decodeValue(tWireType, reflect.ValueOf(wire)) + if dec.err != nil { + return + } + // Remember we've seen this type. + dec.wireType[id] = wire +} + +var errBadCount = errors.New("invalid message length") + +// recvMessage reads the next count-delimited item from the input. It is the converse +// of Encoder.writeMessage. It returns false on EOF or other error reading the message. +func (dec *Decoder) recvMessage() bool { + // Read a count. + nbytes, _, err := decodeUintReader(dec.r, dec.countBuf) + if err != nil { + dec.err = err + return false + } + // Upper limit of 1GB, allowing room to grow a little without overflow. + // TODO: We might want more control over this limit. + if nbytes >= 1<<30 { + dec.err = errBadCount + return false + } + dec.readMessage(int(nbytes)) + return dec.err == nil +} + +// 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 + } + dec.tmp = dec.tmp[:nbytes] + + // Read the data + _, 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) +} + +// toInt turns an encoded uint64 into an int, according to the marshaling rules. +func toInt(x uint64) int64 { + i := int64(x >> 1) + if x&1 != 0 { + i = ^i + } + return i +} + +func (dec *Decoder) nextInt() int64 { + n, _, err := decodeUintReader(&dec.buf, dec.countBuf) + if err != nil { + dec.err = err + } + return toInt(n) +} + +func (dec *Decoder) nextUint() uint64 { + n, _, err := decodeUintReader(&dec.buf, dec.countBuf) + if err != nil { + dec.err = err + } + return n +} + +// decodeTypeSequence parses: +// TypeSequence +// (TypeDefinition DelimitedTypeDefinition*)? +// and returns the type id of the next value. It returns -1 at +// EOF. Upon return, the remainder of dec.buf is the value to be +// decoded. If this is an interface value, it can be ignored by +// resetting that buffer. +func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId { + for dec.err == nil { + if dec.buf.Len() == 0 { + if !dec.recvMessage() { + break + } + } + // Receive a type id. + id := typeId(dec.nextInt()) + if id >= 0 { + // Value follows. + return id + } + // Type definition for (-id) follows. + dec.recvType(-id) + // When decoding an interface, after a type there may be a + // DelimitedValue still in the buffer. Skip its count. + // (Alternatively, the buffer is empty and the byte count + // will be absorbed by recvMessage.) + if dec.buf.Len() > 0 { + if !isInterface { + dec.err = errors.New("extra data in buffer") + break + } + dec.nextUint() + } + } + return -1 +} + +// Decode reads the next value from the connection and stores +// it in the data represented by the empty interface value. +// If e is nil, the value will be discarded. Otherwise, +// the value underlying e must be a pointer to the +// correct type for the next data item received. +func (dec *Decoder) Decode(e interface{}) error { + if e == nil { + return dec.DecodeValue(reflect.Value{}) + } + value := reflect.ValueOf(e) + // If e represents a value as opposed to a pointer, the answer won't + // get back to the caller. Make sure it's a pointer. + if value.Type().Kind() != reflect.Ptr { + dec.err = errors.New("gob: attempt to decode into a non-pointer") + return dec.err + } + return dec.DecodeValue(value) +} + +// DecodeValue reads the next value from the connection. +// If v is the zero reflect.Value (v.Kind() == Invalid), DecodeValue discards the value. +// Otherwise, it stores the value into v. In that case, v must represent +// a non-nil pointer to data or be an assignable reflect.Value (v.CanSet()) +func (dec *Decoder) DecodeValue(v reflect.Value) error { + if v.IsValid() { + if v.Kind() == reflect.Ptr && !v.IsNil() { + // That's okay, we'll store through the pointer. + } else if !v.CanSet() { + return errors.New("gob: DecodeValue of unassignable value") + } + } + // Make sure we're single-threaded through here. + dec.mutex.Lock() + defer dec.mutex.Unlock() + + dec.buf.Reset() // In case data lingers from previous invocation. + dec.err = nil + id := dec.decodeTypeSequence(false) + if dec.err == nil { + dec.decodeValue(id, v) + } + return dec.err +} + +// If debug.go is compiled into the program , debugFunc prints a human-readable +// representation of the gob data read from r by calling that file's Debug function. +// Otherwise it is nil. +var debugFunc func(io.Reader) diff --git a/src/pkg/encoding/gob/doc.go b/src/pkg/encoding/gob/doc.go new file mode 100644 index 000000000..96885f8de --- /dev/null +++ b/src/pkg/encoding/gob/doc.go @@ -0,0 +1,366 @@ +// 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 gob manages streams of gobs - binary values exchanged between an +Encoder (transmitter) and a Decoder (receiver). A typical use is transporting +arguments and results of remote procedure calls (RPCs) such as those provided by +package "rpc". + +A stream of gobs is self-describing. Each data item in the stream is preceded by +a specification of its type, expressed in terms of a small set of predefined +types. Pointers are not transmitted, but the things they point to are +transmitted; that is, the values are flattened. Recursive types work fine, but +recursive values (data with cycles) are problematic. This may change. + +To use gobs, create an Encoder and present it with a series of data items as +values or addresses that can be dereferenced to values. The Encoder makes sure +all type information is sent before it is needed. At the receive side, a +Decoder retrieves values from the encoded stream and unpacks them into local +variables. + +The source and destination values/types need not correspond exactly. For structs, +fields (identified by name) that are in the source but absent from the receiving +variable will be ignored. Fields that are in the receiving variable but missing +from the transmitted type or value will be ignored in the destination. If a field +with the same name is present in both, their types must be compatible. Both the +receiver and transmitter will do all necessary indirection and dereferencing to +convert between gobs and actual Go values. For instance, a gob type that is +schematically, + + struct { A, B int } + +can be sent from or received into any of these Go types: + + struct { A, B int } // the same + *struct { A, B int } // extra indirection of the struct + struct { *A, **B int } // extra indirection of the fields + struct { A, B int64 } // different concrete value type; see below + +It may also be received into any of these: + + struct { A, B int } // the same + struct { B, A int } // ordering doesn't matter; matching is by name + struct { A, B, C int } // extra field (C) ignored + struct { B int } // missing field (A) ignored; data will be dropped + struct { B, C int } // missing field (A) ignored; extra field (C) ignored. + +Attempting to receive into these types will draw a decode error: + + struct { A int; B uint } // change of signedness for B + struct { A int; B float } // change of type for B + struct { } // no field names in common + struct { C, D int } // no field names in common + +Integers are transmitted two ways: arbitrary precision signed integers or +arbitrary precision unsigned integers. There is no int8, int16 etc. +discrimination in the gob format; there are only signed and unsigned integers. As +described below, the transmitter sends the value in a variable-length encoding; +the receiver accepts the value and stores it in the destination variable. +Floating-point numbers are always sent using IEEE-754 64-bit precision (see +below). + +Signed integers may be received into any signed integer variable: int, int16, etc.; +unsigned integers may be received into any unsigned integer variable; and floating +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. + +Functions and channels cannot be sent in a gob. Attempting +to encode a value that contains one will fail. + +The rest of this comment documents the encoding, details that are not important +for most users. Details are presented bottom-up. + +An unsigned integer is sent one of two ways. If it is less than 128, it is sent +as a byte with that value. Otherwise it is sent as a minimal-length big-endian +(high byte first) byte stream holding the value, preceded by one byte holding the +byte count, negated. Thus 0 is transmitted as (00), 7 is transmitted as (07) and +256 is transmitted as (FE 01 00). + +A boolean is encoded within an unsigned integer: 0 for false, 1 for true. + +A signed integer, i, is encoded within an unsigned integer, u. Within u, bits 1 +upward contain the value; bit 0 says whether they should be complemented upon +receipt. The encode algorithm looks like this: + + uint u; + if i < 0 { + u = (^i << 1) | 1 // complement i, bit 0 is 1 + } else { + u = (i << 1) // do not complement i, bit 0 is 0 + } + encodeUnsigned(u) + +The low bit is therefore analogous to a sign bit, but making it the complement bit +instead guarantees that the largest negative integer is not a special case. For +example, -129=^128=(^256>>1) encodes as (FE 01 01). + +Floating-point numbers are always sent as a representation of a float64 value. +That value is converted to a uint64 using math.Float64bits. The uint64 is then +byte-reversed and sent as a regular unsigned integer. The byte-reversal means the +exponent and high-precision part of the mantissa go first. Since the low bits are +often zero, this can save encoding bytes. For instance, 17.0 is encoded in only +three bytes (FE 31 40). + +Strings and slices of bytes are sent as an unsigned count followed by that many +uninterpreted bytes of the value. + +All other slices and arrays are sent as an unsigned count followed by that many +elements using the standard gob encoding for their type, recursively. + +Maps are sent as an unsigned count followed by that man 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 +transmitted. + +Structs are sent as a sequence of (field number, field value) pairs. The field +value is sent using the standard gob encoding for its type, recursively. If a +field has the zero value for its type, it is omitted from the transmission. The +field number is defined by the type of the encoded struct: the first field of the +encoded type is field 0, the second is field 1, etc. When encoding a value, the +field numbers are delta encoded for efficiency and the fields are always sent in +order of increasing field number; the deltas are therefore unsigned. The +initialization for the delta encoding sets the field number to -1, so an unsigned +integer field 0 with value 7 is transmitted as unsigned delta = 1, unsigned value += 7 or (01 07). Finally, after all the fields have been sent a terminating mark +denotes the end of the struct. That mark is a delta=0 value, which has +representation (00). + +Interface types are not checked for compatibility; all interface types are +treated, for transmission, as members of a single "interface" type, analogous to +int or []byte - in effect they're all treated as interface{}. Interface values +are transmitted as a string identifying the concrete type being sent (a name +that must be pre-defined by calling Register), followed by a byte count of the +length of the following data (so the value can be skipped if it cannot be +stored), followed by the usual encoding of concrete (dynamic) value stored in +the interface value. (A nil interface value is identified by the empty string +and transmits no value.) Upon receipt, the decoder verifies that the unpacked +concrete item satisfies the interface of the receiving variable. + +The representation of types is described below. When a type is defined on a given +connection between an Encoder and Decoder, it is assigned a signed integer type +id. When Encoder.Encode(v) is called, it makes sure there is an id assigned for +the type of v and all its elements and then it sends the pair (typeid, encoded-v) +where typeid is the type id of the encoded type of v and encoded-v is the gob +encoding of the value v. + +To define a type, the encoder chooses an unused, positive type id and sends the +pair (-type id, encoded-type) where encoded-type is the gob encoding of a wireType +description, constructed from these types: + + type wireType struct { + ArrayT *ArrayType + SliceT *SliceType + StructT *StructType + MapT *MapType + } + type arrayType struct { + CommonType + Elem typeId + Len int + } + type CommonType struct { + Name string // the name of the struct type + Id int // the id of the type, repeated so it's inside the type + } + type sliceType struct { + CommonType + Elem typeId + } + type structType struct { + CommonType + Field []*fieldType // the fields of the struct. + } + type fieldType struct { + Name string // the name of the field. + Id int // the type id of the field, which must be already defined + } + type mapType struct { + CommonType + Key typeId + Elem typeId + } + +If there are nested type ids, the types for all inner type ids must be defined +before the top-level type id is used to describe an encoded-v. + +For simplicity in setup, the connection is defined to understand these types a +priori, as well as the basic gob types int, uint, etc. Their ids are: + + bool 1 + int 2 + uint 3 + float 4 + []byte 5 + string 6 + complex 7 + interface 8 + // gap for reserved ids. + WireType 16 + ArrayType 17 + CommonType 18 + SliceType 19 + StructType 20 + FieldType 21 + // 22 is slice of fieldType. + MapType 23 + +Finally, each message created by a call to Encode is preceded by an encoded +unsigned integer count of the number of bytes remaining in the message. After +the initial type name, interface values are wrapped the same way; in effect, the +interface value acts like a recursive invocation of Encode. + +In summary, a gob stream looks like + + (byteCount (-type id, encoding of a wireType)* (type id, encoding of a value))* + +where * signifies zero or more repetitions and the type id of a value must +be predefined or be defined before the value in the stream. + +See "Gobs of data" for a design discussion of the gob wire format: +http://golang.org/doc/articles/gobs_of_data.html +*/ +package gob + +/* +Grammar: + +Tokens starting with a lower case letter are terminals; int(n) +and uint(n) represent the signed/unsigned encodings of the value n. + +GobStream: + DelimitedMessage* +DelimitedMessage: + uint(lengthOfMessage) Message +Message: + TypeSequence TypedValue +TypeSequence + (TypeDefinition DelimitedTypeDefinition*)? +DelimitedTypeDefinition: + uint(lengthOfTypeDefinition) TypeDefinition +TypedValue: + int(typeId) Value +TypeDefinition: + int(-typeId) encodingOfWireType +Value: + SingletonValue | StructValue +SingletonValue: + uint(0) FieldValue +FieldValue: + builtinValue | ArrayValue | MapValue | SliceValue | StructValue | InterfaceValue +InterfaceValue: + NilInterfaceValue | NonNilInterfaceValue +NilInterfaceValue: + uint(0) +NonNilInterfaceValue: + ConcreteTypeName TypeSequence InterfaceContents +ConcreteTypeName: + uint(lengthOfName) [already read=n] name +InterfaceContents: + int(concreteTypeId) DelimitedValue +DelimitedValue: + uint(length) Value +ArrayValue: + uint(n) FieldValue*n [n elements] +MapValue: + uint(n) (FieldValue FieldValue)*n [n (key, value) pairs] +SliceValue: + uint(n) FieldValue*n [n elements] +StructValue: + (uint(fieldDelta) FieldValue)* +*/ + +/* +For implementers and the curious, here is an encoded example. Given + type Point struct {X, Y int} +and the value + p := Point{22, 33} +the bytes transmitted that encode p will be: + 1f ff 81 03 01 01 05 50 6f 69 6e 74 01 ff 82 00 + 01 02 01 01 58 01 04 00 01 01 59 01 04 00 00 00 + 07 ff 82 01 2c 01 42 00 +They are determined as follows. + +Since this is the first transmission of type Point, the type descriptor +for Point itself must be sent before the value. This is the first type +we've sent on this Encoder, so it has type id 65 (0 through 64 are +reserved). + + 1f // This item (a type descriptor) is 31 bytes long. + ff 81 // The negative of the id for the type we're defining, -65. + // This is one byte (indicated by FF = -1) followed by + // ^-65<<1 | 1. The low 1 bit signals to complement the + // rest upon receipt. + + // Now we send a type descriptor, which is itself a struct (wireType). + // The type of wireType itself is known (it's built in, as is the type of + // all its components), so we just need to send a *value* of type wireType + // that represents type "Point". + // Here starts the encoding of that value. + // Set the field number implicitly to -1; this is done at the beginning + // of every struct, including nested structs. + 03 // Add 3 to field number; now 2 (wireType.structType; this is a struct). + // structType starts with an embedded CommonType, which appears + // as a regular structure here too. + 01 // add 1 to field number (now 0); start of embedded CommonType. + 01 // add 1 to field number (now 0, the name of the type) + 05 // string is (unsigned) 5 bytes long + 50 6f 69 6e 74 // wireType.structType.CommonType.name = "Point" + 01 // add 1 to field number (now 1, the id of the type) + ff 82 // wireType.structType.CommonType._id = 65 + 00 // end of embedded wiretype.structType.CommonType struct + 01 // add 1 to field number (now 1, the field array in wireType.structType) + 02 // There are two fields in the type (len(structType.field)) + 01 // Start of first field structure; add 1 to get field number 0: field[0].name + 01 // 1 byte + 58 // structType.field[0].name = "X" + 01 // Add 1 to get field number 1: field[0].id + 04 // structType.field[0].typeId is 2 (signed int). + 00 // End of structType.field[0]; start structType.field[1]; set field number to -1. + 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 + 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 + 00 // end of wireType structure + +Now we can send the Point value. Again the field number resets to -1: + + 07 // this value is 7 bytes long + ff 82 // the type number, 65 (1 byte (-FF) followed by 65<<1) + 01 // add one to field number, yielding field 0 + 2c // encoding of signed "22" (0x22 = 44 = 22<<1); Point.x = 22 + 01 // add one to field number, yielding field 1 + 42 // encoding of signed "33" (0x42 = 66 = 33<<1); Point.y = 33 + 00 // end of structure + +The type encoding is long and fairly intricate but we send it only once. +If p is transmitted a second time, the type is already known so the +output will be just: + + 07 ff 82 01 2c 01 42 00 + +A single non-struct value at top level is transmitted like a field with +delta tag 0. For instance, a signed integer with value 3 presented as +the argument to Encode will emit: + + 03 04 00 06 + +Which represents: + + 03 // this value is 3 bytes long + 04 // the type number, 2, represents an integer + 00 // tag delta 0 + 06 // value 3 + +*/ diff --git a/src/pkg/encoding/gob/dump.go b/src/pkg/encoding/gob/dump.go new file mode 100644 index 000000000..17238c98d --- /dev/null +++ b/src/pkg/encoding/gob/dump.go @@ -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. + +// +build ignore + +package main + +// Need to compile package gob with debug.go to build this program. +// See comments in debug.go for how to do this. + +import ( + "encoding/gob" + "fmt" + "os" +) + +func main() { + var err error + file := os.Stdin + if len(os.Args) > 1 { + file, err = os.Open(os.Args[1]) + if err != nil { + fmt.Fprintf(os.Stderr, "dump: %s\n", err) + os.Exit(1) + } + } + gob.Debug(file) +} diff --git a/src/pkg/encoding/gob/encode.go b/src/pkg/encoding/gob/encode.go new file mode 100644 index 000000000..168e08b13 --- /dev/null +++ b/src/pkg/encoding/gob/encode.go @@ -0,0 +1,731 @@ +// 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 gob + +import ( + "bytes" + "math" + "reflect" + "unsafe" +) + +const uint64Size = int(unsafe.Sizeof(uint64(0))) + +// encoderState is the global execution state of an instance of the encoder. +// Field numbers are delta encoded and always increase. The field +// number is initialized to -1 so 0 comes out as delta(1). A delta of +// 0 terminates the structure. +type encoderState struct { + enc *Encoder + b *bytes.Buffer + sendZero bool // encoding an array element or map key/value pair; send zero values + fieldnum int // the last field number written. + buf [1 + uint64Size]byte // buffer used by the encoder; here to avoid allocation. + next *encoderState // for free list +} + +func (enc *Encoder) newEncoderState(b *bytes.Buffer) *encoderState { + e := enc.freeList + if e == nil { + e = new(encoderState) + e.enc = enc + } else { + enc.freeList = e.next + } + e.sendZero = false + e.fieldnum = 0 + e.b = b + return e +} + +func (enc *Encoder) freeEncoderState(e *encoderState) { + e.next = enc.freeList + enc.freeList = e +} + +// Unsigned integers have a two-state encoding. If the number is less +// than 128 (0 through 0x7F), its value is written directly. +// Otherwise the value is written in big-endian byte order preceded +// by the byte length, negated. + +// encodeUint writes an encoded unsigned integer to state.b. +func (state *encoderState) encodeUint(x uint64) { + if x <= 0x7F { + err := state.b.WriteByte(uint8(x)) + if err != nil { + error_(err) + } + return + } + i := uint64Size + for x > 0 { + state.buf[i] = uint8(x) + x >>= 8 + i-- + } + state.buf[i] = uint8(i - uint64Size) // = loop count, negated + _, err := state.b.Write(state.buf[i : uint64Size+1]) + if err != nil { + error_(err) + } +} + +// encodeInt writes an encoded signed integer to state.w. +// The low bit of the encoding says whether to bit complement the (other bits of the) +// uint to recover the int. +func (state *encoderState) encodeInt(i int64) { + var x uint64 + if i < 0 { + x = uint64(^i<<1) | 1 + } else { + x = uint64(i << 1) + } + state.encodeUint(uint64(x)) +} + +// encOp is the signature of an encoding operator for a given type. +type encOp func(i *encInstr, state *encoderState, p unsafe.Pointer) + +// The 'instructions' of the encoding machine +type encInstr struct { + op encOp + field int // field number + indir int // how many pointer indirections to reach the value in the struct + offset uintptr // offset in the structure of the field to encode +} + +// update emits a field number and updates the state to record its value for delta encoding. +// If the instruction pointer is nil, it does nothing +func (state *encoderState) update(instr *encInstr) { + if instr != nil { + state.encodeUint(uint64(instr.field - state.fieldnum)) + state.fieldnum = instr.field + } +} + +// Each encoder for a composite is responsible for handling any +// indirections associated with the elements of the data structure. +// If any pointer so reached is nil, no bytes are written. If the +// data item is zero, no bytes are written. Single values - ints, +// strings etc. - are indirected before calling their encoders. +// Otherwise, the output (for a scalar) is the field number, as an +// encoded integer, followed by the field data in its appropriate +// format. + +// encIndirect dereferences p indir times and returns the result. +func encIndirect(p unsafe.Pointer, indir int) unsafe.Pointer { + for ; indir > 0; indir-- { + p = *(*unsafe.Pointer)(p) + if p == nil { + return unsafe.Pointer(nil) + } + } + return p +} + +// encBool encodes the bool with address p as an unsigned 0 or 1. +func encBool(i *encInstr, state *encoderState, p unsafe.Pointer) { + b := *(*bool)(p) + if b || state.sendZero { + state.update(i) + if b { + state.encodeUint(1) + } else { + state.encodeUint(0) + } + } +} + +// encInt encodes the int with address p. +func encInt(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := int64(*(*int)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeInt(v) + } +} + +// encUint encodes the uint with address p. +func encUint(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := uint64(*(*uint)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeUint(v) + } +} + +// encInt8 encodes the int8 with address p. +func encInt8(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := int64(*(*int8)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeInt(v) + } +} + +// encUint8 encodes the uint8 with address p. +func encUint8(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := uint64(*(*uint8)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeUint(v) + } +} + +// encInt16 encodes the int16 with address p. +func encInt16(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := int64(*(*int16)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeInt(v) + } +} + +// encUint16 encodes the uint16 with address p. +func encUint16(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := uint64(*(*uint16)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeUint(v) + } +} + +// encInt32 encodes the int32 with address p. +func encInt32(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := int64(*(*int32)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeInt(v) + } +} + +// encUint encodes the uint32 with address p. +func encUint32(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := uint64(*(*uint32)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeUint(v) + } +} + +// encInt64 encodes the int64 with address p. +func encInt64(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := *(*int64)(p) + if v != 0 || state.sendZero { + state.update(i) + state.encodeInt(v) + } +} + +// encInt64 encodes the uint64 with address p. +func encUint64(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := *(*uint64)(p) + if v != 0 || state.sendZero { + state.update(i) + state.encodeUint(v) + } +} + +// encUintptr encodes the uintptr with address p. +func encUintptr(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := uint64(*(*uintptr)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeUint(v) + } +} + +// floatBits returns a uint64 holding the bits of a floating-point number. +// Floating-point numbers are transmitted as uint64s holding the bits +// of the underlying representation. They are sent byte-reversed, with +// the exponent end coming out first, so integer floating point numbers +// (for example) transmit more compactly. This routine does the +// swizzling. +func floatBits(f float64) uint64 { + u := math.Float64bits(f) + var v uint64 + for i := 0; i < 8; i++ { + v <<= 8 + v |= u & 0xFF + u >>= 8 + } + return v +} + +// encFloat32 encodes the float32 with address p. +func encFloat32(i *encInstr, state *encoderState, p unsafe.Pointer) { + f := *(*float32)(p) + if f != 0 || state.sendZero { + v := floatBits(float64(f)) + state.update(i) + state.encodeUint(v) + } +} + +// encFloat64 encodes the float64 with address p. +func encFloat64(i *encInstr, state *encoderState, p unsafe.Pointer) { + f := *(*float64)(p) + if f != 0 || state.sendZero { + state.update(i) + v := floatBits(f) + state.encodeUint(v) + } +} + +// encComplex64 encodes the complex64 with address p. +// Complex numbers are just a pair of floating-point numbers, real part first. +func encComplex64(i *encInstr, state *encoderState, p unsafe.Pointer) { + c := *(*complex64)(p) + if c != 0+0i || state.sendZero { + rpart := floatBits(float64(real(c))) + ipart := floatBits(float64(imag(c))) + state.update(i) + state.encodeUint(rpart) + state.encodeUint(ipart) + } +} + +// encComplex128 encodes the complex128 with address p. +func encComplex128(i *encInstr, state *encoderState, p unsafe.Pointer) { + c := *(*complex128)(p) + if c != 0+0i || state.sendZero { + rpart := floatBits(real(c)) + ipart := floatBits(imag(c)) + state.update(i) + state.encodeUint(rpart) + state.encodeUint(ipart) + } +} + +// encUint8Array encodes the byte slice whose header has address p. +// Byte arrays are encoded as an unsigned count followed by the raw bytes. +func encUint8Array(i *encInstr, state *encoderState, p unsafe.Pointer) { + b := *(*[]byte)(p) + if len(b) > 0 || state.sendZero { + state.update(i) + state.encodeUint(uint64(len(b))) + state.b.Write(b) + } +} + +// encString encodes the string whose header has address p. +// Strings are encoded as an unsigned count followed by the raw bytes. +func encString(i *encInstr, state *encoderState, p unsafe.Pointer) { + s := *(*string)(p) + if len(s) > 0 || state.sendZero { + state.update(i) + state.encodeUint(uint64(len(s))) + state.b.WriteString(s) + } +} + +// encStructTerminator encodes the end of an encoded struct +// as delta field number of 0. +func encStructTerminator(i *encInstr, state *encoderState, p unsafe.Pointer) { + state.encodeUint(0) +} + +// Execution engine + +// encEngine an array of instructions indexed by field number of the encoding +// data, typically a struct. It is executed top to bottom, walking the struct. +type encEngine struct { + instr []encInstr +} + +const singletonField = 0 + +// encodeSingle encodes a single top-level non-struct value. +func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintptr) { + state := enc.newEncoderState(b) + state.fieldnum = singletonField + // There is no surrounding struct to frame the transmission, so we must + // generate data even if the item is zero. To do this, set sendZero. + state.sendZero = true + instr := &engine.instr[singletonField] + p := unsafe.Pointer(basep) // offset will be zero + if instr.indir > 0 { + if p = encIndirect(p, instr.indir); p == nil { + return + } + } + instr.op(instr, state, p) + enc.freeEncoderState(state) +} + +// encodeStruct encodes a single struct value. +func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintptr) { + state := enc.newEncoderState(b) + state.fieldnum = -1 + for i := 0; i < len(engine.instr); i++ { + instr := &engine.instr[i] + p := unsafe.Pointer(basep + instr.offset) + if instr.indir > 0 { + if p = encIndirect(p, instr.indir); p == nil { + continue + } + } + instr.op(instr, state, p) + } + enc.freeEncoderState(state) +} + +// encodeArray encodes the array whose 0th element is at p. +func (enc *Encoder) encodeArray(b *bytes.Buffer, p uintptr, op encOp, elemWid uintptr, elemIndir int, length int) { + state := enc.newEncoderState(b) + state.fieldnum = -1 + state.sendZero = true + state.encodeUint(uint64(length)) + for i := 0; i < length; i++ { + elemp := p + up := unsafe.Pointer(elemp) + if elemIndir > 0 { + if up = encIndirect(up, elemIndir); up == nil { + errorf("encodeArray: nil element") + } + elemp = uintptr(up) + } + op(nil, state, unsafe.Pointer(elemp)) + p += uintptr(elemWid) + } + enc.freeEncoderState(state) +} + +// encodeReflectValue is a helper for maps. It encodes the value v. +func encodeReflectValue(state *encoderState, v reflect.Value, op encOp, indir int) { + for i := 0; i < indir && v.IsValid(); i++ { + v = reflect.Indirect(v) + } + if !v.IsValid() { + errorf("encodeReflectValue: nil element") + } + op(nil, state, unsafe.Pointer(unsafeAddr(v))) +} + +// encodeMap encodes a map as unsigned count followed by key:value pairs. +// Because map internals are not exposed, we must use reflection rather than +// addresses. +func (enc *Encoder) encodeMap(b *bytes.Buffer, mv reflect.Value, keyOp, elemOp encOp, keyIndir, elemIndir int) { + state := enc.newEncoderState(b) + state.fieldnum = -1 + state.sendZero = true + keys := mv.MapKeys() + state.encodeUint(uint64(len(keys))) + for _, key := range keys { + encodeReflectValue(state, key, keyOp, keyIndir) + encodeReflectValue(state, mv.MapIndex(key), elemOp, elemIndir) + } + enc.freeEncoderState(state) +} + +// encodeInterface encodes the interface value iv. +// To send an interface, we send a string identifying the concrete type, followed +// by the type identifier (which might require defining that type right now), followed +// 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) { + state := enc.newEncoderState(b) + state.fieldnum = -1 + state.sendZero = true + if iv.IsNil() { + state.encodeUint(0) + return + } + + ut := userType(iv.Elem().Type()) + name, ok := concreteTypeToName[ut.base] + if !ok { + errorf("type not registered for interface: %s", ut.base) + } + // Send the name. + state.encodeUint(uint64(len(name))) + _, err := state.b.WriteString(name) + if err != nil { + error_(err) + } + // Define the type id if necessary. + enc.sendTypeDescriptor(enc.writer(), state, ut) + // Send the type id. + enc.sendTypeId(state, ut) + // Encode the value into a new buffer. Any nested type definitions + // should be written to b, before the encoded value. + enc.pushWriter(b) + data := new(bytes.Buffer) + data.Write(spaceForLength) + enc.encode(data, iv.Elem(), ut) + if enc.err != nil { + error_(enc.err) + } + enc.popWriter() + enc.writeMessage(b, data) + if enc.err != nil { + error_(err) + } + enc.freeEncoderState(state) +} + +// isZero returns whether the value is the zero of its type. +func isZero(val reflect.Value) bool { + switch val.Kind() { + case reflect.Array: + for i := 0; i < val.Len(); i++ { + if !isZero(val.Index(i)) { + return false + } + } + return true + case reflect.Map, reflect.Slice, reflect.String: + return val.Len() == 0 + case reflect.Bool: + return !val.Bool() + case reflect.Complex64, reflect.Complex128: + return val.Complex() == 0 + case reflect.Chan, reflect.Func, reflect.Ptr: + return val.IsNil() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return val.Int() == 0 + case reflect.Float32, reflect.Float64: + return val.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return val.Uint() == 0 + case reflect.Struct: + for i := 0; i < val.NumField(); i++ { + if !isZero(val.Field(i)) { + return false + } + } + return true + } + panic("unknown type in isZero " + val.Type().String()) +} + +// encGobEncoder encodes a value that implements the GobEncoder interface. +// The data is sent as a byte array. +func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value) { + // TODO: should we catch panics from the called method? + // We know it's a GobEncoder, so just call the method directly. + data, err := v.Interface().(GobEncoder).GobEncode() + if err != nil { + error_(err) + } + state := enc.newEncoderState(b) + state.fieldnum = -1 + state.encodeUint(uint64(len(data))) + state.b.Write(data) + enc.freeEncoderState(state) +} + +var encOpTable = [...]encOp{ + reflect.Bool: encBool, + reflect.Int: encInt, + reflect.Int8: encInt8, + reflect.Int16: encInt16, + reflect.Int32: encInt32, + reflect.Int64: encInt64, + reflect.Uint: encUint, + reflect.Uint8: encUint8, + reflect.Uint16: encUint16, + reflect.Uint32: encUint32, + reflect.Uint64: encUint64, + reflect.Uintptr: encUintptr, + reflect.Float32: encFloat32, + reflect.Float64: encFloat64, + reflect.Complex64: encComplex64, + reflect.Complex128: encComplex128, + reflect.String: encString, +} + +// encOpFor returns (a pointer to) the encoding op for the base type under rt and +// the indirection count to reach it. +func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int) { + ut := userType(rt) + // If the type implements GobEncoder, we handle it without further processing. + if ut.isGobEncoder { + return enc.gobEncodeOpFor(ut) + } + // If this type is already in progress, it's a recursive type (e.g. map[string]*T). + // Return the pointer to the op we're already building. + if opPtr := inProgress[rt]; opPtr != nil { + return opPtr, ut.indir + } + typ := ut.base + indir := ut.indir + k := typ.Kind() + var op encOp + if int(k) < len(encOpTable) { + op = encOpTable[k] + } + if op == nil { + inProgress[rt] = &op + // Special cases + switch t := typ; t.Kind() { + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { + op = encUint8Array + break + } + // Slices have a header; we decode it to find the underlying array. + elemOp, indir := enc.encOpFor(t.Elem(), inProgress) + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + slice := (*reflect.SliceHeader)(p) + if !state.sendZero && slice.Len == 0 { + return + } + state.update(i) + state.enc.encodeArray(state.b, slice.Data, *elemOp, t.Elem().Size(), indir, int(slice.Len)) + } + case reflect.Array: + // True arrays have size in the type. + elemOp, indir := enc.encOpFor(t.Elem(), inProgress) + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + state.update(i) + state.enc.encodeArray(state.b, uintptr(p), *elemOp, t.Elem().Size(), indir, t.Len()) + } + case reflect.Map: + keyOp, keyIndir := enc.encOpFor(t.Key(), inProgress) + elemOp, elemIndir := enc.encOpFor(t.Elem(), inProgress) + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + // Maps cannot be accessed by moving addresses around the way + // that slices etc. can. We must recover a full reflection value for + // the iteration. + v := reflect.NewAt(t, unsafe.Pointer(p)).Elem() + mv := reflect.Indirect(v) + // We send zero-length (but non-nil) maps because the + // receiver might want to use the map. (Maps don't use append.) + if !state.sendZero && mv.IsNil() { + return + } + state.update(i) + state.enc.encodeMap(state.b, mv, *keyOp, *elemOp, keyIndir, elemIndir) + } + case reflect.Struct: + // Generate a closure that calls out to the engine for the nested type. + enc.getEncEngine(userType(typ)) + info := mustGetTypeInfo(typ) + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + state.update(i) + // indirect through info to delay evaluation for recursive structs + state.enc.encodeStruct(state.b, info.encoder, uintptr(p)) + } + case reflect.Interface: + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + // Interfaces transmit the name and contents of the concrete + // value they contain. + v := reflect.NewAt(t, unsafe.Pointer(p)).Elem() + iv := reflect.Indirect(v) + if !state.sendZero && (!iv.IsValid() || iv.IsNil()) { + return + } + state.update(i) + state.enc.encodeInterface(state.b, iv) + } + } + } + if op == nil { + errorf("can't happen: encode type %s", rt) + } + return &op, indir +} + +// gobEncodeOpFor returns the op for a type that is known to implement +// GobEncoder. +func (enc *Encoder) gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) { + rt := ut.user + if ut.encIndir == -1 { + rt = reflect.PtrTo(rt) + } else if ut.encIndir > 0 { + for i := int8(0); i < ut.encIndir; i++ { + rt = rt.Elem() + } + } + var op encOp + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + var v reflect.Value + if ut.encIndir == -1 { + // Need to climb up one level to turn value into pointer. + v = reflect.NewAt(rt, unsafe.Pointer(&p)).Elem() + } else { + v = reflect.NewAt(rt, p).Elem() + } + if !state.sendZero && isZero(v) { + return + } + state.update(i) + state.enc.encodeGobEncoder(state.b, v) + } + return &op, int(ut.encIndir) // encIndir: op will get called with p == address of receiver. +} + +// compileEnc returns the engine to compile the type. +func (enc *Encoder) compileEnc(ut *userTypeInfo) *encEngine { + srt := ut.base + engine := new(encEngine) + seen := make(map[reflect.Type]*encOp) + rt := ut.base + if ut.isGobEncoder { + rt = ut.user + } + if !ut.isGobEncoder && + srt.Kind() == reflect.Struct { + for fieldNum, wireFieldNum := 0, 0; fieldNum < srt.NumField(); fieldNum++ { + f := srt.Field(fieldNum) + if !isExported(f.Name) { + continue + } + op, indir := enc.encOpFor(f.Type, seen) + engine.instr = append(engine.instr, encInstr{*op, wireFieldNum, indir, uintptr(f.Offset)}) + wireFieldNum++ + } + if srt.NumField() > 0 && len(engine.instr) == 0 { + errorf("type %s has no exported fields", rt) + } + engine.instr = append(engine.instr, encInstr{encStructTerminator, 0, 0, 0}) + } else { + engine.instr = make([]encInstr, 1) + op, indir := enc.encOpFor(rt, seen) + engine.instr[0] = encInstr{*op, singletonField, indir, 0} // offset is zero + } + return engine +} + +// getEncEngine returns the engine to compile the type. +// typeLock must be held (or we're in initialization and guaranteed single-threaded). +func (enc *Encoder) getEncEngine(ut *userTypeInfo) *encEngine { + info, err1 := getTypeInfo(ut) + if err1 != nil { + error_(err1) + } + if info.encoder == nil { + // mark this engine as underway before compiling to handle recursive types. + info.encoder = new(encEngine) + info.encoder = enc.compileEnc(ut) + } + return info.encoder +} + +// lockAndGetEncEngine is a function that locks and compiles. +// This lets us hold the lock only while compiling, not when encoding. +func (enc *Encoder) lockAndGetEncEngine(ut *userTypeInfo) *encEngine { + typeLock.Lock() + defer typeLock.Unlock() + return enc.getEncEngine(ut) +} + +func (enc *Encoder) encode(b *bytes.Buffer, value reflect.Value, ut *userTypeInfo) { + defer catchError(&enc.err) + engine := enc.lockAndGetEncEngine(ut) + indir := ut.indir + if ut.isGobEncoder { + indir = int(ut.encIndir) + } + for i := 0; i < indir; i++ { + value = reflect.Indirect(value) + } + if !ut.isGobEncoder && value.Type().Kind() == reflect.Struct { + enc.encodeStruct(b, engine, unsafeAddr(value)) + } else { + enc.encodeSingle(b, engine, unsafeAddr(value)) + } +} diff --git a/src/pkg/encoding/gob/encoder.go b/src/pkg/encoding/gob/encoder.go new file mode 100644 index 000000000..a15b5a1f9 --- /dev/null +++ b/src/pkg/encoding/gob/encoder.go @@ -0,0 +1,253 @@ +// 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 gob + +import ( + "bytes" + "errors" + "io" + "reflect" + "sync" +) + +// An Encoder manages the transmission of type and data information to the +// other side of a connection. +type Encoder struct { + mutex sync.Mutex // each item must be sent atomically + w []io.Writer // where to send the data + sent map[reflect.Type]typeId // which types we've already sent + countState *encoderState // stage for writing counts + freeList *encoderState // list of free encoderStates; avoids reallocation + byteBuf bytes.Buffer // buffer for top-level encoderState + err error +} + +// Before we encode a message, we reserve space at the head of the +// buffer in which to encode its length. This means we can use the +// buffer to assemble the message without another allocation. +const maxLength = 9 // Maximum size of an encoded length. +var spaceForLength = make([]byte, maxLength) + +// NewEncoder returns a new encoder that will transmit on the io.Writer. +func NewEncoder(w io.Writer) *Encoder { + enc := new(Encoder) + enc.w = []io.Writer{w} + enc.sent = make(map[reflect.Type]typeId) + enc.countState = enc.newEncoderState(new(bytes.Buffer)) + return enc +} + +// writer() returns the innermost writer the encoder is using +func (enc *Encoder) writer() io.Writer { + return enc.w[len(enc.w)-1] +} + +// pushWriter adds a writer to the encoder. +func (enc *Encoder) pushWriter(w io.Writer) { + enc.w = append(enc.w, w) +} + +// popWriter pops the innermost writer. +func (enc *Encoder) popWriter() { + enc.w = enc.w[0 : len(enc.w)-1] +} + +func (enc *Encoder) badType(rt reflect.Type) { + enc.setError(errors.New("gob: can't encode type " + rt.String())) +} + +func (enc *Encoder) setError(err error) { + if enc.err == nil { // remember the first. + enc.err = err + } +} + +// writeMessage sends the data item preceded by a unsigned count of its length. +func (enc *Encoder) writeMessage(w io.Writer, b *bytes.Buffer) { + // Space has been reserved for the length at the head of the message. + // This is a little dirty: we grab the slice from the bytes.Buffer and massage + // it by hand. + message := b.Bytes() + messageLen := len(message) - maxLength + // Encode the length. + enc.countState.b.Reset() + enc.countState.encodeUint(uint64(messageLen)) + // Copy the length to be a prefix of the message. + offset := maxLength - enc.countState.b.Len() + copy(message[offset:], enc.countState.b.Bytes()) + // Write the data. + _, err := w.Write(message[offset:]) + // Drain the buffer and restore the space at the front for the count of the next message. + b.Reset() + b.Write(spaceForLength) + if err != nil { + enc.setError(err) + } +} + +// sendActualType sends the requested type, without further investigation, unless +// it's been sent before. +func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTypeInfo, actual reflect.Type) (sent bool) { + if _, alreadySent := enc.sent[actual]; alreadySent { + return false + } + typeLock.Lock() + info, err := getTypeInfo(ut) + typeLock.Unlock() + if err != nil { + enc.setError(err) + return + } + // Send the pair (-id, type) + // Id: + state.encodeInt(-int64(info.id)) + // Type: + enc.encode(state.b, reflect.ValueOf(info.wire), wireTypeUserInfo) + enc.writeMessage(w, state.b) + if enc.err != nil { + return + } + + // Remember we've sent this type, both what the user gave us and the base type. + enc.sent[ut.base] = info.id + if ut.user != ut.base { + enc.sent[ut.user] = info.id + } + // Now send the inner types + switch st := actual; st.Kind() { + case reflect.Struct: + for i := 0; i < st.NumField(); i++ { + if isExported(st.Field(i).Name) { + enc.sendType(w, state, st.Field(i).Type) + } + } + case reflect.Array, reflect.Slice: + enc.sendType(w, state, st.Elem()) + case reflect.Map: + enc.sendType(w, state, st.Key()) + enc.sendType(w, state, st.Elem()) + } + return true +} + +// 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) + } + + // It's a concrete value, so drill down to the base type. + switch rt := ut.base; rt.Kind() { + default: + // Basic types and interfaces do not need to be described. + return + case reflect.Slice: + // If it's []uint8, don't send; it's considered basic. + if rt.Elem().Kind() == reflect.Uint8 { + return + } + // Otherwise we do send. + break + case reflect.Array: + // arrays must be sent so we know their lengths and element types. + break + case reflect.Map: + // maps must be sent so we know their lengths and key/value types. + break + case reflect.Struct: + // structs must be sent so we know their fields. + break + case reflect.Chan, reflect.Func: + // Probably a bad field in a struct. + enc.badType(rt) + return + } + + return enc.sendActualType(w, state, ut, ut.base) +} + +// Encode transmits the data item represented by the empty interface value, +// guaranteeing that all necessary type information has been transmitted first. +func (enc *Encoder) Encode(e interface{}) error { + return enc.EncodeValue(reflect.ValueOf(e)) +} + +// sendTypeDescriptor makes sure the remote side knows about this type. +// It will send a descriptor if this is the first time the type has been +// sent. +func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *userTypeInfo) { + // Make sure the type is known to the other side. + // First, have we already sent this type? + rt := ut.base + if ut.isGobEncoder { + rt = ut.user + } + if _, alreadySent := enc.sent[rt]; !alreadySent { + // No, so send it. + sent := enc.sendType(w, state, rt) + if enc.err != nil { + return + } + // If the type info has still not been transmitted, it means we have + // a singleton basic type (int, []byte etc.) at top level. We don't + // need to send the type info but we do need to update enc.sent. + if !sent { + typeLock.Lock() + info, err := getTypeInfo(ut) + typeLock.Unlock() + if err != nil { + enc.setError(err) + return + } + enc.sent[rt] = info.id + } + } +} + +// sendTypeId sends the id, which must have already been defined. +func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) { + // Identify the type of this top-level value. + state.encodeInt(int64(enc.sent[ut.base])) +} + +// 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 { + // Make sure we're single-threaded through here, so multiple + // goroutines can share an encoder. + enc.mutex.Lock() + defer enc.mutex.Unlock() + + // Remove any nested writers remaining due to previous errors. + enc.w = enc.w[0:1] + + ut, err := validUserType(value.Type()) + if err != nil { + return err + } + + enc.err = nil + enc.byteBuf.Reset() + enc.byteBuf.Write(spaceForLength) + state := enc.newEncoderState(&enc.byteBuf) + + enc.sendTypeDescriptor(enc.writer(), state, ut) + enc.sendTypeId(state, ut) + if enc.err != nil { + return enc.err + } + + // Encode the object. + enc.encode(state.b, value, ut) + if enc.err == nil { + enc.writeMessage(enc.writer(), state.b) + } + + enc.freeEncoderState(state) + return enc.err +} diff --git a/src/pkg/encoding/gob/encoder_test.go b/src/pkg/encoding/gob/encoder_test.go new file mode 100644 index 000000000..c4947cbb8 --- /dev/null +++ b/src/pkg/encoding/gob/encoder_test.go @@ -0,0 +1,738 @@ +// 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 gob + +import ( + "bytes" + "fmt" + "io" + "reflect" + "strings" + "testing" +) + +type ET2 struct { + X string +} + +type ET1 struct { + A int + Et2 *ET2 + Next *ET1 +} + +// Like ET1 but with a different name for a field +type ET3 struct { + A int + Et2 *ET2 + DifferentNext *ET1 +} + +// Like ET1 but with a different type for a field +type ET4 struct { + A int + Et2 float64 + Next int +} + +func TestEncoderDecoder(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + et1 := new(ET1) + et1.A = 7 + et1.Et2 = new(ET2) + err := enc.Encode(et1) + if err != nil { + t.Error("encoder fail:", err) + } + dec := NewDecoder(b) + newEt1 := new(ET1) + err = dec.Decode(newEt1) + if err != nil { + t.Fatal("error decoding ET1:", err) + } + + if !reflect.DeepEqual(et1, newEt1) { + t.Fatalf("invalid data for et1: expected %+v; got %+v", *et1, *newEt1) + } + if b.Len() != 0 { + t.Error("not at eof;", b.Len(), "bytes left") + } + + enc.Encode(et1) + newEt1 = new(ET1) + err = dec.Decode(newEt1) + if err != nil { + t.Fatal("round 2: error decoding ET1:", err) + } + if !reflect.DeepEqual(et1, newEt1) { + t.Fatalf("round 2: invalid data for et1: expected %+v; got %+v", *et1, *newEt1) + } + if b.Len() != 0 { + t.Error("round 2: not at eof;", b.Len(), "bytes left") + } + + // Now test with a running encoder/decoder pair that we recognize a type mismatch. + err = enc.Encode(et1) + if err != nil { + t.Error("round 3: encoder fail:", err) + } + newEt2 := new(ET2) + err = dec.Decode(newEt2) + if err == nil { + t.Fatal("round 3: expected `bad type' error decoding ET2") + } +} + +// Run one value through the encoder/decoder, but use the wrong type. +// Input is always an ET1; we compare it to whatever is under 'e'. +func badTypeCheck(e interface{}, shouldFail bool, msg string, t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + et1 := new(ET1) + et1.A = 7 + et1.Et2 = new(ET2) + err := enc.Encode(et1) + if err != nil { + t.Error("encoder fail:", err) + } + dec := NewDecoder(b) + err = dec.Decode(e) + if shouldFail && err == nil { + t.Error("expected error for", msg) + } + if !shouldFail && err != nil { + t.Error("unexpected error for", msg, err) + } +} + +// Test that we recognize a bad type the first time. +func TestWrongTypeDecoder(t *testing.T) { + badTypeCheck(new(ET2), true, "no fields in common", t) + badTypeCheck(new(ET3), false, "different name of field", t) + badTypeCheck(new(ET4), true, "different type of field", t) +} + +func corruptDataCheck(s string, err error, t *testing.T) { + b := bytes.NewBufferString(s) + dec := NewDecoder(b) + err1 := dec.Decode(new(ET2)) + if err1 != err { + t.Errorf("from %q expected error %s; got %s", s, err, err1) + } +} + +// Check that we survive bad data. +func TestBadData(t *testing.T) { + corruptDataCheck("", io.EOF, t) + corruptDataCheck("\x7Fhi", io.ErrUnexpectedEOF, t) + corruptDataCheck("\x03now is the time for all good men", errBadType, t) +} + +// Types not supported by the Encoder. +var unsupportedValues = []interface{}{ + make(chan int), + func(a int) bool { return true }, +} + +func TestUnsupported(t *testing.T) { + var b bytes.Buffer + enc := NewEncoder(&b) + for _, v := range unsupportedValues { + err := enc.Encode(v) + if err == nil { + t.Errorf("expected error for %T; got none", v) + } + } +} + +func encAndDec(in, out interface{}) error { + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(in) + if err != nil { + return err + } + dec := NewDecoder(b) + err = dec.Decode(out) + if err != nil { + return err + } + return nil +} + +func TestTypeToPtrType(t *testing.T) { + // Encode a T, decode a *T + type Type0 struct { + A int + } + t0 := Type0{7} + t0p := new(Type0) + if err := encAndDec(t0, t0p); err != nil { + t.Error(err) + } +} + +func TestPtrTypeToType(t *testing.T) { + // Encode a *T, decode a T + type Type1 struct { + A uint + } + t1p := &Type1{17} + var t1 Type1 + if err := encAndDec(t1, t1p); err != nil { + t.Error(err) + } +} + +func TestTypeToPtrPtrPtrPtrType(t *testing.T) { + type Type2 struct { + A ****float64 + } + t2 := Type2{} + t2.A = new(***float64) + *t2.A = new(**float64) + **t2.A = new(*float64) + ***t2.A = new(float64) + ****t2.A = 27.4 + t2pppp := new(***Type2) + if err := encAndDec(t2, t2pppp); err != nil { + t.Fatal(err) + } + if ****(****t2pppp).A != ****t2.A { + t.Errorf("wrong value after decode: %g not %g", ****(****t2pppp).A, ****t2.A) + } +} + +func TestSlice(t *testing.T) { + type Type3 struct { + A []string + } + t3p := &Type3{[]string{"hello", "world"}} + var t3 Type3 + if err := encAndDec(t3, t3p); err != nil { + t.Error(err) + } +} + +func TestValueError(t *testing.T) { + // Encode a *T, decode a T + type Type4 struct { + A int + } + t4p := &Type4{3} + var t4 Type4 // note: not a pointer. + if err := encAndDec(t4p, t4); err == nil || strings.Index(err.Error(), "pointer") < 0 { + t.Error("expected error about pointer; got", err) + } +} + +func TestArray(t *testing.T) { + type Type5 struct { + A [3]string + B [3]byte + } + type Type6 struct { + A [2]string // can't hold t5.a + } + t5 := Type5{[3]string{"hello", ",", "world"}, [3]byte{1, 2, 3}} + var t5p Type5 + if err := encAndDec(t5, &t5p); err != nil { + t.Error(err) + } + var t6 Type6 + if err := encAndDec(t5, &t6); err == nil { + t.Error("should fail with mismatched array sizes") + } +} + +func TestRecursiveMapType(t *testing.T) { + type recursiveMap map[string]recursiveMap + r1 := recursiveMap{"A": recursiveMap{"B": nil, "C": nil}, "D": nil} + r2 := make(recursiveMap) + if err := encAndDec(r1, &r2); err != nil { + t.Error(err) + } +} + +func TestRecursiveSliceType(t *testing.T) { + type recursiveSlice []recursiveSlice + r1 := recursiveSlice{0: recursiveSlice{0: nil}, 1: nil} + r2 := make(recursiveSlice, 0) + if err := encAndDec(r1, &r2); err != nil { + t.Error(err) + } +} + +// Regression test for bug: must send zero values inside arrays +func TestDefaultsInArray(t *testing.T) { + type Type7 struct { + B []bool + I []int + S []string + F []float64 + } + t7 := Type7{ + []bool{false, false, true}, + []int{0, 0, 1}, + []string{"hi", "", "there"}, + []float64{0, 0, 1}, + } + var t7p Type7 + if err := encAndDec(t7, &t7p); err != nil { + t.Error(err) + } +} + +var testInt int +var testFloat32 float32 +var testString string +var testSlice []string +var testMap map[string]int +var testArray [7]int + +type SingleTest struct { + in interface{} + out interface{} + err string +} + +var singleTests = []SingleTest{ + {17, &testInt, ""}, + {float32(17.5), &testFloat32, ""}, + {"bike shed", &testString, ""}, + {[]string{"bike", "shed", "paint", "color"}, &testSlice, ""}, + {map[string]int{"seven": 7, "twelve": 12}, &testMap, ""}, + {[7]int{4, 55, 0, 0, 0, 0, 0}, &testArray, ""}, // case that once triggered a bug + {[7]int{4, 55, 1, 44, 22, 66, 1234}, &testArray, ""}, + + // Decode errors + {172, &testFloat32, "type"}, +} + +func TestSingletons(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + dec := NewDecoder(b) + for _, test := range singleTests { + b.Reset() + err := enc.Encode(test.in) + if err != nil { + t.Errorf("error encoding %v: %s", test.in, err) + continue + } + err = dec.Decode(test.out) + switch { + case err != nil && test.err == "": + t.Errorf("error decoding %v: %s", test.in, err) + continue + case err == nil && test.err != "": + t.Errorf("expected error decoding %v: %s", test.in, test.err) + continue + case err != nil && test.err != "": + if strings.Index(err.Error(), test.err) < 0 { + t.Errorf("wrong error decoding %v: wanted %s, got %v", test.in, test.err, err) + } + continue + } + // Get rid of the pointer in the rhs + val := reflect.ValueOf(test.out).Elem().Interface() + if !reflect.DeepEqual(test.in, val) { + t.Errorf("decoding singleton: expected %v got %v", test.in, val) + } + } +} + +func TestStructNonStruct(t *testing.T) { + type Struct struct { + A string + } + type NonStruct string + s := Struct{"hello"} + var sp Struct + if err := encAndDec(s, &sp); err != nil { + t.Error(err) + } + var ns NonStruct + if err := encAndDec(s, &ns); err == nil { + t.Error("should get error for struct/non-struct") + } else if strings.Index(err.Error(), "type") < 0 { + t.Error("for struct/non-struct expected type error; got", err) + } + // Now try the other way + var nsp NonStruct + if err := encAndDec(ns, &nsp); err != nil { + t.Error(err) + } + if err := encAndDec(ns, &s); err == nil { + t.Error("should get error for non-struct/struct") + } else if strings.Index(err.Error(), "type") < 0 { + t.Error("for non-struct/struct expected type error; got", err) + } +} + +type interfaceIndirectTestI interface { + F() bool +} + +type interfaceIndirectTestT struct{} + +func (this *interfaceIndirectTestT) F() bool { + return true +} + +// A version of a bug reported on golang-nuts. Also tests top-level +// slice of interfaces. The issue was registering *T caused T to be +// stored as the concrete type. +func TestInterfaceIndirect(t *testing.T) { + Register(&interfaceIndirectTestT{}) + b := new(bytes.Buffer) + w := []interfaceIndirectTestI{&interfaceIndirectTestT{}} + err := NewEncoder(b).Encode(w) + if err != nil { + t.Fatal("encode error:", err) + } + + var r []interfaceIndirectTestI + err = NewDecoder(b).Decode(&r) + if err != nil { + t.Fatal("decode error:", err) + } +} + +// Now follow various tests that decode into things that can't represent the +// encoded value, all of which should be legal. + +// Also, when the ignored object contains an interface value, it may define +// types. Make sure that skipping the value still defines the types by using +// the encoder/decoder pair to send a value afterwards. If an interface +// is sent, its type in the test is always NewType0, so this checks that the +// encoder and decoder don't skew with respect to type definitions. + +type Struct0 struct { + I interface{} +} + +type NewType0 struct { + S string +} + +type ignoreTest struct { + in, out interface{} +} + +var ignoreTests = []ignoreTest{ + // Decode normal struct into an empty struct + {&struct{ A int }{23}, &struct{}{}}, + // Decode normal struct into a nil. + {&struct{ A int }{23}, nil}, + // Decode singleton string into a nil. + {"hello, world", nil}, + // Decode singleton slice into a nil. + {[]int{1, 2, 3, 4}, nil}, + // Decode struct containing an interface into a nil. + {&Struct0{&NewType0{"value0"}}, nil}, + // Decode singleton slice of interfaces into a nil. + {[]interface{}{"hi", &NewType0{"value1"}, 23}, nil}, +} + +func TestDecodeIntoNothing(t *testing.T) { + Register(new(NewType0)) + for i, test := range ignoreTests { + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(test.in) + if err != nil { + t.Errorf("%d: encode error %s:", i, err) + continue + } + dec := NewDecoder(b) + err = dec.Decode(test.out) + if err != nil { + t.Errorf("%d: decode error: %s", i, err) + continue + } + // Now see if the encoder and decoder are in a consistent state. + str := fmt.Sprintf("Value %d", i) + err = enc.Encode(&NewType0{str}) + if err != nil { + t.Fatalf("%d: NewType0 encode error: %s", i, err) + } + ns := new(NewType0) + err = dec.Decode(ns) + if err != nil { + t.Fatalf("%d: NewType0 decode error: %s", i, err) + } + if ns.S != str { + t.Fatalf("%d: expected %q got %q", i, str, ns.S) + } + } +} + +// Another bug from golang-nuts, involving nested interfaces. +type Bug0Outer struct { + Bug0Field interface{} +} + +type Bug0Inner struct { + A int +} + +func TestNestedInterfaces(t *testing.T) { + var buf bytes.Buffer + e := NewEncoder(&buf) + d := NewDecoder(&buf) + Register(new(Bug0Outer)) + Register(new(Bug0Inner)) + f := &Bug0Outer{&Bug0Outer{&Bug0Inner{7}}} + var v interface{} = f + err := e.Encode(&v) + if err != nil { + t.Fatal("Encode:", err) + } + err = d.Decode(&v) + if err != nil { + t.Fatal("Decode:", err) + } + // Make sure it decoded correctly. + outer1, ok := v.(*Bug0Outer) + if !ok { + t.Fatalf("v not Bug0Outer: %T", v) + } + outer2, ok := outer1.Bug0Field.(*Bug0Outer) + if !ok { + t.Fatalf("v.Bug0Field not Bug0Outer: %T", outer1.Bug0Field) + } + inner, ok := outer2.Bug0Field.(*Bug0Inner) + if !ok { + t.Fatalf("v.Bug0Field.Bug0Field not Bug0Inner: %T", outer2.Bug0Field) + } + if inner.A != 7 { + t.Fatalf("final value %d; expected %d", inner.A, 7) + } +} + +// The bugs keep coming. We forgot to send map subtypes before the map. + +type Bug1Elem struct { + Name string + Id int +} + +type Bug1StructMap map[string]Bug1Elem + +func bug1EncDec(in Bug1StructMap, out *Bug1StructMap) error { + return nil +} + +func TestMapBug1(t *testing.T) { + in := make(Bug1StructMap) + in["val1"] = Bug1Elem{"elem1", 1} + in["val2"] = Bug1Elem{"elem2", 2} + + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(in) + if err != nil { + t.Fatal("encode:", err) + } + dec := NewDecoder(b) + out := make(Bug1StructMap) + err = dec.Decode(&out) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(in, out) { + t.Errorf("mismatch: %v %v", in, out) + } +} + +func TestGobMapInterfaceEncode(t *testing.T) { + m := map[string]interface{}{ + "up": uintptr(0), + "i0": []int{-1}, + "i1": []int8{-1}, + "i2": []int16{-1}, + "i3": []int32{-1}, + "i4": []int64{-1}, + "u0": []uint{1}, + "u1": []uint8{1}, + "u2": []uint16{1}, + "u3": []uint32{1}, + "u4": []uint64{1}, + "f0": []float32{1}, + "f1": []float64{1}, + "c0": []complex64{complex(2, -2)}, + "c1": []complex128{complex(2, float64(-2))}, + "us": []uintptr{0}, + "bo": []bool{false}, + "st": []string{"s"}, + } + enc := NewEncoder(new(bytes.Buffer)) + err := enc.Encode(m) + if err != nil { + t.Errorf("encode map: %s", err) + } +} + +func TestSliceReusesMemory(t *testing.T) { + buf := new(bytes.Buffer) + // Bytes + { + x := []byte("abcd") + enc := NewEncoder(buf) + err := enc.Encode(x) + if err != nil { + t.Errorf("bytes: encode: %s", err) + } + // Decode into y, which is big enough. + y := []byte("ABCDE") + addr := &y[0] + dec := NewDecoder(buf) + err = dec.Decode(&y) + if err != nil { + t.Fatal("bytes: decode:", err) + } + if !bytes.Equal(x, y) { + t.Errorf("bytes: expected %q got %q\n", x, y) + } + if addr != &y[0] { + t.Errorf("bytes: unnecessary reallocation") + } + } + // general slice + { + x := []rune("abcd") + enc := NewEncoder(buf) + err := enc.Encode(x) + if err != nil { + t.Errorf("ints: encode: %s", err) + } + // Decode into y, which is big enough. + y := []rune("ABCDE") + addr := &y[0] + dec := NewDecoder(buf) + err = dec.Decode(&y) + if err != nil { + t.Fatal("ints: decode:", err) + } + if !reflect.DeepEqual(x, y) { + t.Errorf("ints: expected %q got %q\n", x, y) + } + if addr != &y[0] { + t.Errorf("ints: unnecessary reallocation") + } + } +} + +// Used to crash: negative count in recvMessage. +func TestBadCount(t *testing.T) { + b := []byte{0xfb, 0xa5, 0x82, 0x2f, 0xca, 0x1} + if err := NewDecoder(bytes.NewBuffer(b)).Decode(nil); err == nil { + t.Error("expected error from bad count") + } else if err.Error() != errBadCount.Error() { + t.Error("expected bad count error; got", err) + } +} + +// Verify that sequential Decoders built on a single input will +// succeed if the input implements ReadByte and there is no +// type information in the stream. +func TestSequentialDecoder(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + const count = 10 + for i := 0; i < count; i++ { + s := fmt.Sprintf("%d", i) + if err := enc.Encode(s); err != nil { + t.Error("encoder fail:", err) + } + } + for i := 0; i < count; i++ { + dec := NewDecoder(b) + var s string + if err := dec.Decode(&s); err != nil { + t.Fatal("decoder fail:", err) + } + if s != fmt.Sprintf("%d", i) { + t.Fatalf("decode expected %d got %s", i, s) + } + } +} + +// Should be able to have unrepresentable fields (chan, func) as long as they +// are unexported. +type Bug2 struct { + A int + b chan int +} + +func TestUnexportedChan(t *testing.T) { + b := Bug2{23, make(chan int)} + var stream bytes.Buffer + enc := NewEncoder(&stream) + if err := enc.Encode(b); err != nil { + t.Fatalf("error encoding unexported channel: %s", err) + } +} + +func TestSliceIncompatibility(t *testing.T) { + var in = []byte{1, 2, 3} + var out []int + if err := encAndDec(in, &out); err == nil { + t.Error("expected compatibility error") + } +} + +// Mutually recursive slices of structs caused problems. +type Bug3 struct { + Num int + Children []*Bug3 +} + +func TestGobPtrSlices(t *testing.T) { + in := []*Bug3{ + {1, nil}, + {2, nil}, + } + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(&in) + if err != nil { + t.Fatal("encode:", err) + } + + var out []*Bug3 + err = NewDecoder(b).Decode(&out) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(in, out) { + t.Fatalf("got %v; wanted %v", out, in) + } +} + +// getDecEnginePtr cached engine for ut.base instead of ut.user so we passed +// a *map and then tried to reuse its engine to decode the inner map. +func TestPtrToMapOfMap(t *testing.T) { + Register(make(map[string]interface{})) + subdata := make(map[string]interface{}) + subdata["bar"] = "baz" + data := make(map[string]interface{}) + data["foo"] = subdata + + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(data) + if err != nil { + t.Fatal("encode:", err) + } + var newData map[string]interface{} + err = NewDecoder(b).Decode(&newData) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(data, newData) { + t.Fatalf("expected %v got %v", data, newData) + } +} diff --git a/src/pkg/encoding/gob/error.go b/src/pkg/encoding/gob/error.go new file mode 100644 index 000000000..92cc0c615 --- /dev/null +++ b/src/pkg/encoding/gob/error.go @@ -0,0 +1,43 @@ +// 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 gob + +import "fmt" + +// Errors in decoding and encoding are handled using panic and recover. +// Panics caused by user error (that is, everything except run-time panics +// such as "index out of bounds" errors) do not leave the file that caused +// them, but are instead turned into plain error returns. Encoding and +// decoding functions and methods that do not return an error either use +// panic to report an error or are guaranteed error-free. + +// A gobError is used to distinguish errors (panics) generated in this package. +type gobError struct { + err error +} + +// errorf is like error_ but takes Printf-style arguments to construct an error. +// It always prefixes the message with "gob: ". +func errorf(format string, args ...interface{}) { + error_(fmt.Errorf("gob: "+format, args...)) +} + +// error wraps the argument error and uses it as the argument to panic. +func error_(err error) { + panic(gobError{err}) +} + +// catchError is meant to be used as a deferred function to turn a panic(gobError) into a +// plain error. It overwrites the error return of the function that deferred its call. +func catchError(err *error) { + if e := recover(); e != nil { + ge, ok := e.(gobError) + if !ok { + panic(e) + } + *err = ge.err + } + return +} diff --git a/src/pkg/encoding/gob/gobencdec_test.go b/src/pkg/encoding/gob/gobencdec_test.go new file mode 100644 index 000000000..45240d764 --- /dev/null +++ b/src/pkg/encoding/gob/gobencdec_test.go @@ -0,0 +1,594 @@ +// Copyright 20011 The Go 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 of the GobEncoder/GobDecoder support. + +package gob + +import ( + "bytes" + "errors" + "fmt" + "io" + "strings" + "testing" + "time" +) + +// Types that implement the GobEncoder/Decoder interfaces. + +type ByteStruct struct { + a byte // not an exported field +} + +type StringStruct struct { + s string // not an exported field +} + +type ArrayStruct struct { + a [8192]byte // not an exported field +} + +type Gobber int + +type ValueGobber string // encodes with a value, decodes with a pointer. + +// The relevant methods + +func (g *ByteStruct) GobEncode() ([]byte, error) { + b := make([]byte, 3) + b[0] = g.a + b[1] = g.a + 1 + b[2] = g.a + 2 + return b, nil +} + +func (g *ByteStruct) GobDecode(data []byte) error { + if g == nil { + return errors.New("NIL RECEIVER") + } + // Expect N sequential-valued bytes. + if len(data) == 0 { + return io.EOF + } + g.a = data[0] + for i, c := range data { + if c != g.a+byte(i) { + return errors.New("invalid data sequence") + } + } + return nil +} + +func (g *StringStruct) GobEncode() ([]byte, error) { + return []byte(g.s), nil +} + +func (g *StringStruct) GobDecode(data []byte) error { + // Expect N sequential-valued bytes. + if len(data) == 0 { + return io.EOF + } + a := data[0] + for i, c := range data { + if c != a+byte(i) { + return errors.New("invalid data sequence") + } + } + g.s = string(data) + return nil +} + +func (a *ArrayStruct) GobEncode() ([]byte, error) { + return a.a[:], nil +} + +func (a *ArrayStruct) GobDecode(data []byte) error { + if len(data) != len(a.a) { + return errors.New("wrong length in array decode") + } + copy(a.a[:], data) + return nil +} + +func (g *Gobber) GobEncode() ([]byte, error) { + return []byte(fmt.Sprintf("VALUE=%d", *g)), nil +} + +func (g *Gobber) GobDecode(data []byte) error { + _, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g)) + return err +} + +func (v ValueGobber) GobEncode() ([]byte, error) { + return []byte(fmt.Sprintf("VALUE=%s", v)), nil +} + +func (v *ValueGobber) GobDecode(data []byte) error { + _, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v)) + return err +} + +// Structs that include GobEncodable fields. + +type GobTest0 struct { + X int // guarantee we have something in common with GobTest* + G *ByteStruct +} + +type GobTest1 struct { + X int // guarantee we have something in common with GobTest* + G *StringStruct +} + +type GobTest2 struct { + X int // guarantee we have something in common with GobTest* + G string // not a GobEncoder - should give us errors +} + +type GobTest3 struct { + X int // guarantee we have something in common with GobTest* + G *Gobber +} + +type GobTest4 struct { + X int // guarantee we have something in common with GobTest* + V ValueGobber +} + +type GobTest5 struct { + X int // guarantee we have something in common with GobTest* + V *ValueGobber +} + +type GobTestIgnoreEncoder struct { + X int // guarantee we have something in common with GobTest* +} + +type GobTestValueEncDec struct { + X int // guarantee we have something in common with GobTest* + G StringStruct // not a pointer. +} + +type GobTestIndirectEncDec struct { + X int // guarantee we have something in common with GobTest* + G ***StringStruct // indirections to the receiver. +} + +type GobTestArrayEncDec struct { + X int // guarantee we have something in common with GobTest* + A ArrayStruct // not a pointer. +} + +type GobTestIndirectArrayEncDec struct { + X int // guarantee we have something in common with GobTest* + A ***ArrayStruct // indirections to a large receiver. +} + +func TestGobEncoderField(t *testing.T) { + b := new(bytes.Buffer) + // First a field that's a structure. + enc := NewEncoder(b) + err := enc.Encode(GobTest0{17, &ByteStruct{'A'}}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTest0) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.G.a != 'A' { + t.Errorf("expected 'A' got %c", x.G.a) + } + // Now a field that's not a structure. + b.Reset() + gobber := Gobber(23) + err = enc.Encode(GobTest3{17, &gobber}) + if err != nil { + t.Fatal("encode error:", err) + } + y := new(GobTest3) + err = dec.Decode(y) + if err != nil { + t.Fatal("decode error:", err) + } + if *y.G != 23 { + t.Errorf("expected '23 got %d", *y.G) + } +} + +// Even though the field is a value, we can still take its address +// and should be able to call the methods. +func TestGobEncoderValueField(t *testing.T) { + b := new(bytes.Buffer) + // First a field that's a structure. + enc := NewEncoder(b) + err := enc.Encode(GobTestValueEncDec{17, StringStruct{"HIJKL"}}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTestValueEncDec) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.G.s != "HIJKL" { + t.Errorf("expected `HIJKL` got %s", x.G.s) + } +} + +// GobEncode/Decode should work even if the value is +// more indirect than the receiver. +func TestGobEncoderIndirectField(t *testing.T) { + b := new(bytes.Buffer) + // First a field that's a structure. + enc := NewEncoder(b) + s := &StringStruct{"HIJKL"} + sp := &s + err := enc.Encode(GobTestIndirectEncDec{17, &sp}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTestIndirectEncDec) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if (***x.G).s != "HIJKL" { + t.Errorf("expected `HIJKL` got %s", (***x.G).s) + } +} + +// Test with a large field with methods. +func TestGobEncoderArrayField(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + var a GobTestArrayEncDec + a.X = 17 + for i := range a.A.a { + a.A.a[i] = byte(i) + } + err := enc.Encode(a) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTestArrayEncDec) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + for i, v := range x.A.a { + if v != byte(i) { + t.Errorf("expected %x got %x", byte(i), v) + break + } + } +} + +// Test an indirection to a large field with methods. +func TestGobEncoderIndirectArrayField(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + var a GobTestIndirectArrayEncDec + a.X = 17 + var array ArrayStruct + ap := &array + app := &ap + a.A = &app + for i := range array.a { + array.a[i] = byte(i) + } + err := enc.Encode(a) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTestIndirectArrayEncDec) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + for i, v := range (***x.A).a { + if v != byte(i) { + t.Errorf("expected %x got %x", byte(i), v) + break + } + } +} + +// As long as the fields have the same name and implement the +// interface, we can cross-connect them. Not sure it's useful +// and may even be bad but it works and it's hard to prevent +// without exposing the contents of the object, which would +// defeat the purpose. +func TestGobEncoderFieldsOfDifferentType(t *testing.T) { + // first, string in field to byte in field + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(GobTest1{17, &StringStruct{"ABC"}}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTest0) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.G.a != 'A' { + t.Errorf("expected 'A' got %c", x.G.a) + } + // now the other direction, byte in field to string in field + b.Reset() + err = enc.Encode(GobTest0{17, &ByteStruct{'X'}}) + if err != nil { + t.Fatal("encode error:", err) + } + y := new(GobTest1) + err = dec.Decode(y) + if err != nil { + t.Fatal("decode error:", err) + } + if y.G.s != "XYZ" { + t.Fatalf("expected `XYZ` got %c", y.G.s) + } +} + +// Test that we can encode a value and decode into a pointer. +func TestGobEncoderValueEncoder(t *testing.T) { + // first, string in field to byte in field + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(GobTest4{17, ValueGobber("hello")}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTest5) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if *x.V != "hello" { + t.Errorf("expected `hello` got %s", x.V) + } +} + +func TestGobEncoderFieldTypeError(t *testing.T) { + // GobEncoder to non-decoder: error + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(GobTest1{17, &StringStruct{"ABC"}}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := &GobTest2{} + err = dec.Decode(x) + if err == nil { + t.Fatal("expected decode error for mismatched fields (encoder to non-decoder)") + } + if strings.Index(err.Error(), "type") < 0 { + t.Fatal("expected type error; got", err) + } + // Non-encoder to GobDecoder: error + b.Reset() + err = enc.Encode(GobTest2{17, "ABC"}) + if err != nil { + t.Fatal("encode error:", err) + } + y := &GobTest1{} + err = dec.Decode(y) + if err == nil { + t.Fatal("expected decode error for mismatched fields (non-encoder to decoder)") + } + if strings.Index(err.Error(), "type") < 0 { + t.Fatal("expected type error; got", err) + } +} + +// Even though ByteStruct is a struct, it's treated as a singleton at the top level. +func TestGobEncoderStructSingleton(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(&ByteStruct{'A'}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(ByteStruct) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.a != 'A' { + t.Errorf("expected 'A' got %c", x.a) + } +} + +func TestGobEncoderNonStructSingleton(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(Gobber(1234)) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + var x Gobber + err = dec.Decode(&x) + if err != nil { + t.Fatal("decode error:", err) + } + if x != 1234 { + t.Errorf("expected 1234 got %d", x) + } +} + +func TestGobEncoderIgnoreStructField(t *testing.T) { + b := new(bytes.Buffer) + // First a field that's a structure. + enc := NewEncoder(b) + err := enc.Encode(GobTest0{17, &ByteStruct{'A'}}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTestIgnoreEncoder) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.X != 17 { + t.Errorf("expected 17 got %c", x.X) + } +} + +func TestGobEncoderIgnoreNonStructField(t *testing.T) { + b := new(bytes.Buffer) + // First a field that's a structure. + enc := NewEncoder(b) + gobber := Gobber(23) + err := enc.Encode(GobTest3{17, &gobber}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTestIgnoreEncoder) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.X != 17 { + t.Errorf("expected 17 got %c", x.X) + } +} + +func TestGobEncoderIgnoreNilEncoder(t *testing.T) { + b := new(bytes.Buffer) + // First a field that's a structure. + enc := NewEncoder(b) + err := enc.Encode(GobTest0{X: 18}) // G is nil + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTest0) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.X != 18 { + t.Errorf("expected x.X = 18, got %v", x.X) + } + if x.G != nil { + t.Errorf("expected x.G = nil, got %v", x.G) + } +} + +type gobDecoderBug0 struct { + foo, bar string +} + +func (br *gobDecoderBug0) String() string { + return br.foo + "-" + br.bar +} + +func (br *gobDecoderBug0) GobEncode() ([]byte, error) { + return []byte(br.String()), nil +} + +func (br *gobDecoderBug0) GobDecode(b []byte) error { + br.foo = "foo" + br.bar = "bar" + return nil +} + +// This was a bug: the receiver has a different indirection level +// than the variable. +func TestGobEncoderExtraIndirect(t *testing.T) { + gdb := &gobDecoderBug0{"foo", "bar"} + buf := new(bytes.Buffer) + e := NewEncoder(buf) + if err := e.Encode(gdb); err != nil { + t.Fatalf("encode: %v", err) + } + d := NewDecoder(buf) + var got *gobDecoderBug0 + if err := d.Decode(&got); err != nil { + t.Fatalf("decode: %v", err) + } + if got.foo != gdb.foo || got.bar != gdb.bar { + t.Errorf("got = %q, want %q", got, gdb) + } +} + +// Another bug: this caused a crash with the new Go1 Time type. +// We throw in a gob-encoding array, to test another case of isZero + +type isZeroBug struct { + T time.Time + S string + I int + A isZeroBugArray +} + +type isZeroBugArray [2]uint8 + +// Receiver is value, not pointer, to test isZero of array. +func (a isZeroBugArray) GobEncode() (b []byte, e error) { + b = append(b, a[:]...) + return b, nil +} + +func (a *isZeroBugArray) GobDecode(data []byte) error { + if len(data) != len(a) { + return io.EOF + } + a[0] = data[0] + a[1] = data[1] + return nil +} + +func TestGobEncodeIsZero(t *testing.T) { + x := isZeroBug{time.Now(), "hello", -55, isZeroBugArray{1, 2}} + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(x) + if err != nil { + t.Fatal("encode:", err) + } + var y isZeroBug + dec := NewDecoder(b) + err = dec.Decode(&y) + if err != nil { + t.Fatal("decode:", err) + } + if x != y { + t.Fatalf("%v != %v", x, y) + } +} + +func TestGobEncodePtrError(t *testing.T) { + var err error + b := new(bytes.Buffer) + enc := NewEncoder(b) + err = enc.Encode(&err) + if err != nil { + t.Fatal("encode:", err) + } + dec := NewDecoder(b) + err2 := fmt.Errorf("foo") + err = dec.Decode(&err2) + if err != nil { + t.Fatal("decode:", err) + } + if err2 != nil { + t.Fatalf("expected nil, got %v", err2) + } +} diff --git a/src/pkg/encoding/gob/timing_test.go b/src/pkg/encoding/gob/timing_test.go new file mode 100644 index 000000000..b9371c423 --- /dev/null +++ b/src/pkg/encoding/gob/timing_test.go @@ -0,0 +1,96 @@ +// 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 gob + +import ( + "bytes" + "fmt" + "io" + "os" + "runtime" + "testing" +) + +type Bench struct { + A int + B float64 + C string + D []byte +} + +func benchmarkEndToEnd(r io.Reader, w io.Writer, b *testing.B) { + b.StopTimer() + enc := NewEncoder(w) + dec := NewDecoder(r) + bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")} + b.StartTimer() + for i := 0; i < b.N; i++ { + if enc.Encode(bench) != nil { + panic("encode error") + } + if dec.Decode(bench) != nil { + panic("decode error") + } + } +} + +func BenchmarkEndToEndPipe(b *testing.B) { + r, w, err := os.Pipe() + if err != nil { + b.Fatal("can't get pipe:", err) + } + benchmarkEndToEnd(r, w, b) +} + +func BenchmarkEndToEndByteBuffer(b *testing.B) { + var buf bytes.Buffer + benchmarkEndToEnd(&buf, &buf, b) +} + +func TestCountEncodeMallocs(t *testing.T) { + 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++ { + 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) +} + +func TestCountDecodeMallocs(t *testing.T) { + 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++ { + 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++ { + *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) +} diff --git a/src/pkg/encoding/gob/type.go b/src/pkg/encoding/gob/type.go new file mode 100644 index 000000000..0dd7a0a77 --- /dev/null +++ b/src/pkg/encoding/gob/type.go @@ -0,0 +1,808 @@ +// 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 gob + +import ( + "errors" + "fmt" + "os" + "reflect" + "sync" + "unicode" + "unicode/utf8" +) + +// userTypeInfo stores the information associated with a type the user has handed +// to the package. It's computed once and stored in a map keyed by reflection +// type. +type userTypeInfo struct { + user reflect.Type // the type the user handed us + base reflect.Type // the base type after all indirections + indir int // number of indirections to reach the base type + isGobEncoder bool // does the type implement GobEncoder? + isGobDecoder bool // does the type implement GobDecoder? + encIndir int8 // number of indirections to reach the receiver type; may be negative + decIndir int8 // number of indirections to reach the receiver type; may be negative +} + +var ( + // Protected by an RWMutex because we read it a lot and write + // it only when we see a new type, typically when compiling. + userTypeLock sync.RWMutex + userTypeCache = make(map[reflect.Type]*userTypeInfo) +) + +// validType returns, and saves, the information associated with user-provided type rt. +// If the user type is not valid, err will be non-nil. To be used when the error handler +// is not set up. +func validUserType(rt reflect.Type) (ut *userTypeInfo, err error) { + userTypeLock.RLock() + ut = userTypeCache[rt] + userTypeLock.RUnlock() + if ut != nil { + return + } + // Now set the value under the write lock. + userTypeLock.Lock() + defer userTypeLock.Unlock() + if ut = userTypeCache[rt]; ut != nil { + // Lost the race; not a problem. + return + } + ut = new(userTypeInfo) + ut.base = rt + ut.user = rt + // A type that is just a cycle of pointers (such as type T *T) cannot + // be represented in gobs, which need some concrete data. We use a + // cycle detection algorithm from Knuth, Vol 2, Section 3.1, Ex 6, + // pp 539-540. As we step through indirections, run another type at + // half speed. If they meet up, there's a cycle. + slowpoke := ut.base // walks half as fast as ut.base + for { + pt := ut.base + if pt.Kind() != reflect.Ptr { + break + } + ut.base = pt.Elem() + if ut.base == slowpoke { // ut.base lapped slowpoke + // recursive pointer type. + return nil, errors.New("can't represent recursive pointer type " + ut.base.String()) + } + if ut.indir%2 == 0 { + slowpoke = slowpoke.Elem() + } + ut.indir++ + } + ut.isGobEncoder, ut.encIndir = implementsInterface(ut.user, gobEncoderInterfaceType) + ut.isGobDecoder, ut.decIndir = implementsInterface(ut.user, gobDecoderInterfaceType) + userTypeCache[rt] = ut + return +} + +var ( + gobEncoderInterfaceType = reflect.TypeOf((*GobEncoder)(nil)).Elem() + gobDecoderInterfaceType = reflect.TypeOf((*GobDecoder)(nil)).Elem() +) + +// implementsInterface reports whether the type implements the +// gobEncoder/gobDecoder interface. +// It also returns the number of indirections required to get to the +// implementation. +func implementsInterface(typ, gobEncDecType reflect.Type) (success bool, indir int8) { + if typ == nil { + return + } + rt := typ + // The type might be a pointer and we need to keep + // dereferencing to the base type until we find an implementation. + for { + if rt.Implements(gobEncDecType) { + return true, indir + } + if p := rt; p.Kind() == reflect.Ptr { + indir++ + if indir > 100 { // insane number of indirections + return false, 0 + } + rt = p.Elem() + continue + } + break + } + // No luck yet, but if this is a base type (non-pointer), the pointer might satisfy. + if typ.Kind() != reflect.Ptr { + // Not a pointer, but does the pointer work? + if reflect.PtrTo(typ).Implements(gobEncDecType) { + return true, -1 + } + } + return false, 0 +} + +// userType returns, and saves, the information associated with user-provided type rt. +// If the user type is not valid, it calls error. +func userType(rt reflect.Type) *userTypeInfo { + ut, err := validUserType(rt) + if err != nil { + error_(err) + } + return ut +} + +// A typeId represents a gob Type as an integer that can be passed on the wire. +// Internally, typeIds are used as keys to a map to recover the underlying type info. +type typeId int32 + +var nextId typeId // incremented for each new type we build +var typeLock sync.Mutex // set while building a type +const firstUserId = 64 // lowest id number granted to user + +type gobType interface { + id() typeId + setId(id typeId) + name() string + string() string // not public; only for debugging + safeString(seen map[typeId]bool) string +} + +var types = make(map[reflect.Type]gobType) +var idToType = make(map[typeId]gobType) +var builtinIdToType map[typeId]gobType // set in init() after builtins are established + +func setTypeId(typ gobType) { + // When building recursive types, someone may get there before us. + if typ.id() != 0 { + return + } + nextId++ + typ.setId(nextId) + idToType[nextId] = typ +} + +func (t typeId) gobType() gobType { + if t == 0 { + return nil + } + return idToType[t] +} + +// string returns the string representation of the type associated with the typeId. +func (t typeId) string() string { + if t.gobType() == nil { + return "" + } + return t.gobType().string() +} + +// Name returns the name of the type associated with the typeId. +func (t typeId) name() string { + if t.gobType() == nil { + return "" + } + return t.gobType().name() +} + +// CommonType holds elements of all types. +// It is a historical artifact, kept for binary compatibility and exported +// only for the benefit of the package's encoding of type descriptors. It is +// not intended for direct use by clients. +type CommonType struct { + Name string + Id typeId +} + +func (t *CommonType) id() typeId { return t.Id } + +func (t *CommonType) setId(id typeId) { t.Id = id } + +func (t *CommonType) string() string { return t.Name } + +func (t *CommonType) safeString(seen map[typeId]bool) string { + return t.Name +} + +func (t *CommonType) name() string { return t.Name } + +// Create and check predefined types +// The string for tBytes is "bytes" not "[]byte" to signify its specialness. + +var ( + // Primordial types, needed during initialization. + // Always passed as pointers so the interface{} type + // goes through without losing its interfaceness. + tBool = bootstrapType("bool", (*bool)(nil), 1) + tInt = bootstrapType("int", (*int)(nil), 2) + tUint = bootstrapType("uint", (*uint)(nil), 3) + tFloat = bootstrapType("float", (*float64)(nil), 4) + tBytes = bootstrapType("bytes", (*[]byte)(nil), 5) + tString = bootstrapType("string", (*string)(nil), 6) + tComplex = bootstrapType("complex", (*complex128)(nil), 7) + tInterface = bootstrapType("interface", (*interface{})(nil), 8) + // Reserve some Ids for compatible expansion + tReserved7 = bootstrapType("_reserved1", (*struct{ r7 int })(nil), 9) + tReserved6 = bootstrapType("_reserved1", (*struct{ r6 int })(nil), 10) + tReserved5 = bootstrapType("_reserved1", (*struct{ r5 int })(nil), 11) + tReserved4 = bootstrapType("_reserved1", (*struct{ r4 int })(nil), 12) + tReserved3 = bootstrapType("_reserved1", (*struct{ r3 int })(nil), 13) + tReserved2 = bootstrapType("_reserved1", (*struct{ r2 int })(nil), 14) + tReserved1 = bootstrapType("_reserved1", (*struct{ r1 int })(nil), 15) +) + +// Predefined because it's needed by the Decoder +var tWireType = mustGetTypeInfo(reflect.TypeOf(wireType{})).id +var wireTypeUserInfo *userTypeInfo // userTypeInfo of (*wireType) + +func init() { + // Some magic numbers to make sure there are no surprises. + checkId(16, tWireType) + checkId(17, mustGetTypeInfo(reflect.TypeOf(arrayType{})).id) + checkId(18, mustGetTypeInfo(reflect.TypeOf(CommonType{})).id) + checkId(19, mustGetTypeInfo(reflect.TypeOf(sliceType{})).id) + checkId(20, mustGetTypeInfo(reflect.TypeOf(structType{})).id) + checkId(21, mustGetTypeInfo(reflect.TypeOf(fieldType{})).id) + checkId(23, mustGetTypeInfo(reflect.TypeOf(mapType{})).id) + + builtinIdToType = make(map[typeId]gobType) + for k, v := range idToType { + builtinIdToType[k] = v + } + + // Move the id space upwards to allow for growth in the predefined world + // without breaking existing files. + if nextId > firstUserId { + panic(fmt.Sprintln("nextId too large:", nextId)) + } + nextId = firstUserId + registerBasics() + wireTypeUserInfo = userType(reflect.TypeOf((*wireType)(nil))) +} + +// Array type +type arrayType struct { + CommonType + Elem typeId + Len int +} + +func newArrayType(name string) *arrayType { + a := &arrayType{CommonType{Name: name}, 0, 0} + return a +} + +func (a *arrayType) init(elem gobType, len int) { + // Set our type id before evaluating the element's, in case it's our own. + setTypeId(a) + a.Elem = elem.id() + a.Len = len +} + +func (a *arrayType) safeString(seen map[typeId]bool) string { + if seen[a.Id] { + return a.Name + } + seen[a.Id] = true + return fmt.Sprintf("[%d]%s", a.Len, a.Elem.gobType().safeString(seen)) +} + +func (a *arrayType) string() string { return a.safeString(make(map[typeId]bool)) } + +// GobEncoder type (something that implements the GobEncoder interface) +type gobEncoderType struct { + CommonType +} + +func newGobEncoderType(name string) *gobEncoderType { + g := &gobEncoderType{CommonType{Name: name}} + setTypeId(g) + return g +} + +func (g *gobEncoderType) safeString(seen map[typeId]bool) string { + return g.Name +} + +func (g *gobEncoderType) string() string { return g.Name } + +// Map type +type mapType struct { + CommonType + Key typeId + Elem typeId +} + +func newMapType(name string) *mapType { + m := &mapType{CommonType{Name: name}, 0, 0} + return m +} + +func (m *mapType) init(key, elem gobType) { + // Set our type id before evaluating the element's, in case it's our own. + setTypeId(m) + m.Key = key.id() + m.Elem = elem.id() +} + +func (m *mapType) safeString(seen map[typeId]bool) string { + if seen[m.Id] { + return m.Name + } + seen[m.Id] = true + key := m.Key.gobType().safeString(seen) + elem := m.Elem.gobType().safeString(seen) + return fmt.Sprintf("map[%s]%s", key, elem) +} + +func (m *mapType) string() string { return m.safeString(make(map[typeId]bool)) } + +// Slice type +type sliceType struct { + CommonType + Elem typeId +} + +func newSliceType(name string) *sliceType { + s := &sliceType{CommonType{Name: name}, 0} + return s +} + +func (s *sliceType) init(elem gobType) { + // Set our type id before evaluating the element's, in case it's our own. + setTypeId(s) + // See the comments about ids in newTypeObject. Only slices and + // structs have mutual recursion. + if elem.id() == 0 { + setTypeId(elem) + } + s.Elem = elem.id() +} + +func (s *sliceType) safeString(seen map[typeId]bool) string { + if seen[s.Id] { + return s.Name + } + seen[s.Id] = true + return fmt.Sprintf("[]%s", s.Elem.gobType().safeString(seen)) +} + +func (s *sliceType) string() string { return s.safeString(make(map[typeId]bool)) } + +// Struct type +type fieldType struct { + Name string + Id typeId +} + +type structType struct { + CommonType + Field []*fieldType +} + +func (s *structType) safeString(seen map[typeId]bool) string { + if s == nil { + return "" + } + if _, ok := seen[s.Id]; ok { + return s.Name + } + seen[s.Id] = true + str := s.Name + " = struct { " + for _, f := range s.Field { + str += fmt.Sprintf("%s %s; ", f.Name, f.Id.gobType().safeString(seen)) + } + str += "}" + return str +} + +func (s *structType) string() string { return s.safeString(make(map[typeId]bool)) } + +func newStructType(name string) *structType { + s := &structType{CommonType{Name: name}, nil} + // For historical reasons we set the id here rather than init. + // See the comment in newTypeObject for details. + setTypeId(s) + return s +} + +// newTypeObject allocates a gobType for the reflection type rt. +// Unless ut represents a GobEncoder, rt should be the base type +// of ut. +// This is only called from the encoding side. The decoding side +// works through typeIds and userTypeInfos alone. +func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, error) { + // Does this type implement GobEncoder? + if ut.isGobEncoder { + return newGobEncoderType(name), nil + } + var err error + var type0, type1 gobType + defer func() { + if err != nil { + delete(types, rt) + } + }() + // Install the top-level type before the subtypes (e.g. struct before + // fields) so recursive types can be constructed safely. + switch t := rt; t.Kind() { + // All basic types are easy: they are predefined. + case reflect.Bool: + return tBool.gobType(), nil + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return tInt.gobType(), nil + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return tUint.gobType(), nil + + case reflect.Float32, reflect.Float64: + return tFloat.gobType(), nil + + case reflect.Complex64, reflect.Complex128: + return tComplex.gobType(), nil + + case reflect.String: + return tString.gobType(), nil + + case reflect.Interface: + return tInterface.gobType(), nil + + case reflect.Array: + at := newArrayType(name) + types[rt] = at + type0, err = getBaseType("", t.Elem()) + if err != nil { + return nil, err + } + // Historical aside: + // For arrays, maps, and slices, we set the type id after the elements + // are constructed. This is to retain the order of type id allocation after + // a fix made to handle recursive types, which changed the order in + // which types are built. Delaying the setting in this way preserves + // type ids while allowing recursive types to be described. Structs, + // done below, were already handling recursion correctly so they + // assign the top-level id before those of the field. + at.init(type0, t.Len()) + return at, nil + + case reflect.Map: + mt := newMapType(name) + types[rt] = mt + type0, err = getBaseType("", t.Key()) + if err != nil { + return nil, err + } + type1, err = getBaseType("", t.Elem()) + if err != nil { + return nil, err + } + mt.init(type0, type1) + return mt, nil + + case reflect.Slice: + // []byte == []uint8 is a special case + if t.Elem().Kind() == reflect.Uint8 { + return tBytes.gobType(), nil + } + st := newSliceType(name) + types[rt] = st + type0, err = getBaseType(t.Elem().Name(), t.Elem()) + if err != nil { + return nil, err + } + st.init(type0) + return st, nil + + case reflect.Struct: + st := newStructType(name) + types[rt] = st + idToType[st.id()] = st + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if !isExported(f.Name) { + continue + } + typ := userType(f.Type).base + tname := typ.Name() + if tname == "" { + t := userType(f.Type).base + tname = t.String() + } + gt, err := getBaseType(tname, f.Type) + if err != nil { + return nil, err + } + // Some mutually recursive types can cause us to be here while + // still defining the element. Fix the element type id here. + // We could do this more neatly by setting the id at the start of + // building every type, but that would break binary compatibility. + if gt.id() == 0 { + setTypeId(gt) + } + st.Field = append(st.Field, &fieldType{f.Name, gt.id()}) + } + return st, nil + + default: + return nil, errors.New("gob NewTypeObject can't handle type: " + rt.String()) + } + return nil, nil +} + +// isExported reports whether this is an exported - upper case - name. +func isExported(name string) bool { + rune, _ := utf8.DecodeRuneInString(name) + return unicode.IsUpper(rune) +} + +// getBaseType returns the Gob type describing the given reflect.Type's base type. +// typeLock must be held. +func getBaseType(name string, rt reflect.Type) (gobType, error) { + ut := userType(rt) + return getType(name, ut, ut.base) +} + +// getType returns the Gob type describing the given reflect.Type. +// Should be called only when handling GobEncoders/Decoders, +// which may be pointers. All other types are handled through the +// base type, never a pointer. +// typeLock must be held. +func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, error) { + typ, present := types[rt] + if present { + return typ, nil + } + typ, err := newTypeObject(name, ut, rt) + if err == nil { + types[rt] = typ + } + return typ, err +} + +func checkId(want, got typeId) { + if want != got { + fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(got), int(want)) + panic("bootstrap type wrong id: " + got.name() + " " + got.string() + " not " + want.string()) + } +} + +// used for building the basic types; called only from init(). the incoming +// interface always refers to a pointer. +func bootstrapType(name string, e interface{}, expect typeId) typeId { + rt := reflect.TypeOf(e).Elem() + _, present := types[rt] + if present { + panic("bootstrap type already present: " + name + ", " + rt.String()) + } + typ := &CommonType{Name: name} + types[rt] = typ + setTypeId(typ) + checkId(expect, nextId) + userType(rt) // might as well cache it now + return nextId +} + +// Representation of the information we send and receive about this type. +// Each value we send is preceded by its type definition: an encoded int. +// However, the very first time we send the value, we first send the pair +// (-id, wireType). +// For bootstrapping purposes, we assume that the recipient knows how +// to decode a wireType; it is exactly the wireType struct here, interpreted +// using the gob rules for sending a structure, except that we assume the +// ids for wireType and structType etc. are known. The relevant pieces +// are built in encode.go's init() function. +// To maintain binary compatibility, if you extend this type, always put +// the new fields last. +type wireType struct { + ArrayT *arrayType + SliceT *sliceType + StructT *structType + MapT *mapType + GobEncoderT *gobEncoderType +} + +func (w *wireType) string() string { + const unknown = "unknown type" + if w == nil { + return unknown + } + switch { + case w.ArrayT != nil: + return w.ArrayT.Name + case w.SliceT != nil: + return w.SliceT.Name + case w.StructT != nil: + return w.StructT.Name + case w.MapT != nil: + return w.MapT.Name + case w.GobEncoderT != nil: + return w.GobEncoderT.Name + } + return unknown +} + +type typeInfo struct { + id typeId + encoder *encEngine + wire *wireType +} + +var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock + +// typeLock must be held. +func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) { + rt := ut.base + if ut.isGobEncoder { + // We want the user type, not the base type. + rt = ut.user + } + info, ok := typeInfoMap[rt] + if ok { + return info, nil + } + info = new(typeInfo) + gt, err := getBaseType(rt.Name(), rt) + if err != nil { + return nil, err + } + info.id = gt.id() + + if ut.isGobEncoder { + userType, err := getType(rt.Name(), ut, rt) + if err != nil { + return nil, err + } + info.wire = &wireType{GobEncoderT: userType.id().gobType().(*gobEncoderType)} + typeInfoMap[ut.user] = info + return info, nil + } + + t := info.id.gobType() + switch typ := rt; typ.Kind() { + case reflect.Array: + info.wire = &wireType{ArrayT: t.(*arrayType)} + case reflect.Map: + info.wire = &wireType{MapT: t.(*mapType)} + case reflect.Slice: + // []byte == []uint8 is a special case handled separately + if typ.Elem().Kind() != reflect.Uint8 { + info.wire = &wireType{SliceT: t.(*sliceType)} + } + case reflect.Struct: + info.wire = &wireType{StructT: t.(*structType)} + } + typeInfoMap[rt] = info + return info, nil +} + +// Called only when a panic is acceptable and unexpected. +func mustGetTypeInfo(rt reflect.Type) *typeInfo { + t, err := getTypeInfo(userType(rt)) + if err != nil { + panic("getTypeInfo: " + err.Error()) + } + return t +} + +// GobEncoder is the interface describing data that provides its own +// representation for encoding values for transmission to a GobDecoder. +// A type that implements GobEncoder and GobDecoder has complete +// control over the representation of its data and may therefore +// contain things such as private fields, channels, and functions, +// which are not usually transmissible in gob streams. +// +// Note: Since gobs can be stored permanently, It is good design +// to guarantee the encoding used by a GobEncoder is stable as the +// software evolves. For instance, it might make sense for GobEncode +// to include a version number in the encoding. +type GobEncoder interface { + // GobEncode returns a byte slice representing the encoding of the + // receiver for transmission to a GobDecoder, usually of the same + // concrete type. + GobEncode() ([]byte, error) +} + +// GobDecoder is the interface describing data that provides its own +// routine for decoding transmitted values sent by a GobEncoder. +type GobDecoder interface { + // GobDecode overwrites the receiver, which must be a pointer, + // with the value represented by the byte slice, which was written + // by GobEncode, usually for the same concrete type. + GobDecode([]byte) error +} + +var ( + nameToConcreteType = make(map[string]reflect.Type) + concreteTypeToName = make(map[reflect.Type]string) +) + +// RegisterName is like Register but uses the provided name rather than the +// type's default. +func RegisterName(name string, value interface{}) { + if name == "" { + // reserved for nil + panic("attempt to register empty name") + } + ut := userType(reflect.TypeOf(value)) + // Check for incompatible duplicates. The name must refer to the + // same user type, and vice versa. + if t, ok := nameToConcreteType[name]; ok && t != ut.user { + panic(fmt.Sprintf("gob: registering duplicate types for %q: %s != %s", name, t, ut.user)) + } + if n, ok := concreteTypeToName[ut.base]; ok && n != name { + panic(fmt.Sprintf("gob: registering duplicate names for %s: %q != %q", ut.user, n, name)) + } + // Store the name and type provided by the user.... + nameToConcreteType[name] = reflect.TypeOf(value) + // but the flattened type in the type table, since that's what decode needs. + concreteTypeToName[ut.base] = name +} + +// Register records a type, identified by a value for that type, under its +// internal type name. That name will identify the concrete type of a value +// sent or received as an interface variable. Only types that will be +// transferred as implementations of interface values need to be registered. +// Expecting to be used only during initialization, it panics if the mapping +// between types and names is not a bijection. +func Register(value interface{}) { + // Default to printed representation for unnamed types + rt := reflect.TypeOf(value) + name := rt.String() + + // But for named types (or pointers to them), qualify with import path. + // Dereference one pointer looking for a named type. + star := "" + if rt.Name() == "" { + if pt := rt; pt.Kind() == reflect.Ptr { + star = "*" + rt = pt + } + } + if rt.Name() != "" { + if rt.PkgPath() == "" { + name = star + rt.Name() + } else { + name = star + rt.PkgPath() + "." + rt.Name() + } + } + + RegisterName(name, value) +} + +func registerBasics() { + Register(int(0)) + Register(int8(0)) + Register(int16(0)) + Register(int32(0)) + Register(int64(0)) + Register(uint(0)) + Register(uint8(0)) + Register(uint16(0)) + Register(uint32(0)) + Register(uint64(0)) + Register(float32(0)) + Register(float64(0)) + Register(complex64(0i)) + Register(complex128(0i)) + Register(uintptr(0)) + Register(false) + Register("") + Register([]byte(nil)) + Register([]int(nil)) + Register([]int8(nil)) + Register([]int16(nil)) + Register([]int32(nil)) + Register([]int64(nil)) + Register([]uint(nil)) + Register([]uint8(nil)) + Register([]uint16(nil)) + Register([]uint32(nil)) + Register([]uint64(nil)) + Register([]float32(nil)) + Register([]float64(nil)) + Register([]complex64(nil)) + Register([]complex128(nil)) + Register([]uintptr(nil)) + Register([]bool(nil)) + Register([]string(nil)) +} diff --git a/src/pkg/encoding/gob/type_test.go b/src/pkg/encoding/gob/type_test.go new file mode 100644 index 000000000..42bdb4cf7 --- /dev/null +++ b/src/pkg/encoding/gob/type_test.go @@ -0,0 +1,161 @@ +// 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 gob + +import ( + "reflect" + "testing" +) + +type typeT struct { + id typeId + str string +} + +var basicTypes = []typeT{ + {tBool, "bool"}, + {tInt, "int"}, + {tUint, "uint"}, + {tFloat, "float"}, + {tBytes, "bytes"}, + {tString, "string"}, +} + +func getTypeUnlocked(name string, rt reflect.Type) gobType { + typeLock.Lock() + defer typeLock.Unlock() + t, err := getBaseType(name, rt) + if err != nil { + panic("getTypeUnlocked: " + err.Error()) + } + return t +} + +// Sanity checks +func TestBasic(t *testing.T) { + for _, tt := range basicTypes { + if tt.id.string() != tt.str { + t.Errorf("checkType: expected %q got %s", tt.str, tt.id.string()) + } + if tt.id == 0 { + t.Errorf("id for %q is zero", tt.str) + } + } +} + +// Reregister some basic types to check registration is idempotent. +func TestReregistration(t *testing.T) { + newtyp := getTypeUnlocked("int", reflect.TypeOf(int(0))) + if newtyp != tInt.gobType() { + t.Errorf("reregistration of %s got new type", newtyp.string()) + } + newtyp = getTypeUnlocked("uint", reflect.TypeOf(uint(0))) + if newtyp != tUint.gobType() { + t.Errorf("reregistration of %s got new type", newtyp.string()) + } + newtyp = getTypeUnlocked("string", reflect.TypeOf("hello")) + if newtyp != tString.gobType() { + t.Errorf("reregistration of %s got new type", newtyp.string()) + } +} + +func TestArrayType(t *testing.T) { + var a3 [3]int + a3int := getTypeUnlocked("foo", reflect.TypeOf(a3)) + newa3int := getTypeUnlocked("bar", reflect.TypeOf(a3)) + if a3int != newa3int { + t.Errorf("second registration of [3]int creates new type") + } + var a4 [4]int + a4int := getTypeUnlocked("goo", reflect.TypeOf(a4)) + if a3int == a4int { + t.Errorf("registration of [3]int creates same type as [4]int") + } + var b3 [3]bool + a3bool := getTypeUnlocked("", reflect.TypeOf(b3)) + if a3int == a3bool { + t.Errorf("registration of [3]bool creates same type as [3]int") + } + str := a3bool.string() + expected := "[3]bool" + if str != expected { + t.Errorf("array printed as %q; expected %q", str, expected) + } +} + +func TestSliceType(t *testing.T) { + var s []int + sint := getTypeUnlocked("slice", reflect.TypeOf(s)) + var news []int + newsint := getTypeUnlocked("slice1", reflect.TypeOf(news)) + if sint != newsint { + t.Errorf("second registration of []int creates new type") + } + var b []bool + sbool := getTypeUnlocked("", reflect.TypeOf(b)) + if sbool == sint { + t.Errorf("registration of []bool creates same type as []int") + } + str := sbool.string() + expected := "[]bool" + if str != expected { + t.Errorf("slice printed as %q; expected %q", str, expected) + } +} + +func TestMapType(t *testing.T) { + var m map[string]int + mapStringInt := getTypeUnlocked("map", reflect.TypeOf(m)) + var newm map[string]int + newMapStringInt := getTypeUnlocked("map1", reflect.TypeOf(newm)) + if mapStringInt != newMapStringInt { + t.Errorf("second registration of map[string]int creates new type") + } + var b map[string]bool + mapStringBool := getTypeUnlocked("", reflect.TypeOf(b)) + if mapStringBool == mapStringInt { + t.Errorf("registration of map[string]bool creates same type as map[string]int") + } + str := mapStringBool.string() + expected := "map[string]bool" + if str != expected { + t.Errorf("map printed as %q; expected %q", str, expected) + } +} + +type Bar struct { + X string +} + +// This structure has pointers and refers to itself, making it a good test case. +type Foo struct { + A int + B int32 // will become int + C string + D []byte + E *float64 // will become float64 + F ****float64 // will become float64 + G *Bar + H *Bar // should not interpolate the definition of Bar again + I *Foo // will not explode +} + +func TestStructType(t *testing.T) { + sstruct := getTypeUnlocked("Foo", reflect.TypeOf(Foo{})) + str := sstruct.string() + // If we can print it correctly, we built it correctly. + expected := "Foo = struct { A int; B int; C string; D bytes; E float; F float; G Bar = struct { X string; }; H Bar; I Foo; }" + if str != expected { + t.Errorf("struct printed as %q; expected %q", str, expected) + } +} + +// Should be OK to register the same type multiple times, as long as they're +// at the same level of indirection. +func TestRegistration(t *testing.T) { + type T struct{ a int } + Register(new(T)) + Register(new(T)) +} diff --git a/src/pkg/encoding/hex/Makefile b/src/pkg/encoding/hex/Makefile deleted file mode 100644 index 22049f43b..000000000 --- a/src/pkg/encoding/hex/Makefile +++ /dev/null @@ -1,11 +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 ../../../Make.inc - -TARG=encoding/hex -GOFILES=\ - hex.go\ - -include ../../../Make.pkg diff --git a/src/pkg/encoding/hex/hex.go b/src/pkg/encoding/hex/hex.go index e7ea8b0f2..167d00e03 100644 --- a/src/pkg/encoding/hex/hex.go +++ b/src/pkg/encoding/hex/hex.go @@ -7,9 +7,9 @@ package hex import ( "bytes" + "errors" + "fmt" "io" - "os" - "strconv" ) const hextable = "0123456789abcdef" @@ -30,16 +30,14 @@ func Encode(dst, src []byte) int { return len(src) * 2 } -// OddLengthInputError results from decoding an odd length slice. -type OddLengthInputError struct{} +// ErrLength results from decoding an odd length slice. +var ErrLength = errors.New("encoding/hex: odd length hex string") -func (OddLengthInputError) String() string { return "odd length hex string" } +// InvalidByteError values describe errors resulting from an invalid byte in a hex string. +type InvalidByteError byte -// InvalidHexCharError results from finding an invalid character in a hex string. -type InvalidHexCharError byte - -func (e InvalidHexCharError) String() string { - return "invalid hex char: " + strconv.Itoa(int(e)) +func (e InvalidByteError) Error() string { + return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e)) } func DecodedLen(x int) int { return x / 2 } @@ -47,21 +45,20 @@ func DecodedLen(x int) int { return x / 2 } // Decode decodes src into DecodedLen(len(src)) bytes, returning the actual // number of bytes written to dst. // -// If Decode encounters invalid input, it returns an OddLengthInputError or an -// InvalidHexCharError. -func Decode(dst, src []byte) (int, os.Error) { +// If Decode encounters invalid input, it returns an error describing the failure. +func Decode(dst, src []byte) (int, error) { if len(src)%2 == 1 { - return 0, OddLengthInputError{} + return 0, ErrLength } for i := 0; i < len(src)/2; i++ { a, ok := fromHexChar(src[i*2]) if !ok { - return 0, InvalidHexCharError(src[i*2]) + return 0, InvalidByteError(src[i*2]) } b, ok := fromHexChar(src[i*2+1]) if !ok { - return 0, InvalidHexCharError(src[i*2+1]) + return 0, InvalidByteError(src[i*2+1]) } dst[i] = (a << 4) | b } @@ -91,7 +88,7 @@ func EncodeToString(src []byte) string { } // DecodeString returns the bytes represented by the hexadecimal string s. -func DecodeString(s string) ([]byte, os.Error) { +func DecodeString(s string) ([]byte, error) { src := []byte(s) dst := make([]byte, DecodedLen(len(src))) _, err := Decode(dst, src) @@ -104,8 +101,8 @@ func DecodeString(s string) ([]byte, os.Error) { // Dump returns a string that contains a hex dump of the given data. The format // of the hex dump matches the output of `hexdump -C` on the command line. func Dump(data []byte) string { - buf := bytes.NewBuffer(nil) - dumper := Dumper(buf) + var buf bytes.Buffer + dumper := Dumper(&buf) dumper.Write(data) dumper.Close() return string(buf.Bytes()) @@ -133,7 +130,7 @@ func toChar(b byte) byte { return b } -func (h *dumper) Write(data []byte) (n int, err os.Error) { +func (h *dumper) Write(data []byte) (n int, err error) { // Output lines look like: // 00000010 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d |./0123456789:;<=| // ^ offset ^ extra space ^ ASCII of line. @@ -185,7 +182,7 @@ func (h *dumper) Write(data []byte) (n int, err os.Error) { return } -func (h *dumper) Close() (err os.Error) { +func (h *dumper) Close() (err error) { // See the comments in Write() for the details of this format. if h.used == 0 { return diff --git a/src/pkg/encoding/hex/hex_test.go b/src/pkg/encoding/hex/hex_test.go index 8e1838e51..456f9eac7 100644 --- a/src/pkg/encoding/hex/hex_test.go +++ b/src/pkg/encoding/hex/hex_test.go @@ -9,141 +9,98 @@ import ( "testing" ) -type encodeTest struct { - in, out []byte +type encDecTest struct { + enc string + dec []byte } -var encodeTests = []encodeTest{ - {[]byte{}, []byte{}}, - {[]byte{0x01}, []byte{'0', '1'}}, - {[]byte{0xff}, []byte{'f', 'f'}}, - {[]byte{0xff, 00}, []byte{'f', 'f', '0', '0'}}, - {[]byte{0}, []byte{'0', '0'}}, - {[]byte{1}, []byte{'0', '1'}}, - {[]byte{2}, []byte{'0', '2'}}, - {[]byte{3}, []byte{'0', '3'}}, - {[]byte{4}, []byte{'0', '4'}}, - {[]byte{5}, []byte{'0', '5'}}, - {[]byte{6}, []byte{'0', '6'}}, - {[]byte{7}, []byte{'0', '7'}}, - {[]byte{8}, []byte{'0', '8'}}, - {[]byte{9}, []byte{'0', '9'}}, - {[]byte{10}, []byte{'0', 'a'}}, - {[]byte{11}, []byte{'0', 'b'}}, - {[]byte{12}, []byte{'0', 'c'}}, - {[]byte{13}, []byte{'0', 'd'}}, - {[]byte{14}, []byte{'0', 'e'}}, - {[]byte{15}, []byte{'0', 'f'}}, +var encDecTests = []encDecTest{ + {"", []byte{}}, + {"0001020304050607", []byte{0, 1, 2, 3, 4, 5, 6, 7}}, + {"08090a0b0c0d0e0f", []byte{8, 9, 10, 11, 12, 13, 14, 15}}, + {"f0f1f2f3f4f5f6f7", []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7}}, + {"f8f9fafbfcfdfeff", []byte{0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}}, + {"67", []byte{'g'}}, + {"e3a1", []byte{0xe3, 0xa1}}, } func TestEncode(t *testing.T) { - for i, test := range encodeTests { - dst := make([]byte, EncodedLen(len(test.in))) - n := Encode(dst, test.in) + for i, test := range encDecTests { + dst := make([]byte, EncodedLen(len(test.dec))) + n := Encode(dst, test.dec) if n != len(dst) { t.Errorf("#%d: bad return value: got: %d want: %d", i, n, len(dst)) } - if bytes.Compare(dst, test.out) != 0 { - t.Errorf("#%d: got: %#v want: %#v", i, dst, test.out) + if string(dst) != test.enc { + t.Errorf("#%d: got: %#v want: %#v", i, dst, test.enc) } } } -type decodeTest struct { - in, out []byte - ok bool -} - -var decodeTests = []decodeTest{ - {[]byte{}, []byte{}, true}, - {[]byte{'0'}, []byte{}, false}, - {[]byte{'0', 'g'}, []byte{}, false}, - {[]byte{'0', '\x01'}, []byte{}, false}, - {[]byte{'0', '0'}, []byte{0}, true}, - {[]byte{'0', '1'}, []byte{1}, true}, - {[]byte{'0', '2'}, []byte{2}, true}, - {[]byte{'0', '3'}, []byte{3}, true}, - {[]byte{'0', '4'}, []byte{4}, true}, - {[]byte{'0', '5'}, []byte{5}, true}, - {[]byte{'0', '6'}, []byte{6}, true}, - {[]byte{'0', '7'}, []byte{7}, true}, - {[]byte{'0', '8'}, []byte{8}, true}, - {[]byte{'0', '9'}, []byte{9}, true}, - {[]byte{'0', 'a'}, []byte{10}, true}, - {[]byte{'0', 'b'}, []byte{11}, true}, - {[]byte{'0', 'c'}, []byte{12}, true}, - {[]byte{'0', 'd'}, []byte{13}, true}, - {[]byte{'0', 'e'}, []byte{14}, true}, - {[]byte{'0', 'f'}, []byte{15}, true}, - {[]byte{'0', 'A'}, []byte{10}, true}, - {[]byte{'0', 'B'}, []byte{11}, true}, - {[]byte{'0', 'C'}, []byte{12}, true}, - {[]byte{'0', 'D'}, []byte{13}, true}, - {[]byte{'0', 'E'}, []byte{14}, true}, - {[]byte{'0', 'F'}, []byte{15}, true}, -} - func TestDecode(t *testing.T) { - for i, test := range decodeTests { - dst := make([]byte, DecodedLen(len(test.in))) - n, err := Decode(dst, test.in) - if err == nil && n != len(dst) { + for i, test := range encDecTests { + dst := make([]byte, DecodedLen(len(test.enc))) + n, err := Decode(dst, []byte(test.enc)) + if err != nil { t.Errorf("#%d: bad return value: got:%d want:%d", i, n, len(dst)) - } - if test.ok != (err == nil) { - t.Errorf("#%d: unexpected err value: %s", i, err) - } - if err == nil && bytes.Compare(dst, test.out) != 0 { - t.Errorf("#%d: got: %#v want: %#v", i, dst, test.out) + } else if !bytes.Equal(dst, test.dec) { + t.Errorf("#%d: got: %#v want: %#v", i, dst, test.dec) } } } -type encodeStringTest struct { - in []byte - out string -} - -var encodeStringTests = []encodeStringTest{ - {[]byte{}, ""}, - {[]byte{0}, "00"}, - {[]byte{0, 1}, "0001"}, - {[]byte{0, 1, 255}, "0001ff"}, +func TestEncodeToString(t *testing.T) { + for i, test := range encDecTests { + s := EncodeToString(test.dec) + if s != test.enc { + t.Errorf("#%d got:%s want:%s", i, s, test.enc) + } + } } -func TestEncodeToString(t *testing.T) { - for i, test := range encodeStringTests { - s := EncodeToString(test.in) - if s != test.out { - t.Errorf("#%d got:%s want:%s", i, s, test.out) +func TestDecodeString(t *testing.T) { + for i, test := range encDecTests { + dst, err := DecodeString(test.enc) + if err != nil { + t.Errorf("#%d: unexpected err value: %s", i, err) + continue + } + if bytes.Compare(dst, test.dec) != 0 { + t.Errorf("#%d: got: %#v want: #%v", i, dst, test.dec) } } } -type decodeStringTest struct { +type errTest struct { in string - out []byte - ok bool + err string } -var decodeStringTests = []decodeStringTest{ - {"", []byte{}, true}, - {"0", []byte{}, false}, - {"00", []byte{0}, true}, - {"0\x01", []byte{}, false}, - {"0g", []byte{}, false}, - {"00ff00", []byte{0, 255, 0}, true}, - {"0000ff", []byte{0, 0, 255}, true}, +var errTests = []errTest{ + {"0", "encoding/hex: odd length hex string"}, + {"0g", "encoding/hex: invalid byte: U+0067 'g'"}, + {"0\x01", "encoding/hex: invalid byte: U+0001"}, } -func TestDecodeString(t *testing.T) { - for i, test := range decodeStringTests { - dst, err := DecodeString(test.in) - if test.ok != (err == nil) { - t.Errorf("#%d: unexpected err value: %s", i, err) +func TestInvalidErr(t *testing.T) { + for i, test := range errTests { + dst := make([]byte, DecodedLen(len(test.in))) + _, err := Decode(dst, []byte(test.in)) + if err == nil { + t.Errorf("#%d: expected error; got none", i) + } else if err.Error() != test.err { + t.Errorf("#%d: got: %v want: %v", i, err, test.err) } - if err == nil && bytes.Compare(dst, test.out) != 0 { - t.Errorf("#%d: got: %#v want: #%v", i, dst, test.out) + } +} + +func TestInvalidStringErr(t *testing.T) { + for i, test := range errTests { + _, err := DecodeString(test.in) + if err == nil { + t.Errorf("#%d: expected error; got none", i) + } else if err.Error() != test.err { + t.Errorf("#%d: got: %v want: %v", i, err, test.err) } } } @@ -155,8 +112,8 @@ func TestDumper(t *testing.T) { } for stride := 1; stride < len(in); stride++ { - out := bytes.NewBuffer(nil) - dumper := Dumper(out) + var out bytes.Buffer + dumper := Dumper(&out) done := 0 for done < len(in) { todo := done + stride diff --git a/src/pkg/encoding/json/bench_test.go b/src/pkg/encoding/json/bench_test.go new file mode 100644 index 000000000..333c1c0ce --- /dev/null +++ b/src/pkg/encoding/json/bench_test.go @@ -0,0 +1,157 @@ +// 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. + +// Large data benchmark. +// The JSON data is a summary of agl's changes in the +// go, webkit, and chromium open source projects. +// We benchmark converting between the JSON form +// and in-memory data structures. + +package json + +import ( + "bytes" + "compress/gzip" + "io/ioutil" + "os" + "testing" +) + +type codeResponse struct { + Tree *codeNode `json:"tree"` + Username string `json:"username"` +} + +type codeNode struct { + Name string `json:"name"` + Kids []*codeNode `json:"kids"` + CLWeight float64 `json:"cl_weight"` + Touches int `json:"touches"` + MinT int64 `json:"min_t"` + MaxT int64 `json:"max_t"` + MeanT int64 `json:"mean_t"` +} + +var codeJSON []byte +var codeStruct codeResponse + +func codeInit() { + f, err := os.Open("testdata/code.json.gz") + if err != nil { + panic(err) + } + defer f.Close() + gz, err := gzip.NewReader(f) + if err != nil { + panic(err) + } + data, err := ioutil.ReadAll(gz) + if err != nil { + panic(err) + } + + codeJSON = data + + if err := Unmarshal(codeJSON, &codeStruct); err != nil { + panic("unmarshal code.json: " + err.Error()) + } + + if data, err = Marshal(&codeStruct); err != nil { + panic("marshal code.json: " + err.Error()) + } + + if !bytes.Equal(data, codeJSON) { + println("different lengths", len(data), len(codeJSON)) + for i := 0; i < len(data) && i < len(codeJSON); i++ { + if data[i] != codeJSON[i] { + println("re-marshal: changed at byte", i) + println("orig: ", string(codeJSON[i-10:i+10])) + println("new: ", string(data[i-10:i+10])) + break + } + } + panic("re-marshal code.json: different result") + } +} + +func BenchmarkCodeEncoder(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + enc := NewEncoder(ioutil.Discard) + for i := 0; i < b.N; i++ { + if err := enc.Encode(&codeStruct); err != nil { + b.Fatal("Encode:", err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeMarshal(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + for i := 0; i < b.N; i++ { + if _, err := Marshal(&codeStruct); err != nil { + b.Fatal("Marshal:", err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeDecoder(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + var buf bytes.Buffer + dec := NewDecoder(&buf) + var r codeResponse + for i := 0; i < b.N; i++ { + buf.Write(codeJSON) + // hide EOF + buf.WriteByte('\n') + buf.WriteByte('\n') + buf.WriteByte('\n') + if err := dec.Decode(&r); err != nil { + b.Fatal("Decode:", err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeUnmarshal(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + for i := 0; i < b.N; i++ { + var r codeResponse + if err := Unmarshal(codeJSON, &r); err != nil { + b.Fatal("Unmmarshal:", err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeUnmarshalReuse(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + var r codeResponse + for i := 0; i < b.N; i++ { + if err := Unmarshal(codeJSON, &r); err != nil { + b.Fatal("Unmmarshal:", err) + } + } + b.SetBytes(int64(len(codeJSON))) +} diff --git a/src/pkg/encoding/json/decode.go b/src/pkg/encoding/json/decode.go new file mode 100644 index 000000000..110c6fd62 --- /dev/null +++ b/src/pkg/encoding/json/decode.go @@ -0,0 +1,979 @@ +// 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. + +// Represents JSON data structure using native Go types: booleans, floats, +// strings, arrays, and maps. + +package json + +import ( + "encoding/base64" + "errors" + "fmt" + "reflect" + "runtime" + "strconv" + "strings" + "unicode" + "unicode/utf16" + "unicode/utf8" +) + +// Unmarshal parses the JSON-encoded data and stores the result +// in the value pointed to by v. +// +// Unmarshal uses the inverse of the encodings that +// Marshal uses, allocating maps, slices, and pointers as necessary, +// with the following additional rules: +// +// To unmarshal JSON into a pointer, Unmarshal first handles the case of +// the JSON being the JSON literal null. In that case, Unmarshal sets +// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into +// 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 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, +// Unmarshal stores one of these in the interface value: +// +// bool, for JSON booleans +// float64, for JSON numbers +// string, for JSON strings +// []interface{}, for JSON arrays +// map[string]interface{}, for JSON objects +// nil for JSON null +// +// If a JSON value is not appropriate for a given target type, +// or if a JSON number overflows the target type, Unmarshal +// skips that field and completes the unmarshalling as best it can. +// If no more serious errors are encountered, Unmarshal returns +// an UnmarshalTypeError describing the earliest such error. +// +func Unmarshal(data []byte, v interface{}) error { + d := new(decodeState).init(data) + + // Quick check for well-formedness. + // Avoids filling out half a data structure + // before discovering a JSON syntax error. + err := checkValid(data, &d.scan) + if err != nil { + return err + } + + 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 +// if it wishes to retain the data after returning. +type Unmarshaler interface { + UnmarshalJSON([]byte) error +} + +// An UnmarshalTypeError describes a JSON value that was +// not appropriate for a value of a specific Go type. +type UnmarshalTypeError struct { + Value string // description of JSON value - "bool", "array", "number -5" + Type reflect.Type // type of Go value it could not be assigned to +} + +func (e *UnmarshalTypeError) Error() string { + return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() +} + +// An UnmarshalFieldError describes a JSON object key that +// led to an unexported (and therefore unwritable) struct field. +type UnmarshalFieldError struct { + Key string + Type reflect.Type + Field reflect.StructField +} + +func (e *UnmarshalFieldError) Error() string { + return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() +} + +// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. +// (The argument to Unmarshal must be a non-nil pointer.) +type InvalidUnmarshalError struct { + Type reflect.Type +} + +func (e *InvalidUnmarshalError) Error() string { + if e.Type == nil { + return "json: Unmarshal(nil)" + } + + if e.Type.Kind() != reflect.Ptr { + return "json: Unmarshal(non-pointer " + e.Type.String() + ")" + } + return "json: Unmarshal(nil " + e.Type.String() + ")" +} + +func (d *decodeState) unmarshal(v interface{}) (err error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + err = r.(error) + } + }() + + rv := reflect.ValueOf(v) + pv := rv + if pv.Kind() != reflect.Ptr || pv.IsNil() { + return &InvalidUnmarshalError{reflect.TypeOf(v)} + } + + d.scan.reset() + // We decode rv not pv.Elem because the Unmarshaler interface + // test must be applied at the top level of the value. + d.value(rv) + return d.savedError +} + +// decodeState represents the state while decoding a JSON value. +type decodeState struct { + data []byte + off int // read offset in data + scan scanner + nextscan scanner // for calls to nextValue + savedError error + tempstr string // scratch space to avoid some allocations +} + +// errPhase is used for errors that should not happen unless +// there is a bug in the JSON decoder or something is editing +// the data slice while the decoder executes. +var errPhase = errors.New("JSON decoder out of sync - data changing underfoot?") + +func (d *decodeState) init(data []byte) *decodeState { + d.data = data + d.off = 0 + d.savedError = nil + return d +} + +// error aborts the decoding by panicking with err. +func (d *decodeState) error(err error) { + panic(err) +} + +// saveError saves the first err it is called with, +// for reporting at the end of the unmarshal. +func (d *decodeState) saveError(err error) { + if d.savedError == nil { + d.savedError = err + } +} + +// next cuts off and returns the next full JSON value in d.data[d.off:]. +// The next value is known to be an object or array, not a literal. +func (d *decodeState) next() []byte { + c := d.data[d.off] + item, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // Our scanner has seen the opening brace/bracket + // and thinks we're still in the middle of the object. + // invent a closing brace/bracket to get it out. + if c == '{' { + d.scan.step(&d.scan, '}') + } else { + d.scan.step(&d.scan, ']') + } + + return item +} + +// scanWhile processes bytes in d.data[d.off:] until it +// receives a scan code not equal to op. +// It updates d.off and returns the new scan code. +func (d *decodeState) scanWhile(op int) int { + var newOp int + for { + if d.off >= len(d.data) { + newOp = d.scan.eof() + d.off = len(d.data) + 1 // mark processed EOF with len+1 + } else { + c := int(d.data[d.off]) + d.off++ + newOp = d.scan.step(&d.scan, c) + } + if newOp != op { + break + } + } + return newOp +} + +// value decodes a JSON value from d.data[d.off:] into the value. +// it updates d.off to point past the decoded value. +func (d *decodeState) value(v reflect.Value) { + if !v.IsValid() { + _, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // d.scan thinks we're still at the beginning of the item. + // Feed in an empty string - the shortest, simplest value - + // so that it knows we got to the end of the value. + if d.scan.redo { + // rewind. + d.scan.redo = false + d.scan.step = stateBeginValue + } + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + return + } + + switch op := d.scanWhile(scanSkipSpace); op { + default: + d.error(errPhase) + + case scanBeginArray: + d.array(v) + + case scanBeginObject: + d.object(v) + + case scanBeginLiteral: + d.literal(v) + } +} + +// indirect walks down v allocating pointers as needed, +// until it gets to a non-pointer. +// if it encounters an Unmarshaler, indirect stops and returns that. +// if decodingNull is true, indirect stops at the last pointer so it can be set to nil. +func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) { + // If v is a named type and is addressable, + // start with its address, so that if the type has pointer methods, + // we find them. + if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { + 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) + } + + if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() { + v = iv.Elem() + continue + } + + pv := v + if pv.Kind() != reflect.Ptr { + break + } + + if pv.Elem().Kind() != reflect.Ptr && decodingNull && pv.CanSet() { + return nil, pv + } + if pv.IsNil() { + pv.Set(reflect.New(pv.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{} + } + v = pv.Elem() + } + return nil, v +} + +// array consumes an array from d.data[d.off-1:], decoding into the value v. +// the first byte of the array ('[') has been read already. +func (d *decodeState) array(v reflect.Value) { + // Check for unmarshaler. + unmarshaler, pv := d.indirect(v, false) + if unmarshaler != nil { + d.off-- + err := unmarshaler.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + v = pv + + // Check type of target. + switch v.Kind() { + 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 + } + + i := 0 + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + // Get element of array, growing if necessary. + if v.Kind() == reflect.Slice { + // Grow slice if necessary + if i >= v.Cap() { + newcap := v.Cap() + v.Cap()/2 + if newcap < 4 { + newcap = 4 + } + newv := reflect.MakeSlice(v.Type(), v.Len(), newcap) + reflect.Copy(newv, v) + v.Set(newv) + } + if i >= v.Len() { + v.SetLen(i + 1) + } + } + + if i < v.Len() { + // Decode into element. + d.value(v.Index(i)) + } else { + // Ran out of fixed array: skip. + d.value(reflect.Value{}) + } + i++ + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + + if i < v.Len() { + if v.Kind() == reflect.Array { + // Array. Zero the rest. + z := reflect.Zero(v.Type().Elem()) + for ; i < v.Len(); i++ { + v.Index(i).Set(z) + } + } else { + v.SetLen(i) + } + } + if i == 0 && v.Kind() == reflect.Slice { + v.Set(reflect.MakeSlice(v.Type(), 0, 0)) + } +} + +// object consumes an object from d.data[d.off-1:], decoding into the value v. +// the first byte of the object ('{') has been read already. +func (d *decodeState) object(v reflect.Value) { + // Check for unmarshaler. + unmarshaler, pv := d.indirect(v, false) + if unmarshaler != nil { + d.off-- + err := unmarshaler.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + iv := v + if iv.Kind() == reflect.Interface { + iv.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 + t := v.Type() + if t.Key() != reflect.TypeOf("") { + d.saveError(&UnmarshalTypeError{"object", v.Type()}) + break + } + mv = v + if mv.IsNil() { + mv.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 + } + + var mapElem reflect.Value + + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Figure out field corresponding to key. + 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 !mapElem.IsValid() { + mapElem = reflect.New(elemType).Elem() + } else { + mapElem.Set(reflect.Zero(elemType)) + } + 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 + } + // 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 + } + } + + // Extract value; name must be exported. + if ok { + if f.PkgPath != "" { + d.saveError(&UnmarshalFieldError{key, st, f}) + } else { + subv = sv.FieldByIndex(f.Index) + } + _, opts := parseTag(f.Tag.Get("json")) + destring = opts.Contains("string") + } + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + if destring { + d.value(reflect.ValueOf(&d.tempstr)) + d.literalStore([]byte(d.tempstr), subv, true) + } 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) + } + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } +} + +// literal consumes a literal from d.data[d.off-1:], decoding into the value v. +// The first byte of the literal has been read already +// (that's how the caller knows it's a literal). +func (d *decodeState) literal(v reflect.Value) { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + + d.literalStore(d.data[start:d.off], v, false) +} + +// literalStore decodes a literal stored in item into v. +// +// fromQuoted indicates whether this literal came from unwrapping a +// string from the ",string" struct tag option. this is used only to +// produce more helpful error messages. +func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) { + // Check for unmarshaler. + wantptr := item[0] == 'n' // null + unmarshaler, pv := d.indirect(v, wantptr) + if unmarshaler != nil { + err := unmarshaler.UnmarshalJSON(item) + if err != nil { + d.error(err) + } + return + } + v = pv + + 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())) + } + + case 't', 'f': // true, false + value := c == 't' + switch v.Kind() { + default: + if fromQuoted { + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.saveError(&UnmarshalTypeError{"bool", v.Type()}) + } + case reflect.Bool: + v.SetBool(value) + case reflect.Interface: + v.Set(reflect.ValueOf(value)) + } + + case '"': // string + s, ok := unquoteBytes(item) + if !ok { + 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(errPhase) + } + } + switch v.Kind() { + default: + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + case reflect.Slice: + if v.Type() != byteSliceType { + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + break + } + b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) + n, err := base64.StdEncoding.Decode(b, s) + if err != nil { + d.saveError(err) + break + } + v.Set(reflect.ValueOf(b[0:n])) + case reflect.String: + v.SetString(string(s)) + case reflect.Interface: + v.Set(reflect.ValueOf(string(s))) + } + + default: // number + if c != '-' && (c < '0' || c > '9') { + 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(errPhase) + } + } + s := string(item) + switch v.Kind() { + default: + 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) + if err != nil { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.Set(reflect.ValueOf(n)) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n, err := strconv.ParseInt(s, 10, 64) + if err != nil || v.OverflowInt(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.SetInt(n) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n, err := strconv.ParseUint(s, 10, 64) + if err != nil || v.OverflowUint(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.SetUint(n) + + case reflect.Float32, reflect.Float64: + n, err := strconv.ParseFloat(s, v.Type().Bits()) + if err != nil || v.OverflowFloat(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.SetFloat(n) + } + } +} + +// The xxxInterface routines build up a value to be stored +// in an empty interface. They are not strictly necessary, +// but they avoid the weight of reflection in this common case. + +// valueInterface is like value but returns interface{} +func (d *decodeState) valueInterface() interface{} { + switch d.scanWhile(scanSkipSpace) { + default: + d.error(errPhase) + case scanBeginArray: + return d.arrayInterface() + case scanBeginObject: + return d.objectInterface() + case scanBeginLiteral: + return d.literalInterface() + } + panic("unreachable") +} + +// arrayInterface is like array but returns []interface{}. +func (d *decodeState) arrayInterface() []interface{} { + var v []interface{} + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + v = append(v, d.valueInterface()) + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + return v +} + +// objectInterface is like object but returns map[string]interface{}. +func (d *decodeState) objectInterface() map[string]interface{} { + m := make(map[string]interface{}) + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + m[key] = d.valueInterface() + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } + return m +} + +// literalInterface is like literal but returns an interface value. +func (d *decodeState) literalInterface() interface{} { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + switch c := item[0]; c { + case 'n': // null + return nil + + case 't', 'f': // true, false + return c == 't' + + case '"': // string + s, ok := unquote(item) + if !ok { + d.error(errPhase) + } + return s + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + n, err := strconv.ParseFloat(string(item), 64) + if err != nil { + d.saveError(&UnmarshalTypeError{"number " + string(item), reflect.TypeOf(0.0)}) + } + return n + } + panic("unreachable") +} + +// getu4 decodes \uXXXX from the beginning of s, returning the hex value, +// or it returns -1. +func getu4(s []byte) rune { + if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { + return -1 + } + r, err := strconv.ParseUint(string(s[2:6]), 16, 64) + if err != nil { + return -1 + } + return rune(r) +} + +// unquote converts a quoted JSON string literal s into an actual string t. +// The rules are different than for Go, so cannot use strconv.Unquote. +func unquote(s []byte) (t string, ok bool) { + s, ok = unquoteBytes(s) + t = string(s) + return +} + +func unquoteBytes(s []byte) (t []byte, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return + } + s = s[1 : len(s)-1] + + // Check for unusual characters. If there are none, + // then no unquoting is needed, so return a slice of the + // original bytes. + r := 0 + for r < len(s) { + c := s[r] + if c == '\\' || c == '"' || c < ' ' { + break + } + if c < utf8.RuneSelf { + r++ + continue + } + rr, size := utf8.DecodeRune(s[r:]) + if rr == utf8.RuneError && size == 1 { + break + } + r += size + } + if r == len(s) { + return s, true + } + + b := make([]byte, len(s)+2*utf8.UTFMax) + w := copy(b, s[0:r]) + for r < len(s) { + // Out of room? Can only happen if s is full of + // malformed UTF-8 and we're replacing each + // byte with RuneError. + if w >= len(b)-2*utf8.UTFMax { + nb := make([]byte, (len(b)+utf8.UTFMax)*2) + copy(nb, b[0:w]) + b = nb + } + switch c := s[r]; { + case c == '\\': + r++ + if r >= len(s) { + return + } + switch s[r] { + default: + return + case '"', '\\', '/', '\'': + b[w] = s[r] + r++ + w++ + case 'b': + b[w] = '\b' + r++ + w++ + case 'f': + b[w] = '\f' + r++ + w++ + case 'n': + b[w] = '\n' + r++ + w++ + case 'r': + b[w] = '\r' + r++ + w++ + case 't': + b[w] = '\t' + r++ + w++ + case 'u': + r-- + rr := getu4(s[r:]) + if rr < 0 { + return + } + r += 6 + if utf16.IsSurrogate(rr) { + rr1 := getu4(s[r:]) + if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { + // A valid pair; consume. + r += 6 + w += utf8.EncodeRune(b[w:], dec) + break + } + // Invalid surrogate; fall back to replacement rune. + rr = unicode.ReplacementChar + } + w += utf8.EncodeRune(b[w:], rr) + } + + // Quote, control characters are invalid. + case c == '"', c < ' ': + return + + // ASCII + case c < utf8.RuneSelf: + b[w] = c + r++ + w++ + + // Coerce to well-formed UTF-8. + default: + rr, size := utf8.DecodeRune(s[r:]) + r += size + w += utf8.EncodeRune(b[w:], rr) + } + } + 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 new file mode 100644 index 000000000..d758758d9 --- /dev/null +++ b/src/pkg/encoding/json/decode_test.go @@ -0,0 +1,640 @@ +// 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. + +package json + +import ( + "bytes" + "fmt" + "reflect" + "strings" + "testing" +) + +type T struct { + X string + Y int + Z int `json:"-"` +} + +type tx struct { + x int +} + +var txType = reflect.TypeOf((*tx)(nil)).Elem() + +// A type that can unmarshal itself. + +type unmarshaler struct { + T bool +} + +func (u *unmarshaler) UnmarshalJSON(b []byte) error { + *u = unmarshaler{true} // All we need to see that UnmarshalJson is called. + return nil +} + +type ustruct struct { + M unmarshaler +} + +var ( + um0, um1 unmarshaler // target2 of unmarshaling + ump = &um1 + umtrue = unmarshaler{true} + umslice = []unmarshaler{{true}} + umslicep = new([]unmarshaler) + umstruct = ustruct{unmarshaler{true}} +) + +type unmarshalTest struct { + in string + ptr interface{} + out interface{} + err error +} + +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)}}, + + // Z has a "-" tag. + {`{"Y": 1, "Z": 2}`, new(T), T{Y: 1}, nil}, + + // 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}}, + + // 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}, + + // 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}, + + // 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}, +} + +func TestMarshal(t *testing.T) { + b, err := Marshal(allValue) + if err != nil { + t.Fatalf("Marshal allValue: %v", err) + } + if string(b) != allValueCompact { + t.Errorf("Marshal allValueCompact") + diff(t, b, []byte(allValueCompact)) + return + } + + b, err = Marshal(pallValue) + if err != nil { + t.Fatalf("Marshal pallValue: %v", err) + } + if string(b) != pallValueCompact { + t.Errorf("Marshal pallValueCompact") + diff(t, b, []byte(pallValueCompact)) + return + } +} + +func TestMarshalBadUTF8(t *testing.T) { + s := "hello\xffworld" + b, err := Marshal(s) + if err == nil { + t.Fatal("Marshal bad UTF8: no error") + } + if len(b) != 0 { + t.Fatal("Marshal returned data") + } + if _, ok := err.(*InvalidUTF8Error); !ok { + t.Fatalf("Marshal did not return InvalidUTF8Error: %T %v", err, err) + } +} + +func TestUnmarshal(t *testing.T) { + for i, tt := range unmarshalTests { + var scan scanner + in := []byte(tt.in) + if err := checkValid(in, &scan); err != nil { + if !reflect.DeepEqual(err, tt.err) { + t.Errorf("#%d: checkValid: %#v", i, err) + continue + } + } + if tt.ptr == nil { + continue + } + // v = new(right-type) + v := reflect.New(reflect.TypeOf(tt.ptr).Elem()) + if err := Unmarshal([]byte(in), v.Interface()); !reflect.DeepEqual(err, tt.err) { + t.Errorf("#%d: %v want %v", i, err, tt.err) + continue + } + if !reflect.DeepEqual(v.Elem().Interface(), tt.out) { + t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), tt.out) + data, _ := Marshal(v.Elem().Interface()) + println(string(data)) + data, _ = Marshal(tt.out) + println(string(data)) + continue + } + } +} + +func TestUnmarshalMarshal(t *testing.T) { + initBig() + var v interface{} + if err := Unmarshal(jsonBig, &v); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + b, err := Marshal(v) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + if bytes.Compare(jsonBig, b) != 0 { + t.Errorf("Marshal jsonBig") + diff(t, b, jsonBig) + return + } +} + +func TestLargeByteSlice(t *testing.T) { + s0 := make([]byte, 2000) + for i := range s0 { + s0[i] = byte(i) + } + b, err := Marshal(s0) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + var s1 []byte + if err := Unmarshal(b, &s1); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if bytes.Compare(s0, s1) != 0 { + t.Errorf("Marshal large byte slice") + diff(t, s0, s1) + } +} + +type Xint struct { + X int +} + +func TestUnmarshalInterface(t *testing.T) { + var xint Xint + var i interface{} = &xint + if err := Unmarshal([]byte(`{"X":1}`), &i); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if xint.X != 1 { + t.Fatalf("Did not write to xint") + } +} + +func TestUnmarshalPtrPtr(t *testing.T) { + var xint Xint + pxint := &xint + if err := Unmarshal([]byte(`{"X":1}`), &pxint); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if xint.X != 1 { + t.Fatalf("Did not write to xint") + } +} + +func TestEscape(t *testing.T) { + const input = `"foobar"` + const expected = `"\"foobar\"\u003chtml\u003e"` + b, err := Marshal(input) + if err != nil { + t.Fatalf("Marshal error: %v", err) + } + if s := string(b); s != expected { + t.Errorf("Encoding of [%s] was [%s], want [%s]", input, s, expected) + } +} + +// WrongString is a struct that's misusing the ,string modifier. +type WrongString struct { + Message string `json:"result,string"` +} + +type wrongStringTest struct { + in, err string +} + +var wrongStringTests = []wrongStringTest{ + {`{"result":"x"}`, `json: invalid use of ,string struct tag, trying to unmarshal "x" into string`}, + {`{"result":"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "foo" into string`}, + {`{"result":"123"}`, `json: invalid use of ,string struct tag, trying to unmarshal "123" into string`}, +} + +// If people misuse the ,string modifier, the error message should be +// helpful, telling the user that they're doing it wrong. +func TestErrorMessageFromMisusedString(t *testing.T) { + for n, tt := range wrongStringTests { + r := strings.NewReader(tt.in) + var s WrongString + err := NewDecoder(r).Decode(&s) + got := fmt.Sprintf("%v", err) + if got != tt.err { + t.Errorf("%d. got err = %q, want %q", n, got, tt.err) + } + } +} + +func noSpace(c rune) rune { + if isSpace(c) { + return -1 + } + return c +} + +type All struct { + Bool bool + Int int + Int8 int8 + Int16 int16 + Int32 int32 + Int64 int64 + Uint uint + Uint8 uint8 + Uint16 uint16 + Uint32 uint32 + Uint64 uint64 + Uintptr uintptr + Float32 float32 + Float64 float64 + + Foo string `json:"bar"` + Foo2 string `json:"bar2,dummyopt"` + + IntStr int64 `json:",string"` + + PBool *bool + PInt *int + PInt8 *int8 + PInt16 *int16 + PInt32 *int32 + PInt64 *int64 + PUint *uint + PUint8 *uint8 + PUint16 *uint16 + PUint32 *uint32 + PUint64 *uint64 + PUintptr *uintptr + PFloat32 *float32 + PFloat64 *float64 + + String string + PString *string + + Map map[string]Small + MapP map[string]*Small + PMap *map[string]Small + PMapP *map[string]*Small + + EmptyMap map[string]Small + NilMap map[string]Small + + Slice []Small + SliceP []*Small + PSlice *[]Small + PSliceP *[]*Small + + EmptySlice []Small + NilSlice []Small + + StringSlice []string + ByteSlice []byte + + Small Small + PSmall *Small + PPSmall **Small + + Interface interface{} + PInterface *interface{} + + unexported int +} + +type Small struct { + Tag string +} + +var allValue = All{ + Bool: true, + Int: 2, + Int8: 3, + Int16: 4, + Int32: 5, + Int64: 6, + Uint: 7, + Uint8: 8, + Uint16: 9, + Uint32: 10, + Uint64: 11, + Uintptr: 12, + Float32: 14.1, + Float64: 15.1, + Foo: "foo", + Foo2: "foo2", + IntStr: 42, + String: "16", + Map: map[string]Small{ + "17": {Tag: "tag17"}, + "18": {Tag: "tag18"}, + }, + MapP: map[string]*Small{ + "19": {Tag: "tag19"}, + "20": nil, + }, + EmptyMap: map[string]Small{}, + Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}}, + SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}}, + EmptySlice: []Small{}, + StringSlice: []string{"str24", "str25", "str26"}, + ByteSlice: []byte{27, 28, 29}, + Small: Small{Tag: "tag30"}, + PSmall: &Small{Tag: "tag31"}, + Interface: 5.2, +} + +var pallValue = All{ + PBool: &allValue.Bool, + PInt: &allValue.Int, + PInt8: &allValue.Int8, + PInt16: &allValue.Int16, + PInt32: &allValue.Int32, + PInt64: &allValue.Int64, + PUint: &allValue.Uint, + PUint8: &allValue.Uint8, + PUint16: &allValue.Uint16, + PUint32: &allValue.Uint32, + PUint64: &allValue.Uint64, + PUintptr: &allValue.Uintptr, + PFloat32: &allValue.Float32, + PFloat64: &allValue.Float64, + PString: &allValue.String, + PMap: &allValue.Map, + PMapP: &allValue.MapP, + PSlice: &allValue.Slice, + PSliceP: &allValue.SliceP, + PPSmall: &allValue.PSmall, + PInterface: &allValue.Interface, +} + +var allValueIndent = `{ + "Bool": true, + "Int": 2, + "Int8": 3, + "Int16": 4, + "Int32": 5, + "Int64": 6, + "Uint": 7, + "Uint8": 8, + "Uint16": 9, + "Uint32": 10, + "Uint64": 11, + "Uintptr": 12, + "Float32": 14.1, + "Float64": 15.1, + "bar": "foo", + "bar2": "foo2", + "IntStr": "42", + "PBool": null, + "PInt": null, + "PInt8": null, + "PInt16": null, + "PInt32": null, + "PInt64": null, + "PUint": null, + "PUint8": null, + "PUint16": null, + "PUint32": null, + "PUint64": null, + "PUintptr": null, + "PFloat32": null, + "PFloat64": null, + "String": "16", + "PString": null, + "Map": { + "17": { + "Tag": "tag17" + }, + "18": { + "Tag": "tag18" + } + }, + "MapP": { + "19": { + "Tag": "tag19" + }, + "20": null + }, + "PMap": null, + "PMapP": null, + "EmptyMap": {}, + "NilMap": null, + "Slice": [ + { + "Tag": "tag20" + }, + { + "Tag": "tag21" + } + ], + "SliceP": [ + { + "Tag": "tag22" + }, + null, + { + "Tag": "tag23" + } + ], + "PSlice": null, + "PSliceP": null, + "EmptySlice": [], + "NilSlice": null, + "StringSlice": [ + "str24", + "str25", + "str26" + ], + "ByteSlice": "Gxwd", + "Small": { + "Tag": "tag30" + }, + "PSmall": { + "Tag": "tag31" + }, + "PPSmall": null, + "Interface": 5.2, + "PInterface": null +}` + +var allValueCompact = strings.Map(noSpace, allValueIndent) + +var pallValueIndent = `{ + "Bool": false, + "Int": 0, + "Int8": 0, + "Int16": 0, + "Int32": 0, + "Int64": 0, + "Uint": 0, + "Uint8": 0, + "Uint16": 0, + "Uint32": 0, + "Uint64": 0, + "Uintptr": 0, + "Float32": 0, + "Float64": 0, + "bar": "", + "bar2": "", + "IntStr": "0", + "PBool": true, + "PInt": 2, + "PInt8": 3, + "PInt16": 4, + "PInt32": 5, + "PInt64": 6, + "PUint": 7, + "PUint8": 8, + "PUint16": 9, + "PUint32": 10, + "PUint64": 11, + "PUintptr": 12, + "PFloat32": 14.1, + "PFloat64": 15.1, + "String": "", + "PString": "16", + "Map": null, + "MapP": null, + "PMap": { + "17": { + "Tag": "tag17" + }, + "18": { + "Tag": "tag18" + } + }, + "PMapP": { + "19": { + "Tag": "tag19" + }, + "20": null + }, + "EmptyMap": null, + "NilMap": null, + "Slice": null, + "SliceP": null, + "PSlice": [ + { + "Tag": "tag20" + }, + { + "Tag": "tag21" + } + ], + "PSliceP": [ + { + "Tag": "tag22" + }, + null, + { + "Tag": "tag23" + } + ], + "EmptySlice": null, + "NilSlice": null, + "StringSlice": null, + "ByteSlice": null, + "Small": { + "Tag": "" + }, + "PSmall": null, + "PPSmall": { + "Tag": "tag31" + }, + "Interface": null, + "PInterface": 5.2 +}` + +var pallValueCompact = strings.Map(noSpace, pallValueIndent) + +func TestRefUnmarshal(t *testing.T) { + type S struct { + // Ref is defined in encode_test.go. + R0 Ref + R1 *Ref + } + want := S{ + R0: 12, + R1: new(Ref), + } + *want.R1 = 12 + + var got S + if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref"}`), &got); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } +} + +// 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") + } +} diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go new file mode 100644 index 000000000..14957b848 --- /dev/null +++ b/src/pkg/encoding/json/encode.go @@ -0,0 +1,551 @@ +// 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. + +// Package json implements encoding and decoding of JSON objects as defined in +// RFC 4627. +// +// See "JSON and Go" for an introduction to this package: +// http://golang.org/doc/articles/json_and_go.html +package json + +import ( + "bytes" + "encoding/base64" + "math" + "reflect" + "runtime" + "sort" + "strconv" + "sync" + "unicode" + "unicode/utf8" +) + +// Marshal returns the JSON encoding of v. +// +// Marshal traverses the value v recursively. +// If an encountered value implements the Marshaler interface +// and is not a nil pointer, Marshal calls its MarshalJSON method +// to produce JSON. The nil pointer exception is not strictly necessary +// but mimics a similar, necessary exception in the behavior of +// UnmarshalJSON. +// +// Otherwise, Marshal uses the following type-dependent default encodings: +// +// Boolean values encode as JSON booleans. +// +// Floating point and integer 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. +// The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e" +// to keep some browsers from misinterpreting JSON output as HTML. +// +// Array and slice values encode as JSON arrays, except that +// []byte encodes as a base64-encoded string, and a nil slice +// encodes as the null JSON object. +// +// Struct values encode as JSON objects. Each exported struct field +// becomes a member of the object unless +// - the field's tag is "-", or +// - the field is empty and its tag specifies the "omitempty" option. +// The empty values are false, 0, any +// 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 +// and options. Examples: +// +// // Field is ignored by this package. +// Field int `json:"-"` +// +// // Field appears in JSON as key "myName". +// Field int `json:"myName"` +// +// // Field appears in JSON as key "myName" and +// // the field is omitted from the object if its value is empty, +// // as defined above. +// Field int `json:"myName,omitempty"` +// +// // Field appears in JSON as key "Field" (the default), but +// // the field is skipped if empty. +// // Note the leading comma. +// 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: +// +// Int64String int64 `json:",string"` +// +// The key name will be used if it's a non-empty string consisting of +// only Unicode letters, digits, dollar signs, percent signs, hyphens, +// underscores and slashes. +// +// Map values encode as JSON objects. +// The map's key type must be string; the object keys are used directly +// as map keys. +// +// Pointer values encode as the value pointed to. +// A nil pointer encodes as the null JSON object. +// +// Interface values encode as the value contained in the interface. +// A nil interface value encodes as the null JSON object. +// +// Channel, complex, and function values cannot be encoded in JSON. +// Attempting to encode such a value causes Marshal to return +// an InvalidTypeError. +// +// JSON cannot represent cyclic data structures and Marshal does not +// handle them. Passing cyclic structures to Marshal will result in +// an infinite recursion. +// +func Marshal(v interface{}) ([]byte, error) { + e := &encodeState{} + err := e.marshal(v) + if err != nil { + return nil, err + } + return e.Bytes(), nil +} + +// MarshalIndent is like Marshal but applies Indent to format the output. +func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { + b, err := Marshal(v) + if err != nil { + return nil, err + } + var buf bytes.Buffer + err = Indent(&buf, b, prefix, indent) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// HTMLEscape appends to dst the JSON-encoded src with <, >, and & +// characters inside string literals changed to \u003c, \u003e, \u0026 +// so that the JSON will be safe to embed inside HTML ") + +produces + + Hello, ! + +but the contextual autoescaping in html/template + + import "html/template" + ... + t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) + err = t.ExecuteTemplate(out, "T", "") + +produces safe, escaped HTML output + + Hello, <script>alert('you have been pwned')</script>! + + +Contexts + +This package understands HTML, CSS, JavaScript, and URIs. It adds sanitizing +functions to each simple action pipeline, so given the excerpt + + {{.}} + +At parse time each {{.}} is overwritten to add escaping functions as necessary. +In this case it becomes + + {{. | html}} + + +Errors + +See the documentation of ErrorCode for details. + + +A fuller picture + +The rest of this package comment may be skipped on first reading; it includes +details necessary to understand escaping contexts and error messages. Most users +will not need to understand these details. + + +Contexts + +Assuming {{.}} is `O'Reilly: How are you?`, the table below shows +how {{.}} appears when used in the context to the left. + + Context {{.}} After + {{.}} O'Reilly: How are <i>you</i>? + O'Reilly: How are you? + O'Reilly: How are %3ci%3eyou%3c/i%3e? + O'Reilly%3a%20How%20are%3ci%3e...%3f + O\x27Reilly: How are \x3ci\x3eyou...? + "O\x27Reilly: How are \x3ci\x3eyou...?" + O\x27Reilly: How are \x3ci\x3eyou...\x3f + +If used in an unsafe context, then the value might be filtered out: + + Context {{.}} After + #ZgotmplZ + +since "O'Reilly:" is not an allowed protocol like "http:". + + +If {{.}} is the innocuous word, `left`, then it can appear more widely, + + Context {{.}} After + {{.}} left + left + left + left + left + left + left + left + left + +Non-string values can be used in JavaScript contexts. +If {{.}} is + + []struct{A,B string}{ "foo", "bar" } + +in the escaped template + + + +then the template output is + + + +See package json to understand how non-string content is marshalled for +embedding in JavaScript contexts. + + +Typed Strings + +By default, this package assumes that all pipelines produce a plain text string. +It adds escaping pipeline stages necessary to correctly and safely embed that +plain text string in the appropriate context. + +When a data value is not plain text, you can make sure it is not over-escaped +by marking it with its type. + +Types HTML, JS, URL, and others from content.go can carry safe content that is +exempted from escaping. + +The template + + Hello, {{.}}! + +can be invoked with + + tmpl.Execute(out, HTML(`World`)) + +to produce + + Hello, World! + +instead of the + + Hello, <b>World<b>! + +that would have been produced if {{.}} was a regular string. + + +Security Model + +http://js-quasis-libraries-and-repl.googlecode.com/svn/trunk/safetemplate.html#problem_definition defines "safe" as used by this package. + +This package assumes that template authors are trusted, that Execute's data +parameter is not, and seeks to preserve the properties below in the face +of untrusted data: + +Structure Preservation Property: +"... when a template author writes an HTML tag in a safe templating language, +the browser will interpret the corresponding portion of the output as a tag +regardless of the values of untrusted data, and similarly for other structures +such as attribute boundaries and JS and CSS string boundaries." + +Code Effect Property: +"... only code specified by the template author should run as a result of +injecting the template output into a page and all code specified by the +template author should run as a result of the same." + +Least Surprise Property: +"A developer (or code reviewer) familiar with HTML, CSS, and JavaScript, who +knows that contextual autoescaping happens should be able to look at a {{.}} +and correctly infer what sanitization happens." +*/ +package template diff --git a/src/pkg/html/template/error.go b/src/pkg/html/template/error.go new file mode 100644 index 000000000..dcac74896 --- /dev/null +++ b/src/pkg/html/template/error.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 template + +import ( + "fmt" +) + +// Error describes a problem encountered during template Escaping. +type Error struct { + // ErrorCode describes the kind of error. + ErrorCode ErrorCode + // Name is the name of the template in which the error was encountered. + Name string + // Line is the line number of the error in the template source or 0. + Line int + // Description is a human-readable description of the problem. + Description string +} + +// ErrorCode is a code for a kind of error. +type ErrorCode int + +// We define codes for each error that manifests while escaping templates, but +// escaped templates may also fail at runtime. +// +// Output: "ZgotmplZ" +// Example: +// +// where {{.X}} evaluates to `javascript:...` +// Discussion: +// "ZgotmplZ" is a special value that indicates that unsafe content reached a +// CSS or URL context at runtime. The output of the example will be +// +// If the data comes from a trusted source, use content types to exempt it +// from filtering: URL(`javascript:...`). +const ( + // OK indicates the lack of an error. + OK ErrorCode = iota + + // ErrAmbigContext: "... appears in an ambiguous URL context" + // Example: + // + // Discussion: + // {{.X}} is in an ambiguous URL context since, depending on {{.C}}, + // it may be either a URL suffix or a query parameter. + // Moving {{.X}} into the condition removes the ambiguity: + // + ErrAmbigContext + + // ErrBadHTML: "expected space, attr name, or end of tag, but got ...", + // "... in unquoted attr", "... in attribute name" + // Example: + // + // + //
+ //
{{end}} + // {{define "attrs"}}href="{{.URL}}"{{end}} + // Discussion: + // Package html/template looks through template calls to compute the + // context. + // Here the {{.URL}} in "attrs" must be treated as a URL when called + // from "main", but you will get this error if "attrs" is not defined + // when "main" is parsed. + ErrNoSuchTemplate + + // ErrOutputContext: "cannot compute output context for template ..." + // Examples: + // {{define "t"}}{{if .T}}{{template "t" .T}}{{end}}{{.H}}",{{end}} + // Discussion: + // A recursive template does not end in the same context in which it + // starts, and a reliable output context cannot be computed. + // Look for typos in the named template. + // If the template should not be called in the named start context, + // look for calls to that template in unexpected contexts. + // Maybe refactor recursive templates to not be recursive. + ErrOutputContext + + // ErrPartialCharset: "unfinished JS regexp charset in ..." + // Example: + // + // Discussion: + // Package html/template does not support interpolation into regular + // expression literal character sets. + ErrPartialCharset + + // ErrPartialEscape: "unfinished escape sequence in ..." + // Example: + // + // Discussion: + // Package html/template does not support actions following a + // backslash. + // This is usually an error and there are better solutions; for + // example + // + // should work, and if {{.X}} is a partial escape sequence such as + // "xA0", mark the whole sequence as safe content: JSStr(`\xA0`) + ErrPartialEscape + + // ErrRangeLoopReentry: "on range loop re-entry: ..." + // Example: + // + // Discussion: + // If an iteration through a range would cause it to end in a + // different context than an earlier pass, there is no single context. + // In the example, there is missing a quote, so it is not clear + // whether {{.}} is meant to be inside a JS string or in a JS value + // context. The second iteration would produce something like + // + // + ErrRangeLoopReentry + + // ErrSlashAmbig: '/' could start a division or regexp. + // Example: + // + // Discussion: + // The example above could produce `var x = 1/-2/i.test(s)...` + // in which the first '/' is a mathematical division operator or it + // could produce `/-2/i.test(s)` in which the first '/' starts a + // regexp literal. + // Look for missing semicolons inside branches, and maybe add + // parentheses to make it clear which interpretation you intend. + ErrSlashAmbig +) + +func (e *Error) Error() string { + if e.Line != 0 { + return fmt.Sprintf("html/template:%s:%d: %s", e.Name, e.Line, e.Description) + } else if e.Name != "" { + return fmt.Sprintf("html/template:%s: %s", e.Name, e.Description) + } + return "html/template: " + e.Description +} + +// errorf creates an error given a format string f and args. +// The template Name still needs to be supplied. +func errorf(k ErrorCode, line int, f string, args ...interface{}) *Error { + return &Error{k, "", line, fmt.Sprintf(f, args...)} +} diff --git a/src/pkg/html/template/escape.go b/src/pkg/html/template/escape.go new file mode 100644 index 000000000..5f0e28e8c --- /dev/null +++ b/src/pkg/html/template/escape.go @@ -0,0 +1,795 @@ +// 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 template + +import ( + "bytes" + "fmt" + "html" + "io" + "text/template" + "text/template/parse" +) + +// escapeTemplates rewrites the named templates, which must be +// associated with t, to guarantee that the output of any of the named +// templates is properly escaped. Names should include the names of +// all templates that might be Executed but need not include helper +// templates. If no error is returned, then the named templates have +// been modified. Otherwise the named templates have been rendered +// unusable. +func escapeTemplates(tmpl *Template, names ...string) error { + e := newEscaper(tmpl) + for _, name := range names { + c, _ := e.escapeTree(context{}, name, 0) + var err error + if c.err != nil { + err, c.err.Name = c.err, name + } else if c.state != stateText { + err = &Error{ErrEndContext, name, 0, fmt.Sprintf("ends in a non-text context: %v", c)} + } + if err != nil { + // Prevent execution of unsafe templates. + for _, name := range names { + if t := tmpl.set[name]; t != nil { + t.text.Tree = nil + } + } + return err + } + tmpl.escaped = true + } + e.commit() + return nil +} + +// funcMap maps command names to functions that render their inputs safe. +var funcMap = template.FuncMap{ + "html_template_attrescaper": attrEscaper, + "html_template_commentescaper": commentEscaper, + "html_template_cssescaper": cssEscaper, + "html_template_cssvaluefilter": cssValueFilter, + "html_template_htmlnamefilter": htmlNameFilter, + "html_template_htmlescaper": htmlEscaper, + "html_template_jsregexpescaper": jsRegexpEscaper, + "html_template_jsstrescaper": jsStrEscaper, + "html_template_jsvalescaper": jsValEscaper, + "html_template_nospaceescaper": htmlNospaceEscaper, + "html_template_rcdataescaper": rcdataEscaper, + "html_template_urlescaper": urlEscaper, + "html_template_urlfilter": urlFilter, + "html_template_urlnormalizer": urlNormalizer, +} + +// equivEscapers matches contextual escapers to equivalent template builtins. +var equivEscapers = map[string]string{ + "html_template_attrescaper": "html", + "html_template_htmlescaper": "html", + "html_template_nospaceescaper": "html", + "html_template_rcdataescaper": "html", + "html_template_urlescaper": "urlquery", + "html_template_urlnormalizer": "urlquery", +} + +// escaper collects type inferences about templates and changes needed to make +// templates injection safe. +type escaper struct { + tmpl *Template + // output[templateName] is the output context for a templateName that + // has been mangled to include its input context. + output map[string]context + // derived[c.mangle(name)] maps to a template derived from the template + // named name templateName for the start context c. + derived map[string]*template.Template + // called[templateName] is a set of called mangled template names. + called map[string]bool + // xxxNodeEdits are the accumulated edits to apply during commit. + // Such edits are not applied immediately in case a template set + // executes a given template in different escaping contexts. + actionNodeEdits map[*parse.ActionNode][]string + templateNodeEdits map[*parse.TemplateNode]string + textNodeEdits map[*parse.TextNode][]byte +} + +// newEscaper creates a blank escaper for the given set. +func newEscaper(t *Template) *escaper { + return &escaper{ + t, + map[string]context{}, + map[string]*template.Template{}, + map[string]bool{}, + map[*parse.ActionNode][]string{}, + map[*parse.TemplateNode]string{}, + map[*parse.TextNode][]byte{}, + } +} + +// filterFailsafe is an innocuous word that is emitted in place of unsafe values +// by sanitizer functions. It is not a keyword in any programming language, +// contains no special characters, is not empty, and when it appears in output +// it is distinct enough that a developer can find the source of the problem +// via a search engine. +const filterFailsafe = "ZgotmplZ" + +// escape escapes a template node. +func (e *escaper) escape(c context, n parse.Node) context { + switch n := n.(type) { + case *parse.ActionNode: + return e.escapeAction(c, n) + case *parse.IfNode: + return e.escapeBranch(c, &n.BranchNode, "if") + case *parse.ListNode: + return e.escapeList(c, n) + case *parse.RangeNode: + return e.escapeBranch(c, &n.BranchNode, "range") + case *parse.TemplateNode: + return e.escapeTemplate(c, n) + case *parse.TextNode: + return e.escapeText(c, n) + case *parse.WithNode: + return e.escapeBranch(c, &n.BranchNode, "with") + } + panic("escaping " + n.String() + " is unimplemented") +} + +// escapeAction escapes an action template node. +func (e *escaper) escapeAction(c context, n *parse.ActionNode) context { + if len(n.Pipe.Decl) != 0 { + // A local variable assignment, not an interpolation. + return c + } + c = nudge(c) + s := make([]string, 0, 3) + switch c.state { + case stateError: + return c + case stateURL, stateCSSDqStr, stateCSSSqStr, stateCSSDqURL, stateCSSSqURL, stateCSSURL: + switch c.urlPart { + case urlPartNone: + s = append(s, "html_template_urlfilter") + fallthrough + case urlPartPreQuery: + switch c.state { + case stateCSSDqStr, stateCSSSqStr: + s = append(s, "html_template_cssescaper") + default: + s = append(s, "html_template_urlnormalizer") + } + case urlPartQueryOrFrag: + s = append(s, "html_template_urlescaper") + case urlPartUnknown: + return context{ + state: stateError, + err: errorf(ErrAmbigContext, n.Line, "%s appears in an ambiguous URL context", n), + } + default: + panic(c.urlPart.String()) + } + case stateJS: + s = append(s, "html_template_jsvalescaper") + // A slash after a value starts a div operator. + c.jsCtx = jsCtxDivOp + case stateJSDqStr, stateJSSqStr: + s = append(s, "html_template_jsstrescaper") + case stateJSRegexp: + s = append(s, "html_template_jsregexpescaper") + case stateCSS: + s = append(s, "html_template_cssvaluefilter") + case stateText: + s = append(s, "html_template_htmlescaper") + case stateRCDATA: + s = append(s, "html_template_rcdataescaper") + case stateAttr: + // Handled below in delim check. + case stateAttrName, stateTag: + c.state = stateAttrName + s = append(s, "html_template_htmlnamefilter") + default: + if isComment(c.state) { + s = append(s, "html_template_commentescaper") + } else { + panic("unexpected state " + c.state.String()) + } + } + switch c.delim { + case delimNone: + // No extra-escaping needed for raw text content. + case delimSpaceOrTagEnd: + s = append(s, "html_template_nospaceescaper") + default: + s = append(s, "html_template_attrescaper") + } + e.editActionNode(n, s) + return c +} + +// ensurePipelineContains ensures that the pipeline has commands with +// the identifiers in s in order. +// If the pipeline already has some of the sanitizers, do not interfere. +// For example, if p is (.X | html) and s is ["escapeJSVal", "html"] then it +// has one matching, "html", and one to insert, "escapeJSVal", to produce +// (.X | escapeJSVal | html). +func ensurePipelineContains(p *parse.PipeNode, s []string) { + if len(s) == 0 { + return + } + n := len(p.Cmds) + // Find the identifiers at the end of the command chain. + 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 + } + continue + } + } + idents = p.Cmds[i+1:] + } + dups := 0 + for _, id := range idents { + if escFnsEq(s[dups], (id.Args[0].(*parse.IdentifierNode)).Ident) { + dups++ + if dups == len(s) { + return + } + } + } + newCmds := make([]*parse.CommandNode, n-len(idents), n+len(s)-dups) + copy(newCmds, p.Cmds) + // Merge existing identifier commands with the sanitizers needed. + for _, id := range idents { + i := indexOfStr((id.Args[0].(*parse.IdentifierNode)).Ident, s, escFnsEq) + if i != -1 { + for _, name := range s[:i] { + newCmds = appendCmd(newCmds, newIdentCmd(name)) + } + s = s[i+1:] + } + newCmds = appendCmd(newCmds, id) + } + // Create any remaining sanitizers. + for _, name := range s { + newCmds = appendCmd(newCmds, newIdentCmd(name)) + } + p.Cmds = newCmds +} + +// redundantFuncs[a][b] implies that funcMap[b](funcMap[a](x)) == funcMap[a](x) +// for all x. +var redundantFuncs = map[string]map[string]bool{ + "html_template_commentescaper": { + "html_template_attrescaper": true, + "html_template_nospaceescaper": true, + "html_template_htmlescaper": true, + }, + "html_template_cssescaper": { + "html_template_attrescaper": true, + }, + "html_template_jsregexpescaper": { + "html_template_attrescaper": true, + }, + "html_template_jsstrescaper": { + "html_template_attrescaper": true, + }, + "html_template_urlescaper": { + "html_template_urlnormalizer": true, + }, +} + +// appendCmd appends the given command to the end of the command pipeline +// unless it is redundant with the last command. +func appendCmd(cmds []*parse.CommandNode, cmd *parse.CommandNode) []*parse.CommandNode { + if n := len(cmds); n != 0 { + last, ok := cmds[n-1].Args[0].(*parse.IdentifierNode) + next, _ := cmd.Args[0].(*parse.IdentifierNode) + if ok && redundantFuncs[last.Ident][next.Ident] { + return cmds + } + } + return append(cmds, cmd) +} + +// indexOfStr is the first i such that eq(s, strs[i]) or -1 if s was not found. +func indexOfStr(s string, strs []string, eq func(a, b string) bool) int { + for i, t := range strs { + if eq(s, t) { + return i + } + } + return -1 +} + +// escFnsEq returns whether the two escaping functions are equivalent. +func escFnsEq(a, b string) bool { + if e := equivEscapers[a]; e != "" { + a = e + } + if e := equivEscapers[b]; e != "" { + b = e + } + return a == b +} + +// newIdentCmd produces a command containing a single identifier node. +func newIdentCmd(identifier string) *parse.CommandNode { + return &parse.CommandNode{ + NodeType: parse.NodeCommand, + Args: []parse.Node{parse.NewIdentifier(identifier)}, + } +} + +// nudge returns the context that would result from following empty string +// transitions from the input context. +// For example, parsing: +// `90% of the time. + e.output[t.Name()] = c + return e.escapeListConditionally(c, t.Tree.Root, filter) +} + +// delimEnds maps each delim to a string of characters that terminate it. +var delimEnds = [...]string{ + delimDoubleQuote: `"`, + delimSingleQuote: "'", + // Determined empirically by running the below in various browsers. + // var div = document.createElement("DIV"); + // for (var i = 0; i < 0x10000; ++i) { + // div.innerHTML = ""; + // if (div.getElementsByTagName("SPAN")[0].title.indexOf("bar") < 0) + // document.write("

U+" + i.toString(16)); + // } + delimSpaceOrTagEnd: " \t\n\f\r>", +} + +var doctypeBytes = []byte("= i; j-- { + if s[j] == '<' { + end = j + break + } + } + } + for j := i; j < end; j++ { + if s[j] == '<' && !bytes.HasPrefix(bytes.ToUpper(s[j:]), doctypeBytes) { + b.Write(s[written:j]) + b.WriteString("<") + written = j + 1 + } + } + } else if isComment(c.state) && c.delim == delimNone { + switch c.state { + case stateJSBlockCmt: + // http://es5.github.com/#x7.4: + // "Comments behave like white space and are + // discarded except that, if a MultiLineComment + // contains a line terminator character, then + // the entire comment is considered to be a + // LineTerminator for purposes of parsing by + // the syntactic grammar." + if bytes.IndexAny(s[written:i1], "\n\r\u2028\u2029") != -1 { + b.WriteByte('\n') + } else { + b.WriteByte(' ') + } + case stateCSSBlockCmt: + b.WriteByte(' ') + } + written = i1 + } + if c.state != c1.state && isComment(c1.state) && c1.delim == delimNone { + // Preserve the portion between written and the comment start. + cs := i1 - 2 + if c1.state == stateHTMLCmt { + // "{{.C}}", + "Hello, <Cincinatti>", + }, + { + "HTML comment not first < in text node.", + "<!--", + "<!--", + }, + { + "HTML normalization 1", + "a < b", + "a < b", + }, + { + "HTML normalization 2", + "a << b", + "a << b", + }, + { + "HTML normalization 3", + "a<b", + "a<b", + }, + { + "HTML doctype not normalized", + "Hello, World!", + "Hello, World!", + }, + { + "HTML doctype not case-insensitive", + "Hello, World!", + "Hello, World!", + }, + { + "No doctype injection", + `Hello, {{.C}}{{else}}world -->{{.W}}{{end}}", + "Hello, <Cincinatti>", + }, + { + "JS line comment", + "", + "", + }, + { + "JS multiline block comment", + "", + // Newline separates break from call. If newline + // removed, then break will consume label leaving + // code invalid. + "", + }, + { + "JS single-line block comment", + "", + // Newline separates break from call. If newline + // removed, then break will consume label leaving + // code invalid. + "", + }, + { + "JS block comment flush with mathematical division", + "", + "", + }, + { + "JS mixed comments", + "", + "", + }, + { + "CSS comments", + "`, + "", + }, + { + "JS attr block comment", + ``, + // Attribute comment tests should pass if the comments + // are successfully elided. + ``, + }, + { + "JS attr line comment", + ``, + ``, + }, + { + "CSS attr block comment", + ``, + ``, + }, + { + "CSS attr line comment", + ``, + ``, + }, + { + "HTML substitution commented out", + "

", + "

", + }, + { + "Comment ends flush with start", + "
", + "", + }, + { + "typed HTML in text", + `{{.W}}`, + `¡Hello, !`, + }, + { + "typed HTML in attribute", + `
`, + `
`, + }, + { + "typed HTML in script", + `
-#errors -Line: 1 Col: 9 Unexpected end tag (strong). Expected DOCTYPE. -Line: 1 Col: 9 Unexpected end tag (strong) after the (implied) root element. -Line: 1 Col: 13 Unexpected end tag (b) after the (implied) root element. -Line: 1 Col: 18 Unexpected end tag (em) after the (implied) root element. -Line: 1 Col: 22 Unexpected end tag (i) after the (implied) root element. -Line: 1 Col: 26 Unexpected end tag (u) after the (implied) root element. -Line: 1 Col: 35 Unexpected end tag (strike) after the (implied) root element. -Line: 1 Col: 39 Unexpected end tag (s) after the (implied) root element. -Line: 1 Col: 47 Unexpected end tag (blink) after the (implied) root element. -Line: 1 Col: 52 Unexpected end tag (tt) after the (implied) root element. -Line: 1 Col: 58 Unexpected end tag (pre) after the (implied) root element. -Line: 1 Col: 64 Unexpected end tag (big) after the (implied) root element. -Line: 1 Col: 72 Unexpected end tag (small) after the (implied) root element. -Line: 1 Col: 79 Unexpected end tag (font) after the (implied) root element. -Line: 1 Col: 88 Unexpected end tag (select) after the (implied) root element. -Line: 1 Col: 93 Unexpected end tag (h1) after the (implied) root element. -Line: 1 Col: 98 Unexpected end tag (h2) after the (implied) root element. -Line: 1 Col: 103 Unexpected end tag (h3) after the (implied) root element. -Line: 1 Col: 108 Unexpected end tag (h4) after the (implied) root element. -Line: 1 Col: 113 Unexpected end tag (h5) after the (implied) root element. -Line: 1 Col: 118 Unexpected end tag (h6) after the (implied) root element. -Line: 1 Col: 125 Unexpected end tag (body) after the (implied) root element. -Line: 1 Col: 130 Unexpected end tag (br). Treated as br element. -Line: 1 Col: 134 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 140 This element (img) has no end tag. -Line: 1 Col: 148 Unexpected end tag (title). Ignored. -Line: 1 Col: 155 Unexpected end tag (span). Ignored. -Line: 1 Col: 163 Unexpected end tag (style). Ignored. -Line: 1 Col: 172 Unexpected end tag (script). Ignored. -Line: 1 Col: 180 Unexpected end tag (table). Ignored. -Line: 1 Col: 185 Unexpected end tag (th). Ignored. -Line: 1 Col: 190 Unexpected end tag (td). Ignored. -Line: 1 Col: 195 Unexpected end tag (tr). Ignored. -Line: 1 Col: 203 This element (frame) has no end tag. -Line: 1 Col: 210 This element (area) has no end tag. -Line: 1 Col: 217 Unexpected end tag (link). Ignored. -Line: 1 Col: 225 This element (param) has no end tag. -Line: 1 Col: 230 This element (hr) has no end tag. -Line: 1 Col: 238 This element (input) has no end tag. -Line: 1 Col: 244 Unexpected end tag (col). Ignored. -Line: 1 Col: 251 Unexpected end tag (base). Ignored. -Line: 1 Col: 258 Unexpected end tag (meta). Ignored. -Line: 1 Col: 269 This element (basefont) has no end tag. -Line: 1 Col: 279 This element (bgsound) has no end tag. -Line: 1 Col: 287 This element (embed) has no end tag. -Line: 1 Col: 296 This element (spacer) has no end tag. -Line: 1 Col: 300 Unexpected end tag (p). Ignored. -Line: 1 Col: 305 End tag (dd) seen too early. Expected other end tag. -Line: 1 Col: 310 End tag (dt) seen too early. Expected other end tag. -Line: 1 Col: 320 Unexpected end tag (caption). Ignored. -Line: 1 Col: 331 Unexpected end tag (colgroup). Ignored. -Line: 1 Col: 339 Unexpected end tag (tbody). Ignored. -Line: 1 Col: 347 Unexpected end tag (tfoot). Ignored. -Line: 1 Col: 355 Unexpected end tag (thead). Ignored. -Line: 1 Col: 365 End tag (address) seen too early. Expected other end tag. -Line: 1 Col: 378 End tag (blockquote) seen too early. Expected other end tag. -Line: 1 Col: 387 End tag (center) seen too early. Expected other end tag. -Line: 1 Col: 393 Unexpected end tag (dir). Ignored. -Line: 1 Col: 399 End tag (div) seen too early. Expected other end tag. -Line: 1 Col: 404 End tag (dl) seen too early. Expected other end tag. -Line: 1 Col: 415 End tag (fieldset) seen too early. Expected other end tag. -Line: 1 Col: 425 End tag (listing) seen too early. Expected other end tag. -Line: 1 Col: 432 End tag (menu) seen too early. Expected other end tag. -Line: 1 Col: 437 End tag (ol) seen too early. Expected other end tag. -Line: 1 Col: 442 End tag (ul) seen too early. Expected other end tag. -Line: 1 Col: 447 End tag (li) seen too early. Expected other end tag. -Line: 1 Col: 454 End tag (nobr) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 460 This element (wbr) has no end tag. -Line: 1 Col: 476 End tag (button) seen too early. Expected other end tag. -Line: 1 Col: 486 End tag (marquee) seen too early. Expected other end tag. -Line: 1 Col: 495 End tag (object) seen too early. Expected other end tag. -Line: 1 Col: 513 Unexpected end tag (html). Ignored. -Line: 1 Col: 513 Unexpected end tag (frameset). Ignored. -Line: 1 Col: 520 Unexpected end tag (head). Ignored. -Line: 1 Col: 529 Unexpected end tag (iframe). Ignored. -Line: 1 Col: 537 This element (image) has no end tag. -Line: 1 Col: 547 This element (isindex) has no end tag. -Line: 1 Col: 557 Unexpected end tag (noembed). Ignored. -Line: 1 Col: 568 Unexpected end tag (noframes). Ignored. -Line: 1 Col: 579 Unexpected end tag (noscript). Ignored. -Line: 1 Col: 590 Unexpected end tag (optgroup). Ignored. -Line: 1 Col: 599 Unexpected end tag (option). Ignored. -Line: 1 Col: 611 Unexpected end tag (plaintext). Ignored. -Line: 1 Col: 622 Unexpected end tag (textarea). Ignored. -#document -| -| -| -|
-|

- -#data -

-#errors -Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. -Line: 1 Col: 20 Unexpected end tag (strong) in table context caused voodoo mode. -Line: 1 Col: 20 End tag (strong) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 24 Unexpected end tag (b) in table context caused voodoo mode. -Line: 1 Col: 24 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 29 Unexpected end tag (em) in table context caused voodoo mode. -Line: 1 Col: 29 End tag (em) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 33 Unexpected end tag (i) in table context caused voodoo mode. -Line: 1 Col: 33 End tag (i) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 37 Unexpected end tag (u) in table context caused voodoo mode. -Line: 1 Col: 37 End tag (u) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 46 Unexpected end tag (strike) in table context caused voodoo mode. -Line: 1 Col: 46 End tag (strike) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 50 Unexpected end tag (s) in table context caused voodoo mode. -Line: 1 Col: 50 End tag (s) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 58 Unexpected end tag (blink) in table context caused voodoo mode. -Line: 1 Col: 58 Unexpected end tag (blink). Ignored. -Line: 1 Col: 63 Unexpected end tag (tt) in table context caused voodoo mode. -Line: 1 Col: 63 End tag (tt) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 69 Unexpected end tag (pre) in table context caused voodoo mode. -Line: 1 Col: 69 End tag (pre) seen too early. Expected other end tag. -Line: 1 Col: 75 Unexpected end tag (big) in table context caused voodoo mode. -Line: 1 Col: 75 End tag (big) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 83 Unexpected end tag (small) in table context caused voodoo mode. -Line: 1 Col: 83 End tag (small) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 90 Unexpected end tag (font) in table context caused voodoo mode. -Line: 1 Col: 90 End tag (font) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 99 Unexpected end tag (select) in table context caused voodoo mode. -Line: 1 Col: 99 Unexpected end tag (select). Ignored. -Line: 1 Col: 104 Unexpected end tag (h1) in table context caused voodoo mode. -Line: 1 Col: 104 End tag (h1) seen too early. Expected other end tag. -Line: 1 Col: 109 Unexpected end tag (h2) in table context caused voodoo mode. -Line: 1 Col: 109 End tag (h2) seen too early. Expected other end tag. -Line: 1 Col: 114 Unexpected end tag (h3) in table context caused voodoo mode. -Line: 1 Col: 114 End tag (h3) seen too early. Expected other end tag. -Line: 1 Col: 119 Unexpected end tag (h4) in table context caused voodoo mode. -Line: 1 Col: 119 End tag (h4) seen too early. Expected other end tag. -Line: 1 Col: 124 Unexpected end tag (h5) in table context caused voodoo mode. -Line: 1 Col: 124 End tag (h5) seen too early. Expected other end tag. -Line: 1 Col: 129 Unexpected end tag (h6) in table context caused voodoo mode. -Line: 1 Col: 129 End tag (h6) seen too early. Expected other end tag. -Line: 1 Col: 136 Unexpected end tag (body) in the table row phase. Ignored. -Line: 1 Col: 141 Unexpected end tag (br) in table context caused voodoo mode. -Line: 1 Col: 141 Unexpected end tag (br). Treated as br element. -Line: 1 Col: 145 Unexpected end tag (a) in table context caused voodoo mode. -Line: 1 Col: 145 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 151 Unexpected end tag (img) in table context caused voodoo mode. -Line: 1 Col: 151 This element (img) has no end tag. -Line: 1 Col: 159 Unexpected end tag (title) in table context caused voodoo mode. -Line: 1 Col: 159 Unexpected end tag (title). Ignored. -Line: 1 Col: 166 Unexpected end tag (span) in table context caused voodoo mode. -Line: 1 Col: 166 Unexpected end tag (span). Ignored. -Line: 1 Col: 174 Unexpected end tag (style) in table context caused voodoo mode. -Line: 1 Col: 174 Unexpected end tag (style). Ignored. -Line: 1 Col: 183 Unexpected end tag (script) in table context caused voodoo mode. -Line: 1 Col: 183 Unexpected end tag (script). Ignored. -Line: 1 Col: 196 Unexpected end tag (th). Ignored. -Line: 1 Col: 201 Unexpected end tag (td). Ignored. -Line: 1 Col: 206 Unexpected end tag (tr). Ignored. -Line: 1 Col: 214 This element (frame) has no end tag. -Line: 1 Col: 221 This element (area) has no end tag. -Line: 1 Col: 228 Unexpected end tag (link). Ignored. -Line: 1 Col: 236 This element (param) has no end tag. -Line: 1 Col: 241 This element (hr) has no end tag. -Line: 1 Col: 249 This element (input) has no end tag. -Line: 1 Col: 255 Unexpected end tag (col). Ignored. -Line: 1 Col: 262 Unexpected end tag (base). Ignored. -Line: 1 Col: 269 Unexpected end tag (meta). Ignored. -Line: 1 Col: 280 This element (basefont) has no end tag. -Line: 1 Col: 290 This element (bgsound) has no end tag. -Line: 1 Col: 298 This element (embed) has no end tag. -Line: 1 Col: 307 This element (spacer) has no end tag. -Line: 1 Col: 311 Unexpected end tag (p). Ignored. -Line: 1 Col: 316 End tag (dd) seen too early. Expected other end tag. -Line: 1 Col: 321 End tag (dt) seen too early. Expected other end tag. -Line: 1 Col: 331 Unexpected end tag (caption). Ignored. -Line: 1 Col: 342 Unexpected end tag (colgroup). Ignored. -Line: 1 Col: 350 Unexpected end tag (tbody). Ignored. -Line: 1 Col: 358 Unexpected end tag (tfoot). Ignored. -Line: 1 Col: 366 Unexpected end tag (thead). Ignored. -Line: 1 Col: 376 End tag (address) seen too early. Expected other end tag. -Line: 1 Col: 389 End tag (blockquote) seen too early. Expected other end tag. -Line: 1 Col: 398 End tag (center) seen too early. Expected other end tag. -Line: 1 Col: 404 Unexpected end tag (dir). Ignored. -Line: 1 Col: 410 End tag (div) seen too early. Expected other end tag. -Line: 1 Col: 415 End tag (dl) seen too early. Expected other end tag. -Line: 1 Col: 426 End tag (fieldset) seen too early. Expected other end tag. -Line: 1 Col: 436 End tag (listing) seen too early. Expected other end tag. -Line: 1 Col: 443 End tag (menu) seen too early. Expected other end tag. -Line: 1 Col: 448 End tag (ol) seen too early. Expected other end tag. -Line: 1 Col: 453 End tag (ul) seen too early. Expected other end tag. -Line: 1 Col: 458 End tag (li) seen too early. Expected other end tag. -Line: 1 Col: 465 End tag (nobr) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 471 This element (wbr) has no end tag. -Line: 1 Col: 487 End tag (button) seen too early. Expected other end tag. -Line: 1 Col: 497 End tag (marquee) seen too early. Expected other end tag. -Line: 1 Col: 506 End tag (object) seen too early. Expected other end tag. -Line: 1 Col: 524 Unexpected end tag (html). Ignored. -Line: 1 Col: 524 Unexpected end tag (frameset). Ignored. -Line: 1 Col: 531 Unexpected end tag (head). Ignored. -Line: 1 Col: 540 Unexpected end tag (iframe). Ignored. -Line: 1 Col: 548 This element (image) has no end tag. -Line: 1 Col: 558 This element (isindex) has no end tag. -Line: 1 Col: 568 Unexpected end tag (noembed). Ignored. -Line: 1 Col: 579 Unexpected end tag (noframes). Ignored. -Line: 1 Col: 590 Unexpected end tag (noscript). Ignored. -Line: 1 Col: 601 Unexpected end tag (optgroup). Ignored. -Line: 1 Col: 610 Unexpected end tag (option). Ignored. -Line: 1 Col: 622 Unexpected end tag (plaintext). Ignored. -Line: 1 Col: 633 Unexpected end tag (textarea). Ignored. -#document -| -| -| -|
-| -| -| -|

- -#data - -#errors -Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE. -Line: 1 Col: 10 Expected closing tag. Unexpected end of file. -#document -| -| -| diff --git a/src/pkg/html/testdata/webkit/tests10.dat b/src/pkg/html/testdata/webkit/tests10.dat deleted file mode 100644 index 4f8df86f2..000000000 --- a/src/pkg/html/testdata/webkit/tests10.dat +++ /dev/null @@ -1,799 +0,0 @@ -#data - -#errors -#document -| -| -| -| -| - -#data -a -#errors -29: Bogus comment -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| - -#data - -#errors -35: Stray “svg” start tag. -42: Stray end tag “svg” -#document -| -| -| -| -| -#errors -43: Stray “svg” start tag. -50: Stray end tag “svg” -#document -| -| -| -| -|

-#errors -34: Start tag “svg” seen in “table”. -41: Stray end tag “svg”. -#document -| -| -| -| -| -| - -#data -
foo
-#errors -34: Start tag “svg” seen in “table”. -46: Stray end tag “g”. -53: Stray end tag “svg”. -#document -| -| -| -| -| -| -| "foo" -| - -#data -
foobar
-#errors -34: Start tag “svg” seen in “table”. -46: Stray end tag “g”. -58: Stray end tag “g”. -65: Stray end tag “svg”. -#document -| -| -| -| -| -| -| "foo" -| -| "bar" -| - -#data -
foobar
-#errors -41: Start tag “svg” seen in “table”. -53: Stray end tag “g”. -65: Stray end tag “g”. -72: Stray end tag “svg”. -#document -| -| -| -| -| -| -| "foo" -| -| "bar" -| -| - -#data -
foobar
-#errors -45: Start tag “svg” seen in “table”. -57: Stray end tag “g”. -69: Stray end tag “g”. -76: Stray end tag “svg”. -#document -| -| -| -| -| -| -| "foo" -| -| "bar" -| -| -| - -#data -
foobar
-#errors -#document -| -| -| -| -| -| -| -|
-| -| -| "foo" -| -| "bar" - -#data -
foobar

baz

-#errors -#document -| -| -| -| -| -| -| -|
-| -| -| "foo" -| -| "bar" -|

-| "baz" - -#data -
foobar

baz

-#errors -#document -| -| -| -| -| -|
-| -| -| "foo" -| -| "bar" -|

-| "baz" - -#data -
foobar

baz

quux -#errors -70: HTML start tag “p” in a foreign namespace context. -81: “table” closed but “caption” was still open. -#document -| -| -| -| -| -|
-| -| -| "foo" -| -| "bar" -|

-| "baz" -|

-| "quux" - -#data -
foobarbaz

quux -#errors -78: “table” closed but “caption” was still open. -78: Unclosed elements on stack. -#document -| -| -| -| -| -|
-| -| -| "foo" -| -| "bar" -| "baz" -|

-| "quux" - -#data -foobar

baz

quux -#errors -44: Start tag “svg” seen in “table”. -56: Stray end tag “g”. -68: Stray end tag “g”. -71: HTML start tag “p” in a foreign namespace context. -71: Start tag “p” seen in “table”. -#document -| -| -| -| -| -| -| "foo" -| -| "bar" -|

-| "baz" -| -| -|

-| "quux" - -#data -

quux -#errors -50: Stray “svg” start tag. -54: Stray “g” start tag. -62: Stray end tag “g” -66: Stray “g” start tag. -74: Stray end tag “g” -77: Stray “p” start tag. -88: “table” end tag with “select” open. -#document -| -| -| -| -| -| -| -|
-|

quux -#errors -36: Start tag “select” seen in “table”. -42: Stray “svg” start tag. -46: Stray “g” start tag. -54: Stray end tag “g” -58: Stray “g” start tag. -66: Stray end tag “g” -69: Stray “p” start tag. -80: “table” end tag with “select” open. -#document -| -| -| -| -| -|

-| "quux" - -#data -foobar

baz -#errors -41: Stray “svg” start tag. -68: HTML start tag “p” in a foreign namespace context. -#document -| -| -| -| -| -| -| "foo" -| -| "bar" -|

-| "baz" - -#data -foobar

baz -#errors -34: Stray “svg” start tag. -61: HTML start tag “p” in a foreign namespace context. -#document -| -| -| -| -| -| -| "foo" -| -| "bar" -|

-| "baz" - -#data -

-#errors -31: Stray “svg” start tag. -35: Stray “g” start tag. -40: Stray end tag “g” -44: Stray “g” start tag. -49: Stray end tag “g” -52: Stray “p” start tag. -58: Stray “span” start tag. -58: End of file seen and there were open elements. -#document -| -| -| -| - -#data -

-#errors -42: Stray “svg” start tag. -46: Stray “g” start tag. -51: Stray end tag “g” -55: Stray “g” start tag. -60: Stray end tag “g” -63: Stray “p” start tag. -69: Stray “span” start tag. -#document -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| xlink:href="foo" -| -| xlink href="foo" - -#data - -#errors -#document -| -| -| -| -| xlink:href="foo" -| xml:lang="en" -| -| -| xlink href="foo" -| xml lang="en" - -#data - -#errors -#document -| -| -| -| -| xlink:href="foo" -| xml:lang="en" -| -| -| xlink href="foo" -| xml lang="en" - -#data -bar -#errors -#document -| -| -| -| -| xlink:href="foo" -| xml:lang="en" -| -| -| xlink href="foo" -| xml lang="en" -| "bar" - -#data - -#errors -#document -| -| -| -| - -#data -

a -#errors -#document -| -| -| -|
-| -| "a" - -#data -
a -#errors -#document -| -| -| -|
-| -| -| "a" - -#data -
-#errors -#document -| -| -| -|
-| -| -| - -#data -
a -#errors -#document -| -| -| -|
-| -| -| -| -| "a" - -#data -

a -#errors -#document -| -| -| -|

-| -| -| -|

-| "a" - -#data -
    a -#errors -40: HTML start tag “ul” in a foreign namespace context. -41: End of file in a foreign namespace context. -#document -| -| -| -| -| -| -|
    -| -|
      -| "a" - -#data -
        a -#errors -35: HTML start tag “ul” in a foreign namespace context. -36: End of file in a foreign namespace context. -#document -| -| -| -| -| -| -| -|
          -| "a" - -#data -

          -#errors -#document -| -| -| -| -|

          -| -| -|

          - -#data -

          -#errors -#document -| -| -| -| -|

          -| -| -|

          - -#data -

          -#errors -#document -| -| -| -|

          -| -| -| -|

          -|

          - -#data -
          -#errors -#document -| -| -| -| -| -|
          -| -|
          -| -| - -#data -
          -#errors -#document -| -| -| -| -| -| -| -|
          -|
          -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data -

-#errors -#document -| -| -| -| -|
-| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| -| - -#data -
-#errors -#document -| -| -| -| -| -| -| -|
-| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| -| -| -| -| -| -| -| -| diff --git a/src/pkg/html/testdata/webkit/tests11.dat b/src/pkg/html/testdata/webkit/tests11.dat deleted file mode 100644 index 638cde479..000000000 --- a/src/pkg/html/testdata/webkit/tests11.dat +++ /dev/null @@ -1,482 +0,0 @@ -#data - -#errors -#document -| -| -| -| -| -| attributeName="" -| attributeType="" -| baseFrequency="" -| baseProfile="" -| calcMode="" -| clipPathUnits="" -| contentScriptType="" -| contentStyleType="" -| diffuseConstant="" -| edgeMode="" -| externalResourcesRequired="" -| filterRes="" -| filterUnits="" -| glyphRef="" -| gradientTransform="" -| gradientUnits="" -| kernelMatrix="" -| kernelUnitLength="" -| keyPoints="" -| keySplines="" -| keyTimes="" -| lengthAdjust="" -| limitingConeAngle="" -| markerHeight="" -| markerUnits="" -| markerWidth="" -| maskContentUnits="" -| maskUnits="" -| numOctaves="" -| pathLength="" -| patternContentUnits="" -| patternTransform="" -| patternUnits="" -| pointsAtX="" -| pointsAtY="" -| pointsAtZ="" -| preserveAlpha="" -| preserveAspectRatio="" -| primitiveUnits="" -| refX="" -| refY="" -| repeatCount="" -| repeatDur="" -| requiredExtensions="" -| requiredFeatures="" -| specularConstant="" -| specularExponent="" -| spreadMethod="" -| startOffset="" -| stdDeviation="" -| stitchTiles="" -| surfaceScale="" -| systemLanguage="" -| tableValues="" -| targetX="" -| targetY="" -| textLength="" -| viewBox="" -| viewTarget="" -| xChannelSelector="" -| yChannelSelector="" -| zoomAndPan="" - -#data - -#errors -#document -| -| -| -| -| -| attributeName="" -| attributeType="" -| baseFrequency="" -| baseProfile="" -| calcMode="" -| clipPathUnits="" -| contentScriptType="" -| contentStyleType="" -| diffuseConstant="" -| edgeMode="" -| externalResourcesRequired="" -| filterRes="" -| filterUnits="" -| glyphRef="" -| gradientTransform="" -| gradientUnits="" -| kernelMatrix="" -| kernelUnitLength="" -| keyPoints="" -| keySplines="" -| keyTimes="" -| lengthAdjust="" -| limitingConeAngle="" -| markerHeight="" -| markerUnits="" -| markerWidth="" -| maskContentUnits="" -| maskUnits="" -| numOctaves="" -| pathLength="" -| patternContentUnits="" -| patternTransform="" -| patternUnits="" -| pointsAtX="" -| pointsAtY="" -| pointsAtZ="" -| preserveAlpha="" -| preserveAspectRatio="" -| primitiveUnits="" -| refX="" -| refY="" -| repeatCount="" -| repeatDur="" -| requiredExtensions="" -| requiredFeatures="" -| specularConstant="" -| specularExponent="" -| spreadMethod="" -| startOffset="" -| stdDeviation="" -| stitchTiles="" -| surfaceScale="" -| systemLanguage="" -| tableValues="" -| targetX="" -| targetY="" -| textLength="" -| viewBox="" -| viewTarget="" -| xChannelSelector="" -| yChannelSelector="" -| zoomAndPan="" - -#data - -#errors -#document -| -| -| -| -| -| attributeName="" -| attributeType="" -| baseFrequency="" -| baseProfile="" -| calcMode="" -| clipPathUnits="" -| contentScriptType="" -| contentStyleType="" -| diffuseConstant="" -| edgeMode="" -| externalResourcesRequired="" -| filterRes="" -| filterUnits="" -| glyphRef="" -| gradientTransform="" -| gradientUnits="" -| kernelMatrix="" -| kernelUnitLength="" -| keyPoints="" -| keySplines="" -| keyTimes="" -| lengthAdjust="" -| limitingConeAngle="" -| markerHeight="" -| markerUnits="" -| markerWidth="" -| maskContentUnits="" -| maskUnits="" -| numOctaves="" -| pathLength="" -| patternContentUnits="" -| patternTransform="" -| patternUnits="" -| pointsAtX="" -| pointsAtY="" -| pointsAtZ="" -| preserveAlpha="" -| preserveAspectRatio="" -| primitiveUnits="" -| refX="" -| refY="" -| repeatCount="" -| repeatDur="" -| requiredExtensions="" -| requiredFeatures="" -| specularConstant="" -| specularExponent="" -| spreadMethod="" -| startOffset="" -| stdDeviation="" -| stitchTiles="" -| surfaceScale="" -| systemLanguage="" -| tableValues="" -| targetX="" -| targetY="" -| textLength="" -| viewBox="" -| viewTarget="" -| xChannelSelector="" -| yChannelSelector="" -| zoomAndPan="" - -#data - -#errors -#document -| -| -| -| -| -| attributename="" -| attributetype="" -| basefrequency="" -| baseprofile="" -| calcmode="" -| clippathunits="" -| contentscripttype="" -| contentstyletype="" -| diffuseconstant="" -| edgemode="" -| externalresourcesrequired="" -| filterres="" -| filterunits="" -| glyphref="" -| gradienttransform="" -| gradientunits="" -| kernelmatrix="" -| kernelunitlength="" -| keypoints="" -| keysplines="" -| keytimes="" -| lengthadjust="" -| limitingconeangle="" -| markerheight="" -| markerunits="" -| markerwidth="" -| maskcontentunits="" -| maskunits="" -| numoctaves="" -| pathlength="" -| patterncontentunits="" -| patterntransform="" -| patternunits="" -| pointsatx="" -| pointsaty="" -| pointsatz="" -| preservealpha="" -| preserveaspectratio="" -| primitiveunits="" -| refx="" -| refy="" -| repeatcount="" -| repeatdur="" -| requiredextensions="" -| requiredfeatures="" -| specularconstant="" -| specularexponent="" -| spreadmethod="" -| startoffset="" -| stddeviation="" -| stitchtiles="" -| surfacescale="" -| systemlanguage="" -| tablevalues="" -| targetx="" -| targety="" -| textlength="" -| viewbox="" -| viewtarget="" -| xchannelselector="" -| ychannelselector="" -| zoomandpan="" - -#data - -#errors -#document -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| diff --git a/src/pkg/html/testdata/webkit/tests12.dat b/src/pkg/html/testdata/webkit/tests12.dat deleted file mode 100644 index 63107d277..000000000 --- a/src/pkg/html/testdata/webkit/tests12.dat +++ /dev/null @@ -1,62 +0,0 @@ -#data -

foobazeggs

spam

quuxbar -#errors -#document -| -| -| -| -|

-| "foo" -| -| -| -| "baz" -| -| -| -| -| "eggs" -| -| -|

-| "spam" -| -| -| -|
-| -| -| "quux" -| "bar" - -#data -foobazeggs

spam
quuxbar -#errors -#document -| -| -| -| -| "foo" -| -| -| -| "baz" -| -| -| -| -| "eggs" -| -| -|

-| "spam" -| -| -| -|
-| -| -| "quux" -| "bar" diff --git a/src/pkg/html/testdata/webkit/tests14.dat b/src/pkg/html/testdata/webkit/tests14.dat deleted file mode 100644 index b8713f885..000000000 --- a/src/pkg/html/testdata/webkit/tests14.dat +++ /dev/null @@ -1,74 +0,0 @@ -#data - -#errors -#document -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -15: Unexpected start tag html -#document -| -| -| abc:def="gh" -| -| -| - -#data - -#errors -15: Unexpected start tag html -#document -| -| -| xml:lang="bar" -| -| - -#data - -#errors -#document -| -| -| 123="456" -| -| - -#data - -#errors -#document -| -| -| 123="456" -| 789="012" -| -| - -#data - -#errors -#document -| -| -| -| -| 789="012" diff --git a/src/pkg/html/testdata/webkit/tests15.dat b/src/pkg/html/testdata/webkit/tests15.dat deleted file mode 100644 index 6ce1c0d16..000000000 --- a/src/pkg/html/testdata/webkit/tests15.dat +++ /dev/null @@ -1,208 +0,0 @@ -#data -

X -#errors -Line: 1 Col: 31 Unexpected end tag (p). Ignored. -Line: 1 Col: 36 Expected closing tag. Unexpected end of file. -#document -| -| -| -| -|

-| -| -| -| -| -| -| " " -|

-| "X" - -#data -

-

X -#errors -Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE. -Line: 1 Col: 16 Unexpected end tag (p). Ignored. -Line: 2 Col: 4 Expected closing tag. Unexpected end of file. -#document -| -| -| -|

-| -| -| -| -| -| -| " -" -|

-| "X" - -#data - -#errors -Line: 1 Col: 22 Unexpected end tag (html) after the (implied) root element. -#document -| -| -| -| -| " " - -#data - -#errors -Line: 1 Col: 22 Unexpected end tag (body) after the (implied) root element. -#document -| -| -| -| -| - -#data - -#errors -Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. -Line: 1 Col: 13 Unexpected end tag (html) after the (implied) root element. -#document -| -| -| -| - -#data -X -#errors -Line: 1 Col: 22 Unexpected end tag (body) after the (implied) root element. -#document -| -| -| -| -| -| "X" - -#data -<!doctype html><table> X<meta></table> -#errors -Line: 1 Col: 24 Unexpected non-space characters in table context caused voodoo mode. -Line: 1 Col: 30 Unexpected start tag (meta) in table context caused voodoo mode. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| " X" -| <meta> -| <table> - -#data -<!doctype html><table> x</table> -#errors -Line: 1 Col: 24 Unexpected non-space characters in table context caused voodoo mode. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| " x" -| <table> - -#data -<!doctype html><table> x </table> -#errors -Line: 1 Col: 25 Unexpected non-space characters in table context caused voodoo mode. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| " x " -| <table> - -#data -<!doctype html><table><tr> x</table> -#errors -Line: 1 Col: 28 Unexpected non-space characters in table context caused voodoo mode. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| " x" -| <table> -| <tbody> -| <tr> - -#data -<!doctype html><table>X<style> <tr>x </style> </table> -#errors -Line: 1 Col: 23 Unexpected non-space characters in table context caused voodoo mode. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "X" -| <table> -| <style> -| " <tr>x " -| " " - -#data -<!doctype html><div><table><a>foo</a> <tr><td>bar</td> </tr></table></div> -#errors -Line: 1 Col: 30 Unexpected start tag (a) in table context caused voodoo mode. -Line: 1 Col: 37 Unexpected end tag (a) in table context caused voodoo mode. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <div> -| <a> -| "foo" -| <table> -| " " -| <tbody> -| <tr> -| <td> -| "bar" -| " " - -#data -<frame></frame></frame><frameset><frame><frameset><frame></frameset><noframes></frameset><noframes> -#errors -6: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>”. -13: Stray start tag “frame”. -21: Stray end tag “frame”. -29: Stray end tag “frame”. -39: “frameset” start tag after “body” already open. -105: End of file seen inside an [R]CDATA element. -105: End of file seen and there were open elements. -XXX: These errors are wrong, please fix me! -#document -| <html> -| <head> -| <frameset> -| <frame> -| <frameset> -| <frame> -| <noframes> -| "</frameset><noframes>" - -#data -<!DOCTYPE html><object></html> -#errors -1: Expected closing tag. Unexpected end of file -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <object> diff --git a/src/pkg/html/testdata/webkit/tests16.dat b/src/pkg/html/testdata/webkit/tests16.dat deleted file mode 100644 index 937dba9f4..000000000 --- a/src/pkg/html/testdata/webkit/tests16.dat +++ /dev/null @@ -1,2277 +0,0 @@ -#data -<!doctype html><script> -#errors -Line: 1 Col: 23 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| <body> - -#data -<!doctype html><script>a -#errors -Line: 1 Col: 24 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "a" -| <body> - -#data -<!doctype html><script>< -#errors -Line: 1 Col: 24 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<" -| <body> - -#data -<!doctype html><script></ -#errors -Line: 1 Col: 25 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</" -| <body> - -#data -<!doctype html><script></S -#errors -Line: 1 Col: 26 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</S" -| <body> - -#data -<!doctype html><script></SC -#errors -Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</SC" -| <body> - -#data -<!doctype html><script></SCR -#errors -Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</SCR" -| <body> - -#data -<!doctype html><script></SCRI -#errors -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</SCRI" -| <body> - -#data -<!doctype html><script></SCRIP -#errors -Line: 1 Col: 30 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</SCRIP" -| <body> - -#data -<!doctype html><script></SCRIPT -#errors -Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</SCRIPT" -| <body> - -#data -<!doctype html><script></SCRIPT -#errors -Line: 1 Col: 32 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| <body> - -#data -<!doctype html><script></s -#errors -Line: 1 Col: 26 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</s" -| <body> - -#data -<!doctype html><script></sc -#errors -Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</sc" -| <body> - -#data -<!doctype html><script></scr -#errors -Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</scr" -| <body> - -#data -<!doctype html><script></scri -#errors -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</scri" -| <body> - -#data -<!doctype html><script></scrip -#errors -Line: 1 Col: 30 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</scrip" -| <body> - -#data -<!doctype html><script></script -#errors -Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</script" -| <body> - -#data -<!doctype html><script></script -#errors -Line: 1 Col: 32 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| <body> - -#data -<!doctype html><script><! -#errors -Line: 1 Col: 25 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!" -| <body> - -#data -<!doctype html><script><!a -#errors -Line: 1 Col: 26 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!a" -| <body> - -#data -<!doctype html><script><!- -#errors -Line: 1 Col: 26 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!-" -| <body> - -#data -<!doctype html><script><!-a -#errors -Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!-a" -| <body> - -#data -<!doctype html><script><!-- -#errors -Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--" -| <body> - -#data -<!doctype html><script><!--a -#errors -Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--a" -| <body> - -#data -<!doctype html><script><!--< -#errors -Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<" -| <body> - -#data -<!doctype html><script><!--<a -#errors -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<a" -| <body> - -#data -<!doctype html><script><!--</ -#errors -Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--</" -| <body> - -#data -<!doctype html><script><!--</script -#errors -Line: 1 Col: 35 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--</script" -| <body> - -#data -<!doctype html><script><!--</script -#errors -Line: 1 Col: 36 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--" -| <body> - -#data -<!doctype html><script><!--<s -#errors -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<s" -| <body> - -#data -<!doctype html><script><!--<script -#errors -Line: 1 Col: 34 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script" -| <body> - -#data -<!doctype html><script><!--<script -#errors -Line: 1 Col: 35 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script " -| <body> - -#data -<!doctype html><script><!--<script < -#errors -Line: 1 Col: 36 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script <" -| <body> - -#data -<!doctype html><script><!--<script <a -#errors -Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script <a" -| <body> - -#data -<!doctype html><script><!--<script </ -#errors -Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </" -| <body> - -#data -<!doctype html><script><!--<script </s -#errors -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </s" -| <body> - -#data -<!doctype html><script><!--<script </script -#errors -Line: 1 Col: 43 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script" -| <body> - -#data -<!doctype html><script><!--<script </scripta -#errors -Line: 1 Col: 44 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </scripta" -| <body> - -#data -<!doctype html><script><!--<script </script -#errors -Line: 1 Col: 44 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<!doctype html><script><!--<script </script> -#errors -Line: 1 Col: 44 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script>" -| <body> - -#data -<!doctype html><script><!--<script </script/ -#errors -Line: 1 Col: 44 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script/" -| <body> - -#data -<!doctype html><script><!--<script </script < -#errors -Line: 1 Col: 45 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script <" -| <body> - -#data -<!doctype html><script><!--<script </script <a -#errors -Line: 1 Col: 46 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script <a" -| <body> - -#data -<!doctype html><script><!--<script </script </ -#errors -Line: 1 Col: 46 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script </" -| <body> - -#data -<!doctype html><script><!--<script </script </script -#errors -Line: 1 Col: 52 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script </script" -| <body> - -#data -<!doctype html><script><!--<script </script </script -#errors -Line: 1 Col: 53 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<!doctype html><script><!--<script </script </script/ -#errors -Line: 1 Col: 53 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<!doctype html><script><!--<script </script </script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<!doctype html><script><!--<script - -#errors -Line: 1 Col: 36 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -" -| <body> - -#data -<!doctype html><script><!--<script -a -#errors -Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -a" -| <body> - -#data -<!doctype html><script><!--<script -< -#errors -Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -<" -| <body> - -#data -<!doctype html><script><!--<script -- -#errors -Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script --" -| <body> - -#data -<!doctype html><script><!--<script --a -#errors -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script --a" -| <body> - -#data -<!doctype html><script><!--<script --< -#errors -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script --<" -| <body> - -#data -<!doctype html><script><!--<script --> -#errors -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<!doctype html><script><!--<script -->< -#errors -Line: 1 Col: 39 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script --><" -| <body> - -#data -<!doctype html><script><!--<script --></ -#errors -Line: 1 Col: 40 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script --></" -| <body> - -#data -<!doctype html><script><!--<script --></script -#errors -Line: 1 Col: 46 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script --></script" -| <body> - -#data -<!doctype html><script><!--<script --></script -#errors -Line: 1 Col: 47 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<!doctype html><script><!--<script --></script/ -#errors -Line: 1 Col: 47 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<!doctype html><script><!--<script --></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<!doctype html><script><!--<script><\/script>--></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script><\/script>-->" -| <body> - -#data -<!doctype html><script><!--<script></scr'+'ipt>--></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></scr'+'ipt>-->" -| <body> - -#data -<!doctype html><script><!--<script></script><script></script></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>" -| <body> - -#data -<!doctype html><script><!--<script></script><script></script>--><!--</script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>--><!--" -| <body> - -#data -<!doctype html><script><!--<script></script><script></script>-- ></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>-- >" -| <body> - -#data -<!doctype html><script><!--<script></script><script></script>- -></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>- ->" -| <body> - -#data -<!doctype html><script><!--<script></script><script></script>- - ></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>- - >" -| <body> - -#data -<!doctype html><script><!--<script></script><script></script>-></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>->" -| <body> - -#data -<!doctype html><script><!--<script>--!></script>X -#errors -Line: 1 Col: 49 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script>--!></script>X" -| <body> - -#data -<!doctype html><script><!--<scr'+'ipt></script>--></script> -#errors -Line: 1 Col: 59 Unexpected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<scr'+'ipt>" -| <body> -| "-->" - -#data -<!doctype html><script><!--<script></scr'+'ipt></script>X -#errors -Line: 1 Col: 57 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></scr'+'ipt></script>X" -| <body> - -#data -<!doctype html><style><!--<style></style>--></style> -#errors -Line: 1 Col: 52 Unexpected end tag (style). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "<!--<style>" -| <body> -| "-->" - -#data -<!doctype html><style><!--</style>X -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "<!--" -| <body> -| "X" - -#data -<!doctype html><style><!--...</style>...--></style> -#errors -Line: 1 Col: 51 Unexpected end tag (style). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "<!--..." -| <body> -| "...-->" - -#data -<!doctype html><style><!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style></style>X -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "<!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style>" -| <body> -| "X" - -#data -<!doctype html><style><!--...<style><!--...--!></style>--></style> -#errors -Line: 1 Col: 66 Unexpected end tag (style). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "<!--...<style><!--...--!>" -| <body> -| "-->" - -#data -<!doctype html><style><!--...</style><!-- --><style>@import ...</style> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "<!--..." -| <!-- --> -| <style> -| "@import ..." -| <body> - -#data -<!doctype html><style>...<style><!--...</style><!-- --></style> -#errors -Line: 1 Col: 63 Unexpected end tag (style). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "...<style><!--..." -| <!-- --> -| <body> - -#data -<!doctype html><style>...<!--[if IE]><style>...</style>X -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "...<!--[if IE]><style>..." -| <body> -| "X" - -#data -<!doctype html><title><!--<title>--> -#errors -Line: 1 Col: 52 Unexpected end tag (title). -#document -| -| -| -| -| "<!--<title>" -| <body> -| "-->" - -#data -<!doctype html><title></title> -#errors -#document -| -| -| -| -| "" -| - -#data -foo/title><link></head><body>X -#errors -Line: 1 Col: 52 Unexpected end of file. Expected end tag (title). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <title> -| "foo/title><link></head><body>X" -| <body> - -#data -<!doctype html><noscript><!--<noscript></noscript>--></noscript> -#errors -Line: 1 Col: 64 Unexpected end tag (noscript). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <noscript> -| "<!--<noscript>" -| <body> -| "-->" - -#data -<!doctype html><noscript><!--</noscript>X<noscript>--></noscript> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <noscript> -| "<!--" -| <body> -| "X" -| <noscript> -| "-->" - -#data -<!doctype html><noscript><iframe></noscript>X -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <noscript> -| "<iframe>" -| <body> -| "X" - -#data -<!doctype html><noframes><!--<noframes></noframes>--></noframes> -#errors -Line: 1 Col: 64 Unexpected end tag (noframes). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <noframes> -| "<!--<noframes>" -| <body> -| "-->" - -#data -<!doctype html><noframes><body><script><!--...</script></body></noframes></html> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <noframes> -| "<body><script><!--...</script></body>" -| <body> - -#data -<!doctype html><textarea><!--<textarea></textarea>--></textarea> -#errors -Line: 1 Col: 64 Unexpected end tag (textarea). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <textarea> -| "<!--<textarea>" -| "-->" - -#data -<!doctype html><textarea></textarea></textarea> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <textarea> -| "</textarea>" - -#data -<!doctype html><iframe><!--<iframe></iframe>--></iframe> -#errors -Line: 1 Col: 56 Unexpected end tag (iframe). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <iframe> -| "<!--<iframe>" -| "-->" - -#data -<!doctype html><iframe>...<!--X->...<!--/X->...</iframe> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <iframe> -| "...<!--X->...<!--/X->..." - -#data -<!doctype html><xmp><!--<xmp></xmp>--></xmp> -#errors -Line: 1 Col: 44 Unexpected end tag (xmp). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <xmp> -| "<!--<xmp>" -| "-->" - -#data -<!doctype html><noembed><!--<noembed></noembed>--></noembed> -#errors -Line: 1 Col: 60 Unexpected end tag (noembed). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <noembed> -| "<!--<noembed>" -| "-->" - -#data -<script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 8 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| <body> - -#data -<script>a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 9 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "a" -| <body> - -#data -<script>< -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 9 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<" -| <body> - -#data -<script></ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 10 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</" -| <body> - -#data -<script></S -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</S" -| <body> - -#data -<script></SC -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 12 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</SC" -| <body> - -#data -<script></SCR -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 13 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</SCR" -| <body> - -#data -<script></SCRI -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</SCRI" -| <body> - -#data -<script></SCRIP -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 15 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</SCRIP" -| <body> - -#data -<script></SCRIPT -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 16 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</SCRIPT" -| <body> - -#data -<script></SCRIPT -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 17 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| <body> - -#data -<script></s -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</s" -| <body> - -#data -<script></sc -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 12 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</sc" -| <body> - -#data -<script></scr -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 13 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</scr" -| <body> - -#data -<script></scri -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</scri" -| <body> - -#data -<script></scrip -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 15 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</scrip" -| <body> - -#data -<script></script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 16 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</script" -| <body> - -#data -<script></script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 17 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| <body> - -#data -<script><! -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 10 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!" -| <body> - -#data -<script><!a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!a" -| <body> - -#data -<script><!- -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!-" -| <body> - -#data -<script><!-a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 12 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!-a" -| <body> - -#data -<script><!-- -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 12 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--" -| <body> - -#data -<script><!--a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 13 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--a" -| <body> - -#data -<script><!--< -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 13 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<" -| <body> - -#data -<script><!--<a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<a" -| <body> - -#data -<script><!--</ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--</" -| <body> - -#data -<script><!--</script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 20 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--</script" -| <body> - -#data -<script><!--</script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 21 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--" -| <body> - -#data -<script><!--<s -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<s" -| <body> - -#data -<script><!--<script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 19 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script" -| <body> - -#data -<script><!--<script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 20 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script " -| <body> - -#data -<script><!--<script < -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 21 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script <" -| <body> - -#data -<script><!--<script <a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 22 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script <a" -| <body> - -#data -<script><!--<script </ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 22 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </" -| <body> - -#data -<script><!--<script </s -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 23 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </s" -| <body> - -#data -<script><!--<script </script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script" -| <body> - -#data -<script><!--<script </scripta -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </scripta" -| <body> - -#data -<script><!--<script </script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<script><!--<script </script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script>" -| <body> - -#data -<script><!--<script </script/ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script/" -| <body> - -#data -<script><!--<script </script < -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 30 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script <" -| <body> - -#data -<script><!--<script </script <a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script <a" -| <body> - -#data -<script><!--<script </script </ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script </" -| <body> - -#data -<script><!--<script </script </script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script </script" -| <body> - -#data -<script><!--<script </script </script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<script><!--<script </script </script/ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<script><!--<script </script </script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<script><!--<script - -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 21 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script -" -| <body> - -#data -<script><!--<script -a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 22 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script -a" -| <body> - -#data -<script><!--<script -- -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 22 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script --" -| <body> - -#data -<script><!--<script --a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 23 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script --a" -| <body> - -#data -<script><!--<script --> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 23 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<script><!--<script -->< -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 24 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script --><" -| <body> - -#data -<script><!--<script --></ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 25 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script --></" -| <body> - -#data -<script><!--<script --></script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script --></script" -| <body> - -#data -<script><!--<script --></script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 32 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<script><!--<script --></script/ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 32 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<script><!--<script --></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<script><!--<script><\/script>--></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script><\/script>-->" -| <body> - -#data -<script><!--<script></scr'+'ipt>--></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></scr'+'ipt>-->" -| <body> - -#data -<script><!--<script></script><script></script></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>" -| <body> - -#data -<script><!--<script></script><script></script>--><!--</script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>--><!--" -| <body> - -#data -<script><!--<script></script><script></script>-- ></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>-- >" -| <body> - -#data -<script><!--<script></script><script></script>- -></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>- ->" -| <body> - -#data -<script><!--<script></script><script></script>- - ></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>- - >" -| <body> - -#data -<script><!--<script></script><script></script>-></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>->" -| <body> - -#data -<script><!--<script>--!></script>X -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 34 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script>--!></script>X" -| <body> - -#data -<script><!--<scr'+'ipt></script>--></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 44 Unexpected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<scr'+'ipt>" -| <body> -| "-->" - -#data -<script><!--<script></scr'+'ipt></script>X -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 42 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script></scr'+'ipt></script>X" -| <body> - -#data -<style><!--<style></style>--></style> -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -Line: 1 Col: 37 Unexpected end tag (style). -#document -| <html> -| <head> -| <style> -| "<!--<style>" -| <body> -| "-->" - -#data -<style><!--</style>X -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| <html> -| <head> -| <style> -| "<!--" -| <body> -| "X" - -#data -<style><!--...</style>...--></style> -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -Line: 1 Col: 36 Unexpected end tag (style). -#document -| <html> -| <head> -| <style> -| "<!--..." -| <body> -| "...-->" - -#data -<style><!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style></style>X -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| <html> -| <head> -| <style> -| "<!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style>" -| <body> -| "X" - -#data -<style><!--...<style><!--...--!></style>--></style> -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -Line: 1 Col: 51 Unexpected end tag (style). -#document -| <html> -| <head> -| <style> -| "<!--...<style><!--...--!>" -| <body> -| "-->" - -#data -<style><!--...</style><!-- --><style>@import ...</style> -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| <html> -| <head> -| <style> -| "<!--..." -| <!-- --> -| <style> -| "@import ..." -| <body> - -#data -<style>...<style><!--...</style><!-- --></style> -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -Line: 1 Col: 48 Unexpected end tag (style). -#document -| <html> -| <head> -| <style> -| "...<style><!--..." -| <!-- --> -| <body> - -#data -<style>...<!--[if IE]><style>...</style>X -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| <html> -| <head> -| <style> -| "...<!--[if IE]><style>..." -| <body> -| "X" - -#data -<title><!--<title>--> -#errors -Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. -Line: 1 Col: 37 Unexpected end tag (title). -#document -| -| -| -| "<!--<title>" -| <body> -| "-->" - -#data -<title></title> -#errors -Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. -#document -| -| -| -| "" -| - -#data -foo/title><link></head><body>X -#errors -Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. -Line: 1 Col: 37 Unexpected end of file. Expected end tag (title). -#document -| <html> -| <head> -| <title> -| "foo/title><link></head><body>X" -| <body> - -#data -<noscript><!--<noscript></noscript>--></noscript> -#errors -Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE. -Line: 1 Col: 49 Unexpected end tag (noscript). -#document -| <html> -| <head> -| <noscript> -| "<!--<noscript>" -| <body> -| "-->" - -#data -<noscript><!--</noscript>X<noscript>--></noscript> -#errors -Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE. -#document -| <html> -| <head> -| <noscript> -| "<!--" -| <body> -| "X" -| <noscript> -| "-->" - -#data -<noscript><iframe></noscript>X -#errors -Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE. -#document -| <html> -| <head> -| <noscript> -| "<iframe>" -| <body> -| "X" - -#data -<noframes><!--<noframes></noframes>--></noframes> -#errors -Line: 1 Col: 10 Unexpected start tag (noframes). Expected DOCTYPE. -Line: 1 Col: 49 Unexpected end tag (noframes). -#document -| <html> -| <head> -| <noframes> -| "<!--<noframes>" -| <body> -| "-->" - -#data -<noframes><body><script><!--...</script></body></noframes></html> -#errors -Line: 1 Col: 10 Unexpected start tag (noframes). Expected DOCTYPE. -#document -| <html> -| <head> -| <noframes> -| "<body><script><!--...</script></body>" -| <body> - -#data -<textarea><!--<textarea></textarea>--></textarea> -#errors -Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE. -Line: 1 Col: 49 Unexpected end tag (textarea). -#document -| <html> -| <head> -| <body> -| <textarea> -| "<!--<textarea>" -| "-->" - -#data -<textarea></textarea></textarea> -#errors -Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| <textarea> -| "</textarea>" - -#data -<iframe><!--<iframe></iframe>--></iframe> -#errors -Line: 1 Col: 8 Unexpected start tag (iframe). Expected DOCTYPE. -Line: 1 Col: 41 Unexpected end tag (iframe). -#document -| <html> -| <head> -| <body> -| <iframe> -| "<!--<iframe>" -| "-->" - -#data -<iframe>...<!--X->...<!--/X->...</iframe> -#errors -Line: 1 Col: 8 Unexpected start tag (iframe). Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| <iframe> -| "...<!--X->...<!--/X->..." - -#data -<xmp><!--<xmp></xmp>--></xmp> -#errors -Line: 1 Col: 5 Unexpected start tag (xmp). Expected DOCTYPE. -Line: 1 Col: 29 Unexpected end tag (xmp). -#document -| <html> -| <head> -| <body> -| <xmp> -| "<!--<xmp>" -| "-->" - -#data -<noembed><!--<noembed></noembed>--></noembed> -#errors -Line: 1 Col: 9 Unexpected start tag (noembed). Expected DOCTYPE. -Line: 1 Col: 45 Unexpected end tag (noembed). -#document -| <html> -| <head> -| <body> -| <noembed> -| "<!--<noembed>" -| "-->" - -#data -<!doctype html><table> - -#errors -Line 2 Col 0 Unexpected end of file. Expected table content. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| " -" - -#data -<!doctype html><table><td><span><font></span><span> -#errors -Line 1 Col 26 Unexpected table cell start tag (td) in the table body phase. -Line 1 Col 45 Unexpected end tag (span). -Line 1 Col 51 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| <span> -| <font> -| <font> -| <span> - -#data -<!doctype html><form><table></form><form></table></form> -#errors -35: Stray end tag “form”. -41: Start tag “form” seen in “table”. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <form> -| <table> -| <form> diff --git a/src/pkg/html/testdata/webkit/tests17.dat b/src/pkg/html/testdata/webkit/tests17.dat deleted file mode 100644 index 7b555f888..000000000 --- a/src/pkg/html/testdata/webkit/tests17.dat +++ /dev/null @@ -1,153 +0,0 @@ -#data -<!doctype html><table><tbody><select><tr> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <table> -| <tbody> -| <tr> - -#data -<!doctype html><table><tr><select><td> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <table> -| <tbody> -| <tr> -| <td> - -#data -<!doctype html><table><tr><td><select><td> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| <select> -| <td> - -#data -<!doctype html><table><tr><th><select><td> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <th> -| <select> -| <td> - -#data -<!doctype html><table><caption><select><tr> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <caption> -| <select> -| <tbody> -| <tr> - -#data -<!doctype html><select><tr> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><select><td> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><select><th> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><select><tbody> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><select><thead> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><select><tfoot> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><select><caption> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><table><tr></table>a -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| "a" diff --git a/src/pkg/html/testdata/webkit/tests18.dat b/src/pkg/html/testdata/webkit/tests18.dat deleted file mode 100644 index 680e1f068..000000000 --- a/src/pkg/html/testdata/webkit/tests18.dat +++ /dev/null @@ -1,269 +0,0 @@ -#data -<!doctype html><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <plaintext> -| "</plaintext>" - -#data -<!doctype html><table><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <plaintext> -| "</plaintext>" -| <table> - -#data -<!doctype html><table><tbody><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <plaintext> -| "</plaintext>" -| <table> -| <tbody> - -#data -<!doctype html><table><tbody><tr><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <plaintext> -| "</plaintext>" -| <table> -| <tbody> -| <tr> - -#data -<!doctype html><table><tbody><tr><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <plaintext> -| "</plaintext>" -| <table> -| <tbody> -| <tr> - -#data -<!doctype html><table><td><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| <plaintext> -| "</plaintext>" - -#data -<!doctype html><table><caption><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <caption> -| <plaintext> -| "</plaintext>" - -#data -<!doctype html><table><tr><style></script></style>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "abc" -| <table> -| <tbody> -| <tr> -| <style> -| "</script>" - -#data -<!doctype html><table><tr><script></style></script>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "abc" -| <table> -| <tbody> -| <tr> -| <script> -| "</style>" - -#data -<!doctype html><table><caption><style></script></style>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <caption> -| <style> -| "</script>" -| "abc" - -#data -<!doctype html><table><td><style></script></style>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| <style> -| "</script>" -| "abc" - -#data -<!doctype html><select><script></style></script>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <script> -| "</style>" -| "abc" - -#data -<!doctype html><table><select><script></style></script>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <script> -| "</style>" -| "abc" -| <table> - -#data -<!doctype html><table><tr><select><script></style></script>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <script> -| "</style>" -| "abc" -| <table> -| <tbody> -| <tr> - -#data -<!doctype html><frameset></frameset><noframes>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <noframes> -| "abc" - -#data -<!doctype html><frameset></frameset><noframes>abc</noframes><!--abc--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <noframes> -| "abc" -| <!-- abc --> - -#data -<!doctype html><frameset></frameset></html><noframes>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <noframes> -| "abc" - -#data -<!doctype html><frameset></frameset></html><noframes>abc</noframes><!--abc--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <noframes> -| "abc" -| <!-- abc --> - -#data -<!doctype html><table><tr></tbody><tfoot> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <tfoot> - -#data -<!doctype html><table><td><svg></svg>abc<td> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| <svg svg> -| "abc" -| <td> diff --git a/src/pkg/html/testdata/webkit/tests19.dat b/src/pkg/html/testdata/webkit/tests19.dat deleted file mode 100644 index 06222f5b9..000000000 --- a/src/pkg/html/testdata/webkit/tests19.dat +++ /dev/null @@ -1,1220 +0,0 @@ -#data -<!doctype html><math><mn DefinitionUrl="foo"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <math math> -| <math mn> -| definitionURL="foo" - -#data -<!doctype html><html></p><!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <!-- foo --> -| <head> -| <body> - -#data -<!doctype html><head></head></p><!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <!-- foo --> -| <body> - -#data -<!doctype html><body><p><pre> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <pre> - -#data -<!doctype html><body><p><listing> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <listing> - -#data -<!doctype html><p><plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <plaintext> - -#data -<!doctype html><p><h1> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <h1> - -#data -<!doctype html><form><isindex> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <form> - -#data -<!doctype html><isindex action="POST"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <form> -| action="POST" -| <hr> -| <label> -| "This is a searchable index. Enter search keywords: " -| <input> -| name="isindex" -| <hr> - -#data -<!doctype html><isindex prompt="this is isindex"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <form> -| <hr> -| <label> -| "this is isindex" -| <input> -| name="isindex" -| <hr> - -#data -<!doctype html><isindex type="hidden"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <form> -| <hr> -| <label> -| "This is a searchable index. Enter search keywords: " -| <input> -| name="isindex" -| type="hidden" -| <hr> - -#data -<!doctype html><isindex name="foo"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <form> -| <hr> -| <label> -| "This is a searchable index. Enter search keywords: " -| <input> -| name="isindex" -| <hr> - -#data -<!doctype html><ruby><p><rp> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <ruby> -| <p> -| <rp> - -#data -<!doctype html><ruby><div><span><rp> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <ruby> -| <div> -| <span> -| <rp> - -#data -<!doctype html><ruby><div><p><rp> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <ruby> -| <div> -| <p> -| <rp> - -#data -<!doctype html><ruby><p><rt> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <ruby> -| <p> -| <rt> - -#data -<!doctype html><ruby><div><span><rt> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <ruby> -| <div> -| <span> -| <rt> - -#data -<!doctype html><ruby><div><p><rt> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <ruby> -| <div> -| <p> -| <rt> - -#data -<!doctype html><math/><foo> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <math math> -| <foo> - -#data -<!doctype html><svg/><foo> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <svg svg> -| <foo> - -#data -<!doctype html><div></body><!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <div> -| <!-- foo --> - -#data -<!doctype html><h1><div><h3><span></h1>foo -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <h1> -| <div> -| <h3> -| <span> -| "foo" - -#data -<!doctype html><p></h3>foo -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| "foo" - -#data -<!doctype html><h3><li>abc</h2>foo -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <h3> -| <li> -| "abc" -| "foo" - -#data -<!doctype html><table>abc<!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "abc" -| <table> -| <!-- foo --> - -#data -<!doctype html><table> <!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| " " -| <!-- foo --> - -#data -<!doctype html><table> b <!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| " b " -| <table> -| <!-- foo --> - -#data -<!doctype html><select><option><option> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <option> -| <option> - -#data -<!doctype html><select><option></optgroup> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <option> - -#data -<!doctype html><select><option></optgroup> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <option> - -#data -<!doctype html><p><math><mi><p><h1> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| <math mi> -| <p> -| <h1> - -#data -<!doctype html><p><math><mo><p><h1> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| <math mo> -| <p> -| <h1> - -#data -<!doctype html><p><math><mn><p><h1> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| <math mn> -| <p> -| <h1> - -#data -<!doctype html><p><math><ms><p><h1> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| <math ms> -| <p> -| <h1> - -#data -<!doctype html><p><math><mtext><p><h1> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| <math mtext> -| <p> -| <h1> - -#data -<!doctype html><frameset></noframes> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<!doctype html><html c=d><body></html><html a=b> -#errors -#document -| <!DOCTYPE html> -| <html> -| a="b" -| c="d" -| <head> -| <body> - -#data -<!doctype html><html c=d><frameset></frameset></html><html a=b> -#errors -#document -| <!DOCTYPE html> -| <html> -| a="b" -| c="d" -| <head> -| <frameset> - -#data -<!doctype html><html><frameset></frameset></html><!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <!-- foo --> - -#data -<!doctype html><html><frameset></frameset></html> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| " " - -#data -<!doctype html><html><frameset></frameset></html>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<!doctype html><html><frameset></frameset></html><p> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<!doctype html><html><frameset></frameset></html></p> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<html><frameset></frameset></html><!doctype html> -#errors -#document -| <html> -| <head> -| <frameset> - -#data -<!doctype html><body><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> - -#data -<!doctype html><p><frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <frame> - -#data -<!doctype html><p>a<frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| "a" - -#data -<!doctype html><p> <frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <frame> - -#data -<!doctype html><pre><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <pre> - -#data -<!doctype html><listing><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <listing> - -#data -<!doctype html><li><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <li> - -#data -<!doctype html><dd><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <dd> - -#data -<!doctype html><dt><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <dt> - -#data -<!doctype html><button><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <button> - -#data -<!doctype html><applet><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <applet> - -#data -<!doctype html><marquee><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <marquee> - -#data -<!doctype html><object><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <object> - -#data -<!doctype html><table><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> - -#data -<!doctype html><area><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <area> - -#data -<!doctype html><basefont><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <basefont> -| <frameset> - -#data -<!doctype html><bgsound><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <bgsound> -| <frameset> - -#data -<!doctype html><br><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <br> - -#data -<!doctype html><embed><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <embed> - -#data -<!doctype html><img><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <img> - -#data -<!doctype html><input><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <input> - -#data -<!doctype html><keygen><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <keygen> - -#data -<!doctype html><wbr><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <wbr> - -#data -<!doctype html><hr><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <hr> - -#data -<!doctype html><textarea></textarea><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <textarea> - -#data -<!doctype html><xmp></xmp><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <xmp> - -#data -<!doctype html><iframe></iframe><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <iframe> - -#data -<!doctype html><select></select><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><svg></svg><frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <frame> - -#data -<!doctype html><math></math><frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <frame> - -#data -<!doctype html><svg><foreignObject><div> <frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <frame> - -#data -<!doctype html><svg>a</svg><frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <svg svg> -| "a" - -#data -<!doctype html><svg> </svg><frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <frame> - -#data -<html>aaa<frameset></frameset> -#errors -#document -| <html> -| <head> -| <body> -| "aaa" - -#data -<html> a <frameset></frameset> -#errors -#document -| <html> -| <head> -| <body> -| "a " - -#data -<!doctype html><div><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<!doctype html><div><body><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <div> - -#data -<!doctype html><p><math></p>a -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| "a" - -#data -<!doctype html><p><math><mn><span></p>a -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| <math mn> -| <span> -| <p> -| "a" - -#data -<!doctype html><math></html> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <math math> - -#data -<!doctype html><meta charset="ascii"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <meta> -| charset="ascii" -| <body> - -#data -<!doctype html><meta http-equiv="content-type" content="text/html;charset=ascii"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <meta> -| content="text/html;charset=ascii" -| http-equiv="content-type" -| <body> - -#data -<!doctype html><head><!--aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--><meta charset="utf8"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <!-- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa --> -| <meta> -| charset="utf8" -| <body> - -#data -<!doctype html><html a=b><head></head><html c=d> -#errors -#document -| <!DOCTYPE html> -| <html> -| a="b" -| c="d" -| <head> -| <body> - -#data -<!doctype html><image/> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <img> - -#data -<!doctype html>a<i>b<table>c<b>d</i>e</b>f -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "a" -| <i> -| "bc" -| <b> -| "de" -| "f" -| <table> - -#data -<!doctype html><table><i>a<b>b<div>c<a>d</i>e</b>f -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <i> -| "a" -| <b> -| "b" -| <b> -| <div> -| <b> -| <i> -| "c" -| <a> -| "d" -| <a> -| "e" -| <a> -| "f" -| <table> - -#data -<!doctype html><i>a<b>b<div>c<a>d</i>e</b>f -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <i> -| "a" -| <b> -| "b" -| <b> -| <div> -| <b> -| <i> -| "c" -| <a> -| "d" -| <a> -| "e" -| <a> -| "f" - -#data -<!doctype html><table><i>a<b>b<div>c</i> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <i> -| "a" -| <b> -| "b" -| <b> -| <div> -| <i> -| "c" -| <table> - -#data -<!doctype html><table><i>a<b>b<div>c<a>d</i>e</b>f -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <i> -| "a" -| <b> -| "b" -| <b> -| <div> -| <b> -| <i> -| "c" -| <a> -| "d" -| <a> -| "e" -| <a> -| "f" -| <table> - -#data -<!doctype html><table><i>a<div>b<tr>c<b>d</i>e -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <i> -| "a" -| <div> -| "b" -| <i> -| "c" -| <b> -| "d" -| <b> -| "e" -| <table> -| <tbody> -| <tr> - -#data -<!doctype html><table><td><table><i>a<div>b<b>c</i>d -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| <i> -| "a" -| <div> -| <i> -| "b" -| <b> -| "c" -| <b> -| "d" -| <table> - -#data -<!doctype html><body><bgsound> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <bgsound> - -#data -<!doctype html><body><basefont> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <basefont> - -#data -<!doctype html><a><b></a><basefont> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <a> -| <b> -| <basefont> - -#data -<!doctype html><a><b></a><bgsound> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <a> -| <b> -| <bgsound> - -#data -<!doctype html><figcaption><article></figcaption>a -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <figcaption> -| <article> -| "a" - -#data -<!doctype html><summary><article></summary>a -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <summary> -| <article> -| "a" - -#data -<!doctype html><p><a><plaintext>b -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <a> -| <plaintext> -| <a> -| "b" diff --git a/src/pkg/html/testdata/webkit/tests2.dat b/src/pkg/html/testdata/webkit/tests2.dat deleted file mode 100644 index 60d859221..000000000 --- a/src/pkg/html/testdata/webkit/tests2.dat +++ /dev/null @@ -1,763 +0,0 @@ -#data -<!DOCTYPE html>Test -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "Test" - -#data -<textarea>test</div>test -#errors -Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE. -Line: 1 Col: 24 Expected closing tag. Unexpected end of file. -#document -| <html> -| <head> -| <body> -| <textarea> -| "test</div>test" - -#data -<table><td> -#errors -Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected table cell start tag (td) in the table body phase. -Line: 1 Col: 11 Expected closing tag. Unexpected end of file. -#document -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> - -#data -<table><td>test</tbody></table> -#errors -Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected table cell start tag (td) in the table body phase. -#document -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| "test" - -#data -<frame>test -#errors -Line: 1 Col: 7 Unexpected start tag (frame). Expected DOCTYPE. -Line: 1 Col: 7 Unexpected start tag frame. Ignored. -#document -| <html> -| <head> -| <body> -| "test" - -#data -<!DOCTYPE html><frameset>test -#errors -Line: 1 Col: 29 Unepxected characters in the frameset phase. Characters ignored. -Line: 1 Col: 29 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<!DOCTYPE html><frameset><!DOCTYPE html> -#errors -Line: 1 Col: 40 Unexpected DOCTYPE. Ignored. -Line: 1 Col: 40 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<!DOCTYPE html><font><p><b>test</font> -#errors -Line: 1 Col: 38 End tag (font) violates step 1, paragraph 3 of the adoption agency algorithm. -Line: 1 Col: 38 End tag (font) violates step 1, paragraph 3 of the adoption agency algorithm. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <font> -| <p> -| <font> -| <b> -| "test" - -#data -<!DOCTYPE html><dt><div><dd> -#errors -Line: 1 Col: 28 Missing end tag (div, dt). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <dt> -| <div> -| <dd> - -#data -<script></x -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</x" -| <body> - -#data -<table><plaintext><td> -#errors -Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. -Line: 1 Col: 18 Unexpected start tag (plaintext) in table context caused voodoo mode. -Line: 1 Col: 22 Unexpected end of file. Expected table content. -#document -| <html> -| <head> -| <body> -| <plaintext> -| "<td>" -| <table> - -#data -<plaintext></plaintext> -#errors -Line: 1 Col: 11 Unexpected start tag (plaintext). Expected DOCTYPE. -Line: 1 Col: 23 Expected closing tag. Unexpected end of file. -#document -| <html> -| <head> -| <body> -| <plaintext> -| "</plaintext>" - -#data -<!DOCTYPE html><table><tr>TEST -#errors -Line: 1 Col: 30 Unexpected non-space characters in table context caused voodoo mode. -Line: 1 Col: 30 Unexpected end of file. Expected table content. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "TEST" -| <table> -| <tbody> -| <tr> - -#data -<!DOCTYPE html><body t1=1><body t2=2><body t3=3 t4=4> -#errors -Line: 1 Col: 37 Unexpected start tag (body). -Line: 1 Col: 53 Unexpected start tag (body). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| t1="1" -| t2="2" -| t3="3" -| t4="4" - -#data -</b test -#errors -Line: 1 Col: 8 Unexpected end of file in attribute name. -Line: 1 Col: 8 End tag contains unexpected attributes. -Line: 1 Col: 8 Unexpected end tag (b). Expected DOCTYPE. -Line: 1 Col: 8 Unexpected end tag (b) after the (implied) root element. -#document -| <html> -| <head> -| <body> - -#data -<!DOCTYPE html></b test<b &=&>X -#errors -Line: 1 Col: 32 Named entity didn't end with ';'. -Line: 1 Col: 33 End tag contains unexpected attributes. -Line: 1 Col: 33 Unexpected end tag (b) after the (implied) root element. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "X" - -#data -<!doctypehtml><scrIPt type=text/x-foobar;baz>X</SCRipt -#errors -Line: 1 Col: 9 No space after literal string 'DOCTYPE'. -Line: 1 Col: 54 Unexpected end of file in the tag name. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| type="text/x-foobar;baz" -| "X</SCRipt" -| <body> - -#data -& -#errors -Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&" - -#data -&# -#errors -Line: 1 Col: 1 Numeric entity expected. Got end of file instead. -Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&#" - -#data -&#X -#errors -Line: 1 Col: 3 Numeric entity expected but none found. -Line: 1 Col: 3 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&#X" - -#data -&#x -#errors -Line: 1 Col: 3 Numeric entity expected but none found. -Line: 1 Col: 3 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&#x" - -#data -- -#errors -Line: 1 Col: 4 Numeric entity didn't end with ';'. -Line: 1 Col: 4 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "-" - -#data -&x-test -#errors -Line: 1 Col: 1 Named entity expected. Got none. -Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&x-test" - -#data -<!doctypehtml><p><li> -#errors -Line: 1 Col: 9 No space after literal string 'DOCTYPE'. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <li> - -#data -<!doctypehtml><p><dt> -#errors -Line: 1 Col: 9 No space after literal string 'DOCTYPE'. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <dt> - -#data -<!doctypehtml><p><dd> -#errors -Line: 1 Col: 9 No space after literal string 'DOCTYPE'. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <dd> - -#data -<!doctypehtml><p><form> -#errors -Line: 1 Col: 9 No space after literal string 'DOCTYPE'. -Line: 1 Col: 23 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <form> - -#data -<!DOCTYPE html><p></P>X -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| "X" - -#data -& -#errors -Line: 1 Col: 4 Named entity didn't end with ';'. -Line: 1 Col: 4 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&" - -#data -&AMp; -#errors -Line: 1 Col: 1 Named entity expected. Got none. -Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&AMp;" - -#data -<!DOCTYPE html><html><head></head><body><thisISasillyTESTelementNameToMakeSureCrazyTagNamesArePARSEDcorrectLY> -#errors -Line: 1 Col: 110 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <thisisasillytestelementnametomakesurecrazytagnamesareparsedcorrectly> - -#data -<!DOCTYPE html>X</body>X -#errors -Line: 1 Col: 24 Unexpected non-space characters in the after body phase. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "XX" - -#data -<!DOCTYPE html><!-- X -#errors -Line: 1 Col: 21 Unexpected end of file in comment. -#document -| <!DOCTYPE html> -| <!-- X --> -| <html> -| <head> -| <body> - -#data -<!DOCTYPE html><table><caption>test TEST</caption><td>test -#errors -Line: 1 Col: 54 Unexpected table cell start tag (td) in the table body phase. -Line: 1 Col: 58 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <caption> -| "test TEST" -| <tbody> -| <tr> -| <td> -| "test" - -#data -<!DOCTYPE html><select><option><optgroup> -#errors -Line: 1 Col: 41 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <option> -| <optgroup> - -#data -<!DOCTYPE html><select><optgroup><option></optgroup><option><select><option> -#errors -Line: 1 Col: 68 Unexpected select start tag in the select phase treated as select end tag. -Line: 1 Col: 76 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <optgroup> -| <option> -| <option> -| <option> - -#data -<!DOCTYPE html><select><optgroup><option><optgroup> -#errors -Line: 1 Col: 51 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <optgroup> -| <option> -| <optgroup> - -#data -<!DOCTYPE html><datalist><option>foo</datalist>bar -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <datalist> -| <option> -| "foo" -| "bar" - -#data -<!DOCTYPE html><font><input><input></font> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <font> -| <input> -| <input> - -#data -<!DOCTYPE html><!-- XXX - XXX --> -#errors -#document -| <!DOCTYPE html> -| <!-- XXX - XXX --> -| <html> -| <head> -| <body> - -#data -<!DOCTYPE html><!-- XXX - XXX -#errors -Line: 1 Col: 29 Unexpected end of file in comment (-) -#document -| <!DOCTYPE html> -| <!-- XXX - XXX --> -| <html> -| <head> -| <body> - -#data -<!DOCTYPE html><!-- XXX - XXX - XXX --> -#errors -#document -| <!DOCTYPE html> -| <!-- XXX - XXX - XXX --> -| <html> -| <head> -| <body> - -#data -<isindex test=x name=x> -#errors -Line: 1 Col: 23 Unexpected start tag (isindex). Expected DOCTYPE. -Line: 1 Col: 23 Unexpected start tag isindex. Don't use it! -#document -| <html> -| <head> -| <body> -| <form> -| <hr> -| <label> -| "This is a searchable index. Enter search keywords: " -| <input> -| name="isindex" -| test="x" -| <hr> - -#data -test -test -#errors -Line: 2 Col: 4 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "test -test" - -#data -<!DOCTYPE html><body><title>test</body> -#errors -#document -| -| -| -| -| -| "test</body>" - -#data -<!DOCTYPE html><body><title>X -#errors -#document -| -| -| -| -| -| "X" -| <meta> -| name="z" -| <link> -| rel="foo" -| <style> -| " -x { content:"</style" } " - -#data -<!DOCTYPE html><select><optgroup></optgroup></select> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <optgroup> - -#data - - -#errors -Line: 2 Col: 1 Unexpected End of file. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> - -#data -<!DOCTYPE html> <html> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> - -#data -<!DOCTYPE html><script> -</script> <title>x -#errors -#document -| -| -| -| -#errors -Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. -Line: 1 Col: 21 Unexpected start tag (script) that can be in head. Moved. -#document -| -| -| -#errors -Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. -Line: 1 Col: 28 Unexpected start tag (style) that can be in head. Moved. -#document -| -| -| -#errors -Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. -#document -| -| -| -| -| "x" -| x -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -Line: 1 Col: 22 Unexpected end of file. Expected end tag (style). -#document -| -| -| --> x -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| -| -| x -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| -| -| x -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| -| -| x -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| -| -|

-#errors -#document -| -| -| -| -| -| ddd -#errors -#document -| -| -| -#errors -#document -| -| -| -| -|
  • -| -|