summaryrefslogtreecommitdiff
path: root/src/pkg
diff options
context:
space:
mode:
authorMichael Stapelberg <stapelberg@debian.org>2013-03-04 21:27:36 +0100
committerMichael Stapelberg <michael@stapelberg.de>2013-03-04 21:27:36 +0100
commit04b08da9af0c450d645ab7389d1467308cfc2db8 (patch)
treedb247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/pkg
parent917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff)
downloadgolang-04b08da9af0c450d645ab7389d1467308cfc2db8.tar.gz
Imported Upstream version 1.1~hg20130304upstream/1.1_hg20130304
Diffstat (limited to 'src/pkg')
-rw-r--r--src/pkg/archive/tar/common.go184
-rw-r--r--src/pkg/archive/tar/example_test.go79
-rw-r--r--src/pkg/archive/tar/reader.go205
-rw-r--r--src/pkg/archive/tar/reader_test.go106
-rw-r--r--src/pkg/archive/tar/stat_atim.go20
-rw-r--r--src/pkg/archive/tar/stat_atimespec.go20
-rw-r--r--src/pkg/archive/tar/stat_unix.go32
-rw-r--r--src/pkg/archive/tar/tar_test.go271
-rw-r--r--src/pkg/archive/tar/testdata/pax.tarbin0 -> 10240 bytes
-rw-r--r--src/pkg/archive/tar/testdata/ustar.tarbin0 -> 2048 bytes
-rw-r--r--src/pkg/archive/tar/writer.go155
-rw-r--r--src/pkg/archive/tar/writer_test.go80
-rw-r--r--src/pkg/archive/zip/reader.go121
-rw-r--r--src/pkg/archive/zip/reader_test.go11
-rw-r--r--src/pkg/archive/zip/struct.go93
-rw-r--r--src/pkg/archive/zip/testdata/zip64.zipbin0 -> 242 bytes
-rw-r--r--src/pkg/archive/zip/writer.go136
-rw-r--r--src/pkg/archive/zip/zip_test.go198
-rw-r--r--src/pkg/bufio/bufio.go100
-rw-r--r--src/pkg/bufio/bufio_test.go310
-rw-r--r--src/pkg/bufio/example_test.go74
-rw-r--r--src/pkg/bufio/export_test.go27
-rw-r--r--src/pkg/bufio/scan.go338
-rw-r--r--src/pkg/bufio/scan_test.go370
-rw-r--r--src/pkg/builtin/builtin.go9
-rw-r--r--src/pkg/bytes/asm_386.s20
-rw-r--r--src/pkg/bytes/asm_amd64.s30
-rw-r--r--src/pkg/bytes/asm_arm.s53
-rw-r--r--src/pkg/bytes/buffer.go38
-rw-r--r--src/pkg/bytes/buffer_test.go117
-rw-r--r--src/pkg/bytes/bytes.go96
-rw-r--r--src/pkg/bytes/bytes_decl.go8
-rw-r--r--src/pkg/bytes/bytes_test.go104
-rw-r--r--src/pkg/bytes/example_test.go65
-rw-r--r--src/pkg/bytes/reader.go21
-rw-r--r--src/pkg/bytes/reader_test.go49
-rw-r--r--src/pkg/compress/bzip2/bit_reader.go13
-rw-r--r--src/pkg/compress/flate/copy.go17
-rw-r--r--src/pkg/compress/flate/copy_test.go52
-rw-r--r--src/pkg/compress/flate/deflate.go2
-rw-r--r--src/pkg/compress/flate/deflate_test.go5
-rw-r--r--src/pkg/compress/flate/fixedhuff.go74
-rw-r--r--src/pkg/compress/flate/flate_test.go113
-rw-r--r--src/pkg/compress/flate/gen.go165
-rw-r--r--src/pkg/compress/flate/inflate.go255
-rw-r--r--src/pkg/compress/flate/reader_test.go95
-rw-r--r--src/pkg/compress/flate/writer_test.go60
-rw-r--r--src/pkg/compress/lzw/reader_test.go12
-rw-r--r--src/pkg/compress/lzw/writer.go13
-rw-r--r--src/pkg/compress/lzw/writer_test.go20
-rw-r--r--src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt2
-rw-r--r--src/pkg/compress/testdata/e.txt2
-rw-r--r--src/pkg/compress/testdata/pi.txt2
-rw-r--r--src/pkg/compress/zlib/example_test.go37
-rw-r--r--src/pkg/compress/zlib/reader.go2
-rw-r--r--src/pkg/container/heap/example_intheap_test.go43
-rw-r--r--src/pkg/container/heap/example_pq_test.go95
-rw-r--r--src/pkg/container/heap/example_test.go105
-rw-r--r--src/pkg/container/heap/heap.go10
-rw-r--r--src/pkg/container/heap/heap_test.go16
-rw-r--r--src/pkg/container/list/example_test.go30
-rw-r--r--src/pkg/container/list/list.go238
-rw-r--r--src/pkg/container/list/list_test.go102
-rw-r--r--src/pkg/container/ring/ring.go2
-rw-r--r--src/pkg/crypto/aes/aes_test.go33
-rw-r--r--src/pkg/crypto/aes/asm_amd64.s287
-rw-r--r--src/pkg/crypto/aes/block.go6
-rw-r--r--src/pkg/crypto/aes/cipher.go8
-rw-r--r--src/pkg/crypto/aes/cipher_asm.go46
-rw-r--r--src/pkg/crypto/aes/cipher_generic.go19
-rw-r--r--src/pkg/crypto/cipher/cbc.go18
-rw-r--r--src/pkg/crypto/cipher/cfb.go6
-rw-r--r--src/pkg/crypto/cipher/cipher_test.go36
-rw-r--r--src/pkg/crypto/cipher/ctr.go2
-rw-r--r--src/pkg/crypto/cipher/example_test.go283
-rw-r--r--src/pkg/crypto/cipher/io.go6
-rw-r--r--src/pkg/crypto/des/des_test.go18
-rw-r--r--src/pkg/crypto/ecdsa/ecdsa.go6
-rw-r--r--src/pkg/crypto/ecdsa/ecdsa_test.go256
-rw-r--r--src/pkg/crypto/ecdsa/testdata/SigVer.rsp.bz2bin0 -> 95485 bytes
-rw-r--r--src/pkg/crypto/elliptic/elliptic.go92
-rw-r--r--src/pkg/crypto/elliptic/elliptic_test.go38
-rw-r--r--src/pkg/crypto/elliptic/p224.go91
-rw-r--r--src/pkg/crypto/hmac/hmac.go32
-rw-r--r--src/pkg/crypto/hmac/hmac_test.go19
-rw-r--r--src/pkg/crypto/md5/gen.go300
-rw-r--r--src/pkg/crypto/md5/md5.go35
-rw-r--r--src/pkg/crypto/md5/md5_test.go62
-rw-r--r--src/pkg/crypto/md5/md5block.go364
-rw-r--r--src/pkg/crypto/rand/example_test.go29
-rw-r--r--src/pkg/crypto/rand/rand_test.go16
-rw-r--r--src/pkg/crypto/rand/rand_unix.go20
-rw-r--r--src/pkg/crypto/rand/rand_windows.go4
-rw-r--r--src/pkg/crypto/rand/util.go61
-rw-r--r--src/pkg/crypto/rc4/rc4.go11
-rw-r--r--src/pkg/crypto/rc4/rc4_amd64.s53
-rw-r--r--src/pkg/crypto/rc4/rc4_arm.s58
-rw-r--r--src/pkg/crypto/rc4/rc4_asm.go18
-rw-r--r--src/pkg/crypto/rc4/rc4_ref.go20
-rw-r--r--src/pkg/crypto/rc4/rc4_test.go60
-rw-r--r--src/pkg/crypto/rsa/pkcs1v15.go42
-rw-r--r--src/pkg/crypto/rsa/pkcs1v15_test.go8
-rw-r--r--src/pkg/crypto/rsa/rsa.go92
-rw-r--r--src/pkg/crypto/rsa/rsa_test.go57
-rw-r--r--src/pkg/crypto/sha1/sha1.go39
-rw-r--r--src/pkg/crypto/sha1/sha1_test.go32
-rw-r--r--src/pkg/crypto/sha1/sha1block.go51
-rw-r--r--src/pkg/crypto/sha256/sha256.go83
-rw-r--r--src/pkg/crypto/sha256/sha256_test.go25
-rw-r--r--src/pkg/crypto/sha256/sha256block.go17
-rw-r--r--src/pkg/crypto/sha512/sha512.go83
-rw-r--r--src/pkg/crypto/sha512/sha512_test.go25
-rw-r--r--src/pkg/crypto/sha512/sha512block.go16
-rw-r--r--src/pkg/crypto/tls/cipher_suites.go4
-rw-r--r--src/pkg/crypto/tls/common.go25
-rw-r--r--src/pkg/crypto/tls/conn.go99
-rw-r--r--src/pkg/crypto/tls/generate_cert.go60
-rw-r--r--src/pkg/crypto/tls/handshake_client.go9
-rw-r--r--src/pkg/crypto/tls/handshake_client_test.go234
-rw-r--r--src/pkg/crypto/tls/handshake_messages.go108
-rw-r--r--src/pkg/crypto/tls/handshake_messages_test.go34
-rw-r--r--src/pkg/crypto/tls/handshake_server.go534
-rw-r--r--src/pkg/crypto/tls/handshake_server_test.go2369
-rw-r--r--src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py57
-rw-r--r--src/pkg/crypto/tls/prf.go21
-rw-r--r--src/pkg/crypto/tls/prf_test.go15
-rw-r--r--src/pkg/crypto/tls/root_test.go61
-rw-r--r--src/pkg/crypto/tls/ticket.go182
-rw-r--r--src/pkg/crypto/tls/tls.go80
-rw-r--r--src/pkg/crypto/tls/tls_test.go107
-rw-r--r--src/pkg/crypto/x509/cert_pool.go2
-rw-r--r--src/pkg/crypto/x509/pem_decrypt.go233
-rw-r--r--src/pkg/crypto/x509/pem_decrypt_test.go223
-rw-r--r--src/pkg/crypto/x509/pkcs8.go22
-rw-r--r--src/pkg/crypto/x509/pkcs8_test.go18
-rw-r--r--src/pkg/crypto/x509/root_darwin.go9
-rw-r--r--src/pkg/crypto/x509/root_plan9.go33
-rw-r--r--src/pkg/crypto/x509/root_stub.go3
-rw-r--r--src/pkg/crypto/x509/root_unix.go6
-rw-r--r--src/pkg/crypto/x509/root_windows.go9
-rw-r--r--src/pkg/crypto/x509/sec1.go69
-rw-r--r--src/pkg/crypto/x509/sec1_test.go22
-rw-r--r--src/pkg/crypto/x509/verify.go155
-rw-r--r--src/pkg/crypto/x509/verify_test.go246
-rw-r--r--src/pkg/crypto/x509/x509.go475
-rw-r--r--src/pkg/crypto/x509/x509_test.go296
-rw-r--r--src/pkg/database/sql/convert.go83
-rw-r--r--src/pkg/database/sql/doc.txt2
-rw-r--r--src/pkg/database/sql/driver/driver.go32
-rw-r--r--src/pkg/database/sql/example_test.go45
-rw-r--r--src/pkg/database/sql/fakedb_test.go79
-rw-r--r--src/pkg/database/sql/sql.go408
-rw-r--r--src/pkg/database/sql/sql_test.go108
-rw-r--r--src/pkg/debug/dwarf/const.go1
-rw-r--r--src/pkg/debug/dwarf/entry.go4
-rw-r--r--src/pkg/debug/elf/file.go53
-rw-r--r--src/pkg/debug/elf/file_test.go134
-rw-r--r--src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.objbin0 -> 6544 bytes
-rw-r--r--src/pkg/debug/elf/testdata/hello-world-core.gzbin0 -> 12678 bytes
-rw-r--r--src/pkg/debug/gosym/pclntab_test.go14
-rw-r--r--src/pkg/debug/gosym/symtab.go127
-rw-r--r--src/pkg/debug/pe/file.go68
-rw-r--r--src/pkg/debug/pe/file_test.go30
-rw-r--r--src/pkg/debug/pe/pe.go11
-rw-r--r--src/pkg/encoding/asn1/asn1.go33
-rw-r--r--src/pkg/encoding/asn1/asn1_test.go8
-rw-r--r--src/pkg/encoding/asn1/common.go2
-rw-r--r--src/pkg/encoding/asn1/marshal.go43
-rw-r--r--src/pkg/encoding/asn1/marshal_test.go12
-rw-r--r--src/pkg/encoding/base32/base32.go3
-rw-r--r--src/pkg/encoding/base32/example_test.go45
-rw-r--r--src/pkg/encoding/base64/base64.go3
-rw-r--r--src/pkg/encoding/base64/base64_test.go1
-rw-r--r--src/pkg/encoding/base64/example_test.go45
-rw-r--r--src/pkg/encoding/binary/binary.go102
-rw-r--r--src/pkg/encoding/binary/binary_test.go90
-rw-r--r--src/pkg/encoding/binary/varint.go2
-rw-r--r--src/pkg/encoding/csv/writer.go14
-rw-r--r--src/pkg/encoding/csv/writer_test.go28
-rw-r--r--src/pkg/encoding/gob/codec_test.go15
-rw-r--r--src/pkg/encoding/gob/decode.go14
-rw-r--r--src/pkg/encoding/gob/decoder.go37
-rw-r--r--src/pkg/encoding/gob/doc.go16
-rw-r--r--src/pkg/encoding/gob/encode.go23
-rw-r--r--src/pkg/encoding/gob/encoder.go12
-rw-r--r--src/pkg/encoding/gob/encoder_test.go106
-rw-r--r--src/pkg/encoding/gob/gobencdec_test.go71
-rw-r--r--src/pkg/encoding/gob/timing_test.go37
-rw-r--r--src/pkg/encoding/gob/type.go21
-rw-r--r--src/pkg/encoding/gob/type_test.go61
-rw-r--r--src/pkg/encoding/hex/hex_test.go2
-rw-r--r--src/pkg/encoding/json/bench_test.go34
-rw-r--r--src/pkg/encoding/json/decode.go244
-rw-r--r--src/pkg/encoding/json/decode_test.go607
-rw-r--r--src/pkg/encoding/json/encode.go266
-rw-r--r--src/pkg/encoding/json/encode_test.go20
-rw-r--r--src/pkg/encoding/json/scanner_test.go9
-rw-r--r--src/pkg/encoding/json/stream.go13
-rw-r--r--src/pkg/encoding/json/stream_test.go46
-rw-r--r--src/pkg/encoding/json/tagkey_test.go10
-rw-r--r--src/pkg/encoding/pem/pem.go63
-rw-r--r--src/pkg/encoding/pem/pem_test.go104
-rw-r--r--src/pkg/encoding/xml/example_test.go40
-rw-r--r--src/pkg/encoding/xml/marshal.go102
-rw-r--r--src/pkg/encoding/xml/marshal_test.go208
-rw-r--r--src/pkg/encoding/xml/read.go74
-rw-r--r--src/pkg/encoding/xml/read_test.go44
-rw-r--r--src/pkg/encoding/xml/typeinfo.go30
-rw-r--r--src/pkg/encoding/xml/xml.go290
-rw-r--r--src/pkg/encoding/xml/xml_test.go133
-rw-r--r--src/pkg/exp/norm/Makefile30
-rw-r--r--src/pkg/exp/norm/composition.go382
-rw-r--r--src/pkg/exp/norm/composition_test.go143
-rw-r--r--src/pkg/exp/norm/example_iter_test.go81
-rw-r--r--src/pkg/exp/norm/forminfo.go229
-rw-r--r--src/pkg/exp/norm/input.go105
-rw-r--r--src/pkg/exp/norm/iter.go401
-rw-r--r--src/pkg/exp/norm/iter_test.go188
-rw-r--r--src/pkg/exp/norm/maketables.go923
-rw-r--r--src/pkg/exp/norm/maketesttables.go45
-rw-r--r--src/pkg/exp/norm/norm_test.go14
-rw-r--r--src/pkg/exp/norm/normalize.go478
-rw-r--r--src/pkg/exp/norm/normalize_test.go750
-rw-r--r--src/pkg/exp/norm/normregtest.go304
-rw-r--r--src/pkg/exp/norm/readwriter.go126
-rw-r--r--src/pkg/exp/norm/readwriter_test.go68
-rw-r--r--src/pkg/exp/norm/tables.go6779
-rw-r--r--src/pkg/exp/norm/trie.go232
-rw-r--r--src/pkg/exp/norm/trie_test.go152
-rw-r--r--src/pkg/exp/norm/triedata_test.go85
-rw-r--r--src/pkg/exp/norm/triegen.go317
-rw-r--r--src/pkg/expvar/expvar_test.go5
-rw-r--r--src/pkg/flag/flag.go19
-rw-r--r--src/pkg/flag/flag_test.go62
-rw-r--r--src/pkg/fmt/doc.go31
-rw-r--r--src/pkg/fmt/fmt_test.go107
-rw-r--r--src/pkg/fmt/format.go82
-rw-r--r--src/pkg/fmt/print.go106
-rw-r--r--src/pkg/fmt/scan.go21
-rw-r--r--src/pkg/fmt/scan_test.go43
-rw-r--r--src/pkg/go/ast/ast.go3
-rw-r--r--src/pkg/go/ast/commentmap.go332
-rw-r--r--src/pkg/go/ast/commentmap_test.go143
-rw-r--r--src/pkg/go/ast/filter.go2
-rw-r--r--src/pkg/go/ast/import.go2
-rw-r--r--src/pkg/go/ast/print.go79
-rw-r--r--src/pkg/go/ast/print_test.go21
-rw-r--r--src/pkg/go/ast/resolve.go4
-rw-r--r--src/pkg/go/ast/scope.go14
-rw-r--r--src/pkg/go/ast/walk.go7
-rw-r--r--src/pkg/go/build/build.go144
-rw-r--r--src/pkg/go/build/build_test.go42
-rw-r--r--src/pkg/go/build/deps_test.go28
-rw-r--r--src/pkg/go/build/doc.go28
-rw-r--r--src/pkg/go/build/read.go238
-rw-r--r--src/pkg/go/build/read_test.go226
-rw-r--r--src/pkg/go/doc/comment.go5
-rw-r--r--src/pkg/go/doc/doc.go12
-rw-r--r--src/pkg/go/doc/doc_test.go2
-rw-r--r--src/pkg/go/doc/example.go270
-rw-r--r--src/pkg/go/doc/example_test.go111
-rw-r--r--src/pkg/go/doc/exports.go2
-rw-r--r--src/pkg/go/doc/reader.go113
-rw-r--r--src/pkg/go/doc/synopsis.go41
-rw-r--r--src/pkg/go/doc/synopsis_test.go5
-rw-r--r--src/pkg/go/doc/testdata/a.0.golden11
-rw-r--r--src/pkg/go/doc/testdata/a.1.golden11
-rw-r--r--src/pkg/go/doc/testdata/a.2.golden11
-rw-r--r--src/pkg/go/doc/testdata/a0.go9
-rw-r--r--src/pkg/go/doc/testdata/a1.go4
-rw-r--r--src/pkg/go/doc/testdata/benchmark.go4
-rw-r--r--src/pkg/go/doc/testdata/e.go2
-rw-r--r--src/pkg/go/doc/testdata/error2.1.golden2
-rw-r--r--src/pkg/go/doc/testdata/error2.go2
-rw-r--r--src/pkg/go/doc/testdata/template.txt7
-rw-r--r--src/pkg/go/doc/testdata/testing.1.golden2
-rw-r--r--src/pkg/go/doc/testdata/testing.go4
-rw-r--r--src/pkg/go/format/format.go200
-rw-r--r--src/pkg/go/format/format_test.go125
-rw-r--r--src/pkg/go/parser/error_test.go10
-rw-r--r--src/pkg/go/parser/interface.go48
-rw-r--r--src/pkg/go/parser/parser.go249
-rw-r--r--src/pkg/go/parser/parser_test.go47
-rw-r--r--src/pkg/go/parser/performance_test.go30
-rw-r--r--src/pkg/go/parser/short_test.go13
-rw-r--r--src/pkg/go/printer/nodes.go164
-rw-r--r--src/pkg/go/printer/performance_test.go4
-rw-r--r--src/pkg/go/printer/printer.go92
-rw-r--r--src/pkg/go/printer/printer_test.go271
-rw-r--r--src/pkg/go/printer/testdata/comments.golden11
-rw-r--r--src/pkg/go/printer/testdata/comments.input10
-rw-r--r--src/pkg/go/printer/testdata/comments2.golden79
-rw-r--r--src/pkg/go/printer/testdata/comments2.input79
-rw-r--r--src/pkg/go/printer/testdata/declarations.golden54
-rw-r--r--src/pkg/go/printer/testdata/declarations.input54
-rw-r--r--src/pkg/go/printer/testdata/expressions.golden15
-rw-r--r--src/pkg/go/printer/testdata/expressions.input15
-rw-r--r--src/pkg/go/printer/testdata/expressions.raw15
-rw-r--r--src/pkg/go/printer/testdata/statements.golden108
-rw-r--r--src/pkg/go/printer/testdata/statements.input86
-rw-r--r--src/pkg/go/scanner/errors.go2
-rw-r--r--src/pkg/go/scanner/scanner.go39
-rw-r--r--src/pkg/go/scanner/scanner_test.go116
-rw-r--r--src/pkg/go/token/position.go22
-rw-r--r--src/pkg/go/token/position_test.go51
-rw-r--r--src/pkg/go/types/api.go105
-rw-r--r--src/pkg/go/types/builtins.go455
-rw-r--r--src/pkg/go/types/check.go507
-rw-r--r--src/pkg/go/types/check_test.go259
-rw-r--r--src/pkg/go/types/const.go718
-rw-r--r--src/pkg/go/types/conversions.go129
-rw-r--r--src/pkg/go/types/errors.go335
-rw-r--r--src/pkg/go/types/exportdata.go111
-rw-r--r--src/pkg/go/types/expr.go1520
-rw-r--r--src/pkg/go/types/gcimporter.go950
-rw-r--r--src/pkg/go/types/gcimporter_test.go180
-rw-r--r--src/pkg/go/types/objects.go186
-rw-r--r--src/pkg/go/types/operand.go411
-rw-r--r--src/pkg/go/types/predicates.go303
-rw-r--r--src/pkg/go/types/resolve.go197
-rw-r--r--src/pkg/go/types/resolver_test.go167
-rw-r--r--src/pkg/go/types/scope.go78
-rw-r--r--src/pkg/go/types/sizes.go162
-rw-r--r--src/pkg/go/types/stmt.go743
-rw-r--r--src/pkg/go/types/testdata/builtins.src401
-rw-r--r--src/pkg/go/types/testdata/const0.src215
-rw-r--r--src/pkg/go/types/testdata/conversions.src18
-rw-r--r--src/pkg/go/types/testdata/decls0.src187
-rw-r--r--src/pkg/go/types/testdata/decls1.src132
-rw-r--r--src/pkg/go/types/testdata/decls2a.src67
-rw-r--r--src/pkg/go/types/testdata/decls2b.src28
-rw-r--r--src/pkg/go/types/testdata/decls3.src253
-rw-r--r--src/pkg/go/types/testdata/exports.go89
-rw-r--r--src/pkg/go/types/testdata/expr0.src161
-rw-r--r--src/pkg/go/types/testdata/expr1.src7
-rw-r--r--src/pkg/go/types/testdata/expr2.src23
-rw-r--r--src/pkg/go/types/testdata/expr3.src463
-rw-r--r--src/pkg/go/types/testdata/stmt0.src288
-rw-r--r--src/pkg/go/types/types.go236
-rw-r--r--src/pkg/go/types/types_test.go171
-rw-r--r--src/pkg/go/types/universe.go146
-rw-r--r--src/pkg/hash/adler32/adler32.go68
-rw-r--r--src/pkg/hash/adler32/adler32_test.go90
-rw-r--r--src/pkg/hash/crc32/crc32.go6
-rw-r--r--src/pkg/hash/crc32/crc32_amd64.go2
-rw-r--r--src/pkg/hash/crc32/crc32_amd64.s14
-rw-r--r--src/pkg/hash/crc32/crc32_test.go18
-rw-r--r--src/pkg/hash/crc64/crc64.go10
-rw-r--r--src/pkg/hash/crc64/crc64_test.go17
-rw-r--r--src/pkg/hash/fnv/fnv.go32
-rw-r--r--src/pkg/hash/fnv/fnv_test.go34
-rw-r--r--src/pkg/html/entity.go4154
-rw-r--r--src/pkg/html/escape.go4
-rw-r--r--src/pkg/html/template/content.go4
-rw-r--r--src/pkg/html/template/doc.go2
-rw-r--r--src/pkg/html/template/error.go4
-rw-r--r--src/pkg/html/template/escape.go14
-rw-r--r--src/pkg/html/template/escape_test.go16
-rw-r--r--src/pkg/html/template/html.go6
-rw-r--r--src/pkg/html/template/js.go2
-rw-r--r--src/pkg/html/template/template.go12
-rw-r--r--src/pkg/html/template/url.go2
-rw-r--r--src/pkg/image/decode_test.go1
-rw-r--r--src/pkg/image/draw/draw.go33
-rw-r--r--src/pkg/image/format.go2
-rw-r--r--src/pkg/image/jpeg/dct_test.go299
-rw-r--r--src/pkg/image/jpeg/huffman.go58
-rw-r--r--src/pkg/image/jpeg/idct.go80
-rw-r--r--src/pkg/image/jpeg/reader.go223
-rw-r--r--src/pkg/image/jpeg/reader_test.go157
-rw-r--r--src/pkg/image/jpeg/scan.go432
-rw-r--r--src/pkg/image/jpeg/writer.go100
-rw-r--r--src/pkg/image/jpeg/writer_test.go93
-rw-r--r--src/pkg/image/names.go2
-rw-r--r--src/pkg/image/png/paeth.go70
-rw-r--r--src/pkg/image/png/paeth_test.go91
-rw-r--r--src/pkg/image/png/reader.go99
-rw-r--r--src/pkg/image/png/reader_test.go113
-rw-r--r--src/pkg/image/png/testdata/benchGray.pngbin0 -> 14709 bytes
-rw-r--r--src/pkg/image/png/testdata/benchNRGBA-gradient.pngbin0 -> 58831 bytes
-rw-r--r--src/pkg/image/png/testdata/benchNRGBA-opaque.pngbin0 -> 44237 bytes
-rw-r--r--src/pkg/image/png/testdata/benchPaletted.pngbin0 -> 13397 bytes
-rw-r--r--src/pkg/image/png/testdata/benchRGB.pngbin0 -> 39571 bytes
-rw-r--r--src/pkg/image/png/writer.go105
-rw-r--r--src/pkg/image/png/writer_test.go45
-rw-r--r--src/pkg/image/testdata/video-001.progressive.jpegbin0 -> 20732 bytes
-rw-r--r--src/pkg/image/testdata/video-001.q50.420.jpegbin0 -> 3407 bytes
-rw-r--r--src/pkg/image/testdata/video-001.q50.420.progressive.jpegbin0 -> 3279 bytes
-rw-r--r--src/pkg/image/testdata/video-001.q50.422.jpegbin0 -> 3608 bytes
-rw-r--r--src/pkg/image/testdata/video-001.q50.422.progressive.jpegbin0 -> 3506 bytes
-rw-r--r--src/pkg/image/testdata/video-001.q50.440.jpegbin0 -> 3662 bytes
-rw-r--r--src/pkg/image/testdata/video-001.q50.440.progressive.jpegbin0 -> 3529 bytes
-rw-r--r--src/pkg/image/testdata/video-001.q50.444.jpegbin0 -> 4032 bytes
-rw-r--r--src/pkg/image/testdata/video-001.q50.444.progressive.jpegbin0 -> 3935 bytes
-rw-r--r--src/pkg/image/testdata/video-005.gray.q50.2x2.jpegbin0 -> 2782 bytes
-rw-r--r--src/pkg/image/testdata/video-005.gray.q50.2x2.progressive.jpegbin0 -> 2699 bytes
-rw-r--r--src/pkg/image/testdata/video-005.gray.q50.jpegbin0 -> 2782 bytes
-rw-r--r--src/pkg/image/testdata/video-005.gray.q50.progressive.jpegbin0 -> 2699 bytes
-rw-r--r--src/pkg/image/ycbcr.go9
-rw-r--r--src/pkg/image/ycbcr_test.go1
-rw-r--r--src/pkg/io/io.go81
-rw-r--r--src/pkg/io/io_test.go76
-rw-r--r--src/pkg/io/ioutil/blackhole.go23
-rw-r--r--src/pkg/io/ioutil/ioutil.go6
-rw-r--r--src/pkg/io/ioutil/ioutil_test.go3
-rw-r--r--src/pkg/io/ioutil/tempfile.go10
-rw-r--r--src/pkg/io/ioutil/tempfile_test.go3
-rw-r--r--src/pkg/log/syslog/syslog.go239
-rw-r--r--src/pkg/log/syslog/syslog_plan9.go8
-rw-r--r--src/pkg/log/syslog/syslog_test.go339
-rw-r--r--src/pkg/log/syslog/syslog_unix.go8
-rw-r--r--src/pkg/math/abs_arm.s7
-rw-r--r--src/pkg/math/acosh.go2
-rw-r--r--src/pkg/math/all_test.go22
-rw-r--r--src/pkg/math/asinh.go2
-rw-r--r--src/pkg/math/atan.go103
-rw-r--r--src/pkg/math/atanh.go2
-rw-r--r--src/pkg/math/big/arith_386.s18
-rw-r--r--src/pkg/math/big/arith_amd64.s331
-rw-r--r--src/pkg/math/big/arith_arm.s18
-rw-r--r--src/pkg/math/big/arith_test.go86
-rw-r--r--src/pkg/math/big/calibrate_test.go42
-rw-r--r--src/pkg/math/big/gcd_test.go47
-rw-r--r--src/pkg/math/big/int.go124
-rw-r--r--src/pkg/math/big/int_test.go205
-rw-r--r--src/pkg/math/big/nat.go309
-rw-r--r--src/pkg/math/big/nat_test.go135
-rw-r--r--src/pkg/math/big/rat.go269
-rw-r--r--src/pkg/math/big/rat_test.go499
-rw-r--r--src/pkg/math/bits.go2
-rw-r--r--src/pkg/math/cbrt.go2
-rw-r--r--src/pkg/math/copysign.go2
-rw-r--r--src/pkg/math/dim_amd64.s2
-rw-r--r--src/pkg/math/erf.go4
-rw-r--r--src/pkg/math/floor_amd64.s70
-rw-r--r--src/pkg/math/frexp_386.s12
-rw-r--r--src/pkg/math/gamma.go27
-rw-r--r--src/pkg/math/hypot.go2
-rw-r--r--src/pkg/math/hypot_386.s46
-rw-r--r--src/pkg/math/hypot_amd64.s14
-rw-r--r--src/pkg/math/ldexp_386.s6
-rw-r--r--src/pkg/math/log10.go3
-rw-r--r--src/pkg/math/log_amd64.s8
-rw-r--r--src/pkg/math/logb.go4
-rw-r--r--src/pkg/math/modf_386.s14
-rw-r--r--src/pkg/math/rand/example_test.go69
-rw-r--r--src/pkg/math/rand/rand_test.go13
-rw-r--r--src/pkg/math/remainder.go2
-rw-r--r--src/pkg/math/sincos.go2
-rw-r--r--src/pkg/math/sincos_386.s8
-rw-r--r--src/pkg/math/sincos_amd64.s2
-rw-r--r--src/pkg/math/tanh.go89
-rw-r--r--src/pkg/mime/grammar.go2
-rw-r--r--src/pkg/mime/multipart/multipart.go24
-rw-r--r--src/pkg/mime/multipart/multipart_test.go28
-rw-r--r--src/pkg/mime/multipart/quotedprintable.go130
-rw-r--r--src/pkg/mime/multipart/quotedprintable_test.go199
-rw-r--r--[-rwxr-xr-x]src/pkg/mime/multipart/testdata/nested-mime0
-rw-r--r--src/pkg/mime/multipart/writer.go29
-rw-r--r--src/pkg/mime/multipart/writer_test.go35
-rw-r--r--src/pkg/mime/testdata/test.types (renamed from src/pkg/mime/test.types)0
-rw-r--r--src/pkg/mime/type_unix.go17
-rw-r--r--src/pkg/mime/type_windows.go10
-rw-r--r--src/pkg/net/cgo_bsd.go4
-rw-r--r--src/pkg/net/cgo_linux.go8
-rw-r--r--src/pkg/net/cgo_netbsd.go14
-rw-r--r--src/pkg/net/cgo_openbsd.go14
-rw-r--r--src/pkg/net/cgo_unix.go10
-rw-r--r--src/pkg/net/conn_test.go114
-rw-r--r--src/pkg/net/dial.go243
-rw-r--r--src/pkg/net/dial_test.go114
-rw-r--r--src/pkg/net/dialgoogle_test.go24
-rw-r--r--src/pkg/net/dnsclient.go7
-rw-r--r--src/pkg/net/dnsclient_unix.go28
-rw-r--r--src/pkg/net/dnsconfig_unix.go (renamed from src/pkg/net/dnsconfig.go)0
-rw-r--r--src/pkg/net/dnsmsg.go2
-rw-r--r--src/pkg/net/example_test.go2
-rw-r--r--src/pkg/net/fd_bsd.go (renamed from src/pkg/net/fd_netbsd.go)9
-rw-r--r--src/pkg/net/fd_darwin.go7
-rw-r--r--src/pkg/net/fd_freebsd.go116
-rw-r--r--src/pkg/net/fd_linux.go7
-rw-r--r--src/pkg/net/fd_openbsd.go116
-rw-r--r--src/pkg/net/fd_plan9.go129
-rw-r--r--src/pkg/net/fd_posix_test.go57
-rw-r--r--src/pkg/net/fd_unix.go (renamed from src/pkg/net/fd.go)319
-rw-r--r--src/pkg/net/fd_unix_test.go58
-rw-r--r--src/pkg/net/fd_windows.go293
-rw-r--r--src/pkg/net/file_plan9.go131
-rw-r--r--src/pkg/net/file_test.go8
-rw-r--r--src/pkg/net/file_unix.go (renamed from src/pkg/net/file.go)40
-rw-r--r--src/pkg/net/http/cgi/child.go12
-rw-r--r--src/pkg/net/http/cgi/child_test.go24
-rw-r--r--src/pkg/net/http/cgi/host_test.go86
-rw-r--r--src/pkg/net/http/cgi/plan9_test.go18
-rw-r--r--src/pkg/net/http/cgi/posix_test.go21
-rwxr-xr-xsrc/pkg/net/http/cgi/testdata/test.cgi55
-rw-r--r--src/pkg/net/http/chunked.go59
-rw-r--r--src/pkg/net/http/chunked_test.go54
-rw-r--r--src/pkg/net/http/client.go178
-rw-r--r--src/pkg/net/http/client_test.go250
-rw-r--r--src/pkg/net/http/cookie.go9
-rw-r--r--src/pkg/net/http/cookie_test.go2
-rw-r--r--src/pkg/net/http/cookiejar/jar.go494
-rw-r--r--src/pkg/net/http/cookiejar/jar_test.go1267
-rw-r--r--src/pkg/net/http/cookiejar/punycode.go159
-rw-r--r--src/pkg/net/http/cookiejar/punycode_test.go161
-rw-r--r--src/pkg/net/http/example_test.go2
-rw-r--r--src/pkg/net/http/export_test.go23
-rw-r--r--src/pkg/net/http/filetransport_test.go10
-rw-r--r--src/pkg/net/http/fs.go196
-rw-r--r--src/pkg/net/http/fs_test.go439
-rw-r--r--src/pkg/net/http/header.go135
-rw-r--r--src/pkg/net/http/header_test.go123
-rw-r--r--src/pkg/net/http/httptest/example_test.go50
-rw-r--r--src/pkg/net/http/httptest/recorder.go24
-rw-r--r--src/pkg/net/http/httptest/recorder_test.go90
-rw-r--r--src/pkg/net/http/httptest/server.go73
-rw-r--r--src/pkg/net/http/httputil/chunked.go59
-rw-r--r--src/pkg/net/http/httputil/chunked_test.go54
-rw-r--r--src/pkg/net/http/httputil/dump.go4
-rw-r--r--src/pkg/net/http/httputil/reverseproxy.go58
-rw-r--r--src/pkg/net/http/httputil/reverseproxy_test.go84
-rw-r--r--src/pkg/net/http/jar.go19
-rw-r--r--src/pkg/net/http/lex.go206
-rw-r--r--src/pkg/net/http/lex_test.go65
-rw-r--r--src/pkg/net/http/npn_test.go118
-rw-r--r--src/pkg/net/http/pprof/pprof.go17
-rw-r--r--src/pkg/net/http/proxy_test.go4
-rw-r--r--src/pkg/net/http/range_test.go22
-rw-r--r--src/pkg/net/http/readrequest_test.go48
-rw-r--r--src/pkg/net/http/request.go247
-rw-r--r--src/pkg/net/http/request_test.go189
-rw-r--r--src/pkg/net/http/requestwrite_test.go63
-rw-r--r--src/pkg/net/http/response.go14
-rw-r--r--src/pkg/net/http/response_test.go162
-rw-r--r--src/pkg/net/http/responsewrite_test.go138
-rw-r--r--src/pkg/net/http/serve_test.go578
-rw-r--r--src/pkg/net/http/server.go873
-rw-r--r--src/pkg/net/http/server_test.go95
-rw-r--r--src/pkg/net/http/transfer.go100
-rw-r--r--src/pkg/net/http/transfer_test.go37
-rw-r--r--src/pkg/net/http/transport.go392
-rw-r--r--src/pkg/net/http/transport_test.go577
-rw-r--r--src/pkg/net/http/z_last_test.go60
-rw-r--r--src/pkg/net/interface.go16
-rw-r--r--src/pkg/net/interface_bsd.go104
-rw-r--r--src/pkg/net/interface_bsd_test.go52
-rw-r--r--src/pkg/net/interface_darwin.go35
-rw-r--r--src/pkg/net/interface_freebsd.go35
-rw-r--r--src/pkg/net/interface_linux.go109
-rw-r--r--src/pkg/net/interface_linux_test.go50
-rw-r--r--src/pkg/net/interface_netbsd.go10
-rw-r--r--src/pkg/net/interface_openbsd.go10
-rw-r--r--src/pkg/net/interface_stub.go17
-rw-r--r--src/pkg/net/interface_test.go116
-rw-r--r--src/pkg/net/interface_unix_test.go145
-rw-r--r--src/pkg/net/interface_windows.go28
-rw-r--r--src/pkg/net/ip.go11
-rw-r--r--src/pkg/net/ip_test.go123
-rw-r--r--src/pkg/net/ipraw_test.go413
-rw-r--r--src/pkg/net/iprawsock.go57
-rw-r--r--src/pkg/net/iprawsock_plan9.go97
-rw-r--r--src/pkg/net/iprawsock_posix.go165
-rw-r--r--src/pkg/net/ipsock.go180
-rw-r--r--src/pkg/net/ipsock_plan9.go214
-rw-r--r--src/pkg/net/ipsock_posix.go35
-rw-r--r--src/pkg/net/lookup.go (renamed from src/pkg/net/doc.go)46
-rw-r--r--src/pkg/net/lookup_plan9.go15
-rw-r--r--src/pkg/net/lookup_test.go36
-rw-r--r--src/pkg/net/lookup_unix.go13
-rw-r--r--src/pkg/net/lookup_windows.go155
-rw-r--r--src/pkg/net/mail/message.go15
-rw-r--r--src/pkg/net/mail/message_test.go17
-rw-r--r--src/pkg/net/multicast_posix_test.go180
-rw-r--r--src/pkg/net/multicast_test.go234
-rw-r--r--src/pkg/net/net.go169
-rw-r--r--src/pkg/net/net_test.go126
-rw-r--r--src/pkg/net/newpollserver_unix.go (renamed from src/pkg/net/newpollserver.go)2
-rw-r--r--src/pkg/net/packetconn_test.go200
-rw-r--r--src/pkg/net/parse_test.go3
-rw-r--r--src/pkg/net/port.go73
-rw-r--r--src/pkg/net/port_test.go2
-rw-r--r--src/pkg/net/port_unix.go69
-rw-r--r--src/pkg/net/protoconn_test.go358
-rw-r--r--src/pkg/net/rpc/client.go43
-rw-r--r--src/pkg/net/rpc/jsonrpc/all_test.go61
-rw-r--r--src/pkg/net/rpc/jsonrpc/server.go13
-rw-r--r--src/pkg/net/rpc/server.go84
-rw-r--r--src/pkg/net/rpc/server_test.go72
-rw-r--r--src/pkg/net/sendfile_freebsd.go105
-rw-r--r--src/pkg/net/sendfile_linux.go4
-rw-r--r--src/pkg/net/sendfile_stub.go2
-rw-r--r--src/pkg/net/sendfile_windows.go4
-rw-r--r--src/pkg/net/server_test.go51
-rw-r--r--src/pkg/net/smtp/smtp.go65
-rw-r--r--src/pkg/net/smtp/smtp_test.go229
-rw-r--r--src/pkg/net/sock.go87
-rw-r--r--src/pkg/net/sock_bsd.go31
-rw-r--r--src/pkg/net/sock_cloexec.go69
-rw-r--r--src/pkg/net/sock_linux.go31
-rw-r--r--src/pkg/net/sock_posix.go67
-rw-r--r--src/pkg/net/sock_unix.go36
-rw-r--r--src/pkg/net/sock_windows.go29
-rw-r--r--src/pkg/net/sockopt_posix.go (renamed from src/pkg/net/sockopt.go)37
-rw-r--r--src/pkg/net/sockoptip.go219
-rw-r--r--src/pkg/net/sockoptip_bsd.go36
-rw-r--r--src/pkg/net/sockoptip_darwin.go90
-rw-r--r--src/pkg/net/sockoptip_freebsd.go92
-rw-r--r--src/pkg/net/sockoptip_linux.go101
-rw-r--r--src/pkg/net/sockoptip_netbsd.go39
-rw-r--r--src/pkg/net/sockoptip_openbsd.go90
-rw-r--r--src/pkg/net/sockoptip_posix.go73
-rw-r--r--src/pkg/net/sockoptip_windows.go61
-rw-r--r--src/pkg/net/sys_cloexec.go54
-rw-r--r--src/pkg/net/tcp_test.go206
-rw-r--r--src/pkg/net/tcpsock.go12
-rw-r--r--src/pkg/net/tcpsock_plan9.go161
-rw-r--r--src/pkg/net/tcpsock_posix.go189
-rw-r--r--src/pkg/net/textproto/reader.go129
-rw-r--r--src/pkg/net/textproto/reader_test.go94
-rw-r--r--src/pkg/net/textproto/textproto.go31
-rw-r--r--src/pkg/net/timeout_test.go540
-rw-r--r--src/pkg/net/udp_test.go63
-rw-r--r--src/pkg/net/udpsock.go16
-rw-r--r--src/pkg/net/udpsock_plan9.go134
-rw-r--r--src/pkg/net/udpsock_posix.go229
-rw-r--r--src/pkg/net/unicast_posix_test.go (renamed from src/pkg/net/unicast_test.go)112
-rw-r--r--src/pkg/net/unix_test.go144
-rw-r--r--src/pkg/net/unixsock_plan9.go145
-rw-r--r--src/pkg/net/unixsock_posix.go286
-rw-r--r--src/pkg/net/url/url.go92
-rw-r--r--src/pkg/net/url/url_test.go107
-rw-r--r--src/pkg/os/dir_plan9.go259
-rw-r--r--src/pkg/os/doc.go4
-rw-r--r--src/pkg/os/env.go2
-rw-r--r--src/pkg/os/env_unix_test.go30
-rw-r--r--src/pkg/os/error.go2
-rw-r--r--src/pkg/os/error_plan9.go21
-rw-r--r--src/pkg/os/error_posix.go21
-rw-r--r--src/pkg/os/error_test.go51
-rw-r--r--src/pkg/os/error_windows.go21
-rw-r--r--src/pkg/os/error_windows_test.go47
-rw-r--r--src/pkg/os/exec.go19
-rw-r--r--src/pkg/os/exec/exec.go10
-rw-r--r--src/pkg/os/exec/exec_test.go134
-rw-r--r--src/pkg/os/exec/lp_plan9.go3
-rw-r--r--src/pkg/os/exec/lp_unix.go3
-rw-r--r--src/pkg/os/exec/lp_unix_test.go55
-rw-r--r--src/pkg/os/exec/lp_windows.go35
-rw-r--r--src/pkg/os/exec_plan9.go58
-rw-r--r--src/pkg/os/exec_posix.go17
-rw-r--r--src/pkg/os/exec_unix.go4
-rw-r--r--src/pkg/os/exec_windows.go4
-rw-r--r--src/pkg/os/file.go4
-rw-r--r--src/pkg/os/file_plan9.go144
-rw-r--r--src/pkg/os/file_posix.go21
-rw-r--r--src/pkg/os/file_unix.go32
-rw-r--r--src/pkg/os/file_windows.go188
-rw-r--r--src/pkg/os/getwd.go23
-rw-r--r--src/pkg/os/os_test.go78
-rw-r--r--src/pkg/os/path_plan9.go4
-rw-r--r--src/pkg/os/path_test.go15
-rw-r--r--src/pkg/os/pipe_bsd.go28
-rw-r--r--src/pkg/os/pipe_linux.go33
-rw-r--r--src/pkg/os/proc.go2
-rw-r--r--src/pkg/os/signal/example_test.go19
-rw-r--r--src/pkg/os/signal/signal_test.go41
-rw-r--r--src/pkg/os/signal/signal_windows_test.go77
-rw-r--r--src/pkg/os/stat_darwin.go6
-rw-r--r--src/pkg/os/stat_freebsd.go6
-rw-r--r--src/pkg/os/stat_linux.go6
-rw-r--r--src/pkg/os/stat_netbsd.go10
-rw-r--r--src/pkg/os/stat_openbsd.go6
-rw-r--r--src/pkg/os/stat_plan9.go52
-rw-r--r--src/pkg/os/stat_windows.go149
-rw-r--r--src/pkg/os/types.go27
-rw-r--r--src/pkg/os/types_notwin.go25
-rw-r--r--src/pkg/os/types_windows.go104
-rw-r--r--src/pkg/os/user/lookup.go22
-rw-r--r--src/pkg/os/user/lookup_stubs.go6
-rw-r--r--src/pkg/os/user/lookup_unix.go21
-rw-r--r--src/pkg/os/user/lookup_windows.go25
-rw-r--r--src/pkg/os/user/user_test.go36
-rw-r--r--src/pkg/path/filepath/example_unix_test.go39
-rw-r--r--src/pkg/path/filepath/match_test.go3
-rw-r--r--src/pkg/path/filepath/path.go130
-rw-r--r--src/pkg/path/filepath/path_plan9.go15
-rw-r--r--src/pkg/path/filepath/path_test.go52
-rw-r--r--src/pkg/path/filepath/path_unix.go15
-rw-r--r--src/pkg/path/filepath/path_windows.go55
-rw-r--r--src/pkg/path/filepath/path_windows_test.go89
-rw-r--r--src/pkg/path/filepath/symlink_windows.go10
-rw-r--r--src/pkg/path/path.go83
-rw-r--r--src/pkg/path/path_test.go11
-rw-r--r--src/pkg/reflect/all_test.go1279
-rw-r--r--src/pkg/reflect/asm_386.s13
-rw-r--r--src/pkg/reflect/asm_amd64.s13
-rw-r--r--src/pkg/reflect/asm_arm.s13
-rw-r--r--src/pkg/reflect/deepequal.go9
-rw-r--r--src/pkg/reflect/example_test.go52
-rw-r--r--src/pkg/reflect/export_test.go18
-rw-r--r--src/pkg/reflect/makefunc.go67
-rw-r--r--src/pkg/reflect/set_test.go8
-rw-r--r--src/pkg/reflect/type.go808
-rw-r--r--src/pkg/reflect/value.go716
-rw-r--r--src/pkg/regexp/all_test.go105
-rw-r--r--src/pkg/regexp/example_test.go144
-rw-r--r--src/pkg/regexp/exec_test.go50
-rw-r--r--src/pkg/regexp/regexp.go74
-rw-r--r--src/pkg/regexp/syntax/compile.go4
-rw-r--r--src/pkg/regexp/syntax/doc.go127
-rw-r--r--src/pkg/regexp/syntax/parse.go14
-rw-r--r--src/pkg/runtime/alg.c21
-rw-r--r--src/pkg/runtime/append_test.go71
-rw-r--r--src/pkg/runtime/arch_386.h4
-rw-r--r--src/pkg/runtime/arch_amd64.h4
-rw-r--r--src/pkg/runtime/arch_arm.h4
-rw-r--r--src/pkg/runtime/asm_386.s188
-rw-r--r--src/pkg/runtime/asm_amd64.s178
-rw-r--r--src/pkg/runtime/asm_arm.s265
-rw-r--r--src/pkg/runtime/atomic_386.c13
-rw-r--r--src/pkg/runtime/atomic_amd64.c7
-rw-r--r--src/pkg/runtime/atomic_arm.c62
-rw-r--r--src/pkg/runtime/callback_windows_386.c3
-rw-r--r--src/pkg/runtime/callback_windows_amd64.c3
-rw-r--r--src/pkg/runtime/cgo/asm_386.s29
-rw-r--r--src/pkg/runtime/cgo/asm_amd64.s45
-rw-r--r--src/pkg/runtime/cgo/asm_arm.s23
-rw-r--r--src/pkg/runtime/cgo/callbacks.c22
-rw-r--r--src/pkg/runtime/cgo/cgo.go2
-rw-r--r--src/pkg/runtime/cgo/cgo_arm.c12
-rw-r--r--src/pkg/runtime/cgo/gcc_386.S25
-rw-r--r--src/pkg/runtime/cgo/gcc_amd64.S48
-rw-r--r--src/pkg/runtime/cgo/gcc_arm.S37
-rw-r--r--src/pkg/runtime/cgo/gcc_darwin_386.c13
-rw-r--r--src/pkg/runtime/cgo/gcc_darwin_amd64.c11
-rw-r--r--src/pkg/runtime/cgo/gcc_freebsd_386.c10
-rw-r--r--src/pkg/runtime/cgo/gcc_freebsd_amd64.c10
-rw-r--r--src/pkg/runtime/cgo/gcc_freebsd_arm.c114
-rw-r--r--src/pkg/runtime/cgo/gcc_linux_386.c9
-rw-r--r--src/pkg/runtime/cgo/gcc_linux_amd64.c7
-rw-r--r--src/pkg/runtime/cgo/gcc_linux_arm.c108
-rw-r--r--src/pkg/runtime/cgo/gcc_netbsd_386.c80
-rw-r--r--src/pkg/runtime/cgo/gcc_netbsd_amd64.c80
-rw-r--r--src/pkg/runtime/cgo/gcc_netbsd_arm.c122
-rw-r--r--src/pkg/runtime/cgo/gcc_openbsd_386.c169
-rw-r--r--src/pkg/runtime/cgo/gcc_openbsd_amd64.c169
-rw-r--r--src/pkg/runtime/cgo/gcc_setenv.c8
-rw-r--r--src/pkg/runtime/cgo/gcc_util.c16
-rw-r--r--src/pkg/runtime/cgo/gcc_windows_386.c13
-rw-r--r--src/pkg/runtime/cgo/gcc_windows_amd64.c13
-rw-r--r--src/pkg/runtime/cgo/libcgo.h8
-rw-r--r--src/pkg/runtime/cgo/netbsd.c13
-rw-r--r--src/pkg/runtime/cgo/openbsd.c21
-rw-r--r--src/pkg/runtime/cgo/setenv.c10
-rw-r--r--src/pkg/runtime/cgocall.c109
-rw-r--r--src/pkg/runtime/chan.c316
-rw-r--r--src/pkg/runtime/closure_386.c105
-rw-r--r--src/pkg/runtime/closure_amd64.c123
-rw-r--r--src/pkg/runtime/closure_arm.c129
-rw-r--r--src/pkg/runtime/complex.c30
-rw-r--r--src/pkg/runtime/complex_test.go67
-rw-r--r--src/pkg/runtime/cpuprof.c22
-rw-r--r--src/pkg/runtime/crash_cgo_test.go88
-rw-r--r--src/pkg/runtime/crash_test.go177
-rw-r--r--src/pkg/runtime/debug.go29
-rw-r--r--src/pkg/runtime/debug/debug.c9
-rw-r--r--src/pkg/runtime/debug/garbage.go101
-rw-r--r--src/pkg/runtime/debug/garbage_test.go100
-rw-r--r--src/pkg/runtime/debug/stack.go2
-rw-r--r--src/pkg/runtime/debug/stack_test.go2
-rw-r--r--src/pkg/runtime/defs_freebsd.go8
-rw-r--r--src/pkg/runtime/defs_freebsd_386.h317
-rw-r--r--src/pkg/runtime/defs_freebsd_amd64.h339
-rw-r--r--src/pkg/runtime/defs_freebsd_arm.h163
-rw-r--r--src/pkg/runtime/defs_linux_386.h2
-rw-r--r--src/pkg/runtime/defs_linux_arm.h2
-rw-r--r--src/pkg/runtime/defs_netbsd.go21
-rw-r--r--src/pkg/runtime/defs_netbsd_386.go42
-rw-r--r--src/pkg/runtime/defs_netbsd_386.h261
-rw-r--r--src/pkg/runtime/defs_netbsd_amd64.go49
-rw-r--r--src/pkg/runtime/defs_netbsd_amd64.h284
-rw-r--r--src/pkg/runtime/defs_netbsd_arm.h140
-rw-r--r--src/pkg/runtime/defs_openbsd.go17
-rw-r--r--src/pkg/runtime/defs_openbsd_386.h248
-rw-r--r--src/pkg/runtime/defs_openbsd_amd64.h272
-rw-r--r--src/pkg/runtime/defs_plan9_386.h31
-rw-r--r--src/pkg/runtime/defs_plan9_amd64.h34
-rw-r--r--src/pkg/runtime/env_plan9.c33
-rw-r--r--src/pkg/runtime/env_posix.c61
-rw-r--r--src/pkg/runtime/export_test.go42
-rw-r--r--src/pkg/runtime/extern.go6
-rw-r--r--src/pkg/runtime/float.c171
-rw-r--r--src/pkg/runtime/gc_test.go55
-rw-r--r--src/pkg/runtime/hashmap.c151
-rw-r--r--src/pkg/runtime/hashmap.h29
-rw-r--r--src/pkg/runtime/iface.c134
-rw-r--r--src/pkg/runtime/iface_test.go138
-rw-r--r--src/pkg/runtime/lfstack.c65
-rw-r--r--src/pkg/runtime/lfstack_test.go130
-rw-r--r--src/pkg/runtime/lock_futex.c3
-rw-r--r--src/pkg/runtime/malloc.goc424
-rw-r--r--src/pkg/runtime/malloc.h132
-rw-r--r--src/pkg/runtime/mallocrep1.go1
-rw-r--r--src/pkg/runtime/mcache.c9
-rw-r--r--src/pkg/runtime/mcentral.c97
-rw-r--r--src/pkg/runtime/mem.go5
-rw-r--r--src/pkg/runtime/mem_darwin.c4
-rw-r--r--src/pkg/runtime/mem_freebsd.c10
-rw-r--r--src/pkg/runtime/mem_linux.c11
-rw-r--r--src/pkg/runtime/mem_netbsd.c10
-rw-r--r--src/pkg/runtime/mem_openbsd.c10
-rw-r--r--src/pkg/runtime/mem_plan9.c5
-rw-r--r--src/pkg/runtime/memclr_arm.s (renamed from src/pkg/runtime/memset_arm.s)53
-rw-r--r--src/pkg/runtime/memmove_arm.s96
-rw-r--r--src/pkg/runtime/mfinal.c10
-rw-r--r--src/pkg/runtime/mgc0.c1891
-rw-r--r--src/pkg/runtime/mgc0.go15
-rw-r--r--src/pkg/runtime/mgc0.h43
-rw-r--r--src/pkg/runtime/mheap.c145
-rw-r--r--src/pkg/runtime/mprof.goc272
-rw-r--r--src/pkg/runtime/os_darwin.h4
-rw-r--r--src/pkg/runtime/os_freebsd.h4
-rw-r--r--src/pkg/runtime/os_linux.h4
-rw-r--r--src/pkg/runtime/os_netbsd.h21
-rw-r--r--src/pkg/runtime/os_openbsd.h17
-rw-r--r--src/pkg/runtime/os_plan9.h23
-rw-r--r--src/pkg/runtime/os_windows.h3
-rw-r--r--src/pkg/runtime/panic.c487
-rw-r--r--src/pkg/runtime/parfor.c215
-rw-r--r--src/pkg/runtime/parfor_test.go144
-rw-r--r--src/pkg/runtime/pprof/pprof.go98
-rw-r--r--src/pkg/runtime/pprof/pprof_test.go19
-rw-r--r--src/pkg/runtime/print.c36
-rw-r--r--src/pkg/runtime/proc.c2934
-rw-r--r--src/pkg/runtime/proc_test.go212
-rw-r--r--src/pkg/runtime/race.c350
-rw-r--r--src/pkg/runtime/race.go29
-rw-r--r--src/pkg/runtime/race.h33
-rw-r--r--src/pkg/runtime/race/README11
-rw-r--r--src/pkg/runtime/race/race.go122
-rw-r--r--src/pkg/runtime/race/race_darwin_amd64.sysobin0 -> 146952 bytes
-rw-r--r--src/pkg/runtime/race/race_linux_amd64.sysobin0 -> 142848 bytes
-rw-r--r--src/pkg/runtime/race/race_test.go157
-rw-r--r--src/pkg/runtime/race/race_windows_amd64.sysobin0 -> 121376 bytes
-rw-r--r--src/pkg/runtime/race/testdata/atomic_test.go271
-rw-r--r--src/pkg/runtime/race/testdata/cgo_test.go20
-rw-r--r--src/pkg/runtime/race/testdata/cgo_test_main.go30
-rw-r--r--src/pkg/runtime/race/testdata/chan_test.go457
-rw-r--r--src/pkg/runtime/race/testdata/comp_test.go132
-rw-r--r--src/pkg/runtime/race/testdata/finalizer_test.go67
-rw-r--r--src/pkg/runtime/race/testdata/io_test.go69
-rw-r--r--src/pkg/runtime/race/testdata/map_test.go163
-rw-r--r--src/pkg/runtime/race/testdata/mop_test.go1384
-rw-r--r--src/pkg/runtime/race/testdata/mutex_test.go138
-rw-r--r--src/pkg/runtime/race/testdata/regression_test.go150
-rw-r--r--src/pkg/runtime/race/testdata/rwmutex_test.go134
-rw-r--r--src/pkg/runtime/race/testdata/select_test.go286
-rw-r--r--src/pkg/runtime/race/testdata/slice_test.go465
-rw-r--r--src/pkg/runtime/race/testdata/sync_test.go197
-rw-r--r--src/pkg/runtime/race/testdata/waitgroup_test.go232
-rw-r--r--src/pkg/runtime/race0.c133
-rw-r--r--src/pkg/runtime/race_amd64.s14
-rw-r--r--src/pkg/runtime/rt0_freebsd_arm.s8
-rw-r--r--src/pkg/runtime/rt0_linux_arm.s18
-rw-r--r--src/pkg/runtime/rt0_netbsd_arm.s8
-rw-r--r--src/pkg/runtime/rt0_plan9_386.s1
-rw-r--r--src/pkg/runtime/rt0_plan9_amd64.s11
-rw-r--r--src/pkg/runtime/rt0_windows_386.s5
-rw-r--r--src/pkg/runtime/rune.c9
-rw-r--r--src/pkg/runtime/runtime-gdb.py17
-rw-r--r--src/pkg/runtime/runtime.c216
-rw-r--r--src/pkg/runtime/runtime.h360
-rw-r--r--src/pkg/runtime/runtime1.goc4
-rw-r--r--src/pkg/runtime/runtime_test.go41
-rw-r--r--src/pkg/runtime/sema.goc61
-rw-r--r--src/pkg/runtime/signal_darwin_386.c19
-rw-r--r--src/pkg/runtime/signal_darwin_amd64.c19
-rw-r--r--src/pkg/runtime/signal_freebsd_386.c24
-rw-r--r--src/pkg/runtime/signal_freebsd_amd64.c24
-rw-r--r--src/pkg/runtime/signal_freebsd_arm.c193
-rw-r--r--src/pkg/runtime/signal_linux_386.c21
-rw-r--r--src/pkg/runtime/signal_linux_amd64.c23
-rw-r--r--src/pkg/runtime/signal_linux_arm.c115
-rw-r--r--src/pkg/runtime/signal_netbsd_386.c122
-rw-r--r--src/pkg/runtime/signal_netbsd_amd64.c143
-rw-r--r--src/pkg/runtime/signal_netbsd_arm.c208
-rw-r--r--src/pkg/runtime/signal_openbsd_386.c19
-rw-r--r--src/pkg/runtime/signal_openbsd_amd64.c27
-rw-r--r--src/pkg/runtime/signal_plan9_386.c101
-rw-r--r--src/pkg/runtime/signal_plan9_amd64.c127
-rw-r--r--src/pkg/runtime/signal_windows_386.c8
-rw-r--r--src/pkg/runtime/signal_windows_amd64.c4
-rw-r--r--src/pkg/runtime/signals_linux.h2
-rw-r--r--src/pkg/runtime/signals_netbsd.h20
-rw-r--r--src/pkg/runtime/signals_openbsd.h20
-rw-r--r--src/pkg/runtime/signals_plan9.h25
-rw-r--r--src/pkg/runtime/sigqueue.goc104
-rw-r--r--src/pkg/runtime/slice.c292
-rw-r--r--src/pkg/runtime/softfloat_arm.c17
-rw-r--r--src/pkg/runtime/stack.c282
-rw-r--r--src/pkg/runtime/stack.h12
-rw-r--r--src/pkg/runtime/stack_test.go51
-rw-r--r--src/pkg/runtime/string.goc98
-rw-r--r--src/pkg/runtime/string_test.go45
-rw-r--r--src/pkg/runtime/symtab.c256
-rw-r--r--src/pkg/runtime/sys_darwin_386.s151
-rw-r--r--src/pkg/runtime/sys_darwin_amd64.s94
-rw-r--r--src/pkg/runtime/sys_freebsd_386.s38
-rw-r--r--src/pkg/runtime/sys_freebsd_amd64.s40
-rw-r--r--src/pkg/runtime/sys_freebsd_arm.s260
-rw-r--r--src/pkg/runtime/sys_linux_386.s45
-rw-r--r--src/pkg/runtime/sys_linux_amd64.s81
-rw-r--r--src/pkg/runtime/sys_linux_arm.s62
-rw-r--r--src/pkg/runtime/sys_netbsd_386.s220
-rw-r--r--src/pkg/runtime/sys_netbsd_amd64.s181
-rw-r--r--src/pkg/runtime/sys_netbsd_arm.s280
-rw-r--r--src/pkg/runtime/sys_openbsd_386.s135
-rw-r--r--src/pkg/runtime/sys_openbsd_amd64.s113
-rw-r--r--src/pkg/runtime/sys_plan9_386.s85
-rw-r--r--src/pkg/runtime/sys_plan9_amd64.s208
-rw-r--r--src/pkg/runtime/sys_windows_386.s42
-rw-r--r--src/pkg/runtime/sys_windows_amd64.s16
-rw-r--r--src/pkg/runtime/syscall_windows_test.go3
-rw-r--r--src/pkg/runtime/thread_darwin.c73
-rw-r--r--src/pkg/runtime/thread_freebsd.c62
-rw-r--r--src/pkg/runtime/thread_linux.c79
-rw-r--r--src/pkg/runtime/thread_netbsd.c113
-rw-r--r--src/pkg/runtime/thread_openbsd.c76
-rw-r--r--src/pkg/runtime/thread_plan9.c222
-rw-r--r--src/pkg/runtime/thread_windows.c26
-rw-r--r--src/pkg/runtime/time.goc45
-rw-r--r--src/pkg/runtime/time_plan9_386.c34
-rw-r--r--src/pkg/runtime/traceback_arm.c103
-rw-r--r--src/pkg/runtime/traceback_x86.c162
-rw-r--r--src/pkg/runtime/type.go15
-rw-r--r--src/pkg/runtime/type.h56
-rw-r--r--src/pkg/runtime/typekind.h41
-rw-r--r--src/pkg/runtime/vdso_linux_amd64.c331
-rw-r--r--src/pkg/runtime/vlop_arm.s316
-rw-r--r--src/pkg/runtime/vlop_arm_test.go70
-rw-r--r--src/pkg/sort/example_keys_test.go96
-rw-r--r--src/pkg/sort/example_reverse_test.go30
-rw-r--r--src/pkg/sort/example_test.go7
-rw-r--r--src/pkg/sort/search.go22
-rw-r--r--src/pkg/sort/search_test.go22
-rw-r--r--src/pkg/sort/sort.go57
-rw-r--r--src/pkg/sort/sort_test.go41
-rw-r--r--src/pkg/strconv/atof.go303
-rw-r--r--src/pkg/strconv/atof_test.go129
-rw-r--r--src/pkg/strconv/atoi.go24
-rw-r--r--src/pkg/strconv/atoi_test.go24
-rw-r--r--src/pkg/strconv/decimal.go2
-rw-r--r--src/pkg/strconv/extfloat.go358
-rw-r--r--src/pkg/strconv/fp_test.go31
-rw-r--r--src/pkg/strconv/ftoa.go161
-rw-r--r--src/pkg/strconv/ftoa_test.go70
-rw-r--r--src/pkg/strconv/isprint.go125
-rw-r--r--src/pkg/strconv/itoa_test.go30
-rw-r--r--src/pkg/strconv/quote.go5
-rw-r--r--src/pkg/strconv/strconv_test.go52
-rw-r--r--src/pkg/strconv/testdata/testfp.txt (renamed from src/pkg/strconv/testfp.txt)0
-rw-r--r--src/pkg/strings/example_test.go16
-rw-r--r--src/pkg/strings/export_test.go36
-rw-r--r--src/pkg/strings/reader.go21
-rw-r--r--src/pkg/strings/reader_test.go23
-rw-r--r--src/pkg/strings/replace.go416
-rw-r--r--src/pkg/strings/replace_test.go437
-rw-r--r--src/pkg/strings/search.go124
-rw-r--r--src/pkg/strings/search_test.go90
-rw-r--r--src/pkg/strings/strings.go116
-rw-r--r--src/pkg/strings/strings_test.go121
-rw-r--r--src/pkg/sync/atomic/64bit_arm.go36
-rw-r--r--src/pkg/sync/atomic/asm_386.s52
-rw-r--r--src/pkg/sync/atomic/asm_amd64.s36
-rw-r--r--src/pkg/sync/atomic/asm_arm.s14
-rw-r--r--src/pkg/sync/atomic/asm_freebsd_arm.s89
-rw-r--r--src/pkg/sync/atomic/asm_linux_arm.s87
-rw-r--r--src/pkg/sync/atomic/asm_netbsd_arm.s89
-rw-r--r--src/pkg/sync/atomic/atomic_test.go189
-rw-r--r--src/pkg/sync/atomic/doc.go55
-rw-r--r--src/pkg/sync/atomic/race.go213
-rw-r--r--src/pkg/sync/cond.go18
-rw-r--r--src/pkg/sync/example_test.go6
-rw-r--r--src/pkg/sync/mutex.go16
-rw-r--r--src/pkg/sync/once.go2
-rw-r--r--src/pkg/sync/once_test.go29
-rw-r--r--src/pkg/sync/race.go34
-rw-r--r--src/pkg/sync/race0.go28
-rw-r--r--src/pkg/sync/rwmutex.go36
-rw-r--r--src/pkg/sync/waitgroup.go39
-rw-r--r--src/pkg/sync/waitgroup_test.go2
-rw-r--r--src/pkg/syscall/asm_freebsd_amd64.s34
-rw-r--r--src/pkg/syscall/asm_freebsd_arm.s124
-rw-r--r--src/pkg/syscall/asm_linux_amd64.s4
-rw-r--r--src/pkg/syscall/asm_netbsd_arm.s124
-rw-r--r--src/pkg/syscall/asm_plan9_386.s8
-rw-r--r--src/pkg/syscall/asm_plan9_amd64.s165
-rw-r--r--src/pkg/syscall/asm_windows_386.s2
-rw-r--r--src/pkg/syscall/asm_windows_amd64.s2
-rw-r--r--src/pkg/syscall/creds_test.go113
-rw-r--r--src/pkg/syscall/dir_plan9.go205
-rw-r--r--src/pkg/syscall/dll_windows.go54
-rw-r--r--src/pkg/syscall/env_plan9.go56
-rw-r--r--src/pkg/syscall/env_unix.go10
-rw-r--r--src/pkg/syscall/env_windows.go20
-rw-r--r--src/pkg/syscall/exec_bsd.go14
-rw-r--r--src/pkg/syscall/exec_linux.go26
-rw-r--r--src/pkg/syscall/exec_plan9.go260
-rw-r--r--src/pkg/syscall/exec_unix.go74
-rw-r--r--src/pkg/syscall/exec_windows.go28
-rwxr-xr-xsrc/pkg/syscall/mkall.sh16
-rwxr-xr-xsrc/pkg/syscall/mkerrors.sh28
-rwxr-xr-xsrc/pkg/syscall/mksyscall.pl24
-rwxr-xr-xsrc/pkg/syscall/mksyscall_windows.pl27
-rw-r--r--src/pkg/syscall/netlink_linux.go193
-rw-r--r--src/pkg/syscall/passfd_test.go201
-rw-r--r--src/pkg/syscall/race.go22
-rw-r--r--src/pkg/syscall/race0.go19
-rw-r--r--src/pkg/syscall/route_bsd.go41
-rw-r--r--src/pkg/syscall/route_darwin.go34
-rw-r--r--src/pkg/syscall/route_freebsd.go45
-rw-r--r--src/pkg/syscall/route_netbsd.go32
-rw-r--r--src/pkg/syscall/route_openbsd.go32
-rw-r--r--src/pkg/syscall/security_windows.go32
-rw-r--r--src/pkg/syscall/sockcmsg_linux.go26
-rw-r--r--src/pkg/syscall/sockcmsg_unix.go82
-rw-r--r--src/pkg/syscall/syscall.go52
-rw-r--r--src/pkg/syscall/syscall_bsd.go48
-rw-r--r--src/pkg/syscall/syscall_darwin.go17
-rw-r--r--src/pkg/syscall/syscall_freebsd.go19
-rw-r--r--src/pkg/syscall/syscall_freebsd_386.go14
-rw-r--r--src/pkg/syscall/syscall_freebsd_amd64.go16
-rw-r--r--src/pkg/syscall/syscall_freebsd_arm.go58
-rw-r--r--src/pkg/syscall/syscall_linux.go118
-rw-r--r--src/pkg/syscall/syscall_linux_386.go84
-rw-r--r--src/pkg/syscall/syscall_linux_amd64.go7
-rw-r--r--src/pkg/syscall/syscall_linux_arm.go97
-rw-r--r--src/pkg/syscall/syscall_netbsd.go79
-rw-r--r--src/pkg/syscall/syscall_netbsd_386.go8
-rw-r--r--src/pkg/syscall/syscall_netbsd_amd64.go10
-rw-r--r--src/pkg/syscall/syscall_netbsd_arm.go42
-rw-r--r--src/pkg/syscall/syscall_openbsd.go10
-rw-r--r--src/pkg/syscall/syscall_plan9.go98
-rw-r--r--src/pkg/syscall/syscall_plan9_386.go27
-rw-r--r--src/pkg/syscall/syscall_plan9_amd64.go14
-rw-r--r--src/pkg/syscall/syscall_unix.go24
-rw-r--r--src/pkg/syscall/syscall_windows.go319
-rw-r--r--src/pkg/syscall/syscall_windows_test.go24
-rw-r--r--src/pkg/syscall/types_darwin.go8
-rw-r--r--src/pkg/syscall/types_freebsd.go15
-rw-r--r--src/pkg/syscall/types_linux.go175
-rw-r--r--src/pkg/syscall/types_netbsd.go35
-rw-r--r--src/pkg/syscall/types_openbsd.go13
-rw-r--r--src/pkg/syscall/zerrors_darwin_386.go93
-rw-r--r--src/pkg/syscall/zerrors_darwin_amd64.go93
-rw-r--r--src/pkg/syscall/zerrors_freebsd_386.go2
-rw-r--r--src/pkg/syscall/zerrors_freebsd_amd64.go2
-rw-r--r--src/pkg/syscall/zerrors_freebsd_arm.go1652
-rw-r--r--src/pkg/syscall/zerrors_linux_386.go1
-rw-r--r--src/pkg/syscall/zerrors_linux_amd64.go1
-rw-r--r--src/pkg/syscall/zerrors_linux_arm.go1
-rw-r--r--src/pkg/syscall/zerrors_netbsd_386.go907
-rw-r--r--src/pkg/syscall/zerrors_netbsd_amd64.go899
-rw-r--r--src/pkg/syscall/zerrors_netbsd_arm.go1644
-rw-r--r--src/pkg/syscall/zerrors_openbsd_386.go109
-rw-r--r--src/pkg/syscall/zerrors_openbsd_amd64.go107
-rw-r--r--src/pkg/syscall/zerrors_plan9_386.go32
-rw-r--r--src/pkg/syscall/zerrors_plan9_amd64.go48
-rw-r--r--src/pkg/syscall/zsyscall_darwin_386.go239
-rw-r--r--src/pkg/syscall/zsyscall_darwin_amd64.go239
-rw-r--r--src/pkg/syscall/zsyscall_freebsd_386.go227
-rw-r--r--src/pkg/syscall/zsyscall_freebsd_amd64.go227
-rw-r--r--src/pkg/syscall/zsyscall_freebsd_arm.go1308
-rw-r--r--src/pkg/syscall/zsyscall_linux_386.go464
-rw-r--r--src/pkg/syscall/zsyscall_linux_amd64.go484
-rw-r--r--src/pkg/syscall/zsyscall_linux_arm.go495
-rw-r--r--src/pkg/syscall/zsyscall_netbsd_386.go214
-rw-r--r--src/pkg/syscall/zsyscall_netbsd_amd64.go214
-rw-r--r--src/pkg/syscall/zsyscall_netbsd_arm.go1228
-rw-r--r--src/pkg/syscall/zsyscall_openbsd_386.go220
-rw-r--r--src/pkg/syscall/zsyscall_openbsd_amd64.go220
-rw-r--r--src/pkg/syscall/zsyscall_plan9_386.go85
-rw-r--r--src/pkg/syscall/zsyscall_plan9_amd64.go282
-rw-r--r--src/pkg/syscall/zsyscall_windows_386.go281
-rw-r--r--src/pkg/syscall/zsyscall_windows_amd64.go281
-rw-r--r--src/pkg/syscall/zsysctl_openbsd.go2
-rw-r--r--src/pkg/syscall/zsysnum_freebsd_386.go2
-rw-r--r--src/pkg/syscall/zsysnum_freebsd_amd64.go2
-rw-r--r--src/pkg/syscall/zsysnum_freebsd_arm.go340
-rw-r--r--src/pkg/syscall/zsysnum_linux_arm.go2
-rw-r--r--src/pkg/syscall/zsysnum_netbsd_386.go13
-rw-r--r--src/pkg/syscall/zsysnum_netbsd_amd64.go13
-rw-r--r--src/pkg/syscall/zsysnum_netbsd_arm.go271
-rw-r--r--src/pkg/syscall/zsysnum_openbsd_386.go11
-rw-r--r--src/pkg/syscall/zsysnum_openbsd_amd64.go11
-rw-r--r--src/pkg/syscall/zsysnum_plan9_amd64.go48
-rw-r--r--src/pkg/syscall/ztypes_darwin_386.go17
-rw-r--r--src/pkg/syscall/ztypes_darwin_amd64.go18
-rw-r--r--src/pkg/syscall/ztypes_freebsd_386.go22
-rw-r--r--src/pkg/syscall/ztypes_freebsd_amd64.go22
-rw-r--r--src/pkg/syscall/ztypes_freebsd_arm.go436
-rw-r--r--src/pkg/syscall/ztypes_linux_386.go209
-rw-r--r--src/pkg/syscall/ztypes_linux_amd64.go209
-rw-r--r--src/pkg/syscall/ztypes_linux_arm.go205
-rw-r--r--src/pkg/syscall/ztypes_netbsd_386.go295
-rw-r--r--src/pkg/syscall/ztypes_netbsd_amd64.go280
-rw-r--r--src/pkg/syscall/ztypes_netbsd_arm.go373
-rw-r--r--src/pkg/syscall/ztypes_openbsd_386.go64
-rw-r--r--src/pkg/syscall/ztypes_openbsd_amd64.go67
-rw-r--r--src/pkg/syscall/ztypes_plan9_amd64.go75
-rw-r--r--src/pkg/syscall/ztypes_windows.go65
-rw-r--r--src/pkg/testing/allocs.go41
-rw-r--r--src/pkg/testing/benchmark.go84
-rw-r--r--src/pkg/testing/example.go80
-rw-r--r--src/pkg/testing/testing.go212
-rw-r--r--src/pkg/text/scanner/scanner.go23
-rw-r--r--src/pkg/text/scanner/scanner_test.go10
-rw-r--r--src/pkg/text/tabwriter/tabwriter.go2
-rw-r--r--src/pkg/text/template/doc.go17
-rw-r--r--src/pkg/text/template/example_test.go16
-rw-r--r--src/pkg/text/template/exec.go192
-rw-r--r--src/pkg/text/template/exec_test.go88
-rw-r--r--src/pkg/text/template/funcs.go18
-rw-r--r--src/pkg/text/template/parse/lex.go241
-rw-r--r--src/pkg/text/template/parse/lex_test.go384
-rw-r--r--src/pkg/text/template/parse/node.go242
-rw-r--r--src/pkg/text/template/parse/parse.go289
-rw-r--r--src/pkg/text/template/parse/parse_test.go87
-rw-r--r--src/pkg/text/template/template.go3
-rw-r--r--src/pkg/time/example_test.go97
-rw-r--r--src/pkg/time/export_test.go19
-rw-r--r--src/pkg/time/format.go485
-rw-r--r--src/pkg/time/internal_test.go2
-rw-r--r--src/pkg/time/sleep.go38
-rw-r--r--src/pkg/time/sleep_test.go74
-rw-r--r--src/pkg/time/tick.go4
-rw-r--r--src/pkg/time/time.go173
-rw-r--r--src/pkg/time/time_test.go424
-rw-r--r--src/pkg/time/zoneinfo.go39
-rw-r--r--src/pkg/time/zoneinfo_read.go12
-rw-r--r--src/pkg/time/zoneinfo_windows.go4
-rw-r--r--src/pkg/unicode/digit.go2
-rw-r--r--src/pkg/unicode/graphic.go32
-rw-r--r--src/pkg/unicode/letter.go80
-rw-r--r--src/pkg/unicode/letter_test.go118
-rw-r--r--src/pkg/unicode/maketables.go75
-rw-r--r--src/pkg/unicode/script_test.go11
-rw-r--r--src/pkg/unicode/tables.go783
-rw-r--r--src/pkg/unicode/utf8/example_test.go192
-rw-r--r--src/pkg/unicode/utf8/utf8.go56
-rw-r--r--src/pkg/unicode/utf8/utf8_test.go101
-rw-r--r--src/pkg/unsafe/unsafe.go4
1153 files changed, 109761 insertions, 24407 deletions
diff --git a/src/pkg/archive/tar/common.go b/src/pkg/archive/tar/common.go
index fc7a40923..60d207c48 100644
--- a/src/pkg/archive/tar/common.go
+++ b/src/pkg/archive/tar/common.go
@@ -9,9 +9,16 @@
// References:
// http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5
// http://www.gnu.org/software/tar/manual/html_node/Standard.html
+// http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html
package tar
-import "time"
+import (
+ "errors"
+ "fmt"
+ "os"
+ "path"
+ "time"
+)
const (
blockSize = 512
@@ -28,6 +35,8 @@ const (
TypeCont = '7' // reserved
TypeXHeader = 'x' // extended header
TypeXGlobalHeader = 'g' // global extended header
+ TypeGNULongName = 'L' // Next file has a long name
+ TypeGNULongLink = 'K' // Next file symlinks to a file w/ a long name
)
// A Header represents a single header in a tar archive.
@@ -49,6 +58,179 @@ type Header struct {
ChangeTime time.Time // status change time
}
+// File name constants from the tar spec.
+const (
+ fileNameSize = 100 // Maximum number of bytes in a standard tar name.
+ fileNamePrefixSize = 155 // Maximum number of ustar extension bytes.
+)
+
+// FileInfo returns an os.FileInfo for the Header.
+func (h *Header) FileInfo() os.FileInfo {
+ return headerFileInfo{h}
+}
+
+// headerFileInfo implements os.FileInfo.
+type headerFileInfo struct {
+ h *Header
+}
+
+func (fi headerFileInfo) Size() int64 { return fi.h.Size }
+func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
+func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
+func (fi headerFileInfo) Sys() interface{} { return fi.h }
+
+// Name returns the base name of the file.
+func (fi headerFileInfo) Name() string {
+ if fi.IsDir() {
+ return path.Clean(fi.h.Name)
+ }
+ return fi.h.Name
+}
+
+// Mode returns the permission and mode bits for the headerFileInfo.
+func (fi headerFileInfo) Mode() (mode os.FileMode) {
+ // Set file permission bits.
+ mode = os.FileMode(fi.h.Mode).Perm()
+
+ // Set setuid, setgid and sticky bits.
+ if fi.h.Mode&c_ISUID != 0 {
+ // setuid
+ mode |= os.ModeSetuid
+ }
+ if fi.h.Mode&c_ISGID != 0 {
+ // setgid
+ mode |= os.ModeSetgid
+ }
+ if fi.h.Mode&c_ISVTX != 0 {
+ // sticky
+ mode |= os.ModeSticky
+ }
+
+ // Set file mode bits.
+ // clear perm, setuid, setgid and sticky bits.
+ m := os.FileMode(fi.h.Mode) &^ 07777
+ if m == c_ISDIR {
+ // directory
+ mode |= os.ModeDir
+ }
+ if m == c_ISFIFO {
+ // named pipe (FIFO)
+ mode |= os.ModeNamedPipe
+ }
+ if m == c_ISLNK {
+ // symbolic link
+ mode |= os.ModeSymlink
+ }
+ if m == c_ISBLK {
+ // device file
+ mode |= os.ModeDevice
+ }
+ if m == c_ISCHR {
+ // Unix character device
+ mode |= os.ModeDevice
+ mode |= os.ModeCharDevice
+ }
+ if m == c_ISSOCK {
+ // Unix domain socket
+ mode |= os.ModeSocket
+ }
+
+ switch fi.h.Typeflag {
+ case TypeLink, TypeSymlink:
+ // hard link, symbolic link
+ mode |= os.ModeSymlink
+ case TypeChar:
+ // character device node
+ mode |= os.ModeDevice
+ mode |= os.ModeCharDevice
+ case TypeBlock:
+ // block device node
+ mode |= os.ModeDevice
+ case TypeDir:
+ // directory
+ mode |= os.ModeDir
+ case TypeFifo:
+ // fifo node
+ mode |= os.ModeNamedPipe
+ }
+
+ return mode
+}
+
+// sysStat, if non-nil, populates h from system-dependent fields of fi.
+var sysStat func(fi os.FileInfo, h *Header) error
+
+// Mode constants from the tar spec.
+const (
+ c_ISUID = 04000 // Set uid
+ c_ISGID = 02000 // Set gid
+ c_ISVTX = 01000 // Save text (sticky bit)
+ c_ISDIR = 040000 // Directory
+ c_ISFIFO = 010000 // FIFO
+ c_ISREG = 0100000 // Regular file
+ c_ISLNK = 0120000 // Symbolic link
+ c_ISBLK = 060000 // Block special file
+ c_ISCHR = 020000 // Character special file
+ c_ISSOCK = 0140000 // Socket
+)
+
+// FileInfoHeader creates a partially-populated Header from fi.
+// If fi describes a symlink, FileInfoHeader records link as the link target.
+// If fi describes a directory, a slash is appended to the name.
+func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
+ if fi == nil {
+ return nil, errors.New("tar: FileInfo is nil")
+ }
+ fm := fi.Mode()
+ h := &Header{
+ Name: fi.Name(),
+ ModTime: fi.ModTime(),
+ Mode: int64(fm.Perm()), // or'd with c_IS* constants later
+ }
+ switch {
+ case fm.IsRegular():
+ h.Mode |= c_ISREG
+ h.Typeflag = TypeReg
+ h.Size = fi.Size()
+ case fi.IsDir():
+ h.Typeflag = TypeDir
+ h.Mode |= c_ISDIR
+ h.Name += "/"
+ case fm&os.ModeSymlink != 0:
+ h.Typeflag = TypeSymlink
+ h.Mode |= c_ISLNK
+ h.Linkname = link
+ case fm&os.ModeDevice != 0:
+ if fm&os.ModeCharDevice != 0 {
+ h.Mode |= c_ISCHR
+ h.Typeflag = TypeChar
+ } else {
+ h.Mode |= c_ISBLK
+ h.Typeflag = TypeBlock
+ }
+ case fm&os.ModeNamedPipe != 0:
+ h.Typeflag = TypeFifo
+ h.Mode |= c_ISFIFO
+ case fm&os.ModeSocket != 0:
+ h.Mode |= c_ISSOCK
+ default:
+ return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm)
+ }
+ if fm&os.ModeSetuid != 0 {
+ h.Mode |= c_ISUID
+ }
+ if fm&os.ModeSetgid != 0 {
+ h.Mode |= c_ISGID
+ }
+ if fm&os.ModeSticky != 0 {
+ h.Mode |= c_ISVTX
+ }
+ if sysStat != nil {
+ return h, sysStat(fi, h)
+ }
+ return h, nil
+}
+
var zeroBlock = make([]byte, blockSize)
// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.
diff --git a/src/pkg/archive/tar/example_test.go b/src/pkg/archive/tar/example_test.go
new file mode 100644
index 000000000..351eaa0e6
--- /dev/null
+++ b/src/pkg/archive/tar/example_test.go
@@ -0,0 +1,79 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tar_test
+
+import (
+ "archive/tar"
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "os"
+)
+
+func Example() {
+ // Create a buffer to write our archive to.
+ buf := new(bytes.Buffer)
+
+ // Create a new tar archive.
+ tw := tar.NewWriter(buf)
+
+ // Add some files to the archive.
+ var files = []struct {
+ Name, Body string
+ }{
+ {"readme.txt", "This archive contains some text files."},
+ {"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
+ {"todo.txt", "Get animal handling licence."},
+ }
+ for _, file := range files {
+ hdr := &tar.Header{
+ Name: file.Name,
+ Size: int64(len(file.Body)),
+ }
+ if err := tw.WriteHeader(hdr); err != nil {
+ log.Fatalln(err)
+ }
+ if _, err := tw.Write([]byte(file.Body)); err != nil {
+ log.Fatalln(err)
+ }
+ }
+ // Make sure to check the error on Close.
+ if err := tw.Close(); err != nil {
+ log.Fatalln(err)
+ }
+
+ // Open the tar archive for reading.
+ r := bytes.NewReader(buf.Bytes())
+ tr := tar.NewReader(r)
+
+ // Iterate through the files in the archive.
+ for {
+ hdr, err := tr.Next()
+ if err == io.EOF {
+ // end of tar archive
+ break
+ }
+ if err != nil {
+ log.Fatalln(err)
+ }
+ fmt.Printf("Contents of %s:\n", hdr.Name)
+ if _, err := io.Copy(os.Stdout, tr); err != nil {
+ log.Fatalln(err)
+ }
+ fmt.Println()
+ }
+
+ // Output:
+ // Contents of readme.txt:
+ // This archive contains some text files.
+ // Contents of gopher.txt:
+ // Gopher names:
+ // George
+ // Geoffrey
+ // Gonzo
+ // Contents of todo.txt:
+ // Get animal handling licence.
+}
diff --git a/src/pkg/archive/tar/reader.go b/src/pkg/archive/tar/reader.go
index 1b40af812..05f82a40d 100644
--- a/src/pkg/archive/tar/reader.go
+++ b/src/pkg/archive/tar/reader.go
@@ -14,6 +14,7 @@ import (
"io/ioutil"
"os"
"strconv"
+ "strings"
"time"
)
@@ -21,24 +22,12 @@ var (
ErrHeader = errors.New("archive/tar: invalid tar header")
)
+const maxNanoSecondIntSize = 9
+
// A Reader provides sequential access to the contents of a tar archive.
// A tar archive consists of a sequence of files.
// The Next method advances to the next file in the archive (including the first),
// and then it can be treated as an io.Reader to access the file's data.
-//
-// Example:
-// tr := tar.NewReader(r)
-// for {
-// hdr, err := tr.Next()
-// if err == io.EOF {
-// // end of tar archive
-// break
-// }
-// if err != nil {
-// // handle error
-// }
-// io.Copy(data, tr)
-// }
type Reader struct {
r io.Reader
err error
@@ -55,13 +44,183 @@ func (tr *Reader) Next() (*Header, error) {
if tr.err == nil {
tr.skipUnread()
}
- if tr.err == nil {
+ if tr.err != nil {
+ return hdr, tr.err
+ }
+ hdr = tr.readHeader()
+ if hdr == nil {
+ return hdr, tr.err
+ }
+ // Check for PAX/GNU header.
+ switch hdr.Typeflag {
+ case TypeXHeader:
+ // PAX extended header
+ headers, err := parsePAX(tr)
+ if err != nil {
+ return nil, err
+ }
+ // We actually read the whole file,
+ // but this skips alignment padding
+ tr.skipUnread()
hdr = tr.readHeader()
+ mergePAX(hdr, headers)
+ return hdr, nil
+ case TypeGNULongName:
+ // We have a GNU long name header. Its contents are the real file name.
+ realname, err := ioutil.ReadAll(tr)
+ if err != nil {
+ return nil, err
+ }
+ hdr, err := tr.Next()
+ hdr.Name = cString(realname)
+ return hdr, err
+ case TypeGNULongLink:
+ // We have a GNU long link header.
+ realname, err := ioutil.ReadAll(tr)
+ if err != nil {
+ return nil, err
+ }
+ hdr, err := tr.Next()
+ hdr.Linkname = cString(realname)
+ return hdr, err
}
return hdr, tr.err
}
-// Parse bytes as a NUL-terminated C-style string.
+// mergePAX merges well known headers according to PAX standard.
+// In general headers with the same name as those found
+// in the header struct overwrite those found in the header
+// struct with higher precision or longer values. Esp. useful
+// for name and linkname fields.
+func mergePAX(hdr *Header, headers map[string]string) error {
+ for k, v := range headers {
+ switch k {
+ case "path":
+ hdr.Name = v
+ case "linkpath":
+ hdr.Linkname = v
+ case "gname":
+ hdr.Gname = v
+ case "uname":
+ hdr.Uname = v
+ case "uid":
+ uid, err := strconv.ParseInt(v, 10, 0)
+ if err != nil {
+ return err
+ }
+ hdr.Uid = int(uid)
+ case "gid":
+ gid, err := strconv.ParseInt(v, 10, 0)
+ if err != nil {
+ return err
+ }
+ hdr.Gid = int(gid)
+ case "atime":
+ t, err := parsePAXTime(v)
+ if err != nil {
+ return err
+ }
+ hdr.AccessTime = t
+ case "mtime":
+ t, err := parsePAXTime(v)
+ if err != nil {
+ return err
+ }
+ hdr.ModTime = t
+ case "ctime":
+ t, err := parsePAXTime(v)
+ if err != nil {
+ return err
+ }
+ hdr.ChangeTime = t
+ case "size":
+ size, err := strconv.ParseInt(v, 10, 0)
+ if err != nil {
+ return err
+ }
+ hdr.Size = int64(size)
+ }
+
+ }
+ return nil
+}
+
+// parsePAXTime takes a string of the form %d.%d as described in
+// the PAX specification.
+func parsePAXTime(t string) (time.Time, error) {
+ buf := []byte(t)
+ pos := bytes.IndexByte(buf, '.')
+ var seconds, nanoseconds int64
+ var err error
+ if pos == -1 {
+ seconds, err = strconv.ParseInt(t, 10, 0)
+ if err != nil {
+ return time.Time{}, err
+ }
+ } else {
+ seconds, err = strconv.ParseInt(string(buf[:pos]), 10, 0)
+ if err != nil {
+ return time.Time{}, err
+ }
+ nano_buf := string(buf[pos+1:])
+ // Pad as needed before converting to a decimal.
+ // For example .030 -> .030000000 -> 30000000 nanoseconds
+ if len(nano_buf) < maxNanoSecondIntSize {
+ // Right pad
+ nano_buf += strings.Repeat("0", maxNanoSecondIntSize-len(nano_buf))
+ } else if len(nano_buf) > maxNanoSecondIntSize {
+ // Right truncate
+ nano_buf = nano_buf[:maxNanoSecondIntSize]
+ }
+ nanoseconds, err = strconv.ParseInt(string(nano_buf), 10, 0)
+ if err != nil {
+ return time.Time{}, err
+ }
+ }
+ ts := time.Unix(seconds, nanoseconds)
+ return ts, nil
+}
+
+// parsePAX parses PAX headers.
+// If an extended header (type 'x') is invalid, ErrHeader is returned
+func parsePAX(r io.Reader) (map[string]string, error) {
+ buf, err := ioutil.ReadAll(r)
+ if err != nil {
+ return nil, err
+ }
+ headers := make(map[string]string)
+ // Each record is constructed as
+ // "%d %s=%s\n", length, keyword, value
+ for len(buf) > 0 {
+ // or the header was empty to start with.
+ var sp int
+ // The size field ends at the first space.
+ sp = bytes.IndexByte(buf, ' ')
+ if sp == -1 {
+ return nil, ErrHeader
+ }
+ // Parse the first token as a decimal integer.
+ n, err := strconv.ParseInt(string(buf[:sp]), 10, 0)
+ if err != nil {
+ return nil, ErrHeader
+ }
+ // Extract everything between the decimal and the n -1 on the
+ // beginning to to eat the ' ', -1 on the end to skip the newline.
+ var record []byte
+ record, buf = buf[sp+1:n-1], buf[n:]
+ // The first equals is guaranteed to mark the end of the key.
+ // Everything else is value.
+ eq := bytes.IndexByte(record, '=')
+ if eq == -1 {
+ return nil, ErrHeader
+ }
+ key, value := record[:eq], record[eq+1:]
+ headers[string(key)] = string(value)
+ }
+ return headers, nil
+}
+
+// cString parses bytes as a NUL-terminated C-style string.
// If a NUL byte is not found then the whole slice is returned as a string.
func cString(b []byte) string {
n := 0
@@ -72,6 +231,18 @@ func cString(b []byte) string {
}
func (tr *Reader) octal(b []byte) int64 {
+ // Check for binary format first.
+ if len(b) > 0 && b[0]&0x80 != 0 {
+ var x int64
+ for i, c := range b {
+ if i == 0 {
+ c &= 0x7f // ignore signal bit in first byte
+ }
+ x = x<<8 | int64(c)
+ }
+ return x
+ }
+
// Removing leading spaces.
for len(b) > 0 && b[0] == ' ' {
b = b[1:]
@@ -87,7 +258,7 @@ func (tr *Reader) octal(b []byte) int64 {
return int64(x)
}
-// Skip any unread bytes in the existing file entry, as well as any alignment padding.
+// skipUnread skips any unread bytes in the existing file entry, as well as any alignment padding.
func (tr *Reader) skipUnread() {
nr := tr.nb + tr.pad // number of bytes to skip
tr.nb, tr.pad = 0, 0
diff --git a/src/pkg/archive/tar/reader_test.go b/src/pkg/archive/tar/reader_test.go
index 0a8646c39..9a1968237 100644
--- a/src/pkg/archive/tar/reader_test.go
+++ b/src/pkg/archive/tar/reader_test.go
@@ -10,6 +10,8 @@ import (
"fmt"
"io"
"os"
+ "reflect"
+ "strings"
"testing"
"time"
)
@@ -108,6 +110,38 @@ var untarTests = []*untarTest{
},
},
},
+ {
+ file: "testdata/pax.tar",
+ headers: []*Header{
+ {
+ Name: "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
+ Mode: 0664,
+ Uid: 1000,
+ Gid: 1000,
+ Uname: "shane",
+ Gname: "shane",
+ Size: 7,
+ ModTime: time.Unix(1350244992, 23960108),
+ ChangeTime: time.Unix(1350244992, 23960108),
+ AccessTime: time.Unix(1350244992, 23960108),
+ Typeflag: TypeReg,
+ },
+ {
+ Name: "a/b",
+ Mode: 0777,
+ Uid: 1000,
+ Gid: 1000,
+ Uname: "shane",
+ Gname: "shane",
+ Size: 0,
+ ModTime: time.Unix(1350266320, 910238425),
+ ChangeTime: time.Unix(1350266320, 910238425),
+ AccessTime: time.Unix(1350266320, 910238425),
+ Typeflag: TypeSymlink,
+ Linkname: "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
+ },
+ },
+ },
}
func TestReader(t *testing.T) {
@@ -133,7 +167,7 @@ testLoop:
}
hdr, err := tr.Next()
if err == io.EOF {
- break
+ continue testLoop
}
if hdr != nil || err != nil {
t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err)
@@ -260,3 +294,73 @@ func TestNonSeekable(t *testing.T) {
t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(test.headers), nread)
}
}
+
+func TestParsePAXHeader(t *testing.T) {
+ paxTests := [][3]string{
+ {"a", "a=name", "10 a=name\n"}, // Test case involving multiple acceptable lengths
+ {"a", "a=name", "9 a=name\n"}, // Test case involving multiple acceptable length
+ {"mtime", "mtime=1350244992.023960108", "30 mtime=1350244992.023960108\n"}}
+ for _, test := range paxTests {
+ key, expected, raw := test[0], test[1], test[2]
+ reader := bytes.NewBuffer([]byte(raw))
+ headers, err := parsePAX(reader)
+ if err != nil {
+ t.Errorf("Couldn't parse correctly formatted headers: %v", err)
+ continue
+ }
+ if strings.EqualFold(headers[key], expected) {
+ t.Errorf("mtime header incorrectly parsed: got %s, wanted %s", headers[key], expected)
+ continue
+ }
+ trailer := make([]byte, 100)
+ n, err := reader.Read(trailer)
+ if err != io.EOF || n != 0 {
+ t.Error("Buffer wasn't consumed")
+ }
+ }
+ badHeader := bytes.NewBuffer([]byte("3 somelongkey="))
+ if _, err := parsePAX(badHeader); err != ErrHeader {
+ t.Fatal("Unexpected success when parsing bad header")
+ }
+}
+
+func TestParsePAXTime(t *testing.T) {
+ // Some valid PAX time values
+ timestamps := map[string]time.Time{
+ "1350244992.023960108": time.Unix(1350244992, 23960108), // The commoon case
+ "1350244992.02396010": time.Unix(1350244992, 23960100), // Lower precision value
+ "1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value
+ "1350244992": time.Unix(1350244992, 0), // Low precision value
+ }
+ for input, expected := range timestamps {
+ ts, err := parsePAXTime(input)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !ts.Equal(expected) {
+ t.Fatalf("Time parsing failure %s %s", ts, expected)
+ }
+ }
+}
+
+func TestMergePAX(t *testing.T) {
+ hdr := new(Header)
+ // Test a string, integer, and time based value.
+ headers := map[string]string{
+ "path": "a/b/c",
+ "uid": "1000",
+ "mtime": "1350244992.023960108",
+ }
+ err := mergePAX(hdr, headers)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := &Header{
+ Name: "a/b/c",
+ Uid: 1000,
+ ModTime: time.Unix(1350244992, 23960108),
+ }
+ if !reflect.DeepEqual(hdr, want) {
+ t.Errorf("incorrect merge: got %+v, want %+v", hdr, want)
+ }
+}
diff --git a/src/pkg/archive/tar/stat_atim.go b/src/pkg/archive/tar/stat_atim.go
new file mode 100644
index 000000000..6029b0871
--- /dev/null
+++ b/src/pkg/archive/tar/stat_atim.go
@@ -0,0 +1,20 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux openbsd
+
+package tar
+
+import (
+ "syscall"
+ "time"
+)
+
+func statAtime(st *syscall.Stat_t) time.Time {
+ return time.Unix(st.Atim.Unix())
+}
+
+func statCtime(st *syscall.Stat_t) time.Time {
+ return time.Unix(st.Ctim.Unix())
+}
diff --git a/src/pkg/archive/tar/stat_atimespec.go b/src/pkg/archive/tar/stat_atimespec.go
new file mode 100644
index 000000000..6f17dbe30
--- /dev/null
+++ b/src/pkg/archive/tar/stat_atimespec.go
@@ -0,0 +1,20 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd netbsd
+
+package tar
+
+import (
+ "syscall"
+ "time"
+)
+
+func statAtime(st *syscall.Stat_t) time.Time {
+ return time.Unix(st.Atimespec.Unix())
+}
+
+func statCtime(st *syscall.Stat_t) time.Time {
+ return time.Unix(st.Ctimespec.Unix())
+}
diff --git a/src/pkg/archive/tar/stat_unix.go b/src/pkg/archive/tar/stat_unix.go
new file mode 100644
index 000000000..92bc92424
--- /dev/null
+++ b/src/pkg/archive/tar/stat_unix.go
@@ -0,0 +1,32 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux darwin freebsd openbsd netbsd
+
+package tar
+
+import (
+ "os"
+ "syscall"
+)
+
+func init() {
+ sysStat = statUnix
+}
+
+func statUnix(fi os.FileInfo, h *Header) error {
+ sys, ok := fi.Sys().(*syscall.Stat_t)
+ if !ok {
+ return nil
+ }
+ h.Uid = int(sys.Uid)
+ h.Gid = int(sys.Gid)
+ // TODO(bradfitz): populate username & group. os/user
+ // doesn't cache LookupId lookups, and lacks group
+ // lookup functions.
+ h.AccessTime = statAtime(sys)
+ h.ChangeTime = statCtime(sys)
+ // TODO(bradfitz): major/minor device numbers?
+ return nil
+}
diff --git a/src/pkg/archive/tar/tar_test.go b/src/pkg/archive/tar/tar_test.go
new file mode 100644
index 000000000..dd6310313
--- /dev/null
+++ b/src/pkg/archive/tar/tar_test.go
@@ -0,0 +1,271 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tar
+
+import (
+ "bytes"
+ "io/ioutil"
+ "os"
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestFileInfoHeader(t *testing.T) {
+ fi, err := os.Stat("testdata/small.txt")
+ if err != nil {
+ t.Fatal(err)
+ }
+ h, err := FileInfoHeader(fi, "")
+ if err != nil {
+ t.Fatalf("FileInfoHeader: %v", err)
+ }
+ if g, e := h.Name, "small.txt"; g != e {
+ t.Errorf("Name = %q; want %q", g, e)
+ }
+ if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISREG; g != e {
+ t.Errorf("Mode = %#o; want %#o", g, e)
+ }
+ if g, e := h.Size, int64(5); g != e {
+ t.Errorf("Size = %v; want %v", g, e)
+ }
+ if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
+ t.Errorf("ModTime = %v; want %v", g, e)
+ }
+}
+
+func TestFileInfoHeaderDir(t *testing.T) {
+ fi, err := os.Stat("testdata")
+ if err != nil {
+ t.Fatal(err)
+ }
+ h, err := FileInfoHeader(fi, "")
+ if err != nil {
+ t.Fatalf("FileInfoHeader: %v", err)
+ }
+ if g, e := h.Name, "testdata/"; g != e {
+ t.Errorf("Name = %q; want %q", g, e)
+ }
+ // Ignoring c_ISGID for golang.org/issue/4867
+ if g, e := h.Mode&^c_ISGID, int64(fi.Mode().Perm())|c_ISDIR; g != e {
+ t.Errorf("Mode = %#o; want %#o", g, e)
+ }
+ if g, e := h.Size, int64(0); g != e {
+ t.Errorf("Size = %v; want %v", g, e)
+ }
+ if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
+ t.Errorf("ModTime = %v; want %v", g, e)
+ }
+}
+
+func TestFileInfoHeaderSymlink(t *testing.T) {
+ h, err := FileInfoHeader(symlink{}, "some-target")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if g, e := h.Name, "some-symlink"; g != e {
+ t.Errorf("Name = %q; want %q", g, e)
+ }
+ if g, e := h.Linkname, "some-target"; g != e {
+ t.Errorf("Linkname = %q; want %q", g, e)
+ }
+}
+
+type symlink struct{}
+
+func (symlink) Name() string { return "some-symlink" }
+func (symlink) Size() int64 { return 0 }
+func (symlink) Mode() os.FileMode { return os.ModeSymlink }
+func (symlink) ModTime() time.Time { return time.Time{} }
+func (symlink) IsDir() bool { return false }
+func (symlink) Sys() interface{} { return nil }
+
+func TestRoundTrip(t *testing.T) {
+ data := []byte("some file contents")
+
+ var b bytes.Buffer
+ tw := NewWriter(&b)
+ hdr := &Header{
+ Name: "file.txt",
+ Uid: 1 << 21, // too big for 8 octal digits
+ Size: int64(len(data)),
+ ModTime: time.Now(),
+ }
+ // tar only supports second precision.
+ hdr.ModTime = hdr.ModTime.Add(-time.Duration(hdr.ModTime.Nanosecond()) * time.Nanosecond)
+ if err := tw.WriteHeader(hdr); err != nil {
+ t.Fatalf("tw.WriteHeader: %v", err)
+ }
+ if _, err := tw.Write(data); err != nil {
+ t.Fatalf("tw.Write: %v", err)
+ }
+ if err := tw.Close(); err != nil {
+ t.Fatalf("tw.Close: %v", err)
+ }
+
+ // Read it back.
+ tr := NewReader(&b)
+ rHdr, err := tr.Next()
+ if err != nil {
+ t.Fatalf("tr.Next: %v", err)
+ }
+ if !reflect.DeepEqual(rHdr, hdr) {
+ t.Errorf("Header mismatch.\n got %+v\nwant %+v", rHdr, hdr)
+ }
+ rData, err := ioutil.ReadAll(tr)
+ if err != nil {
+ t.Fatalf("Read: %v", err)
+ }
+ if !bytes.Equal(rData, data) {
+ t.Errorf("Data mismatch.\n got %q\nwant %q", rData, data)
+ }
+}
+
+type headerRoundTripTest struct {
+ h *Header
+ fm os.FileMode
+}
+
+func TestHeaderRoundTrip(t *testing.T) {
+ golden := []headerRoundTripTest{
+ // regular file.
+ {
+ h: &Header{
+ Name: "test.txt",
+ Mode: 0644 | c_ISREG,
+ Size: 12,
+ ModTime: time.Unix(1360600916, 0),
+ Typeflag: TypeReg,
+ },
+ fm: 0644,
+ },
+ // hard link.
+ {
+ h: &Header{
+ Name: "hard.txt",
+ Mode: 0644 | c_ISLNK,
+ Size: 0,
+ ModTime: time.Unix(1360600916, 0),
+ Typeflag: TypeLink,
+ },
+ fm: 0644 | os.ModeSymlink,
+ },
+ // symbolic link.
+ {
+ h: &Header{
+ Name: "link.txt",
+ Mode: 0777 | c_ISLNK,
+ Size: 0,
+ ModTime: time.Unix(1360600852, 0),
+ Typeflag: TypeSymlink,
+ },
+ fm: 0777 | os.ModeSymlink,
+ },
+ // character device node.
+ {
+ h: &Header{
+ Name: "dev/null",
+ Mode: 0666 | c_ISCHR,
+ Size: 0,
+ ModTime: time.Unix(1360578951, 0),
+ Typeflag: TypeChar,
+ },
+ fm: 0666 | os.ModeDevice | os.ModeCharDevice,
+ },
+ // block device node.
+ {
+ h: &Header{
+ Name: "dev/sda",
+ Mode: 0660 | c_ISBLK,
+ Size: 0,
+ ModTime: time.Unix(1360578954, 0),
+ Typeflag: TypeBlock,
+ },
+ fm: 0660 | os.ModeDevice,
+ },
+ // directory.
+ {
+ h: &Header{
+ Name: "dir/",
+ Mode: 0755 | c_ISDIR,
+ Size: 0,
+ ModTime: time.Unix(1360601116, 0),
+ Typeflag: TypeDir,
+ },
+ fm: 0755 | os.ModeDir,
+ },
+ // fifo node.
+ {
+ h: &Header{
+ Name: "dev/initctl",
+ Mode: 0600 | c_ISFIFO,
+ Size: 0,
+ ModTime: time.Unix(1360578949, 0),
+ Typeflag: TypeFifo,
+ },
+ fm: 0600 | os.ModeNamedPipe,
+ },
+ // setuid.
+ {
+ h: &Header{
+ Name: "bin/su",
+ Mode: 0755 | c_ISREG | c_ISUID,
+ Size: 23232,
+ ModTime: time.Unix(1355405093, 0),
+ Typeflag: TypeReg,
+ },
+ fm: 0755 | os.ModeSetuid,
+ },
+ // setguid.
+ {
+ h: &Header{
+ Name: "group.txt",
+ Mode: 0750 | c_ISREG | c_ISGID,
+ Size: 0,
+ ModTime: time.Unix(1360602346, 0),
+ Typeflag: TypeReg,
+ },
+ fm: 0750 | os.ModeSetgid,
+ },
+ // sticky.
+ {
+ h: &Header{
+ Name: "sticky.txt",
+ Mode: 0600 | c_ISREG | c_ISVTX,
+ Size: 7,
+ ModTime: time.Unix(1360602540, 0),
+ Typeflag: TypeReg,
+ },
+ fm: 0600 | os.ModeSticky,
+ },
+ }
+
+ for i, g := range golden {
+ fi := g.h.FileInfo()
+ h2, err := FileInfoHeader(fi, "")
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ if got, want := h2.Name, g.h.Name; got != want {
+ t.Errorf("i=%d: Name: got %v, want %v", i, got, want)
+ }
+ if got, want := h2.Size, g.h.Size; got != want {
+ t.Errorf("i=%d: Size: got %v, want %v", i, got, want)
+ }
+ if got, want := h2.Mode, g.h.Mode; got != want {
+ t.Errorf("i=%d: Mode: got %o, want %o", i, got, want)
+ }
+ if got, want := fi.Mode(), g.fm; got != want {
+ t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want)
+ }
+ if got, want := h2.ModTime, g.h.ModTime; got != want {
+ t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want)
+ }
+ if sysh, ok := fi.Sys().(*Header); !ok || sysh != g.h {
+ t.Errorf("i=%d: Sys didn't return original *Header", i)
+ }
+ }
+}
diff --git a/src/pkg/archive/tar/testdata/pax.tar b/src/pkg/archive/tar/testdata/pax.tar
new file mode 100644
index 000000000..9bc24b658
--- /dev/null
+++ b/src/pkg/archive/tar/testdata/pax.tar
Binary files differ
diff --git a/src/pkg/archive/tar/testdata/ustar.tar b/src/pkg/archive/tar/testdata/ustar.tar
new file mode 100644
index 000000000..29679d9a3
--- /dev/null
+++ b/src/pkg/archive/tar/testdata/ustar.tar
Binary files differ
diff --git a/src/pkg/archive/tar/writer.go b/src/pkg/archive/tar/writer.go
index b2b7a58a1..d92dd06ea 100644
--- a/src/pkg/archive/tar/writer.go
+++ b/src/pkg/archive/tar/writer.go
@@ -8,33 +8,28 @@ package tar
// - catch more errors (no first header, etc.)
import (
+ "bytes"
"errors"
"fmt"
"io"
+ "os"
+ "path"
"strconv"
+ "strings"
+ "time"
)
var (
ErrWriteTooLong = errors.New("archive/tar: write too long")
ErrFieldTooLong = errors.New("archive/tar: header field too long")
ErrWriteAfterClose = errors.New("archive/tar: write after close")
+ errNameTooLong = errors.New("archive/tar: name too long")
)
// A Writer provides sequential writing of a tar archive in POSIX.1 format.
// A tar archive consists of a sequence of files.
// Call WriteHeader to begin a new file, and then call Write to supply that file's data,
// writing at most hdr.Size bytes in total.
-//
-// Example:
-// tw := tar.NewWriter(w)
-// hdr := new(Header)
-// hdr.Size = length of data in bytes
-// // populate other hdr fields as desired
-// if err := tw.WriteHeader(hdr); err != nil {
-// // handle error
-// }
-// io.Copy(tw, data)
-// tw.Close()
type Writer struct {
w io.Writer
err error
@@ -110,6 +105,12 @@ func (tw *Writer) numeric(b []byte, x int64) {
b[0] |= 0x80 // highest bit indicates binary format
}
+var (
+ minTime = time.Unix(0, 0)
+ // There is room for 11 octal digits (33 bits) of mtime.
+ maxTime = minTime.Add((1<<33 - 1) * time.Second)
+)
+
// WriteHeader writes hdr and prepares to accept the file's contents.
// WriteHeader calls Flush if it is not the first header.
// Calling after a Close will return ErrWriteAfterClose.
@@ -123,34 +124,60 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
if tw.err != nil {
return tw.err
}
-
+ // Decide whether or not to use PAX extensions
+ // TODO(shanemhansen): we might want to use PAX headers for
+ // subsecond time resolution, but for now let's just capture
+ // the long name/long symlink use case.
+ suffix := hdr.Name
+ prefix := ""
+ if len(hdr.Name) > fileNameSize || len(hdr.Linkname) > fileNameSize {
+ var err error
+ prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
+ // Either we were unable to pack the long name into ustar format
+ // or the link name is too long; use PAX headers.
+ if err == errNameTooLong || len(hdr.Linkname) > fileNameSize {
+ if err := tw.writePAXHeader(hdr); err != nil {
+ return err
+ }
+ } else if err != nil {
+ return err
+ }
+ }
tw.nb = int64(hdr.Size)
tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two
header := make([]byte, blockSize)
s := slicer(header)
+ tw.cString(s.next(fileNameSize), suffix)
- // TODO(dsymonds): handle names longer than 100 chars
- copy(s.next(100), []byte(hdr.Name))
-
- tw.octal(s.next(8), hdr.Mode) // 100:108
- tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116
- tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124
- tw.numeric(s.next(12), hdr.Size) // 124:136
- tw.numeric(s.next(12), hdr.ModTime.Unix()) // 136:148
- s.next(8) // chksum (148:156)
- s.next(1)[0] = hdr.Typeflag // 156:157
- tw.cString(s.next(100), hdr.Linkname) // linkname (157:257)
- copy(s.next(8), []byte("ustar\x0000")) // 257:265
- tw.cString(s.next(32), hdr.Uname) // 265:297
- tw.cString(s.next(32), hdr.Gname) // 297:329
- tw.numeric(s.next(8), hdr.Devmajor) // 329:337
- tw.numeric(s.next(8), hdr.Devminor) // 337:345
+ // Handle out of range ModTime carefully.
+ var modTime int64
+ if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) {
+ modTime = hdr.ModTime.Unix()
+ }
+ tw.octal(s.next(8), hdr.Mode) // 100:108
+ tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116
+ tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124
+ tw.numeric(s.next(12), hdr.Size) // 124:136
+ tw.numeric(s.next(12), modTime) // 136:148
+ s.next(8) // chksum (148:156)
+ s.next(1)[0] = hdr.Typeflag // 156:157
+ tw.cString(s.next(100), hdr.Linkname) // linkname (157:257)
+ copy(s.next(8), []byte("ustar\x0000")) // 257:265
+ tw.cString(s.next(32), hdr.Uname) // 265:297
+ tw.cString(s.next(32), hdr.Gname) // 297:329
+ tw.numeric(s.next(8), hdr.Devmajor) // 329:337
+ tw.numeric(s.next(8), hdr.Devminor) // 337:345
+ tw.cString(s.next(155), prefix) // 345:500
// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
if tw.usedBinary {
copy(header[257:265], []byte("ustar \x00"))
}
+ // Use the ustar magic if we used ustar long names.
+ if len(prefix) > 0 {
+ copy(header[257:265], []byte("ustar\000"))
+ }
// The chksum field is terminated by a NUL and a space.
// This is different from the other octal fields.
@@ -168,6 +195,78 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
return tw.err
}
+// writeUSTARLongName splits a USTAR long name hdr.Name.
+// name must be < 256 characters. errNameTooLong is returned
+// if hdr.Name can't be split. The splitting heuristic
+// is compatible with gnu tar.
+func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err error) {
+ length := len(name)
+ if length > fileNamePrefixSize+1 {
+ length = fileNamePrefixSize + 1
+ } else if name[length-1] == '/' {
+ length--
+ }
+ i := strings.LastIndex(name[:length], "/")
+ nlen := length - i - 1
+ if i <= 0 || nlen > fileNameSize || nlen == 0 {
+ err = errNameTooLong
+ return
+ }
+ prefix, suffix = name[:i], name[i+1:]
+ return
+}
+
+// writePaxHeader writes an extended pax header to the
+// archive.
+func (tw *Writer) writePAXHeader(hdr *Header) error {
+ // Prepare extended header
+ ext := new(Header)
+ ext.Typeflag = TypeXHeader
+ // Setting ModTime is required for reader parsing to
+ // succeed, and seems harmless enough.
+ ext.ModTime = hdr.ModTime
+ // The spec asks that we namespace our pseudo files
+ // with the current pid.
+ pid := os.Getpid()
+ dir, file := path.Split(hdr.Name)
+ ext.Name = path.Join(dir,
+ fmt.Sprintf("PaxHeaders.%d", pid), file)[0:100]
+ // Construct the body
+ var buf bytes.Buffer
+ if len(hdr.Name) > fileNameSize {
+ fmt.Fprint(&buf, paxHeader("path="+hdr.Name))
+ }
+ if len(hdr.Linkname) > fileNameSize {
+ fmt.Fprint(&buf, paxHeader("linkpath="+hdr.Linkname))
+ }
+ ext.Size = int64(len(buf.Bytes()))
+ if err := tw.WriteHeader(ext); err != nil {
+ return err
+ }
+ if _, err := tw.Write(buf.Bytes()); err != nil {
+ return err
+ }
+ if err := tw.Flush(); err != nil {
+ return err
+ }
+ return nil
+}
+
+// paxHeader formats a single pax record, prefixing it with the appropriate length
+func paxHeader(msg string) string {
+ const padding = 2 // Extra padding for space and newline
+ size := len(msg) + padding
+ size += len(strconv.Itoa(size))
+ record := fmt.Sprintf("%d %s\n", size, msg)
+ if len(record) != size {
+ // Final adjustment if adding size increased
+ // the number of digits in size
+ size = len(record)
+ record = fmt.Sprintf("%d %s\n", size, msg)
+ }
+ return record
+}
+
// Write writes to the current entry in the tar archive.
// Write returns the error ErrWriteTooLong if more than
// hdr.Size bytes are written after WriteHeader.
diff --git a/src/pkg/archive/tar/writer_test.go b/src/pkg/archive/tar/writer_test.go
index a214e57b9..4cf7c72af 100644
--- a/src/pkg/archive/tar/writer_test.go
+++ b/src/pkg/archive/tar/writer_test.go
@@ -9,6 +9,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "os"
"strings"
"testing"
"testing/iotest"
@@ -101,6 +102,27 @@ var writerTests = []*writerTest{
},
},
},
+ // This file was produced using gnu tar 1.17
+ // gnutar -b 4 --format=ustar (longname/)*15 + file.txt
+ {
+ file: "testdata/ustar.tar",
+ entries: []*writerTestEntry{
+ {
+ header: &Header{
+ Name: strings.Repeat("longname/", 15) + "file.txt",
+ Mode: 0644,
+ Uid: 0765,
+ Gid: 024,
+ Size: 06,
+ ModTime: time.Unix(1360135598, 0),
+ Typeflag: '0',
+ Uname: "shane",
+ Gname: "staff",
+ },
+ contents: "hello\n",
+ },
+ },
+ },
}
// Render byte array in a two-character hexadecimal string, spaced for easy visual inspection.
@@ -180,3 +202,61 @@ testLoop:
}
}
}
+
+func TestPax(t *testing.T) {
+ // Create an archive with a large name
+ fileinfo, err := os.Stat("testdata/small.txt")
+ if err != nil {
+ t.Fatal(err)
+ }
+ hdr, err := FileInfoHeader(fileinfo, "")
+ if err != nil {
+ t.Fatalf("os.Stat: %v", err)
+ }
+ // Force a PAX long name to be written
+ longName := strings.Repeat("ab", 100)
+ contents := strings.Repeat(" ", int(hdr.Size))
+ hdr.Name = longName
+ var buf bytes.Buffer
+ writer := NewWriter(&buf)
+ if err := writer.WriteHeader(hdr); err != nil {
+ t.Fatal(err)
+ }
+ if _, err = writer.Write([]byte(contents)); err != nil {
+ t.Fatal(err)
+ }
+ if err := writer.Close(); err != nil {
+ t.Fatal(err)
+ }
+ // Simple test to make sure PAX extensions are in effect
+ if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
+ t.Fatal("Expected at least one PAX header to be written.")
+ }
+ // Test that we can get a long name back out of the archive.
+ reader := NewReader(&buf)
+ hdr, err = reader.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if hdr.Name != longName {
+ t.Fatal("Couldn't recover long file name")
+ }
+}
+
+func TestPAXHeader(t *testing.T) {
+ medName := strings.Repeat("CD", 50)
+ longName := strings.Repeat("AB", 100)
+ paxTests := [][2]string{
+ {"name=/etc/hosts", "19 name=/etc/hosts\n"},
+ {"a=b", "6 a=b\n"}, // Single digit length
+ {"a=names", "11 a=names\n"}, // Test case involving carries
+ {"name=" + longName, fmt.Sprintf("210 name=%s\n", longName)},
+ {"name=" + medName, fmt.Sprintf("110 name=%s\n", medName)}}
+
+ for _, test := range paxTests {
+ key, expected := test[0], test[1]
+ if result := paxHeader(key); result != expected {
+ t.Fatalf("paxHeader: got %s, expected %s", result, expected)
+ }
+ }
+}
diff --git a/src/pkg/archive/zip/reader.go b/src/pkg/archive/zip/reader.go
index ddd507538..c10f29a83 100644
--- a/src/pkg/archive/zip/reader.go
+++ b/src/pkg/archive/zip/reader.go
@@ -103,7 +103,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
}
z.File = append(z.File, f)
}
- if uint16(len(z.File)) != end.directoryRecords {
+ if uint16(len(z.File)) != uint16(end.directoryRecords) { // only compare 16 bits here
// Return the readDirectoryHeader error if we read
// the wrong number of directory entries.
return err
@@ -123,7 +123,7 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
if err != nil {
return
}
- size := int64(f.CompressedSize)
+ size := int64(f.CompressedSize64)
r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size)
switch f.Method {
case Store: // (no compression)
@@ -220,6 +220,8 @@ func readDirectoryHeader(f *File, r io.Reader) error {
f.CRC32 = b.uint32()
f.CompressedSize = b.uint32()
f.UncompressedSize = b.uint32()
+ f.CompressedSize64 = uint64(f.CompressedSize)
+ f.UncompressedSize64 = uint64(f.UncompressedSize)
filenameLen := int(b.uint16())
extraLen := int(b.uint16())
commentLen := int(b.uint16())
@@ -233,6 +235,35 @@ func readDirectoryHeader(f *File, r io.Reader) error {
f.Name = string(d[:filenameLen])
f.Extra = d[filenameLen : filenameLen+extraLen]
f.Comment = string(d[filenameLen+extraLen:])
+
+ if len(f.Extra) > 0 {
+ b := readBuf(f.Extra)
+ for len(b) >= 4 { // need at least tag and size
+ tag := b.uint16()
+ size := b.uint16()
+ if int(size) > len(b) {
+ return ErrFormat
+ }
+ if tag == zip64ExtraId {
+ // update directory values from the zip64 extra block
+ eb := readBuf(b)
+ if len(eb) >= 8 {
+ f.UncompressedSize64 = eb.uint64()
+ }
+ if len(eb) >= 8 {
+ f.CompressedSize64 = eb.uint64()
+ }
+ if len(eb) >= 8 {
+ f.headerOffset = int64(eb.uint64())
+ }
+ }
+ b = b[size:]
+ }
+ // Should have consumed the whole header.
+ if len(b) != 0 {
+ return ErrFormat
+ }
+ }
return nil
}
@@ -263,15 +294,23 @@ func readDataDescriptor(r io.Reader, f *File) error {
return err
}
b := readBuf(buf[:12])
- f.CRC32 = b.uint32()
- f.CompressedSize = b.uint32()
- f.UncompressedSize = b.uint32()
+ if b.uint32() != f.CRC32 {
+ return ErrChecksum
+ }
+
+ // The two sizes that follow here can be either 32 bits or 64 bits
+ // but the spec is not very clear on this and different
+ // interpretations has been made causing incompatibilities. We
+ // already have the sizes from the central directory so we can
+ // just ignore these.
+
return nil
}
func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) {
// look for directoryEndSignature in the last 1k, then in the last 65k
var buf []byte
+ var directoryEndOffset int64
for i, bLen := range []int64{1024, 65 * 1024} {
if bLen > size {
bLen = size
@@ -282,6 +321,7 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
}
if p := findSignatureInBlock(buf); p >= 0 {
buf = buf[p:]
+ directoryEndOffset = size - bLen + int64(p)
break
}
if i == 1 || bLen == size {
@@ -292,12 +332,12 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
// read header into struct
b := readBuf(buf[4:]) // skip signature
d := &directoryEnd{
- diskNbr: b.uint16(),
- dirDiskNbr: b.uint16(),
- dirRecordsThisDisk: b.uint16(),
- directoryRecords: b.uint16(),
- directorySize: b.uint32(),
- directoryOffset: b.uint32(),
+ diskNbr: uint32(b.uint16()),
+ dirDiskNbr: uint32(b.uint16()),
+ dirRecordsThisDisk: uint64(b.uint16()),
+ directoryRecords: uint64(b.uint16()),
+ directorySize: uint64(b.uint32()),
+ directoryOffset: uint64(b.uint32()),
commentLen: b.uint16(),
}
l := int(d.commentLen)
@@ -305,9 +345,62 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
return nil, errors.New("zip: invalid comment length")
}
d.comment = string(b[:l])
+
+ p, err := findDirectory64End(r, directoryEndOffset)
+ if err == nil && p >= 0 {
+ err = readDirectory64End(r, p, d)
+ }
+ if err != nil {
+ return nil, err
+ }
return d, nil
}
+// findDirectory64End tries to read the zip64 locator just before the
+// directory end and returns the offset of the zip64 directory end if
+// found.
+func findDirectory64End(r io.ReaderAt, directoryEndOffset int64) (int64, error) {
+ locOffset := directoryEndOffset - directory64LocLen
+ if locOffset < 0 {
+ return -1, nil // no need to look for a header outside the file
+ }
+ buf := make([]byte, directory64LocLen)
+ if _, err := r.ReadAt(buf, locOffset); err != nil {
+ return -1, err
+ }
+ b := readBuf(buf)
+ if sig := b.uint32(); sig != directory64LocSignature {
+ return -1, nil
+ }
+ b = b[4:] // skip number of the disk with the start of the zip64 end of central directory
+ p := b.uint64() // relative offset of the zip64 end of central directory record
+ return int64(p), nil
+}
+
+// readDirectory64End reads the zip64 directory end and updates the
+// directory end with the zip64 directory end values.
+func readDirectory64End(r io.ReaderAt, offset int64, d *directoryEnd) (err error) {
+ buf := make([]byte, directory64EndLen)
+ if _, err := r.ReadAt(buf, offset); err != nil {
+ return err
+ }
+
+ b := readBuf(buf)
+ if sig := b.uint32(); sig != directory64EndSignature {
+ return ErrFormat
+ }
+
+ b = b[12:] // skip dir size, version and version needed (uint64 + 2x uint16)
+ d.diskNbr = b.uint32() // number of this disk
+ d.dirDiskNbr = b.uint32() // number of the disk with the start of the central directory
+ d.dirRecordsThisDisk = b.uint64() // total number of entries in the central directory on this disk
+ d.directoryRecords = b.uint64() // total number of entries in the central directory
+ d.directorySize = b.uint64() // size of the central directory
+ d.directoryOffset = b.uint64() // offset of start of central directory with respect to the starting disk number
+
+ return nil
+}
+
func findSignatureInBlock(b []byte) int {
for i := len(b) - directoryEndLen; i >= 0; i-- {
// defined from directoryEndSignature in struct.go
@@ -335,3 +428,9 @@ func (b *readBuf) uint32() uint32 {
*b = (*b)[4:]
return v
}
+
+func (b *readBuf) uint64() uint64 {
+ v := binary.LittleEndian.Uint64(*b)
+ *b = (*b)[8:]
+ return v
+}
diff --git a/src/pkg/archive/zip/reader_test.go b/src/pkg/archive/zip/reader_test.go
index 5f1d1b28a..cf9c59c4b 100644
--- a/src/pkg/archive/zip/reader_test.go
+++ b/src/pkg/archive/zip/reader_test.go
@@ -206,6 +206,17 @@ var tests = []ZipTest{
},
},
},
+ {
+ Name: "zip64.zip",
+ File: []ZipTestFile{
+ {
+ Name: "README",
+ Content: []byte("This small file is in ZIP64 format.\n"),
+ Mtime: "08-10-12 14:33:32",
+ Mode: 0644,
+ },
+ },
+ },
}
var crossPlatform = []ZipTestFile{
diff --git a/src/pkg/archive/zip/struct.go b/src/pkg/archive/zip/struct.go
index 55f3dcfb8..ea067f355 100644
--- a/src/pkg/archive/zip/struct.go
+++ b/src/pkg/archive/zip/struct.go
@@ -7,12 +7,19 @@ Package zip provides support for reading and writing ZIP archives.
See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
-This package does not support ZIP64 or disk spanning.
+This package does not support disk spanning.
+
+A note about ZIP64:
+
+To be backwards compatible the FileHeader has both 32 and 64 bit Size
+fields. The 64 bit fields will always contain the correct value and
+for normal archives both fields will be the same. For files requiring
+the ZIP64 format the 32 bit fields will be 0xffffffff and the 64 bit
+fields must be used instead.
*/
package zip
import (
- "errors"
"os"
"time"
)
@@ -27,11 +34,16 @@ const (
fileHeaderSignature = 0x04034b50
directoryHeaderSignature = 0x02014b50
directoryEndSignature = 0x06054b50
+ directory64LocSignature = 0x07064b50
+ directory64EndSignature = 0x06064b50
dataDescriptorSignature = 0x08074b50 // de-facto standard; required by OS X Finder
fileHeaderLen = 30 // + filename + extra
directoryHeaderLen = 46 // + filename + extra + comment
directoryEndLen = 22 // + comment
dataDescriptorLen = 16 // four uint32: descriptor signature, crc32, compressed size, size
+ dataDescriptor64Len = 24 // descriptor with 8 byte sizes
+ directory64LocLen = 20 //
+ directory64EndLen = 56 // + extra
// Constants for the first byte in CreatorVersion
creatorFAT = 0
@@ -39,22 +51,35 @@ const (
creatorNTFS = 11
creatorVFAT = 14
creatorMacOSX = 19
+
+ // version numbers
+ zipVersion20 = 20 // 2.0
+ zipVersion45 = 45 // 4.5 (reads and writes zip64 archives)
+
+ // limits for non zip64 files
+ uint16max = (1 << 16) - 1
+ uint32max = (1 << 32) - 1
+
+ // extra header id's
+ zip64ExtraId = 0x0001 // zip64 Extended Information Extra Field
)
type FileHeader struct {
- Name string
- CreatorVersion uint16
- ReaderVersion uint16
- Flags uint16
- Method uint16
- ModifiedTime uint16 // MS-DOS time
- ModifiedDate uint16 // MS-DOS date
- CRC32 uint32
- CompressedSize uint32
- UncompressedSize uint32
- Extra []byte
- ExternalAttrs uint32 // Meaning depends on CreatorVersion
- Comment string
+ Name string
+ CreatorVersion uint16
+ ReaderVersion uint16
+ Flags uint16
+ Method uint16
+ ModifiedTime uint16 // MS-DOS time
+ ModifiedDate uint16 // MS-DOS date
+ CRC32 uint32
+ CompressedSize uint32 // deprecated; use CompressedSize64
+ UncompressedSize uint32 // deprecated; use UncompressedSize64
+ CompressedSize64 uint64
+ UncompressedSize64 uint64
+ Extra []byte
+ ExternalAttrs uint32 // Meaning depends on CreatorVersion
+ Comment string
}
// FileInfo returns an os.FileInfo for the FileHeader.
@@ -67,8 +92,13 @@ type headerFileInfo struct {
fh *FileHeader
}
-func (fi headerFileInfo) Name() string { return fi.fh.Name }
-func (fi headerFileInfo) Size() int64 { return int64(fi.fh.UncompressedSize) }
+func (fi headerFileInfo) Name() string { return fi.fh.Name }
+func (fi headerFileInfo) Size() int64 {
+ if fi.fh.UncompressedSize64 > 0 {
+ return int64(fi.fh.UncompressedSize64)
+ }
+ return int64(fi.fh.UncompressedSize)
+}
func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() }
func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() }
@@ -78,25 +108,27 @@ func (fi headerFileInfo) Sys() interface{} { return fi.fh }
// os.FileInfo.
func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
size := fi.Size()
- if size > (1<<32 - 1) {
- return nil, errors.New("zip: file over 4GB")
- }
fh := &FileHeader{
- Name: fi.Name(),
- UncompressedSize: uint32(size),
+ Name: fi.Name(),
+ UncompressedSize64: uint64(size),
}
fh.SetModTime(fi.ModTime())
fh.SetMode(fi.Mode())
+ if fh.UncompressedSize64 > uint32max {
+ fh.UncompressedSize = uint32max
+ } else {
+ fh.UncompressedSize = uint32(fh.UncompressedSize64)
+ }
return fh, nil
}
type directoryEnd struct {
- diskNbr uint16 // unused
- dirDiskNbr uint16 // unused
- dirRecordsThisDisk uint16 // unused
- directoryRecords uint16
- directorySize uint32
- directoryOffset uint32 // relative to file
+ diskNbr uint32 // unused
+ dirDiskNbr uint32 // unused
+ dirRecordsThisDisk uint64 // unused
+ directoryRecords uint64
+ directorySize uint64
+ directoryOffset uint64 // relative to file
commentLen uint16
comment string
}
@@ -190,6 +222,11 @@ func (h *FileHeader) SetMode(mode os.FileMode) {
}
}
+// isZip64 returns true if the file size exceeds the 32 bit limit
+func (fh *FileHeader) isZip64() bool {
+ return fh.CompressedSize64 > uint32max || fh.UncompressedSize64 > uint32max
+}
+
func msdosModeToFileMode(m uint32) (mode os.FileMode) {
if m&msdosDir != 0 {
mode = os.ModeDir | 0777
diff --git a/src/pkg/archive/zip/testdata/zip64.zip b/src/pkg/archive/zip/testdata/zip64.zip
new file mode 100644
index 000000000..a2ee1fa33
--- /dev/null
+++ b/src/pkg/archive/zip/testdata/zip64.zip
Binary files differ
diff --git a/src/pkg/archive/zip/writer.go b/src/pkg/archive/zip/writer.go
index 45eb6bd73..4c696e152 100644
--- a/src/pkg/archive/zip/writer.go
+++ b/src/pkg/archive/zip/writer.go
@@ -27,7 +27,7 @@ type Writer struct {
type header struct {
*FileHeader
- offset uint32
+ offset uint64
}
// NewWriter returns a new Writer writing a zip file to w.
@@ -62,14 +62,36 @@ func (w *Writer) Close() error {
b.uint16(h.ModifiedTime)
b.uint16(h.ModifiedDate)
b.uint32(h.CRC32)
- b.uint32(h.CompressedSize)
- b.uint32(h.UncompressedSize)
+ if h.isZip64() || h.offset > uint32max {
+ // the file needs a zip64 header. store maxint in both
+ // 32 bit size fields (and offset later) to signal that the
+ // zip64 extra header should be used.
+ b.uint32(uint32max) // compressed size
+ b.uint32(uint32max) // uncompressed size
+
+ // append a zip64 extra block to Extra
+ var buf [28]byte // 2x uint16 + 3x uint64
+ eb := writeBuf(buf[:])
+ eb.uint16(zip64ExtraId)
+ eb.uint16(24) // size = 3x uint64
+ eb.uint64(h.UncompressedSize64)
+ eb.uint64(h.CompressedSize64)
+ eb.uint64(h.offset)
+ h.Extra = append(h.Extra, buf[:]...)
+ } else {
+ b.uint32(h.CompressedSize)
+ b.uint32(h.UncompressedSize)
+ }
b.uint16(uint16(len(h.Name)))
b.uint16(uint16(len(h.Extra)))
b.uint16(uint16(len(h.Comment)))
b = b[4:] // skip disk number start and internal file attr (2x uint16)
b.uint32(h.ExternalAttrs)
- b.uint32(h.offset)
+ if h.offset > uint32max {
+ b.uint32(uint32max)
+ } else {
+ b.uint32(uint32(h.offset))
+ }
if _, err := w.cw.Write(buf[:]); err != nil {
return err
}
@@ -85,15 +107,52 @@ func (w *Writer) Close() error {
}
end := w.cw.count
+ records := uint64(len(w.dir))
+ size := uint64(end - start)
+ offset := uint64(start)
+
+ if records > uint16max || size > uint32max || offset > uint32max {
+ var buf [directory64EndLen + directory64LocLen]byte
+ b := writeBuf(buf[:])
+
+ // zip64 end of central directory record
+ b.uint32(directory64EndSignature)
+ b.uint64(directory64EndLen)
+ b.uint16(zipVersion45) // version made by
+ b.uint16(zipVersion45) // version needed to extract
+ b.uint32(0) // number of this disk
+ b.uint32(0) // number of the disk with the start of the central directory
+ b.uint64(records) // total number of entries in the central directory on this disk
+ b.uint64(records) // total number of entries in the central directory
+ b.uint64(size) // size of the central directory
+ b.uint64(offset) // offset of start of central directory with respect to the starting disk number
+
+ // zip64 end of central directory locator
+ b.uint32(directory64LocSignature)
+ b.uint32(0) // number of the disk with the start of the zip64 end of central directory
+ b.uint64(uint64(end)) // relative offset of the zip64 end of central directory record
+ b.uint32(1) // total number of disks
+
+ if _, err := w.cw.Write(buf[:]); err != nil {
+ return err
+ }
+
+ // store max values in the regular end record to signal that
+ // that the zip64 values should be used instead
+ records = uint16max
+ size = uint32max
+ offset = uint32max
+ }
+
// write end record
var buf [directoryEndLen]byte
b := writeBuf(buf[:])
b.uint32(uint32(directoryEndSignature))
- b = b[4:] // skip over disk number and first disk number (2x uint16)
- b.uint16(uint16(len(w.dir))) // number of entries this disk
- b.uint16(uint16(len(w.dir))) // number of entries total
- b.uint32(uint32(end - start)) // size of directory
- b.uint32(uint32(start)) // start of directory
+ b = b[4:] // skip over disk number and first disk number (2x uint16)
+ b.uint16(uint16(records)) // number of entries this disk
+ b.uint16(uint16(records)) // number of entries total
+ b.uint32(uint32(size)) // size of directory
+ b.uint32(uint32(offset)) // start of directory
// skipped size of comment (always zero)
if _, err := w.cw.Write(buf[:]); err != nil {
return err
@@ -115,7 +174,7 @@ func (w *Writer) Create(name string) (io.Writer, error) {
}
// CreateHeader adds a file to the zip file using the provided FileHeader
-// for the file metadata.
+// for the file metadata.
// It returns a Writer to which the file contents should be written.
// The file's contents must be written to the io.Writer before the next
// call to Create, CreateHeader, or Close.
@@ -127,8 +186,9 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
}
fh.Flags |= 0x8 // we will write a data descriptor
- fh.CreatorVersion = fh.CreatorVersion&0xff00 | 0x14
- fh.ReaderVersion = 0x14
+
+ fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
+ fh.ReaderVersion = zipVersion20
fw := &fileWriter{
zipw: w.cw,
@@ -151,7 +211,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
h := &header{
FileHeader: fh,
- offset: uint32(w.cw.count),
+ offset: uint64(w.cw.count),
}
w.dir = append(w.dir, h)
fw.header = h
@@ -173,9 +233,9 @@ func writeHeader(w io.Writer, h *FileHeader) error {
b.uint16(h.Method)
b.uint16(h.ModifiedTime)
b.uint16(h.ModifiedDate)
- b.uint32(h.CRC32)
- b.uint32(h.CompressedSize)
- b.uint32(h.UncompressedSize)
+ b.uint32(0) // since we are writing a data descriptor crc32,
+ b.uint32(0) // compressed size,
+ b.uint32(0) // and uncompressed size should be zero
b.uint16(uint16(len(h.Name)))
b.uint16(uint16(len(h.Extra)))
if _, err := w.Write(buf[:]); err != nil {
@@ -218,17 +278,40 @@ func (w *fileWriter) close() error {
// update FileHeader
fh := w.header.FileHeader
fh.CRC32 = w.crc32.Sum32()
- fh.CompressedSize = uint32(w.compCount.count)
- fh.UncompressedSize = uint32(w.rawCount.count)
+ fh.CompressedSize64 = uint64(w.compCount.count)
+ fh.UncompressedSize64 = uint64(w.rawCount.count)
- // write data descriptor
- var buf [dataDescriptorLen]byte
- b := writeBuf(buf[:])
+ if fh.isZip64() {
+ fh.CompressedSize = uint32max
+ fh.UncompressedSize = uint32max
+ fh.ReaderVersion = zipVersion45 // requires 4.5 - File uses ZIP64 format extensions
+ } else {
+ fh.CompressedSize = uint32(fh.CompressedSize64)
+ fh.UncompressedSize = uint32(fh.UncompressedSize64)
+ }
+
+ // Write data descriptor. This is more complicated than one would
+ // think, see e.g. comments in zipfile.c:putextended() and
+ // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588.
+ // The approach here is to write 8 byte sizes if needed without
+ // adding a zip64 extra in the local header (too late anyway).
+ var buf []byte
+ if fh.isZip64() {
+ buf = make([]byte, dataDescriptor64Len)
+ } else {
+ buf = make([]byte, dataDescriptorLen)
+ }
+ b := writeBuf(buf)
b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
b.uint32(fh.CRC32)
- b.uint32(fh.CompressedSize)
- b.uint32(fh.UncompressedSize)
- _, err := w.zipw.Write(buf[:])
+ if fh.isZip64() {
+ b.uint64(fh.CompressedSize64)
+ b.uint64(fh.UncompressedSize64)
+ } else {
+ b.uint32(fh.CompressedSize)
+ b.uint32(fh.UncompressedSize)
+ }
+ _, err := w.zipw.Write(buf)
return err
}
@@ -262,3 +345,8 @@ func (b *writeBuf) uint32(v uint32) {
binary.LittleEndian.PutUint32(*b, v)
*b = (*b)[4:]
}
+
+func (b *writeBuf) uint64(v uint64) {
+ binary.LittleEndian.PutUint64(*b, v)
+ *b = (*b)[8:]
+}
diff --git a/src/pkg/archive/zip/zip_test.go b/src/pkg/archive/zip/zip_test.go
index d6490c4cb..a8af206a8 100644
--- a/src/pkg/archive/zip/zip_test.go
+++ b/src/pkg/archive/zip/zip_test.go
@@ -9,7 +9,8 @@ package zip
import (
"bytes"
"fmt"
- "reflect"
+ "io"
+ "io/ioutil"
"strings"
"testing"
"time"
@@ -17,8 +18,7 @@ import (
func TestOver65kFiles(t *testing.T) {
if testing.Short() {
- t.Logf("slow test; skipping")
- return
+ t.Skip("slow test; skipping")
}
buf := new(bytes.Buffer)
w := NewWriter(buf)
@@ -58,6 +58,33 @@ func TestModTime(t *testing.T) {
}
}
+func testHeaderRoundTrip(fh *FileHeader, wantUncompressedSize uint32, wantUncompressedSize64 uint64, t *testing.T) {
+ fi := fh.FileInfo()
+ fh2, err := FileInfoHeader(fi)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got, want := fh2.Name, fh.Name; got != want {
+ t.Errorf("Name: got %s, want %s\n", got, want)
+ }
+ if got, want := fh2.UncompressedSize, wantUncompressedSize; got != want {
+ t.Errorf("UncompressedSize: got %d, want %d\n", got, want)
+ }
+ if got, want := fh2.UncompressedSize64, wantUncompressedSize64; got != want {
+ t.Errorf("UncompressedSize64: got %d, want %d\n", got, want)
+ }
+ if got, want := fh2.ModifiedTime, fh.ModifiedTime; got != want {
+ t.Errorf("ModifiedTime: got %d, want %d\n", got, want)
+ }
+ if got, want := fh2.ModifiedDate, fh.ModifiedDate; got != want {
+ t.Errorf("ModifiedDate: got %d, want %d\n", got, want)
+ }
+
+ if sysfh, ok := fi.Sys().(*FileHeader); !ok && sysfh != fh {
+ t.Errorf("Sys didn't return original *FileHeader")
+ }
+}
+
func TestFileHeaderRoundTrip(t *testing.T) {
fh := &FileHeader{
Name: "foo.txt",
@@ -65,17 +92,164 @@ func TestFileHeaderRoundTrip(t *testing.T) {
ModifiedTime: 1234,
ModifiedDate: 5678,
}
- fi := fh.FileInfo()
- fh2, err := FileInfoHeader(fi)
+ testHeaderRoundTrip(fh, fh.UncompressedSize, uint64(fh.UncompressedSize), t)
+}
- // Ignore these fields:
- fh2.CreatorVersion = 0
- fh2.ExternalAttrs = 0
+func TestFileHeaderRoundTrip64(t *testing.T) {
+ fh := &FileHeader{
+ Name: "foo.txt",
+ UncompressedSize64: 9876543210,
+ ModifiedTime: 1234,
+ ModifiedDate: 5678,
+ }
+ testHeaderRoundTrip(fh, uint32max, fh.UncompressedSize64, t)
+}
- if !reflect.DeepEqual(fh, fh2) {
- t.Errorf("mismatch\n input=%#v\noutput=%#v\nerr=%v", fh, fh2, err)
+func TestZip64(t *testing.T) {
+ if testing.Short() {
+ t.Skip("slow test; skipping")
}
- if sysfh, ok := fi.Sys().(*FileHeader); !ok && sysfh != fh {
- t.Errorf("Sys didn't return original *FileHeader")
+ // write 2^32 bytes plus "END\n" to a zip file
+ buf := new(bytes.Buffer)
+ w := NewWriter(buf)
+ f, err := w.Create("huge.txt")
+ if err != nil {
+ t.Fatal(err)
+ }
+ chunk := make([]byte, 1024)
+ for i := range chunk {
+ chunk[i] = '.'
+ }
+ chunk[len(chunk)-1] = '\n'
+ end := []byte("END\n")
+ for i := 0; i < (1<<32)/1024; i++ {
+ _, err := f.Write(chunk)
+ if err != nil {
+ t.Fatal("write chunk:", err)
+ }
+ }
+ _, err = f.Write(end)
+ if err != nil {
+ t.Fatal("write end:", err)
+ }
+ if err := w.Close(); err != nil {
+ t.Fatal(err)
+ }
+
+ // read back zip file and check that we get to the end of it
+ r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
+ if err != nil {
+ t.Fatal("reader:", err)
+ }
+ f0 := r.File[0]
+ rc, err := f0.Open()
+ if err != nil {
+ t.Fatal("opening:", err)
+ }
+ for i := 0; i < (1<<32)/1024; i++ {
+ _, err := io.ReadFull(rc, chunk)
+ if err != nil {
+ t.Fatal("read:", err)
+ }
+ }
+ gotEnd, err := ioutil.ReadAll(rc)
+ if err != nil {
+ t.Fatal("read end:", err)
+ }
+ if !bytes.Equal(gotEnd, end) {
+ t.Errorf("End of zip64 archive %q, want %q", gotEnd, end)
+ }
+ err = rc.Close()
+ if err != nil {
+ t.Fatal("closing:", err)
+ }
+ if got, want := f0.UncompressedSize, uint32(uint32max); got != want {
+ t.Errorf("UncompressedSize %d, want %d", got, want)
+ }
+
+ if got, want := f0.UncompressedSize64, (1<<32)+uint64(len(end)); got != want {
+ t.Errorf("UncompressedSize64 %d, want %d", got, want)
+ }
+}
+
+func testInvalidHeader(h *FileHeader, t *testing.T) {
+ var buf bytes.Buffer
+ z := NewWriter(&buf)
+
+ f, err := z.CreateHeader(h)
+ if err != nil {
+ t.Fatalf("error creating header: %v", err)
+ }
+ if _, err := f.Write([]byte("hi")); err != nil {
+ t.Fatalf("error writing content: %v", err)
+ }
+ if err := z.Close(); err != nil {
+ t.Fatalf("error closing zip writer: %v", err)
+ }
+
+ b := buf.Bytes()
+ if _, err = NewReader(bytes.NewReader(b), int64(len(b))); err != ErrFormat {
+ t.Fatalf("got %v, expected ErrFormat", err)
+ }
+}
+
+func testValidHeader(h *FileHeader, t *testing.T) {
+ var buf bytes.Buffer
+ z := NewWriter(&buf)
+
+ f, err := z.CreateHeader(h)
+ if err != nil {
+ t.Fatalf("error creating header: %v", err)
+ }
+ if _, err := f.Write([]byte("hi")); err != nil {
+ t.Fatalf("error writing content: %v", err)
+ }
+ if err := z.Close(); err != nil {
+ t.Fatalf("error closing zip writer: %v", err)
+ }
+
+ b := buf.Bytes()
+ if _, err = NewReader(bytes.NewReader(b), int64(len(b))); err != nil {
+ t.Fatalf("got %v, expected nil", err)
+ }
+}
+
+// Issue 4302.
+func TestHeaderInvalidTagAndSize(t *testing.T) {
+ const timeFormat = "20060102T150405.000.txt"
+
+ ts := time.Now()
+ filename := ts.Format(timeFormat)
+
+ h := FileHeader{
+ Name: filename,
+ Method: Deflate,
+ Extra: []byte(ts.Format(time.RFC3339Nano)), // missing tag and len
+ }
+ h.SetModTime(ts)
+
+ testInvalidHeader(&h, t)
+}
+
+func TestHeaderTooShort(t *testing.T) {
+ h := FileHeader{
+ Name: "foo.txt",
+ Method: Deflate,
+ Extra: []byte{zip64ExtraId}, // missing size
+ }
+ testInvalidHeader(&h, t)
+}
+
+// Issue 4393. It is valid to have an extra data header
+// which contains no body.
+func TestZeroLengthHeader(t *testing.T) {
+ h := FileHeader{
+ Name: "extadata.txt",
+ Method: Deflate,
+ Extra: []byte{
+ 85, 84, 5, 0, 3, 154, 144, 195, 77, // tag 21589 size 5
+ 85, 120, 0, 0, // tag 30805 size 0
+ },
}
+ testValidHeader(&h, t)
}
diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go
index b44d0e7d1..ee69c2d31 100644
--- a/src/pkg/bufio/bufio.go
+++ b/src/pkg/bufio/bufio.go
@@ -64,6 +64,8 @@ func NewReader(rd io.Reader) *Reader {
return NewReaderSize(rd, defaultBufSize)
}
+var errNegativeRead = errors.New("bufio: reader returned negative count from Read")
+
// fill reads a new chunk into the buffer.
func (b *Reader) fill() {
// Slide existing data to beginning.
@@ -74,10 +76,13 @@ func (b *Reader) fill() {
}
// Read new data.
- n, e := b.rd.Read(b.buf[b.w:])
+ n, err := b.rd.Read(b.buf[b.w:])
+ if n < 0 {
+ panic(errNegativeRead)
+ }
b.w += n
- if e != nil {
- b.err = e
+ if err != nil {
+ b.err = err
}
}
@@ -272,6 +277,9 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
panic("not reached")
}
+// ReadLine is a low-level line-reading primitive. Most callers should use
+// ReadBytes('\n') or ReadString('\n') instead or use a Scanner.
+//
// ReadLine tries to return a single line, not including the end-of-line bytes.
// If the line was too long for the buffer then isPrefix is set and the
// beginning of the line is returned. The rest of the line will be returned
@@ -279,6 +287,9 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
// of the line. The returned buffer is only valid until the next call to
// ReadLine. ReadLine either returns a non-nil line or it returns an error,
// never both.
+//
+// The text returned from ReadLine does not include the line end ("\r\n" or "\n").
+// No indication or error is given if the input ends without a final line end.
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
line, err = b.ReadSlice('\n')
if err == ErrBufferFull {
@@ -320,6 +331,7 @@ func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
// it returns the data read before the error and the error itself (often io.EOF).
// ReadBytes returns err != nil if and only if the returned data does not end in
// delim.
+// For simple uses, a Scanner may be more convenient.
func (b *Reader) ReadBytes(delim byte) (line []byte, err error) {
// Use ReadSlice to look for array,
// accumulating full buffers.
@@ -367,9 +379,45 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err error) {
// it returns the data read before the error and the error itself (often io.EOF).
// ReadString returns err != nil if and only if the returned data does not end in
// delim.
+// For simple uses, a Scanner may be more convenient.
func (b *Reader) ReadString(delim byte) (line string, err error) {
- bytes, e := b.ReadBytes(delim)
- return string(bytes), e
+ bytes, err := b.ReadBytes(delim)
+ return string(bytes), err
+}
+
+// WriteTo implements io.WriterTo.
+func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
+ n, err = b.writeBuf(w)
+ if err != nil {
+ return
+ }
+
+ if r, ok := b.rd.(io.WriterTo); ok {
+ m, err := r.WriteTo(w)
+ n += m
+ return n, err
+ }
+
+ for b.fill(); b.r < b.w; b.fill() {
+ m, err := b.writeBuf(w)
+ n += m
+ if err != nil {
+ return n, err
+ }
+ }
+
+ if b.err == io.EOF {
+ b.err = nil
+ }
+
+ return n, b.readErr()
+}
+
+// writeBuf writes the Reader's buffer to the writer.
+func (b *Reader) writeBuf(w io.Writer) (int64, error) {
+ n, err := w.Write(b.buf[b.r:b.w])
+ b.r += n
+ return int64(n), err
}
// buffered output
@@ -415,17 +463,17 @@ func (b *Writer) Flush() error {
if b.n == 0 {
return nil
}
- n, e := b.wr.Write(b.buf[0:b.n])
- if n < b.n && e == nil {
- e = io.ErrShortWrite
+ n, err := b.wr.Write(b.buf[0:b.n])
+ if n < b.n && err == nil {
+ err = io.ErrShortWrite
}
- if e != nil {
+ if err != nil {
if n > 0 && n < b.n {
copy(b.buf[0:b.n-n], b.buf[n:b.n])
}
b.n -= n
- b.err = e
- return e
+ b.err = err
+ return err
}
b.n = 0
return nil
@@ -529,6 +577,36 @@ func (b *Writer) WriteString(s string) (int, error) {
return nn, nil
}
+// ReadFrom implements io.ReaderFrom.
+func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
+ if b.Buffered() == 0 {
+ if w, ok := b.wr.(io.ReaderFrom); ok {
+ return w.ReadFrom(r)
+ }
+ }
+ var m int
+ for {
+ m, err = r.Read(b.buf[b.n:])
+ if m == 0 {
+ break
+ }
+ b.n += m
+ n += int64(m)
+ if b.Available() == 0 {
+ if err1 := b.Flush(); err1 != nil {
+ return n, err1
+ }
+ }
+ if err != nil {
+ break
+ }
+ }
+ if err == io.EOF {
+ err = nil
+ }
+ return n, err
+}
+
// buffered input and output
// ReadWriter stores pointers to a Reader and a Writer.
diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go
index a43cbd23a..b0e811443 100644
--- a/src/pkg/bufio/bufio_test.go
+++ b/src/pkg/bufio/bufio_test.go
@@ -28,9 +28,9 @@ func newRot13Reader(r io.Reader) *rot13Reader {
}
func (r13 *rot13Reader) Read(p []byte) (int, error) {
- n, e := r13.r.Read(p)
- if e != nil {
- return n, e
+ n, err := r13.r.Read(p)
+ if err != nil {
+ return n, err
}
for i := 0; i < n; i++ {
c := p[i] | 0x20 // lowercase byte
@@ -48,15 +48,15 @@ func readBytes(buf *Reader) string {
var b [1000]byte
nb := 0
for {
- c, e := buf.ReadByte()
- if e == io.EOF {
+ c, err := buf.ReadByte()
+ if err == io.EOF {
break
}
- if e == nil {
+ if err == nil {
b[nb] = c
nb++
- } else if e != iotest.ErrTimeout {
- panic("Data: " + e.Error())
+ } else if err != iotest.ErrTimeout {
+ panic("Data: " + err.Error())
}
}
return string(b[0:nb])
@@ -93,12 +93,12 @@ var readMakers = []readMaker{
func readLines(b *Reader) string {
s := ""
for {
- s1, e := b.ReadString('\n')
- if e == io.EOF {
+ s1, err := b.ReadString('\n')
+ if err == io.EOF {
break
}
- if e != nil && e != iotest.ErrTimeout {
- panic("GetLines: " + e.Error())
+ if err != nil && err != iotest.ErrTimeout {
+ panic("GetLines: " + err.Error())
}
s += s1
}
@@ -110,9 +110,9 @@ func reads(buf *Reader, m int) string {
var b [1000]byte
nb := 0
for {
- n, e := buf.Read(b[nb : nb+m])
+ n, err := buf.Read(b[nb : nb+m])
nb += n
- if e == io.EOF {
+ if err == io.EOF {
break
}
}
@@ -748,7 +748,7 @@ func testReadLineNewlines(t *testing.T, input string, expect []readLineResult) {
b := NewReaderSize(strings.NewReader(input), minReadBufferSize)
for i, e := range expect {
line, isPrefix, err := b.ReadLine()
- if bytes.Compare(line, e.line) != 0 {
+ if !bytes.Equal(line, e.line) {
t.Errorf("%q call %d, line == %q, want %q", input, i, line, e.line)
return
}
@@ -762,3 +762,283 @@ func testReadLineNewlines(t *testing.T, input string, expect []readLineResult) {
}
}
}
+
+func createTestInput(n int) []byte {
+ input := make([]byte, n)
+ for i := range input {
+ // 101 and 251 are arbitrary prime numbers.
+ // The idea is to create an input sequence
+ // which doesn't repeat too frequently.
+ input[i] = byte(i % 251)
+ if i%101 == 0 {
+ input[i] ^= byte(i / 101)
+ }
+ }
+ return input
+}
+
+func TestReaderWriteTo(t *testing.T) {
+ input := createTestInput(8192)
+ r := NewReader(onlyReader{bytes.NewBuffer(input)})
+ w := new(bytes.Buffer)
+ if n, err := r.WriteTo(w); err != nil || n != int64(len(input)) {
+ t.Fatalf("r.WriteTo(w) = %d, %v, want %d, nil", n, err, len(input))
+ }
+
+ for i, val := range w.Bytes() {
+ if val != input[i] {
+ t.Errorf("after write: out[%d] = %#x, want %#x", i, val, input[i])
+ }
+ }
+}
+
+type errorWriterToTest struct {
+ rn, wn int
+ rerr, werr error
+ expected error
+}
+
+func (r errorWriterToTest) Read(p []byte) (int, error) {
+ return len(p) * r.rn, r.rerr
+}
+
+func (w errorWriterToTest) Write(p []byte) (int, error) {
+ return len(p) * w.wn, w.werr
+}
+
+var errorWriterToTests = []errorWriterToTest{
+ {1, 0, nil, io.ErrClosedPipe, io.ErrClosedPipe},
+ {0, 1, io.ErrClosedPipe, nil, io.ErrClosedPipe},
+ {0, 0, io.ErrUnexpectedEOF, io.ErrClosedPipe, io.ErrClosedPipe},
+ {0, 1, io.EOF, nil, nil},
+}
+
+func TestReaderWriteToErrors(t *testing.T) {
+ for i, rw := range errorWriterToTests {
+ r := NewReader(rw)
+ if _, err := r.WriteTo(rw); err != rw.expected {
+ t.Errorf("r.WriteTo(errorWriterToTests[%d]) = _, %v, want _,%v", i, err, rw.expected)
+ }
+ }
+}
+
+func TestWriterReadFrom(t *testing.T) {
+ ws := []func(io.Writer) io.Writer{
+ func(w io.Writer) io.Writer { return onlyWriter{w} },
+ func(w io.Writer) io.Writer { return w },
+ }
+
+ rs := []func(io.Reader) io.Reader{
+ iotest.DataErrReader,
+ func(r io.Reader) io.Reader { return r },
+ }
+
+ for ri, rfunc := range rs {
+ for wi, wfunc := range ws {
+ input := createTestInput(8192)
+ b := new(bytes.Buffer)
+ w := NewWriter(wfunc(b))
+ r := rfunc(bytes.NewBuffer(input))
+ if n, err := w.ReadFrom(r); err != nil || n != int64(len(input)) {
+ t.Errorf("ws[%d],rs[%d]: w.ReadFrom(r) = %d, %v, want %d, nil", wi, ri, n, err, len(input))
+ continue
+ }
+ if got, want := b.String(), string(input); got != want {
+ t.Errorf("ws[%d], rs[%d]:\ngot %q\nwant %q\n", wi, ri, got, want)
+ }
+ }
+ }
+}
+
+type errorReaderFromTest struct {
+ rn, wn int
+ rerr, werr error
+ expected error
+}
+
+func (r errorReaderFromTest) Read(p []byte) (int, error) {
+ return len(p) * r.rn, r.rerr
+}
+
+func (w errorReaderFromTest) Write(p []byte) (int, error) {
+ return len(p) * w.wn, w.werr
+}
+
+var errorReaderFromTests = []errorReaderFromTest{
+ {0, 1, io.EOF, nil, nil},
+ {1, 1, io.EOF, nil, nil},
+ {0, 1, io.ErrClosedPipe, nil, io.ErrClosedPipe},
+ {0, 0, io.ErrClosedPipe, io.ErrShortWrite, io.ErrClosedPipe},
+ {1, 0, nil, io.ErrShortWrite, io.ErrShortWrite},
+}
+
+func TestWriterReadFromErrors(t *testing.T) {
+ for i, rw := range errorReaderFromTests {
+ w := NewWriter(rw)
+ if _, err := w.ReadFrom(rw); err != rw.expected {
+ t.Errorf("w.ReadFrom(errorReaderFromTests[%d]) = _, %v, want _,%v", i, err, rw.expected)
+ }
+ }
+}
+
+// TestWriterReadFromCounts tests that using io.Copy to copy into a
+// bufio.Writer does not prematurely flush the buffer. For example, when
+// buffering writes to a network socket, excessive network writes should be
+// avoided.
+func TestWriterReadFromCounts(t *testing.T) {
+ var w0 writeCountingDiscard
+ b0 := NewWriterSize(&w0, 1234)
+ b0.WriteString(strings.Repeat("x", 1000))
+ if w0 != 0 {
+ t.Fatalf("write 1000 'x's: got %d writes, want 0", w0)
+ }
+ b0.WriteString(strings.Repeat("x", 200))
+ if w0 != 0 {
+ t.Fatalf("write 1200 'x's: got %d writes, want 0", w0)
+ }
+ io.Copy(b0, onlyReader{strings.NewReader(strings.Repeat("x", 30))})
+ if w0 != 0 {
+ t.Fatalf("write 1230 'x's: got %d writes, want 0", w0)
+ }
+ io.Copy(b0, onlyReader{strings.NewReader(strings.Repeat("x", 9))})
+ if w0 != 1 {
+ t.Fatalf("write 1239 'x's: got %d writes, want 1", w0)
+ }
+
+ var w1 writeCountingDiscard
+ b1 := NewWriterSize(&w1, 1234)
+ b1.WriteString(strings.Repeat("x", 1200))
+ b1.Flush()
+ if w1 != 1 {
+ t.Fatalf("flush 1200 'x's: got %d writes, want 1", w1)
+ }
+ b1.WriteString(strings.Repeat("x", 89))
+ if w1 != 1 {
+ t.Fatalf("write 1200 + 89 'x's: got %d writes, want 1", w1)
+ }
+ io.Copy(b1, onlyReader{strings.NewReader(strings.Repeat("x", 700))})
+ if w1 != 1 {
+ t.Fatalf("write 1200 + 789 'x's: got %d writes, want 1", w1)
+ }
+ io.Copy(b1, onlyReader{strings.NewReader(strings.Repeat("x", 600))})
+ if w1 != 2 {
+ t.Fatalf("write 1200 + 1389 'x's: got %d writes, want 2", w1)
+ }
+ b1.Flush()
+ if w1 != 3 {
+ t.Fatalf("flush 1200 + 1389 'x's: got %d writes, want 3", w1)
+ }
+}
+
+// A writeCountingDiscard is like ioutil.Discard and counts the number of times
+// Write is called on it.
+type writeCountingDiscard int
+
+func (w *writeCountingDiscard) Write(p []byte) (int, error) {
+ *w++
+ return len(p), nil
+}
+
+type negativeReader int
+
+func (r *negativeReader) Read([]byte) (int, error) { return -1, nil }
+
+func TestNegativeRead(t *testing.T) {
+ // should panic with a description pointing at the reader, not at itself.
+ // (should NOT panic with slice index error, for example.)
+ b := NewReader(new(negativeReader))
+ defer func() {
+ switch err := recover().(type) {
+ case nil:
+ t.Fatal("read did not panic")
+ case error:
+ if !strings.Contains(err.Error(), "reader returned negative count from Read") {
+ t.Fatalf("wrong panic: %v", err)
+ }
+ default:
+ t.Fatalf("unexpected panic value: %T(%v)", err, err)
+ }
+ }()
+ b.Read(make([]byte, 100))
+}
+
+// An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
+type onlyReader struct {
+ r io.Reader
+}
+
+func (r onlyReader) Read(b []byte) (int, error) {
+ return r.r.Read(b)
+}
+
+// An onlyWriter only implements io.Writer, no matter what other methods the underlying implementation may have.
+type onlyWriter struct {
+ w io.Writer
+}
+
+func (w onlyWriter) Write(b []byte) (int, error) {
+ return w.w.Write(b)
+}
+
+func BenchmarkReaderCopyOptimal(b *testing.B) {
+ // Optimal case is where the underlying reader implements io.WriterTo
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ src := NewReader(bytes.NewBuffer(make([]byte, 8192)))
+ dst := onlyWriter{new(bytes.Buffer)}
+ b.StartTimer()
+ io.Copy(dst, src)
+ }
+}
+
+func BenchmarkReaderCopyUnoptimal(b *testing.B) {
+ // Unoptimal case is where the underlying reader doesn't implement io.WriterTo
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ src := NewReader(onlyReader{bytes.NewBuffer(make([]byte, 8192))})
+ dst := onlyWriter{new(bytes.Buffer)}
+ b.StartTimer()
+ io.Copy(dst, src)
+ }
+}
+
+func BenchmarkReaderCopyNoWriteTo(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ src := onlyReader{NewReader(bytes.NewBuffer(make([]byte, 8192)))}
+ dst := onlyWriter{new(bytes.Buffer)}
+ b.StartTimer()
+ io.Copy(dst, src)
+ }
+}
+
+func BenchmarkWriterCopyOptimal(b *testing.B) {
+ // Optimal case is where the underlying writer implements io.ReaderFrom
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ src := onlyReader{bytes.NewBuffer(make([]byte, 8192))}
+ dst := NewWriter(new(bytes.Buffer))
+ b.StartTimer()
+ io.Copy(dst, src)
+ }
+}
+
+func BenchmarkWriterCopyUnoptimal(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ src := onlyReader{bytes.NewBuffer(make([]byte, 8192))}
+ dst := NewWriter(onlyWriter{new(bytes.Buffer)})
+ b.StartTimer()
+ io.Copy(dst, src)
+ }
+}
+
+func BenchmarkWriterCopyNoReadFrom(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ src := onlyReader{bytes.NewBuffer(make([]byte, 8192))}
+ dst := onlyWriter{NewWriter(new(bytes.Buffer))}
+ b.StartTimer()
+ io.Copy(dst, src)
+ }
+}
diff --git a/src/pkg/bufio/example_test.go b/src/pkg/bufio/example_test.go
new file mode 100644
index 000000000..b545ce39a
--- /dev/null
+++ b/src/pkg/bufio/example_test.go
@@ -0,0 +1,74 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bufio_test
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+)
+
+// The simplest use of a Scanner, to read standard input as a set of lines.
+func ExampleScanner_lines() {
+ scanner := bufio.NewScanner(os.Stdin)
+ for scanner.Scan() {
+ fmt.Println(scanner.Text()) // Println will add back the final '\n'
+ }
+ if err := scanner.Err(); err != nil {
+ fmt.Fprintln(os.Stdout, "reading standard input:", err)
+ }
+}
+
+// Use a Scanner to implement a simple word-count utility by scanning the
+// input as a sequence of space-delimited tokens.
+func ExampleScanner_words() {
+ // An artificial input source.
+ const input = "Now is the winter of our discontent,\nMade glorious summer by this sun of York.\n"
+ scanner := bufio.NewScanner(strings.NewReader(input))
+ // Set the split function for the scanning operation.
+ scanner.Split(bufio.ScanWords)
+ // Count the words.
+ count := 0
+ for scanner.Scan() {
+ count++
+ }
+ if err := scanner.Err(); err != nil {
+ fmt.Fprintln(os.Stdout, "reading input:", err)
+ }
+ fmt.Printf("%d\n", count)
+ // Output: 15
+}
+
+// Use a Scanner with a custom split function (built by wrapping ScanWords) to validate
+// 32-bit decimal input.
+func ExampleScanner_custom() {
+ // An artificial input source.
+ const input = "1234 5678 1234567901234567890"
+ scanner := bufio.NewScanner(strings.NewReader(input))
+ // Create a custom split function by wrapping the existing ScanWords function.
+ split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ advance, token, err = bufio.ScanWords(data, atEOF)
+ if err == nil && token != nil {
+ _, err = strconv.ParseInt(string(token), 10, 32)
+ }
+ return
+ }
+ // Set the split function for the scanning operation.
+ scanner.Split(split)
+ // Validate the input
+ for scanner.Scan() {
+ fmt.Printf("%s\n", scanner.Text())
+ }
+
+ if err := scanner.Err(); err != nil {
+ fmt.Printf("Invalid input: %s", err)
+ }
+ // Output:
+ // 1234
+ // 5678
+ // Invalid input: strconv.ParseInt: parsing "1234567901234567890": value out of range
+}
diff --git a/src/pkg/bufio/export_test.go b/src/pkg/bufio/export_test.go
new file mode 100644
index 000000000..3d3bb27d8
--- /dev/null
+++ b/src/pkg/bufio/export_test.go
@@ -0,0 +1,27 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bufio
+
+// Exported for testing only.
+import (
+ "unicode/utf8"
+)
+
+var IsSpace = isSpace
+
+func (s *Scanner) MaxTokenSize(n int) {
+ if n < utf8.UTFMax || n > 1e9 {
+ panic("bad max token size")
+ }
+ if n < len(s.buf) {
+ s.buf = make([]byte, n)
+ }
+ s.maxTokenSize = n
+}
+
+// ErrOrEOF is like Err, but returns EOF. Used to test a corner case.
+func (s *Scanner) ErrOrEOF() error {
+ return s.err
+}
diff --git a/src/pkg/bufio/scan.go b/src/pkg/bufio/scan.go
new file mode 100644
index 000000000..268ce6d1d
--- /dev/null
+++ b/src/pkg/bufio/scan.go
@@ -0,0 +1,338 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bufio
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "unicode/utf8"
+)
+
+// Scanner provides a convenient interface for reading data such as
+// a file of newline-delimited lines of text. Successive calls to
+// the Scan method will step through the 'tokens' of a file, skipping
+// the bytes between the tokens. The specification of a token is
+// defined by a split function of type SplitFunc; the default split
+// function breaks the input into lines with newlines stripped. Split
+// functions are defined in this package for scanning a file into
+// lines, bytes, UTF-8-encoded runes, and space-delimited words. The
+// client may instead provide a custom split function.
+//
+// Scanning stops unrecoverably at EOF, the first I/O error, or a token too
+// large to fit in the buffer. When a scan stops, the reader may have
+// advanced arbitrarily far past the last token. Programs that need more
+// control over error handling or large tokens, or must run sequential scans
+// on a reader, should use bufio.Reader instead.
+//
+// TODO(r): Provide executable examples.
+//
+type Scanner struct {
+ r io.Reader // The reader provided by the client.
+ split SplitFunc // The function to split the tokens.
+ maxTokenSize int // Maximum size of a token; modified by tests.
+ token []byte // Last token returned by split.
+ buf []byte // Buffer used as argument to split.
+ start int // First non-processed byte in buf.
+ end int // End of data in buf.
+ err error // Sticky error.
+}
+
+// SplitFunc is the signature of the split function used to tokenize the
+// input. The arguments are an initial substring of the remaining unprocessed
+// data and a flag, atEOF, that reports whether the Reader has no more data
+// to give. The return values are the number of bytes to advance the input
+// and the next token to return to the user, plus an error, if any. If the
+// data does not yet hold a complete token, for instance if it has no newline
+// while scanning lines, SplitFunc can return (0, nil) to signal the Scanner
+// to read more data into the slice and try again with a longer slice
+// starting at the same point in the input.
+//
+// If the returned error is non-nil, scanning stops and the error
+// is returned to the client.
+//
+// The function is never called with an empty data slice unless atEOF
+// is true. If atEOF is true, however, data may be non-empty and,
+// as always, holds unprocessed text.
+type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
+
+// Errors returned by Scanner.
+var (
+ ErrTooLong = errors.New("bufio.Scanner: token too long")
+ ErrNegativeAdvance = errors.New("bufio.Scanner: SplitFunc returns negative advance count")
+ ErrAdvanceTooFar = errors.New("bufio.Scanner: SplitFunc returns advance count beyond input")
+)
+
+const (
+ // Maximum size used to buffer a token. The actual maximum token size
+ // may be smaller as the buffer may need to include, for instance, a newline.
+ MaxScanTokenSize = 64 * 1024
+)
+
+// NewScanner returns a new Scanner to read from r.
+func NewScanner(r io.Reader) *Scanner {
+ return &Scanner{
+ r: r,
+ split: ScanLines,
+ maxTokenSize: MaxScanTokenSize,
+ buf: make([]byte, 4096), // Plausible starting size; needn't be large.
+ }
+}
+
+// Err returns the first non-EOF error that was encountered by the Scanner.
+func (s *Scanner) Err() error {
+ if s.err == io.EOF {
+ return nil
+ }
+ return s.err
+}
+
+// Bytes returns the most recent token generated by a call to Scan.
+// The underlying array may point to data that will be overwritten
+// by a subsequent call to Scan. It does no allocation.
+func (s *Scanner) Bytes() []byte {
+ return s.token
+}
+
+// Text returns the most recent token generated by a call to Scan
+// as a newly allocated string holding its bytes.
+func (s *Scanner) Text() string {
+ return string(s.token)
+}
+
+// Scan advances the Scanner to the next token, which will then be
+// available through the Bytes or Text method. It returns false when the
+// scan stops, either by reaching the end of the input or an error.
+// After Scan returns false, the Err method will return any error that
+// occurred during scanning, except that if it was io.EOF, Err
+// will return nil.
+func (s *Scanner) Scan() bool {
+ // Loop until we have a token.
+ for {
+ // See if we can get a token with what we already have.
+ if s.end > s.start {
+ advance, token, err := s.split(s.buf[s.start:s.end], s.err != nil)
+ if err != nil {
+ s.setErr(err)
+ return false
+ }
+ if !s.advance(advance) {
+ return false
+ }
+ s.token = token
+ if token != nil {
+ return true
+ }
+ }
+ // We cannot generate a token with what we are holding.
+ // If we've already hit EOF or an I/O error, we are done.
+ if s.err != nil {
+ // Shut it down.
+ s.start = 0
+ s.end = 0
+ return false
+ }
+ // Must read more data.
+ // First, shift data to beginning of buffer if there's lots of empty space
+ // or space is neded.
+ if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
+ copy(s.buf, s.buf[s.start:s.end])
+ s.end -= s.start
+ s.start = 0
+ }
+ // Is the buffer full? If so, resize.
+ if s.end == len(s.buf) {
+ if len(s.buf) >= s.maxTokenSize {
+ s.setErr(ErrTooLong)
+ return false
+ }
+ newSize := len(s.buf) * 2
+ if newSize > s.maxTokenSize {
+ newSize = s.maxTokenSize
+ }
+ newBuf := make([]byte, newSize)
+ copy(newBuf, s.buf[s.start:s.end])
+ s.buf = newBuf
+ s.end -= s.start
+ s.start = 0
+ continue
+ }
+ // Finally we can read some input.
+ n, err := s.r.Read(s.buf[s.end:len(s.buf)])
+ if err != nil {
+ s.setErr(err)
+ }
+ if n == 0 { // Don't loop forever if Reader doesn't deliver EOF.
+ s.err = io.EOF
+ }
+ s.end += n
+ }
+ panic("not reached")
+}
+
+// advance consumes n bytes of the buffer. It reports whether the advance was legal.
+func (s *Scanner) advance(n int) bool {
+ if n < 0 {
+ s.setErr(ErrNegativeAdvance)
+ return false
+ }
+ if n > s.end-s.start {
+ s.setErr(ErrAdvanceTooFar)
+ return false
+ }
+ s.start += n
+ return true
+}
+
+// setErr records the first error encountered.
+func (s *Scanner) setErr(err error) {
+ if s.err == nil || s.err == io.EOF {
+ s.err = err
+ }
+}
+
+// Split sets the split function for the Scanner. If called, it must be
+// called before Scan. The default split function is ScanLines.
+func (s *Scanner) Split(split SplitFunc) {
+ s.split = split
+}
+
+// Split functions
+
+// ScanBytes is a split function for a Scanner that returns each byte as a token.
+func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ if atEOF && len(data) == 0 {
+ return 0, nil, nil
+ }
+ return 1, data[0:1], nil
+}
+
+var errorRune = []byte(string(utf8.RuneError))
+
+// ScanRunes is a split function for a Scanner that returns each
+// UTF-8-encoded rune as a token. The sequence of runes returned is
+// equivalent to that from a range loop over the input as a string, which
+// means that erroneous UTF-8 encodings translate to U+FFFD = "\xef\xbf\xbd".
+// Because of the Scan interface, this makes it impossible for the client to
+// distinguish correctly encoded replacement runes from encoding errors.
+func ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ if atEOF && len(data) == 0 {
+ return 0, nil, nil
+ }
+
+ // Fast path 1: ASCII.
+ if data[0] < utf8.RuneSelf {
+ return 1, data[0:1], nil
+ }
+
+ // Fast path 2: Correct UTF-8 decode without error.
+ _, width := utf8.DecodeRune(data)
+ if width > 1 {
+ // It's a valid encoding. Width cannot be one for a correctly encoded
+ // non-ASCII rune.
+ return width, data[0:width], nil
+ }
+
+ // We know it's an error: we have width==1 and implicitly r==utf8.RuneError.
+ // Is the error because there wasn't a full rune to be decoded?
+ // FullRune distinguishes correctly between erroneous and incomplete encodings.
+ if !atEOF && !utf8.FullRune(data) {
+ // Incomplete; get more bytes.
+ return 0, nil, nil
+ }
+
+ // We have a real UTF-8 encoding error. Return a properly encoded error rune
+ // but advance only one byte. This matches the behavior of a range loop over
+ // an incorrectly encoded string.
+ return 1, errorRune, nil
+}
+
+// dropCR drops a terminal \r from the data.
+func dropCR(data []byte) []byte {
+ if len(data) > 0 && data[len(data)-1] == '\r' {
+ return data[0 : len(data)-1]
+ }
+ return data
+}
+
+// ScanLines is a split function for a Scanner that returns each line of
+// text, stripped of any trailing end-of-line marker. The returned line may
+// be empty. The end-of-line marker is one optional carriage return followed
+// by one mandatory newline. In regular expression notation, it is `\r?\n'.
+// The last non-empty line of input will be returned even if it has no
+// newline.
+func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ if atEOF && len(data) == 0 {
+ return 0, nil, nil
+ }
+ if i := bytes.IndexByte(data, '\n'); i >= 0 {
+ // We have a full newline-terminated line.
+ return i + 1, dropCR(data[0:i]), nil
+ }
+ // If we're at EOF, we have a final, non-terminated line. Return it.
+ if atEOF {
+ return len(data), dropCR(data), nil
+ }
+ // Request more data.
+ return 0, nil, nil
+}
+
+// isSpace returns whether the character is a Unicode white space character.
+// We avoid dependency on the unicode package, but check validity of the implementation
+// in the tests.
+func isSpace(r rune) bool {
+ if r <= '\u00FF' {
+ // Obvious ASCII ones: \t through \r plus space. Plus two Latin-1 oddballs.
+ switch r {
+ case ' ', '\t', '\n', '\v', '\f', '\r':
+ return true
+ case '\u0085', '\u00A0':
+ return true
+ }
+ return false
+ }
+ // High-valued ones.
+ if '\u2000' <= r && r <= '\u200a' {
+ return true
+ }
+ switch r {
+ case '\u1680', '\u180e', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
+ return true
+ }
+ return false
+}
+
+// ScanWords is a split function for a Scanner that returns each
+// space-separated word of text, with surrounding spaces deleted. It will
+// never return an empty string. The definition of space is set by
+// unicode.IsSpace.
+func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ // Skip leading spaces.
+ start := 0
+ for width := 0; start < len(data); start += width {
+ var r rune
+ r, width = utf8.DecodeRune(data[start:])
+ if !isSpace(r) {
+ break
+ }
+ }
+ if atEOF && len(data) == 0 {
+ return 0, nil, nil
+ }
+ // Scan until space, marking end of word.
+ for width, i := 0, start; i < len(data); i += width {
+ var r rune
+ r, width = utf8.DecodeRune(data[i:])
+ if isSpace(r) {
+ return i + width, data[start:i], nil
+ }
+ }
+ // If we're at EOF, we have a final, non-empty, non-terminated word. Return it.
+ if atEOF && len(data) > start {
+ return len(data), data[start:], nil
+ }
+ // Request more data.
+ return 0, nil, nil
+}
diff --git a/src/pkg/bufio/scan_test.go b/src/pkg/bufio/scan_test.go
new file mode 100644
index 000000000..48729aabb
--- /dev/null
+++ b/src/pkg/bufio/scan_test.go
@@ -0,0 +1,370 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bufio_test
+
+import (
+ . "bufio"
+ "bytes"
+ "errors"
+ "io"
+ "strings"
+ "testing"
+ "unicode"
+ "unicode/utf8"
+)
+
+// Test white space table matches the Unicode definition.
+func TestSpace(t *testing.T) {
+ for r := rune(0); r <= utf8.MaxRune; r++ {
+ if IsSpace(r) != unicode.IsSpace(r) {
+ t.Fatalf("white space property disagrees: %#U should be %t", r, unicode.IsSpace(r))
+ }
+ }
+}
+
+var scanTests = []string{
+ "",
+ "a",
+ "¼",
+ "☹",
+ "\x81", // UTF-8 error
+ "\uFFFD", // correctly encoded RuneError
+ "abcdefgh",
+ "abc def\n\t\tgh ",
+ "abc¼☹\x81\uFFFD日本語\x82abc",
+}
+
+func TestScanByte(t *testing.T) {
+ for n, test := range scanTests {
+ buf := bytes.NewBufferString(test)
+ s := NewScanner(buf)
+ s.Split(ScanBytes)
+ var i int
+ for i = 0; s.Scan(); i++ {
+ if b := s.Bytes(); len(b) != 1 || b[0] != test[i] {
+ t.Errorf("#%d: %d: expected %q got %q", n, i, test, b)
+ }
+ }
+ if i != len(test) {
+ t.Errorf("#%d: termination expected at %d; got %d", n, len(test), i)
+ }
+ err := s.Err()
+ if err != nil {
+ t.Errorf("#%d: %v", n, err)
+ }
+ }
+}
+
+// Test that the rune splitter returns same sequence of runes (not bytes) as for range string.
+func TestScanRune(t *testing.T) {
+ for n, test := range scanTests {
+ buf := bytes.NewBufferString(test)
+ s := NewScanner(buf)
+ s.Split(ScanRunes)
+ var i, runeCount int
+ var expect rune
+ // Use a string range loop to validate the sequence of runes.
+ for i, expect = range string(test) {
+ if !s.Scan() {
+ break
+ }
+ runeCount++
+ got, _ := utf8.DecodeRune(s.Bytes())
+ if got != expect {
+ t.Errorf("#%d: %d: expected %q got %q", n, i, expect, got)
+ }
+ }
+ if s.Scan() {
+ t.Errorf("#%d: scan ran too long, got %q", n, s.Text())
+ }
+ testRuneCount := utf8.RuneCountInString(test)
+ if runeCount != testRuneCount {
+ t.Errorf("#%d: termination expected at %d; got %d", n, testRuneCount, runeCount)
+ }
+ err := s.Err()
+ if err != nil {
+ t.Errorf("#%d: %v", n, err)
+ }
+ }
+}
+
+var wordScanTests = []string{
+ "",
+ " ",
+ "\n",
+ "a",
+ " a ",
+ "abc def",
+ " abc def ",
+ " abc\tdef\nghi\rjkl\fmno\vpqr\u0085stu\u00a0\n",
+}
+
+// Test that the word splitter returns the same data as strings.Fields.
+func TestScanWords(t *testing.T) {
+ for n, test := range wordScanTests {
+ buf := bytes.NewBufferString(test)
+ s := NewScanner(buf)
+ s.Split(ScanWords)
+ words := strings.Fields(test)
+ var wordCount int
+ for wordCount = 0; wordCount < len(words); wordCount++ {
+ if !s.Scan() {
+ break
+ }
+ got := s.Text()
+ if got != words[wordCount] {
+ t.Errorf("#%d: %d: expected %q got %q", n, wordCount, words[wordCount], got)
+ }
+ }
+ if s.Scan() {
+ t.Errorf("#%d: scan ran too long, got %q", n, s.Text())
+ }
+ if wordCount != len(words) {
+ t.Errorf("#%d: termination expected at %d; got %d", n, len(words), wordCount)
+ }
+ err := s.Err()
+ if err != nil {
+ t.Errorf("#%d: %v", n, err)
+ }
+ }
+}
+
+// slowReader is a reader that returns only a few bytes at a time, to test the incremental
+// reads in Scanner.Scan.
+type slowReader struct {
+ max int
+ buf *bytes.Buffer
+}
+
+func (sr *slowReader) Read(p []byte) (n int, err error) {
+ if len(p) > sr.max {
+ p = p[0:sr.max]
+ }
+ return sr.buf.Read(p)
+}
+
+// genLine writes to buf a predictable but non-trivial line of text of length
+// n, including the terminal newline and an occasional carriage return.
+// If addNewline is false, the \r and \n are not emitted.
+func genLine(buf *bytes.Buffer, lineNum, n int, addNewline bool) {
+ buf.Reset()
+ doCR := lineNum%5 == 0
+ if doCR {
+ n--
+ }
+ for i := 0; i < n-1; i++ { // Stop early for \n.
+ c := 'a' + byte(lineNum+i)
+ if c == '\n' || c == '\r' { // Don't confuse us.
+ c = 'N'
+ }
+ buf.WriteByte(c)
+ }
+ if addNewline {
+ if doCR {
+ buf.WriteByte('\r')
+ }
+ buf.WriteByte('\n')
+ }
+ return
+}
+
+// Test the line splitter, including some carriage returns but no long lines.
+func TestScanLongLines(t *testing.T) {
+ const smallMaxTokenSize = 256 // Much smaller for more efficient testing.
+ // Build a buffer of lots of line lengths up to but not exceeding smallMaxTokenSize.
+ tmp := new(bytes.Buffer)
+ buf := new(bytes.Buffer)
+ lineNum := 0
+ j := 0
+ for i := 0; i < 2*smallMaxTokenSize; i++ {
+ genLine(tmp, lineNum, j, true)
+ if j < smallMaxTokenSize {
+ j++
+ } else {
+ j--
+ }
+ buf.Write(tmp.Bytes())
+ lineNum++
+ }
+ s := NewScanner(&slowReader{1, buf})
+ s.Split(ScanLines)
+ s.MaxTokenSize(smallMaxTokenSize)
+ j = 0
+ for lineNum := 0; s.Scan(); lineNum++ {
+ genLine(tmp, lineNum, j, false)
+ if j < smallMaxTokenSize {
+ j++
+ } else {
+ j--
+ }
+ line := tmp.String() // We use the string-valued token here, for variety.
+ if s.Text() != line {
+ t.Errorf("%d: bad line: %d %d\n%.100q\n%.100q\n", lineNum, len(s.Bytes()), len(line), s.Text(), line)
+ }
+ }
+ err := s.Err()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+// Test that the line splitter errors out on a long line.
+func TestScanLineTooLong(t *testing.T) {
+ const smallMaxTokenSize = 256 // Much smaller for more efficient testing.
+ // Build a buffer of lots of line lengths up to but not exceeding smallMaxTokenSize.
+ tmp := new(bytes.Buffer)
+ buf := new(bytes.Buffer)
+ lineNum := 0
+ j := 0
+ for i := 0; i < 2*smallMaxTokenSize; i++ {
+ genLine(tmp, lineNum, j, true)
+ j++
+ buf.Write(tmp.Bytes())
+ lineNum++
+ }
+ s := NewScanner(&slowReader{3, buf})
+ s.Split(ScanLines)
+ s.MaxTokenSize(smallMaxTokenSize)
+ j = 0
+ for lineNum := 0; s.Scan(); lineNum++ {
+ genLine(tmp, lineNum, j, false)
+ if j < smallMaxTokenSize {
+ j++
+ } else {
+ j--
+ }
+ line := tmp.Bytes()
+ if !bytes.Equal(s.Bytes(), line) {
+ t.Errorf("%d: bad line: %d %d\n%.100q\n%.100q\n", lineNum, len(s.Bytes()), len(line), s.Bytes(), line)
+ }
+ }
+ err := s.Err()
+ if err != ErrTooLong {
+ t.Fatalf("expected ErrTooLong; got %s", err)
+ }
+}
+
+// Test that the line splitter handles a final line without a newline.
+func testNoNewline(text string, lines []string, t *testing.T) {
+ buf := bytes.NewBufferString(text)
+ s := NewScanner(&slowReader{7, buf})
+ s.Split(ScanLines)
+ for lineNum := 0; s.Scan(); lineNum++ {
+ line := lines[lineNum]
+ if s.Text() != line {
+ t.Errorf("%d: bad line: %d %d\n%.100q\n%.100q\n", lineNum, len(s.Bytes()), len(line), s.Bytes(), line)
+ }
+ }
+ err := s.Err()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+var noNewlineLines = []string{
+ "abcdefghijklmn\nopqrstuvwxyz",
+}
+
+// Test that the line splitter handles a final line without a newline.
+func TestScanLineNoNewline(t *testing.T) {
+ const text = "abcdefghijklmn\nopqrstuvwxyz"
+ lines := []string{
+ "abcdefghijklmn",
+ "opqrstuvwxyz",
+ }
+ testNoNewline(text, lines, t)
+}
+
+// Test that the line splitter handles a final line with a carriage return but nonewline.
+func TestScanLineReturnButNoNewline(t *testing.T) {
+ const text = "abcdefghijklmn\nopqrstuvwxyz\r"
+ lines := []string{
+ "abcdefghijklmn",
+ "opqrstuvwxyz",
+ }
+ testNoNewline(text, lines, t)
+}
+
+// Test that the line splitter handles a final empty line.
+func TestScanLineEmptyFinalLine(t *testing.T) {
+ const text = "abcdefghijklmn\nopqrstuvwxyz\n\n"
+ lines := []string{
+ "abcdefghijklmn",
+ "opqrstuvwxyz",
+ "",
+ }
+ testNoNewline(text, lines, t)
+}
+
+// Test that the line splitter handles a final empty line with a carriage return but no newline.
+func TestScanLineEmptyFinalLineWithCR(t *testing.T) {
+ const text = "abcdefghijklmn\nopqrstuvwxyz\n\r"
+ lines := []string{
+ "abcdefghijklmn",
+ "opqrstuvwxyz",
+ "",
+ }
+ testNoNewline(text, lines, t)
+}
+
+var testError = errors.New("testError")
+
+// Test the correct error is returned when the split function errors out.
+func TestSplitError(t *testing.T) {
+ // Create a split function that delivers a little data, then a predictable error.
+ numSplits := 0
+ const okCount = 7
+ errorSplit := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ if atEOF {
+ panic("didn't get enough data")
+ }
+ if numSplits >= okCount {
+ return 0, nil, testError
+ }
+ numSplits++
+ return 1, data[0:1], nil
+ }
+ // Read the data.
+ const text = "abcdefghijklmnopqrstuvwxyz"
+ buf := bytes.NewBufferString(text)
+ s := NewScanner(&slowReader{1, buf})
+ s.Split(errorSplit)
+ var i int
+ for i = 0; s.Scan(); i++ {
+ if len(s.Bytes()) != 1 || text[i] != s.Bytes()[0] {
+ t.Errorf("#%d: expected %q got %q", i, text[i], s.Bytes()[0])
+ }
+ }
+ // Check correct termination location and error.
+ if i != okCount {
+ t.Errorf("unexpected termination; expected %d tokens got %d", okCount, i)
+ }
+ err := s.Err()
+ if err != testError {
+ t.Fatalf("expected %q got %v", testError, err)
+ }
+}
+
+// Test that an EOF is overridden by a user-generated scan error.
+func TestErrAtEOF(t *testing.T) {
+ s := NewScanner(strings.NewReader("1 2 33"))
+ // This spitter will fail on last entry, after s.err==EOF.
+ split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ advance, token, err = ScanWords(data, atEOF)
+ if len(token) > 1 {
+ if s.ErrOrEOF() != io.EOF {
+ t.Fatal("not testing EOF")
+ }
+ err = testError
+ }
+ return
+ }
+ s.Split(split)
+ for s.Scan() {
+ }
+ if s.Err() != testError {
+ t.Fatal("wrong error:", s.Err())
+ }
+}
diff --git a/src/pkg/builtin/builtin.go b/src/pkg/builtin/builtin.go
index e81616ca4..7b5e9ab1d 100644
--- a/src/pkg/builtin/builtin.go
+++ b/src/pkg/builtin/builtin.go
@@ -81,9 +81,8 @@ type uintptr uintptr
// integer values.
type byte byte
-// rune is an alias for int and is equivalent to int in all ways. It is
+// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
-// In a future version of Go, it will change to an alias of int32.
type rune rune
// Type is here for the purposes of documentation only. It is a stand-in
@@ -115,6 +114,8 @@ type ComplexType complex64
// result of append, often in the variable holding the slice itself:
// slice = append(slice, elem1, elem2)
// slice = append(slice, anotherSlice...)
+// As a special case, it is legal to append a string to a byte slice, like this:
+// slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type
// The copy built-in function copies elements from a source slice into a
@@ -125,8 +126,8 @@ func append(slice []Type, elems ...Type) []Type
func copy(dst, src []Type) int
// The delete built-in function deletes the element with the specified key
-// (m[key]) from the map. If there is no such element, delete is a no-op.
-// If m is nil, delete panics.
+// (m[key]) from the map. If m is nil or there is no such element, delete
+// is a no-op.
func delete(m map[Type]Type1, key Type)
// The len built-in function returns the length of v, according to its type:
diff --git a/src/pkg/bytes/asm_386.s b/src/pkg/bytes/asm_386.s
index e7833de0c..c444b55e1 100644
--- a/src/pkg/bytes/asm_386.s
+++ b/src/pkg/bytes/asm_386.s
@@ -3,31 +3,31 @@
// license that can be found in the LICENSE file.
TEXT ·IndexByte(SB),7,$0
- MOVL p+0(FP), SI
- MOVL len+4(FP), CX
- MOVB b+12(FP), AL
+ MOVL s+0(FP), SI
+ MOVL s+4(FP), CX
+ MOVB c+12(FP), AL
MOVL SI, DI
CLD; REPN; SCASB
JZ 3(PC)
- MOVL $-1, ret+16(FP)
+ MOVL $-1, r+16(FP)
RET
SUBL SI, DI
SUBL $1, DI
- MOVL DI, ret+16(FP)
+ MOVL DI, r+16(FP)
RET
TEXT ·Equal(SB),7,$0
- MOVL len+4(FP), BX
- MOVL len1+16(FP), CX
+ MOVL a+4(FP), BX
+ MOVL b+16(FP), CX
MOVL $0, AX
CMPL BX, CX
JNE eqret
- MOVL p+0(FP), SI
- MOVL q+12(FP), DI
+ MOVL a+0(FP), SI
+ MOVL b+12(FP), DI
CLD
REP; CMPSB
JNE eqret
MOVL $1, AX
eqret:
- MOVB AX, ret+24(FP)
+ MOVB AX, r+24(FP)
RET
diff --git a/src/pkg/bytes/asm_amd64.s b/src/pkg/bytes/asm_amd64.s
index 5caea5c52..482422642 100644
--- a/src/pkg/bytes/asm_amd64.s
+++ b/src/pkg/bytes/asm_amd64.s
@@ -3,12 +3,12 @@
// license that can be found in the LICENSE file.
TEXT ·IndexByte(SB),7,$0
- MOVQ p+0(FP), SI
- MOVL len+8(FP), BX
- MOVB b+16(FP), AL
+ MOVQ s+0(FP), SI
+ MOVQ s+8(FP), BX
+ MOVB c+24(FP), AL
MOVQ SI, DI
- CMPL BX, $16
+ CMPQ BX, $16
JLT small
// round up to first 16-byte boundary
@@ -63,15 +63,15 @@ condition:
JZ success
failure:
- MOVL $-1, ret+24(FP)
+ MOVQ $-1, r+32(FP)
RET
// handle for lengths < 16
small:
- MOVL BX, CX
+ MOVQ BX, CX
REPN; SCASB
JZ success
- MOVL $-1, ret+24(FP)
+ MOVQ $-1, r+32(FP)
RET
// we've found the chunk containing the byte
@@ -81,28 +81,28 @@ ssesuccess:
BSFW DX, DX
SUBQ SI, DI
ADDQ DI, DX
- MOVL DX, ret+24(FP)
+ MOVQ DX, r+32(FP)
RET
success:
SUBQ SI, DI
SUBL $1, DI
- MOVL DI, ret+24(FP)
+ MOVQ DI, r+32(FP)
RET
TEXT ·Equal(SB),7,$0
- MOVL len+8(FP), BX
- MOVL len1+24(FP), CX
+ MOVQ a+8(FP), BX
+ MOVQ b+32(FP), CX
MOVL $0, AX
- CMPL BX, CX
+ CMPQ BX, CX
JNE eqret
- MOVQ p+0(FP), SI
- MOVQ q+16(FP), DI
+ MOVQ a+0(FP), SI
+ MOVQ b+24(FP), DI
CLD
REP; CMPSB
MOVL $1, DX
CMOVLEQ DX, AX
eqret:
- MOVB AX, ret+32(FP)
+ MOVB AX, r+48(FP)
RET
diff --git a/src/pkg/bytes/asm_arm.s b/src/pkg/bytes/asm_arm.s
index 4ed0c1580..c7685f041 100644
--- a/src/pkg/bytes/asm_arm.s
+++ b/src/pkg/bytes/asm_arm.s
@@ -2,10 +2,55 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// no memchr implementation on arm yet
TEXT ·IndexByte(SB),7,$0
- B ·indexBytePortable(SB)
+ MOVW s+0(FP), R0
+ MOVW s+4(FP), R1
+ MOVBU c+12(FP), R2 // byte to find
+ MOVW R0, R4 // store base for later
+ ADD R0, R1 // end
+
+_loop:
+ CMP R0, R1
+ B.EQ _notfound
+ MOVBU.P 1(R0), R3
+ CMP R2, R3
+ B.NE _loop
+
+ SUB $1, R0 // R0 will be one beyond the position we want
+ SUB R4, R0 // remove base
+ MOVW R0, r+16(FP)
+ RET
+
+_notfound:
+ MOVW $-1, R0
+ MOVW R0, r+16(FP)
+ RET
-// no memcmp implementation on arm yet
TEXT ·Equal(SB),7,$0
- B ·equalPortable(SB)
+ MOVW a+4(FP), R1
+ MOVW b+16(FP), R3
+
+ CMP R1, R3 // unequal lengths are not equal
+ B.NE _notequal
+
+ MOVW a+0(FP), R0
+ MOVW b+12(FP), R2
+ ADD R0, R1 // end
+
+_next:
+ CMP R0, R1
+ B.EQ _equal // reached the end
+ MOVBU.P 1(R0), R4
+ MOVBU.P 1(R2), R5
+ CMP R4, R5
+ B.EQ _next
+
+_notequal:
+ MOVW $0, R0
+ MOVBU R0, r+24(FP)
+ RET
+
+_equal:
+ MOVW $1, R0
+ MOVBU R0, r+24(FP)
+ RET
diff --git a/src/pkg/bytes/buffer.go b/src/pkg/bytes/buffer.go
index afdf22055..85c157798 100644
--- a/src/pkg/bytes/buffer.go
+++ b/src/pkg/bytes/buffer.go
@@ -99,6 +99,19 @@ func (b *Buffer) grow(n int) int {
return b.off + m
}
+// Grow grows the buffer's capacity, if necessary, to guarantee space for
+// another n bytes. After Grow(n), at least n bytes can be written to the
+// buffer without another allocation.
+// If n is negative, Grow will panic.
+// If the buffer can't grow it will panic with ErrTooLarge.
+func (b *Buffer) Grow(n int) {
+ if n < 0 {
+ panic("bytes.Buffer.Grow: negative count")
+ }
+ m := b.grow(n)
+ b.buf = b.buf[0:m]
+}
+
// Write appends the contents of p to the buffer. The return
// value n is the length of p; err is always nil.
// If the buffer becomes too large, Write will panic with
@@ -347,16 +360,25 @@ func (b *Buffer) UnreadByte() error {
// ReadBytes returns err != nil if and only if the returned data does not end in
// delim.
func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
+ slice, err := b.readSlice(delim)
+ // return a copy of slice. The buffer's backing array may
+ // be overwritten by later calls.
+ line = append(line, slice...)
+ return
+}
+
+// readSlice is like ReadBytes but returns a reference to internal buffer data.
+func (b *Buffer) readSlice(delim byte) (line []byte, err error) {
i := IndexByte(b.buf[b.off:], delim)
- size := i + 1
+ end := b.off + i + 1
if i < 0 {
- size = len(b.buf) - b.off
+ end = len(b.buf)
err = io.EOF
}
- line = make([]byte, size)
- copy(line, b.buf[b.off:])
- b.off += size
- return
+ line = b.buf[b.off:end]
+ b.off = end
+ b.lastRead = opRead
+ return line, err
}
// ReadString reads until the first occurrence of delim in the input,
@@ -366,8 +388,8 @@ func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
// ReadString returns err != nil if and only if the returned data does not end
// in delim.
func (b *Buffer) ReadString(delim byte) (line string, err error) {
- bytes, err := b.ReadBytes(delim)
- return string(bytes), err
+ slice, err := b.readSlice(delim)
+ return string(slice), err
}
// NewBuffer creates and initializes a new Buffer using buf as its initial
diff --git a/src/pkg/bytes/buffer_test.go b/src/pkg/bytes/buffer_test.go
index d0af11f10..f9fb2625a 100644
--- a/src/pkg/bytes/buffer_test.go
+++ b/src/pkg/bytes/buffer_test.go
@@ -8,20 +8,21 @@ import (
. "bytes"
"io"
"math/rand"
+ "runtime"
"testing"
"unicode/utf8"
)
-const N = 10000 // make this bigger for a larger (and slower) test
-var data string // test data for write tests
-var bytes []byte // test data; same as data but as a slice.
+const N = 10000 // make this bigger for a larger (and slower) test
+var data string // test data for write tests
+var testBytes []byte // test data; same as data but as a slice.
func init() {
- bytes = make([]byte, N)
+ testBytes = make([]byte, N)
for i := 0; i < N; i++ {
- bytes[i] = 'a' + byte(i%26)
+ testBytes[i] = 'a' + byte(i%26)
}
- data = string(bytes)
+ data = string(testBytes)
}
// Verify that contents of buf match the string s.
@@ -84,7 +85,7 @@ func fillBytes(t *testing.T, testname string, buf *Buffer, s string, n int, fub
}
func TestNewBuffer(t *testing.T) {
- buf := NewBuffer(bytes)
+ buf := NewBuffer(testBytes)
check(t, "NewBuffer", buf, data)
}
@@ -187,7 +188,7 @@ func TestLargeByteWrites(t *testing.T) {
limit = 9
}
for i := 3; i < limit; i += 3 {
- s := fillBytes(t, "TestLargeWrites (1)", &buf, "", 5, bytes)
+ s := fillBytes(t, "TestLargeWrites (1)", &buf, "", 5, testBytes)
empty(t, "TestLargeByteWrites (2)", &buf, s, make([]byte, len(data)/i))
}
check(t, "TestLargeByteWrites (3)", &buf, "")
@@ -205,7 +206,7 @@ func TestLargeStringReads(t *testing.T) {
func TestLargeByteReads(t *testing.T) {
var buf Buffer
for i := 3; i < 30; i += 3 {
- s := fillBytes(t, "TestLargeReads (1)", &buf, "", 5, bytes[0:len(bytes)/i])
+ s := fillBytes(t, "TestLargeReads (1)", &buf, "", 5, testBytes[0:len(testBytes)/i])
empty(t, "TestLargeReads (2)", &buf, s, make([]byte, len(data)))
}
check(t, "TestLargeByteReads (3)", &buf, "")
@@ -219,7 +220,7 @@ func TestMixedReadsAndWrites(t *testing.T) {
if i%2 == 0 {
s = fillString(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, data[0:wlen])
} else {
- s = fillBytes(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, bytes[0:wlen])
+ s = fillBytes(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, testBytes[0:wlen])
}
rlen := rand.Intn(len(data))
@@ -240,7 +241,7 @@ func TestNil(t *testing.T) {
func TestReadFrom(t *testing.T) {
var buf Buffer
for i := 3; i < 30; i += 3 {
- s := fillBytes(t, "TestReadFrom (1)", &buf, "", 5, bytes[0:len(bytes)/i])
+ s := fillBytes(t, "TestReadFrom (1)", &buf, "", 5, testBytes[0:len(testBytes)/i])
var b Buffer
b.ReadFrom(&buf)
empty(t, "TestReadFrom (2)", &b, s, make([]byte, len(data)))
@@ -250,16 +251,16 @@ func TestReadFrom(t *testing.T) {
func TestWriteTo(t *testing.T) {
var buf Buffer
for i := 3; i < 30; i += 3 {
- s := fillBytes(t, "TestReadFrom (1)", &buf, "", 5, bytes[0:len(bytes)/i])
+ s := fillBytes(t, "TestWriteTo (1)", &buf, "", 5, testBytes[0:len(testBytes)/i])
var b Buffer
buf.WriteTo(&b)
- empty(t, "TestReadFrom (2)", &b, s, make([]byte, len(data)))
+ empty(t, "TestWriteTo (2)", &b, s, make([]byte, len(data)))
}
}
func TestRuneIO(t *testing.T) {
const NRune = 1000
- // Built a test array while we write the data
+ // Built a test slice while we write the data
b := make([]byte, utf8.UTFMax*NRune)
var buf Buffer
n := 0
@@ -374,6 +375,72 @@ func TestReadBytes(t *testing.T) {
}
}
+func TestReadString(t *testing.T) {
+ for _, test := range readBytesTests {
+ buf := NewBufferString(test.buffer)
+ var err error
+ for _, expected := range test.expected {
+ var s string
+ s, err = buf.ReadString(test.delim)
+ if s != expected {
+ t.Errorf("expected %q, got %q", expected, s)
+ }
+ if err != nil {
+ break
+ }
+ }
+ if err != test.err {
+ t.Errorf("expected error %v, got %v", test.err, err)
+ }
+ }
+}
+
+func BenchmarkReadString(b *testing.B) {
+ const n = 32 << 10
+
+ data := make([]byte, n)
+ data[n-1] = 'x'
+ b.SetBytes(int64(n))
+ for i := 0; i < b.N; i++ {
+ buf := NewBuffer(data)
+ _, err := buf.ReadString('x')
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func TestGrow(t *testing.T) {
+ x := []byte{'x'}
+ y := []byte{'y'}
+ tmp := make([]byte, 72)
+ for _, startLen := range []int{0, 100, 1000, 10000, 100000} {
+ xBytes := Repeat(x, startLen)
+ for _, growLen := range []int{0, 100, 1000, 10000, 100000} {
+ buf := NewBuffer(xBytes)
+ // If we read, this affects buf.off, which is good to test.
+ readBytes, _ := buf.Read(tmp)
+ buf.Grow(growLen)
+ yBytes := Repeat(y, growLen)
+ // Check no allocation occurs in write, as long as we're single-threaded.
+ var m1, m2 runtime.MemStats
+ runtime.ReadMemStats(&m1)
+ buf.Write(yBytes)
+ runtime.ReadMemStats(&m2)
+ if runtime.GOMAXPROCS(-1) == 1 && m1.Mallocs != m2.Mallocs {
+ t.Errorf("allocation occurred during write")
+ }
+ // Check that buffer has correct data.
+ if !Equal(buf.Bytes()[0:startLen-readBytes], xBytes[readBytes:]) {
+ t.Errorf("bad initial data at %d %d", startLen, growLen)
+ }
+ if !Equal(buf.Bytes()[startLen-readBytes:startLen-readBytes+growLen], yBytes) {
+ t.Errorf("bad written data at %d %d", startLen, growLen)
+ }
+ }
+ }
+}
+
// Was a bug: used to give EOF reading empty slice at EOF.
func TestReadEmptyAtEOF(t *testing.T) {
b := new(Buffer)
@@ -386,3 +453,25 @@ func TestReadEmptyAtEOF(t *testing.T) {
t.Errorf("wrong count; got %d want 0", n)
}
}
+
+func TestUnreadByte(t *testing.T) {
+ b := new(Buffer)
+ b.WriteString("abcdefghijklmnopqrstuvwxyz")
+
+ _, err := b.ReadBytes('m')
+ if err != nil {
+ t.Fatalf("ReadBytes: %v", err)
+ }
+
+ err = b.UnreadByte()
+ if err != nil {
+ t.Fatalf("UnreadByte: %v", err)
+ }
+ c, err := b.ReadByte()
+ if err != nil {
+ t.Fatalf("ReadByte: %v", err)
+ }
+ if c != 'm' {
+ t.Errorf("ReadByte = %q; want %q", c, 'm')
+ }
+}
diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go
index 7d1426fb4..3bab65ef9 100644
--- a/src/pkg/bytes/bytes.go
+++ b/src/pkg/bytes/bytes.go
@@ -11,8 +11,8 @@ import (
"unicode/utf8"
)
-// Compare returns an integer comparing the two byte arrays lexicographically.
-// The result will be 0 if a==b, -1 if a < b, and +1 if a > b
+// Compare returns an integer comparing two byte slices lexicographically.
+// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
// A nil argument is equivalent to an empty slice.
func Compare(a, b []byte) int {
m := len(a)
@@ -37,10 +37,6 @@ func Compare(a, b []byte) int {
return 0
}
-// Equal returns a boolean reporting whether a == b.
-// A nil argument is equivalent to an empty slice.
-func Equal(a, b []byte) bool
-
func equalPortable(a, b []byte) bool {
if len(a) != len(b) {
return false
@@ -53,8 +49,8 @@ func equalPortable(a, b []byte) bool {
return true
}
-// explode splits s into an array of UTF-8 sequences, one per Unicode character (still arrays of bytes),
-// up to a maximum of n byte arrays. Invalid UTF-8 sequences are chopped into individual bytes.
+// explode splits s into a slice of UTF-8 sequences, one per Unicode character (still slices of bytes),
+// up to a maximum of n byte slices. Invalid UTF-8 sequences are chopped into individual bytes.
func explode(s []byte, n int) [][]byte {
if n <= 0 {
n = len(s)
@@ -226,7 +222,7 @@ func LastIndexAny(s []byte, chars string) int {
}
// Generic split: splits after each instance of sep,
-// including sepSave bytes of sep in the subarrays.
+// including sepSave bytes of sep in the subslices.
func genSplit(s, sep []byte, sepSave, n int) [][]byte {
if n == 0 {
return nil
@@ -287,15 +283,15 @@ func SplitAfter(s, sep []byte) [][]byte {
return genSplit(s, sep, len(sep), -1)
}
-// Fields splits the array s around each instance of one or more consecutive white space
-// characters, returning a slice of subarrays of s or an empty list if s contains only white space.
+// Fields splits the slice s around each instance of one or more consecutive white space
+// characters, returning a slice of subslices of s or an empty list if s contains only white space.
func Fields(s []byte) [][]byte {
return FieldsFunc(s, unicode.IsSpace)
}
// FieldsFunc interprets s as a sequence of UTF-8-encoded Unicode code points.
-// It splits the array s at each run of code points c satisfying f(c) and
-// returns a slice of subarrays of s. If no code points in s satisfy f(c), an
+// It splits the slice s at each run of code points c satisfying f(c) and
+// returns a slice of subslices of s. If no code points in s satisfy f(c), an
// empty slice is returned.
func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
n := 0
@@ -333,45 +329,46 @@ func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
return a[0:na]
}
-// Join concatenates the elements of a to create a single byte array. The separator
-// sep is placed between elements in the resulting array.
-func Join(a [][]byte, sep []byte) []byte {
- if len(a) == 0 {
+// Join concatenates the elements of s to create a new byte slice. The separator
+// sep is placed between elements in the resulting slice.
+func Join(s [][]byte, sep []byte) []byte {
+ if len(s) == 0 {
return []byte{}
}
- if len(a) == 1 {
- return a[0]
+ if len(s) == 1 {
+ // Just return a copy.
+ return append([]byte(nil), s[0]...)
}
- n := len(sep) * (len(a) - 1)
- for i := 0; i < len(a); i++ {
- n += len(a[i])
+ n := len(sep) * (len(s) - 1)
+ for _, v := range s {
+ n += len(v)
}
b := make([]byte, n)
- bp := copy(b, a[0])
- for _, s := range a[1:] {
+ bp := copy(b, s[0])
+ for _, v := range s[1:] {
bp += copy(b[bp:], sep)
- bp += copy(b[bp:], s)
+ bp += copy(b[bp:], v)
}
return b
}
-// HasPrefix tests whether the byte array s begins with prefix.
+// HasPrefix tests whether the byte slice s begins with prefix.
func HasPrefix(s, prefix []byte) bool {
return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix)
}
-// HasSuffix tests whether the byte array s ends with suffix.
+// HasSuffix tests whether the byte slice s ends with suffix.
func HasSuffix(s, suffix []byte) bool {
return len(s) >= len(suffix) && Equal(s[len(s)-len(suffix):], suffix)
}
-// Map returns a copy of the byte array s with all its characters modified
+// Map returns a copy of the byte slice s with all its characters modified
// according to the mapping function. If mapping returns a negative value, the character is
// dropped from the string with no replacement. The characters in s and the
// output are interpreted as UTF-8-encoded Unicode code points.
func Map(mapping func(r rune) rune, s []byte) []byte {
- // In the worst case, the array can grow when mapped, making
+ // In the worst case, the slice can grow when mapped, making
// things unpleasant. But it's so rare we barge in assuming it's
// fine. It could also shrink but that falls out naturally.
maxbytes := len(s) // length of b
@@ -412,28 +409,28 @@ func Repeat(b []byte, count int) []byte {
return nb
}
-// ToUpper returns a copy of the byte array s with all Unicode letters mapped to their upper case.
+// ToUpper returns a copy of the byte slice s with all Unicode letters mapped to their upper case.
func ToUpper(s []byte) []byte { return Map(unicode.ToUpper, s) }
-// ToUpper returns a copy of the byte array s with all Unicode letters mapped to their lower case.
+// ToLower returns a copy of the byte slice s with all Unicode letters mapped to their lower case.
func ToLower(s []byte) []byte { return Map(unicode.ToLower, s) }
-// ToTitle returns a copy of the byte array s with all Unicode letters mapped to their title case.
+// ToTitle returns a copy of the byte slice s with all Unicode letters mapped to their title case.
func ToTitle(s []byte) []byte { return Map(unicode.ToTitle, s) }
-// ToUpperSpecial returns a copy of the byte array s with all Unicode letters mapped to their
+// ToUpperSpecial returns a copy of the byte slice s with all Unicode letters mapped to their
// upper case, giving priority to the special casing rules.
func ToUpperSpecial(_case unicode.SpecialCase, s []byte) []byte {
return Map(func(r rune) rune { return _case.ToUpper(r) }, s)
}
-// ToLowerSpecial returns a copy of the byte array s with all Unicode letters mapped to their
+// ToLowerSpecial returns a copy of the byte slice s with all Unicode letters mapped to their
// lower case, giving priority to the special casing rules.
func ToLowerSpecial(_case unicode.SpecialCase, s []byte) []byte {
return Map(func(r rune) rune { return _case.ToLower(r) }, s)
}
-// ToTitleSpecial returns a copy of the byte array s with all Unicode letters mapped to their
+// ToTitleSpecial returns a copy of the byte slice s with all Unicode letters mapped to their
// title case, giving priority to the special casing rules.
func ToTitleSpecial(_case unicode.SpecialCase, s []byte) []byte {
return Map(func(r rune) rune { return _case.ToTitle(r) }, s)
@@ -514,6 +511,24 @@ func TrimFunc(s []byte, f func(r rune) bool) []byte {
return TrimRightFunc(TrimLeftFunc(s, f), f)
}
+// TrimPrefix returns s without the provided leading prefix string.
+// If s doesn't start with prefix, s is returned unchanged.
+func TrimPrefix(s, prefix []byte) []byte {
+ if HasPrefix(s, prefix) {
+ return s[len(prefix):]
+ }
+ return s
+}
+
+// TrimSuffix returns s without the provided trailing suffix string.
+// If s doesn't end with suffix, s is returned unchanged.
+func TrimSuffix(s, suffix []byte) []byte {
+ if HasSuffix(s, suffix) {
+ return s[:len(s)-len(suffix)]
+ }
+ return s
+}
+
// IndexFunc interprets s as a sequence of UTF-8-encoded Unicode code points.
// It returns the byte index in s of the first Unicode
// code point satisfying f(c), or -1 if none do.
@@ -552,7 +567,10 @@ func indexFunc(s []byte, f func(r rune) bool, truth bool) int {
// inverted.
func lastIndexFunc(s []byte, f func(r rune) bool, truth bool) int {
for i := len(s); i > 0; {
- r, size := utf8.DecodeLastRune(s[0:i])
+ r, size := rune(s[i-1]), 1
+ if r >= utf8.RuneSelf {
+ r, size = utf8.DecodeLastRune(s[0:i])
+ }
i -= size
if f(r) == truth {
return i
@@ -619,10 +637,8 @@ func Replace(s, old, new []byte, n int) []byte {
m = Count(s, old)
}
if m == 0 {
- // Nothing to do. Just copy.
- t := make([]byte, len(s))
- copy(t, s)
- return t
+ // Just return a copy.
+ return append([]byte(nil), s...)
}
if n < 0 || m < n {
n = m
diff --git a/src/pkg/bytes/bytes_decl.go b/src/pkg/bytes/bytes_decl.go
index 5d2b9e639..ce78be416 100644
--- a/src/pkg/bytes/bytes_decl.go
+++ b/src/pkg/bytes/bytes_decl.go
@@ -4,5 +4,13 @@
package bytes
+//go:noescape
+
// IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s.
func IndexByte(s []byte, c byte) int // asm_$GOARCH.s
+
+//go:noescape
+
+// Equal returns a boolean reporting whether a == b.
+// A nil argument is equivalent to an empty slice.
+func Equal(a, b []byte) bool // asm_$GOARCH.s
diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go
index 000f23517..1d6274c33 100644
--- a/src/pkg/bytes/bytes_test.go
+++ b/src/pkg/bytes/bytes_test.go
@@ -6,6 +6,7 @@ package bytes_test
import (
. "bytes"
+ "math/rand"
"reflect"
"testing"
"unicode"
@@ -24,16 +25,16 @@ func eq(a, b []string) bool {
return true
}
-func arrayOfString(a [][]byte) []string {
- result := make([]string, len(a))
- for j := 0; j < len(a); j++ {
- result[j] = string(a[j])
+func sliceOfString(s [][]byte) []string {
+ result := make([]string, len(s))
+ for i, v := range s {
+ result[i] = string(v)
}
return result
}
// For ease of reading, the test cases use strings that are converted to byte
-// arrays before invoking the functions.
+// slices before invoking the functions.
var abcd = "abcd"
var faces = "☺☻☹"
@@ -434,7 +435,7 @@ var explodetests = []ExplodeTest{
func TestExplode(t *testing.T) {
for _, tt := range explodetests {
a := SplitN([]byte(tt.s), nil, tt.n)
- result := arrayOfString(a)
+ result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf(`Explode("%s", %d) = %v; want %v`, tt.s, tt.n, result, tt.a)
continue
@@ -472,7 +473,7 @@ var splittests = []SplitTest{
func TestSplit(t *testing.T) {
for _, tt := range splittests {
a := SplitN([]byte(tt.s), []byte(tt.sep), tt.n)
- result := arrayOfString(a)
+ result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a)
continue
@@ -490,6 +491,12 @@ func TestSplit(t *testing.T) {
t.Errorf("Split disagrees withSplitN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a)
}
}
+ if len(a) > 0 {
+ in, out := a[0], s
+ if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] {
+ t.Errorf("Join(%#v, %q) didn't copy", a, tt.sep)
+ }
+ }
}
}
@@ -512,7 +519,7 @@ var splitaftertests = []SplitTest{
func TestSplitAfter(t *testing.T) {
for _, tt := range splitaftertests {
a := SplitAfterN([]byte(tt.s), []byte(tt.sep), tt.n)
- result := arrayOfString(a)
+ result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a)
continue
@@ -552,7 +559,7 @@ var fieldstests = []FieldsTest{
func TestFields(t *testing.T) {
for _, tt := range fieldstests {
a := Fields([]byte(tt.s))
- result := arrayOfString(a)
+ result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf("Fields(%q) = %v; want %v", tt.s, a, tt.a)
continue
@@ -561,6 +568,14 @@ func TestFields(t *testing.T) {
}
func TestFieldsFunc(t *testing.T) {
+ for _, tt := range fieldstests {
+ a := FieldsFunc([]byte(tt.s), unicode.IsSpace)
+ result := sliceOfString(a)
+ if !eq(result, tt.a) {
+ t.Errorf("FieldsFunc(%q, unicode.IsSpace) = %v; want %v", tt.s, a, tt.a)
+ continue
+ }
+ }
pred := func(c rune) bool { return c == 'X' }
var fieldsFuncTests = []FieldsTest{
{"", []string{}},
@@ -570,15 +585,15 @@ func TestFieldsFunc(t *testing.T) {
}
for _, tt := range fieldsFuncTests {
a := FieldsFunc([]byte(tt.s), pred)
- result := arrayOfString(a)
+ result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf("FieldsFunc(%q) = %v, want %v", tt.s, a, tt.a)
}
}
}
-// Test case for any function which accepts and returns a byte array.
-// For ease of creation, we write the byte arrays as strings.
+// Test case for any function which accepts and returns a byte slice.
+// For ease of creation, we write the byte slices as strings.
type StringTest struct {
in, out string
}
@@ -779,8 +794,8 @@ func TestRunes(t *testing.T) {
}
type TrimTest struct {
- f string
- in, cutset, out string
+ f string
+ in, arg, out string
}
var trimTests = []TrimTest{
@@ -805,12 +820,17 @@ var trimTests = []TrimTest{
{"TrimRight", "", "123", ""},
{"TrimRight", "", "", ""},
{"TrimRight", "☺\xc0", "☺", "☺\xc0"},
+ {"TrimPrefix", "aabb", "a", "abb"},
+ {"TrimPrefix", "aabb", "b", "aabb"},
+ {"TrimSuffix", "aabb", "a", "aabb"},
+ {"TrimSuffix", "aabb", "b", "aab"},
}
func TestTrim(t *testing.T) {
for _, tc := range trimTests {
name := tc.f
var f func([]byte, string) []byte
+ var fb func([]byte, []byte) []byte
switch name {
case "Trim":
f = Trim
@@ -818,12 +838,21 @@ func TestTrim(t *testing.T) {
f = TrimLeft
case "TrimRight":
f = TrimRight
+ case "TrimPrefix":
+ fb = TrimPrefix
+ case "TrimSuffix":
+ fb = TrimSuffix
default:
t.Errorf("Undefined trim function %s", name)
}
- actual := string(f([]byte(tc.in), tc.cutset))
+ var actual string
+ if f != nil {
+ actual = string(f([]byte(tc.in), tc.arg))
+ } else {
+ actual = string(fb([]byte(tc.in), []byte(tc.arg)))
+ }
if actual != tc.out {
- t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out)
+ t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out)
}
}
}
@@ -1008,3 +1037,46 @@ func TestEqualFold(t *testing.T) {
}
}
}
+
+var makeFieldsInput = func() []byte {
+ x := make([]byte, 1<<20)
+ // Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space.
+ for i := range x {
+ switch rand.Intn(10) {
+ case 0:
+ x[i] = ' '
+ case 1:
+ if i > 0 && x[i-1] == 'x' {
+ copy(x[i-1:], "χ")
+ break
+ }
+ fallthrough
+ default:
+ x[i] = 'x'
+ }
+ }
+ return x
+}
+
+var fieldsInput = makeFieldsInput()
+
+func BenchmarkFields(b *testing.B) {
+ b.SetBytes(int64(len(fieldsInput)))
+ for i := 0; i < b.N; i++ {
+ Fields(fieldsInput)
+ }
+}
+
+func BenchmarkFieldsFunc(b *testing.B) {
+ b.SetBytes(int64(len(fieldsInput)))
+ for i := 0; i < b.N; i++ {
+ FieldsFunc(fieldsInput, unicode.IsSpace)
+ }
+}
+
+func BenchmarkTrimSpace(b *testing.B) {
+ s := []byte(" Some text. \n")
+ for i := 0; i < b.N; i++ {
+ TrimSpace(s)
+ }
+}
diff --git a/src/pkg/bytes/example_test.go b/src/pkg/bytes/example_test.go
index 6fe8cd5a9..ad2dbc69b 100644
--- a/src/pkg/bytes/example_test.go
+++ b/src/pkg/bytes/example_test.go
@@ -5,24 +5,81 @@
package bytes_test
import (
- . "bytes"
+ "bytes"
"encoding/base64"
+ "fmt"
"io"
"os"
+ "sort"
)
func ExampleBuffer() {
- var b Buffer // A Buffer needs no initialization.
+ var b bytes.Buffer // A Buffer needs no initialization.
b.Write([]byte("Hello "))
- b.Write([]byte("world!"))
+ fmt.Fprintf(&b, "world!")
b.WriteTo(os.Stdout)
// Output: Hello world!
}
func ExampleBuffer_reader() {
// A Buffer can turn a string or a []byte into an io.Reader.
- buf := NewBufferString("R29waGVycyBydWxlIQ==")
+ buf := bytes.NewBufferString("R29waGVycyBydWxlIQ==")
dec := base64.NewDecoder(base64.StdEncoding, buf)
io.Copy(os.Stdout, dec)
// Output: Gophers rule!
}
+
+func ExampleCompare() {
+ // Interpret Compare's result by comparing it to zero.
+ var a, b []byte
+ if bytes.Compare(a, b) < 0 {
+ // a less b
+ }
+ if bytes.Compare(a, b) <= 0 {
+ // a less or equal b
+ }
+ if bytes.Compare(a, b) > 0 {
+ // a greater b
+ }
+ if bytes.Compare(a, b) >= 0 {
+ // a greater or equal b
+ }
+
+ // Prefer Equal to Compare for equality comparisons.
+ if bytes.Equal(a, b) {
+ // a equal b
+ }
+ if !bytes.Equal(a, b) {
+ // a not equal b
+ }
+}
+
+func ExampleCompare_search() {
+ // Binary search to find a matching byte slice.
+ var needle []byte
+ var haystack [][]byte // Assume sorted
+ i := sort.Search(len(haystack), func(i int) bool {
+ // Return haystack[i] >= needle.
+ return bytes.Compare(haystack[i], needle) >= 0
+ })
+ if i < len(haystack) && bytes.Equal(haystack[i], needle) {
+ // Found it!
+ }
+}
+
+func ExampleTrimSuffix() {
+ var b = []byte("Hello, goodbye, etc!")
+ b = bytes.TrimSuffix(b, []byte("goodbye, etc!"))
+ b = bytes.TrimSuffix(b, []byte("gopher"))
+ b = append(b, bytes.TrimSuffix([]byte("world!"), []byte("x!"))...)
+ os.Stdout.Write(b)
+ // Output: Hello, world!
+}
+
+func ExampleTrimPrefix() {
+ var b = []byte("Goodbye,, world!")
+ b = bytes.TrimPrefix(b, []byte("Goodbye,"))
+ b = bytes.TrimPrefix(b, []byte("See ya,"))
+ fmt.Printf("Hello%s", b)
+ // Output: Hello, world!
+}
diff --git a/src/pkg/bytes/reader.go b/src/pkg/bytes/reader.go
index a062e54ba..77511b945 100644
--- a/src/pkg/bytes/reader.go
+++ b/src/pkg/bytes/reader.go
@@ -10,7 +10,7 @@ import (
"unicode/utf8"
)
-// A Reader implements the io.Reader, io.ReaderAt, io.Seeker,
+// A Reader implements the io.Reader, io.ReaderAt, io.WriterTo, io.Seeker,
// io.ByteScanner, and io.RuneScanner interfaces by reading from
// a byte slice.
// Unlike a Buffer, a Reader is read-only and supports seeking.
@@ -121,5 +121,24 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {
return abs, nil
}
+// WriteTo implements the io.WriterTo interface.
+func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
+ r.prevRune = -1
+ if r.i >= len(r.s) {
+ return 0, nil
+ }
+ b := r.s[r.i:]
+ m, err := w.Write(b)
+ if m > len(b) {
+ panic("bytes.Reader.WriteTo: invalid Write count")
+ }
+ r.i += m
+ n = int64(m)
+ if m != len(b) && err == nil {
+ err = io.ErrShortWrite
+ }
+ return
+}
+
// NewReader returns a new Reader reading from b.
func NewReader(b []byte) *Reader { return &Reader{b, 0, -1} }
diff --git a/src/pkg/bytes/reader_test.go b/src/pkg/bytes/reader_test.go
index 2e4b1f26e..f0a3e26c4 100644
--- a/src/pkg/bytes/reader_test.go
+++ b/src/pkg/bytes/reader_test.go
@@ -8,6 +8,7 @@ import (
. "bytes"
"fmt"
"io"
+ "io/ioutil"
"os"
"testing"
)
@@ -86,3 +87,51 @@ func TestReaderAt(t *testing.T) {
}
}
}
+
+func TestReaderWriteTo(t *testing.T) {
+ for i := 0; i < 30; i += 3 {
+ var l int
+ if i > 0 {
+ l = len(data) / i
+ }
+ s := data[:l]
+ r := NewReader(testBytes[:l])
+ var b Buffer
+ n, err := r.WriteTo(&b)
+ if expect := int64(len(s)); n != expect {
+ t.Errorf("got %v; want %v", n, expect)
+ }
+ if err != nil {
+ t.Errorf("for length %d: got error = %v; want nil", l, err)
+ }
+ if b.String() != s {
+ t.Errorf("got string %q; want %q", b.String(), s)
+ }
+ if r.Len() != 0 {
+ t.Errorf("reader contains %v bytes; want 0", r.Len())
+ }
+ }
+}
+
+// verify that copying from an empty reader always has the same results,
+// regardless of the presence of a WriteTo method.
+func TestReaderCopyNothing(t *testing.T) {
+ type nErr struct {
+ n int64
+ err error
+ }
+ type justReader struct {
+ io.Reader
+ }
+ type justWriter struct {
+ io.Writer
+ }
+ discard := justWriter{ioutil.Discard} // hide ReadFrom
+
+ var with, withOut nErr
+ with.n, with.err = io.Copy(discard, NewReader(nil))
+ withOut.n, withOut.err = io.Copy(discard, justReader{NewReader(nil)})
+ if with != withOut {
+ t.Errorf("behavior differs: with = %#v; without: %#v", with, withOut)
+ }
+}
diff --git a/src/pkg/compress/bzip2/bit_reader.go b/src/pkg/compress/bzip2/bit_reader.go
index b35c69a1c..ab1d60651 100644
--- a/src/pkg/compress/bzip2/bit_reader.go
+++ b/src/pkg/compress/bzip2/bit_reader.go
@@ -14,21 +14,16 @@ import (
// because the error handling was verbose. Instead, any error is kept and can
// be checked afterwards.
type bitReader struct {
- r byteReader
+ r io.ByteReader
n uint64
bits uint
err error
}
-// bitReader needs to read bytes from an io.Reader. We attempt to convert the
-// given io.Reader to this interface and, if it doesn't already fit, we wrap in
-// a bufio.Reader.
-type byteReader interface {
- ReadByte() (byte, error)
-}
-
+// newBitReader returns a new bitReader reading from r. If r is not
+// already an io.ByteReader, it will be converted via a bufio.Reader.
func newBitReader(r io.Reader) bitReader {
- byter, ok := r.(byteReader)
+ byter, ok := r.(io.ByteReader)
if !ok {
byter = bufio.NewReader(r)
}
diff --git a/src/pkg/compress/flate/copy.go b/src/pkg/compress/flate/copy.go
new file mode 100644
index 000000000..06e5d2e66
--- /dev/null
+++ b/src/pkg/compress/flate/copy.go
@@ -0,0 +1,17 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flate
+
+// forwardCopy is like the built-in copy function except that it always goes
+// forward from the start, even if the dst and src overlap.
+func forwardCopy(dst, src []byte) int {
+ if len(src) > len(dst) {
+ src = src[:len(dst)]
+ }
+ for i, x := range src {
+ dst[i] = x
+ }
+ return len(src)
+}
diff --git a/src/pkg/compress/flate/copy_test.go b/src/pkg/compress/flate/copy_test.go
new file mode 100644
index 000000000..a9281d446
--- /dev/null
+++ b/src/pkg/compress/flate/copy_test.go
@@ -0,0 +1,52 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flate
+
+import (
+ "testing"
+)
+
+func TestForwardCopy(t *testing.T) {
+ testCases := []struct {
+ dst0, dst1 int
+ src0, src1 int
+ want string
+ }{
+ {0, 9, 0, 9, "012345678"},
+ {0, 5, 4, 9, "45678"},
+ {4, 9, 0, 5, "01230"},
+ {1, 6, 3, 8, "34567"},
+ {3, 8, 1, 6, "12121"},
+ {0, 9, 3, 6, "345"},
+ {3, 6, 0, 9, "012"},
+ {1, 6, 0, 9, "00000"},
+ {0, 4, 7, 8, "7"},
+ {0, 1, 6, 8, "6"},
+ {4, 4, 6, 9, ""},
+ {2, 8, 6, 6, ""},
+ {0, 0, 0, 0, ""},
+ }
+ for _, tc := range testCases {
+ b := []byte("0123456789")
+ dst := b[tc.dst0:tc.dst1]
+ src := b[tc.src0:tc.src1]
+ n := forwardCopy(dst, src)
+ got := string(dst[:n])
+ if got != tc.want {
+ t.Errorf("dst=b[%d:%d], src=b[%d:%d]: got %q, want %q",
+ tc.dst0, tc.dst1, tc.src0, tc.src1, got, tc.want)
+ }
+ // Check that the bytes outside of dst[:n] were not modified.
+ for i, x := range b {
+ if i >= tc.dst0 && i < tc.dst0+n {
+ continue
+ }
+ if int(x) != '0'+i {
+ t.Errorf("dst=b[%d:%d], src=b[%d:%d]: copy overrun at b[%d]: got '%c', want '%c'",
+ tc.dst0, tc.dst1, tc.src0, tc.src1, i, x, '0'+i)
+ }
+ }
+ }
+}
diff --git a/src/pkg/compress/flate/deflate.go b/src/pkg/compress/flate/deflate.go
index e511b50fd..d357fe361 100644
--- a/src/pkg/compress/flate/deflate.go
+++ b/src/pkg/compress/flate/deflate.go
@@ -22,7 +22,7 @@ const (
logMaxOffsetSize = 15 // Standard DEFLATE
minMatchLength = 3 // The smallest match that the compressor looks for
maxMatchLength = 258 // The longest match for the compressor
- minOffsetSize = 1 // The shortest offset that makes any sence
+ minOffsetSize = 1 // The shortest offset that makes any sense
// The maximum number of tokens we put into a single flat block, just too
// stop things from getting too large.
diff --git a/src/pkg/compress/flate/deflate_test.go b/src/pkg/compress/flate/deflate_test.go
index f1e6db2ac..8f4e196b4 100644
--- a/src/pkg/compress/flate/deflate_test.go
+++ b/src/pkg/compress/flate/deflate_test.go
@@ -124,8 +124,7 @@ func (r *sparseReader) Read(b []byte) (n int, err error) {
func TestVeryLongSparseChunk(t *testing.T) {
if testing.Short() {
- t.Logf("skipping sparse chunk during short test")
- return
+ t.Skip("skipping sparse chunk during short test")
}
w, err := NewWriter(ioutil.Discard, 1)
if err != nil {
@@ -334,7 +333,7 @@ var deflateInflateStringTests = []deflateInflateStringTest{
{
"../testdata/e.txt",
"2.718281828...",
- [...]int{10013, 5065, 5096, 5115, 5093, 5079, 5079, 5079, 5079, 5079},
+ [...]int{100018, 50650, 50960, 51150, 50930, 50790, 50790, 50790, 50790, 50790},
},
{
"../testdata/Mark.Twain-Tom.Sawyer.txt",
diff --git a/src/pkg/compress/flate/fixedhuff.go b/src/pkg/compress/flate/fixedhuff.go
new file mode 100644
index 000000000..41a6b25df
--- /dev/null
+++ b/src/pkg/compress/flate/fixedhuff.go
@@ -0,0 +1,74 @@
+package flate
+
+// autogenerated by gen.go, DO NOT EDIT
+
+var fixedHuffmanDecoder = huffmanDecoder{
+ 7,
+ [huffmanNumChunks]uint32{
+ 0x1007, 0x0508, 0x0108, 0x1188, 0x1107, 0x0708, 0x0308, 0x0c09,
+ 0x1087, 0x0608, 0x0208, 0x0a09, 0x0008, 0x0808, 0x0408, 0x0e09,
+ 0x1047, 0x0588, 0x0188, 0x0909, 0x1147, 0x0788, 0x0388, 0x0d09,
+ 0x10c7, 0x0688, 0x0288, 0x0b09, 0x0088, 0x0888, 0x0488, 0x0f09,
+ 0x1027, 0x0548, 0x0148, 0x11c8, 0x1127, 0x0748, 0x0348, 0x0c89,
+ 0x10a7, 0x0648, 0x0248, 0x0a89, 0x0048, 0x0848, 0x0448, 0x0e89,
+ 0x1067, 0x05c8, 0x01c8, 0x0989, 0x1167, 0x07c8, 0x03c8, 0x0d89,
+ 0x10e7, 0x06c8, 0x02c8, 0x0b89, 0x00c8, 0x08c8, 0x04c8, 0x0f89,
+ 0x1017, 0x0528, 0x0128, 0x11a8, 0x1117, 0x0728, 0x0328, 0x0c49,
+ 0x1097, 0x0628, 0x0228, 0x0a49, 0x0028, 0x0828, 0x0428, 0x0e49,
+ 0x1057, 0x05a8, 0x01a8, 0x0949, 0x1157, 0x07a8, 0x03a8, 0x0d49,
+ 0x10d7, 0x06a8, 0x02a8, 0x0b49, 0x00a8, 0x08a8, 0x04a8, 0x0f49,
+ 0x1037, 0x0568, 0x0168, 0x11e8, 0x1137, 0x0768, 0x0368, 0x0cc9,
+ 0x10b7, 0x0668, 0x0268, 0x0ac9, 0x0068, 0x0868, 0x0468, 0x0ec9,
+ 0x1077, 0x05e8, 0x01e8, 0x09c9, 0x1177, 0x07e8, 0x03e8, 0x0dc9,
+ 0x10f7, 0x06e8, 0x02e8, 0x0bc9, 0x00e8, 0x08e8, 0x04e8, 0x0fc9,
+ 0x1007, 0x0518, 0x0118, 0x1198, 0x1107, 0x0718, 0x0318, 0x0c29,
+ 0x1087, 0x0618, 0x0218, 0x0a29, 0x0018, 0x0818, 0x0418, 0x0e29,
+ 0x1047, 0x0598, 0x0198, 0x0929, 0x1147, 0x0798, 0x0398, 0x0d29,
+ 0x10c7, 0x0698, 0x0298, 0x0b29, 0x0098, 0x0898, 0x0498, 0x0f29,
+ 0x1027, 0x0558, 0x0158, 0x11d8, 0x1127, 0x0758, 0x0358, 0x0ca9,
+ 0x10a7, 0x0658, 0x0258, 0x0aa9, 0x0058, 0x0858, 0x0458, 0x0ea9,
+ 0x1067, 0x05d8, 0x01d8, 0x09a9, 0x1167, 0x07d8, 0x03d8, 0x0da9,
+ 0x10e7, 0x06d8, 0x02d8, 0x0ba9, 0x00d8, 0x08d8, 0x04d8, 0x0fa9,
+ 0x1017, 0x0538, 0x0138, 0x11b8, 0x1117, 0x0738, 0x0338, 0x0c69,
+ 0x1097, 0x0638, 0x0238, 0x0a69, 0x0038, 0x0838, 0x0438, 0x0e69,
+ 0x1057, 0x05b8, 0x01b8, 0x0969, 0x1157, 0x07b8, 0x03b8, 0x0d69,
+ 0x10d7, 0x06b8, 0x02b8, 0x0b69, 0x00b8, 0x08b8, 0x04b8, 0x0f69,
+ 0x1037, 0x0578, 0x0178, 0x11f8, 0x1137, 0x0778, 0x0378, 0x0ce9,
+ 0x10b7, 0x0678, 0x0278, 0x0ae9, 0x0078, 0x0878, 0x0478, 0x0ee9,
+ 0x1077, 0x05f8, 0x01f8, 0x09e9, 0x1177, 0x07f8, 0x03f8, 0x0de9,
+ 0x10f7, 0x06f8, 0x02f8, 0x0be9, 0x00f8, 0x08f8, 0x04f8, 0x0fe9,
+ 0x1007, 0x0508, 0x0108, 0x1188, 0x1107, 0x0708, 0x0308, 0x0c19,
+ 0x1087, 0x0608, 0x0208, 0x0a19, 0x0008, 0x0808, 0x0408, 0x0e19,
+ 0x1047, 0x0588, 0x0188, 0x0919, 0x1147, 0x0788, 0x0388, 0x0d19,
+ 0x10c7, 0x0688, 0x0288, 0x0b19, 0x0088, 0x0888, 0x0488, 0x0f19,
+ 0x1027, 0x0548, 0x0148, 0x11c8, 0x1127, 0x0748, 0x0348, 0x0c99,
+ 0x10a7, 0x0648, 0x0248, 0x0a99, 0x0048, 0x0848, 0x0448, 0x0e99,
+ 0x1067, 0x05c8, 0x01c8, 0x0999, 0x1167, 0x07c8, 0x03c8, 0x0d99,
+ 0x10e7, 0x06c8, 0x02c8, 0x0b99, 0x00c8, 0x08c8, 0x04c8, 0x0f99,
+ 0x1017, 0x0528, 0x0128, 0x11a8, 0x1117, 0x0728, 0x0328, 0x0c59,
+ 0x1097, 0x0628, 0x0228, 0x0a59, 0x0028, 0x0828, 0x0428, 0x0e59,
+ 0x1057, 0x05a8, 0x01a8, 0x0959, 0x1157, 0x07a8, 0x03a8, 0x0d59,
+ 0x10d7, 0x06a8, 0x02a8, 0x0b59, 0x00a8, 0x08a8, 0x04a8, 0x0f59,
+ 0x1037, 0x0568, 0x0168, 0x11e8, 0x1137, 0x0768, 0x0368, 0x0cd9,
+ 0x10b7, 0x0668, 0x0268, 0x0ad9, 0x0068, 0x0868, 0x0468, 0x0ed9,
+ 0x1077, 0x05e8, 0x01e8, 0x09d9, 0x1177, 0x07e8, 0x03e8, 0x0dd9,
+ 0x10f7, 0x06e8, 0x02e8, 0x0bd9, 0x00e8, 0x08e8, 0x04e8, 0x0fd9,
+ 0x1007, 0x0518, 0x0118, 0x1198, 0x1107, 0x0718, 0x0318, 0x0c39,
+ 0x1087, 0x0618, 0x0218, 0x0a39, 0x0018, 0x0818, 0x0418, 0x0e39,
+ 0x1047, 0x0598, 0x0198, 0x0939, 0x1147, 0x0798, 0x0398, 0x0d39,
+ 0x10c7, 0x0698, 0x0298, 0x0b39, 0x0098, 0x0898, 0x0498, 0x0f39,
+ 0x1027, 0x0558, 0x0158, 0x11d8, 0x1127, 0x0758, 0x0358, 0x0cb9,
+ 0x10a7, 0x0658, 0x0258, 0x0ab9, 0x0058, 0x0858, 0x0458, 0x0eb9,
+ 0x1067, 0x05d8, 0x01d8, 0x09b9, 0x1167, 0x07d8, 0x03d8, 0x0db9,
+ 0x10e7, 0x06d8, 0x02d8, 0x0bb9, 0x00d8, 0x08d8, 0x04d8, 0x0fb9,
+ 0x1017, 0x0538, 0x0138, 0x11b8, 0x1117, 0x0738, 0x0338, 0x0c79,
+ 0x1097, 0x0638, 0x0238, 0x0a79, 0x0038, 0x0838, 0x0438, 0x0e79,
+ 0x1057, 0x05b8, 0x01b8, 0x0979, 0x1157, 0x07b8, 0x03b8, 0x0d79,
+ 0x10d7, 0x06b8, 0x02b8, 0x0b79, 0x00b8, 0x08b8, 0x04b8, 0x0f79,
+ 0x1037, 0x0578, 0x0178, 0x11f8, 0x1137, 0x0778, 0x0378, 0x0cf9,
+ 0x10b7, 0x0678, 0x0278, 0x0af9, 0x0078, 0x0878, 0x0478, 0x0ef9,
+ 0x1077, 0x05f8, 0x01f8, 0x09f9, 0x1177, 0x07f8, 0x03f8, 0x0df9,
+ 0x10f7, 0x06f8, 0x02f8, 0x0bf9, 0x00f8, 0x08f8, 0x04f8, 0x0ff9,
+ },
+ nil, 0,
+}
diff --git a/src/pkg/compress/flate/flate_test.go b/src/pkg/compress/flate/flate_test.go
index 94efc90ac..aba820a1f 100644
--- a/src/pkg/compress/flate/flate_test.go
+++ b/src/pkg/compress/flate/flate_test.go
@@ -10,122 +10,9 @@ package flate
import (
"bytes"
- "reflect"
"testing"
)
-// The Huffman code lengths used by the fixed-format Huffman blocks.
-var fixedHuffmanBits = [...]int{
- // 0-143 length 8
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
-
- // 144-255 length 9
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
-
- // 256-279 length 7
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7,
-
- // 280-287 length 8
- 8, 8, 8, 8, 8, 8, 8, 8,
-}
-
-type InitDecoderTest struct {
- in []int
- out huffmanDecoder
- ok bool
-}
-
-var initDecoderTests = []*InitDecoderTest{
- // Example from Connell 1973,
- {
- []int{3, 5, 2, 4, 3, 5, 5, 4, 4, 3, 4, 5},
- huffmanDecoder{
- 2, 5,
- [maxCodeLen + 1]int{2: 0, 4, 13, 31},
- [maxCodeLen + 1]int{2: 0, 1, 6, 20},
- // Paper used different code assignment:
- // 2, 9, 4, 0, 10, 8, 3, 7, 1, 5, 11, 6
- // Reordered here so that codes of same length
- // are assigned to increasing numbers.
- []int{2, 0, 4, 9, 3, 7, 8, 10, 1, 5, 6, 11},
- },
- true,
- },
-
- // Example from RFC 1951 section 3.2.2
- {
- []int{2, 1, 3, 3},
- huffmanDecoder{
- 1, 3,
- [maxCodeLen + 1]int{1: 0, 2, 7},
- [maxCodeLen + 1]int{1: 0, 1, 4},
- []int{1, 0, 2, 3},
- },
- true,
- },
-
- // Second example from RFC 1951 section 3.2.2
- {
- []int{3, 3, 3, 3, 3, 2, 4, 4},
- huffmanDecoder{
- 2, 4,
- [maxCodeLen + 1]int{2: 0, 6, 15},
- [maxCodeLen + 1]int{2: 0, 1, 8},
- []int{5, 0, 1, 2, 3, 4, 6, 7},
- },
- true,
- },
-
- // Static Huffman codes (RFC 1951 section 3.2.6)
- {
- fixedHuffmanBits[0:],
- fixedHuffmanDecoder,
- true,
- },
-
- // Illegal input.
- {
- []int{},
- huffmanDecoder{},
- false,
- },
-
- // Illegal input.
- {
- []int{0, 0, 0, 0, 0, 0, 0},
- huffmanDecoder{},
- false,
- },
-}
-
-func TestInitDecoder(t *testing.T) {
- for i, tt := range initDecoderTests {
- var h huffmanDecoder
- if h.init(tt.in) != tt.ok {
- t.Errorf("test %d: init = %v", i, !tt.ok)
- continue
- }
- if !reflect.DeepEqual(&h, &tt.out) {
- t.Errorf("test %d:\nhave %v\nwant %v", i, h, tt.out)
- }
- }
-}
-
func TestUncompressedSource(t *testing.T) {
decoder := NewReader(bytes.NewBuffer([]byte{0x01, 0x01, 0x00, 0xfe, 0xff, 0x11}))
output := make([]byte, 1)
diff --git a/src/pkg/compress/flate/gen.go b/src/pkg/compress/flate/gen.go
new file mode 100644
index 000000000..1427557f8
--- /dev/null
+++ b/src/pkg/compress/flate/gen.go
@@ -0,0 +1,165 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// This program generates fixedhuff.go
+// Invoke as
+//
+// go run gen.go |gofmt >fixedhuff.go
+
+package main
+
+import (
+ "fmt"
+)
+
+const maxCodeLen = 16
+
+// Note: the definition of the huffmanDecoder struct is copied from
+// inflate.go, as it is private to the implementation.
+
+// chunk & 15 is number of bits
+// chunk >> 4 is value, including table link
+
+const (
+ huffmanChunkBits = 9
+ huffmanNumChunks = 1 << huffmanChunkBits
+ huffmanCountMask = 15
+ huffmanValueShift = 4
+)
+
+type huffmanDecoder struct {
+ min int // the minimum code length
+ chunks [huffmanNumChunks]uint32 // chunks as described above
+ links [][]uint32 // overflow links
+ linkMask uint32 // mask the width of the link table
+}
+
+// Initialize Huffman decoding tables from array of code lengths.
+func (h *huffmanDecoder) init(bits []int) bool {
+ // Count number of codes of each length,
+ // compute min and max length.
+ var count [maxCodeLen]int
+ var min, max int
+ for _, n := range bits {
+ if n == 0 {
+ continue
+ }
+ if min == 0 || n < min {
+ min = n
+ }
+ if n > max {
+ max = n
+ }
+ count[n]++
+ }
+ if max == 0 {
+ return false
+ }
+
+ h.min = min
+ var linkBits uint
+ var numLinks int
+ if max > huffmanChunkBits {
+ linkBits = uint(max) - huffmanChunkBits
+ numLinks = 1 << linkBits
+ h.linkMask = uint32(numLinks - 1)
+ }
+ code := 0
+ var nextcode [maxCodeLen]int
+ for i := min; i <= max; i++ {
+ if i == huffmanChunkBits+1 {
+ // create link tables
+ link := code >> 1
+ h.links = make([][]uint32, huffmanNumChunks-link)
+ for j := uint(link); j < huffmanNumChunks; j++ {
+ reverse := int(reverseByte[j>>8]) | int(reverseByte[j&0xff])<<8
+ reverse >>= uint(16 - huffmanChunkBits)
+ off := j - uint(link)
+ h.chunks[reverse] = uint32(off<<huffmanValueShift + uint(i))
+ h.links[off] = make([]uint32, 1<<linkBits)
+ }
+ }
+ n := count[i]
+ nextcode[i] = code
+ code += n
+ code <<= 1
+ }
+
+ for i, n := range bits {
+ if n == 0 {
+ continue
+ }
+ code := nextcode[n]
+ nextcode[n]++
+ chunk := uint32(i<<huffmanValueShift | n)
+ reverse := int(reverseByte[code>>8]) | int(reverseByte[code&0xff])<<8
+ reverse >>= uint(16 - n)
+ if n <= huffmanChunkBits {
+ for off := reverse; off < huffmanNumChunks; off += 1 << uint(n) {
+ h.chunks[off] = chunk
+ }
+ } else {
+ linktab := h.links[h.chunks[reverse&(huffmanNumChunks-1)]>>huffmanValueShift]
+ reverse >>= huffmanChunkBits
+ for off := reverse; off < numLinks; off += 1 << uint(n-huffmanChunkBits) {
+ linktab[off] = chunk
+ }
+ }
+ }
+ return true
+}
+
+func main() {
+ var h huffmanDecoder
+ var bits [288]int
+ initReverseByte()
+ for i := 0; i < 144; i++ {
+ bits[i] = 8
+ }
+ for i := 144; i < 256; i++ {
+ bits[i] = 9
+ }
+ for i := 256; i < 280; i++ {
+ bits[i] = 7
+ }
+ for i := 280; i < 288; i++ {
+ bits[i] = 8
+ }
+ h.init(bits[:])
+ fmt.Println("package flate")
+ fmt.Println()
+ fmt.Println("// autogenerated by gen.go, DO NOT EDIT")
+ fmt.Println()
+ fmt.Println("var fixedHuffmanDecoder = huffmanDecoder{")
+ fmt.Printf("\t%d,\n", h.min)
+ fmt.Println("\t[huffmanNumChunks]uint32{")
+ for i := 0; i < huffmanNumChunks; i++ {
+ if i&7 == 0 {
+ fmt.Printf("\t\t")
+ } else {
+ fmt.Printf(" ")
+ }
+ fmt.Printf("0x%04x,", h.chunks[i])
+ if i&7 == 7 {
+ fmt.Println()
+ }
+ }
+ fmt.Println("\t},")
+ fmt.Println("\tnil, 0,")
+ fmt.Println("}")
+}
+
+var reverseByte [256]byte
+
+func initReverseByte() {
+ for x := 0; x < 256; x++ {
+ var result byte
+ for i := uint(0); i < 8; i++ {
+ result |= byte(((x >> i) & 1) << (7 - i))
+ }
+ reverseByte[x] = result
+ }
+}
diff --git a/src/pkg/compress/flate/inflate.go b/src/pkg/compress/flate/inflate.go
index 3f2042bfe..a8d646019 100644
--- a/src/pkg/compress/flate/inflate.go
+++ b/src/pkg/compress/flate/inflate.go
@@ -16,9 +16,10 @@ import (
const (
maxCodeLen = 16 // max length of Huffman code
maxHist = 32768 // max history required
- maxLit = 286
- maxDist = 32
- numCodes = 19 // number of codes in Huffman meta-code
+ // The next three numbers come from the RFC, section 3.2.7.
+ maxLit = 286
+ maxDist = 32
+ numCodes = 19 // number of codes in Huffman meta-code
)
// A CorruptInputError reports the presence of corrupt input at a given offset.
@@ -53,32 +54,46 @@ func (e *WriteError) Error() string {
return "flate: write error at offset " + strconv.FormatInt(e.Offset, 10) + ": " + e.Err.Error()
}
-// Huffman decoder is based on
-// J. Brian Connell, ``A Huffman-Shannon-Fano Code,''
-// Proceedings of the IEEE, 61(7) (July 1973), pp 1046-1047.
-type huffmanDecoder struct {
- // min, max code length
- min, max int
+// Note that much of the implemenation of huffmanDecoder is also copied
+// into gen.go (in package main) for the purpose of precomputing the
+// fixed huffman tables so they can be included statically.
+
+// The data structure for decoding Huffman tables is based on that of
+// zlib. There is a lookup table of a fixed bit width (huffmanChunkBits),
+// For codes smaller than the table width, there are multiple entries
+// (each combination of trailing bits has the same value). For codes
+// larger than the table width, the table contains a link to an overflow
+// table. The width of each entry in the link table is the maximum code
+// size minus the chunk width.
+
+// Note that you can do a lookup in the table even without all bits
+// filled. Since the extra bits are zero, and the DEFLATE Huffman codes
+// have the property that shorter codes come before longer ones, the
+// bit length estimate in the result is a lower bound on the actual
+// number of bits.
- // limit[i] = largest code word of length i
- // Given code v of length n,
- // need more bits if v > limit[n].
- limit [maxCodeLen + 1]int
+// chunk & 15 is number of bits
+// chunk >> 4 is value, including table link
- // base[i] = smallest code word of length i - seq number
- base [maxCodeLen + 1]int
+const (
+ huffmanChunkBits = 9
+ huffmanNumChunks = 1 << huffmanChunkBits
+ huffmanCountMask = 15
+ huffmanValueShift = 4
+)
- // codes[seq number] = output code.
- // Given code v of length n, value is
- // codes[v - base[n]].
- codes []int
+type huffmanDecoder struct {
+ min int // the minimum code length
+ chunks [huffmanNumChunks]uint32 // chunks as described above
+ links [][]uint32 // overflow links
+ linkMask uint32 // mask the width of the link table
}
// Initialize Huffman decoding tables from array of code lengths.
func (h *huffmanDecoder) init(bits []int) bool {
// Count number of codes of each length,
// compute min and max length.
- var count [maxCodeLen + 1]int
+ var count [maxCodeLen]int
var min, max int
for _, n := range bits {
if n == 0 {
@@ -97,93 +112,58 @@ func (h *huffmanDecoder) init(bits []int) bool {
}
h.min = min
- h.max = max
-
- // For each code range, compute
- // nextcode (first code of that length),
- // limit (last code of that length), and
- // base (offset from first code to sequence number).
+ var linkBits uint
+ var numLinks int
+ if max > huffmanChunkBits {
+ linkBits = uint(max) - huffmanChunkBits
+ numLinks = 1 << linkBits
+ h.linkMask = uint32(numLinks - 1)
+ }
code := 0
- seq := 0
var nextcode [maxCodeLen]int
for i := min; i <= max; i++ {
+ if i == huffmanChunkBits+1 {
+ // create link tables
+ link := code >> 1
+ h.links = make([][]uint32, huffmanNumChunks-link)
+ for j := uint(link); j < huffmanNumChunks; j++ {
+ reverse := int(reverseByte[j>>8]) | int(reverseByte[j&0xff])<<8
+ reverse >>= uint(16 - huffmanChunkBits)
+ off := j - uint(link)
+ h.chunks[reverse] = uint32(off<<huffmanValueShift + uint(i))
+ h.links[off] = make([]uint32, 1<<linkBits)
+ }
+ }
n := count[i]
nextcode[i] = code
- h.base[i] = code - seq
code += n
- seq += n
- h.limit[i] = code - 1
code <<= 1
}
- // Make array mapping sequence numbers to codes.
- if len(h.codes) < len(bits) {
- h.codes = make([]int, len(bits))
- }
for i, n := range bits {
if n == 0 {
continue
}
code := nextcode[n]
nextcode[n]++
- seq := code - h.base[n]
- h.codes[seq] = i
+ chunk := uint32(i<<huffmanValueShift | n)
+ reverse := int(reverseByte[code>>8]) | int(reverseByte[code&0xff])<<8
+ reverse >>= uint(16 - n)
+ if n <= huffmanChunkBits {
+ for off := reverse; off < huffmanNumChunks; off += 1 << uint(n) {
+ h.chunks[off] = chunk
+ }
+ } else {
+ linktab := h.links[h.chunks[reverse&(huffmanNumChunks-1)]>>huffmanValueShift]
+ reverse >>= huffmanChunkBits
+ for off := reverse; off < numLinks; off += 1 << uint(n-huffmanChunkBits) {
+ linktab[off] = chunk
+ }
+ }
}
return true
}
-// Hard-coded Huffman tables for DEFLATE algorithm.
-// See RFC 1951, section 3.2.6.
-var fixedHuffmanDecoder = huffmanDecoder{
- 7, 9,
- [maxCodeLen + 1]int{7: 23, 199, 511},
- [maxCodeLen + 1]int{7: 0, 24, 224},
- []int{
- // length 7: 256-279
- 256, 257, 258, 259, 260, 261, 262,
- 263, 264, 265, 266, 267, 268, 269,
- 270, 271, 272, 273, 274, 275, 276,
- 277, 278, 279,
-
- // length 8: 0-143
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
- 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
- 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
- 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
- 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
- 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
- 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
- 92, 93, 94, 95, 96, 97, 98, 99, 100,
- 101, 102, 103, 104, 105, 106, 107, 108,
- 109, 110, 111, 112, 113, 114, 115, 116,
- 117, 118, 119, 120, 121, 122, 123, 124,
- 125, 126, 127, 128, 129, 130, 131, 132,
- 133, 134, 135, 136, 137, 138, 139, 140,
- 141, 142, 143,
-
- // length 8: 280-287
- 280, 281, 282, 283, 284, 285, 286, 287,
-
- // length 9: 144-255
- 144, 145, 146, 147, 148, 149, 150, 151,
- 152, 153, 154, 155, 156, 157, 158, 159,
- 160, 161, 162, 163, 164, 165, 166, 167,
- 168, 169, 170, 171, 172, 173, 174, 175,
- 176, 177, 178, 179, 180, 181, 182, 183,
- 184, 185, 186, 187, 188, 189, 190, 191,
- 192, 193, 194, 195, 196, 197, 198, 199,
- 200, 201, 202, 203, 204, 205, 206, 207,
- 208, 209, 210, 211, 212, 213, 214, 215,
- 216, 217, 218, 219, 220, 221, 222, 223,
- 224, 225, 226, 227, 228, 229, 230, 231,
- 232, 233, 234, 235, 236, 237, 238, 239,
- 240, 241, 242, 243, 244, 245, 246, 247,
- 248, 249, 250, 251, 252, 253, 254, 255,
- },
-}
-
// The actual read interface needed by NewReader.
// If the passed in io.Reader does not also have ReadByte,
// the NewReader will introduce its own buffering.
@@ -207,11 +187,11 @@ type decompressor struct {
h1, h2 huffmanDecoder
// Length arrays used to define Huffman codes.
- bits [maxLit + maxDist]int
- codebits [numCodes]int
+ bits *[maxLit + maxDist]int
+ codebits *[numCodes]int
// Output history, buffer.
- hist [maxHist]byte
+ hist *[maxHist]byte
hp int // current output position in buffer
hw int // have written hist[0:hw] already
hfull bool // buffer has filled at least once
@@ -306,10 +286,15 @@ func (f *decompressor) readHuffman() error {
}
}
nlit := int(f.b&0x1F) + 257
+ if nlit > maxLit {
+ return CorruptInputError(f.roffset)
+ }
f.b >>= 5
ndist := int(f.b&0x1F) + 1
+ // maxDist is 32, so ndist is always valid.
f.b >>= 5
nclen := int(f.b&0xF) + 4
+ // numCodes is 19, so nclen is always valid.
f.b >>= 4
f.nb -= 5 + 5 + 4
@@ -505,51 +490,49 @@ func (f *decompressor) huffmanBlock() {
return
}
- p := f.hp - dist
- if p < 0 {
- p += len(f.hist)
- }
- for i := 0; i < length; i++ {
- f.hist[f.hp] = f.hist[p]
- f.hp++
- p++
- if f.hp == len(f.hist) {
- // After flush continue copying out of history.
- f.copyLen = length - (i + 1)
- f.copyDist = dist
- f.flush((*decompressor).copyHuff)
- return
- }
- if p == len(f.hist) {
- p = 0
- }
+ f.copyLen, f.copyDist = length, dist
+ if f.copyHist() {
+ return
}
}
panic("unreached")
}
-func (f *decompressor) copyHuff() {
- length := f.copyLen
- dist := f.copyDist
- p := f.hp - dist
+// copyHist copies f.copyLen bytes from f.hist (f.copyDist bytes ago) to itself.
+// It reports whether the f.hist buffer is full.
+func (f *decompressor) copyHist() bool {
+ p := f.hp - f.copyDist
if p < 0 {
p += len(f.hist)
}
- for i := 0; i < length; i++ {
- f.hist[f.hp] = f.hist[p]
- f.hp++
- p++
+ for f.copyLen > 0 {
+ n := f.copyLen
+ if x := len(f.hist) - f.hp; n > x {
+ n = x
+ }
+ if x := len(f.hist) - p; n > x {
+ n = x
+ }
+ forwardCopy(f.hist[f.hp:f.hp+n], f.hist[p:p+n])
+ p += n
+ f.hp += n
+ f.copyLen -= n
if f.hp == len(f.hist) {
- f.copyLen = length - (i + 1)
+ // After flush continue copying out of history.
f.flush((*decompressor).copyHuff)
- return
+ return true
}
if p == len(f.hist) {
p = 0
}
}
+ return false
+}
- // Continue processing Huffman block.
+func (f *decompressor) copyHuff() {
+ if f.copyHist() {
+ return
+ }
f.huffmanBlock()
}
@@ -584,9 +567,9 @@ func (f *decompressor) dataBlock() {
f.copyData()
}
+// copyData copies f.copyLen bytes from the underlying reader into f.hist.
+// It pauses for reads when f.hist is full.
func (f *decompressor) copyData() {
- // Read f.dataLen bytes into history,
- // pausing for reads as history fills.
n := f.copyLen
for n > 0 {
m := len(f.hist) - f.hp
@@ -640,23 +623,23 @@ func (f *decompressor) moreBits() error {
// Read the next Huffman-encoded symbol from f according to h.
func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) {
- for n := uint(h.min); n <= uint(h.max); n++ {
- lim := h.limit[n]
- if lim == -1 {
- continue
- }
+ n := uint(h.min)
+ for {
for f.nb < n {
if err := f.moreBits(); err != nil {
return 0, err
}
}
- v := int(f.b & uint32(1<<n-1))
- v <<= 16 - n
- v = int(reverseByte[v>>8]) | int(reverseByte[v&0xFF])<<8 // reverse bits
- if v <= lim {
+ chunk := h.chunks[f.b&(huffmanNumChunks-1)]
+ n = uint(chunk & huffmanCountMask)
+ if n > huffmanChunkBits {
+ chunk = h.links[chunk>>huffmanValueShift][(f.b>>huffmanChunkBits)&h.linkMask]
+ n = uint(chunk & huffmanCountMask)
+ }
+ if n <= f.nb {
f.b >>= n
f.nb -= n
- return h.codes[v-h.base[n]], nil
+ return int(chunk >> huffmanValueShift), nil
}
}
return 0, CorruptInputError(f.roffset)
@@ -688,7 +671,10 @@ func makeReader(r io.Reader) Reader {
// finished reading.
func NewReader(r io.Reader) io.ReadCloser {
var f decompressor
+ f.bits = new([maxLit + maxDist]int)
+ f.codebits = new([numCodes]int)
f.r = makeReader(r)
+ f.hist = new([maxHist]byte)
f.step = (*decompressor).nextBlock
return &f
}
@@ -700,8 +686,11 @@ func NewReader(r io.Reader) io.ReadCloser {
// to read data compressed by NewWriterDict.
func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser {
var f decompressor
- f.setDict(dict)
f.r = makeReader(r)
+ f.hist = new([maxHist]byte)
+ f.bits = new([maxLit + maxDist]int)
+ f.codebits = new([numCodes]int)
f.step = (*decompressor).nextBlock
+ f.setDict(dict)
return &f
}
diff --git a/src/pkg/compress/flate/reader_test.go b/src/pkg/compress/flate/reader_test.go
new file mode 100644
index 000000000..54ed788db
--- /dev/null
+++ b/src/pkg/compress/flate/reader_test.go
@@ -0,0 +1,95 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flate
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestNlitOutOfRange(t *testing.T) {
+ // Trying to decode this bogus flate data, which has a Huffman table
+ // with nlit=288, should not panic.
+ io.Copy(ioutil.Discard, NewReader(strings.NewReader(
+ "\xfc\xfe\x36\xe7\x5e\x1c\xef\xb3\x55\x58\x77\xb6\x56\xb5\x43\xf4"+
+ "\x6f\xf2\xd2\xe6\x3d\x99\xa0\x85\x8c\x48\xeb\xf8\xda\x83\x04\x2a"+
+ "\x75\xc4\xf8\x0f\x12\x11\xb9\xb4\x4b\x09\xa0\xbe\x8b\x91\x4c")))
+}
+
+const (
+ digits = iota
+ twain
+)
+
+var testfiles = []string{
+ // Digits is the digits of the irrational number e. Its decimal representation
+ // does not repeat, but there are only 10 posible digits, so it should be
+ // reasonably compressible.
+ digits: "../testdata/e.txt",
+ // Twain is Project Gutenberg's edition of Mark Twain's classic English novel.
+ twain: "../testdata/Mark.Twain-Tom.Sawyer.txt",
+}
+
+func benchmarkDecode(b *testing.B, testfile, level, n int) {
+ b.StopTimer()
+ b.SetBytes(int64(n))
+ buf0, err := ioutil.ReadFile(testfiles[testfile])
+ if err != nil {
+ b.Fatal(err)
+ }
+ if len(buf0) == 0 {
+ b.Fatalf("test file %q has no data", testfiles[testfile])
+ }
+ compressed := new(bytes.Buffer)
+ w, err := NewWriter(compressed, level)
+ if err != nil {
+ b.Fatal(err)
+ }
+ for i := 0; i < n; i += len(buf0) {
+ if len(buf0) > n-i {
+ buf0 = buf0[:n-i]
+ }
+ io.Copy(w, bytes.NewBuffer(buf0))
+ }
+ w.Close()
+ buf1 := compressed.Bytes()
+ buf0, compressed, w = nil, nil, nil
+ runtime.GC()
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ io.Copy(ioutil.Discard, NewReader(bytes.NewBuffer(buf1)))
+ }
+}
+
+// These short names are so that gofmt doesn't break the BenchmarkXxx function
+// bodies below over multiple lines.
+const (
+ speed = BestSpeed
+ default_ = DefaultCompression
+ compress = BestCompression
+)
+
+func BenchmarkDecodeDigitsSpeed1e4(b *testing.B) { benchmarkDecode(b, digits, speed, 1e4) }
+func BenchmarkDecodeDigitsSpeed1e5(b *testing.B) { benchmarkDecode(b, digits, speed, 1e5) }
+func BenchmarkDecodeDigitsSpeed1e6(b *testing.B) { benchmarkDecode(b, digits, speed, 1e6) }
+func BenchmarkDecodeDigitsDefault1e4(b *testing.B) { benchmarkDecode(b, digits, default_, 1e4) }
+func BenchmarkDecodeDigitsDefault1e5(b *testing.B) { benchmarkDecode(b, digits, default_, 1e5) }
+func BenchmarkDecodeDigitsDefault1e6(b *testing.B) { benchmarkDecode(b, digits, default_, 1e6) }
+func BenchmarkDecodeDigitsCompress1e4(b *testing.B) { benchmarkDecode(b, digits, compress, 1e4) }
+func BenchmarkDecodeDigitsCompress1e5(b *testing.B) { benchmarkDecode(b, digits, compress, 1e5) }
+func BenchmarkDecodeDigitsCompress1e6(b *testing.B) { benchmarkDecode(b, digits, compress, 1e6) }
+func BenchmarkDecodeTwainSpeed1e4(b *testing.B) { benchmarkDecode(b, twain, speed, 1e4) }
+func BenchmarkDecodeTwainSpeed1e5(b *testing.B) { benchmarkDecode(b, twain, speed, 1e5) }
+func BenchmarkDecodeTwainSpeed1e6(b *testing.B) { benchmarkDecode(b, twain, speed, 1e6) }
+func BenchmarkDecodeTwainDefault1e4(b *testing.B) { benchmarkDecode(b, twain, default_, 1e4) }
+func BenchmarkDecodeTwainDefault1e5(b *testing.B) { benchmarkDecode(b, twain, default_, 1e5) }
+func BenchmarkDecodeTwainDefault1e6(b *testing.B) { benchmarkDecode(b, twain, default_, 1e6) }
+func BenchmarkDecodeTwainCompress1e4(b *testing.B) { benchmarkDecode(b, twain, compress, 1e4) }
+func BenchmarkDecodeTwainCompress1e5(b *testing.B) { benchmarkDecode(b, twain, compress, 1e5) }
+func BenchmarkDecodeTwainCompress1e6(b *testing.B) { benchmarkDecode(b, twain, compress, 1e6) }
diff --git a/src/pkg/compress/flate/writer_test.go b/src/pkg/compress/flate/writer_test.go
new file mode 100644
index 000000000..58431774e
--- /dev/null
+++ b/src/pkg/compress/flate/writer_test.go
@@ -0,0 +1,60 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flate
+
+import (
+ "io/ioutil"
+ "runtime"
+ "testing"
+)
+
+func benchmarkEncoder(b *testing.B, testfile, level, n int) {
+ b.StopTimer()
+ b.SetBytes(int64(n))
+ buf0, err := ioutil.ReadFile(testfiles[testfile])
+ if err != nil {
+ b.Fatal(err)
+ }
+ if len(buf0) == 0 {
+ b.Fatalf("test file %q has no data", testfiles[testfile])
+ }
+ buf1 := make([]byte, n)
+ for i := 0; i < n; i += len(buf0) {
+ if len(buf0) > n-i {
+ buf0 = buf0[:n-i]
+ }
+ copy(buf1[i:], buf0)
+ }
+ buf0 = nil
+ runtime.GC()
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ w, err := NewWriter(ioutil.Discard, level)
+ if err != nil {
+ b.Fatal(err)
+ }
+ w.Write(buf1)
+ w.Close()
+ }
+}
+
+func BenchmarkEncodeDigitsSpeed1e4(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e4) }
+func BenchmarkEncodeDigitsSpeed1e5(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e5) }
+func BenchmarkEncodeDigitsSpeed1e6(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e6) }
+func BenchmarkEncodeDigitsDefault1e4(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e4) }
+func BenchmarkEncodeDigitsDefault1e5(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e5) }
+func BenchmarkEncodeDigitsDefault1e6(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e6) }
+func BenchmarkEncodeDigitsCompress1e4(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e4) }
+func BenchmarkEncodeDigitsCompress1e5(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e5) }
+func BenchmarkEncodeDigitsCompress1e6(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e6) }
+func BenchmarkEncodeTwainSpeed1e4(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e4) }
+func BenchmarkEncodeTwainSpeed1e5(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e5) }
+func BenchmarkEncodeTwainSpeed1e6(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e6) }
+func BenchmarkEncodeTwainDefault1e4(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e4) }
+func BenchmarkEncodeTwainDefault1e5(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e5) }
+func BenchmarkEncodeTwainDefault1e6(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e6) }
+func BenchmarkEncodeTwainCompress1e4(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e4) }
+func BenchmarkEncodeTwainCompress1e5(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e5) }
+func BenchmarkEncodeTwainCompress1e6(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e6) }
diff --git a/src/pkg/compress/lzw/reader_test.go b/src/pkg/compress/lzw/reader_test.go
index e5be12f54..6f155b1bd 100644
--- a/src/pkg/compress/lzw/reader_test.go
+++ b/src/pkg/compress/lzw/reader_test.go
@@ -114,11 +114,19 @@ func TestReader(t *testing.T) {
func benchmarkDecoder(b *testing.B, n int) {
b.StopTimer()
b.SetBytes(int64(n))
- buf0, _ := ioutil.ReadFile("../testdata/e.txt")
- buf0 = buf0[:10000]
+ buf0, err := ioutil.ReadFile("../testdata/e.txt")
+ if err != nil {
+ b.Fatal(err)
+ }
+ if len(buf0) == 0 {
+ b.Fatalf("test file has no data")
+ }
compressed := new(bytes.Buffer)
w := NewWriter(compressed, LSB, 8)
for i := 0; i < n; i += len(buf0) {
+ if len(buf0) > n-i {
+ buf0 = buf0[:n-i]
+ }
io.Copy(w, bytes.NewBuffer(buf0))
}
w.Close()
diff --git a/src/pkg/compress/lzw/writer.go b/src/pkg/compress/lzw/writer.go
index 488ba6428..b20691864 100644
--- a/src/pkg/compress/lzw/writer.go
+++ b/src/pkg/compress/lzw/writer.go
@@ -13,7 +13,7 @@ import (
// A writer is a buffered, flushable writer.
type writer interface {
- WriteByte(byte) error
+ io.ByteWriter
Flush() error
}
@@ -131,13 +131,14 @@ func (e *encoder) incHi() error {
}
// Write writes a compressed representation of p to e's underlying writer.
-func (e *encoder) Write(p []byte) (int, error) {
+func (e *encoder) Write(p []byte) (n int, err error) {
if e.err != nil {
return 0, e.err
}
if len(p) == 0 {
return 0, nil
}
+ n = len(p)
litMask := uint32(1<<e.litWidth - 1)
code := e.savedCode
if code == invalidCode {
@@ -167,11 +168,11 @@ loop:
code = literal
// Increment e.hi, the next implied code. If we run out of codes, reset
// the encoder state (including clearing the hash table) and continue.
- if err := e.incHi(); err != nil {
- if err == errOutOfCodes {
+ if err1 := e.incHi(); err1 != nil {
+ if err1 == errOutOfCodes {
continue
}
- e.err = err
+ e.err = err1
return 0, e.err
}
// Otherwise, insert key -> e.hi into the map that e.table represents.
@@ -184,7 +185,7 @@ loop:
}
}
e.savedCode = code
- return len(p), nil
+ return n, nil
}
// Close closes the encoder, flushing any pending output. It does not close or
diff --git a/src/pkg/compress/lzw/writer_test.go b/src/pkg/compress/lzw/writer_test.go
index d249a09b2..3e4e6de21 100644
--- a/src/pkg/compress/lzw/writer_test.go
+++ b/src/pkg/compress/lzw/writer_test.go
@@ -96,13 +96,29 @@ func TestWriter(t *testing.T) {
}
}
+func TestWriterReturnValues(t *testing.T) {
+ w := NewWriter(ioutil.Discard, LSB, 8)
+ n, err := w.Write([]byte("asdf"))
+ if n != 4 || err != nil {
+ t.Errorf("got %d, %v, want 4, nil", n, err)
+ }
+}
+
func benchmarkEncoder(b *testing.B, n int) {
b.StopTimer()
b.SetBytes(int64(n))
- buf0, _ := ioutil.ReadFile("../testdata/e.txt")
- buf0 = buf0[:10000]
+ buf0, err := ioutil.ReadFile("../testdata/e.txt")
+ if err != nil {
+ b.Fatal(err)
+ }
+ if len(buf0) == 0 {
+ b.Fatalf("test file has no data")
+ }
buf1 := make([]byte, n)
for i := 0; i < n; i += len(buf0) {
+ if len(buf0) > n-i {
+ buf0 = buf0[:n-i]
+ }
copy(buf1[i:], buf0)
}
buf0 = nil
diff --git a/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt b/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt
index 8d0ff4e65..c97da7ecc 100644
--- a/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt
+++ b/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt
@@ -1,4 +1,4 @@
-The Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete
+The Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete
by Mark Twain (Samuel Clemens)
This eBook is for the use of anyone anywhere at no cost and with
diff --git a/src/pkg/compress/testdata/e.txt b/src/pkg/compress/testdata/e.txt
index 76cf2a7b6..5ca186f14 100644
--- a/src/pkg/compress/testdata/e.txt
+++ b/src/pkg/compress/testdata/e.txt
@@ -1 +1 @@
-2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517027618386062613313845830007520449338265602976067371132007093287091274437470472306969772093101416928368190255151086574637721112523897844250569536967707854499699679468644549059879316368892300987931277361782154249992295763514822082698951936680331825288693984964651058209392398294887933203625094431173012381970684161403970198376793206832823764648042953118023287825098194558153017567173613320698112509961818815930416903515988885193458072738667385894228792284998920868058257492796104841984443634632449684875602336248270419786232090021609902353043699418491463140934317381436405462531520961836908887070167683964243781405927145635490613031072085103837505101157477041718986106873969655212671546889570350354021234078498193343210681701210056278802351930332247450158539047304199577770935036604169973297250886876966403555707162268447162560798826517871341951246652010305921236677194325278675398558944896970964097545918569563802363701621120477427228364896134225164450781824423529486363721417402388934412479635743702637552944483379980161254922785092577825620926226483262779333865664816277251640191059004916449982893150566047258027786318641551956532442586982946959308019152987211725563475463964479101459040905862984967912874068705048958586717479854667757573205681288459205413340539220001137863009455606881667400169842055804033637953764520304024322566135278369511778838638744396625322498506549958862342818997077332761717839280349465014345588970719425863987727547109629537415211151368350627526023264847287039207643100595841166120545297030236472549296669381151373227536450988890313602057248176585118063036442812314965507047510254465011727211555194866850800368532281831521960037356252794495158284188294787610852639813955990067376482922443752871846245780361929819713991475644882626039033814418232625150974827987779964373089970388867782271383605772978824125611907176639465070633045279546618550966661856647097113444740160704626215680717481877844371436988218559670959102596862002353718588748569652200050311734392073211390803293634479727355955277349071783793421637012050054513263835440001863239914907054797780566978533580489669062951194324730995876552368128590413832411607226029983305353708761389396391779574540161372236187893652605381558415871869255386061647798340254351284396129460352913325942794904337299085731580290958631382683291477116396337092400316894586360606458459251269946557248391865642097526850823075442545993769170419777800853627309417101634349076964237222943523661255725088147792231519747780605696725380171807763603462459278778465850656050780844211529697521890874019660906651803516501792504619501366585436632712549639908549144200014574760819302212066024330096412704894390397177195180699086998606636583232278709376502260149291011517177635944602023249300280401867723910288097866605651183260043688508817157238669842242201024950551881694803221002515426494639812873677658927688163598312477886520141174110913601164995076629077943646005851941998560162647907615321038727557126992518275687989302761761146162549356495903798045838182323368612016243736569846703785853305275833337939907521660692380533698879565137285593883499894707416181550125397064648171946708348197214488898790676503795903669672494992545279033729636162658976039498576741397359441023744329709355477982629614591442936451428617158587339746791897571211956187385783644758448423555581050025611492391518893099463428413936080383091662818811503715284967059741625628236092168075150177725387402564253470879089137291722828611515915683725241630772254406337875931059826760944203261924285317018781772960235413060672136046000389661093647095141417185777014180606443636815464440053316087783143174440811949422975599314011888683314832802706553833004693290115744147563139997221703804617092894579096271662260740718749975359212756084414737823303270330168237193648002173285734935947564334129943024850235732214597843282641421684878721673367010615094243456984401873312810107945127223737886126058165668053714396127888732527373890392890506865324138062796025930387727697783792868409325365880733988457218746021005311483351323850047827169376218004904795597959290591655470505777514308175112698985188408718564026035305583737832422924185625644255022672155980274012617971928047139600689163828665277009752767069777036439260224372841840883251848770472638440379530166905465937461619323840363893131364327137688841026811219891275223056256756254701725086349765367288605966752740868627407912856576996313789753034660616669804218267724560530660773899624218340859882071864682623215080288286359746839654358856685503773131296587975810501214916207656769950659715344763470320853215603674828608378656803073062657633469774295634643716709397193060876963495328846833613038829431040800296873869117066666146800015121143442256023874474325250769387077775193299942137277211258843608715834835626961661980572526612206797540621062080649882918454395301529982092503005498257043390553570168653120526495614857249257386206917403695213533732531666345466588597286659451136441370331393672118569553952108458407244323835586063106806964924851232632699514603596037297253198368423363904632136710116192821711150282801604488058802382031981493096369596735832742024988245684941273860566491352526706046234450549227581151709314921879592718001940968866986837037302200475314338181092708030017205935530520700706072233999463990571311587099635777359027196285061146514837526209565346713290025994397663114545902685898979115837093419370441155121920117164880566945938131183843765620627846310490346293950029458341164824114969758326011800731699437393506966295712410273239138741754923071862454543222039552735295240245903805744502892246886285336542213815722131163288112052146489805180092024719391710555390113943316681515828843687606961102505171007392762385553386272553538830960671644662370922646809671254061869502143176211668140097595281493907222601112681153108387317617323235263605838173151034595736538223534992935822836851007810884634349983518404451704270189381994243410090575376257767571118090088164183319201962623416288166521374717325477727783488774366518828752156685719506371936565390389449366421764003121527870222366463635755503565576948886549500270853923617105502131147413744106134445544192101336172996285694899193369184729478580729156088510396781959429833186480756083679551496636448965592948187851784038773326247051945050419847742014183947731202815886845707290544057510601285258056594703046836344592652552137008068752009593453607316226118728173928074623094685367823106097921599360019946237993434210687813497346959246469752506246958616909178573976595199392993995567542714654910456860702099012606818704984178079173924071945996323060254707901774527513186809982284730860766536866855516467702911336827563107223346726113705490795365834538637196235856312618387156774118738527722922594743373785695538456246801013905727871016512966636764451872465653730402443684140814488732957847348490003019477888020460324660842875351848364959195082888323206522128104190448047247949291342284951970022601310430062410717971502793433263407995960531446053230488528972917659876016667811937932372453857209607582277178483361613582612896226118129455927462767137794487586753657544861407611931125958512655759734573015333642630767985443385761715333462325270572005303988289499034259566232975782488735029259166825894456894655992658454762694528780516501720674785417887982276806536650641910973434528878338621726156269582654478205672987756426325321594294418039943217000090542650763095588465895171709147607437136893319469090981904501290307099566226620303182649365733698419555776963787624918852865686607600566025605445711337286840205574416030837052312242587223438854123179481388550075689381124935386318635287083799845692619981794523364087429591180747453419551420351726184200845509170845682368200897739455842679214273477560879644279202708312150156406341341617166448069815483764491573900121217041547872591998943825364950514771379399147205219529079396137621107238494290616357604596231253506068537651423115349665683715116604220796394466621163255157729070978473156278277598788136491951257483328793771571459091064841642678309949723674420175862269402159407924480541255360431317992696739157542419296607312393763542139230617876753958711436104089409966089471418340698362993675362621545247298464213752891079884381306095552622720837518629837066787224430195793793786072107254277289071732854874374355781966511716618330881129120245204048682200072344035025448202834254187884653602591506445271657700044521097735585897622655484941621714989532383421600114062950718490427789258552743035221396835679018076406042138307308774460170842688272261177180842664333651780002171903449234264266292261456004337383868335555343453004264818473989215627086095650629340405264943244261445665921291225648893569655009154306426134252668472594914314239398845432486327461842846655985332312210466259890141712103446084271616619001257195870793217569698544013397622096749454185407118446433946990162698351607848924514058940946395267807354579700307051163682519487701189764002827648414160587206184185297189154019688253289309149665345753571427318482016384644832499037886069008072709327673127581966563941148961716832980455139729506687604740915420428429993541025829113502241690769431668574242522509026939034814856451303069925199590436384028429267412573422447765584177886171737265462085498294498946787350929581652632072258992368768457017823038096567883112289305809140572610865884845873101658151167533327674887014829167419701512559782572707406431808601428149024146780472327597684269633935773542930186739439716388611764209004068663398856841681003872389214483176070116684503887212364367043314091155733280182977988736590916659612402021778558854876176161989370794380056663364884365089144805571039765214696027662583599051987042300179465536788
+2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517027618386062613313845830007520449338265602976067371132007093287091274437470472306969772093101416928368190255151086574637721112523897844250569536967707854499699679468644549059879316368892300987931277361782154249992295763514822082698951936680331825288693984964651058209392398294887933203625094431173012381970684161403970198376793206832823764648042953118023287825098194558153017567173613320698112509961818815930416903515988885193458072738667385894228792284998920868058257492796104841984443634632449684875602336248270419786232090021609902353043699418491463140934317381436405462531520961836908887070167683964243781405927145635490613031072085103837505101157477041718986106873969655212671546889570350354021234078498193343210681701210056278802351930332247450158539047304199577770935036604169973297250886876966403555707162268447162560798826517871341951246652010305921236677194325278675398558944896970964097545918569563802363701621120477427228364896134225164450781824423529486363721417402388934412479635743702637552944483379980161254922785092577825620926226483262779333865664816277251640191059004916449982893150566047258027786318641551956532442586982946959308019152987211725563475463964479101459040905862984967912874068705048958586717479854667757573205681288459205413340539220001137863009455606881667400169842055804033637953764520304024322566135278369511778838638744396625322498506549958862342818997077332761717839280349465014345588970719425863987727547109629537415211151368350627526023264847287039207643100595841166120545297030236472549296669381151373227536450988890313602057248176585118063036442812314965507047510254465011727211555194866850800368532281831521960037356252794495158284188294787610852639813955990067376482922443752871846245780361929819713991475644882626039033814418232625150974827987779964373089970388867782271383605772978824125611907176639465070633045279546618550966661856647097113444740160704626215680717481877844371436988218559670959102596862002353718588748569652200050311734392073211390803293634479727355955277349071783793421637012050054513263835440001863239914907054797780566978533580489669062951194324730995876552368128590413832411607226029983305353708761389396391779574540161372236187893652605381558415871869255386061647798340254351284396129460352913325942794904337299085731580290958631382683291477116396337092400316894586360606458459251269946557248391865642097526850823075442545993769170419777800853627309417101634349076964237222943523661255725088147792231519747780605696725380171807763603462459278778465850656050780844211529697521890874019660906651803516501792504619501366585436632712549639908549144200014574760819302212066024330096412704894390397177195180699086998606636583232278709376502260149291011517177635944602023249300280401867723910288097866605651183260043688508817157238669842242201024950551881694803221002515426494639812873677658927688163598312477886520141174110913601164995076629077943646005851941998560162647907615321038727557126992518275687989302761761146162549356495903798045838182323368612016243736569846703785853305275833337939907521660692380533698879565137285593883499894707416181550125397064648171946708348197214488898790676503795903669672494992545279033729636162658976039498576741397359441023744329709355477982629614591442936451428617158587339746791897571211956187385783644758448423555581050025611492391518893099463428413936080383091662818811503715284967059741625628236092168075150177725387402564253470879089137291722828611515915683725241630772254406337875931059826760944203261924285317018781772960235413060672136046000389661093647095141417185777014180606443636815464440053316087783143174440811949422975599314011888683314832802706553833004693290115744147563139997221703804617092894579096271662260740718749975359212756084414737823303270330168237193648002173285734935947564334129943024850235732214597843282641421684878721673367010615094243456984401873312810107945127223737886126058165668053714396127888732527373890392890506865324138062796025930387727697783792868409325365880733988457218746021005311483351323850047827169376218004904795597959290591655470505777514308175112698985188408718564026035305583737832422924185625644255022672155980274012617971928047139600689163828665277009752767069777036439260224372841840883251848770472638440379530166905465937461619323840363893131364327137688841026811219891275223056256756254701725086349765367288605966752740868627407912856576996313789753034660616669804218267724560530660773899624218340859882071864682623215080288286359746839654358856685503773131296587975810501214916207656769950659715344763470320853215603674828608378656803073062657633469774295634643716709397193060876963495328846833613038829431040800296873869117066666146800015121143442256023874474325250769387077775193299942137277211258843608715834835626961661980572526612206797540621062080649882918454395301529982092503005498257043390553570168653120526495614857249257386206917403695213533732531666345466588597286659451136441370331393672118569553952108458407244323835586063106806964924851232632699514603596037297253198368423363904632136710116192821711150282801604488058802382031981493096369596735832742024988245684941273860566491352526706046234450549227581151709314921879592718001940968866986837037302200475314338181092708030017205935530520700706072233999463990571311587099635777359027196285061146514837526209565346713290025994397663114545902685898979115837093419370441155121920117164880566945938131183843765620627846310490346293950029458341164824114969758326011800731699437393506966295712410273239138741754923071862454543222039552735295240245903805744502892246886285336542213815722131163288112052146489805180092024719391710555390113943316681515828843687606961102505171007392762385553386272553538830960671644662370922646809671254061869502143176211668140097595281493907222601112681153108387317617323235263605838173151034595736538223534992935822836851007810884634349983518404451704270189381994243410090575376257767571118090088164183319201962623416288166521374717325477727783488774366518828752156685719506371936565390389449366421764003121527870222366463635755503565576948886549500270853923617105502131147413744106134445544192101336172996285694899193369184729478580729156088510396781959429833186480756083679551496636448965592948187851784038773326247051945050419847742014183947731202815886845707290544057510601285258056594703046836344592652552137008068752009593453607316226118728173928074623094685367823106097921599360019946237993434210687813497346959246469752506246958616909178573976595199392993995567542714654910456860702099012606818704984178079173924071945996323060254707901774527513186809982284730860766536866855516467702911336827563107223346726113705490795365834538637196235856312618387156774118738527722922594743373785695538456246801013905727871016512966636764451872465653730402443684140814488732957847348490003019477888020460324660842875351848364959195082888323206522128104190448047247949291342284951970022601310430062410717971502793433263407995960531446053230488528972917659876016667811937932372453857209607582277178483361613582612896226118129455927462767137794487586753657544861407611931125958512655759734573015333642630767985443385761715333462325270572005303988289499034259566232975782488735029259166825894456894655992658454762694528780516501720674785417887982276806536650641910973434528878338621726156269582654478205672987756426325321594294418039943217000090542650763095588465895171709147607437136893319469090981904501290307099566226620303182649365733698419555776963787624918852865686607600566025605445711337286840205574416030837052312242587223438854123179481388550075689381124935386318635287083799845692619981794523364087429591180747453419551420351726184200845509170845682368200897739455842679214273477560879644279202708312150156406341341617166448069815483764491573900121217041547872591998943825364950514771379399147205219529079396137621107238494290616357604596231253506068537651423115349665683715116604220796394466621163255157729070978473156278277598788136491951257483328793771571459091064841642678309949723674420175862269402159407924480541255360431317992696739157542419296607312393763542139230617876753958711436104089409966089471418340698362993675362621545247298464213752891079884381306095552622720837518629837066787224430195793793786072107254277289071732854874374355781966511716618330881129120245204048682200072344035025448202834254187884653602591506445271657700044521097735585897622655484941621714989532383421600114062950718490427789258552743035221396835679018076406042138307308774460170842688272261177180842664333651780002171903449234264266292261456004337383868335555343453004264818473989215627086095650629340405264943244261445665921291225648893569655009154306426134252668472594914314239398845432486327461842846655985332312210466259890141712103446084271616619001257195870793217569698544013397622096749454185407118446433946990162698351607848924514058940946395267807354579700307051163682519487701189764002827648414160587206184185297189154019688253289309149665345753571427318482016384644832499037886069008072709327673127581966563941148961716832980455139729506687604740915420428429993541025829113502241690769431668574242522509026939034814856451303069925199590436384028429267412573422447765584177886171737265462085498294498946787350929581652632072258992368768457017823038096567883112289305809140572610865884845873101658151167533327674887014829167419701512559782572707406431808601428149024146780472327597684269633935773542930186739439716388611764209004068663398856841681003872389214483176070116684503887212364367043314091155733280182977988736590916659612402021778558854876176161989370794380056663364884365089144805571039765214696027662583599051987042300179465536788567430285974600143785483237068701190078499404930918919181649327259774030074879681484882342932023012128032327460392219687528340516906974194257614673978110715464186273369091584973185011183960482533518748438923177292613543024932562896371361977285456622924461644497284597867711574125670307871885109336344480149675240618536569532074170533486782754827815415561966911055101472799040386897220465550833170782394808785990501947563108984124144672821865459971596639015641941751820935932616316888380132758752601460507676098392625726411120135288591317848299475682472564885533357279772205543568126302535748216585414000805314820697137262149755576051890481622376790414926742600071045922695314835188137463887104273544767623577933993970632396604969145303273887874557905934937772320142954803345000695256980935282887783710670585567749481373858630385762823040694005665340584887527005308832459182183494318049834199639981458773435863115940570443683515285383609442955964360676090221741896883548131643997437764158365242234642619597390455450680695232850751868719449064767791886720306418630751053512149851051207313846648717547518382979990189317751550639981016466414592102406838294603208535554058147159273220677567669213664081505900806952540610628536408293276621931939933861623836069111767785448236129326858199965239275488427435414402884536455595124735546139403154952097397051896240157976832639450633230452192645049651735466775699295718989690470902730288544945416699791992948038254980285946029052763145580316514066229171223429375806143993484914362107993576737317948964252488813720435579287511385856973381976083524423240466778020948399639946684833774706725483618848273000648319163826022110555221246733323184463005504481849916996622087746140216157021029603318588727333298779352570182393861244026868339555870607758169954398469568540671174444932479519572159419645863736126915526457574786985964242176592896862383506370433939811671397544736228625506803682664135541448048997721373174119199970017293907303350869020922519124447393278376156321810842898207706974138707053266117683698647741787180202729412982310888796831880854367327806879771659111654224453806625861711729498038248879986504061563975629936962809358189761491017145343556659542757064194408833816841111166200759787244137082333917886114708228657531078536674695018462140736493917366254937783014074302668422150335117736471853872324040421037907750266020114814935482228916663640782450166815341213505278578539332606110249802273093636740213515386431693015267460536064351732154701091440650878823636764236831187390937464232609021646365627553976834019482932795750624399645272578624400375983422050808935129023122475970644105678361870877172333555465482598906861201410107222465904008553798235253885171623518256518482203125214950700378300411216212126052726059944320443056274522916128891766814160639131235975350390320077529587392412476451850809163911459296071156344204347133544720981178461451077872399140606290228276664309264900592249810291068759434533858330391178747575977065953570979640012224092199031158229259667913153991561438070129260780197022589662923368154312499412259460023399472228171056603931877226800493833148980338548909468685130789292064242819174795866199944411196208730498064385006852620258432842085582338566936649849720817046135376163584015342840674118587581546514598270228676671855309311923340191286170613364873183197560812569460089402953094429119590295968563923037689976327462283900735457144596414108229285922239332836210192822937243590283003884445701383771632056518351970100115722010956997890484964453434612129224964732356126321951155701565824427661599326463155806672053127596948538057364208384918887095176052287817339462747644656858900936266123311152910816041524100214195937349786431661556732702792109593543055579732660554677963552005378304619540636971842916168582734122217145885870814274090248185446421774876925093328785670674677381226752831653559245204578070541352576903253522738963847495646255940378924925007624386893776475310102323746733771474581625530698032499033676455430305274561512961214585944432150749051491453950981001388737926379964873728396416897555132275962011838248650746985492038097691932606437608743209385602815642849756549307909733854185583515789409814007691892389063090542534883896831762904120212949167195811935791203162514344096503132835216728021372415947344095498316138322505486708172221475138425166790445416617303200820330902895488808516797258495813407132180533988828139346049850532340472595097214331492586604248511405819579711564191458842833000525684776874305916390494306871343118796189637475503362820939949343690321031976898112055595369465424704173323895394046035325396758354395350516720261647961347790912327995264929045151148307923369382166010702872651938143844844532639517394110131152502750465749343063766541866128915264446926222884366299462732467958736383501937142786471398054038215513463223702071533134887083174146591492406359493020921122052610312390682941345696785958518393491382340884274312419099152870804332809132993078936867127413922890033069995875921815297612482409116951587789964090352577345938248232053055567238095022266790439614231852991989181065554412477204508510210071522352342792531266930108270633942321762570076323139159349709946933241013908779161651226804414809765618979735043151396066913258379033748620836695475083280318786707751177525663963479259219733577949555498655214193398170268639987388347010255262052312317215254062571636771270010760912281528326508984359568975961038372157726831170734552250194121701541318793651818502020877326906133592182000762327269503283827391243828198170871168108951187896746707073377869592565542713340052326706040004348843432902760360498027862160749469654989210474443927871934536701798673920803845633723311983855862638008516345597194441994344624761123844617615736242015935078520825600604101556889899501732554337298073561699861101908472096600708320280569917042590103876928658336557728758684250492690370934262028022399861803400211320742198642917383679176232826444645756330336556777374808644109969141827774253417010988435853189339175934511574023847292909015468559163792696196841000676598399744972047287881831200233383298030567865480871476464512824264478216644266616732096012564794514827125671326697067367144617795643752391742928503987022583734069852309190464967260243411270345611114149835783901793499713790913696706497637127248466613279908254305449295528594932793818341607827091326680865655921102733746700132583428715240835661522165574998431236278287106649401564670141943713823863454729606978693335973109537126499416282656463708490580151538205338326511289504938566468752921135932220265681856418260827538790002407915892646028490894922299966167437731347776134150965262448332709343898412056926145108857812249139616912534202918139898683901335795857624435194008943955180554746554000051766240202825944828833811886381749594284892013520090951007864941868256009273977667585642598378587497776669563350170748579027248701370264203283965756348010818356182372177082236423186591595883669487322411726504487268392328453010991677518376831599821263237123854357312681202445175401852132663740538802901249728180895021553100673598184430429105288459323064725590442355960551978839325930339572934663055160430923785677229293537208416693134575284011873746854691620648991164726909428982971065606801805807843600461866223562874591385185904416250663222249561448724413813849763797102676020845531824111963927941069619465426480006761727618115630063644321116224837379105623611358836334550102286170517890440570419577859833348463317921904494652923021469259756566389965893747728751393377105569802455757436190501772466214587592374418657530064998056688376964229825501195065837843125232135309371235243969149662310110328243570065781487677299160941153954063362752423712935549926713485031578238899567545287915578420483105749330060197958207739558522807307048950936235550769837881926357141779338750216344391014187576711938914416277109602859415809719913429313295145924373636456473035037374538503489286113141638094752301745088784885645741275003353303416138096560043105860548355773946625033230034341587814634602169235079216111013148948281895391028916816328709309713184139815427678818067628650978085718262117003140003377301581536334149093237034703637513354537634521050370995452942055232078817449370937677056009306353645510913481627378204985657055608784211964039972344556458607689515569686899384896439195225232309703301037277227710870564912966121061494072782442033414057441446459968236966118878411656290355117839944070961772567164919790168195234523807446299877664824873753313018142763910519234685081979001796519907050490865237442841652776611425351538665162781316090964802801234493372427866930894827913465443931965254154829494577875758599482099181824522449312077768250830768282335001597040419199560509705364696473142448453825888112602753909548852639708652339052941829691802357120545328231809270356491743371932080628731303589640570873779967845174740515317401384878082881006046388936711640477755985481263907504747295012609419990373721246201677030517790352952793168766305099837441859803498821239340919805055103821539827677291373138006715339240126954586376422065097810852907639079727841301764553247527073788764069366420012194745702358295481365781809867944020220280822637957006755393575808086318932075864444206644691649334467698180811716568665213389686173592450920801465312529777966137198695916451869432324246404401672381978020728394418264502183131483366019384891972317817154372192103946638473715630226701801343515930442853848941825678870721238520597263859224934763623122188113706307506918260109689069251417142514218153491532129077723748506635489170892850760234351768218355008829647410655814882049239533702270536705630750317499788187009989251020178015601042277836283644323729779929935160925884515772055232896978333126427671291093993103773425910592303277652667641874842441076564447767097790392324958416348527735171981064673837142742974468992320406932506062834468937543016787815320616009057693404906146176607094380110915443261929000745209895959201159412324102274845482605404361871836330268992858623582145643879695210235266673372434423091577183277565800211928270391042391966426911155333594569685782817020325495552528875464466074620294766116004435551604735044292127916358748473501590215522120388281168021413865865168464569964810015633741255098479730138656275460161279246359783661480163871602794405482710196290774543628092612567507181773641749763254436773503632580004042919906963117397787875081560227368824967077635559869284901628768699628053790181848148810833946900016380791075960745504688912686792812391148880036720729730801354431325347713094186717178607522981373539126772812593958220524289991371690685650421575056729991274177149279608831502358697816190894908487717722503860872618384947939757440664912760518878124233683125467278331513186758915668300679210215947336858591201395360301678110413444411030903388761520488296909104689167671555373346622545575975202624771242796225983278405833585897671474205724047439720232895903726148688388003174146490203843590358527993123871042845981608996101945691646983837718267264685264869172948414153004604004299585035164101899027529366867431834955447458124140190754681607770977920579383895378192128847409929537040546962226547278807248685508046571043123854873351653070570784584243335550958221912862797205455466267099131902370311779690892786623112661337671178512943059323281605826535623848164192144732543731002062738466812351691016359252588256806438946389880872735284406462208149513862275239938938734905082625472417781702582044129853760499827899020083498387362992498125742354568439023012261733665820546785671147973065077035475620567428300187473019197310881157516777005071432012726354601912460800451608108641835539669946936947322271670748972850464195392966434725254724357659192969949061670189061433616907056148280980363243454128229968275980226694045642181328624517549652147221620839824594576613342710564957193564431561774500828376935700995419541839029151033187933907614207467028867968594985439789457300768939890070073924697461812855764662265412913204052279071212820653775058280040897163467163709024906774736309136904002615646432159560910851092445162454420141442641660181385990017417408244245378610158433361777292580611159192008414091888191208858207627011483671760749046980914443057262211104583300789331698191603917150622792986282709446275915009683226345073725451366858172483498470080840163868209726371345205439802277866337293290829914010645589761697455978409211409167684020269370229231743334499986901841510888993165125090001163719114994852024821586396216294981753094623047604832399379391002142532996476235163569009445086058091202459904612118623318278614464727795523218635916551883057930657703331498510068357135624341881884405780028844018129031378653794869614630467726914552953690154167025838032477842272417994513653582260971652588356712133519546838335349801503269359798167463231847628306340588324731228951257944267639877946713121042763380872695738609314631539148548792514028885025189788076023838995615684850391995855029256054176767663145354058496296796781349420116003325874431438746248313850214980401681940795687219268462617287403480967931949965604299190281810597603263251746405016454606266765529010639868703668263299050577706266397868453584384057673298268163448646707439990917504018892319267557518354054956017732907127219134577524905771512773358423314008356080926962298894163047287780054743798498545562870729968407382937218623831766524716090967192007237658894226186550487552614557855898773008703234726418384831040394818743616224455286163287628541175946460497027724490799275146445792982549802258601001772437840167723166802004162547244179415547810554178036773553354467030326469619447560812831933095679685582771932031205941616693902049665352189672822671972640029493307384717544753761937017882976382487233361813499414541694736549254840633793674361541081593464960431603544354737728802361047743115330785159902977771499610274627769759612488879448609863349422852847651310277926279743981957617505591300993377368240510902583759345170015340522266144077237050890044496613295859536020556034009492820943862994618834790932894161098856594954213114335608810239423706087108026465913203560121875933791639666437282836752328391688865373751335794859860107569374889645657187292540448508624449947816273842517229343960137212406286783636675845331904743954740664015260871940915743955282773904303868772728262065663129387459875317749973799293043294371763801856280061141619563942414312254397099163565102848315765427037906837175764870230052388197498746636856292655058222887713221781440489538099681072143012394693530931524054081215705402274414521876541901428386744260011889041724570537470755550581632831687247110220353727166112304857340460879272501694701067831178927095527253222125224361673343366384756590949728221809418684074238351567868893421148203905824224324264643630201441787982022116248471657468291146315407563770222740135841109076078464780070182766336227978104546331131294044833570134869585165267459515187680033395522410548181767867772152798270250117195816577603549732923724732067853690257536233971216884390878879262188202305529937132397194333083536231248870386416194361506529551267334207198502259771408638122015980894363561808597010080081622557455039101321981979045520049618583777721048046635533806616517023595097133203631578945644487800945620369784973459902004606886572701865867757842758530645706617127194967371083950603267501532435909029491516973738110897934782297684100117657987098185725131372267749706609250481876835516003714638685918913011736805218743265426063700710595364425062760458252336880552521181566417553430681181548267844169315284408461087588214317641649835663127518728182948655658524206852221830755306118393326934164459415342651778653397980580828158806300749952897558204686612590853678738603318442905510689778698417735603118111677563872589911516803236547002987989628986181014596471307916144369564690909518788574398821730583884980809523077569358851616027719521488998358632323127308909861560777386006984035267826785387215920936255817889813416247486456433211043194821421299793188104636399541496539441501383868748384870224681829391860319598667962363489309283087840712400431022706137591368056518861313458307990705003607588327248867879324093380071864152853317943535073401891193638546730000660453783784472469288830546979000131248952100446949032058838294923613919284305249167833012980192255157050378521810552961623637523647962685751660066539364142273063001648652613891842243501797455993616794063303522111829071597538821839777552812981538570168702202620274678647916644030729018445497956399844836807851997088201407769199261674991148329821854382718946282165387064858588646221611410343570342878862979083418871606214430014533275029715104673156021000043869510583773779766003460887624861640938645252177935289947578496255243925598620521409052346250847830487046492688313289470553891357290706967599556298586669559721686506052072801342104355762779184021797626656484580261591407173477009039475168017709900129391137881248534255949312866653465033728846390649968460644741907524313323903404908195233044389559060547854954620263256676813262435925020249516275607080900436460421497025691488555265022810327762115842282433269528629137662675481993546118143913367579700141255870143319434764035725376914388899683088262844616425575034001428982557620386364384137906519612917777354183694676232982904981261717676191554292570438432239918482261744350470199171258214687683172646078959690569981353264435973965173473319484798758064137926885413552523275720457329477215706850016950046959758389373527538622664943456437071610511521617176237598050900553232154896062817794302268640579555845730600598376482703339859420098582351400179507104569019191359062304102336798080907240196312675268916362136351032648077232914950859151265812143823371072949148088472355286394195993455684156344577951727033374238129903260198160571971183950662758220321837136059718025940870615534713104482272716848395524105913605919812444978458110854511231668173534838253724825347636777581712867205865148285317273569069839935110763432091319780314031658897379628301178409806410175016511072932907832177487566289310650383806093372841399226733384778203302020700517188941706465146238366720632742644336612174011766914919235570905644803016342294301837655263108450172510307540942604409687066288066265900569082451407632599158164499361455172452057020443093722305550217222299706209749268609762787409626448772056043078634808885709143464793241536214303199965695610753570417207285334250171325558818113295504095217830139465216436594262960768570585698507157151317262928960072587601564840556088613165411835958628710665496282599535127193244635791046554389165150954187306071015034430609582302257455974944275067630926322529966338219395202927917973247094559691016402983683080426309910481567503623509654924302589575273521412445149542462972258510120707802110188106722347972579330653187713438466713807546383471635428854957610942841898601794658721444495198801550804042506452191484989920400007310672369944655246020908767882300064337725657385010969899058191290957079866699453765080407917852438222041070599278889267745752084287526377986730360561230710723922581504781379172731261234878334034473833573601973235946604273704635201327182592410906040097638585857716958419563109577748529579836844756803121874818202833941887076311731615289811756429711334181497218078040465077657204457082859417475114926179367379999220181789399433337731146911970737861041963986422166045588965683206701337505745038872111332436739840284188639147633491695114032583475841514170325690161784931455706904169858050217798497637014758914810543205854914100662201721719726878930012101267481270235940855162601689425111458499658315589660460091525797881670384625905383256920520425791378948827579603278877535466861441826827797651258953563761485994485049706638406266121957141911063246061774180577212381659872472432252969098533628440799030007594546281549235506086481557928961969617060715201589825299772803520002610888814176506636216905928021516429198484077446143617891415191517976537848282687018750030264867608433204658525470555882410254654806040437372771834769014720664234434374255514129178503032471263418076525187802925534774001104853996960549926508093910691337614841834884596365621526610332239417467064368340504749943339802285610313083038484571294767389856293937641914407036507544622061186499127249643799875806537850203753189972618014404667793050140301580709266213229273649718653952866567538572115133606114457222800851183757899219543063413692302293139751143702404830227357629039911794499248480915071002444078482866598579406525539141041497342780203520135419925977628178182825372022920108186449448349255421793982723279357095828748597126780783134286180750497175747373730296280477376908932558914598141724852658299510882230055223242218586191394795184220131553319634363922684259164168669438122537135960710031743651959027712571604588486044820674410935215327906816032054215967959066411120187618531256710150212239401285668608469435937408158536481912528004920724042172170913983123118054043277015835629513656274610248827706488865037765175678806872498861657094846665770674577000207144332525555736557083150320019082992096545498737419756608619533492312940263904930982014700371161829485939931199955070455381196711289367735249958182011774799788636393286405807810818657337668157893827656450642917396685579555053188715314552353070355994740186225988149854660737787698781542360397080977412361518245964026869979609564523828584235953564615185448165799966460648261396618720304839119560250381111550938420209894591555760083897989949964566262540514195610780090298667014635238532066032574466820259430618801773091109212741138269148784355679352572808875543164693077235363768226036080174040660997151176880434927489197133087822951123746632635635328517394189466510943745768270782209928468034684157443127739811044186762032954475468077511126663685479944460934809992951875666499902261686019672053749149951226823637895865245462813439289338365156536992413109638102559114643923805213907862893561660998836479175633176725856523591069520326895990054884753424160586689820067483163174286329119633399132709086065074595260357157323069712106423424081597068328707624437165532750228797802598690981111226558888151520837482450034463046505984569690276166958278982913613535306291331427881888249342136442417833519319786543940201465328083410341785272489879050919932369270996567133507711905899945951923990615156165480300145359212550696405345263823452155999210578191371030188979206408883974767667144727314254467923500524618849237455307575734902707342496298879996942094595961008702501329453325358045689285707241207965919809225550560061971283541270202072583994171175520920820151096509526685113897577150810849443508285458749912943857563115668324566827992991861539009255871716840495663991959154034218364537212023678608655364745175654879318925644085274489190918193411667583563439758886046349413111875241038425467937999203546910411935443113219136068129657568583611774564654674861061988591414805799318725367531243470335482637527081353105570818049642498584646147973467599315946514787025065271083508782350656532331797738656666181652390017664988485456054961300215776115255813396184027067814900350252876823607822107397102339146870159735868589015297010347780503292154014359595298683404657471756232196640515401477953167461726208727304820634652469109953327375561090578378455945469160223687689641425960164689647106348074109928546482353083540132332924864037318003195202317476206537726163717445360549726690601711176761047774971666890152163838974311714180622222345718567941507299526201086205084783127474791909996889937275229053674785020500038630036526218800670926674104806027341997756660029427941090400064654281074454007616429525362460261476180471744322889953285828397762184600967669267581270302806519535452053173536808954589902180783145775891280203970053633193821100095443241244197949192916205234421346395653840781209416214835001155883618421164283992454027590719621537570187067083731012246141362048926555668109467076386536083015847614512581588569610030337081197058344452874666198891534664244887911940711423940115986970795745946337170243268484864632018986352827092313047089215684758207753034387689978702323438584381125011714013265769320554911860153519551654627941175593967947958810333935413289702528893533748106257875620364294270257512121137330213811951395756419122685155962476203282038726342066227347868223036522019655729325905068134849292299647248229359787842720945578267329975853818536442370617353517653060396801087899490506654491544577952166038552398013798104340564182403396162494910454712104839439200945914647542424785991096900046541371091630096785951563947332190934511838669964622788855817353221326876634958059123761251203010983867841195725887799206041260049865895027247133146763722204388398558347770112599424691208308595666787531942465131444389971195968105937957532155524204659410081418351120174196853432672343271868099625045432475688702055341969199545300952644398446384346598830418262932239295612610045884644244285011551557765935780379565026806130721758672048541797157896401554276881090475899564605488362989140226580026134158039480357971019004151547655018391755772677897148793477372747525743898158705040701968215101218826088040084551332795162841280679678965570163917067779841529149397403158167896865448841319046368332179115059107813898261026271979696826411179918656038993895418928488851750122504754778999508544083983800725431468842988412616042682248823097788556495765424017114510393927980290997604904428832198976751320535115230545666467143795931915272680278210241540629795828828466355623580986725638200565215519951793551069127710538552661926903526081367717666435071213453983711357500975854405939558661737828297120544693182260401670308530911657973113259516101749193468250063285777004686987177255226525708428745733039859744230639751837209975339055095883623642814493247460522424051972825153787541962759327436278819283740253185668545040893929401040561666867664402868211607294830305236465560955351079987185041352121321534713770667681396211443891632403235741573773787908838267618458756361026435182951815392455211729022985278518025598478407179607904114472041476091765804302984501746867981277584971731733287305281134969591668387877072315968334322509070204019030503595891994666652037530271923764252552910347950343816357721698115464329245608951158732012675424975710520894362639501382962152214033621065422821876739580121286442788547491928976959315766891987305176388698461503354594898541849550251690616888419122873385522699976822609645007504500096116866129171093180282355042553653997166054753907348915189650027442328981181709248273610863801576007240601649547082331349361582435128299050405405333992577071321011503713898695076713447940748097845416328110406350804863393555238405735580863718763530261867971725608155328716436111474875107033512913923595452951407437943144900950809932872153235195999616750297532475931909938012968640379783553559071355708369947311923538531051736669154087312467233440702525006918026747725078958903448856673081487299464807786497709361969389290891718228134002845552513917355978456150353144603409441211512001738697261466786933733154341007587514908295822756919350542184106448264951943804240543255345965248373785310657979037977505031436474651422484768831323479762673689855474944277949916560108528257618964374464656819789319422077536824661110427671936481836360534108748971066866318805026555929568123959680449295166615409802610781691689418764353363449482900125929366840591370059526914934421861891742142561071896846626335874414976973921566392767687720145153302241853125308442727245771161505550519076276250016522166274796257424425420546785767478190959486500575711016264847833741198041625940813327229905891486422127968042984725356237202887830051788539737909455265135144073130049869453403245984236934627060242579432563660640597549471239092372458126154582526667304702319359866523378856244229188278436440434628094888288712101968642736370461639297485616780079779959696843367730352483047478240669928277140069031660709951473154191919911453182543906294573298686613524886500574780251977607442660798300291573030523199052185718628543687577860915726925232573171665625274275808460620177046433101212443409281314659760221360416223031167750085960128475289259463348312408766740128170543067985261868949895004918275008304998926472034986965363326210919830621495095877228260815566702155693484634079776879525038204442326697479264829899016938511552124688935873289878336267819361764023681714606495185508780596635354698788205094762016350757090024201498400967867845405354130050482404996646978558002628931826518708714613909521454987992300431779500489569529280112698632533646737179519363094399609176354568799002814515169743717518330632232942199132137614506411391269837128970829395360832883050256072727563548374205497856659895469089938558918441085605111510354367477810778500572718180809661542709143010161515013086522842238721618109043183163796046431523184434669799904865336375319295967726080853457652274714047941973192220960296582500937408249714373040087376988068797038047223488825819819025644086847749767508999164153502160223967816357097637814023962825054332801828798160046910336602415904504637333597488119998663995617171089911809851197616486499233594328274275983382931099806461605360243604040848379619072542165869409486682092396143083817303621520642297839982533698027039931804024928814430649614747600087654305571672697259114631990688823893005380061568007730984416061355843701277573463708822073792921409548717956947854414951731561828176343929570234710460088230637509877521391223419548471196982303169544468045517922669260631327498272520906329003279972932906827204647650366969765227673645419031639887433042226322021325368176044169612053532174352764937901877252263626883107879345194133825996368795020985033021472307603375442346871647223795507794130304865403488955400210765171630884759704098331306109510294140865574071074640401937347718815339902047036749084359309086354777210564861918603858715882024476138160390378532660185842568914109194464566162667753712365992832481865739251429498555141512136758288423285957759412684479036912662015308418041737698963759002546999454131659341985624780714434977201991702665380714107259910648709897259362243300706760476097690456341576573395549588448948093604077155688747288451838106069038026528318275560395905381507241627615047252487759578650784894547389096573312763852962664517004459626327934637721151028545472312880039058405918498833810711366073657536918428084655898982349219315205257478363855266205400703561310260405145079325925798227406012199249391735122145336707913500607486561657301854049217477162051678486507913573336334257685988361252720250944019430674728667983441293018131344299088234006652915385763779110955708000600143579956351811596764725075668367726052352939773016348235753572874236648294604770429166438403558846422370760111774821079625901180265548868995181239470625954254584491340203400196442965370643088660925268811549596291166168612036195319253262662271108142149856132646467211954801142455133946382385908540917878668826947602781853283155445565265933912487885639504644196022475186011405239187543742526581685003052301877096152411653980646785444273124462179491306502631062903402737260479940181929954454297256377507172705659271779285537195547433852182309492703218343678206382655341157162788603990157495208065443409462446634653253581574814022471260618973060860559065082163068709634119751925774318683671722139063093061019303182326666420628155129647685313861018672921889347039342072245556791239578260248978371473556820782675452142687314252252601795889759116238720807580527221031327444754083319215135934526961397220564699247718289310588394769170851420631557192703636345039529604362885088555160008371973526383838996789184600327073682083234847108471706160879195227388252347506380811606090840124222431476103563328940609282430125462013806032608121942876847907192546246309055749298781661271916548229644317263587524548607563020667656942355342774617635549231817456159185668061686428714964129290560130053913469569829490891003991259088290348791943368696942620662946948514931472688923571615032405542263391673583102728579723061998175868700492227418629077079508809336215346303842967525604369606110193842723883107587771653594778681499030978765900869583480043137176832954871752604714113064847270887246697164585218774442100900090916189819413456305028950484575822161887397443918833085509908566008543102796375247476265353031558684515120283396640547496946343986288291957510384781539068343717740714095628337554413567955424664601335663617305811711646062717854078898495334329100315985673932305693426085376230981047171826940937686754301837015557540822371538037838383342702379535934403549452173960327095407712107332936507766465603712364707109272580867897181182493799540477008369348889220963814281561595610931815183701135104790176383595168144627670903450457460997444500166918675661035889313483800512736411157304599205955471122443903196476642761038164285918037488354360663299436899730090925177601162043761411616688128178292382311221745850238080733727204908880095181889576314103157447684338100457385008523652069340710078955916549813037292944462306371284357984809871964143085146878525033128989319500645722582281175483887671061073178169281242483613796475692482076321356427357261609825142445262515952514875273805633150964052552659776922077806644338105562443538136258941809788015677378951310313157361136026047890761945591820289365770116416881703644242694283057457471567494391573593353763114830246668754727566653059819746822346578699972291792416156043557665183382167059157867799311835820189855730344883681934418305987021880502259192818047775223884407167894780414701414651073580452021499197980812095692195622632313741870979731320870864552236740416185590793816745658234353037283309503729022429802768451559528656923189798000383061378732434546500582722712325031420712488100290697226311129067629080951145758060270806092801504406139446350643069742785469477459876821004441453438033759717384777232052065301037861326418823586036569054773343070911759152582503029410738914441818378779490613137536794654893375260322906277631983337976816641721083140551864133302224787118511817036598365960493964571491686005656771360533192423185262166760222073368844844409234470948568027905894191829969467724456269443308241243846160408284006424867072583661011433404214473683453638496544701067827313169538435919120440283949541956874453676459875488726170687163109591315801609722382049772577307454562979127906177531663252857205858766376754282917933549923678212008601904369428956102301731743150352204665675088491593025926618816581008701658499456495586855628208747248318351516339189292646558880593601275151838235485893426165223086697314511412035659916934103076974774451947043836739600076578628245472064617380804602903639144493859012422380173377038154675297645596518492676039300171943042511794045679862114630138402371099347243455794730048929825402680821621522346560274258486595687074510352794291633405915025075992398611224340312056999780516223878772230396359709132856830486160362127579561601328561866388146004722200580017580282279272167842720649966956840905752590774886105493806116954293569077377792821084159737469613143291808510446953973485067590503662391722108732333169909603363771705474725026941732982890400239372879549386540463828596742216318201530139629734398479588628632934746650690284066719018081265539973675916799759010867483920062877888531102781695087545740384607594616919584610655963327283485609570305572502494416337066573150237126843581984154103154401008430380631442183776750349813408169325201240813452285974626715177152223063741359255747513535160669108359443999692315898156732033027129284241219651936303734407981204656795322986357374589031654007016472204989445629050395873788912680565516464274460174738175296313458739390484560414203426465560422112239134631023161290836446988901247285192778589195228773637440432659264672239982186452797664826673070168802722052338600372842903155828454593854349099449420750911108532138744823216151007808922516285123275724355101999038195993350032641446053470357293073912578481757987468353429629749652545426864234949270336399427519354240001973125098882419600095766257217621860474573769577649582201796258392376391717855799468922496750179251915218219624653575570564228220399546682648329822996167217080156801080799777126517156274295763666959661983507435667132218383358509536665806605597148376773866922551603463644386269977295750658468929599809168949981898588529537874489519527097766262684177088590284321676352132630838812766335363319004134332844347630067982023716933653652880580156390360562722752187272454764258840995216482554453662083811789117725225682611478014242896970967121967502094421226279437073328703410646312100557376727450271638975234111426287828736758358819056742163061523416789476056879277154789714326222041069587947186435439940738639948986836168919377836648327137363654676901173760246643082285362494712605173293777247276797635865806019396287718060679122426813922872134061694882029506831654589707623668302556167559477498715183426989208952182644710514911419441192277010977616645850068963849426165593473112961064282379048216056210094265076173838082479030510998790719611852832556787472942907151041468948104916751035295897242381802288151276582257190705537652455285511598636421244284176256230139538669970308943645907600684938040875210854159851278070333207779865635907968462191534944587677170063778573171211036517486371634098385626541555573292664616402279791195975248525300376741774056125700303625811704838385391207273191845064713669122576415213769896260940351804147432053600369234179035440735703058314741623452840188940808983125191307741823338981880316339159565954543405777784331681162551898060409183018907512170192983622897099598983405484962284289398469847938668614293324543983592637036699355184231661615244505980576745765335552338715678211466689996845227042954589710922163652573965950289645637766038988037941517917867910675199009966139206238732318786758420544279396366759104126821843375015743069045967947046685602358283919759975285865384338189120042853787549302768972168199113340697282255535300044743958830079799736518459131437946494086272149669719100359399974735262764126125995350902609540048669398955899487421379590802893196914845826873123710180229775301190684280440780938156598081694611679374425663244656799606363751546304833112722231812338371779800439731087402647536582575657351059978314264831879619843765495877803685261751835391844920488198629786329743136948511780579298636452193232481339393090754566368038513630619718033957979522539508697432546502659123585049283028832934489284591373621624852528877442891851104093746333590660233239711922814450735588373324057814862662207486215513375036775585494138678352928273109003823116855374520901095101174796663003330352534143230024288248051396631446632656081582045216883922312025671065388459503224002320453633895521539919011035217362720909565500846486605368975498478995875596103167696587161281951919668893326641203784750417081752273735270989343717167642329956935697166213782736138899530515711822960896394055380431939398453970864418654291655853168697537052760701061488025700785387150835779480952313152747735711713643356413242974208137266896149109564214803567792270566625834289773407718710649866150447478726164249976671481383053947984958938064202886667951943482750168192023591633247099185942520392818083953020434979919361853380201407072481627304313418985942503858404365993281651941497377286729589582881907490040331593436076189609669494800067194371424058105327517721952474344983414191979918179909864631583246021516575531754156198940698289315745851842783390581029411600498699307751428513021286202539508732388779357409781288187000829944831476678183644656510024467827445695591845768068704978044824105799710771577579093525803824227377612436908709875189149049904225568041463131309240101049368241449253427992201346380538342369643767428862595140146178201810734100565466708236854312816339049676558789901487477972479202502227218169405159042170892104287552188658308608452708423928652597536146290037780167001654671681605343292907573031466562485809639550080023347676187068086526878722783177420214068980703410506200235273632267291964034093571225623659496432076928058165514428643204955256838543079254299909353199329432966018220787933122323225928276556048763399988478426451731890365879756498207607478270258861409976050788036706732268192473513646356758611212953074644777149423343867876705824452296605797007134458987594126654609414211447540007211790607458330686866231309155780005966522736183536340439991445294960728379007338249976020630448806064574892740547730693971337007962746135534442514745423654662752252624869916077111131569725392943756732215758704952417232428206555322808868670153681482911738542735797154157943689491063759749151524510096986573825654899585216747260540468342338610760823605782941948009334370046866568258579827323875158302566720152604684361412652956519894291184887986819088277339147282063794512260294515707367105637720023427811802621502691790400488001808901847311751199425460594416773315777951735444490965752131026306836047140331442314298077895617051256930051804287472368435536402764392777908638966566390166776625678575354239947427919442544664643315554138265543388487778859972063679660692327601733858843763144148113561693030468420017434061395220072403658812798249143261731617813894970955038369479594617979829257740992171922783223006387384996138434398468502234780438733784470928703890536420557474836284616809363650973790900204118525835525201575239280826462555785658190226958376345342663420946214426672453987171047721482128157607275305173330963455909323664528978019175132987747952929099598069790148515839540444283988381797511245355548426126784217797728268989735007954505834273726937288386902125284843370917479603207479554080911491866208687184899550445210616155437083299502854903659617362726552868081324793106686855857401668022408227992433394360936223390321499357262507480617409173636062365464458476384647869520547719533384203403990244761056010612777546471464177412625548519830144627405538601855708359981544891286863480720710061787059669365218674805943569985859699554089329219507269337550235821561424994538234781138316591662683103065194730233419384164076823699357668723462219641322516076261161976034708844046473083172682611277723613381938490606534404043904909864126903479263503943531836741051762565704797064478004684323069430241749029731181951132935746854550484711078742905499870600373983113761544808189067620753424526993443755719446665453524088287267537759197074526286322840219629557247932987132852479994638938924943286917770190128914220188747760484939855471168524810559991574441551507431214406120333762869533792439547155394213121021954430556748370425907553004950664994802614794524739012802842646689229455664958621308118913500279654910344806150170407268010067948926855360944990373928383520627992820181576427054962997401900837493444950600754365525758905546552402103412862124809003162941975876195941956592556732874237856112669741771367104424821916671499611728903944393665340294226514575682907490402153401026923964977275904729573320027982816062130523130658731513076913832317193626664465502290735017347656293033318520949298475227462534564256702254695786484819977513326393221579478212493307051107367474918016345667888810782101151826314878755138027101379868751299375133303843885631415175908928986956197561123025310875057188962535763225834275763348421016668109884514141469311719314272028007223449941999003964948245457520704922091620614222912795322688239046498239081592961111003756999529251250673688233852648213896986384052437049402152187547825163347082430303521036927849762517317825860862215614519165573478940019558704784741658847364803865995119651409542615026615147651220820245816010801218275982577477652393859159165067449846149161165153821266726927461290533753163055654440793427876550267301214578324885948736899073512166118397877342715872870912311383472485146035661382188014840560716074652441118841800734067898587159273982452147328317214621907330492060817440914125388918087968538960627860118193099489240811702350413554126823863744341209267781729790694714759018264824761112414556423937732224538665992861551475342773370683344173073150805440138894084087253197595538897613986400165639906934600670780501058567196636796167140097031535132386972899001749862948883362389858632127176571330142071330179992326381982094042993377790345261665892577931395405145369730429462079488033141099249907113241694504241391265397274078984953073730364134893688060340009640631540701820289244667315059736321311926231179142794944897281477264038321021720718017561601025111179022163703476297572233435788863537030535008357679180120653016668316780269873860755423748298548246360981608957670421903145684942967286646362305101773132268579232832164818921732941553151386988781837232271364011755881332524294135348699384658137175857614330952147617551708342432434174779579226338663454959438736807839569911987059388085500837507984051126658973018149321061950769007587519836861526164087252594820126991923916722273718430385263107266000047367872474915828601694439920041571102706081507270147619679971490141639274282889578424398001497985658130305740620028554097382687819891158955487586486645709231721825870342960508203415938806006561845735081804032347750084214100574577342802985404049555529215986404933246481040773076611691605586804857302606467764258503301836174306413323887707999698641372275526317649662882467901094531117120243890323410259937511584651917675138077575448307953064925086002835629697045016137935696266759775923436166369375035368699454550392874449940328328128905560530091416446608691247256021455381248285307613556149618444364923014290938289373215312818797541139219415606631622784836152140668972661027123715779503062132916001988806369127647416567067485490795342762338253943990022498972883660263920518704790601584084302914787302246651371144395418253441269003331181914268070735159284180415100555199146564934872796969351992963117195821262627236458009708099166752820365818699111948365866102758375863322993225541477479210421324166848264953111826527351008031659958888814809945737293785681411438021523876706455063233067233939551964260397443829874822322662036352861302543796600943104500158604854027036789711934695579989189112302233381602302236277726084846296189550730850698061500281436425336666311433321645213882557346329366870956708432252564333895997812402164189946978348320376011613913855499933990786652305860332060641949298931012423081105800169745975038516887112037747631577311831360002742502722451570906304496369230938382329175076469684003556425503797106891999812319602533733677437970687713814747552190142928586781724044248049323750330957002929126630316970587409214456472022710796484778657310660832173093768033821742156446602190335203981531618935787083561603302255162155107179460621892674335641960083663483835896703409115513087820138723494714321400450513941428998350576038799343355677628023346565854351219361896876831439866735726040869511136649881229957801618882834124004126142251475184552502502640896823664946401177803776799157180146386554733265278569418005501363433953502870836220605121839418516239153709790768084909674194289061134979961034672077354959593868862427986411437928435620575955500144308051267664432183688321434583708549082240014585748228606859593502657405750939203135881722442164955416889785558265198046245527898343289578416968890756237467281044803018524217706136533236073856228166664597654076844715963930782091017090763377917711485205493367936868430832404126789220929930411890501756484917499452393770674524578019171841679541825554377930299249277892416277257788147974770446005423669346157135208417428211847353652367573702352791459837645712257646122605628127852169580892808988394594406165340521932514843306105322700231133680378433377389724881307874325614952744243584753011150345103737688223837573804282007358586938044331529253129961025096113761670187568525921208929131354473196308440066835155160913925692912175784379179004808848023029304392630921342768601226558630456913133560978156776098711809238440656353136182676923761613389237802972720736243967239854144480757286813436768000573823963610796223140429490728058551444771338682314499547929338131259971996894072233847404542592316639781608209399269744676323921370773991899853301483814622364299493902073285072098040905300059160091641710175605409814301906444379905831277826625762288108104414704097708248077905168225857235732665234414956169007985520848841886027352780861218049418060017941147110410688703738674378147161236141950474056521041002268987858525470689031657094677131822113205505046579701869337769278257145248837213394613987859786320048011792814546859096532616616068403160077901584946840224344163938313618742275417712170336151163782359059685168880561304838542087505126933144171705880517278127917564053282929427357971823360842784676292324980318169828654166132873909074116734612367109059236155113860447246378721244612580406931724769152219217409096880209008801535633471775664392125733993165330324425899852598966724744126503608416484160724482125980550754851232313331300621490042708542735985913041306918279258584509440150719217604794274047740253314305451367710311947544521321732225875550489799267468541529538871443696399406391099267018219539890685186755868574434469213792094590683677929528246795437302263472495359466300235998990248299853826140395410812427393530207575128774273992824866921285637240069184859771126480352376025469714309316636539718514623865421671429236191647402172547787238964043145364190541101514371773797752463632741619269990461595895793940622986041489302535678633503526382069821487003578061101552210224486633247184367035502326672749787730470216165019711937442505629639916559369593557640005236360445141148916155147776301876302136068825296274460238077523189646894043033182148655637014692476427395401909403584437251915352134557610698046469739424511797999048754951422010043090235713636892619493763602673645872492900162675597083797995647487354531686531900176427222751039446099641439322672532108666047912598938351926694497553568096931962642014042788365702610390456105151611792018698900673027082384103280213487456720062839744828713298223957579105420819286308176631987048287388639069922461848323992902685392499812367091421613488781501234093387999776097433615750910992585468475923085725368613605356762146929424264323906626708602846163376051573599050869800314239735368928435294958099434465414316189806451480849292695749412903363373410480943579407321266012450796613789442208485840536446021616517885568969302685188950832476793300404851688934411125834396590422211152736276278672366665845757559585409486248261694480201791748223085835007862255216359325125768382924978090431102048708975715033330963651576804501966025215527080352103848176167004443740572131294252820989545456276344353575741673638980108310579931697917916718271145837435222026387771805250290791645414791173616253155840768495583288190293564201219633684854080865928095131505012602919562576032932512847250469881908146475324342363863860247943921015193235101390117789997483527186469346024554247028375300033725403910085997650987642832802908445662021678362267272292737780213652404028817217012490974899454430826861772239385250883760749742195942655217301733355851389407457348144161511380845358039740277795072051893487170722955427683655826706766313911972211811528466502223383490906676554168336907959409404576472940901354356409277969379842065738891481990225399022315913388145851487225126560927576795873759207013915029216513720851137197522734365458411622066281660256333632074449918511469174455062297146086578736313585389023662557285424516018080487167823688885575325066254262367702604215835160174851981885460860036597606743233346410471991027562358645341748631726556391320606407754779439671383653877377610828300019937359760370467245737880967939894493795829602910746901609451288456550071458091887879542641820145369659962842686882363495879277007025298960996798975941955735253914237782443302746708282008722602053415292735847582937522487377937899136764642153727843553986244015856488692101644781661602962113570056638347990334049623875941092886778920270077504951511405782565295015024484968204744379710872943108541684540513016310902267112951959140520827546866418137305837933236150599142045255880213558474751516267815309465541240524091663857551298894834797423322854504140527354235070335984964593699534959698554244978249586929179182415068053002553370412778703476446244329205906832901886692400222391918714603175399666877477960121790688623311002908668305431787009355066944389131913333586368037447530664502418437136030852288582121720231274167009740351431532131803978033680228154223490183737494117973254478594157962104378787072154814091725163615415163381388912588517924237727229603497305533840942889918919161186249580560073570527227874940321250645426206304469470804277945973817146810395192821550688079136701210109944220737024613687196031491162370967939354636396448139025711768057799751751298979667073292674886430097398814873780767363792886767781170520534367705731566895899181530825761606591843760505051704242093231358724816618683821026679970982966436224723644898648976857100173643547336955619347638598187756855912376232580849341570570863450733443976604780386678461711520325115528237161469200634713570383377229877321365028868868859434051205798386937002783312365427450532283462669786446920780944052138528653384627970748017872477988461146015077617116261800781557915472305214759943058006652042710117125674185860274188801377931279938153727692612114066810156521441903567333926116697140453812010040811760123270513163743154487571768761575554916236601762880220601068655524141619314312671535587154866747899398685510873576261006923021359580838145290642217792987748784161516349497309700794368305080955621264592795333690631936594413261117944256602433064619312002953123619348034504503004315096798588111896950537335671086336886944665564112662287921812114121425167348136472449021275252555647623248505638391391630760976364990288930588053406631352470996993362568102360392264043588787550723319888417590521211390376609272658409023873553418516426444865247805763826160023858280693148922231457758783791564902227590699346481624734399733206013058796068136378152964615963260698744961105368384203105364183675373594176373955988088591188920114871545460924735613515979992999722298041707112256996310945945097765566409972722824015293663094891067963296735505830412258608050740410916678539569261234499102819759563955711753011823480304181029089719655278245770283085321733741593938595853203645590564229716679900322284081259569032886928291260139267587858284765599075828016611120063145411315144108875767081854894287737618991537664505164279985451077400771946398046265077776614053524831090497899859510873112620613018757108643735744708366215377470972660188656210681516328000908086198554303597948479869789466434027029290899143432223920333487108261968698934611177160561910681226015874410833093070377506876977485840324132474643763087889666151972556180371472590029550718424245405129246729039791532535999005557334600111693557020225722442772950263840538309433999383388018839553821540371447394465152512354603526742382254148328248990134023054550811390236768038649723899924257800315803725555410178461863478690646045865826036072306952576113184134225274786464852363324759102670562466350802553058142201552282050989197818420425028259521880098846231828512448393059455162005455907776121981297954040150653985341579053629101777939776957892084510979265382905626736402636703151957650493344879513766262192237185642999150828898080904189181015450813145034385734032579549707819385285699926238835221520814478940626889936085239827537174490903769904145555260249190126341431327373827075950390882531223536876389814182564965563294518709637484074360669912550026080424160562533591856230955376566866124027875883101021495284600804805028045254063691285010599912421270508133194975917146762267305044225075915290251742774636494555052325186322411388406191257012917881384181566918237215400893603475101448554254698937834239606460813666829750019379115061709452680984785152862123171377897417492087541064556959508967969794980679770961683057941674310519254486327358885118436597143583348756027405400165571178309126113117314169066606067613797690123141099672013123730329707678988740099317309687380126740538923612230370779727025191340850390101739924877352408881040807749924412635346413181858792480760553268122881584307471326768283097203149049868884456187976015468233715478415429742230166504759393312132256510189175368566338139736836336126010908419590215582111816677413843969205870515074254852744810154541079359513596653630049188769523677579147319184225806802539818418929888943038224766186405856591859943091324575886587044653095332668532261321209825839180538360814144791320319699276037194760191286674308615217243049852806380129834255379486287824758850820609389214668693729881191560115633701248675404205911464930888219050248857645752083363921499441937170268576222251074166230901665867067714568862793343153513505688216165112807318529333124070912343832502302341169501745502360505475824093175657701604884577017762183184615567978427541088499501610912720817913532406784267161792013428902861583277304794830971705537485109380418091491750245433432217445924133037928381694330975012918544596923388733288616144238100112755828623259628572648121538348900698511503485369544461542161283241700533583180520082915722904696365553178152398468725451306350506984981006205514844020769539324155096762680887603572463913955278222246439122592651921288446961107463586148252820017348957533954255019475442643148903233373926763409115527189768429887783617346613535388507656327107814312435018965109238453660236940276060642119384227665755210663671879603217527184404651560427289869560206997012906367847161654793068868305846508082886614111979138822898112498261434559408961813509226857611474609406147937240008842153535862052780125014270055274468359151840373309373580494342483940467505708347927948338133276237937844629209323999417593374917899786484958148818865149169302451512835579818112344900827168644548306546633975256079615935830821400021951611342337058359111545217293721664061708131602078213341260356852013161345136871600980378712556766143923146458085652084039744217352744813741215277475202259244561520365608268890193913957991844109971588312780020898275935898106482117936157951837937026741451400902833064466209280549839169261068975151083963132117128513257434964510681479694782619701483204392206140109523453209269311762298139422044308117317394338867965739135764377642819353621467837436136161591167926578700137748127848510041447845416464568496606699139509524527949914769441031612575776863713634644477006787131066832417871556281779122339077841275184193161188155887229676749605752053192594847679397486414128879475647133049543555044790277128690095643357913405127375570391806822344718167939329121448449553897728696601037841520390662890781218240141299368590465146519209198605347788576842696538459445700169758422531241268031418456268722581132040056433413524302102739213788415250475704533878002467378571470021087314693254557923134757243640544448132093266582986850659125571745568328831440322798049274104403921761438405750750288608423536966715191668510428001748971774811216784160854454400190449242294333666338347684438072624307319019363571067447363413698467328522605570126450123348367412135721830146848071241856625742852208909104583727386227300781566668914250733456373259567253354316171586533339843321723688126003809020585719930855573100508771533737446465211874481748868710652311198691114058503492239156755462142467550498676710264926176510110766876596258810039163948397811986615585196216487695936398904500383258041054420595482859955239065758108017936807080830518996468540836412752905182813744878769639548306385089756146421874889271294890398025623046812175145502330254086076115859321603465240763923593699949180470780496764486889980902123735780457040380820770357387588525976042434608851075199334470112741787878845674656640471901619633546770714090590826954225196409446319547658653032104723804625249971910690110456227579220926904132753699634145768795242244563973018311291451151322757841320376225862458224784696669785947914981610522628786944136373683125108310682898766123782697506343047263278453719024447970975017396831214493357290791648779915089163278018852504558488782722376705263811803792477835540018117452957747339714012352011459901984753358434861297092928529424139865507522507808919352104173963493428604871342370429572757862549365917805401652536330410692033704691093097588782938291296447890613200063096560747882082122140978472301680600835812336957051454650181292694364578357815608503303392466039553797630836137289498678842851139853615593352782103740733076818433040893624460576706096188294529171362940967592507631348636606011346115980434147450705511490716640635688739020690279453438236930531133440901381392849163507484449076828386687476663619303412376248380175840467851210698290605196112357188811150723607303158506622574566366740720668999061320627793994112805759798332878792144188725498543014546662945079670707688135022230580562225942983096887732856788971494623888272184647618153045844390967248232348259587963698908456664795754200195991919240707615823002328977439748112690476546256873684352229063217889227643289360535947903046811114130586348244566489159211382258867880972564351646404364328416076247766114349880319792230537889671148058968061594279189647401954989466232962162567264739015818692956765601444248501821713300527995551312539849919933907083138030214072556753022600033565715934283182650908979350869698950542635843046765145668997627989606295925119763672907762567862769469947280606094290314917493590511523235698715397127866718077578671910380368991445381484562682604003456798248689847811138328054940490519768008320299631757043011485087384048591850157264392187414592464617404735275250506783992273121600117160338604710710015235631159734711153198198710616109850375758965576728904060387168114313084172893710817412764581206119054145955378853200366615264923610030157044627231777788649806700723598889528747481372190175074700005571108178930354895017924552067329003818814068686247959272205591627902292600592107710510448103392878991286820705448979977319695574374529708195463942431669050083984398993036790655541596099324867822475424361758944371791403787168166189093900243862038610001362193667280872414291108080291896093127526202667881902085595708111853836166128848729527875143202956393295910508349687029060692838441522579419764824996318479414814660898281725690484184326061946254276693688953540732363428302189694947766126078346328490315128061501009539164530614554234923393806214007779256337619373052025699319099789404390847443596972052065999017828537676265683558625452697455260991024576619614037537859594506363227095122489241931813728141668427013096050734578659047904243852086508154491350136491698639048125666610843702294730266721499164849610746803261583352580352858275799038584091667618877199539888680431991650866887781701439663176815592262016991396613153738021294160006906947533431677802632207226265881842757216055461439677336258462997385077307751473833315101468395296411397329672457933540390136107395245686243008096720460995545708974893048753897955544443791303790422346037768729236001386569593952300768091377768847789746299699489949016141866131552200856673695770822720338936659590666350594330040363762591189195691561626122704788696510356062748423100605472091437069471661080277379848576543481249822444235828329813543645124092220896643987201997945619030397327254617823136363375927622656301565813545578319730419339269008282952718252138855126583037630477490625995514925943105307478901043009876580816508144862607975129633326675259272351611791836777128931053144471668835182920514343609292493191180249366051791485330421043899773019267686085347768149502299280938065840007311767895491286098112311307002535600347898600653805084532572431553654422067661352337408211307834360326940015926958459588297845649462271300855594293344520727007718206398887404742186697709349647758173683580193168322111365547392288184271373843690526638607662451284299368435082612881367358536293873792369928837047900484722240370919885912556341130849457067599032002751632513926694249485692320904596897775676762684224768120033279577059394613185252356456291805905295974791266162882381429824622654141067246487216174351317397697122228010100668178786776119825961537643641828573481088089988571570279722274734750248439022607880448075724807701621064670166965100202654371260046641935546165838945950143502160890185703558173661823437491622669077311800121188299737319891006060966841193266075165452741829459541189277264192546108246351931647783837078295218389645376236304858042774417907169146356546201215125418664885396161542055152375000426794253417764590821513675258479774465114750438460596325820468809667795709044645884673847481638045635188183210386594798204376334738389017759714236223057776395541011294523488098341476645559342209402059733452337956309441446698222457026367119493286653989491344225517746402732596722993581333110831711807234044326813737231209669052411856734897392234152750707954137453460386506786693396236535556479102508529284294227710593056660625152290924148057080971159783458351173168204129645967070633303569271821496292272073250126955216172649821895790908865085382490848904421755530946832055636316431893917626269931034289485184392539670922412565933079102365485294162132200251193795272480340133135247014182195618419055761030190199521647459734401211601239235679307823190770288415814605647291481745105388060109787505925537152356112290181284710137917215124667428500061818271276125025241876177485994084521492727902567005925854431027704636911098800554312457229683836980470864041706010966962231877065395275783874454229129966623016408054769705821417128636329650130416501278156397799631957412627634011130135082721772287129164002237230234809031485343677016544959380750634285293053131127965945266651960426350406454862543383772209428482543536823186182982713182489884498260285705690699045790998144649193654563259496570044689011049923939218088155626191834404362264965506449848521612498442375928443642612004256628602157801140467879662339228190804577624109076487087406157070486658398144845855803277997327929143195789110373530019873110486895656281917362036703039179710646309906285483702836118486672219457621775034511770110458001291255925462680537427727378863726783016568351092332280649908459179620305691566806180826586923920561895421631986004793961133953226395999749526798801074576466538377400437463695133685671362553184054638475191646737948743270916620098057717103475575333102702706317395612448413745782734376330101853438497450236265733191742446567787499665000938706441886733491099877926005340862442833450486907338279348425305698737469497333364267191968992849534561045719338665222471536681145666596959735075972188416698767321649331898967182978657974612216573922404856900225324160367805329990925438960169901664189038843548375648056012628830409421321300206164540821986138099462721214327234457806819925823202851398237118926541234460723597174777907172041523181575194793527456442984630888846385381068621715274531612303165705848974316209831401326306699896632888532682145204083110738032052784669279984003137878996525635126885368435559620598057278951754498694219326972133205286374577983487319388899574634252048213337552584571056619586932031563299451502519194559691231437579991138301656117185508816658756751184338145761060365142858427872190232598107834593970738225147111878311540875777560020664124562293239116606733386480367086953749244898068000217666674827426925968686433731916548717750106343608307376281613984107392410037196754833838054369880310983922140260514297591221159148505938770679068701351029862207502287721123345624421024715163941251258954337788492834236361124473822814504596821452253550035968325337489186278678359443979041598043992124889848660795045011701169092519383155609441705397900600291315024253848282782826223304151370929502192196508374714697845805550615914539506437316401173317807741497557116733034632008408954066541694665746735785483133770133628948904397670025863002540635264006601631712883920305576358989492412827022489373848906764385339931878608019223108328847459816417701264089078551777830131616162049792779670521847212730327970738223860581986744668610994383049960437407323195784473254857416239738852016202384784256163512597161783106850156299135559874758848151014815490937380933394074455700842090155903853444962128368313687375166780513082594599771257467939781491953642874321122421579851584491669362551569370916855252644720786527971466476760328471332985501945689772758983450586004316822658631176606237201721007922216410188299330808409384014213759697185976897042759041500946595252763487628135867117352364964121058854934496645898651826545634382851159137631569519895230262881794959971545221250667461174394884433312659432286710965281109501693028351496524082850120190831078678067061851145740970787563117610746428835593915985421673115153096948758378955979586132649569817205284291038172721213138681565524428109871168862743968021885581515367531218374119972919471325465199144188500672036481975944167950887487934416759598361960010994838744709079104099785974656112459851972157558134628546189728615020774374529539536929655449012953097288963767713353842429715394179547179095580120134210175150931491664699052366350233024087218654727629639065723341455005903913890253699317155917179823065162679744711857951506573868504088229934804445549850597823297898617029498418376255258757455303112991914341109413088238114443068843062655305601658801408561023324210300218460588586954418502977463085858496130037238190325162225570729975710727306066072916922978033647048840958711228045188511908718588299514331534128549297173849768523136276076868494780364948299904475715771141080958058141208956059471668626290036145602625334863284986816039463372436667112964460292915746181117789169695839947080954788863503281129626899231110099889317815313946681882028368363373822281414974006917942192888817139116283910295684918233358930813360131488748366464224381776081007739183393749346933644748150564933649323157235306109385796839902153381449126925350768211098738352197507736653475499431740580563099143218212547336281359488317681489194306530426029773885492974570569448783077945878865062970895499843760181694031056909587141386804846359853684034105948341788438963179956468815791937174656705047441528027712541569401365862097760735632832966564135817028088013546326104892768731829917950379944446328158595181380144716817284996793061814177131912099236282922612543236071226270324572637946863533391758737446552006008819975294017572421299723542069630427857950608911113416534893431149175314953530067419744979017235181671568754163484949491289001739377451431928382431183263265079530371177806185851153508809998200482761808307209649636476943066172549186143700971387567940218696710148540307471561091358933165600167252126542502898612259306484105898847129649230941215144563947889999327145875969555737090855150648002321476443037232466147111552578583071024936898814562568786834745518893385181791667579054210421036349316257870476543126790661216644142285017446278477132740595579600648343288827864837043456066966456899746910373987712891593313271266247505582258634928427718355831641593667712218537642376222104779338956378722902509543014182257180331300148113377736941508488867501893156994849838936052666818012783912005801431596441910546663236810148207799356523056490420711364192200177189107935243234322761787712568251126481332974354926568682748715986654943041648468220593921673359485057849622807932422649812705271398407720995707236227009245067665680069149966555737866411877079767754867028786431817941521796178310655030287157272282250812017060713380339641841211253856248920130010782462165136989511064611133562443838185366273563783436921279354709230119655914915800561707258518503167289370411936374780625824298250726464801821523430268081486978164824349353456855843696378384153838051184406043696871666416514036129729992912630842812149152469877429332305214999981829046119471676727503742221367186614654042534463141660649871499001000660041544868437352208483059495953182872280520828676300361091734508632133033647289584176588755345227938480297724485711815574893561311524926772006362198369980664159549388683836411891430443767715498026544959061738265591178545999378510861446014967645550103653971251138583505085112442517772923814396233043724036032603181442991365750246012787514117944901305803452199992701148071712847770301254994886841867572975189214295652512486943983729047410363121899124217339550688778643130750024823361832738729697376598820053895902935486054979802320400472236873557411858132734337978931582039412878989728973298812553514507641535360519462112217000676321611195841029252568536561813138784086477147099724553013170761712163186600291464501378587854802096244703771373587720086738054108140042311418525803293267396324596914044834665722042880679280616029884043400536534009706581694636096660911110968789751801325224478246957913251892122653056085866541115373584912790254654369020869419871125588453729063224423222287139122012248769976837147645598526739225904997885514250047585260297929306159913444898341973583316070107516452301310796620382579278533125161760789984630103493496981494261055367836366022561213767081421091373531780682420175737470287189310207606953355721704357535177461573524838432101571399813798596607129664438314791296359275429627129436142685922138993054980645399144588692472767598544271527788443836760149912897358259961869729756588978741082189422337344547375227693199222635973520722998387368484349176841191020246627479579564349615012657433845758638834735832242535328142047826934473129971189346354502994681747128179298167439644524956655532311649920677163664580318205849626132234652606175413532444702007661807418914040158148560001030119994109595492321434406067634769713089513389171050503856336503545166431774489640061738861761193622676890576955693918707703942304940038440622614449572516631017080642923345170422426679607075404028551182398361531383751432493056398381877995594942545196756559181968690885283434886050828529642437578712929439366177362830136595872723080969468398938676366226456791132977469812675226595621009318322081754694778878755356188335083870248295346078597023609865656376722755704495258739871812593441903785275571333409842450127258596692434317689018966145404453679047136294238156127656824247864736176671770647002431119711090007474065945650315375044177982192306323700872039212085499569681061379189029961178936752146022386905665481382858280449537530160921422195940638787074787991194920898374091788534417523064715030278397979864517336625329511775105559014160459873338186887977858817291976604516353353556047648420520888811722831990044504284486852338334530105533929637308039738230604714104525470094899407601215247602819963846343554852932377161410869591950786873276075400085220065031871239272857835807010762542769655355964789450166013816295177908531139811092831583216931563867459747449584385282701658246192092219529134323496779345585613140207765996142546463288677356891785576835169608392864188830094883324700447958316931533832382377876344426323456301679513671047510469669001217777128065522453689371871451567394733440447280450959433090683667110655953338602938000999949010642769859623260401863733572846679531229683156358145420890540651226419162015504500430562136991850941034609601030543816694795964585804425194905110733387679946734471718615647723811737035654917628707589456035519195603962301157866323750234725054461073979402475184415558178087962822231972692984516683306919505079993357259165675557294585962182052650473353712351623662770479333289322136141858785972771685682725303734836891911847197133753088446777943274857148827821608844765700041403499921376794209627560883081509438030705666022764678117533361028187800710219794428777313146387857817205661409023041499923248268982477222109852189758140879763486146763606368674611966620347304608917277240045953051376938375381543486981101990651706961774052218247422657652138152740612699012706880875386408669901461740890540981877671880076124151967064152117653084325544261017536348281196837493395825742541244634247233586360777980960199745187758845459645895956779558869098404768259253477849930457883128541747079059795909431627722327844578918694214929451540174214623240300841907975296782445969183509474202123617940309048634960534054931299919496087957952586977170236680033862505764938088740994009589948109397983231108838769236490221499111120870639202892490698435333152727991330986335454324971441378059132240814960156485679843966464780280409057580889190254236606774500413415794312112501275232250148067232979652230488493751166084976116412777395311302041566848265531411348993243747890268935173904043294851610659785832253168204202834993641595980197343889883020994152152288611175126686173051956249367180053845637855129171848417841594797435580617856680758491080185805695567990185198397660693358224779136504562705766735170961550493338390452612404395517449136885115987454340932040102218982707539212403241042424451570052968378815749468441508011138612561164102477190903050040240662278945607061512108266146098662040425010583978098192019726759010749924884966139441184159734610382401178556739080566483321039073867083298691078093495828888707110651559651222542929154212923108071159723275797510859911398076844732639426419452063138217862260999160086752446265457028969067192282283045169111363652774517975842147102219099906257373383472726498678244401048998507631630668050267115944636293525120269424810854530602810627264236538250773340575475701704367039596467715959261029438313074897245505729085688496091346323165819468660587092144653716755655531962091865952628448253731353698162517351930115341581171353292035873164168839107994000677266031617527582917398395852606454113318985505747847121053505795649095931672167565624818782002769963734155880000867852567422461511406015760115910256449002264980039498403358091309140197877843650167960167465370287466062584346329708303725980494653589318912163976013193079476972058034710553111117215859219066231028099212084069283091906017370764654655683413207556315315006453462321007133584907633048328153458698497332599801187479664273140279381289961720524540674695271948079930396730194274036466594154400092799908634806622334906695224044652158992864203435098858422692019340575496840904812955522654754650713532842543496616084954788090727649930252702815067862810825243222979985391759845188868387004477101866772159439708514664612871148749531862180941719676843144666435175837688436786081446319641912566574047718699160915550910878919431253671945651261878486910876729910565595155159739659034383628124629118117760949411880105946336671039049777312004243578115790429823045072038322781246413671297959415082918378213212876890545963586369344879749784841123274921331663162812456388238288715648447883142417650147980187858215768793063001153788998014623690135803753306246148576074932567807682651045738059018831237617271889933790487113395588485234240255002352200613574914318259142479829367775490496399350755839668967578364316618369307625603528602940662803255416535431518013714821941772672244005268401996533334184004345525296592918502940131600651124395297874364222806977720437363717873457948420238745151249157913139411148608416429347958793681868609689684640858334131017858142710955416293375915178392341303110543328703526599993904966822112768158316511246866451167351378214345336650598328347443536290312393672084593164394941881138607974670134709640378534907149089842317891739783650654751982883367395714360000003439863363212091718954899055748693397700245632475954504411422582410783866837655467400137324322809113692670682805397549111166171102397437749479335174036135005397581475520834285772800986189401984375446435081498218360112577632447389452051636938585136484259964518361856989088721789764694721246807900330925083496645841656554261294195108847197209106605105540933731954888406444080280579549008076040034154662137669606444293774985897353625591959618552448187940317374508256072895120945456562159540405425814886929842786582357673195799285293120866275922366115137445767916063621675267440451221051052090834707443986137829082352772895849625656881972792768694795806100573787084121444815034797422312103295359297822377134077549545477791813823542607184617108389097825964406170543546968567030745411634244134486308676327949177682923093183221341455482591367202823284396549001805653203960795517074496039006696990334199278212696767771835209083959545341866777944872740383733381985235884202840150981579594685874537989503257362809837592216229258598599123843993575573285028613155970362934249814178056461615863415338635077223269996508860870999964899373049307170967888740149746147542880387421250689212155876692242387434701120990859082164073576380817386959755176083877600277517253037133445654852635661720197563001580049790223419586738061442401502436288957503206533690825756785507020555105572381878574650371086308158185862815883054564662297694803970618265491385181326737485227188267917919091354407852685476254126683398240534022469989966652573155637645862251862823092085424412805997628505488913098331761884983352975136073772030571342739638126588567405013841074788943393996603591853934198416322617654857376671943132840050626295140357877264680649549355746326408186979718630218760025813995719923601345374229758918285167511358171472625828596940798518571870075823122317068134867930884899275181661399609753105295773584618525865211893339375771859916335112163441037910451845019023066893064178977808158101360449495409665363660370075881004450265734935127707426742578608784898185628869980851665713320835842613381142623855420315774246613108873106318111989880289722849790551075148403702290580483052731884959994156606537314021296702220821915862905952604040620011815269664910068587592655660567562963361434230232810747488395040380984981860056164646099819257616235478710913832967563761506732550860683433720438748186791668975746563456020002562889601191100980453350423842063824039434163502977688802779835087481178298349417211674919425601608685332435385951152061809031241698182079314615062073826097180458265687043623935757495737332781578904386011378078508110273049446611821957450170106059384336519458628360682108585130499820420578458577175933849015564447305834515291412561679970569657426139901681932056241927977282026714297258700193234337873153939403115411184101414292741703537542003698760608765500109345299007034032401334806388514095769557147190364152027721127070187421548123931953220997506553022646844227700020589045922742423904937051507367764629844971682121994198274794049092601715727439368569721862936007387077810797440975556627807371228030350048829843919546433753355787895064018998685060281902452191177018634505171087023903398550540704454189088472042376499749035038518949505897971286631644699407490959473411581934618336692169573605081585080837952036335619947691937965065016808710250735070825260046821242820434367245824478859256555487861614478717581068572356895150707602217433511627331709472765932413249132702425519391509083601346239612335001086614623850633127072987745618984384288764099836164964775714638573247333226653894523588365972955159905187411779288608760239306160016168434070611663449248395156319152882728822831375458678269830696691220130954815935450754923554167766876455212545681242936427474153815692219503331560151614492247512488957534835926226263545406704767033866410025277276800886383266629488582740369655329362236090572479794734434077704284318507901973469071141230364111729224929307731939309795452877412451183953480382210373644697046967493042810911797232448615413264031578430955396671061468083815548947146733652483679138566431084747848676243012018489329109615281108087617422779131629345494425395422727309645057976122885347393189600810965202090151104579377602529543130188938184010247010134929317443562883578609861545691161669857388024973756940558138630581099823372565164920155443216861690537054630176154809626620800633059320775897175589925862195462096455464624399535391743228225433267174308492508396461328929584567927365409119947616225155964704061297047759818551878441419948614013153859322060745185909608884280218943358691959604936409651570327527570641500776261323783648149005245481413195989296398441371781402764122087644989688629798910870164270169014007825748311598976330612951195680427485317886333041169767175063822135213839779138443325644288490872919067009802496281560626258636942322658490628628035057282983101266919109637258378149363774960594515216932644945188292639525772348420077356021656909077097264985642831778694777804964343991762549216500608626285329471055602670413384500507827390640287529864161287496473708235188892189612641279553536442286955430551308700009878557534223100547153412810957024870812654319123261956462149376527526356402127388765103883255007364899937167183280028398832319373301564123277185395654932422977953016534830128490677845037490891749347389015649588574802194996722621185874361039774946338633057887487405540005440439344888192044102134790034598411927024921557026873700970995205391930979319495883265922171508324621942300185974396706491149559411733728199869021311629886680267446443489233020607003821262841723679627307191405008084085703978151998148822390059948911946474438682533745889962375133378280532928272016815977970066488394482446332210928320504045983008943565954267256879714918703447338237767914829203283196838105907715727191903042365315650957464549643425328069510396558733549803850995143463506175361480050195045201350200180281506933241918267855737764414097080945745624854867704904368368717590918057269794010465019484853146726642978667687697789291431128505043098192949736165944259471754765135205245072597538577958372797702972231435199958499522344049394502115428867244188717409524554771867484911475031801773304689909317974472957035192387686405544278134169807249382219749124257510162187439772902147704638010731470653154201300583810458905006764557332998149945854655105526374914354195867992595981412218735238407957416123372264063860431988936249867649693592569592128495906254446474331759999685163660305216426770428154681777589339252115538590526823311608302751194384823861552852465010329467297198112105314125898165100120742688143577590825227466863206188376830450921784582526239594189673003640808624233657620979111641766331328852352062487922978959456450333733139422384778582717195412347860434376165241568717943562570215636666680088531006728947033079540804583324192188488870712275670333173939262509073556164513677064199539111948881240659821685787131385056850623094155206877987539740658484250135205615103489821873770245063583314243624807432542464195984647411575625441010389671576677263196442524931941806472423789334668561083789808830313571333157729435664956078125304917594015895146954965223118559669048559467607968190167266634650186182955669893965019614544401768162810604465068448139561667220729261210164692339016793399632833013163850830967942792934551268435760356901970523138364640961311774904600772840862214747547653221505518116489887879087780918009050706040061220010051271575991225725282523378026809030528461581739558198122397010092017202251606352922464781615533532275453264543087093320924631855976580561717446840450048285353396546862678852330044967795580761661801833668792312510460809773895565488962815089519622093675058841609752282328250433712970186608193748968699961301486924694482420723632912367052542145464162968910442981633373266871675946715392611950649224725627254543274193495995569590243279097174392258098103601486364409101491734183079646345064833303404765711827040276868271418084574998493392039317445402616663674646668754385093967129918067471909885312710726724428584870694307099756567949198418996425748884764622030325637751112534060087936904565779272035205921345924272965206683338510673615276261016026647772485083344719891986802656197236420847504962661607797092906844757798251795569758235084371746103310387911789239441630112634077535773520558040066982523191225570519133631407211349723226549151062961739050617857127509403623146700931176133132018631158730886798239298009805089491510788371194099750375473674305745187265414016446924576792185753680363289139664155342066705623272936001177781498886100830877849571709880858667023104043242526785955562077310543072298032125941107957349146684680220501816192150766649106862033378713826058987655210423668198670177861672671972374156917880001690656659046965316154923604061891820982414006103779407166342002735828911994182647812782659666207030384795881442790246669264032799404016800137293477301530941805070587421153284642203006550763966756168318897005152026656649929417382840327305940740147117478464839241225676523593418554066440983706083636457657081801664285044258224551650808864421212113914352453935225522162483791737330329812349528984098613273709957407786789349311975204237925022851375880436791854547836416773151821457226504640800104202100410766027807729152555503218182387221708112766208665317651926458452495269685376314437998340336947124447247796973890514941120010934140073794061859447165516612674930799374705772930521750426383798367668159183589049652163726492960837147204067428996276720315410211504333742057182854090136325721437592054640471894328548696883599785122262130812989581571391597464534806099601555877223193450760315411663112963843719400333736013305526352571490454327925190794007111504785378036370897340146753465517470747096935814912797188187854376797751675927822300312945518595042883902735494672667647506072643698761394806879080593531793001711000214417701504495496412454361656210150919997862972495905809191825255486358703529320142005857057855419217730505342687533799076038746689684283402648733290888881745453047194740939258407362058242849349024756883352446212456101562729065130618520732925434179252299417447855189995098959999877410951464170076989305620163502192692653166599093238118295411937545448509428621839424186218067457128099385258842631930670182098008050900019819621758458932516877698594110522845465835679362969619219080897536813210484518784516230623911878024604050824909336069998094776253792973597037759066145994638578378211017122446355845171941670344732162722443265914858595797823752976323442911242311368603724514438765801271594060878788638511089680883165505046309006148832545452819908256238805872042843941834687865142541377686054291079721004271658
diff --git a/src/pkg/compress/testdata/pi.txt b/src/pkg/compress/testdata/pi.txt
index 58d8f3b6d..ca99bbc2a 100644
--- a/src/pkg/compress/testdata/pi.txt
+++ b/src/pkg/compress/testdata/pi.txt
@@ -1 +1 @@
-3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275900994657640789512694683983525957098258226205224894077267194782684826014769909026401363944374553050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382686838689427741559918559252459539594310499725246808459872736446958486538367362226260991246080512438843904512441365497627807977156914359977001296160894416948685558484063534220722258284886481584560285060168427394522674676788952521385225499546667278239864565961163548862305774564980355936345681743241125150760694794510965960940252288797108931456691368672287489405601015033086179286809208747609178249385890097149096759852613655497818931297848216829989487226588048575640142704775551323796414515237462343645428584447952658678210511413547357395231134271661021359695362314429524849371871101457654035902799344037420073105785390621983874478084784896833214457138687519435064302184531910484810053706146806749192781911979399520614196634287544406437451237181921799983910159195618146751426912397489409071864942319615679452080951465502252316038819301420937621378559566389377870830390697920773467221825625996615014215030680384477345492026054146659252014974428507325186660021324340881907104863317346496514539057962685610055081066587969981635747363840525714591028970641401109712062804390397595156771577004203378699360072305587631763594218731251471205329281918261861258673215791984148488291644706095752706957220917567116722910981690915280173506712748583222871835209353965725121083579151369882091444210067510334671103141267111369908658516398315019701651511685171437657618351556508849099898599823873455283316355076479185358932261854896321329330898570642046752590709154814165498594616371802709819943099244889575712828905923233260972997120844335732654893823911932597463667305836041428138830320382490375898524374417029132765618093773444030707469211201913020330380197621101100449293215160842444859637669838952286847831235526582131449576857262433441893039686426243410773226978028073189154411010446823252716201052652272111660396665573092547110557853763466820653109896526918620564769312570586356620185581007293606598764861179104533488503461136576867532494416680396265797877185560845529654126654085306143444318586769751456614068007002378776591344017127494704205622305389945613140711270004078547332699390814546646458807972708266830634328587856983052358089330657574067954571637752542021149557615814002501262285941302164715509792592309907965473761255176567513575178296664547791745011299614890304639947132962107340437518957359614589019389713111790429782856475032031986915140287080859904801094121472213179476477726224142548545403321571853061422881375850430633217518297986622371721591607716692547487389866549494501146540628433663937900397692656721463853067360965712091807638327166416274888800786925602902284721040317211860820419000422966171196377921337575114959501566049631862947265473642523081770367515906735023507283540567040386743513622224771589150495309844489333096340878076932599397805419341447377441842631298608099888687413260472156951623965864573021631598193195167353812974167729478672422924654366800980676928238280689964004824354037014163149658979409243237896907069779422362508221688957383798623001593776471651228935786015881617557829735233446042815126272037343146531977774160319906655418763979293344195215413418994854447345673831624993419131814809277771038638773431772075456545322077709212019051660962804909263601975988281613323166636528619326686336062735676303544776280350450777235547105859548702790814356240145171806246436267945612753181340783303362542327839449753824372058353114771199260638133467768796959703098339130771098704085913374641442822772634659470474587847787201927715280731767907707157213444730605700733492436931138350493163128404251219256517980694113528013147013047816437885185290928545201165839341965621349143415956258658655705526904965209858033850722426482939728584783163057777560688876446248246857926039535277348030480290058760758251047470916439613626760449256274204208320856611906254543372131535958450687724602901618766795240616342522577195429162991930645537799140373404328752628889639958794757291746426357455254079091451357111369410911939325191076020825202618798531887705842972591677813149699009019211697173727847684726860849003377024242916513005005168323364350389517029893922334517220138128069650117844087451960121228599371623130171144484640903890644954440061986907548516026327505298349187407866808818338510228334508504860825039302133219715518430635455007668282949304137765527939751754613953984683393638304746119966538581538420568533862186725233402830871123282789212507712629463229563989898935821167456270102183564622013496715188190973038119800497340723961036854066431939509790190699639552453005450580685501956730229219139339185680344903982059551002263535361920419947455385938102343955449597783779023742161727111723643435439478221818528624085140066604433258885698670543154706965747458550332323342107301545940516553790686627333799585115625784322988273723198987571415957811196358330059408730681216028764962867446047746491599505497374256269010490377819868359381465741268049256487985561453723478673303904688383436346553794986419270563872931748723320837601123029911367938627089438799362016295154133714248928307220126901475466847653576164773794675200490757155527819653621323926406160136358155907422020203187277605277219005561484255518792530343513984425322341576233610642506390497500865627109535919465897514131034822769306247435363256916078154781811528436679570611086153315044521274739245449454236828860613408414863776700961207151249140430272538607648236341433462351897576645216413767969031495019108575984423919862916421939949072362346468441173940326591840443780513338945257423995082965912285085558215725031071257012668302402929525220118726767562204154205161841634847565169998116141010029960783869092916030288400269104140792886215078424516709087000699282120660418371806535567252532567532861291042487761825829765157959847035622262934860034158722980534989650226291748788202734209222245339856264766914905562842503912757710284027998066365825488926488025456610172967026640765590429099456815065265305371829412703369313785178609040708667114965583434347693385781711386455873678123014587687126603489139095620099393610310291616152881384379099042317473363948045759314931405297634757481193567091101377517210080315590248530906692037671922033229094334676851422144773793937517034436619910403375111735471918550464490263655128162288244625759163330391072253837421821408835086573917715096828874782656995995744906617583441375223970968340800535598491754173818839994469748676265516582765848358845314277568790029095170283529716344562129640435231176006651012412006597558512761785838292041974844236080071930457618932349229279650198751872127267507981255470958904556357921221033346697499235630254947802490114195212382815309114079073860251522742995818072471625916685451333123948049470791191532673430282441860414263639548000448002670496248201792896476697583183271314251702969234889627668440323260927524960357996469256504936818360900323809293459588970695365349406034021665443755890045632882250545255640564482465151875471196218443965825337543885690941130315095261793780029741207665147939425902989695946995565761218656196733786236256125216320862869222103274889218654364802296780705765615144632046927906821207388377814233562823608963208068222468012248261177185896381409183903673672220888321513755600372798394004152970028783076670944474560134556417254370906979396122571429894671543578468788614445812314593571984922528471605049221242470141214780573455105008019086996033027634787081081754501193071412233908663938339529425786905076431006383519834389341596131854347546495569781038293097164651438407007073604112373599843452251610507027056235266012764848308407611830130527932054274628654036036745328651057065874882256981579367897669742205750596834408697350201410206723585020072452256326513410559240190274216248439140359989535394590944070469120914093870012645600162374288021092764579310657922955249887275846101264836999892256959688159205600101655256375678
+3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275900994657640789512694683983525957098258226205224894077267194782684826014769909026401363944374553050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382686838689427741559918559252459539594310499725246808459872736446958486538367362226260991246080512438843904512441365497627807977156914359977001296160894416948685558484063534220722258284886481584560285060168427394522674676788952521385225499546667278239864565961163548862305774564980355936345681743241125150760694794510965960940252288797108931456691368672287489405601015033086179286809208747609178249385890097149096759852613655497818931297848216829989487226588048575640142704775551323796414515237462343645428584447952658678210511413547357395231134271661021359695362314429524849371871101457654035902799344037420073105785390621983874478084784896833214457138687519435064302184531910484810053706146806749192781911979399520614196634287544406437451237181921799983910159195618146751426912397489409071864942319615679452080951465502252316038819301420937621378559566389377870830390697920773467221825625996615014215030680384477345492026054146659252014974428507325186660021324340881907104863317346496514539057962685610055081066587969981635747363840525714591028970641401109712062804390397595156771577004203378699360072305587631763594218731251471205329281918261861258673215791984148488291644706095752706957220917567116722910981690915280173506712748583222871835209353965725121083579151369882091444210067510334671103141267111369908658516398315019701651511685171437657618351556508849099898599823873455283316355076479185358932261854896321329330898570642046752590709154814165498594616371802709819943099244889575712828905923233260972997120844335732654893823911932597463667305836041428138830320382490375898524374417029132765618093773444030707469211201913020330380197621101100449293215160842444859637669838952286847831235526582131449576857262433441893039686426243410773226978028073189154411010446823252716201052652272111660396665573092547110557853763466820653109896526918620564769312570586356620185581007293606598764861179104533488503461136576867532494416680396265797877185560845529654126654085306143444318586769751456614068007002378776591344017127494704205622305389945613140711270004078547332699390814546646458807972708266830634328587856983052358089330657574067954571637752542021149557615814002501262285941302164715509792592309907965473761255176567513575178296664547791745011299614890304639947132962107340437518957359614589019389713111790429782856475032031986915140287080859904801094121472213179476477726224142548545403321571853061422881375850430633217518297986622371721591607716692547487389866549494501146540628433663937900397692656721463853067360965712091807638327166416274888800786925602902284721040317211860820419000422966171196377921337575114959501566049631862947265473642523081770367515906735023507283540567040386743513622224771589150495309844489333096340878076932599397805419341447377441842631298608099888687413260472156951623965864573021631598193195167353812974167729478672422924654366800980676928238280689964004824354037014163149658979409243237896907069779422362508221688957383798623001593776471651228935786015881617557829735233446042815126272037343146531977774160319906655418763979293344195215413418994854447345673831624993419131814809277771038638773431772075456545322077709212019051660962804909263601975988281613323166636528619326686336062735676303544776280350450777235547105859548702790814356240145171806246436267945612753181340783303362542327839449753824372058353114771199260638133467768796959703098339130771098704085913374641442822772634659470474587847787201927715280731767907707157213444730605700733492436931138350493163128404251219256517980694113528013147013047816437885185290928545201165839341965621349143415956258658655705526904965209858033850722426482939728584783163057777560688876446248246857926039535277348030480290058760758251047470916439613626760449256274204208320856611906254543372131535958450687724602901618766795240616342522577195429162991930645537799140373404328752628889639958794757291746426357455254079091451357111369410911939325191076020825202618798531887705842972591677813149699009019211697173727847684726860849003377024242916513005005168323364350389517029893922334517220138128069650117844087451960121228599371623130171144484640903890644954440061986907548516026327505298349187407866808818338510228334508504860825039302133219715518430635455007668282949304137765527939751754613953984683393638304746119966538581538420568533862186725233402830871123282789212507712629463229563989898935821167456270102183564622013496715188190973038119800497340723961036854066431939509790190699639552453005450580685501956730229219139339185680344903982059551002263535361920419947455385938102343955449597783779023742161727111723643435439478221818528624085140066604433258885698670543154706965747458550332323342107301545940516553790686627333799585115625784322988273723198987571415957811196358330059408730681216028764962867446047746491599505497374256269010490377819868359381465741268049256487985561453723478673303904688383436346553794986419270563872931748723320837601123029911367938627089438799362016295154133714248928307220126901475466847653576164773794675200490757155527819653621323926406160136358155907422020203187277605277219005561484255518792530343513984425322341576233610642506390497500865627109535919465897514131034822769306247435363256916078154781811528436679570611086153315044521274739245449454236828860613408414863776700961207151249140430272538607648236341433462351897576645216413767969031495019108575984423919862916421939949072362346468441173940326591840443780513338945257423995082965912285085558215725031071257012668302402929525220118726767562204154205161841634847565169998116141010029960783869092916030288400269104140792886215078424516709087000699282120660418371806535567252532567532861291042487761825829765157959847035622262934860034158722980534989650226291748788202734209222245339856264766914905562842503912757710284027998066365825488926488025456610172967026640765590429099456815065265305371829412703369313785178609040708667114965583434347693385781711386455873678123014587687126603489139095620099393610310291616152881384379099042317473363948045759314931405297634757481193567091101377517210080315590248530906692037671922033229094334676851422144773793937517034436619910403375111735471918550464490263655128162288244625759163330391072253837421821408835086573917715096828874782656995995744906617583441375223970968340800535598491754173818839994469748676265516582765848358845314277568790029095170283529716344562129640435231176006651012412006597558512761785838292041974844236080071930457618932349229279650198751872127267507981255470958904556357921221033346697499235630254947802490114195212382815309114079073860251522742995818072471625916685451333123948049470791191532673430282441860414263639548000448002670496248201792896476697583183271314251702969234889627668440323260927524960357996469256504936818360900323809293459588970695365349406034021665443755890045632882250545255640564482465151875471196218443965825337543885690941130315095261793780029741207665147939425902989695946995565761218656196733786236256125216320862869222103274889218654364802296780705765615144632046927906821207388377814233562823608963208068222468012248261177185896381409183903673672220888321513755600372798394004152970028783076670944474560134556417254370906979396122571429894671543578468788614445812314593571984922528471605049221242470141214780573455105008019086996033027634787081081754501193071412233908663938339529425786905076431006383519834389341596131854347546495569781038293097164651438407007073604112373599843452251610507027056235266012764848308407611830130527932054274628654036036745328651057065874882256981579367897669742205750596834408697350201410206723585020072452256326513410559240190274216248439140359989535394590944070469120914093870012645600162374288021092764579310657922955249887275846101264836999892256959688159205600101655256375678566722796619885782794848855834397518744545512965634434803966420557982936804352202770984294232533022576341807039476994159791594530069752148293366555661567873640053666564165473217043903521329543529169414599041608753201868379370234888689479151071637852902345292440773659495630510074210871426134974595615138498713757047101787957310422969066670214498637464595280824369445789772330048764765241339075920434019634039114732023380715095222010682563427471646024335440051521266932493419673977041595683753555166730273900749729736354964533288869844061196496162773449518273695588220757355176651589855190986665393549481068873206859907540792342402300925900701731960362254756478940647548346647760411463233905651343306844953979070903023460461470961696886885014083470405460742958699138296682468185710318879065287036650832431974404771855678934823089431068287027228097362480939962706074726455399253994428081137369433887294063079261595995462624629707062594845569034711972996409089418059534393251236235508134949004364278527138315912568989295196427287573946914272534366941532361004537304881985517065941217352462589548730167600298865925786628561249665523533829428785425340483083307016537228563559152534784459818313411290019992059813522051173365856407826484942764411376393866924803118364453698589175442647399882284621844900877769776312795722672655562596282542765318300134070922334365779160128093179401718598599933849235495640057099558561134980252499066984233017350358044081168552653117099570899427328709258487894436460050410892266917835258707859512983441729535195378855345737426085902908176515578039059464087350612322611200937310804854852635722825768203416050484662775045003126200800799804925485346941469775164932709504934639382432227188515974054702148289711177792376122578873477188196825462981268685817050740272550263329044976277894423621674119186269439650671515779586756482399391760426017633870454990176143641204692182370764887834196896861181558158736062938603810171215855272668300823834046564758804051380801633638874216371406435495561868964112282140753302655100424104896783528588290243670904887118190909494533144218287661810310073547705498159680772009474696134360928614849417850171807793068108546900094458995279424398139213505586422196483491512639012803832001097738680662877923971801461343244572640097374257007359210031541508936793008169980536520276007277496745840028362405346037263416554259027601834840306811381855105979705664007509426087885735796037324514146786703688098806097164258497595138069309449401515422221943291302173912538355915031003330325111749156969174502714943315155885403922164097229101129035521815762823283182342548326111912800928252561902052630163911477247331485739107775874425387611746578671169414776421441111263583553871361011023267987756410246824032264834641766369806637857681349204530224081972785647198396308781543221166912246415911776732253264335686146186545222681268872684459684424161078540167681420808850280054143613146230821025941737562389942075713627516745731891894562835257044133543758575342698699472547031656613991999682628247270641336222178923903176085428943733935618891651250424404008952719837873864805847268954624388234375178852014395600571048119498842390606136957342315590796703461491434478863604103182350736502778590897578272731305048893989009923913503373250855982655867089242612429473670193907727130706869170926462548423240748550366080136046689511840093668609546325002145852930950000907151058236267293264537382104938724996699339424685516483261134146110680267446637334375340764294026682973865220935701626384648528514903629320199199688285171839536691345222444708045923966028171565515656661113598231122506289058549145097157553900243931535190902107119457300243880176615035270862602537881797519478061013715004489917210022201335013106016391541589578037117792775225978742891917915522417189585361680594741234193398420218745649256443462392531953135103311476394911995072858430658361935369329699289837914941939406085724863968836903265564364216644257607914710869984315733749648835292769328220762947282381537409961545598798259891093717126218283025848112389011968221429457667580718653806506487026133892822994972574530332838963818439447707794022843598834100358385423897354243956475556840952248445541392394100016207693636846776413017819659379971557468541946334893748439129742391433659360410035234377706588867781139498616478747140793263858738624732889645643598774667638479466504074111825658378878454858148962961273998413442726086061872455452360643153710112746809778704464094758280348769758948328241239292960582948619196670918958089833201210318430340128495116203534280144127617285830243559830032042024512072872535581195840149180969253395075778400067465526031446167050827682772223534191102634163157147406123850425845988419907611287258059113935689601431668283176323567325417073420817332230462987992804908514094790368878687894930546955703072619009502076433493359106024545086453628935456862958531315337183868265617862273637169757741830239860065914816164049449650117321313895747062088474802365371031150898427992754426853277974311395143574172219759799359685252285745263796289612691572357986620573408375766873884266405990993505000813375432454635967504844235284874701443545419576258473564216198134073468541117668831186544893776979566517279662326714810338643913751865946730024434500544995399742372328712494834706044063471606325830649829795510109541836235030309453097335834462839476304775645015008507578949548931393944899216125525597701436858943585877526379625597081677643800125436502371412783467926101995585224717220177723700417808419423948725406801556035998390548985723546745642390585850216719031395262944554391316631345308939062046784387785054239390524731362012947691874975191011472315289326772533918146607300089027768963114810902209724520759167297007850580717186381054967973100167870850694207092232908070383263453452038027860990556900134137182368370991949516489600755049341267876436746384902063964019766685592335654639138363185745698147196210841080961884605456039038455343729141446513474940784884423772175154334260306698831768331001133108690421939031080143784334151370924353013677631084913516156422698475074303297167469640666531527035325467112667522460551199581831963763707617991919203579582007595605302346267757943936307463056901080114942714100939136913810725813781357894005599500183542511841721360557275221035268037357265279224173736057511278872181908449006178013889710770822931002797665935838758909395688148560263224393726562472776037890814458837855019702843779362407825052704875816470324581290878395232453237896029841669225489649715606981192186584926770403956481278102179913217416305810554598801300484562997651121241536374515005635070127815926714241342103301566165356024733807843028655257222753049998837015348793008062601809623815161366903341111386538510919367393835229345888322550887064507539473952043968079067086806445096986548801682874343786126453815834280753061845485903798217994599681154419742536344399602902510015888272164745006820704193761584547123183460072629339550548239557137256840232268213012476794522644820910235647752723082081063518899152692889108455571126603965034397896278250016110153235160519655904211844949907789992007329476905868577878720982901352956613978884860509786085957017731298155314951681467176959760994210036183559138777817698458758104466283998806006162298486169353373865787735983361613384133853684211978938900185295691967804554482858483701170967212535338758621582310133103877668272115726949518179589754693992642197915523385766231676275475703546994148929041301863861194391962838870543677743224276809132365449485366768000001065262485473055861598999140170769838548318875014293890899506854530765116803337322265175662207526951791442252808165171667766727930354851542040238174608923283917032754257508676551178593950027933895920576682789677644531840404185540104351348389531201326378369283580827193783126549617459970567450718332065034556644034490453627560011250184335607361222765949278393706478426456763388188075656121689605041611390390639601620221536849410926053876887148379895599991120991646464411918568277004574243434021672276445589330127781586869525069499364610175685060167145354315814801054588605645501332037586454858403240298717093480910556211671546848477803944756979804263180991756422809873998766973237695737015808068229045992123661689025962730430679316531149401764737693873514093361833216142802149763399189835484875625298752423873077559555955465196394401821840998412489826236737714672260616336432964063357281070788758164043814850188411431885988276944901193212968271588841338694346828590066640806314077757725705630729400492940302420498416565479736705485580445865720227637840466823379852827105784319753541795011347273625774080213476826045022851579795797647467022840999561601569108903845824502679265942055503958792298185264800706837650418365620945554346135134152570065974881916341359556719649654032187271602648593049039787489589066127250794828276938953521753621850796297785146188432719223223810158744450528665238022532843891375273845892384422535472653098171578447834215822327020690287232330053862163479885094695472004795231120150432932266282727632177908840087861480221475376578105819702226309717495072127248479478169572961423658595782090830733233560348465318730293026659645013718375428897557971449924654038681799213893469244741985097334626793321072686870768062639919361965044099542167627840914669856925715074315740793805323925239477557441591845821562518192155233709607483329234921034514626437449805596103307994145347784574699992128599999399612281615219314888769388022281083001986016549416542616968586788372609587745676182507275992950893180521872924610867639958916145855058397274209809097817293239301067663868240401113040247007350857828724627134946368531815469690466968693925472519413992914652423857762550047485295476814795467007050347999588867695016124972282040303995463278830695976249361510102436555352230690612949388599015734661023712235478911292547696176005047974928060721268039226911027772261025441492215765045081206771735712027180242968106203776578837166909109418074487814049075517820385653909910477594141321543284406250301802757169650820964273484146957263978842560084531214065935809041271135920041975985136254796160632288736181367373244506079244117639975974619383584574915988097667447093006546342423460634237474666080431701260052055928493695941434081468529815053947178900451835755154125223590590687264878635752541911288877371766374860276606349603536794702692322971868327717393236192007774522126247518698334951510198642698878471719396649769070825217423365662725928440620430214113719922785269984698847702323823840055655517889087661360130477098438611687052310553149162517283732728676007248172987637569816335415074608838663640693470437206688651275688266149730788657015685016918647488541679154596507234287730699853713904300266530783987763850323818215535597323530686043010675760838908627049841888595138091030423595782495143988590113185835840667472370297149785084145853085781339156270760356390763947311455495832266945702494139831634332378975955680856836297253867913275055542524491943589128405045226953812179131914513500993846311774017971512283785460116035955402864405902496466930707769055481028850208085800878115773817191741776017330738554758006056014337743299012728677253043182519757916792969965041460706645712588834697979642931622965520168797300035646304579308840327480771811555330909887025505207680463034608658165394876951960044084820659673794731680864156456505300498816164905788311543454850526600698230931577765003780704661264706021457505793270962047825615247145918965223608396645624105195510522357239739512881816405978591427914816542632892004281609136937773722299983327082082969955737727375667615527113922588055201898876201141680054687365580633471603734291703907986396522961312801782679717289822936070288069087768660593252746378405397691848082041021944719713869256084162451123980620113184541244782050110798760717155683154078865439041210873032402010685341947230476666721749869868547076781205124736792479193150856444775379853799732234456122785843296846647513336573692387201464723679427870042503255589926884349592876124007558756946413705625140011797133166207153715436006876477318675587148783989081074295309410605969443158477539700943988394914432353668539209946879645066533985738887866147629443414010498889931600512076781035886116602029611936396821349607501116498327856353161451684576956871090029997698412632665023477167286573785790857466460772283415403114415294188047825438761770790430001566986776795760909966936075594965152736349811896413043311662774712338817406037317439705406703109676765748695358789670031925866259410510533584384656023391796749267844763708474978333655579007384191473198862713525954625181604342253729962863267496824058060296421146386436864224724887283434170441573482481833301640566959668866769563491416328426414974533349999480002669987588815935073578151958899005395120853510357261373640343675347141048360175464883004078464167452167371904831096767113443494819262681110739948250607394950735031690197318521195526356325843390998224986240670310768318446607291248747540316179699411397387765899868554170318847788675929026070043212666179192235209382278788809886335991160819235355570464634911320859189796132791319756490976000139962344455350143464268604644958624769094347048293294140411146540923988344435159133201077394411184074107684981066347241048239358274019449356651610884631256785297769734684303061462418035852933159734583038455410337010916767763742762102137013548544509263071901147318485749233181672072137279355679528443925481560913728128406333039373562420016045664557414588166052166608738748047243391212955877763906969037078828527753894052460758496231574369171131761347838827194168606625721036851321566478001476752310393578606896111259960281839309548709059073861351914591819510297327875571049729011487171897180046961697770017913919613791417162707018958469214343696762927459109940060084983568425201915593703701011049747339493877885989417433031785348707603221982970579751191440510994235883034546353492349826883624043327267415540301619505680654180939409982020609994140216890900708213307230896621197755306659188141191577836272927461561857103721724710095214236964830864102592887457999322374955191221951903424452307535133806856807354464995127203174487195403976107308060269906258076020292731455252078079914184290638844373499681458273372072663917670201183004648190002413083508846584152148991276106513741539435657211390328574918769094413702090517031487773461652879848235338297260136110984514841823808120540996125274580881099486972216128524897425555516076371675054896173016809613803811914361143992106380050832140987604599309324851025168294467260666138151745712559754953580239983146982203613380828499356705575524712902745397762140493182014658008021566536067765508783804304134310591804606800834591136640834887408005741272586704792258319127415739080914383138456424150940849133918096840251163991936853225557338966953749026620923261318855891580832455571948453875628786128859004106006073746501402627824027346962528217174941582331749239683530136178653673760642166778137739951006589528877427662636841830680190804609849809469763667335662282915132352788806157768278159588669180238940333076441912403412022316368577860357276941541778826435238131905028087018575047046312933353757285386605888904583111450773942935201994321971171642235005644042979892081594307167019857469273848653833436145794634175922573898588001698014757420542995801242958105456510831046297282937584161162532562516572498078492099897990620035936509934721582965174135798491047111660791587436986541222348341887722929446335178653856731962559852026072947674072616767145573649812105677716893484917660771705277187601199908144113058645577910525684304811440261938402322470939249802933550731845890355397133088446174107959162511714864874468611247605428673436709046678468670274091881014249711149657817724279347070216688295610877794405048437528443375108828264771978540006509704033021862556147332117771174413350281608840351781452541964320309576018694649088681545285621346988355444560249556668436602922195124830910605377201980218310103270417838665447181260397190688462370857518080035327047185659499476124248110999288679158969049563947624608424065930948621507690314987020673533848349550836366017848771060809804269247132410009464014373603265645184566792456669551001502298330798496079949882497061723674493612262229617908143114146609412341593593095854079139087208322733549572080757165171876599449856937956238755516175754380917805280294642004472153962807463602113294255916002570735628126387331060058910652457080244749375431841494014821199962764531068006631183823761639663180931444671298615527598201451410275600689297502463040173514891945763607893528555053173314164570504996443890936308438744847839616840518452732884032345202470568516465716477139323775517294795126132398229602394548579754586517458787713318138752959809412174227300352296508089177705068259248822322154938048371454781647213976820963320508305647920482085920475499857320388876391601995240918938945576768749730856955958010659526503036266159750662225084067428898265907510637563569968211510949669744580547288693631020367823250182323708459790111548472087618212477813266330412076216587312970811230758159821248639807212407868878114501655825136178903070860870198975889807456643955157415363193191981070575336633738038272152798849350397480015890519420879711308051233933221903466249917169150948541401871060354603794643379005890957721180804465743962806186717861017156740967662080295766577051291209907944304632892947306159510430902221439371849560634056189342513057268291465783293340524635028929175470872564842600349629611654138230077313327298305001602567240141851520418907011542885799208121984493156999059182011819733500126187728036812481995877070207532406361259313438595542547781961142935163561223496661522614735399674051584998603552953329245752388810136202347624669055816438967863097627365504724348643071218494373485300606387644566272186661701238127715621379746149861328744117714552444708997144522885662942440230184791205478498574521634696448973892062401943518310088283480249249085403077863875165911302873958787098100772718271874529013972836614842142871705531796543076504534324600536361472618180969976933486264077435199928686323835088756683595097265574815431940195576850437248001020413749831872259677387154958399718444907279141965845930083942637020875635398216962055324803212267498911402678528599673405242031091797899905718821949391320753431707980023736590985375520238911643467185582906853711897952626234492483392496342449714656846591248918556629589329909035239233333647435203707701010843880032907598342170185542283861617210417603011645918780539367447472059985023582891833692922337323999480437108419659473162654825748099482509991833006976569367159689364493348864744213500840700660883597235039532340179582557036016936990988671132109798897070517280755855191269930673099250704070245568507786790694766126298082251633136399521170984528092630375922426742575599892892783704744452189363203489415521044597261883800300677617931381399162058062701651024458869247649246891924612125310275731390840470007143561362316992371694848132554200914530410371354532966206392105479824392125172540132314902740585892063217589494345489068463993137570910346332714153162232805522972979538018801628590735729554162788676498274186164218789885741071649069191851162815285486794173638906653885764229158342500673612453849160674137340173572779956341043326883569507814931378007362354180070619180267328551191942676091221035987469241172837493126163395001239599240508454375698507957046222664619000103500490183034153545842833764378111988556318777792537201166718539541835984438305203762819440761594106820716970302285152250573126093046898423433152732131361216582808075212631547730604423774753505952287174402666389148817173086436111389069420279088143119448799417154042103412190847094080254023932942945493878640230512927119097513536000921971105412096683111516328705423028470073120658032626417116165957613272351566662536672718998534199895236884830999302757419916463841427077988708874229277053891227172486322028898425125287217826030500994510824783572905691988555467886079462805371227042466543192145281760741482403827835829719301017888345674167811398954750448339314689630763396657226727043393216745421824557062524797219978668542798977992339579057581890622525473582205236424850783407110144980478726691990186438822932305382318559732869780922253529591017341407334884761005564018242392192695062083183814546983923664613639891012102177095976704908305081854704194664371312299692358895384930136356576186106062228705599423371631021278457446463989738188566746260879482018647487672727222062676465338099801966883680994159075776852639865146253336312450536402610569605513183813174261184420189088853196356986962795036738424313011331753305329802016688817481342988681585577810343231753064784983210629718425184385534427620128234570716988530518326179641178579608888150329602290705614476220915094739035946646916235396809201394578175891088931992112260073928149169481615273842736264298098234063200244024495894456129167049508235812487391799648641133480324757775219708932772262349486015046652681439877051615317026696929704928316285504212898146706195331970269507214378230476875280287354126166391708245925170010714180854800636923259462019002278087409859771921805158532147392653251559035410209284665925299914353791825314545290598415817637058927906909896911164381187809435371521332261443625314490127454772695739393481546916311624928873574718824071503995009446731954316193855485207665738825139639163576723151005556037263394867208207808653734942440115799667507360711159351331959197120948964717553024531364770942094635696982226673775209945168450643623824211853534887989395673187806606107885440005508276570305587448541805778891719207881423351138662929667179643468760077047999537883387870348718021842437342112273940255717690819603092018240188427057046092622564178375265263358324240661253311529423457965569502506810018310900411245379015332966156970522379210325706937051090830789479999004999395322153622748476603613677697978567386584670936679588583788795625946464891376652199588286933801836011932368578558558195556042156250883650203322024513762158204618106705195330653060606501054887167245377942831338871631395596905832083416898476065607118347136218123246227258841990286142087284956879639325464285343075301105285713829643709990356948885285190402956047346131138263878897551788560424998748316382804046848618938189590542039889872650697620201995548412650005394428203930127481638158530396439925470201672759328574366661644110962566337305409219519675148328734808957477775278344221091073111351828046036347198185655572957144747682552857863349342858423118749440003229690697758315903858039353521358860079600342097547392296733310649395601812237812854584317605561733861126734780745850676063048229409653041118306671081893031108871728167519579675347188537229309616143204006381322465841111157758358581135018569047815368938137718472814751998350504781297718599084707621974605887423256995828892535041937958260616211842368768511418316068315867994601652057740529423053601780313357263267054790338401257305912339601880137825421927094767337191987287385248057421248921183470876629667207272325650565129333126059505777727542471241648312832982072361750574673870128209575544305968395555686861188397135522084452852640081252027665557677495969626612604565245684086139238265768583384698499778726706555191854468698469478495734622606294219624557085371272776523098955450193037732166649182578154677292005212667143463209637891852323215018976126034373684067194193037746880999296877582441047878123266253181845960453853543839114496775312864260925211537673258866722604042523491087026958099647595805794663973419064010036361904042033113579336542426303561457009011244800890020801478056603710154122328891465722393145076071670643556827437743965789067972687438473076346451677562103098604092717090951280863090297385044527182892749689212106670081648583395537735919136950153162018908887484210798706899114804669270650940762046502772528650728905328548561433160812693005693785417861096969202538865034577183176686885923681488475276498468821949739729707737187188400414323127636504814531122850990020742409255859252926103021067368154347015252348786351643976235860419194129697690405264832347009911154242601273438022089331096686367898694977994001260164227609260823493041180643829138347354679725399262338791582998486459271734059225620749105308531537182911681637219395188700957788181586850464507699343940987433514431626330317247747486897918209239480833143970840673084079589358108966564775859905563769525232653614424780230826811831037735887089240613031336477371011628214614661679404090518615260360092521947218890918107335871964142144478654899528582343947050079830388538860831035719306002771194558021911942899922722353458707566246926177663178855144350218287026685610665003531050216318206017609217984684936863161293727951873078972637353717150256378733579771808184878458866504335824377004147710414934927438457587107159731559439426412570270965125108115548247939403597681188117282472158250109496096625393395380922195591918188552678062149923172763163218339896938075616855911752998450132067129392404144593862398809381240452191484831646210147389182510109096773869066404158973610476436500068077105656718486281496371118832192445663945814491486165500495676982690308911185687986929470513524816091743243015383684707292898982846022237301452655679898627767968091469798378268764311598832109043715611299766521539635464420869197567370005738764978437686287681792497469438427465256316323005551304174227341646455127812784577772457520386543754282825671412885834544435132562054464241011037955464190581168623059644769587054072141985212106734332410756767575818456990693046047522770167005684543969234041711089888993416350585157887353430815520811772071880379104046983069578685473937656433631979786803671873079693924236321448450354776315670255390065423117920153464977929066241508328858395290542637687668968805033317227800185885069736232403894700471897619347344308437443759925034178807972235859134245813144049847701732361694719765715353197754997162785663119046912609182591249890367654176979903623755286526375733763526969344354400473067198868901968147428767790866979688522501636949856730217523132529265375896415171479559538784278499866456302878831962099830494519874396369070682762657485810439112232618794059941554063270131989895703761105323606298674803779153767511583043208498720920280929752649812569163425000522908872646925284666104665392171482080130502298052637836426959733707053922789153510568883938113249757071331029504430346715989448786847116438328050692507766274500122003526203709466023414648998390252588830148678162196775194583167718762757200505439794412459900771152051546199305098386982542846407255540927403132571632640792934183342147090412542533523248021932277075355546795871638358750181593387174236061551171013123525633485820365146141870049205704372018261733194715700867578539336078622739558185797587258744102542077105475361294047460100094095444959662881486915903899071865980563617137692227290764197755177720104276496949611056220592502420217704269622154958726453989227697660310524980855759471631075870133208861463266412591148633881220284440694169488261529577625325019870359870674380469821942056381255833436421949232275937221289056420943082352544084110864545369404969271494003319782861318186188811118408257865928757426384450059944229568586460481033015388911499486935436030221810943466764000022362550573631294626296096198760564259963946138692330837196265954739234624134597795748524647837980795693198650815977675350553918991151335252298736112779182748542008689539658359421963331502869561192012298889887006079992795411188269023078913107603617634779489432032102773359416908650071932804017163840644987871753756781185321328408216571107549528294974936214608215583205687232185574065161096274874375098092230211609982633033915469494644491004515280925089745074896760324090768983652940657920198315265410658136823791984090645712468948470209357761193139980246813405200394781949866202624008902150166163813538381515037735022966074627952910384068685569070157516624192987244482719429331004854824454580718897633003232525821581280327467962002814762431828622171054352898348208273451680186131719593324711074662228508710666117703465352839577625997744672185715816126411143271794347885990892808486694914139097716736900277758502686646540565950394867841110790116104008572744562938425494167594605487117235946429105850909950214958793112196135908315882620682332156153086833730838173279328196983875087083483880463884784418840031847126974543709373298362402875197920802321878744882872843727378017827008058782410749357514889978911739746129320351081432703251409030487462262942344327571260086642508333187688650756429271605525289544921537651751492196367181049435317858383453865255656640657251363575064353236508936790431702597878177190314867963840828810209461490079715137717099061954969640070867667102330048672631475510537231757114322317411411680622864206388906210192355223546711662137499693269321737043105987225039456574924616978260970253359475020913836673772894438696400028110344026084712899000746807764844088711341352503367877316797709372778682166117865344231732264637847697875144332095340001650692130546476890985050203015044880834261845208730530973189492916425322933612431514306578264070283898409841602950309241897120971601649265613413433422298827909921786042679812457285345801338260995877178113102167340256562744007296834066198480676615805021691833723680399027931606420436812079900316264449146190219458229690992122788553948783538305646864881655562294315673128274390826450611628942803501661336697824051770155219626522725455850738640585299830379180350432876703809252167907571204061237596327685674845079151147313440001832570344920909712435809447900462494313455028900680648704293534037436032625820535790118395649089354345101342969617545249573960621490288728932792520696535386396443225388327522499605986974759882329916263545973324445163755334377492928990581175786355555626937426910947117002165411718219750519831787137106051063795558588905568852887989084750915764639074693619881507814685262133252473837651192990156109189777922008705793396463827490680698769168197492365624226087154176100430608904377976678519661891404144925270480881971498801542057787006521594009289777601330756847966992955433656139847738060394368895887646054983871478968482805384701730871117761159663505039979343869339119789887109156541709133082607647406305711411098839388095481437828474528838368079418884342666222070438722887413947801017721392281911992365405516395893474263953824829609036900288359327745855060801317988407162446563997948275783650195514221551339281978226984278638391679715091262410548725700924070045488485692950448110738087996547481568913935380943474556972128919827177020766613602489581468119133614121258783895577357194986317210844398901423948496659251731388171602663261931065366535041473070804414939169363262373767777095850313255990095762731957308648042467701212327020533742667053142448208168130306397378736642483672539837487690980602182785786216512738563513290148903509883270617258932575363993979055729175160097615459044771692265806315111028038436017374742152476085152099016158582312571590733421736576267142390478279587281505095633092802668458937649649770232973641319060982740633531089792464242134583740901169391964250459128813403498810635400887596820054408364386516617880557608956896727531538081942077332597917278437625661184319891025007491829086475149794003160703845549465385946027452447466812314687943441610993338908992638411847425257044572517459325738989565185716575961481266020310797628254165590506042479114016957900338356574869252800743025623419498286467914476322774005529460903940177536335655471931000175430047504719144899841040015867946179241610016454716551337074073950260442769538553834397550548871099785205401175169747581344926079433689543783221172450687344231989878844128542064742809735625807066983106979935260693392135685881391214807354728463227784908087002467776303605551232386656295178853719673034634701222939581606792509153217489030840886516061119011498443412350124646928028805996134283511884715449771278473361766285062169778717743824362565711779450064477718370221999106695021656757644044997940765037999954845002710665987813603802314126836905783190460792765297277694043613023051787080546511542469395265127101052927070306673024447125973939950514628404767431363739978259184541176413327906460636584152927019030276017339474866960348694976541752429306040727005059039503148522921392575594845078867977925253931765156416197168443524369794447355964260633391055126826061595726217036698506473281266724521989060549880280782881429796336696744124805982192146339565745722102298677599746738126069367069134081559412016115960190237753525556300606247983261249881288192937343476862689219239777833910733106588256813777172328315329082525092733047850724977139448333892552081175608452966590553940965568541706001179857293813998258319293679100391844099286575605993598910002969864460974714718470101531283762631146774209145574041815908800064943237855839308530828305476076799524357391631221886057549673832243195650655460852881201902363644712703748634421727257879503428486312944916318475347531435041392096108796057730987201352484075057637199253650470908582513936863463863368042891767107602111159828875539940120076013947033661793715396306139863655492213741597905119083588290097656647300733879314678913181465109316761575821351424860442292445304113160652700974330088499034675405518640677342603583409608605533747362760935658853109760994238347382222087292464497684560579562516765574088410321731345627735856052358236389532038534024842273371639123973215995440828421666636023296545694703577184873442034227706653837387506169212768015766181095420097708363604361110592409117889540338021426523948929686439808926114635414571535194342850721353453018315875628275733898268898523557799295727645229391567477566676051087887648453493636068278050564622813598885879259940946446041705204470046315137975431737187756039815962647501410906658866162180038266989961965580587208639721176995219466789857011798332440601811575658074284182910615193917630059194314434605154047710570054339000182453117733718955857603607182860506356479979004139761808955363669603162193113250223851791672055180659263518036251214575926238369348222665895576994660491938112486609099798128571823494006615552196112207203092277646200999315244273589488710576623894693889446495093960330454340842102462401048723328750081749179875543879387381439894238011762700837196053094383940063756116458560943129517597713935396074322792489221267045808183313764165818269562105872892447740035947009268662659651422050630078592002488291860839743732353849083964326147000532423540647042089499210250404726781059083644007466380020870126664209457181702946752278540074508552377720890581683918446592829417018288233014971554235235911774818628592967605048203864343108779562892925405638946621948268711042828163893975711757786915430165058602965217459581988878680408110328432739867198621306205559855266036405046282152306154594474489908839081999738747452969810776201487134000122535522246695409315213115337915798026979555710508507473874750758068765376445782524432638046143042889235934852961058269382103498000405248407084403561167817170512813378805705643450616119330424440798260377951198548694559152051960093041271007277849301555038895360338261929343797081874320949914159593396368110627557295278004254863060054523839151068998913578820019411786535682149118528207852130125518518493711503422159542244511900207393539627400208110465530207932867254740543652717595893500716336076321614725815407642053020045340183572338292661915308354095120226329165054426123619197051613839357326693760156914429944943744856809775696303129588719161129294681884936338647392747601226964158848900965717086160598147204467428664208765334799858222090619802173211614230419477754990738738567941189824660913091691772274207233367635032678340586301930193242996397204445179288122854478211953530898910125342975524727635730226281382091807439748671453590778633530160821559911314144205091447293535022230817193663509346865858656314855575862447818620108711889760652969899269328178705576435143382060141077329261063431525337182243385263520217735440715281898137698755157574546939727150488469793619500477720970561793913828989845327426227288647108883270173723258818244658436249580592560338105215606206155713299156084892064340303395262263451454283678698288074251422567451806184149564686111635404971897682154227722479474033571527436819409892050113653400123846714296551867344153741615042563256713430247655125219218035780169240326699541746087592409207004669340396510178134857835694440760470232540755557764728450751826890418293966113310160131119077398632462778219023650660374041606724962490137433217246454097412995570529142438208076098364823465973886691349919784013108015581343979194852830436739012482082444814128095443773898320059864909159505322857914576884962578665885999179867520554558099004556461178755249370124553217170194282884617402736649978475508294228020232901221630102309772151569446427909802190826689868834263071609207914085197695235553488657743425277531197247430873043619511396119080030255878387644206085044730631299277888942729189727169890575925244679660189707482960949190648764693702750773866432391919042254290235318923377293166736086996228032557185308919284403805071030064776847863243191000223929785255372375566213644740096760539439838235764606992465260089090624105904215453927904411529580345334500256244101006359530039598864466169595626351878060688513723462707997327233134693971456285542615467650632465676620279245208581347717608521691340946520307673391841147504140168924121319826881568664561485380287539331160232292555618941042995335640095786495340935115266454024418775949316930560448686420862757201172319526405023099774567647838488973464317215980626787671838005247696884084989185086149003432403476742686245952395890358582135006450998178244636087317754378859677672919526111213859194725451400301180503437875277664402762618941017576872680428176623860680477885242887430259145247073950546525135339459598789619778911041890292943818567205070964606263541732944649576612651953495701860015412623962286413897796733329070567376962156498184506842263690367849555970026079867996261019039331263768556968767029295371162528005543100786408728939225714512481135778627664902425161990277471090335933309304948380597856628844787441469841499067123764789582263294904679812089984857163571087831191848630254501620929805829208334813638405421720056121989353669371336733392464416125223196943471206417375491216357008573694397305979709719726666642267431117762176403068681310351899112271339724036887000996862922546465006385288620393800504778276912835603372548255793912985251506829969107754257647488325341412132800626717094009098223529657957997803018282428490221470748111124018607613415150387569830918652780658896682362523937845272634530420418802508442363190383318384550522367992357752929106925043261446950109861088899914658551881873582528164302520939285258077969737620845637482114433988162710031703151334402309526351929588680690821355853680161000213740851154484912685841268695899174149133820578492800698255195740201818105641297250836070356851055331787840829000041552511865779453963317538532092149720526607831260281961164858098684587525129997404092797683176639914655386108937587952214971731728131517932904431121815871023518740757222100123768721944747209349312324107065080618562372526732540733324875754482967573450019321902199119960797989373383673242576103938985349278777473980508080015544764061053522202325409443567718794565430406735896491017610775948364540823486130254718476485189575836674399791508512858020607820554462991723202028222914886959399729974297471155371858924238493855858595407438104882624648788053304271463011941589896328792678327322456103852197011130466587100500083285177311776489735230926661234588873102883515626446023671996644554727608310118788389151149340939344750073025855814756190881398752357812331342279866503522725367171230756861045004548970360079569827626392344107146584895780241408158405229536937499710665594894459246286619963556350652623405339439142111271810691052290024657423604130093691889255865784668461215679554256605416005071276641766056874274200329577160643448606201239821698271723197826816628249938714995449137302051843669076723577400053932662622760323659751718925901801104290384274185507894887438832703063283279963007200698012244365116394086922220745320244624121155804354542064215121585056896157356414313068883443185280853975927734433655384188340303517822946253702015782157373265523185763554098954033236382319219892171177449469403678296185920803403867575834111518824177439145077366384071880489358256868542011645031357633355509440319236720348651010561049872726472131986543435450409131859513145181276437310438972507004981987052176272494065214619959232142314439776546708351714749367986186552791715824080651063799500184295938799158350171580759883784962257398512129810326379376218322456594236685376799113140108043139732335449090824910499143325843298821033984698141715756010829706583065211347076803680695322971990599904451209087275776225351040902392888779424630483280319132710495478599180196967835321464441189260631526618167443193550817081875477050802654025294109218264858213857526688155584113198560022135158887210365696087515063187533002942118682221893775546027227291290504292259787710667873840000616772154638441292371193521828499824350920891801685572798156421858191197490985730570332667646460728757430565372602768982373259745084479649545648030771598153955827779139373601717422996027353102768719449444917939785144631597314435351850491413941557329382048542123508173912549749819308714396615132942045919380106231421774199184060180347949887691051557905554806953878540066453375981862846419905220452803306263695626490910827627115903856995051246529996062855443838330327638599800792922846659503551211245284087516229060262011857775313747949362055496401073001348853150735487353905602908933526400713274732621960311773433943673385759124508149335736911664541281788171454023054750667136518258284898099512139193995633241336556777098003081910272040997148687418134667006094051021462690280449159646545330107754695413088714165312544813061192407821188690056027781824235022696189344352547633573536485619363254417756613981703930632872166905722259745209192917262199844409646158269456380239502837121686446561785235565164127712826918688615572716201474934052276946595712198314943381622114006936307430444173284786101777743837977037231795255434107223445512555589998646183876764903972461167959018100035098928641204195163551108763204267612979826529425882951141275841262732790798807559751851576841264742209479721843309352972665210015662514552994745127631550917636730259462132930190402837954246323258550301096706922720227074863419005438302650681214142135057154175057508639907673946335146209082888934938376439399256900604067311422093312195936202982972351163259386772241477911629572780752395056251581603133359382311500518626890530658368129988108663263271980611271548858798093487912913707498230575929091862939195014721197586067270092547718025750337730799397134539532646195269996596385654917590458333585799102012713204583903200853878881633637685182083727885131175227769609787962142372162545214591281831798216044111311671406914827170981015457781939202311563871950805024679725792497605772625913328559726371211201905720771409148645074094926718035815157571514050397610963846755569298970383547314100223802583468767350129775413279532060971154506484212185936490997917766874774481882870632315515865032898164228288232746866106592732197907162384642153489852476216789050260998045266483929542357287343977680495774091449538391575565485459058976495198513801007958010783759945775299196700547602252552034453988712538780171960718164078124847847257912407824544361682345239570689514272269750431873633263011103053423335821609333191218806608268341428910415173247216053355849993224548730778822905252324234861531520976938461042582849714963475341837562003014915703279685301868631572488401526639835689563634657435321783493199825542117308467745297085839507616458229630324424328237737450517028560698067889521768198156710781633405266759539424926280756968326107495323390536223090807081455919837355377748742029039018142937311529334644468151212945097596534306284215319445727118614900017650558177095302468875263250119705209476159416768727784472000192789137251841622857783792284439084301181121496366424659033634194540657183544771912446621259392656620306888520055599121235363718226922531781458792593750441448933981608657900876165024635197045828895481793756681046474614105142498870252139936870509372305447734112641354892806841059107716677821238332810262185587751312721179344448201440425745083063944738363793906283008973306241380614589414227694747931665717623182472168350678076487573420491557628217583972975134478990696589532548940335615613167403276472469212505759116251529654568544633498114317670257295661844775487469378464233737238981920662048511894378868224807279352022501796545343757274163910791972952950812942922205347717304184477915673991738418311710362524395716152714669005814700002633010452643547865903290733205468338872078735444762647925297690170912007874183736735087713376977683496344252419949951388315074877537433849458259765560996555954318040920178497184685497370696212088524377013853757681416632722412634423982152941645378000492507262765150789085071265997036708726692764308377229685985169122305037462744310852934305273078865283977335246017463527703205938179125396915621063637625882937571373840754406468964783100704580613446731271591194608435935825987782835266531151065041623295329047772174083559349723758552138048305090009646676088301540612824308740645594431853413755220166305812111033453120745086824339432159043594430312431227471385842030390106070940315235556172767994160020393975099897629335325855575624808996691829864222677502360193257974726742578211119734709402357457222271212526852384295874273501563660093188045493338989741571490544182559738080871565281430102670460284316819230392535297795765862414392701549740879273131051636119137577008929564823323648298263024607975875767745377160102490804624301856524161756655600160859121534556267602192689982855377872583145144082654583484409478463178777374794653580169960779405568701192328608041130904629350871827125934668712766694873899824598527786499569165464029458935064964335809824765965165142090986755203808309203230487342703468288751604071546653834619611223013759451579252696743642531927390036038608236450762698827497618723575476762889950752114804852527950845033958570838130476937881321123674281319487950228066320170022460331989671970649163741175854851878484012054844672588851401562725019821719066960812627785485964818369621410721714214986361918774754509650308957099470934337856981674465828267911940611956037845397855839240761276344105766751024307559814552786167815949657062559755074306521085301597908073343736079432866757890533483669555486803913433720156498834220893399971641479746938696905480089193067138057171505857307148815649920714086758259602876056459782423770242469805328056632787041926768467116266879463486950464507420219373945259262668613552940624781361206202636498199999498405143868285258956342264328707663299304891723400725471764188685351372332667877921738347541480022803392997357936152412755829569276837231234798989446274330454566790062032420516396282588443085438307201495672106460533238537203143242112607424485845094580494081820927639140008540422023556260218564348994145439950410980591817948882628052066441086319001688568155169229486203010738897181007709290590480749092427141018933542818429995988169660993836961644381528877214085268088757488293258735809905670755817017949161906114001908553744882726200936685604475596557476485674008177381703307380305476973609786543859382187220583902344443508867499866506040645874346005331827436296177862518081893144363251205107094690813586440519229512932450078833398788429339342435126343365204385812912834345297308652909783300671261798130316794385535726296998740359570458452230856390098913179475948752126397078375944861139451960286751210561638976008880092746115860800207803341591451797073036835196977766076373785333012024120112046988609209339085365773222392412449051532780950955866459477634482269986074813297302630975028812103517723124465095349653693090018637764094094349837313251321862080214809922685502948454661814715557444709669530177690434272031892770604717784527939160472281534379803539679861424370956683221491465438014593829277393396032754048009552231816667380357183932757077142046723838624617803976292377131209580789363841447929802588065522129262093623930637313496640186619510811583471173312025805866727639992763579078063818813069156366274125431259589936119647626101405563503399523140323113819656236327198961837254845333702062563464223952766943568376761368711962921818754576081617053031590728828700712313666308722754918661395773730546065997437810987649802414011242142773668082751390959313404155826266789510846776118665957660165998178089414985754976284387856100263796543178313634025135814161151902096499133548733131115022700681930135929595971640197196053625033558479980963488718039111612813595968565478868325856437896173159762002419621552896297904819822199462269487137462444729093456470028537694958859591606789282491054412515996300781368367490209374915732896270028656829344431342347351239298259166739503425995868970697267332582735903121288746660451461487850346142827765991608090398652575717263081833494441820193533385071292345774375579344062178711330063106003324053991693682603746176638565758877580201229366353270267100681261825172914608202541892885935244491070138206211553827793565296914576502048643282865557934707209634807372692141186895467322767751335690190153723669036865389161291688887876407525493494249733427181178892759931596719354758988097924525262363659036320070854440784544797348291802082044926670634420437555325050527522833778887040804033531923407685630109347772125639088640413101073817853338316038135280828119040832564401842053746792992622037698718018061122624490909242641985820861751177113789051609140381575003366424156095216328197122335023167422600567941281406217219641842705784328959802882335059828208196666249035857789940333152274817776952843681630088531769694783690580671064828083598046698841098135158654906933319522394363287923990534810987830274500172065433699066117784554364687723631844464768069142828004551074686645392805399409108754939166095731619715033166968309929466349142798780842257220697148875580637480308862995118473187124777291910070227588893486939456289515802965372150409603107761289831263589964893410247036036645058687287589051406841238124247386385427908282733827973326885504935874303160274749063129572349742611221517417153133618622410913869500688835898962349276317316478340077460886655598733382113829928776911495492184192087771606068472874673681886167507221017261103830671787856694812948785048943063086169948798703160515884108282351274153538513365895332948629494495061868514779105804696039069372662670386512905201137810858616188886947957607413585534585151768051973334433495230120395770739623771316030242887200537320998253008977618973129817881944671731160647231476248457551928732782825127182446807824215216469567819294098238926284943760248852279003620219386696482215628093605373178040863727268426696421929946819214908701707533361094791381804063287387593848269535583077395761447997270003472880182785281389503217986345216111066608839314053226944905455527867894417579202440021450780192099804461382547805858048442416404775031536054906591430078158372430123137511562284015838644270890718284816757527123846782459534334449622010096071051370608461801187543120725491334994247617115633321408934609156561550600317384218701570226103101916603887064661438897736318780940711527528174689576401581047016965247557740891644568677717158500583269943401677202156767724068128366565264122982439465133197359199709403275938502669557470231813203243716420586141033606524536939160050644953060161267822648942437397166717661231048975031885732165554988342121802846912529086101485527815277625623750456375769497734336846015607727035509629049392487088406281067943622418704747008368842671022558302403599841645951122485272633632645114017395248086194635840783753556885622317115520947223065437092606797351000565549381224575483728545711797393615756167641692895805257297522338558611388322171107362265816218842443178857488798109026653793426664216990914056536432249301334867988154886628665052346997235574738424830590423677143278792316422403877764330192600192284778313837632536121025336935812624086866699738275977365682227907215832478888642369346396164363308730139814211430306008730666164803678984091335926293402304324974926887831643602681011309570716141912830686577323532639653677390317661361315965553584999398600565155921936759977717933019744688148371103206503693192894521402650915465184309936553493337183425298433679915939417466223900389527673813330617747629574943868716978453767219493506590875711917720875477107189937960894774512654757501871194870738736785890200617373321075693302216320628432065671192096950585761173961632326217708945426214609858410237813215817727602222738133495410481003073275107799948991977963883530734443457532975914263768405442264784216063122769646967156473999043715903323906560726644116438605404838847161912109008701019130726071044114143241976796828547885524779476481802959736049439700479596040292746299203572099761950140348315380947714601056333446998820822120587281510729182971211917876424880354672316916541852256729234429187128163232596965413548589577133208339911288775917226115273379010341362085614577992398778325083550730199818459025958355989260553299673770491722454935329683300002230181517226575787524058832249085821280089747909326100762578770428656006996176212176845478996440705066241710213327486796237430229155358200780141165348065647488230615003392068983794766255036549822805329662862117930628430170492402301985719978948836897183043805182174419147660429752437251683435411217038631379411422095295885798060152938752753799030938871683572095760715221900279379292786303637268765822681241993384808166021603722154710143007377537792699069587121289288019052031601285861825494413353820784883465311632650407642428390870121015194231961652268422003711230464300673442064747718021353070124098860353399152667923871101706221865883573781210935179775604425634694999787251125440854522274810914874307259869602040275941178942581281882159952359658979181144077653354321757595255536158128001163846720319346507296807990793963714961774312119402021297573125165253768017359101557338153772001952444543620071848475663415407442328621060997613243487548847434539665981338717466093020535070271952983943271425371155766600025784423031073429551533945060486222764966687624079324353192992639253731076892135352572321080889819339168668278948281170472624501948409700975760920983724090074717973340788141825195842598096241747610138252643955135259311885045636264188300338539652435997416931322894719878308427600401368074703904097238473945834896186539790594118599310356168436869219485382055780395773881360679549900085123259442529724486666766834641402189915944565309423440650667851948417766779470472041958822043295380326310537494883122180391279678446100139726753892195119117836587662528083690053249004597410947068772912328214304635337283519953648274325833119144459017809607782883583730111857543659958982724531925310588115026307542571493943024453931870179923608166611305426253995833897942971602070338767815033010280120095997252222280801423571094760351925544434929986767817891045559063015953809761875920358937341978962358931125983902598310267193304189215109689156225069659119828323455503059081730735195503721665870288053992138576037035377105178021280129566841984140362872725623214428754302210909472721073474134975514190737043318276626177275996888826027225247133683353452816692779591328861381766349857728936900965749562287103024362590772412219094300871755692625758065709912016659622436080242870024547362036394841255954881727272473653467783647201918303998717627037515724649922289467932322693619177641614618795613956699567783068290316589699430767333508234990790624100202506134057344300695745474682175690441651540636584680463692621274211075399042188716127617787014258864825775223889184599523376292377915585744549477361295525952226578636462118377598473700347971408206994145580719080213590732269233100831759510659019121294795408603640757358750205890208704579670007055262505811420663907459215273309406823649441590891009220296680523325266198911311842016291631076894084723564366808182168657219688268358402785500782804043453710183651096951782335743030504852653738073531074185917705610397395062640355442275156101107261779370634723804990666922161971194259120445084641746383589938239946517395509000859479990136026674261494290066467115067175422177038774507673563742154782905911012619157555870238957001405117822646989944917908301795475876760168094100135837613578591356924455647764464178667115391951357696104864922490083446715486383054477914330097680486878348184672733758436892724310447406807685278625585165092088263813233623148733336714764520450876627614950389949504809560460989604329123358348859990294526400284994280878624039811814884767301216754161106629995553668193123287425702063738352020086863691311733469731741219153633246745325630871347302792174956227014687325867891734558379964351358800959350877556356248810493852999007675135513527792412429277488565888566513247302514710210575352516511814850902750476845518252096331899068527614435138213662152368890578786699432288816028377482035506016029894009119713850179871683633744139275973644017007014763706655703504338121113576415018451821413619823495159601064752712575935185304332875537783057509567425442684712219618709178560783936144511383335649103256405733898667178123972237519316430617013859539474367843392670986712452211189690840236327411496601243483098929941738030588417166613073040067588380432111555379440605497721705942821514886165672771240903387727745629097110134885184374118695655449745736845218066982911045058004299887953899027804383596282409421860556287788428802127553884803728640019441614257499904272009595204654170598104989967504511936471172772220436102614079750809686975176600237187748348016120310234680567112644766123747627852190241202569943534716226660893675219833111813511146503854895025120655772636145473604426859498074396932331297127377157347099713952291182653485155587137336629120242714302503763269501350911612952993785864681307226486008270881333538193703682598867893321238327053297625857382790097826460545598555131836688844628265133798491667839409761353766251798258249663458771950124384040359140849209733754642474488176184070023569580177410177696925077814893386672557898564589851056891960924398841569280696983352240225634570497312245269354193837004843183357196516626721575524193401933099018319309196582920969656247667683659647019595754739345514337413708761517323677204227385674279170698204549953095918872434939524094441678998846319845504852393662972079777452814399418256789457795712552426826089940863317371538896262889629402112108884427376568624527612130371017300785135715404533041507959447776143597437803742436646973247138410492124314138903579092416036406314038149831481905251720937103964026808994832572297954564042701757722904173234796073618787889913318305843069394825961318713816423467218730845133877219086975104942843769325024981656673816260615941768252509993741672883951744066932549653403101452225316189009235376486378482881344209870048096227171226407489571939002918573307460104360729190945767994614929290427981687729426487729952858434647775386906950148984133924540394144680263625402118614317031251117577642829914644533408920976961699098372652361768745605894704968170136974909523072082682887890730190018253425805343421705928713931737993142410852647390948284596418093614138475831136130576108462366837237695913492615824516221552134879244145041756848064120636520170386330129532777699023118648020067556905682295016354931992305914246396217025329747573114094220180199368035026495636955866425906762685687372110339156793839895765565193177883000241613539562437777840801748819373095020699900890899328088397430367736595524891300156633294077907139615464534088791510300651321934486673248275907946807879819425019582622320395131252014109960531260696555404248670549986786923021746989009547850725672978794769888831093487464426400718183160331655511534276155622405474473378049246214952133258527698847336269182649174338987824789278468918828054669982303689939783413747587025805716349413568433929396068192061773331791738208562436433635359863494496890781064019674074436583667071586924521182997893804077137501290858646578905771426833582768978554717687184427726120509266486102051535642840632368481807287940717127966820060727559555904040233178749447346454760628189541512139162918444297651066947969354016866010055196077687335396511614930937570968554559381513789569039251014953265628147011998326992200066392875374713135236421589265126204072887716578358405219646054105435443642166562244565042999010256586927279142752931172082793937751326106052881235373451068372939893580871243869385934389175713376300720319760816604464683937725806909237297523486702916910426369262090199605204121024077648190316014085863558427609537086558164273995349346546314504040199528537252004957805254656251154109252437991326262713609099402902262062836752132305065183934057450112099341464918433323646569371725914489324159006242020612885732926133596808726500045628284557574596592120530341310111827501306961509835515632004310784601906565493806542525229161991819959602752327702249855738824899882707465936355768582560518068964285376850772012220347920993936179268206590142165615925306737944568949070853263568196831861772268249911472615732035807646298116244013316737892788689229032593349861797021994981925739617673075834417098559222170171825712777534491508205278430904619460835217402005838672849709411023266953921445461066215006410674740207009189911951376466904481267253691537162290791385403937560077835153374167747942100384002308951850994548779039346122220865060160500351776264831611153325587705073541279249909859373473787081194253055121436979749914951860535920403830235716352727630874693219622190064260886183676103346002255477477813641012691906569686495012688376296907233961276287223041141813610060264044030035996988919945827397624114613744804059697062576764723766065541618574690527229238228275186799156983390747671146103022776606020061246876477728819096791613354019881402757992174167678799231603963569492851513633647219540611171767387372555728522940054361785176502307544693869307873499110352182532929726044553210797887711449898870911511237250604238753734841257086064069052058452122754533848008205302450456517669518576913200042816758054924811780519832646032445792829730129105318385636821206215531288668564956512613892261367064093953334570526986959692350353094224543865278677673027540402702246384483553239914751363441044050092330361271496081355490531539021002299595756583705381261965683144286057956696622154721695620870013727768536960840704833325132793112232507148630206951245395003735723346807094656483089209801534878705633491092366057554050864111521441481434630437273271045027768661953107858323334857840297160925215326092558932655600672124359464255065996771770388445396181632879614460817789272171836908880126778207430106422524634807454300476492885553409062185153654355474125476152769772667769772777058315801412185688011705028365275543214803488004442979998062157904564161957212784508928489806426497427090579129069217807298769477975112447305991406050629946894280931034216416629935614828130998870745292716048433630818404126469637925843094185442216359084576146078558562473814931427078266215185541603870206876980461747400808324343665382354555109449498431093494759944672673665352517662706772194183191977196378015702169933675083760057163454643671776723387588643405644871566964321041282595645349841388412890420682047007615596916843038999348366793542549210328113363184722592305554383058206941675629992013373175489122037230349072681068534454035993561823576312837767640631013125335212141994611869350833176587852047112364331226765129964171325217513553261867681942338790365468908001827135283584888444111761234101179918709236507184857856221021104009776994453121795022479578069506532965940383987369907240797679040826794007618729547835963492793904576973661643405359792219285870574957481696694062334272619733518136626063735982575552496509807260123668283605928341855848026958413772558970883789942910549800331113884603401939166122186696058491571485733568286149500019097591125218800396419762163559375743718011480559442298730418196808085647265713547612831629200449880315402105530597076666362749328308916880932359290081787411985738317192616728834918402429721290434965526942726402559641463525914348400675867690350382320572934132981593533044446496829441367323442158380761694831219333119819061096142952201536170298575105594326461468505452684975764807808009221335811378197749271768545075538328768874474591593731162470601091244609829424841287520224462594477638749491997840446829257360968534549843266536862844489365704111817793806441616531223600214918768769467398407517176307516849856359201486892943105940202457969622924566644881967576294349535326382171613395757790766370764569570259738800438415805894336137106551859987600754924187211714889295221737721146081154344982665479872580056674724051122007383459271575727715218589946948117940644466399432370044291140747218180224825837736017346685300744985564715420036123593397312914458591522887408719508708632218837288262822884631843717261903305777147651564143822306791847386039147683108141358275755853643597721650028277803713422869688787349795096031108899196143386664068450697420787700280509367203387232629637856038653216432348815557557018469089074647879122436375556668678067610544955017260791142930831285761254481944449473244819093795369008206384631678225064809531810406570254327604385703505922818919878065865412184299217273720955103242251079718077833042609086794273428955735559252723805511440438001239041687716445180226491681641927401106451622431101700056691121733189423400547959684669804298017362570406733282129962153684881404102194463424646220745575643960452985313071409084608499653767803793201899140865814662175319337665970114330608625009829566917638846056762972931464911493704624469351984039534449135141193667933301936617663652555149174982307987072280860859626112660504289296966535652516688885572112276802772743708917389639772257564890533401038855931125679991516589025016486961427207005916056166159702451989051832969278935550303934681219761582183980483960562523091462638447386296039848924386187298507775928792722068554807210497817653286210187476766897248841139560349480376727036316921007350834073865261684507482496448597428134936480372426116704266870831925040997615319076855770327421785010006441984124207396400139603601583810565928413684574119102736420274163723488214524101347716529603128408658419787951116511529827814620379139855006399960326591248525308493690313130100799977191362230866011099929142871249388541612038020411340188887219693477904497527454288072803509305828754420755134816660927879353566521255620139988249628478726214432362853676502591450468377635282587652139156480972141929675549384375582600253168536356731379262475878049445944183429172756988376226261846365452743497662411138451305481449836311789784489732076719508784158618879692955819733250699951402601511675529750575437810242238957925786562128432731202200716730574069286869363930186765958251326499145950260917069347519408975357464016830811798846452473618956056479426358070562563281189269663026479535951097127659136233180866921535788607812759910537171402204506186075374866306350591483916467656723205714516886170790984695932236724946737583099607042589220481550799132752088583781117685214269334786921895240622657921043620348852926267984013953216458791151579050460579710838983371864038024417511347226472547010794793996953554669619726763255229914654933499663234185951450360980344092212206712567698723427940708857070474293173329188523896721971353924492426178641188637790962814486917869468177591717150669111480020759432012061969637795103227089029566085562225452602610460736131368869009281721068198618553780982018471154163630326265699283424155023600978046417108525537612728905335045506135684143775854429677977014660294387687225115363801191758154028120818255606485410787933598921064427244898618961629413418001295130683638609294100083136673372153008352696235737175330738653338204842190308186449184093723944033405244909554558016406460761581010301767488475017661908692946098769201691202181688291040870709560951470416921147027413390052253340834812870353031023919699978597413908593605433599697075604460134242453682496098772581311024732798562072126572499003468293886872304895562253204463602639854225258416464324271611419817802482595563544907219226583863662663750835944314877635156145710745528016159677048442714194435183275698407552677926411261765250615965235457187956673170913319358761628255920783080185206890151504713340386100310055914817852110384754542933389188444120517943969970194112695119526564919594189975418393234647424290702718875223534393673633663200307232747037407123982562024662651974090199762452056198557625760008708173083288344381831070054514493545885422678578551915372292379555494333410174420169600090696415612732297770221217951868376359082255128816470021992348864043959153018464004714321186360622527011541122283802778538911098490201342741014121559769965438877197485376431158229838533123071751132961904559007938064276695819014842627991221792947987348901868471676503827328552059082984529806259250352128451925927986593506132961946796252373972565584157853744567558998032405492186962888490332560851455344391660226257775512916200772796852629387937530454181080729285891989715381797343496187232927614747850192611450413274873242970583408471112333746274617274626582415324271059322506255302314738759251724787322881491455915605036334575424233779160374952502493022351481961381162563911415610326844958072508273431765944054098269765269344579863479709743124498271933113863873159636361218623497261409556079920628316999420072054811525353393946076850019909886553861433495781650089961649079678142901148387645682174914075623767618453775144031475411206760160726460556859257799322070337333398916369504346690694828436629980037414527627716547623825546170883189810868806847853705536480469350958818025360529740793538676511195079373282083146268960071075175520614433784114549950136432446328193346389050936545714506900864483440180428363390513578157273973334537284263372174065775771079830517555721036795976901889958494130195999573017901240193908681356585539661941371794487632079868800371607303220547423572266896801882123424391885984168972277652194032493227314793669234004848976059037958094696041754279613782553781223947646147832926976545162290281701100437846038756544151739433960048915318817576650500951697402415644771293656614253949368884230517400129920556854289853897942669956777027089146513736892206104415481662156804219838476730871787590279209175900695273456682026513373111518000181434120962601658629821076663523361774007837783423709152644063054071807843358061072961105550020415131696373046849213356837265400307509829089364612047891114753037049893952833457824082817386441322710002968311940203323456420826473276233830294639378998375836554559919340866235090967961134004867027123176526663710778725111860354037554487418693519733656621772359229396776463251562023487570113795712096237723431370212031004965152111976013176419408203437348512852602913334915125083119802850177855710725373149139215709105130965059885999931560863655477403551898166733535880048214665099741433761182777723351910741217572841592580872591315074606025634903777263373914461377038021318347447301113032670296917335047701632106616227830027269283365584011791419447808748253360714403296252285775009808599609040936312635621328162071453406104224112083010008587264252112262480142647519426184325853386753874054743491072710049754281159466017136122590440158991600229827801796035194080046513534752698777609527839984368086908989197839693532179980139135442552717910225397010810632143048511378291498511381969143043497500189980681644412123273328307192824362406733196554692677851193152775113446468905504248113361434984604849051258345683266441528489713972376040328212660253516693914082049947320486021627759791771234751097502403078935759937715095021751693555827072533911892334070223832077585802137174778378778391015234132098489423459613692340497998279304144463162707214796117456975719681239291913740982925805561955207434243295982898980529233366415419256367380689494201471241340525072204061794355252555225008748790086568314542835167750542294803274783044056438581591952666758282929705226127628711040134801787224801789684052407924360582742467443076721645270313451354167649668901274786801010295133862698649748212118629040337691568576240699296372493097201628707200189835423690364149270236961938547372480329855045112089192879829874467864129159417531675602533435310626745254507114181483239880607297140234725520713490798398982355268723950909365667878992383712578976248755990443228895388377317348941122757071410959790047919301046740750411435381782464630795989555638991884773781341347070246747362112048986226991888517456251732519341352038115863350123913054441910073628447567514161050410973505852762044489190978901984315485280533985777844313933883994310444465669244550885946314081751220331390681596592510546858013133838152176418210433429788826119630443111388796258746090226130900849975430395771243230616906262919403921439740270894777663702488155499322458825979020631257436910946393252806241642476868495455324938017639371615636847859823715902385421265840615367228607131702674740131145261063765383390315921943469817605358380310612887852051546933639241088467632009567089718367490578163085158138161966882222047570437590614338040725853862083565176998426774523195824182683698270160237414938363496629351576854061397342746470899685618170160551104880971554859118617189668025973541705423985135560018720335079060946421271143993196046527424050882225359773481519135438571253258540493946010865793798058620143366078825219717809025817370870916460452727977153509910340736425020386386718220522879694458387652947951048660717390229327455426785669776865939923416834122274663015062155320502655341460995249356050854921756549134830958906536175693817637473644183378974229700703545206663170929607591989627732423090252397443861014263098687733913882518684316501027964911497737582888913450341148865948670215492101084328080783428089417298008983297536940644969903125399863919581601468995220880662285408414864274786281975546629278814621607171381880180840572084715868906836919393381864278454537956719272397972364651667592011057995663962598535512763558768140213409829016296873429850792471846056874828331381259161962476156902875901072733103299140623864608333378638257926302391590003557609032477281338887339178096966601469615031754226751125993315529674213336300222964906480934582008181061802100227664580400278213336758573019011371754672763059044353131319036092489097246427928455549913490005180295707082919052556781889913899625138662319380053611346224294610248954072404857123256628888931722116432947816190554868054943441034090680716088028227959686950133643814268252170472870863010137301155236861416908375675747637239763185757038109443390564564468524183028148107998376918512127201935044041804604721626939445788377090105974693219720558114078775989772072009689382249303236830515862657281114637996983137517937623215111252349734305240622105244234353732905655163406669506165892878218707756794176080712973781335187117931650033155523822487730653444179453415395202424449703410120874072188109388268167512042299404948179449472732894770111574139441228455521828424922240658752689172272780607116754046973008037039618787796694882555614674384392570115829546661358678671897661297311267200072971553613027503556167817765442287442114729881614802705243806817653573275578602505847084013208837932816008769081300492491473682517035382219619039014999523495387105997351143478292339499187936608692301375596368532373806703591144243268561512109404259582639301678017128669239283231057658851714020211196957064799814031505633045141564414623163763809904402816256917576489142569714163598439317433270237812336938043012892626375382667795034169334323607500248175741808750388475094939454896209740485442635637164995949920980884294790363666297526003243856352945844728944547166209297495496616877414120882130477022816116456044007236351581149729739218966737382647204722642221242016560150284971306332795814302516013694825567014780935790889657134926158161346901806965089556310121218491805847922720691871696316330044858020102860657858591269974637661741463934159569539554203314628026518951167938074573315759846086173702687867602943677780500244673391332431669880354073232388281847501051641331189537036488422690270478052742490603492082954755054003457160184072574536938145531175354210726557835615499874447480427323457880061873149341566046352979779455075359304795687209316724536547208381685855606043801977030764246083489876101345709394877002946175792061952549255757109038525171488525265671045349813419803390641529876343695420256080277614421914318921393908834543131769685101840103844472348948869520981943531906506555354617335814045544837884752526253949665869992058417652780125341033896469818642430034146791380619028059607854888010789705516946215228773090104467462497979992627120951684779568482583341402266477210843362437593741610536734041954738964197895425335036301861400951534766961476255651873823292468547356935802896011536791787303553159378363082248615177770541577576561759358512016692943111138863582159667618830326104164651714846979385422621687161400122378213779774131268977266712992025922017408770076956283473932201088159356286281928563571893384958850603853158179760679479840878360975960149733420572704603521790605647603285569276273495182203236144112584182426247712012035776388895974318232827871314608053533574494297621796789034568169889553518504478325616380709476951699086247100019748809205009521943632378719764870339223811540363475488626845956159755193765410115014067001226927474393888589943859730245414801061235908036274585288493563251585384383242493252666087588908318700709100237377106576985056433928854337658342596750653715005333514489908293887737352051459333049626531415141386124437935885070944688045486975358170212908490787347806814366323322819415827345671356443171537967818058195852464840084032909981943781718177302317003989733050495387356116261023999433259780126893432605584710278764901070923443884634011735556865903585244919370181041626208504299258697435817098133894045934471937493877624232409852832762266604942385129709453245586252103600829286649724174919141988966129558076770979594795306013119159011773943104209049079424448868513086844493705909026006120649425744710353547657859242708130410618546219881830090634588187038755856274911587375421064667951346487586771543838018521348281915812462599335160198935595167968932852205824799421034512715877163345222995418839680448835529753361286837225935390079201666941339091168758803988828869216002373257361588207163516271332810518187602104852180675526648673908900907195138058626735124312215691637902277328705410842037841525683288718046987952513073266340278519059417338920358540395677035611329354482585628287610610698229721420961993509331312171187891078766872044548876089410174798647137882462153955933333275562009439580434537919782280590395959927436913793778664940964048777841748336432684026282932406260081908081804390914556351936856063045089142289645219987798849347477729132797266027658401667890136490508741142126861969862044126965282981087045479861559545338021201155646979976785738920186243599326777689454060508218838227909833627167124490026761178498264377033002081844590009717235204331994708242098771514449751017055643029542821819670009202515615844174205933658148134902693111517093872260026458630561325605792560927332265579346280805683443921373688405650434307396574061017779370141424615493070741360805442100295600095663588977899267630517718781943706761498217564186590116160865408635391513039201316805769034172596453692350806417446562351523929050409479953184074862151210561833854566176652606393713658802521666223576132201941701372664966073252010771947931265282763302413805164907174565964853748354669194523580315301969160480994606814904037819829732360930087135760798621425422096419004367905479049930078372421581954535418371129368658430553842717628035279128821129308351575656599944741788438381565148434229858704245592434693295232821803508333726283791830216591836181554217157448465778420134329982594566884558266171979012180849480332448787258183774805522268151011371745368417870280274452442905474518234674919564188551244421337783521423865979925988203287085109338386829906571994614906290257427686038850511032638544540419184958866538545040571323629681069146814847869659166861842756798460041868762298055562963045953227923051616721591968675849523635298935788507746081537321454642984792310511676357749494622952569497660359473962430995343310404994209677883827002714478494069037073249106444151696053256560586778757417472110827435774315194060757983563629143326397812218946287447798119807225646714664054850131009656786314880090303749338875364183165134982546694673316118123364854397649325026179549357204305402182974871251107404011611405899911093062492312813116340549262571356721818628932786138833718028535056503591952741400869510926167541476792668032109237467087213606278332922386413619594121339278036118276324106004740971111048140003623342714514483334641675466354699731494756643423659493496845884551524150756376605086632827424794136062876041290644913828519456402643153225858624043141838669590633245063000392213192647625962691510904457695301444054618037857503036686212462278639752746667870121003392984873375014475600322100622358029343774955032037012738468163061026570300872275462966796880890587127676361066225722352229739206443093524327228100859973095132528630601105497915644791845004618046762408928925680912930592960642357021061524646205023248966593987324933967376952023991760898474571843531936646529125848064480196520162838795189499336759241485626136995945307287254532463291529110128763770605570609531377527751867923292134955245133089867969165129073841302167573238637575820080363575728002754490327953079900799442541108725693188014667935595834676432868876966610097395749967836593397846346959948950610490383647409504695226063858046758073069912290474089879166872117147527644711604401952718169508289733537148530928937046384420893299771125856840846608339934045689026787516008775461267988015465856522061210953490796707365539702576199431376639960606061106406959330828171876426043573425361756943784848495250108266488395159700490598380812105221111091943323951136051446459834210799058082093716464523127704023160072138543723461267260997870385657091998507595634613248460188409850194287687902268734556500519121546544063829253851276317663922050938345204300773017029940362615434001322763910912988327863920412300445551684054889809080779174636092439334912641164240093880746356607262336695842764583698268734815881961058571835767462009650526065929263548291499045768307210893245857073701660717398194485028842603963660746031184786225831056580870870305567595861341700745402965687634774176431051751036732869245558582082372038601781739405175130437994868822320044378043103170921034261674998000073016094814586374488778522273076330495383944345382770608760763542098445008306247630253572781032783461766970544287155315340016497076657195985041748199087201490875686037783591994719343352772947285537925787684832301101859365800717291186967617655053775030293033830706448912811412025506150896411007623824574488655182581058140345320124754723269087547507078577659732542844459353044992070014538748948226556442223696365544194225441338212225477497535494624827680533336983284156138692363443358553868471111430498248398991803165458638289353799130535222833430137953372954016257623228081138499491876144141322933767106563492528814528239506209022357876684650116660097382753660405446941653422239052108314585847035529352219928272760574821266065291385530345549744551470344939486863429459658431024190785923680224560763936784166270518555178702904073557304620639692453307795782245949710420188043000183881429008173039450507342787013124466860092778581811040911511729374873627887874907465285565434748886831064110051023020875107768918781525622735251550379532444857787277617001964853703555167655209119339343762866284619844026295252183678522367475108809781507098978413086245881522660963551401874495836926917799047120726494905737264286005211403581231076006699518536124862746756375896225299116496066876508261734178484789337295056739007878617925351440621045366250640463728815698232317500596261080921955211150859302955654967538862612972339914628358476048627627027309739202001432248707582337354915246085608210328882974183906478869923273691360048837436615223517058437705545210815513361262142911815615301758882573594892507108879262128641392443309383797333867806131795237315266773820858024701433527009243803266951742119507670884326346442749127558907746863582162166042741315170212458586056233631493164646913946562497471741958354218607748711057338458433689939645913740603382159352243594751626239188685307822821763983237306180204246560477527943104796189724299533029792497481684052893791044947004590864991872727345413508101983881864673609392571930511968645601855782450218231065889437986522432050677379966196955472440585922417953006820451795370043472451762893566770508490213107736625751697335527462302943031203596260953423574397249659211010657817826108745318874803187430823573699195156340957162700992444929749105489851519658664740148225106335367949737142510229341882585117371994499115097583746130105505064197721531929354875371191630262030328588658528480193509225875775597425276584011721342323648084027143356367542046375182552524944329657043861387865901965738802868401894087672816714137033661732650120578653915780703088714261519075001492576112927675193096728453971160213606303090542243966320674323582797889332324405779199278484633339777737655901870574806828678347965624146102899508487399692970750432753029972872297327934442988646412725348160603779707298299173029296308695801996312413304939350493325412355071054461182591141116454534710329881047844067780138077131465400099386306481266614330858206811395838319169545558259426895769841428893743467084107946318932539106963955780706021245974898293564613560788983472419979478564362042094613412387613198865352358312996862268948608408456655606876954501274486631405054735351746873009806322780468912246821460806727627708402402266155485024008952891657117617439020337584877842911289623247059191874691042005848326140677333751027195653994697162517248312230633919328707983800748485726516123434933273356664473358556430235280883924348278760886164943289399166399210488307847777048045728491456303353265070029588906265915498509407972767567129795010098229476228961891591441520032283878773485130979081019129267227103778898053964156362364169154985768408398468861684375407065121039062506128107663799047908879674778069738473170475253442156390387201238806323688037017949308954900776331523063548374256816653361606641980030188287123767481898330246836371488309259283375902278942588060087286038859168849730693948020511221766359138251524278670094406942355120201568377778851824670025651708509249623747726813694284350062938814429987905301056217375459182679973217735029368928065210025396268807498092643458011655715886700443503976505323478287327368840863540002740676783821963522226539290939807367391364082898722017776747168118195856133721583119054682936083236976113450281757830202934845982925000895682630271263295866292147653142233351793093387951357095346377183684092444422096319331295620305575517340067973740614162107923633423805646850092037167152642556371853889571416419772387422610596667396997173168169415435095283193556417705668622215217991151355639707143312893657553844648326201206424338016955862698561022460646069330793847858814367407000599769703649019273328826135329363112403650698652160638987250267238087403396744397830258296894256896741864336134979475245526291426522842419243083388103580053787023999542172113686550275341362211693140694669513186928102574795985605145005021715913317751609957865551981886193211282110709442287240442481153406055895958355815232012184605820563592699303478851132068626627588771446035996656108430725696500563064489187599466596772847171539573612108180841547273142661748933134174632662354222072600146012701206934639520564445543291662986660783089068118790090815295063626782075614388815781351134695366303878412092346942868730839320432333872775496805210302821544324723388845215343727250128589747691460808314404125868181540049187772287869801853454537006526655649170915429522756709222217474112062720656622989806032891672068743654948246108697367225547404812889242471854323605753411672850757552057131156697954584887398742228135887985840783135060548290551482785294891121905383195624228719484759407859398047901094194070671764439032730712135887385049993638838205501683402777496070276844880281912220636888636811043569529300652195528261526991271637277388418993287130563464688227398288763198645709836308917786487086676185485680047672552675414742851028145807403152992197814557756843681110185317498167016426647884090262682824448258027532094549915104518517716546311804904567985713257528117913656278158111288816562285876030875974963849435275676612168959261485030785362045274507752950631012480341804584059432926079854435620093708091821523920371790678121992280496069738238743312626730306795943960954957189577217915597300588693646845576676092450906088202212235719254536715191834872587423919410890444115959932760044506556206461164655665487594247369252336955993030355095817626176231849561906494839673002037763874369343999829430209147073618947932692762445186560239559053705128978163455423320114975994896278424327483788032701418676952621180975006405149755889650293004867605208010491537885413909424531691719987628941277221129464568294860281493181560249677887949813777216229359437811004448060797672429276249510784153446429150842764520002042769470698041775832209097020291657347251582904630910359037842977572651720877244740952267166306005469716387943171196873484688738186656751279298575016363411314627530499019135646823804329970695770150789337728658035712790913767420805655493624646
diff --git a/src/pkg/compress/zlib/example_test.go b/src/pkg/compress/zlib/example_test.go
new file mode 100644
index 000000000..b934ffa61
--- /dev/null
+++ b/src/pkg/compress/zlib/example_test.go
@@ -0,0 +1,37 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package zlib_test
+
+import (
+ "bytes"
+ "compress/zlib"
+ "fmt"
+ "io"
+ "os"
+)
+
+func ExampleNewWriter() {
+ var b bytes.Buffer
+
+ w := zlib.NewWriter(&b)
+ w.Write([]byte("hello, world\n"))
+ w.Close()
+ fmt.Println(b.Bytes())
+ // Output: [120 156 202 72 205 201 201 215 81 40 207 47 202 73 225 2 4 0 0 255 255 33 231 4 147]
+}
+
+func ExampleNewReader() {
+ buff := []byte{120, 156, 202, 72, 205, 201, 201, 215, 81, 40, 207,
+ 47, 202, 73, 225, 2, 4, 0, 0, 255, 255, 33, 231, 4, 147}
+ b := bytes.NewBuffer(buff)
+
+ r, err := zlib.NewReader(b)
+ if err != nil {
+ panic(err)
+ }
+ io.Copy(os.Stdout, r)
+ // Output: hello, world
+ r.Close()
+}
diff --git a/src/pkg/compress/zlib/reader.go b/src/pkg/compress/zlib/reader.go
index f38ef5a88..d54746f4c 100644
--- a/src/pkg/compress/zlib/reader.go
+++ b/src/pkg/compress/zlib/reader.go
@@ -11,7 +11,7 @@ and compress during writing. For example, to write compressed data
to a buffer:
var b bytes.Buffer
- w, err := zlib.NewWriter(&b)
+ w := zlib.NewWriter(&b)
w.Write([]byte("hello, world\n"))
w.Close()
diff --git a/src/pkg/container/heap/example_intheap_test.go b/src/pkg/container/heap/example_intheap_test.go
new file mode 100644
index 000000000..e718cbc58
--- /dev/null
+++ b/src/pkg/container/heap/example_intheap_test.go
@@ -0,0 +1,43 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This example demonstrates an integer heap built using the heap interface.
+package heap_test
+
+import (
+ "container/heap"
+ "fmt"
+)
+
+// An IntHeap is a min-heap of ints.
+type IntHeap []int
+
+func (h IntHeap) Len() int { return len(h) }
+func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
+func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
+
+func (h *IntHeap) Push(x interface{}) {
+ // Push and Pop use pointer receivers because they modify the slice's length,
+ // not just its contents.
+ *h = append(*h, x.(int))
+}
+
+func (h *IntHeap) Pop() interface{} {
+ old := *h
+ n := len(old)
+ x := old[n-1]
+ *h = old[0 : n-1]
+ return x
+}
+
+// This example inserts several ints into an IntHeap and removes them in order of priority.
+func Example_intHeap() {
+ h := &IntHeap{2, 1, 5}
+ heap.Init(h)
+ heap.Push(h, 3)
+ for h.Len() > 0 {
+ fmt.Printf("%d ", heap.Pop(h))
+ }
+ // Output: 1 2 3 5
+}
diff --git a/src/pkg/container/heap/example_pq_test.go b/src/pkg/container/heap/example_pq_test.go
new file mode 100644
index 000000000..8cbeb8d70
--- /dev/null
+++ b/src/pkg/container/heap/example_pq_test.go
@@ -0,0 +1,95 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This example demonstrates a priority queue built using the heap interface.
+package heap_test
+
+import (
+ "container/heap"
+ "fmt"
+)
+
+// An Item is something we manage in a priority queue.
+type Item struct {
+ value string // The value of the item; arbitrary.
+ priority int // The priority of the item in the queue.
+ // The index is needed by update and is maintained by the heap.Interface methods.
+ index int // The index of the item in the heap.
+}
+
+// A PriorityQueue implements heap.Interface and holds Items.
+type PriorityQueue []*Item
+
+func (pq PriorityQueue) Len() int { return len(pq) }
+
+func (pq PriorityQueue) Less(i, j int) bool {
+ // We want Pop to give us the highest, not lowest, priority so we use greater than here.
+ return pq[i].priority > pq[j].priority
+}
+
+func (pq PriorityQueue) Swap(i, j int) {
+ pq[i], pq[j] = pq[j], pq[i]
+ pq[i].index = i
+ pq[j].index = j
+}
+
+func (pq *PriorityQueue) Push(x interface{}) {
+ n := len(*pq)
+ item := x.(*Item)
+ item.index = n
+ *pq = append(*pq, item)
+}
+
+func (pq *PriorityQueue) Pop() interface{} {
+ old := *pq
+ n := len(old)
+ item := old[n-1]
+ item.index = -1 // for safety
+ *pq = old[0 : n-1]
+ return item
+}
+
+// update modifies the priority and value of an Item in the queue.
+func (pq *PriorityQueue) update(item *Item, value string, priority int) {
+ heap.Remove(pq, item.index)
+ item.value = value
+ item.priority = priority
+ heap.Push(pq, item)
+}
+
+// This example inserts some items into a PriorityQueue, manipulates an item,
+// and then removes the items in priority order.
+func Example_priorityQueue() {
+ // Some items and their priorities.
+ items := map[string]int{
+ "banana": 3, "apple": 2, "pear": 4,
+ }
+
+ // Create a priority queue and put the items in it.
+ pq := &PriorityQueue{}
+ heap.Init(pq)
+ for value, priority := range items {
+ item := &Item{
+ value: value,
+ priority: priority,
+ }
+ heap.Push(pq, item)
+ }
+
+ // Insert a new item and then modify its priority.
+ item := &Item{
+ value: "orange",
+ priority: 1,
+ }
+ heap.Push(pq, item)
+ pq.update(item, item.value, 5)
+
+ // Take the items out; they arrive in decreasing priority order.
+ for pq.Len() > 0 {
+ item := heap.Pop(pq).(*Item)
+ fmt.Printf("%.2d:%s ", item.priority, item.value)
+ }
+ // Output:
+ // 05:orange 04:pear 03:banana 02:apple
+}
diff --git a/src/pkg/container/heap/example_test.go b/src/pkg/container/heap/example_test.go
deleted file mode 100644
index 2050bc835..000000000
--- a/src/pkg/container/heap/example_test.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This example demonstrates a priority queue built using the heap interface.
-package heap_test
-
-import (
- "container/heap"
- "fmt"
-)
-
-// An Item is something we manage in a priority queue.
-type Item struct {
- value string // The value of the item; arbitrary.
- priority int // The priority of the item in the queue.
- // The index is needed by changePriority and is maintained by the heap.Interface methods.
- index int // The index of the item in the heap.
-}
-
-// A PriorityQueue implements heap.Interface and holds Items.
-type PriorityQueue []*Item
-
-func (pq PriorityQueue) Len() int { return len(pq) }
-
-func (pq PriorityQueue) Less(i, j int) bool {
- // We want Pop to give us the highest, not lowest, priority so we use greater than here.
- return pq[i].priority > pq[j].priority
-}
-
-func (pq PriorityQueue) Swap(i, j int) {
- pq[i], pq[j] = pq[j], pq[i]
- pq[i].index = i
- pq[j].index = j
-}
-
-func (pq *PriorityQueue) Push(x interface{}) {
- // Push and Pop use pointer receivers because they modify the slice's length,
- // not just its contents.
- // To simplify indexing expressions in these methods, we save a copy of the
- // slice object. We could instead write (*pq)[i].
- a := *pq
- n := len(a)
- a = a[0 : n+1]
- item := x.(*Item)
- item.index = n
- a[n] = item
- *pq = a
-}
-
-func (pq *PriorityQueue) Pop() interface{} {
- a := *pq
- n := len(a)
- item := a[n-1]
- item.index = -1 // for safety
- *pq = a[0 : n-1]
- return item
-}
-
-// update is not used by the example but shows how to take the top item from
-// the queue, update its priority and value, and put it back.
-func (pq *PriorityQueue) update(value string, priority int) {
- item := heap.Pop(pq).(*Item)
- item.value = value
- item.priority = priority
- heap.Push(pq, item)
-}
-
-// changePriority is not used by the example but shows how to change the
-// priority of an arbitrary item.
-func (pq *PriorityQueue) changePriority(item *Item, priority int) {
- heap.Remove(pq, item.index)
- item.priority = priority
- heap.Push(pq, item)
-}
-
-// This example pushes 10 items into a PriorityQueue and takes them out in
-// order of priority.
-func Example() {
- const nItem = 10
- // Random priorities for the items (a permutation of 0..9, times 11)).
- priorities := [nItem]int{
- 77, 22, 44, 55, 11, 88, 33, 99, 00, 66,
- }
- values := [nItem]string{
- "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
- }
- // Create a priority queue and put some items in it.
- pq := make(PriorityQueue, 0, nItem)
- for i := 0; i < cap(pq); i++ {
- item := &Item{
- value: values[i],
- priority: priorities[i],
- }
- heap.Push(&pq, item)
- }
- // Take the items out; should arrive in decreasing priority order.
- // For example, the highest priority (99) is the seventh item, so output starts with 99:"seven".
- for i := 0; i < nItem; i++ {
- item := heap.Pop(&pq).(*Item)
- fmt.Printf("%.2d:%s ", item.priority, item.value)
- }
- // Output:
- // 99:seven 88:five 77:zero 66:nine 55:three 44:two 33:six 22:one 11:four 00:eight
-}
diff --git a/src/pkg/container/heap/heap.go b/src/pkg/container/heap/heap.go
index 67018e6ba..c37e50e3c 100644
--- a/src/pkg/container/heap/heap.go
+++ b/src/pkg/container/heap/heap.go
@@ -4,13 +4,13 @@
// Package heap provides heap operations for any type that implements
// heap.Interface. A heap is a tree with the property that each node is the
-// highest-valued node in its subtree.
+// minimum-valued node in its subtree.
//
// A heap is a common way to implement a priority queue. To build a priority
// queue, implement the Heap interface with the (negative) priority as the
// ordering for the Less method, so Push adds items while Pop removes the
// highest-priority item from the queue. The Examples include such an
-// implementation; the file example_test.go has the complete source.
+// implementation; the file example_pq_test.go has the complete source.
//
package heap
@@ -79,7 +79,7 @@ func Remove(h Interface, i int) interface{} {
func up(h Interface, j int) {
for {
i := (j - 1) / 2 // parent
- if i == j || h.Less(i, j) {
+ if i == j || !h.Less(j, i) {
break
}
h.Swap(i, j)
@@ -90,14 +90,14 @@ func up(h Interface, j int) {
func down(h Interface, i, n int) {
for {
j1 := 2*i + 1
- if j1 >= n {
+ if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
break
}
j := j1 // left child
if j2 := j1 + 1; j2 < n && !h.Less(j1, j2) {
j = j2 // = 2*i + 2 // right child
}
- if h.Less(i, j) {
+ if !h.Less(j, i) {
break
}
h.Swap(i, j)
diff --git a/src/pkg/container/heap/heap_test.go b/src/pkg/container/heap/heap_test.go
index cb31ef6d3..274d587d8 100644
--- a/src/pkg/container/heap/heap_test.go
+++ b/src/pkg/container/heap/heap_test.go
@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package heap_test
+package heap
import (
- . "container/heap"
"testing"
)
@@ -170,3 +169,16 @@ func TestRemove2(t *testing.T) {
}
}
}
+
+func BenchmarkDup(b *testing.B) {
+ const n = 10000
+ h := make(myHeap, n)
+ for i := 0; i < b.N; i++ {
+ for j := 0; j < n; j++ {
+ Push(&h, 0) // all elements are the same
+ }
+ for h.Len() > 0 {
+ Pop(&h)
+ }
+ }
+}
diff --git a/src/pkg/container/list/example_test.go b/src/pkg/container/list/example_test.go
new file mode 100644
index 000000000..7361212d7
--- /dev/null
+++ b/src/pkg/container/list/example_test.go
@@ -0,0 +1,30 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package list_test
+
+import (
+ "container/list"
+ "fmt"
+)
+
+func Example() {
+ // Create a new list and put some numbers in it.
+ l := list.New()
+ e4 := l.PushBack(4)
+ e1 := l.PushFront(1)
+ l.InsertBefore(3, e4)
+ l.InsertAfter(2, e1)
+
+ // Iterate through list and and print its contents.
+ for e := l.Front(); e != nil; e = e.Next() {
+ fmt.Println(e.Value)
+ }
+
+ // Output:
+ // 1
+ // 2
+ // 3
+ // 4
+}
diff --git a/src/pkg/container/list/list.go b/src/pkg/container/list/list.go
index a3fd4b39f..562a5badb 100644
--- a/src/pkg/container/list/list.go
+++ b/src/pkg/container/list/list.go
@@ -11,201 +11,187 @@
//
package list
-// Element is an element in the linked list.
+// Element is an element of a linked list.
type Element struct {
// Next and previous pointers in the doubly-linked list of elements.
- // The front of the list has prev = nil, and the back has next = nil.
+ // To simplify the implementation, internally a list l is implemented
+ // as a ring, such that &l.root is both the next element of the last
+ // list element (l.Back()) and the previous element of the first list
+ // element (l.Front()).
next, prev *Element
// The list to which this element belongs.
list *List
- // The contents of this list element.
+ // The value stored with this element.
Value interface{}
}
// Next returns the next list element or nil.
-func (e *Element) Next() *Element { return e.next }
+func (e *Element) Next() *Element {
+ if p := e.next; p != &e.list.root {
+ return p
+ }
+ return nil
+}
// Prev returns the previous list element or nil.
-func (e *Element) Prev() *Element { return e.prev }
+func (e *Element) Prev() *Element {
+ if p := e.prev; p != &e.list.root {
+ return p
+ }
+ return nil
+}
// List represents a doubly linked list.
// The zero value for List is an empty list ready to use.
type List struct {
- front, back *Element
- len int
+ root Element // sentinel list element, only &root, root.prev, and root.next are used
+ len int // current list length excluding (this) sentinel element
}
-// Init initializes or clears a List.
+// Init initializes or clears list l.
func (l *List) Init() *List {
- l.front = nil
- l.back = nil
+ l.root.next = &l.root
+ l.root.prev = &l.root
l.len = 0
return l
}
// New returns an initialized list.
-func New() *List { return new(List) }
-
-// Front returns the first element in the list.
-func (l *List) Front() *Element { return l.front }
+func New() *List { return new(List).Init() }
-// Back returns the last element in the list.
-func (l *List) Back() *Element { return l.back }
+// Len returns the number of elements of list l.
+func (l *List) Len() int { return l.len }
-// Remove removes the element from the list
-// and returns its Value.
-func (l *List) Remove(e *Element) interface{} {
- l.remove(e)
- e.list = nil // do what remove does not
- return e.Value
+// Front returns the first element of list l or nil
+func (l *List) Front() *Element {
+ if l.len == 0 {
+ return nil
+ }
+ return l.root.next
}
-// remove the element from the list, but do not clear the Element's list field.
-// This is so that other List methods may use remove when relocating Elements
-// without needing to restore the list field.
-func (l *List) remove(e *Element) {
- if e.list != l {
- return
- }
- if e.prev == nil {
- l.front = e.next
- } else {
- e.prev.next = e.next
- }
- if e.next == nil {
- l.back = e.prev
- } else {
- e.next.prev = e.prev
+// Back returns the last element of list l or nil.
+func (l *List) Back() *Element {
+ if l.len == 0 {
+ return nil
}
-
- e.prev = nil
- e.next = nil
- l.len--
+ return l.root.prev
}
-func (l *List) insertBefore(e *Element, mark *Element) {
- if mark.prev == nil {
- // new front of the list
- l.front = e
- } else {
- mark.prev.next = e
+// lazyInit lazily initializes a zero List value.
+func (l *List) lazyInit() {
+ if l.root.next == nil {
+ l.Init()
}
- e.prev = mark.prev
- mark.prev = e
- e.next = mark
- l.len++
}
-func (l *List) insertAfter(e *Element, mark *Element) {
- if mark.next == nil {
- // new back of the list
- l.back = e
- } else {
- mark.next.prev = e
- }
- e.next = mark.next
- mark.next = e
- e.prev = mark
+// insert inserts e after at, increments l.len, and returns e.
+func (l *List) insert(e, at *Element) *Element {
+ n := at.next
+ at.next = e
+ e.prev = at
+ e.next = n
+ n.prev = e
+ e.list = l
l.len++
+ return e
}
-func (l *List) insertFront(e *Element) {
- if l.front == nil {
- // empty list
- l.front, l.back = e, e
- e.prev, e.next = nil, nil
- l.len = 1
- return
- }
- l.insertBefore(e, l.front)
+// insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
+func (l *List) insertValue(v interface{}, at *Element) *Element {
+ return l.insert(&Element{Value: v}, at)
}
-func (l *List) insertBack(e *Element) {
- if l.back == nil {
- // empty list
- l.front, l.back = e, e
- e.prev, e.next = nil, nil
- l.len = 1
- return
+// remove removes e from its list, decrements l.len, and returns e.
+func (l *List) remove(e *Element) *Element {
+ e.prev.next = e.next
+ e.next.prev = e.prev
+ e.next = nil // avoid memory leaks
+ e.prev = nil // avoid memory leaks
+ e.list = nil
+ l.len--
+ return e
+}
+
+// Remove removes e from l if e is an element of list l.
+// It returns the element value e.Value.
+func (l *List) Remove(e *Element) interface{} {
+ if e.list == l {
+ // if e.list == l, l must have been initialized when e was inserted
+ // in l or l == nil (e is a zero Element) and l.remove will crash
+ l.remove(e)
}
- l.insertAfter(e, l.back)
+ return e.Value
}
-// PushFront inserts the value at the front of the list and returns a new Element containing the value.
-func (l *List) PushFront(value interface{}) *Element {
- e := &Element{nil, nil, l, value}
- l.insertFront(e)
- return e
+// Pushfront inserts a new element e with value v at the front of list l and returns e.
+func (l *List) PushFront(v interface{}) *Element {
+ l.lazyInit()
+ return l.insertValue(v, &l.root)
}
-// PushBack inserts the value at the back of the list and returns a new Element containing the value.
-func (l *List) PushBack(value interface{}) *Element {
- e := &Element{nil, nil, l, value}
- l.insertBack(e)
- return e
+// PushBack inserts a new element e with value v at the back of list l and returns e.
+func (l *List) PushBack(v interface{}) *Element {
+ l.lazyInit()
+ return l.insertValue(v, l.root.prev)
}
-// InsertBefore inserts the value immediately before mark and returns a new Element containing the value.
-func (l *List) InsertBefore(value interface{}, mark *Element) *Element {
+// InsertBefore inserts a new element e with value v immediately before mark and returns e.
+// If mark is not an element of l, the list is not modified.
+func (l *List) InsertBefore(v interface{}, mark *Element) *Element {
if mark.list != l {
return nil
}
- e := &Element{nil, nil, l, value}
- l.insertBefore(e, mark)
- return e
+ // see comment in List.Remove about initialization of l
+ return l.insertValue(v, mark.prev)
}
-// InsertAfter inserts the value immediately after mark and returns a new Element containing the value.
-func (l *List) InsertAfter(value interface{}, mark *Element) *Element {
+// InsertAfter inserts a new element e with value v immediately after mark and returns e.
+// If mark is not an element of l, the list is not modified.
+func (l *List) InsertAfter(v interface{}, mark *Element) *Element {
if mark.list != l {
return nil
}
- e := &Element{nil, nil, l, value}
- l.insertAfter(e, mark)
- return e
+ // see comment in List.Remove about initialization of l
+ return l.insertValue(v, mark)
}
-// MoveToFront moves the element to the front of the list.
+// MoveToFront moves element e to the front of list l.
+// If e is not an element of l, the list is not modified.
func (l *List) MoveToFront(e *Element) {
- if e.list != l || l.front == e {
+ if e.list != l || l.root.next == e {
return
}
- l.remove(e)
- l.insertFront(e)
+ // see comment in List.Remove about initialization of l
+ l.insert(l.remove(e), &l.root)
}
-// MoveToBack moves the element to the back of the list.
+// MoveToBack moves element e to the back of list l.
+// If e is not an element of l, the list is not modified.
func (l *List) MoveToBack(e *Element) {
- if e.list != l || l.back == e {
+ if e.list != l || l.root.prev == e {
return
}
- l.remove(e)
- l.insertBack(e)
+ // see comment in List.Remove about initialization of l
+ l.insert(l.remove(e), l.root.prev)
}
-// Len returns the number of elements in the list.
-func (l *List) Len() int { return l.len }
-
-// PushBackList inserts each element of ol at the back of the list.
-func (l *List) PushBackList(ol *List) {
- last := ol.Back()
- for e := ol.Front(); e != nil; e = e.Next() {
- l.PushBack(e.Value)
- if e == last {
- break
- }
+// PushBackList inserts a copy of an other list at the back of list l.
+// The lists l and other may be the same.
+func (l *List) PushBackList(other *List) {
+ l.lazyInit()
+ for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
+ l.insertValue(e.Value, l.root.prev)
}
}
-// PushFrontList inserts each element of ol at the front of the list. The ordering of the passed list is preserved.
-func (l *List) PushFrontList(ol *List) {
- first := ol.Front()
- for e := ol.Back(); e != nil; e = e.Prev() {
- l.PushFront(e.Value)
- if e == first {
- break
- }
+// PushFrontList inserts a copy of an other list at the front of list l.
+// The lists l and other may be the same.
+func (l *List) PushFrontList(other *List) {
+ l.lazyInit()
+ for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
+ l.insertValue(e.Value, &l.root)
}
}
diff --git a/src/pkg/container/list/list_test.go b/src/pkg/container/list/list_test.go
index 1d44ff84e..b4fc77d14 100644
--- a/src/pkg/container/list/list_test.go
+++ b/src/pkg/container/list/list_test.go
@@ -4,65 +4,75 @@
package list
-import (
- "testing"
-)
+import "testing"
+
+func checkListLen(t *testing.T, l *List, len int) bool {
+ if n := l.Len(); n != len {
+ t.Errorf("l.Len() = %d, want %d", n, len)
+ return false
+ }
+ return true
+}
func checkListPointers(t *testing.T, l *List, es []*Element) {
- if len(es) == 0 {
- if l.front != nil || l.back != nil {
- t.Errorf("l.front/l.back = %v/%v should be nil/nil", l.front, l.back)
- }
+ root := &l.root
+
+ if !checkListLen(t, l, len(es)) {
return
}
- if l.front != es[0] {
- t.Errorf("l.front = %v, want %v", l.front, es[0])
- }
- if last := es[len(es)-1]; l.back != last {
- t.Errorf("l.back = %v, want %v", l.back, last)
+ // zero length lists must be the zero value or properly initialized (sentinel circle)
+ if len(es) == 0 {
+ if l.root.next != nil && l.root.next != root || l.root.prev != nil && l.root.prev != root {
+ t.Errorf("l.root.next = %p, l.root.prev = %p; both should both be nil or %p", l.root.next, l.root.prev, root)
+ }
+ return
}
+ // len(es) > 0
+ // check internal and external prev/next connections
for i, e := range es {
- var e_prev, e_next *Element = nil, nil
+ prev := root
+ Prev := (*Element)(nil)
if i > 0 {
- e_prev = es[i-1]
+ prev = es[i-1]
+ Prev = prev
+ }
+ if p := e.prev; p != prev {
+ t.Errorf("elt[%d](%p).prev = %p, want %p", i, e, p, prev)
+ }
+ if p := e.Prev(); p != Prev {
+ t.Errorf("elt[%d](%p).Prev() = %p, want %p", i, e, p, Prev)
}
+
+ next := root
+ Next := (*Element)(nil)
if i < len(es)-1 {
- e_next = es[i+1]
+ next = es[i+1]
+ Next = next
}
- if e.prev != e_prev {
- t.Errorf("elt #%d (%v) has prev=%v, want %v", i, e, e.prev, e_prev)
+ if n := e.next; n != next {
+ t.Errorf("elt[%d](%p).next = %p, want %p", i, e, n, next)
}
- if e.next != e_next {
- t.Errorf("elt #%d (%v) has next=%v, want %v", i, e, e.next, e_next)
+ if n := e.Next(); n != Next {
+ t.Errorf("elt[%d](%p).Next() = %p, want %p", i, e, n, Next)
}
}
}
-func checkListLen(t *testing.T, l *List, n int) {
- if an := l.Len(); an != n {
- t.Errorf("l.Len() = %d, want %d", an, n)
- }
-}
-
func TestList(t *testing.T) {
l := New()
checkListPointers(t, l, []*Element{})
- checkListLen(t, l, 0)
// Single element list
e := l.PushFront("a")
- checkListLen(t, l, 1)
checkListPointers(t, l, []*Element{e})
l.MoveToFront(e)
checkListPointers(t, l, []*Element{e})
l.MoveToBack(e)
checkListPointers(t, l, []*Element{e})
- checkListLen(t, l, 1)
l.Remove(e)
checkListPointers(t, l, []*Element{})
- checkListLen(t, l, 0)
// Bigger list
e2 := l.PushFront(2)
@@ -70,11 +80,9 @@ func TestList(t *testing.T) {
e3 := l.PushBack(3)
e4 := l.PushBack("banana")
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
- checkListLen(t, l, 4)
l.Remove(e2)
checkListPointers(t, l, []*Element{e1, e3, e4})
- checkListLen(t, l, 3)
l.MoveToFront(e3) // move from middle
checkListPointers(t, l, []*Element{e3, e1, e4})
@@ -121,7 +129,7 @@ func TestList(t *testing.T) {
}
}
if sum != 4 {
- t.Errorf("sum over l.Iter() = %d, want 4", sum)
+ t.Errorf("sum over l = %d, want 4", sum)
}
// Clear all elements by iterating
@@ -131,19 +139,18 @@ func TestList(t *testing.T) {
l.Remove(e)
}
checkListPointers(t, l, []*Element{})
- checkListLen(t, l, 0)
}
func checkList(t *testing.T, l *List, es []interface{}) {
- if l.Len() != len(es) {
- t.Errorf("list has len=%v, want %v", l.Len(), len(es))
+ if !checkListLen(t, l, len(es)) {
return
}
+
i := 0
for e := l.Front(); e != nil; e = e.Next() {
le := e.Value.(int)
if le != es[i] {
- t.Errorf("elt #%d has value=%v, want %v", i, le, es[i])
+ t.Errorf("elt[%d].Value = %v, want %v", i, le, es[i])
}
i++
}
@@ -202,8 +209,27 @@ func TestRemove(t *testing.T) {
e := l.Front()
l.Remove(e)
checkListPointers(t, l, []*Element{e2})
- checkListLen(t, l, 1)
l.Remove(e)
checkListPointers(t, l, []*Element{e2})
- checkListLen(t, l, 1)
+}
+
+func TestIssue4103(t *testing.T) {
+ l1 := New()
+ l1.PushBack(1)
+ l1.PushBack(2)
+
+ l2 := New()
+ l2.PushBack(3)
+ l2.PushBack(4)
+
+ e := l1.Front()
+ l2.Remove(e) // l2 should not change because e is not an element of l2
+ if n := l2.Len(); n != 2 {
+ t.Errorf("l2.Len() = %d, want 2", n)
+ }
+
+ l1.InsertBefore(8, e)
+ if n := l1.Len(); n != 3 {
+ t.Errorf("l1.Len() = %d, want 3", n)
+ }
}
diff --git a/src/pkg/container/ring/ring.go b/src/pkg/container/ring/ring.go
index 1d96918d3..6d3b3e5b3 100644
--- a/src/pkg/container/ring/ring.go
+++ b/src/pkg/container/ring/ring.go
@@ -74,7 +74,7 @@ func New(n int) *Ring {
return r
}
-// Link connects ring r with with ring s such that r.Next()
+// Link connects ring r with ring s such that r.Next()
// becomes s and returns the original value for r.Next().
// r must not be empty.
//
diff --git a/src/pkg/crypto/aes/aes_test.go b/src/pkg/crypto/aes/aes_test.go
index e500c666d..6261dd09f 100644
--- a/src/pkg/crypto/aes/aes_test.go
+++ b/src/pkg/crypto/aes/aes_test.go
@@ -221,7 +221,10 @@ L:
if tt.dec != nil {
dec = make([]uint32, len(tt.dec))
}
- expandKey(tt.key, enc, dec)
+ // This test could only test Go version of expandKey because asm
+ // version might use different memory layout for expanded keys
+ // This is OK because we don't expose expanded keys to the outside
+ expandKeyGo(tt.key, enc, dec)
for j, v := range enc {
if v != tt.enc[j] {
t.Errorf("key %d: enc[%d] = %#x, want %#x", i, j, v, tt.enc[j])
@@ -352,15 +355,39 @@ func TestCipherDecrypt(t *testing.T) {
}
func BenchmarkEncrypt(b *testing.B) {
- b.StopTimer()
tt := encryptTests[0]
c, err := NewCipher(tt.key)
if err != nil {
b.Fatal("NewCipher:", err)
}
out := make([]byte, len(tt.in))
- b.StartTimer()
+ b.SetBytes(int64(len(out)))
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
c.Encrypt(out, tt.in)
}
}
+
+func BenchmarkDecrypt(b *testing.B) {
+ tt := encryptTests[0]
+ c, err := NewCipher(tt.key)
+ if err != nil {
+ b.Fatal("NewCipher:", err)
+ }
+ out := make([]byte, len(tt.out))
+ b.SetBytes(int64(len(out)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ c.Decrypt(out, tt.out)
+ }
+}
+
+func BenchmarkExpand(b *testing.B) {
+ tt := encryptTests[0]
+ n := len(tt.key) + 28
+ c := &aesCipher{make([]uint32, n), make([]uint32, n)}
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ expandKey(tt.key, c.enc, c.dec)
+ }
+}
diff --git a/src/pkg/crypto/aes/asm_amd64.s b/src/pkg/crypto/aes/asm_amd64.s
new file mode 100644
index 000000000..25decf978
--- /dev/null
+++ b/src/pkg/crypto/aes/asm_amd64.s
@@ -0,0 +1,287 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// func hasAsm() bool
+// returns whether AES-NI is supported
+TEXT ·hasAsm(SB),7,$0
+ XORQ AX, AX
+ INCL AX
+ CPUID
+ SHRQ $25, CX
+ ANDQ $1, CX
+ MOVB CX, ret+0(FP)
+ RET
+
+// func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
+TEXT ·encryptBlockAsm(SB),7,$0
+ MOVQ nr+0(FP), CX
+ MOVQ xk+8(FP), AX
+ MOVQ dst+16(FP), DX
+ MOVQ src+24(FP), BX
+ MOVUPS 0(AX), X1
+ MOVUPS 0(BX), X0
+ ADDQ $16, AX
+ PXOR X1, X0
+ SUBQ $12, CX
+ JE Lenc196
+ JB Lenc128
+Lenc256:
+ MOVUPS 0(AX), X1
+ AESENC X1, X0
+ MOVUPS 16(AX), X1
+ AESENC X1, X0
+ ADDQ $32, AX
+Lenc196:
+ MOVUPS 0(AX), X1
+ AESENC X1, X0
+ MOVUPS 16(AX), X1
+ AESENC X1, X0
+ ADDQ $32, AX
+Lenc128:
+ MOVUPS 0(AX), X1
+ AESENC X1, X0
+ MOVUPS 16(AX), X1
+ AESENC X1, X0
+ MOVUPS 32(AX), X1
+ AESENC X1, X0
+ MOVUPS 48(AX), X1
+ AESENC X1, X0
+ MOVUPS 64(AX), X1
+ AESENC X1, X0
+ MOVUPS 80(AX), X1
+ AESENC X1, X0
+ MOVUPS 96(AX), X1
+ AESENC X1, X0
+ MOVUPS 112(AX), X1
+ AESENC X1, X0
+ MOVUPS 128(AX), X1
+ AESENC X1, X0
+ MOVUPS 144(AX), X1
+ AESENCLAST X1, X0
+ MOVUPS X0, 0(DX)
+ RET
+
+// func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
+TEXT ·decryptBlockAsm(SB),7,$0
+ MOVQ nr+0(FP), CX
+ MOVQ xk+8(FP), AX
+ MOVQ dst+16(FP), DX
+ MOVQ src+24(FP), BX
+ MOVUPS 0(AX), X1
+ MOVUPS 0(BX), X0
+ ADDQ $16, AX
+ PXOR X1, X0
+ SUBQ $12, CX
+ JE Ldec196
+ JB Ldec128
+Ldec256:
+ MOVUPS 0(AX), X1
+ AESDEC X1, X0
+ MOVUPS 16(AX), X1
+ AESDEC X1, X0
+ ADDQ $32, AX
+Ldec196:
+ MOVUPS 0(AX), X1
+ AESDEC X1, X0
+ MOVUPS 16(AX), X1
+ AESDEC X1, X0
+ ADDQ $32, AX
+Ldec128:
+ MOVUPS 0(AX), X1
+ AESDEC X1, X0
+ MOVUPS 16(AX), X1
+ AESDEC X1, X0
+ MOVUPS 32(AX), X1
+ AESDEC X1, X0
+ MOVUPS 48(AX), X1
+ AESDEC X1, X0
+ MOVUPS 64(AX), X1
+ AESDEC X1, X0
+ MOVUPS 80(AX), X1
+ AESDEC X1, X0
+ MOVUPS 96(AX), X1
+ AESDEC X1, X0
+ MOVUPS 112(AX), X1
+ AESDEC X1, X0
+ MOVUPS 128(AX), X1
+ AESDEC X1, X0
+ MOVUPS 144(AX), X1
+ AESDECLAST X1, X0
+ MOVUPS X0, 0(DX)
+ RET
+
+// func expandKeyAsm(nr int, key *byte, enc, dec *uint32) {
+// Note that round keys are stored in uint128 format, not uint32
+TEXT ·expandKeyAsm(SB),7,$0
+ MOVQ nr+0(FP), CX
+ MOVQ key+8(FP), AX
+ MOVQ enc+16(FP), BX
+ MOVQ dec+24(FP), DX
+ MOVUPS (AX), X0
+ // enc
+ MOVUPS X0, (BX)
+ ADDQ $16, BX
+ PXOR X4, X4 // _expand_key_* expect X4 to be zero
+ CMPL CX, $12
+ JE Lexp_enc196
+ JB Lexp_enc128
+Lexp_enc256:
+ MOVUPS 16(AX), X2
+ MOVUPS X2, (BX)
+ ADDQ $16, BX
+ AESKEYGENASSIST $0x01, X2, X1
+ CALL _expand_key_256a<>(SB)
+ AESKEYGENASSIST $0x01, X0, X1
+ CALL _expand_key_256b<>(SB)
+ AESKEYGENASSIST $0x02, X2, X1
+ CALL _expand_key_256a<>(SB)
+ AESKEYGENASSIST $0x02, X0, X1
+ CALL _expand_key_256b<>(SB)
+ AESKEYGENASSIST $0x04, X2, X1
+ CALL _expand_key_256a<>(SB)
+ AESKEYGENASSIST $0x04, X0, X1
+ CALL _expand_key_256b<>(SB)
+ AESKEYGENASSIST $0x08, X2, X1
+ CALL _expand_key_256a<>(SB)
+ AESKEYGENASSIST $0x08, X0, X1
+ CALL _expand_key_256b<>(SB)
+ AESKEYGENASSIST $0x10, X2, X1
+ CALL _expand_key_256a<>(SB)
+ AESKEYGENASSIST $0x10, X0, X1
+ CALL _expand_key_256b<>(SB)
+ AESKEYGENASSIST $0x20, X2, X1
+ CALL _expand_key_256a<>(SB)
+ AESKEYGENASSIST $0x20, X0, X1
+ CALL _expand_key_256b<>(SB)
+ AESKEYGENASSIST $0x40, X2, X1
+ CALL _expand_key_256a<>(SB)
+ JMP Lexp_dec
+Lexp_enc196:
+ MOVQ 16(AX), X2
+ AESKEYGENASSIST $0x01, X2, X1
+ CALL _expand_key_192a<>(SB)
+ AESKEYGENASSIST $0x02, X2, X1
+ CALL _expand_key_192b<>(SB)
+ AESKEYGENASSIST $0x04, X2, X1
+ CALL _expand_key_192a<>(SB)
+ AESKEYGENASSIST $0x08, X2, X1
+ CALL _expand_key_192b<>(SB)
+ AESKEYGENASSIST $0x10, X2, X1
+ CALL _expand_key_192a<>(SB)
+ AESKEYGENASSIST $0x20, X2, X1
+ CALL _expand_key_192b<>(SB)
+ AESKEYGENASSIST $0x40, X2, X1
+ CALL _expand_key_192a<>(SB)
+ AESKEYGENASSIST $0x80, X2, X1
+ CALL _expand_key_192b<>(SB)
+ JMP Lexp_dec
+Lexp_enc128:
+ AESKEYGENASSIST $0x01, X0, X1
+ CALL _expand_key_128<>(SB)
+ AESKEYGENASSIST $0x02, X0, X1
+ CALL _expand_key_128<>(SB)
+ AESKEYGENASSIST $0x04, X0, X1
+ CALL _expand_key_128<>(SB)
+ AESKEYGENASSIST $0x08, X0, X1
+ CALL _expand_key_128<>(SB)
+ AESKEYGENASSIST $0x10, X0, X1
+ CALL _expand_key_128<>(SB)
+ AESKEYGENASSIST $0x20, X0, X1
+ CALL _expand_key_128<>(SB)
+ AESKEYGENASSIST $0x40, X0, X1
+ CALL _expand_key_128<>(SB)
+ AESKEYGENASSIST $0x80, X0, X1
+ CALL _expand_key_128<>(SB)
+ AESKEYGENASSIST $0x1b, X0, X1
+ CALL _expand_key_128<>(SB)
+ AESKEYGENASSIST $0x36, X0, X1
+ CALL _expand_key_128<>(SB)
+Lexp_dec:
+ // dec
+ SUBQ $16, BX
+ MOVUPS (BX), X1
+ MOVUPS X1, (DX)
+ DECQ CX
+Lexp_dec_loop:
+ MOVUPS -16(BX), X1
+ AESIMC X1, X0
+ MOVUPS X0, 16(DX)
+ SUBQ $16, BX
+ ADDQ $16, DX
+ DECQ CX
+ JNZ Lexp_dec_loop
+ MOVUPS -16(BX), X0
+ MOVUPS X0, 16(DX)
+ RET
+
+#define PSHUFD_X0_X0_ BYTE $0x66; BYTE $0x0f; BYTE $0x70; BYTE $0xc0
+#define PSHUFD_X1_X1_ BYTE $0x66; BYTE $0x0f; BYTE $0x70; BYTE $0xc9
+TEXT _expand_key_128<>(SB),7,$0
+ PSHUFD $0xff, X1, X1
+ SHUFPS $0x10, X0, X4
+ PXOR X4, X0
+ SHUFPS $0x8c, X0, X4
+ PXOR X4, X0
+ PXOR X1, X0
+ MOVUPS X0, (BX)
+ ADDQ $16, BX
+ RET
+
+#define PSLLDQ_X5_ BYTE $0x66; BYTE $0x0f; BYTE $0x73; BYTE $0xfd
+#define PSHUFD_X0_X3_ BYTE $0x66; BYTE $0x0f; BYTE $0x70; BYTE $0xd8
+TEXT _expand_key_192a<>(SB),7,$0
+ PSHUFD $0x55, X1, X1
+ SHUFPS $0x10, X0, X4
+ PXOR X4, X0
+ SHUFPS $0x8c, X0, X4
+ PXOR X4, X0
+ PXOR X1, X0
+
+ MOVAPS X2, X5
+ MOVAPS X2, X6
+ PSLLDQ_X5_; BYTE $0x4
+ PSHUFD $0xff, X0, X3
+ PXOR X3, X2
+ PXOR X5, X2
+
+ MOVAPS X0, X1
+ SHUFPS $0x44, X0, X6
+ MOVUPS X6, (BX)
+ SHUFPS $0x4e, X2, X1
+ MOVUPS X1, 16(BX)
+ ADDQ $32, BX
+ RET
+
+TEXT _expand_key_192b<>(SB),7,$0
+ PSHUFD $0x55, X1, X1
+ SHUFPS $0x10, X0, X4
+ PXOR X4, X0
+ SHUFPS $0x8c, X0, X4
+ PXOR X4, X0
+ PXOR X1, X0
+
+ MOVAPS X2, X5
+ PSLLDQ_X5_; BYTE $0x4
+ PSHUFD $0xff, X0, X3
+ PXOR X3, X2
+ PXOR X5, X2
+
+ MOVUPS X0, (BX)
+ ADDQ $16, BX
+ RET
+
+TEXT _expand_key_256a<>(SB),7,$0
+ JMP _expand_key_128<>(SB)
+
+TEXT _expand_key_256b<>(SB),7,$0
+ PSHUFD $0xaa, X1, X1
+ SHUFPS $0x10, X2, X4
+ PXOR X4, X2
+ SHUFPS $0x8c, X2, X4
+ PXOR X4, X2
+ PXOR X1, X2
+
+ MOVUPS X2, (BX)
+ ADDQ $16, BX
+ RET
diff --git a/src/pkg/crypto/aes/block.go b/src/pkg/crypto/aes/block.go
index b930787ce..57a7e9e25 100644
--- a/src/pkg/crypto/aes/block.go
+++ b/src/pkg/crypto/aes/block.go
@@ -37,7 +37,7 @@
package aes
// Encrypt one block from src into dst, using the expanded key xk.
-func encryptBlock(xk []uint32, dst, src []byte) {
+func encryptBlockGo(xk []uint32, dst, src []byte) {
var s0, s1, s2, s3, t0, t1, t2, t3 uint32
s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
@@ -82,7 +82,7 @@ func encryptBlock(xk []uint32, dst, src []byte) {
}
// Decrypt one block from src into dst, using the expanded key xk.
-func decryptBlock(xk []uint32, dst, src []byte) {
+func decryptBlockGo(xk []uint32, dst, src []byte) {
var s0, s1, s2, s3, t0, t1, t2, t3 uint32
s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
@@ -139,7 +139,7 @@ func rotw(w uint32) uint32 { return w<<8 | w>>24 }
// Key expansion algorithm. See FIPS-197, Figure 11.
// Their rcon[i] is our powx[i-1] << 24.
-func expandKey(key []byte, enc, dec []uint32) {
+func expandKeyGo(key []byte, enc, dec []uint32) {
// Encryption key setup.
var i int
nk := len(key) / 4
diff --git a/src/pkg/crypto/aes/cipher.go b/src/pkg/crypto/aes/cipher.go
index 7d307c93a..d931134a7 100644
--- a/src/pkg/crypto/aes/cipher.go
+++ b/src/pkg/crypto/aes/cipher.go
@@ -45,6 +45,10 @@ func NewCipher(key []byte) (cipher.Block, error) {
func (c *aesCipher) BlockSize() int { return BlockSize }
-func (c *aesCipher) Encrypt(dst, src []byte) { encryptBlock(c.enc, dst, src) }
+func (c *aesCipher) Encrypt(dst, src []byte) {
+ encryptBlock(c.enc, dst, src)
+}
-func (c *aesCipher) Decrypt(dst, src []byte) { decryptBlock(c.dec, dst, src) }
+func (c *aesCipher) Decrypt(dst, src []byte) {
+ decryptBlock(c.dec, dst, src)
+}
diff --git a/src/pkg/crypto/aes/cipher_asm.go b/src/pkg/crypto/aes/cipher_asm.go
new file mode 100644
index 000000000..21369fc38
--- /dev/null
+++ b/src/pkg/crypto/aes/cipher_asm.go
@@ -0,0 +1,46 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64
+
+package aes
+
+// defined in asm_$GOARCH.s
+func hasAsm() bool
+func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
+func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
+func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32)
+
+var useAsm = hasAsm()
+
+func encryptBlock(xk []uint32, dst, src []byte) {
+ if useAsm {
+ encryptBlockAsm(len(xk)/4-1, &xk[0], &dst[0], &src[0])
+ } else {
+ encryptBlockGo(xk, dst, src)
+ }
+}
+func decryptBlock(xk []uint32, dst, src []byte) {
+ if useAsm {
+ decryptBlockAsm(len(xk)/4-1, &xk[0], &dst[0], &src[0])
+ } else {
+ decryptBlockGo(xk, dst, src)
+ }
+}
+func expandKey(key []byte, enc, dec []uint32) {
+ if useAsm {
+ rounds := 10
+ switch len(key) {
+ case 128 / 8:
+ rounds = 10
+ case 192 / 8:
+ rounds = 12
+ case 256 / 8:
+ rounds = 14
+ }
+ expandKeyAsm(rounds, &key[0], &enc[0], &dec[0])
+ } else {
+ expandKeyGo(key, enc, dec)
+ }
+}
diff --git a/src/pkg/crypto/aes/cipher_generic.go b/src/pkg/crypto/aes/cipher_generic.go
new file mode 100644
index 000000000..1714e0f1e
--- /dev/null
+++ b/src/pkg/crypto/aes/cipher_generic.go
@@ -0,0 +1,19 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64
+
+package aes
+
+func encryptBlock(xk []uint32, dst, src []byte) {
+ encryptBlockGo(xk, dst, src)
+}
+
+func decryptBlock(xk []uint32, dst, src []byte) {
+ decryptBlockGo(xk, dst, src)
+}
+
+func expandKey(key []byte, enc, dec []uint32) {
+ expandKeyGo(key, enc, dec)
+}
diff --git a/src/pkg/crypto/cipher/cbc.go b/src/pkg/crypto/cipher/cbc.go
index a48929cf5..913a5643f 100644
--- a/src/pkg/crypto/cipher/cbc.go
+++ b/src/pkg/crypto/cipher/cbc.go
@@ -33,12 +33,21 @@ type cbcEncrypter cbc
// mode, using the given Block. The length of iv must be the same as the
// Block's block size.
func NewCBCEncrypter(b Block, iv []byte) BlockMode {
+ if len(iv) != b.BlockSize() {
+ panic("cipher.NewCBCEncrypter: IV length must equal block size")
+ }
return (*cbcEncrypter)(newCBC(b, iv))
}
func (x *cbcEncrypter) BlockSize() int { return x.blockSize }
func (x *cbcEncrypter) CryptBlocks(dst, src []byte) {
+ if len(src)%x.blockSize != 0 {
+ panic("crypto/cipher: input not full blocks")
+ }
+ if len(dst) < len(src) {
+ panic("crypto/cipher: output smaller than input")
+ }
for len(src) > 0 {
for i := 0; i < x.blockSize; i++ {
x.iv[i] ^= src[i]
@@ -58,12 +67,21 @@ type cbcDecrypter cbc
// mode, using the given Block. The length of iv must be the same as the
// Block's block size and must match the iv used to encrypt the data.
func NewCBCDecrypter(b Block, iv []byte) BlockMode {
+ if len(iv) != b.BlockSize() {
+ panic("cipher.NewCBCDecrypter: IV length must equal block size")
+ }
return (*cbcDecrypter)(newCBC(b, iv))
}
func (x *cbcDecrypter) BlockSize() int { return x.blockSize }
func (x *cbcDecrypter) CryptBlocks(dst, src []byte) {
+ if len(src)%x.blockSize != 0 {
+ panic("crypto/cipher: input not full blocks")
+ }
+ if len(dst) < len(src) {
+ panic("crypto/cipher: output smaller than input")
+ }
for len(src) > 0 {
x.b.Decrypt(x.tmp, src[:x.blockSize])
for i := 0; i < x.blockSize; i++ {
diff --git a/src/pkg/crypto/cipher/cfb.go b/src/pkg/crypto/cipher/cfb.go
index d14165a86..99006b546 100644
--- a/src/pkg/crypto/cipher/cfb.go
+++ b/src/pkg/crypto/cipher/cfb.go
@@ -17,6 +17,9 @@ type cfb struct {
// using the given Block. The iv must be the same length as the Block's block
// size.
func NewCFBEncrypter(block Block, iv []byte) Stream {
+ if len(iv) != block.BlockSize() {
+ panic("cipher.NewCBFEncrypter: IV length must equal block size")
+ }
return newCFB(block, iv, false)
}
@@ -24,6 +27,9 @@ func NewCFBEncrypter(block Block, iv []byte) Stream {
// using the given Block. The iv must be the same length as the Block's block
// size.
func NewCFBDecrypter(block Block, iv []byte) Stream {
+ if len(iv) != block.BlockSize() {
+ panic("cipher.NewCBFEncrypter: IV length must equal block size")
+ }
return newCFB(block, iv, true)
}
diff --git a/src/pkg/crypto/cipher/cipher_test.go b/src/pkg/crypto/cipher/cipher_test.go
new file mode 100644
index 000000000..8da5bce93
--- /dev/null
+++ b/src/pkg/crypto/cipher/cipher_test.go
@@ -0,0 +1,36 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cipher_test
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "testing"
+)
+
+func TestCryptBlocks(t *testing.T) {
+ buf := make([]byte, 16)
+ block, _ := aes.NewCipher(buf)
+
+ mode := cipher.NewCBCDecrypter(block, buf)
+ mustPanic(t, "crypto/cipher: input not full blocks", func() { mode.CryptBlocks(buf, buf[:3]) })
+ mustPanic(t, "crypto/cipher: output smaller than input", func() { mode.CryptBlocks(buf[:3], buf) })
+
+ mode = cipher.NewCBCEncrypter(block, buf)
+ mustPanic(t, "crypto/cipher: input not full blocks", func() { mode.CryptBlocks(buf, buf[:3]) })
+ mustPanic(t, "crypto/cipher: output smaller than input", func() { mode.CryptBlocks(buf[:3], buf) })
+}
+
+func mustPanic(t *testing.T, msg string, f func()) {
+ defer func() {
+ err := recover()
+ if err == nil {
+ t.Errorf("function did not panic, wanted %q", msg)
+ } else if err != msg {
+ t.Errorf("got panic %v, wanted %q", err, msg)
+ }
+ }()
+ f()
+}
diff --git a/src/pkg/crypto/cipher/ctr.go b/src/pkg/crypto/cipher/ctr.go
index 147b74fc2..d9ee9d827 100644
--- a/src/pkg/crypto/cipher/ctr.go
+++ b/src/pkg/crypto/cipher/ctr.go
@@ -23,7 +23,7 @@ type ctr struct {
// counter mode. The length of iv must be the same as the Block's block size.
func NewCTR(block Block, iv []byte) Stream {
if len(iv) != block.BlockSize() {
- panic("cipher.NewCTR: iv length must equal block size")
+ panic("cipher.NewCTR: IV length must equal block size")
}
return &ctr{
diff --git a/src/pkg/crypto/cipher/example_test.go b/src/pkg/crypto/cipher/example_test.go
new file mode 100644
index 000000000..e0027cac2
--- /dev/null
+++ b/src/pkg/crypto/cipher/example_test.go
@@ -0,0 +1,283 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cipher_test
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rand"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "os"
+)
+
+func ExampleNewCBCDecrypter() {
+ key := []byte("example key 1234")
+ ciphertext, _ := hex.DecodeString("f363f3ccdcb12bb883abf484ba77d9cd7d32b5baecb3d4b1b3e0e4beffdb3ded")
+
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+
+ // The IV needs to be unique, but not secure. Therefore it's common to
+ // include it at the beginning of the ciphertext.
+ if len(ciphertext) < aes.BlockSize {
+ panic("ciphertext too short")
+ }
+ iv := ciphertext[:aes.BlockSize]
+ ciphertext = ciphertext[aes.BlockSize:]
+
+ // CBC mode always works in whole blocks.
+ if len(ciphertext)%aes.BlockSize != 0 {
+ panic("ciphertext is not a multiple of the block size")
+ }
+
+ mode := cipher.NewCBCDecrypter(block, iv)
+
+ // CryptBlocks can work in-place if the two arguments are the same.
+ mode.CryptBlocks(ciphertext, ciphertext)
+
+ // If the original plaintext lengths are not a multiple of the block
+ // size, padding would have to be added when encrypting, which would be
+ // removed at this point. For an example, see
+ // https://tools.ietf.org/html/rfc5246#section-6.2.3.2. However, it's
+ // critical to note that ciphertexts must be authenticated (i.e. by
+ // using crypto/hmac) before being decrypted in order to avoid creating
+ // a padding oracle.
+
+ fmt.Printf("%s\n", ciphertext)
+ // Output: exampleplaintext
+}
+
+func ExampleNewCBCEncrypter() {
+ key := []byte("example key 1234")
+ plaintext := []byte("exampleplaintext")
+
+ // CBC mode works on blocks so plaintexts may need to be padded to the
+ // next whole block. For an example of such padding, see
+ // https://tools.ietf.org/html/rfc5246#section-6.2.3.2. Here we'll
+ // assume that the plaintext is already of the correct length.
+ if len(plaintext)%aes.BlockSize != 0 {
+ panic("plaintext is not a multiple of the block size")
+ }
+
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+
+ // The IV needs to be unique, but not secure. Therefore it's common to
+ // include it at the beginning of the ciphertext.
+ ciphertext := make([]byte, aes.BlockSize+len(plaintext))
+ iv := ciphertext[:aes.BlockSize]
+ if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+ panic(err)
+ }
+
+ mode := cipher.NewCBCEncrypter(block, iv)
+ mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
+
+ // It's important to remember that ciphertexts must be authenticated
+ // (i.e. by using crypto/hmac) as well as being encrypted in order to
+ // be secure.
+
+ fmt.Printf("%x\n", ciphertext)
+}
+
+func ExampleNewCFBDecrypter() {
+ key := []byte("example key 1234")
+ ciphertext, _ := hex.DecodeString("22277966616d9bc47177bd02603d08c9a67d5380d0fe8cf3b44438dff7b9")
+
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+
+ // The IV needs to be unique, but not secure. Therefore it's common to
+ // include it at the beginning of the ciphertext.
+ if len(ciphertext) < aes.BlockSize {
+ panic("ciphertext too short")
+ }
+ iv := ciphertext[:aes.BlockSize]
+ ciphertext = ciphertext[aes.BlockSize:]
+
+ stream := cipher.NewCFBDecrypter(block, iv)
+
+ // XORKeyStream can work in-place if the two arguments are the same.
+ stream.XORKeyStream(ciphertext, ciphertext)
+ fmt.Printf("%s", ciphertext)
+ // Output: some plaintext
+}
+
+func ExampleNewCFBEncrypter() {
+ key := []byte("example key 1234")
+ plaintext := []byte("some plaintext")
+
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+
+ // The IV needs to be unique, but not secure. Therefore it's common to
+ // include it at the beginning of the ciphertext.
+ ciphertext := make([]byte, aes.BlockSize+len(plaintext))
+ iv := ciphertext[:aes.BlockSize]
+ if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+ panic(err)
+ }
+
+ stream := cipher.NewCFBEncrypter(block, iv)
+ stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
+
+ // It's important to remember that ciphertexts must be authenticated
+ // (i.e. by using crypto/hmac) as well as being encrypted in order to
+ // be secure.
+}
+
+func ExampleNewCTR() {
+ key := []byte("example key 1234")
+ plaintext := []byte("some plaintext")
+
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+
+ // The IV needs to be unique, but not secure. Therefore it's common to
+ // include it at the beginning of the ciphertext.
+ ciphertext := make([]byte, aes.BlockSize+len(plaintext))
+ iv := ciphertext[:aes.BlockSize]
+ if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+ panic(err)
+ }
+
+ stream := cipher.NewCTR(block, iv)
+ stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
+
+ // It's important to remember that ciphertexts must be authenticated
+ // (i.e. by using crypto/hmac) as well as being encrypted in order to
+ // be secure.
+
+ // CTR mode is the same for both encryption and decryption, so we can
+ // also decrypt that ciphertext with NewCTR.
+
+ plaintext2 := make([]byte, len(plaintext))
+ stream = cipher.NewCTR(block, iv)
+ stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:])
+
+ fmt.Printf("%s\n", plaintext2)
+ // Output: some plaintext
+}
+
+func ExampleNewOFB() {
+ key := []byte("example key 1234")
+ plaintext := []byte("some plaintext")
+
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+
+ // The IV needs to be unique, but not secure. Therefore it's common to
+ // include it at the beginning of the ciphertext.
+ ciphertext := make([]byte, aes.BlockSize+len(plaintext))
+ iv := ciphertext[:aes.BlockSize]
+ if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+ panic(err)
+ }
+
+ stream := cipher.NewOFB(block, iv)
+ stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
+
+ // It's important to remember that ciphertexts must be authenticated
+ // (i.e. by using crypto/hmac) as well as being encrypted in order to
+ // be secure.
+
+ // OFB mode is the same for both encryption and decryption, so we can
+ // also decrypt that ciphertext with NewOFB.
+
+ plaintext2 := make([]byte, len(plaintext))
+ stream = cipher.NewOFB(block, iv)
+ stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:])
+
+ fmt.Printf("%s\n", plaintext2)
+ // Output: some plaintext
+}
+
+func ExampleStreamReader() {
+ key := []byte("example key 1234")
+
+ inFile, err := os.Open("encrypted-file")
+ if err != nil {
+ panic(err)
+ }
+ defer inFile.Close()
+
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+
+ // If the key is unique for each ciphertext, then it's ok to use a zero
+ // IV.
+ var iv [aes.BlockSize]byte
+ stream := cipher.NewOFB(block, iv[:])
+
+ outFile, err := os.OpenFile("decrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
+ if err != nil {
+ panic(err)
+ }
+ defer outFile.Close()
+
+ reader := &cipher.StreamReader{stream, inFile}
+ // Copy the input file to the output file, decrypting as we go.
+ if _, err := io.Copy(outFile, reader); err != nil {
+ panic(err)
+ }
+
+ // Note that this example is simplistic in that it omits any
+ // authentication of the encrypted data. It you were actually to use
+ // StreamReader in this manner, an attacker could flip arbitrary bits in
+ // the output.
+}
+
+func ExampleStreamWriter() {
+ key := []byte("example key 1234")
+
+ inFile, err := os.Open("plaintext-file")
+ if err != nil {
+ panic(err)
+ }
+ defer inFile.Close()
+
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+
+ // If the key is unique for each ciphertext, then it's ok to use a zero
+ // IV.
+ var iv [aes.BlockSize]byte
+ stream := cipher.NewOFB(block, iv[:])
+
+ outFile, err := os.OpenFile("encrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
+ if err != nil {
+ panic(err)
+ }
+ defer outFile.Close()
+
+ writer := &cipher.StreamWriter{stream, outFile, nil}
+ // Copy the input file to the output file, encrypting as we go.
+ if _, err := io.Copy(writer, inFile); err != nil {
+ panic(err)
+ }
+
+ // Note that this example is simplistic in that it omits any
+ // authentication of the encrypted data. It you were actually to use
+ // StreamReader in this manner, an attacker could flip arbitrary bits in
+ // the decrypted result.
+}
diff --git a/src/pkg/crypto/cipher/io.go b/src/pkg/crypto/cipher/io.go
index 76048fbf3..807e8daea 100644
--- a/src/pkg/crypto/cipher/io.go
+++ b/src/pkg/crypto/cipher/io.go
@@ -28,13 +28,10 @@ func (r StreamReader) Read(dst []byte) (n int, err error) {
type StreamWriter struct {
S Stream
W io.Writer
- Err error
+ Err error // unused
}
func (w StreamWriter) Write(src []byte) (n int, err error) {
- if w.Err != nil {
- return 0, w.Err
- }
c := make([]byte, len(src))
w.S.XORKeyStream(c, src)
n, err = w.W.Write(c)
@@ -42,7 +39,6 @@ func (w StreamWriter) Write(src []byte) (n int, err error) {
if err == nil { // should never happen
err = io.ErrShortWrite
}
- w.Err = err
}
return
}
diff --git a/src/pkg/crypto/des/des_test.go b/src/pkg/crypto/des/des_test.go
index e9fc23629..2e87e99b6 100644
--- a/src/pkg/crypto/des/des_test.go
+++ b/src/pkg/crypto/des/des_test.go
@@ -1503,3 +1503,21 @@ func TestSubstitutionTableKnownAnswerDecrypt(t *testing.T) {
}
}
}
+
+func ExampleNewTripleDESCipher() {
+ // NewTripleDESCipher can also be used when EDE2 is required by
+ // duplicating the first 8 bytes of the 16-byte key.
+ ede2Key := []byte("example key 1234")
+
+ var tripleDESKey []byte
+ tripleDESKey = append(tripleDESKey, ede2Key[:16]...)
+ tripleDESKey = append(tripleDESKey, ede2Key[:8]...)
+
+ _, err := NewTripleDESCipher(tripleDESKey)
+ if err != nil {
+ panic(err)
+ }
+
+ // See crypto/cipher for how to use a cipher.Block for encryption and
+ // decryption.
+}
diff --git a/src/pkg/crypto/ecdsa/ecdsa.go b/src/pkg/crypto/ecdsa/ecdsa.go
index 8508e3b4f..512d20c63 100644
--- a/src/pkg/crypto/ecdsa/ecdsa.go
+++ b/src/pkg/crypto/ecdsa/ecdsa.go
@@ -140,14 +140,16 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
w := new(big.Int).ModInverse(s, N)
u1 := e.Mul(e, w)
+ u1.Mod(u1, N)
u2 := w.Mul(r, w)
+ u2.Mod(u2, N)
x1, y1 := c.ScalarBaseMult(u1.Bytes())
x2, y2 := c.ScalarMult(pub.X, pub.Y, u2.Bytes())
- if x1.Cmp(x2) == 0 {
+ x, y := c.Add(x1, y1, x2, y2)
+ if x.Sign() == 0 && y.Sign() == 0 {
return false
}
- x, _ := c.Add(x1, y1, x2, y2)
x.Mod(x, N)
return x.Cmp(r) == 0
}
diff --git a/src/pkg/crypto/ecdsa/ecdsa_test.go b/src/pkg/crypto/ecdsa/ecdsa_test.go
index 3a2b3efba..0c0643193 100644
--- a/src/pkg/crypto/ecdsa/ecdsa_test.go
+++ b/src/pkg/crypto/ecdsa/ecdsa_test.go
@@ -5,11 +5,19 @@
package ecdsa
import (
+ "bufio"
+ "compress/bzip2"
"crypto/elliptic"
"crypto/rand"
"crypto/sha1"
+ "crypto/sha256"
+ "crypto/sha512"
"encoding/hex"
+ "hash"
+ "io"
"math/big"
+ "os"
+ "strings"
"testing"
)
@@ -72,156 +80,112 @@ func fromHex(s string) *big.Int {
return r
}
-// These test vectors were taken from
-// http://csrc.nist.gov/groups/STM/cavp/documents/dss/ecdsatestvectors.zip
-var testVectors = []struct {
- msg string
- Qx, Qy string
- r, s string
- ok bool
-}{
- {
- "09626b45493672e48f3d1226a3aff3201960e577d33a7f72c7eb055302db8fe8ed61685dd036b554942a5737cd1512cdf811ee0c00e6dd2f08c69f08643be396e85dafda664801e772cdb7396868ac47b172245b41986aa2648cb77fbbfa562581be06651355a0c4b090f9d17d8f0ab6cced4e0c9d386cf465a516630f0231bd",
- "9504b5b82d97a264d8b3735e0568decabc4b6ca275bc53cbadfc1c40",
- "03426f80e477603b10dee670939623e3da91a94267fc4e51726009ed",
- "81d3ac609f9575d742028dd496450a58a60eea2dcf8b9842994916e1",
- "96a8c5f382c992e8f30ccce9af120b067ec1d74678fa8445232f75a5",
- false,
- },
- {
- "96b2b6536f6df29be8567a72528aceeaccbaa66c66c534f3868ca9778b02faadb182e4ed34662e73b9d52ecbe9dc8e875fc05033c493108b380689ebf47e5b062e6a0cdb3dd34ce5fe347d92768d72f7b9b377c20aea927043b509c078ed2467d7113405d2ddd458811e6faf41c403a2a239240180f1430a6f4330df5d77de37",
- "851e3100368a22478a0029353045ae40d1d8202ef4d6533cfdddafd8",
- "205302ac69457dd345e86465afa72ee8c74ca97e2b0b999aec1f10c2",
- "4450c2d38b697e990721aa2dbb56578d32b4f5aeb3b9072baa955ee0",
- "e26d4b589166f7b4ba4b1c8fce823fa47aad22f8c9c396b8c6526e12",
- false,
- },
- {
- "86778dbb4a068a01047a8d245d632f636c11d2ad350740b36fad90428b454ad0f120cb558d12ea5c8a23db595d87543d06d1ef489263d01ee529871eb68737efdb8ff85bc7787b61514bed85b7e01d6be209e0a4eb0db5c8df58a5c5bf706d76cb2bdf7800208639e05b89517155d11688236e6a47ed37d8e5a2b1e0adea338e",
- "ad5bda09d319a717c1721acd6688d17020b31b47eef1edea57ceeffc",
- "c8ce98e181770a7c9418c73c63d01494b8b80a41098c5ea50692c984",
- "de5558c257ab4134e52c19d8db3b224a1899cbd08cc508ce8721d5e9",
- "745db7af5a477e5046705c0a5eff1f52cb94a79d481f0c5a5e108ecd",
- true,
- },
- {
- "4bc6ef1958556686dab1e39c3700054a304cbd8f5928603dcd97fafd1f29e69394679b638f71c9344ce6a535d104803d22119f57b5f9477e253817a52afa9bfbc9811d6cc8c8be6b6566c6ef48b439bbb532abe30627548c598867f3861ba0b154dc1c3deca06eb28df8efd28258554b5179883a36fbb1eecf4f93ee19d41e3d",
- "cc5eea2edf964018bdc0504a3793e4d2145142caa09a72ac5fb8d3e8",
- "a48d78ae5d08aa725342773975a00d4219cf7a8029bb8cf3c17c374a",
- "67b861344b4e416d4094472faf4272f6d54a497177fbc5f9ef292836",
- "1d54f3fcdad795bf3b23408ecbac3e1321d1d66f2e4e3d05f41f7020",
- false,
- },
- {
- "bb658732acbf3147729959eb7318a2058308b2739ec58907dd5b11cfa3ecf69a1752b7b7d806fe00ec402d18f96039f0b78dbb90a59c4414fb33f1f4e02e4089de4122cd93df5263a95be4d7084e2126493892816e6a5b4ed123cb705bf930c8f67af0fb4514d5769232a9b008a803af225160ce63f675bd4872c4c97b146e5e",
- "6234c936e27bf141fc7534bfc0a7eedc657f91308203f1dcbd642855",
- "27983d87ca785ef4892c3591ef4a944b1deb125dd58bd351034a6f84",
- "e94e05b42d01d0b965ffdd6c3a97a36a771e8ea71003de76c4ecb13f",
- "1dc6464ffeefbd7872a081a5926e9fc3e66d123f1784340ba17737e9",
- false,
- },
- {
- "7c00be9123bfa2c4290be1d8bc2942c7f897d9a5b7917e3aabd97ef1aab890f148400a89abd554d19bec9d8ed911ce57b22fbcf6d30ca2115f13ce0a3f569a23bad39ee645f624c49c60dcfc11e7d2be24de9c905596d8f23624d63dc46591d1f740e46f982bfae453f107e80db23545782be23ce43708245896fc54e1ee5c43",
- "9f3f037282aaf14d4772edffff331bbdda845c3f65780498cde334f1",
- "8308ee5a16e3bcb721b6bc30000a0419bc1aaedd761be7f658334066",
- "6381d7804a8808e3c17901e4d283b89449096a8fba993388fa11dc54",
- "8e858f6b5b253686a86b757bad23658cda53115ac565abca4e3d9f57",
- false,
- },
- {
- "cffc122a44840dc705bb37130069921be313d8bde0b66201aebc48add028ca131914ef2e705d6bedd19dc6cf9459bbb0f27cdfe3c50483808ffcdaffbeaa5f062e097180f07a40ef4ab6ed03fe07ed6bcfb8afeb42c97eafa2e8a8df469de07317c5e1494c41547478eff4d8c7d9f0f484ad90fedf6e1c35ee68fa73f1691601",
- "a03b88a10d930002c7b17ca6af2fd3e88fa000edf787dc594f8d4fd4",
- "e0cf7acd6ddc758e64847fe4df9915ebda2f67cdd5ec979aa57421f5",
- "387b84dcf37dc343c7d2c5beb82f0bf8bd894b395a7b894565d296c1",
- "4adc12ce7d20a89ce3925e10491c731b15ddb3f339610857a21b53b4",
- false,
- },
- {
- "26e0e0cafd85b43d16255908ccfd1f061c680df75aba3081246b337495783052ba06c60f4a486c1591a4048bae11b4d7fec4f161d80bdc9a7b79d23e44433ed625eab280521a37f23dd3e1bdc5c6a6cfaa026f3c45cf703e76dab57add93fe844dd4cda67dc3bddd01f9152579e49df60969b10f09ce9372fdd806b0c7301866",
- "9a8983c42f2b5a87c37a00458b5970320d247f0c8a88536440173f7d",
- "15e489ec6355351361900299088cfe8359f04fe0cab78dde952be80c",
- "929a21baa173d438ec9f28d6a585a2f9abcfc0a4300898668e476dc0",
- "59a853f046da8318de77ff43f26fe95a92ee296fa3f7e56ce086c872",
- true,
- },
- {
- "1078eac124f48ae4f807e946971d0de3db3748dd349b14cca5c942560fb25401b2252744f18ad5e455d2d97ed5ae745f55ff509c6c8e64606afe17809affa855c4c4cdcaf6b69ab4846aa5624ed0687541aee6f2224d929685736c6a23906d974d3c257abce1a3fb8db5951b89ecb0cda92b5207d93f6618fd0f893c32cf6a6e",
- "d6e55820bb62c2be97650302d59d667a411956138306bd566e5c3c2b",
- "631ab0d64eaf28a71b9cbd27a7a88682a2167cee6251c44e3810894f",
- "65af72bc7721eb71c2298a0eb4eed3cec96a737cc49125706308b129",
- "bd5a987c78e2d51598dbd9c34a9035b0069c580edefdacee17ad892a",
- false,
- },
- {
- "919deb1fdd831c23481dfdb2475dcbe325b04c34f82561ced3d2df0b3d749b36e255c4928973769d46de8b95f162b53cd666cad9ae145e7fcfba97919f703d864efc11eac5f260a5d920d780c52899e5d76f8fe66936ff82130761231f536e6a3d59792f784902c469aa897aabf9a0678f93446610d56d5e0981e4c8a563556b",
- "269b455b1024eb92d860a420f143ac1286b8cce43031562ae7664574",
- "baeb6ca274a77c44a0247e5eb12ca72bdd9a698b3f3ae69c9f1aaa57",
- "cb4ec2160f04613eb0dfe4608486091a25eb12aa4dec1afe91cfb008",
- "40b01d8cd06589481574f958b98ca08ade9d2a8fe31024375c01bb40",
- false,
- },
- {
- "6e012361250dacf6166d2dd1aa7be544c3206a9d43464b3fcd90f3f8cf48d08ec099b59ba6fe7d9bdcfaf244120aed1695d8be32d1b1cd6f143982ab945d635fb48a7c76831c0460851a3d62b7209c30cd9c2abdbe3d2a5282a9fcde1a6f418dd23c409bc351896b9b34d7d3a1a63bbaf3d677e612d4a80fa14829386a64b33f",
- "6d2d695efc6b43b13c14111f2109608f1020e3e03b5e21cfdbc82fcd",
- "26a4859296b7e360b69cf40be7bd97ceaffa3d07743c8489fc47ca1b",
- "9a8cb5f2fdc288b7183c5b32d8e546fc2ed1ca4285eeae00c8b572ad",
- "8c623f357b5d0057b10cdb1a1593dab57cda7bdec9cf868157a79b97",
- true,
- },
- {
- "bf6bd7356a52b234fe24d25557200971fc803836f6fec3cade9642b13a8e7af10ab48b749de76aada9d8927f9b12f75a2c383ca7358e2566c4bb4f156fce1fd4e87ef8c8d2b6b1bdd351460feb22cdca0437ac10ca5e0abbbce9834483af20e4835386f8b1c96daaa41554ceee56730aac04f23a5c765812efa746051f396566",
- "14250131b2599939cf2d6bc491be80ddfe7ad9de644387ee67de2d40",
- "b5dc473b5d014cd504022043c475d3f93c319a8bdcb7262d9e741803",
- "4f21642f2201278a95339a80f75cc91f8321fcb3c9462562f6cbf145",
- "452a5f816ea1f75dee4fd514fa91a0d6a43622981966c59a1b371ff8",
- false,
- },
- {
- "0eb7f4032f90f0bd3cf9473d6d9525d264d14c031a10acd31a053443ed5fe919d5ac35e0be77813071b4062f0b5fdf58ad5f637b76b0b305aec18f82441b6e607b44cdf6e0e3c7c57f24e6fd565e39430af4a6b1d979821ed0175fa03e3125506847654d7e1ae904ce1190ae38dc5919e257bdac2db142a6e7cd4da6c2e83770",
- "d1f342b7790a1667370a1840255ac5bbbdc66f0bc00ae977d99260ac",
- "76416cabae2de9a1000b4646338b774baabfa3db4673790771220cdb",
- "bc85e3fc143d19a7271b2f9e1c04b86146073f3fab4dda1c3b1f35ca",
- "9a5c70ede3c48d5f43307a0c2a4871934424a3303b815df4bb0f128e",
- false,
- },
- {
- "5cc25348a05d85e56d4b03cec450128727bc537c66ec3a9fb613c151033b5e86878632249cba83adcefc6c1e35dcd31702929c3b57871cda5c18d1cf8f9650a25b917efaed56032e43b6fc398509f0d2997306d8f26675f3a8683b79ce17128e006aa0903b39eeb2f1001be65de0520115e6f919de902b32c38d691a69c58c92",
- "7e49a7abf16a792e4c7bbc4d251820a2abd22d9f2fc252a7bf59c9a6",
- "44236a8fb4791c228c26637c28ae59503a2f450d4cfb0dc42aa843b9",
- "084461b4050285a1a85b2113be76a17878d849e6bc489f4d84f15cd8",
- "079b5bddcc4d45de8dbdfd39f69817c7e5afa454a894d03ee1eaaac3",
- false,
- },
- {
- "1951533ce33afb58935e39e363d8497a8dd0442018fd96dff167b3b23d7206a3ee182a3194765df4768a3284e23b8696c199b4686e670d60c9d782f08794a4bccc05cffffbd1a12acd9eb1cfa01f7ebe124da66ecff4599ea7720c3be4bb7285daa1a86ebf53b042bd23208d468c1b3aa87381f8e1ad63e2b4c2ba5efcf05845",
- "31945d12ebaf4d81f02be2b1768ed80784bf35cf5e2ff53438c11493",
- "a62bebffac987e3b9d3ec451eb64c462cdf7b4aa0b1bbb131ceaa0a4",
- "bc3c32b19e42b710bca5c6aaa128564da3ddb2726b25f33603d2af3c",
- "ed1a719cc0c507edc5239d76fe50e2306c145ad252bd481da04180c0",
- false,
- },
-}
-
func TestVectors(t *testing.T) {
- sha := sha1.New()
+ // This test runs the full set of NIST test vectors from
+ // http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip
+ //
+ // The SigVer.rsp file has been edited to remove test vectors for
+ // unsupported algorithms and has been compressed.
+
+ if testing.Short() {
+ return
+ }
+
+ f, err := os.Open("testdata/SigVer.rsp.bz2")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ buf := bufio.NewReader(bzip2.NewReader(f))
+
+ lineNo := 1
+ var h hash.Hash
+ var msg []byte
+ var hashed []byte
+ var r, s *big.Int
+ pub := new(PublicKey)
- for i, test := range testVectors {
- pub := PublicKey{
- Curve: elliptic.P224(),
- X: fromHex(test.Qx),
- Y: fromHex(test.Qy),
+ for {
+ line, err := buf.ReadString('\n')
+ if len(line) == 0 {
+ if err == io.EOF {
+ break
+ }
+ t.Fatalf("error reading from input: %s", err)
}
- msg, _ := hex.DecodeString(test.msg)
- sha.Reset()
- sha.Write(msg)
- hashed := sha.Sum(nil)
- r := fromHex(test.r)
- s := fromHex(test.s)
- if Verify(&pub, hashed, r, s) != test.ok {
- t.Errorf("%d: bad result", i)
+ lineNo++
+ // Need to remove \r\n from the end of the line.
+ if !strings.HasSuffix(line, "\r\n") {
+ t.Fatalf("bad line ending (expected \\r\\n) on line %d", lineNo)
}
- if testing.Short() {
- break
+ line = line[:len(line)-2]
+
+ if len(line) == 0 || line[0] == '#' {
+ continue
+ }
+
+ if line[0] == '[' {
+ line = line[1 : len(line)-1]
+ parts := strings.SplitN(line, ",", 2)
+
+ switch parts[0] {
+ case "P-224":
+ pub.Curve = elliptic.P224()
+ case "P-256":
+ pub.Curve = elliptic.P256()
+ case "P-384":
+ pub.Curve = elliptic.P384()
+ case "P-521":
+ pub.Curve = elliptic.P521()
+ default:
+ pub.Curve = nil
+ }
+
+ switch parts[1] {
+ case "SHA-1":
+ h = sha1.New()
+ case "SHA-224":
+ h = sha256.New224()
+ case "SHA-256":
+ h = sha256.New()
+ case "SHA-384":
+ h = sha512.New384()
+ case "SHA-512":
+ h = sha512.New()
+ default:
+ h = nil
+ }
+
+ continue
+ }
+
+ if h == nil || pub.Curve == nil {
+ continue
+ }
+
+ switch {
+ case strings.HasPrefix(line, "Msg = "):
+ if msg, err = hex.DecodeString(line[6:]); err != nil {
+ t.Fatalf("failed to decode message on line %d: %s", lineNo, err)
+ }
+ case strings.HasPrefix(line, "Qx = "):
+ pub.X = fromHex(line[5:])
+ case strings.HasPrefix(line, "Qy = "):
+ pub.Y = fromHex(line[5:])
+ case strings.HasPrefix(line, "R = "):
+ r = fromHex(line[4:])
+ case strings.HasPrefix(line, "S = "):
+ s = fromHex(line[4:])
+ case strings.HasPrefix(line, "Result = "):
+ expected := line[9] == 'P'
+ h.Reset()
+ h.Write(msg)
+ hashed := h.Sum(hashed[:0])
+ if Verify(pub, hashed, r, s) != expected {
+ t.Fatalf("incorrect result on line %d", lineNo)
+ }
+ default:
+ t.Fatalf("unknown variable on line %d: %s", lineNo, line)
}
}
}
diff --git a/src/pkg/crypto/ecdsa/testdata/SigVer.rsp.bz2 b/src/pkg/crypto/ecdsa/testdata/SigVer.rsp.bz2
new file mode 100644
index 000000000..09fe2b427
--- /dev/null
+++ b/src/pkg/crypto/ecdsa/testdata/SigVer.rsp.bz2
Binary files differ
diff --git a/src/pkg/crypto/elliptic/elliptic.go b/src/pkg/crypto/elliptic/elliptic.go
index 30835a90b..7a4ff6614 100644
--- a/src/pkg/crypto/elliptic/elliptic.go
+++ b/src/pkg/crypto/elliptic/elliptic.go
@@ -31,10 +31,10 @@ type Curve interface {
// Double returns 2*(x,y)
Double(x1, y1 *big.Int) (x, y *big.Int)
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
- ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int)
- // ScalarBaseMult returns k*G, where G is the base point of the group and k
- // is an integer in big-endian form.
- ScalarBaseMult(scalar []byte) (x, y *big.Int)
+ ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int)
+ // ScalarBaseMult returns k*G, where G is the base point of the group
+ // and k is an integer in big-endian form.
+ ScalarBaseMult(k []byte) (x, y *big.Int)
}
// CurveParams contains the parameters of an elliptic curve and also provides
@@ -69,9 +69,24 @@ func (curve *CurveParams) IsOnCurve(x, y *big.Int) bool {
return x3.Cmp(y2) == 0
}
+// zForAffine returns a Jacobian Z value for the affine point (x, y). If x and
+// y are zero, it assumes that they represent the point at infinity because (0,
+// 0) is not on the any of the curves handled here.
+func zForAffine(x, y *big.Int) *big.Int {
+ z := new(big.Int)
+ if x.Sign() != 0 || y.Sign() != 0 {
+ z.SetInt64(1)
+ }
+ return z
+}
+
// affineFromJacobian reverses the Jacobian transform. See the comment at the
-// top of the file.
+// top of the file. If the point is ∞ it returns 0, 0.
func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
+ if z.Sign() == 0 {
+ return new(big.Int), new(big.Int)
+ }
+
zinv := new(big.Int).ModInverse(z, curve.P)
zinvsq := new(big.Int).Mul(zinv, zinv)
@@ -84,14 +99,29 @@ func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.
}
func (curve *CurveParams) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
- z := new(big.Int).SetInt64(1)
- return curve.affineFromJacobian(curve.addJacobian(x1, y1, z, x2, y2, z))
+ z1 := zForAffine(x1, y1)
+ z2 := zForAffine(x2, y2)
+ return curve.affineFromJacobian(curve.addJacobian(x1, y1, z1, x2, y2, z2))
}
// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
// (x2, y2, z2) and returns their sum, also in Jacobian form.
func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl
+ x3, y3, z3 := new(big.Int), new(big.Int), new(big.Int)
+ if z1.Sign() == 0 {
+ x3.Set(x2)
+ y3.Set(y2)
+ z3.Set(z2)
+ return x3, y3, z3
+ }
+ if z2.Sign() == 0 {
+ x3.Set(x1)
+ y3.Set(y1)
+ z3.Set(z1)
+ return x3, y3, z3
+ }
+
z1z1 := new(big.Int).Mul(z1, z1)
z1z1.Mod(z1z1, curve.P)
z2z2 := new(big.Int).Mul(z2, z2)
@@ -102,6 +132,7 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
u2 := new(big.Int).Mul(x2, z1z1)
u2.Mod(u2, curve.P)
h := new(big.Int).Sub(u2, u1)
+ xEqual := h.Sign() == 0
if h.Sign() == -1 {
h.Add(h, curve.P)
}
@@ -119,17 +150,21 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
if r.Sign() == -1 {
r.Add(r, curve.P)
}
+ yEqual := r.Sign() == 0
+ if xEqual && yEqual {
+ return curve.doubleJacobian(x1, y1, z1)
+ }
r.Lsh(r, 1)
v := new(big.Int).Mul(u1, i)
- x3 := new(big.Int).Set(r)
+ x3.Set(r)
x3.Mul(x3, x3)
x3.Sub(x3, j)
x3.Sub(x3, v)
x3.Sub(x3, v)
x3.Mod(x3, curve.P)
- y3 := new(big.Int).Set(r)
+ y3.Set(r)
v.Sub(v, x3)
y3.Mul(y3, v)
s1.Mul(s1, j)
@@ -137,16 +172,10 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
y3.Sub(y3, s1)
y3.Mod(y3, curve.P)
- z3 := new(big.Int).Add(z1, z2)
+ z3.Add(z1, z2)
z3.Mul(z3, z3)
z3.Sub(z3, z1z1)
- if z3.Sign() == -1 {
- z3.Add(z3, curve.P)
- }
z3.Sub(z3, z2z2)
- if z3.Sign() == -1 {
- z3.Add(z3, curve.P)
- }
z3.Mul(z3, h)
z3.Mod(z3, curve.P)
@@ -154,7 +183,7 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
}
func (curve *CurveParams) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
- z1 := new(big.Int).SetInt64(1)
+ z1 := zForAffine(x1, y1)
return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1))
}
@@ -219,40 +248,19 @@ func (curve *CurveParams) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int,
}
func (curve *CurveParams) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
- // We have a slight problem in that the identity of the group (the
- // point at infinity) cannot be represented in (x, y) form on a finite
- // machine. Thus the standard add/double algorithm has to be tweaked
- // slightly: our initial state is not the identity, but x, and we
- // ignore the first true bit in |k|. If we don't find any true bits in
- // |k|, then we return nil, nil, because we cannot return the identity
- // element.
-
Bz := new(big.Int).SetInt64(1)
- x := Bx
- y := By
- z := Bz
+ x, y, z := new(big.Int), new(big.Int), new(big.Int)
- seenFirstTrue := false
for _, byte := range k {
for bitNum := 0; bitNum < 8; bitNum++ {
- if seenFirstTrue {
- x, y, z = curve.doubleJacobian(x, y, z)
- }
+ x, y, z = curve.doubleJacobian(x, y, z)
if byte&0x80 == 0x80 {
- if !seenFirstTrue {
- seenFirstTrue = true
- } else {
- x, y, z = curve.addJacobian(Bx, By, Bz, x, y, z)
- }
+ x, y, z = curve.addJacobian(Bx, By, Bz, x, y, z)
}
byte <<= 1
}
}
- if !seenFirstTrue {
- return nil, nil
- }
-
return curve.affineFromJacobian(x, y, z)
}
@@ -370,7 +378,7 @@ func P384() Curve {
return p384
}
-// P256 returns a Curve which implements P-521 (see FIPS 186-3, section D.2.5)
+// P521 returns a Curve which implements P-521 (see FIPS 186-3, section D.2.5)
func P521() Curve {
initonce.Do(initAll)
return p521
diff --git a/src/pkg/crypto/elliptic/elliptic_test.go b/src/pkg/crypto/elliptic/elliptic_test.go
index 1e3407ee0..58f903966 100644
--- a/src/pkg/crypto/elliptic/elliptic_test.go
+++ b/src/pkg/crypto/elliptic/elliptic_test.go
@@ -322,6 +322,44 @@ func TestGenericBaseMult(t *testing.T) {
}
}
+func TestInfinity(t *testing.T) {
+ tests := []struct {
+ name string
+ curve Curve
+ }{
+ {"p224", P224()},
+ {"p256", P256()},
+ }
+
+ for _, test := range tests {
+ curve := test.curve
+ x, y := curve.ScalarBaseMult(nil)
+ if x.Sign() != 0 || y.Sign() != 0 {
+ t.Errorf("%s: x^0 != ∞", test.name)
+ }
+ x.SetInt64(0)
+ y.SetInt64(0)
+
+ x2, y2 := curve.Double(x, y)
+ if x2.Sign() != 0 || y2.Sign() != 0 {
+ t.Errorf("%s: 2∞ != ∞", test.name)
+ }
+
+ baseX := curve.Params().Gx
+ baseY := curve.Params().Gy
+
+ x3, y3 := curve.Add(baseX, baseY, x, y)
+ if x3.Cmp(baseX) != 0 || y3.Cmp(baseY) != 0 {
+ t.Errorf("%s: x+∞ != x", test.name)
+ }
+
+ x4, y4 := curve.Add(x, y, baseX, baseY)
+ if x4.Cmp(baseX) != 0 || y4.Cmp(baseY) != 0 {
+ t.Errorf("%s: ∞+x != x", test.name)
+ }
+ }
+}
+
func BenchmarkBaseMult(b *testing.B) {
b.ResetTimer()
p224 := P224()
diff --git a/src/pkg/crypto/elliptic/p224.go b/src/pkg/crypto/elliptic/p224.go
index 17571c252..1f7ff3f9d 100644
--- a/src/pkg/crypto/elliptic/p224.go
+++ b/src/pkg/crypto/elliptic/p224.go
@@ -80,10 +80,14 @@ func (p224Curve) Add(bigX1, bigY1, bigX2, bigY2 *big.Int) (x, y *big.Int) {
p224FromBig(&x1, bigX1)
p224FromBig(&y1, bigY1)
- z1[0] = 1
+ if bigX1.Sign() != 0 || bigY1.Sign() != 0 {
+ z1[0] = 1
+ }
p224FromBig(&x2, bigX2)
p224FromBig(&y2, bigY2)
- z2[0] = 1
+ if bigX2.Sign() != 0 || bigY2.Sign() != 0 {
+ z2[0] = 1
+ }
p224AddJacobian(&x3, &y3, &z3, &x1, &y1, &z1, &x2, &y2, &z2)
return p224ToAffine(&x3, &y3, &z3)
@@ -132,6 +136,44 @@ func (curve p224Curve) ScalarBaseMult(scalar []byte) (x, y *big.Int) {
// exactly, making the reflections during a reduce much nicer.
type p224FieldElement [8]uint32
+// p224P is the order of the field, represented as a p224FieldElement.
+var p224P = [8]uint32{1, 0, 0, 0xffff000, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff}
+
+// p224IsZero returns 1 if a == 0 mod p and 0 otherwise.
+//
+// a[i] < 2**29
+func p224IsZero(a *p224FieldElement) uint32 {
+ // Since a p224FieldElement contains 224 bits there are two possible
+ // representations of 0: 0 and p.
+ var minimal p224FieldElement
+ p224Contract(&minimal, a)
+
+ var isZero, isP uint32
+ for i, v := range minimal {
+ isZero |= v
+ isP |= v - p224P[i]
+ }
+
+ // If either isZero or isP is 0, then we should return 1.
+ isZero |= isZero >> 16
+ isZero |= isZero >> 8
+ isZero |= isZero >> 4
+ isZero |= isZero >> 2
+ isZero |= isZero >> 1
+
+ isP |= isP >> 16
+ isP |= isP >> 8
+ isP |= isP >> 4
+ isP |= isP >> 2
+ isP |= isP >> 1
+
+ // For isZero and isP, the LSB is 0 iff all the bits are zero.
+ result := isZero & isP
+ result = (^result) & 1
+
+ return result
+}
+
// p224Add computes *out = a+b
//
// a[i] + b[i] < 2**32
@@ -406,7 +448,7 @@ func p224Contract(out, in *p224FieldElement) {
// true.
top4AllOnes := uint32(0xffffffff)
for i := 4; i < 8; i++ {
- top4AllOnes &= (out[i] & bottom28Bits) - 1
+ top4AllOnes &= out[i]
}
top4AllOnes |= 0xf0000000
// Now we replicate any zero bits to all the bits in top4AllOnes.
@@ -441,7 +483,7 @@ func p224Contract(out, in *p224FieldElement) {
out3Equal = ^uint32(int32(out3Equal<<31) >> 31)
// If out[3] > 0xffff000 then n's MSB will be zero.
- out3GT := ^uint32(int32(n<<31) >> 31)
+ out3GT := ^uint32(int32(n) >> 31)
mask := top4AllOnes & ((out3Equal & bottom3NonZero) | out3GT)
out[0] -= 1 & mask
@@ -463,6 +505,9 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) {
var z1z1, z2z2, u1, u2, s1, s2, h, i, j, r, v p224FieldElement
var c p224LargeFieldElement
+ z1IsZero := p224IsZero(z1)
+ z2IsZero := p224IsZero(z2)
+
// Z1Z1 = Z1²
p224Square(&z1z1, z1, &c)
// Z2Z2 = Z2²
@@ -480,6 +525,7 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) {
// H = U2-U1
p224Sub(&h, &u2, &u1)
p224Reduce(&h)
+ xEqual := p224IsZero(&h)
// I = (2*H)²
for j := 0; j < 8; j++ {
i[j] = h[j] << 1
@@ -491,6 +537,11 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) {
// r = 2*(S2-S1)
p224Sub(&r, &s2, &s1)
p224Reduce(&r)
+ yEqual := p224IsZero(&r)
+ if xEqual == 1 && yEqual == 1 && z1IsZero == 0 && z2IsZero == 0 {
+ p224DoubleJacobian(x3, y3, z3, x1, y1, z1)
+ return
+ }
for i := 0; i < 8; i++ {
r[i] <<= 1
}
@@ -524,6 +575,13 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) {
p224Mul(&z1z1, &z1z1, &r, &c)
p224Sub(y3, &z1z1, &s1)
p224Reduce(y3)
+
+ p224CopyConditional(x3, x2, z1IsZero)
+ p224CopyConditional(x3, x1, z2IsZero)
+ p224CopyConditional(y3, y2, z1IsZero)
+ p224CopyConditional(y3, y1, z2IsZero)
+ p224CopyConditional(z3, z2, z1IsZero)
+ p224CopyConditional(z3, z1, z2IsZero)
}
// p224DoubleJacobian computes *out = a+a.
@@ -593,22 +651,19 @@ func p224CopyConditional(out, in *p224FieldElement, control uint32) {
func p224ScalarMult(outX, outY, outZ, inX, inY, inZ *p224FieldElement, scalar []byte) {
var xx, yy, zz p224FieldElement
for i := 0; i < 8; i++ {
+ outX[i] = 0
+ outY[i] = 0
outZ[i] = 0
}
- firstBit := uint32(1)
for _, byte := range scalar {
for bitNum := uint(0); bitNum < 8; bitNum++ {
p224DoubleJacobian(outX, outY, outZ, outX, outY, outZ)
bit := uint32((byte >> (7 - bitNum)) & 1)
p224AddJacobian(&xx, &yy, &zz, inX, inY, inZ, outX, outY, outZ)
- p224CopyConditional(outX, inX, firstBit&bit)
- p224CopyConditional(outY, inY, firstBit&bit)
- p224CopyConditional(outZ, inZ, firstBit&bit)
- p224CopyConditional(outX, &xx, ^firstBit&bit)
- p224CopyConditional(outY, &yy, ^firstBit&bit)
- p224CopyConditional(outZ, &zz, ^firstBit&bit)
- firstBit = firstBit & ^bit
+ p224CopyConditional(outX, &xx, bit)
+ p224CopyConditional(outY, &yy, bit)
+ p224CopyConditional(outZ, &zz, bit)
}
}
}
@@ -618,16 +673,8 @@ func p224ToAffine(x, y, z *p224FieldElement) (*big.Int, *big.Int) {
var zinv, zinvsq, outx, outy p224FieldElement
var tmp p224LargeFieldElement
- isPointAtInfinity := true
- for i := 0; i < 8; i++ {
- if z[i] != 0 {
- isPointAtInfinity = false
- break
- }
- }
-
- if isPointAtInfinity {
- return nil, nil
+ if isPointAtInfinity := p224IsZero(z); isPointAtInfinity == 1 {
+ return new(big.Int), new(big.Int)
}
p224Invert(&zinv, z)
diff --git a/src/pkg/crypto/hmac/hmac.go b/src/pkg/crypto/hmac/hmac.go
index a97ce0972..b6f4919a7 100644
--- a/src/pkg/crypto/hmac/hmac.go
+++ b/src/pkg/crypto/hmac/hmac.go
@@ -2,13 +2,27 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package hmac implements the Keyed-Hash Message Authentication Code (HMAC) as
-// defined in U.S. Federal Information Processing Standards Publication 198.
-// An HMAC is a cryptographic hash that uses a key to sign a message.
-// The receiver verifies the hash by recomputing it using the same key.
+/*
+Package hmac implements the Keyed-Hash Message Authentication Code (HMAC) as
+defined in U.S. Federal Information Processing Standards Publication 198.
+An HMAC is a cryptographic hash that uses a key to sign a message.
+The receiver verifies the hash by recomputing it using the same key.
+
+Receivers should be careful to use Equal to compare MACs in order to avoid
+timing side-channels:
+
+ // CheckMAC returns true if messageMAC is a valid HMAC tag for message.
+ func CheckMAC(message, messageMAC, key []byte) bool {
+ mac := hmac.New(sha256.New, key)
+ mac.Write(message)
+ expectedMAC := mac.Sum(nil)
+ return hmac.Equal(messageMAC, expectedMAC)
+ }
+*/
package hmac
import (
+ "crypto/subtle"
"hash"
)
@@ -57,7 +71,7 @@ func (h *hmac) BlockSize() int { return h.blocksize }
func (h *hmac) Reset() {
h.inner.Reset()
h.tmpPad(0x36)
- h.inner.Write(h.tmp[0:h.blocksize])
+ h.inner.Write(h.tmp[:h.blocksize])
}
// New returns a new HMAC hash using the given hash.Hash type and key.
@@ -78,3 +92,11 @@ func New(h func() hash.Hash, key []byte) hash.Hash {
hm.Reset()
return hm
}
+
+// Equal compares two MACs for equality without leaking timing information.
+func Equal(mac1, mac2 []byte) bool {
+ // We don't have to be constant time if the lengths of the MACs are
+ // different as that suggests that a completely different hash function
+ // was used.
+ return len(mac1) == len(mac2) && subtle.ConstantTimeCompare(mac1, mac2) == 1
+}
diff --git a/src/pkg/crypto/hmac/hmac_test.go b/src/pkg/crypto/hmac/hmac_test.go
index 07957414c..d4860424e 100644
--- a/src/pkg/crypto/hmac/hmac_test.go
+++ b/src/pkg/crypto/hmac/hmac_test.go
@@ -491,3 +491,22 @@ func TestHMAC(t *testing.T) {
}
}
}
+
+func TestEqual(t *testing.T) {
+ a := []byte("test")
+ b := []byte("test1")
+ c := []byte("test2")
+
+ if !Equal(b, b) {
+ t.Error("Equal failed with equal arguments")
+ }
+ if Equal(a, b) {
+ t.Error("Equal accepted a prefix of the second argument")
+ }
+ if Equal(b, a) {
+ t.Error("Equal accepted a prefix of the first argument")
+ }
+ if Equal(b, c) {
+ t.Error("Equal accepted unequal slices")
+ }
+}
diff --git a/src/pkg/crypto/md5/gen.go b/src/pkg/crypto/md5/gen.go
new file mode 100644
index 000000000..966bdae26
--- /dev/null
+++ b/src/pkg/crypto/md5/gen.go
@@ -0,0 +1,300 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// This program generates md5block.go
+// Invoke as
+//
+// go run gen.go [-full] |gofmt >md5block.go
+//
+// The -full flag causes the generated code to do a full
+// (16x) unrolling instead of a 4x unrolling.
+
+package main
+
+import (
+ "flag"
+ "log"
+ "os"
+ "strings"
+ "text/template"
+)
+
+func main() {
+ flag.Parse()
+
+ t := template.Must(template.New("main").Funcs(funcs).Parse(program))
+ if err := t.Execute(os.Stdout, data); err != nil {
+ log.Fatal(err)
+ }
+}
+
+type Data struct {
+ a, b, c, d string
+ Shift1 []int
+ Shift2 []int
+ Shift3 []int
+ Shift4 []int
+ Table1 []uint32
+ Table2 []uint32
+ Table3 []uint32
+ Table4 []uint32
+ Full bool
+}
+
+var funcs = template.FuncMap{
+ "dup": dup,
+ "relabel": relabel,
+ "rotate": rotate,
+}
+
+func dup(count int, x []int) []int {
+ var out []int
+ for i := 0; i < count; i++ {
+ out = append(out, x...)
+ }
+ return out
+}
+
+func relabel(s string) string {
+ return strings.NewReplacer("a", data.a, "b", data.b, "c", data.c, "d", data.d).Replace(s)
+}
+
+func rotate() string {
+ data.a, data.b, data.c, data.d = data.d, data.a, data.b, data.c
+ return "" // no output
+}
+
+func init() {
+ flag.BoolVar(&data.Full, "full", false, "complete unrolling")
+}
+
+var data = Data{
+ a: "a",
+ b: "b",
+ c: "c",
+ d: "d",
+ Shift1: []int{7, 12, 17, 22},
+ Shift2: []int{5, 9, 14, 20},
+ Shift3: []int{4, 11, 16, 23},
+ Shift4: []int{6, 10, 15, 21},
+
+ // table[i] = int((1<<32) * abs(sin(i+1 radians))).
+ Table1: []uint32{
+ // round 1
+ 0xd76aa478,
+ 0xe8c7b756,
+ 0x242070db,
+ 0xc1bdceee,
+ 0xf57c0faf,
+ 0x4787c62a,
+ 0xa8304613,
+ 0xfd469501,
+ 0x698098d8,
+ 0x8b44f7af,
+ 0xffff5bb1,
+ 0x895cd7be,
+ 0x6b901122,
+ 0xfd987193,
+ 0xa679438e,
+ 0x49b40821,
+ },
+ Table2: []uint32{
+ // round 2
+ 0xf61e2562,
+ 0xc040b340,
+ 0x265e5a51,
+ 0xe9b6c7aa,
+ 0xd62f105d,
+ 0x2441453,
+ 0xd8a1e681,
+ 0xe7d3fbc8,
+ 0x21e1cde6,
+ 0xc33707d6,
+ 0xf4d50d87,
+ 0x455a14ed,
+ 0xa9e3e905,
+ 0xfcefa3f8,
+ 0x676f02d9,
+ 0x8d2a4c8a,
+ },
+ Table3: []uint32{
+ // round3
+ 0xfffa3942,
+ 0x8771f681,
+ 0x6d9d6122,
+ 0xfde5380c,
+ 0xa4beea44,
+ 0x4bdecfa9,
+ 0xf6bb4b60,
+ 0xbebfbc70,
+ 0x289b7ec6,
+ 0xeaa127fa,
+ 0xd4ef3085,
+ 0x4881d05,
+ 0xd9d4d039,
+ 0xe6db99e5,
+ 0x1fa27cf8,
+ 0xc4ac5665,
+ },
+ Table4: []uint32{
+ // round 4
+ 0xf4292244,
+ 0x432aff97,
+ 0xab9423a7,
+ 0xfc93a039,
+ 0x655b59c3,
+ 0x8f0ccc92,
+ 0xffeff47d,
+ 0x85845dd1,
+ 0x6fa87e4f,
+ 0xfe2ce6e0,
+ 0xa3014314,
+ 0x4e0811a1,
+ 0xf7537e82,
+ 0xbd3af235,
+ 0x2ad7d2bb,
+ 0xeb86d391,
+ },
+}
+
+var program = `
+package md5
+
+import (
+ "unsafe"
+ "runtime"
+)
+
+{{if not .Full}}
+ var t1 = [...]uint32{
+ {{range .Table1}}{{printf "\t%#x,\n" .}}{{end}}
+ }
+
+ var t2 = [...]uint32{
+ {{range .Table2}}{{printf "\t%#x,\n" .}}{{end}}
+ }
+
+ var t3 = [...]uint32{
+ {{range .Table3}}{{printf "\t%#x,\n" .}}{{end}}
+ }
+
+ var t4 = [...]uint32{
+ {{range .Table4}}{{printf "\t%#x,\n" .}}{{end}}
+ }
+{{end}}
+
+func block(dig *digest, p []byte) {
+ a := dig.s[0]
+ b := dig.s[1]
+ c := dig.s[2]
+ d := dig.s[3]
+ var X *[16]uint32
+ var xbuf [16]uint32
+ for len(p) >= chunk {
+ aa, bb, cc, dd := a, b, c, d
+
+ // This is a constant condition - it is not evaluated on each iteration.
+ if runtime.GOARCH == "amd64" || runtime.GOARCH == "386" {
+ // MD5 was designed so that x86 processors can just iterate
+ // over the block data directly as uint32s, and we generate
+ // less code and run 1.3x faster if we take advantage of that.
+ // My apologies.
+ X = (*[16]uint32)(unsafe.Pointer(&p[0]))
+ } else if uintptr(unsafe.Pointer(&p[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
+ X = (*[16]uint32)(unsafe.Pointer(&p[0]))
+ } else {
+ X = &xbuf
+ j := 0
+ for i := 0; i < 16; i++ {
+ X[i&15] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24
+ j += 4
+ }
+ }
+
+ {{if .Full}}
+ // Round 1.
+ {{range $i, $s := dup 4 .Shift1}}
+ {{index $.Table1 $i | printf "a += (((c^d)&b)^d) + X[%d] + %d" $i | relabel}}
+ {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
+ {{rotate}}
+ {{end}}
+
+ // Round 2.
+ {{range $i, $s := dup 4 .Shift2}}
+ {{index $.Table2 $i | printf "a += (((b^c)&d)^c) + X[(1+5*%d)&15] + %d" $i | relabel}}
+ {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
+ {{rotate}}
+ {{end}}
+
+ // Round 3.
+ {{range $i, $s := dup 4 .Shift3}}
+ {{index $.Table3 $i | printf "a += (b^c^d) + X[(5+3*%d)&15] + %d" $i | relabel}}
+ {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
+ {{rotate}}
+ {{end}}
+
+ // Round 4.
+ {{range $i, $s := dup 4 .Shift4}}
+ {{index $.Table4 $i | printf "a += (c^(b|^d)) + X[(7*%d)&15] + %d" $i | relabel}}
+ {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
+ {{rotate}}
+ {{end}}
+ {{else}}
+ // Round 1.
+ for i := uint(0); i < 16; {
+ {{range $s := .Shift1}}
+ {{printf "a += (((c^d)&b)^d) + X[i&15] + t1[i&15]" | relabel}}
+ {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
+ i++
+ {{rotate}}
+ {{end}}
+ }
+
+ // Round 2.
+ for i := uint(0); i < 16; {
+ {{range $s := .Shift2}}
+ {{printf "a += (((b^c)&d)^c) + X[(1+5*i)&15] + t2[i&15]" | relabel}}
+ {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
+ i++
+ {{rotate}}
+ {{end}}
+ }
+
+ // Round 3.
+ for i := uint(0); i < 16; {
+ {{range $s := .Shift3}}
+ {{printf "a += (b^c^d) + X[(5+3*i)&15] + t3[i&15]" | relabel}}
+ {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
+ i++
+ {{rotate}}
+ {{end}}
+ }
+
+ // Round 4.
+ for i := uint(0); i < 16; {
+ {{range $s := .Shift4}}
+ {{printf "a += (c^(b|^d)) + X[(7*i)&15] + t4[i&15]" | relabel}}
+ {{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
+ i++
+ {{rotate}}
+ {{end}}
+ }
+ {{end}}
+
+ a += aa
+ b += bb
+ c += cc
+ d += dd
+
+ p = p[chunk:]
+ }
+
+ dig.s[0] = a
+ dig.s[1] = b
+ dig.s[2] = c
+ dig.s[3] = d
+}
+`
diff --git a/src/pkg/crypto/md5/md5.go b/src/pkg/crypto/md5/md5.go
index cfb728c94..825e5c8a2 100644
--- a/src/pkg/crypto/md5/md5.go
+++ b/src/pkg/crypto/md5/md5.go
@@ -21,26 +21,26 @@ const Size = 16
const BlockSize = 64
const (
- _Chunk = 64
- _Init0 = 0x67452301
- _Init1 = 0xEFCDAB89
- _Init2 = 0x98BADCFE
- _Init3 = 0x10325476
+ chunk = 64
+ init0 = 0x67452301
+ init1 = 0xEFCDAB89
+ init2 = 0x98BADCFE
+ init3 = 0x10325476
)
// digest represents the partial evaluation of a checksum.
type digest struct {
s [4]uint32
- x [_Chunk]byte
+ x [chunk]byte
nx int
len uint64
}
func (d *digest) Reset() {
- d.s[0] = _Init0
- d.s[1] = _Init1
- d.s[2] = _Init2
- d.s[3] = _Init3
+ d.s[0] = init0
+ d.s[1] = init1
+ d.s[2] = init2
+ d.s[3] = init3
d.nx = 0
d.len = 0
}
@@ -61,21 +61,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
d.len += uint64(nn)
if d.nx > 0 {
n := len(p)
- if n > _Chunk-d.nx {
- n = _Chunk - d.nx
+ if n > chunk-d.nx {
+ n = chunk - d.nx
}
for i := 0; i < n; i++ {
d.x[d.nx+i] = p[i]
}
d.nx += n
- if d.nx == _Chunk {
- _Block(d, d.x[0:])
+ if d.nx == chunk {
+ block(d, d.x[0:chunk])
d.nx = 0
}
p = p[n:]
}
- n := _Block(d, p)
- p = p[n:]
+ if len(p) >= chunk {
+ n := len(p) &^ (chunk - 1)
+ block(d, p[:n])
+ p = p[n:]
+ }
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
diff --git a/src/pkg/crypto/md5/md5_test.go b/src/pkg/crypto/md5/md5_test.go
index aae875464..3ef4519b9 100644
--- a/src/pkg/crypto/md5/md5_test.go
+++ b/src/pkg/crypto/md5/md5_test.go
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package md5_test
+package md5
import (
- "crypto/md5"
"fmt"
"io"
"testing"
+ "unsafe"
)
type md5Test struct {
@@ -53,14 +53,20 @@ var golden = []md5Test{
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
- c := md5.New()
- for j := 0; j < 3; j++ {
+ c := New()
+ buf := make([]byte, len(g.in)+4)
+ for j := 0; j < 3+4; j++ {
if j < 2 {
io.WriteString(c, g.in)
- } else {
+ } else if j == 2 {
io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:])
+ } else if j > 2 {
+ // test unaligned write
+ buf = buf[1:]
+ copy(buf, g.in)
+ c.Write(buf[:len(g.in)])
}
s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out {
@@ -72,9 +78,53 @@ func TestGolden(t *testing.T) {
}
func ExampleNew() {
- h := md5.New()
+ h := New()
io.WriteString(h, "The fog is getting thicker!")
io.WriteString(h, "And Leon's getting laaarger!")
fmt.Printf("%x", h.Sum(nil))
// Output: e2c569be17396eca2a2e3c11578123ed
}
+
+var bench = New()
+var buf = make([]byte, 8192+1)
+var sum = make([]byte, bench.Size())
+
+func benchmarkSize(b *testing.B, size int, unaligned bool) {
+ b.SetBytes(int64(size))
+ buf := buf
+ if unaligned {
+ if uintptr(unsafe.Pointer(&buf[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
+ buf = buf[1:]
+ }
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bench.Reset()
+ bench.Write(buf[:size])
+ bench.Sum(sum[:0])
+ }
+}
+
+func BenchmarkHash8Bytes(b *testing.B) {
+ benchmarkSize(b, 8, false)
+}
+
+func BenchmarkHash1K(b *testing.B) {
+ benchmarkSize(b, 1024, false)
+}
+
+func BenchmarkHash8K(b *testing.B) {
+ benchmarkSize(b, 8192, false)
+}
+
+func BenchmarkHash8BytesUnaligned(b *testing.B) {
+ benchmarkSize(b, 8, true)
+}
+
+func BenchmarkHash1KUnaligned(b *testing.B) {
+ benchmarkSize(b, 1024, true)
+}
+
+func BenchmarkHash8KUnaligned(b *testing.B) {
+ benchmarkSize(b, 8192, true)
+}
diff --git a/src/pkg/crypto/md5/md5block.go b/src/pkg/crypto/md5/md5block.go
index a887e2e05..0ca421774 100644
--- a/src/pkg/crypto/md5/md5block.go
+++ b/src/pkg/crypto/md5/md5block.go
@@ -1,172 +1,258 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+package md5
-// MD5 block step.
-// In its own file so that a faster assembly or C version
-// can be substituted easily.
+import (
+ "runtime"
+ "unsafe"
+)
-package md5
+const x86 = runtime.GOARCH == "amd64" || runtime.GOARCH == "386"
-// table[i] = int((1<<32) * abs(sin(i+1 radians))).
-var table = []uint32{
- // round 1
- 0xd76aa478,
- 0xe8c7b756,
- 0x242070db,
- 0xc1bdceee,
- 0xf57c0faf,
- 0x4787c62a,
- 0xa8304613,
- 0xfd469501,
- 0x698098d8,
- 0x8b44f7af,
- 0xffff5bb1,
- 0x895cd7be,
- 0x6b901122,
- 0xfd987193,
- 0xa679438e,
- 0x49b40821,
-
- // round 2
- 0xf61e2562,
- 0xc040b340,
- 0x265e5a51,
- 0xe9b6c7aa,
- 0xd62f105d,
- 0x2441453,
- 0xd8a1e681,
- 0xe7d3fbc8,
- 0x21e1cde6,
- 0xc33707d6,
- 0xf4d50d87,
- 0x455a14ed,
- 0xa9e3e905,
- 0xfcefa3f8,
- 0x676f02d9,
- 0x8d2a4c8a,
-
- // round3
- 0xfffa3942,
- 0x8771f681,
- 0x6d9d6122,
- 0xfde5380c,
- 0xa4beea44,
- 0x4bdecfa9,
- 0xf6bb4b60,
- 0xbebfbc70,
- 0x289b7ec6,
- 0xeaa127fa,
- 0xd4ef3085,
- 0x4881d05,
- 0xd9d4d039,
- 0xe6db99e5,
- 0x1fa27cf8,
- 0xc4ac5665,
-
- // round 4
- 0xf4292244,
- 0x432aff97,
- 0xab9423a7,
- 0xfc93a039,
- 0x655b59c3,
- 0x8f0ccc92,
- 0xffeff47d,
- 0x85845dd1,
- 0x6fa87e4f,
- 0xfe2ce6e0,
- 0xa3014314,
- 0x4e0811a1,
- 0xf7537e82,
- 0xbd3af235,
- 0x2ad7d2bb,
- 0xeb86d391,
-}
+var littleEndian bool
-var shift1 = []uint{7, 12, 17, 22}
-var shift2 = []uint{5, 9, 14, 20}
-var shift3 = []uint{4, 11, 16, 23}
-var shift4 = []uint{6, 10, 15, 21}
+func init() {
+ x := uint32(0x04030201)
+ y := [4]byte{0x1, 0x2, 0x3, 0x4}
+ littleEndian = *(*[4]byte)(unsafe.Pointer(&x)) == y
+}
-func _Block(dig *digest, p []byte) int {
+func block(dig *digest, p []byte) {
a := dig.s[0]
b := dig.s[1]
c := dig.s[2]
d := dig.s[3]
- n := 0
- var X [16]uint32
- for len(p) >= _Chunk {
+ var X *[16]uint32
+ var xbuf [16]uint32
+ for len(p) >= chunk {
aa, bb, cc, dd := a, b, c, d
- j := 0
- for i := 0; i < 16; i++ {
- X[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24
- j += 4
+ // This is a constant condition - it is not evaluated on each iteration.
+ if x86 {
+ // MD5 was designed so that x86 processors can just iterate
+ // over the block data directly as uint32s, and we generate
+ // less code and run 1.3x faster if we take advantage of that.
+ // My apologies.
+ X = (*[16]uint32)(unsafe.Pointer(&p[0]))
+ } else if littleEndian && uintptr(unsafe.Pointer(&p[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
+ X = (*[16]uint32)(unsafe.Pointer(&p[0]))
+ } else {
+ X = &xbuf
+ j := 0
+ for i := 0; i < 16; i++ {
+ X[i&15] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24
+ j += 4
+ }
}
- // If this needs to be made faster in the future,
- // the usual trick is to unroll each of these
- // loops by a factor of 4; that lets you replace
- // the shift[] lookups with constants and,
- // with suitable variable renaming in each
- // unrolled body, delete the a, b, c, d = d, a, b, c
- // (or you can let the optimizer do the renaming).
- //
- // The index variables are uint so that % by a power
- // of two can be optimized easily by a compiler.
-
// Round 1.
- for i := uint(0); i < 16; i++ {
- x := i
- s := shift1[i%4]
- f := ((c ^ d) & b) ^ d
- a += f + X[x] + table[i]
- a = a<<s | a>>(32-s) + b
- a, b, c, d = d, a, b, c
- }
+
+ a += (((c ^ d) & b) ^ d) + X[0] + 3614090360
+ a = a<<7 | a>>(32-7) + b
+
+ d += (((b ^ c) & a) ^ c) + X[1] + 3905402710
+ d = d<<12 | d>>(32-12) + a
+
+ c += (((a ^ b) & d) ^ b) + X[2] + 606105819
+ c = c<<17 | c>>(32-17) + d
+
+ b += (((d ^ a) & c) ^ a) + X[3] + 3250441966
+ b = b<<22 | b>>(32-22) + c
+
+ a += (((c ^ d) & b) ^ d) + X[4] + 4118548399
+ a = a<<7 | a>>(32-7) + b
+
+ d += (((b ^ c) & a) ^ c) + X[5] + 1200080426
+ d = d<<12 | d>>(32-12) + a
+
+ c += (((a ^ b) & d) ^ b) + X[6] + 2821735955
+ c = c<<17 | c>>(32-17) + d
+
+ b += (((d ^ a) & c) ^ a) + X[7] + 4249261313
+ b = b<<22 | b>>(32-22) + c
+
+ a += (((c ^ d) & b) ^ d) + X[8] + 1770035416
+ a = a<<7 | a>>(32-7) + b
+
+ d += (((b ^ c) & a) ^ c) + X[9] + 2336552879
+ d = d<<12 | d>>(32-12) + a
+
+ c += (((a ^ b) & d) ^ b) + X[10] + 4294925233
+ c = c<<17 | c>>(32-17) + d
+
+ b += (((d ^ a) & c) ^ a) + X[11] + 2304563134
+ b = b<<22 | b>>(32-22) + c
+
+ a += (((c ^ d) & b) ^ d) + X[12] + 1804603682
+ a = a<<7 | a>>(32-7) + b
+
+ d += (((b ^ c) & a) ^ c) + X[13] + 4254626195
+ d = d<<12 | d>>(32-12) + a
+
+ c += (((a ^ b) & d) ^ b) + X[14] + 2792965006
+ c = c<<17 | c>>(32-17) + d
+
+ b += (((d ^ a) & c) ^ a) + X[15] + 1236535329
+ b = b<<22 | b>>(32-22) + c
// Round 2.
- for i := uint(0); i < 16; i++ {
- x := (1 + 5*i) % 16
- s := shift2[i%4]
- g := ((b ^ c) & d) ^ c
- a += g + X[x] + table[i+16]
- a = a<<s | a>>(32-s) + b
- a, b, c, d = d, a, b, c
- }
+
+ a += (((b ^ c) & d) ^ c) + X[(1+5*0)&15] + 4129170786
+ a = a<<5 | a>>(32-5) + b
+
+ d += (((a ^ b) & c) ^ b) + X[(1+5*1)&15] + 3225465664
+ d = d<<9 | d>>(32-9) + a
+
+ c += (((d ^ a) & b) ^ a) + X[(1+5*2)&15] + 643717713
+ c = c<<14 | c>>(32-14) + d
+
+ b += (((c ^ d) & a) ^ d) + X[(1+5*3)&15] + 3921069994
+ b = b<<20 | b>>(32-20) + c
+
+ a += (((b ^ c) & d) ^ c) + X[(1+5*4)&15] + 3593408605
+ a = a<<5 | a>>(32-5) + b
+
+ d += (((a ^ b) & c) ^ b) + X[(1+5*5)&15] + 38016083
+ d = d<<9 | d>>(32-9) + a
+
+ c += (((d ^ a) & b) ^ a) + X[(1+5*6)&15] + 3634488961
+ c = c<<14 | c>>(32-14) + d
+
+ b += (((c ^ d) & a) ^ d) + X[(1+5*7)&15] + 3889429448
+ b = b<<20 | b>>(32-20) + c
+
+ a += (((b ^ c) & d) ^ c) + X[(1+5*8)&15] + 568446438
+ a = a<<5 | a>>(32-5) + b
+
+ d += (((a ^ b) & c) ^ b) + X[(1+5*9)&15] + 3275163606
+ d = d<<9 | d>>(32-9) + a
+
+ c += (((d ^ a) & b) ^ a) + X[(1+5*10)&15] + 4107603335
+ c = c<<14 | c>>(32-14) + d
+
+ b += (((c ^ d) & a) ^ d) + X[(1+5*11)&15] + 1163531501
+ b = b<<20 | b>>(32-20) + c
+
+ a += (((b ^ c) & d) ^ c) + X[(1+5*12)&15] + 2850285829
+ a = a<<5 | a>>(32-5) + b
+
+ d += (((a ^ b) & c) ^ b) + X[(1+5*13)&15] + 4243563512
+ d = d<<9 | d>>(32-9) + a
+
+ c += (((d ^ a) & b) ^ a) + X[(1+5*14)&15] + 1735328473
+ c = c<<14 | c>>(32-14) + d
+
+ b += (((c ^ d) & a) ^ d) + X[(1+5*15)&15] + 2368359562
+ b = b<<20 | b>>(32-20) + c
// Round 3.
- for i := uint(0); i < 16; i++ {
- x := (5 + 3*i) % 16
- s := shift3[i%4]
- h := b ^ c ^ d
- a += h + X[x] + table[i+32]
- a = a<<s | a>>(32-s) + b
- a, b, c, d = d, a, b, c
- }
+
+ a += (b ^ c ^ d) + X[(5+3*0)&15] + 4294588738
+ a = a<<4 | a>>(32-4) + b
+
+ d += (a ^ b ^ c) + X[(5+3*1)&15] + 2272392833
+ d = d<<11 | d>>(32-11) + a
+
+ c += (d ^ a ^ b) + X[(5+3*2)&15] + 1839030562
+ c = c<<16 | c>>(32-16) + d
+
+ b += (c ^ d ^ a) + X[(5+3*3)&15] + 4259657740
+ b = b<<23 | b>>(32-23) + c
+
+ a += (b ^ c ^ d) + X[(5+3*4)&15] + 2763975236
+ a = a<<4 | a>>(32-4) + b
+
+ d += (a ^ b ^ c) + X[(5+3*5)&15] + 1272893353
+ d = d<<11 | d>>(32-11) + a
+
+ c += (d ^ a ^ b) + X[(5+3*6)&15] + 4139469664
+ c = c<<16 | c>>(32-16) + d
+
+ b += (c ^ d ^ a) + X[(5+3*7)&15] + 3200236656
+ b = b<<23 | b>>(32-23) + c
+
+ a += (b ^ c ^ d) + X[(5+3*8)&15] + 681279174
+ a = a<<4 | a>>(32-4) + b
+
+ d += (a ^ b ^ c) + X[(5+3*9)&15] + 3936430074
+ d = d<<11 | d>>(32-11) + a
+
+ c += (d ^ a ^ b) + X[(5+3*10)&15] + 3572445317
+ c = c<<16 | c>>(32-16) + d
+
+ b += (c ^ d ^ a) + X[(5+3*11)&15] + 76029189
+ b = b<<23 | b>>(32-23) + c
+
+ a += (b ^ c ^ d) + X[(5+3*12)&15] + 3654602809
+ a = a<<4 | a>>(32-4) + b
+
+ d += (a ^ b ^ c) + X[(5+3*13)&15] + 3873151461
+ d = d<<11 | d>>(32-11) + a
+
+ c += (d ^ a ^ b) + X[(5+3*14)&15] + 530742520
+ c = c<<16 | c>>(32-16) + d
+
+ b += (c ^ d ^ a) + X[(5+3*15)&15] + 3299628645
+ b = b<<23 | b>>(32-23) + c
// Round 4.
- for i := uint(0); i < 16; i++ {
- x := (7 * i) % 16
- s := shift4[i%4]
- j := c ^ (b | ^d)
- a += j + X[x] + table[i+48]
- a = a<<s | a>>(32-s) + b
- a, b, c, d = d, a, b, c
- }
+
+ a += (c ^ (b | ^d)) + X[(7*0)&15] + 4096336452
+ a = a<<6 | a>>(32-6) + b
+
+ d += (b ^ (a | ^c)) + X[(7*1)&15] + 1126891415
+ d = d<<10 | d>>(32-10) + a
+
+ c += (a ^ (d | ^b)) + X[(7*2)&15] + 2878612391
+ c = c<<15 | c>>(32-15) + d
+
+ b += (d ^ (c | ^a)) + X[(7*3)&15] + 4237533241
+ b = b<<21 | b>>(32-21) + c
+
+ a += (c ^ (b | ^d)) + X[(7*4)&15] + 1700485571
+ a = a<<6 | a>>(32-6) + b
+
+ d += (b ^ (a | ^c)) + X[(7*5)&15] + 2399980690
+ d = d<<10 | d>>(32-10) + a
+
+ c += (a ^ (d | ^b)) + X[(7*6)&15] + 4293915773
+ c = c<<15 | c>>(32-15) + d
+
+ b += (d ^ (c | ^a)) + X[(7*7)&15] + 2240044497
+ b = b<<21 | b>>(32-21) + c
+
+ a += (c ^ (b | ^d)) + X[(7*8)&15] + 1873313359
+ a = a<<6 | a>>(32-6) + b
+
+ d += (b ^ (a | ^c)) + X[(7*9)&15] + 4264355552
+ d = d<<10 | d>>(32-10) + a
+
+ c += (a ^ (d | ^b)) + X[(7*10)&15] + 2734768916
+ c = c<<15 | c>>(32-15) + d
+
+ b += (d ^ (c | ^a)) + X[(7*11)&15] + 1309151649
+ b = b<<21 | b>>(32-21) + c
+
+ a += (c ^ (b | ^d)) + X[(7*12)&15] + 4149444226
+ a = a<<6 | a>>(32-6) + b
+
+ d += (b ^ (a | ^c)) + X[(7*13)&15] + 3174756917
+ d = d<<10 | d>>(32-10) + a
+
+ c += (a ^ (d | ^b)) + X[(7*14)&15] + 718787259
+ c = c<<15 | c>>(32-15) + d
+
+ b += (d ^ (c | ^a)) + X[(7*15)&15] + 3951481745
+ b = b<<21 | b>>(32-21) + c
a += aa
b += bb
c += cc
d += dd
- p = p[_Chunk:]
- n += _Chunk
+ p = p[chunk:]
}
dig.s[0] = a
dig.s[1] = b
dig.s[2] = c
dig.s[3] = d
- return n
}
diff --git a/src/pkg/crypto/rand/example_test.go b/src/pkg/crypto/rand/example_test.go
new file mode 100644
index 000000000..5af8e46f5
--- /dev/null
+++ b/src/pkg/crypto/rand/example_test.go
@@ -0,0 +1,29 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package rand_test
+
+import (
+ "bytes"
+ "crypto/rand"
+ "fmt"
+ "io"
+)
+
+// This example reads 10 cryptographically secure pseudorandom numbers from
+// rand.Reader and writes them to a byte slice.
+func ExampleRead() {
+ c := 10
+ b := make([]byte, c)
+ n, err := io.ReadFull(rand.Reader, b)
+ if n != len(b) || err != nil {
+ fmt.Println("error:", err)
+ return
+ }
+ // The slice should now contain random bytes instead of only zeroes.
+ fmt.Println(bytes.Equal(b, make([]byte, c)))
+
+ // Output:
+ // false
+}
diff --git a/src/pkg/crypto/rand/rand_test.go b/src/pkg/crypto/rand/rand_test.go
index be3a5a221..e46e61d37 100644
--- a/src/pkg/crypto/rand/rand_test.go
+++ b/src/pkg/crypto/rand/rand_test.go
@@ -7,6 +7,7 @@ package rand
import (
"bytes"
"compress/flate"
+ "io"
"testing"
)
@@ -16,9 +17,9 @@ func TestRead(t *testing.T) {
n = 1e5
}
b := make([]byte, n)
- n, err := Read(b)
+ n, err := io.ReadFull(Reader, b)
if n != len(b) || err != nil {
- t.Fatalf("Read(buf) = %d, %s", n, err)
+ t.Fatalf("ReadFull(buf) = %d, %s", n, err)
}
var z bytes.Buffer
@@ -29,3 +30,14 @@ func TestRead(t *testing.T) {
t.Fatalf("Compressed %d -> %d", len(b), z.Len())
}
}
+
+func TestReadEmpty(t *testing.T) {
+ n, err := Reader.Read(make([]byte, 0))
+ if n != 0 || err != nil {
+ t.Fatalf("Read(make([]byte, 0)) = %d, %v", n, err)
+ }
+ n, err = Reader.Read(nil)
+ if n != 0 || err != nil {
+ t.Fatalf("Read(nil) = %d, %v", n, err)
+ }
+}
diff --git a/src/pkg/crypto/rand/rand_unix.go b/src/pkg/crypto/rand/rand_unix.go
index 5eb4cda2b..18f482472 100644
--- a/src/pkg/crypto/rand/rand_unix.go
+++ b/src/pkg/crypto/rand/rand_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd
+// +build darwin freebsd linux netbsd openbsd plan9
// Unix cryptographically secure pseudorandom number
// generator.
@@ -15,6 +15,7 @@ import (
"crypto/cipher"
"io"
"os"
+ "runtime"
"sync"
"time"
)
@@ -22,7 +23,13 @@ import (
// Easy implementation: read from /dev/urandom.
// This is sufficient on Linux, OS X, and FreeBSD.
-func init() { Reader = &devReader{name: "/dev/urandom"} }
+func init() {
+ if runtime.GOOS == "plan9" {
+ Reader = newReader(nil)
+ } else {
+ Reader = &devReader{name: "/dev/urandom"}
+ }
+}
// A devReader satisfies reads by reading the file named name.
type devReader struct {
@@ -39,14 +46,17 @@ func (r *devReader) Read(b []byte) (n int, err error) {
if f == nil {
return 0, err
}
- r.f = bufio.NewReader(f)
+ if runtime.GOOS == "plan9" {
+ r.f = f
+ } else {
+ r.f = bufio.NewReader(f)
+ }
}
return r.f.Read(b)
}
// Alternate pseudo-random implementation for use on
-// systems without a reliable /dev/urandom. So far we
-// haven't needed it.
+// systems without a reliable /dev/urandom.
// newReader returns a new pseudorandom generator that
// seeds itself by reading from entropy. If entropy == nil,
diff --git a/src/pkg/crypto/rand/rand_windows.go b/src/pkg/crypto/rand/rand_windows.go
index 2b2bd4bba..82b39b64a 100644
--- a/src/pkg/crypto/rand/rand_windows.go
+++ b/src/pkg/crypto/rand/rand_windows.go
@@ -35,6 +35,10 @@ func (r *rngReader) Read(b []byte) (n int, err error) {
}
}
r.mu.Unlock()
+
+ if len(b) == 0 {
+ return 0, nil
+ }
err = syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0])
if err != nil {
return 0, os.NewSyscallError("CryptGenRandom", err)
diff --git a/src/pkg/crypto/rand/util.go b/src/pkg/crypto/rand/util.go
index 5391c1829..50e5b162b 100644
--- a/src/pkg/crypto/rand/util.go
+++ b/src/pkg/crypto/rand/util.go
@@ -10,6 +10,21 @@ import (
"math/big"
)
+// smallPrimes is a list of small, prime numbers that allows us to rapidly
+// exclude some fraction of composite candidates when searching for a random
+// prime. This list is truncated at the point where smallPrimesProduct exceeds
+// a uint64. It does not include two because we ensure that the candidates are
+// odd by construction.
+var smallPrimes = []uint8{
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53,
+}
+
+// smallPrimesProduct is the product of the values in smallPrimes and allows us
+// to reduce a candidate prime by this number and then determine whether it's
+// coprime to all the elements of smallPrimes without further big.Int
+// operations.
+var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365)
+
// Prime returns a number, p, of the given size, such that p is prime
// with high probability.
func Prime(rand io.Reader, bits int) (p *big.Int, err error) {
@@ -25,6 +40,8 @@ func Prime(rand io.Reader, bits int) (p *big.Int, err error) {
bytes := make([]byte, (bits+7)/8)
p = new(big.Int)
+ bigMod := new(big.Int)
+
for {
_, err = io.ReadFull(rand, bytes)
if err != nil {
@@ -33,13 +50,51 @@ func Prime(rand io.Reader, bits int) (p *big.Int, err error) {
// Clear bits in the first byte to make sure the candidate has a size <= bits.
bytes[0] &= uint8(int(1<<b) - 1)
- // Don't let the value be too small, i.e, set the most significant bit.
- bytes[0] |= 1 << (b - 1)
+ // Don't let the value be too small, i.e, set the most significant two bits.
+ // Setting the top two bits, rather than just the top bit,
+ // means that when two of these values are multiplied together,
+ // the result isn't ever one bit short.
+ if b >= 2 {
+ bytes[0] |= 3 << (b - 2)
+ } else {
+ // Here b==1, because b cannot be zero.
+ bytes[0] |= 1
+ if len(bytes) > 1 {
+ bytes[1] |= 0x80
+ }
+ }
// Make the value odd since an even number this large certainly isn't prime.
bytes[len(bytes)-1] |= 1
p.SetBytes(bytes)
- if p.ProbablyPrime(20) {
+
+ // Calculate the value mod the product of smallPrimes. If it's
+ // a multiple of any of these primes we add two until it isn't.
+ // The probability of overflowing is minimal and can be ignored
+ // because we still perform Miller-Rabin tests on the result.
+ bigMod.Mod(p, smallPrimesProduct)
+ mod := bigMod.Uint64()
+
+ NextDelta:
+ for delta := uint64(0); delta < 1<<20; delta += 2 {
+ m := mod + delta
+ for _, prime := range smallPrimes {
+ if m%uint64(prime) == 0 {
+ continue NextDelta
+ }
+ }
+
+ if delta > 0 {
+ bigMod.SetUint64(delta)
+ p.Add(p, bigMod)
+ }
+ break
+ }
+
+ // There is a tiny possibility that, by adding delta, we caused
+ // the number to be one bit too long. Thus we check BitLen
+ // here.
+ if p.ProbablyPrime(20) && p.BitLen() == bits {
return
}
}
diff --git a/src/pkg/crypto/rc4/rc4.go b/src/pkg/crypto/rc4/rc4.go
index 1bb278f74..e0c33fa4b 100644
--- a/src/pkg/crypto/rc4/rc4.go
+++ b/src/pkg/crypto/rc4/rc4.go
@@ -42,17 +42,6 @@ func NewCipher(key []byte) (*Cipher, error) {
return &c, nil
}
-// XORKeyStream sets dst to the result of XORing src with the key stream.
-// Dst and src may be the same slice but otherwise should not overlap.
-func (c *Cipher) XORKeyStream(dst, src []byte) {
- for i := range src {
- c.i += 1
- c.j += c.s[c.i]
- c.s[c.i], c.s[c.j] = c.s[c.j], c.s[c.i]
- dst[i] = src[i] ^ c.s[c.s[c.i]+c.s[c.j]]
- }
-}
-
// Reset zeros the key data so that it will no longer appear in the
// process's memory.
func (c *Cipher) Reset() {
diff --git a/src/pkg/crypto/rc4/rc4_amd64.s b/src/pkg/crypto/rc4/rc4_amd64.s
new file mode 100644
index 000000000..ffe9ada85
--- /dev/null
+++ b/src/pkg/crypto/rc4/rc4_amd64.s
@@ -0,0 +1,53 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// func xorKeyStream(dst, src *byte, n int, state *[256]byte, i, j *uint8)
+TEXT ·xorKeyStream(SB),7,$0
+ MOVQ dst+0(FP), DI
+ MOVQ src+8(FP), SI
+ MOVQ n+16(FP), CX
+ MOVQ state+24(FP), R8
+
+ MOVQ xPtr+32(FP), AX
+ MOVBQZX (AX), AX
+ MOVQ yPtr+40(FP), BX
+ MOVBQZX (BX), BX
+
+loop:
+ CMPQ CX, $0
+ JE done
+
+ // c.i += 1
+ INCB AX
+
+ // c.j += c.s[c.i]
+ MOVB (R8)(AX*1), R9
+ ADDB R9, BX
+
+ MOVBQZX (R8)(BX*1), R10
+
+ MOVB R10, (R8)(AX*1)
+ MOVB R9, (R8)(BX*1)
+
+ // R11 = c.s[c.i]+c.s[c.j]
+ MOVQ R10, R11
+ ADDB R9, R11
+
+ MOVB (R8)(R11*1), R11
+ MOVB (SI), R12
+ XORB R11, R12
+ MOVB R12, (DI)
+
+ INCQ SI
+ INCQ DI
+ DECQ CX
+
+ JMP loop
+done:
+ MOVQ xPtr+32(FP), R8
+ MOVB AX, (R8)
+ MOVQ yPtr+40(FP), R8
+ MOVB BX, (R8)
+
+ RET
diff --git a/src/pkg/crypto/rc4/rc4_arm.s b/src/pkg/crypto/rc4/rc4_arm.s
new file mode 100644
index 000000000..51a332f62
--- /dev/null
+++ b/src/pkg/crypto/rc4/rc4_arm.s
@@ -0,0 +1,58 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Registers
+dst = 0
+src = 1
+n = 2
+state = 3
+pi = 4
+pj = 5
+i = 6
+j = 7
+k = 8
+t = 11
+t2 = 12
+
+// func xorKeyStream(dst, src *byte, n int, state *[256]byte, i, j *uint8)
+TEXT ·xorKeyStream(SB),7,$0
+ MOVW 0(FP), R(dst)
+ MOVW 4(FP), R(src)
+ MOVW 8(FP), R(n)
+ MOVW 12(FP), R(state)
+ MOVW 16(FP), R(pi)
+ MOVW 20(FP), R(pj)
+ MOVBU (R(pi)), R(i)
+ MOVBU (R(pj)), R(j)
+ MOVW $0, R(k)
+
+loop:
+ // i += 1; j += state[i]
+ ADD $1, R(i)
+ AND $0xff, R(i)
+ MOVBU R(i)<<0(R(state)), R(t)
+ ADD R(t), R(j)
+ AND $0xff, R(j)
+
+ // swap state[i] <-> state[j]
+ MOVBU R(j)<<0(R(state)), R(t2)
+ MOVB R(t2), R(i)<<0(R(state))
+ MOVB R(t), R(j)<<0(R(state))
+
+ // dst[k] = src[k] ^ state[state[i] + state[j]]
+ ADD R(t2), R(t)
+ AND $0xff, R(t)
+ MOVBU R(t)<<0(R(state)), R(t)
+ MOVBU R(k)<<0(R(src)), R(t2)
+ EOR R(t), R(t2)
+ MOVB R(t2), R(k)<<0(R(dst))
+
+ ADD $1, R(k)
+ CMP R(k), R(n)
+ BNE loop
+
+done:
+ MOVB R(i), (R(pi))
+ MOVB R(j), (R(pj))
+ RET
diff --git a/src/pkg/crypto/rc4/rc4_asm.go b/src/pkg/crypto/rc4/rc4_asm.go
new file mode 100644
index 000000000..0b66e4a9e
--- /dev/null
+++ b/src/pkg/crypto/rc4/rc4_asm.go
@@ -0,0 +1,18 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64 arm
+
+package rc4
+
+func xorKeyStream(dst, src *byte, n int, state *[256]byte, i, j *uint8)
+
+// XORKeyStream sets dst to the result of XORing src with the key stream.
+// Dst and src may be the same slice but otherwise should not overlap.
+func (c *Cipher) XORKeyStream(dst, src []byte) {
+ if len(src) == 0 {
+ return
+ }
+ xorKeyStream(&dst[0], &src[0], len(src), &c.s, &c.i, &c.j)
+}
diff --git a/src/pkg/crypto/rc4/rc4_ref.go b/src/pkg/crypto/rc4/rc4_ref.go
new file mode 100644
index 000000000..1018548c2
--- /dev/null
+++ b/src/pkg/crypto/rc4/rc4_ref.go
@@ -0,0 +1,20 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64,!arm
+
+package rc4
+
+// XORKeyStream sets dst to the result of XORing src with the key stream.
+// Dst and src may be the same slice but otherwise should not overlap.
+func (c *Cipher) XORKeyStream(dst, src []byte) {
+ i, j := c.i, c.j
+ for k, v := range src {
+ i += 1
+ j += c.s[i]
+ c.s[i], c.s[j] = c.s[j], c.s[i]
+ dst[k] = v ^ c.s[c.s[i]+c.s[j]]
+ }
+ c.i, c.j = i, j
+}
diff --git a/src/pkg/crypto/rc4/rc4_test.go b/src/pkg/crypto/rc4/rc4_test.go
index 6265d9408..9e12789f7 100644
--- a/src/pkg/crypto/rc4/rc4_test.go
+++ b/src/pkg/crypto/rc4/rc4_test.go
@@ -37,6 +37,39 @@ var golden = []rc4Test{
[]byte{0x57, 0x69, 0x6b, 0x69},
[]byte{0x60, 0x44, 0xdb, 0x6d, 0x41, 0xb7},
},
+ {
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{
+ 0xde, 0x18, 0x89, 0x41, 0xa3, 0x37, 0x5d, 0x3a,
+ 0x8a, 0x06, 0x1e, 0x67, 0x57, 0x6e, 0x92, 0x6d,
+ 0xc7, 0x1a, 0x7f, 0xa3, 0xf0, 0xcc, 0xeb, 0x97,
+ 0x45, 0x2b, 0x4d, 0x32, 0x27, 0x96, 0x5f, 0x9e,
+ 0xa8, 0xcc, 0x75, 0x07, 0x6d, 0x9f, 0xb9, 0xc5,
+ 0x41, 0x7a, 0xa5, 0xcb, 0x30, 0xfc, 0x22, 0x19,
+ 0x8b, 0x34, 0x98, 0x2d, 0xbb, 0x62, 0x9e, 0xc0,
+ 0x4b, 0x4f, 0x8b, 0x05, 0xa0, 0x71, 0x08, 0x50,
+ 0x92, 0xa0, 0xc3, 0x58, 0x4a, 0x48, 0xe4, 0xa3,
+ 0x0a, 0x39, 0x7b, 0x8a, 0xcd, 0x1d, 0x00, 0x9e,
+ 0xc8, 0x7d, 0x68, 0x11, 0xf2, 0x2c, 0xf4, 0x9c,
+ 0xa3, 0xe5, 0x93, 0x54, 0xb9, 0x45, 0x15, 0x35,
+ 0xa2, 0x18, 0x7a, 0x86, 0x42, 0x6c, 0xca, 0x7d,
+ 0x5e, 0x82, 0x3e, 0xba, 0x00, 0x44, 0x12, 0x67,
+ 0x12, 0x57, 0xb8, 0xd8, 0x60, 0xae, 0x4c, 0xbd,
+ 0x4c, 0x49, 0x06, 0xbb, 0xc5, 0x35, 0xef, 0xe1,
+ 0x58, 0x7f, 0x08, 0xdb, 0x33, 0x95, 0x5c, 0xdb,
+ 0xcb, 0xad, 0x9b, 0x10, 0xf5, 0x3f, 0xc4, 0xe5,
+ 0x2c, 0x59, 0x15, 0x65, 0x51, 0x84, 0x87, 0xfe,
+ 0x08, 0x4d, 0x0e, 0x3f, 0x03, 0xde, 0xbc, 0xc9,
+ 0xda, 0x1c, 0xe9, 0x0d, 0x08, 0x5c, 0x2d, 0x8a,
+ 0x19, 0xd8, 0x37, 0x30, 0x86, 0x16, 0x36, 0x92,
+ 0x14, 0x2b, 0xd8, 0xfc, 0x5d, 0x7a, 0x73, 0x49,
+ 0x6a, 0x8e, 0x59, 0xee, 0x7e, 0xcf, 0x6b, 0x94,
+ 0x06, 0x63, 0xf4, 0xa6, 0xbe, 0xe6, 0x5b, 0xd2,
+ 0xc8, 0x5c, 0x46, 0x98, 0x6c, 0x1b, 0xef, 0x34,
+ 0x90, 0xd3, 0x7b, 0x38, 0xda, 0x85, 0xd3, 0x2e,
+ 0x97, 0x39, 0xcb, 0x23, 0x4a, 0x2b, 0xe7, 0x40,
+ },
+ },
}
func TestGolden(t *testing.T) {
@@ -51,9 +84,34 @@ func TestGolden(t *testing.T) {
c.XORKeyStream(keystream, keystream)
for j, v := range keystream {
if g.keystream[j] != v {
- t.Errorf("Failed at golden index %d", i)
+ t.Errorf("Failed at golden index %d:\n%x\nvs\n%x", i, keystream, g.keystream)
break
}
}
}
}
+
+func benchmark(b *testing.B, size int64) {
+ buf := make([]byte, size)
+ c, err := NewCipher(golden[0].key)
+ if err != nil {
+ panic(err)
+ }
+ b.SetBytes(size)
+
+ for i := 0; i < b.N; i++ {
+ c.XORKeyStream(buf, buf)
+ }
+}
+
+func BenchmarkRC4_128(b *testing.B) {
+ benchmark(b, 128)
+}
+
+func BenchmarkRC4_1K(b *testing.B) {
+ benchmark(b, 1024)
+}
+
+func BenchmarkRC4_8K(b *testing.B) {
+ benchmark(b, 8096)
+}
diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go
index a32236e47..28ca5d73b 100644
--- a/src/pkg/crypto/rsa/pkcs1v15.go
+++ b/src/pkg/crypto/rsa/pkcs1v15.go
@@ -19,16 +19,19 @@ import (
// WARNING: use of this function to encrypt plaintexts other than session keys
// is dangerous. Use RSA OAEP in new protocols.
func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, err error) {
+ if err := checkPub(pub); err != nil {
+ return nil, err
+ }
k := (pub.N.BitLen() + 7) / 8
if len(msg) > k-11 {
err = ErrMessageTooLong
return
}
- // EM = 0x02 || PS || 0x00 || M
- em := make([]byte, k-1)
- em[0] = 2
- ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):]
+ // EM = 0x00 || 0x02 || PS || 0x00 || M
+ em := make([]byte, k)
+ em[1] = 2
+ ps, mm := em[2:len(em)-len(msg)-1], em[len(em)-len(msg):]
err = nonZeroRandomBytes(ps, rand)
if err != nil {
return
@@ -38,13 +41,18 @@ func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, er
m := new(big.Int).SetBytes(em)
c := encrypt(new(big.Int), pub, m)
- out = c.Bytes()
+
+ copyWithLeftPad(em, c.Bytes())
+ out = em
return
}
// DecryptPKCS1v15 decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5.
// If rand != nil, it uses RSA blinding to avoid timing side-channel attacks.
func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out []byte, err error) {
+ if err := checkPub(&priv.PublicKey); err != nil {
+ return nil, err
+ }
valid, out, err := decryptPKCS1v15(rand, priv, ciphertext)
if err == nil && valid == 0 {
err = ErrDecryption
@@ -67,6 +75,9 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out [
// Encryption Standard PKCS #1'', Daniel Bleichenbacher, Advances in Cryptology
// (Crypto '98).
func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) (err error) {
+ if err := checkPub(&priv.PublicKey); err != nil {
+ return err
+ }
k := (priv.N.BitLen() + 7) / 8
if k-(len(key)+3+8) < 0 {
err = ErrDecryption
@@ -185,9 +196,12 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b
m := new(big.Int).SetBytes(em)
c, err := decrypt(rand, priv, m)
- if err == nil {
- s = c.Bytes()
+ if err != nil {
+ return
}
+
+ copyWithLeftPad(em, c.Bytes())
+ s = em
return
}
@@ -233,11 +247,21 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte)
func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err error) {
hashLen = hash.Size()
if inLen != hashLen {
- return 0, nil, errors.New("input must be hashed message")
+ return 0, nil, errors.New("crypto/rsa: input must be hashed message")
}
prefix, ok := hashPrefixes[hash]
if !ok {
- return 0, nil, errors.New("unsupported hash function")
+ return 0, nil, errors.New("crypto/rsa: unsupported hash function")
}
return
}
+
+// copyWithLeftPad copies src to the end of dest, padding with zero bytes as
+// needed.
+func copyWithLeftPad(dest, src []byte) {
+ numPaddingBytes := len(dest) - len(src)
+ for i := 0; i < numPaddingBytes; i++ {
+ dest[i] = 0
+ }
+ copy(dest[numPaddingBytes:], src)
+}
diff --git a/src/pkg/crypto/rsa/pkcs1v15_test.go b/src/pkg/crypto/rsa/pkcs1v15_test.go
index 58d5fda19..bf9219bae 100644
--- a/src/pkg/crypto/rsa/pkcs1v15_test.go
+++ b/src/pkg/crypto/rsa/pkcs1v15_test.go
@@ -57,7 +57,7 @@ func TestDecryptPKCS1v15(t *testing.T) {
t.Errorf("#%d error decrypting", i)
}
want := []byte(test.out)
- if bytes.Compare(out, want) != 0 {
+ if !bytes.Equal(out, want) {
t.Errorf("#%d got:%#v want:%#v", i, out, want)
}
}
@@ -90,7 +90,7 @@ func TestEncryptPKCS1v15(t *testing.T) {
return false
}
- if bytes.Compare(plaintext, in) != 0 {
+ if !bytes.Equal(plaintext, in) {
t.Errorf("output mismatch: %#v %#v", plaintext, in)
return false
}
@@ -132,7 +132,7 @@ func TestEncryptPKCS1v15SessionKey(t *testing.T) {
t.Errorf("#%d error decrypting", i)
}
want := []byte(test.out)
- if bytes.Compare(key, want) != 0 {
+ if !bytes.Equal(key, want) {
t.Errorf("#%d got:%#v want:%#v", i, key, want)
}
}
@@ -176,7 +176,7 @@ func TestSignPKCS1v15(t *testing.T) {
}
expected, _ := hex.DecodeString(test.out)
- if bytes.Compare(s, expected) != 0 {
+ if !bytes.Equal(s, expected) {
t.Errorf("#%d got: %x want: %x", i, s, expected)
}
}
diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go
index ec77e6869..35a5f7c3c 100644
--- a/src/pkg/crypto/rsa/rsa.go
+++ b/src/pkg/crypto/rsa/rsa.go
@@ -25,6 +25,30 @@ type PublicKey struct {
E int // public exponent
}
+var (
+ errPublicModulus = errors.New("crypto/rsa: missing public modulus")
+ errPublicExponentSmall = errors.New("crypto/rsa: public exponent too small")
+ errPublicExponentLarge = errors.New("crypto/rsa: public exponent too large")
+)
+
+// checkPub sanity checks the public key before we use it.
+// We require pub.E to fit into a 32-bit integer so that we
+// do not have different behavior depending on whether
+// int is 32 or 64 bits. See also
+// http://www.imperialviolet.org/2012/03/16/rsae.html.
+func checkPub(pub *PublicKey) error {
+ if pub.N == nil {
+ return errPublicModulus
+ }
+ if pub.E < 2 {
+ return errPublicExponentSmall
+ }
+ if pub.E > 1<<31-1 {
+ return errPublicExponentLarge
+ }
+ return nil
+}
+
// A PrivateKey represents an RSA key
type PrivateKey struct {
PublicKey // public part.
@@ -37,7 +61,7 @@ type PrivateKey struct {
}
type PrecomputedValues struct {
- Dp, Dq *big.Int // D mod (P-1) (or mod Q-1)
+ Dp, Dq *big.Int // D mod (P-1) (or mod Q-1)
Qinv *big.Int // Q^-1 mod Q
// CRTValues is used for the 3rd and subsequent primes. Due to a
@@ -57,13 +81,17 @@ type CRTValue struct {
// Validate performs basic sanity checks on the key.
// It returns nil if the key is valid, or else an error describing a problem.
func (priv *PrivateKey) Validate() error {
+ if err := checkPub(&priv.PublicKey); err != nil {
+ return err
+ }
+
// Check that the prime factors are actually prime. Note that this is
// just a sanity check. Since the random witnesses chosen by
// ProbablyPrime are deterministic, given the candidate number, it's
// easy for an attack to generate composites that pass this test.
for _, prime := range priv.Primes {
if !prime.ProbablyPrime(20) {
- return errors.New("prime factor is composite")
+ return errors.New("crypto/rsa: prime factor is composite")
}
}
@@ -73,27 +101,23 @@ func (priv *PrivateKey) Validate() error {
modulus.Mul(modulus, prime)
}
if modulus.Cmp(priv.N) != 0 {
- return errors.New("invalid modulus")
+ return errors.New("crypto/rsa: invalid modulus")
}
- // Check that e and totient(Πprimes) are coprime.
- totient := new(big.Int).Set(bigOne)
+
+ // Check that de ≡ 1 mod p-1, for each prime.
+ // This implies that e is coprime to each p-1 as e has a multiplicative
+ // inverse. Therefore e is coprime to lcm(p-1,q-1,r-1,...) =
+ // exponent(ℤ/nℤ). It also implies that a^de ≡ a mod p as a^(p-1) ≡ 1
+ // mod p. Thus a^de ≡ a mod n for all a coprime to n, as required.
+ congruence := new(big.Int)
+ de := new(big.Int).SetInt64(int64(priv.E))
+ de.Mul(de, priv.D)
for _, prime := range priv.Primes {
pminus1 := new(big.Int).Sub(prime, bigOne)
- totient.Mul(totient, pminus1)
- }
- e := big.NewInt(int64(priv.E))
- gcd := new(big.Int)
- x := new(big.Int)
- y := new(big.Int)
- gcd.GCD(x, y, totient, e)
- if gcd.Cmp(bigOne) != 0 {
- return errors.New("invalid public exponent E")
- }
- // Check that de ≡ 1 (mod totient(Πprimes))
- de := new(big.Int).Mul(priv.D, e)
- de.Mod(de, totient)
- if de.Cmp(bigOne) != 0 {
- return errors.New("invalid private exponent D")
+ congruence.Mod(de, pminus1)
+ if congruence.Cmp(bigOne) != 0 {
+ return errors.New("crypto/rsa: invalid exponents")
+ }
}
return nil
}
@@ -118,7 +142,7 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *Priva
priv.E = 65537
if nprimes < 2 {
- return nil, errors.New("rsa.GenerateMultiPrimeKey: nprimes must be >= 2")
+ return nil, errors.New("crypto/rsa: GenerateMultiPrimeKey: nprimes must be >= 2")
}
primes := make([]*big.Int, nprimes)
@@ -126,6 +150,20 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *Priva
NextSetOfPrimes:
for {
todo := bits
+ // crypto/rand should set the top two bits in each prime.
+ // Thus each prime has the form
+ // p_i = 2^bitlen(p_i) × 0.11... (in base 2).
+ // And the product is:
+ // P = 2^todo × α
+ // where α is the product of nprimes numbers of the form 0.11...
+ //
+ // If α < 1/2 (which can happen for nprimes > 2), we need to
+ // shift todo to compensate for lost bits: the mean value of 0.11...
+ // is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2
+ // will give good results.
+ if nprimes >= 7 {
+ todo += (nprimes - 2) / 5
+ }
for i := 0; i < nprimes; i++ {
primes[i], err = rand.Prime(random, todo/(nprimes-i))
if err != nil {
@@ -151,6 +189,12 @@ NextSetOfPrimes:
pminus1.Sub(prime, bigOne)
totient.Mul(totient, pminus1)
}
+ if n.BitLen() != bits {
+ // This should never happen for nprimes == 2 because
+ // crypto/rand should set the top two bits in each prime.
+ // For nprimes > 2 we hope it does not happen often.
+ continue NextSetOfPrimes
+ }
g := new(big.Int)
priv.D = new(big.Int)
@@ -220,6 +264,9 @@ func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int {
// The message must be no longer than the length of the public modulus less
// twice the hash length plus 2.
func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) (out []byte, err error) {
+ if err := checkPub(pub); err != nil {
+ return nil, err
+ }
hash.Reset()
k := (pub.N.BitLen() + 7) / 8
if len(msg) > k-2*hash.Size()-2 {
@@ -406,6 +453,9 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err er
// DecryptOAEP decrypts ciphertext using RSA-OAEP.
// If random != nil, DecryptOAEP uses RSA blinding to avoid timing side-channel attacks.
func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) (msg []byte, err error) {
+ if err := checkPub(&priv.PublicKey); err != nil {
+ return nil, err
+ }
k := (priv.N.BitLen() + 7) / 8
if len(ciphertext) > k ||
k < hash.Size()*2+2 {
diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go
index 0fb9875d0..f08cfe73c 100644
--- a/src/pkg/crypto/rsa/rsa_test.go
+++ b/src/pkg/crypto/rsa/rsa_test.go
@@ -21,15 +21,18 @@ func TestKeyGeneration(t *testing.T) {
if err != nil {
t.Errorf("failed to generate key")
}
+ if bits := priv.N.BitLen(); bits != size {
+ t.Errorf("key too short (%d vs %d)", bits, size)
+ }
testKeyBasics(t, priv)
}
func Test3PrimeKeyGeneration(t *testing.T) {
+ size := 768
if testing.Short() {
- return
+ size = 256
}
- size := 768
priv, err := GenerateMultiPrimeKey(rand.Reader, 3, size)
if err != nil {
t.Errorf("failed to generate key")
@@ -38,11 +41,11 @@ func Test3PrimeKeyGeneration(t *testing.T) {
}
func Test4PrimeKeyGeneration(t *testing.T) {
+ size := 768
if testing.Short() {
- return
+ size = 256
}
- size := 768
priv, err := GenerateMultiPrimeKey(rand.Reader, 4, size)
if err != nil {
t.Errorf("failed to generate key")
@@ -50,6 +53,42 @@ func Test4PrimeKeyGeneration(t *testing.T) {
testKeyBasics(t, priv)
}
+func TestNPrimeKeyGeneration(t *testing.T) {
+ primeSize := 64
+ maxN := 24
+ if testing.Short() {
+ primeSize = 16
+ maxN = 16
+ }
+ // Test that generation of N-prime keys works for N > 4.
+ for n := 5; n < maxN; n++ {
+ priv, err := GenerateMultiPrimeKey(rand.Reader, n, 64+n*primeSize)
+ if err == nil {
+ testKeyBasics(t, priv)
+ } else {
+ t.Errorf("failed to generate %d-prime key", n)
+ }
+ }
+}
+
+func TestGnuTLSKey(t *testing.T) {
+ // This is a key generated by `certtool --generate-privkey --bits 128`.
+ // It's such that de ≢ 1 mod φ(n), but is congruent mod the order of
+ // the group.
+ priv := &PrivateKey{
+ PublicKey: PublicKey{
+ N: fromBase10("290684273230919398108010081414538931343"),
+ E: 65537,
+ },
+ D: fromBase10("31877380284581499213530787347443987241"),
+ Primes: []*big.Int{
+ fromBase10("16775196964030542637"),
+ fromBase10("17328218193455850539"),
+ },
+ }
+ testKeyBasics(t, priv)
+}
+
func testKeyBasics(t *testing.T, priv *PrivateKey) {
if err := priv.Validate(); err != nil {
t.Errorf("Validate() failed: %s", err)
@@ -98,7 +137,7 @@ func BenchmarkRSA2048Decrypt(b *testing.B) {
}
priv.Precompute()
- c := fromBase10("1000")
+ c := fromBase10("8472002792838218989464636159316973636630013835787202418124758118372358261975764365740026024610403138425986214991379012696600761514742817632790916315594342398720903716529235119816755589383377471752116975374952783629225022962092351886861518911824745188989071172097120352727368980275252089141512321893536744324822590480751098257559766328893767334861211872318961900897793874075248286439689249972315699410830094164386544311554704755110361048571142336148077772023880664786019636334369759624917224888206329520528064315309519262325023881707530002540634660750469137117568199824615333883758410040459705787022909848740188613313")
b.StartTimer()
@@ -123,7 +162,7 @@ func Benchmark3PrimeRSA2048Decrypt(b *testing.B) {
}
priv.Precompute()
- c := fromBase10("1000")
+ c := fromBase10("8472002792838218989464636159316973636630013835787202418124758118372358261975764365740026024610403138425986214991379012696600761514742817632790916315594342398720903716529235119816755589383377471752116975374952783629225022962092351886861518911824745188989071172097120352727368980275252089141512321893536744324822590480751098257559766328893767334861211872318961900897793874075248286439689249972315699410830094164386544311554704755110361048571142336148077772023880664786019636334369759624917224888206329520528064315309519262325023881707530002540634660750469137117568199824615333883758410040459705787022909848740188613313")
b.StartTimer()
@@ -158,7 +197,7 @@ func TestEncryptOAEP(t *testing.T) {
if err != nil {
t.Errorf("#%d,%d error: %s", i, j, err)
}
- if bytes.Compare(out, message.out) != 0 {
+ if !bytes.Equal(out, message.out) {
t.Errorf("#%d,%d bad result: %x (want %x)", i, j, out, message.out)
}
}
@@ -182,7 +221,7 @@ func TestDecryptOAEP(t *testing.T) {
out, err := DecryptOAEP(sha1, nil, private, message.out, nil)
if err != nil {
t.Errorf("#%d,%d error: %s", i, j, err)
- } else if bytes.Compare(out, message.in) != 0 {
+ } else if !bytes.Equal(out, message.in) {
t.Errorf("#%d,%d bad result: %#v (want %#v)", i, j, out, message.in)
}
@@ -190,7 +229,7 @@ func TestDecryptOAEP(t *testing.T) {
out, err = DecryptOAEP(sha1, random, private, message.out, nil)
if err != nil {
t.Errorf("#%d,%d (blind) error: %s", i, j, err)
- } else if bytes.Compare(out, message.in) != 0 {
+ } else if !bytes.Equal(out, message.in) {
t.Errorf("#%d,%d (blind) bad result: %#v (want %#v)", i, j, out, message.in)
}
}
diff --git a/src/pkg/crypto/sha1/sha1.go b/src/pkg/crypto/sha1/sha1.go
index 876e7992a..7cfde47dc 100644
--- a/src/pkg/crypto/sha1/sha1.go
+++ b/src/pkg/crypto/sha1/sha1.go
@@ -21,28 +21,28 @@ const Size = 20
const BlockSize = 64
const (
- _Chunk = 64
- _Init0 = 0x67452301
- _Init1 = 0xEFCDAB89
- _Init2 = 0x98BADCFE
- _Init3 = 0x10325476
- _Init4 = 0xC3D2E1F0
+ chunk = 64
+ init0 = 0x67452301
+ init1 = 0xEFCDAB89
+ init2 = 0x98BADCFE
+ init3 = 0x10325476
+ init4 = 0xC3D2E1F0
)
// digest represents the partial evaluation of a checksum.
type digest struct {
h [5]uint32
- x [_Chunk]byte
+ x [chunk]byte
nx int
len uint64
}
func (d *digest) Reset() {
- d.h[0] = _Init0
- d.h[1] = _Init1
- d.h[2] = _Init2
- d.h[3] = _Init3
- d.h[4] = _Init4
+ d.h[0] = init0
+ d.h[1] = init1
+ d.h[2] = init2
+ d.h[3] = init3
+ d.h[4] = init4
d.nx = 0
d.len = 0
}
@@ -63,21 +63,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
d.len += uint64(nn)
if d.nx > 0 {
n := len(p)
- if n > _Chunk-d.nx {
- n = _Chunk - d.nx
+ if n > chunk-d.nx {
+ n = chunk - d.nx
}
for i := 0; i < n; i++ {
d.x[d.nx+i] = p[i]
}
d.nx += n
- if d.nx == _Chunk {
- _Block(d, d.x[0:])
+ if d.nx == chunk {
+ block(d, d.x[0:])
d.nx = 0
}
p = p[n:]
}
- n := _Block(d, p)
- p = p[n:]
+ if len(p) >= chunk {
+ n := len(p) &^ (chunk - 1)
+ block(d, p[:n])
+ p = p[n:]
+ }
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
diff --git a/src/pkg/crypto/sha1/sha1_test.go b/src/pkg/crypto/sha1/sha1_test.go
index 2dc14ac98..57cd4313e 100644
--- a/src/pkg/crypto/sha1/sha1_test.go
+++ b/src/pkg/crypto/sha1/sha1_test.go
@@ -4,10 +4,9 @@
// SHA1 hash algorithm. See RFC 3174.
-package sha1_test
+package sha1
import (
- "crypto/sha1"
"fmt"
"io"
"testing"
@@ -55,7 +54,7 @@ var golden = []sha1Test{
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
- c := sha1.New()
+ c := New()
for j := 0; j < 3; j++ {
if j < 2 {
io.WriteString(c, g.in)
@@ -74,8 +73,33 @@ func TestGolden(t *testing.T) {
}
func ExampleNew() {
- h := sha1.New()
+ h := New()
io.WriteString(h, "His money is twice tainted: 'taint yours and 'taint mine.")
fmt.Printf("% x", h.Sum(nil))
// Output: 59 7f 6a 54 00 10 f9 4c 15 d7 18 06 a9 9a 2c 87 10 e7 47 bd
}
+
+var bench = New()
+var buf = make([]byte, 8192)
+
+func benchmarkSize(b *testing.B, size int) {
+ b.SetBytes(int64(size))
+ sum := make([]byte, bench.Size())
+ for i := 0; i < b.N; i++ {
+ bench.Reset()
+ bench.Write(buf[:size])
+ bench.Sum(sum[:0])
+ }
+}
+
+func BenchmarkHash8Bytes(b *testing.B) {
+ benchmarkSize(b, 8)
+}
+
+func BenchmarkHash1K(b *testing.B) {
+ benchmarkSize(b, 1024)
+}
+
+func BenchmarkHash8K(b *testing.B) {
+ benchmarkSize(b, 8192)
+}
diff --git a/src/pkg/crypto/sha1/sha1block.go b/src/pkg/crypto/sha1/sha1block.go
index b5d32af70..1c9507c68 100644
--- a/src/pkg/crypto/sha1/sha1block.go
+++ b/src/pkg/crypto/sha1/sha1block.go
@@ -15,54 +15,67 @@ const (
_K3 = 0xCA62C1D6
)
-func _Block(dig *digest, p []byte) int {
- var w [80]uint32
+func block(dig *digest, p []byte) {
+ var w [16]uint32
- n := 0
h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4]
- for len(p) >= _Chunk {
+ for len(p) >= chunk {
// Can interlace the computation of w with the
// rounds below if needed for speed.
for i := 0; i < 16; i++ {
j := i * 4
w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
}
- for i := 16; i < 80; i++ {
- tmp := w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]
- w[i] = tmp<<1 | tmp>>(32-1)
- }
a, b, c, d, e := h0, h1, h2, h3, h4
// Each of the four 20-iteration rounds
// differs only in the computation of f and
// the choice of K (_K0, _K1, etc).
- for i := 0; i < 20; i++ {
+ i := 0
+ for ; i < 16; i++ {
+ f := b&c | (^b)&d
+ a5 := a<<5 | a>>(32-5)
+ b30 := b<<30 | b>>(32-30)
+ t := a5 + f + e + w[i&0xf] + _K0
+ a, b, c, d, e = t, a, b30, c, d
+ }
+ for ; i < 20; i++ {
+ tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
+ w[i&0xf] = tmp<<1 | tmp>>(32-1)
+
f := b&c | (^b)&d
a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30)
- t := a5 + f + e + w[i] + _K0
+ t := a5 + f + e + w[i&0xf] + _K0
a, b, c, d, e = t, a, b30, c, d
}
- for i := 20; i < 40; i++ {
+ for ; i < 40; i++ {
+ tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
+ w[i&0xf] = tmp<<1 | tmp>>(32-1)
f := b ^ c ^ d
a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30)
- t := a5 + f + e + w[i] + _K1
+ t := a5 + f + e + w[i&0xf] + _K1
a, b, c, d, e = t, a, b30, c, d
}
- for i := 40; i < 60; i++ {
- f := b&c | b&d | c&d
+ for ; i < 60; i++ {
+ tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
+ w[i&0xf] = tmp<<1 | tmp>>(32-1)
+ f := ((b | c) & d) | (b & c)
+
a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30)
- t := a5 + f + e + w[i] + _K2
+ t := a5 + f + e + w[i&0xf] + _K2
a, b, c, d, e = t, a, b30, c, d
}
- for i := 60; i < 80; i++ {
+ for ; i < 80; i++ {
+ tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
+ w[i&0xf] = tmp<<1 | tmp>>(32-1)
f := b ^ c ^ d
a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30)
- t := a5 + f + e + w[i] + _K3
+ t := a5 + f + e + w[i&0xf] + _K3
a, b, c, d, e = t, a, b30, c, d
}
@@ -72,10 +85,8 @@ func _Block(dig *digest, p []byte) int {
h3 += d
h4 += e
- p = p[_Chunk:]
- n += _Chunk
+ p = p[chunk:]
}
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] = h0, h1, h2, h3, h4
- return n
}
diff --git a/src/pkg/crypto/sha256/sha256.go b/src/pkg/crypto/sha256/sha256.go
index a61e30b42..dc0e18f50 100644
--- a/src/pkg/crypto/sha256/sha256.go
+++ b/src/pkg/crypto/sha256/sha256.go
@@ -26,29 +26,29 @@ const Size224 = 28
const BlockSize = 64
const (
- _Chunk = 64
- _Init0 = 0x6A09E667
- _Init1 = 0xBB67AE85
- _Init2 = 0x3C6EF372
- _Init3 = 0xA54FF53A
- _Init4 = 0x510E527F
- _Init5 = 0x9B05688C
- _Init6 = 0x1F83D9AB
- _Init7 = 0x5BE0CD19
- _Init0_224 = 0xC1059ED8
- _Init1_224 = 0x367CD507
- _Init2_224 = 0x3070DD17
- _Init3_224 = 0xF70E5939
- _Init4_224 = 0xFFC00B31
- _Init5_224 = 0x68581511
- _Init6_224 = 0x64F98FA7
- _Init7_224 = 0xBEFA4FA4
+ chunk = 64
+ init0 = 0x6A09E667
+ init1 = 0xBB67AE85
+ init2 = 0x3C6EF372
+ init3 = 0xA54FF53A
+ init4 = 0x510E527F
+ init5 = 0x9B05688C
+ init6 = 0x1F83D9AB
+ init7 = 0x5BE0CD19
+ init0_224 = 0xC1059ED8
+ init1_224 = 0x367CD507
+ init2_224 = 0x3070DD17
+ init3_224 = 0xF70E5939
+ init4_224 = 0xFFC00B31
+ init5_224 = 0x68581511
+ init6_224 = 0x64F98FA7
+ init7_224 = 0xBEFA4FA4
)
// digest represents the partial evaluation of a checksum.
type digest struct {
h [8]uint32
- x [_Chunk]byte
+ x [chunk]byte
nx int
len uint64
is224 bool // mark if this digest is SHA-224
@@ -56,23 +56,23 @@ type digest struct {
func (d *digest) Reset() {
if !d.is224 {
- d.h[0] = _Init0
- d.h[1] = _Init1
- d.h[2] = _Init2
- d.h[3] = _Init3
- d.h[4] = _Init4
- d.h[5] = _Init5
- d.h[6] = _Init6
- d.h[7] = _Init7
+ d.h[0] = init0
+ d.h[1] = init1
+ d.h[2] = init2
+ d.h[3] = init3
+ d.h[4] = init4
+ d.h[5] = init5
+ d.h[6] = init6
+ d.h[7] = init7
} else {
- d.h[0] = _Init0_224
- d.h[1] = _Init1_224
- d.h[2] = _Init2_224
- d.h[3] = _Init3_224
- d.h[4] = _Init4_224
- d.h[5] = _Init5_224
- d.h[6] = _Init6_224
- d.h[7] = _Init7_224
+ d.h[0] = init0_224
+ d.h[1] = init1_224
+ d.h[2] = init2_224
+ d.h[3] = init3_224
+ d.h[4] = init4_224
+ d.h[5] = init5_224
+ d.h[6] = init6_224
+ d.h[7] = init7_224
}
d.nx = 0
d.len = 0
@@ -107,21 +107,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
d.len += uint64(nn)
if d.nx > 0 {
n := len(p)
- if n > _Chunk-d.nx {
- n = _Chunk - d.nx
+ if n > chunk-d.nx {
+ n = chunk - d.nx
}
for i := 0; i < n; i++ {
d.x[d.nx+i] = p[i]
}
d.nx += n
- if d.nx == _Chunk {
- _Block(d, d.x[0:])
+ if d.nx == chunk {
+ block(d, d.x[0:])
d.nx = 0
}
p = p[n:]
}
- n := _Block(d, p)
- p = p[n:]
+ if len(p) >= chunk {
+ n := len(p) &^ (chunk - 1)
+ block(d, p[:n])
+ p = p[n:]
+ }
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
diff --git a/src/pkg/crypto/sha256/sha256_test.go b/src/pkg/crypto/sha256/sha256_test.go
index a6efb3754..29bf1619a 100644
--- a/src/pkg/crypto/sha256/sha256_test.go
+++ b/src/pkg/crypto/sha256/sha256_test.go
@@ -123,3 +123,28 @@ func TestGolden(t *testing.T) {
}
}
}
+
+var bench = New()
+var buf = make([]byte, 8192)
+
+func benchmarkSize(b *testing.B, size int) {
+ b.SetBytes(int64(size))
+ sum := make([]byte, bench.Size())
+ for i := 0; i < b.N; i++ {
+ bench.Reset()
+ bench.Write(buf[:size])
+ bench.Sum(sum[:0])
+ }
+}
+
+func BenchmarkHash8Bytes(b *testing.B) {
+ benchmarkSize(b, 8)
+}
+
+func BenchmarkHash1K(b *testing.B) {
+ benchmarkSize(b, 1024)
+}
+
+func BenchmarkHash8K(b *testing.B) {
+ benchmarkSize(b, 8192)
+}
diff --git a/src/pkg/crypto/sha256/sha256block.go b/src/pkg/crypto/sha256/sha256block.go
index 7b0f55444..2ac49100a 100644
--- a/src/pkg/crypto/sha256/sha256block.go
+++ b/src/pkg/crypto/sha256/sha256block.go
@@ -75,11 +75,10 @@ var _K = []uint32{
0xc67178f2,
}
-func _Block(dig *digest, p []byte) int {
+func block(dig *digest, p []byte) {
var w [64]uint32
- n := 0
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
- for len(p) >= _Chunk {
+ for len(p) >= chunk {
// Can interlace the computation of w with the
// rounds below if needed for speed.
for i := 0; i < 16; i++ {
@@ -87,10 +86,10 @@ func _Block(dig *digest, p []byte) int {
w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
}
for i := 16; i < 64; i++ {
- t1 := (w[i-2]>>17 | w[i-2]<<(32-17)) ^ (w[i-2]>>19 | w[i-2]<<(32-19)) ^ (w[i-2] >> 10)
-
- t2 := (w[i-15]>>7 | w[i-15]<<(32-7)) ^ (w[i-15]>>18 | w[i-15]<<(32-18)) ^ (w[i-15] >> 3)
-
+ v1 := w[i-2]
+ t1 := (v1>>17 | v1<<(32-17)) ^ (v1>>19 | v1<<(32-19)) ^ (v1 >> 10)
+ v2 := w[i-15]
+ t2 := (v2>>7 | v2<<(32-7)) ^ (v2>>18 | v2<<(32-18)) ^ (v2 >> 3)
w[i] = t1 + w[i-7] + t2 + w[i-16]
}
@@ -120,10 +119,8 @@ func _Block(dig *digest, p []byte) int {
h6 += g
h7 += h
- p = p[_Chunk:]
- n += _Chunk
+ p = p[chunk:]
}
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
- return n
}
diff --git a/src/pkg/crypto/sha512/sha512.go b/src/pkg/crypto/sha512/sha512.go
index a245fd68e..4aec52938 100644
--- a/src/pkg/crypto/sha512/sha512.go
+++ b/src/pkg/crypto/sha512/sha512.go
@@ -26,29 +26,29 @@ const Size384 = 48
const BlockSize = 128
const (
- _Chunk = 128
- _Init0 = 0x6a09e667f3bcc908
- _Init1 = 0xbb67ae8584caa73b
- _Init2 = 0x3c6ef372fe94f82b
- _Init3 = 0xa54ff53a5f1d36f1
- _Init4 = 0x510e527fade682d1
- _Init5 = 0x9b05688c2b3e6c1f
- _Init6 = 0x1f83d9abfb41bd6b
- _Init7 = 0x5be0cd19137e2179
- _Init0_384 = 0xcbbb9d5dc1059ed8
- _Init1_384 = 0x629a292a367cd507
- _Init2_384 = 0x9159015a3070dd17
- _Init3_384 = 0x152fecd8f70e5939
- _Init4_384 = 0x67332667ffc00b31
- _Init5_384 = 0x8eb44a8768581511
- _Init6_384 = 0xdb0c2e0d64f98fa7
- _Init7_384 = 0x47b5481dbefa4fa4
+ chunk = 128
+ init0 = 0x6a09e667f3bcc908
+ init1 = 0xbb67ae8584caa73b
+ init2 = 0x3c6ef372fe94f82b
+ init3 = 0xa54ff53a5f1d36f1
+ init4 = 0x510e527fade682d1
+ init5 = 0x9b05688c2b3e6c1f
+ init6 = 0x1f83d9abfb41bd6b
+ init7 = 0x5be0cd19137e2179
+ init0_384 = 0xcbbb9d5dc1059ed8
+ init1_384 = 0x629a292a367cd507
+ init2_384 = 0x9159015a3070dd17
+ init3_384 = 0x152fecd8f70e5939
+ init4_384 = 0x67332667ffc00b31
+ init5_384 = 0x8eb44a8768581511
+ init6_384 = 0xdb0c2e0d64f98fa7
+ init7_384 = 0x47b5481dbefa4fa4
)
// digest represents the partial evaluation of a checksum.
type digest struct {
h [8]uint64
- x [_Chunk]byte
+ x [chunk]byte
nx int
len uint64
is384 bool // mark if this digest is SHA-384
@@ -56,23 +56,23 @@ type digest struct {
func (d *digest) Reset() {
if !d.is384 {
- d.h[0] = _Init0
- d.h[1] = _Init1
- d.h[2] = _Init2
- d.h[3] = _Init3
- d.h[4] = _Init4
- d.h[5] = _Init5
- d.h[6] = _Init6
- d.h[7] = _Init7
+ d.h[0] = init0
+ d.h[1] = init1
+ d.h[2] = init2
+ d.h[3] = init3
+ d.h[4] = init4
+ d.h[5] = init5
+ d.h[6] = init6
+ d.h[7] = init7
} else {
- d.h[0] = _Init0_384
- d.h[1] = _Init1_384
- d.h[2] = _Init2_384
- d.h[3] = _Init3_384
- d.h[4] = _Init4_384
- d.h[5] = _Init5_384
- d.h[6] = _Init6_384
- d.h[7] = _Init7_384
+ d.h[0] = init0_384
+ d.h[1] = init1_384
+ d.h[2] = init2_384
+ d.h[3] = init3_384
+ d.h[4] = init4_384
+ d.h[5] = init5_384
+ d.h[6] = init6_384
+ d.h[7] = init7_384
}
d.nx = 0
d.len = 0
@@ -107,21 +107,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
d.len += uint64(nn)
if d.nx > 0 {
n := len(p)
- if n > _Chunk-d.nx {
- n = _Chunk - d.nx
+ if n > chunk-d.nx {
+ n = chunk - d.nx
}
for i := 0; i < n; i++ {
d.x[d.nx+i] = p[i]
}
d.nx += n
- if d.nx == _Chunk {
- _Block(d, d.x[0:])
+ if d.nx == chunk {
+ block(d, d.x[0:])
d.nx = 0
}
p = p[n:]
}
- n := _Block(d, p)
- p = p[n:]
+ if len(p) >= chunk {
+ n := len(p) &^ (chunk - 1)
+ block(d, p[:n])
+ p = p[n:]
+ }
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
diff --git a/src/pkg/crypto/sha512/sha512_test.go b/src/pkg/crypto/sha512/sha512_test.go
index a70f7c54e..6eafb1b5f 100644
--- a/src/pkg/crypto/sha512/sha512_test.go
+++ b/src/pkg/crypto/sha512/sha512_test.go
@@ -123,3 +123,28 @@ func TestGolden(t *testing.T) {
}
}
}
+
+var bench = New()
+var buf = make([]byte, 8192)
+
+func benchmarkSize(b *testing.B, size int) {
+ b.SetBytes(int64(size))
+ sum := make([]byte, bench.Size())
+ for i := 0; i < b.N; i++ {
+ bench.Reset()
+ bench.Write(buf[:size])
+ bench.Sum(sum[:0])
+ }
+}
+
+func BenchmarkHash8Bytes(b *testing.B) {
+ benchmarkSize(b, 8)
+}
+
+func BenchmarkHash1K(b *testing.B) {
+ benchmarkSize(b, 1024)
+}
+
+func BenchmarkHash8K(b *testing.B) {
+ benchmarkSize(b, 8192)
+}
diff --git a/src/pkg/crypto/sha512/sha512block.go b/src/pkg/crypto/sha512/sha512block.go
index 6b7506287..3577b4f3d 100644
--- a/src/pkg/crypto/sha512/sha512block.go
+++ b/src/pkg/crypto/sha512/sha512block.go
@@ -91,20 +91,20 @@ var _K = []uint64{
0x6c44198c4a475817,
}
-func _Block(dig *digest, p []byte) int {
+func block(dig *digest, p []byte) {
var w [80]uint64
- n := 0
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
- for len(p) >= _Chunk {
+ for len(p) >= chunk {
for i := 0; i < 16; i++ {
j := i * 8
w[i] = uint64(p[j])<<56 | uint64(p[j+1])<<48 | uint64(p[j+2])<<40 | uint64(p[j+3])<<32 |
uint64(p[j+4])<<24 | uint64(p[j+5])<<16 | uint64(p[j+6])<<8 | uint64(p[j+7])
}
for i := 16; i < 80; i++ {
- t1 := (w[i-2]>>19 | w[i-2]<<(64-19)) ^ (w[i-2]>>61 | w[i-2]<<(64-61)) ^ (w[i-2] >> 6)
-
- t2 := (w[i-15]>>1 | w[i-15]<<(64-1)) ^ (w[i-15]>>8 | w[i-15]<<(64-8)) ^ (w[i-15] >> 7)
+ v1 := w[i-2]
+ t1 := (v1>>19 | v1<<(64-19)) ^ (v1>>61 | v1<<(64-61)) ^ (v1 >> 6)
+ v2 := w[i-15]
+ t2 := (v2>>1 | v2<<(64-1)) ^ (v2>>8 | v2<<(64-8)) ^ (v2 >> 7)
w[i] = t1 + w[i-7] + t2 + w[i-16]
}
@@ -135,10 +135,8 @@ func _Block(dig *digest, p []byte) int {
h6 += g
h7 += h
- p = p[_Chunk:]
- n += _Chunk
+ p = p[chunk:]
}
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
- return n
}
diff --git a/src/pkg/crypto/tls/cipher_suites.go b/src/pkg/crypto/tls/cipher_suites.go
index 5039f319f..a647e19aa 100644
--- a/src/pkg/crypto/tls/cipher_suites.go
+++ b/src/pkg/crypto/tls/cipher_suites.go
@@ -55,9 +55,11 @@ var cipherSuites = []*cipherSuite{
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1},
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1},
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1},
+ {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, false, cipherAES, macSHA1},
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1},
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1},
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1},
+ {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1},
}
func cipherRC4(key, iv []byte, isRead bool) interface{} {
@@ -182,7 +184,9 @@ const (
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
+ TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
)
diff --git a/src/pkg/crypto/tls/common.go b/src/pkg/crypto/tls/common.go
index 4ba0bf874..a888df762 100644
--- a/src/pkg/crypto/tls/common.go
+++ b/src/pkg/crypto/tls/common.go
@@ -41,6 +41,7 @@ const (
const (
typeClientHello uint8 = 1
typeServerHello uint8 = 2
+ typeNewSessionTicket uint8 = 4
typeCertificate uint8 = 11
typeServerKeyExchange uint8 = 12
typeCertificateRequest uint8 = 13
@@ -63,6 +64,7 @@ var (
extensionStatusRequest uint16 = 5
extensionSupportedCurves uint16 = 10
extensionSupportedPoints uint16 = 11
+ extensionSessionTicket uint16 = 35
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
)
@@ -97,6 +99,7 @@ const (
// ConnectionState records basic TLS details about the connection.
type ConnectionState struct {
HandshakeComplete bool
+ DidResume bool
CipherSuite uint16
NegotiatedProtocol string
NegotiatedProtocolIsMutual bool
@@ -180,6 +183,28 @@ type Config struct {
// CipherSuites is a list of supported cipher suites. If CipherSuites
// is nil, TLS uses a list of suites supported by the implementation.
CipherSuites []uint16
+
+ // PreferServerCipherSuites controls whether the server selects the
+ // client's most preferred ciphersuite, or the server's most preferred
+ // ciphersuite. If true then the server's preference, as expressed in
+ // the order of elements in CipherSuites, is used.
+ PreferServerCipherSuites bool
+
+ // SessionTicketsDisabled may be set to true to disable session ticket
+ // (resumption) support.
+ SessionTicketsDisabled bool
+
+ // SessionTicketKey is used by TLS servers to provide session
+ // resumption. See RFC 5077. If zero, it will be filled with
+ // random data before the first server handshake.
+ //
+ // If multiple servers are terminating connections for the same host
+ // they should all have the same SessionTicketKey. If the
+ // SessionTicketKey leaks, previously recorded and future TLS
+ // connections using that key are compromised.
+ SessionTicketKey [32]byte
+
+ serverInitOnce sync.Once
}
func (c *Config) rand() io.Reader {
diff --git a/src/pkg/crypto/tls/conn.go b/src/pkg/crypto/tls/conn.go
index 2a5115dc6..d8c2be00a 100644
--- a/src/pkg/crypto/tls/conn.go
+++ b/src/pkg/crypto/tls/conn.go
@@ -31,6 +31,7 @@ type Conn struct {
haveVers bool // version has been negotiated
config *Config // configuration passed to constructor
handshakeComplete bool
+ didResume bool // whether this connection was a session resumption
cipherSuite uint16
ocspResponse []byte // stapled OCSP response
peerCertificates []*x509.Certificate
@@ -44,8 +45,7 @@ type Conn struct {
clientProtocolFallback bool
// first permanent error
- errMutex sync.Mutex
- err error
+ connErr
// input/output
in, out halfConn // in.Mutex < out.Mutex
@@ -56,21 +56,25 @@ type Conn struct {
tmp [16]byte
}
-func (c *Conn) setError(err error) error {
- c.errMutex.Lock()
- defer c.errMutex.Unlock()
+type connErr struct {
+ mu sync.Mutex
+ value error
+}
+
+func (e *connErr) setError(err error) error {
+ e.mu.Lock()
+ defer e.mu.Unlock()
- if c.err == nil {
- c.err = err
+ if e.value == nil {
+ e.value = err
}
return err
}
-func (c *Conn) error() error {
- c.errMutex.Lock()
- defer c.errMutex.Unlock()
-
- return c.err
+func (e *connErr) error() error {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+ return e.value
}
// Access to net.Conn methods.
@@ -487,6 +491,16 @@ Again:
return err
}
typ := recordType(b.data[0])
+
+ // No valid TLS record has a type of 0x80, however SSLv2 handshakes
+ // start with a uint16 length where the MSB is set and the first record
+ // is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
+ // an SSLv2 client.
+ if want == recordTypeHandshake && typ == 0x80 {
+ c.sendAlert(alertProtocolVersion)
+ return errors.New("tls: unsupported SSLv2 handshake received")
+ }
+
vers := uint16(b.data[1])<<8 | uint16(b.data[2])
n := int(b.data[3])<<8 | int(b.data[4])
if c.haveVers && vers != c.vers {
@@ -499,7 +513,7 @@ Again:
// First message, be extra suspicious:
// this might not be a TLS client.
// Bail out before reading a full 'body', if possible.
- // The current max version is 3.1.
+ // The current max version is 3.1.
// If the version is >= 16.0, it's probably not real.
// Similarly, a clientHello message encodes in
// well under a kilobyte. If the length is >= 12 kB,
@@ -590,9 +604,11 @@ Again:
// sendAlert sends a TLS alert message.
// c.out.Mutex <= L.
func (c *Conn) sendAlertLocked(err alert) error {
- c.tmp[0] = alertLevelError
- if err == alertNoRenegotiation {
+ switch err {
+ case alertNoRenegotiation, alertCloseNotify:
c.tmp[0] = alertLevelWarning
+ default:
+ c.tmp[0] = alertLevelError
}
c.tmp[1] = byte(err)
c.writeRecord(recordTypeAlert, c.tmp[0:2])
@@ -650,8 +666,7 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
c.tmp[0] = alertLevelError
c.tmp[1] = byte(err.(alert))
c.writeRecord(recordTypeAlert, c.tmp[0:2])
- c.err = &net.OpError{Op: "local error", Err: err}
- return n, c.err
+ return n, c.setError(&net.OpError{Op: "local error", Err: err})
}
}
return
@@ -662,8 +677,8 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
// c.in.Mutex < L; c.out.Mutex < L.
func (c *Conn) readHandshake() (interface{}, error) {
for c.hand.Len() < 4 {
- if c.err != nil {
- return nil, c.err
+ if err := c.error(); err != nil {
+ return nil, err
}
if err := c.readRecord(recordTypeHandshake); err != nil {
return nil, err
@@ -674,11 +689,11 @@ func (c *Conn) readHandshake() (interface{}, error) {
n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
if n > maxHandshake {
c.sendAlert(alertInternalError)
- return nil, c.err
+ return nil, c.error()
}
for c.hand.Len() < 4+n {
- if c.err != nil {
- return nil, c.err
+ if err := c.error(); err != nil {
+ return nil, err
}
if err := c.readRecord(recordTypeHandshake); err != nil {
return nil, err
@@ -728,12 +743,12 @@ func (c *Conn) readHandshake() (interface{}, error) {
// Write writes data to the connection.
func (c *Conn) Write(b []byte) (int, error) {
- if c.err != nil {
- return 0, c.err
+ if err := c.error(); err != nil {
+ return 0, err
}
- if c.err = c.Handshake(); c.err != nil {
- return 0, c.err
+ if err := c.Handshake(); err != nil {
+ return 0, c.setError(err)
}
c.out.Lock()
@@ -743,9 +758,28 @@ func (c *Conn) Write(b []byte) (int, error) {
return 0, alertInternalError
}
- var n int
- n, c.err = c.writeRecord(recordTypeApplicationData, b)
- return n, c.err
+ // SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
+ // attack when using block mode ciphers due to predictable IVs.
+ // This can be prevented by splitting each Application Data
+ // record into two records, effectively randomizing the IV.
+ //
+ // http://www.openssl.org/~bodo/tls-cbc.txt
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=665814
+ // http://www.imperialviolet.org/2012/01/15/beastfollowup.html
+
+ var m int
+ if len(b) > 1 && c.vers <= versionTLS10 {
+ if _, ok := c.out.cipher.(cipher.BlockMode); ok {
+ n, err := c.writeRecord(recordTypeApplicationData, b[:1])
+ if err != nil {
+ return n, c.setError(err)
+ }
+ m, b = 1, b[1:]
+ }
+ }
+
+ n, err := c.writeRecord(recordTypeApplicationData, b)
+ return n + m, c.setError(err)
}
// Read can be made to time out and return a net.Error with Timeout() == true
@@ -758,14 +792,14 @@ func (c *Conn) Read(b []byte) (n int, err error) {
c.in.Lock()
defer c.in.Unlock()
- for c.input == nil && c.err == nil {
+ for c.input == nil && c.error() == nil {
if err := c.readRecord(recordTypeApplicationData); err != nil {
// Soft error, like EAGAIN
return 0, err
}
}
- if c.err != nil {
- return 0, c.err
+ if err := c.error(); err != nil {
+ return 0, err
}
n, err = c.input.Read(b)
if c.input.off >= len(c.input.data) {
@@ -819,6 +853,7 @@ func (c *Conn) ConnectionState() ConnectionState {
state.HandshakeComplete = c.handshakeComplete
if c.handshakeComplete {
state.NegotiatedProtocol = c.clientProtocol
+ state.DidResume = c.didResume
state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
state.CipherSuite = c.cipherSuite
state.PeerCertificates = c.peerCertificates
diff --git a/src/pkg/crypto/tls/generate_cert.go b/src/pkg/crypto/tls/generate_cert.go
index 84be5bfd8..215644d24 100644
--- a/src/pkg/crypto/tls/generate_cert.go
+++ b/src/pkg/crypto/tls/generate_cert.go
@@ -16,36 +16,80 @@ import (
"crypto/x509/pkix"
"encoding/pem"
"flag"
+ "fmt"
"log"
"math/big"
+ "net"
"os"
+ "strings"
"time"
)
-var hostName *string = flag.String("host", "127.0.0.1", "Hostname to generate a certificate for")
+var (
+ host = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for")
+ validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011")
+ validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for")
+ isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
+ rsaBits = flag.Int("rsa-bits", 1024, "Size of RSA key to generate")
+)
func main() {
flag.Parse()
- priv, err := rsa.GenerateKey(rand.Reader, 1024)
+ if len(*host) == 0 {
+ log.Fatalf("Missing required --host parameter")
+ }
+
+ priv, err := rsa.GenerateKey(rand.Reader, *rsaBits)
if err != nil {
log.Fatalf("failed to generate private key: %s", err)
return
}
- now := time.Now()
+ var notBefore time.Time
+ if len(*validFrom) == 0 {
+ notBefore = time.Now()
+ } else {
+ notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err)
+ os.Exit(1)
+ }
+ }
+
+ notAfter := notBefore.Add(*validFor)
+
+ // end of ASN.1 time
+ endOfTime := time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC)
+ if notAfter.After(endOfTime) {
+ notAfter = endOfTime
+ }
template := x509.Certificate{
SerialNumber: new(big.Int).SetInt64(0),
Subject: pkix.Name{
- CommonName: *hostName,
Organization: []string{"Acme Co"},
},
- NotBefore: now.Add(-5 * time.Minute).UTC(),
- NotAfter: now.AddDate(1, 0, 0).UTC(), // valid for 1 year.
+ NotBefore: notBefore,
+ NotAfter: notAfter,
+
+ KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
+ ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+ BasicConstraintsValid: true,
+ }
+
+ hosts := strings.Split(*host, ",")
+ for _, h := range hosts {
+ if ip := net.ParseIP(h); ip != nil {
+ template.IPAddresses = append(template.IPAddresses, ip)
+ } else {
+ template.DNSNames = append(template.DNSNames, h)
+ }
+ }
- SubjectKeyId: []byte{1, 2, 3, 4},
- KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
+ if *isCA {
+ template.IsCA = true
+ template.KeyUsage |= x509.KeyUsageCertSign
}
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
diff --git a/src/pkg/crypto/tls/handshake_client.go b/src/pkg/crypto/tls/handshake_client.go
index 2877f1738..7db13bf70 100644
--- a/src/pkg/crypto/tls/handshake_client.go
+++ b/src/pkg/crypto/tls/handshake_client.go
@@ -278,8 +278,9 @@ func (c *Conn) clientHandshake() error {
c.writeRecord(recordTypeHandshake, certVerify.marshal())
}
- masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
- keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)
+ masterSecret := masterFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random)
+ clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
+ keysFromMasterSecret(c.vers, masterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)
clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */)
clientHash := suite.mac(c.vers, clientMAC)
@@ -306,8 +307,8 @@ func (c *Conn) clientHandshake() error {
serverHash := suite.mac(c.vers, serverMAC)
c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
c.readRecord(recordTypeChangeCipherSpec)
- if c.err != nil {
- return c.err
+ if err := c.error(); err != nil {
+ return err
}
msg, err = c.readHandshake()
diff --git a/src/pkg/crypto/tls/handshake_client_test.go b/src/pkg/crypto/tls/handshake_client_test.go
index 8c56daaf6..9673947a4 100644
--- a/src/pkg/crypto/tls/handshake_client_test.go
+++ b/src/pkg/crypto/tls/handshake_client_test.go
@@ -9,6 +9,7 @@ import (
"flag"
"io"
"net"
+ "os"
"testing"
)
@@ -39,7 +40,15 @@ func testClientScript(t *testing.T, name string, clientScript [][]byte, config *
}
func TestHandshakeClientRC4(t *testing.T) {
- testClientScript(t, "RC4", rc4ClientScript, testConfig)
+ var config = *testConfig
+ config.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA}
+ testClientScript(t, "RC4", rc4ClientScript, &config)
+}
+
+func TestHandshakeClientECDHEAES(t *testing.T) {
+ var config = *testConfig
+ config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}
+ testClientScript(t, "ECDHE-AES", ecdheAESClientScript, &config)
}
var connect = flag.Bool("connect", false, "connect to a TLS server on :10443")
@@ -49,25 +58,33 @@ func TestRunClient(t *testing.T) {
return
}
- testConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
-
- conn, err := Dial("tcp", "127.0.0.1:10443", testConfig)
+ tcpConn, err := net.Dial("tcp", "127.0.0.1:10443")
if err != nil {
t.Fatal(err)
}
+ record := &recordingConn{
+ Conn: tcpConn,
+ }
+
+ config := GetTestConfig()
+ conn := Client(record, config)
+ if err := conn.Handshake(); err != nil {
+ t.Fatalf("error from TLS handshake: %s", err)
+ }
+
conn.Write([]byte("hello\n"))
conn.Close()
+
+ record.WriteTo(os.Stdout)
}
// Script of interaction with gnutls implementation.
// The values for this test are obtained by building and running in client mode:
-// % go test -run "TestRunClient" -connect
-// and then:
-// % gnutls-serv -p 10443 --debug 100 --x509keyfile key.pem --x509certfile cert.pem -a > /tmp/log 2>&1
-// % python parse-gnutls-cli-debug-log.py < /tmp/log
+// % go test -test.run "TestRunClient" -connect
+// The recorded bytes are written to stdout.
//
-// Where key.pem is:
+// The server private key is:
// -----BEGIN RSA PRIVATE KEY-----
// MIIBPAIBAAJBAJ+zw4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVD
// TGiXav6ooKXfX3j/7tdkuD8Ey2//Kv7+ue0CAwEAAQJAN6W31vDEP2DjdqhzCDDu
@@ -78,17 +95,20 @@ func TestRunClient(t *testing.T) {
// vnlEGo8K85u+KwIOimM48ZG8oTk7iFdkqLJR1utT3aU=
// -----END RSA PRIVATE KEY-----
//
-// and cert.pem is:
+// and certificate is:
// -----BEGIN CERTIFICATE-----
-// MIIBoDCCAUoCAQAwDQYJKoZIhvcNAQEEBQAwYzELMAkGA1UEBhMCQVUxEzARBgNV
-// BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSMwIQYD
-// VQQDExpTZXJ2ZXIgdGVzdCBjZXJ0ICg1MTIgYml0KTAeFw05NzA5MDkwMzQxMjZa
-// Fw05NzEwMDkwMzQxMjZaMF4xCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
-// YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFzAVBgNVBAMT
-// DkVyaWMgdGhlIFlvdW5nMFEwCQYFKw4DAgwFAANEAAJBALVEqPODnpI4rShlY8S7
-// tB713JNvabvn6Gned7zylwLLiXQAo/PAT6mfdWPTyCX9RlId/Aroh1ou893BA32Q
-// sggwDQYJKoZIhvcNAQEEBQADQQCU5SSgapJSdRXJoX+CpCvFy+JVh9HpSjCpSNKO
-// 19raHv98hKAUJuP9HyM+SUsffO6mAIgitUaqW8/wDMePhEC3
+// MIICKzCCAdWgAwIBAgIJALE1E2URIMWSMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
+// BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+// aWRnaXRzIFB0eSBMdGQwHhcNMTIwNDA2MTcxMDEzWhcNMTUwNDA2MTcxMDEzWjBF
+// MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
+// ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ+z
+// w4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVDTGiXav6ooKXfX3j/
+// 7tdkuD8Ey2//Kv7+ue0CAwEAAaOBpzCBpDAdBgNVHQ4EFgQUeKaXmmO1xaGlM7oi
+// fCNuWxt6zCswdQYDVR0jBG4wbIAUeKaXmmO1xaGlM7oifCNuWxt6zCuhSaRHMEUx
+// CzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRl
+// cm5ldCBXaWRnaXRzIFB0eSBMdGSCCQCxNRNlESDFkjAMBgNVHRMEBTADAQH/MA0G
+// CSqGSIb3DQEBBQUAA0EAhTZAc8G7GtrUWZ8tonAxRnTsg26oyDxRrzms7EC86CJG
+// HZnWRiok1IsFCEv7NRFukrt3uuQSu/TIXpyBqJdgTA==
// -----END CERTIFICATE-----
var rc4ClientScript = [][]byte{
{
@@ -210,3 +230,179 @@ var rc4ClientScript = [][]byte{
0x59, 0xac, 0xc6, 0xb5, 0x56, 0x55, 0x96,
},
}
+
+var ecdheAESClientScript = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00,
+ 0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x13,
+ 0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
+ 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00,
+ 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x52, 0x02, 0x00, 0x00,
+ 0x4e, 0x03, 0x01, 0x50, 0xad, 0x72, 0xb1, 0x14,
+ 0x45, 0xce, 0x0a, 0x95, 0xf9, 0x63, 0xef, 0xa8,
+ 0xe5, 0x07, 0x34, 0x04, 0xe9, 0x08, 0x0f, 0x38,
+ 0xe4, 0x28, 0x27, 0x91, 0x07, 0x03, 0xe2, 0xfe,
+ 0xe3, 0x25, 0xf7, 0x20, 0x08, 0x42, 0xa2, 0x01,
+ 0x69, 0x53, 0xf0, 0xd9, 0x4c, 0xfa, 0x01, 0xa1,
+ 0xce, 0x4b, 0xf8, 0x28, 0x21, 0xad, 0x06, 0xbe,
+ 0xe0, 0x1b, 0x3b, 0xf7, 0xec, 0xd2, 0x52, 0xae,
+ 0x2a, 0x57, 0xb7, 0xa8, 0xc0, 0x13, 0x00, 0x00,
+ 0x06, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x16,
+ 0x03, 0x01, 0x02, 0x39, 0x0b, 0x00, 0x02, 0x35,
+ 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f, 0x30, 0x82,
+ 0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5, 0xa0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xb1, 0x35,
+ 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
+ 0x31, 0x32, 0x30, 0x34, 0x30, 0x36, 0x31, 0x37,
+ 0x31, 0x30, 0x31, 0x33, 0x5a, 0x17, 0x0d, 0x31,
+ 0x35, 0x30, 0x34, 0x30, 0x36, 0x31, 0x37, 0x31,
+ 0x30, 0x31, 0x33, 0x5a, 0x30, 0x45, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
+ 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
+ 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
+ 0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30,
+ 0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3, 0xc3, 0x84,
+ 0x27, 0x95, 0xff, 0x12, 0x31, 0x52, 0x0f, 0x15,
+ 0xef, 0x46, 0x11, 0xc4, 0xad, 0x80, 0xe6, 0x36,
+ 0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61, 0x8d, 0xe0,
+ 0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe, 0x55, 0x66,
+ 0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a, 0xfe, 0xa8,
+ 0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff, 0xee, 0xd7,
+ 0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f, 0xff, 0x2a,
+ 0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, 0xa4, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a, 0x63, 0xb5,
+ 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22, 0x7c, 0x23,
+ 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b, 0x30, 0x75,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x6e, 0x30,
+ 0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97, 0x9a, 0x63,
+ 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22, 0x7c,
+ 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b, 0xa1,
+ 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
+ 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+ 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+ 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
+ 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+ 0x74, 0x64, 0x82, 0x09, 0x00, 0xb1, 0x35, 0x13,
+ 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30, 0x0c, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x03, 0x41, 0x00, 0x85, 0x36, 0x40,
+ 0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4, 0x59, 0x9f,
+ 0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74, 0xec, 0x83,
+ 0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf, 0x39, 0xac,
+ 0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46, 0x1d, 0x99,
+ 0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b, 0x05, 0x08,
+ 0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92, 0xbb, 0x77,
+ 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8, 0x5e, 0x9c,
+ 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16, 0x03, 0x01,
+ 0x00, 0x8b, 0x0c, 0x00, 0x00, 0x87, 0x03, 0x00,
+ 0x17, 0x41, 0x04, 0x1c, 0x8f, 0x9c, 0x6d, 0xe7,
+ 0xab, 0x3e, 0xf8, 0x0a, 0x5d, 0xe1, 0x86, 0xb4,
+ 0xe2, 0x8e, 0xb2, 0x1c, 0x3b, 0xd9, 0xb6, 0x08,
+ 0x80, 0x58, 0x21, 0xe9, 0x0e, 0xc6, 0x66, 0x67,
+ 0x97, 0xcb, 0xb9, 0x92, 0x07, 0x00, 0xc4, 0xe5,
+ 0xec, 0x5f, 0xb4, 0xe2, 0x20, 0xa9, 0xc9, 0x62,
+ 0xd0, 0x98, 0xd5, 0xe3, 0x53, 0xff, 0xd0, 0x0a,
+ 0x6e, 0x29, 0x69, 0x39, 0x2a, 0x4b, 0x5c, 0xd8,
+ 0x6c, 0xf5, 0xfe, 0x00, 0x40, 0x35, 0xa7, 0x26,
+ 0x2e, 0xc2, 0x48, 0x93, 0x32, 0xf7, 0x7d, 0x0f,
+ 0x0d, 0x77, 0x56, 0x9a, 0x85, 0x0c, 0xa6, 0x74,
+ 0x06, 0xb8, 0x3d, 0x90, 0x56, 0x12, 0x63, 0xff,
+ 0x00, 0x5e, 0x0f, 0xf7, 0x24, 0xf7, 0xdb, 0x48,
+ 0x71, 0xe9, 0x2e, 0x03, 0xd3, 0xfa, 0x3a, 0xae,
+ 0xa0, 0xc1, 0x77, 0x3c, 0x4c, 0x59, 0xce, 0x33,
+ 0x1a, 0xd2, 0x47, 0x83, 0xfa, 0xea, 0xd8, 0x1e,
+ 0x06, 0xe7, 0x7d, 0xa0, 0x9b, 0x16, 0x03, 0x01,
+ 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x46, 0x10, 0x00, 0x00,
+ 0x42, 0x41, 0x04, 0x1e, 0x18, 0x37, 0xef, 0x0d,
+ 0x19, 0x51, 0x88, 0x35, 0x75, 0x71, 0xb5, 0xe5,
+ 0x54, 0x5b, 0x12, 0x2e, 0x8f, 0x09, 0x67, 0xfd,
+ 0xa7, 0x24, 0x20, 0x3e, 0xb2, 0x56, 0x1c, 0xce,
+ 0x97, 0x28, 0x5e, 0xf8, 0x2b, 0x2d, 0x4f, 0x9e,
+ 0xf1, 0x07, 0x9f, 0x6c, 0x4b, 0x5b, 0x83, 0x56,
+ 0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49,
+ 0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b,
+ 0xdc, 0x5a, 0x89, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0xd9, 0xa7,
+ 0x80, 0x56, 0x3f, 0xa3, 0x8f, 0x96, 0x72, 0x4e,
+ 0x4e, 0x6e, 0x23, 0x41, 0x8f, 0xda, 0x91, 0xb2,
+ 0x9e, 0x63, 0x23, 0x82, 0x64, 0xcd, 0x07, 0x24,
+ 0xd3, 0x40, 0x20, 0x22, 0x4c, 0xe3, 0xff, 0x38,
+ 0xbb, 0x43, 0x9d, 0x57, 0x11, 0xd5, 0x46, 0xa5,
+ 0x05, 0x29, 0x92, 0x02, 0xce, 0xdf,
+ },
+ {
+ 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x01, 0x00, 0x90, 0xe7, 0xba, 0x0e, 0xb1, 0xda,
+ 0x92, 0xb5, 0x77, 0x56, 0x38, 0xa6, 0x22, 0xc1,
+ 0x72, 0xeb, 0x8a, 0x68, 0x09, 0xb6, 0x74, 0xad,
+ 0xb3, 0x4a, 0xf2, 0xdd, 0x09, 0x9b, 0xc9, 0x4f,
+ 0x84, 0x73, 0x8b, 0xd6, 0x97, 0x50, 0x23, 0x1c,
+ 0xa0, 0xc2, 0x0c, 0x25, 0x18, 0xdd, 0x5e, 0x15,
+ 0x4d, 0xd9, 0xef, 0x4f, 0x6a, 0x43, 0x61, 0x9c,
+ 0x95, 0xde, 0x3c, 0x66, 0xc4, 0xc1, 0x33, 0x56,
+ 0xdd, 0x2f, 0x90, 0xaf, 0x68, 0x5c, 0x9c, 0xa4,
+ 0x90, 0x6d, 0xbf, 0x51, 0x1d, 0x68, 0xcb, 0x81,
+ 0x77, 0x52, 0xa0, 0x93, 0x2a, 0xf8, 0xc7, 0x61,
+ 0x87, 0x76, 0xca, 0x93, 0x9e, 0xd6, 0xee, 0x6f,
+ 0x3f, 0xeb, 0x7d, 0x06, 0xdd, 0x73, 0x4e, 0x27,
+ 0x16, 0x63, 0x92, 0xe4, 0xb2, 0x3f, 0x91, 0x23,
+ 0x21, 0x97, 0x90, 0xce, 0x53, 0xb8, 0xb0, 0x9d,
+ 0xbd, 0xbd, 0x33, 0x84, 0xad, 0x6b, 0x2e, 0x7b,
+ 0xf5, 0xeb, 0x1d, 0x64, 0x37, 0x2e, 0x29, 0x4e,
+ 0xb0, 0x93, 0xdb, 0x92, 0xc7, 0xaa, 0x94, 0xa5,
+ 0x3b, 0x64, 0xd0,
+ },
+ {
+ 0x17, 0x03, 0x01, 0x00, 0x20, 0x11, 0xd8, 0x6b,
+ 0x3c, 0xf6, 0xbe, 0xf4, 0x54, 0x87, 0xec, 0x75,
+ 0x0c, 0x44, 0xdb, 0x92, 0xfc, 0xde, 0x7e, 0x0f,
+ 0x9f, 0x87, 0x87, 0x9c, 0x03, 0xd5, 0x07, 0x84,
+ 0xe0, 0x3a, 0xf8, 0xae, 0x14, 0x17, 0x03, 0x01,
+ 0x00, 0x20, 0xba, 0x54, 0xef, 0x5b, 0xce, 0xfd,
+ 0x47, 0x76, 0x6d, 0xa1, 0x8b, 0xfd, 0x48, 0xde,
+ 0x6e, 0x26, 0xc1, 0x0c, 0x9d, 0x54, 0xbf, 0x98,
+ 0xf6, 0x1c, 0x80, 0xb9, 0xca, 0x93, 0x81, 0x0a,
+ 0x2e, 0x06, 0x15, 0x03, 0x01, 0x00, 0x20, 0x93,
+ 0x3e, 0x38, 0x17, 0xc9, 0x0a, 0xc3, 0xea, 0xd3,
+ 0x92, 0x75, 0xa6, 0x53, 0x37, 0x4d, 0x74, 0x94,
+ 0xbe, 0x01, 0xdc, 0x5c, 0x5a, 0x0f, 0x09, 0xf6,
+ 0x57, 0x33, 0xc3, 0xbc, 0x3f, 0x7a, 0x4d,
+ },
+}
diff --git a/src/pkg/crypto/tls/handshake_messages.go b/src/pkg/crypto/tls/handshake_messages.go
index 54c7a3e63..cdd491707 100644
--- a/src/pkg/crypto/tls/handshake_messages.go
+++ b/src/pkg/crypto/tls/handshake_messages.go
@@ -18,6 +18,8 @@ type clientHelloMsg struct {
ocspStapling bool
supportedCurves []uint16
supportedPoints []uint8
+ ticketSupported bool
+ sessionTicket []uint8
}
func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -36,7 +38,9 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
m.serverName == m1.serverName &&
m.ocspStapling == m1.ocspStapling &&
eqUint16s(m.supportedCurves, m1.supportedCurves) &&
- bytes.Equal(m.supportedPoints, m1.supportedPoints)
+ bytes.Equal(m.supportedPoints, m1.supportedPoints) &&
+ m.ticketSupported == m1.ticketSupported &&
+ bytes.Equal(m.sessionTicket, m1.sessionTicket)
}
func (m *clientHelloMsg) marshal() []byte {
@@ -66,6 +70,10 @@ func (m *clientHelloMsg) marshal() []byte {
extensionsLength += 1 + len(m.supportedPoints)
numExtensions++
}
+ if m.ticketSupported {
+ extensionsLength += len(m.sessionTicket)
+ numExtensions++
+ }
if numExtensions > 0 {
extensionsLength += 4 * numExtensions
length += 2 + extensionsLength
@@ -180,6 +188,17 @@ func (m *clientHelloMsg) marshal() []byte {
z = z[1:]
}
}
+ if m.ticketSupported {
+ // http://tools.ietf.org/html/rfc5077#section-3.2
+ z[0] = byte(extensionSessionTicket >> 8)
+ z[1] = byte(extensionSessionTicket)
+ l := len(m.sessionTicket)
+ z[2] = byte(l >> 8)
+ z[3] = byte(l)
+ z = z[4:]
+ copy(z, m.sessionTicket)
+ z = z[len(m.sessionTicket):]
+ }
m.raw = x
@@ -228,6 +247,8 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
m.nextProtoNeg = false
m.serverName = ""
m.ocspStapling = false
+ m.ticketSupported = false
+ m.sessionTicket = nil
if len(data) == 0 {
// ClientHello is optionally followed by extension data
@@ -311,6 +332,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
}
m.supportedPoints = make([]uint8, l)
copy(m.supportedPoints, data[1:])
+ case extensionSessionTicket:
+ // http://tools.ietf.org/html/rfc5077#section-3.2
+ m.ticketSupported = true
+ m.sessionTicket = data[:length]
}
data = data[length:]
}
@@ -328,6 +353,7 @@ type serverHelloMsg struct {
nextProtoNeg bool
nextProtos []string
ocspStapling bool
+ ticketSupported bool
}
func (m *serverHelloMsg) equal(i interface{}) bool {
@@ -344,7 +370,8 @@ func (m *serverHelloMsg) equal(i interface{}) bool {
m.compressionMethod == m1.compressionMethod &&
m.nextProtoNeg == m1.nextProtoNeg &&
eqStrings(m.nextProtos, m1.nextProtos) &&
- m.ocspStapling == m1.ocspStapling
+ m.ocspStapling == m1.ocspStapling &&
+ m.ticketSupported == m1.ticketSupported
}
func (m *serverHelloMsg) marshal() []byte {
@@ -368,6 +395,9 @@ func (m *serverHelloMsg) marshal() []byte {
if m.ocspStapling {
numExtensions++
}
+ if m.ticketSupported {
+ numExtensions++
+ }
if numExtensions > 0 {
extensionsLength += 4 * numExtensions
length += 2 + extensionsLength
@@ -416,6 +446,11 @@ func (m *serverHelloMsg) marshal() []byte {
z[1] = byte(extensionStatusRequest)
z = z[4:]
}
+ if m.ticketSupported {
+ z[0] = byte(extensionSessionTicket >> 8)
+ z[1] = byte(extensionSessionTicket)
+ z = z[4:]
+ }
m.raw = x
@@ -445,6 +480,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
m.nextProtoNeg = false
m.nextProtos = nil
m.ocspStapling = false
+ m.ticketSupported = false
if len(data) == 0 {
// ServerHello is optionally followed by extension data
@@ -474,14 +510,14 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
switch extension {
case extensionNextProtoNeg:
m.nextProtoNeg = true
- d := data
+ d := data[:length]
for len(d) > 0 {
l := int(d[0])
d = d[1:]
if l == 0 || l > len(d) {
return false
}
- m.nextProtos = append(m.nextProtos, string(d[0:l]))
+ m.nextProtos = append(m.nextProtos, string(d[:l]))
d = d[l:]
}
case extensionStatusRequest:
@@ -489,6 +525,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
return false
}
m.ocspStapling = true
+ case extensionSessionTicket:
+ if length > 0 {
+ return false
+ }
+ m.ticketSupported = true
}
data = data[length:]
}
@@ -1030,6 +1071,65 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
return true
}
+type newSessionTicketMsg struct {
+ raw []byte
+ ticket []byte
+}
+
+func (m *newSessionTicketMsg) equal(i interface{}) bool {
+ m1, ok := i.(*newSessionTicketMsg)
+ if !ok {
+ return false
+ }
+
+ return bytes.Equal(m.raw, m1.raw) &&
+ bytes.Equal(m.ticket, m1.ticket)
+}
+
+func (m *newSessionTicketMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ // See http://tools.ietf.org/html/rfc5077#section-3.3
+ ticketLen := len(m.ticket)
+ length := 2 + 4 + ticketLen
+ x = make([]byte, 4+length)
+ x[0] = typeNewSessionTicket
+ x[1] = uint8(length >> 16)
+ x[2] = uint8(length >> 8)
+ x[3] = uint8(length)
+ x[8] = uint8(ticketLen >> 8)
+ x[9] = uint8(ticketLen)
+ copy(x[10:], m.ticket)
+
+ m.raw = x
+
+ return
+}
+
+func (m *newSessionTicketMsg) unmarshal(data []byte) bool {
+ m.raw = data
+
+ if len(data) < 10 {
+ return false
+ }
+
+ length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
+ if uint32(len(data))-4 != length {
+ return false
+ }
+
+ ticketLen := int(data[8])<<8 + int(data[9])
+ if len(data)-10 != ticketLen {
+ return false
+ }
+
+ m.ticket = data[10:]
+
+ return true
+}
+
func eqUint16s(x, y []uint16) bool {
if len(x) != len(y) {
return false
diff --git a/src/pkg/crypto/tls/handshake_messages_test.go b/src/pkg/crypto/tls/handshake_messages_test.go
index e62a9d581..3434bad9f 100644
--- a/src/pkg/crypto/tls/handshake_messages_test.go
+++ b/src/pkg/crypto/tls/handshake_messages_test.go
@@ -22,6 +22,8 @@ var tests = []interface{}{
&certificateStatusMsg{},
&clientKeyExchangeMsg{},
&nextProtoMsg{},
+ &newSessionTicketMsg{},
+ &sessionState{},
}
type testMessage interface {
@@ -127,6 +129,12 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
for i := range m.supportedCurves {
m.supportedCurves[i] = uint16(rand.Intn(30000))
}
+ if rand.Intn(10) > 5 {
+ m.ticketSupported = true
+ if rand.Intn(10) > 5 {
+ m.sessionTicket = randomBytes(rand.Intn(300), rand)
+ }
+ }
return reflect.ValueOf(m)
}
@@ -149,6 +157,13 @@ func (*serverHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
}
}
+ if rand.Intn(10) > 5 {
+ m.ocspStapling = true
+ }
+ if rand.Intn(10) > 5 {
+ m.ticketSupported = true
+ }
+
return reflect.ValueOf(m)
}
@@ -207,3 +222,22 @@ func (*nextProtoMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m.proto = randomString(rand.Intn(255), rand)
return reflect.ValueOf(m)
}
+
+func (*newSessionTicketMsg) Generate(rand *rand.Rand, size int) reflect.Value {
+ m := &newSessionTicketMsg{}
+ m.ticket = randomBytes(rand.Intn(4), rand)
+ return reflect.ValueOf(m)
+}
+
+func (*sessionState) Generate(rand *rand.Rand, size int) reflect.Value {
+ s := &sessionState{}
+ s.vers = uint16(rand.Intn(10000))
+ s.cipherSuite = uint16(rand.Intn(10000))
+ s.masterSecret = randomBytes(rand.Intn(100), rand)
+ numCerts := rand.Intn(20)
+ s.certificates = make([][]byte, numCerts)
+ for i := 0; i < numCerts; i++ {
+ s.certificates[i] = randomBytes(rand.Intn(10)+1, rand)
+ }
+ return reflect.ValueOf(s)
+}
diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go
index 76adc540c..730991016 100644
--- a/src/pkg/crypto/tls/handshake_server.go
+++ b/src/pkg/crypto/tls/handshake_server.go
@@ -13,31 +13,120 @@ import (
"io"
)
+// serverHandshakeState contains details of a server handshake in progress.
+// It's discarded once the handshake has completed.
+type serverHandshakeState struct {
+ c *Conn
+ clientHello *clientHelloMsg
+ hello *serverHelloMsg
+ suite *cipherSuite
+ ellipticOk bool
+ sessionState *sessionState
+ finishedHash finishedHash
+ masterSecret []byte
+ certsFromClient [][]byte
+}
+
+// serverHandshake performs a TLS handshake as a server.
func (c *Conn) serverHandshake() error {
config := c.config
- msg, err := c.readHandshake()
+
+ // If this is the first server handshake, we generate a random key to
+ // encrypt the tickets with.
+ config.serverInitOnce.Do(func() {
+ if config.SessionTicketsDisabled {
+ return
+ }
+
+ // If the key has already been set then we have nothing to do.
+ for _, b := range config.SessionTicketKey {
+ if b != 0 {
+ return
+ }
+ }
+
+ if _, err := io.ReadFull(config.rand(), config.SessionTicketKey[:]); err != nil {
+ config.SessionTicketsDisabled = true
+ }
+ })
+
+ hs := serverHandshakeState{
+ c: c,
+ }
+ isResume, err := hs.readClientHello()
if err != nil {
return err
}
- clientHello, ok := msg.(*clientHelloMsg)
+
+ // For an overview of TLS handshaking, see https://tools.ietf.org/html/rfc5246#section-7.3
+ if isResume {
+ // The client has included a session ticket and so we do an abbreviated handshake.
+ if err := hs.doResumeHandshake(); err != nil {
+ return err
+ }
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.sendFinished(); err != nil {
+ return err
+ }
+ if err := hs.readFinished(); err != nil {
+ return err
+ }
+ c.didResume = true
+ } else {
+ // The client didn't include a session ticket, or it wasn't
+ // valid so we do a full handshake.
+ if err := hs.doFullHandshake(); err != nil {
+ return err
+ }
+ if err := hs.establishKeys(); err != nil {
+ return err
+ }
+ if err := hs.readFinished(); err != nil {
+ return err
+ }
+ if err := hs.sendSessionTicket(); err != nil {
+ return err
+ }
+ if err := hs.sendFinished(); err != nil {
+ return err
+ }
+ }
+ c.handshakeComplete = true
+
+ return nil
+}
+
+// readClientHello reads a ClientHello message from the client and decides
+// whether we will perform session resumption.
+func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
+ config := hs.c.config
+ c := hs.c
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return false, err
+ }
+ var ok bool
+ hs.clientHello, ok = msg.(*clientHelloMsg)
if !ok {
- return c.sendAlert(alertUnexpectedMessage)
+ return false, c.sendAlert(alertUnexpectedMessage)
}
- vers, ok := mutualVersion(clientHello.vers)
+ c.vers, ok = mutualVersion(hs.clientHello.vers)
if !ok {
- return c.sendAlert(alertProtocolVersion)
+ return false, c.sendAlert(alertProtocolVersion)
}
- c.vers = vers
c.haveVers = true
- finishedHash := newFinishedHash(vers)
- finishedHash.Write(clientHello.marshal())
+ hs.finishedHash = newFinishedHash(c.vers)
+ hs.finishedHash.Write(hs.clientHello.marshal())
- hello := new(serverHelloMsg)
+ hs.hello = new(serverHelloMsg)
supportedCurve := false
Curves:
- for _, curve := range clientHello.supportedCurves {
+ for _, curve := range hs.clientHello.supportedCurves {
switch curve {
case curveP256, curveP384, curveP521:
supportedCurve = true
@@ -46,110 +135,182 @@ Curves:
}
supportedPointFormat := false
- for _, pointFormat := range clientHello.supportedPoints {
+ for _, pointFormat := range hs.clientHello.supportedPoints {
if pointFormat == pointFormatUncompressed {
supportedPointFormat = true
break
}
}
-
- ellipticOk := supportedCurve && supportedPointFormat
-
- var suite *cipherSuite
-FindCipherSuite:
- for _, id := range clientHello.cipherSuites {
- for _, supported := range config.cipherSuites() {
- if id == supported {
- var candidate *cipherSuite
-
- for _, s := range cipherSuites {
- if s.id == id {
- candidate = s
- break
- }
- }
- if candidate == nil {
- continue
- }
- // Don't select a ciphersuite which we can't
- // support for this client.
- if candidate.elliptic && !ellipticOk {
- continue
- }
- suite = candidate
- break FindCipherSuite
- }
- }
- }
+ hs.ellipticOk = supportedCurve && supportedPointFormat
foundCompression := false
// We only support null compression, so check that the client offered it.
- for _, compression := range clientHello.compressionMethods {
+ for _, compression := range hs.clientHello.compressionMethods {
if compression == compressionNone {
foundCompression = true
break
}
}
- if suite == nil || !foundCompression {
- return c.sendAlert(alertHandshakeFailure)
+ if !foundCompression {
+ return false, c.sendAlert(alertHandshakeFailure)
}
- hello.vers = vers
- hello.cipherSuite = suite.id
+ hs.hello.vers = c.vers
t := uint32(config.time().Unix())
- hello.random = make([]byte, 32)
- hello.random[0] = byte(t >> 24)
- hello.random[1] = byte(t >> 16)
- hello.random[2] = byte(t >> 8)
- hello.random[3] = byte(t)
- _, err = io.ReadFull(config.rand(), hello.random[4:])
+ hs.hello.random = make([]byte, 32)
+ hs.hello.random[0] = byte(t >> 24)
+ hs.hello.random[1] = byte(t >> 16)
+ hs.hello.random[2] = byte(t >> 8)
+ hs.hello.random[3] = byte(t)
+ _, err = io.ReadFull(config.rand(), hs.hello.random[4:])
if err != nil {
- return c.sendAlert(alertInternalError)
+ return false, c.sendAlert(alertInternalError)
+ }
+ hs.hello.compressionMethod = compressionNone
+ if len(hs.clientHello.serverName) > 0 {
+ c.serverName = hs.clientHello.serverName
+ }
+ if hs.clientHello.nextProtoNeg {
+ hs.hello.nextProtoNeg = true
+ hs.hello.nextProtos = config.NextProtos
+ }
+
+ if hs.checkForResumption() {
+ return true, nil
+ }
+
+ var preferenceList, supportedList []uint16
+ if c.config.PreferServerCipherSuites {
+ preferenceList = c.config.cipherSuites()
+ supportedList = hs.clientHello.cipherSuites
+ } else {
+ preferenceList = hs.clientHello.cipherSuites
+ supportedList = c.config.cipherSuites()
+ }
+
+ for _, id := range preferenceList {
+ if hs.suite = c.tryCipherSuite(id, supportedList, hs.ellipticOk); hs.suite != nil {
+ break
+ }
+ }
+
+ if hs.suite == nil {
+ return false, c.sendAlert(alertHandshakeFailure)
+ }
+
+ return false, nil
+}
+
+// checkForResumption returns true if we should perform resumption on this connection.
+func (hs *serverHandshakeState) checkForResumption() bool {
+ c := hs.c
+
+ var ok bool
+ if hs.sessionState, ok = c.decryptTicket(hs.clientHello.sessionTicket); !ok {
+ return false
}
- hello.compressionMethod = compressionNone
- if clientHello.nextProtoNeg {
- hello.nextProtoNeg = true
- hello.nextProtos = config.NextProtos
+
+ if hs.sessionState.vers > hs.clientHello.vers {
+ return false
+ }
+ if vers, ok := mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers {
+ return false
+ }
+
+ cipherSuiteOk := false
+ // Check that the client is still offering the ciphersuite in the session.
+ for _, id := range hs.clientHello.cipherSuites {
+ if id == hs.sessionState.cipherSuite {
+ cipherSuiteOk = true
+ break
+ }
+ }
+ if !cipherSuiteOk {
+ return false
+ }
+
+ // Check that we also support the ciphersuite from the session.
+ hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.ellipticOk)
+ if hs.suite == nil {
+ return false
+ }
+
+ sessionHasClientCerts := len(hs.sessionState.certificates) != 0
+ needClientCerts := c.config.ClientAuth == RequireAnyClientCert || c.config.ClientAuth == RequireAndVerifyClientCert
+ if needClientCerts && !sessionHasClientCerts {
+ return false
+ }
+ if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
+ return false
}
+ return true
+}
+
+func (hs *serverHandshakeState) doResumeHandshake() error {
+ c := hs.c
+
+ hs.hello.cipherSuite = hs.suite.id
+ // We echo the client's session ID in the ServerHello to let it know
+ // that we're doing a resumption.
+ hs.hello.sessionId = hs.clientHello.sessionId
+ hs.finishedHash.Write(hs.hello.marshal())
+ c.writeRecord(recordTypeHandshake, hs.hello.marshal())
+
+ if len(hs.sessionState.certificates) > 0 {
+ if _, err := hs.processCertsFromClient(hs.sessionState.certificates); err != nil {
+ return err
+ }
+ }
+
+ hs.masterSecret = hs.sessionState.masterSecret
+
+ return nil
+}
+
+func (hs *serverHandshakeState) doFullHandshake() error {
+ config := hs.c.config
+ c := hs.c
+
if len(config.Certificates) == 0 {
return c.sendAlert(alertInternalError)
}
cert := &config.Certificates[0]
- if len(clientHello.serverName) > 0 {
- c.serverName = clientHello.serverName
- cert = config.getCertificateForName(clientHello.serverName)
+ if len(hs.clientHello.serverName) > 0 {
+ cert = config.getCertificateForName(hs.clientHello.serverName)
}
- if clientHello.ocspStapling && len(cert.OCSPStaple) > 0 {
- hello.ocspStapling = true
+ if hs.clientHello.ocspStapling && len(cert.OCSPStaple) > 0 {
+ hs.hello.ocspStapling = true
}
- finishedHash.Write(hello.marshal())
- c.writeRecord(recordTypeHandshake, hello.marshal())
+ hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled
+ hs.hello.cipherSuite = hs.suite.id
+ hs.finishedHash.Write(hs.hello.marshal())
+ c.writeRecord(recordTypeHandshake, hs.hello.marshal())
certMsg := new(certificateMsg)
certMsg.certificates = cert.Certificate
- finishedHash.Write(certMsg.marshal())
+ hs.finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal())
- if hello.ocspStapling {
+ if hs.hello.ocspStapling {
certStatus := new(certificateStatusMsg)
certStatus.statusType = statusTypeOCSP
certStatus.response = cert.OCSPStaple
- finishedHash.Write(certStatus.marshal())
+ hs.finishedHash.Write(certStatus.marshal())
c.writeRecord(recordTypeHandshake, certStatus.marshal())
}
- keyAgreement := suite.ka()
- skx, err := keyAgreement.generateServerKeyExchange(config, cert, clientHello, hello)
+ keyAgreement := hs.suite.ka()
+ skx, err := keyAgreement.generateServerKeyExchange(config, cert, hs.clientHello, hs.hello)
if err != nil {
c.sendAlert(alertHandshakeFailure)
return err
}
if skx != nil {
- finishedHash.Write(skx.marshal())
+ hs.finishedHash.Write(skx.marshal())
c.writeRecord(recordTypeHandshake, skx.marshal())
}
@@ -166,28 +327,29 @@ FindCipherSuite:
if config.ClientCAs != nil {
certReq.certificateAuthorities = config.ClientCAs.Subjects()
}
- finishedHash.Write(certReq.marshal())
+ hs.finishedHash.Write(certReq.marshal())
c.writeRecord(recordTypeHandshake, certReq.marshal())
}
helloDone := new(serverHelloDoneMsg)
- finishedHash.Write(helloDone.marshal())
+ hs.finishedHash.Write(helloDone.marshal())
c.writeRecord(recordTypeHandshake, helloDone.marshal())
var pub *rsa.PublicKey // public key for client auth, if any
- msg, err = c.readHandshake()
+ msg, err := c.readHandshake()
if err != nil {
return err
}
+ var ok bool
// If we requested a client certificate, then the client must send a
// certificate message, even if it's empty.
if config.ClientAuth >= RequestClientCert {
if certMsg, ok = msg.(*certificateMsg); !ok {
return c.sendAlert(alertHandshakeFailure)
}
- finishedHash.Write(certMsg.marshal())
+ hs.finishedHash.Write(certMsg.marshal())
if len(certMsg.certificates) == 0 {
// The client didn't actually send a certificate
@@ -198,54 +360,9 @@ FindCipherSuite:
}
}
- certs := make([]*x509.Certificate, len(certMsg.certificates))
- for i, asn1Data := range certMsg.certificates {
- if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
- c.sendAlert(alertBadCertificate)
- return errors.New("tls: failed to parse client certificate: " + err.Error())
- }
- }
-
- if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
- opts := x509.VerifyOptions{
- Roots: c.config.ClientCAs,
- CurrentTime: c.config.time(),
- Intermediates: x509.NewCertPool(),
- }
-
- for i, cert := range certs {
- if i == 0 {
- continue
- }
- opts.Intermediates.AddCert(cert)
- }
-
- chains, err := certs[0].Verify(opts)
- if err != nil {
- c.sendAlert(alertBadCertificate)
- return errors.New("tls: failed to verify client's certificate: " + err.Error())
- }
-
- ok := false
- for _, ku := range certs[0].ExtKeyUsage {
- if ku == x509.ExtKeyUsageClientAuth {
- ok = true
- break
- }
- }
- if !ok {
- c.sendAlert(alertHandshakeFailure)
- return errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication")
- }
-
- c.verifiedChains = chains
- }
-
- if len(certs) > 0 {
- if pub, ok = certs[0].PublicKey.(*rsa.PublicKey); !ok {
- return c.sendAlert(alertUnsupportedCertificate)
- }
- c.peerCertificates = certs
+ pub, err = hs.processCertsFromClient(certMsg.certificates)
+ if err != nil {
+ return err
}
msg, err = c.readHandshake()
@@ -259,7 +376,7 @@ FindCipherSuite:
if !ok {
return c.sendAlert(alertUnexpectedMessage)
}
- finishedHash.Write(ckx.marshal())
+ hs.finishedHash.Write(ckx.marshal())
// If we received a client cert in response to our certificate request message,
// the client will send us a certificateVerifyMsg immediately after the
@@ -278,15 +395,15 @@ FindCipherSuite:
}
digest := make([]byte, 0, 36)
- digest = finishedHash.serverMD5.Sum(digest)
- digest = finishedHash.serverSHA1.Sum(digest)
+ digest = hs.finishedHash.serverMD5.Sum(digest)
+ digest = hs.finishedHash.serverSHA1.Sum(digest)
err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature)
if err != nil {
c.sendAlert(alertBadCertificate)
return errors.New("could not validate signature of connection nonces: " + err.Error())
}
- finishedHash.Write(certVerify.marshal())
+ hs.finishedHash.Write(certVerify.marshal())
}
preMasterSecret, err := keyAgreement.processClientKeyExchange(config, cert, ckx, c.vers)
@@ -294,20 +411,38 @@ FindCipherSuite:
c.sendAlert(alertHandshakeFailure)
return err
}
+ hs.masterSecret = masterFromPreMasterSecret(c.vers, preMasterSecret, hs.clientHello.random, hs.hello.random)
+
+ return nil
+}
+
+func (hs *serverHandshakeState) establishKeys() error {
+ c := hs.c
- masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
- keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen)
+ clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
+ keysFromMasterSecret(c.vers, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
- clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */)
- clientHash := suite.mac(c.vers, clientMAC)
+ clientCipher := hs.suite.cipher(clientKey, clientIV, true /* for reading */)
+ clientHash := hs.suite.mac(c.vers, clientMAC)
c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
+
+ serverCipher := hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
+ serverHash := hs.suite.mac(c.vers, serverMAC)
+ c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
+
+ return nil
+}
+
+func (hs *serverHandshakeState) readFinished() error {
+ c := hs.c
+
c.readRecord(recordTypeChangeCipherSpec)
if err := c.error(); err != nil {
return err
}
- if hello.nextProtoNeg {
- msg, err = c.readHandshake()
+ if hs.hello.nextProtoNeg {
+ msg, err := c.readHandshake()
if err != nil {
return err
}
@@ -315,11 +450,11 @@ FindCipherSuite:
if !ok {
return c.sendAlert(alertUnexpectedMessage)
}
- finishedHash.Write(nextProto.marshal())
+ hs.finishedHash.Write(nextProto.marshal())
c.clientProtocol = nextProto.proto
}
- msg, err = c.readHandshake()
+ msg, err := c.readHandshake()
if err != nil {
return err
}
@@ -328,25 +463,142 @@ FindCipherSuite:
return c.sendAlert(alertUnexpectedMessage)
}
- verify := finishedHash.clientSum(masterSecret)
+ verify := hs.finishedHash.clientSum(hs.masterSecret)
if len(verify) != len(clientFinished.verifyData) ||
subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
return c.sendAlert(alertHandshakeFailure)
}
- finishedHash.Write(clientFinished.marshal())
+ hs.finishedHash.Write(clientFinished.marshal())
+ return nil
+}
+
+func (hs *serverHandshakeState) sendSessionTicket() error {
+ if !hs.hello.ticketSupported {
+ return nil
+ }
+
+ c := hs.c
+ m := new(newSessionTicketMsg)
+
+ var err error
+ state := sessionState{
+ vers: c.vers,
+ cipherSuite: hs.suite.id,
+ masterSecret: hs.masterSecret,
+ certificates: hs.certsFromClient,
+ }
+ m.ticket, err = c.encryptTicket(&state)
+ if err != nil {
+ return err
+ }
+
+ hs.finishedHash.Write(m.marshal())
+ c.writeRecord(recordTypeHandshake, m.marshal())
+
+ return nil
+}
+
+func (hs *serverHandshakeState) sendFinished() error {
+ c := hs.c
- serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */)
- serverHash := suite.mac(c.vers, serverMAC)
- c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
finished := new(finishedMsg)
- finished.verifyData = finishedHash.serverSum(masterSecret)
+ finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
+ hs.finishedHash.Write(finished.marshal())
c.writeRecord(recordTypeHandshake, finished.marshal())
- c.handshakeComplete = true
- c.cipherSuite = suite.id
+ c.cipherSuite = hs.suite.id
+
+ return nil
+}
+
+// processCertsFromClient takes a chain of client certificates either from a
+// Certificates message or from a sessionState and verifies them. It returns
+// the public key of the leaf certificate.
+func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*rsa.PublicKey, error) {
+ c := hs.c
+
+ hs.certsFromClient = certificates
+ certs := make([]*x509.Certificate, len(certificates))
+ var err error
+ for i, asn1Data := range certificates {
+ if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
+ c.sendAlert(alertBadCertificate)
+ return nil, errors.New("tls: failed to parse client certificate: " + err.Error())
+ }
+ }
+
+ if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
+ opts := x509.VerifyOptions{
+ Roots: c.config.ClientCAs,
+ CurrentTime: c.config.time(),
+ Intermediates: x509.NewCertPool(),
+ KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
+ }
+
+ for _, cert := range certs[1:] {
+ opts.Intermediates.AddCert(cert)
+ }
+
+ chains, err := certs[0].Verify(opts)
+ if err != nil {
+ c.sendAlert(alertBadCertificate)
+ return nil, errors.New("tls: failed to verify client's certificate: " + err.Error())
+ }
+
+ ok := false
+ for _, ku := range certs[0].ExtKeyUsage {
+ if ku == x509.ExtKeyUsageClientAuth {
+ ok = true
+ break
+ }
+ }
+ if !ok {
+ c.sendAlert(alertHandshakeFailure)
+ return nil, errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication")
+ }
+
+ c.verifiedChains = chains
+ }
+
+ if len(certs) > 0 {
+ pub, ok := certs[0].PublicKey.(*rsa.PublicKey)
+ if !ok {
+ return nil, c.sendAlert(alertUnsupportedCertificate)
+ }
+ c.peerCertificates = certs
+ return pub, nil
+ }
+
+ return nil, nil
+}
+
+// tryCipherSuite returns a cipherSuite with the given id if that cipher suite
+// is acceptable to use.
+func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipticOk bool) *cipherSuite {
+ for _, supported := range supportedCipherSuites {
+ if id == supported {
+ var candidate *cipherSuite
+
+ for _, s := range cipherSuites {
+ if s.id == id {
+ candidate = s
+ break
+ }
+ }
+ if candidate == nil {
+ continue
+ }
+ // Don't select a ciphersuite which we can't
+ // support for this client.
+ if candidate.elliptic && !ellipticOk {
+ continue
+ }
+ return candidate
+ }
+ }
return nil
}
diff --git a/src/pkg/crypto/tls/handshake_server_test.go b/src/pkg/crypto/tls/handshake_server_test.go
index 7c1267101..bf8cbe3ae 100644
--- a/src/pkg/crypto/tls/handshake_server_test.go
+++ b/src/pkg/crypto/tls/handshake_server_test.go
@@ -11,12 +11,15 @@ import (
"encoding/hex"
"encoding/pem"
"flag"
+ "fmt"
"io"
"log"
"math/big"
"net"
+ "os"
"strconv"
"strings"
+ "sync"
"testing"
"time"
)
@@ -80,13 +83,20 @@ func TestRejectBadProtocolVersion(t *testing.T) {
}
func TestNoSuiteOverlap(t *testing.T) {
- clientHello := &clientHelloMsg{nil, 0x0301, nil, nil, []uint16{0xff00}, []uint8{0}, false, "", false, nil, nil}
+ clientHello := &clientHelloMsg{
+ vers: 0x0301,
+ cipherSuites: []uint16{0xff00},
+ compressionMethods: []uint8{0},
+ }
testClientHelloFailure(t, clientHello, alertHandshakeFailure)
-
}
func TestNoCompressionOverlap(t *testing.T) {
- clientHello := &clientHelloMsg{nil, 0x0301, nil, nil, []uint16{TLS_RSA_WITH_RC4_128_SHA}, []uint8{0xff}, false, "", false, nil, nil}
+ clientHello := &clientHelloMsg{
+ vers: 0x0301,
+ cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
+ compressionMethods: []uint8{0xff},
+ }
testClientHelloFailure(t, clientHello, alertHandshakeFailure)
}
@@ -115,6 +125,50 @@ func TestClose(t *testing.T) {
}
}
+func testHandshake(clientConfig, serverConfig *Config) (state ConnectionState, err error) {
+ c, s := net.Pipe()
+ go func() {
+ cli := Client(c, clientConfig)
+ cli.Handshake()
+ c.Close()
+ }()
+ server := Server(s, serverConfig)
+ err = server.Handshake()
+ if err == nil {
+ state = server.ConnectionState()
+ }
+ s.Close()
+ return
+}
+
+func TestCipherSuitePreference(t *testing.T) {
+ serverConfig := &Config{
+ CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
+ Certificates: testConfig.Certificates,
+ }
+ clientConfig := &Config{
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_RC4_128_SHA},
+ InsecureSkipVerify: true,
+ }
+ state, err := testHandshake(clientConfig, serverConfig)
+ if err != nil {
+ t.Fatalf("handshake failed: %s", err)
+ }
+ if state.CipherSuite != TLS_RSA_WITH_AES_128_CBC_SHA {
+ // By default the server should use the client's preference.
+ t.Fatalf("Client's preference was not used, got %x", state.CipherSuite)
+ }
+
+ serverConfig.PreferServerCipherSuites = true
+ state, err = testHandshake(clientConfig, serverConfig)
+ if err != nil {
+ t.Fatalf("handshake failed: %s", err)
+ }
+ if state.CipherSuite != TLS_RSA_WITH_RC4_128_SHA {
+ t.Fatalf("Server's preference was not used, got %x", state.CipherSuite)
+ }
+}
+
func testServerScript(t *testing.T, name string, serverScript [][]byte, config *Config, peers []*x509.Certificate) {
c, s := net.Pipe()
srv := Server(s, config)
@@ -186,6 +240,11 @@ func TestHandshakeServerSNI(t *testing.T) {
testServerScript(t, "SNI", selectCertificateBySNIScript, testConfig, nil)
}
+func TestResumption(t *testing.T) {
+ testServerScript(t, "IssueTicket", issueSessionTicketTest, testConfig, nil)
+ testServerScript(t, "Resume", serverResumeTest, testConfig, nil)
+}
+
type clientauthTest struct {
name string
clientauth ClientAuthType
@@ -203,55 +262,133 @@ func TestClientAuth(t *testing.T) {
}
}
+// recordingConn is a net.Conn that records the traffic that passes through it.
+// WriteTo can be used to produce Go code that contains the recorded traffic.
+type recordingConn struct {
+ net.Conn
+ lock sync.Mutex
+ flows [][]byte
+ currentlyReading bool
+}
+
+func (r *recordingConn) Read(b []byte) (n int, err error) {
+ if n, err = r.Conn.Read(b); n == 0 {
+ return
+ }
+ b = b[:n]
+
+ r.lock.Lock()
+ defer r.lock.Unlock()
+
+ if l := len(r.flows); l == 0 || !r.currentlyReading {
+ buf := make([]byte, len(b))
+ copy(buf, b)
+ r.flows = append(r.flows, buf)
+ } else {
+ r.flows[l-1] = append(r.flows[l-1], b[:n]...)
+ }
+ r.currentlyReading = true
+ return
+}
+
+func (r *recordingConn) Write(b []byte) (n int, err error) {
+ if n, err = r.Conn.Write(b); n == 0 {
+ return
+ }
+ b = b[:n]
+
+ r.lock.Lock()
+ defer r.lock.Unlock()
+
+ if l := len(r.flows); l == 0 || r.currentlyReading {
+ buf := make([]byte, len(b))
+ copy(buf, b)
+ r.flows = append(r.flows, buf)
+ } else {
+ r.flows[l-1] = append(r.flows[l-1], b[:n]...)
+ }
+ r.currentlyReading = false
+ return
+}
+
+// WriteTo writes Go source code to w that contains the recorded traffic.
+func (r *recordingConn) WriteTo(w io.Writer) {
+ fmt.Fprintf(w, "var changeMe = [][]byte {\n")
+ for _, buf := range r.flows {
+ fmt.Fprintf(w, "\t{")
+ for i, b := range buf {
+ if i%8 == 0 {
+ fmt.Fprintf(w, "\n\t\t")
+ }
+ fmt.Fprintf(w, "0x%02x, ", b)
+ }
+ fmt.Fprintf(w, "\n\t},\n")
+ }
+ fmt.Fprintf(w, "}\n")
+}
+
var serve = flag.Bool("serve", false, "run a TLS server on :10443")
var testCipherSuites = flag.String("ciphersuites",
"0x"+strconv.FormatInt(int64(TLS_RSA_WITH_RC4_128_SHA), 16),
"cipher suites to accept in serving mode")
var testClientAuth = flag.Int("clientauth", 0, "value for tls.Config.ClientAuth")
-func TestRunServer(t *testing.T) {
- if !*serve {
- return
- }
-
+func GetTestConfig() *Config {
+ var config = *testConfig
suites := strings.Split(*testCipherSuites, ",")
- testConfig.CipherSuites = make([]uint16, len(suites))
+ config.CipherSuites = make([]uint16, len(suites))
for i := range suites {
suite, err := strconv.ParseUint(suites[i], 0, 64)
if err != nil {
panic(err)
}
- testConfig.CipherSuites[i] = uint16(suite)
+ config.CipherSuites[i] = uint16(suite)
}
- testConfig.ClientAuth = ClientAuthType(*testClientAuth)
+ config.ClientAuth = ClientAuthType(*testClientAuth)
+ return &config
+}
+
+func TestRunServer(t *testing.T) {
+ if !*serve {
+ return
+ }
+
+ config := GetTestConfig()
- l, err := Listen("tcp", ":10443", testConfig)
+ const addr = ":10443"
+ l, err := net.Listen("tcp", addr)
if err != nil {
t.Fatal(err)
}
+ log.Printf("Now listening for connections on %s", addr)
for {
- c, err := l.Accept()
+ tcpConn, err := l.Accept()
if err != nil {
+ log.Printf("error accepting connection: %s", err)
+ break
+ }
+
+ record := &recordingConn{
+ Conn: tcpConn,
+ }
+
+ conn := Server(record, config)
+ if err := conn.Handshake(); err != nil {
log.Printf("error from TLS handshake: %s", err)
break
}
- _, err = c.Write([]byte("hello, world\n"))
+ _, err = conn.Write([]byte("hello, world\n"))
if err != nil {
- log.Printf("error from TLS: %s", err)
+ log.Printf("error from Write: %s", err)
continue
}
- st := c.(*Conn).ConnectionState()
- if len(st.PeerCertificates) > 0 {
- log.Print("Handling request from client ", st.PeerCertificates[0].Subject.CommonName)
- } else {
- log.Print("Handling request from anon client")
- }
+ conn.Close()
- c.Close()
+ record.WriteTo(os.Stdout)
}
}
@@ -296,30 +433,23 @@ func loadPEMCert(in string) *x509.Certificate {
// Script of interaction with gnutls implementation.
// The values for this test are obtained by building and running in server mode:
-// % go test -run "TestRunServer" -serve
-// and then:
-// % gnutls-cli --insecure --debug 100 -p 10443 localhost > /tmp/log 2>&1
-// % python parse-gnutls-cli-debug-log.py < /tmp/log
+// % go test -test.run "TestRunServer" -serve
+// The recorded bytes are written to stdout.
var rc4ServerScript = [][]byte{
{
- 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00,
- 0x76, 0x03, 0x02, 0x4e, 0xdd, 0xe6, 0xa5, 0xf7,
- 0x00, 0x36, 0xf7, 0x83, 0xec, 0x93, 0x7c, 0xd2,
- 0x4d, 0xe7, 0x7b, 0xf5, 0x4c, 0xf7, 0xe3, 0x86,
- 0xe8, 0xec, 0x3b, 0xbd, 0x2c, 0x9a, 0x3f, 0x57,
- 0xf0, 0xa4, 0xd4, 0x00, 0x00, 0x34, 0x00, 0x33,
- 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16,
- 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87,
- 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91,
- 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41,
- 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05,
- 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b,
- 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09,
- 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f,
- 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
+ 0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00,
+ 0x50, 0x03, 0x01, 0x50, 0x77, 0x3d, 0xbd, 0x32,
+ 0x13, 0xd7, 0xea, 0x33, 0x65, 0x02, 0xb8, 0x70,
+ 0xb7, 0x84, 0xc4, 0x05, 0x1f, 0xa4, 0x24, 0xc4,
+ 0x91, 0x69, 0x04, 0x32, 0x96, 0xfe, 0x5b, 0x49,
+ 0x71, 0x60, 0x9a, 0x00, 0x00, 0x28, 0x00, 0x39,
+ 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13,
+ 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f,
+ 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
+ 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,
+ 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01,
+ 0x00,
},
-
{
0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -418,86 +548,145 @@ var rc4ServerScript = [][]byte{
0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e,
0x00, 0x00, 0x00,
},
-
{
0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
- 0x82, 0x00, 0x80, 0x39, 0xe2, 0x0f, 0x49, 0xa0,
- 0xe6, 0xe4, 0x3b, 0x0c, 0x5f, 0xce, 0x39, 0x97,
- 0x6c, 0xb6, 0x41, 0xd9, 0xe1, 0x52, 0x8f, 0x43,
- 0xb3, 0xc6, 0x4f, 0x9a, 0xe2, 0x1e, 0xb9, 0x3b,
- 0xe3, 0x72, 0x17, 0x68, 0xb2, 0x0d, 0x7b, 0x71,
- 0x33, 0x96, 0x5c, 0xf9, 0xfe, 0x18, 0x8f, 0x2f,
- 0x2b, 0x82, 0xec, 0x03, 0xf2, 0x16, 0xa8, 0xf8,
- 0x39, 0xf9, 0xbb, 0x5a, 0xd3, 0x0c, 0xc1, 0x2a,
- 0x52, 0xa1, 0x90, 0x20, 0x6b, 0x24, 0xc9, 0x55,
- 0xee, 0x05, 0xd8, 0xb3, 0x43, 0x58, 0xf6, 0x7f,
- 0x68, 0x2d, 0xb3, 0xd1, 0x1b, 0x30, 0xaa, 0xdf,
- 0xfc, 0x85, 0xf1, 0xab, 0x14, 0x51, 0x91, 0x78,
- 0x29, 0x35, 0x65, 0xe0, 0x9c, 0xf6, 0xb7, 0x35,
- 0x33, 0xdb, 0x28, 0x93, 0x4d, 0x86, 0xbc, 0xfe,
- 0xaa, 0xd1, 0xc0, 0x2e, 0x4d, 0xec, 0xa2, 0x98,
- 0xca, 0x08, 0xb2, 0x91, 0x14, 0xde, 0x97, 0x3a,
- 0xc4, 0x6b, 0x49, 0x14, 0x03, 0x01, 0x00, 0x01,
- 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0x7a, 0xcb,
- 0x3b, 0x0e, 0xbb, 0x7a, 0x56, 0x39, 0xaf, 0x83,
- 0xae, 0xfd, 0x25, 0xfd, 0x64, 0xb4, 0x0c, 0x0c,
- 0x17, 0x46, 0x54, 0x2c, 0x6a, 0x07, 0x83, 0xc6,
- 0x46, 0x08, 0x0b, 0xcd, 0x15, 0x53, 0xef, 0x40,
- 0x4e, 0x56,
+ 0x82, 0x00, 0x80, 0x2d, 0x09, 0x7c, 0x7f, 0xfc,
+ 0x84, 0xce, 0xb3, 0x30, 0x9b, 0xf9, 0xb7, 0xc8,
+ 0xc3, 0xff, 0xee, 0x6f, 0x20, 0x8a, 0xf4, 0xfb,
+ 0x86, 0x55, 0x1f, 0x6a, 0xb4, 0x81, 0x50, 0x3a,
+ 0x46, 0x1b, 0xd3, 0xca, 0x4b, 0x11, 0xff, 0xef,
+ 0x02, 0xbc, 0x18, 0xb8, 0x4a, 0x7d, 0x43, 0x23,
+ 0x96, 0x92, 0x27, 0x7c, 0xca, 0xcf, 0xe6, 0x91,
+ 0xe8, 0x14, 0x97, 0x68, 0xb4, 0xe5, 0xc0, 0xc9,
+ 0x23, 0xdd, 0x54, 0x07, 0xa6, 0x2e, 0x8c, 0x98,
+ 0xfc, 0xc6, 0x8c, 0x04, 0x6b, 0x1b, 0x5f, 0xd5,
+ 0x3d, 0x8b, 0x6c, 0x55, 0x4f, 0x7a, 0xe6, 0x6c,
+ 0x74, 0x2c, 0x1e, 0x34, 0xdb, 0xfb, 0x00, 0xb1,
+ 0x4e, 0x10, 0x21, 0x16, 0xe0, 0x3e, 0xc5, 0x64,
+ 0x84, 0x28, 0x2b, 0x2b, 0x29, 0x47, 0x51, 0x34,
+ 0x76, 0x15, 0x20, 0x71, 0x0b, 0x30, 0xa1, 0x85,
+ 0xd5, 0x15, 0x18, 0x14, 0x64, 0x4b, 0x40, 0x7c,
+ 0x4f, 0xb3, 0x7b, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xab, 0xee,
+ 0xf5, 0x97, 0x5f, 0xc6, 0x78, 0xf3, 0xc6, 0x83,
+ 0x5b, 0x55, 0x4f, 0xcb, 0x45, 0x3f, 0xfa, 0xf7,
+ 0x05, 0x02, 0xc2, 0x63, 0x87, 0x18, 0xb5, 0x9a,
+ 0x62, 0xe2, 0x3f, 0x88, 0x5a, 0x60, 0x61, 0x72,
+ 0xfa, 0x9c,
},
-
{
0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
- 0x01, 0x00, 0x24, 0xd3, 0x72, 0xeb, 0x29, 0xb9,
- 0x15, 0x29, 0xb5, 0xe5, 0xb7, 0xef, 0x5c, 0xb2,
- 0x9d, 0xf6, 0xc8, 0x47, 0xd6, 0xa0, 0x84, 0xf0,
- 0x8c, 0xcb, 0xe6, 0xbe, 0xbc, 0xfb, 0x38, 0x90,
- 0x89, 0x60, 0xa2, 0xe8, 0xaa, 0xb3, 0x12, 0x17,
- 0x03, 0x01, 0x00, 0x21, 0x67, 0x4a, 0x3d, 0x31,
- 0x6c, 0x5a, 0x1c, 0xf9, 0x6e, 0xf1, 0xd8, 0x12,
- 0x0e, 0xb9, 0xfd, 0xfc, 0x66, 0x91, 0xd1, 0x1d,
- 0x6e, 0xe4, 0x55, 0xdd, 0x11, 0xb9, 0xb8, 0xa2,
- 0x65, 0xa1, 0x95, 0x64, 0x1c, 0x15, 0x03, 0x01,
- 0x00, 0x16, 0x9b, 0xa0, 0x24, 0xe3, 0xcb, 0xae,
- 0xad, 0x51, 0xb3, 0x63, 0x59, 0x78, 0x49, 0x24,
- 0x06, 0x6e, 0xee, 0x7a, 0xd7, 0x74, 0x53, 0x04,
+ 0x01, 0x00, 0x24, 0x72, 0xa4, 0xe4, 0xaa, 0xd2,
+ 0xc4, 0x39, 0x7e, 0x2a, 0xc1, 0x6f, 0x34, 0x42,
+ 0x28, 0xcb, 0x9d, 0x7a, 0x09, 0xca, 0x96, 0xad,
+ 0x0e, 0x11, 0x51, 0x8a, 0x06, 0xb0, 0xe9, 0xca,
+ 0xeb, 0xce, 0xe2, 0xd5, 0x2e, 0xc1, 0x8d, 0x17,
+ 0x03, 0x01, 0x00, 0x21, 0x2e, 0x61, 0x86, 0x17,
+ 0xdb, 0xa6, 0x30, 0xe2, 0x62, 0x06, 0x2a, 0x8b,
+ 0x75, 0x2c, 0x2d, 0xcf, 0xf5, 0x01, 0x11, 0x52,
+ 0x81, 0x38, 0xcf, 0xd5, 0xf7, 0xdc, 0x52, 0x31,
+ 0x1f, 0x97, 0x43, 0xc2, 0x71, 0x15, 0x03, 0x01,
+ 0x00, 0x16, 0xe0, 0x21, 0xfe, 0x36, 0x2e, 0x68,
+ 0x2c, 0xf1, 0xbe, 0x04, 0xec, 0xd4, 0xc6, 0xdd,
+ 0xac, 0x6f, 0x4c, 0x85, 0x32, 0x3f, 0x87, 0x1b,
},
}
var des3ServerScript = [][]byte{
{
- 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00,
- 0x76, 0x03, 0x02, 0x4e, 0x84, 0xf4, 0x3c, 0xe4,
- 0xb8, 0xc7, 0xa0, 0x30, 0x55, 0x2a, 0xbc, 0xb7,
- 0x04, 0x6b, 0x6f, 0x87, 0x93, 0x96, 0xbd, 0x1a,
- 0x7a, 0x1e, 0xce, 0xd2, 0x0d, 0xf3, 0x01, 0x03,
- 0xbe, 0x7b, 0x17, 0x00, 0x00, 0x34, 0x00, 0x33,
- 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16,
- 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87,
- 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91,
- 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41,
- 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05,
- 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b,
- 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09,
- 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f,
- 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
+ 0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00,
+ 0xc1, 0x03, 0x03, 0x50, 0xae, 0x5d, 0x38, 0xec,
+ 0xaa, 0x2f, 0x41, 0xf9, 0xd2, 0x7b, 0xa1, 0xfd,
+ 0x0f, 0xff, 0x4e, 0x54, 0x0e, 0x15, 0x57, 0xaf,
+ 0x2c, 0x91, 0xb5, 0x35, 0x5b, 0x2e, 0xb0, 0xec,
+ 0x20, 0xe5, 0xd2, 0x00, 0x00, 0x50, 0xc0, 0x09,
+ 0xc0, 0x23, 0xc0, 0x2b, 0xc0, 0x0a, 0xc0, 0x24,
+ 0xc0, 0x2c, 0xc0, 0x08, 0xc0, 0x13, 0xc0, 0x27,
+ 0xc0, 0x2f, 0xc0, 0x14, 0xc0, 0x30, 0xc0, 0x12,
+ 0x00, 0x33, 0x00, 0x67, 0x00, 0x45, 0x00, 0x9e,
+ 0x00, 0x39, 0x00, 0x6b, 0x00, 0x88, 0x00, 0x16,
+ 0x00, 0x32, 0x00, 0x40, 0x00, 0x44, 0x00, 0xa2,
+ 0x00, 0x38, 0x00, 0x6a, 0x00, 0x87, 0x00, 0x13,
+ 0x00, 0x66, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x41,
+ 0x00, 0x9c, 0x00, 0x35, 0x00, 0x3d, 0x00, 0x84,
+ 0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x01, 0x00,
+ 0x00, 0x48, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00,
+ 0x00, 0x23, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c,
+ 0x00, 0x0a, 0x00, 0x13, 0x00, 0x15, 0x00, 0x17,
+ 0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02,
+ 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x1a,
+ 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x05, 0x01,
+ 0x05, 0x03, 0x06, 0x01, 0x06, 0x03, 0x03, 0x01,
+ 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02,
+ 0x02, 0x03,
},
-
{
- 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
- 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00,
+ 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x16,
- 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
- 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
- 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
- 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
- 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00,
+ 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01,
+ 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02,
+ 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0,
+ 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4,
+ 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
+ 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+ 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+ 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
+ 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+ 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
+ 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39,
+ 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
+ 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33,
+ 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
+ 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+ 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
+ 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
+ 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+ 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79,
+ 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10,
+ 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43,
+ 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85,
+ 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c,
+ 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5,
+ 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c,
+ 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56,
+ 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26,
+ 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21,
+ 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf,
+ 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07,
+ 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39,
+ 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3,
+ 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf,
+ 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb,
+ 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+ 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85,
+ 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23,
+ 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39,
+ 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2,
+ 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce,
+ 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88,
+ 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
@@ -506,172 +695,207 @@ var des3ServerScript = [][]byte{
0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
- 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
- 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
- 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
- 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
- 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
- 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
- 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
- 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
- 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
- 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
- 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
- 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
- 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
- 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
- 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
- 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
- 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
- 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
- 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
- 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
- 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
- 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
- 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
- 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
- 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
- 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
- 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
- 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
- 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
- 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
- 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
- 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
- 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
- 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
- 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
- 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
- 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
- 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
- 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
- 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
- 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
- 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
- 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
- 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
- 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
- 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
- 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
- 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
- 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
- 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
- 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
- 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
- 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
- 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
- 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
- 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
- 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
- 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
- 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
- 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
- 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
- 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
- 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
- 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
- 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
- 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
- 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
- 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
- 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
- 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
- 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e,
- 0x00, 0x00, 0x00,
+ 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85,
+ 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59,
+ 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7,
+ 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95,
+ 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66,
+ 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3,
+ 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13,
+ 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba,
+ 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31,
+ 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50,
+ 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f,
+ 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96,
+ 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f,
+ 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b,
+ 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70,
+ 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e,
+ 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9,
+ 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00,
+ 0x00,
},
-
{
0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
- 0x82, 0x00, 0x80, 0xae, 0xcf, 0x4f, 0x70, 0x0e,
- 0xe5, 0xe7, 0xba, 0xef, 0x0c, 0x66, 0xe9, 0xae,
- 0x76, 0xf4, 0xe0, 0xbc, 0x1c, 0x22, 0x5b, 0x72,
- 0xc9, 0x68, 0x63, 0x44, 0xec, 0x72, 0xc2, 0xca,
- 0xac, 0xc2, 0xf5, 0x5c, 0x28, 0xa1, 0xaf, 0xd0,
- 0xc2, 0xf7, 0x79, 0x71, 0x32, 0x73, 0x86, 0xea,
- 0x39, 0xf6, 0x04, 0x26, 0x19, 0x84, 0x1d, 0x7d,
- 0xa1, 0x21, 0xa6, 0x88, 0xbf, 0x33, 0x5a, 0x64,
- 0xb0, 0xc2, 0xcc, 0x19, 0x7a, 0x8b, 0x6e, 0x94,
- 0x9e, 0x2e, 0x20, 0xbe, 0xdc, 0xe9, 0x8e, 0xae,
- 0x5c, 0x39, 0xc8, 0xcd, 0x0e, 0x19, 0x9a, 0xa2,
- 0xfc, 0x3f, 0x61, 0x9a, 0xca, 0x58, 0x69, 0x0d,
- 0xa8, 0x7b, 0xbe, 0x98, 0x8f, 0xb9, 0x9d, 0x8b,
- 0x68, 0x65, 0xa9, 0x74, 0xcc, 0x8d, 0x0c, 0xb2,
- 0xc4, 0x0f, 0xdc, 0x56, 0x3e, 0x44, 0x61, 0x0a,
- 0x26, 0x93, 0x99, 0xef, 0x67, 0xff, 0x6e, 0x73,
- 0x01, 0xa1, 0x90, 0x14, 0x03, 0x01, 0x00, 0x01,
- 0x01, 0x16, 0x03, 0x01, 0x00, 0x60, 0x49, 0x36,
- 0xc8, 0x38, 0x95, 0xe4, 0x5d, 0x8e, 0x80, 0x10,
- 0x26, 0x9f, 0x87, 0x7d, 0xcd, 0xb9, 0x32, 0x6c,
- 0xff, 0xaa, 0xe0, 0x07, 0xec, 0x33, 0xe2, 0x36,
- 0x9d, 0xd5, 0x83, 0x2c, 0xf0, 0x0a, 0xa0, 0xa8,
- 0x12, 0x9f, 0xca, 0x72, 0xda, 0x70, 0x7d, 0x76,
- 0x80, 0x12, 0x88, 0x07, 0xaa, 0x27, 0x62, 0x33,
- 0xab, 0x55, 0xad, 0x3c, 0x2b, 0x54, 0xc4, 0x1c,
- 0x91, 0xfd, 0x8f, 0x9c, 0xa7, 0x8b, 0x75, 0x10,
- 0xa8, 0x6e, 0xfc, 0x30, 0x52, 0x8a, 0x61, 0x02,
- 0xdb, 0x9c, 0x6f, 0xc8, 0x19, 0x93, 0x5d, 0x41,
- 0x1d, 0x36, 0x68, 0x0b, 0xec, 0x30, 0xae, 0xfb,
- 0x90, 0xdb, 0x6d, 0x83, 0xb0, 0xf2,
+ 0x82, 0x00, 0x80, 0x51, 0x04, 0xf1, 0x7a, 0xbf,
+ 0xe8, 0xa5, 0x86, 0x09, 0xa7, 0xf3, 0xcc, 0x93,
+ 0x00, 0x10, 0x5b, 0xb8, 0xc1, 0x51, 0x0d, 0x5b,
+ 0xcd, 0xed, 0x26, 0x01, 0x69, 0x73, 0xf4, 0x05,
+ 0x8a, 0x6a, 0xc3, 0xb1, 0x9e, 0x84, 0x4e, 0x39,
+ 0xcf, 0x5e, 0x55, 0xa9, 0x70, 0x19, 0x96, 0x91,
+ 0xcd, 0x2c, 0x78, 0x3c, 0xa2, 0x6d, 0xb0, 0x49,
+ 0x86, 0xf6, 0xd1, 0x3a, 0xde, 0x00, 0x4b, 0xa6,
+ 0x25, 0xbf, 0x85, 0x39, 0xce, 0xb1, 0xcf, 0xbc,
+ 0x16, 0xc7, 0x66, 0xac, 0xf8, 0xd2, 0x3b, 0xd1,
+ 0xcc, 0x16, 0xac, 0x63, 0x3c, 0xbe, 0xd9, 0xb6,
+ 0x6a, 0xe4, 0x13, 0x8a, 0xf4, 0x56, 0x2f, 0x92,
+ 0x54, 0xd8, 0xf0, 0x84, 0x01, 0x32, 0x1a, 0xa9,
+ 0x2d, 0xaf, 0x82, 0x0e, 0x00, 0xfa, 0x07, 0x88,
+ 0xd9, 0x87, 0xe7, 0xdc, 0x9e, 0xe9, 0x72, 0x49,
+ 0xb8, 0xfa, 0x8c, 0x7b, 0x07, 0x0b, 0x03, 0x7c,
+ 0x10, 0x8c, 0x8a, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0xa8, 0x61, 0xa4,
+ 0xf4, 0x5f, 0x8a, 0x1f, 0x5c, 0x92, 0x3f, 0x8c,
+ 0xdb, 0xd6, 0x10, 0xcd, 0x9e, 0xe7, 0xf0, 0xc4,
+ 0x3c, 0xb6, 0x1c, 0x9a, 0x56, 0x73, 0x7f, 0xa6,
+ 0x14, 0x24, 0xcb, 0x96, 0x1f, 0xe0, 0xaf, 0xcd,
+ 0x3c, 0x66, 0x43, 0xb7, 0x37, 0x65, 0x34, 0x47,
+ 0xf8, 0x43, 0xf1, 0xcc, 0x15, 0xb8, 0xdc, 0x35,
+ 0xe0, 0xa4, 0x2d, 0x78, 0x94, 0xe0, 0x02, 0xf3,
+ 0x76, 0x46, 0xf7, 0x9b, 0x8d, 0x0d, 0x5d, 0x0b,
+ 0xd3, 0xdd, 0x9a, 0x9e, 0x62, 0x2e, 0xc5, 0x98,
+ 0x75, 0x63, 0x0c, 0xbf, 0x8e, 0x49, 0x33, 0x23,
+ 0x7c, 0x00, 0xcf, 0xfb, 0xcf, 0xba, 0x0f, 0x41,
+ 0x39, 0x89, 0xb9, 0xcc, 0x59, 0xd0, 0x2b, 0xb6,
+ 0xec, 0x04, 0xe2, 0xc0, 0x52, 0xc7, 0xcf, 0x71,
+ 0x47, 0xff, 0x70, 0x7e, 0xa9, 0xbd, 0x1c, 0xdd,
+ 0x17, 0xa5, 0x6c, 0xb7, 0x10, 0x4f, 0x42, 0x18,
+ 0x37, 0x69, 0xa9, 0xd2, 0xb3, 0x18, 0x84, 0x92,
+ 0xa7, 0x47, 0x21, 0xf6, 0x95, 0x63, 0x29, 0xd6,
+ 0xa5, 0xb6, 0xda, 0x65, 0x67, 0x69, 0xc4, 0x26,
+ 0xac, 0x8b, 0x08, 0x58, 0xdd, 0x3c, 0x31, 0x20,
+ 0xd5, 0x0c, 0x88, 0x72, 0x18, 0x16, 0x88, 0x1e,
+ 0x4a, 0x0f, 0xe1, 0xcf, 0x95, 0x24,
},
-
{
- 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
- 0x01, 0x00, 0x28, 0x07, 0xf3, 0x33, 0x84, 0xb1,
- 0x5d, 0x2b, 0x52, 0xa4, 0x63, 0x3c, 0x32, 0xe0,
- 0x0d, 0x22, 0xf5, 0x23, 0xec, 0xf9, 0xa6, 0xec,
- 0xc0, 0x12, 0x69, 0x88, 0xf6, 0x7d, 0x37, 0xcd,
- 0xc2, 0x74, 0x2f, 0xef, 0xf6, 0x49, 0x15, 0xea,
- 0x88, 0x3f, 0x55, 0x17, 0x03, 0x01, 0x00, 0x28,
- 0xaf, 0x00, 0x84, 0xff, 0x11, 0x01, 0x6d, 0xba,
- 0x39, 0x5e, 0x45, 0xe1, 0x52, 0x5e, 0xc1, 0xab,
- 0xde, 0x5b, 0x16, 0xdd, 0xd6, 0x61, 0x57, 0xb8,
- 0x66, 0x8b, 0x2d, 0xde, 0x51, 0x41, 0xc5, 0x09,
- 0xb3, 0x6a, 0x06, 0x43, 0xb4, 0x73, 0x5c, 0xf1,
- 0x15, 0x03, 0x01, 0x00, 0x18, 0xbd, 0x65, 0xb2,
- 0xce, 0x77, 0x2e, 0xf9, 0x11, 0xc4, 0x80, 0x43,
- 0x5a, 0x73, 0x8b, 0x73, 0xdd, 0xf0, 0x54, 0x44,
- 0x7c, 0x56, 0x19, 0x54, 0xda,
+ 0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+ 0xe8, 0x4b, 0xde, 0xef, 0xba, 0x3e, 0x18, 0x1c,
+ 0x1e, 0x5e, 0xbc, 0x87, 0xf1, 0x87, 0x8d, 0x72,
+ 0xe3, 0xbe, 0x0f, 0xdf, 0xfd, 0xd0, 0xb2, 0x89,
+ 0xf8, 0x05, 0x9a, 0x52, 0x47, 0x77, 0x9e, 0xe8,
+ 0xb1, 0x1d, 0x18, 0xed, 0x6a, 0x4b, 0x63, 0x1d,
+ 0xf1, 0x62, 0xd2, 0x65, 0x21, 0x26, 0x73, 0xd4,
+ 0x35, 0x5b, 0x95, 0x89, 0x12, 0x59, 0x23, 0x8c,
+ 0xc3, 0xfc, 0xf9, 0x4d, 0x21, 0x79, 0xa0, 0xbd,
+ 0xff, 0x33, 0xa2, 0x3d, 0x0b, 0x6f, 0x89, 0xc9,
+ 0x23, 0xe4, 0xe7, 0x9f, 0x1d, 0x98, 0xf6, 0xed,
+ 0x02, 0x8d, 0xac, 0x1a, 0xf9, 0xcb, 0xa5, 0x14,
+ 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
+ 0x00, 0x28, 0x91, 0x56, 0x80, 0xe2, 0x6d, 0x51,
+ 0x88, 0x03, 0xf8, 0x49, 0xe6, 0x6a, 0x5a, 0xfb,
+ 0x2f, 0x0b, 0xb5, 0xa1, 0x0d, 0x63, 0x83, 0xae,
+ 0xb9, 0xbc, 0x05, 0xf0, 0x81, 0x00, 0x61, 0x83,
+ 0x38, 0xda, 0x14, 0xf6, 0xea, 0xd8, 0x78, 0x65,
+ 0xc7, 0x26, 0x17, 0x03, 0x01, 0x00, 0x18, 0x81,
+ 0x30, 0x8b, 0x22, 0x5a, 0xd3, 0x7f, 0xc8, 0xf2,
+ 0x8a, 0x6b, 0xa3, 0xba, 0x4d, 0xe7, 0x6e, 0xd2,
+ 0xfd, 0xbf, 0xf2, 0xc5, 0x28, 0xa0, 0x62, 0x17,
+ 0x03, 0x01, 0x00, 0x28, 0x17, 0x83, 0x3c, 0x78,
+ 0x18, 0xfa, 0x8d, 0x58, 0x5c, 0xaa, 0x05, 0x7d,
+ 0x67, 0x96, 0x11, 0x60, 0x11, 0xc0, 0x1e, 0x0d,
+ 0x6a, 0x6e, 0x5f, 0x1d, 0x98, 0x4b, 0xff, 0x82,
+ 0xee, 0x21, 0x06, 0x29, 0xd3, 0x8b, 0x80, 0x78,
+ 0x39, 0x05, 0x34, 0x9b, 0x15, 0x03, 0x01, 0x00,
+ 0x18, 0xa9, 0x38, 0x18, 0x4f, 0x9d, 0x84, 0x75,
+ 0x88, 0x53, 0xd6, 0x85, 0xc2, 0x15, 0x4b, 0xe3,
+ 0xe3, 0x35, 0x9a, 0x74, 0xc9, 0x3e, 0x13, 0xc1,
+ 0x8c,
},
}
var aesServerScript = [][]byte{
{
- 0x16, 0x03, 0x02, 0x00, 0x7f, 0x01, 0x00, 0x00,
- 0x7b, 0x03, 0x02, 0x4d, 0x08, 0x2d, 0x0b, 0xb3,
- 0x57, 0x85, 0x71, 0x4b, 0xfb, 0x34, 0xab, 0x16,
- 0xd4, 0x92, 0x50, 0x81, 0x16, 0x95, 0x11, 0x28,
- 0x1a, 0xcb, 0xff, 0x09, 0x4d, 0x23, 0xa6, 0xfe,
- 0x2e, 0xbb, 0x78, 0x00, 0x00, 0x34, 0x00, 0x33,
- 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16,
- 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87,
- 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91,
- 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41,
- 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05,
- 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b,
- 0x00, 0x8a, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x09,
- 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f,
- 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0xff,
- 0x01, 0x00, 0x01, 0x00,
+ 0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00,
+ 0xc1, 0x03, 0x03, 0x50, 0xae, 0x5c, 0xe9, 0x5e,
+ 0x31, 0x93, 0x82, 0xa5, 0x6f, 0x51, 0x82, 0xc8,
+ 0x55, 0x4f, 0x1f, 0x2e, 0x90, 0x98, 0x81, 0x13,
+ 0x27, 0x80, 0x68, 0xb4, 0x2d, 0xba, 0x3a, 0x76,
+ 0xd8, 0xd7, 0x2c, 0x00, 0x00, 0x50, 0xc0, 0x09,
+ 0xc0, 0x23, 0xc0, 0x2b, 0xc0, 0x0a, 0xc0, 0x24,
+ 0xc0, 0x2c, 0xc0, 0x08, 0xc0, 0x13, 0xc0, 0x27,
+ 0xc0, 0x2f, 0xc0, 0x14, 0xc0, 0x30, 0xc0, 0x12,
+ 0x00, 0x33, 0x00, 0x67, 0x00, 0x45, 0x00, 0x9e,
+ 0x00, 0x39, 0x00, 0x6b, 0x00, 0x88, 0x00, 0x16,
+ 0x00, 0x32, 0x00, 0x40, 0x00, 0x44, 0x00, 0xa2,
+ 0x00, 0x38, 0x00, 0x6a, 0x00, 0x87, 0x00, 0x13,
+ 0x00, 0x66, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x41,
+ 0x00, 0x9c, 0x00, 0x35, 0x00, 0x3d, 0x00, 0x84,
+ 0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x01, 0x00,
+ 0x00, 0x48, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00,
+ 0x00, 0x23, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c,
+ 0x00, 0x0a, 0x00, 0x13, 0x00, 0x15, 0x00, 0x17,
+ 0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02,
+ 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x1a,
+ 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x05, 0x01,
+ 0x05, 0x03, 0x06, 0x01, 0x06, 0x03, 0x03, 0x01,
+ 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02,
+ 0x02, 0x03,
},
-
{
- 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
- 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00,
+ 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x16,
- 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
- 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
- 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
- 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
- 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00,
+ 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01,
+ 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02,
+ 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0,
+ 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4,
+ 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
+ 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+ 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+ 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
+ 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+ 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
+ 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39,
+ 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
+ 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33,
+ 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
+ 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+ 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
+ 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
+ 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+ 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79,
+ 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10,
+ 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43,
+ 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85,
+ 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c,
+ 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5,
+ 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c,
+ 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56,
+ 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26,
+ 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21,
+ 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf,
+ 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07,
+ 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39,
+ 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3,
+ 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf,
+ 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb,
+ 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+ 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85,
+ 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23,
+ 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39,
+ 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2,
+ 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce,
+ 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88,
+ 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
@@ -680,136 +904,144 @@ var aesServerScript = [][]byte{
0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
- 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
- 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
- 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
- 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
- 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
- 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
- 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
- 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
- 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
- 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
- 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
- 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
- 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
- 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
- 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
- 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
- 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
- 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
- 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
- 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
- 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
- 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
- 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
- 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
- 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
- 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
- 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
- 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
- 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
- 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
- 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
- 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
- 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
- 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
- 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
- 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
- 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
- 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
- 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
- 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
- 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
- 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
- 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
- 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
- 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
- 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
- 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
- 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
- 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
- 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
- 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
- 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
- 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
- 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
- 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
- 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
- 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
- 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
- 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
- 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
- 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
- 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
- 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
- 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
- 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
- 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
- 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
- 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
- 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
- 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
- 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e,
- 0x00, 0x00, 0x00,
+ 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85,
+ 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59,
+ 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7,
+ 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95,
+ 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66,
+ 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3,
+ 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13,
+ 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba,
+ 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31,
+ 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50,
+ 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f,
+ 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96,
+ 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f,
+ 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b,
+ 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70,
+ 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e,
+ 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9,
+ 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00,
+ 0x00,
},
-
{
0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
- 0x82, 0x00, 0x80, 0x71, 0x9c, 0xe7, 0x23, 0xfc,
- 0xb9, 0x19, 0x29, 0x82, 0xbf, 0xef, 0x08, 0xf7,
- 0x99, 0x36, 0xc3, 0x4c, 0x6f, 0x05, 0xd2, 0x8b,
- 0x62, 0x2b, 0x19, 0x9b, 0x7f, 0xc0, 0xcc, 0x48,
- 0x30, 0x5f, 0xcd, 0xc3, 0x70, 0x55, 0x53, 0x73,
- 0xfa, 0x79, 0x74, 0xf3, 0xa3, 0x76, 0x9f, 0xa1,
- 0x7f, 0x98, 0xc2, 0xc0, 0xe3, 0xc5, 0xa0, 0x31,
- 0x2f, 0xa6, 0xe8, 0x1e, 0x61, 0x46, 0xb3, 0x9b,
- 0x4b, 0x16, 0xf1, 0x2d, 0xc7, 0x63, 0x7f, 0x79,
- 0x22, 0x30, 0xd1, 0xf2, 0xfc, 0x77, 0x98, 0x0a,
- 0x16, 0x11, 0x63, 0x71, 0x7f, 0x70, 0xef, 0x16,
- 0xbb, 0x39, 0x87, 0x34, 0xac, 0x49, 0xbd, 0x07,
- 0x67, 0xcb, 0x9c, 0xcc, 0xde, 0xef, 0xb1, 0xe0,
- 0xdb, 0x01, 0xb5, 0x35, 0xa9, 0xb3, 0x10, 0x0c,
- 0x4b, 0xee, 0xb3, 0x4e, 0xfd, 0xbe, 0x15, 0x27,
- 0xf0, 0x46, 0xb2, 0x38, 0xba, 0x5f, 0xcc, 0x89,
- 0xec, 0x29, 0x82, 0x14, 0x03, 0x01, 0x00, 0x01,
- 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x3c, 0xfb,
- 0xa4, 0x12, 0xcb, 0x00, 0xf9, 0x57, 0x7e, 0x9b,
- 0xc9, 0xdc, 0x0c, 0xba, 0x9a, 0x81, 0x62, 0xfb,
- 0x26, 0x13, 0x53, 0xfe, 0xaa, 0xcc, 0x82, 0xbb,
- 0xb6, 0x67, 0x7f, 0x39, 0xbe, 0x4d, 0xbb, 0xc0,
- 0x6c, 0x24, 0x31, 0x83, 0xa5, 0x50, 0x3a, 0x75,
- 0x32, 0x64, 0xb5, 0xdb, 0xbe, 0x0a,
+ 0x82, 0x00, 0x80, 0x51, 0x2e, 0xec, 0x0d, 0x86,
+ 0xf3, 0x9f, 0xf2, 0x77, 0x04, 0x27, 0x2b, 0x0e,
+ 0x9c, 0xab, 0x35, 0x84, 0x65, 0xff, 0x36, 0xef,
+ 0xc0, 0x08, 0xc9, 0x1d, 0x9f, 0x29, 0xae, 0x8d,
+ 0xc5, 0x66, 0x81, 0x31, 0x92, 0x5e, 0x3d, 0xac,
+ 0xaa, 0x37, 0x28, 0x2c, 0x06, 0x91, 0xa6, 0xc2,
+ 0xd0, 0x83, 0x34, 0x24, 0x1c, 0x88, 0xfc, 0x0a,
+ 0xcf, 0xbf, 0xc2, 0x94, 0xe2, 0xed, 0xa7, 0x6a,
+ 0xa8, 0x8d, 0x3d, 0xf7, 0x06, 0x7d, 0x69, 0xf8,
+ 0x0d, 0xb2, 0xf7, 0xe4, 0x45, 0xcb, 0x0a, 0x25,
+ 0xcb, 0xb2, 0x2e, 0x38, 0x9a, 0x84, 0x75, 0xe8,
+ 0xe1, 0x42, 0x39, 0xa2, 0x18, 0x0e, 0x48, 0xca,
+ 0x33, 0x16, 0x4e, 0xf6, 0x2f, 0xec, 0x07, 0xe7,
+ 0x57, 0xe1, 0x20, 0x40, 0x40, 0x6d, 0x4e, 0x29,
+ 0x04, 0x1a, 0x8c, 0x99, 0xfb, 0x19, 0x3c, 0xaa,
+ 0x75, 0x64, 0xd3, 0xa6, 0xe6, 0xed, 0x3f, 0x5a,
+ 0xd2, 0xc9, 0x80, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x01, 0x10, 0xe9, 0x9e,
+ 0x06, 0x92, 0x18, 0xbf, 0x5e, 0xaf, 0x33, 0xc1,
+ 0xbf, 0x0e, 0x12, 0x07, 0x48, 0x4f, 0x6b, 0x6c,
+ 0xf5, 0x23, 0x5e, 0x87, 0xa7, 0xd3, 0x50, 0x79,
+ 0x38, 0xdc, 0xe0, 0x49, 0xd3, 0x81, 0x21, 0x12,
+ 0xd0, 0x3d, 0x9a, 0xfb, 0x83, 0xc1, 0x8b, 0xfc,
+ 0x14, 0xd5, 0xd5, 0xa7, 0xa3, 0x34, 0x14, 0x71,
+ 0xbe, 0xea, 0x37, 0x18, 0x12, 0x7f, 0x41, 0xfb,
+ 0xc5, 0x51, 0x17, 0x9d, 0x96, 0x58, 0x14, 0xfb,
+ 0x4f, 0xd7, 0xd3, 0x15, 0x0f, 0xec, 0x5a, 0x0d,
+ 0x35, 0xbb, 0x3c, 0x81, 0x5b, 0x3f, 0xdf, 0x52,
+ 0xa4, 0x4c, 0xcd, 0x13, 0xe1, 0x10, 0x37, 0x34,
+ 0xbf, 0xb4, 0x80, 0x1e, 0x8d, 0xe2, 0xc3, 0x7a,
+ 0x0f, 0x7b, 0x7d, 0x23, 0xeb, 0xd0, 0x99, 0x69,
+ 0xad, 0x0a, 0x2d, 0xb3, 0x6c, 0xd6, 0x80, 0x11,
+ 0x7f, 0x6c, 0xed, 0x1b, 0xcd, 0x08, 0x22, 0x56,
+ 0x90, 0x0e, 0xa4, 0xc3, 0x29, 0x33, 0x96, 0x30,
+ 0x34, 0x94, 0xa1, 0xeb, 0x9c, 0x1b, 0x5a, 0xd1,
+ 0x03, 0x61, 0xf9, 0xdd, 0xf3, 0x64, 0x8a, 0xfd,
+ 0x5f, 0x44, 0xdb, 0x2e, 0xa7, 0xfd, 0xe1, 0x1a,
+ 0x66, 0xc5, 0x01, 0x9c, 0xc7, 0xd1, 0xc4, 0xd3,
+ 0xea, 0x14, 0x3c, 0xed, 0x74, 0xbb, 0x1b, 0x97,
+ 0x8f, 0xf1, 0x29, 0x39, 0x33, 0x92, 0x93, 0x4e,
+ 0xf5, 0x87, 0x91, 0x61, 0x65, 0x8d, 0x27, 0x8d,
+ 0x76, 0xc1, 0xfa, 0x6a, 0x99, 0x80, 0xb1, 0x9b,
+ 0x29, 0x53, 0xce, 0x3e, 0xb6, 0x9a, 0xce, 0x3c,
+ 0x19, 0x5e, 0x48, 0x83, 0xaa, 0xa7, 0x66, 0x98,
+ 0x59, 0xf4, 0xbb, 0xf2, 0xbc, 0xd9, 0xc5, 0x9a,
+ 0xc8, 0x2c, 0x63, 0x58, 0xd5, 0xd4, 0xbc, 0x03,
+ 0xa9, 0x06, 0xa9, 0x80, 0x0d, 0xb3, 0x46, 0x2d,
+ 0xe3, 0xc6, 0xaf, 0x1a, 0x39, 0x18, 0x7e, 0x1e,
+ 0x83, 0x80, 0x46, 0x11, 0xd2, 0x13, 0x9f, 0xda,
+ 0xfc, 0x2d, 0x42, 0xaa, 0x5a, 0x1d, 0x4c, 0x31,
+ 0xe5, 0x58, 0x78, 0x5e, 0xe2, 0x04, 0xd6, 0x23,
+ 0x7f, 0x3f, 0x06, 0xc0, 0x54, 0xf8,
},
-
{
- 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
- 0x01, 0x00, 0x30, 0x43, 0x24, 0x42, 0x55, 0x08,
- 0xe4, 0xc2, 0x15, 0xc9, 0xdb, 0x71, 0x69, 0xee,
- 0x09, 0xc5, 0x1c, 0xfd, 0x46, 0x10, 0xa0, 0x68,
- 0x21, 0xf2, 0x48, 0xac, 0x6c, 0xc0, 0x2b, 0x62,
- 0x07, 0x8f, 0x48, 0x33, 0x0a, 0x6b, 0x62, 0x28,
- 0x2e, 0x2c, 0xad, 0xcb, 0x34, 0x85, 0xca, 0x2e,
- 0xcd, 0x84, 0xf0,
+ 0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+ 0xe8, 0x4b, 0xfb, 0xef, 0xba, 0xed, 0xc5, 0x36,
+ 0xc8, 0x5a, 0x41, 0x3f, 0x05, 0xfa, 0xfe, 0x48,
+ 0xc3, 0x91, 0x12, 0x8b, 0xe8, 0x32, 0x6a, 0x9f,
+ 0xdc, 0x97, 0xe2, 0x77, 0xb9, 0x96, 0x2d, 0xd4,
+ 0xe5, 0xbd, 0xa1, 0xfd, 0x94, 0xbb, 0x74, 0x63,
+ 0xb1, 0x0c, 0x38, 0xbc, 0x6f, 0x69, 0xaf, 0xa3,
+ 0x46, 0x9c, 0x96, 0x41, 0xde, 0x59, 0x23, 0xff,
+ 0x15, 0x6b, 0x3a, 0xef, 0x91, 0x6d, 0x92, 0x44,
+ 0xdc, 0x72, 0x1f, 0x40, 0x3d, 0xb5, 0x34, 0x8f,
+ 0x2a, 0xac, 0x21, 0x69, 0x05, 0x6f, 0xb2, 0x60,
+ 0x32, 0x5d, 0x3d, 0x97, 0xb4, 0x24, 0x99, 0x14,
+ 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
+ 0x00, 0x30, 0x68, 0x27, 0x97, 0xca, 0x63, 0x09,
+ 0x22, 0xed, 0x0e, 0x61, 0x7c, 0x76, 0x31, 0x9c,
+ 0xbe, 0x27, 0xc9, 0xe6, 0x09, 0xc3, 0xc3, 0xc2,
+ 0xf4, 0xa2, 0x32, 0xba, 0x7c, 0xf2, 0x0f, 0xb8,
+ 0x3d, 0xcb, 0xe2, 0x4c, 0xc0, 0x7d, 0x8e, 0x5b,
+ 0x5a, 0xed, 0x05, 0x5c, 0x15, 0x96, 0x69, 0xc2,
+ 0x6f, 0x5f, 0x17, 0x03, 0x01, 0x00, 0x20, 0x5a,
+ 0xfe, 0x0b, 0xe1, 0x6f, 0xa8, 0x54, 0x19, 0x78,
+ 0xca, 0xba, 0x2e, 0x1e, 0x2e, 0xe1, 0x5d, 0x17,
+ 0xe5, 0x97, 0x05, 0x2c, 0x08, 0x0c, 0xff, 0xa8,
+ 0x59, 0xa9, 0xde, 0x5e, 0x21, 0x34, 0x04, 0x17,
+ 0x03, 0x01, 0x00, 0x30, 0x86, 0xb1, 0x3f, 0x88,
+ 0x43, 0xf0, 0x07, 0xee, 0xa8, 0xf4, 0xbc, 0xe7,
+ 0x5f, 0xc6, 0x8c, 0x86, 0x4c, 0xca, 0x70, 0x88,
+ 0xcc, 0x6a, 0xb4, 0x3d, 0x40, 0xe8, 0x54, 0x89,
+ 0x19, 0x43, 0x1f, 0x76, 0xe2, 0xac, 0xb2, 0x5b,
+ 0x92, 0xf8, 0x57, 0x39, 0x2a, 0xc3, 0x6d, 0x13,
+ 0x45, 0xfa, 0x36, 0x9e, 0x15, 0x03, 0x01, 0x00,
+ 0x20, 0x6d, 0xed, 0x7b, 0x59, 0x28, 0x2a, 0x27,
+ 0x04, 0x15, 0x07, 0x4e, 0xeb, 0x13, 0x00, 0xe3,
+ 0x3a, 0x3f, 0xf8, 0xaa, 0x2b, 0x3b, 0x1a, 0x8c,
+ 0x12, 0xd6, 0x4c, 0xec, 0x2a, 0xaf, 0x33, 0x60,
+ 0xaf,
},
}
var sslv3ServerScript = [][]byte{
{
- 0x16, 0x03, 0x00, 0x00, 0x41, 0x01, 0x00, 0x00,
- 0x3d, 0x03, 0x00, 0x4e, 0x70, 0xe2, 0x18, 0x86,
- 0xd6, 0xc6, 0x6f, 0xf3, 0xc8, 0xf4, 0x02, 0xd6,
- 0x4d, 0xee, 0x17, 0x32, 0x4b, 0xd2, 0x78, 0xd8,
- 0xa1, 0x03, 0x5d, 0x68, 0x82, 0x89, 0xbe, 0xfd,
- 0x12, 0xb9, 0x06, 0x00, 0x00, 0x16, 0x00, 0x33,
- 0x00, 0x39, 0x00, 0x16, 0x00, 0x32, 0x00, 0x38,
- 0x00, 0x13, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a,
- 0x00, 0x05, 0x00, 0x04, 0x01, 0x00,
+ 0x16, 0x03, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00,
+ 0x50, 0x03, 0x00, 0x50, 0x77, 0x3d, 0x42, 0xae,
+ 0x84, 0xbd, 0xc5, 0x07, 0xa5, 0xc4, 0xd6, 0x16,
+ 0x4e, 0xd5, 0xc5, 0xfa, 0x02, 0x7a, 0x0f, 0x1d,
+ 0xc1, 0xe1, 0xaa, 0xe3, 0x3b, 0x4b, 0x6f, 0x11,
+ 0xfa, 0x1a, 0xa4, 0x00, 0x00, 0x28, 0x00, 0x39,
+ 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13,
+ 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f,
+ 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
+ 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,
+ 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01,
+ 0x00,
},
-
{
0x16, 0x03, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00,
0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -908,74 +1140,71 @@ var sslv3ServerScript = [][]byte{
0xbd, 0xd9, 0x16, 0x03, 0x00, 0x00, 0x04, 0x0e,
0x00, 0x00, 0x00,
},
-
{
0x16, 0x03, 0x00, 0x00, 0x84, 0x10, 0x00, 0x00,
- 0x80, 0x74, 0x0e, 0x3a, 0xcf, 0xba, 0x9f, 0x1a,
- 0x9b, 0xb2, 0xa4, 0xc7, 0x5d, 0xf3, 0x0c, 0x80,
- 0x06, 0x80, 0xf3, 0x57, 0xb2, 0xd9, 0x36, 0x24,
- 0x6a, 0x06, 0x13, 0x40, 0xf9, 0x7c, 0xb9, 0x3e,
- 0x4b, 0x68, 0x4f, 0x21, 0x90, 0x2d, 0xbd, 0xca,
- 0xd4, 0x83, 0xf0, 0x7a, 0xeb, 0x7a, 0x74, 0x1b,
- 0xcd, 0xfe, 0x69, 0xef, 0xc0, 0x86, 0xa0, 0x24,
- 0x31, 0x65, 0x40, 0xd2, 0xdd, 0x6f, 0xb9, 0xd7,
- 0x8d, 0xc1, 0x69, 0x60, 0x44, 0x7a, 0x75, 0xfb,
- 0x42, 0x6a, 0x0f, 0x66, 0x45, 0x10, 0x73, 0xee,
- 0x87, 0x28, 0x37, 0x83, 0x86, 0xd8, 0x5a, 0xc8,
- 0x60, 0x87, 0xda, 0x33, 0x87, 0xaf, 0x34, 0x8b,
- 0xf5, 0x61, 0x63, 0x7a, 0x5c, 0x60, 0x26, 0xb9,
- 0xdb, 0xa1, 0xb7, 0xe3, 0x60, 0x38, 0x94, 0x5c,
- 0x83, 0x23, 0xd6, 0x8d, 0xc2, 0x14, 0x4a, 0x0f,
- 0x0e, 0x4f, 0xf9, 0x4e, 0x7b, 0x15, 0xcd, 0x18,
- 0x04, 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16,
- 0x03, 0x00, 0x00, 0x3c, 0xbd, 0xbc, 0xec, 0xdc,
- 0x79, 0xb1, 0xae, 0x16, 0xc9, 0x26, 0x9a, 0xc0,
- 0xc0, 0x2c, 0x33, 0x36, 0x13, 0x91, 0x58, 0x5d,
- 0x7d, 0xee, 0x4e, 0xd8, 0x7e, 0xac, 0x88, 0x87,
- 0x0a, 0x75, 0x66, 0xb1, 0x44, 0x79, 0x2f, 0x42,
- 0xe8, 0x92, 0x74, 0x4c, 0xab, 0x36, 0xc8, 0x17,
- 0x5f, 0x02, 0x8a, 0x20, 0x53, 0xe9, 0x1d, 0xb4,
- 0xfe, 0x5c, 0x2b, 0xd9, 0x0a, 0xfb, 0xc6, 0x63,
+ 0x80, 0x4a, 0x8d, 0xc4, 0x38, 0x7a, 0x9c, 0xd6,
+ 0xe8, 0x72, 0x9e, 0xa3, 0xdf, 0x37, 0xb4, 0x6c,
+ 0x58, 0x33, 0x59, 0xd9, 0xc9, 0x4b, 0x50, 0x33,
+ 0x6c, 0xed, 0x73, 0x38, 0x2a, 0x46, 0x55, 0x31,
+ 0xa9, 0x8e, 0x8e, 0xfc, 0x0b, 0x5d, 0x5f, 0x3c,
+ 0x88, 0x28, 0x3f, 0x60, 0x51, 0x13, 0xf1, 0x59,
+ 0x0c, 0xa3, 0x5e, 0xe0, 0xa3, 0x35, 0x06, 0xb1,
+ 0x71, 0x59, 0x24, 0x4e, 0xed, 0x07, 0x15, 0x88,
+ 0x50, 0xef, 0xc2, 0xb2, 0x2a, 0x52, 0x30, 0x6a,
+ 0x7c, 0xbe, 0x2f, 0xc6, 0x8f, 0xa8, 0x83, 0xc5,
+ 0x80, 0x14, 0x62, 0x74, 0x7f, 0x96, 0x9f, 0x41,
+ 0x32, 0x74, 0xdd, 0x76, 0x2d, 0x7b, 0xeb, 0x7b,
+ 0xea, 0xd0, 0x4f, 0x0c, 0xcf, 0x9a, 0x9c, 0xc5,
+ 0x7a, 0xe4, 0xbc, 0xf8, 0xa6, 0xe1, 0x09, 0x8e,
+ 0x7c, 0x53, 0x3a, 0xe3, 0x30, 0x8f, 0x76, 0xee,
+ 0x58, 0xbb, 0xfd, 0x0b, 0x06, 0xb8, 0xdf, 0xb7,
+ 0x31, 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16,
+ 0x03, 0x00, 0x00, 0x3c, 0x13, 0x91, 0xc6, 0x4a,
+ 0x0c, 0x59, 0x25, 0xce, 0x54, 0xc0, 0x1d, 0xb9,
+ 0x2a, 0xff, 0x4d, 0xca, 0x26, 0x0c, 0x8c, 0x04,
+ 0x98, 0x7c, 0x7c, 0x38, 0xa3, 0xf5, 0xf9, 0x36,
+ 0x1c, 0x04, 0x32, 0x47, 0x2d, 0x48, 0x0e, 0x96,
+ 0xe8, 0x2b, 0x5e, 0x5a, 0xc6, 0x0a, 0x48, 0x41,
+ 0x34, 0x5e, 0x62, 0xd5, 0x68, 0x4e, 0x44, 0x1d,
+ 0xb2, 0xa1, 0x11, 0xad, 0x6e, 0x14, 0x85, 0x61,
},
-
{
0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16, 0x03,
- 0x00, 0x00, 0x3c, 0xaa, 0xa1, 0x98, 0xc4, 0x6b,
- 0x5a, 0x16, 0x3f, 0x5f, 0xa4, 0x96, 0x3e, 0x78,
- 0xe4, 0x6f, 0x49, 0x05, 0x47, 0xc4, 0x05, 0x60,
- 0xeb, 0x0b, 0x45, 0xe3, 0xbc, 0x50, 0x11, 0x24,
- 0x5f, 0x01, 0xd7, 0xb8, 0x8f, 0x60, 0x63, 0x66,
- 0xbd, 0x3e, 0xd9, 0xa8, 0x80, 0x43, 0x9f, 0x0b,
- 0x51, 0x61, 0xed, 0x13, 0xc6, 0x21, 0xd0, 0xfe,
- 0xbc, 0x17, 0x3c, 0x36, 0xb0, 0x82, 0x7f, 0x17,
- 0x03, 0x00, 0x00, 0x21, 0xee, 0x44, 0xf3, 0xa6,
- 0x88, 0x9d, 0x78, 0x44, 0xde, 0xdf, 0xeb, 0xc5,
- 0xad, 0xc4, 0xcc, 0x56, 0x5c, 0x54, 0x96, 0x52,
- 0x3f, 0xd9, 0x40, 0x6e, 0x79, 0xd8, 0x58, 0x78,
- 0x4f, 0x5a, 0xe9, 0x06, 0xef, 0x15, 0x03, 0x00,
- 0x00, 0x16, 0xd3, 0xc2, 0x52, 0x99, 0x2a, 0x84,
- 0xc4, 0x52, 0x5f, 0x3b, 0x19, 0xe7, 0xfc, 0x65,
- 0xaf, 0xd3, 0xb7, 0xa3, 0xcc, 0x4a, 0x1d, 0x2e,
+ 0x00, 0x00, 0x3c, 0x88, 0xae, 0xa9, 0xd4, 0xa8,
+ 0x10, 0x8d, 0x65, 0xa6, 0x3e, 0x1e, 0xed, 0xd2,
+ 0xfc, 0xc4, 0x7c, 0xa8, 0x94, 0x4f, 0x11, 0xaf,
+ 0xa6, 0x87, 0x09, 0x37, 0x54, 0xf7, 0x69, 0xd1,
+ 0xb5, 0x25, 0x6b, 0xb5, 0xed, 0xcb, 0x25, 0x39,
+ 0x73, 0xeb, 0x53, 0x6c, 0xc7, 0xb4, 0x29, 0x8f,
+ 0xd6, 0x49, 0xd1, 0x95, 0x59, 0x80, 0x9a, 0x67,
+ 0x5c, 0xb2, 0xe0, 0xbd, 0x1e, 0xff, 0xaa, 0x17,
+ 0x03, 0x00, 0x00, 0x21, 0x65, 0x7b, 0x99, 0x09,
+ 0x02, 0xc3, 0x9d, 0x54, 0xd6, 0xe7, 0x32, 0x62,
+ 0xab, 0xc1, 0x09, 0x91, 0x30, 0x0a, 0xc9, 0xfa,
+ 0x70, 0xec, 0x06, 0x7b, 0xa3, 0xe1, 0x5f, 0xb4,
+ 0x63, 0xe6, 0x5c, 0xba, 0x1f, 0x15, 0x03, 0x00,
+ 0x00, 0x16, 0x40, 0x70, 0xbe, 0xe6, 0xa6, 0xee,
+ 0x8f, 0xd0, 0x87, 0xa0, 0x43, 0xa1, 0x92, 0xd7,
+ 0xd0, 0x1a, 0x0c, 0x20, 0x7c, 0xbf, 0xa2, 0xb5,
},
}
var selectCertificateBySNIScript = [][]byte{
{
- 0x16, 0x03, 0x01, 0x00, 0x6e, 0x01, 0x00, 0x00,
- 0x6a, 0x03, 0x01, 0x4f, 0x85, 0xc4, 0xc2, 0xb9,
- 0x39, 0x80, 0x91, 0x66, 0x65, 0x56, 0x8e, 0xdd,
- 0x48, 0xe9, 0xca, 0x34, 0x02, 0x3c, 0xaf, 0x0d,
- 0x73, 0xb5, 0x2a, 0x05, 0x6e, 0xbd, 0x5e, 0x8f,
- 0x38, 0xf9, 0xe5, 0x00, 0x00, 0x28, 0x00, 0x39,
+ 0x16, 0x03, 0x01, 0x00, 0x6a, 0x01, 0x00, 0x00,
+ 0x66, 0x03, 0x01, 0x50, 0x77, 0x3d, 0xfe, 0xfb,
+ 0x8d, 0xc2, 0x68, 0xeb, 0xf9, 0xfa, 0x54, 0x97,
+ 0x86, 0x45, 0xa2, 0xa3, 0xed, 0xb1, 0x91, 0xb8,
+ 0x28, 0xc0, 0x47, 0xaf, 0xfb, 0xcd, 0xdc, 0x0e,
+ 0xb3, 0xea, 0xa5, 0x00, 0x00, 0x28, 0x00, 0x39,
0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13,
0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f,
0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,
0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01,
- 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00,
0x0e, 0x00, 0x00, 0x0b, 0x73, 0x6e, 0x69, 0x74,
- 0x65, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00,
- 0x23, 0x00, 0x00,
+ 0x65, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
},
{
0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
@@ -1053,632 +1282,684 @@ var selectCertificateBySNIScript = [][]byte{
},
{
0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
- 0x82, 0x00, 0x80, 0x70, 0x1d, 0x34, 0x75, 0xa2,
- 0xe7, 0xe3, 0x2f, 0x3d, 0xc1, 0x1d, 0xca, 0x0b,
- 0xe3, 0x64, 0xb9, 0x1a, 0x00, 0x69, 0xc4, 0x14,
- 0x05, 0x07, 0x7e, 0xc3, 0x51, 0x43, 0x52, 0x66,
- 0xe3, 0xbd, 0xff, 0x1b, 0x1a, 0x6a, 0x84, 0xf2,
- 0x07, 0x24, 0xd7, 0x12, 0xa8, 0x58, 0xcf, 0x8a,
- 0x50, 0x30, 0xe8, 0xc8, 0xb2, 0xf9, 0x58, 0x1c,
- 0x56, 0x53, 0x76, 0x21, 0xe0, 0x03, 0x7f, 0x77,
- 0xa7, 0xf1, 0xad, 0x67, 0xd4, 0xe2, 0x8f, 0xa0,
- 0x58, 0x6c, 0xe0, 0x28, 0x59, 0xf3, 0xd1, 0x53,
- 0x2b, 0x21, 0xbd, 0xa3, 0x84, 0x31, 0x73, 0xbf,
- 0x84, 0x0f, 0x83, 0xf4, 0xc4, 0xd0, 0xe5, 0x3c,
- 0x2d, 0x3e, 0xf2, 0x8a, 0x1e, 0xe7, 0xe9, 0x1f,
- 0x12, 0x13, 0xad, 0x29, 0xd6, 0x0c, 0xc7, 0xc6,
- 0x05, 0x53, 0x7d, 0x5e, 0xc6, 0x92, 0x72, 0xba,
- 0xd2, 0x93, 0x8f, 0x53, 0x84, 0x87, 0x44, 0x05,
- 0x9f, 0x5d, 0x66, 0x14, 0x03, 0x01, 0x00, 0x01,
- 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xfc, 0x71,
- 0xaa, 0xa8, 0x37, 0xa8, 0xbd, 0x63, 0xb7, 0xbc,
- 0x95, 0xef, 0x0c, 0xcf, 0x39, 0x31, 0x93, 0xe6,
- 0x86, 0xbd, 0x3f, 0x56, 0x9d, 0xf0, 0xb2, 0xb5,
- 0xd1, 0xa7, 0xc6, 0x45, 0x89, 0x18, 0xfb, 0xa0,
- 0x7f, 0xc1,
+ 0x82, 0x00, 0x80, 0x69, 0xc3, 0xd4, 0x0e, 0xcc,
+ 0xdc, 0xbc, 0x5e, 0xc2, 0x64, 0xa6, 0xde, 0x3c,
+ 0x0c, 0x7e, 0x0c, 0x6b, 0x80, 0x0f, 0xd4, 0x8f,
+ 0x02, 0x4b, 0xb2, 0xba, 0x8d, 0x01, 0xeb, 0x6b,
+ 0xa1, 0x2e, 0x79, 0x37, 0xba, 0xae, 0x24, 0xc2,
+ 0x26, 0x72, 0x51, 0xe1, 0x82, 0x8e, 0x51, 0x41,
+ 0x1c, 0x54, 0xa4, 0x26, 0xbe, 0x13, 0xcd, 0x1b,
+ 0xc6, 0xed, 0x3d, 0x1f, 0xfd, 0x72, 0x80, 0x90,
+ 0xdb, 0xbf, 0xd6, 0x39, 0x94, 0x5f, 0x48, 0xfb,
+ 0x25, 0x5a, 0xc9, 0x60, 0x9b, 0xd7, 0xc6, 0x20,
+ 0xa8, 0x66, 0x64, 0x13, 0xf3, 0x65, 0xc8, 0xb1,
+ 0xd5, 0x33, 0x21, 0x0e, 0x73, 0x41, 0xc0, 0x18,
+ 0x1a, 0x37, 0xfe, 0xcf, 0x28, 0x2a, 0xcd, 0xe4,
+ 0x0b, 0xac, 0xdd, 0x25, 0x5e, 0xcb, 0x17, 0x51,
+ 0x69, 0xd5, 0x8c, 0xf4, 0xb6, 0x21, 0x98, 0xef,
+ 0x20, 0xdb, 0x14, 0x67, 0xf3, 0x7c, 0x95, 0x6a,
+ 0x48, 0x2a, 0x6a, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0x36, 0x1b,
+ 0x09, 0xe5, 0xb9, 0xb9, 0x4d, 0x7d, 0xae, 0x87,
+ 0xb6, 0x0f, 0xaf, 0xec, 0x22, 0xba, 0x0d, 0xa5,
+ 0x96, 0x5e, 0x64, 0x65, 0xe7, 0xfb, 0xe3, 0xf3,
+ 0x6b, 0x72, 0xa8, 0xdb, 0xed, 0xd8, 0x69, 0x9c,
+ 0x08, 0xd8,
},
{
0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
- 0x01, 0x00, 0x24, 0xb8, 0x6d, 0x9a, 0x90, 0x3c,
- 0x45, 0xe0, 0xff, 0x63, 0xba, 0xab, 0x3d, 0x7a,
- 0xa6, 0x49, 0x5a, 0x13, 0xdc, 0x0e, 0xa3, 0xba,
- 0x7f, 0x04, 0x19, 0x45, 0xfd, 0xfb, 0xbd, 0x00,
- 0xa3, 0xa7, 0x78, 0x81, 0x38, 0x9f, 0x10, 0x17,
- 0x03, 0x01, 0x00, 0x21, 0x43, 0xc3, 0x91, 0xb7,
- 0xbf, 0x50, 0x0b, 0x04, 0xb4, 0x5d, 0xc6, 0x20,
- 0x64, 0xb8, 0x01, 0x09, 0x25, 0x2c, 0x03, 0x30,
- 0xc0, 0x77, 0xc9, 0x5e, 0xe6, 0xe0, 0x99, 0xdc,
- 0xcd, 0x75, 0x9d, 0x51, 0x82, 0x15, 0x03, 0x01,
- 0x00, 0x16, 0x2d, 0x7a, 0x89, 0x7b, 0x36, 0x85,
- 0x2a, 0x93, 0xcb, 0x83, 0xa7, 0x2f, 0x9e, 0x91,
- 0xfc, 0xad, 0x57, 0xca, 0xf5, 0xbc, 0x13, 0x2f,
+ 0x01, 0x00, 0x24, 0x60, 0xf7, 0x09, 0x5f, 0xd1,
+ 0xcb, 0xc9, 0xe1, 0x22, 0xb5, 0x2a, 0xcc, 0xde,
+ 0x7c, 0xa7, 0xb8, 0x85, 0x00, 0xbc, 0xfd, 0x85,
+ 0xe1, 0x91, 0x36, 0xbb, 0x07, 0x42, 0xad, 0x3d,
+ 0x29, 0x62, 0x69, 0xc1, 0x45, 0x92, 0x6f, 0x17,
+ 0x03, 0x01, 0x00, 0x21, 0x0d, 0xf9, 0xd5, 0x87,
+ 0xb9, 0x57, 0x3c, 0x50, 0x19, 0xe4, 0x3a, 0x50,
+ 0x45, 0xcc, 0x86, 0x89, 0xd4, 0x32, 0x79, 0x45,
+ 0x7c, 0x9f, 0x96, 0xd4, 0x54, 0x56, 0x0c, 0x63,
+ 0x72, 0x81, 0xc3, 0xd3, 0xe3, 0x15, 0x03, 0x01,
+ 0x00, 0x16, 0x84, 0xec, 0x2e, 0xf6, 0xaf, 0x4f,
+ 0xee, 0x48, 0x0f, 0xbe, 0xcd, 0x82, 0x5c, 0x56,
+ 0x16, 0xe4, 0xfb, 0x89, 0xc5, 0x57, 0x3e, 0x91,
},
}
-var clientauthTests = []clientauthTest{
- // Server doesn't asks for cert
- // go test -run "TestRunServer" -serve -clientauth 0
- // gnutls-cli --insecure --debug 100 -p 10443 localhost 2>&1 |
- // python parse-gnutls-cli-debug-log.py
- {"NoClientCert", NoClientCert, nil,
- [][]byte{{
- 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00,
- 0x76, 0x03, 0x02, 0x4e, 0xe0, 0x92, 0x5d, 0xcd,
- 0xfe, 0x0c, 0x69, 0xd4, 0x7d, 0x8e, 0xa6, 0x88,
- 0xde, 0x72, 0x04, 0x29, 0x6a, 0x4a, 0x16, 0x23,
- 0xd7, 0x8f, 0xbc, 0xfa, 0x80, 0x73, 0x2e, 0x12,
- 0xb7, 0x0b, 0x39, 0x00, 0x00, 0x34, 0x00, 0x33,
- 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16,
- 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87,
- 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91,
- 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41,
- 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05,
- 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b,
- 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09,
- 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f,
- 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
- },
+var issueSessionTicketTest = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x5a, 0x01, 0x00, 0x00,
+ 0x56, 0x03, 0x01, 0x50, 0x77, 0x3e, 0x49, 0x7a,
+ 0xb7, 0x86, 0x5c, 0x27, 0xd2, 0x97, 0x61, 0xe3,
+ 0x49, 0x41, 0x48, 0xe7, 0x0e, 0xaa, 0x7e, 0x4d,
+ 0xb8, 0xdc, 0x01, 0x97, 0xfb, 0xab, 0x53, 0xb2,
+ 0x5e, 0x36, 0xf6, 0x00, 0x00, 0x28, 0x00, 0x39,
+ 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13,
+ 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f,
+ 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
+ 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,
+ 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01,
+ 0x00, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00,
+ 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+ 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01,
+ 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02,
+ 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0,
+ 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4,
+ 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
+ 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+ 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+ 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
+ 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+ 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
+ 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39,
+ 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
+ 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33,
+ 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
+ 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+ 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
+ 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
+ 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+ 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79,
+ 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10,
+ 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43,
+ 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85,
+ 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c,
+ 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5,
+ 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c,
+ 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56,
+ 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26,
+ 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21,
+ 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf,
+ 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07,
+ 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39,
+ 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3,
+ 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf,
+ 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb,
+ 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+ 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85,
+ 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23,
+ 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39,
+ 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2,
+ 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce,
+ 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88,
+ 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85,
+ 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59,
+ 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7,
+ 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95,
+ 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66,
+ 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3,
+ 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13,
+ 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba,
+ 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31,
+ 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50,
+ 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f,
+ 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96,
+ 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f,
+ 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b,
+ 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70,
+ 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e,
+ 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9,
+ 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00,
+ 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
+ 0x82, 0x00, 0x80, 0x68, 0x10, 0xdc, 0x80, 0xbc,
+ 0xb3, 0x5a, 0x10, 0x75, 0x89, 0xcc, 0xe5, 0x9f,
+ 0xbf, 0xe2, 0xce, 0xa4, 0x9f, 0x7f, 0x60, 0xc4,
+ 0xfe, 0x5c, 0xb5, 0x02, 0x2d, 0xa5, 0xa9, 0x1e,
+ 0x2c, 0x10, 0x79, 0x15, 0x0f, 0xed, 0x96, 0xb3,
+ 0xa8, 0x5e, 0x21, 0xbc, 0x5b, 0xdc, 0x58, 0x04,
+ 0x7d, 0x37, 0xdb, 0xa0, 0x31, 0xe8, 0x4f, 0x04,
+ 0xbc, 0x46, 0x7c, 0xdb, 0x2e, 0x93, 0x07, 0xaf,
+ 0xa6, 0x36, 0xd3, 0x39, 0x8d, 0x1d, 0x95, 0xa8,
+ 0x50, 0x4b, 0xc4, 0x2b, 0xde, 0xd7, 0x04, 0x6d,
+ 0x77, 0x6c, 0x4d, 0x70, 0x51, 0x88, 0x16, 0x31,
+ 0x40, 0xb5, 0xba, 0x90, 0x47, 0x64, 0x0c, 0x87,
+ 0xa5, 0x19, 0xf9, 0x89, 0x24, 0x3c, 0x5e, 0x4b,
+ 0xaa, 0xe0, 0x60, 0x47, 0x0f, 0x2e, 0xcc, 0xc2,
+ 0xd5, 0x21, 0xed, 0x72, 0xd0, 0xa9, 0xdd, 0x2a,
+ 0x2b, 0xef, 0x08, 0x3a, 0x65, 0xea, 0x8b, 0x52,
+ 0x77, 0x2d, 0xcc, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xe2, 0x95,
+ 0x62, 0x3c, 0x18, 0xe5, 0xc7, 0x2c, 0xda, 0x16,
+ 0x9b, 0x28, 0x0d, 0xf7, 0x88, 0x7b, 0x5d, 0x33,
+ 0x55, 0x3b, 0x01, 0x73, 0xf2, 0xc6, 0x4e, 0x96,
+ 0x01, 0x01, 0x83, 0x65, 0xd4, 0xef, 0x12, 0x13,
+ 0x1d, 0x42,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+ 0xe8, 0x4b, 0xd1, 0xef, 0xba, 0xfb, 0x41, 0x92,
+ 0x6d, 0x37, 0x5f, 0xf8, 0x7d, 0x90, 0x0f, 0x01,
+ 0xf8, 0x8c, 0xee, 0xbc, 0xd9, 0x0c, 0x97, 0x7e,
+ 0x23, 0x46, 0xe2, 0x6b, 0x52, 0xc6, 0xc6, 0x97,
+ 0x1d, 0xab, 0xde, 0xa0, 0x86, 0x94, 0xc8, 0x2e,
+ 0x8b, 0x2e, 0x42, 0x5f, 0xc2, 0x70, 0x35, 0xc9,
+ 0xee, 0x37, 0xeb, 0x70, 0xaa, 0x59, 0x23, 0x6c,
+ 0xc8, 0xc1, 0x84, 0x89, 0x39, 0x87, 0x73, 0x0a,
+ 0x7e, 0xba, 0xca, 0xed, 0x63, 0xba, 0x4e, 0x4f,
+ 0xf3, 0x31, 0x4b, 0xf0, 0xee, 0x91, 0xa5, 0xb4,
+ 0x62, 0x01, 0x9e, 0xbd, 0xbc, 0xb3, 0x35, 0x14,
+ 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
+ 0x00, 0x24, 0x3f, 0x66, 0xe4, 0x98, 0xc1, 0x3f,
+ 0xc6, 0x2c, 0x81, 0xfb, 0xa9, 0x9f, 0x27, 0xe9,
+ 0x63, 0x20, 0x1e, 0x0e, 0x4f, 0xfc, 0x5d, 0x12,
+ 0xee, 0x77, 0x73, 0xc6, 0x96, 0x51, 0xf2, 0x26,
+ 0x35, 0x3f, 0xce, 0x6a, 0xa9, 0xfd, 0x17, 0x03,
+ 0x01, 0x00, 0x21, 0x8d, 0xd5, 0x67, 0x60, 0x5d,
+ 0xa7, 0x93, 0xcc, 0x39, 0x78, 0x59, 0xab, 0xdb,
+ 0x10, 0x96, 0xf2, 0xad, 0xa2, 0x85, 0xe2, 0x93,
+ 0x43, 0x43, 0xcf, 0x82, 0xbd, 0x1f, 0xdc, 0x7a,
+ 0x72, 0xd6, 0x83, 0x3b, 0x15, 0x03, 0x01, 0x00,
+ 0x16, 0x89, 0x55, 0xf6, 0x42, 0x71, 0xa9, 0xe9,
+ 0x05, 0x68, 0xe8, 0xce, 0x0d, 0x21, 0xe9, 0xec,
+ 0xf2, 0x27, 0x67, 0xa7, 0x94, 0xf8, 0x34,
+ },
+}
+var serverResumeTest = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0xc2, 0x01, 0x00, 0x00,
+ 0xbe, 0x03, 0x01, 0x50, 0x77, 0x3e, 0x4f, 0x1f,
+ 0x6f, 0xa5, 0x81, 0xeb, 0xb8, 0x80, 0x55, 0xa4,
+ 0x76, 0xc2, 0x7f, 0x27, 0xf2, 0xe7, 0xc9, 0x7a,
+ 0x01, 0x3c, 0xd8, 0xc1, 0xde, 0x99, 0x1f, 0x7c,
+ 0xab, 0x35, 0x98, 0x00, 0x00, 0x28, 0x00, 0x39,
+ 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13,
+ 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f,
+ 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
+ 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,
+ 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01,
+ 0x00, 0x00, 0x6c, 0x00, 0x23, 0x00, 0x68, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+ 0xe8, 0x4b, 0xd1, 0xef, 0xba, 0xfb, 0x41, 0x92,
+ 0x6d, 0x37, 0x5f, 0xf8, 0x7d, 0x90, 0x0f, 0x01,
+ 0xf8, 0x8c, 0xee, 0xbc, 0xd9, 0x0c, 0x97, 0x7e,
+ 0x23, 0x46, 0xe2, 0x6b, 0x52, 0xc6, 0xc6, 0x97,
+ 0x1d, 0xab, 0xde, 0xa0, 0x86, 0x94, 0xc8, 0x2e,
+ 0x8b, 0x2e, 0x42, 0x5f, 0xc2, 0x70, 0x35, 0xc9,
+ 0xee, 0x37, 0xeb, 0x70, 0xaa, 0x59, 0x23, 0x6c,
+ 0xc8, 0xc1, 0x84, 0x89, 0x39, 0x87, 0x73, 0x0a,
+ 0x7e, 0xba, 0xca, 0xed, 0x63, 0xba, 0x4e, 0x4f,
+ 0xf3, 0x31, 0x4b, 0xf0, 0xee, 0x91, 0xa5, 0xb4,
+ 0x62, 0x01, 0x9e, 0xbd, 0xbc, 0xb3, 0x35,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
+ 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x14,
+ 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
+ 0x00, 0x24, 0xc5, 0x35, 0x74, 0x19, 0x05, 0xc5,
+ 0x85, 0x68, 0x48, 0xe8, 0xb5, 0xe9, 0xaf, 0x78,
+ 0xbd, 0x35, 0x6f, 0xe9, 0x79, 0x34, 0x1b, 0xf0,
+ 0x35, 0xd4, 0x4e, 0x55, 0x2e, 0x3c, 0xd5, 0xaf,
+ 0xfc, 0xba, 0xf5, 0x1e, 0x83, 0x32,
+ },
+ {
+ 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x01, 0x00, 0x24, 0x27, 0x28, 0x88, 0xe1, 0x7e,
+ 0x0d, 0x9c, 0x12, 0x50, 0xf6, 0x7a, 0xa7, 0x32,
+ 0x21, 0x68, 0xba, 0xd8, 0x0a, 0xdc, 0x39, 0xef,
+ 0x68, 0x95, 0x82, 0xae, 0xbd, 0x12, 0x79, 0xa1,
+ 0x99, 0xfd, 0xd0, 0x10, 0x8e, 0x4b, 0xd8,
+ },
+ {
+ 0x17, 0x03, 0x01, 0x00, 0x21, 0xc5, 0x7e, 0x0a,
+ 0x52, 0x6a, 0xb9, 0xaa, 0x1d, 0xae, 0x9e, 0x24,
+ 0x9c, 0x34, 0x1e, 0xdb, 0x50, 0x95, 0xee, 0x76,
+ 0xd7, 0x28, 0x88, 0x08, 0xe3, 0x2e, 0x58, 0xf7,
+ 0xdb, 0x34, 0x75, 0xa5, 0x7f, 0x9d, 0x15, 0x03,
+ 0x01, 0x00, 0x16, 0x2c, 0xc1, 0x29, 0x5f, 0x12,
+ 0x1d, 0x19, 0xab, 0xb3, 0xf4, 0x35, 0x1c, 0x62,
+ 0x6a, 0x80, 0x29, 0x0d, 0x0e, 0xef, 0x7d, 0x6e,
+ 0x50,
+ },
+}
- {
- 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
- 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16,
- 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
- 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
- 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
- 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
- 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
- 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
- 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
- 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
- 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
- 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
- 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
- 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
- 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
- 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
- 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
- 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
- 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
- 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
- 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
- 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
- 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
- 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
- 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
- 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
- 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
- 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
- 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
- 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
- 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
- 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
- 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
- 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
- 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
- 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
- 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
- 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
- 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
- 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
- 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
- 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
- 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
- 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
- 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
- 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
- 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
- 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
- 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
- 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
- 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
- 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
- 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
- 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
- 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
- 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
- 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
- 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
- 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
- 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
- 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
- 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
- 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
- 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
- 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
- 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
- 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
- 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
- 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
- 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
- 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
- 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
- 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
- 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
- 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
- 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
- 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
- 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
- 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
- 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
- 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
- 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
- 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
- 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
- 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
- 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e,
- 0x00, 0x00, 0x00,
- },
-
- {
- 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
- 0x82, 0x00, 0x80, 0x10, 0xe1, 0x00, 0x3d, 0x0a,
- 0x6b, 0x02, 0x7f, 0x97, 0xde, 0xfb, 0x65, 0x46,
- 0x1a, 0x50, 0x4e, 0x34, 0x9a, 0xae, 0x14, 0x7e,
- 0xec, 0xef, 0x85, 0x15, 0x3b, 0x39, 0xc2, 0x45,
- 0x04, 0x40, 0x92, 0x71, 0xd6, 0x7e, 0xf6, 0xfd,
- 0x4d, 0x84, 0xf7, 0xc4, 0x77, 0x99, 0x3d, 0xe2,
- 0xc3, 0x8d, 0xb0, 0x4c, 0x74, 0xc8, 0x51, 0xec,
- 0xb2, 0xe8, 0x6b, 0xa1, 0xd2, 0x4d, 0xd8, 0x61,
- 0x92, 0x7a, 0x24, 0x57, 0x44, 0x4f, 0xa2, 0x1e,
- 0x74, 0x0b, 0x06, 0x4b, 0x80, 0x34, 0x8b, 0xfe,
- 0xc2, 0x0e, 0xc1, 0xcd, 0xab, 0x0c, 0x3f, 0x54,
- 0xe2, 0x44, 0xe9, 0x6c, 0x2b, 0xba, 0x7b, 0x64,
- 0xf1, 0x93, 0x65, 0x75, 0xf2, 0x35, 0xff, 0x27,
- 0x03, 0xd5, 0x64, 0xe6, 0x8e, 0xe7, 0x7b, 0x56,
- 0xb6, 0x61, 0x73, 0xeb, 0xa2, 0xdc, 0xa4, 0x6e,
- 0x52, 0xac, 0xbc, 0xba, 0x11, 0xa3, 0xd2, 0x61,
- 0x4a, 0xe0, 0xbb, 0x14, 0x03, 0x01, 0x00, 0x01,
- 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xd2, 0x5a,
- 0x0c, 0x2a, 0x27, 0x96, 0xba, 0xa9, 0x67, 0xd2,
- 0x51, 0x68, 0x32, 0x68, 0x22, 0x1f, 0xb9, 0x27,
- 0x79, 0x59, 0x28, 0xdf, 0x38, 0x1f, 0x92, 0x21,
- 0x5d, 0x0f, 0xf4, 0xc0, 0xee, 0xb7, 0x10, 0x5a,
- 0xa9, 0x45,
- },
-
- {
- 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
- 0x01, 0x00, 0x24, 0x13, 0x6f, 0x6c, 0x71, 0x83,
- 0x59, 0xcf, 0x32, 0x72, 0xe9, 0xce, 0xcc, 0x7a,
- 0x6c, 0xf0, 0x72, 0x39, 0x16, 0xae, 0x40, 0x61,
- 0xfa, 0x92, 0x4c, 0xe7, 0xf2, 0x1a, 0xd7, 0x0c,
- 0x84, 0x76, 0x6c, 0xe9, 0x11, 0x43, 0x19, 0x17,
- 0x03, 0x01, 0x00, 0x21, 0xc0, 0xa2, 0x13, 0x28,
- 0x94, 0x8c, 0x5c, 0xd6, 0x79, 0xb9, 0xfe, 0xae,
- 0x45, 0x4b, 0xc0, 0x7c, 0xae, 0x2d, 0xb4, 0x0d,
- 0x31, 0xc4, 0xad, 0x22, 0xd7, 0x1e, 0x99, 0x1c,
- 0x4c, 0x69, 0xab, 0x42, 0x61, 0x15, 0x03, 0x01,
- 0x00, 0x16, 0xe1, 0x0c, 0x67, 0xf3, 0xf4, 0xb9,
- 0x8e, 0x81, 0x8e, 0x01, 0xb8, 0xa0, 0x69, 0x8c,
- 0x03, 0x11, 0x43, 0x3e, 0xee, 0xb7, 0x4d, 0x69,
- }}},
+var clientauthTests = []clientauthTest{
// Server asks for cert with empty CA list, client doesn't give it.
// go test -run "TestRunServer" -serve -clientauth 1
- // gnutls-cli --insecure --debug 100 -p 10443 localhost
- {"RequestClientCert, none given", RequestClientCert, nil,
- [][]byte{{
- 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00,
- 0x76, 0x03, 0x02, 0x4e, 0xe0, 0x93, 0xe2, 0x47,
- 0x06, 0xa0, 0x61, 0x0c, 0x51, 0xdd, 0xf0, 0xef,
- 0xf4, 0x30, 0x72, 0xe1, 0xa6, 0x50, 0x68, 0x82,
- 0x3c, 0xfb, 0xcb, 0x72, 0x5e, 0x73, 0x9d, 0xda,
- 0x27, 0x35, 0x72, 0x00, 0x00, 0x34, 0x00, 0x33,
- 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16,
- 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87,
- 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91,
- 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41,
- 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05,
- 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b,
- 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09,
- 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f,
- 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
+ {"RequestClientCert, none given", RequestClientCert, nil, [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00,
+ 0x50, 0x03, 0x01, 0x50, 0x77, 0x43, 0x9e, 0x31,
+ 0xe6, 0x36, 0x5e, 0x5e, 0x24, 0xe4, 0x0d, 0x26,
+ 0x34, 0xa7, 0x1c, 0x2e, 0x59, 0x6d, 0xa5, 0x3e,
+ 0x72, 0xf3, 0xa3, 0x1c, 0xbc, 0xb3, 0x27, 0xaf,
+ 0x92, 0x5b, 0x7d, 0x00, 0x00, 0x28, 0x00, 0x39,
+ 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13,
+ 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f,
+ 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
+ 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,
+ 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01,
+ 0x00,
},
-
- {
- 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
- 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16,
- 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
- 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
- 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
- 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
- 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
- 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
- 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
- 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
- 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
- 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
- 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
- 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
- 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
- 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
- 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
- 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
- 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
- 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
- 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
- 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
- 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
- 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
- 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
- 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
- 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
- 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
- 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
- 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
- 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
- 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
- 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
- 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
- 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
- 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
- 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
- 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
- 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
- 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
- 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
- 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
- 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
- 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
- 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
- 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
- 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
- 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
- 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
- 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
- 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
- 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
- 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
- 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
- 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
- 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
- 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
- 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
- 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
- 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
- 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
- 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
- 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
- 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
- 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
- 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
- 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
- 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
- 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
- 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
- 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
- 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
- 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
- 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
- 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
- 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
- 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
- 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
- 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
- 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
- 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
- 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
- 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
- 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
- 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
- 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d,
- 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16,
- 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
- },
-
- {
- 0x16, 0x03, 0x01, 0x00, 0x07, 0x0b, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00,
- 0x86, 0x10, 0x00, 0x00, 0x82, 0x00, 0x80, 0x64,
- 0x28, 0xb9, 0x3f, 0x48, 0xaf, 0x06, 0x22, 0x39,
- 0x56, 0xd8, 0x6f, 0x63, 0x5d, 0x03, 0x48, 0x63,
- 0x01, 0x13, 0xa2, 0xd6, 0x76, 0xc0, 0xab, 0xda,
- 0x25, 0x30, 0x75, 0x6c, 0xaa, 0xb4, 0xdc, 0x35,
- 0x72, 0xdc, 0xf2, 0x43, 0xe4, 0x1d, 0x82, 0xfb,
- 0x6c, 0x64, 0xe2, 0xa7, 0x8f, 0x32, 0x67, 0x6b,
- 0xcd, 0xd2, 0xb2, 0x36, 0x94, 0xbc, 0x6f, 0x46,
- 0x79, 0x29, 0x42, 0xe3, 0x1a, 0xbf, 0xfb, 0x41,
- 0xd5, 0xe3, 0xb4, 0x2a, 0xf6, 0x95, 0x6f, 0x0c,
- 0x87, 0xb9, 0x03, 0x18, 0xa1, 0xea, 0x4a, 0xe2,
- 0x2e, 0x0f, 0x50, 0x00, 0xc1, 0xe8, 0x8c, 0xc8,
- 0xa2, 0xf6, 0xa4, 0x05, 0xf4, 0x38, 0x3e, 0xd9,
- 0x6e, 0x63, 0x96, 0x0c, 0x34, 0x73, 0x90, 0x03,
- 0x55, 0xa6, 0x34, 0xb0, 0x5e, 0x8c, 0x48, 0x40,
- 0x25, 0x45, 0x84, 0xa6, 0x21, 0x3f, 0x81, 0x97,
- 0xa7, 0x11, 0x09, 0x14, 0x95, 0xa5, 0xe5, 0x14,
- 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
- 0x00, 0x24, 0x16, 0xaa, 0x01, 0x2c, 0xa8, 0xc1,
- 0x28, 0xaf, 0x35, 0xc1, 0xc1, 0xf3, 0x0a, 0x25,
- 0x66, 0x6e, 0x27, 0x11, 0xa3, 0xa4, 0xd9, 0xe9,
- 0xea, 0x15, 0x09, 0x9d, 0x28, 0xe3, 0x5b, 0x2b,
- 0xa6, 0x25, 0xa7, 0x14, 0x24, 0x3a,
- },
-
- {
- 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
- 0x01, 0x00, 0x24, 0x9a, 0xa8, 0xd6, 0x77, 0x46,
- 0x45, 0x68, 0x9d, 0x5d, 0xa9, 0x68, 0x03, 0xe5,
- 0xaf, 0xe8, 0xc8, 0x21, 0xc5, 0xc6, 0xc1, 0x50,
- 0xe0, 0xd8, 0x52, 0xce, 0xa3, 0x4f, 0x2d, 0xf4,
- 0xe3, 0xa7, 0x7d, 0x35, 0x80, 0x84, 0x12, 0x17,
- 0x03, 0x01, 0x00, 0x21, 0x8a, 0x82, 0x0c, 0x54,
- 0x1b, 0xeb, 0x77, 0x90, 0x2c, 0x3e, 0xbc, 0xf0,
- 0x23, 0xcc, 0xa8, 0x9f, 0x25, 0x08, 0x12, 0xed,
- 0x43, 0xf1, 0xf9, 0x06, 0xad, 0xa9, 0x4b, 0x97,
- 0x82, 0xb7, 0xc4, 0x0b, 0x4c, 0x15, 0x03, 0x01,
- 0x00, 0x16, 0x05, 0x2d, 0x9d, 0x45, 0x03, 0xb7,
- 0xc2, 0xd1, 0xb5, 0x1a, 0x43, 0xcf, 0x1a, 0x37,
- 0xf4, 0x70, 0xcc, 0xb4, 0xed, 0x07, 0x76, 0x3a,
- }}},
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
+ 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16,
+ 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
+ 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
+ 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
+ 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
+ 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
+ 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
+ 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
+ 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
+ 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
+ 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
+ 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
+ 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
+ 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
+ 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
+ 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
+ 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
+ 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
+ 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
+ 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
+ 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
+ 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
+ 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
+ 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
+ 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
+ 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
+ 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
+ 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
+ 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
+ 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
+ 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
+ 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
+ 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
+ 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
+ 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
+ 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
+ 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
+ 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
+ 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
+ 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
+ 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
+ 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
+ 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
+ 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
+ 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
+ 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
+ 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
+ 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
+ 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
+ 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
+ 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
+ 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
+ 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
+ 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
+ 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
+ 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
+ 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
+ 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
+ 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
+ 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
+ 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d,
+ 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16,
+ 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x07, 0x0b, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00,
+ 0x86, 0x10, 0x00, 0x00, 0x82, 0x00, 0x80, 0x04,
+ 0x58, 0x63, 0x26, 0x32, 0x1b, 0x34, 0xbe, 0x10,
+ 0xe4, 0xe4, 0x3e, 0xcd, 0x36, 0x7f, 0xa8, 0xa8,
+ 0xe0, 0x19, 0xe8, 0x94, 0x13, 0xd9, 0x35, 0xc4,
+ 0x71, 0xb4, 0x91, 0xd4, 0xbc, 0x74, 0x57, 0x9f,
+ 0x93, 0xb7, 0x5d, 0x3b, 0x9c, 0xff, 0x5d, 0x79,
+ 0xdb, 0x86, 0xfc, 0xdc, 0x74, 0x1e, 0x0c, 0xc6,
+ 0xe8, 0x93, 0xcf, 0xaf, 0xba, 0x1d, 0xfd, 0x8a,
+ 0xeb, 0xef, 0xbf, 0xfa, 0xa6, 0xe7, 0x53, 0x98,
+ 0x60, 0x4e, 0x0e, 0x60, 0x7d, 0xea, 0x40, 0x8d,
+ 0x1d, 0x8f, 0xa3, 0xc6, 0x83, 0xbc, 0xef, 0xb7,
+ 0x9a, 0x4a, 0xe7, 0x99, 0xee, 0x0b, 0xc7, 0x46,
+ 0x75, 0x45, 0x66, 0xe8, 0x5f, 0x4b, 0x08, 0xa4,
+ 0xc1, 0x36, 0xd0, 0x36, 0x2c, 0xf2, 0x9a, 0x44,
+ 0x1e, 0x5f, 0x22, 0xf4, 0xbe, 0x66, 0x66, 0x17,
+ 0xd8, 0xb6, 0x0a, 0x89, 0xed, 0x22, 0x80, 0xdb,
+ 0xad, 0x05, 0xd1, 0xb5, 0x93, 0xa1, 0x1c, 0x14,
+ 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
+ 0x00, 0x24, 0x62, 0x6f, 0x3d, 0x30, 0x56, 0x97,
+ 0xde, 0x03, 0x67, 0xa9, 0x63, 0x21, 0xb6, 0xe6,
+ 0x05, 0x69, 0x94, 0xfb, 0x50, 0xc1, 0x99, 0xdd,
+ 0xf6, 0xe8, 0x60, 0xbd, 0xe6, 0xba, 0xe3, 0x50,
+ 0x0a, 0xcd, 0xde, 0x14, 0x16, 0xc4,
+ },
+ {
+ 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x01, 0x00, 0x24, 0xf0, 0x21, 0xf6, 0x84, 0x6a,
+ 0xe3, 0x6b, 0x8a, 0xc5, 0x46, 0x50, 0xca, 0x40,
+ 0xea, 0x4e, 0x82, 0xc1, 0x70, 0x25, 0xd8, 0x7d,
+ 0x60, 0xf5, 0x51, 0x7f, 0x64, 0x03, 0x9f, 0x53,
+ 0xec, 0xfb, 0x57, 0xa9, 0xfc, 0x26, 0x15, 0x17,
+ 0x03, 0x01, 0x00, 0x21, 0xa6, 0xc6, 0x94, 0x2b,
+ 0xa9, 0xcb, 0x93, 0xff, 0xb6, 0xa6, 0xe7, 0xc5,
+ 0x37, 0x86, 0x15, 0x37, 0x57, 0xce, 0xef, 0x54,
+ 0x96, 0x5d, 0x50, 0xa0, 0x50, 0x69, 0x5e, 0x82,
+ 0x61, 0x8d, 0x42, 0xfb, 0x78, 0x15, 0x03, 0x01,
+ 0x00, 0x16, 0x45, 0xd1, 0x86, 0x68, 0x59, 0xc1,
+ 0xaf, 0xac, 0x5c, 0x46, 0x8a, 0x68, 0x69, 0x0c,
+ 0xd7, 0x67, 0xbf, 0xf0, 0x3e, 0xee, 0x45, 0x55,
+ },
+ }},
// Server asks for cert with empty CA list, client gives one
// go test -run "TestRunServer" -serve -clientauth 1
- // gnutls-cli --insecure --debug 100 -p 10443 localhost
- {"RequestClientCert, client gives it", RequestClientCert,
- []*x509.Certificate{clientCertificate},
- [][]byte{{
- 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00,
- 0x76, 0x03, 0x02, 0x4e, 0xe7, 0x44, 0xda, 0x58,
- 0x7d, 0x46, 0x4a, 0x48, 0x97, 0x9f, 0xe5, 0x91,
- 0x11, 0x64, 0xa7, 0x1e, 0x4d, 0xb7, 0xfe, 0x9b,
- 0xc6, 0x63, 0xf8, 0xa4, 0xb5, 0x0b, 0x18, 0xb5,
- 0xbd, 0x19, 0xb3, 0x00, 0x00, 0x34, 0x00, 0x33,
- 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16,
- 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87,
- 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91,
- 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41,
- 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05,
- 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b,
- 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09,
- 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f,
- 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
+ {"RequestClientCert, client gives it", RequestClientCert, []*x509.Certificate{clientCertificate}, [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00,
+ 0x50, 0x03, 0x01, 0x50, 0x77, 0x43, 0x47, 0xfd,
+ 0x1d, 0xb0, 0x60, 0x4c, 0x25, 0x86, 0x45, 0x4a,
+ 0xe5, 0x3f, 0x80, 0x56, 0x18, 0x91, 0x5c, 0xe2,
+ 0x62, 0xc5, 0x77, 0xc2, 0x92, 0xdd, 0xdc, 0x39,
+ 0x23, 0x1d, 0xc5, 0x00, 0x00, 0x28, 0x00, 0x39,
+ 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13,
+ 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f,
+ 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
+ 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,
+ 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01,
+ 0x00,
},
-
- {
- 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
- 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16,
- 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
- 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
- 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
- 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
- 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
- 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
- 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
- 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
- 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
- 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
- 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
- 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
- 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
- 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
- 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
- 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
- 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
- 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
- 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
- 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
- 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
- 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
- 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
- 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
- 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
- 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
- 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
- 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
- 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
- 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
- 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
- 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
- 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
- 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
- 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
- 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
- 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
- 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
- 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
- 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
- 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
- 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
- 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
- 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
- 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
- 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
- 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
- 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
- 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
- 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
- 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
- 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
- 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
- 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
- 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
- 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
- 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
- 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
- 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
- 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
- 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
- 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
- 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
- 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
- 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
- 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
- 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
- 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
- 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
- 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
- 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
- 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
- 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
- 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
- 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
- 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
- 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
- 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
- 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
- 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
- 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
- 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
- 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
- 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
- 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d,
- 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16,
- 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
- },
-
- {
- 0x16, 0x03, 0x01, 0x01, 0xfb, 0x0b, 0x00, 0x01,
- 0xf7, 0x00, 0x01, 0xf4, 0x00, 0x01, 0xf1, 0x30,
- 0x82, 0x01, 0xed, 0x30, 0x82, 0x01, 0x58, 0xa0,
- 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x00, 0x30,
- 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x01, 0x05, 0x30, 0x26, 0x31, 0x10,
- 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
- 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f,
- 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04,
- 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30,
- 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x1e, 0x17, 0x0d,
- 0x31, 0x31, 0x31, 0x32, 0x30, 0x38, 0x30, 0x37,
- 0x35, 0x35, 0x31, 0x32, 0x5a, 0x17, 0x0d, 0x31,
- 0x32, 0x31, 0x32, 0x30, 0x37, 0x30, 0x38, 0x30,
- 0x30, 0x31, 0x32, 0x5a, 0x30, 0x26, 0x31, 0x10,
- 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
- 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f,
- 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04,
- 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30,
- 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, 0x9c, 0x30,
- 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x01, 0x01, 0x03, 0x81, 0x8c, 0x00,
- 0x30, 0x81, 0x88, 0x02, 0x81, 0x80, 0x4e, 0xd0,
- 0x7b, 0x31, 0xe3, 0x82, 0x64, 0xd9, 0x59, 0xc0,
- 0xc2, 0x87, 0xa4, 0x5e, 0x1e, 0x8b, 0x73, 0x33,
- 0xc7, 0x63, 0x53, 0xdf, 0x66, 0x92, 0x06, 0x84,
- 0xf6, 0x64, 0xd5, 0x8f, 0xe4, 0x36, 0xa7, 0x1d,
- 0x2b, 0xe8, 0xb3, 0x20, 0x36, 0x45, 0x23, 0xb5,
- 0xe3, 0x95, 0xae, 0xed, 0xe0, 0xf5, 0x20, 0x9c,
- 0x8d, 0x95, 0xdf, 0x7f, 0x5a, 0x12, 0xef, 0x87,
- 0xe4, 0x5b, 0x68, 0xe4, 0xe9, 0x0e, 0x74, 0xec,
- 0x04, 0x8a, 0x7f, 0xde, 0x93, 0x27, 0xc4, 0x01,
- 0x19, 0x7a, 0xbd, 0xf2, 0xdc, 0x3d, 0x14, 0xab,
- 0xd0, 0x54, 0xca, 0x21, 0x0c, 0xd0, 0x4d, 0x6e,
- 0x87, 0x2e, 0x5c, 0xc5, 0xd2, 0xbb, 0x4d, 0x4b,
- 0x4f, 0xce, 0xb6, 0x2c, 0xf7, 0x7e, 0x88, 0xec,
- 0x7c, 0xd7, 0x02, 0x91, 0x74, 0xa6, 0x1e, 0x0c,
- 0x1a, 0xda, 0xe3, 0x4a, 0x5a, 0x2e, 0xde, 0x13,
- 0x9c, 0x4c, 0x40, 0x88, 0x59, 0x93, 0x02, 0x03,
- 0x01, 0x00, 0x01, 0xa3, 0x32, 0x30, 0x30, 0x30,
- 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
- 0xff, 0x04, 0x04, 0x03, 0x02, 0x00, 0xa0, 0x30,
- 0x0d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x06,
- 0x04, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30, 0x0f,
- 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x08, 0x30,
- 0x06, 0x80, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30,
- 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x01, 0x05, 0x03, 0x81, 0x81, 0x00,
- 0x36, 0x1f, 0xb3, 0x7a, 0x0c, 0x75, 0xc9, 0x6e,
- 0x37, 0x46, 0x61, 0x2b, 0xd5, 0xbd, 0xc0, 0xa7,
- 0x4b, 0xcc, 0x46, 0x9a, 0x81, 0x58, 0x7c, 0x85,
- 0x79, 0x29, 0xc8, 0xc8, 0xc6, 0x67, 0xdd, 0x32,
- 0x56, 0x45, 0x2b, 0x75, 0xb6, 0xe9, 0x24, 0xa9,
- 0x50, 0x9a, 0xbe, 0x1f, 0x5a, 0xfa, 0x1a, 0x15,
- 0xd9, 0xcc, 0x55, 0x95, 0x72, 0x16, 0x83, 0xb9,
- 0xc2, 0xb6, 0x8f, 0xfd, 0x88, 0x8c, 0x38, 0x84,
- 0x1d, 0xab, 0x5d, 0x92, 0x31, 0x13, 0x4f, 0xfd,
- 0x83, 0x3b, 0xc6, 0x9d, 0xf1, 0x11, 0x62, 0xb6,
- 0x8b, 0xec, 0xab, 0x67, 0xbe, 0xc8, 0x64, 0xb0,
- 0x11, 0x50, 0x46, 0x58, 0x17, 0x6b, 0x99, 0x1c,
- 0xd3, 0x1d, 0xfc, 0x06, 0xf1, 0x0e, 0xe5, 0x96,
- 0xa8, 0x0c, 0xf9, 0x78, 0x20, 0xb7, 0x44, 0x18,
- 0x51, 0x8d, 0x10, 0x7e, 0x4f, 0x94, 0x67, 0xdf,
- 0xa3, 0x4e, 0x70, 0x73, 0x8e, 0x90, 0x91, 0x85,
- 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
- 0x82, 0x00, 0x80, 0xa7, 0x2f, 0xed, 0xfa, 0xc2,
- 0xbd, 0x46, 0xa1, 0xf2, 0x69, 0xc5, 0x1d, 0xa1,
- 0x34, 0xd6, 0xd0, 0x84, 0xf5, 0x5d, 0x8c, 0x82,
- 0x8d, 0x98, 0x82, 0x9c, 0xd9, 0x07, 0xe0, 0xf7,
- 0x55, 0x49, 0x4d, 0xa1, 0x48, 0x59, 0x02, 0xd3,
- 0x84, 0x37, 0xaf, 0x01, 0xb3, 0x3a, 0xf4, 0xed,
- 0x99, 0xbe, 0x67, 0x36, 0x19, 0x55, 0xf3, 0xf9,
- 0xcb, 0x94, 0xe5, 0x7b, 0x8b, 0x77, 0xf2, 0x5f,
- 0x4c, 0xfe, 0x01, 0x1f, 0x7b, 0xd7, 0x23, 0x49,
- 0x0c, 0xcb, 0x6c, 0xb0, 0xe7, 0x77, 0xd6, 0xcf,
- 0xa8, 0x7d, 0xdb, 0xa7, 0x14, 0xe2, 0xf5, 0xf3,
- 0xff, 0xba, 0x23, 0xd2, 0x9a, 0x36, 0x14, 0x60,
- 0x2a, 0x91, 0x5d, 0x2b, 0x35, 0x3b, 0xb6, 0xdd,
- 0xcb, 0x6b, 0xdc, 0x18, 0xdc, 0x33, 0xb8, 0xb3,
- 0xc7, 0x27, 0x7e, 0xfc, 0xd2, 0xf7, 0x97, 0x90,
- 0x5e, 0x17, 0xac, 0x14, 0x8e, 0x0f, 0xca, 0xb5,
- 0x6f, 0xc9, 0x2d, 0x16, 0x03, 0x01, 0x00, 0x86,
- 0x0f, 0x00, 0x00, 0x82, 0x00, 0x80, 0x44, 0x7f,
- 0xa2, 0x59, 0x60, 0x0b, 0x5a, 0xc4, 0xaf, 0x1e,
- 0x60, 0xa5, 0x24, 0xea, 0xc1, 0xc3, 0x22, 0x21,
- 0x6b, 0x22, 0x8b, 0x2a, 0x11, 0x82, 0x68, 0x7d,
- 0xb9, 0xdd, 0x9c, 0x27, 0x4c, 0xc2, 0xc8, 0xa2,
- 0x8b, 0x6b, 0x77, 0x8d, 0x3a, 0x2b, 0x8d, 0x2f,
- 0x6a, 0x2b, 0x43, 0xd2, 0xd1, 0xc6, 0x41, 0x79,
- 0xa2, 0x4f, 0x2b, 0xc2, 0xf7, 0xb2, 0x10, 0xad,
- 0xa6, 0x01, 0x51, 0x51, 0x25, 0xe7, 0x58, 0x7a,
- 0xcf, 0x3b, 0xc4, 0x29, 0xb5, 0xe5, 0xa7, 0x83,
- 0xe6, 0xcb, 0x1e, 0xf3, 0x02, 0x0f, 0x53, 0x3b,
- 0xb5, 0x39, 0xef, 0x9c, 0x42, 0xe0, 0xa6, 0x9b,
- 0x2b, 0xdd, 0x60, 0xae, 0x0a, 0x73, 0x35, 0xbe,
- 0x26, 0x10, 0x1b, 0xe9, 0xe9, 0x61, 0xab, 0x20,
- 0xa5, 0x48, 0xc6, 0x60, 0xa6, 0x50, 0x3c, 0xfb,
- 0xa7, 0xca, 0xb0, 0x80, 0x95, 0x1e, 0xce, 0xc7,
- 0xbb, 0x68, 0x44, 0xdc, 0x0e, 0x0e, 0x14, 0x03,
- 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, 0x00,
- 0x24, 0xb6, 0xcd, 0x0c, 0x78, 0xfd, 0xd6, 0xff,
- 0xbe, 0x97, 0xd5, 0x0a, 0x7d, 0x4f, 0xa1, 0x03,
- 0x78, 0xc8, 0x61, 0x6f, 0xf2, 0x4b, 0xa8, 0x56,
- 0x4f, 0x3c, 0xa2, 0xd9, 0xd0, 0x20, 0x13, 0x1b,
- 0x8b, 0x36, 0xb7, 0x33, 0x9c,
- },
-
- {
- 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
- 0x01, 0x00, 0x24, 0xa3, 0x43, 0x94, 0xe7, 0xdf,
- 0xb6, 0xc3, 0x03, 0x9f, 0xc1, 0x59, 0x0c, 0xc3,
- 0x13, 0xae, 0xed, 0xcf, 0xff, 0xf1, 0x80, 0xf3,
- 0x13, 0x63, 0x1c, 0xf0, 0xca, 0xad, 0x9e, 0x71,
- 0x46, 0x5f, 0x6b, 0xeb, 0x10, 0x3f, 0xe3, 0x17,
- 0x03, 0x01, 0x00, 0x21, 0xe9, 0x80, 0x95, 0x6e,
- 0x05, 0x55, 0x2f, 0xed, 0x4d, 0xde, 0x17, 0x3a,
- 0x32, 0x9b, 0x2a, 0x74, 0x30, 0x4f, 0xe0, 0x9f,
- 0x4e, 0xd3, 0x06, 0xbd, 0x3a, 0x43, 0x75, 0x8b,
- 0x5b, 0x9a, 0xd8, 0x2e, 0x56, 0x15, 0x03, 0x01,
- 0x00, 0x16, 0x53, 0xf5, 0xff, 0xe0, 0xa1, 0x6c,
- 0x33, 0xf4, 0x4e, 0x89, 0x68, 0xe1, 0xf7, 0x61,
- 0x13, 0xb3, 0x12, 0xa1, 0x8e, 0x5a, 0x7a, 0x02,
- },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
+ 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16,
+ 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
+ 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
+ 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
+ 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
+ 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
+ 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
+ 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
+ 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
+ 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
+ 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
+ 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
+ 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
+ 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
+ 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
+ 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
+ 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
+ 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
+ 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
+ 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
+ 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
+ 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
+ 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
+ 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
+ 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
+ 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
+ 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
+ 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
+ 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
+ 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
+ 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
+ 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
+ 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
+ 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
+ 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
+ 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
+ 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
+ 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
+ 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
+ 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
+ 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
+ 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
+ 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
+ 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
+ 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
+ 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
+ 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
+ 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
+ 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
+ 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
+ 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
+ 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
+ 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
+ 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
+ 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
+ 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
+ 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
+ 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
+ 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
+ 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
+ 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
+ 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
+ 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d,
+ 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16,
+ 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
},
- },
+ {
+ 0x16, 0x03, 0x01, 0x01, 0xfb, 0x0b, 0x00, 0x01,
+ 0xf7, 0x00, 0x01, 0xf4, 0x00, 0x01, 0xf1, 0x30,
+ 0x82, 0x01, 0xed, 0x30, 0x82, 0x01, 0x58, 0xa0,
+ 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x00, 0x30,
+ 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x30, 0x26, 0x31, 0x10,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f,
+ 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x1e, 0x17, 0x0d,
+ 0x31, 0x31, 0x31, 0x32, 0x30, 0x38, 0x30, 0x37,
+ 0x35, 0x35, 0x31, 0x32, 0x5a, 0x17, 0x0d, 0x31,
+ 0x32, 0x31, 0x32, 0x30, 0x37, 0x30, 0x38, 0x30,
+ 0x30, 0x31, 0x32, 0x5a, 0x30, 0x26, 0x31, 0x10,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f,
+ 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, 0x9c, 0x30,
+ 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x03, 0x81, 0x8c, 0x00,
+ 0x30, 0x81, 0x88, 0x02, 0x81, 0x80, 0x4e, 0xd0,
+ 0x7b, 0x31, 0xe3, 0x82, 0x64, 0xd9, 0x59, 0xc0,
+ 0xc2, 0x87, 0xa4, 0x5e, 0x1e, 0x8b, 0x73, 0x33,
+ 0xc7, 0x63, 0x53, 0xdf, 0x66, 0x92, 0x06, 0x84,
+ 0xf6, 0x64, 0xd5, 0x8f, 0xe4, 0x36, 0xa7, 0x1d,
+ 0x2b, 0xe8, 0xb3, 0x20, 0x36, 0x45, 0x23, 0xb5,
+ 0xe3, 0x95, 0xae, 0xed, 0xe0, 0xf5, 0x20, 0x9c,
+ 0x8d, 0x95, 0xdf, 0x7f, 0x5a, 0x12, 0xef, 0x87,
+ 0xe4, 0x5b, 0x68, 0xe4, 0xe9, 0x0e, 0x74, 0xec,
+ 0x04, 0x8a, 0x7f, 0xde, 0x93, 0x27, 0xc4, 0x01,
+ 0x19, 0x7a, 0xbd, 0xf2, 0xdc, 0x3d, 0x14, 0xab,
+ 0xd0, 0x54, 0xca, 0x21, 0x0c, 0xd0, 0x4d, 0x6e,
+ 0x87, 0x2e, 0x5c, 0xc5, 0xd2, 0xbb, 0x4d, 0x4b,
+ 0x4f, 0xce, 0xb6, 0x2c, 0xf7, 0x7e, 0x88, 0xec,
+ 0x7c, 0xd7, 0x02, 0x91, 0x74, 0xa6, 0x1e, 0x0c,
+ 0x1a, 0xda, 0xe3, 0x4a, 0x5a, 0x2e, 0xde, 0x13,
+ 0x9c, 0x4c, 0x40, 0x88, 0x59, 0x93, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x32, 0x30, 0x30, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x00, 0xa0, 0x30,
+ 0x0d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x06,
+ 0x04, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x08, 0x30,
+ 0x06, 0x80, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30,
+ 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x03, 0x81, 0x81, 0x00,
+ 0x36, 0x1f, 0xb3, 0x7a, 0x0c, 0x75, 0xc9, 0x6e,
+ 0x37, 0x46, 0x61, 0x2b, 0xd5, 0xbd, 0xc0, 0xa7,
+ 0x4b, 0xcc, 0x46, 0x9a, 0x81, 0x58, 0x7c, 0x85,
+ 0x79, 0x29, 0xc8, 0xc8, 0xc6, 0x67, 0xdd, 0x32,
+ 0x56, 0x45, 0x2b, 0x75, 0xb6, 0xe9, 0x24, 0xa9,
+ 0x50, 0x9a, 0xbe, 0x1f, 0x5a, 0xfa, 0x1a, 0x15,
+ 0xd9, 0xcc, 0x55, 0x95, 0x72, 0x16, 0x83, 0xb9,
+ 0xc2, 0xb6, 0x8f, 0xfd, 0x88, 0x8c, 0x38, 0x84,
+ 0x1d, 0xab, 0x5d, 0x92, 0x31, 0x13, 0x4f, 0xfd,
+ 0x83, 0x3b, 0xc6, 0x9d, 0xf1, 0x11, 0x62, 0xb6,
+ 0x8b, 0xec, 0xab, 0x67, 0xbe, 0xc8, 0x64, 0xb0,
+ 0x11, 0x50, 0x46, 0x58, 0x17, 0x6b, 0x99, 0x1c,
+ 0xd3, 0x1d, 0xfc, 0x06, 0xf1, 0x0e, 0xe5, 0x96,
+ 0xa8, 0x0c, 0xf9, 0x78, 0x20, 0xb7, 0x44, 0x18,
+ 0x51, 0x8d, 0x10, 0x7e, 0x4f, 0x94, 0x67, 0xdf,
+ 0xa3, 0x4e, 0x70, 0x73, 0x8e, 0x90, 0x91, 0x85,
+ 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
+ 0x82, 0x00, 0x80, 0x81, 0x46, 0x43, 0xf9, 0xe7,
+ 0xda, 0x8c, 0x92, 0x3a, 0x78, 0x1a, 0x86, 0xb3,
+ 0xbe, 0x83, 0x22, 0xb6, 0xaa, 0x57, 0x37, 0x68,
+ 0x9e, 0x54, 0x3f, 0xd3, 0xce, 0x4d, 0x5e, 0x2a,
+ 0xdc, 0xb0, 0x49, 0x02, 0xbb, 0xc0, 0x45, 0x58,
+ 0x79, 0x10, 0xc7, 0x94, 0x60, 0x9f, 0x1b, 0x5f,
+ 0x18, 0x31, 0x37, 0x9c, 0xe0, 0xe6, 0xdf, 0x5e,
+ 0x70, 0x44, 0xf6, 0x8b, 0xdf, 0xf1, 0xf6, 0x43,
+ 0xc8, 0x2f, 0xd1, 0xce, 0xd0, 0xd6, 0x64, 0x4f,
+ 0xe8, 0x2b, 0xfa, 0xd3, 0xd0, 0xd1, 0x2e, 0xaa,
+ 0x9b, 0x1d, 0x13, 0x5c, 0xbe, 0x57, 0x41, 0x6c,
+ 0x5e, 0x8d, 0xea, 0xa9, 0x3c, 0x58, 0xa0, 0x30,
+ 0x92, 0x77, 0x7a, 0xed, 0x64, 0x58, 0xe5, 0x7f,
+ 0x6a, 0x93, 0x89, 0x66, 0x3d, 0x13, 0x16, 0x56,
+ 0xa0, 0xad, 0xdc, 0x68, 0x95, 0x87, 0x81, 0xd0,
+ 0x90, 0x4d, 0x5f, 0xfe, 0x3e, 0x83, 0x15, 0x2e,
+ 0x50, 0x3c, 0xdd, 0x16, 0x03, 0x01, 0x00, 0x86,
+ 0x0f, 0x00, 0x00, 0x82, 0x00, 0x80, 0x2b, 0xf8,
+ 0x56, 0x48, 0xbb, 0x02, 0x37, 0x15, 0x02, 0x74,
+ 0x33, 0x53, 0x65, 0xa7, 0x7c, 0x2f, 0xc6, 0x5d,
+ 0x80, 0x59, 0xc1, 0xc2, 0x3b, 0xa9, 0xde, 0x4e,
+ 0x70, 0x51, 0xd2, 0xde, 0x58, 0x7f, 0xd8, 0xb9,
+ 0xb6, 0x3b, 0xc8, 0xaa, 0xfc, 0x3d, 0x53, 0x2d,
+ 0x61, 0x4d, 0xf5, 0x60, 0x12, 0xc2, 0xa5, 0x39,
+ 0x0c, 0xa7, 0xc6, 0xac, 0x26, 0x4b, 0xf4, 0x5f,
+ 0xe9, 0xf4, 0xf2, 0x73, 0x48, 0xe4, 0x3b, 0xee,
+ 0xf2, 0xee, 0xc0, 0xee, 0xfb, 0x5b, 0x60, 0xc2,
+ 0x74, 0xe6, 0xf6, 0x43, 0x3e, 0xa4, 0xf7, 0x97,
+ 0x3d, 0xfc, 0xe9, 0x44, 0x21, 0x18, 0x46, 0x05,
+ 0x33, 0xf8, 0xfe, 0x35, 0x5b, 0xe6, 0x8f, 0xef,
+ 0x4d, 0x4c, 0x87, 0xf6, 0xb4, 0x6e, 0x6b, 0x39,
+ 0xd8, 0xaa, 0x1b, 0x33, 0xc9, 0x1c, 0x66, 0x48,
+ 0xbe, 0xfa, 0xb5, 0x92, 0x09, 0xfd, 0xb9, 0xb9,
+ 0xca, 0xe6, 0x6d, 0x71, 0xc6, 0x89, 0x14, 0x03,
+ 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, 0x00,
+ 0x24, 0xe3, 0x2b, 0xef, 0x17, 0xd5, 0xa6, 0x4c,
+ 0x2e, 0x10, 0xac, 0x9c, 0xfe, 0x0f, 0x18, 0x43,
+ 0x95, 0x00, 0x81, 0xf7, 0x7c, 0x00, 0x5b, 0x89,
+ 0x52, 0x41, 0xe4, 0x8a, 0x8a, 0x34, 0x31, 0x09,
+ 0x48, 0x7c, 0xc5, 0xc3, 0x83,
+ },
+ {
+ 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x01, 0x00, 0x24, 0x24, 0xaa, 0xaa, 0x56, 0x8b,
+ 0x41, 0x87, 0x01, 0xbe, 0x80, 0x05, 0x51, 0x36,
+ 0x08, 0xfc, 0xaf, 0xff, 0x7f, 0xf4, 0x74, 0x84,
+ 0x88, 0xdc, 0xb8, 0x8e, 0x70, 0x6c, 0x22, 0x04,
+ 0xee, 0x45, 0x8d, 0xda, 0xed, 0xc6, 0x05, 0x17,
+ 0x03, 0x01, 0x00, 0x21, 0x91, 0x49, 0x4b, 0xed,
+ 0xa3, 0x41, 0xe9, 0x88, 0x3b, 0xa3, 0x01, 0xee,
+ 0x77, 0x4e, 0x12, 0xb4, 0xcd, 0x5e, 0xcc, 0x45,
+ 0x02, 0x5a, 0x20, 0xd6, 0xe8, 0xac, 0xcb, 0x60,
+ 0xcb, 0x1b, 0xef, 0xf9, 0xc2, 0x15, 0x03, 0x01,
+ 0x00, 0x16, 0xd4, 0xcd, 0x92, 0x3c, 0x10, 0x93,
+ 0x68, 0xc3, 0xdd, 0xaf, 0xe9, 0xcb, 0x5d, 0x94,
+ 0x1a, 0x06, 0x81, 0xa7, 0x78, 0x0f, 0xc3, 0x03,
+ },
+ }},
}
// cert.pem and key.pem were generated with generate_cert.go
@@ -1703,7 +1984,7 @@ GFGNEH5PlGffo05wc46QkYU=
/* corresponding key.pem for cert.pem is:
-----BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgE7QezHjgmTZWcDCh6ReHotzM8djU99mkgaE9mTVj+Q2px0r6LMg
+MIICWgIBAAKBgE7QezHjgmTZWcDCh6ReHotzM8djU99mkgaE9mTVj+Q2px0r6LMg
NkUjteOVru3g9SCcjZXff1oS74fkW2jk6Q507ASKf96TJ8QBGXq98tw9FKvQVMoh
DNBNbocuXMXSu01LT862LPd+iOx81wKRdKYeDBra40paLt4TnExAiFmTAgMBAAEC
gYBxvXd8yNteFTns8A/2yomEMC4yeosJJSpp1CsN3BJ7g8/qTnrVPxBy+RU+qr63
@@ -1715,6 +1996,6 @@ RkKVpenBHyrGg0oeN5La7URILWKj7CPXAkBKo6F+d+phNjwIFoN1Xb/RA32w/D1I
saG9sF+UEhRt9AxUfW/U/tIQ9V0ZHHcSg1XaCM5Nvp934brdKdvTOKnJAkBD5h/3
Rybatlvg/fzBEaJFyq09zhngkxlZOUtBVTqzl17RVvY2orgH02U4HbCHy4phxOn7
qTdQRYlHRftgnWK1AkANibn9PRYJ7mJyJ9Dyj2QeNcSkSTzrt0tPvUMf4+meJymN
-1Ntu5+S1DLLzfxlaljWG6ylW6DNxujCyuXIV2rvAMAA=
+1Ntu5+S1DLLzfxlaljWG6ylW6DNxujCyuXIV2rvA
-----END RSA PRIVATE KEY-----
*/
diff --git a/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py b/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py
deleted file mode 100644
index 5692bd32f..000000000
--- a/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright 2010 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# This code is used to parse the debug log from gnutls-cli and generate a
-# script of the handshake. This script is included in handshake_server_test.go.
-# See the comments there for details.
-
-import sys
-
-blocks = []
-
-READ = 1
-WRITE = 2
-
-currentBlockType = 0
-currentBlock = []
-for line in sys.stdin.readlines():
- line = line[:-1]
- if line.startswith("|<7>| WRITE: "):
- if currentBlockType != WRITE:
- if len(currentBlock) > 0:
- blocks.append(currentBlock)
- currentBlock = []
- currentBlockType = WRITE
- elif line.startswith("|<7>| READ: "):
- if currentBlockType != READ:
- if len(currentBlock) > 0:
- blocks.append(currentBlock)
- currentBlock = []
- currentBlockType = READ
- elif line.startswith("|<7>| 0"):
- line = line[13:]
- line = line.strip()
- bs = line.split()
- for b in bs:
- currentBlock.append(int(b, 16))
- elif line.startswith("|<7>| RB-PEEK: Read 1 bytes"):
- currentBlock = currentBlock[:-1]
-
-if len(currentBlock) > 0:
- blocks.append(currentBlock)
-
-for block in blocks:
- sys.stdout.write("\t{\n")
-
- i = 0
- for b in block:
- if i % 8 == 0:
- sys.stdout.write("\t\t")
- sys.stdout.write("0x%02x," % b)
- if i % 8 == 7:
- sys.stdout.write("\n")
- else:
- sys.stdout.write(" ")
- i += 1
- sys.stdout.write("\n\t},\n\n")
diff --git a/src/pkg/crypto/tls/prf.go b/src/pkg/crypto/tls/prf.go
index 637ef03e2..df1eaad05 100644
--- a/src/pkg/crypto/tls/prf.go
+++ b/src/pkg/crypto/tls/prf.go
@@ -106,10 +106,9 @@ var keyExpansionLabel = []byte("key expansion")
var clientFinishedLabel = []byte("client finished")
var serverFinishedLabel = []byte("server finished")
-// keysFromPreMasterSecret generates the connection keys from the pre master
-// secret, given the lengths of the MAC key, cipher key and IV, as defined in
-// RFC 2246, section 6.3.
-func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
+// masterFromPreMasterSecret generates the master secret from the pre-master
+// secret. See http://tools.ietf.org/html/rfc5246#section-8.1
+func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte) []byte {
prf := pRF10
if version == versionSSL30 {
prf = pRF30
@@ -118,9 +117,21 @@ func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serv
var seed [tlsRandomLength * 2]byte
copy(seed[0:len(clientRandom)], clientRandom)
copy(seed[len(clientRandom):], serverRandom)
- masterSecret = make([]byte, masterSecretLength)
+ masterSecret := make([]byte, masterSecretLength)
prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:])
+ return masterSecret
+}
+// keysFromMasterSecret generates the connection keys from the master
+// secret, given the lengths of the MAC key, cipher key and IV, as defined in
+// RFC 2246, section 6.3.
+func keysFromMasterSecret(version uint16, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
+ prf := pRF10
+ if version == versionSSL30 {
+ prf = pRF30
+ }
+
+ var seed [tlsRandomLength * 2]byte
copy(seed[0:len(clientRandom)], serverRandom)
copy(seed[len(serverRandom):], clientRandom)
diff --git a/src/pkg/crypto/tls/prf_test.go b/src/pkg/crypto/tls/prf_test.go
index a32392cef..773a2b2ff 100644
--- a/src/pkg/crypto/tls/prf_test.go
+++ b/src/pkg/crypto/tls/prf_test.go
@@ -48,18 +48,23 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
in, _ := hex.DecodeString(test.preMasterSecret)
clientRandom, _ := hex.DecodeString(test.clientRandom)
serverRandom, _ := hex.DecodeString(test.serverRandom)
- master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret(test.version, in, clientRandom, serverRandom, test.macLen, test.keyLen, 0)
- masterString := hex.EncodeToString(master)
+
+ masterSecret := masterFromPreMasterSecret(test.version, in, clientRandom, serverRandom)
+ if s := hex.EncodeToString(masterSecret); s != test.masterSecret {
+ t.Errorf("#%d: bad master secret %s, want %s", i, s, test.masterSecret)
+ continue
+ }
+
+ clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromMasterSecret(test.version, masterSecret, clientRandom, serverRandom, test.macLen, test.keyLen, 0)
clientMACString := hex.EncodeToString(clientMAC)
serverMACString := hex.EncodeToString(serverMAC)
clientKeyString := hex.EncodeToString(clientKey)
serverKeyString := hex.EncodeToString(serverKey)
- if masterString != test.masterSecret ||
- clientMACString != test.clientMAC ||
+ if clientMACString != test.clientMAC ||
serverMACString != test.serverMAC ||
clientKeyString != test.clientKey ||
serverKeyString != test.serverKey {
- t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s, %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverKeyString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey)
+ t.Errorf("#%d: got: (%s, %s, %s, %s) want: (%s, %s, %s, %s)", i, clientMACString, serverMACString, clientKeyString, serverKeyString, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey)
}
}
}
diff --git a/src/pkg/crypto/tls/root_test.go b/src/pkg/crypto/tls/root_test.go
deleted file mode 100644
index e61c21851..000000000
--- a/src/pkg/crypto/tls/root_test.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package tls
-
-import (
- "crypto/x509"
- "runtime"
- "testing"
-)
-
-var tlsServers = []string{
- "google.com",
- "github.com",
- "twitter.com",
-}
-
-func TestOSCertBundles(t *testing.T) {
- if testing.Short() {
- t.Logf("skipping certificate tests in short mode")
- return
- }
-
- for _, addr := range tlsServers {
- conn, err := Dial("tcp", addr+":443", &Config{ServerName: addr})
- if err != nil {
- t.Errorf("unable to verify %v: %v", addr, err)
- continue
- }
- err = conn.Close()
- if err != nil {
- t.Error(err)
- }
- }
-}
-
-func TestCertHostnameVerifyWindows(t *testing.T) {
- if runtime.GOOS != "windows" {
- return
- }
-
- if testing.Short() {
- t.Logf("skipping certificate tests in short mode")
- return
- }
-
- for _, addr := range tlsServers {
- cfg := &Config{ServerName: "example.com"}
- conn, err := Dial("tcp", addr+":443", cfg)
- if err == nil {
- conn.Close()
- t.Errorf("should fail to verify for example.com: %v", addr)
- continue
- }
- _, ok := err.(x509.HostnameError)
- if !ok {
- t.Errorf("error type mismatch, got: %v", err)
- }
- }
-}
diff --git a/src/pkg/crypto/tls/ticket.go b/src/pkg/crypto/tls/ticket.go
new file mode 100644
index 000000000..4cfc5a53f
--- /dev/null
+++ b/src/pkg/crypto/tls/ticket.go
@@ -0,0 +1,182 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tls
+
+import (
+ "bytes"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/hmac"
+ "crypto/sha256"
+ "crypto/subtle"
+ "errors"
+ "io"
+)
+
+// sessionState contains the information that is serialized into a session
+// ticket in order to later resume a connection.
+type sessionState struct {
+ vers uint16
+ cipherSuite uint16
+ masterSecret []byte
+ certificates [][]byte
+}
+
+func (s *sessionState) equal(i interface{}) bool {
+ s1, ok := i.(*sessionState)
+ if !ok {
+ return false
+ }
+
+ if s.vers != s1.vers ||
+ s.cipherSuite != s1.cipherSuite ||
+ !bytes.Equal(s.masterSecret, s1.masterSecret) {
+ return false
+ }
+
+ if len(s.certificates) != len(s1.certificates) {
+ return false
+ }
+
+ for i := range s.certificates {
+ if !bytes.Equal(s.certificates[i], s1.certificates[i]) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (s *sessionState) marshal() []byte {
+ length := 2 + 2 + 2 + len(s.masterSecret) + 2
+ for _, cert := range s.certificates {
+ length += 4 + len(cert)
+ }
+
+ ret := make([]byte, length)
+ x := ret
+ x[0] = byte(s.vers >> 8)
+ x[1] = byte(s.vers)
+ x[2] = byte(s.cipherSuite >> 8)
+ x[3] = byte(s.cipherSuite)
+ x[4] = byte(len(s.masterSecret) >> 8)
+ x[5] = byte(len(s.masterSecret))
+ x = x[6:]
+ copy(x, s.masterSecret)
+ x = x[len(s.masterSecret):]
+
+ x[0] = byte(len(s.certificates) >> 8)
+ x[1] = byte(len(s.certificates))
+ x = x[2:]
+
+ for _, cert := range s.certificates {
+ x[0] = byte(len(cert) >> 24)
+ x[1] = byte(len(cert) >> 16)
+ x[2] = byte(len(cert) >> 8)
+ x[3] = byte(len(cert))
+ copy(x[4:], cert)
+ x = x[4+len(cert):]
+ }
+
+ return ret
+}
+
+func (s *sessionState) unmarshal(data []byte) bool {
+ if len(data) < 8 {
+ return false
+ }
+
+ s.vers = uint16(data[0])<<8 | uint16(data[1])
+ s.cipherSuite = uint16(data[2])<<8 | uint16(data[3])
+ masterSecretLen := int(data[4])<<8 | int(data[5])
+ data = data[6:]
+ if len(data) < masterSecretLen {
+ return false
+ }
+
+ s.masterSecret = data[:masterSecretLen]
+ data = data[masterSecretLen:]
+
+ if len(data) < 2 {
+ return false
+ }
+
+ numCerts := int(data[0])<<8 | int(data[1])
+ data = data[2:]
+
+ s.certificates = make([][]byte, numCerts)
+ for i := range s.certificates {
+ if len(data) < 4 {
+ return false
+ }
+ certLen := int(data[0])<<24 | int(data[1])<<16 | int(data[2])<<8 | int(data[3])
+ data = data[4:]
+ if certLen < 0 {
+ return false
+ }
+ if len(data) < certLen {
+ return false
+ }
+ s.certificates[i] = data[:certLen]
+ data = data[certLen:]
+ }
+
+ if len(data) > 0 {
+ return false
+ }
+
+ return true
+}
+
+func (c *Conn) encryptTicket(state *sessionState) ([]byte, error) {
+ serialized := state.marshal()
+ encrypted := make([]byte, aes.BlockSize+len(serialized)+sha256.Size)
+ iv := encrypted[:aes.BlockSize]
+ macBytes := encrypted[len(encrypted)-sha256.Size:]
+
+ if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
+ return nil, err
+ }
+ block, err := aes.NewCipher(c.config.SessionTicketKey[:16])
+ if err != nil {
+ return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
+ }
+ cipher.NewCTR(block, iv).XORKeyStream(encrypted[aes.BlockSize:], serialized)
+
+ mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32])
+ mac.Write(encrypted[:len(encrypted)-sha256.Size])
+ mac.Sum(macBytes[:0])
+
+ return encrypted, nil
+}
+
+func (c *Conn) decryptTicket(encrypted []byte) (*sessionState, bool) {
+ if len(encrypted) < aes.BlockSize+sha256.Size {
+ return nil, false
+ }
+
+ iv := encrypted[:aes.BlockSize]
+ macBytes := encrypted[len(encrypted)-sha256.Size:]
+
+ mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32])
+ mac.Write(encrypted[:len(encrypted)-sha256.Size])
+ expected := mac.Sum(nil)
+
+ if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
+ return nil, false
+ }
+
+ block, err := aes.NewCipher(c.config.SessionTicketKey[:16])
+ if err != nil {
+ return nil, false
+ }
+ ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
+ plaintext := ciphertext
+ cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
+
+ state := new(sessionState)
+ ok := state.unmarshal(plaintext)
+ return state, ok
+}
diff --git a/src/pkg/crypto/tls/tls.go b/src/pkg/crypto/tls/tls.go
index 09df5ad44..9230656d6 100644
--- a/src/pkg/crypto/tls/tls.go
+++ b/src/pkg/crypto/tls/tls.go
@@ -6,6 +6,8 @@
package tls
import (
+ "crypto"
+ "crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
@@ -146,30 +148,22 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error)
return
}
- keyDERBlock, _ := pem.Decode(keyPEMBlock)
- if keyDERBlock == nil {
- err = errors.New("crypto/tls: failed to parse key PEM data")
- return
- }
-
- // OpenSSL 0.9.8 generates PKCS#1 private keys by default, while
- // OpenSSL 1.0.0 generates PKCS#8 keys. We try both.
- var key *rsa.PrivateKey
- if key, err = x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes); err != nil {
- var privKey interface{}
- if privKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes); err != nil {
- err = errors.New("crypto/tls: failed to parse key: " + err.Error())
+ var keyDERBlock *pem.Block
+ for {
+ keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
+ if keyDERBlock == nil {
+ err = errors.New("crypto/tls: failed to parse key PEM data")
return
}
-
- var ok bool
- if key, ok = privKey.(*rsa.PrivateKey); !ok {
- err = errors.New("crypto/tls: found non-RSA private key in PKCS#8 wrapping")
- return
+ if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
+ break
}
}
- cert.PrivateKey = key
+ cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
+ if err != nil {
+ return
+ }
// We don't need to parse the public key for TLS, but we so do anyway
// to check that it looks sane and matches the private key.
@@ -178,10 +172,54 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error)
return
}
- if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 {
- err = errors.New("crypto/tls: private key does not match public key")
+ switch pub := x509Cert.PublicKey.(type) {
+ case *rsa.PublicKey:
+ priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
+ if !ok {
+ err = errors.New("crypto/tls: private key type does not match public key type")
+ return
+ }
+ if pub.N.Cmp(priv.N) != 0 {
+ err = errors.New("crypto/tls: private key does not match public key")
+ return
+ }
+ case *ecdsa.PublicKey:
+ priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
+ if !ok {
+ err = errors.New("crypto/tls: private key type does not match public key type")
+ return
+
+ }
+ if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
+ err = errors.New("crypto/tls: private key does not match public key")
+ return
+ }
+ default:
+ err = errors.New("crypto/tls: unknown public key algorithm")
return
}
return
}
+
+// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
+// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
+// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
+func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
+ if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
+ return key, nil
+ }
+ if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
+ switch key := key.(type) {
+ case *rsa.PrivateKey, *ecdsa.PrivateKey:
+ return key, nil
+ default:
+ return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping")
+ }
+ }
+ if key, err := x509.ParseECPrivateKey(der); err == nil {
+ return key, nil
+ }
+
+ return nil, errors.New("crypto/tls: failed to parse private key")
+}
diff --git a/src/pkg/crypto/tls/tls_test.go b/src/pkg/crypto/tls/tls_test.go
new file mode 100644
index 000000000..38229014c
--- /dev/null
+++ b/src/pkg/crypto/tls/tls_test.go
@@ -0,0 +1,107 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tls
+
+import (
+ "testing"
+)
+
+var rsaCertPEM = `-----BEGIN CERTIFICATE-----
+MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANLJ
+hPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wok/4xIA+ui35/MmNa
+rtNuC+BdZ1tMuVCPFZcCAwEAAaNQME4wHQYDVR0OBBYEFJvKs8RfJaXTH08W+SGv
+zQyKn0H8MB8GA1UdIwQYMBaAFJvKs8RfJaXTH08W+SGvzQyKn0H8MAwGA1UdEwQF
+MAMBAf8wDQYJKoZIhvcNAQEFBQADQQBJlffJHybjDGxRMqaRmDhX0+6v02TUKZsW
+r5QuVbpQhH6u+0UgcW0jp9QwpxoPTLTWGXEWBBBurxFwiCBhkQ+V
+-----END CERTIFICATE-----
+`
+
+var rsaKeyPEM = `-----BEGIN RSA PRIVATE KEY-----
+MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo
+k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G
+6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N
+MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW
+SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T
+xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi
+D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
+-----END RSA PRIVATE KEY-----
+`
+
+// keyPEM is the same as rsaKeyPEM, but declares itself as just
+// "PRIVATE KEY", not "RSA PRIVATE KEY". http://golang.org/issue/4477
+var keyPEM = `-----BEGIN PRIVATE KEY-----
+MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo
+k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G
+6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N
+MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW
+SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T
+xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi
+D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
+-----END PRIVATE KEY-----
+`
+
+var ecdsaCertPEM = `-----BEGIN CERTIFICATE-----
+MIIB/jCCAWICCQDscdUxw16XFDAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw
+EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0
+eSBMdGQwHhcNMTIxMTE0MTI0MDQ4WhcNMTUxMTE0MTI0MDQ4WjBFMQswCQYDVQQG
+EwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lk
+Z2l0cyBQdHkgTHRkMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBY9+my9OoeSUR
+lDQdV/x8LsOuLilthhiS1Tz4aGDHIPwC1mlvnf7fg5lecYpMCrLLhauAc1UJXcgl
+01xoLuzgtAEAgv2P/jgytzRSpUYvgLBt1UA0leLYBy6mQQbrNEuqT3INapKIcUv8
+XxYP0xMEUksLPq6Ca+CRSqTtrd/23uTnapkwCQYHKoZIzj0EAQOBigAwgYYCQXJo
+A7Sl2nLVf+4Iu/tAX/IF4MavARKC4PPHK3zfuGfPR3oCCcsAoz3kAzOeijvd0iXb
+H5jBImIxPL4WxQNiBTexAkF8D1EtpYuWdlVQ80/h/f4pBcGiXPqX5h2PQSQY7hP1
++jwM1FGS4fREIOvlBYr/SzzQRtwrvrzGYxDEDbsC0ZGRnA==
+-----END CERTIFICATE-----
+`
+
+var ecdsaKeyPEM = `-----BEGIN EC PARAMETERS-----
+BgUrgQQAIw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIBrsoKp0oqcv6/JovJJDoDVSGWdirrkgCWxrprGlzB9o0X8fV675X0
+NwuBenXFfeZvVcwluO7/Q9wkYoPd/t3jGImgBwYFK4EEACOhgYkDgYYABAFj36bL
+06h5JRGUNB1X/Hwuw64uKW2GGJLVPPhoYMcg/ALWaW+d/t+DmV5xikwKssuFq4Bz
+VQldyCXTXGgu7OC0AQCC/Y/+ODK3NFKlRi+AsG3VQDSV4tgHLqZBBus0S6pPcg1q
+kohxS/xfFg/TEwRSSws+roJr4JFKpO2t3/be5OdqmQ==
+-----END EC PRIVATE KEY-----
+`
+
+var keyPairTests = []struct {
+ algo string
+ cert string
+ key string
+}{
+ {"ECDSA", ecdsaCertPEM, ecdsaKeyPEM},
+ {"RSA", rsaCertPEM, rsaKeyPEM},
+ {"RSA-untyped", rsaCertPEM, keyPEM}, // golang.org/issue/4477
+}
+
+func TestX509KeyPair(t *testing.T) {
+ var pem []byte
+ for _, test := range keyPairTests {
+ pem = []byte(test.cert + test.key)
+ if _, err := X509KeyPair(pem, pem); err != nil {
+ t.Errorf("Failed to load %s cert followed by %s key: %s", test.algo, test.algo, err)
+ }
+ pem = []byte(test.key + test.cert)
+ if _, err := X509KeyPair(pem, pem); err != nil {
+ t.Errorf("Failed to load %s key followed by %s cert: %s", test.algo, test.algo, err)
+ }
+ }
+}
+
+func TestX509MixedKeyPair(t *testing.T) {
+ if _, err := X509KeyPair([]byte(rsaCertPEM), []byte(ecdsaKeyPEM)); err == nil {
+ t.Error("Load of RSA certificate succeeded with ECDSA private key")
+ }
+ if _, err := X509KeyPair([]byte(ecdsaCertPEM), []byte(rsaKeyPEM)); err == nil {
+ t.Error("Load of ECDSA certificate succeeded with RSA private key")
+ }
+}
diff --git a/src/pkg/crypto/x509/cert_pool.go b/src/pkg/crypto/x509/cert_pool.go
index 616a0b3c1..505f4d4f7 100644
--- a/src/pkg/crypto/x509/cert_pool.go
+++ b/src/pkg/crypto/x509/cert_pool.go
@@ -103,7 +103,7 @@ func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
}
// Subjects returns a list of the DER-encoded subjects of
-// all of the certificates in the pool.
+// all of the certificates in the pool.
func (s *CertPool) Subjects() (res [][]byte) {
res = make([][]byte, len(s.certs))
for i, c := range s.certs {
diff --git a/src/pkg/crypto/x509/pem_decrypt.go b/src/pkg/crypto/x509/pem_decrypt.go
new file mode 100644
index 000000000..194c81bf6
--- /dev/null
+++ b/src/pkg/crypto/x509/pem_decrypt.go
@@ -0,0 +1,233 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package x509
+
+// RFC 1423 describes the encryption of PEM blocks. The algorithm used to
+// generate a key from the password was derived by looking at the OpenSSL
+// implementation.
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/des"
+ "crypto/md5"
+ "encoding/hex"
+ "encoding/pem"
+ "errors"
+ "io"
+ "strings"
+)
+
+type PEMCipher int
+
+// Possible values for the EncryptPEMBlock encryption algorithm.
+const (
+ _ PEMCipher = iota
+ PEMCipherDES
+ PEMCipher3DES
+ PEMCipherAES128
+ PEMCipherAES192
+ PEMCipherAES256
+)
+
+// rfc1423Algo holds a method for enciphering a PEM block.
+type rfc1423Algo struct {
+ cipher PEMCipher
+ name string
+ cipherFunc func(key []byte) (cipher.Block, error)
+ keySize int
+ blockSize int
+}
+
+// rfc1423Algos holds a slice of the possible ways to encrypt a PEM
+// block. The ivSize numbers were taken from the OpenSSL source.
+var rfc1423Algos = []rfc1423Algo{{
+ cipher: PEMCipherDES,
+ name: "DES-CBC",
+ cipherFunc: des.NewCipher,
+ keySize: 8,
+ blockSize: des.BlockSize,
+}, {
+ cipher: PEMCipher3DES,
+ name: "DES-EDE3-CBC",
+ cipherFunc: des.NewTripleDESCipher,
+ keySize: 24,
+ blockSize: des.BlockSize,
+}, {
+ cipher: PEMCipherAES128,
+ name: "AES-128-CBC",
+ cipherFunc: aes.NewCipher,
+ keySize: 16,
+ blockSize: aes.BlockSize,
+}, {
+ cipher: PEMCipherAES192,
+ name: "AES-192-CBC",
+ cipherFunc: aes.NewCipher,
+ keySize: 24,
+ blockSize: aes.BlockSize,
+}, {
+ cipher: PEMCipherAES256,
+ name: "AES-256-CBC",
+ cipherFunc: aes.NewCipher,
+ keySize: 32,
+ blockSize: aes.BlockSize,
+},
+}
+
+// deriveKey uses a key derivation function to stretch the password into a key
+// with the number of bits our cipher requires. This algorithm was derived from
+// the OpenSSL source.
+func (c rfc1423Algo) deriveKey(password, salt []byte) []byte {
+ hash := md5.New()
+ out := make([]byte, c.keySize)
+ var digest []byte
+
+ for i := 0; i < len(out); i += len(digest) {
+ hash.Reset()
+ hash.Write(digest)
+ hash.Write(password)
+ hash.Write(salt)
+ digest = hash.Sum(digest[:0])
+ copy(out[i:], digest)
+ }
+ return out
+}
+
+// IsEncryptedPEMBlock returns if the PEM block is password encrypted.
+func IsEncryptedPEMBlock(b *pem.Block) bool {
+ _, ok := b.Headers["DEK-Info"]
+ return ok
+}
+
+// IncorrectPasswordError is returned when an incorrect password is detected.
+var IncorrectPasswordError = errors.New("x509: decryption password incorrect")
+
+// DecryptPEMBlock takes a password encrypted PEM block and the password used to
+// encrypt it and returns a slice of decrypted DER encoded bytes. It inspects
+// the DEK-Info header to determine the algorithm used for decryption. If no
+// DEK-Info header is present, an error is returned. If an incorrect password
+// is detected an IncorrectPasswordError is returned.
+func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
+ dek, ok := b.Headers["DEK-Info"]
+ if !ok {
+ return nil, errors.New("x509: no DEK-Info header in block")
+ }
+
+ idx := strings.Index(dek, ",")
+ if idx == -1 {
+ return nil, errors.New("x509: malformed DEK-Info header")
+ }
+
+ mode, hexIV := dek[:idx], dek[idx+1:]
+ ciph := cipherByName(mode)
+ if ciph == nil {
+ return nil, errors.New("x509: unknown encryption mode")
+ }
+ iv, err := hex.DecodeString(hexIV)
+ if err != nil {
+ return nil, err
+ }
+ if len(iv) != ciph.blockSize {
+ return nil, errors.New("x509: incorrect IV size")
+ }
+
+ // Based on the OpenSSL implementation. The salt is the first 8 bytes
+ // of the initialization vector.
+ key := ciph.deriveKey(password, iv[:8])
+ block, err := ciph.cipherFunc(key)
+ if err != nil {
+ return nil, err
+ }
+
+ data := make([]byte, len(b.Bytes))
+ dec := cipher.NewCBCDecrypter(block, iv)
+ dec.CryptBlocks(data, b.Bytes)
+
+ // Blocks are padded using a scheme where the last n bytes of padding are all
+ // equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423.
+ // For example:
+ // [x y z 2 2]
+ // [x y 7 7 7 7 7 7 7]
+ // If we detect a bad padding, we assume it is an invalid password.
+ dlen := len(data)
+ if dlen == 0 || dlen%ciph.blockSize != 0 {
+ return nil, errors.New("x509: invalid padding")
+ }
+ last := int(data[dlen-1])
+ if dlen < last {
+ return nil, IncorrectPasswordError
+ }
+ if last == 0 || last > ciph.blockSize {
+ return nil, IncorrectPasswordError
+ }
+ for _, val := range data[dlen-last:] {
+ if int(val) != last {
+ return nil, IncorrectPasswordError
+ }
+ }
+ return data[:dlen-last], nil
+}
+
+// EncryptPEMBlock returns a PEM block of the specified type holding the
+// given DER-encoded data encrypted with the specified algorithm and
+// password.
+func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) {
+ ciph := cipherByKey(alg)
+ if ciph == nil {
+ return nil, errors.New("x509: unknown encryption mode")
+ }
+ iv := make([]byte, ciph.blockSize)
+ if _, err := io.ReadFull(rand, iv); err != nil {
+ return nil, errors.New("x509: cannot generate IV: " + err.Error())
+ }
+ // The salt is the first 8 bytes of the initialization vector,
+ // matching the key derivation in DecryptPEMBlock.
+ key := ciph.deriveKey(password, iv[:8])
+ block, err := ciph.cipherFunc(key)
+ if err != nil {
+ return nil, err
+ }
+ enc := cipher.NewCBCEncrypter(block, iv)
+ pad := ciph.blockSize - len(data)%ciph.blockSize
+ encrypted := make([]byte, len(data), len(data)+pad)
+ // We could save this copy by encrypting all the whole blocks in
+ // the data separately, but it doesn't seem worth the additional
+ // code.
+ copy(encrypted, data)
+ // See RFC 1423, section 1.1
+ for i := 0; i < pad; i++ {
+ encrypted = append(encrypted, byte(pad))
+ }
+ enc.CryptBlocks(encrypted, encrypted)
+
+ return &pem.Block{
+ Type: blockType,
+ Headers: map[string]string{
+ "Proc-Type": "4,ENCRYPTED",
+ "DEK-Info": ciph.name + "," + hex.EncodeToString(iv),
+ },
+ Bytes: encrypted,
+ }, nil
+}
+
+func cipherByName(name string) *rfc1423Algo {
+ for i := range rfc1423Algos {
+ alg := &rfc1423Algos[i]
+ if alg.name == name {
+ return alg
+ }
+ }
+ return nil
+}
+
+func cipherByKey(key PEMCipher) *rfc1423Algo {
+ for i := range rfc1423Algos {
+ alg := &rfc1423Algos[i]
+ if alg.cipher == key {
+ return alg
+ }
+ }
+ return nil
+}
diff --git a/src/pkg/crypto/x509/pem_decrypt_test.go b/src/pkg/crypto/x509/pem_decrypt_test.go
new file mode 100644
index 000000000..59ba6f900
--- /dev/null
+++ b/src/pkg/crypto/x509/pem_decrypt_test.go
@@ -0,0 +1,223 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package x509
+
+import (
+ "bytes"
+ "crypto/rand"
+ "encoding/base64"
+ "encoding/pem"
+ "testing"
+)
+
+func TestDecrypt(t *testing.T) {
+ for i, data := range testData {
+ t.Logf("test %d. %s", i, data.kind)
+ block, rest := pem.Decode(data.pemData)
+ if len(rest) > 0 {
+ t.Error("extra data")
+ }
+ der, err := DecryptPEMBlock(block, data.password)
+ if err != nil {
+ t.Error("decrypt failed: ", err)
+ continue
+ }
+ if _, err := ParsePKCS1PrivateKey(der); err != nil {
+ t.Error("invalid private key: ", err)
+ }
+ plainDER, err := base64.StdEncoding.DecodeString(data.plainDER)
+ if err != nil {
+ t.Fatal("cannot decode test DER data: ", err)
+ }
+ if !bytes.Equal(der, plainDER) {
+ t.Error("data mismatch")
+ }
+ }
+}
+
+func TestEncrypt(t *testing.T) {
+ for i, data := range testData {
+ t.Logf("test %d. %s", i, data.kind)
+ plainDER, err := base64.StdEncoding.DecodeString(data.plainDER)
+ if err != nil {
+ t.Fatal("cannot decode test DER data: ", err)
+ }
+ password := []byte("kremvax1")
+ block, err := EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", plainDER, password, data.kind)
+ if err != nil {
+ t.Error("encrypt: ", err)
+ continue
+ }
+ if !IsEncryptedPEMBlock(block) {
+ t.Error("PEM block does not appear to be encrypted")
+ }
+ if block.Type != "RSA PRIVATE KEY" {
+ t.Errorf("unexpected block type; got %q want %q", block.Type, "RSA PRIVATE KEY")
+ }
+ if block.Headers["Proc-Type"] != "4,ENCRYPTED" {
+ t.Errorf("block does not have correct Proc-Type header")
+ }
+ der, err := DecryptPEMBlock(block, password)
+ if err != nil {
+ t.Error("decrypt: ", err)
+ continue
+ }
+ if !bytes.Equal(der, plainDER) {
+ t.Errorf("data mismatch")
+ }
+ }
+}
+
+var testData = []struct {
+ kind PEMCipher
+ password []byte
+ pemData []byte
+ plainDER string
+}{
+ {
+ kind: PEMCipherDES,
+ password: []byte("asdf"),
+ pemData: []byte(`
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-CBC,34F09A4FC8DE22B5
+
+WXxy8kbZdiZvANtKvhmPBLV7eVFj2A5z6oAxvI9KGyhG0ZK0skfnt00C24vfU7m5
+ICXeoqP67lzJ18xCzQfHjDaBNs53DSDT+Iz4e8QUep1xQ30+8QKX2NA2coee3nwc
+6oM1cuvhNUDemBH2i3dKgMVkfaga0zQiiOq6HJyGSncCMSruQ7F9iWEfRbFcxFCx
+qtHb1kirfGKEtgWTF+ynyco6+2gMXNu70L7nJcnxnV/RLFkHt7AUU1yrclxz7eZz
+XOH9VfTjb52q/I8Suozq9coVQwg4tXfIoYUdT//O+mB7zJb9HI9Ps77b9TxDE6Gm
+4C9brwZ3zg2vqXcwwV6QRZMtyll9rOpxkbw6NPlpfBqkc3xS51bbxivbO/Nve4KD
+r12ymjFNF4stXCfJnNqKoZ50BHmEEUDu5Wb0fpVn82XrGw7CYc4iug==
+-----END RSA PRIVATE KEY-----`),
+ plainDER: `
+MIIBPAIBAAJBAPASZe+tCPU6p80AjHhDkVsLYa51D35e/YGa8QcZyooeZM8EHozo
+KD0fNiKI+53bHdy07N+81VQ8/ejPcRoXPlsCAwEAAQJBAMTxIuSq27VpR+zZ7WJf
+c6fvv1OBvpMZ0/d1pxL/KnOAgq2rD5hDtk9b0LGhTPgQAmrrMTKuSeGoIuYE+gKQ
+QvkCIQD+GC1m+/do+QRurr0uo46Kx1LzLeSCrjBk34wiOp2+dwIhAPHfTLRXS2fv
+7rljm0bYa4+eDZpz+E8RcXEgzhhvcQQ9AiAI5eHZJGOyml3MXnQjiPi55WcDOw0w
+glcRgT6QCEtz2wIhANSyqaFtosIkHKqrDUGfz/bb5tqMYTAnBruVPaf/WEOBAiEA
+9xORWeRG1tRpso4+dYy4KdDkuLPIO01KY6neYGm3BCM=`,
+ },
+ {
+ kind: PEMCipher3DES,
+ password: []byte("asdf"),
+ pemData: []byte(`
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,C1F4A6A03682C2C7
+
+0JqVdBEH6iqM7drTkj+e2W/bE3LqakaiWhb9WUVonFkhyu8ca/QzebY3b5gCvAZQ
+YwBvDcT/GHospKqPx+cxDHJNsUASDZws6bz8ZXWJGwZGExKzr0+Qx5fgXn44Ms3x
+8g1ENFuTXtxo+KoNK0zuAMAqp66Llcds3Fjl4XR18QaD0CrVNAfOdgATWZm5GJxk
+Fgx5f84nT+/ovvreG+xeOzWgvtKo0UUZVrhGOgfKLpa57adumcJ6SkUuBtEFpZFB
+ldw5w7WC7d13x2LsRkwo8ZrDKgIV+Y9GNvhuCCkTzNP0V3gNeJpd201HZHR+9n3w
+3z0VjR/MGqsfcy1ziEWMNOO53At3zlG6zP05aHMnMcZoVXadEK6L1gz++inSSDCq
+gI0UJP4e3JVB7AkgYymYAwiYALAkoEIuanxoc50njJk=
+-----END RSA PRIVATE KEY-----`),
+ plainDER: `
+MIIBOwIBAAJBANOCXKdoNS/iP/MAbl9cf1/SF3P+Ns7ZeNL27CfmDh0O6Zduaax5
+NBiumd2PmjkaCu7lQ5JOibHfWn+xJsc3kw0CAwEAAQJANX/W8d1Q/sCqzkuAn4xl
+B5a7qfJWaLHndu1QRLNTRJPn0Ee7OKJ4H0QKOhQM6vpjRrz+P2u9thn6wUxoPsef
+QQIhAP/jCkfejFcy4v15beqKzwz08/tslVjF+Yq41eJGejmxAiEA05pMoqfkyjcx
+fyvGhpoOyoCp71vSGUfR2I9CR65oKh0CIC1Msjs66LlfJtQctRq6bCEtFCxEcsP+
+eEjYo/Sk6WphAiEAxpgWPMJeU/shFT28gS+tmhjPZLpEoT1qkVlC14u0b3ECIQDX
+tZZZxCtPAm7shftEib0VU77Lk8MsXJcx2C4voRsjEw==`,
+ },
+ {
+ kind: PEMCipherAES128,
+ password: []byte("asdf"),
+ pemData: []byte(`
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,D4492E793FC835CC038A728ED174F78A
+
+EyfQSzXSjv6BaNH+NHdXRlkHdimpF9izWlugVJAPApgXrq5YldPe2aGIOFXyJ+QE
+ZIG20DYqaPzJRjTEbPNZ6Es0S2JJ5yCpKxwJuDkgJZKtF39Q2i36JeGbSZQIuWJE
+GZbBpf1jDH/pr0iGonuAdl2PCCZUiy+8eLsD2tyviHUkFLOB+ykYoJ5t8ngZ/B6D
+33U43LLb7+9zD4y3Q9OVHqBFGyHcxCY9+9Qh4ZnFp7DTf6RY5TNEvE3s4g6aDpBs
+3NbvRVvYTgs8K9EPk4K+5R+P2kD8J8KvEIGxVa1vz8QoCJ/jr7Ka2rvNgPCex5/E
+080LzLHPCrXKdlr/f50yhNWq08ZxMWQFkui+FDHPDUaEELKAXV8/5PDxw80Rtybo
+AVYoCVIbZXZCuCO81op8UcOgEpTtyU5Lgh3Mw5scQL0=
+-----END RSA PRIVATE KEY-----`),
+ plainDER: `
+MIIBOgIBAAJBAMBlj5FxYtqbcy8wY89d/S7n0+r5MzD9F63BA/Lpl78vQKtdJ5dT
+cDGh/rBt1ufRrNp0WihcmZi7Mpl/3jHjiWECAwEAAQJABNOHYnKhtDIqFYj1OAJ3
+k3GlU0OlERmIOoeY/cL2V4lgwllPBEs7r134AY4wMmZSBUj8UR/O4SNO668ElKPE
+cQIhAOuqY7/115x5KCdGDMWi+jNaMxIvI4ETGwV40ykGzqlzAiEA0P9oEC3m9tHB
+kbpjSTxaNkrXxDgdEOZz8X0uOUUwHNsCIAwzcSCiGLyYJTULUmP1ESERfW1mlV78
+XzzESaJpIM/zAiBQkSTcl9VhcJreQqvjn5BnPZLP4ZHS4gPwJAGdsj5J4QIhAOVR
+B3WlRNTXR2WsJ5JdByezg9xzdXzULqmga0OE339a`,
+ },
+ {
+ kind: PEMCipherAES192,
+ password: []byte("asdf"),
+ pemData: []byte(`
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-192-CBC,E2C9FB02BCA23ADE1829F8D8BC5F5369
+
+cqVslvHqDDM6qwU6YjezCRifXmKsrgEev7ng6Qs7UmDJOpHDgJQZI9fwMFUhIyn5
+FbCu1SHkLMW52Ld3CuEqMnzWMlhPrW8tFvUOrMWPYSisv7nNq88HobZEJcUNL2MM
+Y15XmHW6IJwPqhKyLHpWXyOCVEh4ODND2nV15PCoi18oTa475baxSk7+1qH7GuIs
+Rb7tshNTMqHbCpyo9Rn3UxeFIf9efdl8YLiMoIqc7J8E5e9VlbeQSdLMQOgDAQJG
+ReUtTw8exmKsY4gsSjhkg5uiw7/ZB1Ihto0qnfQJgjGc680qGkT1d6JfvOfeYAk6
+xn5RqS/h8rYAYm64KnepfC9vIujo4NqpaREDmaLdX5MJPQ+SlytITQvgUsUq3q/t
+Ss85xjQEZH3hzwjQqdJvmA4hYP6SUjxYpBM+02xZ1Xw=
+-----END RSA PRIVATE KEY-----`),
+ plainDER: `
+MIIBOwIBAAJBAMGcRrZiNNmtF20zyS6MQ7pdGx17aFDl+lTl+qnLuJRUCMUG05xs
+OmxmL/O1Qlf+bnqR8Bgg65SfKg21SYuLhiMCAwEAAQJBAL94uuHyO4wux2VC+qpj
+IzPykjdU7XRcDHbbvksf4xokSeUFjjD3PB0Qa83M94y89ZfdILIqS9x5EgSB4/lX
+qNkCIQD6cCIqLfzq/lYbZbQgAAjpBXeQVYsbvVtJrPrXJAlVVQIhAMXpDKMeFPMn
+J0g2rbx1gngx0qOa5r5iMU5w/noN4W2XAiBjf+WzCG5yFvazD+dOx3TC0A8+4x3P
+uZ3pWbaXf5PNuQIgAcdXarvhelH2w2piY1g3BPeFqhzBSCK/yLGxR82KIh8CIQDD
++qGKsd09NhQ/G27y/DARzOYtml1NvdmCQAgsDIIOLA==`,
+ },
+ {
+ kind: PEMCipherAES256,
+ password: []byte("asdf"),
+ pemData: []byte(`
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,8E7ED5CD731902CE938957A886A5FFBD
+
+4Mxr+KIzRVwoOP0wwq6caSkvW0iS+GE2h2Ov/u+n9ZTMwL83PRnmjfjzBgfRZLVf
+JFPXxUK26kMNpIdssNnqGOds+DhB+oSrsNKoxgxSl5OBoYv9eJTVYm7qOyAFIsjr
+DRKAcjYCmzfesr7PVTowwy0RtHmYwyXMGDlAzzZrEvaiySFFmMyKKvtoavwaFoc7
+Pz3RZScwIuubzTGJ1x8EzdffYOsdCa9Mtgpp3L136+23dOd6L/qK2EG2fzrJSHs/
+2XugkleBFSMKzEp9mxXKRfa++uidQvMZTFLDK9w5YjrRvMBo/l2BoZIsq0jAIE1N
+sv5Z/KwlX+3MDEpPQpUwGPlGGdLnjI3UZ+cjgqBcoMiNc6HfgbBgYJSU6aDSHuCk
+clCwByxWkBNgJ2GrkwNrF26v+bGJJJNR4SKouY1jQf0=
+-----END RSA PRIVATE KEY-----`),
+ plainDER: `
+MIIBOgIBAAJBAKy3GFkstoCHIEeUU/qO8207m8WSrjksR+p9B4tf1w5k+2O1V/GY
+AQ5WFCApItcOkQe/I0yZZJk/PmCqMzSxrc8CAwEAAQJAOCAz0F7AW9oNelVQSP8F
+Sfzx7O1yom+qWyAQQJF/gFR11gpf9xpVnnyu1WxIRnDUh1LZwUsjwlDYb7MB74id
+oQIhANPcOiLwOPT4sIUpRM5HG6BF1BI7L77VpyGVk8xNP7X/AiEA0LMHZtk4I+lJ
+nClgYp4Yh2JZ1Znbu7IoQMCEJCjwKDECIGd8Dzm5tViTkUW6Hs3Tlf73nNs65duF
+aRnSglss8I3pAiEAonEnKruawgD8RavDFR+fUgmQiPz4FnGGeVgfwpGG1JECIBYq
+PXHYtPqxQIbD2pScR5qum7iGUh11lEUPkmt+2uqS`,
+ },
+ {
+ // generated with:
+ // openssl genrsa -aes128 -passout pass:asdf -out server.orig.key 128
+ kind: PEMCipherAES128,
+ password: []byte("asdf"),
+ pemData: []byte(`
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,74611ABC2571AF11B1BF9B69E62C89E7
+
+6ei/MlytjE0FFgZOGQ+jrwomKfpl8kdefeE0NSt/DMRrw8OacHAzBNi3pPEa0eX3
+eND9l7C9meCirWovjj9QWVHrXyugFuDIqgdhQ8iHTgCfF3lrmcttVrbIfMDw+smD
+hTP8O1mS/MHl92NE0nhv0w==
+-----END RSA PRIVATE KEY-----`),
+ plainDER: `
+MGMCAQACEQC6ssxmYuauuHGOCDAI54RdAgMBAAECEQCWIn6Yv2O+kBcDF7STctKB
+AgkA8SEfu/2i3g0CCQDGNlXbBHX7kQIIK3Ww5o0cYbECCQDCimPb0dYGsQIIeQ7A
+jryIst8=`,
+ },
+}
diff --git a/src/pkg/crypto/x509/pkcs8.go b/src/pkg/crypto/x509/pkcs8.go
index 4d8e0518e..30caacb3c 100644
--- a/src/pkg/crypto/x509/pkcs8.go
+++ b/src/pkg/crypto/x509/pkcs8.go
@@ -11,8 +11,9 @@ import (
"fmt"
)
-// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See
-// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn.
+// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See
+// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn
+// and RFC5208.
type pkcs8 struct {
Version int
Algo pkix.AlgorithmIdentifier
@@ -21,19 +22,32 @@ type pkcs8 struct {
}
// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See
-// http://www.rsa.com/rsalabs/node.asp?id=2130
+// http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208.
func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
var privKey pkcs8
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
return nil, err
}
switch {
- case privKey.Algo.Algorithm.Equal(oidRSA):
+ case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA):
key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
if err != nil {
return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
}
return key, nil
+
+ case privKey.Algo.Algorithm.Equal(oidPublicKeyECDSA):
+ bytes := privKey.Algo.Parameters.FullBytes
+ namedCurveOID := new(asn1.ObjectIdentifier)
+ if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil {
+ namedCurveOID = nil
+ }
+ key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey)
+ if err != nil {
+ return nil, errors.New("crypto/x509: failed to parse EC private key embedded in PKCS#8: " + err.Error())
+ }
+ return key, nil
+
default:
return nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
}
diff --git a/src/pkg/crypto/x509/pkcs8_test.go b/src/pkg/crypto/x509/pkcs8_test.go
index 372005f90..4114efd0e 100644
--- a/src/pkg/crypto/x509/pkcs8_test.go
+++ b/src/pkg/crypto/x509/pkcs8_test.go
@@ -9,12 +9,20 @@ import (
"testing"
)
-var pkcs8PrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031`
+var pkcs8RSAPrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031`
+
+// Generated using:
+// openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt
+var pkcs8ECPrivateKeyHex = `3081ed020100301006072a8648ce3d020106052b810400230481d53081d20201010441850d81618c5da1aec74c2eed608ba816038506975e6427237c2def150c96a3b13efbfa1f89f1be15cdf4d0ac26422e680e65a0ddd4ad3541ad76165fbf54d6e34ba18189038186000400da97bcedba1eb6d30aeb93c9f9a1454598fa47278df27d6f60ea73eb672d8dc528a9b67885b5b5dcef93c9824f7449ab512ee6a27e76142f56b94b474cfd697e810046c8ca70419365245c1d7d44d0db82c334073835d002232714548abbae6e5700f5ef315ee08b929d8581383dcf2d1c98c2f8a9fccbf79c9579f7b2fd8a90115ac2`
func TestPKCS8(t *testing.T) {
- derBytes, _ := hex.DecodeString(pkcs8PrivateKeyHex)
- _, err := ParsePKCS8PrivateKey(derBytes)
- if err != nil {
- t.Errorf("failed to decode PKCS8 key: %s", err)
+ derBytes, _ := hex.DecodeString(pkcs8RSAPrivateKeyHex)
+ if _, err := ParsePKCS8PrivateKey(derBytes); err != nil {
+ t.Errorf("failed to decode PKCS8 with RSA private key: %s", err)
+ }
+
+ derBytes, _ = hex.DecodeString(pkcs8ECPrivateKeyHex)
+ if _, err := ParsePKCS8PrivateKey(derBytes); err != nil {
+ t.Errorf("failed to decode PKCS8 with EC private key: %s", err)
}
}
diff --git a/src/pkg/crypto/x509/root_darwin.go b/src/pkg/crypto/x509/root_darwin.go
index 0f99581e8..ad3bfb4b4 100644
--- a/src/pkg/crypto/x509/root_darwin.go
+++ b/src/pkg/crypto/x509/root_darwin.go
@@ -70,11 +70,12 @@ func initSystemRoots() {
var data C.CFDataRef = nil
err := C.FetchPEMRoots(&data)
- if err != -1 {
- defer C.CFRelease(C.CFTypeRef(data))
- buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
- roots.AppendCertsFromPEM(buf)
+ if err == -1 {
+ return
}
+ defer C.CFRelease(C.CFTypeRef(data))
+ buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
+ roots.AppendCertsFromPEM(buf)
systemRoots = roots
}
diff --git a/src/pkg/crypto/x509/root_plan9.go b/src/pkg/crypto/x509/root_plan9.go
new file mode 100644
index 000000000..9965caade
--- /dev/null
+++ b/src/pkg/crypto/x509/root_plan9.go
@@ -0,0 +1,33 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build plan9
+
+package x509
+
+import "io/ioutil"
+
+// Possible certificate files; stop after finding one.
+var certFiles = []string{
+ "/sys/lib/tls/ca.pem",
+}
+
+func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
+ return nil, nil
+}
+
+func initSystemRoots() {
+ roots := NewCertPool()
+ for _, file := range certFiles {
+ data, err := ioutil.ReadFile(file)
+ if err == nil {
+ roots.AppendCertsFromPEM(data)
+ systemRoots = roots
+ return
+ }
+ }
+
+ // All of the files failed to load. systemRoots will be nil which will
+ // trigger a specific error at verification time.
+}
diff --git a/src/pkg/crypto/x509/root_stub.go b/src/pkg/crypto/x509/root_stub.go
index 568004108..4c742ccc3 100644
--- a/src/pkg/crypto/x509/root_stub.go
+++ b/src/pkg/crypto/x509/root_stub.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build plan9 darwin,!cgo
+// +build darwin,!cgo
package x509
@@ -11,5 +11,4 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
}
func initSystemRoots() {
- systemRoots = NewCertPool()
}
diff --git a/src/pkg/crypto/x509/root_unix.go b/src/pkg/crypto/x509/root_unix.go
index 76e79f494..1b25a94d0 100644
--- a/src/pkg/crypto/x509/root_unix.go
+++ b/src/pkg/crypto/x509/root_unix.go
@@ -27,9 +27,11 @@ func initSystemRoots() {
data, err := ioutil.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
- break
+ systemRoots = roots
+ return
}
}
- systemRoots = roots
+ // All of the files failed to load. systemRoots will be nil which will
+ // trigger a specific error at verification time.
}
diff --git a/src/pkg/crypto/x509/root_windows.go b/src/pkg/crypto/x509/root_windows.go
index 7e8f2af4b..e8f70a49d 100644
--- a/src/pkg/crypto/x509/root_windows.go
+++ b/src/pkg/crypto/x509/root_windows.go
@@ -98,9 +98,13 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e
// checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
// use as a certificate chain for a SSL/TLS server.
func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
+ servernamep, err := syscall.UTF16PtrFromString(opts.DNSName)
+ if err != nil {
+ return err
+ }
sslPara := &syscall.SSLExtraCertChainPolicyPara{
AuthType: syscall.AUTHTYPE_SERVER,
- ServerName: syscall.StringToUTF16Ptr(opts.DNSName),
+ ServerName: servernamep,
}
sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
@@ -110,7 +114,7 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex
para.Size = uint32(unsafe.Sizeof(*para))
status := syscall.CertChainPolicyStatus{}
- err := syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
+ err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
if err != nil {
return err
}
@@ -222,5 +226,4 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
}
func initSystemRoots() {
- systemRoots = NewCertPool()
}
diff --git a/src/pkg/crypto/x509/sec1.go b/src/pkg/crypto/x509/sec1.go
new file mode 100644
index 000000000..8a2840fbe
--- /dev/null
+++ b/src/pkg/crypto/x509/sec1.go
@@ -0,0 +1,69 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package x509
+
+import (
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "encoding/asn1"
+ "errors"
+ "fmt"
+ "math/big"
+)
+
+const ecPrivKeyVersion = 1
+
+// ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure.
+// References:
+// RFC5915
+// SEC1 - http://www.secg.org/download/aid-780/sec1-v2.pdf
+// Per RFC5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
+// most cases it is not.
+type ecPrivateKey struct {
+ Version int
+ PrivateKey []byte
+ NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
+ PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"`
+}
+
+// ParseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
+func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) {
+ return parseECPrivateKey(nil, der)
+}
+
+// parseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
+// The OID for the named curve may be provided from another source (such as
+// the PKCS8 container) - if it is provided then use this instead of the OID
+// that may exist in the EC private key structure.
+func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *ecdsa.PrivateKey, err error) {
+ var privKey ecPrivateKey
+ if _, err := asn1.Unmarshal(der, &privKey); err != nil {
+ return nil, errors.New("crypto/x509: failed to parse EC private key: " + err.Error())
+ }
+ if privKey.Version != ecPrivKeyVersion {
+ return nil, fmt.Errorf("crypto/x509: unknown EC private key version %d", privKey.Version)
+ }
+
+ var curve elliptic.Curve
+ if namedCurveOID != nil {
+ curve = namedCurveFromOID(*namedCurveOID)
+ } else {
+ curve = namedCurveFromOID(privKey.NamedCurveOID)
+ }
+ if curve == nil {
+ return nil, errors.New("crypto/x509: unknown elliptic curve")
+ }
+
+ k := new(big.Int).SetBytes(privKey.PrivateKey)
+ if k.Cmp(curve.Params().N) >= 0 {
+ return nil, errors.New("crypto/x509: invalid elliptic curve private key value")
+ }
+ priv := new(ecdsa.PrivateKey)
+ priv.Curve = curve
+ priv.D = k
+ priv.X, priv.Y = curve.ScalarBaseMult(privKey.PrivateKey)
+
+ return priv, nil
+}
diff --git a/src/pkg/crypto/x509/sec1_test.go b/src/pkg/crypto/x509/sec1_test.go
new file mode 100644
index 000000000..7135699d2
--- /dev/null
+++ b/src/pkg/crypto/x509/sec1_test.go
@@ -0,0 +1,22 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package x509
+
+import (
+ "encoding/hex"
+ "testing"
+)
+
+// Generated using:
+// openssl ecparam -genkey -name secp384r1 -outform PEM
+var ecPrivateKeyHex = `3081a40201010430bdb9839c08ee793d1157886a7a758a3c8b2a17a4df48f17ace57c72c56b4723cf21dcda21d4e1ad57ff034f19fcfd98ea00706052b81040022a16403620004feea808b5ee2429cfcce13c32160e1c960990bd050bb0fdf7222f3decd0a55008e32a6aa3c9062051c4cba92a7a3b178b24567412d43cdd2f882fa5addddd726fe3e208d2c26d733a773a597abb749714df7256ead5105fa6e7b3650de236b50`
+
+func TestParseECPrivateKey(t *testing.T) {
+ derBytes, _ := hex.DecodeString(ecPrivateKeyHex)
+ _, err := ParseECPrivateKey(derBytes)
+ if err != nil {
+ t.Errorf("failed to decode EC private key: %s", err)
+ }
+}
diff --git a/src/pkg/crypto/x509/verify.go b/src/pkg/crypto/x509/verify.go
index 307c5ef03..b29ddbc80 100644
--- a/src/pkg/crypto/x509/verify.go
+++ b/src/pkg/crypto/x509/verify.go
@@ -5,6 +5,7 @@
package x509
import (
+ "net"
"runtime"
"strings"
"time"
@@ -27,6 +28,9 @@ const (
// TooManyIntermediates results when a path length constraint is
// violated.
TooManyIntermediates
+ // IncompatibleUsage results when the certificate's key usage indicates
+ // that it may only be used for a different purpose.
+ IncompatibleUsage
)
// CertificateInvalidError results when an odd error occurs. Users of this
@@ -39,13 +43,15 @@ type CertificateInvalidError struct {
func (e CertificateInvalidError) Error() string {
switch e.Reason {
case NotAuthorizedToSign:
- return "x509: certificate is not authorized to sign other other certificates"
+ return "x509: certificate is not authorized to sign other certificates"
case Expired:
return "x509: certificate has expired or is not yet valid"
case CANotAuthorizedForThisName:
return "x509: a root or intermediate certificate is not authorized to sign in this domain"
case TooManyIntermediates:
return "x509: too many intermediates for path length constraint"
+ case IncompatibleUsage:
+ return "x509: certificate specifies an incompatible key usage"
}
return "x509: unknown error"
}
@@ -58,14 +64,28 @@ type HostnameError struct {
}
func (h HostnameError) Error() string {
- var valid string
c := h.Certificate
- if len(c.DNSNames) > 0 {
- valid = strings.Join(c.DNSNames, ", ")
+
+ var valid string
+ if ip := net.ParseIP(h.Host); ip != nil {
+ // Trying to validate an IP
+ if len(c.IPAddresses) == 0 {
+ return "x509: cannot validate certificate for " + h.Host + " because it doesn't contain any IP SANs"
+ }
+ for _, san := range c.IPAddresses {
+ if len(valid) > 0 {
+ valid += ", "
+ }
+ valid += san.String()
+ }
} else {
- valid = c.Subject.CommonName
+ if len(c.DNSNames) > 0 {
+ valid = strings.Join(c.DNSNames, ", ")
+ } else {
+ valid = c.Subject.CommonName
+ }
}
- return "certificate is valid for " + valid + ", not " + h.Host
+ return "x509: certificate is valid for " + valid + ", not " + h.Host
}
// UnknownAuthorityError results when the certificate issuer is unknown
@@ -77,6 +97,14 @@ func (e UnknownAuthorityError) Error() string {
return "x509: certificate signed by unknown authority"
}
+// SystemRootsError results when we fail to load the system root certificates.
+type SystemRootsError struct {
+}
+
+func (e SystemRootsError) Error() string {
+ return "x509: failed to load system roots and no roots provided"
+}
+
// VerifyOptions contains parameters for Certificate.Verify. It's a structure
// because other PKIX verification APIs have ended up needing many options.
type VerifyOptions struct {
@@ -84,6 +112,11 @@ type VerifyOptions struct {
Intermediates *CertPool
Roots *CertPool // if nil, the system roots are used
CurrentTime time.Time // if zero, the current time is used
+ // KeyUsage specifies which Extended Key Usage values are acceptable.
+ // An empty list means ExtKeyUsageServerAuth. Key usage is considered a
+ // constraint down the chain which mirrors Windows CryptoAPI behaviour,
+ // but not the spec. To accept any key usage, include ExtKeyUsageAny.
+ KeyUsages []ExtKeyUsage
}
const (
@@ -160,6 +193,9 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
if opts.Roots == nil {
opts.Roots = systemRootsPool()
+ if opts.Roots == nil {
+ return nil, SystemRootsError{}
+ }
}
err = c.isValid(leafCertificate, nil, &opts)
@@ -174,7 +210,35 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
}
}
- return c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts)
+ candidateChains, err := c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts)
+ if err != nil {
+ return
+ }
+
+ keyUsages := opts.KeyUsages
+ if len(keyUsages) == 0 {
+ keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
+ }
+
+ // If any key usage is acceptable then we're done.
+ for _, usage := range keyUsages {
+ if usage == ExtKeyUsageAny {
+ chains = candidateChains
+ return
+ }
+ }
+
+ for _, candidate := range candidateChains {
+ if checkChainForKeyUsage(candidate, keyUsages) {
+ chains = append(chains, candidate)
+ }
+ }
+
+ if len(chains) == 0 {
+ err = CertificateInvalidError{c, IncompatibleUsage}
+ }
+
+ return
}
func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate {
@@ -285,6 +349,22 @@ func toLowerCaseASCII(in string) string {
// VerifyHostname returns nil if c is a valid certificate for the named host.
// Otherwise it returns an error describing the mismatch.
func (c *Certificate) VerifyHostname(h string) error {
+ // IP addresses may be written in [ ].
+ candidateIP := h
+ if len(h) >= 3 && h[0] == '[' && h[len(h)-1] == ']' {
+ candidateIP = h[1 : len(h)-1]
+ }
+ if ip := net.ParseIP(candidateIP); ip != nil {
+ // We only match IP addresses against IP SANs.
+ // https://tools.ietf.org/html/rfc6125#appendix-B.2
+ for _, candidate := range c.IPAddresses {
+ if ip.Equal(candidate) {
+ return nil
+ }
+ }
+ return HostnameError{c, candidateIP}
+ }
+
lowered := toLowerCaseASCII(h)
if len(c.DNSNames) > 0 {
@@ -300,3 +380,64 @@ func (c *Certificate) VerifyHostname(h string) error {
return HostnameError{c, h}
}
+
+func checkChainForKeyUsage(chain []*Certificate, keyUsages []ExtKeyUsage) bool {
+ usages := make([]ExtKeyUsage, len(keyUsages))
+ copy(usages, keyUsages)
+
+ if len(chain) == 0 {
+ return false
+ }
+
+ usagesRemaining := len(usages)
+
+ // We walk down the list and cross out any usages that aren't supported
+ // by each certificate. If we cross out all the usages, then the chain
+ // is unacceptable.
+
+ for i := len(chain) - 1; i >= 0; i-- {
+ cert := chain[i]
+ if len(cert.ExtKeyUsage) == 0 && len(cert.UnknownExtKeyUsage) == 0 {
+ // The certificate doesn't have any extended key usage specified.
+ continue
+ }
+
+ for _, usage := range cert.ExtKeyUsage {
+ if usage == ExtKeyUsageAny {
+ // The certificate is explicitly good for any usage.
+ continue
+ }
+ }
+
+ const invalidUsage ExtKeyUsage = -1
+
+ NextRequestedUsage:
+ for i, requestedUsage := range usages {
+ if requestedUsage == invalidUsage {
+ continue
+ }
+
+ for _, usage := range cert.ExtKeyUsage {
+ if requestedUsage == usage {
+ continue NextRequestedUsage
+ } else if requestedUsage == ExtKeyUsageServerAuth &&
+ (usage == ExtKeyUsageNetscapeServerGatedCrypto ||
+ usage == ExtKeyUsageMicrosoftServerGatedCrypto) {
+ // In order to support COMODO
+ // certificate chains, we have to
+ // accept Netscape or Microsoft SGC
+ // usages as equal to ServerAuth.
+ continue NextRequestedUsage
+ }
+ }
+
+ usages[i] = invalidUsage
+ usagesRemaining--
+ if usagesRemaining == 0 {
+ return false
+ }
+ }
+ }
+
+ return true
+}
diff --git a/src/pkg/crypto/x509/verify_test.go b/src/pkg/crypto/x509/verify_test.go
index 7b171b291..5103ed814 100644
--- a/src/pkg/crypto/x509/verify_test.go
+++ b/src/pkg/crypto/x509/verify_test.go
@@ -15,12 +15,14 @@ import (
)
type verifyTest struct {
- leaf string
- intermediates []string
- roots []string
- currentTime int64
- dnsName string
- systemSkip bool
+ leaf string
+ intermediates []string
+ roots []string
+ currentTime int64
+ dnsName string
+ systemSkip bool
+ keyUsages []ExtKeyUsage
+ testSystemRootsError bool
errorCallback func(*testing.T, int, error) bool
expectedChains [][]string
@@ -28,6 +30,17 @@ type verifyTest struct {
var verifyTests = []verifyTest{
{
+ leaf: googleLeaf,
+ intermediates: []string{thawteIntermediate},
+ currentTime: 1302726541,
+ dnsName: "www.google.com",
+ testSystemRootsError: true,
+
+ // Without any roots specified we should get a system roots
+ // error.
+ errorCallback: expectSystemRootsError,
+ },
+ {
leaf: googleLeaf,
intermediates: []string{thawteIntermediate},
roots: []string{verisignRoot},
@@ -113,6 +126,51 @@ var verifyTests = []verifyTest{
{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"},
},
},
+ {
+ // The default configuration should reject an S/MIME chain.
+ leaf: smimeLeaf,
+ roots: []string{smimeIntermediate},
+ currentTime: 1339436154,
+
+ // Key usage not implemented for Windows yet.
+ systemSkip: true,
+ errorCallback: expectUsageError,
+ },
+ {
+ leaf: smimeLeaf,
+ roots: []string{smimeIntermediate},
+ currentTime: 1339436154,
+ keyUsages: []ExtKeyUsage{ExtKeyUsageServerAuth},
+
+ // Key usage not implemented for Windows yet.
+ systemSkip: true,
+ errorCallback: expectUsageError,
+ },
+ {
+ leaf: smimeLeaf,
+ roots: []string{smimeIntermediate},
+ currentTime: 1339436154,
+ keyUsages: []ExtKeyUsage{ExtKeyUsageEmailProtection},
+
+ // Key usage not implemented for Windows yet.
+ systemSkip: true,
+ expectedChains: [][]string{
+ {"Ryan Hurst", "GlobalSign PersonalSign 2 CA - G2"},
+ },
+ },
+ {
+ leaf: megaLeaf,
+ intermediates: []string{comodoIntermediate1},
+ roots: []string{comodoRoot},
+ currentTime: 1360431182,
+
+ // CryptoAPI can find alternative validation paths so we don't
+ // perform this test with system validation.
+ systemSkip: true,
+ expectedChains: [][]string{
+ {"mega.co.nz", "EssentialSSL CA", "COMODO Certification Authority"},
+ },
+ },
}
func expectHostnameError(t *testing.T, i int, err error) (ok bool) {
@@ -131,6 +189,14 @@ func expectExpired(t *testing.T, i int, err error) (ok bool) {
return true
}
+func expectUsageError(t *testing.T, i int, err error) (ok bool) {
+ if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != IncompatibleUsage {
+ t.Errorf("#%d: error was not IncompatibleUsage: %s", i, err)
+ return false
+ }
+ return true
+}
+
func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) {
if _, ok := err.(UnknownAuthorityError); !ok {
t.Errorf("#%d: error was not UnknownAuthorityError: %s", i, err)
@@ -139,6 +205,14 @@ func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) {
return true
}
+func expectSystemRootsError(t *testing.T, i int, err error) bool {
+ if _, ok := err.(SystemRootsError); !ok {
+ t.Errorf("#%d: error was not SystemRootsError: %s", i, err)
+ return false
+ }
+ return true
+}
+
func certificateFromPEM(pemBytes string) (*Certificate, error) {
block, _ := pem.Decode([]byte(pemBytes))
if block == nil {
@@ -152,11 +226,15 @@ func testVerify(t *testing.T, useSystemRoots bool) {
if useSystemRoots && test.systemSkip {
continue
}
+ if runtime.GOOS == "windows" && test.testSystemRootsError {
+ continue
+ }
opts := VerifyOptions{
Intermediates: NewCertPool(),
DNSName: test.dnsName,
CurrentTime: time.Unix(test.currentTime, 0),
+ KeyUsages: test.keyUsages,
}
if !useSystemRoots {
@@ -184,8 +262,19 @@ func testVerify(t *testing.T, useSystemRoots bool) {
return
}
+ var oldSystemRoots *CertPool
+ if test.testSystemRootsError {
+ oldSystemRoots = systemRootsPool()
+ systemRoots = nil
+ opts.Roots = nil
+ }
+
chains, err := leaf.Verify(opts)
+ if test.testSystemRootsError {
+ systemRoots = oldSystemRoots
+ }
+
if test.errorCallback == nil && err != nil {
t.Errorf("#%d: unexpected error: %s", i, err)
}
@@ -233,8 +322,7 @@ func TestGoVerify(t *testing.T) {
func TestSystemVerify(t *testing.T) {
if runtime.GOOS != "windows" {
- t.Logf("skipping verify test using system APIs on %q", runtime.GOOS)
- return
+ t.Skipf("skipping verify test using system APIs on %q", runtime.GOOS)
}
testVerify(t, true)
@@ -433,3 +521,145 @@ O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
-----END CERTIFICATE-----`
+
+const smimeLeaf = `-----BEGIN CERTIFICATE-----
+MIIFBjCCA+6gAwIBAgISESFvrjT8XcJTEe6rBlPptILlMA0GCSqGSIb3DQEBBQUA
+MFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSowKAYD
+VQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAyIENBIC0gRzIwHhcNMTIwMTIz
+MTYzNjU5WhcNMTUwMTIzMTYzNjU5WjCBlDELMAkGA1UEBhMCVVMxFjAUBgNVBAgT
+DU5ldyBIYW1zcGhpcmUxEzARBgNVBAcTClBvcnRzbW91dGgxGTAXBgNVBAoTEEds
+b2JhbFNpZ24sIEluYy4xEzARBgNVBAMTClJ5YW4gSHVyc3QxKDAmBgkqhkiG9w0B
+CQEWGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC4ASSTvavmsFQAob60ukSSwOAL9nT/s99ltNUCAf5fPH5j
+NceMKxaQse2miOmRRIXaykcq1p/TbI70Ztce38r2mbOwqDHHPVi13GxJEyUXWgaR
+BteDMu5OGyWNG1kchVsGWpbstT0Z4v0md5m1BYFnxB20ebJyOR2lXDxsFK28nnKV
++5eMj76U8BpPQ4SCH7yTMG6y0XXsB3cCrBKr2o3TOYgEKv+oNnbaoMt3UxMt9nSf
+9jyIshjqfnT5Aew3CUNMatO55g5FXXdIukAweg1YSb1ls05qW3sW00T3d7dQs9/7
+NuxCg/A2elmVJSoy8+MLR8JSFEf/aMgjO/TyLg/jAgMBAAGjggGPMIIBizAOBgNV
+HQ8BAf8EBAMCBaAwTQYDVR0gBEYwRDBCBgorBgEEAaAyASgKMDQwMgYIKwYBBQUH
+AgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMCQGA1Ud
+EQQdMBuBGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wCQYDVR0TBAIwADAdBgNV
+HSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwQwYDVR0fBDwwOjA4oDagNIYyaHR0
+cDovL2NybC5nbG9iYWxzaWduLmNvbS9ncy9nc3BlcnNvbmFsc2lnbjJnMi5jcmww
+VQYIKwYBBQUHAQEESTBHMEUGCCsGAQUFBzAChjlodHRwOi8vc2VjdXJlLmdsb2Jh
+bHNpZ24uY29tL2NhY2VydC9nc3BlcnNvbmFsc2lnbjJnMi5jcnQwHQYDVR0OBBYE
+FFWiECe0/L72eVYqcWYnLV6SSjzhMB8GA1UdIwQYMBaAFD8V0m18L+cxnkMKBqiU
+bCw7xe5lMA0GCSqGSIb3DQEBBQUAA4IBAQAhQi6hLPeudmf3IBF4IDzCvRI0FaYd
+BKfprSk/H0PDea4vpsLbWpA0t0SaijiJYtxKjlM4bPd+2chb7ejatDdyrZIzmDVy
+q4c30/xMninGKokpYA11/Ve+i2dvjulu65qasrtQRGybAuuZ67lrp/K3OMFgjV5N
+C3AHYLzvNU4Dwc4QQ1BaMOg6KzYSrKbABRZajfrpC9uiePsv7mDIXLx/toBPxWNl
+a5vJm5DrZdn7uHdvBCE6kMykbOLN5pmEK0UIlwKh6Qi5XD0pzlVkEZliFkBMJgub
+d/eF7xeg7TKPWC5xyOFp9SdMolJM7LTC3wnSO3frBAev+q/nGs9Xxyvs
+-----END CERTIFICATE-----`
+
+const smimeIntermediate = `-----BEGIN CERTIFICATE-----
+MIIEFjCCAv6gAwIBAgILBAAAAAABL07hL1IwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw
+MDBaFw0xOTA0MTMxMDAwMDBaMFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMSowKAYDVQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAy
+IENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBa0H5Nez4
+En3dIlFpX7e5E0YndxQ74xOBbz7kdBd+DLX0LOQMjVPU3DAgKL9ujhH+ZhHkURbH
+3X/94TQSUL/z2JjsaQvS0NqyZXHhM5eeuquzOJRzEQ8+odETzHg2G0Erv7yjSeww
+gkwDWDJnYUDlOjYTDUEG6+i+8Mn425reo4I0E277wD542kmVWeW7+oHv5dZo9e1Q
+yWwiKTEP6BEQVVSBgThXMG4traSSDRUt3T1eQTZx5EObpiBEBO4OTqiBTJfg4vEI
+YgkXzKLpnfszTB6YMDpR9/QS6p3ANB3kfAb+t6udSO3WCst0DGrwHDLBFGDR4UeY
+T5KGGnI7cWL7AgMBAAGjgeUwgeIwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
+MAYBAf8CAQAwHQYDVR0OBBYEFD8V0m18L+cxnkMKBqiUbCw7xe5lMEcGA1UdIARA
+MD4wPAYEVR0gADA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWdu
+LmNvbS9yZXBvc2l0b3J5LzAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmds
+b2JhbHNpZ24ubmV0L3Jvb3QuY3JsMB8GA1UdIwQYMBaAFGB7ZhpFDZfKiVAvfQTN
+NKj//P1LMA0GCSqGSIb3DQEBBQUAA4IBAQBDc3nMpMxJMQMcYUCB3+C73UpvwDE8
+eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX
+eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX
+YEvTWbWwGdPytDFPYIl3/6OqNSXSnZ7DxPcdLJq2uyiga8PB/TTIIHYkdM2+1DE0
+7y3rH/7TjwDVD7SLu5/SdOfKskuMPTjOEvz3K161mymW06klVhubCIWOro/Gx1Q2
+2FQOZ7/2k4uYoOdBTSlb8kTAuzZNgIE0rB2BIYCTz/P6zZIKW0ogbRSH
+-----END CERTIFICATE-----`
+
+var megaLeaf = `-----BEGIN CERTIFICATE-----
+MIIFOjCCBCKgAwIBAgIQWYE8Dup170kZ+k11Lg51OjANBgkqhkiG9w0BAQUFADBy
+MQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD
+VQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEYMBYGA1UE
+AxMPRXNzZW50aWFsU1NMIENBMB4XDTEyMTIxNDAwMDAwMFoXDTE0MTIxNDIzNTk1
+OVowfzEhMB8GA1UECxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMS4wLAYDVQQL
+EyVIb3N0ZWQgYnkgSW5zdHJhIENvcnBvcmF0aW9uIFB0eS4gTFREMRUwEwYDVQQL
+EwxFc3NlbnRpYWxTU0wxEzARBgNVBAMTCm1lZ2EuY28ubnowggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDcxMCClae8BQIaJHBUIVttlLvhbK4XhXPk3RQ3
+G5XA6tLZMBQ33l3F9knYJ0YErXtr8IdfYoulRQFmKFMJl9GtWyg4cGQi2Rcr5VN5
+S5dA1vu4oyJBxE9fPELcK6Yz1vqaf+n6za+mYTiQYKggVdS8/s8hmNuXP9Zk1pIn
++q0pGsf8NAcSHMJgLqPQrTDw+zae4V03DvcYfNKjuno88d2226ld7MAmQZ7uRNsI
+/CnkdelVs+akZsXf0szefSqMJlf08SY32t2jj4Ra7RApVYxOftD9nij/aLfuqOU6
+ow6IgIcIG2ZvXLZwK87c5fxL7UAsTTV+M1sVv8jA33V2oKLhAgMBAAGjggG9MIIB
+uTAfBgNVHSMEGDAWgBTay+qtWwhdzP/8JlTOSeVVxjj0+DAdBgNVHQ4EFgQUmP9l
+6zhyrZ06Qj4zogt+6LKFk4AwDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAw
+NAYDVR0lBC0wKwYIKwYBBQUHAwEGCCsGAQUFBwMCBgorBgEEAYI3CgMDBglghkgB
+hvhCBAEwTwYDVR0gBEgwRjA6BgsrBgEEAbIxAQICBzArMCkGCCsGAQUFBwIBFh1o
+dHRwczovL3NlY3VyZS5jb21vZG8uY29tL0NQUzAIBgZngQwBAgEwOwYDVR0fBDQw
+MjAwoC6gLIYqaHR0cDovL2NybC5jb21vZG9jYS5jb20vRXNzZW50aWFsU1NMQ0Eu
+Y3JsMG4GCCsGAQUFBwEBBGIwYDA4BggrBgEFBQcwAoYsaHR0cDovL2NydC5jb21v
+ZG9jYS5jb20vRXNzZW50aWFsU1NMQ0FfMi5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6
+Ly9vY3NwLmNvbW9kb2NhLmNvbTAlBgNVHREEHjAcggptZWdhLmNvLm56gg53d3cu
+bWVnYS5jby5uejANBgkqhkiG9w0BAQUFAAOCAQEAcYhrsPSvDuwihMOh0ZmRpbOE
+Gw6LqKgLNTmaYUPQhzi2cyIjhUhNvugXQQlP5f0lp5j8cixmArafg1dTn4kQGgD3
+ivtuhBTgKO1VYB/VRoAt6Lmswg3YqyiS7JiLDZxjoV7KoS5xdiaINfHDUaBBY4ZH
+j2BUlPniNBjCqXe/HndUTVUewlxbVps9FyCmH+C4o9DWzdGBzDpCkcmo5nM+cp7q
+ZhTIFTvZfo3zGuBoyu8BzuopCJcFRm3cRiXkpI7iOMUIixO1szkJS6WpL1sKdT73
+UXp08U0LBqoqG130FbzEJBBV3ixbvY6BWMHoCWuaoF12KJnC5kHt2RoWAAgMXA==
+-----END CERTIFICATE-----`
+
+var comodoIntermediate1 = `-----BEGIN CERTIFICATE-----
+MIIFAzCCA+ugAwIBAgIQGLLLuqME8aAPwfLzJkYqSjANBgkqhkiG9w0BAQUFADCB
+gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
+BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
+MDBaFw0xOTEyMzEyMzU5NTlaMHIxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVh
+dGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9E
+TyBDQSBMaW1pdGVkMRgwFgYDVQQDEw9Fc3NlbnRpYWxTU0wgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCt8AiwcsargxIxF3CJhakgEtSYau2A1NHf
+5I5ZLdOWIY120j8YC0YZYwvHIPPlC92AGvFaoL0dds23Izp0XmEbdaqb1IX04XiR
+0y3hr/yYLgbSeT1awB8hLRyuIVPGOqchfr7tZ291HRqfalsGs2rjsQuqag7nbWzD
+ypWMN84hHzWQfdvaGlyoiBSyD8gSIF/F03/o4Tjg27z5H6Gq1huQByH6RSRQXScq
+oChBRVt9vKCiL6qbfltTxfEFFld+Edc7tNkBdtzffRDPUanlOPJ7FAB1WfnwWdsX
+Pvev5gItpHnBXaIcw5rIp6gLSApqLn8tl2X2xQScRMiZln5+pN0vAgMBAAGjggGD
+MIIBfzAfBgNVHSMEGDAWgBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAdBgNVHQ4EFgQU
+2svqrVsIXcz//CZUzknlVcY49PgwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
+MAYBAf8CAQAwIAYDVR0lBBkwFwYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMD4GA1Ud
+IAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5jb21v
+ZG8uY29tL0NQUzBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9kb2Nh
+LmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBsBggrBgEFBQcB
+AQRgMF4wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NvbW9k
+b1VUTlNHQ0NBLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2Eu
+Y29tMA0GCSqGSIb3DQEBBQUAA4IBAQAtlzR6QDLqcJcvgTtLeRJ3rvuq1xqo2l/z
+odueTZbLN3qo6u6bldudu+Ennv1F7Q5Slqz0J790qpL0pcRDAB8OtXj5isWMcL2a
+ejGjKdBZa0wztSz4iw+SY1dWrCRnilsvKcKxudokxeRiDn55w/65g+onO7wdQ7Vu
+F6r7yJiIatnyfKH2cboZT7g440LX8NqxwCPf3dfxp+0Jj1agq8MLy6SSgIGSH6lv
++Wwz3D5XxqfyH8wqfOQsTEZf6/Nh9yvENZ+NWPU6g0QO2JOsTGvMd/QDzczc4BxL
+XSXaPV7Od4rhPsbXlM1wSTz/Dr0ISKvlUhQVnQ6cGodWaK2cCQBk
+-----END CERTIFICATE-----`
+
+var comodoRoot = `-----BEGIN CERTIFICATE-----
+MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB
+gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
+BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
+MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
+YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
+RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3
+UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI
+2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8
+Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp
++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+
+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O
+nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW
+/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g
+PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u
+QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY
+SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv
+IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
+RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4
+zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd
+BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB
+ZQ==
+-----END CERTIFICATE-----`
diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go
index c4d85e67f..b802bf4eb 100644
--- a/src/pkg/crypto/x509/x509.go
+++ b/src/pkg/crypto/x509/x509.go
@@ -9,6 +9,8 @@ import (
"bytes"
"crypto"
"crypto/dsa"
+ "crypto/ecdsa"
+ "crypto/elliptic"
"crypto/rsa"
"crypto/sha1"
"crypto/x509/pkix"
@@ -17,6 +19,8 @@ import (
"errors"
"io"
"math/big"
+ "net"
+ "strconv"
"time"
)
@@ -106,6 +110,8 @@ type dsaSignature struct {
R, S *big.Int
}
+type ecdsaSignature dsaSignature
+
type validity struct {
NotBefore, NotAfter time.Time
}
@@ -133,6 +139,10 @@ const (
SHA512WithRSA
DSAWithSHA1
DSAWithSHA256
+ ECDSAWithSHA1
+ ECDSAWithSHA256
+ ECDSAWithSHA384
+ ECDSAWithSHA512
)
type PublicKeyAlgorithm int
@@ -141,14 +151,15 @@ const (
UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota
RSA
DSA
+ ECDSA
)
// OIDs for signature algorithms
//
// pkcs-1 OBJECT IDENTIFIER ::= {
// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
-//
-//
+//
+//
// RFC 3279 2.2.1 RSA Signature Algorithms
//
// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 }
@@ -156,13 +167,19 @@ const (
// md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 }
//
// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 }
-//
+//
// dsaWithSha1 OBJECT IDENTIFIER ::= {
-// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 }
+// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 }
+//
+// RFC 3279 2.2.3 ECDSA Signature Algorithm
+//
+// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) ansi-x962(10045)
+// signatures(4) ecdsa-with-SHA1(1)}
//
//
// RFC 4055 5 PKCS #1 Version 1.5
-//
+//
// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 }
//
// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 }
@@ -176,15 +193,30 @@ const (
// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101)
// csor(3) algorithms(4) id-dsa-with-sha2(3) 2}
//
+// RFC 5758 3.2 ECDSA Signature Algorithm
+//
+// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
+//
+// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 }
+//
+// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 }
+
var (
- oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
- oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
- oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
- oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
- oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
- oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
- oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
- oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2}
+ oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
+ oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
+ oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
+ oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
+ oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
+ oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
+ oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
+ oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2}
+ oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
+ oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
+ oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
+ oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
)
func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm {
@@ -205,6 +237,14 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm
return DSAWithSHA1
case oid.Equal(oidSignatureDSAWithSHA256):
return DSAWithSHA256
+ case oid.Equal(oidSignatureECDSAWithSHA1):
+ return ECDSAWithSHA1
+ case oid.Equal(oidSignatureECDSAWithSHA256):
+ return ECDSAWithSHA256
+ case oid.Equal(oidSignatureECDSAWithSHA384):
+ return ECDSAWithSHA384
+ case oid.Equal(oidSignatureECDSAWithSHA512):
+ return ECDSAWithSHA512
}
return UnknownSignatureAlgorithm
}
@@ -218,21 +258,81 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm
//
// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
// x9-57(10040) x9cm(4) 1 }
+//
+// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters
+//
+// id-ecPublicKey OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
var (
- oidPublicKeyRsa = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
- oidPublicKeyDsa = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
+ oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
+ oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
+ oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
)
func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm {
switch {
- case oid.Equal(oidPublicKeyRsa):
+ case oid.Equal(oidPublicKeyRSA):
return RSA
- case oid.Equal(oidPublicKeyDsa):
+ case oid.Equal(oidPublicKeyDSA):
return DSA
+ case oid.Equal(oidPublicKeyECDSA):
+ return ECDSA
}
return UnknownPublicKeyAlgorithm
}
+// RFC 5480, 2.1.1.1. Named Curve
+//
+// secp224r1 OBJECT IDENTIFIER ::= {
+// iso(1) identified-organization(3) certicom(132) curve(0) 33 }
+//
+// secp256r1 OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)
+// prime(1) 7 }
+//
+// secp384r1 OBJECT IDENTIFIER ::= {
+// iso(1) identified-organization(3) certicom(132) curve(0) 34 }
+//
+// secp521r1 OBJECT IDENTIFIER ::= {
+// iso(1) identified-organization(3) certicom(132) curve(0) 35 }
+//
+// NB: secp256r1 is equivalent to prime256v1
+var (
+ oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33}
+ oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
+ oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
+ oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
+)
+
+func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
+ switch {
+ case oid.Equal(oidNamedCurveP224):
+ return elliptic.P224()
+ case oid.Equal(oidNamedCurveP256):
+ return elliptic.P256()
+ case oid.Equal(oidNamedCurveP384):
+ return elliptic.P384()
+ case oid.Equal(oidNamedCurveP521):
+ return elliptic.P521()
+ }
+ return nil
+}
+
+func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {
+ switch curve {
+ case elliptic.P224():
+ return oidNamedCurveP224, true
+ case elliptic.P256():
+ return oidNamedCurveP256, true
+ case elliptic.P384():
+ return oidNamedCurveP384, true
+ case elliptic.P521():
+ return oidNamedCurveP521, true
+ }
+
+ return nil, false
+}
+
// KeyUsage represents the set of actions that are valid for a given key. It's
// a bitmap of the KeyUsage* constants.
type KeyUsage int
@@ -262,13 +362,18 @@ const (
// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
var (
- oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0}
- oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1}
- oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
- oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3}
- oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4}
- oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8}
- oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9}
+ oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0}
+ oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1}
+ oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
+ oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3}
+ oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4}
+ oidExtKeyUsageIPSECEndSystem = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5}
+ oidExtKeyUsageIPSECTunnel = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6}
+ oidExtKeyUsageIPSECUser = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7}
+ oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8}
+ oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9}
+ oidExtKeyUsageMicrosoftServerGatedCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3}
+ oidExtKeyUsageNetscapeServerGatedCrypto = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1}
)
// ExtKeyUsage represents an extended set of actions that are valid for a given key.
@@ -281,10 +386,52 @@ const (
ExtKeyUsageClientAuth
ExtKeyUsageCodeSigning
ExtKeyUsageEmailProtection
+ ExtKeyUsageIPSECEndSystem
+ ExtKeyUsageIPSECTunnel
+ ExtKeyUsageIPSECUser
ExtKeyUsageTimeStamping
ExtKeyUsageOCSPSigning
+ ExtKeyUsageMicrosoftServerGatedCrypto
+ ExtKeyUsageNetscapeServerGatedCrypto
)
+// extKeyUsageOIDs contains the mapping between an ExtKeyUsage and its OID.
+var extKeyUsageOIDs = []struct {
+ extKeyUsage ExtKeyUsage
+ oid asn1.ObjectIdentifier
+}{
+ {ExtKeyUsageAny, oidExtKeyUsageAny},
+ {ExtKeyUsageServerAuth, oidExtKeyUsageServerAuth},
+ {ExtKeyUsageClientAuth, oidExtKeyUsageClientAuth},
+ {ExtKeyUsageCodeSigning, oidExtKeyUsageCodeSigning},
+ {ExtKeyUsageEmailProtection, oidExtKeyUsageEmailProtection},
+ {ExtKeyUsageIPSECEndSystem, oidExtKeyUsageIPSECEndSystem},
+ {ExtKeyUsageIPSECTunnel, oidExtKeyUsageIPSECTunnel},
+ {ExtKeyUsageIPSECUser, oidExtKeyUsageIPSECUser},
+ {ExtKeyUsageTimeStamping, oidExtKeyUsageTimeStamping},
+ {ExtKeyUsageOCSPSigning, oidExtKeyUsageOCSPSigning},
+ {ExtKeyUsageMicrosoftServerGatedCrypto, oidExtKeyUsageMicrosoftServerGatedCrypto},
+ {ExtKeyUsageNetscapeServerGatedCrypto, oidExtKeyUsageNetscapeServerGatedCrypto},
+}
+
+func extKeyUsageFromOID(oid asn1.ObjectIdentifier) (eku ExtKeyUsage, ok bool) {
+ for _, pair := range extKeyUsageOIDs {
+ if oid.Equal(pair.oid) {
+ return pair.extKeyUsage, true
+ }
+ }
+ return
+}
+
+func oidFromExtKeyUsage(eku ExtKeyUsage) (oid asn1.ObjectIdentifier, ok bool) {
+ for _, pair := range extKeyUsageOIDs {
+ if eku == pair.extKeyUsage {
+ return pair.oid, true
+ }
+ }
+ return
+}
+
// A Certificate represents an X.509 certificate.
type Certificate struct {
Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature).
@@ -319,6 +466,7 @@ type Certificate struct {
// Subject Alternate Name values
DNSNames []string
EmailAddresses []string
+ IPAddresses []net.IP
// Name constraints
PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical.
@@ -344,6 +492,55 @@ func (c *Certificate) Equal(other *Certificate) bool {
return bytes.Equal(c.Raw, other.Raw)
}
+// Entrust have a broken root certificate (CN=Entrust.net Certification
+// Authority (2048)) which isn't marked as a CA certificate and is thus invalid
+// according to PKIX.
+// We recognise this certificate by its SubjectPublicKeyInfo and exempt it
+// from the Basic Constraints requirement.
+// See http://www.entrust.net/knowledge-base/technote.cfm?tn=7869
+//
+// TODO(agl): remove this hack once their reissued root is sufficiently
+// widespread.
+var entrustBrokenSPKI = []byte{
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0x97, 0xa3, 0x2d, 0x3c, 0x9e, 0xde, 0x05,
+ 0xda, 0x13, 0xc2, 0x11, 0x8d, 0x9d, 0x8e, 0xe3,
+ 0x7f, 0xc7, 0x4b, 0x7e, 0x5a, 0x9f, 0xb3, 0xff,
+ 0x62, 0xab, 0x73, 0xc8, 0x28, 0x6b, 0xba, 0x10,
+ 0x64, 0x82, 0x87, 0x13, 0xcd, 0x57, 0x18, 0xff,
+ 0x28, 0xce, 0xc0, 0xe6, 0x0e, 0x06, 0x91, 0x50,
+ 0x29, 0x83, 0xd1, 0xf2, 0xc3, 0x2a, 0xdb, 0xd8,
+ 0xdb, 0x4e, 0x04, 0xcc, 0x00, 0xeb, 0x8b, 0xb6,
+ 0x96, 0xdc, 0xbc, 0xaa, 0xfa, 0x52, 0x77, 0x04,
+ 0xc1, 0xdb, 0x19, 0xe4, 0xae, 0x9c, 0xfd, 0x3c,
+ 0x8b, 0x03, 0xef, 0x4d, 0xbc, 0x1a, 0x03, 0x65,
+ 0xf9, 0xc1, 0xb1, 0x3f, 0x72, 0x86, 0xf2, 0x38,
+ 0xaa, 0x19, 0xae, 0x10, 0x88, 0x78, 0x28, 0xda,
+ 0x75, 0xc3, 0x3d, 0x02, 0x82, 0x02, 0x9c, 0xb9,
+ 0xc1, 0x65, 0x77, 0x76, 0x24, 0x4c, 0x98, 0xf7,
+ 0x6d, 0x31, 0x38, 0xfb, 0xdb, 0xfe, 0xdb, 0x37,
+ 0x02, 0x76, 0xa1, 0x18, 0x97, 0xa6, 0xcc, 0xde,
+ 0x20, 0x09, 0x49, 0x36, 0x24, 0x69, 0x42, 0xf6,
+ 0xe4, 0x37, 0x62, 0xf1, 0x59, 0x6d, 0xa9, 0x3c,
+ 0xed, 0x34, 0x9c, 0xa3, 0x8e, 0xdb, 0xdc, 0x3a,
+ 0xd7, 0xf7, 0x0a, 0x6f, 0xef, 0x2e, 0xd8, 0xd5,
+ 0x93, 0x5a, 0x7a, 0xed, 0x08, 0x49, 0x68, 0xe2,
+ 0x41, 0xe3, 0x5a, 0x90, 0xc1, 0x86, 0x55, 0xfc,
+ 0x51, 0x43, 0x9d, 0xe0, 0xb2, 0xc4, 0x67, 0xb4,
+ 0xcb, 0x32, 0x31, 0x25, 0xf0, 0x54, 0x9f, 0x4b,
+ 0xd1, 0x6f, 0xdb, 0xd4, 0xdd, 0xfc, 0xaf, 0x5e,
+ 0x6c, 0x78, 0x90, 0x95, 0xde, 0xca, 0x3a, 0x48,
+ 0xb9, 0x79, 0x3c, 0x9b, 0x19, 0xd6, 0x75, 0x05,
+ 0xa0, 0xf9, 0x88, 0xd7, 0xc1, 0xe8, 0xa5, 0x09,
+ 0xe4, 0x1a, 0x15, 0xdc, 0x87, 0x23, 0xaa, 0xb2,
+ 0x75, 0x8c, 0x63, 0x25, 0x87, 0xd8, 0xf8, 0x3d,
+ 0xa6, 0xc2, 0xcc, 0x66, 0xff, 0xa5, 0x66, 0x68,
+ 0x55, 0x02, 0x03, 0x01, 0x00, 0x01,
+}
+
// CheckSignatureFrom verifies that the signature on c is a valid signature
// from parent.
func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err error) {
@@ -352,8 +549,10 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err error) {
// certificate, or the extension is present but the cA boolean is not
// asserted, then the certified public key MUST NOT be used to verify
// certificate signatures."
- if parent.Version == 3 && !parent.BasicConstraintsValid ||
- parent.BasicConstraintsValid && !parent.IsCA {
+ // (except for Entrust, see comment above entrustBrokenSPKI)
+ if (parent.Version == 3 && !parent.BasicConstraintsValid ||
+ parent.BasicConstraintsValid && !parent.IsCA) &&
+ !bytes.Equal(c.RawSubjectPublicKeyInfo, entrustBrokenSPKI) {
return ConstraintViolationError{}
}
@@ -376,13 +575,13 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
var hashType crypto.Hash
switch algo {
- case SHA1WithRSA, DSAWithSHA1:
+ case SHA1WithRSA, DSAWithSHA1, ECDSAWithSHA1:
hashType = crypto.SHA1
- case SHA256WithRSA, DSAWithSHA256:
+ case SHA256WithRSA, DSAWithSHA256, ECDSAWithSHA256:
hashType = crypto.SHA256
- case SHA384WithRSA:
+ case SHA384WithRSA, ECDSAWithSHA384:
hashType = crypto.SHA384
- case SHA512WithRSA:
+ case SHA512WithRSA, ECDSAWithSHA512:
hashType = crypto.SHA512
default:
return ErrUnsupportedAlgorithm
@@ -411,6 +610,18 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
return errors.New("DSA verification failure")
}
return
+ case *ecdsa.PublicKey:
+ ecdsaSig := new(ecdsaSignature)
+ if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
+ return err
+ }
+ if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
+ return errors.New("crypto/x509: ECDSA signature contained zero or negative values")
+ }
+ if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) {
+ return errors.New("crypto/x509: ECDSA verification failure")
+ }
+ return
}
return ErrUnsupportedAlgorithm
}
@@ -446,8 +657,6 @@ type nameConstraints struct {
type generalSubtree struct {
Name string `asn1:"tag:2,optional,ia5"`
- Min int `asn1:"optional,tag:0"`
- Max int `asn1:"optional,tag:1"`
}
func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) {
@@ -460,6 +669,13 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
return nil, err
}
+ if p.N.Sign() <= 0 {
+ return nil, errors.New("x509: RSA modulus is not a positive number")
+ }
+ if p.E <= 0 {
+ return nil, errors.New("x509: RSA public exponent is not a positive number")
+ }
+
pub := &rsa.PublicKey{
E: p.E,
N: p.N,
@@ -489,6 +705,27 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
Y: p,
}
return pub, nil
+ case ECDSA:
+ paramsData := keyData.Algorithm.Parameters.FullBytes
+ namedCurveOID := new(asn1.ObjectIdentifier)
+ _, err := asn1.Unmarshal(paramsData, namedCurveOID)
+ if err != nil {
+ return nil, err
+ }
+ namedCurve := namedCurveFromOID(*namedCurveOID)
+ if namedCurve == nil {
+ return nil, errors.New("crypto/x509: unsupported elliptic curve")
+ }
+ x, y := elliptic.Unmarshal(namedCurve, asn1Data)
+ if x == nil {
+ return nil, errors.New("crypto/x509: failed to unmarshal elliptic curve point")
+ }
+ pub := &ecdsa.PublicKey{
+ Curve: namedCurve,
+ X: x,
+ Y: y,
+ }
+ return pub, nil
default:
return nil, nil
}
@@ -607,6 +844,13 @@ func parseCertificate(in *certificate) (*Certificate, error) {
case 2:
out.DNSNames = append(out.DNSNames, string(v.Bytes))
parsedName = true
+ case 7:
+ switch len(v.Bytes) {
+ case net.IPv4len, net.IPv6len:
+ out.IPAddresses = append(out.IPAddresses, v.Bytes)
+ default:
+ return nil, errors.New("x509: certificate contained IP address of length " + strconv.Itoa(len(v.Bytes)))
+ }
}
}
@@ -643,7 +887,7 @@ func parseCertificate(in *certificate) (*Certificate, error) {
}
for _, subtree := range constraints.Permitted {
- if subtree.Min > 0 || subtree.Max > 0 || len(subtree.Name) == 0 {
+ if len(subtree.Name) == 0 {
if e.Critical {
return out, UnhandledCriticalExtension{}
}
@@ -679,22 +923,9 @@ func parseCertificate(in *certificate) (*Certificate, error) {
}
for _, u := range keyUsage {
- switch {
- case u.Equal(oidExtKeyUsageAny):
- out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageAny)
- case u.Equal(oidExtKeyUsageServerAuth):
- out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageServerAuth)
- case u.Equal(oidExtKeyUsageClientAuth):
- out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageClientAuth)
- case u.Equal(oidExtKeyUsageCodeSigning):
- out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageCodeSigning)
- case u.Equal(oidExtKeyUsageEmailProtection):
- out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageEmailProtection)
- case u.Equal(oidExtKeyUsageTimeStamping):
- out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageTimeStamping)
- case u.Equal(oidExtKeyUsageOCSPSigning):
- out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageOCSPSigning)
- default:
+ if extKeyUsage, ok := extKeyUsageFromOID(u); ok {
+ out.ExtKeyUsage = append(out.ExtKeyUsage, extKeyUsage)
+ } else {
out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u)
}
}
@@ -783,6 +1014,7 @@ func reverseBitsInAByte(in byte) byte {
var (
oidExtensionSubjectKeyId = []int{2, 5, 29, 14}
oidExtensionKeyUsage = []int{2, 5, 29, 15}
+ oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37}
oidExtensionAuthorityKeyId = []int{2, 5, 29, 35}
oidExtensionBasicConstraints = []int{2, 5, 29, 19}
oidExtensionSubjectAltName = []int{2, 5, 29, 17}
@@ -791,7 +1023,7 @@ var (
)
func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
- ret = make([]pkix.Extension, 7 /* maximum number of elements. */)
+ ret = make([]pkix.Extension, 8 /* maximum number of elements. */)
n := 0
if template.KeyUsage != 0 {
@@ -814,6 +1046,27 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++
}
+ if len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0 {
+ ret[n].Id = oidExtensionExtendedKeyUsage
+
+ var oids []asn1.ObjectIdentifier
+ for _, u := range template.ExtKeyUsage {
+ if oid, ok := oidFromExtKeyUsage(u); ok {
+ oids = append(oids, oid)
+ } else {
+ panic("internal error")
+ }
+ }
+
+ oids = append(oids, template.UnknownExtKeyUsage...)
+
+ ret[n].Value, err = asn1.Marshal(oids)
+ if err != nil {
+ return
+ }
+ n++
+ }
+
if template.BasicConstraintsValid {
ret[n].Id = oidExtensionBasicConstraints
ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen})
@@ -842,11 +1095,22 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++
}
- if len(template.DNSNames) > 0 {
+ if len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0 {
ret[n].Id = oidExtensionSubjectAltName
- rawValues := make([]asn1.RawValue, len(template.DNSNames))
- for i, name := range template.DNSNames {
- rawValues[i] = asn1.RawValue{Tag: 2, Class: 2, Bytes: []byte(name)}
+ var rawValues []asn1.RawValue
+ for _, name := range template.DNSNames {
+ rawValues = append(rawValues, asn1.RawValue{Tag: 2, Class: 2, Bytes: []byte(name)})
+ }
+ for _, email := range template.EmailAddresses {
+ rawValues = append(rawValues, asn1.RawValue{Tag: 1, Class: 2, Bytes: []byte(email)})
+ }
+ for _, rawIP := range template.IPAddresses {
+ // If possible, we always want to encode IPv4 addresses in 4 bytes.
+ ip := rawIP.To4()
+ if ip == nil {
+ ip = rawIP
+ }
+ rawValues = append(rawValues, asn1.RawValue{Tag: 7, Class: 2, Bytes: ip})
}
ret[n].Value, err = asn1.Marshal(rawValues)
if err != nil {
@@ -890,11 +1154,6 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
return ret[0:n], nil
}
-var (
- oidSHA1WithRSA = []int{1, 2, 840, 113549, 1, 1, 5}
- oidRSA = []int{1, 2, 840, 113549, 1, 1, 1}
-)
-
func subjectBytes(cert *Certificate) ([]byte, error) {
if len(cert.RawSubject) > 0 {
return cert.RawSubject, nil
@@ -905,8 +1164,9 @@ func subjectBytes(cert *Certificate) ([]byte, error) {
// CreateCertificate creates a new certificate based on a template. The
// following members of template are used: SerialNumber, Subject, NotBefore,
-// NotAfter, KeyUsage, BasicConstraintsValid, IsCA, MaxPathLen, SubjectKeyId,
-// DNSNames, PermittedDNSDomainsCritical, PermittedDNSDomains.
+// NotAfter, KeyUsage, ExtKeyUsage, UnknownExtKeyUsage, BasicConstraintsValid,
+// IsCA, MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical,
+// PermittedDNSDomains.
//
// The certificate is signed by parent. If parent is equal to template then the
// certificate is self-signed. The parameter pub is the public key of the
@@ -914,23 +1174,61 @@ func subjectBytes(cert *Certificate) ([]byte, error) {
//
// The returned slice is the certificate in DER encoding.
//
-// The only supported key type is RSA (*rsa.PublicKey for pub, *rsa.PrivateKey
-// for priv).
+// The only supported key types are RSA and ECDSA (*rsa.PublicKey or
+// *ecdsa.PublicKey for pub, *rsa.PrivateKey or *ecdsa.PublicKey for priv).
func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (cert []byte, err error) {
- rsaPub, ok := pub.(*rsa.PublicKey)
- if !ok {
- return nil, errors.New("x509: non-RSA public keys not supported")
+ var publicKeyBytes []byte
+ var publicKeyAlgorithm pkix.AlgorithmIdentifier
+
+ switch pub := pub.(type) {
+ case *rsa.PublicKey:
+ publicKeyBytes, err = asn1.Marshal(rsaPublicKey{
+ N: pub.N,
+ E: pub.E,
+ })
+ publicKeyAlgorithm.Algorithm = oidPublicKeyRSA
+ case *ecdsa.PublicKey:
+ oid, ok := oidFromNamedCurve(pub.Curve)
+ if !ok {
+ return nil, errors.New("x509: unknown elliptic curve")
+ }
+ publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
+ var paramBytes []byte
+ paramBytes, err = asn1.Marshal(oid)
+ if err != nil {
+ return
+ }
+ publicKeyAlgorithm.Parameters.FullBytes = paramBytes
+ publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
+ default:
+ return nil, errors.New("x509: only RSA and ECDSA public keys supported")
}
- rsaPriv, ok := priv.(*rsa.PrivateKey)
- if !ok {
- return nil, errors.New("x509: non-RSA private keys not supported")
+ var signatureAlgorithm pkix.AlgorithmIdentifier
+ var hashFunc crypto.Hash
+
+ switch priv := priv.(type) {
+ case *rsa.PrivateKey:
+ signatureAlgorithm.Algorithm = oidSignatureSHA1WithRSA
+ hashFunc = crypto.SHA1
+ case *ecdsa.PrivateKey:
+ switch priv.Curve {
+ case elliptic.P224(), elliptic.P256():
+ hashFunc = crypto.SHA256
+ signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA256
+ case elliptic.P384():
+ hashFunc = crypto.SHA384
+ signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA384
+ case elliptic.P521():
+ hashFunc = crypto.SHA512
+ signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA512
+ default:
+ return nil, errors.New("x509: unknown elliptic curve")
+ }
+ default:
+ return nil, errors.New("x509: only RSA and ECDSA private keys supported")
}
- asn1PublicKey, err := asn1.Marshal(rsaPublicKey{
- N: rsaPub.N,
- E: rsaPub.E,
- })
if err != nil {
return
}
@@ -954,15 +1252,15 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
return
}
- encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey}
+ encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes}
c := tbsCertificate{
Version: 2,
SerialNumber: template.SerialNumber,
- SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},
+ SignatureAlgorithm: signatureAlgorithm,
Issuer: asn1.RawValue{FullBytes: asn1Issuer},
- Validity: validity{template.NotBefore, template.NotAfter},
+ Validity: validity{template.NotBefore.UTC(), template.NotAfter.UTC()},
Subject: asn1.RawValue{FullBytes: asn1Subject},
- PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey},
+ PublicKey: publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey},
Extensions: extensions,
}
@@ -973,11 +1271,24 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
c.Raw = tbsCertContents
- h := sha1.New()
+ h := hashFunc.New()
h.Write(tbsCertContents)
digest := h.Sum(nil)
- signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest)
+ var signature []byte
+
+ switch priv := priv.(type) {
+ case *rsa.PrivateKey:
+ signature, err = rsa.SignPKCS1v15(rand, priv, hashFunc, digest)
+ case *ecdsa.PrivateKey:
+ var r, s *big.Int
+ if r, s, err = ecdsa.Sign(rand, priv, digest); err == nil {
+ signature, err = asn1.Marshal(ecdsaSignature{r, s})
+ }
+ default:
+ panic("internal error")
+ }
+
if err != nil {
return
}
@@ -985,7 +1296,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
cert, err = asn1.Marshal(certificate{
nil,
c,
- pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},
+ signatureAlgorithm,
asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
})
return
@@ -1037,8 +1348,8 @@ func (c *Certificate) CreateCRL(rand io.Reader, priv interface{}, revokedCerts [
Algorithm: oidSignatureSHA1WithRSA,
},
Issuer: c.Subject.ToRDNSequence(),
- ThisUpdate: now,
- NextUpdate: expiry,
+ ThisUpdate: now.UTC(),
+ NextUpdate: expiry.UTC(),
RevokedCertificates: revokedCerts,
}
diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go
index f0327b012..abd4fe84d 100644
--- a/src/pkg/crypto/x509/x509_test.go
+++ b/src/pkg/crypto/x509/x509_test.go
@@ -7,14 +7,20 @@ package x509
import (
"bytes"
"crypto/dsa"
+ "crypto/ecdsa"
+ "crypto/elliptic"
"crypto/rand"
"crypto/rsa"
+ _ "crypto/sha256"
+ _ "crypto/sha512"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"math/big"
+ "net"
+ "reflect"
"testing"
"time"
)
@@ -169,6 +175,49 @@ func TestMatchHostnames(t *testing.T) {
}
}
+func TestMatchIP(t *testing.T) {
+ // Check that pattern matching is working.
+ c := &Certificate{
+ DNSNames: []string{"*.foo.bar.baz"},
+ Subject: pkix.Name{
+ CommonName: "*.foo.bar.baz",
+ },
+ }
+ err := c.VerifyHostname("quux.foo.bar.baz")
+ if err != nil {
+ t.Fatalf("VerifyHostname(quux.foo.bar.baz): %v", err)
+ }
+
+ // But check that if we change it to be matching against an IP address,
+ // it is rejected.
+ c = &Certificate{
+ DNSNames: []string{"*.2.3.4"},
+ Subject: pkix.Name{
+ CommonName: "*.2.3.4",
+ },
+ }
+ err = c.VerifyHostname("1.2.3.4")
+ if err == nil {
+ t.Fatalf("VerifyHostname(1.2.3.4) should have failed, did not")
+ }
+
+ c = &Certificate{
+ IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")},
+ }
+ err = c.VerifyHostname("127.0.0.1")
+ if err != nil {
+ t.Fatalf("VerifyHostname(127.0.0.1): %v", err)
+ }
+ err = c.VerifyHostname("::1")
+ if err != nil {
+ t.Fatalf("VerifyHostname(::1): %v", err)
+ }
+ err = c.VerifyHostname("[::1]")
+ if err != nil {
+ t.Fatalf("VerifyHostname([::1]): %v", err)
+ }
+}
+
func TestCertificateParse(t *testing.T) {
s, _ := hex.DecodeString(certBytes)
certs, err := ParseCertificates(s)
@@ -237,65 +286,220 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
random := rand.Reader
block, _ := pem.Decode([]byte(pemPrivateKey))
- priv, err := ParsePKCS1PrivateKey(block.Bytes)
+ rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
- t.Errorf("Failed to parse private key: %s", err)
- return
+ t.Fatalf("Failed to parse private key: %s", err)
}
- commonName := "test.example.com"
- template := Certificate{
- SerialNumber: big.NewInt(1),
- Subject: pkix.Name{
- CommonName: commonName,
- Organization: []string{"Acme Co"},
- },
- NotBefore: time.Unix(1000, 0),
- NotAfter: time.Unix(100000, 0),
+ ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ t.Fatalf("Failed to generate ECDSA key: %s", err)
+ }
- SubjectKeyId: []byte{1, 2, 3, 4},
- KeyUsage: KeyUsageCertSign,
+ tests := []struct {
+ name string
+ pub, priv interface{}
+ checkSig bool
+ }{
+ {"RSA/RSA", &rsaPriv.PublicKey, rsaPriv, true},
+ {"RSA/ECDSA", &rsaPriv.PublicKey, ecdsaPriv, false},
+ {"ECDSA/RSA", &ecdsaPriv.PublicKey, rsaPriv, false},
+ {"ECDSA/ECDSA", &ecdsaPriv.PublicKey, ecdsaPriv, true},
+ }
- BasicConstraintsValid: true,
- IsCA: true,
- DNSNames: []string{"test.example.com"},
+ testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth}
+ testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{3, 2, 1}}
- PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
- PermittedDNSDomains: []string{".example.com", "example.com"},
- }
+ for _, test := range tests {
+ commonName := "test.example.com"
+ template := Certificate{
+ SerialNumber: big.NewInt(1),
+ Subject: pkix.Name{
+ CommonName: commonName,
+ Organization: []string{"Σ Acme Co"},
+ },
+ NotBefore: time.Unix(1000, 0),
+ NotAfter: time.Unix(100000, 0),
- derBytes, err := CreateCertificate(random, &template, &template, &priv.PublicKey, priv)
- if err != nil {
- t.Errorf("Failed to create certificate: %s", err)
- return
- }
+ SubjectKeyId: []byte{1, 2, 3, 4},
+ KeyUsage: KeyUsageCertSign,
- cert, err := ParseCertificate(derBytes)
- if err != nil {
- t.Errorf("Failed to parse certificate: %s", err)
- return
- }
+ ExtKeyUsage: testExtKeyUsage,
+ UnknownExtKeyUsage: testUnknownExtKeyUsage,
- if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) {
- t.Errorf("Failed to parse policy identifiers: got:%#v want:%#v", cert.PolicyIdentifiers, template.PolicyIdentifiers)
- }
+ BasicConstraintsValid: true,
+ IsCA: true,
- if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" {
- t.Errorf("Failed to parse name constraints: %#v", cert.PermittedDNSDomains)
- }
+ DNSNames: []string{"test.example.com"},
+ EmailAddresses: []string{"gopher@golang.org"},
+ IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")},
- if cert.Subject.CommonName != commonName {
- t.Errorf("Subject wasn't correctly copied from the template. Got %s, want %s", cert.Subject.CommonName, commonName)
- }
+ PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
+ PermittedDNSDomains: []string{".example.com", "example.com"},
+ }
+
+ derBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv)
+ if err != nil {
+ t.Errorf("%s: failed to create certificate: %s", test.name, err)
+ continue
+ }
+
+ cert, err := ParseCertificate(derBytes)
+ if err != nil {
+ t.Errorf("%s: failed to parse certificate: %s", test.name, err)
+ continue
+ }
+
+ if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) {
+ t.Errorf("%s: failed to parse policy identifiers: got:%#v want:%#v", test.name, cert.PolicyIdentifiers, template.PolicyIdentifiers)
+ }
+
+ if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" {
+ t.Errorf("%s: failed to parse name constraints: %#v", test.name, cert.PermittedDNSDomains)
+ }
+
+ if cert.Subject.CommonName != commonName {
+ t.Errorf("%s: subject wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Subject.CommonName, commonName)
+ }
+
+ if cert.Issuer.CommonName != commonName {
+ t.Errorf("%s: issuer wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Issuer.CommonName, commonName)
+ }
+
+ if !reflect.DeepEqual(cert.ExtKeyUsage, testExtKeyUsage) {
+ t.Errorf("%s: extkeyusage wasn't correctly copied from the template. Got %v, want %v", test.name, cert.ExtKeyUsage, testExtKeyUsage)
+ }
- if cert.Issuer.CommonName != commonName {
- t.Errorf("Issuer wasn't correctly copied from the template. Got %s, want %s", cert.Issuer.CommonName, commonName)
+ if !reflect.DeepEqual(cert.UnknownExtKeyUsage, testUnknownExtKeyUsage) {
+ t.Errorf("%s: unknown extkeyusage wasn't correctly copied from the template. Got %v, want %v", test.name, cert.UnknownExtKeyUsage, testUnknownExtKeyUsage)
+ }
+
+ if !reflect.DeepEqual(cert.DNSNames, template.DNSNames) {
+ t.Errorf("%s: SAN DNS names differ from template. Got %v, want %v", test.name, cert.DNSNames, template.DNSNames)
+ }
+
+ if !reflect.DeepEqual(cert.EmailAddresses, template.EmailAddresses) {
+ t.Errorf("%s: SAN emails differ from template. Got %v, want %v", test.name, cert.EmailAddresses, template.EmailAddresses)
+ }
+
+ if !reflect.DeepEqual(cert.IPAddresses, template.IPAddresses) {
+ t.Errorf("%s: SAN IPs differ from template. Got %v, want %v", test.name, cert.IPAddresses, template.IPAddresses)
+ }
+
+ if test.checkSig {
+ err = cert.CheckSignatureFrom(cert)
+ if err != nil {
+ t.Errorf("%s: signature verification failed: %s", test.name, err)
+ }
+ }
}
+}
- err = cert.CheckSignatureFrom(cert)
- if err != nil {
- t.Errorf("Signature verification failed: %s", err)
- return
+// Self-signed certificate using ECDSA with SHA1 & secp256r1
+var ecdsaSHA1CertPem = `
+-----BEGIN CERTIFICATE-----
+MIICDjCCAbUCCQDF6SfN0nsnrjAJBgcqhkjOPQQBMIGPMQswCQYDVQQGEwJVUzET
+MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMG
+A1UECgwMR29vZ2xlLCBJbmMuMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
+CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIwMjAyMDUw
+WhcNMjIwNTE4MjAyMDUwWjCBjzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
+b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTATBgNVBAoMDEdvb2dsZSwg
+SW5jLjEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20xIzAhBgkqhkiG9w0BCQEWFGdv
+bGFuZy1kZXZAZ21haWwuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/Wgn
+WQDo5+bz71T0327ERgd5SDDXFbXLpzIZDXTkjpe8QTEbsF+ezsQfrekrpDPC4Cd3
+P9LY0tG+aI8IyVKdUjAJBgcqhkjOPQQBA0gAMEUCIGlsqMcRqWVIWTD6wXwe6Jk2
+DKxL46r/FLgJYnzBEH99AiEA3fBouObsvV1R3oVkb4BQYnD4/4LeId6lAT43YvyV
+a/A=
+-----END CERTIFICATE-----
+`
+
+// Self-signed certificate using ECDSA with SHA256 & secp256r1
+var ecdsaSHA256p256CertPem = `
+-----BEGIN CERTIFICATE-----
+MIICDzCCAbYCCQDlsuMWvgQzhTAKBggqhkjOPQQDAjCBjzELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
+BgNVBAoMDEdvb2dsZSwgSW5jLjEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20xIzAh
+BgkqhkiG9w0BCQEWFGdvbGFuZy1kZXZAZ21haWwuY29tMB4XDTEyMDUyMTAwMTkx
+NloXDTIyMDUxOTAwMTkxNlowgY8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
+Zm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYDVQQKDAxHb29nbGUs
+IEluYy4xFzAVBgNVBAMMDnd3dy5nb29nbGUuY29tMSMwIQYJKoZIhvcNAQkBFhRn
+b2xhbmctZGV2QGdtYWlsLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPMt
+2ErhxAty5EJRu9yM+MTy+hUXm3pdW1ensAv382KoGExSXAFWP7pjJnNtHO+XSwVm
+YNtqjcAGFKpweoN//kQwCgYIKoZIzj0EAwIDRwAwRAIgIYSaUA/IB81gjbIw/hUV
+70twxJr5EcgOo0hLp3Jm+EYCIFDO3NNcgmURbJ1kfoS3N/0O+irUtoPw38YoNkqJ
+h5wi
+-----END CERTIFICATE-----
+`
+
+// Self-signed certificate using ECDSA with SHA256 & secp384r1
+var ecdsaSHA256p384CertPem = `
+-----BEGIN CERTIFICATE-----
+MIICSjCCAdECCQDje/no7mXkVzAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS
+BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
+CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMDYxMDM0
+WhcNMjIwNTE5MDYxMDM0WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
+b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg
+SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s
+YW5nLWRldkBnbWFpbC5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARRuzRNIKRK
+jIktEmXanNmrTR/q/FaHXLhWRZ6nHWe26Fw7Rsrbk+VjGy4vfWtNn7xSFKrOu5ze
+qxKnmE0h5E480MNgrUiRkaGO2GMJJVmxx20aqkXOk59U8yGA4CghE6MwCgYIKoZI
+zj0EAwIDZwAwZAIwBZEN8gvmRmfeP/9C1PRLzODIY4JqWub2PLRT4mv9GU+yw3Gr
+PU9A3CHMdEcdw/MEAjBBO1lId8KOCh9UZunsSMfqXiVurpzmhWd6VYZ/32G+M+Mh
+3yILeYQzllt/g0rKVRk=
+-----END CERTIFICATE-----
+`
+
+// Self-signed certificate using ECDSA with SHA384 & secp521r1
+var ecdsaSHA384p521CertPem = `
+-----BEGIN CERTIFICATE-----
+MIICljCCAfcCCQDhp1AFD/ahKjAKBggqhkjOPQQDAzCBjjELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS
+BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
+CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMTUwNDI5
+WhcNMjIwNTE5MTUwNDI5WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
+b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg
+SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s
+YW5nLWRldkBnbWFpbC5jb20wgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABACqx9Rv
+IssRs1LWYcNN+WffwlHw4Tv3y8/LIAA9MF1ZScIonU9nRMxt4a2uGJVCPDw6JHpz
+PaYc0E9puLoE9AfKpwFr59Jkot7dBg55SKPEFkddoip/rvmN7NPAWjMBirOwjOkm
+8FPthvPhGPqsu9AvgVuHu3PosWiHGNrhh379pva8MzAKBggqhkjOPQQDAwOBjAAw
+gYgCQgEHNmswkUdPpHqrVxp9PvLVl+xxPuHBkT+75z9JizyxtqykHQo9Uh6SWCYH
+BF9KLolo01wMt8DjoYP5Fb3j5MH7xwJCAbWZzTOp4l4DPkIvAh4LeC4VWbwPPyqh
+kBg71w/iEcSY3wUKgHGcJJrObZw7wys91I5kENljqw/Samdr3ka+jBJa
+-----END CERTIFICATE-----
+`
+
+var ecdsaTests = []struct {
+ sigAlgo SignatureAlgorithm
+ pemCert string
+}{
+ {ECDSAWithSHA1, ecdsaSHA1CertPem},
+ {ECDSAWithSHA256, ecdsaSHA256p256CertPem},
+ {ECDSAWithSHA256, ecdsaSHA256p384CertPem},
+ {ECDSAWithSHA384, ecdsaSHA384p521CertPem},
+}
+
+func TestECDSA(t *testing.T) {
+ for i, test := range ecdsaTests {
+ pemBlock, _ := pem.Decode([]byte(test.pemCert))
+ cert, err := ParseCertificate(pemBlock.Bytes)
+ if err != nil {
+ t.Errorf("%d: failed to parse certificate: %s", i, err)
+ continue
+ }
+ if sa := cert.SignatureAlgorithm; sa != test.sigAlgo {
+ t.Errorf("%d: signature algorithm is %v, want %v", i, sa, test.sigAlgo)
+ }
+ if parsedKey, ok := cert.PublicKey.(*ecdsa.PublicKey); !ok {
+ t.Errorf("%d: wanted an ECDSA public key but found: %#v", i, parsedKey)
+ }
+ if pka := cert.PublicKeyAlgorithm; pka != ECDSA {
+ t.Errorf("%d: public key algorithm is %v, want ECDSA", i, pka)
+ }
+ if err = cert.CheckSignatureFrom(cert); err != nil {
+ t.Errorf("%d: certificate verification failed: %s", i, err)
+ }
}
}
@@ -374,7 +578,7 @@ func TestVerifyCertificateWithDSASignature(t *testing.T) {
}
// test cert is self-signed
if err = cert.CheckSignatureFrom(cert); err != nil {
- t.Fatalf("DSA Certificate verfication failed: %s", err)
+ t.Fatalf("DSA Certificate verification failed: %s", err)
}
}
diff --git a/src/pkg/database/sql/convert.go b/src/pkg/database/sql/convert.go
index bfcb03ccf..853a7826c 100644
--- a/src/pkg/database/sql/convert.go
+++ b/src/pkg/database/sql/convert.go
@@ -14,53 +14,115 @@ import (
"strconv"
)
-// subsetTypeArgs takes a slice of arguments from callers of the sql
-// package and converts them into a slice of the driver package's
-// "subset types".
-func subsetTypeArgs(args []interface{}) ([]driver.Value, error) {
- out := make([]driver.Value, len(args))
+var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
+
+// driverArgs converts arguments from callers of Stmt.Exec and
+// Stmt.Query into driver Values.
+//
+// The statement si may be nil, if no statement is available.
+func driverArgs(si driver.Stmt, args []interface{}) ([]driver.Value, error) {
+ dargs := make([]driver.Value, len(args))
+ cc, ok := si.(driver.ColumnConverter)
+
+ // Normal path, for a driver.Stmt that is not a ColumnConverter.
+ if !ok {
+ for n, arg := range args {
+ var err error
+ dargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
+ if err != nil {
+ return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
+ }
+ }
+ return dargs, nil
+ }
+
+ // Let the Stmt convert its own arguments.
for n, arg := range args {
+ // First, see if the value itself knows how to convert
+ // itself to a driver type. For example, a NullString
+ // struct changing into a string or nil.
+ if svi, ok := arg.(driver.Valuer); ok {
+ sv, err := svi.Value()
+ if err != nil {
+ return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err)
+ }
+ if !driver.IsValue(sv) {
+ return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv)
+ }
+ arg = sv
+ }
+
+ // Second, ask the column to sanity check itself. For
+ // example, drivers might use this to make sure that
+ // an int64 values being inserted into a 16-bit
+ // integer field is in range (before getting
+ // truncated), or that a nil can't go into a NOT NULL
+ // column before going across the network to get the
+ // same error.
var err error
- out[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
+ dargs[n], err = cc.ColumnConverter(n).ConvertValue(arg)
if err != nil {
- return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n+1, err)
+ return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n, err)
+ }
+ if !driver.IsValue(dargs[n]) {
+ return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T",
+ arg, dargs[n])
}
}
- return out, nil
+
+ return dargs, nil
}
// convertAssign copies to dest the value in src, converting it if possible.
// An error is returned if the copy would result in loss of information.
// dest should be a pointer type.
func convertAssign(dest, src interface{}) error {
- // Common cases, without reflect. Fall through.
+ // Common cases, without reflect.
switch s := src.(type) {
case string:
switch d := dest.(type) {
case *string:
+ if d == nil {
+ return errNilPtr
+ }
*d = s
return nil
case *[]byte:
+ if d == nil {
+ return errNilPtr
+ }
*d = []byte(s)
return nil
}
case []byte:
switch d := dest.(type) {
case *string:
+ if d == nil {
+ return errNilPtr
+ }
*d = string(s)
return nil
case *interface{}:
+ if d == nil {
+ return errNilPtr
+ }
bcopy := make([]byte, len(s))
copy(bcopy, s)
*d = bcopy
return nil
case *[]byte:
+ if d == nil {
+ return errNilPtr
+ }
*d = s
return nil
}
case nil:
switch d := dest.(type) {
case *[]byte:
+ if d == nil {
+ return errNilPtr
+ }
*d = nil
return nil
}
@@ -98,6 +160,9 @@ func convertAssign(dest, src interface{}) error {
if dpv.Kind() != reflect.Ptr {
return errors.New("destination not a pointer")
}
+ if dpv.IsNil() {
+ return errNilPtr
+ }
if !sv.IsValid() {
sv = reflect.ValueOf(src)
diff --git a/src/pkg/database/sql/doc.txt b/src/pkg/database/sql/doc.txt
index fb1659548..405c5ed2a 100644
--- a/src/pkg/database/sql/doc.txt
+++ b/src/pkg/database/sql/doc.txt
@@ -21,7 +21,7 @@ Goals of the sql and sql/driver packages:
Database Driver -> sql (to register) + sql/driver (implement interfaces)
* Make type casting/conversions consistent between all drivers. To
- achieve this, most of the conversions are done in the db package,
+ achieve this, most of the conversions are done in the sql package,
not in each driver. The drivers then only have to deal with a
smaller set of types.
diff --git a/src/pkg/database/sql/driver/driver.go b/src/pkg/database/sql/driver/driver.go
index 2f5280db8..2434e419b 100644
--- a/src/pkg/database/sql/driver/driver.go
+++ b/src/pkg/database/sql/driver/driver.go
@@ -56,7 +56,7 @@ var ErrBadConn = errors.New("driver: bad connection")
// Execer is an optional interface that may be implemented by a Conn.
//
-// If a Conn does not implement Execer, the db package's DB.Exec will
+// If a Conn does not implement Execer, the sql package's DB.Exec will
// first prepare a query, execute the statement, and then close the
// statement.
//
@@ -65,6 +65,17 @@ type Execer interface {
Exec(query string, args []Value) (Result, error)
}
+// Queryer is an optional interface that may be implemented by a Conn.
+//
+// If a Conn does not implement Queryer, the sql package's DB.Query will
+// first prepare a query, execute the statement, and then close the
+// statement.
+//
+// Query may return ErrSkip.
+type Queryer interface {
+ Query(query string, args []Value) (Rows, error)
+}
+
// Conn is a connection to a database. It is not used concurrently
// by multiple goroutines.
//
@@ -104,23 +115,8 @@ type Result interface {
type Stmt interface {
// Close closes the statement.
//
- // Closing a statement should not interrupt any outstanding
- // query created from that statement. That is, the following
- // order of operations is valid:
- //
- // * create a driver statement
- // * call Query on statement, returning Rows
- // * close the statement
- // * read from Rows
- //
- // If closing a statement invalidates currently-running
- // queries, the final step above will incorrectly fail.
- //
- // TODO(bradfitz): possibly remove the restriction above, if
- // enough driver authors object and find it complicates their
- // code too much. The sql package could be smarter about
- // refcounting the statement and closing it at the appropriate
- // time.
+ // As of Go 1.1, a Stmt will not be closed if it's in use
+ // by any queries.
Close() error
// NumInput returns the number of placeholder parameters.
diff --git a/src/pkg/database/sql/example_test.go b/src/pkg/database/sql/example_test.go
new file mode 100644
index 000000000..d47eed50c
--- /dev/null
+++ b/src/pkg/database/sql/example_test.go
@@ -0,0 +1,45 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sql_test
+
+import (
+ "database/sql"
+ "fmt"
+ "log"
+)
+
+var db *sql.DB
+
+func ExampleDB_Query() {
+ age := 27
+ rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
+ if err != nil {
+ log.Fatal(err)
+ }
+ for rows.Next() {
+ var name string
+ if err := rows.Scan(&name); err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("%s is %d\n", name, age)
+ }
+ if err := rows.Err(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleDB_QueryRow() {
+ id := 123
+ var username string
+ err := db.QueryRow("SELECT username FROM users WHERE id=?", id).Scan(&username)
+ switch {
+ case err == sql.ErrNoRows:
+ log.Printf("No user with that ID.")
+ case err != nil:
+ log.Fatal(err)
+ default:
+ fmt.Printf("Username is %s\n", username)
+ }
+}
diff --git a/src/pkg/database/sql/fakedb_test.go b/src/pkg/database/sql/fakedb_test.go
index 184e7756c..55597f7de 100644
--- a/src/pkg/database/sql/fakedb_test.go
+++ b/src/pkg/database/sql/fakedb_test.go
@@ -31,7 +31,7 @@ var _ = log.Printf
// INSERT|<tablename>|col=val,col2=val2,col3=?
// SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=?
//
-// When opening a a fakeDriver's database, it starts empty with no
+// When opening a fakeDriver's database, it starts empty with no
// tables. All tables and data are stored in memory only.
type fakeDriver struct {
mu sync.Mutex
@@ -42,9 +42,10 @@ type fakeDriver struct {
type fakeDB struct {
name string
- mu sync.Mutex
- free []*fakeConn
- tables map[string]*table
+ mu sync.Mutex
+ free []*fakeConn
+ tables map[string]*table
+ badConn bool
}
type table struct {
@@ -83,6 +84,7 @@ type fakeConn struct {
stmtsMade int
stmtsClosed int
numPrepare int
+ bad bool
}
func (c *fakeConn) incrStat(v *int) {
@@ -122,7 +124,9 @@ func init() {
// Supports dsn forms:
// <dbname>
-// <dbname>;<opts> (no currently supported options)
+// <dbname>;<opts> (only currently supported option is `badConn`,
+// which causes driver.ErrBadConn to be returned on
+// every other conn.Begin())
func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
parts := strings.Split(dsn, ";")
if len(parts) < 1 {
@@ -135,7 +139,12 @@ func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
d.mu.Lock()
d.openCount++
d.mu.Unlock()
- return &fakeConn{db: db}, nil
+ conn := &fakeConn{db: db}
+
+ if len(parts) >= 2 && parts[1] == "badConn" {
+ conn.bad = true
+ }
+ return conn, nil
}
func (d *fakeDriver) getDB(name string) *fakeDB {
@@ -199,7 +208,20 @@ func (db *fakeDB) columnType(table, column string) (typ string, ok bool) {
return "", false
}
+func (c *fakeConn) isBad() bool {
+ // if not simulating bad conn, do nothing
+ if !c.bad {
+ return false
+ }
+ // alternate between bad conn and not bad conn
+ c.db.badConn = !c.db.badConn
+ return c.db.badConn
+}
+
func (c *fakeConn) Begin() (driver.Tx, error) {
+ if c.isBad() {
+ return nil, driver.ErrBadConn
+ }
if c.currTx != nil {
return nil, errors.New("already in a transaction")
}
@@ -234,7 +256,19 @@ func checkSubsetTypes(args []driver.Value) error {
func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error) {
// This is an optional interface, but it's implemented here
- // just to check that all the args of of the proper types.
+ // just to check that all the args are of the proper types.
+ // ErrSkip is returned so the caller acts as if we didn't
+ // implement this at all.
+ err := checkSubsetTypes(args)
+ if err != nil {
+ return nil, err
+ }
+ return nil, driver.ErrSkip
+}
+
+func (c *fakeConn) Query(query string, args []driver.Value) (driver.Rows, error) {
+ // This is an optional interface, but it's implemented here
+ // just to check that all the args are of the proper types.
// ErrSkip is returned so the caller acts as if we didn't
// implement this at all.
err := checkSubsetTypes(args)
@@ -249,7 +283,7 @@ func errf(msg string, args ...interface{}) error {
}
// parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=?
-// (note that where where columns must always contain ? marks,
+// (note that where columns must always contain ? marks,
// just a limitation for fakedb)
func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
if len(parts) != 3 {
@@ -383,6 +417,9 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
}
func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
+ if len(s.placeholderConverter) == 0 {
+ return driver.DefaultParameterConverter
+ }
return s.placeholderConverter[idx]
}
@@ -598,6 +635,28 @@ func (rc *rowsCursor) Next(dest []driver.Value) error {
return nil
}
+// fakeDriverString is like driver.String, but indirects pointers like
+// DefaultValueConverter.
+//
+// This could be surprising behavior to retroactively apply to
+// driver.String now that Go1 is out, but this is convenient for
+// our TestPointerParamsAndScans.
+//
+type fakeDriverString struct{}
+
+func (fakeDriverString) ConvertValue(v interface{}) (driver.Value, error) {
+ switch c := v.(type) {
+ case string, []byte:
+ return v, nil
+ case *string:
+ if c == nil {
+ return nil, nil
+ }
+ return *c, nil
+ }
+ return fmt.Sprintf("%v", v), nil
+}
+
func converterForType(typ string) driver.ValueConverter {
switch typ {
case "bool":
@@ -607,9 +666,9 @@ func converterForType(typ string) driver.ValueConverter {
case "int32":
return driver.Int32
case "string":
- return driver.NotNull{Converter: driver.String}
+ return driver.NotNull{Converter: fakeDriverString{}}
case "nullstring":
- return driver.Null{Converter: driver.String}
+ return driver.Null{Converter: fakeDriverString{}}
case "int64":
// TODO(coopernurse): add type-specific converter
return driver.NotNull{Converter: driver.DefaultParameterConverter}
diff --git a/src/pkg/database/sql/sql.go b/src/pkg/database/sql/sql.go
index 89136ef6e..4faaa11b1 100644
--- a/src/pkg/database/sql/sql.go
+++ b/src/pkg/database/sql/sql.go
@@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"io"
+ "runtime"
"sync"
)
@@ -189,9 +190,66 @@ type DB struct {
driver driver.Driver
dsn string
- mu sync.Mutex // protects freeConn and closed
- freeConn []driver.Conn
- closed bool
+ mu sync.Mutex // protects following fields
+ outConn map[driver.Conn]bool // whether the conn is in use
+ freeConn []driver.Conn
+ closed bool
+ dep map[finalCloser]depSet
+ onConnPut map[driver.Conn][]func() // code (with mu held) run when conn is next returned
+ lastPut map[driver.Conn]string // stacktrace of last conn's put; debug only
+}
+
+// depSet is a finalCloser's outstanding dependencies
+type depSet map[interface{}]bool // set of true bools
+
+// The finalCloser interface is used by (*DB).addDep and (*DB).get
+type finalCloser interface {
+ // finalClose is called when the reference count of an object
+ // goes to zero. (*DB).mu is not held while calling it.
+ finalClose() error
+}
+
+// addDep notes that x now depends on dep, and x's finalClose won't be
+// called until all of x's dependencies are removed with removeDep.
+func (db *DB) addDep(x finalCloser, dep interface{}) {
+ //println(fmt.Sprintf("addDep(%T %p, %T %p)", x, x, dep, dep))
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ if db.dep == nil {
+ db.dep = make(map[finalCloser]depSet)
+ }
+ xdep := db.dep[x]
+ if xdep == nil {
+ xdep = make(depSet)
+ db.dep[x] = xdep
+ }
+ xdep[dep] = true
+}
+
+// removeDep notes that x no longer depends on dep.
+// If x still has dependencies, nil is returned.
+// If x no longer has any dependencies, its finalClose method will be
+// called and its error value will be returned.
+func (db *DB) removeDep(x finalCloser, dep interface{}) error {
+ //println(fmt.Sprintf("removeDep(%T %p, %T %p)", x, x, dep, dep))
+ done := false
+
+ db.mu.Lock()
+ xdep := db.dep[x]
+ if xdep != nil {
+ delete(xdep, dep)
+ if len(xdep) == 0 {
+ delete(db.dep, x)
+ done = true
+ }
+ }
+ db.mu.Unlock()
+
+ if !done {
+ return nil
+ }
+ //println(fmt.Sprintf("calling final close on %T %v (%#v)", x, x, x))
+ return x.finalClose()
}
// Open opens a database specified by its database driver name and a
@@ -201,11 +259,20 @@ type DB struct {
// Most users will open a database via a driver-specific connection
// helper function that returns a *DB.
func Open(driverName, dataSourceName string) (*DB, error) {
- driver, ok := drivers[driverName]
+ driveri, ok := drivers[driverName]
if !ok {
return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
}
- return &DB{driver: driver, dsn: dataSourceName}, nil
+ // TODO: optionally proactively connect to a Conn to check
+ // the dataSourceName: golang.org/issue/4804
+ db := &DB{
+ driver: driveri,
+ dsn: dataSourceName,
+ outConn: make(map[driver.Conn]bool),
+ lastPut: make(map[driver.Conn]string),
+ onConnPut: make(map[driver.Conn][]func()),
+ }
+ return db, nil
}
// Close closes the database, releasing any open resources.
@@ -241,22 +308,38 @@ func (db *DB) conn() (driver.Conn, error) {
if n := len(db.freeConn); n > 0 {
conn := db.freeConn[n-1]
db.freeConn = db.freeConn[:n-1]
+ db.outConn[conn] = true
db.mu.Unlock()
return conn, nil
}
db.mu.Unlock()
- return db.driver.Open(db.dsn)
+ conn, err := db.driver.Open(db.dsn)
+ if err == nil {
+ db.mu.Lock()
+ db.outConn[conn] = true
+ db.mu.Unlock()
+ }
+ return conn, err
}
+// connIfFree returns (wanted, true) if wanted is still a valid conn and
+// isn't in use.
+//
+// If wanted is valid but in use, connIfFree returns (wanted, false).
+// If wanted is invalid, connIfFre returns (nil, false).
func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) {
db.mu.Lock()
defer db.mu.Unlock()
+ if db.outConn[wanted] {
+ return conn, false
+ }
for i, conn := range db.freeConn {
if conn != wanted {
continue
}
db.freeConn[i] = db.freeConn[len(db.freeConn)-1]
db.freeConn = db.freeConn[:len(db.freeConn)-1]
+ db.outConn[wanted] = true
return wanted, true
}
return nil, false
@@ -265,14 +348,52 @@ func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) {
// putConnHook is a hook for testing.
var putConnHook func(*DB, driver.Conn)
+// noteUnusedDriverStatement notes that si is no longer used and should
+// be closed whenever possible (when c is next not in use), unless c is
+// already closed.
+func (db *DB) noteUnusedDriverStatement(c driver.Conn, si driver.Stmt) {
+ db.mu.Lock()
+ defer db.mu.Unlock()
+ if db.outConn[c] {
+ db.onConnPut[c] = append(db.onConnPut[c], func() {
+ si.Close()
+ })
+ } else {
+ si.Close()
+ }
+}
+
+// debugGetPut determines whether getConn & putConn calls' stack traces
+// are returned for more verbose crashes.
+const debugGetPut = false
+
// putConn adds a connection to the db's free pool.
-// err is optionally the last error that occured on this connection.
+// err is optionally the last error that occurred on this connection.
func (db *DB) putConn(c driver.Conn, err error) {
+ db.mu.Lock()
+ if !db.outConn[c] {
+ if debugGetPut {
+ fmt.Printf("putConn(%v) DUPLICATE was: %s\n\nPREVIOUS was: %s", c, stack(), db.lastPut[c])
+ }
+ panic("sql: connection returned that was never out")
+ }
+ if debugGetPut {
+ db.lastPut[c] = stack()
+ }
+ delete(db.outConn, c)
+
+ if fns, ok := db.onConnPut[c]; ok {
+ for _, fn := range fns {
+ fn()
+ }
+ delete(db.onConnPut, c)
+ }
+
if err == driver.ErrBadConn {
// Don't reuse bad connections.
+ db.mu.Unlock()
return
}
- db.mu.Lock()
if putConnHook != nil {
putConnHook(db, c)
}
@@ -287,7 +408,9 @@ func (db *DB) putConn(c driver.Conn, err error) {
c.Close()
}
-// Prepare creates a prepared statement for later execution.
+// Prepare creates a prepared statement for later queries or executions.
+// Multiple queries or executions may be run concurrently from the
+// returned statement.
func (db *DB) Prepare(query string) (*Stmt, error) {
var stmt *Stmt
var err error
@@ -300,7 +423,7 @@ func (db *DB) Prepare(query string) (*Stmt, error) {
return stmt, err
}
-func (db *DB) prepare(query string) (stmt *Stmt, err error) {
+func (db *DB) prepare(query string) (*Stmt, error) {
// TODO: check if db.driver supports an optional
// driver.Preparer interface and call that instead, if so,
// otherwise we make a prepared statement that's bound
@@ -311,28 +434,27 @@ func (db *DB) prepare(query string) (stmt *Stmt, err error) {
if err != nil {
return nil, err
}
- defer db.putConn(ci, err)
si, err := ci.Prepare(query)
if err != nil {
+ db.putConn(ci, err)
return nil, err
}
- stmt = &Stmt{
+ stmt := &Stmt{
db: db,
query: query,
css: []connStmt{{ci, si}},
}
+ db.addDep(stmt, stmt)
return stmt, nil
}
// Exec executes a query without returning any rows.
+// The args are for any placeholder parameters in the query.
func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
- sargs, err := subsetTypeArgs(args)
- if err != nil {
- return nil, err
- }
var res Result
+ var err error
for i := 0; i < 10; i++ {
- res, err = db.exec(query, sargs)
+ res, err = db.exec(query, args)
if err != driver.ErrBadConn {
break
}
@@ -340,15 +462,21 @@ func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
return res, err
}
-func (db *DB) exec(query string, sargs []driver.Value) (res Result, err error) {
+func (db *DB) exec(query string, args []interface{}) (res Result, err error) {
ci, err := db.conn()
if err != nil {
return nil, err
}
- defer db.putConn(ci, err)
+ defer func() {
+ db.putConn(ci, err)
+ }()
if execer, ok := ci.(driver.Execer); ok {
- resi, err := execer.Exec(query, sargs)
+ dargs, err := driverArgs(nil, args)
+ if err != nil {
+ return nil, err
+ }
+ resi, err := execer.Exec(query, dargs)
if err != driver.ErrSkip {
if err != nil {
return nil, err
@@ -363,25 +491,83 @@ func (db *DB) exec(query string, sargs []driver.Value) (res Result, err error) {
}
defer sti.Close()
- resi, err := sti.Exec(sargs)
+ return resultFromStatement(sti, args...)
+}
+
+// Query executes a query that returns rows, typically a SELECT.
+// The args are for any placeholder parameters in the query.
+func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
+ var rows *Rows
+ var err error
+ for i := 0; i < 10; i++ {
+ rows, err = db.query(query, args)
+ if err != driver.ErrBadConn {
+ break
+ }
+ }
+ return rows, err
+}
+
+func (db *DB) query(query string, args []interface{}) (*Rows, error) {
+ ci, err := db.conn()
if err != nil {
return nil, err
}
- return result{resi}, nil
+
+ releaseConn := func(err error) { db.putConn(ci, err) }
+
+ return db.queryConn(ci, releaseConn, query, args)
}
-// Query executes a query that returns rows, typically a SELECT.
-func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
- stmt, err := db.Prepare(query)
+// queryConn executes a query on the given connection.
+// The connection gets released by the releaseConn function.
+func (db *DB) queryConn(ci driver.Conn, releaseConn func(error), query string, args []interface{}) (*Rows, error) {
+ if queryer, ok := ci.(driver.Queryer); ok {
+ dargs, err := driverArgs(nil, args)
+ if err != nil {
+ releaseConn(err)
+ return nil, err
+ }
+ rowsi, err := queryer.Query(query, dargs)
+ if err != driver.ErrSkip {
+ if err != nil {
+ releaseConn(err)
+ return nil, err
+ }
+ // Note: ownership of ci passes to the *Rows, to be freed
+ // with releaseConn.
+ rows := &Rows{
+ db: db,
+ ci: ci,
+ releaseConn: releaseConn,
+ rowsi: rowsi,
+ }
+ return rows, nil
+ }
+ }
+
+ sti, err := ci.Prepare(query)
if err != nil {
+ releaseConn(err)
return nil, err
}
- rows, err := stmt.Query(args...)
+
+ rowsi, err := rowsiFromStatement(sti, args...)
if err != nil {
- stmt.Close()
+ releaseConn(err)
+ sti.Close()
return nil, err
}
- rows.closeStmt = stmt
+
+ // Note: ownership of ci passes to the *Rows, to be freed
+ // with releaseConn.
+ rows := &Rows{
+ db: db,
+ ci: ci,
+ releaseConn: releaseConn,
+ rowsi: rowsi,
+ closeStmt: sti,
+ }
return rows, nil
}
@@ -415,7 +601,7 @@ func (db *DB) begin() (tx *Tx, err error) {
txi, err := ci.Begin()
if err != nil {
db.putConn(ci, err)
- return nil, fmt.Errorf("sql: failed to Begin transaction: %v", err)
+ return nil, err
}
return &Tx{
db: db,
@@ -577,13 +763,12 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
}
defer tx.releaseConn()
- sargs, err := subsetTypeArgs(args)
- if err != nil {
- return nil, err
- }
-
if execer, ok := ci.(driver.Execer); ok {
- resi, err := execer.Exec(query, sargs)
+ dargs, err := driverArgs(nil, args)
+ if err != nil {
+ return nil, err
+ }
+ resi, err := execer.Exec(query, dargs)
if err == nil {
return result{resi}, nil
}
@@ -598,29 +783,19 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
}
defer sti.Close()
- resi, err := sti.Exec(sargs)
- if err != nil {
- return nil, err
- }
- return result{resi}, nil
+ return resultFromStatement(sti, args...)
}
// Query executes a query that returns rows, typically a SELECT.
func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) {
- if tx.done {
- return nil, ErrTxDone
- }
- stmt, err := tx.Prepare(query)
- if err != nil {
- return nil, err
- }
- rows, err := stmt.Query(args...)
+ ci, err := tx.grabConn()
if err != nil {
- stmt.Close()
return nil, err
}
- rows.closeStmt = stmt
- return rows, err
+
+ releaseConn := func(err error) { tx.releaseConn() }
+
+ return tx.db.queryConn(ci, releaseConn, query, args)
}
// QueryRow executes a query that is expected to return at most one row.
@@ -644,6 +819,8 @@ type Stmt struct {
query string // that created the Stmt
stickyErr error // if non-nil, this error is returned for all operations
+ closemu sync.RWMutex // held exclusively during close, for read otherwise.
+
// If in a transaction, else both nil:
tx *Tx
txsi driver.Stmt
@@ -661,12 +838,18 @@ type Stmt struct {
// Exec executes a prepared statement with the given arguments and
// returns a Result summarizing the effect of the statement.
func (s *Stmt) Exec(args ...interface{}) (Result, error) {
+ s.closemu.RLock()
+ defer s.closemu.RUnlock()
_, releaseConn, si, err := s.connStmt()
if err != nil {
return nil, err
}
defer releaseConn(nil)
+ return resultFromStatement(si, args...)
+}
+
+func resultFromStatement(si driver.Stmt, args ...interface{}) (Result, error) {
// -1 means the driver doesn't know how to count the number of
// placeholders, so we won't sanity check input here and instead let the
// driver deal with errors.
@@ -674,51 +857,12 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
return nil, fmt.Errorf("sql: expected %d arguments, got %d", want, len(args))
}
- sargs := make([]driver.Value, len(args))
-
- // Convert args to subset types.
- if cc, ok := si.(driver.ColumnConverter); ok {
- for n, arg := range args {
- // First, see if the value itself knows how to convert
- // itself to a driver type. For example, a NullString
- // struct changing into a string or nil.
- if svi, ok := arg.(driver.Valuer); ok {
- sv, err := svi.Value()
- if err != nil {
- return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err)
- }
- if !driver.IsValue(sv) {
- return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv)
- }
- arg = sv
- }
-
- // Second, ask the column to sanity check itself. For
- // example, drivers might use this to make sure that
- // an int64 values being inserted into a 16-bit
- // integer field is in range (before getting
- // truncated), or that a nil can't go into a NOT NULL
- // column before going across the network to get the
- // same error.
- sargs[n], err = cc.ColumnConverter(n).ConvertValue(arg)
- if err != nil {
- return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
- }
- if !driver.IsValue(sargs[n]) {
- return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T",
- arg, sargs[n])
- }
- }
- } else {
- for n, arg := range args {
- sargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
- if err != nil {
- return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
- }
- }
+ dargs, err := driverArgs(si, args)
+ if err != nil {
+ return nil, err
}
- resi, err := si.Exec(sargs)
+ resi, err := si.Exec(dargs)
if err != nil {
return nil, err
}
@@ -794,35 +938,54 @@ func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(error), si driver.St
// Query executes a prepared query statement with the given arguments
// and returns the query results as a *Rows.
func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
+ s.closemu.RLock()
+ defer s.closemu.RUnlock()
+
ci, releaseConn, si, err := s.connStmt()
if err != nil {
return nil, err
}
+ rowsi, err := rowsiFromStatement(si, args...)
+ if err != nil {
+ releaseConn(err)
+ return nil, err
+ }
+
+ // Note: ownership of ci passes to the *Rows, to be freed
+ // with releaseConn.
+ rows := &Rows{
+ db: s.db,
+ ci: ci,
+ rowsi: rowsi,
+ // releaseConn set below
+ }
+ s.db.addDep(s, rows)
+ rows.releaseConn = func(err error) {
+ releaseConn(err)
+ s.db.removeDep(s, rows)
+ }
+ return rows, nil
+}
+
+func rowsiFromStatement(si driver.Stmt, args ...interface{}) (driver.Rows, error) {
// -1 means the driver doesn't know how to count the number of
// placeholders, so we won't sanity check input here and instead let the
// driver deal with errors.
if want := si.NumInput(); want != -1 && len(args) != want {
return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", si.NumInput(), len(args))
}
- sargs, err := subsetTypeArgs(args)
+
+ dargs, err := driverArgs(si, args)
if err != nil {
return nil, err
}
- rowsi, err := si.Query(sargs)
+
+ rowsi, err := si.Query(dargs)
if err != nil {
- releaseConn(err)
return nil, err
}
- // Note: ownership of ci passes to the *Rows, to be freed
- // with releaseConn.
- rows := &Rows{
- db: s.db,
- ci: ci,
- releaseConn: releaseConn,
- rowsi: rowsi,
- }
- return rows, nil
+ return rowsi, nil
}
// QueryRow executes a prepared query statement with the given arguments.
@@ -846,6 +1009,9 @@ func (s *Stmt) QueryRow(args ...interface{}) *Row {
// Close closes the statement.
func (s *Stmt) Close() error {
+ s.closemu.Lock()
+ defer s.closemu.Unlock()
+
if s.stickyErr != nil {
return s.stickyErr
}
@@ -858,18 +1024,17 @@ func (s *Stmt) Close() error {
if s.tx != nil {
s.txsi.Close()
- } else {
- for _, v := range s.css {
- if ci, match := s.db.connIfFree(v.ci); match {
- v.si.Close()
- s.db.putConn(ci, nil)
- } else {
- // TODO(bradfitz): care that we can't close
- // this statement because the statement's
- // connection is in use?
- }
- }
+ return nil
+ }
+
+ return s.db.removeDep(s, s)
+}
+
+func (s *Stmt) finalClose() error {
+ for _, v := range s.css {
+ s.db.noteUnusedDriverStatement(v.ci, v.si)
}
+ s.css = nil
return nil
}
@@ -888,14 +1053,14 @@ func (s *Stmt) Close() error {
// ...
type Rows struct {
db *DB
- ci driver.Conn // owned; must call putconn when closed to release
+ ci driver.Conn // owned; must call releaseConn when closed to release
releaseConn func(error)
rowsi driver.Rows
closed bool
lastcols []driver.Value
lasterr error
- closeStmt *Stmt // if non-nil, statement to Close on close
+ closeStmt driver.Stmt // if non-nil, statement to Close on close
}
// Next prepares the next result row for reading with the Scan method.
@@ -1064,3 +1229,8 @@ type Result interface {
type result struct {
driver.Result
}
+
+func stack() string {
+ var buf [1024]byte
+ return string(buf[:runtime.Stack(buf[:], false)])
+}
diff --git a/src/pkg/database/sql/sql_test.go b/src/pkg/database/sql/sql_test.go
index b29670586..53b229600 100644
--- a/src/pkg/database/sql/sql_test.go
+++ b/src/pkg/database/sql/sql_test.go
@@ -8,7 +8,6 @@ import (
"database/sql/driver"
"fmt"
"reflect"
- "runtime"
"strings"
"testing"
"time"
@@ -63,6 +62,10 @@ func exec(t *testing.T, db *DB, query string, args ...interface{}) {
}
func closeDB(t *testing.T, db *DB) {
+ if e := recover(); e != nil {
+ fmt.Printf("Panic: %v\n", e)
+ panic(e)
+ }
err := db.Close()
if err != nil {
t.Fatalf("error closing DB: %v", err)
@@ -270,6 +273,35 @@ func TestStatementQueryRow(t *testing.T) {
}
+// golang.org/issue/3734
+func TestStatementQueryRowConcurrent(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+ stmt, err := db.Prepare("SELECT|people|age|name=?")
+ if err != nil {
+ t.Fatalf("Prepare: %v", err)
+ }
+ defer stmt.Close()
+
+ const n = 10
+ ch := make(chan error, n)
+ for i := 0; i < n; i++ {
+ go func() {
+ var age int
+ err := stmt.QueryRow("Alice").Scan(&age)
+ if err == nil && age != 1 {
+ err = fmt.Errorf("unexpected age %d", age)
+ }
+ ch <- err
+ }()
+ }
+ for i := 0; i < n; i++ {
+ if err := <-ch; err != nil {
+ t.Error(err)
+ }
+ }
+}
+
// just a test of fakedb itself
func TestBogusPreboundParameters(t *testing.T) {
db := newTestDB(t, "foo")
@@ -306,8 +338,8 @@ func TestExec(t *testing.T) {
{[]interface{}{7, 9}, ""},
// Invalid conversions:
- {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting Exec argument #1's type: sql/driver: value 4294967295 overflows int32"},
- {[]interface{}{"Brad", "strconv fail"}, "sql: converting Exec argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"},
+ {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting argument #1's type: sql/driver: value 4294967295 overflows int32"},
+ {[]interface{}{"Brad", "strconv fail"}, "sql: converting argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"},
// Wrong number of args:
{[]interface{}{}, "sql: expected 2 arguments, got 0"},
@@ -402,6 +434,39 @@ func TestTxQueryInvalid(t *testing.T) {
}
}
+// Tests fix for issue 4433, that retries in Begin happen when
+// conn.Begin() returns ErrBadConn
+func TestTxErrBadConn(t *testing.T) {
+ db, err := Open("test", fakeDBName+";badConn")
+ if err != nil {
+ t.Fatalf("Open: %v", err)
+ }
+ if _, err := db.Exec("WIPE"); err != nil {
+ t.Fatalf("exec wipe: %v", err)
+ }
+ defer closeDB(t, db)
+ exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
+ stmt, err := db.Prepare("INSERT|t1|name=?,age=?")
+ if err != nil {
+ t.Fatalf("Stmt, err = %v, %v", stmt, err)
+ }
+ defer stmt.Close()
+ tx, err := db.Begin()
+ if err != nil {
+ t.Fatalf("Begin = %v", err)
+ }
+ txs := tx.Stmt(stmt)
+ defer txs.Close()
+ _, err = txs.Exec("Bobby", 7)
+ if err != nil {
+ t.Fatalf("Exec = %v", err)
+ }
+ err = tx.Commit()
+ if err != nil {
+ t.Fatalf("Commit = %v", err)
+ }
+}
+
// Tests fix for issue 2542, that we release a lock when querying on
// a closed connection.
func TestIssue2542Deadlock(t *testing.T) {
@@ -415,6 +480,30 @@ func TestIssue2542Deadlock(t *testing.T) {
}
}
+// From golang.org/issue/3865
+func TestCloseStmtBeforeRows(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+
+ s, err := db.Prepare("SELECT|people|name|")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ r, err := s.Query()
+ if err != nil {
+ s.Close()
+ t.Fatal(err)
+ }
+
+ err = s.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ r.Close()
+}
+
// Tests fix for issue 2788, that we bind nil to a []byte if the
// value in the column is sql null
func TestNullByteSlice(t *testing.T) {
@@ -608,7 +697,14 @@ func nullTestRun(t *testing.T, spec nullTestSpec) {
}
}
-func stack() string {
- buf := make([]byte, 1024)
- return string(buf[:runtime.Stack(buf, false)])
+// golang.org/issue/4859
+func TestQueryRowNilScanDest(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+ var name *string // nil pointer
+ err := db.QueryRow("SELECT|people|name|").Scan(name)
+ want := "sql: Scan error on column index 0: destination pointer is nil"
+ if err == nil || err.Error() != want {
+ t.Errorf("error = %q; want %q", err.Error(), want)
+ }
}
diff --git a/src/pkg/debug/dwarf/const.go b/src/pkg/debug/dwarf/const.go
index 918b153d0..ad696dc32 100644
--- a/src/pkg/debug/dwarf/const.go
+++ b/src/pkg/debug/dwarf/const.go
@@ -207,6 +207,7 @@ const (
formRef8 format = 0x14
formRefUdata format = 0x15
formIndirect format = 0x16
+ formFlagPresent format = 0x19
)
// A Tag is the classification (the type) of an Entry.
diff --git a/src/pkg/debug/dwarf/entry.go b/src/pkg/debug/dwarf/entry.go
index 2885d8fa2..f376e4088 100644
--- a/src/pkg/debug/dwarf/entry.go
+++ b/src/pkg/debug/dwarf/entry.go
@@ -185,6 +185,10 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
// flag
case formFlag:
val = b.uint8() == 1
+ case formFlagPresent:
+ // The attribute is implicitly indicated as present, and no value is
+ // encoded in the debugging information entry itself.
+ val = true
// reference to other entry
case formRefAddr:
diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go
index 184ca8375..acb9817af 100644
--- a/src/pkg/debug/elf/file.go
+++ b/src/pkg/debug/elf/file.go
@@ -31,6 +31,7 @@ type FileHeader struct {
ByteOrder binary.ByteOrder
Type Type
Machine Machine
+ Entry uint64
}
// A File represents an open ELF file.
@@ -240,6 +241,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
}
f.Type = Type(hdr.Type)
f.Machine = Machine(hdr.Machine)
+ f.Entry = uint64(hdr.Entry)
if v := Version(hdr.Version); v != f.Version {
return nil, &FormatError{0, "mismatched ELF version", v}
}
@@ -258,6 +260,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
}
f.Type = Type(hdr.Type)
f.Machine = Machine(hdr.Machine)
+ f.Entry = uint64(hdr.Entry)
if v := Version(hdr.Version); v != f.Version {
return nil, &FormatError{0, "mismatched ELF version", v}
}
@@ -269,7 +272,8 @@ func NewFile(r io.ReaderAt) (*File, error) {
shnum = int(hdr.Shnum)
shstrndx = int(hdr.Shstrndx)
}
- if shstrndx < 0 || shstrndx >= shnum {
+
+ if shnum > 0 && shoff > 0 && (shstrndx < 0 || shstrndx >= shnum) {
return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx}
}
@@ -364,6 +368,10 @@ func NewFile(r io.ReaderAt) (*File, error) {
f.Sections[i] = s
}
+ if len(f.Sections) == 0 {
+ return f, nil
+ }
+
// Load section header string table.
shstrtab, err := f.Sections[shstrndx].Data()
if err != nil {
@@ -414,10 +422,6 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) {
return nil, nil, errors.New("cannot load string table section")
}
- // The first entry is all zeros.
- var skip [Sym32Size]byte
- symtab.Read(skip[0:])
-
symbols := make([]Symbol, symtab.Len()/Sym32Size)
i := 0
@@ -457,10 +461,6 @@ func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) {
return nil, nil, errors.New("cannot load string table section")
}
- // The first entry is all zeros.
- var skip [Sym64Size]byte
- symtab.Read(skip[0:])
-
symbols := make([]Symbol, symtab.Len()/Sym64Size)
i := 0
@@ -705,8 +705,8 @@ func (f *File) gnuVersionInit(str []byte) {
// gnuVersion adds Library and Version information to sym,
// which came from offset i of the symbol table.
func (f *File) gnuVersion(i int, sym *ImportedSymbol) {
- // Each entry is two bytes; skip undef entry at beginning.
- i = (i + 1) * 2
+ // Each entry is two bytes.
+ i = i * 2
if i >= len(f.gnuVersym) {
return
}
@@ -723,6 +723,20 @@ func (f *File) gnuVersion(i int, sym *ImportedSymbol) {
// referred to by the binary f that are expected to be
// linked with the binary at dynamic link time.
func (f *File) ImportedLibraries() ([]string, error) {
+ return f.DynString(DT_NEEDED)
+}
+
+// DynString returns the strings listed for the given tag in the file's dynamic
+// section.
+//
+// The tag must be one that takes string values: DT_NEEDED, DT_SONAME, DT_RPATH, or
+// DT_RUNPATH.
+func (f *File) DynString(tag DynTag) ([]string, error) {
+ switch tag {
+ case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH:
+ default:
+ return nil, fmt.Errorf("non-string-valued tag %v", tag)
+ }
ds := f.SectionByType(SHT_DYNAMIC)
if ds == nil {
// not dynamic, so no libraries
@@ -738,25 +752,24 @@ func (f *File) ImportedLibraries() ([]string, error) {
}
var all []string
for len(d) > 0 {
- var tag DynTag
- var value uint64
+ var t DynTag
+ var v uint64
switch f.Class {
case ELFCLASS32:
- tag = DynTag(f.ByteOrder.Uint32(d[0:4]))
- value = uint64(f.ByteOrder.Uint32(d[4:8]))
+ t = DynTag(f.ByteOrder.Uint32(d[0:4]))
+ v = uint64(f.ByteOrder.Uint32(d[4:8]))
d = d[8:]
case ELFCLASS64:
- tag = DynTag(f.ByteOrder.Uint64(d[0:8]))
- value = f.ByteOrder.Uint64(d[8:16])
+ t = DynTag(f.ByteOrder.Uint64(d[0:8]))
+ v = f.ByteOrder.Uint64(d[8:16])
d = d[16:]
}
- if tag == DT_NEEDED {
- s, ok := getString(str, int(value))
+ if t == tag {
+ s, ok := getString(str, int(v))
if ok {
all = append(all, s)
}
}
}
-
return all, nil
}
diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go
index 98f2723c8..f9aa7265a 100644
--- a/src/pkg/debug/elf/file_test.go
+++ b/src/pkg/debug/elf/file_test.go
@@ -5,10 +5,14 @@
package elf
import (
+ "bytes"
+ "compress/gzip"
"debug/dwarf"
"encoding/binary"
+ "io"
"net"
"os"
+ "path"
"reflect"
"runtime"
"testing"
@@ -19,12 +23,13 @@ type fileTest struct {
hdr FileHeader
sections []SectionHeader
progs []ProgHeader
+ needed []string
}
var fileTests = []fileTest{
{
"testdata/gcc-386-freebsd-exec",
- FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386},
+ FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386, 0x80483cc},
[]SectionHeader{
{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
{".interp", SHT_PROGBITS, SHF_ALLOC, 0x80480d4, 0xd4, 0x15, 0x0, 0x0, 0x1, 0x0},
@@ -64,10 +69,11 @@ var fileTests = []fileTest{
{PT_LOAD, PF_R + PF_W, 0x5fc, 0x80495fc, 0x80495fc, 0xd8, 0xf8, 0x1000},
{PT_DYNAMIC, PF_R + PF_W, 0x60c, 0x804960c, 0x804960c, 0x98, 0x98, 0x4},
},
+ []string{"libc.so.6"},
},
{
"testdata/gcc-amd64-linux-exec",
- FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64},
+ FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64, 0x4003e0},
[]SectionHeader{
{"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
{".interp", SHT_PROGBITS, SHF_ALLOC, 0x400200, 0x200, 0x1c, 0x0, 0x0, 0x1, 0x0},
@@ -117,6 +123,32 @@ var fileTests = []fileTest{
{PT_LOOS + 0x474E550, PF_R, 0x5b8, 0x4005b8, 0x4005b8, 0x24, 0x24, 0x4},
{PT_LOOS + 0x474E551, PF_R + PF_W, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8},
},
+ []string{"libc.so.6"},
+ },
+ {
+ "testdata/hello-world-core.gz",
+ FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0x0, binary.LittleEndian, ET_CORE, EM_X86_64, 0x0},
+ []SectionHeader{},
+ []ProgHeader{
+ {Type: PT_NOTE, Flags: 0x0, Off: 0x3f8, Vaddr: 0x0, Paddr: 0x0, Filesz: 0x8ac, Memsz: 0x0, Align: 0x0},
+ {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x1000, Vaddr: 0x400000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_R, Off: 0x1000, Vaddr: 0x401000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x2000, Vaddr: 0x402000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3000, Vaddr: 0x7f54078b8000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1b5000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: 0x0, Off: 0x3000, Vaddr: 0x7f5407a6d000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1ff000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_R, Off: 0x3000, Vaddr: 0x7f5407c6c000, Paddr: 0x0, Filesz: 0x4000, Memsz: 0x4000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x7000, Vaddr: 0x7f5407c70000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x9000, Vaddr: 0x7f5407c72000, Paddr: 0x0, Filesz: 0x5000, Memsz: 0x5000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0xe000, Vaddr: 0x7f5407c77000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x22000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0xe000, Vaddr: 0x7f5407e81000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x11000, Vaddr: 0x7f5407e96000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_R, Off: 0x14000, Vaddr: 0x7f5407e99000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x15000, Vaddr: 0x7f5407e9a000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x17000, Vaddr: 0x7fff79972000, Paddr: 0x0, Filesz: 0x23000, Memsz: 0x23000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3a000, Vaddr: 0x7fff799f8000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
+ {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3b000, Vaddr: 0xffffffffff600000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000},
+ },
+ nil,
},
}
@@ -124,9 +156,18 @@ func TestOpen(t *testing.T) {
for i := range fileTests {
tt := &fileTests[i]
- f, err := Open(tt.file)
+ var f *File
+ var err error
+ if path.Ext(tt.file) == ".gz" {
+ var r io.ReaderAt
+ if r, err = decompress(tt.file); err == nil {
+ f, err = NewFile(r)
+ }
+ } else {
+ f, err = Open(tt.file)
+ }
if err != nil {
- t.Error(err)
+ t.Errorf("cannot open file %s: %v", tt.file, err)
continue
}
if !reflect.DeepEqual(f.FileHeader, tt.hdr) {
@@ -161,26 +202,69 @@ func TestOpen(t *testing.T) {
if tn != fn {
t.Errorf("open %s: len(Progs) = %d, want %d", tt.file, fn, tn)
}
+ tl := tt.needed
+ fl, err := f.ImportedLibraries()
+ if err != nil {
+ t.Error(err)
+ }
+ if !reflect.DeepEqual(tl, fl) {
+ t.Errorf("open %s: DT_NEEDED = %v, want %v", tt.file, tl, fl)
+ }
}
}
+// elf.NewFile requires io.ReaderAt, which compress/gzip cannot
+// provide. Decompress the file to a bytes.Reader.
+func decompress(gz string) (io.ReaderAt, error) {
+ in, err := os.Open(gz)
+ if err != nil {
+ return nil, err
+ }
+ defer in.Close()
+ r, err := gzip.NewReader(in)
+ if err != nil {
+ return nil, err
+ }
+ var out bytes.Buffer
+ _, err = io.Copy(&out, r)
+ return bytes.NewReader(out.Bytes()), err
+}
+
+type relocationTestEntry struct {
+ entryNumber int
+ entry *dwarf.Entry
+}
+
type relocationTest struct {
- file string
- firstEntry *dwarf.Entry
+ file string
+ entries []relocationTestEntry
}
var relocationTests = []relocationTest{
{
"testdata/go-relocation-test-gcc441-x86-64.obj",
- &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}},
+ []relocationTestEntry{
+ {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
+ },
},
{
"testdata/go-relocation-test-gcc441-x86.obj",
- &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}},
+ []relocationTestEntry{
+ {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
+ },
},
{
"testdata/go-relocation-test-gcc424-x86-64.obj",
- &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}},
+ []relocationTestEntry{
+ {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
+ },
+ },
+ {
+ "testdata/gcc-amd64-openbsd-debug-with-rela.obj",
+ []relocationTestEntry{
+ {203, &dwarf.Entry{Offset: 0xc62, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_interval"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(236)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x0}}}}},
+ {204, &dwarf.Entry{Offset: 0xc70, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_value"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(237)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x10}}}}},
+ },
},
}
@@ -196,20 +280,24 @@ func TestDWARFRelocations(t *testing.T) {
t.Error(err)
continue
}
- reader := dwarf.Reader()
- // Checking only the first entry is sufficient since it has
- // many different strings. If the relocation had failed, all
- // the string offsets would be zero and all the strings would
- // end up being the same.
- firstEntry, err := reader.Next()
- if err != nil {
- t.Error(err)
- continue
- }
-
- if !reflect.DeepEqual(test.firstEntry, firstEntry) {
- t.Errorf("#%d: mismatch: got:%#v want:%#v", i, firstEntry, test.firstEntry)
- continue
+ for _, testEntry := range test.entries {
+ reader := dwarf.Reader()
+ for j := 0; j < testEntry.entryNumber; j++ {
+ entry, err := reader.Next()
+ if entry == nil || err != nil {
+ t.Errorf("Failed to skip to entry %d: %v", testEntry.entryNumber, err)
+ continue
+ }
+ }
+ entry, err := reader.Next()
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ if !reflect.DeepEqual(testEntry.entry, entry) {
+ t.Errorf("#%d/%d: mismatch: got:%#v want:%#v", i, testEntry.entryNumber, entry, testEntry.entry)
+ continue
+ }
}
}
}
diff --git a/src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj b/src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj
new file mode 100644
index 000000000..f62b1ea1c
--- /dev/null
+++ b/src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj
Binary files differ
diff --git a/src/pkg/debug/elf/testdata/hello-world-core.gz b/src/pkg/debug/elf/testdata/hello-world-core.gz
new file mode 100644
index 000000000..806af6edb
--- /dev/null
+++ b/src/pkg/debug/elf/testdata/hello-world-core.gz
Binary files differ
diff --git a/src/pkg/debug/gosym/pclntab_test.go b/src/pkg/debug/gosym/pclntab_test.go
index ade704335..20acba612 100644
--- a/src/pkg/debug/gosym/pclntab_test.go
+++ b/src/pkg/debug/gosym/pclntab_test.go
@@ -52,6 +52,14 @@ func dotest() bool {
return true
}
+func endtest() {
+ if pclineTempDir != "" {
+ os.RemoveAll(pclineTempDir)
+ pclineTempDir = ""
+ pclinetestBinary = ""
+ }
+}
+
func getTable(t *testing.T) *Table {
f, tab := crack(os.Args[0], t)
f.Close()
@@ -95,6 +103,7 @@ func TestLineFromAline(t *testing.T) {
if !dotest() {
return
}
+ defer endtest()
tab := getTable(t)
@@ -129,7 +138,7 @@ func TestLineFromAline(t *testing.T) {
if !ok {
t.Errorf("file %s starts on line %d", path, line)
} else if line != ll+1 {
- t.Errorf("expected next line of file %s to be %d, got %d", path, ll+1, line)
+ t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line)
}
lastline[path] = line
}
@@ -142,6 +151,7 @@ func TestLineAline(t *testing.T) {
if !dotest() {
return
}
+ defer endtest()
tab := getTable(t)
@@ -183,7 +193,7 @@ func TestPCLine(t *testing.T) {
if !dotest() {
return
}
- defer os.RemoveAll(pclineTempDir)
+ defer endtest()
f, tab := crack(pclinetestBinary, t)
text := f.Section(".text")
diff --git a/src/pkg/debug/gosym/symtab.go b/src/pkg/debug/gosym/symtab.go
index 52d7d55a3..81ed4fb27 100644
--- a/src/pkg/debug/gosym/symtab.go
+++ b/src/pkg/debug/gosym/symtab.go
@@ -13,6 +13,7 @@ package gosym
// and the Go format is the runtime source, specifically ../../runtime/symtab.c.
import (
+ "bytes"
"encoding/binary"
"fmt"
"strconv"
@@ -98,24 +99,116 @@ type Table struct {
}
type sym struct {
- value uint32
- gotype uint32
+ value uint64
+ gotype uint64
typ byte
name []byte
}
+var littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}
+var bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00}
+
+var oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00}
+
func walksymtab(data []byte, fn func(sym) error) error {
+ var order binary.ByteOrder = binary.BigEndian
+ newTable := false
+ switch {
+ case bytes.HasPrefix(data, oldLittleEndianSymtab):
+ // Same as Go 1.0, but little endian.
+ // Format was used during interim development between Go 1.0 and Go 1.1.
+ // Should not be widespread, but easy to support.
+ data = data[6:]
+ order = binary.LittleEndian
+ case bytes.HasPrefix(data, bigEndianSymtab):
+ newTable = true
+ case bytes.HasPrefix(data, littleEndianSymtab):
+ newTable = true
+ order = binary.LittleEndian
+ }
+ var ptrsz int
+ if newTable {
+ if len(data) < 8 {
+ return &DecodingError{len(data), "unexpected EOF", nil}
+ }
+ ptrsz = int(data[7])
+ if ptrsz != 4 && ptrsz != 8 {
+ return &DecodingError{7, "invalid pointer size", ptrsz}
+ }
+ data = data[8:]
+ }
var s sym
p := data
- for len(p) >= 6 {
- s.value = binary.BigEndian.Uint32(p[0:4])
- typ := p[4]
- if typ&0x80 == 0 {
- return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ}
+ for len(p) >= 4 {
+ var typ byte
+ if newTable {
+ // Symbol type, value, Go type.
+ typ = p[0] & 0x3F
+ wideValue := p[0]&0x40 != 0
+ goType := p[0]&0x80 != 0
+ if typ < 26 {
+ typ += 'A'
+ } else {
+ typ += 'a' - 26
+ }
+ s.typ = typ
+ p = p[1:]
+ if wideValue {
+ if len(p) < ptrsz {
+ return &DecodingError{len(data), "unexpected EOF", nil}
+ }
+ // fixed-width value
+ if ptrsz == 8 {
+ s.value = order.Uint64(p[0:8])
+ p = p[8:]
+ } else {
+ s.value = uint64(order.Uint32(p[0:4]))
+ p = p[4:]
+ }
+ } else {
+ // varint value
+ s.value = 0
+ shift := uint(0)
+ for len(p) > 0 && p[0]&0x80 != 0 {
+ s.value |= uint64(p[0]&0x7F) << shift
+ shift += 7
+ p = p[1:]
+ }
+ if len(p) == 0 {
+ return &DecodingError{len(data), "unexpected EOF", nil}
+ }
+ s.value |= uint64(p[0]) << shift
+ p = p[1:]
+ }
+ if goType {
+ if len(p) < ptrsz {
+ return &DecodingError{len(data), "unexpected EOF", nil}
+ }
+ // fixed-width go type
+ if ptrsz == 8 {
+ s.gotype = order.Uint64(p[0:8])
+ p = p[8:]
+ } else {
+ s.gotype = uint64(order.Uint32(p[0:4]))
+ p = p[4:]
+ }
+ }
+ } else {
+ // Value, symbol type.
+ s.value = uint64(order.Uint32(p[0:4]))
+ if len(p) < 5 {
+ return &DecodingError{len(data), "unexpected EOF", nil}
+ }
+ typ = p[4]
+ if typ&0x80 == 0 {
+ return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ}
+ }
+ typ &^= 0x80
+ s.typ = typ
+ p = p[5:]
}
- typ &^= 0x80
- s.typ = typ
- p = p[5:]
+
+ // Name.
var i int
var nnul int
for i = 0; i < len(p); i++ {
@@ -134,13 +227,21 @@ func walksymtab(data []byte, fn func(sym) error) error {
}
}
}
- if i+nnul+4 > len(p) {
+ if len(p) < i+nnul {
return &DecodingError{len(data), "unexpected EOF", nil}
}
s.name = p[0:i]
i += nnul
- s.gotype = binary.BigEndian.Uint32(p[i : i+4])
- p = p[i+4:]
+ p = p[i:]
+
+ if !newTable {
+ if len(p) < 4 {
+ return &DecodingError{len(data), "unexpected EOF", nil}
+ }
+ // Go type.
+ s.gotype = uint64(order.Uint32(p[:4]))
+ p = p[4:]
+ }
fn(s)
}
return nil
diff --git a/src/pkg/debug/pe/file.go b/src/pkg/debug/pe/file.go
index 6b98a5f45..f521566ef 100644
--- a/src/pkg/debug/pe/file.go
+++ b/src/pkg/debug/pe/file.go
@@ -19,6 +19,7 @@ import (
type File struct {
FileHeader
Sections []*Section
+ Symbols []*Symbol
closer io.Closer
}
@@ -49,6 +50,14 @@ type Section struct {
sr *io.SectionReader
}
+type Symbol struct {
+ Name string
+ Value uint32
+ SectionNumber int16
+ Type uint16
+ StorageClass uint8
+}
+
type ImportDirectory struct {
OriginalFirstThunk uint32
TimeDateStamp uint32
@@ -122,12 +131,13 @@ func NewFile(r io.ReaderAt) (*File, error) {
}
var base int64
if dosheader[0] == 'M' && dosheader[1] == 'Z' {
+ signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
var sign [4]byte
- r.ReadAt(sign[0:], int64(dosheader[0x3c]))
+ r.ReadAt(sign[:], signoff)
if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
return nil, errors.New("Invalid PE File Format.")
}
- base = int64(dosheader[0x3c]) + 4
+ base = signoff + 4
} else {
base = int64(0)
}
@@ -138,16 +148,52 @@ func NewFile(r io.ReaderAt) (*File, error) {
if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 {
return nil, errors.New("Invalid PE File Format.")
}
- // get symbol string table
- sr.Seek(int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols), os.SEEK_SET)
- var l uint32
- if err := binary.Read(sr, binary.LittleEndian, &l); err != nil {
- return nil, err
- }
- ss := make([]byte, l)
- if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols)); err != nil {
- return nil, err
+
+ var ss []byte
+ if f.FileHeader.NumberOfSymbols > 0 {
+ // Get COFF string table, which is located at the end of the COFF symbol table.
+ sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET)
+ var l uint32
+ if err := binary.Read(sr, binary.LittleEndian, &l); err != nil {
+ return nil, err
+ }
+ ss = make([]byte, l)
+ if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil {
+ return nil, err
+ }
+
+ // Process COFF symbol table.
+ sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET)
+ aux := uint8(0)
+ for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ {
+ cs := new(COFFSymbol)
+ if err := binary.Read(sr, binary.LittleEndian, cs); err != nil {
+ return nil, err
+ }
+ if aux > 0 {
+ aux--
+ continue
+ }
+ var name string
+ if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 {
+ si := int(binary.LittleEndian.Uint32(cs.Name[4:]))
+ name, _ = getString(ss, si)
+ } else {
+ name = cstring(cs.Name[:])
+ }
+ aux = cs.NumberOfAuxSymbols
+ s := &Symbol{
+ Name: name,
+ Value: cs.Value,
+ SectionNumber: cs.SectionNumber,
+ Type: cs.Type,
+ StorageClass: cs.StorageClass,
+ }
+ f.Symbols = append(f.Symbols, s)
+ }
}
+
+ // Process sections.
sr.Seek(base, os.SEEK_SET)
binary.Read(sr, binary.LittleEndian, &f.FileHeader)
sr.Seek(int64(f.FileHeader.SizeOfOptionalHeader), os.SEEK_CUR) //Skip OptionalHeader
diff --git a/src/pkg/debug/pe/file_test.go b/src/pkg/debug/pe/file_test.go
index 2815d720b..c0f9fcb95 100644
--- a/src/pkg/debug/pe/file_test.go
+++ b/src/pkg/debug/pe/file_test.go
@@ -13,6 +13,7 @@ type fileTest struct {
file string
hdr FileHeader
sections []*SectionHeader
+ symbols []*Symbol
}
var fileTests = []fileTest{
@@ -33,6 +34,24 @@ var fileTests = []fileTest{
{".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832},
{".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832},
},
+ []*Symbol{
+ {".file", 0x0, -2, 0x0, 0x67},
+ {"_main", 0x0, 1, 0x20, 0x2},
+ {".text", 0x0, 1, 0x0, 0x3},
+ {".data", 0x0, 2, 0x0, 0x3},
+ {".bss", 0x0, 3, 0x0, 0x3},
+ {".debug_abbrev", 0x0, 4, 0x0, 0x3},
+ {".debug_info", 0x0, 5, 0x0, 0x3},
+ {".debug_line", 0x0, 6, 0x0, 0x3},
+ {".rdata", 0x0, 7, 0x0, 0x3},
+ {".debug_frame", 0x0, 8, 0x0, 0x3},
+ {".debug_loc", 0x0, 9, 0x0, 0x3},
+ {".debug_pubnames", 0x0, 10, 0x0, 0x3},
+ {".debug_pubtypes", 0x0, 11, 0x0, 0x3},
+ {".debug_aranges", 0x0, 12, 0x0, 0x3},
+ {"___main", 0x0, 0, 0x20, 0x2},
+ {"_puts", 0x0, 0, 0x20, 0x2},
+ },
},
{
"testdata/gcc-386-mingw-exec",
@@ -54,6 +73,7 @@ var fileTests = []fileTest{
{Name: ".debug_frame", VirtualSize: 0x34, VirtualAddress: 0xe000, Size: 0x200, Offset: 0x3800, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42300000},
{Name: ".debug_loc", VirtualSize: 0x38, VirtualAddress: 0xf000, Size: 0x200, Offset: 0x3a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
},
+ []*Symbol{},
},
}
@@ -86,7 +106,15 @@ func TestOpen(t *testing.T) {
if tn != fn {
t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn)
}
-
+ for i, have := range f.Symbols {
+ if i >= len(tt.symbols) {
+ break
+ }
+ want := tt.symbols[i]
+ if !reflect.DeepEqual(have, want) {
+ t.Errorf("open %s, symbol %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
+ }
+ }
}
}
diff --git a/src/pkg/debug/pe/pe.go b/src/pkg/debug/pe/pe.go
index b3dab739a..0606217b3 100644
--- a/src/pkg/debug/pe/pe.go
+++ b/src/pkg/debug/pe/pe.go
@@ -27,6 +27,17 @@ type SectionHeader32 struct {
Characteristics uint32
}
+const COFFSymbolSize = 18
+
+type COFFSymbol struct {
+ Name [8]uint8
+ Value uint32
+ SectionNumber int16
+ Type uint16
+ StorageClass uint8
+ NumberOfAuxSymbols uint8
+}
+
const (
IMAGE_FILE_MACHINE_UNKNOWN = 0x0
IMAGE_FILE_MACHINE_AM33 = 0x1d3
diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go
index ac2b5f8da..cac9d64b5 100644
--- a/src/pkg/encoding/asn1/asn1.go
+++ b/src/pkg/encoding/asn1/asn1.go
@@ -77,15 +77,15 @@ func parseInt64(bytes []byte) (ret int64, err error) {
// parseInt treats the given bytes as a big-endian, signed integer and returns
// the result.
-func parseInt(bytes []byte) (int, error) {
+func parseInt32(bytes []byte) (int32, error) {
ret64, err := parseInt64(bytes)
if err != nil {
return 0, err
}
- if ret64 != int64(int(ret64)) {
+ if ret64 != int64(int32(ret64)) {
return 0, StructuralError{"integer too large"}
}
- return int(ret64), nil
+ return int32(ret64), nil
}
var bigOne = big.NewInt(1)
@@ -670,7 +670,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
err = err1
return
case enumeratedType:
- parsedInt, err1 := parseInt(innerBytes)
+ parsedInt, err1 := parseInt32(innerBytes)
if err1 == nil {
v.SetInt(int64(parsedInt))
}
@@ -692,19 +692,20 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
}
err = err1
return
- case reflect.Int, reflect.Int32:
- parsedInt, err1 := parseInt(innerBytes)
- if err1 == nil {
- val.SetInt(int64(parsedInt))
- }
- err = err1
- return
- case reflect.Int64:
- parsedInt, err1 := parseInt64(innerBytes)
- if err1 == nil {
- val.SetInt(parsedInt)
+ case reflect.Int, reflect.Int32, reflect.Int64:
+ if val.Type().Size() == 4 {
+ parsedInt, err1 := parseInt32(innerBytes)
+ if err1 == nil {
+ val.SetInt(int64(parsedInt))
+ }
+ err = err1
+ } else {
+ parsedInt, err1 := parseInt64(innerBytes)
+ if err1 == nil {
+ val.SetInt(parsedInt)
+ }
+ err = err1
}
- err = err1
return
// TODO(dfc) Add support for the remaining integer types
case reflect.Struct:
diff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go
index eb848bdb4..6e98dcf0b 100644
--- a/src/pkg/encoding/asn1/asn1_test.go
+++ b/src/pkg/encoding/asn1/asn1_test.go
@@ -64,7 +64,7 @@ var int32TestData = []int32Test{
func TestParseInt32(t *testing.T) {
for i, test := range int32TestData {
- ret, err := parseInt(test.in)
+ ret, err := parseInt32(test.in)
if (err == nil) != test.ok {
t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
}
@@ -124,7 +124,7 @@ func TestBitString(t *testing.T) {
t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
}
if err == nil {
- if test.bitLength != ret.BitLength || bytes.Compare(ret.Bytes, test.out) != 0 {
+ if test.bitLength != ret.BitLength || !bytes.Equal(ret.Bytes, test.out) {
t.Errorf("#%d: Bad result: %v (expected %v %v)", i, ret, test.out, test.bitLength)
}
}
@@ -166,7 +166,7 @@ func TestBitStringRightAlign(t *testing.T) {
for i, test := range bitStringRightAlignTests {
bs := BitString{test.in, test.inlen}
out := bs.RightAlign()
- if bytes.Compare(out, test.out) != 0 {
+ if !bytes.Equal(out, test.out) {
t.Errorf("#%d got: %x want: %x", i, out, test.out)
}
}
@@ -477,7 +477,7 @@ func TestRawStructs(t *testing.T) {
if s.A != 0x50 {
t.Errorf("bad value for A: got %d want %d", s.A, 0x50)
}
- if bytes.Compare([]byte(s.Raw), input) != 0 {
+ if !bytes.Equal([]byte(s.Raw), input) {
t.Errorf("bad value for Raw: got %x want %x", s.Raw, input)
}
}
diff --git a/src/pkg/encoding/asn1/common.go b/src/pkg/encoding/asn1/common.go
index 03856bc55..33a117ece 100644
--- a/src/pkg/encoding/asn1/common.go
+++ b/src/pkg/encoding/asn1/common.go
@@ -98,6 +98,8 @@ func parseFieldParameters(str string) (ret fieldParameters) {
ret.stringType = tagIA5String
case part == "printable":
ret.stringType = tagPrintableString
+ case part == "utf8":
+ ret.stringType = tagUTF8String
case strings.HasPrefix(part, "default:"):
i, err := strconv.ParseInt(part[8:], 10, 64)
if err == nil {
diff --git a/src/pkg/encoding/asn1/marshal.go b/src/pkg/encoding/asn1/marshal.go
index 163bca575..0c216fdb3 100644
--- a/src/pkg/encoding/asn1/marshal.go
+++ b/src/pkg/encoding/asn1/marshal.go
@@ -6,11 +6,13 @@ package asn1
import (
"bytes"
+ "errors"
"fmt"
"io"
"math/big"
"reflect"
"time"
+ "unicode/utf8"
)
// A forkableWriter is an in-memory buffer that can be
@@ -280,6 +282,11 @@ func marshalIA5String(out *forkableWriter, s string) (err error) {
return
}
+func marshalUTF8String(out *forkableWriter, s string) (err error) {
+ _, err = out.Write([]byte(s))
+ return
+}
+
func marshalTwoDigits(out *forkableWriter, v int) (err error) {
err = out.WriteByte(byte('0' + (v/10)%10))
if err != nil {
@@ -289,8 +296,7 @@ func marshalTwoDigits(out *forkableWriter, v int) (err error) {
}
func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
- utc := t.UTC()
- year, month, day := utc.Date()
+ year, month, day := t.Date()
switch {
case 1950 <= year && year < 2000:
@@ -314,7 +320,7 @@ func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
return
}
- hour, min, sec := utc.Clock()
+ hour, min, sec := t.Clock()
err = marshalTwoDigits(out, hour)
if err != nil {
@@ -446,10 +452,13 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter
}
return
case reflect.String:
- if params.stringType == tagIA5String {
+ switch params.stringType {
+ case tagIA5String:
return marshalIA5String(out, v.String())
- } else {
+ case tagPrintableString:
return marshalPrintableString(out, v.String())
+ default:
+ return marshalUTF8String(out, v.String())
}
return
}
@@ -492,11 +501,27 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
}
class := classUniversal
- if params.stringType != 0 {
- if tag != tagPrintableString {
- return StructuralError{"Explicit string type given to non-string member"}
+ if params.stringType != 0 && tag != tagPrintableString {
+ return StructuralError{"Explicit string type given to non-string member"}
+ }
+
+ if tag == tagPrintableString {
+ if params.stringType == 0 {
+ // This is a string without an explicit string type. We'll use
+ // a PrintableString if the character set in the string is
+ // sufficiently limited, otherwise we'll use a UTF8String.
+ for _, r := range v.String() {
+ if r >= utf8.RuneSelf || !isPrintable(byte(r)) {
+ if !utf8.ValidString(v.String()) {
+ return errors.New("asn1: string not valid UTF-8")
+ }
+ tag = tagUTF8String
+ break
+ }
+ }
+ } else {
+ tag = params.stringType
}
- tag = params.stringType
}
if params.set {
diff --git a/src/pkg/encoding/asn1/marshal_test.go b/src/pkg/encoding/asn1/marshal_test.go
index f43bcae68..b4dbe71ef 100644
--- a/src/pkg/encoding/asn1/marshal_test.go
+++ b/src/pkg/encoding/asn1/marshal_test.go
@@ -82,7 +82,7 @@ var marshalTests = []marshalTest{
{explicitTagTest{64}, "3005a503020140"},
{time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"},
{time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"},
- {time.Unix(1258325776, 0).In(PST), "17113039313131353232353631362d30383030"},
+ {time.Unix(1258325776, 0).In(PST), "17113039313131353134353631362d30383030"},
{BitString{[]byte{0x80}, 1}, "03020780"},
{BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"},
{ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},
@@ -122,6 +122,7 @@ var marshalTests = []marshalTest{
{testSET([]int{10}), "310302010a"},
{omitEmptyTest{[]string{}}, "3000"},
{omitEmptyTest{[]string{"1"}}, "30053003130131"},
+ {"Σ", "0c02cea3"},
}
func TestMarshal(t *testing.T) {
@@ -131,9 +132,16 @@ func TestMarshal(t *testing.T) {
t.Errorf("#%d failed: %s", i, err)
}
out, _ := hex.DecodeString(test.out)
- if bytes.Compare(out, data) != 0 {
+ if !bytes.Equal(out, data) {
t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out)
}
}
}
+
+func TestInvalidUTF8(t *testing.T) {
+ _, err := Marshal(string([]byte{0xff, 0xff}))
+ if err == nil {
+ t.Errorf("invalid UTF8 string was accepted")
+ }
+}
diff --git a/src/pkg/encoding/base32/base32.go b/src/pkg/encoding/base32/base32.go
index 71da6e22b..dbefc48fa 100644
--- a/src/pkg/encoding/base32/base32.go
+++ b/src/pkg/encoding/base32/base32.go
@@ -237,7 +237,6 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
dlen := 8
// do the top bytes contain any data?
- dbufloop:
for j := 0; j < 8; {
if len(src) == 0 {
return n, false, CorruptInputError(len(osrc) - len(src) - j)
@@ -258,7 +257,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
}
dlen = j
end = true
- break dbufloop
+ break
}
dbuf[j] = enc.decodeMap[in]
if dbuf[j] == 0xFF {
diff --git a/src/pkg/encoding/base32/example_test.go b/src/pkg/encoding/base32/example_test.go
new file mode 100644
index 000000000..f6128d900
--- /dev/null
+++ b/src/pkg/encoding/base32/example_test.go
@@ -0,0 +1,45 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Keep in sync with ../base64/example_test.go.
+
+package base32_test
+
+import (
+ "encoding/base32"
+ "fmt"
+ "os"
+)
+
+func ExampleEncoding_EncodeToString() {
+ data := []byte("any + old & data")
+ str := base32.StdEncoding.EncodeToString(data)
+ fmt.Println(str)
+ // Output:
+ // MFXHSIBLEBXWYZBAEYQGIYLUME======
+}
+
+func ExampleEncoding_DecodeString() {
+ str := "ONXW2ZJAMRQXIYJAO5UXI2BAAAQGC3TEEDX3XPY="
+ data, err := base32.StdEncoding.DecodeString(str)
+ if err != nil {
+ fmt.Println("error:", err)
+ return
+ }
+ fmt.Printf("%q\n", data)
+ // Output:
+ // "some data with \x00 and \ufeff"
+}
+
+func ExampleNewEncoder() {
+ input := []byte("foo\x00bar")
+ encoder := base32.NewEncoder(base32.StdEncoding, os.Stdout)
+ encoder.Write(input)
+ // Must close the encoder when finished to flush any partial blocks.
+ // If you comment out the following line, the last partial block "r"
+ // won't be encoded.
+ encoder.Close()
+ // Output:
+ // MZXW6ADCMFZA====
+}
diff --git a/src/pkg/encoding/base64/base64.go b/src/pkg/encoding/base64/base64.go
index 0b842f066..e66672a1c 100644
--- a/src/pkg/encoding/base64/base64.go
+++ b/src/pkg/encoding/base64/base64.go
@@ -216,7 +216,6 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
var dbuf [4]byte
dlen := 4
- dbufloop:
for j := 0; j < 4; {
if len(src) == 0 {
return n, false, CorruptInputError(len(osrc) - len(src) - j)
@@ -240,7 +239,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
}
dlen = j
end = true
- break dbufloop
+ break
}
dbuf[j] = enc.decodeMap[in]
if dbuf[j] == 0xFF {
diff --git a/src/pkg/encoding/base64/base64_test.go b/src/pkg/encoding/base64/base64_test.go
index f9b863c36..2166abd7a 100644
--- a/src/pkg/encoding/base64/base64_test.go
+++ b/src/pkg/encoding/base64/base64_test.go
@@ -257,6 +257,7 @@ func TestDecoderIssue3577(t *testing.T) {
wantErr := errors.New("my error")
next <- nextRead{5, nil}
next <- nextRead{10, wantErr}
+ next <- nextRead{0, wantErr}
d := NewDecoder(StdEncoding, &faultInjectReader{
source: "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==", // twas brillig...
nextc: next,
diff --git a/src/pkg/encoding/base64/example_test.go b/src/pkg/encoding/base64/example_test.go
new file mode 100644
index 000000000..d18b856a0
--- /dev/null
+++ b/src/pkg/encoding/base64/example_test.go
@@ -0,0 +1,45 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Keep in sync with ../base32/example_test.go.
+
+package base64_test
+
+import (
+ "encoding/base64"
+ "fmt"
+ "os"
+)
+
+func ExampleEncoding_EncodeToString() {
+ data := []byte("any + old & data")
+ str := base64.StdEncoding.EncodeToString(data)
+ fmt.Println(str)
+ // Output:
+ // YW55ICsgb2xkICYgZGF0YQ==
+}
+
+func ExampleEncoding_DecodeString() {
+ str := "c29tZSBkYXRhIHdpdGggACBhbmQg77u/"
+ data, err := base64.StdEncoding.DecodeString(str)
+ if err != nil {
+ fmt.Println("error:", err)
+ return
+ }
+ fmt.Printf("%q\n", data)
+ // Output:
+ // "some data with \x00 and \ufeff"
+}
+
+func ExampleNewEncoder() {
+ input := []byte("foo\x00bar")
+ encoder := base64.NewEncoder(base64.StdEncoding, os.Stdout)
+ encoder.Write(input)
+ // Must close the encoder when finished to flush any partial blocks.
+ // If you comment out the following line, the last partial block "r"
+ // won't be encoded.
+ encoder.Close()
+ // Output:
+ // Zm9vAGJhcg==
+}
diff --git a/src/pkg/encoding/binary/binary.go b/src/pkg/encoding/binary/binary.go
index 712e490e6..edbac197d 100644
--- a/src/pkg/encoding/binary/binary.go
+++ b/src/pkg/encoding/binary/binary.go
@@ -125,6 +125,9 @@ func (bigEndian) GoString() string { return "binary.BigEndian" }
// of fixed-size values.
// Bytes read from r are decoded using the specified byte order
// and written to successive fields of the data.
+// When reading into structs, the field data for fields with
+// blank (_) field names is skipped; i.e., blank field names
+// may be used for padding.
func Read(r io.Reader, order ByteOrder, data interface{}) error {
// Fast path for basic types.
if n := intDestSize(data); n != 0 {
@@ -154,7 +157,7 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
return nil
}
- // Fallback to reflect-based.
+ // Fallback to reflect-based decoding.
var v reflect.Value
switch d := reflect.ValueOf(data); d.Kind() {
case reflect.Ptr:
@@ -164,9 +167,9 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
default:
return errors.New("binary.Read: invalid type " + d.Type().String())
}
- size := dataSize(v)
- if size < 0 {
- return errors.New("binary.Read: invalid type " + v.Type().String())
+ size, err := dataSize(v)
+ if err != nil {
+ return errors.New("binary.Read: " + err.Error())
}
d := &decoder{order: order, buf: make([]byte, size)}
if _, err := io.ReadFull(r, d.buf); err != nil {
@@ -181,6 +184,8 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
// values, or a pointer to such data.
// Bytes written to w are encoded using the specified byte order
// and read from successive fields of the data.
+// When writing structs, zero values are written for fields
+// with blank (_) field names.
func Write(w io.Writer, order ByteOrder, data interface{}) error {
// Fast path for basic types.
var b [8]byte
@@ -239,76 +244,80 @@ func Write(w io.Writer, order ByteOrder, data interface{}) error {
_, err := w.Write(bs)
return err
}
+
+ // Fallback to reflect-based encoding.
v := reflect.Indirect(reflect.ValueOf(data))
- size := dataSize(v)
- if size < 0 {
- return errors.New("binary.Write: invalid type " + v.Type().String())
+ size, err := dataSize(v)
+ if err != nil {
+ return errors.New("binary.Write: " + err.Error())
}
buf := make([]byte, size)
e := &encoder{order: order, buf: buf}
e.value(v)
- _, err := w.Write(buf)
+ _, err = w.Write(buf)
return err
}
// Size returns how many bytes Write would generate to encode the value v, which
// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data.
func Size(v interface{}) int {
- return dataSize(reflect.Indirect(reflect.ValueOf(v)))
+ n, err := dataSize(reflect.Indirect(reflect.ValueOf(v)))
+ if err != nil {
+ return -1
+ }
+ return n
}
// dataSize returns the number of bytes the actual data represented by v occupies in memory.
// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice
// it returns the length of the slice times the element size and does not count the memory
// occupied by the header.
-func dataSize(v reflect.Value) int {
+func dataSize(v reflect.Value) (int, error) {
if v.Kind() == reflect.Slice {
- elem := sizeof(v.Type().Elem())
- if elem < 0 {
- return -1
+ elem, err := sizeof(v.Type().Elem())
+ if err != nil {
+ return 0, err
}
- return v.Len() * elem
+ return v.Len() * elem, nil
}
return sizeof(v.Type())
}
-func sizeof(t reflect.Type) int {
+func sizeof(t reflect.Type) (int, error) {
switch t.Kind() {
case reflect.Array:
- n := sizeof(t.Elem())
- if n < 0 {
- return -1
+ n, err := sizeof(t.Elem())
+ if err != nil {
+ return 0, err
}
- return t.Len() * n
+ return t.Len() * n, nil
case reflect.Struct:
sum := 0
for i, n := 0, t.NumField(); i < n; i++ {
- s := sizeof(t.Field(i).Type)
- if s < 0 {
- return -1
+ s, err := sizeof(t.Field(i).Type)
+ if err != nil {
+ return 0, err
}
sum += s
}
- return sum
+ return sum, nil
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
- return int(t.Size())
+ return int(t.Size()), nil
}
- return -1
+ return 0, errors.New("invalid type " + t.String())
}
-type decoder struct {
+type coder struct {
order ByteOrder
buf []byte
}
-type encoder struct {
- order ByteOrder
- buf []byte
-}
+type decoder coder
+type encoder coder
func (d *decoder) uint8() uint8 {
x := d.buf[0]
@@ -379,9 +388,19 @@ func (d *decoder) value(v reflect.Value) {
}
case reflect.Struct:
+ t := v.Type()
l := v.NumField()
for i := 0; i < l; i++ {
- d.value(v.Field(i))
+ // Note: Calling v.CanSet() below is an optimization.
+ // It would be sufficient to check the field name,
+ // but creating the StructField info for each field is
+ // costly (run "go test -bench=ReadStruct" and compare
+ // results when making changes to this code).
+ if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" {
+ d.value(v)
+ } else {
+ d.skip(v)
+ }
}
case reflect.Slice:
@@ -435,9 +454,15 @@ func (e *encoder) value(v reflect.Value) {
}
case reflect.Struct:
+ t := v.Type()
l := v.NumField()
for i := 0; i < l; i++ {
- e.value(v.Field(i))
+ // see comment for corresponding code in decoder.value()
+ if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" {
+ e.value(v)
+ } else {
+ e.skip(v)
+ }
}
case reflect.Slice:
@@ -492,6 +517,19 @@ func (e *encoder) value(v reflect.Value) {
}
}
+func (d *decoder) skip(v reflect.Value) {
+ n, _ := dataSize(v)
+ d.buf = d.buf[n:]
+}
+
+func (e *encoder) skip(v reflect.Value) {
+ n, _ := dataSize(v)
+ for i := range e.buf[0:n] {
+ e.buf[i] = 0
+ }
+ e.buf = e.buf[n:]
+}
+
// intDestSize returns the size of the integer that ptrType points to,
// or 0 if the type is not supported.
func intDestSize(ptrType interface{}) int {
diff --git a/src/pkg/encoding/binary/binary_test.go b/src/pkg/encoding/binary/binary_test.go
index ff361b7e3..056f0998f 100644
--- a/src/pkg/encoding/binary/binary_test.go
+++ b/src/pkg/encoding/binary/binary_test.go
@@ -9,6 +9,7 @@ import (
"io"
"math"
"reflect"
+ "strings"
"testing"
)
@@ -120,18 +121,14 @@ func testWrite(t *testing.T, order ByteOrder, b []byte, s1 interface{}) {
checkResult(t, "Write", order, err, buf.Bytes(), b)
}
-func TestBigEndianRead(t *testing.T) { testRead(t, BigEndian, big, s) }
-
-func TestLittleEndianRead(t *testing.T) { testRead(t, LittleEndian, little, s) }
-
-func TestBigEndianWrite(t *testing.T) { testWrite(t, BigEndian, big, s) }
-
-func TestLittleEndianWrite(t *testing.T) { testWrite(t, LittleEndian, little, s) }
+func TestLittleEndianRead(t *testing.T) { testRead(t, LittleEndian, little, s) }
+func TestLittleEndianWrite(t *testing.T) { testWrite(t, LittleEndian, little, s) }
+func TestLittleEndianPtrWrite(t *testing.T) { testWrite(t, LittleEndian, little, &s) }
+func TestBigEndianRead(t *testing.T) { testRead(t, BigEndian, big, s) }
+func TestBigEndianWrite(t *testing.T) { testWrite(t, BigEndian, big, s) }
func TestBigEndianPtrWrite(t *testing.T) { testWrite(t, BigEndian, big, &s) }
-func TestLittleEndianPtrWrite(t *testing.T) { testWrite(t, LittleEndian, little, &s) }
-
func TestReadSlice(t *testing.T) {
slice := make([]int32, 2)
err := Read(bytes.NewBuffer(src), BigEndian, slice)
@@ -147,20 +144,81 @@ func TestWriteSlice(t *testing.T) {
func TestWriteT(t *testing.T) {
buf := new(bytes.Buffer)
ts := T{}
- err := Write(buf, BigEndian, ts)
- if err == nil {
- t.Errorf("WriteT: have nil, want non-nil")
+ if err := Write(buf, BigEndian, ts); err == nil {
+ t.Errorf("WriteT: have err == nil, want non-nil")
}
tv := reflect.Indirect(reflect.ValueOf(ts))
for i, n := 0, tv.NumField(); i < n; i++ {
- err = Write(buf, BigEndian, tv.Field(i).Interface())
- if err == nil {
- t.Errorf("WriteT.%v: have nil, want non-nil", tv.Field(i).Type())
+ typ := tv.Field(i).Type().String()
+ if typ == "[4]int" {
+ typ = "int" // the problem is int, not the [4]
+ }
+ if err := Write(buf, BigEndian, tv.Field(i).Interface()); err == nil {
+ t.Errorf("WriteT.%v: have err == nil, want non-nil", tv.Field(i).Type())
+ } else if !strings.Contains(err.Error(), typ) {
+ t.Errorf("WriteT: have err == %q, want it to mention %s", err, typ)
}
}
}
+type BlankFields struct {
+ A uint32
+ _ int32
+ B float64
+ _ [4]int16
+ C byte
+ _ [7]byte
+ _ struct {
+ f [8]float32
+ }
+}
+
+type BlankFieldsProbe struct {
+ A uint32
+ P0 int32
+ B float64
+ P1 [4]int16
+ C byte
+ P2 [7]byte
+ P3 struct {
+ F [8]float32
+ }
+}
+
+func TestBlankFields(t *testing.T) {
+ buf := new(bytes.Buffer)
+ b1 := BlankFields{A: 1234567890, B: 2.718281828, C: 42}
+ if err := Write(buf, LittleEndian, &b1); err != nil {
+ t.Error(err)
+ }
+
+ // zero values must have been written for blank fields
+ var p BlankFieldsProbe
+ if err := Read(buf, LittleEndian, &p); err != nil {
+ t.Error(err)
+ }
+
+ // quick test: only check first value of slices
+ if p.P0 != 0 || p.P1[0] != 0 || p.P2[0] != 0 || p.P3.F[0] != 0 {
+ t.Errorf("non-zero values for originally blank fields: %#v", p)
+ }
+
+ // write p and see if we can probe only some fields
+ if err := Write(buf, LittleEndian, &p); err != nil {
+ t.Error(err)
+ }
+
+ // read should ignore blank fields in b2
+ var b2 BlankFields
+ if err := Read(buf, LittleEndian, &b2); err != nil {
+ t.Error(err)
+ }
+ if b1.A != b2.A || b1.B != b2.B || b1.C != b2.C {
+ t.Errorf("%#v != %#v", b1, b2)
+ }
+}
+
type byteSliceReader struct {
remain []byte
}
@@ -187,7 +245,7 @@ func BenchmarkReadStruct(b *testing.B) {
bsr := &byteSliceReader{}
var buf bytes.Buffer
Write(&buf, BigEndian, &s)
- n := dataSize(reflect.ValueOf(s))
+ n, _ := dataSize(reflect.ValueOf(s))
b.SetBytes(int64(n))
t := s
b.ResetTimer()
diff --git a/src/pkg/encoding/binary/varint.go b/src/pkg/encoding/binary/varint.go
index b756afdd0..7035529f2 100644
--- a/src/pkg/encoding/binary/varint.go
+++ b/src/pkg/encoding/binary/varint.go
@@ -123,7 +123,7 @@ func ReadUvarint(r io.ByteReader) (uint64, error) {
panic("unreachable")
}
-// ReadVarint reads an encoded unsigned integer from r and returns it as a uint64.
+// ReadVarint reads an encoded signed integer from r and returns it as an int64.
func ReadVarint(r io.ByteReader) (int64, error) {
ux, err := ReadUvarint(r) // ok to continue in presence of error
x := int64(ux >> 1)
diff --git a/src/pkg/encoding/csv/writer.go b/src/pkg/encoding/csv/writer.go
index c4dcba566..1faecb664 100644
--- a/src/pkg/encoding/csv/writer.go
+++ b/src/pkg/encoding/csv/writer.go
@@ -22,7 +22,7 @@ import (
//
// If UseCRLF is true, the Writer ends each record with \r\n instead of \n.
type Writer struct {
- Comma rune // Field delimiter (set to to ',' by NewWriter)
+ Comma rune // Field delimiter (set to ',' by NewWriter)
UseCRLF bool // True to use \r\n as the line terminator
w *bufio.Writer
}
@@ -92,20 +92,26 @@ func (w *Writer) Write(record []string) (err error) {
}
// Flush writes any buffered data to the underlying io.Writer.
+// To check if an error occurred during the Flush, call Error.
func (w *Writer) Flush() {
w.w.Flush()
}
+// Error reports any error that has occurred during a previous Write or Flush.
+func (w *Writer) Error() error {
+ _, err := w.w.Write(nil)
+ return err
+}
+
// WriteAll writes multiple CSV records to w using Write and then calls Flush.
func (w *Writer) WriteAll(records [][]string) (err error) {
for _, record := range records {
err = w.Write(record)
if err != nil {
- break
+ return err
}
}
- w.Flush()
- return nil
+ return w.w.Flush()
}
// fieldNeedsQuotes returns true if our field must be enclosed in quotes.
diff --git a/src/pkg/encoding/csv/writer_test.go b/src/pkg/encoding/csv/writer_test.go
index 578959007..03ca6b093 100644
--- a/src/pkg/encoding/csv/writer_test.go
+++ b/src/pkg/encoding/csv/writer_test.go
@@ -6,6 +6,7 @@ package csv
import (
"bytes"
+ "errors"
"testing"
)
@@ -42,3 +43,30 @@ func TestWrite(t *testing.T) {
}
}
}
+
+type errorWriter struct{}
+
+func (e errorWriter) Write(b []byte) (int, error) {
+ return 0, errors.New("Test")
+}
+
+func TestError(t *testing.T) {
+ b := &bytes.Buffer{}
+ f := NewWriter(b)
+ f.Write([]string{"abc"})
+ f.Flush()
+ err := f.Error()
+
+ if err != nil {
+ t.Errorf("Unexpected error: %s\n", err)
+ }
+
+ f = NewWriter(errorWriter{})
+ f.Write([]string{"abc"})
+ f.Flush()
+ err = f.Error()
+
+ if err == nil {
+ t.Error("Error should not be nil")
+ }
+}
diff --git a/src/pkg/encoding/gob/codec_test.go b/src/pkg/encoding/gob/codec_test.go
index ebcbb78eb..482212b74 100644
--- a/src/pkg/encoding/gob/codec_test.go
+++ b/src/pkg/encoding/gob/codec_test.go
@@ -7,6 +7,7 @@ package gob
import (
"bytes"
"errors"
+ "flag"
"math"
"math/rand"
"reflect"
@@ -16,6 +17,8 @@ import (
"unsafe"
)
+var doFuzzTests = flag.Bool("gob.fuzz", false, "run the fuzz tests, which are large and very slow")
+
// Guarantee encoding format by comparing some encodings to hand-written values
type EncodeT struct {
x uint64
@@ -1434,7 +1437,8 @@ func encFuzzDec(rng *rand.Rand, in interface{}) error {
// This does some "fuzz testing" by attempting to decode a sequence of random bytes.
func TestFuzz(t *testing.T) {
- if testing.Short() {
+ if !*doFuzzTests {
+ t.Logf("disabled; run with -gob.fuzz to enable")
return
}
@@ -1453,11 +1457,16 @@ func TestFuzz(t *testing.T) {
}
func TestFuzzRegressions(t *testing.T) {
+ if !*doFuzzTests {
+ t.Logf("disabled; run with -gob.fuzz to enable")
+ return
+ }
+
// An instance triggering a type name of length ~102 GB.
testFuzz(t, 1328492090837718000, 100, new(float32))
// An instance triggering a type name of 1.6 GB.
- // Commented out because it takes 5m to run.
- //testFuzz(t, 1330522872628565000, 100, new(int))
+ // Note: can take several minutes to run.
+ testFuzz(t, 1330522872628565000, 100, new(int))
}
func testFuzz(t *testing.T, seed int64, n int, input ...interface{}) {
diff --git a/src/pkg/encoding/gob/decode.go b/src/pkg/encoding/gob/decode.go
index e32a178ab..a80d9f919 100644
--- a/src/pkg/encoding/gob/decode.go
+++ b/src/pkg/encoding/gob/decode.go
@@ -62,15 +62,15 @@ func overflow(name string) error {
// Used only by the Decoder to read the message length.
func decodeUintReader(r io.Reader, buf []byte) (x uint64, width int, err error) {
width = 1
- _, err = r.Read(buf[0:width])
- if err != nil {
+ n, err := io.ReadFull(r, buf[0:width])
+ if n == 0 {
return
}
b := buf[0]
if b <= 0x7f {
return uint64(b), width, nil
}
- n := -int(int8(b))
+ n = -int(int8(b))
if n > uint64Size {
err = errBadUint
return
@@ -562,6 +562,9 @@ func (dec *Decoder) ignoreSingle(engine *decEngine) {
func (dec *Decoder) decodeArrayHelper(state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl error) {
instr := &decInstr{elemOp, 0, elemIndir, 0, ovfl}
for i := 0; i < length; i++ {
+ if state.b.Len() == 0 {
+ errorf("decoding array or slice: length exceeds input size (%d elements)", length)
+ }
up := unsafe.Pointer(p)
if elemIndir > 1 {
up = decIndirect(up, elemIndir)
@@ -652,9 +655,6 @@ func (dec *Decoder) ignoreMap(state *decoderState, keyOp, elemOp decOp) {
// Slices are encoded as an unsigned length followed by the elements.
func (dec *Decoder) decodeSlice(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, indir, elemIndir int, ovfl error) {
nr := state.decodeUint()
- if nr > uint64(state.b.Len()) {
- errorf("length of slice exceeds input size (%d elements)", nr)
- }
n := int(nr)
if indir > 0 {
up := unsafe.Pointer(p)
@@ -717,7 +717,9 @@ func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p ui
errorf("name too long (%d bytes): %.20q...", len(name), name)
}
// The concrete type must be registered.
+ registerLock.RLock()
typ, ok := nameToConcreteType[name]
+ registerLock.RUnlock()
if !ok {
errorf("name not registered for interface: %q", name)
}
diff --git a/src/pkg/encoding/gob/decoder.go b/src/pkg/encoding/gob/decoder.go
index c5c7d3fdb..04f706ca5 100644
--- a/src/pkg/encoding/gob/decoder.go
+++ b/src/pkg/encoding/gob/decoder.go
@@ -87,21 +87,38 @@ func (dec *Decoder) recvMessage() bool {
// readMessage reads the next nbytes bytes from the input.
func (dec *Decoder) readMessage(nbytes int) {
- // Allocate the buffer.
- if cap(dec.tmp) < nbytes {
- dec.tmp = make([]byte, nbytes+100) // room to grow
+ // Allocate the dec.tmp buffer, up to 10KB.
+ const maxBuf = 10 * 1024
+ nTmp := nbytes
+ if nTmp > maxBuf {
+ nTmp = maxBuf
}
- dec.tmp = dec.tmp[:nbytes]
+ if cap(dec.tmp) < nTmp {
+ nAlloc := nTmp + 100 // A little extra for growth.
+ if nAlloc > maxBuf {
+ nAlloc = maxBuf
+ }
+ dec.tmp = make([]byte, nAlloc)
+ }
+ dec.tmp = dec.tmp[:nTmp]
// Read the data
- _, dec.err = io.ReadFull(dec.r, dec.tmp)
- if dec.err != nil {
- if dec.err == io.EOF {
- dec.err = io.ErrUnexpectedEOF
+ dec.buf.Grow(nbytes)
+ for nbytes > 0 {
+ if nbytes < nTmp {
+ dec.tmp = dec.tmp[:nbytes]
}
- return
+ var nRead int
+ nRead, dec.err = io.ReadFull(dec.r, dec.tmp)
+ if dec.err != nil {
+ if dec.err == io.EOF {
+ dec.err = io.ErrUnexpectedEOF
+ }
+ return
+ }
+ dec.buf.Write(dec.tmp)
+ nbytes -= nRead
}
- dec.buf.Write(dec.tmp)
}
// toInt turns an encoded uint64 into an int, according to the marshaling rules.
diff --git a/src/pkg/encoding/gob/doc.go b/src/pkg/encoding/gob/doc.go
index 821d9a3fe..5bd61b12e 100644
--- a/src/pkg/encoding/gob/doc.go
+++ b/src/pkg/encoding/gob/doc.go
@@ -67,11 +67,13 @@ point values may be received into any floating point variable. However,
the destination variable must be able to represent the value or the decode
operation will fail.
-Structs, arrays and slices are also supported. Strings and arrays of bytes are
-supported with a special, efficient representation (see below). When a slice is
-decoded, if the existing slice has capacity the slice will be extended in place;
-if not, a new array is allocated. Regardless, the length of the resulting slice
-reports the number of elements decoded.
+Structs, arrays and slices are also supported. Structs encode and
+decode only exported fields. Strings and arrays of bytes are supported
+with a special, efficient representation (see below). When a slice
+is decoded, if the existing slice has capacity the slice will be
+extended in place; if not, a new array is allocated. Regardless,
+the length of the resulting slice reports the number of elements
+decoded.
Functions and channels cannot be sent in a gob. Attempting
to encode a value that contains one will fail.
@@ -118,7 +120,7 @@ elements using the standard gob encoding for their type, recursively.
Maps are sent as an unsigned count followed by that many key, element
pairs. Empty but non-nil maps are sent, so if the sender has allocated
-a map, the receiver will allocate a map even no elements are
+a map, the receiver will allocate a map even if no elements are
transmitted.
Structs are sent as a sequence of (field number, field value) pairs. The field
@@ -328,7 +330,7 @@ reserved).
01 // Add 1 to get field number 0: field[1].name
01 // 1 byte
59 // structType.field[1].name = "Y"
- 01 // Add 1 to get field number 1: field[0].id
+ 01 // Add 1 to get field number 1: field[1].id
04 // struct.Type.field[1].typeId is 2 (signed int).
00 // End of structType.field[1]; end of structType.field.
00 // end of wireType.structType structure
diff --git a/src/pkg/encoding/gob/encode.go b/src/pkg/encoding/gob/encode.go
index 168e08b13..ea37a6cbd 100644
--- a/src/pkg/encoding/gob/encode.go
+++ b/src/pkg/encoding/gob/encode.go
@@ -426,6 +426,12 @@ func (enc *Encoder) encodeMap(b *bytes.Buffer, mv reflect.Value, keyOp, elemOp e
// by the concrete value. A nil value gets sent as the empty string for the name,
// followed by no value.
func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) {
+ // Gobs can encode nil interface values but not typed interface
+ // values holding nil pointers, since nil pointers point to no value.
+ elem := iv.Elem()
+ if elem.Kind() == reflect.Ptr && elem.IsNil() {
+ errorf("gob: cannot encode nil pointer of type %s inside interface", iv.Elem().Type())
+ }
state := enc.newEncoderState(b)
state.fieldnum = -1
state.sendZero = true
@@ -435,7 +441,9 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) {
}
ut := userType(iv.Elem().Type())
+ registerLock.RLock()
name, ok := concreteTypeToName[ut.base]
+ registerLock.RUnlock()
if !ok {
errorf("type not registered for interface: %s", ut.base)
}
@@ -454,7 +462,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) {
enc.pushWriter(b)
data := new(bytes.Buffer)
data.Write(spaceForLength)
- enc.encode(data, iv.Elem(), ut)
+ enc.encode(data, elem, ut)
if enc.err != nil {
error_(enc.err)
}
@@ -698,9 +706,20 @@ func (enc *Encoder) getEncEngine(ut *userTypeInfo) *encEngine {
error_(err1)
}
if info.encoder == nil {
- // mark this engine as underway before compiling to handle recursive types.
+ // Assign the encEngine now, so recursive types work correctly. But...
info.encoder = new(encEngine)
+ // ... if we fail to complete building the engine, don't cache the half-built machine.
+ // Doing this here means we won't cache a type that is itself OK but
+ // that contains a nested type that won't compile. The result is consistent
+ // error behavior when Encode is called multiple times on the top-level type.
+ ok := false
+ defer func() {
+ if !ok {
+ info.encoder = nil
+ }
+ }()
info.encoder = enc.compileEnc(ut)
+ ok = true
}
return info.encoder
}
diff --git a/src/pkg/encoding/gob/encoder.go b/src/pkg/encoding/gob/encoder.go
index a15b5a1f9..f669c3d5b 100644
--- a/src/pkg/encoding/gob/encoder.go
+++ b/src/pkg/encoding/gob/encoder.go
@@ -132,13 +132,13 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp
return true
}
-// sendType sends the type info to the other side, if necessary.
+// sendType sends the type info to the other side, if necessary.
func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) {
ut := userType(origt)
if ut.isGobEncoder {
// The rules are different: regardless of the underlying type's representation,
- // we need to tell the other side that this exact type is a GobEncoder.
- return enc.sendActualType(w, state, ut, ut.user)
+ // we need to tell the other side that the base type is a GobEncoder.
+ return enc.sendActualType(w, state, ut, ut.base)
}
// It's a concrete value, so drill down to the base type.
@@ -218,6 +218,12 @@ func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) {
// EncodeValue transmits the data item represented by the reflection value,
// guaranteeing that all necessary type information has been transmitted first.
func (enc *Encoder) EncodeValue(value reflect.Value) error {
+ // Gobs contain values. They cannot represent nil pointers, which
+ // have no value to encode.
+ if value.Kind() == reflect.Ptr && value.IsNil() {
+ panic("gob: cannot encode nil pointer of type " + value.Type().String())
+ }
+
// Make sure we're single-threaded through here, so multiple
// goroutines can share an encoder.
enc.mutex.Lock()
diff --git a/src/pkg/encoding/gob/encoder_test.go b/src/pkg/encoding/gob/encoder_test.go
index c4947cbb8..b684772c6 100644
--- a/src/pkg/encoding/gob/encoder_test.go
+++ b/src/pkg/encoding/gob/encoder_test.go
@@ -736,3 +736,109 @@ func TestPtrToMapOfMap(t *testing.T) {
t.Fatalf("expected %v got %v", data, newData)
}
}
+
+// A top-level nil pointer generates a panic with a helpful string-valued message.
+func TestTopLevelNilPointer(t *testing.T) {
+ errMsg := topLevelNilPanic(t)
+ if errMsg == "" {
+ t.Fatal("top-level nil pointer did not panic")
+ }
+ if !strings.Contains(errMsg, "nil pointer") {
+ t.Fatal("expected nil pointer error, got:", errMsg)
+ }
+}
+
+func topLevelNilPanic(t *testing.T) (panicErr string) {
+ defer func() {
+ e := recover()
+ if err, ok := e.(string); ok {
+ panicErr = err
+ }
+ }()
+ var ip *int
+ buf := new(bytes.Buffer)
+ if err := NewEncoder(buf).Encode(ip); err != nil {
+ t.Fatal("error in encode:", err)
+ }
+ return
+}
+
+func TestNilPointerInsideInterface(t *testing.T) {
+ var ip *int
+ si := struct {
+ I interface{}
+ }{
+ I: ip,
+ }
+ buf := new(bytes.Buffer)
+ err := NewEncoder(buf).Encode(si)
+ if err == nil {
+ t.Fatal("expected error, got none")
+ }
+ errMsg := err.Error()
+ if !strings.Contains(errMsg, "nil pointer") || !strings.Contains(errMsg, "interface") {
+ t.Fatal("expected error about nil pointer and interface, got:", errMsg)
+ }
+}
+
+type Bug4Public struct {
+ Name string
+ Secret Bug4Secret
+}
+
+type Bug4Secret struct {
+ a int // error: no exported fields.
+}
+
+// Test that a failed compilation doesn't leave around an executable encoder.
+// Issue 3273.
+func TestMutipleEncodingsOfBadType(t *testing.T) {
+ x := Bug4Public{
+ Name: "name",
+ Secret: Bug4Secret{1},
+ }
+ buf := new(bytes.Buffer)
+ enc := NewEncoder(buf)
+ err := enc.Encode(x)
+ if err == nil {
+ t.Fatal("first encoding: expected error")
+ }
+ buf.Reset()
+ enc = NewEncoder(buf)
+ err = enc.Encode(x)
+ if err == nil {
+ t.Fatal("second encoding: expected error")
+ }
+ if !strings.Contains(err.Error(), "no exported fields") {
+ t.Errorf("expected error about no exported fields; got %v", err)
+ }
+}
+
+// There was an error check comparing the length of the input with the
+// length of the slice being decoded. It was wrong because the next
+// thing in the input might be a type definition, which would lead to
+// an incorrect length check. This test reproduces the corner case.
+
+type Z struct {
+}
+
+func Test29ElementSlice(t *testing.T) {
+ Register(Z{})
+ src := make([]interface{}, 100) // Size needs to be bigger than size of type definition.
+ for i := range src {
+ src[i] = Z{}
+ }
+ buf := new(bytes.Buffer)
+ err := NewEncoder(buf).Encode(src)
+ if err != nil {
+ t.Fatalf("encode: %v", err)
+ return
+ }
+
+ var dst []interface{}
+ err = NewDecoder(buf).Decode(&dst)
+ if err != nil {
+ t.Errorf("decode: %v", err)
+ return
+ }
+}
diff --git a/src/pkg/encoding/gob/gobencdec_test.go b/src/pkg/encoding/gob/gobencdec_test.go
index 45240d764..ddcd80b1a 100644
--- a/src/pkg/encoding/gob/gobencdec_test.go
+++ b/src/pkg/encoding/gob/gobencdec_test.go
@@ -1,4 +1,4 @@
-// Copyright 20011 The Go Authors. All rights reserved.
+// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -142,6 +142,18 @@ type GobTest5 struct {
V *ValueGobber
}
+type GobTest6 struct {
+ X int // guarantee we have something in common with GobTest*
+ V ValueGobber
+ W *ValueGobber
+}
+
+type GobTest7 struct {
+ X int // guarantee we have something in common with GobTest*
+ V *ValueGobber
+ W ValueGobber
+}
+
type GobTestIgnoreEncoder struct {
X int // guarantee we have something in common with GobTest*
}
@@ -336,7 +348,7 @@ func TestGobEncoderFieldsOfDifferentType(t *testing.T) {
t.Fatal("decode error:", err)
}
if y.G.s != "XYZ" {
- t.Fatalf("expected `XYZ` got %c", y.G.s)
+ t.Fatalf("expected `XYZ` got %q", y.G.s)
}
}
@@ -360,6 +372,61 @@ func TestGobEncoderValueEncoder(t *testing.T) {
}
}
+// Test that we can use a value then a pointer type of a GobEncoder
+// in the same encoded value. Bug 4647.
+func TestGobEncoderValueThenPointer(t *testing.T) {
+ v := ValueGobber("forty-two")
+ w := ValueGobber("six-by-nine")
+
+ // this was a bug: encoding a GobEncoder by value before a GobEncoder
+ // pointer would cause duplicate type definitions to be sent.
+
+ b := new(bytes.Buffer)
+ enc := NewEncoder(b)
+ if err := enc.Encode(GobTest6{42, v, &w}); err != nil {
+ t.Fatal("encode error:", err)
+ }
+ dec := NewDecoder(b)
+ x := new(GobTest6)
+ if err := dec.Decode(x); err != nil {
+ t.Fatal("decode error:", err)
+ }
+ if got, want := x.V, v; got != want {
+ t.Errorf("v = %q, want %q", got, want)
+ }
+ if got, want := x.W, w; got == nil {
+ t.Errorf("w = nil, want %q", want)
+ } else if *got != want {
+ t.Errorf("w = %q, want %q", *got, want)
+ }
+}
+
+// Test that we can use a pointer then a value type of a GobEncoder
+// in the same encoded value.
+func TestGobEncoderPointerThenValue(t *testing.T) {
+ v := ValueGobber("forty-two")
+ w := ValueGobber("six-by-nine")
+
+ b := new(bytes.Buffer)
+ enc := NewEncoder(b)
+ if err := enc.Encode(GobTest7{42, &v, w}); err != nil {
+ t.Fatal("encode error:", err)
+ }
+ dec := NewDecoder(b)
+ x := new(GobTest7)
+ if err := dec.Decode(x); err != nil {
+ t.Fatal("decode error:", err)
+ }
+ if got, want := x.V, v; got == nil {
+ t.Errorf("v = nil, want %q", want)
+ } else if *got != want {
+ t.Errorf("v = %q, want %q", got, want)
+ }
+ if got, want := x.W, w; got != want {
+ t.Errorf("w = %q, want %q", got, want)
+ }
+}
+
func TestGobEncoderFieldTypeError(t *testing.T) {
// GobEncoder to non-decoder: error
b := new(bytes.Buffer)
diff --git a/src/pkg/encoding/gob/timing_test.go b/src/pkg/encoding/gob/timing_test.go
index b9371c423..13eb11925 100644
--- a/src/pkg/encoding/gob/timing_test.go
+++ b/src/pkg/encoding/gob/timing_test.go
@@ -9,7 +9,6 @@ import (
"fmt"
"io"
"os"
- "runtime"
"testing"
)
@@ -50,47 +49,43 @@ func BenchmarkEndToEndByteBuffer(b *testing.B) {
}
func TestCountEncodeMallocs(t *testing.T) {
+ const N = 1000
+
var buf bytes.Buffer
enc := NewEncoder(&buf)
bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
- memstats := new(runtime.MemStats)
- runtime.ReadMemStats(memstats)
- mallocs := 0 - memstats.Mallocs
- const count = 1000
- for i := 0; i < count; i++ {
+
+ allocs := testing.AllocsPerRun(N, func() {
err := enc.Encode(bench)
if err != nil {
t.Fatal("encode:", err)
}
- }
- runtime.ReadMemStats(memstats)
- mallocs += memstats.Mallocs
- fmt.Printf("mallocs per encode of type Bench: %d\n", mallocs/count)
+ })
+ fmt.Printf("mallocs per encode of type Bench: %v\n", allocs)
}
func TestCountDecodeMallocs(t *testing.T) {
+ const N = 1000
+
var buf bytes.Buffer
enc := NewEncoder(&buf)
bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
- const count = 1000
- for i := 0; i < count; i++ {
+
+ // Fill the buffer with enough to decode
+ testing.AllocsPerRun(N, func() {
err := enc.Encode(bench)
if err != nil {
t.Fatal("encode:", err)
}
- }
+ })
+
dec := NewDecoder(&buf)
- memstats := new(runtime.MemStats)
- runtime.ReadMemStats(memstats)
- mallocs := 0 - memstats.Mallocs
- for i := 0; i < count; i++ {
+ allocs := testing.AllocsPerRun(N, func() {
*bench = Bench{}
err := dec.Decode(&bench)
if err != nil {
t.Fatal("decode:", err)
}
- }
- runtime.ReadMemStats(memstats)
- mallocs += memstats.Mallocs
- fmt.Printf("mallocs per decode of type Bench: %d\n", mallocs/count)
+ })
+ fmt.Printf("mallocs per decode of type Bench: %v\n", allocs)
}
diff --git a/src/pkg/encoding/gob/type.go b/src/pkg/encoding/gob/type.go
index 0dd7a0a77..ea0db4eac 100644
--- a/src/pkg/encoding/gob/type.go
+++ b/src/pkg/encoding/gob/type.go
@@ -712,6 +712,7 @@ type GobDecoder interface {
}
var (
+ registerLock sync.RWMutex
nameToConcreteType = make(map[string]reflect.Type)
concreteTypeToName = make(map[reflect.Type]string)
)
@@ -723,6 +724,8 @@ func RegisterName(name string, value interface{}) {
// reserved for nil
panic("attempt to register empty name")
}
+ registerLock.Lock()
+ defer registerLock.Unlock()
ut := userType(reflect.TypeOf(value))
// Check for incompatible duplicates. The name must refer to the
// same user type, and vice versa.
@@ -749,12 +752,28 @@ func Register(value interface{}) {
rt := reflect.TypeOf(value)
name := rt.String()
- // But for named types (or pointers to them), qualify with import path.
+ // But for named types (or pointers to them), qualify with import path (but see inner comment).
// Dereference one pointer looking for a named type.
star := ""
if rt.Name() == "" {
if pt := rt; pt.Kind() == reflect.Ptr {
star = "*"
+ // NOTE: The following line should be rt = pt.Elem() to implement
+ // what the comment above claims, but fixing it would break compatibility
+ // with existing gobs.
+ //
+ // Given package p imported as "full/p" with these definitions:
+ // package p
+ // type T1 struct { ... }
+ // this table shows the intended and actual strings used by gob to
+ // name the types:
+ //
+ // Type Correct string Actual string
+ //
+ // T1 full/p.T1 full/p.T1
+ // *T1 *full/p.T1 *p.T1
+ //
+ // The missing full path cannot be fixed without breaking existing gob decoders.
rt = pt
}
}
diff --git a/src/pkg/encoding/gob/type_test.go b/src/pkg/encoding/gob/type_test.go
index 42bdb4cf7..e230d22d4 100644
--- a/src/pkg/encoding/gob/type_test.go
+++ b/src/pkg/encoding/gob/type_test.go
@@ -5,6 +5,7 @@
package gob
import (
+ "bytes"
"reflect"
"testing"
)
@@ -159,3 +160,63 @@ func TestRegistration(t *testing.T) {
Register(new(T))
Register(new(T))
}
+
+type N1 struct{}
+type N2 struct{}
+
+// See comment in type.go/Register.
+func TestRegistrationNaming(t *testing.T) {
+ testCases := []struct {
+ t interface{}
+ name string
+ }{
+ {&N1{}, "*gob.N1"},
+ {N2{}, "encoding/gob.N2"},
+ }
+
+ for _, tc := range testCases {
+ Register(tc.t)
+
+ tct := reflect.TypeOf(tc.t)
+ registerLock.RLock()
+ ct := nameToConcreteType[tc.name]
+ registerLock.RUnlock()
+ if ct != tct {
+ t.Errorf("nameToConcreteType[%q] = %v, want %v", tc.name, ct, tct)
+ }
+ // concreteTypeToName is keyed off the base type.
+ if tct.Kind() == reflect.Ptr {
+ tct = tct.Elem()
+ }
+ if n := concreteTypeToName[tct]; n != tc.name {
+ t.Errorf("concreteTypeToName[%v] got %v, want %v", tct, n, tc.name)
+ }
+ }
+}
+
+func TestStressParallel(t *testing.T) {
+ type T2 struct{ A int }
+ c := make(chan bool)
+ const N = 10
+ for i := 0; i < N; i++ {
+ go func() {
+ p := new(T2)
+ Register(p)
+ b := new(bytes.Buffer)
+ enc := NewEncoder(b)
+ err := enc.Encode(p)
+ if err != nil {
+ t.Error("encoder fail:", err)
+ }
+ dec := NewDecoder(b)
+ err = dec.Decode(p)
+ if err != nil {
+ t.Error("decoder fail:", err)
+ }
+ c <- true
+ }()
+ }
+ for i := 0; i < N; i++ {
+ <-c
+ }
+}
diff --git a/src/pkg/encoding/hex/hex_test.go b/src/pkg/encoding/hex/hex_test.go
index 456f9eac7..356f590f0 100644
--- a/src/pkg/encoding/hex/hex_test.go
+++ b/src/pkg/encoding/hex/hex_test.go
@@ -65,7 +65,7 @@ func TestDecodeString(t *testing.T) {
t.Errorf("#%d: unexpected err value: %s", i, err)
continue
}
- if bytes.Compare(dst, test.dec) != 0 {
+ if !bytes.Equal(dst, test.dec) {
t.Errorf("#%d: got: %#v want: #%v", i, dst, test.dec)
}
}
diff --git a/src/pkg/encoding/json/bench_test.go b/src/pkg/encoding/json/bench_test.go
index 333c1c0ce..29dbc26d4 100644
--- a/src/pkg/encoding/json/bench_test.go
+++ b/src/pkg/encoding/json/bench_test.go
@@ -153,5 +153,37 @@ func BenchmarkCodeUnmarshalReuse(b *testing.B) {
b.Fatal("Unmmarshal:", err)
}
}
- b.SetBytes(int64(len(codeJSON)))
+}
+
+func BenchmarkUnmarshalString(b *testing.B) {
+ data := []byte(`"hello, world"`)
+ var s string
+
+ for i := 0; i < b.N; i++ {
+ if err := Unmarshal(data, &s); err != nil {
+ b.Fatal("Unmarshal:", err)
+ }
+ }
+}
+
+func BenchmarkUnmarshalFloat64(b *testing.B) {
+ var f float64
+ data := []byte(`3.14`)
+
+ for i := 0; i < b.N; i++ {
+ if err := Unmarshal(data, &f); err != nil {
+ b.Fatal("Unmarshal:", err)
+ }
+ }
+}
+
+func BenchmarkUnmarshalInt64(b *testing.B) {
+ var x int64
+ data := []byte(`3`)
+
+ for i := 0; i < b.N; i++ {
+ if err := Unmarshal(data, &x); err != nil {
+ b.Fatal("Unmarshal:", err)
+ }
+ }
}
diff --git a/src/pkg/encoding/json/decode.go b/src/pkg/encoding/json/decode.go
index d61f88706..f2ec9cb67 100644
--- a/src/pkg/encoding/json/decode.go
+++ b/src/pkg/encoding/json/decode.go
@@ -33,6 +33,10 @@ import (
// the value pointed at by the pointer. If the pointer is nil, Unmarshal
// allocates a new value for it to point to.
//
+// To unmarshal JSON into a struct, Unmarshal matches incoming object
+// keys to the keys used by Marshal (either the struct field name or its tag),
+// preferring an exact match but also accepting a case-insensitive match.
+//
// To unmarshal JSON into an interface value, Unmarshal unmarshals
// the JSON into the concrete value contained in the interface value.
// If the interface value is nil, that is, has no concrete value stored in it,
@@ -51,24 +55,29 @@ import (
// If no more serious errors are encountered, Unmarshal returns
// an UnmarshalTypeError describing the earliest such error.
//
+// When unmarshaling quoted strings, invalid UTF-8 or
+// invalid UTF-16 surrogate pairs are not treated as an error.
+// Instead, they are replaced by the Unicode replacement
+// character U+FFFD.
+//
func Unmarshal(data []byte, v interface{}) error {
- d := new(decodeState).init(data)
-
- // Quick check for well-formedness.
+ // Check for well-formedness.
// Avoids filling out half a data structure
// before discovering a JSON syntax error.
+ var d decodeState
err := checkValid(data, &d.scan)
if err != nil {
return err
}
+ d.init(data)
return d.unmarshal(v)
}
// Unmarshaler is the interface implemented by objects
// that can unmarshal a JSON description of themselves.
-// The input can be assumed to be a valid JSON object
-// encoding. UnmarshalJSON must copy the JSON data
+// The input can be assumed to be a valid encoding of
+// a JSON value. UnmarshalJSON must copy the JSON data
// if it wishes to retain the data after returning.
type Unmarshaler interface {
UnmarshalJSON([]byte) error
@@ -87,6 +96,7 @@ func (e *UnmarshalTypeError) Error() string {
// An UnmarshalFieldError describes a JSON object key that
// led to an unexported (and therefore unwritable) struct field.
+// (No longer used; kept for compatibility.)
type UnmarshalFieldError struct {
Key string
Type reflect.Type
@@ -125,18 +135,33 @@ func (d *decodeState) unmarshal(v interface{}) (err error) {
}()
rv := reflect.ValueOf(v)
- pv := rv
- if pv.Kind() != reflect.Ptr || pv.IsNil() {
+ if rv.Kind() != reflect.Ptr || rv.IsNil() {
return &InvalidUnmarshalError{reflect.TypeOf(v)}
}
d.scan.reset()
- // We decode rv not pv.Elem because the Unmarshaler interface
+ // We decode rv not rv.Elem because the Unmarshaler interface
// test must be applied at the top level of the value.
d.value(rv)
return d.savedError
}
+// A Number represents a JSON number literal.
+type Number string
+
+// String returns the literal text of the number.
+func (n Number) String() string { return string(n) }
+
+// Float64 returns the number as a float64.
+func (n Number) Float64() (float64, error) {
+ return strconv.ParseFloat(string(n), 64)
+}
+
+// Int64 returns the number as an int64.
+func (n Number) Int64() (int64, error) {
+ return strconv.ParseInt(string(n), 10, 64)
+}
+
// decodeState represents the state while decoding a JSON value.
type decodeState struct {
data []byte
@@ -145,6 +170,7 @@ type decodeState struct {
nextscan scanner // for calls to nextValue
savedError error
tempstr string // scratch space to avoid some allocations
+ useNumber bool
}
// errPhase is used for errors that should not happen unless
@@ -265,47 +291,32 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
v = v.Addr()
}
for {
- var isUnmarshaler bool
- if v.Type().NumMethod() > 0 {
- // Remember that this is an unmarshaler,
- // but wait to return it until after allocating
- // the pointer (if necessary).
- _, isUnmarshaler = v.Interface().(Unmarshaler)
- }
-
// Load value from interface, but only if the result will be
// usefully addressable.
- if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() {
- e := iv.Elem()
+ if v.Kind() == reflect.Interface && !v.IsNil() {
+ e := v.Elem()
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
v = e
continue
}
}
- pv := v
- if pv.Kind() != reflect.Ptr {
+ if v.Kind() != reflect.Ptr {
break
}
- if pv.Elem().Kind() != reflect.Ptr && decodingNull && pv.CanSet() {
- return nil, pv
+ if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() {
+ break
}
- if pv.IsNil() {
- pv.Set(reflect.New(pv.Type().Elem()))
+ if v.IsNil() {
+ v.Set(reflect.New(v.Type().Elem()))
}
- if isUnmarshaler {
- // Using v.Interface().(Unmarshaler)
- // here means that we have to use a pointer
- // as the struct field. We cannot use a value inside
- // a pointer to a struct, because in that case
- // v.Interface() is the value (x.f) not the pointer (&x.f).
- // This is an unfortunate consequence of reflect.
- // An alternative would be to look up the
- // UnmarshalJSON method and return a FuncValue.
- return v.Interface().(Unmarshaler), reflect.Value{}
+ if v.Type().NumMethod() > 0 {
+ if unmarshaler, ok := v.Interface().(Unmarshaler); ok {
+ return unmarshaler, reflect.Value{}
+ }
}
- v = pv.Elem()
+ v = v.Elem()
}
return nil, v
}
@@ -327,15 +338,19 @@ func (d *decodeState) array(v reflect.Value) {
// Check type of target.
switch v.Kind() {
+ case reflect.Interface:
+ if v.NumMethod() == 0 {
+ // Decoding into nil interface? Switch to non-reflect code.
+ v.Set(reflect.ValueOf(d.arrayInterface()))
+ return
+ }
+ // Otherwise it's invalid.
+ fallthrough
default:
d.saveError(&UnmarshalTypeError{"array", v.Type()})
d.off--
d.next()
return
- case reflect.Interface:
- // Decoding into nil interface? Switch to non-reflect code.
- v.Set(reflect.ValueOf(d.arrayInterface()))
- return
case reflect.Array:
case reflect.Slice:
break
@@ -421,36 +436,27 @@ func (d *decodeState) object(v reflect.Value) {
v = pv
// Decoding into nil interface? Switch to non-reflect code.
- iv := v
- if iv.Kind() == reflect.Interface {
- iv.Set(reflect.ValueOf(d.objectInterface()))
+ if v.Kind() == reflect.Interface && v.NumMethod() == 0 {
+ v.Set(reflect.ValueOf(d.objectInterface()))
return
}
// Check type of target: struct or map[string]T
- var (
- mv reflect.Value
- sv reflect.Value
- )
switch v.Kind() {
case reflect.Map:
- // map must have string type
+ // map must have string kind
t := v.Type()
- if t.Key() != reflect.TypeOf("") {
+ if t.Key().Kind() != reflect.String {
d.saveError(&UnmarshalTypeError{"object", v.Type()})
break
}
- mv = v
- if mv.IsNil() {
- mv.Set(reflect.MakeMap(t))
+ if v.IsNil() {
+ v.Set(reflect.MakeMap(t))
}
case reflect.Struct:
- sv = v
+
default:
d.saveError(&UnmarshalTypeError{"object", v.Type()})
- }
-
- if !mv.IsValid() && !sv.IsValid() {
d.off--
d.next() // skip over { } in input
return
@@ -482,8 +488,8 @@ func (d *decodeState) object(v reflect.Value) {
var subv reflect.Value
destring := false // whether the value is wrapped in a string to be decoded first
- if mv.IsValid() {
- elemType := mv.Type().Elem()
+ if v.Kind() == reflect.Map {
+ elemType := v.Type().Elem()
if !mapElem.IsValid() {
mapElem = reflect.New(elemType).Elem()
} else {
@@ -491,51 +497,30 @@ func (d *decodeState) object(v reflect.Value) {
}
subv = mapElem
} else {
- var f reflect.StructField
- var ok bool
- st := sv.Type()
- for i := 0; i < sv.NumField(); i++ {
- sf := st.Field(i)
- tag := sf.Tag.Get("json")
- if tag == "-" {
- // Pretend this field doesn't exist.
- continue
- }
- if sf.Anonymous {
- // Pretend this field doesn't exist,
- // so that we can do a good job with
- // these in a later version.
- continue
- }
- // First, tag match
- tagName, _ := parseTag(tag)
- if tagName == key {
- f = sf
- ok = true
- break // no better match possible
- }
- // Second, exact field name match
- if sf.Name == key {
- f = sf
- ok = true
+ var f *field
+ fields := cachedTypeFields(v.Type())
+ for i := range fields {
+ ff := &fields[i]
+ if ff.name == key {
+ f = ff
+ break
}
- // Third, case-insensitive field name match,
- // but only if a better match hasn't already been seen
- if !ok && strings.EqualFold(sf.Name, key) {
- f = sf
- ok = true
+ if f == nil && strings.EqualFold(ff.name, key) {
+ f = ff
}
}
-
- // Extract value; name must be exported.
- if ok {
- if f.PkgPath != "" {
- d.saveError(&UnmarshalFieldError{key, st, f})
- } else {
- subv = sv.FieldByIndex(f.Index)
+ if f != nil {
+ subv = v
+ destring = f.quoted
+ for _, i := range f.index {
+ if subv.Kind() == reflect.Ptr {
+ if subv.IsNil() {
+ subv.Set(reflect.New(subv.Type().Elem()))
+ }
+ subv = subv.Elem()
+ }
+ subv = subv.Field(i)
}
- _, opts := parseTag(f.Tag.Get("json"))
- destring = opts.Contains("string")
}
}
@@ -554,10 +539,12 @@ func (d *decodeState) object(v reflect.Value) {
} else {
d.value(subv)
}
+
// Write value back to map;
// if using struct, subv points into struct already.
- if mv.IsValid() {
- mv.SetMapIndex(reflect.ValueOf(key), subv)
+ if v.Kind() == reflect.Map {
+ kv := reflect.ValueOf(key).Convert(v.Type().Key())
+ v.SetMapIndex(kv, subv)
}
// Next token must be , or }.
@@ -586,6 +573,21 @@ func (d *decodeState) literal(v reflect.Value) {
d.literalStore(d.data[start:d.off], v, false)
}
+// convertNumber converts the number literal s to a float64 or a Number
+// depending on the setting of d.useNumber.
+func (d *decodeState) convertNumber(s string) (interface{}, error) {
+ if d.useNumber {
+ return Number(s), nil
+ }
+ f, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0)}
+ }
+ return f, nil
+}
+
+var numberType = reflect.TypeOf(Number(""))
+
// literalStore decodes a literal stored in item into v.
//
// fromQuoted indicates whether this literal came from unwrapping a
@@ -612,12 +614,10 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
switch c := item[0]; c {
case 'n': // null
switch v.Kind() {
- default:
- d.saveError(&UnmarshalTypeError{"null", v.Type()})
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
v.Set(reflect.Zero(v.Type()))
+ // otherwise, ignore null for primitives/string
}
-
case 't', 'f': // true, false
value := c == 't'
switch v.Kind() {
@@ -630,7 +630,11 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
case reflect.Bool:
v.SetBool(value)
case reflect.Interface:
- v.Set(reflect.ValueOf(value))
+ if v.NumMethod() == 0 {
+ v.Set(reflect.ValueOf(value))
+ } else {
+ d.saveError(&UnmarshalTypeError{"bool", v.Type()})
+ }
}
case '"': // string
@@ -660,7 +664,11 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
case reflect.String:
v.SetString(string(s))
case reflect.Interface:
- v.Set(reflect.ValueOf(string(s)))
+ if v.NumMethod() == 0 {
+ v.Set(reflect.ValueOf(string(s)))
+ } else {
+ d.saveError(&UnmarshalTypeError{"string", v.Type()})
+ }
}
default: // number
@@ -674,15 +682,23 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
s := string(item)
switch v.Kind() {
default:
+ if v.Kind() == reflect.String && v.Type() == numberType {
+ v.SetString(s)
+ break
+ }
if fromQuoted {
d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
} else {
d.error(&UnmarshalTypeError{"number", v.Type()})
}
case reflect.Interface:
- n, err := strconv.ParseFloat(s, 64)
+ n, err := d.convertNumber(s)
if err != nil {
- d.saveError(&UnmarshalTypeError{"number " + s, v.Type()})
+ d.saveError(err)
+ break
+ }
+ if v.NumMethod() != 0 {
+ d.saveError(&UnmarshalTypeError{"number", v.Type()})
break
}
v.Set(reflect.ValueOf(n))
@@ -735,7 +751,7 @@ func (d *decodeState) valueInterface() interface{} {
// arrayInterface is like array but returns []interface{}.
func (d *decodeState) arrayInterface() []interface{} {
- var v []interface{}
+ var v = make([]interface{}, 0)
for {
// Look ahead for ] - can only happen on first iteration.
op := d.scanWhile(scanSkipSpace)
@@ -836,9 +852,9 @@ func (d *decodeState) literalInterface() interface{} {
if c != '-' && (c < '0' || c > '9') {
d.error(errPhase)
}
- n, err := strconv.ParseFloat(string(item), 64)
+ n, err := d.convertNumber(string(item))
if err != nil {
- d.saveError(&UnmarshalTypeError{"number " + string(item), reflect.TypeOf(0.0)})
+ d.saveError(err)
}
return n
}
@@ -979,11 +995,3 @@ func unquoteBytes(s []byte) (t []byte, ok bool) {
}
return b[0:w], true
}
-
-// The following is issue 3069.
-
-// BUG(rsc): This package ignores anonymous (embedded) struct fields
-// during encoding and decoding. A future version may assign meaning
-// to them. To force an anonymous field to be ignored in all future
-// versions of this package, use an explicit `json:"-"` tag in the struct
-// definition.
diff --git a/src/pkg/encoding/json/decode_test.go b/src/pkg/encoding/json/decode_test.go
index 6fac22c4a..e1bd918dd 100644
--- a/src/pkg/encoding/json/decode_test.go
+++ b/src/pkg/encoding/json/decode_test.go
@@ -7,9 +7,11 @@ package json
import (
"bytes"
"fmt"
+ "image"
"reflect"
"strings"
"testing"
+ "time"
)
type T struct {
@@ -18,6 +20,32 @@ type T struct {
Z int `json:"-"`
}
+type U struct {
+ Alphabet string `json:"alpha"`
+}
+
+type V struct {
+ F1 interface{}
+ F2 int32
+ F3 Number
+}
+
+// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshalling with and
+// without UseNumber
+var ifaceNumAsFloat64 = map[string]interface{}{
+ "k1": float64(1),
+ "k2": "s",
+ "k3": []interface{}{float64(1), float64(2.0), float64(3e-3)},
+ "k4": map[string]interface{}{"kk1": "s", "kk2": float64(2)},
+}
+
+var ifaceNumAsNumber = map[string]interface{}{
+ "k1": Number("1"),
+ "k2": "s",
+ "k3": []interface{}{Number("1"), Number("2.0"), Number("3e-3")},
+ "k4": map[string]interface{}{"kk1": "s", "kk2": Number("2")},
+}
+
type tx struct {
x int
}
@@ -48,55 +76,297 @@ var (
umstruct = ustruct{unmarshaler{true}}
)
+// Test data structures for anonymous fields.
+
+type Point struct {
+ Z int
+}
+
+type Top struct {
+ Level0 int
+ Embed0
+ *Embed0a
+ *Embed0b `json:"e,omitempty"` // treated as named
+ Embed0c `json:"-"` // ignored
+ Loop
+ Embed0p // has Point with X, Y, used
+ Embed0q // has Point with Z, used
+}
+
+type Embed0 struct {
+ Level1a int // overridden by Embed0a's Level1a with json tag
+ Level1b int // used because Embed0a's Level1b is renamed
+ Level1c int // used because Embed0a's Level1c is ignored
+ Level1d int // annihilated by Embed0a's Level1d
+ Level1e int `json:"x"` // annihilated by Embed0a.Level1e
+}
+
+type Embed0a struct {
+ Level1a int `json:"Level1a,omitempty"`
+ Level1b int `json:"LEVEL1B,omitempty"`
+ Level1c int `json:"-"`
+ Level1d int // annihilated by Embed0's Level1d
+ Level1f int `json:"x"` // annihilated by Embed0's Level1e
+}
+
+type Embed0b Embed0
+
+type Embed0c Embed0
+
+type Embed0p struct {
+ image.Point
+}
+
+type Embed0q struct {
+ Point
+}
+
+type Loop struct {
+ Loop1 int `json:",omitempty"`
+ Loop2 int `json:",omitempty"`
+ *Loop
+}
+
+// From reflect test:
+// The X in S6 and S7 annihilate, but they also block the X in S8.S9.
+type S5 struct {
+ S6
+ S7
+ S8
+}
+
+type S6 struct {
+ X int
+}
+
+type S7 S6
+
+type S8 struct {
+ S9
+}
+
+type S9 struct {
+ X int
+ Y int
+}
+
+// From reflect test:
+// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9.
+type S10 struct {
+ S11
+ S12
+ S13
+}
+
+type S11 struct {
+ S6
+}
+
+type S12 struct {
+ S6
+}
+
+type S13 struct {
+ S8
+}
+
type unmarshalTest struct {
- in string
- ptr interface{}
- out interface{}
- err error
+ in string
+ ptr interface{}
+ out interface{}
+ err error
+ useNumber bool
+}
+
+type Ambig struct {
+ // Given "hello", the first match should win.
+ First int `json:"HELLO"`
+ Second int `json:"Hello"`
}
var unmarshalTests = []unmarshalTest{
// basic types
- {`true`, new(bool), true, nil},
- {`1`, new(int), 1, nil},
- {`1.2`, new(float64), 1.2, nil},
- {`-5`, new(int16), int16(-5), nil},
- {`"a\u1234"`, new(string), "a\u1234", nil},
- {`"http:\/\/"`, new(string), "http://", nil},
- {`"g-clef: \uD834\uDD1E"`, new(string), "g-clef: \U0001D11E", nil},
- {`"invalid: \uD834x\uDD1E"`, new(string), "invalid: \uFFFDx\uFFFD", nil},
- {"null", new(interface{}), nil, nil},
- {`{"X": [1,2,3], "Y": 4}`, new(T), T{Y: 4}, &UnmarshalTypeError{"array", reflect.TypeOf("")}},
- {`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}},
+ {in: `true`, ptr: new(bool), out: true},
+ {in: `1`, ptr: new(int), out: 1},
+ {in: `1.2`, ptr: new(float64), out: 1.2},
+ {in: `-5`, ptr: new(int16), out: int16(-5)},
+ {in: `2`, ptr: new(Number), out: Number("2"), useNumber: true},
+ {in: `2`, ptr: new(Number), out: Number("2")},
+ {in: `2`, ptr: new(interface{}), out: float64(2.0)},
+ {in: `2`, ptr: new(interface{}), out: Number("2"), useNumber: true},
+ {in: `"a\u1234"`, ptr: new(string), out: "a\u1234"},
+ {in: `"http:\/\/"`, ptr: new(string), out: "http://"},
+ {in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"},
+ {in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},
+ {in: "null", ptr: new(interface{}), out: nil},
+ {in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf("")}},
+ {in: `{"x": 1}`, ptr: new(tx), out: tx{}},
+ {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
+ {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
+ {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsFloat64},
+ {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsNumber, useNumber: true},
+
+ // raw values with whitespace
+ {in: "\n true ", ptr: new(bool), out: true},
+ {in: "\t 1 ", ptr: new(int), out: 1},
+ {in: "\r 1.2 ", ptr: new(float64), out: 1.2},
+ {in: "\t -5 \n", ptr: new(int16), out: int16(-5)},
+ {in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"},
// Z has a "-" tag.
- {`{"Y": 1, "Z": 2}`, new(T), T{Y: 1}, nil},
+ {in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}},
+
+ {in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}},
+ {in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}},
+ {in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}},
// syntax errors
- {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}},
- {`[1, 2, 3+]`, nil, nil, &SyntaxError{"invalid character '+' after array element", 9}},
+ {in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", 17}},
+ {in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}},
+ {in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true},
+
+ // raw value errors
+ {in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
+ {in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 5}},
+ {in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
+ {in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 8}},
+ {in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
+ {in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 6}},
+ {in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
+ {in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 11}},
// array tests
- {`[1, 2, 3]`, new([3]int), [3]int{1, 2, 3}, nil},
- {`[1, 2, 3]`, new([1]int), [1]int{1}, nil},
- {`[1, 2, 3]`, new([5]int), [5]int{1, 2, 3, 0, 0}, nil},
+ {in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}},
+ {in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}},
+ {in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}},
+
+ // empty array to interface test
+ {in: `[]`, ptr: new([]interface{}), out: []interface{}{}},
+ {in: `null`, ptr: new([]interface{}), out: []interface{}(nil)},
+ {in: `{"T":[]}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": []interface{}{}}},
+ {in: `{"T":null}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": interface{}(nil)}},
// composite tests
- {allValueIndent, new(All), allValue, nil},
- {allValueCompact, new(All), allValue, nil},
- {allValueIndent, new(*All), &allValue, nil},
- {allValueCompact, new(*All), &allValue, nil},
- {pallValueIndent, new(All), pallValue, nil},
- {pallValueCompact, new(All), pallValue, nil},
- {pallValueIndent, new(*All), &pallValue, nil},
- {pallValueCompact, new(*All), &pallValue, nil},
+ {in: allValueIndent, ptr: new(All), out: allValue},
+ {in: allValueCompact, ptr: new(All), out: allValue},
+ {in: allValueIndent, ptr: new(*All), out: &allValue},
+ {in: allValueCompact, ptr: new(*All), out: &allValue},
+ {in: pallValueIndent, ptr: new(All), out: pallValue},
+ {in: pallValueCompact, ptr: new(All), out: pallValue},
+ {in: pallValueIndent, ptr: new(*All), out: &pallValue},
+ {in: pallValueCompact, ptr: new(*All), out: &pallValue},
// unmarshal interface test
- {`{"T":false}`, &um0, umtrue, nil}, // use "false" so test will fail if custom unmarshaler is not called
- {`{"T":false}`, &ump, &umtrue, nil},
- {`[{"T":false}]`, &umslice, umslice, nil},
- {`[{"T":false}]`, &umslicep, &umslice, nil},
- {`{"M":{"T":false}}`, &umstruct, umstruct, nil},
+ {in: `{"T":false}`, ptr: &um0, out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called
+ {in: `{"T":false}`, ptr: &ump, out: &umtrue},
+ {in: `[{"T":false}]`, ptr: &umslice, out: umslice},
+ {in: `[{"T":false}]`, ptr: &umslicep, out: &umslice},
+ {in: `{"M":{"T":false}}`, ptr: &umstruct, out: umstruct},
+
+ {
+ in: `{
+ "Level0": 1,
+ "Level1b": 2,
+ "Level1c": 3,
+ "x": 4,
+ "Level1a": 5,
+ "LEVEL1B": 6,
+ "e": {
+ "Level1a": 8,
+ "Level1b": 9,
+ "Level1c": 10,
+ "Level1d": 11,
+ "x": 12
+ },
+ "Loop1": 13,
+ "Loop2": 14,
+ "X": 15,
+ "Y": 16,
+ "Z": 17
+ }`,
+ ptr: new(Top),
+ out: Top{
+ Level0: 1,
+ Embed0: Embed0{
+ Level1b: 2,
+ Level1c: 3,
+ },
+ Embed0a: &Embed0a{
+ Level1a: 5,
+ Level1b: 6,
+ },
+ Embed0b: &Embed0b{
+ Level1a: 8,
+ Level1b: 9,
+ Level1c: 10,
+ Level1d: 11,
+ Level1e: 12,
+ },
+ Loop: Loop{
+ Loop1: 13,
+ Loop2: 14,
+ },
+ Embed0p: Embed0p{
+ Point: image.Point{X: 15, Y: 16},
+ },
+ Embed0q: Embed0q{
+ Point: Point{Z: 17},
+ },
+ },
+ },
+ {
+ in: `{"hello": 1}`,
+ ptr: new(Ambig),
+ out: Ambig{First: 1},
+ },
+
+ {
+ in: `{"X": 1,"Y":2}`,
+ ptr: new(S5),
+ out: S5{S8: S8{S9: S9{Y: 2}}},
+ },
+ {
+ in: `{"X": 1,"Y":2}`,
+ ptr: new(S10),
+ out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}},
+ },
+
+ // invalid UTF-8 is coerced to valid UTF-8.
+ {
+ in: "\"hello\xffworld\"",
+ ptr: new(string),
+ out: "hello\ufffdworld",
+ },
+ {
+ in: "\"hello\xc2\xc2world\"",
+ ptr: new(string),
+ out: "hello\ufffd\ufffdworld",
+ },
+ {
+ in: "\"hello\xc2\xffworld\"",
+ ptr: new(string),
+ out: "hello\ufffd\ufffdworld",
+ },
+ {
+ in: "\"hello\\ud800world\"",
+ ptr: new(string),
+ out: "hello\ufffdworld",
+ },
+ {
+ in: "\"hello\\ud800\\ud800world\"",
+ ptr: new(string),
+ out: "hello\ufffd\ufffdworld",
+ },
+ {
+ in: "\"hello\\ud800\\ud800world\"",
+ ptr: new(string),
+ out: "hello\ufffd\ufffdworld",
+ },
+ {
+ in: "\"hello\xed\xa0\x80\xed\xb0\x80world\"",
+ ptr: new(string),
+ out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld",
+ },
}
func TestMarshal(t *testing.T) {
@@ -135,6 +405,18 @@ func TestMarshalBadUTF8(t *testing.T) {
}
}
+func TestMarshalNumberZeroVal(t *testing.T) {
+ var n Number
+ out, err := Marshal(n)
+ if err != nil {
+ t.Fatal(err)
+ }
+ outStr := string(out)
+ if outStr != "0" {
+ t.Fatalf("Invalid zero val for Number: %q", outStr)
+ }
+}
+
func TestUnmarshal(t *testing.T) {
for i, tt := range unmarshalTests {
var scan scanner
@@ -150,7 +432,11 @@ func TestUnmarshal(t *testing.T) {
}
// v = new(right-type)
v := reflect.New(reflect.TypeOf(tt.ptr).Elem())
- if err := Unmarshal([]byte(in), v.Interface()); !reflect.DeepEqual(err, tt.err) {
+ dec := NewDecoder(bytes.NewBuffer(in))
+ if tt.useNumber {
+ dec.UseNumber()
+ }
+ if err := dec.Decode(v.Interface()); !reflect.DeepEqual(err, tt.err) {
t.Errorf("#%d: %v want %v", i, err, tt.err)
continue
}
@@ -162,6 +448,28 @@ func TestUnmarshal(t *testing.T) {
println(string(data))
continue
}
+
+ // Check round trip.
+ if tt.err == nil {
+ enc, err := Marshal(v.Interface())
+ if err != nil {
+ t.Errorf("#%d: error re-marshaling: %v", i, err)
+ continue
+ }
+ vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
+ dec = NewDecoder(bytes.NewBuffer(enc))
+ if tt.useNumber {
+ dec.UseNumber()
+ }
+ if err := dec.Decode(vv.Interface()); err != nil {
+ t.Errorf("#%d: error re-unmarshaling: %v", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) {
+ t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface())
+ continue
+ }
+ }
}
}
@@ -175,13 +483,45 @@ func TestUnmarshalMarshal(t *testing.T) {
if err != nil {
t.Fatalf("Marshal: %v", err)
}
- if bytes.Compare(jsonBig, b) != 0 {
+ if !bytes.Equal(jsonBig, b) {
t.Errorf("Marshal jsonBig")
diff(t, b, jsonBig)
return
}
}
+var numberTests = []struct {
+ in string
+ i int64
+ intErr string
+ f float64
+ floatErr string
+}{
+ {in: "-1.23e1", intErr: "strconv.ParseInt: parsing \"-1.23e1\": invalid syntax", f: -1.23e1},
+ {in: "-12", i: -12, f: -12.0},
+ {in: "1e1000", intErr: "strconv.ParseInt: parsing \"1e1000\": invalid syntax", floatErr: "strconv.ParseFloat: parsing \"1e1000\": value out of range"},
+}
+
+// Independent of Decode, basic coverage of the accessors in Number
+func TestNumberAccessors(t *testing.T) {
+ for _, tt := range numberTests {
+ n := Number(tt.in)
+ if s := n.String(); s != tt.in {
+ t.Errorf("Number(%q).String() is %q", tt.in, s)
+ }
+ if i, err := n.Int64(); err == nil && tt.intErr == "" && i != tt.i {
+ t.Errorf("Number(%q).Int64() is %d", tt.in, i)
+ } else if (err == nil && tt.intErr != "") || (err != nil && err.Error() != tt.intErr) {
+ t.Errorf("Number(%q).Int64() wanted error %q but got: %v", tt.in, tt.intErr, err)
+ }
+ if f, err := n.Float64(); err == nil && tt.floatErr == "" && f != tt.f {
+ t.Errorf("Number(%q).Float64() is %g", tt.in, f)
+ } else if (err == nil && tt.floatErr != "") || (err != nil && err.Error() != tt.floatErr) {
+ t.Errorf("Number(%q).Float64() wanted error %q but got: %v", tt.in, tt.floatErr, err)
+ }
+ }
+}
+
func TestLargeByteSlice(t *testing.T) {
s0 := make([]byte, 2000)
for i := range s0 {
@@ -195,7 +535,7 @@ func TestLargeByteSlice(t *testing.T) {
if err := Unmarshal(b, &s1); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
- if bytes.Compare(s0, s1) != 0 {
+ if !bytes.Equal(s0, s1) {
t.Errorf("Marshal large byte slice")
diff(t, s0, s1)
}
@@ -610,35 +950,6 @@ func TestRefUnmarshal(t *testing.T) {
}
}
-// Test that anonymous fields are ignored.
-// We may assign meaning to them later.
-func TestAnonymous(t *testing.T) {
- type S struct {
- T
- N int
- }
-
- data, err := Marshal(new(S))
- if err != nil {
- t.Fatalf("Marshal: %v", err)
- }
- want := `{"N":0}`
- if string(data) != want {
- t.Fatalf("Marshal = %#q, want %#q", string(data), want)
- }
-
- var s S
- if err := Unmarshal([]byte(`{"T": 1, "T": {"Y": 1}, "N": 2}`), &s); err != nil {
- t.Fatalf("Unmarshal: %v", err)
- }
- if s.N != 2 {
- t.Fatal("Unmarshal: did not set N")
- }
- if s.T.Y != 0 {
- t.Fatal("Unmarshal: did set T.Y")
- }
-}
-
// Test that the empty string doesn't panic decoding when ,string is specified
// Issue 3450
func TestEmptyString(t *testing.T) {
@@ -703,3 +1014,167 @@ func TestInterfaceSet(t *testing.T) {
}
}
}
+
+// JSON null values should be ignored for primitives and string values instead of resulting in an error.
+// Issue 2540
+func TestUnmarshalNulls(t *testing.T) {
+ jsonData := []byte(`{
+ "Bool" : null,
+ "Int" : null,
+ "Int8" : null,
+ "Int16" : null,
+ "Int32" : null,
+ "Int64" : null,
+ "Uint" : null,
+ "Uint8" : null,
+ "Uint16" : null,
+ "Uint32" : null,
+ "Uint64" : null,
+ "Float32" : null,
+ "Float64" : null,
+ "String" : null}`)
+
+ nulls := All{
+ Bool: true,
+ Int: 2,
+ Int8: 3,
+ Int16: 4,
+ Int32: 5,
+ Int64: 6,
+ Uint: 7,
+ Uint8: 8,
+ Uint16: 9,
+ Uint32: 10,
+ Uint64: 11,
+ Float32: 12.1,
+ Float64: 13.1,
+ String: "14"}
+
+ err := Unmarshal(jsonData, &nulls)
+ if err != nil {
+ t.Errorf("Unmarshal of null values failed: %v", err)
+ }
+ if !nulls.Bool || nulls.Int != 2 || nulls.Int8 != 3 || nulls.Int16 != 4 || nulls.Int32 != 5 || nulls.Int64 != 6 ||
+ nulls.Uint != 7 || nulls.Uint8 != 8 || nulls.Uint16 != 9 || nulls.Uint32 != 10 || nulls.Uint64 != 11 ||
+ nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" {
+
+ t.Errorf("Unmarshal of null values affected primitives")
+ }
+}
+
+func TestStringKind(t *testing.T) {
+ type stringKind string
+ type aMap map[stringKind]int
+
+ var m1, m2 map[stringKind]int
+ m1 = map[stringKind]int{
+ "foo": 42,
+ }
+
+ data, err := Marshal(m1)
+ if err != nil {
+ t.Errorf("Unexpected error marshalling: %v", err)
+ }
+
+ err = Unmarshal(data, &m2)
+ if err != nil {
+ t.Errorf("Unexpected error unmarshalling: %v", err)
+ }
+
+ if !reflect.DeepEqual(m1, m2) {
+ t.Error("Items should be equal after encoding and then decoding")
+ }
+
+}
+
+var decodeTypeErrorTests = []struct {
+ dest interface{}
+ src string
+}{
+ {new(string), `{"user": "name"}`}, // issue 4628.
+ {new(error), `{}`}, // issue 4222
+ {new(error), `[]`},
+ {new(error), `""`},
+ {new(error), `123`},
+ {new(error), `true`},
+}
+
+func TestUnmarshalTypeError(t *testing.T) {
+ for _, item := range decodeTypeErrorTests {
+ err := Unmarshal([]byte(item.src), item.dest)
+ if _, ok := err.(*UnmarshalTypeError); !ok {
+ t.Errorf("expected type error for Unmarshal(%q, type %T): got %T",
+ item.src, item.dest, err)
+ }
+ }
+}
+
+var unmarshalSyntaxTests = []string{
+ "tru",
+ "fals",
+ "nul",
+ "123e",
+ `"hello`,
+ `[1,2,3`,
+ `{"key":1`,
+ `{"key":1,`,
+}
+
+func TestUnmarshalSyntax(t *testing.T) {
+ var x interface{}
+ for _, src := range unmarshalSyntaxTests {
+ err := Unmarshal([]byte(src), &x)
+ if _, ok := err.(*SyntaxError); !ok {
+ t.Errorf("expected syntax error for Unmarshal(%q): got %T", src, err)
+ }
+ }
+}
+
+// Test handling of unexported fields that should be ignored.
+// Issue 4660
+type unexportedFields struct {
+ Name string
+ m map[string]interface{} `json:"-"`
+ m2 map[string]interface{} `json:"abcd"`
+}
+
+func TestUnmarshalUnexported(t *testing.T) {
+ input := `{"Name": "Bob", "m": {"x": 123}, "m2": {"y": 456}, "abcd": {"z": 789}}`
+ want := &unexportedFields{Name: "Bob"}
+
+ out := &unexportedFields{}
+ err := Unmarshal([]byte(input), out)
+ if err != nil {
+ t.Errorf("got error %v, expected nil", err)
+ }
+ if !reflect.DeepEqual(out, want) {
+ t.Errorf("got %q, want %q", out, want)
+ }
+}
+
+// Time3339 is a time.Time which encodes to and from JSON
+// as an RFC 3339 time in UTC.
+type Time3339 time.Time
+
+func (t *Time3339) UnmarshalJSON(b []byte) error {
+ if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
+ return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time", b)
+ }
+ tm, err := time.Parse(time.RFC3339, string(b[1:len(b)-1]))
+ if err != nil {
+ return err
+ }
+ *t = Time3339(tm)
+ return nil
+}
+
+func TestUnmarshalJSONLiteralError(t *testing.T) {
+ var t3 Time3339
+ err := Unmarshal([]byte(`"0000-00-00T00:00:00Z"`), &t3)
+ if err == nil {
+ t.Fatalf("expected error; got time %v", time.Time(t3))
+ }
+ if !strings.Contains(err.Error(), "range") {
+ t.Errorf("got err = %v; want out of range error", err)
+ }
+}
diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go
index b6e1cb16e..fb57f1d51 100644
--- a/src/pkg/encoding/json/encode.go
+++ b/src/pkg/encoding/json/encode.go
@@ -36,7 +36,7 @@ import (
//
// Boolean values encode as JSON booleans.
//
-// Floating point and integer values encode as JSON numbers.
+// Floating point, integer, and Number values encode as JSON numbers.
//
// String values encode as JSON strings, with each invalid UTF-8 sequence
// replaced by the encoding of the Unicode replacement character U+FFFD.
@@ -55,7 +55,7 @@ import (
// nil pointer or interface value, and any array, slice, map, or string of
// length zero. The object's default key string is the struct field name
// but can be specified in the struct field's tag value. The "json" key in
-// struct field's tag value is the key name, followed by an optional comma
+// the struct field's tag value is the key name, followed by an optional comma
// and options. Examples:
//
// // Field is ignored by this package.
@@ -75,8 +75,9 @@ import (
// Field int `json:",omitempty"`
//
// The "string" option signals that a field is stored as JSON inside a
-// JSON-encoded string. This extra level of encoding is sometimes
-// used when communicating with JavaScript programs:
+// JSON-encoded string. It applies only to fields of string, floating point,
+// or integer types. This extra level of encoding is sometimes used when
+// communicating with JavaScript programs:
//
// Int64String int64 `json:",string"`
//
@@ -84,6 +85,16 @@ import (
// only Unicode letters, digits, dollar signs, percent signs, hyphens,
// underscores and slashes.
//
+// Anonymous struct fields are usually marshaled as if their inner exported fields
+// were fields in the outer struct, subject to the usual Go visibility rules.
+// An anonymous struct field with a name given in its JSON tag is treated as
+// having that name instead of as anonymous.
+//
+// Handling of anonymous struct fields is new in Go 1.1.
+// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of
+// an anonymous struct field in both current and earlier versions, give the field
+// a JSON tag of "-".
+//
// Map values encode as JSON objects.
// The map's key type must be string; the object keys are used directly
// as map keys.
@@ -312,6 +323,14 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
e.Write(b)
}
case reflect.String:
+ if v.Type() == numberType {
+ numStr := v.String()
+ if numStr == "" {
+ numStr = "0" // Number's zero-val
+ }
+ e.WriteString(numStr)
+ break
+ }
if quoted {
sb, err := Marshal(v.String())
if err != nil {
@@ -325,9 +344,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
case reflect.Struct:
e.WriteByte('{')
first := true
- for _, ef := range encodeFields(v.Type()) {
- fieldValue := v.Field(ef.i)
- if ef.omitEmpty && isEmptyValue(fieldValue) {
+ for _, f := range cachedTypeFields(v.Type()) {
+ fv := fieldByIndex(v, f.index)
+ if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
continue
}
if first {
@@ -335,9 +354,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
} else {
e.WriteByte(',')
}
- e.string(ef.tag)
+ e.string(f.name)
e.WriteByte(':')
- e.reflectValueQuoted(fieldValue, ef.quoted)
+ e.reflectValueQuoted(fv, f.quoted)
}
e.WriteByte('}')
@@ -419,7 +438,7 @@ func isValidTag(s string) bool {
}
for _, c := range s {
switch {
- case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~", c):
+ case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
// Backslash and quote chars are reserved, but
// otherwise any punctuation chars are allowed
// in a tag name.
@@ -432,6 +451,19 @@ func isValidTag(s string) bool {
return true
}
+func fieldByIndex(v reflect.Value, index []int) reflect.Value {
+ for _, i := range index {
+ if v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ return reflect.Value{}
+ }
+ v = v.Elem()
+ }
+ v = v.Field(i)
+ }
+ return v
+}
+
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
// It implements the methods to sort by string.
type stringValues []reflect.Value
@@ -490,67 +522,185 @@ func (e *encodeState) string(s string) (int, error) {
return e.Len() - len0, nil
}
-// encodeField contains information about how to encode a field of a
-// struct.
-type encodeField struct {
- i int // field index in struct
- tag string
- quoted bool
+// A field represents a single field found in a struct.
+type field struct {
+ name string
+ tag bool
+ index []int
+ typ reflect.Type
omitEmpty bool
+ quoted bool
}
-var (
- typeCacheLock sync.RWMutex
- encodeFieldsCache = make(map[reflect.Type][]encodeField)
-)
+// byName sorts field by name, breaking ties with depth,
+// then breaking ties with "name came from json tag", then
+// breaking ties with index sequence.
+type byName []field
-// encodeFields returns a slice of encodeField for a given
-// struct type.
-func encodeFields(t reflect.Type) []encodeField {
- typeCacheLock.RLock()
- fs, ok := encodeFieldsCache[t]
- typeCacheLock.RUnlock()
- if ok {
- return fs
- }
+func (x byName) Len() int { return len(x) }
+
+func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
- typeCacheLock.Lock()
- defer typeCacheLock.Unlock()
- fs, ok = encodeFieldsCache[t]
- if ok {
- return fs
+func (x byName) Less(i, j int) bool {
+ if x[i].name != x[j].name {
+ return x[i].name < x[j].name
}
+ if len(x[i].index) != len(x[j].index) {
+ return len(x[i].index) < len(x[j].index)
+ }
+ if x[i].tag != x[j].tag {
+ return x[i].tag
+ }
+ return byIndex(x).Less(i, j)
+}
- v := reflect.Zero(t)
- n := v.NumField()
- for i := 0; i < n; i++ {
- f := t.Field(i)
- if f.PkgPath != "" {
- continue
+// byIndex sorts field by index sequence.
+type byIndex []field
+
+func (x byIndex) Len() int { return len(x) }
+
+func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+func (x byIndex) Less(i, j int) bool {
+ for k, xik := range x[i].index {
+ if k >= len(x[j].index) {
+ return false
}
- if f.Anonymous {
- // We want to do a better job with these later,
- // so for now pretend they don't exist.
- continue
+ if xik != x[j].index[k] {
+ return xik < x[j].index[k]
}
- var ef encodeField
- ef.i = i
- ef.tag = f.Name
+ }
+ return len(x[i].index) < len(x[j].index)
+}
+
+// typeFields returns a list of fields that JSON should recognize for the given type.
+// The algorithm is breadth-first search over the set of structs to include - the top struct
+// and then any reachable anonymous structs.
+func typeFields(t reflect.Type) []field {
+ // Anonymous fields to explore at the current level and the next.
+ current := []field{}
+ next := []field{{typ: t}}
+
+ // Count of queued names for current level and the next.
+ count := map[reflect.Type]int{}
+ nextCount := map[reflect.Type]int{}
+
+ // Types already visited at an earlier level.
+ visited := map[reflect.Type]bool{}
+
+ // Fields found.
+ var fields []field
+
+ for len(next) > 0 {
+ current, next = next, current[:0]
+ count, nextCount = nextCount, map[reflect.Type]int{}
- tv := f.Tag.Get("json")
- if tv != "" {
- if tv == "-" {
+ for _, f := range current {
+ if visited[f.typ] {
continue
}
- name, opts := parseTag(tv)
- if isValidTag(name) {
- ef.tag = name
+ visited[f.typ] = true
+
+ // Scan f.typ for fields to include.
+ for i := 0; i < f.typ.NumField(); i++ {
+ sf := f.typ.Field(i)
+ if sf.PkgPath != "" { // unexported
+ continue
+ }
+ tag := sf.Tag.Get("json")
+ if tag == "-" {
+ continue
+ }
+ name, opts := parseTag(tag)
+ if !isValidTag(name) {
+ name = ""
+ }
+ index := make([]int, len(f.index)+1)
+ copy(index, f.index)
+ index[len(f.index)] = i
+
+ ft := sf.Type
+ if ft.Name() == "" && ft.Kind() == reflect.Ptr {
+ // Follow pointer.
+ ft = ft.Elem()
+ }
+
+ // Record found field and index sequence.
+ if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
+ tagged := name != ""
+ if name == "" {
+ name = sf.Name
+ }
+ fields = append(fields, field{name, tagged, index, ft,
+ opts.Contains("omitempty"), opts.Contains("string")})
+ if count[f.typ] > 1 {
+ // If there were multiple instances, add a second,
+ // so that the annihilation code will see a duplicate.
+ // It only cares about the distinction between 1 or 2,
+ // so don't bother generating any more copies.
+ fields = append(fields, fields[len(fields)-1])
+ }
+ continue
+ }
+
+ // Record new anonymous struct to explore in next round.
+ nextCount[ft]++
+ if nextCount[ft] == 1 {
+ next = append(next, field{name: ft.Name(), index: index, typ: ft})
+ }
}
- ef.omitEmpty = opts.Contains("omitempty")
- ef.quoted = opts.Contains("string")
}
- fs = append(fs, ef)
}
- encodeFieldsCache[t] = fs
- return fs
+
+ sort.Sort(byName(fields))
+
+ // Remove fields with annihilating name collisions
+ // and also fields shadowed by fields with explicit JSON tags.
+ name := ""
+ out := fields[:0]
+ for _, f := range fields {
+ if f.name != name {
+ name = f.name
+ out = append(out, f)
+ continue
+ }
+ if n := len(out); n > 0 && out[n-1].name == name && (!out[n-1].tag || f.tag) {
+ out = out[:n-1]
+ }
+ }
+ fields = out
+
+ sort.Sort(byIndex(fields))
+
+ return fields
+}
+
+var fieldCache struct {
+ sync.RWMutex
+ m map[reflect.Type][]field
+}
+
+// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
+func cachedTypeFields(t reflect.Type) []field {
+ fieldCache.RLock()
+ f := fieldCache.m[t]
+ fieldCache.RUnlock()
+ if f != nil {
+ return f
+ }
+
+ // Compute fields without lock.
+ // Might duplicate effort but won't hold other computations back.
+ f = typeFields(t)
+ if f == nil {
+ f = []field{}
+ }
+
+ fieldCache.Lock()
+ if fieldCache.m == nil {
+ fieldCache.m = map[reflect.Type][]field{}
+ }
+ fieldCache.m[t] = f
+ fieldCache.Unlock()
+ return f
}
diff --git a/src/pkg/encoding/json/encode_test.go b/src/pkg/encoding/json/encode_test.go
index cb1c77eb5..be74c997c 100644
--- a/src/pkg/encoding/json/encode_test.go
+++ b/src/pkg/encoding/json/encode_test.go
@@ -186,3 +186,23 @@ func TestMarshalerEscaping(t *testing.T) {
t.Errorf("got %q, want %q", got, want)
}
}
+
+type IntType int
+
+type MyStruct struct {
+ IntType
+}
+
+func TestAnonymousNonstruct(t *testing.T) {
+ var i IntType = 11
+ a := MyStruct{i}
+ const want = `{"IntType":11}`
+
+ b, err := Marshal(a)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ if got := string(b); got != want {
+ t.Errorf("got %q, want %q", got, want)
+ }
+}
diff --git a/src/pkg/encoding/json/scanner_test.go b/src/pkg/encoding/json/scanner_test.go
index 14d850865..77d3455d3 100644
--- a/src/pkg/encoding/json/scanner_test.go
+++ b/src/pkg/encoding/json/scanner_test.go
@@ -92,7 +92,7 @@ func TestCompactBig(t *testing.T) {
t.Fatalf("Compact: %v", err)
}
b := buf.Bytes()
- if bytes.Compare(b, jsonBig) != 0 {
+ if !bytes.Equal(b, jsonBig) {
t.Error("Compact(jsonBig) != jsonBig")
diff(t, b, jsonBig)
return
@@ -118,7 +118,7 @@ func TestIndentBig(t *testing.T) {
t.Fatalf("Indent2: %v", err)
}
b1 := buf1.Bytes()
- if bytes.Compare(b1, b) != 0 {
+ if !bytes.Equal(b1, b) {
t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)")
diff(t, b1, b)
return
@@ -130,7 +130,7 @@ func TestIndentBig(t *testing.T) {
t.Fatalf("Compact: %v", err)
}
b1 = buf1.Bytes()
- if bytes.Compare(b1, jsonBig) != 0 {
+ if !bytes.Equal(b1, jsonBig) {
t.Error("Compact(Indent(jsonBig)) != jsonBig")
diff(t, b1, jsonBig)
return
@@ -277,9 +277,6 @@ func genArray(n int) []interface{} {
if f > n {
f = n
}
- if n > 0 && f == 0 {
- f = 1
- }
x := make([]interface{}, f)
for i := range x {
x[i] = genValue(((i+1)*n)/f - (i*n)/f)
diff --git a/src/pkg/encoding/json/stream.go b/src/pkg/encoding/json/stream.go
index 7d1cc5f11..00f4726cf 100644
--- a/src/pkg/encoding/json/stream.go
+++ b/src/pkg/encoding/json/stream.go
@@ -5,6 +5,7 @@
package json
import (
+ "bytes"
"errors"
"io"
)
@@ -26,6 +27,10 @@ func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r}
}
+// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
+// Number instead of as a float64.
+func (dec *Decoder) UseNumber() { dec.d.useNumber = true }
+
// Decode reads the next JSON-encoded value from its
// input and stores it in the value pointed to by v.
//
@@ -54,6 +59,12 @@ func (dec *Decoder) Decode(v interface{}) error {
return err
}
+// Buffered returns a reader of the data remaining in the Decoder's
+// buffer. The reader is valid until the next call to Decode.
+func (dec *Decoder) Buffered() io.Reader {
+ return bytes.NewReader(dec.buf)
+}
+
// readValue reads a JSON value into dec.buf.
// It returns the length of the encoding.
func (dec *Decoder) readValue() (int, error) {
@@ -74,7 +85,7 @@ Input:
// scanEnd is delayed one byte.
// We might block trying to get that byte from src,
// so instead invent a space byte.
- if v == scanEndObject && dec.scan.step(&dec.scan, ' ') == scanEnd {
+ if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd {
scanp += i + 1
break Input
}
diff --git a/src/pkg/encoding/json/stream_test.go b/src/pkg/encoding/json/stream_test.go
index ce5a7e6d6..07c9e1d39 100644
--- a/src/pkg/encoding/json/stream_test.go
+++ b/src/pkg/encoding/json/stream_test.go
@@ -6,7 +6,10 @@ package json
import (
"bytes"
+ "io/ioutil"
+ "net"
"reflect"
+ "strings"
"testing"
)
@@ -82,6 +85,28 @@ func TestDecoder(t *testing.T) {
}
}
+func TestDecoderBuffered(t *testing.T) {
+ r := strings.NewReader(`{"Name": "Gopher"} extra `)
+ var m struct {
+ Name string
+ }
+ d := NewDecoder(r)
+ err := d.Decode(&m)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if m.Name != "Gopher" {
+ t.Errorf("Name = %q; want Gopher", m.Name)
+ }
+ rest, err := ioutil.ReadAll(d.Buffered())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if g, w := string(rest), " extra "; g != w {
+ t.Errorf("Remaining = %q; want %q", g, w)
+ }
+}
+
func nlines(s string, n int) string {
if n <= 0 {
return ""
@@ -145,3 +170,24 @@ func TestNullRawMessage(t *testing.T) {
t.Fatalf("Marshal: have %#q want %#q", b, msg)
}
}
+
+var blockingTests = []string{
+ `{"x": 1}`,
+ `[1, 2, 3]`,
+}
+
+func TestBlocking(t *testing.T) {
+ for _, enc := range blockingTests {
+ r, w := net.Pipe()
+ go w.Write([]byte(enc))
+ var val interface{}
+
+ // If Decode reads beyond what w.Write writes above,
+ // it will block, and the test will deadlock.
+ if err := NewDecoder(r).Decode(&val); err != nil {
+ t.Errorf("decoding %s: %v", enc, err)
+ }
+ r.Close()
+ w.Close()
+ }
+}
diff --git a/src/pkg/encoding/json/tagkey_test.go b/src/pkg/encoding/json/tagkey_test.go
index da8b12bd8..23e71c752 100644
--- a/src/pkg/encoding/json/tagkey_test.go
+++ b/src/pkg/encoding/json/tagkey_test.go
@@ -60,6 +60,14 @@ type badCodeTag struct {
Z string `json:" !\"#&'()*+,."`
}
+type spaceTag struct {
+ Q string `json:"With space"`
+}
+
+type unicodeTag struct {
+ W string `json:"Ελλάδα"`
+}
+
var structTagObjectKeyTests = []struct {
raw interface{}
value string
@@ -78,6 +86,8 @@ var structTagObjectKeyTests = []struct {
{badCodeTag{"Reliable Man"}, "Reliable Man", "Z"},
{percentSlashTag{"brut"}, "brut", "text/html%"},
{punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"},
+ {spaceTag{"Perreddu"}, "Perreddu", "With space"},
+ {unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"},
}
func TestStructTagObjectKey(t *testing.T) {
diff --git a/src/pkg/encoding/pem/pem.go b/src/pkg/encoding/pem/pem.go
index 3c1f5ab70..8ff7ee8c3 100644
--- a/src/pkg/encoding/pem/pem.go
+++ b/src/pkg/encoding/pem/pem.go
@@ -11,6 +11,7 @@ import (
"bytes"
"encoding/base64"
"io"
+ "sort"
)
// A Block represents a PEM encoded structure.
@@ -209,26 +210,46 @@ func (l *lineBreaker) Close() (err error) {
return
}
-func Encode(out io.Writer, b *Block) (err error) {
- _, err = out.Write(pemStart[1:])
- if err != nil {
- return
+func writeHeader(out io.Writer, k, v string) error {
+ _, err := out.Write([]byte(k + ": " + v + "\n"))
+ return err
+}
+
+func Encode(out io.Writer, b *Block) error {
+ if _, err := out.Write(pemStart[1:]); err != nil {
+ return err
}
- _, err = out.Write([]byte(b.Type + "-----\n"))
- if err != nil {
- return
+ if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil {
+ return err
}
if len(b.Headers) > 0 {
- for k, v := range b.Headers {
- _, err = out.Write([]byte(k + ": " + v + "\n"))
- if err != nil {
- return
+ const procType = "Proc-Type"
+ h := make([]string, 0, len(b.Headers))
+ hasProcType := false
+ for k := range b.Headers {
+ if k == procType {
+ hasProcType = true
+ continue
}
+ h = append(h, k)
}
- _, err = out.Write([]byte{'\n'})
- if err != nil {
- return
+ // The Proc-Type header must be written first.
+ // See RFC 1421, section 4.6.1.1
+ if hasProcType {
+ if err := writeHeader(out, procType, b.Headers[procType]); err != nil {
+ return err
+ }
+ }
+ // For consistency of output, write other headers sorted by key.
+ sort.Strings(h)
+ for _, k := range h {
+ if err := writeHeader(out, k, b.Headers[k]); err != nil {
+ return err
+ }
+ }
+ if _, err := out.Write([]byte{'\n'}); err != nil {
+ return err
}
}
@@ -236,19 +257,17 @@ func Encode(out io.Writer, b *Block) (err error) {
breaker.out = out
b64 := base64.NewEncoder(base64.StdEncoding, &breaker)
- _, err = b64.Write(b.Bytes)
- if err != nil {
- return
+ if _, err := b64.Write(b.Bytes); err != nil {
+ return err
}
b64.Close()
breaker.Close()
- _, err = out.Write(pemEnd[1:])
- if err != nil {
- return
+ if _, err := out.Write(pemEnd[1:]); err != nil {
+ return err
}
- _, err = out.Write([]byte(b.Type + "-----\n"))
- return
+ _, err := out.Write([]byte(b.Type + "-----\n"))
+ return err
}
func EncodeToMemory(b *Block) []byte {
diff --git a/src/pkg/encoding/pem/pem_test.go b/src/pkg/encoding/pem/pem_test.go
index 613353483..ccce42cf1 100644
--- a/src/pkg/encoding/pem/pem_test.go
+++ b/src/pkg/encoding/pem/pem_test.go
@@ -43,7 +43,7 @@ func TestDecode(t *testing.T) {
if !reflect.DeepEqual(result, privateKey) {
t.Errorf("#1 got:%#v want:%#v", result, privateKey)
}
- result, _ = Decode([]byte(pemPrivateKey))
+ result, _ = Decode([]byte(pemPrivateKey2))
if !reflect.DeepEqual(result, privateKey2) {
t.Errorf("#2 got:%#v want:%#v", result, privateKey2)
}
@@ -51,8 +51,8 @@ func TestDecode(t *testing.T) {
func TestEncode(t *testing.T) {
r := EncodeToMemory(privateKey2)
- if string(r) != pemPrivateKey {
- t.Errorf("got:%s want:%s", r, pemPrivateKey)
+ if string(r) != pemPrivateKey2 {
+ t.Errorf("got:%s want:%s", r, pemPrivateKey2)
}
}
@@ -341,50 +341,64 @@ var privateKey = &Block{Type: "RSA PRIVATE KEY",
},
}
-var privateKey2 = &Block{Type: "RSA PRIVATE KEY",
- Headers: map[string]string{},
- Bytes: []uint8{0x30, 0x82, 0x1, 0x3a, 0x2, 0x1, 0x0, 0x2,
- 0x41, 0x0, 0xb2, 0x99, 0xf, 0x49, 0xc4, 0x7d, 0xfa, 0x8c,
- 0xd4, 0x0, 0xae, 0x6a, 0x4d, 0x1b, 0x8a, 0x3b, 0x6a, 0x13,
- 0x64, 0x2b, 0x23, 0xf2, 0x8b, 0x0, 0x3b, 0xfb, 0x97, 0x79,
- 0xa, 0xde, 0x9a, 0x4c, 0xc8, 0x2b, 0x8b, 0x2a, 0x81, 0x74,
- 0x7d, 0xde, 0xc0, 0x8b, 0x62, 0x96, 0xe5, 0x3a, 0x8, 0xc3,
- 0x31, 0x68, 0x7e, 0xf2, 0x5c, 0x4b, 0xf4, 0x93, 0x6b, 0xa1,
- 0xc0, 0xe6, 0x4, 0x1e, 0x9d, 0x15, 0x2, 0x3, 0x1, 0x0, 0x1,
- 0x2, 0x41, 0x0, 0x8a, 0xbd, 0x6a, 0x69, 0xf4, 0xd1, 0xa4,
- 0xb4, 0x87, 0xf0, 0xab, 0x8d, 0x7a, 0xae, 0xfd, 0x38, 0x60,
- 0x94, 0x5, 0xc9, 0x99, 0x98, 0x4e, 0x30, 0xf5, 0x67, 0xe1,
- 0xe8, 0xae, 0xef, 0xf4, 0x4e, 0x8b, 0x18, 0xbd, 0xb1, 0xec,
- 0x78, 0xdf, 0xa3, 0x1a, 0x55, 0xe3, 0x2a, 0x48, 0xd7, 0xfb,
- 0x13, 0x1f, 0x5a, 0xf1, 0xf4, 0x4d, 0x7d, 0x6b, 0x2c, 0xed,
- 0x2a, 0x9d, 0xf5, 0xe5, 0xae, 0x45, 0x35, 0x2, 0x21, 0x0,
- 0xda, 0xb2, 0xf1, 0x80, 0x48, 0xba, 0xa6, 0x8d, 0xe7, 0xdf,
- 0x4, 0xd2, 0xd3, 0x5d, 0x5d, 0x80, 0xe6, 0xe, 0x2d, 0xfa,
- 0x42, 0xd5, 0xa, 0x9b, 0x4, 0x21, 0x90, 0x32, 0x71, 0x5e,
- 0x46, 0xb3, 0x2, 0x21, 0x0, 0xd1, 0xf, 0x2e, 0x66, 0xb1,
- 0xd0, 0xc1, 0x3f, 0x10, 0xef, 0x99, 0x27, 0xbf, 0x53, 0x24,
- 0xa3, 0x79, 0xca, 0x21, 0x81, 0x46, 0xcb, 0xf9, 0xca, 0xfc,
- 0x79, 0x52, 0x21, 0xf1, 0x6a, 0x31, 0x17, 0x2, 0x20, 0x21,
- 0x2, 0x89, 0x79, 0x37, 0x81, 0x14, 0xca, 0xae, 0x88, 0xf7,
- 0xd, 0x6b, 0x61, 0xd8, 0x4f, 0x30, 0x6a, 0x4b, 0x7e, 0x4e,
- 0xc0, 0x21, 0x4d, 0xac, 0x9d, 0xf4, 0x49, 0xe8, 0xda, 0xb6,
- 0x9, 0x2, 0x20, 0x16, 0xb3, 0xec, 0x59, 0x10, 0xa4, 0x57,
- 0xe8, 0xe, 0x61, 0xc6, 0xa3, 0xf, 0x5e, 0xeb, 0x12, 0xa9,
- 0xae, 0x2e, 0xb7, 0x48, 0x45, 0xec, 0x69, 0x83, 0xc3, 0x75,
- 0xc, 0xe4, 0x97, 0xa0, 0x9f, 0x2, 0x20, 0x69, 0x52, 0xb4,
- 0x6, 0xe8, 0x50, 0x60, 0x71, 0x4c, 0x3a, 0xb7, 0x66, 0xba,
- 0xd, 0x8a, 0xc9, 0xb7, 0xd, 0xa3, 0x8, 0x6c, 0xa3, 0xf2,
- 0x62, 0xb0, 0x2a, 0x84, 0xaa, 0x2f, 0xd6, 0x1e, 0x55,
+var privateKey2 = &Block{
+ Type: "RSA PRIVATE KEY",
+ Headers: map[string]string{
+ "Proc-Type": "4,ENCRYPTED",
+ "DEK-Info": "AES-128-CBC,BFCD243FEDBB40A4AA6DDAA1335473A4",
+ "Content-Domain": "RFC822",
+ },
+ Bytes: []uint8{
+ 0xa8, 0x35, 0xcc, 0x2b, 0xb9, 0xcb, 0x21, 0xab, 0xc0,
+ 0x9d, 0x76, 0x61, 0x0, 0xf4, 0x81, 0xad, 0x69, 0xd2,
+ 0xc0, 0x42, 0x41, 0x3b, 0xe4, 0x3c, 0xaf, 0x59, 0x5e,
+ 0x6d, 0x2a, 0x3c, 0x9c, 0xa1, 0xa4, 0x5e, 0x68, 0x37,
+ 0xc4, 0x8c, 0x70, 0x1c, 0xa9, 0x18, 0xe6, 0xc2, 0x2b,
+ 0x8a, 0x91, 0xdc, 0x2d, 0x1f, 0x8, 0x23, 0x39, 0xf1,
+ 0x4b, 0x8b, 0x1b, 0x2f, 0x46, 0xb, 0xb2, 0x26, 0xba,
+ 0x4f, 0x40, 0x80, 0x39, 0xc4, 0xb1, 0xcb, 0x3b, 0xb4,
+ 0x65, 0x3f, 0x1b, 0xb2, 0xf7, 0x8, 0xd2, 0xc6, 0xd5,
+ 0xa8, 0x9f, 0x23, 0x69, 0xb6, 0x3d, 0xf9, 0xac, 0x1c,
+ 0xb3, 0x13, 0x87, 0x64, 0x4, 0x37, 0xdb, 0x40, 0xc8,
+ 0x82, 0xc, 0xd0, 0xf8, 0x21, 0x7c, 0xdc, 0xbd, 0x9, 0x4,
+ 0x20, 0x16, 0xb0, 0x97, 0xe2, 0x6d, 0x56, 0x1d, 0xe3,
+ 0xec, 0xf0, 0xfc, 0xe2, 0x56, 0xad, 0xa4, 0x3, 0x70,
+ 0x6d, 0x63, 0x3c, 0x1, 0xbe, 0x3e, 0x28, 0x38, 0x6f,
+ 0xc0, 0xe6, 0xfd, 0x85, 0xd1, 0x53, 0xa8, 0x9b, 0xcb,
+ 0xd4, 0x4, 0xb1, 0x73, 0xb9, 0x73, 0x32, 0xd6, 0x7a,
+ 0xc6, 0x29, 0x25, 0xa5, 0xda, 0x17, 0x93, 0x7a, 0x10,
+ 0xe8, 0x41, 0xfb, 0xa5, 0x17, 0x20, 0xf8, 0x4e, 0xe9,
+ 0xe3, 0x8f, 0x51, 0x20, 0x13, 0xbb, 0xde, 0xb7, 0x93,
+ 0xae, 0x13, 0x8a, 0xf6, 0x9, 0xf4, 0xa6, 0x41, 0xe0,
+ 0x2b, 0x51, 0x1a, 0x30, 0x38, 0xd, 0xb1, 0x3b, 0x67,
+ 0x87, 0x64, 0xf5, 0xca, 0x32, 0x67, 0xd1, 0xc8, 0xa5,
+ 0x3d, 0x23, 0x72, 0xc4, 0x6, 0xaf, 0x8f, 0x7b, 0x26,
+ 0xac, 0x3c, 0x75, 0x91, 0xa1, 0x0, 0x13, 0xc6, 0x5c,
+ 0x49, 0xd5, 0x3c, 0xe7, 0xb2, 0xb2, 0x99, 0xe0, 0xd5,
+ 0x25, 0xfa, 0xe2, 0x12, 0x80, 0x37, 0x85, 0xcf, 0x92,
+ 0xca, 0x1b, 0x9f, 0xf3, 0x4e, 0xd8, 0x80, 0xef, 0x3c,
+ 0xce, 0xcd, 0xf5, 0x90, 0x9e, 0xf9, 0xa7, 0xb2, 0xc,
+ 0x49, 0x4, 0xf1, 0x9, 0x8f, 0xea, 0x63, 0xd2, 0x70,
+ 0xbb, 0x86, 0xbf, 0x34, 0xab, 0xb2, 0x3, 0xb1, 0x59,
+ 0x33, 0x16, 0x17, 0xb0, 0xdb, 0x77, 0x38, 0xf4, 0xb4,
+ 0x94, 0xb, 0x25, 0x16, 0x7e, 0x22, 0xd4, 0xf9, 0x22,
+ 0xb9, 0x78, 0xa3, 0x4, 0x84, 0x4, 0xd2, 0xda, 0x84,
+ 0x2d, 0x63, 0xdd, 0xf8, 0x50, 0x6a, 0xf6, 0xe3, 0xf5,
+ 0x65, 0x40, 0x7c, 0xa9,
},
}
-var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
-MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
-fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
-/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
-RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
-EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
-IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
-tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
+var pemPrivateKey2 = `-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+Content-Domain: RFC822
+DEK-Info: AES-128-CBC,BFCD243FEDBB40A4AA6DDAA1335473A4
+
+qDXMK7nLIavAnXZhAPSBrWnSwEJBO+Q8r1lebSo8nKGkXmg3xIxwHKkY5sIripHc
+LR8IIznxS4sbL0YLsia6T0CAOcSxyzu0ZT8bsvcI0sbVqJ8jabY9+awcsxOHZAQ3
+20DIggzQ+CF83L0JBCAWsJfibVYd4+zw/OJWraQDcG1jPAG+Pig4b8Dm/YXRU6ib
+y9QEsXO5czLWesYpJaXaF5N6EOhB+6UXIPhO6eOPUSATu963k64TivYJ9KZB4CtR
+GjA4DbE7Z4dk9coyZ9HIpT0jcsQGr497Jqw8dZGhABPGXEnVPOeyspng1SX64hKA
+N4XPksobn/NO2IDvPM7N9ZCe+aeyDEkE8QmP6mPScLuGvzSrsgOxWTMWF7Dbdzj0
+tJQLJRZ+ItT5Irl4owSEBNLahC1j3fhQavbj9WVAfKk=
-----END RSA PRIVATE KEY-----
`
diff --git a/src/pkg/encoding/xml/example_test.go b/src/pkg/encoding/xml/example_test.go
index 97c8c0b0d..becedd583 100644
--- a/src/pkg/encoding/xml/example_test.go
+++ b/src/pkg/encoding/xml/example_test.go
@@ -50,6 +50,46 @@ func ExampleMarshalIndent() {
// </person>
}
+func ExampleEncoder() {
+ type Address struct {
+ City, State string
+ }
+ type Person struct {
+ XMLName xml.Name `xml:"person"`
+ Id int `xml:"id,attr"`
+ FirstName string `xml:"name>first"`
+ LastName string `xml:"name>last"`
+ Age int `xml:"age"`
+ Height float32 `xml:"height,omitempty"`
+ Married bool
+ Address
+ Comment string `xml:",comment"`
+ }
+
+ v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42}
+ v.Comment = " Need more details. "
+ v.Address = Address{"Hanga Roa", "Easter Island"}
+
+ enc := xml.NewEncoder(os.Stdout)
+ enc.Indent(" ", " ")
+ if err := enc.Encode(v); err != nil {
+ fmt.Printf("error: %v\n", err)
+ }
+
+ // Output:
+ // <person id="13">
+ // <name>
+ // <first>John</first>
+ // <last>Doe</last>
+ // </name>
+ // <age>42</age>
+ // <Married>false</Married>
+ // <City>Hanga Roa</City>
+ // <State>Easter Island</State>
+ // <!-- Need more details. -->
+ // </person>
+}
+
// This example demonstrates unmarshaling an XML excerpt into a value with
// some preset fields. Note that the Phone field isn't modified and that
// the XML <Company> element is ignored. Also, the Groups field is assigned
diff --git a/src/pkg/encoding/xml/marshal.go b/src/pkg/encoding/xml/marshal.go
index 6c3170bdd..ea58ce254 100644
--- a/src/pkg/encoding/xml/marshal.go
+++ b/src/pkg/encoding/xml/marshal.go
@@ -45,7 +45,7 @@ const (
// - a field with tag "name,attr" becomes an attribute with
// the given name in the XML element.
// - a field with tag ",attr" becomes an attribute with the
-// field name in the in the XML element.
+// field name in the XML element.
// - a field with tag ",chardata" is written as character data,
// not as an XML element.
// - a field with tag ",innerxml" is written verbatim, not subject
@@ -57,8 +57,8 @@ const (
// if the field value is empty. The empty values are false, 0, any
// nil pointer or interface value, and any array, slice, map, or
// string of length zero.
-// - a non-pointer anonymous struct field is handled as if the
-// fields of its value were part of the outer struct.
+// - an anonymous struct field is handled as if the fields of its
+// value were part of the outer struct.
//
// If a field uses a tag "a>b>c", then the element c will be nested inside
// parent elements a and b. Fields that appear next to each other that name
@@ -81,11 +81,8 @@ func Marshal(v interface{}) ([]byte, error) {
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
var b bytes.Buffer
enc := NewEncoder(&b)
- enc.prefix = prefix
- enc.indent = indent
- err := enc.marshalValue(reflect.ValueOf(v), nil)
- enc.Flush()
- if err != nil {
+ enc.Indent(prefix, indent)
+ if err := enc.Encode(v); err != nil {
return nil, err
}
return b.Bytes(), nil
@@ -101,14 +98,24 @@ func NewEncoder(w io.Writer) *Encoder {
return &Encoder{printer{Writer: bufio.NewWriter(w)}}
}
+// Indent sets the encoder to generate XML in which each element
+// begins on a new indented line that starts with prefix and is followed by
+// one or more copies of indent according to the nesting depth.
+func (enc *Encoder) Indent(prefix, indent string) {
+ enc.prefix = prefix
+ enc.indent = indent
+}
+
// Encode writes the XML encoding of v to the stream.
//
// See the documentation for Marshal for details about the conversion
// of Go values to XML.
func (enc *Encoder) Encode(v interface{}) error {
err := enc.marshalValue(reflect.ValueOf(v), nil)
- enc.Flush()
- return err
+ if err != nil {
+ return err
+ }
+ return enc.Flush()
}
type printer struct {
@@ -117,6 +124,7 @@ type printer struct {
prefix string
depth int
indentedIn bool
+ putNewline bool
}
// marshalValue writes one or more XML elements representing val.
@@ -164,7 +172,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
xmlname := tinfo.xmlname
if xmlname.name != "" {
xmlns, name = xmlname.xmlns, xmlname.name
- } else if v, ok := val.FieldByIndex(xmlname.idx).Interface().(Name); ok && v.Local != "" {
+ } else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
xmlns, name = v.Space, v.Local
}
}
@@ -185,7 +193,9 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
if xmlns != "" {
p.WriteString(` xmlns="`)
// TODO: EscapeString, to avoid the allocation.
- Escape(p, []byte(xmlns))
+ if err := EscapeText(p, []byte(xmlns)); err != nil {
+ return err
+ }
p.WriteByte('"')
}
@@ -195,7 +205,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
if finfo.flags&fAttr == 0 {
continue
}
- fv := val.FieldByIndex(finfo.idx)
+ fv := finfo.value(val)
if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
continue
}
@@ -224,7 +234,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
p.WriteString(name)
p.WriteByte('>')
- return nil
+ return p.cachedWriteError()
}
var timeType = reflect.TypeOf(time.Time{})
@@ -241,50 +251,70 @@ func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) error {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
p.WriteString(strconv.FormatUint(val.Uint(), 10))
case reflect.Float32, reflect.Float64:
- p.WriteString(strconv.FormatFloat(val.Float(), 'g', -1, 64))
+ p.WriteString(strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()))
case reflect.String:
// TODO: Add EscapeString.
- Escape(p, []byte(val.String()))
+ EscapeText(p, []byte(val.String()))
case reflect.Bool:
p.WriteString(strconv.FormatBool(val.Bool()))
case reflect.Array:
// will be [...]byte
- bytes := make([]byte, val.Len())
- for i := range bytes {
- bytes[i] = val.Index(i).Interface().(byte)
+ var bytes []byte
+ if val.CanAddr() {
+ bytes = val.Slice(0, val.Len()).Bytes()
+ } else {
+ bytes = make([]byte, val.Len())
+ reflect.Copy(reflect.ValueOf(bytes), val)
}
- Escape(p, bytes)
+ EscapeText(p, bytes)
case reflect.Slice:
// will be []byte
- Escape(p, val.Bytes())
+ EscapeText(p, val.Bytes())
default:
return &UnsupportedTypeError{typ}
}
- return nil
+ return p.cachedWriteError()
}
var ddBytes = []byte("--")
func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
if val.Type() == timeType {
- p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano))
- return nil
+ _, err := p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano))
+ return err
}
s := parentStack{printer: p}
for i := range tinfo.fields {
finfo := &tinfo.fields[i]
- if finfo.flags&(fAttr|fAny) != 0 {
+ if finfo.flags&(fAttr) != 0 {
continue
}
- vf := val.FieldByIndex(finfo.idx)
+ vf := finfo.value(val)
switch finfo.flags & fMode {
case fCharData:
+ var scratch [64]byte
switch vf.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ Escape(p, strconv.AppendInt(scratch[:0], vf.Int(), 10))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ Escape(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10))
+ case reflect.Float32, reflect.Float64:
+ Escape(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits()))
+ case reflect.Bool:
+ Escape(p, strconv.AppendBool(scratch[:0], vf.Bool()))
case reflect.String:
- Escape(p, []byte(vf.String()))
+ if err := EscapeText(p, []byte(vf.String())); err != nil {
+ return err
+ }
case reflect.Slice:
if elem, ok := vf.Interface().([]byte); ok {
- Escape(p, elem)
+ if err := EscapeText(p, elem); err != nil {
+ return err
+ }
+ }
+ case reflect.Struct:
+ if vf.Type() == timeType {
+ Escape(p, []byte(vf.Interface().(time.Time).Format(time.RFC3339Nano)))
}
}
continue
@@ -340,7 +370,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
continue
}
- case fElement:
+ case fElement, fElement | fAny:
s.trim(finfo.parents)
if len(finfo.parents) > len(s.stack) {
if vf.Kind() != reflect.Ptr && vf.Kind() != reflect.Interface || !vf.IsNil() {
@@ -353,7 +383,13 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
}
}
s.trim(nil)
- return nil
+ return p.cachedWriteError()
+}
+
+// return the bufio Writer's cached write error
+func (p *printer) cachedWriteError() error {
+ _, err := p.Write(nil)
+ return err
}
func (p *printer) writeIndent(depthDelta int) {
@@ -368,7 +404,11 @@ func (p *printer) writeIndent(depthDelta int) {
}
p.indentedIn = false
}
- p.WriteByte('\n')
+ if p.putNewline {
+ p.WriteByte('\n')
+ } else {
+ p.putNewline = true
+ }
if len(p.prefix) > 0 {
p.WriteString(p.prefix)
}
diff --git a/src/pkg/encoding/xml/marshal_test.go b/src/pkg/encoding/xml/marshal_test.go
index b6978a1e6..3a190def6 100644
--- a/src/pkg/encoding/xml/marshal_test.go
+++ b/src/pkg/encoding/xml/marshal_test.go
@@ -5,6 +5,10 @@
package xml
import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
"reflect"
"strconv"
"strings"
@@ -56,6 +60,36 @@ type Book struct {
Title string `xml:",chardata"`
}
+type Event struct {
+ XMLName struct{} `xml:"event"`
+ Year int `xml:",chardata"`
+}
+
+type Movie struct {
+ XMLName struct{} `xml:"movie"`
+ Length uint `xml:",chardata"`
+}
+
+type Pi struct {
+ XMLName struct{} `xml:"pi"`
+ Approximation float32 `xml:",chardata"`
+}
+
+type Universe struct {
+ XMLName struct{} `xml:"universe"`
+ Visible float64 `xml:",chardata"`
+}
+
+type Particle struct {
+ XMLName struct{} `xml:"particle"`
+ HasMass bool `xml:",chardata"`
+}
+
+type Departure struct {
+ XMLName struct{} `xml:"departure"`
+ When time.Time `xml:",chardata"`
+}
+
type SecretAgent struct {
XMLName struct{} `xml:"agent"`
Handle string `xml:"handle,attr"`
@@ -108,7 +142,7 @@ type EmbedA struct {
type EmbedB struct {
FieldB string
- EmbedC
+ *EmbedC
}
type EmbedC struct {
@@ -185,6 +219,18 @@ type AnyTest struct {
AnyField AnyHolder `xml:",any"`
}
+type AnyOmitTest struct {
+ XMLName struct{} `xml:"a"`
+ Nested string `xml:"nested>value"`
+ AnyField *AnyHolder `xml:",any,omitempty"`
+}
+
+type AnySliceTest struct {
+ XMLName struct{} `xml:"a"`
+ Nested string `xml:"nested>value"`
+ AnyField []AnyHolder `xml:",any"`
+}
+
type AnyHolder struct {
XMLName Name
XML string `xml:",innerxml"`
@@ -330,6 +376,12 @@ var marshalTests = []struct {
{Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&amp;friends</domain>`},
{Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
{Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride &amp; Prejudice</book>`},
+ {Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`},
+ {Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`},
+ {Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`},
+ {Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`},
+ {Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`},
+ {Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`},
{Value: atomValue, ExpectXML: atomXml},
{
Value: &Ship{
@@ -493,7 +545,7 @@ var marshalTests = []struct {
},
EmbedB: EmbedB{
FieldB: "A.B.B",
- EmbedC: EmbedC{
+ EmbedC: &EmbedC{
FieldA1: "A.B.C.A1",
FieldA2: "A.B.C.A2",
FieldB: "", // Shadowed by A.B.B
@@ -649,12 +701,43 @@ var marshalTests = []struct {
XML: "<sub>unknown</sub>",
},
},
- UnmarshalOnly: true,
},
{
- Value: &AnyTest{Nested: "known", AnyField: AnyHolder{XML: "<unknown/>"}},
- ExpectXML: `<a><nested><value>known</value></nested></a>`,
- MarshalOnly: true,
+ Value: &AnyTest{Nested: "known",
+ AnyField: AnyHolder{
+ XML: "<unknown/>",
+ XMLName: Name{Local: "AnyField"},
+ },
+ },
+ ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`,
+ },
+ {
+ ExpectXML: `<a><nested><value>b</value></nested></a>`,
+ Value: &AnyOmitTest{
+ Nested: "b",
+ },
+ },
+ {
+ ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`,
+ Value: &AnySliceTest{
+ Nested: "b",
+ AnyField: []AnyHolder{
+ {
+ XMLName: Name{Local: "c"},
+ XML: "<d>e</d>",
+ },
+ {
+ XMLName: Name{Space: "f", Local: "g"},
+ XML: "<h>i</h>",
+ },
+ },
+ },
+ },
+ {
+ ExpectXML: `<a><nested><value>b</value></nested></a>`,
+ Value: &AnySliceTest{
+ Nested: "b",
+ },
},
// Test recursive types.
@@ -684,6 +767,29 @@ var marshalTests = []struct {
Value: &IgnoreTest{},
UnmarshalOnly: true,
},
+
+ // Test escaping.
+ {
+ ExpectXML: `<a><nested><value>dquote: &#34;; squote: &#39;; ampersand: &amp;; less: &lt;; greater: &gt;;</value></nested><empty></empty></a>`,
+ Value: &AnyTest{
+ Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`,
+ AnyField: AnyHolder{XMLName: Name{Local: "empty"}},
+ },
+ },
+ {
+ ExpectXML: `<a><nested><value>newline: &#xA;; cr: &#xD;; tab: &#x9;;</value></nested><AnyField></AnyField></a>`,
+ Value: &AnyTest{
+ Nested: "newline: \n; cr: \r; tab: \t;",
+ AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}},
+ },
+ },
+ {
+ ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>",
+ Value: &AnyTest{
+ Nested: "1\n2\n3\n\n4\n5",
+ },
+ UnmarshalOnly: true,
+ },
}
func TestMarshal(t *testing.T) {
@@ -735,6 +841,24 @@ var marshalErrorTests = []struct {
},
}
+var marshalIndentTests = []struct {
+ Value interface{}
+ Prefix string
+ Indent string
+ ExpectXML string
+}{
+ {
+ Value: &SecretAgent{
+ Handle: "007",
+ Identity: "James Bond",
+ Obfuscate: "<redacted/>",
+ },
+ Prefix: "",
+ Indent: "\t",
+ ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"),
+ },
+}
+
func TestMarshalErrors(t *testing.T) {
for idx, test := range marshalErrorTests {
_, err := Marshal(test.Value)
@@ -779,6 +903,78 @@ func TestUnmarshal(t *testing.T) {
}
}
+func TestMarshalIndent(t *testing.T) {
+ for i, test := range marshalIndentTests {
+ data, err := MarshalIndent(test.Value, test.Prefix, test.Indent)
+ if err != nil {
+ t.Errorf("#%d: Error: %s", i, err)
+ continue
+ }
+ if got, want := string(data), test.ExpectXML; got != want {
+ t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want)
+ }
+ }
+}
+
+type limitedBytesWriter struct {
+ w io.Writer
+ remain int // until writes fail
+}
+
+func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
+ if lw.remain <= 0 {
+ println("error")
+ return 0, errors.New("write limit hit")
+ }
+ if len(p) > lw.remain {
+ p = p[:lw.remain]
+ n, _ = lw.w.Write(p)
+ lw.remain = 0
+ return n, errors.New("write limit hit")
+ }
+ n, err = lw.w.Write(p)
+ lw.remain -= n
+ return n, err
+}
+
+func TestMarshalWriteErrors(t *testing.T) {
+ var buf bytes.Buffer
+ const writeCap = 1024
+ w := &limitedBytesWriter{&buf, writeCap}
+ enc := NewEncoder(w)
+ var err error
+ var i int
+ const n = 4000
+ for i = 1; i <= n; i++ {
+ err = enc.Encode(&Passenger{
+ Name: []string{"Alice", "Bob"},
+ Weight: 5,
+ })
+ if err != nil {
+ break
+ }
+ }
+ if err == nil {
+ t.Error("expected an error")
+ }
+ if i == n {
+ t.Errorf("expected to fail before the end")
+ }
+ if buf.Len() != writeCap {
+ t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
+ }
+}
+
+func TestMarshalWriteIOErrors(t *testing.T) {
+ enc := NewEncoder(errWriter{})
+
+ expectErr := "unwritable"
+ err := enc.Encode(&Passenger{})
+ if err == nil || err.Error() != expectErr {
+ t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr)
+ }
+}
+
func BenchmarkMarshal(b *testing.B) {
for i := 0; i < b.N; i++ {
Marshal(atomValue)
diff --git a/src/pkg/encoding/xml/read.go b/src/pkg/encoding/xml/read.go
index c21682420..344ab514e 100644
--- a/src/pkg/encoding/xml/read.go
+++ b/src/pkg/encoding/xml/read.go
@@ -81,8 +81,8 @@ import (
// of the above rules and the struct has a field with tag ",any",
// unmarshal maps the sub-element to that struct field.
//
-// * A non-pointer anonymous struct field is handled as if the
-// fields of its value were part of the outer struct.
+// * An anonymous struct field is handled as if the fields of its
+// value were part of the outer struct.
//
// * A struct field with tag "-" is never unmarshalled into.
//
@@ -248,7 +248,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
}
return UnmarshalError(e)
}
- fv := sv.FieldByIndex(finfo.idx)
+ fv := finfo.value(sv)
if _, ok := fv.Interface().(Name); ok {
fv.Set(reflect.ValueOf(start.Name))
}
@@ -260,7 +260,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
finfo := &tinfo.fields[i]
switch finfo.flags & fMode {
case fAttr:
- strv := sv.FieldByIndex(finfo.idx)
+ strv := finfo.value(sv)
// Look for attribute.
for _, a := range start.Attr {
if a.Name.Local == finfo.name {
@@ -271,22 +271,22 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
case fCharData:
if !saveData.IsValid() {
- saveData = sv.FieldByIndex(finfo.idx)
+ saveData = finfo.value(sv)
}
case fComment:
if !saveComment.IsValid() {
- saveComment = sv.FieldByIndex(finfo.idx)
+ saveComment = finfo.value(sv)
}
- case fAny:
+ case fAny, fAny | fElement:
if !saveAny.IsValid() {
- saveAny = sv.FieldByIndex(finfo.idx)
+ saveAny = finfo.value(sv)
}
case fInnerXml:
if !saveXML.IsValid() {
- saveXML = sv.FieldByIndex(finfo.idx)
+ saveXML = finfo.value(sv)
if p.saved == nil {
saveXMLIndex = 0
p.saved = new(bytes.Buffer)
@@ -374,68 +374,58 @@ Loop:
}
func copyValue(dst reflect.Value, src []byte) (err error) {
- // Helper functions for integer and unsigned integer conversions
- var itmp int64
- getInt64 := func() bool {
- itmp, err = strconv.ParseInt(string(src), 10, 64)
- // TODO: should check sizes
- return err == nil
- }
- var utmp uint64
- getUint64 := func() bool {
- utmp, err = strconv.ParseUint(string(src), 10, 64)
- // TODO: check for overflow?
- return err == nil
- }
- var ftmp float64
- getFloat64 := func() bool {
- ftmp, err = strconv.ParseFloat(string(src), 64)
- // TODO: check for overflow?
- return err == nil
+ if dst.Kind() == reflect.Ptr {
+ if dst.IsNil() {
+ dst.Set(reflect.New(dst.Type().Elem()))
+ }
+ dst = dst.Elem()
}
// Save accumulated data.
- switch t := dst; t.Kind() {
+ switch dst.Kind() {
case reflect.Invalid:
- // Probably a comment.
+ // Probably a commendst.
default:
- return errors.New("cannot happen: unknown type " + t.Type().String())
+ return errors.New("cannot happen: unknown type " + dst.Type().String())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- if !getInt64() {
+ itmp, err := strconv.ParseInt(string(src), 10, dst.Type().Bits())
+ if err != nil {
return err
}
- t.SetInt(itmp)
+ dst.SetInt(itmp)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- if !getUint64() {
+ utmp, err := strconv.ParseUint(string(src), 10, dst.Type().Bits())
+ if err != nil {
return err
}
- t.SetUint(utmp)
+ dst.SetUint(utmp)
case reflect.Float32, reflect.Float64:
- if !getFloat64() {
+ ftmp, err := strconv.ParseFloat(string(src), dst.Type().Bits())
+ if err != nil {
return err
}
- t.SetFloat(ftmp)
+ dst.SetFloat(ftmp)
case reflect.Bool:
value, err := strconv.ParseBool(strings.TrimSpace(string(src)))
if err != nil {
return err
}
- t.SetBool(value)
+ dst.SetBool(value)
case reflect.String:
- t.SetString(string(src))
+ dst.SetString(string(src))
case reflect.Slice:
if len(src) == 0 {
// non-nil to flag presence
src = []byte{}
}
- t.SetBytes(src)
+ dst.SetBytes(src)
case reflect.Struct:
- if t.Type() == timeType {
+ if dst.Type() == timeType {
tv, err := time.Parse(time.RFC3339, string(src))
if err != nil {
return err
}
- t.Set(reflect.ValueOf(tv))
+ dst.Set(reflect.ValueOf(tv))
}
}
return nil
@@ -461,7 +451,7 @@ Loop:
}
if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local {
// It's a perfect match, unmarshal the field.
- return true, p.unmarshal(sv.FieldByIndex(finfo.idx), start)
+ return true, p.unmarshal(finfo.value(sv), start)
}
if len(finfo.parents) > len(parents) && finfo.parents[len(parents)] == start.Name.Local {
// It's a prefix for the field. Break and recurse
diff --git a/src/pkg/encoding/xml/read_test.go b/src/pkg/encoding/xml/read_test.go
index 8df09b3cc..b45e2f0e6 100644
--- a/src/pkg/encoding/xml/read_test.go
+++ b/src/pkg/encoding/xml/read_test.go
@@ -355,3 +355,47 @@ func TestUnmarshalWithoutNameType(t *testing.T) {
t.Fatalf("have %v\nwant %v", x.Attr, OK)
}
}
+
+func TestUnmarshalAttr(t *testing.T) {
+ type ParamVal struct {
+ Int int `xml:"int,attr"`
+ }
+
+ type ParamPtr struct {
+ Int *int `xml:"int,attr"`
+ }
+
+ type ParamStringPtr struct {
+ Int *string `xml:"int,attr"`
+ }
+
+ x := []byte(`<Param int="1" />`)
+
+ p1 := &ParamPtr{}
+ if err := Unmarshal(x, p1); err != nil {
+ t.Fatalf("Unmarshal: %s", err)
+ }
+ if p1.Int == nil {
+ t.Fatalf("Unmarshal failed in to *int field")
+ } else if *p1.Int != 1 {
+ t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p1.Int, 1)
+ }
+
+ p2 := &ParamVal{}
+ if err := Unmarshal(x, p2); err != nil {
+ t.Fatalf("Unmarshal: %s", err)
+ }
+ if p2.Int != 1 {
+ t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p2.Int, 1)
+ }
+
+ p3 := &ParamStringPtr{}
+ if err := Unmarshal(x, p3); err != nil {
+ t.Fatalf("Unmarshal: %s", err)
+ }
+ if p3.Int == nil {
+ t.Fatalf("Unmarshal failed in to *string field")
+ } else if *p3.Int != "1" {
+ t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p3.Int, 1)
+ }
+}
diff --git a/src/pkg/encoding/xml/typeinfo.go b/src/pkg/encoding/xml/typeinfo.go
index 8e2e4508b..bbeb28d87 100644
--- a/src/pkg/encoding/xml/typeinfo.go
+++ b/src/pkg/encoding/xml/typeinfo.go
@@ -66,10 +66,14 @@ func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
// For embedded structs, embed its fields.
if f.Anonymous {
- if f.Type.Kind() != reflect.Struct {
+ t := f.Type
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ if t.Kind() != reflect.Struct {
continue
}
- inner, err := getTypeInfo(f.Type)
+ inner, err := getTypeInfo(t)
if err != nil {
return nil, err
}
@@ -150,6 +154,9 @@ func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, erro
// This will also catch multiple modes in a single field.
valid = false
}
+ if finfo.flags&fMode == fAny {
+ finfo.flags |= fElement
+ }
if finfo.flags&fOmitEmpty != 0 && finfo.flags&(fElement|fAttr) == 0 {
valid = false
}
@@ -327,3 +334,22 @@ type TagPathError struct {
func (e *TagPathError) Error() string {
return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)
}
+
+// value returns v's field value corresponding to finfo.
+// It's equivalent to v.FieldByIndex(finfo.idx), but initializes
+// and dereferences pointers as necessary.
+func (finfo *fieldInfo) value(v reflect.Value) reflect.Value {
+ for i, x := range finfo.idx {
+ if i > 0 {
+ t := v.Type()
+ if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
+ if v.IsNil() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ v = v.Elem()
+ }
+ }
+ v = v.Field(x)
+ }
+ return v
+}
diff --git a/src/pkg/encoding/xml/xml.go b/src/pkg/encoding/xml/xml.go
index 5066f5c01..143fec554 100644
--- a/src/pkg/encoding/xml/xml.go
+++ b/src/pkg/encoding/xml/xml.go
@@ -181,7 +181,6 @@ type Decoder struct {
ns map[string]string
err error
line int
- tmp [32]byte
}
// NewDecoder creates a new XML parser reading from r.
@@ -584,6 +583,7 @@ func (d *Decoder) RawToken() (Token, error) {
if inquote == 0 && b == '>' && depth == 0 {
break
}
+ HandleB:
d.buf.WriteByte(b)
switch {
case b == inquote:
@@ -599,7 +599,35 @@ func (d *Decoder) RawToken() (Token, error) {
depth--
case b == '<' && inquote == 0:
- depth++
+ // Look for <!-- to begin comment.
+ s := "!--"
+ for i := 0; i < len(s); i++ {
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if b != s[i] {
+ for j := 0; j < i; j++ {
+ d.buf.WriteByte(s[j])
+ }
+ depth++
+ goto HandleB
+ }
+ }
+
+ // Remove < that was written above.
+ d.buf.Truncate(d.buf.Len() - 1)
+
+ // Look for terminator.
+ var b0, b1 byte
+ for {
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if b0 == '-' && b1 == '-' && b == '>' {
+ break
+ }
+ b0, b1 = b1, b
+ }
}
}
return Directive(d.buf.Bytes()), nil
@@ -848,78 +876,103 @@ Input:
// XML in all its glory allows a document to define and use
// its own character names with <!ENTITY ...> directives.
// Parsers are required to recognize lt, gt, amp, apos, and quot
- // even if they have not been declared. That's all we allow.
- var i int
- for i = 0; i < len(d.tmp); i++ {
- var ok bool
- d.tmp[i], ok = d.getc()
- if !ok {
- if d.err == io.EOF {
- d.err = d.syntaxError("unexpected EOF")
- }
+ // even if they have not been declared.
+ before := d.buf.Len()
+ d.buf.WriteByte('&')
+ var ok bool
+ var text string
+ var haveText bool
+ if b, ok = d.mustgetc(); !ok {
+ return nil
+ }
+ if b == '#' {
+ d.buf.WriteByte(b)
+ if b, ok = d.mustgetc(); !ok {
return nil
}
- c := d.tmp[i]
- if c == ';' {
- break
- }
- if 'a' <= c && c <= 'z' ||
- 'A' <= c && c <= 'Z' ||
- '0' <= c && c <= '9' ||
- c == '_' || c == '#' {
- continue
+ base := 10
+ if b == 'x' {
+ base = 16
+ d.buf.WriteByte(b)
+ if b, ok = d.mustgetc(); !ok {
+ return nil
+ }
}
- d.ungetc(c)
- break
- }
- s := string(d.tmp[0:i])
- if i >= len(d.tmp) {
- if !d.Strict {
- b0, b1 = 0, 0
- d.buf.WriteByte('&')
- d.buf.Write(d.tmp[0:i])
- continue Input
+ start := d.buf.Len()
+ for '0' <= b && b <= '9' ||
+ base == 16 && 'a' <= b && b <= 'f' ||
+ base == 16 && 'A' <= b && b <= 'F' {
+ d.buf.WriteByte(b)
+ if b, ok = d.mustgetc(); !ok {
+ return nil
+ }
}
- d.err = d.syntaxError("character entity expression &" + s + "... too long")
- return nil
- }
- var haveText bool
- var text string
- if i >= 2 && s[0] == '#' {
- var n uint64
- var err error
- if i >= 3 && s[1] == 'x' {
- n, err = strconv.ParseUint(s[2:], 16, 64)
+ if b != ';' {
+ d.ungetc(b)
} else {
- n, err = strconv.ParseUint(s[1:], 10, 64)
- }
- if err == nil && n <= unicode.MaxRune {
- text = string(n)
- haveText = true
+ s := string(d.buf.Bytes()[start:])
+ d.buf.WriteByte(';')
+ n, err := strconv.ParseUint(s, base, 64)
+ if err == nil && n <= unicode.MaxRune {
+ text = string(n)
+ haveText = true
+ }
}
} else {
- if r, ok := entity[s]; ok {
- text = string(r)
- haveText = true
- } else if d.Entity != nil {
- text, haveText = d.Entity[s]
+ d.ungetc(b)
+ if !d.readName() {
+ if d.err != nil {
+ return nil
+ }
+ ok = false
}
- }
- if !haveText {
- if !d.Strict {
- b0, b1 = 0, 0
- d.buf.WriteByte('&')
- d.buf.Write(d.tmp[0:i])
- continue Input
+ if b, ok = d.mustgetc(); !ok {
+ return nil
}
- d.err = d.syntaxError("invalid character entity &" + s + ";")
- return nil
+ if b != ';' {
+ d.ungetc(b)
+ } else {
+ name := d.buf.Bytes()[before+1:]
+ d.buf.WriteByte(';')
+ if isName(name) {
+ s := string(name)
+ if r, ok := entity[s]; ok {
+ text = string(r)
+ haveText = true
+ } else if d.Entity != nil {
+ text, haveText = d.Entity[s]
+ }
+ }
+ }
+ }
+
+ if haveText {
+ d.buf.Truncate(before)
+ d.buf.Write([]byte(text))
+ b0, b1 = 0, 0
+ continue Input
}
- d.buf.Write([]byte(text))
- b0, b1 = 0, 0
- continue Input
+ if !d.Strict {
+ b0, b1 = 0, 0
+ continue Input
+ }
+ ent := string(d.buf.Bytes()[before])
+ if ent[len(ent)-1] != ';' {
+ ent += " (no semicolon)"
+ }
+ d.err = d.syntaxError("invalid character entity " + ent)
+ return nil
}
- d.buf.WriteByte(b)
+
+ // We must rewrite unescaped \r and \r\n into \n.
+ if b == '\r' {
+ d.buf.WriteByte('\n')
+ } else if b1 == '\r' && b == '\n' {
+ // Skip \r\n--we already wrote \n.
+ } else {
+ d.buf.WriteByte(b)
+ }
+
b0, b1 = b1, b
}
data := d.buf.Bytes()
@@ -940,20 +993,7 @@ Input:
}
}
- // Must rewrite \r and \r\n into \n.
- w := 0
- for r := 0; r < len(data); r++ {
- b := data[r]
- if b == '\r' {
- if r+1 < len(data) && data[r+1] == '\n' {
- continue
- }
- b = '\n'
- }
- data[w] = b
- w++
- }
- return data[0:w]
+ return data
}
// Decide whether the given rune is in the XML Character Range, per
@@ -989,18 +1029,34 @@ func (d *Decoder) nsname() (name Name, ok bool) {
// Do not set d.err if the name is missing (unless unexpected EOF is received):
// let the caller provide better context.
func (d *Decoder) name() (s string, ok bool) {
+ d.buf.Reset()
+ if !d.readName() {
+ return "", false
+ }
+
+ // Now we check the characters.
+ s = d.buf.String()
+ if !isName([]byte(s)) {
+ d.err = d.syntaxError("invalid XML name: " + s)
+ return "", false
+ }
+ return s, true
+}
+
+// Read a name and append its bytes to d.buf.
+// The name is delimited by any single-byte character not valid in names.
+// All multi-byte characters are accepted; the caller must check their validity.
+func (d *Decoder) readName() (ok bool) {
var b byte
if b, ok = d.mustgetc(); !ok {
return
}
-
- // As a first approximation, we gather the bytes [A-Za-z_:.-\x80-\xFF]*
if b < utf8.RuneSelf && !isNameByte(b) {
d.ungetc(b)
- return "", false
+ return false
}
- d.buf.Reset()
d.buf.WriteByte(b)
+
for {
if b, ok = d.mustgetc(); !ok {
return
@@ -1011,16 +1067,7 @@ func (d *Decoder) name() (s string, ok bool) {
}
d.buf.WriteByte(b)
}
-
- // Then we check the characters.
- s = d.buf.String()
- for i, c := range s {
- if !unicode.Is(first, c) && (i == 0 || !unicode.Is(second, c)) {
- d.err = d.syntaxError("invalid XML name: " + s)
- return "", false
- }
- }
- return s, true
+ return true
}
func isNameByte(c byte) bool {
@@ -1030,6 +1077,30 @@ func isNameByte(c byte) bool {
c == '_' || c == ':' || c == '.' || c == '-'
}
+func isName(s []byte) bool {
+ if len(s) == 0 {
+ return false
+ }
+ c, n := utf8.DecodeRune(s)
+ if c == utf8.RuneError && n == 1 {
+ return false
+ }
+ if !unicode.Is(first, c) {
+ return false
+ }
+ for n < len(s) {
+ s = s[n:]
+ c, n = utf8.DecodeRune(s)
+ if c == utf8.RuneError && n == 1 {
+ return false
+ }
+ if !unicode.Is(first, c) && !unicode.Is(second, c) {
+ return false
+ }
+ }
+ return true
+}
+
// These tables were generated by cut and paste from Appendix B of
// the XML spec at http://www.xml.com/axml/testaxml.htm
// and then reformatting. First corresponds to (Letter | '_' | ':')
@@ -1621,7 +1692,7 @@ var HTMLAutoClose = htmlAutoClose
var htmlAutoClose = []string{
/*
hget http://www.w3.org/TR/html4/loose.dtd |
- 9 sed -n 's/<!ELEMENT (.*) - O EMPTY.+/ "\1",/p' | tr A-Z a-z
+ 9 sed -n 's/<!ELEMENT ([^ ]*) +- O EMPTY.+/ "\1",/p' | tr A-Z a-z
*/
"basefont",
"br",
@@ -1631,7 +1702,7 @@ var htmlAutoClose = []string{
"param",
"hr",
"input",
- "col ",
+ "col",
"frame",
"isindex",
"base",
@@ -1644,11 +1715,14 @@ var (
esc_amp = []byte("&amp;")
esc_lt = []byte("&lt;")
esc_gt = []byte("&gt;")
+ esc_tab = []byte("&#x9;")
+ esc_nl = []byte("&#xA;")
+ esc_cr = []byte("&#xD;")
)
-// Escape writes to w the properly escaped XML equivalent
+// EscapeText writes to w the properly escaped XML equivalent
// of the plain text data s.
-func Escape(w io.Writer, s []byte) {
+func EscapeText(w io.Writer, s []byte) error {
var esc []byte
last := 0
for i, c := range s {
@@ -1663,14 +1737,34 @@ func Escape(w io.Writer, s []byte) {
esc = esc_lt
case '>':
esc = esc_gt
+ case '\t':
+ esc = esc_tab
+ case '\n':
+ esc = esc_nl
+ case '\r':
+ esc = esc_cr
default:
continue
}
- w.Write(s[last:i])
- w.Write(esc)
+ if _, err := w.Write(s[last:i]); err != nil {
+ return err
+ }
+ if _, err := w.Write(esc); err != nil {
+ return err
+ }
last = i + 1
}
- w.Write(s[last:])
+ if _, err := w.Write(s[last:]); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Escape is like EscapeText but omits the error return value.
+// It is provided for backwards compatibility with Go 1.0.
+// Code targeting Go 1.1 or later should use EscapeText.
+func Escape(w io.Writer, s []byte) {
+ EscapeText(w, s)
}
// procInstEncoding parses the `encoding="..."` or `encoding='...'`
diff --git a/src/pkg/encoding/xml/xml_test.go b/src/pkg/encoding/xml/xml_test.go
index 1d0696ce0..54dab5484 100644
--- a/src/pkg/encoding/xml/xml_test.go
+++ b/src/pkg/encoding/xml/xml_test.go
@@ -5,6 +5,7 @@
package xml
import (
+ "fmt"
"io"
"reflect"
"strings"
@@ -18,6 +19,7 @@ const testInput = `
<body xmlns:foo="ns1" xmlns="ns2" xmlns:tag="ns3" ` +
"\r\n\t" + ` >
<hello lang="en">World &lt;&gt;&apos;&quot; &#x767d;&#40300;翔</hello>
+ <query>&何; &is-it;</query>
<goodbye />
<outer foo:attr="value" xmlns:tag="ns4">
<inner/>
@@ -27,6 +29,8 @@ const testInput = `
</tag:name>
</body><!-- missing final newline -->`
+var testEntity = map[string]string{"何": "What", "is-it": "is it?"}
+
var rawTokens = []Token{
CharData("\n"),
ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
@@ -40,6 +44,10 @@ var rawTokens = []Token{
CharData("World <>'\" 白鵬翔"),
EndElement{Name{"", "hello"}},
CharData("\n "),
+ StartElement{Name{"", "query"}, []Attr{}},
+ CharData("What is it?"),
+ EndElement{Name{"", "query"}},
+ CharData("\n "),
StartElement{Name{"", "goodbye"}, []Attr{}},
EndElement{Name{"", "goodbye"}},
CharData("\n "),
@@ -73,6 +81,10 @@ var cookedTokens = []Token{
CharData("World <>'\" 白鵬翔"),
EndElement{Name{"ns2", "hello"}},
CharData("\n "),
+ StartElement{Name{"ns2", "query"}, []Attr{}},
+ CharData("What is it?"),
+ EndElement{Name{"ns2", "query"}},
+ CharData("\n "),
StartElement{Name{"ns2", "goodbye"}, []Attr{}},
EndElement{Name{"ns2", "goodbye"}},
CharData("\n "),
@@ -155,9 +167,65 @@ var xmlInput = []string{
func TestRawToken(t *testing.T) {
d := NewDecoder(strings.NewReader(testInput))
+ d.Entity = testEntity
testRawToken(t, d, rawTokens)
}
+const nonStrictInput = `
+<tag>non&entity</tag>
+<tag>&unknown;entity</tag>
+<tag>&#123</tag>
+<tag>&#zzz;</tag>
+<tag>&なまえ3;</tag>
+<tag>&lt-gt;</tag>
+<tag>&;</tag>
+<tag>&0a;</tag>
+`
+
+var nonStringEntity = map[string]string{"": "oops!", "0a": "oops!"}
+
+var nonStrictTokens = []Token{
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("non&entity"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&unknown;entity"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&#123"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&#zzz;"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&なまえ3;"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&lt-gt;"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&;"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+ StartElement{Name{"", "tag"}, []Attr{}},
+ CharData("&0a;"),
+ EndElement{Name{"", "tag"}},
+ CharData("\n"),
+}
+
+func TestNonStrictRawToken(t *testing.T) {
+ d := NewDecoder(strings.NewReader(nonStrictInput))
+ d.Strict = false
+ testRawToken(t, d, nonStrictTokens)
+}
+
type downCaser struct {
t *testing.T
r io.ByteReader
@@ -219,7 +287,18 @@ func testRawToken(t *testing.T, d *Decoder, rawTokens []Token) {
t.Fatalf("token %d: unexpected error: %s", i, err)
}
if !reflect.DeepEqual(have, want) {
- t.Errorf("token %d = %#v want %#v", i, have, want)
+ var shave, swant string
+ if _, ok := have.(CharData); ok {
+ shave = fmt.Sprintf("CharData(%q)", have)
+ } else {
+ shave = fmt.Sprintf("%#v", have)
+ }
+ if _, ok := want.(CharData); ok {
+ swant = fmt.Sprintf("CharData(%q)", want)
+ } else {
+ swant = fmt.Sprintf("%#v", want)
+ }
+ t.Errorf("token %d = %s, want %s", i, shave, swant)
}
}
}
@@ -272,6 +351,7 @@ func TestNestedDirectives(t *testing.T) {
func TestToken(t *testing.T) {
d := NewDecoder(strings.NewReader(testInput))
+ d.Entity = testEntity
for i, want := range cookedTokens {
have, err := d.Token()
@@ -531,8 +611,8 @@ var characterTests = []struct {
{"\xef\xbf\xbe<doc/>", "illegal character code U+FFFE"},
{"<?xml version=\"1.0\"?><doc>\r\n<hiya/>\x07<toots/></doc>", "illegal character code U+0007"},
{"<?xml version=\"1.0\"?><doc \x12='value'>what's up</doc>", "expected attribute name in element"},
- {"<doc>&\x01;</doc>", "invalid character entity &;"},
- {"<doc>&\xef\xbf\xbe;</doc>", "invalid character entity &;"},
+ {"<doc>&\x01;</doc>", "invalid character entity & (no semicolon)"},
+ {"<doc>&\xef\xbf\xbe;</doc>", "invalid character entity & (no semicolon)"},
}
func TestDisallowedCharacters(t *testing.T) {
@@ -576,3 +656,50 @@ func TestProcInstEncoding(t *testing.T) {
}
}
}
+
+// Ensure that directives with comments include the complete
+// text of any nested directives.
+
+var directivesWithCommentsInput = `
+<!DOCTYPE [<!-- a comment --><!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]>
+<!DOCTYPE [<!ENTITY go "Golang"><!-- a comment-->]>
+<!DOCTYPE <!-> <!> <!----> <!-->--> <!--->--> [<!ENTITY go "Golang"><!-- a comment-->]>
+`
+
+var directivesWithCommentsTokens = []Token{
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`),
+ CharData("\n"),
+ Directive(`DOCTYPE [<!ENTITY go "Golang">]`),
+ CharData("\n"),
+ Directive(`DOCTYPE <!-> <!> [<!ENTITY go "Golang">]`),
+ CharData("\n"),
+}
+
+func TestDirectivesWithComments(t *testing.T) {
+ d := NewDecoder(strings.NewReader(directivesWithCommentsInput))
+
+ for i, want := range directivesWithCommentsTokens {
+ have, err := d.Token()
+ if err != nil {
+ t.Fatalf("token %d: unexpected error: %s", i, err)
+ }
+ if !reflect.DeepEqual(have, want) {
+ t.Errorf("token %d = %#v want %#v", i, have, want)
+ }
+ }
+}
+
+// Writer whose Write method always returns an error.
+type errWriter struct{}
+
+func (errWriter) Write(p []byte) (n int, err error) { return 0, fmt.Errorf("unwritable") }
+
+func TestEscapeTextIOErrors(t *testing.T) {
+ expectErr := "unwritable"
+ err := EscapeText(errWriter{}, []byte{'A'})
+
+ if err == nil || err.Error() != expectErr {
+ t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr)
+ }
+}
diff --git a/src/pkg/exp/norm/Makefile b/src/pkg/exp/norm/Makefile
new file mode 100644
index 000000000..f278eb02f
--- /dev/null
+++ b/src/pkg/exp/norm/Makefile
@@ -0,0 +1,30 @@
+# Copyright 2011 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+maketables: maketables.go triegen.go
+ go build $^
+
+maketesttables: maketesttables.go triegen.go
+ go build $^
+
+normregtest: normregtest.go
+ go build $^
+
+tables: maketables
+ ./maketables > tables.go
+ gofmt -w tables.go
+
+trietesttables: maketesttables
+ ./maketesttables > triedata_test.go
+ gofmt -w triedata_test.go
+
+# Downloads from www.unicode.org, so not part
+# of standard test scripts.
+test: testtables regtest
+
+testtables: maketables
+ ./maketables -test -tables=
+
+regtest: normregtest
+ ./normregtest
diff --git a/src/pkg/exp/norm/composition.go b/src/pkg/exp/norm/composition.go
new file mode 100644
index 000000000..2d52f99dc
--- /dev/null
+++ b/src/pkg/exp/norm/composition.go
@@ -0,0 +1,382 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import "unicode/utf8"
+
+const (
+ maxCombiningChars = 30
+ maxBufferSize = maxCombiningChars + 2 // +1 to hold starter +1 to hold CGJ
+ maxBackRunes = maxCombiningChars - 1
+ maxNFCExpansion = 3 // NFC(0x1D160)
+ maxNFKCExpansion = 18 // NFKC(0xFDFA)
+
+ maxByteBufferSize = utf8.UTFMax * maxBufferSize // 128
+)
+
+// reorderBuffer is used to normalize a single segment. Characters inserted with
+// insert are decomposed and reordered based on CCC. The compose method can
+// be used to recombine characters. Note that the byte buffer does not hold
+// the UTF-8 characters in order. Only the rune array is maintained in sorted
+// order. flush writes the resulting segment to a byte array.
+type reorderBuffer struct {
+ rune [maxBufferSize]Properties // Per character info.
+ byte [maxByteBufferSize]byte // UTF-8 buffer. Referenced by runeInfo.pos.
+ nrune int // Number of runeInfos.
+ nbyte uint8 // Number or bytes.
+ f formInfo
+
+ src input
+ nsrc int
+ tmpBytes input
+}
+
+func (rb *reorderBuffer) init(f Form, src []byte) {
+ rb.f = *formTable[f]
+ rb.src.setBytes(src)
+ rb.nsrc = len(src)
+}
+
+func (rb *reorderBuffer) initString(f Form, src string) {
+ rb.f = *formTable[f]
+ rb.src.setString(src)
+ rb.nsrc = len(src)
+}
+
+// reset discards all characters from the buffer.
+func (rb *reorderBuffer) reset() {
+ rb.nrune = 0
+ rb.nbyte = 0
+}
+
+// flush appends the normalized segment to out and resets rb.
+func (rb *reorderBuffer) flush(out []byte) []byte {
+ for i := 0; i < rb.nrune; i++ {
+ start := rb.rune[i].pos
+ end := start + rb.rune[i].size
+ out = append(out, rb.byte[start:end]...)
+ }
+ rb.reset()
+ return out
+}
+
+// flushCopy copies the normalized segment to buf and resets rb.
+// It returns the number of bytes written to buf.
+func (rb *reorderBuffer) flushCopy(buf []byte) int {
+ p := 0
+ for i := 0; i < rb.nrune; i++ {
+ runep := rb.rune[i]
+ p += copy(buf[p:], rb.byte[runep.pos:runep.pos+runep.size])
+ }
+ rb.reset()
+ return p
+}
+
+// insertOrdered inserts a rune in the buffer, ordered by Canonical Combining Class.
+// It returns false if the buffer is not large enough to hold the rune.
+// It is used internally by insert and insertString only.
+func (rb *reorderBuffer) insertOrdered(info Properties) bool {
+ n := rb.nrune
+ if n >= maxCombiningChars+1 {
+ return false
+ }
+ b := rb.rune[:]
+ cc := info.ccc
+ if cc > 0 {
+ // Find insertion position + move elements to make room.
+ for ; n > 0; n-- {
+ if b[n-1].ccc <= cc {
+ break
+ }
+ b[n] = b[n-1]
+ }
+ }
+ rb.nrune += 1
+ pos := uint8(rb.nbyte)
+ rb.nbyte += utf8.UTFMax
+ info.pos = pos
+ b[n] = info
+ return true
+}
+
+// insert inserts the given rune in the buffer ordered by CCC.
+// It returns true if the buffer was large enough to hold the decomposed rune.
+func (rb *reorderBuffer) insert(src input, i int, info Properties) bool {
+ if rune := src.hangul(i); rune != 0 {
+ return rb.decomposeHangul(rune)
+ }
+ if info.hasDecomposition() {
+ return rb.insertDecomposed(info.Decomposition())
+ }
+ return rb.insertSingle(src, i, info)
+}
+
+// insertDecomposed inserts an entry in to the reorderBuffer for each rune
+// in dcomp. dcomp must be a sequence of decomposed UTF-8-encoded runes.
+func (rb *reorderBuffer) insertDecomposed(dcomp []byte) bool {
+ saveNrune, saveNbyte := rb.nrune, rb.nbyte
+ rb.tmpBytes.setBytes(dcomp)
+ for i := 0; i < len(dcomp); {
+ info := rb.f.info(rb.tmpBytes, i)
+ pos := rb.nbyte
+ if !rb.insertOrdered(info) {
+ rb.nrune, rb.nbyte = saveNrune, saveNbyte
+ return false
+ }
+ i += copy(rb.byte[pos:], dcomp[i:i+int(info.size)])
+ }
+ return true
+}
+
+// insertSingle inserts an entry in the reorderBuffer for the rune at
+// position i. info is the runeInfo for the rune at position i.
+func (rb *reorderBuffer) insertSingle(src input, i int, info Properties) bool {
+ // insertOrder changes nbyte
+ pos := rb.nbyte
+ if !rb.insertOrdered(info) {
+ return false
+ }
+ src.copySlice(rb.byte[pos:], i, i+int(info.size))
+ return true
+}
+
+// appendRune inserts a rune at the end of the buffer. It is used for Hangul.
+func (rb *reorderBuffer) appendRune(r rune) {
+ bn := rb.nbyte
+ sz := utf8.EncodeRune(rb.byte[bn:], rune(r))
+ rb.nbyte += utf8.UTFMax
+ rb.rune[rb.nrune] = Properties{pos: bn, size: uint8(sz)}
+ rb.nrune++
+}
+
+// assignRune sets a rune at position pos. It is used for Hangul and recomposition.
+func (rb *reorderBuffer) assignRune(pos int, r rune) {
+ bn := rb.rune[pos].pos
+ sz := utf8.EncodeRune(rb.byte[bn:], rune(r))
+ rb.rune[pos] = Properties{pos: bn, size: uint8(sz)}
+}
+
+// runeAt returns the rune at position n. It is used for Hangul and recomposition.
+func (rb *reorderBuffer) runeAt(n int) rune {
+ inf := rb.rune[n]
+ r, _ := utf8.DecodeRune(rb.byte[inf.pos : inf.pos+inf.size])
+ return r
+}
+
+// bytesAt returns the UTF-8 encoding of the rune at position n.
+// It is used for Hangul and recomposition.
+func (rb *reorderBuffer) bytesAt(n int) []byte {
+ inf := rb.rune[n]
+ return rb.byte[inf.pos : int(inf.pos)+int(inf.size)]
+}
+
+// For Hangul we combine algorithmically, instead of using tables.
+const (
+ hangulBase = 0xAC00 // UTF-8(hangulBase) -> EA B0 80
+ hangulBase0 = 0xEA
+ hangulBase1 = 0xB0
+ hangulBase2 = 0x80
+
+ hangulEnd = hangulBase + jamoLVTCount // UTF-8(0xD7A4) -> ED 9E A4
+ hangulEnd0 = 0xED
+ hangulEnd1 = 0x9E
+ hangulEnd2 = 0xA4
+
+ jamoLBase = 0x1100 // UTF-8(jamoLBase) -> E1 84 00
+ jamoLBase0 = 0xE1
+ jamoLBase1 = 0x84
+ jamoLEnd = 0x1113
+ jamoVBase = 0x1161
+ jamoVEnd = 0x1176
+ jamoTBase = 0x11A7
+ jamoTEnd = 0x11C3
+
+ jamoTCount = 28
+ jamoVCount = 21
+ jamoVTCount = 21 * 28
+ jamoLVTCount = 19 * 21 * 28
+)
+
+const hangulUTF8Size = 3
+
+func isHangul(b []byte) bool {
+ if len(b) < hangulUTF8Size {
+ return false
+ }
+ b0 := b[0]
+ if b0 < hangulBase0 {
+ return false
+ }
+ b1 := b[1]
+ switch {
+ case b0 == hangulBase0:
+ return b1 >= hangulBase1
+ case b0 < hangulEnd0:
+ return true
+ case b0 > hangulEnd0:
+ return false
+ case b1 < hangulEnd1:
+ return true
+ }
+ return b1 == hangulEnd1 && b[2] < hangulEnd2
+}
+
+func isHangulString(b string) bool {
+ if len(b) < hangulUTF8Size {
+ return false
+ }
+ b0 := b[0]
+ if b0 < hangulBase0 {
+ return false
+ }
+ b1 := b[1]
+ switch {
+ case b0 == hangulBase0:
+ return b1 >= hangulBase1
+ case b0 < hangulEnd0:
+ return true
+ case b0 > hangulEnd0:
+ return false
+ case b1 < hangulEnd1:
+ return true
+ }
+ return b1 == hangulEnd1 && b[2] < hangulEnd2
+}
+
+// Caller must ensure len(b) >= 2.
+func isJamoVT(b []byte) bool {
+ // True if (rune & 0xff00) == jamoLBase
+ return b[0] == jamoLBase0 && (b[1]&0xFC) == jamoLBase1
+}
+
+func isHangulWithoutJamoT(b []byte) bool {
+ c, _ := utf8.DecodeRune(b)
+ c -= hangulBase
+ return c < jamoLVTCount && c%jamoTCount == 0
+}
+
+// decomposeHangul writes the decomposed Hangul to buf and returns the number
+// of bytes written. len(buf) should be at least 9.
+func decomposeHangul(buf []byte, r rune) int {
+ const JamoUTF8Len = 3
+ r -= hangulBase
+ x := r % jamoTCount
+ r /= jamoTCount
+ utf8.EncodeRune(buf, jamoLBase+r/jamoVCount)
+ utf8.EncodeRune(buf[JamoUTF8Len:], jamoVBase+r%jamoVCount)
+ if x != 0 {
+ utf8.EncodeRune(buf[2*JamoUTF8Len:], jamoTBase+x)
+ return 3 * JamoUTF8Len
+ }
+ return 2 * JamoUTF8Len
+}
+
+// decomposeHangul algorithmically decomposes a Hangul rune into
+// its Jamo components.
+// See http://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul.
+func (rb *reorderBuffer) decomposeHangul(r rune) bool {
+ b := rb.rune[:]
+ n := rb.nrune
+ if n+3 > len(b) {
+ return false
+ }
+ r -= hangulBase
+ x := r % jamoTCount
+ r /= jamoTCount
+ rb.appendRune(jamoLBase + r/jamoVCount)
+ rb.appendRune(jamoVBase + r%jamoVCount)
+ if x != 0 {
+ rb.appendRune(jamoTBase + x)
+ }
+ return true
+}
+
+// combineHangul algorithmically combines Jamo character components into Hangul.
+// See http://unicode.org/reports/tr15/#Hangul for details on combining Hangul.
+func (rb *reorderBuffer) combineHangul(s, i, k int) {
+ b := rb.rune[:]
+ bn := rb.nrune
+ for ; i < bn; i++ {
+ cccB := b[k-1].ccc
+ cccC := b[i].ccc
+ if cccB == 0 {
+ s = k - 1
+ }
+ if s != k-1 && cccB >= cccC {
+ // b[i] is blocked by greater-equal cccX below it
+ b[k] = b[i]
+ k++
+ } else {
+ l := rb.runeAt(s) // also used to compare to hangulBase
+ v := rb.runeAt(i) // also used to compare to jamoT
+ switch {
+ case jamoLBase <= l && l < jamoLEnd &&
+ jamoVBase <= v && v < jamoVEnd:
+ // 11xx plus 116x to LV
+ rb.assignRune(s, hangulBase+
+ (l-jamoLBase)*jamoVTCount+(v-jamoVBase)*jamoTCount)
+ case hangulBase <= l && l < hangulEnd &&
+ jamoTBase < v && v < jamoTEnd &&
+ ((l-hangulBase)%jamoTCount) == 0:
+ // ACxx plus 11Ax to LVT
+ rb.assignRune(s, l+v-jamoTBase)
+ default:
+ b[k] = b[i]
+ k++
+ }
+ }
+ }
+ rb.nrune = k
+}
+
+// compose recombines the runes in the buffer.
+// It should only be used to recompose a single segment, as it will not
+// handle alternations between Hangul and non-Hangul characters correctly.
+func (rb *reorderBuffer) compose() {
+ // UAX #15, section X5 , including Corrigendum #5
+ // "In any character sequence beginning with starter S, a character C is
+ // blocked from S if and only if there is some character B between S
+ // and C, and either B is a starter or it has the same or higher
+ // combining class as C."
+ bn := rb.nrune
+ if bn == 0 {
+ return
+ }
+ k := 1
+ b := rb.rune[:]
+ for s, i := 0, 1; i < bn; i++ {
+ if isJamoVT(rb.bytesAt(i)) {
+ // Redo from start in Hangul mode. Necessary to support
+ // U+320E..U+321E in NFKC mode.
+ rb.combineHangul(s, i, k)
+ return
+ }
+ ii := b[i]
+ // We can only use combineForward as a filter if we later
+ // get the info for the combined character. This is more
+ // expensive than using the filter. Using combinesBackward()
+ // is safe.
+ if ii.combinesBackward() {
+ cccB := b[k-1].ccc
+ cccC := ii.ccc
+ blocked := false // b[i] blocked by starter or greater or equal CCC?
+ if cccB == 0 {
+ s = k - 1
+ } else {
+ blocked = s != k-1 && cccB >= cccC
+ }
+ if !blocked {
+ combined := combine(rb.runeAt(s), rb.runeAt(i))
+ if combined != 0 {
+ rb.assignRune(s, combined)
+ continue
+ }
+ }
+ }
+ b[k] = b[i]
+ k++
+ }
+ rb.nrune = k
+}
diff --git a/src/pkg/exp/norm/composition_test.go b/src/pkg/exp/norm/composition_test.go
new file mode 100644
index 000000000..976aa21ed
--- /dev/null
+++ b/src/pkg/exp/norm/composition_test.go
@@ -0,0 +1,143 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import "testing"
+
+// TestCase is used for most tests.
+type TestCase struct {
+ in []rune
+ out []rune
+}
+
+type insertFunc func(rb *reorderBuffer, r rune) bool
+
+func insert(rb *reorderBuffer, r rune) bool {
+ src := inputString(string(r))
+ return rb.insert(src, 0, rb.f.info(src, 0))
+}
+
+func runTests(t *testing.T, name string, fm Form, f insertFunc, tests []TestCase) {
+ rb := reorderBuffer{}
+ rb.init(fm, nil)
+ for i, test := range tests {
+ rb.reset()
+ for j, rune := range test.in {
+ b := []byte(string(rune))
+ src := inputBytes(b)
+ if !rb.insert(src, 0, rb.f.info(src, 0)) {
+ t.Errorf("%s:%d: insert failed for rune %d", name, i, j)
+ }
+ }
+ if rb.f.composing {
+ rb.compose()
+ }
+ if rb.nrune != len(test.out) {
+ t.Errorf("%s:%d: length = %d; want %d", name, i, rb.nrune, len(test.out))
+ continue
+ }
+ for j, want := range test.out {
+ found := rune(rb.runeAt(j))
+ if found != want {
+ t.Errorf("%s:%d: runeAt(%d) = %U; want %U", name, i, j, found, want)
+ }
+ }
+ }
+}
+
+type flushFunc func(rb *reorderBuffer) []byte
+
+func testFlush(t *testing.T, name string, fn flushFunc) {
+ rb := reorderBuffer{}
+ rb.init(NFC, nil)
+ out := fn(&rb)
+ if len(out) != 0 {
+ t.Errorf("%s: wrote bytes on flush of empty buffer. (len(out) = %d)", name, len(out))
+ }
+
+ for _, r := range []rune("world!") {
+ insert(&rb, r)
+ }
+
+ out = []byte("Hello ")
+ out = rb.flush(out)
+ want := "Hello world!"
+ if string(out) != want {
+ t.Errorf(`%s: output after flush was "%s"; want "%s"`, name, string(out), want)
+ }
+ if rb.nrune != 0 {
+ t.Errorf("%s: non-null size of info buffer (rb.nrune == %d)", name, rb.nrune)
+ }
+ if rb.nbyte != 0 {
+ t.Errorf("%s: non-null size of byte buffer (rb.nbyte == %d)", name, rb.nbyte)
+ }
+}
+
+func flushF(rb *reorderBuffer) []byte {
+ out := make([]byte, 0)
+ return rb.flush(out)
+}
+
+func flushCopyF(rb *reorderBuffer) []byte {
+ out := make([]byte, maxByteBufferSize)
+ n := rb.flushCopy(out)
+ return out[:n]
+}
+
+func TestFlush(t *testing.T) {
+ testFlush(t, "flush", flushF)
+ testFlush(t, "flushCopy", flushCopyF)
+}
+
+var insertTests = []TestCase{
+ {[]rune{'a'}, []rune{'a'}},
+ {[]rune{0x300}, []rune{0x300}},
+ {[]rune{0x300, 0x316}, []rune{0x316, 0x300}}, // CCC(0x300)==230; CCC(0x316)==220
+ {[]rune{0x316, 0x300}, []rune{0x316, 0x300}},
+ {[]rune{0x41, 0x316, 0x300}, []rune{0x41, 0x316, 0x300}},
+ {[]rune{0x41, 0x300, 0x316}, []rune{0x41, 0x316, 0x300}},
+ {[]rune{0x300, 0x316, 0x41}, []rune{0x316, 0x300, 0x41}},
+ {[]rune{0x41, 0x300, 0x40, 0x316}, []rune{0x41, 0x300, 0x40, 0x316}},
+}
+
+func TestInsert(t *testing.T) {
+ runTests(t, "TestInsert", NFD, insert, insertTests)
+}
+
+var decompositionNFDTest = []TestCase{
+ {[]rune{0xC0}, []rune{0x41, 0x300}},
+ {[]rune{0xAC00}, []rune{0x1100, 0x1161}},
+ {[]rune{0x01C4}, []rune{0x01C4}},
+ {[]rune{0x320E}, []rune{0x320E}},
+ {[]rune("음ẻ과"), []rune{0x110B, 0x1173, 0x11B7, 0x65, 0x309, 0x1100, 0x116A}},
+}
+
+var decompositionNFKDTest = []TestCase{
+ {[]rune{0xC0}, []rune{0x41, 0x300}},
+ {[]rune{0xAC00}, []rune{0x1100, 0x1161}},
+ {[]rune{0x01C4}, []rune{0x44, 0x5A, 0x030C}},
+ {[]rune{0x320E}, []rune{0x28, 0x1100, 0x1161, 0x29}},
+}
+
+func TestDecomposition(t *testing.T) {
+ runTests(t, "TestDecompositionNFD", NFD, insert, decompositionNFDTest)
+ runTests(t, "TestDecompositionNFKD", NFKD, insert, decompositionNFKDTest)
+}
+
+var compositionTest = []TestCase{
+ {[]rune{0x41, 0x300}, []rune{0xC0}},
+ {[]rune{0x41, 0x316}, []rune{0x41, 0x316}},
+ {[]rune{0x41, 0x300, 0x35D}, []rune{0xC0, 0x35D}},
+ {[]rune{0x41, 0x316, 0x300}, []rune{0xC0, 0x316}},
+ // blocking starter
+ {[]rune{0x41, 0x316, 0x40, 0x300}, []rune{0x41, 0x316, 0x40, 0x300}},
+ {[]rune{0x1100, 0x1161}, []rune{0xAC00}},
+ // parenthesized Hangul, alternate between ASCII and Hangul.
+ {[]rune{0x28, 0x1100, 0x1161, 0x29}, []rune{0x28, 0xAC00, 0x29}},
+}
+
+func TestComposition(t *testing.T) {
+ runTests(t, "TestComposition", NFC, insert, compositionTest)
+}
diff --git a/src/pkg/exp/norm/example_iter_test.go b/src/pkg/exp/norm/example_iter_test.go
new file mode 100644
index 000000000..edb9fcf55
--- /dev/null
+++ b/src/pkg/exp/norm/example_iter_test.go
@@ -0,0 +1,81 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm_test
+
+import (
+ "bytes"
+ "exp/norm"
+ "fmt"
+ "unicode/utf8"
+)
+
+// EqualSimple uses a norm.Iter to compare two non-normalized
+// strings for equivalence.
+func EqualSimple(a, b string) bool {
+ var ia, ib norm.Iter
+ ia.InitString(norm.NFKD, a)
+ ib.InitString(norm.NFKD, b)
+ for !ia.Done() && !ib.Done() {
+ if !bytes.Equal(ia.Next(), ib.Next()) {
+ return false
+ }
+ }
+ return ia.Done() && ib.Done()
+}
+
+// FindPrefix finds the longest common prefix of ASCII characters
+// of a and b.
+func FindPrefix(a, b string) int {
+ i := 0
+ for ; i < len(a) && i < len(b) && a[i] < utf8.RuneSelf && a[i] == b[i]; i++ {
+ }
+ return i
+}
+
+// EqualOpt is like EqualSimple, but optimizes the special
+// case for ASCII characters.
+func EqualOpt(a, b string) bool {
+ n := FindPrefix(a, b)
+ a, b = a[n:], b[n:]
+ var ia, ib norm.Iter
+ ia.InitString(norm.NFKD, a)
+ ib.InitString(norm.NFKD, b)
+ for !ia.Done() && !ib.Done() {
+ if !bytes.Equal(ia.Next(), ib.Next()) {
+ return false
+ }
+ if n := int64(FindPrefix(a[ia.Pos():], b[ib.Pos():])); n != 0 {
+ ia.Seek(n, 1)
+ ib.Seek(n, 1)
+ }
+ }
+ return ia.Done() && ib.Done()
+}
+
+var compareTests = []struct{ a, b string }{
+ {"aaa", "aaa"},
+ {"aaa", "aab"},
+ {"a\u0300a", "\u00E0a"},
+ {"a\u0300\u0320b", "a\u0320\u0300b"},
+ {"\u1E0A\u0323", "\x44\u0323\u0307"},
+ // A character that decomposes into multiple segments
+ // spans several iterations.
+ {"\u3304", "\u30A4\u30CB\u30F3\u30AF\u3099"},
+}
+
+func ExampleIter() {
+ for i, t := range compareTests {
+ r0 := EqualSimple(t.a, t.b)
+ r1 := EqualOpt(t.a, t.b)
+ fmt.Printf("%d: %v %v\n", i, r0, r1)
+ }
+ // Output:
+ // 0: true true
+ // 1: false false
+ // 2: true true
+ // 3: true true
+ // 4: true true
+ // 5: true true
+}
diff --git a/src/pkg/exp/norm/forminfo.go b/src/pkg/exp/norm/forminfo.go
new file mode 100644
index 000000000..7f7ee72e8
--- /dev/null
+++ b/src/pkg/exp/norm/forminfo.go
@@ -0,0 +1,229 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+// This file contains Form-specific logic and wrappers for data in tables.go.
+
+// Rune info is stored in a separate trie per composing form. A composing form
+// and its corresponding decomposing form share the same trie. Each trie maps
+// a rune to a uint16. The values take two forms. For v >= 0x8000:
+// bits
+// 0..8: ccc
+// 9..12: qcInfo (see below). isYesD is always true (no decompostion).
+// 16: 1
+// For v < 0x8000, the respective rune has a decomposition and v is an index
+// into a byte array of UTF-8 decomposition sequences and additional info and
+// has the form:
+// <header> <decomp_byte>* [<tccc> [<lccc>]]
+// The header contains the number of bytes in the decomposition (excluding this
+// length byte). The two most significant bits of this length byte correspond
+// to bit 2 and 3 of qcIfo (see below). The byte sequence itself starts at v+1.
+// The byte sequence is followed by a trailing and leading CCC if the values
+// for these are not zero. The value of v determines which ccc are appended
+// to the sequences. For v < firstCCC, there are none, for v >= firstCCC,
+// the sequence is followed by a trailing ccc, and for v >= firstLeadingCC
+// there is an additional leading ccc.
+
+const (
+ qcInfoMask = 0xF // to clear all but the relevant bits in a qcInfo
+ headerLenMask = 0x3F // extract the length value from the header byte
+ headerFlagsMask = 0xC0 // extract the qcInfo bits from the header byte
+)
+
+// Properties provides access to normalization properties of a rune.
+type Properties struct {
+ pos uint8 // start position in reorderBuffer; used in composition.go
+ size uint8 // length of UTF-8 encoding of this rune
+ ccc uint8 // leading canonical combining class (ccc if not decomposition)
+ tccc uint8 // trailing canonical combining class (ccc if not decomposition)
+ flags qcInfo // quick check flags
+ index uint16
+}
+
+// functions dispatchable per form
+type lookupFunc func(b input, i int) Properties
+
+// formInfo holds Form-specific functions and tables.
+type formInfo struct {
+ form Form
+ composing, compatibility bool // form type
+ info lookupFunc
+ nextMain iterFunc
+}
+
+var formTable []*formInfo
+
+func init() {
+ formTable = make([]*formInfo, 4)
+
+ for i := range formTable {
+ f := &formInfo{}
+ formTable[i] = f
+ f.form = Form(i)
+ if Form(i) == NFKD || Form(i) == NFKC {
+ f.compatibility = true
+ f.info = lookupInfoNFKC
+ } else {
+ f.info = lookupInfoNFC
+ }
+ f.nextMain = nextDecomposed
+ if Form(i) == NFC || Form(i) == NFKC {
+ f.nextMain = nextComposed
+ f.composing = true
+ }
+ }
+}
+
+// We do not distinguish between boundaries for NFC, NFD, etc. to avoid
+// unexpected behavior for the user. For example, in NFD, there is a boundary
+// after 'a'. However, 'a' might combine with modifiers, so from the application's
+// perspective it is not a good boundary. We will therefore always use the
+// boundaries for the combining variants.
+
+// BoundaryBefore returns true if this rune starts a new segment and
+// cannot combine with any rune on the left.
+func (p Properties) BoundaryBefore() bool {
+ if p.ccc == 0 && !p.combinesBackward() {
+ return true
+ }
+ // We assume that the CCC of the first character in a decomposition
+ // is always non-zero if different from info.ccc and that we can return
+ // false at this point. This is verified by maketables.
+ return false
+}
+
+// BoundaryAfter returns true if this rune cannot combine with runes to the right
+// and always denotes the end of a segment.
+func (p Properties) BoundaryAfter() bool {
+ return p.isInert()
+}
+
+// We pack quick check data in 4 bits:
+// 0: NFD_QC Yes (0) or No (1). No also means there is a decomposition.
+// 1..2: NFC_QC Yes(00), No (10), or Maybe (11)
+// 3: Combines forward (0 == false, 1 == true)
+//
+// When all 4 bits are zero, the character is inert, meaning it is never
+// influenced by normalization.
+type qcInfo uint8
+
+func (p Properties) isYesC() bool { return p.flags&0x4 == 0 }
+func (p Properties) isYesD() bool { return p.flags&0x1 == 0 }
+
+func (p Properties) combinesForward() bool { return p.flags&0x8 != 0 }
+func (p Properties) combinesBackward() bool { return p.flags&0x2 != 0 } // == isMaybe
+func (p Properties) hasDecomposition() bool { return p.flags&0x1 != 0 } // == isNoD
+
+func (p Properties) isInert() bool {
+ return p.flags&0xf == 0 && p.ccc == 0
+}
+
+func (p Properties) multiSegment() bool {
+ return p.index >= firstMulti && p.index < endMulti
+}
+
+// Decomposition returns the decomposition for the underlying rune
+// or nil if there is none.
+func (p Properties) Decomposition() []byte {
+ if p.index == 0 {
+ return nil
+ }
+ i := p.index
+ n := decomps[i] & headerLenMask
+ i++
+ return decomps[i : i+uint16(n)]
+}
+
+// Size returns the length of UTF-8 encoding of the rune.
+func (p Properties) Size() int {
+ return int(p.size)
+}
+
+// CCC returns the canonical combining class of the underlying rune.
+func (p Properties) CCC() uint8 {
+ if p.index > firstCCCZeroExcept {
+ return 0
+ }
+ return p.ccc
+}
+
+// LeadCCC returns the CCC of the first rune in the decomposition.
+// If there is no decomposition, LeadCCC equals CCC.
+func (p Properties) LeadCCC() uint8 {
+ return p.ccc
+}
+
+// TrailCCC returns the CCC of the last rune in the decomposition.
+// If there is no decomposition, TrailCCC equals CCC.
+func (p Properties) TrailCCC() uint8 {
+ return p.tccc
+}
+
+// Recomposition
+// We use 32-bit keys instead of 64-bit for the two codepoint keys.
+// This clips off the bits of three entries, but we know this will not
+// result in a collision. In the unlikely event that changes to
+// UnicodeData.txt introduce collisions, the compiler will catch it.
+// Note that the recomposition map for NFC and NFKC are identical.
+
+// combine returns the combined rune or 0 if it doesn't exist.
+func combine(a, b rune) rune {
+ key := uint32(uint16(a))<<16 + uint32(uint16(b))
+ return recompMap[key]
+}
+
+func lookupInfoNFC(b input, i int) Properties {
+ v, sz := b.charinfoNFC(i)
+ return compInfo(v, sz)
+}
+
+func lookupInfoNFKC(b input, i int) Properties {
+ v, sz := b.charinfoNFKC(i)
+ return compInfo(v, sz)
+}
+
+// Properties returns properties for the first rune in s.
+func (f Form) Properties(s []byte) Properties {
+ if f == NFC || f == NFD {
+ return compInfo(nfcTrie.lookup(s))
+ }
+ return compInfo(nfkcTrie.lookup(s))
+}
+
+// PropertiesString returns properties for the first rune in s.
+func (f Form) PropertiesString(s string) Properties {
+ if f == NFC || f == NFD {
+ return compInfo(nfcTrie.lookupString(s))
+ }
+ return compInfo(nfkcTrie.lookupString(s))
+}
+
+// compInfo converts the information contained in v and sz
+// to a Properties. See the comment at the top of the file
+// for more information on the format.
+func compInfo(v uint16, sz int) Properties {
+ if v == 0 {
+ return Properties{size: uint8(sz)}
+ } else if v >= 0x8000 {
+ return Properties{
+ size: uint8(sz),
+ ccc: uint8(v),
+ tccc: uint8(v),
+ flags: qcInfo(v>>8) & qcInfoMask,
+ }
+ }
+ // has decomposition
+ h := decomps[v]
+ f := (qcInfo(h&headerFlagsMask) >> 4) | 0x1
+ ri := Properties{size: uint8(sz), flags: f, index: v}
+ if v >= firstCCC {
+ v += uint16(h&headerLenMask) + 1
+ ri.tccc = decomps[v]
+ if v >= firstLeadingCCC {
+ ri.ccc = decomps[v+1]
+ }
+ }
+ return ri
+}
diff --git a/src/pkg/exp/norm/input.go b/src/pkg/exp/norm/input.go
new file mode 100644
index 000000000..d0177a14a
--- /dev/null
+++ b/src/pkg/exp/norm/input.go
@@ -0,0 +1,105 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import "unicode/utf8"
+
+type input struct {
+ str string
+ bytes []byte
+}
+
+func inputBytes(str []byte) input {
+ return input{bytes: str}
+}
+
+func inputString(str string) input {
+ return input{str: str}
+}
+
+func (in *input) setBytes(str []byte) {
+ in.str = ""
+ in.bytes = str
+}
+
+func (in *input) setString(str string) {
+ in.str = str
+ in.bytes = nil
+}
+
+func (in *input) _byte(p int) byte {
+ if in.bytes == nil {
+ return in.str[p]
+ }
+ return in.bytes[p]
+}
+
+func (in *input) skipASCII(p, max int) int {
+ if in.bytes == nil {
+ for ; p < max && in.str[p] < utf8.RuneSelf; p++ {
+ }
+ } else {
+ for ; p < max && in.bytes[p] < utf8.RuneSelf; p++ {
+ }
+ }
+ return p
+}
+
+func (in *input) skipNonStarter(p int) int {
+ if in.bytes == nil {
+ for ; p < len(in.str) && !utf8.RuneStart(in.str[p]); p++ {
+ }
+ } else {
+ for ; p < len(in.bytes) && !utf8.RuneStart(in.bytes[p]); p++ {
+ }
+ }
+ return p
+}
+
+func (in *input) appendSlice(buf []byte, b, e int) []byte {
+ if in.bytes != nil {
+ return append(buf, in.bytes[b:e]...)
+ }
+ for i := b; i < e; i++ {
+ buf = append(buf, in.str[i])
+ }
+ return buf
+}
+
+func (in *input) copySlice(buf []byte, b, e int) int {
+ if in.bytes == nil {
+ return copy(buf, in.str[b:e])
+ }
+ return copy(buf, in.bytes[b:e])
+}
+
+func (in *input) charinfoNFC(p int) (uint16, int) {
+ if in.bytes == nil {
+ return nfcTrie.lookupString(in.str[p:])
+ }
+ return nfcTrie.lookup(in.bytes[p:])
+}
+
+func (in *input) charinfoNFKC(p int) (uint16, int) {
+ if in.bytes == nil {
+ return nfkcTrie.lookupString(in.str[p:])
+ }
+ return nfkcTrie.lookup(in.bytes[p:])
+}
+
+func (in *input) hangul(p int) (r rune) {
+ if in.bytes == nil {
+ if !isHangulString(in.str[p:]) {
+ return 0
+ }
+ r, _ = utf8.DecodeRuneInString(in.str[p:])
+ } else {
+ if !isHangul(in.bytes[p:]) {
+ return 0
+ }
+ r, _ = utf8.DecodeRune(in.bytes[p:])
+ }
+ return r
+}
diff --git a/src/pkg/exp/norm/iter.go b/src/pkg/exp/norm/iter.go
new file mode 100644
index 000000000..a9546247c
--- /dev/null
+++ b/src/pkg/exp/norm/iter.go
@@ -0,0 +1,401 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import (
+ "fmt"
+ "unicode/utf8"
+)
+
+const MaxSegmentSize = maxByteBufferSize
+
+// An Iter iterates over a string or byte slice, while normalizing it
+// to a given Form.
+type Iter struct {
+ rb reorderBuffer
+ buf [maxByteBufferSize]byte
+ info Properties // first character saved from previous iteration
+ next iterFunc // implementation of next depends on form
+ asciiF iterFunc
+
+ p int // current position in input source
+ multiSeg []byte // remainder of multi-segment decomposition
+}
+
+type iterFunc func(*Iter) []byte
+
+// Init initializes i to iterate over src after normalizing it to Form f.
+func (i *Iter) Init(f Form, src []byte) {
+ i.p = 0
+ if len(src) == 0 {
+ i.setDone()
+ i.rb.nsrc = 0
+ return
+ }
+ i.multiSeg = nil
+ i.rb.init(f, src)
+ i.next = i.rb.f.nextMain
+ i.asciiF = nextASCIIBytes
+ i.info = i.rb.f.info(i.rb.src, i.p)
+}
+
+// InitString initializes i to iterate over src after normalizing it to Form f.
+func (i *Iter) InitString(f Form, src string) {
+ i.p = 0
+ if len(src) == 0 {
+ i.setDone()
+ i.rb.nsrc = 0
+ return
+ }
+ i.multiSeg = nil
+ i.rb.initString(f, src)
+ i.next = i.rb.f.nextMain
+ i.asciiF = nextASCIIString
+ i.info = i.rb.f.info(i.rb.src, i.p)
+}
+
+// Seek sets the segment to be returned by the next call to Next to start
+// at position p. It is the responsibility of the caller to set p to the
+// start of a UTF8 rune.
+func (i *Iter) Seek(offset int64, whence int) (int64, error) {
+ var abs int64
+ switch whence {
+ case 0:
+ abs = offset
+ case 1:
+ abs = int64(i.p) + offset
+ case 2:
+ abs = int64(i.rb.nsrc) + offset
+ default:
+ return 0, fmt.Errorf("norm: invalid whence")
+ }
+ if abs < 0 {
+ return 0, fmt.Errorf("norm: negative position")
+ }
+ if int(abs) >= i.rb.nsrc {
+ i.setDone()
+ return int64(i.p), nil
+ }
+ i.p = int(abs)
+ i.multiSeg = nil
+ i.next = i.rb.f.nextMain
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ return abs, nil
+}
+
+// returnSlice returns a slice of the underlying input type as a byte slice.
+// If the underlying is of type []byte, it will simply return a slice.
+// If the underlying is of type string, it will copy the slice to the buffer
+// and return that.
+func (i *Iter) returnSlice(a, b int) []byte {
+ if i.rb.src.bytes == nil {
+ return i.buf[:copy(i.buf[:], i.rb.src.str[a:b])]
+ }
+ return i.rb.src.bytes[a:b]
+}
+
+// Pos returns the byte position at which the next call to Next will commence processing.
+func (i *Iter) Pos() int {
+ return i.p
+}
+
+func (i *Iter) setDone() {
+ i.next = nextDone
+ i.p = i.rb.nsrc
+}
+
+// Done returns true if there is no more input to process.
+func (i *Iter) Done() bool {
+ return i.p >= i.rb.nsrc
+}
+
+// Next returns f(i.input[i.Pos():n]), where n is a boundary of i.input.
+// For any input a and b for which f(a) == f(b), subsequent calls
+// to Next will return the same segments.
+// Modifying runes are grouped together with the preceding starter, if such a starter exists.
+// Although not guaranteed, n will typically be the smallest possible n.
+func (i *Iter) Next() []byte {
+ return i.next(i)
+}
+
+func nextASCIIBytes(i *Iter) []byte {
+ p := i.p + 1
+ if p >= i.rb.nsrc {
+ i.setDone()
+ return i.rb.src.bytes[i.p:p]
+ }
+ if i.rb.src.bytes[p] < utf8.RuneSelf {
+ p0 := i.p
+ i.p = p
+ return i.rb.src.bytes[p0:p]
+ }
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ i.next = i.rb.f.nextMain
+ return i.next(i)
+}
+
+func nextASCIIString(i *Iter) []byte {
+ p := i.p + 1
+ if p >= i.rb.nsrc {
+ i.buf[0] = i.rb.src.str[i.p]
+ i.setDone()
+ return i.buf[:1]
+ }
+ if i.rb.src.str[p] < utf8.RuneSelf {
+ i.buf[0] = i.rb.src.str[i.p]
+ i.p = p
+ return i.buf[:1]
+ }
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ i.next = i.rb.f.nextMain
+ return i.next(i)
+}
+
+func nextHangul(i *Iter) []byte {
+ if r := i.rb.src.hangul(i.p); r != 0 {
+ i.p += hangulUTF8Size
+ if i.p >= i.rb.nsrc {
+ i.setDone()
+ }
+ return i.buf[:decomposeHangul(i.buf[:], r)]
+ }
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ i.next = i.rb.f.nextMain
+ return i.next(i)
+}
+
+func nextDone(i *Iter) []byte {
+ return nil
+}
+
+// nextMulti is used for iterating over multi-segment decompositions
+// for decomposing normal forms.
+func nextMulti(i *Iter) []byte {
+ j := 0
+ d := i.multiSeg
+ // skip first rune
+ for j = 1; j < len(d) && !utf8.RuneStart(d[j]); j++ {
+ }
+ for j < len(d) {
+ info := i.rb.f.info(input{bytes: d}, j)
+ if info.ccc == 0 {
+ i.multiSeg = d[j:]
+ return d[:j]
+ }
+ j += int(info.size)
+ }
+ // treat last segment as normal decomposition
+ i.next = i.rb.f.nextMain
+ return i.next(i)
+}
+
+// nextMultiNorm is used for iterating over multi-segment decompositions
+// for composing normal forms.
+func nextMultiNorm(i *Iter) []byte {
+ j := 0
+ d := i.multiSeg
+ // skip first rune
+ for j = 1; j < len(d) && !utf8.RuneStart(d[j]); j++ {
+ }
+ for j < len(d) {
+ info := i.rb.f.info(input{bytes: d}, j)
+ if info.ccc == 0 {
+ i.multiSeg = d[j:]
+ return d[:j]
+ }
+ j += int(info.size)
+ }
+ i.multiSeg = nil
+ i.next = nextComposed
+ i.p++ // restore old valud of i.p. See nextComposed.
+ if i.p >= i.rb.nsrc {
+ i.setDone()
+ }
+ return d
+}
+
+// nextDecomposed is the implementation of Next for forms NFD and NFKD.
+func nextDecomposed(i *Iter) (next []byte) {
+ startp, outp := i.p, 0
+ inCopyStart, outCopyStart := i.p, 0
+ for {
+ if sz := int(i.info.size); sz <= 1 {
+ p := i.p
+ i.p++ // ASCII or illegal byte. Either way, advance by 1.
+ if i.p >= i.rb.nsrc {
+ i.setDone()
+ return i.returnSlice(p, i.p)
+ } else if i.rb.src._byte(i.p) < utf8.RuneSelf {
+ i.next = i.asciiF
+ return i.returnSlice(p, i.p)
+ }
+ outp++
+ } else if d := i.info.Decomposition(); d != nil {
+ // Note: If leading CCC != 0, then len(d) == 2 and last is also non-zero.
+ // Case 1: there is a leftover to copy. In this case the decomposition
+ // must begin with a modifier and should always be appended.
+ // Case 2: no leftover. Simply return d if followed by a ccc == 0 value.
+ p := outp + len(d)
+ if outp > 0 {
+ i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p)
+ if p > len(i.buf) {
+ return i.buf[:outp]
+ }
+ } else if i.info.multiSegment() {
+ // outp must be 0 as multi-segment decompositions always
+ // start a new segment.
+ if i.multiSeg == nil {
+ i.multiSeg = d
+ i.next = nextMulti
+ return nextMulti(i)
+ }
+ // We are in the last segment. Treat as normal decomposition.
+ d = i.multiSeg
+ i.multiSeg = nil
+ p = len(d)
+ }
+ prevCC := i.info.tccc
+ if i.p += sz; i.p >= i.rb.nsrc {
+ i.setDone()
+ i.info = Properties{} // Force BoundaryBefore to succeed.
+ } else {
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ }
+ if i.info.BoundaryBefore() {
+ if outp > 0 {
+ copy(i.buf[outp:], d)
+ return i.buf[:p]
+ }
+ return d
+ }
+ copy(i.buf[outp:], d)
+ outp = p
+ inCopyStart, outCopyStart = i.p, outp
+ if i.info.ccc < prevCC {
+ goto doNorm
+ }
+ continue
+ } else if r := i.rb.src.hangul(i.p); r != 0 {
+ i.next = nextHangul
+ i.p += hangulUTF8Size
+ if i.p >= i.rb.nsrc {
+ i.setDone()
+ }
+ return i.buf[:decomposeHangul(i.buf[:], r)]
+ } else {
+ p := outp + sz
+ if p > len(i.buf) {
+ break
+ }
+ outp = p
+ i.p += sz
+ }
+ if i.p >= i.rb.nsrc {
+ i.setDone()
+ break
+ }
+ prevCC := i.info.tccc
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ if i.info.BoundaryBefore() {
+ break
+ } else if i.info.ccc < prevCC {
+ goto doNorm
+ }
+ }
+ if outCopyStart == 0 {
+ return i.returnSlice(inCopyStart, i.p)
+ } else if inCopyStart < i.p {
+ i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p)
+ }
+ return i.buf[:outp]
+doNorm:
+ // Insert what we have decomposed so far in the reorderBuffer.
+ // As we will only reorder, there will always be enough room.
+ i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p)
+ if !i.rb.insertDecomposed(i.buf[0:outp]) {
+ // Start over to prevent decompositions from crossing segment boundaries.
+ // This is a rare occurrence.
+ i.p = startp
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ }
+ for {
+ if !i.rb.insert(i.rb.src, i.p, i.info) {
+ break
+ }
+ if i.p += int(i.info.size); i.p >= i.rb.nsrc {
+ i.setDone()
+ break
+ }
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ if i.info.ccc == 0 {
+ break
+ }
+ }
+ // new segment or too many combining characters: exit normalization
+ return i.buf[:i.rb.flushCopy(i.buf[:])]
+}
+
+// nextComposed is the implementation of Next for forms NFC and NFKC.
+func nextComposed(i *Iter) []byte {
+ outp, startp := 0, i.p
+ var prevCC uint8
+ for {
+ if !i.info.isYesC() {
+ goto doNorm
+ }
+ if cc := i.info.ccc; cc == 0 && outp > 0 {
+ break
+ } else if cc < prevCC {
+ goto doNorm
+ }
+ prevCC = i.info.tccc
+ sz := int(i.info.size)
+ if sz == 0 {
+ sz = 1 // illegal rune: copy byte-by-byte
+ }
+ p := outp + sz
+ if p > len(i.buf) {
+ break
+ }
+ outp = p
+ i.p += sz
+ if i.p >= i.rb.nsrc {
+ i.setDone()
+ break
+ } else if i.rb.src._byte(i.p) < utf8.RuneSelf {
+ i.next = i.asciiF
+ break
+ }
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ }
+ return i.returnSlice(startp, i.p)
+doNorm:
+ multi := false
+ i.p = startp
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ for {
+ if !i.rb.insert(i.rb.src, i.p, i.info) {
+ break
+ }
+ multi = multi || i.info.multiSegment()
+ if i.p += int(i.info.size); i.p >= i.rb.nsrc {
+ i.setDone()
+ break
+ }
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ if i.info.BoundaryBefore() {
+ break
+ }
+ }
+ i.rb.compose()
+ seg := i.buf[:i.rb.flushCopy(i.buf[:])]
+ if multi {
+ i.p-- // fake not being done yet
+ i.multiSeg = seg
+ i.next = nextMultiNorm
+ return nextMultiNorm(i)
+ }
+ return seg
+}
diff --git a/src/pkg/exp/norm/iter_test.go b/src/pkg/exp/norm/iter_test.go
new file mode 100644
index 000000000..826119362
--- /dev/null
+++ b/src/pkg/exp/norm/iter_test.go
@@ -0,0 +1,188 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import (
+ "strings"
+ "testing"
+)
+
+func doIterNorm(f Form, s string) []byte {
+ acc := []byte{}
+ i := Iter{}
+ i.InitString(f, s)
+ for !i.Done() {
+ acc = append(acc, i.Next()...)
+ }
+ return acc
+}
+
+func runIterTests(t *testing.T, name string, f Form, tests []AppendTest, norm bool) {
+ for i, test := range tests {
+ in := test.left + test.right
+ gold := test.out
+ if norm {
+ gold = string(f.AppendString(nil, test.out))
+ }
+ out := string(doIterNorm(f, in))
+ if len(out) != len(gold) {
+ const msg = "%s:%d: length is %d; want %d"
+ t.Errorf(msg, name, i, len(out), len(gold))
+ }
+ if out != gold {
+ // Find first rune that differs and show context.
+ ir := []rune(out)
+ ig := []rune(gold)
+ t.Errorf("\n%X != \n%X", ir, ig)
+ for j := 0; j < len(ir) && j < len(ig); j++ {
+ if ir[j] == ig[j] {
+ continue
+ }
+ if j -= 3; j < 0 {
+ j = 0
+ }
+ for e := j + 7; j < e && j < len(ir) && j < len(ig); j++ {
+ const msg = "%s:%d: runeAt(%d) = %U; want %U"
+ t.Errorf(msg, name, i, j, ir[j], ig[j])
+ }
+ break
+ }
+ }
+ }
+}
+
+func rep(r rune, n int) string {
+ return strings.Repeat(string(r), n)
+}
+
+const segSize = maxByteBufferSize
+
+var iterTests = []AppendTest{
+ {"", ascii, ascii},
+ {"", txt_all, txt_all},
+ {"", "a" + rep(0x0300, segSize/2), "a" + rep(0x0300, segSize/2)},
+}
+
+var iterTestsD = []AppendTest{
+ { // segment overflow on unchanged character
+ "",
+ "a" + rep(0x0300, segSize/2) + "\u0316",
+ "a" + rep(0x0300, segSize/2-1) + "\u0316\u0300",
+ },
+ { // segment overflow on unchanged character + start value
+ "",
+ "a" + rep(0x0300, segSize/2+maxCombiningChars+4) + "\u0316",
+ "a" + rep(0x0300, segSize/2+maxCombiningChars) + "\u0316" + rep(0x300, 4),
+ },
+ { // segment overflow on decomposition
+ "",
+ "a" + rep(0x0300, segSize/2-1) + "\u0340",
+ "a" + rep(0x0300, segSize/2),
+ },
+ { // segment overflow on decomposition + start value
+ "",
+ "a" + rep(0x0300, segSize/2-1) + "\u0340" + rep(0x300, maxCombiningChars+4) + "\u0320",
+ "a" + rep(0x0300, segSize/2-1) + rep(0x300, maxCombiningChars+1) + "\u0320" + rep(0x300, 4),
+ },
+ { // start value after ASCII overflow
+ "",
+ rep('a', segSize) + rep(0x300, maxCombiningChars+2) + "\u0320",
+ rep('a', segSize) + rep(0x300, maxCombiningChars) + "\u0320\u0300\u0300",
+ },
+ { // start value after Hangul overflow
+ "",
+ rep(0xAC00, segSize/6) + rep(0x300, maxCombiningChars+2) + "\u0320",
+ strings.Repeat("\u1100\u1161", segSize/6) + rep(0x300, maxCombiningChars+1) + "\u0320" + rep(0x300, 1),
+ },
+ { // start value after cc=0
+ "",
+ "您您" + rep(0x300, maxCombiningChars+4) + "\u0320",
+ "您您" + rep(0x300, maxCombiningChars) + "\u0320" + rep(0x300, 4),
+ },
+ { // start value after normalization
+ "",
+ "\u0300\u0320a" + rep(0x300, maxCombiningChars+4) + "\u0320",
+ "\u0320\u0300a" + rep(0x300, maxCombiningChars) + "\u0320" + rep(0x300, 4),
+ },
+}
+
+var iterTestsC = []AppendTest{
+ { // ordering of non-composing combining characters
+ "",
+ "\u0305\u0316",
+ "\u0316\u0305",
+ },
+ { // segment overflow
+ "",
+ "a" + rep(0x0305, segSize/2+4) + "\u0316",
+ "a" + rep(0x0305, segSize/2-1) + "\u0316" + rep(0x305, 5),
+ },
+}
+
+func TestIterNextD(t *testing.T) {
+ runIterTests(t, "IterNextD1", NFKD, appendTests, true)
+ runIterTests(t, "IterNextD2", NFKD, iterTests, true)
+ runIterTests(t, "IterNextD3", NFKD, iterTestsD, false)
+}
+
+func TestIterNextC(t *testing.T) {
+ runIterTests(t, "IterNextC1", NFKC, appendTests, true)
+ runIterTests(t, "IterNextC2", NFKC, iterTests, true)
+ runIterTests(t, "IterNextC3", NFKC, iterTestsC, false)
+}
+
+type SegmentTest struct {
+ in string
+ out []string
+}
+
+var segmentTests = []SegmentTest{
+ {"\u1E0A\u0323a", []string{"\x44\u0323\u0307", "a", ""}},
+ {rep('a', segSize), append(strings.Split(rep('a', segSize), ""), "")},
+ {rep('a', segSize+2), append(strings.Split(rep('a', segSize+2), ""), "")},
+ {rep('a', segSize) + "\u0300aa",
+ append(strings.Split(rep('a', segSize-1), ""), "a\u0300", "a", "a", "")},
+}
+
+var segmentTestsK = []SegmentTest{
+ {"\u3332", []string{"\u30D5", "\u30A1", "\u30E9", "\u30C3", "\u30C8\u3099", ""}},
+ // last segment of multi-segment decomposition needs normalization
+ {"\u3332\u093C", []string{"\u30D5", "\u30A1", "\u30E9", "\u30C3", "\u30C8\u093C\u3099", ""}},
+ // Hangul and Jamo are grouped togeter.
+ {"\uAC00", []string{"\u1100\u1161", ""}},
+ {"\uAC01", []string{"\u1100\u1161\u11A8", ""}},
+ {"\u1100\u1161", []string{"\u1100\u1161", ""}},
+}
+
+// Note that, by design, segmentation is equal for composing and decomposing forms.
+func TestIterSegmentation(t *testing.T) {
+ segmentTest(t, "SegmentTestD", NFD, segmentTests)
+ segmentTest(t, "SegmentTestC", NFC, segmentTests)
+ segmentTest(t, "SegmentTestD", NFKD, segmentTestsK)
+ segmentTest(t, "SegmentTestC", NFKC, segmentTestsK)
+}
+
+func segmentTest(t *testing.T, name string, f Form, tests []SegmentTest) {
+ iter := Iter{}
+ for i, tt := range tests {
+ iter.InitString(f, tt.in)
+ for j, seg := range tt.out {
+ if seg == "" {
+ if !iter.Done() {
+ res := string(iter.Next())
+ t.Errorf(`%s:%d:%d: expected Done()==true, found segment "%s"`, name, i, j, res)
+ }
+ continue
+ }
+ if iter.Done() {
+ t.Errorf("%s:%d:%d: Done()==true, want false", name, i, j)
+ }
+ seg = f.String(seg)
+ if res := string(iter.Next()); res != seg {
+ t.Errorf(`%s:%d:%d" segment was "%s" (%d); want "%s" (%d) %X %X`, name, i, j, res, len(res), seg, len(seg), []rune(res), []rune(seg))
+ }
+ }
+ }
+}
diff --git a/src/pkg/exp/norm/maketables.go b/src/pkg/exp/norm/maketables.go
new file mode 100644
index 000000000..50c0c310a
--- /dev/null
+++ b/src/pkg/exp/norm/maketables.go
@@ -0,0 +1,923 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// Normalization table generator.
+// Data read from the web.
+// See forminfo.go for a description of the trie values associated with each rune.
+
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "unicode"
+)
+
+func main() {
+ flag.Parse()
+ loadUnicodeData()
+ loadCompositionExclusions()
+ completeCharFields(FCanonical)
+ completeCharFields(FCompatibility)
+ verifyComputed()
+ printChars()
+ makeTables()
+ testDerived()
+}
+
+var url = flag.String("url",
+ "http://www.unicode.org/Public/"+unicode.Version+"/ucd/",
+ "URL of Unicode database directory")
+var tablelist = flag.String("tables",
+ "all",
+ "comma-separated list of which tables to generate; "+
+ "can be 'decomp', 'recomp', 'info' and 'all'")
+var test = flag.Bool("test",
+ false,
+ "test existing tables; can be used to compare web data with package data")
+var verbose = flag.Bool("verbose",
+ false,
+ "write data to stdout as it is parsed")
+var localFiles = flag.Bool("local",
+ false,
+ "data files have been copied to the current directory; for debugging only")
+
+var logger = log.New(os.Stderr, "", log.Lshortfile)
+
+// UnicodeData.txt has form:
+// 0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;;
+// 007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A
+// See http://unicode.org/reports/tr44/ for full explanation
+// The fields:
+const (
+ FCodePoint = iota
+ FName
+ FGeneralCategory
+ FCanonicalCombiningClass
+ FBidiClass
+ FDecompMapping
+ FDecimalValue
+ FDigitValue
+ FNumericValue
+ FBidiMirrored
+ FUnicode1Name
+ FISOComment
+ FSimpleUppercaseMapping
+ FSimpleLowercaseMapping
+ FSimpleTitlecaseMapping
+ NumField
+
+ MaxChar = 0x10FFFF // anything above this shouldn't exist
+)
+
+// Quick Check properties of runes allow us to quickly
+// determine whether a rune may occur in a normal form.
+// For a given normal form, a rune may be guaranteed to occur
+// verbatim (QC=Yes), may or may not combine with another
+// rune (QC=Maybe), or may not occur (QC=No).
+type QCResult int
+
+const (
+ QCUnknown QCResult = iota
+ QCYes
+ QCNo
+ QCMaybe
+)
+
+func (r QCResult) String() string {
+ switch r {
+ case QCYes:
+ return "Yes"
+ case QCNo:
+ return "No"
+ case QCMaybe:
+ return "Maybe"
+ }
+ return "***UNKNOWN***"
+}
+
+const (
+ FCanonical = iota // NFC or NFD
+ FCompatibility // NFKC or NFKD
+ FNumberOfFormTypes
+)
+
+const (
+ MComposed = iota // NFC or NFKC
+ MDecomposed // NFD or NFKD
+ MNumberOfModes
+)
+
+// This contains only the properties we're interested in.
+type Char struct {
+ name string
+ codePoint rune // if zero, this index is not a valid code point.
+ ccc uint8 // canonical combining class
+ excludeInComp bool // from CompositionExclusions.txt
+ compatDecomp bool // it has a compatibility expansion
+
+ forms [FNumberOfFormTypes]FormInfo // For FCanonical and FCompatibility
+
+ state State
+}
+
+var chars = make([]Char, MaxChar+1)
+
+func (c Char) String() string {
+ buf := new(bytes.Buffer)
+
+ fmt.Fprintf(buf, "%U [%s]:\n", c.codePoint, c.name)
+ fmt.Fprintf(buf, " ccc: %v\n", c.ccc)
+ fmt.Fprintf(buf, " excludeInComp: %v\n", c.excludeInComp)
+ fmt.Fprintf(buf, " compatDecomp: %v\n", c.compatDecomp)
+ fmt.Fprintf(buf, " state: %v\n", c.state)
+ fmt.Fprintf(buf, " NFC:\n")
+ fmt.Fprint(buf, c.forms[FCanonical])
+ fmt.Fprintf(buf, " NFKC:\n")
+ fmt.Fprint(buf, c.forms[FCompatibility])
+
+ return buf.String()
+}
+
+// In UnicodeData.txt, some ranges are marked like this:
+// 3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;;
+// 4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;;
+// parseCharacter keeps a state variable indicating the weirdness.
+type State int
+
+const (
+ SNormal State = iota // known to be zero for the type
+ SFirst
+ SLast
+ SMissing
+)
+
+var lastChar = rune('\u0000')
+
+func (c Char) isValid() bool {
+ return c.codePoint != 0 && c.state != SMissing
+}
+
+type FormInfo struct {
+ quickCheck [MNumberOfModes]QCResult // index: MComposed or MDecomposed
+ verified [MNumberOfModes]bool // index: MComposed or MDecomposed
+
+ combinesForward bool // May combine with rune on the right
+ combinesBackward bool // May combine with rune on the left
+ isOneWay bool // Never appears in result
+ inDecomp bool // Some decompositions result in this char.
+ decomp Decomposition
+ expandedDecomp Decomposition
+}
+
+func (f FormInfo) String() string {
+ buf := bytes.NewBuffer(make([]byte, 0))
+
+ fmt.Fprintf(buf, " quickCheck[C]: %v\n", f.quickCheck[MComposed])
+ fmt.Fprintf(buf, " quickCheck[D]: %v\n", f.quickCheck[MDecomposed])
+ fmt.Fprintf(buf, " cmbForward: %v\n", f.combinesForward)
+ fmt.Fprintf(buf, " cmbBackward: %v\n", f.combinesBackward)
+ fmt.Fprintf(buf, " isOneWay: %v\n", f.isOneWay)
+ fmt.Fprintf(buf, " inDecomp: %v\n", f.inDecomp)
+ fmt.Fprintf(buf, " decomposition: %X\n", f.decomp)
+ fmt.Fprintf(buf, " expandedDecomp: %X\n", f.expandedDecomp)
+
+ return buf.String()
+}
+
+type Decomposition []rune
+
+func openReader(file string) (input io.ReadCloser) {
+ if *localFiles {
+ f, err := os.Open(file)
+ if err != nil {
+ logger.Fatal(err)
+ }
+ input = f
+ } else {
+ path := *url + file
+ resp, err := http.Get(path)
+ if err != nil {
+ logger.Fatal(err)
+ }
+ if resp.StatusCode != 200 {
+ logger.Fatal("bad GET status for "+file, resp.Status)
+ }
+ input = resp.Body
+ }
+ return
+}
+
+func parseDecomposition(s string, skipfirst bool) (a []rune, e error) {
+ decomp := strings.Split(s, " ")
+ if len(decomp) > 0 && skipfirst {
+ decomp = decomp[1:]
+ }
+ for _, d := range decomp {
+ point, err := strconv.ParseUint(d, 16, 64)
+ if err != nil {
+ return a, err
+ }
+ a = append(a, rune(point))
+ }
+ return a, nil
+}
+
+func parseCharacter(line string) {
+ field := strings.Split(line, ";")
+ if len(field) != NumField {
+ logger.Fatalf("%5s: %d fields (expected %d)\n", line, len(field), NumField)
+ }
+ x, err := strconv.ParseUint(field[FCodePoint], 16, 64)
+ point := int(x)
+ if err != nil {
+ logger.Fatalf("%.5s...: %s", line, err)
+ }
+ if point == 0 {
+ return // not interesting and we use 0 as unset
+ }
+ if point > MaxChar {
+ logger.Fatalf("%5s: Rune %X > MaxChar (%X)", line, point, MaxChar)
+ return
+ }
+ state := SNormal
+ switch {
+ case strings.Index(field[FName], ", First>") > 0:
+ state = SFirst
+ case strings.Index(field[FName], ", Last>") > 0:
+ state = SLast
+ }
+ firstChar := lastChar + 1
+ lastChar = rune(point)
+ if state != SLast {
+ firstChar = lastChar
+ }
+ x, err = strconv.ParseUint(field[FCanonicalCombiningClass], 10, 64)
+ if err != nil {
+ logger.Fatalf("%U: bad ccc field: %s", int(x), err)
+ }
+ ccc := uint8(x)
+ decmap := field[FDecompMapping]
+ exp, e := parseDecomposition(decmap, false)
+ isCompat := false
+ if e != nil {
+ if len(decmap) > 0 {
+ exp, e = parseDecomposition(decmap, true)
+ if e != nil {
+ logger.Fatalf(`%U: bad decomp |%v|: "%s"`, int(x), decmap, e)
+ }
+ isCompat = true
+ }
+ }
+ for i := firstChar; i <= lastChar; i++ {
+ char := &chars[i]
+ char.name = field[FName]
+ char.codePoint = i
+ char.forms[FCompatibility].decomp = exp
+ if !isCompat {
+ char.forms[FCanonical].decomp = exp
+ } else {
+ char.compatDecomp = true
+ }
+ if len(decmap) > 0 {
+ char.forms[FCompatibility].decomp = exp
+ }
+ char.ccc = ccc
+ char.state = SMissing
+ if i == lastChar {
+ char.state = state
+ }
+ }
+ return
+}
+
+func loadUnicodeData() {
+ f := openReader("UnicodeData.txt")
+ defer f.Close()
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ parseCharacter(scanner.Text())
+ }
+ if scanner.Err() != nil {
+ logger.Fatal(scanner.Err())
+ }
+}
+
+var singlePointRe = regexp.MustCompile(`^([0-9A-F]+) *$`)
+
+// CompositionExclusions.txt has form:
+// 0958 # ...
+// See http://unicode.org/reports/tr44/ for full explanation
+func parseExclusion(line string) int {
+ comment := strings.Index(line, "#")
+ if comment >= 0 {
+ line = line[0:comment]
+ }
+ if len(line) == 0 {
+ return 0
+ }
+ matches := singlePointRe.FindStringSubmatch(line)
+ if len(matches) != 2 {
+ logger.Fatalf("%s: %d matches (expected 1)\n", line, len(matches))
+ }
+ point, err := strconv.ParseUint(matches[1], 16, 64)
+ if err != nil {
+ logger.Fatalf("%.5s...: %s", line, err)
+ }
+ return int(point)
+}
+
+func loadCompositionExclusions() {
+ f := openReader("CompositionExclusions.txt")
+ defer f.Close()
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ point := parseExclusion(scanner.Text())
+ if point == 0 {
+ continue
+ }
+ c := &chars[point]
+ if c.excludeInComp {
+ logger.Fatalf("%U: Duplicate entry in exclusions.", c.codePoint)
+ }
+ c.excludeInComp = true
+ }
+ if scanner.Err() != nil {
+ log.Fatal(scanner.Err())
+ }
+}
+
+// hasCompatDecomp returns true if any of the recursive
+// decompositions contains a compatibility expansion.
+// In this case, the character may not occur in NFK*.
+func hasCompatDecomp(r rune) bool {
+ c := &chars[r]
+ if c.compatDecomp {
+ return true
+ }
+ for _, d := range c.forms[FCompatibility].decomp {
+ if hasCompatDecomp(d) {
+ return true
+ }
+ }
+ return false
+}
+
+// Hangul related constants.
+const (
+ HangulBase = 0xAC00
+ HangulEnd = 0xD7A4 // hangulBase + Jamo combinations (19 * 21 * 28)
+
+ JamoLBase = 0x1100
+ JamoLEnd = 0x1113
+ JamoVBase = 0x1161
+ JamoVEnd = 0x1176
+ JamoTBase = 0x11A8
+ JamoTEnd = 0x11C3
+)
+
+func isHangul(r rune) bool {
+ return HangulBase <= r && r < HangulEnd
+}
+
+func ccc(r rune) uint8 {
+ return chars[r].ccc
+}
+
+// Insert a rune in a buffer, ordered by Canonical Combining Class.
+func insertOrdered(b Decomposition, r rune) Decomposition {
+ n := len(b)
+ b = append(b, 0)
+ cc := ccc(r)
+ if cc > 0 {
+ // Use bubble sort.
+ for ; n > 0; n-- {
+ if ccc(b[n-1]) <= cc {
+ break
+ }
+ b[n] = b[n-1]
+ }
+ }
+ b[n] = r
+ return b
+}
+
+// Recursively decompose.
+func decomposeRecursive(form int, r rune, d Decomposition) Decomposition {
+ if isHangul(r) {
+ return d
+ }
+ dcomp := chars[r].forms[form].decomp
+ if len(dcomp) == 0 {
+ return insertOrdered(d, r)
+ }
+ for _, c := range dcomp {
+ d = decomposeRecursive(form, c, d)
+ }
+ return d
+}
+
+func completeCharFields(form int) {
+ // Phase 0: pre-expand decomposition.
+ for i := range chars {
+ f := &chars[i].forms[form]
+ if len(f.decomp) == 0 {
+ continue
+ }
+ exp := make(Decomposition, 0)
+ for _, c := range f.decomp {
+ exp = decomposeRecursive(form, c, exp)
+ }
+ f.expandedDecomp = exp
+ }
+
+ // Phase 1: composition exclusion, mark decomposition.
+ for i := range chars {
+ c := &chars[i]
+ f := &c.forms[form]
+
+ // Marks script-specific exclusions and version restricted.
+ f.isOneWay = c.excludeInComp
+
+ // Singletons
+ f.isOneWay = f.isOneWay || len(f.decomp) == 1
+
+ // Non-starter decompositions
+ if len(f.decomp) > 1 {
+ chk := c.ccc != 0 || chars[f.decomp[0]].ccc != 0
+ f.isOneWay = f.isOneWay || chk
+ }
+
+ // Runes that decompose into more than two runes.
+ f.isOneWay = f.isOneWay || len(f.decomp) > 2
+
+ if form == FCompatibility {
+ f.isOneWay = f.isOneWay || hasCompatDecomp(c.codePoint)
+ }
+
+ for _, r := range f.decomp {
+ chars[r].forms[form].inDecomp = true
+ }
+ }
+
+ // Phase 2: forward and backward combining.
+ for i := range chars {
+ c := &chars[i]
+ f := &c.forms[form]
+
+ if !f.isOneWay && len(f.decomp) == 2 {
+ f0 := &chars[f.decomp[0]].forms[form]
+ f1 := &chars[f.decomp[1]].forms[form]
+ if !f0.isOneWay {
+ f0.combinesForward = true
+ }
+ if !f1.isOneWay {
+ f1.combinesBackward = true
+ }
+ }
+ }
+
+ // Phase 3: quick check values.
+ for i := range chars {
+ c := &chars[i]
+ f := &c.forms[form]
+
+ switch {
+ case len(f.decomp) > 0:
+ f.quickCheck[MDecomposed] = QCNo
+ case isHangul(rune(i)):
+ f.quickCheck[MDecomposed] = QCNo
+ default:
+ f.quickCheck[MDecomposed] = QCYes
+ }
+ switch {
+ case f.isOneWay:
+ f.quickCheck[MComposed] = QCNo
+ case (i & 0xffff00) == JamoLBase:
+ f.quickCheck[MComposed] = QCYes
+ if JamoLBase <= i && i < JamoLEnd {
+ f.combinesForward = true
+ }
+ if JamoVBase <= i && i < JamoVEnd {
+ f.quickCheck[MComposed] = QCMaybe
+ f.combinesBackward = true
+ f.combinesForward = true
+ }
+ if JamoTBase <= i && i < JamoTEnd {
+ f.quickCheck[MComposed] = QCMaybe
+ f.combinesBackward = true
+ }
+ case !f.combinesBackward:
+ f.quickCheck[MComposed] = QCYes
+ default:
+ f.quickCheck[MComposed] = QCMaybe
+ }
+ }
+}
+
+func printBytes(b []byte, name string) {
+ fmt.Printf("// %s: %d bytes\n", name, len(b))
+ fmt.Printf("var %s = [...]byte {", name)
+ for i, c := range b {
+ switch {
+ case i%64 == 0:
+ fmt.Printf("\n// Bytes %x - %x\n", i, i+63)
+ case i%8 == 0:
+ fmt.Printf("\n")
+ }
+ fmt.Printf("0x%.2X, ", c)
+ }
+ fmt.Print("\n}\n\n")
+}
+
+// See forminfo.go for format.
+func makeEntry(f *FormInfo) uint16 {
+ e := uint16(0)
+ if f.combinesForward {
+ e |= 0x8
+ }
+ if f.quickCheck[MDecomposed] == QCNo {
+ e |= 0x1
+ }
+ switch f.quickCheck[MComposed] {
+ case QCYes:
+ case QCNo:
+ e |= 0x4
+ case QCMaybe:
+ e |= 0x6
+ default:
+ log.Fatalf("Illegal quickcheck value %v.", f.quickCheck[MComposed])
+ }
+ return e
+}
+
+// decompSet keeps track of unique decompositions, grouped by whether
+// the decomposition is followed by a trailing and/or leading CCC.
+type decompSet [6]map[string]bool
+
+const (
+ normalDecomp = iota
+ firstMulti
+ firstCCC
+ endMulti
+ firstLeadingCCC
+ firstCCCZeroExcept
+ lastDecomp
+)
+
+var cname = []string{"firstMulti", "firstCCC", "endMulti", "firstLeadingCCC", "firstCCCZeroExcept", "lastDecomp"}
+
+func makeDecompSet() decompSet {
+ m := decompSet{}
+ for i := range m {
+ m[i] = make(map[string]bool)
+ }
+ return m
+}
+func (m *decompSet) insert(key int, s string) {
+ m[key][s] = true
+}
+
+func printCharInfoTables() int {
+ mkstr := func(r rune, f *FormInfo) (int, string) {
+ d := f.expandedDecomp
+ s := string([]rune(d))
+ if max := 1 << 6; len(s) >= max {
+ const msg = "%U: too many bytes in decomposition: %d >= %d"
+ logger.Fatalf(msg, r, len(s), max)
+ }
+ head := uint8(len(s))
+ if f.quickCheck[MComposed] != QCYes {
+ head |= 0x40
+ }
+ if f.combinesForward {
+ head |= 0x80
+ }
+ s = string([]byte{head}) + s
+
+ lccc := ccc(d[0])
+ tccc := ccc(d[len(d)-1])
+ cc := ccc(r)
+ if cc != 0 && lccc == 0 && tccc == 0 {
+ logger.Fatalf("%U: trailing and leading ccc are 0 for non-zero ccc %d", r, cc)
+ }
+ if tccc < lccc && lccc != 0 {
+ const msg = "%U: lccc (%d) must be <= tcc (%d)"
+ logger.Fatalf(msg, r, lccc, tccc)
+ }
+ index := normalDecomp
+ if tccc > 0 || lccc > 0 {
+ s += string([]byte{tccc})
+ index = endMulti
+ for _, r := range d[1:] {
+ if ccc(r) == 0 {
+ index = firstCCC
+ }
+ }
+ if lccc > 0 {
+ s += string([]byte{lccc})
+ if index == firstCCC {
+ logger.Fatalf("%U: multi-segment decomposition not supported for decompositions with leading CCC != 0", r)
+ }
+ index = firstLeadingCCC
+ }
+ if cc != lccc {
+ if cc != 0 {
+ logger.Fatalf("%U: for lccc != ccc, expected ccc to be 0; was %d", r, cc)
+ }
+ index = firstCCCZeroExcept
+ }
+ } else if len(d) > 1 {
+ index = firstMulti
+ }
+ return index, s
+ }
+
+ decompSet := makeDecompSet()
+
+ // Store the uniqued decompositions in a byte buffer,
+ // preceded by their byte length.
+ for _, c := range chars {
+ for _, f := range c.forms {
+ if len(f.expandedDecomp) == 0 {
+ continue
+ }
+ if f.combinesBackward {
+ logger.Fatalf("%U: combinesBackward and decompose", c.codePoint)
+ }
+ index, s := mkstr(c.codePoint, &f)
+ decompSet.insert(index, s)
+ }
+ }
+
+ decompositions := bytes.NewBuffer(make([]byte, 0, 10000))
+ size := 0
+ positionMap := make(map[string]uint16)
+ decompositions.WriteString("\000")
+ fmt.Println("const (")
+ for i, m := range decompSet {
+ sa := []string{}
+ for s := range m {
+ sa = append(sa, s)
+ }
+ sort.Strings(sa)
+ for _, s := range sa {
+ p := decompositions.Len()
+ decompositions.WriteString(s)
+ positionMap[s] = uint16(p)
+ }
+ if cname[i] != "" {
+ fmt.Printf("%s = 0x%X\n", cname[i], decompositions.Len())
+ }
+ }
+ fmt.Println("maxDecomp = 0x8000")
+ fmt.Println(")")
+ b := decompositions.Bytes()
+ printBytes(b, "decomps")
+ size += len(b)
+
+ varnames := []string{"nfc", "nfkc"}
+ for i := 0; i < FNumberOfFormTypes; i++ {
+ trie := newNode()
+ for r, c := range chars {
+ f := c.forms[i]
+ d := f.expandedDecomp
+ if len(d) != 0 {
+ _, key := mkstr(c.codePoint, &f)
+ trie.insert(rune(r), positionMap[key])
+ if c.ccc != ccc(d[0]) {
+ // We assume the lead ccc of a decomposition !=0 in this case.
+ if ccc(d[0]) == 0 {
+ logger.Fatalf("Expected leading CCC to be non-zero; ccc is %d", c.ccc)
+ }
+ }
+ } else if v := makeEntry(&f)<<8 | uint16(c.ccc); v != 0 {
+ trie.insert(c.codePoint, 0x8000|v)
+ }
+ }
+ size += trie.printTables(varnames[i])
+ }
+ return size
+}
+
+func contains(sa []string, s string) bool {
+ for _, a := range sa {
+ if a == s {
+ return true
+ }
+ }
+ return false
+}
+
+// Extract the version number from the URL.
+func version() string {
+ // From http://www.unicode.org/standard/versions/#Version_Numbering:
+ // for the later Unicode versions, data files are located in
+ // versioned directories.
+ fields := strings.Split(*url, "/")
+ for _, f := range fields {
+ if match, _ := regexp.MatchString(`[0-9]\.[0-9]\.[0-9]`, f); match {
+ return f
+ }
+ }
+ logger.Fatal("unknown version")
+ return "Unknown"
+}
+
+const fileHeader = `// Generated by running
+// maketables --tables=%s --url=%s
+// DO NOT EDIT
+
+package norm
+
+`
+
+func makeTables() {
+ size := 0
+ if *tablelist == "" {
+ return
+ }
+ list := strings.Split(*tablelist, ",")
+ if *tablelist == "all" {
+ list = []string{"recomp", "info"}
+ }
+ fmt.Printf(fileHeader, *tablelist, *url)
+
+ fmt.Println("// Version is the Unicode edition from which the tables are derived.")
+ fmt.Printf("const Version = %q\n\n", version())
+
+ if contains(list, "info") {
+ size += printCharInfoTables()
+ }
+
+ if contains(list, "recomp") {
+ // Note that we use 32 bit keys, instead of 64 bit.
+ // This clips the bits of three entries, but we know
+ // this won't cause a collision. The compiler will catch
+ // any changes made to UnicodeData.txt that introduces
+ // a collision.
+ // Note that the recomposition map for NFC and NFKC
+ // are identical.
+
+ // Recomposition map
+ nrentries := 0
+ for _, c := range chars {
+ f := c.forms[FCanonical]
+ if !f.isOneWay && len(f.decomp) > 0 {
+ nrentries++
+ }
+ }
+ sz := nrentries * 8
+ size += sz
+ fmt.Printf("// recompMap: %d bytes (entries only)\n", sz)
+ fmt.Println("var recompMap = map[uint32]rune{")
+ for i, c := range chars {
+ f := c.forms[FCanonical]
+ d := f.decomp
+ if !f.isOneWay && len(d) > 0 {
+ key := uint32(uint16(d[0]))<<16 + uint32(uint16(d[1]))
+ fmt.Printf("0x%.8X: 0x%.4X,\n", key, i)
+ }
+ }
+ fmt.Printf("}\n\n")
+ }
+
+ fmt.Printf("// Total size of tables: %dKB (%d bytes)\n", (size+512)/1024, size)
+}
+
+func printChars() {
+ if *verbose {
+ for _, c := range chars {
+ if !c.isValid() || c.state == SMissing {
+ continue
+ }
+ fmt.Println(c)
+ }
+ }
+}
+
+// verifyComputed does various consistency tests.
+func verifyComputed() {
+ for i, c := range chars {
+ for _, f := range c.forms {
+ isNo := (f.quickCheck[MDecomposed] == QCNo)
+ if (len(f.decomp) > 0) != isNo && !isHangul(rune(i)) {
+ log.Fatalf("%U: NF*D must be no if rune decomposes", i)
+ }
+
+ isMaybe := f.quickCheck[MComposed] == QCMaybe
+ if f.combinesBackward != isMaybe {
+ log.Fatalf("%U: NF*C must be maybe if combinesBackward", i)
+ }
+ }
+ nfc := c.forms[FCanonical]
+ nfkc := c.forms[FCompatibility]
+ if nfc.combinesBackward != nfkc.combinesBackward {
+ logger.Fatalf("%U: Cannot combine combinesBackward\n", c.codePoint)
+ }
+ }
+}
+
+var qcRe = regexp.MustCompile(`([0-9A-F\.]+) *; (NF.*_QC); ([YNM]) #.*`)
+
+// Use values in DerivedNormalizationProps.txt to compare against the
+// values we computed.
+// DerivedNormalizationProps.txt has form:
+// 00C0..00C5 ; NFD_QC; N # ...
+// 0374 ; NFD_QC; N # ...
+// See http://unicode.org/reports/tr44/ for full explanation
+func testDerived() {
+ if !*test {
+ return
+ }
+ f := openReader("DerivedNormalizationProps.txt")
+ defer f.Close()
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ line := scanner.Text()
+ qc := qcRe.FindStringSubmatch(line)
+ if qc == nil {
+ continue
+ }
+ rng := strings.Split(qc[1], "..")
+ i, err := strconv.ParseUint(rng[0], 16, 64)
+ if err != nil {
+ log.Fatal(err)
+ }
+ j := i
+ if len(rng) > 1 {
+ j, err = strconv.ParseUint(rng[1], 16, 64)
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ var ftype, mode int
+ qt := strings.TrimSpace(qc[2])
+ switch qt {
+ case "NFC_QC":
+ ftype, mode = FCanonical, MComposed
+ case "NFD_QC":
+ ftype, mode = FCanonical, MDecomposed
+ case "NFKC_QC":
+ ftype, mode = FCompatibility, MComposed
+ case "NFKD_QC":
+ ftype, mode = FCompatibility, MDecomposed
+ default:
+ log.Fatalf(`Unexpected quick check type "%s"`, qt)
+ }
+ var qr QCResult
+ switch qc[3] {
+ case "Y":
+ qr = QCYes
+ case "N":
+ qr = QCNo
+ case "M":
+ qr = QCMaybe
+ default:
+ log.Fatalf(`Unexpected quick check value "%s"`, qc[3])
+ }
+ var lastFailed bool
+ // Verify current
+ for ; i <= j; i++ {
+ c := &chars[int(i)]
+ c.forms[ftype].verified[mode] = true
+ curqr := c.forms[ftype].quickCheck[mode]
+ if curqr != qr {
+ if !lastFailed {
+ logger.Printf("%s: %.4X..%.4X -- %s\n",
+ qt, int(i), int(j), line[0:50])
+ }
+ logger.Printf("%U: FAILED %s (was %v need %v)\n",
+ int(i), qt, curqr, qr)
+ lastFailed = true
+ }
+ }
+ }
+ if scanner.Err() != nil {
+ logger.Fatal(scanner.Err())
+ }
+ // Any unspecified value must be QCYes. Verify this.
+ for i, c := range chars {
+ for j, fd := range c.forms {
+ for k, qr := range fd.quickCheck {
+ if !fd.verified[k] && qr != QCYes {
+ m := "%U: FAIL F:%d M:%d (was %v need Yes) %s\n"
+ logger.Printf(m, i, j, k, qr, c.name)
+ }
+ }
+ }
+ }
+}
diff --git a/src/pkg/exp/norm/maketesttables.go b/src/pkg/exp/norm/maketesttables.go
new file mode 100644
index 000000000..6d11ec069
--- /dev/null
+++ b/src/pkg/exp/norm/maketesttables.go
@@ -0,0 +1,45 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// Generate test data for trie code.
+
+package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ printTestTables()
+}
+
+// We take the smallest, largest and an arbitrary value for each
+// of the UTF-8 sequence lengths.
+var testRunes = []rune{
+ 0x01, 0x0C, 0x7F, // 1-byte sequences
+ 0x80, 0x100, 0x7FF, // 2-byte sequences
+ 0x800, 0x999, 0xFFFF, // 3-byte sequences
+ 0x10000, 0x10101, 0x10FFFF, // 4-byte sequences
+ 0x200, 0x201, 0x202, 0x210, 0x215, // five entries in one sparse block
+}
+
+const fileHeader = `// Generated by running
+// maketesttables
+// DO NOT EDIT
+
+package norm
+
+`
+
+func printTestTables() {
+ fmt.Print(fileHeader)
+ fmt.Printf("var testRunes = %#v\n\n", testRunes)
+ t := newNode()
+ for i, r := range testRunes {
+ t.insert(r, uint16(i))
+ }
+ t.printTables("testdata")
+}
diff --git a/src/pkg/exp/norm/norm_test.go b/src/pkg/exp/norm/norm_test.go
new file mode 100644
index 000000000..12dacfcf3
--- /dev/null
+++ b/src/pkg/exp/norm/norm_test.go
@@ -0,0 +1,14 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm_test
+
+import (
+ "testing"
+)
+
+func TestPlaceHolder(t *testing.T) {
+ // Does nothing, just allows the Makefile to be canonical
+ // while waiting for the package itself to be written.
+}
diff --git a/src/pkg/exp/norm/normalize.go b/src/pkg/exp/norm/normalize.go
new file mode 100644
index 000000000..1c3e49b77
--- /dev/null
+++ b/src/pkg/exp/norm/normalize.go
@@ -0,0 +1,478 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package norm contains types and functions for normalizing Unicode strings.
+package norm
+
+import "unicode/utf8"
+
+// A Form denotes a canonical representation of Unicode code points.
+// The Unicode-defined normalization and equivalence forms are:
+//
+// NFC Unicode Normalization Form C
+// NFD Unicode Normalization Form D
+// NFKC Unicode Normalization Form KC
+// NFKD Unicode Normalization Form KD
+//
+// For a Form f, this documentation uses the notation f(x) to mean
+// the bytes or string x converted to the given form.
+// A position n in x is called a boundary if conversion to the form can
+// proceed independently on both sides:
+// f(x) == append(f(x[0:n]), f(x[n:])...)
+//
+// References: http://unicode.org/reports/tr15/ and
+// http://unicode.org/notes/tn5/.
+type Form int
+
+const (
+ NFC Form = iota
+ NFD
+ NFKC
+ NFKD
+)
+
+// Bytes returns f(b). May return b if f(b) = b.
+func (f Form) Bytes(b []byte) []byte {
+ rb := reorderBuffer{}
+ rb.init(f, b)
+ n := quickSpan(&rb, 0)
+ if n == len(b) {
+ return b
+ }
+ out := make([]byte, n, len(b))
+ copy(out, b[0:n])
+ return doAppend(&rb, out, n)
+}
+
+// String returns f(s).
+func (f Form) String(s string) string {
+ rb := reorderBuffer{}
+ rb.initString(f, s)
+ n := quickSpan(&rb, 0)
+ if n == len(s) {
+ return s
+ }
+ out := make([]byte, n, len(s))
+ copy(out, s[0:n])
+ return string(doAppend(&rb, out, n))
+}
+
+// IsNormal returns true if b == f(b).
+func (f Form) IsNormal(b []byte) bool {
+ rb := reorderBuffer{}
+ rb.init(f, b)
+ bp := quickSpan(&rb, 0)
+ if bp == len(b) {
+ return true
+ }
+ for bp < len(b) {
+ decomposeSegment(&rb, bp)
+ if rb.f.composing {
+ rb.compose()
+ }
+ for i := 0; i < rb.nrune; i++ {
+ info := rb.rune[i]
+ if bp+int(info.size) > len(b) {
+ return false
+ }
+ p := info.pos
+ pe := p + info.size
+ for ; p < pe; p++ {
+ if b[bp] != rb.byte[p] {
+ return false
+ }
+ bp++
+ }
+ }
+ rb.reset()
+ bp = quickSpan(&rb, bp)
+ }
+ return true
+}
+
+// IsNormalString returns true if s == f(s).
+func (f Form) IsNormalString(s string) bool {
+ rb := reorderBuffer{}
+ rb.initString(f, s)
+ bp := quickSpan(&rb, 0)
+ if bp == len(s) {
+ return true
+ }
+ for bp < len(s) {
+ decomposeSegment(&rb, bp)
+ if rb.f.composing {
+ rb.compose()
+ }
+ for i := 0; i < rb.nrune; i++ {
+ info := rb.rune[i]
+ if bp+int(info.size) > len(s) {
+ return false
+ }
+ p := info.pos
+ pe := p + info.size
+ for ; p < pe; p++ {
+ if s[bp] != rb.byte[p] {
+ return false
+ }
+ bp++
+ }
+ }
+ rb.reset()
+ bp = quickSpan(&rb, bp)
+ }
+ return true
+}
+
+// patchTail fixes a case where a rune may be incorrectly normalized
+// if it is followed by illegal continuation bytes. It returns the
+// patched buffer and whether there were trailing continuation bytes.
+func patchTail(rb *reorderBuffer, buf []byte) ([]byte, bool) {
+ info, p := lastRuneStart(&rb.f, buf)
+ if p == -1 || info.size == 0 {
+ return buf, false
+ }
+ end := p + int(info.size)
+ extra := len(buf) - end
+ if extra > 0 {
+ // Potentially allocating memory. However, this only
+ // happens with ill-formed UTF-8.
+ x := make([]byte, 0)
+ x = append(x, buf[len(buf)-extra:]...)
+ buf = decomposeToLastBoundary(rb, buf[:end])
+ if rb.f.composing {
+ rb.compose()
+ }
+ buf = rb.flush(buf)
+ return append(buf, x...), true
+ }
+ return buf, false
+}
+
+func appendQuick(rb *reorderBuffer, dst []byte, i int) ([]byte, int) {
+ if rb.nsrc == i {
+ return dst, i
+ }
+ end := quickSpan(rb, i)
+ return rb.src.appendSlice(dst, i, end), end
+}
+
+// Append returns f(append(out, b...)).
+// The buffer out must be nil, empty, or equal to f(out).
+func (f Form) Append(out []byte, src ...byte) []byte {
+ if len(src) == 0 {
+ return out
+ }
+ rb := reorderBuffer{}
+ rb.init(f, src)
+ return doAppend(&rb, out, 0)
+}
+
+func doAppend(rb *reorderBuffer, out []byte, p int) []byte {
+ src, n := rb.src, rb.nsrc
+ doMerge := len(out) > 0
+ if q := src.skipNonStarter(p); q > p {
+ // Move leading non-starters to destination.
+ out = src.appendSlice(out, p, q)
+ buf, endsInError := patchTail(rb, out)
+ if endsInError {
+ out = buf
+ doMerge = false // no need to merge, ends with illegal UTF-8
+ } else {
+ out = decomposeToLastBoundary(rb, buf) // force decomposition
+ }
+ p = q
+ }
+ fd := &rb.f
+ if doMerge {
+ var info Properties
+ if p < n {
+ info = fd.info(src, p)
+ if p == 0 && !info.BoundaryBefore() {
+ out = decomposeToLastBoundary(rb, out)
+ }
+ }
+ if info.size == 0 || info.BoundaryBefore() {
+ if fd.composing {
+ rb.compose()
+ }
+ out = rb.flush(out)
+ if info.size == 0 {
+ // Append incomplete UTF-8 encoding.
+ return src.appendSlice(out, p, n)
+ }
+ }
+ }
+ if rb.nrune == 0 {
+ out, p = appendQuick(rb, out, p)
+ }
+ for p < n {
+ p = decomposeSegment(rb, p)
+ if fd.composing {
+ rb.compose()
+ }
+ out = rb.flush(out)
+ out, p = appendQuick(rb, out, p)
+ }
+ return out
+}
+
+// AppendString returns f(append(out, []byte(s))).
+// The buffer out must be nil, empty, or equal to f(out).
+func (f Form) AppendString(out []byte, src string) []byte {
+ if len(src) == 0 {
+ return out
+ }
+ rb := reorderBuffer{}
+ rb.initString(f, src)
+ return doAppend(&rb, out, 0)
+}
+
+// QuickSpan returns a boundary n such that b[0:n] == f(b[0:n]).
+// It is not guaranteed to return the largest such n.
+func (f Form) QuickSpan(b []byte) int {
+ rb := reorderBuffer{}
+ rb.init(f, b)
+ n := quickSpan(&rb, 0)
+ return n
+}
+
+func quickSpan(rb *reorderBuffer, i int) int {
+ var lastCC uint8
+ var nc int
+ lastSegStart := i
+ src, n := rb.src, rb.nsrc
+ for i < n {
+ if j := src.skipASCII(i, n); i != j {
+ i = j
+ lastSegStart = i - 1
+ lastCC = 0
+ nc = 0
+ continue
+ }
+ info := rb.f.info(src, i)
+ if info.size == 0 {
+ // include incomplete runes
+ return n
+ }
+ cc := info.ccc
+ if rb.f.composing {
+ if !info.isYesC() {
+ break
+ }
+ } else {
+ if !info.isYesD() {
+ break
+ }
+ }
+ if cc == 0 {
+ lastSegStart = i
+ nc = 0
+ } else {
+ if nc >= maxCombiningChars {
+ lastSegStart = i
+ lastCC = cc
+ nc = 1
+ } else {
+ if lastCC > cc {
+ return lastSegStart
+ }
+ nc++
+ }
+ }
+ lastCC = cc
+ i += int(info.size)
+ }
+ if i == n {
+ return n
+ }
+ if rb.f.composing {
+ return lastSegStart
+ }
+ return i
+}
+
+// QuickSpanString returns a boundary n such that b[0:n] == f(s[0:n]).
+// It is not guaranteed to return the largest such n.
+func (f Form) QuickSpanString(s string) int {
+ rb := reorderBuffer{}
+ rb.initString(f, s)
+ return quickSpan(&rb, 0)
+}
+
+// FirstBoundary returns the position i of the first boundary in b
+// or -1 if b contains no boundary.
+func (f Form) FirstBoundary(b []byte) int {
+ rb := reorderBuffer{}
+ rb.init(f, b)
+ return firstBoundary(&rb)
+}
+
+func firstBoundary(rb *reorderBuffer) int {
+ src, nsrc := rb.src, rb.nsrc
+ i := src.skipNonStarter(0)
+ if i >= nsrc {
+ return -1
+ }
+ fd := &rb.f
+ info := fd.info(src, i)
+ for n := 0; info.size != 0 && !info.BoundaryBefore(); {
+ i += int(info.size)
+ if n++; n >= maxCombiningChars {
+ return i
+ }
+ if i >= nsrc {
+ if !info.BoundaryAfter() {
+ return -1
+ }
+ return nsrc
+ }
+ info = fd.info(src, i)
+ }
+ if info.size == 0 {
+ return -1
+ }
+ return i
+}
+
+// FirstBoundaryInString returns the position i of the first boundary in s
+// or -1 if s contains no boundary.
+func (f Form) FirstBoundaryInString(s string) int {
+ rb := reorderBuffer{}
+ rb.initString(f, s)
+ return firstBoundary(&rb)
+}
+
+// LastBoundary returns the position i of the last boundary in b
+// or -1 if b contains no boundary.
+func (f Form) LastBoundary(b []byte) int {
+ return lastBoundary(formTable[f], b)
+}
+
+func lastBoundary(fd *formInfo, b []byte) int {
+ i := len(b)
+ info, p := lastRuneStart(fd, b)
+ if p == -1 {
+ return -1
+ }
+ if info.size == 0 { // ends with incomplete rune
+ if p == 0 { // starts with incomplete rune
+ return -1
+ }
+ i = p
+ info, p = lastRuneStart(fd, b[:i])
+ if p == -1 { // incomplete UTF-8 encoding or non-starter bytes without a starter
+ return i
+ }
+ }
+ if p+int(info.size) != i { // trailing non-starter bytes: illegal UTF-8
+ return i
+ }
+ if info.BoundaryAfter() {
+ return i
+ }
+ i = p
+ for n := 0; i >= 0 && !info.BoundaryBefore(); {
+ info, p = lastRuneStart(fd, b[:i])
+ if n++; n >= maxCombiningChars {
+ return len(b)
+ }
+ if p+int(info.size) != i {
+ if p == -1 { // no boundary found
+ return -1
+ }
+ return i // boundary after an illegal UTF-8 encoding
+ }
+ i = p
+ }
+ return i
+}
+
+// decomposeSegment scans the first segment in src into rb.
+// It returns the number of bytes consumed from src.
+// TODO(mpvl): consider inserting U+034f (Combining Grapheme Joiner)
+// when we detect a sequence of 30+ non-starter chars.
+func decomposeSegment(rb *reorderBuffer, sp int) int {
+ // Force one character to be consumed.
+ info := rb.f.info(rb.src, sp)
+ if info.size == 0 {
+ return 0
+ }
+ for rb.insert(rb.src, sp, info) {
+ sp += int(info.size)
+ if sp >= rb.nsrc {
+ break
+ }
+ info = rb.f.info(rb.src, sp)
+ bound := info.BoundaryBefore()
+ if bound || info.size == 0 {
+ break
+ }
+ }
+ return sp
+}
+
+// lastRuneStart returns the runeInfo and position of the last
+// rune in buf or the zero runeInfo and -1 if no rune was found.
+func lastRuneStart(fd *formInfo, buf []byte) (Properties, int) {
+ p := len(buf) - 1
+ for ; p >= 0 && !utf8.RuneStart(buf[p]); p-- {
+ }
+ if p < 0 {
+ return Properties{}, -1
+ }
+ return fd.info(inputBytes(buf), p), p
+}
+
+// decomposeToLastBoundary finds an open segment at the end of the buffer
+// and scans it into rb. Returns the buffer minus the last segment.
+func decomposeToLastBoundary(rb *reorderBuffer, buf []byte) []byte {
+ fd := &rb.f
+ info, i := lastRuneStart(fd, buf)
+ if int(info.size) != len(buf)-i {
+ // illegal trailing continuation bytes
+ return buf
+ }
+ if info.BoundaryAfter() {
+ return buf
+ }
+ var add [maxBackRunes]Properties // stores runeInfo in reverse order
+ add[0] = info
+ padd := 1
+ n := 1
+ p := len(buf) - int(info.size)
+ for ; p >= 0 && !info.BoundaryBefore(); p -= int(info.size) {
+ info, i = lastRuneStart(fd, buf[:p])
+ if int(info.size) != p-i {
+ break
+ }
+ // Check that decomposition doesn't result in overflow.
+ if info.hasDecomposition() {
+ if isHangul(buf) {
+ i += int(info.size)
+ n++
+ } else {
+ dcomp := info.Decomposition()
+ for i := 0; i < len(dcomp); {
+ inf := rb.f.info(inputBytes(dcomp), i)
+ i += int(inf.size)
+ n++
+ }
+ }
+ } else {
+ n++
+ }
+ if n > maxBackRunes {
+ break
+ }
+ add[padd] = info
+ padd++
+ }
+ pp := p
+ for padd--; padd >= 0; padd-- {
+ info = add[padd]
+ rb.insert(inputBytes(buf), pp, info)
+ pp += int(info.size)
+ }
+ return buf[:p]
+}
diff --git a/src/pkg/exp/norm/normalize_test.go b/src/pkg/exp/norm/normalize_test.go
new file mode 100644
index 000000000..9a6b46e41
--- /dev/null
+++ b/src/pkg/exp/norm/normalize_test.go
@@ -0,0 +1,750 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import (
+ "bytes"
+ "io"
+ "strings"
+ "testing"
+)
+
+type PositionTest struct {
+ input string
+ pos int
+ buffer string // expected contents of reorderBuffer, if applicable
+}
+
+type positionFunc func(rb *reorderBuffer, s string) int
+
+func runPosTests(t *testing.T, name string, f Form, fn positionFunc, tests []PositionTest) {
+ rb := reorderBuffer{}
+ rb.init(f, nil)
+ for i, test := range tests {
+ rb.reset()
+ rb.src = inputString(test.input)
+ rb.nsrc = len(test.input)
+ pos := fn(&rb, test.input)
+ if pos != test.pos {
+ t.Errorf("%s:%d: position is %d; want %d", name, i, pos, test.pos)
+ }
+ runes := []rune(test.buffer)
+ if rb.nrune != len(runes) {
+ t.Errorf("%s:%d: reorder buffer length is %d; want %d", name, i, rb.nrune, len(runes))
+ continue
+ }
+ for j, want := range runes {
+ found := rune(rb.runeAt(j))
+ if found != want {
+ t.Errorf("%s:%d: rune at %d is %U; want %U", name, i, j, found, want)
+ }
+ }
+ }
+}
+
+var decomposeSegmentTests = []PositionTest{
+ // illegal runes
+ {"\xC0", 0, ""},
+ {"\u00E0\x80", 2, "\u0061\u0300"},
+ // starter
+ {"a", 1, "a"},
+ {"ab", 1, "a"},
+ // starter + composing
+ {"a\u0300", 3, "a\u0300"},
+ {"a\u0300b", 3, "a\u0300"},
+ // with decomposition
+ {"\u00C0", 2, "A\u0300"},
+ {"\u00C0b", 2, "A\u0300"},
+ // long
+ {strings.Repeat("\u0300", 31), 62, strings.Repeat("\u0300", 31)},
+ // ends with incomplete UTF-8 encoding
+ {"\xCC", 0, ""},
+ {"\u0300\xCC", 2, "\u0300"},
+}
+
+func decomposeSegmentF(rb *reorderBuffer, s string) int {
+ rb.src = inputString(s)
+ rb.nsrc = len(s)
+ return decomposeSegment(rb, 0)
+}
+
+func TestDecomposeSegment(t *testing.T) {
+ runPosTests(t, "TestDecomposeSegment", NFC, decomposeSegmentF, decomposeSegmentTests)
+}
+
+var firstBoundaryTests = []PositionTest{
+ // no boundary
+ {"", -1, ""},
+ {"\u0300", -1, ""},
+ {"\x80\x80", -1, ""},
+ // illegal runes
+ {"\xff", 0, ""},
+ {"\u0300\xff", 2, ""},
+ {"\u0300\xc0\x80\x80", 2, ""},
+ // boundaries
+ {"a", 0, ""},
+ {"\u0300a", 2, ""},
+ // Hangul
+ {"\u1103\u1161", 0, ""},
+ {"\u110B\u1173\u11B7", 0, ""},
+ {"\u1161\u110B\u1173\u11B7", 3, ""},
+ {"\u1173\u11B7\u1103\u1161", 6, ""},
+ // too many combining characters.
+ {strings.Repeat("\u0300", maxCombiningChars-1), -1, ""},
+ {strings.Repeat("\u0300", maxCombiningChars), 60, ""},
+ {strings.Repeat("\u0300", maxCombiningChars+1), 60, ""},
+}
+
+func firstBoundaryF(rb *reorderBuffer, s string) int {
+ return rb.f.form.FirstBoundary([]byte(s))
+}
+
+func firstBoundaryStringF(rb *reorderBuffer, s string) int {
+ return rb.f.form.FirstBoundaryInString(s)
+}
+
+func TestFirstBoundary(t *testing.T) {
+ runPosTests(t, "TestFirstBoundary", NFC, firstBoundaryF, firstBoundaryTests)
+ runPosTests(t, "TestFirstBoundaryInString", NFC, firstBoundaryStringF, firstBoundaryTests)
+}
+
+var decomposeToLastTests = []PositionTest{
+ // ends with inert character
+ {"Hello!", 6, ""},
+ {"\u0632", 2, ""},
+ {"a\u0301\u0635", 5, ""},
+ // ends with non-inert starter
+ {"a", 0, "a"},
+ {"a\u0301a", 3, "a"},
+ {"a\u0301\u03B9", 3, "\u03B9"},
+ {"a\u0327", 0, "a\u0327"},
+ // illegal runes
+ {"\xFF", 1, ""},
+ {"aa\xFF", 3, ""},
+ {"\xC0\x80\x80", 3, ""},
+ {"\xCC\x80\x80", 3, ""},
+ // ends with incomplete UTF-8 encoding
+ {"a\xCC", 2, ""},
+ // ends with combining characters
+ {"\u0300\u0301", 0, "\u0300\u0301"},
+ {"a\u0300\u0301", 0, "a\u0300\u0301"},
+ {"a\u0301\u0308", 0, "a\u0301\u0308"},
+ {"a\u0308\u0301", 0, "a\u0308\u0301"},
+ {"aaaa\u0300\u0301", 3, "a\u0300\u0301"},
+ {"\u0300a\u0300\u0301", 2, "a\u0300\u0301"},
+ {"\u00C0", 0, "A\u0300"},
+ {"a\u00C0", 1, "A\u0300"},
+ // decomposing
+ {"a\u0300\uFDC0", 3, "\u0645\u062C\u064A"},
+ {"\uFDC0" + strings.Repeat("\u0300", 26), 0, "\u0645\u062C\u064A" + strings.Repeat("\u0300", 26)},
+ // Hangul
+ {"a\u1103", 1, "\u1103"},
+ {"a\u110B", 1, "\u110B"},
+ {"a\u110B\u1173", 1, "\u110B\u1173"},
+ // See comment in composition.go:compBoundaryAfter.
+ {"a\u110B\u1173\u11B7", 1, "\u110B\u1173\u11B7"},
+ {"a\uC73C", 1, "\u110B\u1173"},
+ {"다음", 3, "\u110B\u1173\u11B7"},
+ {"다", 0, "\u1103\u1161"},
+ {"\u1103\u1161\u110B\u1173\u11B7", 6, "\u110B\u1173\u11B7"},
+ {"\u110B\u1173\u11B7\u1103\u1161", 9, "\u1103\u1161"},
+ {"다음음", 6, "\u110B\u1173\u11B7"},
+ {"음다다", 6, "\u1103\u1161"},
+ // buffer overflow
+ {"a" + strings.Repeat("\u0300", 30), 3, strings.Repeat("\u0300", 29)},
+ {"\uFDFA" + strings.Repeat("\u0300", 14), 3, strings.Repeat("\u0300", 14)},
+ // weird UTF-8
+ {"a\u0300\u11B7", 0, "a\u0300\u11B7"},
+}
+
+func decomposeToLast(rb *reorderBuffer, s string) int {
+ buf := decomposeToLastBoundary(rb, []byte(s))
+ return len(buf)
+}
+
+func TestDecomposeToLastBoundary(t *testing.T) {
+ runPosTests(t, "TestDecomposeToLastBoundary", NFKC, decomposeToLast, decomposeToLastTests)
+}
+
+var lastBoundaryTests = []PositionTest{
+ // ends with inert character
+ {"Hello!", 6, ""},
+ {"\u0632", 2, ""},
+ // ends with non-inert starter
+ {"a", 0, ""},
+ // illegal runes
+ {"\xff", 1, ""},
+ {"aa\xff", 3, ""},
+ {"a\xff\u0300", 1, ""},
+ {"\xc0\x80\x80", 3, ""},
+ {"\xc0\x80\x80\u0300", 3, ""},
+ // ends with incomplete UTF-8 encoding
+ {"\xCC", -1, ""},
+ {"\xE0\x80", -1, ""},
+ {"\xF0\x80\x80", -1, ""},
+ {"a\xCC", 0, ""},
+ {"\x80\xCC", 1, ""},
+ {"\xCC\xCC", 1, ""},
+ // ends with combining characters
+ {"a\u0300\u0301", 0, ""},
+ {"aaaa\u0300\u0301", 3, ""},
+ {"\u0300a\u0300\u0301", 2, ""},
+ {"\u00C0", 0, ""},
+ {"a\u00C0", 1, ""},
+ // decomposition may recombine
+ {"\u0226", 0, ""},
+ // no boundary
+ {"", -1, ""},
+ {"\u0300\u0301", -1, ""},
+ {"\u0300", -1, ""},
+ {"\x80\x80", -1, ""},
+ {"\x80\x80\u0301", -1, ""},
+ // Hangul
+ {"다음", 3, ""},
+ {"다", 0, ""},
+ {"\u1103\u1161\u110B\u1173\u11B7", 6, ""},
+ {"\u110B\u1173\u11B7\u1103\u1161", 9, ""},
+ // too many combining characters.
+ {strings.Repeat("\u0300", maxCombiningChars-1), -1, ""},
+ {strings.Repeat("\u0300", maxCombiningChars), 60, ""},
+ {strings.Repeat("\u0300", maxCombiningChars+1), 62, ""},
+}
+
+func lastBoundaryF(rb *reorderBuffer, s string) int {
+ return rb.f.form.LastBoundary([]byte(s))
+}
+
+func TestLastBoundary(t *testing.T) {
+ runPosTests(t, "TestLastBoundary", NFC, lastBoundaryF, lastBoundaryTests)
+}
+
+var quickSpanTests = []PositionTest{
+ {"", 0, ""},
+ // starters
+ {"a", 1, ""},
+ {"abc", 3, ""},
+ {"\u043Eb", 3, ""},
+ // incomplete last rune.
+ {"\xCC", 1, ""},
+ {"a\xCC", 2, ""},
+ // incorrectly ordered combining characters
+ {"\u0300\u0316", 0, ""},
+ {"\u0300\u0316cd", 0, ""},
+ // have a maximum number of combining characters.
+ {strings.Repeat("\u035D", 30) + "\u035B", 62, ""},
+ {"a" + strings.Repeat("\u035D", 30) + "\u035B", 63, ""},
+ {"Ɵ" + strings.Repeat("\u035D", 30) + "\u035B", 64, ""},
+ {"aa" + strings.Repeat("\u035D", 30) + "\u035B", 64, ""},
+}
+
+var quickSpanNFDTests = []PositionTest{
+ // needs decomposing
+ {"\u00C0", 0, ""},
+ {"abc\u00C0", 3, ""},
+ // correctly ordered combining characters
+ {"\u0300", 2, ""},
+ {"ab\u0300", 4, ""},
+ {"ab\u0300cd", 6, ""},
+ {"\u0300cd", 4, ""},
+ {"\u0316\u0300", 4, ""},
+ {"ab\u0316\u0300", 6, ""},
+ {"ab\u0316\u0300cd", 8, ""},
+ {"ab\u0316\u0300\u00C0", 6, ""},
+ {"\u0316\u0300cd", 6, ""},
+ {"\u043E\u0308b", 5, ""},
+ // incorrectly ordered combining characters
+ {"ab\u0300\u0316", 1, ""}, // TODO: we could skip 'b' as well.
+ {"ab\u0300\u0316cd", 1, ""},
+ // Hangul
+ {"같은", 0, ""},
+}
+
+var quickSpanNFCTests = []PositionTest{
+ // okay composed
+ {"\u00C0", 2, ""},
+ {"abc\u00C0", 5, ""},
+ // correctly ordered combining characters
+ {"ab\u0300", 1, ""},
+ {"ab\u0300cd", 1, ""},
+ {"ab\u0316\u0300", 1, ""},
+ {"ab\u0316\u0300cd", 1, ""},
+ {"\u00C0\u035D", 4, ""},
+ // we do not special case leading combining characters
+ {"\u0300cd", 0, ""},
+ {"\u0300", 0, ""},
+ {"\u0316\u0300", 0, ""},
+ {"\u0316\u0300cd", 0, ""},
+ // incorrectly ordered combining characters
+ {"ab\u0300\u0316", 1, ""},
+ {"ab\u0300\u0316cd", 1, ""},
+ // Hangul
+ {"같은", 6, ""},
+}
+
+func doQuickSpan(rb *reorderBuffer, s string) int {
+ return rb.f.form.QuickSpan([]byte(s))
+}
+
+func doQuickSpanString(rb *reorderBuffer, s string) int {
+ return rb.f.form.QuickSpanString(s)
+}
+
+func TestQuickSpan(t *testing.T) {
+ runPosTests(t, "TestQuickSpanNFD1", NFD, doQuickSpan, quickSpanTests)
+ runPosTests(t, "TestQuickSpanNFD2", NFD, doQuickSpan, quickSpanNFDTests)
+ runPosTests(t, "TestQuickSpanNFC1", NFC, doQuickSpan, quickSpanTests)
+ runPosTests(t, "TestQuickSpanNFC2", NFC, doQuickSpan, quickSpanNFCTests)
+
+ runPosTests(t, "TestQuickSpanStringNFD1", NFD, doQuickSpanString, quickSpanTests)
+ runPosTests(t, "TestQuickSpanStringNFD2", NFD, doQuickSpanString, quickSpanNFDTests)
+ runPosTests(t, "TestQuickSpanStringNFC1", NFC, doQuickSpanString, quickSpanTests)
+ runPosTests(t, "TestQuickSpanStringNFC2", NFC, doQuickSpanString, quickSpanNFCTests)
+}
+
+var isNormalTests = []PositionTest{
+ {"", 1, ""},
+ // illegal runes
+ {"\xff", 1, ""},
+ // starters
+ {"a", 1, ""},
+ {"abc", 1, ""},
+ {"\u043Eb", 1, ""},
+ // incorrectly ordered combining characters
+ {"\u0300\u0316", 0, ""},
+ {"ab\u0300\u0316", 0, ""},
+ {"ab\u0300\u0316cd", 0, ""},
+ {"\u0300\u0316cd", 0, ""},
+}
+var isNormalNFDTests = []PositionTest{
+ // needs decomposing
+ {"\u00C0", 0, ""},
+ {"abc\u00C0", 0, ""},
+ // correctly ordered combining characters
+ {"\u0300", 1, ""},
+ {"ab\u0300", 1, ""},
+ {"ab\u0300cd", 1, ""},
+ {"\u0300cd", 1, ""},
+ {"\u0316\u0300", 1, ""},
+ {"ab\u0316\u0300", 1, ""},
+ {"ab\u0316\u0300cd", 1, ""},
+ {"\u0316\u0300cd", 1, ""},
+ {"\u043E\u0308b", 1, ""},
+ // Hangul
+ {"같은", 0, ""},
+}
+var isNormalNFCTests = []PositionTest{
+ // okay composed
+ {"\u00C0", 1, ""},
+ {"abc\u00C0", 1, ""},
+ // need reordering
+ {"a\u0300", 0, ""},
+ {"a\u0300cd", 0, ""},
+ {"a\u0316\u0300", 0, ""},
+ {"a\u0316\u0300cd", 0, ""},
+ // correctly ordered combining characters
+ {"ab\u0300", 1, ""},
+ {"ab\u0300cd", 1, ""},
+ {"ab\u0316\u0300", 1, ""},
+ {"ab\u0316\u0300cd", 1, ""},
+ {"\u00C0\u035D", 1, ""},
+ {"\u0300", 1, ""},
+ {"\u0316\u0300cd", 1, ""},
+ // Hangul
+ {"같은", 1, ""},
+}
+
+func isNormalF(rb *reorderBuffer, s string) int {
+ if rb.f.form.IsNormal([]byte(s)) {
+ return 1
+ }
+ return 0
+}
+
+func TestIsNormal(t *testing.T) {
+ runPosTests(t, "TestIsNormalNFD1", NFD, isNormalF, isNormalTests)
+ runPosTests(t, "TestIsNormalNFD2", NFD, isNormalF, isNormalNFDTests)
+ runPosTests(t, "TestIsNormalNFC1", NFC, isNormalF, isNormalTests)
+ runPosTests(t, "TestIsNormalNFC2", NFC, isNormalF, isNormalNFCTests)
+}
+
+type AppendTest struct {
+ left string
+ right string
+ out string
+}
+
+type appendFunc func(f Form, out []byte, s string) []byte
+
+func runAppendTests(t *testing.T, name string, f Form, fn appendFunc, tests []AppendTest) {
+ for i, test := range tests {
+ out := []byte(test.left)
+ out = fn(f, out, test.right)
+ outs := string(out)
+ if len(outs) != len(test.out) {
+ t.Errorf("%s:%d: length is %d; want %d", name, i, len(outs), len(test.out))
+ }
+ if outs != test.out {
+ // Find first rune that differs and show context.
+ ir := []rune(outs)
+ ig := []rune(test.out)
+ for j := 0; j < len(ir) && j < len(ig); j++ {
+ if ir[j] == ig[j] {
+ continue
+ }
+ if j -= 3; j < 0 {
+ j = 0
+ }
+ for e := j + 7; j < e && j < len(ir) && j < len(ig); j++ {
+ t.Errorf("%s:%d: runeAt(%d) = %U; want %U", name, i, j, ir[j], ig[j])
+ }
+ break
+ }
+ }
+ }
+}
+
+var appendTests = []AppendTest{
+ // empty buffers
+ {"", "", ""},
+ {"a", "", "a"},
+ {"", "a", "a"},
+ {"", "\u0041\u0307\u0304", "\u01E0"},
+ // segment split across buffers
+ {"", "a\u0300b", "\u00E0b"},
+ {"a", "\u0300b", "\u00E0b"},
+ {"a", "\u0300\u0316", "\u00E0\u0316"},
+ {"a", "\u0316\u0300", "\u00E0\u0316"},
+ {"a", "\u0300a\u0300", "\u00E0\u00E0"},
+ {"a", "\u0300a\u0300a\u0300", "\u00E0\u00E0\u00E0"},
+ {"a", "\u0300aaa\u0300aaa\u0300", "\u00E0aa\u00E0aa\u00E0"},
+ {"a\u0300", "\u0327", "\u00E0\u0327"},
+ {"a\u0327", "\u0300", "\u00E0\u0327"},
+ {"a\u0316", "\u0300", "\u00E0\u0316"},
+ {"\u0041\u0307", "\u0304", "\u01E0"},
+ // Hangul
+ {"", "\u110B\u1173", "\uC73C"},
+ {"", "\u1103\u1161", "\uB2E4"},
+ {"", "\u110B\u1173\u11B7", "\uC74C"},
+ {"", "\u320E", "\x28\uAC00\x29"},
+ {"", "\x28\u1100\u1161\x29", "\x28\uAC00\x29"},
+ {"\u1103", "\u1161", "\uB2E4"},
+ {"\u110B", "\u1173\u11B7", "\uC74C"},
+ {"\u110B\u1173", "\u11B7", "\uC74C"},
+ {"\uC73C", "\u11B7", "\uC74C"},
+ // UTF-8 encoding split across buffers
+ {"a\xCC", "\x80", "\u00E0"},
+ {"a\xCC", "\x80b", "\u00E0b"},
+ {"a\xCC", "\x80a\u0300", "\u00E0\u00E0"},
+ {"a\xCC", "\x80\x80", "\u00E0\x80"},
+ {"a\xCC", "\x80\xCC", "\u00E0\xCC"},
+ {"a\u0316\xCC", "\x80a\u0316\u0300", "\u00E0\u0316\u00E0\u0316"},
+ // ending in incomplete UTF-8 encoding
+ {"", "\xCC", "\xCC"},
+ {"a", "\xCC", "a\xCC"},
+ {"a", "b\xCC", "ab\xCC"},
+ {"\u0226", "\xCC", "\u0226\xCC"},
+ // illegal runes
+ {"", "\x80", "\x80"},
+ {"", "\x80\x80\x80", "\x80\x80\x80"},
+ {"", "\xCC\x80\x80\x80", "\xCC\x80\x80\x80"},
+ {"", "a\x80", "a\x80"},
+ {"", "a\x80\x80\x80", "a\x80\x80\x80"},
+ {"", "a\x80\x80\x80\x80\x80\x80", "a\x80\x80\x80\x80\x80\x80"},
+ {"a", "\x80\x80\x80", "a\x80\x80\x80"},
+ // overflow
+ {"", strings.Repeat("\x80", 33), strings.Repeat("\x80", 33)},
+ {strings.Repeat("\x80", 33), "", strings.Repeat("\x80", 33)},
+ {strings.Repeat("\x80", 33), strings.Repeat("\x80", 33), strings.Repeat("\x80", 66)},
+ // overflow of combining characters
+ {strings.Repeat("\u0300", 33), "", strings.Repeat("\u0300", 33)},
+ // weird UTF-8
+ {"\u00E0\xE1", "\x86", "\u00E0\xE1\x86"},
+ {"a\u0300\u11B7", "\u0300", "\u00E0\u11B7\u0300"},
+ {"a\u0300\u11B7\u0300", "\u0300", "\u00E0\u11B7\u0300\u0300"},
+ {"\u0300", "\xF8\x80\x80\x80\x80\u0300", "\u0300\xF8\x80\x80\x80\x80\u0300"},
+ {"\u0300", "\xFC\x80\x80\x80\x80\x80\u0300", "\u0300\xFC\x80\x80\x80\x80\x80\u0300"},
+ {"\xF8\x80\x80\x80\x80\u0300", "\u0300", "\xF8\x80\x80\x80\x80\u0300\u0300"},
+ {"\xFC\x80\x80\x80\x80\x80\u0300", "\u0300", "\xFC\x80\x80\x80\x80\x80\u0300\u0300"},
+ {"\xF8\x80\x80\x80", "\x80\u0300\u0300", "\xF8\x80\x80\x80\x80\u0300\u0300"},
+}
+
+func appendF(f Form, out []byte, s string) []byte {
+ return f.Append(out, []byte(s)...)
+}
+
+func appendStringF(f Form, out []byte, s string) []byte {
+ return f.AppendString(out, s)
+}
+
+func bytesF(f Form, out []byte, s string) []byte {
+ buf := []byte{}
+ buf = append(buf, out...)
+ buf = append(buf, s...)
+ return f.Bytes(buf)
+}
+
+func stringF(f Form, out []byte, s string) []byte {
+ outs := string(out) + s
+ return []byte(f.String(outs))
+}
+
+func TestAppend(t *testing.T) {
+ runAppendTests(t, "TestAppend", NFKC, appendF, appendTests)
+ runAppendTests(t, "TestAppendString", NFKC, appendStringF, appendTests)
+ runAppendTests(t, "TestBytes", NFKC, bytesF, appendTests)
+ runAppendTests(t, "TestString", NFKC, stringF, appendTests)
+}
+
+func appendBench(f Form, in []byte) func() {
+ buf := make([]byte, 0, 4*len(in))
+ return func() {
+ f.Append(buf, in...)
+ }
+}
+
+func iterBench(f Form, in []byte) func() {
+ iter := Iter{}
+ return func() {
+ iter.Init(f, in)
+ for !iter.Done() {
+ iter.Next()
+ }
+ }
+}
+
+func readerBench(f Form, in []byte) func() {
+ buf := make([]byte, 4*len(in))
+ return func() {
+ r := f.Reader(bytes.NewReader(in))
+ var err error
+ for err == nil {
+ _, err = r.Read(buf)
+ }
+ if err != io.EOF {
+ panic("")
+ }
+ }
+}
+
+func writerBench(f Form, in []byte) func() {
+ buf := make([]byte, 0, 4*len(in))
+ return func() {
+ r := f.Writer(bytes.NewBuffer(buf))
+ if _, err := r.Write(in); err != nil {
+ panic("")
+ }
+ }
+}
+
+func appendBenchmarks(bm []func(), f Form, in []byte) []func() {
+ //bm = append(bm, appendBench(f, in))
+ bm = append(bm, iterBench(f, in))
+ //bm = append(bm, readerBench(f, in))
+ //bm = append(bm, writerBench(f, in))
+ return bm
+}
+
+func doFormBenchmark(b *testing.B, inf, f Form, s string) {
+ b.StopTimer()
+ in := inf.Bytes([]byte(s))
+ bm := appendBenchmarks(nil, f, in)
+ b.SetBytes(int64(len(in) * len(bm)))
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ for _, fn := range bm {
+ fn()
+ }
+ }
+}
+
+var ascii = strings.Repeat("There is nothing to change here! ", 500)
+
+func BenchmarkNormalizeAsciiNFC(b *testing.B) {
+ doFormBenchmark(b, NFC, NFC, ascii)
+}
+func BenchmarkNormalizeAsciiNFD(b *testing.B) {
+ doFormBenchmark(b, NFC, NFD, ascii)
+}
+func BenchmarkNormalizeAsciiNFKC(b *testing.B) {
+ doFormBenchmark(b, NFC, NFKC, ascii)
+}
+func BenchmarkNormalizeAsciiNFKD(b *testing.B) {
+ doFormBenchmark(b, NFC, NFKD, ascii)
+}
+
+func BenchmarkNormalizeNFC2NFC(b *testing.B) {
+ doFormBenchmark(b, NFC, NFC, txt_all)
+}
+func BenchmarkNormalizeNFC2NFD(b *testing.B) {
+ doFormBenchmark(b, NFC, NFD, txt_all)
+}
+func BenchmarkNormalizeNFD2NFC(b *testing.B) {
+ doFormBenchmark(b, NFD, NFC, txt_all)
+}
+func BenchmarkNormalizeNFD2NFD(b *testing.B) {
+ doFormBenchmark(b, NFD, NFD, txt_all)
+}
+
+// Hangul is often special-cased, so we test it separately.
+func BenchmarkNormalizeHangulNFC2NFC(b *testing.B) {
+ doFormBenchmark(b, NFC, NFC, txt_kr)
+}
+func BenchmarkNormalizeHangulNFC2NFD(b *testing.B) {
+ doFormBenchmark(b, NFC, NFD, txt_kr)
+}
+func BenchmarkNormalizeHangulNFD2NFC(b *testing.B) {
+ doFormBenchmark(b, NFD, NFC, txt_kr)
+}
+func BenchmarkNormalizeHangulNFD2NFD(b *testing.B) {
+ doFormBenchmark(b, NFD, NFD, txt_kr)
+}
+
+var forms = []Form{NFC, NFD, NFKC, NFKD}
+
+func doTextBenchmark(b *testing.B, s string) {
+ b.StopTimer()
+ in := []byte(s)
+ bm := []func(){}
+ for _, f := range forms {
+ bm = appendBenchmarks(bm, f, in)
+ }
+ b.SetBytes(int64(len(s) * len(bm)))
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ for _, f := range bm {
+ f()
+ }
+ }
+}
+
+func BenchmarkCanonicalOrdering(b *testing.B) {
+ doTextBenchmark(b, txt_canon)
+}
+func BenchmarkExtendedLatin(b *testing.B) {
+ doTextBenchmark(b, txt_vn)
+}
+func BenchmarkMiscTwoByteUtf8(b *testing.B) {
+ doTextBenchmark(b, twoByteUtf8)
+}
+func BenchmarkMiscThreeByteUtf8(b *testing.B) {
+ doTextBenchmark(b, threeByteUtf8)
+}
+func BenchmarkHangul(b *testing.B) {
+ doTextBenchmark(b, txt_kr)
+}
+func BenchmarkJapanese(b *testing.B) {
+ doTextBenchmark(b, txt_jp)
+}
+func BenchmarkChinese(b *testing.B) {
+ doTextBenchmark(b, txt_cn)
+}
+func BenchmarkOverflow(b *testing.B) {
+ doTextBenchmark(b, overflow)
+}
+
+var overflow = string(bytes.Repeat([]byte("\u035D"), 4096)) + "\u035B"
+
+// Tests sampled from the Canonical ordering tests (Part 2) of
+// http://unicode.org/Public/UNIDATA/NormalizationTest.txt
+const txt_canon = `\u0061\u0315\u0300\u05AE\u0300\u0062 \u0061\u0300\u0315\u0300\u05AE\u0062
+\u0061\u0302\u0315\u0300\u05AE\u0062 \u0061\u0307\u0315\u0300\u05AE\u0062
+\u0061\u0315\u0300\u05AE\u030A\u0062 \u0061\u059A\u0316\u302A\u031C\u0062
+\u0061\u032E\u059A\u0316\u302A\u0062 \u0061\u0338\u093C\u0334\u0062
+\u0061\u059A\u0316\u302A\u0339 \u0061\u0341\u0315\u0300\u05AE\u0062
+\u0061\u0348\u059A\u0316\u302A\u0062 \u0061\u0361\u0345\u035D\u035C\u0062
+\u0061\u0366\u0315\u0300\u05AE\u0062 \u0061\u0315\u0300\u05AE\u0486\u0062
+\u0061\u05A4\u059A\u0316\u302A\u0062 \u0061\u0315\u0300\u05AE\u0613\u0062
+\u0061\u0315\u0300\u05AE\u0615\u0062 \u0061\u0617\u0315\u0300\u05AE\u0062
+\u0061\u0619\u0618\u064D\u064E\u0062 \u0061\u0315\u0300\u05AE\u0654\u0062
+\u0061\u0315\u0300\u05AE\u06DC\u0062 \u0061\u0733\u0315\u0300\u05AE\u0062
+\u0061\u0744\u059A\u0316\u302A\u0062 \u0061\u0315\u0300\u05AE\u0745\u0062
+\u0061\u09CD\u05B0\u094D\u3099\u0062 \u0061\u0E38\u0E48\u0E38\u0C56\u0062
+\u0061\u0EB8\u0E48\u0E38\u0E49\u0062 \u0061\u0F72\u0F71\u0EC8\u0F71\u0062
+\u0061\u1039\u05B0\u094D\u3099\u0062 \u0061\u05B0\u094D\u3099\u1A60\u0062
+\u0061\u3099\u093C\u0334\u1BE6\u0062 \u0061\u3099\u093C\u0334\u1C37\u0062
+\u0061\u1CD9\u059A\u0316\u302A\u0062 \u0061\u2DED\u0315\u0300\u05AE\u0062
+\u0061\u2DEF\u0315\u0300\u05AE\u0062 \u0061\u302D\u302E\u059A\u0316\u0062`
+
+// Taken from http://creativecommons.org/licenses/by-sa/3.0/vn/
+const txt_vn = `Với các điều kiện sau: Ghi nhận công của tác giả.
+Nếu bạn sử dụng, chuyển đổi, hoặc xây dựng dự án từ
+nội dung được chia sẻ này, bạn phải áp dụng giấy phép này hoặc
+một giấy phép khác có các điều khoản tương tự như giấy phép này
+cho dự án của bạn. Hiểu rằng: Miễn — Bất kỳ các điều kiện nào
+trên đây cũng có thể được miễn bỏ nếu bạn được sự cho phép của
+người sở hữu bản quyền. Phạm vi công chúng — Khi tác phẩm hoặc
+bất kỳ chương nào của tác phẩm đã trong vùng dành cho công
+chúng theo quy định của pháp luật thì tình trạng của nó không
+bị ảnh hưởng bởi giấy phép trong bất kỳ trường hợp nào.`
+
+// Taken from http://creativecommons.org/licenses/by-sa/1.0/deed.ru
+const txt_ru = `При обязательном соблюдении следующих условий:
+Attribution — Вы должны атрибутировать произведение (указывать
+автора и источник) в порядке, предусмотренном автором или
+лицензиаром (но только так, чтобы никоим образом не подразумевалось,
+что они поддерживают вас или использование вами данного произведения).
+Υπό τις ακόλουθες προϋποθέσεις:`
+
+// Taken from http://creativecommons.org/licenses/by-sa/3.0/gr/
+const txt_gr = `Αναφορά Δημιουργού — Θα πρέπει να κάνετε την αναφορά στο έργο με τον
+τρόπο που έχει οριστεί από το δημιουργό ή το χορηγούντο την άδεια
+(χωρίς όμως να εννοείται με οποιονδήποτε τρόπο ότι εγκρίνουν εσάς ή
+τη χρήση του έργου από εσάς). Παρόμοια Διανομή — Εάν αλλοιώσετε,
+τροποποιήσετε ή δημιουργήσετε περαιτέρω βασισμένοι στο έργο θα
+μπορείτε να διανέμετε το έργο που θα προκύψει μόνο με την ίδια ή
+παρόμοια άδεια.`
+
+// Taken from http://creativecommons.org/licenses/by-sa/3.0/deed.ar
+const txt_ar = `بموجب الشروط التالية نسب المصنف — يجب عليك أن
+تنسب العمل بالطريقة التي تحددها المؤلف أو المرخص (ولكن ليس بأي حال من
+الأحوال أن توحي وتقترح بتحول أو استخدامك للعمل).
+المشاركة على قدم المساواة — إذا كنت يعدل ، والتغيير ، أو الاستفادة
+من هذا العمل ، قد ينتج عن توزيع العمل إلا في ظل تشابه او تطابق فى واحد
+لهذا الترخيص.`
+
+// Taken from http://creativecommons.org/licenses/by-sa/1.0/il/
+const txt_il = `בכפוף לתנאים הבאים: ייחוס — עליך לייחס את היצירה (לתת קרדיט) באופן
+המצויין על-ידי היוצר או מעניק הרישיון (אך לא בשום אופן המרמז על כך
+שהם תומכים בך או בשימוש שלך ביצירה). שיתוף זהה — אם תחליט/י לשנות,
+לעבד או ליצור יצירה נגזרת בהסתמך על יצירה זו, תוכל/י להפיץ את יצירתך
+החדשה רק תחת אותו הרישיון או רישיון דומה לרישיון זה.`
+
+const twoByteUtf8 = txt_ru + txt_gr + txt_ar + txt_il
+
+// Taken from http://creativecommons.org/licenses/by-sa/2.0/kr/
+const txt_kr = `다음과 같은 조건을 따라야 합니다: 저작자표시
+(Attribution) — 저작자나 이용허락자가 정한 방법으로 저작물의
+원저작자를 표시하여야 합니다(그러나 원저작자가 이용자나 이용자의
+이용을 보증하거나 추천한다는 의미로 표시해서는 안됩니다).
+동일조건변경허락 — 이 저작물을 이용하여 만든 이차적 저작물에는 본
+라이선스와 동일한 라이선스를 적용해야 합니다.`
+
+// Taken from http://creativecommons.org/licenses/by-sa/3.0/th/
+const txt_th = `ภายใต้เงื่อนไข ดังต่อไปนี้ : แสดงที่มา — คุณต้องแสดงที่
+มาของงานดังกล่าว ตามรูปแบบที่ผู้สร้างสรรค์หรือผู้อนุญาตกำหนด (แต่
+ไม่ใช่ในลักษณะที่ว่า พวกเขาสนับสนุนคุณหรือสนับสนุนการที่
+คุณนำงานไปใช้) อนุญาตแบบเดียวกัน — หากคุณดัดแปลง เปลี่ยนรูป หรื
+อต่อเติมงานนี้ คุณต้องใช้สัญญาอนุญาตแบบเดียวกันหรือแบบที่เหมื
+อนกับสัญญาอนุญาตที่ใช้กับงานนี้เท่านั้น`
+
+const threeByteUtf8 = txt_th
+
+// Taken from http://creativecommons.org/licenses/by-sa/2.0/jp/
+const txt_jp = `あなたの従うべき条件は以下の通りです。
+表示 — あなたは原著作者のクレジットを表示しなければなりません。
+継承 — もしあなたがこの作品を改変、変形または加工した場合、
+あなたはその結果生じた作品をこの作品と同一の許諾条件の下でのみ
+頒布することができます。`
+
+// http://creativecommons.org/licenses/by-sa/2.5/cn/
+const txt_cn = `您可以自由: 复制、发行、展览、表演、放映、
+广播或通过信息网络传播本作品 创作演绎作品
+对本作品进行商业性使用 惟须遵守下列条件:
+署名 — 您必须按照作者或者许可人指定的方式对作品进行署名。
+相同方式共享 — 如果您改变、转换本作品或者以本作品为基础进行创作,
+您只能采用与本协议相同的许可协议发布基于本作品的演绎作品。`
+
+const txt_cjk = txt_cn + txt_jp + txt_kr
+const txt_all = txt_vn + twoByteUtf8 + threeByteUtf8 + txt_cjk
diff --git a/src/pkg/exp/norm/normregtest.go b/src/pkg/exp/norm/normregtest.go
new file mode 100644
index 000000000..b77b5b545
--- /dev/null
+++ b/src/pkg/exp/norm/normregtest.go
@@ -0,0 +1,304 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "exp/norm"
+ "flag"
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+ "path"
+ "regexp"
+ "runtime"
+ "strconv"
+ "strings"
+ "time"
+ "unicode"
+ "unicode/utf8"
+)
+
+func main() {
+ flag.Parse()
+ loadTestData()
+ CharacterByCharacterTests()
+ StandardTests()
+ PerformanceTest()
+ if errorCount == 0 {
+ fmt.Println("PASS")
+ }
+}
+
+const file = "NormalizationTest.txt"
+
+var url = flag.String("url",
+ "http://www.unicode.org/Public/"+unicode.Version+"/ucd/"+file,
+ "URL of Unicode database directory")
+var localFiles = flag.Bool("local",
+ false,
+ "data files have been copied to the current directory; for debugging only")
+
+var logger = log.New(os.Stderr, "", log.Lshortfile)
+
+// This regression test runs the test set in NormalizationTest.txt
+// (taken from http://www.unicode.org/Public/<unicode.Version>/ucd/).
+//
+// NormalizationTest.txt has form:
+// @Part0 # Specific cases
+// #
+// 1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE
+// 1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW
+//
+// Each test has 5 columns (c1, c2, c3, c4, c5), where
+// (c1, c2, c3, c4, c5) == (c1, NFC(c1), NFD(c1), NFKC(c1), NFKD(c1))
+//
+// CONFORMANCE:
+// 1. The following invariants must be true for all conformant implementations
+//
+// NFC
+// c2 == NFC(c1) == NFC(c2) == NFC(c3)
+// c4 == NFC(c4) == NFC(c5)
+//
+// NFD
+// c3 == NFD(c1) == NFD(c2) == NFD(c3)
+// c5 == NFD(c4) == NFD(c5)
+//
+// NFKC
+// c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
+//
+// NFKD
+// c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
+//
+// 2. For every code point X assigned in this version of Unicode that is not
+// specifically listed in Part 1, the following invariants must be true
+// for all conformant implementations:
+//
+// X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X)
+//
+
+// Column types.
+const (
+ cRaw = iota
+ cNFC
+ cNFD
+ cNFKC
+ cNFKD
+ cMaxColumns
+)
+
+// Holds data from NormalizationTest.txt
+var part []Part
+
+type Part struct {
+ name string
+ number int
+ tests []Test
+}
+
+type Test struct {
+ name string
+ partnr int
+ number int
+ r rune // used for character by character test
+ cols [cMaxColumns]string // Each has 5 entries, see below.
+}
+
+func (t Test) Name() string {
+ if t.number < 0 {
+ return part[t.partnr].name
+ }
+ return fmt.Sprintf("%s:%d", part[t.partnr].name, t.number)
+}
+
+var partRe = regexp.MustCompile(`@Part(\d) # (.*)$`)
+var testRe = regexp.MustCompile(`^` + strings.Repeat(`([\dA-F ]+);`, 5) + ` # (.*)$`)
+
+var counter int
+
+// Load the data form NormalizationTest.txt
+func loadTestData() {
+ if *localFiles {
+ pwd, _ := os.Getwd()
+ *url = "file://" + path.Join(pwd, file)
+ }
+ t := &http.Transport{}
+ t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
+ c := &http.Client{Transport: t}
+ resp, err := c.Get(*url)
+ if err != nil {
+ logger.Fatal(err)
+ }
+ if resp.StatusCode != 200 {
+ logger.Fatal("bad GET status for "+file, resp.Status)
+ }
+ f := resp.Body
+ defer f.Close()
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ line := scanner.Text()
+ if len(line) == 0 || line[0] == '#' {
+ continue
+ }
+ m := partRe.FindStringSubmatch(line)
+ if m != nil {
+ if len(m) < 3 {
+ logger.Fatal("Failed to parse Part: ", line)
+ }
+ i, err := strconv.Atoi(m[1])
+ if err != nil {
+ logger.Fatal(err)
+ }
+ name := m[2]
+ part = append(part, Part{name: name[:len(name)-1], number: i})
+ continue
+ }
+ m = testRe.FindStringSubmatch(line)
+ if m == nil || len(m) < 7 {
+ logger.Fatalf(`Failed to parse: "%s" result: %#v`, line, m)
+ }
+ test := Test{name: m[6], partnr: len(part) - 1, number: counter}
+ counter++
+ for j := 1; j < len(m)-1; j++ {
+ for _, split := range strings.Split(m[j], " ") {
+ r, err := strconv.ParseUint(split, 16, 64)
+ if err != nil {
+ logger.Fatal(err)
+ }
+ if test.r == 0 {
+ // save for CharacterByCharacterTests
+ test.r = rune(r)
+ }
+ var buf [utf8.UTFMax]byte
+ sz := utf8.EncodeRune(buf[:], rune(r))
+ test.cols[j-1] += string(buf[:sz])
+ }
+ }
+ part := &part[len(part)-1]
+ part.tests = append(part.tests, test)
+ }
+ if scanner.Err() != nil {
+ logger.Fatal(scanner.Err())
+ }
+}
+
+var fstr = []string{"NFC", "NFD", "NFKC", "NFKD"}
+
+var errorCount int
+
+func cmpResult(t *Test, name string, f norm.Form, gold, test, result string) {
+ if gold != result {
+ errorCount++
+ if errorCount > 20 {
+ return
+ }
+ st, sr, sg := []rune(test), []rune(result), []rune(gold)
+ logger.Printf("%s:%s: %s(%X)=%X; want:%X: %s",
+ t.Name(), name, fstr[f], st, sr, sg, t.name)
+ }
+}
+
+func cmpIsNormal(t *Test, name string, f norm.Form, test string, result, want bool) {
+ if result != want {
+ errorCount++
+ if errorCount > 20 {
+ return
+ }
+ logger.Printf("%s:%s: %s(%X)=%v; want: %v", t.Name(), name, fstr[f], []rune(test), result, want)
+ }
+}
+
+func doTest(t *Test, f norm.Form, gold, test string) {
+ result := f.Bytes([]byte(test))
+ cmpResult(t, "Bytes", f, gold, test, string(result))
+ sresult := f.String(test)
+ cmpResult(t, "String", f, gold, test, sresult)
+ acc := []byte{}
+ i := norm.Iter{}
+ i.InitString(f, test)
+ for !i.Done() {
+ acc = append(acc, i.Next()...)
+ }
+ cmpResult(t, "Iter.Next", f, gold, test, string(acc))
+ for i := range test {
+ out := f.Append(f.Bytes([]byte(test[:i])), []byte(test[i:])...)
+ cmpResult(t, fmt.Sprintf(":Append:%d", i), f, gold, test, string(out))
+ }
+ cmpIsNormal(t, "IsNormal", f, test, f.IsNormal([]byte(test)), test == gold)
+}
+
+func doConformanceTests(t *Test, partn int) {
+ for i := 0; i <= 2; i++ {
+ doTest(t, norm.NFC, t.cols[1], t.cols[i])
+ doTest(t, norm.NFD, t.cols[2], t.cols[i])
+ doTest(t, norm.NFKC, t.cols[3], t.cols[i])
+ doTest(t, norm.NFKD, t.cols[4], t.cols[i])
+ }
+ for i := 3; i <= 4; i++ {
+ doTest(t, norm.NFC, t.cols[3], t.cols[i])
+ doTest(t, norm.NFD, t.cols[4], t.cols[i])
+ doTest(t, norm.NFKC, t.cols[3], t.cols[i])
+ doTest(t, norm.NFKD, t.cols[4], t.cols[i])
+ }
+}
+
+func CharacterByCharacterTests() {
+ tests := part[1].tests
+ var last rune = 0
+ for i := 0; i <= len(tests); i++ { // last one is special case
+ var r rune
+ if i == len(tests) {
+ r = 0x2FA1E // Don't have to go to 0x10FFFF
+ } else {
+ r = tests[i].r
+ }
+ for last++; last < r; last++ {
+ // Check all characters that were not explicitly listed in the test.
+ t := &Test{partnr: 1, number: -1}
+ char := string(last)
+ doTest(t, norm.NFC, char, char)
+ doTest(t, norm.NFD, char, char)
+ doTest(t, norm.NFKC, char, char)
+ doTest(t, norm.NFKD, char, char)
+ }
+ if i < len(tests) {
+ doConformanceTests(&tests[i], 1)
+ }
+ }
+}
+
+func StandardTests() {
+ for _, j := range []int{0, 2, 3} {
+ for _, test := range part[j].tests {
+ doConformanceTests(&test, j)
+ }
+ }
+}
+
+// PerformanceTest verifies that normalization is O(n). If any of the
+// code does not properly check for maxCombiningChars, normalization
+// may exhibit O(n**2) behavior.
+func PerformanceTest() {
+ runtime.GOMAXPROCS(2)
+ success := make(chan bool, 1)
+ go func() {
+ buf := bytes.Repeat([]byte("\u035D"), 1024*1024)
+ buf = append(buf, "\u035B"...)
+ norm.NFC.Append(nil, buf...)
+ success <- true
+ }()
+ timeout := time.After(1 * time.Second)
+ select {
+ case <-success:
+ // test completed before the timeout
+ case <-timeout:
+ errorCount++
+ logger.Printf(`unexpectedly long time to complete PerformanceTest`)
+ }
+}
diff --git a/src/pkg/exp/norm/readwriter.go b/src/pkg/exp/norm/readwriter.go
new file mode 100644
index 000000000..2682894de
--- /dev/null
+++ b/src/pkg/exp/norm/readwriter.go
@@ -0,0 +1,126 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import "io"
+
+type normWriter struct {
+ rb reorderBuffer
+ w io.Writer
+ buf []byte
+}
+
+// Write implements the standard write interface. If the last characters are
+// not at a normalization boundary, the bytes will be buffered for the next
+// write. The remaining bytes will be written on close.
+func (w *normWriter) Write(data []byte) (n int, err error) {
+ // Process data in pieces to keep w.buf size bounded.
+ const chunk = 4000
+
+ for len(data) > 0 {
+ // Normalize into w.buf.
+ m := len(data)
+ if m > chunk {
+ m = chunk
+ }
+ w.rb.src = inputBytes(data[:m])
+ w.rb.nsrc = m
+ w.buf = doAppend(&w.rb, w.buf, 0)
+ data = data[m:]
+ n += m
+
+ // Write out complete prefix, save remainder.
+ // Note that lastBoundary looks back at most 30 runes.
+ i := lastBoundary(&w.rb.f, w.buf)
+ if i == -1 {
+ i = 0
+ }
+ if i > 0 {
+ if _, err = w.w.Write(w.buf[:i]); err != nil {
+ break
+ }
+ bn := copy(w.buf, w.buf[i:])
+ w.buf = w.buf[:bn]
+ }
+ }
+ return n, err
+}
+
+// Close forces data that remains in the buffer to be written.
+func (w *normWriter) Close() error {
+ if len(w.buf) > 0 {
+ _, err := w.w.Write(w.buf)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Writer returns a new writer that implements Write(b)
+// by writing f(b) to w. The returned writer may use an
+// an internal buffer to maintain state across Write calls.
+// Calling its Close method writes any buffered data to w.
+func (f Form) Writer(w io.Writer) io.WriteCloser {
+ wr := &normWriter{rb: reorderBuffer{}, w: w}
+ wr.rb.init(f, nil)
+ return wr
+}
+
+type normReader struct {
+ rb reorderBuffer
+ r io.Reader
+ inbuf []byte
+ outbuf []byte
+ bufStart int
+ lastBoundary int
+ err error
+}
+
+// Read implements the standard read interface.
+func (r *normReader) Read(p []byte) (int, error) {
+ for {
+ if r.lastBoundary-r.bufStart > 0 {
+ n := copy(p, r.outbuf[r.bufStart:r.lastBoundary])
+ r.bufStart += n
+ if r.lastBoundary-r.bufStart > 0 {
+ return n, nil
+ }
+ return n, r.err
+ }
+ if r.err != nil {
+ return 0, r.err
+ }
+ outn := copy(r.outbuf, r.outbuf[r.lastBoundary:])
+ r.outbuf = r.outbuf[0:outn]
+ r.bufStart = 0
+
+ n, err := r.r.Read(r.inbuf)
+ r.rb.src = inputBytes(r.inbuf[0:n])
+ r.rb.nsrc, r.err = n, err
+ if n > 0 {
+ r.outbuf = doAppend(&r.rb, r.outbuf, 0)
+ }
+ if err == io.EOF {
+ r.lastBoundary = len(r.outbuf)
+ } else {
+ r.lastBoundary = lastBoundary(&r.rb.f, r.outbuf)
+ if r.lastBoundary == -1 {
+ r.lastBoundary = 0
+ }
+ }
+ }
+ panic("should not reach here")
+}
+
+// Reader returns a new reader that implements Read
+// by reading data from r and returning f(data).
+func (f Form) Reader(r io.Reader) io.Reader {
+ const chunk = 4000
+ buf := make([]byte, chunk)
+ rr := &normReader{rb: reorderBuffer{}, r: r, inbuf: buf}
+ rr.rb.init(f, buf)
+ return rr
+}
diff --git a/src/pkg/exp/norm/readwriter_test.go b/src/pkg/exp/norm/readwriter_test.go
new file mode 100644
index 000000000..3b49eb0a2
--- /dev/null
+++ b/src/pkg/exp/norm/readwriter_test.go
@@ -0,0 +1,68 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+ "testing"
+)
+
+var ioTests = []AppendTest{
+ {"", strings.Repeat("a\u0316\u0300", 6), strings.Repeat("\u00E0\u0316", 6)},
+ {"", strings.Repeat("a\u0300\u0316", 4000), strings.Repeat("\u00E0\u0316", 4000)},
+ {"", strings.Repeat("\x80\x80", 4000), strings.Repeat("\x80\x80", 4000)},
+ {"", "\u0041\u0307\u0304", "\u01E0"},
+}
+
+var bufSizes = []int{1, 2, 3, 4, 5, 6, 7, 8, 100, 101, 102, 103, 4000, 4001, 4002, 4003}
+
+func readFunc(size int) appendFunc {
+ return func(f Form, out []byte, s string) []byte {
+ out = append(out, s...)
+ r := f.Reader(bytes.NewBuffer(out))
+ buf := make([]byte, size)
+ result := []byte{}
+ for n, err := 0, error(nil); err == nil; {
+ n, err = r.Read(buf)
+ result = append(result, buf[:n]...)
+ }
+ return result
+ }
+}
+
+func TestReader(t *testing.T) {
+ for _, s := range bufSizes {
+ name := fmt.Sprintf("TestReader%da", s)
+ runAppendTests(t, name, NFKC, readFunc(s), appendTests)
+ name = fmt.Sprintf("TestReader%db", s)
+ runAppendTests(t, name, NFKC, readFunc(s), ioTests)
+ }
+}
+
+func writeFunc(size int) appendFunc {
+ return func(f Form, out []byte, s string) []byte {
+ in := append(out, s...)
+ result := new(bytes.Buffer)
+ w := f.Writer(result)
+ buf := make([]byte, size)
+ for n := 0; len(in) > 0; in = in[n:] {
+ n = copy(buf, in)
+ _, _ = w.Write(buf[:n])
+ }
+ w.Close()
+ return result.Bytes()
+ }
+}
+
+func TestWriter(t *testing.T) {
+ for _, s := range bufSizes {
+ name := fmt.Sprintf("TestWriter%da", s)
+ runAppendTests(t, name, NFKC, writeFunc(s), appendTests)
+ name = fmt.Sprintf("TestWriter%db", s)
+ runAppendTests(t, name, NFKC, writeFunc(s), ioTests)
+ }
+}
diff --git a/src/pkg/exp/norm/tables.go b/src/pkg/exp/norm/tables.go
new file mode 100644
index 000000000..fa33a34a1
--- /dev/null
+++ b/src/pkg/exp/norm/tables.go
@@ -0,0 +1,6779 @@
+// Generated by running
+// maketables --tables=all --url=http://www.unicode.org/Public/6.2.0/ucd/
+// DO NOT EDIT
+
+package norm
+
+// Version is the Unicode edition from which the tables are derived.
+const Version = "6.2.0"
+
+const (
+ firstMulti = 0x18CF
+ firstCCC = 0x2E74
+ endMulti = 0x2F4A
+ firstLeadingCCC = 0x4994
+ firstCCCZeroExcept = 0x49AA
+ lastDecomp = 0x49D1
+ maxDecomp = 0x8000
+)
+
+// decomps: 18897 bytes
+var decomps = [...]byte{
+ // Bytes 0 - 3f
+ 0x00, 0x41, 0x20, 0x41, 0x21, 0x41, 0x22, 0x41,
+ 0x23, 0x41, 0x24, 0x41, 0x25, 0x41, 0x26, 0x41,
+ 0x27, 0x41, 0x28, 0x41, 0x29, 0x41, 0x2A, 0x41,
+ 0x2B, 0x41, 0x2C, 0x41, 0x2D, 0x41, 0x2E, 0x41,
+ 0x2F, 0x41, 0x30, 0x41, 0x31, 0x41, 0x32, 0x41,
+ 0x33, 0x41, 0x34, 0x41, 0x35, 0x41, 0x36, 0x41,
+ 0x37, 0x41, 0x38, 0x41, 0x39, 0x41, 0x3A, 0x41,
+ 0x3B, 0x41, 0x3C, 0x41, 0x3D, 0x41, 0x3E, 0x41,
+ // Bytes 40 - 7f
+ 0x3F, 0x41, 0x40, 0x41, 0x41, 0x41, 0x42, 0x41,
+ 0x43, 0x41, 0x44, 0x41, 0x45, 0x41, 0x46, 0x41,
+ 0x47, 0x41, 0x48, 0x41, 0x49, 0x41, 0x4A, 0x41,
+ 0x4B, 0x41, 0x4C, 0x41, 0x4D, 0x41, 0x4E, 0x41,
+ 0x4F, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41,
+ 0x53, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41,
+ 0x57, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x41,
+ 0x5B, 0x41, 0x5C, 0x41, 0x5D, 0x41, 0x5E, 0x41,
+ // Bytes 80 - bf
+ 0x5F, 0x41, 0x60, 0x41, 0x61, 0x41, 0x62, 0x41,
+ 0x63, 0x41, 0x64, 0x41, 0x65, 0x41, 0x66, 0x41,
+ 0x67, 0x41, 0x68, 0x41, 0x69, 0x41, 0x6A, 0x41,
+ 0x6B, 0x41, 0x6C, 0x41, 0x6D, 0x41, 0x6E, 0x41,
+ 0x6F, 0x41, 0x70, 0x41, 0x71, 0x41, 0x72, 0x41,
+ 0x73, 0x41, 0x74, 0x41, 0x75, 0x41, 0x76, 0x41,
+ 0x77, 0x41, 0x78, 0x41, 0x79, 0x41, 0x7A, 0x41,
+ 0x7B, 0x41, 0x7C, 0x41, 0x7D, 0x41, 0x7E, 0x42,
+ // Bytes c0 - ff
+ 0xC2, 0xA2, 0x42, 0xC2, 0xA3, 0x42, 0xC2, 0xA5,
+ 0x42, 0xC2, 0xA6, 0x42, 0xC2, 0xAC, 0x42, 0xC2,
+ 0xB4, 0x42, 0xC2, 0xB7, 0x42, 0xC3, 0x86, 0x42,
+ 0xC3, 0xB0, 0x42, 0xC4, 0xA6, 0x42, 0xC4, 0xA7,
+ 0x42, 0xC4, 0xB1, 0x42, 0xC5, 0x8B, 0x42, 0xC5,
+ 0x93, 0x42, 0xC6, 0x8E, 0x42, 0xC6, 0x90, 0x42,
+ 0xC6, 0xAB, 0x42, 0xC8, 0xA2, 0x42, 0xC8, 0xB7,
+ 0x42, 0xC9, 0x90, 0x42, 0xC9, 0x91, 0x42, 0xC9,
+ // Bytes 100 - 13f
+ 0x92, 0x42, 0xC9, 0x94, 0x42, 0xC9, 0x95, 0x42,
+ 0xC9, 0x99, 0x42, 0xC9, 0x9B, 0x42, 0xC9, 0x9C,
+ 0x42, 0xC9, 0x9F, 0x42, 0xC9, 0xA1, 0x42, 0xC9,
+ 0xA3, 0x42, 0xC9, 0xA5, 0x42, 0xC9, 0xA6, 0x42,
+ 0xC9, 0xA8, 0x42, 0xC9, 0xA9, 0x42, 0xC9, 0xAA,
+ 0x42, 0xC9, 0xAD, 0x42, 0xC9, 0xAF, 0x42, 0xC9,
+ 0xB0, 0x42, 0xC9, 0xB1, 0x42, 0xC9, 0xB2, 0x42,
+ 0xC9, 0xB3, 0x42, 0xC9, 0xB4, 0x42, 0xC9, 0xB5,
+ // Bytes 140 - 17f
+ 0x42, 0xC9, 0xB8, 0x42, 0xC9, 0xB9, 0x42, 0xC9,
+ 0xBB, 0x42, 0xCA, 0x81, 0x42, 0xCA, 0x82, 0x42,
+ 0xCA, 0x83, 0x42, 0xCA, 0x89, 0x42, 0xCA, 0x8A,
+ 0x42, 0xCA, 0x8B, 0x42, 0xCA, 0x8C, 0x42, 0xCA,
+ 0x90, 0x42, 0xCA, 0x91, 0x42, 0xCA, 0x92, 0x42,
+ 0xCA, 0x95, 0x42, 0xCA, 0x9D, 0x42, 0xCA, 0x9F,
+ 0x42, 0xCA, 0xB9, 0x42, 0xCE, 0x91, 0x42, 0xCE,
+ 0x92, 0x42, 0xCE, 0x93, 0x42, 0xCE, 0x94, 0x42,
+ // Bytes 180 - 1bf
+ 0xCE, 0x95, 0x42, 0xCE, 0x96, 0x42, 0xCE, 0x97,
+ 0x42, 0xCE, 0x98, 0x42, 0xCE, 0x99, 0x42, 0xCE,
+ 0x9A, 0x42, 0xCE, 0x9B, 0x42, 0xCE, 0x9C, 0x42,
+ 0xCE, 0x9D, 0x42, 0xCE, 0x9E, 0x42, 0xCE, 0x9F,
+ 0x42, 0xCE, 0xA0, 0x42, 0xCE, 0xA1, 0x42, 0xCE,
+ 0xA3, 0x42, 0xCE, 0xA4, 0x42, 0xCE, 0xA5, 0x42,
+ 0xCE, 0xA6, 0x42, 0xCE, 0xA7, 0x42, 0xCE, 0xA8,
+ 0x42, 0xCE, 0xA9, 0x42, 0xCE, 0xB1, 0x42, 0xCE,
+ // Bytes 1c0 - 1ff
+ 0xB2, 0x42, 0xCE, 0xB3, 0x42, 0xCE, 0xB4, 0x42,
+ 0xCE, 0xB5, 0x42, 0xCE, 0xB6, 0x42, 0xCE, 0xB7,
+ 0x42, 0xCE, 0xB8, 0x42, 0xCE, 0xB9, 0x42, 0xCE,
+ 0xBA, 0x42, 0xCE, 0xBB, 0x42, 0xCE, 0xBC, 0x42,
+ 0xCE, 0xBD, 0x42, 0xCE, 0xBE, 0x42, 0xCE, 0xBF,
+ 0x42, 0xCF, 0x80, 0x42, 0xCF, 0x81, 0x42, 0xCF,
+ 0x82, 0x42, 0xCF, 0x83, 0x42, 0xCF, 0x84, 0x42,
+ 0xCF, 0x85, 0x42, 0xCF, 0x86, 0x42, 0xCF, 0x87,
+ // Bytes 200 - 23f
+ 0x42, 0xCF, 0x88, 0x42, 0xCF, 0x89, 0x42, 0xCF,
+ 0x9C, 0x42, 0xCF, 0x9D, 0x42, 0xD0, 0xBD, 0x42,
+ 0xD7, 0x90, 0x42, 0xD7, 0x91, 0x42, 0xD7, 0x92,
+ 0x42, 0xD7, 0x93, 0x42, 0xD7, 0x94, 0x42, 0xD7,
+ 0x9B, 0x42, 0xD7, 0x9C, 0x42, 0xD7, 0x9D, 0x42,
+ 0xD7, 0xA2, 0x42, 0xD7, 0xA8, 0x42, 0xD7, 0xAA,
+ 0x42, 0xD8, 0xA1, 0x42, 0xD8, 0xA7, 0x42, 0xD8,
+ 0xA8, 0x42, 0xD8, 0xA9, 0x42, 0xD8, 0xAA, 0x42,
+ // Bytes 240 - 27f
+ 0xD8, 0xAB, 0x42, 0xD8, 0xAC, 0x42, 0xD8, 0xAD,
+ 0x42, 0xD8, 0xAE, 0x42, 0xD8, 0xAF, 0x42, 0xD8,
+ 0xB0, 0x42, 0xD8, 0xB1, 0x42, 0xD8, 0xB2, 0x42,
+ 0xD8, 0xB3, 0x42, 0xD8, 0xB4, 0x42, 0xD8, 0xB5,
+ 0x42, 0xD8, 0xB6, 0x42, 0xD8, 0xB7, 0x42, 0xD8,
+ 0xB8, 0x42, 0xD8, 0xB9, 0x42, 0xD8, 0xBA, 0x42,
+ 0xD9, 0x81, 0x42, 0xD9, 0x82, 0x42, 0xD9, 0x83,
+ 0x42, 0xD9, 0x84, 0x42, 0xD9, 0x85, 0x42, 0xD9,
+ // Bytes 280 - 2bf
+ 0x86, 0x42, 0xD9, 0x87, 0x42, 0xD9, 0x88, 0x42,
+ 0xD9, 0x89, 0x42, 0xD9, 0x8A, 0x42, 0xD9, 0xAE,
+ 0x42, 0xD9, 0xAF, 0x42, 0xD9, 0xB1, 0x42, 0xD9,
+ 0xB9, 0x42, 0xD9, 0xBA, 0x42, 0xD9, 0xBB, 0x42,
+ 0xD9, 0xBE, 0x42, 0xD9, 0xBF, 0x42, 0xDA, 0x80,
+ 0x42, 0xDA, 0x83, 0x42, 0xDA, 0x84, 0x42, 0xDA,
+ 0x86, 0x42, 0xDA, 0x87, 0x42, 0xDA, 0x88, 0x42,
+ 0xDA, 0x8C, 0x42, 0xDA, 0x8D, 0x42, 0xDA, 0x8E,
+ // Bytes 2c0 - 2ff
+ 0x42, 0xDA, 0x91, 0x42, 0xDA, 0x98, 0x42, 0xDA,
+ 0xA1, 0x42, 0xDA, 0xA4, 0x42, 0xDA, 0xA6, 0x42,
+ 0xDA, 0xA9, 0x42, 0xDA, 0xAD, 0x42, 0xDA, 0xAF,
+ 0x42, 0xDA, 0xB1, 0x42, 0xDA, 0xB3, 0x42, 0xDA,
+ 0xBA, 0x42, 0xDA, 0xBB, 0x42, 0xDA, 0xBE, 0x42,
+ 0xDB, 0x81, 0x42, 0xDB, 0x85, 0x42, 0xDB, 0x86,
+ 0x42, 0xDB, 0x87, 0x42, 0xDB, 0x88, 0x42, 0xDB,
+ 0x89, 0x42, 0xDB, 0x8B, 0x42, 0xDB, 0x8C, 0x42,
+ // Bytes 300 - 33f
+ 0xDB, 0x90, 0x42, 0xDB, 0x92, 0x43, 0xE0, 0xBC,
+ 0x8B, 0x43, 0xE1, 0x83, 0x9C, 0x43, 0xE1, 0x84,
+ 0x80, 0x43, 0xE1, 0x84, 0x81, 0x43, 0xE1, 0x84,
+ 0x82, 0x43, 0xE1, 0x84, 0x83, 0x43, 0xE1, 0x84,
+ 0x84, 0x43, 0xE1, 0x84, 0x85, 0x43, 0xE1, 0x84,
+ 0x86, 0x43, 0xE1, 0x84, 0x87, 0x43, 0xE1, 0x84,
+ 0x88, 0x43, 0xE1, 0x84, 0x89, 0x43, 0xE1, 0x84,
+ 0x8A, 0x43, 0xE1, 0x84, 0x8B, 0x43, 0xE1, 0x84,
+ // Bytes 340 - 37f
+ 0x8C, 0x43, 0xE1, 0x84, 0x8D, 0x43, 0xE1, 0x84,
+ 0x8E, 0x43, 0xE1, 0x84, 0x8F, 0x43, 0xE1, 0x84,
+ 0x90, 0x43, 0xE1, 0x84, 0x91, 0x43, 0xE1, 0x84,
+ 0x92, 0x43, 0xE1, 0x84, 0x94, 0x43, 0xE1, 0x84,
+ 0x95, 0x43, 0xE1, 0x84, 0x9A, 0x43, 0xE1, 0x84,
+ 0x9C, 0x43, 0xE1, 0x84, 0x9D, 0x43, 0xE1, 0x84,
+ 0x9E, 0x43, 0xE1, 0x84, 0xA0, 0x43, 0xE1, 0x84,
+ 0xA1, 0x43, 0xE1, 0x84, 0xA2, 0x43, 0xE1, 0x84,
+ // Bytes 380 - 3bf
+ 0xA3, 0x43, 0xE1, 0x84, 0xA7, 0x43, 0xE1, 0x84,
+ 0xA9, 0x43, 0xE1, 0x84, 0xAB, 0x43, 0xE1, 0x84,
+ 0xAC, 0x43, 0xE1, 0x84, 0xAD, 0x43, 0xE1, 0x84,
+ 0xAE, 0x43, 0xE1, 0x84, 0xAF, 0x43, 0xE1, 0x84,
+ 0xB2, 0x43, 0xE1, 0x84, 0xB6, 0x43, 0xE1, 0x85,
+ 0x80, 0x43, 0xE1, 0x85, 0x87, 0x43, 0xE1, 0x85,
+ 0x8C, 0x43, 0xE1, 0x85, 0x97, 0x43, 0xE1, 0x85,
+ 0x98, 0x43, 0xE1, 0x85, 0x99, 0x43, 0xE1, 0x85,
+ // Bytes 3c0 - 3ff
+ 0xA0, 0x43, 0xE1, 0x85, 0xA1, 0x43, 0xE1, 0x85,
+ 0xA2, 0x43, 0xE1, 0x85, 0xA3, 0x43, 0xE1, 0x85,
+ 0xA4, 0x43, 0xE1, 0x85, 0xA5, 0x43, 0xE1, 0x85,
+ 0xA6, 0x43, 0xE1, 0x85, 0xA7, 0x43, 0xE1, 0x85,
+ 0xA8, 0x43, 0xE1, 0x85, 0xA9, 0x43, 0xE1, 0x85,
+ 0xAA, 0x43, 0xE1, 0x85, 0xAB, 0x43, 0xE1, 0x85,
+ 0xAC, 0x43, 0xE1, 0x85, 0xAD, 0x43, 0xE1, 0x85,
+ 0xAE, 0x43, 0xE1, 0x85, 0xAF, 0x43, 0xE1, 0x85,
+ // Bytes 400 - 43f
+ 0xB0, 0x43, 0xE1, 0x85, 0xB1, 0x43, 0xE1, 0x85,
+ 0xB2, 0x43, 0xE1, 0x85, 0xB3, 0x43, 0xE1, 0x85,
+ 0xB4, 0x43, 0xE1, 0x85, 0xB5, 0x43, 0xE1, 0x86,
+ 0x84, 0x43, 0xE1, 0x86, 0x85, 0x43, 0xE1, 0x86,
+ 0x88, 0x43, 0xE1, 0x86, 0x91, 0x43, 0xE1, 0x86,
+ 0x92, 0x43, 0xE1, 0x86, 0x94, 0x43, 0xE1, 0x86,
+ 0x9E, 0x43, 0xE1, 0x86, 0xA1, 0x43, 0xE1, 0x86,
+ 0xAA, 0x43, 0xE1, 0x86, 0xAC, 0x43, 0xE1, 0x86,
+ // Bytes 440 - 47f
+ 0xAD, 0x43, 0xE1, 0x86, 0xB0, 0x43, 0xE1, 0x86,
+ 0xB1, 0x43, 0xE1, 0x86, 0xB2, 0x43, 0xE1, 0x86,
+ 0xB3, 0x43, 0xE1, 0x86, 0xB4, 0x43, 0xE1, 0x86,
+ 0xB5, 0x43, 0xE1, 0x87, 0x87, 0x43, 0xE1, 0x87,
+ 0x88, 0x43, 0xE1, 0x87, 0x8C, 0x43, 0xE1, 0x87,
+ 0x8E, 0x43, 0xE1, 0x87, 0x93, 0x43, 0xE1, 0x87,
+ 0x97, 0x43, 0xE1, 0x87, 0x99, 0x43, 0xE1, 0x87,
+ 0x9D, 0x43, 0xE1, 0x87, 0x9F, 0x43, 0xE1, 0x87,
+ // Bytes 480 - 4bf
+ 0xB1, 0x43, 0xE1, 0x87, 0xB2, 0x43, 0xE1, 0xB4,
+ 0x82, 0x43, 0xE1, 0xB4, 0x96, 0x43, 0xE1, 0xB4,
+ 0x97, 0x43, 0xE1, 0xB4, 0x9C, 0x43, 0xE1, 0xB4,
+ 0x9D, 0x43, 0xE1, 0xB4, 0xA5, 0x43, 0xE1, 0xB5,
+ 0xBB, 0x43, 0xE1, 0xB6, 0x85, 0x43, 0xE2, 0x80,
+ 0x82, 0x43, 0xE2, 0x80, 0x83, 0x43, 0xE2, 0x80,
+ 0x90, 0x43, 0xE2, 0x80, 0x93, 0x43, 0xE2, 0x80,
+ 0x94, 0x43, 0xE2, 0x82, 0xA9, 0x43, 0xE2, 0x86,
+ // Bytes 4c0 - 4ff
+ 0x90, 0x43, 0xE2, 0x86, 0x91, 0x43, 0xE2, 0x86,
+ 0x92, 0x43, 0xE2, 0x86, 0x93, 0x43, 0xE2, 0x88,
+ 0x82, 0x43, 0xE2, 0x88, 0x87, 0x43, 0xE2, 0x88,
+ 0x91, 0x43, 0xE2, 0x88, 0x92, 0x43, 0xE2, 0x94,
+ 0x82, 0x43, 0xE2, 0x96, 0xA0, 0x43, 0xE2, 0x97,
+ 0x8B, 0x43, 0xE2, 0xA6, 0x85, 0x43, 0xE2, 0xA6,
+ 0x86, 0x43, 0xE2, 0xB5, 0xA1, 0x43, 0xE3, 0x80,
+ 0x81, 0x43, 0xE3, 0x80, 0x82, 0x43, 0xE3, 0x80,
+ // Bytes 500 - 53f
+ 0x88, 0x43, 0xE3, 0x80, 0x89, 0x43, 0xE3, 0x80,
+ 0x8A, 0x43, 0xE3, 0x80, 0x8B, 0x43, 0xE3, 0x80,
+ 0x8C, 0x43, 0xE3, 0x80, 0x8D, 0x43, 0xE3, 0x80,
+ 0x8E, 0x43, 0xE3, 0x80, 0x8F, 0x43, 0xE3, 0x80,
+ 0x90, 0x43, 0xE3, 0x80, 0x91, 0x43, 0xE3, 0x80,
+ 0x92, 0x43, 0xE3, 0x80, 0x94, 0x43, 0xE3, 0x80,
+ 0x95, 0x43, 0xE3, 0x80, 0x96, 0x43, 0xE3, 0x80,
+ 0x97, 0x43, 0xE3, 0x82, 0xA1, 0x43, 0xE3, 0x82,
+ // Bytes 540 - 57f
+ 0xA2, 0x43, 0xE3, 0x82, 0xA3, 0x43, 0xE3, 0x82,
+ 0xA4, 0x43, 0xE3, 0x82, 0xA5, 0x43, 0xE3, 0x82,
+ 0xA6, 0x43, 0xE3, 0x82, 0xA7, 0x43, 0xE3, 0x82,
+ 0xA8, 0x43, 0xE3, 0x82, 0xA9, 0x43, 0xE3, 0x82,
+ 0xAA, 0x43, 0xE3, 0x82, 0xAB, 0x43, 0xE3, 0x82,
+ 0xAD, 0x43, 0xE3, 0x82, 0xAF, 0x43, 0xE3, 0x82,
+ 0xB1, 0x43, 0xE3, 0x82, 0xB3, 0x43, 0xE3, 0x82,
+ 0xB5, 0x43, 0xE3, 0x82, 0xB7, 0x43, 0xE3, 0x82,
+ // Bytes 580 - 5bf
+ 0xB9, 0x43, 0xE3, 0x82, 0xBB, 0x43, 0xE3, 0x82,
+ 0xBD, 0x43, 0xE3, 0x82, 0xBF, 0x43, 0xE3, 0x83,
+ 0x81, 0x43, 0xE3, 0x83, 0x83, 0x43, 0xE3, 0x83,
+ 0x84, 0x43, 0xE3, 0x83, 0x86, 0x43, 0xE3, 0x83,
+ 0x88, 0x43, 0xE3, 0x83, 0x8A, 0x43, 0xE3, 0x83,
+ 0x8B, 0x43, 0xE3, 0x83, 0x8C, 0x43, 0xE3, 0x83,
+ 0x8D, 0x43, 0xE3, 0x83, 0x8E, 0x43, 0xE3, 0x83,
+ 0x8F, 0x43, 0xE3, 0x83, 0x92, 0x43, 0xE3, 0x83,
+ // Bytes 5c0 - 5ff
+ 0x95, 0x43, 0xE3, 0x83, 0x98, 0x43, 0xE3, 0x83,
+ 0x9B, 0x43, 0xE3, 0x83, 0x9E, 0x43, 0xE3, 0x83,
+ 0x9F, 0x43, 0xE3, 0x83, 0xA0, 0x43, 0xE3, 0x83,
+ 0xA1, 0x43, 0xE3, 0x83, 0xA2, 0x43, 0xE3, 0x83,
+ 0xA3, 0x43, 0xE3, 0x83, 0xA4, 0x43, 0xE3, 0x83,
+ 0xA5, 0x43, 0xE3, 0x83, 0xA6, 0x43, 0xE3, 0x83,
+ 0xA7, 0x43, 0xE3, 0x83, 0xA8, 0x43, 0xE3, 0x83,
+ 0xA9, 0x43, 0xE3, 0x83, 0xAA, 0x43, 0xE3, 0x83,
+ // Bytes 600 - 63f
+ 0xAB, 0x43, 0xE3, 0x83, 0xAC, 0x43, 0xE3, 0x83,
+ 0xAD, 0x43, 0xE3, 0x83, 0xAF, 0x43, 0xE3, 0x83,
+ 0xB0, 0x43, 0xE3, 0x83, 0xB1, 0x43, 0xE3, 0x83,
+ 0xB2, 0x43, 0xE3, 0x83, 0xB3, 0x43, 0xE3, 0x83,
+ 0xBB, 0x43, 0xE3, 0x83, 0xBC, 0x43, 0xE3, 0x92,
+ 0x9E, 0x43, 0xE3, 0x92, 0xB9, 0x43, 0xE3, 0x92,
+ 0xBB, 0x43, 0xE3, 0x93, 0x9F, 0x43, 0xE3, 0x94,
+ 0x95, 0x43, 0xE3, 0x9B, 0xAE, 0x43, 0xE3, 0x9B,
+ // Bytes 640 - 67f
+ 0xBC, 0x43, 0xE3, 0x9E, 0x81, 0x43, 0xE3, 0xA0,
+ 0xAF, 0x43, 0xE3, 0xA1, 0xA2, 0x43, 0xE3, 0xA1,
+ 0xBC, 0x43, 0xE3, 0xA3, 0x87, 0x43, 0xE3, 0xA3,
+ 0xA3, 0x43, 0xE3, 0xA4, 0x9C, 0x43, 0xE3, 0xA4,
+ 0xBA, 0x43, 0xE3, 0xA8, 0xAE, 0x43, 0xE3, 0xA9,
+ 0xAC, 0x43, 0xE3, 0xAB, 0xA4, 0x43, 0xE3, 0xAC,
+ 0x88, 0x43, 0xE3, 0xAC, 0x99, 0x43, 0xE3, 0xAD,
+ 0x89, 0x43, 0xE3, 0xAE, 0x9D, 0x43, 0xE3, 0xB0,
+ // Bytes 680 - 6bf
+ 0x98, 0x43, 0xE3, 0xB1, 0x8E, 0x43, 0xE3, 0xB4,
+ 0xB3, 0x43, 0xE3, 0xB6, 0x96, 0x43, 0xE3, 0xBA,
+ 0xAC, 0x43, 0xE3, 0xBA, 0xB8, 0x43, 0xE3, 0xBC,
+ 0x9B, 0x43, 0xE3, 0xBF, 0xBC, 0x43, 0xE4, 0x80,
+ 0x88, 0x43, 0xE4, 0x80, 0x98, 0x43, 0xE4, 0x80,
+ 0xB9, 0x43, 0xE4, 0x81, 0x86, 0x43, 0xE4, 0x82,
+ 0x96, 0x43, 0xE4, 0x83, 0xA3, 0x43, 0xE4, 0x84,
+ 0xAF, 0x43, 0xE4, 0x88, 0x82, 0x43, 0xE4, 0x88,
+ // Bytes 6c0 - 6ff
+ 0xA7, 0x43, 0xE4, 0x8A, 0xA0, 0x43, 0xE4, 0x8C,
+ 0x81, 0x43, 0xE4, 0x8C, 0xB4, 0x43, 0xE4, 0x8D,
+ 0x99, 0x43, 0xE4, 0x8F, 0x95, 0x43, 0xE4, 0x8F,
+ 0x99, 0x43, 0xE4, 0x90, 0x8B, 0x43, 0xE4, 0x91,
+ 0xAB, 0x43, 0xE4, 0x94, 0xAB, 0x43, 0xE4, 0x95,
+ 0x9D, 0x43, 0xE4, 0x95, 0xA1, 0x43, 0xE4, 0x95,
+ 0xAB, 0x43, 0xE4, 0x97, 0x97, 0x43, 0xE4, 0x97,
+ 0xB9, 0x43, 0xE4, 0x98, 0xB5, 0x43, 0xE4, 0x9A,
+ // Bytes 700 - 73f
+ 0xBE, 0x43, 0xE4, 0x9B, 0x87, 0x43, 0xE4, 0xA6,
+ 0x95, 0x43, 0xE4, 0xA7, 0xA6, 0x43, 0xE4, 0xA9,
+ 0xAE, 0x43, 0xE4, 0xA9, 0xB6, 0x43, 0xE4, 0xAA,
+ 0xB2, 0x43, 0xE4, 0xAC, 0xB3, 0x43, 0xE4, 0xAF,
+ 0x8E, 0x43, 0xE4, 0xB3, 0x8E, 0x43, 0xE4, 0xB3,
+ 0xAD, 0x43, 0xE4, 0xB3, 0xB8, 0x43, 0xE4, 0xB5,
+ 0x96, 0x43, 0xE4, 0xB8, 0x80, 0x43, 0xE4, 0xB8,
+ 0x81, 0x43, 0xE4, 0xB8, 0x83, 0x43, 0xE4, 0xB8,
+ // Bytes 740 - 77f
+ 0x89, 0x43, 0xE4, 0xB8, 0x8A, 0x43, 0xE4, 0xB8,
+ 0x8B, 0x43, 0xE4, 0xB8, 0x8D, 0x43, 0xE4, 0xB8,
+ 0x99, 0x43, 0xE4, 0xB8, 0xA6, 0x43, 0xE4, 0xB8,
+ 0xA8, 0x43, 0xE4, 0xB8, 0xAD, 0x43, 0xE4, 0xB8,
+ 0xB2, 0x43, 0xE4, 0xB8, 0xB6, 0x43, 0xE4, 0xB8,
+ 0xB8, 0x43, 0xE4, 0xB8, 0xB9, 0x43, 0xE4, 0xB8,
+ 0xBD, 0x43, 0xE4, 0xB8, 0xBF, 0x43, 0xE4, 0xB9,
+ 0x81, 0x43, 0xE4, 0xB9, 0x99, 0x43, 0xE4, 0xB9,
+ // Bytes 780 - 7bf
+ 0x9D, 0x43, 0xE4, 0xBA, 0x82, 0x43, 0xE4, 0xBA,
+ 0x85, 0x43, 0xE4, 0xBA, 0x86, 0x43, 0xE4, 0xBA,
+ 0x8C, 0x43, 0xE4, 0xBA, 0x94, 0x43, 0xE4, 0xBA,
+ 0xA0, 0x43, 0xE4, 0xBA, 0xA4, 0x43, 0xE4, 0xBA,
+ 0xAE, 0x43, 0xE4, 0xBA, 0xBA, 0x43, 0xE4, 0xBB,
+ 0x80, 0x43, 0xE4, 0xBB, 0x8C, 0x43, 0xE4, 0xBB,
+ 0xA4, 0x43, 0xE4, 0xBC, 0x81, 0x43, 0xE4, 0xBC,
+ 0x91, 0x43, 0xE4, 0xBD, 0xA0, 0x43, 0xE4, 0xBE,
+ // Bytes 7c0 - 7ff
+ 0x80, 0x43, 0xE4, 0xBE, 0x86, 0x43, 0xE4, 0xBE,
+ 0x8B, 0x43, 0xE4, 0xBE, 0xAE, 0x43, 0xE4, 0xBE,
+ 0xBB, 0x43, 0xE4, 0xBE, 0xBF, 0x43, 0xE5, 0x80,
+ 0x82, 0x43, 0xE5, 0x80, 0xAB, 0x43, 0xE5, 0x81,
+ 0xBA, 0x43, 0xE5, 0x82, 0x99, 0x43, 0xE5, 0x83,
+ 0x8F, 0x43, 0xE5, 0x83, 0x9A, 0x43, 0xE5, 0x83,
+ 0xA7, 0x43, 0xE5, 0x84, 0xAA, 0x43, 0xE5, 0x84,
+ 0xBF, 0x43, 0xE5, 0x85, 0x80, 0x43, 0xE5, 0x85,
+ // Bytes 800 - 83f
+ 0x85, 0x43, 0xE5, 0x85, 0x8D, 0x43, 0xE5, 0x85,
+ 0x94, 0x43, 0xE5, 0x85, 0xA4, 0x43, 0xE5, 0x85,
+ 0xA5, 0x43, 0xE5, 0x85, 0xA7, 0x43, 0xE5, 0x85,
+ 0xA8, 0x43, 0xE5, 0x85, 0xA9, 0x43, 0xE5, 0x85,
+ 0xAB, 0x43, 0xE5, 0x85, 0xAD, 0x43, 0xE5, 0x85,
+ 0xB7, 0x43, 0xE5, 0x86, 0x80, 0x43, 0xE5, 0x86,
+ 0x82, 0x43, 0xE5, 0x86, 0x8D, 0x43, 0xE5, 0x86,
+ 0x92, 0x43, 0xE5, 0x86, 0x95, 0x43, 0xE5, 0x86,
+ // Bytes 840 - 87f
+ 0x96, 0x43, 0xE5, 0x86, 0x97, 0x43, 0xE5, 0x86,
+ 0x99, 0x43, 0xE5, 0x86, 0xA4, 0x43, 0xE5, 0x86,
+ 0xAB, 0x43, 0xE5, 0x86, 0xAC, 0x43, 0xE5, 0x86,
+ 0xB5, 0x43, 0xE5, 0x86, 0xB7, 0x43, 0xE5, 0x87,
+ 0x89, 0x43, 0xE5, 0x87, 0x8C, 0x43, 0xE5, 0x87,
+ 0x9C, 0x43, 0xE5, 0x87, 0x9E, 0x43, 0xE5, 0x87,
+ 0xA0, 0x43, 0xE5, 0x87, 0xB5, 0x43, 0xE5, 0x88,
+ 0x80, 0x43, 0xE5, 0x88, 0x83, 0x43, 0xE5, 0x88,
+ // Bytes 880 - 8bf
+ 0x87, 0x43, 0xE5, 0x88, 0x97, 0x43, 0xE5, 0x88,
+ 0x9D, 0x43, 0xE5, 0x88, 0xA9, 0x43, 0xE5, 0x88,
+ 0xBA, 0x43, 0xE5, 0x88, 0xBB, 0x43, 0xE5, 0x89,
+ 0x86, 0x43, 0xE5, 0x89, 0x8D, 0x43, 0xE5, 0x89,
+ 0xB2, 0x43, 0xE5, 0x89, 0xB7, 0x43, 0xE5, 0x8A,
+ 0x89, 0x43, 0xE5, 0x8A, 0x9B, 0x43, 0xE5, 0x8A,
+ 0xA3, 0x43, 0xE5, 0x8A, 0xB3, 0x43, 0xE5, 0x8A,
+ 0xB4, 0x43, 0xE5, 0x8B, 0x87, 0x43, 0xE5, 0x8B,
+ // Bytes 8c0 - 8ff
+ 0x89, 0x43, 0xE5, 0x8B, 0x92, 0x43, 0xE5, 0x8B,
+ 0x9E, 0x43, 0xE5, 0x8B, 0xA4, 0x43, 0xE5, 0x8B,
+ 0xB5, 0x43, 0xE5, 0x8B, 0xB9, 0x43, 0xE5, 0x8B,
+ 0xBA, 0x43, 0xE5, 0x8C, 0x85, 0x43, 0xE5, 0x8C,
+ 0x86, 0x43, 0xE5, 0x8C, 0x95, 0x43, 0xE5, 0x8C,
+ 0x97, 0x43, 0xE5, 0x8C, 0x9A, 0x43, 0xE5, 0x8C,
+ 0xB8, 0x43, 0xE5, 0x8C, 0xBB, 0x43, 0xE5, 0x8C,
+ 0xBF, 0x43, 0xE5, 0x8D, 0x81, 0x43, 0xE5, 0x8D,
+ // Bytes 900 - 93f
+ 0x84, 0x43, 0xE5, 0x8D, 0x85, 0x43, 0xE5, 0x8D,
+ 0x89, 0x43, 0xE5, 0x8D, 0x91, 0x43, 0xE5, 0x8D,
+ 0x94, 0x43, 0xE5, 0x8D, 0x9A, 0x43, 0xE5, 0x8D,
+ 0x9C, 0x43, 0xE5, 0x8D, 0xA9, 0x43, 0xE5, 0x8D,
+ 0xB0, 0x43, 0xE5, 0x8D, 0xB3, 0x43, 0xE5, 0x8D,
+ 0xB5, 0x43, 0xE5, 0x8D, 0xBD, 0x43, 0xE5, 0x8D,
+ 0xBF, 0x43, 0xE5, 0x8E, 0x82, 0x43, 0xE5, 0x8E,
+ 0xB6, 0x43, 0xE5, 0x8F, 0x83, 0x43, 0xE5, 0x8F,
+ // Bytes 940 - 97f
+ 0x88, 0x43, 0xE5, 0x8F, 0x8A, 0x43, 0xE5, 0x8F,
+ 0x8C, 0x43, 0xE5, 0x8F, 0x9F, 0x43, 0xE5, 0x8F,
+ 0xA3, 0x43, 0xE5, 0x8F, 0xA5, 0x43, 0xE5, 0x8F,
+ 0xAB, 0x43, 0xE5, 0x8F, 0xAF, 0x43, 0xE5, 0x8F,
+ 0xB1, 0x43, 0xE5, 0x8F, 0xB3, 0x43, 0xE5, 0x90,
+ 0x86, 0x43, 0xE5, 0x90, 0x88, 0x43, 0xE5, 0x90,
+ 0x8D, 0x43, 0xE5, 0x90, 0x8F, 0x43, 0xE5, 0x90,
+ 0x9D, 0x43, 0xE5, 0x90, 0xB8, 0x43, 0xE5, 0x90,
+ // Bytes 980 - 9bf
+ 0xB9, 0x43, 0xE5, 0x91, 0x82, 0x43, 0xE5, 0x91,
+ 0x88, 0x43, 0xE5, 0x91, 0xA8, 0x43, 0xE5, 0x92,
+ 0x9E, 0x43, 0xE5, 0x92, 0xA2, 0x43, 0xE5, 0x92,
+ 0xBD, 0x43, 0xE5, 0x93, 0xB6, 0x43, 0xE5, 0x94,
+ 0x90, 0x43, 0xE5, 0x95, 0x8F, 0x43, 0xE5, 0x95,
+ 0x93, 0x43, 0xE5, 0x95, 0x95, 0x43, 0xE5, 0x95,
+ 0xA3, 0x43, 0xE5, 0x96, 0x84, 0x43, 0xE5, 0x96,
+ 0x87, 0x43, 0xE5, 0x96, 0x99, 0x43, 0xE5, 0x96,
+ // Bytes 9c0 - 9ff
+ 0x9D, 0x43, 0xE5, 0x96, 0xAB, 0x43, 0xE5, 0x96,
+ 0xB3, 0x43, 0xE5, 0x96, 0xB6, 0x43, 0xE5, 0x97,
+ 0x80, 0x43, 0xE5, 0x97, 0x82, 0x43, 0xE5, 0x97,
+ 0xA2, 0x43, 0xE5, 0x98, 0x86, 0x43, 0xE5, 0x99,
+ 0x91, 0x43, 0xE5, 0x99, 0xA8, 0x43, 0xE5, 0x99,
+ 0xB4, 0x43, 0xE5, 0x9B, 0x97, 0x43, 0xE5, 0x9B,
+ 0x9B, 0x43, 0xE5, 0x9B, 0xB9, 0x43, 0xE5, 0x9C,
+ 0x96, 0x43, 0xE5, 0x9C, 0x97, 0x43, 0xE5, 0x9C,
+ // Bytes a00 - a3f
+ 0x9F, 0x43, 0xE5, 0x9C, 0xB0, 0x43, 0xE5, 0x9E,
+ 0x8B, 0x43, 0xE5, 0x9F, 0x8E, 0x43, 0xE5, 0x9F,
+ 0xB4, 0x43, 0xE5, 0xA0, 0x8D, 0x43, 0xE5, 0xA0,
+ 0xB1, 0x43, 0xE5, 0xA0, 0xB2, 0x43, 0xE5, 0xA1,
+ 0x80, 0x43, 0xE5, 0xA1, 0x9A, 0x43, 0xE5, 0xA1,
+ 0x9E, 0x43, 0xE5, 0xA2, 0xA8, 0x43, 0xE5, 0xA2,
+ 0xAC, 0x43, 0xE5, 0xA2, 0xB3, 0x43, 0xE5, 0xA3,
+ 0x98, 0x43, 0xE5, 0xA3, 0x9F, 0x43, 0xE5, 0xA3,
+ // Bytes a40 - a7f
+ 0xAB, 0x43, 0xE5, 0xA3, 0xAE, 0x43, 0xE5, 0xA3,
+ 0xB0, 0x43, 0xE5, 0xA3, 0xB2, 0x43, 0xE5, 0xA3,
+ 0xB7, 0x43, 0xE5, 0xA4, 0x82, 0x43, 0xE5, 0xA4,
+ 0x86, 0x43, 0xE5, 0xA4, 0x8A, 0x43, 0xE5, 0xA4,
+ 0x95, 0x43, 0xE5, 0xA4, 0x9A, 0x43, 0xE5, 0xA4,
+ 0x9C, 0x43, 0xE5, 0xA4, 0xA2, 0x43, 0xE5, 0xA4,
+ 0xA7, 0x43, 0xE5, 0xA4, 0xA9, 0x43, 0xE5, 0xA5,
+ 0x84, 0x43, 0xE5, 0xA5, 0x88, 0x43, 0xE5, 0xA5,
+ // Bytes a80 - abf
+ 0x91, 0x43, 0xE5, 0xA5, 0x94, 0x43, 0xE5, 0xA5,
+ 0xA2, 0x43, 0xE5, 0xA5, 0xB3, 0x43, 0xE5, 0xA7,
+ 0x98, 0x43, 0xE5, 0xA7, 0xAC, 0x43, 0xE5, 0xA8,
+ 0x9B, 0x43, 0xE5, 0xA8, 0xA7, 0x43, 0xE5, 0xA9,
+ 0xA2, 0x43, 0xE5, 0xA9, 0xA6, 0x43, 0xE5, 0xAA,
+ 0xB5, 0x43, 0xE5, 0xAC, 0x88, 0x43, 0xE5, 0xAC,
+ 0xA8, 0x43, 0xE5, 0xAC, 0xBE, 0x43, 0xE5, 0xAD,
+ 0x90, 0x43, 0xE5, 0xAD, 0x97, 0x43, 0xE5, 0xAD,
+ // Bytes ac0 - aff
+ 0xA6, 0x43, 0xE5, 0xAE, 0x80, 0x43, 0xE5, 0xAE,
+ 0x85, 0x43, 0xE5, 0xAE, 0x97, 0x43, 0xE5, 0xAF,
+ 0x83, 0x43, 0xE5, 0xAF, 0x98, 0x43, 0xE5, 0xAF,
+ 0xA7, 0x43, 0xE5, 0xAF, 0xAE, 0x43, 0xE5, 0xAF,
+ 0xB3, 0x43, 0xE5, 0xAF, 0xB8, 0x43, 0xE5, 0xAF,
+ 0xBF, 0x43, 0xE5, 0xB0, 0x86, 0x43, 0xE5, 0xB0,
+ 0x8F, 0x43, 0xE5, 0xB0, 0xA2, 0x43, 0xE5, 0xB0,
+ 0xB8, 0x43, 0xE5, 0xB0, 0xBF, 0x43, 0xE5, 0xB1,
+ // Bytes b00 - b3f
+ 0xA0, 0x43, 0xE5, 0xB1, 0xA2, 0x43, 0xE5, 0xB1,
+ 0xA4, 0x43, 0xE5, 0xB1, 0xA5, 0x43, 0xE5, 0xB1,
+ 0xAE, 0x43, 0xE5, 0xB1, 0xB1, 0x43, 0xE5, 0xB2,
+ 0x8D, 0x43, 0xE5, 0xB3, 0x80, 0x43, 0xE5, 0xB4,
+ 0x99, 0x43, 0xE5, 0xB5, 0x83, 0x43, 0xE5, 0xB5,
+ 0x90, 0x43, 0xE5, 0xB5, 0xAB, 0x43, 0xE5, 0xB5,
+ 0xAE, 0x43, 0xE5, 0xB5, 0xBC, 0x43, 0xE5, 0xB6,
+ 0xB2, 0x43, 0xE5, 0xB6, 0xBA, 0x43, 0xE5, 0xB7,
+ // Bytes b40 - b7f
+ 0x9B, 0x43, 0xE5, 0xB7, 0xA1, 0x43, 0xE5, 0xB7,
+ 0xA2, 0x43, 0xE5, 0xB7, 0xA5, 0x43, 0xE5, 0xB7,
+ 0xA6, 0x43, 0xE5, 0xB7, 0xB1, 0x43, 0xE5, 0xB7,
+ 0xBD, 0x43, 0xE5, 0xB7, 0xBE, 0x43, 0xE5, 0xB8,
+ 0xA8, 0x43, 0xE5, 0xB8, 0xBD, 0x43, 0xE5, 0xB9,
+ 0xA9, 0x43, 0xE5, 0xB9, 0xB2, 0x43, 0xE5, 0xB9,
+ 0xB4, 0x43, 0xE5, 0xB9, 0xBA, 0x43, 0xE5, 0xB9,
+ 0xBC, 0x43, 0xE5, 0xB9, 0xBF, 0x43, 0xE5, 0xBA,
+ // Bytes b80 - bbf
+ 0xA6, 0x43, 0xE5, 0xBA, 0xB0, 0x43, 0xE5, 0xBA,
+ 0xB3, 0x43, 0xE5, 0xBA, 0xB6, 0x43, 0xE5, 0xBB,
+ 0x89, 0x43, 0xE5, 0xBB, 0x8A, 0x43, 0xE5, 0xBB,
+ 0x92, 0x43, 0xE5, 0xBB, 0x93, 0x43, 0xE5, 0xBB,
+ 0x99, 0x43, 0xE5, 0xBB, 0xAC, 0x43, 0xE5, 0xBB,
+ 0xB4, 0x43, 0xE5, 0xBB, 0xBE, 0x43, 0xE5, 0xBC,
+ 0x84, 0x43, 0xE5, 0xBC, 0x8B, 0x43, 0xE5, 0xBC,
+ 0x93, 0x43, 0xE5, 0xBC, 0xA2, 0x43, 0xE5, 0xBD,
+ // Bytes bc0 - bff
+ 0x90, 0x43, 0xE5, 0xBD, 0x93, 0x43, 0xE5, 0xBD,
+ 0xA1, 0x43, 0xE5, 0xBD, 0xA2, 0x43, 0xE5, 0xBD,
+ 0xA9, 0x43, 0xE5, 0xBD, 0xAB, 0x43, 0xE5, 0xBD,
+ 0xB3, 0x43, 0xE5, 0xBE, 0x8B, 0x43, 0xE5, 0xBE,
+ 0x8C, 0x43, 0xE5, 0xBE, 0x97, 0x43, 0xE5, 0xBE,
+ 0x9A, 0x43, 0xE5, 0xBE, 0xA9, 0x43, 0xE5, 0xBE,
+ 0xAD, 0x43, 0xE5, 0xBF, 0x83, 0x43, 0xE5, 0xBF,
+ 0x8D, 0x43, 0xE5, 0xBF, 0x97, 0x43, 0xE5, 0xBF,
+ // Bytes c00 - c3f
+ 0xB5, 0x43, 0xE5, 0xBF, 0xB9, 0x43, 0xE6, 0x80,
+ 0x92, 0x43, 0xE6, 0x80, 0x9C, 0x43, 0xE6, 0x81,
+ 0xB5, 0x43, 0xE6, 0x82, 0x81, 0x43, 0xE6, 0x82,
+ 0x94, 0x43, 0xE6, 0x83, 0x87, 0x43, 0xE6, 0x83,
+ 0x98, 0x43, 0xE6, 0x83, 0xA1, 0x43, 0xE6, 0x84,
+ 0x88, 0x43, 0xE6, 0x85, 0x84, 0x43, 0xE6, 0x85,
+ 0x88, 0x43, 0xE6, 0x85, 0x8C, 0x43, 0xE6, 0x85,
+ 0x8E, 0x43, 0xE6, 0x85, 0xA0, 0x43, 0xE6, 0x85,
+ // Bytes c40 - c7f
+ 0xA8, 0x43, 0xE6, 0x85, 0xBA, 0x43, 0xE6, 0x86,
+ 0x8E, 0x43, 0xE6, 0x86, 0x90, 0x43, 0xE6, 0x86,
+ 0xA4, 0x43, 0xE6, 0x86, 0xAF, 0x43, 0xE6, 0x86,
+ 0xB2, 0x43, 0xE6, 0x87, 0x9E, 0x43, 0xE6, 0x87,
+ 0xB2, 0x43, 0xE6, 0x87, 0xB6, 0x43, 0xE6, 0x88,
+ 0x80, 0x43, 0xE6, 0x88, 0x88, 0x43, 0xE6, 0x88,
+ 0x90, 0x43, 0xE6, 0x88, 0x9B, 0x43, 0xE6, 0x88,
+ 0xAE, 0x43, 0xE6, 0x88, 0xB4, 0x43, 0xE6, 0x88,
+ // Bytes c80 - cbf
+ 0xB6, 0x43, 0xE6, 0x89, 0x8B, 0x43, 0xE6, 0x89,
+ 0x93, 0x43, 0xE6, 0x89, 0x9D, 0x43, 0xE6, 0x8A,
+ 0x95, 0x43, 0xE6, 0x8A, 0xB1, 0x43, 0xE6, 0x8B,
+ 0x89, 0x43, 0xE6, 0x8B, 0x8F, 0x43, 0xE6, 0x8B,
+ 0x93, 0x43, 0xE6, 0x8B, 0x94, 0x43, 0xE6, 0x8B,
+ 0xBC, 0x43, 0xE6, 0x8B, 0xBE, 0x43, 0xE6, 0x8C,
+ 0x87, 0x43, 0xE6, 0x8C, 0xBD, 0x43, 0xE6, 0x8D,
+ 0x90, 0x43, 0xE6, 0x8D, 0x95, 0x43, 0xE6, 0x8D,
+ // Bytes cc0 - cff
+ 0xA8, 0x43, 0xE6, 0x8D, 0xBB, 0x43, 0xE6, 0x8E,
+ 0x83, 0x43, 0xE6, 0x8E, 0xA0, 0x43, 0xE6, 0x8E,
+ 0xA9, 0x43, 0xE6, 0x8F, 0x84, 0x43, 0xE6, 0x8F,
+ 0x85, 0x43, 0xE6, 0x8F, 0xA4, 0x43, 0xE6, 0x90,
+ 0x9C, 0x43, 0xE6, 0x90, 0xA2, 0x43, 0xE6, 0x91,
+ 0x92, 0x43, 0xE6, 0x91, 0xA9, 0x43, 0xE6, 0x91,
+ 0xB7, 0x43, 0xE6, 0x91, 0xBE, 0x43, 0xE6, 0x92,
+ 0x9A, 0x43, 0xE6, 0x92, 0x9D, 0x43, 0xE6, 0x93,
+ // Bytes d00 - d3f
+ 0x84, 0x43, 0xE6, 0x94, 0xAF, 0x43, 0xE6, 0x94,
+ 0xB4, 0x43, 0xE6, 0x95, 0x8F, 0x43, 0xE6, 0x95,
+ 0x96, 0x43, 0xE6, 0x95, 0xAC, 0x43, 0xE6, 0x95,
+ 0xB8, 0x43, 0xE6, 0x96, 0x87, 0x43, 0xE6, 0x96,
+ 0x97, 0x43, 0xE6, 0x96, 0x99, 0x43, 0xE6, 0x96,
+ 0xA4, 0x43, 0xE6, 0x96, 0xB0, 0x43, 0xE6, 0x96,
+ 0xB9, 0x43, 0xE6, 0x97, 0x85, 0x43, 0xE6, 0x97,
+ 0xA0, 0x43, 0xE6, 0x97, 0xA2, 0x43, 0xE6, 0x97,
+ // Bytes d40 - d7f
+ 0xA3, 0x43, 0xE6, 0x97, 0xA5, 0x43, 0xE6, 0x98,
+ 0x93, 0x43, 0xE6, 0x98, 0xA0, 0x43, 0xE6, 0x99,
+ 0x89, 0x43, 0xE6, 0x99, 0xB4, 0x43, 0xE6, 0x9A,
+ 0x88, 0x43, 0xE6, 0x9A, 0x91, 0x43, 0xE6, 0x9A,
+ 0x9C, 0x43, 0xE6, 0x9A, 0xB4, 0x43, 0xE6, 0x9B,
+ 0x86, 0x43, 0xE6, 0x9B, 0xB0, 0x43, 0xE6, 0x9B,
+ 0xB4, 0x43, 0xE6, 0x9B, 0xB8, 0x43, 0xE6, 0x9C,
+ 0x80, 0x43, 0xE6, 0x9C, 0x88, 0x43, 0xE6, 0x9C,
+ // Bytes d80 - dbf
+ 0x89, 0x43, 0xE6, 0x9C, 0x97, 0x43, 0xE6, 0x9C,
+ 0x9B, 0x43, 0xE6, 0x9C, 0xA1, 0x43, 0xE6, 0x9C,
+ 0xA8, 0x43, 0xE6, 0x9D, 0x8E, 0x43, 0xE6, 0x9D,
+ 0x93, 0x43, 0xE6, 0x9D, 0x96, 0x43, 0xE6, 0x9D,
+ 0x9E, 0x43, 0xE6, 0x9D, 0xBB, 0x43, 0xE6, 0x9E,
+ 0x85, 0x43, 0xE6, 0x9E, 0x97, 0x43, 0xE6, 0x9F,
+ 0xB3, 0x43, 0xE6, 0x9F, 0xBA, 0x43, 0xE6, 0xA0,
+ 0x97, 0x43, 0xE6, 0xA0, 0x9F, 0x43, 0xE6, 0xA0,
+ // Bytes dc0 - dff
+ 0xAA, 0x43, 0xE6, 0xA1, 0x92, 0x43, 0xE6, 0xA2,
+ 0x81, 0x43, 0xE6, 0xA2, 0x85, 0x43, 0xE6, 0xA2,
+ 0x8E, 0x43, 0xE6, 0xA2, 0xA8, 0x43, 0xE6, 0xA4,
+ 0x94, 0x43, 0xE6, 0xA5, 0x82, 0x43, 0xE6, 0xA6,
+ 0xA3, 0x43, 0xE6, 0xA7, 0xAA, 0x43, 0xE6, 0xA8,
+ 0x82, 0x43, 0xE6, 0xA8, 0x93, 0x43, 0xE6, 0xAA,
+ 0xA8, 0x43, 0xE6, 0xAB, 0x93, 0x43, 0xE6, 0xAB,
+ 0x9B, 0x43, 0xE6, 0xAC, 0x84, 0x43, 0xE6, 0xAC,
+ // Bytes e00 - e3f
+ 0xA0, 0x43, 0xE6, 0xAC, 0xA1, 0x43, 0xE6, 0xAD,
+ 0x94, 0x43, 0xE6, 0xAD, 0xA2, 0x43, 0xE6, 0xAD,
+ 0xA3, 0x43, 0xE6, 0xAD, 0xB2, 0x43, 0xE6, 0xAD,
+ 0xB7, 0x43, 0xE6, 0xAD, 0xB9, 0x43, 0xE6, 0xAE,
+ 0x9F, 0x43, 0xE6, 0xAE, 0xAE, 0x43, 0xE6, 0xAE,
+ 0xB3, 0x43, 0xE6, 0xAE, 0xBA, 0x43, 0xE6, 0xAE,
+ 0xBB, 0x43, 0xE6, 0xAF, 0x8B, 0x43, 0xE6, 0xAF,
+ 0x8D, 0x43, 0xE6, 0xAF, 0x94, 0x43, 0xE6, 0xAF,
+ // Bytes e40 - e7f
+ 0x9B, 0x43, 0xE6, 0xB0, 0x8F, 0x43, 0xE6, 0xB0,
+ 0x94, 0x43, 0xE6, 0xB0, 0xB4, 0x43, 0xE6, 0xB1,
+ 0x8E, 0x43, 0xE6, 0xB1, 0xA7, 0x43, 0xE6, 0xB2,
+ 0x88, 0x43, 0xE6, 0xB2, 0xBF, 0x43, 0xE6, 0xB3,
+ 0x8C, 0x43, 0xE6, 0xB3, 0x8D, 0x43, 0xE6, 0xB3,
+ 0xA5, 0x43, 0xE6, 0xB3, 0xA8, 0x43, 0xE6, 0xB4,
+ 0x96, 0x43, 0xE6, 0xB4, 0x9B, 0x43, 0xE6, 0xB4,
+ 0x9E, 0x43, 0xE6, 0xB4, 0xB4, 0x43, 0xE6, 0xB4,
+ // Bytes e80 - ebf
+ 0xBE, 0x43, 0xE6, 0xB5, 0x81, 0x43, 0xE6, 0xB5,
+ 0xA9, 0x43, 0xE6, 0xB5, 0xAA, 0x43, 0xE6, 0xB5,
+ 0xB7, 0x43, 0xE6, 0xB5, 0xB8, 0x43, 0xE6, 0xB6,
+ 0x85, 0x43, 0xE6, 0xB7, 0x8B, 0x43, 0xE6, 0xB7,
+ 0x9A, 0x43, 0xE6, 0xB7, 0xAA, 0x43, 0xE6, 0xB7,
+ 0xB9, 0x43, 0xE6, 0xB8, 0x9A, 0x43, 0xE6, 0xB8,
+ 0xAF, 0x43, 0xE6, 0xB9, 0xAE, 0x43, 0xE6, 0xBA,
+ 0x80, 0x43, 0xE6, 0xBA, 0x9C, 0x43, 0xE6, 0xBA,
+ // Bytes ec0 - eff
+ 0xBA, 0x43, 0xE6, 0xBB, 0x87, 0x43, 0xE6, 0xBB,
+ 0x8B, 0x43, 0xE6, 0xBB, 0x91, 0x43, 0xE6, 0xBB,
+ 0x9B, 0x43, 0xE6, 0xBC, 0x8F, 0x43, 0xE6, 0xBC,
+ 0x94, 0x43, 0xE6, 0xBC, 0xA2, 0x43, 0xE6, 0xBC,
+ 0xA3, 0x43, 0xE6, 0xBD, 0xAE, 0x43, 0xE6, 0xBF,
+ 0x86, 0x43, 0xE6, 0xBF, 0xAB, 0x43, 0xE6, 0xBF,
+ 0xBE, 0x43, 0xE7, 0x80, 0x9B, 0x43, 0xE7, 0x80,
+ 0x9E, 0x43, 0xE7, 0x80, 0xB9, 0x43, 0xE7, 0x81,
+ // Bytes f00 - f3f
+ 0x8A, 0x43, 0xE7, 0x81, 0xAB, 0x43, 0xE7, 0x81,
+ 0xB0, 0x43, 0xE7, 0x81, 0xB7, 0x43, 0xE7, 0x81,
+ 0xBD, 0x43, 0xE7, 0x82, 0x99, 0x43, 0xE7, 0x82,
+ 0xAD, 0x43, 0xE7, 0x83, 0x88, 0x43, 0xE7, 0x83,
+ 0x99, 0x43, 0xE7, 0x84, 0xA1, 0x43, 0xE7, 0x85,
+ 0x85, 0x43, 0xE7, 0x85, 0x89, 0x43, 0xE7, 0x85,
+ 0xAE, 0x43, 0xE7, 0x86, 0x9C, 0x43, 0xE7, 0x87,
+ 0x8E, 0x43, 0xE7, 0x87, 0x90, 0x43, 0xE7, 0x88,
+ // Bytes f40 - f7f
+ 0x90, 0x43, 0xE7, 0x88, 0x9B, 0x43, 0xE7, 0x88,
+ 0xA8, 0x43, 0xE7, 0x88, 0xAA, 0x43, 0xE7, 0x88,
+ 0xAB, 0x43, 0xE7, 0x88, 0xB5, 0x43, 0xE7, 0x88,
+ 0xB6, 0x43, 0xE7, 0x88, 0xBB, 0x43, 0xE7, 0x88,
+ 0xBF, 0x43, 0xE7, 0x89, 0x87, 0x43, 0xE7, 0x89,
+ 0x90, 0x43, 0xE7, 0x89, 0x99, 0x43, 0xE7, 0x89,
+ 0x9B, 0x43, 0xE7, 0x89, 0xA2, 0x43, 0xE7, 0x89,
+ 0xB9, 0x43, 0xE7, 0x8A, 0x80, 0x43, 0xE7, 0x8A,
+ // Bytes f80 - fbf
+ 0x95, 0x43, 0xE7, 0x8A, 0xAC, 0x43, 0xE7, 0x8A,
+ 0xAF, 0x43, 0xE7, 0x8B, 0x80, 0x43, 0xE7, 0x8B,
+ 0xBC, 0x43, 0xE7, 0x8C, 0xAA, 0x43, 0xE7, 0x8D,
+ 0xB5, 0x43, 0xE7, 0x8D, 0xBA, 0x43, 0xE7, 0x8E,
+ 0x84, 0x43, 0xE7, 0x8E, 0x87, 0x43, 0xE7, 0x8E,
+ 0x89, 0x43, 0xE7, 0x8E, 0x8B, 0x43, 0xE7, 0x8E,
+ 0xA5, 0x43, 0xE7, 0x8E, 0xB2, 0x43, 0xE7, 0x8F,
+ 0x9E, 0x43, 0xE7, 0x90, 0x86, 0x43, 0xE7, 0x90,
+ // Bytes fc0 - fff
+ 0x89, 0x43, 0xE7, 0x90, 0xA2, 0x43, 0xE7, 0x91,
+ 0x87, 0x43, 0xE7, 0x91, 0x9C, 0x43, 0xE7, 0x91,
+ 0xA9, 0x43, 0xE7, 0x91, 0xB1, 0x43, 0xE7, 0x92,
+ 0x85, 0x43, 0xE7, 0x92, 0x89, 0x43, 0xE7, 0x92,
+ 0x98, 0x43, 0xE7, 0x93, 0x8A, 0x43, 0xE7, 0x93,
+ 0x9C, 0x43, 0xE7, 0x93, 0xA6, 0x43, 0xE7, 0x94,
+ 0x86, 0x43, 0xE7, 0x94, 0x98, 0x43, 0xE7, 0x94,
+ 0x9F, 0x43, 0xE7, 0x94, 0xA4, 0x43, 0xE7, 0x94,
+ // Bytes 1000 - 103f
+ 0xA8, 0x43, 0xE7, 0x94, 0xB0, 0x43, 0xE7, 0x94,
+ 0xB2, 0x43, 0xE7, 0x94, 0xB3, 0x43, 0xE7, 0x94,
+ 0xB7, 0x43, 0xE7, 0x94, 0xBB, 0x43, 0xE7, 0x94,
+ 0xBE, 0x43, 0xE7, 0x95, 0x99, 0x43, 0xE7, 0x95,
+ 0xA5, 0x43, 0xE7, 0x95, 0xB0, 0x43, 0xE7, 0x96,
+ 0x8B, 0x43, 0xE7, 0x96, 0x92, 0x43, 0xE7, 0x97,
+ 0xA2, 0x43, 0xE7, 0x98, 0x90, 0x43, 0xE7, 0x98,
+ 0x9D, 0x43, 0xE7, 0x98, 0x9F, 0x43, 0xE7, 0x99,
+ // Bytes 1040 - 107f
+ 0x82, 0x43, 0xE7, 0x99, 0xA9, 0x43, 0xE7, 0x99,
+ 0xB6, 0x43, 0xE7, 0x99, 0xBD, 0x43, 0xE7, 0x9A,
+ 0xAE, 0x43, 0xE7, 0x9A, 0xBF, 0x43, 0xE7, 0x9B,
+ 0x8A, 0x43, 0xE7, 0x9B, 0x9B, 0x43, 0xE7, 0x9B,
+ 0xA3, 0x43, 0xE7, 0x9B, 0xA7, 0x43, 0xE7, 0x9B,
+ 0xAE, 0x43, 0xE7, 0x9B, 0xB4, 0x43, 0xE7, 0x9C,
+ 0x81, 0x43, 0xE7, 0x9C, 0x9E, 0x43, 0xE7, 0x9C,
+ 0x9F, 0x43, 0xE7, 0x9D, 0x80, 0x43, 0xE7, 0x9D,
+ // Bytes 1080 - 10bf
+ 0x8A, 0x43, 0xE7, 0x9E, 0x8B, 0x43, 0xE7, 0x9E,
+ 0xA7, 0x43, 0xE7, 0x9F, 0x9B, 0x43, 0xE7, 0x9F,
+ 0xA2, 0x43, 0xE7, 0x9F, 0xB3, 0x43, 0xE7, 0xA1,
+ 0x8E, 0x43, 0xE7, 0xA1, 0xAB, 0x43, 0xE7, 0xA2,
+ 0x8C, 0x43, 0xE7, 0xA2, 0x91, 0x43, 0xE7, 0xA3,
+ 0x8A, 0x43, 0xE7, 0xA3, 0x8C, 0x43, 0xE7, 0xA3,
+ 0xBB, 0x43, 0xE7, 0xA4, 0xAA, 0x43, 0xE7, 0xA4,
+ 0xBA, 0x43, 0xE7, 0xA4, 0xBC, 0x43, 0xE7, 0xA4,
+ // Bytes 10c0 - 10ff
+ 0xBE, 0x43, 0xE7, 0xA5, 0x88, 0x43, 0xE7, 0xA5,
+ 0x89, 0x43, 0xE7, 0xA5, 0x90, 0x43, 0xE7, 0xA5,
+ 0x96, 0x43, 0xE7, 0xA5, 0x9D, 0x43, 0xE7, 0xA5,
+ 0x9E, 0x43, 0xE7, 0xA5, 0xA5, 0x43, 0xE7, 0xA5,
+ 0xBF, 0x43, 0xE7, 0xA6, 0x81, 0x43, 0xE7, 0xA6,
+ 0x8D, 0x43, 0xE7, 0xA6, 0x8E, 0x43, 0xE7, 0xA6,
+ 0x8F, 0x43, 0xE7, 0xA6, 0xAE, 0x43, 0xE7, 0xA6,
+ 0xB8, 0x43, 0xE7, 0xA6, 0xBE, 0x43, 0xE7, 0xA7,
+ // Bytes 1100 - 113f
+ 0x8A, 0x43, 0xE7, 0xA7, 0x98, 0x43, 0xE7, 0xA7,
+ 0xAB, 0x43, 0xE7, 0xA8, 0x9C, 0x43, 0xE7, 0xA9,
+ 0x80, 0x43, 0xE7, 0xA9, 0x8A, 0x43, 0xE7, 0xA9,
+ 0x8F, 0x43, 0xE7, 0xA9, 0xB4, 0x43, 0xE7, 0xA9,
+ 0xBA, 0x43, 0xE7, 0xAA, 0x81, 0x43, 0xE7, 0xAA,
+ 0xB1, 0x43, 0xE7, 0xAB, 0x8B, 0x43, 0xE7, 0xAB,
+ 0xAE, 0x43, 0xE7, 0xAB, 0xB9, 0x43, 0xE7, 0xAC,
+ 0xA0, 0x43, 0xE7, 0xAE, 0x8F, 0x43, 0xE7, 0xAF,
+ // Bytes 1140 - 117f
+ 0x80, 0x43, 0xE7, 0xAF, 0x86, 0x43, 0xE7, 0xAF,
+ 0x89, 0x43, 0xE7, 0xB0, 0xBE, 0x43, 0xE7, 0xB1,
+ 0xA0, 0x43, 0xE7, 0xB1, 0xB3, 0x43, 0xE7, 0xB1,
+ 0xBB, 0x43, 0xE7, 0xB2, 0x92, 0x43, 0xE7, 0xB2,
+ 0xBE, 0x43, 0xE7, 0xB3, 0x92, 0x43, 0xE7, 0xB3,
+ 0x96, 0x43, 0xE7, 0xB3, 0xA3, 0x43, 0xE7, 0xB3,
+ 0xA7, 0x43, 0xE7, 0xB3, 0xA8, 0x43, 0xE7, 0xB3,
+ 0xB8, 0x43, 0xE7, 0xB4, 0x80, 0x43, 0xE7, 0xB4,
+ // Bytes 1180 - 11bf
+ 0x90, 0x43, 0xE7, 0xB4, 0xA2, 0x43, 0xE7, 0xB4,
+ 0xAF, 0x43, 0xE7, 0xB5, 0x82, 0x43, 0xE7, 0xB5,
+ 0x9B, 0x43, 0xE7, 0xB5, 0xA3, 0x43, 0xE7, 0xB6,
+ 0xA0, 0x43, 0xE7, 0xB6, 0xBE, 0x43, 0xE7, 0xB7,
+ 0x87, 0x43, 0xE7, 0xB7, 0xB4, 0x43, 0xE7, 0xB8,
+ 0x82, 0x43, 0xE7, 0xB8, 0x89, 0x43, 0xE7, 0xB8,
+ 0xB7, 0x43, 0xE7, 0xB9, 0x81, 0x43, 0xE7, 0xB9,
+ 0x85, 0x43, 0xE7, 0xBC, 0xB6, 0x43, 0xE7, 0xBC,
+ // Bytes 11c0 - 11ff
+ 0xBE, 0x43, 0xE7, 0xBD, 0x91, 0x43, 0xE7, 0xBD,
+ 0xB2, 0x43, 0xE7, 0xBD, 0xB9, 0x43, 0xE7, 0xBD,
+ 0xBA, 0x43, 0xE7, 0xBE, 0x85, 0x43, 0xE7, 0xBE,
+ 0x8A, 0x43, 0xE7, 0xBE, 0x95, 0x43, 0xE7, 0xBE,
+ 0x9A, 0x43, 0xE7, 0xBE, 0xBD, 0x43, 0xE7, 0xBF,
+ 0xBA, 0x43, 0xE8, 0x80, 0x81, 0x43, 0xE8, 0x80,
+ 0x85, 0x43, 0xE8, 0x80, 0x8C, 0x43, 0xE8, 0x80,
+ 0x92, 0x43, 0xE8, 0x80, 0xB3, 0x43, 0xE8, 0x81,
+ // Bytes 1200 - 123f
+ 0x86, 0x43, 0xE8, 0x81, 0xA0, 0x43, 0xE8, 0x81,
+ 0xAF, 0x43, 0xE8, 0x81, 0xB0, 0x43, 0xE8, 0x81,
+ 0xBE, 0x43, 0xE8, 0x81, 0xBF, 0x43, 0xE8, 0x82,
+ 0x89, 0x43, 0xE8, 0x82, 0x8B, 0x43, 0xE8, 0x82,
+ 0xAD, 0x43, 0xE8, 0x82, 0xB2, 0x43, 0xE8, 0x84,
+ 0x83, 0x43, 0xE8, 0x84, 0xBE, 0x43, 0xE8, 0x87,
+ 0x98, 0x43, 0xE8, 0x87, 0xA3, 0x43, 0xE8, 0x87,
+ 0xA8, 0x43, 0xE8, 0x87, 0xAA, 0x43, 0xE8, 0x87,
+ // Bytes 1240 - 127f
+ 0xAD, 0x43, 0xE8, 0x87, 0xB3, 0x43, 0xE8, 0x87,
+ 0xBC, 0x43, 0xE8, 0x88, 0x81, 0x43, 0xE8, 0x88,
+ 0x84, 0x43, 0xE8, 0x88, 0x8C, 0x43, 0xE8, 0x88,
+ 0x98, 0x43, 0xE8, 0x88, 0x9B, 0x43, 0xE8, 0x88,
+ 0x9F, 0x43, 0xE8, 0x89, 0xAE, 0x43, 0xE8, 0x89,
+ 0xAF, 0x43, 0xE8, 0x89, 0xB2, 0x43, 0xE8, 0x89,
+ 0xB8, 0x43, 0xE8, 0x89, 0xB9, 0x43, 0xE8, 0x8A,
+ 0x8B, 0x43, 0xE8, 0x8A, 0x91, 0x43, 0xE8, 0x8A,
+ // Bytes 1280 - 12bf
+ 0x9D, 0x43, 0xE8, 0x8A, 0xB1, 0x43, 0xE8, 0x8A,
+ 0xB3, 0x43, 0xE8, 0x8A, 0xBD, 0x43, 0xE8, 0x8B,
+ 0xA5, 0x43, 0xE8, 0x8B, 0xA6, 0x43, 0xE8, 0x8C,
+ 0x9D, 0x43, 0xE8, 0x8C, 0xA3, 0x43, 0xE8, 0x8C,
+ 0xB6, 0x43, 0xE8, 0x8D, 0x92, 0x43, 0xE8, 0x8D,
+ 0x93, 0x43, 0xE8, 0x8D, 0xA3, 0x43, 0xE8, 0x8E,
+ 0xAD, 0x43, 0xE8, 0x8E, 0xBD, 0x43, 0xE8, 0x8F,
+ 0x89, 0x43, 0xE8, 0x8F, 0x8A, 0x43, 0xE8, 0x8F,
+ // Bytes 12c0 - 12ff
+ 0x8C, 0x43, 0xE8, 0x8F, 0x9C, 0x43, 0xE8, 0x8F,
+ 0xA7, 0x43, 0xE8, 0x8F, 0xAF, 0x43, 0xE8, 0x8F,
+ 0xB1, 0x43, 0xE8, 0x90, 0xBD, 0x43, 0xE8, 0x91,
+ 0x89, 0x43, 0xE8, 0x91, 0x97, 0x43, 0xE8, 0x93,
+ 0xAE, 0x43, 0xE8, 0x93, 0xB1, 0x43, 0xE8, 0x93,
+ 0xB3, 0x43, 0xE8, 0x93, 0xBC, 0x43, 0xE8, 0x94,
+ 0x96, 0x43, 0xE8, 0x95, 0xA4, 0x43, 0xE8, 0x97,
+ 0x8D, 0x43, 0xE8, 0x97, 0xBA, 0x43, 0xE8, 0x98,
+ // Bytes 1300 - 133f
+ 0x86, 0x43, 0xE8, 0x98, 0x92, 0x43, 0xE8, 0x98,
+ 0xAD, 0x43, 0xE8, 0x98, 0xBF, 0x43, 0xE8, 0x99,
+ 0x8D, 0x43, 0xE8, 0x99, 0x90, 0x43, 0xE8, 0x99,
+ 0x9C, 0x43, 0xE8, 0x99, 0xA7, 0x43, 0xE8, 0x99,
+ 0xA9, 0x43, 0xE8, 0x99, 0xAB, 0x43, 0xE8, 0x9A,
+ 0x88, 0x43, 0xE8, 0x9A, 0xA9, 0x43, 0xE8, 0x9B,
+ 0xA2, 0x43, 0xE8, 0x9C, 0x8E, 0x43, 0xE8, 0x9C,
+ 0xA8, 0x43, 0xE8, 0x9D, 0xAB, 0x43, 0xE8, 0x9D,
+ // Bytes 1340 - 137f
+ 0xB9, 0x43, 0xE8, 0x9E, 0x86, 0x43, 0xE8, 0x9E,
+ 0xBA, 0x43, 0xE8, 0x9F, 0xA1, 0x43, 0xE8, 0xA0,
+ 0x81, 0x43, 0xE8, 0xA0, 0x9F, 0x43, 0xE8, 0xA1,
+ 0x80, 0x43, 0xE8, 0xA1, 0x8C, 0x43, 0xE8, 0xA1,
+ 0xA0, 0x43, 0xE8, 0xA1, 0xA3, 0x43, 0xE8, 0xA3,
+ 0x82, 0x43, 0xE8, 0xA3, 0x8F, 0x43, 0xE8, 0xA3,
+ 0x97, 0x43, 0xE8, 0xA3, 0x9E, 0x43, 0xE8, 0xA3,
+ 0xA1, 0x43, 0xE8, 0xA3, 0xB8, 0x43, 0xE8, 0xA3,
+ // Bytes 1380 - 13bf
+ 0xBA, 0x43, 0xE8, 0xA4, 0x90, 0x43, 0xE8, 0xA5,
+ 0x81, 0x43, 0xE8, 0xA5, 0xA4, 0x43, 0xE8, 0xA5,
+ 0xBE, 0x43, 0xE8, 0xA6, 0x86, 0x43, 0xE8, 0xA6,
+ 0x8B, 0x43, 0xE8, 0xA6, 0x96, 0x43, 0xE8, 0xA7,
+ 0x92, 0x43, 0xE8, 0xA7, 0xA3, 0x43, 0xE8, 0xA8,
+ 0x80, 0x43, 0xE8, 0xAA, 0xA0, 0x43, 0xE8, 0xAA,
+ 0xAA, 0x43, 0xE8, 0xAA, 0xBF, 0x43, 0xE8, 0xAB,
+ 0x8B, 0x43, 0xE8, 0xAB, 0x92, 0x43, 0xE8, 0xAB,
+ // Bytes 13c0 - 13ff
+ 0x96, 0x43, 0xE8, 0xAB, 0xAD, 0x43, 0xE8, 0xAB,
+ 0xB8, 0x43, 0xE8, 0xAB, 0xBE, 0x43, 0xE8, 0xAC,
+ 0x81, 0x43, 0xE8, 0xAC, 0xB9, 0x43, 0xE8, 0xAD,
+ 0x98, 0x43, 0xE8, 0xAE, 0x80, 0x43, 0xE8, 0xAE,
+ 0x8A, 0x43, 0xE8, 0xB0, 0xB7, 0x43, 0xE8, 0xB1,
+ 0x86, 0x43, 0xE8, 0xB1, 0x88, 0x43, 0xE8, 0xB1,
+ 0x95, 0x43, 0xE8, 0xB1, 0xB8, 0x43, 0xE8, 0xB2,
+ 0x9D, 0x43, 0xE8, 0xB2, 0xA1, 0x43, 0xE8, 0xB2,
+ // Bytes 1400 - 143f
+ 0xA9, 0x43, 0xE8, 0xB2, 0xAB, 0x43, 0xE8, 0xB3,
+ 0x81, 0x43, 0xE8, 0xB3, 0x82, 0x43, 0xE8, 0xB3,
+ 0x87, 0x43, 0xE8, 0xB3, 0x88, 0x43, 0xE8, 0xB3,
+ 0x93, 0x43, 0xE8, 0xB4, 0x88, 0x43, 0xE8, 0xB4,
+ 0x9B, 0x43, 0xE8, 0xB5, 0xA4, 0x43, 0xE8, 0xB5,
+ 0xB0, 0x43, 0xE8, 0xB5, 0xB7, 0x43, 0xE8, 0xB6,
+ 0xB3, 0x43, 0xE8, 0xB6, 0xBC, 0x43, 0xE8, 0xB7,
+ 0x8B, 0x43, 0xE8, 0xB7, 0xAF, 0x43, 0xE8, 0xB7,
+ // Bytes 1440 - 147f
+ 0xB0, 0x43, 0xE8, 0xBA, 0xAB, 0x43, 0xE8, 0xBB,
+ 0x8A, 0x43, 0xE8, 0xBB, 0x94, 0x43, 0xE8, 0xBC,
+ 0xA6, 0x43, 0xE8, 0xBC, 0xAA, 0x43, 0xE8, 0xBC,
+ 0xB8, 0x43, 0xE8, 0xBC, 0xBB, 0x43, 0xE8, 0xBD,
+ 0xA2, 0x43, 0xE8, 0xBE, 0x9B, 0x43, 0xE8, 0xBE,
+ 0x9E, 0x43, 0xE8, 0xBE, 0xB0, 0x43, 0xE8, 0xBE,
+ 0xB5, 0x43, 0xE8, 0xBE, 0xB6, 0x43, 0xE9, 0x80,
+ 0xA3, 0x43, 0xE9, 0x80, 0xB8, 0x43, 0xE9, 0x81,
+ // Bytes 1480 - 14bf
+ 0x8A, 0x43, 0xE9, 0x81, 0xA9, 0x43, 0xE9, 0x81,
+ 0xB2, 0x43, 0xE9, 0x81, 0xBC, 0x43, 0xE9, 0x82,
+ 0x8F, 0x43, 0xE9, 0x82, 0x91, 0x43, 0xE9, 0x82,
+ 0x94, 0x43, 0xE9, 0x83, 0x8E, 0x43, 0xE9, 0x83,
+ 0x9E, 0x43, 0xE9, 0x83, 0xB1, 0x43, 0xE9, 0x83,
+ 0xBD, 0x43, 0xE9, 0x84, 0x91, 0x43, 0xE9, 0x84,
+ 0x9B, 0x43, 0xE9, 0x85, 0x89, 0x43, 0xE9, 0x85,
+ 0xAA, 0x43, 0xE9, 0x86, 0x99, 0x43, 0xE9, 0x86,
+ // Bytes 14c0 - 14ff
+ 0xB4, 0x43, 0xE9, 0x87, 0x86, 0x43, 0xE9, 0x87,
+ 0x8C, 0x43, 0xE9, 0x87, 0x8F, 0x43, 0xE9, 0x87,
+ 0x91, 0x43, 0xE9, 0x88, 0xB4, 0x43, 0xE9, 0x88,
+ 0xB8, 0x43, 0xE9, 0x89, 0xB6, 0x43, 0xE9, 0x89,
+ 0xBC, 0x43, 0xE9, 0x8B, 0x97, 0x43, 0xE9, 0x8B,
+ 0x98, 0x43, 0xE9, 0x8C, 0x84, 0x43, 0xE9, 0x8D,
+ 0x8A, 0x43, 0xE9, 0x8F, 0xB9, 0x43, 0xE9, 0x90,
+ 0x95, 0x43, 0xE9, 0x95, 0xB7, 0x43, 0xE9, 0x96,
+ // Bytes 1500 - 153f
+ 0x80, 0x43, 0xE9, 0x96, 0x8B, 0x43, 0xE9, 0x96,
+ 0xAD, 0x43, 0xE9, 0x96, 0xB7, 0x43, 0xE9, 0x98,
+ 0x9C, 0x43, 0xE9, 0x98, 0xAE, 0x43, 0xE9, 0x99,
+ 0x8B, 0x43, 0xE9, 0x99, 0x8D, 0x43, 0xE9, 0x99,
+ 0xB5, 0x43, 0xE9, 0x99, 0xB8, 0x43, 0xE9, 0x99,
+ 0xBC, 0x43, 0xE9, 0x9A, 0x86, 0x43, 0xE9, 0x9A,
+ 0xA3, 0x43, 0xE9, 0x9A, 0xB6, 0x43, 0xE9, 0x9A,
+ 0xB7, 0x43, 0xE9, 0x9A, 0xB8, 0x43, 0xE9, 0x9A,
+ // Bytes 1540 - 157f
+ 0xB9, 0x43, 0xE9, 0x9B, 0x83, 0x43, 0xE9, 0x9B,
+ 0xA2, 0x43, 0xE9, 0x9B, 0xA3, 0x43, 0xE9, 0x9B,
+ 0xA8, 0x43, 0xE9, 0x9B, 0xB6, 0x43, 0xE9, 0x9B,
+ 0xB7, 0x43, 0xE9, 0x9C, 0xA3, 0x43, 0xE9, 0x9C,
+ 0xB2, 0x43, 0xE9, 0x9D, 0x88, 0x43, 0xE9, 0x9D,
+ 0x91, 0x43, 0xE9, 0x9D, 0x96, 0x43, 0xE9, 0x9D,
+ 0x9E, 0x43, 0xE9, 0x9D, 0xA2, 0x43, 0xE9, 0x9D,
+ 0xA9, 0x43, 0xE9, 0x9F, 0x8B, 0x43, 0xE9, 0x9F,
+ // Bytes 1580 - 15bf
+ 0x9B, 0x43, 0xE9, 0x9F, 0xA0, 0x43, 0xE9, 0x9F,
+ 0xAD, 0x43, 0xE9, 0x9F, 0xB3, 0x43, 0xE9, 0x9F,
+ 0xBF, 0x43, 0xE9, 0xA0, 0x81, 0x43, 0xE9, 0xA0,
+ 0x85, 0x43, 0xE9, 0xA0, 0x8B, 0x43, 0xE9, 0xA0,
+ 0x98, 0x43, 0xE9, 0xA0, 0xA9, 0x43, 0xE9, 0xA0,
+ 0xBB, 0x43, 0xE9, 0xA1, 0x9E, 0x43, 0xE9, 0xA2,
+ 0xA8, 0x43, 0xE9, 0xA3, 0x9B, 0x43, 0xE9, 0xA3,
+ 0x9F, 0x43, 0xE9, 0xA3, 0xA2, 0x43, 0xE9, 0xA3,
+ // Bytes 15c0 - 15ff
+ 0xAF, 0x43, 0xE9, 0xA3, 0xBC, 0x43, 0xE9, 0xA4,
+ 0xA8, 0x43, 0xE9, 0xA4, 0xA9, 0x43, 0xE9, 0xA6,
+ 0x96, 0x43, 0xE9, 0xA6, 0x99, 0x43, 0xE9, 0xA6,
+ 0xA7, 0x43, 0xE9, 0xA6, 0xAC, 0x43, 0xE9, 0xA7,
+ 0x82, 0x43, 0xE9, 0xA7, 0xB1, 0x43, 0xE9, 0xA7,
+ 0xBE, 0x43, 0xE9, 0xA9, 0xAA, 0x43, 0xE9, 0xAA,
+ 0xA8, 0x43, 0xE9, 0xAB, 0x98, 0x43, 0xE9, 0xAB,
+ 0x9F, 0x43, 0xE9, 0xAC, 0x92, 0x43, 0xE9, 0xAC,
+ // Bytes 1600 - 163f
+ 0xA5, 0x43, 0xE9, 0xAC, 0xAF, 0x43, 0xE9, 0xAC,
+ 0xB2, 0x43, 0xE9, 0xAC, 0xBC, 0x43, 0xE9, 0xAD,
+ 0x9A, 0x43, 0xE9, 0xAD, 0xAF, 0x43, 0xE9, 0xB1,
+ 0x80, 0x43, 0xE9, 0xB1, 0x97, 0x43, 0xE9, 0xB3,
+ 0xA5, 0x43, 0xE9, 0xB3, 0xBD, 0x43, 0xE9, 0xB5,
+ 0xA7, 0x43, 0xE9, 0xB6, 0xB4, 0x43, 0xE9, 0xB7,
+ 0xBA, 0x43, 0xE9, 0xB8, 0x9E, 0x43, 0xE9, 0xB9,
+ 0xB5, 0x43, 0xE9, 0xB9, 0xBF, 0x43, 0xE9, 0xBA,
+ // Bytes 1640 - 167f
+ 0x97, 0x43, 0xE9, 0xBA, 0x9F, 0x43, 0xE9, 0xBA,
+ 0xA5, 0x43, 0xE9, 0xBA, 0xBB, 0x43, 0xE9, 0xBB,
+ 0x83, 0x43, 0xE9, 0xBB, 0x8D, 0x43, 0xE9, 0xBB,
+ 0x8E, 0x43, 0xE9, 0xBB, 0x91, 0x43, 0xE9, 0xBB,
+ 0xB9, 0x43, 0xE9, 0xBB, 0xBD, 0x43, 0xE9, 0xBB,
+ 0xBE, 0x43, 0xE9, 0xBC, 0x85, 0x43, 0xE9, 0xBC,
+ 0x8E, 0x43, 0xE9, 0xBC, 0x8F, 0x43, 0xE9, 0xBC,
+ 0x93, 0x43, 0xE9, 0xBC, 0x96, 0x43, 0xE9, 0xBC,
+ // Bytes 1680 - 16bf
+ 0xA0, 0x43, 0xE9, 0xBC, 0xBB, 0x43, 0xE9, 0xBD,
+ 0x83, 0x43, 0xE9, 0xBD, 0x8A, 0x43, 0xE9, 0xBD,
+ 0x92, 0x43, 0xE9, 0xBE, 0x8D, 0x43, 0xE9, 0xBE,
+ 0x8E, 0x43, 0xE9, 0xBE, 0x9C, 0x43, 0xE9, 0xBE,
+ 0x9F, 0x43, 0xE9, 0xBE, 0xA0, 0x43, 0xEA, 0x9D,
+ 0xAF, 0x44, 0xF0, 0xA0, 0x84, 0xA2, 0x44, 0xF0,
+ 0xA0, 0x94, 0x9C, 0x44, 0xF0, 0xA0, 0x94, 0xA5,
+ 0x44, 0xF0, 0xA0, 0x95, 0x8B, 0x44, 0xF0, 0xA0,
+ // Bytes 16c0 - 16ff
+ 0x98, 0xBA, 0x44, 0xF0, 0xA0, 0xA0, 0x84, 0x44,
+ 0xF0, 0xA0, 0xA3, 0x9E, 0x44, 0xF0, 0xA0, 0xA8,
+ 0xAC, 0x44, 0xF0, 0xA0, 0xAD, 0xA3, 0x44, 0xF0,
+ 0xA1, 0x93, 0xA4, 0x44, 0xF0, 0xA1, 0x9A, 0xA8,
+ 0x44, 0xF0, 0xA1, 0x9B, 0xAA, 0x44, 0xF0, 0xA1,
+ 0xA7, 0x88, 0x44, 0xF0, 0xA1, 0xAC, 0x98, 0x44,
+ 0xF0, 0xA1, 0xB4, 0x8B, 0x44, 0xF0, 0xA1, 0xB7,
+ 0xA4, 0x44, 0xF0, 0xA1, 0xB7, 0xA6, 0x44, 0xF0,
+ // Bytes 1700 - 173f
+ 0xA2, 0x86, 0x83, 0x44, 0xF0, 0xA2, 0x86, 0x9F,
+ 0x44, 0xF0, 0xA2, 0x8C, 0xB1, 0x44, 0xF0, 0xA2,
+ 0x9B, 0x94, 0x44, 0xF0, 0xA2, 0xA1, 0x84, 0x44,
+ 0xF0, 0xA2, 0xA1, 0x8A, 0x44, 0xF0, 0xA2, 0xAC,
+ 0x8C, 0x44, 0xF0, 0xA2, 0xAF, 0xB1, 0x44, 0xF0,
+ 0xA3, 0x80, 0x8A, 0x44, 0xF0, 0xA3, 0x8A, 0xB8,
+ 0x44, 0xF0, 0xA3, 0x8D, 0x9F, 0x44, 0xF0, 0xA3,
+ 0x8E, 0x93, 0x44, 0xF0, 0xA3, 0x8E, 0x9C, 0x44,
+ // Bytes 1740 - 177f
+ 0xF0, 0xA3, 0x8F, 0x83, 0x44, 0xF0, 0xA3, 0x8F,
+ 0x95, 0x44, 0xF0, 0xA3, 0x91, 0xAD, 0x44, 0xF0,
+ 0xA3, 0x9A, 0xA3, 0x44, 0xF0, 0xA3, 0xA2, 0xA7,
+ 0x44, 0xF0, 0xA3, 0xAA, 0x8D, 0x44, 0xF0, 0xA3,
+ 0xAB, 0xBA, 0x44, 0xF0, 0xA3, 0xB2, 0xBC, 0x44,
+ 0xF0, 0xA3, 0xB4, 0x9E, 0x44, 0xF0, 0xA3, 0xBB,
+ 0x91, 0x44, 0xF0, 0xA3, 0xBD, 0x9E, 0x44, 0xF0,
+ 0xA3, 0xBE, 0x8E, 0x44, 0xF0, 0xA4, 0x89, 0xA3,
+ // Bytes 1780 - 17bf
+ 0x44, 0xF0, 0xA4, 0x8B, 0xAE, 0x44, 0xF0, 0xA4,
+ 0x8E, 0xAB, 0x44, 0xF0, 0xA4, 0x98, 0x88, 0x44,
+ 0xF0, 0xA4, 0x9C, 0xB5, 0x44, 0xF0, 0xA4, 0xA0,
+ 0x94, 0x44, 0xF0, 0xA4, 0xB0, 0xB6, 0x44, 0xF0,
+ 0xA4, 0xB2, 0x92, 0x44, 0xF0, 0xA4, 0xBE, 0xA1,
+ 0x44, 0xF0, 0xA4, 0xBE, 0xB8, 0x44, 0xF0, 0xA5,
+ 0x81, 0x84, 0x44, 0xF0, 0xA5, 0x83, 0xB2, 0x44,
+ 0xF0, 0xA5, 0x83, 0xB3, 0x44, 0xF0, 0xA5, 0x84,
+ // Bytes 17c0 - 17ff
+ 0x99, 0x44, 0xF0, 0xA5, 0x84, 0xB3, 0x44, 0xF0,
+ 0xA5, 0x89, 0x89, 0x44, 0xF0, 0xA5, 0x90, 0x9D,
+ 0x44, 0xF0, 0xA5, 0x98, 0xA6, 0x44, 0xF0, 0xA5,
+ 0x9A, 0x9A, 0x44, 0xF0, 0xA5, 0x9B, 0x85, 0x44,
+ 0xF0, 0xA5, 0xA5, 0xBC, 0x44, 0xF0, 0xA5, 0xAA,
+ 0xA7, 0x44, 0xF0, 0xA5, 0xAE, 0xAB, 0x44, 0xF0,
+ 0xA5, 0xB2, 0x80, 0x44, 0xF0, 0xA5, 0xB3, 0x90,
+ 0x44, 0xF0, 0xA5, 0xBE, 0x86, 0x44, 0xF0, 0xA6,
+ // Bytes 1800 - 183f
+ 0x87, 0x9A, 0x44, 0xF0, 0xA6, 0x88, 0xA8, 0x44,
+ 0xF0, 0xA6, 0x89, 0x87, 0x44, 0xF0, 0xA6, 0x8B,
+ 0x99, 0x44, 0xF0, 0xA6, 0x8C, 0xBE, 0x44, 0xF0,
+ 0xA6, 0x93, 0x9A, 0x44, 0xF0, 0xA6, 0x94, 0xA3,
+ 0x44, 0xF0, 0xA6, 0x96, 0xA8, 0x44, 0xF0, 0xA6,
+ 0x9E, 0xA7, 0x44, 0xF0, 0xA6, 0x9E, 0xB5, 0x44,
+ 0xF0, 0xA6, 0xAC, 0xBC, 0x44, 0xF0, 0xA6, 0xB0,
+ 0xB6, 0x44, 0xF0, 0xA6, 0xB3, 0x95, 0x44, 0xF0,
+ // Bytes 1840 - 187f
+ 0xA6, 0xB5, 0xAB, 0x44, 0xF0, 0xA6, 0xBC, 0xAC,
+ 0x44, 0xF0, 0xA6, 0xBE, 0xB1, 0x44, 0xF0, 0xA7,
+ 0x83, 0x92, 0x44, 0xF0, 0xA7, 0x8F, 0x8A, 0x44,
+ 0xF0, 0xA7, 0x99, 0xA7, 0x44, 0xF0, 0xA7, 0xA2,
+ 0xAE, 0x44, 0xF0, 0xA7, 0xA5, 0xA6, 0x44, 0xF0,
+ 0xA7, 0xB2, 0xA8, 0x44, 0xF0, 0xA7, 0xBB, 0x93,
+ 0x44, 0xF0, 0xA7, 0xBC, 0xAF, 0x44, 0xF0, 0xA8,
+ 0x97, 0x92, 0x44, 0xF0, 0xA8, 0x97, 0xAD, 0x44,
+ // Bytes 1880 - 18bf
+ 0xF0, 0xA8, 0x9C, 0xAE, 0x44, 0xF0, 0xA8, 0xAF,
+ 0xBA, 0x44, 0xF0, 0xA8, 0xB5, 0xB7, 0x44, 0xF0,
+ 0xA9, 0x85, 0x85, 0x44, 0xF0, 0xA9, 0x87, 0x9F,
+ 0x44, 0xF0, 0xA9, 0x88, 0x9A, 0x44, 0xF0, 0xA9,
+ 0x90, 0x8A, 0x44, 0xF0, 0xA9, 0x92, 0x96, 0x44,
+ 0xF0, 0xA9, 0x96, 0xB6, 0x44, 0xF0, 0xA9, 0xAC,
+ 0xB0, 0x44, 0xF0, 0xAA, 0x83, 0x8E, 0x44, 0xF0,
+ 0xAA, 0x84, 0x85, 0x44, 0xF0, 0xAA, 0x88, 0x8E,
+ // Bytes 18c0 - 18ff
+ 0x44, 0xF0, 0xAA, 0x8A, 0x91, 0x44, 0xF0, 0xAA,
+ 0x8E, 0x92, 0x44, 0xF0, 0xAA, 0x98, 0x80, 0x06,
+ 0xE0, 0xA7, 0x87, 0xE0, 0xA6, 0xBE, 0x06, 0xE0,
+ 0xA7, 0x87, 0xE0, 0xA7, 0x97, 0x06, 0xE0, 0xAD,
+ 0x87, 0xE0, 0xAC, 0xBE, 0x06, 0xE0, 0xAD, 0x87,
+ 0xE0, 0xAD, 0x96, 0x06, 0xE0, 0xAD, 0x87, 0xE0,
+ 0xAD, 0x97, 0x06, 0xE0, 0xAE, 0x92, 0xE0, 0xAF,
+ 0x97, 0x06, 0xE0, 0xAF, 0x86, 0xE0, 0xAE, 0xBE,
+ // Bytes 1900 - 193f
+ 0x06, 0xE0, 0xAF, 0x86, 0xE0, 0xAF, 0x97, 0x06,
+ 0xE0, 0xAF, 0x87, 0xE0, 0xAE, 0xBE, 0x06, 0xE0,
+ 0xB2, 0xBF, 0xE0, 0xB3, 0x95, 0x06, 0xE0, 0xB3,
+ 0x86, 0xE0, 0xB3, 0x95, 0x06, 0xE0, 0xB3, 0x86,
+ 0xE0, 0xB3, 0x96, 0x06, 0xE0, 0xB5, 0x86, 0xE0,
+ 0xB4, 0xBE, 0x06, 0xE0, 0xB5, 0x86, 0xE0, 0xB5,
+ 0x97, 0x06, 0xE0, 0xB5, 0x87, 0xE0, 0xB4, 0xBE,
+ 0x06, 0xE0, 0xB7, 0x99, 0xE0, 0xB7, 0x9F, 0x06,
+ // Bytes 1940 - 197f
+ 0xE1, 0x80, 0xA5, 0xE1, 0x80, 0xAE, 0x06, 0xE1,
+ 0xAC, 0x85, 0xE1, 0xAC, 0xB5, 0x06, 0xE1, 0xAC,
+ 0x87, 0xE1, 0xAC, 0xB5, 0x06, 0xE1, 0xAC, 0x89,
+ 0xE1, 0xAC, 0xB5, 0x06, 0xE1, 0xAC, 0x8B, 0xE1,
+ 0xAC, 0xB5, 0x06, 0xE1, 0xAC, 0x8D, 0xE1, 0xAC,
+ 0xB5, 0x06, 0xE1, 0xAC, 0x91, 0xE1, 0xAC, 0xB5,
+ 0x06, 0xE1, 0xAC, 0xBA, 0xE1, 0xAC, 0xB5, 0x06,
+ 0xE1, 0xAC, 0xBC, 0xE1, 0xAC, 0xB5, 0x06, 0xE1,
+ // Bytes 1980 - 19bf
+ 0xAC, 0xBE, 0xE1, 0xAC, 0xB5, 0x06, 0xE1, 0xAC,
+ 0xBF, 0xE1, 0xAC, 0xB5, 0x06, 0xE1, 0xAD, 0x82,
+ 0xE1, 0xAC, 0xB5, 0x08, 0xF0, 0x91, 0x84, 0xB1,
+ 0xF0, 0x91, 0x84, 0xA7, 0x08, 0xF0, 0x91, 0x84,
+ 0xB2, 0xF0, 0x91, 0x84, 0xA7, 0x09, 0xE0, 0xB3,
+ 0x86, 0xE0, 0xB3, 0x82, 0xE0, 0xB3, 0x95, 0x42,
+ 0x21, 0x21, 0x42, 0x21, 0x3F, 0x42, 0x2E, 0x2E,
+ 0x42, 0x30, 0x2C, 0x42, 0x30, 0x2E, 0x42, 0x31,
+ // Bytes 19c0 - 19ff
+ 0x2C, 0x42, 0x31, 0x2E, 0x42, 0x31, 0x30, 0x42,
+ 0x31, 0x31, 0x42, 0x31, 0x32, 0x42, 0x31, 0x33,
+ 0x42, 0x31, 0x34, 0x42, 0x31, 0x35, 0x42, 0x31,
+ 0x36, 0x42, 0x31, 0x37, 0x42, 0x31, 0x38, 0x42,
+ 0x31, 0x39, 0x42, 0x32, 0x2C, 0x42, 0x32, 0x2E,
+ 0x42, 0x32, 0x30, 0x42, 0x32, 0x31, 0x42, 0x32,
+ 0x32, 0x42, 0x32, 0x33, 0x42, 0x32, 0x34, 0x42,
+ 0x32, 0x35, 0x42, 0x32, 0x36, 0x42, 0x32, 0x37,
+ // Bytes 1a00 - 1a3f
+ 0x42, 0x32, 0x38, 0x42, 0x32, 0x39, 0x42, 0x33,
+ 0x2C, 0x42, 0x33, 0x2E, 0x42, 0x33, 0x30, 0x42,
+ 0x33, 0x31, 0x42, 0x33, 0x32, 0x42, 0x33, 0x33,
+ 0x42, 0x33, 0x34, 0x42, 0x33, 0x35, 0x42, 0x33,
+ 0x36, 0x42, 0x33, 0x37, 0x42, 0x33, 0x38, 0x42,
+ 0x33, 0x39, 0x42, 0x34, 0x2C, 0x42, 0x34, 0x2E,
+ 0x42, 0x34, 0x30, 0x42, 0x34, 0x31, 0x42, 0x34,
+ 0x32, 0x42, 0x34, 0x33, 0x42, 0x34, 0x34, 0x42,
+ // Bytes 1a40 - 1a7f
+ 0x34, 0x35, 0x42, 0x34, 0x36, 0x42, 0x34, 0x37,
+ 0x42, 0x34, 0x38, 0x42, 0x34, 0x39, 0x42, 0x35,
+ 0x2C, 0x42, 0x35, 0x2E, 0x42, 0x35, 0x30, 0x42,
+ 0x36, 0x2C, 0x42, 0x36, 0x2E, 0x42, 0x37, 0x2C,
+ 0x42, 0x37, 0x2E, 0x42, 0x38, 0x2C, 0x42, 0x38,
+ 0x2E, 0x42, 0x39, 0x2C, 0x42, 0x39, 0x2E, 0x42,
+ 0x3D, 0x3D, 0x42, 0x3F, 0x21, 0x42, 0x3F, 0x3F,
+ 0x42, 0x41, 0x55, 0x42, 0x42, 0x71, 0x42, 0x43,
+ // Bytes 1a80 - 1abf
+ 0x44, 0x42, 0x44, 0x4A, 0x42, 0x44, 0x5A, 0x42,
+ 0x44, 0x7A, 0x42, 0x47, 0x42, 0x42, 0x47, 0x79,
+ 0x42, 0x48, 0x50, 0x42, 0x48, 0x56, 0x42, 0x48,
+ 0x67, 0x42, 0x48, 0x7A, 0x42, 0x49, 0x49, 0x42,
+ 0x49, 0x4A, 0x42, 0x49, 0x55, 0x42, 0x49, 0x56,
+ 0x42, 0x49, 0x58, 0x42, 0x4B, 0x42, 0x42, 0x4B,
+ 0x4B, 0x42, 0x4B, 0x4D, 0x42, 0x4C, 0x4A, 0x42,
+ 0x4C, 0x6A, 0x42, 0x4D, 0x42, 0x42, 0x4D, 0x43,
+ // Bytes 1ac0 - 1aff
+ 0x42, 0x4D, 0x44, 0x42, 0x4D, 0x56, 0x42, 0x4D,
+ 0x57, 0x42, 0x4E, 0x4A, 0x42, 0x4E, 0x6A, 0x42,
+ 0x4E, 0x6F, 0x42, 0x50, 0x48, 0x42, 0x50, 0x52,
+ 0x42, 0x50, 0x61, 0x42, 0x52, 0x73, 0x42, 0x53,
+ 0x44, 0x42, 0x53, 0x4D, 0x42, 0x53, 0x53, 0x42,
+ 0x53, 0x76, 0x42, 0x54, 0x4D, 0x42, 0x56, 0x49,
+ 0x42, 0x57, 0x43, 0x42, 0x57, 0x5A, 0x42, 0x57,
+ 0x62, 0x42, 0x58, 0x49, 0x42, 0x63, 0x63, 0x42,
+ // Bytes 1b00 - 1b3f
+ 0x63, 0x64, 0x42, 0x63, 0x6D, 0x42, 0x64, 0x42,
+ 0x42, 0x64, 0x61, 0x42, 0x64, 0x6C, 0x42, 0x64,
+ 0x6D, 0x42, 0x64, 0x7A, 0x42, 0x65, 0x56, 0x42,
+ 0x66, 0x66, 0x42, 0x66, 0x69, 0x42, 0x66, 0x6C,
+ 0x42, 0x66, 0x6D, 0x42, 0x68, 0x61, 0x42, 0x69,
+ 0x69, 0x42, 0x69, 0x6A, 0x42, 0x69, 0x6E, 0x42,
+ 0x69, 0x76, 0x42, 0x69, 0x78, 0x42, 0x6B, 0x41,
+ 0x42, 0x6B, 0x56, 0x42, 0x6B, 0x57, 0x42, 0x6B,
+ // Bytes 1b40 - 1b7f
+ 0x67, 0x42, 0x6B, 0x6C, 0x42, 0x6B, 0x6D, 0x42,
+ 0x6B, 0x74, 0x42, 0x6C, 0x6A, 0x42, 0x6C, 0x6D,
+ 0x42, 0x6C, 0x6E, 0x42, 0x6C, 0x78, 0x42, 0x6D,
+ 0x32, 0x42, 0x6D, 0x33, 0x42, 0x6D, 0x41, 0x42,
+ 0x6D, 0x56, 0x42, 0x6D, 0x57, 0x42, 0x6D, 0x62,
+ 0x42, 0x6D, 0x67, 0x42, 0x6D, 0x6C, 0x42, 0x6D,
+ 0x6D, 0x42, 0x6D, 0x73, 0x42, 0x6E, 0x41, 0x42,
+ 0x6E, 0x46, 0x42, 0x6E, 0x56, 0x42, 0x6E, 0x57,
+ // Bytes 1b80 - 1bbf
+ 0x42, 0x6E, 0x6A, 0x42, 0x6E, 0x6D, 0x42, 0x6E,
+ 0x73, 0x42, 0x6F, 0x56, 0x42, 0x70, 0x41, 0x42,
+ 0x70, 0x46, 0x42, 0x70, 0x56, 0x42, 0x70, 0x57,
+ 0x42, 0x70, 0x63, 0x42, 0x70, 0x73, 0x42, 0x73,
+ 0x72, 0x42, 0x73, 0x74, 0x42, 0x76, 0x69, 0x42,
+ 0x78, 0x69, 0x43, 0x28, 0x31, 0x29, 0x43, 0x28,
+ 0x32, 0x29, 0x43, 0x28, 0x33, 0x29, 0x43, 0x28,
+ 0x34, 0x29, 0x43, 0x28, 0x35, 0x29, 0x43, 0x28,
+ // Bytes 1bc0 - 1bff
+ 0x36, 0x29, 0x43, 0x28, 0x37, 0x29, 0x43, 0x28,
+ 0x38, 0x29, 0x43, 0x28, 0x39, 0x29, 0x43, 0x28,
+ 0x41, 0x29, 0x43, 0x28, 0x42, 0x29, 0x43, 0x28,
+ 0x43, 0x29, 0x43, 0x28, 0x44, 0x29, 0x43, 0x28,
+ 0x45, 0x29, 0x43, 0x28, 0x46, 0x29, 0x43, 0x28,
+ 0x47, 0x29, 0x43, 0x28, 0x48, 0x29, 0x43, 0x28,
+ 0x49, 0x29, 0x43, 0x28, 0x4A, 0x29, 0x43, 0x28,
+ 0x4B, 0x29, 0x43, 0x28, 0x4C, 0x29, 0x43, 0x28,
+ // Bytes 1c00 - 1c3f
+ 0x4D, 0x29, 0x43, 0x28, 0x4E, 0x29, 0x43, 0x28,
+ 0x4F, 0x29, 0x43, 0x28, 0x50, 0x29, 0x43, 0x28,
+ 0x51, 0x29, 0x43, 0x28, 0x52, 0x29, 0x43, 0x28,
+ 0x53, 0x29, 0x43, 0x28, 0x54, 0x29, 0x43, 0x28,
+ 0x55, 0x29, 0x43, 0x28, 0x56, 0x29, 0x43, 0x28,
+ 0x57, 0x29, 0x43, 0x28, 0x58, 0x29, 0x43, 0x28,
+ 0x59, 0x29, 0x43, 0x28, 0x5A, 0x29, 0x43, 0x28,
+ 0x61, 0x29, 0x43, 0x28, 0x62, 0x29, 0x43, 0x28,
+ // Bytes 1c40 - 1c7f
+ 0x63, 0x29, 0x43, 0x28, 0x64, 0x29, 0x43, 0x28,
+ 0x65, 0x29, 0x43, 0x28, 0x66, 0x29, 0x43, 0x28,
+ 0x67, 0x29, 0x43, 0x28, 0x68, 0x29, 0x43, 0x28,
+ 0x69, 0x29, 0x43, 0x28, 0x6A, 0x29, 0x43, 0x28,
+ 0x6B, 0x29, 0x43, 0x28, 0x6C, 0x29, 0x43, 0x28,
+ 0x6D, 0x29, 0x43, 0x28, 0x6E, 0x29, 0x43, 0x28,
+ 0x6F, 0x29, 0x43, 0x28, 0x70, 0x29, 0x43, 0x28,
+ 0x71, 0x29, 0x43, 0x28, 0x72, 0x29, 0x43, 0x28,
+ // Bytes 1c80 - 1cbf
+ 0x73, 0x29, 0x43, 0x28, 0x74, 0x29, 0x43, 0x28,
+ 0x75, 0x29, 0x43, 0x28, 0x76, 0x29, 0x43, 0x28,
+ 0x77, 0x29, 0x43, 0x28, 0x78, 0x29, 0x43, 0x28,
+ 0x79, 0x29, 0x43, 0x28, 0x7A, 0x29, 0x43, 0x2E,
+ 0x2E, 0x2E, 0x43, 0x31, 0x30, 0x2E, 0x43, 0x31,
+ 0x31, 0x2E, 0x43, 0x31, 0x32, 0x2E, 0x43, 0x31,
+ 0x33, 0x2E, 0x43, 0x31, 0x34, 0x2E, 0x43, 0x31,
+ 0x35, 0x2E, 0x43, 0x31, 0x36, 0x2E, 0x43, 0x31,
+ // Bytes 1cc0 - 1cff
+ 0x37, 0x2E, 0x43, 0x31, 0x38, 0x2E, 0x43, 0x31,
+ 0x39, 0x2E, 0x43, 0x32, 0x30, 0x2E, 0x43, 0x3A,
+ 0x3A, 0x3D, 0x43, 0x3D, 0x3D, 0x3D, 0x43, 0x43,
+ 0x6F, 0x2E, 0x43, 0x46, 0x41, 0x58, 0x43, 0x47,
+ 0x48, 0x7A, 0x43, 0x47, 0x50, 0x61, 0x43, 0x49,
+ 0x49, 0x49, 0x43, 0x4C, 0x54, 0x44, 0x43, 0x4C,
+ 0xC2, 0xB7, 0x43, 0x4D, 0x48, 0x7A, 0x43, 0x4D,
+ 0x50, 0x61, 0x43, 0x4D, 0xCE, 0xA9, 0x43, 0x50,
+ // Bytes 1d00 - 1d3f
+ 0x50, 0x4D, 0x43, 0x50, 0x50, 0x56, 0x43, 0x50,
+ 0x54, 0x45, 0x43, 0x54, 0x45, 0x4C, 0x43, 0x54,
+ 0x48, 0x7A, 0x43, 0x56, 0x49, 0x49, 0x43, 0x58,
+ 0x49, 0x49, 0x43, 0x61, 0x2F, 0x63, 0x43, 0x61,
+ 0x2F, 0x73, 0x43, 0x61, 0xCA, 0xBE, 0x43, 0x62,
+ 0x61, 0x72, 0x43, 0x63, 0x2F, 0x6F, 0x43, 0x63,
+ 0x2F, 0x75, 0x43, 0x63, 0x61, 0x6C, 0x43, 0x63,
+ 0x6D, 0x32, 0x43, 0x63, 0x6D, 0x33, 0x43, 0x64,
+ // Bytes 1d40 - 1d7f
+ 0x6D, 0x32, 0x43, 0x64, 0x6D, 0x33, 0x43, 0x65,
+ 0x72, 0x67, 0x43, 0x66, 0x66, 0x69, 0x43, 0x66,
+ 0x66, 0x6C, 0x43, 0x67, 0x61, 0x6C, 0x43, 0x68,
+ 0x50, 0x61, 0x43, 0x69, 0x69, 0x69, 0x43, 0x6B,
+ 0x48, 0x7A, 0x43, 0x6B, 0x50, 0x61, 0x43, 0x6B,
+ 0x6D, 0x32, 0x43, 0x6B, 0x6D, 0x33, 0x43, 0x6B,
+ 0xCE, 0xA9, 0x43, 0x6C, 0x6F, 0x67, 0x43, 0x6C,
+ 0xC2, 0xB7, 0x43, 0x6D, 0x69, 0x6C, 0x43, 0x6D,
+ // Bytes 1d80 - 1dbf
+ 0x6D, 0x32, 0x43, 0x6D, 0x6D, 0x33, 0x43, 0x6D,
+ 0x6F, 0x6C, 0x43, 0x72, 0x61, 0x64, 0x43, 0x76,
+ 0x69, 0x69, 0x43, 0x78, 0x69, 0x69, 0x43, 0xC2,
+ 0xB0, 0x43, 0x43, 0xC2, 0xB0, 0x46, 0x43, 0xCA,
+ 0xBC, 0x6E, 0x43, 0xCE, 0xBC, 0x41, 0x43, 0xCE,
+ 0xBC, 0x46, 0x43, 0xCE, 0xBC, 0x56, 0x43, 0xCE,
+ 0xBC, 0x57, 0x43, 0xCE, 0xBC, 0x67, 0x43, 0xCE,
+ 0xBC, 0x6C, 0x43, 0xCE, 0xBC, 0x6D, 0x43, 0xCE,
+ // Bytes 1dc0 - 1dff
+ 0xBC, 0x73, 0x44, 0x28, 0x31, 0x30, 0x29, 0x44,
+ 0x28, 0x31, 0x31, 0x29, 0x44, 0x28, 0x31, 0x32,
+ 0x29, 0x44, 0x28, 0x31, 0x33, 0x29, 0x44, 0x28,
+ 0x31, 0x34, 0x29, 0x44, 0x28, 0x31, 0x35, 0x29,
+ 0x44, 0x28, 0x31, 0x36, 0x29, 0x44, 0x28, 0x31,
+ 0x37, 0x29, 0x44, 0x28, 0x31, 0x38, 0x29, 0x44,
+ 0x28, 0x31, 0x39, 0x29, 0x44, 0x28, 0x32, 0x30,
+ 0x29, 0x44, 0x30, 0xE7, 0x82, 0xB9, 0x44, 0x31,
+ // Bytes 1e00 - 1e3f
+ 0xE2, 0x81, 0x84, 0x44, 0x31, 0xE6, 0x97, 0xA5,
+ 0x44, 0x31, 0xE6, 0x9C, 0x88, 0x44, 0x31, 0xE7,
+ 0x82, 0xB9, 0x44, 0x32, 0xE6, 0x97, 0xA5, 0x44,
+ 0x32, 0xE6, 0x9C, 0x88, 0x44, 0x32, 0xE7, 0x82,
+ 0xB9, 0x44, 0x33, 0xE6, 0x97, 0xA5, 0x44, 0x33,
+ 0xE6, 0x9C, 0x88, 0x44, 0x33, 0xE7, 0x82, 0xB9,
+ 0x44, 0x34, 0xE6, 0x97, 0xA5, 0x44, 0x34, 0xE6,
+ 0x9C, 0x88, 0x44, 0x34, 0xE7, 0x82, 0xB9, 0x44,
+ // Bytes 1e40 - 1e7f
+ 0x35, 0xE6, 0x97, 0xA5, 0x44, 0x35, 0xE6, 0x9C,
+ 0x88, 0x44, 0x35, 0xE7, 0x82, 0xB9, 0x44, 0x36,
+ 0xE6, 0x97, 0xA5, 0x44, 0x36, 0xE6, 0x9C, 0x88,
+ 0x44, 0x36, 0xE7, 0x82, 0xB9, 0x44, 0x37, 0xE6,
+ 0x97, 0xA5, 0x44, 0x37, 0xE6, 0x9C, 0x88, 0x44,
+ 0x37, 0xE7, 0x82, 0xB9, 0x44, 0x38, 0xE6, 0x97,
+ 0xA5, 0x44, 0x38, 0xE6, 0x9C, 0x88, 0x44, 0x38,
+ 0xE7, 0x82, 0xB9, 0x44, 0x39, 0xE6, 0x97, 0xA5,
+ // Bytes 1e80 - 1ebf
+ 0x44, 0x39, 0xE6, 0x9C, 0x88, 0x44, 0x39, 0xE7,
+ 0x82, 0xB9, 0x44, 0x56, 0x49, 0x49, 0x49, 0x44,
+ 0x61, 0x2E, 0x6D, 0x2E, 0x44, 0x6B, 0x63, 0x61,
+ 0x6C, 0x44, 0x70, 0x2E, 0x6D, 0x2E, 0x44, 0x76,
+ 0x69, 0x69, 0x69, 0x44, 0xD5, 0xA5, 0xD6, 0x82,
+ 0x44, 0xD5, 0xB4, 0xD5, 0xA5, 0x44, 0xD5, 0xB4,
+ 0xD5, 0xAB, 0x44, 0xD5, 0xB4, 0xD5, 0xAD, 0x44,
+ 0xD5, 0xB4, 0xD5, 0xB6, 0x44, 0xD5, 0xBE, 0xD5,
+ // Bytes 1ec0 - 1eff
+ 0xB6, 0x44, 0xD7, 0x90, 0xD7, 0x9C, 0x44, 0xD8,
+ 0xA7, 0xD9, 0xB4, 0x44, 0xD8, 0xA8, 0xD8, 0xAC,
+ 0x44, 0xD8, 0xA8, 0xD8, 0xAD, 0x44, 0xD8, 0xA8,
+ 0xD8, 0xAE, 0x44, 0xD8, 0xA8, 0xD8, 0xB1, 0x44,
+ 0xD8, 0xA8, 0xD8, 0xB2, 0x44, 0xD8, 0xA8, 0xD9,
+ 0x85, 0x44, 0xD8, 0xA8, 0xD9, 0x86, 0x44, 0xD8,
+ 0xA8, 0xD9, 0x87, 0x44, 0xD8, 0xA8, 0xD9, 0x89,
+ 0x44, 0xD8, 0xA8, 0xD9, 0x8A, 0x44, 0xD8, 0xAA,
+ // Bytes 1f00 - 1f3f
+ 0xD8, 0xAC, 0x44, 0xD8, 0xAA, 0xD8, 0xAD, 0x44,
+ 0xD8, 0xAA, 0xD8, 0xAE, 0x44, 0xD8, 0xAA, 0xD8,
+ 0xB1, 0x44, 0xD8, 0xAA, 0xD8, 0xB2, 0x44, 0xD8,
+ 0xAA, 0xD9, 0x85, 0x44, 0xD8, 0xAA, 0xD9, 0x86,
+ 0x44, 0xD8, 0xAA, 0xD9, 0x87, 0x44, 0xD8, 0xAA,
+ 0xD9, 0x89, 0x44, 0xD8, 0xAA, 0xD9, 0x8A, 0x44,
+ 0xD8, 0xAB, 0xD8, 0xAC, 0x44, 0xD8, 0xAB, 0xD8,
+ 0xB1, 0x44, 0xD8, 0xAB, 0xD8, 0xB2, 0x44, 0xD8,
+ // Bytes 1f40 - 1f7f
+ 0xAB, 0xD9, 0x85, 0x44, 0xD8, 0xAB, 0xD9, 0x86,
+ 0x44, 0xD8, 0xAB, 0xD9, 0x87, 0x44, 0xD8, 0xAB,
+ 0xD9, 0x89, 0x44, 0xD8, 0xAB, 0xD9, 0x8A, 0x44,
+ 0xD8, 0xAC, 0xD8, 0xAD, 0x44, 0xD8, 0xAC, 0xD9,
+ 0x85, 0x44, 0xD8, 0xAC, 0xD9, 0x89, 0x44, 0xD8,
+ 0xAC, 0xD9, 0x8A, 0x44, 0xD8, 0xAD, 0xD8, 0xAC,
+ 0x44, 0xD8, 0xAD, 0xD9, 0x85, 0x44, 0xD8, 0xAD,
+ 0xD9, 0x89, 0x44, 0xD8, 0xAD, 0xD9, 0x8A, 0x44,
+ // Bytes 1f80 - 1fbf
+ 0xD8, 0xAE, 0xD8, 0xAC, 0x44, 0xD8, 0xAE, 0xD8,
+ 0xAD, 0x44, 0xD8, 0xAE, 0xD9, 0x85, 0x44, 0xD8,
+ 0xAE, 0xD9, 0x89, 0x44, 0xD8, 0xAE, 0xD9, 0x8A,
+ 0x44, 0xD8, 0xB3, 0xD8, 0xAC, 0x44, 0xD8, 0xB3,
+ 0xD8, 0xAD, 0x44, 0xD8, 0xB3, 0xD8, 0xAE, 0x44,
+ 0xD8, 0xB3, 0xD8, 0xB1, 0x44, 0xD8, 0xB3, 0xD9,
+ 0x85, 0x44, 0xD8, 0xB3, 0xD9, 0x87, 0x44, 0xD8,
+ 0xB3, 0xD9, 0x89, 0x44, 0xD8, 0xB3, 0xD9, 0x8A,
+ // Bytes 1fc0 - 1fff
+ 0x44, 0xD8, 0xB4, 0xD8, 0xAC, 0x44, 0xD8, 0xB4,
+ 0xD8, 0xAD, 0x44, 0xD8, 0xB4, 0xD8, 0xAE, 0x44,
+ 0xD8, 0xB4, 0xD8, 0xB1, 0x44, 0xD8, 0xB4, 0xD9,
+ 0x85, 0x44, 0xD8, 0xB4, 0xD9, 0x87, 0x44, 0xD8,
+ 0xB4, 0xD9, 0x89, 0x44, 0xD8, 0xB4, 0xD9, 0x8A,
+ 0x44, 0xD8, 0xB5, 0xD8, 0xAD, 0x44, 0xD8, 0xB5,
+ 0xD8, 0xAE, 0x44, 0xD8, 0xB5, 0xD8, 0xB1, 0x44,
+ 0xD8, 0xB5, 0xD9, 0x85, 0x44, 0xD8, 0xB5, 0xD9,
+ // Bytes 2000 - 203f
+ 0x89, 0x44, 0xD8, 0xB5, 0xD9, 0x8A, 0x44, 0xD8,
+ 0xB6, 0xD8, 0xAC, 0x44, 0xD8, 0xB6, 0xD8, 0xAD,
+ 0x44, 0xD8, 0xB6, 0xD8, 0xAE, 0x44, 0xD8, 0xB6,
+ 0xD8, 0xB1, 0x44, 0xD8, 0xB6, 0xD9, 0x85, 0x44,
+ 0xD8, 0xB6, 0xD9, 0x89, 0x44, 0xD8, 0xB6, 0xD9,
+ 0x8A, 0x44, 0xD8, 0xB7, 0xD8, 0xAD, 0x44, 0xD8,
+ 0xB7, 0xD9, 0x85, 0x44, 0xD8, 0xB7, 0xD9, 0x89,
+ 0x44, 0xD8, 0xB7, 0xD9, 0x8A, 0x44, 0xD8, 0xB8,
+ // Bytes 2040 - 207f
+ 0xD9, 0x85, 0x44, 0xD8, 0xB9, 0xD8, 0xAC, 0x44,
+ 0xD8, 0xB9, 0xD9, 0x85, 0x44, 0xD8, 0xB9, 0xD9,
+ 0x89, 0x44, 0xD8, 0xB9, 0xD9, 0x8A, 0x44, 0xD8,
+ 0xBA, 0xD8, 0xAC, 0x44, 0xD8, 0xBA, 0xD9, 0x85,
+ 0x44, 0xD8, 0xBA, 0xD9, 0x89, 0x44, 0xD8, 0xBA,
+ 0xD9, 0x8A, 0x44, 0xD9, 0x81, 0xD8, 0xAC, 0x44,
+ 0xD9, 0x81, 0xD8, 0xAD, 0x44, 0xD9, 0x81, 0xD8,
+ 0xAE, 0x44, 0xD9, 0x81, 0xD9, 0x85, 0x44, 0xD9,
+ // Bytes 2080 - 20bf
+ 0x81, 0xD9, 0x89, 0x44, 0xD9, 0x81, 0xD9, 0x8A,
+ 0x44, 0xD9, 0x82, 0xD8, 0xAD, 0x44, 0xD9, 0x82,
+ 0xD9, 0x85, 0x44, 0xD9, 0x82, 0xD9, 0x89, 0x44,
+ 0xD9, 0x82, 0xD9, 0x8A, 0x44, 0xD9, 0x83, 0xD8,
+ 0xA7, 0x44, 0xD9, 0x83, 0xD8, 0xAC, 0x44, 0xD9,
+ 0x83, 0xD8, 0xAD, 0x44, 0xD9, 0x83, 0xD8, 0xAE,
+ 0x44, 0xD9, 0x83, 0xD9, 0x84, 0x44, 0xD9, 0x83,
+ 0xD9, 0x85, 0x44, 0xD9, 0x83, 0xD9, 0x89, 0x44,
+ // Bytes 20c0 - 20ff
+ 0xD9, 0x83, 0xD9, 0x8A, 0x44, 0xD9, 0x84, 0xD8,
+ 0xA7, 0x44, 0xD9, 0x84, 0xD8, 0xAC, 0x44, 0xD9,
+ 0x84, 0xD8, 0xAD, 0x44, 0xD9, 0x84, 0xD8, 0xAE,
+ 0x44, 0xD9, 0x84, 0xD9, 0x85, 0x44, 0xD9, 0x84,
+ 0xD9, 0x87, 0x44, 0xD9, 0x84, 0xD9, 0x89, 0x44,
+ 0xD9, 0x84, 0xD9, 0x8A, 0x44, 0xD9, 0x85, 0xD8,
+ 0xA7, 0x44, 0xD9, 0x85, 0xD8, 0xAC, 0x44, 0xD9,
+ 0x85, 0xD8, 0xAD, 0x44, 0xD9, 0x85, 0xD8, 0xAE,
+ // Bytes 2100 - 213f
+ 0x44, 0xD9, 0x85, 0xD9, 0x85, 0x44, 0xD9, 0x85,
+ 0xD9, 0x89, 0x44, 0xD9, 0x85, 0xD9, 0x8A, 0x44,
+ 0xD9, 0x86, 0xD8, 0xAC, 0x44, 0xD9, 0x86, 0xD8,
+ 0xAD, 0x44, 0xD9, 0x86, 0xD8, 0xAE, 0x44, 0xD9,
+ 0x86, 0xD8, 0xB1, 0x44, 0xD9, 0x86, 0xD8, 0xB2,
+ 0x44, 0xD9, 0x86, 0xD9, 0x85, 0x44, 0xD9, 0x86,
+ 0xD9, 0x86, 0x44, 0xD9, 0x86, 0xD9, 0x87, 0x44,
+ 0xD9, 0x86, 0xD9, 0x89, 0x44, 0xD9, 0x86, 0xD9,
+ // Bytes 2140 - 217f
+ 0x8A, 0x44, 0xD9, 0x87, 0xD8, 0xAC, 0x44, 0xD9,
+ 0x87, 0xD9, 0x85, 0x44, 0xD9, 0x87, 0xD9, 0x89,
+ 0x44, 0xD9, 0x87, 0xD9, 0x8A, 0x44, 0xD9, 0x88,
+ 0xD9, 0xB4, 0x44, 0xD9, 0x8A, 0xD8, 0xAC, 0x44,
+ 0xD9, 0x8A, 0xD8, 0xAD, 0x44, 0xD9, 0x8A, 0xD8,
+ 0xAE, 0x44, 0xD9, 0x8A, 0xD8, 0xB1, 0x44, 0xD9,
+ 0x8A, 0xD8, 0xB2, 0x44, 0xD9, 0x8A, 0xD9, 0x85,
+ 0x44, 0xD9, 0x8A, 0xD9, 0x86, 0x44, 0xD9, 0x8A,
+ // Bytes 2180 - 21bf
+ 0xD9, 0x87, 0x44, 0xD9, 0x8A, 0xD9, 0x89, 0x44,
+ 0xD9, 0x8A, 0xD9, 0x8A, 0x44, 0xD9, 0x8A, 0xD9,
+ 0xB4, 0x44, 0xDB, 0x87, 0xD9, 0xB4, 0x45, 0x28,
+ 0xE1, 0x84, 0x80, 0x29, 0x45, 0x28, 0xE1, 0x84,
+ 0x82, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x83, 0x29,
+ 0x45, 0x28, 0xE1, 0x84, 0x85, 0x29, 0x45, 0x28,
+ 0xE1, 0x84, 0x86, 0x29, 0x45, 0x28, 0xE1, 0x84,
+ 0x87, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x89, 0x29,
+ // Bytes 21c0 - 21ff
+ 0x45, 0x28, 0xE1, 0x84, 0x8B, 0x29, 0x45, 0x28,
+ 0xE1, 0x84, 0x8C, 0x29, 0x45, 0x28, 0xE1, 0x84,
+ 0x8E, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x8F, 0x29,
+ 0x45, 0x28, 0xE1, 0x84, 0x90, 0x29, 0x45, 0x28,
+ 0xE1, 0x84, 0x91, 0x29, 0x45, 0x28, 0xE1, 0x84,
+ 0x92, 0x29, 0x45, 0x28, 0xE4, 0xB8, 0x80, 0x29,
+ 0x45, 0x28, 0xE4, 0xB8, 0x83, 0x29, 0x45, 0x28,
+ 0xE4, 0xB8, 0x89, 0x29, 0x45, 0x28, 0xE4, 0xB9,
+ // Bytes 2200 - 223f
+ 0x9D, 0x29, 0x45, 0x28, 0xE4, 0xBA, 0x8C, 0x29,
+ 0x45, 0x28, 0xE4, 0xBA, 0x94, 0x29, 0x45, 0x28,
+ 0xE4, 0xBB, 0xA3, 0x29, 0x45, 0x28, 0xE4, 0xBC,
+ 0x81, 0x29, 0x45, 0x28, 0xE4, 0xBC, 0x91, 0x29,
+ 0x45, 0x28, 0xE5, 0x85, 0xAB, 0x29, 0x45, 0x28,
+ 0xE5, 0x85, 0xAD, 0x29, 0x45, 0x28, 0xE5, 0x8A,
+ 0xB4, 0x29, 0x45, 0x28, 0xE5, 0x8D, 0x81, 0x29,
+ 0x45, 0x28, 0xE5, 0x8D, 0x94, 0x29, 0x45, 0x28,
+ // Bytes 2240 - 227f
+ 0xE5, 0x90, 0x8D, 0x29, 0x45, 0x28, 0xE5, 0x91,
+ 0xBC, 0x29, 0x45, 0x28, 0xE5, 0x9B, 0x9B, 0x29,
+ 0x45, 0x28, 0xE5, 0x9C, 0x9F, 0x29, 0x45, 0x28,
+ 0xE5, 0xAD, 0xA6, 0x29, 0x45, 0x28, 0xE6, 0x97,
+ 0xA5, 0x29, 0x45, 0x28, 0xE6, 0x9C, 0x88, 0x29,
+ 0x45, 0x28, 0xE6, 0x9C, 0x89, 0x29, 0x45, 0x28,
+ 0xE6, 0x9C, 0xA8, 0x29, 0x45, 0x28, 0xE6, 0xA0,
+ 0xAA, 0x29, 0x45, 0x28, 0xE6, 0xB0, 0xB4, 0x29,
+ // Bytes 2280 - 22bf
+ 0x45, 0x28, 0xE7, 0x81, 0xAB, 0x29, 0x45, 0x28,
+ 0xE7, 0x89, 0xB9, 0x29, 0x45, 0x28, 0xE7, 0x9B,
+ 0xA3, 0x29, 0x45, 0x28, 0xE7, 0xA4, 0xBE, 0x29,
+ 0x45, 0x28, 0xE7, 0xA5, 0x9D, 0x29, 0x45, 0x28,
+ 0xE7, 0xA5, 0xAD, 0x29, 0x45, 0x28, 0xE8, 0x87,
+ 0xAA, 0x29, 0x45, 0x28, 0xE8, 0x87, 0xB3, 0x29,
+ 0x45, 0x28, 0xE8, 0xB2, 0xA1, 0x29, 0x45, 0x28,
+ 0xE8, 0xB3, 0x87, 0x29, 0x45, 0x28, 0xE9, 0x87,
+ // Bytes 22c0 - 22ff
+ 0x91, 0x29, 0x45, 0x30, 0xE2, 0x81, 0x84, 0x33,
+ 0x45, 0x31, 0x30, 0xE6, 0x97, 0xA5, 0x45, 0x31,
+ 0x30, 0xE6, 0x9C, 0x88, 0x45, 0x31, 0x30, 0xE7,
+ 0x82, 0xB9, 0x45, 0x31, 0x31, 0xE6, 0x97, 0xA5,
+ 0x45, 0x31, 0x31, 0xE6, 0x9C, 0x88, 0x45, 0x31,
+ 0x31, 0xE7, 0x82, 0xB9, 0x45, 0x31, 0x32, 0xE6,
+ 0x97, 0xA5, 0x45, 0x31, 0x32, 0xE6, 0x9C, 0x88,
+ 0x45, 0x31, 0x32, 0xE7, 0x82, 0xB9, 0x45, 0x31,
+ // Bytes 2300 - 233f
+ 0x33, 0xE6, 0x97, 0xA5, 0x45, 0x31, 0x33, 0xE7,
+ 0x82, 0xB9, 0x45, 0x31, 0x34, 0xE6, 0x97, 0xA5,
+ 0x45, 0x31, 0x34, 0xE7, 0x82, 0xB9, 0x45, 0x31,
+ 0x35, 0xE6, 0x97, 0xA5, 0x45, 0x31, 0x35, 0xE7,
+ 0x82, 0xB9, 0x45, 0x31, 0x36, 0xE6, 0x97, 0xA5,
+ 0x45, 0x31, 0x36, 0xE7, 0x82, 0xB9, 0x45, 0x31,
+ 0x37, 0xE6, 0x97, 0xA5, 0x45, 0x31, 0x37, 0xE7,
+ 0x82, 0xB9, 0x45, 0x31, 0x38, 0xE6, 0x97, 0xA5,
+ // Bytes 2340 - 237f
+ 0x45, 0x31, 0x38, 0xE7, 0x82, 0xB9, 0x45, 0x31,
+ 0x39, 0xE6, 0x97, 0xA5, 0x45, 0x31, 0x39, 0xE7,
+ 0x82, 0xB9, 0x45, 0x31, 0xE2, 0x81, 0x84, 0x32,
+ 0x45, 0x31, 0xE2, 0x81, 0x84, 0x33, 0x45, 0x31,
+ 0xE2, 0x81, 0x84, 0x34, 0x45, 0x31, 0xE2, 0x81,
+ 0x84, 0x35, 0x45, 0x31, 0xE2, 0x81, 0x84, 0x36,
+ 0x45, 0x31, 0xE2, 0x81, 0x84, 0x37, 0x45, 0x31,
+ 0xE2, 0x81, 0x84, 0x38, 0x45, 0x31, 0xE2, 0x81,
+ // Bytes 2380 - 23bf
+ 0x84, 0x39, 0x45, 0x32, 0x30, 0xE6, 0x97, 0xA5,
+ 0x45, 0x32, 0x30, 0xE7, 0x82, 0xB9, 0x45, 0x32,
+ 0x31, 0xE6, 0x97, 0xA5, 0x45, 0x32, 0x31, 0xE7,
+ 0x82, 0xB9, 0x45, 0x32, 0x32, 0xE6, 0x97, 0xA5,
+ 0x45, 0x32, 0x32, 0xE7, 0x82, 0xB9, 0x45, 0x32,
+ 0x33, 0xE6, 0x97, 0xA5, 0x45, 0x32, 0x33, 0xE7,
+ 0x82, 0xB9, 0x45, 0x32, 0x34, 0xE6, 0x97, 0xA5,
+ 0x45, 0x32, 0x34, 0xE7, 0x82, 0xB9, 0x45, 0x32,
+ // Bytes 23c0 - 23ff
+ 0x35, 0xE6, 0x97, 0xA5, 0x45, 0x32, 0x36, 0xE6,
+ 0x97, 0xA5, 0x45, 0x32, 0x37, 0xE6, 0x97, 0xA5,
+ 0x45, 0x32, 0x38, 0xE6, 0x97, 0xA5, 0x45, 0x32,
+ 0x39, 0xE6, 0x97, 0xA5, 0x45, 0x32, 0xE2, 0x81,
+ 0x84, 0x33, 0x45, 0x32, 0xE2, 0x81, 0x84, 0x35,
+ 0x45, 0x33, 0x30, 0xE6, 0x97, 0xA5, 0x45, 0x33,
+ 0x31, 0xE6, 0x97, 0xA5, 0x45, 0x33, 0xE2, 0x81,
+ 0x84, 0x34, 0x45, 0x33, 0xE2, 0x81, 0x84, 0x35,
+ // Bytes 2400 - 243f
+ 0x45, 0x33, 0xE2, 0x81, 0x84, 0x38, 0x45, 0x34,
+ 0xE2, 0x81, 0x84, 0x35, 0x45, 0x35, 0xE2, 0x81,
+ 0x84, 0x36, 0x45, 0x35, 0xE2, 0x81, 0x84, 0x38,
+ 0x45, 0x37, 0xE2, 0x81, 0x84, 0x38, 0x45, 0x41,
+ 0xE2, 0x88, 0x95, 0x6D, 0x45, 0x56, 0xE2, 0x88,
+ 0x95, 0x6D, 0x45, 0x6D, 0xE2, 0x88, 0x95, 0x73,
+ 0x46, 0x31, 0xE2, 0x81, 0x84, 0x31, 0x30, 0x46,
+ 0x43, 0xE2, 0x88, 0x95, 0x6B, 0x67, 0x46, 0x6D,
+ // Bytes 2440 - 247f
+ 0xE2, 0x88, 0x95, 0x73, 0x32, 0x46, 0xD8, 0xA8,
+ 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD8, 0xA8, 0xD8,
+ 0xAE, 0xD9, 0x8A, 0x46, 0xD8, 0xAA, 0xD8, 0xAC,
+ 0xD9, 0x85, 0x46, 0xD8, 0xAA, 0xD8, 0xAC, 0xD9,
+ 0x89, 0x46, 0xD8, 0xAA, 0xD8, 0xAC, 0xD9, 0x8A,
+ 0x46, 0xD8, 0xAA, 0xD8, 0xAD, 0xD8, 0xAC, 0x46,
+ 0xD8, 0xAA, 0xD8, 0xAD, 0xD9, 0x85, 0x46, 0xD8,
+ 0xAA, 0xD8, 0xAE, 0xD9, 0x85, 0x46, 0xD8, 0xAA,
+ // Bytes 2480 - 24bf
+ 0xD8, 0xAE, 0xD9, 0x89, 0x46, 0xD8, 0xAA, 0xD8,
+ 0xAE, 0xD9, 0x8A, 0x46, 0xD8, 0xAA, 0xD9, 0x85,
+ 0xD8, 0xAC, 0x46, 0xD8, 0xAA, 0xD9, 0x85, 0xD8,
+ 0xAD, 0x46, 0xD8, 0xAA, 0xD9, 0x85, 0xD8, 0xAE,
+ 0x46, 0xD8, 0xAA, 0xD9, 0x85, 0xD9, 0x89, 0x46,
+ 0xD8, 0xAA, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD8,
+ 0xAC, 0xD8, 0xAD, 0xD9, 0x89, 0x46, 0xD8, 0xAC,
+ 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD8, 0xAC, 0xD9,
+ // Bytes 24c0 - 24ff
+ 0x85, 0xD8, 0xAD, 0x46, 0xD8, 0xAC, 0xD9, 0x85,
+ 0xD9, 0x89, 0x46, 0xD8, 0xAC, 0xD9, 0x85, 0xD9,
+ 0x8A, 0x46, 0xD8, 0xAD, 0xD8, 0xAC, 0xD9, 0x8A,
+ 0x46, 0xD8, 0xAD, 0xD9, 0x85, 0xD9, 0x89, 0x46,
+ 0xD8, 0xAD, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD8,
+ 0xB3, 0xD8, 0xAC, 0xD8, 0xAD, 0x46, 0xD8, 0xB3,
+ 0xD8, 0xAC, 0xD9, 0x89, 0x46, 0xD8, 0xB3, 0xD8,
+ 0xAD, 0xD8, 0xAC, 0x46, 0xD8, 0xB3, 0xD8, 0xAE,
+ // Bytes 2500 - 253f
+ 0xD9, 0x89, 0x46, 0xD8, 0xB3, 0xD8, 0xAE, 0xD9,
+ 0x8A, 0x46, 0xD8, 0xB3, 0xD9, 0x85, 0xD8, 0xAC,
+ 0x46, 0xD8, 0xB3, 0xD9, 0x85, 0xD8, 0xAD, 0x46,
+ 0xD8, 0xB3, 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD8,
+ 0xB4, 0xD8, 0xAC, 0xD9, 0x8A, 0x46, 0xD8, 0xB4,
+ 0xD8, 0xAD, 0xD9, 0x85, 0x46, 0xD8, 0xB4, 0xD8,
+ 0xAD, 0xD9, 0x8A, 0x46, 0xD8, 0xB4, 0xD9, 0x85,
+ 0xD8, 0xAE, 0x46, 0xD8, 0xB4, 0xD9, 0x85, 0xD9,
+ // Bytes 2540 - 257f
+ 0x85, 0x46, 0xD8, 0xB5, 0xD8, 0xAD, 0xD8, 0xAD,
+ 0x46, 0xD8, 0xB5, 0xD8, 0xAD, 0xD9, 0x8A, 0x46,
+ 0xD8, 0xB5, 0xD9, 0x84, 0xD9, 0x89, 0x46, 0xD8,
+ 0xB5, 0xD9, 0x84, 0xDB, 0x92, 0x46, 0xD8, 0xB5,
+ 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD8, 0xB6, 0xD8,
+ 0xAD, 0xD9, 0x89, 0x46, 0xD8, 0xB6, 0xD8, 0xAD,
+ 0xD9, 0x8A, 0x46, 0xD8, 0xB6, 0xD8, 0xAE, 0xD9,
+ 0x85, 0x46, 0xD8, 0xB7, 0xD9, 0x85, 0xD8, 0xAD,
+ // Bytes 2580 - 25bf
+ 0x46, 0xD8, 0xB7, 0xD9, 0x85, 0xD9, 0x85, 0x46,
+ 0xD8, 0xB7, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD8,
+ 0xB9, 0xD8, 0xAC, 0xD9, 0x85, 0x46, 0xD8, 0xB9,
+ 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD8, 0xB9, 0xD9,
+ 0x85, 0xD9, 0x89, 0x46, 0xD8, 0xB9, 0xD9, 0x85,
+ 0xD9, 0x8A, 0x46, 0xD8, 0xBA, 0xD9, 0x85, 0xD9,
+ 0x85, 0x46, 0xD8, 0xBA, 0xD9, 0x85, 0xD9, 0x89,
+ 0x46, 0xD8, 0xBA, 0xD9, 0x85, 0xD9, 0x8A, 0x46,
+ // Bytes 25c0 - 25ff
+ 0xD9, 0x81, 0xD8, 0xAE, 0xD9, 0x85, 0x46, 0xD9,
+ 0x81, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x82,
+ 0xD9, 0x84, 0xDB, 0x92, 0x46, 0xD9, 0x82, 0xD9,
+ 0x85, 0xD8, 0xAD, 0x46, 0xD9, 0x82, 0xD9, 0x85,
+ 0xD9, 0x85, 0x46, 0xD9, 0x82, 0xD9, 0x85, 0xD9,
+ 0x8A, 0x46, 0xD9, 0x83, 0xD9, 0x85, 0xD9, 0x85,
+ 0x46, 0xD9, 0x83, 0xD9, 0x85, 0xD9, 0x8A, 0x46,
+ 0xD9, 0x84, 0xD8, 0xAC, 0xD8, 0xAC, 0x46, 0xD9,
+ // Bytes 2600 - 263f
+ 0x84, 0xD8, 0xAC, 0xD9, 0x85, 0x46, 0xD9, 0x84,
+ 0xD8, 0xAC, 0xD9, 0x8A, 0x46, 0xD9, 0x84, 0xD8,
+ 0xAD, 0xD9, 0x85, 0x46, 0xD9, 0x84, 0xD8, 0xAD,
+ 0xD9, 0x89, 0x46, 0xD9, 0x84, 0xD8, 0xAD, 0xD9,
+ 0x8A, 0x46, 0xD9, 0x84, 0xD8, 0xAE, 0xD9, 0x85,
+ 0x46, 0xD9, 0x84, 0xD9, 0x85, 0xD8, 0xAD, 0x46,
+ 0xD9, 0x84, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD9,
+ 0x85, 0xD8, 0xAC, 0xD8, 0xAD, 0x46, 0xD9, 0x85,
+ // Bytes 2640 - 267f
+ 0xD8, 0xAC, 0xD8, 0xAE, 0x46, 0xD9, 0x85, 0xD8,
+ 0xAC, 0xD9, 0x85, 0x46, 0xD9, 0x85, 0xD8, 0xAC,
+ 0xD9, 0x8A, 0x46, 0xD9, 0x85, 0xD8, 0xAD, 0xD8,
+ 0xAC, 0x46, 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x85,
+ 0x46, 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x8A, 0x46,
+ 0xD9, 0x85, 0xD8, 0xAE, 0xD8, 0xAC, 0x46, 0xD9,
+ 0x85, 0xD8, 0xAE, 0xD9, 0x85, 0x46, 0xD9, 0x85,
+ 0xD8, 0xAE, 0xD9, 0x8A, 0x46, 0xD9, 0x85, 0xD9,
+ // Bytes 2680 - 26bf
+ 0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x86, 0xD8, 0xAC,
+ 0xD8, 0xAD, 0x46, 0xD9, 0x86, 0xD8, 0xAC, 0xD9,
+ 0x85, 0x46, 0xD9, 0x86, 0xD8, 0xAC, 0xD9, 0x89,
+ 0x46, 0xD9, 0x86, 0xD8, 0xAC, 0xD9, 0x8A, 0x46,
+ 0xD9, 0x86, 0xD8, 0xAD, 0xD9, 0x85, 0x46, 0xD9,
+ 0x86, 0xD8, 0xAD, 0xD9, 0x89, 0x46, 0xD9, 0x86,
+ 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD9, 0x86, 0xD9,
+ 0x85, 0xD9, 0x89, 0x46, 0xD9, 0x86, 0xD9, 0x85,
+ // Bytes 26c0 - 26ff
+ 0xD9, 0x8A, 0x46, 0xD9, 0x87, 0xD9, 0x85, 0xD8,
+ 0xAC, 0x46, 0xD9, 0x87, 0xD9, 0x85, 0xD9, 0x85,
+ 0x46, 0xD9, 0x8A, 0xD8, 0xAC, 0xD9, 0x8A, 0x46,
+ 0xD9, 0x8A, 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD9,
+ 0x8A, 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD9, 0x8A,
+ 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xD8, 0xA7, 0x46, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xD8, 0xAC, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD8,
+ // Bytes 2700 - 273f
+ 0xAD, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xAE,
+ 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xB1, 0x46,
+ 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xB2, 0x46, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xD9, 0x85, 0x46, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xD9, 0x86, 0x46, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xD9, 0x87, 0x46, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xD9, 0x88, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD9,
+ 0x89, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x8A,
+ // Bytes 2740 - 277f
+ 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x86, 0x46,
+ 0xD9, 0x8A, 0xD9, 0x94, 0xDB, 0x87, 0x46, 0xD9,
+ 0x8A, 0xD9, 0x94, 0xDB, 0x88, 0x46, 0xD9, 0x8A,
+ 0xD9, 0x94, 0xDB, 0x90, 0x46, 0xD9, 0x8A, 0xD9,
+ 0x94, 0xDB, 0x95, 0x46, 0xE0, 0xB9, 0x8D, 0xE0,
+ 0xB8, 0xB2, 0x46, 0xE0, 0xBA, 0xAB, 0xE0, 0xBA,
+ 0x99, 0x46, 0xE0, 0xBA, 0xAB, 0xE0, 0xBA, 0xA1,
+ 0x46, 0xE0, 0xBB, 0x8D, 0xE0, 0xBA, 0xB2, 0x46,
+ // Bytes 2780 - 27bf
+ 0xE0, 0xBD, 0x80, 0xE0, 0xBE, 0xB5, 0x46, 0xE0,
+ 0xBD, 0x82, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBD,
+ 0x8C, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBD, 0x91,
+ 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBD, 0x96, 0xE0,
+ 0xBE, 0xB7, 0x46, 0xE0, 0xBD, 0x9B, 0xE0, 0xBE,
+ 0xB7, 0x46, 0xE0, 0xBE, 0x90, 0xE0, 0xBE, 0xB5,
+ 0x46, 0xE0, 0xBE, 0x92, 0xE0, 0xBE, 0xB7, 0x46,
+ 0xE0, 0xBE, 0x9C, 0xE0, 0xBE, 0xB7, 0x46, 0xE0,
+ // Bytes 27c0 - 27ff
+ 0xBE, 0xA1, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBE,
+ 0xA6, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBE, 0xAB,
+ 0xE0, 0xBE, 0xB7, 0x46, 0xE1, 0x84, 0x80, 0xE1,
+ 0x85, 0xA1, 0x46, 0xE1, 0x84, 0x82, 0xE1, 0x85,
+ 0xA1, 0x46, 0xE1, 0x84, 0x83, 0xE1, 0x85, 0xA1,
+ 0x46, 0xE1, 0x84, 0x85, 0xE1, 0x85, 0xA1, 0x46,
+ 0xE1, 0x84, 0x86, 0xE1, 0x85, 0xA1, 0x46, 0xE1,
+ 0x84, 0x87, 0xE1, 0x85, 0xA1, 0x46, 0xE1, 0x84,
+ // Bytes 2800 - 283f
+ 0x89, 0xE1, 0x85, 0xA1, 0x46, 0xE1, 0x84, 0x8B,
+ 0xE1, 0x85, 0xA1, 0x46, 0xE1, 0x84, 0x8B, 0xE1,
+ 0x85, 0xAE, 0x46, 0xE1, 0x84, 0x8C, 0xE1, 0x85,
+ 0xA1, 0x46, 0xE1, 0x84, 0x8E, 0xE1, 0x85, 0xA1,
+ 0x46, 0xE1, 0x84, 0x8F, 0xE1, 0x85, 0xA1, 0x46,
+ 0xE1, 0x84, 0x90, 0xE1, 0x85, 0xA1, 0x46, 0xE1,
+ 0x84, 0x91, 0xE1, 0x85, 0xA1, 0x46, 0xE1, 0x84,
+ 0x92, 0xE1, 0x85, 0xA1, 0x46, 0xE2, 0x80, 0xB2,
+ // Bytes 2840 - 287f
+ 0xE2, 0x80, 0xB2, 0x46, 0xE2, 0x80, 0xB5, 0xE2,
+ 0x80, 0xB5, 0x46, 0xE2, 0x88, 0xAB, 0xE2, 0x88,
+ 0xAB, 0x46, 0xE2, 0x88, 0xAE, 0xE2, 0x88, 0xAE,
+ 0x46, 0xE3, 0x81, 0xBB, 0xE3, 0x81, 0x8B, 0x46,
+ 0xE3, 0x82, 0x88, 0xE3, 0x82, 0x8A, 0x46, 0xE3,
+ 0x82, 0xAD, 0xE3, 0x83, 0xAD, 0x46, 0xE3, 0x82,
+ 0xB3, 0xE3, 0x82, 0xB3, 0x46, 0xE3, 0x82, 0xB3,
+ 0xE3, 0x83, 0x88, 0x46, 0xE3, 0x83, 0x88, 0xE3,
+ // Bytes 2880 - 28bf
+ 0x83, 0xB3, 0x46, 0xE3, 0x83, 0x8A, 0xE3, 0x83,
+ 0x8E, 0x46, 0xE3, 0x83, 0x9B, 0xE3, 0x83, 0xB3,
+ 0x46, 0xE3, 0x83, 0x9F, 0xE3, 0x83, 0xAA, 0x46,
+ 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xA9, 0x46, 0xE3,
+ 0x83, 0xAC, 0xE3, 0x83, 0xA0, 0x46, 0xE5, 0xA4,
+ 0xA7, 0xE6, 0xAD, 0xA3, 0x46, 0xE5, 0xB9, 0xB3,
+ 0xE6, 0x88, 0x90, 0x46, 0xE6, 0x98, 0x8E, 0xE6,
+ 0xB2, 0xBB, 0x46, 0xE6, 0x98, 0xAD, 0xE5, 0x92,
+ // Bytes 28c0 - 28ff
+ 0x8C, 0x47, 0x72, 0x61, 0x64, 0xE2, 0x88, 0x95,
+ 0x73, 0x47, 0xE3, 0x80, 0x94, 0x53, 0xE3, 0x80,
+ 0x95, 0x48, 0x28, 0xE1, 0x84, 0x80, 0xE1, 0x85,
+ 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x82, 0xE1,
+ 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x83,
+ 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84,
+ 0x85, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1,
+ 0x84, 0x86, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28,
+ // Bytes 2900 - 293f
+ 0xE1, 0x84, 0x87, 0xE1, 0x85, 0xA1, 0x29, 0x48,
+ 0x28, 0xE1, 0x84, 0x89, 0xE1, 0x85, 0xA1, 0x29,
+ 0x48, 0x28, 0xE1, 0x84, 0x8B, 0xE1, 0x85, 0xA1,
+ 0x29, 0x48, 0x28, 0xE1, 0x84, 0x8C, 0xE1, 0x85,
+ 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x8C, 0xE1,
+ 0x85, 0xAE, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x8E,
+ 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84,
+ 0x8F, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1,
+ // Bytes 2940 - 297f
+ 0x84, 0x90, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28,
+ 0xE1, 0x84, 0x91, 0xE1, 0x85, 0xA1, 0x29, 0x48,
+ 0x28, 0xE1, 0x84, 0x92, 0xE1, 0x85, 0xA1, 0x29,
+ 0x48, 0x72, 0x61, 0x64, 0xE2, 0x88, 0x95, 0x73,
+ 0x32, 0x48, 0xD8, 0xA7, 0xD9, 0x83, 0xD8, 0xA8,
+ 0xD8, 0xB1, 0x48, 0xD8, 0xA7, 0xD9, 0x84, 0xD9,
+ 0x84, 0xD9, 0x87, 0x48, 0xD8, 0xB1, 0xD8, 0xB3,
+ 0xD9, 0x88, 0xD9, 0x84, 0x48, 0xD8, 0xB1, 0xDB,
+ // Bytes 2980 - 29bf
+ 0x8C, 0xD8, 0xA7, 0xD9, 0x84, 0x48, 0xD8, 0xB5,
+ 0xD9, 0x84, 0xD8, 0xB9, 0xD9, 0x85, 0x48, 0xD8,
+ 0xB9, 0xD9, 0x84, 0xD9, 0x8A, 0xD9, 0x87, 0x48,
+ 0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x85, 0xD8, 0xAF,
+ 0x48, 0xD9, 0x88, 0xD8, 0xB3, 0xD9, 0x84, 0xD9,
+ 0x85, 0x49, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2,
+ 0xE2, 0x80, 0xB2, 0x49, 0xE2, 0x80, 0xB5, 0xE2,
+ 0x80, 0xB5, 0xE2, 0x80, 0xB5, 0x49, 0xE2, 0x88,
+ // Bytes 29c0 - 29ff
+ 0xAB, 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB, 0x49,
+ 0xE2, 0x88, 0xAE, 0xE2, 0x88, 0xAE, 0xE2, 0x88,
+ 0xAE, 0x49, 0xE3, 0x80, 0x94, 0xE4, 0xB8, 0x89,
+ 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x80, 0x94, 0xE4,
+ 0xBA, 0x8C, 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x80,
+ 0x94, 0xE5, 0x8B, 0x9D, 0xE3, 0x80, 0x95, 0x49,
+ 0xE3, 0x80, 0x94, 0xE5, 0xAE, 0x89, 0xE3, 0x80,
+ 0x95, 0x49, 0xE3, 0x80, 0x94, 0xE6, 0x89, 0x93,
+ // Bytes 2a00 - 2a3f
+ 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x80, 0x94, 0xE6,
+ 0x95, 0x97, 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x80,
+ 0x94, 0xE6, 0x9C, 0xAC, 0xE3, 0x80, 0x95, 0x49,
+ 0xE3, 0x80, 0x94, 0xE7, 0x82, 0xB9, 0xE3, 0x80,
+ 0x95, 0x49, 0xE3, 0x80, 0x94, 0xE7, 0x9B, 0x97,
+ 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x82, 0xA2, 0xE3,
+ 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0x49, 0xE3, 0x82,
+ 0xA4, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x81, 0x49,
+ // Bytes 2a40 - 2a7f
+ 0xE3, 0x82, 0xA6, 0xE3, 0x82, 0xA9, 0xE3, 0x83,
+ 0xB3, 0x49, 0xE3, 0x82, 0xAA, 0xE3, 0x83, 0xB3,
+ 0xE3, 0x82, 0xB9, 0x49, 0xE3, 0x82, 0xAA, 0xE3,
+ 0x83, 0xBC, 0xE3, 0x83, 0xA0, 0x49, 0xE3, 0x82,
+ 0xAB, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0xAA, 0x49,
+ 0xE3, 0x82, 0xB1, 0xE3, 0x83, 0xBC, 0xE3, 0x82,
+ 0xB9, 0x49, 0xE3, 0x82, 0xB3, 0xE3, 0x83, 0xAB,
+ 0xE3, 0x83, 0x8A, 0x49, 0xE3, 0x82, 0xBB, 0xE3,
+ // Bytes 2a80 - 2abf
+ 0x83, 0xB3, 0xE3, 0x83, 0x81, 0x49, 0xE3, 0x82,
+ 0xBB, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88, 0x49,
+ 0xE3, 0x83, 0x86, 0xE3, 0x82, 0x99, 0xE3, 0x82,
+ 0xB7, 0x49, 0xE3, 0x83, 0x88, 0xE3, 0x82, 0x99,
+ 0xE3, 0x83, 0xAB, 0x49, 0xE3, 0x83, 0x8E, 0xE3,
+ 0x83, 0x83, 0xE3, 0x83, 0x88, 0x49, 0xE3, 0x83,
+ 0x8F, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0x84, 0x49,
+ 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ // Bytes 2ac0 - 2aff
+ 0xAB, 0x49, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x9A,
+ 0xE3, 0x82, 0xB3, 0x49, 0xE3, 0x83, 0x95, 0xE3,
+ 0x83, 0xA9, 0xE3, 0x83, 0xB3, 0x49, 0xE3, 0x83,
+ 0x98, 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xBD, 0x49,
+ 0xE3, 0x83, 0x98, 0xE3, 0x83, 0xAB, 0xE3, 0x83,
+ 0x84, 0x49, 0xE3, 0x83, 0x9B, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x83, 0xAB, 0x49, 0xE3, 0x83, 0x9B, 0xE3,
+ 0x83, 0xBC, 0xE3, 0x83, 0xB3, 0x49, 0xE3, 0x83,
+ // Bytes 2b00 - 2b3f
+ 0x9E, 0xE3, 0x82, 0xA4, 0xE3, 0x83, 0xAB, 0x49,
+ 0xE3, 0x83, 0x9E, 0xE3, 0x83, 0x83, 0xE3, 0x83,
+ 0x8F, 0x49, 0xE3, 0x83, 0x9E, 0xE3, 0x83, 0xAB,
+ 0xE3, 0x82, 0xAF, 0x49, 0xE3, 0x83, 0xA4, 0xE3,
+ 0x83, 0xBC, 0xE3, 0x83, 0xAB, 0x49, 0xE3, 0x83,
+ 0xA6, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0xB3, 0x49,
+ 0xE3, 0x83, 0xAF, 0xE3, 0x83, 0x83, 0xE3, 0x83,
+ 0x88, 0x4C, 0xE1, 0x84, 0x8C, 0xE1, 0x85, 0xAE,
+ // Bytes 2b40 - 2b7f
+ 0xE1, 0x84, 0x8B, 0xE1, 0x85, 0xB4, 0x4C, 0xE2,
+ 0x80, 0xB2, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2,
+ 0xE2, 0x80, 0xB2, 0x4C, 0xE2, 0x88, 0xAB, 0xE2,
+ 0x88, 0xAB, 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB,
+ 0x4C, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0xAB, 0xE3,
+ 0x83, 0x95, 0xE3, 0x82, 0xA1, 0x4C, 0xE3, 0x82,
+ 0xA8, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xAB, 0xE3,
+ 0x83, 0xBC, 0x4C, 0xE3, 0x82, 0xAB, 0xE3, 0x82,
+ // Bytes 2b80 - 2bbf
+ 0x99, 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xB3, 0x4C,
+ 0xE3, 0x82, 0xAB, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0xB3, 0xE3, 0x83, 0x9E, 0x4C, 0xE3, 0x82, 0xAB,
+ 0xE3, 0x83, 0xA9, 0xE3, 0x83, 0x83, 0xE3, 0x83,
+ 0x88, 0x4C, 0xE3, 0x82, 0xAB, 0xE3, 0x83, 0xAD,
+ 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xBC, 0x4C, 0xE3,
+ 0x82, 0xAD, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0x8B,
+ 0xE3, 0x83, 0xBC, 0x4C, 0xE3, 0x82, 0xAD, 0xE3,
+ // Bytes 2bc0 - 2bff
+ 0x83, 0xA5, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xBC,
+ 0x4C, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0xE3,
+ 0x83, 0xA9, 0xE3, 0x83, 0xA0, 0x4C, 0xE3, 0x82,
+ 0xAF, 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x83, 0x8D, 0x4C, 0xE3, 0x82, 0xB5, 0xE3, 0x82,
+ 0xA4, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAB, 0x4C,
+ 0xE3, 0x82, 0xBF, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0xBC, 0xE3, 0x82, 0xB9, 0x4C, 0xE3, 0x83, 0x8F,
+ // Bytes 2c00 - 2c3f
+ 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+ 0x84, 0x4C, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x9A,
+ 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAB, 0x4C, 0xE3,
+ 0x83, 0x95, 0xE3, 0x82, 0xA3, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x83, 0x88, 0x4C, 0xE3, 0x83, 0x98, 0xE3,
+ 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xBF,
+ 0x4C, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x9A, 0xE3,
+ 0x83, 0x8B, 0xE3, 0x83, 0x92, 0x4C, 0xE3, 0x83,
+ // Bytes 2c40 - 2c7f
+ 0x98, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xB3, 0xE3,
+ 0x82, 0xB9, 0x4C, 0xE3, 0x83, 0x9B, 0xE3, 0x82,
+ 0x99, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x88, 0x4C,
+ 0xE3, 0x83, 0x9E, 0xE3, 0x82, 0xA4, 0xE3, 0x82,
+ 0xAF, 0xE3, 0x83, 0xAD, 0x4C, 0xE3, 0x83, 0x9F,
+ 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAD, 0xE3, 0x83,
+ 0xB3, 0x4C, 0xE3, 0x83, 0xA1, 0xE3, 0x83, 0xBC,
+ 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xAB, 0x4C, 0xE3,
+ // Bytes 2c80 - 2cbf
+ 0x83, 0xAA, 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88,
+ 0xE3, 0x83, 0xAB, 0x4C, 0xE3, 0x83, 0xAB, 0xE3,
+ 0x83, 0x92, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC,
+ 0x4C, 0xE6, 0xA0, 0xAA, 0xE5, 0xBC, 0x8F, 0xE4,
+ 0xBC, 0x9A, 0xE7, 0xA4, 0xBE, 0x4E, 0x28, 0xE1,
+ 0x84, 0x8B, 0xE1, 0x85, 0xA9, 0xE1, 0x84, 0x92,
+ 0xE1, 0x85, 0xAE, 0x29, 0x4F, 0xD8, 0xAC, 0xD9,
+ 0x84, 0x20, 0xD8, 0xAC, 0xD9, 0x84, 0xD8, 0xA7,
+ // Bytes 2cc0 - 2cff
+ 0xD9, 0x84, 0xD9, 0x87, 0x4F, 0xE1, 0x84, 0x8E,
+ 0xE1, 0x85, 0xA1, 0xE1, 0x86, 0xB7, 0xE1, 0x84,
+ 0x80, 0xE1, 0x85, 0xA9, 0x4F, 0xE3, 0x82, 0xA2,
+ 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x9A, 0xE3, 0x83,
+ 0xBC, 0xE3, 0x83, 0x88, 0x4F, 0xE3, 0x82, 0xA2,
+ 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x98, 0xE3, 0x82,
+ 0x9A, 0xE3, 0x82, 0xA2, 0x4F, 0xE3, 0x82, 0xAD,
+ 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xAF, 0xE3, 0x83,
+ // Bytes 2d00 - 2d3f
+ 0x83, 0xE3, 0x83, 0x88, 0x4F, 0xE3, 0x82, 0xB5,
+ 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x81, 0xE3, 0x83,
+ 0xBC, 0xE3, 0x83, 0xA0, 0x4F, 0xE3, 0x83, 0x8F,
+ 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+ 0xAC, 0xE3, 0x83, 0xAB, 0x4F, 0xE3, 0x83, 0x98,
+ 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0xBF, 0xE3, 0x83,
+ 0xBC, 0xE3, 0x83, 0xAB, 0x4F, 0xE3, 0x83, 0x9B,
+ 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xA4, 0xE3, 0x83,
+ // Bytes 2d40 - 2d7f
+ 0xB3, 0xE3, 0x83, 0x88, 0x4F, 0xE3, 0x83, 0x9E,
+ 0xE3, 0x83, 0xB3, 0xE3, 0x82, 0xB7, 0xE3, 0x83,
+ 0xA7, 0xE3, 0x83, 0xB3, 0x4F, 0xE3, 0x83, 0xA1,
+ 0xE3, 0x82, 0xAB, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0x88, 0xE3, 0x83, 0xB3, 0x4F, 0xE3, 0x83, 0xAB,
+ 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x95, 0xE3, 0x82,
+ 0x99, 0xE3, 0x83, 0xAB, 0x51, 0x28, 0xE1, 0x84,
+ 0x8B, 0xE1, 0x85, 0xA9, 0xE1, 0x84, 0x8C, 0xE1,
+ // Bytes 2d80 - 2dbf
+ 0x85, 0xA5, 0xE1, 0x86, 0xAB, 0x29, 0x52, 0xE3,
+ 0x82, 0xAD, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xAB,
+ 0xE3, 0x82, 0xBF, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0xBC, 0x52, 0xE3, 0x82, 0xAD, 0xE3, 0x83, 0xAD,
+ 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0xA9, 0xE3, 0x83, 0xA0, 0x52, 0xE3, 0x82, 0xAD,
+ 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xA1, 0xE3, 0x83,
+ 0xBC, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xAB, 0x52,
+ // Bytes 2dc0 - 2dff
+ 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+ 0xA9, 0xE3, 0x83, 0xA0, 0xE3, 0x83, 0x88, 0xE3,
+ 0x83, 0xB3, 0x52, 0xE3, 0x82, 0xAF, 0xE3, 0x83,
+ 0xAB, 0xE3, 0x82, 0xBB, 0xE3, 0x82, 0x99, 0xE3,
+ 0x82, 0xA4, 0xE3, 0x83, 0xAD, 0x52, 0xE3, 0x83,
+ 0x8F, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x82, 0xBB, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88,
+ 0x52, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x9A, 0xE3,
+ // Bytes 2e00 - 2e3f
+ 0x82, 0xA2, 0xE3, 0x82, 0xB9, 0xE3, 0x83, 0x88,
+ 0xE3, 0x83, 0xAB, 0x52, 0xE3, 0x83, 0x95, 0xE3,
+ 0x82, 0x99, 0xE3, 0x83, 0x83, 0xE3, 0x82, 0xB7,
+ 0xE3, 0x82, 0xA7, 0xE3, 0x83, 0xAB, 0x52, 0xE3,
+ 0x83, 0x9F, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0x8F,
+ 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+ 0xAB, 0x52, 0xE3, 0x83, 0xAC, 0xE3, 0x83, 0xB3,
+ 0xE3, 0x83, 0x88, 0xE3, 0x82, 0xB1, 0xE3, 0x82,
+ // Bytes 2e40 - 2e7f
+ 0x99, 0xE3, 0x83, 0xB3, 0x61, 0xD8, 0xB5, 0xD9,
+ 0x84, 0xD9, 0x89, 0x20, 0xD8, 0xA7, 0xD9, 0x84,
+ 0xD9, 0x84, 0xD9, 0x87, 0x20, 0xD8, 0xB9, 0xD9,
+ 0x84, 0xD9, 0x8A, 0xD9, 0x87, 0x20, 0xD9, 0x88,
+ 0xD8, 0xB3, 0xD9, 0x84, 0xD9, 0x85, 0x86, 0xE0,
+ 0xB3, 0x86, 0xE0, 0xB3, 0x82, 0x86, 0xE0, 0xB7,
+ 0x99, 0xE0, 0xB7, 0x8F, 0x09, 0xE0, 0xB7, 0x99,
+ 0xE0, 0xB7, 0x8F, 0xE0, 0xB7, 0x8A, 0x09, 0x44,
+ // Bytes 2e80 - 2ebf
+ 0x44, 0x5A, 0xCC, 0x8C, 0xE6, 0x44, 0x44, 0x7A,
+ 0xCC, 0x8C, 0xE6, 0x44, 0x64, 0x7A, 0xCC, 0x8C,
+ 0xE6, 0x46, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x93,
+ 0xE6, 0x46, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x94,
+ 0xE6, 0x46, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x95,
+ 0xDC, 0x49, 0xE3, 0x83, 0xA1, 0xE3, 0x82, 0xAB,
+ 0xE3, 0x82, 0x99, 0x08, 0x4C, 0xE3, 0x82, 0xAD,
+ 0xE3, 0x82, 0x99, 0xE3, 0x82, 0xAB, 0xE3, 0x82,
+ // Bytes 2ec0 - 2eff
+ 0x99, 0x08, 0x4C, 0xE3, 0x82, 0xB3, 0xE3, 0x83,
+ 0xBC, 0xE3, 0x83, 0x9B, 0xE3, 0x82, 0x9A, 0x08,
+ 0x4C, 0xE3, 0x83, 0xA4, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x83, 0x88, 0xE3, 0x82, 0x99, 0x08, 0x4F, 0xE3,
+ 0x82, 0xA4, 0xE3, 0x83, 0x8B, 0xE3, 0x83, 0xB3,
+ 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0x08, 0x4F,
+ 0xE3, 0x82, 0xB7, 0xE3, 0x83, 0xAA, 0xE3, 0x83,
+ 0xB3, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0x08,
+ // Bytes 2f00 - 2f3f
+ 0x4F, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x9A, 0xE3,
+ 0x83, 0xBC, 0xE3, 0x82, 0xB7, 0xE3, 0x82, 0x99,
+ 0x08, 0x4F, 0xE3, 0x83, 0x9B, 0xE3, 0x82, 0x9A,
+ 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88, 0xE3, 0x82,
+ 0x99, 0x08, 0x52, 0xE3, 0x82, 0xA8, 0xE3, 0x82,
+ 0xB9, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xBC, 0xE3,
+ 0x83, 0x88, 0xE3, 0x82, 0x99, 0x08, 0x52, 0xE3,
+ 0x83, 0x95, 0xE3, 0x82, 0xA1, 0xE3, 0x83, 0xA9,
+ // Bytes 2f40 - 2f7f
+ 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88, 0xE3, 0x82,
+ 0x99, 0x08, 0x03, 0x3C, 0xCC, 0xB8, 0x01, 0x03,
+ 0x3D, 0xCC, 0xB8, 0x01, 0x03, 0x3E, 0xCC, 0xB8,
+ 0x01, 0x03, 0x41, 0xCC, 0x80, 0xE6, 0x03, 0x41,
+ 0xCC, 0x81, 0xE6, 0x03, 0x41, 0xCC, 0x83, 0xE6,
+ 0x03, 0x41, 0xCC, 0x84, 0xE6, 0x03, 0x41, 0xCC,
+ 0x89, 0xE6, 0x03, 0x41, 0xCC, 0x8C, 0xE6, 0x03,
+ 0x41, 0xCC, 0x8F, 0xE6, 0x03, 0x41, 0xCC, 0x91,
+ // Bytes 2f80 - 2fbf
+ 0xE6, 0x03, 0x41, 0xCC, 0xA5, 0xDC, 0x03, 0x41,
+ 0xCC, 0xA8, 0xCA, 0x03, 0x42, 0xCC, 0x87, 0xE6,
+ 0x03, 0x42, 0xCC, 0xA3, 0xDC, 0x03, 0x42, 0xCC,
+ 0xB1, 0xDC, 0x03, 0x43, 0xCC, 0x81, 0xE6, 0x03,
+ 0x43, 0xCC, 0x82, 0xE6, 0x03, 0x43, 0xCC, 0x87,
+ 0xE6, 0x03, 0x43, 0xCC, 0x8C, 0xE6, 0x03, 0x44,
+ 0xCC, 0x87, 0xE6, 0x03, 0x44, 0xCC, 0x8C, 0xE6,
+ 0x03, 0x44, 0xCC, 0xA3, 0xDC, 0x03, 0x44, 0xCC,
+ // Bytes 2fc0 - 2fff
+ 0xA7, 0xCA, 0x03, 0x44, 0xCC, 0xAD, 0xDC, 0x03,
+ 0x44, 0xCC, 0xB1, 0xDC, 0x03, 0x45, 0xCC, 0x80,
+ 0xE6, 0x03, 0x45, 0xCC, 0x81, 0xE6, 0x03, 0x45,
+ 0xCC, 0x83, 0xE6, 0x03, 0x45, 0xCC, 0x86, 0xE6,
+ 0x03, 0x45, 0xCC, 0x87, 0xE6, 0x03, 0x45, 0xCC,
+ 0x88, 0xE6, 0x03, 0x45, 0xCC, 0x89, 0xE6, 0x03,
+ 0x45, 0xCC, 0x8C, 0xE6, 0x03, 0x45, 0xCC, 0x8F,
+ 0xE6, 0x03, 0x45, 0xCC, 0x91, 0xE6, 0x03, 0x45,
+ // Bytes 3000 - 303f
+ 0xCC, 0xA8, 0xCA, 0x03, 0x45, 0xCC, 0xAD, 0xDC,
+ 0x03, 0x45, 0xCC, 0xB0, 0xDC, 0x03, 0x46, 0xCC,
+ 0x87, 0xE6, 0x03, 0x47, 0xCC, 0x81, 0xE6, 0x03,
+ 0x47, 0xCC, 0x82, 0xE6, 0x03, 0x47, 0xCC, 0x84,
+ 0xE6, 0x03, 0x47, 0xCC, 0x86, 0xE6, 0x03, 0x47,
+ 0xCC, 0x87, 0xE6, 0x03, 0x47, 0xCC, 0x8C, 0xE6,
+ 0x03, 0x47, 0xCC, 0xA7, 0xCA, 0x03, 0x48, 0xCC,
+ 0x82, 0xE6, 0x03, 0x48, 0xCC, 0x87, 0xE6, 0x03,
+ // Bytes 3040 - 307f
+ 0x48, 0xCC, 0x88, 0xE6, 0x03, 0x48, 0xCC, 0x8C,
+ 0xE6, 0x03, 0x48, 0xCC, 0xA3, 0xDC, 0x03, 0x48,
+ 0xCC, 0xA7, 0xCA, 0x03, 0x48, 0xCC, 0xAE, 0xDC,
+ 0x03, 0x49, 0xCC, 0x80, 0xE6, 0x03, 0x49, 0xCC,
+ 0x81, 0xE6, 0x03, 0x49, 0xCC, 0x82, 0xE6, 0x03,
+ 0x49, 0xCC, 0x83, 0xE6, 0x03, 0x49, 0xCC, 0x84,
+ 0xE6, 0x03, 0x49, 0xCC, 0x86, 0xE6, 0x03, 0x49,
+ 0xCC, 0x87, 0xE6, 0x03, 0x49, 0xCC, 0x89, 0xE6,
+ // Bytes 3080 - 30bf
+ 0x03, 0x49, 0xCC, 0x8C, 0xE6, 0x03, 0x49, 0xCC,
+ 0x8F, 0xE6, 0x03, 0x49, 0xCC, 0x91, 0xE6, 0x03,
+ 0x49, 0xCC, 0xA3, 0xDC, 0x03, 0x49, 0xCC, 0xA8,
+ 0xCA, 0x03, 0x49, 0xCC, 0xB0, 0xDC, 0x03, 0x4A,
+ 0xCC, 0x82, 0xE6, 0x03, 0x4B, 0xCC, 0x81, 0xE6,
+ 0x03, 0x4B, 0xCC, 0x8C, 0xE6, 0x03, 0x4B, 0xCC,
+ 0xA3, 0xDC, 0x03, 0x4B, 0xCC, 0xA7, 0xCA, 0x03,
+ 0x4B, 0xCC, 0xB1, 0xDC, 0x03, 0x4C, 0xCC, 0x81,
+ // Bytes 30c0 - 30ff
+ 0xE6, 0x03, 0x4C, 0xCC, 0x8C, 0xE6, 0x03, 0x4C,
+ 0xCC, 0xA7, 0xCA, 0x03, 0x4C, 0xCC, 0xAD, 0xDC,
+ 0x03, 0x4C, 0xCC, 0xB1, 0xDC, 0x03, 0x4D, 0xCC,
+ 0x81, 0xE6, 0x03, 0x4D, 0xCC, 0x87, 0xE6, 0x03,
+ 0x4D, 0xCC, 0xA3, 0xDC, 0x03, 0x4E, 0xCC, 0x80,
+ 0xE6, 0x03, 0x4E, 0xCC, 0x81, 0xE6, 0x03, 0x4E,
+ 0xCC, 0x83, 0xE6, 0x03, 0x4E, 0xCC, 0x87, 0xE6,
+ 0x03, 0x4E, 0xCC, 0x8C, 0xE6, 0x03, 0x4E, 0xCC,
+ // Bytes 3100 - 313f
+ 0xA3, 0xDC, 0x03, 0x4E, 0xCC, 0xA7, 0xCA, 0x03,
+ 0x4E, 0xCC, 0xAD, 0xDC, 0x03, 0x4E, 0xCC, 0xB1,
+ 0xDC, 0x03, 0x4F, 0xCC, 0x80, 0xE6, 0x03, 0x4F,
+ 0xCC, 0x81, 0xE6, 0x03, 0x4F, 0xCC, 0x86, 0xE6,
+ 0x03, 0x4F, 0xCC, 0x89, 0xE6, 0x03, 0x4F, 0xCC,
+ 0x8B, 0xE6, 0x03, 0x4F, 0xCC, 0x8C, 0xE6, 0x03,
+ 0x4F, 0xCC, 0x8F, 0xE6, 0x03, 0x4F, 0xCC, 0x91,
+ 0xE6, 0x03, 0x50, 0xCC, 0x81, 0xE6, 0x03, 0x50,
+ // Bytes 3140 - 317f
+ 0xCC, 0x87, 0xE6, 0x03, 0x52, 0xCC, 0x81, 0xE6,
+ 0x03, 0x52, 0xCC, 0x87, 0xE6, 0x03, 0x52, 0xCC,
+ 0x8C, 0xE6, 0x03, 0x52, 0xCC, 0x8F, 0xE6, 0x03,
+ 0x52, 0xCC, 0x91, 0xE6, 0x03, 0x52, 0xCC, 0xA7,
+ 0xCA, 0x03, 0x52, 0xCC, 0xB1, 0xDC, 0x03, 0x53,
+ 0xCC, 0x82, 0xE6, 0x03, 0x53, 0xCC, 0x87, 0xE6,
+ 0x03, 0x53, 0xCC, 0xA6, 0xDC, 0x03, 0x53, 0xCC,
+ 0xA7, 0xCA, 0x03, 0x54, 0xCC, 0x87, 0xE6, 0x03,
+ // Bytes 3180 - 31bf
+ 0x54, 0xCC, 0x8C, 0xE6, 0x03, 0x54, 0xCC, 0xA3,
+ 0xDC, 0x03, 0x54, 0xCC, 0xA6, 0xDC, 0x03, 0x54,
+ 0xCC, 0xA7, 0xCA, 0x03, 0x54, 0xCC, 0xAD, 0xDC,
+ 0x03, 0x54, 0xCC, 0xB1, 0xDC, 0x03, 0x55, 0xCC,
+ 0x80, 0xE6, 0x03, 0x55, 0xCC, 0x81, 0xE6, 0x03,
+ 0x55, 0xCC, 0x82, 0xE6, 0x03, 0x55, 0xCC, 0x86,
+ 0xE6, 0x03, 0x55, 0xCC, 0x89, 0xE6, 0x03, 0x55,
+ 0xCC, 0x8A, 0xE6, 0x03, 0x55, 0xCC, 0x8B, 0xE6,
+ // Bytes 31c0 - 31ff
+ 0x03, 0x55, 0xCC, 0x8C, 0xE6, 0x03, 0x55, 0xCC,
+ 0x8F, 0xE6, 0x03, 0x55, 0xCC, 0x91, 0xE6, 0x03,
+ 0x55, 0xCC, 0xA3, 0xDC, 0x03, 0x55, 0xCC, 0xA4,
+ 0xDC, 0x03, 0x55, 0xCC, 0xA8, 0xCA, 0x03, 0x55,
+ 0xCC, 0xAD, 0xDC, 0x03, 0x55, 0xCC, 0xB0, 0xDC,
+ 0x03, 0x56, 0xCC, 0x83, 0xE6, 0x03, 0x56, 0xCC,
+ 0xA3, 0xDC, 0x03, 0x57, 0xCC, 0x80, 0xE6, 0x03,
+ 0x57, 0xCC, 0x81, 0xE6, 0x03, 0x57, 0xCC, 0x82,
+ // Bytes 3200 - 323f
+ 0xE6, 0x03, 0x57, 0xCC, 0x87, 0xE6, 0x03, 0x57,
+ 0xCC, 0x88, 0xE6, 0x03, 0x57, 0xCC, 0xA3, 0xDC,
+ 0x03, 0x58, 0xCC, 0x87, 0xE6, 0x03, 0x58, 0xCC,
+ 0x88, 0xE6, 0x03, 0x59, 0xCC, 0x80, 0xE6, 0x03,
+ 0x59, 0xCC, 0x81, 0xE6, 0x03, 0x59, 0xCC, 0x82,
+ 0xE6, 0x03, 0x59, 0xCC, 0x83, 0xE6, 0x03, 0x59,
+ 0xCC, 0x84, 0xE6, 0x03, 0x59, 0xCC, 0x87, 0xE6,
+ 0x03, 0x59, 0xCC, 0x88, 0xE6, 0x03, 0x59, 0xCC,
+ // Bytes 3240 - 327f
+ 0x89, 0xE6, 0x03, 0x59, 0xCC, 0xA3, 0xDC, 0x03,
+ 0x5A, 0xCC, 0x81, 0xE6, 0x03, 0x5A, 0xCC, 0x82,
+ 0xE6, 0x03, 0x5A, 0xCC, 0x87, 0xE6, 0x03, 0x5A,
+ 0xCC, 0x8C, 0xE6, 0x03, 0x5A, 0xCC, 0xA3, 0xDC,
+ 0x03, 0x5A, 0xCC, 0xB1, 0xDC, 0x03, 0x61, 0xCC,
+ 0x80, 0xE6, 0x03, 0x61, 0xCC, 0x81, 0xE6, 0x03,
+ 0x61, 0xCC, 0x83, 0xE6, 0x03, 0x61, 0xCC, 0x84,
+ 0xE6, 0x03, 0x61, 0xCC, 0x89, 0xE6, 0x03, 0x61,
+ // Bytes 3280 - 32bf
+ 0xCC, 0x8C, 0xE6, 0x03, 0x61, 0xCC, 0x8F, 0xE6,
+ 0x03, 0x61, 0xCC, 0x91, 0xE6, 0x03, 0x61, 0xCC,
+ 0xA5, 0xDC, 0x03, 0x61, 0xCC, 0xA8, 0xCA, 0x03,
+ 0x62, 0xCC, 0x87, 0xE6, 0x03, 0x62, 0xCC, 0xA3,
+ 0xDC, 0x03, 0x62, 0xCC, 0xB1, 0xDC, 0x03, 0x63,
+ 0xCC, 0x81, 0xE6, 0x03, 0x63, 0xCC, 0x82, 0xE6,
+ 0x03, 0x63, 0xCC, 0x87, 0xE6, 0x03, 0x63, 0xCC,
+ 0x8C, 0xE6, 0x03, 0x64, 0xCC, 0x87, 0xE6, 0x03,
+ // Bytes 32c0 - 32ff
+ 0x64, 0xCC, 0x8C, 0xE6, 0x03, 0x64, 0xCC, 0xA3,
+ 0xDC, 0x03, 0x64, 0xCC, 0xA7, 0xCA, 0x03, 0x64,
+ 0xCC, 0xAD, 0xDC, 0x03, 0x64, 0xCC, 0xB1, 0xDC,
+ 0x03, 0x65, 0xCC, 0x80, 0xE6, 0x03, 0x65, 0xCC,
+ 0x81, 0xE6, 0x03, 0x65, 0xCC, 0x83, 0xE6, 0x03,
+ 0x65, 0xCC, 0x86, 0xE6, 0x03, 0x65, 0xCC, 0x87,
+ 0xE6, 0x03, 0x65, 0xCC, 0x88, 0xE6, 0x03, 0x65,
+ 0xCC, 0x89, 0xE6, 0x03, 0x65, 0xCC, 0x8C, 0xE6,
+ // Bytes 3300 - 333f
+ 0x03, 0x65, 0xCC, 0x8F, 0xE6, 0x03, 0x65, 0xCC,
+ 0x91, 0xE6, 0x03, 0x65, 0xCC, 0xA8, 0xCA, 0x03,
+ 0x65, 0xCC, 0xAD, 0xDC, 0x03, 0x65, 0xCC, 0xB0,
+ 0xDC, 0x03, 0x66, 0xCC, 0x87, 0xE6, 0x03, 0x67,
+ 0xCC, 0x81, 0xE6, 0x03, 0x67, 0xCC, 0x82, 0xE6,
+ 0x03, 0x67, 0xCC, 0x84, 0xE6, 0x03, 0x67, 0xCC,
+ 0x86, 0xE6, 0x03, 0x67, 0xCC, 0x87, 0xE6, 0x03,
+ 0x67, 0xCC, 0x8C, 0xE6, 0x03, 0x67, 0xCC, 0xA7,
+ // Bytes 3340 - 337f
+ 0xCA, 0x03, 0x68, 0xCC, 0x82, 0xE6, 0x03, 0x68,
+ 0xCC, 0x87, 0xE6, 0x03, 0x68, 0xCC, 0x88, 0xE6,
+ 0x03, 0x68, 0xCC, 0x8C, 0xE6, 0x03, 0x68, 0xCC,
+ 0xA3, 0xDC, 0x03, 0x68, 0xCC, 0xA7, 0xCA, 0x03,
+ 0x68, 0xCC, 0xAE, 0xDC, 0x03, 0x68, 0xCC, 0xB1,
+ 0xDC, 0x03, 0x69, 0xCC, 0x80, 0xE6, 0x03, 0x69,
+ 0xCC, 0x81, 0xE6, 0x03, 0x69, 0xCC, 0x82, 0xE6,
+ 0x03, 0x69, 0xCC, 0x83, 0xE6, 0x03, 0x69, 0xCC,
+ // Bytes 3380 - 33bf
+ 0x84, 0xE6, 0x03, 0x69, 0xCC, 0x86, 0xE6, 0x03,
+ 0x69, 0xCC, 0x89, 0xE6, 0x03, 0x69, 0xCC, 0x8C,
+ 0xE6, 0x03, 0x69, 0xCC, 0x8F, 0xE6, 0x03, 0x69,
+ 0xCC, 0x91, 0xE6, 0x03, 0x69, 0xCC, 0xA3, 0xDC,
+ 0x03, 0x69, 0xCC, 0xA8, 0xCA, 0x03, 0x69, 0xCC,
+ 0xB0, 0xDC, 0x03, 0x6A, 0xCC, 0x82, 0xE6, 0x03,
+ 0x6A, 0xCC, 0x8C, 0xE6, 0x03, 0x6B, 0xCC, 0x81,
+ 0xE6, 0x03, 0x6B, 0xCC, 0x8C, 0xE6, 0x03, 0x6B,
+ // Bytes 33c0 - 33ff
+ 0xCC, 0xA3, 0xDC, 0x03, 0x6B, 0xCC, 0xA7, 0xCA,
+ 0x03, 0x6B, 0xCC, 0xB1, 0xDC, 0x03, 0x6C, 0xCC,
+ 0x81, 0xE6, 0x03, 0x6C, 0xCC, 0x8C, 0xE6, 0x03,
+ 0x6C, 0xCC, 0xA7, 0xCA, 0x03, 0x6C, 0xCC, 0xAD,
+ 0xDC, 0x03, 0x6C, 0xCC, 0xB1, 0xDC, 0x03, 0x6D,
+ 0xCC, 0x81, 0xE6, 0x03, 0x6D, 0xCC, 0x87, 0xE6,
+ 0x03, 0x6D, 0xCC, 0xA3, 0xDC, 0x03, 0x6E, 0xCC,
+ 0x80, 0xE6, 0x03, 0x6E, 0xCC, 0x81, 0xE6, 0x03,
+ // Bytes 3400 - 343f
+ 0x6E, 0xCC, 0x83, 0xE6, 0x03, 0x6E, 0xCC, 0x87,
+ 0xE6, 0x03, 0x6E, 0xCC, 0x8C, 0xE6, 0x03, 0x6E,
+ 0xCC, 0xA3, 0xDC, 0x03, 0x6E, 0xCC, 0xA7, 0xCA,
+ 0x03, 0x6E, 0xCC, 0xAD, 0xDC, 0x03, 0x6E, 0xCC,
+ 0xB1, 0xDC, 0x03, 0x6F, 0xCC, 0x80, 0xE6, 0x03,
+ 0x6F, 0xCC, 0x81, 0xE6, 0x03, 0x6F, 0xCC, 0x86,
+ 0xE6, 0x03, 0x6F, 0xCC, 0x89, 0xE6, 0x03, 0x6F,
+ 0xCC, 0x8B, 0xE6, 0x03, 0x6F, 0xCC, 0x8C, 0xE6,
+ // Bytes 3440 - 347f
+ 0x03, 0x6F, 0xCC, 0x8F, 0xE6, 0x03, 0x6F, 0xCC,
+ 0x91, 0xE6, 0x03, 0x70, 0xCC, 0x81, 0xE6, 0x03,
+ 0x70, 0xCC, 0x87, 0xE6, 0x03, 0x72, 0xCC, 0x81,
+ 0xE6, 0x03, 0x72, 0xCC, 0x87, 0xE6, 0x03, 0x72,
+ 0xCC, 0x8C, 0xE6, 0x03, 0x72, 0xCC, 0x8F, 0xE6,
+ 0x03, 0x72, 0xCC, 0x91, 0xE6, 0x03, 0x72, 0xCC,
+ 0xA7, 0xCA, 0x03, 0x72, 0xCC, 0xB1, 0xDC, 0x03,
+ 0x73, 0xCC, 0x82, 0xE6, 0x03, 0x73, 0xCC, 0x87,
+ // Bytes 3480 - 34bf
+ 0xE6, 0x03, 0x73, 0xCC, 0xA6, 0xDC, 0x03, 0x73,
+ 0xCC, 0xA7, 0xCA, 0x03, 0x74, 0xCC, 0x87, 0xE6,
+ 0x03, 0x74, 0xCC, 0x88, 0xE6, 0x03, 0x74, 0xCC,
+ 0x8C, 0xE6, 0x03, 0x74, 0xCC, 0xA3, 0xDC, 0x03,
+ 0x74, 0xCC, 0xA6, 0xDC, 0x03, 0x74, 0xCC, 0xA7,
+ 0xCA, 0x03, 0x74, 0xCC, 0xAD, 0xDC, 0x03, 0x74,
+ 0xCC, 0xB1, 0xDC, 0x03, 0x75, 0xCC, 0x80, 0xE6,
+ 0x03, 0x75, 0xCC, 0x81, 0xE6, 0x03, 0x75, 0xCC,
+ // Bytes 34c0 - 34ff
+ 0x82, 0xE6, 0x03, 0x75, 0xCC, 0x86, 0xE6, 0x03,
+ 0x75, 0xCC, 0x89, 0xE6, 0x03, 0x75, 0xCC, 0x8A,
+ 0xE6, 0x03, 0x75, 0xCC, 0x8B, 0xE6, 0x03, 0x75,
+ 0xCC, 0x8C, 0xE6, 0x03, 0x75, 0xCC, 0x8F, 0xE6,
+ 0x03, 0x75, 0xCC, 0x91, 0xE6, 0x03, 0x75, 0xCC,
+ 0xA3, 0xDC, 0x03, 0x75, 0xCC, 0xA4, 0xDC, 0x03,
+ 0x75, 0xCC, 0xA8, 0xCA, 0x03, 0x75, 0xCC, 0xAD,
+ 0xDC, 0x03, 0x75, 0xCC, 0xB0, 0xDC, 0x03, 0x76,
+ // Bytes 3500 - 353f
+ 0xCC, 0x83, 0xE6, 0x03, 0x76, 0xCC, 0xA3, 0xDC,
+ 0x03, 0x77, 0xCC, 0x80, 0xE6, 0x03, 0x77, 0xCC,
+ 0x81, 0xE6, 0x03, 0x77, 0xCC, 0x82, 0xE6, 0x03,
+ 0x77, 0xCC, 0x87, 0xE6, 0x03, 0x77, 0xCC, 0x88,
+ 0xE6, 0x03, 0x77, 0xCC, 0x8A, 0xE6, 0x03, 0x77,
+ 0xCC, 0xA3, 0xDC, 0x03, 0x78, 0xCC, 0x87, 0xE6,
+ 0x03, 0x78, 0xCC, 0x88, 0xE6, 0x03, 0x79, 0xCC,
+ 0x80, 0xE6, 0x03, 0x79, 0xCC, 0x81, 0xE6, 0x03,
+ // Bytes 3540 - 357f
+ 0x79, 0xCC, 0x82, 0xE6, 0x03, 0x79, 0xCC, 0x83,
+ 0xE6, 0x03, 0x79, 0xCC, 0x84, 0xE6, 0x03, 0x79,
+ 0xCC, 0x87, 0xE6, 0x03, 0x79, 0xCC, 0x88, 0xE6,
+ 0x03, 0x79, 0xCC, 0x89, 0xE6, 0x03, 0x79, 0xCC,
+ 0x8A, 0xE6, 0x03, 0x79, 0xCC, 0xA3, 0xDC, 0x03,
+ 0x7A, 0xCC, 0x81, 0xE6, 0x03, 0x7A, 0xCC, 0x82,
+ 0xE6, 0x03, 0x7A, 0xCC, 0x87, 0xE6, 0x03, 0x7A,
+ 0xCC, 0x8C, 0xE6, 0x03, 0x7A, 0xCC, 0xA3, 0xDC,
+ // Bytes 3580 - 35bf
+ 0x03, 0x7A, 0xCC, 0xB1, 0xDC, 0x04, 0xC2, 0xA8,
+ 0xCC, 0x80, 0xE6, 0x04, 0xC2, 0xA8, 0xCC, 0x81,
+ 0xE6, 0x04, 0xC2, 0xA8, 0xCD, 0x82, 0xE6, 0x04,
+ 0xC3, 0x86, 0xCC, 0x81, 0xE6, 0x04, 0xC3, 0x86,
+ 0xCC, 0x84, 0xE6, 0x04, 0xC3, 0x98, 0xCC, 0x81,
+ 0xE6, 0x04, 0xC3, 0xA6, 0xCC, 0x81, 0xE6, 0x04,
+ 0xC3, 0xA6, 0xCC, 0x84, 0xE6, 0x04, 0xC3, 0xB8,
+ 0xCC, 0x81, 0xE6, 0x04, 0xC5, 0xBF, 0xCC, 0x87,
+ // Bytes 35c0 - 35ff
+ 0xE6, 0x04, 0xC6, 0xB7, 0xCC, 0x8C, 0xE6, 0x04,
+ 0xCA, 0x92, 0xCC, 0x8C, 0xE6, 0x04, 0xCE, 0x91,
+ 0xCC, 0x80, 0xE6, 0x04, 0xCE, 0x91, 0xCC, 0x81,
+ 0xE6, 0x04, 0xCE, 0x91, 0xCC, 0x84, 0xE6, 0x04,
+ 0xCE, 0x91, 0xCC, 0x86, 0xE6, 0x04, 0xCE, 0x91,
+ 0xCD, 0x85, 0xF0, 0x04, 0xCE, 0x95, 0xCC, 0x80,
+ 0xE6, 0x04, 0xCE, 0x95, 0xCC, 0x81, 0xE6, 0x04,
+ 0xCE, 0x97, 0xCC, 0x80, 0xE6, 0x04, 0xCE, 0x97,
+ // Bytes 3600 - 363f
+ 0xCC, 0x81, 0xE6, 0x04, 0xCE, 0x97, 0xCD, 0x85,
+ 0xF0, 0x04, 0xCE, 0x99, 0xCC, 0x80, 0xE6, 0x04,
+ 0xCE, 0x99, 0xCC, 0x81, 0xE6, 0x04, 0xCE, 0x99,
+ 0xCC, 0x84, 0xE6, 0x04, 0xCE, 0x99, 0xCC, 0x86,
+ 0xE6, 0x04, 0xCE, 0x99, 0xCC, 0x88, 0xE6, 0x04,
+ 0xCE, 0x9F, 0xCC, 0x80, 0xE6, 0x04, 0xCE, 0x9F,
+ 0xCC, 0x81, 0xE6, 0x04, 0xCE, 0xA1, 0xCC, 0x94,
+ 0xE6, 0x04, 0xCE, 0xA5, 0xCC, 0x80, 0xE6, 0x04,
+ // Bytes 3640 - 367f
+ 0xCE, 0xA5, 0xCC, 0x81, 0xE6, 0x04, 0xCE, 0xA5,
+ 0xCC, 0x84, 0xE6, 0x04, 0xCE, 0xA5, 0xCC, 0x86,
+ 0xE6, 0x04, 0xCE, 0xA5, 0xCC, 0x88, 0xE6, 0x04,
+ 0xCE, 0xA9, 0xCC, 0x80, 0xE6, 0x04, 0xCE, 0xA9,
+ 0xCC, 0x81, 0xE6, 0x04, 0xCE, 0xA9, 0xCD, 0x85,
+ 0xF0, 0x04, 0xCE, 0xB1, 0xCC, 0x84, 0xE6, 0x04,
+ 0xCE, 0xB1, 0xCC, 0x86, 0xE6, 0x04, 0xCE, 0xB1,
+ 0xCD, 0x85, 0xF0, 0x04, 0xCE, 0xB5, 0xCC, 0x80,
+ // Bytes 3680 - 36bf
+ 0xE6, 0x04, 0xCE, 0xB5, 0xCC, 0x81, 0xE6, 0x04,
+ 0xCE, 0xB7, 0xCD, 0x85, 0xF0, 0x04, 0xCE, 0xB9,
+ 0xCC, 0x80, 0xE6, 0x04, 0xCE, 0xB9, 0xCC, 0x81,
+ 0xE6, 0x04, 0xCE, 0xB9, 0xCC, 0x84, 0xE6, 0x04,
+ 0xCE, 0xB9, 0xCC, 0x86, 0xE6, 0x04, 0xCE, 0xB9,
+ 0xCD, 0x82, 0xE6, 0x04, 0xCE, 0xBF, 0xCC, 0x80,
+ 0xE6, 0x04, 0xCE, 0xBF, 0xCC, 0x81, 0xE6, 0x04,
+ 0xCF, 0x81, 0xCC, 0x93, 0xE6, 0x04, 0xCF, 0x81,
+ // Bytes 36c0 - 36ff
+ 0xCC, 0x94, 0xE6, 0x04, 0xCF, 0x85, 0xCC, 0x80,
+ 0xE6, 0x04, 0xCF, 0x85, 0xCC, 0x81, 0xE6, 0x04,
+ 0xCF, 0x85, 0xCC, 0x84, 0xE6, 0x04, 0xCF, 0x85,
+ 0xCC, 0x86, 0xE6, 0x04, 0xCF, 0x85, 0xCD, 0x82,
+ 0xE6, 0x04, 0xCF, 0x89, 0xCD, 0x85, 0xF0, 0x04,
+ 0xCF, 0x92, 0xCC, 0x81, 0xE6, 0x04, 0xCF, 0x92,
+ 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0x86, 0xCC, 0x88,
+ 0xE6, 0x04, 0xD0, 0x90, 0xCC, 0x86, 0xE6, 0x04,
+ // Bytes 3700 - 373f
+ 0xD0, 0x90, 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0x93,
+ 0xCC, 0x81, 0xE6, 0x04, 0xD0, 0x95, 0xCC, 0x80,
+ 0xE6, 0x04, 0xD0, 0x95, 0xCC, 0x86, 0xE6, 0x04,
+ 0xD0, 0x95, 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0x96,
+ 0xCC, 0x86, 0xE6, 0x04, 0xD0, 0x96, 0xCC, 0x88,
+ 0xE6, 0x04, 0xD0, 0x97, 0xCC, 0x88, 0xE6, 0x04,
+ 0xD0, 0x98, 0xCC, 0x80, 0xE6, 0x04, 0xD0, 0x98,
+ 0xCC, 0x84, 0xE6, 0x04, 0xD0, 0x98, 0xCC, 0x86,
+ // Bytes 3740 - 377f
+ 0xE6, 0x04, 0xD0, 0x98, 0xCC, 0x88, 0xE6, 0x04,
+ 0xD0, 0x9A, 0xCC, 0x81, 0xE6, 0x04, 0xD0, 0x9E,
+ 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0xA3, 0xCC, 0x84,
+ 0xE6, 0x04, 0xD0, 0xA3, 0xCC, 0x86, 0xE6, 0x04,
+ 0xD0, 0xA3, 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0xA3,
+ 0xCC, 0x8B, 0xE6, 0x04, 0xD0, 0xA7, 0xCC, 0x88,
+ 0xE6, 0x04, 0xD0, 0xAB, 0xCC, 0x88, 0xE6, 0x04,
+ 0xD0, 0xAD, 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0xB0,
+ // Bytes 3780 - 37bf
+ 0xCC, 0x86, 0xE6, 0x04, 0xD0, 0xB0, 0xCC, 0x88,
+ 0xE6, 0x04, 0xD0, 0xB3, 0xCC, 0x81, 0xE6, 0x04,
+ 0xD0, 0xB5, 0xCC, 0x80, 0xE6, 0x04, 0xD0, 0xB5,
+ 0xCC, 0x86, 0xE6, 0x04, 0xD0, 0xB5, 0xCC, 0x88,
+ 0xE6, 0x04, 0xD0, 0xB6, 0xCC, 0x86, 0xE6, 0x04,
+ 0xD0, 0xB6, 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0xB7,
+ 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0xB8, 0xCC, 0x80,
+ 0xE6, 0x04, 0xD0, 0xB8, 0xCC, 0x84, 0xE6, 0x04,
+ // Bytes 37c0 - 37ff
+ 0xD0, 0xB8, 0xCC, 0x86, 0xE6, 0x04, 0xD0, 0xB8,
+ 0xCC, 0x88, 0xE6, 0x04, 0xD0, 0xBA, 0xCC, 0x81,
+ 0xE6, 0x04, 0xD0, 0xBE, 0xCC, 0x88, 0xE6, 0x04,
+ 0xD1, 0x83, 0xCC, 0x84, 0xE6, 0x04, 0xD1, 0x83,
+ 0xCC, 0x86, 0xE6, 0x04, 0xD1, 0x83, 0xCC, 0x88,
+ 0xE6, 0x04, 0xD1, 0x83, 0xCC, 0x8B, 0xE6, 0x04,
+ 0xD1, 0x87, 0xCC, 0x88, 0xE6, 0x04, 0xD1, 0x8B,
+ 0xCC, 0x88, 0xE6, 0x04, 0xD1, 0x8D, 0xCC, 0x88,
+ // Bytes 3800 - 383f
+ 0xE6, 0x04, 0xD1, 0x96, 0xCC, 0x88, 0xE6, 0x04,
+ 0xD1, 0xB4, 0xCC, 0x8F, 0xE6, 0x04, 0xD1, 0xB5,
+ 0xCC, 0x8F, 0xE6, 0x04, 0xD3, 0x98, 0xCC, 0x88,
+ 0xE6, 0x04, 0xD3, 0x99, 0xCC, 0x88, 0xE6, 0x04,
+ 0xD3, 0xA8, 0xCC, 0x88, 0xE6, 0x04, 0xD3, 0xA9,
+ 0xCC, 0x88, 0xE6, 0x04, 0xD8, 0xA7, 0xD9, 0x93,
+ 0xE6, 0x04, 0xD8, 0xA7, 0xD9, 0x94, 0xE6, 0x04,
+ 0xD8, 0xA7, 0xD9, 0x95, 0xDC, 0x04, 0xD9, 0x88,
+ // Bytes 3840 - 387f
+ 0xD9, 0x94, 0xE6, 0x04, 0xD9, 0x8A, 0xD9, 0x94,
+ 0xE6, 0x04, 0xDB, 0x81, 0xD9, 0x94, 0xE6, 0x04,
+ 0xDB, 0x92, 0xD9, 0x94, 0xE6, 0x04, 0xDB, 0x95,
+ 0xD9, 0x94, 0xE6, 0x05, 0x41, 0xCC, 0x82, 0xCC,
+ 0x80, 0xE6, 0x05, 0x41, 0xCC, 0x82, 0xCC, 0x81,
+ 0xE6, 0x05, 0x41, 0xCC, 0x82, 0xCC, 0x83, 0xE6,
+ 0x05, 0x41, 0xCC, 0x82, 0xCC, 0x89, 0xE6, 0x05,
+ 0x41, 0xCC, 0x86, 0xCC, 0x80, 0xE6, 0x05, 0x41,
+ // Bytes 3880 - 38bf
+ 0xCC, 0x86, 0xCC, 0x81, 0xE6, 0x05, 0x41, 0xCC,
+ 0x86, 0xCC, 0x83, 0xE6, 0x05, 0x41, 0xCC, 0x86,
+ 0xCC, 0x89, 0xE6, 0x05, 0x41, 0xCC, 0x87, 0xCC,
+ 0x84, 0xE6, 0x05, 0x41, 0xCC, 0x88, 0xCC, 0x84,
+ 0xE6, 0x05, 0x41, 0xCC, 0x8A, 0xCC, 0x81, 0xE6,
+ 0x05, 0x41, 0xCC, 0xA3, 0xCC, 0x82, 0xE6, 0x05,
+ 0x41, 0xCC, 0xA3, 0xCC, 0x86, 0xE6, 0x05, 0x43,
+ 0xCC, 0xA7, 0xCC, 0x81, 0xE6, 0x05, 0x45, 0xCC,
+ // Bytes 38c0 - 38ff
+ 0x82, 0xCC, 0x80, 0xE6, 0x05, 0x45, 0xCC, 0x82,
+ 0xCC, 0x81, 0xE6, 0x05, 0x45, 0xCC, 0x82, 0xCC,
+ 0x83, 0xE6, 0x05, 0x45, 0xCC, 0x82, 0xCC, 0x89,
+ 0xE6, 0x05, 0x45, 0xCC, 0x84, 0xCC, 0x80, 0xE6,
+ 0x05, 0x45, 0xCC, 0x84, 0xCC, 0x81, 0xE6, 0x05,
+ 0x45, 0xCC, 0xA3, 0xCC, 0x82, 0xE6, 0x05, 0x45,
+ 0xCC, 0xA7, 0xCC, 0x86, 0xE6, 0x05, 0x49, 0xCC,
+ 0x88, 0xCC, 0x81, 0xE6, 0x05, 0x4C, 0xCC, 0xA3,
+ // Bytes 3900 - 393f
+ 0xCC, 0x84, 0xE6, 0x05, 0x4F, 0xCC, 0x82, 0xCC,
+ 0x80, 0xE6, 0x05, 0x4F, 0xCC, 0x82, 0xCC, 0x81,
+ 0xE6, 0x05, 0x4F, 0xCC, 0x82, 0xCC, 0x83, 0xE6,
+ 0x05, 0x4F, 0xCC, 0x82, 0xCC, 0x89, 0xE6, 0x05,
+ 0x4F, 0xCC, 0x83, 0xCC, 0x81, 0xE6, 0x05, 0x4F,
+ 0xCC, 0x83, 0xCC, 0x84, 0xE6, 0x05, 0x4F, 0xCC,
+ 0x83, 0xCC, 0x88, 0xE6, 0x05, 0x4F, 0xCC, 0x84,
+ 0xCC, 0x80, 0xE6, 0x05, 0x4F, 0xCC, 0x84, 0xCC,
+ // Bytes 3940 - 397f
+ 0x81, 0xE6, 0x05, 0x4F, 0xCC, 0x87, 0xCC, 0x84,
+ 0xE6, 0x05, 0x4F, 0xCC, 0x88, 0xCC, 0x84, 0xE6,
+ 0x05, 0x4F, 0xCC, 0x9B, 0xCC, 0x80, 0xE6, 0x05,
+ 0x4F, 0xCC, 0x9B, 0xCC, 0x81, 0xE6, 0x05, 0x4F,
+ 0xCC, 0x9B, 0xCC, 0x83, 0xE6, 0x05, 0x4F, 0xCC,
+ 0x9B, 0xCC, 0x89, 0xE6, 0x05, 0x4F, 0xCC, 0x9B,
+ 0xCC, 0xA3, 0xDC, 0x05, 0x4F, 0xCC, 0xA3, 0xCC,
+ 0x82, 0xE6, 0x05, 0x4F, 0xCC, 0xA8, 0xCC, 0x84,
+ // Bytes 3980 - 39bf
+ 0xE6, 0x05, 0x52, 0xCC, 0xA3, 0xCC, 0x84, 0xE6,
+ 0x05, 0x53, 0xCC, 0x81, 0xCC, 0x87, 0xE6, 0x05,
+ 0x53, 0xCC, 0x8C, 0xCC, 0x87, 0xE6, 0x05, 0x53,
+ 0xCC, 0xA3, 0xCC, 0x87, 0xE6, 0x05, 0x55, 0xCC,
+ 0x83, 0xCC, 0x81, 0xE6, 0x05, 0x55, 0xCC, 0x84,
+ 0xCC, 0x88, 0xE6, 0x05, 0x55, 0xCC, 0x88, 0xCC,
+ 0x80, 0xE6, 0x05, 0x55, 0xCC, 0x88, 0xCC, 0x81,
+ 0xE6, 0x05, 0x55, 0xCC, 0x88, 0xCC, 0x84, 0xE6,
+ // Bytes 39c0 - 39ff
+ 0x05, 0x55, 0xCC, 0x88, 0xCC, 0x8C, 0xE6, 0x05,
+ 0x55, 0xCC, 0x9B, 0xCC, 0x80, 0xE6, 0x05, 0x55,
+ 0xCC, 0x9B, 0xCC, 0x81, 0xE6, 0x05, 0x55, 0xCC,
+ 0x9B, 0xCC, 0x83, 0xE6, 0x05, 0x55, 0xCC, 0x9B,
+ 0xCC, 0x89, 0xE6, 0x05, 0x55, 0xCC, 0x9B, 0xCC,
+ 0xA3, 0xDC, 0x05, 0x61, 0xCC, 0x82, 0xCC, 0x80,
+ 0xE6, 0x05, 0x61, 0xCC, 0x82, 0xCC, 0x81, 0xE6,
+ 0x05, 0x61, 0xCC, 0x82, 0xCC, 0x83, 0xE6, 0x05,
+ // Bytes 3a00 - 3a3f
+ 0x61, 0xCC, 0x82, 0xCC, 0x89, 0xE6, 0x05, 0x61,
+ 0xCC, 0x86, 0xCC, 0x80, 0xE6, 0x05, 0x61, 0xCC,
+ 0x86, 0xCC, 0x81, 0xE6, 0x05, 0x61, 0xCC, 0x86,
+ 0xCC, 0x83, 0xE6, 0x05, 0x61, 0xCC, 0x86, 0xCC,
+ 0x89, 0xE6, 0x05, 0x61, 0xCC, 0x87, 0xCC, 0x84,
+ 0xE6, 0x05, 0x61, 0xCC, 0x88, 0xCC, 0x84, 0xE6,
+ 0x05, 0x61, 0xCC, 0x8A, 0xCC, 0x81, 0xE6, 0x05,
+ 0x61, 0xCC, 0xA3, 0xCC, 0x82, 0xE6, 0x05, 0x61,
+ // Bytes 3a40 - 3a7f
+ 0xCC, 0xA3, 0xCC, 0x86, 0xE6, 0x05, 0x63, 0xCC,
+ 0xA7, 0xCC, 0x81, 0xE6, 0x05, 0x65, 0xCC, 0x82,
+ 0xCC, 0x80, 0xE6, 0x05, 0x65, 0xCC, 0x82, 0xCC,
+ 0x81, 0xE6, 0x05, 0x65, 0xCC, 0x82, 0xCC, 0x83,
+ 0xE6, 0x05, 0x65, 0xCC, 0x82, 0xCC, 0x89, 0xE6,
+ 0x05, 0x65, 0xCC, 0x84, 0xCC, 0x80, 0xE6, 0x05,
+ 0x65, 0xCC, 0x84, 0xCC, 0x81, 0xE6, 0x05, 0x65,
+ 0xCC, 0xA3, 0xCC, 0x82, 0xE6, 0x05, 0x65, 0xCC,
+ // Bytes 3a80 - 3abf
+ 0xA7, 0xCC, 0x86, 0xE6, 0x05, 0x69, 0xCC, 0x88,
+ 0xCC, 0x81, 0xE6, 0x05, 0x6C, 0xCC, 0xA3, 0xCC,
+ 0x84, 0xE6, 0x05, 0x6F, 0xCC, 0x82, 0xCC, 0x80,
+ 0xE6, 0x05, 0x6F, 0xCC, 0x82, 0xCC, 0x81, 0xE6,
+ 0x05, 0x6F, 0xCC, 0x82, 0xCC, 0x83, 0xE6, 0x05,
+ 0x6F, 0xCC, 0x82, 0xCC, 0x89, 0xE6, 0x05, 0x6F,
+ 0xCC, 0x83, 0xCC, 0x81, 0xE6, 0x05, 0x6F, 0xCC,
+ 0x83, 0xCC, 0x84, 0xE6, 0x05, 0x6F, 0xCC, 0x83,
+ // Bytes 3ac0 - 3aff
+ 0xCC, 0x88, 0xE6, 0x05, 0x6F, 0xCC, 0x84, 0xCC,
+ 0x80, 0xE6, 0x05, 0x6F, 0xCC, 0x84, 0xCC, 0x81,
+ 0xE6, 0x05, 0x6F, 0xCC, 0x87, 0xCC, 0x84, 0xE6,
+ 0x05, 0x6F, 0xCC, 0x88, 0xCC, 0x84, 0xE6, 0x05,
+ 0x6F, 0xCC, 0x9B, 0xCC, 0x80, 0xE6, 0x05, 0x6F,
+ 0xCC, 0x9B, 0xCC, 0x81, 0xE6, 0x05, 0x6F, 0xCC,
+ 0x9B, 0xCC, 0x83, 0xE6, 0x05, 0x6F, 0xCC, 0x9B,
+ 0xCC, 0x89, 0xE6, 0x05, 0x6F, 0xCC, 0x9B, 0xCC,
+ // Bytes 3b00 - 3b3f
+ 0xA3, 0xDC, 0x05, 0x6F, 0xCC, 0xA3, 0xCC, 0x82,
+ 0xE6, 0x05, 0x6F, 0xCC, 0xA8, 0xCC, 0x84, 0xE6,
+ 0x05, 0x72, 0xCC, 0xA3, 0xCC, 0x84, 0xE6, 0x05,
+ 0x73, 0xCC, 0x81, 0xCC, 0x87, 0xE6, 0x05, 0x73,
+ 0xCC, 0x8C, 0xCC, 0x87, 0xE6, 0x05, 0x73, 0xCC,
+ 0xA3, 0xCC, 0x87, 0xE6, 0x05, 0x75, 0xCC, 0x83,
+ 0xCC, 0x81, 0xE6, 0x05, 0x75, 0xCC, 0x84, 0xCC,
+ 0x88, 0xE6, 0x05, 0x75, 0xCC, 0x88, 0xCC, 0x80,
+ // Bytes 3b40 - 3b7f
+ 0xE6, 0x05, 0x75, 0xCC, 0x88, 0xCC, 0x81, 0xE6,
+ 0x05, 0x75, 0xCC, 0x88, 0xCC, 0x84, 0xE6, 0x05,
+ 0x75, 0xCC, 0x88, 0xCC, 0x8C, 0xE6, 0x05, 0x75,
+ 0xCC, 0x9B, 0xCC, 0x80, 0xE6, 0x05, 0x75, 0xCC,
+ 0x9B, 0xCC, 0x81, 0xE6, 0x05, 0x75, 0xCC, 0x9B,
+ 0xCC, 0x83, 0xE6, 0x05, 0x75, 0xCC, 0x9B, 0xCC,
+ 0x89, 0xE6, 0x05, 0x75, 0xCC, 0x9B, 0xCC, 0xA3,
+ 0xDC, 0x05, 0xE1, 0xBE, 0xBF, 0xCC, 0x80, 0xE6,
+ // Bytes 3b80 - 3bbf
+ 0x05, 0xE1, 0xBE, 0xBF, 0xCC, 0x81, 0xE6, 0x05,
+ 0xE1, 0xBE, 0xBF, 0xCD, 0x82, 0xE6, 0x05, 0xE1,
+ 0xBF, 0xBE, 0xCC, 0x80, 0xE6, 0x05, 0xE1, 0xBF,
+ 0xBE, 0xCC, 0x81, 0xE6, 0x05, 0xE1, 0xBF, 0xBE,
+ 0xCD, 0x82, 0xE6, 0x05, 0xE2, 0x86, 0x90, 0xCC,
+ 0xB8, 0x01, 0x05, 0xE2, 0x86, 0x92, 0xCC, 0xB8,
+ 0x01, 0x05, 0xE2, 0x86, 0x94, 0xCC, 0xB8, 0x01,
+ 0x05, 0xE2, 0x87, 0x90, 0xCC, 0xB8, 0x01, 0x05,
+ // Bytes 3bc0 - 3bff
+ 0xE2, 0x87, 0x92, 0xCC, 0xB8, 0x01, 0x05, 0xE2,
+ 0x87, 0x94, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x88,
+ 0x83, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x88, 0x88,
+ 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x88, 0x8B, 0xCC,
+ 0xB8, 0x01, 0x05, 0xE2, 0x88, 0xA3, 0xCC, 0xB8,
+ 0x01, 0x05, 0xE2, 0x88, 0xA5, 0xCC, 0xB8, 0x01,
+ 0x05, 0xE2, 0x88, 0xBC, 0xCC, 0xB8, 0x01, 0x05,
+ 0xE2, 0x89, 0x83, 0xCC, 0xB8, 0x01, 0x05, 0xE2,
+ // Bytes 3c00 - 3c3f
+ 0x89, 0x85, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x89,
+ 0x88, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x89, 0x8D,
+ 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x89, 0xA1, 0xCC,
+ 0xB8, 0x01, 0x05, 0xE2, 0x89, 0xA4, 0xCC, 0xB8,
+ 0x01, 0x05, 0xE2, 0x89, 0xA5, 0xCC, 0xB8, 0x01,
+ 0x05, 0xE2, 0x89, 0xB2, 0xCC, 0xB8, 0x01, 0x05,
+ 0xE2, 0x89, 0xB3, 0xCC, 0xB8, 0x01, 0x05, 0xE2,
+ 0x89, 0xB6, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x89,
+ // Bytes 3c40 - 3c7f
+ 0xB7, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x89, 0xBA,
+ 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x89, 0xBB, 0xCC,
+ 0xB8, 0x01, 0x05, 0xE2, 0x89, 0xBC, 0xCC, 0xB8,
+ 0x01, 0x05, 0xE2, 0x89, 0xBD, 0xCC, 0xB8, 0x01,
+ 0x05, 0xE2, 0x8A, 0x82, 0xCC, 0xB8, 0x01, 0x05,
+ 0xE2, 0x8A, 0x83, 0xCC, 0xB8, 0x01, 0x05, 0xE2,
+ 0x8A, 0x86, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x8A,
+ 0x87, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x8A, 0x91,
+ // Bytes 3c80 - 3cbf
+ 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x8A, 0x92, 0xCC,
+ 0xB8, 0x01, 0x05, 0xE2, 0x8A, 0xA2, 0xCC, 0xB8,
+ 0x01, 0x05, 0xE2, 0x8A, 0xA8, 0xCC, 0xB8, 0x01,
+ 0x05, 0xE2, 0x8A, 0xA9, 0xCC, 0xB8, 0x01, 0x05,
+ 0xE2, 0x8A, 0xAB, 0xCC, 0xB8, 0x01, 0x05, 0xE2,
+ 0x8A, 0xB2, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x8A,
+ 0xB3, 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x8A, 0xB4,
+ 0xCC, 0xB8, 0x01, 0x05, 0xE2, 0x8A, 0xB5, 0xCC,
+ // Bytes 3cc0 - 3cff
+ 0xB8, 0x01, 0x06, 0xCE, 0x91, 0xCC, 0x93, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0x91, 0xCC, 0x94, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0x95, 0xCC, 0x93, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0x95, 0xCC, 0x93, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCE, 0x95, 0xCC, 0x94, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0x95, 0xCC, 0x94, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCE, 0x97, 0xCC, 0x93, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0x97, 0xCC, 0x94, 0xCD,
+ // Bytes 3d00 - 3d3f
+ 0x85, 0xF0, 0x06, 0xCE, 0x99, 0xCC, 0x93, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0x99, 0xCC, 0x93, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCE, 0x99, 0xCC, 0x93, 0xCD,
+ 0x82, 0xE6, 0x06, 0xCE, 0x99, 0xCC, 0x94, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0x99, 0xCC, 0x94, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCE, 0x99, 0xCC, 0x94, 0xCD,
+ 0x82, 0xE6, 0x06, 0xCE, 0x9F, 0xCC, 0x93, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0x9F, 0xCC, 0x93, 0xCC,
+ // Bytes 3d40 - 3d7f
+ 0x81, 0xE6, 0x06, 0xCE, 0x9F, 0xCC, 0x94, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0x9F, 0xCC, 0x94, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCE, 0xA5, 0xCC, 0x94, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0xA5, 0xCC, 0x94, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCE, 0xA5, 0xCC, 0x94, 0xCD,
+ 0x82, 0xE6, 0x06, 0xCE, 0xA9, 0xCC, 0x93, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0xA9, 0xCC, 0x94, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0xB1, 0xCC, 0x80, 0xCD,
+ // Bytes 3d80 - 3dbf
+ 0x85, 0xF0, 0x06, 0xCE, 0xB1, 0xCC, 0x81, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0xB1, 0xCC, 0x93, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0xB1, 0xCC, 0x94, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0xB1, 0xCD, 0x82, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0xB5, 0xCC, 0x93, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0xB5, 0xCC, 0x93, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCE, 0xB5, 0xCC, 0x94, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0xB5, 0xCC, 0x94, 0xCC,
+ // Bytes 3dc0 - 3dff
+ 0x81, 0xE6, 0x06, 0xCE, 0xB7, 0xCC, 0x80, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0xB7, 0xCC, 0x81, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0xB7, 0xCC, 0x93, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0xB7, 0xCC, 0x94, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0xB7, 0xCD, 0x82, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCE, 0xB9, 0xCC, 0x88, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x88, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x88, 0xCD,
+ // Bytes 3e00 - 3e3f
+ 0x82, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x93, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x93, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x93, 0xCD,
+ 0x82, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x94, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x94, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCE, 0xB9, 0xCC, 0x94, 0xCD,
+ 0x82, 0xE6, 0x06, 0xCE, 0xBF, 0xCC, 0x93, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0xBF, 0xCC, 0x93, 0xCC,
+ // Bytes 3e40 - 3e7f
+ 0x81, 0xE6, 0x06, 0xCE, 0xBF, 0xCC, 0x94, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCE, 0xBF, 0xCC, 0x94, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x88, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x88, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x88, 0xCD,
+ 0x82, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x93, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x93, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x93, 0xCD,
+ // Bytes 3e80 - 3ebf
+ 0x82, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x94, 0xCC,
+ 0x80, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x94, 0xCC,
+ 0x81, 0xE6, 0x06, 0xCF, 0x85, 0xCC, 0x94, 0xCD,
+ 0x82, 0xE6, 0x06, 0xCF, 0x89, 0xCC, 0x80, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCF, 0x89, 0xCC, 0x81, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCF, 0x89, 0xCC, 0x93, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCF, 0x89, 0xCC, 0x94, 0xCD,
+ 0x85, 0xF0, 0x06, 0xCF, 0x89, 0xCD, 0x82, 0xCD,
+ // Bytes 3ec0 - 3eff
+ 0x85, 0xF0, 0x06, 0xE0, 0xA4, 0xA8, 0xE0, 0xA4,
+ 0xBC, 0x07, 0x06, 0xE0, 0xA4, 0xB0, 0xE0, 0xA4,
+ 0xBC, 0x07, 0x06, 0xE0, 0xA4, 0xB3, 0xE0, 0xA4,
+ 0xBC, 0x07, 0x06, 0xE0, 0xB1, 0x86, 0xE0, 0xB1,
+ 0x96, 0x5B, 0x06, 0xE0, 0xB7, 0x99, 0xE0, 0xB7,
+ 0x8A, 0x09, 0x06, 0xE3, 0x81, 0x86, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0x8B, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0x8D, 0xE3, 0x82,
+ // Bytes 3f00 - 3f3f
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0x8F, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0x91, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0x93, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0x95, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0x97, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0x99, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0x9B, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0x9D, 0xE3, 0x82,
+ // Bytes 3f40 - 3f7f
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0x9F, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0xA1, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0xA4, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0xA6, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0xA8, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0xAF, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0xAF, 0xE3, 0x82,
+ 0x9A, 0x08, 0x06, 0xE3, 0x81, 0xB2, 0xE3, 0x82,
+ // Bytes 3f80 - 3fbf
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0xB2, 0xE3, 0x82,
+ 0x9A, 0x08, 0x06, 0xE3, 0x81, 0xB5, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0xB5, 0xE3, 0x82,
+ 0x9A, 0x08, 0x06, 0xE3, 0x81, 0xB8, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0xB8, 0xE3, 0x82,
+ 0x9A, 0x08, 0x06, 0xE3, 0x81, 0xBB, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x81, 0xBB, 0xE3, 0x82,
+ 0x9A, 0x08, 0x06, 0xE3, 0x82, 0x9D, 0xE3, 0x82,
+ // Bytes 3fc0 - 3fff
+ 0x99, 0x08, 0x06, 0xE3, 0x82, 0xA6, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x82, 0xAB, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x82, 0xAD, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x82, 0xAF, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x82, 0xB1, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x82, 0xB3, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x82, 0xB5, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x82, 0xB7, 0xE3, 0x82,
+ // Bytes 4000 - 403f
+ 0x99, 0x08, 0x06, 0xE3, 0x82, 0xB9, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x82, 0xBB, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x82, 0xBD, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x82, 0xBF, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0x81, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0x84, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0x86, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0x88, 0xE3, 0x82,
+ // Bytes 4040 - 407f
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0x8F, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0x8F, 0xE3, 0x82,
+ 0x9A, 0x08, 0x06, 0xE3, 0x83, 0x92, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0x92, 0xE3, 0x82,
+ 0x9A, 0x08, 0x06, 0xE3, 0x83, 0x95, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0x95, 0xE3, 0x82,
+ 0x9A, 0x08, 0x06, 0xE3, 0x83, 0x98, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0x98, 0xE3, 0x82,
+ // Bytes 4080 - 40bf
+ 0x9A, 0x08, 0x06, 0xE3, 0x83, 0x9B, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0x9B, 0xE3, 0x82,
+ 0x9A, 0x08, 0x06, 0xE3, 0x83, 0xAF, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0xB0, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0xB1, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0xB2, 0xE3, 0x82,
+ 0x99, 0x08, 0x06, 0xE3, 0x83, 0xBD, 0xE3, 0x82,
+ 0x99, 0x08, 0x08, 0xCE, 0x91, 0xCC, 0x93, 0xCC,
+ // Bytes 40c0 - 40ff
+ 0x80, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0x91, 0xCC,
+ 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xF0, 0x08, 0xCE,
+ 0x91, 0xCC, 0x93, 0xCD, 0x82, 0xCD, 0x85, 0xF0,
+ 0x08, 0xCE, 0x91, 0xCC, 0x94, 0xCC, 0x80, 0xCD,
+ 0x85, 0xF0, 0x08, 0xCE, 0x91, 0xCC, 0x94, 0xCC,
+ 0x81, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0x91, 0xCC,
+ 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xF0, 0x08, 0xCE,
+ 0x97, 0xCC, 0x93, 0xCC, 0x80, 0xCD, 0x85, 0xF0,
+ // Bytes 4100 - 413f
+ 0x08, 0xCE, 0x97, 0xCC, 0x93, 0xCC, 0x81, 0xCD,
+ 0x85, 0xF0, 0x08, 0xCE, 0x97, 0xCC, 0x93, 0xCD,
+ 0x82, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0x97, 0xCC,
+ 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xF0, 0x08, 0xCE,
+ 0x97, 0xCC, 0x94, 0xCC, 0x81, 0xCD, 0x85, 0xF0,
+ 0x08, 0xCE, 0x97, 0xCC, 0x94, 0xCD, 0x82, 0xCD,
+ 0x85, 0xF0, 0x08, 0xCE, 0xA9, 0xCC, 0x93, 0xCC,
+ 0x80, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0xA9, 0xCC,
+ // Bytes 4140 - 417f
+ 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xF0, 0x08, 0xCE,
+ 0xA9, 0xCC, 0x93, 0xCD, 0x82, 0xCD, 0x85, 0xF0,
+ 0x08, 0xCE, 0xA9, 0xCC, 0x94, 0xCC, 0x80, 0xCD,
+ 0x85, 0xF0, 0x08, 0xCE, 0xA9, 0xCC, 0x94, 0xCC,
+ 0x81, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0xA9, 0xCC,
+ 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xF0, 0x08, 0xCE,
+ 0xB1, 0xCC, 0x93, 0xCC, 0x80, 0xCD, 0x85, 0xF0,
+ 0x08, 0xCE, 0xB1, 0xCC, 0x93, 0xCC, 0x81, 0xCD,
+ // Bytes 4180 - 41bf
+ 0x85, 0xF0, 0x08, 0xCE, 0xB1, 0xCC, 0x93, 0xCD,
+ 0x82, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0xB1, 0xCC,
+ 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xF0, 0x08, 0xCE,
+ 0xB1, 0xCC, 0x94, 0xCC, 0x81, 0xCD, 0x85, 0xF0,
+ 0x08, 0xCE, 0xB1, 0xCC, 0x94, 0xCD, 0x82, 0xCD,
+ 0x85, 0xF0, 0x08, 0xCE, 0xB7, 0xCC, 0x93, 0xCC,
+ 0x80, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0xB7, 0xCC,
+ 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xF0, 0x08, 0xCE,
+ // Bytes 41c0 - 41ff
+ 0xB7, 0xCC, 0x93, 0xCD, 0x82, 0xCD, 0x85, 0xF0,
+ 0x08, 0xCE, 0xB7, 0xCC, 0x94, 0xCC, 0x80, 0xCD,
+ 0x85, 0xF0, 0x08, 0xCE, 0xB7, 0xCC, 0x94, 0xCC,
+ 0x81, 0xCD, 0x85, 0xF0, 0x08, 0xCE, 0xB7, 0xCC,
+ 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xF0, 0x08, 0xCF,
+ 0x89, 0xCC, 0x93, 0xCC, 0x80, 0xCD, 0x85, 0xF0,
+ 0x08, 0xCF, 0x89, 0xCC, 0x93, 0xCC, 0x81, 0xCD,
+ 0x85, 0xF0, 0x08, 0xCF, 0x89, 0xCC, 0x93, 0xCD,
+ // Bytes 4200 - 423f
+ 0x82, 0xCD, 0x85, 0xF0, 0x08, 0xCF, 0x89, 0xCC,
+ 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xF0, 0x08, 0xCF,
+ 0x89, 0xCC, 0x94, 0xCC, 0x81, 0xCD, 0x85, 0xF0,
+ 0x08, 0xCF, 0x89, 0xCC, 0x94, 0xCD, 0x82, 0xCD,
+ 0x85, 0xF0, 0x08, 0xF0, 0x91, 0x82, 0x99, 0xF0,
+ 0x91, 0x82, 0xBA, 0x07, 0x08, 0xF0, 0x91, 0x82,
+ 0x9B, 0xF0, 0x91, 0x82, 0xBA, 0x07, 0x08, 0xF0,
+ 0x91, 0x82, 0xA5, 0xF0, 0x91, 0x82, 0xBA, 0x07,
+ // Bytes 4240 - 427f
+ 0x43, 0x20, 0xCC, 0x81, 0xE6, 0x43, 0x20, 0xCC,
+ 0x83, 0xE6, 0x43, 0x20, 0xCC, 0x84, 0xE6, 0x43,
+ 0x20, 0xCC, 0x85, 0xE6, 0x43, 0x20, 0xCC, 0x86,
+ 0xE6, 0x43, 0x20, 0xCC, 0x87, 0xE6, 0x43, 0x20,
+ 0xCC, 0x88, 0xE6, 0x43, 0x20, 0xCC, 0x8A, 0xE6,
+ 0x43, 0x20, 0xCC, 0x8B, 0xE6, 0x43, 0x20, 0xCC,
+ 0x93, 0xE6, 0x43, 0x20, 0xCC, 0x94, 0xE6, 0x43,
+ 0x20, 0xCC, 0xA7, 0xCA, 0x43, 0x20, 0xCC, 0xA8,
+ // Bytes 4280 - 42bf
+ 0xCA, 0x43, 0x20, 0xCC, 0xB3, 0xDC, 0x43, 0x20,
+ 0xCD, 0x82, 0xE6, 0x43, 0x20, 0xCD, 0x85, 0xF0,
+ 0x43, 0x20, 0xD9, 0x8B, 0x1B, 0x43, 0x20, 0xD9,
+ 0x8C, 0x1C, 0x43, 0x20, 0xD9, 0x8D, 0x1D, 0x43,
+ 0x20, 0xD9, 0x8E, 0x1E, 0x43, 0x20, 0xD9, 0x8F,
+ 0x1F, 0x43, 0x20, 0xD9, 0x90, 0x20, 0x43, 0x20,
+ 0xD9, 0x91, 0x21, 0x43, 0x20, 0xD9, 0x92, 0x22,
+ 0x43, 0x41, 0xCC, 0x8A, 0xE6, 0x43, 0x73, 0xCC,
+ // Bytes 42c0 - 42ff
+ 0x87, 0xE6, 0x44, 0x20, 0xE3, 0x82, 0x99, 0x08,
+ 0x44, 0x20, 0xE3, 0x82, 0x9A, 0x08, 0x44, 0xC2,
+ 0xA8, 0xCC, 0x81, 0xE6, 0x44, 0xCE, 0x91, 0xCC,
+ 0x81, 0xE6, 0x44, 0xCE, 0x95, 0xCC, 0x81, 0xE6,
+ 0x44, 0xCE, 0x97, 0xCC, 0x81, 0xE6, 0x44, 0xCE,
+ 0x99, 0xCC, 0x81, 0xE6, 0x44, 0xCE, 0x9F, 0xCC,
+ 0x81, 0xE6, 0x44, 0xCE, 0xA5, 0xCC, 0x81, 0xE6,
+ 0x44, 0xCE, 0xA5, 0xCC, 0x88, 0xE6, 0x44, 0xCE,
+ // Bytes 4300 - 433f
+ 0xA9, 0xCC, 0x81, 0xE6, 0x44, 0xCE, 0xB1, 0xCC,
+ 0x81, 0xE6, 0x44, 0xCE, 0xB5, 0xCC, 0x81, 0xE6,
+ 0x44, 0xCE, 0xB7, 0xCC, 0x81, 0xE6, 0x44, 0xCE,
+ 0xB9, 0xCC, 0x81, 0xE6, 0x44, 0xCE, 0xBF, 0xCC,
+ 0x81, 0xE6, 0x44, 0xCF, 0x85, 0xCC, 0x81, 0xE6,
+ 0x44, 0xCF, 0x89, 0xCC, 0x81, 0xE6, 0x44, 0xD7,
+ 0x90, 0xD6, 0xB7, 0x11, 0x44, 0xD7, 0x90, 0xD6,
+ 0xB8, 0x12, 0x44, 0xD7, 0x90, 0xD6, 0xBC, 0x15,
+ // Bytes 4340 - 437f
+ 0x44, 0xD7, 0x91, 0xD6, 0xBC, 0x15, 0x44, 0xD7,
+ 0x91, 0xD6, 0xBF, 0x17, 0x44, 0xD7, 0x92, 0xD6,
+ 0xBC, 0x15, 0x44, 0xD7, 0x93, 0xD6, 0xBC, 0x15,
+ 0x44, 0xD7, 0x94, 0xD6, 0xBC, 0x15, 0x44, 0xD7,
+ 0x95, 0xD6, 0xB9, 0x13, 0x44, 0xD7, 0x95, 0xD6,
+ 0xBC, 0x15, 0x44, 0xD7, 0x96, 0xD6, 0xBC, 0x15,
+ 0x44, 0xD7, 0x98, 0xD6, 0xBC, 0x15, 0x44, 0xD7,
+ 0x99, 0xD6, 0xB4, 0x0E, 0x44, 0xD7, 0x99, 0xD6,
+ // Bytes 4380 - 43bf
+ 0xBC, 0x15, 0x44, 0xD7, 0x9A, 0xD6, 0xBC, 0x15,
+ 0x44, 0xD7, 0x9B, 0xD6, 0xBC, 0x15, 0x44, 0xD7,
+ 0x9B, 0xD6, 0xBF, 0x17, 0x44, 0xD7, 0x9C, 0xD6,
+ 0xBC, 0x15, 0x44, 0xD7, 0x9E, 0xD6, 0xBC, 0x15,
+ 0x44, 0xD7, 0xA0, 0xD6, 0xBC, 0x15, 0x44, 0xD7,
+ 0xA1, 0xD6, 0xBC, 0x15, 0x44, 0xD7, 0xA3, 0xD6,
+ 0xBC, 0x15, 0x44, 0xD7, 0xA4, 0xD6, 0xBC, 0x15,
+ 0x44, 0xD7, 0xA4, 0xD6, 0xBF, 0x17, 0x44, 0xD7,
+ // Bytes 43c0 - 43ff
+ 0xA6, 0xD6, 0xBC, 0x15, 0x44, 0xD7, 0xA7, 0xD6,
+ 0xBC, 0x15, 0x44, 0xD7, 0xA8, 0xD6, 0xBC, 0x15,
+ 0x44, 0xD7, 0xA9, 0xD6, 0xBC, 0x15, 0x44, 0xD7,
+ 0xA9, 0xD7, 0x81, 0x18, 0x44, 0xD7, 0xA9, 0xD7,
+ 0x82, 0x19, 0x44, 0xD7, 0xAA, 0xD6, 0xBC, 0x15,
+ 0x44, 0xD7, 0xB2, 0xD6, 0xB7, 0x11, 0x44, 0xD8,
+ 0xA7, 0xD9, 0x8B, 0x1B, 0x44, 0xD8, 0xA7, 0xD9,
+ 0x93, 0xE6, 0x44, 0xD8, 0xA7, 0xD9, 0x94, 0xE6,
+ // Bytes 4400 - 443f
+ 0x44, 0xD8, 0xA7, 0xD9, 0x95, 0xDC, 0x44, 0xD8,
+ 0xB0, 0xD9, 0xB0, 0x23, 0x44, 0xD8, 0xB1, 0xD9,
+ 0xB0, 0x23, 0x44, 0xD9, 0x80, 0xD9, 0x8B, 0x1B,
+ 0x44, 0xD9, 0x80, 0xD9, 0x8E, 0x1E, 0x44, 0xD9,
+ 0x80, 0xD9, 0x8F, 0x1F, 0x44, 0xD9, 0x80, 0xD9,
+ 0x90, 0x20, 0x44, 0xD9, 0x80, 0xD9, 0x91, 0x21,
+ 0x44, 0xD9, 0x80, 0xD9, 0x92, 0x22, 0x44, 0xD9,
+ 0x87, 0xD9, 0xB0, 0x23, 0x44, 0xD9, 0x88, 0xD9,
+ // Bytes 4440 - 447f
+ 0x94, 0xE6, 0x44, 0xD9, 0x89, 0xD9, 0xB0, 0x23,
+ 0x44, 0xD9, 0x8A, 0xD9, 0x94, 0xE6, 0x44, 0xDB,
+ 0x92, 0xD9, 0x94, 0xE6, 0x44, 0xDB, 0x95, 0xD9,
+ 0x94, 0xE6, 0x45, 0x20, 0xCC, 0x88, 0xCC, 0x80,
+ 0xE6, 0x45, 0x20, 0xCC, 0x88, 0xCC, 0x81, 0xE6,
+ 0x45, 0x20, 0xCC, 0x88, 0xCD, 0x82, 0xE6, 0x45,
+ 0x20, 0xCC, 0x93, 0xCC, 0x80, 0xE6, 0x45, 0x20,
+ 0xCC, 0x93, 0xCC, 0x81, 0xE6, 0x45, 0x20, 0xCC,
+ // Bytes 4480 - 44bf
+ 0x93, 0xCD, 0x82, 0xE6, 0x45, 0x20, 0xCC, 0x94,
+ 0xCC, 0x80, 0xE6, 0x45, 0x20, 0xCC, 0x94, 0xCC,
+ 0x81, 0xE6, 0x45, 0x20, 0xCC, 0x94, 0xCD, 0x82,
+ 0xE6, 0x45, 0x20, 0xD9, 0x8C, 0xD9, 0x91, 0x21,
+ 0x45, 0x20, 0xD9, 0x8D, 0xD9, 0x91, 0x21, 0x45,
+ 0x20, 0xD9, 0x8E, 0xD9, 0x91, 0x21, 0x45, 0x20,
+ 0xD9, 0x8F, 0xD9, 0x91, 0x21, 0x45, 0x20, 0xD9,
+ 0x90, 0xD9, 0x91, 0x21, 0x45, 0x20, 0xD9, 0x91,
+ // Bytes 44c0 - 44ff
+ 0xD9, 0xB0, 0x23, 0x45, 0xE2, 0xAB, 0x9D, 0xCC,
+ 0xB8, 0x01, 0x46, 0xCE, 0xB9, 0xCC, 0x88, 0xCC,
+ 0x81, 0xE6, 0x46, 0xCF, 0x85, 0xCC, 0x88, 0xCC,
+ 0x81, 0xE6, 0x46, 0xD7, 0xA9, 0xD6, 0xBC, 0xD7,
+ 0x81, 0x18, 0x46, 0xD7, 0xA9, 0xD6, 0xBC, 0xD7,
+ 0x82, 0x19, 0x46, 0xD9, 0x80, 0xD9, 0x8E, 0xD9,
+ 0x91, 0x21, 0x46, 0xD9, 0x80, 0xD9, 0x8F, 0xD9,
+ 0x91, 0x21, 0x46, 0xD9, 0x80, 0xD9, 0x90, 0xD9,
+ // Bytes 4500 - 453f
+ 0x91, 0x21, 0x46, 0xE0, 0xA4, 0x95, 0xE0, 0xA4,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0x96, 0xE0, 0xA4,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0x97, 0xE0, 0xA4,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0x9C, 0xE0, 0xA4,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0xA1, 0xE0, 0xA4,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0xA2, 0xE0, 0xA4,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0xAB, 0xE0, 0xA4,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA4, 0xAF, 0xE0, 0xA4,
+ // Bytes 4540 - 457f
+ 0xBC, 0x07, 0x46, 0xE0, 0xA6, 0xA1, 0xE0, 0xA6,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA6, 0xA2, 0xE0, 0xA6,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA6, 0xAF, 0xE0, 0xA6,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA8, 0x96, 0xE0, 0xA8,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA8, 0x97, 0xE0, 0xA8,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA8, 0x9C, 0xE0, 0xA8,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA8, 0xAB, 0xE0, 0xA8,
+ 0xBC, 0x07, 0x46, 0xE0, 0xA8, 0xB2, 0xE0, 0xA8,
+ // Bytes 4580 - 45bf
+ 0xBC, 0x07, 0x46, 0xE0, 0xA8, 0xB8, 0xE0, 0xA8,
+ 0xBC, 0x07, 0x46, 0xE0, 0xAC, 0xA1, 0xE0, 0xAC,
+ 0xBC, 0x07, 0x46, 0xE0, 0xAC, 0xA2, 0xE0, 0xAC,
+ 0xBC, 0x07, 0x46, 0xE0, 0xBE, 0xB2, 0xE0, 0xBE,
+ 0x80, 0x82, 0x46, 0xE0, 0xBE, 0xB3, 0xE0, 0xBE,
+ 0x80, 0x82, 0x46, 0xE3, 0x83, 0x86, 0xE3, 0x82,
+ 0x99, 0x08, 0x48, 0xF0, 0x9D, 0x85, 0x97, 0xF0,
+ 0x9D, 0x85, 0xA5, 0xD8, 0x48, 0xF0, 0x9D, 0x85,
+ // Bytes 45c0 - 45ff
+ 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xD8, 0x48, 0xF0,
+ 0x9D, 0x86, 0xB9, 0xF0, 0x9D, 0x85, 0xA5, 0xD8,
+ 0x48, 0xF0, 0x9D, 0x86, 0xBA, 0xF0, 0x9D, 0x85,
+ 0xA5, 0xD8, 0x49, 0xE0, 0xBE, 0xB2, 0xE0, 0xBD,
+ 0xB1, 0xE0, 0xBE, 0x80, 0x82, 0x49, 0xE0, 0xBE,
+ 0xB3, 0xE0, 0xBD, 0xB1, 0xE0, 0xBE, 0x80, 0x82,
+ 0x4C, 0xF0, 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85,
+ 0xA5, 0xF0, 0x9D, 0x85, 0xAE, 0xD8, 0x4C, 0xF0,
+ // Bytes 4600 - 463f
+ 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0,
+ 0x9D, 0x85, 0xAF, 0xD8, 0x4C, 0xF0, 0x9D, 0x85,
+ 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85,
+ 0xB0, 0xD8, 0x4C, 0xF0, 0x9D, 0x85, 0x98, 0xF0,
+ 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xB1, 0xD8,
+ 0x4C, 0xF0, 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85,
+ 0xA5, 0xF0, 0x9D, 0x85, 0xB2, 0xD8, 0x4C, 0xF0,
+ 0x9D, 0x86, 0xB9, 0xF0, 0x9D, 0x85, 0xA5, 0xF0,
+ // Bytes 4640 - 467f
+ 0x9D, 0x85, 0xAE, 0xD8, 0x4C, 0xF0, 0x9D, 0x86,
+ 0xB9, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85,
+ 0xAF, 0xD8, 0x4C, 0xF0, 0x9D, 0x86, 0xBA, 0xF0,
+ 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xAE, 0xD8,
+ 0x4C, 0xF0, 0x9D, 0x86, 0xBA, 0xF0, 0x9D, 0x85,
+ 0xA5, 0xF0, 0x9D, 0x85, 0xAF, 0xD8, 0x83, 0x41,
+ 0xCC, 0x82, 0xE6, 0x83, 0x41, 0xCC, 0x86, 0xE6,
+ 0x83, 0x41, 0xCC, 0x87, 0xE6, 0x83, 0x41, 0xCC,
+ // Bytes 4680 - 46bf
+ 0x88, 0xE6, 0x83, 0x41, 0xCC, 0x8A, 0xE6, 0x83,
+ 0x41, 0xCC, 0xA3, 0xDC, 0x83, 0x43, 0xCC, 0xA7,
+ 0xCA, 0x83, 0x45, 0xCC, 0x82, 0xE6, 0x83, 0x45,
+ 0xCC, 0x84, 0xE6, 0x83, 0x45, 0xCC, 0xA3, 0xDC,
+ 0x83, 0x45, 0xCC, 0xA7, 0xCA, 0x83, 0x49, 0xCC,
+ 0x88, 0xE6, 0x83, 0x4C, 0xCC, 0xA3, 0xDC, 0x83,
+ 0x4F, 0xCC, 0x82, 0xE6, 0x83, 0x4F, 0xCC, 0x83,
+ 0xE6, 0x83, 0x4F, 0xCC, 0x84, 0xE6, 0x83, 0x4F,
+ // Bytes 46c0 - 46ff
+ 0xCC, 0x87, 0xE6, 0x83, 0x4F, 0xCC, 0x88, 0xE6,
+ 0x83, 0x4F, 0xCC, 0x9B, 0xD8, 0x83, 0x4F, 0xCC,
+ 0xA3, 0xDC, 0x83, 0x4F, 0xCC, 0xA8, 0xCA, 0x83,
+ 0x52, 0xCC, 0xA3, 0xDC, 0x83, 0x53, 0xCC, 0x81,
+ 0xE6, 0x83, 0x53, 0xCC, 0x8C, 0xE6, 0x83, 0x53,
+ 0xCC, 0xA3, 0xDC, 0x83, 0x55, 0xCC, 0x83, 0xE6,
+ 0x83, 0x55, 0xCC, 0x84, 0xE6, 0x83, 0x55, 0xCC,
+ 0x88, 0xE6, 0x83, 0x55, 0xCC, 0x9B, 0xD8, 0x83,
+ // Bytes 4700 - 473f
+ 0x61, 0xCC, 0x82, 0xE6, 0x83, 0x61, 0xCC, 0x86,
+ 0xE6, 0x83, 0x61, 0xCC, 0x87, 0xE6, 0x83, 0x61,
+ 0xCC, 0x88, 0xE6, 0x83, 0x61, 0xCC, 0x8A, 0xE6,
+ 0x83, 0x61, 0xCC, 0xA3, 0xDC, 0x83, 0x63, 0xCC,
+ 0xA7, 0xCA, 0x83, 0x65, 0xCC, 0x82, 0xE6, 0x83,
+ 0x65, 0xCC, 0x84, 0xE6, 0x83, 0x65, 0xCC, 0xA3,
+ 0xDC, 0x83, 0x65, 0xCC, 0xA7, 0xCA, 0x83, 0x69,
+ 0xCC, 0x88, 0xE6, 0x83, 0x6C, 0xCC, 0xA3, 0xDC,
+ // Bytes 4740 - 477f
+ 0x83, 0x6F, 0xCC, 0x82, 0xE6, 0x83, 0x6F, 0xCC,
+ 0x83, 0xE6, 0x83, 0x6F, 0xCC, 0x84, 0xE6, 0x83,
+ 0x6F, 0xCC, 0x87, 0xE6, 0x83, 0x6F, 0xCC, 0x88,
+ 0xE6, 0x83, 0x6F, 0xCC, 0x9B, 0xD8, 0x83, 0x6F,
+ 0xCC, 0xA3, 0xDC, 0x83, 0x6F, 0xCC, 0xA8, 0xCA,
+ 0x83, 0x72, 0xCC, 0xA3, 0xDC, 0x83, 0x73, 0xCC,
+ 0x81, 0xE6, 0x83, 0x73, 0xCC, 0x8C, 0xE6, 0x83,
+ 0x73, 0xCC, 0xA3, 0xDC, 0x83, 0x75, 0xCC, 0x83,
+ // Bytes 4780 - 47bf
+ 0xE6, 0x83, 0x75, 0xCC, 0x84, 0xE6, 0x83, 0x75,
+ 0xCC, 0x88, 0xE6, 0x83, 0x75, 0xCC, 0x9B, 0xD8,
+ 0x84, 0xCE, 0x91, 0xCC, 0x93, 0xE6, 0x84, 0xCE,
+ 0x91, 0xCC, 0x94, 0xE6, 0x84, 0xCE, 0x95, 0xCC,
+ 0x93, 0xE6, 0x84, 0xCE, 0x95, 0xCC, 0x94, 0xE6,
+ 0x84, 0xCE, 0x97, 0xCC, 0x93, 0xE6, 0x84, 0xCE,
+ 0x97, 0xCC, 0x94, 0xE6, 0x84, 0xCE, 0x99, 0xCC,
+ 0x93, 0xE6, 0x84, 0xCE, 0x99, 0xCC, 0x94, 0xE6,
+ // Bytes 47c0 - 47ff
+ 0x84, 0xCE, 0x9F, 0xCC, 0x93, 0xE6, 0x84, 0xCE,
+ 0x9F, 0xCC, 0x94, 0xE6, 0x84, 0xCE, 0xA5, 0xCC,
+ 0x94, 0xE6, 0x84, 0xCE, 0xA9, 0xCC, 0x93, 0xE6,
+ 0x84, 0xCE, 0xA9, 0xCC, 0x94, 0xE6, 0x84, 0xCE,
+ 0xB1, 0xCC, 0x80, 0xE6, 0x84, 0xCE, 0xB1, 0xCC,
+ 0x81, 0xE6, 0x84, 0xCE, 0xB1, 0xCC, 0x93, 0xE6,
+ 0x84, 0xCE, 0xB1, 0xCC, 0x94, 0xE6, 0x84, 0xCE,
+ 0xB1, 0xCD, 0x82, 0xE6, 0x84, 0xCE, 0xB5, 0xCC,
+ // Bytes 4800 - 483f
+ 0x93, 0xE6, 0x84, 0xCE, 0xB5, 0xCC, 0x94, 0xE6,
+ 0x84, 0xCE, 0xB7, 0xCC, 0x80, 0xE6, 0x84, 0xCE,
+ 0xB7, 0xCC, 0x81, 0xE6, 0x84, 0xCE, 0xB7, 0xCC,
+ 0x93, 0xE6, 0x84, 0xCE, 0xB7, 0xCC, 0x94, 0xE6,
+ 0x84, 0xCE, 0xB7, 0xCD, 0x82, 0xE6, 0x84, 0xCE,
+ 0xB9, 0xCC, 0x88, 0xE6, 0x84, 0xCE, 0xB9, 0xCC,
+ 0x93, 0xE6, 0x84, 0xCE, 0xB9, 0xCC, 0x94, 0xE6,
+ 0x84, 0xCE, 0xBF, 0xCC, 0x93, 0xE6, 0x84, 0xCE,
+ // Bytes 4840 - 487f
+ 0xBF, 0xCC, 0x94, 0xE6, 0x84, 0xCF, 0x85, 0xCC,
+ 0x88, 0xE6, 0x84, 0xCF, 0x85, 0xCC, 0x93, 0xE6,
+ 0x84, 0xCF, 0x85, 0xCC, 0x94, 0xE6, 0x84, 0xCF,
+ 0x89, 0xCC, 0x80, 0xE6, 0x84, 0xCF, 0x89, 0xCC,
+ 0x81, 0xE6, 0x84, 0xCF, 0x89, 0xCC, 0x93, 0xE6,
+ 0x84, 0xCF, 0x89, 0xCC, 0x94, 0xE6, 0x84, 0xCF,
+ 0x89, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0x91, 0xCC,
+ 0x93, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0x91, 0xCC,
+ // Bytes 4880 - 48bf
+ 0x93, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0x91, 0xCC,
+ 0x93, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0x91, 0xCC,
+ 0x94, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0x91, 0xCC,
+ 0x94, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0x91, 0xCC,
+ 0x94, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0x97, 0xCC,
+ 0x93, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0x97, 0xCC,
+ 0x93, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0x97, 0xCC,
+ 0x93, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0x97, 0xCC,
+ // Bytes 48c0 - 48ff
+ 0x94, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0x97, 0xCC,
+ 0x94, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0x97, 0xCC,
+ 0x94, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0xA9, 0xCC,
+ 0x93, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0xA9, 0xCC,
+ 0x93, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0xA9, 0xCC,
+ 0x93, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0xA9, 0xCC,
+ 0x94, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0xA9, 0xCC,
+ 0x94, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0xA9, 0xCC,
+ // Bytes 4900 - 493f
+ 0x94, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0xB1, 0xCC,
+ 0x93, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0xB1, 0xCC,
+ 0x93, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0xB1, 0xCC,
+ 0x93, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0xB1, 0xCC,
+ 0x94, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0xB1, 0xCC,
+ 0x94, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0xB1, 0xCC,
+ 0x94, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0xB7, 0xCC,
+ 0x93, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0xB7, 0xCC,
+ // Bytes 4940 - 497f
+ 0x93, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0xB7, 0xCC,
+ 0x93, 0xCD, 0x82, 0xE6, 0x86, 0xCE, 0xB7, 0xCC,
+ 0x94, 0xCC, 0x80, 0xE6, 0x86, 0xCE, 0xB7, 0xCC,
+ 0x94, 0xCC, 0x81, 0xE6, 0x86, 0xCE, 0xB7, 0xCC,
+ 0x94, 0xCD, 0x82, 0xE6, 0x86, 0xCF, 0x89, 0xCC,
+ 0x93, 0xCC, 0x80, 0xE6, 0x86, 0xCF, 0x89, 0xCC,
+ 0x93, 0xCC, 0x81, 0xE6, 0x86, 0xCF, 0x89, 0xCC,
+ 0x93, 0xCD, 0x82, 0xE6, 0x86, 0xCF, 0x89, 0xCC,
+ // Bytes 4980 - 49bf
+ 0x94, 0xCC, 0x80, 0xE6, 0x86, 0xCF, 0x89, 0xCC,
+ 0x94, 0xCC, 0x81, 0xE6, 0x86, 0xCF, 0x89, 0xCC,
+ 0x94, 0xCD, 0x82, 0xE6, 0x42, 0xCC, 0x80, 0xE6,
+ 0xE6, 0x42, 0xCC, 0x81, 0xE6, 0xE6, 0x42, 0xCC,
+ 0x93, 0xE6, 0xE6, 0x44, 0xCC, 0x88, 0xCC, 0x81,
+ 0xE6, 0xE6, 0x43, 0xE3, 0x82, 0x99, 0x08, 0x08,
+ 0x43, 0xE3, 0x82, 0x9A, 0x08, 0x08, 0x46, 0xE0,
+ 0xBD, 0xB1, 0xE0, 0xBD, 0xB2, 0x82, 0x81, 0x46,
+ // Bytes 49c0 - 49ff
+ 0xE0, 0xBD, 0xB1, 0xE0, 0xBD, 0xB4, 0x84, 0x81,
+ 0x46, 0xE0, 0xBD, 0xB1, 0xE0, 0xBE, 0x80, 0x82,
+ 0x81,
+}
+
+// nfcValues: 2944 entries, 5888 bytes
+// Block 2 is the null block.
+var nfcValues = [2944]uint16{
+ // Block 0x0, offset 0x0
+ 0x003c: 0x8800, 0x003d: 0x8800, 0x003e: 0x8800,
+ // Block 0x1, offset 0x40
+ 0x0041: 0x8800, 0x0042: 0x8800, 0x0043: 0x8800, 0x0044: 0x8800, 0x0045: 0x8800,
+ 0x0046: 0x8800, 0x0047: 0x8800, 0x0048: 0x8800, 0x0049: 0x8800, 0x004a: 0x8800, 0x004b: 0x8800,
+ 0x004c: 0x8800, 0x004d: 0x8800, 0x004e: 0x8800, 0x004f: 0x8800, 0x0050: 0x8800,
+ 0x0052: 0x8800, 0x0053: 0x8800, 0x0054: 0x8800, 0x0055: 0x8800, 0x0056: 0x8800, 0x0057: 0x8800,
+ 0x0058: 0x8800, 0x0059: 0x8800, 0x005a: 0x8800,
+ 0x0061: 0x8800, 0x0062: 0x8800, 0x0063: 0x8800,
+ 0x0064: 0x8800, 0x0065: 0x8800, 0x0066: 0x8800, 0x0067: 0x8800, 0x0068: 0x8800, 0x0069: 0x8800,
+ 0x006a: 0x8800, 0x006b: 0x8800, 0x006c: 0x8800, 0x006d: 0x8800, 0x006e: 0x8800, 0x006f: 0x8800,
+ 0x0070: 0x8800, 0x0072: 0x8800, 0x0073: 0x8800, 0x0074: 0x8800, 0x0075: 0x8800,
+ 0x0076: 0x8800, 0x0077: 0x8800, 0x0078: 0x8800, 0x0079: 0x8800, 0x007a: 0x8800,
+ // Block 0x2, offset 0x80
+ // Block 0x3, offset 0xc0
+ 0x00c0: 0x2f59, 0x00c1: 0x2f5e, 0x00c2: 0x466e, 0x00c3: 0x2f63, 0x00c4: 0x467d, 0x00c5: 0x4682,
+ 0x00c6: 0x8800, 0x00c7: 0x468c, 0x00c8: 0x2fcc, 0x00c9: 0x2fd1, 0x00ca: 0x4691, 0x00cb: 0x2fe5,
+ 0x00cc: 0x3058, 0x00cd: 0x305d, 0x00ce: 0x3062, 0x00cf: 0x46a5, 0x00d1: 0x30ee,
+ 0x00d2: 0x3111, 0x00d3: 0x3116, 0x00d4: 0x46af, 0x00d5: 0x46b4, 0x00d6: 0x46c3,
+ 0x00d8: 0x8800, 0x00d9: 0x319d, 0x00da: 0x31a2, 0x00db: 0x31a7, 0x00dc: 0x46f5, 0x00dd: 0x321f,
+ 0x00e0: 0x3265, 0x00e1: 0x326a, 0x00e2: 0x46ff, 0x00e3: 0x326f,
+ 0x00e4: 0x470e, 0x00e5: 0x4713, 0x00e6: 0x8800, 0x00e7: 0x471d, 0x00e8: 0x32d8, 0x00e9: 0x32dd,
+ 0x00ea: 0x4722, 0x00eb: 0x32f1, 0x00ec: 0x3369, 0x00ed: 0x336e, 0x00ee: 0x3373, 0x00ef: 0x4736,
+ 0x00f1: 0x33ff, 0x00f2: 0x3422, 0x00f3: 0x3427, 0x00f4: 0x4740, 0x00f5: 0x4745,
+ 0x00f6: 0x4754, 0x00f8: 0x8800, 0x00f9: 0x34b3, 0x00fa: 0x34b8, 0x00fb: 0x34bd,
+ 0x00fc: 0x4786, 0x00fd: 0x353a, 0x00ff: 0x3553,
+ // Block 0x4, offset 0x100
+ 0x0100: 0x2f68, 0x0101: 0x3274, 0x0102: 0x4673, 0x0103: 0x4704, 0x0104: 0x2f86, 0x0105: 0x3292,
+ 0x0106: 0x2f9a, 0x0107: 0x32a6, 0x0108: 0x2f9f, 0x0109: 0x32ab, 0x010a: 0x2fa4, 0x010b: 0x32b0,
+ 0x010c: 0x2fa9, 0x010d: 0x32b5, 0x010e: 0x2fb3, 0x010f: 0x32bf,
+ 0x0112: 0x4696, 0x0113: 0x4727, 0x0114: 0x2fdb, 0x0115: 0x32e7, 0x0116: 0x2fe0, 0x0117: 0x32ec,
+ 0x0118: 0x2ffe, 0x0119: 0x330a, 0x011a: 0x2fef, 0x011b: 0x32fb, 0x011c: 0x3017, 0x011d: 0x3323,
+ 0x011e: 0x3021, 0x011f: 0x332d, 0x0120: 0x3026, 0x0121: 0x3332, 0x0122: 0x3030, 0x0123: 0x333c,
+ 0x0124: 0x3035, 0x0125: 0x3341, 0x0128: 0x3067, 0x0129: 0x3378,
+ 0x012a: 0x306c, 0x012b: 0x337d, 0x012c: 0x3071, 0x012d: 0x3382, 0x012e: 0x3094, 0x012f: 0x33a0,
+ 0x0130: 0x3076, 0x0134: 0x309e, 0x0135: 0x33aa,
+ 0x0136: 0x30b2, 0x0137: 0x33c3, 0x0139: 0x30bc, 0x013a: 0x33cd, 0x013b: 0x30c6,
+ 0x013c: 0x33d7, 0x013d: 0x30c1, 0x013e: 0x33d2,
+ // Block 0x5, offset 0x140
+ 0x0143: 0x30e9, 0x0144: 0x33fa, 0x0145: 0x3102,
+ 0x0146: 0x3413, 0x0147: 0x30f8, 0x0148: 0x3409,
+ 0x014c: 0x46b9, 0x014d: 0x474a, 0x014e: 0x311b, 0x014f: 0x342c, 0x0150: 0x3125, 0x0151: 0x3436,
+ 0x0154: 0x3143, 0x0155: 0x3454, 0x0156: 0x315c, 0x0157: 0x346d,
+ 0x0158: 0x314d, 0x0159: 0x345e, 0x015a: 0x46dc, 0x015b: 0x476d, 0x015c: 0x3166, 0x015d: 0x3477,
+ 0x015e: 0x3175, 0x015f: 0x3486, 0x0160: 0x46e1, 0x0161: 0x4772, 0x0162: 0x318e, 0x0163: 0x34a4,
+ 0x0164: 0x317f, 0x0165: 0x3495, 0x0168: 0x46eb, 0x0169: 0x477c,
+ 0x016a: 0x46f0, 0x016b: 0x4781, 0x016c: 0x31ac, 0x016d: 0x34c2, 0x016e: 0x31b6, 0x016f: 0x34cc,
+ 0x0170: 0x31bb, 0x0171: 0x34d1, 0x0172: 0x31d9, 0x0173: 0x34ef, 0x0174: 0x31fc, 0x0175: 0x3512,
+ 0x0176: 0x3224, 0x0177: 0x353f, 0x0178: 0x3238, 0x0179: 0x3247, 0x017a: 0x3567, 0x017b: 0x3251,
+ 0x017c: 0x3571, 0x017d: 0x3256, 0x017e: 0x3576, 0x017f: 0x8800,
+ // Block 0x6, offset 0x180
+ 0x018d: 0x2f72, 0x018e: 0x327e, 0x018f: 0x3080, 0x0190: 0x338c, 0x0191: 0x312a,
+ 0x0192: 0x343b, 0x0193: 0x31c0, 0x0194: 0x34d6, 0x0195: 0x39b9, 0x0196: 0x3b48, 0x0197: 0x39b2,
+ 0x0198: 0x3b41, 0x0199: 0x39c0, 0x019a: 0x3b4f, 0x019b: 0x39ab, 0x019c: 0x3b3a,
+ 0x019e: 0x389a, 0x019f: 0x3a29, 0x01a0: 0x3893, 0x01a1: 0x3a22, 0x01a2: 0x359d, 0x01a3: 0x35af,
+ 0x01a6: 0x302b, 0x01a7: 0x3337, 0x01a8: 0x30a8, 0x01a9: 0x33b9,
+ 0x01aa: 0x46d2, 0x01ab: 0x4763, 0x01ac: 0x397a, 0x01ad: 0x3b09, 0x01ae: 0x35c1, 0x01af: 0x35c7,
+ 0x01b0: 0x33af, 0x01b4: 0x3012, 0x01b5: 0x331e,
+ 0x01b8: 0x30e4, 0x01b9: 0x33f5, 0x01ba: 0x38a1, 0x01bb: 0x3a30,
+ 0x01bc: 0x3597, 0x01bd: 0x35a9, 0x01be: 0x35a3, 0x01bf: 0x35b5,
+ // Block 0x7, offset 0x1c0
+ 0x01c0: 0x2f77, 0x01c1: 0x3283, 0x01c2: 0x2f7c, 0x01c3: 0x3288, 0x01c4: 0x2ff4, 0x01c5: 0x3300,
+ 0x01c6: 0x2ff9, 0x01c7: 0x3305, 0x01c8: 0x3085, 0x01c9: 0x3391, 0x01ca: 0x308a, 0x01cb: 0x3396,
+ 0x01cc: 0x312f, 0x01cd: 0x3440, 0x01ce: 0x3134, 0x01cf: 0x3445, 0x01d0: 0x3152, 0x01d1: 0x3463,
+ 0x01d2: 0x3157, 0x01d3: 0x3468, 0x01d4: 0x31c5, 0x01d5: 0x34db, 0x01d6: 0x31ca, 0x01d7: 0x34e0,
+ 0x01d8: 0x3170, 0x01d9: 0x3481, 0x01da: 0x3189, 0x01db: 0x349f,
+ 0x01de: 0x3044, 0x01df: 0x3350,
+ 0x01e6: 0x4678, 0x01e7: 0x4709, 0x01e8: 0x46a0, 0x01e9: 0x4731,
+ 0x01ea: 0x3949, 0x01eb: 0x3ad8, 0x01ec: 0x3926, 0x01ed: 0x3ab5, 0x01ee: 0x46be, 0x01ef: 0x474f,
+ 0x01f0: 0x3942, 0x01f1: 0x3ad1, 0x01f2: 0x322e, 0x01f3: 0x3549,
+ // Block 0x8, offset 0x200
+ 0x0200: 0x86e6, 0x0201: 0x86e6, 0x0202: 0x86e6, 0x0203: 0x86e6, 0x0204: 0x86e6, 0x0205: 0x80e6,
+ 0x0206: 0x86e6, 0x0207: 0x86e6, 0x0208: 0x86e6, 0x0209: 0x86e6, 0x020a: 0x86e6, 0x020b: 0x86e6,
+ 0x020c: 0x86e6, 0x020d: 0x80e6, 0x020e: 0x80e6, 0x020f: 0x86e6, 0x0210: 0x80e6, 0x0211: 0x86e6,
+ 0x0212: 0x80e6, 0x0213: 0x86e6, 0x0214: 0x86e6, 0x0215: 0x80e8, 0x0216: 0x80dc, 0x0217: 0x80dc,
+ 0x0218: 0x80dc, 0x0219: 0x80dc, 0x021a: 0x80e8, 0x021b: 0x86d8, 0x021c: 0x80dc, 0x021d: 0x80dc,
+ 0x021e: 0x80dc, 0x021f: 0x80dc, 0x0220: 0x80dc, 0x0221: 0x80ca, 0x0222: 0x80ca, 0x0223: 0x86dc,
+ 0x0224: 0x86dc, 0x0225: 0x86dc, 0x0226: 0x86dc, 0x0227: 0x86ca, 0x0228: 0x86ca, 0x0229: 0x80dc,
+ 0x022a: 0x80dc, 0x022b: 0x80dc, 0x022c: 0x80dc, 0x022d: 0x86dc, 0x022e: 0x86dc, 0x022f: 0x80dc,
+ 0x0230: 0x86dc, 0x0231: 0x86dc, 0x0232: 0x80dc, 0x0233: 0x80dc, 0x0234: 0x8001, 0x0235: 0x8001,
+ 0x0236: 0x8001, 0x0237: 0x8001, 0x0238: 0x8601, 0x0239: 0x80dc, 0x023a: 0x80dc, 0x023b: 0x80dc,
+ 0x023c: 0x80dc, 0x023d: 0x80e6, 0x023e: 0x80e6, 0x023f: 0x80e6,
+ // Block 0x9, offset 0x240
+ 0x0240: 0x4994, 0x0241: 0x4999, 0x0242: 0x86e6, 0x0243: 0x499e, 0x0244: 0x49a3, 0x0245: 0x86f0,
+ 0x0246: 0x80e6, 0x0247: 0x80dc, 0x0248: 0x80dc, 0x0249: 0x80dc, 0x024a: 0x80e6, 0x024b: 0x80e6,
+ 0x024c: 0x80e6, 0x024d: 0x80dc, 0x024e: 0x80dc, 0x0250: 0x80e6, 0x0251: 0x80e6,
+ 0x0252: 0x80e6, 0x0253: 0x80dc, 0x0254: 0x80dc, 0x0255: 0x80dc, 0x0256: 0x80dc, 0x0257: 0x80e6,
+ 0x0258: 0x80e8, 0x0259: 0x80dc, 0x025a: 0x80dc, 0x025b: 0x80e6, 0x025c: 0x80e9, 0x025d: 0x80ea,
+ 0x025e: 0x80ea, 0x025f: 0x80e9, 0x0260: 0x80ea, 0x0261: 0x80ea, 0x0262: 0x80e9, 0x0263: 0x80e6,
+ 0x0264: 0x80e6, 0x0265: 0x80e6, 0x0266: 0x80e6, 0x0267: 0x80e6, 0x0268: 0x80e6, 0x0269: 0x80e6,
+ 0x026a: 0x80e6, 0x026b: 0x80e6, 0x026c: 0x80e6, 0x026d: 0x80e6, 0x026e: 0x80e6, 0x026f: 0x80e6,
+ 0x0274: 0x0170,
+ 0x027e: 0x0037,
+ // Block 0xa, offset 0x280
+ 0x0285: 0x358b,
+ 0x0286: 0x35d3, 0x0287: 0x00d1, 0x0288: 0x35f1, 0x0289: 0x35fd, 0x028a: 0x360f,
+ 0x028c: 0x362d, 0x028e: 0x363f, 0x028f: 0x365d, 0x0290: 0x3df2, 0x0291: 0x8800,
+ 0x0295: 0x8800, 0x0297: 0x8800,
+ 0x0299: 0x8800,
+ 0x029f: 0x8800, 0x02a1: 0x8800,
+ 0x02a5: 0x8800, 0x02a9: 0x8800,
+ 0x02aa: 0x3621, 0x02ab: 0x3651, 0x02ac: 0x47e4, 0x02ad: 0x3681, 0x02ae: 0x480e, 0x02af: 0x3693,
+ 0x02b0: 0x3e5a, 0x02b1: 0x8800, 0x02b5: 0x8800,
+ 0x02b7: 0x8800, 0x02b9: 0x8800,
+ 0x02bf: 0x8800,
+ // Block 0xb, offset 0x2c0
+ 0x02c0: 0x370b, 0x02c1: 0x3717, 0x02c3: 0x3705,
+ 0x02c6: 0x8800, 0x02c7: 0x36f3,
+ 0x02cc: 0x3747, 0x02cd: 0x372f, 0x02ce: 0x3759, 0x02d0: 0x8800,
+ 0x02d3: 0x8800, 0x02d5: 0x8800, 0x02d6: 0x8800, 0x02d7: 0x8800,
+ 0x02d8: 0x8800, 0x02d9: 0x373b, 0x02da: 0x8800,
+ 0x02de: 0x8800, 0x02e3: 0x8800,
+ 0x02e7: 0x8800,
+ 0x02eb: 0x8800, 0x02ed: 0x8800,
+ 0x02f0: 0x8800, 0x02f3: 0x8800, 0x02f5: 0x8800,
+ 0x02f6: 0x8800, 0x02f7: 0x8800, 0x02f8: 0x8800, 0x02f9: 0x37bf, 0x02fa: 0x8800,
+ 0x02fe: 0x8800,
+ // Block 0xc, offset 0x300
+ 0x0301: 0x371d, 0x0302: 0x37a1,
+ 0x0310: 0x36f9, 0x0311: 0x377d,
+ 0x0312: 0x36ff, 0x0313: 0x3783, 0x0316: 0x3711, 0x0317: 0x3795,
+ 0x0318: 0x8800, 0x0319: 0x8800, 0x031a: 0x3813, 0x031b: 0x3819, 0x031c: 0x3723, 0x031d: 0x37a7,
+ 0x031e: 0x3729, 0x031f: 0x37ad, 0x0322: 0x3735, 0x0323: 0x37b9,
+ 0x0324: 0x3741, 0x0325: 0x37c5, 0x0326: 0x374d, 0x0327: 0x37d1, 0x0328: 0x8800, 0x0329: 0x8800,
+ 0x032a: 0x381f, 0x032b: 0x3825, 0x032c: 0x3777, 0x032d: 0x37fb, 0x032e: 0x3753, 0x032f: 0x37d7,
+ 0x0330: 0x375f, 0x0331: 0x37e3, 0x0332: 0x3765, 0x0333: 0x37e9, 0x0334: 0x376b, 0x0335: 0x37ef,
+ 0x0338: 0x3771, 0x0339: 0x37f5,
+ // Block 0xd, offset 0x340
+ 0x0351: 0x80dc,
+ 0x0352: 0x80e6, 0x0353: 0x80e6, 0x0354: 0x80e6, 0x0355: 0x80e6, 0x0356: 0x80dc, 0x0357: 0x80e6,
+ 0x0358: 0x80e6, 0x0359: 0x80e6, 0x035a: 0x80de, 0x035b: 0x80dc, 0x035c: 0x80e6, 0x035d: 0x80e6,
+ 0x035e: 0x80e6, 0x035f: 0x80e6, 0x0360: 0x80e6, 0x0361: 0x80e6, 0x0362: 0x80dc, 0x0363: 0x80dc,
+ 0x0364: 0x80dc, 0x0365: 0x80dc, 0x0366: 0x80dc, 0x0367: 0x80dc, 0x0368: 0x80e6, 0x0369: 0x80e6,
+ 0x036a: 0x80dc, 0x036b: 0x80e6, 0x036c: 0x80e6, 0x036d: 0x80de, 0x036e: 0x80e4, 0x036f: 0x80e6,
+ 0x0370: 0x800a, 0x0371: 0x800b, 0x0372: 0x800c, 0x0373: 0x800d, 0x0374: 0x800e, 0x0375: 0x800f,
+ 0x0376: 0x8010, 0x0377: 0x8011, 0x0378: 0x8012, 0x0379: 0x8013, 0x037a: 0x8013, 0x037b: 0x8014,
+ 0x037c: 0x8015, 0x037d: 0x8016, 0x037f: 0x8017,
+ // Block 0xe, offset 0x380
+ 0x0388: 0x8800, 0x038a: 0x8800, 0x038b: 0x801b,
+ 0x038c: 0x801c, 0x038d: 0x801d, 0x038e: 0x801e, 0x038f: 0x801f, 0x0390: 0x8020, 0x0391: 0x8021,
+ 0x0392: 0x8022, 0x0393: 0x86e6, 0x0394: 0x86e6, 0x0395: 0x86dc, 0x0396: 0x80dc, 0x0397: 0x80e6,
+ 0x0398: 0x80e6, 0x0399: 0x80e6, 0x039a: 0x80e6, 0x039b: 0x80e6, 0x039c: 0x80dc, 0x039d: 0x80e6,
+ 0x039e: 0x80e6, 0x039f: 0x80dc,
+ 0x03b0: 0x8023,
+ // Block 0xf, offset 0x3c0
+ 0x03c5: 0x8800,
+ 0x03c6: 0x1946, 0x03c7: 0x8800, 0x03c8: 0x194d, 0x03c9: 0x8800, 0x03ca: 0x1954, 0x03cb: 0x8800,
+ 0x03cc: 0x195b, 0x03cd: 0x8800, 0x03ce: 0x1962, 0x03d1: 0x8800,
+ 0x03d2: 0x1969,
+ 0x03f4: 0x8007, 0x03f5: 0x8600,
+ 0x03fa: 0x8800, 0x03fb: 0x1970,
+ 0x03fc: 0x8800, 0x03fd: 0x1977, 0x03fe: 0x8800, 0x03ff: 0x8800,
+ // Block 0x10, offset 0x400
+ 0x0400: 0x2f81, 0x0401: 0x328d, 0x0402: 0x2f8b, 0x0403: 0x3297, 0x0404: 0x2f90, 0x0405: 0x329c,
+ 0x0406: 0x2f95, 0x0407: 0x32a1, 0x0408: 0x38b6, 0x0409: 0x3a45, 0x040a: 0x2fae, 0x040b: 0x32ba,
+ 0x040c: 0x2fb8, 0x040d: 0x32c4, 0x040e: 0x2fc7, 0x040f: 0x32d3, 0x0410: 0x2fbd, 0x0411: 0x32c9,
+ 0x0412: 0x2fc2, 0x0413: 0x32ce, 0x0414: 0x38d9, 0x0415: 0x3a68, 0x0416: 0x38e0, 0x0417: 0x3a6f,
+ 0x0418: 0x3003, 0x0419: 0x330f, 0x041a: 0x3008, 0x041b: 0x3314, 0x041c: 0x38ee, 0x041d: 0x3a7d,
+ 0x041e: 0x300d, 0x041f: 0x3319, 0x0420: 0x301c, 0x0421: 0x3328, 0x0422: 0x303a, 0x0423: 0x3346,
+ 0x0424: 0x3049, 0x0425: 0x3355, 0x0426: 0x303f, 0x0427: 0x334b, 0x0428: 0x304e, 0x0429: 0x335a,
+ 0x042a: 0x3053, 0x042b: 0x335f, 0x042c: 0x3099, 0x042d: 0x33a5, 0x042e: 0x38f5, 0x042f: 0x3a84,
+ 0x0430: 0x30a3, 0x0431: 0x33b4, 0x0432: 0x30ad, 0x0433: 0x33be, 0x0434: 0x30b7, 0x0435: 0x33c8,
+ 0x0436: 0x46aa, 0x0437: 0x473b, 0x0438: 0x38fc, 0x0439: 0x3a8b, 0x043a: 0x30d0, 0x043b: 0x33e1,
+ 0x043c: 0x30cb, 0x043d: 0x33dc, 0x043e: 0x30d5, 0x043f: 0x33e6,
+ // Block 0x11, offset 0x440
+ 0x0440: 0x30da, 0x0441: 0x33eb, 0x0442: 0x30df, 0x0443: 0x33f0, 0x0444: 0x30f3, 0x0445: 0x3404,
+ 0x0446: 0x30fd, 0x0447: 0x340e, 0x0448: 0x310c, 0x0449: 0x341d, 0x044a: 0x3107, 0x044b: 0x3418,
+ 0x044c: 0x391f, 0x044d: 0x3aae, 0x044e: 0x392d, 0x044f: 0x3abc, 0x0450: 0x3934, 0x0451: 0x3ac3,
+ 0x0452: 0x393b, 0x0453: 0x3aca, 0x0454: 0x3139, 0x0455: 0x344a, 0x0456: 0x313e, 0x0457: 0x344f,
+ 0x0458: 0x3148, 0x0459: 0x3459, 0x045a: 0x46d7, 0x045b: 0x4768, 0x045c: 0x3981, 0x045d: 0x3b10,
+ 0x045e: 0x3161, 0x045f: 0x3472, 0x0460: 0x316b, 0x0461: 0x347c, 0x0462: 0x46e6, 0x0463: 0x4777,
+ 0x0464: 0x3988, 0x0465: 0x3b17, 0x0466: 0x398f, 0x0467: 0x3b1e, 0x0468: 0x3996, 0x0469: 0x3b25,
+ 0x046a: 0x317a, 0x046b: 0x348b, 0x046c: 0x3184, 0x046d: 0x349a, 0x046e: 0x3198, 0x046f: 0x34ae,
+ 0x0470: 0x3193, 0x0471: 0x34a9, 0x0472: 0x31d4, 0x0473: 0x34ea, 0x0474: 0x31e3, 0x0475: 0x34f9,
+ 0x0476: 0x31de, 0x0477: 0x34f4, 0x0478: 0x399d, 0x0479: 0x3b2c, 0x047a: 0x39a4, 0x047b: 0x3b33,
+ 0x047c: 0x31e8, 0x047d: 0x34fe, 0x047e: 0x31ed, 0x047f: 0x3503,
+ // Block 0x12, offset 0x480
+ 0x0480: 0x31f2, 0x0481: 0x3508, 0x0482: 0x31f7, 0x0483: 0x350d, 0x0484: 0x3206, 0x0485: 0x351c,
+ 0x0486: 0x3201, 0x0487: 0x3517, 0x0488: 0x320b, 0x0489: 0x3526, 0x048a: 0x3210, 0x048b: 0x352b,
+ 0x048c: 0x3215, 0x048d: 0x3530, 0x048e: 0x3233, 0x048f: 0x354e, 0x0490: 0x324c, 0x0491: 0x356c,
+ 0x0492: 0x325b, 0x0493: 0x357b, 0x0494: 0x3260, 0x0495: 0x3580, 0x0496: 0x3364, 0x0497: 0x3490,
+ 0x0498: 0x3521, 0x0499: 0x355d, 0x049b: 0x35bb,
+ 0x04a0: 0x4687, 0x04a1: 0x4718, 0x04a2: 0x2f6d, 0x04a3: 0x3279,
+ 0x04a4: 0x3862, 0x04a5: 0x39f1, 0x04a6: 0x385b, 0x04a7: 0x39ea, 0x04a8: 0x3870, 0x04a9: 0x39ff,
+ 0x04aa: 0x3869, 0x04ab: 0x39f8, 0x04ac: 0x38a8, 0x04ad: 0x3a37, 0x04ae: 0x387e, 0x04af: 0x3a0d,
+ 0x04b0: 0x3877, 0x04b1: 0x3a06, 0x04b2: 0x388c, 0x04b3: 0x3a1b, 0x04b4: 0x3885, 0x04b5: 0x3a14,
+ 0x04b6: 0x38af, 0x04b7: 0x3a3e, 0x04b8: 0x469b, 0x04b9: 0x472c, 0x04ba: 0x2fea, 0x04bb: 0x32f6,
+ 0x04bc: 0x2fd6, 0x04bd: 0x32e2, 0x04be: 0x38c4, 0x04bf: 0x3a53,
+ // Block 0x13, offset 0x4c0
+ 0x04c0: 0x38bd, 0x04c1: 0x3a4c, 0x04c2: 0x38d2, 0x04c3: 0x3a61, 0x04c4: 0x38cb, 0x04c5: 0x3a5a,
+ 0x04c6: 0x38e7, 0x04c7: 0x3a76, 0x04c8: 0x307b, 0x04c9: 0x3387, 0x04ca: 0x308f, 0x04cb: 0x339b,
+ 0x04cc: 0x46cd, 0x04cd: 0x475e, 0x04ce: 0x3120, 0x04cf: 0x3431, 0x04d0: 0x390a, 0x04d1: 0x3a99,
+ 0x04d2: 0x3903, 0x04d3: 0x3a92, 0x04d4: 0x3918, 0x04d5: 0x3aa7, 0x04d6: 0x3911, 0x04d7: 0x3aa0,
+ 0x04d8: 0x3973, 0x04d9: 0x3b02, 0x04da: 0x3957, 0x04db: 0x3ae6, 0x04dc: 0x3950, 0x04dd: 0x3adf,
+ 0x04de: 0x3965, 0x04df: 0x3af4, 0x04e0: 0x395e, 0x04e1: 0x3aed, 0x04e2: 0x396c, 0x04e3: 0x3afb,
+ 0x04e4: 0x31cf, 0x04e5: 0x34e5, 0x04e6: 0x31b1, 0x04e7: 0x34c7, 0x04e8: 0x39ce, 0x04e9: 0x3b5d,
+ 0x04ea: 0x39c7, 0x04eb: 0x3b56, 0x04ec: 0x39dc, 0x04ed: 0x3b6b, 0x04ee: 0x39d5, 0x04ef: 0x3b64,
+ 0x04f0: 0x39e3, 0x04f1: 0x3b72, 0x04f2: 0x321a, 0x04f3: 0x3535, 0x04f4: 0x3242, 0x04f5: 0x3562,
+ 0x04f6: 0x323d, 0x04f7: 0x3558, 0x04f8: 0x3229, 0x04f9: 0x3544,
+ // Block 0x14, offset 0x500
+ 0x0500: 0x47ea, 0x0501: 0x47f0, 0x0502: 0x4904, 0x0503: 0x491c, 0x0504: 0x490c, 0x0505: 0x4924,
+ 0x0506: 0x4914, 0x0507: 0x492c, 0x0508: 0x4790, 0x0509: 0x4796, 0x050a: 0x4874, 0x050b: 0x488c,
+ 0x050c: 0x487c, 0x050d: 0x4894, 0x050e: 0x4884, 0x050f: 0x489c, 0x0510: 0x47fc, 0x0511: 0x4802,
+ 0x0512: 0x3da2, 0x0513: 0x3db2, 0x0514: 0x3daa, 0x0515: 0x3dba,
+ 0x0518: 0x479c, 0x0519: 0x47a2, 0x051a: 0x3cd2, 0x051b: 0x3ce2, 0x051c: 0x3cda, 0x051d: 0x3cea,
+ 0x0520: 0x4814, 0x0521: 0x481a, 0x0522: 0x4934, 0x0523: 0x494c,
+ 0x0524: 0x493c, 0x0525: 0x4954, 0x0526: 0x4944, 0x0527: 0x495c, 0x0528: 0x47a8, 0x0529: 0x47ae,
+ 0x052a: 0x48a4, 0x052b: 0x48bc, 0x052c: 0x48ac, 0x052d: 0x48c4, 0x052e: 0x48b4, 0x052f: 0x48cc,
+ 0x0530: 0x482c, 0x0531: 0x4832, 0x0532: 0x3e02, 0x0533: 0x3e1a, 0x0534: 0x3e0a, 0x0535: 0x3e22,
+ 0x0536: 0x3e12, 0x0537: 0x3e2a, 0x0538: 0x47b4, 0x0539: 0x47ba, 0x053a: 0x3d02, 0x053b: 0x3d1a,
+ 0x053c: 0x3d0a, 0x053d: 0x3d22, 0x053e: 0x3d12, 0x053f: 0x3d2a,
+ // Block 0x15, offset 0x540
+ 0x0540: 0x4838, 0x0541: 0x483e, 0x0542: 0x3e32, 0x0543: 0x3e42, 0x0544: 0x3e3a, 0x0545: 0x3e4a,
+ 0x0548: 0x47c0, 0x0549: 0x47c6, 0x054a: 0x3d32, 0x054b: 0x3d42,
+ 0x054c: 0x3d3a, 0x054d: 0x3d4a, 0x0550: 0x484a, 0x0551: 0x4850,
+ 0x0552: 0x3e6a, 0x0553: 0x3e82, 0x0554: 0x3e72, 0x0555: 0x3e8a, 0x0556: 0x3e7a, 0x0557: 0x3e92,
+ 0x0559: 0x47cc, 0x055b: 0x3d52, 0x055d: 0x3d5a,
+ 0x055f: 0x3d62, 0x0560: 0x4862, 0x0561: 0x4868, 0x0562: 0x4964, 0x0563: 0x497c,
+ 0x0564: 0x496c, 0x0565: 0x4984, 0x0566: 0x4974, 0x0567: 0x498c, 0x0568: 0x47d2, 0x0569: 0x47d8,
+ 0x056a: 0x48d4, 0x056b: 0x48ec, 0x056c: 0x48dc, 0x056d: 0x48f4, 0x056e: 0x48e4, 0x056f: 0x48fc,
+ 0x0570: 0x47de, 0x0571: 0x4304, 0x0572: 0x367b, 0x0573: 0x430a, 0x0574: 0x4808, 0x0575: 0x4310,
+ 0x0576: 0x368d, 0x0577: 0x4316, 0x0578: 0x36ab, 0x0579: 0x431c, 0x057a: 0x36c3, 0x057b: 0x4322,
+ 0x057c: 0x4856, 0x057d: 0x4328,
+ // Block 0x16, offset 0x580
+ 0x0580: 0x3d8a, 0x0581: 0x3d92, 0x0582: 0x416e, 0x0583: 0x418c, 0x0584: 0x4178, 0x0585: 0x4196,
+ 0x0586: 0x4182, 0x0587: 0x41a0, 0x0588: 0x3cc2, 0x0589: 0x3cca, 0x058a: 0x40ba, 0x058b: 0x40d8,
+ 0x058c: 0x40c4, 0x058d: 0x40e2, 0x058e: 0x40ce, 0x058f: 0x40ec, 0x0590: 0x3dd2, 0x0591: 0x3dda,
+ 0x0592: 0x41aa, 0x0593: 0x41c8, 0x0594: 0x41b4, 0x0595: 0x41d2, 0x0596: 0x41be, 0x0597: 0x41dc,
+ 0x0598: 0x3cf2, 0x0599: 0x3cfa, 0x059a: 0x40f6, 0x059b: 0x4114, 0x059c: 0x4100, 0x059d: 0x411e,
+ 0x059e: 0x410a, 0x059f: 0x4128, 0x05a0: 0x3eaa, 0x05a1: 0x3eb2, 0x05a2: 0x41e6, 0x05a3: 0x4204,
+ 0x05a4: 0x41f0, 0x05a5: 0x420e, 0x05a6: 0x41fa, 0x05a7: 0x4218, 0x05a8: 0x3d6a, 0x05a9: 0x3d72,
+ 0x05aa: 0x4132, 0x05ab: 0x4150, 0x05ac: 0x413c, 0x05ad: 0x415a, 0x05ae: 0x4146, 0x05af: 0x4164,
+ 0x05b0: 0x366f, 0x05b1: 0x3669, 0x05b2: 0x3d7a, 0x05b3: 0x3675, 0x05b4: 0x3d82,
+ 0x05b6: 0x47f6, 0x05b7: 0x3d9a, 0x05b8: 0x35df, 0x05b9: 0x35d9, 0x05ba: 0x35cd, 0x05bb: 0x42d4,
+ 0x05bc: 0x35e5, 0x05be: 0x01d3, 0x05bf: 0x8800,
+ // Block 0x17, offset 0x5c0
+ 0x05c1: 0x3591, 0x05c2: 0x3dc2, 0x05c3: 0x3687, 0x05c4: 0x3dca,
+ 0x05c6: 0x4820, 0x05c7: 0x3de2, 0x05c8: 0x35eb, 0x05c9: 0x42da, 0x05ca: 0x35f7, 0x05cb: 0x42e0,
+ 0x05cc: 0x3603, 0x05cd: 0x3b79, 0x05ce: 0x3b80, 0x05cf: 0x3b87, 0x05d0: 0x369f, 0x05d1: 0x3699,
+ 0x05d2: 0x3dea, 0x05d3: 0x44ca, 0x05d6: 0x36a5, 0x05d7: 0x3dfa,
+ 0x05d8: 0x361b, 0x05d9: 0x3615, 0x05da: 0x3609, 0x05db: 0x42e6, 0x05dd: 0x3b8e,
+ 0x05de: 0x3b95, 0x05df: 0x3b9c, 0x05e0: 0x36d5, 0x05e1: 0x36cf, 0x05e2: 0x3e52, 0x05e3: 0x44d2,
+ 0x05e4: 0x36b7, 0x05e5: 0x36bd, 0x05e6: 0x36db, 0x05e7: 0x3e62, 0x05e8: 0x364b, 0x05e9: 0x3645,
+ 0x05ea: 0x3639, 0x05eb: 0x42f2, 0x05ec: 0x3633, 0x05ed: 0x3585, 0x05ee: 0x42ce, 0x05ef: 0x0081,
+ 0x05f2: 0x3e9a, 0x05f3: 0x36e1, 0x05f4: 0x3ea2,
+ 0x05f6: 0x486e, 0x05f7: 0x3eba, 0x05f8: 0x3627, 0x05f9: 0x42ec, 0x05fa: 0x3657, 0x05fb: 0x42fe,
+ 0x05fc: 0x3663, 0x05fd: 0x00ce, 0x05fe: 0x8800,
+ // Block 0x18, offset 0x600
+ 0x0601: 0x3bf0, 0x0603: 0x8800, 0x0604: 0x3bf7, 0x0605: 0x8800,
+ 0x0607: 0x3bfe, 0x0608: 0x8800, 0x0609: 0x3c05,
+ 0x060d: 0x8800,
+ 0x0620: 0x2f4f, 0x0621: 0x8800, 0x0622: 0x3c13,
+ 0x0624: 0x8800, 0x0625: 0x8800,
+ 0x062d: 0x3c0c, 0x062e: 0x2f4a, 0x062f: 0x2f54,
+ 0x0630: 0x3c1a, 0x0631: 0x3c21, 0x0632: 0x8800, 0x0633: 0x8800, 0x0634: 0x3c28, 0x0635: 0x3c2f,
+ 0x0636: 0x8800, 0x0637: 0x8800, 0x0638: 0x3c36, 0x0639: 0x3c3d, 0x063a: 0x8800, 0x063b: 0x8800,
+ 0x063c: 0x8800, 0x063d: 0x8800,
+ // Block 0x19, offset 0x640
+ 0x0640: 0x3c44, 0x0641: 0x3c4b, 0x0642: 0x8800, 0x0643: 0x8800, 0x0644: 0x3c60, 0x0645: 0x3c67,
+ 0x0646: 0x8800, 0x0647: 0x8800, 0x0648: 0x3c6e, 0x0649: 0x3c75,
+ 0x0651: 0x8800,
+ 0x0652: 0x8800,
+ 0x0662: 0x8800,
+ 0x0668: 0x8800, 0x0669: 0x8800,
+ 0x066b: 0x8800, 0x066c: 0x3c8a, 0x066d: 0x3c91, 0x066e: 0x3c98, 0x066f: 0x3c9f,
+ 0x0672: 0x8800, 0x0673: 0x8800, 0x0674: 0x8800, 0x0675: 0x8800,
+ // Block 0x1a, offset 0x680
+ 0x0686: 0x8800, 0x068b: 0x8800,
+ 0x068c: 0x3ef2, 0x068d: 0x8800, 0x068e: 0x3efa, 0x068f: 0x8800, 0x0690: 0x3f02, 0x0691: 0x8800,
+ 0x0692: 0x3f0a, 0x0693: 0x8800, 0x0694: 0x3f12, 0x0695: 0x8800, 0x0696: 0x3f1a, 0x0697: 0x8800,
+ 0x0698: 0x3f22, 0x0699: 0x8800, 0x069a: 0x3f2a, 0x069b: 0x8800, 0x069c: 0x3f32, 0x069d: 0x8800,
+ 0x069e: 0x3f3a, 0x069f: 0x8800, 0x06a0: 0x3f42, 0x06a1: 0x8800, 0x06a2: 0x3f4a,
+ 0x06a4: 0x8800, 0x06a5: 0x3f52, 0x06a6: 0x8800, 0x06a7: 0x3f5a, 0x06a8: 0x8800, 0x06a9: 0x3f62,
+ 0x06af: 0x8800,
+ 0x06b0: 0x3f6a, 0x06b1: 0x3f72, 0x06b2: 0x8800, 0x06b3: 0x3f7a, 0x06b4: 0x3f82, 0x06b5: 0x8800,
+ 0x06b6: 0x3f8a, 0x06b7: 0x3f92, 0x06b8: 0x8800, 0x06b9: 0x3f9a, 0x06ba: 0x3fa2, 0x06bb: 0x8800,
+ 0x06bc: 0x3faa, 0x06bd: 0x3fb2,
+ // Block 0x1b, offset 0x6c0
+ 0x06d4: 0x3eea,
+ 0x06d9: 0x8608, 0x06da: 0x8608, 0x06dd: 0x8800,
+ 0x06de: 0x3fba,
+ 0x06e6: 0x8800,
+ 0x06eb: 0x8800, 0x06ec: 0x3fca, 0x06ed: 0x8800, 0x06ee: 0x3fd2, 0x06ef: 0x8800,
+ 0x06f0: 0x3fda, 0x06f1: 0x8800, 0x06f2: 0x3fe2, 0x06f3: 0x8800, 0x06f4: 0x3fea, 0x06f5: 0x8800,
+ 0x06f6: 0x3ff2, 0x06f7: 0x8800, 0x06f8: 0x3ffa, 0x06f9: 0x8800, 0x06fa: 0x4002, 0x06fb: 0x8800,
+ 0x06fc: 0x400a, 0x06fd: 0x8800, 0x06fe: 0x4012, 0x06ff: 0x8800,
+ // Block 0x1c, offset 0x700
+ 0x0700: 0x401a, 0x0701: 0x8800, 0x0702: 0x4022, 0x0704: 0x8800, 0x0705: 0x402a,
+ 0x0706: 0x8800, 0x0707: 0x4032, 0x0708: 0x8800, 0x0709: 0x403a,
+ 0x070f: 0x8800, 0x0710: 0x4042, 0x0711: 0x404a,
+ 0x0712: 0x8800, 0x0713: 0x4052, 0x0714: 0x405a, 0x0715: 0x8800, 0x0716: 0x4062, 0x0717: 0x406a,
+ 0x0718: 0x8800, 0x0719: 0x4072, 0x071a: 0x407a, 0x071b: 0x8800, 0x071c: 0x4082, 0x071d: 0x408a,
+ 0x072f: 0x8800,
+ 0x0730: 0x8800, 0x0731: 0x8800, 0x0732: 0x8800, 0x0734: 0x3fc2,
+ 0x0737: 0x4092, 0x0738: 0x409a, 0x0739: 0x40a2, 0x073a: 0x40aa,
+ 0x073d: 0x8800, 0x073e: 0x40b2,
+ // Block 0x1d, offset 0x740
+ 0x0740: 0x13e9, 0x0741: 0x0d6d, 0x0742: 0x1445, 0x0743: 0x1411, 0x0744: 0x0ec9, 0x0745: 0x075d,
+ 0x0746: 0x0951, 0x0747: 0x1699, 0x0748: 0x1699, 0x0749: 0x0a7d, 0x074a: 0x14cd, 0x074b: 0x09b5,
+ 0x074c: 0x0a79, 0x074d: 0x0c61, 0x074e: 0x1041, 0x074f: 0x11d1, 0x0750: 0x1309, 0x0751: 0x1345,
+ 0x0752: 0x1379, 0x0753: 0x148d, 0x0754: 0x0de5, 0x0755: 0x0e71, 0x0756: 0x0f1d, 0x0757: 0x0fb5,
+ 0x0758: 0x12d1, 0x0759: 0x14b5, 0x075a: 0x15e1, 0x075b: 0x0781, 0x075c: 0x0925, 0x075d: 0x0df9,
+ 0x075e: 0x0f41, 0x075f: 0x1305, 0x0760: 0x1631, 0x0761: 0x0b25, 0x0762: 0x0ee9, 0x0763: 0x12f5,
+ 0x0764: 0x1389, 0x0765: 0x0c95, 0x0766: 0x122d, 0x0767: 0x1351, 0x0768: 0x0b91, 0x0769: 0x0d81,
+ 0x076a: 0x0e89, 0x076b: 0x0f8d, 0x076c: 0x1499, 0x076d: 0x07c1, 0x076e: 0x0859, 0x076f: 0x08c5,
+ 0x0770: 0x0cfd, 0x0771: 0x0df1, 0x0772: 0x0f3d, 0x0773: 0x1061, 0x0774: 0x11e9, 0x0775: 0x12fd,
+ 0x0776: 0x1315, 0x0777: 0x1439, 0x0778: 0x155d, 0x0779: 0x1611, 0x077a: 0x162d, 0x077b: 0x109d,
+ 0x077c: 0x10dd, 0x077d: 0x1195, 0x077e: 0x12b5, 0x077f: 0x14e9,
+ // Block 0x1e, offset 0x780
+ 0x0780: 0x1639, 0x0781: 0x13bd, 0x0782: 0x0a39, 0x0783: 0x0bad, 0x0784: 0x114d, 0x0785: 0x120d,
+ 0x0786: 0x0f71, 0x0787: 0x10a5, 0x0788: 0x1409, 0x0789: 0x1555, 0x078a: 0x0a35, 0x078b: 0x0b01,
+ 0x078c: 0x0de9, 0x078d: 0x0e9d, 0x078e: 0x0ed1, 0x078f: 0x1185, 0x0790: 0x11ad, 0x0791: 0x1515,
+ 0x0792: 0x08c1, 0x0793: 0x1219, 0x0794: 0x0865, 0x0795: 0x0861, 0x0796: 0x1109, 0x0797: 0x1199,
+ 0x0798: 0x12cd, 0x0799: 0x151d, 0x079a: 0x13d9, 0x079b: 0x0c99, 0x079c: 0x0de5, 0x079d: 0x13c9,
+ 0x079e: 0x0769, 0x079f: 0x0ad5, 0x07a0: 0x0c05, 0x07a1: 0x0fa1, 0x07a2: 0x1021, 0x07a3: 0x08e5,
+ 0x07a4: 0x10ad, 0x07a5: 0x07d1, 0x07a6: 0x0be9, 0x07a7: 0x0749, 0x07a8: 0x0e5d, 0x07a9: 0x0d15,
+ 0x07aa: 0x1181, 0x07ab: 0x0939, 0x07ac: 0x0a25, 0x07ad: 0x106d, 0x07ae: 0x12d5, 0x07af: 0x13ad,
+ 0x07b0: 0x0e29, 0x07b1: 0x1469, 0x07b2: 0x0e55, 0x07b3: 0x0ca9, 0x07b4: 0x128d, 0x07b5: 0x0cc9,
+ 0x07b6: 0x101d, 0x07b7: 0x079d, 0x07b8: 0x0819, 0x07b9: 0x085d, 0x07ba: 0x0dc5, 0x07bb: 0x116d,
+ 0x07bc: 0x1265, 0x07bd: 0x13b9, 0x07be: 0x14c9, 0x07bf: 0x08cd,
+ // Block 0x1f, offset 0x7c0
+ 0x07c0: 0x0981, 0x07c1: 0x0a89, 0x07c2: 0x0ba1, 0x07c3: 0x0d31, 0x07c4: 0x0eed, 0x07c5: 0x10b1,
+ 0x07c6: 0x1505, 0x07c7: 0x15e9, 0x07c8: 0x163d, 0x07c9: 0x1655, 0x07ca: 0x08a9, 0x07cb: 0x0d65,
+ 0x07cc: 0x0e15, 0x07cd: 0x145d, 0x07ce: 0x0b6d, 0x07cf: 0x0c49, 0x07d0: 0x0c65, 0x07d1: 0x0cf5,
+ 0x07d2: 0x0edd, 0x07d3: 0x0f29, 0x07d4: 0x0fd9, 0x07d5: 0x10fd, 0x07d6: 0x11a1, 0x07d7: 0x1205,
+ 0x07d8: 0x144d, 0x07d9: 0x12dd, 0x07da: 0x1475, 0x07db: 0x14ed, 0x07dc: 0x0881, 0x07dd: 0x08ad,
+ 0x07de: 0x0995, 0x07df: 0x0f19, 0x07e0: 0x1365, 0x07e1: 0x13ad, 0x07e2: 0x0b8d, 0x07e3: 0x0bfd,
+ 0x07e4: 0x0cc1, 0x07e5: 0x0e21, 0x07e6: 0x1149, 0x07e7: 0x0f95, 0x07e8: 0x07ad, 0x07e9: 0x09f1,
+ 0x07ea: 0x0ad5, 0x07eb: 0x0b39, 0x07ec: 0x0c09, 0x07ed: 0x0fb1, 0x07ee: 0x0fcd, 0x07ef: 0x11dd,
+ 0x07f0: 0x11fd, 0x07f1: 0x14d1, 0x07f2: 0x1551, 0x07f3: 0x1561, 0x07f4: 0x159d, 0x07f5: 0x07c5,
+ 0x07f6: 0x10f1, 0x07f7: 0x14bd, 0x07f8: 0x1539, 0x07f9: 0x0c21, 0x07fa: 0x0789, 0x07fb: 0x07e9,
+ 0x07fc: 0x0ad9, 0x07fd: 0x0af9, 0x07fe: 0x0d21, 0x07ff: 0x0de5,
+ // Block 0x20, offset 0x800
+ 0x0800: 0x0f35, 0x0801: 0x103d, 0x0802: 0x12e9, 0x0803: 0x1489, 0x0804: 0x1691, 0x0805: 0x0d55,
+ 0x0806: 0x1511, 0x0807: 0x08a5, 0x0808: 0x0da1, 0x0809: 0x0dad, 0x080a: 0x0e81, 0x080b: 0x0eb9,
+ 0x080c: 0x0fbd, 0x080d: 0x1019, 0x080e: 0x1099, 0x080f: 0x117d, 0x0810: 0x15a9, 0x0811: 0x0821,
+ 0x0812: 0x0c75, 0x0813: 0x1521, 0x0814: 0x07d9, 0x0815: 0x0b1d, 0x0816: 0x0ea1, 0x0817: 0x1451,
+ 0x0818: 0x0bd9, 0x0819: 0x0c29, 0x081a: 0x0db5, 0x081b: 0x0fa1, 0x081c: 0x1529, 0x081d: 0x0889,
+ 0x081e: 0x0971, 0x081f: 0x0b09, 0x0820: 0x0d45, 0x0821: 0x0d91, 0x0822: 0x0dd1, 0x0823: 0x0e65,
+ 0x0824: 0x0fb9, 0x0825: 0x102d, 0x0826: 0x11c9, 0x0827: 0x1369, 0x0828: 0x1375, 0x0829: 0x14c5,
+ 0x082a: 0x1545, 0x082b: 0x08f5, 0x082c: 0x0ebd, 0x082d: 0x0975, 0x082e: 0x0f39, 0x082f: 0x0fdd,
+ 0x0830: 0x12f9, 0x0831: 0x152d, 0x0832: 0x1619, 0x0833: 0x1641, 0x0834: 0x0da9, 0x0835: 0x0e99,
+ 0x0836: 0x1235, 0x0837: 0x1129, 0x0838: 0x1135, 0x0839: 0x1159, 0x083a: 0x0f89, 0x083b: 0x0f11,
+ 0x083c: 0x13d5, 0x083d: 0x07a5, 0x083e: 0x129d, 0x083f: 0x088d,
+ // Block 0x21, offset 0x840
+ 0x0840: 0x087d, 0x0841: 0x0b7d, 0x0842: 0x0c9d, 0x0843: 0x1165, 0x0844: 0x0ac5, 0x0845: 0x0e75,
+ 0x0846: 0x0d61, 0x0847: 0x1459, 0x0848: 0x1359, 0x0849: 0x1519, 0x084a: 0x1395, 0x084b: 0x0b99,
+ 0x084c: 0x07f9, 0x084d: 0x09cd, 0x0850: 0x0a21,
+ 0x0852: 0x0d51, 0x0855: 0x0869, 0x0856: 0x0f91, 0x0857: 0x1055,
+ 0x0858: 0x10b9, 0x0859: 0x10d5, 0x085a: 0x10d9, 0x085b: 0x10ed, 0x085c: 0x1569, 0x085d: 0x115d,
+ 0x085e: 0x11e1, 0x0860: 0x1301, 0x0862: 0x13c5,
+ 0x0865: 0x1479, 0x0866: 0x14a5,
+ 0x086a: 0x15bd, 0x086b: 0x15c1, 0x086c: 0x15c5, 0x086d: 0x1629, 0x086e: 0x149d, 0x086f: 0x1535,
+ 0x0870: 0x07c9, 0x0871: 0x07ed, 0x0872: 0x0801, 0x0873: 0x08bd, 0x0874: 0x08c9, 0x0875: 0x0909,
+ 0x0876: 0x09bd, 0x0877: 0x09d9, 0x0878: 0x09e1, 0x0879: 0x0a1d, 0x087a: 0x0a29, 0x087b: 0x0b05,
+ 0x087c: 0x0b0d, 0x087d: 0x0c15, 0x087e: 0x0c3d, 0x087f: 0x0c45,
+ // Block 0x22, offset 0x880
+ 0x0880: 0x0c5d, 0x0881: 0x0d09, 0x0882: 0x0d39, 0x0883: 0x0d59, 0x0884: 0x0dc9, 0x0885: 0x0e8d,
+ 0x0886: 0x0ea9, 0x0887: 0x0ed9, 0x0888: 0x0f2d, 0x0889: 0x0f4d, 0x088a: 0x0fc1, 0x088b: 0x10a1,
+ 0x088c: 0x10bd, 0x088d: 0x10c5, 0x088e: 0x10c1, 0x088f: 0x10c9, 0x0890: 0x10cd, 0x0891: 0x10d1,
+ 0x0892: 0x10e5, 0x0893: 0x10e9, 0x0894: 0x110d, 0x0895: 0x1121, 0x0896: 0x113d, 0x0897: 0x11a1,
+ 0x0898: 0x11a9, 0x0899: 0x11b1, 0x089a: 0x11c5, 0x089b: 0x11ed, 0x089c: 0x123d, 0x089d: 0x1271,
+ 0x089e: 0x1271, 0x089f: 0x12d9, 0x08a0: 0x1381, 0x08a1: 0x1399, 0x08a2: 0x13cd, 0x08a3: 0x13d1,
+ 0x08a4: 0x1415, 0x08a5: 0x1419, 0x08a6: 0x1471, 0x08a7: 0x1479, 0x08a8: 0x1549, 0x08a9: 0x158d,
+ 0x08aa: 0x15a5, 0x08ab: 0x0c0d, 0x08ac: 0x1780, 0x08ad: 0x1255,
+ 0x08b0: 0x0751, 0x08b1: 0x0855, 0x08b2: 0x0815, 0x08b3: 0x07bd, 0x08b4: 0x07fd, 0x08b5: 0x0829,
+ 0x08b6: 0x08b9, 0x08b7: 0x08d5, 0x08b8: 0x09bd, 0x08b9: 0x09a9, 0x08ba: 0x09b9, 0x08bb: 0x09d5,
+ 0x08bc: 0x0a21, 0x08bd: 0x0a31, 0x08be: 0x0a75, 0x08bf: 0x0a81,
+ // Block 0x23, offset 0x8c0
+ 0x08c0: 0x0a9d, 0x08c1: 0x0aad, 0x08c2: 0x0b95, 0x08c3: 0x0b9d, 0x08c4: 0x0bcd, 0x08c5: 0x0bed,
+ 0x08c6: 0x0c1d, 0x08c7: 0x0c35, 0x08c8: 0x0c25, 0x08c9: 0x0c45, 0x08ca: 0x0c39, 0x08cb: 0x0c5d,
+ 0x08cc: 0x0c79, 0x08cd: 0x0cd1, 0x08ce: 0x0cdd, 0x08cf: 0x0ce5, 0x08d0: 0x0d0d, 0x08d1: 0x0d51,
+ 0x08d2: 0x0d81, 0x08d3: 0x0d85, 0x08d4: 0x0d99, 0x08d5: 0x0e19, 0x08d6: 0x0e29, 0x08d7: 0x0e81,
+ 0x08d8: 0x0ecd, 0x08d9: 0x0ec5, 0x08da: 0x0ed9, 0x08db: 0x0ef5, 0x08dc: 0x0f2d, 0x08dd: 0x1085,
+ 0x08de: 0x0f51, 0x08df: 0x0f85, 0x08e0: 0x0f91, 0x08e1: 0x0fd1, 0x08e2: 0x0fed, 0x08e3: 0x1011,
+ 0x08e4: 0x1035, 0x08e5: 0x1039, 0x08e6: 0x1055, 0x08e7: 0x1059, 0x08e8: 0x1069, 0x08e9: 0x107d,
+ 0x08ea: 0x1079, 0x08eb: 0x10a9, 0x08ec: 0x1125, 0x08ed: 0x113d, 0x08ee: 0x1155, 0x08ef: 0x118d,
+ 0x08f0: 0x11a1, 0x08f1: 0x11bd, 0x08f2: 0x11ed, 0x08f3: 0x12a1, 0x08f4: 0x12c9, 0x08f5: 0x133d,
+ 0x08f6: 0x1385, 0x08f7: 0x1391, 0x08f8: 0x1399, 0x08f9: 0x13b1, 0x08fa: 0x13c5, 0x08fb: 0x13b5,
+ 0x08fc: 0x13cd, 0x08fd: 0x13c9, 0x08fe: 0x13c1, 0x08ff: 0x13d1,
+ // Block 0x24, offset 0x900
+ 0x0900: 0x13dd, 0x0901: 0x1419, 0x0902: 0x1455, 0x0903: 0x1485, 0x0904: 0x14b9, 0x0905: 0x14d9,
+ 0x0906: 0x1525, 0x0907: 0x1549, 0x0908: 0x1569, 0x0909: 0x157d, 0x090a: 0x158d, 0x090b: 0x1599,
+ 0x090c: 0x15a5, 0x090d: 0x15f9, 0x090e: 0x1699, 0x090f: 0x1717, 0x0910: 0x1712, 0x0911: 0x1744,
+ 0x0912: 0x0679, 0x0913: 0x06a1, 0x0914: 0x06a5, 0x0915: 0x17c6, 0x0916: 0x17f3, 0x0917: 0x186b,
+ 0x0918: 0x1685, 0x0919: 0x1695,
+ // Block 0x25, offset 0x940
+ 0x0940: 0x076d, 0x0941: 0x0765, 0x0942: 0x0775, 0x0943: 0x16a9, 0x0944: 0x07b9, 0x0945: 0x07c9,
+ 0x0946: 0x07cd, 0x0947: 0x07d5, 0x0948: 0x07dd, 0x0949: 0x07e1, 0x094a: 0x07ed, 0x094b: 0x07e5,
+ 0x094c: 0x0625, 0x094d: 0x16bd, 0x094e: 0x0801, 0x094f: 0x0805, 0x0950: 0x0809, 0x0951: 0x0825,
+ 0x0952: 0x16ae, 0x0953: 0x0629, 0x0954: 0x0811, 0x0955: 0x0831, 0x0956: 0x16b8, 0x0957: 0x0841,
+ 0x0958: 0x0849, 0x0959: 0x07a9, 0x095a: 0x0851, 0x095b: 0x0855, 0x095c: 0x1893, 0x095d: 0x0871,
+ 0x095e: 0x0879, 0x095f: 0x0631, 0x0960: 0x0891, 0x0961: 0x0895, 0x0962: 0x089d, 0x0963: 0x08a1,
+ 0x0964: 0x0635, 0x0965: 0x08b9, 0x0966: 0x08bd, 0x0967: 0x08c9, 0x0968: 0x08d5, 0x0969: 0x08d9,
+ 0x096a: 0x08dd, 0x096b: 0x08e5, 0x096c: 0x0905, 0x096d: 0x0909, 0x096e: 0x0911, 0x096f: 0x0921,
+ 0x0970: 0x0929, 0x0971: 0x092d, 0x0972: 0x092d, 0x0973: 0x092d, 0x0974: 0x16cc, 0x0975: 0x0f05,
+ 0x0976: 0x0941, 0x0977: 0x0949, 0x0978: 0x16d1, 0x0979: 0x0955, 0x097a: 0x095d, 0x097b: 0x0965,
+ 0x097c: 0x098d, 0x097d: 0x0979, 0x097e: 0x0985, 0x097f: 0x0989,
+ // Block 0x26, offset 0x980
+ 0x0980: 0x0991, 0x0981: 0x0999, 0x0982: 0x099d, 0x0983: 0x09a5, 0x0984: 0x09ad, 0x0985: 0x09b1,
+ 0x0986: 0x09b1, 0x0987: 0x09b9, 0x0988: 0x09c1, 0x0989: 0x09c5, 0x098a: 0x09d1, 0x098b: 0x09f5,
+ 0x098c: 0x09d9, 0x098d: 0x09f9, 0x098e: 0x09dd, 0x098f: 0x09e5, 0x0990: 0x087d, 0x0991: 0x0a41,
+ 0x0992: 0x0a09, 0x0993: 0x0a0d, 0x0994: 0x0a11, 0x0995: 0x0a05, 0x0996: 0x0a19, 0x0997: 0x0a15,
+ 0x0998: 0x0a2d, 0x0999: 0x16d6, 0x099a: 0x0a49, 0x099b: 0x0a4d, 0x099c: 0x0a55, 0x099d: 0x0a61,
+ 0x099e: 0x0a69, 0x099f: 0x0a85, 0x09a0: 0x16db, 0x09a1: 0x16e0, 0x09a2: 0x0a91, 0x09a3: 0x0a95,
+ 0x09a4: 0x0a99, 0x09a5: 0x0a8d, 0x09a6: 0x0aa1, 0x09a7: 0x0639, 0x09a8: 0x063d, 0x09a9: 0x0aa9,
+ 0x09aa: 0x0ab1, 0x09ab: 0x0ab1, 0x09ac: 0x16e5, 0x09ad: 0x0acd, 0x09ae: 0x0ad1, 0x09af: 0x0ad5,
+ 0x09b0: 0x0add, 0x09b1: 0x16ea, 0x09b2: 0x0ae5, 0x09b3: 0x0ae9, 0x09b4: 0x0bc1, 0x09b5: 0x0af1,
+ 0x09b6: 0x0641, 0x09b7: 0x0afd, 0x09b8: 0x0b0d, 0x09b9: 0x0b19, 0x09ba: 0x0b15, 0x09bb: 0x16f4,
+ 0x09bc: 0x0b21, 0x09bd: 0x16f9, 0x09be: 0x0b2d, 0x09bf: 0x0b29,
+ // Block 0x27, offset 0x9c0
+ 0x09c0: 0x0b31, 0x09c1: 0x0b41, 0x09c2: 0x0b45, 0x09c3: 0x0645, 0x09c4: 0x0b55, 0x09c5: 0x0b5d,
+ 0x09c6: 0x0b61, 0x09c7: 0x0b65, 0x09c8: 0x0649, 0x09c9: 0x16fe, 0x09ca: 0x064d, 0x09cb: 0x0b81,
+ 0x09cc: 0x0b85, 0x09cd: 0x0b89, 0x09ce: 0x0b91, 0x09cf: 0x18c5, 0x09d0: 0x0ba9, 0x09d1: 0x1708,
+ 0x09d2: 0x1708, 0x09d3: 0x1249, 0x09d4: 0x0bb9, 0x09d5: 0x0bb9, 0x09d6: 0x0651, 0x09d7: 0x172b,
+ 0x09d8: 0x17fd, 0x09d9: 0x0bc9, 0x09da: 0x0bd1, 0x09db: 0x0655, 0x09dc: 0x0be5, 0x09dd: 0x0bf5,
+ 0x09de: 0x0bf9, 0x09df: 0x0c01, 0x09e0: 0x0c11, 0x09e1: 0x065d, 0x09e2: 0x0659, 0x09e3: 0x0c15,
+ 0x09e4: 0x170d, 0x09e5: 0x0c19, 0x09e6: 0x0c2d, 0x09e7: 0x0c31, 0x09e8: 0x0c35, 0x09e9: 0x0c31,
+ 0x09ea: 0x0c41, 0x09eb: 0x0c45, 0x09ec: 0x0c55, 0x09ed: 0x0c4d, 0x09ee: 0x0c51, 0x09ef: 0x0c59,
+ 0x09f0: 0x0c5d, 0x09f1: 0x0c61, 0x09f2: 0x0c6d, 0x09f3: 0x0c71, 0x09f4: 0x0c89, 0x09f5: 0x0c91,
+ 0x09f6: 0x0ca1, 0x09f7: 0x0cb5, 0x09f8: 0x171c, 0x09f9: 0x0cb1, 0x09fa: 0x0ca5, 0x09fb: 0x0cbd,
+ 0x09fc: 0x0cc5, 0x09fd: 0x0cd9, 0x09fe: 0x1721, 0x09ff: 0x0ce1,
+ // Block 0x28, offset 0xa00
+ 0x0a00: 0x0cd5, 0x0a01: 0x0ccd, 0x0a02: 0x0661, 0x0a03: 0x0ce9, 0x0a04: 0x0cf1, 0x0a05: 0x0cf9,
+ 0x0a06: 0x0ced, 0x0a07: 0x0665, 0x0a08: 0x0d09, 0x0a09: 0x0d11, 0x0a0a: 0x1726, 0x0a0b: 0x0d3d,
+ 0x0a0c: 0x0d71, 0x0a0d: 0x0d4d, 0x0a0e: 0x0671, 0x0a0f: 0x0d59, 0x0a10: 0x066d, 0x0a11: 0x0669,
+ 0x0a12: 0x0835, 0x0a13: 0x0839, 0x0a14: 0x0d75, 0x0a15: 0x0d5d, 0x0a16: 0x121d, 0x0a17: 0x06d5,
+ 0x0a18: 0x0d81, 0x0a19: 0x0d85, 0x0a1a: 0x0d89, 0x0a1b: 0x0d9d, 0x0a1c: 0x0d95, 0x0a1d: 0x173f,
+ 0x0a1e: 0x0675, 0x0a1f: 0x0db1, 0x0a20: 0x0da5, 0x0a21: 0x0dc1, 0x0a22: 0x0dc9, 0x0a23: 0x1749,
+ 0x0a24: 0x0dcd, 0x0a25: 0x0db9, 0x0a26: 0x0dd5, 0x0a27: 0x0679, 0x0a28: 0x0dd9, 0x0a29: 0x0ddd,
+ 0x0a2a: 0x0de1, 0x0a2b: 0x0ded, 0x0a2c: 0x174e, 0x0a2d: 0x0df5, 0x0a2e: 0x067d, 0x0a2f: 0x0e01,
+ 0x0a30: 0x1753, 0x0a31: 0x0e05, 0x0a32: 0x0681, 0x0a33: 0x0e11, 0x0a34: 0x0e1d, 0x0a35: 0x0e29,
+ 0x0a36: 0x0e2d, 0x0a37: 0x1758, 0x0a38: 0x16ef, 0x0a39: 0x175d, 0x0a3a: 0x0e4d, 0x0a3b: 0x1762,
+ 0x0a3c: 0x0e59, 0x0a3d: 0x0e61, 0x0a3e: 0x0e51, 0x0a3f: 0x0e6d,
+ // Block 0x29, offset 0xa40
+ 0x0a40: 0x0e7d, 0x0a41: 0x0e8d, 0x0a42: 0x0e81, 0x0a43: 0x0e85, 0x0a44: 0x0e91, 0x0a45: 0x0e95,
+ 0x0a46: 0x1767, 0x0a47: 0x0e79, 0x0a48: 0x0ead, 0x0a49: 0x0eb1, 0x0a4a: 0x0685, 0x0a4b: 0x0ec5,
+ 0x0a4c: 0x0ec1, 0x0a4d: 0x176c, 0x0a4e: 0x0ea5, 0x0a4f: 0x0ee1, 0x0a50: 0x1771, 0x0a51: 0x1776,
+ 0x0a52: 0x0ee5, 0x0a53: 0x0ef9, 0x0a54: 0x0ef5, 0x0a55: 0x0ef1, 0x0a56: 0x0689, 0x0a57: 0x0efd,
+ 0x0a58: 0x0f0d, 0x0a59: 0x0f09, 0x0a5a: 0x0f15, 0x0a5b: 0x16b3, 0x0a5c: 0x0f25, 0x0a5d: 0x177b,
+ 0x0a5e: 0x0f31, 0x0a5f: 0x1785, 0x0a60: 0x0f45, 0x0a61: 0x0f51, 0x0a62: 0x0f65, 0x0a63: 0x178a,
+ 0x0a64: 0x0f79, 0x0a65: 0x0f7d, 0x0a66: 0x178f, 0x0a67: 0x1794, 0x0a68: 0x0f99, 0x0a69: 0x0fa9,
+ 0x0a6a: 0x068d, 0x0a6b: 0x0fad, 0x0a6c: 0x0691, 0x0a6d: 0x0691, 0x0a6e: 0x0fc5, 0x0a6f: 0x0fc9,
+ 0x0a70: 0x0fd1, 0x0a71: 0x0fd5, 0x0a72: 0x0fe1, 0x0a73: 0x0695, 0x0a74: 0x0ff9, 0x0a75: 0x1799,
+ 0x0a76: 0x1015, 0x0a77: 0x179e, 0x0a78: 0x1021, 0x0a79: 0x1703, 0x0a7a: 0x1031, 0x0a7b: 0x17a3,
+ 0x0a7c: 0x17a8, 0x0a7d: 0x17ad, 0x0a7e: 0x0699, 0x0a7f: 0x069d,
+ // Block 0x2a, offset 0xa80
+ 0x0a80: 0x1069, 0x0a81: 0x17b7, 0x0a82: 0x17b2, 0x0a83: 0x17bc, 0x0a84: 0x17c1, 0x0a85: 0x1071,
+ 0x0a86: 0x1075, 0x0a87: 0x1075, 0x0a88: 0x107d, 0x0a89: 0x06a5, 0x0a8a: 0x1081, 0x0a8b: 0x06a9,
+ 0x0a8c: 0x06ad, 0x0a8d: 0x17cb, 0x0a8e: 0x1095, 0x0a8f: 0x109d, 0x0a90: 0x10a9, 0x0a91: 0x06b1,
+ 0x0a92: 0x17d0, 0x0a93: 0x10cd, 0x0a94: 0x17d5, 0x0a95: 0x17da, 0x0a96: 0x10ed, 0x0a97: 0x1105,
+ 0x0a98: 0x06b5, 0x0a99: 0x110d, 0x0a9a: 0x1111, 0x0a9b: 0x1115, 0x0a9c: 0x17df, 0x0a9d: 0x17e4,
+ 0x0a9e: 0x17e4, 0x0a9f: 0x112d, 0x0aa0: 0x06b9, 0x0aa1: 0x17e9, 0x0aa2: 0x1141, 0x0aa3: 0x1145,
+ 0x0aa4: 0x06bd, 0x0aa5: 0x17ee, 0x0aa6: 0x1161, 0x0aa7: 0x06c1, 0x0aa8: 0x1171, 0x0aa9: 0x1169,
+ 0x0aaa: 0x1179, 0x0aab: 0x17f8, 0x0aac: 0x1191, 0x0aad: 0x06c5, 0x0aae: 0x119d, 0x0aaf: 0x11a5,
+ 0x0ab0: 0x11b5, 0x0ab1: 0x06c9, 0x0ab2: 0x1802, 0x0ab3: 0x1807, 0x0ab4: 0x06cd, 0x0ab5: 0x180c,
+ 0x0ab6: 0x11cd, 0x0ab7: 0x1811, 0x0ab8: 0x11d9, 0x0ab9: 0x11e5, 0x0aba: 0x11ed, 0x0abb: 0x1816,
+ 0x0abc: 0x181b, 0x0abd: 0x1201, 0x0abe: 0x1820, 0x0abf: 0x1209,
+ // Block 0x2b, offset 0xac0
+ 0x0ac0: 0x1730, 0x0ac1: 0x06d1, 0x0ac2: 0x1221, 0x0ac3: 0x1225, 0x0ac4: 0x06d9, 0x0ac5: 0x1229,
+ 0x0ac6: 0x0aa5, 0x0ac7: 0x1825, 0x0ac8: 0x182a, 0x0ac9: 0x1735, 0x0aca: 0x173a, 0x0acb: 0x1249,
+ 0x0acc: 0x124d, 0x0acd: 0x1465, 0x0ace: 0x06dd, 0x0acf: 0x1279, 0x0ad0: 0x1275, 0x0ad1: 0x127d,
+ 0x0ad2: 0x08b1, 0x0ad3: 0x1281, 0x0ad4: 0x1285, 0x0ad5: 0x1289, 0x0ad6: 0x1291, 0x0ad7: 0x182f,
+ 0x0ad8: 0x128d, 0x0ad9: 0x1295, 0x0ada: 0x12a9, 0x0adb: 0x12ad, 0x0adc: 0x1299, 0x0add: 0x12b1,
+ 0x0ade: 0x12c5, 0x0adf: 0x12d9, 0x0ae0: 0x12a5, 0x0ae1: 0x12b9, 0x0ae2: 0x12bd, 0x0ae3: 0x12c1,
+ 0x0ae4: 0x1834, 0x0ae5: 0x183e, 0x0ae6: 0x1839, 0x0ae7: 0x06e1, 0x0ae8: 0x12e1, 0x0ae9: 0x12e5,
+ 0x0aea: 0x12ed, 0x0aeb: 0x1852, 0x0aec: 0x12f1, 0x0aed: 0x1843, 0x0aee: 0x06e5, 0x0aef: 0x06e9,
+ 0x0af0: 0x1848, 0x0af1: 0x184d, 0x0af2: 0x06ed, 0x0af3: 0x1311, 0x0af4: 0x1315, 0x0af5: 0x1319,
+ 0x0af6: 0x131d, 0x0af7: 0x1329, 0x0af8: 0x1325, 0x0af9: 0x1331, 0x0afa: 0x132d, 0x0afb: 0x133d,
+ 0x0afc: 0x1335, 0x0afd: 0x1339, 0x0afe: 0x1341, 0x0aff: 0x06f1,
+ // Block 0x2c, offset 0xb00
+ 0x0b00: 0x1349, 0x0b01: 0x134d, 0x0b02: 0x06f5, 0x0b03: 0x135d, 0x0b04: 0x1361, 0x0b05: 0x1857,
+ 0x0b06: 0x136d, 0x0b07: 0x1371, 0x0b08: 0x06f9, 0x0b09: 0x137d, 0x0b0a: 0x062d, 0x0b0b: 0x185c,
+ 0x0b0c: 0x1861, 0x0b0d: 0x06fd, 0x0b0e: 0x0701, 0x0b0f: 0x13a9, 0x0b10: 0x13c1, 0x0b11: 0x13dd,
+ 0x0b12: 0x13ed, 0x0b13: 0x1866, 0x0b14: 0x1401, 0x0b15: 0x1405, 0x0b16: 0x141d, 0x0b17: 0x1429,
+ 0x0b18: 0x1870, 0x0b19: 0x16c2, 0x0b1a: 0x1435, 0x0b1b: 0x1431, 0x0b1c: 0x143d, 0x0b1d: 0x16c7,
+ 0x0b1e: 0x1449, 0x0b1f: 0x1455, 0x0b20: 0x1875, 0x0b21: 0x187a, 0x0b22: 0x1495, 0x0b23: 0x14a1,
+ 0x0b24: 0x14a9, 0x0b25: 0x187f, 0x0b26: 0x14ad, 0x0b27: 0x14d5, 0x0b28: 0x14e1, 0x0b29: 0x14e5,
+ 0x0b2a: 0x14dd, 0x0b2b: 0x14f1, 0x0b2c: 0x14f5, 0x0b2d: 0x1884, 0x0b2e: 0x1501, 0x0b2f: 0x0705,
+ 0x0b30: 0x1509, 0x0b31: 0x1889, 0x0b32: 0x0709, 0x0b33: 0x1541, 0x0b34: 0x0b35, 0x0b35: 0x1559,
+ 0x0b36: 0x188e, 0x0b37: 0x1898, 0x0b38: 0x070d, 0x0b39: 0x0711, 0x0b3a: 0x1581, 0x0b3b: 0x189d,
+ 0x0b3c: 0x0715, 0x0b3d: 0x18a2, 0x0b3e: 0x1599, 0x0b3f: 0x1599,
+ // Block 0x2d, offset 0xb40
+ 0x0b40: 0x15a1, 0x0b41: 0x18a7, 0x0b42: 0x15b9, 0x0b43: 0x0719, 0x0b44: 0x15c9, 0x0b45: 0x15d5,
+ 0x0b46: 0x15dd, 0x0b47: 0x15e5, 0x0b48: 0x071d, 0x0b49: 0x18ac, 0x0b4a: 0x15f9, 0x0b4b: 0x1615,
+ 0x0b4c: 0x1621, 0x0b4d: 0x0721, 0x0b4e: 0x0725, 0x0b4f: 0x1625, 0x0b50: 0x18b1, 0x0b51: 0x0729,
+ 0x0b52: 0x18b6, 0x0b53: 0x18bb, 0x0b54: 0x18c0, 0x0b55: 0x1649, 0x0b56: 0x072d, 0x0b57: 0x165d,
+ 0x0b58: 0x1665, 0x0b59: 0x1669, 0x0b5a: 0x1671, 0x0b5b: 0x1679, 0x0b5c: 0x1681, 0x0b5d: 0x18ca,
+}
+
+// nfcSparseOffset: 98 entries, 196 bytes
+var nfcSparseOffset = []uint16{0x0, 0x2, 0x6, 0x8, 0x13, 0x23, 0x25, 0x2a, 0x35, 0x44, 0x51, 0x59, 0x5d, 0x62, 0x64, 0x73, 0x7b, 0x82, 0x85, 0x8d, 0x91, 0x95, 0x97, 0x99, 0xa2, 0xa6, 0xad, 0xb2, 0xb5, 0xbf, 0xc1, 0xc8, 0xd0, 0xd3, 0xd5, 0xd7, 0xd9, 0xde, 0xed, 0xf9, 0xfb, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10b, 0x10d, 0x110, 0x113, 0x115, 0x118, 0x11b, 0x11f, 0x128, 0x12a, 0x12d, 0x12f, 0x139, 0x148, 0x14a, 0x158, 0x15b, 0x161, 0x167, 0x172, 0x176, 0x178, 0x17a, 0x17c, 0x17e, 0x180, 0x186, 0x189, 0x18b, 0x18d, 0x18f, 0x192, 0x194, 0x196, 0x198, 0x19a, 0x1a0, 0x1a3, 0x1a5, 0x1a7, 0x1a9, 0x1b7, 0x1c0, 0x1c2, 0x1c4, 0x1ca, 0x1d2, 0x1d9, 0x1dc, 0x1e9, 0x1f3, 0x1f5}
+
+// nfcSparseValues: 503 entries, 2012 bytes
+var nfcSparseValues = [503]valueRange{
+ // Block 0x0, offset 0x1
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8800, lo: 0xa8, hi: 0xa8},
+ // Block 0x1, offset 0x2
+ {value: 0x0091, lo: 0x03},
+ {value: 0x46c8, lo: 0xa0, hi: 0xa1},
+ {value: 0x46fa, lo: 0xaf, hi: 0xb0},
+ {value: 0x8800, lo: 0xb7, hi: 0xb7},
+ // Block 0x2, offset 0x3
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8800, lo: 0x92, hi: 0x92},
+ // Block 0x3, offset 0x4
+ {value: 0x0006, lo: 0x0a},
+ {value: 0x8800, lo: 0x81, hi: 0x81},
+ {value: 0x8800, lo: 0x85, hi: 0x85},
+ {value: 0x8800, lo: 0x89, hi: 0x89},
+ {value: 0x4826, lo: 0x8a, hi: 0x8a},
+ {value: 0x4844, lo: 0x8b, hi: 0x8b},
+ {value: 0x36b1, lo: 0x8c, hi: 0x8c},
+ {value: 0x36c9, lo: 0x8d, hi: 0x8d},
+ {value: 0x485c, lo: 0x8e, hi: 0x8e},
+ {value: 0x8800, lo: 0x92, hi: 0x92},
+ {value: 0x36e7, lo: 0x93, hi: 0x94},
+ // Block 0x4, offset 0x5
+ {value: 0x0000, lo: 0x0f},
+ {value: 0x8800, lo: 0x83, hi: 0x83},
+ {value: 0x8800, lo: 0x87, hi: 0x87},
+ {value: 0x8800, lo: 0x8b, hi: 0x8b},
+ {value: 0x8800, lo: 0x8d, hi: 0x8d},
+ {value: 0x378f, lo: 0x90, hi: 0x90},
+ {value: 0x379b, lo: 0x91, hi: 0x91},
+ {value: 0x3789, lo: 0x93, hi: 0x93},
+ {value: 0x8800, lo: 0x96, hi: 0x96},
+ {value: 0x3801, lo: 0x97, hi: 0x97},
+ {value: 0x37cb, lo: 0x9c, hi: 0x9c},
+ {value: 0x37b3, lo: 0x9d, hi: 0x9d},
+ {value: 0x37dd, lo: 0x9e, hi: 0x9e},
+ {value: 0x8800, lo: 0xb4, hi: 0xb5},
+ {value: 0x3807, lo: 0xb6, hi: 0xb6},
+ {value: 0x380d, lo: 0xb7, hi: 0xb7},
+ // Block 0x5, offset 0x6
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0x83, hi: 0x87},
+ // Block 0x6, offset 0x7
+ {value: 0x0001, lo: 0x04},
+ {value: 0x8018, lo: 0x81, hi: 0x82},
+ {value: 0x80e6, lo: 0x84, hi: 0x84},
+ {value: 0x80dc, lo: 0x85, hi: 0x85},
+ {value: 0x8012, lo: 0x87, hi: 0x87},
+ // Block 0x7, offset 0x8
+ {value: 0x0000, lo: 0x0a},
+ {value: 0x80e6, lo: 0x90, hi: 0x97},
+ {value: 0x801e, lo: 0x98, hi: 0x98},
+ {value: 0x801f, lo: 0x99, hi: 0x99},
+ {value: 0x8020, lo: 0x9a, hi: 0x9a},
+ {value: 0x382b, lo: 0xa2, hi: 0xa2},
+ {value: 0x3831, lo: 0xa3, hi: 0xa3},
+ {value: 0x383d, lo: 0xa4, hi: 0xa4},
+ {value: 0x3837, lo: 0xa5, hi: 0xa5},
+ {value: 0x3843, lo: 0xa6, hi: 0xa6},
+ {value: 0x8800, lo: 0xa7, hi: 0xa7},
+ // Block 0x8, offset 0x9
+ {value: 0x0000, lo: 0x0e},
+ {value: 0x3855, lo: 0x80, hi: 0x80},
+ {value: 0x8800, lo: 0x81, hi: 0x81},
+ {value: 0x3849, lo: 0x82, hi: 0x82},
+ {value: 0x8800, lo: 0x92, hi: 0x92},
+ {value: 0x384f, lo: 0x93, hi: 0x93},
+ {value: 0x8800, lo: 0x95, hi: 0x95},
+ {value: 0x80e6, lo: 0x96, hi: 0x9c},
+ {value: 0x80e6, lo: 0x9f, hi: 0xa2},
+ {value: 0x80dc, lo: 0xa3, hi: 0xa3},
+ {value: 0x80e6, lo: 0xa4, hi: 0xa4},
+ {value: 0x80e6, lo: 0xa7, hi: 0xa8},
+ {value: 0x80dc, lo: 0xaa, hi: 0xaa},
+ {value: 0x80e6, lo: 0xab, hi: 0xac},
+ {value: 0x80dc, lo: 0xad, hi: 0xad},
+ // Block 0x9, offset 0xa
+ {value: 0x0000, lo: 0x0c},
+ {value: 0x8024, lo: 0x91, hi: 0x91},
+ {value: 0x80e6, lo: 0xb0, hi: 0xb0},
+ {value: 0x80dc, lo: 0xb1, hi: 0xb1},
+ {value: 0x80e6, lo: 0xb2, hi: 0xb3},
+ {value: 0x80dc, lo: 0xb4, hi: 0xb4},
+ {value: 0x80e6, lo: 0xb5, hi: 0xb6},
+ {value: 0x80dc, lo: 0xb7, hi: 0xb9},
+ {value: 0x80e6, lo: 0xba, hi: 0xba},
+ {value: 0x80dc, lo: 0xbb, hi: 0xbc},
+ {value: 0x80e6, lo: 0xbd, hi: 0xbd},
+ {value: 0x80dc, lo: 0xbe, hi: 0xbe},
+ {value: 0x80e6, lo: 0xbf, hi: 0xbf},
+ // Block 0xa, offset 0xb
+ {value: 0x000a, lo: 0x07},
+ {value: 0x80e6, lo: 0x80, hi: 0x80},
+ {value: 0x80e6, lo: 0x81, hi: 0x81},
+ {value: 0x80dc, lo: 0x82, hi: 0x83},
+ {value: 0x80dc, lo: 0x84, hi: 0x85},
+ {value: 0x80dc, lo: 0x86, hi: 0x87},
+ {value: 0x80dc, lo: 0x88, hi: 0x89},
+ {value: 0x80e6, lo: 0x8a, hi: 0x8a},
+ // Block 0xb, offset 0xc
+ {value: 0x0000, lo: 0x03},
+ {value: 0x80e6, lo: 0xab, hi: 0xb1},
+ {value: 0x80dc, lo: 0xb2, hi: 0xb2},
+ {value: 0x80e6, lo: 0xb3, hi: 0xb3},
+ // Block 0xc, offset 0xd
+ {value: 0x0000, lo: 0x04},
+ {value: 0x80e6, lo: 0x96, hi: 0x99},
+ {value: 0x80e6, lo: 0x9b, hi: 0xa3},
+ {value: 0x80e6, lo: 0xa5, hi: 0xa7},
+ {value: 0x80e6, lo: 0xa9, hi: 0xad},
+ // Block 0xd, offset 0xe
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80dc, lo: 0x99, hi: 0x9b},
+ // Block 0xe, offset 0xf
+ {value: 0x0000, lo: 0x0e},
+ {value: 0x80e6, lo: 0xa4, hi: 0xa5},
+ {value: 0x80dc, lo: 0xa6, hi: 0xa6},
+ {value: 0x80e6, lo: 0xa7, hi: 0xa8},
+ {value: 0x80dc, lo: 0xa9, hi: 0xa9},
+ {value: 0x80e6, lo: 0xaa, hi: 0xac},
+ {value: 0x80dc, lo: 0xad, hi: 0xaf},
+ {value: 0x801b, lo: 0xb0, hi: 0xb0},
+ {value: 0x801c, lo: 0xb1, hi: 0xb1},
+ {value: 0x801d, lo: 0xb2, hi: 0xb2},
+ {value: 0x80e6, lo: 0xb3, hi: 0xb5},
+ {value: 0x80dc, lo: 0xb6, hi: 0xb6},
+ {value: 0x80e6, lo: 0xb7, hi: 0xb8},
+ {value: 0x80dc, lo: 0xb9, hi: 0xba},
+ {value: 0x80e6, lo: 0xbb, hi: 0xbe},
+ // Block 0xf, offset 0x10
+ {value: 0x0000, lo: 0x07},
+ {value: 0x8800, lo: 0xa8, hi: 0xa8},
+ {value: 0x3ec2, lo: 0xa9, hi: 0xa9},
+ {value: 0x8800, lo: 0xb0, hi: 0xb0},
+ {value: 0x3eca, lo: 0xb1, hi: 0xb1},
+ {value: 0x8800, lo: 0xb3, hi: 0xb3},
+ {value: 0x3ed2, lo: 0xb4, hi: 0xb4},
+ {value: 0x8607, lo: 0xbc, hi: 0xbc},
+ // Block 0x10, offset 0x11
+ {value: 0x0008, lo: 0x06},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x80e6, lo: 0x91, hi: 0x91},
+ {value: 0x80dc, lo: 0x92, hi: 0x92},
+ {value: 0x80e6, lo: 0x93, hi: 0x93},
+ {value: 0x80e6, lo: 0x94, hi: 0x94},
+ {value: 0x4502, lo: 0x98, hi: 0x9f},
+ // Block 0x11, offset 0x12
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8007, lo: 0xbc, hi: 0xbc},
+ {value: 0x8600, lo: 0xbe, hi: 0xbe},
+ // Block 0x12, offset 0x13
+ {value: 0x0007, lo: 0x07},
+ {value: 0x8800, lo: 0x87, hi: 0x87},
+ {value: 0x18cf, lo: 0x8b, hi: 0x8c},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x8600, lo: 0x97, hi: 0x97},
+ {value: 0x4542, lo: 0x9c, hi: 0x9c},
+ {value: 0x454a, lo: 0x9d, hi: 0x9d},
+ {value: 0x4552, lo: 0x9f, hi: 0x9f},
+ // Block 0x13, offset 0x14
+ {value: 0x0000, lo: 0x03},
+ {value: 0x457a, lo: 0xb3, hi: 0xb3},
+ {value: 0x4582, lo: 0xb6, hi: 0xb6},
+ {value: 0x8007, lo: 0xbc, hi: 0xbc},
+ // Block 0x14, offset 0x15
+ {value: 0x0008, lo: 0x03},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x455a, lo: 0x99, hi: 0x9b},
+ {value: 0x4572, lo: 0x9e, hi: 0x9e},
+ // Block 0x15, offset 0x16
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8007, lo: 0xbc, hi: 0xbc},
+ // Block 0x16, offset 0x17
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ // Block 0x17, offset 0x18
+ {value: 0x0000, lo: 0x08},
+ {value: 0x8800, lo: 0x87, hi: 0x87},
+ {value: 0x18e4, lo: 0x88, hi: 0x88},
+ {value: 0x18dd, lo: 0x8b, hi: 0x8b},
+ {value: 0x18eb, lo: 0x8c, hi: 0x8c},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x8600, lo: 0x96, hi: 0x97},
+ {value: 0x458a, lo: 0x9c, hi: 0x9c},
+ {value: 0x4592, lo: 0x9d, hi: 0x9d},
+ // Block 0x18, offset 0x19
+ {value: 0x0000, lo: 0x03},
+ {value: 0x8800, lo: 0x92, hi: 0x92},
+ {value: 0x18f2, lo: 0x94, hi: 0x94},
+ {value: 0x8600, lo: 0xbe, hi: 0xbe},
+ // Block 0x19, offset 0x1a
+ {value: 0x0000, lo: 0x06},
+ {value: 0x8800, lo: 0x86, hi: 0x87},
+ {value: 0x18f9, lo: 0x8a, hi: 0x8a},
+ {value: 0x1907, lo: 0x8b, hi: 0x8b},
+ {value: 0x1900, lo: 0x8c, hi: 0x8c},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x8600, lo: 0x97, hi: 0x97},
+ // Block 0x1a, offset 0x1b
+ {value: 0x0607, lo: 0x04},
+ {value: 0x8800, lo: 0x86, hi: 0x86},
+ {value: 0x3eda, lo: 0x88, hi: 0x88},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x8054, lo: 0x95, hi: 0x96},
+ // Block 0x1b, offset 0x1c
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8007, lo: 0xbc, hi: 0xbc},
+ {value: 0x8800, lo: 0xbf, hi: 0xbf},
+ // Block 0x1c, offset 0x1d
+ {value: 0x0000, lo: 0x09},
+ {value: 0x190e, lo: 0x80, hi: 0x80},
+ {value: 0x8600, lo: 0x82, hi: 0x82},
+ {value: 0x8800, lo: 0x86, hi: 0x86},
+ {value: 0x1915, lo: 0x87, hi: 0x87},
+ {value: 0x191c, lo: 0x88, hi: 0x88},
+ {value: 0x2e66, lo: 0x8a, hi: 0x8a},
+ {value: 0x19a5, lo: 0x8b, hi: 0x8b},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x8600, lo: 0x95, hi: 0x96},
+ // Block 0x1d, offset 0x1e
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8600, lo: 0xbe, hi: 0xbe},
+ // Block 0x1e, offset 0x1f
+ {value: 0x0000, lo: 0x06},
+ {value: 0x8800, lo: 0x86, hi: 0x87},
+ {value: 0x1923, lo: 0x8a, hi: 0x8a},
+ {value: 0x1931, lo: 0x8b, hi: 0x8b},
+ {value: 0x192a, lo: 0x8c, hi: 0x8c},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x8600, lo: 0x97, hi: 0x97},
+ // Block 0x1f, offset 0x20
+ {value: 0x0007, lo: 0x07},
+ {value: 0x8609, lo: 0x8a, hi: 0x8a},
+ {value: 0x8600, lo: 0x8f, hi: 0x8f},
+ {value: 0x8800, lo: 0x99, hi: 0x99},
+ {value: 0x3ee2, lo: 0x9a, hi: 0x9a},
+ {value: 0x2e6d, lo: 0x9c, hi: 0x9d},
+ {value: 0x1938, lo: 0x9e, hi: 0x9e},
+ {value: 0x8600, lo: 0x9f, hi: 0x9f},
+ // Block 0x20, offset 0x21
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8067, lo: 0xb8, hi: 0xb9},
+ {value: 0x8009, lo: 0xba, hi: 0xba},
+ // Block 0x21, offset 0x22
+ {value: 0x0000, lo: 0x01},
+ {value: 0x806b, lo: 0x88, hi: 0x8b},
+ // Block 0x22, offset 0x23
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8076, lo: 0xb8, hi: 0xb9},
+ // Block 0x23, offset 0x24
+ {value: 0x0000, lo: 0x01},
+ {value: 0x807a, lo: 0x88, hi: 0x8b},
+ // Block 0x24, offset 0x25
+ {value: 0x0000, lo: 0x04},
+ {value: 0x80dc, lo: 0x98, hi: 0x99},
+ {value: 0x80dc, lo: 0xb5, hi: 0xb5},
+ {value: 0x80dc, lo: 0xb7, hi: 0xb7},
+ {value: 0x80d8, lo: 0xb9, hi: 0xb9},
+ // Block 0x25, offset 0x26
+ {value: 0x0000, lo: 0x0e},
+ {value: 0x2786, lo: 0x83, hi: 0x83},
+ {value: 0x278d, lo: 0x8d, hi: 0x8d},
+ {value: 0x2794, lo: 0x92, hi: 0x92},
+ {value: 0x279b, lo: 0x97, hi: 0x97},
+ {value: 0x27a2, lo: 0x9c, hi: 0x9c},
+ {value: 0x277f, lo: 0xa9, hi: 0xa9},
+ {value: 0x8081, lo: 0xb1, hi: 0xb1},
+ {value: 0x8082, lo: 0xb2, hi: 0xb2},
+ {value: 0x49b6, lo: 0xb3, hi: 0xb3},
+ {value: 0x8084, lo: 0xb4, hi: 0xb4},
+ {value: 0x49bf, lo: 0xb5, hi: 0xb5},
+ {value: 0x459a, lo: 0xb6, hi: 0xb6},
+ {value: 0x45a2, lo: 0xb8, hi: 0xb8},
+ {value: 0x8082, lo: 0xba, hi: 0xbd},
+ // Block 0x26, offset 0x27
+ {value: 0x0000, lo: 0x0b},
+ {value: 0x8082, lo: 0x80, hi: 0x80},
+ {value: 0x49c8, lo: 0x81, hi: 0x81},
+ {value: 0x80e6, lo: 0x82, hi: 0x83},
+ {value: 0x8009, lo: 0x84, hi: 0x84},
+ {value: 0x80e6, lo: 0x86, hi: 0x87},
+ {value: 0x27b0, lo: 0x93, hi: 0x93},
+ {value: 0x27b7, lo: 0x9d, hi: 0x9d},
+ {value: 0x27be, lo: 0xa2, hi: 0xa2},
+ {value: 0x27c5, lo: 0xa7, hi: 0xa7},
+ {value: 0x27cc, lo: 0xac, hi: 0xac},
+ {value: 0x27a9, lo: 0xb9, hi: 0xb9},
+ // Block 0x27, offset 0x28
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80dc, lo: 0x86, hi: 0x86},
+ // Block 0x28, offset 0x29
+ {value: 0x0000, lo: 0x05},
+ {value: 0x8800, lo: 0xa5, hi: 0xa5},
+ {value: 0x193f, lo: 0xa6, hi: 0xa6},
+ {value: 0x8600, lo: 0xae, hi: 0xae},
+ {value: 0x8007, lo: 0xb7, hi: 0xb7},
+ {value: 0x8009, lo: 0xb9, hi: 0xba},
+ // Block 0x29, offset 0x2a
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80dc, lo: 0x8d, hi: 0x8d},
+ // Block 0x2a, offset 0x2b
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8800, lo: 0x80, hi: 0x92},
+ // Block 0x2b, offset 0x2c
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8e00, lo: 0xa1, hi: 0xb5},
+ // Block 0x2c, offset 0x2d
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8600, lo: 0xa8, hi: 0xbf},
+ // Block 0x2d, offset 0x2e
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8600, lo: 0x80, hi: 0x82},
+ // Block 0x2e, offset 0x2f
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0x9d, hi: 0x9f},
+ // Block 0x2f, offset 0x30
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8009, lo: 0x94, hi: 0x94},
+ {value: 0x8009, lo: 0xb4, hi: 0xb4},
+ // Block 0x30, offset 0x31
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8009, lo: 0x92, hi: 0x92},
+ {value: 0x80e6, lo: 0x9d, hi: 0x9d},
+ // Block 0x31, offset 0x32
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e4, lo: 0xa9, hi: 0xa9},
+ // Block 0x32, offset 0x33
+ {value: 0x0008, lo: 0x02},
+ {value: 0x80de, lo: 0xb9, hi: 0xba},
+ {value: 0x80dc, lo: 0xbb, hi: 0xbb},
+ // Block 0x33, offset 0x34
+ {value: 0x0000, lo: 0x02},
+ {value: 0x80e6, lo: 0x97, hi: 0x97},
+ {value: 0x80dc, lo: 0x98, hi: 0x98},
+ // Block 0x34, offset 0x35
+ {value: 0x0000, lo: 0x03},
+ {value: 0x8009, lo: 0xa0, hi: 0xa0},
+ {value: 0x80e6, lo: 0xb5, hi: 0xbc},
+ {value: 0x80dc, lo: 0xbf, hi: 0xbf},
+ // Block 0x35, offset 0x36
+ {value: 0x0000, lo: 0x08},
+ {value: 0x197e, lo: 0x80, hi: 0x80},
+ {value: 0x1985, lo: 0x81, hi: 0x81},
+ {value: 0x8800, lo: 0x82, hi: 0x82},
+ {value: 0x198c, lo: 0x83, hi: 0x83},
+ {value: 0x8009, lo: 0x84, hi: 0x84},
+ {value: 0x80e6, lo: 0xab, hi: 0xab},
+ {value: 0x80dc, lo: 0xac, hi: 0xac},
+ {value: 0x80e6, lo: 0xad, hi: 0xb3},
+ // Block 0x36, offset 0x37
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0xaa, hi: 0xab},
+ // Block 0x37, offset 0x38
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8007, lo: 0xa6, hi: 0xa6},
+ {value: 0x8009, lo: 0xb2, hi: 0xb3},
+ // Block 0x38, offset 0x39
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8007, lo: 0xb7, hi: 0xb7},
+ // Block 0x39, offset 0x3a
+ {value: 0x0000, lo: 0x09},
+ {value: 0x80e6, lo: 0x90, hi: 0x92},
+ {value: 0x8001, lo: 0x94, hi: 0x94},
+ {value: 0x80dc, lo: 0x95, hi: 0x99},
+ {value: 0x80e6, lo: 0x9a, hi: 0x9b},
+ {value: 0x80dc, lo: 0x9c, hi: 0x9f},
+ {value: 0x80e6, lo: 0xa0, hi: 0xa0},
+ {value: 0x8001, lo: 0xa2, hi: 0xa8},
+ {value: 0x80dc, lo: 0xad, hi: 0xad},
+ {value: 0x80e6, lo: 0xb4, hi: 0xb4},
+ // Block 0x3a, offset 0x3b
+ {value: 0x0000, lo: 0x0e},
+ {value: 0x80e6, lo: 0x80, hi: 0x81},
+ {value: 0x80dc, lo: 0x82, hi: 0x82},
+ {value: 0x80e6, lo: 0x83, hi: 0x89},
+ {value: 0x80dc, lo: 0x8a, hi: 0x8a},
+ {value: 0x80e6, lo: 0x8b, hi: 0x8c},
+ {value: 0x80ea, lo: 0x8d, hi: 0x8d},
+ {value: 0x80d6, lo: 0x8e, hi: 0x8e},
+ {value: 0x80dc, lo: 0x8f, hi: 0x8f},
+ {value: 0x80ca, lo: 0x90, hi: 0x90},
+ {value: 0x80e6, lo: 0x91, hi: 0xa6},
+ {value: 0x80e9, lo: 0xbc, hi: 0xbc},
+ {value: 0x80dc, lo: 0xbd, hi: 0xbd},
+ {value: 0x80e6, lo: 0xbe, hi: 0xbe},
+ {value: 0x80dc, lo: 0xbf, hi: 0xbf},
+ // Block 0x3b, offset 0x3c
+ {value: 0x0004, lo: 0x01},
+ {value: 0x04a5, lo: 0x80, hi: 0x81},
+ // Block 0x3c, offset 0x3d
+ {value: 0x0000, lo: 0x0d},
+ {value: 0x80e6, lo: 0x90, hi: 0x91},
+ {value: 0x8001, lo: 0x92, hi: 0x93},
+ {value: 0x80e6, lo: 0x94, hi: 0x97},
+ {value: 0x8001, lo: 0x98, hi: 0x9a},
+ {value: 0x80e6, lo: 0x9b, hi: 0x9c},
+ {value: 0x80e6, lo: 0xa1, hi: 0xa1},
+ {value: 0x8001, lo: 0xa5, hi: 0xa6},
+ {value: 0x80e6, lo: 0xa7, hi: 0xa7},
+ {value: 0x80dc, lo: 0xa8, hi: 0xa8},
+ {value: 0x80e6, lo: 0xa9, hi: 0xa9},
+ {value: 0x8001, lo: 0xaa, hi: 0xab},
+ {value: 0x80dc, lo: 0xac, hi: 0xaf},
+ {value: 0x80e6, lo: 0xb0, hi: 0xb0},
+ // Block 0x3d, offset 0x3e
+ {value: 0x4261, lo: 0x02},
+ {value: 0x01b8, lo: 0xa6, hi: 0xa6},
+ {value: 0x0057, lo: 0xaa, hi: 0xab},
+ // Block 0x3e, offset 0x3f
+ {value: 0x0007, lo: 0x05},
+ {value: 0x8800, lo: 0x90, hi: 0x90},
+ {value: 0x8800, lo: 0x92, hi: 0x92},
+ {value: 0x8800, lo: 0x94, hi: 0x94},
+ {value: 0x3ba3, lo: 0x9a, hi: 0x9b},
+ {value: 0x3bb1, lo: 0xae, hi: 0xae},
+ // Block 0x3f, offset 0x40
+ {value: 0x000e, lo: 0x05},
+ {value: 0x3bb8, lo: 0x8d, hi: 0x8e},
+ {value: 0x3bbf, lo: 0x8f, hi: 0x8f},
+ {value: 0x8800, lo: 0x90, hi: 0x90},
+ {value: 0x8800, lo: 0x92, hi: 0x92},
+ {value: 0x8800, lo: 0x94, hi: 0x94},
+ // Block 0x40, offset 0x41
+ {value: 0x4c1e, lo: 0x0a},
+ {value: 0x8800, lo: 0x83, hi: 0x83},
+ {value: 0x3bcd, lo: 0x84, hi: 0x84},
+ {value: 0x8800, lo: 0x88, hi: 0x88},
+ {value: 0x3bd4, lo: 0x89, hi: 0x89},
+ {value: 0x8800, lo: 0x8b, hi: 0x8b},
+ {value: 0x3bdb, lo: 0x8c, hi: 0x8c},
+ {value: 0x8800, lo: 0xa3, hi: 0xa3},
+ {value: 0x3be2, lo: 0xa4, hi: 0xa5},
+ {value: 0x3be9, lo: 0xa6, hi: 0xa6},
+ {value: 0x8800, lo: 0xbc, hi: 0xbc},
+ // Block 0x41, offset 0x42
+ {value: 0x0007, lo: 0x03},
+ {value: 0x3c52, lo: 0xa0, hi: 0xa1},
+ {value: 0x3c7c, lo: 0xa2, hi: 0xa3},
+ {value: 0x3ca6, lo: 0xaa, hi: 0xad},
+ // Block 0x42, offset 0x43
+ {value: 0x0004, lo: 0x01},
+ {value: 0x04fd, lo: 0xa9, hi: 0xaa},
+ // Block 0x43, offset 0x44
+ {value: 0x0000, lo: 0x01},
+ {value: 0x44c3, lo: 0x9c, hi: 0x9c},
+ // Block 0x44, offset 0x45
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0xaf, hi: 0xb1},
+ // Block 0x45, offset 0x46
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0xbf, hi: 0xbf},
+ // Block 0x46, offset 0x47
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0xa0, hi: 0xbf},
+ // Block 0x47, offset 0x48
+ {value: 0x0000, lo: 0x05},
+ {value: 0x80da, lo: 0xaa, hi: 0xaa},
+ {value: 0x80e4, lo: 0xab, hi: 0xab},
+ {value: 0x80e8, lo: 0xac, hi: 0xac},
+ {value: 0x80de, lo: 0xad, hi: 0xad},
+ {value: 0x80e0, lo: 0xae, hi: 0xaf},
+ // Block 0x48, offset 0x49
+ {value: 0x0000, lo: 0x02},
+ {value: 0x80e6, lo: 0xaf, hi: 0xaf},
+ {value: 0x80e6, lo: 0xb4, hi: 0xbd},
+ // Block 0x49, offset 0x4a
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0x9f, hi: 0x9f},
+ // Block 0x4a, offset 0x4b
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0xb0, hi: 0xb1},
+ // Block 0x4b, offset 0x4c
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0x86, hi: 0x86},
+ // Block 0x4c, offset 0x4d
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8009, lo: 0x84, hi: 0x84},
+ {value: 0x80e6, lo: 0xa0, hi: 0xb1},
+ // Block 0x4d, offset 0x4e
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80dc, lo: 0xab, hi: 0xad},
+ // Block 0x4e, offset 0x4f
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0x93, hi: 0x93},
+ // Block 0x4f, offset 0x50
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8007, lo: 0xb3, hi: 0xb3},
+ // Block 0x50, offset 0x51
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0x80, hi: 0x80},
+ // Block 0x51, offset 0x52
+ {value: 0x0000, lo: 0x05},
+ {value: 0x80e6, lo: 0xb0, hi: 0xb0},
+ {value: 0x80e6, lo: 0xb2, hi: 0xb3},
+ {value: 0x80dc, lo: 0xb4, hi: 0xb4},
+ {value: 0x80e6, lo: 0xb7, hi: 0xb8},
+ {value: 0x80e6, lo: 0xbe, hi: 0xbf},
+ // Block 0x52, offset 0x53
+ {value: 0x0000, lo: 0x02},
+ {value: 0x80e6, lo: 0x81, hi: 0x81},
+ {value: 0x8009, lo: 0xb6, hi: 0xb6},
+ // Block 0x53, offset 0x54
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0xad, hi: 0xad},
+ // Block 0x54, offset 0x55
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8100, lo: 0x80, hi: 0xbf},
+ // Block 0x55, offset 0x56
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8100, lo: 0x80, hi: 0xa3},
+ // Block 0x56, offset 0x57
+ {value: 0x0006, lo: 0x0d},
+ {value: 0x4376, lo: 0x9d, hi: 0x9d},
+ {value: 0x801a, lo: 0x9e, hi: 0x9e},
+ {value: 0x43e8, lo: 0x9f, hi: 0x9f},
+ {value: 0x43d6, lo: 0xaa, hi: 0xab},
+ {value: 0x44da, lo: 0xac, hi: 0xac},
+ {value: 0x44e2, lo: 0xad, hi: 0xad},
+ {value: 0x432e, lo: 0xae, hi: 0xb1},
+ {value: 0x434c, lo: 0xb2, hi: 0xb4},
+ {value: 0x4364, lo: 0xb5, hi: 0xb6},
+ {value: 0x4370, lo: 0xb8, hi: 0xb8},
+ {value: 0x437c, lo: 0xb9, hi: 0xbb},
+ {value: 0x4394, lo: 0xbc, hi: 0xbc},
+ {value: 0x439a, lo: 0xbe, hi: 0xbe},
+ // Block 0x57, offset 0x58
+ {value: 0x0006, lo: 0x08},
+ {value: 0x43a0, lo: 0x80, hi: 0x81},
+ {value: 0x43ac, lo: 0x83, hi: 0x84},
+ {value: 0x43be, lo: 0x86, hi: 0x89},
+ {value: 0x43e2, lo: 0x8a, hi: 0x8a},
+ {value: 0x435e, lo: 0x8b, hi: 0x8b},
+ {value: 0x4346, lo: 0x8c, hi: 0x8c},
+ {value: 0x438e, lo: 0x8d, hi: 0x8d},
+ {value: 0x43b8, lo: 0x8e, hi: 0x8e},
+ // Block 0x58, offset 0x59
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0xa0, hi: 0xa6},
+ // Block 0x59, offset 0x5a
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80dc, lo: 0xbd, hi: 0xbd},
+ // Block 0x5a, offset 0x5b
+ {value: 0x00db, lo: 0x05},
+ {value: 0x80dc, lo: 0x8d, hi: 0x8d},
+ {value: 0x80e6, lo: 0x8f, hi: 0x8f},
+ {value: 0x80e6, lo: 0xb8, hi: 0xb8},
+ {value: 0x8001, lo: 0xb9, hi: 0xba},
+ {value: 0x8009, lo: 0xbf, hi: 0xbf},
+ // Block 0x5b, offset 0x5c
+ {value: 0x05fe, lo: 0x07},
+ {value: 0x8800, lo: 0x99, hi: 0x99},
+ {value: 0x4222, lo: 0x9a, hi: 0x9a},
+ {value: 0x8800, lo: 0x9b, hi: 0x9b},
+ {value: 0x422c, lo: 0x9c, hi: 0x9c},
+ {value: 0x8800, lo: 0xa5, hi: 0xa5},
+ {value: 0x4236, lo: 0xab, hi: 0xab},
+ {value: 0x8009, lo: 0xb9, hi: 0xba},
+ // Block 0x5c, offset 0x5d
+ {value: 0x0000, lo: 0x06},
+ {value: 0x80e6, lo: 0x80, hi: 0x82},
+ {value: 0x8600, lo: 0xa7, hi: 0xa7},
+ {value: 0x1993, lo: 0xae, hi: 0xae},
+ {value: 0x199c, lo: 0xaf, hi: 0xaf},
+ {value: 0x8800, lo: 0xb1, hi: 0xb2},
+ {value: 0x8009, lo: 0xb3, hi: 0xb4},
+ // Block 0x5d, offset 0x5e
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8009, lo: 0xb6, hi: 0xb6},
+ {value: 0x8007, lo: 0xb7, hi: 0xb7},
+ // Block 0x5e, offset 0x5f
+ {value: 0x0000, lo: 0x0c},
+ {value: 0x45b2, lo: 0x9e, hi: 0x9e},
+ {value: 0x45bc, lo: 0x9f, hi: 0x9f},
+ {value: 0x45f0, lo: 0xa0, hi: 0xa0},
+ {value: 0x45fe, lo: 0xa1, hi: 0xa1},
+ {value: 0x460c, lo: 0xa2, hi: 0xa2},
+ {value: 0x461a, lo: 0xa3, hi: 0xa3},
+ {value: 0x4628, lo: 0xa4, hi: 0xa4},
+ {value: 0x80d8, lo: 0xa5, hi: 0xa6},
+ {value: 0x8001, lo: 0xa7, hi: 0xa9},
+ {value: 0x80e2, lo: 0xad, hi: 0xad},
+ {value: 0x80d8, lo: 0xae, hi: 0xb2},
+ {value: 0x80dc, lo: 0xbb, hi: 0xbf},
+ // Block 0x5f, offset 0x60
+ {value: 0x0000, lo: 0x09},
+ {value: 0x80dc, lo: 0x80, hi: 0x82},
+ {value: 0x80e6, lo: 0x85, hi: 0x89},
+ {value: 0x80dc, lo: 0x8a, hi: 0x8b},
+ {value: 0x80e6, lo: 0xaa, hi: 0xad},
+ {value: 0x45c6, lo: 0xbb, hi: 0xbb},
+ {value: 0x45d0, lo: 0xbc, hi: 0xbc},
+ {value: 0x4636, lo: 0xbd, hi: 0xbd},
+ {value: 0x4652, lo: 0xbe, hi: 0xbe},
+ {value: 0x4644, lo: 0xbf, hi: 0xbf},
+ // Block 0x60, offset 0x61
+ {value: 0x0000, lo: 0x01},
+ {value: 0x4660, lo: 0x80, hi: 0x80},
+ // Block 0x61, offset 0x62
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0x82, hi: 0x84},
+}
+
+// nfcLookup: 1088 bytes
+// Block 0 is the null block.
+var nfcLookup = [1088]uint8{
+ // Block 0x0, offset 0x0
+ // Block 0x1, offset 0x40
+ // Block 0x2, offset 0x80
+ // Block 0x3, offset 0xc0
+ 0x0c2: 0x2c, 0x0c3: 0x01, 0x0c4: 0x02, 0x0c5: 0x03, 0x0c6: 0x2d, 0x0c7: 0x04,
+ 0x0c8: 0x05, 0x0ca: 0x2e, 0x0cc: 0x06, 0x0cd: 0x07, 0x0ce: 0x08, 0x0cf: 0x2f,
+ 0x0d0: 0x09, 0x0d1: 0x30, 0x0d2: 0x31, 0x0d3: 0x0a, 0x0d6: 0x0b, 0x0d7: 0x32,
+ 0x0d8: 0x33, 0x0d9: 0x0c, 0x0db: 0x34, 0x0dc: 0x35, 0x0dd: 0x36, 0x0df: 0x37,
+ 0x0e0: 0x02, 0x0e1: 0x03, 0x0e2: 0x04, 0x0e3: 0x05,
+ 0x0ea: 0x06, 0x0eb: 0x07, 0x0ec: 0x07, 0x0ed: 0x08, 0x0ef: 0x09,
+ 0x0f0: 0x0e,
+ // Block 0x4, offset 0x100
+ 0x120: 0x38, 0x121: 0x39, 0x123: 0x3a, 0x124: 0x3b, 0x125: 0x3c, 0x126: 0x3d, 0x127: 0x3e,
+ 0x128: 0x3f, 0x129: 0x40, 0x12a: 0x41, 0x12b: 0x42, 0x12c: 0x3d, 0x12d: 0x43, 0x12e: 0x44, 0x12f: 0x45,
+ 0x131: 0x46, 0x132: 0x47, 0x133: 0x48, 0x134: 0x49, 0x135: 0x4a, 0x137: 0x4b,
+ 0x138: 0x4c, 0x139: 0x4d, 0x13a: 0x4e, 0x13b: 0x4f, 0x13c: 0x50, 0x13d: 0x51, 0x13e: 0x52, 0x13f: 0x53,
+ // Block 0x5, offset 0x140
+ 0x140: 0x54, 0x142: 0x55, 0x144: 0x56, 0x145: 0x57, 0x146: 0x58, 0x147: 0x59,
+ 0x14d: 0x5a,
+ 0x15c: 0x5b, 0x15f: 0x5c,
+ 0x162: 0x5d, 0x164: 0x5e,
+ 0x168: 0x5f, 0x169: 0x60, 0x16c: 0x0d, 0x16d: 0x61, 0x16e: 0x62, 0x16f: 0x63,
+ 0x170: 0x64, 0x173: 0x65, 0x177: 0x66,
+ 0x178: 0x0e, 0x179: 0x0f, 0x17a: 0x10, 0x17b: 0x11, 0x17c: 0x12, 0x17d: 0x13, 0x17e: 0x14, 0x17f: 0x15,
+ // Block 0x6, offset 0x180
+ 0x180: 0x67, 0x183: 0x68, 0x184: 0x69, 0x186: 0x6a, 0x187: 0x6b,
+ 0x188: 0x6c, 0x189: 0x16, 0x18a: 0x17, 0x18b: 0x6d, 0x18c: 0x6e,
+ 0x1ab: 0x6f,
+ 0x1b3: 0x70, 0x1b5: 0x71, 0x1b7: 0x72,
+ // Block 0x7, offset 0x1c0
+ 0x1c0: 0x73, 0x1c1: 0x18, 0x1c2: 0x19, 0x1c3: 0x1a,
+ // Block 0x8, offset 0x200
+ 0x219: 0x74, 0x21a: 0x75, 0x21b: 0x76,
+ 0x220: 0x77, 0x223: 0x78, 0x224: 0x79, 0x225: 0x7a, 0x226: 0x7b, 0x227: 0x7c,
+ 0x22a: 0x7d, 0x22b: 0x7e, 0x22f: 0x7f,
+ 0x230: 0x80, 0x231: 0x80, 0x232: 0x80, 0x233: 0x80, 0x234: 0x80, 0x235: 0x80, 0x236: 0x80, 0x237: 0x80,
+ 0x238: 0x80, 0x239: 0x80, 0x23a: 0x80, 0x23b: 0x80, 0x23c: 0x80, 0x23d: 0x80, 0x23e: 0x80, 0x23f: 0x80,
+ // Block 0x9, offset 0x240
+ 0x240: 0x80, 0x241: 0x80, 0x242: 0x80, 0x243: 0x80, 0x244: 0x80, 0x245: 0x80, 0x246: 0x80, 0x247: 0x80,
+ 0x248: 0x80, 0x249: 0x80, 0x24a: 0x80, 0x24b: 0x80, 0x24c: 0x80, 0x24d: 0x80, 0x24e: 0x80, 0x24f: 0x80,
+ 0x250: 0x80, 0x251: 0x80, 0x252: 0x80, 0x253: 0x80, 0x254: 0x80, 0x255: 0x80, 0x256: 0x80, 0x257: 0x80,
+ 0x258: 0x80, 0x259: 0x80, 0x25a: 0x80, 0x25b: 0x80, 0x25c: 0x80, 0x25d: 0x80, 0x25e: 0x80, 0x25f: 0x80,
+ 0x260: 0x80, 0x261: 0x80, 0x262: 0x80, 0x263: 0x80, 0x264: 0x80, 0x265: 0x80, 0x266: 0x80, 0x267: 0x80,
+ 0x268: 0x80, 0x269: 0x80, 0x26a: 0x80, 0x26b: 0x80, 0x26c: 0x80, 0x26d: 0x80, 0x26e: 0x80, 0x26f: 0x80,
+ 0x270: 0x80, 0x271: 0x80, 0x272: 0x80, 0x273: 0x80, 0x274: 0x80, 0x275: 0x80, 0x276: 0x80, 0x277: 0x80,
+ 0x278: 0x80, 0x279: 0x80, 0x27a: 0x80, 0x27b: 0x80, 0x27c: 0x80, 0x27d: 0x80, 0x27e: 0x80, 0x27f: 0x80,
+ // Block 0xa, offset 0x280
+ 0x280: 0x80, 0x281: 0x80, 0x282: 0x80, 0x283: 0x80, 0x284: 0x80, 0x285: 0x80, 0x286: 0x80, 0x287: 0x80,
+ 0x288: 0x80, 0x289: 0x80, 0x28a: 0x80, 0x28b: 0x80, 0x28c: 0x80, 0x28d: 0x80, 0x28e: 0x80, 0x28f: 0x80,
+ 0x290: 0x80, 0x291: 0x80, 0x292: 0x80, 0x293: 0x80, 0x294: 0x80, 0x295: 0x80, 0x296: 0x80, 0x297: 0x80,
+ 0x298: 0x80, 0x299: 0x80, 0x29a: 0x80, 0x29b: 0x80, 0x29c: 0x80, 0x29d: 0x80, 0x29e: 0x81,
+ // Block 0xb, offset 0x2c0
+ 0x2e4: 0x1b, 0x2e5: 0x1c, 0x2e6: 0x1d, 0x2e7: 0x1e,
+ 0x2e8: 0x1f, 0x2e9: 0x20, 0x2ea: 0x21, 0x2eb: 0x22, 0x2ec: 0x82, 0x2ed: 0x83,
+ 0x2f8: 0x84,
+ // Block 0xc, offset 0x300
+ 0x307: 0x85,
+ 0x328: 0x86,
+ // Block 0xd, offset 0x340
+ 0x341: 0x77, 0x342: 0x87, 0x344: 0x88, 0x347: 0x7c,
+ 0x35a: 0x89,
+ // Block 0xe, offset 0x380
+ 0x385: 0x8a, 0x386: 0x8b, 0x387: 0x8c,
+ 0x389: 0x8d,
+ // Block 0xf, offset 0x3c0
+ 0x3e0: 0x23, 0x3e1: 0x24, 0x3e2: 0x25, 0x3e3: 0x26, 0x3e4: 0x27, 0x3e5: 0x28, 0x3e6: 0x29, 0x3e7: 0x2a,
+ 0x3e8: 0x2b,
+ // Block 0x10, offset 0x400
+ 0x410: 0x0a, 0x411: 0x0b,
+ 0x41d: 0x0c,
+ 0x42f: 0x0d,
+}
+
+var nfcTrie = trie{nfcLookup[:], nfcValues[:], nfcSparseValues[:], nfcSparseOffset[:], 44}
+
+// nfkcValues: 5760 entries, 11520 bytes
+// Block 2 is the null block.
+var nfkcValues = [5760]uint16{
+ // Block 0x0, offset 0x0
+ 0x003c: 0x8800, 0x003d: 0x8800, 0x003e: 0x8800,
+ // Block 0x1, offset 0x40
+ 0x0041: 0x8800, 0x0042: 0x8800, 0x0043: 0x8800, 0x0044: 0x8800, 0x0045: 0x8800,
+ 0x0046: 0x8800, 0x0047: 0x8800, 0x0048: 0x8800, 0x0049: 0x8800, 0x004a: 0x8800, 0x004b: 0x8800,
+ 0x004c: 0x8800, 0x004d: 0x8800, 0x004e: 0x8800, 0x004f: 0x8800, 0x0050: 0x8800,
+ 0x0052: 0x8800, 0x0053: 0x8800, 0x0054: 0x8800, 0x0055: 0x8800, 0x0056: 0x8800, 0x0057: 0x8800,
+ 0x0058: 0x8800, 0x0059: 0x8800, 0x005a: 0x8800,
+ 0x0061: 0x8800, 0x0062: 0x8800, 0x0063: 0x8800,
+ 0x0064: 0x8800, 0x0065: 0x8800, 0x0066: 0x8800, 0x0067: 0x8800, 0x0068: 0x8800, 0x0069: 0x8800,
+ 0x006a: 0x8800, 0x006b: 0x8800, 0x006c: 0x8800, 0x006d: 0x8800, 0x006e: 0x8800, 0x006f: 0x8800,
+ 0x0070: 0x8800, 0x0072: 0x8800, 0x0073: 0x8800, 0x0074: 0x8800, 0x0075: 0x8800,
+ 0x0076: 0x8800, 0x0077: 0x8800, 0x0078: 0x8800, 0x0079: 0x8800, 0x007a: 0x8800,
+ // Block 0x2, offset 0x80
+ // Block 0x3, offset 0xc0
+ 0x00c0: 0x2f59, 0x00c1: 0x2f5e, 0x00c2: 0x466e, 0x00c3: 0x2f63, 0x00c4: 0x467d, 0x00c5: 0x4682,
+ 0x00c6: 0x8800, 0x00c7: 0x468c, 0x00c8: 0x2fcc, 0x00c9: 0x2fd1, 0x00ca: 0x4691, 0x00cb: 0x2fe5,
+ 0x00cc: 0x3058, 0x00cd: 0x305d, 0x00ce: 0x3062, 0x00cf: 0x46a5, 0x00d1: 0x30ee,
+ 0x00d2: 0x3111, 0x00d3: 0x3116, 0x00d4: 0x46af, 0x00d5: 0x46b4, 0x00d6: 0x46c3,
+ 0x00d8: 0x8800, 0x00d9: 0x319d, 0x00da: 0x31a2, 0x00db: 0x31a7, 0x00dc: 0x46f5, 0x00dd: 0x321f,
+ 0x00e0: 0x3265, 0x00e1: 0x326a, 0x00e2: 0x46ff, 0x00e3: 0x326f,
+ 0x00e4: 0x470e, 0x00e5: 0x4713, 0x00e6: 0x8800, 0x00e7: 0x471d, 0x00e8: 0x32d8, 0x00e9: 0x32dd,
+ 0x00ea: 0x4722, 0x00eb: 0x32f1, 0x00ec: 0x3369, 0x00ed: 0x336e, 0x00ee: 0x3373, 0x00ef: 0x4736,
+ 0x00f1: 0x33ff, 0x00f2: 0x3422, 0x00f3: 0x3427, 0x00f4: 0x4740, 0x00f5: 0x4745,
+ 0x00f6: 0x4754, 0x00f8: 0x8800, 0x00f9: 0x34b3, 0x00fa: 0x34b8, 0x00fb: 0x34bd,
+ 0x00fc: 0x4786, 0x00fd: 0x353a, 0x00ff: 0x3553,
+ // Block 0x4, offset 0x100
+ 0x0100: 0x2f68, 0x0101: 0x3274, 0x0102: 0x4673, 0x0103: 0x4704, 0x0104: 0x2f86, 0x0105: 0x3292,
+ 0x0106: 0x2f9a, 0x0107: 0x32a6, 0x0108: 0x2f9f, 0x0109: 0x32ab, 0x010a: 0x2fa4, 0x010b: 0x32b0,
+ 0x010c: 0x2fa9, 0x010d: 0x32b5, 0x010e: 0x2fb3, 0x010f: 0x32bf,
+ 0x0112: 0x4696, 0x0113: 0x4727, 0x0114: 0x2fdb, 0x0115: 0x32e7, 0x0116: 0x2fe0, 0x0117: 0x32ec,
+ 0x0118: 0x2ffe, 0x0119: 0x330a, 0x011a: 0x2fef, 0x011b: 0x32fb, 0x011c: 0x3017, 0x011d: 0x3323,
+ 0x011e: 0x3021, 0x011f: 0x332d, 0x0120: 0x3026, 0x0121: 0x3332, 0x0122: 0x3030, 0x0123: 0x333c,
+ 0x0124: 0x3035, 0x0125: 0x3341, 0x0128: 0x3067, 0x0129: 0x3378,
+ 0x012a: 0x306c, 0x012b: 0x337d, 0x012c: 0x3071, 0x012d: 0x3382, 0x012e: 0x3094, 0x012f: 0x33a0,
+ 0x0130: 0x3076, 0x0132: 0x1a9f, 0x0133: 0x1b29, 0x0134: 0x309e, 0x0135: 0x33aa,
+ 0x0136: 0x30b2, 0x0137: 0x33c3, 0x0139: 0x30bc, 0x013a: 0x33cd, 0x013b: 0x30c6,
+ 0x013c: 0x33d7, 0x013d: 0x30c1, 0x013e: 0x33d2, 0x013f: 0x1cee,
+ // Block 0x5, offset 0x140
+ 0x0140: 0x1d76, 0x0143: 0x30e9, 0x0144: 0x33fa, 0x0145: 0x3102,
+ 0x0146: 0x3413, 0x0147: 0x30f8, 0x0148: 0x3409, 0x0149: 0x1d9e,
+ 0x014c: 0x46b9, 0x014d: 0x474a, 0x014e: 0x311b, 0x014f: 0x342c, 0x0150: 0x3125, 0x0151: 0x3436,
+ 0x0154: 0x3143, 0x0155: 0x3454, 0x0156: 0x315c, 0x0157: 0x346d,
+ 0x0158: 0x314d, 0x0159: 0x345e, 0x015a: 0x46dc, 0x015b: 0x476d, 0x015c: 0x3166, 0x015d: 0x3477,
+ 0x015e: 0x3175, 0x015f: 0x3486, 0x0160: 0x46e1, 0x0161: 0x4772, 0x0162: 0x318e, 0x0163: 0x34a4,
+ 0x0164: 0x317f, 0x0165: 0x3495, 0x0168: 0x46eb, 0x0169: 0x477c,
+ 0x016a: 0x46f0, 0x016b: 0x4781, 0x016c: 0x31ac, 0x016d: 0x34c2, 0x016e: 0x31b6, 0x016f: 0x34cc,
+ 0x0170: 0x31bb, 0x0171: 0x34d1, 0x0172: 0x31d9, 0x0173: 0x34ef, 0x0174: 0x31fc, 0x0175: 0x3512,
+ 0x0176: 0x3224, 0x0177: 0x353f, 0x0178: 0x3238, 0x0179: 0x3247, 0x017a: 0x3567, 0x017b: 0x3251,
+ 0x017c: 0x3571, 0x017d: 0x3256, 0x017e: 0x3576, 0x017f: 0x00a7,
+ // Block 0x6, offset 0x180
+ 0x0184: 0x2e7f, 0x0185: 0x2e85,
+ 0x0186: 0x2e8b, 0x0187: 0x1ab4, 0x0188: 0x1ab7, 0x0189: 0x1b4a, 0x018a: 0x1ac9, 0x018b: 0x1acc,
+ 0x018c: 0x1b80, 0x018d: 0x2f72, 0x018e: 0x327e, 0x018f: 0x3080, 0x0190: 0x338c, 0x0191: 0x312a,
+ 0x0192: 0x343b, 0x0193: 0x31c0, 0x0194: 0x34d6, 0x0195: 0x39b9, 0x0196: 0x3b48, 0x0197: 0x39b2,
+ 0x0198: 0x3b41, 0x0199: 0x39c0, 0x019a: 0x3b4f, 0x019b: 0x39ab, 0x019c: 0x3b3a,
+ 0x019e: 0x389a, 0x019f: 0x3a29, 0x01a0: 0x3893, 0x01a1: 0x3a22, 0x01a2: 0x359d, 0x01a3: 0x35af,
+ 0x01a6: 0x302b, 0x01a7: 0x3337, 0x01a8: 0x30a8, 0x01a9: 0x33b9,
+ 0x01aa: 0x46d2, 0x01ab: 0x4763, 0x01ac: 0x397a, 0x01ad: 0x3b09, 0x01ae: 0x35c1, 0x01af: 0x35c7,
+ 0x01b0: 0x33af, 0x01b1: 0x1a84, 0x01b2: 0x1a87, 0x01b3: 0x1b11, 0x01b4: 0x3012, 0x01b5: 0x331e,
+ 0x01b8: 0x30e4, 0x01b9: 0x33f5, 0x01ba: 0x38a1, 0x01bb: 0x3a30,
+ 0x01bc: 0x3597, 0x01bd: 0x35a9, 0x01be: 0x35a3, 0x01bf: 0x35b5,
+ // Block 0x7, offset 0x1c0
+ 0x01c0: 0x2f77, 0x01c1: 0x3283, 0x01c2: 0x2f7c, 0x01c3: 0x3288, 0x01c4: 0x2ff4, 0x01c5: 0x3300,
+ 0x01c6: 0x2ff9, 0x01c7: 0x3305, 0x01c8: 0x3085, 0x01c9: 0x3391, 0x01ca: 0x308a, 0x01cb: 0x3396,
+ 0x01cc: 0x312f, 0x01cd: 0x3440, 0x01ce: 0x3134, 0x01cf: 0x3445, 0x01d0: 0x3152, 0x01d1: 0x3463,
+ 0x01d2: 0x3157, 0x01d3: 0x3468, 0x01d4: 0x31c5, 0x01d5: 0x34db, 0x01d6: 0x31ca, 0x01d7: 0x34e0,
+ 0x01d8: 0x3170, 0x01d9: 0x3481, 0x01da: 0x3189, 0x01db: 0x349f,
+ 0x01de: 0x3044, 0x01df: 0x3350,
+ 0x01e6: 0x4678, 0x01e7: 0x4709, 0x01e8: 0x46a0, 0x01e9: 0x4731,
+ 0x01ea: 0x3949, 0x01eb: 0x3ad8, 0x01ec: 0x3926, 0x01ed: 0x3ab5, 0x01ee: 0x46be, 0x01ef: 0x474f,
+ 0x01f0: 0x3942, 0x01f1: 0x3ad1, 0x01f2: 0x322e, 0x01f3: 0x3549,
+ // Block 0x8, offset 0x200
+ 0x0200: 0x86e6, 0x0201: 0x86e6, 0x0202: 0x86e6, 0x0203: 0x86e6, 0x0204: 0x86e6, 0x0205: 0x80e6,
+ 0x0206: 0x86e6, 0x0207: 0x86e6, 0x0208: 0x86e6, 0x0209: 0x86e6, 0x020a: 0x86e6, 0x020b: 0x86e6,
+ 0x020c: 0x86e6, 0x020d: 0x80e6, 0x020e: 0x80e6, 0x020f: 0x86e6, 0x0210: 0x80e6, 0x0211: 0x86e6,
+ 0x0212: 0x80e6, 0x0213: 0x86e6, 0x0214: 0x86e6, 0x0215: 0x80e8, 0x0216: 0x80dc, 0x0217: 0x80dc,
+ 0x0218: 0x80dc, 0x0219: 0x80dc, 0x021a: 0x80e8, 0x021b: 0x86d8, 0x021c: 0x80dc, 0x021d: 0x80dc,
+ 0x021e: 0x80dc, 0x021f: 0x80dc, 0x0220: 0x80dc, 0x0221: 0x80ca, 0x0222: 0x80ca, 0x0223: 0x86dc,
+ 0x0224: 0x86dc, 0x0225: 0x86dc, 0x0226: 0x86dc, 0x0227: 0x86ca, 0x0228: 0x86ca, 0x0229: 0x80dc,
+ 0x022a: 0x80dc, 0x022b: 0x80dc, 0x022c: 0x80dc, 0x022d: 0x86dc, 0x022e: 0x86dc, 0x022f: 0x80dc,
+ 0x0230: 0x86dc, 0x0231: 0x86dc, 0x0232: 0x80dc, 0x0233: 0x80dc, 0x0234: 0x8001, 0x0235: 0x8001,
+ 0x0236: 0x8001, 0x0237: 0x8001, 0x0238: 0x8601, 0x0239: 0x80dc, 0x023a: 0x80dc, 0x023b: 0x80dc,
+ 0x023c: 0x80dc, 0x023d: 0x80e6, 0x023e: 0x80e6, 0x023f: 0x80e6,
+ // Block 0x9, offset 0x240
+ 0x0240: 0x4994, 0x0241: 0x4999, 0x0242: 0x86e6, 0x0243: 0x499e, 0x0244: 0x49a3, 0x0245: 0x86f0,
+ 0x0246: 0x80e6, 0x0247: 0x80dc, 0x0248: 0x80dc, 0x0249: 0x80dc, 0x024a: 0x80e6, 0x024b: 0x80e6,
+ 0x024c: 0x80e6, 0x024d: 0x80dc, 0x024e: 0x80dc, 0x0250: 0x80e6, 0x0251: 0x80e6,
+ 0x0252: 0x80e6, 0x0253: 0x80dc, 0x0254: 0x80dc, 0x0255: 0x80dc, 0x0256: 0x80dc, 0x0257: 0x80e6,
+ 0x0258: 0x80e8, 0x0259: 0x80dc, 0x025a: 0x80dc, 0x025b: 0x80e6, 0x025c: 0x80e9, 0x025d: 0x80ea,
+ 0x025e: 0x80ea, 0x025f: 0x80e9, 0x0260: 0x80ea, 0x0261: 0x80ea, 0x0262: 0x80e9, 0x0263: 0x80e6,
+ 0x0264: 0x80e6, 0x0265: 0x80e6, 0x0266: 0x80e6, 0x0267: 0x80e6, 0x0268: 0x80e6, 0x0269: 0x80e6,
+ 0x026a: 0x80e6, 0x026b: 0x80e6, 0x026c: 0x80e6, 0x026d: 0x80e6, 0x026e: 0x80e6, 0x026f: 0x80e6,
+ 0x0274: 0x0170,
+ 0x027a: 0x428b,
+ 0x027e: 0x0037,
+ // Block 0xa, offset 0x280
+ 0x0284: 0x4240, 0x0285: 0x4461,
+ 0x0286: 0x35d3, 0x0287: 0x00d1, 0x0288: 0x35f1, 0x0289: 0x35fd, 0x028a: 0x360f,
+ 0x028c: 0x362d, 0x028e: 0x363f, 0x028f: 0x365d, 0x0290: 0x3df2, 0x0291: 0x8800,
+ 0x0295: 0x8800, 0x0297: 0x8800,
+ 0x0299: 0x8800,
+ 0x029f: 0x8800, 0x02a1: 0x8800,
+ 0x02a5: 0x8800, 0x02a9: 0x8800,
+ 0x02aa: 0x3621, 0x02ab: 0x3651, 0x02ac: 0x47e4, 0x02ad: 0x3681, 0x02ae: 0x480e, 0x02af: 0x3693,
+ 0x02b0: 0x3e5a, 0x02b1: 0x8800, 0x02b5: 0x8800,
+ 0x02b7: 0x8800, 0x02b9: 0x8800,
+ 0x02bf: 0x8800,
+ // Block 0xb, offset 0x2c0
+ 0x02c1: 0x8800, 0x02c5: 0x8800,
+ 0x02c9: 0x8800, 0x02ca: 0x4826, 0x02cb: 0x4844,
+ 0x02cc: 0x36b1, 0x02cd: 0x36c9, 0x02ce: 0x485c, 0x02d0: 0x01be, 0x02d1: 0x01d0,
+ 0x02d2: 0x01ac, 0x02d3: 0x42f2, 0x02d4: 0x42f8, 0x02d5: 0x01fa, 0x02d6: 0x01e8,
+ 0x02f0: 0x01d6, 0x02f1: 0x01eb, 0x02f2: 0x01ee, 0x02f4: 0x0188, 0x02f5: 0x01c7,
+ 0x02f9: 0x01a6,
+ // Block 0xc, offset 0x300
+ 0x0300: 0x370b, 0x0301: 0x3717, 0x0303: 0x3705,
+ 0x0306: 0x8800, 0x0307: 0x36f3,
+ 0x030c: 0x3747, 0x030d: 0x372f, 0x030e: 0x3759, 0x0310: 0x8800,
+ 0x0313: 0x8800, 0x0315: 0x8800, 0x0316: 0x8800, 0x0317: 0x8800,
+ 0x0318: 0x8800, 0x0319: 0x373b, 0x031a: 0x8800,
+ 0x031e: 0x8800, 0x0323: 0x8800,
+ 0x0327: 0x8800,
+ 0x032b: 0x8800, 0x032d: 0x8800,
+ 0x0330: 0x8800, 0x0333: 0x8800, 0x0335: 0x8800,
+ 0x0336: 0x8800, 0x0337: 0x8800, 0x0338: 0x8800, 0x0339: 0x37bf, 0x033a: 0x8800,
+ 0x033e: 0x8800,
+ // Block 0xd, offset 0x340
+ 0x0341: 0x371d, 0x0342: 0x37a1,
+ 0x0350: 0x36f9, 0x0351: 0x377d,
+ 0x0352: 0x36ff, 0x0353: 0x3783, 0x0356: 0x3711, 0x0357: 0x3795,
+ 0x0358: 0x8800, 0x0359: 0x8800, 0x035a: 0x3813, 0x035b: 0x3819, 0x035c: 0x3723, 0x035d: 0x37a7,
+ 0x035e: 0x3729, 0x035f: 0x37ad, 0x0362: 0x3735, 0x0363: 0x37b9,
+ 0x0364: 0x3741, 0x0365: 0x37c5, 0x0366: 0x374d, 0x0367: 0x37d1, 0x0368: 0x8800, 0x0369: 0x8800,
+ 0x036a: 0x381f, 0x036b: 0x3825, 0x036c: 0x3777, 0x036d: 0x37fb, 0x036e: 0x3753, 0x036f: 0x37d7,
+ 0x0370: 0x375f, 0x0371: 0x37e3, 0x0372: 0x3765, 0x0373: 0x37e9, 0x0374: 0x376b, 0x0375: 0x37ef,
+ 0x0378: 0x3771, 0x0379: 0x37f5,
+ // Block 0xe, offset 0x380
+ 0x0387: 0x1ea3,
+ 0x0391: 0x80dc,
+ 0x0392: 0x80e6, 0x0393: 0x80e6, 0x0394: 0x80e6, 0x0395: 0x80e6, 0x0396: 0x80dc, 0x0397: 0x80e6,
+ 0x0398: 0x80e6, 0x0399: 0x80e6, 0x039a: 0x80de, 0x039b: 0x80dc, 0x039c: 0x80e6, 0x039d: 0x80e6,
+ 0x039e: 0x80e6, 0x039f: 0x80e6, 0x03a0: 0x80e6, 0x03a1: 0x80e6, 0x03a2: 0x80dc, 0x03a3: 0x80dc,
+ 0x03a4: 0x80dc, 0x03a5: 0x80dc, 0x03a6: 0x80dc, 0x03a7: 0x80dc, 0x03a8: 0x80e6, 0x03a9: 0x80e6,
+ 0x03aa: 0x80dc, 0x03ab: 0x80e6, 0x03ac: 0x80e6, 0x03ad: 0x80de, 0x03ae: 0x80e4, 0x03af: 0x80e6,
+ 0x03b0: 0x800a, 0x03b1: 0x800b, 0x03b2: 0x800c, 0x03b3: 0x800d, 0x03b4: 0x800e, 0x03b5: 0x800f,
+ 0x03b6: 0x8010, 0x03b7: 0x8011, 0x03b8: 0x8012, 0x03b9: 0x8013, 0x03ba: 0x8013, 0x03bb: 0x8014,
+ 0x03bc: 0x8015, 0x03bd: 0x8016, 0x03bf: 0x8017,
+ // Block 0xf, offset 0x3c0
+ 0x03c8: 0x8800, 0x03ca: 0x8800, 0x03cb: 0x801b,
+ 0x03cc: 0x801c, 0x03cd: 0x801d, 0x03ce: 0x801e, 0x03cf: 0x801f, 0x03d0: 0x8020, 0x03d1: 0x8021,
+ 0x03d2: 0x8022, 0x03d3: 0x86e6, 0x03d4: 0x86e6, 0x03d5: 0x86dc, 0x03d6: 0x80dc, 0x03d7: 0x80e6,
+ 0x03d8: 0x80e6, 0x03d9: 0x80e6, 0x03da: 0x80e6, 0x03db: 0x80e6, 0x03dc: 0x80dc, 0x03dd: 0x80e6,
+ 0x03de: 0x80e6, 0x03df: 0x80dc,
+ 0x03f0: 0x8023, 0x03f5: 0x1ec6,
+ 0x03f6: 0x2155, 0x03f7: 0x2191, 0x03f8: 0x218c,
+ // Block 0x10, offset 0x400
+ 0x0405: 0x8800,
+ 0x0406: 0x1946, 0x0407: 0x8800, 0x0408: 0x194d, 0x0409: 0x8800, 0x040a: 0x1954, 0x040b: 0x8800,
+ 0x040c: 0x195b, 0x040d: 0x8800, 0x040e: 0x1962, 0x0411: 0x8800,
+ 0x0412: 0x1969,
+ 0x0434: 0x8007, 0x0435: 0x8600,
+ 0x043a: 0x8800, 0x043b: 0x1970,
+ 0x043c: 0x8800, 0x043d: 0x1977, 0x043e: 0x8800, 0x043f: 0x8800,
+ // Block 0x11, offset 0x440
+ 0x0440: 0x0069, 0x0441: 0x006b, 0x0442: 0x006f, 0x0443: 0x0083, 0x0444: 0x00f8, 0x0445: 0x00fb,
+ 0x0446: 0x0485, 0x0447: 0x0085, 0x0448: 0x0089, 0x0449: 0x008b, 0x044a: 0x0107, 0x044b: 0x010a,
+ 0x044c: 0x010d, 0x044d: 0x008f, 0x044f: 0x0097, 0x0450: 0x009b, 0x0451: 0x00e3,
+ 0x0452: 0x009f, 0x0453: 0x0101, 0x0454: 0x0489, 0x0455: 0x048d, 0x0456: 0x00a1, 0x0457: 0x00a9,
+ 0x0458: 0x00ab, 0x0459: 0x0495, 0x045a: 0x012b, 0x045b: 0x00ad, 0x045c: 0x0499, 0x045d: 0x01be,
+ 0x045e: 0x01c1, 0x045f: 0x01c4, 0x0460: 0x01fa, 0x0461: 0x01fd, 0x0462: 0x0093, 0x0463: 0x00a5,
+ 0x0464: 0x00ab, 0x0465: 0x00ad, 0x0466: 0x01be, 0x0467: 0x01c1, 0x0468: 0x01eb, 0x0469: 0x01fa,
+ 0x046a: 0x01fd,
+ 0x0478: 0x020c,
+ // Block 0x12, offset 0x480
+ 0x049b: 0x00fe, 0x049c: 0x0087, 0x049d: 0x0104,
+ 0x049e: 0x00d7, 0x049f: 0x010d, 0x04a0: 0x008d, 0x04a1: 0x0110, 0x04a2: 0x0113, 0x04a3: 0x0119,
+ 0x04a4: 0x011f, 0x04a5: 0x0122, 0x04a6: 0x0125, 0x04a7: 0x049d, 0x04a8: 0x016a, 0x04a9: 0x0128,
+ 0x04aa: 0x04a1, 0x04ab: 0x016d, 0x04ac: 0x0131, 0x04ad: 0x012e, 0x04ae: 0x0134, 0x04af: 0x0137,
+ 0x04b0: 0x013a, 0x04b1: 0x013d, 0x04b2: 0x0140, 0x04b3: 0x014c, 0x04b4: 0x014f, 0x04b5: 0x00ef,
+ 0x04b6: 0x0152, 0x04b7: 0x0155, 0x04b8: 0x0491, 0x04b9: 0x0158, 0x04ba: 0x015b, 0x04bb: 0x00b5,
+ 0x04bc: 0x015e, 0x04bd: 0x0161, 0x04be: 0x0164, 0x04bf: 0x01d0,
+ // Block 0x13, offset 0x4c0
+ 0x04c0: 0x2f81, 0x04c1: 0x328d, 0x04c2: 0x2f8b, 0x04c3: 0x3297, 0x04c4: 0x2f90, 0x04c5: 0x329c,
+ 0x04c6: 0x2f95, 0x04c7: 0x32a1, 0x04c8: 0x38b6, 0x04c9: 0x3a45, 0x04ca: 0x2fae, 0x04cb: 0x32ba,
+ 0x04cc: 0x2fb8, 0x04cd: 0x32c4, 0x04ce: 0x2fc7, 0x04cf: 0x32d3, 0x04d0: 0x2fbd, 0x04d1: 0x32c9,
+ 0x04d2: 0x2fc2, 0x04d3: 0x32ce, 0x04d4: 0x38d9, 0x04d5: 0x3a68, 0x04d6: 0x38e0, 0x04d7: 0x3a6f,
+ 0x04d8: 0x3003, 0x04d9: 0x330f, 0x04da: 0x3008, 0x04db: 0x3314, 0x04dc: 0x38ee, 0x04dd: 0x3a7d,
+ 0x04de: 0x300d, 0x04df: 0x3319, 0x04e0: 0x301c, 0x04e1: 0x3328, 0x04e2: 0x303a, 0x04e3: 0x3346,
+ 0x04e4: 0x3049, 0x04e5: 0x3355, 0x04e6: 0x303f, 0x04e7: 0x334b, 0x04e8: 0x304e, 0x04e9: 0x335a,
+ 0x04ea: 0x3053, 0x04eb: 0x335f, 0x04ec: 0x3099, 0x04ed: 0x33a5, 0x04ee: 0x38f5, 0x04ef: 0x3a84,
+ 0x04f0: 0x30a3, 0x04f1: 0x33b4, 0x04f2: 0x30ad, 0x04f3: 0x33be, 0x04f4: 0x30b7, 0x04f5: 0x33c8,
+ 0x04f6: 0x46aa, 0x04f7: 0x473b, 0x04f8: 0x38fc, 0x04f9: 0x3a8b, 0x04fa: 0x30d0, 0x04fb: 0x33e1,
+ 0x04fc: 0x30cb, 0x04fd: 0x33dc, 0x04fe: 0x30d5, 0x04ff: 0x33e6,
+ // Block 0x14, offset 0x500
+ 0x0500: 0x30da, 0x0501: 0x33eb, 0x0502: 0x30df, 0x0503: 0x33f0, 0x0504: 0x30f3, 0x0505: 0x3404,
+ 0x0506: 0x30fd, 0x0507: 0x340e, 0x0508: 0x310c, 0x0509: 0x341d, 0x050a: 0x3107, 0x050b: 0x3418,
+ 0x050c: 0x391f, 0x050d: 0x3aae, 0x050e: 0x392d, 0x050f: 0x3abc, 0x0510: 0x3934, 0x0511: 0x3ac3,
+ 0x0512: 0x393b, 0x0513: 0x3aca, 0x0514: 0x3139, 0x0515: 0x344a, 0x0516: 0x313e, 0x0517: 0x344f,
+ 0x0518: 0x3148, 0x0519: 0x3459, 0x051a: 0x46d7, 0x051b: 0x4768, 0x051c: 0x3981, 0x051d: 0x3b10,
+ 0x051e: 0x3161, 0x051f: 0x3472, 0x0520: 0x316b, 0x0521: 0x347c, 0x0522: 0x46e6, 0x0523: 0x4777,
+ 0x0524: 0x3988, 0x0525: 0x3b17, 0x0526: 0x398f, 0x0527: 0x3b1e, 0x0528: 0x3996, 0x0529: 0x3b25,
+ 0x052a: 0x317a, 0x052b: 0x348b, 0x052c: 0x3184, 0x052d: 0x349a, 0x052e: 0x3198, 0x052f: 0x34ae,
+ 0x0530: 0x3193, 0x0531: 0x34a9, 0x0532: 0x31d4, 0x0533: 0x34ea, 0x0534: 0x31e3, 0x0535: 0x34f9,
+ 0x0536: 0x31de, 0x0537: 0x34f4, 0x0538: 0x399d, 0x0539: 0x3b2c, 0x053a: 0x39a4, 0x053b: 0x3b33,
+ 0x053c: 0x31e8, 0x053d: 0x34fe, 0x053e: 0x31ed, 0x053f: 0x3503,
+ // Block 0x15, offset 0x540
+ 0x0540: 0x31f2, 0x0541: 0x3508, 0x0542: 0x31f7, 0x0543: 0x350d, 0x0544: 0x3206, 0x0545: 0x351c,
+ 0x0546: 0x3201, 0x0547: 0x3517, 0x0548: 0x320b, 0x0549: 0x3526, 0x054a: 0x3210, 0x054b: 0x352b,
+ 0x054c: 0x3215, 0x054d: 0x3530, 0x054e: 0x3233, 0x054f: 0x354e, 0x0550: 0x324c, 0x0551: 0x356c,
+ 0x0552: 0x325b, 0x0553: 0x357b, 0x0554: 0x3260, 0x0555: 0x3580, 0x0556: 0x3364, 0x0557: 0x3490,
+ 0x0558: 0x3521, 0x0559: 0x355d, 0x055a: 0x1d22, 0x055b: 0x42bd,
+ 0x0560: 0x4687, 0x0561: 0x4718, 0x0562: 0x2f6d, 0x0563: 0x3279,
+ 0x0564: 0x3862, 0x0565: 0x39f1, 0x0566: 0x385b, 0x0567: 0x39ea, 0x0568: 0x3870, 0x0569: 0x39ff,
+ 0x056a: 0x3869, 0x056b: 0x39f8, 0x056c: 0x38a8, 0x056d: 0x3a37, 0x056e: 0x387e, 0x056f: 0x3a0d,
+ 0x0570: 0x3877, 0x0571: 0x3a06, 0x0572: 0x388c, 0x0573: 0x3a1b, 0x0574: 0x3885, 0x0575: 0x3a14,
+ 0x0576: 0x38af, 0x0577: 0x3a3e, 0x0578: 0x469b, 0x0579: 0x472c, 0x057a: 0x2fea, 0x057b: 0x32f6,
+ 0x057c: 0x2fd6, 0x057d: 0x32e2, 0x057e: 0x38c4, 0x057f: 0x3a53,
+ // Block 0x16, offset 0x580
+ 0x0580: 0x38bd, 0x0581: 0x3a4c, 0x0582: 0x38d2, 0x0583: 0x3a61, 0x0584: 0x38cb, 0x0585: 0x3a5a,
+ 0x0586: 0x38e7, 0x0587: 0x3a76, 0x0588: 0x307b, 0x0589: 0x3387, 0x058a: 0x308f, 0x058b: 0x339b,
+ 0x058c: 0x46cd, 0x058d: 0x475e, 0x058e: 0x3120, 0x058f: 0x3431, 0x0590: 0x390a, 0x0591: 0x3a99,
+ 0x0592: 0x3903, 0x0593: 0x3a92, 0x0594: 0x3918, 0x0595: 0x3aa7, 0x0596: 0x3911, 0x0597: 0x3aa0,
+ 0x0598: 0x3973, 0x0599: 0x3b02, 0x059a: 0x3957, 0x059b: 0x3ae6, 0x059c: 0x3950, 0x059d: 0x3adf,
+ 0x059e: 0x3965, 0x059f: 0x3af4, 0x05a0: 0x395e, 0x05a1: 0x3aed, 0x05a2: 0x396c, 0x05a3: 0x3afb,
+ 0x05a4: 0x31cf, 0x05a5: 0x34e5, 0x05a6: 0x31b1, 0x05a7: 0x34c7, 0x05a8: 0x39ce, 0x05a9: 0x3b5d,
+ 0x05aa: 0x39c7, 0x05ab: 0x3b56, 0x05ac: 0x39dc, 0x05ad: 0x3b6b, 0x05ae: 0x39d5, 0x05af: 0x3b64,
+ 0x05b0: 0x39e3, 0x05b1: 0x3b72, 0x05b2: 0x321a, 0x05b3: 0x3535, 0x05b4: 0x3242, 0x05b5: 0x3562,
+ 0x05b6: 0x323d, 0x05b7: 0x3558, 0x05b8: 0x3229, 0x05b9: 0x3544,
+ // Block 0x17, offset 0x5c0
+ 0x05c0: 0x47ea, 0x05c1: 0x47f0, 0x05c2: 0x4904, 0x05c3: 0x491c, 0x05c4: 0x490c, 0x05c5: 0x4924,
+ 0x05c6: 0x4914, 0x05c7: 0x492c, 0x05c8: 0x4790, 0x05c9: 0x4796, 0x05ca: 0x4874, 0x05cb: 0x488c,
+ 0x05cc: 0x487c, 0x05cd: 0x4894, 0x05ce: 0x4884, 0x05cf: 0x489c, 0x05d0: 0x47fc, 0x05d1: 0x4802,
+ 0x05d2: 0x3da2, 0x05d3: 0x3db2, 0x05d4: 0x3daa, 0x05d5: 0x3dba,
+ 0x05d8: 0x479c, 0x05d9: 0x47a2, 0x05da: 0x3cd2, 0x05db: 0x3ce2, 0x05dc: 0x3cda, 0x05dd: 0x3cea,
+ 0x05e0: 0x4814, 0x05e1: 0x481a, 0x05e2: 0x4934, 0x05e3: 0x494c,
+ 0x05e4: 0x493c, 0x05e5: 0x4954, 0x05e6: 0x4944, 0x05e7: 0x495c, 0x05e8: 0x47a8, 0x05e9: 0x47ae,
+ 0x05ea: 0x48a4, 0x05eb: 0x48bc, 0x05ec: 0x48ac, 0x05ed: 0x48c4, 0x05ee: 0x48b4, 0x05ef: 0x48cc,
+ 0x05f0: 0x482c, 0x05f1: 0x4832, 0x05f2: 0x3e02, 0x05f3: 0x3e1a, 0x05f4: 0x3e0a, 0x05f5: 0x3e22,
+ 0x05f6: 0x3e12, 0x05f7: 0x3e2a, 0x05f8: 0x47b4, 0x05f9: 0x47ba, 0x05fa: 0x3d02, 0x05fb: 0x3d1a,
+ 0x05fc: 0x3d0a, 0x05fd: 0x3d22, 0x05fe: 0x3d12, 0x05ff: 0x3d2a,
+ // Block 0x18, offset 0x600
+ 0x0600: 0x4838, 0x0601: 0x483e, 0x0602: 0x3e32, 0x0603: 0x3e42, 0x0604: 0x3e3a, 0x0605: 0x3e4a,
+ 0x0608: 0x47c0, 0x0609: 0x47c6, 0x060a: 0x3d32, 0x060b: 0x3d42,
+ 0x060c: 0x3d3a, 0x060d: 0x3d4a, 0x0610: 0x484a, 0x0611: 0x4850,
+ 0x0612: 0x3e6a, 0x0613: 0x3e82, 0x0614: 0x3e72, 0x0615: 0x3e8a, 0x0616: 0x3e7a, 0x0617: 0x3e92,
+ 0x0619: 0x47cc, 0x061b: 0x3d52, 0x061d: 0x3d5a,
+ 0x061f: 0x3d62, 0x0620: 0x4862, 0x0621: 0x4868, 0x0622: 0x4964, 0x0623: 0x497c,
+ 0x0624: 0x496c, 0x0625: 0x4984, 0x0626: 0x4974, 0x0627: 0x498c, 0x0628: 0x47d2, 0x0629: 0x47d8,
+ 0x062a: 0x48d4, 0x062b: 0x48ec, 0x062c: 0x48dc, 0x062d: 0x48f4, 0x062e: 0x48e4, 0x062f: 0x48fc,
+ 0x0630: 0x47de, 0x0631: 0x4304, 0x0632: 0x367b, 0x0633: 0x430a, 0x0634: 0x4808, 0x0635: 0x4310,
+ 0x0636: 0x368d, 0x0637: 0x4316, 0x0638: 0x36ab, 0x0639: 0x431c, 0x063a: 0x36c3, 0x063b: 0x4322,
+ 0x063c: 0x4856, 0x063d: 0x4328,
+ // Block 0x19, offset 0x640
+ 0x0640: 0x3d8a, 0x0641: 0x3d92, 0x0642: 0x416e, 0x0643: 0x418c, 0x0644: 0x4178, 0x0645: 0x4196,
+ 0x0646: 0x4182, 0x0647: 0x41a0, 0x0648: 0x3cc2, 0x0649: 0x3cca, 0x064a: 0x40ba, 0x064b: 0x40d8,
+ 0x064c: 0x40c4, 0x064d: 0x40e2, 0x064e: 0x40ce, 0x064f: 0x40ec, 0x0650: 0x3dd2, 0x0651: 0x3dda,
+ 0x0652: 0x41aa, 0x0653: 0x41c8, 0x0654: 0x41b4, 0x0655: 0x41d2, 0x0656: 0x41be, 0x0657: 0x41dc,
+ 0x0658: 0x3cf2, 0x0659: 0x3cfa, 0x065a: 0x40f6, 0x065b: 0x4114, 0x065c: 0x4100, 0x065d: 0x411e,
+ 0x065e: 0x410a, 0x065f: 0x4128, 0x0660: 0x3eaa, 0x0661: 0x3eb2, 0x0662: 0x41e6, 0x0663: 0x4204,
+ 0x0664: 0x41f0, 0x0665: 0x420e, 0x0666: 0x41fa, 0x0667: 0x4218, 0x0668: 0x3d6a, 0x0669: 0x3d72,
+ 0x066a: 0x4132, 0x066b: 0x4150, 0x066c: 0x413c, 0x066d: 0x415a, 0x066e: 0x4146, 0x066f: 0x4164,
+ 0x0670: 0x366f, 0x0671: 0x3669, 0x0672: 0x3d7a, 0x0673: 0x3675, 0x0674: 0x3d82,
+ 0x0676: 0x47f6, 0x0677: 0x3d9a, 0x0678: 0x35df, 0x0679: 0x35d9, 0x067a: 0x35cd, 0x067b: 0x42d4,
+ 0x067c: 0x35e5, 0x067d: 0x426d, 0x067e: 0x01d3, 0x067f: 0x426d,
+ // Block 0x1a, offset 0x680
+ 0x0680: 0x4286, 0x0681: 0x4468, 0x0682: 0x3dc2, 0x0683: 0x3687, 0x0684: 0x3dca,
+ 0x0686: 0x4820, 0x0687: 0x3de2, 0x0688: 0x35eb, 0x0689: 0x42da, 0x068a: 0x35f7, 0x068b: 0x42e0,
+ 0x068c: 0x3603, 0x068d: 0x446f, 0x068e: 0x4476, 0x068f: 0x447d, 0x0690: 0x369f, 0x0691: 0x3699,
+ 0x0692: 0x3dea, 0x0693: 0x44ca, 0x0696: 0x36a5, 0x0697: 0x3dfa,
+ 0x0698: 0x361b, 0x0699: 0x3615, 0x069a: 0x3609, 0x069b: 0x42e6, 0x069d: 0x4484,
+ 0x069e: 0x448b, 0x069f: 0x4492, 0x06a0: 0x36d5, 0x06a1: 0x36cf, 0x06a2: 0x3e52, 0x06a3: 0x44d2,
+ 0x06a4: 0x36b7, 0x06a5: 0x36bd, 0x06a6: 0x36db, 0x06a7: 0x3e62, 0x06a8: 0x364b, 0x06a9: 0x3645,
+ 0x06aa: 0x3639, 0x06ab: 0x42f2, 0x06ac: 0x3633, 0x06ad: 0x445a, 0x06ae: 0x4461, 0x06af: 0x0081,
+ 0x06b2: 0x3e9a, 0x06b3: 0x36e1, 0x06b4: 0x3ea2,
+ 0x06b6: 0x486e, 0x06b7: 0x3eba, 0x06b8: 0x3627, 0x06b9: 0x42ec, 0x06ba: 0x3657, 0x06bb: 0x42fe,
+ 0x06bc: 0x3663, 0x06bd: 0x4240, 0x06be: 0x4272,
+ // Block 0x1b, offset 0x6c0
+ 0x06c0: 0x1d1a, 0x06c1: 0x1d1e, 0x06c2: 0x0047, 0x06c3: 0x1d96, 0x06c5: 0x1d2a,
+ 0x06c6: 0x1d2e, 0x06c7: 0x00ec, 0x06c9: 0x1d9a, 0x06ca: 0x008f, 0x06cb: 0x0051,
+ 0x06cc: 0x0051, 0x06cd: 0x0051, 0x06ce: 0x0091, 0x06cf: 0x00dd, 0x06d0: 0x0053, 0x06d1: 0x0053,
+ 0x06d2: 0x0059, 0x06d3: 0x0099, 0x06d5: 0x005d, 0x06d6: 0x1acf,
+ 0x06d9: 0x0061, 0x06da: 0x0063, 0x06db: 0x0065, 0x06dc: 0x0065, 0x06dd: 0x0065,
+ 0x06e0: 0x1ae1, 0x06e1: 0x1d0a, 0x06e2: 0x1aea,
+ 0x06e4: 0x0075, 0x06e6: 0x01b8, 0x06e8: 0x0075,
+ 0x06ea: 0x0057, 0x06eb: 0x42b8, 0x06ec: 0x0045, 0x06ed: 0x0047, 0x06ef: 0x008b,
+ 0x06f0: 0x004b, 0x06f1: 0x004d, 0x06f3: 0x005b, 0x06f4: 0x009f, 0x06f5: 0x020f,
+ 0x06f6: 0x0212, 0x06f7: 0x0215, 0x06f8: 0x0218, 0x06f9: 0x0093, 0x06fb: 0x1cda,
+ 0x06fc: 0x01e8, 0x06fd: 0x01c1, 0x06fe: 0x0179, 0x06ff: 0x01a0,
+ // Block 0x1c, offset 0x700
+ 0x0700: 0x04d5, 0x0705: 0x0049,
+ 0x0706: 0x0089, 0x0707: 0x008b, 0x0708: 0x0093, 0x0709: 0x0095,
+ 0x0710: 0x2370, 0x0711: 0x237c,
+ 0x0712: 0x2430, 0x0713: 0x2358, 0x0714: 0x23dc, 0x0715: 0x2364, 0x0716: 0x23e2, 0x0717: 0x23fa,
+ 0x0718: 0x2406, 0x0719: 0x236a, 0x071a: 0x240c, 0x071b: 0x2376, 0x071c: 0x2400, 0x071d: 0x2412,
+ 0x071e: 0x2418, 0x071f: 0x1dfe, 0x0720: 0x0053, 0x0721: 0x1a9c, 0x0722: 0x1ce6, 0x0723: 0x1aa5,
+ 0x0724: 0x006d, 0x0725: 0x1aed, 0x0726: 0x1d12, 0x0727: 0x1e8a, 0x0728: 0x1aa8, 0x0729: 0x0071,
+ 0x072a: 0x1af9, 0x072b: 0x1d16, 0x072c: 0x0059, 0x072d: 0x0047, 0x072e: 0x0049, 0x072f: 0x005b,
+ 0x0730: 0x0093, 0x0731: 0x1b26, 0x0732: 0x1d5a, 0x0733: 0x1b2f, 0x0734: 0x00ad, 0x0735: 0x1ba4,
+ 0x0736: 0x1d8e, 0x0737: 0x1e9e, 0x0738: 0x1b32, 0x0739: 0x00b1, 0x073a: 0x1ba7, 0x073b: 0x1d92,
+ 0x073c: 0x0099, 0x073d: 0x0087, 0x073e: 0x0089, 0x073f: 0x009b,
+ // Block 0x1d, offset 0x740
+ 0x0741: 0x3bf0, 0x0743: 0x8800, 0x0744: 0x3bf7, 0x0745: 0x8800,
+ 0x0747: 0x3bfe, 0x0748: 0x8800, 0x0749: 0x3c05,
+ 0x074d: 0x8800,
+ 0x0760: 0x2f4f, 0x0761: 0x8800, 0x0762: 0x3c13,
+ 0x0764: 0x8800, 0x0765: 0x8800,
+ 0x076d: 0x3c0c, 0x076e: 0x2f4a, 0x076f: 0x2f54,
+ 0x0770: 0x3c1a, 0x0771: 0x3c21, 0x0772: 0x8800, 0x0773: 0x8800, 0x0774: 0x3c28, 0x0775: 0x3c2f,
+ 0x0776: 0x8800, 0x0777: 0x8800, 0x0778: 0x3c36, 0x0779: 0x3c3d, 0x077a: 0x8800, 0x077b: 0x8800,
+ 0x077c: 0x8800, 0x077d: 0x8800,
+ // Block 0x1e, offset 0x780
+ 0x0780: 0x3c44, 0x0781: 0x3c4b, 0x0782: 0x8800, 0x0783: 0x8800, 0x0784: 0x3c60, 0x0785: 0x3c67,
+ 0x0786: 0x8800, 0x0787: 0x8800, 0x0788: 0x3c6e, 0x0789: 0x3c75,
+ 0x0791: 0x8800,
+ 0x0792: 0x8800,
+ 0x07a2: 0x8800,
+ 0x07a8: 0x8800, 0x07a9: 0x8800,
+ 0x07ab: 0x8800, 0x07ac: 0x3c8a, 0x07ad: 0x3c91, 0x07ae: 0x3c98, 0x07af: 0x3c9f,
+ 0x07b2: 0x8800, 0x07b3: 0x8800, 0x07b4: 0x8800, 0x07b5: 0x8800,
+ // Block 0x1f, offset 0x7c0
+ 0x07e0: 0x0023, 0x07e1: 0x0025, 0x07e2: 0x0027, 0x07e3: 0x0029,
+ 0x07e4: 0x002b, 0x07e5: 0x002d, 0x07e6: 0x002f, 0x07e7: 0x0031, 0x07e8: 0x0033, 0x07e9: 0x19c4,
+ 0x07ea: 0x19c7, 0x07eb: 0x19ca, 0x07ec: 0x19cd, 0x07ed: 0x19d0, 0x07ee: 0x19d3, 0x07ef: 0x19d6,
+ 0x07f0: 0x19d9, 0x07f1: 0x19dc, 0x07f2: 0x19df, 0x07f3: 0x19e8, 0x07f4: 0x1baa, 0x07f5: 0x1bae,
+ 0x07f6: 0x1bb2, 0x07f7: 0x1bb6, 0x07f8: 0x1bba, 0x07f9: 0x1bbe, 0x07fa: 0x1bc2, 0x07fb: 0x1bc6,
+ 0x07fc: 0x1bca, 0x07fd: 0x1dc2, 0x07fe: 0x1dc7, 0x07ff: 0x1dcc,
+ // Block 0x20, offset 0x800
+ 0x0800: 0x1dd1, 0x0801: 0x1dd6, 0x0802: 0x1ddb, 0x0803: 0x1de0, 0x0804: 0x1de5, 0x0805: 0x1dea,
+ 0x0806: 0x1def, 0x0807: 0x1df4, 0x0808: 0x19c1, 0x0809: 0x19e5, 0x080a: 0x1a09, 0x080b: 0x1a2d,
+ 0x080c: 0x1a51, 0x080d: 0x1a5a, 0x080e: 0x1a60, 0x080f: 0x1a66, 0x0810: 0x1a6c, 0x0811: 0x1ca2,
+ 0x0812: 0x1ca6, 0x0813: 0x1caa, 0x0814: 0x1cae, 0x0815: 0x1cb2, 0x0816: 0x1cb6, 0x0817: 0x1cba,
+ 0x0818: 0x1cbe, 0x0819: 0x1cc2, 0x081a: 0x1cc6, 0x081b: 0x1cca, 0x081c: 0x1c36, 0x081d: 0x1c3a,
+ 0x081e: 0x1c3e, 0x081f: 0x1c42, 0x0820: 0x1c46, 0x0821: 0x1c4a, 0x0822: 0x1c4e, 0x0823: 0x1c52,
+ 0x0824: 0x1c56, 0x0825: 0x1c5a, 0x0826: 0x1c5e, 0x0827: 0x1c62, 0x0828: 0x1c66, 0x0829: 0x1c6a,
+ 0x082a: 0x1c6e, 0x082b: 0x1c72, 0x082c: 0x1c76, 0x082d: 0x1c7a, 0x082e: 0x1c7e, 0x082f: 0x1c82,
+ 0x0830: 0x1c86, 0x0831: 0x1c8a, 0x0832: 0x1c8e, 0x0833: 0x1c92, 0x0834: 0x1c96, 0x0835: 0x1c9a,
+ 0x0836: 0x0043, 0x0837: 0x0045, 0x0838: 0x0047, 0x0839: 0x0049, 0x083a: 0x004b, 0x083b: 0x004d,
+ 0x083c: 0x004f, 0x083d: 0x0051, 0x083e: 0x0053, 0x083f: 0x0055,
+ // Block 0x21, offset 0x840
+ 0x0840: 0x0731, 0x0841: 0x0755, 0x0842: 0x0761, 0x0843: 0x0771, 0x0844: 0x0779, 0x0845: 0x0785,
+ 0x0846: 0x078d, 0x0847: 0x0795, 0x0848: 0x07a1, 0x0849: 0x07f5, 0x084a: 0x080d, 0x084b: 0x081d,
+ 0x084c: 0x082d, 0x084d: 0x083d, 0x084e: 0x084d, 0x084f: 0x086d, 0x0850: 0x0871, 0x0851: 0x0875,
+ 0x0852: 0x08a9, 0x0853: 0x08d1, 0x0854: 0x08e1, 0x0855: 0x08e9, 0x0856: 0x08ed, 0x0857: 0x08f9,
+ 0x0858: 0x0915, 0x0859: 0x0919, 0x085a: 0x0931, 0x085b: 0x0935, 0x085c: 0x093d, 0x085d: 0x094d,
+ 0x085e: 0x09e9, 0x085f: 0x09fd, 0x0860: 0x0a3d, 0x0861: 0x0a51, 0x0862: 0x0a59, 0x0863: 0x0a5d,
+ 0x0864: 0x0a6d, 0x0865: 0x0a89, 0x0866: 0x0ab5, 0x0867: 0x0ac1, 0x0868: 0x0ae1, 0x0869: 0x0aed,
+ 0x086a: 0x0af1, 0x086b: 0x0af5, 0x086c: 0x0b0d, 0x086d: 0x0b11, 0x086e: 0x0b3d, 0x086f: 0x0b49,
+ 0x0870: 0x0b51, 0x0871: 0x0b59, 0x0872: 0x0b69, 0x0873: 0x0b71, 0x0874: 0x0b79, 0x0875: 0x0ba5,
+ 0x0876: 0x0ba9, 0x0877: 0x0bb1, 0x0878: 0x0bb5, 0x0879: 0x0bbd, 0x087a: 0x0bc5, 0x087b: 0x0bd5,
+ 0x087c: 0x0bf1, 0x087d: 0x0c69, 0x087e: 0x0c7d, 0x087f: 0x0c81,
+ // Block 0x22, offset 0x880
+ 0x0880: 0x0d01, 0x0881: 0x0d05, 0x0882: 0x0d19, 0x0883: 0x0d1d, 0x0884: 0x0d25, 0x0885: 0x0d2d,
+ 0x0886: 0x0d35, 0x0887: 0x0d41, 0x0888: 0x0d69, 0x0889: 0x0d79, 0x088a: 0x0d8d, 0x088b: 0x0dfd,
+ 0x088c: 0x0e09, 0x088d: 0x0e19, 0x088e: 0x0e25, 0x088f: 0x0e31, 0x0890: 0x0e39, 0x0891: 0x0e3d,
+ 0x0892: 0x0e41, 0x0893: 0x0e45, 0x0894: 0x0e49, 0x0895: 0x0f01, 0x0896: 0x0f49, 0x0897: 0x0f55,
+ 0x0898: 0x0f59, 0x0899: 0x0f5d, 0x089a: 0x0f61, 0x089b: 0x0f69, 0x089c: 0x0f6d, 0x089d: 0x0f81,
+ 0x089e: 0x0f9d, 0x089f: 0x0fa5, 0x08a0: 0x0fe5, 0x08a1: 0x0fe9, 0x08a2: 0x0ff1, 0x08a3: 0x0ff5,
+ 0x08a4: 0x0ffd, 0x08a5: 0x1001, 0x08a6: 0x1025, 0x08a7: 0x1029, 0x08a8: 0x1045, 0x08a9: 0x1049,
+ 0x08aa: 0x104d, 0x08ab: 0x1051, 0x08ac: 0x1065, 0x08ad: 0x1089, 0x08ae: 0x108d, 0x08af: 0x1091,
+ 0x08b0: 0x10b5, 0x08b1: 0x10f5, 0x08b2: 0x10f9, 0x08b3: 0x1119, 0x08b4: 0x1129, 0x08b5: 0x1131,
+ 0x08b6: 0x1151, 0x08b7: 0x1175, 0x08b8: 0x11b9, 0x08b9: 0x11c1, 0x08ba: 0x11d5, 0x08bb: 0x11e1,
+ 0x08bc: 0x11e9, 0x08bd: 0x11f1, 0x08be: 0x11f5, 0x08bf: 0x11f9,
+ // Block 0x23, offset 0x8c0
+ 0x08c0: 0x1211, 0x08c1: 0x1215, 0x08c2: 0x1231, 0x08c3: 0x1239, 0x08c4: 0x1241, 0x08c5: 0x1245,
+ 0x08c6: 0x1251, 0x08c7: 0x1259, 0x08c8: 0x125d, 0x08c9: 0x1261, 0x08ca: 0x1269, 0x08cb: 0x126d,
+ 0x08cc: 0x130d, 0x08cd: 0x1321, 0x08ce: 0x1355, 0x08cf: 0x1359, 0x08d0: 0x1361, 0x08d1: 0x138d,
+ 0x08d2: 0x1395, 0x08d3: 0x139d, 0x08d4: 0x13a5, 0x08d5: 0x13e1, 0x08d6: 0x13e5, 0x08d7: 0x13ed,
+ 0x08d8: 0x13f1, 0x08d9: 0x13f5, 0x08da: 0x1421, 0x08db: 0x1425, 0x08dc: 0x142d, 0x08dd: 0x1441,
+ 0x08de: 0x1445, 0x08df: 0x1461, 0x08e0: 0x1469, 0x08e1: 0x146d, 0x08e2: 0x1491, 0x08e3: 0x14b1,
+ 0x08e4: 0x14c1, 0x08e5: 0x14c5, 0x08e6: 0x14cd, 0x08e7: 0x14f9, 0x08e8: 0x14fd, 0x08e9: 0x150d,
+ 0x08ea: 0x1531, 0x08eb: 0x153d, 0x08ec: 0x154d, 0x08ed: 0x1565, 0x08ee: 0x156d, 0x08ef: 0x1571,
+ 0x08f0: 0x1575, 0x08f1: 0x1579, 0x08f2: 0x1585, 0x08f3: 0x1589, 0x08f4: 0x1591, 0x08f5: 0x15ad,
+ 0x08f6: 0x15b1, 0x08f7: 0x15b5, 0x08f8: 0x15cd, 0x08f9: 0x15d1, 0x08fa: 0x15d9, 0x08fb: 0x15ed,
+ 0x08fc: 0x15f1, 0x08fd: 0x15f5, 0x08fe: 0x15fd, 0x08ff: 0x1601,
+ // Block 0x24, offset 0x900
+ 0x0906: 0x8800, 0x090b: 0x8800,
+ 0x090c: 0x3ef2, 0x090d: 0x8800, 0x090e: 0x3efa, 0x090f: 0x8800, 0x0910: 0x3f02, 0x0911: 0x8800,
+ 0x0912: 0x3f0a, 0x0913: 0x8800, 0x0914: 0x3f12, 0x0915: 0x8800, 0x0916: 0x3f1a, 0x0917: 0x8800,
+ 0x0918: 0x3f22, 0x0919: 0x8800, 0x091a: 0x3f2a, 0x091b: 0x8800, 0x091c: 0x3f32, 0x091d: 0x8800,
+ 0x091e: 0x3f3a, 0x091f: 0x8800, 0x0920: 0x3f42, 0x0921: 0x8800, 0x0922: 0x3f4a,
+ 0x0924: 0x8800, 0x0925: 0x3f52, 0x0926: 0x8800, 0x0927: 0x3f5a, 0x0928: 0x8800, 0x0929: 0x3f62,
+ 0x092f: 0x8800,
+ 0x0930: 0x3f6a, 0x0931: 0x3f72, 0x0932: 0x8800, 0x0933: 0x3f7a, 0x0934: 0x3f82, 0x0935: 0x8800,
+ 0x0936: 0x3f8a, 0x0937: 0x3f92, 0x0938: 0x8800, 0x0939: 0x3f9a, 0x093a: 0x3fa2, 0x093b: 0x8800,
+ 0x093c: 0x3faa, 0x093d: 0x3fb2,
+ // Block 0x25, offset 0x940
+ 0x0954: 0x3eea,
+ 0x0959: 0x8608, 0x095a: 0x8608, 0x095b: 0x42c2, 0x095c: 0x42c8, 0x095d: 0x8800,
+ 0x095e: 0x3fba, 0x095f: 0x285f,
+ 0x0966: 0x8800,
+ 0x096b: 0x8800, 0x096c: 0x3fca, 0x096d: 0x8800, 0x096e: 0x3fd2, 0x096f: 0x8800,
+ 0x0970: 0x3fda, 0x0971: 0x8800, 0x0972: 0x3fe2, 0x0973: 0x8800, 0x0974: 0x3fea, 0x0975: 0x8800,
+ 0x0976: 0x3ff2, 0x0977: 0x8800, 0x0978: 0x3ffa, 0x0979: 0x8800, 0x097a: 0x4002, 0x097b: 0x8800,
+ 0x097c: 0x400a, 0x097d: 0x8800, 0x097e: 0x4012, 0x097f: 0x8800,
+ // Block 0x26, offset 0x980
+ 0x0980: 0x401a, 0x0981: 0x8800, 0x0982: 0x4022, 0x0984: 0x8800, 0x0985: 0x402a,
+ 0x0986: 0x8800, 0x0987: 0x4032, 0x0988: 0x8800, 0x0989: 0x403a,
+ 0x098f: 0x8800, 0x0990: 0x4042, 0x0991: 0x404a,
+ 0x0992: 0x8800, 0x0993: 0x4052, 0x0994: 0x405a, 0x0995: 0x8800, 0x0996: 0x4062, 0x0997: 0x406a,
+ 0x0998: 0x8800, 0x0999: 0x4072, 0x099a: 0x407a, 0x099b: 0x8800, 0x099c: 0x4082, 0x099d: 0x408a,
+ 0x09af: 0x8800,
+ 0x09b0: 0x8800, 0x09b1: 0x8800, 0x09b2: 0x8800, 0x09b4: 0x3fc2,
+ 0x09b7: 0x4092, 0x09b8: 0x409a, 0x09b9: 0x40a2, 0x09ba: 0x40aa,
+ 0x09bd: 0x8800, 0x09be: 0x40b2, 0x09bf: 0x2874,
+ // Block 0x27, offset 0x9c0
+ 0x09c0: 0x03a9, 0x09c1: 0x03ad, 0x09c2: 0x047d, 0x09c3: 0x0481, 0x09c4: 0x03b1, 0x09c5: 0x03b5,
+ 0x09c6: 0x03b9, 0x09c7: 0x0415, 0x09c8: 0x0419, 0x09c9: 0x041d, 0x09ca: 0x0421, 0x09cb: 0x0425,
+ 0x09cc: 0x0429, 0x09cd: 0x042d, 0x09ce: 0x0431,
+ 0x09d2: 0x0731, 0x09d3: 0x078d, 0x09d4: 0x073d, 0x09d5: 0x09ed, 0x09d6: 0x0741, 0x09d7: 0x0759,
+ 0x09d8: 0x0745, 0x09d9: 0x1005, 0x09da: 0x0779, 0x09db: 0x074d, 0x09dc: 0x0735, 0x09dd: 0x0a71,
+ 0x09de: 0x0a01, 0x09df: 0x07a1,
+ // Block 0x28, offset 0xa00
+ 0x0a00: 0x2196, 0x0a01: 0x219c, 0x0a02: 0x21a2, 0x0a03: 0x21a8, 0x0a04: 0x21ae, 0x0a05: 0x21b4,
+ 0x0a06: 0x21ba, 0x0a07: 0x21c0, 0x0a08: 0x21c6, 0x0a09: 0x21cc, 0x0a0a: 0x21d2, 0x0a0b: 0x21d8,
+ 0x0a0c: 0x21de, 0x0a0d: 0x21e4, 0x0a0e: 0x28d1, 0x0a0f: 0x28da, 0x0a10: 0x28e3, 0x0a11: 0x28ec,
+ 0x0a12: 0x28f5, 0x0a13: 0x28fe, 0x0a14: 0x2907, 0x0a15: 0x2910, 0x0a16: 0x2919, 0x0a17: 0x292b,
+ 0x0a18: 0x2934, 0x0a19: 0x293d, 0x0a1a: 0x2946, 0x0a1b: 0x294f, 0x0a1c: 0x2922, 0x0a1d: 0x2d74,
+ 0x0a1e: 0x2ca5, 0x0a20: 0x21ea, 0x0a21: 0x2202, 0x0a22: 0x21f6, 0x0a23: 0x224a,
+ 0x0a24: 0x2208, 0x0a25: 0x2226, 0x0a26: 0x21f0, 0x0a27: 0x2220, 0x0a28: 0x21fc, 0x0a29: 0x2232,
+ 0x0a2a: 0x2262, 0x0a2b: 0x2280, 0x0a2c: 0x227a, 0x0a2d: 0x226e, 0x0a2e: 0x22bc, 0x0a2f: 0x2250,
+ 0x0a30: 0x225c, 0x0a31: 0x2274, 0x0a32: 0x2268, 0x0a33: 0x2292, 0x0a34: 0x223e, 0x0a35: 0x2286,
+ 0x0a36: 0x22b0, 0x0a37: 0x2298, 0x0a38: 0x222c, 0x0a39: 0x220e, 0x0a3a: 0x2244, 0x0a3b: 0x2256,
+ 0x0a3c: 0x228c, 0x0a3d: 0x2214, 0x0a3e: 0x22b6, 0x0a3f: 0x2238,
+ // Block 0x29, offset 0xa40
+ 0x0a40: 0x229e, 0x0a41: 0x221a, 0x0a42: 0x22a4, 0x0a43: 0x22aa, 0x0a44: 0x09a1, 0x0a45: 0x0b75,
+ 0x0a46: 0x0d19, 0x0a47: 0x1139,
+ 0x0a50: 0x1d06, 0x0a51: 0x19eb,
+ 0x0a52: 0x19ee, 0x0a53: 0x19f1, 0x0a54: 0x19f4, 0x0a55: 0x19f7, 0x0a56: 0x19fa, 0x0a57: 0x19fd,
+ 0x0a58: 0x1a00, 0x0a59: 0x1a03, 0x0a5a: 0x1a0c, 0x0a5b: 0x1a0f, 0x0a5c: 0x1a12, 0x0a5d: 0x1a15,
+ 0x0a5e: 0x1a18, 0x0a5f: 0x1a1b, 0x0a60: 0x030d, 0x0a61: 0x0315, 0x0a62: 0x0319, 0x0a63: 0x0321,
+ 0x0a64: 0x0325, 0x0a65: 0x0329, 0x0a66: 0x0331, 0x0a67: 0x0339, 0x0a68: 0x033d, 0x0a69: 0x0345,
+ 0x0a6a: 0x0349, 0x0a6b: 0x034d, 0x0a6c: 0x0351, 0x0a6d: 0x0355, 0x0a6e: 0x27d3, 0x0a6f: 0x27da,
+ 0x0a70: 0x27e1, 0x0a71: 0x27e8, 0x0a72: 0x27ef, 0x0a73: 0x27f6, 0x0a74: 0x27fd, 0x0a75: 0x2804,
+ 0x0a76: 0x2812, 0x0a77: 0x2819, 0x0a78: 0x2820, 0x0a79: 0x2827, 0x0a7a: 0x282e, 0x0a7b: 0x2835,
+ 0x0a7c: 0x2cc4, 0x0a7d: 0x2b39, 0x0a7e: 0x280b,
+ // Block 0x2a, offset 0xa80
+ 0x0a80: 0x0731, 0x0a81: 0x078d, 0x0a82: 0x073d, 0x0a83: 0x09ed, 0x0a84: 0x0791, 0x0a85: 0x0821,
+ 0x0a86: 0x0739, 0x0a87: 0x081d, 0x0a88: 0x077d, 0x0a89: 0x08f9, 0x0a8a: 0x0d79, 0x0a8b: 0x0f01,
+ 0x0a8c: 0x0e49, 0x0a8d: 0x0d8d, 0x0a8e: 0x14cd, 0x0a8f: 0x09fd, 0x0a90: 0x0d41, 0x0a91: 0x0dbd,
+ 0x0a92: 0x0d7d, 0x0a93: 0x10bd, 0x0a94: 0x096d, 0x0a95: 0x0f75, 0x0a96: 0x13f9, 0x0a97: 0x10d1,
+ 0x0a98: 0x08b5, 0x0a99: 0x1101, 0x0a9a: 0x100d, 0x0a9b: 0x0a89, 0x0a9c: 0x1481, 0x0a9d: 0x07f1,
+ 0x0a9e: 0x091d, 0x0a9f: 0x0e69, 0x0aa0: 0x1595, 0x0aa1: 0x07b5, 0x0aa2: 0x0845, 0x0aa3: 0x0e0d,
+ 0x0aa4: 0x0741, 0x0aa5: 0x0759, 0x0aa6: 0x0745, 0x0aa7: 0x0b4d, 0x0aa8: 0x0961, 0x0aa9: 0x08f1,
+ 0x0aaa: 0x0ac9, 0x0aab: 0x0abd, 0x0aac: 0x105d, 0x0aad: 0x07b1, 0x0aae: 0x140d, 0x0aaf: 0x090d,
+ 0x0ab0: 0x0a65, 0x0ab1: 0x1a1e, 0x0ab2: 0x1a21, 0x0ab3: 0x1a24, 0x0ab4: 0x1a27, 0x0ab5: 0x1a30,
+ 0x0ab6: 0x1a33, 0x0ab7: 0x1a36, 0x0ab8: 0x1a39, 0x0ab9: 0x1a3c, 0x0aba: 0x1a3f, 0x0abb: 0x1a42,
+ 0x0abc: 0x1a45, 0x0abd: 0x1a48, 0x0abe: 0x1a4b, 0x0abf: 0x1a54,
+ // Block 0x2b, offset 0xac0
+ 0x0ac0: 0x1e08, 0x0ac1: 0x1e17, 0x0ac2: 0x1e26, 0x0ac3: 0x1e35, 0x0ac4: 0x1e44, 0x0ac5: 0x1e53,
+ 0x0ac6: 0x1e62, 0x0ac7: 0x1e71, 0x0ac8: 0x1e80, 0x0ac9: 0x22ce, 0x0aca: 0x22e0, 0x0acb: 0x22f2,
+ 0x0acc: 0x1a96, 0x0acd: 0x1d46, 0x0ace: 0x1b14, 0x0acf: 0x1cea, 0x0ad0: 0x053d, 0x0ad1: 0x0545,
+ 0x0ad2: 0x054d, 0x0ad3: 0x0555, 0x0ad4: 0x055d, 0x0ad5: 0x0561, 0x0ad6: 0x0565, 0x0ad7: 0x0569,
+ 0x0ad8: 0x056d, 0x0ad9: 0x0571, 0x0ada: 0x0575, 0x0adb: 0x0579, 0x0adc: 0x057d, 0x0add: 0x0581,
+ 0x0ade: 0x0585, 0x0adf: 0x0589, 0x0ae0: 0x058d, 0x0ae1: 0x0595, 0x0ae2: 0x0599, 0x0ae3: 0x059d,
+ 0x0ae4: 0x05a1, 0x0ae5: 0x05a5, 0x0ae6: 0x05a9, 0x0ae7: 0x05ad, 0x0ae8: 0x05b1, 0x0ae9: 0x05b5,
+ 0x0aea: 0x05b9, 0x0aeb: 0x05bd, 0x0aec: 0x05c1, 0x0aed: 0x05c5, 0x0aee: 0x05c9, 0x0aef: 0x05cd,
+ 0x0af0: 0x05d1, 0x0af1: 0x05d5, 0x0af2: 0x05d9, 0x0af3: 0x05e1, 0x0af4: 0x05e9, 0x0af5: 0x05f1,
+ 0x0af6: 0x05f5, 0x0af7: 0x05f9, 0x0af8: 0x05fd, 0x0af9: 0x0601, 0x0afa: 0x0605, 0x0afb: 0x0609,
+ 0x0afc: 0x060d, 0x0afd: 0x0611, 0x0afe: 0x0615,
+ // Block 0x2c, offset 0xb00
+ 0x0b00: 0x2cd4, 0x0b01: 0x2b60, 0x0b02: 0x2ce4, 0x0b03: 0x2a2b, 0x0b04: 0x2ede, 0x0b05: 0x2a35,
+ 0x0b06: 0x2a3f, 0x0b07: 0x2f22, 0x0b08: 0x2b6d, 0x0b09: 0x2a49, 0x0b0a: 0x2a53, 0x0b0b: 0x2a5d,
+ 0x0b0c: 0x2b94, 0x0b0d: 0x2ba1, 0x0b0e: 0x2b7a, 0x0b0f: 0x2b87, 0x0b10: 0x2eb4, 0x0b11: 0x2bae,
+ 0x0b12: 0x2bbb, 0x0b13: 0x2d86, 0x0b14: 0x2866, 0x0b15: 0x2d99, 0x0b16: 0x2dac, 0x0b17: 0x2cf4,
+ 0x0b18: 0x2bc8, 0x0b19: 0x2dbf, 0x0b1a: 0x2dd2, 0x0b1b: 0x2bd5, 0x0b1c: 0x2a67, 0x0b1d: 0x2a71,
+ 0x0b1e: 0x2ec2, 0x0b1f: 0x2be2, 0x0b20: 0x2d04, 0x0b21: 0x2eef, 0x0b22: 0x2a7b, 0x0b23: 0x2a85,
+ 0x0b24: 0x2bef, 0x0b25: 0x2a8f, 0x0b26: 0x2a99, 0x0b27: 0x287b, 0x0b28: 0x2882, 0x0b29: 0x2aa3,
+ 0x0b2a: 0x2aad, 0x0b2b: 0x2de5, 0x0b2c: 0x2bfc, 0x0b2d: 0x2d14, 0x0b2e: 0x2df8, 0x0b2f: 0x2c09,
+ 0x0b30: 0x2ac1, 0x0b31: 0x2ab7, 0x0b32: 0x2f36, 0x0b33: 0x2c16, 0x0b34: 0x2e0b, 0x0b35: 0x2acb,
+ 0x0b36: 0x2d24, 0x0b37: 0x2ad5, 0x0b38: 0x2c30, 0x0b39: 0x2adf, 0x0b3a: 0x2c3d, 0x0b3b: 0x2f00,
+ 0x0b3c: 0x2c23, 0x0b3d: 0x2d34, 0x0b3e: 0x2c4a, 0x0b3f: 0x2889,
+ // Block 0x2d, offset 0xb40
+ 0x0b40: 0x2f11, 0x0b41: 0x2ae9, 0x0b42: 0x2af3, 0x0b43: 0x2c57, 0x0b44: 0x2afd, 0x0b45: 0x2b07,
+ 0x0b46: 0x2b11, 0x0b47: 0x2d44, 0x0b48: 0x2c64, 0x0b49: 0x2890, 0x0b4a: 0x2e1e, 0x0b4b: 0x2ea9,
+ 0x0b4c: 0x2d54, 0x0b4d: 0x2c71, 0x0b4e: 0x2ed0, 0x0b4f: 0x2b1b, 0x0b50: 0x2b25, 0x0b51: 0x2c7e,
+ 0x0b52: 0x2897, 0x0b53: 0x2c8b, 0x0b54: 0x2d64, 0x0b55: 0x289e, 0x0b56: 0x2e31, 0x0b57: 0x2b2f,
+ 0x0b58: 0x1df9, 0x0b59: 0x1e0d, 0x0b5a: 0x1e1c, 0x0b5b: 0x1e2b, 0x0b5c: 0x1e3a, 0x0b5d: 0x1e49,
+ 0x0b5e: 0x1e58, 0x0b5f: 0x1e67, 0x0b60: 0x1e76, 0x0b61: 0x1e85, 0x0b62: 0x22d4, 0x0b63: 0x22e6,
+ 0x0b64: 0x22f8, 0x0b65: 0x2304, 0x0b66: 0x2310, 0x0b67: 0x231c, 0x0b68: 0x2328, 0x0b69: 0x2334,
+ 0x0b6a: 0x2340, 0x0b6b: 0x234c, 0x0b6c: 0x2388, 0x0b6d: 0x2394, 0x0b6e: 0x23a0, 0x0b6f: 0x23ac,
+ 0x0b70: 0x23b8, 0x0b71: 0x1d56, 0x0b72: 0x1b08, 0x0b73: 0x1a78, 0x0b74: 0x1d26, 0x0b75: 0x1b89,
+ 0x0b76: 0x1b98, 0x0b77: 0x1b0e, 0x0b78: 0x1d3e, 0x0b79: 0x1d42, 0x0b7a: 0x1aa2, 0x0b7b: 0x28ac,
+ 0x0b7c: 0x28ba, 0x0b7d: 0x28a5, 0x0b7e: 0x28b3, 0x0b7f: 0x2c98,
+ // Block 0x2e, offset 0xb80
+ 0x0b80: 0x1b8c, 0x0b81: 0x1b74, 0x0b82: 0x1da2, 0x0b83: 0x1b5c, 0x0b84: 0x1b35, 0x0b85: 0x1aab,
+ 0x0b86: 0x1aba, 0x0b87: 0x1a8a, 0x0b88: 0x1d32, 0x0b89: 0x1e94, 0x0b8a: 0x1b8f, 0x0b8b: 0x1b77,
+ 0x0b8c: 0x1da6, 0x0b8d: 0x1db2, 0x0b8e: 0x1b68, 0x0b8f: 0x1b3e, 0x0b90: 0x1a99, 0x0b91: 0x1d5e,
+ 0x0b92: 0x1cf2, 0x0b93: 0x1cde, 0x0b94: 0x1d0e, 0x0b95: 0x1db6, 0x0b96: 0x1b6b, 0x0b97: 0x1b0b,
+ 0x0b98: 0x1b41, 0x0b99: 0x1b20, 0x0b9a: 0x1b83, 0x0b9b: 0x1dba, 0x0b9c: 0x1b6e, 0x0b9d: 0x1b02,
+ 0x0b9e: 0x1b44, 0x0b9f: 0x1d7e, 0x0ba0: 0x1d36, 0x0ba1: 0x1b56, 0x0ba2: 0x1d66, 0x0ba3: 0x1d82,
+ 0x0ba4: 0x1d3a, 0x0ba5: 0x1b59, 0x0ba6: 0x1d6a, 0x0ba7: 0x242a, 0x0ba8: 0x243e, 0x0ba9: 0x1ad8,
+ 0x0baa: 0x1d62, 0x0bab: 0x1cf6, 0x0bac: 0x1ce2, 0x0bad: 0x1d8a, 0x0bae: 0x28c1, 0x0baf: 0x2958,
+ 0x0bb0: 0x1b9b, 0x0bb1: 0x1b86, 0x0bb2: 0x1dbe, 0x0bb3: 0x1b71, 0x0bb4: 0x1b92, 0x0bb5: 0x1b7a,
+ 0x0bb6: 0x1daa, 0x0bb7: 0x1b5f, 0x0bb8: 0x1b38, 0x0bb9: 0x1ac3, 0x0bba: 0x1b95, 0x0bbb: 0x1b7d,
+ 0x0bbc: 0x1dae, 0x0bbd: 0x1b62, 0x0bbe: 0x1b3b, 0x0bbf: 0x1ac6,
+ // Block 0x2f, offset 0xbc0
+ 0x0bc0: 0x1d6e, 0x0bc1: 0x1cfa, 0x0bc2: 0x1e8f, 0x0bc3: 0x1a7b, 0x0bc4: 0x1afc, 0x0bc5: 0x1aff,
+ 0x0bc6: 0x2437, 0x0bc7: 0x1cd6, 0x0bc8: 0x1b05, 0x0bc9: 0x1a8d, 0x0bca: 0x1b23, 0x0bcb: 0x1a90,
+ 0x0bcc: 0x1b2c, 0x0bcd: 0x1aae, 0x0bce: 0x1ab1, 0x0bcf: 0x1b47, 0x0bd0: 0x1b4d, 0x0bd1: 0x1b50,
+ 0x0bd2: 0x1d72, 0x0bd3: 0x1b53, 0x0bd4: 0x1b65, 0x0bd5: 0x1d7a, 0x0bd6: 0x1d86, 0x0bd7: 0x1ad2,
+ 0x0bd8: 0x1e99, 0x0bd9: 0x1cfe, 0x0bda: 0x1ad5, 0x0bdb: 0x1b9e, 0x0bdc: 0x1ae7, 0x0bdd: 0x1af6,
+ 0x0bde: 0x2424, 0x0bdf: 0x241e, 0x0be0: 0x1e03, 0x0be1: 0x1e12, 0x0be2: 0x1e21, 0x0be3: 0x1e30,
+ 0x0be4: 0x1e3f, 0x0be5: 0x1e4e, 0x0be6: 0x1e5d, 0x0be7: 0x1e6c, 0x0be8: 0x1e7b, 0x0be9: 0x22c8,
+ 0x0bea: 0x22da, 0x0beb: 0x22ec, 0x0bec: 0x22fe, 0x0bed: 0x230a, 0x0bee: 0x2316, 0x0bef: 0x2322,
+ 0x0bf0: 0x232e, 0x0bf1: 0x233a, 0x0bf2: 0x2346, 0x0bf3: 0x2382, 0x0bf4: 0x238e, 0x0bf5: 0x239a,
+ 0x0bf6: 0x23a6, 0x0bf7: 0x23b2, 0x0bf8: 0x23be, 0x0bf9: 0x23c4, 0x0bfa: 0x23ca, 0x0bfb: 0x23d0,
+ 0x0bfc: 0x23d6, 0x0bfd: 0x23e8, 0x0bfe: 0x23ee, 0x0bff: 0x1d52,
+ // Block 0x30, offset 0xc00
+ 0x0c00: 0x13e9, 0x0c01: 0x0d6d, 0x0c02: 0x1445, 0x0c03: 0x1411, 0x0c04: 0x0ec9, 0x0c05: 0x075d,
+ 0x0c06: 0x0951, 0x0c07: 0x1699, 0x0c08: 0x1699, 0x0c09: 0x0a7d, 0x0c0a: 0x14cd, 0x0c0b: 0x09b5,
+ 0x0c0c: 0x0a79, 0x0c0d: 0x0c61, 0x0c0e: 0x1041, 0x0c0f: 0x11d1, 0x0c10: 0x1309, 0x0c11: 0x1345,
+ 0x0c12: 0x1379, 0x0c13: 0x148d, 0x0c14: 0x0de5, 0x0c15: 0x0e71, 0x0c16: 0x0f1d, 0x0c17: 0x0fb5,
+ 0x0c18: 0x12d1, 0x0c19: 0x14b5, 0x0c1a: 0x15e1, 0x0c1b: 0x0781, 0x0c1c: 0x0925, 0x0c1d: 0x0df9,
+ 0x0c1e: 0x0f41, 0x0c1f: 0x1305, 0x0c20: 0x1631, 0x0c21: 0x0b25, 0x0c22: 0x0ee9, 0x0c23: 0x12f5,
+ 0x0c24: 0x1389, 0x0c25: 0x0c95, 0x0c26: 0x122d, 0x0c27: 0x1351, 0x0c28: 0x0b91, 0x0c29: 0x0d81,
+ 0x0c2a: 0x0e89, 0x0c2b: 0x0f8d, 0x0c2c: 0x1499, 0x0c2d: 0x07c1, 0x0c2e: 0x0859, 0x0c2f: 0x08c5,
+ 0x0c30: 0x0cfd, 0x0c31: 0x0df1, 0x0c32: 0x0f3d, 0x0c33: 0x1061, 0x0c34: 0x11e9, 0x0c35: 0x12fd,
+ 0x0c36: 0x1315, 0x0c37: 0x1439, 0x0c38: 0x155d, 0x0c39: 0x1611, 0x0c3a: 0x162d, 0x0c3b: 0x109d,
+ 0x0c3c: 0x10dd, 0x0c3d: 0x1195, 0x0c3e: 0x12b5, 0x0c3f: 0x14e9,
+ // Block 0x31, offset 0xc40
+ 0x0c40: 0x1639, 0x0c41: 0x13bd, 0x0c42: 0x0a39, 0x0c43: 0x0bad, 0x0c44: 0x114d, 0x0c45: 0x120d,
+ 0x0c46: 0x0f71, 0x0c47: 0x10a5, 0x0c48: 0x1409, 0x0c49: 0x1555, 0x0c4a: 0x0a35, 0x0c4b: 0x0b01,
+ 0x0c4c: 0x0de9, 0x0c4d: 0x0e9d, 0x0c4e: 0x0ed1, 0x0c4f: 0x1185, 0x0c50: 0x11ad, 0x0c51: 0x1515,
+ 0x0c52: 0x08c1, 0x0c53: 0x1219, 0x0c54: 0x0865, 0x0c55: 0x0861, 0x0c56: 0x1109, 0x0c57: 0x1199,
+ 0x0c58: 0x12cd, 0x0c59: 0x151d, 0x0c5a: 0x13d9, 0x0c5b: 0x0c99, 0x0c5c: 0x0de5, 0x0c5d: 0x13c9,
+ 0x0c5e: 0x0769, 0x0c5f: 0x0ad5, 0x0c60: 0x0c05, 0x0c61: 0x0fa1, 0x0c62: 0x1021, 0x0c63: 0x08e5,
+ 0x0c64: 0x10ad, 0x0c65: 0x07d1, 0x0c66: 0x0be9, 0x0c67: 0x0749, 0x0c68: 0x0e5d, 0x0c69: 0x0d15,
+ 0x0c6a: 0x1181, 0x0c6b: 0x0939, 0x0c6c: 0x0a25, 0x0c6d: 0x106d, 0x0c6e: 0x12d5, 0x0c6f: 0x13ad,
+ 0x0c70: 0x0e29, 0x0c71: 0x1469, 0x0c72: 0x0e55, 0x0c73: 0x0ca9, 0x0c74: 0x128d, 0x0c75: 0x0cc9,
+ 0x0c76: 0x101d, 0x0c77: 0x079d, 0x0c78: 0x0819, 0x0c79: 0x085d, 0x0c7a: 0x0dc5, 0x0c7b: 0x116d,
+ 0x0c7c: 0x1265, 0x0c7d: 0x13b9, 0x0c7e: 0x14c9, 0x0c7f: 0x08cd,
+ // Block 0x32, offset 0xc80
+ 0x0c80: 0x0981, 0x0c81: 0x0a89, 0x0c82: 0x0ba1, 0x0c83: 0x0d31, 0x0c84: 0x0eed, 0x0c85: 0x10b1,
+ 0x0c86: 0x1505, 0x0c87: 0x15e9, 0x0c88: 0x163d, 0x0c89: 0x1655, 0x0c8a: 0x08a9, 0x0c8b: 0x0d65,
+ 0x0c8c: 0x0e15, 0x0c8d: 0x145d, 0x0c8e: 0x0b6d, 0x0c8f: 0x0c49, 0x0c90: 0x0c65, 0x0c91: 0x0cf5,
+ 0x0c92: 0x0edd, 0x0c93: 0x0f29, 0x0c94: 0x0fd9, 0x0c95: 0x10fd, 0x0c96: 0x11a1, 0x0c97: 0x1205,
+ 0x0c98: 0x144d, 0x0c99: 0x12dd, 0x0c9a: 0x1475, 0x0c9b: 0x14ed, 0x0c9c: 0x0881, 0x0c9d: 0x08ad,
+ 0x0c9e: 0x0995, 0x0c9f: 0x0f19, 0x0ca0: 0x1365, 0x0ca1: 0x13ad, 0x0ca2: 0x0b8d, 0x0ca3: 0x0bfd,
+ 0x0ca4: 0x0cc1, 0x0ca5: 0x0e21, 0x0ca6: 0x1149, 0x0ca7: 0x0f95, 0x0ca8: 0x07ad, 0x0ca9: 0x09f1,
+ 0x0caa: 0x0ad5, 0x0cab: 0x0b39, 0x0cac: 0x0c09, 0x0cad: 0x0fb1, 0x0cae: 0x0fcd, 0x0caf: 0x11dd,
+ 0x0cb0: 0x11fd, 0x0cb1: 0x14d1, 0x0cb2: 0x1551, 0x0cb3: 0x1561, 0x0cb4: 0x159d, 0x0cb5: 0x07c5,
+ 0x0cb6: 0x10f1, 0x0cb7: 0x14bd, 0x0cb8: 0x1539, 0x0cb9: 0x0c21, 0x0cba: 0x0789, 0x0cbb: 0x07e9,
+ 0x0cbc: 0x0ad9, 0x0cbd: 0x0af9, 0x0cbe: 0x0d21, 0x0cbf: 0x0de5,
+ // Block 0x33, offset 0xcc0
+ 0x0cc0: 0x0f35, 0x0cc1: 0x103d, 0x0cc2: 0x12e9, 0x0cc3: 0x1489, 0x0cc4: 0x1691, 0x0cc5: 0x0d55,
+ 0x0cc6: 0x1511, 0x0cc7: 0x08a5, 0x0cc8: 0x0da1, 0x0cc9: 0x0dad, 0x0cca: 0x0e81, 0x0ccb: 0x0eb9,
+ 0x0ccc: 0x0fbd, 0x0ccd: 0x1019, 0x0cce: 0x1099, 0x0ccf: 0x117d, 0x0cd0: 0x15a9, 0x0cd1: 0x0821,
+ 0x0cd2: 0x0c75, 0x0cd3: 0x1521, 0x0cd4: 0x07d9, 0x0cd5: 0x0b1d, 0x0cd6: 0x0ea1, 0x0cd7: 0x1451,
+ 0x0cd8: 0x0bd9, 0x0cd9: 0x0c29, 0x0cda: 0x0db5, 0x0cdb: 0x0fa1, 0x0cdc: 0x1529, 0x0cdd: 0x0889,
+ 0x0cde: 0x0971, 0x0cdf: 0x0b09, 0x0ce0: 0x0d45, 0x0ce1: 0x0d91, 0x0ce2: 0x0dd1, 0x0ce3: 0x0e65,
+ 0x0ce4: 0x0fb9, 0x0ce5: 0x102d, 0x0ce6: 0x11c9, 0x0ce7: 0x1369, 0x0ce8: 0x1375, 0x0ce9: 0x14c5,
+ 0x0cea: 0x1545, 0x0ceb: 0x08f5, 0x0cec: 0x0ebd, 0x0ced: 0x0975, 0x0cee: 0x0f39, 0x0cef: 0x0fdd,
+ 0x0cf0: 0x12f9, 0x0cf1: 0x152d, 0x0cf2: 0x1619, 0x0cf3: 0x1641, 0x0cf4: 0x0da9, 0x0cf5: 0x0e99,
+ 0x0cf6: 0x1235, 0x0cf7: 0x1129, 0x0cf8: 0x1135, 0x0cf9: 0x1159, 0x0cfa: 0x0f89, 0x0cfb: 0x0f11,
+ 0x0cfc: 0x13d5, 0x0cfd: 0x07a5, 0x0cfe: 0x129d, 0x0cff: 0x088d,
+ // Block 0x34, offset 0xd00
+ 0x0d00: 0x087d, 0x0d01: 0x0b7d, 0x0d02: 0x0c9d, 0x0d03: 0x1165, 0x0d04: 0x0ac5, 0x0d05: 0x0e75,
+ 0x0d06: 0x0d61, 0x0d07: 0x1459, 0x0d08: 0x1359, 0x0d09: 0x1519, 0x0d0a: 0x1395, 0x0d0b: 0x0b99,
+ 0x0d0c: 0x07f9, 0x0d0d: 0x09cd, 0x0d10: 0x0a21,
+ 0x0d12: 0x0d51, 0x0d15: 0x0869, 0x0d16: 0x0f91, 0x0d17: 0x1055,
+ 0x0d18: 0x10b9, 0x0d19: 0x10d5, 0x0d1a: 0x10d9, 0x0d1b: 0x10ed, 0x0d1c: 0x1569, 0x0d1d: 0x115d,
+ 0x0d1e: 0x11e1, 0x0d20: 0x1301, 0x0d22: 0x13c5,
+ 0x0d25: 0x1479, 0x0d26: 0x14a5,
+ 0x0d2a: 0x15bd, 0x0d2b: 0x15c1, 0x0d2c: 0x15c5, 0x0d2d: 0x1629, 0x0d2e: 0x149d, 0x0d2f: 0x1535,
+ 0x0d30: 0x07c9, 0x0d31: 0x07ed, 0x0d32: 0x0801, 0x0d33: 0x08bd, 0x0d34: 0x08c9, 0x0d35: 0x0909,
+ 0x0d36: 0x09bd, 0x0d37: 0x09d9, 0x0d38: 0x09e1, 0x0d39: 0x0a1d, 0x0d3a: 0x0a29, 0x0d3b: 0x0b05,
+ 0x0d3c: 0x0b0d, 0x0d3d: 0x0c15, 0x0d3e: 0x0c3d, 0x0d3f: 0x0c45,
+ // Block 0x35, offset 0xd40
+ 0x0d40: 0x0c5d, 0x0d41: 0x0d09, 0x0d42: 0x0d39, 0x0d43: 0x0d59, 0x0d44: 0x0dc9, 0x0d45: 0x0e8d,
+ 0x0d46: 0x0ea9, 0x0d47: 0x0ed9, 0x0d48: 0x0f2d, 0x0d49: 0x0f4d, 0x0d4a: 0x0fc1, 0x0d4b: 0x10a1,
+ 0x0d4c: 0x10bd, 0x0d4d: 0x10c5, 0x0d4e: 0x10c1, 0x0d4f: 0x10c9, 0x0d50: 0x10cd, 0x0d51: 0x10d1,
+ 0x0d52: 0x10e5, 0x0d53: 0x10e9, 0x0d54: 0x110d, 0x0d55: 0x1121, 0x0d56: 0x113d, 0x0d57: 0x11a1,
+ 0x0d58: 0x11a9, 0x0d59: 0x11b1, 0x0d5a: 0x11c5, 0x0d5b: 0x11ed, 0x0d5c: 0x123d, 0x0d5d: 0x1271,
+ 0x0d5e: 0x1271, 0x0d5f: 0x12d9, 0x0d60: 0x1381, 0x0d61: 0x1399, 0x0d62: 0x13cd, 0x0d63: 0x13d1,
+ 0x0d64: 0x1415, 0x0d65: 0x1419, 0x0d66: 0x1471, 0x0d67: 0x1479, 0x0d68: 0x1549, 0x0d69: 0x158d,
+ 0x0d6a: 0x15a5, 0x0d6b: 0x0c0d, 0x0d6c: 0x1780, 0x0d6d: 0x1255,
+ 0x0d70: 0x0751, 0x0d71: 0x0855, 0x0d72: 0x0815, 0x0d73: 0x07bd, 0x0d74: 0x07fd, 0x0d75: 0x0829,
+ 0x0d76: 0x08b9, 0x0d77: 0x08d5, 0x0d78: 0x09bd, 0x0d79: 0x09a9, 0x0d7a: 0x09b9, 0x0d7b: 0x09d5,
+ 0x0d7c: 0x0a21, 0x0d7d: 0x0a31, 0x0d7e: 0x0a75, 0x0d7f: 0x0a81,
+ // Block 0x36, offset 0xd80
+ 0x0d80: 0x0a9d, 0x0d81: 0x0aad, 0x0d82: 0x0b95, 0x0d83: 0x0b9d, 0x0d84: 0x0bcd, 0x0d85: 0x0bed,
+ 0x0d86: 0x0c1d, 0x0d87: 0x0c35, 0x0d88: 0x0c25, 0x0d89: 0x0c45, 0x0d8a: 0x0c39, 0x0d8b: 0x0c5d,
+ 0x0d8c: 0x0c79, 0x0d8d: 0x0cd1, 0x0d8e: 0x0cdd, 0x0d8f: 0x0ce5, 0x0d90: 0x0d0d, 0x0d91: 0x0d51,
+ 0x0d92: 0x0d81, 0x0d93: 0x0d85, 0x0d94: 0x0d99, 0x0d95: 0x0e19, 0x0d96: 0x0e29, 0x0d97: 0x0e81,
+ 0x0d98: 0x0ecd, 0x0d99: 0x0ec5, 0x0d9a: 0x0ed9, 0x0d9b: 0x0ef5, 0x0d9c: 0x0f2d, 0x0d9d: 0x1085,
+ 0x0d9e: 0x0f51, 0x0d9f: 0x0f85, 0x0da0: 0x0f91, 0x0da1: 0x0fd1, 0x0da2: 0x0fed, 0x0da3: 0x1011,
+ 0x0da4: 0x1035, 0x0da5: 0x1039, 0x0da6: 0x1055, 0x0da7: 0x1059, 0x0da8: 0x1069, 0x0da9: 0x107d,
+ 0x0daa: 0x1079, 0x0dab: 0x10a9, 0x0dac: 0x1125, 0x0dad: 0x113d, 0x0dae: 0x1155, 0x0daf: 0x118d,
+ 0x0db0: 0x11a1, 0x0db1: 0x11bd, 0x0db2: 0x11ed, 0x0db3: 0x12a1, 0x0db4: 0x12c9, 0x0db5: 0x133d,
+ 0x0db6: 0x1385, 0x0db7: 0x1391, 0x0db8: 0x1399, 0x0db9: 0x13b1, 0x0dba: 0x13c5, 0x0dbb: 0x13b5,
+ 0x0dbc: 0x13cd, 0x0dbd: 0x13c9, 0x0dbe: 0x13c1, 0x0dbf: 0x13d1,
+ // Block 0x37, offset 0xdc0
+ 0x0dc0: 0x13dd, 0x0dc1: 0x1419, 0x0dc2: 0x1455, 0x0dc3: 0x1485, 0x0dc4: 0x14b9, 0x0dc5: 0x14d9,
+ 0x0dc6: 0x1525, 0x0dc7: 0x1549, 0x0dc8: 0x1569, 0x0dc9: 0x157d, 0x0dca: 0x158d, 0x0dcb: 0x1599,
+ 0x0dcc: 0x15a5, 0x0dcd: 0x15f9, 0x0dce: 0x1699, 0x0dcf: 0x1717, 0x0dd0: 0x1712, 0x0dd1: 0x1744,
+ 0x0dd2: 0x0679, 0x0dd3: 0x06a1, 0x0dd4: 0x06a5, 0x0dd5: 0x17c6, 0x0dd6: 0x17f3, 0x0dd7: 0x186b,
+ 0x0dd8: 0x1685, 0x0dd9: 0x1695,
+ // Block 0x38, offset 0xe00
+ 0x0e00: 0x1b17, 0x0e01: 0x1b1a, 0x0e02: 0x1b1d, 0x0e03: 0x1d4a, 0x0e04: 0x1d4e, 0x0e05: 0x1ba1,
+ 0x0e06: 0x1ba1,
+ 0x0e13: 0x1eb7, 0x0e14: 0x1ea8, 0x0e15: 0x1ead, 0x0e16: 0x1ebc, 0x0e17: 0x1eb2,
+ 0x0e1d: 0x4376,
+ 0x0e1e: 0x801a, 0x0e1f: 0x43e8, 0x0e20: 0x0227, 0x0e21: 0x020f, 0x0e22: 0x0218, 0x0e23: 0x021b,
+ 0x0e24: 0x021e, 0x0e25: 0x0221, 0x0e26: 0x0224, 0x0e27: 0x022a, 0x0e28: 0x022d, 0x0e29: 0x0017,
+ 0x0e2a: 0x43d6, 0x0e2b: 0x43dc, 0x0e2c: 0x44da, 0x0e2d: 0x44e2, 0x0e2e: 0x432e, 0x0e2f: 0x4334,
+ 0x0e30: 0x433a, 0x0e31: 0x4340, 0x0e32: 0x434c, 0x0e33: 0x4352, 0x0e34: 0x4358, 0x0e35: 0x4364,
+ 0x0e36: 0x436a, 0x0e38: 0x4370, 0x0e39: 0x437c, 0x0e3a: 0x4382, 0x0e3b: 0x4388,
+ 0x0e3c: 0x4394, 0x0e3e: 0x439a,
+ // Block 0x39, offset 0xe40
+ 0x0e40: 0x43a0, 0x0e41: 0x43a6, 0x0e43: 0x43ac, 0x0e44: 0x43b2,
+ 0x0e46: 0x43be, 0x0e47: 0x43c4, 0x0e48: 0x43ca, 0x0e49: 0x43d0, 0x0e4a: 0x43e2, 0x0e4b: 0x435e,
+ 0x0e4c: 0x4346, 0x0e4d: 0x438e, 0x0e4e: 0x43b8, 0x0e4f: 0x1ec1, 0x0e50: 0x0293, 0x0e51: 0x0293,
+ 0x0e52: 0x029c, 0x0e53: 0x029c, 0x0e54: 0x029c, 0x0e55: 0x029c, 0x0e56: 0x029f, 0x0e57: 0x029f,
+ 0x0e58: 0x029f, 0x0e59: 0x029f, 0x0e5a: 0x02a5, 0x0e5b: 0x02a5, 0x0e5c: 0x02a5, 0x0e5d: 0x02a5,
+ 0x0e5e: 0x0299, 0x0e5f: 0x0299, 0x0e60: 0x0299, 0x0e61: 0x0299, 0x0e62: 0x02a2, 0x0e63: 0x02a2,
+ 0x0e64: 0x02a2, 0x0e65: 0x02a2, 0x0e66: 0x0296, 0x0e67: 0x0296, 0x0e68: 0x0296, 0x0e69: 0x0296,
+ 0x0e6a: 0x02c9, 0x0e6b: 0x02c9, 0x0e6c: 0x02c9, 0x0e6d: 0x02c9, 0x0e6e: 0x02cc, 0x0e6f: 0x02cc,
+ 0x0e70: 0x02cc, 0x0e71: 0x02cc, 0x0e72: 0x02ab, 0x0e73: 0x02ab, 0x0e74: 0x02ab, 0x0e75: 0x02ab,
+ 0x0e76: 0x02a8, 0x0e77: 0x02a8, 0x0e78: 0x02a8, 0x0e79: 0x02a8, 0x0e7a: 0x02ae, 0x0e7b: 0x02ae,
+ 0x0e7c: 0x02ae, 0x0e7d: 0x02ae, 0x0e7e: 0x02b1, 0x0e7f: 0x02b1,
+ // Block 0x3a, offset 0xe80
+ 0x0e80: 0x02b1, 0x0e81: 0x02b1, 0x0e82: 0x02ba, 0x0e83: 0x02ba, 0x0e84: 0x02b7, 0x0e85: 0x02b7,
+ 0x0e86: 0x02bd, 0x0e87: 0x02bd, 0x0e88: 0x02b4, 0x0e89: 0x02b4, 0x0e8a: 0x02c3, 0x0e8b: 0x02c3,
+ 0x0e8c: 0x02c0, 0x0e8d: 0x02c0, 0x0e8e: 0x02cf, 0x0e8f: 0x02cf, 0x0e90: 0x02cf, 0x0e91: 0x02cf,
+ 0x0e92: 0x02d5, 0x0e93: 0x02d5, 0x0e94: 0x02d5, 0x0e95: 0x02d5, 0x0e96: 0x02db, 0x0e97: 0x02db,
+ 0x0e98: 0x02db, 0x0e99: 0x02db, 0x0e9a: 0x02d8, 0x0e9b: 0x02d8, 0x0e9c: 0x02d8, 0x0e9d: 0x02d8,
+ 0x0e9e: 0x02de, 0x0e9f: 0x02de, 0x0ea0: 0x02e1, 0x0ea1: 0x02e1, 0x0ea2: 0x02e1, 0x0ea3: 0x02e1,
+ 0x0ea4: 0x4454, 0x0ea5: 0x4454, 0x0ea6: 0x02e7, 0x0ea7: 0x02e7, 0x0ea8: 0x02e7, 0x0ea9: 0x02e7,
+ 0x0eaa: 0x02e4, 0x0eab: 0x02e4, 0x0eac: 0x02e4, 0x0ead: 0x02e4, 0x0eae: 0x0302, 0x0eaf: 0x0302,
+ 0x0eb0: 0x444e, 0x0eb1: 0x444e,
+ // Block 0x3b, offset 0xec0
+ 0x0ed3: 0x02d2, 0x0ed4: 0x02d2, 0x0ed5: 0x02d2, 0x0ed6: 0x02d2, 0x0ed7: 0x02f0,
+ 0x0ed8: 0x02f0, 0x0ed9: 0x02ed, 0x0eda: 0x02ed, 0x0edb: 0x02f3, 0x0edc: 0x02f3, 0x0edd: 0x2191,
+ 0x0ede: 0x02f9, 0x0edf: 0x02f9, 0x0ee0: 0x02ea, 0x0ee1: 0x02ea, 0x0ee2: 0x02f6, 0x0ee3: 0x02f6,
+ 0x0ee4: 0x02ff, 0x0ee5: 0x02ff, 0x0ee6: 0x02ff, 0x0ee7: 0x02ff, 0x0ee8: 0x0287, 0x0ee9: 0x0287,
+ 0x0eea: 0x26ec, 0x0eeb: 0x26ec, 0x0eec: 0x275c, 0x0eed: 0x275c, 0x0eee: 0x272b, 0x0eef: 0x272b,
+ 0x0ef0: 0x2747, 0x0ef1: 0x2747, 0x0ef2: 0x2740, 0x0ef3: 0x2740, 0x0ef4: 0x274e, 0x0ef5: 0x274e,
+ 0x0ef6: 0x2755, 0x0ef7: 0x2755, 0x0ef8: 0x2755, 0x0ef9: 0x2732, 0x0efa: 0x2732, 0x0efb: 0x2732,
+ 0x0efc: 0x02fc, 0x0efd: 0x02fc, 0x0efe: 0x02fc, 0x0eff: 0x02fc,
+ // Block 0x3c, offset 0xf00
+ 0x0f00: 0x26f3, 0x0f01: 0x26fa, 0x0f02: 0x2716, 0x0f03: 0x2732, 0x0f04: 0x2739, 0x0f05: 0x1ecb,
+ 0x0f06: 0x1ed0, 0x0f07: 0x1ed5, 0x0f08: 0x1ee4, 0x0f09: 0x1ef3, 0x0f0a: 0x1ef8, 0x0f0b: 0x1efd,
+ 0x0f0c: 0x1f02, 0x0f0d: 0x1f07, 0x0f0e: 0x1f16, 0x0f0f: 0x1f25, 0x0f10: 0x1f2a, 0x0f11: 0x1f2f,
+ 0x0f12: 0x1f3e, 0x0f13: 0x1f4d, 0x0f14: 0x1f52, 0x0f15: 0x1f57, 0x0f16: 0x1f5c, 0x0f17: 0x1f6b,
+ 0x0f18: 0x1f70, 0x0f19: 0x1f7f, 0x0f1a: 0x1f84, 0x0f1b: 0x1f89, 0x0f1c: 0x1f98, 0x0f1d: 0x1f9d,
+ 0x0f1e: 0x1fa2, 0x0f1f: 0x1fac, 0x0f20: 0x1fe8, 0x0f21: 0x1ff7, 0x0f22: 0x2006, 0x0f23: 0x200b,
+ 0x0f24: 0x2010, 0x0f25: 0x201a, 0x0f26: 0x2029, 0x0f27: 0x202e, 0x0f28: 0x203d, 0x0f29: 0x2042,
+ 0x0f2a: 0x2047, 0x0f2b: 0x2056, 0x0f2c: 0x205b, 0x0f2d: 0x206a, 0x0f2e: 0x206f, 0x0f2f: 0x2074,
+ 0x0f30: 0x2079, 0x0f31: 0x207e, 0x0f32: 0x2083, 0x0f33: 0x2088, 0x0f34: 0x208d, 0x0f35: 0x2092,
+ 0x0f36: 0x2097, 0x0f37: 0x209c, 0x0f38: 0x20a1, 0x0f39: 0x20a6, 0x0f3a: 0x20ab, 0x0f3b: 0x20b0,
+ 0x0f3c: 0x20b5, 0x0f3d: 0x20ba, 0x0f3e: 0x20bf, 0x0f3f: 0x20c9,
+ // Block 0x3d, offset 0xf40
+ 0x0f40: 0x20ce, 0x0f41: 0x20d3, 0x0f42: 0x20d8, 0x0f43: 0x20e2, 0x0f44: 0x20e7, 0x0f45: 0x20f1,
+ 0x0f46: 0x20f6, 0x0f47: 0x20fb, 0x0f48: 0x2100, 0x0f49: 0x2105, 0x0f4a: 0x210a, 0x0f4b: 0x210f,
+ 0x0f4c: 0x2114, 0x0f4d: 0x2119, 0x0f4e: 0x2128, 0x0f4f: 0x2137, 0x0f50: 0x213c, 0x0f51: 0x2141,
+ 0x0f52: 0x2146, 0x0f53: 0x214b, 0x0f54: 0x2150, 0x0f55: 0x215a, 0x0f56: 0x215f, 0x0f57: 0x2164,
+ 0x0f58: 0x2173, 0x0f59: 0x2182, 0x0f5a: 0x2187, 0x0f5b: 0x4406, 0x0f5c: 0x440c, 0x0f5d: 0x4442,
+ 0x0f5e: 0x4499, 0x0f5f: 0x44a0, 0x0f60: 0x44a7, 0x0f61: 0x44ae, 0x0f62: 0x44b5, 0x0f63: 0x44bc,
+ 0x0f64: 0x2708, 0x0f65: 0x270f, 0x0f66: 0x2716, 0x0f67: 0x271d, 0x0f68: 0x2732, 0x0f69: 0x2739,
+ 0x0f6a: 0x1eda, 0x0f6b: 0x1edf, 0x0f6c: 0x1ee4, 0x0f6d: 0x1ee9, 0x0f6e: 0x1ef3, 0x0f6f: 0x1ef8,
+ 0x0f70: 0x1f0c, 0x0f71: 0x1f11, 0x0f72: 0x1f16, 0x0f73: 0x1f1b, 0x0f74: 0x1f25, 0x0f75: 0x1f2a,
+ 0x0f76: 0x1f34, 0x0f77: 0x1f39, 0x0f78: 0x1f3e, 0x0f79: 0x1f43, 0x0f7a: 0x1f4d, 0x0f7b: 0x1f52,
+ 0x0f7c: 0x207e, 0x0f7d: 0x2083, 0x0f7e: 0x2092, 0x0f7f: 0x2097,
+ // Block 0x3e, offset 0xf80
+ 0x0f80: 0x209c, 0x0f81: 0x20b0, 0x0f82: 0x20b5, 0x0f83: 0x20ba, 0x0f84: 0x20bf, 0x0f85: 0x20d8,
+ 0x0f86: 0x20e2, 0x0f87: 0x20e7, 0x0f88: 0x20ec, 0x0f89: 0x2100, 0x0f8a: 0x211e, 0x0f8b: 0x2123,
+ 0x0f8c: 0x2128, 0x0f8d: 0x212d, 0x0f8e: 0x2137, 0x0f8f: 0x213c, 0x0f90: 0x4442, 0x0f91: 0x2169,
+ 0x0f92: 0x216e, 0x0f93: 0x2173, 0x0f94: 0x2178, 0x0f95: 0x2182, 0x0f96: 0x2187, 0x0f97: 0x26f3,
+ 0x0f98: 0x26fa, 0x0f99: 0x2701, 0x0f9a: 0x2716, 0x0f9b: 0x2724, 0x0f9c: 0x1ecb, 0x0f9d: 0x1ed0,
+ 0x0f9e: 0x1ed5, 0x0f9f: 0x1ee4, 0x0fa0: 0x1eee, 0x0fa1: 0x1efd, 0x0fa2: 0x1f02, 0x0fa3: 0x1f07,
+ 0x0fa4: 0x1f16, 0x0fa5: 0x1f20, 0x0fa6: 0x1f3e, 0x0fa7: 0x1f57, 0x0fa8: 0x1f5c, 0x0fa9: 0x1f6b,
+ 0x0faa: 0x1f70, 0x0fab: 0x1f7f, 0x0fac: 0x1f89, 0x0fad: 0x1f98, 0x0fae: 0x1f9d, 0x0faf: 0x1fa2,
+ 0x0fb0: 0x1fac, 0x0fb1: 0x1fe8, 0x0fb2: 0x1fed, 0x0fb3: 0x1ff7, 0x0fb4: 0x2006, 0x0fb5: 0x200b,
+ 0x0fb6: 0x2010, 0x0fb7: 0x201a, 0x0fb8: 0x2029, 0x0fb9: 0x203d, 0x0fba: 0x2042, 0x0fbb: 0x2047,
+ 0x0fbc: 0x2056, 0x0fbd: 0x205b, 0x0fbe: 0x206a, 0x0fbf: 0x206f,
+ // Block 0x3f, offset 0xfc0
+ 0x0fc0: 0x2074, 0x0fc1: 0x2079, 0x0fc2: 0x2088, 0x0fc3: 0x208d, 0x0fc4: 0x20a1, 0x0fc5: 0x20a6,
+ 0x0fc6: 0x20ab, 0x0fc7: 0x20b0, 0x0fc8: 0x20b5, 0x0fc9: 0x20c9, 0x0fca: 0x20ce, 0x0fcb: 0x20d3,
+ 0x0fcc: 0x20d8, 0x0fcd: 0x20dd, 0x0fce: 0x20f1, 0x0fcf: 0x20f6, 0x0fd0: 0x20fb, 0x0fd1: 0x2100,
+ 0x0fd2: 0x210f, 0x0fd3: 0x2114, 0x0fd4: 0x2119, 0x0fd5: 0x2128, 0x0fd6: 0x2132, 0x0fd7: 0x2141,
+ 0x0fd8: 0x2146, 0x0fd9: 0x4436, 0x0fda: 0x215a, 0x0fdb: 0x215f, 0x0fdc: 0x2164, 0x0fdd: 0x2173,
+ 0x0fde: 0x217d, 0x0fdf: 0x2716, 0x0fe0: 0x2724, 0x0fe1: 0x1ee4, 0x0fe2: 0x1eee, 0x0fe3: 0x1f16,
+ 0x0fe4: 0x1f20, 0x0fe5: 0x1f3e, 0x0fe6: 0x1f48, 0x0fe7: 0x1fac, 0x0fe8: 0x1fb1, 0x0fe9: 0x1fd4,
+ 0x0fea: 0x1fd9, 0x0feb: 0x20b0, 0x0fec: 0x20b5, 0x0fed: 0x20d8, 0x0fee: 0x2128, 0x0fef: 0x2132,
+ 0x0ff0: 0x2173, 0x0ff1: 0x217d, 0x0ff2: 0x44ea, 0x0ff3: 0x44f2, 0x0ff4: 0x44fa, 0x0ff5: 0x2033,
+ 0x0ff6: 0x2038, 0x0ff7: 0x204c, 0x0ff8: 0x2051, 0x0ff9: 0x2060, 0x0ffa: 0x2065, 0x0ffb: 0x1fb6,
+ 0x0ffc: 0x1fbb, 0x0ffd: 0x1fde, 0x0ffe: 0x1fe3, 0x0fff: 0x1f75,
+ // Block 0x40, offset 0x1000
+ 0x1000: 0x1f7a, 0x1001: 0x1f61, 0x1002: 0x1f66, 0x1003: 0x1f8e, 0x1004: 0x1f93, 0x1005: 0x1ffc,
+ 0x1006: 0x2001, 0x1007: 0x201f, 0x1008: 0x2024, 0x1009: 0x1fc0, 0x100a: 0x1fc5, 0x100b: 0x1fca,
+ 0x100c: 0x1fd4, 0x100d: 0x1fcf, 0x100e: 0x1fa7, 0x100f: 0x1ff2, 0x1010: 0x2015, 0x1011: 0x2033,
+ 0x1012: 0x2038, 0x1013: 0x204c, 0x1014: 0x2051, 0x1015: 0x2060, 0x1016: 0x2065, 0x1017: 0x1fb6,
+ 0x1018: 0x1fbb, 0x1019: 0x1fde, 0x101a: 0x1fe3, 0x101b: 0x1f75, 0x101c: 0x1f7a, 0x101d: 0x1f61,
+ 0x101e: 0x1f66, 0x101f: 0x1f8e, 0x1020: 0x1f93, 0x1021: 0x1ffc, 0x1022: 0x2001, 0x1023: 0x201f,
+ 0x1024: 0x2024, 0x1025: 0x1fc0, 0x1026: 0x1fc5, 0x1027: 0x1fca, 0x1028: 0x1fd4, 0x1029: 0x1fcf,
+ 0x102a: 0x1fa7, 0x102b: 0x1ff2, 0x102c: 0x2015, 0x102d: 0x1fc0, 0x102e: 0x1fc5, 0x102f: 0x1fca,
+ 0x1030: 0x1fd4, 0x1031: 0x1fb1, 0x1032: 0x1fd9, 0x1033: 0x202e, 0x1034: 0x1f98, 0x1035: 0x1f9d,
+ 0x1036: 0x1fa2, 0x1037: 0x1fc0, 0x1038: 0x1fc5, 0x1039: 0x1fca, 0x103a: 0x202e, 0x103b: 0x203d,
+ 0x103c: 0x43ee, 0x103d: 0x43ee,
+ // Block 0x41, offset 0x1040
+ 0x1050: 0x2453, 0x1051: 0x2468,
+ 0x1052: 0x2468, 0x1053: 0x246f, 0x1054: 0x2476, 0x1055: 0x248b, 0x1056: 0x2492, 0x1057: 0x2499,
+ 0x1058: 0x24bc, 0x1059: 0x24bc, 0x105a: 0x24df, 0x105b: 0x24d8, 0x105c: 0x24f4, 0x105d: 0x24e6,
+ 0x105e: 0x24ed, 0x105f: 0x2510, 0x1060: 0x2510, 0x1061: 0x2509, 0x1062: 0x2517, 0x1063: 0x2517,
+ 0x1064: 0x2541, 0x1065: 0x2541, 0x1066: 0x255d, 0x1067: 0x2525, 0x1068: 0x2525, 0x1069: 0x251e,
+ 0x106a: 0x2533, 0x106b: 0x2533, 0x106c: 0x253a, 0x106d: 0x253a, 0x106e: 0x2564, 0x106f: 0x2572,
+ 0x1070: 0x2572, 0x1071: 0x2579, 0x1072: 0x2579, 0x1073: 0x2580, 0x1074: 0x2587, 0x1075: 0x258e,
+ 0x1076: 0x2595, 0x1077: 0x2595, 0x1078: 0x259c, 0x1079: 0x25aa, 0x107a: 0x25b8, 0x107b: 0x25b1,
+ 0x107c: 0x25bf, 0x107d: 0x25bf, 0x107e: 0x25d4, 0x107f: 0x25db,
+ // Block 0x42, offset 0x1080
+ 0x1080: 0x260c, 0x1081: 0x261a, 0x1082: 0x2613, 0x1083: 0x25f7, 0x1084: 0x25f7, 0x1085: 0x2621,
+ 0x1086: 0x2621, 0x1087: 0x2628, 0x1088: 0x2628, 0x1089: 0x2652, 0x108a: 0x2659, 0x108b: 0x2660,
+ 0x108c: 0x2636, 0x108d: 0x2644, 0x108e: 0x2667, 0x108f: 0x266e,
+ 0x1092: 0x263d, 0x1093: 0x26c2, 0x1094: 0x26c9, 0x1095: 0x269f, 0x1096: 0x26a6, 0x1097: 0x268a,
+ 0x1098: 0x268a, 0x1099: 0x2691, 0x109a: 0x26bb, 0x109b: 0x26b4, 0x109c: 0x26de, 0x109d: 0x26de,
+ 0x109e: 0x244c, 0x109f: 0x2461, 0x10a0: 0x245a, 0x10a1: 0x2484, 0x10a2: 0x247d, 0x10a3: 0x24a7,
+ 0x10a4: 0x24a0, 0x10a5: 0x24ca, 0x10a6: 0x24ae, 0x10a7: 0x24c3, 0x10a8: 0x24fb, 0x10a9: 0x2548,
+ 0x10aa: 0x252c, 0x10ab: 0x256b, 0x10ac: 0x2605, 0x10ad: 0x262f, 0x10ae: 0x26d7, 0x10af: 0x26d0,
+ 0x10b0: 0x26e5, 0x10b1: 0x267c, 0x10b2: 0x25e2, 0x10b3: 0x26ad, 0x10b4: 0x25d4, 0x10b5: 0x260c,
+ 0x10b6: 0x25a3, 0x10b7: 0x25f0, 0x10b8: 0x2683, 0x10b9: 0x2675, 0x10ba: 0x25fe, 0x10bb: 0x25e9,
+ 0x10bc: 0x25fe, 0x10bd: 0x2683, 0x10be: 0x24b5, 0x10bf: 0x24d1,
+ // Block 0x43, offset 0x10c0
+ 0x10c0: 0x264b, 0x10c1: 0x25c6, 0x10c2: 0x2445, 0x10c3: 0x25e9, 0x10c4: 0x258e, 0x10c5: 0x255d,
+ 0x10c6: 0x2502, 0x10c7: 0x2698,
+ 0x10f0: 0x2556, 0x10f1: 0x25cd, 0x10f2: 0x296a, 0x10f3: 0x2961, 0x10f4: 0x2997, 0x10f5: 0x2985,
+ 0x10f6: 0x2973, 0x10f7: 0x298e, 0x10f8: 0x29a0, 0x10f9: 0x254f, 0x10fa: 0x2e44, 0x10fb: 0x2cb4,
+ 0x10fc: 0x297c,
+ // Block 0x44, offset 0x1100
+ 0x1110: 0x0019, 0x1111: 0x04f5,
+ 0x1112: 0x04f9, 0x1113: 0x0035, 0x1114: 0x0037, 0x1115: 0x0003, 0x1116: 0x003f, 0x1117: 0x0531,
+ 0x1118: 0x0535, 0x1119: 0x1c9e,
+ 0x1120: 0x80e6, 0x1121: 0x80e6, 0x1122: 0x80e6, 0x1123: 0x80e6,
+ 0x1124: 0x80e6, 0x1125: 0x80e6, 0x1126: 0x80e6,
+ 0x1130: 0x19b5, 0x1131: 0x04b5, 0x1132: 0x04b1, 0x1133: 0x007f, 0x1134: 0x007f, 0x1135: 0x0011,
+ 0x1136: 0x0013, 0x1137: 0x00b7, 0x1138: 0x00bb, 0x1139: 0x0529, 0x113a: 0x052d, 0x113b: 0x051d,
+ 0x113c: 0x0521, 0x113d: 0x0505, 0x113e: 0x0509, 0x113f: 0x04fd,
+ // Block 0x45, offset 0x1140
+ 0x1140: 0x0501, 0x1141: 0x050d, 0x1142: 0x0511, 0x1143: 0x0515, 0x1144: 0x0519,
+ 0x1147: 0x0077, 0x1148: 0x007b, 0x1149: 0x424f, 0x114a: 0x424f, 0x114b: 0x424f,
+ 0x114c: 0x424f, 0x114d: 0x007f, 0x114e: 0x007f, 0x114f: 0x007f, 0x1150: 0x0019, 0x1151: 0x04f5,
+ 0x1152: 0x001d, 0x1154: 0x0037, 0x1155: 0x0035, 0x1156: 0x003f, 0x1157: 0x0003,
+ 0x1158: 0x04b5, 0x1159: 0x0011, 0x115a: 0x0013, 0x115b: 0x00b7, 0x115c: 0x00bb, 0x115d: 0x0529,
+ 0x115e: 0x052d, 0x115f: 0x0007, 0x1160: 0x000d, 0x1161: 0x0015, 0x1162: 0x0017, 0x1163: 0x001b,
+ 0x1164: 0x0039, 0x1165: 0x003d, 0x1166: 0x003b, 0x1168: 0x0079, 0x1169: 0x0009,
+ 0x116a: 0x000b, 0x116b: 0x0041,
+ 0x1170: 0x4290, 0x1171: 0x4412, 0x1172: 0x4295, 0x1174: 0x429a,
+ 0x1176: 0x429f, 0x1177: 0x4418, 0x1178: 0x42a4, 0x1179: 0x441e, 0x117a: 0x42a9, 0x117b: 0x4424,
+ 0x117c: 0x42ae, 0x117d: 0x442a, 0x117e: 0x42b3, 0x117f: 0x4430,
+ // Block 0x46, offset 0x1180
+ 0x1180: 0x0230, 0x1181: 0x43f4, 0x1182: 0x43f4, 0x1183: 0x43fa, 0x1184: 0x43fa, 0x1185: 0x443c,
+ 0x1186: 0x443c, 0x1187: 0x4400, 0x1188: 0x4400, 0x1189: 0x4448, 0x118a: 0x4448, 0x118b: 0x4448,
+ 0x118c: 0x4448, 0x118d: 0x0233, 0x118e: 0x0233, 0x118f: 0x0236, 0x1190: 0x0236, 0x1191: 0x0236,
+ 0x1192: 0x0236, 0x1193: 0x0239, 0x1194: 0x0239, 0x1195: 0x023c, 0x1196: 0x023c, 0x1197: 0x023c,
+ 0x1198: 0x023c, 0x1199: 0x023f, 0x119a: 0x023f, 0x119b: 0x023f, 0x119c: 0x023f, 0x119d: 0x0242,
+ 0x119e: 0x0242, 0x119f: 0x0242, 0x11a0: 0x0242, 0x11a1: 0x0245, 0x11a2: 0x0245, 0x11a3: 0x0245,
+ 0x11a4: 0x0245, 0x11a5: 0x0248, 0x11a6: 0x0248, 0x11a7: 0x0248, 0x11a8: 0x0248, 0x11a9: 0x024b,
+ 0x11aa: 0x024b, 0x11ab: 0x024e, 0x11ac: 0x024e, 0x11ad: 0x0251, 0x11ae: 0x0251, 0x11af: 0x0254,
+ 0x11b0: 0x0254, 0x11b1: 0x0257, 0x11b2: 0x0257, 0x11b3: 0x0257, 0x11b4: 0x0257, 0x11b5: 0x025a,
+ 0x11b6: 0x025a, 0x11b7: 0x025a, 0x11b8: 0x025a, 0x11b9: 0x025d, 0x11ba: 0x025d, 0x11bb: 0x025d,
+ 0x11bc: 0x025d, 0x11bd: 0x0260, 0x11be: 0x0260, 0x11bf: 0x0260,
+ // Block 0x47, offset 0x11c0
+ 0x11c0: 0x0260, 0x11c1: 0x0263, 0x11c2: 0x0263, 0x11c3: 0x0263, 0x11c4: 0x0263, 0x11c5: 0x0266,
+ 0x11c6: 0x0266, 0x11c7: 0x0266, 0x11c8: 0x0266, 0x11c9: 0x0269, 0x11ca: 0x0269, 0x11cb: 0x0269,
+ 0x11cc: 0x0269, 0x11cd: 0x026c, 0x11ce: 0x026c, 0x11cf: 0x026c, 0x11d0: 0x026c, 0x11d1: 0x026f,
+ 0x11d2: 0x026f, 0x11d3: 0x026f, 0x11d4: 0x026f, 0x11d5: 0x0272, 0x11d6: 0x0272, 0x11d7: 0x0272,
+ 0x11d8: 0x0272, 0x11d9: 0x0275, 0x11da: 0x0275, 0x11db: 0x0275, 0x11dc: 0x0275, 0x11dd: 0x0278,
+ 0x11de: 0x0278, 0x11df: 0x0278, 0x11e0: 0x0278, 0x11e1: 0x027b, 0x11e2: 0x027b, 0x11e3: 0x027b,
+ 0x11e4: 0x027b, 0x11e5: 0x027e, 0x11e6: 0x027e, 0x11e7: 0x027e, 0x11e8: 0x027e, 0x11e9: 0x0281,
+ 0x11ea: 0x0281, 0x11eb: 0x0281, 0x11ec: 0x0281, 0x11ed: 0x0284, 0x11ee: 0x0284, 0x11ef: 0x0287,
+ 0x11f0: 0x0287, 0x11f1: 0x028a, 0x11f2: 0x028a, 0x11f3: 0x028a, 0x11f4: 0x028a, 0x11f5: 0x2e91,
+ 0x11f6: 0x2e91, 0x11f7: 0x2e99, 0x11f8: 0x2e99, 0x11f9: 0x2ea1, 0x11fa: 0x2ea1, 0x11fb: 0x20c4,
+ 0x11fc: 0x20c4,
+ // Block 0x48, offset 0x1200
+ 0x1200: 0x0081, 0x1201: 0x0083, 0x1202: 0x0085, 0x1203: 0x0087, 0x1204: 0x0089, 0x1205: 0x008b,
+ 0x1206: 0x008d, 0x1207: 0x008f, 0x1208: 0x0091, 0x1209: 0x0093, 0x120a: 0x0095, 0x120b: 0x0097,
+ 0x120c: 0x0099, 0x120d: 0x009b, 0x120e: 0x009d, 0x120f: 0x009f, 0x1210: 0x00a1, 0x1211: 0x00a3,
+ 0x1212: 0x00a5, 0x1213: 0x00a7, 0x1214: 0x00a9, 0x1215: 0x00ab, 0x1216: 0x00ad, 0x1217: 0x00af,
+ 0x1218: 0x00b1, 0x1219: 0x00b3, 0x121a: 0x00b5, 0x121b: 0x00b7, 0x121c: 0x00b9, 0x121d: 0x00bb,
+ 0x121e: 0x00bd, 0x121f: 0x04e9, 0x1220: 0x04ed, 0x1221: 0x04f9, 0x1222: 0x050d, 0x1223: 0x0511,
+ 0x1224: 0x04f5, 0x1225: 0x061d, 0x1226: 0x0615, 0x1227: 0x0539, 0x1228: 0x0541, 0x1229: 0x0549,
+ 0x122a: 0x0551, 0x122b: 0x0559, 0x122c: 0x05dd, 0x122d: 0x05e5, 0x122e: 0x05ed, 0x122f: 0x0591,
+ 0x1230: 0x0621, 0x1231: 0x053d, 0x1232: 0x0545, 0x1233: 0x054d, 0x1234: 0x0555, 0x1235: 0x055d,
+ 0x1236: 0x0561, 0x1237: 0x0565, 0x1238: 0x0569, 0x1239: 0x056d, 0x123a: 0x0571, 0x123b: 0x0575,
+ 0x123c: 0x0579, 0x123d: 0x057d, 0x123e: 0x0581, 0x123f: 0x0585,
+ // Block 0x49, offset 0x1240
+ 0x1240: 0x0589, 0x1241: 0x058d, 0x1242: 0x0595, 0x1243: 0x0599, 0x1244: 0x059d, 0x1245: 0x05a1,
+ 0x1246: 0x05a5, 0x1247: 0x05a9, 0x1248: 0x05ad, 0x1249: 0x05b1, 0x124a: 0x05b5, 0x124b: 0x05b9,
+ 0x124c: 0x05bd, 0x124d: 0x05c1, 0x124e: 0x05c5, 0x124f: 0x05c9, 0x1250: 0x05cd, 0x1251: 0x05d1,
+ 0x1252: 0x05d5, 0x1253: 0x05d9, 0x1254: 0x05e1, 0x1255: 0x05e9, 0x1256: 0x05f1, 0x1257: 0x05f5,
+ 0x1258: 0x05f9, 0x1259: 0x05fd, 0x125a: 0x0601, 0x125b: 0x0605, 0x125c: 0x0609, 0x125d: 0x0619,
+ 0x125e: 0x49aa, 0x125f: 0x49b0, 0x1260: 0x03bd, 0x1261: 0x030d, 0x1262: 0x0311, 0x1263: 0x0435,
+ 0x1264: 0x0315, 0x1265: 0x0439, 0x1266: 0x043d, 0x1267: 0x0319, 0x1268: 0x031d, 0x1269: 0x0321,
+ 0x126a: 0x0441, 0x126b: 0x0445, 0x126c: 0x0449, 0x126d: 0x044d, 0x126e: 0x0451, 0x126f: 0x0455,
+ 0x1270: 0x0361, 0x1271: 0x0325, 0x1272: 0x0329, 0x1273: 0x032d, 0x1274: 0x0375, 0x1275: 0x0331,
+ 0x1276: 0x0335, 0x1277: 0x0339, 0x1278: 0x033d, 0x1279: 0x0341, 0x127a: 0x0345, 0x127b: 0x0349,
+ 0x127c: 0x034d, 0x127d: 0x0351, 0x127e: 0x0355,
+ // Block 0x4a, offset 0x1280
+ 0x1280: 0x0063, 0x1281: 0x0065, 0x1282: 0x0067, 0x1283: 0x0069, 0x1284: 0x006b, 0x1285: 0x006d,
+ 0x1286: 0x006f, 0x1287: 0x0071, 0x1288: 0x0073, 0x1289: 0x0075, 0x128a: 0x0083, 0x128b: 0x0085,
+ 0x128c: 0x0087, 0x128d: 0x0089, 0x128e: 0x008b, 0x128f: 0x008d, 0x1290: 0x008f, 0x1291: 0x0091,
+ 0x1292: 0x0093, 0x1293: 0x0095, 0x1294: 0x0097, 0x1295: 0x0099, 0x1296: 0x009b, 0x1297: 0x009d,
+ 0x1298: 0x009f, 0x1299: 0x00a1, 0x129a: 0x00a3, 0x129b: 0x00a5, 0x129c: 0x00a7, 0x129d: 0x00a9,
+ 0x129e: 0x00ab, 0x129f: 0x00ad, 0x12a0: 0x00af, 0x12a1: 0x00b1, 0x12a2: 0x00b3, 0x12a3: 0x00b5,
+ 0x12a4: 0x00e0, 0x12a5: 0x00f5, 0x12a8: 0x0173, 0x12a9: 0x0176,
+ 0x12aa: 0x0179, 0x12ab: 0x017c, 0x12ac: 0x017f, 0x12ad: 0x0182, 0x12ae: 0x0185, 0x12af: 0x0188,
+ 0x12b0: 0x018b, 0x12b1: 0x018e, 0x12b2: 0x0191, 0x12b3: 0x0194, 0x12b4: 0x0197, 0x12b5: 0x019a,
+ 0x12b6: 0x019d, 0x12b7: 0x01a0, 0x12b8: 0x01a3, 0x12b9: 0x0188, 0x12ba: 0x01a6, 0x12bb: 0x01a9,
+ 0x12bc: 0x01ac, 0x12bd: 0x01af, 0x12be: 0x01b2, 0x12bf: 0x01b5,
+ // Block 0x4b, offset 0x12c0
+ 0x12c0: 0x01fd, 0x12c1: 0x0200, 0x12c2: 0x0203, 0x12c3: 0x04cd, 0x12c4: 0x01c7, 0x12c5: 0x01d0,
+ 0x12c6: 0x01d6, 0x12c7: 0x01fa, 0x12c8: 0x01eb, 0x12c9: 0x01e8, 0x12ca: 0x0206, 0x12cb: 0x0209,
+ 0x12ce: 0x0021, 0x12cf: 0x0023, 0x12d0: 0x0025, 0x12d1: 0x0027,
+ 0x12d2: 0x0029, 0x12d3: 0x002b, 0x12d4: 0x002d, 0x12d5: 0x002f, 0x12d6: 0x0031, 0x12d7: 0x0033,
+ 0x12d8: 0x0021, 0x12d9: 0x0023, 0x12da: 0x0025, 0x12db: 0x0027, 0x12dc: 0x0029, 0x12dd: 0x002b,
+ 0x12de: 0x002d, 0x12df: 0x002f, 0x12e0: 0x0031, 0x12e1: 0x0033, 0x12e2: 0x0021, 0x12e3: 0x0023,
+ 0x12e4: 0x0025, 0x12e5: 0x0027, 0x12e6: 0x0029, 0x12e7: 0x002b, 0x12e8: 0x002d, 0x12e9: 0x002f,
+ 0x12ea: 0x0031, 0x12eb: 0x0033, 0x12ec: 0x0021, 0x12ed: 0x0023, 0x12ee: 0x0025, 0x12ef: 0x0027,
+ 0x12f0: 0x0029, 0x12f1: 0x002b, 0x12f2: 0x002d, 0x12f3: 0x002f, 0x12f4: 0x0031, 0x12f5: 0x0033,
+ 0x12f6: 0x0021, 0x12f7: 0x0023, 0x12f8: 0x0025, 0x12f9: 0x0027, 0x12fa: 0x0029, 0x12fb: 0x002b,
+ 0x12fc: 0x002d, 0x12fd: 0x002f, 0x12fe: 0x0031, 0x12ff: 0x0033,
+ // Block 0x4c, offset 0x1300
+ 0x1300: 0x0233, 0x1301: 0x0236, 0x1302: 0x0242, 0x1303: 0x024b, 0x1305: 0x0284,
+ 0x1306: 0x0254, 0x1307: 0x0245, 0x1308: 0x0263, 0x1309: 0x028a, 0x130a: 0x0275, 0x130b: 0x0278,
+ 0x130c: 0x027b, 0x130d: 0x027e, 0x130e: 0x0257, 0x130f: 0x0269, 0x1310: 0x026f, 0x1311: 0x025d,
+ 0x1312: 0x0272, 0x1313: 0x0251, 0x1314: 0x025a, 0x1315: 0x023c, 0x1316: 0x023f, 0x1317: 0x0248,
+ 0x1318: 0x024e, 0x1319: 0x0260, 0x131a: 0x0266, 0x131b: 0x026c, 0x131c: 0x028d, 0x131d: 0x02de,
+ 0x131e: 0x02c6, 0x131f: 0x0290, 0x1321: 0x0236, 0x1322: 0x0242,
+ 0x1324: 0x0281, 0x1327: 0x0245, 0x1329: 0x028a,
+ 0x132a: 0x0275, 0x132b: 0x0278, 0x132c: 0x027b, 0x132d: 0x027e, 0x132e: 0x0257, 0x132f: 0x0269,
+ 0x1330: 0x026f, 0x1331: 0x025d, 0x1332: 0x0272, 0x1334: 0x025a, 0x1335: 0x023c,
+ 0x1336: 0x023f, 0x1337: 0x0248, 0x1339: 0x0260, 0x133b: 0x026c,
+ // Block 0x4d, offset 0x1340
+ 0x1342: 0x0242,
+ 0x1347: 0x0245, 0x1349: 0x028a, 0x134b: 0x0278,
+ 0x134d: 0x027e, 0x134e: 0x0257, 0x134f: 0x0269, 0x1351: 0x025d,
+ 0x1352: 0x0272, 0x1354: 0x025a, 0x1357: 0x0248,
+ 0x1359: 0x0260, 0x135b: 0x026c, 0x135d: 0x02de,
+ 0x135f: 0x0290, 0x1361: 0x0236, 0x1362: 0x0242,
+ 0x1364: 0x0281, 0x1367: 0x0245, 0x1368: 0x0263, 0x1369: 0x028a,
+ 0x136a: 0x0275, 0x136c: 0x027b, 0x136d: 0x027e, 0x136e: 0x0257, 0x136f: 0x0269,
+ 0x1370: 0x026f, 0x1371: 0x025d, 0x1372: 0x0272, 0x1374: 0x025a, 0x1375: 0x023c,
+ 0x1376: 0x023f, 0x1377: 0x0248, 0x1379: 0x0260, 0x137a: 0x0266, 0x137b: 0x026c,
+ 0x137c: 0x028d, 0x137e: 0x02c6,
+ // Block 0x4e, offset 0x1380
+ 0x1380: 0x0233, 0x1381: 0x0236, 0x1382: 0x0242, 0x1383: 0x024b, 0x1384: 0x0281, 0x1385: 0x0284,
+ 0x1386: 0x0254, 0x1387: 0x0245, 0x1388: 0x0263, 0x1389: 0x028a, 0x138b: 0x0278,
+ 0x138c: 0x027b, 0x138d: 0x027e, 0x138e: 0x0257, 0x138f: 0x0269, 0x1390: 0x026f, 0x1391: 0x025d,
+ 0x1392: 0x0272, 0x1393: 0x0251, 0x1394: 0x025a, 0x1395: 0x023c, 0x1396: 0x023f, 0x1397: 0x0248,
+ 0x1398: 0x024e, 0x1399: 0x0260, 0x139a: 0x0266, 0x139b: 0x026c,
+ 0x13a1: 0x0236, 0x13a2: 0x0242, 0x13a3: 0x024b,
+ 0x13a5: 0x0284, 0x13a6: 0x0254, 0x13a7: 0x0245, 0x13a8: 0x0263, 0x13a9: 0x028a,
+ 0x13ab: 0x0278, 0x13ac: 0x027b, 0x13ad: 0x027e, 0x13ae: 0x0257, 0x13af: 0x0269,
+ 0x13b0: 0x026f, 0x13b1: 0x025d, 0x13b2: 0x0272, 0x13b3: 0x0251, 0x13b4: 0x025a, 0x13b5: 0x023c,
+ 0x13b6: 0x023f, 0x13b7: 0x0248, 0x13b8: 0x024e, 0x13b9: 0x0260, 0x13ba: 0x0266, 0x13bb: 0x026c,
+ // Block 0x4f, offset 0x13c0
+ 0x13c0: 0x19bb, 0x13c1: 0x19b8, 0x13c2: 0x19be, 0x13c3: 0x19e2, 0x13c4: 0x1a06, 0x13c5: 0x1a2a,
+ 0x13c6: 0x1a4e, 0x13c7: 0x1a57, 0x13c8: 0x1a5d, 0x13c9: 0x1a63, 0x13ca: 0x1a69,
+ 0x13d0: 0x1bce, 0x13d1: 0x1bd2,
+ 0x13d2: 0x1bd6, 0x13d3: 0x1bda, 0x13d4: 0x1bde, 0x13d5: 0x1be2, 0x13d6: 0x1be6, 0x13d7: 0x1bea,
+ 0x13d8: 0x1bee, 0x13d9: 0x1bf2, 0x13da: 0x1bf6, 0x13db: 0x1bfa, 0x13dc: 0x1bfe, 0x13dd: 0x1c02,
+ 0x13de: 0x1c06, 0x13df: 0x1c0a, 0x13e0: 0x1c0e, 0x13e1: 0x1c12, 0x13e2: 0x1c16, 0x13e3: 0x1c1a,
+ 0x13e4: 0x1c1e, 0x13e5: 0x1c22, 0x13e6: 0x1c26, 0x13e7: 0x1c2a, 0x13e8: 0x1c2e, 0x13e9: 0x1c32,
+ 0x13ea: 0x28c9, 0x13eb: 0x0047, 0x13ec: 0x0065, 0x13ed: 0x1a7e, 0x13ee: 0x1af3,
+ 0x13f0: 0x0043, 0x13f1: 0x0045, 0x13f2: 0x0047, 0x13f3: 0x0049, 0x13f4: 0x004b, 0x13f5: 0x004d,
+ 0x13f6: 0x004f, 0x13f7: 0x0051, 0x13f8: 0x0053, 0x13f9: 0x0055, 0x13fa: 0x0057, 0x13fb: 0x0059,
+ 0x13fc: 0x005b, 0x13fd: 0x005d, 0x13fe: 0x005f, 0x13ff: 0x0061,
+ // Block 0x50, offset 0x1400
+ 0x1400: 0x2858, 0x1401: 0x286d, 0x1402: 0x0575,
+ 0x1410: 0x0c81, 0x1411: 0x0ab9,
+ 0x1412: 0x0945, 0x1413: 0x45aa, 0x1414: 0x078d, 0x1415: 0x0a61, 0x1416: 0x13a1, 0x1417: 0x0a71,
+ 0x1418: 0x0799, 0x1419: 0x0d49, 0x141a: 0x0f21, 0x141b: 0x0d21, 0x141c: 0x0899, 0x141d: 0x0bdd,
+ 0x141e: 0x0831, 0x141f: 0x0d29, 0x1420: 0x0885, 0x1421: 0x1189, 0x1422: 0x0ff5, 0x1423: 0x13fd,
+ 0x1424: 0x0a45, 0x1425: 0x097d, 0x1426: 0x0ed5, 0x1427: 0x0c8d, 0x1428: 0x0cb9, 0x1429: 0x0731,
+ 0x142a: 0x073d, 0x142b: 0x147d, 0x142c: 0x0b4d, 0x142d: 0x0759, 0x142e: 0x0961, 0x142f: 0x0cad,
+ 0x1430: 0x1425, 0x1431: 0x0c85, 0x1432: 0x10e1, 0x1433: 0x111d, 0x1434: 0x0969, 0x1435: 0x0eb5,
+ 0x1436: 0x0d7d, 0x1437: 0x0d79, 0x1438: 0x1009, 0x1439: 0x089d, 0x143a: 0x09c9,
+ // Block 0x51, offset 0x1440
+ 0x1440: 0x076d, 0x1441: 0x0765, 0x1442: 0x0775, 0x1443: 0x16a9, 0x1444: 0x07b9, 0x1445: 0x07c9,
+ 0x1446: 0x07cd, 0x1447: 0x07d5, 0x1448: 0x07dd, 0x1449: 0x07e1, 0x144a: 0x07ed, 0x144b: 0x07e5,
+ 0x144c: 0x0625, 0x144d: 0x16bd, 0x144e: 0x0801, 0x144f: 0x0805, 0x1450: 0x0809, 0x1451: 0x0825,
+ 0x1452: 0x16ae, 0x1453: 0x0629, 0x1454: 0x0811, 0x1455: 0x0831, 0x1456: 0x16b8, 0x1457: 0x0841,
+ 0x1458: 0x0849, 0x1459: 0x07a9, 0x145a: 0x0851, 0x145b: 0x0855, 0x145c: 0x1893, 0x145d: 0x0871,
+ 0x145e: 0x0879, 0x145f: 0x0631, 0x1460: 0x0891, 0x1461: 0x0895, 0x1462: 0x089d, 0x1463: 0x08a1,
+ 0x1464: 0x0635, 0x1465: 0x08b9, 0x1466: 0x08bd, 0x1467: 0x08c9, 0x1468: 0x08d5, 0x1469: 0x08d9,
+ 0x146a: 0x08dd, 0x146b: 0x08e5, 0x146c: 0x0905, 0x146d: 0x0909, 0x146e: 0x0911, 0x146f: 0x0921,
+ 0x1470: 0x0929, 0x1471: 0x092d, 0x1472: 0x092d, 0x1473: 0x092d, 0x1474: 0x16cc, 0x1475: 0x0f05,
+ 0x1476: 0x0941, 0x1477: 0x0949, 0x1478: 0x16d1, 0x1479: 0x0955, 0x147a: 0x095d, 0x147b: 0x0965,
+ 0x147c: 0x098d, 0x147d: 0x0979, 0x147e: 0x0985, 0x147f: 0x0989,
+ // Block 0x52, offset 0x1480
+ 0x1480: 0x0991, 0x1481: 0x0999, 0x1482: 0x099d, 0x1483: 0x09a5, 0x1484: 0x09ad, 0x1485: 0x09b1,
+ 0x1486: 0x09b1, 0x1487: 0x09b9, 0x1488: 0x09c1, 0x1489: 0x09c5, 0x148a: 0x09d1, 0x148b: 0x09f5,
+ 0x148c: 0x09d9, 0x148d: 0x09f9, 0x148e: 0x09dd, 0x148f: 0x09e5, 0x1490: 0x087d, 0x1491: 0x0a41,
+ 0x1492: 0x0a09, 0x1493: 0x0a0d, 0x1494: 0x0a11, 0x1495: 0x0a05, 0x1496: 0x0a19, 0x1497: 0x0a15,
+ 0x1498: 0x0a2d, 0x1499: 0x16d6, 0x149a: 0x0a49, 0x149b: 0x0a4d, 0x149c: 0x0a55, 0x149d: 0x0a61,
+ 0x149e: 0x0a69, 0x149f: 0x0a85, 0x14a0: 0x16db, 0x14a1: 0x16e0, 0x14a2: 0x0a91, 0x14a3: 0x0a95,
+ 0x14a4: 0x0a99, 0x14a5: 0x0a8d, 0x14a6: 0x0aa1, 0x14a7: 0x0639, 0x14a8: 0x063d, 0x14a9: 0x0aa9,
+ 0x14aa: 0x0ab1, 0x14ab: 0x0ab1, 0x14ac: 0x16e5, 0x14ad: 0x0acd, 0x14ae: 0x0ad1, 0x14af: 0x0ad5,
+ 0x14b0: 0x0add, 0x14b1: 0x16ea, 0x14b2: 0x0ae5, 0x14b3: 0x0ae9, 0x14b4: 0x0bc1, 0x14b5: 0x0af1,
+ 0x14b6: 0x0641, 0x14b7: 0x0afd, 0x14b8: 0x0b0d, 0x14b9: 0x0b19, 0x14ba: 0x0b15, 0x14bb: 0x16f4,
+ 0x14bc: 0x0b21, 0x14bd: 0x16f9, 0x14be: 0x0b2d, 0x14bf: 0x0b29,
+ // Block 0x53, offset 0x14c0
+ 0x14c0: 0x0b31, 0x14c1: 0x0b41, 0x14c2: 0x0b45, 0x14c3: 0x0645, 0x14c4: 0x0b55, 0x14c5: 0x0b5d,
+ 0x14c6: 0x0b61, 0x14c7: 0x0b65, 0x14c8: 0x0649, 0x14c9: 0x16fe, 0x14ca: 0x064d, 0x14cb: 0x0b81,
+ 0x14cc: 0x0b85, 0x14cd: 0x0b89, 0x14ce: 0x0b91, 0x14cf: 0x18c5, 0x14d0: 0x0ba9, 0x14d1: 0x1708,
+ 0x14d2: 0x1708, 0x14d3: 0x1249, 0x14d4: 0x0bb9, 0x14d5: 0x0bb9, 0x14d6: 0x0651, 0x14d7: 0x172b,
+ 0x14d8: 0x17fd, 0x14d9: 0x0bc9, 0x14da: 0x0bd1, 0x14db: 0x0655, 0x14dc: 0x0be5, 0x14dd: 0x0bf5,
+ 0x14de: 0x0bf9, 0x14df: 0x0c01, 0x14e0: 0x0c11, 0x14e1: 0x065d, 0x14e2: 0x0659, 0x14e3: 0x0c15,
+ 0x14e4: 0x170d, 0x14e5: 0x0c19, 0x14e6: 0x0c2d, 0x14e7: 0x0c31, 0x14e8: 0x0c35, 0x14e9: 0x0c31,
+ 0x14ea: 0x0c41, 0x14eb: 0x0c45, 0x14ec: 0x0c55, 0x14ed: 0x0c4d, 0x14ee: 0x0c51, 0x14ef: 0x0c59,
+ 0x14f0: 0x0c5d, 0x14f1: 0x0c61, 0x14f2: 0x0c6d, 0x14f3: 0x0c71, 0x14f4: 0x0c89, 0x14f5: 0x0c91,
+ 0x14f6: 0x0ca1, 0x14f7: 0x0cb5, 0x14f8: 0x171c, 0x14f9: 0x0cb1, 0x14fa: 0x0ca5, 0x14fb: 0x0cbd,
+ 0x14fc: 0x0cc5, 0x14fd: 0x0cd9, 0x14fe: 0x1721, 0x14ff: 0x0ce1,
+ // Block 0x54, offset 0x1500
+ 0x1500: 0x0cd5, 0x1501: 0x0ccd, 0x1502: 0x0661, 0x1503: 0x0ce9, 0x1504: 0x0cf1, 0x1505: 0x0cf9,
+ 0x1506: 0x0ced, 0x1507: 0x0665, 0x1508: 0x0d09, 0x1509: 0x0d11, 0x150a: 0x1726, 0x150b: 0x0d3d,
+ 0x150c: 0x0d71, 0x150d: 0x0d4d, 0x150e: 0x0671, 0x150f: 0x0d59, 0x1510: 0x066d, 0x1511: 0x0669,
+ 0x1512: 0x0835, 0x1513: 0x0839, 0x1514: 0x0d75, 0x1515: 0x0d5d, 0x1516: 0x121d, 0x1517: 0x06d5,
+ 0x1518: 0x0d81, 0x1519: 0x0d85, 0x151a: 0x0d89, 0x151b: 0x0d9d, 0x151c: 0x0d95, 0x151d: 0x173f,
+ 0x151e: 0x0675, 0x151f: 0x0db1, 0x1520: 0x0da5, 0x1521: 0x0dc1, 0x1522: 0x0dc9, 0x1523: 0x1749,
+ 0x1524: 0x0dcd, 0x1525: 0x0db9, 0x1526: 0x0dd5, 0x1527: 0x0679, 0x1528: 0x0dd9, 0x1529: 0x0ddd,
+ 0x152a: 0x0de1, 0x152b: 0x0ded, 0x152c: 0x174e, 0x152d: 0x0df5, 0x152e: 0x067d, 0x152f: 0x0e01,
+ 0x1530: 0x1753, 0x1531: 0x0e05, 0x1532: 0x0681, 0x1533: 0x0e11, 0x1534: 0x0e1d, 0x1535: 0x0e29,
+ 0x1536: 0x0e2d, 0x1537: 0x1758, 0x1538: 0x16ef, 0x1539: 0x175d, 0x153a: 0x0e4d, 0x153b: 0x1762,
+ 0x153c: 0x0e59, 0x153d: 0x0e61, 0x153e: 0x0e51, 0x153f: 0x0e6d,
+ // Block 0x55, offset 0x1540
+ 0x1540: 0x0e7d, 0x1541: 0x0e8d, 0x1542: 0x0e81, 0x1543: 0x0e85, 0x1544: 0x0e91, 0x1545: 0x0e95,
+ 0x1546: 0x1767, 0x1547: 0x0e79, 0x1548: 0x0ead, 0x1549: 0x0eb1, 0x154a: 0x0685, 0x154b: 0x0ec5,
+ 0x154c: 0x0ec1, 0x154d: 0x176c, 0x154e: 0x0ea5, 0x154f: 0x0ee1, 0x1550: 0x1771, 0x1551: 0x1776,
+ 0x1552: 0x0ee5, 0x1553: 0x0ef9, 0x1554: 0x0ef5, 0x1555: 0x0ef1, 0x1556: 0x0689, 0x1557: 0x0efd,
+ 0x1558: 0x0f0d, 0x1559: 0x0f09, 0x155a: 0x0f15, 0x155b: 0x16b3, 0x155c: 0x0f25, 0x155d: 0x177b,
+ 0x155e: 0x0f31, 0x155f: 0x1785, 0x1560: 0x0f45, 0x1561: 0x0f51, 0x1562: 0x0f65, 0x1563: 0x178a,
+ 0x1564: 0x0f79, 0x1565: 0x0f7d, 0x1566: 0x178f, 0x1567: 0x1794, 0x1568: 0x0f99, 0x1569: 0x0fa9,
+ 0x156a: 0x068d, 0x156b: 0x0fad, 0x156c: 0x0691, 0x156d: 0x0691, 0x156e: 0x0fc5, 0x156f: 0x0fc9,
+ 0x1570: 0x0fd1, 0x1571: 0x0fd5, 0x1572: 0x0fe1, 0x1573: 0x0695, 0x1574: 0x0ff9, 0x1575: 0x1799,
+ 0x1576: 0x1015, 0x1577: 0x179e, 0x1578: 0x1021, 0x1579: 0x1703, 0x157a: 0x1031, 0x157b: 0x17a3,
+ 0x157c: 0x17a8, 0x157d: 0x17ad, 0x157e: 0x0699, 0x157f: 0x069d,
+ // Block 0x56, offset 0x1580
+ 0x1580: 0x1069, 0x1581: 0x17b7, 0x1582: 0x17b2, 0x1583: 0x17bc, 0x1584: 0x17c1, 0x1585: 0x1071,
+ 0x1586: 0x1075, 0x1587: 0x1075, 0x1588: 0x107d, 0x1589: 0x06a5, 0x158a: 0x1081, 0x158b: 0x06a9,
+ 0x158c: 0x06ad, 0x158d: 0x17cb, 0x158e: 0x1095, 0x158f: 0x109d, 0x1590: 0x10a9, 0x1591: 0x06b1,
+ 0x1592: 0x17d0, 0x1593: 0x10cd, 0x1594: 0x17d5, 0x1595: 0x17da, 0x1596: 0x10ed, 0x1597: 0x1105,
+ 0x1598: 0x06b5, 0x1599: 0x110d, 0x159a: 0x1111, 0x159b: 0x1115, 0x159c: 0x17df, 0x159d: 0x17e4,
+ 0x159e: 0x17e4, 0x159f: 0x112d, 0x15a0: 0x06b9, 0x15a1: 0x17e9, 0x15a2: 0x1141, 0x15a3: 0x1145,
+ 0x15a4: 0x06bd, 0x15a5: 0x17ee, 0x15a6: 0x1161, 0x15a7: 0x06c1, 0x15a8: 0x1171, 0x15a9: 0x1169,
+ 0x15aa: 0x1179, 0x15ab: 0x17f8, 0x15ac: 0x1191, 0x15ad: 0x06c5, 0x15ae: 0x119d, 0x15af: 0x11a5,
+ 0x15b0: 0x11b5, 0x15b1: 0x06c9, 0x15b2: 0x1802, 0x15b3: 0x1807, 0x15b4: 0x06cd, 0x15b5: 0x180c,
+ 0x15b6: 0x11cd, 0x15b7: 0x1811, 0x15b8: 0x11d9, 0x15b9: 0x11e5, 0x15ba: 0x11ed, 0x15bb: 0x1816,
+ 0x15bc: 0x181b, 0x15bd: 0x1201, 0x15be: 0x1820, 0x15bf: 0x1209,
+ // Block 0x57, offset 0x15c0
+ 0x15c0: 0x1730, 0x15c1: 0x06d1, 0x15c2: 0x1221, 0x15c3: 0x1225, 0x15c4: 0x06d9, 0x15c5: 0x1229,
+ 0x15c6: 0x0aa5, 0x15c7: 0x1825, 0x15c8: 0x182a, 0x15c9: 0x1735, 0x15ca: 0x173a, 0x15cb: 0x1249,
+ 0x15cc: 0x124d, 0x15cd: 0x1465, 0x15ce: 0x06dd, 0x15cf: 0x1279, 0x15d0: 0x1275, 0x15d1: 0x127d,
+ 0x15d2: 0x08b1, 0x15d3: 0x1281, 0x15d4: 0x1285, 0x15d5: 0x1289, 0x15d6: 0x1291, 0x15d7: 0x182f,
+ 0x15d8: 0x128d, 0x15d9: 0x1295, 0x15da: 0x12a9, 0x15db: 0x12ad, 0x15dc: 0x1299, 0x15dd: 0x12b1,
+ 0x15de: 0x12c5, 0x15df: 0x12d9, 0x15e0: 0x12a5, 0x15e1: 0x12b9, 0x15e2: 0x12bd, 0x15e3: 0x12c1,
+ 0x15e4: 0x1834, 0x15e5: 0x183e, 0x15e6: 0x1839, 0x15e7: 0x06e1, 0x15e8: 0x12e1, 0x15e9: 0x12e5,
+ 0x15ea: 0x12ed, 0x15eb: 0x1852, 0x15ec: 0x12f1, 0x15ed: 0x1843, 0x15ee: 0x06e5, 0x15ef: 0x06e9,
+ 0x15f0: 0x1848, 0x15f1: 0x184d, 0x15f2: 0x06ed, 0x15f3: 0x1311, 0x15f4: 0x1315, 0x15f5: 0x1319,
+ 0x15f6: 0x131d, 0x15f7: 0x1329, 0x15f8: 0x1325, 0x15f9: 0x1331, 0x15fa: 0x132d, 0x15fb: 0x133d,
+ 0x15fc: 0x1335, 0x15fd: 0x1339, 0x15fe: 0x1341, 0x15ff: 0x06f1,
+ // Block 0x58, offset 0x1600
+ 0x1600: 0x1349, 0x1601: 0x134d, 0x1602: 0x06f5, 0x1603: 0x135d, 0x1604: 0x1361, 0x1605: 0x1857,
+ 0x1606: 0x136d, 0x1607: 0x1371, 0x1608: 0x06f9, 0x1609: 0x137d, 0x160a: 0x062d, 0x160b: 0x185c,
+ 0x160c: 0x1861, 0x160d: 0x06fd, 0x160e: 0x0701, 0x160f: 0x13a9, 0x1610: 0x13c1, 0x1611: 0x13dd,
+ 0x1612: 0x13ed, 0x1613: 0x1866, 0x1614: 0x1401, 0x1615: 0x1405, 0x1616: 0x141d, 0x1617: 0x1429,
+ 0x1618: 0x1870, 0x1619: 0x16c2, 0x161a: 0x1435, 0x161b: 0x1431, 0x161c: 0x143d, 0x161d: 0x16c7,
+ 0x161e: 0x1449, 0x161f: 0x1455, 0x1620: 0x1875, 0x1621: 0x187a, 0x1622: 0x1495, 0x1623: 0x14a1,
+ 0x1624: 0x14a9, 0x1625: 0x187f, 0x1626: 0x14ad, 0x1627: 0x14d5, 0x1628: 0x14e1, 0x1629: 0x14e5,
+ 0x162a: 0x14dd, 0x162b: 0x14f1, 0x162c: 0x14f5, 0x162d: 0x1884, 0x162e: 0x1501, 0x162f: 0x0705,
+ 0x1630: 0x1509, 0x1631: 0x1889, 0x1632: 0x0709, 0x1633: 0x1541, 0x1634: 0x0b35, 0x1635: 0x1559,
+ 0x1636: 0x188e, 0x1637: 0x1898, 0x1638: 0x070d, 0x1639: 0x0711, 0x163a: 0x1581, 0x163b: 0x189d,
+ 0x163c: 0x0715, 0x163d: 0x18a2, 0x163e: 0x1599, 0x163f: 0x1599,
+ // Block 0x59, offset 0x1640
+ 0x1640: 0x15a1, 0x1641: 0x18a7, 0x1642: 0x15b9, 0x1643: 0x0719, 0x1644: 0x15c9, 0x1645: 0x15d5,
+ 0x1646: 0x15dd, 0x1647: 0x15e5, 0x1648: 0x071d, 0x1649: 0x18ac, 0x164a: 0x15f9, 0x164b: 0x1615,
+ 0x164c: 0x1621, 0x164d: 0x0721, 0x164e: 0x0725, 0x164f: 0x1625, 0x1650: 0x18b1, 0x1651: 0x0729,
+ 0x1652: 0x18b6, 0x1653: 0x18bb, 0x1654: 0x18c0, 0x1655: 0x1649, 0x1656: 0x072d, 0x1657: 0x165d,
+ 0x1658: 0x1665, 0x1659: 0x1669, 0x165a: 0x1671, 0x165b: 0x1679, 0x165c: 0x1681, 0x165d: 0x18ca,
+}
+
+// nfkcSparseOffset: 128 entries, 256 bytes
+var nfkcSparseOffset = []uint16{0x0, 0xe, 0x12, 0x1b, 0x25, 0x35, 0x37, 0x3c, 0x47, 0x56, 0x63, 0x6b, 0x6f, 0x74, 0x76, 0x85, 0x8d, 0x94, 0x97, 0x9f, 0xa3, 0xa7, 0xa9, 0xab, 0xb4, 0xb8, 0xbf, 0xc4, 0xc7, 0xd1, 0xd3, 0xda, 0xe2, 0xe6, 0xe8, 0xeb, 0xef, 0xf5, 0x106, 0x112, 0x114, 0x11a, 0x11c, 0x11e, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12b, 0x12e, 0x130, 0x133, 0x136, 0x13a, 0x143, 0x145, 0x148, 0x14a, 0x154, 0x15f, 0x16e, 0x17c, 0x18a, 0x19a, 0x1a8, 0x1af, 0x1b5, 0x1c4, 0x1c8, 0x1ca, 0x1ce, 0x1d0, 0x1d3, 0x1d5, 0x1d8, 0x1da, 0x1dd, 0x1df, 0x1e1, 0x1e3, 0x1ef, 0x1f8, 0x1ff, 0x20c, 0x20f, 0x211, 0x213, 0x215, 0x217, 0x219, 0x21c, 0x21e, 0x220, 0x222, 0x224, 0x22a, 0x22d, 0x22f, 0x231, 0x233, 0x235, 0x244, 0x246, 0x24c, 0x254, 0x25b, 0x25e, 0x26b, 0x275, 0x277, 0x279, 0x27d, 0x282, 0x28e, 0x293, 0x29c, 0x2a2, 0x2a7, 0x2ab, 0x2b0, 0x2b4, 0x2c4, 0x2d2, 0x2e0, 0x2ee, 0x2f8, 0x2fa}
+
+// nfkcSparseValues: 772 entries, 3088 bytes
+var nfkcSparseValues = [772]valueRange{
+ // Block 0x0, offset 0x1
+ {value: 0x0002, lo: 0x0d},
+ {value: 0x0001, lo: 0xa0, hi: 0xa0},
+ {value: 0x425e, lo: 0xa8, hi: 0xa8},
+ {value: 0x0083, lo: 0xaa, hi: 0xaa},
+ {value: 0x424a, lo: 0xaf, hi: 0xaf},
+ {value: 0x0025, lo: 0xb2, hi: 0xb3},
+ {value: 0x4240, lo: 0xb4, hi: 0xb4},
+ {value: 0x01dc, lo: 0xb5, hi: 0xb5},
+ {value: 0x4277, lo: 0xb8, hi: 0xb8},
+ {value: 0x0023, lo: 0xb9, hi: 0xb9},
+ {value: 0x009f, lo: 0xba, hi: 0xba},
+ {value: 0x235e, lo: 0xbc, hi: 0xbc},
+ {value: 0x2352, lo: 0xbd, hi: 0xbd},
+ {value: 0x23f4, lo: 0xbe, hi: 0xbe},
+ // Block 0x1, offset 0x2
+ {value: 0x0091, lo: 0x03},
+ {value: 0x46c8, lo: 0xa0, hi: 0xa1},
+ {value: 0x46fa, lo: 0xaf, hi: 0xb0},
+ {value: 0x8800, lo: 0xb7, hi: 0xb7},
+ // Block 0x2, offset 0x3
+ {value: 0x0003, lo: 0x08},
+ {value: 0x8800, lo: 0x92, hi: 0x92},
+ {value: 0x0091, lo: 0xb0, hi: 0xb0},
+ {value: 0x011c, lo: 0xb1, hi: 0xb1},
+ {value: 0x0095, lo: 0xb2, hi: 0xb2},
+ {value: 0x00a5, lo: 0xb3, hi: 0xb3},
+ {value: 0x0143, lo: 0xb4, hi: 0xb6},
+ {value: 0x00af, lo: 0xb7, hi: 0xb7},
+ {value: 0x00b3, lo: 0xb8, hi: 0xb8},
+ // Block 0x3, offset 0x4
+ {value: 0x000a, lo: 0x09},
+ {value: 0x4254, lo: 0x98, hi: 0x98},
+ {value: 0x4259, lo: 0x99, hi: 0x9a},
+ {value: 0x427c, lo: 0x9b, hi: 0x9b},
+ {value: 0x4245, lo: 0x9c, hi: 0x9c},
+ {value: 0x4268, lo: 0x9d, hi: 0x9d},
+ {value: 0x0116, lo: 0xa0, hi: 0xa0},
+ {value: 0x0099, lo: 0xa1, hi: 0xa1},
+ {value: 0x00a7, lo: 0xa2, hi: 0xa3},
+ {value: 0x0167, lo: 0xa4, hi: 0xa4},
+ // Block 0x4, offset 0x5
+ {value: 0x0000, lo: 0x0f},
+ {value: 0x8800, lo: 0x83, hi: 0x83},
+ {value: 0x8800, lo: 0x87, hi: 0x87},
+ {value: 0x8800, lo: 0x8b, hi: 0x8b},
+ {value: 0x8800, lo: 0x8d, hi: 0x8d},
+ {value: 0x378f, lo: 0x90, hi: 0x90},
+ {value: 0x379b, lo: 0x91, hi: 0x91},
+ {value: 0x3789, lo: 0x93, hi: 0x93},
+ {value: 0x8800, lo: 0x96, hi: 0x96},
+ {value: 0x3801, lo: 0x97, hi: 0x97},
+ {value: 0x37cb, lo: 0x9c, hi: 0x9c},
+ {value: 0x37b3, lo: 0x9d, hi: 0x9d},
+ {value: 0x37dd, lo: 0x9e, hi: 0x9e},
+ {value: 0x8800, lo: 0xb4, hi: 0xb5},
+ {value: 0x3807, lo: 0xb6, hi: 0xb6},
+ {value: 0x380d, lo: 0xb7, hi: 0xb7},
+ // Block 0x5, offset 0x6
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0x83, hi: 0x87},
+ // Block 0x6, offset 0x7
+ {value: 0x0001, lo: 0x04},
+ {value: 0x8018, lo: 0x81, hi: 0x82},
+ {value: 0x80e6, lo: 0x84, hi: 0x84},
+ {value: 0x80dc, lo: 0x85, hi: 0x85},
+ {value: 0x8012, lo: 0x87, hi: 0x87},
+ // Block 0x7, offset 0x8
+ {value: 0x0000, lo: 0x0a},
+ {value: 0x80e6, lo: 0x90, hi: 0x97},
+ {value: 0x801e, lo: 0x98, hi: 0x98},
+ {value: 0x801f, lo: 0x99, hi: 0x99},
+ {value: 0x8020, lo: 0x9a, hi: 0x9a},
+ {value: 0x382b, lo: 0xa2, hi: 0xa2},
+ {value: 0x3831, lo: 0xa3, hi: 0xa3},
+ {value: 0x383d, lo: 0xa4, hi: 0xa4},
+ {value: 0x3837, lo: 0xa5, hi: 0xa5},
+ {value: 0x3843, lo: 0xa6, hi: 0xa6},
+ {value: 0x8800, lo: 0xa7, hi: 0xa7},
+ // Block 0x8, offset 0x9
+ {value: 0x0000, lo: 0x0e},
+ {value: 0x3855, lo: 0x80, hi: 0x80},
+ {value: 0x8800, lo: 0x81, hi: 0x81},
+ {value: 0x3849, lo: 0x82, hi: 0x82},
+ {value: 0x8800, lo: 0x92, hi: 0x92},
+ {value: 0x384f, lo: 0x93, hi: 0x93},
+ {value: 0x8800, lo: 0x95, hi: 0x95},
+ {value: 0x80e6, lo: 0x96, hi: 0x9c},
+ {value: 0x80e6, lo: 0x9f, hi: 0xa2},
+ {value: 0x80dc, lo: 0xa3, hi: 0xa3},
+ {value: 0x80e6, lo: 0xa4, hi: 0xa4},
+ {value: 0x80e6, lo: 0xa7, hi: 0xa8},
+ {value: 0x80dc, lo: 0xaa, hi: 0xaa},
+ {value: 0x80e6, lo: 0xab, hi: 0xac},
+ {value: 0x80dc, lo: 0xad, hi: 0xad},
+ // Block 0x9, offset 0xa
+ {value: 0x0000, lo: 0x0c},
+ {value: 0x8024, lo: 0x91, hi: 0x91},
+ {value: 0x80e6, lo: 0xb0, hi: 0xb0},
+ {value: 0x80dc, lo: 0xb1, hi: 0xb1},
+ {value: 0x80e6, lo: 0xb2, hi: 0xb3},
+ {value: 0x80dc, lo: 0xb4, hi: 0xb4},
+ {value: 0x80e6, lo: 0xb5, hi: 0xb6},
+ {value: 0x80dc, lo: 0xb7, hi: 0xb9},
+ {value: 0x80e6, lo: 0xba, hi: 0xba},
+ {value: 0x80dc, lo: 0xbb, hi: 0xbc},
+ {value: 0x80e6, lo: 0xbd, hi: 0xbd},
+ {value: 0x80dc, lo: 0xbe, hi: 0xbe},
+ {value: 0x80e6, lo: 0xbf, hi: 0xbf},
+ // Block 0xa, offset 0xb
+ {value: 0x000a, lo: 0x07},
+ {value: 0x80e6, lo: 0x80, hi: 0x80},
+ {value: 0x80e6, lo: 0x81, hi: 0x81},
+ {value: 0x80dc, lo: 0x82, hi: 0x83},
+ {value: 0x80dc, lo: 0x84, hi: 0x85},
+ {value: 0x80dc, lo: 0x86, hi: 0x87},
+ {value: 0x80dc, lo: 0x88, hi: 0x89},
+ {value: 0x80e6, lo: 0x8a, hi: 0x8a},
+ // Block 0xb, offset 0xc
+ {value: 0x0000, lo: 0x03},
+ {value: 0x80e6, lo: 0xab, hi: 0xb1},
+ {value: 0x80dc, lo: 0xb2, hi: 0xb2},
+ {value: 0x80e6, lo: 0xb3, hi: 0xb3},
+ // Block 0xc, offset 0xd
+ {value: 0x0000, lo: 0x04},
+ {value: 0x80e6, lo: 0x96, hi: 0x99},
+ {value: 0x80e6, lo: 0x9b, hi: 0xa3},
+ {value: 0x80e6, lo: 0xa5, hi: 0xa7},
+ {value: 0x80e6, lo: 0xa9, hi: 0xad},
+ // Block 0xd, offset 0xe
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80dc, lo: 0x99, hi: 0x9b},
+ // Block 0xe, offset 0xf
+ {value: 0x0000, lo: 0x0e},
+ {value: 0x80e6, lo: 0xa4, hi: 0xa5},
+ {value: 0x80dc, lo: 0xa6, hi: 0xa6},
+ {value: 0x80e6, lo: 0xa7, hi: 0xa8},
+ {value: 0x80dc, lo: 0xa9, hi: 0xa9},
+ {value: 0x80e6, lo: 0xaa, hi: 0xac},
+ {value: 0x80dc, lo: 0xad, hi: 0xaf},
+ {value: 0x801b, lo: 0xb0, hi: 0xb0},
+ {value: 0x801c, lo: 0xb1, hi: 0xb1},
+ {value: 0x801d, lo: 0xb2, hi: 0xb2},
+ {value: 0x80e6, lo: 0xb3, hi: 0xb5},
+ {value: 0x80dc, lo: 0xb6, hi: 0xb6},
+ {value: 0x80e6, lo: 0xb7, hi: 0xb8},
+ {value: 0x80dc, lo: 0xb9, hi: 0xba},
+ {value: 0x80e6, lo: 0xbb, hi: 0xbe},
+ // Block 0xf, offset 0x10
+ {value: 0x0000, lo: 0x07},
+ {value: 0x8800, lo: 0xa8, hi: 0xa8},
+ {value: 0x3ec2, lo: 0xa9, hi: 0xa9},
+ {value: 0x8800, lo: 0xb0, hi: 0xb0},
+ {value: 0x3eca, lo: 0xb1, hi: 0xb1},
+ {value: 0x8800, lo: 0xb3, hi: 0xb3},
+ {value: 0x3ed2, lo: 0xb4, hi: 0xb4},
+ {value: 0x8607, lo: 0xbc, hi: 0xbc},
+ // Block 0x10, offset 0x11
+ {value: 0x0008, lo: 0x06},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x80e6, lo: 0x91, hi: 0x91},
+ {value: 0x80dc, lo: 0x92, hi: 0x92},
+ {value: 0x80e6, lo: 0x93, hi: 0x93},
+ {value: 0x80e6, lo: 0x94, hi: 0x94},
+ {value: 0x4502, lo: 0x98, hi: 0x9f},
+ // Block 0x11, offset 0x12
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8007, lo: 0xbc, hi: 0xbc},
+ {value: 0x8600, lo: 0xbe, hi: 0xbe},
+ // Block 0x12, offset 0x13
+ {value: 0x0007, lo: 0x07},
+ {value: 0x8800, lo: 0x87, hi: 0x87},
+ {value: 0x18cf, lo: 0x8b, hi: 0x8c},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x8600, lo: 0x97, hi: 0x97},
+ {value: 0x4542, lo: 0x9c, hi: 0x9c},
+ {value: 0x454a, lo: 0x9d, hi: 0x9d},
+ {value: 0x4552, lo: 0x9f, hi: 0x9f},
+ // Block 0x13, offset 0x14
+ {value: 0x0000, lo: 0x03},
+ {value: 0x457a, lo: 0xb3, hi: 0xb3},
+ {value: 0x4582, lo: 0xb6, hi: 0xb6},
+ {value: 0x8007, lo: 0xbc, hi: 0xbc},
+ // Block 0x14, offset 0x15
+ {value: 0x0008, lo: 0x03},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x455a, lo: 0x99, hi: 0x9b},
+ {value: 0x4572, lo: 0x9e, hi: 0x9e},
+ // Block 0x15, offset 0x16
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8007, lo: 0xbc, hi: 0xbc},
+ // Block 0x16, offset 0x17
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ // Block 0x17, offset 0x18
+ {value: 0x0000, lo: 0x08},
+ {value: 0x8800, lo: 0x87, hi: 0x87},
+ {value: 0x18e4, lo: 0x88, hi: 0x88},
+ {value: 0x18dd, lo: 0x8b, hi: 0x8b},
+ {value: 0x18eb, lo: 0x8c, hi: 0x8c},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x8600, lo: 0x96, hi: 0x97},
+ {value: 0x458a, lo: 0x9c, hi: 0x9c},
+ {value: 0x4592, lo: 0x9d, hi: 0x9d},
+ // Block 0x18, offset 0x19
+ {value: 0x0000, lo: 0x03},
+ {value: 0x8800, lo: 0x92, hi: 0x92},
+ {value: 0x18f2, lo: 0x94, hi: 0x94},
+ {value: 0x8600, lo: 0xbe, hi: 0xbe},
+ // Block 0x19, offset 0x1a
+ {value: 0x0000, lo: 0x06},
+ {value: 0x8800, lo: 0x86, hi: 0x87},
+ {value: 0x18f9, lo: 0x8a, hi: 0x8a},
+ {value: 0x1907, lo: 0x8b, hi: 0x8b},
+ {value: 0x1900, lo: 0x8c, hi: 0x8c},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x8600, lo: 0x97, hi: 0x97},
+ // Block 0x1a, offset 0x1b
+ {value: 0x0607, lo: 0x04},
+ {value: 0x8800, lo: 0x86, hi: 0x86},
+ {value: 0x3eda, lo: 0x88, hi: 0x88},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x8054, lo: 0x95, hi: 0x96},
+ // Block 0x1b, offset 0x1c
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8007, lo: 0xbc, hi: 0xbc},
+ {value: 0x8800, lo: 0xbf, hi: 0xbf},
+ // Block 0x1c, offset 0x1d
+ {value: 0x0000, lo: 0x09},
+ {value: 0x190e, lo: 0x80, hi: 0x80},
+ {value: 0x8600, lo: 0x82, hi: 0x82},
+ {value: 0x8800, lo: 0x86, hi: 0x86},
+ {value: 0x1915, lo: 0x87, hi: 0x87},
+ {value: 0x191c, lo: 0x88, hi: 0x88},
+ {value: 0x2e66, lo: 0x8a, hi: 0x8a},
+ {value: 0x19a5, lo: 0x8b, hi: 0x8b},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x8600, lo: 0x95, hi: 0x96},
+ // Block 0x1d, offset 0x1e
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8600, lo: 0xbe, hi: 0xbe},
+ // Block 0x1e, offset 0x1f
+ {value: 0x0000, lo: 0x06},
+ {value: 0x8800, lo: 0x86, hi: 0x87},
+ {value: 0x1923, lo: 0x8a, hi: 0x8a},
+ {value: 0x1931, lo: 0x8b, hi: 0x8b},
+ {value: 0x192a, lo: 0x8c, hi: 0x8c},
+ {value: 0x8009, lo: 0x8d, hi: 0x8d},
+ {value: 0x8600, lo: 0x97, hi: 0x97},
+ // Block 0x1f, offset 0x20
+ {value: 0x0007, lo: 0x07},
+ {value: 0x8609, lo: 0x8a, hi: 0x8a},
+ {value: 0x8600, lo: 0x8f, hi: 0x8f},
+ {value: 0x8800, lo: 0x99, hi: 0x99},
+ {value: 0x3ee2, lo: 0x9a, hi: 0x9a},
+ {value: 0x2e6d, lo: 0x9c, hi: 0x9d},
+ {value: 0x1938, lo: 0x9e, hi: 0x9e},
+ {value: 0x8600, lo: 0x9f, hi: 0x9f},
+ // Block 0x20, offset 0x21
+ {value: 0x0000, lo: 0x03},
+ {value: 0x2763, lo: 0xb3, hi: 0xb3},
+ {value: 0x8067, lo: 0xb8, hi: 0xb9},
+ {value: 0x8009, lo: 0xba, hi: 0xba},
+ // Block 0x21, offset 0x22
+ {value: 0x0000, lo: 0x01},
+ {value: 0x806b, lo: 0x88, hi: 0x8b},
+ // Block 0x22, offset 0x23
+ {value: 0x0000, lo: 0x02},
+ {value: 0x2778, lo: 0xb3, hi: 0xb3},
+ {value: 0x8076, lo: 0xb8, hi: 0xb9},
+ // Block 0x23, offset 0x24
+ {value: 0x0000, lo: 0x03},
+ {value: 0x807a, lo: 0x88, hi: 0x8b},
+ {value: 0x276a, lo: 0x9c, hi: 0x9c},
+ {value: 0x2771, lo: 0x9d, hi: 0x9d},
+ // Block 0x24, offset 0x25
+ {value: 0x0000, lo: 0x05},
+ {value: 0x0305, lo: 0x8c, hi: 0x8c},
+ {value: 0x80dc, lo: 0x98, hi: 0x99},
+ {value: 0x80dc, lo: 0xb5, hi: 0xb5},
+ {value: 0x80dc, lo: 0xb7, hi: 0xb7},
+ {value: 0x80d8, lo: 0xb9, hi: 0xb9},
+ // Block 0x25, offset 0x26
+ {value: 0x0000, lo: 0x10},
+ {value: 0x2786, lo: 0x83, hi: 0x83},
+ {value: 0x278d, lo: 0x8d, hi: 0x8d},
+ {value: 0x2794, lo: 0x92, hi: 0x92},
+ {value: 0x279b, lo: 0x97, hi: 0x97},
+ {value: 0x27a2, lo: 0x9c, hi: 0x9c},
+ {value: 0x277f, lo: 0xa9, hi: 0xa9},
+ {value: 0x8081, lo: 0xb1, hi: 0xb1},
+ {value: 0x8082, lo: 0xb2, hi: 0xb2},
+ {value: 0x49b6, lo: 0xb3, hi: 0xb3},
+ {value: 0x8084, lo: 0xb4, hi: 0xb4},
+ {value: 0x49bf, lo: 0xb5, hi: 0xb5},
+ {value: 0x459a, lo: 0xb6, hi: 0xb6},
+ {value: 0x45da, lo: 0xb7, hi: 0xb7},
+ {value: 0x45a2, lo: 0xb8, hi: 0xb8},
+ {value: 0x45e5, lo: 0xb9, hi: 0xb9},
+ {value: 0x8082, lo: 0xba, hi: 0xbd},
+ // Block 0x26, offset 0x27
+ {value: 0x0000, lo: 0x0b},
+ {value: 0x8082, lo: 0x80, hi: 0x80},
+ {value: 0x49c8, lo: 0x81, hi: 0x81},
+ {value: 0x80e6, lo: 0x82, hi: 0x83},
+ {value: 0x8009, lo: 0x84, hi: 0x84},
+ {value: 0x80e6, lo: 0x86, hi: 0x87},
+ {value: 0x27b0, lo: 0x93, hi: 0x93},
+ {value: 0x27b7, lo: 0x9d, hi: 0x9d},
+ {value: 0x27be, lo: 0xa2, hi: 0xa2},
+ {value: 0x27c5, lo: 0xa7, hi: 0xa7},
+ {value: 0x27cc, lo: 0xac, hi: 0xac},
+ {value: 0x27a9, lo: 0xb9, hi: 0xb9},
+ // Block 0x27, offset 0x28
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80dc, lo: 0x86, hi: 0x86},
+ // Block 0x28, offset 0x29
+ {value: 0x0000, lo: 0x05},
+ {value: 0x8800, lo: 0xa5, hi: 0xa5},
+ {value: 0x193f, lo: 0xa6, hi: 0xa6},
+ {value: 0x8600, lo: 0xae, hi: 0xae},
+ {value: 0x8007, lo: 0xb7, hi: 0xb7},
+ {value: 0x8009, lo: 0xb9, hi: 0xba},
+ // Block 0x29, offset 0x2a
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80dc, lo: 0x8d, hi: 0x8d},
+ // Block 0x2a, offset 0x2b
+ {value: 0x0000, lo: 0x01},
+ {value: 0x0309, lo: 0xbc, hi: 0xbc},
+ // Block 0x2b, offset 0x2c
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8800, lo: 0x80, hi: 0x92},
+ // Block 0x2c, offset 0x2d
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8e00, lo: 0xa1, hi: 0xb5},
+ // Block 0x2d, offset 0x2e
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8600, lo: 0xa8, hi: 0xbf},
+ // Block 0x2e, offset 0x2f
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8600, lo: 0x80, hi: 0x82},
+ // Block 0x2f, offset 0x30
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0x9d, hi: 0x9f},
+ // Block 0x30, offset 0x31
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8009, lo: 0x94, hi: 0x94},
+ {value: 0x8009, lo: 0xb4, hi: 0xb4},
+ // Block 0x31, offset 0x32
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8009, lo: 0x92, hi: 0x92},
+ {value: 0x80e6, lo: 0x9d, hi: 0x9d},
+ // Block 0x32, offset 0x33
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e4, lo: 0xa9, hi: 0xa9},
+ // Block 0x33, offset 0x34
+ {value: 0x0008, lo: 0x02},
+ {value: 0x80de, lo: 0xb9, hi: 0xba},
+ {value: 0x80dc, lo: 0xbb, hi: 0xbb},
+ // Block 0x34, offset 0x35
+ {value: 0x0000, lo: 0x02},
+ {value: 0x80e6, lo: 0x97, hi: 0x97},
+ {value: 0x80dc, lo: 0x98, hi: 0x98},
+ // Block 0x35, offset 0x36
+ {value: 0x0000, lo: 0x03},
+ {value: 0x8009, lo: 0xa0, hi: 0xa0},
+ {value: 0x80e6, lo: 0xb5, hi: 0xbc},
+ {value: 0x80dc, lo: 0xbf, hi: 0xbf},
+ // Block 0x36, offset 0x37
+ {value: 0x0000, lo: 0x08},
+ {value: 0x197e, lo: 0x80, hi: 0x80},
+ {value: 0x1985, lo: 0x81, hi: 0x81},
+ {value: 0x8800, lo: 0x82, hi: 0x82},
+ {value: 0x198c, lo: 0x83, hi: 0x83},
+ {value: 0x8009, lo: 0x84, hi: 0x84},
+ {value: 0x80e6, lo: 0xab, hi: 0xab},
+ {value: 0x80dc, lo: 0xac, hi: 0xac},
+ {value: 0x80e6, lo: 0xad, hi: 0xb3},
+ // Block 0x37, offset 0x38
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0xaa, hi: 0xab},
+ // Block 0x38, offset 0x39
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8007, lo: 0xa6, hi: 0xa6},
+ {value: 0x8009, lo: 0xb2, hi: 0xb3},
+ // Block 0x39, offset 0x3a
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8007, lo: 0xb7, hi: 0xb7},
+ // Block 0x3a, offset 0x3b
+ {value: 0x0000, lo: 0x09},
+ {value: 0x80e6, lo: 0x90, hi: 0x92},
+ {value: 0x8001, lo: 0x94, hi: 0x94},
+ {value: 0x80dc, lo: 0x95, hi: 0x99},
+ {value: 0x80e6, lo: 0x9a, hi: 0x9b},
+ {value: 0x80dc, lo: 0x9c, hi: 0x9f},
+ {value: 0x80e6, lo: 0xa0, hi: 0xa0},
+ {value: 0x8001, lo: 0xa2, hi: 0xa8},
+ {value: 0x80dc, lo: 0xad, hi: 0xad},
+ {value: 0x80e6, lo: 0xb4, hi: 0xb4},
+ // Block 0x3b, offset 0x3c
+ {value: 0x0002, lo: 0x0a},
+ {value: 0x0043, lo: 0xac, hi: 0xac},
+ {value: 0x00d4, lo: 0xad, hi: 0xad},
+ {value: 0x0045, lo: 0xae, hi: 0xae},
+ {value: 0x0049, lo: 0xb0, hi: 0xb1},
+ {value: 0x00e9, lo: 0xb2, hi: 0xb2},
+ {value: 0x004f, lo: 0xb3, hi: 0xba},
+ {value: 0x005f, lo: 0xbc, hi: 0xbc},
+ {value: 0x00f2, lo: 0xbd, hi: 0xbd},
+ {value: 0x0061, lo: 0xbe, hi: 0xbe},
+ {value: 0x0065, lo: 0xbf, hi: 0xbf},
+ // Block 0x3c, offset 0x3d
+ {value: 0x0000, lo: 0x0e},
+ {value: 0x80e6, lo: 0x80, hi: 0x81},
+ {value: 0x80dc, lo: 0x82, hi: 0x82},
+ {value: 0x80e6, lo: 0x83, hi: 0x89},
+ {value: 0x80dc, lo: 0x8a, hi: 0x8a},
+ {value: 0x80e6, lo: 0x8b, hi: 0x8c},
+ {value: 0x80ea, lo: 0x8d, hi: 0x8d},
+ {value: 0x80d6, lo: 0x8e, hi: 0x8e},
+ {value: 0x80dc, lo: 0x8f, hi: 0x8f},
+ {value: 0x80ca, lo: 0x90, hi: 0x90},
+ {value: 0x80e6, lo: 0x91, hi: 0xa6},
+ {value: 0x80e9, lo: 0xbc, hi: 0xbc},
+ {value: 0x80dc, lo: 0xbd, hi: 0xbd},
+ {value: 0x80e6, lo: 0xbe, hi: 0xbe},
+ {value: 0x80dc, lo: 0xbf, hi: 0xbf},
+ // Block 0x3d, offset 0x3e
+ {value: 0x0000, lo: 0x0d},
+ {value: 0x0001, lo: 0x80, hi: 0x8a},
+ {value: 0x04ad, lo: 0x91, hi: 0x91},
+ {value: 0x4281, lo: 0x97, hi: 0x97},
+ {value: 0x001d, lo: 0xa4, hi: 0xa4},
+ {value: 0x19b5, lo: 0xa5, hi: 0xa5},
+ {value: 0x1c9e, lo: 0xa6, hi: 0xa6},
+ {value: 0x0001, lo: 0xaf, hi: 0xaf},
+ {value: 0x283c, lo: 0xb3, hi: 0xb3},
+ {value: 0x29a9, lo: 0xb4, hi: 0xb4},
+ {value: 0x2843, lo: 0xb6, hi: 0xb6},
+ {value: 0x29b3, lo: 0xb7, hi: 0xb7},
+ {value: 0x19af, lo: 0xbc, hi: 0xbc},
+ {value: 0x424f, lo: 0xbe, hi: 0xbe},
+ // Block 0x3e, offset 0x3f
+ {value: 0x0002, lo: 0x0d},
+ {value: 0x1a75, lo: 0x87, hi: 0x87},
+ {value: 0x1a72, lo: 0x88, hi: 0x88},
+ {value: 0x19b2, lo: 0x89, hi: 0x89},
+ {value: 0x2b46, lo: 0x97, hi: 0x97},
+ {value: 0x0001, lo: 0x9f, hi: 0x9f},
+ {value: 0x0021, lo: 0xb0, hi: 0xb0},
+ {value: 0x0093, lo: 0xb1, hi: 0xb1},
+ {value: 0x0029, lo: 0xb4, hi: 0xb9},
+ {value: 0x0017, lo: 0xba, hi: 0xba},
+ {value: 0x04d9, lo: 0xbb, hi: 0xbb},
+ {value: 0x003b, lo: 0xbc, hi: 0xbc},
+ {value: 0x0011, lo: 0xbd, hi: 0xbe},
+ {value: 0x009d, lo: 0xbf, hi: 0xbf},
+ // Block 0x3f, offset 0x40
+ {value: 0x0002, lo: 0x0f},
+ {value: 0x0021, lo: 0x80, hi: 0x89},
+ {value: 0x0017, lo: 0x8a, hi: 0x8a},
+ {value: 0x04d9, lo: 0x8b, hi: 0x8b},
+ {value: 0x003b, lo: 0x8c, hi: 0x8c},
+ {value: 0x0011, lo: 0x8d, hi: 0x8e},
+ {value: 0x0083, lo: 0x90, hi: 0x90},
+ {value: 0x008b, lo: 0x91, hi: 0x91},
+ {value: 0x009f, lo: 0x92, hi: 0x92},
+ {value: 0x00b1, lo: 0x93, hi: 0x93},
+ {value: 0x0107, lo: 0x94, hi: 0x94},
+ {value: 0x0091, lo: 0x95, hi: 0x95},
+ {value: 0x0097, lo: 0x96, hi: 0x99},
+ {value: 0x00a1, lo: 0x9a, hi: 0x9a},
+ {value: 0x00a7, lo: 0x9b, hi: 0x9c},
+ {value: 0x1adb, lo: 0xa8, hi: 0xa8},
+ // Block 0x40, offset 0x41
+ {value: 0x0000, lo: 0x0d},
+ {value: 0x80e6, lo: 0x90, hi: 0x91},
+ {value: 0x8001, lo: 0x92, hi: 0x93},
+ {value: 0x80e6, lo: 0x94, hi: 0x97},
+ {value: 0x8001, lo: 0x98, hi: 0x9a},
+ {value: 0x80e6, lo: 0x9b, hi: 0x9c},
+ {value: 0x80e6, lo: 0xa1, hi: 0xa1},
+ {value: 0x8001, lo: 0xa5, hi: 0xa6},
+ {value: 0x80e6, lo: 0xa7, hi: 0xa7},
+ {value: 0x80dc, lo: 0xa8, hi: 0xa8},
+ {value: 0x80e6, lo: 0xa9, hi: 0xa9},
+ {value: 0x8001, lo: 0xaa, hi: 0xab},
+ {value: 0x80dc, lo: 0xac, hi: 0xaf},
+ {value: 0x80e6, lo: 0xb0, hi: 0xb0},
+ // Block 0x41, offset 0x42
+ {value: 0x0007, lo: 0x06},
+ {value: 0x22c2, lo: 0x89, hi: 0x89},
+ {value: 0x8800, lo: 0x90, hi: 0x90},
+ {value: 0x8800, lo: 0x92, hi: 0x92},
+ {value: 0x8800, lo: 0x94, hi: 0x94},
+ {value: 0x3ba3, lo: 0x9a, hi: 0x9b},
+ {value: 0x3bb1, lo: 0xae, hi: 0xae},
+ // Block 0x42, offset 0x43
+ {value: 0x000e, lo: 0x05},
+ {value: 0x3bb8, lo: 0x8d, hi: 0x8e},
+ {value: 0x3bbf, lo: 0x8f, hi: 0x8f},
+ {value: 0x8800, lo: 0x90, hi: 0x90},
+ {value: 0x8800, lo: 0x92, hi: 0x92},
+ {value: 0x8800, lo: 0x94, hi: 0x94},
+ // Block 0x43, offset 0x44
+ {value: 0x0173, lo: 0x0e},
+ {value: 0x8800, lo: 0x83, hi: 0x83},
+ {value: 0x3bcd, lo: 0x84, hi: 0x84},
+ {value: 0x8800, lo: 0x88, hi: 0x88},
+ {value: 0x3bd4, lo: 0x89, hi: 0x89},
+ {value: 0x8800, lo: 0x8b, hi: 0x8b},
+ {value: 0x3bdb, lo: 0x8c, hi: 0x8c},
+ {value: 0x8800, lo: 0xa3, hi: 0xa3},
+ {value: 0x3be2, lo: 0xa4, hi: 0xa4},
+ {value: 0x8800, lo: 0xa5, hi: 0xa5},
+ {value: 0x3be9, lo: 0xa6, hi: 0xa6},
+ {value: 0x284a, lo: 0xac, hi: 0xad},
+ {value: 0x2851, lo: 0xaf, hi: 0xaf},
+ {value: 0x29c7, lo: 0xb0, hi: 0xb0},
+ {value: 0x8800, lo: 0xbc, hi: 0xbc},
+ // Block 0x44, offset 0x45
+ {value: 0x0007, lo: 0x03},
+ {value: 0x3c52, lo: 0xa0, hi: 0xa1},
+ {value: 0x3c7c, lo: 0xa2, hi: 0xa3},
+ {value: 0x3ca6, lo: 0xaa, hi: 0xad},
+ // Block 0x45, offset 0x46
+ {value: 0x0004, lo: 0x01},
+ {value: 0x04fd, lo: 0xa9, hi: 0xaa},
+ // Block 0x46, offset 0x47
+ {value: 0x0002, lo: 0x03},
+ {value: 0x0057, lo: 0x80, hi: 0x8f},
+ {value: 0x0083, lo: 0x90, hi: 0xa9},
+ {value: 0x0021, lo: 0xaa, hi: 0xaa},
+ // Block 0x47, offset 0x48
+ {value: 0x0000, lo: 0x01},
+ {value: 0x2b53, lo: 0x8c, hi: 0x8c},
+ // Block 0x48, offset 0x49
+ {value: 0x0263, lo: 0x02},
+ {value: 0x1cce, lo: 0xb4, hi: 0xb4},
+ {value: 0x1a6f, lo: 0xb5, hi: 0xb6},
+ // Block 0x49, offset 0x4a
+ {value: 0x0000, lo: 0x01},
+ {value: 0x44c3, lo: 0x9c, hi: 0x9c},
+ // Block 0x4a, offset 0x4b
+ {value: 0x0000, lo: 0x02},
+ {value: 0x0095, lo: 0xbc, hi: 0xbc},
+ {value: 0x006d, lo: 0xbd, hi: 0xbd},
+ // Block 0x4b, offset 0x4c
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0xaf, hi: 0xb1},
+ // Block 0x4c, offset 0x4d
+ {value: 0x0000, lo: 0x02},
+ {value: 0x04f1, lo: 0xaf, hi: 0xaf},
+ {value: 0x8009, lo: 0xbf, hi: 0xbf},
+ // Block 0x4d, offset 0x4e
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0xa0, hi: 0xbf},
+ // Block 0x4e, offset 0x4f
+ {value: 0x0000, lo: 0x01},
+ {value: 0x0e35, lo: 0x9f, hi: 0x9f},
+ // Block 0x4f, offset 0x50
+ {value: 0x0000, lo: 0x01},
+ {value: 0x169d, lo: 0xb3, hi: 0xb3},
+ // Block 0x50, offset 0x51
+ {value: 0x0004, lo: 0x0b},
+ {value: 0x1605, lo: 0x80, hi: 0x82},
+ {value: 0x161d, lo: 0x83, hi: 0x83},
+ {value: 0x1635, lo: 0x84, hi: 0x85},
+ {value: 0x1645, lo: 0x86, hi: 0x89},
+ {value: 0x1659, lo: 0x8a, hi: 0x8c},
+ {value: 0x166d, lo: 0x8d, hi: 0x8d},
+ {value: 0x1675, lo: 0x8e, hi: 0x8e},
+ {value: 0x167d, lo: 0x8f, hi: 0x90},
+ {value: 0x1689, lo: 0x91, hi: 0x93},
+ {value: 0x1699, lo: 0x94, hi: 0x94},
+ {value: 0x16a1, lo: 0x95, hi: 0x95},
+ // Block 0x51, offset 0x52
+ {value: 0x0004, lo: 0x08},
+ {value: 0x0001, lo: 0x80, hi: 0x80},
+ {value: 0x80da, lo: 0xaa, hi: 0xaa},
+ {value: 0x80e4, lo: 0xab, hi: 0xac},
+ {value: 0x80de, lo: 0xad, hi: 0xad},
+ {value: 0x80e0, lo: 0xae, hi: 0xae},
+ {value: 0x80e0, lo: 0xaf, hi: 0xaf},
+ {value: 0x0525, lo: 0xb6, hi: 0xb6},
+ {value: 0x08f9, lo: 0xb8, hi: 0xba},
+ // Block 0x52, offset 0x53
+ {value: 0x0004, lo: 0x06},
+ {value: 0x030d, lo: 0xb1, hi: 0xb2},
+ {value: 0x0435, lo: 0xb3, hi: 0xb3},
+ {value: 0x0315, lo: 0xb4, hi: 0xb4},
+ {value: 0x0439, lo: 0xb5, hi: 0xb6},
+ {value: 0x0319, lo: 0xb7, hi: 0xb9},
+ {value: 0x0441, lo: 0xba, hi: 0xbf},
+ // Block 0x53, offset 0x54
+ {value: 0x0004, lo: 0x0c},
+ {value: 0x0361, lo: 0x80, hi: 0x80},
+ {value: 0x0325, lo: 0x81, hi: 0x83},
+ {value: 0x0375, lo: 0x84, hi: 0x84},
+ {value: 0x0331, lo: 0x85, hi: 0x8e},
+ {value: 0x03c1, lo: 0x8f, hi: 0xa3},
+ {value: 0x03bd, lo: 0xa4, hi: 0xa4},
+ {value: 0x0359, lo: 0xa5, hi: 0xa6},
+ {value: 0x0459, lo: 0xa7, hi: 0xad},
+ {value: 0x0365, lo: 0xae, hi: 0xae},
+ {value: 0x0475, lo: 0xaf, hi: 0xb0},
+ {value: 0x0369, lo: 0xb1, hi: 0xb3},
+ {value: 0x0379, lo: 0xb4, hi: 0xbf},
+ // Block 0x54, offset 0x55
+ {value: 0x0000, lo: 0x02},
+ {value: 0x80e6, lo: 0xaf, hi: 0xaf},
+ {value: 0x80e6, lo: 0xb4, hi: 0xbd},
+ // Block 0x55, offset 0x56
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0x9f, hi: 0x9f},
+ // Block 0x56, offset 0x57
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0xb0, hi: 0xb1},
+ // Block 0x57, offset 0x58
+ {value: 0x0000, lo: 0x01},
+ {value: 0x16a5, lo: 0xb0, hi: 0xb0},
+ // Block 0x58, offset 0x59
+ {value: 0x000c, lo: 0x01},
+ {value: 0x00da, lo: 0xb8, hi: 0xb9},
+ // Block 0x59, offset 0x5a
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0x86, hi: 0x86},
+ // Block 0x5a, offset 0x5b
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8009, lo: 0x84, hi: 0x84},
+ {value: 0x80e6, lo: 0xa0, hi: 0xb1},
+ // Block 0x5b, offset 0x5c
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80dc, lo: 0xab, hi: 0xad},
+ // Block 0x5c, offset 0x5d
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0x93, hi: 0x93},
+ // Block 0x5d, offset 0x5e
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8007, lo: 0xb3, hi: 0xb3},
+ // Block 0x5e, offset 0x5f
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0x80, hi: 0x80},
+ // Block 0x5f, offset 0x60
+ {value: 0x0000, lo: 0x05},
+ {value: 0x80e6, lo: 0xb0, hi: 0xb0},
+ {value: 0x80e6, lo: 0xb2, hi: 0xb3},
+ {value: 0x80dc, lo: 0xb4, hi: 0xb4},
+ {value: 0x80e6, lo: 0xb7, hi: 0xb8},
+ {value: 0x80e6, lo: 0xbe, hi: 0xbf},
+ // Block 0x60, offset 0x61
+ {value: 0x0000, lo: 0x02},
+ {value: 0x80e6, lo: 0x81, hi: 0x81},
+ {value: 0x8009, lo: 0xb6, hi: 0xb6},
+ // Block 0x61, offset 0x62
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8009, lo: 0xad, hi: 0xad},
+ // Block 0x62, offset 0x63
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8100, lo: 0x80, hi: 0xbf},
+ // Block 0x63, offset 0x64
+ {value: 0x0000, lo: 0x01},
+ {value: 0x8100, lo: 0x80, hi: 0xa3},
+ // Block 0x64, offset 0x65
+ {value: 0x0002, lo: 0x01},
+ {value: 0x0003, lo: 0x81, hi: 0xbf},
+ // Block 0x65, offset 0x66
+ {value: 0x0004, lo: 0x0e},
+ {value: 0x03c1, lo: 0x82, hi: 0x87},
+ {value: 0x03d9, lo: 0x8a, hi: 0x8f},
+ {value: 0x03f1, lo: 0x92, hi: 0x97},
+ {value: 0x0409, lo: 0x9a, hi: 0x9c},
+ {value: 0x00bf, lo: 0xa0, hi: 0xa0},
+ {value: 0x00c2, lo: 0xa1, hi: 0xa1},
+ {value: 0x00cb, lo: 0xa2, hi: 0xa2},
+ {value: 0x424a, lo: 0xa3, hi: 0xa3},
+ {value: 0x00c8, lo: 0xa4, hi: 0xa4},
+ {value: 0x00c5, lo: 0xa5, hi: 0xa5},
+ {value: 0x04b9, lo: 0xa6, hi: 0xa6},
+ {value: 0x04dd, lo: 0xa8, hi: 0xa8},
+ {value: 0x04bd, lo: 0xa9, hi: 0xac},
+ {value: 0x04e1, lo: 0xad, hi: 0xae},
+ // Block 0x66, offset 0x67
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80dc, lo: 0xbd, hi: 0xbd},
+ // Block 0x67, offset 0x68
+ {value: 0x00db, lo: 0x05},
+ {value: 0x80dc, lo: 0x8d, hi: 0x8d},
+ {value: 0x80e6, lo: 0x8f, hi: 0x8f},
+ {value: 0x80e6, lo: 0xb8, hi: 0xb8},
+ {value: 0x8001, lo: 0xb9, hi: 0xba},
+ {value: 0x8009, lo: 0xbf, hi: 0xbf},
+ // Block 0x68, offset 0x69
+ {value: 0x05fe, lo: 0x07},
+ {value: 0x8800, lo: 0x99, hi: 0x99},
+ {value: 0x4222, lo: 0x9a, hi: 0x9a},
+ {value: 0x8800, lo: 0x9b, hi: 0x9b},
+ {value: 0x422c, lo: 0x9c, hi: 0x9c},
+ {value: 0x8800, lo: 0xa5, hi: 0xa5},
+ {value: 0x4236, lo: 0xab, hi: 0xab},
+ {value: 0x8009, lo: 0xb9, hi: 0xba},
+ // Block 0x69, offset 0x6a
+ {value: 0x0000, lo: 0x06},
+ {value: 0x80e6, lo: 0x80, hi: 0x82},
+ {value: 0x8600, lo: 0xa7, hi: 0xa7},
+ {value: 0x1993, lo: 0xae, hi: 0xae},
+ {value: 0x199c, lo: 0xaf, hi: 0xaf},
+ {value: 0x8800, lo: 0xb1, hi: 0xb2},
+ {value: 0x8009, lo: 0xb3, hi: 0xb4},
+ // Block 0x6a, offset 0x6b
+ {value: 0x0000, lo: 0x02},
+ {value: 0x8009, lo: 0xb6, hi: 0xb6},
+ {value: 0x8007, lo: 0xb7, hi: 0xb7},
+ // Block 0x6b, offset 0x6c
+ {value: 0x0000, lo: 0x0c},
+ {value: 0x45b2, lo: 0x9e, hi: 0x9e},
+ {value: 0x45bc, lo: 0x9f, hi: 0x9f},
+ {value: 0x45f0, lo: 0xa0, hi: 0xa0},
+ {value: 0x45fe, lo: 0xa1, hi: 0xa1},
+ {value: 0x460c, lo: 0xa2, hi: 0xa2},
+ {value: 0x461a, lo: 0xa3, hi: 0xa3},
+ {value: 0x4628, lo: 0xa4, hi: 0xa4},
+ {value: 0x80d8, lo: 0xa5, hi: 0xa6},
+ {value: 0x8001, lo: 0xa7, hi: 0xa9},
+ {value: 0x80e2, lo: 0xad, hi: 0xad},
+ {value: 0x80d8, lo: 0xae, hi: 0xb2},
+ {value: 0x80dc, lo: 0xbb, hi: 0xbf},
+ // Block 0x6c, offset 0x6d
+ {value: 0x0000, lo: 0x09},
+ {value: 0x80dc, lo: 0x80, hi: 0x82},
+ {value: 0x80e6, lo: 0x85, hi: 0x89},
+ {value: 0x80dc, lo: 0x8a, hi: 0x8b},
+ {value: 0x80e6, lo: 0xaa, hi: 0xad},
+ {value: 0x45c6, lo: 0xbb, hi: 0xbb},
+ {value: 0x45d0, lo: 0xbc, hi: 0xbc},
+ {value: 0x4636, lo: 0xbd, hi: 0xbd},
+ {value: 0x4652, lo: 0xbe, hi: 0xbe},
+ {value: 0x4644, lo: 0xbf, hi: 0xbf},
+ // Block 0x6d, offset 0x6e
+ {value: 0x0000, lo: 0x01},
+ {value: 0x4660, lo: 0x80, hi: 0x80},
+ // Block 0x6e, offset 0x6f
+ {value: 0x0000, lo: 0x01},
+ {value: 0x80e6, lo: 0x82, hi: 0x84},
+ // Block 0x6f, offset 0x70
+ {value: 0x0002, lo: 0x03},
+ {value: 0x0043, lo: 0x80, hi: 0x99},
+ {value: 0x0083, lo: 0x9a, hi: 0xb3},
+ {value: 0x0043, lo: 0xb4, hi: 0xbf},
+ // Block 0x70, offset 0x71
+ {value: 0x0002, lo: 0x04},
+ {value: 0x005b, lo: 0x80, hi: 0x8d},
+ {value: 0x0083, lo: 0x8e, hi: 0x94},
+ {value: 0x0093, lo: 0x96, hi: 0xa7},
+ {value: 0x0043, lo: 0xa8, hi: 0xbf},
+ // Block 0x71, offset 0x72
+ {value: 0x0002, lo: 0x0b},
+ {value: 0x0073, lo: 0x80, hi: 0x81},
+ {value: 0x0083, lo: 0x82, hi: 0x9b},
+ {value: 0x0043, lo: 0x9c, hi: 0x9c},
+ {value: 0x0047, lo: 0x9e, hi: 0x9f},
+ {value: 0x004f, lo: 0xa2, hi: 0xa2},
+ {value: 0x0055, lo: 0xa5, hi: 0xa6},
+ {value: 0x005d, lo: 0xa9, hi: 0xac},
+ {value: 0x0067, lo: 0xae, hi: 0xb5},
+ {value: 0x0083, lo: 0xb6, hi: 0xb9},
+ {value: 0x008d, lo: 0xbb, hi: 0xbb},
+ {value: 0x0091, lo: 0xbd, hi: 0xbf},
+ // Block 0x72, offset 0x73
+ {value: 0x0002, lo: 0x04},
+ {value: 0x0097, lo: 0x80, hi: 0x83},
+ {value: 0x00a1, lo: 0x85, hi: 0x8f},
+ {value: 0x0043, lo: 0x90, hi: 0xa9},
+ {value: 0x0083, lo: 0xaa, hi: 0xbf},
+ // Block 0x73, offset 0x74
+ {value: 0x0002, lo: 0x08},
+ {value: 0x00af, lo: 0x80, hi: 0x83},
+ {value: 0x0043, lo: 0x84, hi: 0x85},
+ {value: 0x0049, lo: 0x87, hi: 0x8a},
+ {value: 0x0055, lo: 0x8d, hi: 0x94},
+ {value: 0x0067, lo: 0x96, hi: 0x9c},
+ {value: 0x0083, lo: 0x9e, hi: 0xb7},
+ {value: 0x0043, lo: 0xb8, hi: 0xb9},
+ {value: 0x0049, lo: 0xbb, hi: 0xbe},
+ // Block 0x74, offset 0x75
+ {value: 0x0002, lo: 0x05},
+ {value: 0x0053, lo: 0x80, hi: 0x84},
+ {value: 0x005f, lo: 0x86, hi: 0x86},
+ {value: 0x0067, lo: 0x8a, hi: 0x90},
+ {value: 0x0083, lo: 0x92, hi: 0xab},
+ {value: 0x0043, lo: 0xac, hi: 0xbf},
+ // Block 0x75, offset 0x76
+ {value: 0x0002, lo: 0x04},
+ {value: 0x006b, lo: 0x80, hi: 0x85},
+ {value: 0x0083, lo: 0x86, hi: 0x9f},
+ {value: 0x0043, lo: 0xa0, hi: 0xb9},
+ {value: 0x0083, lo: 0xba, hi: 0xbf},
+ // Block 0x76, offset 0x77
+ {value: 0x0002, lo: 0x03},
+ {value: 0x008f, lo: 0x80, hi: 0x93},
+ {value: 0x0043, lo: 0x94, hi: 0xad},
+ {value: 0x0083, lo: 0xae, hi: 0xbf},
+ // Block 0x77, offset 0x78
+ {value: 0x0002, lo: 0x04},
+ {value: 0x00a7, lo: 0x80, hi: 0x87},
+ {value: 0x0043, lo: 0x88, hi: 0xa1},
+ {value: 0x0083, lo: 0xa2, hi: 0xbb},
+ {value: 0x0043, lo: 0xbc, hi: 0xbf},
+ // Block 0x78, offset 0x79
+ {value: 0x0002, lo: 0x03},
+ {value: 0x004b, lo: 0x80, hi: 0x95},
+ {value: 0x0083, lo: 0x96, hi: 0xaf},
+ {value: 0x0043, lo: 0xb0, hi: 0xbf},
+ // Block 0x79, offset 0x7a
+ {value: 0x0003, lo: 0x0f},
+ {value: 0x01b8, lo: 0x80, hi: 0x80},
+ {value: 0x04d1, lo: 0x81, hi: 0x81},
+ {value: 0x01bb, lo: 0x82, hi: 0x9a},
+ {value: 0x04cd, lo: 0x9b, hi: 0x9b},
+ {value: 0x01c7, lo: 0x9c, hi: 0x9c},
+ {value: 0x01d0, lo: 0x9d, hi: 0x9d},
+ {value: 0x01d6, lo: 0x9e, hi: 0x9e},
+ {value: 0x01fa, lo: 0x9f, hi: 0x9f},
+ {value: 0x01eb, lo: 0xa0, hi: 0xa0},
+ {value: 0x01e8, lo: 0xa1, hi: 0xa1},
+ {value: 0x0173, lo: 0xa2, hi: 0xb2},
+ {value: 0x0188, lo: 0xb3, hi: 0xb3},
+ {value: 0x01a6, lo: 0xb4, hi: 0xba},
+ {value: 0x04d1, lo: 0xbb, hi: 0xbb},
+ {value: 0x01bb, lo: 0xbc, hi: 0xbf},
+ // Block 0x7a, offset 0x7b
+ {value: 0x0003, lo: 0x0d},
+ {value: 0x01c7, lo: 0x80, hi: 0x94},
+ {value: 0x04cd, lo: 0x95, hi: 0x95},
+ {value: 0x01c7, lo: 0x96, hi: 0x96},
+ {value: 0x01d0, lo: 0x97, hi: 0x97},
+ {value: 0x01d6, lo: 0x98, hi: 0x98},
+ {value: 0x01fa, lo: 0x99, hi: 0x99},
+ {value: 0x01eb, lo: 0x9a, hi: 0x9a},
+ {value: 0x01e8, lo: 0x9b, hi: 0x9b},
+ {value: 0x0173, lo: 0x9c, hi: 0xac},
+ {value: 0x0188, lo: 0xad, hi: 0xad},
+ {value: 0x01a6, lo: 0xae, hi: 0xb4},
+ {value: 0x04d1, lo: 0xb5, hi: 0xb5},
+ {value: 0x01bb, lo: 0xb6, hi: 0xbf},
+ // Block 0x7b, offset 0x7c
+ {value: 0x0003, lo: 0x0d},
+ {value: 0x01d9, lo: 0x80, hi: 0x8e},
+ {value: 0x04cd, lo: 0x8f, hi: 0x8f},
+ {value: 0x01c7, lo: 0x90, hi: 0x90},
+ {value: 0x01d0, lo: 0x91, hi: 0x91},
+ {value: 0x01d6, lo: 0x92, hi: 0x92},
+ {value: 0x01fa, lo: 0x93, hi: 0x93},
+ {value: 0x01eb, lo: 0x94, hi: 0x94},
+ {value: 0x01e8, lo: 0x95, hi: 0x95},
+ {value: 0x0173, lo: 0x96, hi: 0xa6},
+ {value: 0x0188, lo: 0xa7, hi: 0xa7},
+ {value: 0x01a6, lo: 0xa8, hi: 0xae},
+ {value: 0x04d1, lo: 0xaf, hi: 0xaf},
+ {value: 0x01bb, lo: 0xb0, hi: 0xbf},
+ // Block 0x7c, offset 0x7d
+ {value: 0x0003, lo: 0x0d},
+ {value: 0x01eb, lo: 0x80, hi: 0x88},
+ {value: 0x04cd, lo: 0x89, hi: 0x89},
+ {value: 0x01c7, lo: 0x8a, hi: 0x8a},
+ {value: 0x01d0, lo: 0x8b, hi: 0x8b},
+ {value: 0x01d6, lo: 0x8c, hi: 0x8c},
+ {value: 0x01fa, lo: 0x8d, hi: 0x8d},
+ {value: 0x01eb, lo: 0x8e, hi: 0x8e},
+ {value: 0x01e8, lo: 0x8f, hi: 0x8f},
+ {value: 0x0173, lo: 0x90, hi: 0xa0},
+ {value: 0x0188, lo: 0xa1, hi: 0xa1},
+ {value: 0x01a6, lo: 0xa2, hi: 0xa8},
+ {value: 0x04d1, lo: 0xa9, hi: 0xa9},
+ {value: 0x01bb, lo: 0xaa, hi: 0xbf},
+ // Block 0x7d, offset 0x7e
+ {value: 0x0002, lo: 0x09},
+ {value: 0x0063, lo: 0x80, hi: 0x89},
+ {value: 0x1a93, lo: 0x8a, hi: 0x8a},
+ {value: 0x1ac3, lo: 0x8b, hi: 0x8b},
+ {value: 0x1ade, lo: 0x8c, hi: 0x8c},
+ {value: 0x1ae4, lo: 0x8d, hi: 0x8d},
+ {value: 0x1d02, lo: 0x8e, hi: 0x8e},
+ {value: 0x1af0, lo: 0x8f, hi: 0x8f},
+ {value: 0x1abd, lo: 0xaa, hi: 0xaa},
+ {value: 0x1ac0, lo: 0xab, hi: 0xab},
+ // Block 0x7e, offset 0x7f
+ {value: 0x0000, lo: 0x01},
+ {value: 0x1a81, lo: 0x90, hi: 0x90},
+ // Block 0x7f, offset 0x80
+ {value: 0x0028, lo: 0x09},
+ {value: 0x2a0d, lo: 0x80, hi: 0x80},
+ {value: 0x29d1, lo: 0x81, hi: 0x81},
+ {value: 0x29db, lo: 0x82, hi: 0x82},
+ {value: 0x29ef, lo: 0x83, hi: 0x84},
+ {value: 0x29f9, lo: 0x85, hi: 0x86},
+ {value: 0x29e5, lo: 0x87, hi: 0x87},
+ {value: 0x2a03, lo: 0x88, hi: 0x88},
+ {value: 0x0be1, lo: 0x90, hi: 0x90},
+ {value: 0x0959, lo: 0x91, hi: 0x91},
+}
+
+// nfkcLookup: 1216 bytes
+// Block 0 is the null block.
+var nfkcLookup = [1216]uint8{
+ // Block 0x0, offset 0x0
+ // Block 0x1, offset 0x40
+ // Block 0x2, offset 0x80
+ // Block 0x3, offset 0xc0
+ 0x0c2: 0x58, 0x0c3: 0x01, 0x0c4: 0x02, 0x0c5: 0x03, 0x0c6: 0x59, 0x0c7: 0x04,
+ 0x0c8: 0x05, 0x0ca: 0x5a, 0x0cb: 0x5b, 0x0cc: 0x06, 0x0cd: 0x07, 0x0ce: 0x08, 0x0cf: 0x09,
+ 0x0d0: 0x0a, 0x0d1: 0x5c, 0x0d2: 0x5d, 0x0d3: 0x0b, 0x0d6: 0x0c, 0x0d7: 0x5e,
+ 0x0d8: 0x5f, 0x0d9: 0x0d, 0x0db: 0x60, 0x0dc: 0x61, 0x0dd: 0x62, 0x0df: 0x63,
+ 0x0e0: 0x02, 0x0e1: 0x03, 0x0e2: 0x04, 0x0e3: 0x05,
+ 0x0ea: 0x06, 0x0eb: 0x07, 0x0ec: 0x07, 0x0ed: 0x08, 0x0ef: 0x09,
+ 0x0f0: 0x10,
+ // Block 0x4, offset 0x100
+ 0x120: 0x64, 0x121: 0x65, 0x123: 0x66, 0x124: 0x67, 0x125: 0x68, 0x126: 0x69, 0x127: 0x6a,
+ 0x128: 0x6b, 0x129: 0x6c, 0x12a: 0x6d, 0x12b: 0x6e, 0x12c: 0x69, 0x12d: 0x6f, 0x12e: 0x70, 0x12f: 0x71,
+ 0x131: 0x72, 0x132: 0x73, 0x133: 0x74, 0x134: 0x75, 0x135: 0x76, 0x137: 0x77,
+ 0x138: 0x78, 0x139: 0x79, 0x13a: 0x7a, 0x13b: 0x7b, 0x13c: 0x7c, 0x13d: 0x7d, 0x13e: 0x7e, 0x13f: 0x7f,
+ // Block 0x5, offset 0x140
+ 0x140: 0x80, 0x142: 0x81, 0x143: 0x82, 0x144: 0x83, 0x145: 0x84, 0x146: 0x85, 0x147: 0x86,
+ 0x14d: 0x87,
+ 0x15c: 0x88, 0x15f: 0x89,
+ 0x162: 0x8a, 0x164: 0x8b,
+ 0x168: 0x8c, 0x169: 0x8d, 0x16c: 0x0e, 0x16d: 0x8e, 0x16e: 0x8f, 0x16f: 0x90,
+ 0x170: 0x91, 0x173: 0x92, 0x174: 0x93, 0x175: 0x0f, 0x176: 0x10, 0x177: 0x94,
+ 0x178: 0x11, 0x179: 0x12, 0x17a: 0x13, 0x17b: 0x14, 0x17c: 0x15, 0x17d: 0x16, 0x17e: 0x17, 0x17f: 0x18,
+ // Block 0x6, offset 0x180
+ 0x180: 0x95, 0x181: 0x96, 0x182: 0x97, 0x183: 0x98, 0x184: 0x19, 0x185: 0x1a, 0x186: 0x99, 0x187: 0x9a,
+ 0x188: 0x9b, 0x189: 0x1b, 0x18a: 0x1c, 0x18b: 0x9c, 0x18c: 0x9d,
+ 0x191: 0x1d, 0x192: 0x1e, 0x193: 0x9e,
+ 0x1a8: 0x9f, 0x1a9: 0xa0, 0x1ab: 0xa1,
+ 0x1b1: 0xa2, 0x1b3: 0xa3, 0x1b5: 0xa4, 0x1b7: 0xa5,
+ 0x1ba: 0xa6, 0x1bb: 0xa7, 0x1bc: 0x1f, 0x1bd: 0x20, 0x1be: 0x21, 0x1bf: 0xa8,
+ // Block 0x7, offset 0x1c0
+ 0x1c0: 0xa9, 0x1c1: 0x22, 0x1c2: 0x23, 0x1c3: 0x24, 0x1c4: 0xaa, 0x1c5: 0xab, 0x1c6: 0x25,
+ 0x1c8: 0x26, 0x1c9: 0x27, 0x1ca: 0x28, 0x1cb: 0x29, 0x1cc: 0x2a, 0x1cd: 0x2b, 0x1ce: 0x2c, 0x1cf: 0x2d,
+ // Block 0x8, offset 0x200
+ 0x219: 0xac, 0x21a: 0xad, 0x21b: 0xae, 0x21d: 0xaf, 0x21f: 0xb0,
+ 0x220: 0xb1, 0x223: 0xb2, 0x224: 0xb3, 0x225: 0xb4, 0x226: 0xb5, 0x227: 0xb6,
+ 0x22a: 0xb7, 0x22b: 0xb8, 0x22f: 0xb9,
+ 0x230: 0xba, 0x231: 0xba, 0x232: 0xba, 0x233: 0xba, 0x234: 0xba, 0x235: 0xba, 0x236: 0xba, 0x237: 0xba,
+ 0x238: 0xba, 0x239: 0xba, 0x23a: 0xba, 0x23b: 0xba, 0x23c: 0xba, 0x23d: 0xba, 0x23e: 0xba, 0x23f: 0xba,
+ // Block 0x9, offset 0x240
+ 0x240: 0xba, 0x241: 0xba, 0x242: 0xba, 0x243: 0xba, 0x244: 0xba, 0x245: 0xba, 0x246: 0xba, 0x247: 0xba,
+ 0x248: 0xba, 0x249: 0xba, 0x24a: 0xba, 0x24b: 0xba, 0x24c: 0xba, 0x24d: 0xba, 0x24e: 0xba, 0x24f: 0xba,
+ 0x250: 0xba, 0x251: 0xba, 0x252: 0xba, 0x253: 0xba, 0x254: 0xba, 0x255: 0xba, 0x256: 0xba, 0x257: 0xba,
+ 0x258: 0xba, 0x259: 0xba, 0x25a: 0xba, 0x25b: 0xba, 0x25c: 0xba, 0x25d: 0xba, 0x25e: 0xba, 0x25f: 0xba,
+ 0x260: 0xba, 0x261: 0xba, 0x262: 0xba, 0x263: 0xba, 0x264: 0xba, 0x265: 0xba, 0x266: 0xba, 0x267: 0xba,
+ 0x268: 0xba, 0x269: 0xba, 0x26a: 0xba, 0x26b: 0xba, 0x26c: 0xba, 0x26d: 0xba, 0x26e: 0xba, 0x26f: 0xba,
+ 0x270: 0xba, 0x271: 0xba, 0x272: 0xba, 0x273: 0xba, 0x274: 0xba, 0x275: 0xba, 0x276: 0xba, 0x277: 0xba,
+ 0x278: 0xba, 0x279: 0xba, 0x27a: 0xba, 0x27b: 0xba, 0x27c: 0xba, 0x27d: 0xba, 0x27e: 0xba, 0x27f: 0xba,
+ // Block 0xa, offset 0x280
+ 0x280: 0xba, 0x281: 0xba, 0x282: 0xba, 0x283: 0xba, 0x284: 0xba, 0x285: 0xba, 0x286: 0xba, 0x287: 0xba,
+ 0x288: 0xba, 0x289: 0xba, 0x28a: 0xba, 0x28b: 0xba, 0x28c: 0xba, 0x28d: 0xba, 0x28e: 0xba, 0x28f: 0xba,
+ 0x290: 0xba, 0x291: 0xba, 0x292: 0xba, 0x293: 0xba, 0x294: 0xba, 0x295: 0xba, 0x296: 0xba, 0x297: 0xba,
+ 0x298: 0xba, 0x299: 0xba, 0x29a: 0xba, 0x29b: 0xba, 0x29c: 0xba, 0x29d: 0xba, 0x29e: 0xbb,
+ // Block 0xb, offset 0x2c0
+ 0x2e4: 0x2e, 0x2e5: 0x2f, 0x2e6: 0x30, 0x2e7: 0x31,
+ 0x2e8: 0x32, 0x2e9: 0x33, 0x2ea: 0x34, 0x2eb: 0x35, 0x2ec: 0x36, 0x2ed: 0x37, 0x2ee: 0x38, 0x2ef: 0x39,
+ 0x2f0: 0x3a, 0x2f1: 0x3b, 0x2f2: 0x3c, 0x2f3: 0x3d, 0x2f4: 0x3e, 0x2f5: 0x3f, 0x2f6: 0x40, 0x2f7: 0x41,
+ 0x2f8: 0x42, 0x2f9: 0x43, 0x2fa: 0x44, 0x2fb: 0x45, 0x2fc: 0xbc, 0x2fd: 0x46, 0x2fe: 0x47, 0x2ff: 0xbd,
+ // Block 0xc, offset 0x300
+ 0x307: 0xbe,
+ 0x328: 0xbf,
+ // Block 0xd, offset 0x340
+ 0x341: 0xb1, 0x342: 0xc0, 0x344: 0xc1, 0x347: 0xb6,
+ 0x35a: 0xc2,
+ // Block 0xe, offset 0x380
+ 0x385: 0xc3, 0x386: 0xc4, 0x387: 0xc5,
+ 0x389: 0xc6,
+ 0x390: 0xc7, 0x391: 0xc8, 0x392: 0xc9, 0x393: 0xca, 0x394: 0xcb, 0x395: 0xcc, 0x396: 0xcd, 0x397: 0xce,
+ 0x398: 0xcf, 0x399: 0xd0, 0x39a: 0x48, 0x39b: 0xd1, 0x39c: 0xd2, 0x39d: 0xd3, 0x39e: 0xd4, 0x39f: 0x49,
+ // Block 0xf, offset 0x3c0
+ 0x3f8: 0x4a, 0x3f9: 0x4b, 0x3fa: 0x4c,
+ // Block 0x10, offset 0x400
+ 0x404: 0x4d, 0x405: 0xd5, 0x406: 0xd6,
+ 0x408: 0x4e, 0x409: 0xd7,
+ // Block 0x11, offset 0x440
+ 0x460: 0x4f, 0x461: 0x50, 0x462: 0x51, 0x463: 0x52, 0x464: 0x53, 0x465: 0x54, 0x466: 0x55, 0x467: 0x56,
+ 0x468: 0x57,
+ // Block 0x12, offset 0x480
+ 0x490: 0x0a, 0x491: 0x0b,
+ 0x49d: 0x0c, 0x49e: 0x0d, 0x49f: 0x0e,
+ 0x4af: 0x0f,
+}
+
+var nfkcTrie = trie{nfkcLookup[:], nfkcValues[:], nfkcSparseValues[:], nfkcSparseOffset[:], 88}
+
+// recompMap: 7464 bytes (entries only)
+var recompMap = map[uint32]rune{
+ 0x00410300: 0x00C0,
+ 0x00410301: 0x00C1,
+ 0x00410302: 0x00C2,
+ 0x00410303: 0x00C3,
+ 0x00410308: 0x00C4,
+ 0x0041030A: 0x00C5,
+ 0x00430327: 0x00C7,
+ 0x00450300: 0x00C8,
+ 0x00450301: 0x00C9,
+ 0x00450302: 0x00CA,
+ 0x00450308: 0x00CB,
+ 0x00490300: 0x00CC,
+ 0x00490301: 0x00CD,
+ 0x00490302: 0x00CE,
+ 0x00490308: 0x00CF,
+ 0x004E0303: 0x00D1,
+ 0x004F0300: 0x00D2,
+ 0x004F0301: 0x00D3,
+ 0x004F0302: 0x00D4,
+ 0x004F0303: 0x00D5,
+ 0x004F0308: 0x00D6,
+ 0x00550300: 0x00D9,
+ 0x00550301: 0x00DA,
+ 0x00550302: 0x00DB,
+ 0x00550308: 0x00DC,
+ 0x00590301: 0x00DD,
+ 0x00610300: 0x00E0,
+ 0x00610301: 0x00E1,
+ 0x00610302: 0x00E2,
+ 0x00610303: 0x00E3,
+ 0x00610308: 0x00E4,
+ 0x0061030A: 0x00E5,
+ 0x00630327: 0x00E7,
+ 0x00650300: 0x00E8,
+ 0x00650301: 0x00E9,
+ 0x00650302: 0x00EA,
+ 0x00650308: 0x00EB,
+ 0x00690300: 0x00EC,
+ 0x00690301: 0x00ED,
+ 0x00690302: 0x00EE,
+ 0x00690308: 0x00EF,
+ 0x006E0303: 0x00F1,
+ 0x006F0300: 0x00F2,
+ 0x006F0301: 0x00F3,
+ 0x006F0302: 0x00F4,
+ 0x006F0303: 0x00F5,
+ 0x006F0308: 0x00F6,
+ 0x00750300: 0x00F9,
+ 0x00750301: 0x00FA,
+ 0x00750302: 0x00FB,
+ 0x00750308: 0x00FC,
+ 0x00790301: 0x00FD,
+ 0x00790308: 0x00FF,
+ 0x00410304: 0x0100,
+ 0x00610304: 0x0101,
+ 0x00410306: 0x0102,
+ 0x00610306: 0x0103,
+ 0x00410328: 0x0104,
+ 0x00610328: 0x0105,
+ 0x00430301: 0x0106,
+ 0x00630301: 0x0107,
+ 0x00430302: 0x0108,
+ 0x00630302: 0x0109,
+ 0x00430307: 0x010A,
+ 0x00630307: 0x010B,
+ 0x0043030C: 0x010C,
+ 0x0063030C: 0x010D,
+ 0x0044030C: 0x010E,
+ 0x0064030C: 0x010F,
+ 0x00450304: 0x0112,
+ 0x00650304: 0x0113,
+ 0x00450306: 0x0114,
+ 0x00650306: 0x0115,
+ 0x00450307: 0x0116,
+ 0x00650307: 0x0117,
+ 0x00450328: 0x0118,
+ 0x00650328: 0x0119,
+ 0x0045030C: 0x011A,
+ 0x0065030C: 0x011B,
+ 0x00470302: 0x011C,
+ 0x00670302: 0x011D,
+ 0x00470306: 0x011E,
+ 0x00670306: 0x011F,
+ 0x00470307: 0x0120,
+ 0x00670307: 0x0121,
+ 0x00470327: 0x0122,
+ 0x00670327: 0x0123,
+ 0x00480302: 0x0124,
+ 0x00680302: 0x0125,
+ 0x00490303: 0x0128,
+ 0x00690303: 0x0129,
+ 0x00490304: 0x012A,
+ 0x00690304: 0x012B,
+ 0x00490306: 0x012C,
+ 0x00690306: 0x012D,
+ 0x00490328: 0x012E,
+ 0x00690328: 0x012F,
+ 0x00490307: 0x0130,
+ 0x004A0302: 0x0134,
+ 0x006A0302: 0x0135,
+ 0x004B0327: 0x0136,
+ 0x006B0327: 0x0137,
+ 0x004C0301: 0x0139,
+ 0x006C0301: 0x013A,
+ 0x004C0327: 0x013B,
+ 0x006C0327: 0x013C,
+ 0x004C030C: 0x013D,
+ 0x006C030C: 0x013E,
+ 0x004E0301: 0x0143,
+ 0x006E0301: 0x0144,
+ 0x004E0327: 0x0145,
+ 0x006E0327: 0x0146,
+ 0x004E030C: 0x0147,
+ 0x006E030C: 0x0148,
+ 0x004F0304: 0x014C,
+ 0x006F0304: 0x014D,
+ 0x004F0306: 0x014E,
+ 0x006F0306: 0x014F,
+ 0x004F030B: 0x0150,
+ 0x006F030B: 0x0151,
+ 0x00520301: 0x0154,
+ 0x00720301: 0x0155,
+ 0x00520327: 0x0156,
+ 0x00720327: 0x0157,
+ 0x0052030C: 0x0158,
+ 0x0072030C: 0x0159,
+ 0x00530301: 0x015A,
+ 0x00730301: 0x015B,
+ 0x00530302: 0x015C,
+ 0x00730302: 0x015D,
+ 0x00530327: 0x015E,
+ 0x00730327: 0x015F,
+ 0x0053030C: 0x0160,
+ 0x0073030C: 0x0161,
+ 0x00540327: 0x0162,
+ 0x00740327: 0x0163,
+ 0x0054030C: 0x0164,
+ 0x0074030C: 0x0165,
+ 0x00550303: 0x0168,
+ 0x00750303: 0x0169,
+ 0x00550304: 0x016A,
+ 0x00750304: 0x016B,
+ 0x00550306: 0x016C,
+ 0x00750306: 0x016D,
+ 0x0055030A: 0x016E,
+ 0x0075030A: 0x016F,
+ 0x0055030B: 0x0170,
+ 0x0075030B: 0x0171,
+ 0x00550328: 0x0172,
+ 0x00750328: 0x0173,
+ 0x00570302: 0x0174,
+ 0x00770302: 0x0175,
+ 0x00590302: 0x0176,
+ 0x00790302: 0x0177,
+ 0x00590308: 0x0178,
+ 0x005A0301: 0x0179,
+ 0x007A0301: 0x017A,
+ 0x005A0307: 0x017B,
+ 0x007A0307: 0x017C,
+ 0x005A030C: 0x017D,
+ 0x007A030C: 0x017E,
+ 0x004F031B: 0x01A0,
+ 0x006F031B: 0x01A1,
+ 0x0055031B: 0x01AF,
+ 0x0075031B: 0x01B0,
+ 0x0041030C: 0x01CD,
+ 0x0061030C: 0x01CE,
+ 0x0049030C: 0x01CF,
+ 0x0069030C: 0x01D0,
+ 0x004F030C: 0x01D1,
+ 0x006F030C: 0x01D2,
+ 0x0055030C: 0x01D3,
+ 0x0075030C: 0x01D4,
+ 0x00DC0304: 0x01D5,
+ 0x00FC0304: 0x01D6,
+ 0x00DC0301: 0x01D7,
+ 0x00FC0301: 0x01D8,
+ 0x00DC030C: 0x01D9,
+ 0x00FC030C: 0x01DA,
+ 0x00DC0300: 0x01DB,
+ 0x00FC0300: 0x01DC,
+ 0x00C40304: 0x01DE,
+ 0x00E40304: 0x01DF,
+ 0x02260304: 0x01E0,
+ 0x02270304: 0x01E1,
+ 0x00C60304: 0x01E2,
+ 0x00E60304: 0x01E3,
+ 0x0047030C: 0x01E6,
+ 0x0067030C: 0x01E7,
+ 0x004B030C: 0x01E8,
+ 0x006B030C: 0x01E9,
+ 0x004F0328: 0x01EA,
+ 0x006F0328: 0x01EB,
+ 0x01EA0304: 0x01EC,
+ 0x01EB0304: 0x01ED,
+ 0x01B7030C: 0x01EE,
+ 0x0292030C: 0x01EF,
+ 0x006A030C: 0x01F0,
+ 0x00470301: 0x01F4,
+ 0x00670301: 0x01F5,
+ 0x004E0300: 0x01F8,
+ 0x006E0300: 0x01F9,
+ 0x00C50301: 0x01FA,
+ 0x00E50301: 0x01FB,
+ 0x00C60301: 0x01FC,
+ 0x00E60301: 0x01FD,
+ 0x00D80301: 0x01FE,
+ 0x00F80301: 0x01FF,
+ 0x0041030F: 0x0200,
+ 0x0061030F: 0x0201,
+ 0x00410311: 0x0202,
+ 0x00610311: 0x0203,
+ 0x0045030F: 0x0204,
+ 0x0065030F: 0x0205,
+ 0x00450311: 0x0206,
+ 0x00650311: 0x0207,
+ 0x0049030F: 0x0208,
+ 0x0069030F: 0x0209,
+ 0x00490311: 0x020A,
+ 0x00690311: 0x020B,
+ 0x004F030F: 0x020C,
+ 0x006F030F: 0x020D,
+ 0x004F0311: 0x020E,
+ 0x006F0311: 0x020F,
+ 0x0052030F: 0x0210,
+ 0x0072030F: 0x0211,
+ 0x00520311: 0x0212,
+ 0x00720311: 0x0213,
+ 0x0055030F: 0x0214,
+ 0x0075030F: 0x0215,
+ 0x00550311: 0x0216,
+ 0x00750311: 0x0217,
+ 0x00530326: 0x0218,
+ 0x00730326: 0x0219,
+ 0x00540326: 0x021A,
+ 0x00740326: 0x021B,
+ 0x0048030C: 0x021E,
+ 0x0068030C: 0x021F,
+ 0x00410307: 0x0226,
+ 0x00610307: 0x0227,
+ 0x00450327: 0x0228,
+ 0x00650327: 0x0229,
+ 0x00D60304: 0x022A,
+ 0x00F60304: 0x022B,
+ 0x00D50304: 0x022C,
+ 0x00F50304: 0x022D,
+ 0x004F0307: 0x022E,
+ 0x006F0307: 0x022F,
+ 0x022E0304: 0x0230,
+ 0x022F0304: 0x0231,
+ 0x00590304: 0x0232,
+ 0x00790304: 0x0233,
+ 0x00A80301: 0x0385,
+ 0x03910301: 0x0386,
+ 0x03950301: 0x0388,
+ 0x03970301: 0x0389,
+ 0x03990301: 0x038A,
+ 0x039F0301: 0x038C,
+ 0x03A50301: 0x038E,
+ 0x03A90301: 0x038F,
+ 0x03CA0301: 0x0390,
+ 0x03990308: 0x03AA,
+ 0x03A50308: 0x03AB,
+ 0x03B10301: 0x03AC,
+ 0x03B50301: 0x03AD,
+ 0x03B70301: 0x03AE,
+ 0x03B90301: 0x03AF,
+ 0x03CB0301: 0x03B0,
+ 0x03B90308: 0x03CA,
+ 0x03C50308: 0x03CB,
+ 0x03BF0301: 0x03CC,
+ 0x03C50301: 0x03CD,
+ 0x03C90301: 0x03CE,
+ 0x03D20301: 0x03D3,
+ 0x03D20308: 0x03D4,
+ 0x04150300: 0x0400,
+ 0x04150308: 0x0401,
+ 0x04130301: 0x0403,
+ 0x04060308: 0x0407,
+ 0x041A0301: 0x040C,
+ 0x04180300: 0x040D,
+ 0x04230306: 0x040E,
+ 0x04180306: 0x0419,
+ 0x04380306: 0x0439,
+ 0x04350300: 0x0450,
+ 0x04350308: 0x0451,
+ 0x04330301: 0x0453,
+ 0x04560308: 0x0457,
+ 0x043A0301: 0x045C,
+ 0x04380300: 0x045D,
+ 0x04430306: 0x045E,
+ 0x0474030F: 0x0476,
+ 0x0475030F: 0x0477,
+ 0x04160306: 0x04C1,
+ 0x04360306: 0x04C2,
+ 0x04100306: 0x04D0,
+ 0x04300306: 0x04D1,
+ 0x04100308: 0x04D2,
+ 0x04300308: 0x04D3,
+ 0x04150306: 0x04D6,
+ 0x04350306: 0x04D7,
+ 0x04D80308: 0x04DA,
+ 0x04D90308: 0x04DB,
+ 0x04160308: 0x04DC,
+ 0x04360308: 0x04DD,
+ 0x04170308: 0x04DE,
+ 0x04370308: 0x04DF,
+ 0x04180304: 0x04E2,
+ 0x04380304: 0x04E3,
+ 0x04180308: 0x04E4,
+ 0x04380308: 0x04E5,
+ 0x041E0308: 0x04E6,
+ 0x043E0308: 0x04E7,
+ 0x04E80308: 0x04EA,
+ 0x04E90308: 0x04EB,
+ 0x042D0308: 0x04EC,
+ 0x044D0308: 0x04ED,
+ 0x04230304: 0x04EE,
+ 0x04430304: 0x04EF,
+ 0x04230308: 0x04F0,
+ 0x04430308: 0x04F1,
+ 0x0423030B: 0x04F2,
+ 0x0443030B: 0x04F3,
+ 0x04270308: 0x04F4,
+ 0x04470308: 0x04F5,
+ 0x042B0308: 0x04F8,
+ 0x044B0308: 0x04F9,
+ 0x06270653: 0x0622,
+ 0x06270654: 0x0623,
+ 0x06480654: 0x0624,
+ 0x06270655: 0x0625,
+ 0x064A0654: 0x0626,
+ 0x06D50654: 0x06C0,
+ 0x06C10654: 0x06C2,
+ 0x06D20654: 0x06D3,
+ 0x0928093C: 0x0929,
+ 0x0930093C: 0x0931,
+ 0x0933093C: 0x0934,
+ 0x09C709BE: 0x09CB,
+ 0x09C709D7: 0x09CC,
+ 0x0B470B56: 0x0B48,
+ 0x0B470B3E: 0x0B4B,
+ 0x0B470B57: 0x0B4C,
+ 0x0B920BD7: 0x0B94,
+ 0x0BC60BBE: 0x0BCA,
+ 0x0BC70BBE: 0x0BCB,
+ 0x0BC60BD7: 0x0BCC,
+ 0x0C460C56: 0x0C48,
+ 0x0CBF0CD5: 0x0CC0,
+ 0x0CC60CD5: 0x0CC7,
+ 0x0CC60CD6: 0x0CC8,
+ 0x0CC60CC2: 0x0CCA,
+ 0x0CCA0CD5: 0x0CCB,
+ 0x0D460D3E: 0x0D4A,
+ 0x0D470D3E: 0x0D4B,
+ 0x0D460D57: 0x0D4C,
+ 0x0DD90DCA: 0x0DDA,
+ 0x0DD90DCF: 0x0DDC,
+ 0x0DDC0DCA: 0x0DDD,
+ 0x0DD90DDF: 0x0DDE,
+ 0x1025102E: 0x1026,
+ 0x1B051B35: 0x1B06,
+ 0x1B071B35: 0x1B08,
+ 0x1B091B35: 0x1B0A,
+ 0x1B0B1B35: 0x1B0C,
+ 0x1B0D1B35: 0x1B0E,
+ 0x1B111B35: 0x1B12,
+ 0x1B3A1B35: 0x1B3B,
+ 0x1B3C1B35: 0x1B3D,
+ 0x1B3E1B35: 0x1B40,
+ 0x1B3F1B35: 0x1B41,
+ 0x1B421B35: 0x1B43,
+ 0x00410325: 0x1E00,
+ 0x00610325: 0x1E01,
+ 0x00420307: 0x1E02,
+ 0x00620307: 0x1E03,
+ 0x00420323: 0x1E04,
+ 0x00620323: 0x1E05,
+ 0x00420331: 0x1E06,
+ 0x00620331: 0x1E07,
+ 0x00C70301: 0x1E08,
+ 0x00E70301: 0x1E09,
+ 0x00440307: 0x1E0A,
+ 0x00640307: 0x1E0B,
+ 0x00440323: 0x1E0C,
+ 0x00640323: 0x1E0D,
+ 0x00440331: 0x1E0E,
+ 0x00640331: 0x1E0F,
+ 0x00440327: 0x1E10,
+ 0x00640327: 0x1E11,
+ 0x0044032D: 0x1E12,
+ 0x0064032D: 0x1E13,
+ 0x01120300: 0x1E14,
+ 0x01130300: 0x1E15,
+ 0x01120301: 0x1E16,
+ 0x01130301: 0x1E17,
+ 0x0045032D: 0x1E18,
+ 0x0065032D: 0x1E19,
+ 0x00450330: 0x1E1A,
+ 0x00650330: 0x1E1B,
+ 0x02280306: 0x1E1C,
+ 0x02290306: 0x1E1D,
+ 0x00460307: 0x1E1E,
+ 0x00660307: 0x1E1F,
+ 0x00470304: 0x1E20,
+ 0x00670304: 0x1E21,
+ 0x00480307: 0x1E22,
+ 0x00680307: 0x1E23,
+ 0x00480323: 0x1E24,
+ 0x00680323: 0x1E25,
+ 0x00480308: 0x1E26,
+ 0x00680308: 0x1E27,
+ 0x00480327: 0x1E28,
+ 0x00680327: 0x1E29,
+ 0x0048032E: 0x1E2A,
+ 0x0068032E: 0x1E2B,
+ 0x00490330: 0x1E2C,
+ 0x00690330: 0x1E2D,
+ 0x00CF0301: 0x1E2E,
+ 0x00EF0301: 0x1E2F,
+ 0x004B0301: 0x1E30,
+ 0x006B0301: 0x1E31,
+ 0x004B0323: 0x1E32,
+ 0x006B0323: 0x1E33,
+ 0x004B0331: 0x1E34,
+ 0x006B0331: 0x1E35,
+ 0x004C0323: 0x1E36,
+ 0x006C0323: 0x1E37,
+ 0x1E360304: 0x1E38,
+ 0x1E370304: 0x1E39,
+ 0x004C0331: 0x1E3A,
+ 0x006C0331: 0x1E3B,
+ 0x004C032D: 0x1E3C,
+ 0x006C032D: 0x1E3D,
+ 0x004D0301: 0x1E3E,
+ 0x006D0301: 0x1E3F,
+ 0x004D0307: 0x1E40,
+ 0x006D0307: 0x1E41,
+ 0x004D0323: 0x1E42,
+ 0x006D0323: 0x1E43,
+ 0x004E0307: 0x1E44,
+ 0x006E0307: 0x1E45,
+ 0x004E0323: 0x1E46,
+ 0x006E0323: 0x1E47,
+ 0x004E0331: 0x1E48,
+ 0x006E0331: 0x1E49,
+ 0x004E032D: 0x1E4A,
+ 0x006E032D: 0x1E4B,
+ 0x00D50301: 0x1E4C,
+ 0x00F50301: 0x1E4D,
+ 0x00D50308: 0x1E4E,
+ 0x00F50308: 0x1E4F,
+ 0x014C0300: 0x1E50,
+ 0x014D0300: 0x1E51,
+ 0x014C0301: 0x1E52,
+ 0x014D0301: 0x1E53,
+ 0x00500301: 0x1E54,
+ 0x00700301: 0x1E55,
+ 0x00500307: 0x1E56,
+ 0x00700307: 0x1E57,
+ 0x00520307: 0x1E58,
+ 0x00720307: 0x1E59,
+ 0x00520323: 0x1E5A,
+ 0x00720323: 0x1E5B,
+ 0x1E5A0304: 0x1E5C,
+ 0x1E5B0304: 0x1E5D,
+ 0x00520331: 0x1E5E,
+ 0x00720331: 0x1E5F,
+ 0x00530307: 0x1E60,
+ 0x00730307: 0x1E61,
+ 0x00530323: 0x1E62,
+ 0x00730323: 0x1E63,
+ 0x015A0307: 0x1E64,
+ 0x015B0307: 0x1E65,
+ 0x01600307: 0x1E66,
+ 0x01610307: 0x1E67,
+ 0x1E620307: 0x1E68,
+ 0x1E630307: 0x1E69,
+ 0x00540307: 0x1E6A,
+ 0x00740307: 0x1E6B,
+ 0x00540323: 0x1E6C,
+ 0x00740323: 0x1E6D,
+ 0x00540331: 0x1E6E,
+ 0x00740331: 0x1E6F,
+ 0x0054032D: 0x1E70,
+ 0x0074032D: 0x1E71,
+ 0x00550324: 0x1E72,
+ 0x00750324: 0x1E73,
+ 0x00550330: 0x1E74,
+ 0x00750330: 0x1E75,
+ 0x0055032D: 0x1E76,
+ 0x0075032D: 0x1E77,
+ 0x01680301: 0x1E78,
+ 0x01690301: 0x1E79,
+ 0x016A0308: 0x1E7A,
+ 0x016B0308: 0x1E7B,
+ 0x00560303: 0x1E7C,
+ 0x00760303: 0x1E7D,
+ 0x00560323: 0x1E7E,
+ 0x00760323: 0x1E7F,
+ 0x00570300: 0x1E80,
+ 0x00770300: 0x1E81,
+ 0x00570301: 0x1E82,
+ 0x00770301: 0x1E83,
+ 0x00570308: 0x1E84,
+ 0x00770308: 0x1E85,
+ 0x00570307: 0x1E86,
+ 0x00770307: 0x1E87,
+ 0x00570323: 0x1E88,
+ 0x00770323: 0x1E89,
+ 0x00580307: 0x1E8A,
+ 0x00780307: 0x1E8B,
+ 0x00580308: 0x1E8C,
+ 0x00780308: 0x1E8D,
+ 0x00590307: 0x1E8E,
+ 0x00790307: 0x1E8F,
+ 0x005A0302: 0x1E90,
+ 0x007A0302: 0x1E91,
+ 0x005A0323: 0x1E92,
+ 0x007A0323: 0x1E93,
+ 0x005A0331: 0x1E94,
+ 0x007A0331: 0x1E95,
+ 0x00680331: 0x1E96,
+ 0x00740308: 0x1E97,
+ 0x0077030A: 0x1E98,
+ 0x0079030A: 0x1E99,
+ 0x017F0307: 0x1E9B,
+ 0x00410323: 0x1EA0,
+ 0x00610323: 0x1EA1,
+ 0x00410309: 0x1EA2,
+ 0x00610309: 0x1EA3,
+ 0x00C20301: 0x1EA4,
+ 0x00E20301: 0x1EA5,
+ 0x00C20300: 0x1EA6,
+ 0x00E20300: 0x1EA7,
+ 0x00C20309: 0x1EA8,
+ 0x00E20309: 0x1EA9,
+ 0x00C20303: 0x1EAA,
+ 0x00E20303: 0x1EAB,
+ 0x1EA00302: 0x1EAC,
+ 0x1EA10302: 0x1EAD,
+ 0x01020301: 0x1EAE,
+ 0x01030301: 0x1EAF,
+ 0x01020300: 0x1EB0,
+ 0x01030300: 0x1EB1,
+ 0x01020309: 0x1EB2,
+ 0x01030309: 0x1EB3,
+ 0x01020303: 0x1EB4,
+ 0x01030303: 0x1EB5,
+ 0x1EA00306: 0x1EB6,
+ 0x1EA10306: 0x1EB7,
+ 0x00450323: 0x1EB8,
+ 0x00650323: 0x1EB9,
+ 0x00450309: 0x1EBA,
+ 0x00650309: 0x1EBB,
+ 0x00450303: 0x1EBC,
+ 0x00650303: 0x1EBD,
+ 0x00CA0301: 0x1EBE,
+ 0x00EA0301: 0x1EBF,
+ 0x00CA0300: 0x1EC0,
+ 0x00EA0300: 0x1EC1,
+ 0x00CA0309: 0x1EC2,
+ 0x00EA0309: 0x1EC3,
+ 0x00CA0303: 0x1EC4,
+ 0x00EA0303: 0x1EC5,
+ 0x1EB80302: 0x1EC6,
+ 0x1EB90302: 0x1EC7,
+ 0x00490309: 0x1EC8,
+ 0x00690309: 0x1EC9,
+ 0x00490323: 0x1ECA,
+ 0x00690323: 0x1ECB,
+ 0x004F0323: 0x1ECC,
+ 0x006F0323: 0x1ECD,
+ 0x004F0309: 0x1ECE,
+ 0x006F0309: 0x1ECF,
+ 0x00D40301: 0x1ED0,
+ 0x00F40301: 0x1ED1,
+ 0x00D40300: 0x1ED2,
+ 0x00F40300: 0x1ED3,
+ 0x00D40309: 0x1ED4,
+ 0x00F40309: 0x1ED5,
+ 0x00D40303: 0x1ED6,
+ 0x00F40303: 0x1ED7,
+ 0x1ECC0302: 0x1ED8,
+ 0x1ECD0302: 0x1ED9,
+ 0x01A00301: 0x1EDA,
+ 0x01A10301: 0x1EDB,
+ 0x01A00300: 0x1EDC,
+ 0x01A10300: 0x1EDD,
+ 0x01A00309: 0x1EDE,
+ 0x01A10309: 0x1EDF,
+ 0x01A00303: 0x1EE0,
+ 0x01A10303: 0x1EE1,
+ 0x01A00323: 0x1EE2,
+ 0x01A10323: 0x1EE3,
+ 0x00550323: 0x1EE4,
+ 0x00750323: 0x1EE5,
+ 0x00550309: 0x1EE6,
+ 0x00750309: 0x1EE7,
+ 0x01AF0301: 0x1EE8,
+ 0x01B00301: 0x1EE9,
+ 0x01AF0300: 0x1EEA,
+ 0x01B00300: 0x1EEB,
+ 0x01AF0309: 0x1EEC,
+ 0x01B00309: 0x1EED,
+ 0x01AF0303: 0x1EEE,
+ 0x01B00303: 0x1EEF,
+ 0x01AF0323: 0x1EF0,
+ 0x01B00323: 0x1EF1,
+ 0x00590300: 0x1EF2,
+ 0x00790300: 0x1EF3,
+ 0x00590323: 0x1EF4,
+ 0x00790323: 0x1EF5,
+ 0x00590309: 0x1EF6,
+ 0x00790309: 0x1EF7,
+ 0x00590303: 0x1EF8,
+ 0x00790303: 0x1EF9,
+ 0x03B10313: 0x1F00,
+ 0x03B10314: 0x1F01,
+ 0x1F000300: 0x1F02,
+ 0x1F010300: 0x1F03,
+ 0x1F000301: 0x1F04,
+ 0x1F010301: 0x1F05,
+ 0x1F000342: 0x1F06,
+ 0x1F010342: 0x1F07,
+ 0x03910313: 0x1F08,
+ 0x03910314: 0x1F09,
+ 0x1F080300: 0x1F0A,
+ 0x1F090300: 0x1F0B,
+ 0x1F080301: 0x1F0C,
+ 0x1F090301: 0x1F0D,
+ 0x1F080342: 0x1F0E,
+ 0x1F090342: 0x1F0F,
+ 0x03B50313: 0x1F10,
+ 0x03B50314: 0x1F11,
+ 0x1F100300: 0x1F12,
+ 0x1F110300: 0x1F13,
+ 0x1F100301: 0x1F14,
+ 0x1F110301: 0x1F15,
+ 0x03950313: 0x1F18,
+ 0x03950314: 0x1F19,
+ 0x1F180300: 0x1F1A,
+ 0x1F190300: 0x1F1B,
+ 0x1F180301: 0x1F1C,
+ 0x1F190301: 0x1F1D,
+ 0x03B70313: 0x1F20,
+ 0x03B70314: 0x1F21,
+ 0x1F200300: 0x1F22,
+ 0x1F210300: 0x1F23,
+ 0x1F200301: 0x1F24,
+ 0x1F210301: 0x1F25,
+ 0x1F200342: 0x1F26,
+ 0x1F210342: 0x1F27,
+ 0x03970313: 0x1F28,
+ 0x03970314: 0x1F29,
+ 0x1F280300: 0x1F2A,
+ 0x1F290300: 0x1F2B,
+ 0x1F280301: 0x1F2C,
+ 0x1F290301: 0x1F2D,
+ 0x1F280342: 0x1F2E,
+ 0x1F290342: 0x1F2F,
+ 0x03B90313: 0x1F30,
+ 0x03B90314: 0x1F31,
+ 0x1F300300: 0x1F32,
+ 0x1F310300: 0x1F33,
+ 0x1F300301: 0x1F34,
+ 0x1F310301: 0x1F35,
+ 0x1F300342: 0x1F36,
+ 0x1F310342: 0x1F37,
+ 0x03990313: 0x1F38,
+ 0x03990314: 0x1F39,
+ 0x1F380300: 0x1F3A,
+ 0x1F390300: 0x1F3B,
+ 0x1F380301: 0x1F3C,
+ 0x1F390301: 0x1F3D,
+ 0x1F380342: 0x1F3E,
+ 0x1F390342: 0x1F3F,
+ 0x03BF0313: 0x1F40,
+ 0x03BF0314: 0x1F41,
+ 0x1F400300: 0x1F42,
+ 0x1F410300: 0x1F43,
+ 0x1F400301: 0x1F44,
+ 0x1F410301: 0x1F45,
+ 0x039F0313: 0x1F48,
+ 0x039F0314: 0x1F49,
+ 0x1F480300: 0x1F4A,
+ 0x1F490300: 0x1F4B,
+ 0x1F480301: 0x1F4C,
+ 0x1F490301: 0x1F4D,
+ 0x03C50313: 0x1F50,
+ 0x03C50314: 0x1F51,
+ 0x1F500300: 0x1F52,
+ 0x1F510300: 0x1F53,
+ 0x1F500301: 0x1F54,
+ 0x1F510301: 0x1F55,
+ 0x1F500342: 0x1F56,
+ 0x1F510342: 0x1F57,
+ 0x03A50314: 0x1F59,
+ 0x1F590300: 0x1F5B,
+ 0x1F590301: 0x1F5D,
+ 0x1F590342: 0x1F5F,
+ 0x03C90313: 0x1F60,
+ 0x03C90314: 0x1F61,
+ 0x1F600300: 0x1F62,
+ 0x1F610300: 0x1F63,
+ 0x1F600301: 0x1F64,
+ 0x1F610301: 0x1F65,
+ 0x1F600342: 0x1F66,
+ 0x1F610342: 0x1F67,
+ 0x03A90313: 0x1F68,
+ 0x03A90314: 0x1F69,
+ 0x1F680300: 0x1F6A,
+ 0x1F690300: 0x1F6B,
+ 0x1F680301: 0x1F6C,
+ 0x1F690301: 0x1F6D,
+ 0x1F680342: 0x1F6E,
+ 0x1F690342: 0x1F6F,
+ 0x03B10300: 0x1F70,
+ 0x03B50300: 0x1F72,
+ 0x03B70300: 0x1F74,
+ 0x03B90300: 0x1F76,
+ 0x03BF0300: 0x1F78,
+ 0x03C50300: 0x1F7A,
+ 0x03C90300: 0x1F7C,
+ 0x1F000345: 0x1F80,
+ 0x1F010345: 0x1F81,
+ 0x1F020345: 0x1F82,
+ 0x1F030345: 0x1F83,
+ 0x1F040345: 0x1F84,
+ 0x1F050345: 0x1F85,
+ 0x1F060345: 0x1F86,
+ 0x1F070345: 0x1F87,
+ 0x1F080345: 0x1F88,
+ 0x1F090345: 0x1F89,
+ 0x1F0A0345: 0x1F8A,
+ 0x1F0B0345: 0x1F8B,
+ 0x1F0C0345: 0x1F8C,
+ 0x1F0D0345: 0x1F8D,
+ 0x1F0E0345: 0x1F8E,
+ 0x1F0F0345: 0x1F8F,
+ 0x1F200345: 0x1F90,
+ 0x1F210345: 0x1F91,
+ 0x1F220345: 0x1F92,
+ 0x1F230345: 0x1F93,
+ 0x1F240345: 0x1F94,
+ 0x1F250345: 0x1F95,
+ 0x1F260345: 0x1F96,
+ 0x1F270345: 0x1F97,
+ 0x1F280345: 0x1F98,
+ 0x1F290345: 0x1F99,
+ 0x1F2A0345: 0x1F9A,
+ 0x1F2B0345: 0x1F9B,
+ 0x1F2C0345: 0x1F9C,
+ 0x1F2D0345: 0x1F9D,
+ 0x1F2E0345: 0x1F9E,
+ 0x1F2F0345: 0x1F9F,
+ 0x1F600345: 0x1FA0,
+ 0x1F610345: 0x1FA1,
+ 0x1F620345: 0x1FA2,
+ 0x1F630345: 0x1FA3,
+ 0x1F640345: 0x1FA4,
+ 0x1F650345: 0x1FA5,
+ 0x1F660345: 0x1FA6,
+ 0x1F670345: 0x1FA7,
+ 0x1F680345: 0x1FA8,
+ 0x1F690345: 0x1FA9,
+ 0x1F6A0345: 0x1FAA,
+ 0x1F6B0345: 0x1FAB,
+ 0x1F6C0345: 0x1FAC,
+ 0x1F6D0345: 0x1FAD,
+ 0x1F6E0345: 0x1FAE,
+ 0x1F6F0345: 0x1FAF,
+ 0x03B10306: 0x1FB0,
+ 0x03B10304: 0x1FB1,
+ 0x1F700345: 0x1FB2,
+ 0x03B10345: 0x1FB3,
+ 0x03AC0345: 0x1FB4,
+ 0x03B10342: 0x1FB6,
+ 0x1FB60345: 0x1FB7,
+ 0x03910306: 0x1FB8,
+ 0x03910304: 0x1FB9,
+ 0x03910300: 0x1FBA,
+ 0x03910345: 0x1FBC,
+ 0x00A80342: 0x1FC1,
+ 0x1F740345: 0x1FC2,
+ 0x03B70345: 0x1FC3,
+ 0x03AE0345: 0x1FC4,
+ 0x03B70342: 0x1FC6,
+ 0x1FC60345: 0x1FC7,
+ 0x03950300: 0x1FC8,
+ 0x03970300: 0x1FCA,
+ 0x03970345: 0x1FCC,
+ 0x1FBF0300: 0x1FCD,
+ 0x1FBF0301: 0x1FCE,
+ 0x1FBF0342: 0x1FCF,
+ 0x03B90306: 0x1FD0,
+ 0x03B90304: 0x1FD1,
+ 0x03CA0300: 0x1FD2,
+ 0x03B90342: 0x1FD6,
+ 0x03CA0342: 0x1FD7,
+ 0x03990306: 0x1FD8,
+ 0x03990304: 0x1FD9,
+ 0x03990300: 0x1FDA,
+ 0x1FFE0300: 0x1FDD,
+ 0x1FFE0301: 0x1FDE,
+ 0x1FFE0342: 0x1FDF,
+ 0x03C50306: 0x1FE0,
+ 0x03C50304: 0x1FE1,
+ 0x03CB0300: 0x1FE2,
+ 0x03C10313: 0x1FE4,
+ 0x03C10314: 0x1FE5,
+ 0x03C50342: 0x1FE6,
+ 0x03CB0342: 0x1FE7,
+ 0x03A50306: 0x1FE8,
+ 0x03A50304: 0x1FE9,
+ 0x03A50300: 0x1FEA,
+ 0x03A10314: 0x1FEC,
+ 0x00A80300: 0x1FED,
+ 0x1F7C0345: 0x1FF2,
+ 0x03C90345: 0x1FF3,
+ 0x03CE0345: 0x1FF4,
+ 0x03C90342: 0x1FF6,
+ 0x1FF60345: 0x1FF7,
+ 0x039F0300: 0x1FF8,
+ 0x03A90300: 0x1FFA,
+ 0x03A90345: 0x1FFC,
+ 0x21900338: 0x219A,
+ 0x21920338: 0x219B,
+ 0x21940338: 0x21AE,
+ 0x21D00338: 0x21CD,
+ 0x21D40338: 0x21CE,
+ 0x21D20338: 0x21CF,
+ 0x22030338: 0x2204,
+ 0x22080338: 0x2209,
+ 0x220B0338: 0x220C,
+ 0x22230338: 0x2224,
+ 0x22250338: 0x2226,
+ 0x223C0338: 0x2241,
+ 0x22430338: 0x2244,
+ 0x22450338: 0x2247,
+ 0x22480338: 0x2249,
+ 0x003D0338: 0x2260,
+ 0x22610338: 0x2262,
+ 0x224D0338: 0x226D,
+ 0x003C0338: 0x226E,
+ 0x003E0338: 0x226F,
+ 0x22640338: 0x2270,
+ 0x22650338: 0x2271,
+ 0x22720338: 0x2274,
+ 0x22730338: 0x2275,
+ 0x22760338: 0x2278,
+ 0x22770338: 0x2279,
+ 0x227A0338: 0x2280,
+ 0x227B0338: 0x2281,
+ 0x22820338: 0x2284,
+ 0x22830338: 0x2285,
+ 0x22860338: 0x2288,
+ 0x22870338: 0x2289,
+ 0x22A20338: 0x22AC,
+ 0x22A80338: 0x22AD,
+ 0x22A90338: 0x22AE,
+ 0x22AB0338: 0x22AF,
+ 0x227C0338: 0x22E0,
+ 0x227D0338: 0x22E1,
+ 0x22910338: 0x22E2,
+ 0x22920338: 0x22E3,
+ 0x22B20338: 0x22EA,
+ 0x22B30338: 0x22EB,
+ 0x22B40338: 0x22EC,
+ 0x22B50338: 0x22ED,
+ 0x304B3099: 0x304C,
+ 0x304D3099: 0x304E,
+ 0x304F3099: 0x3050,
+ 0x30513099: 0x3052,
+ 0x30533099: 0x3054,
+ 0x30553099: 0x3056,
+ 0x30573099: 0x3058,
+ 0x30593099: 0x305A,
+ 0x305B3099: 0x305C,
+ 0x305D3099: 0x305E,
+ 0x305F3099: 0x3060,
+ 0x30613099: 0x3062,
+ 0x30643099: 0x3065,
+ 0x30663099: 0x3067,
+ 0x30683099: 0x3069,
+ 0x306F3099: 0x3070,
+ 0x306F309A: 0x3071,
+ 0x30723099: 0x3073,
+ 0x3072309A: 0x3074,
+ 0x30753099: 0x3076,
+ 0x3075309A: 0x3077,
+ 0x30783099: 0x3079,
+ 0x3078309A: 0x307A,
+ 0x307B3099: 0x307C,
+ 0x307B309A: 0x307D,
+ 0x30463099: 0x3094,
+ 0x309D3099: 0x309E,
+ 0x30AB3099: 0x30AC,
+ 0x30AD3099: 0x30AE,
+ 0x30AF3099: 0x30B0,
+ 0x30B13099: 0x30B2,
+ 0x30B33099: 0x30B4,
+ 0x30B53099: 0x30B6,
+ 0x30B73099: 0x30B8,
+ 0x30B93099: 0x30BA,
+ 0x30BB3099: 0x30BC,
+ 0x30BD3099: 0x30BE,
+ 0x30BF3099: 0x30C0,
+ 0x30C13099: 0x30C2,
+ 0x30C43099: 0x30C5,
+ 0x30C63099: 0x30C7,
+ 0x30C83099: 0x30C9,
+ 0x30CF3099: 0x30D0,
+ 0x30CF309A: 0x30D1,
+ 0x30D23099: 0x30D3,
+ 0x30D2309A: 0x30D4,
+ 0x30D53099: 0x30D6,
+ 0x30D5309A: 0x30D7,
+ 0x30D83099: 0x30D9,
+ 0x30D8309A: 0x30DA,
+ 0x30DB3099: 0x30DC,
+ 0x30DB309A: 0x30DD,
+ 0x30A63099: 0x30F4,
+ 0x30EF3099: 0x30F7,
+ 0x30F03099: 0x30F8,
+ 0x30F13099: 0x30F9,
+ 0x30F23099: 0x30FA,
+ 0x30FD3099: 0x30FE,
+ 0x109910BA: 0x1109A,
+ 0x109B10BA: 0x1109C,
+ 0x10A510BA: 0x110AB,
+ 0x11311127: 0x1112E,
+ 0x11321127: 0x1112F,
+}
+
+// Total size of tables: 50KB (51625 bytes)
diff --git a/src/pkg/exp/norm/trie.go b/src/pkg/exp/norm/trie.go
new file mode 100644
index 000000000..82267a8d3
--- /dev/null
+++ b/src/pkg/exp/norm/trie.go
@@ -0,0 +1,232 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+type valueRange struct {
+ value uint16 // header: value:stride
+ lo, hi byte // header: lo:n
+}
+
+type trie struct {
+ index []uint8
+ values []uint16
+ sparse []valueRange
+ sparseOffset []uint16
+ cutoff uint8 // indices >= cutoff are sparse
+}
+
+// lookupValue determines the type of block n and looks up the value for b.
+// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block
+// is a list of ranges with an accompanying value. Given a matching range r,
+// the value for b is by r.value + (b - r.lo) * stride.
+func (t *trie) lookupValue(n uint8, b byte) uint16 {
+ if n < t.cutoff {
+ return t.values[uint16(n)<<6+uint16(b)]
+ }
+ offset := t.sparseOffset[n-t.cutoff]
+ header := t.sparse[offset]
+ lo := offset + 1
+ hi := lo + uint16(header.lo)
+ for lo < hi {
+ m := lo + (hi-lo)/2
+ r := t.sparse[m]
+ if r.lo <= b && b <= r.hi {
+ return r.value + uint16(b-r.lo)*header.value
+ }
+ if b < r.lo {
+ hi = m
+ } else {
+ lo = m + 1
+ }
+ }
+ return 0
+}
+
+const (
+ t1 = 0x00 // 0000 0000
+ tx = 0x80 // 1000 0000
+ t2 = 0xC0 // 1100 0000
+ t3 = 0xE0 // 1110 0000
+ t4 = 0xF0 // 1111 0000
+ t5 = 0xF8 // 1111 1000
+ t6 = 0xFC // 1111 1100
+ te = 0xFE // 1111 1110
+)
+
+// lookup returns the trie value for the first UTF-8 encoding in s and
+// the width in bytes of this encoding. The size will be 0 if s does not
+// hold enough bytes to complete the encoding. len(s) must be greater than 0.
+func (t *trie) lookup(s []byte) (v uint16, sz int) {
+ c0 := s[0]
+ switch {
+ case c0 < tx:
+ return t.values[c0], 1
+ case c0 < t2:
+ return 0, 1
+ case c0 < t3:
+ if len(s) < 2 {
+ return 0, 0
+ }
+ i := t.index[c0]
+ c1 := s[1]
+ if c1 < tx || t2 <= c1 {
+ return 0, 1
+ }
+ return t.lookupValue(i, c1), 2
+ case c0 < t4:
+ if len(s) < 3 {
+ return 0, 0
+ }
+ i := t.index[c0]
+ c1 := s[1]
+ if c1 < tx || t2 <= c1 {
+ return 0, 1
+ }
+ o := uint16(i)<<6 + uint16(c1)
+ i = t.index[o]
+ c2 := s[2]
+ if c2 < tx || t2 <= c2 {
+ return 0, 2
+ }
+ return t.lookupValue(i, c2), 3
+ case c0 < t5:
+ if len(s) < 4 {
+ return 0, 0
+ }
+ i := t.index[c0]
+ c1 := s[1]
+ if c1 < tx || t2 <= c1 {
+ return 0, 1
+ }
+ o := uint16(i)<<6 + uint16(c1)
+ i = t.index[o]
+ c2 := s[2]
+ if c2 < tx || t2 <= c2 {
+ return 0, 2
+ }
+ o = uint16(i)<<6 + uint16(c2)
+ i = t.index[o]
+ c3 := s[3]
+ if c3 < tx || t2 <= c3 {
+ return 0, 3
+ }
+ return t.lookupValue(i, c3), 4
+ }
+ // Illegal rune
+ return 0, 1
+}
+
+// lookupString returns the trie value for the first UTF-8 encoding in s and
+// the width in bytes of this encoding. The size will be 0 if s does not
+// hold enough bytes to complete the encoding. len(s) must be greater than 0.
+func (t *trie) lookupString(s string) (v uint16, sz int) {
+ c0 := s[0]
+ switch {
+ case c0 < tx:
+ return t.values[c0], 1
+ case c0 < t2:
+ return 0, 1
+ case c0 < t3:
+ if len(s) < 2 {
+ return 0, 0
+ }
+ i := t.index[c0]
+ c1 := s[1]
+ if c1 < tx || t2 <= c1 {
+ return 0, 1
+ }
+ return t.lookupValue(i, c1), 2
+ case c0 < t4:
+ if len(s) < 3 {
+ return 0, 0
+ }
+ i := t.index[c0]
+ c1 := s[1]
+ if c1 < tx || t2 <= c1 {
+ return 0, 1
+ }
+ o := uint16(i)<<6 + uint16(c1)
+ i = t.index[o]
+ c2 := s[2]
+ if c2 < tx || t2 <= c2 {
+ return 0, 2
+ }
+ return t.lookupValue(i, c2), 3
+ case c0 < t5:
+ if len(s) < 4 {
+ return 0, 0
+ }
+ i := t.index[c0]
+ c1 := s[1]
+ if c1 < tx || t2 <= c1 {
+ return 0, 1
+ }
+ o := uint16(i)<<6 + uint16(c1)
+ i = t.index[o]
+ c2 := s[2]
+ if c2 < tx || t2 <= c2 {
+ return 0, 2
+ }
+ o = uint16(i)<<6 + uint16(c2)
+ i = t.index[o]
+ c3 := s[3]
+ if c3 < tx || t2 <= c3 {
+ return 0, 3
+ }
+ return t.lookupValue(i, c3), 4
+ }
+ // Illegal rune
+ return 0, 1
+}
+
+// lookupUnsafe returns the trie value for the first UTF-8 encoding in s.
+// s must hold a full encoding.
+func (t *trie) lookupUnsafe(s []byte) uint16 {
+ c0 := s[0]
+ if c0 < tx {
+ return t.values[c0]
+ }
+ if c0 < t2 {
+ return 0
+ }
+ i := t.index[c0]
+ if c0 < t3 {
+ return t.lookupValue(i, s[1])
+ }
+ i = t.index[uint16(i)<<6+uint16(s[1])]
+ if c0 < t4 {
+ return t.lookupValue(i, s[2])
+ }
+ i = t.index[uint16(i)<<6+uint16(s[2])]
+ if c0 < t5 {
+ return t.lookupValue(i, s[3])
+ }
+ return 0
+}
+
+// lookupStringUnsafe returns the trie value for the first UTF-8 encoding in s.
+// s must hold a full encoding.
+func (t *trie) lookupStringUnsafe(s string) uint16 {
+ c0 := s[0]
+ if c0 < tx {
+ return t.values[c0]
+ }
+ if c0 < t2 {
+ return 0
+ }
+ i := t.index[c0]
+ if c0 < t3 {
+ return t.lookupValue(i, s[1])
+ }
+ i = t.index[uint16(i)<<6+uint16(s[1])]
+ if c0 < t4 {
+ return t.lookupValue(i, s[2])
+ }
+ i = t.index[uint16(i)<<6+uint16(s[2])]
+ if c0 < t5 {
+ return t.lookupValue(i, s[3])
+ }
+ return 0
+}
diff --git a/src/pkg/exp/norm/trie_test.go b/src/pkg/exp/norm/trie_test.go
new file mode 100644
index 000000000..1a75cc705
--- /dev/null
+++ b/src/pkg/exp/norm/trie_test.go
@@ -0,0 +1,152 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import (
+ "testing"
+ "unicode/utf8"
+)
+
+// Test data is located in triedata_test.go; generated by maketesttables.
+var testdata = testdataTrie
+
+type rangeTest struct {
+ block uint8
+ lookup byte
+ result uint16
+ table []valueRange
+ offsets []uint16
+}
+
+var range1Off = []uint16{0, 2}
+var range1 = []valueRange{
+ {0, 1, 0},
+ {1, 0x80, 0x80},
+ {0, 2, 0},
+ {1, 0x80, 0x80},
+ {9, 0xff, 0xff},
+}
+
+var rangeTests = []rangeTest{
+ {10, 0x80, 1, range1, range1Off},
+ {10, 0x00, 0, range1, range1Off},
+ {11, 0x80, 1, range1, range1Off},
+ {11, 0xff, 9, range1, range1Off},
+ {11, 0x00, 0, range1, range1Off},
+}
+
+func TestLookupSparse(t *testing.T) {
+ for i, test := range rangeTests {
+ n := trie{sparse: test.table, sparseOffset: test.offsets, cutoff: 10}
+ v := n.lookupValue(test.block, test.lookup)
+ if v != test.result {
+ t.Errorf("LookupSparse:%d: found %X; want %X", i, v, test.result)
+ }
+ }
+}
+
+// Test cases for illegal runes.
+type trietest struct {
+ size int
+ bytes []byte
+}
+
+var tests = []trietest{
+ // illegal runes
+ {1, []byte{0x80}},
+ {1, []byte{0xFF}},
+ {1, []byte{t2, tx - 1}},
+ {1, []byte{t2, t2}},
+ {2, []byte{t3, tx, tx - 1}},
+ {2, []byte{t3, tx, t2}},
+ {1, []byte{t3, tx - 1, tx}},
+ {3, []byte{t4, tx, tx, tx - 1}},
+ {3, []byte{t4, tx, tx, t2}},
+ {1, []byte{t4, t2, tx, tx - 1}},
+ {2, []byte{t4, tx, t2, tx - 1}},
+
+ // short runes
+ {0, []byte{t2}},
+ {0, []byte{t3, tx}},
+ {0, []byte{t4, tx, tx}},
+
+ // we only support UTF-8 up to utf8.UTFMax bytes (4 bytes)
+ {1, []byte{t5, tx, tx, tx, tx}},
+ {1, []byte{t6, tx, tx, tx, tx, tx}},
+}
+
+func mkUTF8(r rune) ([]byte, int) {
+ var b [utf8.UTFMax]byte
+ sz := utf8.EncodeRune(b[:], r)
+ return b[:sz], sz
+}
+
+func TestLookup(t *testing.T) {
+ for i, tt := range testRunes {
+ b, szg := mkUTF8(tt)
+ v, szt := testdata.lookup(b)
+ if int(v) != i {
+ t.Errorf("lookup(%U): found value %#x, expected %#x", tt, v, i)
+ }
+ if szt != szg {
+ t.Errorf("lookup(%U): found size %d, expected %d", tt, szt, szg)
+ }
+ }
+ for i, tt := range tests {
+ v, sz := testdata.lookup(tt.bytes)
+ if v != 0 {
+ t.Errorf("lookup of illegal rune, case %d: found value %#x, expected 0", i, v)
+ }
+ if sz != tt.size {
+ t.Errorf("lookup of illegal rune, case %d: found size %d, expected %d", i, sz, tt.size)
+ }
+ }
+ // Verify defaults.
+ if v, _ := testdata.lookup([]byte{0xC1, 0x8C}); v != 0 {
+ t.Errorf("lookup of non-existing rune should be 0; found %X", v)
+ }
+}
+
+func TestLookupUnsafe(t *testing.T) {
+ for i, tt := range testRunes {
+ b, _ := mkUTF8(tt)
+ v := testdata.lookupUnsafe(b)
+ if int(v) != i {
+ t.Errorf("lookupUnsafe(%U): found value %#x, expected %#x", i, v, i)
+ }
+ }
+}
+
+func TestLookupString(t *testing.T) {
+ for i, tt := range testRunes {
+ b, szg := mkUTF8(tt)
+ v, szt := testdata.lookupString(string(b))
+ if int(v) != i {
+ t.Errorf("lookup(%U): found value %#x, expected %#x", i, v, i)
+ }
+ if szt != szg {
+ t.Errorf("lookup(%U): found size %d, expected %d", i, szt, szg)
+ }
+ }
+ for i, tt := range tests {
+ v, sz := testdata.lookupString(string(tt.bytes))
+ if int(v) != 0 {
+ t.Errorf("lookup of illegal rune, case %d: found value %#x, expected 0", i, v)
+ }
+ if sz != tt.size {
+ t.Errorf("lookup of illegal rune, case %d: found size %d, expected %d", i, sz, tt.size)
+ }
+ }
+}
+
+func TestLookupStringUnsafe(t *testing.T) {
+ for i, tt := range testRunes {
+ b, _ := mkUTF8(tt)
+ v := testdata.lookupStringUnsafe(string(b))
+ if int(v) != i {
+ t.Errorf("lookupUnsafe(%U): found value %#x, expected %#x", i, v, i)
+ }
+ }
+}
diff --git a/src/pkg/exp/norm/triedata_test.go b/src/pkg/exp/norm/triedata_test.go
new file mode 100644
index 000000000..d6c832d46
--- /dev/null
+++ b/src/pkg/exp/norm/triedata_test.go
@@ -0,0 +1,85 @@
+// Generated by running
+// maketesttables
+// DO NOT EDIT
+
+package norm
+
+var testRunes = []int32{1, 12, 127, 128, 256, 2047, 2048, 2457, 65535, 65536, 65793, 1114111, 512, 513, 514, 528, 533}
+
+// testdataValues: 192 entries, 384 bytes
+// Block 2 is the null block.
+var testdataValues = [192]uint16{
+ // Block 0x0, offset 0x0
+ 0x000c: 0x0001,
+ // Block 0x1, offset 0x40
+ 0x007f: 0x0002,
+ // Block 0x2, offset 0x80
+}
+
+// testdataSparseOffset: 10 entries, 20 bytes
+var testdataSparseOffset = []uint16{0x0, 0x2, 0x4, 0x8, 0xa, 0xc, 0xe, 0x10, 0x12, 0x14}
+
+// testdataSparseValues: 22 entries, 88 bytes
+var testdataSparseValues = [22]valueRange{
+ // Block 0x0, offset 0x1
+ {value: 0x0000, lo: 0x01},
+ {value: 0x0003, lo: 0x80, hi: 0x80},
+ // Block 0x1, offset 0x2
+ {value: 0x0000, lo: 0x01},
+ {value: 0x0004, lo: 0x80, hi: 0x80},
+ // Block 0x2, offset 0x3
+ {value: 0x0001, lo: 0x03},
+ {value: 0x000c, lo: 0x80, hi: 0x82},
+ {value: 0x000f, lo: 0x90, hi: 0x90},
+ {value: 0x0010, lo: 0x95, hi: 0x95},
+ // Block 0x3, offset 0x4
+ {value: 0x0000, lo: 0x01},
+ {value: 0x0005, lo: 0xbf, hi: 0xbf},
+ // Block 0x4, offset 0x5
+ {value: 0x0000, lo: 0x01},
+ {value: 0x0006, lo: 0x80, hi: 0x80},
+ // Block 0x5, offset 0x6
+ {value: 0x0000, lo: 0x01},
+ {value: 0x0007, lo: 0x99, hi: 0x99},
+ // Block 0x6, offset 0x7
+ {value: 0x0000, lo: 0x01},
+ {value: 0x0008, lo: 0xbf, hi: 0xbf},
+ // Block 0x7, offset 0x8
+ {value: 0x0000, lo: 0x01},
+ {value: 0x0009, lo: 0x80, hi: 0x80},
+ // Block 0x8, offset 0x9
+ {value: 0x0000, lo: 0x01},
+ {value: 0x000a, lo: 0x81, hi: 0x81},
+ // Block 0x9, offset 0xa
+ {value: 0x0000, lo: 0x01},
+ {value: 0x000b, lo: 0xbf, hi: 0xbf},
+}
+
+// testdataLookup: 640 bytes
+// Block 0 is the null block.
+var testdataLookup = [640]uint8{
+ // Block 0x0, offset 0x0
+ // Block 0x1, offset 0x40
+ // Block 0x2, offset 0x80
+ // Block 0x3, offset 0xc0
+ 0x0c2: 0x01, 0x0c4: 0x02,
+ 0x0c8: 0x03,
+ 0x0df: 0x04,
+ 0x0e0: 0x02,
+ 0x0ef: 0x03,
+ 0x0f0: 0x05, 0x0f4: 0x07,
+ // Block 0x4, offset 0x100
+ 0x120: 0x05, 0x126: 0x06,
+ // Block 0x5, offset 0x140
+ 0x17f: 0x07,
+ // Block 0x6, offset 0x180
+ 0x180: 0x08, 0x184: 0x09,
+ // Block 0x7, offset 0x1c0
+ 0x1d0: 0x04,
+ // Block 0x8, offset 0x200
+ 0x23f: 0x0a,
+ // Block 0x9, offset 0x240
+ 0x24f: 0x06,
+}
+
+var testdataTrie = trie{testdataLookup[:], testdataValues[:], testdataSparseValues[:], testdataSparseOffset[:], 1}
diff --git a/src/pkg/exp/norm/triegen.go b/src/pkg/exp/norm/triegen.go
new file mode 100644
index 000000000..52c88b039
--- /dev/null
+++ b/src/pkg/exp/norm/triegen.go
@@ -0,0 +1,317 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// Trie table generator.
+// Used by make*tables tools to generate a go file with trie data structures
+// for mapping UTF-8 to a 16-bit value. All but the last byte in a UTF-8 byte
+// sequence are used to lookup offsets in the index table to be used for the
+// next byte. The last byte is used to index into a table with 16-bit values.
+
+package main
+
+import (
+ "fmt"
+ "hash/crc32"
+ "log"
+ "unicode/utf8"
+)
+
+const (
+ blockSize = 64
+ blockOffset = 2 // Subtract two blocks to compensate for the 0x80 added to continuation bytes.
+ maxSparseEntries = 16
+)
+
+// Intermediate trie structure
+type trieNode struct {
+ table [256]*trieNode
+ value int
+ b byte
+ leaf bool
+}
+
+func newNode() *trieNode {
+ return new(trieNode)
+}
+
+func (n trieNode) String() string {
+ s := fmt.Sprint("trieNode{table: { non-nil at index: ")
+ for i, v := range n.table {
+ if v != nil {
+ s += fmt.Sprintf("%d, ", i)
+ }
+ }
+ s += fmt.Sprintf("}, value:%#x, b:%#x leaf:%v}", n.value, n.b, n.leaf)
+ return s
+}
+
+func (n trieNode) isInternal() bool {
+ internal := true
+ for i := 0; i < 256; i++ {
+ if nn := n.table[i]; nn != nil {
+ if !internal && !nn.leaf {
+ log.Fatalf("triegen: isInternal: node contains both leaf and non-leaf children (%v)", n)
+ }
+ internal = internal && !nn.leaf
+ }
+ }
+ return internal
+}
+
+func (n trieNode) mostFrequentStride() int {
+ counts := make(map[int]int)
+ v := 0
+ for _, t := range n.table[0x80 : 0x80+blockSize] {
+ if t != nil {
+ if stride := t.value - v; v != 0 && stride >= 0 {
+ counts[stride]++
+ }
+ v = t.value
+ } else {
+ v = 0
+ }
+ }
+ var maxs, maxc int
+ for stride, cnt := range counts {
+ if cnt > maxc || (cnt == maxc && stride < maxs) {
+ maxs, maxc = stride, cnt
+ }
+ }
+ return maxs
+}
+
+func (n trieNode) countSparseEntries() int {
+ stride := n.mostFrequentStride()
+ var count, v int
+ for _, t := range n.table[0x80 : 0x80+blockSize] {
+ tv := 0
+ if t != nil {
+ tv = t.value
+ }
+ if tv-v != stride {
+ if tv != 0 {
+ count++
+ }
+ }
+ v = tv
+ }
+ return count
+}
+
+func (n *trieNode) insert(r rune, value uint16) {
+ var p [utf8.UTFMax]byte
+ sz := utf8.EncodeRune(p[:], r)
+
+ for i := 0; i < sz; i++ {
+ if n.leaf {
+ log.Fatalf("triegen: insert: node (%#v) should not be a leaf", n)
+ }
+ nn := n.table[p[i]]
+ if nn == nil {
+ nn = newNode()
+ nn.b = p[i]
+ n.table[p[i]] = nn
+ }
+ n = nn
+ }
+ n.value = int(value)
+ n.leaf = true
+}
+
+type nodeIndex struct {
+ lookupBlocks []*trieNode
+ valueBlocks []*trieNode
+ sparseBlocks []*trieNode
+ sparseOffset []uint16
+ sparseCount int
+
+ lookupBlockIdx map[uint32]int
+ valueBlockIdx map[uint32]int
+}
+
+func newIndex() *nodeIndex {
+ index := &nodeIndex{}
+ index.lookupBlocks = make([]*trieNode, 0)
+ index.valueBlocks = make([]*trieNode, 0)
+ index.sparseBlocks = make([]*trieNode, 0)
+ index.sparseOffset = make([]uint16, 1)
+ index.lookupBlockIdx = make(map[uint32]int)
+ index.valueBlockIdx = make(map[uint32]int)
+ return index
+}
+
+func computeOffsets(index *nodeIndex, n *trieNode) int {
+ if n.leaf {
+ return n.value
+ }
+ hasher := crc32.New(crc32.MakeTable(crc32.IEEE))
+ // We only index continuation bytes.
+ for i := 0; i < blockSize; i++ {
+ v := 0
+ if nn := n.table[0x80+i]; nn != nil {
+ v = computeOffsets(index, nn)
+ }
+ hasher.Write([]byte{uint8(v >> 8), uint8(v)})
+ }
+ h := hasher.Sum32()
+ if n.isInternal() {
+ v, ok := index.lookupBlockIdx[h]
+ if !ok {
+ v = len(index.lookupBlocks) - blockOffset
+ index.lookupBlocks = append(index.lookupBlocks, n)
+ index.lookupBlockIdx[h] = v
+ }
+ n.value = v
+ } else {
+ v, ok := index.valueBlockIdx[h]
+ if !ok {
+ if c := n.countSparseEntries(); c > maxSparseEntries {
+ v = len(index.valueBlocks) - blockOffset
+ index.valueBlocks = append(index.valueBlocks, n)
+ index.valueBlockIdx[h] = v
+ } else {
+ v = -len(index.sparseOffset)
+ index.sparseBlocks = append(index.sparseBlocks, n)
+ index.sparseOffset = append(index.sparseOffset, uint16(index.sparseCount))
+ index.sparseCount += c + 1
+ index.valueBlockIdx[h] = v
+ }
+ }
+ n.value = v
+ }
+ return n.value
+}
+
+func printValueBlock(nr int, n *trieNode, offset int) {
+ boff := nr * blockSize
+ fmt.Printf("\n// Block %#x, offset %#x", nr, boff)
+ var printnewline bool
+ for i := 0; i < blockSize; i++ {
+ if i%6 == 0 {
+ printnewline = true
+ }
+ v := 0
+ if nn := n.table[i+offset]; nn != nil {
+ v = nn.value
+ }
+ if v != 0 {
+ if printnewline {
+ fmt.Printf("\n")
+ printnewline = false
+ }
+ fmt.Printf("%#04x:%#04x, ", boff+i, v)
+ }
+ }
+}
+
+func printSparseBlock(nr int, n *trieNode) {
+ boff := -n.value
+ fmt.Printf("\n// Block %#x, offset %#x", nr, boff)
+ v := 0
+ //stride := f(n)
+ stride := n.mostFrequentStride()
+ c := n.countSparseEntries()
+ fmt.Printf("\n{value:%#04x,lo:%#02x},", stride, uint8(c))
+ for i, nn := range n.table[0x80 : 0x80+blockSize] {
+ nv := 0
+ if nn != nil {
+ nv = nn.value
+ }
+ if nv-v != stride {
+ if v != 0 {
+ fmt.Printf(",hi:%#02x},", 0x80+i-1)
+ }
+ if nv != 0 {
+ fmt.Printf("\n{value:%#04x,lo:%#02x", nv, nn.b)
+ }
+ }
+ v = nv
+ }
+ if v != 0 {
+ fmt.Printf(",hi:%#02x},", 0x80+blockSize-1)
+ }
+}
+
+func printLookupBlock(nr int, n *trieNode, offset, cutoff int) {
+ boff := nr * blockSize
+ fmt.Printf("\n// Block %#x, offset %#x", nr, boff)
+ var printnewline bool
+ for i := 0; i < blockSize; i++ {
+ if i%8 == 0 {
+ printnewline = true
+ }
+ v := 0
+ if nn := n.table[i+offset]; nn != nil {
+ v = nn.value
+ }
+ if v != 0 {
+ if v < 0 {
+ v = -v - 1 + cutoff
+ }
+ if printnewline {
+ fmt.Printf("\n")
+ printnewline = false
+ }
+ fmt.Printf("%#03x:%#02x, ", boff+i, v)
+ }
+ }
+}
+
+// printTables returns the size in bytes of the generated tables.
+func (t *trieNode) printTables(name string) int {
+ index := newIndex()
+ // Values for 7-bit ASCII are stored in first two block, followed by nil block.
+ index.valueBlocks = append(index.valueBlocks, nil, nil, nil)
+ // First byte of multi-byte UTF-8 codepoints are indexed in 4th block.
+ index.lookupBlocks = append(index.lookupBlocks, nil, nil, nil, nil)
+ // Index starter bytes of multi-byte UTF-8.
+ for i := 0xC0; i < 0x100; i++ {
+ if t.table[i] != nil {
+ computeOffsets(index, t.table[i])
+ }
+ }
+
+ nv := len(index.valueBlocks) * blockSize
+ fmt.Printf("// %sValues: %d entries, %d bytes\n", name, nv, nv*2)
+ fmt.Printf("// Block 2 is the null block.\n")
+ fmt.Printf("var %sValues = [%d]uint16 {", name, nv)
+ printValueBlock(0, t, 0)
+ printValueBlock(1, t, 64)
+ printValueBlock(2, newNode(), 0)
+ for i := 3; i < len(index.valueBlocks); i++ {
+ printValueBlock(i, index.valueBlocks[i], 0x80)
+ }
+ fmt.Print("\n}\n\n")
+
+ ls := len(index.sparseBlocks)
+ fmt.Printf("// %sSparseOffset: %d entries, %d bytes\n", name, ls, ls*2)
+ fmt.Printf("var %sSparseOffset = %#v\n\n", name, index.sparseOffset[1:])
+
+ ns := index.sparseCount
+ fmt.Printf("// %sSparseValues: %d entries, %d bytes\n", name, ns, ns*4)
+ fmt.Printf("var %sSparseValues = [%d]valueRange {", name, ns)
+ for i, n := range index.sparseBlocks {
+ printSparseBlock(i, n)
+ }
+ fmt.Print("\n}\n\n")
+
+ cutoff := len(index.valueBlocks) - blockOffset
+ ni := len(index.lookupBlocks) * blockSize
+ fmt.Printf("// %sLookup: %d bytes\n", name, ni)
+ fmt.Printf("// Block 0 is the null block.\n")
+ fmt.Printf("var %sLookup = [%d]uint8 {", name, ni)
+ printLookupBlock(0, newNode(), 0, cutoff)
+ printLookupBlock(1, newNode(), 0, cutoff)
+ printLookupBlock(2, newNode(), 0, cutoff)
+ printLookupBlock(3, t, 0xC0, cutoff)
+ for i := 4; i < len(index.lookupBlocks); i++ {
+ printLookupBlock(i, index.lookupBlocks[i], 0x80, cutoff)
+ }
+ fmt.Print("\n}\n\n")
+ fmt.Printf("var %sTrie = trie{ %sLookup[:], %sValues[:], %sSparseValues[:], %sSparseOffset[:], %d}\n\n",
+ name, name, name, name, name, cutoff)
+ return nv*2 + ns*4 + ni + ls*2
+}
diff --git a/src/pkg/expvar/expvar_test.go b/src/pkg/expvar/expvar_test.go
index bbd9dd8d6..572c62bee 100644
--- a/src/pkg/expvar/expvar_test.go
+++ b/src/pkg/expvar/expvar_test.go
@@ -18,6 +18,7 @@ func RemoveAll() {
}
func TestInt(t *testing.T) {
+ RemoveAll()
reqs := NewInt("requests")
if reqs.i != 0 {
t.Errorf("reqs.i = %v, want 0", reqs.i)
@@ -43,6 +44,7 @@ func TestInt(t *testing.T) {
}
func TestFloat(t *testing.T) {
+ RemoveAll()
reqs := NewFloat("requests-float")
if reqs.f != 0.0 {
t.Errorf("reqs.f = %v, want 0", reqs.f)
@@ -68,6 +70,7 @@ func TestFloat(t *testing.T) {
}
func TestString(t *testing.T) {
+ RemoveAll()
name := NewString("my-name")
if name.s != "" {
t.Errorf("name.s = %q, want \"\"", name.s)
@@ -84,6 +87,7 @@ func TestString(t *testing.T) {
}
func TestMapCounter(t *testing.T) {
+ RemoveAll()
colors := NewMap("bike-shed-colors")
colors.Add("red", 1)
@@ -123,6 +127,7 @@ func TestMapCounter(t *testing.T) {
}
func TestFunc(t *testing.T) {
+ RemoveAll()
var x interface{} = []string{"a", "b"}
f := Func(func() interface{} { return x })
if s, exp := f.String(), `["a","b"]`; s != exp {
diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go
index 5444ad141..85dd8c3b3 100644
--- a/src/pkg/flag/flag.go
+++ b/src/pkg/flag/flag.go
@@ -33,7 +33,7 @@
After parsing, the arguments after the flag are available as the
slice flag.Args() or individually as flag.Arg(i).
- The arguments are indexed from 0 up to flag.NArg().
+ The arguments are indexed from 0 through flag.NArg()-1.
Command line flag syntax:
-flag
@@ -91,6 +91,15 @@ func (b *boolValue) Set(s string) error {
func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) }
+func (b *boolValue) IsBoolFlag() bool { return true }
+
+// optional interface to indicate boolean flags that can be
+// supplied without "=value" text
+type boolFlag interface {
+ Value
+ IsBoolFlag() bool
+}
+
// -- int Value
type intValue int
@@ -204,6 +213,10 @@ func (d *durationValue) String() string { return (*time.Duration)(d).String() }
// Value is the interface to the dynamic value stored in a flag.
// (The default value is represented as a string.)
+//
+// If a Value has an IsBoolFlag() bool method returning true,
+// the command-line parser makes -name equivalent to -name=true
+// rather than using the next command-line argument.
type Value interface {
String() string
Set(string) error
@@ -704,10 +717,10 @@ func (f *FlagSet) parseOne() (bool, error) {
}
return false, f.failf("flag provided but not defined: -%s", name)
}
- if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg
+ if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg
if has_value {
if err := fv.Set(value); err != nil {
- f.failf("invalid boolean value %q for -%s: %v", value, name, err)
+ return false, f.failf("invalid boolean value %q for -%s: %v", value, name, err)
}
} else {
fv.Set("true")
diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go
index a9561f269..ddd54b277 100644
--- a/src/pkg/flag/flag_test.go
+++ b/src/pkg/flag/flag_test.go
@@ -15,17 +15,6 @@ import (
"time"
)
-var (
- test_bool = Bool("test_bool", false, "bool value")
- test_int = Int("test_int", 0, "int value")
- test_int64 = Int64("test_int64", 0, "int64 value")
- test_uint = Uint("test_uint", 0, "uint value")
- test_uint64 = Uint64("test_uint64", 0, "uint64 value")
- test_string = String("test_string", "0", "string value")
- test_float64 = Float64("test_float64", 0, "float64 value")
- test_duration = Duration("test_duration", 0, "time.Duration value")
-)
-
func boolString(s string) string {
if s == "0" {
return "false"
@@ -34,6 +23,16 @@ func boolString(s string) string {
}
func TestEverything(t *testing.T) {
+ ResetForTesting(nil)
+ Bool("test_bool", false, "bool value")
+ Int("test_int", 0, "int value")
+ Int64("test_int64", 0, "int64 value")
+ Uint("test_uint", 0, "uint value")
+ Uint64("test_uint64", 0, "uint64 value")
+ String("test_string", "0", "string value")
+ Float64("test_float64", 0, "float64 value")
+ Duration("test_duration", 0, "time.Duration value")
+
m := make(map[string]*Flag)
desired := "0"
visitor := func(f *Flag) {
@@ -208,6 +207,47 @@ func TestUserDefined(t *testing.T) {
}
}
+// Declare a user-defined boolean flag type.
+type boolFlagVar struct {
+ count int
+}
+
+func (b *boolFlagVar) String() string {
+ return fmt.Sprintf("%d", b.count)
+}
+
+func (b *boolFlagVar) Set(value string) error {
+ if value == "true" {
+ b.count++
+ }
+ return nil
+}
+
+func (b *boolFlagVar) IsBoolFlag() bool {
+ return b.count < 4
+}
+
+func TestUserDefinedBool(t *testing.T) {
+ var flags FlagSet
+ flags.Init("test", ContinueOnError)
+ var b boolFlagVar
+ var err error
+ flags.Var(&b, "b", "usage")
+ if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil {
+ if b.count < 4 {
+ t.Error(err)
+ }
+ }
+
+ if b.count != 4 {
+ t.Errorf("want: %d; got: %d", 4, b.count)
+ }
+
+ if err == nil {
+ t.Error("expected error; got none")
+ }
+}
+
func TestSetOutput(t *testing.T) {
var flags FlagSet
var buf bytes.Buffer
diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go
index a9b9c9d0c..3cd02d7ed 100644
--- a/src/pkg/fmt/doc.go
+++ b/src/pkg/fmt/doc.go
@@ -31,8 +31,8 @@
%X base 16, with upper-case letters for A-F
%U Unicode format: U+1234; same as "U+%04X"
Floating-point and complex constituents:
- %b decimalless scientific notation with exponent a power of two,
- in the manner of strconv.FormatFloat with the 'b' format,
+ %b decimalless scientific notation with exponent a power of two,
+ in the manner of strconv.FormatFloat with the 'b' format,
e.g. -123456p-78
%e scientific notation, e.g. -1234.456e+78
%E scientific notation, e.g. -1234.456E+78
@@ -56,13 +56,17 @@
character '*', causing their values to be obtained from the next
operand, which must be of type int.
- For numeric values, width sets the width of the field and precision
- sets the number of places after the decimal, if appropriate. For
- example, the format %6.2f prints 123.45.
+ For numeric values, width sets the minimum width of the field and
+ precision sets the number of places after the decimal, if appropriate,
+ except that for %g/%G it sets the total number of digits. For example,
+ given 123.45 the format %6.2f prints 123.45 while %.4g prints 123.5.
+ The default precision for %e and %f is 6; for %g it is the smallest
+ number of digits necessary to identify the value uniquely.
- For strings, width is the minimum number of characters to output,
- padding with spaces if necessary, and precision is the maximum
- number of characters to output, truncating if necessary.
+ For most values, width is the minimum number of characters to output,
+ padding the formatted form with spaces if necessary.
+ For strings, precision is the maximum number of characters to output,
+ truncating if necessary.
Other flags:
+ always print a sign for numeric values;
@@ -70,11 +74,17 @@
- pad with spaces on the right rather than the left (left-justify the field)
# alternate format: add leading 0 for octal (%#o), 0x for hex (%#x);
0X for hex (%#X); suppress 0x for %p (%#p);
- print a raw (backquoted) string if possible for %q (%#q);
+ for %q, print a raw (backquoted) string if strconv.CanBackquote
+ returns true;
write e.g. U+0078 'x' if the character is printable for %U (%#U).
' ' (space) leave a space for elided sign in numbers (% d);
put spaces between bytes printing strings or slices in hex (% x, % X)
- 0 pad with leading zeros rather than spaces
+ 0 pad with leading zeros rather than spaces;
+ for numbers, this moves the padding after the sign
+
+ Flags are ignored by verbs that do not expect them.
+ For example there is no alternate decimal format, so %#d and %d
+ behave identically.
For each Printf-like function, there is also a Print function
that takes no format and is equivalent to saying %v for every
@@ -152,6 +162,7 @@
%T is not implemented
%e %E %f %F %g %G are all equivalent and scan any floating point or complex value
%s and %v on strings scan a space-delimited token
+ Flags # and + are not implemented.
The familiar base-setting prefixes 0 (octal) and 0x
(hexadecimal) are accepted when scanning integers without a
diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go
index de0342967..af4b5c8f8 100644
--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -9,7 +9,6 @@ import (
. "fmt"
"io"
"math"
- "runtime" // for the malloc count test only
"strings"
"testing"
"time"
@@ -96,7 +95,7 @@ type SI struct {
I interface{}
}
-// A type with a String method with pointer receiver for testing %p
+// P is a type with a String method with pointer receiver for testing %p.
type P int
var pValue P
@@ -105,6 +104,9 @@ func (p *P) String() string {
return "String(p)"
}
+var barray = [5]renamedUint8{1, 2, 3, 4, 5}
+var bslice = barray[:]
+
var b byte
var fmttests = []struct {
@@ -127,6 +129,10 @@ var fmttests = []struct {
{"%s", []byte("abc"), "abc"},
{"%x", []byte("abc"), "616263"},
{"% x", []byte("abc\xff"), "61 62 63 ff"},
+ {"%#x", []byte("abc\xff"), "0x610x620x630xff"},
+ {"%#X", []byte("abc\xff"), "0X610X620X630XFF"},
+ {"%# x", []byte("abc\xff"), "0x61 0x62 0x63 0xff"},
+ {"%# X", []byte("abc\xff"), "0X61 0X62 0X63 0XFF"},
{"% X", []byte("abc\xff"), "61 62 63 FF"},
{"%x", []byte("xyz"), "78797a"},
{"%X", []byte("xyz"), "78797A"},
@@ -172,6 +178,8 @@ var fmttests = []struct {
{"%.3q", "日本語日本語", `"日本語"`},
{"%.3q", []byte("日本語日本語"), `"日本語"`},
{"%10.1q", "日本語日本語", ` "日"`},
+ {"%10v", nil, " <nil>"},
+ {"%-10v", nil, "<nil> "},
// integers
{"%d", 12345, "12345"},
@@ -328,14 +336,18 @@ var fmttests = []struct {
// arrays
{"%v", array, "[1 2 3 4 5]"},
{"%v", iarray, "[1 hello 2.5 <nil>]"},
+ {"%v", barray, "[1 2 3 4 5]"},
{"%v", &array, "&[1 2 3 4 5]"},
{"%v", &iarray, "&[1 hello 2.5 <nil>]"},
+ {"%v", &barray, "&[1 2 3 4 5]"},
// slices
{"%v", slice, "[1 2 3 4 5]"},
{"%v", islice, "[1 hello 2.5 <nil>]"},
+ {"%v", bslice, "[1 2 3 4 5]"},
{"%v", &slice, "&[1 2 3 4 5]"},
{"%v", &islice, "&[1 hello 2.5 <nil>]"},
+ {"%v", &bslice, "&[1 2 3 4 5]"},
// complexes with %v
{"%v", 1 + 2i, "(1+2i)"},
@@ -350,10 +362,12 @@ var fmttests = []struct {
{"%+v", B{1, 2}, `{I:<1> j:2}`},
{"%+v", C{1, B{2, 3}}, `{i:1 B:{I:<2> j:3}}`},
- // q on Stringable items
+ // other formats on Stringable items
{"%s", I(23), `<23>`},
{"%q", I(23), `"<23>"`},
{"%x", I(23), `3c32333e`},
+ {"%#x", I(23), `0x3c0x320x330x3e`},
+ {"%# x", I(23), `0x3c 0x32 0x33 0x3e`},
{"%d", I(23), `23`}, // Stringer applies only to string formats.
// go syntax
@@ -375,6 +389,9 @@ var fmttests = []struct {
{"%#v", &iarray, `&[4]interface {}{1, "hello", 2.5, interface {}(nil)}`},
{"%#v", map[int]byte(nil), `map[int]uint8(nil)`},
{"%#v", map[int]byte{}, `map[int]uint8{}`},
+ {"%#v", "foo", `"foo"`},
+ {"%#v", barray, `[5]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`},
+ {"%#v", bslice, `[]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`},
// slices with other formats
{"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`},
@@ -400,6 +417,9 @@ var fmttests = []struct {
{"%x", renamedString("thing"), "7468696e67"},
{"%d", renamedBytes([]byte{1, 2, 15}), `[1 2 15]`},
{"%q", renamedBytes([]byte("hello")), `"hello"`},
+ {"%x", []renamedUint8{'a', 'b', 'c'}, "616263"},
+ {"%s", []renamedUint8{'h', 'e', 'l', 'l', 'o'}, "hello"},
+ {"%q", []renamedUint8{'h', 'e', 'l', 'l', 'o'}, `"hello"`},
{"%v", renamedFloat32(22), "22"},
{"%v", renamedFloat64(33), "33"},
{"%v", renamedComplex64(3 + 4i), "(3+4i)"},
@@ -419,6 +439,8 @@ var fmttests = []struct {
{"%T", renamedComplex128(4 - 3i), "fmt_test.renamedComplex128"},
{"%T", intVal, "int"},
{"%6T", &intVal, " *int"},
+ {"%10T", nil, " <nil>"},
+ {"%-10T", nil, "<nil> "},
// %p
{"p0=%p", new(int), "p0=0xPTR"},
@@ -441,6 +463,11 @@ var fmttests = []struct {
{"%v", (*int)(nil), "<nil>"},
{"%v", new(int), "0xPTR"},
+ // %d etc. pointers use specified base.
+ {"%d", new(int), "PTR_d"},
+ {"%o", new(int), "PTR_o"},
+ {"%x", new(int), "PTR_x"},
+
// %d on Stringer should give integer if possible
{"%s", time.Time{}.Month(), "January"},
{"%d", time.Time{}.Month(), "1"},
@@ -464,20 +491,37 @@ var fmttests = []struct {
// Used to crash because nByte didn't allow for a sign.
{"%b", int64(-1 << 63), "-1000000000000000000000000000000000000000000000000000000000000000"},
+
+ // Complex fmt used to leave the plus flag set for future entries in the array
+ // causing +2+0i and +3+0i instead of 2+0i and 3+0i.
+ {"%v", []complex64{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"},
+ {"%v", []complex128{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"},
}
func TestSprintf(t *testing.T) {
for _, tt := range fmttests {
s := Sprintf(tt.fmt, tt.val)
if i := strings.Index(tt.out, "PTR"); i >= 0 {
+ pattern := "PTR"
+ chars := "0123456789abcdefABCDEF"
+ switch {
+ case strings.HasPrefix(tt.out[i:], "PTR_d"):
+ pattern = "PTR_d"
+ chars = chars[:10]
+ case strings.HasPrefix(tt.out[i:], "PTR_o"):
+ pattern = "PTR_o"
+ chars = chars[:8]
+ case strings.HasPrefix(tt.out[i:], "PTR_x"):
+ pattern = "PTR_x"
+ }
j := i
for ; j < len(s); j++ {
c := s[j]
- if (c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F') {
+ if !strings.ContainsRune(chars, rune(c)) {
break
}
}
- s = s[0:i] + "PTR" + s[j:]
+ s = s[0:i] + pattern + s[j:]
}
if s != tt.out {
if _, ok := tt.val.(string); ok {
@@ -527,6 +571,14 @@ func BenchmarkSprintfFloat(b *testing.B) {
}
}
+func BenchmarkManyArgs(b *testing.B) {
+ var buf bytes.Buffer
+ for i := 0; i < b.N; i++ {
+ buf.Reset()
+ Fprintf(&buf, "%2d/%2d/%2d %d:%d:%d %s %s\n", 3, 4, 5, 11, 12, 13, "hello", "world")
+ }
+}
+
var mallocBuf bytes.Buffer
var mallocTest = []struct {
@@ -550,17 +602,9 @@ var _ bytes.Buffer
func TestCountMallocs(t *testing.T) {
for _, mt := range mallocTest {
- const N = 100
- memstats := new(runtime.MemStats)
- runtime.ReadMemStats(memstats)
- mallocs := 0 - memstats.Mallocs
- for i := 0; i < N; i++ {
- mt.fn()
- }
- runtime.ReadMemStats(memstats)
- mallocs += memstats.Mallocs
- if mallocs/N > uint64(mt.count) {
- t.Errorf("%s: expected %d mallocs, got %d", mt.desc, mt.count, mallocs/N)
+ mallocs := testing.AllocsPerRun(100, mt.fn)
+ if got, max := mallocs, float64(mt.count); got > max {
+ t.Errorf("%s: got %v allocs, want <=%v", mt.desc, got, max)
}
}
}
@@ -636,7 +680,8 @@ func TestStructPrinter(t *testing.T) {
}
}
-// Check map printing using substrings so we don't depend on the print order.
+// presentInMap checks map printing using substrings so we don't depend on the
+// print order.
func presentInMap(s string, a []string, t *testing.T) {
for i := 0; i < len(a); i++ {
loc := strings.Index(s, a[i])
@@ -677,8 +722,8 @@ func TestEmptyMap(t *testing.T) {
}
}
-// Check that Sprint (and hence Print, Fprint) puts spaces in the right places,
-// that is, between arg pairs in which neither is a string.
+// TestBlank checks that Sprint (and hence Print, Fprint) puts spaces in the
+// right places, that is, between arg pairs in which neither is a string.
func TestBlank(t *testing.T) {
got := Sprint("<", 1, ">:", 1, 2, 3, "!")
expect := "<1>:1 2 3!"
@@ -687,8 +732,8 @@ func TestBlank(t *testing.T) {
}
}
-// Check that Sprintln (and hence Println, Fprintln) puts spaces in the right places,
-// that is, between all arg pairs.
+// TestBlankln checks that Sprintln (and hence Println, Fprintln) puts spaces in
+// the right places, that is, between all arg pairs.
func TestBlankln(t *testing.T) {
got := Sprintln("<", 1, ">:", 1, 2, 3, "!")
expect := "< 1 >: 1 2 3 !\n"
@@ -697,7 +742,7 @@ func TestBlankln(t *testing.T) {
}
}
-// Check Formatter with Sprint, Sprintln, Sprintf
+// TestFormatterPrintln checks Formatter with Sprint, Sprintln, Sprintf.
func TestFormatterPrintln(t *testing.T) {
f := F(1)
expect := "<v=F(1)>\n"
@@ -746,7 +791,7 @@ func TestWidthAndPrecision(t *testing.T) {
}
}
-// A type that panics in String.
+// Panic is a type that panics in String.
type Panic struct {
message interface{}
}
@@ -761,7 +806,7 @@ func (p Panic) String() string {
panic(p.message)
}
-// A type that panics in Format.
+// PanicF is a type that panics in Format.
type PanicF struct {
message interface{}
}
@@ -799,7 +844,7 @@ func TestPanics(t *testing.T) {
}
}
-// Test that erroneous String routine doesn't cause fatal recursion.
+// recurCount tests that erroneous String routine doesn't cause fatal recursion.
var recurCount = 0
type Recur struct {
@@ -842,3 +887,15 @@ func TestIsSpace(t *testing.T) {
}
}
}
+
+func TestNilDoesNotBecomeTyped(t *testing.T) {
+ type A struct{}
+ type B struct{}
+ var a *A = nil
+ var b B = B{}
+ got := Sprintf("%s %s %s %s %s", nil, a, nil, b, nil)
+ const expect = "%!s(<nil>) %!s(*fmt_test.A=<nil>) %!s(<nil>) {} %!s(<nil>)"
+ if got != expect {
+ t.Errorf("expected:\n\t%q\ngot:\n\t%q", expect, got)
+ }
+}
diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go
index caf900d5c..5665db12c 100644
--- a/src/pkg/fmt/format.go
+++ b/src/pkg/fmt/format.go
@@ -72,7 +72,7 @@ func (f *fmt) init(buf *buffer) {
f.clearflags()
}
-// Compute left and right padding widths (only one will be non-zero).
+// computePadding computes left and right padding widths (only one will be non-zero).
func (f *fmt) computePadding(width int) (padding []byte, leftWidth, rightWidth int) {
left := !f.minus
w := f.wid
@@ -95,7 +95,7 @@ func (f *fmt) computePadding(width int) (padding []byte, leftWidth, rightWidth i
return
}
-// Generate n bytes of padding.
+// writePadding generates n bytes of padding.
func (f *fmt) writePadding(n int, padding []byte) {
for n > 0 {
m := n
@@ -107,14 +107,13 @@ func (f *fmt) writePadding(n int, padding []byte) {
}
}
-// Append b to f.buf, padded on left (w > 0) or right (w < 0 or f.minus)
-// clear flags afterwards.
+// pad appends b to f.buf, padded on left (w > 0) or right (w < 0 or f.minus).
func (f *fmt) pad(b []byte) {
- var padding []byte
- var left, right int
- if f.widPresent && f.wid != 0 {
- padding, left, right = f.computePadding(len(b))
+ if !f.widPresent || f.wid == 0 {
+ f.buf.Write(b)
+ return
}
+ padding, left, right := f.computePadding(len(b))
if left > 0 {
f.writePadding(left, padding)
}
@@ -124,14 +123,13 @@ func (f *fmt) pad(b []byte) {
}
}
-// append s to buf, padded on left (w > 0) or right (w < 0 or f.minus).
-// clear flags afterwards.
+// padString appends s to buf, padded on left (w > 0) or right (w < 0 or f.minus).
func (f *fmt) padString(s string) {
- var padding []byte
- var left, right int
- if f.widPresent && f.wid != 0 {
- padding, left, right = f.computePadding(utf8.RuneCountInString(s))
+ if !f.widPresent || f.wid == 0 {
+ f.buf.WriteString(s)
+ return
}
+ padding, left, right := f.computePadding(utf8.RuneCountInString(s))
if left > 0 {
f.writePadding(left, padding)
}
@@ -141,17 +139,6 @@ func (f *fmt) padString(s string) {
}
}
-func putint(buf []byte, base, val uint64, digits string) int {
- i := len(buf) - 1
- for val >= base {
- buf[i] = digits[val%base]
- i--
- val /= base
- }
- buf[i] = digits[val]
- return i - 1
-}
-
var (
trueBytes = []byte("true")
falseBytes = []byte("false")
@@ -285,18 +272,41 @@ func (f *fmt) fmt_s(s string) {
f.padString(s)
}
-// fmt_sx formats a string as a hexadecimal encoding of its bytes.
-func (f *fmt) fmt_sx(s, digits string) {
+// fmt_sbx formats a string or byte slice as a hexadecimal encoding of its bytes.
+func (f *fmt) fmt_sbx(s string, b []byte, digits string) {
+ n := len(b)
+ if b == nil {
+ n = len(s)
+ }
+ x := digits[10] - 'a' + 'x'
// TODO: Avoid buffer by pre-padding.
- var b []byte
- for i := 0; i < len(s); i++ {
+ var buf []byte
+ for i := 0; i < n; i++ {
if i > 0 && f.space {
- b = append(b, ' ')
+ buf = append(buf, ' ')
}
- v := s[i]
- b = append(b, digits[v>>4], digits[v&0xF])
+ if f.sharp {
+ buf = append(buf, '0', x)
+ }
+ var c byte
+ if b == nil {
+ c = s[i]
+ } else {
+ c = b[i]
+ }
+ buf = append(buf, digits[c>>4], digits[c&0xF])
}
- f.pad(b)
+ f.pad(buf)
+}
+
+// fmt_sx formats a string as a hexadecimal encoding of its bytes.
+func (f *fmt) fmt_sx(s, digits string) {
+ f.fmt_sbx(s, nil, digits)
+}
+
+// fmt_bx formats a byte slice as a hexadecimal encoding of its bytes.
+func (f *fmt) fmt_bx(b []byte, digits string) {
+ f.fmt_sbx("", b, digits)
}
// fmt_q formats a string as a double-quoted, escaped Go string constant.
@@ -373,7 +383,7 @@ func (f *fmt) fmt_f64(v float64) { f.formatFloat(v, 'f', doPrec(f, 6), 64) }
// fmt_g64 formats a float64 in the 'f' or 'e' form according to size.
func (f *fmt) fmt_g64(v float64) { f.formatFloat(v, 'g', doPrec(f, -1), 64) }
-// fmt_g64 formats a float64 in the 'f' or 'E' form according to size.
+// fmt_G64 formats a float64 in the 'f' or 'E' form according to size.
func (f *fmt) fmt_G64(v float64) { f.formatFloat(v, 'G', doPrec(f, -1), 64) }
// fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2).
@@ -405,6 +415,7 @@ func (f *fmt) fmt_fb32(v float32) { f.formatFloat(float64(v), 'b', 0, 32) }
func (f *fmt) fmt_c64(v complex64, verb rune) {
f.buf.WriteByte('(')
r := real(v)
+ oldPlus := f.plus
for i := 0; ; i++ {
switch verb {
case 'e':
@@ -424,6 +435,7 @@ func (f *fmt) fmt_c64(v complex64, verb rune) {
f.plus = true
r = imag(v)
}
+ f.plus = oldPlus
f.buf.Write(irparenBytes)
}
@@ -431,6 +443,7 @@ func (f *fmt) fmt_c64(v complex64, verb rune) {
func (f *fmt) fmt_c128(v complex128, verb rune) {
f.buf.WriteByte('(')
r := real(v)
+ oldPlus := f.plus
for i := 0; ; i++ {
switch verb {
case 'e':
@@ -450,5 +463,6 @@ func (f *fmt) fmt_c128(v complex128, verb rune) {
f.plus = true
r = imag(v)
}
+ f.plus = oldPlus
f.buf.Write(irparenBytes)
}
diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go
index 13438243c..7d7aa93b2 100644
--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -26,8 +26,8 @@ var (
extraBytes = []byte("%!(EXTRA ")
irparenBytes = []byte("i)")
bytesBytes = []byte("[]byte{")
- widthBytes = []byte("%!(BADWIDTH)")
- precBytes = []byte("%!(BADPREC)")
+ badWidthBytes = []byte("%!(BADWIDTH)")
+ badPrecBytes = []byte("%!(BADPREC)")
noVerbBytes = []byte("%!(NOVERB)")
)
@@ -153,7 +153,7 @@ func newCache(f func() interface{}) *cache {
var ppFree = newCache(func() interface{} { return new(pp) })
-// Allocate a new pp struct or grab a cached one.
+// newPrinter allocates a new pp struct or grab a cached one.
func newPrinter() *pp {
p := ppFree.get().(*pp)
p.panicking = false
@@ -162,7 +162,7 @@ func newPrinter() *pp {
return p
}
-// Save used pp structs in ppFree; avoids an allocation per invocation.
+// free saves used pp structs in ppFree; avoids an allocation per invocation.
func (p *pp) free() {
// Don't hold on to pp structs with large buffers.
if cap(p.buf) > 1024 {
@@ -231,7 +231,7 @@ func Sprintf(format string, a ...interface{}) string {
return s
}
-// Errorf formats according to a format specifier and returns the string
+// Errorf formats according to a format specifier and returns the string
// as a value that satisfies error.
func Errorf(format string, a ...interface{}) error {
return errors.New(Sprintf(format, a...))
@@ -299,7 +299,7 @@ func Sprintln(a ...interface{}) string {
return s
}
-// Get the i'th arg of the struct value.
+// getField gets the i'th arg of the struct value.
// If the arg itself is an interface, return a value for
// the thing inside the interface, not the interface itself.
func getField(v reflect.Value, i int) reflect.Value {
@@ -310,7 +310,7 @@ func getField(v reflect.Value, i int) reflect.Value {
return val
}
-// Convert ASCII to integer. n is 0 (and got is false) if no number present.
+// parsenum converts ASCII to integer. num is 0 (and isnum is false) if no number present.
func parsenum(s string, start, end int) (num int, isnum bool, newi int) {
if start >= end {
return 0, false, end
@@ -545,10 +545,15 @@ func (p *pp) fmtString(v string, verb rune, goSyntax bool) {
}
}
-func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, depth int) {
+func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, typ reflect.Type, depth int) {
if verb == 'v' || verb == 'd' {
if goSyntax {
- p.buf.Write(bytesBytes)
+ if typ == nil {
+ p.buf.Write(bytesBytes)
+ } else {
+ p.buf.WriteString(typ.String())
+ p.buf.WriteByte('{')
+ }
} else {
p.buf.WriteByte('[')
}
@@ -569,24 +574,27 @@ func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, depth int) {
}
return
}
- s := string(v)
switch verb {
case 's':
- p.fmt.fmt_s(s)
+ p.fmt.fmt_s(string(v))
case 'x':
- p.fmt.fmt_sx(s, ldigits)
+ p.fmt.fmt_bx(v, ldigits)
case 'X':
- p.fmt.fmt_sx(s, udigits)
+ p.fmt.fmt_bx(v, udigits)
case 'q':
- p.fmt.fmt_q(s)
+ p.fmt.fmt_q(string(v))
default:
p.badVerb(verb)
}
}
func (p *pp) fmtPointer(value reflect.Value, verb rune, goSyntax bool) {
+ use0x64 := true
switch verb {
- case 'p', 'v', 'b', 'd', 'o', 'x', 'X':
+ case 'p', 'v':
+ // ok
+ case 'b', 'd', 'o', 'x', 'X':
+ use0x64 = false
// ok
default:
p.badVerb(verb)
@@ -616,7 +624,11 @@ func (p *pp) fmtPointer(value reflect.Value, verb rune, goSyntax bool) {
} else if verb == 'v' && u == 0 {
p.buf.Write(nilAngleBytes)
} else {
- p.fmt0x64(uint64(u), !p.fmt.sharp)
+ if use0x64 {
+ p.fmt0x64(uint64(u), !p.fmt.sharp)
+ } else {
+ p.fmtUint64(uint64(u), verb, false)
+ }
}
}
@@ -712,17 +724,18 @@ func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString
}
func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth int) (wasString bool) {
+ p.field = field
+ p.value = reflect.Value{}
+
if field == nil {
if verb == 'T' || verb == 'v' {
- p.buf.Write(nilAngleBytes)
+ p.fmt.pad(nilAngleBytes)
} else {
p.badVerb(verb)
}
return false
}
- p.field = field
- p.value = reflect.Value{}
// Special processing considerations.
// %T (the value's type) and %p (its address) are special; we always do them first.
switch verb {
@@ -734,8 +747,17 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth
return false
}
- if wasString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled {
- return wasString
+ // Clear flags for base formatters.
+ // handleMethods needs them, so we must restore them later.
+ // We could call handleMethods here and avoid this work, but
+ // handleMethods is expensive enough to be worth delaying.
+ oldPlus := p.fmt.plus
+ oldSharp := p.fmt.sharp
+ if plus {
+ p.fmt.plus = false
+ }
+ if goSyntax {
+ p.fmt.sharp = false
}
// Some types can be done without reflection.
@@ -776,9 +798,16 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth
p.fmtString(f, verb, goSyntax)
wasString = verb == 's' || verb == 'v'
case []byte:
- p.fmtBytes(f, verb, goSyntax, depth)
+ p.fmtBytes(f, verb, goSyntax, nil, depth)
wasString = verb == 's'
default:
+ // Restore flags in case handleMethods finds a Formatter.
+ p.fmt.plus = oldPlus
+ p.fmt.sharp = oldSharp
+ // If the type is not simple, it might have methods.
+ if wasString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled {
+ return wasString
+ }
// Need to use reflection
return p.printReflectValue(reflect.ValueOf(field), verb, plus, goSyntax, depth)
}
@@ -915,19 +944,22 @@ BigSwitch:
}
case reflect.Array, reflect.Slice:
// Byte slices are special.
- if f.Type().Elem().Kind() == reflect.Uint8 {
- // We know it's a slice of bytes, but we also know it does not have static type
- // []byte, or it would have been caught above. Therefore we cannot convert
- // it directly in the (slightly) obvious way: f.Interface().([]byte); it doesn't have
- // that type, and we can't write an expression of the right type and do a
- // conversion because we don't have a static way to write the right type.
- // So we build a slice by hand. This is a rare case but it would be nice
- // if reflection could help a little more.
- bytes := make([]byte, f.Len())
- for i := range bytes {
- bytes[i] = byte(f.Index(i).Uint())
+ if typ := f.Type(); typ.Elem().Kind() == reflect.Uint8 {
+ var bytes []byte
+ if f.Kind() == reflect.Slice {
+ bytes = f.Bytes()
+ } else if f.CanAddr() {
+ bytes = f.Slice(0, f.Len()).Bytes()
+ } else {
+ // We have an array, but we cannot Slice() a non-addressable array,
+ // so we build a slice by hand. This is a rare case but it would be nice
+ // if reflection could help a little more.
+ bytes = make([]byte, f.Len())
+ for i := range bytes {
+ bytes[i] = byte(f.Index(i).Uint())
+ }
}
- p.fmtBytes(bytes, verb, goSyntax, depth)
+ p.fmtBytes(bytes, verb, goSyntax, typ, depth)
wasString = verb == 's'
break
}
@@ -1033,7 +1065,7 @@ func (p *pp) doPrintf(format string, a []interface{}) {
if i < end && format[i] == '*' {
p.fmt.wid, p.fmt.widPresent, i, fieldnum = intFromArg(a, end, i, fieldnum)
if !p.fmt.widPresent {
- p.buf.Write(widthBytes)
+ p.buf.Write(badWidthBytes)
}
} else {
p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end)
@@ -1043,7 +1075,7 @@ func (p *pp) doPrintf(format string, a []interface{}) {
if format[i+1] == '*' {
p.fmt.prec, p.fmt.precPresent, i, fieldnum = intFromArg(a, end, i+1, fieldnum)
if !p.fmt.precPresent {
- p.buf.Write(precBytes)
+ p.buf.Write(badPrecBytes)
}
} else {
p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end)
@@ -1099,7 +1131,7 @@ func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) {
prevString := false
for fieldnum := 0; fieldnum < len(a); fieldnum++ {
p.fmt.clearflags()
- // always add spaces if we're doing println
+ // always add spaces if we're doing Println
field := a[fieldnum]
if fieldnum > 0 {
isString := field != nil && reflect.TypeOf(field).Kind() == reflect.String
diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go
index 0b3e04069..bf888c4d8 100644
--- a/src/pkg/fmt/scan.go
+++ b/src/pkg/fmt/scan.go
@@ -33,8 +33,8 @@ type ScanState interface {
ReadRune() (r rune, size int, err error)
// UnreadRune causes the next call to ReadRune to return the same rune.
UnreadRune() error
- // SkipSpace skips space in the input. Newlines are treated as space
- // unless the scan operation is Scanln, Fscanln or Sscanln, in which case
+ // SkipSpace skips space in the input. Newlines are treated as space
+ // unless the scan operation is Scanln, Fscanln or Sscanln, in which case
// a newline is treated as EOF.
SkipSpace()
// Token skips space in the input if skipSpace is true, then returns the
@@ -312,8 +312,9 @@ func notSpace(r rune) bool {
return !isSpace(r)
}
-// skipSpace provides Scan() methods the ability to skip space and newline characters
-// in keeping with the current scanning mode set by format strings and Scan()/Scanln().
+// SkipSpace provides Scan methods the ability to skip space and newline
+// characters in keeping with the current scanning mode set by format strings
+// and Scan/Scanln.
func (s *ss) SkipSpace() {
s.skipSpace(false)
}
@@ -337,7 +338,10 @@ func (r *readRune) readByte() (b byte, err error) {
r.pending--
return
}
- _, err = r.reader.Read(r.pendBuf[0:1])
+ n, err := io.ReadFull(r.reader, r.pendBuf[0:1])
+ if n != 1 {
+ return 0, err
+ }
return r.pendBuf[0], err
}
@@ -378,7 +382,7 @@ func (r *readRune) ReadRune() (rr rune, size int, err error) {
var ssFree = newCache(func() interface{} { return new(ss) })
-// Allocate a new ss struct or grab a cached one.
+// newScanState allocates a new ss struct or grab a cached one.
func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) {
// If the reader is a *ss, then we've got a recursive
// call to Scan, so re-use the scan state.
@@ -410,7 +414,7 @@ func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) {
return
}
-// Save used ss structs in ssFree; avoid an allocation per invocation.
+// free saves used ss structs in ssFree; avoid an allocation per invocation.
func (s *ss) free(old ssave) {
// If it was used recursively, just restore the old state.
if old.validSave {
@@ -1090,7 +1094,8 @@ func (s *ss) advance(format string) (i int) {
// There was space in the format, so there should be space (EOF)
// in the input.
inputc := s.getRune()
- if inputc == eof {
+ if inputc == eof || inputc == '\n' {
+ // If we've reached a newline, stop now; don't read ahead.
return
}
if !isSpace(inputc) {
diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go
index 320857b73..4e2c0feb2 100644
--- a/src/pkg/fmt/scan_test.go
+++ b/src/pkg/fmt/scan_test.go
@@ -626,7 +626,7 @@ func TestScanlnWithMiddleNewline(t *testing.T) {
}
}
-// Special Reader that counts reads at end of file.
+// eofCounter is a special Reader that counts reads at end of file.
type eofCounter struct {
reader *strings.Reader
eofCount int
@@ -640,8 +640,8 @@ func (ec *eofCounter) Read(b []byte) (n int, err error) {
return
}
-// Verify that when we scan, we see at most EOF once per call to a Scan function,
-// and then only when it's really an EOF
+// TestEOF verifies that when we scan, we see at most EOF once per call to a
+// Scan function, and then only when it's really an EOF.
func TestEOF(t *testing.T) {
ec := &eofCounter{strings.NewReader("123\n"), 0}
var a int
@@ -668,7 +668,7 @@ func TestEOF(t *testing.T) {
}
}
-// Verify that we see an EOF error if we run out of input.
+// TestEOFAtEndOfInput verifies that we see an EOF error if we run out of input.
// This was a buglet: we used to get "expected integer".
func TestEOFAtEndOfInput(t *testing.T) {
var i, j int
@@ -730,7 +730,8 @@ func TestEOFAllTypes(t *testing.T) {
}
}
-// Verify that, at least when using bufio, successive calls to Fscan do not lose runes.
+// TestUnreadRuneWithBufio verifies that, at least when using bufio, successive
+// calls to Fscan do not lose runes.
func TestUnreadRuneWithBufio(t *testing.T) {
r := bufio.NewReader(strings.NewReader("123αb"))
var i int
@@ -753,7 +754,7 @@ func TestUnreadRuneWithBufio(t *testing.T) {
type TwoLines string
-// Attempt to read two lines into the object. Scanln should prevent this
+// Scan attempts to read two lines into the object. Scanln should prevent this
// because it stops at newline; Scan and Scanf should be fine.
func (t *TwoLines) Scan(state ScanState, verb rune) error {
chars := make([]rune, 0, 100)
@@ -810,6 +811,34 @@ func TestMultiLine(t *testing.T) {
}
}
+// simpleReader is a strings.Reader that implements only Read, not ReadRune.
+// Good for testing readahead.
+type simpleReader struct {
+ sr *strings.Reader
+}
+
+func (s *simpleReader) Read(b []byte) (n int, err error) {
+ return s.sr.Read(b)
+}
+
+// TestLineByLineFscanf tests that Fscanf does not read past newline. Issue
+// 3481.
+func TestLineByLineFscanf(t *testing.T) {
+ r := &simpleReader{strings.NewReader("1\n2\n")}
+ var i, j int
+ n, err := Fscanf(r, "%v\n", &i)
+ if n != 1 || err != nil {
+ t.Fatalf("first read: %d %q", n, err)
+ }
+ n, err = Fscanf(r, "%v\n", &j)
+ if n != 1 || err != nil {
+ t.Fatalf("second read: %d %q", n, err)
+ }
+ if i != 1 || j != 2 {
+ t.Errorf("wrong values; wanted 1 2 got %d %d", i, j)
+ }
+}
+
// RecursiveInt accepts a string matching %d.%d.%d....
// and parses it into a linked list.
// It allows us to benchmark recursive descent style scanners.
@@ -835,7 +864,7 @@ func (r *RecursiveInt) Scan(state ScanState, verb rune) (err error) {
return
}
-// Perform the same scanning task as RecursiveInt.Scan
+// scanInts performs the same scanning task as RecursiveInt.Scan
// but without recurring through scanner, so we can compare
// performance more directly.
func scanInts(r *RecursiveInt, b *bytes.Buffer) (err error) {
diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go
index d2e75dc1c..bf533d1d2 100644
--- a/src/pkg/go/ast/ast.go
+++ b/src/pkg/go/ast/ast.go
@@ -407,6 +407,7 @@ type (
// A ChanType node represents a channel type.
ChanType struct {
Begin token.Pos // position of "chan" keyword or "<-" (whichever comes first)
+ Arrow token.Pos // position of "<-" (noPos if there is no "<-")
Dir ChanDir // channel direction
Value Expr // value type
}
@@ -554,7 +555,7 @@ type (
// A DeclStmt node represents a declaration in a statement list.
DeclStmt struct {
- Decl Decl
+ Decl Decl // *GenDecl with CONST, TYPE, or VAR token
}
// An EmptyStmt node represents an empty statement.
diff --git a/src/pkg/go/ast/commentmap.go b/src/pkg/go/ast/commentmap.go
new file mode 100644
index 000000000..252d460af
--- /dev/null
+++ b/src/pkg/go/ast/commentmap.go
@@ -0,0 +1,332 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ast
+
+import (
+ "bytes"
+ "fmt"
+ "go/token"
+ "sort"
+)
+
+type byPos []*CommentGroup
+
+func (a byPos) Len() int { return len(a) }
+func (a byPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
+func (a byPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+// sortComments sorts the list of comment groups in source order.
+//
+func sortComments(list []*CommentGroup) {
+ // TODO(gri): Does it make sense to check for sorted-ness
+ // first (because we know that sorted-ness is
+ // very likely)?
+ if orderedList := byPos(list); !sort.IsSorted(orderedList) {
+ sort.Sort(orderedList)
+ }
+}
+
+// A CommentMap maps an AST node to a list of comment groups
+// associated with it. See NewCommentMap for a description of
+// the association.
+//
+type CommentMap map[Node][]*CommentGroup
+
+func (cmap CommentMap) addComment(n Node, c *CommentGroup) {
+ list := cmap[n]
+ if len(list) == 0 {
+ list = []*CommentGroup{c}
+ } else {
+ list = append(list, c)
+ }
+ cmap[n] = list
+}
+
+type byInterval []Node
+
+func (a byInterval) Len() int { return len(a) }
+func (a byInterval) Less(i, j int) bool {
+ pi, pj := a[i].Pos(), a[j].Pos()
+ return pi < pj || pi == pj && a[i].End() > a[j].End()
+}
+func (a byInterval) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+// nodeList returns the list of nodes of the AST n in source order.
+//
+func nodeList(n Node) []Node {
+ var list []Node
+ Inspect(n, func(n Node) bool {
+ // don't collect comments
+ switch n.(type) {
+ case nil, *CommentGroup, *Comment:
+ return false
+ }
+ list = append(list, n)
+ return true
+ })
+ // Note: The current implementation assumes that Inspect traverses the
+ // AST in depth-first and thus _source_ order. If AST traversal
+ // does not follow source order, the sorting call below will be
+ // required.
+ // sort.Sort(byInterval(list))
+ return list
+}
+
+// A commentListReader helps iterating through a list of comment groups.
+//
+type commentListReader struct {
+ fset *token.FileSet
+ list []*CommentGroup
+ index int
+ comment *CommentGroup // comment group at current index
+ pos, end token.Position // source interval of comment group at current index
+}
+
+func (r *commentListReader) eol() bool {
+ return r.index >= len(r.list)
+}
+
+func (r *commentListReader) next() {
+ if !r.eol() {
+ r.comment = r.list[r.index]
+ r.pos = r.fset.Position(r.comment.Pos())
+ r.end = r.fset.Position(r.comment.End())
+ r.index++
+ }
+}
+
+// A nodeStack keeps track of nested nodes.
+// A node lower on the stack lexically contains the nodes higher on the stack.
+//
+type nodeStack []Node
+
+// push pops all nodes that appear lexically before n
+// and then pushes n on the stack.
+//
+func (s *nodeStack) push(n Node) {
+ s.pop(n.Pos())
+ *s = append((*s), n)
+}
+
+// pop pops all nodes that appear lexically before pos
+// (i.e., whose lexical extent has ended before or at pos).
+// It returns the last node popped.
+//
+func (s *nodeStack) pop(pos token.Pos) (top Node) {
+ i := len(*s)
+ for i > 0 && (*s)[i-1].End() <= pos {
+ top = (*s)[i-1]
+ i--
+ }
+ *s = (*s)[0:i]
+ return top
+}
+
+// NewCommentMap creates a new comment map by associating comment groups
+// of the comments list with the nodes of the AST specified by node.
+//
+// A comment group g is associated with a node n if:
+//
+// - g starts on the same line as n ends
+// - g starts on the line immediately following n, and there is
+// at least one empty line after g and before the next node
+// - g starts before n and is not associated to the node before n
+// via the previous rules
+//
+// NewCommentMap tries to associate a comment group to the "largest"
+// node possible: For instance, if the comment is a line comment
+// trailing an assignment, the comment is associated with the entire
+// assignment rather than just the last operand in the assignment.
+//
+func NewCommentMap(fset *token.FileSet, node Node, comments []*CommentGroup) CommentMap {
+ if len(comments) == 0 {
+ return nil // no comments to map
+ }
+
+ cmap := make(CommentMap)
+
+ // set up comment reader r
+ tmp := make([]*CommentGroup, len(comments))
+ copy(tmp, comments) // don't change incomming comments
+ sortComments(tmp)
+ r := commentListReader{fset: fset, list: tmp} // !r.eol() because len(comments) > 0
+ r.next()
+
+ // create node list in lexical order
+ nodes := nodeList(node)
+ nodes = append(nodes, nil) // append sentinel
+
+ // set up iteration variables
+ var (
+ p Node // previous node
+ pend token.Position // end of p
+ pg Node // previous node group (enclosing nodes of "importance")
+ pgend token.Position // end of pg
+ stack nodeStack // stack of node groups
+ )
+
+ for _, q := range nodes {
+ var qpos token.Position
+ if q != nil {
+ qpos = fset.Position(q.Pos()) // current node position
+ } else {
+ // set fake sentinel position to infinity so that
+ // all comments get processed before the sentinel
+ const infinity = 1 << 30
+ qpos.Offset = infinity
+ qpos.Line = infinity
+ }
+
+ // process comments before current node
+ for r.end.Offset <= qpos.Offset {
+ // determine recent node group
+ if top := stack.pop(r.comment.Pos()); top != nil {
+ pg = top
+ pgend = fset.Position(pg.End())
+ }
+ // Try to associate a comment first with a node group
+ // (i.e., a node of "importance" such as a declaration);
+ // if that fails, try to associate it with the most recent
+ // node.
+ // TODO(gri) try to simplify the logic below
+ var assoc Node
+ switch {
+ case pg != nil &&
+ (pgend.Line == r.pos.Line ||
+ pgend.Line+1 == r.pos.Line && r.end.Line+1 < qpos.Line):
+ // 1) comment starts on same line as previous node group ends, or
+ // 2) comment starts on the line immediately after the
+ // previous node group and there is an empty line before
+ // the current node
+ // => associate comment with previous node group
+ assoc = pg
+ case p != nil &&
+ (pend.Line == r.pos.Line ||
+ pend.Line+1 == r.pos.Line && r.end.Line+1 < qpos.Line ||
+ q == nil):
+ // same rules apply as above for p rather than pg,
+ // but also associate with p if we are at the end (q == nil)
+ assoc = p
+ default:
+ // otherwise, associate comment with current node
+ if q == nil {
+ // we can only reach here if there was no p
+ // which would imply that there were no nodes
+ panic("internal error: no comments should be associated with sentinel")
+ }
+ assoc = q
+ }
+ cmap.addComment(assoc, r.comment)
+ if r.eol() {
+ return cmap
+ }
+ r.next()
+ }
+
+ // update previous node
+ p = q
+ pend = fset.Position(p.End())
+
+ // update previous node group if we see an "important" node
+ switch q.(type) {
+ case *File, *Field, Decl, Spec, Stmt:
+ stack.push(q)
+ }
+ }
+
+ return cmap
+}
+
+// Update replaces an old node in the comment map with the new node
+// and returns the new node. Comments that were associated with the
+// old node are associated with the new node.
+//
+func (cmap CommentMap) Update(old, new Node) Node {
+ if list := cmap[old]; len(list) > 0 {
+ delete(cmap, old)
+ cmap[new] = append(cmap[new], list...)
+ }
+ return new
+}
+
+// Filter returns a new comment map consisting of only those
+// entries of cmap for which a corresponding node exists in
+// the AST specified by node.
+//
+func (cmap CommentMap) Filter(node Node) CommentMap {
+ umap := make(CommentMap)
+ Inspect(node, func(n Node) bool {
+ if g := cmap[n]; len(g) > 0 {
+ umap[n] = g
+ }
+ return true
+ })
+ return umap
+}
+
+// Comments returns the list of comment groups in the comment map.
+// The result is sorted is source order.
+//
+func (cmap CommentMap) Comments() []*CommentGroup {
+ list := make([]*CommentGroup, 0, len(cmap))
+ for _, e := range cmap {
+ list = append(list, e...)
+ }
+ sortComments(list)
+ return list
+}
+
+func summary(list []*CommentGroup) string {
+ const maxLen = 40
+ var buf bytes.Buffer
+
+ // collect comments text
+loop:
+ for _, group := range list {
+ // Note: CommentGroup.Text() does too much work for what we
+ // need and would only replace this innermost loop.
+ // Just do it explicitly.
+ for _, comment := range group.List {
+ if buf.Len() >= maxLen {
+ break loop
+ }
+ buf.WriteString(comment.Text)
+ }
+ }
+
+ // truncate if too long
+ if buf.Len() > maxLen {
+ buf.Truncate(maxLen - 3)
+ buf.WriteString("...")
+ }
+
+ // replace any invisibles with blanks
+ bytes := buf.Bytes()
+ for i, b := range bytes {
+ switch b {
+ case '\t', '\n', '\r':
+ bytes[i] = ' '
+ }
+ }
+
+ return string(bytes)
+}
+
+func (cmap CommentMap) String() string {
+ var buf bytes.Buffer
+ fmt.Fprintln(&buf, "CommentMap {")
+ for node, comment := range cmap {
+ // print name of identifiers; print node type for other nodes
+ var s string
+ if ident, ok := node.(*Ident); ok {
+ s = ident.Name
+ } else {
+ s = fmt.Sprintf("%T", node)
+ }
+ fmt.Fprintf(&buf, "\t%p %20s: %s\n", node, s, summary(comment))
+ }
+ fmt.Fprintln(&buf, "}")
+ return buf.String()
+}
diff --git a/src/pkg/go/ast/commentmap_test.go b/src/pkg/go/ast/commentmap_test.go
new file mode 100644
index 000000000..e372eab74
--- /dev/null
+++ b/src/pkg/go/ast/commentmap_test.go
@@ -0,0 +1,143 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// To avoid a cyclic dependency with go/parser, this file is in a separate package.
+
+package ast_test
+
+import (
+ "bytes"
+ "fmt"
+ . "go/ast"
+ "go/parser"
+ "go/token"
+ "sort"
+ "testing"
+)
+
+const src = `
+// the very first comment
+
+// package p
+package p /* the name is p */
+
+// imports
+import (
+ "bytes" // bytes
+ "fmt" // fmt
+ "go/ast"
+ "go/parser"
+)
+
+// T
+type T struct {
+ a, b, c int // associated with a, b, c
+ // associated with x, y
+ x, y float64 // float values
+ z complex128 // complex value
+}
+// also associated with T
+
+// x
+var x = 0 // x = 0
+// also associated with x
+
+// f1
+func f1() {
+ /* associated with s1 */
+ s1()
+ // also associated with s1
+
+ // associated with s2
+
+ // also associated with s2
+ s2() // line comment for s2
+}
+// associated with f1
+// also associated with f1
+
+// associated with f2
+
+// f2
+func f2() {
+}
+
+func f3() {
+ i := 1 /* 1 */ + 2 // addition
+ _ = i
+}
+
+// the very last comment
+`
+
+// res maps a key of the form "line number: node type"
+// to the associated comments' text.
+//
+var res = map[string]string{
+ " 5: *ast.File": "the very first comment\npackage p\n",
+ " 5: *ast.Ident": " the name is p\n",
+ " 8: *ast.GenDecl": "imports\n",
+ " 9: *ast.ImportSpec": "bytes\n",
+ "10: *ast.ImportSpec": "fmt\n",
+ "16: *ast.GenDecl": "T\nalso associated with T\n",
+ "17: *ast.Field": "associated with a, b, c\n",
+ "19: *ast.Field": "associated with x, y\nfloat values\n",
+ "20: *ast.Field": "complex value\n",
+ "25: *ast.GenDecl": "x\nx = 0\nalso associated with x\n",
+ "29: *ast.FuncDecl": "f1\nassociated with f1\nalso associated with f1\n",
+ "31: *ast.ExprStmt": " associated with s1\nalso associated with s1\n",
+ "37: *ast.ExprStmt": "associated with s2\nalso associated with s2\nline comment for s2\n",
+ "45: *ast.FuncDecl": "associated with f2\nf2\n",
+ "49: *ast.AssignStmt": "addition\n",
+ "49: *ast.BasicLit": " 1\n",
+ "50: *ast.Ident": "the very last comment\n",
+}
+
+func ctext(list []*CommentGroup) string {
+ var buf bytes.Buffer
+ for _, g := range list {
+ buf.WriteString(g.Text())
+ }
+ return buf.String()
+}
+
+func TestCommentMap(t *testing.T) {
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "", src, parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+ cmap := NewCommentMap(fset, f, f.Comments)
+
+ // very correct association of comments
+ for n, list := range cmap {
+ key := fmt.Sprintf("%2d: %T", fset.Position(n.Pos()).Line, n)
+ got := ctext(list)
+ want := res[key]
+ if got != want {
+ t.Errorf("%s: got %q; want %q", key, got, want)
+ }
+ }
+
+ // verify that no comments got lost
+ if n := len(cmap.Comments()); n != len(f.Comments) {
+ t.Errorf("got %d comment groups in map; want %d", n, len(f.Comments))
+ }
+
+ // support code to update test:
+ // set genMap to true to generate res map
+ const genMap = false
+ if genMap {
+ out := make([]string, 0, len(cmap))
+ for n, list := range cmap {
+ out = append(out, fmt.Sprintf("\t\"%2d: %T\":\t%q,", fset.Position(n.Pos()).Line, n, ctext(list)))
+ }
+ sort.Strings(out)
+ for _, s := range out {
+ fmt.Println(s)
+ }
+ }
+}
+
+// TODO(gri): add tests for Filter.
diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go
index 4a89b8909..4db5814cb 100644
--- a/src/pkg/go/ast/filter.go
+++ b/src/pkg/go/ast/filter.go
@@ -414,7 +414,7 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
if path := imp.Path.Value; !seen[path] {
// TODO: consider handling cases where:
// - 2 imports exist with the same import path but
- // have different local names (one should probably
+ // have different local names (one should probably
// keep both of them)
// - 2 imports exist but only one has a comment
// - 2 imports exist and they both have (possibly
diff --git a/src/pkg/go/ast/import.go b/src/pkg/go/ast/import.go
index 2d4f69aae..a68a4840f 100644
--- a/src/pkg/go/ast/import.go
+++ b/src/pkg/go/ast/import.go
@@ -20,7 +20,7 @@ func SortImports(fset *token.FileSet, f *File) {
break
}
- if d.Lparen == token.NoPos {
+ if !d.Lparen.IsValid() {
// Not a block: sorted by default.
continue
}
diff --git a/src/pkg/go/ast/print.go b/src/pkg/go/ast/print.go
index 02cf9e022..4a1ce480f 100644
--- a/src/pkg/go/ast/print.go
+++ b/src/pkg/go/ast/print.go
@@ -34,7 +34,8 @@ func NotNilFilter(_ string, v reflect.Value) bool {
//
// A non-nil FieldFilter f may be provided to control the output:
// struct fields for which f(fieldname, fieldvalue) is true are
-// are printed; all others are filtered from the output.
+// are printed; all others are filtered from the output. Unexported
+// struct fields are never printed.
//
func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (err error) {
// setup printer
@@ -107,8 +108,10 @@ func (p *printer) Write(data []byte) (n int, err error) {
}
p.last = b
}
- m, err = p.output.Write(data[n:])
- n += m
+ if len(data) > n {
+ m, err = p.output.Write(data[n:])
+ n += m
+ }
return
}
@@ -145,15 +148,18 @@ func (p *printer) print(x reflect.Value) {
p.print(x.Elem())
case reflect.Map:
- p.printf("%s (len = %d) {\n", x.Type(), x.Len())
- p.indent++
- for _, key := range x.MapKeys() {
- p.print(key)
- p.printf(": ")
- p.print(x.MapIndex(key))
+ p.printf("%s (len = %d) {", x.Type(), x.Len())
+ if x.Len() > 0 {
+ p.indent++
p.printf("\n")
+ for _, key := range x.MapKeys() {
+ p.print(key)
+ p.printf(": ")
+ p.print(x.MapIndex(key))
+ p.printf("\n")
+ }
+ p.indent--
}
- p.indent--
p.printf("}")
case reflect.Ptr:
@@ -169,32 +175,57 @@ func (p *printer) print(x reflect.Value) {
p.print(x.Elem())
}
+ case reflect.Array:
+ p.printf("%s {", x.Type())
+ if x.Len() > 0 {
+ p.indent++
+ p.printf("\n")
+ for i, n := 0, x.Len(); i < n; i++ {
+ p.printf("%d: ", i)
+ p.print(x.Index(i))
+ p.printf("\n")
+ }
+ p.indent--
+ }
+ p.printf("}")
+
case reflect.Slice:
if s, ok := x.Interface().([]byte); ok {
p.printf("%#q", s)
return
}
- p.printf("%s (len = %d) {\n", x.Type(), x.Len())
- p.indent++
- for i, n := 0, x.Len(); i < n; i++ {
- p.printf("%d: ", i)
- p.print(x.Index(i))
+ p.printf("%s (len = %d) {", x.Type(), x.Len())
+ if x.Len() > 0 {
+ p.indent++
p.printf("\n")
+ for i, n := 0, x.Len(); i < n; i++ {
+ p.printf("%d: ", i)
+ p.print(x.Index(i))
+ p.printf("\n")
+ }
+ p.indent--
}
- p.indent--
p.printf("}")
case reflect.Struct:
- p.printf("%s {\n", x.Type())
- p.indent++
t := x.Type()
+ p.printf("%s {", t)
+ p.indent++
+ first := true
for i, n := 0, t.NumField(); i < n; i++ {
- name := t.Field(i).Name
- value := x.Field(i)
- if p.filter == nil || p.filter(name, value) {
- p.printf("%s: ", name)
- p.print(value)
- p.printf("\n")
+ // exclude non-exported fields because their
+ // values cannot be accessed via reflection
+ if name := t.Field(i).Name; IsExported(name) {
+ value := x.Field(i)
+ if p.filter == nil || p.filter(name, value) {
+ if first {
+ p.printf("\n")
+ first = false
+ }
+ p.printf("%s: ", name)
+ p.print(value)
+ p.printf("\n")
+ }
}
}
p.indent--
diff --git a/src/pkg/go/ast/print_test.go b/src/pkg/go/ast/print_test.go
index 71c028e75..210f16430 100644
--- a/src/pkg/go/ast/print_test.go
+++ b/src/pkg/go/ast/print_test.go
@@ -23,6 +23,7 @@ var tests = []struct {
{"foobar", "0 \"foobar\""},
// maps
+ {map[Expr]string{}, `0 map[ast.Expr]string (len = 0) {}`},
{map[string]int{"a": 1},
`0 map[string]int (len = 1) {
1 . "a": 1
@@ -31,7 +32,21 @@ var tests = []struct {
// pointers
{new(int), "0 *0"},
+ // arrays
+ {[0]int{}, `0 [0]int {}`},
+ {[3]int{1, 2, 3},
+ `0 [3]int {
+ 1 . 0: 1
+ 2 . 1: 2
+ 3 . 2: 3
+ 4 }`},
+ {[...]int{42},
+ `0 [1]int {
+ 1 . 0: 42
+ 2 }`},
+
// slices
+ {[]int{}, `0 []int (len = 0) {}`},
{[]int{1, 2, 3},
`0 []int (len = 3) {
1 . 0: 1
@@ -40,6 +55,12 @@ var tests = []struct {
4 }`},
// structs
+ {struct{}{}, `0 struct {} {}`},
+ {struct{ x int }{007}, `0 struct { x int } {}`},
+ {struct{ X, y int }{42, 991},
+ `0 struct { X int; y int } {
+ 1 . X: 42
+ 2 }`},
{struct{ X, Y int }{42, 991},
`0 struct { X int; Y int } {
1 . X: 42
diff --git a/src/pkg/go/ast/resolve.go b/src/pkg/go/ast/resolve.go
index 908e61c5d..0406bfc58 100644
--- a/src/pkg/go/ast/resolve.go
+++ b/src/pkg/go/ast/resolve.go
@@ -57,7 +57,7 @@ func resolve(scope *Scope, ident *Ident) bool {
// An Importer must determine the canonical import path and
// check the map to see if it is already present in the imports map.
// If so, the Importer can return the map entry. Otherwise, the
-// Importer should load the package data for the given path into
+// Importer should load the package data for the given path into
// a new *Object (pkg), record pkg in the imports map, and then
// return pkg.
type Importer func(imports map[string]*Object, path string) (pkg *Object, err error)
@@ -136,7 +136,7 @@ func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer,
for _, obj := range pkg.Data.(*Scope).Objects {
p.declare(fileScope, pkgScope, obj)
}
- } else {
+ } else if name != "_" {
// declare imported package object in file scope
// (do not re-use pkg in the file scope but create
// a new object instead; the Decl field is different
diff --git a/src/pkg/go/ast/scope.go b/src/pkg/go/ast/scope.go
index 11e6b13f1..8df5b2c65 100644
--- a/src/pkg/go/ast/scope.go
+++ b/src/pkg/go/ast/scope.go
@@ -64,18 +64,16 @@ func (s *Scope) String() string {
// ----------------------------------------------------------------------------
// Objects
-// TODO(gri) Consider replacing the Object struct with an interface
-// and a corresponding set of object implementations.
-
// An Object describes a named language entity such as a package,
// constant, type, variable, function (incl. methods), or label.
//
// The Data fields contains object-specific data:
//
-// Kind Data type Data value
-// Pkg *Scope package scope
-// Con int iota for the respective declaration
-// Con != nil constant value
+// Kind Data type Data value
+// Pkg *types.Package package scope
+// Con int iota for the respective declaration
+// Con != nil constant value
+// Typ *Scope (used as method scope during type checking - transient)
//
type Object struct {
Kind ObjKind
@@ -137,7 +135,7 @@ func (obj *Object) Pos() token.Pos {
return token.NoPos
}
-// ObKind describes what an object represents.
+// ObjKind describes what an object represents.
type ObjKind int
// The list of possible Object kinds.
diff --git a/src/pkg/go/ast/walk.go b/src/pkg/go/ast/walk.go
index 181cfd149..fef2503c3 100644
--- a/src/pkg/go/ast/walk.go
+++ b/src/pkg/go/ast/walk.go
@@ -158,7 +158,9 @@ func Walk(v Visitor, node Node) {
Walk(v, n.Fields)
case *FuncType:
- Walk(v, n.Params)
+ if n.Params != nil {
+ Walk(v, n.Params)
+ }
if n.Results != nil {
Walk(v, n.Results)
}
@@ -344,9 +346,6 @@ func Walk(v Visitor, node Node) {
}
Walk(v, n.Name)
walkDeclList(v, n.Decls)
- for _, g := range n.Comments {
- Walk(v, g)
- }
// don't walk n.Comments - they have been
// visited already through the individual
// nodes
diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go
index 7a81d5030..16c3da458 100644
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -33,6 +33,7 @@ type Context struct {
GOPATH string // Go path
CgoEnabled bool // whether cgo can be used
BuildTags []string // additional tags to recognize in +build lines
+ InstallTag string // package install directory suffix
UseAllFiles bool // use files regardless of +build lines, file names
Compiler string // compiler to assume when computing target paths
@@ -116,12 +117,27 @@ func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) {
return f(root, dir)
}
- if p, err := filepath.EvalSymlinks(root); err == nil {
- root = p
+ // Try using paths we received.
+ if rel, ok = hasSubdir(root, dir); ok {
+ return
}
- if p, err := filepath.EvalSymlinks(dir); err == nil {
- dir = p
+
+ // Try expanding symlinks and comparing
+ // expanded against unexpanded and
+ // expanded against expanded.
+ rootSym, _ := filepath.EvalSymlinks(root)
+ dirSym, _ := filepath.EvalSymlinks(dir)
+
+ if rel, ok = hasSubdir(rootSym, dir); ok {
+ return
+ }
+ if rel, ok = hasSubdir(root, dirSym); ok {
+ return
}
+ return hasSubdir(rootSym, dirSym)
+}
+
+func hasSubdir(root, dir string) (rel string, ok bool) {
const sep = string(filepath.Separator)
root = filepath.Clean(root)
if !strings.HasSuffix(root, sep) {
@@ -180,6 +196,21 @@ func (ctxt *Context) gopath() []string {
// Do not get confused by this common mistake.
continue
}
+ if strings.Contains(p, "~") && runtime.GOOS != "windows" {
+ // Path segments containing ~ on Unix are almost always
+ // users who have incorrectly quoted ~ while setting GOPATH,
+ // preventing it from expanding to $HOME.
+ // The situation is made more confusing by the fact that
+ // bash allows quoted ~ in $PATH (most shells do not).
+ // Do not get confused by this, and do not try to use the path.
+ // It does not exist, and printing errors about it confuses
+ // those users even more, because they think "sure ~ exists!".
+ // The go command diagnoses this situation and prints a
+ // useful error.
+ // On Windows, ~ is used in short names, such as c:\progra~1
+ // for c:\program files.
+ continue
+ }
all = append(all, p)
}
return all
@@ -213,10 +244,16 @@ var Default Context = defaultContext()
var cgoEnabled = map[string]bool{
"darwin/386": true,
"darwin/amd64": true,
- "linux/386": true,
- "linux/amd64": true,
"freebsd/386": true,
"freebsd/amd64": true,
+ "linux/386": true,
+ "linux/amd64": true,
+ "linux/arm": true,
+ "netbsd/386": true,
+ "netbsd/amd64": true,
+ "netbsd/arm": true,
+ "openbsd/386": true,
+ "openbsd/amd64": true,
"windows/386": true,
"windows/amd64": true,
}
@@ -278,12 +315,15 @@ type Package struct {
PkgObj string // installed .a file
// Source files
- GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
- CgoFiles []string // .go source files that import "C"
- CFiles []string // .c source files
- HFiles []string // .h source files
- SFiles []string // .s source files
- SysoFiles []string // .syso system object files to add to archive
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string // .go source files that import "C"
+ IgnoredGoFiles []string // .go source files ignored for this build
+ CFiles []string // .c source files
+ HFiles []string // .h source files
+ SFiles []string // .s source files
+ SysoFiles []string // .syso system object files to add to archive
+ SwigFiles []string // .swig files
+ SwigCXXFiles []string // .swigcxx files
// Cgo directives
CgoPkgConfig []string // Cgo pkg-config directives
@@ -346,6 +386,9 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
p := &Package{
ImportPath: path,
}
+ if path == "" {
+ return p, fmt.Errorf("import %q: invalid import path", path)
+ }
var pkga string
var pkgerr error
@@ -354,7 +397,11 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
dir, elem := pathpkg.Split(p.ImportPath)
pkga = "pkg/gccgo/" + dir + "lib" + elem + ".a"
case "gc":
- pkga = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + "/" + p.ImportPath + ".a"
+ tag := ""
+ if ctxt.InstallTag != "" {
+ tag = "_" + ctxt.InstallTag
+ }
+ pkga = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + tag + "/" + p.ImportPath + ".a"
default:
// Save error for end of function.
pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler)
@@ -410,6 +457,13 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
if strings.HasPrefix(path, "/") {
return p, fmt.Errorf("import %q: cannot import absolute path", path)
}
+
+ // tried records the location of unsucsessful package lookups
+ var tried struct {
+ goroot string
+ gopath []string
+ }
+
// Determine directory from import path.
if ctxt.GOROOT != "" {
dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", path)
@@ -421,6 +475,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
p.Root = ctxt.GOROOT
goto Found
}
+ tried.goroot = dir
}
for _, root := range ctxt.gopath() {
dir := ctxt.joinPath(root, "src", path)
@@ -431,8 +486,28 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
p.Root = root
goto Found
}
+ tried.gopath = append(tried.gopath, dir)
+ }
+
+ // package was not found
+ var paths []string
+ if tried.goroot != "" {
+ paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot))
+ } else {
+ paths = append(paths, "\t($GOROOT not set)")
}
- return p, fmt.Errorf("import %q: cannot find package", path)
+ var i int
+ var format = "\t%s (from $GOPATH)"
+ for ; i < len(tried.gopath); i++ {
+ if i > 0 {
+ format = "\t%s"
+ }
+ paths = append(paths, fmt.Sprintf(format, tried.gopath[i]))
+ }
+ if i == 0 {
+ paths = append(paths, "\t($GOPATH not set)")
+ }
+ return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n"))
}
Found:
@@ -476,17 +551,22 @@ Found:
strings.HasPrefix(name, ".") {
continue
}
- if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
- continue
- }
i := strings.LastIndex(name, ".")
if i < 0 {
i = len(name)
}
ext := name[i:]
+
+ if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) {
+ if ext == ".go" {
+ p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
+ }
+ continue
+ }
+
switch ext {
- case ".go", ".c", ".s", ".h", ".S":
+ case ".go", ".c", ".s", ".h", ".S", ".swig", ".swigcxx":
// tentatively okay - read to make sure
case ".syso":
// binary objects to add to package archive
@@ -504,7 +584,13 @@ Found:
if err != nil {
return p, err
}
- data, err := ioutil.ReadAll(f)
+
+ var data []byte
+ if strings.HasSuffix(filename, ".go") {
+ data, err = readImports(f, false)
+ } else {
+ data, err = readComments(f)
+ }
f.Close()
if err != nil {
return p, fmt.Errorf("read %s: %v", filename, err)
@@ -512,6 +598,9 @@ Found:
// Look for +build comments to accept or reject the file.
if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) {
+ if ext == ".go" {
+ p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
+ }
continue
}
@@ -529,6 +618,12 @@ Found:
case ".S":
Sfiles = append(Sfiles, name)
continue
+ case ".swig":
+ p.SwigFiles = append(p.SwigFiles, name)
+ continue
+ case ".swigcxx":
+ p.SwigCXXFiles = append(p.SwigCXXFiles, name)
+ continue
}
pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments)
@@ -536,8 +631,9 @@ Found:
return p, err
}
- pkg := string(pf.Name.Name)
+ pkg := pf.Name.Name
if pkg == "documentation" {
+ p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
continue
}
@@ -570,7 +666,7 @@ Found:
if !ok {
continue
}
- quoted := string(spec.Path.Value)
+ quoted := spec.Path.Value
path, err := strconv.Unquote(quoted)
if err != nil {
log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted)
@@ -678,7 +774,7 @@ func (ctxt *Context) shouldBuild(content []byte) bool {
}
line = bytes.TrimSpace(line)
if len(line) == 0 { // Blank line
- end = cap(content) - cap(line) // &line[0] - &content[0]
+ end = len(content) - len(p)
continue
}
if !bytes.HasPrefix(line, slashslash) { // Not comment line
@@ -872,6 +968,8 @@ func splitQuoted(s string) (r []string, err error) {
// $GOARCH
// cgo (if cgo is enabled)
// !cgo (if cgo is disabled)
+// ctxt.Compiler
+// !ctxt.Compiler
// tag (if tag is listed in ctxt.BuildTags)
// !tag (if tag is not listed in ctxt.BuildTags)
// a comma-separated list of any of these
@@ -903,7 +1001,7 @@ func (ctxt *Context) match(name string) bool {
if ctxt.CgoEnabled && name == "cgo" {
return true
}
- if name == ctxt.GOOS || name == ctxt.GOARCH {
+ if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler {
return true
}
diff --git a/src/pkg/go/build/build_test.go b/src/pkg/go/build/build_test.go
index 560ebad5c..d8cf98840 100644
--- a/src/pkg/go/build/build_test.go
+++ b/src/pkg/go/build/build_test.go
@@ -61,6 +61,19 @@ func TestDotSlashImport(t *testing.T) {
}
}
+func TestEmptyImport(t *testing.T) {
+ p, err := Import("", Default.GOROOT, FindOnly)
+ if err == nil {
+ t.Fatal(`Import("") returned nil error.`)
+ }
+ if p == nil {
+ t.Fatal(`Import("") returned nil package.`)
+ }
+ if p.ImportPath != "" {
+ t.Fatalf("ImportPath=%q, want %q.", p.ImportPath, "")
+ }
+}
+
func TestLocalDirectory(t *testing.T) {
cwd, err := os.Getwd()
if err != nil {
@@ -75,3 +88,32 @@ func TestLocalDirectory(t *testing.T) {
t.Fatalf("ImportPath=%q, want %q", p.ImportPath, "go/build")
}
}
+
+func TestShouldBuild(t *testing.T) {
+ const file1 = "// +build tag1\n\n" +
+ "package main\n"
+
+ const file2 = "// +build cgo\n\n" +
+ "// This package implements parsing of tags like\n" +
+ "// +build tag1\n" +
+ "package build"
+
+ const file3 = "// Copyright The Go Authors.\n\n" +
+ "package build\n\n" +
+ "// shouldBuild checks tags given by lines of the form\n" +
+ "// +build tag\n" +
+ "func shouldBuild(content []byte)\n"
+
+ ctx := &Context{BuildTags: []string{"tag1"}}
+ if !ctx.shouldBuild([]byte(file1)) {
+ t.Errorf("should not build file1, expected the contrary")
+ }
+ if ctx.shouldBuild([]byte(file2)) {
+ t.Errorf("should build file2, expected the contrary")
+ }
+
+ ctx = &Context{BuildTags: nil}
+ if !ctx.shouldBuild([]byte(file3)) {
+ t.Errorf("should not build file3, expected the contrary")
+ }
+}
diff --git a/src/pkg/go/build/deps_test.go b/src/pkg/go/build/deps_test.go
index 4e9f32a03..9a715ba60 100644
--- a/src/pkg/go/build/deps_test.go
+++ b/src/pkg/go/build/deps_test.go
@@ -5,10 +5,9 @@
// This file exercises the import parser but also checks that
// some low-level packages do not have new dependencies added.
-package build_test
+package build
import (
- "go/build"
"sort"
"testing"
)
@@ -24,13 +23,13 @@ import (
// be used as dependencies by other rules.
//
// DO NOT CHANGE THIS DATA TO FIX BUILDS.
-//
+//
var pkgDeps = map[string][]string{
// L0 is the lowest level, core, nearly unavoidable packages.
"errors": {},
"io": {"errors", "sync"},
"runtime": {"unsafe"},
- "sync": {"sync/atomic"},
+ "sync": {"sync/atomic", "unsafe"},
"sync/atomic": {"unsafe"},
"unsafe": {},
@@ -142,7 +141,7 @@ var pkgDeps = map[string][]string{
// Packages used by testing must be low-level (L2+fmt).
"regexp": {"L2", "regexp/syntax"},
"regexp/syntax": {"L2"},
- "runtime/debug": {"L2", "fmt", "io/ioutil", "os"},
+ "runtime/debug": {"L2", "fmt", "io/ioutil", "os", "time"},
"runtime/pprof": {"L2", "fmt", "text/tabwriter"},
"text/tabwriter": {"L2"},
@@ -177,7 +176,7 @@ var pkgDeps = map[string][]string{
},
// One of a kind.
- "archive/tar": {"L4", "OS"},
+ "archive/tar": {"L4", "OS", "syscall"},
"archive/zip": {"L4", "OS", "compress/flate"},
"compress/bzip2": {"L4"},
"compress/flate": {"L4"},
@@ -249,18 +248,23 @@ var pkgDeps = map[string][]string{
"net/mail": {"L4", "NET", "OS"},
"net/textproto": {"L4", "OS", "net"},
+ // Support libraries for crypto that aren't L2.
+ "CRYPTO-SUPPORT": {
+ "crypto/subtle",
+ },
+
// Core crypto.
"crypto/aes": {"L3"},
"crypto/des": {"L3"},
- "crypto/hmac": {"L3"},
+ "crypto/hmac": {"L3", "CRYPTO-SUPPORT"},
"crypto/md5": {"L3"},
"crypto/rc4": {"L3"},
"crypto/sha1": {"L3"},
"crypto/sha256": {"L3"},
"crypto/sha512": {"L3"},
- "crypto/subtle": {"L3"},
"CRYPTO": {
+ "CRYPTO-SUPPORT",
"crypto/aes",
"crypto/des",
"crypto/hmac",
@@ -269,7 +273,6 @@ var pkgDeps = map[string][]string{
"crypto/sha1",
"crypto/sha256",
"crypto/sha512",
- "crypto/subtle",
},
// Random byte, number generation.
@@ -300,7 +303,10 @@ var pkgDeps = map[string][]string{
"L4", "CRYPTO-MATH", "CGO", "OS",
"crypto/x509", "encoding/pem", "net", "syscall",
},
- "crypto/x509": {"L4", "CRYPTO-MATH", "OS", "CGO", "crypto/x509/pkix", "encoding/pem", "syscall"},
+ "crypto/x509": {
+ "L4", "CRYPTO-MATH", "OS", "CGO",
+ "crypto/x509/pkix", "encoding/pem", "encoding/hex", "net", "syscall",
+ },
"crypto/x509/pkix": {"L4", "CRYPTO-MATH"},
// Simple net+crypto-aware packages.
@@ -375,7 +381,7 @@ func TestDependencies(t *testing.T) {
}
sort.Strings(all)
- ctxt := build.Default
+ ctxt := Default
test := func(mustImport bool) {
for _, pkg := range all {
if isMacro(pkg) {
diff --git a/src/pkg/go/build/doc.go b/src/pkg/go/build/doc.go
index 67c26ac7f..c562d05d0 100644
--- a/src/pkg/go/build/doc.go
+++ b/src/pkg/go/build/doc.go
@@ -23,12 +23,12 @@
// As in the Go tree, each target operating system and
// architecture pair has its own subdirectory of pkg
// (pkg/GOOS_GOARCH).
-//
+//
// If DIR is a directory listed in the Go path, a package with
// source in DIR/src/foo/bar can be imported as "foo/bar" and
// has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a"
// (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a").
-//
+//
// The bin/ directory holds compiled commands.
// Each command is named for its source directory, but only
// using the final element, not the entire path. That is, the
@@ -36,11 +36,11 @@
// DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped
// so that you can add DIR/bin to your PATH to get at the
// installed commands.
-//
+//
// Here's an example directory layout:
-//
+//
// GOPATH=/home/user/gocode
-//
+//
// /home/user/gocode/
// src/
// foo/
@@ -60,9 +60,12 @@
// A build constraint is a line comment beginning with the directive +build
// that lists the conditions under which a file should be included in the package.
// Constraints may appear in any kind of source file (not just Go), but
-// they must be appear near the top of the file, preceded
+// they must appear near the top of the file, preceded
// only by blank lines and other line comments.
//
+// To distinguish build constraints from package documentation, a series of
+// build constraints must be followed by a blank line.
+//
// A build constraint is evaluated as the OR of space-separated options;
// each option evaluates as the AND of its comma-separated terms;
// and each term is an alphanumeric word or, preceded by !, its negation.
@@ -74,10 +77,21 @@
//
// (linux AND 386) OR (darwin AND (NOT cgo))
//
+// A file may have multiple build constraints. The overall constraint is the AND
+// of the individual constraints. That is, the build constraints:
+//
+// // +build linux darwin
+// // +build 386
+//
+// corresponds to the boolean formula:
+//
+// (linux OR darwin) AND 386
+//
// During a particular build, the following words are satisfied:
//
// - the target operating system, as spelled by runtime.GOOS
// - the target architecture, as spelled by runtime.GOARCH
+// - the compiler being used, currently either "gc" or "gccgo"
// - "cgo", if ctxt.CgoEnabled is true
// - any additional words listed in ctxt.BuildTags
//
@@ -100,7 +114,7 @@
// default functionality for other systems, which in this case would
// carry the constraint:
//
-// // +build !linux !darwin !cgo
+// // +build !linux,!darwin !cgo
//
// Naming a file dns_windows.go will cause it to be included only when
// building the package for Windows; similarly, math_386.s will be included
diff --git a/src/pkg/go/build/read.go b/src/pkg/go/build/read.go
new file mode 100644
index 000000000..c8079dfd1
--- /dev/null
+++ b/src/pkg/go/build/read.go
@@ -0,0 +1,238 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package build
+
+import (
+ "bufio"
+ "errors"
+ "io"
+)
+
+type importReader struct {
+ b *bufio.Reader
+ buf []byte
+ peek byte
+ err error
+ eof bool
+ nerr int
+}
+
+func isIdent(c byte) bool {
+ return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= 0x80
+}
+
+var (
+ errSyntax = errors.New("syntax error")
+ errNUL = errors.New("unexpected NUL in input")
+)
+
+// syntaxError records a syntax error, but only if an I/O error has not already been recorded.
+func (r *importReader) syntaxError() {
+ if r.err == nil {
+ r.err = errSyntax
+ }
+}
+
+// readByte reads the next byte from the input, saves it in buf, and returns it.
+// If an error occurs, readByte records the error in r.err and returns 0.
+func (r *importReader) readByte() byte {
+ c, err := r.b.ReadByte()
+ if err == nil {
+ r.buf = append(r.buf, c)
+ if c == 0 {
+ err = errNUL
+ }
+ }
+ if err != nil {
+ if err == io.EOF {
+ r.eof = true
+ } else if r.err == nil {
+ r.err = err
+ }
+ c = 0
+ }
+ return c
+}
+
+// peekByte returns the next byte from the input reader but does not advance beyond it.
+// If skipSpace is set, peekByte skips leading spaces and comments.
+func (r *importReader) peekByte(skipSpace bool) byte {
+ if r.err != nil {
+ if r.nerr++; r.nerr > 10000 {
+ panic("go/build: import reader looping")
+ }
+ return 0
+ }
+
+ // Use r.peek as first input byte.
+ // Don't just return r.peek here: it might have been left by peekByte(false)
+ // and this might be peekByte(true).
+ c := r.peek
+ if c == 0 {
+ c = r.readByte()
+ }
+ for r.err == nil && !r.eof {
+ if skipSpace {
+ // For the purposes of this reader, semicolons are never necessary to
+ // understand the input and are treated as spaces.
+ switch c {
+ case ' ', '\f', '\t', '\r', '\n', ';':
+ c = r.readByte()
+ continue
+
+ case '/':
+ c = r.readByte()
+ if c == '/' {
+ for c != '\n' && r.err == nil && !r.eof {
+ c = r.readByte()
+ }
+ } else if c == '*' {
+ var c1 byte
+ for (c != '*' || c1 != '/') && r.err == nil {
+ if r.eof {
+ r.syntaxError()
+ }
+ c, c1 = c1, r.readByte()
+ }
+ } else {
+ r.syntaxError()
+ }
+ c = r.readByte()
+ continue
+ }
+ }
+ break
+ }
+ r.peek = c
+ return r.peek
+}
+
+// nextByte is like peekByte but advances beyond the returned byte.
+func (r *importReader) nextByte(skipSpace bool) byte {
+ c := r.peekByte(skipSpace)
+ r.peek = 0
+ return c
+}
+
+// readKeyword reads the given keyword from the input.
+// If the keyword is not present, readKeyword records a syntax error.
+func (r *importReader) readKeyword(kw string) {
+ r.peekByte(true)
+ for i := 0; i < len(kw); i++ {
+ if r.nextByte(false) != kw[i] {
+ r.syntaxError()
+ return
+ }
+ }
+ if isIdent(r.peekByte(false)) {
+ r.syntaxError()
+ }
+}
+
+// readIdent reads an identifier from the input.
+// If an identifier is not present, readIdent records a syntax error.
+func (r *importReader) readIdent() {
+ c := r.peekByte(true)
+ if !isIdent(c) {
+ r.syntaxError()
+ return
+ }
+ for isIdent(r.peekByte(false)) {
+ r.peek = 0
+ }
+}
+
+// readString reads a quoted string literal from the input.
+// If an identifier is not present, readString records a syntax error.
+func (r *importReader) readString() {
+ switch r.nextByte(true) {
+ case '`':
+ for r.err == nil {
+ if r.nextByte(false) == '`' {
+ break
+ }
+ if r.eof {
+ r.syntaxError()
+ }
+ }
+ case '"':
+ for r.err == nil {
+ c := r.nextByte(false)
+ if c == '"' {
+ break
+ }
+ if r.eof || c == '\n' {
+ r.syntaxError()
+ }
+ if c == '\\' {
+ r.nextByte(false)
+ }
+ }
+ default:
+ r.syntaxError()
+ }
+}
+
+// readImport reads an import clause - optional identifier followed by quoted string -
+// from the input.
+func (r *importReader) readImport() {
+ c := r.peekByte(true)
+ if c == '.' {
+ r.peek = 0
+ } else if isIdent(c) {
+ r.readIdent()
+ }
+ r.readString()
+}
+
+// readComments is like ioutil.ReadAll, except that it only reads the leading
+// block of comments in the file.
+func readComments(f io.Reader) ([]byte, error) {
+ r := &importReader{b: bufio.NewReader(f)}
+ r.peekByte(true)
+ if r.err == nil && !r.eof {
+ // Didn't reach EOF, so must have found a non-space byte. Remove it.
+ r.buf = r.buf[:len(r.buf)-1]
+ }
+ return r.buf, r.err
+}
+
+// readImports is like ioutil.ReadAll, except that it expects a Go file as input
+// and stops reading the input once the imports have completed.
+func readImports(f io.Reader, reportSyntaxError bool) ([]byte, error) {
+ r := &importReader{b: bufio.NewReader(f)}
+
+ r.readKeyword("package")
+ r.readIdent()
+ for r.peekByte(true) == 'i' {
+ r.readKeyword("import")
+ if r.peekByte(true) == '(' {
+ r.nextByte(false)
+ for r.peekByte(true) != ')' && r.err == nil {
+ r.readImport()
+ }
+ r.nextByte(false)
+ } else {
+ r.readImport()
+ }
+ }
+
+ // If we stopped successfully before EOF, we read a byte that told us we were done.
+ // Return all but that last byte, which would cause a syntax error if we let it through.
+ if r.err == nil && !r.eof {
+ return r.buf[:len(r.buf)-1], nil
+ }
+
+ // If we stopped for a syntax error, consume the whole file so that
+ // we are sure we don't change the errors that go/parser returns.
+ if r.err == errSyntax && !reportSyntaxError {
+ r.err = nil
+ for r.err == nil && !r.eof {
+ r.readByte()
+ }
+ }
+
+ return r.buf, r.err
+}
diff --git a/src/pkg/go/build/read_test.go b/src/pkg/go/build/read_test.go
new file mode 100644
index 000000000..2dcc1208f
--- /dev/null
+++ b/src/pkg/go/build/read_test.go
@@ -0,0 +1,226 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package build
+
+import (
+ "io"
+ "strings"
+ "testing"
+)
+
+const quote = "`"
+
+type readTest struct {
+ // Test input contains ℙ where readImports should stop.
+ in string
+ err string
+}
+
+var readImportsTests = []readTest{
+ {
+ `package p`,
+ "",
+ },
+ {
+ `package p; import "x"`,
+ "",
+ },
+ {
+ `package p; import . "x"`,
+ "",
+ },
+ {
+ `package p; import "x";ℙvar x = 1`,
+ "",
+ },
+ {
+ `package p
+
+ // comment
+
+ import "x"
+ import _ "x"
+ import a "x"
+
+ /* comment */
+
+ import (
+ "x" /* comment */
+ _ "x"
+ a "x" // comment
+ ` + quote + `x` + quote + `
+ _ /*comment*/ ` + quote + `x` + quote + `
+ a ` + quote + `x` + quote + `
+ )
+ import (
+ )
+ import ()
+ import()import()import()
+ import();import();import()
+
+ ℙvar x = 1
+ `,
+ "",
+ },
+}
+
+var readCommentsTests = []readTest{
+ {
+ `ℙpackage p`,
+ "",
+ },
+ {
+ `ℙpackage p; import "x"`,
+ "",
+ },
+ {
+ `ℙpackage p; import . "x"`,
+ "",
+ },
+ {
+ `// foo
+
+ /* bar */
+
+ /* quux */ // baz
+
+ /*/ zot */
+
+ // asdf
+ ℙHello, world`,
+ "",
+ },
+}
+
+func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) {
+ for i, tt := range tests {
+ var in, testOut string
+ j := strings.Index(tt.in, "ℙ")
+ if j < 0 {
+ in = tt.in
+ testOut = tt.in
+ } else {
+ in = tt.in[:j] + tt.in[j+len("ℙ"):]
+ testOut = tt.in[:j]
+ }
+ r := strings.NewReader(in)
+ buf, err := read(r)
+ if err != nil {
+ if tt.err == "" {
+ t.Errorf("#%d: err=%q, expected success (%q)", i, err, string(buf))
+ continue
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("#%d: err=%q, expected %q", i, err, tt.err)
+ continue
+ }
+ continue
+ }
+ if err == nil && tt.err != "" {
+ t.Errorf("#%d: success, expected %q", i, tt.err)
+ continue
+ }
+
+ out := string(buf)
+ if out != testOut {
+ t.Errorf("#%d: wrong output:\nhave %q\nwant %q\n", i, out, testOut)
+ }
+ }
+}
+
+func TestReadImports(t *testing.T) {
+ testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return readImports(r, true) })
+}
+
+func TestReadComments(t *testing.T) {
+ testRead(t, readCommentsTests, readComments)
+}
+
+var readFailuresTests = []readTest{
+ {
+ `package`,
+ "syntax error",
+ },
+ {
+ "package p\n\x00\nimport `math`\n",
+ "unexpected NUL in input",
+ },
+ {
+ `package p; import`,
+ "syntax error",
+ },
+ {
+ `package p; import "`,
+ "syntax error",
+ },
+ {
+ "package p; import ` \n\n",
+ "syntax error",
+ },
+ {
+ `package p; import "x`,
+ "syntax error",
+ },
+ {
+ `package p; import _`,
+ "syntax error",
+ },
+ {
+ `package p; import _ "`,
+ "syntax error",
+ },
+ {
+ `package p; import _ "x`,
+ "syntax error",
+ },
+ {
+ `package p; import .`,
+ "syntax error",
+ },
+ {
+ `package p; import . "`,
+ "syntax error",
+ },
+ {
+ `package p; import . "x`,
+ "syntax error",
+ },
+ {
+ `package p; import (`,
+ "syntax error",
+ },
+ {
+ `package p; import ("`,
+ "syntax error",
+ },
+ {
+ `package p; import ("x`,
+ "syntax error",
+ },
+ {
+ `package p; import ("x"`,
+ "syntax error",
+ },
+}
+
+func TestReadFailures(t *testing.T) {
+ // Errors should be reported (true arg to readImports).
+ testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return readImports(r, true) })
+}
+
+func TestReadFailuresIgnored(t *testing.T) {
+ // Syntax errors should not be reported (false arg to readImports).
+ // Instead, entire file should be the output and no error.
+ // Convert tests not to return syntax errors.
+ tests := make([]readTest, len(readFailuresTests))
+ copy(tests, readFailuresTests)
+ for i := range tests {
+ tt := &tests[i]
+ if !strings.Contains(tt.err, "NUL") {
+ tt.err = ""
+ }
+ }
+ testRead(t, tests, func(r io.Reader) ([]byte, error) { return readImports(r, false) })
+}
diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go
index 6f0edd4ba..c4b7e6ae6 100644
--- a/src/pkg/go/doc/comment.go
+++ b/src/pkg/go/doc/comment.go
@@ -174,7 +174,7 @@ func unindent(block []string) {
}
// heading returns the trimmed line if it passes as a section heading;
-// otherwise it returns the empty string.
+// otherwise it returns the empty string.
func heading(line string) string {
line = strings.TrimSpace(line)
if len(line) == 0 {
@@ -229,7 +229,8 @@ type block struct {
var nonAlphaNumRx = regexp.MustCompile(`[^a-zA-Z0-9]`)
func anchorID(line string) string {
- return nonAlphaNumRx.ReplaceAllString(line, "_")
+ // Add a "hdr-" prefix to avoid conflicting with IDs used for package symbols.
+ return "hdr-" + nonAlphaNumRx.ReplaceAllString(line, "_")
}
// ToHTML converts comment text to formatted HTML.
diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go
index 9c606315d..65b1b83eb 100644
--- a/src/pkg/go/doc/doc.go
+++ b/src/pkg/go/doc/doc.go
@@ -17,7 +17,16 @@ type Package struct {
ImportPath string
Imports []string
Filenames []string
- Bugs []string
+ // DEPRECATED. For backward compatibility Bugs is still populated,
+ // but all new code should use Notes instead.
+ Bugs []string
+
+ // Notes such as TODO(userid): or SECURITY(userid):
+ // along the lines of BUG(userid). Any marker with 2 or more upper
+ // case [A-Z] letters is recognised.
+ // BUG is explicitly not included in these notes but will
+ // be in a subsequent change when the Bugs field above is removed.
+ Notes map[string][]string
// declarations
Consts []*Value
@@ -89,6 +98,7 @@ func New(pkg *ast.Package, importPath string, mode Mode) *Package {
Imports: sortedKeys(r.imports),
Filenames: r.filenames,
Bugs: r.bugs,
+ Notes: r.notes,
Consts: sortedValues(r.values, token.CONST),
Types: sortedTypes(r.types, mode&AllMethods != 0),
Vars: sortedValues(r.values, token.VAR),
diff --git a/src/pkg/go/doc/doc_test.go b/src/pkg/go/doc/doc_test.go
index f957ede4a..8043038b4 100644
--- a/src/pkg/go/doc/doc_test.go
+++ b/src/pkg/go/doc/doc_test.go
@@ -123,7 +123,7 @@ func test(t *testing.T, mode Mode) {
}
// compare
- if bytes.Compare(got, want) != 0 {
+ if !bytes.Equal(got, want) {
t.Errorf("package %s\n\tgot:\n%s\n\twant:\n%s", pkg.Name, got, want)
}
}
diff --git a/src/pkg/go/doc/example.go b/src/pkg/go/doc/example.go
index a7e0e250a..693ad5b94 100644
--- a/src/pkg/go/doc/example.go
+++ b/src/pkg/go/doc/example.go
@@ -9,21 +9,29 @@ package doc
import (
"go/ast"
"go/token"
+ "path"
"regexp"
"sort"
+ "strconv"
"strings"
"unicode"
"unicode/utf8"
)
+// An Example represents an example function found in a source files.
type Example struct {
- Name string // name of the item being exemplified
- Doc string // example function doc string
- Code ast.Node
- Comments []*ast.CommentGroup
- Output string // expected output
+ Name string // name of the item being exemplified
+ Doc string // example function doc string
+ Code ast.Node
+ Play *ast.File // a whole program version of the example
+ Comments []*ast.CommentGroup
+ Output string // expected output
+ EmptyOutput bool // expect empty output
+ Order int // original source code order
}
+// Examples returns the examples found in the files, sorted by Name field.
+// The Order fields record the order in which the examples were encountered.
func Examples(files ...*ast.File) []*Example {
var list []*Example
for _, file := range files {
@@ -52,12 +60,16 @@ func Examples(files ...*ast.File) []*Example {
if f.Doc != nil {
doc = f.Doc.Text()
}
+ output, hasOutput := exampleOutput(f.Body, file.Comments)
flist = append(flist, &Example{
- Name: name[len("Example"):],
- Doc: doc,
- Code: f.Body,
- Comments: file.Comments,
- Output: exampleOutput(f, file.Comments),
+ Name: name[len("Example"):],
+ Doc: doc,
+ Code: f.Body,
+ Play: playExample(file, f.Body),
+ Comments: file.Comments,
+ Output: output,
+ EmptyOutput: output == "" && hasOutput,
+ Order: len(flist),
})
}
if !hasTests && numDecl > 1 && len(flist) == 1 {
@@ -65,6 +77,7 @@ func Examples(files ...*ast.File) []*Example {
// other top-level declarations, and no tests or
// benchmarks, use the whole file as the example.
flist[0].Code = file
+ flist[0].Play = playExampleFile(file)
}
list = append(list, flist...)
}
@@ -74,26 +87,22 @@ func Examples(files ...*ast.File) []*Example {
var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`)
-func exampleOutput(fun *ast.FuncDecl, comments []*ast.CommentGroup) string {
- // find the last comment in the function
- var last *ast.CommentGroup
- for _, cg := range comments {
- if cg.Pos() < fun.Pos() {
- continue
- }
- if cg.End() > fun.End() {
- break
- }
- last = cg
- }
- if last != nil {
+// Extracts the expected output and whether there was a valid output comment
+func exampleOutput(b *ast.BlockStmt, comments []*ast.CommentGroup) (output string, ok bool) {
+ if _, last := lastComment(b, comments); last != nil {
// test that it begins with the correct prefix
text := last.Text()
if loc := outputPrefix.FindStringIndex(text); loc != nil {
- return strings.TrimSpace(text[loc[1]:])
+ text = text[loc[1]:]
+ // Strip zero or more spaces followed by \n or a single space.
+ text = strings.TrimLeft(text, " ")
+ if len(text) > 0 && text[0] == '\n' {
+ text = text[1:]
+ }
+ return text, true
}
}
- return "" // no suitable comment found
+ return "", false // no suitable comment found
}
// isTest tells whether name looks like a test, example, or benchmark.
@@ -115,3 +124,214 @@ type exampleByName []*Example
func (s exampleByName) Len() int { return len(s) }
func (s exampleByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s exampleByName) Less(i, j int) bool { return s[i].Name < s[j].Name }
+
+// playExample synthesizes a new *ast.File based on the provided
+// file with the provided function body as the body of main.
+func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
+ if !strings.HasSuffix(file.Name.Name, "_test") {
+ // We don't support examples that are part of the
+ // greater package (yet).
+ return nil
+ }
+
+ // Find top-level declarations in the file.
+ topDecls := make(map[*ast.Object]bool)
+ for _, decl := range file.Decls {
+ switch d := decl.(type) {
+ case *ast.FuncDecl:
+ topDecls[d.Name.Obj] = true
+ case *ast.GenDecl:
+ for _, spec := range d.Specs {
+ switch s := spec.(type) {
+ case *ast.TypeSpec:
+ topDecls[s.Name.Obj] = true
+ case *ast.ValueSpec:
+ for _, id := range s.Names {
+ topDecls[id.Obj] = true
+ }
+ }
+ }
+ }
+ }
+
+ // Find unresolved identifiers and uses of top-level declarations.
+ unresolved := make(map[string]bool)
+ usesTopDecl := false
+ var inspectFunc func(ast.Node) bool
+ inspectFunc = func(n ast.Node) bool {
+ // For selector expressions, only inspect the left hand side.
+ // (For an expression like fmt.Println, only add "fmt" to the
+ // set of unresolved names, not "Println".)
+ if e, ok := n.(*ast.SelectorExpr); ok {
+ ast.Inspect(e.X, inspectFunc)
+ return false
+ }
+ if id, ok := n.(*ast.Ident); ok {
+ if id.Obj == nil {
+ unresolved[id.Name] = true
+ } else if topDecls[id.Obj] {
+ usesTopDecl = true
+ }
+ }
+ return true
+ }
+ ast.Inspect(body, inspectFunc)
+ if usesTopDecl {
+ // We don't support examples that are not self-contained (yet).
+ return nil
+ }
+
+ // Remove predeclared identifiers from unresolved list.
+ for n := range unresolved {
+ if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] {
+ delete(unresolved, n)
+ }
+ }
+
+ // Use unresolved identifiers to determine the imports used by this
+ // example. The heuristic assumes package names match base import
+ // paths for imports w/o renames (should be good enough most of the time).
+ namedImports := make(map[string]string) // [name]path
+ var blankImports []ast.Spec // _ imports
+ for _, s := range file.Imports {
+ p, err := strconv.Unquote(s.Path.Value)
+ if err != nil {
+ continue
+ }
+ n := path.Base(p)
+ if s.Name != nil {
+ n = s.Name.Name
+ switch n {
+ case "_":
+ blankImports = append(blankImports, s)
+ continue
+ case ".":
+ // We can't resolve dot imports (yet).
+ return nil
+ }
+ }
+ if unresolved[n] {
+ namedImports[n] = p
+ delete(unresolved, n)
+ }
+ }
+
+ // If there are other unresolved identifiers, give up because this
+ // synthesized file is not going to build.
+ if len(unresolved) > 0 {
+ return nil
+ }
+
+ // Include documentation belonging to blank imports.
+ var comments []*ast.CommentGroup
+ for _, s := range blankImports {
+ if c := s.(*ast.ImportSpec).Doc; c != nil {
+ comments = append(comments, c)
+ }
+ }
+
+ // Include comments that are inside the function body.
+ for _, c := range file.Comments {
+ if body.Pos() <= c.Pos() && c.End() <= body.End() {
+ comments = append(comments, c)
+ }
+ }
+
+ // Strip "Output:" commment and adjust body end position.
+ body, comments = stripOutputComment(body, comments)
+
+ // Synthesize import declaration.
+ importDecl := &ast.GenDecl{
+ Tok: token.IMPORT,
+ Lparen: 1, // Need non-zero Lparen and Rparen so that printer
+ Rparen: 1, // treats this as a factored import.
+ }
+ for n, p := range namedImports {
+ s := &ast.ImportSpec{Path: &ast.BasicLit{Value: strconv.Quote(p)}}
+ if path.Base(p) != n {
+ s.Name = ast.NewIdent(n)
+ }
+ importDecl.Specs = append(importDecl.Specs, s)
+ }
+ importDecl.Specs = append(importDecl.Specs, blankImports...)
+
+ // Synthesize main function.
+ funcDecl := &ast.FuncDecl{
+ Name: ast.NewIdent("main"),
+ Type: &ast.FuncType{},
+ Body: body,
+ }
+
+ // Synthesize file.
+ return &ast.File{
+ Name: ast.NewIdent("main"),
+ Decls: []ast.Decl{importDecl, funcDecl},
+ Comments: comments,
+ }
+}
+
+// playExampleFile takes a whole file example and synthesizes a new *ast.File
+// such that the example is function main in package main.
+func playExampleFile(file *ast.File) *ast.File {
+ // Strip copyright comment if present.
+ comments := file.Comments
+ if len(comments) > 0 && strings.HasPrefix(comments[0].Text(), "Copyright") {
+ comments = comments[1:]
+ }
+
+ // Copy declaration slice, rewriting the ExampleX function to main.
+ var decls []ast.Decl
+ for _, d := range file.Decls {
+ if f, ok := d.(*ast.FuncDecl); ok && isTest(f.Name.Name, "Example") {
+ // Copy the FuncDecl, as it may be used elsewhere.
+ newF := *f
+ newF.Name = ast.NewIdent("main")
+ newF.Body, comments = stripOutputComment(f.Body, comments)
+ d = &newF
+ }
+ decls = append(decls, d)
+ }
+
+ // Copy the File, as it may be used elsewhere.
+ f := *file
+ f.Name = ast.NewIdent("main")
+ f.Decls = decls
+ f.Comments = comments
+ return &f
+}
+
+// stripOutputComment finds and removes an "Output:" commment from body
+// and comments, and adjusts the body block's end position.
+func stripOutputComment(body *ast.BlockStmt, comments []*ast.CommentGroup) (*ast.BlockStmt, []*ast.CommentGroup) {
+ // Do nothing if no "Output:" comment found.
+ i, last := lastComment(body, comments)
+ if last == nil || !outputPrefix.MatchString(last.Text()) {
+ return body, comments
+ }
+
+ // Copy body and comments, as the originals may be used elsewhere.
+ newBody := &ast.BlockStmt{
+ Lbrace: body.Lbrace,
+ List: body.List,
+ Rbrace: last.Pos(),
+ }
+ newComments := make([]*ast.CommentGroup, len(comments)-1)
+ copy(newComments, comments[:i])
+ copy(newComments[i:], comments[i+1:])
+ return newBody, newComments
+}
+
+// lastComment returns the last comment inside the provided block.
+func lastComment(b *ast.BlockStmt, c []*ast.CommentGroup) (i int, last *ast.CommentGroup) {
+ pos, end := b.Pos(), b.End()
+ for j, cg := range c {
+ if cg.Pos() < pos {
+ continue
+ }
+ if cg.End() > end {
+ break
+ }
+ i, last = j, cg
+ }
+ return
+}
diff --git a/src/pkg/go/doc/example_test.go b/src/pkg/go/doc/example_test.go
new file mode 100644
index 000000000..b70efd93d
--- /dev/null
+++ b/src/pkg/go/doc/example_test.go
@@ -0,0 +1,111 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package doc_test
+
+import (
+ "bytes"
+ "go/doc"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "strings"
+ "testing"
+)
+
+const exampleTestFile = `
+package foo_test
+
+import (
+ "fmt"
+ "log"
+ "os/exec"
+)
+
+func ExampleHello() {
+ fmt.Println("Hello, world!")
+ // Output: Hello, world!
+}
+
+func ExampleImport() {
+ out, err := exec.Command("date").Output()
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("The date is %s\n", out)
+}
+`
+
+var exampleTestCases = []struct {
+ Name, Play, Output string
+}{
+ {
+ Name: "Hello",
+ Play: exampleHelloPlay,
+ Output: "Hello, world!\n",
+ },
+ {
+ Name: "Import",
+ Play: exampleImportPlay,
+ },
+}
+
+const exampleHelloPlay = `package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ fmt.Println("Hello, world!")
+}
+`
+const exampleImportPlay = `package main
+
+import (
+ "fmt"
+ "log"
+ "os/exec"
+)
+
+func main() {
+ out, err := exec.Command("date").Output()
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("The date is %s\n", out)
+}
+`
+
+func TestExamples(t *testing.T) {
+ fs := token.NewFileSet()
+ file, err := parser.ParseFile(fs, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, e := range doc.Examples(file) {
+ c := exampleTestCases[i]
+ if e.Name != c.Name {
+ t.Errorf("got Name == %q, want %q", e.Name, c.Name)
+ }
+ if w := c.Play; w != "" {
+ var g string // hah
+ if e.Play == nil {
+ g = "<nil>"
+ } else {
+ b := new(bytes.Buffer)
+ if err := format.Node(b, fs, e.Play); err != nil {
+ t.Fatal(err)
+ }
+ g = b.String()
+ }
+ if g != w {
+ t.Errorf("%s: got Play == %q, want %q", c.Name, g, w)
+ }
+ }
+ if g, w := e.Output, c.Output; g != w {
+ t.Errorf("%s: got Output == %q, want %q", c.Name, g, w)
+ }
+ }
+}
diff --git a/src/pkg/go/doc/exports.go b/src/pkg/go/doc/exports.go
index 146be5d87..ff01285d4 100644
--- a/src/pkg/go/doc/exports.go
+++ b/src/pkg/go/doc/exports.go
@@ -107,7 +107,7 @@ func (r *reader) filterParamList(fields *ast.FieldList) {
// filterType strips any unexported struct fields or method types from typ
// in place. If fields (or methods) have been removed, the corresponding
-// struct or interface type has the Incomplete field set to true.
+// struct or interface type has the Incomplete field set to true.
//
func (r *reader) filterType(parent *namedType, typ ast.Expr) {
switch t := typ.(type) {
diff --git a/src/pkg/go/doc/reader.go b/src/pkg/go/doc/reader.go
index 5eaae37b7..dd6a57299 100644
--- a/src/pkg/go/doc/reader.go
+++ b/src/pkg/go/doc/reader.go
@@ -46,7 +46,7 @@ func (mset methodSet) set(f *ast.FuncDecl) {
// since it has documentation, assume f is simply another
// implementation and ignore it. This does not happen if the
// caller is using go/build.ScanDir to determine the list of
- // files implementing a package.
+ // files implementing a package.
return
}
// function doesn't exist or has no documentation; use f
@@ -149,6 +149,7 @@ type reader struct {
doc string // package documentation, if any
filenames []string
bugs []string
+ notes map[string][]string
// declarations
imports map[string]int
@@ -400,10 +401,23 @@ func (r *reader) readFunc(fun *ast.FuncDecl) {
}
var (
- bug_markers = regexp.MustCompile("^/[/*][ \t]*BUG\\(.*\\):[ \t]*") // BUG(uid):
- bug_content = regexp.MustCompile("[^ \n\r\t]+") // at least one non-whitespace char
+ noteMarker = regexp.MustCompile(`^/[/*][ \t]*([A-Z][A-Z]+)\(.+\):[ \t]*(.*)`) // MARKER(uid)
+ noteContent = regexp.MustCompile(`[^ \n\r\t]+`) // at least one non-whitespace char
)
+func readNote(c *ast.CommentGroup) (marker, annotation string) {
+ text := c.List[0].Text
+ if m := noteMarker.FindStringSubmatch(text); m != nil {
+ if btxt := m[2]; noteContent.MatchString(btxt) {
+ // non-empty MARKER comment; collect comment without the MARKER prefix
+ list := append([]*ast.Comment(nil), c.List...) // make a copy
+ list[0].Text = m[2]
+ return m[1], (&ast.CommentGroup{List: list}).Text()
+ }
+ }
+ return "", ""
+}
+
// readFile adds the AST for a source file to the reader.
//
func (r *reader) readFile(src *ast.File) {
@@ -469,16 +483,12 @@ func (r *reader) readFile(src *ast.File) {
}
}
- // collect BUG(...) comments
+ // collect MARKER(...): annotations
for _, c := range src.Comments {
- text := c.List[0].Text
- if m := bug_markers.FindStringIndex(text); m != nil {
- // found a BUG comment; maybe empty
- if btxt := text[m[1]:]; bug_content.MatchString(btxt) {
- // non-empty BUG comment; collect comment without BUG prefix
- list := append([]*ast.Comment(nil), c.List...) // make a copy
- list[0].Text = text[m[1]:]
- r.bugs = append(r.bugs, (&ast.CommentGroup{List: list}).Text())
+ if marker, text := readNote(c); marker != "" {
+ r.notes[marker] = append(r.notes[marker], text)
+ if marker == "BUG" {
+ r.bugs = append(r.bugs, text)
}
}
}
@@ -492,9 +502,10 @@ func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
r.mode = mode
r.types = make(map[string]*namedType)
r.funcs = make(methodSet)
+ r.notes = make(map[string][]string)
// sort package files before reading them so that the
- // result result does not depend on map iteration order
+ // result does not depend on map iteration order
i := 0
for filename := range pkg.Files {
r.filenames[i] = filename
@@ -515,29 +526,6 @@ func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
// ----------------------------------------------------------------------------
// Types
-var predeclaredTypes = map[string]bool{
- "bool": true,
- "byte": true,
- "complex64": true,
- "complex128": true,
- "error": true,
- "float32": true,
- "float64": true,
- "int": true,
- "int8": true,
- "int16": true,
- "int32": true,
- "int64": true,
- "rune": true,
- "string": true,
- "uint": true,
- "uint8": true,
- "uint16": true,
- "uint32": true,
- "uint64": true,
- "uintptr": true,
-}
-
func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) *Func {
if f == nil || f.Decl == nil || f.Decl.Recv == nil || len(f.Decl.Recv.List) != 1 {
return f // shouldn't happen, but be safe
@@ -620,7 +608,7 @@ func (r *reader) computeMethodSets() {
// types that have no declaration. Instead, these functions and methods
// are shown at the package level. It also removes types with missing
// declarations or which are not visible.
-//
+//
func (r *reader) cleanupTypes() {
for _, t := range r.types {
visible := r.isVisible(t.name)
@@ -772,3 +760,54 @@ func sortedFuncs(m methodSet, allMethods bool) []*Func {
)
return list
}
+
+// ----------------------------------------------------------------------------
+// Predeclared identifiers
+
+var predeclaredTypes = map[string]bool{
+ "bool": true,
+ "byte": true,
+ "complex64": true,
+ "complex128": true,
+ "error": true,
+ "float32": true,
+ "float64": true,
+ "int": true,
+ "int8": true,
+ "int16": true,
+ "int32": true,
+ "int64": true,
+ "rune": true,
+ "string": true,
+ "uint": true,
+ "uint8": true,
+ "uint16": true,
+ "uint32": true,
+ "uint64": true,
+ "uintptr": true,
+}
+
+var predeclaredFuncs = map[string]bool{
+ "append": true,
+ "cap": true,
+ "close": true,
+ "complex": true,
+ "copy": true,
+ "delete": true,
+ "imag": true,
+ "len": true,
+ "make": true,
+ "new": true,
+ "panic": true,
+ "print": true,
+ "println": true,
+ "real": true,
+ "recover": true,
+}
+
+var predeclaredConstants = map[string]bool{
+ "false": true,
+ "iota": true,
+ "nil": true,
+ "true": true,
+}
diff --git a/src/pkg/go/doc/synopsis.go b/src/pkg/go/doc/synopsis.go
index 2192d78c0..2d1817439 100644
--- a/src/pkg/go/doc/synopsis.go
+++ b/src/pkg/go/doc/synopsis.go
@@ -4,7 +4,10 @@
package doc
-import "unicode"
+import (
+ "strings"
+ "unicode"
+)
// firstSentenceLen returns the length of the first sentence in s.
// The sentence ends after the first period followed by space and
@@ -24,17 +27,12 @@ func firstSentenceLen(s string) int {
return len(s)
}
-// Synopsis returns a cleaned version of the first sentence in s.
-// That sentence ends after the first period followed by space and
-// not preceded by exactly one uppercase letter. The result string
-// has no \n, \r, or \t characters and uses only single spaces between
-// words.
-//
-func Synopsis(s string) string {
- n := firstSentenceLen(s)
+// clean replaces each sequence of space, \n, \r, or \t characters
+// with a single space and removes any trailing and leading spaces.
+func clean(s string) string {
var b []byte
p := byte(' ')
- for i := 0; i < n; i++ {
+ for i := 0; i < len(s); i++ {
q := s[i]
if q == '\n' || q == '\r' || q == '\t' {
q = ' '
@@ -50,3 +48,26 @@ func Synopsis(s string) string {
}
return string(b)
}
+
+// Synopsis returns a cleaned version of the first sentence in s.
+// That sentence ends after the first period followed by space and
+// not preceded by exactly one uppercase letter. The result string
+// has no \n, \r, or \t characters and uses only single spaces between
+// words. If s starts with any of the IllegalPrefixes, the result
+// is the empty string.
+//
+func Synopsis(s string) string {
+ s = clean(s[0:firstSentenceLen(s)])
+ for _, prefix := range IllegalPrefixes {
+ if strings.HasPrefix(strings.ToLower(s), prefix) {
+ return ""
+ }
+ }
+ return s
+}
+
+var IllegalPrefixes = []string{
+ "copyright",
+ "all rights",
+ "author",
+}
diff --git a/src/pkg/go/doc/synopsis_test.go b/src/pkg/go/doc/synopsis_test.go
index dfc6598af..fd7081a07 100644
--- a/src/pkg/go/doc/synopsis_test.go
+++ b/src/pkg/go/doc/synopsis_test.go
@@ -28,6 +28,11 @@ var tests = []struct {
{"P. Q. ", 8, "P. Q."},
{"Package Καλημέρα κόσμε.", 36, "Package Καλημέρα κόσμε."},
{"Package こんにちは 世界\n", 31, "Package こんにちは 世界"},
+ {"Package foo does bar.", 21, "Package foo does bar."},
+ {"Copyright 2012 Google, Inc. Package foo does bar.", 27, ""},
+ {"All Rights reserved. Package foo does bar.", 20, ""},
+ {"All rights reserved. Package foo does bar.", 20, ""},
+ {"Authors: foo@bar.com. Package foo does bar.", 21, ""},
}
func TestSynopsis(t *testing.T) {
diff --git a/src/pkg/go/doc/testdata/a.0.golden b/src/pkg/go/doc/testdata/a.0.golden
index 24db02d34..ae3756c84 100644
--- a/src/pkg/go/doc/testdata/a.0.golden
+++ b/src/pkg/go/doc/testdata/a.0.golden
@@ -8,6 +8,17 @@ FILENAMES
testdata/a0.go
testdata/a1.go
+BUGS .Bugs is now deprecated, please use .Notes instead
+ // bug0
+ // bug1
+
BUGS
// bug0
// bug1
+
+SECBUGS
+ // sec hole 0 need to fix asap
+
+TODOS
+ // todo0
+ // todo1
diff --git a/src/pkg/go/doc/testdata/a.1.golden b/src/pkg/go/doc/testdata/a.1.golden
index 24db02d34..ae3756c84 100644
--- a/src/pkg/go/doc/testdata/a.1.golden
+++ b/src/pkg/go/doc/testdata/a.1.golden
@@ -8,6 +8,17 @@ FILENAMES
testdata/a0.go
testdata/a1.go
+BUGS .Bugs is now deprecated, please use .Notes instead
+ // bug0
+ // bug1
+
BUGS
// bug0
// bug1
+
+SECBUGS
+ // sec hole 0 need to fix asap
+
+TODOS
+ // todo0
+ // todo1
diff --git a/src/pkg/go/doc/testdata/a.2.golden b/src/pkg/go/doc/testdata/a.2.golden
index 24db02d34..ae3756c84 100644
--- a/src/pkg/go/doc/testdata/a.2.golden
+++ b/src/pkg/go/doc/testdata/a.2.golden
@@ -8,6 +8,17 @@ FILENAMES
testdata/a0.go
testdata/a1.go
+BUGS .Bugs is now deprecated, please use .Notes instead
+ // bug0
+ // bug1
+
BUGS
// bug0
// bug1
+
+SECBUGS
+ // sec hole 0 need to fix asap
+
+TODOS
+ // todo0
+ // todo1
diff --git a/src/pkg/go/doc/testdata/a0.go b/src/pkg/go/doc/testdata/a0.go
index dc552989e..71af470ee 100644
--- a/src/pkg/go/doc/testdata/a0.go
+++ b/src/pkg/go/doc/testdata/a0.go
@@ -6,3 +6,12 @@
package a
//BUG(uid): bug0
+
+//TODO(uid): todo0
+
+// A note with some spaces after it, should be ignored (watch out for
+// emacs modes that remove trailing whitespace).
+//NOTE(uid):
+
+// SECBUG(uid): sec hole 0
+// need to fix asap
diff --git a/src/pkg/go/doc/testdata/a1.go b/src/pkg/go/doc/testdata/a1.go
index 098776c1b..9fad1e09b 100644
--- a/src/pkg/go/doc/testdata/a1.go
+++ b/src/pkg/go/doc/testdata/a1.go
@@ -6,3 +6,7 @@
package a
//BUG(uid): bug1
+
+//TODO(uid): todo1
+
+//TODO(): ignored
diff --git a/src/pkg/go/doc/testdata/benchmark.go b/src/pkg/go/doc/testdata/benchmark.go
index 0aded5bb4..905e49644 100644
--- a/src/pkg/go/doc/testdata/benchmark.go
+++ b/src/pkg/go/doc/testdata/benchmark.go
@@ -13,7 +13,7 @@ import (
)
var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
-var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")
+var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark")
// An internal type but exported because it is cross-package; part of the implementation
// of go test.
@@ -151,7 +151,7 @@ func (b *B) launch() {
b.runN(n)
// Run the benchmark for at least the specified amount of time.
- d := time.Duration(*benchTime * float64(time.Second))
+ d := *benchTime
for !b.failed && b.duration < d && n < 1e9 {
last := n
// Predict iterations/sec.
diff --git a/src/pkg/go/doc/testdata/e.go b/src/pkg/go/doc/testdata/e.go
index 19dd138cf..ec432e3e5 100644
--- a/src/pkg/go/doc/testdata/e.go
+++ b/src/pkg/go/doc/testdata/e.go
@@ -106,7 +106,7 @@ type U4 struct {
*u5
}
-// U4.M should appear as method of U4.
+// U4.M should appear as method of U4.
func (*U4) M() {}
type u5 struct {
diff --git a/src/pkg/go/doc/testdata/error2.1.golden b/src/pkg/go/doc/testdata/error2.1.golden
index 776bd1b3e..dbcc1b03e 100644
--- a/src/pkg/go/doc/testdata/error2.1.golden
+++ b/src/pkg/go/doc/testdata/error2.1.golden
@@ -10,7 +10,7 @@ FILENAMES
TYPES
//
type I0 interface {
- // When embedded, the the locally declared error interface
+ // When embedded, the locally-declared error interface
// is only visible if all declarations are shown.
error
}
diff --git a/src/pkg/go/doc/testdata/error2.go b/src/pkg/go/doc/testdata/error2.go
index 6cc36feef..6ee96c245 100644
--- a/src/pkg/go/doc/testdata/error2.go
+++ b/src/pkg/go/doc/testdata/error2.go
@@ -5,7 +5,7 @@
package error2
type I0 interface {
- // When embedded, the the locally declared error interface
+ // When embedded, the locally-declared error interface
// is only visible if all declarations are shown.
error
}
diff --git a/src/pkg/go/doc/testdata/template.txt b/src/pkg/go/doc/testdata/template.txt
index 32e331cdd..d3882b6b9 100644
--- a/src/pkg/go/doc/testdata/template.txt
+++ b/src/pkg/go/doc/testdata/template.txt
@@ -60,6 +60,9 @@ TYPES
{{end}}{{end}}{{end}}{{/*
*/}}{{with .Bugs}}
-BUGS
+BUGS .Bugs is now deprecated, please use .Notes instead
{{range .}} {{synopsis .}}
-{{end}}{{end}} \ No newline at end of file
+{{end}}{{end}}{{with .Notes}}{{range $marker, $content := .}}
+{{$marker}}S
+{{range $content}} {{synopsis .}}
+{{end}}{{end}}{{end}} \ No newline at end of file
diff --git a/src/pkg/go/doc/testdata/testing.1.golden b/src/pkg/go/doc/testdata/testing.1.golden
index d26a4685c..ffdb5c3b5 100644
--- a/src/pkg/go/doc/testdata/testing.1.golden
+++ b/src/pkg/go/doc/testdata/testing.1.golden
@@ -45,7 +45,7 @@ VARIABLES
)
//
- var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")
+ var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark")
//
var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
diff --git a/src/pkg/go/doc/testdata/testing.go b/src/pkg/go/doc/testdata/testing.go
index 71c1d1eaf..c2499ad77 100644
--- a/src/pkg/go/doc/testdata/testing.go
+++ b/src/pkg/go/doc/testdata/testing.go
@@ -197,7 +197,7 @@ func (c *common) Fatalf(format string, args ...interface{}) {
c.FailNow()
}
-// Parallel signals that this test is to be run in parallel with (and only with)
+// Parallel signals that this test is to be run in parallel with (and only with)
// other parallel tests in this CPU group.
func (t *T) Parallel() {
t.signal <- (*T)(nil) // Release main testing loop
@@ -215,7 +215,7 @@ func tRunner(t *T, test *InternalTest) {
t.start = time.Now()
// When this goroutine is done, either because test.F(t)
- // returned normally or because a test failure triggered
+ // returned normally or because a test failure triggered
// a call to runtime.Goexit, record the duration and send
// a signal saying that the test is done.
defer func() {
diff --git a/src/pkg/go/format/format.go b/src/pkg/go/format/format.go
new file mode 100644
index 000000000..65b0e4e4b
--- /dev/null
+++ b/src/pkg/go/format/format.go
@@ -0,0 +1,200 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package format implements standard formatting of Go source.
+package format
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/printer"
+ "go/token"
+ "io"
+ "strings"
+)
+
+var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
+
+// Node formats node in canonical gofmt style and writes the result to dst.
+//
+// The node type must be *ast.File, *printer.CommentedNode, []ast.Decl,
+// []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec,
+// or ast.Stmt. Node does not modify node. Imports are not sorted for
+// nodes representing partial source files (i.e., if the node is not an
+// *ast.File or a *printer.CommentedNode not wrapping an *ast.File).
+//
+// The function may return early (before the entire result is written)
+// and return a formatting error, for instance due to an incorrect AST.
+//
+func Node(dst io.Writer, fset *token.FileSet, node interface{}) error {
+ // Determine if we have a complete source file (file != nil).
+ var file *ast.File
+ var cnode *printer.CommentedNode
+ switch n := node.(type) {
+ case *ast.File:
+ file = n
+ case *printer.CommentedNode:
+ if f, ok := n.Node.(*ast.File); ok {
+ file = f
+ cnode = n
+ }
+ }
+
+ // Sort imports if necessary.
+ if file != nil && hasUnsortedImports(file) {
+ // Make a copy of the AST because ast.SortImports is destructive.
+ // TODO(gri) Do this more efficiently.
+ var buf bytes.Buffer
+ err := config.Fprint(&buf, fset, file)
+ if err != nil {
+ return err
+ }
+ file, err = parser.ParseFile(fset, "", buf.Bytes(), parser.ParseComments)
+ if err != nil {
+ // We should never get here. If we do, provide good diagnostic.
+ return fmt.Errorf("format.Node internal error (%s)", err)
+ }
+ ast.SortImports(fset, file)
+
+ // Use new file with sorted imports.
+ node = file
+ if cnode != nil {
+ node = &printer.CommentedNode{Node: file, Comments: cnode.Comments}
+ }
+ }
+
+ return config.Fprint(dst, fset, node)
+}
+
+// Source formats src in canonical gofmt style and writes the result to dst
+// or returns an I/O or syntax error. src is expected to be a syntactically
+// correct Go source file, or a list of Go declarations or statements.
+//
+// If src is a partial source file, the leading and trailing space of src
+// is applied to the result (such that it has the same leading and trailing
+// space as src), and the formatted src is indented by the same amount as
+// the first line of src containing code. Imports are not sorted for partial
+// source files.
+//
+func Source(src []byte) ([]byte, error) {
+ fset := token.NewFileSet()
+ node, err := parse(fset, src)
+ if err != nil {
+ return nil, err
+ }
+
+ var buf bytes.Buffer
+ if file, ok := node.(*ast.File); ok {
+ // Complete source file.
+ ast.SortImports(fset, file)
+ err := config.Fprint(&buf, fset, file)
+ if err != nil {
+ return nil, err
+ }
+
+ } else {
+ // Partial source file.
+ // Determine and prepend leading space.
+ i, j := 0, 0
+ for j < len(src) && isSpace(src[j]) {
+ if src[j] == '\n' {
+ i = j + 1 // index of last line in leading space
+ }
+ j++
+ }
+ buf.Write(src[:i])
+
+ // Determine indentation of first code line.
+ // Spaces are ignored unless there are no tabs,
+ // in which case spaces count as one tab.
+ indent := 0
+ hasSpace := false
+ for _, b := range src[i:j] {
+ switch b {
+ case ' ':
+ hasSpace = true
+ case '\t':
+ indent++
+ }
+ }
+ if indent == 0 && hasSpace {
+ indent = 1
+ }
+
+ // Format the source.
+ cfg := config
+ cfg.Indent = indent
+ err := cfg.Fprint(&buf, fset, node)
+ if err != nil {
+ return nil, err
+ }
+
+ // Determine and append trailing space.
+ i = len(src)
+ for i > 0 && isSpace(src[i-1]) {
+ i--
+ }
+ buf.Write(src[i:])
+ }
+
+ return buf.Bytes(), nil
+}
+
+func hasUnsortedImports(file *ast.File) bool {
+ for _, d := range file.Decls {
+ d, ok := d.(*ast.GenDecl)
+ if !ok || d.Tok != token.IMPORT {
+ // Not an import declaration, so we're done.
+ // Imports are always first.
+ return false
+ }
+ if d.Lparen.IsValid() {
+ // For now assume all grouped imports are unsorted.
+ // TODO(gri) Should check if they are sorted already.
+ return true
+ }
+ // Ungrouped imports are sorted by default.
+ }
+ return false
+}
+
+func isSpace(b byte) bool {
+ return b == ' ' || b == '\t' || b == '\n' || b == '\r'
+}
+
+func parse(fset *token.FileSet, src []byte) (interface{}, error) {
+ // Try as a complete source file.
+ file, err := parser.ParseFile(fset, "", src, parser.ParseComments)
+ if err == nil {
+ return file, nil
+ }
+ // If the source is missing a package clause, try as a source fragment; otherwise fail.
+ if !strings.Contains(err.Error(), "expected 'package'") {
+ return nil, err
+ }
+
+ // Try as a declaration list by prepending a package clause in front of src.
+ // Use ';' not '\n' to keep line numbers intact.
+ psrc := append([]byte("package p;"), src...)
+ file, err = parser.ParseFile(fset, "", psrc, parser.ParseComments)
+ if err == nil {
+ return file.Decls, nil
+ }
+ // If the source is missing a declaration, try as a statement list; otherwise fail.
+ if !strings.Contains(err.Error(), "expected declaration") {
+ return nil, err
+ }
+
+ // Try as statement list by wrapping a function around src.
+ fsrc := append(append([]byte("package p; func _() {"), src...), '}')
+ file, err = parser.ParseFile(fset, "", fsrc, parser.ParseComments)
+ if err == nil {
+ return file.Decls[0].(*ast.FuncDecl).Body.List, nil
+ }
+
+ // Failed, and out of options.
+ return nil, err
+}
diff --git a/src/pkg/go/format/format_test.go b/src/pkg/go/format/format_test.go
new file mode 100644
index 000000000..7d7940bb5
--- /dev/null
+++ b/src/pkg/go/format/format_test.go
@@ -0,0 +1,125 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package format
+
+import (
+ "bytes"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "strings"
+ "testing"
+)
+
+const testfile = "format_test.go"
+
+func diff(t *testing.T, dst, src []byte) {
+ line := 1
+ offs := 0 // line offset
+ for i := 0; i < len(dst) && i < len(src); i++ {
+ d := dst[i]
+ s := src[i]
+ if d != s {
+ t.Errorf("dst:%d: %s\n", line, dst[offs:i+1])
+ t.Errorf("src:%d: %s\n", line, src[offs:i+1])
+ return
+ }
+ if s == '\n' {
+ line++
+ offs = i + 1
+ }
+ }
+ if len(dst) != len(src) {
+ t.Errorf("len(dst) = %d, len(src) = %d\nsrc = %q", len(dst), len(src), src)
+ }
+}
+
+func TestNode(t *testing.T) {
+ src, err := ioutil.ReadFile(testfile)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, testfile, src, parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var buf bytes.Buffer
+
+ if err = Node(&buf, fset, file); err != nil {
+ t.Fatal("Node failed:", err)
+ }
+
+ diff(t, buf.Bytes(), src)
+}
+
+func TestSource(t *testing.T) {
+ src, err := ioutil.ReadFile(testfile)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ res, err := Source(src)
+ if err != nil {
+ t.Fatal("Source failed:", err)
+ }
+
+ diff(t, res, src)
+}
+
+// Test cases that are expected to fail are marked by the prefix "ERROR".
+var tests = []string{
+ // declaration lists
+ `import "go/format"`,
+ "var x int",
+ "var x int\n\ntype T struct{}",
+
+ // statement lists
+ "x := 0",
+ "f(a, b, c)\nvar x int = f(1, 2, 3)",
+
+ // indentation, leading and trailing space
+ "\tx := 0\n\tgo f()",
+ "\tx := 0\n\tgo f()\n\n\n",
+ "\n\t\t\n\n\tx := 0\n\tgo f()\n\n\n",
+ "\n\t\t\n\n\t\t\tx := 0\n\t\t\tgo f()\n\n\n",
+ "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation inside raw strings
+
+ // erroneous programs
+ "ERRORvar x",
+ "ERROR1 + 2 +",
+ "ERRORx := 0",
+}
+
+func String(s string) (string, error) {
+ res, err := Source([]byte(s))
+ if err != nil {
+ return "", err
+ }
+ return string(res), nil
+}
+
+func TestPartial(t *testing.T) {
+ for _, src := range tests {
+ if strings.HasPrefix(src, "ERROR") {
+ // test expected to fail
+ src = src[5:] // remove ERROR prefix
+ res, err := String(src)
+ if err == nil && res == src {
+ t.Errorf("formatting succeeded but was expected to fail:\n%q", src)
+ }
+ } else {
+ // test expected to succeed
+ res, err := String(src)
+ if err != nil {
+ t.Errorf("formatting failed (%s):\n%q", err, src)
+ } else if res != src {
+ t.Errorf("formatting incorrect:\nsource: %q\nresult: %q", src, res)
+ }
+ }
+ }
+}
diff --git a/src/pkg/go/parser/error_test.go b/src/pkg/go/parser/error_test.go
index 377c8b80c..b59fda11a 100644
--- a/src/pkg/go/parser/error_test.go
+++ b/src/pkg/go/parser/error_test.go
@@ -34,9 +34,11 @@ import (
const testdata = "testdata"
+var fsetErrs *token.FileSet
+
// getFile assumes that each filename occurs at most once
func getFile(filename string) (file *token.File) {
- fset.Iterate(func(f *token.File) bool {
+ fsetErrs.Iterate(func(f *token.File) bool {
if f.Name() == filename {
if file != nil {
panic(filename + " used multiple times")
@@ -125,7 +127,7 @@ func compareErrors(t *testing.T, expected map[token.Pos]string, found scanner.Er
if len(expected) > 0 {
t.Errorf("%d errors not reported:", len(expected))
for pos, msg := range expected {
- t.Errorf("%s: %s\n", fset.Position(pos), msg)
+ t.Errorf("%s: %s\n", fsetErrs.Position(pos), msg)
}
}
}
@@ -137,12 +139,13 @@ func checkErrors(t *testing.T, filename string, input interface{}) {
return
}
- _, err = ParseFile(fset, filename, src, DeclarationErrors)
+ _, err = ParseFile(fsetErrs, filename, src, DeclarationErrors|AllErrors)
found, ok := err.(scanner.ErrorList)
if err != nil && !ok {
t.Error(err)
return
}
+ found.RemoveMultiples()
// we are expecting the following errors
// (collect these after parsing a file so that it is found in the file set)
@@ -153,6 +156,7 @@ func checkErrors(t *testing.T, filename string, input interface{}) {
}
func TestErrors(t *testing.T) {
+ fsetErrs = token.NewFileSet()
list, err := ioutil.ReadDir(testdata)
if err != nil {
t.Fatal(err)
diff --git a/src/pkg/go/parser/interface.go b/src/pkg/go/parser/interface.go
index 5c203a784..39affdd6b 100644
--- a/src/pkg/go/parser/interface.go
+++ b/src/pkg/go/parser/interface.go
@@ -52,12 +52,13 @@ func readSource(filename string, src interface{}) ([]byte, error) {
type Mode uint
const (
- PackageClauseOnly Mode = 1 << iota // parsing stops after package clause
- ImportsOnly // parsing stops after import declarations
- ParseComments // parse comments and add them to AST
- Trace // print a trace of parsed productions
- DeclarationErrors // report declaration errors
- SpuriousErrors // report all (not just the first) errors per line
+ PackageClauseOnly Mode = 1 << iota // stop parsing after package clause
+ ImportsOnly // stop parsing after import declarations
+ ParseComments // parse comments and add them to AST
+ Trace // print a trace of parsed productions
+ DeclarationErrors // report declaration errors
+ SpuriousErrors // same as AllErrors, for backward-compatibility
+ AllErrors = SpuriousErrors // report all errors (not just the first 10 on different lines)
)
// ParseFile parses the source code of a single Go source file and returns
@@ -79,26 +80,39 @@ const (
// representing the fragments of erroneous source code). Multiple errors
// are returned via a scanner.ErrorList which is sorted by file position.
//
-func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (*ast.File, error) {
+func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (f *ast.File, err error) {
// get source
text, err := readSource(filename, src)
if err != nil {
return nil, err
}
- // parse source
var p parser
- p.init(fset, filename, text, mode)
- f := p.parseFile()
+ defer func() {
+ if e := recover(); e != nil {
+ _ = e.(bailout) // re-panics if it's not a bailout
+ }
+
+ // set result values
+ if f == nil {
+ // source is not a valid Go source file - satisfy
+ // ParseFile API and return a valid (but) empty
+ // *ast.File
+ f = &ast.File{
+ Name: new(ast.Ident),
+ Scope: ast.NewScope(nil),
+ }
+ }
- // sort errors
- if p.mode&SpuriousErrors == 0 {
- p.errors.RemoveMultiples()
- } else {
p.errors.Sort()
- }
+ err = p.errors.Err()
+ }()
+
+ // parse source
+ p.init(fset, filename, text, mode)
+ f = p.parseFile()
- return f, p.errors.Err()
+ return
}
// ParseDir calls ParseFile for the files in the directory specified by path and
@@ -149,7 +163,7 @@ func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, m
// ParseExpr is a convenience function for obtaining the AST of an expression x.
// The position information recorded in the AST is undefined.
-//
+//
func ParseExpr(x string) (ast.Expr, error) {
// parse x within the context of a complete package for correct scopes;
// use //line directive for correct positions in error messages and put
diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index 20e505d97..a021a5abe 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -28,7 +28,7 @@ type parser struct {
// Tracing/debugging
mode Mode // parsing mode
trace bool // == (mode & Trace != 0)
- indent uint // indentation used for tracing output
+ indent int // indentation used for tracing output
// Comments
comments []*ast.CommentGroup
@@ -56,7 +56,7 @@ type parser struct {
unresolved []*ast.Ident // unresolved identifiers
imports []*ast.ImportSpec // list of imports
- // Label scope
+ // Label scopes
// (maintained by open/close LabelScope)
labelScope *ast.Scope // label scope for current function
targetStack [][]*ast.Ident // stack of unresolved labels
@@ -75,14 +75,6 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mod
p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
p.next()
-
- // set up the pkgScope here (as opposed to in parseFile) because
- // there are other parser entry points (ParseExpr, etc.)
- p.openScope()
- p.pkgScope = p.topScope
-
- // for the same reason, set up a label scope
- p.openLabelScope()
}
// ----------------------------------------------------------------------------
@@ -157,7 +149,7 @@ func (p *parser) shortVarDecl(decl *ast.AssignStmt, list []ast.Expr) {
}
}
} else {
- p.errorExpected(x.Pos(), "identifier")
+ p.errorExpected(x.Pos(), "identifier on left side of :=")
}
}
if n == 0 && p.mode&DeclarationErrors != 0 {
@@ -170,7 +162,12 @@ func (p *parser) shortVarDecl(decl *ast.AssignStmt, list []ast.Expr) {
// internal consistency.
var unresolved = new(ast.Object)
-func (p *parser) resolve(x ast.Expr) {
+// If x is an identifier, tryResolve attempts to resolve x by looking up
+// the object it denotes. If no object is found and collectUnresolved is
+// set, x is marked as unresolved and collected in the list of unresolved
+// identifiers.
+//
+func (p *parser) tryResolve(x ast.Expr, collectUnresolved bool) {
// nothing to do if x is not an identifier or the blank identifier
ident, _ := x.(*ast.Ident)
if ident == nil {
@@ -191,23 +188,30 @@ func (p *parser) resolve(x ast.Expr) {
// must be found either in the file scope, package scope
// (perhaps in another file), or universe scope --- collect
// them so that they can be resolved later
- ident.Obj = unresolved
- p.unresolved = append(p.unresolved, ident)
+ if collectUnresolved {
+ ident.Obj = unresolved
+ p.unresolved = append(p.unresolved, ident)
+ }
+}
+
+func (p *parser) resolve(x ast.Expr) {
+ p.tryResolve(x, true)
}
// ----------------------------------------------------------------------------
// Parsing support
func (p *parser) printTrace(a ...interface{}) {
- const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " +
- ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
- const n = uint(len(dots))
+ const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
+ const n = len(dots)
pos := p.file.Position(p.pos)
fmt.Printf("%5d:%3d: ", pos.Line, pos.Column)
i := 2 * p.indent
- for ; i > n; i -= n {
+ for i > n {
fmt.Print(dots)
+ i -= n
}
+ // i <= n
fmt.Print(dots[0:i])
fmt.Println(a...)
}
@@ -218,7 +222,7 @@ func trace(p *parser, msg string) *parser {
return p
}
-// Usage pattern: defer un(trace(p, "..."));
+// Usage pattern: defer un(trace(p, "..."))
func un(p *parser) {
p.indent--
p.printTrace(")")
@@ -304,14 +308,14 @@ func (p *parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline
func (p *parser) next() {
p.leadComment = nil
p.lineComment = nil
- line := p.file.Line(p.pos) // current line
+ prev := p.pos
p.next0()
if p.tok == token.COMMENT {
var comment *ast.CommentGroup
var endline int
- if p.file.Line(p.pos) == line {
+ if p.file.Line(p.pos) == p.file.Line(prev) {
// The comment is on same line as the previous token; it
// cannot be a lead comment but may be a line comment.
comment, endline = p.consumeCommentGroup(0)
@@ -336,8 +340,26 @@ func (p *parser) next() {
}
}
+// A bailout panic is raised to indicate early termination.
+type bailout struct{}
+
func (p *parser) error(pos token.Pos, msg string) {
- p.errors.Add(p.file.Position(pos), msg)
+ epos := p.file.Position(pos)
+
+ // If AllErrors is not set, discard errors reported on the same line
+ // as the last recorded error and stop parsing if there are more than
+ // 10 errors.
+ if p.mode&AllErrors == 0 {
+ n := len(p.errors)
+ if n > 0 && p.errors[n-1].Pos.Line == epos.Line {
+ return // discard - likely a spurious error
+ }
+ if n > 10 {
+ panic(bailout{})
+ }
+ }
+
+ p.errors.Add(epos, msg)
}
func (p *parser) errorExpected(pos token.Pos, msg string) {
@@ -585,14 +607,15 @@ func (p *parser) parseTypeName() ast.Expr {
return ident
}
-func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr {
+func (p *parser) parseArrayType() ast.Expr {
if p.trace {
defer un(trace(p, "ArrayType"))
}
lbrack := p.expect(token.LBRACK)
var len ast.Expr
- if ellipsisOk && p.tok == token.ELLIPSIS {
+ // always permit ellipsis for more fault-tolerant parsing
+ if p.tok == token.ELLIPSIS {
len = &ast.Ellipsis{Ellipsis: p.pos}
p.next()
} else if p.tok != token.RBRACK {
@@ -704,7 +727,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
if isParam && p.tok == token.ELLIPSIS {
pos := p.pos
p.next()
- typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message
+ typ := p.tryIdentOrType() // don't use parseType so we can provide better error message
if typ != nil {
p.resolve(typ)
} else {
@@ -713,7 +736,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
}
return &ast.Ellipsis{Ellipsis: pos, Elt: typ}
}
- return p.tryIdentOrType(false)
+ return p.tryIdentOrType()
}
// If the result is an identifier, it is not resolved.
@@ -931,29 +954,31 @@ func (p *parser) parseChanType() *ast.ChanType {
pos := p.pos
dir := ast.SEND | ast.RECV
+ var arrow token.Pos
if p.tok == token.CHAN {
p.next()
if p.tok == token.ARROW {
+ arrow = p.pos
p.next()
dir = ast.SEND
}
} else {
- p.expect(token.ARROW)
+ arrow = p.expect(token.ARROW)
p.expect(token.CHAN)
dir = ast.RECV
}
value := p.parseType()
- return &ast.ChanType{Begin: pos, Dir: dir, Value: value}
+ return &ast.ChanType{Begin: pos, Arrow: arrow, Dir: dir, Value: value}
}
// If the result is an identifier, it is not resolved.
-func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
+func (p *parser) tryIdentOrType() ast.Expr {
switch p.tok {
case token.IDENT:
return p.parseTypeName()
case token.LBRACK:
- return p.parseArrayType(ellipsisOk)
+ return p.parseArrayType()
case token.STRUCT:
return p.parseStructType()
case token.MUL:
@@ -980,7 +1005,7 @@ func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
}
func (p *parser) tryType() ast.Expr {
- typ := p.tryIdentOrType(false)
+ typ := p.tryIdentOrType()
if typ != nil {
p.resolve(typ)
}
@@ -1088,7 +1113,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr {
return p.parseFuncTypeOrLit()
}
- if typ := p.tryIdentOrType(true); typ != nil {
+ if typ := p.tryIdentOrType(); typ != nil {
// could be type for composite literal or conversion
_, isIdent := typ.(*ast.Ident)
assert(!isIdent, "type cannot be identifier")
@@ -1193,14 +1218,35 @@ func (p *parser) parseElement(keyOk bool) ast.Expr {
return p.parseLiteralValue(nil)
}
- x := p.checkExpr(p.parseExpr(keyOk)) // don't resolve if map key
+ // Because the parser doesn't know the composite literal type, it cannot
+ // know if a key that's an identifier is a struct field name or a name
+ // denoting a value. The former is not resolved by the parser or the
+ // resolver.
+ //
+ // Instead, _try_ to resolve such a key if possible. If it resolves,
+ // it a) has correctly resolved, or b) incorrectly resolved because
+ // the key is a struct field with a name matching another identifier.
+ // In the former case we are done, and in the latter case we don't
+ // care because the type checker will do a separate field lookup.
+ //
+ // If the key does not resolve, it a) must be defined at the top
+ // level in another file of the same package, the universe scope, or be
+ // undeclared; or b) it is a struct field. In the former case, the type
+ // checker can do a top-level lookup, and in the latter case it will do
+ // a separate field lookup.
+ x := p.checkExpr(p.parseExpr(keyOk))
if keyOk {
if p.tok == token.COLON {
colon := p.pos
p.next()
+ // Try to resolve the key but don't collect it
+ // as unresolved identifier if it fails so that
+ // we don't get (possibly false) errors about
+ // undeclared names.
+ p.tryResolve(x, false)
return &ast.KeyValueExpr{Key: x, Colon: colon, Value: p.parseElement(false)}
}
- p.resolve(x) // not a map key
+ p.resolve(x) // not a key
}
return x
@@ -1404,16 +1450,49 @@ func (p *parser) parseUnaryExpr(lhs bool) ast.Expr {
case token.ARROW:
// channel type or receive expression
- pos := p.pos
+ arrow := p.pos
p.next()
- if p.tok == token.CHAN {
- p.next()
- value := p.parseType()
- return &ast.ChanType{Begin: pos, Dir: ast.RECV, Value: value}
- }
+
+ // If the next token is token.CHAN we still don't know if it
+ // is a channel type or a receive operation - we only know
+ // once we have found the end of the unary expression. There
+ // are two cases:
+ //
+ // <- type => (<-type) must be channel type
+ // <- expr => <-(expr) is a receive from an expression
+ //
+ // In the first case, the arrow must be re-associated with
+ // the channel type parsed already:
+ //
+ // <- (chan type) => (<-chan type)
+ // <- (chan<- type) => (<-chan (<-type))
x := p.parseUnaryExpr(false)
- return &ast.UnaryExpr{OpPos: pos, Op: token.ARROW, X: p.checkExpr(x)}
+
+ // determine which case we have
+ if typ, ok := x.(*ast.ChanType); ok {
+ // (<-type)
+
+ // re-associate position info and <-
+ dir := ast.SEND
+ for ok && dir == ast.SEND {
+ if typ.Dir == ast.RECV {
+ // error: (<-type) is (<-(<-chan T))
+ p.errorExpected(typ.Arrow, "'chan'")
+ }
+ arrow, typ.Begin, typ.Arrow = typ.Arrow, arrow, arrow
+ dir, typ.Dir = typ.Dir, ast.RECV
+ typ, ok = typ.Value.(*ast.ChanType)
+ }
+ if dir == ast.SEND {
+ p.errorExpected(arrow, "channel type")
+ }
+
+ return x
+ }
+
+ // <-(expr)
+ return &ast.UnaryExpr{OpPos: arrow, Op: token.ARROW, X: p.checkExpr(x)}
case token.MUL:
// pointer type or unary "*" expression
@@ -1774,7 +1853,7 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
//
// switch t := 0; t := x.(T) { ... }
//
- // (this code is not valid Go because the first t will
+ // (this code is not valid Go because the first t
// cannot be accessed and thus is never used, the extra
// scope is needed for the correct error message).
//
@@ -2012,7 +2091,7 @@ func (p *parser) parseStmt() (s ast.Stmt) {
// ----------------------------------------------------------------------------
// Declarations
-type parseSpecFunction func(p *parser, doc *ast.CommentGroup, iota int) ast.Spec
+type parseSpecFunction func(p *parser, doc *ast.CommentGroup, keyword token.Token, iota int) ast.Spec
func isValidImport(lit string) bool {
const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
@@ -2025,7 +2104,7 @@ func isValidImport(lit string) bool {
return s != ""
}
-func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
+func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.Spec {
if p.trace {
defer un(trace(p, "ImportSpec"))
}
@@ -2063,15 +2142,15 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
return spec
}
-func parseConstSpec(p *parser, doc *ast.CommentGroup, iota int) ast.Spec {
+func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota int) ast.Spec {
if p.trace {
- defer un(trace(p, "ConstSpec"))
+ defer un(trace(p, keyword.String()+"Spec"))
}
idents := p.parseIdentList()
typ := p.tryType()
var values []ast.Expr
- if typ != nil || p.tok == token.ASSIGN || iota == 0 {
+ if p.tok == token.ASSIGN || keyword == token.CONST && (typ != nil || iota == 0) || keyword == token.VAR && typ == nil {
p.expect(token.ASSIGN)
values = p.parseRhsList()
}
@@ -2088,12 +2167,16 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup, iota int) ast.Spec {
Values: values,
Comment: p.lineComment,
}
- p.declare(spec, iota, p.topScope, ast.Con, idents...)
+ kind := ast.Con
+ if keyword == token.VAR {
+ kind = ast.Var
+ }
+ p.declare(spec, iota, p.topScope, kind, idents...)
return spec
}
-func parseTypeSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
+func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.Spec {
if p.trace {
defer un(trace(p, "TypeSpec"))
}
@@ -2114,36 +2197,6 @@ func parseTypeSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
return spec
}
-func parseVarSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
- if p.trace {
- defer un(trace(p, "VarSpec"))
- }
-
- idents := p.parseIdentList()
- typ := p.tryType()
- var values []ast.Expr
- if typ == nil || p.tok == token.ASSIGN {
- p.expect(token.ASSIGN)
- values = p.parseRhsList()
- }
- p.expectSemi() // call before accessing p.linecomment
-
- // Go spec: The scope of a constant or variable identifier declared inside
- // a function begins at the end of the ConstSpec or VarSpec and ends at
- // the end of the innermost containing block.
- // (Global identifiers are resolved in a separate phase after parsing.)
- spec := &ast.ValueSpec{
- Doc: doc,
- Names: idents,
- Type: typ,
- Values: values,
- Comment: p.lineComment,
- }
- p.declare(spec, nil, p.topScope, ast.Var, idents...)
-
- return spec
-}
-
func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.GenDecl {
if p.trace {
defer un(trace(p, "GenDecl("+keyword.String()+")"))
@@ -2157,12 +2210,12 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen
lparen = p.pos
p.next()
for iota := 0; p.tok != token.RPAREN && p.tok != token.EOF; iota++ {
- list = append(list, f(p, p.leadComment, iota))
+ list = append(list, f(p, p.leadComment, keyword, iota))
}
rparen = p.expect(token.RPAREN)
p.expectSemi()
} else {
- list = append(list, f(p, nil, 0))
+ list = append(list, f(p, nil, keyword, 0))
}
return &ast.GenDecl{
@@ -2262,14 +2315,11 @@ func (p *parser) parseDecl(sync func(*parser)) ast.Decl {
var f parseSpecFunction
switch p.tok {
- case token.CONST:
- f = parseConstSpec
+ case token.CONST, token.VAR:
+ f = (*parser).parseValueSpec
case token.TYPE:
- f = parseTypeSpec
-
- case token.VAR:
- f = parseVarSpec
+ f = (*parser).parseTypeSpec
case token.FUNC:
return p.parseFuncDecl()
@@ -2292,6 +2342,12 @@ func (p *parser) parseFile() *ast.File {
defer un(trace(p, "File"))
}
+ // Don't bother parsing the rest if we had errors scanning the first token.
+ // Likely not a Go source file at all.
+ if p.errors.Len() != 0 {
+ return nil
+ }
+
// package clause
doc := p.leadComment
pos := p.expect(token.PACKAGE)
@@ -2303,15 +2359,19 @@ func (p *parser) parseFile() *ast.File {
}
p.expectSemi()
- var decls []ast.Decl
-
- // Don't bother parsing the rest if we had errors already.
+ // Don't bother parsing the rest if we had errors parsing the package clause.
// Likely not a Go source file at all.
+ if p.errors.Len() != 0 {
+ return nil
+ }
- if p.errors.Len() == 0 && p.mode&PackageClauseOnly == 0 {
+ p.openScope()
+ p.pkgScope = p.topScope
+ var decls []ast.Decl
+ if p.mode&PackageClauseOnly == 0 {
// import decls
for p.tok == token.IMPORT {
- decls = append(decls, p.parseGenDecl(token.IMPORT, parseImportSpec))
+ decls = append(decls, p.parseGenDecl(token.IMPORT, (*parser).parseImportSpec))
}
if p.mode&ImportsOnly == 0 {
@@ -2321,8 +2381,9 @@ func (p *parser) parseFile() *ast.File {
}
}
}
-
- assert(p.topScope == p.pkgScope, "imbalanced scopes")
+ p.closeScope()
+ assert(p.topScope == nil, "unbalanced scopes")
+ assert(p.labelScope == nil, "unbalanced label scopes")
// resolve global identifiers within the same file
i := 0
diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go
index 1b7a41b1b..1960377b0 100644
--- a/src/pkg/go/parser/parser_test.go
+++ b/src/pkg/go/parser/parser_test.go
@@ -135,6 +135,53 @@ func TestVarScope(t *testing.T) {
}
}
+func TestObjects(t *testing.T) {
+ const src = `
+package p
+import fmt "fmt"
+const pi = 3.14
+type T struct{}
+var x int
+func f() { L: }
+`
+
+ f, err := ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ objects := map[string]ast.ObjKind{
+ "p": ast.Bad, // not in a scope
+ "fmt": ast.Bad, // not resolved yet
+ "pi": ast.Con,
+ "T": ast.Typ,
+ "x": ast.Var,
+ "int": ast.Bad, // not resolved yet
+ "f": ast.Fun,
+ "L": ast.Lbl,
+ }
+
+ ast.Inspect(f, func(n ast.Node) bool {
+ if ident, ok := n.(*ast.Ident); ok {
+ obj := ident.Obj
+ if obj == nil {
+ if objects[ident.Name] != ast.Bad {
+ t.Errorf("no object for %s", ident.Name)
+ }
+ return true
+ }
+ if obj.Name != ident.Name {
+ t.Errorf("names don't match: obj.Name = %s, ident.Name = %s", obj.Name, ident.Name)
+ }
+ kind := objects[ident.Name]
+ if obj.Kind != kind {
+ t.Errorf("%s: obj.Kind = %s; want %s", ident.Name, obj.Kind, kind)
+ }
+ }
+ return true
+ })
+}
+
func TestUnresolved(t *testing.T) {
f, err := ParseFile(fset, "", `
package p
diff --git a/src/pkg/go/parser/performance_test.go b/src/pkg/go/parser/performance_test.go
new file mode 100644
index 000000000..f2732c0e2
--- /dev/null
+++ b/src/pkg/go/parser/performance_test.go
@@ -0,0 +1,30 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package parser
+
+import (
+ "go/token"
+ "io/ioutil"
+ "testing"
+)
+
+var src = readFile("parser.go")
+
+func readFile(filename string) []byte {
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ panic(err)
+ }
+ return data
+}
+
+func BenchmarkParse(b *testing.B) {
+ b.SetBytes(int64(len(src)))
+ for i := 0; i < b.N; i++ {
+ if _, err := ParseFile(token.NewFileSet(), "", src, ParseComments); err != nil {
+ b.Fatalf("benchmark failed due to parse error: %s", err)
+ }
+ }
+}
diff --git a/src/pkg/go/parser/short_test.go b/src/pkg/go/parser/short_test.go
index 238492bf3..c62f7e050 100644
--- a/src/pkg/go/parser/short_test.go
+++ b/src/pkg/go/parser/short_test.go
@@ -13,8 +13,10 @@ var valids = []string{
`package p;`,
`package p; import "fmt"; func f() { fmt.Println("Hello, World!") };`,
`package p; func f() { if f(T{}) {} };`,
- `package p; func f() { _ = (<-chan int)(x) };`,
- `package p; func f() { _ = (<-chan <-chan int)(x) };`,
+ `package p; func f() { _ = <-chan int(nil) };`,
+ `package p; func f() { _ = (<-chan int)(nil) };`,
+ `package p; func f() { _ = (<-chan <-chan int)(nil) };`,
+ `package p; func f() { _ = <-chan <-chan <-chan <-chan <-int(nil) };`,
`package p; func f(func() func() func());`,
`package p; func f(...T);`,
`package p; func f(float, ...int);`,
@@ -64,8 +66,11 @@ var invalids = []string{
`package p; var a = []int{[ /* ERROR "expected expression" */ ]int};`,
`package p; var a = ( /* ERROR "expected expression" */ []int);`,
`package p; var a = a[[ /* ERROR "expected expression" */ ]int:[]int];`,
- `package p; var a = <- /* ERROR "expected expression" */ chan int;`,
- `package p; func f() { select { case _ <- chan /* ERROR "expected expression" */ int: } };`,
+ `package p; var a = <- /* ERROR "expected expression" */ chan int;`,
+ `package p; func f() { select { case _ <- chan /* ERROR "expected expression" */ int: } };`,
+ `package p; func f() { _ = (<-<- /* ERROR "expected 'chan'" */ chan int)(nil) };`,
+ `package p; func f() { _ = (<-chan<-chan<-chan<-chan<-chan<- /* ERROR "expected channel type" */ int)(nil) };`,
+ `package p; func f() { var t []int; t /* ERROR "expected identifier on left side of :=" */ [0] := 0 };`,
}
func TestInvalid(t *testing.T) {
diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go
index f13f9a5a8..ee0bbf1ed 100644
--- a/src/pkg/go/printer/nodes.go
+++ b/src/pkg/go/printer/nodes.go
@@ -83,7 +83,7 @@ func (p *printer) setComment(g *ast.CommentGroup) {
// don't overwrite any pending comment in the p.comment cache
// (there may be a pending comment when a line comment is
// immediately followed by a lead comment with no other
- // tokens inbetween)
+ // tokens between)
if p.commentOffset == infinity {
p.nextComment() // get comment ready for use
}
@@ -203,7 +203,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
} else {
const r = 4 // threshold
ratio := float64(size) / float64(prevSize)
- useFF = ratio <= 1/r || r <= ratio
+ useFF = ratio <= 1.0/r || r <= ratio
}
}
@@ -307,7 +307,7 @@ func (p *printer) parameters(fields *ast.FieldList) {
p.print(blank)
}
// parameter type
- p.expr(par.Type)
+ p.expr(stripParensAlways(par.Type))
prevLine = parLineEnd
}
// if the closing ")" is on a separate line from the last parameter,
@@ -325,13 +325,18 @@ func (p *printer) parameters(fields *ast.FieldList) {
}
func (p *printer) signature(params, result *ast.FieldList) {
- p.parameters(params)
+ if params != nil {
+ p.parameters(params)
+ } else {
+ p.print(token.LPAREN, token.RPAREN)
+ }
n := result.NumFields()
if n > 0 {
+ // result != nil
p.print(blank)
if n == 1 && result.List[0].Names == nil {
// single anonymous result; no ()'s
- p.expr(result.List[0].Type)
+ p.expr(stripParensAlways(result.List[0].Type))
return
}
p.parameters(result)
@@ -725,7 +730,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
case *ast.FuncLit:
p.expr(x.Type)
- p.funcBody(x.Body, p.distance(x.Type.Pos(), p.pos), true)
+ p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body)
case *ast.ParenExpr:
if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
@@ -786,7 +791,14 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
if len(x.Args) > 1 {
depth++
}
- p.expr1(x.Fun, token.HighestPrec, depth)
+ if _, ok := x.Fun.(*ast.FuncType); ok {
+ // conversions to literal function types require parentheses around the type
+ p.print(token.LPAREN)
+ p.expr1(x.Fun, token.HighestPrec, depth)
+ p.print(token.RPAREN)
+ } else {
+ p.expr1(x.Fun, token.HighestPrec, depth)
+ }
p.print(x.Lparen, token.LPAREN)
if x.Ellipsis.IsValid() {
p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis)
@@ -848,9 +860,9 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
case ast.SEND | ast.RECV:
p.print(token.CHAN)
case ast.RECV:
- p.print(token.ARROW, token.CHAN)
+ p.print(token.ARROW, token.CHAN) // x.Arrow and x.Pos() are the same
case ast.SEND:
- p.print(token.CHAN, token.ARROW)
+ p.print(token.CHAN, x.Arrow, token.ARROW)
}
p.print(blank)
p.expr(x.Value)
@@ -877,30 +889,38 @@ func (p *printer) expr(x ast.Expr) {
// Print the statement list indented, but without a newline after the last statement.
// Extra line breaks between statements in the source are respected but at most one
// empty line is printed between statements.
-func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) {
- // TODO(gri): fix _indent code
- if _indent > 0 {
+func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) {
+ if nindent > 0 {
p.print(indent)
}
multiLine := false
- for i, s := range list {
- // _indent == 0 only for lists of switch/select case clauses;
- // in those cases each clause is a new section
- p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || _indent == 0 || multiLine)
- p.stmt(s, nextIsRBrace && i == len(list)-1)
- multiLine = p.isMultiLine(s)
- }
- if _indent > 0 {
+ i := 0
+ for _, s := range list {
+ // ignore empty statements (was issue 3466)
+ if _, isEmpty := s.(*ast.EmptyStmt); !isEmpty {
+ // _indent == 0 only for lists of switch/select case clauses;
+ // in those cases each clause is a new section
+ if len(p.output) > 0 {
+ // only print line break if we are not at the beginning of the output
+ // (i.e., we are not printing only a partial program)
+ p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || multiLine)
+ }
+ p.stmt(s, nextIsRBrace && i == len(list)-1)
+ multiLine = p.isMultiLine(s)
+ i++
+ }
+ }
+ if nindent > 0 {
p.print(unindent)
}
}
// block prints an *ast.BlockStmt; it always spans at least two lines.
-func (p *printer) block(s *ast.BlockStmt, indent int) {
- p.print(s.Pos(), token.LBRACE)
- p.stmtList(s.List, indent, true)
- p.linebreak(p.lineFor(s.Rbrace), 1, ignore, true)
- p.print(s.Rbrace, token.RBRACE)
+func (p *printer) block(b *ast.BlockStmt, nindent int) {
+ p.print(b.Lbrace, token.LBRACE)
+ p.stmtList(b.List, nindent, true)
+ p.linebreak(p.lineFor(b.Rbrace), 1, ignore, true)
+ p.print(b.Rbrace, token.RBRACE)
}
func isTypeName(x ast.Expr) bool {
@@ -939,6 +959,13 @@ func stripParens(x ast.Expr) ast.Expr {
return x
}
+func stripParensAlways(x ast.Expr) ast.Expr {
+ if x, ok := x.(*ast.ParenExpr); ok {
+ return stripParensAlways(x.X)
+ }
+ return x
+}
+
func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
p.print(blank)
needsBlank := false
@@ -1405,19 +1432,19 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
return
}
-func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
+// bodySize is like nodeSize but it is specialized for *ast.BlockStmt's.
+func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
pos1 := b.Pos()
pos2 := b.Rbrace
if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
// opening and closing brace are on different lines - don't make it a one-liner
- return false
+ return maxSize + 1
}
if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) {
// too many statements or there is a comment inside - don't make it a one-liner
- return false
+ return maxSize + 1
}
// otherwise, estimate body size
- const maxSize = 100
bodySize := 0
for i, s := range b.List {
if i > 0 {
@@ -1425,19 +1452,23 @@ func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
}
bodySize += p.nodeSize(s, maxSize)
}
- return headerSize+bodySize <= maxSize
+ return bodySize
}
-func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) {
+// adjBlock prints an "adjacent" block (e.g., a for-loop or function body) following
+// a header (e.g., a for-loop control clause or function signature) of given headerSize.
+// If the header's and block's size are "small enough" and the block is "simple enough",
+// the block is printed on the current line, without line breaks, spaced from the header
+// by sep. Otherwise the block's opening "{" is printed on the current line, followed by
+// lines for the block's statements and its closing "}".
+//
+func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
if b == nil {
return
}
- if p.isOneLineFunc(b, headerSize) {
- sep := vtab
- if isLit {
- sep = blank
- }
+ const maxSize = 100
+ if headerSize+p.bodySize(b, maxSize) <= maxSize {
p.print(sep, b.Lbrace, token.LBRACE)
if len(b.List) > 0 {
p.print(blank)
@@ -1453,17 +1484,20 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) {
return
}
- p.print(blank)
+ if sep != ignore {
+ p.print(blank) // always use blank
+ }
p.block(b, 1)
}
-// distance returns the column difference between from and to if both
-// are on the same line; if they are on different lines (or unknown)
-// the result is infinity.
-func (p *printer) distance(from0 token.Pos, to token.Position) int {
- from := p.posFor(from0)
- if from.IsValid() && to.IsValid() && from.Line == to.Line {
- return to.Column - from.Column
+// distanceFrom returns the column difference between from and p.pos (the current
+// estimated position) if both are on the same line; if they are on different lines
+// (or unknown) the result is infinity.
+func (p *printer) distanceFrom(from token.Pos) int {
+ if from.IsValid() && p.pos.IsValid() {
+ if f := p.posFor(from); f.Line == p.pos.Line {
+ return p.pos.Column - f.Column
+ }
}
return infinity
}
@@ -1477,7 +1511,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl) {
}
p.expr(d.Name)
p.signature(d.Type.Params, d.Type.Results)
- p.funcBody(d.Body, p.distance(d.Pos(), p.pos), false)
+ p.adjBlock(p.distanceFrom(d.Pos()), vtab, d.Body)
}
func (p *printer) decl(decl ast.Decl) {
@@ -1507,31 +1541,35 @@ func declToken(decl ast.Decl) (tok token.Token) {
return
}
-func (p *printer) file(src *ast.File) {
- p.setComment(src.Doc)
- p.print(src.Pos(), token.PACKAGE, blank)
- p.expr(src.Name)
-
- if len(src.Decls) > 0 {
- tok := token.ILLEGAL
- for _, d := range src.Decls {
- prev := tok
- tok = declToken(d)
- // if the declaration token changed (e.g., from CONST to TYPE)
- // or the next declaration has documentation associated with it,
- // print an empty line between top-level declarations
- // (because p.linebreak is called with the position of d, which
- // is past any documentation, the minimum requirement is satisfied
- // even w/o the extra getDoc(d) nil-check - leave it in case the
- // linebreak logic improves - there's already a TODO).
+func (p *printer) declList(list []ast.Decl) {
+ tok := token.ILLEGAL
+ for _, d := range list {
+ prev := tok
+ tok = declToken(d)
+ // If the declaration token changed (e.g., from CONST to TYPE)
+ // or the next declaration has documentation associated with it,
+ // print an empty line between top-level declarations.
+ // (because p.linebreak is called with the position of d, which
+ // is past any documentation, the minimum requirement is satisfied
+ // even w/o the extra getDoc(d) nil-check - leave it in case the
+ // linebreak logic improves - there's already a TODO).
+ if len(p.output) > 0 {
+ // only print line break if we are not at the beginning of the output
+ // (i.e., we are not printing only a partial program)
min := 1
if prev != tok || getDoc(d) != nil {
min = 2
}
p.linebreak(p.lineFor(d.Pos()), min, ignore, false)
- p.decl(d)
}
+ p.decl(d)
}
+}
+func (p *printer) file(src *ast.File) {
+ p.setComment(src.Doc)
+ p.print(src.Pos(), token.PACKAGE, blank)
+ p.expr(src.Name)
+ p.declList(src.Decls)
p.print(newline)
}
diff --git a/src/pkg/go/printer/performance_test.go b/src/pkg/go/printer/performance_test.go
index 0c6a4e71f..5b29affcb 100644
--- a/src/pkg/go/printer/performance_test.go
+++ b/src/pkg/go/printer/performance_test.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// This file implements a simple printer performance benchmark:
-// go test -bench=BenchmarkPrint
+// go test -bench=BenchmarkPrint
package printer
@@ -20,7 +20,7 @@ import (
var testfile *ast.File
func testprint(out io.Writer, file *ast.File) {
- if err := (&Config{TabIndent | UseSpaces, 8}).Fprint(out, fset, file); err != nil {
+ if err := (&Config{TabIndent | UseSpaces, 8, 0}).Fprint(out, fset, file); err != nil {
log.Fatalf("print error: %s", err)
}
}
diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go
index a027d32da..3c8d23e65 100644
--- a/src/pkg/go/printer/printer.go
+++ b/src/pkg/go/printer/printer.go
@@ -14,6 +14,7 @@ import (
"strconv"
"strings"
"text/tabwriter"
+ "unicode"
)
const (
@@ -164,15 +165,15 @@ func (p *printer) atLineBegin(pos token.Position) {
// write indentation
// use "hard" htabs - indentation columns
// must not be discarded by the tabwriter
- for i := 0; i < p.indent; i++ {
+ n := p.Config.Indent + p.indent // include base indentation
+ for i := 0; i < n; i++ {
p.output = append(p.output, '\t')
}
// update positions
- i := p.indent
- p.pos.Offset += i
- p.pos.Column += i
- p.out.Column += i
+ p.pos.Offset += n
+ p.pos.Column += n
+ p.out.Column += n
}
// writeByte writes ch n times to p.output and updates p.pos.
@@ -220,14 +221,6 @@ func (p *printer) writeString(pos token.Position, s string, isLit bool) {
// atLineBegin updates p.pos if there's indentation, but p.pos
// is the position of s.
p.pos = pos
- // reset state if the file changed
- // (used when printing merged ASTs of different files
- // e.g., the result of ast.MergePackageFiles)
- if p.last.IsValid() && p.last.Filename != pos.Filename {
- p.indent = 0
- p.mode = 0
- p.wsbuf = p.wsbuf[0:0]
- }
}
if isLit {
@@ -405,6 +398,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *as
// Split comment text into lines
// (using strings.Split(text, "\n") is significantly slower for
// this specific purpose, as measured with: go test -bench=Print)
+//
func split(text string) []string {
// count lines (comment text never ends in a newline)
n := 1
@@ -432,6 +426,7 @@ func split(text string) []string {
// Returns true if s contains only white space
// (only tabs and blanks can appear in the printer's context).
+//
func isBlank(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] > ' ' {
@@ -441,6 +436,7 @@ func isBlank(s string) bool {
return true
}
+// commonPrefix returns the common prefix of a and b.
func commonPrefix(a, b string) string {
i := 0
for i < len(a) && i < len(b) && a[i] == b[i] && (a[i] <= ' ' || a[i] == '*') {
@@ -449,11 +445,22 @@ func commonPrefix(a, b string) string {
return a[0:i]
}
+// trimRight returns s with trailing whitespace removed.
+func trimRight(s string) string {
+ return strings.TrimRightFunc(s, unicode.IsSpace)
+}
+
+// stripCommonPrefix removes a common prefix from /*-style comment lines (unless no
+// comment line is indented, all but the first line have some form of space prefix).
+// The prefix is computed using heuristics such that is likely that the comment
+// contents are nicely laid out after re-printing each line using the printer's
+// current indentation.
+//
func stripCommonPrefix(lines []string) {
- if len(lines) < 2 {
+ if len(lines) <= 1 {
return // at most one line - nothing to do
}
- // len(lines) >= 2
+ // len(lines) > 1
// The heuristic in this function tries to handle a few
// common patterns of /*-style comments: Comments where
@@ -479,7 +486,7 @@ func stripCommonPrefix(lines []string) {
for i, line := range lines[1 : len(lines)-1] {
switch {
case isBlank(line):
- lines[1+i] = "" // range starts at line 1
+ lines[1+i] = "" // range starts with lines[1]
case first:
prefix = commonPrefix(line, line)
first = false
@@ -544,9 +551,7 @@ func stripCommonPrefix(lines []string) {
}
// Shorten the computed common prefix by the length of
// suffix, if it is found as suffix of the prefix.
- if strings.HasSuffix(prefix, string(suffix)) {
- prefix = prefix[0 : len(prefix)-len(suffix)]
- }
+ prefix = strings.TrimSuffix(prefix, string(suffix))
}
}
@@ -570,9 +575,9 @@ func stripCommonPrefix(lines []string) {
}
// Remove the common prefix from all but the first and empty lines.
- for i, line := range lines[1:] {
- if len(line) != 0 {
- lines[1+i] = line[len(prefix):] // range starts at line 1
+ for i, line := range lines {
+ if i > 0 && line != "" {
+ lines[i] = line[len(prefix):]
}
}
}
@@ -605,13 +610,26 @@ func (p *printer) writeComment(comment *ast.Comment) {
// shortcut common case of //-style comments
if text[1] == '/' {
- p.writeString(pos, text, true)
+ p.writeString(pos, trimRight(text), true)
return
}
// for /*-style comments, print line by line and let the
// write function take care of the proper indentation
lines := split(text)
+
+ // The comment started in the first column but is going
+ // to be indented. For an idempotent result, add indentation
+ // to all lines such that they look like they were indented
+ // before - this will make sure the common prefix computation
+ // is the same independent of how many times formatting is
+ // applied (was issue 1835).
+ if pos.IsValid() && pos.Column == 1 && p.indent > 0 {
+ for i, line := range lines[1:] {
+ lines[1+i] = " " + line
+ }
+ }
+
stripCommonPrefix(lines)
// write comment lines, separated by formfeed,
@@ -622,7 +640,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
pos = p.pos
}
if len(line) > 0 {
- p.writeString(pos, line, true)
+ p.writeString(pos, trimRight(line), true)
}
}
}
@@ -1012,9 +1030,9 @@ func (p *printer) printNode(node interface{}) error {
case ast.Expr:
p.expr(n)
case ast.Stmt:
- // A labeled statement will un-indent to position the
- // label. Set indent to 1 so we don't get indent "underflow".
- if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt {
+ // A labeled statement will un-indent to position the label.
+ // Set p.indent to 1 so we don't get indent "underflow".
+ if _, ok := n.(*ast.LabeledStmt); ok {
p.indent = 1
}
p.stmt(n, false)
@@ -1022,6 +1040,17 @@ func (p *printer) printNode(node interface{}) error {
p.decl(n)
case ast.Spec:
p.spec(n, 1, false)
+ case []ast.Stmt:
+ // A labeled statement will un-indent to position the label.
+ // Set p.indent to 1 so we don't get indent "underflow".
+ for _, s := range n {
+ if _, ok := s.(*ast.LabeledStmt); ok {
+ p.indent = 1
+ }
+ }
+ p.stmtList(n, 0, false)
+ case []ast.Decl:
+ p.declList(n)
case *ast.File:
p.file(n)
default:
@@ -1140,7 +1169,7 @@ func (p *trimmer) Write(data []byte) (n int, err error) {
// ----------------------------------------------------------------------------
// Public interface
-// A Mode value is a set of flags (or 0). They coontrol printing.
+// A Mode value is a set of flags (or 0). They control printing.
type Mode uint
const (
@@ -1154,6 +1183,7 @@ const (
type Config struct {
Mode Mode // default: 0
Tabwidth int // default: 8
+ Indent int // default: 0 (all code is indented at least by this much)
}
// fprint implements Fprint and takes a nodesSizes map for setting up the printer state.
@@ -1198,7 +1228,7 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{
}
// flush tabwriter, if any
- if tw, _ := (output).(*tabwriter.Writer); tw != nil {
+ if tw, _ := output.(*tabwriter.Writer); tw != nil {
err = tw.Flush()
}
@@ -1215,8 +1245,8 @@ type CommentedNode struct {
// Fprint "pretty-prints" an AST node to output for a given configuration cfg.
// Position information is interpreted relative to the file set fset.
-// The node type must be *ast.File, *CommentedNode, or assignment-compatible
-// to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.
+// The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt,
+// or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.
//
func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
return cfg.fprint(output, fset, node, make(map[ast.Node]int))
diff --git a/src/pkg/go/printer/printer_test.go b/src/pkg/go/printer/printer_test.go
index 497d671f2..8454ac12b 100644
--- a/src/pkg/go/printer/printer_test.go
+++ b/src/pkg/go/printer/printer_test.go
@@ -6,7 +6,9 @@ package printer
import (
"bytes"
+ "errors"
"flag"
+ "fmt"
"go/ast"
"go/parser"
"go/token"
@@ -25,33 +27,28 @@ var update = flag.Bool("update", false, "update golden files")
var fset = token.NewFileSet()
-func lineString(text []byte, i int) string {
- i0 := i
- for i < len(text) && text[i] != '\n' {
- i++
- }
- return string(text[i0:i])
-}
-
type checkMode uint
const (
export checkMode = 1 << iota
rawFormat
+ idempotent
)
-func runcheck(t *testing.T, source, golden string, mode checkMode) {
- // parse source
- prog, err := parser.ParseFile(fset, source, nil, parser.ParseComments)
+// format parses src, prints the corresponding AST, verifies the resulting
+// src is syntactically correct, and returns the resulting src or an error
+// if any.
+func format(src []byte, mode checkMode) ([]byte, error) {
+ // parse src
+ f, err := parser.ParseFile(fset, "", src, parser.ParseComments)
if err != nil {
- t.Error(err)
- return
+ return nil, fmt.Errorf("parse: %s\n%s", err, src)
}
// filter exports if necessary
if mode&export != 0 {
- ast.FileExports(prog) // ignore result
- prog.Comments = nil // don't print comments that are not in AST
+ ast.FileExports(f) // ignore result
+ f.Comments = nil // don't print comments that are not in AST
}
// determine printer configuration
@@ -60,17 +57,72 @@ func runcheck(t *testing.T, source, golden string, mode checkMode) {
cfg.Mode |= RawFormat
}
- // format source
+ // print AST
var buf bytes.Buffer
- if err := cfg.Fprint(&buf, fset, prog); err != nil {
- t.Error(err)
+ if err := cfg.Fprint(&buf, fset, f); err != nil {
+ return nil, fmt.Errorf("print: %s", err)
}
- res := buf.Bytes()
- // formatted source must be valid
+ // make sure formated output is syntactically correct
+ res := buf.Bytes()
if _, err := parser.ParseFile(fset, "", res, 0); err != nil {
+ return nil, fmt.Errorf("re-parse: %s\n%s", err, buf.Bytes())
+ }
+
+ return res, nil
+}
+
+// lineAt returns the line in text starting at offset offs.
+func lineAt(text []byte, offs int) []byte {
+ i := offs
+ for i < len(text) && text[i] != '\n' {
+ i++
+ }
+ return text[offs:i]
+}
+
+// diff compares a and b.
+func diff(aname, bname string, a, b []byte) error {
+ var buf bytes.Buffer // holding long error message
+
+ // compare lengths
+ if len(a) != len(b) {
+ fmt.Fprintf(&buf, "\nlength changed: len(%s) = %d, len(%s) = %d", aname, len(a), bname, len(b))
+ }
+
+ // compare contents
+ line := 1
+ offs := 1
+ for i := 0; i < len(a) && i < len(b); i++ {
+ ch := a[i]
+ if ch != b[i] {
+ fmt.Fprintf(&buf, "\n%s:%d:%d: %s", aname, line, i-offs+1, lineAt(a, offs))
+ fmt.Fprintf(&buf, "\n%s:%d:%d: %s", bname, line, i-offs+1, lineAt(b, offs))
+ fmt.Fprintf(&buf, "\n\n")
+ break
+ }
+ if ch == '\n' {
+ line++
+ offs = i + 1
+ }
+ }
+
+ if buf.Len() > 0 {
+ return errors.New(buf.String())
+ }
+ return nil
+}
+
+func runcheck(t *testing.T, source, golden string, mode checkMode) {
+ src, err := ioutil.ReadFile(source)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ res, err := format(src, mode)
+ if err != nil {
t.Error(err)
- t.Logf("\n%s", res)
return
}
@@ -89,23 +141,19 @@ func runcheck(t *testing.T, source, golden string, mode checkMode) {
return
}
- // compare lengths
- if len(res) != len(gld) {
- t.Errorf("len = %d, expected %d (= len(%s))", len(res), len(gld), golden)
+ // formatted source and golden must be the same
+ if err := diff(source, golden, res, gld); err != nil {
+ t.Error(err)
+ return
}
- // compare contents
- for i, line, offs := 0, 1, 0; i < len(res) && i < len(gld); i++ {
- ch := res[i]
- if ch != gld[i] {
- t.Errorf("%s:%d:%d: %s", source, line, i-offs+1, lineString(res, offs))
- t.Errorf("%s:%d:%d: %s", golden, line, i-offs+1, lineString(gld, offs))
- t.Error()
- return
- }
- if ch == '\n' {
- line++
- offs = i + 1
+ if mode&idempotent != 0 {
+ // formatting golden must be idempotent
+ // (This is very difficult to achieve in general and for now
+ // it is only checked for files explicitly marked as such.)
+ res, err = format(gld, mode)
+ if err := diff(golden, fmt.Sprintf("format(%s)", golden), gld, res); err != nil {
+ t.Errorf("golden is not idempotent: %s", err)
}
}
}
@@ -142,15 +190,16 @@ type entry struct {
// Use go test -update to create/update the respective golden files.
var data = []entry{
- {"empty.input", "empty.golden", 0},
+ {"empty.input", "empty.golden", idempotent},
{"comments.input", "comments.golden", 0},
{"comments.input", "comments.x", export},
- {"linebreaks.input", "linebreaks.golden", 0},
- {"expressions.input", "expressions.golden", 0},
- {"expressions.input", "expressions.raw", rawFormat},
+ {"comments2.input", "comments2.golden", idempotent},
+ {"linebreaks.input", "linebreaks.golden", idempotent},
+ {"expressions.input", "expressions.golden", idempotent},
+ {"expressions.input", "expressions.raw", rawFormat | idempotent},
{"declarations.input", "declarations.golden", 0},
{"statements.input", "statements.golden", 0},
- {"slow.input", "slow.golden", 0},
+ {"slow.input", "slow.golden", idempotent},
}
func TestFiles(t *testing.T) {
@@ -248,7 +297,7 @@ func testComment(t *testing.T, f *ast.File, srclen int, comment *ast.Comment) {
}
}
-// Verify that the printer produces always produces a correct program
+// Verify that the printer produces a correct program
// even if the position information of comments introducing newlines
// is incorrect.
func TestBadComments(t *testing.T) {
@@ -385,28 +434,136 @@ func (t *t) foo(a, b, c int) int {
}
}
-// TextX is a skeleton test that can be filled in for debugging one-off cases.
-// Do not remove.
-func TestX(t *testing.T) {
- const src = `
-package p
-func _() {}
-`
- // parse original
- f, err := parser.ParseFile(fset, "src", src, parser.ParseComments)
+var decls = []string{
+ `import "fmt"`,
+ "const pi = 3.1415\nconst e = 2.71828\n\nvar x = pi",
+ "func sum(x, y int) int\t{ return x + y }",
+}
+
+func TestDeclLists(t *testing.T) {
+ for _, src := range decls {
+ file, err := parser.ParseFile(fset, "", "package p;"+src, parser.ParseComments)
+ if err != nil {
+ panic(err) // error in test
+ }
+
+ var buf bytes.Buffer
+ err = Fprint(&buf, fset, file.Decls) // only print declarations
+ if err != nil {
+ panic(err) // error in test
+ }
+
+ out := buf.String()
+ if out != src {
+ t.Errorf("\ngot : %q\nwant: %q\n", out, src)
+ }
+ }
+}
+
+var stmts = []string{
+ "i := 0",
+ "select {}\nvar a, b = 1, 2\nreturn a + b",
+ "go f()\ndefer func() {}()",
+}
+
+func TestStmtLists(t *testing.T) {
+ for _, src := range stmts {
+ file, err := parser.ParseFile(fset, "", "package p; func _() {"+src+"}", parser.ParseComments)
+ if err != nil {
+ panic(err) // error in test
+ }
+
+ var buf bytes.Buffer
+ err = Fprint(&buf, fset, file.Decls[0].(*ast.FuncDecl).Body.List) // only print statements
+ if err != nil {
+ panic(err) // error in test
+ }
+
+ out := buf.String()
+ if out != src {
+ t.Errorf("\ngot : %q\nwant: %q\n", out, src)
+ }
+ }
+}
+
+func TestBaseIndent(t *testing.T) {
+ // The testfile must not contain multi-line raw strings since those
+ // are not indented (because their values must not change) and make
+ // this test fail.
+ const filename = "printer.go"
+ src, err := ioutil.ReadFile(filename)
if err != nil {
- t.Fatal(err)
+ panic(err) // error in test
+ }
+
+ file, err := parser.ParseFile(fset, filename, src, 0)
+ if err != nil {
+ panic(err) // error in test
+ }
+
+ var buf bytes.Buffer
+ for indent := 0; indent < 4; indent++ {
+ buf.Reset()
+ (&Config{Tabwidth: tabwidth, Indent: indent}).Fprint(&buf, fset, file)
+ // all code must be indented by at least 'indent' tabs
+ lines := bytes.Split(buf.Bytes(), []byte{'\n'})
+ for i, line := range lines {
+ if len(line) == 0 {
+ continue // empty lines don't have indentation
+ }
+ n := 0
+ for j, b := range line {
+ if b != '\t' {
+ // end of indentation
+ n = j
+ break
+ }
+ }
+ if n < indent {
+ t.Errorf("line %d: got only %d tabs; want at least %d: %q", i, n, indent, line)
+ }
+ }
+ }
+}
+
+// TestFuncType tests that an ast.FuncType with a nil Params field
+// can be printed (per go/ast specification). Test case for issue 3870.
+func TestFuncType(t *testing.T) {
+ src := &ast.File{
+ Name: &ast.Ident{Name: "p"},
+ Decls: []ast.Decl{
+ &ast.FuncDecl{
+ Name: &ast.Ident{Name: "f"},
+ Type: &ast.FuncType{},
+ },
+ },
}
- // pretty-print original
var buf bytes.Buffer
- if err = (&Config{Mode: UseSpaces, Tabwidth: 8}).Fprint(&buf, fset, f); err != nil {
+ if err := Fprint(&buf, fset, src); err != nil {
t.Fatal(err)
}
+ got := buf.String()
- // parse pretty printed original
- if _, err := parser.ParseFile(fset, "", buf.Bytes(), 0); err != nil {
- t.Fatalf("%s\n%s", err, buf.Bytes())
+ const want = `package p
+
+func f()
+`
+
+ if got != want {
+ t.Fatalf("got:\n%s\nwant:\n%s\n", got, want)
}
+}
+// TextX is a skeleton test that can be filled in for debugging one-off cases.
+// Do not remove.
+func TestX(t *testing.T) {
+ const src = `
+package p
+func _() {}
+`
+ _, err := format([]byte(src), 0)
+ if err != nil {
+ t.Error(err)
+ }
}
diff --git a/src/pkg/go/printer/testdata/comments.golden b/src/pkg/go/printer/testdata/comments.golden
index d9aa2d82f..610a42a68 100644
--- a/src/pkg/go/printer/testdata/comments.golden
+++ b/src/pkg/go/printer/testdata/comments.golden
@@ -529,7 +529,7 @@ func _() {
}
func _() {
- var a = []int{1, 2}// jasldf
+ var a = []int{1, 2}// jasldf
_ = a
}
@@ -626,4 +626,13 @@ func _() {
var lflag bool // -l - disable line directives
}
+// Trailing white space in comments should be trimmed
+func _() {
+ // This comment has 4 blanks following that should be trimmed:
+ /* Each line of this comment has blanks or tabs following that should be trimmed:
+ line 2:
+ line 3:
+ */
+}
+
/* This comment is the last entry in this file. It must be printed and should be followed by a newline */
diff --git a/src/pkg/go/printer/testdata/comments.input b/src/pkg/go/printer/testdata/comments.input
index 6084b3fe4..d121dd4be 100644
--- a/src/pkg/go/printer/testdata/comments.input
+++ b/src/pkg/go/printer/testdata/comments.input
@@ -534,7 +534,7 @@ func _() {
}
func _() {
- var a = []int{1, 2, // jasldf
+ var a = []int{1, 2, // jasldf
}
_ = a
}
@@ -630,5 +630,13 @@ var vflag string // -v [y.output] - y.output file
var lflag bool // -l - disable line directives
}
+// Trailing white space in comments should be trimmed
+func _() {
+// This comment has 4 blanks following that should be trimmed:
+/* Each line of this comment has blanks or tabs following that should be trimmed:
+ line 2:
+ line 3:
+*/
+}
/* This comment is the last entry in this file. It must be printed and should be followed by a newline */
diff --git a/src/pkg/go/printer/testdata/comments2.golden b/src/pkg/go/printer/testdata/comments2.golden
new file mode 100644
index 000000000..d3b50bf3e
--- /dev/null
+++ b/src/pkg/go/printer/testdata/comments2.golden
@@ -0,0 +1,79 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This is a package for testing comment placement by go/printer.
+//
+package main
+
+// Test cases for idempotent comment formatting (was issue 1835).
+/*
+c1a
+*/
+/*
+ c1b
+*/
+/* foo
+c1c
+*/
+/* foo
+ c1d
+*/
+/*
+c1e
+foo */
+/*
+ c1f
+ foo */
+
+func f() {
+ /*
+ c2a
+ */
+ /*
+ c2b
+ */
+ /* foo
+ c2c
+ */
+ /* foo
+ c2d
+ */
+ /*
+ c2e
+ foo */
+ /*
+ c2f
+ foo */
+}
+
+func g() {
+ /*
+ c3a
+ */
+ /*
+ c3b
+ */
+ /* foo
+ c3c
+ */
+ /* foo
+ c3d
+ */
+ /*
+ c3e
+ foo */
+ /*
+ c3f
+ foo */
+}
+
+// Test case taken literally from issue 1835.
+func main() {
+ /*
+ prints test 5 times
+ */
+ for i := 0; i < 5; i++ {
+ println("test")
+ }
+}
diff --git a/src/pkg/go/printer/testdata/comments2.input b/src/pkg/go/printer/testdata/comments2.input
new file mode 100644
index 000000000..6f8c85c94
--- /dev/null
+++ b/src/pkg/go/printer/testdata/comments2.input
@@ -0,0 +1,79 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This is a package for testing comment placement by go/printer.
+//
+package main
+
+// Test cases for idempotent comment formatting (was issue 1835).
+/*
+c1a
+*/
+/*
+ c1b
+*/
+/* foo
+c1c
+*/
+/* foo
+ c1d
+*/
+/*
+c1e
+foo */
+/*
+ c1f
+ foo */
+
+func f() {
+/*
+c2a
+*/
+/*
+ c2b
+*/
+/* foo
+c2c
+*/
+/* foo
+ c2d
+*/
+/*
+c2e
+foo */
+/*
+ c2f
+ foo */
+}
+
+func g() {
+/*
+c3a
+*/
+/*
+ c3b
+*/
+/* foo
+c3c
+*/
+/* foo
+ c3d
+*/
+/*
+c3e
+foo */
+/*
+ c3f
+ foo */
+}
+
+// Test case taken literally from issue 1835.
+func main() {
+/*
+prints test 5 times
+*/
+ for i := 0; i < 5; i++ {
+ println("test")
+ }
+} \ No newline at end of file
diff --git a/src/pkg/go/printer/testdata/declarations.golden b/src/pkg/go/printer/testdata/declarations.golden
index 71ed32ed1..0ad72d349 100644
--- a/src/pkg/go/printer/testdata/declarations.golden
+++ b/src/pkg/go/printer/testdata/declarations.golden
@@ -654,6 +654,35 @@ var _ = map[int]int{
abcde: a, // align with previous line
}
+// alignment of map composite entries: test cases from issue 3965
+// aligned
+var _ = T1{
+ a: x,
+ b: y,
+ cccccccccccccccccccc: z,
+}
+
+// not aligned
+var _ = T2{
+ a: x,
+ b: y,
+ ccccccccccccccccccccc: z,
+}
+
+// aligned
+var _ = T3{
+ aaaaaaaaaaaaaaaaaaaa: x,
+ b: y,
+ c: z,
+}
+
+// not aligned
+var _ = T4{
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: x,
+ b: y,
+ c: z,
+}
+
func _() {
var _ = T{
a, // must introduce trailing comma
@@ -858,3 +887,28 @@ type _ interface {
r string,
x ...int)
}
+
+// omit superfluous parentheses in parameter lists
+func _(int)
+func _(int)
+func _(x int)
+func _(x int)
+func _(x, y int)
+func _(x, y int)
+
+func _() int
+func _() int
+func _() int
+
+func _() (x int)
+func _() (x int)
+func _() (x int)
+
+// special cases: some channel types require parentheses
+func _(x chan (<-chan int))
+func _(x chan (<-chan int))
+func _(x chan (<-chan int))
+
+func _(x chan<- (chan int))
+func _(x chan<- (chan int))
+func _(x chan<- (chan int))
diff --git a/src/pkg/go/printer/testdata/declarations.input b/src/pkg/go/printer/testdata/declarations.input
index d74cff25d..455c0c6c1 100644
--- a/src/pkg/go/printer/testdata/declarations.input
+++ b/src/pkg/go/printer/testdata/declarations.input
@@ -667,6 +667,35 @@ var _ = map[int]int{
abcde: a, // align with previous line
}
+// alignment of map composite entries: test cases from issue 3965
+// aligned
+var _ = T1{
+ a: x,
+ b: y,
+ cccccccccccccccccccc: z,
+}
+
+// not aligned
+var _ = T2{
+ a: x,
+ b: y,
+ ccccccccccccccccccccc: z,
+}
+
+// aligned
+var _ = T3{
+ aaaaaaaaaaaaaaaaaaaa: x,
+ b: y,
+ c: z,
+}
+
+// not aligned
+var _ = T4{
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: x,
+ b: y,
+ c: z,
+}
+
func _() {
var _ = T{
@@ -867,3 +896,28 @@ p, q,
r string,
x ...int)
}
+
+// omit superfluous parentheses in parameter lists
+func _((int))
+func _((((((int))))))
+func _(x (int))
+func _(x (((((int))))))
+func _(x, y (int))
+func _(x, y (((((int))))))
+
+func _() (int)
+func _() ((int))
+func _() ((((((int))))))
+
+func _() (x int)
+func _() (x (int))
+func _() (x (((((int))))))
+
+// special cases: some channel types require parentheses
+func _(x chan(<-chan int))
+func _(x (chan(<-chan int)))
+func _(x ((((chan(<-chan int))))))
+
+func _(x chan<-(chan int))
+func _(x (chan<-(chan int)))
+func _(x ((((chan<-(chan int))))))
diff --git a/src/pkg/go/printer/testdata/expressions.golden b/src/pkg/go/printer/testdata/expressions.golden
index 45fa4d97a..4291c557c 100644
--- a/src/pkg/go/printer/testdata/expressions.golden
+++ b/src/pkg/go/printer/testdata/expressions.golden
@@ -647,3 +647,18 @@ func _() {
a...,
)
}
+
+// Literal function types in conversions must be parenthesized;
+// for now go/parser accepts the unparenthesized form where it
+// is non-ambiguous.
+func _() {
+ // these conversions should be rewritten to look
+ // the same as the parenthesized conversions below
+ _ = (func())(nil)
+ _ = (func(x int) float)(nil)
+ _ = (func() func() func())(nil)
+
+ _ = (func())(nil)
+ _ = (func(x int) float)(nil)
+ _ = (func() func() func())(nil)
+}
diff --git a/src/pkg/go/printer/testdata/expressions.input b/src/pkg/go/printer/testdata/expressions.input
index f545c6605..1ec12a050 100644
--- a/src/pkg/go/printer/testdata/expressions.input
+++ b/src/pkg/go/printer/testdata/expressions.input
@@ -676,3 +676,18 @@ func _() {
a...,
)
}
+
+// Literal function types in conversions must be parenthesized;
+// for now go/parser accepts the unparenthesized form where it
+// is non-ambiguous.
+func _() {
+ // these conversions should be rewritten to look
+ // the same as the parenthesized conversions below
+ _ = func()()(nil)
+ _ = func(x int)(float)(nil)
+ _ = func() func() func()()(nil)
+
+ _ = (func()())(nil)
+ _ = (func(x int)(float))(nil)
+ _ = (func() func() func()())(nil)
+}
diff --git a/src/pkg/go/printer/testdata/expressions.raw b/src/pkg/go/printer/testdata/expressions.raw
index 87a4b0083..062900e07 100644
--- a/src/pkg/go/printer/testdata/expressions.raw
+++ b/src/pkg/go/printer/testdata/expressions.raw
@@ -647,3 +647,18 @@ func _() {
a...,
)
}
+
+// Literal function types in conversions must be parenthesized;
+// for now go/parser accepts the unparenthesized form where it
+// is non-ambiguous.
+func _() {
+ // these conversions should be rewritten to look
+ // the same as the parenthesized conversions below
+ _ = (func())(nil)
+ _ = (func(x int) float)(nil)
+ _ = (func() func() func())(nil)
+
+ _ = (func())(nil)
+ _ = (func(x int) float)(nil)
+ _ = (func() func() func())(nil)
+}
diff --git a/src/pkg/go/printer/testdata/statements.golden b/src/pkg/go/printer/testdata/statements.golden
index 4d70617bf..3b298f95e 100644
--- a/src/pkg/go/printer/testdata/statements.golden
+++ b/src/pkg/go/printer/testdata/statements.golden
@@ -241,7 +241,7 @@ func _() {
}
}
-// Formatting of for-statement headers.
+// Formatting of for-statement headers for single-line for-loops.
func _() {
for {
}
@@ -279,6 +279,86 @@ func _() {
} // no parens printed
}
+// Formatting of for-statement headers for multi-line for-loops.
+func _() {
+ for {
+ }
+ for expr {
+ }
+ for expr {
+ } // no parens printed
+ for {
+ } // no semicolons printed
+ for x := expr; ; {
+ use(x)
+ }
+ for expr {
+ } // no semicolons printed
+ for expr {
+ } // no semicolons and parens printed
+ for ; ; expr = false {
+ }
+ for x := expr; expr; {
+ use(x)
+ }
+ for x := expr; ; expr = false {
+ use(x)
+ }
+ for ; expr; expr = false {
+ }
+ for x := expr; expr; expr = false {
+ use(x)
+ }
+ for x := range []int{} {
+ use(x)
+ }
+ for x := range []int{} {
+ use(x)
+ } // no parens printed
+}
+
+// Formatting of selected short single- and multi-line statements.
+func _() {
+ if cond {
+ }
+ if cond {
+ } // multiple lines
+ if cond {
+ } else {
+ } // else clause always requires multiple lines
+
+ for {
+ }
+ for i := 0; i < len(a); 1++ {
+ }
+ for i := 0; i < len(a); 1++ {
+ a[i] = i
+ }
+ for i := 0; i < len(a); 1++ {
+ a[i] = i
+ } // multiple lines
+
+ for i := range a {
+ }
+ for i := range a {
+ a[i] = i
+ }
+ for i := range a {
+ a[i] = i
+ } // multiple lines
+
+ go func() {
+ for {
+ a <- <-b
+ }
+ }()
+ defer func() {
+ if x := recover(); x != nil {
+ err = fmt.Sprintf("error: %s", x.msg)
+ }
+ }()
+}
+
// Don't remove mandatory parentheses around composite literals in control clauses.
func _() {
// strip parentheses - no composite literals or composite literals don't start with a type name
@@ -527,3 +607,29 @@ AVeryLongLabelThatShouldNotAffectFormatting:
// There should be a single empty line before this comment.
MoreCode()
}
+
+// Formatting of empty statements.
+func _() {
+
+}
+
+func _() {
+}
+
+func _() {
+}
+
+func _() {
+ f()
+}
+
+func _() {
+L:
+ ;
+}
+
+func _() {
+L:
+ ;
+ f()
+}
diff --git a/src/pkg/go/printer/testdata/statements.input b/src/pkg/go/printer/testdata/statements.input
index bd03bc98b..e7fcc0e54 100644
--- a/src/pkg/go/printer/testdata/statements.input
+++ b/src/pkg/go/printer/testdata/statements.input
@@ -223,7 +223,7 @@ func _() {
}
-// Formatting of for-statement headers.
+// Formatting of for-statement headers for single-line for-loops.
func _() {
for{}
for expr {}
@@ -235,14 +235,70 @@ func _() {
for; ; expr = false {}
for x :=expr; expr; {use(x)}
for x := expr;; expr=false {use(x)}
- for;expr;expr =false {
- }
+ for;expr;expr =false {}
for x := expr;expr;expr = false { use(x) }
for x := range []int{} { use(x) }
for x := range (([]int{})) { use(x) } // no parens printed
}
+// Formatting of for-statement headers for multi-line for-loops.
+func _() {
+ for{
+ }
+ for expr {
+ }
+ for (expr) {
+ } // no parens printed
+ for;;{
+ } // no semicolons printed
+ for x :=expr;; {use( x)
+ }
+ for; expr;{
+ } // no semicolons printed
+ for; ((expr));{
+ } // no semicolons and parens printed
+ for; ; expr = false {
+ }
+ for x :=expr; expr; {use(x)
+ }
+ for x := expr;; expr=false {use(x)
+ }
+ for;expr;expr =false {
+ }
+ for x := expr;expr;expr = false {
+ use(x)
+ }
+ for x := range []int{} {
+ use(x) }
+ for x := range (([]int{})) {
+ use(x) } // no parens printed
+}
+
+
+// Formatting of selected short single- and multi-line statements.
+func _() {
+ if cond {}
+ if cond {
+ } // multiple lines
+ if cond {} else {} // else clause always requires multiple lines
+
+ for {}
+ for i := 0; i < len(a); 1++ {}
+ for i := 0; i < len(a); 1++ { a[i] = i }
+ for i := 0; i < len(a); 1++ { a[i] = i
+ } // multiple lines
+
+ for i := range a {}
+ for i := range a { a[i] = i }
+ for i := range a { a[i] = i
+ } // multiple lines
+
+ go func() { for { a <- <-b } }()
+ defer func() { if x := recover(); x != nil { err = fmt.Sprintf("error: %s", x.msg) } }()
+}
+
+
// Don't remove mandatory parentheses around composite literals in control clauses.
func _() {
// strip parentheses - no composite literals or composite literals don't start with a type name
@@ -468,3 +524,27 @@ AVeryLongLabelThatShouldNotAffectFormatting:
// There should be a single empty line before this comment.
MoreCode()
}
+
+
+// Formatting of empty statements.
+func _() {
+ ;;;;;;;;;;;;;;;;;;;;;;;;;
+}
+
+func _() {;;;;;;;;;;;;;;;;;;;;;;;;;
+}
+
+func _() {;;;;;;;;;;;;;;;;;;;;;;;;;}
+
+func _() {
+f();;;;;;;;;;;;;;;;;;;;;;;;;
+}
+
+func _() {
+L:;;;;;;;;;;;;
+}
+
+func _() {
+L:;;;;;;;;;;;;
+ f()
+}
diff --git a/src/pkg/go/scanner/errors.go b/src/pkg/go/scanner/errors.go
index 8a75a9650..22de69c3c 100644
--- a/src/pkg/go/scanner/errors.go
+++ b/src/pkg/go/scanner/errors.go
@@ -120,7 +120,7 @@ func PrintError(w io.Writer, err error) {
for _, e := range list {
fmt.Fprintf(w, "%s\n", e)
}
- } else {
+ } else if err != nil {
fmt.Fprintf(w, "%s\n", err)
}
}
diff --git a/src/pkg/go/scanner/scanner.go b/src/pkg/go/scanner/scanner.go
index da508747a..3322c58b3 100644
--- a/src/pkg/go/scanner/scanner.go
+++ b/src/pkg/go/scanner/scanner.go
@@ -81,7 +81,7 @@ func (s *Scanner) next() {
}
}
-// A mode value is set of flags (or 0).
+// A mode value is a set of flags (or 0).
// They control scanner behavior.
//
type Mode uint
@@ -125,6 +125,9 @@ func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode
s.ErrorCount = 0
s.next()
+ if s.ch == '\uFEFF' {
+ s.next() // ignore BOM
+ }
}
func (s *Scanner) error(offs int, msg string) {
@@ -157,11 +160,15 @@ func (s *Scanner) interpretLineComment(text []byte) {
func (s *Scanner) scanComment() string {
// initial '/' already consumed; s.ch == '/' || s.ch == '*'
offs := s.offset - 1 // position of initial '/'
+ hasCR := false
if s.ch == '/' {
//-style comment
s.next()
for s.ch != '\n' && s.ch >= 0 {
+ if s.ch == '\r' {
+ hasCR = true
+ }
s.next()
}
if offs == s.lineOffset {
@@ -175,6 +182,9 @@ func (s *Scanner) scanComment() string {
s.next()
for s.ch >= 0 {
ch := s.ch
+ if ch == '\r' {
+ hasCR = true
+ }
s.next()
if ch == '*' && s.ch == '/' {
s.next()
@@ -185,7 +195,12 @@ func (s *Scanner) scanComment() string {
s.error(offs, "comment not terminated")
exit:
- return string(s.src[offs:s.offset])
+ lit := s.src[offs:s.offset]
+ if hasCR {
+ lit = stripCR(lit)
+ }
+
+ return string(lit)
}
func (s *Scanner) findLineEnd() bool {
@@ -378,7 +393,7 @@ func (s *Scanner) scanEscape(quote rune) {
for ; i > 0 && s.ch != quote && s.ch >= 0; i-- {
s.next()
}
- if x > max || 0xd800 <= x && x < 0xe000 {
+ if x > max || 0xD800 <= x && x < 0xE000 {
s.error(offs, "escape sequence is invalid Unicode code point")
}
}
@@ -527,6 +542,8 @@ func (s *Scanner) switch4(tok0, tok1 token.Token, ch2 rune, tok2, tok3 token.Tok
// token.IMAG, token.CHAR, token.STRING) or token.COMMENT, the literal string
// has the corresponding value.
//
+// If the returned token is a keyword, the literal string is the keyword.
+//
// If the returned token is token.SEMICOLON, the corresponding
// literal string is ";" if the semicolon was present in the source,
// and "\n" if the semicolon was inserted because of a newline or
@@ -560,12 +577,18 @@ scanAgain:
switch ch := s.ch; {
case isLetter(ch):
lit = s.scanIdentifier()
- tok = token.Lookup(lit)
- switch tok {
- case token.IDENT, token.BREAK, token.CONTINUE, token.FALLTHROUGH, token.RETURN:
+ if len(lit) > 1 {
+ // keywords are longer than one letter - avoid lookup otherwise
+ tok = token.Lookup(lit)
+ switch tok {
+ case token.IDENT, token.BREAK, token.CONTINUE, token.FALLTHROUGH, token.RETURN:
+ insertSemi = true
+ }
+ } else {
insertSemi = true
+ tok = token.IDENT
}
- case digitVal(ch) < 10:
+ case '0' <= ch && ch <= '9':
insertSemi = true
tok, lit = s.scanNumber(false)
default:
@@ -598,7 +621,7 @@ scanAgain:
case ':':
tok = s.switch2(token.COLON, token.DEFINE)
case '.':
- if digitVal(s.ch) < 10 {
+ if '0' <= s.ch && s.ch <= '9' {
insertSemi = true
tok, lit = s.scanNumber(true)
} else if s.ch == '.' {
diff --git a/src/pkg/go/scanner/scanner_test.go b/src/pkg/go/scanner/scanner_test.go
index 06223e23b..1c19053e6 100644
--- a/src/pkg/go/scanner/scanner_test.go
+++ b/src/pkg/go/scanner/scanner_test.go
@@ -6,6 +6,7 @@ package scanner
import (
"go/token"
+ "io/ioutil"
"os"
"path/filepath"
"runtime"
@@ -43,12 +44,16 @@ var tokens = [...]elt{
// Special tokens
{token.COMMENT, "/* a comment */", special},
{token.COMMENT, "// a comment \n", special},
+ {token.COMMENT, "/*\r*/", special},
+ {token.COMMENT, "//\r\n", special},
// Identifiers and basic type literals
{token.IDENT, "foobar", literal},
{token.IDENT, "a۰۱۸", literal},
{token.IDENT, "foo६४", literal},
{token.IDENT, "bar9876", literal},
+ {token.IDENT, "ŝ", literal}, // was bug (issue 4000)
+ {token.IDENT, "ŝfoo", literal}, // was bug (issue 4000)
{token.INT, "0", literal},
{token.INT, "1", literal},
{token.INT, "123456789012345678890", literal},
@@ -214,8 +219,6 @@ func checkPos(t *testing.T, lit string, p token.Pos, expected token.Position) {
// Verify that calling Scan() provides the correct results.
func TestScan(t *testing.T) {
- // make source
- src_linecount := newlineCount(string(source))
whitespace_linecount := newlineCount(whitespace)
// error handler
@@ -226,59 +229,81 @@ func TestScan(t *testing.T) {
// verify scan
var s Scanner
s.Init(fset.AddFile("", fset.Base(), len(source)), source, eh, ScanComments|dontInsertSemis)
- index := 0
- // epos is the expected position
+
+ // set up expected position
epos := token.Position{
Filename: "",
Offset: 0,
Line: 1,
Column: 1,
}
+
+ index := 0
for {
pos, tok, lit := s.Scan()
- if lit == "" {
- // no literal value for non-literal tokens
- lit = tok.String()
+
+ // check position
+ if tok == token.EOF {
+ // correction for EOF
+ epos.Line = newlineCount(string(source))
+ epos.Column = 2
}
+ checkPos(t, lit, pos, epos)
+
+ // check token
e := elt{token.EOF, "", special}
if index < len(tokens) {
e = tokens[index]
+ index++
}
- if tok == token.EOF {
- lit = "<EOF>"
- epos.Line = src_linecount
- epos.Column = 2
- }
- checkPos(t, lit, pos, epos)
if tok != e.tok {
t.Errorf("bad token for %q: got %s, expected %s", lit, tok, e.tok)
}
- if e.tok.IsLiteral() {
- // no CRs in raw string literals
- elit := e.lit
- if elit[0] == '`' {
- elit = string(stripCR([]byte(elit)))
- epos.Offset += len(e.lit) - len(lit) // correct position
- }
- if lit != elit {
- t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, elit)
- }
- }
+
+ // check token class
if tokenclass(tok) != e.class {
t.Errorf("bad class for %q: got %d, expected %d", lit, tokenclass(tok), e.class)
}
- epos.Offset += len(lit) + len(whitespace)
- epos.Line += newlineCount(lit) + whitespace_linecount
- if tok == token.COMMENT && lit[1] == '/' {
- // correct for unaccounted '/n' in //-style comment
- epos.Offset++
- epos.Line++
+
+ // check literal
+ elit := ""
+ switch e.tok {
+ case token.COMMENT:
+ // no CRs in comments
+ elit = string(stripCR([]byte(e.lit)))
+ //-style comment literal doesn't contain newline
+ if elit[1] == '/' {
+ elit = elit[0 : len(elit)-1]
+ }
+ case token.IDENT:
+ elit = e.lit
+ case token.SEMICOLON:
+ elit = ";"
+ default:
+ if e.tok.IsLiteral() {
+ // no CRs in raw string literals
+ elit = e.lit
+ if elit[0] == '`' {
+ elit = string(stripCR([]byte(elit)))
+ }
+ } else if e.tok.IsKeyword() {
+ elit = e.lit
+ }
+ }
+ if lit != elit {
+ t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, elit)
}
- index++
+
if tok == token.EOF {
break
}
+
+ // update position
+ epos.Offset += len(e.lit) + len(whitespace)
+ epos.Line += newlineCount(e.lit) + whitespace_linecount
+
}
+
if s.ErrorCount != 0 {
t.Errorf("found %d errors", s.ErrorCount)
}
@@ -321,6 +346,7 @@ var lines = []string{
// # indicates a semicolon present in the source
// $ indicates an automatically inserted semicolon
"",
+ "\ufeff#;", // first BOM is ignored
"#;",
"foo$\n",
"123$\n",
@@ -521,7 +547,7 @@ func TestLineComments(t *testing.T) {
}
}
-// Verify that initializing the same scanner more then once works correctly.
+// Verify that initializing the same scanner more than once works correctly.
func TestInit(t *testing.T) {
var s Scanner
@@ -669,6 +695,7 @@ var errors = []struct {
{"0X", token.INT, 0, "illegal hexadecimal number"},
{"\"abc\x00def\"", token.STRING, 4, "illegal character NUL"},
{"\"abc\x80def\"", token.STRING, 4, "illegal UTF-8 encoding"},
+ {"\ufeff\ufeff", token.ILLEGAL, 3, "illegal character U+FEFF"}, // only first BOM is ignored
}
func TestScanErrors(t *testing.T) {
@@ -683,7 +710,7 @@ func BenchmarkScan(b *testing.B) {
file := fset.AddFile("", fset.Base(), len(source))
var s Scanner
b.StartTimer()
- for i := b.N - 1; i >= 0; i-- {
+ for i := 0; i < b.N; i++ {
s.Init(file, source, nil, ScanComments)
for {
_, tok, _ := s.Scan()
@@ -693,3 +720,26 @@ func BenchmarkScan(b *testing.B) {
}
}
}
+
+func BenchmarkScanFile(b *testing.B) {
+ b.StopTimer()
+ const filename = "scanner.go"
+ src, err := ioutil.ReadFile(filename)
+ if err != nil {
+ panic(err)
+ }
+ fset := token.NewFileSet()
+ file := fset.AddFile(filename, fset.Base(), len(src))
+ b.SetBytes(int64(len(src)))
+ var s Scanner
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ s.Init(file, src, nil, ScanComments)
+ for {
+ _, tok, _ := s.Scan()
+ if tok == token.EOF {
+ break
+ }
+ }
+ }
+}
diff --git a/src/pkg/go/token/position.go b/src/pkg/go/token/position.go
index 647d1b770..f5d999561 100644
--- a/src/pkg/go/token/position.go
+++ b/src/pkg/go/token/position.go
@@ -76,7 +76,7 @@ type Pos int
// associated with it, and NoPos().IsValid() is false. NoPos is always
// smaller than any other Pos value. The corresponding Position value
// for NoPos is the zero value for Position.
-//
+//
const NoPos Pos = 0
// IsValid returns true if the position is valid.
@@ -295,9 +295,9 @@ type FileSet struct {
// NewFileSet creates a new file set.
func NewFileSet() *FileSet {
- s := new(FileSet)
- s.base = 1 // 0 == NoPos
- return s
+ return &FileSet{
+ base: 1, // 0 == NoPos
+ }
}
// Base returns the minimum base offset that must be provided to
@@ -347,7 +347,7 @@ func (s *FileSet) AddFile(filename string, base, size int) *File {
// Iterate calls f for the files in the file set in the order they were added
// until f returns false.
-//
+//
func (s *FileSet) Iterate(f func(*File) bool) {
for i := 0; ; i++ {
var file *File
@@ -367,8 +367,10 @@ func searchFiles(a []*File, x int) int {
}
func (s *FileSet) file(p Pos) *File {
+ s.mutex.RLock()
// common case: p is in last file
if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size {
+ s.mutex.RUnlock()
return f
}
// p is not in last file - search all files
@@ -376,10 +378,14 @@ func (s *FileSet) file(p Pos) *File {
f := s.files[i]
// f.base <= int(p) by definition of searchFiles
if int(p) <= f.base+f.size {
- s.last = f
+ s.mutex.RUnlock()
+ s.mutex.Lock()
+ s.last = f // race is ok - s.last is only a cache
+ s.mutex.Unlock()
return f
}
}
+ s.mutex.RUnlock()
return nil
}
@@ -389,9 +395,7 @@ func (s *FileSet) file(p Pos) *File {
//
func (s *FileSet) File(p Pos) (f *File) {
if p != NoPos {
- s.mutex.RLock()
f = s.file(p)
- s.mutex.RUnlock()
}
return
}
@@ -399,11 +403,9 @@ func (s *FileSet) File(p Pos) (f *File) {
// Position converts a Pos in the fileset into a general Position.
func (s *FileSet) Position(p Pos) (pos Position) {
if p != NoPos {
- s.mutex.RLock()
if f := s.file(p); f != nil {
pos = f.position(p)
}
- s.mutex.RUnlock()
}
return
}
diff --git a/src/pkg/go/token/position_test.go b/src/pkg/go/token/position_test.go
index 160107df4..1d36c2226 100644
--- a/src/pkg/go/token/position_test.go
+++ b/src/pkg/go/token/position_test.go
@@ -6,6 +6,8 @@ package token
import (
"fmt"
+ "math/rand"
+ "sync"
"testing"
)
@@ -179,3 +181,52 @@ func TestFiles(t *testing.T) {
}
}
}
+
+// FileSet.File should return nil if Pos is past the end of the FileSet.
+func TestFileSetPastEnd(t *testing.T) {
+ fset := NewFileSet()
+ for _, test := range tests {
+ fset.AddFile(test.filename, fset.Base(), test.size)
+ }
+ if f := fset.File(Pos(fset.Base())); f != nil {
+ t.Errorf("expected nil, got %v", f)
+ }
+}
+
+func TestFileSetCacheUnlikely(t *testing.T) {
+ fset := NewFileSet()
+ offsets := make(map[string]int)
+ for _, test := range tests {
+ offsets[test.filename] = fset.Base()
+ fset.AddFile(test.filename, fset.Base(), test.size)
+ }
+ for file, pos := range offsets {
+ f := fset.File(Pos(pos))
+ if f.Name() != file {
+ t.Errorf("expecting %q at position %d, got %q", file, pos, f.Name())
+ }
+ }
+}
+
+// issue 4345. Test concurrent use of FileSet.Pos does not trigger a
+// race in the FileSet position cache.
+func TestFileSetRace(t *testing.T) {
+ fset := NewFileSet()
+ for i := 0; i < 100; i++ {
+ fset.AddFile(fmt.Sprintf("file-%d", i), fset.Base(), 1031)
+ }
+ max := int32(fset.Base())
+ var stop sync.WaitGroup
+ r := rand.New(rand.NewSource(7))
+ for i := 0; i < 2; i++ {
+ r := rand.New(rand.NewSource(r.Int63()))
+ stop.Add(1)
+ go func() {
+ for i := 0; i < 1000; i++ {
+ fset.Position(Pos(r.Int31n(max)))
+ }
+ stop.Done()
+ }()
+ }
+ stop.Wait()
+}
diff --git a/src/pkg/go/types/api.go b/src/pkg/go/types/api.go
new file mode 100644
index 000000000..13b453faa
--- /dev/null
+++ b/src/pkg/go/types/api.go
@@ -0,0 +1,105 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package types declares the data structures for representing
+// Go types and implements typechecking of package files.
+//
+// WARNING: THE TYPES API IS SUBJECT TO CHANGE.
+//
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// A Context specifies the supporting context for type checking.
+// An empty Context is a ready-to-use default context.
+type Context struct {
+ // If Error != nil, it is called with each error found
+ // during type checking. The error strings of errors with
+ // detailed position information are formatted as follows:
+ // filename:line:column: message
+ Error func(err error)
+
+ // If Ident != nil, it is called for each identifier id
+ // denoting an Object in the files provided to Check, and
+ // obj is the denoted object.
+ // Ident is not called for fields and methods in struct or
+ // interface types or composite literals, or for blank (_)
+ // or dot (.) identifiers in dot-imports.
+ // TODO(gri) Consider making Fields and Methods ordinary
+ // Objects - than we could lift this restriction.
+ Ident func(id *ast.Ident, obj Object)
+
+ // If Expr != nil, it is called exactly once for each expression x
+ // that is type-checked: typ is the expression type, and val is the
+ // value if x is constant, val is nil otherwise.
+ //
+ // If x is a literal value (constant, composite literal), typ is always
+ // the dynamic type of x (never an interface type). Otherwise, typ is x's
+ // static type (possibly an interface type).
+ //
+ // Constants are represented as follows:
+ //
+ // bool -> bool
+ // numeric -> int64, *big.Int, *big.Rat, Complex
+ // string -> string
+ // nil -> NilType
+ //
+ // Constant values are normalized, that is, they are represented
+ // using the "smallest" possible type that can represent the value.
+ // For instance, 1.0 is represented as an int64 because it can be
+ // represented accurately as an int64.
+ Expr func(x ast.Expr, typ Type, val interface{})
+
+ // If Import != nil, it is called for each imported package.
+ // Otherwise, GcImporter is called.
+ Import Importer
+
+ // If Alignof != nil, it is called to determine the alignment
+ // of the given type. Otherwise DefaultAlignmentof is called.
+ // Alignof must implement the alignment guarantees required by
+ // the spec.
+ Alignof func(Type) int64
+
+ // If Offsetsof != nil, it is called to determine the offsets
+ // of the given struct fields, in bytes. Otherwise DefaultOffsetsof
+ // is called. Offsetsof must implement the offset guarantees
+ // required by the spec.
+ Offsetsof func(fields []*Field) []int64
+
+ // If Sizeof != nil, it is called to determine the size of the
+ // given type. Otherwise, DefaultSizeof is called. Sizeof must
+ // implement the size guarantees required by the spec.
+ Sizeof func(Type) int64
+}
+
+// An Importer resolves import paths to Package objects.
+// The imports map records the packages already imported,
+// indexed by package id (canonical import path).
+// An Importer must determine the canonical import path and
+// check the map to see if it is already present in the imports map.
+// If so, the Importer can return the map entry. Otherwise, the
+// Importer should load the package data for the given path into
+// a new *Package, record pkg in the imports map, and then
+// return pkg.
+type Importer func(imports map[string]*Package, path string) (pkg *Package, err error)
+
+// Check resolves and typechecks a set of package files within the given
+// context. It returns the package and the first error encountered, if
+// any. If the context's Error handler is nil, Check terminates as soon
+// as the first error is encountered; otherwise it continues until the
+// entire package is checked. If there are errors, the package may be
+// only partially type-checked, and the resulting package may be incomplete
+// (missing objects, imports, etc.).
+func (ctxt *Context) Check(fset *token.FileSet, files []*ast.File) (*Package, error) {
+ return check(ctxt, fset, files)
+}
+
+// Check is shorthand for ctxt.Check where ctxt is a default (empty) context.
+func Check(fset *token.FileSet, files []*ast.File) (*Package, error) {
+ var ctxt Context
+ return ctxt.Check(fset, files)
+}
diff --git a/src/pkg/go/types/builtins.go b/src/pkg/go/types/builtins.go
new file mode 100644
index 000000000..ad9259118
--- /dev/null
+++ b/src/pkg/go/types/builtins.go
@@ -0,0 +1,455 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements typechecking of builtin function calls.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// builtin typechecks a built-in call. The built-in type is bin, and iota is the current
+// value of iota or -1 if iota doesn't have a value in the current context. The result
+// of the call is returned via x. If the call has type errors, the returned x is marked
+// as invalid (x.mode == invalid).
+//
+func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota int) {
+ args := call.Args
+ id := bin.id
+
+ // declare before goto's
+ var arg0 ast.Expr // first argument, if present
+
+ // check argument count
+ n := len(args)
+ msg := ""
+ if n < bin.nargs {
+ msg = "not enough"
+ } else if !bin.isVariadic && n > bin.nargs {
+ msg = "too many"
+ }
+ if msg != "" {
+ check.invalidOp(call.Pos(), msg+" arguments for %s (expected %d, found %d)", call, bin.nargs, n)
+ goto Error
+ }
+
+ // common case: evaluate first argument if present;
+ // if it is an expression, x has the expression value
+ if n > 0 {
+ arg0 = args[0]
+ switch id {
+ case _Make, _New, _Print, _Println, _Offsetof, _Trace:
+ // respective cases below do the work
+ default:
+ // argument must be an expression
+ check.expr(x, arg0, nil, iota)
+ if x.mode == invalid {
+ goto Error
+ }
+ }
+ }
+
+ switch id {
+ case _Append:
+ if _, ok := underlying(x.typ).(*Slice); !ok {
+ check.invalidArg(x.pos(), "%s is not a typed slice", x)
+ goto Error
+ }
+ resultTyp := x.typ
+ for _, arg := range args[1:] {
+ check.expr(x, arg, nil, iota)
+ if x.mode == invalid {
+ goto Error
+ }
+ // TODO(gri) check assignability
+ }
+ x.mode = value
+ x.typ = resultTyp
+
+ case _Cap, _Len:
+ mode := invalid
+ var val interface{}
+ switch typ := implicitArrayDeref(underlying(x.typ)).(type) {
+ case *Basic:
+ if isString(typ) && id == _Len {
+ if x.mode == constant {
+ mode = constant
+ val = int64(len(x.val.(string)))
+ } else {
+ mode = value
+ }
+ }
+
+ case *Array:
+ mode = value
+ // spec: "The expressions len(s) and cap(s) are constants
+ // if the type of s is an array or pointer to an array and
+ // the expression s does not contain channel receives or
+ // function calls; in this case s is not evaluated."
+ if !check.containsCallsOrReceives(arg0) {
+ mode = constant
+ val = typ.Len
+ }
+
+ case *Slice, *Chan:
+ mode = value
+
+ case *Map:
+ if id == _Len {
+ mode = value
+ }
+ }
+
+ if mode == invalid {
+ check.invalidArg(x.pos(), "%s for %s", x, bin.name)
+ goto Error
+ }
+ x.mode = mode
+ x.typ = Typ[Int]
+ x.val = val
+
+ case _Close:
+ ch, ok := underlying(x.typ).(*Chan)
+ if !ok {
+ check.invalidArg(x.pos(), "%s is not a channel", x)
+ goto Error
+ }
+ if ch.Dir&ast.SEND == 0 {
+ check.invalidArg(x.pos(), "%s must not be a receive-only channel", x)
+ goto Error
+ }
+ x.mode = novalue
+
+ case _Complex:
+ if !check.complexArg(x) {
+ goto Error
+ }
+
+ var y operand
+ check.expr(&y, args[1], nil, iota)
+ if y.mode == invalid {
+ goto Error
+ }
+ if !check.complexArg(&y) {
+ goto Error
+ }
+
+ check.convertUntyped(x, y.typ)
+ if x.mode == invalid {
+ goto Error
+ }
+ check.convertUntyped(&y, x.typ)
+ if y.mode == invalid {
+ goto Error
+ }
+
+ if !IsIdentical(x.typ, y.typ) {
+ check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
+ goto Error
+ }
+
+ typ := underlying(x.typ).(*Basic)
+ if x.mode == constant && y.mode == constant {
+ x.val = binaryOpConst(x.val, toImagConst(y.val), token.ADD, typ)
+ } else {
+ x.mode = value
+ }
+
+ switch typ.Kind {
+ case Float32:
+ x.typ = Typ[Complex64]
+ case Float64:
+ x.typ = Typ[Complex128]
+ case UntypedInt, UntypedRune, UntypedFloat:
+ x.typ = Typ[UntypedComplex]
+ default:
+ check.invalidArg(x.pos(), "float32 or float64 arguments expected")
+ goto Error
+ }
+
+ case _Copy:
+ var y operand
+ check.expr(&y, args[1], nil, iota)
+ if y.mode == invalid {
+ goto Error
+ }
+
+ var dst, src Type
+ if t, ok := underlying(x.typ).(*Slice); ok {
+ dst = t.Elt
+ }
+ switch t := underlying(y.typ).(type) {
+ case *Basic:
+ if isString(y.typ) {
+ src = Typ[Byte]
+ }
+ case *Slice:
+ src = t.Elt
+ }
+
+ if dst == nil || src == nil {
+ check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y)
+ goto Error
+ }
+
+ if !IsIdentical(dst, src) {
+ check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
+ goto Error
+ }
+
+ x.mode = value
+ x.typ = Typ[Int]
+
+ case _Delete:
+ m, ok := underlying(x.typ).(*Map)
+ if !ok {
+ check.invalidArg(x.pos(), "%s is not a map", x)
+ goto Error
+ }
+ check.expr(x, args[1], nil, iota)
+ if x.mode == invalid {
+ goto Error
+ }
+ if !x.isAssignable(check.ctxt, m.Key) {
+ check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.Key)
+ goto Error
+ }
+ x.mode = novalue
+
+ case _Imag, _Real:
+ if !isComplex(x.typ) {
+ check.invalidArg(x.pos(), "%s must be a complex number", x)
+ goto Error
+ }
+ if x.mode == constant {
+ // nothing to do for x.val == 0
+ if !isZeroConst(x.val) {
+ c := x.val.(Complex)
+ if id == _Real {
+ x.val = c.Re
+ } else {
+ x.val = c.Im
+ }
+ }
+ } else {
+ x.mode = value
+ }
+ k := Invalid
+ switch underlying(x.typ).(*Basic).Kind {
+ case Complex64:
+ k = Float32
+ case Complex128:
+ k = Float64
+ case UntypedComplex:
+ k = UntypedFloat
+ default:
+ unreachable()
+ }
+ x.typ = Typ[k]
+
+ case _Make:
+ resultTyp := check.typ(arg0, false)
+ if resultTyp == Typ[Invalid] {
+ goto Error
+ }
+ var min int // minimum number of arguments
+ switch underlying(resultTyp).(type) {
+ case *Slice:
+ min = 2
+ case *Map, *Chan:
+ min = 1
+ default:
+ check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0)
+ goto Error
+ }
+ if n := len(args); n < min || min+1 < n {
+ check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, n)
+ goto Error
+ }
+ var sizes []interface{} // constant integer arguments, if any
+ for _, arg := range args[1:] {
+ check.expr(x, arg, nil, iota)
+ if x.isInteger(check.ctxt) {
+ if x.mode == constant {
+ if isNegConst(x.val) {
+ check.invalidArg(x.pos(), "%s must not be negative", x)
+ // safe to continue
+ } else {
+ sizes = append(sizes, x.val) // x.val >= 0
+ }
+ }
+ } else {
+ check.invalidArg(x.pos(), "%s must be an integer", x)
+ // safe to continue
+ }
+ }
+ if len(sizes) == 2 && compareConst(sizes[0], sizes[1], token.GTR) {
+ check.invalidArg(args[1].Pos(), "length and capacity swapped")
+ // safe to continue
+ }
+ x.mode = variable
+ x.typ = resultTyp
+
+ case _New:
+ resultTyp := check.typ(arg0, false)
+ if resultTyp == Typ[Invalid] {
+ goto Error
+ }
+ x.mode = variable
+ x.typ = &Pointer{Base: resultTyp}
+
+ case _Panic:
+ x.mode = novalue
+
+ case _Print, _Println:
+ for _, arg := range args {
+ check.expr(x, arg, nil, -1)
+ if x.mode == invalid {
+ goto Error
+ }
+ }
+ x.mode = novalue
+
+ case _Recover:
+ x.mode = value
+ x.typ = new(Interface)
+
+ case _Alignof:
+ x.mode = constant
+ x.val = check.ctxt.alignof(x.typ)
+ x.typ = Typ[Uintptr]
+
+ case _Offsetof:
+ arg, ok := unparen(arg0).(*ast.SelectorExpr)
+ if !ok {
+ check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0)
+ goto Error
+ }
+ check.expr(x, arg.X, nil, -1)
+ if x.mode == invalid {
+ goto Error
+ }
+ sel := arg.Sel.Name
+ res := lookupField(x.typ, QualifiedName{check.pkg, arg.Sel.Name})
+ if res.index == nil {
+ check.invalidArg(x.pos(), "%s has no single field %s", x, sel)
+ goto Error
+ }
+ offs := check.ctxt.offsetof(deref(x.typ), res.index)
+ if offs < 0 {
+ check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, x)
+ goto Error
+ }
+ x.mode = constant
+ x.val = offs
+ x.typ = Typ[Uintptr]
+
+ case _Sizeof:
+ x.mode = constant
+ x.val = check.ctxt.sizeof(x.typ)
+ x.typ = Typ[Uintptr]
+
+ case _Assert:
+ // assert(pred) causes a typechecker error if pred is false.
+ // The result of assert is the value of pred if there is no error.
+ // Note: assert is only available in self-test mode.
+ if x.mode != constant || !isBoolean(x.typ) {
+ check.invalidArg(x.pos(), "%s is not a boolean constant", x)
+ goto Error
+ }
+ pred, ok := x.val.(bool)
+ if !ok {
+ check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x)
+ goto Error
+ }
+ if !pred {
+ check.errorf(call.Pos(), "%s failed", call)
+ // compile-time assertion failure - safe to continue
+ }
+
+ case _Trace:
+ // trace(x, y, z, ...) dumps the positions, expressions, and
+ // values of its arguments. The result of trace is the value
+ // of the first argument.
+ // Note: trace is only available in self-test mode.
+ if len(args) == 0 {
+ check.dump("%s: trace() without arguments", call.Pos())
+ x.mode = novalue
+ x.expr = call
+ return
+ }
+ var t operand
+ x1 := x
+ for _, arg := range args {
+ check.rawExpr(x1, arg, nil, iota, true) // permit trace for types, e.g.: new(trace(T))
+ check.dump("%s: %s", x1.pos(), x1)
+ x1 = &t // use incoming x only for first argument
+ }
+
+ default:
+ check.invalidAST(call.Pos(), "unknown builtin id %d", id)
+ goto Error
+ }
+
+ x.expr = call
+ return
+
+Error:
+ x.mode = invalid
+ x.expr = call
+}
+
+// implicitArrayDeref returns A if typ is of the form *A and A is an array;
+// otherwise it returns typ.
+//
+func implicitArrayDeref(typ Type) Type {
+ if p, ok := typ.(*Pointer); ok {
+ if a, ok := underlying(p.Base).(*Array); ok {
+ return a
+ }
+ }
+ return typ
+}
+
+// containsCallsOrReceives reports if x contains function calls or channel receives.
+// Expects that x was type-checked already.
+//
+func (check *checker) containsCallsOrReceives(x ast.Expr) (found bool) {
+ ast.Inspect(x, func(x ast.Node) bool {
+ switch x := x.(type) {
+ case *ast.CallExpr:
+ // calls and conversions look the same
+ if !check.conversions[x] {
+ found = true
+ }
+ case *ast.UnaryExpr:
+ if x.Op == token.ARROW {
+ found = true
+ }
+ }
+ return !found // no need to continue if found
+ })
+ return
+}
+
+// unparen removes any parentheses surrounding an expression and returns
+// the naked expression.
+//
+func unparen(x ast.Expr) ast.Expr {
+ if p, ok := x.(*ast.ParenExpr); ok {
+ return unparen(p.X)
+ }
+ return x
+}
+
+func (check *checker) complexArg(x *operand) bool {
+ t, _ := underlying(x.typ).(*Basic)
+ if t != nil && (t.Info&IsFloat != 0 || t.Kind == UntypedInt || t.Kind == UntypedRune) {
+ return true
+ }
+ check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x)
+ return false
+}
diff --git a/src/pkg/go/types/check.go b/src/pkg/go/types/check.go
new file mode 100644
index 000000000..f7b87e30c
--- /dev/null
+++ b/src/pkg/go/types/check.go
@@ -0,0 +1,507 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements the Check function, which typechecks a package.
+
+package types
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+)
+
+// debugging support
+const (
+ debug = true // leave on during development
+ trace = false // turn on for detailed type resolution traces
+)
+
+type checker struct {
+ ctxt *Context
+ fset *token.FileSet
+ files []*ast.File
+
+ // lazily initialized
+ pkg *Package // current package
+ firsterr error // first error encountered
+ idents map[*ast.Ident]Object // maps identifiers to their unique object
+ objects map[*ast.Object]Object // maps *ast.Objects to their unique object
+ initspecs map[*ast.ValueSpec]*ast.ValueSpec // "inherited" type and initialization expressions for constant declarations
+ methods map[*TypeName]*Scope // maps type names to associated methods
+ conversions map[*ast.CallExpr]bool // set of type-checked conversions (to distinguish from calls)
+
+ // untyped expressions
+ // TODO(gri): Consider merging the untyped and constants map. Should measure
+ // the ratio between untyped non-constant and untyped constant expressions
+ // to make an informed decision.
+ untyped map[ast.Expr]*Basic // map of expressions of untyped type
+ constants map[ast.Expr]interface{} // map of untyped constant expressions; each key also appears in untyped
+ shiftOps map[ast.Expr]bool // map of lhs shift operands with delayed type-checking
+
+ // functions
+ funclist []function // list of functions/methods with correct signatures and non-empty bodies
+ funcsig *Signature // signature of currently typechecked function
+ pos []token.Pos // stack of expr positions; debugging support, used if trace is set
+}
+
+func (check *checker) register(id *ast.Ident, obj Object) {
+ // When an expression is evaluated more than once (happens
+ // in rare cases, e.g. for statement expressions, see
+ // comment in stmt.go), the object has been registered
+ // before. Don't do anything in that case.
+ if alt := check.idents[id]; alt != nil {
+ assert(alt == obj)
+ return
+ }
+ check.idents[id] = obj
+ if f := check.ctxt.Ident; f != nil {
+ f(id, obj)
+ }
+}
+
+// lookup returns the unique Object denoted by the identifier.
+// For identifiers without assigned *ast.Object, it uses the
+// checker.idents map; for identifiers with an *ast.Object it
+// uses the checker.objects map.
+//
+// TODO(gri) Once identifier resolution is done entirely by
+// the typechecker, only the idents map is needed.
+//
+func (check *checker) lookup(ident *ast.Ident) Object {
+ obj := check.idents[ident]
+ astObj := ident.Obj
+
+ if obj != nil {
+ assert(astObj == nil || check.objects[astObj] == nil || check.objects[astObj] == obj)
+ return obj
+ }
+
+ if astObj == nil {
+ return nil
+ }
+
+ if obj = check.objects[astObj]; obj == nil {
+ obj = newObj(check.pkg, astObj)
+ check.objects[astObj] = obj
+ }
+ check.register(ident, obj)
+
+ return obj
+}
+
+type function struct {
+ obj *Func // for debugging/tracing only
+ sig *Signature
+ body *ast.BlockStmt
+}
+
+// later adds a function with non-empty body to the list of functions
+// that need to be processed after all package-level declarations
+// are typechecked.
+//
+func (check *checker) later(f *Func, sig *Signature, body *ast.BlockStmt) {
+ // functions implemented elsewhere (say in assembly) have no body
+ if body != nil {
+ check.funclist = append(check.funclist, function{f, sig, body})
+ }
+}
+
+func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) {
+ assert(check.lookup(ident) == nil) // identifier already declared or resolved
+ check.register(ident, obj)
+ if ident.Name != "_" {
+ if alt := scope.Insert(obj); alt != nil {
+ prevDecl := ""
+ if pos := alt.GetPos(); pos.IsValid() {
+ prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos))
+ }
+ check.errorf(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
+ }
+ }
+}
+
+func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spec *ast.ValueSpec, iota int) {
+ if len(lhs) == 0 {
+ check.invalidAST(pos, "missing lhs in declaration")
+ return
+ }
+
+ // determine type for all of lhs, if any
+ // (but only set it for the object we typecheck!)
+ var typ Type
+ if spec.Type != nil {
+ typ = check.typ(spec.Type, false)
+ }
+
+ // len(lhs) > 0
+ rhs := spec.Values
+ if len(lhs) == len(rhs) {
+ // check only lhs and rhs corresponding to obj
+ var l, r ast.Expr
+ for i, name := range lhs {
+ if check.lookup(name) == obj {
+ l = lhs[i]
+ r = rhs[i]
+ break
+ }
+ }
+ assert(l != nil)
+ switch obj := obj.(type) {
+ case *Const:
+ obj.Type = typ
+ case *Var:
+ obj.Type = typ
+ default:
+ unreachable()
+ }
+ check.assign1to1(l, r, nil, true, iota)
+ return
+ }
+
+ // there must be a type or initialization expressions
+ if typ == nil && len(rhs) == 0 {
+ check.invalidAST(pos, "missing type or initialization expression")
+ typ = Typ[Invalid]
+ }
+
+ // if we have a type, mark all of lhs
+ if typ != nil {
+ for _, name := range lhs {
+ switch obj := check.lookup(name).(type) {
+ case *Const:
+ obj.Type = typ
+ case *Var:
+ obj.Type = typ
+ default:
+ unreachable()
+ }
+ }
+ }
+
+ // check initial values, if any
+ if len(rhs) > 0 {
+ // TODO(gri) should try to avoid this conversion
+ lhx := make([]ast.Expr, len(lhs))
+ for i, e := range lhs {
+ lhx[i] = e
+ }
+ check.assignNtoM(lhx, rhs, true, iota)
+ }
+}
+
+// object typechecks an object by assigning it a type.
+//
+func (check *checker) object(obj Object, cycleOk bool) {
+ switch obj := obj.(type) {
+ case *Package:
+ // nothing to do
+ case *Const:
+ if obj.Type != nil {
+ return // already checked
+ }
+ // The obj.Val field for constants is initialized to its respective
+ // iota value by the parser.
+ // The object's fields can be in one of the following states:
+ // Type != nil => the constant value is Val
+ // Type == nil => the constant is not typechecked yet, and Val can be:
+ // Val is int => Val is the value of iota for this declaration
+ // Val == nil => the object's expression is being evaluated
+ if obj.Val == nil {
+ check.errorf(obj.GetPos(), "illegal cycle in initialization of %s", obj.Name)
+ obj.Type = Typ[Invalid]
+ return
+ }
+ spec := obj.spec
+ iota := obj.Val.(int)
+ obj.Val = nil // mark obj as "visited" for cycle detection
+ // determine spec for type and initialization expressions
+ init := spec
+ if len(init.Values) == 0 {
+ init = check.initspecs[spec]
+ }
+ check.valueSpec(spec.Pos(), obj, spec.Names, init, iota)
+
+ case *Var:
+ if obj.Type != nil {
+ return // already checked
+ }
+ if obj.visited {
+ check.errorf(obj.GetPos(), "illegal cycle in initialization of %s", obj.Name)
+ obj.Type = Typ[Invalid]
+ return
+ }
+ switch d := obj.decl.(type) {
+ case *ast.Field:
+ unreachable() // function parameters are always typed when collected
+ case *ast.ValueSpec:
+ obj.visited = true
+ check.valueSpec(d.Pos(), obj, d.Names, d, 0)
+ case *ast.AssignStmt:
+ // If we reach here, we have a short variable declaration
+ // where the rhs didn't typecheck and thus the lhs has no
+ // types.
+ obj.visited = true
+ obj.Type = Typ[Invalid]
+ default:
+ unreachable() // see also function newObj
+ }
+
+ case *TypeName:
+ if obj.Type != nil {
+ return // already checked
+ }
+ typ := &NamedType{Obj: obj}
+ obj.Type = typ // "mark" object so recursion terminates
+ typ.Underlying = underlying(check.typ(obj.spec.Type, cycleOk))
+ // typecheck associated method signatures
+ if scope := check.methods[obj]; scope != nil {
+ switch t := typ.Underlying.(type) {
+ case *Struct:
+ // struct fields must not conflict with methods
+ for _, f := range t.Fields {
+ if m := scope.Lookup(f.Name); m != nil {
+ check.errorf(m.GetPos(), "type %s has both field and method named %s", obj.Name, f.Name)
+ // ok to continue
+ }
+ }
+ case *Interface:
+ // methods cannot be associated with an interface type
+ for _, m := range scope.Entries {
+ recv := m.(*Func).decl.Recv.List[0].Type
+ check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name)
+ // ok to continue
+ }
+ }
+ // typecheck method signatures
+ var methods []*Method
+ for _, obj := range scope.Entries {
+ m := obj.(*Func)
+ sig := check.typ(m.decl.Type, cycleOk).(*Signature)
+ params, _ := check.collectParams(m.decl.Recv, false)
+ sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
+ m.Type = sig
+ methods = append(methods, &Method{QualifiedName{check.pkg, m.Name}, sig})
+ check.later(m, sig, m.decl.Body)
+ }
+ typ.Methods = methods
+ delete(check.methods, obj) // we don't need this scope anymore
+ }
+
+ case *Func:
+ if obj.Type != nil {
+ return // already checked
+ }
+ fdecl := obj.decl
+ // methods are typechecked when their receivers are typechecked
+ if fdecl.Recv == nil {
+ sig := check.typ(fdecl.Type, cycleOk).(*Signature)
+ if obj.Name == "init" && (len(sig.Params) != 0 || len(sig.Results) != 0) {
+ check.errorf(fdecl.Pos(), "func init must have no arguments and no return values")
+ // ok to continue
+ }
+ obj.Type = sig
+ check.later(obj, sig, fdecl.Body)
+ }
+
+ default:
+ unreachable()
+ }
+}
+
+// assocInitvals associates "inherited" initialization expressions
+// with the corresponding *ast.ValueSpec in the check.initspecs map
+// for constant declarations without explicit initialization expressions.
+//
+func (check *checker) assocInitvals(decl *ast.GenDecl) {
+ var last *ast.ValueSpec
+ for _, s := range decl.Specs {
+ if s, ok := s.(*ast.ValueSpec); ok {
+ if len(s.Values) > 0 {
+ last = s
+ } else {
+ check.initspecs[s] = last
+ }
+ }
+ }
+ if last == nil {
+ check.invalidAST(decl.Pos(), "no initialization values provided")
+ }
+}
+
+// assocMethod associates a method declaration with the respective
+// receiver base type. meth.Recv must exist.
+//
+func (check *checker) assocMethod(meth *ast.FuncDecl) {
+ // The receiver type is one of the following (enforced by parser):
+ // - *ast.Ident
+ // - *ast.StarExpr{*ast.Ident}
+ // - *ast.BadExpr (parser error)
+ typ := meth.Recv.List[0].Type
+ if ptr, ok := typ.(*ast.StarExpr); ok {
+ typ = ptr.X
+ }
+ // determine receiver base type name
+ ident, ok := typ.(*ast.Ident)
+ if !ok {
+ // not an identifier - parser reported error already
+ return // ignore this method
+ }
+ // determine receiver base type object
+ var tname *TypeName
+ if obj := check.lookup(ident); obj != nil {
+ obj, ok := obj.(*TypeName)
+ if !ok {
+ check.errorf(ident.Pos(), "%s is not a type", ident.Name)
+ return // ignore this method
+ }
+ if obj.spec == nil {
+ check.errorf(ident.Pos(), "cannot define method on non-local type %s", ident.Name)
+ return // ignore this method
+ }
+ tname = obj
+ } else {
+ // identifier not declared/resolved - parser reported error already
+ return // ignore this method
+ }
+ // declare method in receiver base type scope
+ scope := check.methods[tname]
+ if scope == nil {
+ scope = new(Scope)
+ check.methods[tname] = scope
+ }
+ check.declareIdent(scope, meth.Name, &Func{Pkg: check.pkg, Name: meth.Name.Name, decl: meth})
+}
+
+func (check *checker) decl(decl ast.Decl) {
+ switch d := decl.(type) {
+ case *ast.BadDecl:
+ // ignore
+ case *ast.GenDecl:
+ for _, spec := range d.Specs {
+ switch s := spec.(type) {
+ case *ast.ImportSpec:
+ // nothing to do (handled by check.resolve)
+ case *ast.ValueSpec:
+ for _, name := range s.Names {
+ check.object(check.lookup(name), false)
+ }
+ case *ast.TypeSpec:
+ check.object(check.lookup(s.Name), false)
+ default:
+ check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
+ }
+ }
+ case *ast.FuncDecl:
+ // methods are checked when their respective base types are checked
+ if d.Recv != nil {
+ return
+ }
+ obj := check.lookup(d.Name)
+ // Initialization functions don't have an object associated with them
+ // since they are not in any scope. Create a dummy object for them.
+ if d.Name.Name == "init" {
+ assert(obj == nil) // all other functions should have an object
+ obj = &Func{Pkg: check.pkg, Name: d.Name.Name, decl: d}
+ check.register(d.Name, obj)
+ }
+ check.object(obj, false)
+ default:
+ check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
+ }
+}
+
+// A bailout panic is raised to indicate early termination.
+type bailout struct{}
+
+func check(ctxt *Context, fset *token.FileSet, files []*ast.File) (pkg *Package, err error) {
+ // initialize checker
+ check := checker{
+ ctxt: ctxt,
+ fset: fset,
+ files: files,
+ idents: make(map[*ast.Ident]Object),
+ objects: make(map[*ast.Object]Object),
+ initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec),
+ methods: make(map[*TypeName]*Scope),
+ conversions: make(map[*ast.CallExpr]bool),
+ untyped: make(map[ast.Expr]*Basic),
+ constants: make(map[ast.Expr]interface{}),
+ shiftOps: make(map[ast.Expr]bool),
+ }
+
+ // set results and handle panics
+ defer func() {
+ pkg = check.pkg
+ switch p := recover().(type) {
+ case nil, bailout:
+ // normal return or early exit
+ err = check.firsterr
+ default:
+ // unexpected panic: don't crash clients
+ if debug {
+ check.dump("INTERNAL PANIC: %v", p)
+ panic(p)
+ }
+ // TODO(gri) add a test case for this scenario
+ err = fmt.Errorf("types internal error: %v", p)
+ }
+ }()
+
+ // resolve identifiers
+ imp := ctxt.Import
+ if imp == nil {
+ imp = GcImport
+ }
+ methods := check.resolve(imp)
+
+ // associate methods with types
+ for _, m := range methods {
+ check.assocMethod(m)
+ }
+
+ // typecheck all declarations
+ for _, f := range check.files {
+ for _, d := range f.Decls {
+ check.decl(d)
+ }
+ }
+
+ // typecheck all function/method bodies
+ // (funclist may grow when checking statements - do not use range clause!)
+ for i := 0; i < len(check.funclist); i++ {
+ f := check.funclist[i]
+ if trace {
+ s := "<function literal>"
+ if f.obj != nil {
+ s = f.obj.Name
+ }
+ fmt.Println("---", s)
+ }
+ check.funcsig = f.sig
+ check.stmtList(f.body.List)
+ }
+
+ // remaining untyped expressions must indeed be untyped
+ if debug {
+ for x, typ := range check.untyped {
+ if !isUntyped(typ) {
+ check.dump("%s: %s (type %s) is not untyped", x.Pos(), x, typ)
+ panic(0)
+ }
+ }
+ }
+
+ // notify client of any untyped types left
+ // TODO(gri) Consider doing this before and
+ // after function body checking for smaller
+ // map size and more immediate feedback.
+ if ctxt.Expr != nil {
+ for x, typ := range check.untyped {
+ ctxt.Expr(x, typ, check.constants[x])
+ }
+ }
+
+ return
+}
diff --git a/src/pkg/go/types/check_test.go b/src/pkg/go/types/check_test.go
new file mode 100644
index 000000000..470f3a1a9
--- /dev/null
+++ b/src/pkg/go/types/check_test.go
@@ -0,0 +1,259 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements a typechecker test harness. The packages specified
+// in tests are typechecked. Error messages reported by the typechecker are
+// compared against the error messages expected in the test files.
+//
+// Expected errors are indicated in the test files by putting a comment
+// of the form /* ERROR "rx" */ immediately following an offending token.
+// The harness will verify that an error matching the regular expression
+// rx is reported at that source position. Consecutive comments may be
+// used to indicate multiple errors for the same token position.
+//
+// For instance, the following test file indicates that a "not declared"
+// error should be reported for the undeclared variable x:
+//
+// package p
+// func f() {
+// _ = x /* ERROR "not declared" */ + 1
+// }
+
+package types
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "io/ioutil"
+ "os"
+ "regexp"
+ "testing"
+)
+
+var listErrors = flag.Bool("list", false, "list errors")
+
+// The test filenames do not end in .go so that they are invisible
+// to gofmt since they contain comments that must not change their
+// positions relative to surrounding tokens.
+
+var tests = []struct {
+ name string
+ files []string
+}{
+ {"decls0", []string{"testdata/decls0.src"}},
+ {"decls1", []string{"testdata/decls1.src"}},
+ {"decls2", []string{"testdata/decls2a.src", "testdata/decls2b.src"}},
+ {"decls3", []string{"testdata/decls3.src"}},
+ {"const0", []string{"testdata/const0.src"}},
+ {"expr0", []string{"testdata/expr0.src"}},
+ {"expr1", []string{"testdata/expr1.src"}},
+ {"expr2", []string{"testdata/expr2.src"}},
+ {"expr3", []string{"testdata/expr3.src"}},
+ {"builtins", []string{"testdata/builtins.src"}},
+ {"conversions", []string{"testdata/conversions.src"}},
+ {"stmt0", []string{"testdata/stmt0.src"}},
+}
+
+var fset = token.NewFileSet()
+
+func getFile(filename string) (file *token.File) {
+ fset.Iterate(func(f *token.File) bool {
+ if f.Name() == filename {
+ file = f
+ return false // end iteration
+ }
+ return true
+ })
+ return file
+}
+
+// Positioned errors are of the form filename:line:column: message .
+var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`)
+
+// splitError splits an error's error message into a position string
+// and the actual error message. If there's no position information,
+// pos is the empty string, and msg is the entire error message.
+//
+func splitError(err error) (pos, msg string) {
+ msg = err.Error()
+ if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 {
+ pos = m[1]
+ msg = m[2]
+ }
+ return
+}
+
+func parseFiles(t *testing.T, testname string, filenames []string) ([]*ast.File, []error) {
+ var files []*ast.File
+ var errlist []error
+ for _, filename := range filenames {
+ file, err := parser.ParseFile(fset, filename, nil, parser.DeclarationErrors|parser.AllErrors)
+ if file == nil {
+ t.Fatalf("%s: could not parse file %s", testname, filename)
+ }
+ files = append(files, file)
+ if err != nil {
+ if list, _ := err.(scanner.ErrorList); len(list) > 0 {
+ for _, err := range list {
+ errlist = append(errlist, err)
+ }
+ } else {
+ errlist = append(errlist, err)
+ }
+ }
+ }
+ return files, errlist
+}
+
+// ERROR comments must be of the form /* ERROR "rx" */ and rx is
+// a regular expression that matches the expected error message.
+//
+var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`)
+
+// errMap collects the regular expressions of ERROR comments found
+// in files and returns them as a map of error positions to error messages.
+//
+func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string {
+ errmap := make(map[string][]string)
+
+ for _, file := range files {
+ filename := fset.Position(file.Package).Filename
+ src, err := ioutil.ReadFile(filename)
+ if err != nil {
+ t.Fatalf("%s: could not read %s", testname, filename)
+ }
+
+ var s scanner.Scanner
+ // file was parsed already - do not add it again to the file
+ // set otherwise the position information returned here will
+ // not match the position information collected by the parser
+ s.Init(getFile(filename), src, nil, scanner.ScanComments)
+ var prev string // position string of last non-comment, non-semicolon token
+
+ scanFile:
+ for {
+ pos, tok, lit := s.Scan()
+ switch tok {
+ case token.EOF:
+ break scanFile
+ case token.COMMENT:
+ s := errRx.FindStringSubmatch(lit)
+ if len(s) == 2 {
+ errmap[prev] = append(errmap[prev], string(s[1]))
+ }
+ case token.SEMICOLON:
+ // ignore automatically inserted semicolon
+ if lit == "\n" {
+ continue scanFile
+ }
+ fallthrough
+ default:
+ prev = fset.Position(pos).String()
+ }
+ }
+ }
+
+ return errmap
+}
+
+func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
+ for _, err := range errlist {
+ pos, msg := splitError(err)
+ list := errmap[pos]
+ index := -1 // list index of matching message, if any
+ // we expect one of the messages in list to match the error at pos
+ for i, msg := range list {
+ rx, err := regexp.Compile(msg)
+ if err != nil {
+ t.Errorf("%s: %v", pos, err)
+ continue
+ }
+ if rx.MatchString(msg) {
+ index = i
+ break
+ }
+ }
+ if index >= 0 {
+ // eliminate from list
+ if n := len(list) - 1; n > 0 {
+ // not the last entry - swap in last element and shorten list by 1
+ list[index] = list[n]
+ errmap[pos] = list[:n]
+ } else {
+ // last entry - remove list from map
+ delete(errmap, pos)
+ }
+ } else {
+ t.Errorf("%s: no error expected: %q", pos, msg)
+ }
+
+ }
+}
+
+func checkFiles(t *testing.T, testname string, testfiles []string) {
+ // parse files and collect parser errors
+ files, errlist := parseFiles(t, testname, testfiles)
+
+ // typecheck and collect typechecker errors
+ var ctxt Context
+ ctxt.Error = func(err error) { errlist = append(errlist, err) }
+ ctxt.Check(fset, files)
+
+ if *listErrors {
+ t.Errorf("--- %s: %d errors found:", testname, len(errlist))
+ for _, err := range errlist {
+ t.Error(err)
+ }
+ return
+ }
+
+ // match and eliminate errors
+ // we are expecting the following errors
+ // (collect these after parsing the files so that
+ // they are found in the file set)
+ errmap := errMap(t, testname, files)
+ eliminate(t, errmap, errlist)
+
+ // there should be no expected errors left
+ if len(errmap) > 0 {
+ t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", testname, len(errmap))
+ for pos, list := range errmap {
+ for _, rx := range list {
+ t.Errorf("%s: %q", pos, rx)
+ }
+ }
+ }
+}
+
+var testBuiltinsDeclared = false
+
+func TestCheck(t *testing.T) {
+ // Declare builtins for testing.
+ // Not done in an init func to avoid an init race with
+ // the construction of the Universe var.
+ if !testBuiltinsDeclared {
+ testBuiltinsDeclared = true
+ // Pkg == nil for Universe objects
+ def(&Func{Name: "assert", Type: &builtin{_Assert, "assert", 1, false, true}})
+ def(&Func{Name: "trace", Type: &builtin{_Trace, "trace", 0, true, true}})
+ }
+
+ // For easy debugging w/o changing the testing code,
+ // if there is a local test file, only test that file.
+ const testfile = "testdata/test.go"
+ if fi, err := os.Stat(testfile); err == nil && !fi.IsDir() {
+ fmt.Printf("WARNING: Testing only %s (remove it to run all tests)\n", testfile)
+ checkFiles(t, testfile, []string{testfile})
+ return
+ }
+
+ // Otherwise, run all the tests.
+ for _, test := range tests {
+ checkFiles(t, test.name, test.files)
+ }
+}
diff --git a/src/pkg/go/types/const.go b/src/pkg/go/types/const.go
new file mode 100644
index 000000000..503652e75
--- /dev/null
+++ b/src/pkg/go/types/const.go
@@ -0,0 +1,718 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements operations on constant values.
+
+package types
+
+import (
+ "fmt"
+ "go/token"
+ "math/big"
+ "strconv"
+)
+
+// TODO(gri) At the moment, constants are different types
+// passed around as interface{} values. Introduce a Const
+// interface and use methods instead of xConst functions.
+
+// Representation of constant values.
+//
+// bool -> bool (true, false)
+// numeric -> int64, *big.Int, *big.Rat, Complex (ordered by increasing data structure "size")
+// string -> string
+// nil -> NilType (nilConst)
+//
+// Numeric constants are normalized after each operation such
+// that they are represented by the "smallest" data structure
+// required to represent the constant, independent of actual
+// type. Non-numeric constants are always normalized.
+
+// Representation of complex numbers.
+type Complex struct {
+ Re, Im *big.Rat
+}
+
+func (c Complex) String() string {
+ if c.Re.Sign() == 0 {
+ return fmt.Sprintf("%si", c.Im)
+ }
+ // normalized complex values always have an imaginary part
+ return fmt.Sprintf("(%s + %si)", c.Re, c.Im)
+}
+
+// Representation of nil.
+type NilType struct{}
+
+func (NilType) String() string {
+ return "nil"
+}
+
+// Frequently used values.
+var (
+ nilConst = NilType{}
+ zeroConst = int64(0)
+)
+
+// int64 bounds
+var (
+ minInt64 = big.NewInt(-1 << 63)
+ maxInt64 = big.NewInt(1<<63 - 1)
+)
+
+// normalizeIntConst returns the smallest constant representation
+// for the specific value of x; either an int64 or a *big.Int value.
+//
+func normalizeIntConst(x *big.Int) interface{} {
+ if minInt64.Cmp(x) <= 0 && x.Cmp(maxInt64) <= 0 {
+ return x.Int64()
+ }
+ return x
+}
+
+// normalizeRatConst returns the smallest constant representation
+// for the specific value of x; either an int64, *big.Int,
+// or *big.Rat value.
+//
+func normalizeRatConst(x *big.Rat) interface{} {
+ if x.IsInt() {
+ return normalizeIntConst(x.Num())
+ }
+ return x
+}
+
+// newComplex returns the smallest constant representation
+// for the specific value re + im*i; either an int64, *big.Int,
+// *big.Rat, or complex value.
+//
+func newComplex(re, im *big.Rat) interface{} {
+ if im.Sign() == 0 {
+ return normalizeRatConst(re)
+ }
+ return Complex{re, im}
+}
+
+// makeRuneConst returns the int64 code point for the rune literal
+// lit. The result is nil if lit is not a correct rune literal.
+//
+func makeRuneConst(lit string) interface{} {
+ if n := len(lit); n >= 2 {
+ if code, _, _, err := strconv.UnquoteChar(lit[1:n-1], '\''); err == nil {
+ return int64(code)
+ }
+ }
+ return nil
+}
+
+// makeRuneConst returns the smallest integer constant representation
+// (int64, *big.Int) for the integer literal lit. The result is nil if
+// lit is not a correct integer literal.
+//
+func makeIntConst(lit string) interface{} {
+ if x, err := strconv.ParseInt(lit, 0, 64); err == nil {
+ return x
+ }
+ if x, ok := new(big.Int).SetString(lit, 0); ok {
+ return x
+ }
+ return nil
+}
+
+// makeFloatConst returns the smallest floating-point constant representation
+// (int64, *big.Int, *big.Rat) for the floating-point literal lit. The result
+// is nil if lit is not a correct floating-point literal.
+//
+func makeFloatConst(lit string) interface{} {
+ if x, ok := new(big.Rat).SetString(lit); ok {
+ return normalizeRatConst(x)
+ }
+ return nil
+}
+
+// makeComplexConst returns the complex constant representation (Complex) for
+// the imaginary literal lit. The result is nil if lit is not a correct imaginary
+// literal.
+//
+func makeComplexConst(lit string) interface{} {
+ n := len(lit)
+ if n > 0 && lit[n-1] == 'i' {
+ if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok {
+ return newComplex(big.NewRat(0, 1), im)
+ }
+ }
+ return nil
+}
+
+// makeStringConst returns the string constant representation (string) for
+// the string literal lit. The result is nil if lit is not a correct string
+// literal.
+//
+func makeStringConst(lit string) interface{} {
+ if s, err := strconv.Unquote(lit); err == nil {
+ return s
+ }
+ return nil
+}
+
+// toImagConst returns the constant Complex(0, x) for a non-complex x.
+func toImagConst(x interface{}) interface{} {
+ var im *big.Rat
+ switch x := x.(type) {
+ case int64:
+ im = big.NewRat(x, 1)
+ case *big.Int:
+ im = new(big.Rat).SetFrac(x, int1)
+ case *big.Rat:
+ im = x
+ default:
+ unreachable()
+ }
+ return Complex{rat0, im}
+}
+
+// isZeroConst reports whether the value of constant x is 0.
+// x must be normalized.
+//
+func isZeroConst(x interface{}) bool {
+ i, ok := x.(int64) // good enough since constants are normalized
+ return ok && i == 0
+}
+
+// isNegConst reports whether the value of constant x is < 0.
+// x must be a non-complex numeric value.
+//
+func isNegConst(x interface{}) bool {
+ switch x := x.(type) {
+ case int64:
+ return x < 0
+ case *big.Int:
+ return x.Sign() < 0
+ case *big.Rat:
+ return x.Sign() < 0
+ }
+ unreachable()
+ return false
+}
+
+// isRepresentableConst reports whether the value of constant x can
+// be represented as a value of the basic type Typ[as] without loss
+// of precision.
+//
+func isRepresentableConst(x interface{}, ctxt *Context, as BasicKind) bool {
+ switch x := x.(type) {
+ case bool:
+ return as == Bool || as == UntypedBool
+
+ case int64:
+ switch as {
+ case Int:
+ var s = uint(ctxt.sizeof(Typ[as])) * 8
+ return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
+ case Int8:
+ const s = 8
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int16:
+ const s = 16
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int32:
+ const s = 32
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int64:
+ return true
+ case Uint, Uintptr:
+ var s = uint(ctxt.sizeof(Typ[as])) * 8
+ return 0 <= x && x <= int64(1)<<(s-1)-1
+ case Uint8:
+ const s = 8
+ return 0 <= x && x <= 1<<s-1
+ case Uint16:
+ const s = 16
+ return 0 <= x && x <= 1<<s-1
+ case Uint32:
+ const s = 32
+ return 0 <= x && x <= 1<<s-1
+ case Uint64:
+ return 0 <= x
+ case Float32:
+ return true // TODO(gri) fix this
+ case Float64:
+ return true // TODO(gri) fix this
+ case Complex64:
+ return true // TODO(gri) fix this
+ case Complex128:
+ return true // TODO(gri) fix this
+ case UntypedInt, UntypedFloat, UntypedComplex:
+ return true
+ }
+
+ case *big.Int:
+ switch as {
+ case Uint, Uintptr:
+ var s = uint(ctxt.sizeof(Typ[as])) * 8
+ return x.Sign() >= 0 && x.BitLen() <= int(s)
+ case Uint64:
+ return x.Sign() >= 0 && x.BitLen() <= 64
+ case Float32:
+ return true // TODO(gri) fix this
+ case Float64:
+ return true // TODO(gri) fix this
+ case Complex64:
+ return true // TODO(gri) fix this
+ case Complex128:
+ return true // TODO(gri) fix this
+ case UntypedInt, UntypedFloat, UntypedComplex:
+ return true
+ }
+
+ case *big.Rat:
+ switch as {
+ case Float32:
+ return true // TODO(gri) fix this
+ case Float64:
+ return true // TODO(gri) fix this
+ case Complex64:
+ return true // TODO(gri) fix this
+ case Complex128:
+ return true // TODO(gri) fix this
+ case UntypedFloat, UntypedComplex:
+ return true
+ }
+
+ case Complex:
+ switch as {
+ case Complex64:
+ return true // TODO(gri) fix this
+ case Complex128:
+ return true // TODO(gri) fix this
+ case UntypedComplex:
+ return true
+ }
+
+ case string:
+ return as == String || as == UntypedString
+
+ case NilType:
+ return as == UntypedNil || as == UnsafePointer
+
+ default:
+ unreachable()
+ }
+
+ return false
+}
+
+var (
+ int1 = big.NewInt(1)
+ rat0 = big.NewRat(0, 1)
+)
+
+// complexity returns a measure of representation complexity for constant x.
+func complexity(x interface{}) int {
+ switch x.(type) {
+ case bool, string, NilType:
+ return 1
+ case int64:
+ return 2
+ case *big.Int:
+ return 3
+ case *big.Rat:
+ return 4
+ case Complex:
+ return 5
+ }
+ unreachable()
+ return 0
+}
+
+// matchConst returns the matching representation (same type) with the
+// smallest complexity for two constant values x and y. They must be
+// of the same "kind" (boolean, numeric, string, or NilType).
+//
+func matchConst(x, y interface{}) (_, _ interface{}) {
+ if complexity(x) > complexity(y) {
+ y, x = matchConst(y, x)
+ return x, y
+ }
+ // complexity(x) <= complexity(y)
+
+ switch x := x.(type) {
+ case bool, Complex, string, NilType:
+ return x, y
+
+ case int64:
+ switch y := y.(type) {
+ case int64:
+ return x, y
+ case *big.Int:
+ return big.NewInt(x), y
+ case *big.Rat:
+ return big.NewRat(x, 1), y
+ case Complex:
+ return Complex{big.NewRat(x, 1), rat0}, y
+ }
+
+ case *big.Int:
+ switch y := y.(type) {
+ case *big.Int:
+ return x, y
+ case *big.Rat:
+ return new(big.Rat).SetFrac(x, int1), y
+ case Complex:
+ return Complex{new(big.Rat).SetFrac(x, int1), rat0}, y
+ }
+
+ case *big.Rat:
+ switch y := y.(type) {
+ case *big.Rat:
+ return x, y
+ case Complex:
+ return Complex{x, rat0}, y
+ }
+ }
+
+ unreachable()
+ return nil, nil
+}
+
+// is32bit reports whether x can be represented using 32 bits.
+func is32bit(x int64) bool {
+ return -1<<31 <= x && x <= 1<<31-1
+}
+
+// is63bit reports whether x can be represented using 63 bits.
+func is63bit(x int64) bool {
+ return -1<<62 <= x && x <= 1<<62-1
+}
+
+// unaryOpConst returns the result of the constant evaluation op x where x is of the given type.
+func unaryOpConst(x interface{}, ctxt *Context, op token.Token, typ *Basic) interface{} {
+ switch op {
+ case token.ADD:
+ return x // nothing to do
+ case token.SUB:
+ switch x := x.(type) {
+ case int64:
+ if z := -x; z != x {
+ return z // no overflow
+ }
+ // overflow - need to convert to big.Int
+ return normalizeIntConst(new(big.Int).Neg(big.NewInt(x)))
+ case *big.Int:
+ return normalizeIntConst(new(big.Int).Neg(x))
+ case *big.Rat:
+ return normalizeRatConst(new(big.Rat).Neg(x))
+ case Complex:
+ return newComplex(new(big.Rat).Neg(x.Re), new(big.Rat).Neg(x.Im))
+ }
+ case token.XOR:
+ var z big.Int
+ switch x := x.(type) {
+ case int64:
+ z.Not(big.NewInt(x))
+ case *big.Int:
+ z.Not(x)
+ default:
+ unreachable()
+ }
+ // For unsigned types, the result will be negative and
+ // thus "too large": We must limit the result size to
+ // the type's size.
+ if typ.Info&IsUnsigned != 0 {
+ s := uint(ctxt.sizeof(typ)) * 8
+ z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)<<s
+ }
+ return normalizeIntConst(&z)
+ case token.NOT:
+ return !x.(bool)
+ }
+ unreachable()
+ return nil
+}
+
+// binaryOpConst returns the result of the constant evaluation x op y;
+// both operands must be of the same constant "kind" (boolean, numeric, or string).
+// If typ is an integer type, division (op == token.QUO) is using integer division
+// (and the result is guaranteed to be integer) rather than floating-point
+// division. Division by zero leads to a run-time panic.
+//
+func binaryOpConst(x, y interface{}, op token.Token, typ *Basic) interface{} {
+ x, y = matchConst(x, y)
+
+ switch x := x.(type) {
+ case bool:
+ y := y.(bool)
+ switch op {
+ case token.LAND:
+ return x && y
+ case token.LOR:
+ return x || y
+ }
+
+ case int64:
+ y := y.(int64)
+ switch op {
+ case token.ADD:
+ // TODO(gri) can do better than this
+ if is63bit(x) && is63bit(y) {
+ return x + y
+ }
+ return normalizeIntConst(new(big.Int).Add(big.NewInt(x), big.NewInt(y)))
+ case token.SUB:
+ // TODO(gri) can do better than this
+ if is63bit(x) && is63bit(y) {
+ return x - y
+ }
+ return normalizeIntConst(new(big.Int).Sub(big.NewInt(x), big.NewInt(y)))
+ case token.MUL:
+ // TODO(gri) can do better than this
+ if is32bit(x) && is32bit(y) {
+ return x * y
+ }
+ return normalizeIntConst(new(big.Int).Mul(big.NewInt(x), big.NewInt(y)))
+ case token.REM:
+ return x % y
+ case token.QUO:
+ if typ.Info&IsInteger != 0 {
+ return x / y
+ }
+ return normalizeRatConst(new(big.Rat).SetFrac(big.NewInt(x), big.NewInt(y)))
+ case token.AND:
+ return x & y
+ case token.OR:
+ return x | y
+ case token.XOR:
+ return x ^ y
+ case token.AND_NOT:
+ return x &^ y
+ }
+
+ case *big.Int:
+ y := y.(*big.Int)
+ var z big.Int
+ switch op {
+ case token.ADD:
+ z.Add(x, y)
+ case token.SUB:
+ z.Sub(x, y)
+ case token.MUL:
+ z.Mul(x, y)
+ case token.REM:
+ z.Rem(x, y)
+ case token.QUO:
+ if typ.Info&IsInteger != 0 {
+ z.Quo(x, y)
+ } else {
+ return normalizeRatConst(new(big.Rat).SetFrac(x, y))
+ }
+ case token.AND:
+ z.And(x, y)
+ case token.OR:
+ z.Or(x, y)
+ case token.XOR:
+ z.Xor(x, y)
+ case token.AND_NOT:
+ z.AndNot(x, y)
+ default:
+ unreachable()
+ }
+ return normalizeIntConst(&z)
+
+ case *big.Rat:
+ y := y.(*big.Rat)
+ var z big.Rat
+ switch op {
+ case token.ADD:
+ z.Add(x, y)
+ case token.SUB:
+ z.Sub(x, y)
+ case token.MUL:
+ z.Mul(x, y)
+ case token.QUO:
+ z.Quo(x, y)
+ default:
+ unreachable()
+ }
+ return normalizeRatConst(&z)
+
+ case Complex:
+ y := y.(Complex)
+ a, b := x.Re, x.Im
+ c, d := y.Re, y.Im
+ var re, im big.Rat
+ switch op {
+ case token.ADD:
+ // (a+c) + i(b+d)
+ re.Add(a, c)
+ im.Add(b, d)
+ case token.SUB:
+ // (a-c) + i(b-d)
+ re.Sub(a, c)
+ im.Sub(b, d)
+ case token.MUL:
+ // (ac-bd) + i(bc+ad)
+ var ac, bd, bc, ad big.Rat
+ ac.Mul(a, c)
+ bd.Mul(b, d)
+ bc.Mul(b, c)
+ ad.Mul(a, d)
+ re.Sub(&ac, &bd)
+ im.Add(&bc, &ad)
+ case token.QUO:
+ // (ac+bd)/s + i(bc-ad)/s, with s = cc + dd
+ var ac, bd, bc, ad, s big.Rat
+ ac.Mul(a, c)
+ bd.Mul(b, d)
+ bc.Mul(b, c)
+ ad.Mul(a, d)
+ s.Add(c.Mul(c, c), d.Mul(d, d))
+ re.Add(&ac, &bd)
+ re.Quo(&re, &s)
+ im.Sub(&bc, &ad)
+ im.Quo(&im, &s)
+ default:
+ unreachable()
+ }
+ return newComplex(&re, &im)
+
+ case string:
+ if op == token.ADD {
+ return x + y.(string)
+ }
+ }
+
+ unreachable()
+ return nil
+}
+
+// shiftConst returns the result of the constant evaluation x op s
+// where op is token.SHL or token.SHR (<< or >>). x must be an
+// integer constant.
+//
+func shiftConst(x interface{}, s uint, op token.Token) interface{} {
+ switch x := x.(type) {
+ case int64:
+ switch op {
+ case token.SHL:
+ z := big.NewInt(x)
+ return normalizeIntConst(z.Lsh(z, s))
+ case token.SHR:
+ return x >> s
+ }
+
+ case *big.Int:
+ var z big.Int
+ switch op {
+ case token.SHL:
+ return normalizeIntConst(z.Lsh(x, s))
+ case token.SHR:
+ return normalizeIntConst(z.Rsh(x, s))
+ }
+ }
+
+ unreachable()
+ return nil
+}
+
+// compareConst returns the result of the constant comparison x op y;
+// both operands must be of the same "kind" (boolean, numeric, string,
+// or NilType).
+//
+func compareConst(x, y interface{}, op token.Token) (z bool) {
+ x, y = matchConst(x, y)
+
+ // x == y => x == y
+ // x != y => x != y
+ // x > y => y < x
+ // x >= y => u <= x
+ swap := false
+ switch op {
+ case token.GTR:
+ swap = true
+ op = token.LSS
+ case token.GEQ:
+ swap = true
+ op = token.LEQ
+ }
+
+ // x == y => x == y
+ // x != y => !(x == y)
+ // x < y => x < y
+ // x <= y => !(y < x)
+ negate := false
+ switch op {
+ case token.NEQ:
+ negate = true
+ op = token.EQL
+ case token.LEQ:
+ swap = !swap
+ negate = true
+ op = token.LSS
+ }
+
+ if negate {
+ defer func() { z = !z }()
+ }
+
+ if swap {
+ x, y = y, x
+ }
+
+ switch x := x.(type) {
+ case bool:
+ if op == token.EQL {
+ return x == y.(bool)
+ }
+
+ case int64:
+ y := y.(int64)
+ switch op {
+ case token.EQL:
+ return x == y
+ case token.LSS:
+ return x < y
+ }
+
+ case *big.Int:
+ s := x.Cmp(y.(*big.Int))
+ switch op {
+ case token.EQL:
+ return s == 0
+ case token.LSS:
+ return s < 0
+ }
+
+ case *big.Rat:
+ s := x.Cmp(y.(*big.Rat))
+ switch op {
+ case token.EQL:
+ return s == 0
+ case token.LSS:
+ return s < 0
+ }
+
+ case Complex:
+ y := y.(Complex)
+ if op == token.EQL {
+ return x.Re.Cmp(y.Re) == 0 && x.Im.Cmp(y.Im) == 0
+ }
+
+ case string:
+ y := y.(string)
+ switch op {
+ case token.EQL:
+ return x == y
+ case token.LSS:
+ return x < y
+ }
+
+ case NilType:
+ if op == token.EQL {
+ return x == y.(NilType)
+ }
+ }
+
+ fmt.Printf("x = %s (%T), y = %s (%T)\n", x, x, y, y)
+ unreachable()
+ return
+}
diff --git a/src/pkg/go/types/conversions.go b/src/pkg/go/types/conversions.go
new file mode 100644
index 000000000..fcbaf7717
--- /dev/null
+++ b/src/pkg/go/types/conversions.go
@@ -0,0 +1,129 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements typechecking of conversions.
+
+package types
+
+import (
+ "go/ast"
+)
+
+// conversion typechecks the type conversion conv to type typ. iota is the current
+// value of iota or -1 if iota doesn't have a value in the current context. The result
+// of the conversion is returned via x. If the conversion has type errors, the returned
+// x is marked as invalid (x.mode == invalid).
+//
+func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota int) {
+ // all conversions have one argument
+ if len(conv.Args) != 1 {
+ check.invalidOp(conv.Pos(), "%s conversion requires exactly one argument", conv)
+ goto Error
+ }
+
+ // evaluate argument
+ check.expr(x, conv.Args[0], nil, iota)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ if x.mode == constant && isConstType(typ) {
+ // constant conversion
+ // TODO(gri) implement this
+ } else {
+ // non-constant conversion
+ if !x.isConvertible(check.ctxt, typ) {
+ check.invalidOp(conv.Pos(), "cannot convert %s to %s", x, typ)
+ goto Error
+ }
+ x.mode = value
+ }
+
+ check.conversions[conv] = true // for cap/len checking
+ x.expr = conv
+ x.typ = typ
+ return
+
+Error:
+ x.mode = invalid
+}
+
+func (x *operand) isConvertible(ctxt *Context, T Type) bool {
+ // "x is assignable to T"
+ if x.isAssignable(ctxt, T) {
+ return true
+ }
+
+ // "x's type and T have identical underlying types"
+ V := x.typ
+ Vu := underlying(V)
+ Tu := underlying(T)
+ if IsIdentical(Vu, Tu) {
+ return true
+ }
+
+ // "x's type and T are unnamed pointer types and their pointer base types have identical underlying types"
+ if V, ok := V.(*Pointer); ok {
+ if T, ok := T.(*Pointer); ok {
+ if IsIdentical(underlying(V.Base), underlying(T.Base)) {
+ return true
+ }
+ }
+ }
+
+ // "x's type and T are both integer or floating point types"
+ if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) {
+ return true
+ }
+
+ // "x's type and T are both complex types"
+ if isComplex(V) && isComplex(T) {
+ return true
+ }
+
+ // "x is an integer or a slice of bytes or runes and T is a string type"
+ if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
+ return true
+ }
+
+ // "x is a string and T is a slice of bytes or runes"
+ if isString(V) && isBytesOrRunes(Tu) {
+ return true
+ }
+
+ // package unsafe:
+ // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
+ if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
+ return true
+ }
+ // "and vice versa"
+ if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
+ return true
+ }
+
+ return false
+}
+
+func isUintptr(typ Type) bool {
+ t, ok := typ.(*Basic)
+ return ok && t.Kind == Uintptr
+}
+
+func isUnsafePointer(typ Type) bool {
+ t, ok := typ.(*Basic)
+ return ok && t.Kind == UnsafePointer
+}
+
+func isPointer(typ Type) bool {
+ _, ok := typ.(*Pointer)
+ return ok
+}
+
+func isBytesOrRunes(typ Type) bool {
+ if s, ok := typ.(*Slice); ok {
+ t, ok := underlying(s.Elt).(*Basic)
+ return ok && (t.Kind == Byte || t.Kind == Rune)
+ }
+ return false
+}
diff --git a/src/pkg/go/types/errors.go b/src/pkg/go/types/errors.go
new file mode 100644
index 000000000..62ee54791
--- /dev/null
+++ b/src/pkg/go/types/errors.go
@@ -0,0 +1,335 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements various error reporters.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/token"
+)
+
+// TODO(gri) eventually assert and unimplemented should disappear.
+func assert(p bool) {
+ if !p {
+ panic("assertion failed")
+ }
+}
+
+func unreachable() {
+ panic("unreachable")
+}
+
+func (check *checker) printTrace(format string, args []interface{}) {
+ const dots = ". . . . . . . . . . . . . . . . . . . . "
+ n := len(check.pos) - 1
+ i := 3 * n
+ for i > len(dots) {
+ fmt.Print(dots)
+ i -= len(dots)
+ }
+ // i <= len(dots)
+ fmt.Printf("%s:\t", check.fset.Position(check.pos[n]))
+ fmt.Print(dots[0:i])
+ fmt.Println(check.formatMsg(format, args))
+}
+
+func (check *checker) trace(pos token.Pos, format string, args ...interface{}) {
+ check.pos = append(check.pos, pos)
+ check.printTrace(format, args)
+}
+
+func (check *checker) untrace(format string, args ...interface{}) {
+ if len(format) > 0 {
+ check.printTrace(format, args)
+ }
+ check.pos = check.pos[:len(check.pos)-1]
+}
+
+func (check *checker) formatMsg(format string, args []interface{}) string {
+ for i, arg := range args {
+ switch a := arg.(type) {
+ case token.Pos:
+ args[i] = check.fset.Position(a).String()
+ case ast.Expr:
+ args[i] = exprString(a)
+ case Type:
+ args[i] = typeString(a)
+ case operand:
+ panic("internal error: should always pass *operand")
+ }
+ }
+ return fmt.Sprintf(format, args...)
+}
+
+// dump is only needed for debugging
+func (check *checker) dump(format string, args ...interface{}) {
+ fmt.Println(check.formatMsg(format, args))
+}
+
+func (check *checker) err(err error) {
+ if check.firsterr == nil {
+ check.firsterr = err
+ }
+ f := check.ctxt.Error
+ if f == nil {
+ panic(bailout{}) // report only first error
+ }
+ f(err)
+}
+
+func (check *checker) errorf(pos token.Pos, format string, args ...interface{}) {
+ check.err(fmt.Errorf("%s: %s", check.fset.Position(pos), check.formatMsg(format, args)))
+}
+
+func (check *checker) invalidAST(pos token.Pos, format string, args ...interface{}) {
+ check.errorf(pos, "invalid AST: "+format, args...)
+}
+
+func (check *checker) invalidArg(pos token.Pos, format string, args ...interface{}) {
+ check.errorf(pos, "invalid argument: "+format, args...)
+}
+
+func (check *checker) invalidOp(pos token.Pos, format string, args ...interface{}) {
+ check.errorf(pos, "invalid operation: "+format, args...)
+}
+
+// exprString returns a (simplified) string representation for an expression.
+func exprString(expr ast.Expr) string {
+ var buf bytes.Buffer
+ writeExpr(&buf, expr)
+ return buf.String()
+}
+
+// TODO(gri) Need to merge with typeString since some expressions are types (try: ([]int)(a))
+func writeExpr(buf *bytes.Buffer, expr ast.Expr) {
+ switch x := expr.(type) {
+ case *ast.Ident:
+ buf.WriteString(x.Name)
+
+ case *ast.BasicLit:
+ buf.WriteString(x.Value)
+
+ case *ast.FuncLit:
+ buf.WriteString("(func literal)")
+
+ case *ast.CompositeLit:
+ buf.WriteString("(composite literal)")
+
+ case *ast.ParenExpr:
+ buf.WriteByte('(')
+ writeExpr(buf, x.X)
+ buf.WriteByte(')')
+
+ case *ast.SelectorExpr:
+ writeExpr(buf, x.X)
+ buf.WriteByte('.')
+ buf.WriteString(x.Sel.Name)
+
+ case *ast.IndexExpr:
+ writeExpr(buf, x.X)
+ buf.WriteByte('[')
+ writeExpr(buf, x.Index)
+ buf.WriteByte(']')
+
+ case *ast.SliceExpr:
+ writeExpr(buf, x.X)
+ buf.WriteByte('[')
+ if x.Low != nil {
+ writeExpr(buf, x.Low)
+ }
+ buf.WriteByte(':')
+ if x.High != nil {
+ writeExpr(buf, x.High)
+ }
+ buf.WriteByte(']')
+
+ case *ast.TypeAssertExpr:
+ writeExpr(buf, x.X)
+ buf.WriteString(".(...)")
+
+ case *ast.CallExpr:
+ writeExpr(buf, x.Fun)
+ buf.WriteByte('(')
+ for i, arg := range x.Args {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ writeExpr(buf, arg)
+ }
+ buf.WriteByte(')')
+
+ case *ast.StarExpr:
+ buf.WriteByte('*')
+ writeExpr(buf, x.X)
+
+ case *ast.UnaryExpr:
+ buf.WriteString(x.Op.String())
+ writeExpr(buf, x.X)
+
+ case *ast.BinaryExpr:
+ // The AST preserves source-level parentheses so there is
+ // no need to introduce parentheses here for correctness.
+ writeExpr(buf, x.X)
+ buf.WriteByte(' ')
+ buf.WriteString(x.Op.String())
+ buf.WriteByte(' ')
+ writeExpr(buf, x.Y)
+
+ default:
+ fmt.Fprintf(buf, "<expr %T>", x)
+ }
+}
+
+// typeString returns a string representation for typ.
+func typeString(typ Type) string {
+ var buf bytes.Buffer
+ writeType(&buf, typ)
+ return buf.String()
+}
+
+func writeParams(buf *bytes.Buffer, params []*Var, isVariadic bool) {
+ buf.WriteByte('(')
+ for i, par := range params {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ if par.Name != "" {
+ buf.WriteString(par.Name)
+ buf.WriteByte(' ')
+ }
+ if isVariadic && i == len(params)-1 {
+ buf.WriteString("...")
+ }
+ writeType(buf, par.Type)
+ }
+ buf.WriteByte(')')
+}
+
+func writeSignature(buf *bytes.Buffer, sig *Signature) {
+ writeParams(buf, sig.Params, sig.IsVariadic)
+ if len(sig.Results) == 0 {
+ // no result
+ return
+ }
+
+ buf.WriteByte(' ')
+ if len(sig.Results) == 1 && sig.Results[0].Name == "" {
+ // single unnamed result
+ writeType(buf, sig.Results[0].Type.(Type))
+ return
+ }
+
+ // multiple or named result(s)
+ writeParams(buf, sig.Results, false)
+}
+
+func writeType(buf *bytes.Buffer, typ Type) {
+ switch t := typ.(type) {
+ case nil:
+ buf.WriteString("<nil>")
+
+ case *Basic:
+ buf.WriteString(t.Name)
+
+ case *Array:
+ fmt.Fprintf(buf, "[%d]", t.Len)
+ writeType(buf, t.Elt)
+
+ case *Slice:
+ buf.WriteString("[]")
+ writeType(buf, t.Elt)
+
+ case *Struct:
+ buf.WriteString("struct{")
+ for i, f := range t.Fields {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ if !f.IsAnonymous {
+ buf.WriteString(f.Name)
+ buf.WriteByte(' ')
+ }
+ writeType(buf, f.Type)
+ if f.Tag != "" {
+ fmt.Fprintf(buf, " %q", f.Tag)
+ }
+ }
+ buf.WriteByte('}')
+
+ case *Pointer:
+ buf.WriteByte('*')
+ writeType(buf, t.Base)
+
+ case *Result:
+ writeParams(buf, t.Values, false)
+
+ case *Signature:
+ buf.WriteString("func")
+ writeSignature(buf, t)
+
+ case *builtin:
+ fmt.Fprintf(buf, "<type of %s>", t.name)
+
+ case *Interface:
+ buf.WriteString("interface{")
+ for i, m := range t.Methods {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ buf.WriteString(m.Name)
+ writeSignature(buf, m.Type)
+ }
+ buf.WriteByte('}')
+
+ case *Map:
+ buf.WriteString("map[")
+ writeType(buf, t.Key)
+ buf.WriteByte(']')
+ writeType(buf, t.Elt)
+
+ case *Chan:
+ var s string
+ switch t.Dir {
+ case ast.SEND:
+ s = "chan<- "
+ case ast.RECV:
+ s = "<-chan "
+ default:
+ s = "chan "
+ }
+ buf.WriteString(s)
+ writeType(buf, t.Elt)
+
+ case *NamedType:
+ s := "<NamedType w/o object>"
+ if obj := t.Obj; obj != nil {
+ if obj.Pkg != nil && obj.Pkg.Path != "" {
+ buf.WriteString(obj.Pkg.Path)
+ buf.WriteString(".")
+ }
+ s = t.Obj.GetName()
+ }
+ buf.WriteString(s)
+
+ default:
+ fmt.Fprintf(buf, "<type %T>", t)
+ }
+}
+
+func (t *Array) String() string { return typeString(t) }
+func (t *Basic) String() string { return typeString(t) }
+func (t *Chan) String() string { return typeString(t) }
+func (t *Interface) String() string { return typeString(t) }
+func (t *Map) String() string { return typeString(t) }
+func (t *NamedType) String() string { return typeString(t) }
+func (t *Pointer) String() string { return typeString(t) }
+func (t *Result) String() string { return typeString(t) }
+func (t *Signature) String() string { return typeString(t) }
+func (t *Slice) String() string { return typeString(t) }
+func (t *Struct) String() string { return typeString(t) }
+func (t *builtin) String() string { return typeString(t) }
diff --git a/src/pkg/go/types/exportdata.go b/src/pkg/go/types/exportdata.go
new file mode 100644
index 000000000..1f6a3c725
--- /dev/null
+++ b/src/pkg/go/types/exportdata.go
@@ -0,0 +1,111 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements FindGcExportData.
+
+package types
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+)
+
+func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
+ // See $GOROOT/include/ar.h.
+ hdr := make([]byte, 16+12+6+6+8+10+2)
+ _, err = io.ReadFull(r, hdr)
+ if err != nil {
+ return
+ }
+ // leave for debugging
+ if false {
+ fmt.Printf("header: %s", hdr)
+ }
+ s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
+ size, err = strconv.Atoi(s)
+ if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
+ err = errors.New("invalid archive header")
+ return
+ }
+ name = strings.TrimSpace(string(hdr[:16]))
+ return
+}
+
+// FindGcExportData positions the reader r at the beginning of the
+// export data section of an underlying GC-created object/archive
+// file by reading from it. The reader must be positioned at the
+// start of the file before calling this function.
+//
+func FindGcExportData(r *bufio.Reader) (err error) {
+ // Read first line to make sure this is an object file.
+ line, err := r.ReadSlice('\n')
+ if err != nil {
+ return
+ }
+ if string(line) == "!<arch>\n" {
+ // Archive file. Scan to __.PKGDEF, which should
+ // be second archive entry.
+ var name string
+ var size int
+
+ // First entry should be __.GOSYMDEF.
+ // Older archives used __.SYMDEF, so allow that too.
+ // Read and discard.
+ if name, size, err = readGopackHeader(r); err != nil {
+ return
+ }
+ if name != "__.SYMDEF" && name != "__.GOSYMDEF" {
+ err = errors.New("go archive does not begin with __.SYMDEF or __.GOSYMDEF")
+ return
+ }
+ const block = 4096
+ tmp := make([]byte, block)
+ for size > 0 {
+ n := size
+ if n > block {
+ n = block
+ }
+ if _, err = io.ReadFull(r, tmp[:n]); err != nil {
+ return
+ }
+ size -= n
+ }
+
+ // Second entry should be __.PKGDEF.
+ if name, size, err = readGopackHeader(r); err != nil {
+ return
+ }
+ if name != "__.PKGDEF" {
+ err = errors.New("go archive is missing __.PKGDEF")
+ return
+ }
+
+ // Read first line of __.PKGDEF data, so that line
+ // is once again the first line of the input.
+ if line, err = r.ReadSlice('\n'); err != nil {
+ return
+ }
+ }
+
+ // Now at __.PKGDEF in archive or still at beginning of file.
+ // Either way, line should begin with "go object ".
+ if !strings.HasPrefix(string(line), "go object ") {
+ err = errors.New("not a go object file")
+ return
+ }
+
+ // Skip over object header to export data.
+ // Begins after first line with $$.
+ for line[0] != '$' {
+ if line, err = r.ReadSlice('\n'); err != nil {
+ return
+ }
+ }
+
+ return
+}
diff --git a/src/pkg/go/types/expr.go b/src/pkg/go/types/expr.go
new file mode 100644
index 000000000..86d782d48
--- /dev/null
+++ b/src/pkg/go/types/expr.go
@@ -0,0 +1,1520 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements typechecking of expressions.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+ "strconv"
+)
+
+// TODO(gri) Cleanups
+// - don't print error messages referring to invalid types (they are likely spurious errors)
+// - simplify invalid handling: maybe just use Typ[Invalid] as marker, get rid of invalid Mode for values?
+// - rethink error handling: should all callers check if x.mode == valid after making a call?
+// - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used
+// - use "" or "_" consistently for anonymous identifiers? (e.g. reeceivers that have no name)
+// - consider storing error messages in invalid operands for better error messages/debugging output
+
+// TODO(gri) API issues
+// - clients need access to builtins type information
+// - API tests are missing (e.g., identifiers should be handled as expressions in callbacks)
+
+func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) {
+ if list == nil {
+ return
+ }
+ var last *Var
+ for i, field := range list.List {
+ ftype := field.Type
+ if t, _ := ftype.(*ast.Ellipsis); t != nil {
+ ftype = t.Elt
+ if variadicOk && i == len(list.List)-1 {
+ isVariadic = true
+ } else {
+ check.invalidAST(field.Pos(), "... not permitted")
+ // ok to continue
+ }
+ }
+ // the parser ensures that f.Tag is nil and we don't
+ // care if a constructed AST contains a non-nil tag
+ typ := check.typ(ftype, true)
+ if len(field.Names) > 0 {
+ // named parameter
+ for _, name := range field.Names {
+ par := check.lookup(name).(*Var)
+ par.Type = typ
+ last = par
+ copy := *par
+ params = append(params, &copy)
+ }
+ } else {
+ // anonymous parameter
+ par := &Var{Type: typ}
+ last = nil // not accessible inside function
+ params = append(params, par)
+ }
+ }
+ // For a variadic function, change the last parameter's object type
+ // from T to []T (this is the type used inside the function), but
+ // keep the params list unchanged (this is the externally visible type).
+ if isVariadic && last != nil {
+ last.Type = &Slice{Elt: last.Type}
+ }
+ return
+}
+
+func (check *checker) collectMethods(list *ast.FieldList) (methods []*Method) {
+ if list == nil {
+ return
+ }
+ for _, f := range list.List {
+ typ := check.typ(f.Type, len(f.Names) > 0) // cycles are not ok for embedded interfaces
+ // the parser ensures that f.Tag is nil and we don't
+ // care if a constructed AST contains a non-nil tag
+ if len(f.Names) > 0 {
+ // methods (the parser ensures that there's only one
+ // and we don't care if a constructed AST has more)
+ sig, ok := typ.(*Signature)
+ if !ok {
+ check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ)
+ continue
+ }
+ for _, name := range f.Names {
+ methods = append(methods, &Method{QualifiedName{check.pkg, name.Name}, sig})
+ }
+ } else {
+ // embedded interface
+ utyp := underlying(typ)
+ if ityp, ok := utyp.(*Interface); ok {
+ methods = append(methods, ityp.Methods...)
+ } else if utyp != Typ[Invalid] {
+ // if utyp is invalid, don't complain (the root cause was reported before)
+ check.errorf(f.Type.Pos(), "%s is not an interface type", typ)
+ }
+ }
+ }
+ // Check for double declarations.
+ // The parser inserts methods into an interface-local scope, so local
+ // double declarations are reported by the parser already. We need to
+ // check again for conflicts due to embedded interfaces. This will lead
+ // to a 2nd error message if the double declaration was reported before
+ // by the parser.
+ // TODO(gri) clean this up a bit
+ seen := make(map[string]bool)
+ for _, m := range methods {
+ if seen[m.Name] {
+ check.errorf(list.Pos(), "multiple methods named %s", m.Name)
+ return // keep multiple entries, lookup will only return the first entry
+ }
+ seen[m.Name] = true
+ }
+ return
+}
+
+func (check *checker) tag(t *ast.BasicLit) string {
+ if t != nil {
+ if t.Kind == token.STRING {
+ if val, err := strconv.Unquote(t.Value); err == nil {
+ return val
+ }
+ }
+ check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
+ }
+ return ""
+}
+
+func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field) {
+ if list == nil {
+ return
+ }
+
+ var typ Type // current field typ
+ var tag string // current field tag
+ add := func(name string, isAnonymous bool) {
+ fields = append(fields, &Field{QualifiedName{check.pkg, name}, typ, tag, isAnonymous})
+ }
+
+ for _, f := range list.List {
+ typ = check.typ(f.Type, cycleOk)
+ tag = check.tag(f.Tag)
+ if len(f.Names) > 0 {
+ // named fields
+ for _, name := range f.Names {
+ add(name.Name, false)
+ }
+ } else {
+ // anonymous field
+ switch t := deref(typ).(type) {
+ case *Basic:
+ add(t.Name, true)
+ case *NamedType:
+ add(t.Obj.GetName(), true)
+ default:
+ if typ != Typ[Invalid] {
+ check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ)
+ }
+ }
+ }
+ }
+
+ return
+}
+
+type opPredicates map[token.Token]func(Type) bool
+
+var unaryOpPredicates = opPredicates{
+ token.ADD: isNumeric,
+ token.SUB: isNumeric,
+ token.XOR: isInteger,
+ token.NOT: isBoolean,
+}
+
+func (check *checker) op(m opPredicates, x *operand, op token.Token) bool {
+ if pred := m[op]; pred != nil {
+ if !pred(x.typ) {
+ check.invalidOp(x.pos(), "operator %s not defined for %s", op, x)
+ return false
+ }
+ } else {
+ check.invalidAST(x.pos(), "unknown operator %s", op)
+ return false
+ }
+ return true
+}
+
+func (check *checker) unary(x *operand, op token.Token) {
+ switch op {
+ case token.AND:
+ // spec: "As an exception to the addressability
+ // requirement x may also be a composite literal."
+ if _, ok := unparen(x.expr).(*ast.CompositeLit); ok {
+ x.mode = variable
+ }
+ if x.mode != variable {
+ check.invalidOp(x.pos(), "cannot take address of %s", x)
+ goto Error
+ }
+ x.typ = &Pointer{Base: x.typ}
+ return
+
+ case token.ARROW:
+ typ, ok := underlying(x.typ).(*Chan)
+ if !ok {
+ check.invalidOp(x.pos(), "cannot receive from non-channel %s", x)
+ goto Error
+ }
+ if typ.Dir&ast.RECV == 0 {
+ check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x)
+ goto Error
+ }
+ x.mode = valueok
+ x.typ = typ.Elt
+ return
+ }
+
+ if !check.op(unaryOpPredicates, x, op) {
+ goto Error
+ }
+
+ if x.mode == constant {
+ typ := underlying(x.typ).(*Basic)
+ x.val = unaryOpConst(x.val, check.ctxt, op, typ)
+ // Typed constants must be representable in
+ // their type after each constant operation.
+ check.isRepresentable(x, typ)
+ return
+ }
+
+ x.mode = value
+ // x.typ remains unchanged
+ return
+
+Error:
+ x.mode = invalid
+}
+
+func isShift(op token.Token) bool {
+ return op == token.SHL || op == token.SHR
+}
+
+func isComparison(op token.Token) bool {
+ // Note: tokens are not ordered well to make this much easier
+ switch op {
+ case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
+ return true
+ }
+ return false
+}
+
+// isRepresentable checks that a constant operand is representable in the given type.
+func (check *checker) isRepresentable(x *operand, typ *Basic) {
+ if x.mode != constant || isUntyped(typ) {
+ return
+ }
+
+ if !isRepresentableConst(x.val, check.ctxt, typ.Kind) {
+ var msg string
+ if isNumeric(x.typ) && isNumeric(typ) {
+ msg = "%s overflows %s"
+ } else {
+ msg = "cannot convert %s to %s"
+ }
+ check.errorf(x.pos(), msg, x, typ)
+ x.mode = invalid
+ }
+}
+
+// updateExprType updates the type of all untyped nodes in the
+// expression tree of x to typ. If shiftOp is set, x is the lhs
+// of a shift expression. In that case, and if x is in the set
+// of shift operands with delayed type checking, and typ is not
+// an untyped type, updateExprType will check if typ is an
+// integer type.
+// If Context.Expr != nil, it is called for all nodes that are
+// now assigned their final (not untyped) type.
+func (check *checker) updateExprType(x ast.Expr, typ Type, shiftOp bool) {
+ switch x := x.(type) {
+ case *ast.BadExpr,
+ *ast.FuncLit,
+ *ast.CompositeLit,
+ *ast.SelectorExpr,
+ *ast.IndexExpr,
+ *ast.SliceExpr,
+ *ast.TypeAssertExpr,
+ *ast.CallExpr,
+ *ast.StarExpr,
+ *ast.KeyValueExpr,
+ *ast.ArrayType,
+ *ast.StructType,
+ *ast.FuncType,
+ *ast.InterfaceType,
+ *ast.MapType,
+ *ast.ChanType:
+ // these expression are never untyped - nothing to do
+ return
+
+ case *ast.Ident, *ast.BasicLit:
+ // update type
+
+ case *ast.ParenExpr:
+ check.updateExprType(x.X, typ, false)
+
+ case *ast.UnaryExpr:
+ check.updateExprType(x.X, typ, false)
+
+ case *ast.BinaryExpr:
+ if isComparison(x.Op) {
+ // result type is independent of operand types
+ } else if isShift(x.Op) {
+ // result type depends only on lhs operand
+ check.updateExprType(x.X, typ, true)
+ } else {
+ // operand types match result type
+ check.updateExprType(x.X, typ, false)
+ check.updateExprType(x.Y, typ, false)
+ }
+
+ case *ast.Ellipsis:
+ unreachable()
+ default:
+ unreachable()
+ }
+
+ // TODO(gri) t should always exist, shouldn't it?
+ if t := check.untyped[x]; t != nil {
+ if isUntyped(typ) {
+ check.untyped[x] = typ.(*Basic)
+ } else {
+ // notify clients of final type for x
+ if f := check.ctxt.Expr; f != nil {
+ f(x, typ, check.constants[x])
+ }
+ delete(check.untyped, x)
+ delete(check.constants, x)
+ // check delayed shift
+ // Note: Using shiftOp is an optimization: it prevents
+ // map lookups when we know x is not a shiftOp in the
+ // first place.
+ if shiftOp && check.shiftOps[x] {
+ if !isInteger(typ) {
+ check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ)
+ }
+ delete(check.shiftOps, x)
+ }
+ }
+ }
+}
+
+// convertUntyped attempts to set the type of an untyped value to the target type.
+func (check *checker) convertUntyped(x *operand, target Type) {
+ if x.mode == invalid || !isUntyped(x.typ) {
+ return
+ }
+
+ // TODO(gri) Sloppy code - clean up. This function is central
+ // to assignment and expression checking.
+
+ if isUntyped(target) {
+ // both x and target are untyped
+ xkind := x.typ.(*Basic).Kind
+ tkind := target.(*Basic).Kind
+ if isNumeric(x.typ) && isNumeric(target) {
+ if xkind < tkind {
+ x.typ = target
+ check.updateExprType(x.expr, target, false)
+ }
+ } else if xkind != tkind {
+ goto Error
+ }
+ return
+ }
+
+ // typed target
+ switch t := underlying(target).(type) {
+ case nil:
+ // We may reach here due to previous type errors.
+ // Be conservative and don't crash.
+ x.mode = invalid
+ return
+ case *Basic:
+ check.isRepresentable(x, t)
+ if x.mode == invalid {
+ return // error already reported
+ }
+ case *Interface:
+ if !x.isNil() && len(t.Methods) > 0 /* empty interfaces are ok */ {
+ goto Error
+ }
+ // Update operand types to the default type rather then
+ // the target (interface) type: values must have concrete
+ // dynamic types. If the value is nil, keep it untyped
+ // (this is important for tools such as go vet which need
+ // the dynamic type for argument checking of say, print
+ // functions)
+ if x.isNil() {
+ target = Typ[UntypedNil]
+ } else {
+ // cannot assign untyped values to non-empty interfaces
+ if len(t.Methods) > 0 {
+ goto Error
+ }
+ target = defaultType(x.typ)
+ }
+ case *Pointer, *Signature, *Slice, *Map, *Chan:
+ if !x.isNil() {
+ goto Error
+ }
+ // keep nil untyped - see comment for interfaces, above
+ target = Typ[UntypedNil]
+ default:
+ if debug {
+ check.dump("convertUntyped(x = %v, target = %v)", x, target)
+ }
+ unreachable()
+ }
+
+ x.typ = target
+ check.updateExprType(x.expr, target, false)
+ return
+
+Error:
+ check.errorf(x.pos(), "cannot convert %s to %s", x, target)
+ x.mode = invalid
+}
+
+func (check *checker) comparison(x, y *operand, op token.Token) {
+ // TODO(gri) deal with interface vs non-interface comparison
+
+ valid := false
+ if x.isAssignable(check.ctxt, y.typ) || y.isAssignable(check.ctxt, x.typ) {
+ switch op {
+ case token.EQL, token.NEQ:
+ valid = isComparable(x.typ) ||
+ x.isNil() && hasNil(y.typ) ||
+ y.isNil() && hasNil(x.typ)
+ case token.LSS, token.LEQ, token.GTR, token.GEQ:
+ valid = isOrdered(x.typ)
+ default:
+ unreachable()
+ }
+ }
+
+ if !valid {
+ check.invalidOp(x.pos(), "cannot compare %s %s %s", x, op, y)
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant && y.mode == constant {
+ x.val = compareConst(x.val, y.val, op)
+ } else {
+ x.mode = value
+ }
+
+ x.typ = Typ[UntypedBool]
+}
+
+func (check *checker) shift(x, y *operand, op token.Token) {
+ // spec: "The right operand in a shift expression must have unsigned
+ // integer type or be an untyped constant that can be converted to
+ // unsigned integer type."
+ switch {
+ case isInteger(y.typ) && isUnsigned(y.typ):
+ // nothing to do
+ case y.mode == constant && isUntyped(y.typ):
+ check.convertUntyped(x, Typ[UntypedInt])
+ default:
+ check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y)
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant {
+ if y.mode == constant {
+ // constant shift - lhs must be (representable as) an integer
+ if isUntyped(x.typ) {
+ if !isRepresentableConst(x.val, check.ctxt, UntypedInt) {
+ check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
+ x.mode = invalid
+ return
+ }
+ x.typ = Typ[UntypedInt]
+ }
+ assert(x.isInteger(check.ctxt))
+
+ // rhs must be within reasonable bounds
+ const stupidShift = 1024
+ s, ok := y.val.(int64)
+ if !ok || s < 0 || s >= stupidShift {
+ check.invalidOp(y.pos(), "%s: stupid shift", y)
+ x.mode = invalid
+ return
+ }
+
+ // everything's ok
+ x.val = shiftConst(x.val, uint(s), op)
+ return
+ }
+
+ // non-constant shift with constant lhs
+ if isUntyped(x.typ) {
+ // spec: "If the left operand of a non-constant shift expression is
+ // an untyped constant, the type of the constant is what it would be
+ // if the shift expression were replaced by its left operand alone;
+ // the type is int if it cannot be determined from the context (for
+ // instance, if the shift expression is an operand in a comparison
+ // against an untyped constant)".
+
+ // delay operand checking until we know the type
+ check.shiftOps[x.expr] = true
+ x.mode = value
+ return
+ }
+ }
+
+ // non-constant shift - lhs must be an integer
+ if !isInteger(x.typ) {
+ check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
+ x.mode = invalid
+ return
+ }
+
+ // non-constant shift
+ x.mode = value
+}
+
+var binaryOpPredicates = opPredicates{
+ token.ADD: func(typ Type) bool { return isNumeric(typ) || isString(typ) },
+ token.SUB: isNumeric,
+ token.MUL: isNumeric,
+ token.QUO: isNumeric,
+ token.REM: isInteger,
+
+ token.AND: isInteger,
+ token.OR: isInteger,
+ token.XOR: isInteger,
+ token.AND_NOT: isInteger,
+
+ token.LAND: isBoolean,
+ token.LOR: isBoolean,
+}
+
+func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token, iota int) {
+ var y operand
+
+ check.expr(x, lhs, nil, iota)
+ check.expr(&y, rhs, nil, iota)
+
+ if x.mode == invalid {
+ return
+ }
+ if y.mode == invalid {
+ x.mode = invalid
+ x.expr = y.expr
+ return
+ }
+
+ if isShift(op) {
+ check.shift(x, &y, op)
+ return
+ }
+
+ check.convertUntyped(x, y.typ)
+ if x.mode == invalid {
+ return
+ }
+ check.convertUntyped(&y, x.typ)
+ if y.mode == invalid {
+ x.mode = invalid
+ return
+ }
+
+ if isComparison(op) {
+ check.comparison(x, &y, op)
+ return
+ }
+
+ if !IsIdentical(x.typ, y.typ) {
+ check.invalidOp(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
+ x.mode = invalid
+ return
+ }
+
+ if !check.op(binaryOpPredicates, x, op) {
+ x.mode = invalid
+ return
+ }
+
+ if (op == token.QUO || op == token.REM) && y.mode == constant && isZeroConst(y.val) {
+ check.invalidOp(y.pos(), "division by zero")
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant && y.mode == constant {
+ typ := underlying(x.typ).(*Basic)
+ x.val = binaryOpConst(x.val, y.val, op, typ)
+ // Typed constants must be representable in
+ // their type after each constant operation.
+ check.isRepresentable(x, typ)
+ return
+ }
+
+ x.mode = value
+ // x.typ is unchanged
+}
+
+// index checks an index expression for validity. If length >= 0, it is the upper
+// bound for the index. The result is a valid index >= 0, or a negative value.
+//
+func (check *checker) index(index ast.Expr, length int64, iota int) int64 {
+ var x operand
+
+ check.expr(&x, index, nil, iota)
+ if !x.isInteger(check.ctxt) {
+ check.errorf(x.pos(), "index %s must be integer", &x)
+ return -1
+ }
+ if x.mode != constant {
+ return -1 // we cannot check more
+ }
+ // The spec doesn't require int64 indices, but perhaps it should.
+ i, ok := x.val.(int64)
+ if !ok {
+ check.errorf(x.pos(), "stupid index %s", &x)
+ return -1
+ }
+ if i < 0 {
+ check.errorf(x.pos(), "index %s must not be negative", &x)
+ return -1
+ }
+ if length >= 0 && i >= length {
+ check.errorf(x.pos(), "index %s is out of bounds (>= %d)", &x, length)
+ return -1
+ }
+
+ return i
+}
+
+// compositeLitKey resolves unresolved composite literal keys.
+// For details, see comment in go/parser/parser.go, method parseElement.
+func (check *checker) compositeLitKey(key ast.Expr) {
+ if ident, ok := key.(*ast.Ident); ok && ident.Obj == nil {
+ if obj := check.pkg.Scope.Lookup(ident.Name); obj != nil {
+ check.register(ident, obj)
+ } else if obj := Universe.Lookup(ident.Name); obj != nil {
+ check.register(ident, obj)
+ } else {
+ check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
+ }
+ }
+}
+
+// indexElts checks the elements (elts) of an array or slice composite literal
+// against the literal's element type (typ), and the element indices against
+// the literal length if known (length >= 0). It returns the length of the
+// literal (maximum index value + 1).
+//
+func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota int) int64 {
+ visited := make(map[int64]bool, len(elts))
+ var index, max int64
+ for _, e := range elts {
+ // determine and check index
+ validIndex := false
+ eval := e
+ if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
+ check.compositeLitKey(kv.Key)
+ if i := check.index(kv.Key, length, iota); i >= 0 {
+ index = i
+ validIndex = true
+ }
+ eval = kv.Value
+ } else if length >= 0 && index >= length {
+ check.errorf(e.Pos(), "index %d is out of bounds (>= %d)", index, length)
+ } else {
+ validIndex = true
+ }
+
+ // if we have a valid index, check for duplicate entries
+ if validIndex {
+ if visited[index] {
+ check.errorf(e.Pos(), "duplicate index %d in array or slice literal", index)
+ }
+ visited[index] = true
+ }
+ index++
+ if index > max {
+ max = index
+ }
+
+ // check element against composite literal element type
+ var x operand
+ check.expr(&x, eval, typ, iota)
+ if !check.assignment(&x, typ) && x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s value in array or slice literal", &x, typ)
+ }
+ }
+ return max
+}
+
+// argument typechecks passing an argument arg (if arg != nil) or
+// x (if arg == nil) to the i'th parameter of the given signature.
+// If passSlice is set, the argument is followed by ... in the call.
+//
+func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) {
+ // determine parameter
+ var par *Var
+ n := len(sig.Params)
+ if i < n {
+ par = sig.Params[i]
+ } else if sig.IsVariadic {
+ par = sig.Params[n-1]
+ } else {
+ check.errorf(arg.Pos(), "too many arguments")
+ return
+ }
+
+ // determine argument
+ var z operand
+ z.mode = variable
+ z.expr = nil // TODO(gri) can we do better here? (for good error messages)
+ z.typ = par.Type
+
+ if arg != nil {
+ check.expr(x, arg, z.typ, -1)
+ }
+ if x.mode == invalid {
+ return // ignore this argument
+ }
+
+ // check last argument of the form x...
+ if passSlice {
+ if i+1 != n {
+ check.errorf(x.pos(), "can only use ... with matching parameter")
+ return // ignore this argument
+ }
+ // spec: "If the final argument is assignable to a slice type []T,
+ // it may be passed unchanged as the value for a ...T parameter if
+ // the argument is followed by ..."
+ z.typ = &Slice{Elt: z.typ} // change final parameter type to []T
+ }
+
+ if !check.assignment(x, z.typ) && x.mode != invalid {
+ check.errorf(x.pos(), "cannot pass argument %s to %s", x, &z)
+ }
+}
+
+var emptyResult Result
+
+func (check *checker) callExpr(x *operand) {
+ var typ Type
+ var val interface{}
+ switch x.mode {
+ case invalid:
+ return // nothing to do
+ case novalue:
+ typ = &emptyResult
+ case constant:
+ typ = x.typ
+ val = x.val
+ default:
+ typ = x.typ
+ }
+
+ // if the operand is untyped, delay notification
+ // until it becomes typed or until the end of
+ // type checking
+ if isUntyped(typ) {
+ check.untyped[x.expr] = typ.(*Basic)
+ if val != nil {
+ check.constants[x.expr] = val
+ }
+ return
+ }
+
+ // TODO(gri) ensure that literals always report
+ // their dynamic (never interface) type.
+ // This is not the case yet.
+
+ if check.ctxt.Expr != nil {
+ check.ctxt.Expr(x.expr, typ, val)
+ }
+}
+
+// rawExpr typechecks expression e and initializes x with the expression
+// value or type. If an error occurred, x.mode is set to invalid.
+// If hint != nil, it is the type of a composite literal element.
+// iota >= 0 indicates that the expression is part of a constant declaration.
+// cycleOk indicates whether it is ok for a type expression to refer to itself.
+//
+func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycleOk bool) {
+ if trace {
+ c := ""
+ if cycleOk {
+ c = " ⨁"
+ }
+ check.trace(e.Pos(), "%s (%s, %d%s)", e, typeString(hint), iota, c)
+ defer check.untrace("=> %s", x)
+ }
+
+ defer check.callExpr(x)
+
+ switch e := e.(type) {
+ case *ast.BadExpr:
+ goto Error // error was reported before
+
+ case *ast.Ident:
+ if e.Name == "_" {
+ check.invalidOp(e.Pos(), "cannot use _ as value or type")
+ goto Error
+ }
+ obj := check.lookup(e)
+ if obj == nil {
+ goto Error // error was reported before
+ }
+ check.object(obj, cycleOk)
+ switch obj := obj.(type) {
+ case *Package:
+ check.errorf(e.Pos(), "use of package %s not in selector", obj.Name)
+ goto Error
+ case *Const:
+ if obj.Val == nil {
+ goto Error // cycle detected
+ }
+ x.mode = constant
+ if obj == universeIota {
+ if iota < 0 {
+ check.invalidAST(e.Pos(), "cannot use iota outside constant declaration")
+ goto Error
+ }
+ x.val = int64(iota)
+ } else {
+ x.val = obj.Val
+ }
+ case *TypeName:
+ x.mode = typexpr
+ if !cycleOk && underlying(obj.Type) == nil {
+ check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.Name)
+ x.expr = e
+ x.typ = Typ[Invalid]
+ return // don't goto Error - need x.mode == typexpr
+ }
+ case *Var:
+ x.mode = variable
+ case *Func:
+ x.mode = value
+ default:
+ unreachable()
+ }
+ x.typ = obj.GetType()
+
+ case *ast.Ellipsis:
+ // ellipses are handled explicitly where they are legal
+ // (array composite literals and parameter lists)
+ check.errorf(e.Pos(), "invalid use of '...'")
+ goto Error
+
+ case *ast.BasicLit:
+ x.setConst(e.Kind, e.Value)
+ if x.mode == invalid {
+ check.invalidAST(e.Pos(), "invalid literal %v", e.Value)
+ goto Error
+ }
+
+ case *ast.FuncLit:
+ if sig, ok := check.typ(e.Type, false).(*Signature); ok {
+ x.mode = value
+ x.typ = sig
+ check.later(nil, sig, e.Body)
+ } else {
+ check.invalidAST(e.Pos(), "invalid function literal %s", e)
+ goto Error
+ }
+
+ case *ast.CompositeLit:
+ typ := hint
+ openArray := false
+ if e.Type != nil {
+ // [...]T array types may only appear with composite literals.
+ // Check for them here so we don't have to handle ... in general.
+ typ = nil
+ if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil {
+ if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil {
+ // We have an "open" [...]T array type.
+ // Create a new ArrayType with unknown length (-1)
+ // and finish setting it up after analyzing the literal.
+ typ = &Array{Len: -1, Elt: check.typ(atyp.Elt, cycleOk)}
+ openArray = true
+ }
+ }
+ if typ == nil {
+ typ = check.typ(e.Type, false)
+ }
+ }
+ if typ == nil {
+ check.errorf(e.Pos(), "missing type in composite literal")
+ goto Error
+ }
+
+ switch utyp := underlying(deref(typ)).(type) {
+ case *Struct:
+ if len(e.Elts) == 0 {
+ break
+ }
+ fields := utyp.Fields
+ if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok {
+ // all elements must have keys
+ visited := make([]bool, len(fields))
+ for _, e := range e.Elts {
+ kv, _ := e.(*ast.KeyValueExpr)
+ if kv == nil {
+ check.errorf(e.Pos(), "mixture of field:value and value elements in struct literal")
+ continue
+ }
+ key, _ := kv.Key.(*ast.Ident)
+ if key == nil {
+ check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key)
+ continue
+ }
+ i := utyp.fieldIndex(QualifiedName{check.pkg, key.Name})
+ if i < 0 {
+ check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name)
+ continue
+ }
+ // 0 <= i < len(fields)
+ if visited[i] {
+ check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name)
+ continue
+ }
+ visited[i] = true
+ check.expr(x, kv.Value, nil, iota)
+ etyp := fields[i].Type
+ if !check.assignment(x, etyp) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp)
+ }
+ continue
+ }
+ }
+ } else {
+ // no element must have a key
+ for i, e := range e.Elts {
+ if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
+ check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal")
+ continue
+ }
+ check.expr(x, e, nil, iota)
+ if i >= len(fields) {
+ check.errorf(x.pos(), "too many values in struct literal")
+ break // cannot continue
+ }
+ // i < len(fields)
+ etyp := fields[i].Type
+ if !check.assignment(x, etyp) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp)
+ }
+ continue
+ }
+ }
+ if len(e.Elts) < len(fields) {
+ check.errorf(e.Rbrace, "too few values in struct literal")
+ // ok to continue
+ }
+ }
+
+ case *Array:
+ n := check.indexedElts(e.Elts, utyp.Elt, utyp.Len, iota)
+ // if we have an "open" [...]T array, set the length now that we know it
+ if openArray {
+ utyp.Len = n
+ }
+
+ case *Slice:
+ check.indexedElts(e.Elts, utyp.Elt, -1, iota)
+
+ case *Map:
+ visited := make(map[interface{}]bool, len(e.Elts))
+ for _, e := range e.Elts {
+ kv, _ := e.(*ast.KeyValueExpr)
+ if kv == nil {
+ check.errorf(e.Pos(), "missing key in map literal")
+ continue
+ }
+ check.compositeLitKey(kv.Key)
+ check.expr(x, kv.Key, nil, iota)
+ if !check.assignment(x, utyp.Key) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.Key)
+ }
+ continue
+ }
+ if x.mode == constant {
+ if visited[x.val] {
+ check.errorf(x.pos(), "duplicate key %s in map literal", x.val)
+ continue
+ }
+ visited[x.val] = true
+ }
+ check.expr(x, kv.Value, utyp.Elt, iota)
+ if !check.assignment(x, utyp.Elt) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.Elt)
+ }
+ continue
+ }
+ }
+
+ default:
+ check.errorf(e.Pos(), "%s is not a valid composite literal type", typ)
+ goto Error
+ }
+
+ x.mode = value
+ x.typ = typ
+
+ case *ast.ParenExpr:
+ check.rawExpr(x, e.X, nil, iota, cycleOk)
+
+ case *ast.SelectorExpr:
+ sel := e.Sel.Name
+ // If the identifier refers to a package, handle everything here
+ // so we don't need a "package" mode for operands: package names
+ // can only appear in qualified identifiers which are mapped to
+ // selector expressions.
+ if ident, ok := e.X.(*ast.Ident); ok {
+ if pkg, ok := check.lookup(ident).(*Package); ok {
+ exp := pkg.Scope.Lookup(sel)
+ // gcimported package scopes contain non-exported
+ // objects such as types used in partially exported
+ // objects - do not accept them
+ if exp == nil || !ast.IsExported(exp.GetName()) {
+ check.errorf(e.Pos(), "cannot refer to unexported %s", e)
+ goto Error
+ }
+ check.register(e.Sel, exp)
+ // Simplified version of the code for *ast.Idents:
+ // - imported packages use types.Scope and types.Objects
+ // - imported objects are always fully initialized
+ switch exp := exp.(type) {
+ case *Const:
+ assert(exp.Val != nil)
+ x.mode = constant
+ x.typ = exp.Type
+ x.val = exp.Val
+ case *TypeName:
+ x.mode = typexpr
+ x.typ = exp.Type
+ case *Var:
+ x.mode = variable
+ x.typ = exp.Type
+ case *Func:
+ x.mode = value
+ x.typ = exp.Type
+ default:
+ unreachable()
+ }
+ x.expr = e
+ return
+ }
+ }
+
+ check.exprOrType(x, e.X, iota, false)
+ if x.mode == invalid {
+ goto Error
+ }
+ res := lookupField(x.typ, QualifiedName{check.pkg, sel})
+ if res.mode == invalid {
+ check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel)
+ goto Error
+ }
+ if x.mode == typexpr {
+ // method expression
+ sig, ok := res.typ.(*Signature)
+ if !ok {
+ check.invalidOp(e.Pos(), "%s has no method %s", x, sel)
+ goto Error
+ }
+ // the receiver type becomes the type of the first function
+ // argument of the method expression's function type
+ // TODO(gri) at the moment, method sets don't correctly track
+ // pointer vs non-pointer receivers => typechecker is too lenient
+ x.mode = value
+ x.typ = &Signature{
+ Params: append([]*Var{{Type: x.typ}}, sig.Params...),
+ Results: sig.Results,
+ IsVariadic: sig.IsVariadic,
+ }
+ } else {
+ // regular selector
+ x.mode = res.mode
+ x.typ = res.typ
+ }
+
+ case *ast.IndexExpr:
+ check.expr(x, e.X, nil, iota)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ valid := false
+ length := int64(-1) // valid if >= 0
+ switch typ := underlying(x.typ).(type) {
+ case *Basic:
+ if isString(typ) {
+ valid = true
+ if x.mode == constant {
+ length = int64(len(x.val.(string)))
+ }
+ // an indexed string always yields a byte value
+ // (not a constant) even if the string and the
+ // index are constant
+ x.mode = value
+ x.typ = Typ[Byte]
+ }
+
+ case *Array:
+ valid = true
+ length = typ.Len
+ if x.mode != variable {
+ x.mode = value
+ }
+ x.typ = typ.Elt
+
+ case *Pointer:
+ if typ, _ := underlying(typ.Base).(*Array); typ != nil {
+ valid = true
+ length = typ.Len
+ x.mode = variable
+ x.typ = typ.Elt
+ }
+
+ case *Slice:
+ valid = true
+ x.mode = variable
+ x.typ = typ.Elt
+
+ case *Map:
+ var key operand
+ check.expr(&key, e.Index, nil, iota)
+ if !check.assignment(&key, typ.Key) {
+ if key.mode != invalid {
+ check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.Key)
+ }
+ goto Error
+ }
+ x.mode = valueok
+ x.typ = typ.Elt
+ x.expr = e
+ return
+ }
+
+ if !valid {
+ check.invalidOp(x.pos(), "cannot index %s", x)
+ goto Error
+ }
+
+ if e.Index == nil {
+ check.invalidAST(e.Pos(), "missing index expression for %s", x)
+ return
+ }
+
+ check.index(e.Index, length, iota)
+ // ok to continue
+
+ case *ast.SliceExpr:
+ check.expr(x, e.X, nil, iota)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ valid := false
+ length := int64(-1) // valid if >= 0
+ switch typ := underlying(x.typ).(type) {
+ case *Basic:
+ if isString(typ) {
+ valid = true
+ if x.mode == constant {
+ length = int64(len(x.val.(string))) + 1 // +1 for slice
+ }
+ // a sliced string always yields a string value
+ // of the same type as the original string (not
+ // a constant) even if the string and the indices
+ // are constant
+ x.mode = value
+ // x.typ doesn't change, but if it is an untyped
+ // string it becomes string (see also issue 4913).
+ if typ.Kind == UntypedString {
+ x.typ = Typ[String]
+ }
+ }
+
+ case *Array:
+ valid = true
+ length = typ.Len + 1 // +1 for slice
+ if x.mode != variable {
+ check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x)
+ goto Error
+ }
+ x.typ = &Slice{Elt: typ.Elt}
+
+ case *Pointer:
+ if typ, _ := underlying(typ.Base).(*Array); typ != nil {
+ valid = true
+ length = typ.Len + 1 // +1 for slice
+ x.mode = variable
+ x.typ = &Slice{Elt: typ.Elt}
+ }
+
+ case *Slice:
+ valid = true
+ x.mode = variable
+ // x.typ doesn't change
+ }
+
+ if !valid {
+ check.invalidOp(x.pos(), "cannot slice %s", x)
+ goto Error
+ }
+
+ lo := int64(0)
+ if e.Low != nil {
+ lo = check.index(e.Low, length, iota)
+ }
+
+ hi := int64(-1)
+ if e.High != nil {
+ hi = check.index(e.High, length, iota)
+ } else if length >= 0 {
+ hi = length
+ }
+
+ if lo >= 0 && hi >= 0 && lo > hi {
+ check.errorf(e.Low.Pos(), "inverted slice range: %d > %d", lo, hi)
+ // ok to continue
+ }
+
+ case *ast.TypeAssertExpr:
+ check.expr(x, e.X, nil, iota)
+ if x.mode == invalid {
+ goto Error
+ }
+ var T *Interface
+ if T, _ = underlying(x.typ).(*Interface); T == nil {
+ check.invalidOp(x.pos(), "%s is not an interface", x)
+ goto Error
+ }
+ // x.(type) expressions are handled explicitly in type switches
+ if e.Type == nil {
+ check.errorf(e.Pos(), "use of .(type) outside type switch")
+ goto Error
+ }
+ typ := check.typ(e.Type, false)
+ if typ == Typ[Invalid] {
+ goto Error
+ }
+ if method, wrongType := missingMethod(typ, T); method != nil {
+ var msg string
+ if wrongType {
+ msg = "%s cannot have dynamic type %s (wrong type for method %s)"
+ } else {
+ msg = "%s cannot have dynamic type %s (missing method %s)"
+ }
+ check.errorf(e.Type.Pos(), msg, x, typ, method.Name)
+ // ok to continue
+ }
+ x.mode = valueok
+ x.expr = e
+ x.typ = typ
+
+ case *ast.CallExpr:
+ check.exprOrType(x, e.Fun, iota, false)
+ if x.mode == invalid {
+ goto Error
+ } else if x.mode == typexpr {
+ check.conversion(x, e, x.typ, iota)
+ } else if sig, ok := underlying(x.typ).(*Signature); ok {
+ // check parameters
+
+ // If we have a trailing ... at the end of the parameter
+ // list, the last argument must match the parameter type
+ // []T of a variadic function parameter x ...T.
+ passSlice := false
+ if e.Ellipsis.IsValid() {
+ if sig.IsVariadic {
+ passSlice = true
+ } else {
+ check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun)
+ // ok to continue
+ }
+ }
+
+ // If we have a single argument that is a function call
+ // we need to handle it separately. Determine if this
+ // is the case without checking the argument.
+ var call *ast.CallExpr
+ if len(e.Args) == 1 {
+ call, _ = unparen(e.Args[0]).(*ast.CallExpr)
+ }
+
+ n := 0 // parameter count
+ if call != nil {
+ // We have a single argument that is a function call.
+ check.expr(x, call, nil, -1)
+ if x.mode == invalid {
+ goto Error // TODO(gri): we can do better
+ }
+ if t, _ := x.typ.(*Result); t != nil {
+ // multiple result values
+ n = len(t.Values)
+ for i, obj := range t.Values {
+ x.mode = value
+ x.expr = nil // TODO(gri) can we do better here? (for good error messages)
+ x.typ = obj.Type
+ check.argument(sig, i, nil, x, passSlice && i+1 == n)
+ }
+ } else {
+ // single result value
+ n = 1
+ check.argument(sig, 0, nil, x, passSlice)
+ }
+
+ } else {
+ // We don't have a single argument or it is not a function call.
+ n = len(e.Args)
+ for i, arg := range e.Args {
+ check.argument(sig, i, arg, x, passSlice && i+1 == n)
+ }
+ }
+
+ // determine if we have enough arguments
+ if sig.IsVariadic {
+ // a variadic function accepts an "empty"
+ // last argument: count one extra
+ n++
+ }
+ if n < len(sig.Params) {
+ check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun)
+ // ok to continue
+ }
+
+ // determine result
+ switch len(sig.Results) {
+ case 0:
+ x.mode = novalue
+ case 1:
+ x.mode = value
+ x.typ = sig.Results[0].Type
+ default:
+ x.mode = value
+ x.typ = &Result{Values: sig.Results}
+ }
+
+ } else if bin, ok := x.typ.(*builtin); ok {
+ check.builtin(x, e, bin, iota)
+
+ } else {
+ check.invalidOp(x.pos(), "cannot call non-function %s", x)
+ goto Error
+ }
+
+ case *ast.StarExpr:
+ check.exprOrType(x, e.X, iota, true)
+ switch x.mode {
+ case invalid:
+ goto Error
+ case typexpr:
+ x.typ = &Pointer{Base: x.typ}
+ default:
+ if typ, ok := underlying(x.typ).(*Pointer); ok {
+ x.mode = variable
+ x.typ = typ.Base
+ } else {
+ check.invalidOp(x.pos(), "cannot indirect %s", x)
+ goto Error
+ }
+ }
+
+ case *ast.UnaryExpr:
+ check.expr(x, e.X, nil, iota)
+ if x.mode == invalid {
+ goto Error
+ }
+ check.unary(x, e.Op)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ case *ast.BinaryExpr:
+ check.binary(x, e.X, e.Y, e.Op, iota)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ case *ast.KeyValueExpr:
+ // key:value expressions are handled in composite literals
+ check.invalidAST(e.Pos(), "no key:value expected")
+ goto Error
+
+ case *ast.ArrayType:
+ if e.Len != nil {
+ check.expr(x, e.Len, nil, iota)
+ if x.mode == invalid {
+ goto Error
+ }
+ if x.mode != constant {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "array length %s must be constant", x)
+ }
+ goto Error
+ }
+ n, ok := x.val.(int64)
+ if !ok || n < 0 {
+ check.errorf(x.pos(), "invalid array length %s", x)
+ goto Error
+ }
+ x.typ = &Array{Len: n, Elt: check.typ(e.Elt, cycleOk)}
+ } else {
+ x.typ = &Slice{Elt: check.typ(e.Elt, true)}
+ }
+ x.mode = typexpr
+
+ case *ast.StructType:
+ x.mode = typexpr
+ x.typ = &Struct{Fields: check.collectFields(e.Fields, cycleOk)}
+
+ case *ast.FuncType:
+ params, isVariadic := check.collectParams(e.Params, true)
+ results, _ := check.collectParams(e.Results, false)
+ x.mode = typexpr
+ x.typ = &Signature{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic}
+
+ case *ast.InterfaceType:
+ x.mode = typexpr
+ x.typ = &Interface{Methods: check.collectMethods(e.Methods)}
+
+ case *ast.MapType:
+ x.mode = typexpr
+ x.typ = &Map{Key: check.typ(e.Key, true), Elt: check.typ(e.Value, true)}
+
+ case *ast.ChanType:
+ x.mode = typexpr
+ x.typ = &Chan{Dir: e.Dir, Elt: check.typ(e.Value, true)}
+
+ default:
+ if debug {
+ check.dump("expr = %v (%T)", e, e)
+ }
+ unreachable()
+ }
+
+ // everything went well
+ x.expr = e
+ return
+
+Error:
+ x.mode = invalid
+ x.expr = e
+}
+
+// exprOrType is like rawExpr but reports an error if e doesn't represents a value or type.
+func (check *checker) exprOrType(x *operand, e ast.Expr, iota int, cycleOk bool) {
+ check.rawExpr(x, e, nil, iota, cycleOk)
+ if x.mode == novalue {
+ check.errorf(x.pos(), "%s used as value or type", x)
+ x.mode = invalid
+ }
+}
+
+// expr is like rawExpr but reports an error if e doesn't represents a value.
+func (check *checker) expr(x *operand, e ast.Expr, hint Type, iota int) {
+ check.rawExpr(x, e, hint, iota, false)
+ switch x.mode {
+ case novalue:
+ check.errorf(x.pos(), "%s used as value", x)
+ x.mode = invalid
+ case typexpr:
+ check.errorf(x.pos(), "%s is not an expression", x)
+ x.mode = invalid
+ }
+}
+
+func (check *checker) rawTyp(e ast.Expr, cycleOk, nilOk bool) Type {
+ var x operand
+ check.rawExpr(&x, e, nil, -1, cycleOk)
+ switch x.mode {
+ case invalid:
+ // ignore - error reported before
+ case novalue:
+ check.errorf(x.pos(), "%s used as type", &x)
+ case typexpr:
+ return x.typ
+ case constant:
+ if nilOk && x.isNil() {
+ return nil
+ }
+ fallthrough
+ default:
+ check.errorf(x.pos(), "%s is not a type", &x)
+ }
+ return Typ[Invalid]
+}
+
+// typOrNil is like rawExpr but reports an error if e doesn't represents a type or the predeclared value nil.
+// It returns e's type, nil, or Typ[Invalid] if an error occurred.
+//
+func (check *checker) typOrNil(e ast.Expr, cycleOk bool) Type {
+ return check.rawTyp(e, cycleOk, true)
+}
+
+// typ is like rawExpr but reports an error if e doesn't represents a type.
+// It returns e's type, or Typ[Invalid] if an error occurred.
+//
+func (check *checker) typ(e ast.Expr, cycleOk bool) Type {
+ return check.rawTyp(e, cycleOk, false)
+}
diff --git a/src/pkg/go/types/gcimporter.go b/src/pkg/go/types/gcimporter.go
new file mode 100644
index 000000000..7f968eb8d
--- /dev/null
+++ b/src/pkg/go/types/gcimporter.go
@@ -0,0 +1,950 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements an Importer for gc-generated object files.
+
+package types
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/token"
+ "io"
+ "math/big"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "text/scanner"
+)
+
+var pkgExts = [...]string{".a", ".5", ".6", ".8"}
+
+// FindPkg returns the filename and unique package id for an import
+// path based on package information provided by build.Import (using
+// the build.Default build.Context).
+// If no file was found, an empty filename is returned.
+//
+func FindPkg(path, srcDir string) (filename, id string) {
+ if len(path) == 0 {
+ return
+ }
+
+ id = path
+ var noext string
+ switch {
+ default:
+ // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
+ // Don't require the source files to be present.
+ bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
+ if bp.PkgObj == "" {
+ return
+ }
+ noext = strings.TrimSuffix(bp.PkgObj, ".a")
+
+ case build.IsLocalImport(path):
+ // "./x" -> "/this/directory/x.ext", "/this/directory/x"
+ noext = filepath.Join(srcDir, path)
+ id = noext
+
+ case filepath.IsAbs(path):
+ // for completeness only - go/build.Import
+ // does not support absolute imports
+ // "/x" -> "/x.ext", "/x"
+ noext = path
+ }
+
+ // try extensions
+ for _, ext := range pkgExts {
+ filename = noext + ext
+ if f, err := os.Stat(filename); err == nil && !f.IsDir() {
+ return
+ }
+ }
+
+ filename = "" // not found
+ return
+}
+
+// GcImportData imports a package by reading the gc-generated export data,
+// adds the corresponding package object to the imports map indexed by id,
+// and returns the object.
+//
+// The imports map must contains all packages already imported. The data
+// reader position must be the beginning of the export data section. The
+// filename is only used in error messages.
+//
+// If imports[id] contains the completely imported package, that package
+// can be used directly, and there is no need to call this function (but
+// there is also no harm but for extra time used).
+//
+func GcImportData(imports map[string]*Package, filename, id string, data *bufio.Reader) (pkg *Package, err error) {
+ // support for gcParser error handling
+ defer func() {
+ if r := recover(); r != nil {
+ err = r.(importError) // will re-panic if r is not an importError
+ }
+ }()
+
+ var p gcParser
+ p.init(filename, id, data, imports)
+ pkg = p.parseExport()
+
+ return
+}
+
+// GcImport imports a gc-generated package given its import path, adds the
+// corresponding package object to the imports map, and returns the object.
+// Local import paths are interpreted relative to the current working directory.
+// The imports map must contains all packages already imported.
+// GcImport satisfies the ast.Importer signature.
+//
+func GcImport(imports map[string]*Package, path string) (pkg *Package, err error) {
+ if path == "unsafe" {
+ return Unsafe, nil
+ }
+
+ srcDir := "."
+ if build.IsLocalImport(path) {
+ srcDir, err = os.Getwd()
+ if err != nil {
+ return
+ }
+ }
+
+ filename, id := FindPkg(path, srcDir)
+ if filename == "" {
+ err = errors.New("can't find import: " + id)
+ return
+ }
+
+ // no need to re-import if the package was imported completely before
+ if pkg = imports[id]; pkg != nil && pkg.Complete {
+ return
+ }
+
+ // open file
+ f, err := os.Open(filename)
+ if err != nil {
+ return
+ }
+ defer func() {
+ f.Close()
+ if err != nil {
+ // add file name to error
+ err = fmt.Errorf("reading export data: %s: %v", filename, err)
+ }
+ }()
+
+ buf := bufio.NewReader(f)
+ if err = FindGcExportData(buf); err != nil {
+ return
+ }
+
+ pkg, err = GcImportData(imports, filename, id, buf)
+
+ return
+}
+
+// ----------------------------------------------------------------------------
+// gcParser
+
+// gcParser parses the exports inside a gc compiler-produced
+// object/archive file and populates its scope with the results.
+type gcParser struct {
+ scanner scanner.Scanner
+ tok rune // current token
+ lit string // literal string; only valid for Ident, Int, String tokens
+ id string // package id of imported package
+ imports map[string]*Package // package id -> package object
+}
+
+func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*Package) {
+ p.scanner.Init(src)
+ p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
+ p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
+ p.scanner.Whitespace = 1<<'\t' | 1<<' '
+ p.scanner.Filename = filename // for good error messages
+ p.next()
+ p.id = id
+ p.imports = imports
+ // leave for debugging
+ if false {
+ // check consistency of imports map
+ for _, pkg := range imports {
+ if pkg.Name == "" {
+ fmt.Printf("no package name for %s\n", pkg.Path)
+ }
+ }
+ }
+}
+
+func (p *gcParser) next() {
+ p.tok = p.scanner.Scan()
+ switch p.tok {
+ case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·':
+ p.lit = p.scanner.TokenText()
+ default:
+ p.lit = ""
+ }
+ // leave for debugging
+ if false {
+ fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
+ }
+}
+
+func declConst(pkg *Package, name string) *Const {
+ // the constant may have been imported before - if it exists
+ // already in the respective scope, return that constant
+ scope := pkg.Scope
+ if obj := scope.Lookup(name); obj != nil {
+ return obj.(*Const)
+ }
+ // otherwise create a new constant and insert it into the scope
+ obj := &Const{Pkg: pkg, Name: name}
+ scope.Insert(obj)
+ return obj
+}
+
+func declTypeName(pkg *Package, name string) *TypeName {
+ scope := pkg.Scope
+ if obj := scope.Lookup(name); obj != nil {
+ return obj.(*TypeName)
+ }
+ obj := &TypeName{Pkg: pkg, Name: name}
+ // a named type may be referred to before the underlying type
+ // is known - set it up
+ obj.Type = &NamedType{Obj: obj}
+ scope.Insert(obj)
+ return obj
+}
+
+func declVar(pkg *Package, name string) *Var {
+ scope := pkg.Scope
+ if obj := scope.Lookup(name); obj != nil {
+ return obj.(*Var)
+ }
+ obj := &Var{Pkg: pkg, Name: name}
+ scope.Insert(obj)
+ return obj
+}
+
+func declFunc(pkg *Package, name string) *Func {
+ scope := pkg.Scope
+ if obj := scope.Lookup(name); obj != nil {
+ return obj.(*Func)
+ }
+ obj := &Func{Pkg: pkg, Name: name}
+ scope.Insert(obj)
+ return obj
+}
+
+// ----------------------------------------------------------------------------
+// Error handling
+
+// Internal errors are boxed as importErrors.
+type importError struct {
+ pos scanner.Position
+ err error
+}
+
+func (e importError) Error() string {
+ return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
+}
+
+func (p *gcParser) error(err interface{}) {
+ if s, ok := err.(string); ok {
+ err = errors.New(s)
+ }
+ // panic with a runtime.Error if err is not an error
+ panic(importError{p.scanner.Pos(), err.(error)})
+}
+
+func (p *gcParser) errorf(format string, args ...interface{}) {
+ p.error(fmt.Sprintf(format, args...))
+}
+
+func (p *gcParser) expect(tok rune) string {
+ lit := p.lit
+ if p.tok != tok {
+ p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
+ }
+ p.next()
+ return lit
+}
+
+func (p *gcParser) expectSpecial(tok string) {
+ sep := 'x' // not white space
+ i := 0
+ for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' {
+ sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
+ p.next()
+ i++
+ }
+ if i < len(tok) {
+ p.errorf("expected %q, got %q", tok, tok[0:i])
+ }
+}
+
+func (p *gcParser) expectKeyword(keyword string) {
+ lit := p.expect(scanner.Ident)
+ if lit != keyword {
+ p.errorf("expected keyword %s, got %q", keyword, lit)
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Qualified and unqualified names
+
+// PackageId = string_lit .
+//
+func (p *gcParser) parsePackageId() string {
+ id, err := strconv.Unquote(p.expect(scanner.String))
+ if err != nil {
+ p.error(err)
+ }
+ // id == "" stands for the imported package id
+ // (only known at time of package installation)
+ if id == "" {
+ id = p.id
+ }
+ return id
+}
+
+// PackageName = ident .
+//
+func (p *gcParser) parsePackageName() string {
+ return p.expect(scanner.Ident)
+}
+
+// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
+func (p *gcParser) parseDotIdent() string {
+ ident := ""
+ if p.tok != scanner.Int {
+ sep := 'x' // not white space
+ for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' {
+ ident += p.lit
+ sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
+ p.next()
+ }
+ }
+ if ident == "" {
+ p.expect(scanner.Ident) // use expect() for error handling
+ }
+ return ident
+}
+
+// QualifiedName = "@" PackageId "." dotIdentifier .
+//
+func (p *gcParser) parseQualifiedName() (id, name string) {
+ p.expect('@')
+ id = p.parsePackageId()
+ p.expect('.')
+ name = p.parseDotIdent()
+ return
+}
+
+// getPkg returns the package for a given id. If the package is
+// not found but we have a package name, create the package and
+// add it to the p.imports map.
+//
+func (p *gcParser) getPkg(id, name string) *Package {
+ // package unsafe is not in the imports map - handle explicitly
+ if id == "unsafe" {
+ return Unsafe
+ }
+ pkg := p.imports[id]
+ if pkg == nil && name != "" {
+ pkg = &Package{Name: name, Path: id, Scope: new(Scope)}
+ p.imports[id] = pkg
+ }
+ return pkg
+}
+
+// parseExportedName is like parseQualifiedName, but
+// the package id is resolved to an imported *Package.
+//
+func (p *gcParser) parseExportedName() (pkg *Package, name string) {
+ id, name := p.parseQualifiedName()
+ pkg = p.getPkg(id, "")
+ if pkg == nil {
+ p.errorf("%s package not found", id)
+ }
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Types
+
+// BasicType = identifier .
+//
+func (p *gcParser) parseBasicType() Type {
+ id := p.expect(scanner.Ident)
+ obj := Universe.Lookup(id)
+ if obj, ok := obj.(*TypeName); ok {
+ return obj.Type
+ }
+ p.errorf("not a basic type: %s", id)
+ return nil
+}
+
+// ArrayType = "[" int_lit "]" Type .
+//
+func (p *gcParser) parseArrayType() Type {
+ // "[" already consumed and lookahead known not to be "]"
+ lit := p.expect(scanner.Int)
+ p.expect(']')
+ elt := p.parseType()
+ n, err := strconv.ParseInt(lit, 10, 64)
+ if err != nil {
+ p.error(err)
+ }
+ return &Array{Len: n, Elt: elt}
+}
+
+// MapType = "map" "[" Type "]" Type .
+//
+func (p *gcParser) parseMapType() Type {
+ p.expectKeyword("map")
+ p.expect('[')
+ key := p.parseType()
+ p.expect(']')
+ elt := p.parseType()
+ return &Map{Key: key, Elt: elt}
+}
+
+// Name = identifier | "?" | QualifiedName .
+//
+// If materializePkg is set, a package is returned for fully qualified names.
+// That package may be a fake package (without name, scope, and not in the
+// p.imports map), created for the sole purpose of providing a package path
+// for QualifiedNames. Fake packages are created when the package id is not
+// found in the p.imports map; we cannot create a real package in that case
+// because we don't have a package name.
+//
+// TODO(gri): consider changing QualifiedIdents to (path, name) pairs to
+// simplify this code.
+//
+func (p *gcParser) parseName(materializePkg bool) (pkg *Package, name string) {
+ switch p.tok {
+ case scanner.Ident:
+ name = p.lit
+ p.next()
+ case '?':
+ // anonymous
+ p.next()
+ case '@':
+ // exported name prefixed with package path
+ var id string
+ id, name = p.parseQualifiedName()
+ if materializePkg {
+ // we don't have a package name - if the package
+ // doesn't exist yet, create a fake package instead
+ pkg = p.getPkg(id, "")
+ if pkg == nil {
+ pkg = &Package{Path: id}
+ }
+ }
+ default:
+ p.error("name expected")
+ }
+ return
+}
+
+// Field = Name Type [ string_lit ] .
+//
+func (p *gcParser) parseField() *Field {
+ var f Field
+ f.Pkg, f.Name = p.parseName(true)
+ f.Type = p.parseType()
+ if p.tok == scanner.String {
+ f.Tag = p.expect(scanner.String)
+ }
+ if f.Name == "" {
+ // anonymous field - typ must be T or *T and T must be a type name
+ if typ, ok := deref(f.Type).(*NamedType); ok && typ.Obj != nil {
+ f.Name = typ.Obj.GetName()
+ f.IsAnonymous = true
+ } else {
+ p.errorf("anonymous field expected")
+ }
+ }
+ return &f
+}
+
+// StructType = "struct" "{" [ FieldList ] "}" .
+// FieldList = Field { ";" Field } .
+//
+func (p *gcParser) parseStructType() Type {
+ var fields []*Field
+
+ p.expectKeyword("struct")
+ p.expect('{')
+ for p.tok != '}' {
+ if len(fields) > 0 {
+ p.expect(';')
+ }
+ fields = append(fields, p.parseField())
+ }
+ p.expect('}')
+
+ return &Struct{Fields: fields}
+}
+
+// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
+//
+func (p *gcParser) parseParameter() (par *Var, isVariadic bool) {
+ _, name := p.parseName(false)
+ if name == "" {
+ name = "_" // cannot access unnamed identifiers
+ }
+ if p.tok == '.' {
+ p.expectSpecial("...")
+ isVariadic = true
+ }
+ typ := p.parseType()
+ // ignore argument tag (e.g. "noescape")
+ if p.tok == scanner.String {
+ p.next()
+ }
+ par = &Var{Name: name, Type: typ} // Pkg == nil
+ return
+}
+
+// Parameters = "(" [ ParameterList ] ")" .
+// ParameterList = { Parameter "," } Parameter .
+//
+func (p *gcParser) parseParameters() (list []*Var, isVariadic bool) {
+ p.expect('(')
+ for p.tok != ')' {
+ if len(list) > 0 {
+ p.expect(',')
+ }
+ par, variadic := p.parseParameter()
+ list = append(list, par)
+ if variadic {
+ if isVariadic {
+ p.error("... not on final argument")
+ }
+ isVariadic = true
+ }
+ }
+ p.expect(')')
+
+ return
+}
+
+// Signature = Parameters [ Result ] .
+// Result = Type | Parameters .
+//
+func (p *gcParser) parseSignature() *Signature {
+ params, isVariadic := p.parseParameters()
+
+ // optional result type
+ var results []*Var
+ if p.tok == '(' {
+ var variadic bool
+ results, variadic = p.parseParameters()
+ if variadic {
+ p.error("... not permitted on result type")
+ }
+ }
+
+ return &Signature{Params: params, Results: results, IsVariadic: isVariadic}
+}
+
+// InterfaceType = "interface" "{" [ MethodList ] "}" .
+// MethodList = Method { ";" Method } .
+// Method = Name Signature .
+//
+// The methods of embedded interfaces are always "inlined"
+// by the compiler and thus embedded interfaces are never
+// visible in the export data.
+//
+func (p *gcParser) parseInterfaceType() Type {
+ var methods []*Method
+
+ p.expectKeyword("interface")
+ p.expect('{')
+ for p.tok != '}' {
+ if len(methods) > 0 {
+ p.expect(';')
+ }
+ pkg, name := p.parseName(true)
+ typ := p.parseSignature()
+ methods = append(methods, &Method{QualifiedName{pkg, name}, typ})
+ }
+ p.expect('}')
+
+ return &Interface{Methods: methods}
+}
+
+// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
+//
+func (p *gcParser) parseChanType() Type {
+ dir := ast.SEND | ast.RECV
+ if p.tok == scanner.Ident {
+ p.expectKeyword("chan")
+ if p.tok == '<' {
+ p.expectSpecial("<-")
+ dir = ast.SEND
+ }
+ } else {
+ p.expectSpecial("<-")
+ p.expectKeyword("chan")
+ dir = ast.RECV
+ }
+ elt := p.parseType()
+ return &Chan{Dir: dir, Elt: elt}
+}
+
+// Type =
+// BasicType | TypeName | ArrayType | SliceType | StructType |
+// PointerType | FuncType | InterfaceType | MapType | ChanType |
+// "(" Type ")" .
+//
+// BasicType = ident .
+// TypeName = ExportedName .
+// SliceType = "[" "]" Type .
+// PointerType = "*" Type .
+// FuncType = "func" Signature .
+//
+func (p *gcParser) parseType() Type {
+ switch p.tok {
+ case scanner.Ident:
+ switch p.lit {
+ default:
+ return p.parseBasicType()
+ case "struct":
+ return p.parseStructType()
+ case "func":
+ // FuncType
+ p.next()
+ return p.parseSignature()
+ case "interface":
+ return p.parseInterfaceType()
+ case "map":
+ return p.parseMapType()
+ case "chan":
+ return p.parseChanType()
+ }
+ case '@':
+ // TypeName
+ pkg, name := p.parseExportedName()
+ return declTypeName(pkg, name).Type
+ case '[':
+ p.next() // look ahead
+ if p.tok == ']' {
+ // SliceType
+ p.next()
+ return &Slice{Elt: p.parseType()}
+ }
+ return p.parseArrayType()
+ case '*':
+ // PointerType
+ p.next()
+ return &Pointer{Base: p.parseType()}
+ case '<':
+ return p.parseChanType()
+ case '(':
+ // "(" Type ")"
+ p.next()
+ typ := p.parseType()
+ p.expect(')')
+ return typ
+ }
+ p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit)
+ return nil
+}
+
+// ----------------------------------------------------------------------------
+// Declarations
+
+// ImportDecl = "import" PackageName PackageId .
+//
+func (p *gcParser) parseImportDecl() {
+ p.expectKeyword("import")
+ name := p.parsePackageName()
+ p.getPkg(p.parsePackageId(), name)
+}
+
+// int_lit = [ "+" | "-" ] { "0" ... "9" } .
+//
+func (p *gcParser) parseInt() (neg bool, val string) {
+ switch p.tok {
+ case '-':
+ neg = true
+ fallthrough
+ case '+':
+ p.next()
+ }
+ val = p.expect(scanner.Int)
+ return
+}
+
+// number = int_lit [ "p" int_lit ] .
+//
+func (p *gcParser) parseNumber() (x operand) {
+ x.mode = constant
+
+ // mantissa
+ neg, val := p.parseInt()
+ mant, ok := new(big.Int).SetString(val, 0)
+ assert(ok)
+ if neg {
+ mant.Neg(mant)
+ }
+
+ if p.lit == "p" {
+ // exponent (base 2)
+ p.next()
+ neg, val = p.parseInt()
+ exp64, err := strconv.ParseUint(val, 10, 0)
+ if err != nil {
+ p.error(err)
+ }
+ exp := uint(exp64)
+ if neg {
+ denom := big.NewInt(1)
+ denom.Lsh(denom, exp)
+ x.typ = Typ[UntypedFloat]
+ x.val = normalizeRatConst(new(big.Rat).SetFrac(mant, denom))
+ return
+ }
+ if exp > 0 {
+ mant.Lsh(mant, exp)
+ }
+ x.typ = Typ[UntypedFloat]
+ x.val = normalizeIntConst(mant)
+ return
+ }
+
+ x.typ = Typ[UntypedInt]
+ x.val = normalizeIntConst(mant)
+ return
+}
+
+// ConstDecl = "const" ExportedName [ Type ] "=" Literal .
+// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit .
+// bool_lit = "true" | "false" .
+// complex_lit = "(" float_lit "+" float_lit "i" ")" .
+// rune_lit = "(" int_lit "+" int_lit ")" .
+// string_lit = `"` { unicode_char } `"` .
+//
+func (p *gcParser) parseConstDecl() {
+ p.expectKeyword("const")
+ pkg, name := p.parseExportedName()
+ obj := declConst(pkg, name)
+ var x operand
+ if p.tok != '=' {
+ obj.Type = p.parseType()
+ }
+ p.expect('=')
+ switch p.tok {
+ case scanner.Ident:
+ // bool_lit
+ if p.lit != "true" && p.lit != "false" {
+ p.error("expected true or false")
+ }
+ x.typ = Typ[UntypedBool]
+ x.val = p.lit == "true"
+ p.next()
+
+ case '-', scanner.Int:
+ // int_lit
+ x = p.parseNumber()
+
+ case '(':
+ // complex_lit or rune_lit
+ p.next()
+ if p.tok == scanner.Char {
+ p.next()
+ p.expect('+')
+ x = p.parseNumber()
+ x.typ = Typ[UntypedRune]
+ p.expect(')')
+ break
+ }
+ re := p.parseNumber()
+ p.expect('+')
+ im := p.parseNumber()
+ p.expectKeyword("i")
+ p.expect(')')
+ x.typ = Typ[UntypedComplex]
+ // TODO(gri) fix this
+ _, _ = re, im
+ x.val = zeroConst
+
+ case scanner.Char:
+ // rune_lit
+ x.setConst(token.CHAR, p.lit)
+ p.next()
+
+ case scanner.String:
+ // string_lit
+ x.setConst(token.STRING, p.lit)
+ p.next()
+
+ default:
+ p.errorf("expected literal got %s", scanner.TokenString(p.tok))
+ }
+ if obj.Type == nil {
+ obj.Type = x.typ
+ }
+ assert(x.val != nil)
+ obj.Val = x.val
+}
+
+// TypeDecl = "type" ExportedName Type .
+//
+func (p *gcParser) parseTypeDecl() {
+ p.expectKeyword("type")
+ pkg, name := p.parseExportedName()
+ obj := declTypeName(pkg, name)
+
+ // The type object may have been imported before and thus already
+ // have a type associated with it. We still need to parse the type
+ // structure, but throw it away if the object already has a type.
+ // This ensures that all imports refer to the same type object for
+ // a given type declaration.
+ typ := p.parseType()
+
+ if name := obj.Type.(*NamedType); name.Underlying == nil {
+ name.Underlying = typ
+ }
+}
+
+// VarDecl = "var" ExportedName Type .
+//
+func (p *gcParser) parseVarDecl() {
+ p.expectKeyword("var")
+ pkg, name := p.parseExportedName()
+ obj := declVar(pkg, name)
+ obj.Type = p.parseType()
+}
+
+// Func = Signature [ Body ] .
+// Body = "{" ... "}" .
+//
+func (p *gcParser) parseFunc() *Signature {
+ sig := p.parseSignature()
+ if p.tok == '{' {
+ p.next()
+ for i := 1; i > 0; p.next() {
+ switch p.tok {
+ case '{':
+ i++
+ case '}':
+ i--
+ }
+ }
+ }
+ return sig
+}
+
+// MethodDecl = "func" Receiver Name Func .
+// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
+//
+func (p *gcParser) parseMethodDecl() {
+ // "func" already consumed
+ p.expect('(')
+ recv, _ := p.parseParameter() // receiver
+ p.expect(')')
+
+ // determine receiver base type object
+ typ := recv.Type
+ if ptr, ok := typ.(*Pointer); ok {
+ typ = ptr.Base
+ }
+ base := typ.(*NamedType)
+
+ // parse method name, signature, and possibly inlined body
+ pkg, name := p.parseName(true) // unexported method names in imports are qualified with their package.
+ sig := p.parseFunc()
+ sig.Recv = recv
+
+ // add method to type unless type was imported before
+ // and method exists already
+ // TODO(gri) investigate if this can be avoided
+ for _, m := range base.Methods {
+ if m.Name == name {
+ return // method was added before
+ }
+ }
+ base.Methods = append(base.Methods, &Method{QualifiedName{pkg, name}, sig})
+}
+
+// FuncDecl = "func" ExportedName Func .
+//
+func (p *gcParser) parseFuncDecl() {
+ // "func" already consumed
+ pkg, name := p.parseExportedName()
+ typ := p.parseFunc()
+ declFunc(pkg, name).Type = typ
+}
+
+// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
+//
+func (p *gcParser) parseDecl() {
+ switch p.lit {
+ case "import":
+ p.parseImportDecl()
+ case "const":
+ p.parseConstDecl()
+ case "type":
+ p.parseTypeDecl()
+ case "var":
+ p.parseVarDecl()
+ case "func":
+ p.next() // look ahead
+ if p.tok == '(' {
+ p.parseMethodDecl()
+ } else {
+ p.parseFuncDecl()
+ }
+ }
+ p.expect('\n')
+}
+
+// ----------------------------------------------------------------------------
+// Export
+
+// Export = "PackageClause { Decl } "$$" .
+// PackageClause = "package" PackageName [ "safe" ] "\n" .
+//
+func (p *gcParser) parseExport() *Package {
+ p.expectKeyword("package")
+ name := p.parsePackageName()
+ if p.tok != '\n' {
+ // A package is safe if it was compiled with the -u flag,
+ // which disables the unsafe package.
+ // TODO(gri) remember "safe" package
+ p.expectKeyword("safe")
+ }
+ p.expect('\n')
+
+ pkg := p.getPkg(p.id, name)
+
+ for p.tok != '$' && p.tok != scanner.EOF {
+ p.parseDecl()
+ }
+
+ if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' {
+ // don't call next()/expect() since reading past the
+ // export data may cause scanner errors (e.g. NUL chars)
+ p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch)
+ }
+
+ if n := p.scanner.ErrorCount; n != 0 {
+ p.errorf("expected no scanner errors, got %d", n)
+ }
+
+ // package was imported completely and without errors
+ pkg.Complete = true
+
+ return pkg
+}
diff --git a/src/pkg/go/types/gcimporter_test.go b/src/pkg/go/types/gcimporter_test.go
new file mode 100644
index 000000000..b793eb4cb
--- /dev/null
+++ b/src/pkg/go/types/gcimporter_test.go
@@ -0,0 +1,180 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "go/ast"
+ "go/build"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+)
+
+var gcPath string // Go compiler path
+
+func init() {
+ // determine compiler
+ var gc string
+ switch runtime.GOARCH {
+ case "386":
+ gc = "8g"
+ case "amd64":
+ gc = "6g"
+ case "arm":
+ gc = "5g"
+ default:
+ gcPath = "unknown-GOARCH-compiler"
+ return
+ }
+ gcPath = filepath.Join(build.ToolDir, gc)
+}
+
+func compile(t *testing.T, dirname, filename string) string {
+ cmd := exec.Command(gcPath, filename)
+ cmd.Dir = dirname
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("%s", out)
+ t.Fatalf("%s %s failed: %s", gcPath, filename, err)
+ }
+ archCh, _ := build.ArchChar(runtime.GOARCH)
+ // filename should end with ".go"
+ return filepath.Join(dirname, filename[:len(filename)-2]+archCh)
+}
+
+// Use the same global imports map for all tests. The effect is
+// as if all tested packages were imported into a single package.
+var imports = make(map[string]*Package)
+
+func testPath(t *testing.T, path string) bool {
+ t0 := time.Now()
+ _, err := GcImport(imports, path)
+ if err != nil {
+ t.Errorf("testPath(%s): %s", path, err)
+ return false
+ }
+ t.Logf("testPath(%s): %v", path, time.Since(t0))
+ return true
+}
+
+const maxTime = 30 * time.Second
+
+func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
+ dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
+ list, err := ioutil.ReadDir(dirname)
+ if err != nil {
+ t.Fatalf("testDir(%s): %s", dirname, err)
+ }
+ for _, f := range list {
+ if time.Now().After(endTime) {
+ t.Log("testing time used up")
+ return
+ }
+ switch {
+ case !f.IsDir():
+ // try extensions
+ for _, ext := range pkgExts {
+ if strings.HasSuffix(f.Name(), ext) {
+ name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
+ if testPath(t, filepath.Join(dir, name)) {
+ nimports++
+ }
+ }
+ }
+ case f.IsDir():
+ nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
+ }
+ }
+ return
+}
+
+func TestGcImport(t *testing.T) {
+ // On cross-compile builds, the path will not exist.
+ // Need to use GOHOSTOS, which is not available.
+ if _, err := os.Stat(gcPath); err != nil {
+ t.Skipf("skipping test: %v", err)
+ }
+
+ if outFn := compile(t, "testdata", "exports.go"); outFn != "" {
+ defer os.Remove(outFn)
+ }
+
+ nimports := 0
+ if testPath(t, "./testdata/exports") {
+ nimports++
+ }
+ nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages
+ t.Logf("tested %d imports", nimports)
+}
+
+var importedObjectTests = []struct {
+ name string
+ kind ast.ObjKind
+ typ string
+}{
+ {"unsafe.Pointer", ast.Typ, "Pointer"},
+ {"math.Pi", ast.Con, "untyped float"},
+ {"io.Reader", ast.Typ, "interface{Read(p []byte) (n int, err error)}"},
+ {"io.ReadWriter", ast.Typ, "interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"},
+ {"math.Sin", ast.Fun, "func(x·2 float64) (_ float64)"},
+ // TODO(gri) add more tests
+}
+
+func TestGcImportedTypes(t *testing.T) {
+ // This package does not yet know how to read gccgo export data.
+ if runtime.Compiler == "gccgo" {
+ return
+ }
+ for _, test := range importedObjectTests {
+ s := strings.Split(test.name, ".")
+ if len(s) != 2 {
+ t.Fatal("inconsistent test data")
+ }
+ importPath := s[0]
+ objName := s[1]
+
+ pkg, err := GcImport(imports, importPath)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ obj := pkg.Scope.Lookup(objName)
+
+ // TODO(gri) should define an accessor on Object
+ var kind ast.ObjKind
+ var typ Type
+ switch obj := obj.(type) {
+ case *Const:
+ kind = ast.Con
+ typ = obj.Type
+ case *TypeName:
+ kind = ast.Typ
+ typ = obj.Type
+ case *Var:
+ kind = ast.Var
+ typ = obj.Type
+ case *Func:
+ kind = ast.Fun
+ typ = obj.Type
+ default:
+ unreachable()
+ }
+
+ if kind != test.kind {
+ t.Errorf("%s: got kind = %q; want %q", test.name, kind, test.kind)
+ }
+
+ str := typeString(underlying(typ))
+ if str != test.typ {
+ t.Errorf("%s: got type = %q; want %q", test.name, typ, test.typ)
+ }
+ }
+}
diff --git a/src/pkg/go/types/objects.go b/src/pkg/go/types/objects.go
new file mode 100644
index 000000000..02291d34c
--- /dev/null
+++ b/src/pkg/go/types/objects.go
@@ -0,0 +1,186 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// An Object describes a named language entity such as a package,
+// constant, type, variable, function (incl. methods), or label.
+// All objects implement the Object interface.
+//
+type Object interface {
+ GetPkg() *Package
+ GetName() string
+ GetType() Type
+ GetPos() token.Pos
+
+ anObject()
+}
+
+// A Package represents the contents (objects) of a Go package.
+type Package struct {
+ Name string
+ Path string // import path, "" for current (non-imported) package
+ Scope *Scope // package-level scope
+ Imports map[string]*Package // map of import paths to imported packages
+ Complete bool // if set, this package was imported completely
+
+ spec *ast.ImportSpec
+}
+
+// A Const represents a declared constant.
+type Const struct {
+ Pkg *Package
+ Name string
+ Type Type
+ Val interface{}
+
+ spec *ast.ValueSpec
+}
+
+// A TypeName represents a declared type.
+type TypeName struct {
+ Pkg *Package
+ Name string
+ Type Type // *NamedType or *Basic
+
+ spec *ast.TypeSpec
+}
+
+// A Variable represents a declared variable (including function parameters and results).
+type Var struct {
+ Pkg *Package // nil for parameters
+ Name string
+ Type Type
+
+ visited bool // for initialization cycle detection
+ decl interface{}
+}
+
+// A Func represents a declared function.
+type Func struct {
+ Pkg *Package
+ Name string
+ Type Type // *Signature or *Builtin
+
+ decl *ast.FuncDecl
+}
+
+func (obj *Package) GetPkg() *Package { return obj }
+func (obj *Const) GetPkg() *Package { return obj.Pkg }
+func (obj *TypeName) GetPkg() *Package { return obj.Pkg }
+func (obj *Var) GetPkg() *Package { return obj.Pkg }
+func (obj *Func) GetPkg() *Package { return obj.Pkg }
+
+func (obj *Package) GetName() string { return obj.Name }
+func (obj *Const) GetName() string { return obj.Name }
+func (obj *TypeName) GetName() string { return obj.Name }
+func (obj *Var) GetName() string { return obj.Name }
+func (obj *Func) GetName() string { return obj.Name }
+
+func (obj *Package) GetType() Type { return Typ[Invalid] }
+func (obj *Const) GetType() Type { return obj.Type }
+func (obj *TypeName) GetType() Type { return obj.Type }
+func (obj *Var) GetType() Type { return obj.Type }
+func (obj *Func) GetType() Type { return obj.Type }
+
+func (obj *Package) GetPos() token.Pos {
+ if obj.spec != nil {
+ return obj.spec.Pos()
+ }
+ return token.NoPos
+}
+
+func (obj *Const) GetPos() token.Pos {
+ for _, n := range obj.spec.Names {
+ if n.Name == obj.Name {
+ return n.Pos()
+ }
+ }
+ return token.NoPos
+}
+func (obj *TypeName) GetPos() token.Pos {
+ if obj.spec != nil {
+ return obj.spec.Pos()
+ }
+ return token.NoPos
+}
+
+func (obj *Var) GetPos() token.Pos {
+ switch d := obj.decl.(type) {
+ case *ast.Field:
+ for _, n := range d.Names {
+ if n.Name == obj.Name {
+ return n.Pos()
+ }
+ }
+ case *ast.ValueSpec:
+ for _, n := range d.Names {
+ if n.Name == obj.Name {
+ return n.Pos()
+ }
+ }
+ case *ast.AssignStmt:
+ for _, x := range d.Lhs {
+ if ident, isIdent := x.(*ast.Ident); isIdent && ident.Name == obj.Name {
+ return ident.Pos()
+ }
+ }
+ }
+ return token.NoPos
+}
+func (obj *Func) GetPos() token.Pos {
+ if obj.decl != nil && obj.decl.Name != nil {
+ return obj.decl.Name.Pos()
+ }
+ return token.NoPos
+}
+
+func (*Package) anObject() {}
+func (*Const) anObject() {}
+func (*TypeName) anObject() {}
+func (*Var) anObject() {}
+func (*Func) anObject() {}
+
+// newObj returns a new Object for a given *ast.Object.
+// It does not canonicalize them (it always returns a new one).
+// For canonicalization, see check.lookup.
+//
+// TODO(gri) Once we do identifier resolution completely in
+// in the typechecker, this functionality can go.
+//
+func newObj(pkg *Package, astObj *ast.Object) Object {
+ assert(pkg != nil)
+ name := astObj.Name
+ typ, _ := astObj.Type.(Type)
+ switch astObj.Kind {
+ case ast.Bad:
+ // ignore
+ case ast.Pkg:
+ unreachable()
+ case ast.Con:
+ return &Const{Pkg: pkg, Name: name, Type: typ, Val: astObj.Data, spec: astObj.Decl.(*ast.ValueSpec)}
+ case ast.Typ:
+ return &TypeName{Pkg: pkg, Name: name, Type: typ, spec: astObj.Decl.(*ast.TypeSpec)}
+ case ast.Var:
+ switch astObj.Decl.(type) {
+ case *ast.Field: // function parameters
+ case *ast.ValueSpec: // proper variable declarations
+ case *ast.AssignStmt: // short variable declarations
+ default:
+ unreachable() // everything else is not ok
+ }
+ return &Var{Pkg: pkg, Name: name, Type: typ, decl: astObj.Decl}
+ case ast.Fun:
+ return &Func{Pkg: pkg, Name: name, Type: typ, decl: astObj.Decl.(*ast.FuncDecl)}
+ case ast.Lbl:
+ unreachable() // for now
+ }
+ unreachable()
+ return nil
+}
diff --git a/src/pkg/go/types/operand.go b/src/pkg/go/types/operand.go
new file mode 100644
index 000000000..982ffef8d
--- /dev/null
+++ b/src/pkg/go/types/operand.go
@@ -0,0 +1,411 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file defines operands and associated operations.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/token"
+)
+
+// An operandMode specifies the (addressing) mode of an operand.
+type operandMode int
+
+const (
+ invalid operandMode = iota // operand is invalid (due to an earlier error) - ignore
+ novalue // operand represents no value (result of a function call w/o result)
+ typexpr // operand is a type
+ constant // operand is a constant; the operand's typ is a Basic type
+ variable // operand is an addressable variable
+ value // operand is a computed value
+ valueok // like mode == value, but operand may be used in a comma,ok expression
+)
+
+var operandModeString = [...]string{
+ invalid: "invalid",
+ novalue: "no value",
+ typexpr: "type",
+ constant: "constant",
+ variable: "variable",
+ value: "value",
+ valueok: "value,ok",
+}
+
+// An operand represents an intermediate value during type checking.
+// Operands have an (addressing) mode, the expression evaluating to
+// the operand, the operand's type, and for constants a constant value.
+//
+type operand struct {
+ mode operandMode
+ expr ast.Expr
+ typ Type
+ val interface{}
+}
+
+// pos returns the position of the expression corresponding to x.
+// If x is invalid the position is token.NoPos.
+//
+func (x *operand) pos() token.Pos {
+ // x.expr may not be set if x is invalid
+ if x.expr == nil {
+ return token.NoPos
+ }
+ return x.expr.Pos()
+}
+
+func (x *operand) String() string {
+ if x.mode == invalid {
+ return "invalid operand"
+ }
+ var buf bytes.Buffer
+ if x.expr != nil {
+ buf.WriteString(exprString(x.expr))
+ buf.WriteString(" (")
+ }
+ buf.WriteString(operandModeString[x.mode])
+ if x.mode == constant {
+ format := " %v"
+ if isString(x.typ) {
+ format = " %q"
+ }
+ fmt.Fprintf(&buf, format, x.val)
+ }
+ if x.mode != novalue && (x.mode != constant || !isUntyped(x.typ)) {
+ fmt.Fprintf(&buf, " of type %s", typeString(x.typ))
+ }
+ if x.expr != nil {
+ buf.WriteByte(')')
+ }
+ return buf.String()
+}
+
+// setConst sets x to the untyped constant for literal lit.
+func (x *operand) setConst(tok token.Token, lit string) {
+ x.mode = invalid
+
+ var kind BasicKind
+ var val interface{}
+ switch tok {
+ case token.INT:
+ kind = UntypedInt
+ val = makeIntConst(lit)
+
+ case token.FLOAT:
+ kind = UntypedFloat
+ val = makeFloatConst(lit)
+
+ case token.IMAG:
+ kind = UntypedComplex
+ val = makeComplexConst(lit)
+
+ case token.CHAR:
+ kind = UntypedRune
+ val = makeRuneConst(lit)
+
+ case token.STRING:
+ kind = UntypedString
+ val = makeStringConst(lit)
+ }
+
+ if val != nil {
+ x.mode = constant
+ x.typ = Typ[kind]
+ x.val = val
+ }
+}
+
+// isNil reports whether x is the predeclared nil constant.
+func (x *operand) isNil() bool {
+ return x.mode == constant && x.val == nilConst
+}
+
+// TODO(gri) The functions operand.isAssignable, checker.convertUntyped,
+// checker.isRepresentable, and checker.assignOperand are
+// overlapping in functionality. Need to simplify and clean up.
+
+// isAssignable reports whether x is assignable to a variable of type T.
+func (x *operand) isAssignable(ctxt *Context, T Type) bool {
+ if x.mode == invalid || T == Typ[Invalid] {
+ return true // avoid spurious errors
+ }
+
+ V := x.typ
+
+ // x's type is identical to T
+ if IsIdentical(V, T) {
+ return true
+ }
+
+ Vu := underlying(V)
+ Tu := underlying(T)
+
+ // x's type V and T have identical underlying types
+ // and at least one of V or T is not a named type
+ if IsIdentical(Vu, Tu) {
+ return !isNamed(V) || !isNamed(T)
+ }
+
+ // T is an interface type and x implements T
+ if Ti, ok := Tu.(*Interface); ok {
+ if m, _ := missingMethod(x.typ, Ti); m == nil {
+ return true
+ }
+ }
+
+ // x is a bidirectional channel value, T is a channel
+ // type, x's type V and T have identical element types,
+ // and at least one of V or T is not a named type
+ if Vc, ok := Vu.(*Chan); ok && Vc.Dir == ast.SEND|ast.RECV {
+ if Tc, ok := Tu.(*Chan); ok && IsIdentical(Vc.Elt, Tc.Elt) {
+ return !isNamed(V) || !isNamed(T)
+ }
+ }
+
+ // x is the predeclared identifier nil and T is a pointer,
+ // function, slice, map, channel, or interface type
+ if x.isNil() {
+ switch t := Tu.(type) {
+ case *Basic:
+ if t.Kind == UnsafePointer {
+ return true
+ }
+ case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface:
+ return true
+ }
+ return false
+ }
+
+ // x is an untyped constant representable by a value of type T
+ // TODO(gri) This is borrowing from checker.convertUntyped and
+ // checker.isRepresentable. Need to clean up.
+ if isUntyped(Vu) {
+ switch t := Tu.(type) {
+ case *Basic:
+ if x.mode == constant {
+ return isRepresentableConst(x.val, ctxt, t.Kind)
+ }
+ // The result of a comparison is an untyped boolean,
+ // but may not be a constant.
+ if Vb, _ := Vu.(*Basic); Vb != nil {
+ return Vb.Kind == UntypedBool && isBoolean(Tu)
+ }
+ case *Interface:
+ return x.isNil() || len(t.Methods) == 0
+ case *Pointer, *Signature, *Slice, *Map, *Chan:
+ return x.isNil()
+ }
+ }
+
+ return false
+}
+
+// isInteger reports whether x is a (typed or untyped) integer value.
+func (x *operand) isInteger(ctxt *Context) bool {
+ return x.mode == invalid ||
+ isInteger(x.typ) ||
+ x.mode == constant && isRepresentableConst(x.val, ctxt, UntypedInt)
+}
+
+// lookupResult represents the result of a struct field/method lookup.
+type lookupResult struct {
+ mode operandMode
+ typ Type
+ index []int // field index sequence; nil for methods
+}
+
+type embeddedType struct {
+ typ *NamedType
+ index []int // field index sequence
+ multiples bool // if set, typ is embedded multiple times at the same level
+}
+
+// lookupFieldBreadthFirst searches all types in list for a single entry (field
+// or method) of the given name from the given package. If such a field is found,
+// the result describes the field mode and type; otherwise the result mode is invalid.
+// (This function is similar in structure to FieldByNameFunc in reflect/type.go)
+//
+func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res lookupResult) {
+ // visited records the types that have been searched already.
+ visited := make(map[*NamedType]bool)
+
+ // embedded types of the next lower level
+ var next []embeddedType
+
+ // potentialMatch is invoked every time a match is found.
+ potentialMatch := func(multiples bool, mode operandMode, typ Type) bool {
+ if multiples || res.mode != invalid {
+ // name appeared already at this level - annihilate
+ res.mode = invalid
+ return false
+ }
+ // first appearance of name
+ res.mode = mode
+ res.typ = typ
+ res.index = nil
+ return true
+ }
+
+ // Search the current level if there is any work to do and collect
+ // embedded types of the next lower level in the next list.
+ for len(list) > 0 {
+ // The res.mode indicates whether we have found a match already
+ // on this level (mode != invalid), or not (mode == invalid).
+ assert(res.mode == invalid)
+
+ // start with empty next list (don't waste underlying array)
+ next = next[:0]
+
+ // look for name in all types at this level
+ for _, e := range list {
+ typ := e.typ
+ if visited[typ] {
+ continue
+ }
+ visited[typ] = true
+
+ // look for a matching attached method
+ for _, m := range typ.Methods {
+ if name.IsSame(m.QualifiedName) {
+ assert(m.Type != nil)
+ if !potentialMatch(e.multiples, value, m.Type) {
+ return // name collision
+ }
+ }
+ }
+
+ switch t := typ.Underlying.(type) {
+ case *Struct:
+ // look for a matching field and collect embedded types
+ for i, f := range t.Fields {
+ if name.IsSame(f.QualifiedName) {
+ assert(f.Type != nil)
+ if !potentialMatch(e.multiples, variable, f.Type) {
+ return // name collision
+ }
+ var index []int
+ index = append(index, e.index...) // copy e.index
+ index = append(index, i)
+ res.index = index
+ continue
+ }
+ // Collect embedded struct fields for searching the next
+ // lower level, but only if we have not seen a match yet
+ // (if we have a match it is either the desired field or
+ // we have a name collision on the same level; in either
+ // case we don't need to look further).
+ // Embedded fields are always of the form T or *T where
+ // T is a named type. If typ appeared multiple times at
+ // this level, f.Type appears multiple times at the next
+ // level.
+ if f.IsAnonymous && res.mode == invalid {
+ // Ignore embedded basic types - only user-defined
+ // named types can have methods or have struct fields.
+ if t, _ := deref(f.Type).(*NamedType); t != nil {
+ var index []int
+ index = append(index, e.index...) // copy e.index
+ index = append(index, i)
+ next = append(next, embeddedType{t, index, e.multiples})
+ }
+ }
+ }
+
+ case *Interface:
+ // look for a matching method
+ for _, m := range t.Methods {
+ if name.IsSame(m.QualifiedName) {
+ assert(m.Type != nil)
+ if !potentialMatch(e.multiples, value, m.Type) {
+ return // name collision
+ }
+ }
+ }
+ }
+ }
+
+ if res.mode != invalid {
+ // we found a single match on this level
+ return
+ }
+
+ // No match and no collision so far.
+ // Compute the list to search for the next level.
+ list = list[:0] // don't waste underlying array
+ for _, e := range next {
+ // Instead of adding the same type multiple times, look for
+ // it in the list and mark it as multiple if it was added
+ // before.
+ // We use a sequential search (instead of a map for next)
+ // because the lists tend to be small, can easily be reused,
+ // and explicit search appears to be faster in this case.
+ if alt := findType(list, e.typ); alt != nil {
+ alt.multiples = true
+ } else {
+ list = append(list, e)
+ }
+ }
+
+ }
+
+ return
+}
+
+func findType(list []embeddedType, typ *NamedType) *embeddedType {
+ for i := range list {
+ if p := &list[i]; p.typ == typ {
+ return p
+ }
+ }
+ return nil
+}
+
+func lookupField(typ Type, name QualifiedName) lookupResult {
+ typ = deref(typ)
+
+ if t, ok := typ.(*NamedType); ok {
+ for _, m := range t.Methods {
+ if name.IsSame(m.QualifiedName) {
+ assert(m.Type != nil)
+ return lookupResult{value, m.Type, nil}
+ }
+ }
+ typ = t.Underlying
+ }
+
+ switch t := typ.(type) {
+ case *Struct:
+ var next []embeddedType
+ for i, f := range t.Fields {
+ if name.IsSame(f.QualifiedName) {
+ return lookupResult{variable, f.Type, []int{i}}
+ }
+ if f.IsAnonymous {
+ // Possible optimization: If the embedded type
+ // is a pointer to the current type we could
+ // ignore it.
+ // Ignore embedded basic types - only user-defined
+ // named types can have methods or have struct fields.
+ if t, _ := deref(f.Type).(*NamedType); t != nil {
+ next = append(next, embeddedType{t, []int{i}, false})
+ }
+ }
+ }
+ if len(next) > 0 {
+ return lookupFieldBreadthFirst(next, name)
+ }
+
+ case *Interface:
+ for _, m := range t.Methods {
+ if name.IsSame(m.QualifiedName) {
+ return lookupResult{value, m.Type, nil}
+ }
+ }
+ }
+
+ // not found
+ return lookupResult{mode: invalid}
+}
diff --git a/src/pkg/go/types/predicates.go b/src/pkg/go/types/predicates.go
new file mode 100644
index 000000000..a99c91a4e
--- /dev/null
+++ b/src/pkg/go/types/predicates.go
@@ -0,0 +1,303 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements commonly used type predicates.
+
+package types
+
+func isNamed(typ Type) bool {
+ if _, ok := typ.(*Basic); ok {
+ return ok
+ }
+ _, ok := typ.(*NamedType)
+ return ok
+}
+
+func isBoolean(typ Type) bool {
+ t, ok := underlying(typ).(*Basic)
+ return ok && t.Info&IsBoolean != 0
+}
+
+func isInteger(typ Type) bool {
+ t, ok := underlying(typ).(*Basic)
+ return ok && t.Info&IsInteger != 0
+}
+
+func isUnsigned(typ Type) bool {
+ t, ok := underlying(typ).(*Basic)
+ return ok && t.Info&IsUnsigned != 0
+}
+
+func isFloat(typ Type) bool {
+ t, ok := underlying(typ).(*Basic)
+ return ok && t.Info&IsFloat != 0
+}
+
+func isComplex(typ Type) bool {
+ t, ok := underlying(typ).(*Basic)
+ return ok && t.Info&IsComplex != 0
+}
+
+func isNumeric(typ Type) bool {
+ t, ok := underlying(typ).(*Basic)
+ return ok && t.Info&IsNumeric != 0
+}
+
+func isString(typ Type) bool {
+ t, ok := underlying(typ).(*Basic)
+ return ok && t.Info&IsString != 0
+}
+
+func isUntyped(typ Type) bool {
+ t, ok := underlying(typ).(*Basic)
+ return ok && t.Info&IsUntyped != 0
+}
+
+func isOrdered(typ Type) bool {
+ t, ok := underlying(typ).(*Basic)
+ return ok && t.Info&IsOrdered != 0
+}
+
+func isConstType(typ Type) bool {
+ t, ok := underlying(typ).(*Basic)
+ return ok && t.Info&IsConstType != 0
+}
+
+func isComparable(typ Type) bool {
+ switch t := underlying(typ).(type) {
+ case *Basic:
+ return t.Kind != Invalid && t.Kind != UntypedNil
+ case *Pointer, *Interface, *Chan:
+ // assumes types are equal for pointers and channels
+ return true
+ case *Struct:
+ for _, f := range t.Fields {
+ if !isComparable(f.Type) {
+ return false
+ }
+ }
+ return true
+ case *Array:
+ return isComparable(t.Elt)
+ }
+ return false
+}
+
+func hasNil(typ Type) bool {
+ switch underlying(typ).(type) {
+ case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
+ return true
+ }
+ return false
+}
+
+// IsIdentical returns true if x and y are identical.
+func IsIdentical(x, y Type) bool {
+ if x == y {
+ return true
+ }
+
+ switch x := x.(type) {
+ case *Basic:
+ // Basic types are singletons except for the rune and byte
+ // aliases, thus we cannot solely rely on the x == y check
+ // above.
+ if y, ok := y.(*Basic); ok {
+ return x.Kind == y.Kind
+ }
+
+ case *Array:
+ // Two array types are identical if they have identical element types
+ // and the same array length.
+ if y, ok := y.(*Array); ok {
+ return x.Len == y.Len && IsIdentical(x.Elt, y.Elt)
+ }
+
+ case *Slice:
+ // Two slice types are identical if they have identical element types.
+ if y, ok := y.(*Slice); ok {
+ return IsIdentical(x.Elt, y.Elt)
+ }
+
+ case *Struct:
+ // Two struct types are identical if they have the same sequence of fields,
+ // and if corresponding fields have the same names, and identical types,
+ // and identical tags. Two anonymous fields are considered to have the same
+ // name. Lower-case field names from different packages are always different.
+ if y, ok := y.(*Struct); ok {
+ if len(x.Fields) == len(y.Fields) {
+ for i, f := range x.Fields {
+ g := y.Fields[i]
+ if !f.QualifiedName.IsSame(g.QualifiedName) ||
+ !IsIdentical(f.Type, g.Type) ||
+ f.Tag != g.Tag ||
+ f.IsAnonymous != g.IsAnonymous {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+ case *Pointer:
+ // Two pointer types are identical if they have identical base types.
+ if y, ok := y.(*Pointer); ok {
+ return IsIdentical(x.Base, y.Base)
+ }
+
+ case *Signature:
+ // Two function types are identical if they have the same number of parameters
+ // and result values, corresponding parameter and result types are identical,
+ // and either both functions are variadic or neither is. Parameter and result
+ // names are not required to match.
+ if y, ok := y.(*Signature); ok {
+ return identicalTypes(x.Params, y.Params) &&
+ identicalTypes(x.Results, y.Results) &&
+ x.IsVariadic == y.IsVariadic
+ }
+
+ case *Interface:
+ // Two interface types are identical if they have the same set of methods with
+ // the same names and identical function types. Lower-case method names from
+ // different packages are always different. The order of the methods is irrelevant.
+ if y, ok := y.(*Interface); ok {
+ return identicalMethods(x.Methods, y.Methods) // methods are sorted
+ }
+
+ case *Map:
+ // Two map types are identical if they have identical key and value types.
+ if y, ok := y.(*Map); ok {
+ return IsIdentical(x.Key, y.Key) && IsIdentical(x.Elt, y.Elt)
+ }
+
+ case *Chan:
+ // Two channel types are identical if they have identical value types
+ // and the same direction.
+ if y, ok := y.(*Chan); ok {
+ return x.Dir == y.Dir && IsIdentical(x.Elt, y.Elt)
+ }
+
+ case *NamedType:
+ // Two named types are identical if their type names originate
+ // in the same type declaration.
+ if y, ok := y.(*NamedType); ok {
+ return x.Obj == y.Obj
+ }
+ }
+
+ return false
+}
+
+// identicalTypes returns true if both lists a and b have the
+// same length and corresponding objects have identical types.
+func identicalTypes(a, b []*Var) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i, x := range a {
+ y := b[i]
+ if !IsIdentical(x.Type, y.Type) {
+ return false
+ }
+ }
+ return true
+}
+
+// identicalMethods returns true if both lists a and b have the
+// same length and corresponding methods have identical types.
+// TODO(gri) make this more efficient
+func identicalMethods(a, b []*Method) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ m := make(map[QualifiedName]*Method)
+ for _, x := range a {
+ assert(m[x.QualifiedName] == nil) // method list must not have duplicate entries
+ m[x.QualifiedName] = x
+ }
+ for _, y := range b {
+ if x := m[y.QualifiedName]; x == nil || !IsIdentical(x.Type, y.Type) {
+ return false
+ }
+ }
+ return true
+}
+
+// underlying returns the underlying type of typ.
+func underlying(typ Type) Type {
+ // Basic types are representing themselves directly even though they are named.
+ if typ, ok := typ.(*NamedType); ok {
+ return typ.Underlying // underlying types are never NamedTypes
+ }
+ return typ
+}
+
+// deref returns a pointer's base type; otherwise it returns typ.
+func deref(typ Type) Type {
+ if typ, ok := underlying(typ).(*Pointer); ok {
+ return typ.Base
+ }
+ return typ
+}
+
+// defaultType returns the default "typed" type for an "untyped" type;
+// it returns the incoming type for all other types. If there is no
+// corresponding untyped type, the result is Typ[Invalid].
+//
+func defaultType(typ Type) Type {
+ if t, ok := typ.(*Basic); ok {
+ k := Invalid
+ switch t.Kind {
+ // case UntypedNil:
+ // There is no default type for nil. For a good error message,
+ // catch this case before calling this function.
+ case UntypedBool:
+ k = Bool
+ case UntypedInt:
+ k = Int
+ case UntypedRune:
+ k = Rune
+ case UntypedFloat:
+ k = Float64
+ case UntypedComplex:
+ k = Complex128
+ case UntypedString:
+ k = String
+ }
+ typ = Typ[k]
+ }
+ return typ
+}
+
+// missingMethod returns (nil, false) if typ implements T, otherwise
+// it returns the first missing method required by T and whether it
+// is missing or simply has the wrong type.
+//
+func missingMethod(typ Type, T *Interface) (method *Method, wrongType bool) {
+ // TODO(gri): this needs to correctly compare method names (taking package into account)
+ // TODO(gri): distinguish pointer and non-pointer receivers
+ // an interface type implements T if it has no methods with conflicting signatures
+ // Note: This is stronger than the current spec. Should the spec require this?
+ if ityp, _ := underlying(typ).(*Interface); ityp != nil {
+ for _, m := range T.Methods {
+ res := lookupField(ityp, m.QualifiedName) // TODO(gri) no need to go via lookupField
+ if res.mode != invalid && !IsIdentical(res.typ, m.Type) {
+ return m, true
+ }
+ }
+ return
+ }
+
+ // a concrete type implements T if it implements all methods of T.
+ for _, m := range T.Methods {
+ res := lookupField(typ, m.QualifiedName)
+ if res.mode == invalid {
+ return m, false
+ }
+ if !IsIdentical(res.typ, m.Type) {
+ return m, true
+ }
+ }
+ return
+}
diff --git a/src/pkg/go/types/resolve.go b/src/pkg/go/types/resolve.go
new file mode 100644
index 000000000..43db60708
--- /dev/null
+++ b/src/pkg/go/types/resolve.go
@@ -0,0 +1,197 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "strconv"
+)
+
+func (check *checker) declareObj(scope, altScope *Scope, obj Object, dotImport token.Pos) {
+ alt := scope.Insert(obj)
+ if alt == nil && altScope != nil {
+ // see if there is a conflicting declaration in altScope
+ alt = altScope.Lookup(obj.GetName())
+ }
+ if alt != nil {
+ prevDecl := ""
+
+ // for dot-imports, local declarations are declared first - swap messages
+ if dotImport.IsValid() {
+ if pos := alt.GetPos(); pos.IsValid() {
+ check.errorf(pos, fmt.Sprintf("%s redeclared in this block by dot-import at %s",
+ obj.GetName(), check.fset.Position(dotImport)))
+ return
+ }
+
+ // get by w/o other position
+ check.errorf(dotImport, fmt.Sprintf("dot-import redeclares %s", obj.GetName()))
+ return
+ }
+
+ if pos := alt.GetPos(); pos.IsValid() {
+ prevDecl = fmt.Sprintf("\n\tother declaration at %s", check.fset.Position(pos))
+ }
+ check.errorf(obj.GetPos(), fmt.Sprintf("%s redeclared in this block%s", obj.GetName(), prevDecl))
+ }
+}
+
+func (check *checker) resolveIdent(scope *Scope, ident *ast.Ident) bool {
+ for ; scope != nil; scope = scope.Outer {
+ if obj := scope.Lookup(ident.Name); obj != nil {
+ check.register(ident, obj)
+ return true
+ }
+ }
+ return false
+}
+
+func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
+ pkg := &Package{Scope: &Scope{Outer: Universe}, Imports: make(map[string]*Package)}
+ check.pkg = pkg
+
+ // complete package scope
+ i := 0
+ for _, file := range check.files {
+ // package names must match
+ switch name := file.Name.Name; {
+ case pkg.Name == "":
+ pkg.Name = name
+ case name != pkg.Name:
+ check.errorf(file.Package, "package %s; expected %s", name, pkg.Name)
+ continue // ignore this file
+ }
+
+ // keep this file
+ check.files[i] = file
+ i++
+
+ // the package identifier denotes the current package
+ check.register(file.Name, pkg)
+
+ // insert top-level file objects in package scope
+ // (the parser took care of declaration errors)
+ for _, decl := range file.Decls {
+ switch d := decl.(type) {
+ case *ast.BadDecl:
+ // ignore
+ case *ast.GenDecl:
+ if d.Tok == token.CONST {
+ check.assocInitvals(d)
+ }
+ for _, spec := range d.Specs {
+ switch s := spec.(type) {
+ case *ast.ImportSpec:
+ // handled separately below
+ case *ast.ValueSpec:
+ for _, name := range s.Names {
+ if name.Name == "_" {
+ continue
+ }
+ pkg.Scope.Insert(check.lookup(name))
+ }
+ case *ast.TypeSpec:
+ if s.Name.Name == "_" {
+ continue
+ }
+ pkg.Scope.Insert(check.lookup(s.Name))
+ default:
+ check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
+ }
+ }
+ case *ast.FuncDecl:
+ if d.Recv != nil {
+ // collect method
+ methods = append(methods, d)
+ continue
+ }
+ if d.Name.Name == "_" || d.Name.Name == "init" {
+ continue // blank (_) and init functions are inaccessible
+ }
+ pkg.Scope.Insert(check.lookup(d.Name))
+ default:
+ check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
+ }
+ }
+ }
+ check.files = check.files[0:i]
+
+ // complete file scopes with imports and resolve identifiers
+ for _, file := range check.files {
+ // build file scope by processing all imports
+ importErrors := false
+ fileScope := &Scope{Outer: pkg.Scope}
+ for _, spec := range file.Imports {
+ if importer == nil {
+ importErrors = true
+ continue
+ }
+ path, _ := strconv.Unquote(spec.Path.Value)
+ imp, err := importer(pkg.Imports, path)
+ if err != nil {
+ check.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
+ importErrors = true
+ continue
+ }
+ // TODO(gri) If a local package name != "." is provided,
+ // global identifier resolution could proceed even if the
+ // import failed. Consider adjusting the logic here a bit.
+
+ // local name overrides imported package name
+ name := imp.Name
+ if spec.Name != nil {
+ name = spec.Name.Name
+ }
+
+ // add import to file scope
+ if name == "." {
+ // merge imported scope with file scope
+ for _, obj := range imp.Scope.Entries {
+ // gcimported package scopes contain non-exported
+ // objects such as types used in partially exported
+ // objects - do not accept them
+ if ast.IsExported(obj.GetName()) {
+ check.declareObj(fileScope, pkg.Scope, obj, spec.Pos())
+ }
+ }
+ // TODO(gri) consider registering the "." identifier
+ // if we have Context.Ident callbacks for say blank
+ // (_) identifiers
+ // check.register(spec.Name, pkg)
+ } else if name != "_" {
+ // declare imported package object in file scope
+ // (do not re-use imp in the file scope but create
+ // a new object instead; the Decl field is different
+ // for different files)
+ obj := &Package{Name: name, Scope: imp.Scope, spec: spec}
+ check.declareObj(fileScope, pkg.Scope, obj, token.NoPos)
+ }
+ }
+
+ // resolve identifiers
+ if importErrors {
+ // don't use the universe scope without correct imports
+ // (objects in the universe may be shadowed by imports;
+ // with missing imports, identifiers might get resolved
+ // incorrectly to universe objects)
+ pkg.Scope.Outer = nil
+ }
+ i := 0
+ for _, ident := range file.Unresolved {
+ if !check.resolveIdent(fileScope, ident) {
+ check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
+ file.Unresolved[i] = ident
+ i++
+ }
+
+ }
+ file.Unresolved = file.Unresolved[0:i]
+ pkg.Scope.Outer = Universe // reset outer scope (is nil if there were importErrors)
+ }
+
+ return
+}
diff --git a/src/pkg/go/types/resolver_test.go b/src/pkg/go/types/resolver_test.go
new file mode 100644
index 000000000..d4e364451
--- /dev/null
+++ b/src/pkg/go/types/resolver_test.go
@@ -0,0 +1,167 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "testing"
+)
+
+var sources = []string{
+ `
+ package p
+ import "fmt"
+ import "math"
+ const pi = math.Pi
+ func sin(x float64) float64 {
+ return math.Sin(x)
+ }
+ var Println = fmt.Println
+ `,
+ `
+ package p
+ import "fmt"
+ func f() string {
+ _ = "foo"
+ return fmt.Sprintf("%d", g())
+ }
+ func g() (x int) { return }
+ `,
+ `
+ package p
+ import . "go/parser"
+ import "sync"
+ func g() Mode { return ImportsOnly }
+ var _, x int = 1, 2
+ func init() {}
+ type T struct{ sync.Mutex; a, b, c int}
+ type I interface{ m() }
+ var _ = T{a: 1, b: 2, c: 3}
+ func (_ T) m() {}
+ `,
+}
+
+var pkgnames = []string{
+ "fmt",
+ "math",
+}
+
+func TestResolveQualifiedIdents(t *testing.T) {
+ // parse package files
+ fset := token.NewFileSet()
+ var files []*ast.File
+ for _, src := range sources {
+ f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors)
+ if err != nil {
+ t.Fatal(err)
+ }
+ files = append(files, f)
+ }
+
+ // resolve and type-check package AST
+ idents := make(map[*ast.Ident]Object)
+ var ctxt Context
+ ctxt.Ident = func(id *ast.Ident, obj Object) { idents[id] = obj }
+ pkg, err := ctxt.Check(fset, files)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // check that all packages were imported
+ for _, name := range pkgnames {
+ if pkg.Imports[name] == nil {
+ t.Errorf("package %s not imported", name)
+ }
+ }
+
+ // check that there are no top-level unresolved identifiers
+ for _, f := range files {
+ for _, x := range f.Unresolved {
+ t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name)
+ }
+ }
+
+ // check that qualified identifiers are resolved
+ for _, f := range files {
+ ast.Inspect(f, func(n ast.Node) bool {
+ if s, ok := n.(*ast.SelectorExpr); ok {
+ if x, ok := s.X.(*ast.Ident); ok {
+ obj := idents[x]
+ if obj == nil {
+ t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name)
+ return false
+ }
+ if _, ok := obj.(*Package); ok && idents[s.Sel] == nil {
+ t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name)
+ return false
+ }
+ return false
+ }
+ return false
+ }
+ return true
+ })
+ }
+
+ // Currently, the Check API doesn't call Ident for fields, methods, and composite literal keys.
+ // Introduce them artifically so that we can run the check below.
+ for _, f := range files {
+ ast.Inspect(f, func(n ast.Node) bool {
+ switch x := n.(type) {
+ case *ast.StructType:
+ for _, list := range x.Fields.List {
+ for _, f := range list.Names {
+ assert(idents[f] == nil)
+ idents[f] = &Var{Pkg: pkg, Name: f.Name}
+ }
+ }
+ case *ast.InterfaceType:
+ for _, list := range x.Methods.List {
+ for _, f := range list.Names {
+ assert(idents[f] == nil)
+ idents[f] = &Func{Pkg: pkg, Name: f.Name}
+ }
+ }
+ case *ast.CompositeLit:
+ for _, e := range x.Elts {
+ if kv, ok := e.(*ast.KeyValueExpr); ok {
+ if k, ok := kv.Key.(*ast.Ident); ok {
+ assert(idents[k] == nil)
+ idents[k] = &Var{Pkg: pkg, Name: k.Name}
+ }
+ }
+ }
+ }
+ return true
+ })
+ }
+
+ // check that each identifier in the source is enumerated by the Context.Ident callback
+ for _, f := range files {
+ ast.Inspect(f, func(n ast.Node) bool {
+ if x, ok := n.(*ast.Ident); ok && x.Name != "_" && x.Name != "." {
+ obj := idents[x]
+ if obj == nil {
+ t.Errorf("%s: unresolved identifier %s", fset.Position(x.Pos()), x.Name)
+ } else {
+ delete(idents, x)
+ }
+ return false
+ }
+ return true
+ })
+ }
+
+ // TODO(gri) enable code below
+ // At the moment, the type checker introduces artifical identifiers which are not
+ // present in the source. Once it doesn't do that anymore, enable the checks below.
+ /*
+ for x := range idents {
+ t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name)
+ }
+ */
+}
diff --git a/src/pkg/go/types/scope.go b/src/pkg/go/types/scope.go
new file mode 100644
index 000000000..463ee40c5
--- /dev/null
+++ b/src/pkg/go/types/scope.go
@@ -0,0 +1,78 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+)
+
+// A Scope maintains the set of named language entities declared
+// in the scope and a link to the immediately surrounding (outer)
+// scope.
+//
+type Scope struct {
+ Outer *Scope
+ Entries []Object // scope entries in insertion order
+ large map[string]Object // for fast lookup - only used for larger scopes
+}
+
+// Lookup returns the object with the given name if it is
+// found in scope s, otherwise it returns nil. Outer scopes
+// are ignored.
+//
+func (s *Scope) Lookup(name string) Object {
+ if s.large != nil {
+ return s.large[name]
+ }
+ for _, obj := range s.Entries {
+ if obj.GetName() == name {
+ return obj
+ }
+ }
+ return nil
+}
+
+// Insert attempts to insert an object obj into scope s.
+// If s already contains an object with the same name,
+// Insert leaves s unchanged and returns that object.
+// Otherwise it inserts obj and returns nil.
+//
+func (s *Scope) Insert(obj Object) Object {
+ name := obj.GetName()
+ if alt := s.Lookup(name); alt != nil {
+ return alt
+ }
+ s.Entries = append(s.Entries, obj)
+
+ // If the scope size reaches a threshold, use a map for faster lookups.
+ const threshold = 20
+ if len(s.Entries) > threshold {
+ if s.large == nil {
+ m := make(map[string]Object, len(s.Entries))
+ for _, obj := range s.Entries {
+ m[obj.GetName()] = obj
+ }
+ s.large = m
+ }
+ s.large[name] = obj
+ }
+
+ return nil
+}
+
+// Debugging support
+func (s *Scope) String() string {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "scope %p {", s)
+ if s != nil && len(s.Entries) > 0 {
+ fmt.Fprintln(&buf)
+ for _, obj := range s.Entries {
+ fmt.Fprintf(&buf, "\t%s\t%T\n", obj.GetName(), obj)
+ }
+ }
+ fmt.Fprintf(&buf, "}\n")
+ return buf.String()
+}
diff --git a/src/pkg/go/types/sizes.go b/src/pkg/go/types/sizes.go
new file mode 100644
index 000000000..ef6499ba4
--- /dev/null
+++ b/src/pkg/go/types/sizes.go
@@ -0,0 +1,162 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements support for (unsafe) Alignof, Offsetof, and Sizeof.
+
+package types
+
+func (ctxt *Context) alignof(typ Type) int64 {
+ if f := ctxt.Alignof; f != nil {
+ if a := f(typ); a >= 1 {
+ return a
+ }
+ panic("Context.Alignof returned an alignment < 1")
+ }
+ return DefaultAlignof(typ)
+}
+
+func (ctxt *Context) offsetsof(s *Struct) []int64 {
+ offsets := s.offsets
+ if offsets == nil {
+ // compute offsets on demand
+ if f := ctxt.Offsetsof; f != nil {
+ offsets = f(s.Fields)
+ // sanity checks
+ if len(offsets) != len(s.Fields) {
+ panic("Context.Offsetsof returned the wrong number of offsets")
+ }
+ for _, o := range offsets {
+ if o < 0 {
+ panic("Context.Offsetsof returned an offset < 0")
+ }
+ }
+ } else {
+ offsets = DefaultOffsetsof(s.Fields)
+ }
+ s.offsets = offsets
+ }
+ return offsets
+}
+
+// offsetof returns the offset of the field specified via
+// the index sequence relative to typ. It returns a value
+// < 0 if the field is in an embedded pointer type.
+func (ctxt *Context) offsetof(typ Type, index []int) int64 {
+ var o int64
+ for _, i := range index {
+ s, _ := underlying(typ).(*Struct)
+ if s == nil {
+ return -1
+ }
+ o += ctxt.offsetsof(s)[i]
+ typ = s.Fields[i].Type
+ }
+ return o
+}
+
+func (ctxt *Context) sizeof(typ Type) int64 {
+ if f := ctxt.Sizeof; f != nil {
+ if s := f(typ); s >= 0 {
+ return s
+ }
+ panic("Context.Sizeof returned a size < 0")
+ }
+ return DefaultSizeof(typ)
+}
+
+// DefaultMaxAlign is the default maximum alignment, in bytes,
+// used by DefaultAlignof.
+const DefaultMaxAlign = 8
+
+// DefaultAlignof implements the default alignment computation
+// for unsafe.Alignof. It is used if Context.Alignof == nil.
+func DefaultAlignof(typ Type) int64 {
+ // For arrays and structs, alignment is defined in terms
+ // of alignment of the elements and fields, respectively.
+ switch t := underlying(typ).(type) {
+ case *Array:
+ // spec: "For a variable x of array type: unsafe.Alignof(x)
+ // is the same as unsafe.Alignof(x[0]), but at least 1."
+ return DefaultAlignof(t.Elt)
+ case *Struct:
+ // spec: "For a variable x of struct type: unsafe.Alignof(x)
+ // is the largest of the values unsafe.Alignof(x.f) for each
+ // field f of x, but at least 1."
+ max := int64(1)
+ for _, f := range t.Fields {
+ if a := DefaultAlignof(f.Type); a > max {
+ max = a
+ }
+ }
+ return max
+ }
+ a := DefaultSizeof(typ) // may be 0
+ // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
+ if a < 1 {
+ return 1
+ }
+ if a > DefaultMaxAlign {
+ return DefaultMaxAlign
+ }
+ return a
+}
+
+// align returns the smallest y >= x such that y % a == 0.
+func align(x, a int64) int64 {
+ y := x + a - 1
+ return y - y%a
+}
+
+// DefaultOffsetsof implements the default field offset computation
+// for unsafe.Offsetof. It is used if Context.Offsetsof == nil.
+func DefaultOffsetsof(fields []*Field) []int64 {
+ offsets := make([]int64, len(fields))
+ var o int64
+ for i, f := range fields {
+ a := DefaultAlignof(f.Type)
+ o = align(o, a)
+ offsets[i] = o
+ o += DefaultSizeof(f.Type)
+ }
+ return offsets
+}
+
+// DefaultPtrSize is the default size of ints, uint, and pointers, in bytes,
+// used by DefaultSizeof.
+const DefaultPtrSize = 8
+
+// DefaultSizeof implements the default size computation
+// for unsafe.Sizeof. It is used if Context.Sizeof == nil.
+func DefaultSizeof(typ Type) int64 {
+ switch t := underlying(typ).(type) {
+ case *Basic:
+ if s := t.size; s > 0 {
+ return s
+ }
+ if t.Kind == String {
+ return DefaultPtrSize * 2
+ }
+ case *Array:
+ a := DefaultAlignof(t.Elt)
+ s := DefaultSizeof(t.Elt)
+ return align(s, a) * t.Len // may be 0
+ case *Slice:
+ return DefaultPtrSize * 3
+ case *Struct:
+ n := len(t.Fields)
+ if n == 0 {
+ return 0
+ }
+ offsets := t.offsets
+ if t.offsets == nil {
+ // compute offsets on demand
+ offsets = DefaultOffsetsof(t.Fields)
+ t.offsets = offsets
+ }
+ return offsets[n-1] + DefaultSizeof(t.Fields[n-1].Type)
+ case *Signature:
+ return DefaultPtrSize * 2
+ }
+ return DefaultPtrSize // catch-all
+}
diff --git a/src/pkg/go/types/stmt.go b/src/pkg/go/types/stmt.go
new file mode 100644
index 000000000..53c46a167
--- /dev/null
+++ b/src/pkg/go/types/stmt.go
@@ -0,0 +1,743 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements typechecking of statements.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// assigment reports whether x can be assigned to a variable of type 'to',
+// if necessary by attempting to convert untyped values to the appropriate
+// type. If x.mode == invalid upon return, then assignment has already
+// issued an error message and the caller doesn't have to report another.
+// TODO(gri) This latter behavior is for historic reasons and complicates
+// callers. Needs to be cleaned up.
+func (check *checker) assignment(x *operand, to Type) bool {
+ if x.mode == invalid {
+ return false
+ }
+
+ if t, ok := x.typ.(*Result); ok {
+ // TODO(gri) elsewhere we use "assignment count mismatch" (consolidate)
+ check.errorf(x.pos(), "%d-valued expression %s used as single value", len(t.Values), x)
+ x.mode = invalid
+ return false
+ }
+
+ check.convertUntyped(x, to)
+
+ return x.mode != invalid && x.isAssignable(check.ctxt, to)
+}
+
+// assign1to1 typechecks a single assignment of the form lhs = rhs (if rhs != nil),
+// or lhs = x (if rhs == nil). If decl is set, the lhs operand must be an identifier.
+// If its type is not set, it is deduced from the type or value of x. If lhs has a
+// type it is used as a hint when evaluating rhs, if present.
+//
+func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota int) {
+ ident, _ := lhs.(*ast.Ident)
+ if x == nil {
+ assert(rhs != nil)
+ x = new(operand)
+ }
+
+ if ident != nil && ident.Name == "_" {
+ // anything can be assigned to a blank identifier - check rhs only, if present
+ if rhs != nil {
+ check.expr(x, rhs, nil, iota)
+ }
+ return
+ }
+
+ if !decl {
+ // regular assignment - start with lhs to obtain a type hint
+ // TODO(gri) clean this up - we don't need type hints anymore
+ var z operand
+ check.expr(&z, lhs, nil, -1)
+ if z.mode == invalid {
+ z.typ = nil // so we can proceed with rhs
+ }
+
+ if rhs != nil {
+ check.expr(x, rhs, z.typ, -1)
+ if x.mode == invalid {
+ return
+ }
+ }
+
+ if x.mode == invalid || z.mode == invalid {
+ return
+ }
+
+ if !check.assignment(x, z.typ) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
+ }
+ return
+ }
+ if z.mode == constant {
+ check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
+ }
+ return
+ }
+
+ // declaration - lhs must be an identifier
+ if ident == nil {
+ check.errorf(lhs.Pos(), "cannot declare %s", lhs)
+ return
+ }
+
+ // lhs may or may not be typed yet
+ obj := check.lookup(ident)
+ var typ Type
+ if t := obj.GetType(); t != nil {
+ typ = t
+ }
+
+ if rhs != nil {
+ check.expr(x, rhs, typ, iota)
+ // continue even if x.mode == invalid
+ }
+
+ if typ == nil {
+ // determine lhs type from rhs expression;
+ // for variables, convert untyped types to
+ // default types
+ typ = Typ[Invalid]
+ if x.mode != invalid {
+ typ = x.typ
+ if _, ok := obj.(*Var); ok && isUntyped(typ) {
+ if x.isNil() {
+ check.errorf(x.pos(), "use of untyped nil")
+ x.mode = invalid
+ } else {
+ typ = defaultType(typ)
+ }
+ }
+ }
+ switch obj := obj.(type) {
+ case *Const:
+ obj.Type = typ
+ case *Var:
+ obj.Type = typ
+ default:
+ unreachable()
+ }
+ }
+
+ if x.mode != invalid {
+ if !check.assignment(x, typ) {
+ if x.mode != invalid {
+ switch obj.(type) {
+ case *Const:
+ check.errorf(x.pos(), "cannot assign %s to variable of type %s", x, typ)
+ case *Var:
+ check.errorf(x.pos(), "cannot initialize constant of type %s with %s", typ, x)
+ default:
+ unreachable()
+ }
+ x.mode = invalid
+ }
+ }
+ }
+
+ // for constants, set their value
+ if obj, ok := obj.(*Const); ok {
+ assert(obj.Val == nil)
+ if x.mode != invalid {
+ if x.mode == constant {
+ if isConstType(x.typ) {
+ obj.Val = x.val
+ } else {
+ check.errorf(x.pos(), "%s has invalid constant type", x)
+ }
+ } else {
+ check.errorf(x.pos(), "%s is not constant", x)
+ }
+ }
+ if obj.Val == nil {
+ // set the constant to its type's zero value to reduce spurious errors
+ switch typ := underlying(obj.Type); {
+ case typ == Typ[Invalid]:
+ // ignore
+ case isBoolean(typ):
+ obj.Val = false
+ case isNumeric(typ):
+ obj.Val = int64(0)
+ case isString(typ):
+ obj.Val = ""
+ case hasNil(typ):
+ obj.Val = nilConst
+ default:
+ // in all other cases just prevent use of the constant
+ // TODO(gri) re-evaluate this code
+ obj.Val = nilConst
+ }
+ }
+ }
+}
+
+// assignNtoM typechecks a general assignment. If decl is set, the lhs operands
+// must be identifiers. If their types are not set, they are deduced from the
+// types of the corresponding rhs expressions. iota >= 0 indicates that the
+// "assignment" is part of a constant/variable declaration.
+// Precondition: len(lhs) > 0 .
+//
+func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) {
+ assert(len(lhs) > 0)
+
+ if len(lhs) == len(rhs) {
+ for i, e := range rhs {
+ check.assign1to1(lhs[i], e, nil, decl, iota)
+ }
+ return
+ }
+
+ if len(rhs) == 1 {
+ // len(lhs) > 1, therefore a correct rhs expression
+ // cannot be a shift and we don't need a type hint;
+ // ok to evaluate rhs first
+ var x operand
+ check.expr(&x, rhs[0], nil, iota)
+ if x.mode == invalid {
+ // If decl is set, this leaves the lhs identifiers
+ // untyped. We catch this when looking up the respective
+ // object.
+ return
+ }
+
+ if t, ok := x.typ.(*Result); ok && len(lhs) == len(t.Values) {
+ // function result
+ x.mode = value
+ for i, obj := range t.Values {
+ x.expr = nil // TODO(gri) should do better here
+ x.typ = obj.Type
+ check.assign1to1(lhs[i], nil, &x, decl, iota)
+ }
+ return
+ }
+
+ if x.mode == valueok && len(lhs) == 2 {
+ // comma-ok expression
+ x.mode = value
+ check.assign1to1(lhs[0], nil, &x, decl, iota)
+
+ x.mode = value
+ x.typ = Typ[UntypedBool]
+ check.assign1to1(lhs[1], nil, &x, decl, iota)
+ return
+ }
+ }
+
+ check.errorf(lhs[0].Pos(), "assignment count mismatch: %d = %d", len(lhs), len(rhs))
+
+ // avoid checking the same declaration over and over
+ // again for each lhs identifier that has no type yet
+ if iota >= 0 {
+ // declaration
+ for _, e := range lhs {
+ if name, ok := e.(*ast.Ident); ok {
+ switch obj := check.lookup(name).(type) {
+ case *Const:
+ obj.Type = Typ[Invalid]
+ case *Var:
+ obj.Type = Typ[Invalid]
+ default:
+ unreachable()
+ }
+ }
+ }
+ }
+}
+
+func (check *checker) optionalStmt(s ast.Stmt) {
+ if s != nil {
+ check.stmt(s)
+ }
+}
+
+func (check *checker) stmtList(list []ast.Stmt) {
+ for _, s := range list {
+ check.stmt(s)
+ }
+}
+
+func (check *checker) call(call *ast.CallExpr) {
+ var x operand
+ check.rawExpr(&x, call, nil, -1, false) // don't check if value is used
+ // TODO(gri) If a builtin is called, the builtin must be valid in statement context.
+}
+
+func (check *checker) multipleDefaults(list []ast.Stmt) {
+ var first ast.Stmt
+ for _, s := range list {
+ var d ast.Stmt
+ switch c := s.(type) {
+ case *ast.CaseClause:
+ if len(c.List) == 0 {
+ d = s
+ }
+ case *ast.CommClause:
+ if c.Comm == nil {
+ d = s
+ }
+ default:
+ check.invalidAST(s.Pos(), "case/communication clause expected")
+ }
+ if d != nil {
+ if first != nil {
+ check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos())
+ } else {
+ first = d
+ }
+ }
+ }
+}
+
+// stmt typechecks statement s.
+func (check *checker) stmt(s ast.Stmt) {
+ switch s := s.(type) {
+ case *ast.BadStmt, *ast.EmptyStmt:
+ // ignore
+
+ case *ast.DeclStmt:
+ d, _ := s.Decl.(*ast.GenDecl)
+ if d == nil || (d.Tok != token.CONST && d.Tok != token.TYPE && d.Tok != token.VAR) {
+ check.invalidAST(token.NoPos, "const, type, or var declaration expected")
+ return
+ }
+ if d.Tok == token.CONST {
+ check.assocInitvals(d)
+ }
+ check.decl(d)
+
+ case *ast.LabeledStmt:
+ // TODO(gri) anything to do with label itself?
+ check.stmt(s.Stmt)
+
+ case *ast.ExprStmt:
+ var x operand
+ used := false
+ switch e := unparen(s.X).(type) {
+ case *ast.CallExpr:
+ // function calls are permitted
+ used = true
+ // but some builtins are excluded
+ // (Caution: This evaluates e.Fun twice, once here and once
+ // below as part of s.X. This has consequences for
+ // check.register. Perhaps this can be avoided.)
+ check.expr(&x, e.Fun, nil, -1)
+ if x.mode != invalid {
+ if b, ok := x.typ.(*builtin); ok && !b.isStatement {
+ used = false
+ }
+ }
+ case *ast.UnaryExpr:
+ // receive operations are permitted
+ if e.Op == token.ARROW {
+ used = true
+ }
+ }
+ if !used {
+ check.errorf(s.Pos(), "%s not used", s.X)
+ // ok to continue
+ }
+ check.rawExpr(&x, s.X, nil, -1, false)
+ if x.mode == typexpr {
+ check.errorf(x.pos(), "%s is not an expression", &x)
+ }
+
+ case *ast.SendStmt:
+ var ch, x operand
+ check.expr(&ch, s.Chan, nil, -1)
+ check.expr(&x, s.Value, nil, -1)
+ if ch.mode == invalid || x.mode == invalid {
+ return
+ }
+ if tch, ok := underlying(ch.typ).(*Chan); !ok || tch.Dir&ast.SEND == 0 || !check.assignment(&x, tch.Elt) {
+ if x.mode != invalid {
+ check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch)
+ }
+ }
+
+ case *ast.IncDecStmt:
+ var op token.Token
+ switch s.Tok {
+ case token.INC:
+ op = token.ADD
+ case token.DEC:
+ op = token.SUB
+ default:
+ check.invalidAST(s.TokPos, "unknown inc/dec operation %s", s.Tok)
+ return
+ }
+ var x operand
+ Y := &ast.BasicLit{ValuePos: s.X.Pos(), Kind: token.INT, Value: "1"} // use x's position
+ check.binary(&x, s.X, Y, op, -1)
+ if x.mode == invalid {
+ return
+ }
+ check.assign1to1(s.X, nil, &x, false, -1)
+
+ case *ast.AssignStmt:
+ switch s.Tok {
+ case token.ASSIGN, token.DEFINE:
+ if len(s.Lhs) == 0 {
+ check.invalidAST(s.Pos(), "missing lhs in assignment")
+ return
+ }
+ check.assignNtoM(s.Lhs, s.Rhs, s.Tok == token.DEFINE, -1)
+ default:
+ // assignment operations
+ if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
+ check.errorf(s.TokPos, "assignment operation %s requires single-valued expressions", s.Tok)
+ return
+ }
+ // TODO(gri) make this conversion more efficient
+ var op token.Token
+ switch s.Tok {
+ case token.ADD_ASSIGN:
+ op = token.ADD
+ case token.SUB_ASSIGN:
+ op = token.SUB
+ case token.MUL_ASSIGN:
+ op = token.MUL
+ case token.QUO_ASSIGN:
+ op = token.QUO
+ case token.REM_ASSIGN:
+ op = token.REM
+ case token.AND_ASSIGN:
+ op = token.AND
+ case token.OR_ASSIGN:
+ op = token.OR
+ case token.XOR_ASSIGN:
+ op = token.XOR
+ case token.SHL_ASSIGN:
+ op = token.SHL
+ case token.SHR_ASSIGN:
+ op = token.SHR
+ case token.AND_NOT_ASSIGN:
+ op = token.AND_NOT
+ default:
+ check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok)
+ return
+ }
+ var x operand
+ check.binary(&x, s.Lhs[0], s.Rhs[0], op, -1)
+ if x.mode == invalid {
+ return
+ }
+ check.assign1to1(s.Lhs[0], nil, &x, false, -1)
+ }
+
+ case *ast.GoStmt:
+ check.call(s.Call)
+
+ case *ast.DeferStmt:
+ check.call(s.Call)
+
+ case *ast.ReturnStmt:
+ sig := check.funcsig
+ if n := len(sig.Results); n > 0 {
+ // TODO(gri) should not have to compute lhs, named every single time - clean this up
+ lhs := make([]ast.Expr, n)
+ named := false // if set, function has named results
+ for i, res := range sig.Results {
+ if len(res.Name) > 0 {
+ // a blank (_) result parameter is a named result
+ named = true
+ }
+ name := ast.NewIdent(res.Name)
+ name.NamePos = s.Pos()
+ check.register(name, &Var{Name: res.Name, Type: res.Type}) // Pkg == nil
+ lhs[i] = name
+ }
+ if len(s.Results) > 0 || !named {
+ // TODO(gri) assignNtoM should perhaps not require len(lhs) > 0
+ check.assignNtoM(lhs, s.Results, false, -1)
+ }
+ } else if len(s.Results) > 0 {
+ check.errorf(s.Pos(), "no result values expected")
+ }
+
+ case *ast.BranchStmt:
+ // TODO(gri) implement this
+
+ case *ast.BlockStmt:
+ check.stmtList(s.List)
+
+ case *ast.IfStmt:
+ check.optionalStmt(s.Init)
+ var x operand
+ check.expr(&x, s.Cond, nil, -1)
+ if x.mode != invalid && !isBoolean(x.typ) {
+ check.errorf(s.Cond.Pos(), "non-boolean condition in if statement")
+ }
+ check.stmt(s.Body)
+ check.optionalStmt(s.Else)
+
+ case *ast.SwitchStmt:
+ check.optionalStmt(s.Init)
+ var x operand
+ tag := s.Tag
+ if tag == nil {
+ // use fake true tag value and position it at the opening { of the switch
+ ident := &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
+ check.register(ident, Universe.Lookup("true"))
+ tag = ident
+ }
+ check.expr(&x, tag, nil, -1)
+
+ check.multipleDefaults(s.Body.List)
+ seen := make(map[interface{}]token.Pos)
+ for _, s := range s.Body.List {
+ clause, _ := s.(*ast.CaseClause)
+ if clause == nil {
+ continue // error reported before
+ }
+ if x.mode != invalid {
+ for _, expr := range clause.List {
+ x := x // copy of x (don't modify original)
+ var y operand
+ check.expr(&y, expr, nil, -1)
+ if y.mode == invalid {
+ continue // error reported before
+ }
+ // If we have a constant case value, it must appear only
+ // once in the switch statement. Determine if there is a
+ // duplicate entry, but only report an error if there are
+ // no other errors.
+ var dupl token.Pos
+ var yy operand
+ if y.mode == constant {
+ // TODO(gri) This code doesn't work correctly for
+ // large integer, floating point, or
+ // complex values - the respective struct
+ // comparisons are shallow. Need to use a
+ // hash function to index the map.
+ dupl = seen[y.val]
+ seen[y.val] = y.pos()
+ yy = y // remember y
+ }
+ // TODO(gri) The convertUntyped call pair below appears in other places. Factor!
+ // Order matters: By comparing y against x, error positions are at the case values.
+ check.convertUntyped(&y, x.typ)
+ if y.mode == invalid {
+ continue // error reported before
+ }
+ check.convertUntyped(&x, y.typ)
+ if x.mode == invalid {
+ continue // error reported before
+ }
+ check.comparison(&y, &x, token.EQL)
+ if y.mode != invalid && dupl.IsValid() {
+ check.errorf(yy.pos(), "%s is duplicate case (previous at %s)",
+ &yy, check.fset.Position(dupl))
+ }
+ }
+ }
+ check.stmtList(clause.Body)
+ }
+
+ case *ast.TypeSwitchStmt:
+ check.optionalStmt(s.Init)
+
+ // A type switch guard must be of the form:
+ //
+ // TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
+ //
+ // The parser is checking syntactic correctness;
+ // remaining syntactic errors are considered AST errors here.
+ // TODO(gri) better factoring of error handling (invalid ASTs)
+ //
+ var lhs *Var // lhs variable or nil
+ var rhs ast.Expr
+ switch guard := s.Assign.(type) {
+ case *ast.ExprStmt:
+ rhs = guard.X
+ case *ast.AssignStmt:
+ if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 {
+ check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ return
+ }
+ ident, _ := guard.Lhs[0].(*ast.Ident)
+ if ident == nil {
+ check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ return
+ }
+ lhs = check.lookup(ident).(*Var)
+ rhs = guard.Rhs[0]
+ default:
+ check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ return
+ }
+
+ // rhs must be of the form: expr.(type) and expr must be an interface
+ expr, _ := rhs.(*ast.TypeAssertExpr)
+ if expr == nil || expr.Type != nil {
+ check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ return
+ }
+ var x operand
+ check.expr(&x, expr.X, nil, -1)
+ if x.mode == invalid {
+ return
+ }
+ var T *Interface
+ if T, _ = underlying(x.typ).(*Interface); T == nil {
+ check.errorf(x.pos(), "%s is not an interface", &x)
+ return
+ }
+
+ check.multipleDefaults(s.Body.List)
+ for _, s := range s.Body.List {
+ clause, _ := s.(*ast.CaseClause)
+ if clause == nil {
+ continue // error reported before
+ }
+ // Check each type in this type switch case.
+ var typ Type
+ for _, expr := range clause.List {
+ typ = check.typOrNil(expr, false)
+ if typ != nil && typ != Typ[Invalid] {
+ if method, wrongType := missingMethod(typ, T); method != nil {
+ var msg string
+ if wrongType {
+ msg = "%s cannot have dynamic type %s (wrong type for method %s)"
+ } else {
+ msg = "%s cannot have dynamic type %s (missing method %s)"
+ }
+ check.errorf(expr.Pos(), msg, &x, typ, method.Name)
+ // ok to continue
+ }
+ }
+ }
+ // If lhs exists, set its type for each clause.
+ if lhs != nil {
+ // In clauses with a case listing exactly one type, the variable has that type;
+ // otherwise, the variable has the type of the expression in the TypeSwitchGuard.
+ if len(clause.List) != 1 || typ == nil {
+ typ = x.typ
+ }
+ lhs.Type = typ
+ }
+ check.stmtList(clause.Body)
+ }
+
+ // There is only one object (lhs) associated with a lhs identifier, but that object
+ // assumes different types for different clauses. Set it back to the type of the
+ // TypeSwitchGuard expression so that that variable always has a valid type.
+ if lhs != nil {
+ lhs.Type = x.typ
+ }
+
+ case *ast.SelectStmt:
+ check.multipleDefaults(s.Body.List)
+ for _, s := range s.Body.List {
+ clause, _ := s.(*ast.CommClause)
+ if clause == nil {
+ continue // error reported before
+ }
+ check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt)
+ check.stmtList(clause.Body)
+ }
+
+ case *ast.ForStmt:
+ check.optionalStmt(s.Init)
+ if s.Cond != nil {
+ var x operand
+ check.expr(&x, s.Cond, nil, -1)
+ if x.mode != invalid && !isBoolean(x.typ) {
+ check.errorf(s.Cond.Pos(), "non-boolean condition in for statement")
+ }
+ }
+ check.optionalStmt(s.Post)
+ check.stmt(s.Body)
+
+ case *ast.RangeStmt:
+ // check expression to iterate over
+ decl := s.Tok == token.DEFINE
+ var x operand
+ check.expr(&x, s.X, nil, -1)
+ if x.mode == invalid {
+ // if we don't have a declaration, we can still check the loop's body
+ if !decl {
+ check.stmt(s.Body)
+ }
+ return
+ }
+
+ // determine key/value types
+ var key, val Type
+ switch typ := underlying(x.typ).(type) {
+ case *Basic:
+ if isString(typ) {
+ key = Typ[UntypedInt]
+ val = Typ[UntypedRune]
+ }
+ case *Array:
+ key = Typ[UntypedInt]
+ val = typ.Elt
+ case *Slice:
+ key = Typ[UntypedInt]
+ val = typ.Elt
+ case *Pointer:
+ if typ, _ := underlying(typ.Base).(*Array); typ != nil {
+ key = Typ[UntypedInt]
+ val = typ.Elt
+ }
+ case *Map:
+ key = typ.Key
+ val = typ.Elt
+ case *Chan:
+ key = typ.Elt
+ if typ.Dir&ast.RECV == 0 {
+ check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
+ // ok to continue
+ }
+ if s.Value != nil {
+ check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
+ // ok to continue
+ }
+ }
+
+ if key == nil {
+ check.errorf(x.pos(), "cannot range over %s", &x)
+ // if we don't have a declaration, we can still check the loop's body
+ if !decl {
+ check.stmt(s.Body)
+ }
+ return
+ }
+
+ // check assignment to/declaration of iteration variables
+ // TODO(gri) The error messages/positions are not great here,
+ // they refer to the expression in the range clause.
+ // Should give better messages w/o too much code
+ // duplication (assignment checking).
+ x.mode = value
+ if s.Key != nil {
+ x.typ = key
+ x.expr = s.Key
+ check.assign1to1(s.Key, nil, &x, decl, -1)
+ } else {
+ check.invalidAST(s.Pos(), "range clause requires index iteration variable")
+ // ok to continue
+ }
+ if s.Value != nil {
+ x.typ = val
+ x.expr = s.Value
+ check.assign1to1(s.Value, nil, &x, decl, -1)
+ }
+
+ check.stmt(s.Body)
+
+ default:
+ check.errorf(s.Pos(), "invalid statement")
+ }
+}
diff --git a/src/pkg/go/types/testdata/builtins.src b/src/pkg/go/types/testdata/builtins.src
new file mode 100644
index 000000000..c08c442ce
--- /dev/null
+++ b/src/pkg/go/types/testdata/builtins.src
@@ -0,0 +1,401 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// builtin calls
+
+package builtins
+
+import "unsafe"
+
+func _append() {
+ var x int
+ var s []byte
+ _0 := append /* ERROR "argument" */ ()
+ _1 := append("foo" /* ERROR "not a typed slice" */)
+ _2 := append(nil /* ERROR "not a typed slice" */, s)
+ _3 := append(x /* ERROR "not a typed slice" */, s)
+ _4 := append(s)
+ append /* ERROR "not used" */ (s)
+}
+
+func _cap() {
+ var a [10]bool
+ var p *[20]int
+ var s []int
+ var c chan string
+ _0 := cap /* ERROR "argument" */ ()
+ _1 := cap /* ERROR "argument" */ (1, 2)
+ _2 := cap(42 /* ERROR "invalid" */)
+ const _3 = cap(a)
+ assert(_3 == 10)
+ const _4 = cap(p)
+ assert(_4 == 20)
+ _5 := cap(c)
+ cap /* ERROR "not used" */ (c)
+
+ // issue 4744
+ type T struct{ a [10]int }
+ const _ = cap(((*T)(nil)).a)
+}
+
+func _close() {
+ var c chan int
+ var r <-chan int
+ close /* ERROR "argument" */ ()
+ close /* ERROR "argument" */ (1, 2)
+ close(42 /* ERROR "not a channel" */)
+ close(r /* ERROR "receive-only channel" */)
+ close(c)
+}
+
+func _complex() {
+ var i32 int32
+ var f32 float32
+ var f64 float64
+ var c64 complex64
+ _ = complex /* ERROR "argument" */ ()
+ _ = complex /* ERROR "argument" */ (1)
+ _ = complex(true /* ERROR "invalid argument" */ , 0)
+ _ = complex(i32 /* ERROR "invalid argument" */ , 0)
+ _ = complex("foo" /* ERROR "invalid argument" */ , 0)
+ _ = complex(c64 /* ERROR "invalid argument" */ , 0)
+ _ = complex(0, true /* ERROR "invalid argument" */ )
+ _ = complex(0, i32 /* ERROR "invalid argument" */ )
+ _ = complex(0, "foo" /* ERROR "invalid argument" */ )
+ _ = complex(0, c64 /* ERROR "invalid argument" */ )
+ _ = complex(f32, f32)
+ _ = complex(f32, 1)
+ _ = complex(f32, 1.0)
+ _ = complex(f32, 'a')
+ _ = complex(f64, f64)
+ _ = complex(f64, 1)
+ _ = complex(f64, 1.0)
+ _ = complex(f64, 'a')
+ _ = complex(f32 /* ERROR "mismatched types" */, f64)
+ _ = complex(f64 /* ERROR "mismatched types" */, f32)
+ _ = complex(1, 1)
+ _ = complex(1, 1.1)
+ _ = complex(1, 'a')
+ complex /* ERROR "not used" */ (1, 2)
+}
+
+func _copy() {
+ copy /* ERROR "not enough arguments" */ ()
+ copy /* ERROR "not enough arguments" */ ("foo")
+ copy([ /* ERROR "copy expects slice arguments" */ ...]int{}, []int{})
+ copy([ /* ERROR "copy expects slice arguments" */ ]int{}, [...]int{})
+ copy([ /* ERROR "different element types" */ ]int8{}, "foo")
+
+ // spec examples
+ var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
+ var s = make([]int, 6)
+ var b = make([]byte, 5)
+ n1 := copy(s, a[0:]) // n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
+ n2 := copy(s, s[2:]) // n2 == 4, s == []int{2, 3, 4, 5, 4, 5}
+ n3 := copy(b, "Hello, World!") // n3 == 5, b == []byte("Hello")
+}
+
+func _delete() {
+ var m map[string]int
+ var s string
+ delete /* ERROR "argument" */ ()
+ delete /* ERROR "argument" */ (1)
+ delete /* ERROR "argument" */ (1, 2, 3)
+ delete(m, 0 /* ERROR "not assignable" */)
+ delete(m, s)
+}
+
+func _imag() {
+ var f32 float32
+ var f64 float64
+ var c64 complex64
+ var c128 complex128
+ _ = imag /* ERROR "argument" */ ()
+ _ = imag /* ERROR "argument" */ (1, 2)
+ _ = imag(10 /* ERROR "must be a complex number" */)
+ _ = imag(2.7182818 /* ERROR "must be a complex number" */)
+ _ = imag("foo" /* ERROR "must be a complex number" */)
+ const _5 = imag(1 + 2i)
+ assert(_5 == 2)
+ f32 = _5
+ f64 = _5
+ const _6 = imag(0i)
+ assert(_6 == 0)
+ f32 = imag(c64)
+ f64 = imag(c128)
+ f32 = imag /* ERROR "cannot assign" */ (c128)
+ f64 = imag /* ERROR "cannot assign" */ (c64)
+ imag /* ERROR "not used" */ (c64)
+}
+
+func _len() {
+ const c = "foobar"
+ var a [10]bool
+ var p *[20]int
+ var s []int
+ var m map[string]complex128
+ _ = len /* ERROR "argument" */ ()
+ _ = len /* ERROR "argument" */ (1, 2)
+ _ = len(42 /* ERROR "invalid" */)
+ const _3 = len(c)
+ assert(_3 == 6)
+ const _4 = len(a)
+ assert(_4 == 10)
+ const _5 = len(p)
+ assert(_5 == 20)
+ _ = len(m)
+ len /* ERROR "not used" */ (c)
+
+ // esoteric case
+ var t string
+ var hash map[interface{}][]*[10]int
+ const n = len /* ERROR "not constant" */ (hash[recover()][len(t)])
+ assert /* ERROR "failed" */ (n == 10)
+ var ch <-chan int
+ const nn = len /* ERROR "not constant" */ (hash[<-ch][len(t)])
+ _ = nn // TODO(gri) remove this once unused constants get type-checked
+
+ // issue 4744
+ type T struct{ a [10]int }
+ const _ = len(((*T)(nil)).a)
+}
+
+func _make() {
+ n := 0
+
+ _ = make /* ERROR "argument" */ ()
+ _ = make(1 /* ERROR "not a type" */)
+ _ = make(int /* ERROR "cannot make" */)
+
+ // slices
+ _ = make/* ERROR "arguments" */ ([]int)
+ _ = make/* ERROR "arguments" */ ([]int, 2, 3, 4)
+ _ = make([]int, int /* ERROR "not an expression" */)
+ _ = make([]int, 10, float32 /* ERROR "not an expression" */)
+ _ = make([]int, "foo" /* ERROR "must be an integer" */)
+ _ = make([]int, 10, 2.3 /* ERROR "must be an integer" */)
+ _ = make([]int, 5, 10.0)
+ _ = make([]int, 0i)
+ _ = make([]int, - /* ERROR "must not be negative" */ 1, 10)
+ _ = make([]int, 0, - /* ERROR "must not be negative" */ 1)
+ _ = make([]int, - /* ERROR "must not be negative" */ 1, - /* ERROR "must not be negative" */ 1)
+ _ = make([]int, 1<<100, 1<<100) // run-time panic
+ _ = make([]int, 1 /* ERROR "length and capacity swapped" */ <<100 + 1, 1<<100)
+ _ = make([]int, 1 /* ERROR "length and capacity swapped" */ <<100, 12345)
+
+ // maps
+ _ = make /* ERROR "arguments" */ (map[int]string, 10, 20)
+ _ = make(map[int]float32, int /* ERROR "not an expression" */)
+ _ = make(map[int]float32, "foo" /* ERROR "must be an integer" */)
+ _ = make(map[int]float32, 10)
+ _ = make(map[int]float32, n)
+ _ = make(map[int]float32, int64(n))
+
+ // channels
+ _ = make /* ERROR "arguments" */ (chan int, 10, 20)
+ _ = make(chan int, int /* ERROR "not an expression" */)
+ _ = make(chan<- int, "foo" /* ERROR "must be an integer" */)
+ _ = make(<-chan float64, 10)
+ _ = make(chan chan int, n)
+ _ = make(chan string, int64(n))
+
+ make /* ERROR "not used" */ ([]int, 10)
+}
+
+func _new() {
+ _ = new /* ERROR "argument" */ ()
+ _ = new /* ERROR "argument" */ (1, 2)
+ _ = new("foo" /* ERROR "not a type" */)
+ p := new(float64)
+ _ = new(struct{ x, y int })
+ q := new(*float64)
+ _ = *p == **q
+ new /* ERROR "not used" */ (int)
+}
+
+func _panic() {
+ panic /* ERROR "arguments" */ ()
+ panic /* ERROR "arguments" */ (1, 2)
+ panic(0)
+ panic("foo")
+ panic(false)
+}
+
+func _print() {
+ print()
+ print(1)
+ print(1, 2)
+ print("foo")
+ print(2.718281828)
+ print(false)
+}
+
+func _println() {
+ println()
+ println(1)
+ println(1, 2)
+ println("foo")
+ println(2.718281828)
+ println(false)
+}
+
+func _real() {
+ var f32 float32
+ var f64 float64
+ var c64 complex64
+ var c128 complex128
+ _ = real /* ERROR "argument" */ ()
+ _ = real /* ERROR "argument" */ (1, 2)
+ _ = real(10 /* ERROR "must be a complex number" */)
+ _ = real(2.7182818 /* ERROR "must be a complex number" */)
+ _ = real("foo" /* ERROR "must be a complex number" */)
+ const _5 = real(1 + 2i)
+ assert(_5 == 1)
+ f32 = _5
+ f64 = _5
+ const _6 = real(0i)
+ assert(_6 == 0)
+ f32 = real(c64)
+ f64 = real(c128)
+ f32 = real /* ERROR "cannot assign" */ (c128)
+ f64 = real /* ERROR "cannot assign" */ (c64)
+ real /* ERROR "not used" */ (c64)
+}
+
+func _recover() {
+ _ = recover()
+ _ = recover /* ERROR "argument" */ (10)
+ recover()
+}
+
+// assuming types.DefaultPtrSize == 8
+type S0 struct{ // offset
+ a bool // 0
+ b rune // 4
+ c *int // 8
+ d bool // 16
+ e complex128 // 24
+} // 40
+
+type S1 struct{ // offset
+ x float32 // 0
+ y string // 8
+ z *S1 // 24
+ S0 // 32
+} // 72
+
+type S2 struct{ // offset
+ *S1 // 0
+} // 8
+
+func _Alignof() {
+ var x int
+ _ = unsafe /* ERROR "argument" */ .Alignof()
+ _ = unsafe /* ERROR "argument" */ .Alignof(1, 2)
+ _ = unsafe.Alignof(int /* ERROR "not an expression" */)
+ _ = unsafe.Alignof(42)
+ _ = unsafe.Alignof(new(struct{}))
+ unsafe /* ERROR "not used" */ .Alignof(x)
+
+ var y S0
+ assert(unsafe.Alignof(y.a) == 1)
+ assert(unsafe.Alignof(y.b) == 4)
+ assert(unsafe.Alignof(y.c) == 8)
+ assert(unsafe.Alignof(y.d) == 1)
+ assert(unsafe.Alignof(y.e) == 8)
+}
+
+func _Offsetof() {
+ var x struct{ f int }
+ _ = unsafe /* ERROR "argument" */ .Offsetof()
+ _ = unsafe /* ERROR "argument" */ .Offsetof(1, 2)
+ _ = unsafe.Offsetof(int /* ERROR "not an expression" */)
+ _ = unsafe.Offsetof(x /* ERROR "not a selector" */)
+ _ = unsafe.Offsetof(x.f)
+ _ = unsafe.Offsetof((x.f))
+ _ = unsafe.Offsetof((((((((x))).f)))))
+ unsafe /* ERROR "not used" */ .Offsetof(x.f)
+
+ var y0 S0
+ assert(unsafe.Offsetof(y0.a) == 0)
+ assert(unsafe.Offsetof(y0.b) == 4)
+ assert(unsafe.Offsetof(y0.c) == 8)
+ assert(unsafe.Offsetof(y0.d) == 16)
+ assert(unsafe.Offsetof(y0.e) == 24)
+
+ var y1 S1
+ assert(unsafe.Offsetof(y1.x) == 0)
+ assert(unsafe.Offsetof(y1.y) == 8)
+ assert(unsafe.Offsetof(y1.z) == 24)
+ assert(unsafe.Offsetof(y1.S0) == 32)
+
+ assert(unsafe.Offsetof(y1.S0.a) == 0) // relative to S0
+ assert(unsafe.Offsetof(y1.a) == 32) // relative to S1
+ assert(unsafe.Offsetof(y1.b) == 36) // relative to S1
+ assert(unsafe.Offsetof(y1.c) == 40) // relative to S1
+ assert(unsafe.Offsetof(y1.d) == 48) // relative to S1
+ assert(unsafe.Offsetof(y1.e) == 56) // relative to S1
+
+ var y2 S2
+ assert(unsafe.Offsetof(y2.S1) == 0)
+ _ = unsafe.Offsetof(y2 /* ERROR "embedded via pointer" */ .x)
+}
+
+func _Sizeof() {
+ var x int
+ _ = unsafe /* ERROR "argument" */ .Sizeof()
+ _ = unsafe /* ERROR "argument" */ .Sizeof(1, 2)
+ _ = unsafe.Sizeof(int /* ERROR "not an expression" */)
+ _ = unsafe.Sizeof(42)
+ _ = unsafe.Sizeof(new(complex128))
+ unsafe /* ERROR "not used" */ .Sizeof(x)
+
+ // basic types have size guarantees
+ assert(unsafe.Sizeof(byte(0)) == 1)
+ assert(unsafe.Sizeof(uint8(0)) == 1)
+ assert(unsafe.Sizeof(int8(0)) == 1)
+ assert(unsafe.Sizeof(uint16(0)) == 2)
+ assert(unsafe.Sizeof(int16(0)) == 2)
+ assert(unsafe.Sizeof(uint32(0)) == 4)
+ assert(unsafe.Sizeof(int32(0)) == 4)
+ assert(unsafe.Sizeof(float32(0)) == 4)
+ assert(unsafe.Sizeof(uint64(0)) == 8)
+ assert(unsafe.Sizeof(int64(0)) == 8)
+ assert(unsafe.Sizeof(float64(0)) == 8)
+ assert(unsafe.Sizeof(complex64(0)) == 8)
+ assert(unsafe.Sizeof(complex128(0)) == 16)
+
+ var y0 S0
+ assert(unsafe.Sizeof(y0.a) == 1)
+ assert(unsafe.Sizeof(y0.b) == 4)
+ assert(unsafe.Sizeof(y0.c) == 8)
+ assert(unsafe.Sizeof(y0.d) == 1)
+ assert(unsafe.Sizeof(y0.e) == 16)
+ assert(unsafe.Sizeof(y0) == 40)
+
+ var y1 S1
+ assert(unsafe.Sizeof(y1) == 72)
+
+ var y2 S2
+ assert(unsafe.Sizeof(y2) == 8)
+}
+
+// self-testing only
+func _assert() {
+ var x int
+ assert /* ERROR "argument" */ ()
+ assert /* ERROR "argument" */ (1, 2)
+ assert("foo" /* ERROR "boolean constant" */ )
+ assert(x /* ERROR "boolean constant" */)
+ assert(true)
+ assert /* ERROR "failed" */ (false)
+}
+
+// self-testing only
+func _trace() {
+ // Uncomment the code below to test trace - will produce console output
+ // _ = trace /* ERROR "no value" */ ()
+ // _ = trace(1)
+ // _ = trace(true, 1.2, '\'', "foo", 42i, "foo" <= "bar")
+}
diff --git a/src/pkg/go/types/testdata/const0.src b/src/pkg/go/types/testdata/const0.src
new file mode 100644
index 000000000..a2ca344c7
--- /dev/null
+++ b/src/pkg/go/types/testdata/const0.src
@@ -0,0 +1,215 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// constant declarations
+
+package const0
+
+// constants declarations must be initialized by constants
+var x = 0
+const c0 = x /* ERROR "not constant" */
+
+// untyped constants
+const (
+ // boolean values
+ ub0 = false
+ ub1 = true
+ ub2 = 2 < 1
+ ub3 = ui1 == uf1
+ ub4 = true /* ERROR "cannot convert" */ == 0
+
+ // integer values
+ ui0 = 0
+ ui1 = 1
+ ui2 = 42
+ ui3 = 3141592653589793238462643383279502884197169399375105820974944592307816406286
+ ui4 = -10
+
+ ui5 = ui0 + ui1
+ ui6 = ui1 - ui1
+ ui7 = ui2 * ui1
+ ui8 = ui3 / ui3
+ ui9 = ui3 % ui3
+
+ ui10 = 1 / 0 /* ERROR "division by zero" */
+ ui11 = ui1 / 0 /* ERROR "division by zero" */
+ ui12 = ui3 / ui0 /* ERROR "division by zero" */
+ ui13 = 1 % 0 /* ERROR "division by zero" */
+ ui14 = ui1 % 0 /* ERROR "division by zero" */
+ ui15 = ui3 % ui0 /* ERROR "division by zero" */
+
+ ui16 = ui2 & ui3
+ ui17 = ui2 | ui3
+ ui18 = ui2 ^ ui3
+
+ // floating point values
+ uf0 = 0.
+ uf1 = 1.
+ uf2 = 4.2e1
+ uf3 = 3.141592653589793238462643383279502884197169399375105820974944592307816406286
+ uf4 = 1e-1
+
+ uf5 = uf0 + uf1
+ uf6 = uf1 - uf1
+ uf7 = uf2 * uf1
+ uf8 = uf3 / uf3
+ uf9 = uf3 /* ERROR "not defined" */ % uf3
+
+ uf10 = 1 / 0 /* ERROR "division by zero" */
+ uf11 = uf1 / 0 /* ERROR "division by zero" */
+ uf12 = uf3 / uf0 /* ERROR "division by zero" */
+
+ uf16 = uf2 /* ERROR "not defined" */ & uf3
+ uf17 = uf2 /* ERROR "not defined" */ | uf3
+ uf18 = uf2 /* ERROR "not defined" */ ^ uf3
+
+ // complex values
+ uc0 = 0.i
+ uc1 = 1.i
+ uc2 = 4.2e1i
+ uc3 = 3.141592653589793238462643383279502884197169399375105820974944592307816406286i
+ uc4 = 1e-1i
+
+ uc5 = uc0 + uc1
+ uc6 = uc1 - uc1
+ uc7 = uc2 * uc1
+ uc8 = uc3 / uc3
+ uc9 = uc3 /* ERROR "not defined" */ % uc3
+
+ uc10 = 1 / 0 /* ERROR "division by zero" */
+ uc11 = uc1 / 0 /* ERROR "division by zero" */
+ uc12 = uc3 / uc0 /* ERROR "division by zero" */
+
+ uc16 = uc2 /* ERROR "not defined" */ & uc3
+ uc17 = uc2 /* ERROR "not defined" */ | uc3
+ uc18 = uc2 /* ERROR "not defined" */ ^ uc3
+)
+
+type (
+ mybool bool
+ myint int
+ myfloat float64
+ mycomplex complex128
+)
+
+// typed constants
+const (
+ // boolean values
+ tb0 bool = false
+ tb1 bool = true
+ tb2 mybool = 2 < 1
+ tb3 mybool = ti1 /* ERROR "cannot compare" */ == tf1
+
+ // integer values
+ ti0 int8 = ui0
+ ti1 int32 = ui1
+ ti2 int64 = ui2
+ ti3 myint = ui3 /* ERROR "overflows" */
+ ti4 myint = ui4
+
+ ti5 = ti0 /* ERROR "mismatched types" */ + ti1
+ ti6 = ti1 - ti1
+ ti7 = ti2 /* ERROR "mismatched types" */ * ti1
+ //ti8 = ti3 / ti3 // TODO(gri) enable this
+ //ti9 = ti3 % ti3 // TODO(gri) enable this
+
+ ti10 = 1 / 0 /* ERROR "division by zero" */
+ ti11 = ti1 / 0 /* ERROR "division by zero" */
+ ti12 = ti3 /* ERROR "mismatched types" */ / ti0
+ ti13 = 1 % 0 /* ERROR "division by zero" */
+ ti14 = ti1 % 0 /* ERROR "division by zero" */
+ ti15 = ti3 /* ERROR "mismatched types" */ % ti0
+
+ ti16 = ti2 /* ERROR "mismatched types" */ & ti3
+ ti17 = ti2 /* ERROR "mismatched types" */ | ti4
+ ti18 = ti2 ^ ti5 // no mismatched types error because the type of ti5 is unknown
+
+ // floating point values
+ tf0 float32 = 0.
+ tf1 float32 = 1.
+ tf2 float64 = 4.2e1
+ tf3 myfloat = 3.141592653589793238462643383279502884197169399375105820974944592307816406286
+ tf4 myfloat = 1e-1
+
+ tf5 = tf0 + tf1
+ tf6 = tf1 - tf1
+ tf7 = tf2 /* ERROR "mismatched types" */ * tf1
+ // tf8 = tf3 / tf3 // TODO(gri) enable this
+ tf9 = tf3 /* ERROR "not defined" */ % tf3
+
+ tf10 = 1 / 0 /* ERROR "division by zero" */
+ tf11 = tf1 / 0 /* ERROR "division by zero" */
+ tf12 = tf3 /* ERROR "mismatched types" */ / tf0
+
+ tf16 = tf2 /* ERROR "mismatched types" */ & tf3
+ tf17 = tf2 /* ERROR "mismatched types" */ | tf3
+ tf18 = tf2 /* ERROR "mismatched types" */ ^ tf3
+
+ // complex values
+ tc0 = 0.i
+ tc1 = 1.i
+ tc2 = 4.2e1i
+ tc3 = 3.141592653589793238462643383279502884197169399375105820974944592307816406286i
+ tc4 = 1e-1i
+
+ tc5 = tc0 + tc1
+ tc6 = tc1 - tc1
+ tc7 = tc2 * tc1
+ tc8 = tc3 / tc3
+ tc9 = tc3 /* ERROR "not defined" */ % tc3
+
+ tc10 = 1 / 0 /* ERROR "division by zero" */
+ tc11 = tc1 / 0 /* ERROR "division by zero" */
+ tc12 = tc3 / tc0 /* ERROR "division by zero" */
+
+ tc16 = tc2 /* ERROR "not defined" */ & tc3
+ tc17 = tc2 /* ERROR "not defined" */ | tc3
+ tc18 = tc2 /* ERROR "not defined" */ ^ tc3
+)
+
+// initialization cycles
+const (
+ a /* ERROR "cycle" */ = a
+ b /* ERROR "cycle" */ , c /* ERROR "cycle" */, d, e = e, d, c, b // TODO(gri) should only have one cycle error
+ f float64 = d
+)
+
+// multiple initialization
+const (
+ a1, a2, a3 = 7, 3.1415926, "foo"
+ b1, b2, b3 = b3, b1, 42
+ _p0 = assert(a1 == 7)
+ _p1 = assert(a2 == 3.1415926)
+ _p2 = assert(a3 == "foo")
+ _p3 = assert(b1 == 42)
+ _p4 = assert(b2 == 42)
+ _p5 = assert(b3 == 42)
+)
+
+// iota
+const (
+ iota0 = iota
+ iota1 = iota
+ iota2 = iota*2
+ _a0 = assert(iota0 == 0)
+ _a1 = assert(iota1 == 1)
+ _a2 = assert(iota2 == 4)
+ iota6 = iota*3
+
+ iota7
+ iota8
+ _a3 = assert(iota7 == 21)
+ _a4 = assert(iota8 == 24)
+)
+
+const (
+ _b0 = iota
+ _b1 = assert(iota + iota2 == 5)
+)
+
+// special cases
+const (
+ _n0 = nil /* ERROR "invalid constant type" */
+ _n1 = [ /* ERROR "not constant" */ ]int{}
+) \ No newline at end of file
diff --git a/src/pkg/go/types/testdata/conversions.src b/src/pkg/go/types/testdata/conversions.src
new file mode 100644
index 000000000..1b1518366
--- /dev/null
+++ b/src/pkg/go/types/testdata/conversions.src
@@ -0,0 +1,18 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// conversions
+
+package conversions
+
+// argument count
+var (
+ _v0 = int /* ERROR "one argument" */ ()
+ _v1 = int /* ERROR "one argument" */ (1, 2)
+)
+
+//
+var (
+ _v2 = int8(0)
+) \ No newline at end of file
diff --git a/src/pkg/go/types/testdata/decls0.src b/src/pkg/go/types/testdata/decls0.src
new file mode 100644
index 000000000..f0115bd9d
--- /dev/null
+++ b/src/pkg/go/types/testdata/decls0.src
@@ -0,0 +1,187 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// type declarations
+
+package decls0
+
+import (
+ "unsafe"
+ // we can have multiple blank imports (was bug)
+ _ "math"
+ _ "net/rpc"
+ // reflect defines a type "flag" which shows up in the gc export data
+ "reflect"
+ . "reflect"
+)
+
+// reflect.flag must not be visible in this package
+type flag int
+type _ reflect /* ERROR "cannot refer to unexported" */ .flag
+
+// dot-imported exported objects may conflict with local objects
+type Value /* ERROR "redeclared in this block by dot-import" */ struct{}
+
+const pi = 3.1415
+
+type (
+ N undeclared /* ERROR "undeclared" */
+ B bool
+ I int32
+ A [10]P
+ T struct {
+ x, y P
+ }
+ P *T
+ R (*R)
+ F func(A) I
+ Y interface {
+ f(A) I
+ }
+ S [](((P)))
+ M map[I]F
+ C chan<- I
+
+ // blank types must be typechecked
+ _ pi /* ERROR "not a type" */
+ _ struct{}
+ _ struct{ pi /* ERROR "not a type" */ }
+)
+
+
+// invalid array types
+type (
+ iA0 [... /* ERROR "invalid use of '...'" */ ]byte
+ iA1 [1 /* ERROR "invalid array length" */ <<100]int
+ iA2 [- /* ERROR "invalid array length" */ 1]complex128
+ iA3 ["foo" /* ERROR "invalid array length" */ ]string
+)
+
+
+type (
+ p1 pi /* ERROR "no single field or method foo" */ .foo
+ p2 unsafe.Pointer
+)
+
+
+type (
+ Pi pi /* ERROR "not a type" */
+
+ a /* ERROR "illegal cycle" */ a
+ a /* ERROR "redeclared" */ int
+
+ // where the cycle error appears depends on the
+ // order in which declarations are processed
+ // (which depends on the order in which a map
+ // is iterated through)
+ b /* ERROR "illegal cycle" */ c
+ c d
+ d e
+ e b
+
+ t *t
+
+ U V
+ V *W
+ W U
+
+ P1 *S2
+ P2 P1
+
+ S0 struct {
+ }
+ S1 struct {
+ a, b, c int
+ u, v, a /* ERROR "redeclared" */ float32
+ }
+ S2 struct {
+ U // anonymous field
+ // TODO(gri) recognize double-declaration below
+ // U /* ERROR "redeclared" */ int
+ }
+ S3 struct {
+ x S2
+ }
+ S4/* ERROR "illegal cycle" */ struct {
+ S4
+ }
+ S5 /* ERROR "illegal cycle" */ struct {
+ S6
+ }
+ S6 struct {
+ field S7
+ }
+ S7 struct {
+ S5
+ }
+
+ L1 []L1
+ L2 []int
+
+ A1 [10.0]int
+ A2 /* ERROR "illegal cycle" */ [10]A2
+ A3 /* ERROR "illegal cycle" */ [10]struct {
+ x A4
+ }
+ A4 [10]A3
+
+ F1 func()
+ F2 func(x, y, z float32)
+ F3 func(x, y, x /* ERROR "redeclared" */ float32)
+ F4 func() (x, y, x /* ERROR "redeclared" */ float32)
+ F5 func(x int) (x /* ERROR "redeclared" */ float32)
+ F6 func(x ...int)
+
+ I1 interface{}
+ I2 interface {
+ m1()
+ }
+ I3 interface { /* ERROR "multiple methods named m1" */
+ m1()
+ m1 /* ERROR "redeclared" */ ()
+ }
+ I4 interface {
+ m1(x, y, x /* ERROR "redeclared" */ float32)
+ m2() (x, y, x /* ERROR "redeclared" */ float32)
+ m3(x int) (x /* ERROR "redeclared" */ float32)
+ }
+ I5 interface {
+ m1(I5)
+ }
+ I6 interface {
+ S0 /* ERROR "not an interface" */
+ }
+ I7 interface {
+ I1
+ I1
+ }
+ I8 /* ERROR "illegal cycle" */ interface {
+ I8
+ }
+ // Use I09 (rather than I9) because it appears lexically before
+ // I10 so that we get the illegal cycle here rather then in the
+ // declaration of I10. If the implementation sorts by position
+ // rather than name, the error message will still be here.
+ I09 /* ERROR "illegal cycle" */ interface {
+ I10
+ }
+ I10 interface {
+ I11
+ }
+ I11 interface {
+ I09
+ }
+
+ C1 chan int
+ C2 <-chan int
+ C3 chan<- C3
+ C4 chan C5
+ C5 chan C6
+ C6 chan C4
+
+ M1 map[Last]string
+ M2 map[string]M2
+
+ Last int
+)
diff --git a/src/pkg/go/types/testdata/decls1.src b/src/pkg/go/types/testdata/decls1.src
new file mode 100644
index 000000000..2251f457f
--- /dev/null
+++ b/src/pkg/go/types/testdata/decls1.src
@@ -0,0 +1,132 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// variable declarations
+
+package decls1
+
+import (
+ "math"
+)
+
+// Global variables without initialization
+var (
+ a, b bool
+ c byte
+ d uint8
+ r rune
+ i int
+ j, k, l int
+ x, y float32
+ xx, yy float64
+ u, v complex64
+ uu, vv complex128
+ s, t string
+ array []byte
+ iface interface{}
+
+ blank _ /* ERROR "cannot use _" */
+)
+
+// Global variables with initialization
+var (
+ s1 = i + j
+ s2 = i /* ERROR "mismatched types" */ + x
+ s3 = c + d
+ s4 = s + t
+ s5 = s /* ERROR "invalid operation" */ / t
+ s6 = array[t1]
+ s7 = array[x /* ERROR "index" */]
+ s8 = &a
+ s10 = &42 /* ERROR "cannot take address" */
+ s11 = &v
+ s12 = -(u + *t11) / *&v
+ s13 = a /* ERROR "shifted operand" */ << d
+ s14 = i << j /* ERROR "must be unsigned" */
+ s18 = math.Pi * 10.0
+ s19 = s1 /* ERROR "cannot call" */ ()
+ s20 = f0 /* ERROR "no value" */ ()
+ s21 = f6(1, s1, i)
+ s22 = f6(1, s1, uu /* ERROR "cannot assign" */ )
+
+ t1 int = i + j
+ t2 int = i /* ERROR "mismatched types" */ + x
+ t3 int = c /* ERROR "cannot assign" */ + d
+ t4 string = s + t
+ t5 string = s /* ERROR "invalid operation" */ / t
+ t6 byte = array[t1]
+ t7 byte = array[x /* ERROR "index" */]
+ t8 *int = & /* ERROR "cannot assign" */ a
+ t10 *int = &42 /* ERROR "cannot take address" */
+ t11 *complex64 = &v
+ t12 complex64 = -(u + *t11) / *&v
+ t13 int = a /* ERROR "shifted operand" */ << d
+ t14 int = i << j /* ERROR "must be unsigned" */
+ t15 math /* ERROR "not in selector" */
+ t16 math /* ERROR "unexported" */ .xxx
+ t17 math /* ERROR "not a type" */ .Pi
+ t18 float64 = math.Pi * 10.0
+ t19 int = t1 /* ERROR "cannot call" */ ()
+ t20 int = f0 /* ERROR "no value" */ ()
+)
+
+// Various more complex expressions
+var (
+ u1 = x /* ERROR "not an interface" */ .(int)
+ u2 = iface.([]int)
+ u3 = iface.(a /* ERROR "not a type" */ )
+ u4, ok = iface.(int)
+ u5 /* ERROR "assignment count mismatch" */ , ok2, ok3 = iface.(int)
+)
+
+// Constant expression initializations
+var (
+ v1 = 1 /* ERROR "cannot convert" */ + "foo"
+ v2 = c + 255
+ v3 = c + 256 /* ERROR "overflows" */
+ v4 = r + 2147483647
+ v5 = r + 2147483648 /* ERROR "overflows" */
+ v6 = 42
+ v7 = v6 + 9223372036854775807
+ v8 = v6 + 9223372036854775808 /* ERROR "overflows" */
+ v9 = i + 1 << 10
+ v10 byte = 1024 /* ERROR "overflows" */
+ v11 = xx/yy*yy - xx
+ v12 = true && false
+ v13 = nil /* ERROR "use of untyped nil" */
+)
+
+// Multiple assignment expressions
+var (
+ m1a, m1b = 1, 2
+ m2a /* ERROR "assignment count mismatch" */ , m2b, m2c = 1, 2
+ m3a /* ERROR "assignment count mismatch" */ , m3b = 1, 2, 3
+)
+
+// Declaration of parameters and results
+func f0() {}
+func f1(a /* ERROR "not a type" */) {}
+func f2(a, b, c d /* ERROR "not a type" */) {}
+
+func f3() int {}
+func f4() a /* ERROR "not a type" */ {}
+func f5() (a, b, c d /* ERROR "not a type" */) {}
+
+func f6(a, b, c int) complex128 { return 0 }
+
+// Declaration of receivers
+type T struct{}
+
+func (T) m0() {}
+func (*T) m1() {}
+func (x T) m2() {}
+func (x *T) m3() {}
+
+
+// Initialization functions
+func init() {}
+func /* ERROR "no arguments and no return values" */ init(int) {}
+func /* ERROR "no arguments and no return values" */ init() int { return 0 }
+func /* ERROR "no arguments and no return values" */ init(int) int {}
+func (T) init(int) int { return 0 }
diff --git a/src/pkg/go/types/testdata/decls2a.src b/src/pkg/go/types/testdata/decls2a.src
new file mode 100644
index 000000000..3867be737
--- /dev/null
+++ b/src/pkg/go/types/testdata/decls2a.src
@@ -0,0 +1,67 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// method declarations
+
+package decls2
+
+import "time"
+
+// T1 declared before its methods.
+type T1 struct{
+ f int
+}
+
+func (T1) m() {}
+func (T1) m /* ERROR "redeclared" */ () {}
+func (x *T1) f /* ERROR "field and method" */ () {}
+
+// T2's method declared before the type.
+func (*T2) f /* ERROR "field and method" */ () {}
+
+type T2 struct {
+ f int
+}
+
+// Methods declared without a declared type.
+func (undeclared /* ERROR "undeclared" */) m() {}
+func (x *undeclared /* ERROR "undeclared" */) m() {}
+
+// TODO(gri) try to get rid of double error reporting here
+func (pi /* ERROR "not a type" */) m1() {}
+func (x pi /* ERROR "not a type" */) m2() {}
+func (x *pi /* ERROR "not a type" */ ) m3() {} // TODO(gri) not closing the last /* comment crashes the system
+
+// Blank types.
+type _ struct { m int }
+type _ struct { m int }
+
+// TODO(gri) blank idents not fully checked - disabled for now
+// func (_ /* ERROR "cannot use _" */) m() {}
+// func (_ /* ERROR "cannot use _" */) m() {}
+
+// Methods with receiver base type declared in another file.
+func (T3) m1() {}
+func (*T3) m2() {}
+func (x T3) m3() {}
+func (x *T3) f /* ERROR "field and method" */ () {}
+
+// Methods of non-struct type.
+type T4 func()
+
+func (self T4) m() func() { return self }
+
+// Methods associated with an interface.
+type T5 interface {
+ m() int
+}
+
+func (T5 /* ERROR "invalid receiver" */) m1() {}
+func (T5 /* ERROR "invalid receiver" */) m2() {}
+
+// Methods associated with non-local or unnamed types.
+func (int /* ERROR "non-local type" */ ) m() {}
+func ([ /* ERROR "expected" */ ]int) m() {}
+func (time /* ERROR "expected" */ .Time) m() {}
+func (x interface /* ERROR "expected" */ {}) m() {}
diff --git a/src/pkg/go/types/testdata/decls2b.src b/src/pkg/go/types/testdata/decls2b.src
new file mode 100644
index 000000000..c7f9ddf01
--- /dev/null
+++ b/src/pkg/go/types/testdata/decls2b.src
@@ -0,0 +1,28 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// method declarations
+
+package decls2
+
+const pi = 3.1415
+
+func (T1) m /* ERROR "redeclared" */ () {}
+
+type T3 struct {
+ f *T3
+}
+
+type T6 struct {
+ x int
+}
+
+func (t *T6) m1() int {
+ return t.x
+}
+
+func f() {
+ var t *T6
+ t.m1()
+} \ No newline at end of file
diff --git a/src/pkg/go/types/testdata/decls3.src b/src/pkg/go/types/testdata/decls3.src
new file mode 100644
index 000000000..6aa9f90e9
--- /dev/null
+++ b/src/pkg/go/types/testdata/decls3.src
@@ -0,0 +1,253 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// embedded types
+
+package decls3
+
+// fields with the same name at the same level cancel each other out
+
+func _() {
+ type (
+ T1 struct { X int }
+ T2 struct { X int }
+ T3 struct { T1; T2 } // X is embedded twice at the same level via T1->X, T2->X
+ )
+
+ var t T3
+ _ = t /* ERROR "no single field or method" */ .X
+}
+
+func _() {
+ type (
+ T1 struct { X int }
+ T2 struct { T1 }
+ T3 struct { T1 }
+ T4 struct { T2; T3 } // X is embedded twice at the same level via T2->T1->X, T3->T1->X
+ )
+
+ var t T4
+ _ = t /* ERROR "no single field or method" */ .X
+}
+
+func issue4355() {
+ type (
+ T1 struct {X int}
+ T2 struct {T1}
+ T3 struct {T2}
+ T4 struct {T2}
+ T5 struct {T3; T4} // X is embedded twice at the same level via T3->T2->T1->X, T4->T2->T1->X
+ )
+
+ var t T5
+ _ = t /* ERROR "no single field or method" */ .X
+}
+
+// Embedded fields can be predeclared types.
+
+func _() {
+ type T0 struct{
+ int
+ float32
+ f int
+ }
+ var x T0
+ _ = x.int
+ _ = x.float32
+ _ = x.f
+
+ type T1 struct{
+ T0
+ }
+ var y T1
+ _ = y.int
+ _ = y.float32
+ _ = y.f
+}
+
+// Borrowed from the FieldByName test cases in reflect/all_test.go.
+
+type D1 struct {
+ d int
+}
+type D2 struct {
+ d int
+}
+
+type S0 struct {
+ A, B, C int
+ D1
+ D2
+}
+
+type S1 struct {
+ B int
+ S0
+}
+
+type S2 struct {
+ A int
+ *S1
+}
+
+type S1x struct {
+ S1
+}
+
+type S1y struct {
+ S1
+}
+
+type S3 struct {
+ S1x
+ S2
+ D, E int
+ *S1y
+}
+
+type S4 struct {
+ *S4
+ A int
+}
+
+// The X in S6 and S7 annihilate, but they also block the X in S8.S9.
+type S5 struct {
+ S6
+ S7
+ S8
+}
+
+type S6 struct {
+ X int
+}
+
+type S7 S6
+
+type S8 struct {
+ S9
+}
+
+type S9 struct {
+ X int
+ Y int
+}
+
+// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9.
+type S10 struct {
+ S11
+ S12
+ S13
+}
+
+type S11 struct {
+ S6
+}
+
+type S12 struct {
+ S6
+}
+
+type S13 struct {
+ S8
+}
+
+func _() {
+ _ = struct /* ERROR "no single field or method" */ {}{}.Foo
+ _ = S0{}.A
+ _ = S0 /* ERROR "no single field or method" */ {}.D
+ _ = S1{}.A
+ _ = S1{}.B
+ _ = S1{}.S0
+ _ = S1{}.C
+ _ = S2{}.A
+ _ = S2{}.S1
+ _ = S2{}.B
+ _ = S2{}.C
+ _ = S2 /* ERROR "no single field or method" */ {}.D
+ _ = S3 /* ERROR "no single field or method" */ {}.S1
+ _ = S3{}.A
+ _ = S3 /* ERROR "no single field or method" */ {}.B
+ _ = S3{}.D
+ _ = S3{}.E
+ _ = S4{}.A
+ _ = S4 /* ERROR "no single field or method" */ {}.B
+ _ = S5 /* ERROR "no single field or method" */ {}.X
+ _ = S5{}.Y
+ _ = S10 /* ERROR "no single field or method" */ {}.X
+ _ = S10{}.Y
+}
+
+// Borrowed from the FieldByName benchmark in reflect/all_test.go.
+
+type R0 struct {
+ *R1
+ *R2
+ *R3
+ *R4
+}
+
+type R1 struct {
+ *R5
+ *R6
+ *R7
+ *R8
+}
+
+type R2 R1
+type R3 R1
+type R4 R1
+
+type R5 struct {
+ *R9
+ *R10
+ *R11
+ *R12
+}
+
+type R6 R5
+type R7 R5
+type R8 R5
+
+type R9 struct {
+ *R13
+ *R14
+ *R15
+ *R16
+}
+
+type R10 R9
+type R11 R9
+type R12 R9
+
+type R13 struct {
+ *R17
+ *R18
+ *R19
+ *R20
+}
+
+type R14 R13
+type R15 R13
+type R16 R13
+
+type R17 struct {
+ *R21
+ *R22
+ *R23
+ *R24
+}
+
+type R18 R17
+type R19 R17
+type R20 R17
+
+type R21 struct {
+ X int
+}
+
+type R22 R21
+type R23 R21
+type R24 R21
+
+var _ = R0 /* ERROR "no single field or method" */ {}.X \ No newline at end of file
diff --git a/src/pkg/go/types/testdata/exports.go b/src/pkg/go/types/testdata/exports.go
new file mode 100644
index 000000000..8ee28b094
--- /dev/null
+++ b/src/pkg/go/types/testdata/exports.go
@@ -0,0 +1,89 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file is used to generate an object file which
+// serves as test file for gcimporter_test.go.
+
+package exports
+
+import (
+ "go/ast"
+)
+
+// Issue 3682: Correctly read dotted identifiers from export data.
+const init1 = 0
+
+func init() {}
+
+const (
+ C0 int = 0
+ C1 = 3.14159265
+ C2 = 2.718281828i
+ C3 = -123.456e-789
+ C4 = +123.456E+789
+ C5 = 1234i
+ C6 = "foo\n"
+ C7 = `bar\n`
+)
+
+type (
+ T1 int
+ T2 [10]int
+ T3 []int
+ T4 *int
+ T5 chan int
+ T6a chan<- int
+ T6b chan (<-chan int)
+ T6c chan<- (chan int)
+ T7 <-chan *ast.File
+ T8 struct{}
+ T9 struct {
+ a int
+ b, c float32
+ d []string `go:"tag"`
+ }
+ T10 struct {
+ T8
+ T9
+ _ *T10
+ }
+ T11 map[int]string
+ T12 interface{}
+ T13 interface {
+ m1()
+ m2(int) float32
+ }
+ T14 interface {
+ T12
+ T13
+ m3(x ...struct{}) []T9
+ }
+ T15 func()
+ T16 func(int)
+ T17 func(x int)
+ T18 func() float32
+ T19 func() (x float32)
+ T20 func(...interface{})
+ T21 struct{ next *T21 }
+ T22 struct{ link *T23 }
+ T23 struct{ link *T22 }
+ T24 *T24
+ T25 *T26
+ T26 *T27
+ T27 *T25
+ T28 func(T28) T28
+)
+
+var (
+ V0 int
+ V1 = -991.0
+)
+
+func F1() {}
+func F2(x int) {}
+func F3() int { return 0 }
+func F4() float32 { return 0 }
+func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10)
+
+func (p *T1) M1()
diff --git a/src/pkg/go/types/testdata/expr0.src b/src/pkg/go/types/testdata/expr0.src
new file mode 100644
index 000000000..8d057f63c
--- /dev/null
+++ b/src/pkg/go/types/testdata/expr0.src
@@ -0,0 +1,161 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// unary expressions
+
+package expr0
+
+var (
+ // bool
+ b0 = true
+ b1 bool = b0
+ b2 = !true
+ b3 = !b1
+ b4 bool = !true
+ b5 bool = !b4
+ b6 = +b0 /* ERROR "not defined" */
+ b7 = -b0 /* ERROR "not defined" */
+ b8 = ^b0 /* ERROR "not defined" */
+ b9 = *b0 /* ERROR "cannot indirect" */
+ b10 = &true /* ERROR "cannot take address" */
+ b11 = &b0
+ b12 = <-b0 /* ERROR "cannot receive" */
+
+ // int
+ i0 = 1
+ i1 int = i0
+ i2 = +1
+ i3 = +i0
+ i4 int = +1
+ i5 int = +i4
+ i6 = -1
+ i7 = -i0
+ i8 int = -1
+ i9 int = -i4
+ i10 = !i0 /* ERROR "not defined" */
+ i11 = ^1
+ i12 = ^i0
+ i13 int = ^1
+ i14 int = ^i4
+ i15 = *i0 /* ERROR "cannot indirect" */
+ i16 = &i0
+ i17 = *i16
+ i18 = <-i16 /* ERROR "cannot receive" */
+
+ // uint
+ u0 = uint(1)
+ u1 uint = u0
+ u2 = +1
+ u3 = +u0
+ u4 uint = +1
+ u5 uint = +u4
+ u6 = -1
+ u7 = -u0
+ u8 uint = - /* ERROR "overflows" */ 1
+ u9 uint = -u4
+ u10 = !u0 /* ERROR "not defined" */
+ u11 = ^1
+ u12 = ^i0
+ u13 uint = ^ /* ERROR "overflows" */ 1
+ u14 uint = ^u4
+ u15 = *u0 /* ERROR "cannot indirect" */
+ u16 = &u0
+ u17 = *u16
+ u18 = <-u16 /* ERROR "cannot receive" */
+ u19 = ^uint(0)
+
+ // float64
+ f0 = float64(1)
+ f1 float64 = f0
+ f2 = +1
+ f3 = +f0
+ f4 float64 = +1
+ f5 float64 = +f4 /* ERROR not defined */
+ f6 = -1
+ f7 = -f0
+ f8 float64 = -1
+ f9 float64 = -f4
+ f10 = !f0 /* ERROR "not defined" */
+ f11 = ^1
+ f12 = ^i0
+ f13 float64 = ^1
+ f14 float64 = ^f4 /* ERROR "not defined" */
+ f15 = *f0 /* ERROR "cannot indirect" */
+ f16 = &f0
+ f17 = *u16
+ f18 = <-u16 /* ERROR "cannot receive" */
+
+ // complex128
+ c0 = complex128(1)
+ c1 complex128 = c0
+ c2 = +1
+ c3 = +c0
+ c4 complex128 = +1
+ c5 complex128 = +c4 /* ERROR not defined */
+ c6 = -1
+ c7 = -c0
+ c8 complex128 = -1
+ c9 complex128 = -c4
+ c10 = !c0 /* ERROR "not defined" */
+ c11 = ^1
+ c12 = ^i0
+ c13 complex128 = ^1
+ c14 complex128 = ^c4 /* ERROR "not defined" */
+ c15 = *c0 /* ERROR "cannot indirect" */
+ c16 = &c0
+ c17 = *u16
+ c18 = <-u16 /* ERROR "cannot receive" */
+
+ // string
+ s0 = "foo"
+ s1 = +"foo" /* ERROR "not defined" */
+ s2 = -s0 /* ERROR "not defined" */
+ s3 = !s0 /* ERROR "not defined" */
+ s4 = ^s0 /* ERROR "not defined" */
+ s5 = *s4 /* ERROR "cannot indirect" */
+ s6 = &s4
+ s7 = *s6
+ s8 = <-s7 /* ERROR "cannot receive" */
+
+ // channel
+ ch chan int
+ rc <-chan float64
+ sc chan <- string
+ ch0 = +ch /* ERROR "not defined" */
+ ch1 = -ch /* ERROR "not defined" */
+ ch2 = !ch /* ERROR "not defined" */
+ ch3 = ^ch /* ERROR "not defined" */
+ ch4 = *ch /* ERROR "cannot indirect" */
+ ch5 = &ch
+ ch6 = *ch5
+ ch7 = <-ch
+ ch8 = <-rc
+ ch9 = <-sc /* ERROR "cannot receive" */
+)
+
+// address of composite literals
+type T struct{x, y int}
+
+func f() T { return T{} }
+
+var (
+ _ = &T{1, 2}
+ _ = &[...]int{}
+ _ = &[]int{}
+ _ = &[]int{}
+ _ = &map[string]T{}
+ _ = &(T{1, 2})
+ _ = &((((T{1, 2}))))
+ _ = &f /* ERROR "cannot take address" */ ()
+)
+
+// recursive pointer types
+type P *P
+
+var (
+ p1 P = new(P)
+ p2 P = *p1
+ p3 P = &p2
+)
+
diff --git a/src/pkg/go/types/testdata/expr1.src b/src/pkg/go/types/testdata/expr1.src
new file mode 100644
index 000000000..8ef0aed6d
--- /dev/null
+++ b/src/pkg/go/types/testdata/expr1.src
@@ -0,0 +1,7 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// binary expressions
+
+package expr1
diff --git a/src/pkg/go/types/testdata/expr2.src b/src/pkg/go/types/testdata/expr2.src
new file mode 100644
index 000000000..674be4005
--- /dev/null
+++ b/src/pkg/go/types/testdata/expr2.src
@@ -0,0 +1,23 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// comparisons
+
+package expr2
+
+func _bool() {
+ const t = true == true
+ const f = true == false
+ _ = t /* ERROR "cannot compare" */ < f
+ _ = 0 /* ERROR "cannot convert" */ == t
+ var b bool
+ var x, y float32
+ b = x < y
+ _ = struct{b bool}{x < y}
+}
+
+// corner cases
+var (
+ v0 = nil /* ERROR "cannot compare" */ == nil
+) \ No newline at end of file
diff --git a/src/pkg/go/types/testdata/expr3.src b/src/pkg/go/types/testdata/expr3.src
new file mode 100644
index 000000000..ff17f2eee
--- /dev/null
+++ b/src/pkg/go/types/testdata/expr3.src
@@ -0,0 +1,463 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// various expressions
+
+package expr3
+
+func shifts1() {
+ var (
+ i0 int
+ u0 uint
+ )
+
+ var (
+ v0 = 1<<0
+ v1 = 1<<i0 /* ERROR "must be unsigned" */
+ v2 = 1<<u0
+ v3 = 1<<"foo" /* ERROR "must be unsigned" */
+ v4 = 1<<- /* ERROR "stupid shift" */ 1
+ v5 = 1<<1025 /* ERROR "stupid shift" */
+ v6 = 1 /* ERROR "overflows" */ <<100
+
+ v10 uint = 1 << 0
+ v11 uint = 1 << u0
+ v12 float32 = 1 /* ERROR "must be integer" */ << u0
+ )
+}
+
+func shifts2() {
+ // from the spec
+ var (
+ s uint = 33
+ i = 1<<s // 1 has type int
+ j int32 = 1<<s // 1 has type int32; j == 0
+ k = uint64(1<<s) // 1 has type uint64; k == 1<<33
+ m int = 1.0<<s // 1.0 has type int
+ n = 1.0<<s != 0 // 1.0 has type int; n == false if ints are 32bits in size
+ o = 1<<s == 2<<s // 1 and 2 have type int; o == true if ints are 32bits in size
+ p = 1<<s == 1<<33 // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int
+ u = 1.0 /* ERROR "must be integer" */ <<s // illegal: 1.0 has type float64, cannot shift
+ v float32 = 1 /* ERROR "must be integer" */ <<s // illegal: 1 has type float32, cannot shift
+ w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression
+ )
+}
+
+func shifts3(a int16, b float32) {
+ var (
+ s uint = 11
+ u = 1 /* ERROR "must be integer" */ <<s + 1.0
+ v complex128 = 1 /* ERROR "must be integer" */ << s + 1.0 /* ERROR "must be integer" */ << s + 1
+ )
+ x := 1.0 /* ERROR "must be integer" */ <<s + 1
+ shifts3(1.0 << s, 1 /* ERROR "must be integer" */ >> s)
+ // TODO(gri) add more tests (systematically)
+}
+
+func shifts4() {
+ // from src/pkg/compress/lzw/reader.go:90
+ {
+ var d struct {
+ bits uint32
+ width uint
+ }
+ _ = uint16(d.bits & (1<<d.width - 1))
+ }
+
+ // from src/pkg/debug/dwarf/buf.go:116
+ {
+ var ux uint64
+ var bits uint
+ x := int64(ux)
+ if x&(1<<(bits-1)) != 0 {}
+ }
+
+ // from src/pkg/encoding/asn1/asn1.go:160
+ {
+ var bytes []byte
+ if bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 {}
+ }
+
+ // from src/pkg/math/big/rat.go:140
+ {
+ var exp int
+ var mantissa uint64
+ shift := uint64(-1022 - (exp - 1)) // [1..53)
+ _ = mantissa & (1<<shift - 1)
+ }
+
+ // from src/pkg/net/interface.go:51
+ {
+ type Flags uint
+ var f Flags
+ var i int
+ if f&(1<<uint(i)) != 0 {}
+ }
+
+ // from src/pkg/runtime/softfloat64.go:234
+ {
+ var gm uint64
+ var shift uint
+ _ = gm & (1<<shift - 1)
+ }
+
+ // from src/pkg/strconv/atof.go:326
+ {
+ var mant uint64
+ var mantbits uint
+ if mant == 2<<mantbits {}
+ }
+
+ // from src/pkg/syscall/route_bsd.go:82
+ {
+ var Addrs int32
+ const rtaRtMask = 1
+ var i uint
+ if Addrs&rtaRtMask&(1<<i) == 0 {}
+ }
+
+ // from src/pkg/text/scanner/scanner.go:540
+ {
+ var s struct { Whitespace uint64 }
+ var ch rune
+ for s.Whitespace&(1<<uint(ch)) != 0 {}
+ }
+}
+
+// TODO(gri) The error messages below depond on adjusting the spec
+// to reflect what gc is doing at the moment (the spec
+// asks for run-time errors at the moment - see issue 4231).
+//
+func indexes() {
+ _ = 1 /* ERROR "cannot index" */ [0]
+ _ = indexes /* ERROR "cannot index" */ [0]
+ _ = ( /* ERROR "cannot slice" */ 12 + 3)[1:2]
+
+ var a [10]int
+ _ = a[true /* ERROR "must be integer" */ ]
+ _ = a["foo" /* ERROR "must be integer" */ ]
+ _ = a[1.1 /* ERROR "must be integer" */ ]
+ _ = a[1.0]
+ _ = a[- /* ERROR "index .* negative" */ 1]
+ _ = a[- /* ERROR "index .* negative" */ 1 :]
+ _ = a[: - /* ERROR "index .* negative" */ 1]
+ var a0 int
+ a0 = a[0]
+ var a1 int32
+ a1 = a /* ERROR "cannot assign" */ [1]
+ _ = a[9]
+ _ = a[10 /* ERROR "index .* out of bounds" */ ]
+ _ = a[1 /* ERROR "stupid index" */ <<100]
+ _ = a[10:]
+ _ = a[:10]
+ _ = a[10:10]
+ _ = a[11 /* ERROR "index .* out of bounds" */ :]
+ _ = a[: 11 /* ERROR "index .* out of bounds" */ ]
+ _ = a[: 1 /* ERROR "stupid index" */ <<100]
+
+ pa := &a
+ _ = pa[9]
+ _ = pa[10 /* ERROR "index .* out of bounds" */ ]
+ _ = pa[1 /* ERROR "stupid index" */ <<100]
+ _ = pa[10:]
+ _ = pa[:10]
+ _ = pa[10:10]
+ _ = pa[11 /* ERROR "index .* out of bounds" */ :]
+ _ = pa[: 11 /* ERROR "index .* out of bounds" */ ]
+ _ = pa[: 1 /* ERROR "stupid index" */ <<100]
+
+ var b [0]int
+ _ = b[0 /* ERROR "index .* out of bounds" */ ]
+ _ = b[:]
+ _ = b[0:]
+ _ = b[:0]
+ _ = b[0:0]
+
+ var s []int
+ _ = s[- /* ERROR "index .* negative" */ 1]
+ _ = s[- /* ERROR "index .* negative" */ 1 :]
+ _ = s[: - /* ERROR "index .* negative" */ 1]
+ _ = s[0]
+ _ = s[1 : 2]
+ _ = s[2 /* ERROR "inverted slice range" */ : 1]
+ _ = s[2 :]
+ _ = s[: 1 /* ERROR "stupid index" */ <<100]
+ _ = s[1 /* ERROR "stupid index" */ <<100 :]
+ _ = s[1 /* ERROR "stupid index" */ <<100 : 1 /* ERROR "stupid index" */ <<100]
+
+ var t string
+ _ = t[- /* ERROR "index .* negative" */ 1]
+ _ = t[- /* ERROR "index .* negative" */ 1 :]
+ _ = t[: - /* ERROR "index .* negative" */ 1]
+ var t0 byte
+ t0 = t[0]
+ var t1 rune
+ t1 = t /* ERROR "cannot assign" */ [2]
+ _ = ("foo" + "bar")[5]
+ _ = ("foo" + "bar")[6 /* ERROR "index .* out of bounds" */ ]
+
+ const c = "foo"
+ _ = c[- /* ERROR "index .* negative" */ 1]
+ _ = c[- /* ERROR "index .* negative" */ 1 :]
+ _ = c[: - /* ERROR "index .* negative" */ 1]
+ var c0 byte
+ c0 = c[0]
+ var c2 float32
+ c2 = c /* ERROR "cannot assign" */ [2]
+ _ = c[3 /* ERROR "index .* out of bounds" */ ]
+ _ = ""[0 /* ERROR "index .* out of bounds" */ ]
+
+ _ = s[1<<30] // no compile-time error here
+
+ // issue 4913
+ type mystring string
+ var ss string
+ var ms mystring
+ var i, j int
+ ss = "foo"[1:2]
+ ss = "foo"[i:j]
+ ms = "foo" /* ERROR "cannot assign" */ [1:2]
+ ms = "foo" /* ERROR "cannot assign" */ [i:j]
+}
+
+type T struct {
+ x int
+}
+
+func (*T) m() {}
+
+func method_expressions() {
+ _ = T /* ERROR "no single field or method" */ .a
+ _ = T /* ERROR "has no method" */ .x
+ _ = T.m
+ var f func(*T) = (*T).m
+ var g func(*T) = ( /* ERROR "cannot assign" */ T).m
+}
+
+func struct_literals() {
+ type T0 struct {
+ a, b, c int
+ }
+
+ type T1 struct {
+ T0
+ a, b int
+ u float64
+ s string
+ }
+
+ // keyed elements
+ _ = T1{}
+ _ = T1{a: 0, 1 /* ERROR "mixture of .* elements" */ }
+ _ = T1{aa /* ERROR "unknown field" */ : 0}
+ _ = T1{1 /* ERROR "invalid field name" */ : 0}
+ _ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10}
+ _ = T1{a: "foo" /* ERROR "cannot use" */ }
+ _ = T1{c /* ERROR "unknown field" */ : 0}
+ _ = T1{T0: { /* ERROR "missing type" */ }}
+ _ = T1{T0: T0{}}
+ _ = T1{T0 /* ERROR "invalid field name" */ .a: 0}
+
+ // unkeyed elements
+ _ = T0{1, 2, 3}
+ _ = T0{1, b /* ERROR "mixture" */ : 2, 3}
+ _ = T0{1, 2} /* ERROR "too few values" */
+ _ = T0{1, 2, 3, 4 /* ERROR "too many values" */ }
+ _ = T0{1, "foo" /* ERROR "cannot use" */, 3.4 /* ERROR "cannot use" */}
+}
+
+func array_literals() {
+ type A0 [0]int
+ _ = A0{}
+ _ = A0{0 /* ERROR "index .* out of bounds" */}
+ _ = A0{0 /* ERROR "index .* out of bounds" */ : 0}
+
+ type A1 [10]int
+ _ = A1{}
+ _ = A1{0, 1, 2}
+ _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
+ _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /* ERROR "index .* out of bounds" */ }
+ _ = A1{- /* ERROR "index .* negative" */ 1: 0}
+ _ = A1{8: 8, 9}
+ _ = A1{8: 8, 9, 10 /* ERROR "index .* out of bounds" */ }
+ _ = A1{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
+ _ = A1{5: 5, 6, 7, 3: 3, 4}
+ _ = A1{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ }
+ _ = A1{10 /* ERROR "index .* out of bounds" */ : 10, 10 /* ERROR "index .* out of bounds" */ : 10}
+ _ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "stupid index" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
+ _ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "stupid index" */ <<100: 4}
+ _ = A1{2.0}
+ _ = A1{2.1 /* ERROR "cannot use" */ }
+ _ = A1{"foo" /* ERROR "cannot use" */ }
+
+ a0 := [...]int{}
+ assert(len(a0) == 0)
+
+ a1 := [...]int{0, 1, 2}
+ assert(len(a1) == 3)
+ var a13 [3]int
+ var a14 [4]int
+ a13 = a1
+ a14 = a1 /* ERROR "cannot assign" */
+
+ a2 := [...]int{- /* ERROR "index .* negative" */ 1: 0}
+
+ a3 := [...]int{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
+ assert(len(a3) == 5) // somewhat arbitrary
+
+ a4 := [...]complex128{0, 1, 2, 1<<10-2: -1i, 1i, 400: 10, 12, 14}
+ assert(len(a4) == 1024)
+
+ // from the spec
+ type Point struct { x, y float32 }
+ _ = [...]Point{Point{1.5, -3.5}, Point{0, 0}}
+ _ = [...]Point{{1.5, -3.5}, {0, 0}}
+ _ = [][]int{[]int{1, 2, 3}, []int{4, 5}}
+ _ = [][]int{{1, 2, 3}, {4, 5}}
+ _ = [...]*Point{&Point{1.5, -3.5}, &Point{0, 0}}
+ _ = [...]*Point{{1.5, -3.5}, {0, 0}}
+}
+
+func slice_literals() {
+ type S0 []int
+ _ = S0{}
+ _ = S0{0, 1, 2}
+ _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
+ _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+ _ = S0{- /* ERROR "index .* negative" */ 1: 0}
+ _ = S0{8: 8, 9}
+ _ = S0{8: 8, 9, 10}
+ _ = S0{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
+ _ = S0{5: 5, 6, 7, 3: 3, 4}
+ _ = S0{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ }
+ _ = S0{10: 10, 10 /* ERROR "duplicate index" */ : 10}
+ _ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "stupid index" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
+ _ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "stupid index" */ <<100: 4}
+ _ = S0{2.0}
+ _ = S0{2.1 /* ERROR "cannot use" */ }
+ _ = S0{"foo" /* ERROR "cannot use" */ }
+
+ // indices must be resolved correctly
+ // (for details, see comment in go/parser/parser.go, method parseElement)
+ index1 := 1
+ _ = S0{index1: 1}
+ _ = S0{index2: 2}
+ _ = S0{index3 /* ERROR "undeclared name" */ : 3}
+}
+
+var index2 int = 2
+
+func map_literals() {
+ type M0 map[string]int
+ type M1 map[bool]int
+ type M2 map[*int]int
+
+ _ = M0{}
+ _ = M0{1 /* ERROR "missing key" */ }
+ _ = M0{1 /* ERROR "cannot use .* as string key" */ : 2}
+ _ = M0{"foo": "bar" /* ERROR "cannot use .* as int value" */ }
+ _ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 }
+
+ // map keys must be resolved correctly
+ // (for details, see comment in go/parser/parser.go, method parseElement)
+ key1 := "foo"
+ _ = M0{key1: 1}
+ _ = M0{key2: 2}
+ _ = M0{key3 /* ERROR "undeclared name" */ : 2}
+
+ _ = M1{true: 1, false: 0}
+ _ = M2{nil: 0, &index2: 1}
+}
+
+var key2 string = "bar"
+
+type I interface {
+ m()
+}
+
+type I2 interface {
+ m(int)
+}
+
+type T1 struct{}
+type T2 struct{}
+
+func (T2) m(int) {}
+
+func type_asserts() {
+ var x int
+ _ = x /* ERROR "not an interface" */ .(int)
+
+ var e interface{}
+ var ok bool
+ x, ok = e.(int)
+
+ var t I
+ _ = t /* ERROR "use of .* outside type switch" */ .(type)
+ _ = t.(T)
+ _ = t.(T1 /* ERROR "missing method m" */ )
+ _ = t.(T2 /* ERROR "wrong type for method m" */ )
+ _ = t.(I2 /* ERROR "wrong type for method m" */ )
+}
+
+func f0() {}
+func f1(x int) {}
+func f2(u float32, s string) {}
+func fs(s []byte) {}
+func fv(x ...int) {}
+func fi(x ... interface{}) {}
+
+func g0() {}
+func g1() int { return 0}
+func g2() (u float32, s string) { return }
+func gs() []byte { return nil }
+
+func _calls() {
+ var x int
+ var y float32
+ var s []int
+
+ f0()
+ _ = f0 /* ERROR "used as value" */ ()
+ f0(g0 /* ERROR "too many arguments" */ )
+
+ f1(0)
+ f1(x)
+ f1(10.0)
+ f1 /* ERROR "too few arguments" */ ()
+ f1(x, y /* ERROR "too many arguments" */ )
+ f1(s /* ERROR "cannot assign" */ )
+ f1(x ... /* ERROR "cannot use ..." */ )
+ f1(g0 /* ERROR "used as value" */ ())
+ f1(g1())
+ // f1(g2()) // TODO(gri) missing position in error message
+
+ f2 /* ERROR "too few arguments" */ ()
+ f2 /* ERROR "too few arguments" */ (3.14)
+ f2(3.14, "foo")
+ f2(x /* ERROR "cannot assign" */ , "foo")
+ f2(g0 /* ERROR "used as value" */ ())
+ f2 /* ERROR "too few arguments" */ (g1 /* ERROR "cannot assign" */ ())
+ f2(g2())
+
+ fs /* ERROR "too few arguments" */ ()
+ fs(g0 /* ERROR "used as value" */ ())
+ fs(g1 /* ERROR "cannot assign" */ ())
+ // fs(g2()) // TODO(gri) missing position in error message
+ fs(gs())
+
+ fv()
+ fv(1, 2.0, x)
+ fv(s /* ERROR "cannot assign" */ )
+ fv(s...)
+ fv(1, s /* ERROR "can only use ... with matching parameter" */ ...)
+ fv(gs /* ERROR "cannot assign" */ ())
+ fv(gs /* ERROR "cannot assign" */ ()...)
+
+ fi()
+ fi(1, 2.0, x, 3.14, "foo")
+ fi(g2())
+ fi(0, g2)
+ fi(0, g2 /* ERROR "2-valued expression" */ ())
+}
diff --git a/src/pkg/go/types/testdata/stmt0.src b/src/pkg/go/types/testdata/stmt0.src
new file mode 100644
index 000000000..9d85de3bb
--- /dev/null
+++ b/src/pkg/go/types/testdata/stmt0.src
@@ -0,0 +1,288 @@
+// 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.
+
+// statements
+
+package stmt0
+
+func _() {
+ b, i, f, c, s := false, 1, 1.0, 1i, "foo"
+ b = i /* ERROR "cannot assign" */
+ i = f /* ERROR "cannot assign" */
+ f = c /* ERROR "cannot assign" */
+ c = s /* ERROR "cannot assign" */
+ s = b /* ERROR "cannot assign" */
+
+ v0 /* ERROR "mismatch" */, v1, v2 := 1, 2, 3, 4
+
+ b = true
+
+ i += 1
+ i += "foo" /* ERROR "cannot convert.*int" */
+
+ f -= 1
+ f -= "foo" /* ERROR "cannot convert.*float64" */
+
+ c *= 1
+ c /= 0 /* ERROR "division by zero" */
+
+ s += "bar"
+ s += 1 /* ERROR "cannot convert.*string" */
+
+ var u64 uint64
+ u64 += 1<<u64
+
+ undeclared /* ERROR "undeclared" */ = 991
+}
+
+func incdecs() {
+ const c = 3.14
+ c /* ERROR "cannot assign" */ ++
+ s := "foo"
+ s /* ERROR "cannot convert" */ --
+ 3.14 /* ERROR "cannot assign" */ ++
+ var (
+ x int
+ y float32
+ z complex128
+ )
+ x++
+ y--
+ z++
+}
+
+func sends() {
+ var ch chan int
+ var rch <-chan int
+ var x int
+ x /* ERROR "cannot send" */ <- x
+ rch /* ERROR "cannot send" */ <- x
+ ch <- "foo" /* ERROR "cannot convert" */
+ ch <- x
+}
+
+func selects() {
+ select {}
+ var (
+ ch chan int
+ sc chan <- bool
+ x int
+ )
+ select {
+ case <-ch:
+ ch <- x
+ case t, ok := <-ch:
+ x = t
+ case <-sc /* ERROR "cannot receive from send-only channel" */ :
+ }
+ select {
+ default:
+ default /* ERROR "multiple defaults" */ :
+ }
+}
+
+func gos() {
+ go 1 /* ERROR "expected function/method call" */
+ go gos()
+ var c chan int
+ go close(c)
+ go len(c) // TODO(gri) this should not be legal
+}
+
+func defers() {
+ defer 1 /* ERROR "expected function/method call" */
+ defer defers()
+ var c chan int
+ defer close(c)
+ defer len(c) // TODO(gri) this should not be legal
+}
+
+func switches() {
+ var x int
+
+ switch x {
+ default:
+ default /* ERROR "multiple defaults" */ :
+ }
+
+ switch {
+ case 1 /* ERROR "cannot convert" */ :
+ }
+
+ switch int32(x) {
+ case 1, 2:
+ case x /* ERROR "cannot compare" */ :
+ }
+
+ switch x {
+ case 1 /* ERROR "overflows int" */ << 100:
+ }
+
+ switch x {
+ case 1:
+ case 1 /* ERROR "duplicate case" */ :
+ case 2, 3, 4:
+ case 1 /* ERROR "duplicate case" */ :
+ }
+
+ // TODO(gri) duplicate 64bit values that don't fit into an int64 are not yet detected
+ switch uint64(x) {
+ case 1<<64-1:
+ case 1<<64-1:
+ }
+}
+
+type I interface {
+ m()
+}
+
+type I2 interface {
+ m(int)
+}
+
+type T struct{}
+type T1 struct{}
+type T2 struct{}
+
+func (T) m() {}
+func (T2) m(int) {}
+
+func typeswitches() {
+ var i int
+ var x interface{}
+
+ switch x.(type) {}
+ switch (x /* ERROR "outside type switch" */ .(type)) {}
+
+ switch x.(type) {
+ default:
+ default /* ERROR "multiple defaults" */ :
+ }
+
+ switch x := x.(type) {}
+
+ switch x := x.(type) {
+ case int:
+ var y int = x
+ }
+
+ switch x := i /* ERROR "not an interface" */ .(type) {}
+
+ switch t := x.(type) {
+ case nil:
+ var v bool = t /* ERROR "cannot assign" */
+ case int:
+ var v int = t
+ case float32, complex64:
+ var v float32 = t /* ERROR "cannot assign" */
+ default:
+ var v float32 = t /* ERROR "cannot assign" */
+ }
+
+ var t I
+ switch t.(type) {
+ case T:
+ case T1 /* ERROR "missing method m" */ :
+ case T2 /* ERROR "wrong type for method m" */ :
+ case I2 /* ERROR "wrong type for method m" */ :
+ }
+}
+
+func typeswitch0() {
+ switch y := interface{}(nil).(type) {
+ case int:
+ // TODO(gri) y has the wrong type here (type-checking
+ // of captured variable is delayed)
+ // func() int { return y + 0 }()
+ }
+}
+
+func rangeloops() {
+ var (
+ x int
+ a [10]float32
+ b []string
+ p *[10]complex128
+ pp **[10]complex128
+ s string
+ m map[int]bool
+ c chan int
+ sc chan<- int
+ rc <-chan int
+ )
+
+ for _ = range x /* ERROR "cannot range over" */ {}
+ for i := range x /* ERROR "cannot range over" */ {}
+
+ for i := range a {
+ var ii int
+ ii = i
+ }
+ for i, x := range a {
+ var ii int
+ ii = i
+ var xx float64
+ xx = x /* ERROR "cannot assign" */
+ }
+ var ii int
+ var xx float32
+ for ii, xx := range a {}
+
+ for i := range b {
+ var ii int
+ ii = i
+ }
+ for i, x := range b {
+ var ii int
+ ii = i
+ var xx string
+ xx = x
+ }
+
+ for i := range s {
+ var ii int
+ ii = i
+ }
+ for i, x := range s {
+ var ii int
+ ii = i
+ var xx rune
+ xx = x
+ }
+
+ for _, x := range p {
+ var xx complex128
+ xx = x
+ }
+
+ for _, x := range pp /* ERROR "cannot range over" */ {}
+
+ for k := range m {
+ var kk int32
+ kk = k /* ERROR "cannot assign" */
+ }
+ for k, v := range m {
+ var kk int
+ kk = k
+ if v {}
+ }
+
+ for _, _ /* ERROR "only one iteration variable" */ = range c {}
+ for e := range c {
+ var ee int
+ ee = e
+ }
+ for _ = range sc /* ERROR "cannot range over send-only channel" */ {}
+ for _ = range rc {}
+
+ // constant strings
+ const cs = "foo"
+ for i, x := range cs {}
+ for i, x := range "" {
+ var ii int
+ ii = i
+ var xx rune
+ xx = x
+ }
+}
diff --git a/src/pkg/go/types/types.go b/src/pkg/go/types/types.go
new file mode 100644
index 000000000..2f2e579bd
--- /dev/null
+++ b/src/pkg/go/types/types.go
@@ -0,0 +1,236 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import "go/ast"
+
+// All types implement the Type interface.
+type Type interface {
+ String() string
+ aType()
+}
+
+// BasicKind describes the kind of basic type.
+type BasicKind int
+
+const (
+ Invalid BasicKind = iota // type is invalid
+
+ // predeclared types
+ Bool
+ Int
+ Int8
+ Int16
+ Int32
+ Int64
+ Uint
+ Uint8
+ Uint16
+ Uint32
+ Uint64
+ Uintptr
+ Float32
+ Float64
+ Complex64
+ Complex128
+ String
+ UnsafePointer
+
+ // types for untyped values
+ UntypedBool
+ UntypedInt
+ UntypedRune
+ UntypedFloat
+ UntypedComplex
+ UntypedString
+ UntypedNil
+
+ // aliases
+ Byte = Uint8
+ Rune = Int32
+)
+
+// BasicInfo is a set of flags describing properties of a basic type.
+type BasicInfo int
+
+// Properties of basic types.
+const (
+ IsBoolean BasicInfo = 1 << iota
+ IsInteger
+ IsUnsigned
+ IsFloat
+ IsComplex
+ IsString
+ IsUntyped
+
+ IsOrdered = IsInteger | IsFloat | IsString
+ IsNumeric = IsInteger | IsFloat | IsComplex
+ IsConstType = IsBoolean | IsNumeric | IsString
+)
+
+// A Basic represents a basic type.
+type Basic struct {
+ Kind BasicKind
+ Info BasicInfo
+ size int64 // use DefaultSizeof to get size
+ Name string
+}
+
+// An Array represents an array type [Len]Elt.
+type Array struct {
+ Len int64
+ Elt Type
+}
+
+// A Slice represents a slice type []Elt.
+type Slice struct {
+ Elt Type
+}
+
+// A QualifiedName is a name qualified with the package that declared the name.
+// Note: Pkg may be a fake package (no name, no scope) because the GC compiler's
+// export information doesn't provide full information in some cases.
+// TODO(gri): Should change Pkg to PkgPath since it's the only thing we care about.
+type QualifiedName struct {
+ Pkg *Package // nil only for predeclared error.Error (exported)
+ Name string // unqualified type name for anonymous fields
+}
+
+// IsSame reports whether p and q are the same.
+func (p QualifiedName) IsSame(q QualifiedName) bool {
+ // spec:
+ // "Two identifiers are different if they are spelled differently,
+ // or if they appear in different packages and are not exported.
+ // Otherwise, they are the same."
+ if p.Name != q.Name {
+ return false
+ }
+ // p.Name == q.Name
+ return ast.IsExported(p.Name) || p.Pkg.Path == q.Pkg.Path
+}
+
+// A Field represents a field of a struct.
+type Field struct {
+ QualifiedName
+ Type Type
+ Tag string
+ IsAnonymous bool
+}
+
+// A Struct represents a struct type struct{...}.
+type Struct struct {
+ Fields []*Field
+ offsets []int64 // field offsets in bytes, lazily computed
+}
+
+func (typ *Struct) fieldIndex(name QualifiedName) int {
+ for i, f := range typ.Fields {
+ if f.QualifiedName.IsSame(name) {
+ return i
+ }
+ }
+ return -1
+}
+
+// A Pointer represents a pointer type *Base.
+type Pointer struct {
+ Base Type
+}
+
+// A Result represents a (multi-value) function call result.
+type Result struct {
+ Values []*Var // Signature.Results of the function called
+}
+
+// A Signature represents a user-defined function type func(...) (...).
+type Signature struct {
+ Recv *Var // nil if not a method
+ Params []*Var // (incoming) parameters from left to right; or nil
+ Results []*Var // (outgoing) results from left to right; or nil
+ IsVariadic bool // true if the last parameter's type is of the form ...T
+}
+
+// builtinId is an id of a builtin function.
+type builtinId int
+
+// Predeclared builtin functions.
+const (
+ // Universe scope
+ _Append builtinId = iota
+ _Cap
+ _Close
+ _Complex
+ _Copy
+ _Delete
+ _Imag
+ _Len
+ _Make
+ _New
+ _Panic
+ _Print
+ _Println
+ _Real
+ _Recover
+
+ // Unsafe package
+ _Alignof
+ _Offsetof
+ _Sizeof
+
+ // Testing support
+ _Assert
+ _Trace
+)
+
+// A builtin represents the type of a built-in function.
+type builtin struct {
+ id builtinId
+ name string
+ nargs int // number of arguments (minimum if variadic)
+ isVariadic bool
+ isStatement bool // true if the built-in is valid as an expression statement
+}
+
+// A Method represents a method.
+type Method struct {
+ QualifiedName
+ Type *Signature
+}
+
+// An Interface represents an interface type interface{...}.
+type Interface struct {
+ Methods []*Method // TODO(gri) consider keeping them in sorted order
+}
+
+// A Map represents a map type map[Key]Elt.
+type Map struct {
+ Key, Elt Type
+}
+
+// A Chan represents a channel type chan Elt, <-chan Elt, or chan<-Elt.
+type Chan struct {
+ Dir ast.ChanDir
+ Elt Type
+}
+
+// A NamedType represents a named type as declared in a type declaration.
+type NamedType struct {
+ Obj *TypeName // corresponding declared object
+ Underlying Type // nil if not fully declared yet; never a *NamedType
+ Methods []*Method // TODO(gri) consider keeping them in sorted order
+}
+
+func (*Basic) aType() {}
+func (*Array) aType() {}
+func (*Slice) aType() {}
+func (*Struct) aType() {}
+func (*Pointer) aType() {}
+func (*Result) aType() {}
+func (*Signature) aType() {}
+func (*builtin) aType() {}
+func (*Interface) aType() {}
+func (*Map) aType() {}
+func (*Chan) aType() {}
+func (*NamedType) aType() {}
diff --git a/src/pkg/go/types/types_test.go b/src/pkg/go/types/types_test.go
new file mode 100644
index 000000000..8e228fa67
--- /dev/null
+++ b/src/pkg/go/types/types_test.go
@@ -0,0 +1,171 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains tests verifying the types associated with an AST after
+// type checking.
+
+package types
+
+import (
+ "go/ast"
+ "go/parser"
+ "testing"
+)
+
+const filename = "<src>"
+
+func makePkg(t *testing.T, src string) (*Package, error) {
+ file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
+ if err != nil {
+ return nil, err
+ }
+ pkg, err := Check(fset, []*ast.File{file})
+ return pkg, err
+}
+
+type testEntry struct {
+ src, str string
+}
+
+// dup returns a testEntry where both src and str are the same.
+func dup(s string) testEntry {
+ return testEntry{s, s}
+}
+
+var testTypes = []testEntry{
+ // basic types
+ dup("int"),
+ dup("float32"),
+ dup("string"),
+
+ // arrays
+ dup("[10]int"),
+
+ // slices
+ dup("[]int"),
+ dup("[][]int"),
+
+ // structs
+ dup("struct{}"),
+ dup("struct{x int}"),
+ {`struct {
+ x, y int
+ z float32 "foo"
+ }`, `struct{x int; y int; z float32 "foo"}`},
+ {`struct {
+ string
+ elems []T
+ }`, `struct{string; elems []T}`},
+
+ // pointers
+ dup("*int"),
+ dup("***struct{}"),
+ dup("*struct{a int; b float32}"),
+
+ // functions
+ dup("func()"),
+ dup("func(x int)"),
+ {"func(x, y int)", "func(x int, y int)"},
+ {"func(x, y int, z string)", "func(x int, y int, z string)"},
+ dup("func(int)"),
+ {"func(int, string, byte)", "func(int, string, byte)"},
+
+ dup("func() int"),
+ {"func() (string)", "func() string"},
+ dup("func() (u int)"),
+ {"func() (u, v int, w string)", "func() (u int, v int, w string)"},
+
+ dup("func(int) string"),
+ dup("func(x int) string"),
+ dup("func(x int) (u string)"),
+ {"func(x, y int) (u string)", "func(x int, y int) (u string)"},
+
+ dup("func(...int) string"),
+ dup("func(x ...int) string"),
+ dup("func(x ...int) (u string)"),
+ {"func(x, y ...int) (u string)", "func(x int, y ...int) (u string)"},
+
+ // interfaces
+ dup("interface{}"),
+ dup("interface{m()}"),
+ dup(`interface{m(int) float32; String() string}`),
+ // TODO(gri) add test for interface w/ anonymous field
+
+ // maps
+ dup("map[string]int"),
+ {"map[struct{x, y int}][]byte", "map[struct{x int; y int}][]byte"},
+
+ // channels
+ dup("chan int"),
+ dup("chan<- func()"),
+ dup("<-chan []func() int"),
+}
+
+func TestTypes(t *testing.T) {
+ for _, test := range testTypes {
+ src := "package p; type T " + test.src
+ pkg, err := makePkg(t, src)
+ if err != nil {
+ t.Errorf("%s: %s", src, err)
+ continue
+ }
+ typ := underlying(pkg.Scope.Lookup("T").GetType())
+ str := typeString(typ)
+ if str != test.str {
+ t.Errorf("%s: got %s, want %s", test.src, str, test.str)
+ }
+ }
+}
+
+var testExprs = []testEntry{
+ // basic type literals
+ dup("x"),
+ dup("true"),
+ dup("42"),
+ dup("3.1415"),
+ dup("2.71828i"),
+ dup(`'a'`),
+ dup(`"foo"`),
+ dup("`bar`"),
+
+ // arbitrary expressions
+ dup("&x"),
+ dup("*&x"),
+ dup("(x)"),
+ dup("x + y"),
+ dup("x + y * 10"),
+ dup("t.foo"),
+ dup("s[0]"),
+ dup("s[x:y]"),
+ dup("s[:y]"),
+ dup("s[x:]"),
+ dup("s[:]"),
+ dup("f(1, 2.3)"),
+ dup("-f(10, 20)"),
+ dup("f(x + y, +3.1415)"),
+ {"func(a, b int) {}", "(func literal)"},
+ {"func(a, b int) []int {}(1, 2)[x]", "(func literal)(1, 2)[x]"},
+ {"[]int{1, 2, 3}", "(composite literal)"},
+ {"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"},
+ {"i.([]string)", "i.(...)"},
+}
+
+func TestExprs(t *testing.T) {
+ for _, test := range testExprs {
+ src := "package p; var _ = " + test.src + "; var (x, y int; s []string; f func(int, float32) int; i interface{}; t interface { foo() })"
+ file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
+ if err != nil {
+ t.Errorf("%s: %s", src, err)
+ continue
+ }
+ // TODO(gri) writing the code below w/o the decl variable will
+ // cause a 386 compiler error (out of fixed registers)
+ decl := file.Decls[0].(*ast.GenDecl)
+ expr := decl.Specs[0].(*ast.ValueSpec).Values[0]
+ str := exprString(expr)
+ if str != test.str {
+ t.Errorf("%s: got %s, want %s", test.src, str, test.str)
+ }
+ }
+}
diff --git a/src/pkg/go/types/universe.go b/src/pkg/go/types/universe.go
new file mode 100644
index 000000000..b218525c1
--- /dev/null
+++ b/src/pkg/go/types/universe.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.
+
+// This file implements the universe and unsafe package scopes.
+
+package types
+
+import (
+ "go/ast"
+ "strings"
+)
+
+var (
+ Universe *Scope
+ Unsafe *Package
+ universeIota *Const
+)
+
+// Predeclared types, indexed by BasicKind.
+var Typ = [...]*Basic{
+ Invalid: {Invalid, 0, 0, "invalid type"},
+
+ Bool: {Bool, IsBoolean, 1, "bool"},
+ Int: {Int, IsInteger, 0, "int"},
+ Int8: {Int8, IsInteger, 1, "int8"},
+ Int16: {Int16, IsInteger, 2, "int16"},
+ Int32: {Int32, IsInteger, 4, "int32"},
+ Int64: {Int64, IsInteger, 8, "int64"},
+ Uint: {Uint, IsInteger | IsUnsigned, 0, "uint"},
+ Uint8: {Uint8, IsInteger | IsUnsigned, 1, "uint8"},
+ Uint16: {Uint16, IsInteger | IsUnsigned, 2, "uint16"},
+ Uint32: {Uint32, IsInteger | IsUnsigned, 4, "uint32"},
+ Uint64: {Uint64, IsInteger | IsUnsigned, 8, "uint64"},
+ Uintptr: {Uintptr, IsInteger | IsUnsigned, 0, "uintptr"},
+ Float32: {Float32, IsFloat, 4, "float32"},
+ Float64: {Float64, IsFloat, 8, "float64"},
+ Complex64: {Complex64, IsComplex, 8, "complex64"},
+ Complex128: {Complex128, IsComplex, 16, "complex128"},
+ String: {String, IsString, 0, "string"},
+ UnsafePointer: {UnsafePointer, 0, 0, "Pointer"},
+
+ UntypedBool: {UntypedBool, IsBoolean | IsUntyped, 0, "untyped boolean"},
+ UntypedInt: {UntypedInt, IsInteger | IsUntyped, 0, "untyped integer"},
+ UntypedRune: {UntypedRune, IsInteger | IsUntyped, 0, "untyped rune"},
+ UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, 0, "untyped float"},
+ UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, 0, "untyped complex"},
+ UntypedString: {UntypedString, IsString | IsUntyped, 0, "untyped string"},
+ UntypedNil: {UntypedNil, IsUntyped, 0, "untyped nil"},
+}
+
+var aliases = [...]*Basic{
+ {Byte, IsInteger | IsUnsigned, 1, "byte"},
+ {Rune, IsInteger, 4, "rune"},
+}
+
+var predeclaredConstants = [...]*Const{
+ {nil, "true", Typ[UntypedBool], true, nil},
+ {nil, "false", Typ[UntypedBool], false, nil},
+ {nil, "iota", Typ[UntypedInt], zeroConst, nil},
+ {nil, "nil", Typ[UntypedNil], nilConst, nil},
+}
+
+var predeclaredFunctions = [...]*builtin{
+ {_Append, "append", 1, true, false},
+ {_Cap, "cap", 1, false, false},
+ {_Close, "close", 1, false, true},
+ {_Complex, "complex", 2, false, false},
+ {_Copy, "copy", 2, false, true},
+ {_Delete, "delete", 2, false, true},
+ {_Imag, "imag", 1, false, false},
+ {_Len, "len", 1, false, false},
+ {_Make, "make", 1, true, false},
+ {_New, "new", 1, false, false},
+ {_Panic, "panic", 1, false, true},
+ {_Print, "print", 0, true, true},
+ {_Println, "println", 0, true, true},
+ {_Real, "real", 1, false, false},
+ {_Recover, "recover", 0, false, true},
+
+ {_Alignof, "Alignof", 1, false, false},
+ {_Offsetof, "Offsetof", 1, false, false},
+ {_Sizeof, "Sizeof", 1, false, false},
+}
+
+func init() {
+ Universe = new(Scope)
+ Unsafe = &Package{Name: "unsafe", Scope: new(Scope)}
+
+ // predeclared types
+ for _, t := range Typ {
+ def(&TypeName{Name: t.Name, Type: t})
+ }
+ for _, t := range aliases {
+ def(&TypeName{Name: t.Name, Type: t})
+ }
+
+ // error type
+ {
+ // Error has a nil package in its qualified name since it is in no package
+ err := &Method{QualifiedName{nil, "Error"}, &Signature{Results: []*Var{{Name: "", Type: Typ[String]}}}}
+ def(&TypeName{Name: "error", Type: &NamedType{Underlying: &Interface{Methods: []*Method{err}}}})
+ }
+
+ for _, c := range predeclaredConstants {
+ def(c)
+ }
+
+ for _, f := range predeclaredFunctions {
+ def(&Func{Name: f.name, Type: f})
+ }
+
+ universeIota = Universe.Lookup("iota").(*Const)
+}
+
+// Objects with names containing blanks are internal and not entered into
+// a scope. Objects with exported names are inserted in the unsafe package
+// scope; other objects are inserted in the universe scope.
+//
+func def(obj Object) {
+ name := obj.GetName()
+ if strings.Index(name, " ") >= 0 {
+ return // nothing to do
+ }
+ // fix Obj link for named types
+ if typ, ok := obj.GetType().(*NamedType); ok {
+ typ.Obj = obj.(*TypeName)
+ }
+ // exported identifiers go into package unsafe
+ scope := Universe
+ if ast.IsExported(name) {
+ scope = Unsafe.Scope
+ // set Pkg field
+ switch obj := obj.(type) {
+ case *TypeName:
+ obj.Pkg = Unsafe
+ case *Func:
+ obj.Pkg = Unsafe
+ default:
+ unreachable()
+ }
+ }
+ if scope.Insert(obj) != nil {
+ panic("internal error: double declaration")
+ }
+}
diff --git a/src/pkg/hash/adler32/adler32.go b/src/pkg/hash/adler32/adler32.go
index 64fe68c44..7c80796bf 100644
--- a/src/pkg/hash/adler32/adler32.go
+++ b/src/pkg/hash/adler32/adler32.go
@@ -3,7 +3,8 @@
// license that can be found in the LICENSE file.
// Package adler32 implements the Adler-32 checksum.
-// Defined in RFC 1950:
+//
+// It is defined in RFC 1950:
// Adler-32 is composed of two sums accumulated per byte: s1 is
// the sum of all bytes, s2 is the sum of all s1 values. Both sums
// are done modulo 65521. s1 is initialized to 1, s2 to zero. The
@@ -14,20 +15,22 @@ package adler32
import "hash"
const (
+ // mod is the largest prime that is less than 65536.
mod = 65521
+ // nmax is the largest n such that
+ // 255 * n * (n+1) / 2 + (n+1) * (mod-1) <= 2^32-1.
+ // It is mentioned in RFC 1950 (search for "5552").
+ nmax = 5552
)
// The size of an Adler-32 checksum in bytes.
const Size = 4
// digest represents the partial evaluation of a checksum.
-type digest struct {
- // invariant: (a < mod && b < mod) || a <= b
- // invariant: a + b + 255 <= 0xffffffff
- a, b uint32
-}
+// The low 16 bits are s1, the high 16 bits are s2.
+type digest uint32
-func (d *digest) Reset() { d.a, d.b = 1, 0 }
+func (d *digest) Reset() { *d = 1 }
// New returns a new hash.Hash32 computing the Adler-32 checksum.
func New() hash.Hash32 {
@@ -40,47 +43,36 @@ func (d *digest) Size() int { return Size }
func (d *digest) BlockSize() int { return 1 }
-// Add p to the running checksum a, b.
-func update(a, b uint32, p []byte) (aa, bb uint32) {
- for _, pi := range p {
- a += uint32(pi)
- b += a
- // invariant: a <= b
- if b > (0xffffffff-255)/2 {
- a %= mod
- b %= mod
- // invariant: a < mod && b < mod
- } else {
- // invariant: a + b + 255 <= 2 * b + 255 <= 0xffffffff
+// Add p to the running checksum d.
+func update(d digest, p []byte) digest {
+ s1, s2 := uint32(d&0xffff), uint32(d>>16)
+ for len(p) > 0 {
+ var q []byte
+ if len(p) > nmax {
+ p, q = p[:nmax], p[nmax:]
}
+ for _, x := range p {
+ s1 += uint32(x)
+ s2 += s1
+ }
+ s1 %= mod
+ s2 %= mod
+ p = q
}
- return a, b
-}
-
-// Return the 32-bit checksum corresponding to a, b.
-func finish(a, b uint32) uint32 {
- if b >= mod {
- a %= mod
- b %= mod
- }
- return b<<16 | a
+ return digest(s2<<16 | s1)
}
func (d *digest) Write(p []byte) (nn int, err error) {
- d.a, d.b = update(d.a, d.b, p)
+ *d = update(*d, p)
return len(p), nil
}
-func (d *digest) Sum32() uint32 { return finish(d.a, d.b) }
+func (d *digest) Sum32() uint32 { return uint32(*d) }
func (d *digest) Sum(in []byte) []byte {
- s := d.Sum32()
- in = append(in, byte(s>>24))
- in = append(in, byte(s>>16))
- in = append(in, byte(s>>8))
- in = append(in, byte(s))
- return in
+ s := uint32(*d)
+ return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s))
}
// Checksum returns the Adler-32 checksum of data.
-func Checksum(data []byte) uint32 { return finish(update(1, 0, data)) }
+func Checksum(data []byte) uint32 { return uint32(update(1, data)) }
diff --git a/src/pkg/hash/adler32/adler32_test.go b/src/pkg/hash/adler32/adler32_test.go
index 01f931c68..0e9c938d8 100644
--- a/src/pkg/hash/adler32/adler32_test.go
+++ b/src/pkg/hash/adler32/adler32_test.go
@@ -5,26 +5,23 @@
package adler32
import (
- "bytes"
- "io"
+ "strings"
"testing"
)
-type _Adler32Test struct {
+var golden = []struct {
out uint32
in string
-}
-
-var golden = []_Adler32Test{
- {0x1, ""},
- {0x620062, "a"},
- {0x12600c4, "ab"},
- {0x24d0127, "abc"},
- {0x3d8018b, "abcd"},
- {0x5c801f0, "abcde"},
- {0x81e0256, "abcdef"},
- {0xadb02bd, "abcdefg"},
- {0xe000325, "abcdefgh"},
+}{
+ {0x00000001, ""},
+ {0x00620062, "a"},
+ {0x012600c4, "ab"},
+ {0x024d0127, "abc"},
+ {0x03d8018b, "abcd"},
+ {0x05c801f0, "abcde"},
+ {0x081e0256, "abcdef"},
+ {0x0adb02bd, "abcdefg"},
+ {0x0e000325, "abcdefgh"},
{0x118e038e, "abcdefghi"},
{0x158603f8, "abcdefghij"},
{0x3f090f02, "Discard medicine more than two years old."},
@@ -48,30 +45,61 @@ var golden = []_Adler32Test{
{0x91dd304f, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"},
{0x2e5d1316, "How can you write a big system without C++? -Paul Glick"},
{0xd0201df6, "'Invariant assertions' is the most elegant programming technique! -Tom Szymanski"},
+ {0x211297c8, strings.Repeat("\xff", 5548) + "8"},
+ {0xbaa198c8, strings.Repeat("\xff", 5549) + "9"},
+ {0x553499be, strings.Repeat("\xff", 5550) + "0"},
+ {0xf0c19abe, strings.Repeat("\xff", 5551) + "1"},
+ {0x8d5c9bbe, strings.Repeat("\xff", 5552) + "2"},
+ {0x2af69cbe, strings.Repeat("\xff", 5553) + "3"},
+ {0xc9809dbe, strings.Repeat("\xff", 5554) + "4"},
+ {0x69189ebe, strings.Repeat("\xff", 5555) + "5"},
+ {0x86af0001, strings.Repeat("\x00", 1e5)},
+ {0x79660b4d, strings.Repeat("a", 1e5)},
+ {0x110588ee, strings.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1e4)},
+}
+
+// checksum is a slow but simple implementation of the Adler-32 checksum.
+// It is a straight port of the sample code in RFC 1950 section 9.
+func checksum(p []byte) uint32 {
+ s1, s2 := uint32(1), uint32(0)
+ for _, x := range p {
+ s1 = (s1 + uint32(x)) % mod
+ s2 = (s2 + s1) % mod
+ }
+ return s2<<16 | s1
}
func TestGolden(t *testing.T) {
- for i := 0; i < len(golden); i++ {
- g := golden[i]
- c := New()
- io.WriteString(c, g.in)
- s := c.Sum32()
- if s != g.out {
- t.Errorf("adler32(%s) = 0x%x want 0x%x", g.in, s, g.out)
- t.FailNow()
+ for _, g := range golden {
+ in := g.in
+ if len(in) > 220 {
+ in = in[:100] + "..." + in[len(in)-100:]
+ }
+ p := []byte(g.in)
+ if got := checksum(p); got != g.out {
+ t.Errorf("simple implementation: checksum(%q) = 0x%x want 0x%x", in, got, g.out)
+ continue
+ }
+ if got := Checksum(p); got != g.out {
+ t.Errorf("optimized implementation: Checksum(%q) = 0x%x want 0x%x", in, got, g.out)
+ continue
}
}
}
-func BenchmarkGolden(b *testing.B) {
- b.StopTimer()
- c := New()
- var buf bytes.Buffer
- for _, g := range golden {
- buf.Write([]byte(g.in))
+func BenchmarkAdler32KB(b *testing.B) {
+ b.SetBytes(1024)
+ data := make([]byte, 1024)
+ for i := range data {
+ data[i] = byte(i)
}
- b.StartTimer()
+ h := New()
+ in := make([]byte, 0, h.Size())
+
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
- c.Write(buf.Bytes())
+ h.Reset()
+ h.Write(data)
+ h.Sum(in)
}
}
diff --git a/src/pkg/hash/crc32/crc32.go b/src/pkg/hash/crc32/crc32.go
index 236d77872..a2a21a06f 100644
--- a/src/pkg/hash/crc32/crc32.go
+++ b/src/pkg/hash/crc32/crc32.go
@@ -123,11 +123,7 @@ func (d *digest) Sum32() uint32 { return d.crc }
func (d *digest) Sum(in []byte) []byte {
s := d.Sum32()
- in = append(in, byte(s>>24))
- in = append(in, byte(s>>16))
- in = append(in, byte(s>>8))
- in = append(in, byte(s))
- return in
+ return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s))
}
// Checksum returns the CRC-32 checksum of data
diff --git a/src/pkg/hash/crc32/crc32_amd64.go b/src/pkg/hash/crc32/crc32_amd64.go
index 83349bc6c..b5bc6d3cf 100644
--- a/src/pkg/hash/crc32/crc32_amd64.go
+++ b/src/pkg/hash/crc32/crc32_amd64.go
@@ -13,7 +13,7 @@ func haveSSE42() bool
// castagnoliSSE42 is defined in crc_amd64.s and uses the SSE4.2 CRC32
// instruction.
-func castagnoliSSE42(uint32, []byte) uint32
+func castagnoliSSE42(crc uint32, p []byte) uint32
var sse42 = haveSSE42()
diff --git a/src/pkg/hash/crc32/crc32_amd64.s b/src/pkg/hash/crc32/crc32_amd64.s
index a9e5317e1..6e6a364ee 100644
--- a/src/pkg/hash/crc32/crc32_amd64.s
+++ b/src/pkg/hash/crc32/crc32_amd64.s
@@ -6,12 +6,12 @@
TEXT ·castagnoliSSE42(SB),7,$0
MOVL crc+0(FP), AX // CRC value
MOVQ p+8(FP), SI // data pointer
- MOVL p+16(FP), CX // len(p)
+ MOVQ p+16(FP), CX // len(p)
NOTL AX
/* If there's less than 8 bytes to process, we do it byte-by-byte. */
- CMPL CX, $8
+ CMPQ CX, $8
JL cleanup
/* Process individual bytes until the input is 8-byte aligned. */
@@ -21,13 +21,13 @@ startup:
JZ aligned
CRC32B (SI), AX
- DECL CX
+ DECQ CX
INCQ SI
JMP startup
aligned:
/* The input is now 8-byte aligned and we can process 8-byte chunks. */
- CMPL CX, $8
+ CMPQ CX, $8
JL cleanup
CRC32Q (SI), AX
@@ -37,7 +37,7 @@ aligned:
cleanup:
/* We may have some bytes left over that we process one at a time. */
- CMPL CX, $0
+ CMPQ CX, $0
JE done
CRC32B (SI), AX
@@ -47,7 +47,7 @@ cleanup:
done:
NOTL AX
- MOVL AX, ret+24(FP)
+ MOVL AX, r+32(FP)
RET
// func haveSSE42() bool
@@ -57,6 +57,6 @@ TEXT ·haveSSE42(SB),7,$0
CPUID
SHRQ $20, CX
ANDQ $1, CX
- MOVB CX, ret+0(FP)
+ MOVB CX, r+0(FP)
RET
diff --git a/src/pkg/hash/crc32/crc32_test.go b/src/pkg/hash/crc32/crc32_test.go
index 7e82dd755..75dc26e7c 100644
--- a/src/pkg/hash/crc32/crc32_test.go
+++ b/src/pkg/hash/crc32/crc32_test.go
@@ -82,16 +82,18 @@ func TestGolden(t *testing.T) {
}
func BenchmarkCrc32KB(b *testing.B) {
- b.StopTimer()
- data := make([]uint8, 1024)
- for i := 0; i < 1024; i++ {
- data[i] = uint8(i)
+ b.SetBytes(1024)
+ data := make([]byte, 1024)
+ for i := range data {
+ data[i] = byte(i)
}
- c := NewIEEE()
- b.StartTimer()
- b.SetBytes(int64(len(data)))
+ h := NewIEEE()
+ in := make([]byte, 0, h.Size())
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
- c.Write(data)
+ h.Reset()
+ h.Write(data)
+ h.Sum(in)
}
}
diff --git a/src/pkg/hash/crc64/crc64.go b/src/pkg/hash/crc64/crc64.go
index 5b64390f3..692586798 100644
--- a/src/pkg/hash/crc64/crc64.go
+++ b/src/pkg/hash/crc64/crc64.go
@@ -79,15 +79,7 @@ func (d *digest) Sum64() uint64 { return d.crc }
func (d *digest) Sum(in []byte) []byte {
s := d.Sum64()
- in = append(in, byte(s>>56))
- in = append(in, byte(s>>48))
- in = append(in, byte(s>>40))
- in = append(in, byte(s>>32))
- in = append(in, byte(s>>24))
- in = append(in, byte(s>>16))
- in = append(in, byte(s>>8))
- in = append(in, byte(s))
- return in
+ return append(in, byte(s>>56), byte(s>>48), byte(s>>40), byte(s>>32), byte(s>>24), byte(s>>16), byte(s>>8), byte(s))
}
// Checksum returns the CRC-64 checksum of data
diff --git a/src/pkg/hash/crc64/crc64_test.go b/src/pkg/hash/crc64/crc64_test.go
index e932524e0..81a87b56e 100644
--- a/src/pkg/hash/crc64/crc64_test.go
+++ b/src/pkg/hash/crc64/crc64_test.go
@@ -64,15 +64,18 @@ func TestGolden(t *testing.T) {
}
func BenchmarkCrc64KB(b *testing.B) {
- b.StopTimer()
- data := make([]uint8, 1024)
- for i := 0; i < 1024; i++ {
- data[i] = uint8(i)
+ b.SetBytes(1024)
+ data := make([]byte, 1024)
+ for i := range data {
+ data[i] = byte(i)
}
- c := New(tab)
- b.StartTimer()
+ h := New(tab)
+ in := make([]byte, 0, h.Size())
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
- c.Write(data)
+ h.Reset()
+ h.Write(data)
+ h.Sum(in)
}
}
diff --git a/src/pkg/hash/fnv/fnv.go b/src/pkg/hash/fnv/fnv.go
index ea5019818..b5ecd4a7c 100644
--- a/src/pkg/hash/fnv/fnv.go
+++ b/src/pkg/hash/fnv/fnv.go
@@ -111,44 +111,20 @@ func (s *sum64a) BlockSize() int { return 1 }
func (s *sum32) Sum(in []byte) []byte {
v := uint32(*s)
- in = append(in, byte(v>>24))
- in = append(in, byte(v>>16))
- in = append(in, byte(v>>8))
- in = append(in, byte(v))
- return in
+ return append(in, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}
func (s *sum32a) Sum(in []byte) []byte {
v := uint32(*s)
- in = append(in, byte(v>>24))
- in = append(in, byte(v>>16))
- in = append(in, byte(v>>8))
- in = append(in, byte(v))
- return in
+ return append(in, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}
func (s *sum64) Sum(in []byte) []byte {
v := uint64(*s)
- in = append(in, byte(v>>56))
- in = append(in, byte(v>>48))
- in = append(in, byte(v>>40))
- in = append(in, byte(v>>32))
- in = append(in, byte(v>>24))
- in = append(in, byte(v>>16))
- in = append(in, byte(v>>8))
- in = append(in, byte(v))
- return in
+ return append(in, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}
func (s *sum64a) Sum(in []byte) []byte {
v := uint64(*s)
- in = append(in, byte(v>>56))
- in = append(in, byte(v>>48))
- in = append(in, byte(v>>40))
- in = append(in, byte(v>>32))
- in = append(in, byte(v>>24))
- in = append(in, byte(v>>16))
- in = append(in, byte(v>>8))
- in = append(in, byte(v))
- return in
+ return append(in, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}
diff --git a/src/pkg/hash/fnv/fnv_test.go b/src/pkg/hash/fnv/fnv_test.go
index 17454deda..89d39b38a 100644
--- a/src/pkg/hash/fnv/fnv_test.go
+++ b/src/pkg/hash/fnv/fnv_test.go
@@ -11,8 +11,6 @@ import (
"testing"
)
-const testDataSize = 40
-
type golden struct {
sum []byte
text string
@@ -134,34 +132,34 @@ func testIntegrity(t *testing.T, h hash.Hash) {
}
}
-func Benchmark32(b *testing.B) {
- benchmark(b, New32())
+func BenchmarkFnv32KB(b *testing.B) {
+ benchmarkKB(b, New32())
}
-func Benchmark32a(b *testing.B) {
- benchmark(b, New32a())
+func BenchmarkFnv32aKB(b *testing.B) {
+ benchmarkKB(b, New32a())
}
-func Benchmark64(b *testing.B) {
- benchmark(b, New64())
+func BenchmarkFnv64KB(b *testing.B) {
+ benchmarkKB(b, New64())
}
-func Benchmark64a(b *testing.B) {
- benchmark(b, New64a())
+func BenchmarkFnv64aKB(b *testing.B) {
+ benchmarkKB(b, New64a())
}
-func benchmark(b *testing.B, h hash.Hash) {
- b.ResetTimer()
- b.SetBytes(testDataSize)
- data := make([]byte, testDataSize)
+func benchmarkKB(b *testing.B, h hash.Hash) {
+ b.SetBytes(1024)
+ data := make([]byte, 1024)
for i := range data {
- data[i] = byte(i + 'a')
+ data[i] = byte(i)
}
+ in := make([]byte, 0, h.Size())
- b.StartTimer()
- for todo := b.N; todo != 0; todo-- {
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
h.Reset()
h.Write(data)
- h.Sum(nil)
+ h.Sum(in)
}
}
diff --git a/src/pkg/html/entity.go b/src/pkg/html/entity.go
index bd8307523..af8a007ed 100644
--- a/src/pkg/html/entity.go
+++ b/src/pkg/html/entity.go
@@ -75,2083 +75,2083 @@ var entity = map[string]rune{
"Copf;": '\U00002102',
"Coproduct;": '\U00002210',
"CounterClockwiseContourIntegral;": '\U00002233',
- "Cross;": '\U00002A2F',
- "Cscr;": '\U0001D49E',
- "Cup;": '\U000022D3',
- "CupCap;": '\U0000224D',
- "DD;": '\U00002145',
- "DDotrahd;": '\U00002911',
- "DJcy;": '\U00000402',
- "DScy;": '\U00000405',
- "DZcy;": '\U0000040F',
- "Dagger;": '\U00002021',
- "Darr;": '\U000021A1',
- "Dashv;": '\U00002AE4',
- "Dcaron;": '\U0000010E',
- "Dcy;": '\U00000414',
- "Del;": '\U00002207',
- "Delta;": '\U00000394',
- "Dfr;": '\U0001D507',
- "DiacriticalAcute;": '\U000000B4',
- "DiacriticalDot;": '\U000002D9',
- "DiacriticalDoubleAcute;": '\U000002DD',
- "DiacriticalGrave;": '\U00000060',
- "DiacriticalTilde;": '\U000002DC',
- "Diamond;": '\U000022C4',
- "DifferentialD;": '\U00002146',
- "Dopf;": '\U0001D53B',
- "Dot;": '\U000000A8',
- "DotDot;": '\U000020DC',
- "DotEqual;": '\U00002250',
- "DoubleContourIntegral;": '\U0000222F',
- "DoubleDot;": '\U000000A8',
- "DoubleDownArrow;": '\U000021D3',
- "DoubleLeftArrow;": '\U000021D0',
- "DoubleLeftRightArrow;": '\U000021D4',
- "DoubleLeftTee;": '\U00002AE4',
- "DoubleLongLeftArrow;": '\U000027F8',
- "DoubleLongLeftRightArrow;": '\U000027FA',
- "DoubleLongRightArrow;": '\U000027F9',
- "DoubleRightArrow;": '\U000021D2',
- "DoubleRightTee;": '\U000022A8',
- "DoubleUpArrow;": '\U000021D1',
- "DoubleUpDownArrow;": '\U000021D5',
- "DoubleVerticalBar;": '\U00002225',
- "DownArrow;": '\U00002193',
- "DownArrowBar;": '\U00002913',
- "DownArrowUpArrow;": '\U000021F5',
- "DownBreve;": '\U00000311',
- "DownLeftRightVector;": '\U00002950',
- "DownLeftTeeVector;": '\U0000295E',
- "DownLeftVector;": '\U000021BD',
- "DownLeftVectorBar;": '\U00002956',
- "DownRightTeeVector;": '\U0000295F',
- "DownRightVector;": '\U000021C1',
- "DownRightVectorBar;": '\U00002957',
- "DownTee;": '\U000022A4',
- "DownTeeArrow;": '\U000021A7',
- "Downarrow;": '\U000021D3',
- "Dscr;": '\U0001D49F',
- "Dstrok;": '\U00000110',
- "ENG;": '\U0000014A',
- "ETH;": '\U000000D0',
- "Eacute;": '\U000000C9',
- "Ecaron;": '\U0000011A',
- "Ecirc;": '\U000000CA',
- "Ecy;": '\U0000042D',
- "Edot;": '\U00000116',
- "Efr;": '\U0001D508',
- "Egrave;": '\U000000C8',
- "Element;": '\U00002208',
- "Emacr;": '\U00000112',
- "EmptySmallSquare;": '\U000025FB',
- "EmptyVerySmallSquare;": '\U000025AB',
- "Eogon;": '\U00000118',
- "Eopf;": '\U0001D53C',
- "Epsilon;": '\U00000395',
- "Equal;": '\U00002A75',
- "EqualTilde;": '\U00002242',
- "Equilibrium;": '\U000021CC',
- "Escr;": '\U00002130',
- "Esim;": '\U00002A73',
- "Eta;": '\U00000397',
- "Euml;": '\U000000CB',
- "Exists;": '\U00002203',
- "ExponentialE;": '\U00002147',
- "Fcy;": '\U00000424',
- "Ffr;": '\U0001D509',
- "FilledSmallSquare;": '\U000025FC',
- "FilledVerySmallSquare;": '\U000025AA',
- "Fopf;": '\U0001D53D',
- "ForAll;": '\U00002200',
- "Fouriertrf;": '\U00002131',
- "Fscr;": '\U00002131',
- "GJcy;": '\U00000403',
- "GT;": '\U0000003E',
- "Gamma;": '\U00000393',
- "Gammad;": '\U000003DC',
- "Gbreve;": '\U0000011E',
- "Gcedil;": '\U00000122',
- "Gcirc;": '\U0000011C',
- "Gcy;": '\U00000413',
- "Gdot;": '\U00000120',
- "Gfr;": '\U0001D50A',
- "Gg;": '\U000022D9',
- "Gopf;": '\U0001D53E',
- "GreaterEqual;": '\U00002265',
- "GreaterEqualLess;": '\U000022DB',
- "GreaterFullEqual;": '\U00002267',
- "GreaterGreater;": '\U00002AA2',
- "GreaterLess;": '\U00002277',
- "GreaterSlantEqual;": '\U00002A7E',
- "GreaterTilde;": '\U00002273',
- "Gscr;": '\U0001D4A2',
- "Gt;": '\U0000226B',
- "HARDcy;": '\U0000042A',
- "Hacek;": '\U000002C7',
- "Hat;": '\U0000005E',
- "Hcirc;": '\U00000124',
- "Hfr;": '\U0000210C',
- "HilbertSpace;": '\U0000210B',
- "Hopf;": '\U0000210D',
- "HorizontalLine;": '\U00002500',
- "Hscr;": '\U0000210B',
- "Hstrok;": '\U00000126',
- "HumpDownHump;": '\U0000224E',
- "HumpEqual;": '\U0000224F',
- "IEcy;": '\U00000415',
- "IJlig;": '\U00000132',
- "IOcy;": '\U00000401',
- "Iacute;": '\U000000CD',
- "Icirc;": '\U000000CE',
- "Icy;": '\U00000418',
- "Idot;": '\U00000130',
- "Ifr;": '\U00002111',
- "Igrave;": '\U000000CC',
- "Im;": '\U00002111',
- "Imacr;": '\U0000012A',
- "ImaginaryI;": '\U00002148',
- "Implies;": '\U000021D2',
- "Int;": '\U0000222C',
- "Integral;": '\U0000222B',
- "Intersection;": '\U000022C2',
- "InvisibleComma;": '\U00002063',
- "InvisibleTimes;": '\U00002062',
- "Iogon;": '\U0000012E',
- "Iopf;": '\U0001D540',
- "Iota;": '\U00000399',
- "Iscr;": '\U00002110',
- "Itilde;": '\U00000128',
- "Iukcy;": '\U00000406',
- "Iuml;": '\U000000CF',
- "Jcirc;": '\U00000134',
- "Jcy;": '\U00000419',
- "Jfr;": '\U0001D50D',
- "Jopf;": '\U0001D541',
- "Jscr;": '\U0001D4A5',
- "Jsercy;": '\U00000408',
- "Jukcy;": '\U00000404',
- "KHcy;": '\U00000425',
- "KJcy;": '\U0000040C',
- "Kappa;": '\U0000039A',
- "Kcedil;": '\U00000136',
- "Kcy;": '\U0000041A',
- "Kfr;": '\U0001D50E',
- "Kopf;": '\U0001D542',
- "Kscr;": '\U0001D4A6',
- "LJcy;": '\U00000409',
- "LT;": '\U0000003C',
- "Lacute;": '\U00000139',
- "Lambda;": '\U0000039B',
- "Lang;": '\U000027EA',
- "Laplacetrf;": '\U00002112',
- "Larr;": '\U0000219E',
- "Lcaron;": '\U0000013D',
- "Lcedil;": '\U0000013B',
- "Lcy;": '\U0000041B',
- "LeftAngleBracket;": '\U000027E8',
- "LeftArrow;": '\U00002190',
- "LeftArrowBar;": '\U000021E4',
- "LeftArrowRightArrow;": '\U000021C6',
- "LeftCeiling;": '\U00002308',
- "LeftDoubleBracket;": '\U000027E6',
- "LeftDownTeeVector;": '\U00002961',
- "LeftDownVector;": '\U000021C3',
- "LeftDownVectorBar;": '\U00002959',
- "LeftFloor;": '\U0000230A',
- "LeftRightArrow;": '\U00002194',
- "LeftRightVector;": '\U0000294E',
- "LeftTee;": '\U000022A3',
- "LeftTeeArrow;": '\U000021A4',
- "LeftTeeVector;": '\U0000295A',
- "LeftTriangle;": '\U000022B2',
- "LeftTriangleBar;": '\U000029CF',
- "LeftTriangleEqual;": '\U000022B4',
- "LeftUpDownVector;": '\U00002951',
- "LeftUpTeeVector;": '\U00002960',
- "LeftUpVector;": '\U000021BF',
- "LeftUpVectorBar;": '\U00002958',
- "LeftVector;": '\U000021BC',
- "LeftVectorBar;": '\U00002952',
- "Leftarrow;": '\U000021D0',
- "Leftrightarrow;": '\U000021D4',
- "LessEqualGreater;": '\U000022DA',
- "LessFullEqual;": '\U00002266',
- "LessGreater;": '\U00002276',
- "LessLess;": '\U00002AA1',
- "LessSlantEqual;": '\U00002A7D',
- "LessTilde;": '\U00002272',
- "Lfr;": '\U0001D50F',
- "Ll;": '\U000022D8',
- "Lleftarrow;": '\U000021DA',
- "Lmidot;": '\U0000013F',
- "LongLeftArrow;": '\U000027F5',
- "LongLeftRightArrow;": '\U000027F7',
- "LongRightArrow;": '\U000027F6',
- "Longleftarrow;": '\U000027F8',
- "Longleftrightarrow;": '\U000027FA',
- "Longrightarrow;": '\U000027F9',
- "Lopf;": '\U0001D543',
- "LowerLeftArrow;": '\U00002199',
- "LowerRightArrow;": '\U00002198',
- "Lscr;": '\U00002112',
- "Lsh;": '\U000021B0',
- "Lstrok;": '\U00000141',
- "Lt;": '\U0000226A',
- "Map;": '\U00002905',
- "Mcy;": '\U0000041C',
- "MediumSpace;": '\U0000205F',
- "Mellintrf;": '\U00002133',
- "Mfr;": '\U0001D510',
- "MinusPlus;": '\U00002213',
- "Mopf;": '\U0001D544',
- "Mscr;": '\U00002133',
- "Mu;": '\U0000039C',
- "NJcy;": '\U0000040A',
- "Nacute;": '\U00000143',
- "Ncaron;": '\U00000147',
- "Ncedil;": '\U00000145',
- "Ncy;": '\U0000041D',
- "NegativeMediumSpace;": '\U0000200B',
- "NegativeThickSpace;": '\U0000200B',
- "NegativeThinSpace;": '\U0000200B',
- "NegativeVeryThinSpace;": '\U0000200B',
- "NestedGreaterGreater;": '\U0000226B',
- "NestedLessLess;": '\U0000226A',
- "NewLine;": '\U0000000A',
- "Nfr;": '\U0001D511',
- "NoBreak;": '\U00002060',
- "NonBreakingSpace;": '\U000000A0',
- "Nopf;": '\U00002115',
- "Not;": '\U00002AEC',
- "NotCongruent;": '\U00002262',
- "NotCupCap;": '\U0000226D',
- "NotDoubleVerticalBar;": '\U00002226',
- "NotElement;": '\U00002209',
- "NotEqual;": '\U00002260',
- "NotExists;": '\U00002204',
- "NotGreater;": '\U0000226F',
- "NotGreaterEqual;": '\U00002271',
- "NotGreaterLess;": '\U00002279',
- "NotGreaterTilde;": '\U00002275',
- "NotLeftTriangle;": '\U000022EA',
- "NotLeftTriangleEqual;": '\U000022EC',
- "NotLess;": '\U0000226E',
- "NotLessEqual;": '\U00002270',
- "NotLessGreater;": '\U00002278',
- "NotLessTilde;": '\U00002274',
- "NotPrecedes;": '\U00002280',
- "NotPrecedesSlantEqual;": '\U000022E0',
- "NotReverseElement;": '\U0000220C',
- "NotRightTriangle;": '\U000022EB',
- "NotRightTriangleEqual;": '\U000022ED',
- "NotSquareSubsetEqual;": '\U000022E2',
- "NotSquareSupersetEqual;": '\U000022E3',
- "NotSubsetEqual;": '\U00002288',
- "NotSucceeds;": '\U00002281',
- "NotSucceedsSlantEqual;": '\U000022E1',
- "NotSupersetEqual;": '\U00002289',
- "NotTilde;": '\U00002241',
- "NotTildeEqual;": '\U00002244',
- "NotTildeFullEqual;": '\U00002247',
- "NotTildeTilde;": '\U00002249',
- "NotVerticalBar;": '\U00002224',
- "Nscr;": '\U0001D4A9',
- "Ntilde;": '\U000000D1',
- "Nu;": '\U0000039D',
- "OElig;": '\U00000152',
- "Oacute;": '\U000000D3',
- "Ocirc;": '\U000000D4',
- "Ocy;": '\U0000041E',
- "Odblac;": '\U00000150',
- "Ofr;": '\U0001D512',
- "Ograve;": '\U000000D2',
- "Omacr;": '\U0000014C',
- "Omega;": '\U000003A9',
- "Omicron;": '\U0000039F',
- "Oopf;": '\U0001D546',
- "OpenCurlyDoubleQuote;": '\U0000201C',
- "OpenCurlyQuote;": '\U00002018',
- "Or;": '\U00002A54',
- "Oscr;": '\U0001D4AA',
- "Oslash;": '\U000000D8',
- "Otilde;": '\U000000D5',
- "Otimes;": '\U00002A37',
- "Ouml;": '\U000000D6',
- "OverBar;": '\U0000203E',
- "OverBrace;": '\U000023DE',
- "OverBracket;": '\U000023B4',
- "OverParenthesis;": '\U000023DC',
- "PartialD;": '\U00002202',
- "Pcy;": '\U0000041F',
- "Pfr;": '\U0001D513',
- "Phi;": '\U000003A6',
- "Pi;": '\U000003A0',
- "PlusMinus;": '\U000000B1',
- "Poincareplane;": '\U0000210C',
- "Popf;": '\U00002119',
- "Pr;": '\U00002ABB',
- "Precedes;": '\U0000227A',
- "PrecedesEqual;": '\U00002AAF',
- "PrecedesSlantEqual;": '\U0000227C',
- "PrecedesTilde;": '\U0000227E',
- "Prime;": '\U00002033',
- "Product;": '\U0000220F',
- "Proportion;": '\U00002237',
- "Proportional;": '\U0000221D',
- "Pscr;": '\U0001D4AB',
- "Psi;": '\U000003A8',
- "QUOT;": '\U00000022',
- "Qfr;": '\U0001D514',
- "Qopf;": '\U0000211A',
- "Qscr;": '\U0001D4AC',
- "RBarr;": '\U00002910',
- "REG;": '\U000000AE',
- "Racute;": '\U00000154',
- "Rang;": '\U000027EB',
- "Rarr;": '\U000021A0',
- "Rarrtl;": '\U00002916',
- "Rcaron;": '\U00000158',
- "Rcedil;": '\U00000156',
- "Rcy;": '\U00000420',
- "Re;": '\U0000211C',
- "ReverseElement;": '\U0000220B',
- "ReverseEquilibrium;": '\U000021CB',
- "ReverseUpEquilibrium;": '\U0000296F',
- "Rfr;": '\U0000211C',
- "Rho;": '\U000003A1',
- "RightAngleBracket;": '\U000027E9',
- "RightArrow;": '\U00002192',
- "RightArrowBar;": '\U000021E5',
- "RightArrowLeftArrow;": '\U000021C4',
- "RightCeiling;": '\U00002309',
- "RightDoubleBracket;": '\U000027E7',
- "RightDownTeeVector;": '\U0000295D',
- "RightDownVector;": '\U000021C2',
- "RightDownVectorBar;": '\U00002955',
- "RightFloor;": '\U0000230B',
- "RightTee;": '\U000022A2',
- "RightTeeArrow;": '\U000021A6',
- "RightTeeVector;": '\U0000295B',
- "RightTriangle;": '\U000022B3',
- "RightTriangleBar;": '\U000029D0',
- "RightTriangleEqual;": '\U000022B5',
- "RightUpDownVector;": '\U0000294F',
- "RightUpTeeVector;": '\U0000295C',
- "RightUpVector;": '\U000021BE',
- "RightUpVectorBar;": '\U00002954',
- "RightVector;": '\U000021C0',
- "RightVectorBar;": '\U00002953',
- "Rightarrow;": '\U000021D2',
- "Ropf;": '\U0000211D',
- "RoundImplies;": '\U00002970',
- "Rrightarrow;": '\U000021DB',
- "Rscr;": '\U0000211B',
- "Rsh;": '\U000021B1',
- "RuleDelayed;": '\U000029F4',
- "SHCHcy;": '\U00000429',
- "SHcy;": '\U00000428',
- "SOFTcy;": '\U0000042C',
- "Sacute;": '\U0000015A',
- "Sc;": '\U00002ABC',
- "Scaron;": '\U00000160',
- "Scedil;": '\U0000015E',
- "Scirc;": '\U0000015C',
- "Scy;": '\U00000421',
- "Sfr;": '\U0001D516',
- "ShortDownArrow;": '\U00002193',
- "ShortLeftArrow;": '\U00002190',
- "ShortRightArrow;": '\U00002192',
- "ShortUpArrow;": '\U00002191',
- "Sigma;": '\U000003A3',
- "SmallCircle;": '\U00002218',
- "Sopf;": '\U0001D54A',
- "Sqrt;": '\U0000221A',
- "Square;": '\U000025A1',
- "SquareIntersection;": '\U00002293',
- "SquareSubset;": '\U0000228F',
- "SquareSubsetEqual;": '\U00002291',
- "SquareSuperset;": '\U00002290',
- "SquareSupersetEqual;": '\U00002292',
- "SquareUnion;": '\U00002294',
- "Sscr;": '\U0001D4AE',
- "Star;": '\U000022C6',
- "Sub;": '\U000022D0',
- "Subset;": '\U000022D0',
- "SubsetEqual;": '\U00002286',
- "Succeeds;": '\U0000227B',
- "SucceedsEqual;": '\U00002AB0',
- "SucceedsSlantEqual;": '\U0000227D',
- "SucceedsTilde;": '\U0000227F',
- "SuchThat;": '\U0000220B',
- "Sum;": '\U00002211',
- "Sup;": '\U000022D1',
- "Superset;": '\U00002283',
- "SupersetEqual;": '\U00002287',
- "Supset;": '\U000022D1',
- "THORN;": '\U000000DE',
- "TRADE;": '\U00002122',
- "TSHcy;": '\U0000040B',
- "TScy;": '\U00000426',
- "Tab;": '\U00000009',
- "Tau;": '\U000003A4',
- "Tcaron;": '\U00000164',
- "Tcedil;": '\U00000162',
- "Tcy;": '\U00000422',
- "Tfr;": '\U0001D517',
- "Therefore;": '\U00002234',
- "Theta;": '\U00000398',
- "ThinSpace;": '\U00002009',
- "Tilde;": '\U0000223C',
- "TildeEqual;": '\U00002243',
- "TildeFullEqual;": '\U00002245',
- "TildeTilde;": '\U00002248',
- "Topf;": '\U0001D54B',
- "TripleDot;": '\U000020DB',
- "Tscr;": '\U0001D4AF',
- "Tstrok;": '\U00000166',
- "Uacute;": '\U000000DA',
- "Uarr;": '\U0000219F',
- "Uarrocir;": '\U00002949',
- "Ubrcy;": '\U0000040E',
- "Ubreve;": '\U0000016C',
- "Ucirc;": '\U000000DB',
- "Ucy;": '\U00000423',
- "Udblac;": '\U00000170',
- "Ufr;": '\U0001D518',
- "Ugrave;": '\U000000D9',
- "Umacr;": '\U0000016A',
- "UnderBar;": '\U0000005F',
- "UnderBrace;": '\U000023DF',
- "UnderBracket;": '\U000023B5',
- "UnderParenthesis;": '\U000023DD',
- "Union;": '\U000022C3',
- "UnionPlus;": '\U0000228E',
- "Uogon;": '\U00000172',
- "Uopf;": '\U0001D54C',
- "UpArrow;": '\U00002191',
- "UpArrowBar;": '\U00002912',
- "UpArrowDownArrow;": '\U000021C5',
- "UpDownArrow;": '\U00002195',
- "UpEquilibrium;": '\U0000296E',
- "UpTee;": '\U000022A5',
- "UpTeeArrow;": '\U000021A5',
- "Uparrow;": '\U000021D1',
- "Updownarrow;": '\U000021D5',
- "UpperLeftArrow;": '\U00002196',
- "UpperRightArrow;": '\U00002197',
- "Upsi;": '\U000003D2',
- "Upsilon;": '\U000003A5',
- "Uring;": '\U0000016E',
- "Uscr;": '\U0001D4B0',
- "Utilde;": '\U00000168',
- "Uuml;": '\U000000DC',
- "VDash;": '\U000022AB',
- "Vbar;": '\U00002AEB',
- "Vcy;": '\U00000412',
- "Vdash;": '\U000022A9',
- "Vdashl;": '\U00002AE6',
- "Vee;": '\U000022C1',
- "Verbar;": '\U00002016',
- "Vert;": '\U00002016',
- "VerticalBar;": '\U00002223',
- "VerticalLine;": '\U0000007C',
- "VerticalSeparator;": '\U00002758',
- "VerticalTilde;": '\U00002240',
- "VeryThinSpace;": '\U0000200A',
- "Vfr;": '\U0001D519',
- "Vopf;": '\U0001D54D',
- "Vscr;": '\U0001D4B1',
- "Vvdash;": '\U000022AA',
- "Wcirc;": '\U00000174',
- "Wedge;": '\U000022C0',
- "Wfr;": '\U0001D51A',
- "Wopf;": '\U0001D54E',
- "Wscr;": '\U0001D4B2',
- "Xfr;": '\U0001D51B',
- "Xi;": '\U0000039E',
- "Xopf;": '\U0001D54F',
- "Xscr;": '\U0001D4B3',
- "YAcy;": '\U0000042F',
- "YIcy;": '\U00000407',
- "YUcy;": '\U0000042E',
- "Yacute;": '\U000000DD',
- "Ycirc;": '\U00000176',
- "Ycy;": '\U0000042B',
- "Yfr;": '\U0001D51C',
- "Yopf;": '\U0001D550',
- "Yscr;": '\U0001D4B4',
- "Yuml;": '\U00000178',
- "ZHcy;": '\U00000416',
- "Zacute;": '\U00000179',
- "Zcaron;": '\U0000017D',
- "Zcy;": '\U00000417',
- "Zdot;": '\U0000017B',
- "ZeroWidthSpace;": '\U0000200B',
- "Zeta;": '\U00000396',
- "Zfr;": '\U00002128',
- "Zopf;": '\U00002124',
- "Zscr;": '\U0001D4B5',
- "aacute;": '\U000000E1',
- "abreve;": '\U00000103',
- "ac;": '\U0000223E',
- "acd;": '\U0000223F',
- "acirc;": '\U000000E2',
- "acute;": '\U000000B4',
- "acy;": '\U00000430',
- "aelig;": '\U000000E6',
- "af;": '\U00002061',
- "afr;": '\U0001D51E',
- "agrave;": '\U000000E0',
- "alefsym;": '\U00002135',
- "aleph;": '\U00002135',
- "alpha;": '\U000003B1',
- "amacr;": '\U00000101',
- "amalg;": '\U00002A3F',
- "amp;": '\U00000026',
- "and;": '\U00002227',
- "andand;": '\U00002A55',
- "andd;": '\U00002A5C',
- "andslope;": '\U00002A58',
- "andv;": '\U00002A5A',
- "ang;": '\U00002220',
- "ange;": '\U000029A4',
- "angle;": '\U00002220',
- "angmsd;": '\U00002221',
- "angmsdaa;": '\U000029A8',
- "angmsdab;": '\U000029A9',
- "angmsdac;": '\U000029AA',
- "angmsdad;": '\U000029AB',
- "angmsdae;": '\U000029AC',
- "angmsdaf;": '\U000029AD',
- "angmsdag;": '\U000029AE',
- "angmsdah;": '\U000029AF',
- "angrt;": '\U0000221F',
- "angrtvb;": '\U000022BE',
- "angrtvbd;": '\U0000299D',
- "angsph;": '\U00002222',
- "angst;": '\U000000C5',
- "angzarr;": '\U0000237C',
- "aogon;": '\U00000105',
- "aopf;": '\U0001D552',
- "ap;": '\U00002248',
- "apE;": '\U00002A70',
- "apacir;": '\U00002A6F',
- "ape;": '\U0000224A',
- "apid;": '\U0000224B',
- "apos;": '\U00000027',
- "approx;": '\U00002248',
- "approxeq;": '\U0000224A',
- "aring;": '\U000000E5',
- "ascr;": '\U0001D4B6',
- "ast;": '\U0000002A',
- "asymp;": '\U00002248',
- "asympeq;": '\U0000224D',
- "atilde;": '\U000000E3',
- "auml;": '\U000000E4',
- "awconint;": '\U00002233',
- "awint;": '\U00002A11',
- "bNot;": '\U00002AED',
- "backcong;": '\U0000224C',
- "backepsilon;": '\U000003F6',
- "backprime;": '\U00002035',
- "backsim;": '\U0000223D',
- "backsimeq;": '\U000022CD',
- "barvee;": '\U000022BD',
- "barwed;": '\U00002305',
- "barwedge;": '\U00002305',
- "bbrk;": '\U000023B5',
- "bbrktbrk;": '\U000023B6',
- "bcong;": '\U0000224C',
- "bcy;": '\U00000431',
- "bdquo;": '\U0000201E',
- "becaus;": '\U00002235',
- "because;": '\U00002235',
- "bemptyv;": '\U000029B0',
- "bepsi;": '\U000003F6',
- "bernou;": '\U0000212C',
- "beta;": '\U000003B2',
- "beth;": '\U00002136',
- "between;": '\U0000226C',
- "bfr;": '\U0001D51F',
- "bigcap;": '\U000022C2',
- "bigcirc;": '\U000025EF',
- "bigcup;": '\U000022C3',
- "bigodot;": '\U00002A00',
- "bigoplus;": '\U00002A01',
- "bigotimes;": '\U00002A02',
- "bigsqcup;": '\U00002A06',
- "bigstar;": '\U00002605',
- "bigtriangledown;": '\U000025BD',
- "bigtriangleup;": '\U000025B3',
- "biguplus;": '\U00002A04',
- "bigvee;": '\U000022C1',
- "bigwedge;": '\U000022C0',
- "bkarow;": '\U0000290D',
- "blacklozenge;": '\U000029EB',
- "blacksquare;": '\U000025AA',
- "blacktriangle;": '\U000025B4',
- "blacktriangledown;": '\U000025BE',
- "blacktriangleleft;": '\U000025C2',
- "blacktriangleright;": '\U000025B8',
- "blank;": '\U00002423',
- "blk12;": '\U00002592',
- "blk14;": '\U00002591',
- "blk34;": '\U00002593',
- "block;": '\U00002588',
- "bnot;": '\U00002310',
- "bopf;": '\U0001D553',
- "bot;": '\U000022A5',
- "bottom;": '\U000022A5',
- "bowtie;": '\U000022C8',
- "boxDL;": '\U00002557',
- "boxDR;": '\U00002554',
- "boxDl;": '\U00002556',
- "boxDr;": '\U00002553',
- "boxH;": '\U00002550',
- "boxHD;": '\U00002566',
- "boxHU;": '\U00002569',
- "boxHd;": '\U00002564',
- "boxHu;": '\U00002567',
- "boxUL;": '\U0000255D',
- "boxUR;": '\U0000255A',
- "boxUl;": '\U0000255C',
- "boxUr;": '\U00002559',
- "boxV;": '\U00002551',
- "boxVH;": '\U0000256C',
- "boxVL;": '\U00002563',
- "boxVR;": '\U00002560',
- "boxVh;": '\U0000256B',
- "boxVl;": '\U00002562',
- "boxVr;": '\U0000255F',
- "boxbox;": '\U000029C9',
- "boxdL;": '\U00002555',
- "boxdR;": '\U00002552',
- "boxdl;": '\U00002510',
- "boxdr;": '\U0000250C',
- "boxh;": '\U00002500',
- "boxhD;": '\U00002565',
- "boxhU;": '\U00002568',
- "boxhd;": '\U0000252C',
- "boxhu;": '\U00002534',
- "boxminus;": '\U0000229F',
- "boxplus;": '\U0000229E',
- "boxtimes;": '\U000022A0',
- "boxuL;": '\U0000255B',
- "boxuR;": '\U00002558',
- "boxul;": '\U00002518',
- "boxur;": '\U00002514',
- "boxv;": '\U00002502',
- "boxvH;": '\U0000256A',
- "boxvL;": '\U00002561',
- "boxvR;": '\U0000255E',
- "boxvh;": '\U0000253C',
- "boxvl;": '\U00002524',
- "boxvr;": '\U0000251C',
- "bprime;": '\U00002035',
- "breve;": '\U000002D8',
- "brvbar;": '\U000000A6',
- "bscr;": '\U0001D4B7',
- "bsemi;": '\U0000204F',
- "bsim;": '\U0000223D',
- "bsime;": '\U000022CD',
- "bsol;": '\U0000005C',
- "bsolb;": '\U000029C5',
- "bsolhsub;": '\U000027C8',
- "bull;": '\U00002022',
- "bullet;": '\U00002022',
- "bump;": '\U0000224E',
- "bumpE;": '\U00002AAE',
- "bumpe;": '\U0000224F',
- "bumpeq;": '\U0000224F',
- "cacute;": '\U00000107',
- "cap;": '\U00002229',
- "capand;": '\U00002A44',
- "capbrcup;": '\U00002A49',
- "capcap;": '\U00002A4B',
- "capcup;": '\U00002A47',
- "capdot;": '\U00002A40',
- "caret;": '\U00002041',
- "caron;": '\U000002C7',
- "ccaps;": '\U00002A4D',
- "ccaron;": '\U0000010D',
- "ccedil;": '\U000000E7',
- "ccirc;": '\U00000109',
- "ccups;": '\U00002A4C',
- "ccupssm;": '\U00002A50',
- "cdot;": '\U0000010B',
- "cedil;": '\U000000B8',
- "cemptyv;": '\U000029B2',
- "cent;": '\U000000A2',
- "centerdot;": '\U000000B7',
- "cfr;": '\U0001D520',
- "chcy;": '\U00000447',
- "check;": '\U00002713',
- "checkmark;": '\U00002713',
- "chi;": '\U000003C7',
- "cir;": '\U000025CB',
- "cirE;": '\U000029C3',
- "circ;": '\U000002C6',
- "circeq;": '\U00002257',
- "circlearrowleft;": '\U000021BA',
- "circlearrowright;": '\U000021BB',
- "circledR;": '\U000000AE',
- "circledS;": '\U000024C8',
- "circledast;": '\U0000229B',
- "circledcirc;": '\U0000229A',
- "circleddash;": '\U0000229D',
- "cire;": '\U00002257',
- "cirfnint;": '\U00002A10',
- "cirmid;": '\U00002AEF',
- "cirscir;": '\U000029C2',
- "clubs;": '\U00002663',
- "clubsuit;": '\U00002663',
- "colon;": '\U0000003A',
- "colone;": '\U00002254',
- "coloneq;": '\U00002254',
- "comma;": '\U0000002C',
- "commat;": '\U00000040',
- "comp;": '\U00002201',
- "compfn;": '\U00002218',
- "complement;": '\U00002201',
- "complexes;": '\U00002102',
- "cong;": '\U00002245',
- "congdot;": '\U00002A6D',
- "conint;": '\U0000222E',
- "copf;": '\U0001D554',
- "coprod;": '\U00002210',
- "copy;": '\U000000A9',
- "copysr;": '\U00002117',
- "crarr;": '\U000021B5',
- "cross;": '\U00002717',
- "cscr;": '\U0001D4B8',
- "csub;": '\U00002ACF',
- "csube;": '\U00002AD1',
- "csup;": '\U00002AD0',
- "csupe;": '\U00002AD2',
- "ctdot;": '\U000022EF',
- "cudarrl;": '\U00002938',
- "cudarrr;": '\U00002935',
- "cuepr;": '\U000022DE',
- "cuesc;": '\U000022DF',
- "cularr;": '\U000021B6',
- "cularrp;": '\U0000293D',
- "cup;": '\U0000222A',
- "cupbrcap;": '\U00002A48',
- "cupcap;": '\U00002A46',
- "cupcup;": '\U00002A4A',
- "cupdot;": '\U0000228D',
- "cupor;": '\U00002A45',
- "curarr;": '\U000021B7',
- "curarrm;": '\U0000293C',
- "curlyeqprec;": '\U000022DE',
- "curlyeqsucc;": '\U000022DF',
- "curlyvee;": '\U000022CE',
- "curlywedge;": '\U000022CF',
- "curren;": '\U000000A4',
- "curvearrowleft;": '\U000021B6',
- "curvearrowright;": '\U000021B7',
- "cuvee;": '\U000022CE',
- "cuwed;": '\U000022CF',
- "cwconint;": '\U00002232',
- "cwint;": '\U00002231',
- "cylcty;": '\U0000232D',
- "dArr;": '\U000021D3',
- "dHar;": '\U00002965',
- "dagger;": '\U00002020',
- "daleth;": '\U00002138',
- "darr;": '\U00002193',
- "dash;": '\U00002010',
- "dashv;": '\U000022A3',
- "dbkarow;": '\U0000290F',
- "dblac;": '\U000002DD',
- "dcaron;": '\U0000010F',
- "dcy;": '\U00000434',
- "dd;": '\U00002146',
- "ddagger;": '\U00002021',
- "ddarr;": '\U000021CA',
- "ddotseq;": '\U00002A77',
- "deg;": '\U000000B0',
- "delta;": '\U000003B4',
- "demptyv;": '\U000029B1',
- "dfisht;": '\U0000297F',
- "dfr;": '\U0001D521',
- "dharl;": '\U000021C3',
- "dharr;": '\U000021C2',
- "diam;": '\U000022C4',
- "diamond;": '\U000022C4',
- "diamondsuit;": '\U00002666',
- "diams;": '\U00002666',
- "die;": '\U000000A8',
- "digamma;": '\U000003DD',
- "disin;": '\U000022F2',
- "div;": '\U000000F7',
- "divide;": '\U000000F7',
- "divideontimes;": '\U000022C7',
- "divonx;": '\U000022C7',
- "djcy;": '\U00000452',
- "dlcorn;": '\U0000231E',
- "dlcrop;": '\U0000230D',
- "dollar;": '\U00000024',
- "dopf;": '\U0001D555',
- "dot;": '\U000002D9',
- "doteq;": '\U00002250',
- "doteqdot;": '\U00002251',
- "dotminus;": '\U00002238',
- "dotplus;": '\U00002214',
- "dotsquare;": '\U000022A1',
- "doublebarwedge;": '\U00002306',
- "downarrow;": '\U00002193',
- "downdownarrows;": '\U000021CA',
- "downharpoonleft;": '\U000021C3',
- "downharpoonright;": '\U000021C2',
- "drbkarow;": '\U00002910',
- "drcorn;": '\U0000231F',
- "drcrop;": '\U0000230C',
- "dscr;": '\U0001D4B9',
- "dscy;": '\U00000455',
- "dsol;": '\U000029F6',
- "dstrok;": '\U00000111',
- "dtdot;": '\U000022F1',
- "dtri;": '\U000025BF',
- "dtrif;": '\U000025BE',
- "duarr;": '\U000021F5',
- "duhar;": '\U0000296F',
- "dwangle;": '\U000029A6',
- "dzcy;": '\U0000045F',
- "dzigrarr;": '\U000027FF',
- "eDDot;": '\U00002A77',
- "eDot;": '\U00002251',
- "eacute;": '\U000000E9',
- "easter;": '\U00002A6E',
- "ecaron;": '\U0000011B',
- "ecir;": '\U00002256',
- "ecirc;": '\U000000EA',
- "ecolon;": '\U00002255',
- "ecy;": '\U0000044D',
- "edot;": '\U00000117',
- "ee;": '\U00002147',
- "efDot;": '\U00002252',
- "efr;": '\U0001D522',
- "eg;": '\U00002A9A',
- "egrave;": '\U000000E8',
- "egs;": '\U00002A96',
- "egsdot;": '\U00002A98',
- "el;": '\U00002A99',
- "elinters;": '\U000023E7',
- "ell;": '\U00002113',
- "els;": '\U00002A95',
- "elsdot;": '\U00002A97',
- "emacr;": '\U00000113',
- "empty;": '\U00002205',
- "emptyset;": '\U00002205',
- "emptyv;": '\U00002205',
- "emsp;": '\U00002003',
- "emsp13;": '\U00002004',
- "emsp14;": '\U00002005',
- "eng;": '\U0000014B',
- "ensp;": '\U00002002',
- "eogon;": '\U00000119',
- "eopf;": '\U0001D556',
- "epar;": '\U000022D5',
- "eparsl;": '\U000029E3',
- "eplus;": '\U00002A71',
- "epsi;": '\U000003B5',
- "epsilon;": '\U000003B5',
- "epsiv;": '\U000003F5',
- "eqcirc;": '\U00002256',
- "eqcolon;": '\U00002255',
- "eqsim;": '\U00002242',
- "eqslantgtr;": '\U00002A96',
- "eqslantless;": '\U00002A95',
- "equals;": '\U0000003D',
- "equest;": '\U0000225F',
- "equiv;": '\U00002261',
- "equivDD;": '\U00002A78',
- "eqvparsl;": '\U000029E5',
- "erDot;": '\U00002253',
- "erarr;": '\U00002971',
- "escr;": '\U0000212F',
- "esdot;": '\U00002250',
- "esim;": '\U00002242',
- "eta;": '\U000003B7',
- "eth;": '\U000000F0',
- "euml;": '\U000000EB',
- "euro;": '\U000020AC',
- "excl;": '\U00000021',
- "exist;": '\U00002203',
- "expectation;": '\U00002130',
- "exponentiale;": '\U00002147',
- "fallingdotseq;": '\U00002252',
- "fcy;": '\U00000444',
- "female;": '\U00002640',
- "ffilig;": '\U0000FB03',
- "fflig;": '\U0000FB00',
- "ffllig;": '\U0000FB04',
- "ffr;": '\U0001D523',
- "filig;": '\U0000FB01',
- "flat;": '\U0000266D',
- "fllig;": '\U0000FB02',
- "fltns;": '\U000025B1',
- "fnof;": '\U00000192',
- "fopf;": '\U0001D557',
- "forall;": '\U00002200',
- "fork;": '\U000022D4',
- "forkv;": '\U00002AD9',
- "fpartint;": '\U00002A0D',
- "frac12;": '\U000000BD',
- "frac13;": '\U00002153',
- "frac14;": '\U000000BC',
- "frac15;": '\U00002155',
- "frac16;": '\U00002159',
- "frac18;": '\U0000215B',
- "frac23;": '\U00002154',
- "frac25;": '\U00002156',
- "frac34;": '\U000000BE',
- "frac35;": '\U00002157',
- "frac38;": '\U0000215C',
- "frac45;": '\U00002158',
- "frac56;": '\U0000215A',
- "frac58;": '\U0000215D',
- "frac78;": '\U0000215E',
- "frasl;": '\U00002044',
- "frown;": '\U00002322',
- "fscr;": '\U0001D4BB',
- "gE;": '\U00002267',
- "gEl;": '\U00002A8C',
- "gacute;": '\U000001F5',
- "gamma;": '\U000003B3',
- "gammad;": '\U000003DD',
- "gap;": '\U00002A86',
- "gbreve;": '\U0000011F',
- "gcirc;": '\U0000011D',
- "gcy;": '\U00000433',
- "gdot;": '\U00000121',
- "ge;": '\U00002265',
- "gel;": '\U000022DB',
- "geq;": '\U00002265',
- "geqq;": '\U00002267',
- "geqslant;": '\U00002A7E',
- "ges;": '\U00002A7E',
- "gescc;": '\U00002AA9',
- "gesdot;": '\U00002A80',
- "gesdoto;": '\U00002A82',
- "gesdotol;": '\U00002A84',
- "gesles;": '\U00002A94',
- "gfr;": '\U0001D524',
- "gg;": '\U0000226B',
- "ggg;": '\U000022D9',
- "gimel;": '\U00002137',
- "gjcy;": '\U00000453',
- "gl;": '\U00002277',
- "glE;": '\U00002A92',
- "gla;": '\U00002AA5',
- "glj;": '\U00002AA4',
- "gnE;": '\U00002269',
- "gnap;": '\U00002A8A',
- "gnapprox;": '\U00002A8A',
- "gne;": '\U00002A88',
- "gneq;": '\U00002A88',
- "gneqq;": '\U00002269',
- "gnsim;": '\U000022E7',
- "gopf;": '\U0001D558',
- "grave;": '\U00000060',
- "gscr;": '\U0000210A',
- "gsim;": '\U00002273',
- "gsime;": '\U00002A8E',
- "gsiml;": '\U00002A90',
- "gt;": '\U0000003E',
- "gtcc;": '\U00002AA7',
- "gtcir;": '\U00002A7A',
- "gtdot;": '\U000022D7',
- "gtlPar;": '\U00002995',
- "gtquest;": '\U00002A7C',
- "gtrapprox;": '\U00002A86',
- "gtrarr;": '\U00002978',
- "gtrdot;": '\U000022D7',
- "gtreqless;": '\U000022DB',
- "gtreqqless;": '\U00002A8C',
- "gtrless;": '\U00002277',
- "gtrsim;": '\U00002273',
- "hArr;": '\U000021D4',
- "hairsp;": '\U0000200A',
- "half;": '\U000000BD',
- "hamilt;": '\U0000210B',
- "hardcy;": '\U0000044A',
- "harr;": '\U00002194',
- "harrcir;": '\U00002948',
- "harrw;": '\U000021AD',
- "hbar;": '\U0000210F',
- "hcirc;": '\U00000125',
- "hearts;": '\U00002665',
- "heartsuit;": '\U00002665',
- "hellip;": '\U00002026',
- "hercon;": '\U000022B9',
- "hfr;": '\U0001D525',
- "hksearow;": '\U00002925',
- "hkswarow;": '\U00002926',
- "hoarr;": '\U000021FF',
- "homtht;": '\U0000223B',
- "hookleftarrow;": '\U000021A9',
- "hookrightarrow;": '\U000021AA',
- "hopf;": '\U0001D559',
- "horbar;": '\U00002015',
- "hscr;": '\U0001D4BD',
- "hslash;": '\U0000210F',
- "hstrok;": '\U00000127',
- "hybull;": '\U00002043',
- "hyphen;": '\U00002010',
- "iacute;": '\U000000ED',
- "ic;": '\U00002063',
- "icirc;": '\U000000EE',
- "icy;": '\U00000438',
- "iecy;": '\U00000435',
- "iexcl;": '\U000000A1',
- "iff;": '\U000021D4',
- "ifr;": '\U0001D526',
- "igrave;": '\U000000EC',
- "ii;": '\U00002148',
- "iiiint;": '\U00002A0C',
- "iiint;": '\U0000222D',
- "iinfin;": '\U000029DC',
- "iiota;": '\U00002129',
- "ijlig;": '\U00000133',
- "imacr;": '\U0000012B',
- "image;": '\U00002111',
- "imagline;": '\U00002110',
- "imagpart;": '\U00002111',
- "imath;": '\U00000131',
- "imof;": '\U000022B7',
- "imped;": '\U000001B5',
- "in;": '\U00002208',
- "incare;": '\U00002105',
- "infin;": '\U0000221E',
- "infintie;": '\U000029DD',
- "inodot;": '\U00000131',
- "int;": '\U0000222B',
- "intcal;": '\U000022BA',
- "integers;": '\U00002124',
- "intercal;": '\U000022BA',
- "intlarhk;": '\U00002A17',
- "intprod;": '\U00002A3C',
- "iocy;": '\U00000451',
- "iogon;": '\U0000012F',
- "iopf;": '\U0001D55A',
- "iota;": '\U000003B9',
- "iprod;": '\U00002A3C',
- "iquest;": '\U000000BF',
- "iscr;": '\U0001D4BE',
- "isin;": '\U00002208',
- "isinE;": '\U000022F9',
- "isindot;": '\U000022F5',
- "isins;": '\U000022F4',
- "isinsv;": '\U000022F3',
- "isinv;": '\U00002208',
- "it;": '\U00002062',
- "itilde;": '\U00000129',
- "iukcy;": '\U00000456',
- "iuml;": '\U000000EF',
- "jcirc;": '\U00000135',
- "jcy;": '\U00000439',
- "jfr;": '\U0001D527',
- "jmath;": '\U00000237',
- "jopf;": '\U0001D55B',
- "jscr;": '\U0001D4BF',
- "jsercy;": '\U00000458',
- "jukcy;": '\U00000454',
- "kappa;": '\U000003BA',
- "kappav;": '\U000003F0',
- "kcedil;": '\U00000137',
- "kcy;": '\U0000043A',
- "kfr;": '\U0001D528',
- "kgreen;": '\U00000138',
- "khcy;": '\U00000445',
- "kjcy;": '\U0000045C',
- "kopf;": '\U0001D55C',
- "kscr;": '\U0001D4C0',
- "lAarr;": '\U000021DA',
- "lArr;": '\U000021D0',
- "lAtail;": '\U0000291B',
- "lBarr;": '\U0000290E',
- "lE;": '\U00002266',
- "lEg;": '\U00002A8B',
- "lHar;": '\U00002962',
- "lacute;": '\U0000013A',
- "laemptyv;": '\U000029B4',
- "lagran;": '\U00002112',
- "lambda;": '\U000003BB',
- "lang;": '\U000027E8',
- "langd;": '\U00002991',
- "langle;": '\U000027E8',
- "lap;": '\U00002A85',
- "laquo;": '\U000000AB',
- "larr;": '\U00002190',
- "larrb;": '\U000021E4',
- "larrbfs;": '\U0000291F',
- "larrfs;": '\U0000291D',
- "larrhk;": '\U000021A9',
- "larrlp;": '\U000021AB',
- "larrpl;": '\U00002939',
- "larrsim;": '\U00002973',
- "larrtl;": '\U000021A2',
- "lat;": '\U00002AAB',
- "latail;": '\U00002919',
- "late;": '\U00002AAD',
- "lbarr;": '\U0000290C',
- "lbbrk;": '\U00002772',
- "lbrace;": '\U0000007B',
- "lbrack;": '\U0000005B',
- "lbrke;": '\U0000298B',
- "lbrksld;": '\U0000298F',
- "lbrkslu;": '\U0000298D',
- "lcaron;": '\U0000013E',
- "lcedil;": '\U0000013C',
- "lceil;": '\U00002308',
- "lcub;": '\U0000007B',
- "lcy;": '\U0000043B',
- "ldca;": '\U00002936',
- "ldquo;": '\U0000201C',
- "ldquor;": '\U0000201E',
- "ldrdhar;": '\U00002967',
- "ldrushar;": '\U0000294B',
- "ldsh;": '\U000021B2',
- "le;": '\U00002264',
- "leftarrow;": '\U00002190',
- "leftarrowtail;": '\U000021A2',
- "leftharpoondown;": '\U000021BD',
- "leftharpoonup;": '\U000021BC',
- "leftleftarrows;": '\U000021C7',
- "leftrightarrow;": '\U00002194',
- "leftrightarrows;": '\U000021C6',
- "leftrightharpoons;": '\U000021CB',
- "leftrightsquigarrow;": '\U000021AD',
- "leftthreetimes;": '\U000022CB',
- "leg;": '\U000022DA',
- "leq;": '\U00002264',
- "leqq;": '\U00002266',
- "leqslant;": '\U00002A7D',
- "les;": '\U00002A7D',
- "lescc;": '\U00002AA8',
- "lesdot;": '\U00002A7F',
- "lesdoto;": '\U00002A81',
- "lesdotor;": '\U00002A83',
- "lesges;": '\U00002A93',
- "lessapprox;": '\U00002A85',
- "lessdot;": '\U000022D6',
- "lesseqgtr;": '\U000022DA',
- "lesseqqgtr;": '\U00002A8B',
- "lessgtr;": '\U00002276',
- "lesssim;": '\U00002272',
- "lfisht;": '\U0000297C',
- "lfloor;": '\U0000230A',
- "lfr;": '\U0001D529',
- "lg;": '\U00002276',
- "lgE;": '\U00002A91',
- "lhard;": '\U000021BD',
- "lharu;": '\U000021BC',
- "lharul;": '\U0000296A',
- "lhblk;": '\U00002584',
- "ljcy;": '\U00000459',
- "ll;": '\U0000226A',
- "llarr;": '\U000021C7',
- "llcorner;": '\U0000231E',
- "llhard;": '\U0000296B',
- "lltri;": '\U000025FA',
- "lmidot;": '\U00000140',
- "lmoust;": '\U000023B0',
- "lmoustache;": '\U000023B0',
- "lnE;": '\U00002268',
- "lnap;": '\U00002A89',
- "lnapprox;": '\U00002A89',
- "lne;": '\U00002A87',
- "lneq;": '\U00002A87',
- "lneqq;": '\U00002268',
- "lnsim;": '\U000022E6',
- "loang;": '\U000027EC',
- "loarr;": '\U000021FD',
- "lobrk;": '\U000027E6',
- "longleftarrow;": '\U000027F5',
- "longleftrightarrow;": '\U000027F7',
- "longmapsto;": '\U000027FC',
- "longrightarrow;": '\U000027F6',
- "looparrowleft;": '\U000021AB',
- "looparrowright;": '\U000021AC',
- "lopar;": '\U00002985',
- "lopf;": '\U0001D55D',
- "loplus;": '\U00002A2D',
- "lotimes;": '\U00002A34',
- "lowast;": '\U00002217',
- "lowbar;": '\U0000005F',
- "loz;": '\U000025CA',
- "lozenge;": '\U000025CA',
- "lozf;": '\U000029EB',
- "lpar;": '\U00000028',
- "lparlt;": '\U00002993',
- "lrarr;": '\U000021C6',
- "lrcorner;": '\U0000231F',
- "lrhar;": '\U000021CB',
- "lrhard;": '\U0000296D',
- "lrm;": '\U0000200E',
- "lrtri;": '\U000022BF',
- "lsaquo;": '\U00002039',
- "lscr;": '\U0001D4C1',
- "lsh;": '\U000021B0',
- "lsim;": '\U00002272',
- "lsime;": '\U00002A8D',
- "lsimg;": '\U00002A8F',
- "lsqb;": '\U0000005B',
- "lsquo;": '\U00002018',
- "lsquor;": '\U0000201A',
- "lstrok;": '\U00000142',
- "lt;": '\U0000003C',
- "ltcc;": '\U00002AA6',
- "ltcir;": '\U00002A79',
- "ltdot;": '\U000022D6',
- "lthree;": '\U000022CB',
- "ltimes;": '\U000022C9',
- "ltlarr;": '\U00002976',
- "ltquest;": '\U00002A7B',
- "ltrPar;": '\U00002996',
- "ltri;": '\U000025C3',
- "ltrie;": '\U000022B4',
- "ltrif;": '\U000025C2',
- "lurdshar;": '\U0000294A',
- "luruhar;": '\U00002966',
- "mDDot;": '\U0000223A',
- "macr;": '\U000000AF',
- "male;": '\U00002642',
- "malt;": '\U00002720',
- "maltese;": '\U00002720',
- "map;": '\U000021A6',
- "mapsto;": '\U000021A6',
- "mapstodown;": '\U000021A7',
- "mapstoleft;": '\U000021A4',
- "mapstoup;": '\U000021A5',
- "marker;": '\U000025AE',
- "mcomma;": '\U00002A29',
- "mcy;": '\U0000043C',
- "mdash;": '\U00002014',
- "measuredangle;": '\U00002221',
- "mfr;": '\U0001D52A',
- "mho;": '\U00002127',
- "micro;": '\U000000B5',
- "mid;": '\U00002223',
- "midast;": '\U0000002A',
- "midcir;": '\U00002AF0',
- "middot;": '\U000000B7',
- "minus;": '\U00002212',
- "minusb;": '\U0000229F',
- "minusd;": '\U00002238',
- "minusdu;": '\U00002A2A',
- "mlcp;": '\U00002ADB',
- "mldr;": '\U00002026',
- "mnplus;": '\U00002213',
- "models;": '\U000022A7',
- "mopf;": '\U0001D55E',
- "mp;": '\U00002213',
- "mscr;": '\U0001D4C2',
- "mstpos;": '\U0000223E',
- "mu;": '\U000003BC',
- "multimap;": '\U000022B8',
- "mumap;": '\U000022B8',
- "nLeftarrow;": '\U000021CD',
- "nLeftrightarrow;": '\U000021CE',
- "nRightarrow;": '\U000021CF',
- "nVDash;": '\U000022AF',
- "nVdash;": '\U000022AE',
- "nabla;": '\U00002207',
- "nacute;": '\U00000144',
- "nap;": '\U00002249',
- "napos;": '\U00000149',
- "napprox;": '\U00002249',
- "natur;": '\U0000266E',
- "natural;": '\U0000266E',
- "naturals;": '\U00002115',
- "nbsp;": '\U000000A0',
- "ncap;": '\U00002A43',
- "ncaron;": '\U00000148',
- "ncedil;": '\U00000146',
- "ncong;": '\U00002247',
- "ncup;": '\U00002A42',
- "ncy;": '\U0000043D',
- "ndash;": '\U00002013',
- "ne;": '\U00002260',
- "neArr;": '\U000021D7',
- "nearhk;": '\U00002924',
- "nearr;": '\U00002197',
- "nearrow;": '\U00002197',
- "nequiv;": '\U00002262',
- "nesear;": '\U00002928',
- "nexist;": '\U00002204',
- "nexists;": '\U00002204',
- "nfr;": '\U0001D52B',
- "nge;": '\U00002271',
- "ngeq;": '\U00002271',
- "ngsim;": '\U00002275',
- "ngt;": '\U0000226F',
- "ngtr;": '\U0000226F',
- "nhArr;": '\U000021CE',
- "nharr;": '\U000021AE',
- "nhpar;": '\U00002AF2',
- "ni;": '\U0000220B',
- "nis;": '\U000022FC',
- "nisd;": '\U000022FA',
- "niv;": '\U0000220B',
- "njcy;": '\U0000045A',
- "nlArr;": '\U000021CD',
- "nlarr;": '\U0000219A',
- "nldr;": '\U00002025',
- "nle;": '\U00002270',
- "nleftarrow;": '\U0000219A',
- "nleftrightarrow;": '\U000021AE',
- "nleq;": '\U00002270',
- "nless;": '\U0000226E',
- "nlsim;": '\U00002274',
- "nlt;": '\U0000226E',
- "nltri;": '\U000022EA',
- "nltrie;": '\U000022EC',
- "nmid;": '\U00002224',
- "nopf;": '\U0001D55F',
- "not;": '\U000000AC',
- "notin;": '\U00002209',
- "notinva;": '\U00002209',
- "notinvb;": '\U000022F7',
- "notinvc;": '\U000022F6',
- "notni;": '\U0000220C',
- "notniva;": '\U0000220C',
- "notnivb;": '\U000022FE',
- "notnivc;": '\U000022FD',
- "npar;": '\U00002226',
- "nparallel;": '\U00002226',
- "npolint;": '\U00002A14',
- "npr;": '\U00002280',
- "nprcue;": '\U000022E0',
- "nprec;": '\U00002280',
- "nrArr;": '\U000021CF',
- "nrarr;": '\U0000219B',
- "nrightarrow;": '\U0000219B',
- "nrtri;": '\U000022EB',
- "nrtrie;": '\U000022ED',
- "nsc;": '\U00002281',
- "nsccue;": '\U000022E1',
- "nscr;": '\U0001D4C3',
- "nshortmid;": '\U00002224',
- "nshortparallel;": '\U00002226',
- "nsim;": '\U00002241',
- "nsime;": '\U00002244',
- "nsimeq;": '\U00002244',
- "nsmid;": '\U00002224',
- "nspar;": '\U00002226',
- "nsqsube;": '\U000022E2',
- "nsqsupe;": '\U000022E3',
- "nsub;": '\U00002284',
- "nsube;": '\U00002288',
- "nsubseteq;": '\U00002288',
- "nsucc;": '\U00002281',
- "nsup;": '\U00002285',
- "nsupe;": '\U00002289',
- "nsupseteq;": '\U00002289',
- "ntgl;": '\U00002279',
- "ntilde;": '\U000000F1',
- "ntlg;": '\U00002278',
- "ntriangleleft;": '\U000022EA',
- "ntrianglelefteq;": '\U000022EC',
- "ntriangleright;": '\U000022EB',
- "ntrianglerighteq;": '\U000022ED',
- "nu;": '\U000003BD',
- "num;": '\U00000023',
- "numero;": '\U00002116',
- "numsp;": '\U00002007',
- "nvDash;": '\U000022AD',
- "nvHarr;": '\U00002904',
- "nvdash;": '\U000022AC',
- "nvinfin;": '\U000029DE',
- "nvlArr;": '\U00002902',
- "nvrArr;": '\U00002903',
- "nwArr;": '\U000021D6',
- "nwarhk;": '\U00002923',
- "nwarr;": '\U00002196',
- "nwarrow;": '\U00002196',
- "nwnear;": '\U00002927',
- "oS;": '\U000024C8',
- "oacute;": '\U000000F3',
- "oast;": '\U0000229B',
- "ocir;": '\U0000229A',
- "ocirc;": '\U000000F4',
- "ocy;": '\U0000043E',
- "odash;": '\U0000229D',
- "odblac;": '\U00000151',
- "odiv;": '\U00002A38',
- "odot;": '\U00002299',
- "odsold;": '\U000029BC',
- "oelig;": '\U00000153',
- "ofcir;": '\U000029BF',
- "ofr;": '\U0001D52C',
- "ogon;": '\U000002DB',
- "ograve;": '\U000000F2',
- "ogt;": '\U000029C1',
- "ohbar;": '\U000029B5',
- "ohm;": '\U000003A9',
- "oint;": '\U0000222E',
- "olarr;": '\U000021BA',
- "olcir;": '\U000029BE',
- "olcross;": '\U000029BB',
- "oline;": '\U0000203E',
- "olt;": '\U000029C0',
- "omacr;": '\U0000014D',
- "omega;": '\U000003C9',
- "omicron;": '\U000003BF',
- "omid;": '\U000029B6',
- "ominus;": '\U00002296',
- "oopf;": '\U0001D560',
- "opar;": '\U000029B7',
- "operp;": '\U000029B9',
- "oplus;": '\U00002295',
- "or;": '\U00002228',
- "orarr;": '\U000021BB',
- "ord;": '\U00002A5D',
- "order;": '\U00002134',
- "orderof;": '\U00002134',
- "ordf;": '\U000000AA',
- "ordm;": '\U000000BA',
- "origof;": '\U000022B6',
- "oror;": '\U00002A56',
- "orslope;": '\U00002A57',
- "orv;": '\U00002A5B',
- "oscr;": '\U00002134',
- "oslash;": '\U000000F8',
- "osol;": '\U00002298',
- "otilde;": '\U000000F5',
- "otimes;": '\U00002297',
- "otimesas;": '\U00002A36',
- "ouml;": '\U000000F6',
- "ovbar;": '\U0000233D',
- "par;": '\U00002225',
- "para;": '\U000000B6',
- "parallel;": '\U00002225',
- "parsim;": '\U00002AF3',
- "parsl;": '\U00002AFD',
- "part;": '\U00002202',
- "pcy;": '\U0000043F',
- "percnt;": '\U00000025',
- "period;": '\U0000002E',
- "permil;": '\U00002030',
- "perp;": '\U000022A5',
- "pertenk;": '\U00002031',
- "pfr;": '\U0001D52D',
- "phi;": '\U000003C6',
- "phiv;": '\U000003D5',
- "phmmat;": '\U00002133',
- "phone;": '\U0000260E',
- "pi;": '\U000003C0',
- "pitchfork;": '\U000022D4',
- "piv;": '\U000003D6',
- "planck;": '\U0000210F',
- "planckh;": '\U0000210E',
- "plankv;": '\U0000210F',
- "plus;": '\U0000002B',
- "plusacir;": '\U00002A23',
- "plusb;": '\U0000229E',
- "pluscir;": '\U00002A22',
- "plusdo;": '\U00002214',
- "plusdu;": '\U00002A25',
- "pluse;": '\U00002A72',
- "plusmn;": '\U000000B1',
- "plussim;": '\U00002A26',
- "plustwo;": '\U00002A27',
- "pm;": '\U000000B1',
- "pointint;": '\U00002A15',
- "popf;": '\U0001D561',
- "pound;": '\U000000A3',
- "pr;": '\U0000227A',
- "prE;": '\U00002AB3',
- "prap;": '\U00002AB7',
- "prcue;": '\U0000227C',
- "pre;": '\U00002AAF',
- "prec;": '\U0000227A',
- "precapprox;": '\U00002AB7',
- "preccurlyeq;": '\U0000227C',
- "preceq;": '\U00002AAF',
- "precnapprox;": '\U00002AB9',
- "precneqq;": '\U00002AB5',
- "precnsim;": '\U000022E8',
- "precsim;": '\U0000227E',
- "prime;": '\U00002032',
- "primes;": '\U00002119',
- "prnE;": '\U00002AB5',
- "prnap;": '\U00002AB9',
- "prnsim;": '\U000022E8',
- "prod;": '\U0000220F',
- "profalar;": '\U0000232E',
- "profline;": '\U00002312',
- "profsurf;": '\U00002313',
- "prop;": '\U0000221D',
- "propto;": '\U0000221D',
- "prsim;": '\U0000227E',
- "prurel;": '\U000022B0',
- "pscr;": '\U0001D4C5',
- "psi;": '\U000003C8',
- "puncsp;": '\U00002008',
- "qfr;": '\U0001D52E',
- "qint;": '\U00002A0C',
- "qopf;": '\U0001D562',
- "qprime;": '\U00002057',
- "qscr;": '\U0001D4C6',
- "quaternions;": '\U0000210D',
- "quatint;": '\U00002A16',
- "quest;": '\U0000003F',
- "questeq;": '\U0000225F',
- "quot;": '\U00000022',
- "rAarr;": '\U000021DB',
- "rArr;": '\U000021D2',
- "rAtail;": '\U0000291C',
- "rBarr;": '\U0000290F',
- "rHar;": '\U00002964',
- "racute;": '\U00000155',
- "radic;": '\U0000221A',
- "raemptyv;": '\U000029B3',
- "rang;": '\U000027E9',
- "rangd;": '\U00002992',
- "range;": '\U000029A5',
- "rangle;": '\U000027E9',
- "raquo;": '\U000000BB',
- "rarr;": '\U00002192',
- "rarrap;": '\U00002975',
- "rarrb;": '\U000021E5',
- "rarrbfs;": '\U00002920',
- "rarrc;": '\U00002933',
- "rarrfs;": '\U0000291E',
- "rarrhk;": '\U000021AA',
- "rarrlp;": '\U000021AC',
- "rarrpl;": '\U00002945',
- "rarrsim;": '\U00002974',
- "rarrtl;": '\U000021A3',
- "rarrw;": '\U0000219D',
- "ratail;": '\U0000291A',
- "ratio;": '\U00002236',
- "rationals;": '\U0000211A',
- "rbarr;": '\U0000290D',
- "rbbrk;": '\U00002773',
- "rbrace;": '\U0000007D',
- "rbrack;": '\U0000005D',
- "rbrke;": '\U0000298C',
- "rbrksld;": '\U0000298E',
- "rbrkslu;": '\U00002990',
- "rcaron;": '\U00000159',
- "rcedil;": '\U00000157',
- "rceil;": '\U00002309',
- "rcub;": '\U0000007D',
- "rcy;": '\U00000440',
- "rdca;": '\U00002937',
- "rdldhar;": '\U00002969',
- "rdquo;": '\U0000201D',
- "rdquor;": '\U0000201D',
- "rdsh;": '\U000021B3',
- "real;": '\U0000211C',
- "realine;": '\U0000211B',
- "realpart;": '\U0000211C',
- "reals;": '\U0000211D',
- "rect;": '\U000025AD',
- "reg;": '\U000000AE',
- "rfisht;": '\U0000297D',
- "rfloor;": '\U0000230B',
- "rfr;": '\U0001D52F',
- "rhard;": '\U000021C1',
- "rharu;": '\U000021C0',
- "rharul;": '\U0000296C',
- "rho;": '\U000003C1',
- "rhov;": '\U000003F1',
- "rightarrow;": '\U00002192',
- "rightarrowtail;": '\U000021A3',
- "rightharpoondown;": '\U000021C1',
- "rightharpoonup;": '\U000021C0',
- "rightleftarrows;": '\U000021C4',
- "rightleftharpoons;": '\U000021CC',
- "rightrightarrows;": '\U000021C9',
- "rightsquigarrow;": '\U0000219D',
- "rightthreetimes;": '\U000022CC',
- "ring;": '\U000002DA',
- "risingdotseq;": '\U00002253',
- "rlarr;": '\U000021C4',
- "rlhar;": '\U000021CC',
- "rlm;": '\U0000200F',
- "rmoust;": '\U000023B1',
- "rmoustache;": '\U000023B1',
- "rnmid;": '\U00002AEE',
- "roang;": '\U000027ED',
- "roarr;": '\U000021FE',
- "robrk;": '\U000027E7',
- "ropar;": '\U00002986',
- "ropf;": '\U0001D563',
- "roplus;": '\U00002A2E',
- "rotimes;": '\U00002A35',
- "rpar;": '\U00000029',
- "rpargt;": '\U00002994',
- "rppolint;": '\U00002A12',
- "rrarr;": '\U000021C9',
- "rsaquo;": '\U0000203A',
- "rscr;": '\U0001D4C7',
- "rsh;": '\U000021B1',
- "rsqb;": '\U0000005D',
- "rsquo;": '\U00002019',
- "rsquor;": '\U00002019',
- "rthree;": '\U000022CC',
- "rtimes;": '\U000022CA',
- "rtri;": '\U000025B9',
- "rtrie;": '\U000022B5',
- "rtrif;": '\U000025B8',
- "rtriltri;": '\U000029CE',
- "ruluhar;": '\U00002968',
- "rx;": '\U0000211E',
- "sacute;": '\U0000015B',
- "sbquo;": '\U0000201A',
- "sc;": '\U0000227B',
- "scE;": '\U00002AB4',
- "scap;": '\U00002AB8',
- "scaron;": '\U00000161',
- "sccue;": '\U0000227D',
- "sce;": '\U00002AB0',
- "scedil;": '\U0000015F',
- "scirc;": '\U0000015D',
- "scnE;": '\U00002AB6',
- "scnap;": '\U00002ABA',
- "scnsim;": '\U000022E9',
- "scpolint;": '\U00002A13',
- "scsim;": '\U0000227F',
- "scy;": '\U00000441',
- "sdot;": '\U000022C5',
- "sdotb;": '\U000022A1',
- "sdote;": '\U00002A66',
- "seArr;": '\U000021D8',
- "searhk;": '\U00002925',
- "searr;": '\U00002198',
- "searrow;": '\U00002198',
- "sect;": '\U000000A7',
- "semi;": '\U0000003B',
- "seswar;": '\U00002929',
- "setminus;": '\U00002216',
- "setmn;": '\U00002216',
- "sext;": '\U00002736',
- "sfr;": '\U0001D530',
- "sfrown;": '\U00002322',
- "sharp;": '\U0000266F',
- "shchcy;": '\U00000449',
- "shcy;": '\U00000448',
- "shortmid;": '\U00002223',
- "shortparallel;": '\U00002225',
- "shy;": '\U000000AD',
- "sigma;": '\U000003C3',
- "sigmaf;": '\U000003C2',
- "sigmav;": '\U000003C2',
- "sim;": '\U0000223C',
- "simdot;": '\U00002A6A',
- "sime;": '\U00002243',
- "simeq;": '\U00002243',
- "simg;": '\U00002A9E',
- "simgE;": '\U00002AA0',
- "siml;": '\U00002A9D',
- "simlE;": '\U00002A9F',
- "simne;": '\U00002246',
- "simplus;": '\U00002A24',
- "simrarr;": '\U00002972',
- "slarr;": '\U00002190',
- "smallsetminus;": '\U00002216',
- "smashp;": '\U00002A33',
- "smeparsl;": '\U000029E4',
- "smid;": '\U00002223',
- "smile;": '\U00002323',
- "smt;": '\U00002AAA',
- "smte;": '\U00002AAC',
- "softcy;": '\U0000044C',
- "sol;": '\U0000002F',
- "solb;": '\U000029C4',
- "solbar;": '\U0000233F',
- "sopf;": '\U0001D564',
- "spades;": '\U00002660',
- "spadesuit;": '\U00002660',
- "spar;": '\U00002225',
- "sqcap;": '\U00002293',
- "sqcup;": '\U00002294',
- "sqsub;": '\U0000228F',
- "sqsube;": '\U00002291',
- "sqsubset;": '\U0000228F',
- "sqsubseteq;": '\U00002291',
- "sqsup;": '\U00002290',
- "sqsupe;": '\U00002292',
- "sqsupset;": '\U00002290',
- "sqsupseteq;": '\U00002292',
- "squ;": '\U000025A1',
- "square;": '\U000025A1',
- "squarf;": '\U000025AA',
- "squf;": '\U000025AA',
- "srarr;": '\U00002192',
- "sscr;": '\U0001D4C8',
- "ssetmn;": '\U00002216',
- "ssmile;": '\U00002323',
- "sstarf;": '\U000022C6',
- "star;": '\U00002606',
- "starf;": '\U00002605',
- "straightepsilon;": '\U000003F5',
- "straightphi;": '\U000003D5',
- "strns;": '\U000000AF',
- "sub;": '\U00002282',
- "subE;": '\U00002AC5',
- "subdot;": '\U00002ABD',
- "sube;": '\U00002286',
- "subedot;": '\U00002AC3',
- "submult;": '\U00002AC1',
- "subnE;": '\U00002ACB',
- "subne;": '\U0000228A',
- "subplus;": '\U00002ABF',
- "subrarr;": '\U00002979',
- "subset;": '\U00002282',
- "subseteq;": '\U00002286',
- "subseteqq;": '\U00002AC5',
- "subsetneq;": '\U0000228A',
- "subsetneqq;": '\U00002ACB',
- "subsim;": '\U00002AC7',
- "subsub;": '\U00002AD5',
- "subsup;": '\U00002AD3',
- "succ;": '\U0000227B',
- "succapprox;": '\U00002AB8',
- "succcurlyeq;": '\U0000227D',
- "succeq;": '\U00002AB0',
- "succnapprox;": '\U00002ABA',
- "succneqq;": '\U00002AB6',
- "succnsim;": '\U000022E9',
- "succsim;": '\U0000227F',
- "sum;": '\U00002211',
- "sung;": '\U0000266A',
- "sup;": '\U00002283',
- "sup1;": '\U000000B9',
- "sup2;": '\U000000B2',
- "sup3;": '\U000000B3',
- "supE;": '\U00002AC6',
- "supdot;": '\U00002ABE',
- "supdsub;": '\U00002AD8',
- "supe;": '\U00002287',
- "supedot;": '\U00002AC4',
- "suphsol;": '\U000027C9',
- "suphsub;": '\U00002AD7',
- "suplarr;": '\U0000297B',
- "supmult;": '\U00002AC2',
- "supnE;": '\U00002ACC',
- "supne;": '\U0000228B',
- "supplus;": '\U00002AC0',
- "supset;": '\U00002283',
- "supseteq;": '\U00002287',
- "supseteqq;": '\U00002AC6',
- "supsetneq;": '\U0000228B',
- "supsetneqq;": '\U00002ACC',
- "supsim;": '\U00002AC8',
- "supsub;": '\U00002AD4',
- "supsup;": '\U00002AD6',
- "swArr;": '\U000021D9',
- "swarhk;": '\U00002926',
- "swarr;": '\U00002199',
- "swarrow;": '\U00002199',
- "swnwar;": '\U0000292A',
- "szlig;": '\U000000DF',
- "target;": '\U00002316',
- "tau;": '\U000003C4',
- "tbrk;": '\U000023B4',
- "tcaron;": '\U00000165',
- "tcedil;": '\U00000163',
- "tcy;": '\U00000442',
- "tdot;": '\U000020DB',
- "telrec;": '\U00002315',
- "tfr;": '\U0001D531',
- "there4;": '\U00002234',
- "therefore;": '\U00002234',
- "theta;": '\U000003B8',
- "thetasym;": '\U000003D1',
- "thetav;": '\U000003D1',
- "thickapprox;": '\U00002248',
- "thicksim;": '\U0000223C',
- "thinsp;": '\U00002009',
- "thkap;": '\U00002248',
- "thksim;": '\U0000223C',
- "thorn;": '\U000000FE',
- "tilde;": '\U000002DC',
- "times;": '\U000000D7',
- "timesb;": '\U000022A0',
- "timesbar;": '\U00002A31',
- "timesd;": '\U00002A30',
- "tint;": '\U0000222D',
- "toea;": '\U00002928',
- "top;": '\U000022A4',
- "topbot;": '\U00002336',
- "topcir;": '\U00002AF1',
- "topf;": '\U0001D565',
- "topfork;": '\U00002ADA',
- "tosa;": '\U00002929',
- "tprime;": '\U00002034',
- "trade;": '\U00002122',
- "triangle;": '\U000025B5',
- "triangledown;": '\U000025BF',
- "triangleleft;": '\U000025C3',
- "trianglelefteq;": '\U000022B4',
- "triangleq;": '\U0000225C',
- "triangleright;": '\U000025B9',
- "trianglerighteq;": '\U000022B5',
- "tridot;": '\U000025EC',
- "trie;": '\U0000225C',
- "triminus;": '\U00002A3A',
- "triplus;": '\U00002A39',
- "trisb;": '\U000029CD',
- "tritime;": '\U00002A3B',
- "trpezium;": '\U000023E2',
- "tscr;": '\U0001D4C9',
- "tscy;": '\U00000446',
- "tshcy;": '\U0000045B',
- "tstrok;": '\U00000167',
- "twixt;": '\U0000226C',
- "twoheadleftarrow;": '\U0000219E',
- "twoheadrightarrow;": '\U000021A0',
- "uArr;": '\U000021D1',
- "uHar;": '\U00002963',
- "uacute;": '\U000000FA',
- "uarr;": '\U00002191',
- "ubrcy;": '\U0000045E',
- "ubreve;": '\U0000016D',
- "ucirc;": '\U000000FB',
- "ucy;": '\U00000443',
- "udarr;": '\U000021C5',
- "udblac;": '\U00000171',
- "udhar;": '\U0000296E',
- "ufisht;": '\U0000297E',
- "ufr;": '\U0001D532',
- "ugrave;": '\U000000F9',
- "uharl;": '\U000021BF',
- "uharr;": '\U000021BE',
- "uhblk;": '\U00002580',
- "ulcorn;": '\U0000231C',
- "ulcorner;": '\U0000231C',
- "ulcrop;": '\U0000230F',
- "ultri;": '\U000025F8',
- "umacr;": '\U0000016B',
- "uml;": '\U000000A8',
- "uogon;": '\U00000173',
- "uopf;": '\U0001D566',
- "uparrow;": '\U00002191',
- "updownarrow;": '\U00002195',
- "upharpoonleft;": '\U000021BF',
- "upharpoonright;": '\U000021BE',
- "uplus;": '\U0000228E',
- "upsi;": '\U000003C5',
- "upsih;": '\U000003D2',
- "upsilon;": '\U000003C5',
- "upuparrows;": '\U000021C8',
- "urcorn;": '\U0000231D',
- "urcorner;": '\U0000231D',
- "urcrop;": '\U0000230E',
- "uring;": '\U0000016F',
- "urtri;": '\U000025F9',
- "uscr;": '\U0001D4CA',
- "utdot;": '\U000022F0',
- "utilde;": '\U00000169',
- "utri;": '\U000025B5',
- "utrif;": '\U000025B4',
- "uuarr;": '\U000021C8',
- "uuml;": '\U000000FC',
- "uwangle;": '\U000029A7',
- "vArr;": '\U000021D5',
- "vBar;": '\U00002AE8',
- "vBarv;": '\U00002AE9',
- "vDash;": '\U000022A8',
- "vangrt;": '\U0000299C',
- "varepsilon;": '\U000003F5',
- "varkappa;": '\U000003F0',
- "varnothing;": '\U00002205',
- "varphi;": '\U000003D5',
- "varpi;": '\U000003D6',
- "varpropto;": '\U0000221D',
- "varr;": '\U00002195',
- "varrho;": '\U000003F1',
- "varsigma;": '\U000003C2',
- "vartheta;": '\U000003D1',
- "vartriangleleft;": '\U000022B2',
- "vartriangleright;": '\U000022B3',
- "vcy;": '\U00000432',
- "vdash;": '\U000022A2',
- "vee;": '\U00002228',
- "veebar;": '\U000022BB',
- "veeeq;": '\U0000225A',
- "vellip;": '\U000022EE',
- "verbar;": '\U0000007C',
- "vert;": '\U0000007C',
- "vfr;": '\U0001D533',
- "vltri;": '\U000022B2',
- "vopf;": '\U0001D567',
- "vprop;": '\U0000221D',
- "vrtri;": '\U000022B3',
- "vscr;": '\U0001D4CB',
- "vzigzag;": '\U0000299A',
- "wcirc;": '\U00000175',
- "wedbar;": '\U00002A5F',
- "wedge;": '\U00002227',
- "wedgeq;": '\U00002259',
- "weierp;": '\U00002118',
- "wfr;": '\U0001D534',
- "wopf;": '\U0001D568',
- "wp;": '\U00002118',
- "wr;": '\U00002240',
- "wreath;": '\U00002240',
- "wscr;": '\U0001D4CC',
- "xcap;": '\U000022C2',
- "xcirc;": '\U000025EF',
- "xcup;": '\U000022C3',
- "xdtri;": '\U000025BD',
- "xfr;": '\U0001D535',
- "xhArr;": '\U000027FA',
- "xharr;": '\U000027F7',
- "xi;": '\U000003BE',
- "xlArr;": '\U000027F8',
- "xlarr;": '\U000027F5',
- "xmap;": '\U000027FC',
- "xnis;": '\U000022FB',
- "xodot;": '\U00002A00',
- "xopf;": '\U0001D569',
- "xoplus;": '\U00002A01',
- "xotime;": '\U00002A02',
- "xrArr;": '\U000027F9',
- "xrarr;": '\U000027F6',
- "xscr;": '\U0001D4CD',
- "xsqcup;": '\U00002A06',
- "xuplus;": '\U00002A04',
- "xutri;": '\U000025B3',
- "xvee;": '\U000022C1',
- "xwedge;": '\U000022C0',
- "yacute;": '\U000000FD',
- "yacy;": '\U0000044F',
- "ycirc;": '\U00000177',
- "ycy;": '\U0000044B',
- "yen;": '\U000000A5',
- "yfr;": '\U0001D536',
- "yicy;": '\U00000457',
- "yopf;": '\U0001D56A',
- "yscr;": '\U0001D4CE',
- "yucy;": '\U0000044E',
- "yuml;": '\U000000FF',
- "zacute;": '\U0000017A',
- "zcaron;": '\U0000017E',
- "zcy;": '\U00000437',
- "zdot;": '\U0000017C',
- "zeetrf;": '\U00002128',
- "zeta;": '\U000003B6',
- "zfr;": '\U0001D537',
- "zhcy;": '\U00000436',
- "zigrarr;": '\U000021DD',
- "zopf;": '\U0001D56B',
- "zscr;": '\U0001D4CF',
- "zwj;": '\U0000200D',
- "zwnj;": '\U0000200C',
- "AElig": '\U000000C6',
- "AMP": '\U00000026',
- "Aacute": '\U000000C1',
- "Acirc": '\U000000C2',
- "Agrave": '\U000000C0',
- "Aring": '\U000000C5',
- "Atilde": '\U000000C3',
- "Auml": '\U000000C4',
- "COPY": '\U000000A9',
- "Ccedil": '\U000000C7',
- "ETH": '\U000000D0',
- "Eacute": '\U000000C9',
- "Ecirc": '\U000000CA',
- "Egrave": '\U000000C8',
- "Euml": '\U000000CB',
- "GT": '\U0000003E',
- "Iacute": '\U000000CD',
- "Icirc": '\U000000CE',
- "Igrave": '\U000000CC',
- "Iuml": '\U000000CF',
- "LT": '\U0000003C',
- "Ntilde": '\U000000D1',
- "Oacute": '\U000000D3',
- "Ocirc": '\U000000D4',
- "Ograve": '\U000000D2',
- "Oslash": '\U000000D8',
- "Otilde": '\U000000D5',
- "Ouml": '\U000000D6',
- "QUOT": '\U00000022',
- "REG": '\U000000AE',
- "THORN": '\U000000DE',
- "Uacute": '\U000000DA',
- "Ucirc": '\U000000DB',
- "Ugrave": '\U000000D9',
- "Uuml": '\U000000DC',
- "Yacute": '\U000000DD',
- "aacute": '\U000000E1',
- "acirc": '\U000000E2',
- "acute": '\U000000B4',
- "aelig": '\U000000E6',
- "agrave": '\U000000E0',
- "amp": '\U00000026',
- "aring": '\U000000E5',
- "atilde": '\U000000E3',
- "auml": '\U000000E4',
- "brvbar": '\U000000A6',
- "ccedil": '\U000000E7',
- "cedil": '\U000000B8',
- "cent": '\U000000A2',
- "copy": '\U000000A9',
- "curren": '\U000000A4',
- "deg": '\U000000B0',
- "divide": '\U000000F7',
- "eacute": '\U000000E9',
- "ecirc": '\U000000EA',
- "egrave": '\U000000E8',
- "eth": '\U000000F0',
- "euml": '\U000000EB',
- "frac12": '\U000000BD',
- "frac14": '\U000000BC',
- "frac34": '\U000000BE',
- "gt": '\U0000003E',
- "iacute": '\U000000ED',
- "icirc": '\U000000EE',
- "iexcl": '\U000000A1',
- "igrave": '\U000000EC',
- "iquest": '\U000000BF',
- "iuml": '\U000000EF',
- "laquo": '\U000000AB',
- "lt": '\U0000003C',
- "macr": '\U000000AF',
- "micro": '\U000000B5',
- "middot": '\U000000B7',
- "nbsp": '\U000000A0',
- "not": '\U000000AC',
- "ntilde": '\U000000F1',
- "oacute": '\U000000F3',
- "ocirc": '\U000000F4',
- "ograve": '\U000000F2',
- "ordf": '\U000000AA',
- "ordm": '\U000000BA',
- "oslash": '\U000000F8',
- "otilde": '\U000000F5',
- "ouml": '\U000000F6',
- "para": '\U000000B6',
- "plusmn": '\U000000B1',
- "pound": '\U000000A3',
- "quot": '\U00000022',
- "raquo": '\U000000BB',
- "reg": '\U000000AE',
- "sect": '\U000000A7',
- "shy": '\U000000AD',
- "sup1": '\U000000B9',
- "sup2": '\U000000B2',
- "sup3": '\U000000B3',
- "szlig": '\U000000DF',
- "thorn": '\U000000FE',
- "times": '\U000000D7',
- "uacute": '\U000000FA',
- "ucirc": '\U000000FB',
- "ugrave": '\U000000F9',
- "uml": '\U000000A8',
- "uuml": '\U000000FC',
- "yacute": '\U000000FD',
- "yen": '\U000000A5',
- "yuml": '\U000000FF',
+ "Cross;": '\U00002A2F',
+ "Cscr;": '\U0001D49E',
+ "Cup;": '\U000022D3',
+ "CupCap;": '\U0000224D',
+ "DD;": '\U00002145',
+ "DDotrahd;": '\U00002911',
+ "DJcy;": '\U00000402',
+ "DScy;": '\U00000405',
+ "DZcy;": '\U0000040F',
+ "Dagger;": '\U00002021',
+ "Darr;": '\U000021A1',
+ "Dashv;": '\U00002AE4',
+ "Dcaron;": '\U0000010E',
+ "Dcy;": '\U00000414',
+ "Del;": '\U00002207',
+ "Delta;": '\U00000394',
+ "Dfr;": '\U0001D507',
+ "DiacriticalAcute;": '\U000000B4',
+ "DiacriticalDot;": '\U000002D9',
+ "DiacriticalDoubleAcute;": '\U000002DD',
+ "DiacriticalGrave;": '\U00000060',
+ "DiacriticalTilde;": '\U000002DC',
+ "Diamond;": '\U000022C4',
+ "DifferentialD;": '\U00002146',
+ "Dopf;": '\U0001D53B',
+ "Dot;": '\U000000A8',
+ "DotDot;": '\U000020DC',
+ "DotEqual;": '\U00002250',
+ "DoubleContourIntegral;": '\U0000222F',
+ "DoubleDot;": '\U000000A8',
+ "DoubleDownArrow;": '\U000021D3',
+ "DoubleLeftArrow;": '\U000021D0',
+ "DoubleLeftRightArrow;": '\U000021D4',
+ "DoubleLeftTee;": '\U00002AE4',
+ "DoubleLongLeftArrow;": '\U000027F8',
+ "DoubleLongLeftRightArrow;": '\U000027FA',
+ "DoubleLongRightArrow;": '\U000027F9',
+ "DoubleRightArrow;": '\U000021D2',
+ "DoubleRightTee;": '\U000022A8',
+ "DoubleUpArrow;": '\U000021D1',
+ "DoubleUpDownArrow;": '\U000021D5',
+ "DoubleVerticalBar;": '\U00002225',
+ "DownArrow;": '\U00002193',
+ "DownArrowBar;": '\U00002913',
+ "DownArrowUpArrow;": '\U000021F5',
+ "DownBreve;": '\U00000311',
+ "DownLeftRightVector;": '\U00002950',
+ "DownLeftTeeVector;": '\U0000295E',
+ "DownLeftVector;": '\U000021BD',
+ "DownLeftVectorBar;": '\U00002956',
+ "DownRightTeeVector;": '\U0000295F',
+ "DownRightVector;": '\U000021C1',
+ "DownRightVectorBar;": '\U00002957',
+ "DownTee;": '\U000022A4',
+ "DownTeeArrow;": '\U000021A7',
+ "Downarrow;": '\U000021D3',
+ "Dscr;": '\U0001D49F',
+ "Dstrok;": '\U00000110',
+ "ENG;": '\U0000014A',
+ "ETH;": '\U000000D0',
+ "Eacute;": '\U000000C9',
+ "Ecaron;": '\U0000011A',
+ "Ecirc;": '\U000000CA',
+ "Ecy;": '\U0000042D',
+ "Edot;": '\U00000116',
+ "Efr;": '\U0001D508',
+ "Egrave;": '\U000000C8',
+ "Element;": '\U00002208',
+ "Emacr;": '\U00000112',
+ "EmptySmallSquare;": '\U000025FB',
+ "EmptyVerySmallSquare;": '\U000025AB',
+ "Eogon;": '\U00000118',
+ "Eopf;": '\U0001D53C',
+ "Epsilon;": '\U00000395',
+ "Equal;": '\U00002A75',
+ "EqualTilde;": '\U00002242',
+ "Equilibrium;": '\U000021CC',
+ "Escr;": '\U00002130',
+ "Esim;": '\U00002A73',
+ "Eta;": '\U00000397',
+ "Euml;": '\U000000CB',
+ "Exists;": '\U00002203',
+ "ExponentialE;": '\U00002147',
+ "Fcy;": '\U00000424',
+ "Ffr;": '\U0001D509',
+ "FilledSmallSquare;": '\U000025FC',
+ "FilledVerySmallSquare;": '\U000025AA',
+ "Fopf;": '\U0001D53D',
+ "ForAll;": '\U00002200',
+ "Fouriertrf;": '\U00002131',
+ "Fscr;": '\U00002131',
+ "GJcy;": '\U00000403',
+ "GT;": '\U0000003E',
+ "Gamma;": '\U00000393',
+ "Gammad;": '\U000003DC',
+ "Gbreve;": '\U0000011E',
+ "Gcedil;": '\U00000122',
+ "Gcirc;": '\U0000011C',
+ "Gcy;": '\U00000413',
+ "Gdot;": '\U00000120',
+ "Gfr;": '\U0001D50A',
+ "Gg;": '\U000022D9',
+ "Gopf;": '\U0001D53E',
+ "GreaterEqual;": '\U00002265',
+ "GreaterEqualLess;": '\U000022DB',
+ "GreaterFullEqual;": '\U00002267',
+ "GreaterGreater;": '\U00002AA2',
+ "GreaterLess;": '\U00002277',
+ "GreaterSlantEqual;": '\U00002A7E',
+ "GreaterTilde;": '\U00002273',
+ "Gscr;": '\U0001D4A2',
+ "Gt;": '\U0000226B',
+ "HARDcy;": '\U0000042A',
+ "Hacek;": '\U000002C7',
+ "Hat;": '\U0000005E',
+ "Hcirc;": '\U00000124',
+ "Hfr;": '\U0000210C',
+ "HilbertSpace;": '\U0000210B',
+ "Hopf;": '\U0000210D',
+ "HorizontalLine;": '\U00002500',
+ "Hscr;": '\U0000210B',
+ "Hstrok;": '\U00000126',
+ "HumpDownHump;": '\U0000224E',
+ "HumpEqual;": '\U0000224F',
+ "IEcy;": '\U00000415',
+ "IJlig;": '\U00000132',
+ "IOcy;": '\U00000401',
+ "Iacute;": '\U000000CD',
+ "Icirc;": '\U000000CE',
+ "Icy;": '\U00000418',
+ "Idot;": '\U00000130',
+ "Ifr;": '\U00002111',
+ "Igrave;": '\U000000CC',
+ "Im;": '\U00002111',
+ "Imacr;": '\U0000012A',
+ "ImaginaryI;": '\U00002148',
+ "Implies;": '\U000021D2',
+ "Int;": '\U0000222C',
+ "Integral;": '\U0000222B',
+ "Intersection;": '\U000022C2',
+ "InvisibleComma;": '\U00002063',
+ "InvisibleTimes;": '\U00002062',
+ "Iogon;": '\U0000012E',
+ "Iopf;": '\U0001D540',
+ "Iota;": '\U00000399',
+ "Iscr;": '\U00002110',
+ "Itilde;": '\U00000128',
+ "Iukcy;": '\U00000406',
+ "Iuml;": '\U000000CF',
+ "Jcirc;": '\U00000134',
+ "Jcy;": '\U00000419',
+ "Jfr;": '\U0001D50D',
+ "Jopf;": '\U0001D541',
+ "Jscr;": '\U0001D4A5',
+ "Jsercy;": '\U00000408',
+ "Jukcy;": '\U00000404',
+ "KHcy;": '\U00000425',
+ "KJcy;": '\U0000040C',
+ "Kappa;": '\U0000039A',
+ "Kcedil;": '\U00000136',
+ "Kcy;": '\U0000041A',
+ "Kfr;": '\U0001D50E',
+ "Kopf;": '\U0001D542',
+ "Kscr;": '\U0001D4A6',
+ "LJcy;": '\U00000409',
+ "LT;": '\U0000003C',
+ "Lacute;": '\U00000139',
+ "Lambda;": '\U0000039B',
+ "Lang;": '\U000027EA',
+ "Laplacetrf;": '\U00002112',
+ "Larr;": '\U0000219E',
+ "Lcaron;": '\U0000013D',
+ "Lcedil;": '\U0000013B',
+ "Lcy;": '\U0000041B',
+ "LeftAngleBracket;": '\U000027E8',
+ "LeftArrow;": '\U00002190',
+ "LeftArrowBar;": '\U000021E4',
+ "LeftArrowRightArrow;": '\U000021C6',
+ "LeftCeiling;": '\U00002308',
+ "LeftDoubleBracket;": '\U000027E6',
+ "LeftDownTeeVector;": '\U00002961',
+ "LeftDownVector;": '\U000021C3',
+ "LeftDownVectorBar;": '\U00002959',
+ "LeftFloor;": '\U0000230A',
+ "LeftRightArrow;": '\U00002194',
+ "LeftRightVector;": '\U0000294E',
+ "LeftTee;": '\U000022A3',
+ "LeftTeeArrow;": '\U000021A4',
+ "LeftTeeVector;": '\U0000295A',
+ "LeftTriangle;": '\U000022B2',
+ "LeftTriangleBar;": '\U000029CF',
+ "LeftTriangleEqual;": '\U000022B4',
+ "LeftUpDownVector;": '\U00002951',
+ "LeftUpTeeVector;": '\U00002960',
+ "LeftUpVector;": '\U000021BF',
+ "LeftUpVectorBar;": '\U00002958',
+ "LeftVector;": '\U000021BC',
+ "LeftVectorBar;": '\U00002952',
+ "Leftarrow;": '\U000021D0',
+ "Leftrightarrow;": '\U000021D4',
+ "LessEqualGreater;": '\U000022DA',
+ "LessFullEqual;": '\U00002266',
+ "LessGreater;": '\U00002276',
+ "LessLess;": '\U00002AA1',
+ "LessSlantEqual;": '\U00002A7D',
+ "LessTilde;": '\U00002272',
+ "Lfr;": '\U0001D50F',
+ "Ll;": '\U000022D8',
+ "Lleftarrow;": '\U000021DA',
+ "Lmidot;": '\U0000013F',
+ "LongLeftArrow;": '\U000027F5',
+ "LongLeftRightArrow;": '\U000027F7',
+ "LongRightArrow;": '\U000027F6',
+ "Longleftarrow;": '\U000027F8',
+ "Longleftrightarrow;": '\U000027FA',
+ "Longrightarrow;": '\U000027F9',
+ "Lopf;": '\U0001D543',
+ "LowerLeftArrow;": '\U00002199',
+ "LowerRightArrow;": '\U00002198',
+ "Lscr;": '\U00002112',
+ "Lsh;": '\U000021B0',
+ "Lstrok;": '\U00000141',
+ "Lt;": '\U0000226A',
+ "Map;": '\U00002905',
+ "Mcy;": '\U0000041C',
+ "MediumSpace;": '\U0000205F',
+ "Mellintrf;": '\U00002133',
+ "Mfr;": '\U0001D510',
+ "MinusPlus;": '\U00002213',
+ "Mopf;": '\U0001D544',
+ "Mscr;": '\U00002133',
+ "Mu;": '\U0000039C',
+ "NJcy;": '\U0000040A',
+ "Nacute;": '\U00000143',
+ "Ncaron;": '\U00000147',
+ "Ncedil;": '\U00000145',
+ "Ncy;": '\U0000041D',
+ "NegativeMediumSpace;": '\U0000200B',
+ "NegativeThickSpace;": '\U0000200B',
+ "NegativeThinSpace;": '\U0000200B',
+ "NegativeVeryThinSpace;": '\U0000200B',
+ "NestedGreaterGreater;": '\U0000226B',
+ "NestedLessLess;": '\U0000226A',
+ "NewLine;": '\U0000000A',
+ "Nfr;": '\U0001D511',
+ "NoBreak;": '\U00002060',
+ "NonBreakingSpace;": '\U000000A0',
+ "Nopf;": '\U00002115',
+ "Not;": '\U00002AEC',
+ "NotCongruent;": '\U00002262',
+ "NotCupCap;": '\U0000226D',
+ "NotDoubleVerticalBar;": '\U00002226',
+ "NotElement;": '\U00002209',
+ "NotEqual;": '\U00002260',
+ "NotExists;": '\U00002204',
+ "NotGreater;": '\U0000226F',
+ "NotGreaterEqual;": '\U00002271',
+ "NotGreaterLess;": '\U00002279',
+ "NotGreaterTilde;": '\U00002275',
+ "NotLeftTriangle;": '\U000022EA',
+ "NotLeftTriangleEqual;": '\U000022EC',
+ "NotLess;": '\U0000226E',
+ "NotLessEqual;": '\U00002270',
+ "NotLessGreater;": '\U00002278',
+ "NotLessTilde;": '\U00002274',
+ "NotPrecedes;": '\U00002280',
+ "NotPrecedesSlantEqual;": '\U000022E0',
+ "NotReverseElement;": '\U0000220C',
+ "NotRightTriangle;": '\U000022EB',
+ "NotRightTriangleEqual;": '\U000022ED',
+ "NotSquareSubsetEqual;": '\U000022E2',
+ "NotSquareSupersetEqual;": '\U000022E3',
+ "NotSubsetEqual;": '\U00002288',
+ "NotSucceeds;": '\U00002281',
+ "NotSucceedsSlantEqual;": '\U000022E1',
+ "NotSupersetEqual;": '\U00002289',
+ "NotTilde;": '\U00002241',
+ "NotTildeEqual;": '\U00002244',
+ "NotTildeFullEqual;": '\U00002247',
+ "NotTildeTilde;": '\U00002249',
+ "NotVerticalBar;": '\U00002224',
+ "Nscr;": '\U0001D4A9',
+ "Ntilde;": '\U000000D1',
+ "Nu;": '\U0000039D',
+ "OElig;": '\U00000152',
+ "Oacute;": '\U000000D3',
+ "Ocirc;": '\U000000D4',
+ "Ocy;": '\U0000041E',
+ "Odblac;": '\U00000150',
+ "Ofr;": '\U0001D512',
+ "Ograve;": '\U000000D2',
+ "Omacr;": '\U0000014C',
+ "Omega;": '\U000003A9',
+ "Omicron;": '\U0000039F',
+ "Oopf;": '\U0001D546',
+ "OpenCurlyDoubleQuote;": '\U0000201C',
+ "OpenCurlyQuote;": '\U00002018',
+ "Or;": '\U00002A54',
+ "Oscr;": '\U0001D4AA',
+ "Oslash;": '\U000000D8',
+ "Otilde;": '\U000000D5',
+ "Otimes;": '\U00002A37',
+ "Ouml;": '\U000000D6',
+ "OverBar;": '\U0000203E',
+ "OverBrace;": '\U000023DE',
+ "OverBracket;": '\U000023B4',
+ "OverParenthesis;": '\U000023DC',
+ "PartialD;": '\U00002202',
+ "Pcy;": '\U0000041F',
+ "Pfr;": '\U0001D513',
+ "Phi;": '\U000003A6',
+ "Pi;": '\U000003A0',
+ "PlusMinus;": '\U000000B1',
+ "Poincareplane;": '\U0000210C',
+ "Popf;": '\U00002119',
+ "Pr;": '\U00002ABB',
+ "Precedes;": '\U0000227A',
+ "PrecedesEqual;": '\U00002AAF',
+ "PrecedesSlantEqual;": '\U0000227C',
+ "PrecedesTilde;": '\U0000227E',
+ "Prime;": '\U00002033',
+ "Product;": '\U0000220F',
+ "Proportion;": '\U00002237',
+ "Proportional;": '\U0000221D',
+ "Pscr;": '\U0001D4AB',
+ "Psi;": '\U000003A8',
+ "QUOT;": '\U00000022',
+ "Qfr;": '\U0001D514',
+ "Qopf;": '\U0000211A',
+ "Qscr;": '\U0001D4AC',
+ "RBarr;": '\U00002910',
+ "REG;": '\U000000AE',
+ "Racute;": '\U00000154',
+ "Rang;": '\U000027EB',
+ "Rarr;": '\U000021A0',
+ "Rarrtl;": '\U00002916',
+ "Rcaron;": '\U00000158',
+ "Rcedil;": '\U00000156',
+ "Rcy;": '\U00000420',
+ "Re;": '\U0000211C',
+ "ReverseElement;": '\U0000220B',
+ "ReverseEquilibrium;": '\U000021CB',
+ "ReverseUpEquilibrium;": '\U0000296F',
+ "Rfr;": '\U0000211C',
+ "Rho;": '\U000003A1',
+ "RightAngleBracket;": '\U000027E9',
+ "RightArrow;": '\U00002192',
+ "RightArrowBar;": '\U000021E5',
+ "RightArrowLeftArrow;": '\U000021C4',
+ "RightCeiling;": '\U00002309',
+ "RightDoubleBracket;": '\U000027E7',
+ "RightDownTeeVector;": '\U0000295D',
+ "RightDownVector;": '\U000021C2',
+ "RightDownVectorBar;": '\U00002955',
+ "RightFloor;": '\U0000230B',
+ "RightTee;": '\U000022A2',
+ "RightTeeArrow;": '\U000021A6',
+ "RightTeeVector;": '\U0000295B',
+ "RightTriangle;": '\U000022B3',
+ "RightTriangleBar;": '\U000029D0',
+ "RightTriangleEqual;": '\U000022B5',
+ "RightUpDownVector;": '\U0000294F',
+ "RightUpTeeVector;": '\U0000295C',
+ "RightUpVector;": '\U000021BE',
+ "RightUpVectorBar;": '\U00002954',
+ "RightVector;": '\U000021C0',
+ "RightVectorBar;": '\U00002953',
+ "Rightarrow;": '\U000021D2',
+ "Ropf;": '\U0000211D',
+ "RoundImplies;": '\U00002970',
+ "Rrightarrow;": '\U000021DB',
+ "Rscr;": '\U0000211B',
+ "Rsh;": '\U000021B1',
+ "RuleDelayed;": '\U000029F4',
+ "SHCHcy;": '\U00000429',
+ "SHcy;": '\U00000428',
+ "SOFTcy;": '\U0000042C',
+ "Sacute;": '\U0000015A',
+ "Sc;": '\U00002ABC',
+ "Scaron;": '\U00000160',
+ "Scedil;": '\U0000015E',
+ "Scirc;": '\U0000015C',
+ "Scy;": '\U00000421',
+ "Sfr;": '\U0001D516',
+ "ShortDownArrow;": '\U00002193',
+ "ShortLeftArrow;": '\U00002190',
+ "ShortRightArrow;": '\U00002192',
+ "ShortUpArrow;": '\U00002191',
+ "Sigma;": '\U000003A3',
+ "SmallCircle;": '\U00002218',
+ "Sopf;": '\U0001D54A',
+ "Sqrt;": '\U0000221A',
+ "Square;": '\U000025A1',
+ "SquareIntersection;": '\U00002293',
+ "SquareSubset;": '\U0000228F',
+ "SquareSubsetEqual;": '\U00002291',
+ "SquareSuperset;": '\U00002290',
+ "SquareSupersetEqual;": '\U00002292',
+ "SquareUnion;": '\U00002294',
+ "Sscr;": '\U0001D4AE',
+ "Star;": '\U000022C6',
+ "Sub;": '\U000022D0',
+ "Subset;": '\U000022D0',
+ "SubsetEqual;": '\U00002286',
+ "Succeeds;": '\U0000227B',
+ "SucceedsEqual;": '\U00002AB0',
+ "SucceedsSlantEqual;": '\U0000227D',
+ "SucceedsTilde;": '\U0000227F',
+ "SuchThat;": '\U0000220B',
+ "Sum;": '\U00002211',
+ "Sup;": '\U000022D1',
+ "Superset;": '\U00002283',
+ "SupersetEqual;": '\U00002287',
+ "Supset;": '\U000022D1',
+ "THORN;": '\U000000DE',
+ "TRADE;": '\U00002122',
+ "TSHcy;": '\U0000040B',
+ "TScy;": '\U00000426',
+ "Tab;": '\U00000009',
+ "Tau;": '\U000003A4',
+ "Tcaron;": '\U00000164',
+ "Tcedil;": '\U00000162',
+ "Tcy;": '\U00000422',
+ "Tfr;": '\U0001D517',
+ "Therefore;": '\U00002234',
+ "Theta;": '\U00000398',
+ "ThinSpace;": '\U00002009',
+ "Tilde;": '\U0000223C',
+ "TildeEqual;": '\U00002243',
+ "TildeFullEqual;": '\U00002245',
+ "TildeTilde;": '\U00002248',
+ "Topf;": '\U0001D54B',
+ "TripleDot;": '\U000020DB',
+ "Tscr;": '\U0001D4AF',
+ "Tstrok;": '\U00000166',
+ "Uacute;": '\U000000DA',
+ "Uarr;": '\U0000219F',
+ "Uarrocir;": '\U00002949',
+ "Ubrcy;": '\U0000040E',
+ "Ubreve;": '\U0000016C',
+ "Ucirc;": '\U000000DB',
+ "Ucy;": '\U00000423',
+ "Udblac;": '\U00000170',
+ "Ufr;": '\U0001D518',
+ "Ugrave;": '\U000000D9',
+ "Umacr;": '\U0000016A',
+ "UnderBar;": '\U0000005F',
+ "UnderBrace;": '\U000023DF',
+ "UnderBracket;": '\U000023B5',
+ "UnderParenthesis;": '\U000023DD',
+ "Union;": '\U000022C3',
+ "UnionPlus;": '\U0000228E',
+ "Uogon;": '\U00000172',
+ "Uopf;": '\U0001D54C',
+ "UpArrow;": '\U00002191',
+ "UpArrowBar;": '\U00002912',
+ "UpArrowDownArrow;": '\U000021C5',
+ "UpDownArrow;": '\U00002195',
+ "UpEquilibrium;": '\U0000296E',
+ "UpTee;": '\U000022A5',
+ "UpTeeArrow;": '\U000021A5',
+ "Uparrow;": '\U000021D1',
+ "Updownarrow;": '\U000021D5',
+ "UpperLeftArrow;": '\U00002196',
+ "UpperRightArrow;": '\U00002197',
+ "Upsi;": '\U000003D2',
+ "Upsilon;": '\U000003A5',
+ "Uring;": '\U0000016E',
+ "Uscr;": '\U0001D4B0',
+ "Utilde;": '\U00000168',
+ "Uuml;": '\U000000DC',
+ "VDash;": '\U000022AB',
+ "Vbar;": '\U00002AEB',
+ "Vcy;": '\U00000412',
+ "Vdash;": '\U000022A9',
+ "Vdashl;": '\U00002AE6',
+ "Vee;": '\U000022C1',
+ "Verbar;": '\U00002016',
+ "Vert;": '\U00002016',
+ "VerticalBar;": '\U00002223',
+ "VerticalLine;": '\U0000007C',
+ "VerticalSeparator;": '\U00002758',
+ "VerticalTilde;": '\U00002240',
+ "VeryThinSpace;": '\U0000200A',
+ "Vfr;": '\U0001D519',
+ "Vopf;": '\U0001D54D',
+ "Vscr;": '\U0001D4B1',
+ "Vvdash;": '\U000022AA',
+ "Wcirc;": '\U00000174',
+ "Wedge;": '\U000022C0',
+ "Wfr;": '\U0001D51A',
+ "Wopf;": '\U0001D54E',
+ "Wscr;": '\U0001D4B2',
+ "Xfr;": '\U0001D51B',
+ "Xi;": '\U0000039E',
+ "Xopf;": '\U0001D54F',
+ "Xscr;": '\U0001D4B3',
+ "YAcy;": '\U0000042F',
+ "YIcy;": '\U00000407',
+ "YUcy;": '\U0000042E',
+ "Yacute;": '\U000000DD',
+ "Ycirc;": '\U00000176',
+ "Ycy;": '\U0000042B',
+ "Yfr;": '\U0001D51C',
+ "Yopf;": '\U0001D550',
+ "Yscr;": '\U0001D4B4',
+ "Yuml;": '\U00000178',
+ "ZHcy;": '\U00000416',
+ "Zacute;": '\U00000179',
+ "Zcaron;": '\U0000017D',
+ "Zcy;": '\U00000417',
+ "Zdot;": '\U0000017B',
+ "ZeroWidthSpace;": '\U0000200B',
+ "Zeta;": '\U00000396',
+ "Zfr;": '\U00002128',
+ "Zopf;": '\U00002124',
+ "Zscr;": '\U0001D4B5',
+ "aacute;": '\U000000E1',
+ "abreve;": '\U00000103',
+ "ac;": '\U0000223E',
+ "acd;": '\U0000223F',
+ "acirc;": '\U000000E2',
+ "acute;": '\U000000B4',
+ "acy;": '\U00000430',
+ "aelig;": '\U000000E6',
+ "af;": '\U00002061',
+ "afr;": '\U0001D51E',
+ "agrave;": '\U000000E0',
+ "alefsym;": '\U00002135',
+ "aleph;": '\U00002135',
+ "alpha;": '\U000003B1',
+ "amacr;": '\U00000101',
+ "amalg;": '\U00002A3F',
+ "amp;": '\U00000026',
+ "and;": '\U00002227',
+ "andand;": '\U00002A55',
+ "andd;": '\U00002A5C',
+ "andslope;": '\U00002A58',
+ "andv;": '\U00002A5A',
+ "ang;": '\U00002220',
+ "ange;": '\U000029A4',
+ "angle;": '\U00002220',
+ "angmsd;": '\U00002221',
+ "angmsdaa;": '\U000029A8',
+ "angmsdab;": '\U000029A9',
+ "angmsdac;": '\U000029AA',
+ "angmsdad;": '\U000029AB',
+ "angmsdae;": '\U000029AC',
+ "angmsdaf;": '\U000029AD',
+ "angmsdag;": '\U000029AE',
+ "angmsdah;": '\U000029AF',
+ "angrt;": '\U0000221F',
+ "angrtvb;": '\U000022BE',
+ "angrtvbd;": '\U0000299D',
+ "angsph;": '\U00002222',
+ "angst;": '\U000000C5',
+ "angzarr;": '\U0000237C',
+ "aogon;": '\U00000105',
+ "aopf;": '\U0001D552',
+ "ap;": '\U00002248',
+ "apE;": '\U00002A70',
+ "apacir;": '\U00002A6F',
+ "ape;": '\U0000224A',
+ "apid;": '\U0000224B',
+ "apos;": '\U00000027',
+ "approx;": '\U00002248',
+ "approxeq;": '\U0000224A',
+ "aring;": '\U000000E5',
+ "ascr;": '\U0001D4B6',
+ "ast;": '\U0000002A',
+ "asymp;": '\U00002248',
+ "asympeq;": '\U0000224D',
+ "atilde;": '\U000000E3',
+ "auml;": '\U000000E4',
+ "awconint;": '\U00002233',
+ "awint;": '\U00002A11',
+ "bNot;": '\U00002AED',
+ "backcong;": '\U0000224C',
+ "backepsilon;": '\U000003F6',
+ "backprime;": '\U00002035',
+ "backsim;": '\U0000223D',
+ "backsimeq;": '\U000022CD',
+ "barvee;": '\U000022BD',
+ "barwed;": '\U00002305',
+ "barwedge;": '\U00002305',
+ "bbrk;": '\U000023B5',
+ "bbrktbrk;": '\U000023B6',
+ "bcong;": '\U0000224C',
+ "bcy;": '\U00000431',
+ "bdquo;": '\U0000201E',
+ "becaus;": '\U00002235',
+ "because;": '\U00002235',
+ "bemptyv;": '\U000029B0',
+ "bepsi;": '\U000003F6',
+ "bernou;": '\U0000212C',
+ "beta;": '\U000003B2',
+ "beth;": '\U00002136',
+ "between;": '\U0000226C',
+ "bfr;": '\U0001D51F',
+ "bigcap;": '\U000022C2',
+ "bigcirc;": '\U000025EF',
+ "bigcup;": '\U000022C3',
+ "bigodot;": '\U00002A00',
+ "bigoplus;": '\U00002A01',
+ "bigotimes;": '\U00002A02',
+ "bigsqcup;": '\U00002A06',
+ "bigstar;": '\U00002605',
+ "bigtriangledown;": '\U000025BD',
+ "bigtriangleup;": '\U000025B3',
+ "biguplus;": '\U00002A04',
+ "bigvee;": '\U000022C1',
+ "bigwedge;": '\U000022C0',
+ "bkarow;": '\U0000290D',
+ "blacklozenge;": '\U000029EB',
+ "blacksquare;": '\U000025AA',
+ "blacktriangle;": '\U000025B4',
+ "blacktriangledown;": '\U000025BE',
+ "blacktriangleleft;": '\U000025C2',
+ "blacktriangleright;": '\U000025B8',
+ "blank;": '\U00002423',
+ "blk12;": '\U00002592',
+ "blk14;": '\U00002591',
+ "blk34;": '\U00002593',
+ "block;": '\U00002588',
+ "bnot;": '\U00002310',
+ "bopf;": '\U0001D553',
+ "bot;": '\U000022A5',
+ "bottom;": '\U000022A5',
+ "bowtie;": '\U000022C8',
+ "boxDL;": '\U00002557',
+ "boxDR;": '\U00002554',
+ "boxDl;": '\U00002556',
+ "boxDr;": '\U00002553',
+ "boxH;": '\U00002550',
+ "boxHD;": '\U00002566',
+ "boxHU;": '\U00002569',
+ "boxHd;": '\U00002564',
+ "boxHu;": '\U00002567',
+ "boxUL;": '\U0000255D',
+ "boxUR;": '\U0000255A',
+ "boxUl;": '\U0000255C',
+ "boxUr;": '\U00002559',
+ "boxV;": '\U00002551',
+ "boxVH;": '\U0000256C',
+ "boxVL;": '\U00002563',
+ "boxVR;": '\U00002560',
+ "boxVh;": '\U0000256B',
+ "boxVl;": '\U00002562',
+ "boxVr;": '\U0000255F',
+ "boxbox;": '\U000029C9',
+ "boxdL;": '\U00002555',
+ "boxdR;": '\U00002552',
+ "boxdl;": '\U00002510',
+ "boxdr;": '\U0000250C',
+ "boxh;": '\U00002500',
+ "boxhD;": '\U00002565',
+ "boxhU;": '\U00002568',
+ "boxhd;": '\U0000252C',
+ "boxhu;": '\U00002534',
+ "boxminus;": '\U0000229F',
+ "boxplus;": '\U0000229E',
+ "boxtimes;": '\U000022A0',
+ "boxuL;": '\U0000255B',
+ "boxuR;": '\U00002558',
+ "boxul;": '\U00002518',
+ "boxur;": '\U00002514',
+ "boxv;": '\U00002502',
+ "boxvH;": '\U0000256A',
+ "boxvL;": '\U00002561',
+ "boxvR;": '\U0000255E',
+ "boxvh;": '\U0000253C',
+ "boxvl;": '\U00002524',
+ "boxvr;": '\U0000251C',
+ "bprime;": '\U00002035',
+ "breve;": '\U000002D8',
+ "brvbar;": '\U000000A6',
+ "bscr;": '\U0001D4B7',
+ "bsemi;": '\U0000204F',
+ "bsim;": '\U0000223D',
+ "bsime;": '\U000022CD',
+ "bsol;": '\U0000005C',
+ "bsolb;": '\U000029C5',
+ "bsolhsub;": '\U000027C8',
+ "bull;": '\U00002022',
+ "bullet;": '\U00002022',
+ "bump;": '\U0000224E',
+ "bumpE;": '\U00002AAE',
+ "bumpe;": '\U0000224F',
+ "bumpeq;": '\U0000224F',
+ "cacute;": '\U00000107',
+ "cap;": '\U00002229',
+ "capand;": '\U00002A44',
+ "capbrcup;": '\U00002A49',
+ "capcap;": '\U00002A4B',
+ "capcup;": '\U00002A47',
+ "capdot;": '\U00002A40',
+ "caret;": '\U00002041',
+ "caron;": '\U000002C7',
+ "ccaps;": '\U00002A4D',
+ "ccaron;": '\U0000010D',
+ "ccedil;": '\U000000E7',
+ "ccirc;": '\U00000109',
+ "ccups;": '\U00002A4C',
+ "ccupssm;": '\U00002A50',
+ "cdot;": '\U0000010B',
+ "cedil;": '\U000000B8',
+ "cemptyv;": '\U000029B2',
+ "cent;": '\U000000A2',
+ "centerdot;": '\U000000B7',
+ "cfr;": '\U0001D520',
+ "chcy;": '\U00000447',
+ "check;": '\U00002713',
+ "checkmark;": '\U00002713',
+ "chi;": '\U000003C7',
+ "cir;": '\U000025CB',
+ "cirE;": '\U000029C3',
+ "circ;": '\U000002C6',
+ "circeq;": '\U00002257',
+ "circlearrowleft;": '\U000021BA',
+ "circlearrowright;": '\U000021BB',
+ "circledR;": '\U000000AE',
+ "circledS;": '\U000024C8',
+ "circledast;": '\U0000229B',
+ "circledcirc;": '\U0000229A',
+ "circleddash;": '\U0000229D',
+ "cire;": '\U00002257',
+ "cirfnint;": '\U00002A10',
+ "cirmid;": '\U00002AEF',
+ "cirscir;": '\U000029C2',
+ "clubs;": '\U00002663',
+ "clubsuit;": '\U00002663',
+ "colon;": '\U0000003A',
+ "colone;": '\U00002254',
+ "coloneq;": '\U00002254',
+ "comma;": '\U0000002C',
+ "commat;": '\U00000040',
+ "comp;": '\U00002201',
+ "compfn;": '\U00002218',
+ "complement;": '\U00002201',
+ "complexes;": '\U00002102',
+ "cong;": '\U00002245',
+ "congdot;": '\U00002A6D',
+ "conint;": '\U0000222E',
+ "copf;": '\U0001D554',
+ "coprod;": '\U00002210',
+ "copy;": '\U000000A9',
+ "copysr;": '\U00002117',
+ "crarr;": '\U000021B5',
+ "cross;": '\U00002717',
+ "cscr;": '\U0001D4B8',
+ "csub;": '\U00002ACF',
+ "csube;": '\U00002AD1',
+ "csup;": '\U00002AD0',
+ "csupe;": '\U00002AD2',
+ "ctdot;": '\U000022EF',
+ "cudarrl;": '\U00002938',
+ "cudarrr;": '\U00002935',
+ "cuepr;": '\U000022DE',
+ "cuesc;": '\U000022DF',
+ "cularr;": '\U000021B6',
+ "cularrp;": '\U0000293D',
+ "cup;": '\U0000222A',
+ "cupbrcap;": '\U00002A48',
+ "cupcap;": '\U00002A46',
+ "cupcup;": '\U00002A4A',
+ "cupdot;": '\U0000228D',
+ "cupor;": '\U00002A45',
+ "curarr;": '\U000021B7',
+ "curarrm;": '\U0000293C',
+ "curlyeqprec;": '\U000022DE',
+ "curlyeqsucc;": '\U000022DF',
+ "curlyvee;": '\U000022CE',
+ "curlywedge;": '\U000022CF',
+ "curren;": '\U000000A4',
+ "curvearrowleft;": '\U000021B6',
+ "curvearrowright;": '\U000021B7',
+ "cuvee;": '\U000022CE',
+ "cuwed;": '\U000022CF',
+ "cwconint;": '\U00002232',
+ "cwint;": '\U00002231',
+ "cylcty;": '\U0000232D',
+ "dArr;": '\U000021D3',
+ "dHar;": '\U00002965',
+ "dagger;": '\U00002020',
+ "daleth;": '\U00002138',
+ "darr;": '\U00002193',
+ "dash;": '\U00002010',
+ "dashv;": '\U000022A3',
+ "dbkarow;": '\U0000290F',
+ "dblac;": '\U000002DD',
+ "dcaron;": '\U0000010F',
+ "dcy;": '\U00000434',
+ "dd;": '\U00002146',
+ "ddagger;": '\U00002021',
+ "ddarr;": '\U000021CA',
+ "ddotseq;": '\U00002A77',
+ "deg;": '\U000000B0',
+ "delta;": '\U000003B4',
+ "demptyv;": '\U000029B1',
+ "dfisht;": '\U0000297F',
+ "dfr;": '\U0001D521',
+ "dharl;": '\U000021C3',
+ "dharr;": '\U000021C2',
+ "diam;": '\U000022C4',
+ "diamond;": '\U000022C4',
+ "diamondsuit;": '\U00002666',
+ "diams;": '\U00002666',
+ "die;": '\U000000A8',
+ "digamma;": '\U000003DD',
+ "disin;": '\U000022F2',
+ "div;": '\U000000F7',
+ "divide;": '\U000000F7',
+ "divideontimes;": '\U000022C7',
+ "divonx;": '\U000022C7',
+ "djcy;": '\U00000452',
+ "dlcorn;": '\U0000231E',
+ "dlcrop;": '\U0000230D',
+ "dollar;": '\U00000024',
+ "dopf;": '\U0001D555',
+ "dot;": '\U000002D9',
+ "doteq;": '\U00002250',
+ "doteqdot;": '\U00002251',
+ "dotminus;": '\U00002238',
+ "dotplus;": '\U00002214',
+ "dotsquare;": '\U000022A1',
+ "doublebarwedge;": '\U00002306',
+ "downarrow;": '\U00002193',
+ "downdownarrows;": '\U000021CA',
+ "downharpoonleft;": '\U000021C3',
+ "downharpoonright;": '\U000021C2',
+ "drbkarow;": '\U00002910',
+ "drcorn;": '\U0000231F',
+ "drcrop;": '\U0000230C',
+ "dscr;": '\U0001D4B9',
+ "dscy;": '\U00000455',
+ "dsol;": '\U000029F6',
+ "dstrok;": '\U00000111',
+ "dtdot;": '\U000022F1',
+ "dtri;": '\U000025BF',
+ "dtrif;": '\U000025BE',
+ "duarr;": '\U000021F5',
+ "duhar;": '\U0000296F',
+ "dwangle;": '\U000029A6',
+ "dzcy;": '\U0000045F',
+ "dzigrarr;": '\U000027FF',
+ "eDDot;": '\U00002A77',
+ "eDot;": '\U00002251',
+ "eacute;": '\U000000E9',
+ "easter;": '\U00002A6E',
+ "ecaron;": '\U0000011B',
+ "ecir;": '\U00002256',
+ "ecirc;": '\U000000EA',
+ "ecolon;": '\U00002255',
+ "ecy;": '\U0000044D',
+ "edot;": '\U00000117',
+ "ee;": '\U00002147',
+ "efDot;": '\U00002252',
+ "efr;": '\U0001D522',
+ "eg;": '\U00002A9A',
+ "egrave;": '\U000000E8',
+ "egs;": '\U00002A96',
+ "egsdot;": '\U00002A98',
+ "el;": '\U00002A99',
+ "elinters;": '\U000023E7',
+ "ell;": '\U00002113',
+ "els;": '\U00002A95',
+ "elsdot;": '\U00002A97',
+ "emacr;": '\U00000113',
+ "empty;": '\U00002205',
+ "emptyset;": '\U00002205',
+ "emptyv;": '\U00002205',
+ "emsp;": '\U00002003',
+ "emsp13;": '\U00002004',
+ "emsp14;": '\U00002005',
+ "eng;": '\U0000014B',
+ "ensp;": '\U00002002',
+ "eogon;": '\U00000119',
+ "eopf;": '\U0001D556',
+ "epar;": '\U000022D5',
+ "eparsl;": '\U000029E3',
+ "eplus;": '\U00002A71',
+ "epsi;": '\U000003B5',
+ "epsilon;": '\U000003B5',
+ "epsiv;": '\U000003F5',
+ "eqcirc;": '\U00002256',
+ "eqcolon;": '\U00002255',
+ "eqsim;": '\U00002242',
+ "eqslantgtr;": '\U00002A96',
+ "eqslantless;": '\U00002A95',
+ "equals;": '\U0000003D',
+ "equest;": '\U0000225F',
+ "equiv;": '\U00002261',
+ "equivDD;": '\U00002A78',
+ "eqvparsl;": '\U000029E5',
+ "erDot;": '\U00002253',
+ "erarr;": '\U00002971',
+ "escr;": '\U0000212F',
+ "esdot;": '\U00002250',
+ "esim;": '\U00002242',
+ "eta;": '\U000003B7',
+ "eth;": '\U000000F0',
+ "euml;": '\U000000EB',
+ "euro;": '\U000020AC',
+ "excl;": '\U00000021',
+ "exist;": '\U00002203',
+ "expectation;": '\U00002130',
+ "exponentiale;": '\U00002147',
+ "fallingdotseq;": '\U00002252',
+ "fcy;": '\U00000444',
+ "female;": '\U00002640',
+ "ffilig;": '\U0000FB03',
+ "fflig;": '\U0000FB00',
+ "ffllig;": '\U0000FB04',
+ "ffr;": '\U0001D523',
+ "filig;": '\U0000FB01',
+ "flat;": '\U0000266D',
+ "fllig;": '\U0000FB02',
+ "fltns;": '\U000025B1',
+ "fnof;": '\U00000192',
+ "fopf;": '\U0001D557',
+ "forall;": '\U00002200',
+ "fork;": '\U000022D4',
+ "forkv;": '\U00002AD9',
+ "fpartint;": '\U00002A0D',
+ "frac12;": '\U000000BD',
+ "frac13;": '\U00002153',
+ "frac14;": '\U000000BC',
+ "frac15;": '\U00002155',
+ "frac16;": '\U00002159',
+ "frac18;": '\U0000215B',
+ "frac23;": '\U00002154',
+ "frac25;": '\U00002156',
+ "frac34;": '\U000000BE',
+ "frac35;": '\U00002157',
+ "frac38;": '\U0000215C',
+ "frac45;": '\U00002158',
+ "frac56;": '\U0000215A',
+ "frac58;": '\U0000215D',
+ "frac78;": '\U0000215E',
+ "frasl;": '\U00002044',
+ "frown;": '\U00002322',
+ "fscr;": '\U0001D4BB',
+ "gE;": '\U00002267',
+ "gEl;": '\U00002A8C',
+ "gacute;": '\U000001F5',
+ "gamma;": '\U000003B3',
+ "gammad;": '\U000003DD',
+ "gap;": '\U00002A86',
+ "gbreve;": '\U0000011F',
+ "gcirc;": '\U0000011D',
+ "gcy;": '\U00000433',
+ "gdot;": '\U00000121',
+ "ge;": '\U00002265',
+ "gel;": '\U000022DB',
+ "geq;": '\U00002265',
+ "geqq;": '\U00002267',
+ "geqslant;": '\U00002A7E',
+ "ges;": '\U00002A7E',
+ "gescc;": '\U00002AA9',
+ "gesdot;": '\U00002A80',
+ "gesdoto;": '\U00002A82',
+ "gesdotol;": '\U00002A84',
+ "gesles;": '\U00002A94',
+ "gfr;": '\U0001D524',
+ "gg;": '\U0000226B',
+ "ggg;": '\U000022D9',
+ "gimel;": '\U00002137',
+ "gjcy;": '\U00000453',
+ "gl;": '\U00002277',
+ "glE;": '\U00002A92',
+ "gla;": '\U00002AA5',
+ "glj;": '\U00002AA4',
+ "gnE;": '\U00002269',
+ "gnap;": '\U00002A8A',
+ "gnapprox;": '\U00002A8A',
+ "gne;": '\U00002A88',
+ "gneq;": '\U00002A88',
+ "gneqq;": '\U00002269',
+ "gnsim;": '\U000022E7',
+ "gopf;": '\U0001D558',
+ "grave;": '\U00000060',
+ "gscr;": '\U0000210A',
+ "gsim;": '\U00002273',
+ "gsime;": '\U00002A8E',
+ "gsiml;": '\U00002A90',
+ "gt;": '\U0000003E',
+ "gtcc;": '\U00002AA7',
+ "gtcir;": '\U00002A7A',
+ "gtdot;": '\U000022D7',
+ "gtlPar;": '\U00002995',
+ "gtquest;": '\U00002A7C',
+ "gtrapprox;": '\U00002A86',
+ "gtrarr;": '\U00002978',
+ "gtrdot;": '\U000022D7',
+ "gtreqless;": '\U000022DB',
+ "gtreqqless;": '\U00002A8C',
+ "gtrless;": '\U00002277',
+ "gtrsim;": '\U00002273',
+ "hArr;": '\U000021D4',
+ "hairsp;": '\U0000200A',
+ "half;": '\U000000BD',
+ "hamilt;": '\U0000210B',
+ "hardcy;": '\U0000044A',
+ "harr;": '\U00002194',
+ "harrcir;": '\U00002948',
+ "harrw;": '\U000021AD',
+ "hbar;": '\U0000210F',
+ "hcirc;": '\U00000125',
+ "hearts;": '\U00002665',
+ "heartsuit;": '\U00002665',
+ "hellip;": '\U00002026',
+ "hercon;": '\U000022B9',
+ "hfr;": '\U0001D525',
+ "hksearow;": '\U00002925',
+ "hkswarow;": '\U00002926',
+ "hoarr;": '\U000021FF',
+ "homtht;": '\U0000223B',
+ "hookleftarrow;": '\U000021A9',
+ "hookrightarrow;": '\U000021AA',
+ "hopf;": '\U0001D559',
+ "horbar;": '\U00002015',
+ "hscr;": '\U0001D4BD',
+ "hslash;": '\U0000210F',
+ "hstrok;": '\U00000127',
+ "hybull;": '\U00002043',
+ "hyphen;": '\U00002010',
+ "iacute;": '\U000000ED',
+ "ic;": '\U00002063',
+ "icirc;": '\U000000EE',
+ "icy;": '\U00000438',
+ "iecy;": '\U00000435',
+ "iexcl;": '\U000000A1',
+ "iff;": '\U000021D4',
+ "ifr;": '\U0001D526',
+ "igrave;": '\U000000EC',
+ "ii;": '\U00002148',
+ "iiiint;": '\U00002A0C',
+ "iiint;": '\U0000222D',
+ "iinfin;": '\U000029DC',
+ "iiota;": '\U00002129',
+ "ijlig;": '\U00000133',
+ "imacr;": '\U0000012B',
+ "image;": '\U00002111',
+ "imagline;": '\U00002110',
+ "imagpart;": '\U00002111',
+ "imath;": '\U00000131',
+ "imof;": '\U000022B7',
+ "imped;": '\U000001B5',
+ "in;": '\U00002208',
+ "incare;": '\U00002105',
+ "infin;": '\U0000221E',
+ "infintie;": '\U000029DD',
+ "inodot;": '\U00000131',
+ "int;": '\U0000222B',
+ "intcal;": '\U000022BA',
+ "integers;": '\U00002124',
+ "intercal;": '\U000022BA',
+ "intlarhk;": '\U00002A17',
+ "intprod;": '\U00002A3C',
+ "iocy;": '\U00000451',
+ "iogon;": '\U0000012F',
+ "iopf;": '\U0001D55A',
+ "iota;": '\U000003B9',
+ "iprod;": '\U00002A3C',
+ "iquest;": '\U000000BF',
+ "iscr;": '\U0001D4BE',
+ "isin;": '\U00002208',
+ "isinE;": '\U000022F9',
+ "isindot;": '\U000022F5',
+ "isins;": '\U000022F4',
+ "isinsv;": '\U000022F3',
+ "isinv;": '\U00002208',
+ "it;": '\U00002062',
+ "itilde;": '\U00000129',
+ "iukcy;": '\U00000456',
+ "iuml;": '\U000000EF',
+ "jcirc;": '\U00000135',
+ "jcy;": '\U00000439',
+ "jfr;": '\U0001D527',
+ "jmath;": '\U00000237',
+ "jopf;": '\U0001D55B',
+ "jscr;": '\U0001D4BF',
+ "jsercy;": '\U00000458',
+ "jukcy;": '\U00000454',
+ "kappa;": '\U000003BA',
+ "kappav;": '\U000003F0',
+ "kcedil;": '\U00000137',
+ "kcy;": '\U0000043A',
+ "kfr;": '\U0001D528',
+ "kgreen;": '\U00000138',
+ "khcy;": '\U00000445',
+ "kjcy;": '\U0000045C',
+ "kopf;": '\U0001D55C',
+ "kscr;": '\U0001D4C0',
+ "lAarr;": '\U000021DA',
+ "lArr;": '\U000021D0',
+ "lAtail;": '\U0000291B',
+ "lBarr;": '\U0000290E',
+ "lE;": '\U00002266',
+ "lEg;": '\U00002A8B',
+ "lHar;": '\U00002962',
+ "lacute;": '\U0000013A',
+ "laemptyv;": '\U000029B4',
+ "lagran;": '\U00002112',
+ "lambda;": '\U000003BB',
+ "lang;": '\U000027E8',
+ "langd;": '\U00002991',
+ "langle;": '\U000027E8',
+ "lap;": '\U00002A85',
+ "laquo;": '\U000000AB',
+ "larr;": '\U00002190',
+ "larrb;": '\U000021E4',
+ "larrbfs;": '\U0000291F',
+ "larrfs;": '\U0000291D',
+ "larrhk;": '\U000021A9',
+ "larrlp;": '\U000021AB',
+ "larrpl;": '\U00002939',
+ "larrsim;": '\U00002973',
+ "larrtl;": '\U000021A2',
+ "lat;": '\U00002AAB',
+ "latail;": '\U00002919',
+ "late;": '\U00002AAD',
+ "lbarr;": '\U0000290C',
+ "lbbrk;": '\U00002772',
+ "lbrace;": '\U0000007B',
+ "lbrack;": '\U0000005B',
+ "lbrke;": '\U0000298B',
+ "lbrksld;": '\U0000298F',
+ "lbrkslu;": '\U0000298D',
+ "lcaron;": '\U0000013E',
+ "lcedil;": '\U0000013C',
+ "lceil;": '\U00002308',
+ "lcub;": '\U0000007B',
+ "lcy;": '\U0000043B',
+ "ldca;": '\U00002936',
+ "ldquo;": '\U0000201C',
+ "ldquor;": '\U0000201E',
+ "ldrdhar;": '\U00002967',
+ "ldrushar;": '\U0000294B',
+ "ldsh;": '\U000021B2',
+ "le;": '\U00002264',
+ "leftarrow;": '\U00002190',
+ "leftarrowtail;": '\U000021A2',
+ "leftharpoondown;": '\U000021BD',
+ "leftharpoonup;": '\U000021BC',
+ "leftleftarrows;": '\U000021C7',
+ "leftrightarrow;": '\U00002194',
+ "leftrightarrows;": '\U000021C6',
+ "leftrightharpoons;": '\U000021CB',
+ "leftrightsquigarrow;": '\U000021AD',
+ "leftthreetimes;": '\U000022CB',
+ "leg;": '\U000022DA',
+ "leq;": '\U00002264',
+ "leqq;": '\U00002266',
+ "leqslant;": '\U00002A7D',
+ "les;": '\U00002A7D',
+ "lescc;": '\U00002AA8',
+ "lesdot;": '\U00002A7F',
+ "lesdoto;": '\U00002A81',
+ "lesdotor;": '\U00002A83',
+ "lesges;": '\U00002A93',
+ "lessapprox;": '\U00002A85',
+ "lessdot;": '\U000022D6',
+ "lesseqgtr;": '\U000022DA',
+ "lesseqqgtr;": '\U00002A8B',
+ "lessgtr;": '\U00002276',
+ "lesssim;": '\U00002272',
+ "lfisht;": '\U0000297C',
+ "lfloor;": '\U0000230A',
+ "lfr;": '\U0001D529',
+ "lg;": '\U00002276',
+ "lgE;": '\U00002A91',
+ "lhard;": '\U000021BD',
+ "lharu;": '\U000021BC',
+ "lharul;": '\U0000296A',
+ "lhblk;": '\U00002584',
+ "ljcy;": '\U00000459',
+ "ll;": '\U0000226A',
+ "llarr;": '\U000021C7',
+ "llcorner;": '\U0000231E',
+ "llhard;": '\U0000296B',
+ "lltri;": '\U000025FA',
+ "lmidot;": '\U00000140',
+ "lmoust;": '\U000023B0',
+ "lmoustache;": '\U000023B0',
+ "lnE;": '\U00002268',
+ "lnap;": '\U00002A89',
+ "lnapprox;": '\U00002A89',
+ "lne;": '\U00002A87',
+ "lneq;": '\U00002A87',
+ "lneqq;": '\U00002268',
+ "lnsim;": '\U000022E6',
+ "loang;": '\U000027EC',
+ "loarr;": '\U000021FD',
+ "lobrk;": '\U000027E6',
+ "longleftarrow;": '\U000027F5',
+ "longleftrightarrow;": '\U000027F7',
+ "longmapsto;": '\U000027FC',
+ "longrightarrow;": '\U000027F6',
+ "looparrowleft;": '\U000021AB',
+ "looparrowright;": '\U000021AC',
+ "lopar;": '\U00002985',
+ "lopf;": '\U0001D55D',
+ "loplus;": '\U00002A2D',
+ "lotimes;": '\U00002A34',
+ "lowast;": '\U00002217',
+ "lowbar;": '\U0000005F',
+ "loz;": '\U000025CA',
+ "lozenge;": '\U000025CA',
+ "lozf;": '\U000029EB',
+ "lpar;": '\U00000028',
+ "lparlt;": '\U00002993',
+ "lrarr;": '\U000021C6',
+ "lrcorner;": '\U0000231F',
+ "lrhar;": '\U000021CB',
+ "lrhard;": '\U0000296D',
+ "lrm;": '\U0000200E',
+ "lrtri;": '\U000022BF',
+ "lsaquo;": '\U00002039',
+ "lscr;": '\U0001D4C1',
+ "lsh;": '\U000021B0',
+ "lsim;": '\U00002272',
+ "lsime;": '\U00002A8D',
+ "lsimg;": '\U00002A8F',
+ "lsqb;": '\U0000005B',
+ "lsquo;": '\U00002018',
+ "lsquor;": '\U0000201A',
+ "lstrok;": '\U00000142',
+ "lt;": '\U0000003C',
+ "ltcc;": '\U00002AA6',
+ "ltcir;": '\U00002A79',
+ "ltdot;": '\U000022D6',
+ "lthree;": '\U000022CB',
+ "ltimes;": '\U000022C9',
+ "ltlarr;": '\U00002976',
+ "ltquest;": '\U00002A7B',
+ "ltrPar;": '\U00002996',
+ "ltri;": '\U000025C3',
+ "ltrie;": '\U000022B4',
+ "ltrif;": '\U000025C2',
+ "lurdshar;": '\U0000294A',
+ "luruhar;": '\U00002966',
+ "mDDot;": '\U0000223A',
+ "macr;": '\U000000AF',
+ "male;": '\U00002642',
+ "malt;": '\U00002720',
+ "maltese;": '\U00002720',
+ "map;": '\U000021A6',
+ "mapsto;": '\U000021A6',
+ "mapstodown;": '\U000021A7',
+ "mapstoleft;": '\U000021A4',
+ "mapstoup;": '\U000021A5',
+ "marker;": '\U000025AE',
+ "mcomma;": '\U00002A29',
+ "mcy;": '\U0000043C',
+ "mdash;": '\U00002014',
+ "measuredangle;": '\U00002221',
+ "mfr;": '\U0001D52A',
+ "mho;": '\U00002127',
+ "micro;": '\U000000B5',
+ "mid;": '\U00002223',
+ "midast;": '\U0000002A',
+ "midcir;": '\U00002AF0',
+ "middot;": '\U000000B7',
+ "minus;": '\U00002212',
+ "minusb;": '\U0000229F',
+ "minusd;": '\U00002238',
+ "minusdu;": '\U00002A2A',
+ "mlcp;": '\U00002ADB',
+ "mldr;": '\U00002026',
+ "mnplus;": '\U00002213',
+ "models;": '\U000022A7',
+ "mopf;": '\U0001D55E',
+ "mp;": '\U00002213',
+ "mscr;": '\U0001D4C2',
+ "mstpos;": '\U0000223E',
+ "mu;": '\U000003BC',
+ "multimap;": '\U000022B8',
+ "mumap;": '\U000022B8',
+ "nLeftarrow;": '\U000021CD',
+ "nLeftrightarrow;": '\U000021CE',
+ "nRightarrow;": '\U000021CF',
+ "nVDash;": '\U000022AF',
+ "nVdash;": '\U000022AE',
+ "nabla;": '\U00002207',
+ "nacute;": '\U00000144',
+ "nap;": '\U00002249',
+ "napos;": '\U00000149',
+ "napprox;": '\U00002249',
+ "natur;": '\U0000266E',
+ "natural;": '\U0000266E',
+ "naturals;": '\U00002115',
+ "nbsp;": '\U000000A0',
+ "ncap;": '\U00002A43',
+ "ncaron;": '\U00000148',
+ "ncedil;": '\U00000146',
+ "ncong;": '\U00002247',
+ "ncup;": '\U00002A42',
+ "ncy;": '\U0000043D',
+ "ndash;": '\U00002013',
+ "ne;": '\U00002260',
+ "neArr;": '\U000021D7',
+ "nearhk;": '\U00002924',
+ "nearr;": '\U00002197',
+ "nearrow;": '\U00002197',
+ "nequiv;": '\U00002262',
+ "nesear;": '\U00002928',
+ "nexist;": '\U00002204',
+ "nexists;": '\U00002204',
+ "nfr;": '\U0001D52B',
+ "nge;": '\U00002271',
+ "ngeq;": '\U00002271',
+ "ngsim;": '\U00002275',
+ "ngt;": '\U0000226F',
+ "ngtr;": '\U0000226F',
+ "nhArr;": '\U000021CE',
+ "nharr;": '\U000021AE',
+ "nhpar;": '\U00002AF2',
+ "ni;": '\U0000220B',
+ "nis;": '\U000022FC',
+ "nisd;": '\U000022FA',
+ "niv;": '\U0000220B',
+ "njcy;": '\U0000045A',
+ "nlArr;": '\U000021CD',
+ "nlarr;": '\U0000219A',
+ "nldr;": '\U00002025',
+ "nle;": '\U00002270',
+ "nleftarrow;": '\U0000219A',
+ "nleftrightarrow;": '\U000021AE',
+ "nleq;": '\U00002270',
+ "nless;": '\U0000226E',
+ "nlsim;": '\U00002274',
+ "nlt;": '\U0000226E',
+ "nltri;": '\U000022EA',
+ "nltrie;": '\U000022EC',
+ "nmid;": '\U00002224',
+ "nopf;": '\U0001D55F',
+ "not;": '\U000000AC',
+ "notin;": '\U00002209',
+ "notinva;": '\U00002209',
+ "notinvb;": '\U000022F7',
+ "notinvc;": '\U000022F6',
+ "notni;": '\U0000220C',
+ "notniva;": '\U0000220C',
+ "notnivb;": '\U000022FE',
+ "notnivc;": '\U000022FD',
+ "npar;": '\U00002226',
+ "nparallel;": '\U00002226',
+ "npolint;": '\U00002A14',
+ "npr;": '\U00002280',
+ "nprcue;": '\U000022E0',
+ "nprec;": '\U00002280',
+ "nrArr;": '\U000021CF',
+ "nrarr;": '\U0000219B',
+ "nrightarrow;": '\U0000219B',
+ "nrtri;": '\U000022EB',
+ "nrtrie;": '\U000022ED',
+ "nsc;": '\U00002281',
+ "nsccue;": '\U000022E1',
+ "nscr;": '\U0001D4C3',
+ "nshortmid;": '\U00002224',
+ "nshortparallel;": '\U00002226',
+ "nsim;": '\U00002241',
+ "nsime;": '\U00002244',
+ "nsimeq;": '\U00002244',
+ "nsmid;": '\U00002224',
+ "nspar;": '\U00002226',
+ "nsqsube;": '\U000022E2',
+ "nsqsupe;": '\U000022E3',
+ "nsub;": '\U00002284',
+ "nsube;": '\U00002288',
+ "nsubseteq;": '\U00002288',
+ "nsucc;": '\U00002281',
+ "nsup;": '\U00002285',
+ "nsupe;": '\U00002289',
+ "nsupseteq;": '\U00002289',
+ "ntgl;": '\U00002279',
+ "ntilde;": '\U000000F1',
+ "ntlg;": '\U00002278',
+ "ntriangleleft;": '\U000022EA',
+ "ntrianglelefteq;": '\U000022EC',
+ "ntriangleright;": '\U000022EB',
+ "ntrianglerighteq;": '\U000022ED',
+ "nu;": '\U000003BD',
+ "num;": '\U00000023',
+ "numero;": '\U00002116',
+ "numsp;": '\U00002007',
+ "nvDash;": '\U000022AD',
+ "nvHarr;": '\U00002904',
+ "nvdash;": '\U000022AC',
+ "nvinfin;": '\U000029DE',
+ "nvlArr;": '\U00002902',
+ "nvrArr;": '\U00002903',
+ "nwArr;": '\U000021D6',
+ "nwarhk;": '\U00002923',
+ "nwarr;": '\U00002196',
+ "nwarrow;": '\U00002196',
+ "nwnear;": '\U00002927',
+ "oS;": '\U000024C8',
+ "oacute;": '\U000000F3',
+ "oast;": '\U0000229B',
+ "ocir;": '\U0000229A',
+ "ocirc;": '\U000000F4',
+ "ocy;": '\U0000043E',
+ "odash;": '\U0000229D',
+ "odblac;": '\U00000151',
+ "odiv;": '\U00002A38',
+ "odot;": '\U00002299',
+ "odsold;": '\U000029BC',
+ "oelig;": '\U00000153',
+ "ofcir;": '\U000029BF',
+ "ofr;": '\U0001D52C',
+ "ogon;": '\U000002DB',
+ "ograve;": '\U000000F2',
+ "ogt;": '\U000029C1',
+ "ohbar;": '\U000029B5',
+ "ohm;": '\U000003A9',
+ "oint;": '\U0000222E',
+ "olarr;": '\U000021BA',
+ "olcir;": '\U000029BE',
+ "olcross;": '\U000029BB',
+ "oline;": '\U0000203E',
+ "olt;": '\U000029C0',
+ "omacr;": '\U0000014D',
+ "omega;": '\U000003C9',
+ "omicron;": '\U000003BF',
+ "omid;": '\U000029B6',
+ "ominus;": '\U00002296',
+ "oopf;": '\U0001D560',
+ "opar;": '\U000029B7',
+ "operp;": '\U000029B9',
+ "oplus;": '\U00002295',
+ "or;": '\U00002228',
+ "orarr;": '\U000021BB',
+ "ord;": '\U00002A5D',
+ "order;": '\U00002134',
+ "orderof;": '\U00002134',
+ "ordf;": '\U000000AA',
+ "ordm;": '\U000000BA',
+ "origof;": '\U000022B6',
+ "oror;": '\U00002A56',
+ "orslope;": '\U00002A57',
+ "orv;": '\U00002A5B',
+ "oscr;": '\U00002134',
+ "oslash;": '\U000000F8',
+ "osol;": '\U00002298',
+ "otilde;": '\U000000F5',
+ "otimes;": '\U00002297',
+ "otimesas;": '\U00002A36',
+ "ouml;": '\U000000F6',
+ "ovbar;": '\U0000233D',
+ "par;": '\U00002225',
+ "para;": '\U000000B6',
+ "parallel;": '\U00002225',
+ "parsim;": '\U00002AF3',
+ "parsl;": '\U00002AFD',
+ "part;": '\U00002202',
+ "pcy;": '\U0000043F',
+ "percnt;": '\U00000025',
+ "period;": '\U0000002E',
+ "permil;": '\U00002030',
+ "perp;": '\U000022A5',
+ "pertenk;": '\U00002031',
+ "pfr;": '\U0001D52D',
+ "phi;": '\U000003C6',
+ "phiv;": '\U000003D5',
+ "phmmat;": '\U00002133',
+ "phone;": '\U0000260E',
+ "pi;": '\U000003C0',
+ "pitchfork;": '\U000022D4',
+ "piv;": '\U000003D6',
+ "planck;": '\U0000210F',
+ "planckh;": '\U0000210E',
+ "plankv;": '\U0000210F',
+ "plus;": '\U0000002B',
+ "plusacir;": '\U00002A23',
+ "plusb;": '\U0000229E',
+ "pluscir;": '\U00002A22',
+ "plusdo;": '\U00002214',
+ "plusdu;": '\U00002A25',
+ "pluse;": '\U00002A72',
+ "plusmn;": '\U000000B1',
+ "plussim;": '\U00002A26',
+ "plustwo;": '\U00002A27',
+ "pm;": '\U000000B1',
+ "pointint;": '\U00002A15',
+ "popf;": '\U0001D561',
+ "pound;": '\U000000A3',
+ "pr;": '\U0000227A',
+ "prE;": '\U00002AB3',
+ "prap;": '\U00002AB7',
+ "prcue;": '\U0000227C',
+ "pre;": '\U00002AAF',
+ "prec;": '\U0000227A',
+ "precapprox;": '\U00002AB7',
+ "preccurlyeq;": '\U0000227C',
+ "preceq;": '\U00002AAF',
+ "precnapprox;": '\U00002AB9',
+ "precneqq;": '\U00002AB5',
+ "precnsim;": '\U000022E8',
+ "precsim;": '\U0000227E',
+ "prime;": '\U00002032',
+ "primes;": '\U00002119',
+ "prnE;": '\U00002AB5',
+ "prnap;": '\U00002AB9',
+ "prnsim;": '\U000022E8',
+ "prod;": '\U0000220F',
+ "profalar;": '\U0000232E',
+ "profline;": '\U00002312',
+ "profsurf;": '\U00002313',
+ "prop;": '\U0000221D',
+ "propto;": '\U0000221D',
+ "prsim;": '\U0000227E',
+ "prurel;": '\U000022B0',
+ "pscr;": '\U0001D4C5',
+ "psi;": '\U000003C8',
+ "puncsp;": '\U00002008',
+ "qfr;": '\U0001D52E',
+ "qint;": '\U00002A0C',
+ "qopf;": '\U0001D562',
+ "qprime;": '\U00002057',
+ "qscr;": '\U0001D4C6',
+ "quaternions;": '\U0000210D',
+ "quatint;": '\U00002A16',
+ "quest;": '\U0000003F',
+ "questeq;": '\U0000225F',
+ "quot;": '\U00000022',
+ "rAarr;": '\U000021DB',
+ "rArr;": '\U000021D2',
+ "rAtail;": '\U0000291C',
+ "rBarr;": '\U0000290F',
+ "rHar;": '\U00002964',
+ "racute;": '\U00000155',
+ "radic;": '\U0000221A',
+ "raemptyv;": '\U000029B3',
+ "rang;": '\U000027E9',
+ "rangd;": '\U00002992',
+ "range;": '\U000029A5',
+ "rangle;": '\U000027E9',
+ "raquo;": '\U000000BB',
+ "rarr;": '\U00002192',
+ "rarrap;": '\U00002975',
+ "rarrb;": '\U000021E5',
+ "rarrbfs;": '\U00002920',
+ "rarrc;": '\U00002933',
+ "rarrfs;": '\U0000291E',
+ "rarrhk;": '\U000021AA',
+ "rarrlp;": '\U000021AC',
+ "rarrpl;": '\U00002945',
+ "rarrsim;": '\U00002974',
+ "rarrtl;": '\U000021A3',
+ "rarrw;": '\U0000219D',
+ "ratail;": '\U0000291A',
+ "ratio;": '\U00002236',
+ "rationals;": '\U0000211A',
+ "rbarr;": '\U0000290D',
+ "rbbrk;": '\U00002773',
+ "rbrace;": '\U0000007D',
+ "rbrack;": '\U0000005D',
+ "rbrke;": '\U0000298C',
+ "rbrksld;": '\U0000298E',
+ "rbrkslu;": '\U00002990',
+ "rcaron;": '\U00000159',
+ "rcedil;": '\U00000157',
+ "rceil;": '\U00002309',
+ "rcub;": '\U0000007D',
+ "rcy;": '\U00000440',
+ "rdca;": '\U00002937',
+ "rdldhar;": '\U00002969',
+ "rdquo;": '\U0000201D',
+ "rdquor;": '\U0000201D',
+ "rdsh;": '\U000021B3',
+ "real;": '\U0000211C',
+ "realine;": '\U0000211B',
+ "realpart;": '\U0000211C',
+ "reals;": '\U0000211D',
+ "rect;": '\U000025AD',
+ "reg;": '\U000000AE',
+ "rfisht;": '\U0000297D',
+ "rfloor;": '\U0000230B',
+ "rfr;": '\U0001D52F',
+ "rhard;": '\U000021C1',
+ "rharu;": '\U000021C0',
+ "rharul;": '\U0000296C',
+ "rho;": '\U000003C1',
+ "rhov;": '\U000003F1',
+ "rightarrow;": '\U00002192',
+ "rightarrowtail;": '\U000021A3',
+ "rightharpoondown;": '\U000021C1',
+ "rightharpoonup;": '\U000021C0',
+ "rightleftarrows;": '\U000021C4',
+ "rightleftharpoons;": '\U000021CC',
+ "rightrightarrows;": '\U000021C9',
+ "rightsquigarrow;": '\U0000219D',
+ "rightthreetimes;": '\U000022CC',
+ "ring;": '\U000002DA',
+ "risingdotseq;": '\U00002253',
+ "rlarr;": '\U000021C4',
+ "rlhar;": '\U000021CC',
+ "rlm;": '\U0000200F',
+ "rmoust;": '\U000023B1',
+ "rmoustache;": '\U000023B1',
+ "rnmid;": '\U00002AEE',
+ "roang;": '\U000027ED',
+ "roarr;": '\U000021FE',
+ "robrk;": '\U000027E7',
+ "ropar;": '\U00002986',
+ "ropf;": '\U0001D563',
+ "roplus;": '\U00002A2E',
+ "rotimes;": '\U00002A35',
+ "rpar;": '\U00000029',
+ "rpargt;": '\U00002994',
+ "rppolint;": '\U00002A12',
+ "rrarr;": '\U000021C9',
+ "rsaquo;": '\U0000203A',
+ "rscr;": '\U0001D4C7',
+ "rsh;": '\U000021B1',
+ "rsqb;": '\U0000005D',
+ "rsquo;": '\U00002019',
+ "rsquor;": '\U00002019',
+ "rthree;": '\U000022CC',
+ "rtimes;": '\U000022CA',
+ "rtri;": '\U000025B9',
+ "rtrie;": '\U000022B5',
+ "rtrif;": '\U000025B8',
+ "rtriltri;": '\U000029CE',
+ "ruluhar;": '\U00002968',
+ "rx;": '\U0000211E',
+ "sacute;": '\U0000015B',
+ "sbquo;": '\U0000201A',
+ "sc;": '\U0000227B',
+ "scE;": '\U00002AB4',
+ "scap;": '\U00002AB8',
+ "scaron;": '\U00000161',
+ "sccue;": '\U0000227D',
+ "sce;": '\U00002AB0',
+ "scedil;": '\U0000015F',
+ "scirc;": '\U0000015D',
+ "scnE;": '\U00002AB6',
+ "scnap;": '\U00002ABA',
+ "scnsim;": '\U000022E9',
+ "scpolint;": '\U00002A13',
+ "scsim;": '\U0000227F',
+ "scy;": '\U00000441',
+ "sdot;": '\U000022C5',
+ "sdotb;": '\U000022A1',
+ "sdote;": '\U00002A66',
+ "seArr;": '\U000021D8',
+ "searhk;": '\U00002925',
+ "searr;": '\U00002198',
+ "searrow;": '\U00002198',
+ "sect;": '\U000000A7',
+ "semi;": '\U0000003B',
+ "seswar;": '\U00002929',
+ "setminus;": '\U00002216',
+ "setmn;": '\U00002216',
+ "sext;": '\U00002736',
+ "sfr;": '\U0001D530',
+ "sfrown;": '\U00002322',
+ "sharp;": '\U0000266F',
+ "shchcy;": '\U00000449',
+ "shcy;": '\U00000448',
+ "shortmid;": '\U00002223',
+ "shortparallel;": '\U00002225',
+ "shy;": '\U000000AD',
+ "sigma;": '\U000003C3',
+ "sigmaf;": '\U000003C2',
+ "sigmav;": '\U000003C2',
+ "sim;": '\U0000223C',
+ "simdot;": '\U00002A6A',
+ "sime;": '\U00002243',
+ "simeq;": '\U00002243',
+ "simg;": '\U00002A9E',
+ "simgE;": '\U00002AA0',
+ "siml;": '\U00002A9D',
+ "simlE;": '\U00002A9F',
+ "simne;": '\U00002246',
+ "simplus;": '\U00002A24',
+ "simrarr;": '\U00002972',
+ "slarr;": '\U00002190',
+ "smallsetminus;": '\U00002216',
+ "smashp;": '\U00002A33',
+ "smeparsl;": '\U000029E4',
+ "smid;": '\U00002223',
+ "smile;": '\U00002323',
+ "smt;": '\U00002AAA',
+ "smte;": '\U00002AAC',
+ "softcy;": '\U0000044C',
+ "sol;": '\U0000002F',
+ "solb;": '\U000029C4',
+ "solbar;": '\U0000233F',
+ "sopf;": '\U0001D564',
+ "spades;": '\U00002660',
+ "spadesuit;": '\U00002660',
+ "spar;": '\U00002225',
+ "sqcap;": '\U00002293',
+ "sqcup;": '\U00002294',
+ "sqsub;": '\U0000228F',
+ "sqsube;": '\U00002291',
+ "sqsubset;": '\U0000228F',
+ "sqsubseteq;": '\U00002291',
+ "sqsup;": '\U00002290',
+ "sqsupe;": '\U00002292',
+ "sqsupset;": '\U00002290',
+ "sqsupseteq;": '\U00002292',
+ "squ;": '\U000025A1',
+ "square;": '\U000025A1',
+ "squarf;": '\U000025AA',
+ "squf;": '\U000025AA',
+ "srarr;": '\U00002192',
+ "sscr;": '\U0001D4C8',
+ "ssetmn;": '\U00002216',
+ "ssmile;": '\U00002323',
+ "sstarf;": '\U000022C6',
+ "star;": '\U00002606',
+ "starf;": '\U00002605',
+ "straightepsilon;": '\U000003F5',
+ "straightphi;": '\U000003D5',
+ "strns;": '\U000000AF',
+ "sub;": '\U00002282',
+ "subE;": '\U00002AC5',
+ "subdot;": '\U00002ABD',
+ "sube;": '\U00002286',
+ "subedot;": '\U00002AC3',
+ "submult;": '\U00002AC1',
+ "subnE;": '\U00002ACB',
+ "subne;": '\U0000228A',
+ "subplus;": '\U00002ABF',
+ "subrarr;": '\U00002979',
+ "subset;": '\U00002282',
+ "subseteq;": '\U00002286',
+ "subseteqq;": '\U00002AC5',
+ "subsetneq;": '\U0000228A',
+ "subsetneqq;": '\U00002ACB',
+ "subsim;": '\U00002AC7',
+ "subsub;": '\U00002AD5',
+ "subsup;": '\U00002AD3',
+ "succ;": '\U0000227B',
+ "succapprox;": '\U00002AB8',
+ "succcurlyeq;": '\U0000227D',
+ "succeq;": '\U00002AB0',
+ "succnapprox;": '\U00002ABA',
+ "succneqq;": '\U00002AB6',
+ "succnsim;": '\U000022E9',
+ "succsim;": '\U0000227F',
+ "sum;": '\U00002211',
+ "sung;": '\U0000266A',
+ "sup;": '\U00002283',
+ "sup1;": '\U000000B9',
+ "sup2;": '\U000000B2',
+ "sup3;": '\U000000B3',
+ "supE;": '\U00002AC6',
+ "supdot;": '\U00002ABE',
+ "supdsub;": '\U00002AD8',
+ "supe;": '\U00002287',
+ "supedot;": '\U00002AC4',
+ "suphsol;": '\U000027C9',
+ "suphsub;": '\U00002AD7',
+ "suplarr;": '\U0000297B',
+ "supmult;": '\U00002AC2',
+ "supnE;": '\U00002ACC',
+ "supne;": '\U0000228B',
+ "supplus;": '\U00002AC0',
+ "supset;": '\U00002283',
+ "supseteq;": '\U00002287',
+ "supseteqq;": '\U00002AC6',
+ "supsetneq;": '\U0000228B',
+ "supsetneqq;": '\U00002ACC',
+ "supsim;": '\U00002AC8',
+ "supsub;": '\U00002AD4',
+ "supsup;": '\U00002AD6',
+ "swArr;": '\U000021D9',
+ "swarhk;": '\U00002926',
+ "swarr;": '\U00002199',
+ "swarrow;": '\U00002199',
+ "swnwar;": '\U0000292A',
+ "szlig;": '\U000000DF',
+ "target;": '\U00002316',
+ "tau;": '\U000003C4',
+ "tbrk;": '\U000023B4',
+ "tcaron;": '\U00000165',
+ "tcedil;": '\U00000163',
+ "tcy;": '\U00000442',
+ "tdot;": '\U000020DB',
+ "telrec;": '\U00002315',
+ "tfr;": '\U0001D531',
+ "there4;": '\U00002234',
+ "therefore;": '\U00002234',
+ "theta;": '\U000003B8',
+ "thetasym;": '\U000003D1',
+ "thetav;": '\U000003D1',
+ "thickapprox;": '\U00002248',
+ "thicksim;": '\U0000223C',
+ "thinsp;": '\U00002009',
+ "thkap;": '\U00002248',
+ "thksim;": '\U0000223C',
+ "thorn;": '\U000000FE',
+ "tilde;": '\U000002DC',
+ "times;": '\U000000D7',
+ "timesb;": '\U000022A0',
+ "timesbar;": '\U00002A31',
+ "timesd;": '\U00002A30',
+ "tint;": '\U0000222D',
+ "toea;": '\U00002928',
+ "top;": '\U000022A4',
+ "topbot;": '\U00002336',
+ "topcir;": '\U00002AF1',
+ "topf;": '\U0001D565',
+ "topfork;": '\U00002ADA',
+ "tosa;": '\U00002929',
+ "tprime;": '\U00002034',
+ "trade;": '\U00002122',
+ "triangle;": '\U000025B5',
+ "triangledown;": '\U000025BF',
+ "triangleleft;": '\U000025C3',
+ "trianglelefteq;": '\U000022B4',
+ "triangleq;": '\U0000225C',
+ "triangleright;": '\U000025B9',
+ "trianglerighteq;": '\U000022B5',
+ "tridot;": '\U000025EC',
+ "trie;": '\U0000225C',
+ "triminus;": '\U00002A3A',
+ "triplus;": '\U00002A39',
+ "trisb;": '\U000029CD',
+ "tritime;": '\U00002A3B',
+ "trpezium;": '\U000023E2',
+ "tscr;": '\U0001D4C9',
+ "tscy;": '\U00000446',
+ "tshcy;": '\U0000045B',
+ "tstrok;": '\U00000167',
+ "twixt;": '\U0000226C',
+ "twoheadleftarrow;": '\U0000219E',
+ "twoheadrightarrow;": '\U000021A0',
+ "uArr;": '\U000021D1',
+ "uHar;": '\U00002963',
+ "uacute;": '\U000000FA',
+ "uarr;": '\U00002191',
+ "ubrcy;": '\U0000045E',
+ "ubreve;": '\U0000016D',
+ "ucirc;": '\U000000FB',
+ "ucy;": '\U00000443',
+ "udarr;": '\U000021C5',
+ "udblac;": '\U00000171',
+ "udhar;": '\U0000296E',
+ "ufisht;": '\U0000297E',
+ "ufr;": '\U0001D532',
+ "ugrave;": '\U000000F9',
+ "uharl;": '\U000021BF',
+ "uharr;": '\U000021BE',
+ "uhblk;": '\U00002580',
+ "ulcorn;": '\U0000231C',
+ "ulcorner;": '\U0000231C',
+ "ulcrop;": '\U0000230F',
+ "ultri;": '\U000025F8',
+ "umacr;": '\U0000016B',
+ "uml;": '\U000000A8',
+ "uogon;": '\U00000173',
+ "uopf;": '\U0001D566',
+ "uparrow;": '\U00002191',
+ "updownarrow;": '\U00002195',
+ "upharpoonleft;": '\U000021BF',
+ "upharpoonright;": '\U000021BE',
+ "uplus;": '\U0000228E',
+ "upsi;": '\U000003C5',
+ "upsih;": '\U000003D2',
+ "upsilon;": '\U000003C5',
+ "upuparrows;": '\U000021C8',
+ "urcorn;": '\U0000231D',
+ "urcorner;": '\U0000231D',
+ "urcrop;": '\U0000230E',
+ "uring;": '\U0000016F',
+ "urtri;": '\U000025F9',
+ "uscr;": '\U0001D4CA',
+ "utdot;": '\U000022F0',
+ "utilde;": '\U00000169',
+ "utri;": '\U000025B5',
+ "utrif;": '\U000025B4',
+ "uuarr;": '\U000021C8',
+ "uuml;": '\U000000FC',
+ "uwangle;": '\U000029A7',
+ "vArr;": '\U000021D5',
+ "vBar;": '\U00002AE8',
+ "vBarv;": '\U00002AE9',
+ "vDash;": '\U000022A8',
+ "vangrt;": '\U0000299C',
+ "varepsilon;": '\U000003F5',
+ "varkappa;": '\U000003F0',
+ "varnothing;": '\U00002205',
+ "varphi;": '\U000003D5',
+ "varpi;": '\U000003D6',
+ "varpropto;": '\U0000221D',
+ "varr;": '\U00002195',
+ "varrho;": '\U000003F1',
+ "varsigma;": '\U000003C2',
+ "vartheta;": '\U000003D1',
+ "vartriangleleft;": '\U000022B2',
+ "vartriangleright;": '\U000022B3',
+ "vcy;": '\U00000432',
+ "vdash;": '\U000022A2',
+ "vee;": '\U00002228',
+ "veebar;": '\U000022BB',
+ "veeeq;": '\U0000225A',
+ "vellip;": '\U000022EE',
+ "verbar;": '\U0000007C',
+ "vert;": '\U0000007C',
+ "vfr;": '\U0001D533',
+ "vltri;": '\U000022B2',
+ "vopf;": '\U0001D567',
+ "vprop;": '\U0000221D',
+ "vrtri;": '\U000022B3',
+ "vscr;": '\U0001D4CB',
+ "vzigzag;": '\U0000299A',
+ "wcirc;": '\U00000175',
+ "wedbar;": '\U00002A5F',
+ "wedge;": '\U00002227',
+ "wedgeq;": '\U00002259',
+ "weierp;": '\U00002118',
+ "wfr;": '\U0001D534',
+ "wopf;": '\U0001D568',
+ "wp;": '\U00002118',
+ "wr;": '\U00002240',
+ "wreath;": '\U00002240',
+ "wscr;": '\U0001D4CC',
+ "xcap;": '\U000022C2',
+ "xcirc;": '\U000025EF',
+ "xcup;": '\U000022C3',
+ "xdtri;": '\U000025BD',
+ "xfr;": '\U0001D535',
+ "xhArr;": '\U000027FA',
+ "xharr;": '\U000027F7',
+ "xi;": '\U000003BE',
+ "xlArr;": '\U000027F8',
+ "xlarr;": '\U000027F5',
+ "xmap;": '\U000027FC',
+ "xnis;": '\U000022FB',
+ "xodot;": '\U00002A00',
+ "xopf;": '\U0001D569',
+ "xoplus;": '\U00002A01',
+ "xotime;": '\U00002A02',
+ "xrArr;": '\U000027F9',
+ "xrarr;": '\U000027F6',
+ "xscr;": '\U0001D4CD',
+ "xsqcup;": '\U00002A06',
+ "xuplus;": '\U00002A04',
+ "xutri;": '\U000025B3',
+ "xvee;": '\U000022C1',
+ "xwedge;": '\U000022C0',
+ "yacute;": '\U000000FD',
+ "yacy;": '\U0000044F',
+ "ycirc;": '\U00000177',
+ "ycy;": '\U0000044B',
+ "yen;": '\U000000A5',
+ "yfr;": '\U0001D536',
+ "yicy;": '\U00000457',
+ "yopf;": '\U0001D56A',
+ "yscr;": '\U0001D4CE',
+ "yucy;": '\U0000044E',
+ "yuml;": '\U000000FF',
+ "zacute;": '\U0000017A',
+ "zcaron;": '\U0000017E',
+ "zcy;": '\U00000437',
+ "zdot;": '\U0000017C',
+ "zeetrf;": '\U00002128',
+ "zeta;": '\U000003B6',
+ "zfr;": '\U0001D537',
+ "zhcy;": '\U00000436',
+ "zigrarr;": '\U000021DD',
+ "zopf;": '\U0001D56B',
+ "zscr;": '\U0001D4CF',
+ "zwj;": '\U0000200D',
+ "zwnj;": '\U0000200C',
+ "AElig": '\U000000C6',
+ "AMP": '\U00000026',
+ "Aacute": '\U000000C1',
+ "Acirc": '\U000000C2',
+ "Agrave": '\U000000C0',
+ "Aring": '\U000000C5',
+ "Atilde": '\U000000C3',
+ "Auml": '\U000000C4',
+ "COPY": '\U000000A9',
+ "Ccedil": '\U000000C7',
+ "ETH": '\U000000D0',
+ "Eacute": '\U000000C9',
+ "Ecirc": '\U000000CA',
+ "Egrave": '\U000000C8',
+ "Euml": '\U000000CB',
+ "GT": '\U0000003E',
+ "Iacute": '\U000000CD',
+ "Icirc": '\U000000CE',
+ "Igrave": '\U000000CC',
+ "Iuml": '\U000000CF',
+ "LT": '\U0000003C',
+ "Ntilde": '\U000000D1',
+ "Oacute": '\U000000D3',
+ "Ocirc": '\U000000D4',
+ "Ograve": '\U000000D2',
+ "Oslash": '\U000000D8',
+ "Otilde": '\U000000D5',
+ "Ouml": '\U000000D6',
+ "QUOT": '\U00000022',
+ "REG": '\U000000AE',
+ "THORN": '\U000000DE',
+ "Uacute": '\U000000DA',
+ "Ucirc": '\U000000DB',
+ "Ugrave": '\U000000D9',
+ "Uuml": '\U000000DC',
+ "Yacute": '\U000000DD',
+ "aacute": '\U000000E1',
+ "acirc": '\U000000E2',
+ "acute": '\U000000B4',
+ "aelig": '\U000000E6',
+ "agrave": '\U000000E0',
+ "amp": '\U00000026',
+ "aring": '\U000000E5',
+ "atilde": '\U000000E3',
+ "auml": '\U000000E4',
+ "brvbar": '\U000000A6',
+ "ccedil": '\U000000E7',
+ "cedil": '\U000000B8',
+ "cent": '\U000000A2',
+ "copy": '\U000000A9',
+ "curren": '\U000000A4',
+ "deg": '\U000000B0',
+ "divide": '\U000000F7',
+ "eacute": '\U000000E9',
+ "ecirc": '\U000000EA',
+ "egrave": '\U000000E8',
+ "eth": '\U000000F0',
+ "euml": '\U000000EB',
+ "frac12": '\U000000BD',
+ "frac14": '\U000000BC',
+ "frac34": '\U000000BE',
+ "gt": '\U0000003E',
+ "iacute": '\U000000ED',
+ "icirc": '\U000000EE',
+ "iexcl": '\U000000A1',
+ "igrave": '\U000000EC',
+ "iquest": '\U000000BF',
+ "iuml": '\U000000EF',
+ "laquo": '\U000000AB',
+ "lt": '\U0000003C',
+ "macr": '\U000000AF',
+ "micro": '\U000000B5',
+ "middot": '\U000000B7',
+ "nbsp": '\U000000A0',
+ "not": '\U000000AC',
+ "ntilde": '\U000000F1',
+ "oacute": '\U000000F3',
+ "ocirc": '\U000000F4',
+ "ograve": '\U000000F2',
+ "ordf": '\U000000AA',
+ "ordm": '\U000000BA',
+ "oslash": '\U000000F8',
+ "otilde": '\U000000F5',
+ "ouml": '\U000000F6',
+ "para": '\U000000B6',
+ "plusmn": '\U000000B1',
+ "pound": '\U000000A3',
+ "quot": '\U00000022',
+ "raquo": '\U000000BB',
+ "reg": '\U000000AE',
+ "sect": '\U000000A7',
+ "shy": '\U000000AD',
+ "sup1": '\U000000B9',
+ "sup2": '\U000000B2',
+ "sup3": '\U000000B3',
+ "szlig": '\U000000DF',
+ "thorn": '\U000000FE',
+ "times": '\U000000D7',
+ "uacute": '\U000000FA',
+ "ucirc": '\U000000FB',
+ "ugrave": '\U000000F9',
+ "uml": '\U000000A8',
+ "uuml": '\U000000FC',
+ "yacute": '\U000000FD',
+ "yen": '\U000000A5',
+ "yuml": '\U000000FF',
}
// HTML entities that are two unicode codepoints.
diff --git a/src/pkg/html/escape.go b/src/pkg/html/escape.go
index 24cb7af85..eff0384e0 100644
--- a/src/pkg/html/escape.go
+++ b/src/pkg/html/escape.go
@@ -15,7 +15,7 @@ type writer interface {
WriteString(string) (int, error)
}
-// These replacements permit compatibility with old numeric entities that
+// These replacements permit compatibility with old numeric entities that
// assumed Windows-1252 encoding.
// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference
var replacementTable = [...]rune{
@@ -51,7 +51,7 @@ var replacementTable = [...]rune{
'\u009D',
'\u017E',
'\u0178', // Last entry is 0x9F.
- // 0x00->'\uFFFD' is handled programmatically.
+ // 0x00->'\uFFFD' is handled programmatically.
// 0x0D->'\u000D' is a no-op.
}
diff --git a/src/pkg/html/template/content.go b/src/pkg/html/template/content.go
index c1bd2e494..9d1f74f6f 100644
--- a/src/pkg/html/template/content.go
+++ b/src/pkg/html/template/content.go
@@ -30,7 +30,7 @@ type (
HTMLAttr string
// JS encapsulates a known safe EcmaScript5 Expression, for example,
- // `(x + y * z())`.
+ // `(x + y * z())`.
// Template authors are responsible for ensuring that typed expressions
// do not break the intended precedence and that there is no
// statement/expression ambiguity as when passing an expression like
@@ -47,7 +47,7 @@ type (
// JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not.
JSStr string
- // URL encapsulates a known safe URL as defined in RFC 3896.
+ // URL encapsulates a known safe URL or URL substring (see RFC 3986).
// A URL like `javascript:checkThatFormNotEditedBeforeLeavingPage()`
// from a trusted source should go in the page, but by default dynamic
// `javascript:` URLs are filtered out since they are a frequently
diff --git a/src/pkg/html/template/doc.go b/src/pkg/html/template/doc.go
index f470facfd..d422ada37 100644
--- a/src/pkg/html/template/doc.go
+++ b/src/pkg/html/template/doc.go
@@ -119,7 +119,7 @@ If {{.}} is the innocuous word, `left`, then it can appear more widely,
Non-string values can be used in JavaScript contexts.
If {{.}} is
- []struct{A,B string}{ "foo", "bar" }
+ struct{A,B string}{ "foo", "bar" }
in the escaped template
diff --git a/src/pkg/html/template/error.go b/src/pkg/html/template/error.go
index dcac74896..46e49ccf8 100644
--- a/src/pkg/html/template/error.go
+++ b/src/pkg/html/template/error.go
@@ -102,7 +102,7 @@ const (
//
// {{define "main"}} <script>{{template "helper"}}</script> {{end}}
// {{define "helper"}} document.write(' <div title=" ') {{end}}
- //
+ //
// "helper" does not produce a valid document fragment, so should
// not be Executed directly.
ErrEndContext
@@ -161,7 +161,7 @@ const (
// 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
- //
+ //
// <script>var x = ['firstValue,'secondValue]</script>
ErrRangeLoopReentry
diff --git a/src/pkg/html/template/escape.go b/src/pkg/html/template/escape.go
index 5f0e28e8c..4829bfcc4 100644
--- a/src/pkg/html/template/escape.go
+++ b/src/pkg/html/template/escape.go
@@ -220,10 +220,7 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) {
idents := p.Cmds
for i := n - 1; i >= 0; i-- {
if cmd := p.Cmds[i]; len(cmd.Args) != 0 {
- if id, ok := cmd.Args[0].(*parse.IdentifierNode); ok {
- if id.Ident == "noescape" {
- return
- }
+ if _, ok := cmd.Args[0].(*parse.IdentifierNode); ok {
continue
}
}
@@ -242,10 +239,11 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) {
copy(newCmds, p.Cmds)
// Merge existing identifier commands with the sanitizers needed.
for _, id := range idents {
+ pos := id.Args[0].Position()
i := indexOfStr((id.Args[0].(*parse.IdentifierNode)).Ident, s, escFnsEq)
if i != -1 {
for _, name := range s[:i] {
- newCmds = appendCmd(newCmds, newIdentCmd(name))
+ newCmds = appendCmd(newCmds, newIdentCmd(name, pos))
}
s = s[i+1:]
}
@@ -253,7 +251,7 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) {
}
// Create any remaining sanitizers.
for _, name := range s {
- newCmds = appendCmd(newCmds, newIdentCmd(name))
+ newCmds = appendCmd(newCmds, newIdentCmd(name, p.Position()))
}
p.Cmds = newCmds
}
@@ -315,10 +313,10 @@ func escFnsEq(a, b string) bool {
}
// newIdentCmd produces a command containing a single identifier node.
-func newIdentCmd(identifier string) *parse.CommandNode {
+func newIdentCmd(identifier string, pos parse.Pos) *parse.CommandNode {
return &parse.CommandNode{
NodeType: parse.NodeCommand,
- Args: []parse.Node{parse.NewIdentifier(identifier)},
+ Args: []parse.Node{parse.NewIdentifier(identifier).SetPos(pos)},
}
}
diff --git a/src/pkg/html/template/escape_test.go b/src/pkg/html/template/escape_test.go
index ce12c1795..de3659ba8 100644
--- a/src/pkg/html/template/escape_test.go
+++ b/src/pkg/html/template/escape_test.go
@@ -551,11 +551,6 @@ func TestEscape(t *testing.T) {
"<textarea>&lt;a&gt;&lt;b&gt;</textarea>",
},
{
- "auditable exemption from escaping",
- "{{range .A}}{{. | noescape}}{{end}}",
- "<a><b>",
- },
- {
"No tag injection",
`{{"10$"}}<{{"script src,evil.org/pwnd.js"}}...`,
`10$&lt;script src,evil.org/pwnd.js...`,
@@ -659,12 +654,6 @@ func TestEscape(t *testing.T) {
for _, test := range tests {
tmpl := New(test.name)
- // TODO: Move noescape into template/func.go
- tmpl.Funcs(FuncMap{
- "noescape": func(a ...interface{}) string {
- return fmt.Sprint(a...)
- },
- })
tmpl = Must(tmpl.Parse(test.input))
b := new(bytes.Buffer)
if err := tmpl.Execute(b, data); err != nil {
@@ -1539,6 +1528,11 @@ func TestEnsurePipelineContains(t *testing.T) {
".X | urlquery | html | print",
[]string{"urlquery", "html"},
},
+ {
+ "{{($).X | html | print}}",
+ "($).X | urlquery | html | print",
+ []string{"urlquery", "html"},
+ },
}
for i, test := range tests {
tmpl := template.Must(template.New("test").Parse(test.input))
diff --git a/src/pkg/html/template/html.go b/src/pkg/html/template/html.go
index 36c88e23e..f25f1074c 100644
--- a/src/pkg/html/template/html.go
+++ b/src/pkg/html/template/html.go
@@ -106,7 +106,7 @@ var htmlNospaceReplacementTable = []string{
'<': "&lt;",
'=': "&#61;",
'>': "&gt;",
- // A parse error in the attribute value (unquoted) and
+ // A parse error in the attribute value (unquoted) and
// before attribute value states.
// Treated as a quoting character by IE.
'`': "&#96;",
@@ -128,7 +128,7 @@ var htmlNospaceNormReplacementTable = []string{
'<': "&lt;",
'=': "&#61;",
'>': "&gt;",
- // A parse error in the attribute value (unquoted) and
+ // A parse error in the attribute value (unquoted) and
// before attribute value states.
// Treated as a quoting character by IE.
'`': "&#96;",
@@ -143,7 +143,7 @@ func htmlReplacer(s string, replacementTable []string, badRunes bool) string {
if repl := replacementTable[r]; len(repl) != 0 {
b.WriteString(s[written:i])
b.WriteString(repl)
- // Valid as long as replacementTable doesn't
+ // Valid as long as replacementTable doesn't
// include anything above 0x7f.
written = i + utf8.RuneLen(r)
}
diff --git a/src/pkg/html/template/js.go b/src/pkg/html/template/js.go
index a895a50aa..a9740931f 100644
--- a/src/pkg/html/template/js.go
+++ b/src/pkg/html/template/js.go
@@ -14,7 +14,7 @@ import (
)
// nextJSCtx returns the context that determines whether a slash after the
-// given run of tokens tokens starts a regular expression instead of a division
+// given run of tokens starts a regular expression instead of a division
// operator: / or /=.
//
// This assumes that the token run does not include any string tokens, comment
diff --git a/src/pkg/html/template/template.go b/src/pkg/html/template/template.go
index edac7335c..768cee7d5 100644
--- a/src/pkg/html/template/template.go
+++ b/src/pkg/html/template/template.go
@@ -14,8 +14,8 @@ import (
"text/template/parse"
)
-// Template is a specialized template.Template that produces a safe HTML
-// document fragment.
+// Template is a specialized Template from "text/template" that produces a safe
+// HTML document fragment.
type Template struct {
escaped bool
// We could embed the text/template field, but it's safer not to because
@@ -238,7 +238,8 @@ func (t *Template) Name() string {
// return values of which the second has type error. In that case, if the
// second (error) argument evaluates to non-nil during execution, execution
// terminates and Execute returns that error. FuncMap has the same base type
-// as template.FuncMap, copied here so clients need not import "text/template".
+// as FuncMap in "text/template", copied here so clients need not import
+// "text/template".
type FuncMap map[string]interface{}
// Funcs adds the elements of the argument map to the template's function map.
@@ -268,7 +269,10 @@ func (t *Template) Lookup(name string) *Template {
return t.set[name]
}
-// Must panics if err is non-nil in the same way as template.Must.
+// Must is a helper that wraps a call to a function returning (*Template, error)
+// and panics if the error is non-nil. It is intended for use in variable initializations
+// such as
+// var t = template.Must(template.New("name").Parse("html"))
func Must(t *Template, err error) *Template {
if err != nil {
panic(err)
diff --git a/src/pkg/html/template/url.go b/src/pkg/html/template/url.go
index 454c791ec..2ca76bf38 100644
--- a/src/pkg/html/template/url.go
+++ b/src/pkg/html/template/url.go
@@ -60,7 +60,7 @@ func urlProcessor(norm bool, args ...interface{}) string {
c := s[i]
switch c {
// Single quote and parens are sub-delims in RFC 3986, but we
- // escape them so the output can be embedded in in single
+ // escape them so the output can be embedded in single
// quoted attributes and unquoted CSS url(...) constructs.
// Single quotes are reserved in URLs, but are only used in
// the obsolete "mark" rule in an appendix in RFC 3986
diff --git a/src/pkg/image/decode_test.go b/src/pkg/image/decode_test.go
index d65986724..8dee57ee4 100644
--- a/src/pkg/image/decode_test.go
+++ b/src/pkg/image/decode_test.go
@@ -31,6 +31,7 @@ var imageTests = []imageTest{
{"testdata/video-001.png", "testdata/video-001.5bpp.gif", 128 << 8},
// JPEG is a lossy format and hence needs a non-zero tolerance.
{"testdata/video-001.png", "testdata/video-001.jpeg", 8 << 8},
+ {"testdata/video-001.png", "testdata/video-001.progressive.jpeg", 8 << 8},
// Grayscale images.
{"testdata/video-005.gray.png", "testdata/video-005.gray.jpeg", 8 << 8},
{"testdata/video-005.gray.png", "testdata/video-005.gray.png", 0},
diff --git a/src/pkg/image/draw/draw.go b/src/pkg/image/draw/draw.go
index bef325c0c..56d30dd6f 100644
--- a/src/pkg/image/draw/draw.go
+++ b/src/pkg/image/draw/draw.go
@@ -81,8 +81,9 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
drawNRGBAOver(dst0, r, src0, sp)
return
case *image.YCbCr:
- drawYCbCr(dst0, r, src0, sp)
- return
+ if drawYCbCr(dst0, r, src0, sp) {
+ return
+ }
}
} else if mask0, ok := mask.(*image.Alpha); ok {
switch src0 := src.(type) {
@@ -104,8 +105,9 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
drawNRGBASrc(dst0, r, src0, sp)
return
case *image.YCbCr:
- drawYCbCr(dst0, r, src0, sp)
- return
+ if drawYCbCr(dst0, r, src0, sp) {
+ return
+ }
}
}
}
@@ -345,7 +347,7 @@ func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image
}
}
-func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) {
+func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) (ok bool) {
// An image.YCbCr is always fully opaque, and so if the mask is implicitly nil
// (i.e. fully opaque) then the op is effectively always Src.
x0 := (r.Min.X - dst.Rect.Min.X) * 4
@@ -353,6 +355,19 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po
y0 := r.Min.Y - dst.Rect.Min.Y
y1 := r.Max.Y - dst.Rect.Min.Y
switch src.SubsampleRatio {
+ case image.YCbCrSubsampleRatio444:
+ for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
+ dpix := dst.Pix[y*dst.Stride:]
+ yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
+ ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X)
+ for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
+ rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
+ dpix[x+0] = rr
+ dpix[x+1] = gg
+ dpix[x+2] = bb
+ dpix[x+3] = 255
+ }
+ }
case image.YCbCrSubsampleRatio422:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
@@ -381,12 +396,11 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po
dpix[x+3] = 255
}
}
- default:
- // Default to 4:4:4 subsampling.
+ case image.YCbCrSubsampleRatio440:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
- ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X)
+ ci := (sy/2-src.Rect.Min.Y/2)*src.CStride + (sp.X - src.Rect.Min.X)
for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
dpix[x+0] = rr
@@ -395,7 +409,10 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po
dpix[x+3] = 255
}
}
+ default:
+ return false
}
+ return true
}
func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask *image.Alpha, mp image.Point) {
diff --git a/src/pkg/image/format.go b/src/pkg/image/format.go
index f93d356b0..36635bcc5 100644
--- a/src/pkg/image/format.go
+++ b/src/pkg/image/format.go
@@ -39,7 +39,7 @@ type reader interface {
Peek(int) ([]byte, error)
}
-// AsReader converts an io.Reader to a reader.
+// asReader converts an io.Reader to a reader.
func asReader(r io.Reader) reader {
if rr, ok := r.(reader); ok {
return rr
diff --git a/src/pkg/image/jpeg/dct_test.go b/src/pkg/image/jpeg/dct_test.go
new file mode 100644
index 000000000..7389f7e4f
--- /dev/null
+++ b/src/pkg/image/jpeg/dct_test.go
@@ -0,0 +1,299 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package jpeg
+
+import (
+ "bytes"
+ "fmt"
+ "math"
+ "math/rand"
+ "testing"
+)
+
+func benchmarkDCT(b *testing.B, f func(*block)) {
+ b.StopTimer()
+ blocks := make([]block, 0, b.N*len(testBlocks))
+ for i := 0; i < b.N; i++ {
+ blocks = append(blocks, testBlocks[:]...)
+ }
+ b.StartTimer()
+ for i := range blocks {
+ f(&blocks[i])
+ }
+}
+
+func BenchmarkFDCT(b *testing.B) {
+ benchmarkDCT(b, fdct)
+}
+
+func BenchmarkIDCT(b *testing.B) {
+ benchmarkDCT(b, idct)
+}
+
+func TestDCT(t *testing.T) {
+ blocks := make([]block, len(testBlocks))
+ copy(blocks, testBlocks[:])
+
+ // Append some randomly generated blocks of varying sparseness.
+ r := rand.New(rand.NewSource(123))
+ for i := 0; i < 100; i++ {
+ b := block{}
+ n := r.Int() % 64
+ for j := 0; j < n; j++ {
+ b[r.Int()%len(b)] = r.Int31() % 256
+ }
+ blocks = append(blocks, b)
+ }
+
+ // Check that the FDCT and IDCT functions are inverses, after a scale and
+ // level shift. Scaling reduces the rounding errors in the conversion from
+ // floats to ints.
+ for i, b := range blocks {
+ got, want := b, b
+ for j := range got {
+ got[j] = (got[j] - 128) * 8
+ }
+ slowFDCT(&got)
+ slowIDCT(&got)
+ for j := range got {
+ got[j] = got[j]/8 + 128
+ }
+ if differ(&got, &want) {
+ t.Errorf("i=%d: IDCT(FDCT)\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want)
+ }
+ }
+
+ // Check that the optimized and slow FDCT implementations agree.
+ // The fdct function already does a scale and level shift.
+ for i, b := range blocks {
+ got, want := b, b
+ fdct(&got)
+ for j := range want {
+ want[j] = (want[j] - 128) * 8
+ }
+ slowFDCT(&want)
+ if differ(&got, &want) {
+ t.Errorf("i=%d: FDCT\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want)
+ }
+ }
+
+ // Check that the optimized and slow IDCT implementations agree.
+ for i, b := range blocks {
+ got, want := b, b
+ idct(&got)
+ slowIDCT(&want)
+ if differ(&got, &want) {
+ t.Errorf("i=%d: IDCT\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want)
+ }
+ }
+}
+
+// differ returns whether any pair-wise elements in b0 and b1 differ by 2 or
+// more. That tolerance is because there isn't a single definitive decoding of
+// a given JPEG image, even before the YCbCr to RGB conversion; implementations
+// can have different IDCT rounding errors.
+func differ(b0, b1 *block) bool {
+ for i := range b0 {
+ delta := b0[i] - b1[i]
+ if delta < -2 || +2 < delta {
+ return true
+ }
+ }
+ return false
+}
+
+// alpha returns 1 if i is 0 and returns √2 otherwise.
+func alpha(i int) float64 {
+ if i == 0 {
+ return 1
+ }
+ return math.Sqrt2
+}
+
+var cosines [32]float64 // cosines[k] = cos(π/2 * k/8)
+
+func init() {
+ for k := range cosines {
+ cosines[k] = math.Cos(math.Pi * float64(k) / 16)
+ }
+}
+
+// slowFDCT performs the 8*8 2-dimensional forward discrete cosine transform:
+//
+// dst[u,v] = (1/8) * Σ_x Σ_y alpha(u) * alpha(v) * src[x,y] *
+// cos((π/2) * (2*x + 1) * u / 8) *
+// cos((π/2) * (2*y + 1) * v / 8)
+//
+// x and y are in pixel space, and u and v are in transform space.
+//
+// b acts as both dst and src.
+func slowFDCT(b *block) {
+ var dst [blockSize]float64
+ for v := 0; v < 8; v++ {
+ for u := 0; u < 8; u++ {
+ sum := 0.0
+ for y := 0; y < 8; y++ {
+ for x := 0; x < 8; x++ {
+ sum += alpha(u) * alpha(v) * float64(b[8*y+x]) *
+ cosines[((2*x+1)*u)%32] *
+ cosines[((2*y+1)*v)%32]
+ }
+ }
+ dst[8*v+u] = sum / 8
+ }
+ }
+ // Convert from float64 to int32.
+ for i := range dst {
+ b[i] = int32(dst[i] + 0.5)
+ }
+}
+
+// slowIDCT performs the 8*8 2-dimensional inverse discrete cosine transform:
+//
+// dst[x,y] = (1/8) * Σ_u Σ_v alpha(u) * alpha(v) * src[u,v] *
+// cos((π/2) * (2*x + 1) * u / 8) *
+// cos((π/2) * (2*y + 1) * v / 8)
+//
+// x and y are in pixel space, and u and v are in transform space.
+//
+// b acts as both dst and src.
+func slowIDCT(b *block) {
+ var dst [blockSize]float64
+ for y := 0; y < 8; y++ {
+ for x := 0; x < 8; x++ {
+ sum := 0.0
+ for v := 0; v < 8; v++ {
+ for u := 0; u < 8; u++ {
+ sum += alpha(u) * alpha(v) * float64(b[8*v+u]) *
+ cosines[((2*x+1)*u)%32] *
+ cosines[((2*y+1)*v)%32]
+ }
+ }
+ dst[8*y+x] = sum / 8
+ }
+ }
+ // Convert from float64 to int32.
+ for i := range dst {
+ b[i] = int32(dst[i] + 0.5)
+ }
+}
+
+func (b *block) String() string {
+ s := bytes.NewBuffer(nil)
+ fmt.Fprintf(s, "{\n")
+ for y := 0; y < 8; y++ {
+ fmt.Fprintf(s, "\t")
+ for x := 0; x < 8; x++ {
+ fmt.Fprintf(s, "0x%04x, ", uint16(b[8*y+x]))
+ }
+ fmt.Fprintln(s)
+ }
+ fmt.Fprintf(s, "}")
+ return s.String()
+}
+
+// testBlocks are the first 10 pre-IDCT blocks from ../testdata/video-001.jpeg.
+var testBlocks = [10]block{
+ {
+ 0x7f, 0xf6, 0x01, 0x07, 0xff, 0x00, 0x00, 0x00,
+ 0xf5, 0x01, 0xfa, 0x01, 0xfe, 0x00, 0x01, 0x00,
+ 0x05, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0xff, 0xf8, 0x00, 0x01, 0xff, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00,
+ 0xff, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0xfe,
+ },
+ {
+ 0x29, 0x07, 0x00, 0xfc, 0x01, 0x01, 0x00, 0x00,
+ 0x07, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff,
+ 0xff, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x00, 0xff, 0x01, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0xfa, 0x01, 0x00, 0x01, 0x00, 0x01, 0xff,
+ 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x02,
+ },
+ {
+ 0xc5, 0xfa, 0x01, 0x00, 0x00, 0x01, 0x00, 0xff,
+ 0x02, 0xff, 0x01, 0x00, 0x01, 0x00, 0xff, 0x00,
+ 0xff, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00,
+ 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x86, 0x05, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00,
+ 0xf2, 0x06, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00,
+ 0xf6, 0xfa, 0xf9, 0x00, 0xff, 0x01, 0x00, 0x00,
+ 0xf9, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x01, 0xff, 0x01, 0x00, 0xff, 0x00, 0x00,
+ },
+ {
+ 0x24, 0xfe, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00,
+ 0x08, 0xfd, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00,
+ 0x06, 0x03, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x01, 0xff, 0x00, 0x01, 0x00, 0x00,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x01,
+ },
+ {
+ 0xcd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+ 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff,
+ },
+ {
+ 0x81, 0xfe, 0x05, 0xff, 0x01, 0xff, 0x01, 0x00,
+ 0xef, 0xf9, 0x00, 0xf9, 0x00, 0xff, 0x00, 0xff,
+ 0x05, 0xf9, 0x00, 0xf8, 0x01, 0xff, 0x01, 0xff,
+ 0x00, 0xff, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01,
+ 0xff, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
+ },
+ {
+ 0x28, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x02, 0x01, 0x03, 0x00, 0xff, 0x00, 0x01,
+ 0xfe, 0x02, 0x01, 0x03, 0xff, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0xfd, 0x00, 0x01, 0x00, 0xff, 0x00,
+ 0x01, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01,
+ },
+ {
+ 0xdf, 0xf9, 0xfe, 0x00, 0x03, 0x01, 0xff, 0xff,
+ 0x04, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x01,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ },
+ {
+ 0x88, 0xfd, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff,
+ 0xe1, 0x06, 0x06, 0x01, 0xff, 0x00, 0x01, 0x00,
+ 0x08, 0x00, 0xfa, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x08, 0x01, 0x00, 0xff, 0x01, 0xff, 0x00, 0x00,
+ 0xf5, 0xff, 0x00, 0x01, 0xff, 0x01, 0x01, 0x00,
+ 0xff, 0xff, 0x01, 0xff, 0x01, 0x00, 0x01, 0x00,
+ 0x00, 0x01, 0x01, 0xff, 0x00, 0xff, 0x00, 0x01,
+ 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00,
+ },
+}
diff --git a/src/pkg/image/jpeg/huffman.go b/src/pkg/image/jpeg/huffman.go
index d2382490f..9b731fdc4 100644
--- a/src/pkg/image/jpeg/huffman.go
+++ b/src/pkg/image/jpeg/huffman.go
@@ -15,9 +15,9 @@ const maxNumValues = 256
// Bit stream for the Huffman decoder.
// The n least significant bits of a form the unread bits, to be read in MSB to LSB order.
type bits struct {
- a int // accumulator.
- n int // the number of unread bits in a.
- m int // mask. m==1<<(n-1) when n>0, with m==0 when n==0.
+ a uint32 // accumulator.
+ m uint32 // mask. m==1<<(n-1) when n>0, with m==0 when n==0.
+ n int // the number of unread bits in a.
}
// Huffman table decoder, specified in section C.
@@ -39,7 +39,7 @@ func (d *decoder) ensureNBits(n int) error {
if err != nil {
return err
}
- d.b.a = d.b.a<<8 | int(c)
+ d.b.a = d.b.a<<8 | uint32(c)
d.b.n += 8
if d.b.m == 0 {
d.b.m = 1 << 7
@@ -61,15 +61,16 @@ func (d *decoder) ensureNBits(n int) error {
}
// The composition of RECEIVE and EXTEND, specified in section F.2.2.1.
-func (d *decoder) receiveExtend(t uint8) (int, error) {
- err := d.ensureNBits(int(t))
- if err != nil {
- return 0, err
+func (d *decoder) receiveExtend(t uint8) (int32, error) {
+ if d.b.n < int(t) {
+ if err := d.ensureNBits(int(t)); err != nil {
+ return 0, err
+ }
}
d.b.n -= int(t)
d.b.m >>= t
- s := 1 << t
- x := (d.b.a >> uint8(d.b.n)) & (s - 1)
+ s := int32(1) << t
+ x := int32(d.b.a>>uint8(d.b.n)) & (s - 1)
if x < s>>1 {
x += ((-1) << t) + 1
}
@@ -92,8 +93,7 @@ func (d *decoder) processDHT(n int) error {
return FormatError("bad Tc value")
}
th := d.tmp[0] & 0x0f
- const isBaseline = true // Progressive mode is not yet supported.
- if th > maxTh || isBaseline && th > 1 {
+ if th > maxTh || !d.progressive && th > 1 {
return FormatError("bad Th value")
}
h := &d.huff[tc][th]
@@ -163,15 +163,16 @@ func (d *decoder) processDHT(n int) error {
// Returns the next Huffman-coded value from the bit stream, decoded according to h.
// TODO(nigeltao): This decoding algorithm is simple, but slow. A lookahead table, instead of always
-// peeling off only 1 bit at at time, ought to be faster.
+// peeling off only 1 bit at time, ought to be faster.
func (d *decoder) decodeHuffman(h *huffman) (uint8, error) {
if h.length == 0 {
return 0, FormatError("uninitialized Huffman table")
}
for i, code := 0, 0; i < maxCodeLength; i++ {
- err := d.ensureNBits(1)
- if err != nil {
- return 0, err
+ if d.b.n == 0 {
+ if err := d.ensureNBits(1); err != nil {
+ return 0, err
+ }
}
if d.b.a&d.b.m != 0 {
code |= 1
@@ -185,3 +186,28 @@ func (d *decoder) decodeHuffman(h *huffman) (uint8, error) {
}
return 0, FormatError("bad Huffman code")
}
+
+func (d *decoder) decodeBit() (bool, error) {
+ if d.b.n == 0 {
+ if err := d.ensureNBits(1); err != nil {
+ return false, err
+ }
+ }
+ ret := d.b.a&d.b.m != 0
+ d.b.n--
+ d.b.m >>= 1
+ return ret, nil
+}
+
+func (d *decoder) decodeBits(n int) (uint32, error) {
+ if d.b.n < n {
+ if err := d.ensureNBits(n); err != nil {
+ return 0, err
+ }
+ }
+ ret := d.b.a >> uint(d.b.n-n)
+ ret &= (1 << uint(n)) - 1
+ d.b.n -= n
+ d.b.m >>= uint(n)
+ return ret, nil
+}
diff --git a/src/pkg/image/jpeg/idct.go b/src/pkg/image/jpeg/idct.go
index b387dfdff..46fcaecb7 100644
--- a/src/pkg/image/jpeg/idct.go
+++ b/src/pkg/image/jpeg/idct.go
@@ -37,6 +37,10 @@ package jpeg
*
*/
+const blockSize = 64 // A DCT block is 8x8.
+
+type block [blockSize]int32
+
const (
w1 = 2841 // 2048*sqrt(2)*cos(1*pi/16)
w2 = 2676 // 2048*sqrt(2)*cos(2*pi/16)
@@ -55,9 +59,7 @@ const (
r2 = 181 // 256/sqrt(2)
)
-// idct performs a 2-D Inverse Discrete Cosine Transformation, followed by a
-// +128 level shift and a clip to [0, 255], writing the results to dst.
-// stride is the number of elements between successive rows of dst.
+// idct performs a 2-D Inverse Discrete Cosine Transformation.
//
// The input coefficients should already have been multiplied by the
// appropriate quantization table. We use fixed-point computation, with the
@@ -67,33 +69,34 @@ const (
// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the
// discrete W transform and for the discrete Fourier transform", IEEE Trans. on
// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984.
-func idct(dst []byte, stride int, src *block) {
+func idct(src *block) {
// Horizontal 1-D IDCT.
for y := 0; y < 8; y++ {
+ y8 := y * 8
// If all the AC components are zero, then the IDCT is trivial.
- if src[y*8+1] == 0 && src[y*8+2] == 0 && src[y*8+3] == 0 &&
- src[y*8+4] == 0 && src[y*8+5] == 0 && src[y*8+6] == 0 && src[y*8+7] == 0 {
- dc := src[y*8+0] << 3
- src[y*8+0] = dc
- src[y*8+1] = dc
- src[y*8+2] = dc
- src[y*8+3] = dc
- src[y*8+4] = dc
- src[y*8+5] = dc
- src[y*8+6] = dc
- src[y*8+7] = dc
+ if src[y8+1] == 0 && src[y8+2] == 0 && src[y8+3] == 0 &&
+ src[y8+4] == 0 && src[y8+5] == 0 && src[y8+6] == 0 && src[y8+7] == 0 {
+ dc := src[y8+0] << 3
+ src[y8+0] = dc
+ src[y8+1] = dc
+ src[y8+2] = dc
+ src[y8+3] = dc
+ src[y8+4] = dc
+ src[y8+5] = dc
+ src[y8+6] = dc
+ src[y8+7] = dc
continue
}
// Prescale.
- x0 := (src[y*8+0] << 11) + 128
- x1 := src[y*8+4] << 11
- x2 := src[y*8+6]
- x3 := src[y*8+2]
- x4 := src[y*8+1]
- x5 := src[y*8+7]
- x6 := src[y*8+5]
- x7 := src[y*8+3]
+ x0 := (src[y8+0] << 11) + 128
+ x1 := src[y8+4] << 11
+ x2 := src[y8+6]
+ x3 := src[y8+2]
+ x4 := src[y8+1]
+ x5 := src[y8+7]
+ x6 := src[y8+5]
+ x7 := src[y8+3]
// Stage 1.
x8 := w7 * (x4 + x5)
@@ -123,14 +126,14 @@ func idct(dst []byte, stride int, src *block) {
x4 = (r2*(x4-x5) + 128) >> 8
// Stage 4.
- src[8*y+0] = (x7 + x1) >> 8
- src[8*y+1] = (x3 + x2) >> 8
- src[8*y+2] = (x0 + x4) >> 8
- src[8*y+3] = (x8 + x6) >> 8
- src[8*y+4] = (x8 - x6) >> 8
- src[8*y+5] = (x0 - x4) >> 8
- src[8*y+6] = (x3 - x2) >> 8
- src[8*y+7] = (x7 - x1) >> 8
+ src[y8+0] = (x7 + x1) >> 8
+ src[y8+1] = (x3 + x2) >> 8
+ src[y8+2] = (x0 + x4) >> 8
+ src[y8+3] = (x8 + x6) >> 8
+ src[y8+4] = (x8 - x6) >> 8
+ src[y8+5] = (x0 - x4) >> 8
+ src[y8+6] = (x3 - x2) >> 8
+ src[y8+7] = (x7 - x1) >> 8
}
// Vertical 1-D IDCT.
@@ -186,19 +189,4 @@ func idct(dst []byte, stride int, src *block) {
src[8*6+x] = (y3 - y2) >> 14
src[8*7+x] = (y7 - y1) >> 14
}
-
- // Level shift by +128, clip to [0, 255], and write to dst.
- for y := 0; y < 8; y++ {
- for x := 0; x < 8; x++ {
- c := src[y*8+x]
- if c < -128 {
- c = 0
- } else if c > 127 {
- c = 255
- } else {
- c += 128
- }
- dst[y*stride+x] = uint8(c)
- }
- }
}
diff --git a/src/pkg/image/jpeg/reader.go b/src/pkg/image/jpeg/reader.go
index d9adf6e58..1ee6bbcd1 100644
--- a/src/pkg/image/jpeg/reader.go
+++ b/src/pkg/image/jpeg/reader.go
@@ -35,11 +35,7 @@ type component struct {
tq uint8 // Quantization table destination selector.
}
-type block [blockSize]int
-
const (
- blockSize = 64 // A DCT block is 8x8.
-
dcTable = 0
acTable = 1
maxTc = 1
@@ -51,7 +47,7 @@ const (
// A color JPEG image has Y, Cb and Cr components.
nColorComponent = 3
- // We only support 4:4:4, 4:2:2 and 4:2:0 downsampling, and therefore the
+ // We only support 4:4:4, 4:4:0, 4:2:2 and 4:2:0 downsampling, and therefore the
// number of luma samples per chroma sample is at most 2 in the horizontal
// and 2 in the vertical direction.
maxH = 2
@@ -74,7 +70,9 @@ const (
comMarker = 0xfe // COMment.
)
-// Maps from the zig-zag ordering to the natural ordering.
+// unzig maps from the zig-zag ordering to the natural ordering. For example,
+// unzig[3] is the column and row of the fourth element in zig-zag order. The
+// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
var unzig = [blockSize]int{
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
@@ -94,15 +92,18 @@ type Reader interface {
type decoder struct {
r Reader
+ b bits
width, height int
img1 *image.Gray
img3 *image.YCbCr
ri int // Restart Interval.
nComp int
+ progressive bool
+ eobRun uint16 // End-of-Band run, specified in section G.1.2.2.
comp [nColorComponent]component
+ progCoeffs [nColorComponent][]block // Saved state between progressive-mode scans.
huff [maxTc + 1][maxTh + 1]huffman
- quant [maxTq + 1]block
- b bits
+ quant [maxTq + 1]block // Quantization tables, in zig-zag order.
tmp [1024]byte
}
@@ -146,20 +147,33 @@ func (d *decoder) processSOF(n int) error {
return UnsupportedError("SOF has wrong number of image components")
}
for i := 0; i < d.nComp; i++ {
- hv := d.tmp[7+3*i]
- d.comp[i].h = int(hv >> 4)
- d.comp[i].v = int(hv & 0x0f)
d.comp[i].c = d.tmp[6+3*i]
d.comp[i].tq = d.tmp[8+3*i]
if d.nComp == nGrayComponent {
+ // If a JPEG image has only one component, section A.2 says "this data
+ // is non-interleaved by definition" and section A.2.2 says "[in this
+ // case...] the order of data units within a scan shall be left-to-right
+ // and top-to-bottom... regardless of the values of H_1 and V_1". Section
+ // 4.8.2 also says "[for non-interleaved data], the MCU is defined to be
+ // one data unit". Similarly, section A.1.1 explains that it is the ratio
+ // of H_i to max_j(H_j) that matters, and similarly for V. For grayscale
+ // images, H_1 is the maximum H_j for all components j, so that ratio is
+ // always 1. The component's (h, v) is effectively always (1, 1): even if
+ // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8
+ // MCUs, not two 16x8 MCUs.
+ d.comp[i].h = 1
+ d.comp[i].v = 1
continue
}
- // For color images, we only support 4:4:4, 4:2:2 or 4:2:0 chroma
+ hv := d.tmp[7+3*i]
+ d.comp[i].h = int(hv >> 4)
+ d.comp[i].v = int(hv & 0x0f)
+ // For color images, we only support 4:4:4, 4:4:0, 4:2:2 or 4:2:0 chroma
// downsampling ratios. This implies that the (h, v) values for the Y
- // component are either (1, 1), (2, 1) or (2, 2), and the (h, v)
+ // component are either (1, 1), (1, 2), (2, 1) or (2, 2), and the (h, v)
// values for the Cr and Cb components must be (1, 1).
if i == 0 {
- if hv != 0x11 && hv != 0x21 && hv != 0x22 {
+ if hv != 0x11 && hv != 0x21 && hv != 0x22 && hv != 0x12 {
return UnsupportedError("luma downsample ratio")
}
} else if hv != 0x11 {
@@ -186,7 +200,7 @@ func (d *decoder) processDQT(n int) error {
return FormatError("bad Tq value")
}
for i := range d.quant[tq] {
- d.quant[tq][i] = int(d.tmp[i+1])
+ d.quant[tq][i] = int32(d.tmp[i+1])
}
}
if n != 0 {
@@ -195,161 +209,6 @@ func (d *decoder) processDQT(n int) error {
return nil
}
-// makeImg allocates and initializes the destination image.
-func (d *decoder) makeImg(h0, v0, mxx, myy int) {
- if d.nComp == nGrayComponent {
- m := image.NewGray(image.Rect(0, 0, 8*mxx, 8*myy))
- d.img1 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.Gray)
- return
- }
- var subsampleRatio image.YCbCrSubsampleRatio
- switch h0 * v0 {
- case 1:
- subsampleRatio = image.YCbCrSubsampleRatio444
- case 2:
- subsampleRatio = image.YCbCrSubsampleRatio422
- case 4:
- subsampleRatio = image.YCbCrSubsampleRatio420
- default:
- panic("unreachable")
- }
- m := image.NewYCbCr(image.Rect(0, 0, 8*h0*mxx, 8*v0*myy), subsampleRatio)
- d.img3 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.YCbCr)
-}
-
-// Specified in section B.2.3.
-func (d *decoder) processSOS(n int) error {
- if d.nComp == 0 {
- return FormatError("missing SOF marker")
- }
- if n != 4+2*d.nComp {
- return UnsupportedError("SOS has wrong length")
- }
- _, err := io.ReadFull(d.r, d.tmp[0:4+2*d.nComp])
- if err != nil {
- return err
- }
- if int(d.tmp[0]) != d.nComp {
- return UnsupportedError("SOS has wrong number of image components")
- }
- var scan [nColorComponent]struct {
- td uint8 // DC table selector.
- ta uint8 // AC table selector.
- }
- for i := 0; i < d.nComp; i++ {
- cs := d.tmp[1+2*i] // Component selector.
- if cs != d.comp[i].c {
- return UnsupportedError("scan components out of order")
- }
- scan[i].td = d.tmp[2+2*i] >> 4
- scan[i].ta = d.tmp[2+2*i] & 0x0f
- }
- // mxx and myy are the number of MCUs (Minimum Coded Units) in the image.
- h0, v0 := d.comp[0].h, d.comp[0].v // The h and v values from the Y components.
- mxx := (d.width + 8*h0 - 1) / (8 * h0)
- myy := (d.height + 8*v0 - 1) / (8 * v0)
- if d.img1 == nil && d.img3 == nil {
- d.makeImg(h0, v0, mxx, myy)
- }
-
- mcu, expectedRST := 0, uint8(rst0Marker)
- var (
- b block
- dc [nColorComponent]int
- )
- for my := 0; my < myy; my++ {
- for mx := 0; mx < mxx; mx++ {
- for i := 0; i < d.nComp; i++ {
- qt := &d.quant[d.comp[i].tq]
- for j := 0; j < d.comp[i].h*d.comp[i].v; j++ {
- // TODO(nigeltao): make this a "var b block" once the compiler's escape
- // analysis is good enough to allocate it on the stack, not the heap.
- b = block{}
-
- // Decode the DC coefficient, as specified in section F.2.2.1.
- value, err := d.decodeHuffman(&d.huff[dcTable][scan[i].td])
- if err != nil {
- return err
- }
- if value > 16 {
- return UnsupportedError("excessive DC component")
- }
- dcDelta, err := d.receiveExtend(value)
- if err != nil {
- return err
- }
- dc[i] += dcDelta
- b[0] = dc[i] * qt[0]
-
- // Decode the AC coefficients, as specified in section F.2.2.2.
- for k := 1; k < blockSize; k++ {
- value, err := d.decodeHuffman(&d.huff[acTable][scan[i].ta])
- if err != nil {
- return err
- }
- val0 := value >> 4
- val1 := value & 0x0f
- if val1 != 0 {
- k += int(val0)
- if k > blockSize {
- return FormatError("bad DCT index")
- }
- ac, err := d.receiveExtend(val1)
- if err != nil {
- return err
- }
- b[unzig[k]] = ac * qt[k]
- } else {
- if val0 != 0x0f {
- break
- }
- k += 0x0f
- }
- }
-
- // Perform the inverse DCT and store the MCU component to the image.
- if d.nComp == nGrayComponent {
- idct(d.img1.Pix[8*(my*d.img1.Stride+mx):], d.img1.Stride, &b)
- } else {
- switch i {
- case 0:
- mx0 := h0*mx + (j % 2)
- my0 := v0*my + (j / 2)
- idct(d.img3.Y[8*(my0*d.img3.YStride+mx0):], d.img3.YStride, &b)
- case 1:
- idct(d.img3.Cb[8*(my*d.img3.CStride+mx):], d.img3.CStride, &b)
- case 2:
- idct(d.img3.Cr[8*(my*d.img3.CStride+mx):], d.img3.CStride, &b)
- }
- }
- } // for j
- } // for i
- mcu++
- if d.ri > 0 && mcu%d.ri == 0 && mcu < mxx*myy {
- // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input,
- // but this one assumes well-formed input, and hence the restart marker follows immediately.
- _, err := io.ReadFull(d.r, d.tmp[0:2])
- if err != nil {
- return err
- }
- if d.tmp[0] != 0xff || d.tmp[1] != expectedRST {
- return FormatError("bad RST marker")
- }
- expectedRST++
- if expectedRST == rst7Marker+1 {
- expectedRST = rst0Marker
- }
- // Reset the Huffman decoder.
- d.b = bits{}
- // Reset the DC components, as per section F.2.1.3.1.
- dc = [nColorComponent]int{}
- }
- } // for mx
- } // for my
-
- return nil
-}
-
// Specified in section B.2.4.4.
func (d *decoder) processDRI(n int) error {
if n != 2 {
@@ -390,9 +249,26 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) {
return nil, FormatError("missing 0xff marker start")
}
marker := d.tmp[1]
+ for marker == 0xff {
+ // Section B.1.1.2 says, "Any marker may optionally be preceded by any
+ // number of fill bytes, which are bytes assigned code X'FF'".
+ marker, err = d.r.ReadByte()
+ if err != nil {
+ return nil, err
+ }
+ }
if marker == eoiMarker { // End Of Image.
break
}
+ if rst0Marker <= marker && marker <= rst7Marker {
+ // Figures B.2 and B.16 of the specification suggest that restart markers should
+ // only occur between Entropy Coded Segments and not after the final ECS.
+ // However, some encoders may generate incorrect JPEGs with a final restart
+ // marker. That restart marker will be seen here instead of inside the processSOS
+ // method, and is ignored as a harmless error. Restart markers have no extra data,
+ // so we check for this before we read the 16-bit length of the segment.
+ continue
+ }
// Read the 16-bit length of the segment. The value includes the 2 bytes for the
// length itself, so we subtract 2 to get the number of remaining bytes.
@@ -406,13 +282,12 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) {
}
switch {
- case marker == sof0Marker: // Start Of Frame (Baseline).
+ case marker == sof0Marker || marker == sof2Marker: // Start Of Frame.
+ d.progressive = marker == sof2Marker
err = d.processSOF(n)
if configOnly {
return nil, err
}
- case marker == sof2Marker: // Start Of Frame (Progressive).
- err = UnsupportedError("progressive mode")
case marker == dhtMarker: // Define Huffman Table.
err = d.processDHT(n)
case marker == dqtMarker: // Define Quantization Table.
@@ -421,7 +296,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, error) {
err = d.processSOS(n)
case marker == driMarker: // Define Restart Interval.
err = d.processDRI(n)
- case marker >= app0Marker && marker <= app15Marker || marker == comMarker: // APPlication specific, or COMment.
+ case app0Marker <= marker && marker <= app15Marker || marker == comMarker: // APPlication specific, or COMment.
err = d.ignore(n)
default:
err = UnsupportedError("unknown marker")
diff --git a/src/pkg/image/jpeg/reader_test.go b/src/pkg/image/jpeg/reader_test.go
new file mode 100644
index 000000000..b520a8ab1
--- /dev/null
+++ b/src/pkg/image/jpeg/reader_test.go
@@ -0,0 +1,157 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package jpeg
+
+import (
+ "bytes"
+ "fmt"
+ "image"
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+// TestDecodeProgressive tests that decoding the baseline and progressive
+// versions of the same image result in exactly the same pixel data, in YCbCr
+// space for color images, and Y space for grayscale images.
+func TestDecodeProgressive(t *testing.T) {
+ testCases := []string{
+ "../testdata/video-001",
+ "../testdata/video-001.q50.420",
+ "../testdata/video-001.q50.422",
+ "../testdata/video-001.q50.440",
+ "../testdata/video-001.q50.444",
+ "../testdata/video-005.gray.q50",
+ "../testdata/video-005.gray.q50.2x2",
+ }
+ for _, tc := range testCases {
+ m0, err := decodeFile(tc + ".jpeg")
+ if err != nil {
+ t.Errorf("%s: %v", tc+".jpeg", err)
+ continue
+ }
+ m1, err := decodeFile(tc + ".progressive.jpeg")
+ if err != nil {
+ t.Errorf("%s: %v", tc+".progressive.jpeg", err)
+ continue
+ }
+ if m0.Bounds() != m1.Bounds() {
+ t.Errorf("%s: bounds differ: %v and %v", tc, m0.Bounds(), m1.Bounds())
+ continue
+ }
+ switch m0 := m0.(type) {
+ case *image.YCbCr:
+ m1 := m1.(*image.YCbCr)
+ if err := check(m0.Bounds(), m0.Y, m1.Y, m0.YStride, m1.YStride); err != nil {
+ t.Errorf("%s (Y): %v", tc, err)
+ continue
+ }
+ if err := check(m0.Bounds(), m0.Cb, m1.Cb, m0.CStride, m1.CStride); err != nil {
+ t.Errorf("%s (Cb): %v", tc, err)
+ continue
+ }
+ if err := check(m0.Bounds(), m0.Cr, m1.Cr, m0.CStride, m1.CStride); err != nil {
+ t.Errorf("%s (Cr): %v", tc, err)
+ continue
+ }
+ case *image.Gray:
+ m1 := m1.(*image.Gray)
+ if err := check(m0.Bounds(), m0.Pix, m1.Pix, m0.Stride, m1.Stride); err != nil {
+ t.Errorf("%s: %v", tc, err)
+ continue
+ }
+ default:
+ t.Errorf("%s: unexpected image type %T", tc, m0)
+ continue
+ }
+ }
+}
+
+func decodeFile(filename string) (image.Image, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return Decode(f)
+
+}
+
+// check checks that the two pix data are equal, within the given bounds.
+func check(bounds image.Rectangle, pix0, pix1 []byte, stride0, stride1 int) error {
+ if len(pix0) != len(pix1) {
+ return fmt.Errorf("len(pix) %d and %d differ", len(pix0), len(pix1))
+ }
+ if stride0 != stride1 {
+ return fmt.Errorf("strides %d and %d differ", stride0, stride1)
+ }
+ if stride0%8 != 0 {
+ return fmt.Errorf("stride %d is not a multiple of 8", stride0)
+ }
+ // Compare the two pix data, one 8x8 block at a time.
+ for y := 0; y < len(pix0)/stride0; y += 8 {
+ for x := 0; x < stride0; x += 8 {
+ if x >= bounds.Max.X || y >= bounds.Max.Y {
+ // We don't care if the two pix data differ if the 8x8 block is
+ // entirely outside of the image's bounds. For example, this can
+ // occur with a 4:2:0 chroma subsampling and a 1x1 image. Baseline
+ // decoding works on the one 16x16 MCU as a whole; progressive
+ // decoding's first pass works on that 16x16 MCU as a whole but
+ // refinement passes only process one 8x8 block within the MCU.
+ continue
+ }
+
+ for j := 0; j < 8; j++ {
+ for i := 0; i < 8; i++ {
+ index := (y+j)*stride0 + (x + i)
+ if pix0[index] != pix1[index] {
+ return fmt.Errorf("blocks at (%d, %d) differ:\n%sand\n%s", x, y,
+ pixString(pix0, stride0, x, y),
+ pixString(pix1, stride1, x, y),
+ )
+ }
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func pixString(pix []byte, stride, x, y int) string {
+ s := bytes.NewBuffer(nil)
+ for j := 0; j < 8; j++ {
+ fmt.Fprintf(s, "\t")
+ for i := 0; i < 8; i++ {
+ fmt.Fprintf(s, "%02x ", pix[(y+j)*stride+(x+i)])
+ }
+ fmt.Fprintf(s, "\n")
+ }
+ return s.String()
+}
+
+func benchmarkDecode(b *testing.B, filename string) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ b.Fatal(err)
+ }
+ cfg, err := DecodeConfig(bytes.NewReader(data))
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.SetBytes(int64(cfg.Width * cfg.Height * 4))
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Decode(bytes.NewReader(data))
+ }
+}
+
+func BenchmarkDecodeBaseline(b *testing.B) {
+ benchmarkDecode(b, "../testdata/video-001.jpeg")
+}
+
+func BenchmarkDecodeProgressive(b *testing.B) {
+ benchmarkDecode(b, "../testdata/video-001.progressive.jpeg")
+}
diff --git a/src/pkg/image/jpeg/scan.go b/src/pkg/image/jpeg/scan.go
new file mode 100644
index 000000000..e3ae8ae44
--- /dev/null
+++ b/src/pkg/image/jpeg/scan.go
@@ -0,0 +1,432 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package jpeg
+
+import (
+ "image"
+ "io"
+)
+
+// makeImg allocates and initializes the destination image.
+func (d *decoder) makeImg(h0, v0, mxx, myy int) {
+ if d.nComp == nGrayComponent {
+ m := image.NewGray(image.Rect(0, 0, 8*mxx, 8*myy))
+ d.img1 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.Gray)
+ return
+ }
+ var subsampleRatio image.YCbCrSubsampleRatio
+ switch {
+ case h0 == 1 && v0 == 1:
+ subsampleRatio = image.YCbCrSubsampleRatio444
+ case h0 == 1 && v0 == 2:
+ subsampleRatio = image.YCbCrSubsampleRatio440
+ case h0 == 2 && v0 == 1:
+ subsampleRatio = image.YCbCrSubsampleRatio422
+ case h0 == 2 && v0 == 2:
+ subsampleRatio = image.YCbCrSubsampleRatio420
+ default:
+ panic("unreachable")
+ }
+ m := image.NewYCbCr(image.Rect(0, 0, 8*h0*mxx, 8*v0*myy), subsampleRatio)
+ d.img3 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.YCbCr)
+}
+
+// Specified in section B.2.3.
+func (d *decoder) processSOS(n int) error {
+ if d.nComp == 0 {
+ return FormatError("missing SOF marker")
+ }
+ if n < 6 || 4+2*d.nComp < n || n%2 != 0 {
+ return FormatError("SOS has wrong length")
+ }
+ _, err := io.ReadFull(d.r, d.tmp[:n])
+ if err != nil {
+ return err
+ }
+ nComp := int(d.tmp[0])
+ if n != 4+2*nComp {
+ return FormatError("SOS length inconsistent with number of components")
+ }
+ var scan [nColorComponent]struct {
+ compIndex uint8
+ td uint8 // DC table selector.
+ ta uint8 // AC table selector.
+ }
+ for i := 0; i < nComp; i++ {
+ cs := d.tmp[1+2*i] // Component selector.
+ compIndex := -1
+ for j, comp := range d.comp {
+ if cs == comp.c {
+ compIndex = j
+ }
+ }
+ if compIndex < 0 {
+ return FormatError("unknown component selector")
+ }
+ scan[i].compIndex = uint8(compIndex)
+ scan[i].td = d.tmp[2+2*i] >> 4
+ scan[i].ta = d.tmp[2+2*i] & 0x0f
+ }
+
+ // zigStart and zigEnd are the spectral selection bounds.
+ // ah and al are the successive approximation high and low values.
+ // The spec calls these values Ss, Se, Ah and Al.
+ //
+ // For progressive JPEGs, these are the two more-or-less independent
+ // aspects of progression. Spectral selection progression is when not
+ // all of a block's 64 DCT coefficients are transmitted in one pass.
+ // For example, three passes could transmit coefficient 0 (the DC
+ // component), coefficients 1-5, and coefficients 6-63, in zig-zag
+ // order. Successive approximation is when not all of the bits of a
+ // band of coefficients are transmitted in one pass. For example,
+ // three passes could transmit the 6 most significant bits, followed
+ // by the second-least significant bit, followed by the least
+ // significant bit.
+ //
+ // For baseline JPEGs, these parameters are hard-coded to 0/63/0/0.
+ zigStart, zigEnd, ah, al := int32(0), int32(blockSize-1), uint32(0), uint32(0)
+ if d.progressive {
+ zigStart = int32(d.tmp[1+2*nComp])
+ zigEnd = int32(d.tmp[2+2*nComp])
+ ah = uint32(d.tmp[3+2*nComp] >> 4)
+ al = uint32(d.tmp[3+2*nComp] & 0x0f)
+ if (zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || blockSize <= zigEnd {
+ return FormatError("bad spectral selection bounds")
+ }
+ if zigStart != 0 && nComp != 1 {
+ return FormatError("progressive AC coefficients for more than one component")
+ }
+ if ah != 0 && ah != al+1 {
+ return FormatError("bad successive approximation values")
+ }
+ }
+
+ // mxx and myy are the number of MCUs (Minimum Coded Units) in the image.
+ h0, v0 := d.comp[0].h, d.comp[0].v // The h and v values from the Y components.
+ mxx := (d.width + 8*h0 - 1) / (8 * h0)
+ myy := (d.height + 8*v0 - 1) / (8 * v0)
+ if d.img1 == nil && d.img3 == nil {
+ d.makeImg(h0, v0, mxx, myy)
+ if d.progressive {
+ for i := 0; i < nComp; i++ {
+ compIndex := scan[i].compIndex
+ d.progCoeffs[compIndex] = make([]block, mxx*myy*d.comp[compIndex].h*d.comp[compIndex].v)
+ }
+ }
+ }
+
+ d.b = bits{}
+ mcu, expectedRST := 0, uint8(rst0Marker)
+ var (
+ // b is the decoded coefficients, in natural (not zig-zag) order.
+ b block
+ dc [nColorComponent]int32
+ // mx0 and my0 are the location of the current (in terms of 8x8 blocks).
+ // For example, with 4:2:0 chroma subsampling, the block whose top left
+ // pixel co-ordinates are (16, 8) is the third block in the first row:
+ // mx0 is 2 and my0 is 0, even though the pixel is in the second MCU.
+ // TODO(nigeltao): rename mx0 and my0 to bx and by?
+ mx0, my0 int
+ blockCount int
+ )
+ for my := 0; my < myy; my++ {
+ for mx := 0; mx < mxx; mx++ {
+ for i := 0; i < nComp; i++ {
+ compIndex := scan[i].compIndex
+ qt := &d.quant[d.comp[compIndex].tq]
+ for j := 0; j < d.comp[compIndex].h*d.comp[compIndex].v; j++ {
+ // The blocks are traversed one MCU at a time. For 4:2:0 chroma
+ // subsampling, there are four Y 8x8 blocks in every 16x16 MCU.
+ // For a baseline 32x16 pixel image, the Y blocks visiting order is:
+ // 0 1 4 5
+ // 2 3 6 7
+ //
+ // For progressive images, the DC data blocks (zigStart == 0) are traversed
+ // as above, but AC data blocks are traversed left to right, top to bottom:
+ // 0 1 2 3
+ // 4 5 6 7
+ //
+ // To further complicate matters, there is no AC data for any blocks that
+ // are inside the image at the MCU level but outside the image at the pixel
+ // level. For example, a 24x16 pixel 4:2:0 progressive image consists of
+ // two 16x16 MCUs. The earlier scans will process 8 Y blocks:
+ // 0 1 4 5
+ // 2 3 6 7
+ // The later scans will process only 6 Y blocks:
+ // 0 1 2
+ // 3 4 5
+ if zigStart == 0 {
+ mx0, my0 = d.comp[compIndex].h*mx, d.comp[compIndex].v*my
+ if h0 == 1 {
+ my0 += j
+ } else {
+ mx0 += j % 2
+ my0 += j / 2
+ }
+ } else {
+ q := mxx * d.comp[compIndex].h
+ mx0 = blockCount % q
+ my0 = blockCount / q
+ blockCount++
+ if mx0*8 >= d.width || my0*8 >= d.height {
+ continue
+ }
+ }
+
+ // Load the previous partially decoded coefficients, if applicable.
+ if d.progressive {
+ b = d.progCoeffs[compIndex][my0*mxx*d.comp[compIndex].h+mx0]
+ } else {
+ b = block{}
+ }
+
+ if ah != 0 {
+ if err := d.refine(&b, &d.huff[acTable][scan[i].ta], zigStart, zigEnd, 1<<al); err != nil {
+ return err
+ }
+ } else {
+ zig := zigStart
+ if zig == 0 {
+ zig++
+ // Decode the DC coefficient, as specified in section F.2.2.1.
+ value, err := d.decodeHuffman(&d.huff[dcTable][scan[i].td])
+ if err != nil {
+ return err
+ }
+ if value > 16 {
+ return UnsupportedError("excessive DC component")
+ }
+ dcDelta, err := d.receiveExtend(value)
+ if err != nil {
+ return err
+ }
+ dc[compIndex] += dcDelta
+ b[0] = dc[compIndex] << al
+ }
+
+ if zig <= zigEnd && d.eobRun > 0 {
+ d.eobRun--
+ } else {
+ // Decode the AC coefficients, as specified in section F.2.2.2.
+ for ; zig <= zigEnd; zig++ {
+ value, err := d.decodeHuffman(&d.huff[acTable][scan[i].ta])
+ if err != nil {
+ return err
+ }
+ val0 := value >> 4
+ val1 := value & 0x0f
+ if val1 != 0 {
+ zig += int32(val0)
+ if zig > zigEnd {
+ break
+ }
+ ac, err := d.receiveExtend(val1)
+ if err != nil {
+ return err
+ }
+ b[unzig[zig]] = ac << al
+ } else {
+ if val0 != 0x0f {
+ d.eobRun = uint16(1 << val0)
+ if val0 != 0 {
+ bits, err := d.decodeBits(int(val0))
+ if err != nil {
+ return err
+ }
+ d.eobRun |= uint16(bits)
+ }
+ d.eobRun--
+ break
+ }
+ zig += 0x0f
+ }
+ }
+ }
+ }
+
+ if d.progressive {
+ if zigEnd != blockSize-1 || al != 0 {
+ // We haven't completely decoded this 8x8 block. Save the coefficients.
+ d.progCoeffs[compIndex][my0*mxx*d.comp[compIndex].h+mx0] = b
+ // At this point, we could execute the rest of the loop body to dequantize and
+ // perform the inverse DCT, to save early stages of a progressive image to the
+ // *image.YCbCr buffers (the whole point of progressive encoding), but in Go,
+ // the jpeg.Decode function does not return until the entire image is decoded,
+ // so we "continue" here to avoid wasted computation.
+ continue
+ }
+ }
+
+ // Dequantize, perform the inverse DCT and store the block to the image.
+ for zig := 0; zig < blockSize; zig++ {
+ b[unzig[zig]] *= qt[zig]
+ }
+ idct(&b)
+ dst, stride := []byte(nil), 0
+ if d.nComp == nGrayComponent {
+ dst, stride = d.img1.Pix[8*(my0*d.img1.Stride+mx0):], d.img1.Stride
+ } else {
+ switch compIndex {
+ case 0:
+ dst, stride = d.img3.Y[8*(my0*d.img3.YStride+mx0):], d.img3.YStride
+ case 1:
+ dst, stride = d.img3.Cb[8*(my0*d.img3.CStride+mx0):], d.img3.CStride
+ case 2:
+ dst, stride = d.img3.Cr[8*(my0*d.img3.CStride+mx0):], d.img3.CStride
+ default:
+ return UnsupportedError("too many components")
+ }
+ }
+ // Level shift by +128, clip to [0, 255], and write to dst.
+ for y := 0; y < 8; y++ {
+ y8 := y * 8
+ yStride := y * stride
+ for x := 0; x < 8; x++ {
+ c := b[y8+x]
+ if c < -128 {
+ c = 0
+ } else if c > 127 {
+ c = 255
+ } else {
+ c += 128
+ }
+ dst[yStride+x] = uint8(c)
+ }
+ }
+ } // for j
+ } // for i
+ mcu++
+ if d.ri > 0 && mcu%d.ri == 0 && mcu < mxx*myy {
+ // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input,
+ // but this one assumes well-formed input, and hence the restart marker follows immediately.
+ _, err := io.ReadFull(d.r, d.tmp[0:2])
+ if err != nil {
+ return err
+ }
+ if d.tmp[0] != 0xff || d.tmp[1] != expectedRST {
+ return FormatError("bad RST marker")
+ }
+ expectedRST++
+ if expectedRST == rst7Marker+1 {
+ expectedRST = rst0Marker
+ }
+ // Reset the Huffman decoder.
+ d.b = bits{}
+ // Reset the DC components, as per section F.2.1.3.1.
+ dc = [nColorComponent]int32{}
+ // Reset the progressive decoder state, as per section G.1.2.2.
+ d.eobRun = 0
+ }
+ } // for mx
+ } // for my
+
+ return nil
+}
+
+// refine decodes a successive approximation refinement block, as specified in
+// section G.1.2.
+func (d *decoder) refine(b *block, h *huffman, zigStart, zigEnd, delta int32) error {
+ // Refining a DC component is trivial.
+ if zigStart == 0 {
+ if zigEnd != 0 {
+ panic("unreachable")
+ }
+ bit, err := d.decodeBit()
+ if err != nil {
+ return err
+ }
+ if bit {
+ b[0] |= delta
+ }
+ return nil
+ }
+
+ // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3.
+ zig := zigStart
+ if d.eobRun == 0 {
+ loop:
+ for ; zig <= zigEnd; zig++ {
+ z := int32(0)
+ value, err := d.decodeHuffman(h)
+ if err != nil {
+ return err
+ }
+ val0 := value >> 4
+ val1 := value & 0x0f
+
+ switch val1 {
+ case 0:
+ if val0 != 0x0f {
+ d.eobRun = uint16(1 << val0)
+ if val0 != 0 {
+ bits, err := d.decodeBits(int(val0))
+ if err != nil {
+ return err
+ }
+ d.eobRun |= uint16(bits)
+ }
+ break loop
+ }
+ case 1:
+ z = delta
+ bit, err := d.decodeBit()
+ if err != nil {
+ return err
+ }
+ if !bit {
+ z = -z
+ }
+ default:
+ return FormatError("unexpected Huffman code")
+ }
+
+ zig, err = d.refineNonZeroes(b, zig, zigEnd, int32(val0), delta)
+ if err != nil {
+ return err
+ }
+ if zig > zigEnd {
+ return FormatError("too many coefficients")
+ }
+ if z != 0 {
+ b[unzig[zig]] = z
+ }
+ }
+ }
+ if d.eobRun > 0 {
+ d.eobRun--
+ if _, err := d.refineNonZeroes(b, zig, zigEnd, -1, delta); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// refineNonZeroes refines non-zero entries of b in zig-zag order. If nz >= 0,
+// the first nz zero entries are skipped over.
+func (d *decoder) refineNonZeroes(b *block, zig, zigEnd, nz, delta int32) (int32, error) {
+ for ; zig <= zigEnd; zig++ {
+ u := unzig[zig]
+ if b[u] == 0 {
+ if nz == 0 {
+ break
+ }
+ nz--
+ continue
+ }
+ bit, err := d.decodeBit()
+ if err != nil {
+ return 0, err
+ }
+ if !bit {
+ continue
+ }
+ if b[u] >= 0 {
+ b[u] += delta
+ } else {
+ b[u] -= delta
+ }
+ }
+ return zig, nil
+}
diff --git a/src/pkg/image/jpeg/writer.go b/src/pkg/image/jpeg/writer.go
index 3322c09fe..c58fbf305 100644
--- a/src/pkg/image/jpeg/writer.go
+++ b/src/pkg/image/jpeg/writer.go
@@ -21,7 +21,7 @@ func min(x, y int) int {
}
// div returns a/b rounded to the nearest integer, instead of rounded to zero.
-func div(a int, b int) int {
+func div(a, b int32) int32 {
if a >= 0 {
return (a + (b >> 1)) / b
}
@@ -56,26 +56,28 @@ const (
nQuantIndex
)
-// unscaledQuant are the unscaled quantization tables. Each encoder copies and
-// scales the tables according to its quality parameter.
+// unscaledQuant are the unscaled quantization tables in zig-zag order. Each
+// encoder copies and scales the tables according to its quality parameter.
+// The values are derived from section K.1 after converting from natural to
+// zig-zag order.
var unscaledQuant = [nQuantIndex][blockSize]byte{
// Luminance.
{
- 16, 11, 10, 16, 24, 40, 51, 61,
- 12, 12, 14, 19, 26, 58, 60, 55,
- 14, 13, 16, 24, 40, 57, 69, 56,
- 14, 17, 22, 29, 51, 87, 80, 62,
- 18, 22, 37, 56, 68, 109, 103, 77,
- 24, 35, 55, 64, 81, 104, 113, 92,
- 49, 64, 78, 87, 103, 121, 120, 101,
- 72, 92, 95, 98, 112, 100, 103, 99,
+ 16, 11, 12, 14, 12, 10, 16, 14,
+ 13, 14, 18, 17, 16, 19, 24, 40,
+ 26, 24, 22, 22, 24, 49, 35, 37,
+ 29, 40, 58, 51, 61, 60, 57, 51,
+ 56, 55, 64, 72, 92, 78, 64, 68,
+ 87, 69, 55, 56, 80, 109, 81, 87,
+ 95, 98, 103, 104, 103, 62, 77, 113,
+ 121, 112, 100, 120, 92, 101, 103, 99,
},
// Chrominance.
{
- 17, 18, 24, 47, 99, 99, 99, 99,
- 18, 21, 26, 66, 99, 99, 99, 99,
- 24, 26, 56, 99, 99, 99, 99, 99,
- 47, 66, 99, 99, 99, 99, 99, 99,
+ 17, 18, 18, 24, 21, 24, 47, 26,
+ 26, 47, 99, 66, 56, 66, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
@@ -208,8 +210,8 @@ func init() {
// writer is a buffered writer.
type writer interface {
Flush() error
- Write([]byte) (int, error)
- WriteByte(byte) error
+ io.Writer
+ io.ByteWriter
}
// encoder encodes an image to the JPEG format.
@@ -222,7 +224,7 @@ type encoder struct {
buf [16]byte
// bits and nBits are accumulated bits to write to w.
bits, nBits uint32
- // quant is the scaled quantization tables.
+ // quant is the scaled quantization tables, in zig-zag order.
quant [nQuantIndex][blockSize]byte
}
@@ -266,14 +268,14 @@ func (e *encoder) emit(bits, nBits uint32) {
}
// emitHuff emits the given value with the given Huffman encoder.
-func (e *encoder) emitHuff(h huffIndex, value int) {
+func (e *encoder) emitHuff(h huffIndex, value int32) {
x := theHuffmanLUT[h][value]
e.emit(x&(1<<24-1), x>>24)
}
// emitHuffRLE emits a run of runLength copies of value encoded with the given
// Huffman encoder.
-func (e *encoder) emitHuffRLE(h huffIndex, runLength, value int) {
+func (e *encoder) emitHuffRLE(h huffIndex, runLength, value int32) {
a, b := value, value
if a < 0 {
a, b = -value, value-1
@@ -284,7 +286,7 @@ func (e *encoder) emitHuffRLE(h huffIndex, runLength, value int) {
} else {
nBits = 8 + uint32(bitCount[a>>8])
}
- e.emitHuff(h, runLength<<4|int(nBits))
+ e.emitHuff(h, runLength<<4|int32(nBits))
if nBits > 0 {
e.emit(uint32(b)&(1<<nBits-1), nBits)
}
@@ -301,7 +303,7 @@ func (e *encoder) writeMarkerHeader(marker uint8, markerlen int) {
// writeDQT writes the Define Quantization Table marker.
func (e *encoder) writeDQT() {
- markerlen := 2 + int(nQuantIndex)*(1+blockSize)
+ const markerlen = 2 + int(nQuantIndex)*(1+blockSize)
e.writeMarkerHeader(dqtMarker, markerlen)
for i := range e.quant {
e.writeByte(uint8(i))
@@ -311,7 +313,7 @@ func (e *encoder) writeDQT() {
// writeSOF0 writes the Start Of Frame (Baseline) marker.
func (e *encoder) writeSOF0(size image.Point) {
- markerlen := 8 + 3*nColorComponent
+ const markerlen = 8 + 3*nColorComponent
e.writeMarkerHeader(sof0Marker, markerlen)
e.buf[0] = 8 // 8-bit color.
e.buf[1] = uint8(size.Y >> 8)
@@ -344,15 +346,16 @@ func (e *encoder) writeDHT() {
// writeBlock writes a block of pixel data using the given quantization table,
// returning the post-quantized DC value of the DCT-transformed block.
-func (e *encoder) writeBlock(b *block, q quantIndex, prevDC int) int {
+// b is in natural (not zig-zag) order.
+func (e *encoder) writeBlock(b *block, q quantIndex, prevDC int32) int32 {
fdct(b)
// Emit the DC delta.
- dc := div(b[0], (8 * int(e.quant[q][0])))
+ dc := div(b[0], 8*int32(e.quant[q][0]))
e.emitHuffRLE(huffIndex(2*q+0), 0, dc-prevDC)
// Emit the AC components.
- h, runLength := huffIndex(2*q+1), 0
- for k := 1; k < blockSize; k++ {
- ac := div(b[unzig[k]], (8 * int(e.quant[q][k])))
+ h, runLength := huffIndex(2*q+1), int32(0)
+ for zig := 1; zig < blockSize; zig++ {
+ ac := div(b[unzig[zig]], 8*int32(e.quant[q][zig]))
if ac == 0 {
runLength++
} else {
@@ -380,9 +383,9 @@ func toYCbCr(m image.Image, p image.Point, yBlock, cbBlock, crBlock *block) {
for i := 0; i < 8; i++ {
r, g, b, _ := m.At(min(p.X+i, xmax), min(p.Y+j, ymax)).RGBA()
yy, cb, cr := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
- yBlock[8*j+i] = int(yy)
- cbBlock[8*j+i] = int(cb)
- crBlock[8*j+i] = int(cr)
+ yBlock[8*j+i] = int32(yy)
+ cbBlock[8*j+i] = int32(cb)
+ crBlock[8*j+i] = int32(cr)
}
}
}
@@ -405,9 +408,9 @@ func rgbaToYCbCr(m *image.RGBA, p image.Point, yBlock, cbBlock, crBlock *block)
}
pix := m.Pix[offset+sx*4:]
yy, cb, cr := color.RGBToYCbCr(pix[0], pix[1], pix[2])
- yBlock[8*j+i] = int(yy)
- cbBlock[8*j+i] = int(cb)
- crBlock[8*j+i] = int(cr)
+ yBlock[8*j+i] = int32(yy)
+ cbBlock[8*j+i] = int32(cb)
+ crBlock[8*j+i] = int32(cr)
}
}
}
@@ -433,10 +436,12 @@ func scale(dst *block, src *[4]block) {
// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
// - component 2 uses DC table 1 and AC table 1 "\x02\x11",
// - component 3 uses DC table 1 and AC table 1 "\x03\x11",
-// - padding "\x00\x00\x00".
+// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
+// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
+// should be 0x00, 0x3f, 0x00<<4 | 0x00.
var sosHeader = []byte{
0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02,
- 0x11, 0x03, 0x11, 0x00, 0x00, 0x00,
+ 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
}
// writeSOS writes the StartOfScan marker.
@@ -444,12 +449,11 @@ func (e *encoder) writeSOS(m image.Image) {
e.write(sosHeader)
var (
// Scratch buffers to hold the YCbCr values.
- yBlock block
- cbBlock [4]block
- crBlock [4]block
- cBlock block
+ // The blocks are in natural (not zig-zag) order.
+ b block
+ cb, cr [4]block
// DC components are delta-encoded.
- prevDCY, prevDCCb, prevDCCr int
+ prevDCY, prevDCCb, prevDCCr int32
)
bounds := m.Bounds()
rgba, _ := m.(*image.RGBA)
@@ -460,16 +464,16 @@ func (e *encoder) writeSOS(m image.Image) {
yOff := (i & 2) * 4
p := image.Pt(x+xOff, y+yOff)
if rgba != nil {
- rgbaToYCbCr(rgba, p, &yBlock, &cbBlock[i], &crBlock[i])
+ rgbaToYCbCr(rgba, p, &b, &cb[i], &cr[i])
} else {
- toYCbCr(m, p, &yBlock, &cbBlock[i], &crBlock[i])
+ toYCbCr(m, p, &b, &cb[i], &cr[i])
}
- prevDCY = e.writeBlock(&yBlock, 0, prevDCY)
+ prevDCY = e.writeBlock(&b, 0, prevDCY)
}
- scale(&cBlock, &cbBlock)
- prevDCCb = e.writeBlock(&cBlock, 1, prevDCCb)
- scale(&cBlock, &crBlock)
- prevDCCr = e.writeBlock(&cBlock, 1, prevDCCr)
+ scale(&b, &cb)
+ prevDCCb = e.writeBlock(&b, 1, prevDCCb)
+ scale(&b, &cr)
+ prevDCCr = e.writeBlock(&b, 1, prevDCCr)
}
}
// Pad the last byte with 1's.
diff --git a/src/pkg/image/jpeg/writer_test.go b/src/pkg/image/jpeg/writer_test.go
index b8e8fa34e..0b2143f5b 100644
--- a/src/pkg/image/jpeg/writer_test.go
+++ b/src/pkg/image/jpeg/writer_test.go
@@ -6,6 +6,7 @@ package jpeg
import (
"bytes"
+ "fmt"
"image"
"image/color"
"image/png"
@@ -15,6 +16,87 @@ import (
"testing"
)
+// zigzag maps from the natural ordering to the zig-zag ordering. For example,
+// zigzag[0*8 + 3] is the zig-zag sequence number of the element in the fourth
+// column and first row.
+var zigzag = [blockSize]int{
+ 0, 1, 5, 6, 14, 15, 27, 28,
+ 2, 4, 7, 13, 16, 26, 29, 42,
+ 3, 8, 12, 17, 25, 30, 41, 43,
+ 9, 11, 18, 24, 31, 40, 44, 53,
+ 10, 19, 23, 32, 39, 45, 52, 54,
+ 20, 22, 33, 38, 46, 51, 55, 60,
+ 21, 34, 37, 47, 50, 56, 59, 61,
+ 35, 36, 48, 49, 57, 58, 62, 63,
+}
+
+func TestZigUnzig(t *testing.T) {
+ for i := 0; i < blockSize; i++ {
+ if unzig[zigzag[i]] != i {
+ t.Errorf("unzig[zigzag[%d]] == %d", i, unzig[zigzag[i]])
+ }
+ if zigzag[unzig[i]] != i {
+ t.Errorf("zigzag[unzig[%d]] == %d", i, zigzag[unzig[i]])
+ }
+ }
+}
+
+// unscaledQuantInNaturalOrder are the unscaled quantization tables in
+// natural (not zig-zag) order, as specified in section K.1.
+var unscaledQuantInNaturalOrder = [nQuantIndex][blockSize]byte{
+ // Luminance.
+ {
+ 16, 11, 10, 16, 24, 40, 51, 61,
+ 12, 12, 14, 19, 26, 58, 60, 55,
+ 14, 13, 16, 24, 40, 57, 69, 56,
+ 14, 17, 22, 29, 51, 87, 80, 62,
+ 18, 22, 37, 56, 68, 109, 103, 77,
+ 24, 35, 55, 64, 81, 104, 113, 92,
+ 49, 64, 78, 87, 103, 121, 120, 101,
+ 72, 92, 95, 98, 112, 100, 103, 99,
+ },
+ // Chrominance.
+ {
+ 17, 18, 24, 47, 99, 99, 99, 99,
+ 18, 21, 26, 66, 99, 99, 99, 99,
+ 24, 26, 56, 99, 99, 99, 99, 99,
+ 47, 66, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ },
+}
+
+func TestUnscaledQuant(t *testing.T) {
+ bad := false
+ for i := quantIndex(0); i < nQuantIndex; i++ {
+ for zig := 0; zig < blockSize; zig++ {
+ got := unscaledQuant[i][zig]
+ want := unscaledQuantInNaturalOrder[i][unzig[zig]]
+ if got != want {
+ t.Errorf("i=%d, zig=%d: got %d, want %d", i, zig, got, want)
+ bad = true
+ }
+ }
+ }
+ if bad {
+ names := [nQuantIndex]string{"Luminance", "Chrominance"}
+ buf := &bytes.Buffer{}
+ for i, name := range names {
+ fmt.Fprintf(buf, "// %s.\n{\n", name)
+ for zig := 0; zig < blockSize; zig++ {
+ fmt.Fprintf(buf, "%d, ", unscaledQuantInNaturalOrder[i][unzig[zig]])
+ if zig%8 == 7 {
+ buf.WriteString("\n")
+ }
+ }
+ buf.WriteString("},\n")
+ }
+ t.Logf("expected unscaledQuant values:\n%s", buf.String())
+ }
+}
+
var testCase = []struct {
filename string
quality int
@@ -89,24 +171,21 @@ func TestWriter(t *testing.T) {
}
}
-func BenchmarkEncodeRGBOpaque(b *testing.B) {
+func BenchmarkEncode(b *testing.B) {
b.StopTimer()
img := image.NewRGBA(image.Rect(0, 0, 640, 480))
- // Set all pixels to 0xFF alpha to force opaque mode.
bo := img.Bounds()
rnd := rand.New(rand.NewSource(123))
for y := bo.Min.Y; y < bo.Max.Y; y++ {
for x := bo.Min.X; x < bo.Max.X; x++ {
- img.Set(x, y, color.RGBA{
+ img.SetRGBA(x, y, color.RGBA{
uint8(rnd.Intn(256)),
uint8(rnd.Intn(256)),
uint8(rnd.Intn(256)),
- 255})
+ 255,
+ })
}
}
- if !img.Opaque() {
- b.Fatal("expected image to be opaque")
- }
b.SetBytes(640 * 480 * 4)
b.StartTimer()
options := &Options{Quality: 90}
diff --git a/src/pkg/image/names.go b/src/pkg/image/names.go
index 55f634c17..04ee2cfb4 100644
--- a/src/pkg/image/names.go
+++ b/src/pkg/image/names.go
@@ -20,7 +20,7 @@ var (
)
// Uniform is an infinite-sized Image of uniform color.
-// It implements the color.Color, color.ColorModel, and Image interfaces.
+// It implements the color.Color, color.Model, and Image interfaces.
type Uniform struct {
C color.Color
}
diff --git a/src/pkg/image/png/paeth.go b/src/pkg/image/png/paeth.go
new file mode 100644
index 000000000..37978aa66
--- /dev/null
+++ b/src/pkg/image/png/paeth.go
@@ -0,0 +1,70 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package png
+
+// paeth implements the Paeth filter function, as per the PNG specification.
+func paeth(a, b, c uint8) uint8 {
+ // This is an optimized version of the sample code in the PNG spec.
+ // For example, the sample code starts with:
+ // p := int(a) + int(b) - int(c)
+ // pa := abs(p - int(a))
+ // but the optimized form uses fewer arithmetic operations:
+ // pa := int(b) - int(c)
+ // pa = abs(pa)
+ pc := int(c)
+ pa := int(b) - pc
+ pb := int(a) - pc
+ pc = pa + pb
+ if pa < 0 {
+ pa = -pa
+ }
+ if pb < 0 {
+ pb = -pb
+ }
+ if pc < 0 {
+ pc = -pc
+ }
+ if pa <= pb && pa <= pc {
+ return a
+ } else if pb <= pc {
+ return b
+ }
+ return c
+}
+
+// filterPaeth applies the Paeth filter to the cdat slice.
+// cdat is the current row's data, pdat is the previous row's data.
+func filterPaeth(cdat, pdat []byte, bytesPerPixel int) {
+ var a, b, c, pa, pb, pc int
+ for i := 0; i < bytesPerPixel; i++ {
+ a, c = 0, 0
+ for j := i; j < len(cdat); j += bytesPerPixel {
+ b = int(pdat[j])
+ pa = b - c
+ pb = a - c
+ pc = pa + pb
+ if pa < 0 {
+ pa = -pa
+ }
+ if pb < 0 {
+ pb = -pb
+ }
+ if pc < 0 {
+ pc = -pc
+ }
+ if pa <= pb && pa <= pc {
+ // No-op.
+ } else if pb <= pc {
+ a = b
+ } else {
+ a = c
+ }
+ a += int(cdat[j])
+ a &= 0xff
+ cdat[j] = uint8(a)
+ c = b
+ }
+ }
+}
diff --git a/src/pkg/image/png/paeth_test.go b/src/pkg/image/png/paeth_test.go
new file mode 100644
index 000000000..bb084861a
--- /dev/null
+++ b/src/pkg/image/png/paeth_test.go
@@ -0,0 +1,91 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package png
+
+import (
+ "bytes"
+ "math/rand"
+ "testing"
+)
+
+func abs(x int) int {
+ if x < 0 {
+ return -x
+ }
+ return x
+}
+
+// slowPaeth is a slow but simple implementation of the Paeth function.
+// It is a straight port of the sample code in the PNG spec, section 9.4.
+func slowPaeth(a, b, c uint8) uint8 {
+ p := int(a) + int(b) - int(c)
+ pa := abs(p - int(a))
+ pb := abs(p - int(b))
+ pc := abs(p - int(c))
+ if pa <= pb && pa <= pc {
+ return a
+ } else if pb <= pc {
+ return b
+ }
+ return c
+}
+
+// slowFilterPaeth is a slow but simple implementation of func filterPaeth.
+func slowFilterPaeth(cdat, pdat []byte, bytesPerPixel int) {
+ for i := 0; i < bytesPerPixel; i++ {
+ cdat[i] += paeth(0, pdat[i], 0)
+ }
+ for i := bytesPerPixel; i < len(cdat); i++ {
+ cdat[i] += paeth(cdat[i-bytesPerPixel], pdat[i], pdat[i-bytesPerPixel])
+ }
+}
+
+func TestPaeth(t *testing.T) {
+ for a := 0; a < 256; a += 15 {
+ for b := 0; b < 256; b += 15 {
+ for c := 0; c < 256; c += 15 {
+ got := paeth(uint8(a), uint8(b), uint8(c))
+ want := slowPaeth(uint8(a), uint8(b), uint8(c))
+ if got != want {
+ t.Errorf("a, b, c = %d, %d, %d: got %d, want %d", a, b, c, got, want)
+ }
+ }
+ }
+ }
+}
+
+func BenchmarkPaeth(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ paeth(uint8(i>>16), uint8(i>>8), uint8(i))
+ }
+}
+
+func TestPaethDecode(t *testing.T) {
+ pdat0 := make([]byte, 32)
+ pdat1 := make([]byte, 32)
+ pdat2 := make([]byte, 32)
+ cdat0 := make([]byte, 32)
+ cdat1 := make([]byte, 32)
+ cdat2 := make([]byte, 32)
+ r := rand.New(rand.NewSource(1))
+ for bytesPerPixel := 1; bytesPerPixel <= 8; bytesPerPixel++ {
+ for i := 0; i < 100; i++ {
+ for j := range pdat0 {
+ pdat0[j] = uint8(r.Uint32())
+ cdat0[j] = uint8(r.Uint32())
+ }
+ copy(pdat1, pdat0)
+ copy(pdat2, pdat0)
+ copy(cdat1, cdat0)
+ copy(cdat2, cdat0)
+ filterPaeth(cdat1, pdat1, bytesPerPixel)
+ slowFilterPaeth(cdat2, pdat2, bytesPerPixel)
+ if !bytes.Equal(cdat1, cdat2) {
+ t.Errorf("bytesPerPixel: %d\npdat0: % x\ncdat0: % x\ngot: % x\nwant: % x", bytesPerPixel, pdat0, cdat0, cdat1, cdat2)
+ break
+ }
+ }
+ }
+}
diff --git a/src/pkg/image/png/reader.go b/src/pkg/image/png/reader.go
index fe07d60a9..a6bf86ede 100644
--- a/src/pkg/image/png/reader.go
+++ b/src/pkg/image/png/reader.go
@@ -98,13 +98,6 @@ type UnsupportedError string
func (e UnsupportedError) Error() string { return "png: unsupported feature: " + string(e) }
-func abs(x int) int {
- if x < 0 {
- return -x
- }
- return x
-}
-
func min(a, b int) int {
if a < b {
return a
@@ -200,10 +193,19 @@ func (d *decoder) parsePLTE(length uint32) error {
d.crc.Write(d.tmp[:n])
switch d.cb {
case cbP1, cbP2, cbP4, cbP8:
- d.palette = color.Palette(make([]color.Color, np))
+ d.palette = make(color.Palette, 256)
for i := 0; i < np; i++ {
d.palette[i] = color.RGBA{d.tmp[3*i+0], d.tmp[3*i+1], d.tmp[3*i+2], 0xff}
}
+ for i := np; i < 256; i++ {
+ // Initialize the rest of the palette to opaque black. The spec (section
+ // 11.2.3) says that "any out-of-range pixel value found in the image data
+ // is an error", but some real-world PNG files have out-of-range pixel
+ // values. We fall back to opaque black, the same as libpng 1.5.13;
+ // ImageMagick 6.5.7 returns an error.
+ d.palette[i] = color.RGBA{0x00, 0x00, 0x00, 0xff}
+ }
+ d.palette = d.palette[:np]
case cbTC8, cbTCA8, cbTC16, cbTCA16:
// As per the PNG spec, a PLTE chunk is optional (and for practical purposes,
// ignorable) for the ctTrueColor and ctTrueColorAlpha color types (section 4.1.2).
@@ -228,12 +230,12 @@ func (d *decoder) parsetRNS(length uint32) error {
case cbTC8, cbTC16:
return UnsupportedError("truecolor transparency")
case cbP1, cbP2, cbP4, cbP8:
- if n > len(d.palette) {
- return FormatError("bad tRNS length")
+ if len(d.palette) < n {
+ d.palette = d.palette[:n]
}
for i := 0; i < n; i++ {
rgba := d.palette[i].(color.RGBA)
- d.palette[i] = color.RGBA{rgba.R, rgba.G, rgba.B, d.tmp[i]}
+ d.palette[i] = color.NRGBA{rgba.R, rgba.G, rgba.B, d.tmp[i]}
}
case cbGA8, cbGA16, cbTCA8, cbTCA16:
return FormatError("tRNS, color type mismatch")
@@ -241,20 +243,6 @@ func (d *decoder) parsetRNS(length uint32) error {
return d.verifyChecksum()
}
-// The Paeth filter function, as per the PNG specification.
-func paeth(a, b, c uint8) uint8 {
- p := int(a) + int(b) - int(c)
- pa := abs(p - int(a))
- pb := abs(p - int(b))
- pc := abs(p - int(c))
- if pa <= pb && pa <= pc {
- return a
- } else if pb <= pc {
- return b
- }
- return c
-}
-
// Read presents one or more IDAT chunks as one continuous stream (minus the
// intermediate chunk headers and footers). If the PNG data looked like:
// ... len0 IDAT xxx crc0 len1 IDAT yy crc1 len2 IEND crc2
@@ -300,7 +288,7 @@ func (d *decoder) decode() (image.Image, error) {
}
defer r.Close()
bitsPerPixel := 0
- maxPalette := uint8(0)
+ pixOffset := 0
var (
gray *image.Gray
rgba *image.RGBA
@@ -328,7 +316,6 @@ func (d *decoder) decode() (image.Image, error) {
bitsPerPixel = d.depth
paletted = image.NewPaletted(image.Rect(0, 0, d.width, d.height), d.palette)
img = paletted
- maxPalette = uint8(len(d.palette) - 1)
case cbTCA8:
bitsPerPixel = 32
nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height))
@@ -375,8 +362,8 @@ func (d *decoder) decode() (image.Image, error) {
cdat[i] += cdat[i-bytesPerPixel]
}
case ftUp:
- for i := 0; i < len(cdat); i++ {
- cdat[i] += pdat[i]
+ for i, p := range pdat {
+ cdat[i] += p
}
case ftAverage:
for i := 0; i < bytesPerPixel; i++ {
@@ -386,12 +373,7 @@ func (d *decoder) decode() (image.Image, error) {
cdat[i] += uint8((int(cdat[i-bytesPerPixel]) + int(pdat[i])) / 2)
}
case ftPaeth:
- for i := 0; i < bytesPerPixel; i++ {
- cdat[i] += paeth(0, pdat[i], 0)
- }
- for i := bytesPerPixel; i < len(cdat); i++ {
- cdat[i] += paeth(cdat[i-bytesPerPixel], pdat[i], pdat[i-bytesPerPixel])
- }
+ filterPaeth(cdat, pdat, bytesPerPixel)
default:
return nil, FormatError("bad filter type")
}
@@ -423,25 +405,31 @@ func (d *decoder) decode() (image.Image, error) {
}
}
case cbG8:
- for x := 0; x < d.width; x++ {
- gray.SetGray(x, y, color.Gray{cdat[x]})
- }
+ copy(gray.Pix[pixOffset:], cdat)
+ pixOffset += gray.Stride
case cbGA8:
for x := 0; x < d.width; x++ {
ycol := cdat[2*x+0]
nrgba.SetNRGBA(x, y, color.NRGBA{ycol, ycol, ycol, cdat[2*x+1]})
}
case cbTC8:
+ pix, i, j := rgba.Pix, pixOffset, 0
for x := 0; x < d.width; x++ {
- rgba.SetRGBA(x, y, color.RGBA{cdat[3*x+0], cdat[3*x+1], cdat[3*x+2], 0xff})
+ pix[i+0] = cdat[j+0]
+ pix[i+1] = cdat[j+1]
+ pix[i+2] = cdat[j+2]
+ pix[i+3] = 0xff
+ i += 4
+ j += 3
}
+ pixOffset += rgba.Stride
case cbP1:
for x := 0; x < d.width; x += 8 {
b := cdat[x/8]
for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ {
idx := b >> 7
- if idx > maxPalette {
- return nil, FormatError("palette index out of range")
+ if len(paletted.Palette) <= int(idx) {
+ paletted.Palette = paletted.Palette[:int(idx)+1]
}
paletted.SetColorIndex(x+x2, y, idx)
b <<= 1
@@ -452,8 +440,8 @@ func (d *decoder) decode() (image.Image, error) {
b := cdat[x/4]
for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ {
idx := b >> 6
- if idx > maxPalette {
- return nil, FormatError("palette index out of range")
+ if len(paletted.Palette) <= int(idx) {
+ paletted.Palette = paletted.Palette[:int(idx)+1]
}
paletted.SetColorIndex(x+x2, y, idx)
b <<= 2
@@ -464,24 +452,26 @@ func (d *decoder) decode() (image.Image, error) {
b := cdat[x/2]
for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ {
idx := b >> 4
- if idx > maxPalette {
- return nil, FormatError("palette index out of range")
+ if len(paletted.Palette) <= int(idx) {
+ paletted.Palette = paletted.Palette[:int(idx)+1]
}
paletted.SetColorIndex(x+x2, y, idx)
b <<= 4
}
}
case cbP8:
- for x := 0; x < d.width; x++ {
- if cdat[x] > maxPalette {
- return nil, FormatError("palette index out of range")
+ if len(paletted.Palette) != 255 {
+ for x := 0; x < d.width; x++ {
+ if len(paletted.Palette) <= int(cdat[x]) {
+ paletted.Palette = paletted.Palette[:int(cdat[x])+1]
+ }
}
- paletted.SetColorIndex(x, y, cdat[x])
}
+ copy(paletted.Pix[pixOffset:], cdat)
+ pixOffset += paletted.Stride
case cbTCA8:
- for x := 0; x < d.width; x++ {
- nrgba.SetNRGBA(x, y, color.NRGBA{cdat[4*x+0], cdat[4*x+1], cdat[4*x+2], cdat[4*x+3]})
- }
+ copy(nrgba.Pix[pixOffset:], cdat)
+ pixOffset += nrgba.Stride
case cbG16:
for x := 0; x < d.width; x++ {
ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1])
@@ -662,10 +652,11 @@ func DecodeConfig(r io.Reader) (image.Config, error) {
}
return image.Config{}, err
}
- if d.stage == dsSeenIHDR && d.cb != cbP8 {
+ paletted := d.cb == cbP8 || d.cb == cbP4 || d.cb == cbP2 || d.cb == cbP1
+ if d.stage == dsSeenIHDR && !paletted {
break
}
- if d.stage == dsSeenPLTE && d.cb == cbP8 {
+ if d.stage == dsSeenPLTE && paletted {
break
}
}
diff --git a/src/pkg/image/png/reader_test.go b/src/pkg/image/png/reader_test.go
index 24c4ea448..ac0d949a9 100644
--- a/src/pkg/image/png/reader_test.go
+++ b/src/pkg/image/png/reader_test.go
@@ -10,6 +10,7 @@ import (
"image"
"image/color"
"io"
+ "io/ioutil"
"os"
"strings"
"testing"
@@ -37,6 +38,14 @@ var filenames = []string{
"basn6a16",
}
+var filenamesPaletted = []string{
+ "basn3p01",
+ "basn3p02",
+ "basn3p04",
+ "basn3p08",
+ "basn3p08-trns",
+}
+
var filenamesShort = []string{
"basn0g01",
"basn0g04-31",
@@ -106,13 +115,18 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
lastAlpha := -1
io.WriteString(w, "PLTE {\n")
for i, c := range cpm {
- r, g, b, a := c.RGBA()
- if a != 0xffff {
+ var r, g, b, a uint8
+ switch c := c.(type) {
+ case color.RGBA:
+ r, g, b, a = c.R, c.G, c.B, 0xff
+ case color.NRGBA:
+ r, g, b, a = c.R, c.G, c.B, c.A
+ default:
+ panic("unknown palette color type")
+ }
+ if a != 0xff {
lastAlpha = i
}
- r >>= 8
- g >>= 8
- b >>= 8
fmt.Fprintf(w, " (%3d,%3d,%3d) # rgb = (0x%02x,0x%02x,0x%02x)\n", r, g, b, r, g, b)
}
io.WriteString(w, "}\n")
@@ -202,7 +216,7 @@ func TestReader(t *testing.T) {
}
piper, pipew := io.Pipe()
- pb := bufio.NewReader(piper)
+ pb := bufio.NewScanner(piper)
go sng(pipew, fn, img)
defer piper.Close()
@@ -213,7 +227,7 @@ func TestReader(t *testing.T) {
continue
}
defer sf.Close()
- sb := bufio.NewReader(sf)
+ sb := bufio.NewScanner(sf)
if err != nil {
t.Error(fn, err)
continue
@@ -221,24 +235,28 @@ func TestReader(t *testing.T) {
// Compare the two, in SNG format, line by line.
for {
- ps, perr := pb.ReadString('\n')
- ss, serr := sb.ReadString('\n')
- if perr == io.EOF && serr == io.EOF {
+ pdone := pb.Scan()
+ sdone := sb.Scan()
+ if pdone && sdone {
break
}
- if perr != nil {
- t.Error(fn, perr)
- break
- }
- if serr != nil {
- t.Error(fn, serr)
+ if pdone || sdone {
+ t.Errorf("%s: Different sizes", fn)
break
}
+ ps := pb.Text()
+ ss := sb.Text()
if ps != ss {
t.Errorf("%s: Mismatch\n%sversus\n%s\n", fn, ps, ss)
break
}
}
+ if pb.Err() != nil {
+ t.Error(fn, pb.Err())
+ }
+ if sb.Err() != nil {
+ t.Error(fn, sb.Err())
+ }
}
}
@@ -267,3 +285,66 @@ func TestReaderError(t *testing.T) {
}
}
}
+
+func TestPalettedDecodeConfig(t *testing.T) {
+ for _, fn := range filenamesPaletted {
+ f, err := os.Open("testdata/pngsuite/" + fn + ".png")
+ if err != nil {
+ t.Errorf("%s: open failed: %v", fn, err)
+ continue
+ }
+ defer f.Close()
+ cfg, err := DecodeConfig(f)
+ if err != nil {
+ t.Errorf("%s: %v", fn, err)
+ continue
+ }
+ pal, ok := cfg.ColorModel.(color.Palette)
+ if !ok {
+ t.Errorf("%s: expected paletted color model", fn)
+ continue
+ }
+ if pal == nil {
+ t.Errorf("%s: palette not initialized", fn)
+ continue
+ }
+ }
+}
+
+func benchmarkDecode(b *testing.B, filename string, bytesPerPixel int) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ b.Fatal(err)
+ }
+ s := string(data)
+ cfg, err := DecodeConfig(strings.NewReader(s))
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.SetBytes(int64(cfg.Width * cfg.Height * bytesPerPixel))
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Decode(strings.NewReader(s))
+ }
+}
+
+func BenchmarkDecodeGray(b *testing.B) {
+ benchmarkDecode(b, "testdata/benchGray.png", 1)
+}
+
+func BenchmarkDecodeNRGBAGradient(b *testing.B) {
+ benchmarkDecode(b, "testdata/benchNRGBA-gradient.png", 4)
+}
+
+func BenchmarkDecodeNRGBAOpaque(b *testing.B) {
+ benchmarkDecode(b, "testdata/benchNRGBA-opaque.png", 4)
+}
+
+func BenchmarkDecodePaletted(b *testing.B) {
+ benchmarkDecode(b, "testdata/benchPaletted.png", 1)
+}
+
+func BenchmarkDecodeRGB(b *testing.B) {
+ benchmarkDecode(b, "testdata/benchRGB.png", 4)
+}
diff --git a/src/pkg/image/png/testdata/benchGray.png b/src/pkg/image/png/testdata/benchGray.png
new file mode 100644
index 000000000..42bc6c3a0
--- /dev/null
+++ b/src/pkg/image/png/testdata/benchGray.png
Binary files differ
diff --git a/src/pkg/image/png/testdata/benchNRGBA-gradient.png b/src/pkg/image/png/testdata/benchNRGBA-gradient.png
new file mode 100644
index 000000000..961934cca
--- /dev/null
+++ b/src/pkg/image/png/testdata/benchNRGBA-gradient.png
Binary files differ
diff --git a/src/pkg/image/png/testdata/benchNRGBA-opaque.png b/src/pkg/image/png/testdata/benchNRGBA-opaque.png
new file mode 100644
index 000000000..ca4f4a037
--- /dev/null
+++ b/src/pkg/image/png/testdata/benchNRGBA-opaque.png
Binary files differ
diff --git a/src/pkg/image/png/testdata/benchPaletted.png b/src/pkg/image/png/testdata/benchPaletted.png
new file mode 100644
index 000000000..4b4d5b992
--- /dev/null
+++ b/src/pkg/image/png/testdata/benchPaletted.png
Binary files differ
diff --git a/src/pkg/image/png/testdata/benchRGB.png b/src/pkg/image/png/testdata/benchRGB.png
new file mode 100644
index 000000000..31ac65a3f
--- /dev/null
+++ b/src/pkg/image/png/testdata/benchRGB.png
Binary files differ
diff --git a/src/pkg/image/png/writer.go b/src/pkg/image/png/writer.go
index 57c03792b..093d47193 100644
--- a/src/pkg/image/png/writer.go
+++ b/src/pkg/image/png/writer.go
@@ -21,7 +21,7 @@ type encoder struct {
err error
header [8]byte
footer [4]byte
- tmp [3 * 256]byte
+ tmp [4 * 256]byte
}
// Big-endian.
@@ -70,7 +70,7 @@ func (e *encoder) writeChunk(b []byte, name string) {
e.err = UnsupportedError(name + " chunk is too large: " + strconv.Itoa(len(b)))
return
}
- writeUint32(e.header[0:4], n)
+ writeUint32(e.header[:4], n)
e.header[4] = name[0]
e.header[5] = name[1]
e.header[6] = name[2]
@@ -78,9 +78,9 @@ func (e *encoder) writeChunk(b []byte, name string) {
crc := crc32.NewIEEE()
crc.Write(e.header[4:8])
crc.Write(b)
- writeUint32(e.footer[0:4], crc.Sum32())
+ writeUint32(e.footer[:4], crc.Sum32())
- _, e.err = e.w.Write(e.header[0:8])
+ _, e.err = e.w.Write(e.header[:8])
if e.err != nil {
return
}
@@ -88,7 +88,7 @@ func (e *encoder) writeChunk(b []byte, name string) {
if e.err != nil {
return
}
- _, e.err = e.w.Write(e.footer[0:4])
+ _, e.err = e.w.Write(e.footer[:4])
}
func (e *encoder) writeIHDR() {
@@ -122,36 +122,29 @@ func (e *encoder) writeIHDR() {
e.tmp[10] = 0 // default compression method
e.tmp[11] = 0 // default filter method
e.tmp[12] = 0 // non-interlaced
- e.writeChunk(e.tmp[0:13], "IHDR")
+ e.writeChunk(e.tmp[:13], "IHDR")
}
-func (e *encoder) writePLTE(p color.Palette) {
+func (e *encoder) writePLTEAndTRNS(p color.Palette) {
if len(p) < 1 || len(p) > 256 {
e.err = FormatError("bad palette length: " + strconv.Itoa(len(p)))
return
}
- for i, c := range p {
- r, g, b, _ := c.RGBA()
- e.tmp[3*i+0] = uint8(r >> 8)
- e.tmp[3*i+1] = uint8(g >> 8)
- e.tmp[3*i+2] = uint8(b >> 8)
- }
- e.writeChunk(e.tmp[0:3*len(p)], "PLTE")
-}
-
-func (e *encoder) maybeWritetRNS(p color.Palette) {
last := -1
for i, c := range p {
- _, _, _, a := c.RGBA()
- if a != 0xffff {
+ c1 := color.NRGBAModel.Convert(c).(color.NRGBA)
+ e.tmp[3*i+0] = c1.R
+ e.tmp[3*i+1] = c1.G
+ e.tmp[3*i+2] = c1.B
+ if c1.A != 0xff {
last = i
}
- e.tmp[i] = uint8(a >> 8)
+ e.tmp[3*256+i] = c1.A
}
- if last == -1 {
- return
+ e.writeChunk(e.tmp[:3*len(p)], "PLTE")
+ if last != -1 {
+ e.writeChunk(e.tmp[3*256:3*256+1+last], "tRNS")
}
- e.writeChunk(e.tmp[:last+1], "tRNS")
}
// An encoder is an io.Writer that satisfies writes by writing PNG IDAT chunks,
@@ -297,26 +290,42 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
}
pr := make([]uint8, 1+bpp*b.Dx())
+ gray, _ := m.(*image.Gray)
+ rgba, _ := m.(*image.RGBA)
+ paletted, _ := m.(*image.Paletted)
+ nrgba, _ := m.(*image.NRGBA)
+
for y := b.Min.Y; y < b.Max.Y; y++ {
// Convert from colors to bytes.
i := 1
switch cb {
case cbG8:
- for x := b.Min.X; x < b.Max.X; x++ {
- c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
- cr[0][i] = c.Y
- i++
+ if gray != nil {
+ offset := (y - b.Min.Y) * gray.Stride
+ copy(cr[0][1:], gray.Pix[offset:offset+b.Dx()])
+ } else {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
+ cr[0][i] = c.Y
+ i++
+ }
}
case cbTC8:
// We have previously verified that the alpha value is fully opaque.
cr0 := cr[0]
- if rgba, _ := m.(*image.RGBA); rgba != nil {
- j0 := (y - b.Min.Y) * rgba.Stride
+ stride, pix := 0, []byte(nil)
+ if rgba != nil {
+ stride, pix = rgba.Stride, rgba.Pix
+ } else if nrgba != nil {
+ stride, pix = nrgba.Stride, nrgba.Pix
+ }
+ if stride != 0 {
+ j0 := (y - b.Min.Y) * stride
j1 := j0 + b.Dx()*4
for j := j0; j < j1; j += 4 {
- cr0[i+0] = rgba.Pix[j+0]
- cr0[i+1] = rgba.Pix[j+1]
- cr0[i+2] = rgba.Pix[j+2]
+ cr0[i+0] = pix[j+0]
+ cr0[i+1] = pix[j+1]
+ cr0[i+2] = pix[j+2]
i += 3
}
} else {
@@ -329,9 +338,9 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
}
}
case cbP8:
- if p, _ := m.(*image.Paletted); p != nil {
- offset := (y - b.Min.Y) * p.Stride
- copy(cr[0][1:], p.Pix[offset:offset+b.Dx()])
+ if paletted != nil {
+ offset := (y - b.Min.Y) * paletted.Stride
+ copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
} else {
pi := m.(image.PalettedImage)
for x := b.Min.X; x < b.Max.X; x++ {
@@ -340,14 +349,19 @@ func writeImage(w io.Writer, m image.Image, cb int) error {
}
}
case cbTCA8:
- // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
- for x := b.Min.X; x < b.Max.X; x++ {
- c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
- cr[0][i+0] = c.R
- cr[0][i+1] = c.G
- cr[0][i+2] = c.B
- cr[0][i+3] = c.A
- i += 4
+ if nrgba != nil {
+ offset := (y - b.Min.Y) * nrgba.Stride
+ copy(cr[0][1:], nrgba.Pix[offset:offset+b.Dx()*4])
+ } else {
+ // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
+ for x := b.Min.X; x < b.Max.X; x++ {
+ c := color.NRGBAModel.Convert(m.At(x, y)).(color.NRGBA)
+ cr[0][i+0] = c.R
+ cr[0][i+1] = c.G
+ cr[0][i+2] = c.B
+ cr[0][i+3] = c.A
+ i += 4
+ }
}
case cbG16:
for x := b.Min.X; x < b.Max.X; x++ {
@@ -412,7 +426,7 @@ func (e *encoder) writeIDATs() {
e.err = bw.Flush()
}
-func (e *encoder) writeIEND() { e.writeChunk(e.tmp[0:0], "IEND") }
+func (e *encoder) writeIEND() { e.writeChunk(nil, "IEND") }
// Encode writes the Image m to w in PNG format. Any Image may be encoded, but
// images that are not image.NRGBA might be encoded lossily.
@@ -460,8 +474,7 @@ func Encode(w io.Writer, m image.Image) error {
_, e.err = io.WriteString(w, pngHeader)
e.writeIHDR()
if pal != nil {
- e.writePLTE(pal)
- e.maybeWritetRNS(pal)
+ e.writePLTEAndTRNS(pal)
}
e.writeIDATs()
e.writeIEND()
diff --git a/src/pkg/image/png/writer_test.go b/src/pkg/image/png/writer_test.go
index 644c4fb44..3116fc9ff 100644
--- a/src/pkg/image/png/writer_test.go
+++ b/src/pkg/image/png/writer_test.go
@@ -101,6 +101,49 @@ func TestSubImage(t *testing.T) {
}
}
+func BenchmarkEncodeGray(b *testing.B) {
+ b.StopTimer()
+ img := image.NewGray(image.Rect(0, 0, 640, 480))
+ b.SetBytes(640 * 480 * 1)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Encode(ioutil.Discard, img)
+ }
+}
+
+func BenchmarkEncodeNRGBOpaque(b *testing.B) {
+ b.StopTimer()
+ img := image.NewNRGBA(image.Rect(0, 0, 640, 480))
+ // Set all pixels to 0xFF alpha to force opaque mode.
+ bo := img.Bounds()
+ for y := bo.Min.Y; y < bo.Max.Y; y++ {
+ for x := bo.Min.X; x < bo.Max.X; x++ {
+ img.Set(x, y, color.NRGBA{0, 0, 0, 255})
+ }
+ }
+ if !img.Opaque() {
+ b.Fatal("expected image to be opaque")
+ }
+ b.SetBytes(640 * 480 * 4)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Encode(ioutil.Discard, img)
+ }
+}
+
+func BenchmarkEncodeNRGBA(b *testing.B) {
+ b.StopTimer()
+ img := image.NewNRGBA(image.Rect(0, 0, 640, 480))
+ if img.Opaque() {
+ b.Fatal("expected image not to be opaque")
+ }
+ b.SetBytes(640 * 480 * 4)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Encode(ioutil.Discard, img)
+ }
+}
+
func BenchmarkEncodePaletted(b *testing.B) {
b.StopTimer()
img := image.NewPaletted(image.Rect(0, 0, 640, 480), color.Palette{
@@ -138,7 +181,7 @@ func BenchmarkEncodeRGBA(b *testing.B) {
b.StopTimer()
img := image.NewRGBA(image.Rect(0, 0, 640, 480))
if img.Opaque() {
- b.Fatal("expected image to not be opaque")
+ b.Fatal("expected image not to be opaque")
}
b.SetBytes(640 * 480 * 4)
b.StartTimer()
diff --git a/src/pkg/image/testdata/video-001.progressive.jpeg b/src/pkg/image/testdata/video-001.progressive.jpeg
new file mode 100644
index 000000000..b8cae2359
--- /dev/null
+++ b/src/pkg/image/testdata/video-001.progressive.jpeg
Binary files differ
diff --git a/src/pkg/image/testdata/video-001.q50.420.jpeg b/src/pkg/image/testdata/video-001.q50.420.jpeg
new file mode 100644
index 000000000..83fb0f8ab
--- /dev/null
+++ b/src/pkg/image/testdata/video-001.q50.420.jpeg
Binary files differ
diff --git a/src/pkg/image/testdata/video-001.q50.420.progressive.jpeg b/src/pkg/image/testdata/video-001.q50.420.progressive.jpeg
new file mode 100644
index 000000000..b048eb205
--- /dev/null
+++ b/src/pkg/image/testdata/video-001.q50.420.progressive.jpeg
Binary files differ
diff --git a/src/pkg/image/testdata/video-001.q50.422.jpeg b/src/pkg/image/testdata/video-001.q50.422.jpeg
new file mode 100644
index 000000000..60fff4ff9
--- /dev/null
+++ b/src/pkg/image/testdata/video-001.q50.422.jpeg
Binary files differ
diff --git a/src/pkg/image/testdata/video-001.q50.422.progressive.jpeg b/src/pkg/image/testdata/video-001.q50.422.progressive.jpeg
new file mode 100644
index 000000000..926d005de
--- /dev/null
+++ b/src/pkg/image/testdata/video-001.q50.422.progressive.jpeg
Binary files differ
diff --git a/src/pkg/image/testdata/video-001.q50.440.jpeg b/src/pkg/image/testdata/video-001.q50.440.jpeg
new file mode 100644
index 000000000..32eeeaef6
--- /dev/null
+++ b/src/pkg/image/testdata/video-001.q50.440.jpeg
Binary files differ
diff --git a/src/pkg/image/testdata/video-001.q50.440.progressive.jpeg b/src/pkg/image/testdata/video-001.q50.440.progressive.jpeg
new file mode 100644
index 000000000..e641a3bbb
--- /dev/null
+++ b/src/pkg/image/testdata/video-001.q50.440.progressive.jpeg
Binary files differ
diff --git a/src/pkg/image/testdata/video-001.q50.444.jpeg b/src/pkg/image/testdata/video-001.q50.444.jpeg
new file mode 100644
index 000000000..7d5743382
--- /dev/null
+++ b/src/pkg/image/testdata/video-001.q50.444.jpeg
Binary files differ
diff --git a/src/pkg/image/testdata/video-001.q50.444.progressive.jpeg b/src/pkg/image/testdata/video-001.q50.444.progressive.jpeg
new file mode 100644
index 000000000..ff7d5f9ff
--- /dev/null
+++ b/src/pkg/image/testdata/video-001.q50.444.progressive.jpeg
Binary files differ
diff --git a/src/pkg/image/testdata/video-005.gray.q50.2x2.jpeg b/src/pkg/image/testdata/video-005.gray.q50.2x2.jpeg
new file mode 100644
index 000000000..630b615f7
--- /dev/null
+++ b/src/pkg/image/testdata/video-005.gray.q50.2x2.jpeg
Binary files differ
diff --git a/src/pkg/image/testdata/video-005.gray.q50.2x2.progressive.jpeg b/src/pkg/image/testdata/video-005.gray.q50.2x2.progressive.jpeg
new file mode 100644
index 000000000..c6b93608c
--- /dev/null
+++ b/src/pkg/image/testdata/video-005.gray.q50.2x2.progressive.jpeg
Binary files differ
diff --git a/src/pkg/image/testdata/video-005.gray.q50.jpeg b/src/pkg/image/testdata/video-005.gray.q50.jpeg
new file mode 100644
index 000000000..c65b5a794
--- /dev/null
+++ b/src/pkg/image/testdata/video-005.gray.q50.jpeg
Binary files differ
diff --git a/src/pkg/image/testdata/video-005.gray.q50.progressive.jpeg b/src/pkg/image/testdata/video-005.gray.q50.progressive.jpeg
new file mode 100644
index 000000000..24b70e8bf
--- /dev/null
+++ b/src/pkg/image/testdata/video-005.gray.q50.progressive.jpeg
Binary files differ
diff --git a/src/pkg/image/ycbcr.go b/src/pkg/image/ycbcr.go
index c1a0b666f..5b73bef78 100644
--- a/src/pkg/image/ycbcr.go
+++ b/src/pkg/image/ycbcr.go
@@ -15,6 +15,7 @@ const (
YCbCrSubsampleRatio444 YCbCrSubsampleRatio = iota
YCbCrSubsampleRatio422
YCbCrSubsampleRatio420
+ YCbCrSubsampleRatio440
)
func (s YCbCrSubsampleRatio) String() string {
@@ -25,6 +26,8 @@ func (s YCbCrSubsampleRatio) String() string {
return "YCbCrSubsampleRatio422"
case YCbCrSubsampleRatio420:
return "YCbCrSubsampleRatio420"
+ case YCbCrSubsampleRatio440:
+ return "YCbCrSubsampleRatio440"
}
return "YCbCrSubsampleRatioUnknown"
}
@@ -39,6 +42,7 @@ func (s YCbCrSubsampleRatio) String() string {
// For 4:4:4, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/1.
// For 4:2:2, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/2.
// For 4:2:0, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/4.
+// For 4:4:0, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/2.
type YCbCr struct {
Y, Cb, Cr []uint8
YStride int
@@ -82,6 +86,8 @@ func (p *YCbCr) COffset(x, y int) int {
return (y-p.Rect.Min.Y)*p.CStride + (x/2 - p.Rect.Min.X/2)
case YCbCrSubsampleRatio420:
return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/2 - p.Rect.Min.X/2)
+ case YCbCrSubsampleRatio440:
+ return (y/2-p.Rect.Min.Y/2)*p.CStride + (x - p.Rect.Min.X)
}
// Default to 4:4:4 subsampling.
return (y-p.Rect.Min.Y)*p.CStride + (x - p.Rect.Min.X)
@@ -126,6 +132,9 @@ func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr {
case YCbCrSubsampleRatio420:
cw = (r.Max.X+1)/2 - r.Min.X/2
ch = (r.Max.Y+1)/2 - r.Min.Y/2
+ case YCbCrSubsampleRatio440:
+ cw = w
+ ch = (r.Max.Y+1)/2 - r.Min.Y/2
default:
// Default to 4:4:4 subsampling.
cw = w
diff --git a/src/pkg/image/ycbcr_test.go b/src/pkg/image/ycbcr_test.go
index 5fa95be3e..a5f448265 100644
--- a/src/pkg/image/ycbcr_test.go
+++ b/src/pkg/image/ycbcr_test.go
@@ -36,6 +36,7 @@ func TestYCbCr(t *testing.T) {
YCbCrSubsampleRatio444,
YCbCrSubsampleRatio422,
YCbCrSubsampleRatio420,
+ YCbCrSubsampleRatio440,
}
deltas := []Point{
Pt(0, 0),
diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go
index 54bf159eb..23d05e575 100644
--- a/src/pkg/io/io.go
+++ b/src/pkg/io/io.go
@@ -130,11 +130,23 @@ type ReadWriteSeeker interface {
}
// ReaderFrom is the interface that wraps the ReadFrom method.
+//
+// ReadFrom reads data from r until EOF or error.
+// The return value n is the number of bytes read.
+// Any error except io.EOF encountered during the read is also returned.
+//
+// The Copy function uses ReaderFrom if available.
type ReaderFrom interface {
ReadFrom(r Reader) (n int64, err error)
}
// WriterTo is the interface that wraps the WriteTo method.
+//
+// WriteTo writes data to w until there's no more data to write or
+// when an error occurs. The return value n is the number of bytes
+// written. Any error encountered during the write is also returned.
+//
+// The Copy function uses WriterTo if available.
type WriterTo interface {
WriteTo(w Writer) (n int64, err error)
}
@@ -204,6 +216,11 @@ type ByteScanner interface {
UnreadByte() error
}
+// ByteWriter is the interface that wraps the WriteByte method.
+type ByteWriter interface {
+ WriteByte(c byte) error
+}
+
// RuneReader is the interface that wraps the ReadRune method.
//
// ReadRune reads a single UTF-8 encoded Unicode character
@@ -245,6 +262,7 @@ func WriteString(w Writer, s string) (n int, err error) {
// If an EOF happens after reading fewer than min bytes,
// ReadAtLeast returns ErrUnexpectedEOF.
// If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer.
+// On return, n >= min if and only if err == nil.
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {
if len(buf) < min {
return 0, ErrShortBuffer
@@ -254,12 +272,10 @@ func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {
nn, err = r.Read(buf[n:])
n += nn
}
- if err == EOF {
- if n >= min {
- err = nil
- } else if n > 0 {
- err = ErrUnexpectedEOF
- }
+ if n >= min {
+ err = nil
+ } else if n > 0 && err == EOF {
+ err = ErrUnexpectedEOF
}
return
}
@@ -269,56 +285,28 @@ func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {
// The error is EOF only if no bytes were read.
// If an EOF happens after reading some but not all the bytes,
// ReadFull returns ErrUnexpectedEOF.
+// On return, n == len(buf) if and only if err == nil.
func ReadFull(r Reader, buf []byte) (n int, err error) {
return ReadAtLeast(r, buf, len(buf))
}
// CopyN copies n bytes (or until an error) from src to dst.
// It returns the number of bytes copied and the earliest
-// error encountered while copying. Because Read can
-// return the full amount requested as well as an error
-// (including EOF), so can CopyN.
+// error encountered while copying.
+// On return, written == n if and only if err == nil.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented using it.
func CopyN(dst Writer, src Reader, n int64) (written int64, err error) {
- // If the writer has a ReadFrom method, use it to do the copy.
- // Avoids a buffer allocation and a copy.
- if rt, ok := dst.(ReaderFrom); ok {
- written, err = rt.ReadFrom(LimitReader(src, n))
- if written < n && err == nil {
- // rt stopped early; must have been EOF.
- err = EOF
- }
- return
+ written, err = Copy(dst, LimitReader(src, n))
+ if written == n {
+ return n, nil
}
- buf := make([]byte, 32*1024)
- for written < n {
- l := len(buf)
- if d := n - written; d < int64(l) {
- l = int(d)
- }
- nr, er := src.Read(buf[0:l])
- if nr > 0 {
- nw, ew := dst.Write(buf[0:nr])
- if nw > 0 {
- written += int64(nw)
- }
- if ew != nil {
- err = ew
- break
- }
- if nr != nw {
- err = ErrShortWrite
- break
- }
- }
- if er != nil {
- err = er
- break
- }
+ if written < n && err == nil {
+ // src stopped early; must have been EOF.
+ err = EOF
}
- return written, err
+ return
}
// Copy copies from src to dst until either EOF is reached
@@ -451,6 +439,11 @@ func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) {
off += s.base
if max := s.limit - off; int64(len(p)) > max {
p = p[0:max]
+ n, err = s.r.ReadAt(p, off)
+ if err == nil {
+ err = EOF
+ }
+ return n, err
}
return s.r.ReadAt(p, off)
}
diff --git a/src/pkg/io/io_test.go b/src/pkg/io/io_test.go
index 1e671b59b..1bc451e44 100644
--- a/src/pkg/io/io_test.go
+++ b/src/pkg/io/io_test.go
@@ -6,6 +6,8 @@ package io_test
import (
"bytes"
+ "errors"
+ "fmt"
. "io"
"strings"
"testing"
@@ -88,6 +90,12 @@ func (w *noReadFrom) Write(p []byte) (n int, err error) {
return w.w.Write(p)
}
+type wantedAndErrReader struct{}
+
+func (wantedAndErrReader) Read(p []byte) (int, error) {
+ return len(p), errors.New("wantedAndErrReader error")
+}
+
func TestCopyNEOF(t *testing.T) {
// Test that EOF behavior is the same regardless of whether
// argument to CopyN has ReadFrom.
@@ -113,6 +121,16 @@ func TestCopyNEOF(t *testing.T) {
if n != 3 || err != EOF {
t.Errorf("CopyN(bytes.Buffer, foo, 4) = %d, %v; want 3, EOF", n, err)
}
+
+ n, err = CopyN(b, wantedAndErrReader{}, 5)
+ if n != 5 || err != nil {
+ t.Errorf("CopyN(bytes.Buffer, wantedAndErrReader, 5) = %d, %v; want 5, nil", n, err)
+ }
+
+ n, err = CopyN(&noReadFrom{b}, wantedAndErrReader{}, 5)
+ if n != 5 || err != nil {
+ t.Errorf("CopyN(noReadFrom, wantedAndErrReader, 5) = %d, %v; want 5, nil", n, err)
+ }
}
func TestReadAtLeast(t *testing.T) {
@@ -120,22 +138,30 @@ func TestReadAtLeast(t *testing.T) {
testReadAtLeast(t, &rb)
}
-// A version of bytes.Buffer that returns n > 0, EOF on Read
+// A version of bytes.Buffer that returns n > 0, err on Read
// when the input is exhausted.
-type dataAndEOFBuffer struct {
+type dataAndErrorBuffer struct {
+ err error
bytes.Buffer
}
-func (r *dataAndEOFBuffer) Read(p []byte) (n int, err error) {
+func (r *dataAndErrorBuffer) Read(p []byte) (n int, err error) {
n, err = r.Buffer.Read(p)
if n > 0 && r.Buffer.Len() == 0 && err == nil {
- err = EOF
+ err = r.err
}
return
}
func TestReadAtLeastWithDataAndEOF(t *testing.T) {
- var rb dataAndEOFBuffer
+ var rb dataAndErrorBuffer
+ rb.err = EOF
+ testReadAtLeast(t, &rb)
+}
+
+func TestReadAtLeastWithDataAndError(t *testing.T) {
+ var rb dataAndErrorBuffer
+ rb.err = fmt.Errorf("fake error")
testReadAtLeast(t, &rb)
}
@@ -169,8 +195,12 @@ func testReadAtLeast(t *testing.T, rb ReadWriter) {
}
rb.Write([]byte("4"))
n, err = ReadAtLeast(rb, buf, 2)
- if err != ErrUnexpectedEOF {
- t.Errorf("expected ErrUnexpectedEOF, got %v", err)
+ want := ErrUnexpectedEOF
+ if rb, ok := rb.(*dataAndErrorBuffer); ok && rb.err != EOF {
+ want = rb.err
+ }
+ if err != want {
+ t.Errorf("expected %v, got %v", want, err)
}
if n != 1 {
t.Errorf("expected to have read 1 bytes, got %v", n)
@@ -203,3 +233,35 @@ func TestTeeReader(t *testing.T) {
t.Errorf("closed tee: ReadFull(r, dst) = %d, %v; want 0, EPIPE", n, err)
}
}
+
+func TestSectionReader_ReadAt(tst *testing.T) {
+ dat := "a long sample data, 1234567890"
+ tests := []struct {
+ data string
+ off int
+ n int
+ bufLen int
+ at int
+ exp string
+ err error
+ }{
+ {data: "", off: 0, n: 10, bufLen: 2, at: 0, exp: "", err: EOF},
+ {data: dat, off: 0, n: len(dat), bufLen: 0, at: 0, exp: "", err: nil},
+ {data: dat, off: len(dat), n: 1, bufLen: 1, at: 0, exp: "", err: EOF},
+ {data: dat, off: 0, n: len(dat) + 2, bufLen: len(dat), at: 0, exp: dat, err: nil},
+ {data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 0, exp: dat[:len(dat)/2], err: nil},
+ {data: dat, off: 0, n: len(dat), bufLen: len(dat), at: 0, exp: dat, err: nil},
+ {data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[2 : 2+len(dat)/2], err: nil},
+ {data: dat, off: 3, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[5 : 5+len(dat)/2], err: nil},
+ {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 - 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: nil},
+ {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 + 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: EOF},
+ }
+ for i, t := range tests {
+ r := strings.NewReader(t.data)
+ s := NewSectionReader(r, int64(t.off), int64(t.n))
+ buf := make([]byte, t.bufLen)
+ if n, err := s.ReadAt(buf, int64(t.at)); n != len(t.exp) || string(buf[:n]) != t.exp || err != t.err {
+ tst.Fatalf("%d: ReadAt(%d) = %q, %v; expected %q, %v", i, t.at, buf[:n], err, t.exp, t.err)
+ }
+ }
+}
diff --git a/src/pkg/io/ioutil/blackhole.go b/src/pkg/io/ioutil/blackhole.go
new file mode 100644
index 000000000..101d2c121
--- /dev/null
+++ b/src/pkg/io/ioutil/blackhole.go
@@ -0,0 +1,23 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ioutil
+
+var blackHoleBuf = make(chan []byte, 1)
+
+func blackHole() []byte {
+ select {
+ case b := <-blackHoleBuf:
+ return b
+ default:
+ }
+ return make([]byte, 8192)
+}
+
+func blackHolePut(p []byte) {
+ select {
+ case blackHoleBuf <- p:
+ default:
+ }
+}
diff --git a/src/pkg/io/ioutil/ioutil.go b/src/pkg/io/ioutil/ioutil.go
index f072b8c75..0eb146c0a 100644
--- a/src/pkg/io/ioutil/ioutil.go
+++ b/src/pkg/io/ioutil/ioutil.go
@@ -130,12 +130,12 @@ func (devNull) Write(p []byte) (int, error) {
return len(p), nil
}
-var blackHole = make([]byte, 8192)
-
func (devNull) ReadFrom(r io.Reader) (n int64, err error) {
+ buf := blackHole()
+ defer blackHolePut(buf)
readSize := 0
for {
- readSize, err = r.Read(blackHole)
+ readSize, err = r.Read(buf)
n += int64(readSize)
if err != nil {
if err == io.EOF {
diff --git a/src/pkg/io/ioutil/ioutil_test.go b/src/pkg/io/ioutil/ioutil_test.go
index d9c43bead..c297847b4 100644
--- a/src/pkg/io/ioutil/ioutil_test.go
+++ b/src/pkg/io/ioutil/ioutil_test.go
@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package ioutil_test
+package ioutil
import (
- . "io/ioutil"
"os"
"testing"
)
diff --git a/src/pkg/io/ioutil/tempfile.go b/src/pkg/io/ioutil/tempfile.go
index 42d2e6758..4a06e9756 100644
--- a/src/pkg/io/ioutil/tempfile.go
+++ b/src/pkg/io/ioutil/tempfile.go
@@ -8,26 +8,30 @@ import (
"os"
"path/filepath"
"strconv"
+ "sync"
"time"
)
-// Random number state, accessed without lock; racy but harmless.
+// Random number state.
// We generate random temporary file names so that there's a good
// chance the file doesn't exist yet - keeps the number of tries in
// TempFile to a minimum.
var rand uint32
+var randmu sync.Mutex
func reseed() uint32 {
return uint32(time.Now().UnixNano() + int64(os.Getpid()))
}
func nextSuffix() string {
+ randmu.Lock()
r := rand
if r == 0 {
r = reseed()
}
r = r*1664525 + 1013904223 // constants from Numerical Recipes
rand = r
+ randmu.Unlock()
return strconv.Itoa(int(1e9 + r%1e9))[1:]
}
@@ -38,8 +42,8 @@ func nextSuffix() string {
// for temporary files (see os.TempDir).
// Multiple programs calling TempFile simultaneously
// will not choose the same file. The caller can use f.Name()
-// to find the name of the file. It is the caller's responsibility to
-// remove the file when no longer needed.
+// to find the pathname of the file. It is the caller's responsibility
+// to remove the file when no longer needed.
func TempFile(dir, prefix string) (f *os.File, err error) {
if dir == "" {
dir = os.TempDir()
diff --git a/src/pkg/io/ioutil/tempfile_test.go b/src/pkg/io/ioutil/tempfile_test.go
index 80c62f672..d2a132a11 100644
--- a/src/pkg/io/ioutil/tempfile_test.go
+++ b/src/pkg/io/ioutil/tempfile_test.go
@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package ioutil_test
+package ioutil
import (
- . "io/ioutil"
"os"
"path/filepath"
"regexp"
diff --git a/src/pkg/log/syslog/syslog.go b/src/pkg/log/syslog/syslog.go
index f53310cb0..8bdd9825e 100644
--- a/src/pkg/log/syslog/syslog.go
+++ b/src/pkg/log/syslog/syslog.go
@@ -4,9 +4,13 @@
// +build !windows,!plan9
-// Package syslog provides a simple interface to the system log service. It
-// can send messages to the syslog daemon using UNIX domain sockets, UDP, or
-// TCP connections.
+// Package syslog provides a simple interface to the system log
+// service. It can send messages to the syslog daemon using UNIX
+// domain sockets, UDP or TCP.
+//
+// Only one call to Dial is necessary. On write failures,
+// the syslog client will attempt to reconnect to the server
+// and write again.
package syslog
import (
@@ -15,11 +19,23 @@ import (
"log"
"net"
"os"
+ "strings"
+ "sync"
+ "time"
)
+// The Priority is a combination of the syslog facility and
+// severity. For example, LOG_ALERT | LOG_FTP sends an alert severity
+// message from the FTP facility. The default severity is LOG_EMERG;
+// the default facility is LOG_KERN.
type Priority int
+const severityMask = 0x07
+const facilityMask = 0xf8
+
const (
+ // Severity.
+
// From /usr/include/sys/syslog.h.
// These are the same on Linux, BSD, and OS X.
LOG_EMERG Priority = iota
@@ -32,129 +48,220 @@ const (
LOG_DEBUG
)
+const (
+ // Facility.
+
+ // From /usr/include/sys/syslog.h.
+ // These are the same up to LOG_FTP on Linux, BSD, and OS X.
+ LOG_KERN Priority = iota << 3
+ LOG_USER
+ LOG_MAIL
+ LOG_DAEMON
+ LOG_AUTH
+ LOG_SYSLOG
+ LOG_LPR
+ LOG_NEWS
+ LOG_UUCP
+ LOG_CRON
+ LOG_AUTHPRIV
+ LOG_FTP
+ _ // unused
+ _ // unused
+ _ // unused
+ _ // unused
+ LOG_LOCAL0
+ LOG_LOCAL1
+ LOG_LOCAL2
+ LOG_LOCAL3
+ LOG_LOCAL4
+ LOG_LOCAL5
+ LOG_LOCAL6
+ LOG_LOCAL7
+)
+
// A Writer is a connection to a syslog server.
type Writer struct {
priority Priority
- prefix string
- conn serverConn
-}
+ tag string
+ hostname string
+ network string
+ raddr string
-type serverConn interface {
- writeBytes(p Priority, prefix string, b []byte) (int, error)
- writeString(p Priority, prefix string, s string) (int, error)
- close() error
+ mu sync.Mutex // guards conn
+ conn net.Conn
}
-type netConn struct {
- conn net.Conn
+// New establishes a new connection to the system log daemon. Each
+// write to the returned writer sends a log message with the given
+// priority and prefix.
+func New(priority Priority, tag string) (w *Writer, err error) {
+ return Dial("", "", priority, tag)
}
-// New establishes a new connection to the system log daemon.
-// Each write to the returned writer sends a log message with
-// the given priority and prefix.
-func New(priority Priority, prefix string) (w *Writer, err error) {
- return Dial("", "", priority, prefix)
+// Dial establishes a connection to a log daemon by connecting to
+// address raddr on the network net. Each write to the returned
+// writer sends a log message with the given facility, severity and
+// tag.
+func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) {
+ if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG {
+ return nil, errors.New("log/syslog: invalid priority")
+ }
+
+ if tag == "" {
+ tag = os.Args[0]
+ }
+ hostname, _ := os.Hostname()
+
+ w := &Writer{
+ priority: priority,
+ tag: tag,
+ hostname: hostname,
+ network: network,
+ raddr: raddr,
+ }
+
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ err := w.connect()
+ if err != nil {
+ return nil, err
+ }
+ return w, err
}
-// Dial establishes a connection to a log daemon by connecting
-// to address raddr on the network net.
-// Each write to the returned writer sends a log message with
-// the given priority and prefix.
-func Dial(network, raddr string, priority Priority, prefix string) (w *Writer, err error) {
- if prefix == "" {
- prefix = os.Args[0]
+// connect makes a connection to the syslog server.
+// It must be called with w.mu held.
+func (w *Writer) connect() (err error) {
+ if w.conn != nil {
+ // ignore err from close, it makes sense to continue anyway
+ w.conn.Close()
+ w.conn = nil
}
- var conn serverConn
- if network == "" {
- conn, err = unixSyslog()
+
+ if w.network == "" {
+ w.conn, err = unixSyslog()
+ if w.hostname == "" {
+ w.hostname = "localhost"
+ }
} else {
var c net.Conn
- c, err = net.Dial(network, raddr)
- conn = netConn{c}
+ c, err = net.Dial(w.network, w.raddr)
+ if err == nil {
+ w.conn = c
+ if w.hostname == "" {
+ w.hostname = c.LocalAddr().String()
+ }
+ }
}
- return &Writer{priority, prefix, conn}, err
+ return
}
// Write sends a log message to the syslog daemon.
func (w *Writer) Write(b []byte) (int, error) {
- if w.priority > LOG_DEBUG || w.priority < LOG_EMERG {
- return 0, errors.New("log/syslog: invalid priority")
- }
- return w.conn.writeBytes(w.priority, w.prefix, b)
+ return w.writeAndRetry(w.priority, string(b))
}
-func (w *Writer) writeString(p Priority, s string) (int, error) {
- return w.conn.writeString(p, w.prefix, s)
-}
+// Close closes a connection to the syslog daemon.
+func (w *Writer) Close() error {
+ w.mu.Lock()
+ defer w.mu.Unlock()
-func (w *Writer) Close() error { return w.conn.close() }
+ if w.conn != nil {
+ err := w.conn.Close()
+ w.conn = nil
+ return err
+ }
+ return nil
+}
-// Emerg logs a message using the LOG_EMERG priority.
+// Emerg logs a message with severity LOG_EMERG, ignoring the severity
+// passed to New.
func (w *Writer) Emerg(m string) (err error) {
- _, err = w.writeString(LOG_EMERG, m)
+ _, err = w.writeAndRetry(LOG_EMERG, m)
return err
}
-// Alert logs a message using the LOG_ALERT priority.
+// Alert logs a message with severity LOG_ALERT, ignoring the severity
+// passed to New.
func (w *Writer) Alert(m string) (err error) {
- _, err = w.writeString(LOG_ALERT, m)
+ _, err = w.writeAndRetry(LOG_ALERT, m)
return err
}
-// Crit logs a message using the LOG_CRIT priority.
+// Crit logs a message with severity LOG_CRIT, ignoring the severity
+// passed to New.
func (w *Writer) Crit(m string) (err error) {
- _, err = w.writeString(LOG_CRIT, m)
+ _, err = w.writeAndRetry(LOG_CRIT, m)
return err
}
-// Err logs a message using the LOG_ERR priority.
+// Err logs a message with severity LOG_ERR, ignoring the severity
+// passed to New.
func (w *Writer) Err(m string) (err error) {
- _, err = w.writeString(LOG_ERR, m)
+ _, err = w.writeAndRetry(LOG_ERR, m)
return err
}
-// Warning logs a message using the LOG_WARNING priority.
+// Wanring logs a message with severity LOG_WARNING, ignoring the
+// severity passed to New.
func (w *Writer) Warning(m string) (err error) {
- _, err = w.writeString(LOG_WARNING, m)
+ _, err = w.writeAndRetry(LOG_WARNING, m)
return err
}
-// Notice logs a message using the LOG_NOTICE priority.
+// Notice logs a message with severity LOG_NOTICE, ignoring the
+// severity passed to New.
func (w *Writer) Notice(m string) (err error) {
- _, err = w.writeString(LOG_NOTICE, m)
+ _, err = w.writeAndRetry(LOG_NOTICE, m)
return err
}
-// Info logs a message using the LOG_INFO priority.
+// Info logs a message with severity LOG_INFO, ignoring the severity
+// passed to New.
func (w *Writer) Info(m string) (err error) {
- _, err = w.writeString(LOG_INFO, m)
+ _, err = w.writeAndRetry(LOG_INFO, m)
return err
}
-// Debug logs a message using the LOG_DEBUG priority.
+// Debug logs a message with severity LOG_DEBUG, ignoring the severity
+// passed to New.
func (w *Writer) Debug(m string) (err error) {
- _, err = w.writeString(LOG_DEBUG, m)
+ _, err = w.writeAndRetry(LOG_DEBUG, m)
return err
}
-func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error) {
- _, err := fmt.Fprintf(n.conn, "<%d>%s: %s\n", p, prefix, b)
- if err != nil {
+func (w *Writer) writeAndRetry(p Priority, s string) (int, error) {
+ pr := (w.priority & facilityMask) | (p & severityMask)
+
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ if w.conn != nil {
+ if n, err := w.write(pr, s); err == nil {
+ return n, err
+ }
+ }
+ if err := w.connect(); err != nil {
return 0, err
}
- return len(b), nil
+ return w.write(pr, s)
}
-func (n netConn) writeString(p Priority, prefix string, s string) (int, error) {
- _, err := fmt.Fprintf(n.conn, "<%d>%s: %s\n", p, prefix, s)
- if err != nil {
- return 0, err
+// write generates and writes a syslog formatted string. The
+// format is as follows: <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG
+func (w *Writer) write(p Priority, msg string) (int, error) {
+ // ensure it ends in a \n
+ nl := ""
+ if !strings.HasSuffix(msg, "\n") {
+ nl = "\n"
}
- return len(s), nil
-}
-func (n netConn) close() error {
- return n.conn.Close()
+ timestamp := time.Now().Format(time.RFC3339)
+ fmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s",
+ p, timestamp, w.hostname,
+ w.tag, os.Getpid(), msg, nl)
+ return len(msg), nil
}
// NewLogger creates a log.Logger whose output is written to
diff --git a/src/pkg/log/syslog/syslog_plan9.go b/src/pkg/log/syslog/syslog_plan9.go
new file mode 100644
index 000000000..0c05f6f83
--- /dev/null
+++ b/src/pkg/log/syslog/syslog_plan9.go
@@ -0,0 +1,8 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package syslog provides a simple interface to the system log service.
+package syslog
+
+// BUG(akumar): This package is not implemented on Plan 9 yet.
diff --git a/src/pkg/log/syslog/syslog_test.go b/src/pkg/log/syslog/syslog_test.go
index 0fd623905..ec4525190 100644
--- a/src/pkg/log/syslog/syslog_test.go
+++ b/src/pkg/log/syslog/syslog_test.go
@@ -7,52 +7,181 @@
package syslog
import (
+ "bufio"
+ "fmt"
"io"
+ "io/ioutil"
"log"
"net"
+ "os"
+ "sync"
"testing"
"time"
)
-var serverAddr string
-
-func runSyslog(c net.PacketConn, done chan<- string) {
+func runPktSyslog(c net.PacketConn, done chan<- string) {
var buf [4096]byte
- var rcvd string = ""
+ var rcvd string
+ ct := 0
for {
- n, _, err := c.ReadFrom(buf[0:])
- if err != nil || n == 0 {
+ var n int
+ var err error
+
+ c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ n, _, err = c.ReadFrom(buf[:])
+ rcvd += string(buf[:n])
+ if err != nil {
+ if oe, ok := err.(*net.OpError); ok {
+ if ct < 3 && oe.Temporary() {
+ ct++
+ continue
+ }
+ }
break
}
- rcvd += string(buf[0:n])
}
+ c.Close()
done <- rcvd
}
-func startServer(done chan<- string) {
- c, e := net.ListenPacket("udp", "127.0.0.1:0")
- if e != nil {
- log.Fatalf("net.ListenPacket failed udp :0 %v", e)
+var crashy = false
+
+func runStreamSyslog(l net.Listener, done chan<- string, wg *sync.WaitGroup) {
+ for {
+ var c net.Conn
+ var err error
+ if c, err = l.Accept(); err != nil {
+ return
+ }
+ wg.Add(1)
+ go func(c net.Conn) {
+ defer wg.Done()
+ c.SetReadDeadline(time.Now().Add(5 * time.Second))
+ b := bufio.NewReader(c)
+ for ct := 1; !crashy || ct&7 != 0; ct++ {
+ s, err := b.ReadString('\n')
+ if err != nil {
+ break
+ }
+ done <- s
+ }
+ c.Close()
+ }(c)
}
- serverAddr = c.LocalAddr().String()
- c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
- go runSyslog(c, done)
}
-func skipNetTest(t *testing.T) bool {
- if testing.Short() {
- // Depends on syslog daemon running, and sometimes it's not.
- t.Logf("skipping syslog test during -short")
- return true
+func startServer(n, la string, done chan<- string) (addr string, sock io.Closer, wg *sync.WaitGroup) {
+ if n == "udp" || n == "tcp" {
+ la = "127.0.0.1:0"
+ } else {
+ // unix and unixgram: choose an address if none given
+ if la == "" {
+ // use ioutil.TempFile to get a name that is unique
+ f, err := ioutil.TempFile("", "syslogtest")
+ if err != nil {
+ log.Fatal("TempFile: ", err)
+ }
+ f.Close()
+ la = f.Name()
+ }
+ os.Remove(la)
+ }
+
+ wg = new(sync.WaitGroup)
+ if n == "udp" || n == "unixgram" {
+ l, e := net.ListenPacket(n, la)
+ if e != nil {
+ log.Fatalf("startServer failed: %v", e)
+ }
+ addr = l.LocalAddr().String()
+ sock = l
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ runPktSyslog(l, done)
+ }()
+ } else {
+ l, e := net.Listen(n, la)
+ if e != nil {
+ log.Fatalf("startServer failed: %v", e)
+ }
+ addr = l.Addr().String()
+ sock = l
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ runStreamSyslog(l, done, wg)
+ }()
+ }
+ return
+}
+
+func TestWithSimulated(t *testing.T) {
+ msg := "Test 123"
+ transport := []string{"unix", "unixgram", "udp", "tcp"}
+
+ for _, tr := range transport {
+ done := make(chan string)
+ addr, _, _ := startServer(tr, "", done)
+ if tr == "unix" || tr == "unixgram" {
+ defer os.Remove(addr)
+ }
+ s, err := Dial(tr, addr, LOG_INFO|LOG_USER, "syslog_test")
+ if err != nil {
+ t.Fatalf("Dial() failed: %v", err)
+ }
+ err = s.Info(msg)
+ if err != nil {
+ t.Fatalf("log failed: %v", err)
+ }
+ check(t, msg, <-done)
+ s.Close()
+ }
+}
+
+func TestFlap(t *testing.T) {
+ net := "unix"
+ done := make(chan string)
+ addr, sock, _ := startServer(net, "", done)
+ defer os.Remove(addr)
+ defer sock.Close()
+
+ s, err := Dial(net, addr, LOG_INFO|LOG_USER, "syslog_test")
+ if err != nil {
+ t.Fatalf("Dial() failed: %v", err)
+ }
+ msg := "Moo 2"
+ err = s.Info(msg)
+ if err != nil {
+ t.Fatalf("log failed: %v", err)
+ }
+ check(t, msg, <-done)
+
+ // restart the server
+ _, sock2, _ := startServer(net, addr, done)
+ defer sock2.Close()
+
+ // and try retransmitting
+ msg = "Moo 3"
+ err = s.Info(msg)
+ if err != nil {
+ t.Fatalf("log failed: %v", err)
}
- return false
+ check(t, msg, <-done)
+
+ s.Close()
}
func TestNew(t *testing.T) {
- if skipNetTest(t) {
- return
+ if LOG_LOCAL7 != 23<<3 {
+ t.Fatalf("LOG_LOCAL7 has wrong value")
+ }
+ if testing.Short() {
+ // Depends on syslog daemon running, and sometimes it's not.
+ t.Skip("skipping syslog test during -short")
}
- s, err := New(LOG_INFO, "")
+
+ s, err := New(LOG_INFO|LOG_USER, "the_tag")
if err != nil {
t.Fatalf("New() failed: %s", err)
}
@@ -61,57 +190,159 @@ func TestNew(t *testing.T) {
}
func TestNewLogger(t *testing.T) {
- if skipNetTest(t) {
- return
+ if testing.Short() {
+ t.Skip("skipping syslog test during -short")
}
- f, err := NewLogger(LOG_INFO, 0)
+ f, err := NewLogger(LOG_USER|LOG_INFO, 0)
if f == nil {
t.Error(err)
}
}
func TestDial(t *testing.T) {
- if skipNetTest(t) {
- return
+ if testing.Short() {
+ t.Skip("skipping syslog test during -short")
}
- l, err := Dial("", "", LOG_ERR, "syslog_test")
+ f, err := Dial("", "", (LOG_LOCAL7|LOG_DEBUG)+1, "syslog_test")
+ if f != nil {
+ t.Fatalf("Should have trapped bad priority")
+ }
+ f, err = Dial("", "", -1, "syslog_test")
+ if f != nil {
+ t.Fatalf("Should have trapped bad priority")
+ }
+ l, err := Dial("", "", LOG_USER|LOG_ERR, "syslog_test")
if err != nil {
t.Fatalf("Dial() failed: %s", err)
}
l.Close()
}
-func TestUDPDial(t *testing.T) {
- done := make(chan string)
- startServer(done)
- l, err := Dial("udp", serverAddr, LOG_INFO, "syslog_test")
- if err != nil {
- t.Fatalf("syslog.Dial() failed: %s", err)
- }
- msg := "udp test"
- l.Info(msg)
- expected := "<6>syslog_test: udp test\n"
- rcvd := <-done
- if rcvd != expected {
- t.Fatalf("s.Info() = '%q', but wanted '%q'", rcvd, expected)
+func check(t *testing.T, in, out string) {
+ tmpl := fmt.Sprintf("<%d>%%s %%s syslog_test[%%d]: %s\n", LOG_USER+LOG_INFO, in)
+ if hostname, err := os.Hostname(); err != nil {
+ t.Error("Error retrieving hostname")
+ } else {
+ var parsedHostname, timestamp string
+ var pid int
+ if n, err := fmt.Sscanf(out, tmpl, &timestamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname {
+ t.Errorf("Got %q, does not match template %q (%d %s)", out, tmpl, n, err)
+ }
}
}
func TestWrite(t *testing.T) {
- done := make(chan string)
- startServer(done)
- l, err := Dial("udp", serverAddr, LOG_ERR, "syslog_test")
- if err != nil {
- t.Fatalf("syslog.Dial() failed: %s", err)
+ tests := []struct {
+ pri Priority
+ pre string
+ msg string
+ exp string
+ }{
+ {LOG_USER | LOG_ERR, "syslog_test", "", "%s %s syslog_test[%d]: \n"},
+ {LOG_USER | LOG_ERR, "syslog_test", "write test", "%s %s syslog_test[%d]: write test\n"},
+ // Write should not add \n if there already is one
+ {LOG_USER | LOG_ERR, "syslog_test", "write test 2\n", "%s %s syslog_test[%d]: write test 2\n"},
}
- msg := "write test"
- _, err = io.WriteString(l, msg)
+
+ if hostname, err := os.Hostname(); err != nil {
+ t.Fatalf("Error retrieving hostname")
+ } else {
+ for _, test := range tests {
+ done := make(chan string)
+ addr, sock, _ := startServer("udp", "", done)
+ defer sock.Close()
+ l, err := Dial("udp", addr, test.pri, test.pre)
+ if err != nil {
+ t.Fatalf("syslog.Dial() failed: %v", err)
+ }
+ _, err = io.WriteString(l, test.msg)
+ if err != nil {
+ t.Fatalf("WriteString() failed: %v", err)
+ }
+ rcvd := <-done
+ test.exp = fmt.Sprintf("<%d>", test.pri) + test.exp
+ var parsedHostname, timestamp string
+ var pid int
+ if n, err := fmt.Sscanf(rcvd, test.exp, &timestamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname {
+ t.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, test.exp, n, err)
+ }
+ }
+ }
+}
+
+func TestConcurrentWrite(t *testing.T) {
+ addr, sock, _ := startServer("udp", "", make(chan string))
+ defer sock.Close()
+ w, err := Dial("udp", addr, LOG_USER|LOG_ERR, "how's it going?")
if err != nil {
- t.Fatalf("WriteString() failed: %s", err)
+ t.Fatalf("syslog.Dial() failed: %v", err)
+ }
+ var wg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ err := w.Info("test")
+ if err != nil {
+ t.Errorf("Info() failed: %v", err)
+ return
+ }
+ wg.Done()
+ }()
}
- expected := "<3>syslog_test: write test\n"
- rcvd := <-done
- if rcvd != expected {
- t.Fatalf("s.Info() = '%q', but wanted '%q'", rcvd, expected)
+ wg.Wait()
+}
+
+func TestConcurrentReconnect(t *testing.T) {
+ crashy = true
+ defer func() { crashy = false }()
+
+ net := "unix"
+ done := make(chan string)
+ addr, sock, srvWG := startServer(net, "", done)
+ defer os.Remove(addr)
+
+ // count all the messages arriving
+ count := make(chan int)
+ go func() {
+ ct := 0
+ for _ = range done {
+ ct++
+ // we are looking for 500 out of 1000 events
+ // here because lots of log messages are lost
+ // in buffers (kernel and/or bufio)
+ if ct > 500 {
+ break
+ }
+ }
+ count <- ct
+ }()
+
+ var wg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ w, err := Dial(net, addr, LOG_USER|LOG_ERR, "tag")
+ if err != nil {
+ t.Fatalf("syslog.Dial() failed: %v", err)
+ }
+ for i := 0; i < 100; i++ {
+ err := w.Info("test")
+ if err != nil {
+ t.Errorf("Info() failed: %v", err)
+ return
+ }
+ }
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+ sock.Close()
+ srvWG.Wait()
+ close(done)
+
+ select {
+ case <-count:
+ case <-time.After(100 * time.Millisecond):
+ t.Error("timeout in concurrent reconnect")
}
}
diff --git a/src/pkg/log/syslog/syslog_unix.go b/src/pkg/log/syslog/syslog_unix.go
index 46a164dd5..a0001ccae 100644
--- a/src/pkg/log/syslog/syslog_unix.go
+++ b/src/pkg/log/syslog/syslog_unix.go
@@ -14,18 +14,16 @@ import (
// unixSyslog opens a connection to the syslog daemon running on the
// local machine using a Unix domain socket.
-func unixSyslog() (conn serverConn, err error) {
+func unixSyslog() (conn net.Conn, err error) {
logTypes := []string{"unixgram", "unix"}
logPaths := []string{"/dev/log", "/var/run/syslog"}
- var raddr string
for _, network := range logTypes {
for _, path := range logPaths {
- raddr = path
- conn, err := net.Dial(network, raddr)
+ conn, err := net.Dial(network, path)
if err != nil {
continue
} else {
- return netConn{conn}, nil
+ return conn, nil
}
}
}
diff --git a/src/pkg/math/abs_arm.s b/src/pkg/math/abs_arm.s
index 23f6a2a2d..37a1459fe 100644
--- a/src/pkg/math/abs_arm.s
+++ b/src/pkg/math/abs_arm.s
@@ -3,4 +3,9 @@
// license that can be found in the LICENSE file.
TEXT ·Abs(SB),7,$0
- B ·abs(SB)
+ MOVW x+0(FP), R0
+ MOVW x+4(FP), R1
+ AND $((1<<31)-1), R1
+ MOVW R0, r+8(FP)
+ MOVW R1, r+12(FP)
+ RET
diff --git a/src/pkg/math/acosh.go b/src/pkg/math/acosh.go
index c6c8645e1..e394008b0 100644
--- a/src/pkg/math/acosh.go
+++ b/src/pkg/math/acosh.go
@@ -33,7 +33,7 @@ package math
// acosh(NaN) is NaN without signal.
//
-// Acosh(x) calculates the inverse hyperbolic cosine of x.
+// Acosh returns the inverse hyperbolic cosine of x.
//
// Special cases are:
// Acosh(+Inf) = +Inf
diff --git a/src/pkg/math/all_test.go b/src/pkg/math/all_test.go
index ed66a42fb..0d8b10f67 100644
--- a/src/pkg/math/all_test.go
+++ b/src/pkg/math/all_test.go
@@ -1128,11 +1128,11 @@ var vfgammaSC = []float64{
NaN(),
}
var gammaSC = []float64{
+ NaN(),
+ NaN(),
Inf(-1),
Inf(1),
Inf(1),
- Inf(1),
- Inf(1),
NaN(),
}
@@ -1693,6 +1693,17 @@ func alike(a, b float64) bool {
return false
}
+func TestNaN(t *testing.T) {
+ f64 := NaN()
+ if f64 == f64 {
+ t.Fatalf("NaN() returns %g, expected NaN", f64)
+ }
+ f32 := float32(f64)
+ if f32 == f32 {
+ t.Fatalf("float32(NaN()) is %g, expected NaN", f32)
+ }
+}
+
func TestAcos(t *testing.T) {
for i := 0; i < len(vf); i++ {
a := vf[i] / 10
@@ -2270,6 +2281,13 @@ func TestLog2(t *testing.T) {
t.Errorf("Log2(%g) = %g, want %g", vflogSC[i], f, logSC[i])
}
}
+ for i := -1074; i <= 1023; i++ {
+ f := Ldexp(1, i)
+ l := Log2(f)
+ if l != float64(i) {
+ t.Errorf("Log2(2**%d) = %g, want %d", i, l, i)
+ }
+ }
}
func TestModf(t *testing.T) {
diff --git a/src/pkg/math/asinh.go b/src/pkg/math/asinh.go
index 0defbb9be..ff2de0215 100644
--- a/src/pkg/math/asinh.go
+++ b/src/pkg/math/asinh.go
@@ -30,7 +30,7 @@ package math
// := sign(x)*log1p(|x| + x**2/(1 + sqrt(1+x**2)))
//
-// Asinh(x) calculates the inverse hyperbolic sine of x.
+// Asinh returns the inverse hyperbolic sine of x.
//
// Special cases are:
// Asinh(±0) = ±0
diff --git a/src/pkg/math/atan.go b/src/pkg/math/atan.go
index d424a2be4..c107d388d 100644
--- a/src/pkg/math/atan.go
+++ b/src/pkg/math/atan.go
@@ -6,51 +6,92 @@ package math
/*
Floating-point arctangent.
-
- Atan returns the value of the arctangent of its
- argument in the range [-pi/2,pi/2].
- There are no error returns.
- Coefficients are #5077 from Hart & Cheney. (19.56D)
*/
-// xatan evaluates a series valid in the
-// range [-0.414...,+0.414...]. (tan(pi/8))
-func xatan(arg float64) float64 {
+// The original C code, the long comment, and the constants below were
+// from http://netlib.sandia.gov/cephes/cmath/atan.c, available from
+// http://www.netlib.org/cephes/cmath.tgz.
+// The go code is a version of the original C.
+//
+// atan.c
+// Inverse circular tangent (arctangent)
+//
+// SYNOPSIS:
+// double x, y, atan();
+// y = atan( x );
+//
+// DESCRIPTION:
+// Returns radian angle between -pi/2 and +pi/2 whose tangent is x.
+//
+// Range reduction is from three intervals into the interval from zero to 0.66.
+// The approximant uses a rational function of degree 4/5 of the form
+// x + x**3 P(x)/Q(x).
+//
+// ACCURACY:
+// Relative error:
+// arithmetic domain # trials peak rms
+// DEC -10, 10 50000 2.4e-17 8.3e-18
+// IEEE -10, 10 10^6 1.8e-16 5.0e-17
+//
+// Cephes Math Library Release 2.8: June, 2000
+// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
+//
+// The readme file at http://netlib.sandia.gov/cephes/ says:
+// Some software in this archive may be from the book _Methods and
+// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
+// International, 1989) or from the Cephes Mathematical Library, a
+// commercial product. In either event, it is copyrighted by the author.
+// What you see here may be used freely but it comes with no support or
+// guarantee.
+//
+// The two known misprints in the book are repaired here in the
+// source listings for the gamma function and the incomplete beta
+// integral.
+//
+// Stephen L. Moshier
+// moshier@na-net.ornl.gov
+
+// xatan evaluates a series valid in the range [0, 0.66].
+func xatan(x float64) float64 {
const (
- P4 = .161536412982230228262e2
- P3 = .26842548195503973794141e3
- P2 = .11530293515404850115428136e4
- P1 = .178040631643319697105464587e4
- P0 = .89678597403663861959987488e3
- Q4 = .5895697050844462222791e2
- Q3 = .536265374031215315104235e3
- Q2 = .16667838148816337184521798e4
- Q1 = .207933497444540981287275926e4
- Q0 = .89678597403663861962481162e3
+ P0 = -8.750608600031904122785e-01
+ P1 = -1.615753718733365076637e+01
+ P2 = -7.500855792314704667340e+01
+ P3 = -1.228866684490136173410e+02
+ P4 = -6.485021904942025371773e+01
+ Q0 = +2.485846490142306297962e+01
+ Q1 = +1.650270098316988542046e+02
+ Q2 = +4.328810604912902668951e+02
+ Q3 = +4.853903996359136964868e+02
+ Q4 = +1.945506571482613964425e+02
)
- sq := arg * arg
- value := ((((P4*sq+P3)*sq+P2)*sq+P1)*sq + P0)
- value = value / (((((sq+Q4)*sq+Q3)*sq+Q2)*sq+Q1)*sq + Q0)
- return value * arg
+ z := x * x
+ z = z * ((((P0*z+P1)*z+P2)*z+P3)*z + P4) / (((((z+Q0)*z+Q1)*z+Q2)*z+Q3)*z + Q4)
+ z = x*z + x
+ return z
}
// satan reduces its argument (known to be positive)
-// to the range [0,0.414...] and calls xatan.
-func satan(arg float64) float64 {
- if arg < Sqrt2-1 {
- return xatan(arg)
+// to the range [0, 0.66] and calls xatan.
+func satan(x float64) float64 {
+ const (
+ Morebits = 6.123233995736765886130e-17 // pi/2 = PIO2 + Morebits
+ Tan3pio8 = 2.41421356237309504880 // tan(3*pi/8)
+ )
+ if x <= 0.66 {
+ return xatan(x)
}
- if arg > Sqrt2+1 {
- return Pi/2 - xatan(1/arg)
+ if x > Tan3pio8 {
+ return Pi/2 - xatan(1/x) + Morebits
}
- return Pi/4 + xatan((arg-1)/(arg+1))
+ return Pi/4 + xatan((x-1)/(x+1)) + 0.5*Morebits
}
// Atan returns the arctangent of x.
//
// Special cases are:
-// Atan(±0) = ±0
-// Atan(±Inf) = ±Pi/2
+// Atan(±0) = ±0
+// Atan(±Inf) = ±Pi/2
func Atan(x float64) float64
func atan(x float64) float64 {
diff --git a/src/pkg/math/atanh.go b/src/pkg/math/atanh.go
index 5b5d46855..113d5c103 100644
--- a/src/pkg/math/atanh.go
+++ b/src/pkg/math/atanh.go
@@ -36,7 +36,7 @@ package math
// atanh(+-1) is +-INF with signal.
//
-// Atanh(x) calculates the inverse hyperbolic tangent of x.
+// Atanh returns the inverse hyperbolic tangent of x.
//
// Special cases are:
// Atanh(1) = +Inf
diff --git a/src/pkg/math/big/arith_386.s b/src/pkg/math/big/arith_386.s
index f1262c651..c62483317 100644
--- a/src/pkg/math/big/arith_386.s
+++ b/src/pkg/math/big/arith_386.s
@@ -29,7 +29,7 @@ TEXT ·addVV(SB),7,$0
MOVL z+0(FP), DI
MOVL x+12(FP), SI
MOVL y+24(FP), CX
- MOVL n+4(FP), BP
+ MOVL z+4(FP), BP
MOVL $0, BX // i = 0
MOVL $0, DX // c = 0
JMP E1
@@ -54,7 +54,7 @@ TEXT ·subVV(SB),7,$0
MOVL z+0(FP), DI
MOVL x+12(FP), SI
MOVL y+24(FP), CX
- MOVL n+4(FP), BP
+ MOVL z+4(FP), BP
MOVL $0, BX // i = 0
MOVL $0, DX // c = 0
JMP E2
@@ -78,7 +78,7 @@ TEXT ·addVW(SB),7,$0
MOVL z+0(FP), DI
MOVL x+12(FP), SI
MOVL y+24(FP), AX // c = y
- MOVL n+4(FP), BP
+ MOVL z+4(FP), BP
MOVL $0, BX // i = 0
JMP E3
@@ -100,7 +100,7 @@ TEXT ·subVW(SB),7,$0
MOVL z+0(FP), DI
MOVL x+12(FP), SI
MOVL y+24(FP), AX // c = y
- MOVL n+4(FP), BP
+ MOVL z+4(FP), BP
MOVL $0, BX // i = 0
JMP E4
@@ -120,7 +120,7 @@ E4: CMPL BX, BP // i < n
// func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB),7,$0
- MOVL n+4(FP), BX // i = n
+ MOVL z+4(FP), BX // i = z
SUBL $1, BX // i--
JL X8b // i < 0 (n <= 0)
@@ -155,7 +155,7 @@ X8b: MOVL $0, c+28(FP)
// func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB),7,$0
- MOVL n+4(FP), BP
+ MOVL z+4(FP), BP
SUBL $1, BP // n--
JL X9b // n < 0 (n <= 0)
@@ -196,7 +196,7 @@ TEXT ·mulAddVWW(SB),7,$0
MOVL x+12(FP), SI
MOVL y+24(FP), BP
MOVL r+28(FP), CX // c = r
- MOVL n+4(FP), BX
+ MOVL z+4(FP), BX
LEAL (DI)(BX*4), DI
LEAL (SI)(BX*4), SI
NEGL BX // i = -n
@@ -222,7 +222,7 @@ TEXT ·addMulVVW(SB),7,$0
MOVL z+0(FP), DI
MOVL x+12(FP), SI
MOVL y+24(FP), BP
- MOVL n+4(FP), BX
+ MOVL z+4(FP), BX
LEAL (DI)(BX*4), DI
LEAL (SI)(BX*4), SI
NEGL BX // i = -n
@@ -251,7 +251,7 @@ TEXT ·divWVW(SB),7,$0
MOVL xn+12(FP), DX // r = xn
MOVL x+16(FP), SI
MOVL y+28(FP), CX
- MOVL n+4(FP), BX // i = n
+ MOVL z+4(FP), BX // i = z
JMP E7
L7: MOVL (SI)(BX*4), AX
diff --git a/src/pkg/math/big/arith_amd64.s b/src/pkg/math/big/arith_amd64.s
index 54f647322..d85964502 100644
--- a/src/pkg/math/big/arith_amd64.s
+++ b/src/pkg/math/big/arith_amd64.s
@@ -5,7 +5,15 @@
// This file provides fast assembly versions for the elementary
// arithmetic operations on vectors implemented in arith.go.
-// TODO(gri) - experiment with unrolled loops for faster execution
+// Literal instruction for MOVQ $0, CX.
+// (MOVQ $0, reg is translated to XORQ reg, reg and clears CF.)
+#define ZERO_CX BYTE $0x48; \
+ BYTE $0xc7; \
+ BYTE $0xc1; \
+ BYTE $0x00; \
+ BYTE $0x00; \
+ BYTE $0x00; \
+ BYTE $0x00
// func mulWW(x, y Word) (z1, z0 Word)
TEXT ·mulWW(SB),7,$0
@@ -28,114 +36,231 @@ TEXT ·divWW(SB),7,$0
// func addVV(z, x, y []Word) (c Word)
TEXT ·addVV(SB),7,$0
+ MOVQ z+8(FP), DI
+ MOVQ x+24(FP), R8
+ MOVQ y+48(FP), R9
MOVQ z+0(FP), R10
- MOVQ x+16(FP), R8
- MOVQ y+32(FP), R9
- MOVL n+8(FP), R11
- MOVQ $0, BX // i = 0
- MOVQ $0, DX // c = 0
- JMP E1
-
-L1: MOVQ (R8)(BX*8), AX
- RCRQ $1, DX
- ADCQ (R9)(BX*8), AX
- RCLQ $1, DX
- MOVQ AX, (R10)(BX*8)
- ADDL $1, BX // i++
-E1: CMPQ BX, R11 // i < n
- JL L1
-
- MOVQ DX, c+48(FP)
+ MOVQ $0, CX // c = 0
+ MOVQ $0, SI // i = 0
+
+ // s/JL/JMP/ below to disable the unrolled loop
+ SUBQ $4, DI // n -= 4
+ JL V1 // if n < 0 goto V1
+
+U1: // n >= 0
+ // regular loop body unrolled 4x
+ RCRQ $1, CX // CF = c
+ MOVQ 0(R8)(SI*8), R11
+ MOVQ 8(R8)(SI*8), R12
+ MOVQ 16(R8)(SI*8), R13
+ MOVQ 24(R8)(SI*8), R14
+ ADCQ 0(R9)(SI*8), R11
+ ADCQ 8(R9)(SI*8), R12
+ ADCQ 16(R9)(SI*8), R13
+ ADCQ 24(R9)(SI*8), R14
+ MOVQ R11, 0(R10)(SI*8)
+ MOVQ R12, 8(R10)(SI*8)
+ MOVQ R13, 16(R10)(SI*8)
+ MOVQ R14, 24(R10)(SI*8)
+ RCLQ $1, CX // c = CF
+
+ ADDQ $4, SI // i += 4
+ SUBQ $4, DI // n -= 4
+ JGE U1 // if n >= 0 goto U1
+
+V1: ADDQ $4, DI // n += 4
+ JLE E1 // if n <= 0 goto E1
+
+L1: // n > 0
+ RCRQ $1, CX // CF = c
+ MOVQ 0(R8)(SI*8), R11
+ ADCQ 0(R9)(SI*8), R11
+ MOVQ R11, 0(R10)(SI*8)
+ RCLQ $1, CX // c = CF
+
+ ADDQ $1, SI // i++
+ SUBQ $1, DI // n--
+ JG L1 // if n > 0 goto L1
+
+E1: MOVQ CX, c+72(FP) // return c
RET
// func subVV(z, x, y []Word) (c Word)
-// (same as addVV_s except for SBBQ instead of ADCQ and label names)
+// (same as addVV except for SBBQ instead of ADCQ and label names)
TEXT ·subVV(SB),7,$0
+ MOVQ z+8(FP), DI
+ MOVQ x+24(FP), R8
+ MOVQ y+48(FP), R9
MOVQ z+0(FP), R10
- MOVQ x+16(FP), R8
- MOVQ y+32(FP), R9
- MOVL n+8(FP), R11
- MOVQ $0, BX // i = 0
- MOVQ $0, DX // c = 0
- JMP E2
-
-L2: MOVQ (R8)(BX*8), AX
- RCRQ $1, DX
- SBBQ (R9)(BX*8), AX
- RCLQ $1, DX
- MOVQ AX, (R10)(BX*8)
- ADDL $1, BX // i++
-
-E2: CMPQ BX, R11 // i < n
- JL L2
- MOVQ DX, c+48(FP)
+ MOVQ $0, CX // c = 0
+ MOVQ $0, SI // i = 0
+
+ // s/JL/JMP/ below to disable the unrolled loop
+ SUBQ $4, DI // n -= 4
+ JL V2 // if n < 0 goto V2
+
+U2: // n >= 0
+ // regular loop body unrolled 4x
+ RCRQ $1, CX // CF = c
+ MOVQ 0(R8)(SI*8), R11
+ MOVQ 8(R8)(SI*8), R12
+ MOVQ 16(R8)(SI*8), R13
+ MOVQ 24(R8)(SI*8), R14
+ SBBQ 0(R9)(SI*8), R11
+ SBBQ 8(R9)(SI*8), R12
+ SBBQ 16(R9)(SI*8), R13
+ SBBQ 24(R9)(SI*8), R14
+ MOVQ R11, 0(R10)(SI*8)
+ MOVQ R12, 8(R10)(SI*8)
+ MOVQ R13, 16(R10)(SI*8)
+ MOVQ R14, 24(R10)(SI*8)
+ RCLQ $1, CX // c = CF
+
+ ADDQ $4, SI // i += 4
+ SUBQ $4, DI // n -= 4
+ JGE U2 // if n >= 0 goto U2
+
+V2: ADDQ $4, DI // n += 4
+ JLE E2 // if n <= 0 goto E2
+
+L2: // n > 0
+ RCRQ $1, CX // CF = c
+ MOVQ 0(R8)(SI*8), R11
+ SBBQ 0(R9)(SI*8), R11
+ MOVQ R11, 0(R10)(SI*8)
+ RCLQ $1, CX // c = CF
+
+ ADDQ $1, SI // i++
+ SUBQ $1, DI // n--
+ JG L2 // if n > 0 goto L2
+
+E2: MOVQ CX, c+72(FP) // return c
RET
// func addVW(z, x []Word, y Word) (c Word)
TEXT ·addVW(SB),7,$0
+ MOVQ z+8(FP), DI
+ MOVQ x+24(FP), R8
+ MOVQ y+48(FP), CX // c = y
MOVQ z+0(FP), R10
- MOVQ x+16(FP), R8
- MOVQ y+32(FP), AX // c = y
- MOVL n+8(FP), R11
- MOVQ $0, BX // i = 0
- JMP E3
-L3: ADDQ (R8)(BX*8), AX
- MOVQ AX, (R10)(BX*8)
- RCLQ $1, AX
- ANDQ $1, AX
- ADDL $1, BX // i++
-
-E3: CMPQ BX, R11 // i < n
- JL L3
-
- MOVQ AX, c+40(FP)
+ MOVQ $0, SI // i = 0
+
+ // s/JL/JMP/ below to disable the unrolled loop
+ SUBQ $4, DI // n -= 4
+ JL V3 // if n < 4 goto V3
+
+U3: // n >= 0
+ // regular loop body unrolled 4x
+ MOVQ 0(R8)(SI*8), R11
+ MOVQ 8(R8)(SI*8), R12
+ MOVQ 16(R8)(SI*8), R13
+ MOVQ 24(R8)(SI*8), R14
+ ADDQ CX, R11
+ ZERO_CX
+ ADCQ $0, R12
+ ADCQ $0, R13
+ ADCQ $0, R14
+ SETCS CX // c = CF
+ MOVQ R11, 0(R10)(SI*8)
+ MOVQ R12, 8(R10)(SI*8)
+ MOVQ R13, 16(R10)(SI*8)
+ MOVQ R14, 24(R10)(SI*8)
+
+ ADDQ $4, SI // i += 4
+ SUBQ $4, DI // n -= 4
+ JGE U3 // if n >= 0 goto U3
+
+V3: ADDQ $4, DI // n += 4
+ JLE E3 // if n <= 0 goto E3
+
+L3: // n > 0
+ ADDQ 0(R8)(SI*8), CX
+ MOVQ CX, 0(R10)(SI*8)
+ ZERO_CX
+ RCLQ $1, CX // c = CF
+
+ ADDQ $1, SI // i++
+ SUBQ $1, DI // n--
+ JG L3 // if n > 0 goto L3
+
+E3: MOVQ CX, c+56(FP) // return c
RET
// func subVW(z, x []Word, y Word) (c Word)
+// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names)
TEXT ·subVW(SB),7,$0
+ MOVQ z+8(FP), DI
+ MOVQ x+24(FP), R8
+ MOVQ y+48(FP), CX // c = y
MOVQ z+0(FP), R10
- MOVQ x+16(FP), R8
- MOVQ y+32(FP), AX // c = y
- MOVL n+8(FP), R11
- MOVQ $0, BX // i = 0
- JMP E4
-
-L4: MOVQ (R8)(BX*8), DX // TODO(gri) is there a reverse SUBQ?
- SUBQ AX, DX
- MOVQ DX, (R10)(BX*8)
- RCLQ $1, AX
- ANDQ $1, AX
- ADDL $1, BX // i++
-
-E4: CMPQ BX, R11 // i < n
- JL L4
-
- MOVQ AX, c+40(FP)
+
+ MOVQ $0, SI // i = 0
+
+ // s/JL/JMP/ below to disable the unrolled loop
+ SUBQ $4, DI // n -= 4
+ JL V4 // if n < 4 goto V4
+
+U4: // n >= 0
+ // regular loop body unrolled 4x
+ MOVQ 0(R8)(SI*8), R11
+ MOVQ 8(R8)(SI*8), R12
+ MOVQ 16(R8)(SI*8), R13
+ MOVQ 24(R8)(SI*8), R14
+ SUBQ CX, R11
+ ZERO_CX
+ SBBQ $0, R12
+ SBBQ $0, R13
+ SBBQ $0, R14
+ SETCS CX // c = CF
+ MOVQ R11, 0(R10)(SI*8)
+ MOVQ R12, 8(R10)(SI*8)
+ MOVQ R13, 16(R10)(SI*8)
+ MOVQ R14, 24(R10)(SI*8)
+
+ ADDQ $4, SI // i += 4
+ SUBQ $4, DI // n -= 4
+ JGE U4 // if n >= 0 goto U4
+
+V4: ADDQ $4, DI // n += 4
+ JLE E4 // if n <= 0 goto E4
+
+L4: // n > 0
+ MOVQ 0(R8)(SI*8), R11
+ SUBQ CX, R11
+ MOVQ R11, 0(R10)(SI*8)
+ ZERO_CX
+ RCLQ $1, CX // c = CF
+
+ ADDQ $1, SI // i++
+ SUBQ $1, DI // n--
+ JG L4 // if n > 0 goto L4
+
+E4: MOVQ CX, c+56(FP) // return c
RET
// func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB),7,$0
- MOVL n+8(FP), BX // i = n
- SUBL $1, BX // i--
+ MOVQ z+8(FP), BX // i = z
+ SUBQ $1, BX // i--
JL X8b // i < 0 (n <= 0)
// n > 0
MOVQ z+0(FP), R10
- MOVQ x+16(FP), R8
- MOVL s+32(FP), CX
+ MOVQ x+24(FP), R8
+ MOVQ s+48(FP), CX
MOVQ (R8)(BX*8), AX // w1 = x[n-1]
MOVQ $0, DX
SHLQ CX, DX:AX // w1>>ŝ
- MOVQ DX, c+40(FP)
+ MOVQ DX, c+56(FP)
- CMPL BX, $0
+ CMPQ BX, $0
JLE X8a // i <= 0
// i > 0
@@ -143,7 +268,7 @@ L8: MOVQ AX, DX // w = w1
MOVQ -8(R8)(BX*8), AX // w1 = x[i-1]
SHLQ CX, DX:AX // w<<s | w1>>ŝ
MOVQ DX, (R10)(BX*8) // z[i] = w<<s | w1>>ŝ
- SUBL $1, BX // i--
+ SUBQ $1, BX // i--
JG L8 // i > 0
// i <= 0
@@ -151,24 +276,24 @@ X8a: SHLQ CX, AX // w1<<s
MOVQ AX, (R10) // z[0] = w1<<s
RET
-X8b: MOVQ $0, c+40(FP)
+X8b: MOVQ $0, c+56(FP)
RET
// func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB),7,$0
- MOVL n+8(FP), R11
- SUBL $1, R11 // n--
+ MOVQ z+8(FP), R11
+ SUBQ $1, R11 // n--
JL X9b // n < 0 (n <= 0)
// n > 0
MOVQ z+0(FP), R10
- MOVQ x+16(FP), R8
- MOVL s+32(FP), CX
+ MOVQ x+24(FP), R8
+ MOVQ s+48(FP), CX
MOVQ (R8), AX // w1 = x[0]
MOVQ $0, DX
SHRQ CX, DX:AX // w1<<ŝ
- MOVQ DX, c+40(FP)
+ MOVQ DX, c+56(FP)
MOVQ $0, BX // i = 0
JMP E9
@@ -178,7 +303,7 @@ L9: MOVQ AX, DX // w = w1
MOVQ 8(R8)(BX*8), AX // w1 = x[i+1]
SHRQ CX, DX:AX // w>>s | w1<<ŝ
MOVQ DX, (R10)(BX*8) // z[i] = w>>s | w1<<ŝ
- ADDL $1, BX // i++
+ ADDQ $1, BX // i++
E9: CMPQ BX, R11
JL L9 // i < n-1
@@ -188,17 +313,17 @@ X9a: SHRQ CX, AX // w1>>s
MOVQ AX, (R10)(R11*8) // z[n-1] = w1>>s
RET
-X9b: MOVQ $0, c+40(FP)
+X9b: MOVQ $0, c+56(FP)
RET
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
TEXT ·mulAddVWW(SB),7,$0
MOVQ z+0(FP), R10
- MOVQ x+16(FP), R8
- MOVQ y+32(FP), R9
- MOVQ r+40(FP), CX // c = r
- MOVL n+8(FP), R11
+ MOVQ x+24(FP), R8
+ MOVQ y+48(FP), R9
+ MOVQ r+56(FP), CX // c = r
+ MOVQ z+8(FP), R11
MOVQ $0, BX // i = 0
JMP E5
@@ -208,21 +333,21 @@ L5: MOVQ (R8)(BX*8), AX
ADCQ $0, DX
MOVQ AX, (R10)(BX*8)
MOVQ DX, CX
- ADDL $1, BX // i++
+ ADDQ $1, BX // i++
E5: CMPQ BX, R11 // i < n
JL L5
- MOVQ CX, c+48(FP)
+ MOVQ CX, c+64(FP)
RET
// func addMulVVW(z, x []Word, y Word) (c Word)
TEXT ·addMulVVW(SB),7,$0
MOVQ z+0(FP), R10
- MOVQ x+16(FP), R8
- MOVQ y+32(FP), R9
- MOVL n+8(FP), R11
+ MOVQ x+24(FP), R8
+ MOVQ y+48(FP), R9
+ MOVQ z+8(FP), R11
MOVQ $0, BX // i = 0
MOVQ $0, CX // c = 0
JMP E6
@@ -234,41 +359,41 @@ L6: MOVQ (R8)(BX*8), AX
ADDQ AX, (R10)(BX*8)
ADCQ $0, DX
MOVQ DX, CX
- ADDL $1, BX // i++
+ ADDQ $1, BX // i++
E6: CMPQ BX, R11 // i < n
JL L6
- MOVQ CX, c+40(FP)
+ MOVQ CX, c+56(FP)
RET
// func divWVW(z []Word, xn Word, x []Word, y Word) (r Word)
TEXT ·divWVW(SB),7,$0
MOVQ z+0(FP), R10
- MOVQ xn+16(FP), DX // r = xn
- MOVQ x+24(FP), R8
- MOVQ y+40(FP), R9
- MOVL n+8(FP), BX // i = n
+ MOVQ xn+24(FP), DX // r = xn
+ MOVQ x+32(FP), R8
+ MOVQ y+56(FP), R9
+ MOVQ z+8(FP), BX // i = z
JMP E7
L7: MOVQ (R8)(BX*8), AX
DIVQ R9
MOVQ AX, (R10)(BX*8)
-E7: SUBL $1, BX // i--
+E7: SUBQ $1, BX // i--
JGE L7 // i >= 0
- MOVQ DX, r+48(FP)
+ MOVQ DX, r+64(FP)
RET
// func bitLen(x Word) (n int)
TEXT ·bitLen(SB),7,$0
BSRQ x+0(FP), AX
JZ Z1
- INCL AX
- MOVL AX, n+8(FP)
+ ADDQ $1, AX
+ MOVQ AX, n+8(FP)
RET
-Z1: MOVL $0, n+8(FP)
+Z1: MOVQ $0, n+8(FP)
RET
diff --git a/src/pkg/math/big/arith_arm.s b/src/pkg/math/big/arith_arm.s
index dbf3360b5..64610f915 100644
--- a/src/pkg/math/big/arith_arm.s
+++ b/src/pkg/math/big/arith_arm.s
@@ -13,7 +13,7 @@ TEXT ·addVV(SB),7,$0
MOVW z+0(FP), R1
MOVW x+12(FP), R2
MOVW y+24(FP), R3
- MOVW n+4(FP), R4
+ MOVW z+4(FP), R4
MOVW R4<<2, R4
ADD R1, R4
B E1
@@ -41,7 +41,7 @@ TEXT ·subVV(SB),7,$0
MOVW z+0(FP), R1
MOVW x+12(FP), R2
MOVW y+24(FP), R3
- MOVW n+4(FP), R4
+ MOVW z+4(FP), R4
MOVW R4<<2, R4
ADD R1, R4
B E2
@@ -68,7 +68,7 @@ TEXT ·addVW(SB),7,$0
MOVW z+0(FP), R1
MOVW x+12(FP), R2
MOVW y+24(FP), R3
- MOVW n+4(FP), R4
+ MOVW z+4(FP), R4
MOVW R4<<2, R4
ADD R1, R4
CMP R1, R4
@@ -102,7 +102,7 @@ TEXT ·subVW(SB),7,$0
MOVW z+0(FP), R1
MOVW x+12(FP), R2
MOVW y+24(FP), R3
- MOVW n+4(FP), R4
+ MOVW z+4(FP), R4
MOVW R4<<2, R4
ADD R1, R4
CMP R1, R4
@@ -134,7 +134,7 @@ E4:
// func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB),7,$0
- MOVW n+4(FP), R5
+ MOVW z+4(FP), R5
CMP $0, R5
BEQ X7
@@ -183,7 +183,7 @@ X7:
// func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB),7,$0
- MOVW n+4(FP), R5
+ MOVW z+4(FP), R5
CMP $0, R5
BEQ X6
@@ -238,7 +238,7 @@ TEXT ·mulAddVWW(SB),7,$0
MOVW x+12(FP), R2
MOVW y+24(FP), R3
MOVW r+28(FP), R4
- MOVW n+4(FP), R5
+ MOVW z+4(FP), R5
MOVW R5<<2, R5
ADD R1, R5
B E8
@@ -265,7 +265,7 @@ TEXT ·addMulVVW(SB),7,$0
MOVW z+0(FP), R1
MOVW x+12(FP), R2
MOVW y+24(FP), R3
- MOVW n+4(FP), R5
+ MOVW z+4(FP), R5
MOVW R5<<2, R5
ADD R1, R5
MOVW $0, R4
@@ -314,7 +314,7 @@ TEXT ·mulWW(SB),7,$0
// func bitLen(x Word) (n int)
TEXT ·bitLen(SB),7,$0
MOVW x+0(FP), R0
- WORD $0xe16f0f10 // CLZ R0, R0 (count leading zeros)
+ CLZ R0, R0
MOVW $32, R1
SUB.S R0, R1
MOVW R1, n+4(FP)
diff --git a/src/pkg/math/big/arith_test.go b/src/pkg/math/big/arith_test.go
index c7e3d284c..3615a659c 100644
--- a/src/pkg/math/big/arith_test.go
+++ b/src/pkg/math/big/arith_test.go
@@ -4,7 +4,10 @@
package big
-import "testing"
+import (
+ "math/rand"
+ "testing"
+)
type funWW func(x, y, c Word) (z1, z0 Word)
type argWW struct {
@@ -100,6 +103,43 @@ func TestFunVV(t *testing.T) {
}
}
+// Always the same seed for reproducible results.
+var rnd = rand.New(rand.NewSource(0))
+
+func rndW() Word {
+ return Word(rnd.Int63()<<1 | rnd.Int63n(2))
+}
+
+func rndV(n int) []Word {
+ v := make([]Word, n)
+ for i := range v {
+ v[i] = rndW()
+ }
+ return v
+}
+
+func benchmarkFunVV(b *testing.B, f funVV, n int) {
+ x := rndV(n)
+ y := rndV(n)
+ z := make([]Word, n)
+ b.SetBytes(int64(n * _W))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ f(z, x, y)
+ }
+}
+
+func BenchmarkAddVV_1(b *testing.B) { benchmarkFunVV(b, addVV, 1) }
+func BenchmarkAddVV_2(b *testing.B) { benchmarkFunVV(b, addVV, 2) }
+func BenchmarkAddVV_3(b *testing.B) { benchmarkFunVV(b, addVV, 3) }
+func BenchmarkAddVV_4(b *testing.B) { benchmarkFunVV(b, addVV, 4) }
+func BenchmarkAddVV_5(b *testing.B) { benchmarkFunVV(b, addVV, 5) }
+func BenchmarkAddVV_1e1(b *testing.B) { benchmarkFunVV(b, addVV, 1e1) }
+func BenchmarkAddVV_1e2(b *testing.B) { benchmarkFunVV(b, addVV, 1e2) }
+func BenchmarkAddVV_1e3(b *testing.B) { benchmarkFunVV(b, addVV, 1e3) }
+func BenchmarkAddVV_1e4(b *testing.B) { benchmarkFunVV(b, addVV, 1e4) }
+func BenchmarkAddVV_1e5(b *testing.B) { benchmarkFunVV(b, addVV, 1e5) }
+
type funVW func(z, x []Word, y Word) (c Word)
type argVW struct {
z, x nat
@@ -210,6 +250,28 @@ func TestFunVW(t *testing.T) {
}
}
+func benchmarkFunVW(b *testing.B, f funVW, n int) {
+ x := rndV(n)
+ y := rndW()
+ z := make([]Word, n)
+ b.SetBytes(int64(n * _W))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ f(z, x, y)
+ }
+}
+
+func BenchmarkAddVW_1(b *testing.B) { benchmarkFunVW(b, addVW, 1) }
+func BenchmarkAddVW_2(b *testing.B) { benchmarkFunVW(b, addVW, 2) }
+func BenchmarkAddVW_3(b *testing.B) { benchmarkFunVW(b, addVW, 3) }
+func BenchmarkAddVW_4(b *testing.B) { benchmarkFunVW(b, addVW, 4) }
+func BenchmarkAddVW_5(b *testing.B) { benchmarkFunVW(b, addVW, 5) }
+func BenchmarkAddVW_1e1(b *testing.B) { benchmarkFunVW(b, addVW, 1e1) }
+func BenchmarkAddVW_1e2(b *testing.B) { benchmarkFunVW(b, addVW, 1e2) }
+func BenchmarkAddVW_1e3(b *testing.B) { benchmarkFunVW(b, addVW, 1e3) }
+func BenchmarkAddVW_1e4(b *testing.B) { benchmarkFunVW(b, addVW, 1e4) }
+func BenchmarkAddVW_1e5(b *testing.B) { benchmarkFunVW(b, addVW, 1e5) }
+
type funVWW func(z, x []Word, y, r Word) (c Word)
type argVWW struct {
z, x nat
@@ -334,6 +396,28 @@ func TestMulAddWWW(t *testing.T) {
}
}
+func benchmarkAddMulVVW(b *testing.B, n int) {
+ x := rndV(n)
+ y := rndW()
+ z := make([]Word, n)
+ b.SetBytes(int64(n * _W))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ addMulVVW(z, x, y)
+ }
+}
+
+func BenchmarkAddMulVVW_1(b *testing.B) { benchmarkAddMulVVW(b, 1) }
+func BenchmarkAddMulVVW_2(b *testing.B) { benchmarkAddMulVVW(b, 2) }
+func BenchmarkAddMulVVW_3(b *testing.B) { benchmarkAddMulVVW(b, 3) }
+func BenchmarkAddMulVVW_4(b *testing.B) { benchmarkAddMulVVW(b, 4) }
+func BenchmarkAddMulVVW_5(b *testing.B) { benchmarkAddMulVVW(b, 5) }
+func BenchmarkAddMulVVW_1e1(b *testing.B) { benchmarkAddMulVVW(b, 1e1) }
+func BenchmarkAddMulVVW_1e2(b *testing.B) { benchmarkAddMulVVW(b, 1e2) }
+func BenchmarkAddMulVVW_1e3(b *testing.B) { benchmarkAddMulVVW(b, 1e3) }
+func BenchmarkAddMulVVW_1e4(b *testing.B) { benchmarkAddMulVVW(b, 1e4) }
+func BenchmarkAddMulVVW_1e5(b *testing.B) { benchmarkAddMulVVW(b, 1e5) }
+
func testWordBitLen(t *testing.T, fname string, f func(Word) int) {
for i := 0; i <= _W; i++ {
x := Word(1) << uint(i-1) // i == 0 => x == 0
diff --git a/src/pkg/math/big/calibrate_test.go b/src/pkg/math/big/calibrate_test.go
index efe1837bb..f69ffbf5c 100644
--- a/src/pkg/math/big/calibrate_test.go
+++ b/src/pkg/math/big/calibrate_test.go
@@ -21,15 +21,17 @@ import (
var calibrate = flag.Bool("calibrate", false, "run calibration test")
-// measure returns the time to run f
-func measure(f func()) time.Duration {
- const N = 100
- start := time.Now()
- for i := N; i > 0; i-- {
- f()
- }
- stop := time.Now()
- return stop.Sub(start) / N
+func karatsubaLoad(b *testing.B) {
+ BenchmarkMul(b)
+}
+
+// measureKaratsuba returns the time to run a Karatsuba-relevant benchmark
+// given Karatsuba threshold th.
+func measureKaratsuba(th int) time.Duration {
+ th, karatsubaThreshold = karatsubaThreshold, th
+ res := testing.Benchmark(karatsubaLoad)
+ karatsubaThreshold = th
+ return time.Duration(res.NsPerOp())
}
func computeThresholds() {
@@ -37,35 +39,33 @@ func computeThresholds() {
fmt.Printf("(run repeatedly for good results)\n")
// determine Tk, the work load execution time using basic multiplication
- karatsubaThreshold = 1e9 // disable karatsuba
- Tb := measure(benchmarkMulLoad)
- fmt.Printf("Tb = %dns\n", Tb)
+ Tb := measureKaratsuba(1e9) // th == 1e9 => Karatsuba multiplication disabled
+ fmt.Printf("Tb = %10s\n", Tb)
// thresholds
- n := 8 // any lower values for the threshold lead to very slow multiplies
+ th := 4
th1 := -1
th2 := -1
var deltaOld time.Duration
- for count := -1; count != 0; count-- {
+ for count := -1; count != 0 && th < 128; count-- {
// determine Tk, the work load execution time using Karatsuba multiplication
- karatsubaThreshold = n // enable karatsuba
- Tk := measure(benchmarkMulLoad)
+ Tk := measureKaratsuba(th)
// improvement over Tb
delta := (Tb - Tk) * 100 / Tb
- fmt.Printf("n = %3d Tk = %8dns %4d%%", n, Tk, delta)
+ fmt.Printf("th = %3d Tk = %10s %4d%%", th, Tk, delta)
// determine break-even point
if Tk < Tb && th1 < 0 {
- th1 = n
+ th1 = th
fmt.Print(" break-even point")
}
// determine diminishing return
if 0 < delta && delta < deltaOld && th2 < 0 {
- th2 = n
+ th2 = th
fmt.Print(" diminishing return")
}
deltaOld = delta
@@ -74,10 +74,10 @@ func computeThresholds() {
// trigger counter
if th1 >= 0 && th2 >= 0 && count < 0 {
- count = 20 // this many extra measurements after we got both thresholds
+ count = 10 // this many extra measurements after we got both thresholds
}
- n++
+ th++
}
}
diff --git a/src/pkg/math/big/gcd_test.go b/src/pkg/math/big/gcd_test.go
new file mode 100644
index 000000000..c0b9f5830
--- /dev/null
+++ b/src/pkg/math/big/gcd_test.go
@@ -0,0 +1,47 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements a GCD benchmark.
+// Usage: go test math/big -test.bench GCD
+
+package big
+
+import (
+ "math/rand"
+ "testing"
+)
+
+// randInt returns a pseudo-random Int in the range [1<<(size-1), (1<<size) - 1]
+func randInt(r *rand.Rand, size uint) *Int {
+ n := new(Int).Lsh(intOne, size-1)
+ x := new(Int).Rand(r, n)
+ return x.Add(x, n) // make sure result > 1<<(size-1)
+}
+
+func runGCD(b *testing.B, aSize, bSize uint) {
+ b.StopTimer()
+ var r = rand.New(rand.NewSource(1234))
+ aa := randInt(r, aSize)
+ bb := randInt(r, bSize)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ new(Int).GCD(nil, nil, aa, bb)
+ }
+}
+
+func BenchmarkGCD10x10(b *testing.B) { runGCD(b, 10, 10) }
+func BenchmarkGCD10x100(b *testing.B) { runGCD(b, 10, 100) }
+func BenchmarkGCD10x1000(b *testing.B) { runGCD(b, 10, 1000) }
+func BenchmarkGCD10x10000(b *testing.B) { runGCD(b, 10, 10000) }
+func BenchmarkGCD10x100000(b *testing.B) { runGCD(b, 10, 100000) }
+func BenchmarkGCD100x100(b *testing.B) { runGCD(b, 100, 100) }
+func BenchmarkGCD100x1000(b *testing.B) { runGCD(b, 100, 1000) }
+func BenchmarkGCD100x10000(b *testing.B) { runGCD(b, 100, 10000) }
+func BenchmarkGCD100x100000(b *testing.B) { runGCD(b, 100, 100000) }
+func BenchmarkGCD1000x1000(b *testing.B) { runGCD(b, 1000, 1000) }
+func BenchmarkGCD1000x10000(b *testing.B) { runGCD(b, 1000, 10000) }
+func BenchmarkGCD1000x100000(b *testing.B) { runGCD(b, 1000, 100000) }
+func BenchmarkGCD10000x10000(b *testing.B) { runGCD(b, 10000, 10000) }
+func BenchmarkGCD10000x100000(b *testing.B) { runGCD(b, 10000, 100000) }
+func BenchmarkGCD100000x100000(b *testing.B) { runGCD(b, 100000, 100000) }
diff --git a/src/pkg/math/big/int.go b/src/pkg/math/big/int.go
index cd2cd0e2d..bf2fd2009 100644
--- a/src/pkg/math/big/int.go
+++ b/src/pkg/math/big/int.go
@@ -51,6 +51,13 @@ func (z *Int) SetInt64(x int64) *Int {
return z
}
+// SetUint64 sets z to x and returns z.
+func (z *Int) SetUint64(x uint64) *Int {
+ z.abs = z.abs.setUint64(uint64(x))
+ z.neg = false
+ return z
+}
+
// NewInt allocates and returns a new Int set to x.
func NewInt(x int64) *Int {
return new(Int).SetInt64(x)
@@ -412,7 +419,7 @@ func (x *Int) Format(s fmt.State, ch rune) {
if precisionSet {
switch {
case len(digits) < precision:
- zeroes = precision - len(digits) // count of zero padding
+ zeroes = precision - len(digits) // count of zero padding
case digits == "0" && precision == 0:
return // print nothing if zero value (x == 0) and zero precision ("." or ".0")
}
@@ -519,6 +526,19 @@ func (x *Int) Int64() int64 {
return v
}
+// Uint64 returns the uint64 representation of x.
+// If x cannot be represented in an uint64, the result is undefined.
+func (x *Int) Uint64() uint64 {
+ if len(x.abs) == 0 {
+ return 0
+ }
+ v := uint64(x.abs[0])
+ if _W == 32 && len(x.abs) > 1 {
+ v |= uint64(x.abs[1]) << 32
+ }
+ return v
+}
+
// SetString sets z to the value of s, interpreted in the given base,
// and returns z and a boolean indicating success. If SetString fails,
// the value of z is undefined but the returned value is nil.
@@ -561,19 +581,18 @@ func (x *Int) BitLen() int {
return x.abs.bitLen()
}
-// Exp sets z = x**y mod m and returns z. If m is nil, z = x**y.
+// Exp sets z = x**y mod |m| (i.e. the sign of m is ignored), and returns z.
+// If y <= 0, the result is 1; if m == nil or m == 0, z = x**y.
// See Knuth, volume 2, section 4.6.3.
func (z *Int) Exp(x, y, m *Int) *Int {
if y.neg || len(y.abs) == 0 {
- neg := x.neg
- z.SetInt64(1)
- z.neg = neg
- return z
+ return z.SetInt64(1)
}
+ // y > 0
var mWords nat
if m != nil {
- mWords = m.abs
+ mWords = m.abs // m.abs may be nil for m == 0
}
z.abs = z.abs.expNN(x.abs, y.abs, mWords)
@@ -581,12 +600,12 @@ func (z *Int) Exp(x, y, m *Int) *Int {
return z
}
-// GCD sets z to the greatest common divisor of a and b, which must be
-// positive numbers, and returns z.
+// GCD sets z to the greatest common divisor of a and b, which both must
+// be > 0, and returns z.
// If x and y are not nil, GCD sets x and y such that z = a*x + b*y.
-// If either a or b is not positive, GCD sets z = x = y = 0.
+// If either a or b is <= 0, GCD sets z = x = y = 0.
func (z *Int) GCD(x, y, a, b *Int) *Int {
- if a.neg || b.neg {
+ if a.Sign() <= 0 || b.Sign() <= 0 {
z.SetInt64(0)
if x != nil {
x.SetInt64(0)
@@ -596,6 +615,9 @@ func (z *Int) GCD(x, y, a, b *Int) *Int {
}
return z
}
+ if x == nil && y == nil {
+ return z.binaryGCD(a, b)
+ }
A := new(Int).Set(a)
B := new(Int).Set(b)
@@ -640,6 +662,63 @@ func (z *Int) GCD(x, y, a, b *Int) *Int {
return z
}
+// binaryGCD sets z to the greatest common divisor of a and b, which both must
+// be > 0, and returns z.
+// See Knuth, The Art of Computer Programming, Vol. 2, Section 4.5.2, Algorithm B.
+func (z *Int) binaryGCD(a, b *Int) *Int {
+ u := z
+ v := new(Int)
+
+ // use one Euclidean iteration to ensure that u and v are approx. the same size
+ switch {
+ case len(a.abs) > len(b.abs):
+ u.Set(b)
+ v.Rem(a, b)
+ case len(a.abs) < len(b.abs):
+ u.Set(a)
+ v.Rem(b, a)
+ default:
+ u.Set(a)
+ v.Set(b)
+ }
+
+ // v might be 0 now
+ if len(v.abs) == 0 {
+ return u
+ }
+ // u > 0 && v > 0
+
+ // determine largest k such that u = u' << k, v = v' << k
+ k := u.abs.trailingZeroBits()
+ if vk := v.abs.trailingZeroBits(); vk < k {
+ k = vk
+ }
+ u.Rsh(u, k)
+ v.Rsh(v, k)
+
+ // determine t (we know that u > 0)
+ t := new(Int)
+ if u.abs[0]&1 != 0 {
+ // u is odd
+ t.Neg(v)
+ } else {
+ t.Set(u)
+ }
+
+ for len(t.abs) > 0 {
+ // reduce t
+ t.Rsh(t, t.abs.trailingZeroBits())
+ if t.neg {
+ v.Neg(t)
+ } else {
+ u.Set(t)
+ }
+ t.Sub(u, v)
+ }
+
+ return u.Lsh(u, k)
+}
+
// ProbablyPrime performs n Miller-Rabin tests to check whether x is prime.
// If it returns true, x is prime with probability 1 - 1/4^n.
// If it returns false, x is not prime.
@@ -697,6 +776,13 @@ func (z *Int) Rsh(x *Int, n uint) *Int {
// Bit returns the value of the i'th bit of x. That is, it
// returns (x>>i)&1. The bit index i must be >= 0.
func (x *Int) Bit(i int) uint {
+ if i == 0 {
+ // optimization for common case: odd/even test of x
+ if len(x.abs) > 0 {
+ return uint(x.abs[0] & 1) // bit 0 is same for -x
+ }
+ return 0
+ }
if i < 0 {
panic("negative bit index")
}
@@ -894,3 +980,19 @@ func (z *Int) GobDecode(buf []byte) error {
z.abs = z.abs.setBytes(buf[1:])
return nil
}
+
+// MarshalJSON implements the json.Marshaler interface.
+func (x *Int) MarshalJSON() ([]byte, error) {
+ // TODO(gri): get rid of the []byte/string conversions
+ return []byte(x.String()), nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (z *Int) UnmarshalJSON(x []byte) error {
+ // TODO(gri): get rid of the []byte/string conversions
+ _, ok := z.SetString(string(x), 0)
+ if !ok {
+ return fmt.Errorf("math/big: cannot unmarshal %s into a *big.Int", x)
+ }
+ return nil
+}
diff --git a/src/pkg/math/big/int_test.go b/src/pkg/math/big/int_test.go
index 9700a9b5a..6c981e775 100644
--- a/src/pkg/math/big/int_test.go
+++ b/src/pkg/math/big/int_test.go
@@ -8,6 +8,7 @@ import (
"bytes"
"encoding/gob"
"encoding/hex"
+ "encoding/json"
"fmt"
"math/rand"
"testing"
@@ -642,7 +643,7 @@ func TestSetBytes(t *testing.T) {
func checkBytes(b []byte) bool {
b2 := new(Int).SetBytes(b).Bytes()
- return bytes.Compare(b, b2) == 0
+ return bytes.Equal(b, b2)
}
func TestBytes(t *testing.T) {
@@ -766,8 +767,10 @@ var expTests = []struct {
x, y, m string
out string
}{
+ {"5", "-7", "", "1"},
+ {"-5", "-7", "", "1"},
{"5", "0", "", "1"},
- {"-5", "0", "", "-1"},
+ {"-5", "0", "", "1"},
{"5", "1", "", "5"},
{"-5", "1", "", "-5"},
{"-2", "3", "2", "0"},
@@ -778,6 +781,7 @@ var expTests = []struct {
{"0x8000000000000000", "3", "6719", "5447"},
{"0x8000000000000000", "1000", "6719", "1603"},
{"0x8000000000000000", "1000000", "6719", "3199"},
+ {"0x8000000000000000", "-1000000", "6719", "1"},
{
"2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347",
"298472983472983471903246121093472394872319615612417471234712061",
@@ -806,25 +810,33 @@ func TestExp(t *testing.T) {
continue
}
- z := y.Exp(x, y, m)
- if !isNormalized(z) {
- t.Errorf("#%d: %v is not normalized", i, *z)
+ z1 := new(Int).Exp(x, y, m)
+ if !isNormalized(z1) {
+ t.Errorf("#%d: %v is not normalized", i, *z1)
}
- if z.Cmp(out) != 0 {
- t.Errorf("#%d: got %s want %s", i, z, out)
+ if z1.Cmp(out) != 0 {
+ t.Errorf("#%d: got %s want %s", i, z1, out)
+ }
+
+ if m == nil {
+ // the result should be the same as for m == 0;
+ // specifically, there should be no div-zero panic
+ m = &Int{abs: nat{}} // m != nil && len(m.abs) == 0
+ z2 := new(Int).Exp(x, y, m)
+ if z2.Cmp(z1) != 0 {
+ t.Errorf("#%d: got %s want %s", i, z1, z2)
+ }
}
}
}
func checkGcd(aBytes, bBytes []byte) bool {
- a := new(Int).SetBytes(aBytes)
- b := new(Int).SetBytes(bBytes)
-
x := new(Int)
y := new(Int)
- d := new(Int)
+ a := new(Int).SetBytes(aBytes)
+ b := new(Int).SetBytes(bBytes)
- d.GCD(x, y, a, b)
+ d := new(Int).GCD(x, y, a, b)
x.Mul(x, a)
y.Mul(y, b)
x.Add(x, y)
@@ -833,32 +845,70 @@ func checkGcd(aBytes, bBytes []byte) bool {
}
var gcdTests = []struct {
- a, b int64
- d, x, y int64
+ d, x, y, a, b string
}{
- {120, 23, 1, -9, 47},
-}
-
-func TestGcd(t *testing.T) {
- for i, test := range gcdTests {
- a := NewInt(test.a)
- b := NewInt(test.b)
+ // a <= 0 || b <= 0
+ {"0", "0", "0", "0", "0"},
+ {"0", "0", "0", "0", "7"},
+ {"0", "0", "0", "11", "0"},
+ {"0", "0", "0", "-77", "35"},
+ {"0", "0", "0", "64515", "-24310"},
+ {"0", "0", "0", "-64515", "-24310"},
+
+ {"1", "-9", "47", "120", "23"},
+ {"7", "1", "-2", "77", "35"},
+ {"935", "-3", "8", "64515", "24310"},
+ {"935000000000000000", "-3", "8", "64515000000000000000", "24310000000000000000"},
+ {"1", "-221", "22059940471369027483332068679400581064239780177629666810348940098015901108344", "98920366548084643601728869055592650835572950932266967461790948584315647051443", "991"},
+
+ // test early exit (after one Euclidean iteration) in binaryGCD
+ {"1", "", "", "1", "98920366548084643601728869055592650835572950932266967461790948584315647051443"},
+}
+
+func testGcd(t *testing.T, d, x, y, a, b *Int) {
+ var X *Int
+ if x != nil {
+ X = new(Int)
+ }
+ var Y *Int
+ if y != nil {
+ Y = new(Int)
+ }
- x := new(Int)
- y := new(Int)
- d := new(Int)
+ D := new(Int).GCD(X, Y, a, b)
+ if D.Cmp(d) != 0 {
+ t.Errorf("GCD(%s, %s): got d = %s, want %s", a, b, D, d)
+ }
+ if x != nil && X.Cmp(x) != 0 {
+ t.Errorf("GCD(%s, %s): got x = %s, want %s", a, b, X, x)
+ }
+ if y != nil && Y.Cmp(y) != 0 {
+ t.Errorf("GCD(%s, %s): got y = %s, want %s", a, b, Y, y)
+ }
- expectedX := NewInt(test.x)
- expectedY := NewInt(test.y)
- expectedD := NewInt(test.d)
+ // binaryGCD requires a > 0 && b > 0
+ if a.Sign() <= 0 || b.Sign() <= 0 {
+ return
+ }
- d.GCD(x, y, a, b)
+ D.binaryGCD(a, b)
+ if D.Cmp(d) != 0 {
+ t.Errorf("binaryGcd(%s, %s): got d = %s, want %s", a, b, D, d)
+ }
+}
- if expectedX.Cmp(x) != 0 ||
- expectedY.Cmp(y) != 0 ||
- expectedD.Cmp(d) != 0 {
- t.Errorf("#%d got (%s %s %s) want (%s %s %s)", i, x, y, d, expectedX, expectedY, expectedD)
- }
+func TestGcd(t *testing.T) {
+ for _, test := range gcdTests {
+ d, _ := new(Int).SetString(test.d, 0)
+ x, _ := new(Int).SetString(test.x, 0)
+ y, _ := new(Int).SetString(test.y, 0)
+ a, _ := new(Int).SetString(test.a, 0)
+ b, _ := new(Int).SetString(test.b, 0)
+
+ testGcd(t, d, nil, nil, a, b)
+ testGcd(t, d, x, nil, a, b)
+ testGcd(t, d, nil, y, a, b)
+ testGcd(t, d, x, y, a, b)
}
quick.Check(checkGcd, nil)
@@ -1085,6 +1135,36 @@ func TestInt64(t *testing.T) {
}
}
+var uint64Tests = []uint64{
+ 0,
+ 1,
+ 4294967295,
+ 4294967296,
+ 8589934591,
+ 8589934592,
+ 9223372036854775807,
+ 9223372036854775808,
+ 18446744073709551615, // 1<<64 - 1
+}
+
+func TestUint64(t *testing.T) {
+ in := new(Int)
+ for i, testVal := range uint64Tests {
+ in.SetUint64(testVal)
+ out := in.Uint64()
+
+ if out != testVal {
+ t.Errorf("#%d got %d want %d", i, out, testVal)
+ }
+
+ str := fmt.Sprint(testVal)
+ strOut := in.String()
+ if strOut != str {
+ t.Errorf("#%d.String got %s want %s", i, strOut, str)
+ }
+ }
+}
+
var bitwiseTests = []struct {
x, y string
and, or, xor, andNot string
@@ -1368,8 +1448,12 @@ func TestModInverse(t *testing.T) {
}
}
-// used by TestIntGobEncoding and TestRatGobEncoding
-var gobEncodingTests = []string{
+var encodingTests = []string{
+ "-539345864568634858364538753846587364875430589374589",
+ "-678645873",
+ "-100",
+ "-2",
+ "-1",
"0",
"1",
"2",
@@ -1383,26 +1467,37 @@ func TestIntGobEncoding(t *testing.T) {
var medium bytes.Buffer
enc := gob.NewEncoder(&medium)
dec := gob.NewDecoder(&medium)
- for i, test := range gobEncodingTests {
- for j := 0; j < 2; j++ {
- medium.Reset() // empty buffer for each test case (in case of failures)
- stest := test
- if j != 0 {
- // negative numbers
- stest = "-" + test
- }
- var tx Int
- tx.SetString(stest, 10)
- if err := enc.Encode(&tx); err != nil {
- t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err)
- }
- var rx Int
- if err := dec.Decode(&rx); err != nil {
- t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err)
- }
- if rx.Cmp(&tx) != 0 {
- t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx)
- }
+ for _, test := range encodingTests {
+ medium.Reset() // empty buffer for each test case (in case of failures)
+ var tx Int
+ tx.SetString(test, 10)
+ if err := enc.Encode(&tx); err != nil {
+ t.Errorf("encoding of %s failed: %s", &tx, err)
+ }
+ var rx Int
+ if err := dec.Decode(&rx); err != nil {
+ t.Errorf("decoding of %s failed: %s", &tx, err)
+ }
+ if rx.Cmp(&tx) != 0 {
+ t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx)
+ }
+ }
+}
+
+func TestIntJSONEncoding(t *testing.T) {
+ for _, test := range encodingTests {
+ var tx Int
+ tx.SetString(test, 10)
+ b, err := json.Marshal(&tx)
+ if err != nil {
+ t.Errorf("marshaling of %s failed: %s", &tx, err)
+ }
+ var rx Int
+ if err := json.Unmarshal(b, &rx); err != nil {
+ t.Errorf("unmarshaling of %s failed: %s", &tx, err)
+ }
+ if rx.Cmp(&tx) != 0 {
+ t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx)
}
}
}
diff --git a/src/pkg/math/big/nat.go b/src/pkg/math/big/nat.go
index eaa6ff066..9d09f97b7 100644
--- a/src/pkg/math/big/nat.go
+++ b/src/pkg/math/big/nat.go
@@ -236,7 +236,7 @@ func karatsubaSub(z, x nat, n int) {
// Operands that are shorter than karatsubaThreshold are multiplied using
// "grade school" multiplication; for longer operands the Karatsuba algorithm
// is used.
-var karatsubaThreshold int = 32 // computed by calibrate.go
+var karatsubaThreshold int = 40 // computed by calibrate.go
// karatsuba multiplies x and y and leaves the result in z.
// Both x and y must have the same length n and n must be a
@@ -342,7 +342,7 @@ func alias(x, y nat) bool {
return cap(x) > 0 && cap(y) > 0 && &x[0:cap(x)][cap(x)-1] == &y[0:cap(y)][cap(y)-1]
}
-// addAt implements z += x*(1<<(_W*i)); z must be long enough.
+// addAt implements z += x<<(_W*i); z must be long enough.
// (we don't use nat.add because we need z to stay the same
// slice, and we don't need to normalize z after each addition)
func addAt(z, x nat, i int) {
@@ -396,7 +396,7 @@ func (z nat) mul(x, y nat) nat {
}
// use basic multiplication if the numbers are small
- if n < karatsubaThreshold || n < 2 {
+ if n < karatsubaThreshold {
z = z.make(m + n)
basicMul(z, x, y)
return z.norm()
@@ -405,8 +405,8 @@ func (z nat) mul(x, y nat) nat {
// determine Karatsuba length k such that
//
- // x = x1*b + x0
- // y = y1*b + y0 (and k <= len(y), which implies k <= len(x))
+ // x = xh*b + x0 (0 <= x0 < b)
+ // y = yh*b + y0 (0 <= y0 < b)
// b = 1<<(_W*k) ("base" of digits xi, yi)
//
k := karatsubaLen(n)
@@ -417,27 +417,44 @@ func (z nat) mul(x, y nat) nat {
y0 := y[0:k] // y0 is not normalized
z = z.make(max(6*k, m+n)) // enough space for karatsuba of x0*y0 and full result of x*y
karatsuba(z, x0, y0)
- z = z[0 : m+n] // z has final length but may be incomplete, upper portion is garbage
+ z = z[0 : m+n] // z has final length but may be incomplete
+ z[2*k:].clear() // upper portion of z is garbage (and 2*k <= m+n since k <= n <= m)
- // If x1 and/or y1 are not 0, add missing terms to z explicitly:
+ // If xh != 0 or yh != 0, add the missing terms to z. For
+ //
+ // xh = xi*b^i + ... + x2*b^2 + x1*b (0 <= xi < b)
+ // yh = y1*b (0 <= y1 < b)
+ //
+ // the missing terms are
+ //
+ // x0*y1*b and xi*y0*b^i, xi*y1*b^(i+1) for i > 0
//
- // m+n 2*k 0
- // z = [ ... | x0*y0 ]
- // + [ x1*y1 ]
- // + [ x1*y0 ]
- // + [ x0*y1 ]
+ // since all the yi for i > 1 are 0 by choice of k: If any of them
+ // were > 0, then yh >= b^2 and thus y >= b^2. Then k' = k*2 would
+ // be a larger valid threshold contradicting the assumption about k.
//
if k < n || m != n {
- x1 := x[k:] // x1 is normalized because x is
- y1 := y[k:] // y1 is normalized because y is
var t nat
- t = t.mul(x1, y1)
- copy(z[2*k:], t)
- z[2*k+len(t):].clear() // upper portion of z is garbage
- t = t.mul(x1, y0.norm())
- addAt(z, t, k)
- t = t.mul(x0.norm(), y1)
+
+ // add x0*y1*b
+ x0 := x0.norm()
+ y1 := y[k:] // y1 is normalized because y is
+ t = t.mul(x0, y1) // update t so we don't lose t's underlying array
addAt(z, t, k)
+
+ // add xi*y0<<i, xi*y1*b<<(i+k)
+ y0 := y0.norm()
+ for i := k; i < len(x); i += k {
+ xi := x[i:]
+ if len(xi) > k {
+ xi = xi[:k]
+ }
+ xi = xi.norm()
+ t = t.mul(xi, y0)
+ addAt(z, t, i)
+ t = t.mul(xi, y1)
+ addAt(z, t, i+k)
+ }
}
return z.norm()
@@ -493,14 +510,9 @@ func (z nat) div(z2, u, v nat) (q, r nat) {
}
if len(v) == 1 {
- var rprime Word
- q, rprime = z.divW(u, v[0])
- if rprime > 0 {
- r = z2.make(1)
- r[0] = rprime
- } else {
- r = z2.make(0)
- }
+ var r2 Word
+ q, r2 = z.divW(u, v[0])
+ r = z2.setWord(r2)
return
}
@@ -740,7 +752,7 @@ func (x nat) string(charset string) string {
// convert power of two and non power of two bases separately
if b == b&-b {
// shift is base-b digit size in bits
- shift := uint(trailingZeroBits(b)) // shift > 0 because b >= 2
+ shift := trailingZeroBits(b) // shift > 0 because b >= 2
mask := Word(1)<<shift - 1
w := x[0]
nbits := uint(_W) // number of unprocessed bits in w
@@ -814,18 +826,18 @@ func (x nat) string(charset string) string {
// Convert words of q to base b digits in s. If q is large, it is recursively "split in half"
// by nat/nat division using tabulated divisors. Otherwise, it is converted iteratively using
-// repeated nat/Word divison.
+// repeated nat/Word division.
//
-// The iterative method processes n Words by n divW() calls, each of which visits every Word in the
-// incrementally shortened q for a total of n + (n-1) + (n-2) ... + 2 + 1, or n(n+1)/2 divW()'s.
-// Recursive conversion divides q by its approximate square root, yielding two parts, each half
+// The iterative method processes n Words by n divW() calls, each of which visits every Word in the
+// incrementally shortened q for a total of n + (n-1) + (n-2) ... + 2 + 1, or n(n+1)/2 divW()'s.
+// Recursive conversion divides q by its approximate square root, yielding two parts, each half
// the size of q. Using the iterative method on both halves means 2 * (n/2)(n/2 + 1)/2 divW()'s
// plus the expensive long div(). Asymptotically, the ratio is favorable at 1/2 the divW()'s, and
-// is made better by splitting the subblocks recursively. Best is to split blocks until one more
-// split would take longer (because of the nat/nat div()) than the twice as many divW()'s of the
-// iterative approach. This threshold is represented by leafSize. Benchmarking of leafSize in the
-// range 2..64 shows that values of 8 and 16 work well, with a 4x speedup at medium lengths and
-// ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for
+// is made better by splitting the subblocks recursively. Best is to split blocks until one more
+// split would take longer (because of the nat/nat div()) than the twice as many divW()'s of the
+// iterative approach. This threshold is represented by leafSize. Benchmarking of leafSize in the
+// range 2..64 shows that values of 8 and 16 work well, with a 4x speedup at medium lengths and
+// ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for
// specific hardware.
//
func (q nat) convertWords(s []byte, charset string, b Word, ndigits int, bb Word, table []divisor) {
@@ -908,8 +920,10 @@ type divisor struct {
ndigits int // digit length of divisor in terms of output base digits
}
-var cacheBase10 [64]divisor // cached divisors for base 10
-var cacheLock sync.Mutex // protects cacheBase10
+var cacheBase10 struct {
+ sync.Mutex
+ table [64]divisor // cached divisors for base 10
+}
// expWW computes x**y
func (z nat) expWW(x, y Word) nat {
@@ -925,34 +939,28 @@ func divisors(m int, b Word, ndigits int, bb Word) []divisor {
// determine k where (bb**leafSize)**(2**k) >= sqrt(x)
k := 1
- for words := leafSize; words < m>>1 && k < len(cacheBase10); words <<= 1 {
+ for words := leafSize; words < m>>1 && k < len(cacheBase10.table); words <<= 1 {
k++
}
- // create new table of divisors or extend and reuse existing table as appropriate
- var table []divisor
- var cached bool
- switch b {
- case 10:
- table = cacheBase10[0:k] // reuse old table for this conversion
- cached = true
- default:
- table = make([]divisor, k) // new table for this conversion
+ // reuse and extend existing table of divisors or create new table as appropriate
+ var table []divisor // for b == 10, table overlaps with cacheBase10.table
+ if b == 10 {
+ cacheBase10.Lock()
+ table = cacheBase10.table[0:k] // reuse old table for this conversion
+ } else {
+ table = make([]divisor, k) // create new table for this conversion
}
// extend table
if table[k-1].ndigits == 0 {
- if cached {
- cacheLock.Lock() // begin critical section
- }
-
// add new entries as needed
var larger nat
for i := 0; i < k; i++ {
if table[i].ndigits == 0 {
if i == 0 {
- table[i].bbb = nat(nil).expWW(bb, Word(leafSize))
- table[i].ndigits = ndigits * leafSize
+ table[0].bbb = nat(nil).expWW(bb, Word(leafSize))
+ table[0].ndigits = ndigits * leafSize
} else {
table[i].bbb = nat(nil).mul(table[i-1].bbb, table[i-1].bbb)
table[i].ndigits = 2 * table[i-1].ndigits
@@ -968,10 +976,10 @@ func divisors(m int, b Word, ndigits int, bb Word) []divisor {
table[i].nbits = table[i].bbb.bitLen()
}
}
+ }
- if cached {
- cacheLock.Unlock() // end critical section
- }
+ if b == 10 {
+ cacheBase10.Unlock()
}
return table
@@ -993,10 +1001,9 @@ var deBruijn64Lookup = []byte{
54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
}
-// trailingZeroBits returns the number of consecutive zero bits on the right
-// side of the given Word.
-// See Knuth, volume 4, section 7.3.1
-func trailingZeroBits(x Word) int {
+// trailingZeroBits returns the number of consecutive least significant zero
+// bits of x.
+func trailingZeroBits(x Word) uint {
// x & -x leaves only the right-most bit set in the word. Let k be the
// index of that bit. Since only a single bit is set, the value is two
// to the power of k. Multiplying by a power of two is equivalent to
@@ -1005,18 +1012,33 @@ func trailingZeroBits(x Word) int {
// Therefore, if we have a left shifted version of this constant we can
// find by how many bits it was shifted by looking at which six bit
// substring ended up at the top of the word.
+ // (Knuth, volume 4, section 7.3.1)
switch _W {
case 32:
- return int(deBruijn32Lookup[((x&-x)*deBruijn32)>>27])
+ return uint(deBruijn32Lookup[((x&-x)*deBruijn32)>>27])
case 64:
- return int(deBruijn64Lookup[((x&-x)*(deBruijn64&_M))>>58])
+ return uint(deBruijn64Lookup[((x&-x)*(deBruijn64&_M))>>58])
default:
- panic("Unknown word size")
+ panic("unknown word size")
}
return 0
}
+// trailingZeroBits returns the number of consecutive least significant zero
+// bits of x.
+func (x nat) trailingZeroBits() uint {
+ if len(x) == 0 {
+ return 0
+ }
+ var i uint
+ for x[i] == 0 {
+ i++
+ }
+ // x[i] != 0
+ return i*_W + trailingZeroBits(x[i])
+}
+
// z = x << s
func (z nat) shl(x nat, s uint) nat {
m := len(x)
@@ -1169,29 +1191,6 @@ func (x nat) modW(d Word) (r Word) {
return divWVW(q, 0, x, d)
}
-// powersOfTwoDecompose finds q and k with x = q * 1<<k and q is odd, or q and k are 0.
-func (x nat) powersOfTwoDecompose() (q nat, k int) {
- if len(x) == 0 {
- return x, 0
- }
-
- // One of the words must be non-zero by definition,
- // so this loop will terminate with i < len(x), and
- // i is the number of 0 words.
- i := 0
- for x[i] == 0 {
- i++
- }
- n := trailingZeroBits(x[i]) // x[i] != 0
-
- q = make(nat, len(x)-i)
- shrVU(q, x[i:], uint(n))
-
- q = q.norm()
- k = i*_W + n
- return
-}
-
// random creates a random integer in [0..limit), using the space in z if
// possible. n is the bit length of limit.
func (z nat) random(rand *rand.Rand, limit nat, n int) nat {
@@ -1207,17 +1206,19 @@ func (z nat) random(rand *rand.Rand, limit nat, n int) nat {
mask := Word((1 << bitLengthOfMSW) - 1)
for {
- for i := range z {
- switch _W {
- case 32:
+ switch _W {
+ case 32:
+ for i := range z {
z[i] = Word(rand.Uint32())
- case 64:
+ }
+ case 64:
+ for i := range z {
z[i] = Word(rand.Uint32()) | Word(rand.Uint32())<<32
}
+ default:
+ panic("unknown word size")
}
-
z[len(limit)-1] &= mask
-
if z.cmp(limit) < 0 {
break
}
@@ -1226,11 +1227,11 @@ func (z nat) random(rand *rand.Rand, limit nat, n int) nat {
return z.norm()
}
-// If m != nil, expNN calculates x**y mod m. Otherwise it calculates x**y. It
-// reuses the storage of z if possible.
+// If m != 0 (i.e., len(m) != 0), expNN sets z to x**y mod m;
+// otherwise it sets z to x**y. The result is the value of z.
func (z nat) expNN(x, y, m nat) nat {
if alias(z, x) || alias(z, y) {
- // We cannot allow in place modification of x or y.
+ // We cannot allow in-place modification of x or y.
z = nil
}
@@ -1239,15 +1240,24 @@ func (z nat) expNN(x, y, m nat) nat {
z[0] = 1
return z
}
+ // y > 0
- if m != nil {
+ if len(m) != 0 {
// We likely end up being as long as the modulus.
z = z.make(len(m))
}
z = z.set(x)
- v := y[len(y)-1]
- // It's invalid for the most significant word to be zero, therefore we
- // will find a one bit.
+
+ // If the base is non-trivial and the exponent is large, we use
+ // 4-bit, windowed exponentiation. This involves precomputing 14 values
+ // (x^2...x^15) but then reduces the number of multiply-reduces by a
+ // third. Even for a 32-bit exponent, this reduces the number of
+ // operations.
+ if len(x) > 1 && len(y) > 1 && len(m) > 0 {
+ return z.expNNWindowed(x, y, m)
+ }
+
+ v := y[len(y)-1] // v > 0 because y is normalized and y > 0
shift := leadingZeros(v) + 1
v <<= shift
var q nat
@@ -1259,15 +1269,21 @@ func (z nat) expNN(x, y, m nat) nat {
// we also multiply by x, thus adding one to the power.
w := _W - int(shift)
+ // zz and r are used to avoid allocating in mul and div as
+ // otherwise the arguments would alias.
+ var zz, r nat
for j := 0; j < w; j++ {
- z = z.mul(z, z)
+ zz = zz.mul(z, z)
+ zz, z = z, zz
if v&mask != 0 {
- z = z.mul(z, x)
+ zz = zz.mul(z, x)
+ zz, z = z, zz
}
- if m != nil {
- q, z = q.div(z, z, m)
+ if len(m) != 0 {
+ zz, r = zz.div(r, z, m)
+ zz, r, q, z = q, z, zz, r
}
v <<= 1
@@ -1277,14 +1293,17 @@ func (z nat) expNN(x, y, m nat) nat {
v = y[i]
for j := 0; j < _W; j++ {
- z = z.mul(z, z)
+ zz = zz.mul(z, z)
+ zz, z = z, zz
if v&mask != 0 {
- z = z.mul(z, x)
+ zz = zz.mul(z, x)
+ zz, z = z, zz
}
- if m != nil {
- q, z = q.div(z, z, m)
+ if len(m) != 0 {
+ zz, r = zz.div(r, z, m)
+ zz, r, q, z = q, z, zz, r
}
v <<= 1
@@ -1294,6 +1313,69 @@ func (z nat) expNN(x, y, m nat) nat {
return z.norm()
}
+// expNNWindowed calculates x**y mod m using a fixed, 4-bit window.
+func (z nat) expNNWindowed(x, y, m nat) nat {
+ // zz and r are used to avoid allocating in mul and div as otherwise
+ // the arguments would alias.
+ var zz, r nat
+
+ const n = 4
+ // powers[i] contains x^i.
+ var powers [1 << n]nat
+ powers[0] = natOne
+ powers[1] = x
+ for i := 2; i < 1<<n; i += 2 {
+ p2, p, p1 := &powers[i/2], &powers[i], &powers[i+1]
+ *p = p.mul(*p2, *p2)
+ zz, r = zz.div(r, *p, m)
+ *p, r = r, *p
+ *p1 = p1.mul(*p, x)
+ zz, r = zz.div(r, *p1, m)
+ *p1, r = r, *p1
+ }
+
+ z = z.setWord(1)
+
+ for i := len(y) - 1; i >= 0; i-- {
+ yi := y[i]
+ for j := 0; j < _W; j += n {
+ if i != len(y)-1 || j != 0 {
+ // Unrolled loop for significant performance
+ // gain. Use go test -bench=".*" in crypto/rsa
+ // to check performance before making changes.
+ zz = zz.mul(z, z)
+ zz, z = z, zz
+ zz, r = zz.div(r, z, m)
+ z, r = r, z
+
+ zz = zz.mul(z, z)
+ zz, z = z, zz
+ zz, r = zz.div(r, z, m)
+ z, r = r, z
+
+ zz = zz.mul(z, z)
+ zz, z = z, zz
+ zz, r = zz.div(r, z, m)
+ z, r = r, z
+
+ zz = zz.mul(z, z)
+ zz, z = z, zz
+ zz, r = zz.div(r, z, m)
+ z, r = r, z
+ }
+
+ zz = zz.mul(z, powers[yi>>(_W-n)])
+ zz, z = z, zz
+ zz, r = zz.div(r, z, m)
+ z, r = r, z
+
+ yi <<= n
+ }
+ }
+
+ return z.norm()
+}
+
// probablyPrime performs reps Miller-Rabin tests to check whether n is prime.
// If it returns true, n is prime with probability 1 - 1/4^reps.
// If it returns false, n is not prime.
@@ -1343,8 +1425,9 @@ func (n nat) probablyPrime(reps int) bool {
}
nm1 := nat(nil).sub(n, natOne)
- // 1<<k * q = nm1;
- q, k := nm1.powersOfTwoDecompose()
+ // determine q, k such that nm1 = q << k
+ k := nm1.trailingZeroBits()
+ q := nat(nil).shr(nm1, k)
nm3 := nat(nil).sub(nm1, natTwo)
rand := rand.New(rand.NewSource(int64(n[0])))
@@ -1360,7 +1443,7 @@ NextRandom:
if y.cmp(natOne) == 0 || y.cmp(nm1) == 0 {
continue
}
- for j := 1; j < k; j++ {
+ for j := uint(1); j < k; j++ {
y = y.mul(y, y)
quotient, y = quotient.div(y, y, n)
if y.cmp(nm1) == 0 {
diff --git a/src/pkg/math/big/nat_test.go b/src/pkg/math/big/nat_test.go
index becde5d17..2dd7bf639 100644
--- a/src/pkg/math/big/nat_test.go
+++ b/src/pkg/math/big/nat_test.go
@@ -6,6 +6,7 @@ package big
import (
"io"
+ "runtime"
"strings"
"testing"
)
@@ -62,6 +63,36 @@ var prodNN = []argNN{
{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}},
+ // 3^100 * 3^28 = 3^128
+ {
+ natFromString("11790184577738583171520872861412518665678211592275841109096961"),
+ natFromString("515377520732011331036461129765621272702107522001"),
+ natFromString("22876792454961"),
+ },
+ // z = 111....1 (70000 digits)
+ // x = 10^(99*700) + ... + 10^1400 + 10^700 + 1
+ // y = 111....1 (700 digits, larger than Karatsuba threshold on 32-bit and 64-bit)
+ {
+ natFromString(strings.Repeat("1", 70000)),
+ natFromString("1" + strings.Repeat(strings.Repeat("0", 699)+"1", 99)),
+ natFromString(strings.Repeat("1", 700)),
+ },
+ // z = 111....1 (20000 digits)
+ // x = 10^10000 + 1
+ // y = 111....1 (10000 digits)
+ {
+ natFromString(strings.Repeat("1", 20000)),
+ natFromString("1" + strings.Repeat("0", 9999) + "1"),
+ natFromString(strings.Repeat("1", 10000)),
+ },
+}
+
+func natFromString(s string) nat {
+ x, _, err := nat(nil).scan(strings.NewReader(s), 0)
+ if err != nil {
+ panic(err)
+ }
+ return x
}
func TestSet(t *testing.T) {
@@ -135,26 +166,43 @@ func TestMulRangeN(t *testing.T) {
}
}
-var mulArg, mulTmp nat
-
-func init() {
- const n = 1000
- mulArg = make(nat, n)
- for i := 0; i < n; i++ {
- mulArg[i] = _M
+// allocBytes returns the number of bytes allocated by invoking f.
+func allocBytes(f func()) uint64 {
+ var stats runtime.MemStats
+ runtime.ReadMemStats(&stats)
+ t := stats.TotalAlloc
+ f()
+ runtime.ReadMemStats(&stats)
+ return stats.TotalAlloc - t
+}
+
+// TestMulUnbalanced tests that multiplying numbers of different lengths
+// does not cause deep recursion and in turn allocate too much memory.
+// Test case for issue 3807.
+func TestMulUnbalanced(t *testing.T) {
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
+ x := rndNat(50000)
+ y := rndNat(40)
+ allocSize := allocBytes(func() {
+ nat(nil).mul(x, y)
+ })
+ inputSize := uint64(len(x)+len(y)) * _S
+ if ratio := allocSize / uint64(inputSize); ratio > 10 {
+ t.Errorf("multiplication uses too much memory (%d > %d times the size of inputs)", allocSize, ratio)
}
}
-func benchmarkMulLoad() {
- for j := 1; j <= 10; j++ {
- x := mulArg[0 : j*100]
- mulTmp.mul(x, x)
- }
+func rndNat(n int) nat {
+ return nat(rndV(n)).norm()
}
func BenchmarkMul(b *testing.B) {
+ mulx := rndNat(1e4)
+ muly := rndNat(1e4)
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
- benchmarkMulLoad()
+ var z nat
+ z.mul(mulx, muly)
}
}
@@ -362,6 +410,20 @@ func TestScanPi(t *testing.T) {
}
}
+func TestScanPiParallel(t *testing.T) {
+ const n = 2
+ c := make(chan int)
+ for i := 0; i < n; i++ {
+ go func() {
+ TestScanPi(t)
+ c <- 0
+ }()
+ }
+ for i := 0; i < n; i++ {
+ <-c
+ }
+}
+
func BenchmarkScanPi(b *testing.B) {
for i := 0; i < b.N; i++ {
var x nat
@@ -369,6 +431,28 @@ func BenchmarkScanPi(b *testing.B) {
}
}
+func BenchmarkStringPiParallel(b *testing.B) {
+ var x nat
+ x, _, _ = x.scan(strings.NewReader(pi), 0)
+ if x.decimalString() != pi {
+ panic("benchmark incorrect: conversion failed")
+ }
+ n := runtime.GOMAXPROCS(0)
+ m := b.N / n // n*m <= b.N due to flooring, but the error is neglibible (n is not very large)
+ c := make(chan int, n)
+ for i := 0; i < n; i++ {
+ go func() {
+ for j := 0; j < m; j++ {
+ x.decimalString()
+ }
+ c <- 0
+ }()
+ }
+ for i := 0; i < n; i++ {
+ <-c
+ }
+}
+
func BenchmarkScan10Base2(b *testing.B) { ScanHelper(b, 2, 10, 10) }
func BenchmarkScan100Base2(b *testing.B) { ScanHelper(b, 2, 10, 100) }
func BenchmarkScan1000Base2(b *testing.B) { ScanHelper(b, 2, 10, 1000) }
@@ -463,13 +547,13 @@ func BenchmarkLeafSize13(b *testing.B) { LeafSizeHelper(b, 10, 13) }
func BenchmarkLeafSize14(b *testing.B) { LeafSizeHelper(b, 10, 14) }
func BenchmarkLeafSize15(b *testing.B) { LeafSizeHelper(b, 10, 15) }
func BenchmarkLeafSize16(b *testing.B) { LeafSizeHelper(b, 10, 16) }
-func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths
+func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths
func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) }
func LeafSizeHelper(b *testing.B, base Word, size int) {
b.StopTimer()
originalLeafSize := leafSize
- resetTable(cacheBase10[:])
+ resetTable(cacheBase10.table[:])
leafSize = size
b.StartTimer()
@@ -486,7 +570,7 @@ func LeafSizeHelper(b *testing.B, base Word, size int) {
}
b.StopTimer()
- resetTable(cacheBase10[:])
+ resetTable(cacheBase10.table[:])
leafSize = originalLeafSize
b.StartTimer()
}
@@ -616,14 +700,23 @@ func TestModW(t *testing.T) {
}
func TestTrailingZeroBits(t *testing.T) {
- var x Word
- x--
- for i := 0; i < _W; i++ {
- if trailingZeroBits(x) != i {
- t.Errorf("Failed at step %d: x: %x got: %d", i, x, trailingZeroBits(x))
+ x := Word(1)
+ for i := uint(0); i <= _W; i++ {
+ n := trailingZeroBits(x)
+ if n != i%_W {
+ t.Errorf("got trailingZeroBits(%#x) = %d; want %d", x, n, i%_W)
}
x <<= 1
}
+
+ y := nat(nil).set(natOne)
+ for i := uint(0); i <= 3*_W; i++ {
+ n := y.trailingZeroBits()
+ if n != i {
+ t.Errorf("got 0x%s.trailingZeroBits() = %d; want %d", y.string(lowercaseDigits[0:16]), n, i)
+ }
+ y = y.shl(y, 1)
+ }
}
var expNNTests = []struct {
diff --git a/src/pkg/math/big/rat.go b/src/pkg/math/big/rat.go
index 7bd83fc0f..3e6473d92 100644
--- a/src/pkg/math/big/rat.go
+++ b/src/pkg/math/big/rat.go
@@ -10,14 +10,17 @@ import (
"encoding/binary"
"errors"
"fmt"
+ "math"
"strings"
)
// A Rat represents a quotient a/b of arbitrary precision.
// The zero value for a Rat represents the value 0.
type Rat struct {
- a Int
- b nat // len(b) == 0 acts like b == 1
+ // To make zero values for Rat work w/o initialization,
+ // a zero value of b (len(b) == 0) acts like b == 1.
+ // a.neg determines the sign of the Rat, b.neg is ignored.
+ a, b Int
}
// NewRat creates a new Rat with numerator a and denominator b.
@@ -25,6 +28,156 @@ func NewRat(a, b int64) *Rat {
return new(Rat).SetFrac64(a, b)
}
+// SetFloat64 sets z to exactly f and returns z.
+// If f is not finite, SetFloat returns nil.
+func (z *Rat) SetFloat64(f float64) *Rat {
+ const expMask = 1<<11 - 1
+ bits := math.Float64bits(f)
+ mantissa := bits & (1<<52 - 1)
+ exp := int((bits >> 52) & expMask)
+ switch exp {
+ case expMask: // non-finite
+ return nil
+ case 0: // denormal
+ exp -= 1022
+ default: // normal
+ mantissa |= 1 << 52
+ exp -= 1023
+ }
+
+ shift := 52 - exp
+
+ // Optimisation (?): partially pre-normalise.
+ for mantissa&1 == 0 && shift > 0 {
+ mantissa >>= 1
+ shift--
+ }
+
+ z.a.SetUint64(mantissa)
+ z.a.neg = f < 0
+ z.b.Set(intOne)
+ if shift > 0 {
+ z.b.Lsh(&z.b, uint(shift))
+ } else {
+ z.a.Lsh(&z.a, uint(-shift))
+ }
+ return z.norm()
+}
+
+// isFinite reports whether f represents a finite rational value.
+// It is equivalent to !math.IsNan(f) && !math.IsInf(f, 0).
+func isFinite(f float64) bool {
+ return math.Abs(f) <= math.MaxFloat64
+}
+
+// low64 returns the least significant 64 bits of natural number z.
+func low64(z nat) uint64 {
+ if len(z) == 0 {
+ return 0
+ }
+ if _W == 32 && len(z) > 1 {
+ return uint64(z[1])<<32 | uint64(z[0])
+ }
+ return uint64(z[0])
+}
+
+// quotToFloat returns the non-negative IEEE 754 double-precision
+// value nearest to the quotient a/b, using round-to-even in halfway
+// cases. It does not mutate its arguments.
+// Preconditions: b is non-zero; a and b have no common factors.
+func quotToFloat(a, b nat) (f float64, exact bool) {
+ // TODO(adonovan): specialize common degenerate cases: 1.0, integers.
+ alen := a.bitLen()
+ if alen == 0 {
+ return 0, true
+ }
+ blen := b.bitLen()
+ if blen == 0 {
+ panic("division by zero")
+ }
+
+ // 1. Left-shift A or B such that quotient A/B is in [1<<53, 1<<55).
+ // (54 bits if A<B when they are left-aligned, 55 bits if A>=B.)
+ // This is 2 or 3 more than the float64 mantissa field width of 52:
+ // - the optional extra bit is shifted away in step 3 below.
+ // - the high-order 1 is omitted in float64 "normal" representation;
+ // - the low-order 1 will be used during rounding then discarded.
+ exp := alen - blen
+ var a2, b2 nat
+ a2 = a2.set(a)
+ b2 = b2.set(b)
+ if shift := 54 - exp; shift > 0 {
+ a2 = a2.shl(a2, uint(shift))
+ } else if shift < 0 {
+ b2 = b2.shl(b2, uint(-shift))
+ }
+
+ // 2. Compute quotient and remainder (q, r). NB: due to the
+ // extra shift, the low-order bit of q is logically the
+ // high-order bit of r.
+ var q nat
+ q, r := q.div(a2, a2, b2) // (recycle a2)
+ mantissa := low64(q)
+ haveRem := len(r) > 0 // mantissa&1 && !haveRem => remainder is exactly half
+
+ // 3. If quotient didn't fit in 54 bits, re-do division by b2<<1
+ // (in effect---we accomplish this incrementally).
+ if mantissa>>54 == 1 {
+ if mantissa&1 == 1 {
+ haveRem = true
+ }
+ mantissa >>= 1
+ exp++
+ }
+ if mantissa>>53 != 1 {
+ panic("expected exactly 54 bits of result")
+ }
+
+ // 4. Rounding.
+ if -1022-52 <= exp && exp <= -1022 {
+ // Denormal case; lose 'shift' bits of precision.
+ shift := uint64(-1022 - (exp - 1)) // [1..53)
+ lostbits := mantissa & (1<<shift - 1)
+ haveRem = haveRem || lostbits != 0
+ mantissa >>= shift
+ exp = -1023 + 2
+ }
+ // Round q using round-half-to-even.
+ exact = !haveRem
+ if mantissa&1 != 0 {
+ exact = false
+ if haveRem || mantissa&2 != 0 {
+ if mantissa++; mantissa >= 1<<54 {
+ // Complete rollover 11...1 => 100...0, so shift is safe
+ mantissa >>= 1
+ exp++
+ }
+ }
+ }
+ mantissa >>= 1 // discard rounding bit. Mantissa now scaled by 2^53.
+
+ f = math.Ldexp(float64(mantissa), exp-53)
+ if math.IsInf(f, 0) {
+ exact = false
+ }
+ return
+}
+
+// Float64 returns the nearest float64 value to z.
+// If z is exactly representable as a float64, Float64 returns exact=true.
+// If z is negative, so too is f, even if f==0.
+func (z *Rat) Float64() (f float64, exact bool) {
+ b := z.b.abs
+ if len(b) == 0 {
+ b = b.set(natOne) // materialize denominator
+ }
+ f, exact = quotToFloat(z.a.abs, b)
+ if z.a.neg {
+ f = -f
+ }
+ return
+}
+
// SetFrac sets z to a/b and returns z.
func (z *Rat) SetFrac(a, b *Int) *Rat {
z.a.neg = a.neg != b.neg
@@ -36,7 +189,7 @@ func (z *Rat) SetFrac(a, b *Int) *Rat {
babs = nat(nil).set(babs) // make a copy
}
z.a.abs = z.a.abs.set(a.abs)
- z.b = z.b.set(babs)
+ z.b.abs = z.b.abs.set(babs)
return z.norm()
}
@@ -50,21 +203,21 @@ func (z *Rat) SetFrac64(a, b int64) *Rat {
b = -b
z.a.neg = !z.a.neg
}
- z.b = z.b.setUint64(uint64(b))
+ z.b.abs = z.b.abs.setUint64(uint64(b))
return z.norm()
}
// SetInt sets z to x (by making a copy of x) and returns z.
func (z *Rat) SetInt(x *Int) *Rat {
z.a.Set(x)
- z.b = z.b.make(0)
+ z.b.abs = z.b.abs.make(0)
return z
}
// SetInt64 sets z to x and returns z.
func (z *Rat) SetInt64(x int64) *Rat {
z.a.SetInt64(x)
- z.b = z.b.make(0)
+ z.b.abs = z.b.abs.make(0)
return z
}
@@ -72,7 +225,7 @@ func (z *Rat) SetInt64(x int64) *Rat {
func (z *Rat) Set(x *Rat) *Rat {
if z != x {
z.a.Set(&x.a)
- z.b = z.b.set(x.b)
+ z.b.Set(&x.b)
}
return z
}
@@ -97,15 +250,15 @@ func (z *Rat) Inv(x *Rat) *Rat {
panic("division by zero")
}
z.Set(x)
- a := z.b
+ a := z.b.abs
if len(a) == 0 {
- a = a.setWord(1) // materialize numerator
+ a = a.set(natOne) // materialize numerator
}
b := z.a.abs
if b.cmp(natOne) == 0 {
b = b.make(0) // normalize denominator
}
- z.a.abs, z.b = a, b // sign doesn't change
+ z.a.abs, z.b.abs = a, b // sign doesn't change
return z
}
@@ -121,38 +274,26 @@ func (x *Rat) Sign() int {
// IsInt returns true if the denominator of x is 1.
func (x *Rat) IsInt() bool {
- return len(x.b) == 0 || x.b.cmp(natOne) == 0
+ return len(x.b.abs) == 0 || x.b.abs.cmp(natOne) == 0
}
// Num returns the numerator of x; it may be <= 0.
// The result is a reference to x's numerator; it
-// may change if a new value is assigned to x.
+// may change if a new value is assigned to x, and vice versa.
+// The sign of the numerator corresponds to the sign of x.
func (x *Rat) Num() *Int {
return &x.a
}
// Denom returns the denominator of x; it is always > 0.
// The result is a reference to x's denominator; it
-// may change if a new value is assigned to x.
+// may change if a new value is assigned to x, and vice versa.
func (x *Rat) Denom() *Int {
- if len(x.b) == 0 {
- return &Int{abs: nat{1}}
+ x.b.neg = false // the result is always >= 0
+ if len(x.b.abs) == 0 {
+ x.b.abs = x.b.abs.set(natOne) // materialize denominator
}
- return &Int{abs: x.b}
-}
-
-func gcd(x, y nat) nat {
- // Euclidean algorithm.
- var a, b nat
- a = a.set(x)
- b = b.set(y)
- for len(b) != 0 {
- var q, r nat
- _, r = q.div(r, a, b)
- a = b
- b = r
- }
- return a
+ return &x.b
}
func (z *Rat) norm() *Rat {
@@ -160,17 +301,25 @@ func (z *Rat) norm() *Rat {
case len(z.a.abs) == 0:
// z == 0 - normalize sign and denominator
z.a.neg = false
- z.b = z.b.make(0)
- case len(z.b) == 0:
+ z.b.abs = z.b.abs.make(0)
+ case len(z.b.abs) == 0:
// z is normalized int - nothing to do
- case z.b.cmp(natOne) == 0:
+ case z.b.abs.cmp(natOne) == 0:
// z is int - normalize denominator
- z.b = z.b.make(0)
+ z.b.abs = z.b.abs.make(0)
default:
- if f := gcd(z.a.abs, z.b); f.cmp(natOne) != 0 {
- z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f)
- z.b, _ = z.b.div(nil, z.b, f)
+ neg := z.a.neg
+ z.a.neg = false
+ z.b.neg = false
+ if f := NewInt(0).binaryGCD(&z.a, &z.b); f.Cmp(intOne) != 0 {
+ z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f.abs)
+ z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs)
+ if z.b.abs.cmp(natOne) == 0 {
+ // z is int - normalize denominator
+ z.b.abs = z.b.abs.make(0)
+ }
}
+ z.a.neg = neg
}
return z
}
@@ -207,31 +356,31 @@ func scaleDenom(x *Int, f nat) *Int {
// +1 if x > y
//
func (x *Rat) Cmp(y *Rat) int {
- return scaleDenom(&x.a, y.b).Cmp(scaleDenom(&y.a, x.b))
+ return scaleDenom(&x.a, y.b.abs).Cmp(scaleDenom(&y.a, x.b.abs))
}
// Add sets z to the sum x+y and returns z.
func (z *Rat) Add(x, y *Rat) *Rat {
- a1 := scaleDenom(&x.a, y.b)
- a2 := scaleDenom(&y.a, x.b)
+ a1 := scaleDenom(&x.a, y.b.abs)
+ a2 := scaleDenom(&y.a, x.b.abs)
z.a.Add(a1, a2)
- z.b = mulDenom(z.b, x.b, y.b)
+ z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs)
return z.norm()
}
// Sub sets z to the difference x-y and returns z.
func (z *Rat) Sub(x, y *Rat) *Rat {
- a1 := scaleDenom(&x.a, y.b)
- a2 := scaleDenom(&y.a, x.b)
+ a1 := scaleDenom(&x.a, y.b.abs)
+ a2 := scaleDenom(&y.a, x.b.abs)
z.a.Sub(a1, a2)
- z.b = mulDenom(z.b, x.b, y.b)
+ z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs)
return z.norm()
}
// Mul sets z to the product x*y and returns z.
func (z *Rat) Mul(x, y *Rat) *Rat {
z.a.Mul(&x.a, &y.a)
- z.b = mulDenom(z.b, x.b, y.b)
+ z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs)
return z.norm()
}
@@ -241,10 +390,10 @@ func (z *Rat) Quo(x, y *Rat) *Rat {
if len(y.a.abs) == 0 {
panic("division by zero")
}
- a := scaleDenom(&x.a, y.b)
- b := scaleDenom(&y.a, x.b)
+ a := scaleDenom(&x.a, y.b.abs)
+ b := scaleDenom(&y.a, x.b.abs)
z.a.abs = a.abs
- z.b = b.abs
+ z.b.abs = b.abs
z.a.neg = a.neg != b.neg
return z.norm()
}
@@ -286,7 +435,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
}
s = s[sep+1:]
var err error
- if z.b, _, err = z.b.scan(strings.NewReader(s), 10); err != nil {
+ if z.b.abs, _, err = z.b.abs.scan(strings.NewReader(s), 10); err != nil {
return nil, false
}
return z.norm(), true
@@ -317,11 +466,11 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
}
powTen := nat(nil).expNN(natTen, exp.abs, nil)
if exp.neg {
- z.b = powTen
+ z.b.abs = powTen
z.norm()
} else {
z.a.abs = z.a.abs.mul(z.a.abs, powTen)
- z.b = z.b.make(0)
+ z.b.abs = z.b.abs.make(0)
}
return z, true
@@ -330,8 +479,8 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
// String returns a string representation of z in the form "a/b" (even if b == 1).
func (x *Rat) String() string {
s := "/1"
- if len(x.b) != 0 {
- s = "/" + x.b.decimalString()
+ if len(x.b.abs) != 0 {
+ s = "/" + x.b.abs.decimalString()
}
return x.a.String() + s
}
@@ -355,9 +504,9 @@ func (x *Rat) FloatString(prec int) string {
}
return s
}
- // x.b != 0
+ // x.b.abs != 0
- q, r := nat(nil).div(nat(nil), x.a.abs, x.b)
+ q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs)
p := natOne
if prec > 0 {
@@ -365,11 +514,11 @@ func (x *Rat) FloatString(prec int) string {
}
r = r.mul(r, p)
- r, r2 := r.div(nat(nil), r, x.b)
+ r, r2 := r.div(nat(nil), r, x.b.abs)
// see if we need to round up
r2 = r2.add(r2, r2)
- if x.b.cmp(r2) <= 0 {
+ if x.b.abs.cmp(r2) <= 0 {
r = r.add(r, natOne)
if r.cmp(p) >= 0 {
q = nat(nil).add(q, natOne)
@@ -396,8 +545,8 @@ const ratGobVersion byte = 1
// GobEncode implements the gob.GobEncoder interface.
func (x *Rat) GobEncode() ([]byte, error) {
- buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b))*_S) // extra bytes for version and sign bit (1), and numerator length (4)
- i := x.b.bytes(buf)
+ buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b.abs))*_S) // extra bytes for version and sign bit (1), and numerator length (4)
+ i := x.b.abs.bytes(buf)
j := x.a.abs.bytes(buf[0:i])
n := i - j
if int(uint32(n)) != n {
@@ -427,6 +576,6 @@ func (z *Rat) GobDecode(buf []byte) error {
i := j + binary.BigEndian.Uint32(buf[j-4:j])
z.a.neg = b&1 != 0
z.a.abs = z.a.abs.setBytes(buf[j:i])
- z.b = z.b.setBytes(buf[i:])
+ z.b.abs = z.b.abs.setBytes(buf[i:])
return nil
}
diff --git a/src/pkg/math/big/rat_test.go b/src/pkg/math/big/rat_test.go
index f7f31ae1a..462dfb723 100644
--- a/src/pkg/math/big/rat_test.go
+++ b/src/pkg/math/big/rat_test.go
@@ -8,6 +8,9 @@ import (
"bytes"
"encoding/gob"
"fmt"
+ "math"
+ "strconv"
+ "strings"
"testing"
)
@@ -387,30 +390,19 @@ func TestRatGobEncoding(t *testing.T) {
var medium bytes.Buffer
enc := gob.NewEncoder(&medium)
dec := gob.NewDecoder(&medium)
- for i, test := range gobEncodingTests {
- for j := 0; j < 4; j++ {
- medium.Reset() // empty buffer for each test case (in case of failures)
- stest := test
- if j&1 != 0 {
- // negative numbers
- stest = "-" + test
- }
- if j%2 != 0 {
- // fractions
- stest = stest + "." + test
- }
- var tx Rat
- tx.SetString(stest)
- if err := enc.Encode(&tx); err != nil {
- t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err)
- }
- var rx Rat
- if err := dec.Decode(&rx); err != nil {
- t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err)
- }
- if rx.Cmp(&tx) != 0 {
- t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx)
- }
+ for _, test := range encodingTests {
+ medium.Reset() // empty buffer for each test case (in case of failures)
+ var tx Rat
+ tx.SetString(test + ".14159265")
+ if err := enc.Encode(&tx); err != nil {
+ t.Errorf("encoding of %s failed: %s", &tx, err)
+ }
+ var rx Rat
+ if err := dec.Decode(&rx); err != nil {
+ t.Errorf("decoding of %s failed: %s", &tx, err)
+ }
+ if rx.Cmp(&tx) != 0 {
+ t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx)
}
}
}
@@ -454,3 +446,462 @@ func TestIssue2379(t *testing.T) {
t.Errorf("5) got %s want %s", x, q)
}
}
+
+func TestIssue3521(t *testing.T) {
+ a := new(Int)
+ b := new(Int)
+ a.SetString("64375784358435883458348587", 0)
+ b.SetString("4789759874531", 0)
+
+ // 0) a raw zero value has 1 as denominator
+ zero := new(Rat)
+ one := NewInt(1)
+ if zero.Denom().Cmp(one) != 0 {
+ t.Errorf("0) got %s want %s", zero.Denom(), one)
+ }
+
+ // 1a) a zero value remains zero independent of denominator
+ x := new(Rat)
+ x.Denom().Set(new(Int).Neg(b))
+ if x.Cmp(zero) != 0 {
+ t.Errorf("1a) got %s want %s", x, zero)
+ }
+
+ // 1b) a zero value may have a denominator != 0 and != 1
+ x.Num().Set(a)
+ qab := new(Rat).SetFrac(a, b)
+ if x.Cmp(qab) != 0 {
+ t.Errorf("1b) got %s want %s", x, qab)
+ }
+
+ // 2a) an integral value becomes a fraction depending on denominator
+ x.SetFrac64(10, 2)
+ x.Denom().SetInt64(3)
+ q53 := NewRat(5, 3)
+ if x.Cmp(q53) != 0 {
+ t.Errorf("2a) got %s want %s", x, q53)
+ }
+
+ // 2b) an integral value becomes a fraction depending on denominator
+ x = NewRat(10, 2)
+ x.Denom().SetInt64(3)
+ if x.Cmp(q53) != 0 {
+ t.Errorf("2b) got %s want %s", x, q53)
+ }
+
+ // 3) changing the numerator/denominator of a Rat changes the Rat
+ x.SetFrac(a, b)
+ a = x.Num()
+ b = x.Denom()
+ a.SetInt64(5)
+ b.SetInt64(3)
+ if x.Cmp(q53) != 0 {
+ t.Errorf("3) got %s want %s", x, q53)
+ }
+}
+
+// Test inputs to Rat.SetString. The prefix "long:" causes the test
+// to be skipped in --test.short mode. (The threshold is about 500us.)
+var float64inputs = []string{
+ //
+ // Constants plundered from strconv/testfp.txt.
+ //
+
+ // Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP
+ "5e+125",
+ "69e+267",
+ "999e-026",
+ "7861e-034",
+ "75569e-254",
+ "928609e-261",
+ "9210917e+080",
+ "84863171e+114",
+ "653777767e+273",
+ "5232604057e-298",
+ "27235667517e-109",
+ "653532977297e-123",
+ "3142213164987e-294",
+ "46202199371337e-072",
+ "231010996856685e-073",
+ "9324754620109615e+212",
+ "78459735791271921e+049",
+ "272104041512242479e+200",
+ "6802601037806061975e+198",
+ "20505426358836677347e-221",
+ "836168422905420598437e-234",
+ "4891559871276714924261e+222",
+
+ // Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP
+ "9e-265",
+ "85e-037",
+ "623e+100",
+ "3571e+263",
+ "81661e+153",
+ "920657e-023",
+ "4603285e-024",
+ "87575437e-309",
+ "245540327e+122",
+ "6138508175e+120",
+ "83356057653e+193",
+ "619534293513e+124",
+ "2335141086879e+218",
+ "36167929443327e-159",
+ "609610927149051e-255",
+ "3743626360493413e-165",
+ "94080055902682397e-242",
+ "899810892172646163e+283",
+ "7120190517612959703e+120",
+ "25188282901709339043e-252",
+ "308984926168550152811e-052",
+ "6372891218502368041059e+064",
+
+ // Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP
+ "5e-20",
+ "67e+14",
+ "985e+15",
+ "7693e-42",
+ "55895e-16",
+ "996622e-44",
+ "7038531e-32",
+ "60419369e-46",
+ "702990899e-20",
+ "6930161142e-48",
+ "25933168707e+13",
+ "596428896559e+20",
+
+ // Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP
+ "3e-23",
+ "57e+18",
+ "789e-35",
+ "2539e-18",
+ "76173e+28",
+ "887745e-11",
+ "5382571e-37",
+ "82381273e-35",
+ "750486563e-38",
+ "3752432815e-39",
+ "75224575729e-45",
+ "459926601011e+15",
+
+ //
+ // Constants plundered from strconv/atof_test.go.
+ //
+
+ "0",
+ "1",
+ "+1",
+ "1e23",
+ "1E23",
+ "100000000000000000000000",
+ "1e-100",
+ "123456700",
+ "99999999999999974834176",
+ "100000000000000000000001",
+ "100000000000000008388608",
+ "100000000000000016777215",
+ "100000000000000016777216",
+ "-1",
+ "-0.1",
+ "-0", // NB: exception made for this input
+ "1e-20",
+ "625e-3",
+
+ // largest float64
+ "1.7976931348623157e308",
+ "-1.7976931348623157e308",
+ // next float64 - too large
+ "1.7976931348623159e308",
+ "-1.7976931348623159e308",
+ // the border is ...158079
+ // borderline - okay
+ "1.7976931348623158e308",
+ "-1.7976931348623158e308",
+ // borderline - too large
+ "1.797693134862315808e308",
+ "-1.797693134862315808e308",
+
+ // a little too large
+ "1e308",
+ "2e308",
+ "1e309",
+
+ // way too large
+ "1e310",
+ "-1e310",
+ "1e400",
+ "-1e400",
+ "long:1e400000",
+ "long:-1e400000",
+
+ // denormalized
+ "1e-305",
+ "1e-306",
+ "1e-307",
+ "1e-308",
+ "1e-309",
+ "1e-310",
+ "1e-322",
+ // smallest denormal
+ "5e-324",
+ "4e-324",
+ "3e-324",
+ // too small
+ "2e-324",
+ // way too small
+ "1e-350",
+ "long:1e-400000",
+ // way too small, negative
+ "-1e-350",
+ "long:-1e-400000",
+
+ // try to overflow exponent
+ // [Disabled: too slow and memory-hungry with rationals.]
+ // "1e-4294967296",
+ // "1e+4294967296",
+ // "1e-18446744073709551616",
+ // "1e+18446744073709551616",
+
+ // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
+ "2.2250738585072012e-308",
+ // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
+
+ "2.2250738585072011e-308",
+
+ // A very large number (initially wrongly parsed by the fast algorithm).
+ "4.630813248087435e+307",
+
+ // A different kind of very large number.
+ "22.222222222222222",
+ "long:2." + strings.Repeat("2", 4000) + "e+1",
+
+ // Exactly halfway between 1 and math.Nextafter(1, 2).
+ // Round to even (down).
+ "1.00000000000000011102230246251565404236316680908203125",
+ // Slightly lower; still round down.
+ "1.00000000000000011102230246251565404236316680908203124",
+ // Slightly higher; round up.
+ "1.00000000000000011102230246251565404236316680908203126",
+ // Slightly higher, but you have to read all the way to the end.
+ "long:1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1",
+
+ // Smallest denormal, 2^(-1022-52)
+ "4.940656458412465441765687928682213723651e-324",
+ // Half of smallest denormal, 2^(-1022-53)
+ "2.470328229206232720882843964341106861825e-324",
+ // A little more than the exact half of smallest denormal
+ // 2^-1075 + 2^-1100. (Rounds to 1p-1074.)
+ "2.470328302827751011111470718709768633275e-324",
+ // The exact halfway between smallest normal and largest denormal:
+ // 2^-1022 - 2^-1075. (Rounds to 2^-1022.)
+ "2.225073858507201136057409796709131975935e-308",
+
+ "1152921504606846975", // 1<<60 - 1
+ "-1152921504606846975", // -(1<<60 - 1)
+ "1152921504606846977", // 1<<60 + 1
+ "-1152921504606846977", // -(1<<60 + 1)
+
+ "1/3",
+}
+
+func TestFloat64SpecialCases(t *testing.T) {
+ for _, input := range float64inputs {
+ if strings.HasPrefix(input, "long:") {
+ if testing.Short() {
+ continue
+ }
+ input = input[len("long:"):]
+ }
+
+ r, ok := new(Rat).SetString(input)
+ if !ok {
+ t.Errorf("Rat.SetString(%q) failed", input)
+ continue
+ }
+ f, exact := r.Float64()
+
+ // 1. Check string -> Rat -> float64 conversions are
+ // consistent with strconv.ParseFloat.
+ // Skip this check if the input uses "a/b" rational syntax.
+ if !strings.Contains(input, "/") {
+ e, _ := strconv.ParseFloat(input, 64)
+
+ // Careful: negative Rats too small for
+ // float64 become -0, but Rat obviously cannot
+ // preserve the sign from SetString("-0").
+ switch {
+ case math.Float64bits(e) == math.Float64bits(f):
+ // Ok: bitwise equal.
+ case f == 0 && r.Num().BitLen() == 0:
+ // Ok: Rat(0) is equivalent to both +/- float64(0).
+ default:
+ t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta=%g", input, e, e, f, f, f-e)
+ }
+ }
+
+ if !isFinite(f) {
+ continue
+ }
+
+ // 2. Check f is best approximation to r.
+ if !checkIsBestApprox(t, f, r) {
+ // Append context information.
+ t.Errorf("(input was %q)", input)
+ }
+
+ // 3. Check f->R->f roundtrip is non-lossy.
+ checkNonLossyRoundtrip(t, f)
+
+ // 4. Check exactness using slow algorithm.
+ if wasExact := new(Rat).SetFloat64(f).Cmp(r) == 0; wasExact != exact {
+ t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact)
+ }
+ }
+}
+
+func TestFloat64Distribution(t *testing.T) {
+ // Generate a distribution of (sign, mantissa, exp) values
+ // broader than the float64 range, and check Rat.Float64()
+ // always picks the closest float64 approximation.
+ var add = []int64{
+ 0,
+ 1,
+ 3,
+ 5,
+ 7,
+ 9,
+ 11,
+ }
+ var winc, einc = uint64(1), int(1) // soak test (~75s on x86-64)
+ if testing.Short() {
+ winc, einc = 10, 500 // quick test (~12ms on x86-64)
+ }
+
+ for _, sign := range "+-" {
+ for _, a := range add {
+ for wid := uint64(0); wid < 60; wid += winc {
+ b := int64(1<<wid + a)
+ if sign == '-' {
+ b = -b
+ }
+ for exp := -1100; exp < 1100; exp += einc {
+ num, den := NewInt(b), NewInt(1)
+ if exp > 0 {
+ num.Lsh(num, uint(exp))
+ } else {
+ den.Lsh(den, uint(-exp))
+ }
+ r := new(Rat).SetFrac(num, den)
+ f, _ := r.Float64()
+
+ if !checkIsBestApprox(t, f, r) {
+ // Append context information.
+ t.Errorf("(input was mantissa %#x, exp %d; f=%g (%b); f~%g; r=%v)",
+ b, exp, f, f, math.Ldexp(float64(b), exp), r)
+ }
+
+ checkNonLossyRoundtrip(t, f)
+ }
+ }
+ }
+ }
+}
+
+// TestFloat64NonFinite checks that SetFloat64 of a non-finite value
+// returns nil.
+func TestSetFloat64NonFinite(t *testing.T) {
+ for _, f := range []float64{math.NaN(), math.Inf(+1), math.Inf(-1)} {
+ var r Rat
+ if r2 := r.SetFloat64(f); r2 != nil {
+ t.Errorf("SetFloat64(%g) was %v, want nil", f, r2)
+ }
+ }
+}
+
+// checkNonLossyRoundtrip checks that a float->Rat->float roundtrip is
+// non-lossy for finite f.
+func checkNonLossyRoundtrip(t *testing.T, f float64) {
+ if !isFinite(f) {
+ return
+ }
+ r := new(Rat).SetFloat64(f)
+ if r == nil {
+ t.Errorf("Rat.SetFloat64(%g (%b)) == nil", f, f)
+ return
+ }
+ f2, exact := r.Float64()
+ if f != f2 || !exact {
+ t.Errorf("Rat.SetFloat64(%g).Float64() = %g (%b), %v, want %g (%b), %v; delta=%b",
+ f, f2, f2, exact, f, f, true, f2-f)
+ }
+}
+
+// delta returns the absolute difference between r and f.
+func delta(r *Rat, f float64) *Rat {
+ d := new(Rat).Sub(r, new(Rat).SetFloat64(f))
+ return d.Abs(d)
+}
+
+// checkIsBestApprox checks that f is the best possible float64
+// approximation of r.
+// Returns true on success.
+func checkIsBestApprox(t *testing.T, f float64, r *Rat) bool {
+ if math.Abs(f) >= math.MaxFloat64 {
+ // Cannot check +Inf, -Inf, nor the float next to them (MaxFloat64).
+ // But we have tests for these special cases.
+ return true
+ }
+
+ // r must be strictly between f0 and f1, the floats bracketing f.
+ f0 := math.Nextafter(f, math.Inf(-1))
+ f1 := math.Nextafter(f, math.Inf(+1))
+
+ // For f to be correct, r must be closer to f than to f0 or f1.
+ df := delta(r, f)
+ df0 := delta(r, f0)
+ df1 := delta(r, f1)
+ if df.Cmp(df0) > 0 {
+ t.Errorf("Rat(%v).Float64() = %g (%b), but previous float64 %g (%b) is closer", r, f, f, f0, f0)
+ return false
+ }
+ if df.Cmp(df1) > 0 {
+ t.Errorf("Rat(%v).Float64() = %g (%b), but next float64 %g (%b) is closer", r, f, f, f1, f1)
+ return false
+ }
+ if df.Cmp(df0) == 0 && !isEven(f) {
+ t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f0, f0)
+ return false
+ }
+ if df.Cmp(df1) == 0 && !isEven(f) {
+ t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f1, f1)
+ return false
+ }
+ return true
+}
+
+func isEven(f float64) bool { return math.Float64bits(f)&1 == 0 }
+
+func TestIsFinite(t *testing.T) {
+ finites := []float64{
+ 1.0 / 3,
+ 4891559871276714924261e+222,
+ math.MaxFloat64,
+ math.SmallestNonzeroFloat64,
+ -math.MaxFloat64,
+ -math.SmallestNonzeroFloat64,
+ }
+ for _, f := range finites {
+ if !isFinite(f) {
+ t.Errorf("!IsFinite(%g (%b))", f, f)
+ }
+ }
+ nonfinites := []float64{
+ math.NaN(),
+ math.Inf(-1),
+ math.Inf(+1),
+ }
+ for _, f := range nonfinites {
+ if isFinite(f) {
+ t.Errorf("IsFinite(%g, (%b))", f, f)
+ }
+ }
+}
diff --git a/src/pkg/math/bits.go b/src/pkg/math/bits.go
index 1cf60ce7d..0df0b1cc9 100644
--- a/src/pkg/math/bits.go
+++ b/src/pkg/math/bits.go
@@ -5,7 +5,7 @@
package math
const (
- uvnan = 0x7FF0000000000001
+ uvnan = 0x7FF8000000000001
uvinf = 0x7FF0000000000000
uvneginf = 0xFFF0000000000000
mask = 0x7FF
diff --git a/src/pkg/math/cbrt.go b/src/pkg/math/cbrt.go
index 8c43f0afb..272e30923 100644
--- a/src/pkg/math/cbrt.go
+++ b/src/pkg/math/cbrt.go
@@ -12,7 +12,7 @@ package math
(http://www.jstor.org/stable/2006387?seq=9, accessed 11-Feb-2010)
*/
-// Cbrt returns the cube root of its argument.
+// Cbrt returns the cube root of x.
//
// Special cases are:
// Cbrt(±0) = ±0
diff --git a/src/pkg/math/copysign.go b/src/pkg/math/copysign.go
index ee65456a1..719c64b9e 100644
--- a/src/pkg/math/copysign.go
+++ b/src/pkg/math/copysign.go
@@ -4,7 +4,7 @@
package math
-// Copysign(x, y) returns a value with the magnitude
+// Copysign returns a value with the magnitude
// of x and the sign of y.
func Copysign(x, y float64) float64 {
const sign = 1 << 63
diff --git a/src/pkg/math/dim_amd64.s b/src/pkg/math/dim_amd64.s
index c867db553..a1505ce44 100644
--- a/src/pkg/math/dim_amd64.s
+++ b/src/pkg/math/dim_amd64.s
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
#define PosInf 0x7FF0000000000000
-#define NaN 0x7FF0000000000001
+#define NaN 0x7FF8000000000001
#define NegInf 0xFFF0000000000000
// func Dim(x, y float64) float64
diff --git a/src/pkg/math/erf.go b/src/pkg/math/erf.go
index c6f32bdbe..4cd80f80c 100644
--- a/src/pkg/math/erf.go
+++ b/src/pkg/math/erf.go
@@ -179,7 +179,7 @@ const (
sb7 = -2.24409524465858183362e+01 // 0xC03670E242712D62
)
-// Erf(x) returns the error function of x.
+// Erf returns the error function of x.
//
// Special cases are:
// Erf(+Inf) = 1
@@ -256,7 +256,7 @@ func Erf(x float64) float64 {
return 1 - r/x
}
-// Erfc(x) returns the complementary error function of x.
+// Erfc returns the complementary error function of x.
//
// Special cases are:
// Erfc(+Inf) = 0
diff --git a/src/pkg/math/floor_amd64.s b/src/pkg/math/floor_amd64.s
index 9fc49a56f..e72cc3cf9 100644
--- a/src/pkg/math/floor_amd64.s
+++ b/src/pkg/math/floor_amd64.s
@@ -1,12 +1,74 @@
-// Copyright 2011 The Go Authors. All rights reserved.
+// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#define Big 0x4330000000000000 // 2**52
+
+// func Floor(x float64) float64
TEXT ·Floor(SB),7,$0
- JMP ·floor(SB)
+ MOVQ x+0(FP), AX
+ MOVQ $~(1<<63), DX // sign bit mask
+ ANDQ AX,DX // DX = |x|
+ SUBQ $1,DX
+ MOVQ $(Big - 1), CX // if |x| >= 2**52-1 or IsNaN(x) or |x| == 0, return x
+ CMPQ DX,CX
+ JAE isBig_floor
+ MOVQ AX, X0 // X0 = x
+ CVTTSD2SQ X0, AX
+ CVTSQ2SD AX, X1 // X1 = float(int(x))
+ CMPSD X1, X0, 1 // compare LT; X0 = 0xffffffffffffffff or 0
+ MOVSD $(-1.0), X2
+ ANDPD X2, X0 // if x < float(int(x)) {X0 = -1} else {X0 = 0}
+ ADDSD X1, X0
+ MOVSD X0, r+8(FP)
+ RET
+isBig_floor:
+ MOVQ AX, r+8(FP) // return x
+ RET
+// func Ceil(x float64) float64
TEXT ·Ceil(SB),7,$0
- JMP ·ceil(SB)
+ MOVQ x+0(FP), AX
+ MOVQ $~(1<<63), DX // sign bit mask
+ MOVQ AX, BX // BX = copy of x
+ ANDQ DX, BX // BX = |x|
+ MOVQ $Big, CX // if |x| >= 2**52 or IsNaN(x), return x
+ CMPQ BX, CX
+ JAE isBig_ceil
+ MOVQ AX, X0 // X0 = x
+ MOVQ DX, X2 // X2 = sign bit mask
+ CVTTSD2SQ X0, AX
+ ANDNPD X0, X2 // X2 = sign
+ CVTSQ2SD AX, X1 // X1 = float(int(x))
+ CMPSD X1, X0, 2 // compare LE; X0 = 0xffffffffffffffff or 0
+ ORPD X2, X1 // if X1 = 0.0, incorporate sign
+ MOVSD $1.0, X3
+ ANDNPD X3, X0
+ ORPD X2, X0 // if float(int(x)) <= x {X0 = 1} else {X0 = -0}
+ ADDSD X1, X0
+ MOVSD X0, r+8(FP)
+ RET
+isBig_ceil:
+ MOVQ AX, r+8(FP)
+ RET
+// func Trunc(x float64) float64
TEXT ·Trunc(SB),7,$0
- JMP ·trunc(SB)
+ MOVQ x+0(FP), AX
+ MOVQ $~(1<<63), DX // sign bit mask
+ MOVQ AX, BX // BX = copy of x
+ ANDQ DX, BX // BX = |x|
+ MOVQ $Big, CX // if |x| >= 2**52 or IsNaN(x), return x
+ CMPQ BX, CX
+ JAE isBig_trunc
+ MOVQ AX, X0
+ MOVQ DX, X2 // X2 = sign bit mask
+ CVTTSD2SQ X0, AX
+ ANDNPD X0, X2 // X2 = sign
+ CVTSQ2SD AX, X0 // X0 = float(int(x))
+ ORPD X2, X0 // if X0 = 0.0, incorporate sign
+ MOVSD X0, r+8(FP)
+ RET
+isBig_trunc:
+ MOVQ AX, r+8(FP) // return x
+ RET
diff --git a/src/pkg/math/frexp_386.s b/src/pkg/math/frexp_386.s
index 177c4b97b..95e50de02 100644
--- a/src/pkg/math/frexp_386.s
+++ b/src/pkg/math/frexp_386.s
@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// func Frexp(x float64) (f float64, e int)
+// func Frexp(f float64) (frac float64, exp int)
TEXT ·Frexp(SB),7,$0
- FMOVD x+0(FP), F0 // F0=x
+ FMOVD f+0(FP), F0 // F0=f
FXAM
FSTSW AX
SAHF
@@ -12,12 +12,12 @@ TEXT ·Frexp(SB),7,$0
JCS nan_zero_inf
FXTRACT // F0=f (0<=f<1), F1=e
FMULD $(0.5), F0 // F0=f (0.5<=f<1), F1=e
- FMOVDP F0, f+8(FP) // F0=e
+ FMOVDP F0, frac+8(FP) // F0=e
FLD1 // F0=1, F1=e
FADDDP F0, F1 // F0=e+1
- FMOVLP F0, e+16(FP) // (int=int32)
+ FMOVLP F0, exp+16(FP) // (int=int32)
RET
nan_zero_inf:
- FMOVDP F0, f+8(FP) // F0=e
- MOVL $0, e+16(FP) // e=0
+ FMOVDP F0, frac+8(FP) // F0=e
+ MOVL $0, exp+16(FP) // exp=0
RET
diff --git a/src/pkg/math/gamma.go b/src/pkg/math/gamma.go
index 7c6f421ba..164f54f33 100644
--- a/src/pkg/math/gamma.go
+++ b/src/pkg/math/gamma.go
@@ -110,19 +110,26 @@ func stirling(x float64) float64 {
return y
}
-// Gamma(x) returns the Gamma function of x.
+// Gamma returns the Gamma function of x.
//
// Special cases are:
-// Gamma(±Inf) = ±Inf
+// Gamma(+Inf) = +Inf
+// Gamma(+0) = +Inf
+// Gamma(-0) = -Inf
+// Gamma(x) = NaN for integer x < 0
+// Gamma(-Inf) = NaN
// Gamma(NaN) = NaN
-// Large values overflow to +Inf.
-// Zero and negative integer arguments return ±Inf.
func Gamma(x float64) float64 {
const Euler = 0.57721566490153286060651209008240243104215933593992 // A001620
// special cases
switch {
- case IsInf(x, -1) || IsNaN(x):
- return x
+ case isNegInt(x) || IsInf(x, -1) || IsNaN(x):
+ return NaN()
+ case x == 0:
+ if Signbit(x) {
+ return Inf(-1)
+ }
+ return Inf(1)
case x < -170.5674972726612 || x > 171.61447887182298:
return Inf(1)
}
@@ -185,3 +192,11 @@ small:
}
return z / ((1 + Euler*x) * x)
}
+
+func isNegInt(x float64) bool {
+ if x < 0 {
+ _, xf := Modf(x)
+ return xf == 0
+ }
+ return false
+}
diff --git a/src/pkg/math/hypot.go b/src/pkg/math/hypot.go
index df4d3eb70..3846e6d87 100644
--- a/src/pkg/math/hypot.go
+++ b/src/pkg/math/hypot.go
@@ -8,7 +8,7 @@ package math
Hypot -- sqrt(p*p + q*q), but overflows only if the result does.
*/
-// Hypot computes Sqrt(p*p + q*q), taking care to avoid
+// Hypot returns Sqrt(p*p + q*q), taking care to avoid
// unnecessary overflow and underflow.
//
// Special cases are:
diff --git a/src/pkg/math/hypot_386.s b/src/pkg/math/hypot_386.s
index 70ff19a17..51cd90419 100644
--- a/src/pkg/math/hypot_386.s
+++ b/src/pkg/math/hypot_386.s
@@ -2,35 +2,35 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// func Hypot(x, y float64) float64
+// func Hypot(p, q float64) float64
TEXT ·Hypot(SB),7,$0
// test bits for not-finite
- MOVL xh+4(FP), AX // high word x
+ MOVL p+4(FP), AX // high word p
ANDL $0x7ff00000, AX
CMPL AX, $0x7ff00000
JEQ not_finite
- MOVL yh+12(FP), AX // high word y
+ MOVL q+12(FP), AX // high word q
ANDL $0x7ff00000, AX
CMPL AX, $0x7ff00000
JEQ not_finite
- FMOVD x+0(FP), F0 // F0=x
- FABS // F0=|x|
- FMOVD y+8(FP), F0 // F0=y, F1=|x|
- FABS // F0=|y|, F1=|x|
+ FMOVD p+0(FP), F0 // F0=p
+ FABS // F0=|p|
+ FMOVD q+8(FP), F0 // F0=q, F1=|p|
+ FABS // F0=|q|, F1=|p|
FUCOMI F0, F1 // compare F0 to F1
JCC 2(PC) // jump if F0 >= F1
- FXCHD F0, F1 // F0=|x| (larger), F1=|y| (smaller)
+ FXCHD F0, F1 // F0=|p| (larger), F1=|q| (smaller)
FTST // compare F0 to 0
FSTSW AX
ANDW $0x4000, AX
JNE 10(PC) // jump if F0 = 0
- FXCHD F0, F1 // F0=y (smaller), F1=x (larger)
- FDIVD F1, F0 // F0=y(=y/x), F1=x
- FMULD F0, F0 // F0=y*y, F1=x
- FLD1 // F0=1, F1=y*y, F2=x
- FADDDP F0, F1 // F0=1+y*y, F1=x
- FSQRT // F0=sqrt(1+y*y), F1=x
- FMULDP F0, F1 // F0=x*sqrt(1+y*y)
+ FXCHD F0, F1 // F0=q (smaller), F1=p (larger)
+ FDIVD F1, F0 // F0=q(=q/p), F1=p
+ FMULD F0, F0 // F0=q*q, F1=p
+ FLD1 // F0=1, F1=q*q, F2=p
+ FADDDP F0, F1 // F0=1+q*q, F1=p
+ FSQRT // F0=sqrt(1+q*q), F1=p
+ FMULDP F0, F1 // F0=p*sqrt(1+q*q)
FMOVDP F0, r+16(FP)
RET
FMOVDP F0, F1 // F0=0
@@ -38,20 +38,20 @@ TEXT ·Hypot(SB),7,$0
RET
not_finite:
// test bits for -Inf or +Inf
- MOVL xh+4(FP), AX // high word x
- ORL xl+0(FP), AX // low word x
+ MOVL p+4(FP), AX // high word p
+ ORL p+0(FP), AX // low word p
ANDL $0x7fffffff, AX
CMPL AX, $0x7ff00000
JEQ is_inf
- MOVL yh+12(FP), AX // high word y
- ORL yl+8(FP), AX // low word y
+ MOVL q+12(FP), AX // high word q
+ ORL q+8(FP), AX // low word q
ANDL $0x7fffffff, AX
CMPL AX, $0x7ff00000
JEQ is_inf
- MOVL $0x7ff00000, rh+20(FP) // return NaN = 0x7FF0000000000001
- MOVL $0x00000001, rl+16(FP)
+ MOVL $0x7ff80000, r+20(FP) // return NaN = 0x7FF8000000000001
+ MOVL $0x00000001, r+16(FP)
RET
is_inf:
- MOVL AX, rh+20(FP) // return +Inf = 0x7FF0000000000000
- MOVL $0x00000000, rl+16(FP)
+ MOVL AX, r+20(FP) // return +Inf = 0x7FF0000000000000
+ MOVL $0x00000000, r+16(FP)
RET
diff --git a/src/pkg/math/hypot_amd64.s b/src/pkg/math/hypot_amd64.s
index 1f691e70e..02fff5b92 100644
--- a/src/pkg/math/hypot_amd64.s
+++ b/src/pkg/math/hypot_amd64.s
@@ -2,17 +2,17 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#define PosInf 0x7ff0000000000000
-#define NaN 0x7FF0000000000001
+#define PosInf 0x7FF0000000000000
+#define NaN 0x7FF8000000000001
-// func Hypot(x, y float64) float64
+// func Hypot(p, q float64) float64
TEXT ·Hypot(SB),7,$0
// test bits for special cases
- MOVQ x+0(FP), BX
+ MOVQ p+0(FP), BX
MOVQ $~(1<<63), AX
- ANDQ AX, BX // x = |x|
- MOVQ y+8(FP), CX
- ANDQ AX, CX // y = |y|
+ ANDQ AX, BX // p = |p|
+ MOVQ q+8(FP), CX
+ ANDQ AX, CX // q = |q|
MOVQ $PosInf, AX
CMPQ AX, BX
JLE isInfOrNaN
diff --git a/src/pkg/math/ldexp_386.s b/src/pkg/math/ldexp_386.s
index ed91ffcd3..3a65629d2 100644
--- a/src/pkg/math/ldexp_386.s
+++ b/src/pkg/math/ldexp_386.s
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// func Ldexp(f float64, e int) float64
+// func Ldexp(frac float64, exp int) float64
TEXT ·Ldexp(SB),7,$0
- FMOVL e+8(FP), F0 // F0=e
- FMOVD x+0(FP), F0 // F0=x, F1=e
+ FMOVL exp+8(FP), F0 // F0=exp
+ FMOVD frac+0(FP), F0 // F0=frac, F1=e
FSCALE // F0=x*2**e, F1=e
FMOVDP F0, F1 // F0=x*2**e
FMOVDP F0, r+12(FP)
diff --git a/src/pkg/math/log10.go b/src/pkg/math/log10.go
index 67c163a49..95cfbf47c 100644
--- a/src/pkg/math/log10.go
+++ b/src/pkg/math/log10.go
@@ -17,5 +17,6 @@ func log10(x float64) float64 {
func Log2(x float64) float64
func log2(x float64) float64 {
- return Log(x) * (1 / Ln2)
+ frac, exp := Frexp(x)
+ return Log(frac)*(1/Ln2) + float64(exp)
}
diff --git a/src/pkg/math/log_amd64.s b/src/pkg/math/log_amd64.s
index 79e35907c..75bc55764 100644
--- a/src/pkg/math/log_amd64.s
+++ b/src/pkg/math/log_amd64.s
@@ -12,7 +12,7 @@
#define L5 1.818357216161805012e-01 // 0x3FC7466496CB03DE
#define L6 1.531383769920937332e-01 // 0x3FC39A09D078C69F
#define L7 1.479819860511658591e-01 // 0x3FC2F112DF3E5244
-#define NaN 0x7FF0000000000001
+#define NaN 0x7FF8000000000001
#define NegInf 0xFFF0000000000000
#define PosInf 0x7FF0000000000000
@@ -54,13 +54,13 @@ TEXT ·Log(SB),7,$0
// s := f / (2 + f)
MOVSD $2.0, X0
ADDSD X2, X0
- MOVSD X2, X3
+ MOVAPD X2, X3
DIVSD X0, X3 // x1=k, x2= f, x3= s
// s2 := s * s
- MOVSD X3, X4 // x1= k, x2= f, x3= s
+ MOVAPD X3, X4 // x1= k, x2= f, x3= s
MULSD X4, X4 // x1= k, x2= f, x3= s, x4= s2
// s4 := s2 * s2
- MOVSD X4, X5 // x1= k, x2= f, x3= s, x4= s2
+ MOVAPD X4, X5 // x1= k, x2= f, x3= s, x4= s2
MULSD X5, X5 // x1= k, x2= f, x3= s, x4= s2, x5= s4
// t1 := s2 * (L1 + s4*(L3+s4*(L5+s4*L7)))
MOVSD $L7, X6
diff --git a/src/pkg/math/logb.go b/src/pkg/math/logb.go
index d32f9f100..f2769d4fd 100644
--- a/src/pkg/math/logb.go
+++ b/src/pkg/math/logb.go
@@ -4,7 +4,7 @@
package math
-// Logb(x) returns the binary exponent of x.
+// Logb returns the binary exponent of x.
//
// Special cases are:
// Logb(±Inf) = +Inf
@@ -23,7 +23,7 @@ func Logb(x float64) float64 {
return float64(ilogb(x))
}
-// Ilogb(x) returns the binary exponent of x as an integer.
+// Ilogb returns the binary exponent of x as an integer.
//
// Special cases are:
// Ilogb(±Inf) = MaxInt32
diff --git a/src/pkg/math/modf_386.s b/src/pkg/math/modf_386.s
index 5ccab9812..f5dc415c3 100644
--- a/src/pkg/math/modf_386.s
+++ b/src/pkg/math/modf_386.s
@@ -2,18 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// func Modf(x float64) (int float64, frac float64)
+// func Modf(f float64) (int float64, frac float64)
TEXT ·Modf(SB),7,$0
- FMOVD x+0(FP), F0 // F0=x
- FMOVD F0, F1 // F0=x, F1=x
+ FMOVD f+0(FP), F0 // F0=f
+ FMOVD F0, F1 // F0=f, F1=f
FSTCW -2(SP) // save old Control Word
MOVW -2(SP), AX
ORW $0x0c00, AX // Rounding Control set to truncate
MOVW AX, -4(SP) // store new Control Word
FLDCW -4(SP) // load new Control Word
- FRNDINT // F0=trunc(x), F1=x
+ FRNDINT // F0=trunc(f), F1=f
FLDCW -2(SP) // load old Control Word
- FSUBD F0, F1 // F0=trunc(x), F1=x-trunc(x)
- FMOVDP F0, i+8(FP) // F0=x-trunc(x)
- FMOVDP F0, f+16(FP)
+ FSUBD F0, F1 // F0=trunc(f), F1=f-trunc(f)
+ FMOVDP F0, int+8(FP) // F0=f-trunc(f)
+ FMOVDP F0, frac+16(FP)
RET
diff --git a/src/pkg/math/rand/example_test.go b/src/pkg/math/rand/example_test.go
new file mode 100644
index 000000000..4fe207d85
--- /dev/null
+++ b/src/pkg/math/rand/example_test.go
@@ -0,0 +1,69 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package rand_test
+
+import (
+ "fmt"
+ "math/rand"
+ "os"
+ "text/tabwriter"
+)
+
+// This test serves as an example but also makes sure we don't change
+// the output of the random number generator when given a fixed seed.
+
+// This example shows the use of each of the methods on a *Rand.
+// The use of the global functions is the same, without the receiver.
+func Example() {
+ // Create and seed the generator.
+ // Typically a non-fixed seed should be used, such as time.Now().UnixNano().
+ // Using a fixed seed will produce the same output on every run.
+ r := rand.New(rand.NewSource(99))
+
+ // The tabwriter here helps us generate aligned output.
+ w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0)
+ defer w.Flush()
+ show := func(name string, v1, v2, v3 interface{}) {
+ fmt.Fprintf(w, "%s\t%v\t%v\t%v\n", name, v1, v2, v3)
+ }
+
+ // Float32 and Float64 values are in [0, 1).
+ show("Float32", r.Float32(), r.Float32(), r.Float32())
+ show("Float64", r.Float64(), r.Float64(), r.Float64())
+
+ // ExpFloat64 values have an average of 1 but decay exponentially.
+ show("ExpFloat64", r.ExpFloat64(), r.ExpFloat64(), r.ExpFloat64())
+
+ // NormFloat64 values have an average of 0 and a standard deviation of 1.
+ show("NormFloat64", r.NormFloat64(), r.NormFloat64(), r.NormFloat64())
+
+ // Int31, Int63, and Uint32 generate values of the given width.
+ // The Int method (not shown) is like either Int31 or Int63
+ // depending on the size of 'int'.
+ show("Int31", r.Int31(), r.Int31(), r.Int31())
+ show("Int63", r.Int63(), r.Int63(), r.Int63())
+ show("Uint32", r.Int63(), r.Int63(), r.Int63())
+
+ // Intn, Int31n, and Int63n limit their output to be < n.
+ // They do so more carefully than using r.Int()%n.
+ show("Intn(10)", r.Intn(10), r.Intn(10), r.Intn(10))
+ show("Int31n(10)", r.Int31n(10), r.Int31n(10), r.Int31n(10))
+ show("Int63n(10)", r.Int63n(10), r.Int63n(10), r.Int63n(10))
+
+ // Perm generates a random permutation of the numbers [0, n).
+ show("Perm", r.Perm(5), r.Perm(5), r.Perm(5))
+ // Output:
+ // Float32 0.2635776 0.6358173 0.6718283
+ // Float64 0.628605430454327 0.4504798828572669 0.9562755949377957
+ // ExpFloat64 0.3362240648200941 1.4256072328483647 0.24354758816173044
+ // NormFloat64 0.17233959114940064 1.577014951434847 0.04259129641113857
+ // Int31 1501292890 1486668269 182840835
+ // Int63 3546343826724305832 5724354148158589552 5239846799706671610
+ // Uint32 5927547564735367388 637072299495207830 4128311955958246186
+ // Intn(10) 1 2 5
+ // Int31n(10) 4 7 8
+ // Int63n(10) 7 6 3
+ // Perm [1 4 2 3 0] [4 2 1 3 0] [1 2 4 0 3]
+}
diff --git a/src/pkg/math/rand/rand_test.go b/src/pkg/math/rand/rand_test.go
index bbd44e3f8..4d3abdb60 100644
--- a/src/pkg/math/rand/rand_test.go
+++ b/src/pkg/math/rand/rand_test.go
@@ -57,16 +57,13 @@ func (this *statsResults) checkSimilarDistribution(expected *statsResults) error
func getStatsResults(samples []float64) *statsResults {
res := new(statsResults)
- var sum float64
- for i := range samples {
- sum += samples[i]
+ var sum, squaresum float64
+ for _, s := range samples {
+ sum += s
+ squaresum += s * s
}
res.mean = sum / float64(len(samples))
- var devsum float64
- for i := range samples {
- devsum += math.Pow(samples[i]-res.mean, 2)
- }
- res.stddev = math.Sqrt(devsum / float64(len(samples)))
+ res.stddev = math.Sqrt(squaresum/float64(len(samples)) - res.mean*res.mean)
return res
}
diff --git a/src/pkg/math/remainder.go b/src/pkg/math/remainder.go
index 41efd7908..9a4e4154c 100644
--- a/src/pkg/math/remainder.go
+++ b/src/pkg/math/remainder.go
@@ -4,7 +4,7 @@
package math
-// The original C code and the the comment below are from
+// The original C code and the comment below are from
// FreeBSD's /usr/src/lib/msun/src/e_remainder.c and came
// with this notice. The go code is a simplified version of
// the original C.
diff --git a/src/pkg/math/sincos.go b/src/pkg/math/sincos.go
index 730042920..718030319 100644
--- a/src/pkg/math/sincos.go
+++ b/src/pkg/math/sincos.go
@@ -6,7 +6,7 @@ package math
// Coefficients _sin[] and _cos[] are found in pkg/math/sin.go.
-// Sincos(x) returns Sin(x), Cos(x).
+// Sincos returns Sin(x), Cos(x).
//
// Special cases are:
// Sincos(±0) = ±0, 1
diff --git a/src/pkg/math/sincos_386.s b/src/pkg/math/sincos_386.s
index 9dd37a3b7..8f5e0f8d1 100644
--- a/src/pkg/math/sincos_386.s
+++ b/src/pkg/math/sincos_386.s
@@ -9,8 +9,8 @@ TEXT ·Sincos(SB),7,$0
FSTSW AX // AX=status word
ANDW $0x0400, AX
JNE 4(PC) // jump if x outside range
- FMOVDP F0, c+16(FP) // F0=sin(x)
- FMOVDP F0, s+8(FP)
+ FMOVDP F0, cos+16(FP) // F0=sin(x)
+ FMOVDP F0, sin+8(FP)
RET
FLDPI // F0=Pi, F1=x
FADDD F0, F0 // F0=2*Pi, F1=x
@@ -21,6 +21,6 @@ TEXT ·Sincos(SB),7,$0
JNE -3(PC) // jump if reduction incomplete
FMOVDP F0, F1 // F0=reduced_x
FSINCOS // F0=cos(reduced_x), F1=sin(reduced_x)
- FMOVDP F0, c+16(FP) // F0=sin(reduced_x)
- FMOVDP F0, s+8(FP)
+ FMOVDP F0, cos+16(FP) // F0=sin(reduced_x)
+ FMOVDP F0, sin+8(FP)
RET
diff --git a/src/pkg/math/sincos_amd64.s b/src/pkg/math/sincos_amd64.s
index 18c824e51..c9dea0916 100644
--- a/src/pkg/math/sincos_amd64.s
+++ b/src/pkg/math/sincos_amd64.s
@@ -19,7 +19,7 @@
#define PosOne 0x3FF0000000000000
#define PosInf 0x7FF0000000000000
-#define NaN 0x7FF0000000000001
+#define NaN 0x7FF8000000000001
#define PI4A 0.7853981554508209228515625 // pi/4 split into three parts
#define PI4B 0.794662735614792836713604629039764404296875e-8
#define PI4C 0.306161699786838294306516483068750264552437361480769e-16
diff --git a/src/pkg/math/tanh.go b/src/pkg/math/tanh.go
index 03a641b4d..7305be66c 100644
--- a/src/pkg/math/tanh.go
+++ b/src/pkg/math/tanh.go
@@ -4,12 +4,66 @@
package math
-/*
- Floating-point hyperbolic tangent.
+// The original C code, the long comment, and the constants
+// below were from http://netlib.sandia.gov/cephes/cmath/sin.c,
+// available from http://www.netlib.org/cephes/cmath.tgz.
+// The go code is a simplified version of the original C.
+// tanh.c
+//
+// Hyperbolic tangent
+//
+// SYNOPSIS:
+//
+// double x, y, tanh();
+//
+// y = tanh( x );
+//
+// DESCRIPTION:
+//
+// Returns hyperbolic tangent of argument in the range MINLOG to MAXLOG.
+// MAXLOG = 8.8029691931113054295988e+01 = log(2**127)
+// MINLOG = -8.872283911167299960540e+01 = log(2**-128)
+//
+// A rational function is used for |x| < 0.625. The form
+// x + x**3 P(x)/Q(x) of Cody & Waite is employed.
+// Otherwise,
+// tanh(x) = sinh(x)/cosh(x) = 1 - 2/(exp(2x) + 1).
+//
+// ACCURACY:
+//
+// Relative error:
+// arithmetic domain # trials peak rms
+// IEEE -2,2 30000 2.5e-16 5.8e-17
+//
+// Cephes Math Library Release 2.8: June, 2000
+// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
+//
+// The readme file at http://netlib.sandia.gov/cephes/ says:
+// Some software in this archive may be from the book _Methods and
+// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
+// International, 1989) or from the Cephes Mathematical Library, a
+// commercial product. In either event, it is copyrighted by the author.
+// What you see here may be used freely but it comes with no support or
+// guarantee.
+//
+// The two known misprints in the book are repaired here in the
+// source listings for the gamma function and the incomplete beta
+// integral.
+//
+// Stephen L. Moshier
+// moshier@na-net.ornl.gov
+//
- Sinh and Cosh are called except for large arguments, which
- would cause overflow improperly.
-*/
+var tanhP = [...]float64{
+ -9.64399179425052238628E-1,
+ -9.92877231001918586564E1,
+ -1.61468768441708447952E3,
+}
+var tanhQ = [...]float64{
+ 1.12811678491632931402E2,
+ 2.23548839060100448583E3,
+ 4.84406305325125486048E3,
+}
// Tanh computes the hyperbolic tangent of x.
//
@@ -18,15 +72,26 @@ package math
// Tanh(±Inf) = ±1
// Tanh(NaN) = NaN
func Tanh(x float64) float64 {
- if x < 0 {
- x = -x
- if x > 21 {
+ const MAXLOG = 8.8029691931113054295988e+01 // log(2**127)
+ z := Abs(x)
+ switch {
+ case z > 0.5*MAXLOG:
+ if x < 0 {
return -1
}
- return -Sinh(x) / Cosh(x)
- }
- if x > 21 {
return 1
+ case z >= 0.625:
+ s := Exp(2 * z)
+ z = 1 - 2/(s+1)
+ if x < 0 {
+ z = -z
+ }
+ default:
+ if x == 0 {
+ return x
+ }
+ s := x * x
+ z = x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2])
}
- return Sinh(x) / Cosh(x)
+ return z
}
diff --git a/src/pkg/mime/grammar.go b/src/pkg/mime/grammar.go
index 83cc41134..09e941e3e 100644
--- a/src/pkg/mime/grammar.go
+++ b/src/pkg/mime/grammar.go
@@ -22,7 +22,7 @@ func isTokenChar(r rune) bool {
return r > 0x20 && r < 0x7f && !isTSpecial(r)
}
-// isToken returns true if s is a 'token' as as defined by RFC 1521
+// isToken returns true if s is a 'token' as defined by RFC 1521
// and RFC 2045.
func isToken(s string) bool {
if s == "" {
diff --git a/src/pkg/mime/multipart/multipart.go b/src/pkg/mime/multipart/multipart.go
index e9e337b92..77e969b41 100644
--- a/src/pkg/mime/multipart/multipart.go
+++ b/src/pkg/mime/multipart/multipart.go
@@ -37,6 +37,11 @@ type Part struct {
disposition string
dispositionParams map[string]string
+
+ // r is either a reader directly reading from mr, or it's a
+ // wrapper around such a reader, decoding the
+ // Content-Transfer-Encoding
+ r io.Reader
}
// FormName returns the name parameter if p has a Content-Disposition
@@ -71,7 +76,7 @@ func (p *Part) parseContentDisposition() {
}
}
-// NewReader creates a new multipart Reader reading from r using the
+// NewReader creates a new multipart Reader reading from reader using the
// given MIME boundary.
func NewReader(reader io.Reader, boundary string) *Reader {
b := []byte("\r\n--" + boundary + "--")
@@ -94,6 +99,12 @@ func newPart(mr *Reader) (*Part, error) {
if err := bp.populateHeaders(); err != nil {
return nil, err
}
+ bp.r = partReader{bp}
+ const cte = "Content-Transfer-Encoding"
+ if bp.Header.Get(cte) == "quoted-printable" {
+ bp.Header.Del(cte)
+ bp.r = newQuotedPrintableReader(bp.r)
+ }
return bp, nil
}
@@ -109,6 +120,17 @@ func (bp *Part) populateHeaders() error {
// Read reads the body of a part, after its headers and before the
// next part (if any) begins.
func (p *Part) Read(d []byte) (n int, err error) {
+ return p.r.Read(d)
+}
+
+// partReader implements io.Reader by reading raw bytes directly from the
+// wrapped *Part, without doing any Transfer-Encoding decoding.
+type partReader struct {
+ p *Part
+}
+
+func (pr partReader) Read(d []byte) (n int, err error) {
+ p := pr.p
defer func() {
p.bytesRead += n
}()
diff --git a/src/pkg/mime/multipart/multipart_test.go b/src/pkg/mime/multipart/multipart_test.go
index cd65e177e..d662e8340 100644
--- a/src/pkg/mime/multipart/multipart_test.go
+++ b/src/pkg/mime/multipart/multipart_test.go
@@ -339,9 +339,10 @@ func TestLineContinuation(t *testing.T) {
if err != nil {
t.Fatalf("didn't get a part")
}
- n, err := io.Copy(ioutil.Discard, part)
+ var buf bytes.Buffer
+ n, err := io.Copy(&buf, part)
if err != nil {
- t.Errorf("error reading part: %v", err)
+ t.Errorf("error reading part: %v\nread so far: %q", err, buf.String())
}
if n <= 0 {
t.Errorf("read %d bytes; expected >0", n)
@@ -349,6 +350,29 @@ func TestLineContinuation(t *testing.T) {
}
}
+func TestQuotedPrintableEncoding(t *testing.T) {
+ // From http://golang.org/issue/4411
+ body := "--0016e68ee29c5d515f04cedf6733\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=text\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nwords words words words words words words words words words words words wor=\r\nds words words words words words words words words words words words words =\r\nwords words words words words words words words words words words words wor=\r\nds words words words words words words words words words words words words =\r\nwords words words words words words words words words\r\n--0016e68ee29c5d515f04cedf6733\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=submit\r\n\r\nSubmit\r\n--0016e68ee29c5d515f04cedf6733--"
+ r := NewReader(strings.NewReader(body), "0016e68ee29c5d515f04cedf6733")
+ part, err := r.NextPart()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if te, ok := part.Header["Content-Transfer-Encoding"]; ok {
+ t.Errorf("unexpected Content-Transfer-Encoding of %q", te)
+ }
+ var buf bytes.Buffer
+ _, err = io.Copy(&buf, part)
+ if err != nil {
+ t.Error(err)
+ }
+ got := buf.String()
+ want := "words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words"
+ if got != want {
+ t.Errorf("wrong part value:\n got: %q\nwant: %q", got, want)
+ }
+}
+
// Test parsing an image attachment from gmail, which previously failed.
func TestNested(t *testing.T) {
// nested-mime is the body part of a multipart/mixed email
diff --git a/src/pkg/mime/multipart/quotedprintable.go b/src/pkg/mime/multipart/quotedprintable.go
new file mode 100644
index 000000000..9e18e1ea1
--- /dev/null
+++ b/src/pkg/mime/multipart/quotedprintable.go
@@ -0,0 +1,130 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The file define a quoted-printable decoder, as specified in RFC 2045.
+// Deviations:
+// 1. in addition to "=\r\n", "=\n" is also treated as soft line break.
+// 2. it will pass through a '\r' or '\n' not preceded by '=', consistent
+// with other broken QP encoders & decoders.
+
+package multipart
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+)
+
+type qpReader struct {
+ br *bufio.Reader
+ skipWhite bool
+ rerr error // last read error
+ line []byte // to be consumed before more of br
+}
+
+func newQuotedPrintableReader(r io.Reader) io.Reader {
+ return &qpReader{
+ br: bufio.NewReader(r),
+ skipWhite: true,
+ }
+}
+
+func fromHex(b byte) (byte, error) {
+ switch {
+ case b >= '0' && b <= '9':
+ return b - '0', nil
+ case b >= 'A' && b <= 'F':
+ return b - 'A' + 10, nil
+ }
+ return 0, fmt.Errorf("multipart: invalid quoted-printable hex byte 0x%02x", b)
+}
+
+func (q *qpReader) readHexByte(v []byte) (b byte, err error) {
+ if len(v) < 2 {
+ return 0, io.ErrUnexpectedEOF
+ }
+ var hb, lb byte
+ if hb, err = fromHex(v[0]); err != nil {
+ return 0, err
+ }
+ if lb, err = fromHex(v[1]); err != nil {
+ return 0, err
+ }
+ return hb<<4 | lb, nil
+}
+
+func isQPSkipWhiteByte(b byte) bool {
+ return b == ' ' || b == '\t'
+}
+
+func isQPDiscardWhitespace(r rune) bool {
+ switch r {
+ case '\n', '\r', ' ', '\t':
+ return true
+ }
+ return false
+}
+
+var (
+ crlf = []byte("\r\n")
+ lf = []byte("\n")
+ softSuffix = []byte("=")
+)
+
+func (q *qpReader) Read(p []byte) (n int, err error) {
+ for len(p) > 0 {
+ if len(q.line) == 0 {
+ if q.rerr != nil {
+ return n, q.rerr
+ }
+ q.skipWhite = true
+ q.line, q.rerr = q.br.ReadSlice('\n')
+
+ // Does the line end in CRLF instead of just LF?
+ hasLF := bytes.HasSuffix(q.line, lf)
+ hasCR := bytes.HasSuffix(q.line, crlf)
+ wholeLine := q.line
+ q.line = bytes.TrimRightFunc(wholeLine, isQPDiscardWhitespace)
+ if bytes.HasSuffix(q.line, softSuffix) {
+ rightStripped := wholeLine[len(q.line):]
+ q.line = q.line[:len(q.line)-1]
+ if !bytes.HasPrefix(rightStripped, lf) && !bytes.HasPrefix(rightStripped, crlf) {
+ q.rerr = fmt.Errorf("multipart: invalid bytes after =: %q", rightStripped)
+ }
+ } else if hasLF {
+ if hasCR {
+ q.line = append(q.line, '\r', '\n')
+ } else {
+ q.line = append(q.line, '\n')
+ }
+ }
+ continue
+ }
+ b := q.line[0]
+ if q.skipWhite && isQPSkipWhiteByte(b) {
+ q.line = q.line[1:]
+ continue
+ }
+ q.skipWhite = false
+
+ switch {
+ case b == '=':
+ b, err = q.readHexByte(q.line[1:])
+ if err != nil {
+ return n, err
+ }
+ q.line = q.line[2:] // 2 of the 3; other 1 is done below
+ case b == '\t' || b == '\r' || b == '\n':
+ break
+ case b < ' ' || b > '~':
+ return n, fmt.Errorf("multipart: invalid unescaped byte 0x%02x in quoted-printable body", b)
+ }
+ p[0] = b
+ p = p[1:]
+ q.line = q.line[1:]
+ n++
+ }
+ return n, nil
+}
diff --git a/src/pkg/mime/multipart/quotedprintable_test.go b/src/pkg/mime/multipart/quotedprintable_test.go
new file mode 100644
index 000000000..7bcf5767b
--- /dev/null
+++ b/src/pkg/mime/multipart/quotedprintable_test.go
@@ -0,0 +1,199 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package multipart
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "os/exec"
+ "regexp"
+ "sort"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestQuotedPrintable(t *testing.T) {
+ tests := []struct {
+ in, want string
+ err interface{}
+ }{
+ {in: "", want: ""},
+ {in: "foo bar", want: "foo bar"},
+ {in: "foo bar=3D", want: "foo bar="},
+ {in: "foo bar=\n", want: "foo bar"},
+ {in: "foo bar\n", want: "foo bar\n"}, // somewhat lax.
+ {in: "foo bar=0", want: "foo bar", err: io.ErrUnexpectedEOF},
+ {in: "foo bar=ab", want: "foo bar", err: "multipart: invalid quoted-printable hex byte 0x61"},
+ {in: "foo bar=0D=0A", want: "foo bar\r\n"},
+ {in: " A B =\r\n C ", want: "A B C"},
+ {in: " A B =\n C ", want: "A B C"}, // lax. treating LF as CRLF
+ {in: "foo=\nbar", want: "foobar"},
+ {in: "foo\x00bar", want: "foo", err: "multipart: invalid unescaped byte 0x00 in quoted-printable body"},
+ {in: "foo bar\xff", want: "foo bar", err: "multipart: invalid unescaped byte 0xff in quoted-printable body"},
+
+ // Equal sign.
+ {in: "=3D30\n", want: "=30\n"},
+ {in: "=00=FF0=\n", want: "\x00\xff0"},
+
+ // Trailing whitespace
+ {in: "foo \n", want: "foo\n"},
+ {in: "foo \n\nfoo =\n\nfoo=20\n\n", want: "foo\n\nfoo \nfoo \n\n"},
+
+ // Tests that we allow bare \n and \r through, despite it being strictly
+ // not permitted per RFC 2045, Section 6.7 Page 22 bullet (4).
+ {in: "foo\nbar", want: "foo\nbar"},
+ {in: "foo\rbar", want: "foo\rbar"},
+ {in: "foo\r\nbar", want: "foo\r\nbar"},
+
+ // Different types of soft line-breaks.
+ {in: "foo=\r\nbar", want: "foobar"},
+ {in: "foo=\nbar", want: "foobar"},
+ {in: "foo=\rbar", want: "foo", err: "multipart: invalid quoted-printable hex byte 0x0d"},
+ {in: "foo=\r\r\r \nbar", want: "foo", err: `multipart: invalid bytes after =: "\r\r\r \n"`},
+ }
+ for _, tt := range tests {
+ var buf bytes.Buffer
+ _, err := io.Copy(&buf, newQuotedPrintableReader(strings.NewReader(tt.in)))
+ if got := buf.String(); got != tt.want {
+ t.Errorf("for %q, got %q; want %q", tt.in, got, tt.want)
+ }
+ switch verr := tt.err.(type) {
+ case nil:
+ if err != nil {
+ t.Errorf("for %q, got unexpected error: %v", tt.in, err)
+ }
+ case string:
+ if got := fmt.Sprint(err); got != verr {
+ t.Errorf("for %q, got error %q; want %q", tt.in, got, verr)
+ }
+ case error:
+ if err != verr {
+ t.Errorf("for %q, got error %q; want %q", tt.in, err, verr)
+ }
+ }
+ }
+
+}
+
+func everySequence(base, alpha string, length int, fn func(string)) {
+ if len(base) == length {
+ fn(base)
+ return
+ }
+ for i := 0; i < len(alpha); i++ {
+ everySequence(base+alpha[i:i+1], alpha, length, fn)
+ }
+}
+
+var useQprint = flag.Bool("qprint", false, "Compare against the 'qprint' program.")
+
+var badSoftRx = regexp.MustCompile(`=([^\r\n]+?\n)|([^\r\n]+$)|(\r$)|(\r[^\n]+\n)|( \r\n)`)
+
+func TestQPExhaustive(t *testing.T) {
+ if *useQprint {
+ _, err := exec.LookPath("qprint")
+ if err != nil {
+ t.Fatalf("Error looking for qprint: %v", err)
+ }
+ }
+
+ var buf bytes.Buffer
+ res := make(map[string]int)
+ everySequence("", "0A \r\n=", 6, func(s string) {
+ if strings.HasSuffix(s, "=") || strings.Contains(s, "==") {
+ return
+ }
+ buf.Reset()
+ _, err := io.Copy(&buf, newQuotedPrintableReader(strings.NewReader(s)))
+ if err != nil {
+ errStr := err.Error()
+ if strings.Contains(errStr, "invalid bytes after =:") {
+ errStr = "invalid bytes after ="
+ }
+ res[errStr]++
+ if strings.Contains(errStr, "invalid quoted-printable hex byte ") {
+ if strings.HasSuffix(errStr, "0x20") && (strings.Contains(s, "=0 ") || strings.Contains(s, "=A ") || strings.Contains(s, "= ")) {
+ return
+ }
+ if strings.HasSuffix(errStr, "0x3d") && (strings.Contains(s, "=0=") || strings.Contains(s, "=A=")) {
+ return
+ }
+ if strings.HasSuffix(errStr, "0x0a") || strings.HasSuffix(errStr, "0x0d") {
+ // bunch of cases; since whitespace at the end of of a line before \n is removed.
+ return
+ }
+ }
+ if strings.Contains(errStr, "unexpected EOF") {
+ return
+ }
+ if errStr == "invalid bytes after =" && badSoftRx.MatchString(s) {
+ return
+ }
+ t.Errorf("decode(%q) = %v", s, err)
+ return
+ }
+ if *useQprint {
+ cmd := exec.Command("qprint", "-d")
+ cmd.Stdin = strings.NewReader(s)
+ stderr, err := cmd.StderrPipe()
+ if err != nil {
+ panic(err)
+ }
+ qpres := make(chan interface{}, 2)
+ go func() {
+ br := bufio.NewReader(stderr)
+ s, _ := br.ReadString('\n')
+ if s != "" {
+ qpres <- errors.New(s)
+ if cmd.Process != nil {
+ // It can get stuck on invalid input, like:
+ // echo -n "0000= " | qprint -d
+ cmd.Process.Kill()
+ }
+ }
+ }()
+ go func() {
+ want, err := cmd.Output()
+ if err == nil {
+ qpres <- want
+ }
+ }()
+ select {
+ case got := <-qpres:
+ if want, ok := got.([]byte); ok {
+ if string(want) != buf.String() {
+ t.Errorf("go decode(%q) = %q; qprint = %q", s, want, buf.String())
+ }
+ } else {
+ t.Logf("qprint -d(%q) = %v", s, got)
+ }
+ case <-time.After(5 * time.Second):
+ t.Logf("qprint timeout on %q", s)
+ }
+ }
+ res["OK"]++
+ })
+ var outcomes []string
+ for k, v := range res {
+ outcomes = append(outcomes, fmt.Sprintf("%v: %d", k, v))
+ }
+ sort.Strings(outcomes)
+ got := strings.Join(outcomes, "\n")
+ want := `OK: 21576
+invalid bytes after =: 3397
+multipart: invalid quoted-printable hex byte 0x0a: 1400
+multipart: invalid quoted-printable hex byte 0x0d: 2700
+multipart: invalid quoted-printable hex byte 0x20: 2490
+multipart: invalid quoted-printable hex byte 0x3d: 440
+unexpected EOF: 3122`
+ if got != want {
+ t.Errorf("Got:\n%s\nWant:\n%s", got, want)
+ }
+}
diff --git a/src/pkg/mime/multipart/testdata/nested-mime b/src/pkg/mime/multipart/testdata/nested-mime
index 71c238e38..71c238e38 100755..100644
--- a/src/pkg/mime/multipart/testdata/nested-mime
+++ b/src/pkg/mime/multipart/testdata/nested-mime
diff --git a/src/pkg/mime/multipart/writer.go b/src/pkg/mime/multipart/writer.go
index ec70be492..e13a956af 100644
--- a/src/pkg/mime/multipart/writer.go
+++ b/src/pkg/mime/multipart/writer.go
@@ -30,11 +30,38 @@ func NewWriter(w io.Writer) *Writer {
}
}
-// Boundary returns the Writer's randomly selected boundary string.
+// Boundary returns the Writer's boundary.
func (w *Writer) Boundary() string {
return w.boundary
}
+// SetBoundary overrides the Writer's default randomly-generated
+// boundary separator with an explicit value.
+//
+// SetBoundary must be called before any parts are created, may only
+// contain certain ASCII characters, and must be 1-69 bytes long.
+func (w *Writer) SetBoundary(boundary string) error {
+ if w.lastpart != nil {
+ return errors.New("mime: SetBoundary called after write")
+ }
+ // rfc2046#section-5.1.1
+ if len(boundary) < 1 || len(boundary) > 69 {
+ return errors.New("mime: invalid boundary length")
+ }
+ for _, b := range boundary {
+ if 'A' <= b && b <= 'Z' || 'a' <= b && b <= 'z' || '0' <= b && b <= '9' {
+ continue
+ }
+ switch b {
+ case '\'', '(', ')', '+', '_', ',', '-', '.', '/', ':', '=', '?':
+ continue
+ }
+ return errors.New("mime: invalid boundary character")
+ }
+ w.boundary = boundary
+ return nil
+}
+
// FormDataContentType returns the Content-Type for an HTTP
// multipart/form-data with this Writer's Boundary.
func (w *Writer) FormDataContentType() string {
diff --git a/src/pkg/mime/multipart/writer_test.go b/src/pkg/mime/multipart/writer_test.go
index 494e936c4..52d68bcb6 100644
--- a/src/pkg/mime/multipart/writer_test.go
+++ b/src/pkg/mime/multipart/writer_test.go
@@ -7,6 +7,7 @@ package multipart
import (
"bytes"
"io/ioutil"
+ "strings"
"testing"
)
@@ -76,3 +77,37 @@ func TestWriter(t *testing.T) {
t.Fatalf("expected end of parts; got %v, %v", part, err)
}
}
+
+func TestWriterSetBoundary(t *testing.T) {
+ var b bytes.Buffer
+ w := NewWriter(&b)
+ tests := []struct {
+ b string
+ ok bool
+ }{
+ {"abc", true},
+ {"", false},
+ {"ungültig", false},
+ {"!", false},
+ {strings.Repeat("x", 69), true},
+ {strings.Repeat("x", 70), false},
+ {"bad!ascii!", false},
+ {"my-separator", true},
+ }
+ for i, tt := range tests {
+ err := w.SetBoundary(tt.b)
+ got := err == nil
+ if got != tt.ok {
+ t.Errorf("%d. boundary %q = %v (%v); want %v", i, tt.b, got, err, tt.ok)
+ } else if tt.ok {
+ got := w.Boundary()
+ if got != tt.b {
+ t.Errorf("boundary = %q; want %q", got, tt.b)
+ }
+ }
+ }
+ w.Close()
+ if got := b.String(); !strings.Contains(got, "\r\n--my-separator--\r\n") {
+ t.Errorf("expected my-separator in output. got: %q", got)
+ }
+}
diff --git a/src/pkg/mime/test.types b/src/pkg/mime/testdata/test.types
index 9b040edd7..9b040edd7 100644
--- a/src/pkg/mime/test.types
+++ b/src/pkg/mime/testdata/test.types
diff --git a/src/pkg/mime/type_unix.go b/src/pkg/mime/type_unix.go
index 2dab1eac7..857a0ab67 100644
--- a/src/pkg/mime/type_unix.go
+++ b/src/pkg/mime/type_unix.go
@@ -23,15 +23,11 @@ func loadMimeFile(filename string) {
if err != nil {
return
}
+ defer f.Close()
- reader := bufio.NewReader(f)
- for {
- line, err := reader.ReadString('\n')
- if err != nil {
- f.Close()
- return
- }
- fields := strings.Fields(line)
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ fields := strings.Fields(scanner.Text())
if len(fields) <= 1 || fields[0][0] == '#' {
continue
}
@@ -43,6 +39,9 @@ func loadMimeFile(filename string) {
setExtensionType("."+ext, mimeType)
}
}
+ if err := scanner.Err(); err != nil {
+ panic(err)
+ }
}
func initMime() {
@@ -52,7 +51,7 @@ func initMime() {
}
func initMimeForTests() map[string]string {
- typeFiles = []string{"test.types"}
+ typeFiles = []string{"testdata/test.types"}
return map[string]string{
".t1": "application/test",
".t2": "text/test; charset=utf-8",
diff --git a/src/pkg/mime/type_windows.go b/src/pkg/mime/type_windows.go
index bc388893b..180f948d1 100644
--- a/src/pkg/mime/type_windows.go
+++ b/src/pkg/mime/type_windows.go
@@ -11,7 +11,8 @@ import (
func initMime() {
var root syscall.Handle
- if syscall.RegOpenKeyEx(syscall.HKEY_CLASSES_ROOT, syscall.StringToUTF16Ptr(`\`),
+ rootpathp, _ := syscall.UTF16PtrFromString(`\`)
+ if syscall.RegOpenKeyEx(syscall.HKEY_CLASSES_ROOT, rootpathp,
0, syscall.KEY_READ, &root) != nil {
return
}
@@ -31,15 +32,17 @@ func initMime() {
continue
}
var h syscall.Handle
+ extpathp, _ := syscall.UTF16PtrFromString(`\` + ext)
if syscall.RegOpenKeyEx(
- syscall.HKEY_CLASSES_ROOT, syscall.StringToUTF16Ptr(`\`+ext),
+ syscall.HKEY_CLASSES_ROOT, extpathp,
0, syscall.KEY_READ, &h) != nil {
continue
}
var typ uint32
n = uint32(len(buf) * 2) // api expects array of bytes, not uint16
+ contenttypep, _ := syscall.UTF16PtrFromString("Content Type")
if syscall.RegQueryValueEx(
- h, syscall.StringToUTF16Ptr("Content Type"),
+ h, contenttypep,
nil, &typ, (*byte)(unsafe.Pointer(&buf[0])), &n) != nil {
syscall.RegCloseKey(h)
continue
@@ -55,7 +58,6 @@ func initMime() {
func initMimeForTests() map[string]string {
return map[string]string{
- ".bmp": "image/bmp",
".png": "image/png",
}
}
diff --git a/src/pkg/net/cgo_bsd.go b/src/pkg/net/cgo_bsd.go
index 63750f7a3..3b38e3d83 100644
--- a/src/pkg/net/cgo_bsd.go
+++ b/src/pkg/net/cgo_bsd.go
@@ -11,6 +11,6 @@ package net
*/
import "C"
-func cgoAddrInfoMask() C.int {
- return C.AI_MASK
+func cgoAddrInfoFlags() C.int {
+ return (C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL) & C.AI_MASK
}
diff --git a/src/pkg/net/cgo_linux.go b/src/pkg/net/cgo_linux.go
index 8d4413d2d..f6cefa89a 100644
--- a/src/pkg/net/cgo_linux.go
+++ b/src/pkg/net/cgo_linux.go
@@ -9,6 +9,12 @@ package net
*/
import "C"
-func cgoAddrInfoMask() C.int {
+func cgoAddrInfoFlags() C.int {
+ // NOTE(rsc): In theory there are approximately balanced
+ // arguments for and against including AI_ADDRCONFIG
+ // in the flags (it includes IPv4 results only on IPv4 systems,
+ // and similarly for IPv6), but in practice setting it causes
+ // getaddrinfo to return the wrong canonical name on Linux.
+ // So definitely leave it out.
return C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL
}
diff --git a/src/pkg/net/cgo_netbsd.go b/src/pkg/net/cgo_netbsd.go
new file mode 100644
index 000000000..aeaf8e568
--- /dev/null
+++ b/src/pkg/net/cgo_netbsd.go
@@ -0,0 +1,14 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+/*
+#include <netdb.h>
+*/
+import "C"
+
+func cgoAddrInfoFlags() C.int {
+ return C.AI_CANONNAME
+}
diff --git a/src/pkg/net/cgo_openbsd.go b/src/pkg/net/cgo_openbsd.go
new file mode 100644
index 000000000..aeaf8e568
--- /dev/null
+++ b/src/pkg/net/cgo_openbsd.go
@@ -0,0 +1,14 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+/*
+#include <netdb.h>
+*/
+import "C"
+
+func cgoAddrInfoFlags() C.int {
+ return C.AI_CANONNAME
+}
diff --git a/src/pkg/net/cgo_unix.go b/src/pkg/net/cgo_unix.go
index 36a3f3d34..7476140eb 100644
--- a/src/pkg/net/cgo_unix.go
+++ b/src/pkg/net/cgo_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux
+// +build darwin freebsd linux netbsd openbsd
package net
@@ -81,13 +81,7 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet
var res *C.struct_addrinfo
var hints C.struct_addrinfo
- // NOTE(rsc): In theory there are approximately balanced
- // arguments for and against including AI_ADDRCONFIG
- // in the flags (it includes IPv4 results only on IPv4 systems,
- // and similarly for IPv6), but in practice setting it causes
- // getaddrinfo to return the wrong canonical name on Linux.
- // So definitely leave it out.
- hints.ai_flags = (C.AI_ALL | C.AI_V4MAPPED | C.AI_CANONNAME) & cgoAddrInfoMask()
+ hints.ai_flags = cgoAddrInfoFlags()
h := C.CString(name)
defer C.free(unsafe.Pointer(h))
diff --git a/src/pkg/net/conn_test.go b/src/pkg/net/conn_test.go
new file mode 100644
index 000000000..fdb90862f
--- /dev/null
+++ b/src/pkg/net/conn_test.go
@@ -0,0 +1,114 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements API tests across platforms and will never have a build
+// tag.
+
+package net
+
+import (
+ "os"
+ "runtime"
+ "testing"
+ "time"
+)
+
+var connTests = []struct {
+ net string
+ addr string
+}{
+ {"tcp", "127.0.0.1:0"},
+ {"unix", testUnixAddr()},
+ {"unixpacket", testUnixAddr()},
+}
+
+// someTimeout is used just to test that net.Conn implementations
+// don't explode when their SetFooDeadline methods are called.
+// It isn't actually used for testing timeouts.
+const someTimeout = 10 * time.Second
+
+func TestConnAndListener(t *testing.T) {
+ for _, tt := range connTests {
+ switch tt.net {
+ case "unix", "unixpacket":
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ continue
+ }
+ if tt.net == "unixpacket" && runtime.GOOS != "linux" {
+ continue
+ }
+ }
+
+ ln, err := Listen(tt.net, tt.addr)
+ if err != nil {
+ t.Fatalf("Listen failed: %v", err)
+ }
+ defer func(ln Listener, net, addr string) {
+ ln.Close()
+ switch net {
+ case "unix", "unixpacket":
+ os.Remove(addr)
+ }
+ }(ln, tt.net, tt.addr)
+ ln.Addr()
+
+ done := make(chan int)
+ go transponder(t, ln, done)
+
+ c, err := Dial(tt.net, ln.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial failed: %v", err)
+ }
+ defer c.Close()
+ c.LocalAddr()
+ c.RemoteAddr()
+ c.SetDeadline(time.Now().Add(someTimeout))
+ c.SetReadDeadline(time.Now().Add(someTimeout))
+ c.SetWriteDeadline(time.Now().Add(someTimeout))
+
+ if _, err := c.Write([]byte("CONN TEST")); err != nil {
+ t.Fatalf("Conn.Write failed: %v", err)
+ }
+ rb := make([]byte, 128)
+ if _, err := c.Read(rb); err != nil {
+ t.Fatalf("Conn.Read failed: %v", err)
+ }
+
+ <-done
+ }
+}
+
+func transponder(t *testing.T, ln Listener, done chan<- int) {
+ defer func() { done <- 1 }()
+
+ switch ln := ln.(type) {
+ case *TCPListener:
+ ln.SetDeadline(time.Now().Add(someTimeout))
+ case *UnixListener:
+ ln.SetDeadline(time.Now().Add(someTimeout))
+ }
+ c, err := ln.Accept()
+ if err != nil {
+ t.Errorf("Listener.Accept failed: %v", err)
+ return
+ }
+ defer c.Close()
+ c.LocalAddr()
+ c.RemoteAddr()
+ c.SetDeadline(time.Now().Add(someTimeout))
+ c.SetReadDeadline(time.Now().Add(someTimeout))
+ c.SetWriteDeadline(time.Now().Add(someTimeout))
+
+ b := make([]byte, 128)
+ n, err := c.Read(b)
+ if err != nil {
+ t.Errorf("Conn.Read failed: %v", err)
+ return
+ }
+ if _, err := c.Write(b[:n]); err != nil {
+ t.Errorf("Conn.Write failed: %v", err)
+ return
+ }
+}
diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go
index 10ca5faf7..22e1e7dd8 100644
--- a/src/pkg/net/dial.go
+++ b/src/pkg/net/dial.go
@@ -5,15 +5,91 @@
package net
import (
+ "errors"
"time"
)
-func parseDialNetwork(net string) (afnet string, proto int, err error) {
+// A DialOption modifies a DialOpt call.
+type DialOption interface {
+ dialOption()
+}
+
+var (
+ // TCP is a dial option to dial with TCP (over IPv4 or IPv6).
+ TCP = Network("tcp")
+
+ // UDP is a dial option to dial with UDP (over IPv4 or IPv6).
+ UDP = Network("udp")
+)
+
+// Network returns a DialOption to dial using the given network.
+//
+// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
+// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
+// (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and
+// "unixpacket".
+//
+// For IP networks, net must be "ip", "ip4" or "ip6" followed
+// by a colon and a protocol number or name, such as
+// "ipv4:1" or "ip6:ospf".
+func Network(net string) DialOption {
+ return dialNetwork(net)
+}
+
+type dialNetwork string
+
+func (dialNetwork) dialOption() {}
+
+// Deadline returns a DialOption to fail a dial that doesn't
+// complete before t.
+func Deadline(t time.Time) DialOption {
+ return dialDeadline(t)
+}
+
+// Timeout returns a DialOption to fail a dial that doesn't
+// complete within the provided duration.
+func Timeout(d time.Duration) DialOption {
+ return dialDeadline(time.Now().Add(d))
+}
+
+type dialDeadline time.Time
+
+func (dialDeadline) dialOption() {}
+
+type tcpFastOpen struct{}
+
+func (tcpFastOpen) dialOption() {}
+
+// TODO(bradfitz): implement this (golang.org/issue/4842) and unexport this.
+//
+// TCPFastTimeout returns an option to use TCP Fast Open (TFO) when
+// doing this dial. It is only valid for use with TCP connections.
+// Data sent over a TFO connection may be processed by the peer
+// multiple times, so should be used with caution.
+func todo_TCPFastTimeout() DialOption {
+ return tcpFastOpen{}
+}
+
+type localAddrOption struct {
+ la Addr
+}
+
+func (localAddrOption) dialOption() {}
+
+// LocalAddress returns a dial option to perform a dial with the
+// provided local address. The address must be of a compatible type
+// for the network being dialed.
+func LocalAddress(addr Addr) DialOption {
+ return localAddrOption{addr}
+}
+
+func parseNetwork(net string) (afnet string, proto int, err error) {
i := last(net, ':')
if i < 0 { // no colon
switch net {
case "tcp", "tcp4", "tcp6":
case "udp", "udp4", "udp6":
+ case "ip", "ip4", "ip6":
case "unix", "unixgram", "unixpacket":
default:
return "", 0, UnknownNetworkError(net)
@@ -36,40 +112,27 @@ func parseDialNetwork(net string) (afnet string, proto int, err error) {
return "", 0, UnknownNetworkError(net)
}
-func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) {
- afnet, _, err = parseDialNetwork(net)
+func resolveAddr(op, net, addr string, deadline time.Time) (Addr, error) {
+ afnet, _, err := parseNetwork(net)
if err != nil {
- return "", nil, &OpError{op, net, nil, err}
+ return nil, &OpError{op, net, nil, err}
}
if op == "dial" && addr == "" {
- return "", nil, &OpError{op, net, nil, errMissingAddress}
+ return nil, &OpError{op, net, nil, errMissingAddress}
}
switch afnet {
- case "tcp", "tcp4", "tcp6":
- if addr != "" {
- a, err = ResolveTCPAddr(afnet, addr)
- }
- case "udp", "udp4", "udp6":
- if addr != "" {
- a, err = ResolveUDPAddr(afnet, addr)
- }
- case "ip", "ip4", "ip6":
- if addr != "" {
- a, err = ResolveIPAddr(afnet, addr)
- }
case "unix", "unixgram", "unixpacket":
- if addr != "" {
- a, err = ResolveUnixAddr(afnet, addr)
- }
+ return ResolveUnixAddr(afnet, addr)
}
- return
+ return resolveInternetAddr(afnet, addr, deadline)
}
// Dial connects to the address addr on the network net.
//
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
-// (IPv4-only), "ip6" (IPv6-only), "unix" and "unixpacket".
+// (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and
+// "unixpacket".
//
// For TCP and UDP networks, addresses have the form host:port.
// If host is a literal IPv6 address, it must be enclosed
@@ -81,7 +144,7 @@ func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) {
// Dial("tcp", "google.com:80")
// Dial("tcp", "[de:ad:be:ef::ca:fe]:80")
//
-// For IP networks, addr must be "ip", "ip4" or "ip6" followed
+// For IP networks, net must be "ip", "ip4" or "ip6" followed
// by a colon and a protocol number or name.
//
// Examples:
@@ -89,25 +152,71 @@ func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) {
// Dial("ip6:ospf", "::1")
//
func Dial(net, addr string) (Conn, error) {
- _, addri, err := resolveNetAddr("dial", net, addr)
+ return DialOpt(addr, dialNetwork(net))
+}
+
+func netFromOptions(opts []DialOption) string {
+ for _, opt := range opts {
+ if p, ok := opt.(dialNetwork); ok {
+ return string(p)
+ }
+ }
+ return "tcp"
+}
+
+func deadlineFromOptions(opts []DialOption) time.Time {
+ for _, opt := range opts {
+ if d, ok := opt.(dialDeadline); ok {
+ return time.Time(d)
+ }
+ }
+ return noDeadline
+}
+
+var noLocalAddr Addr // nil
+
+func localAddrFromOptions(opts []DialOption) Addr {
+ for _, opt := range opts {
+ if o, ok := opt.(localAddrOption); ok {
+ return o.la
+ }
+ }
+ return noLocalAddr
+}
+
+// DialOpt dials addr using the provided options.
+// If no options are provided, DialOpt(addr) is equivalent
+// to Dial("tcp", addr). See Dial for the syntax of addr.
+func DialOpt(addr string, opts ...DialOption) (Conn, error) {
+ net := netFromOptions(opts)
+ deadline := deadlineFromOptions(opts)
+ la := localAddrFromOptions(opts)
+ ra, err := resolveAddr("dial", net, addr, deadline)
if err != nil {
return nil, err
}
- return dialAddr(net, addr, addri)
+ return dial(net, addr, la, ra, deadline)
}
-func dialAddr(net, addr string, addri Addr) (c Conn, err error) {
- switch ra := addri.(type) {
+func dial(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error) {
+ if la != nil && la.Network() != ra.Network() {
+ return nil, &OpError{"dial", net, ra, errors.New("mismatched local addr type " + la.Network())}
+ }
+ switch ra := ra.(type) {
case *TCPAddr:
- c, err = DialTCP(net, nil, ra)
+ la, _ := la.(*TCPAddr)
+ c, err = dialTCP(net, la, ra, deadline)
case *UDPAddr:
- c, err = DialUDP(net, nil, ra)
+ la, _ := la.(*UDPAddr)
+ c, err = dialUDP(net, la, ra, deadline)
case *IPAddr:
- c, err = DialIP(net, nil, ra)
+ la, _ := la.(*IPAddr)
+ c, err = dialIP(net, la, ra, deadline)
case *UnixAddr:
- c, err = DialUnix(net, nil, ra)
+ la, _ := la.(*UnixAddr)
+ c, err = dialUnix(net, la, ra, deadline)
default:
- err = &OpError{"dial", net + " " + addr, nil, UnknownNetworkError(net)}
+ err = &OpError{"dial", net + " " + addr, ra, UnknownNetworkError(net)}
}
if err != nil {
return nil, err
@@ -118,10 +227,14 @@ func dialAddr(net, addr string, addri Addr) (c Conn, err error) {
// DialTimeout acts like Dial but takes a timeout.
// The timeout includes name resolution, if required.
func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
- // TODO(bradfitz): the timeout should be pushed down into the
- // net package's event loop, so on timeout to dead hosts we
- // don't have a goroutine sticking around for the default of
- // ~3 minutes.
+ return dialTimeout(net, addr, timeout)
+}
+
+// dialTimeoutRace is the old implementation of DialTimeout, still used
+// on operating systems where the deadline hasn't been pushed down
+// into the pollserver.
+// TODO: fix this on plan9.
+func dialTimeoutRace(net, addr string, timeout time.Duration) (Conn, error) {
t := time.NewTimer(timeout)
defer t.Stop()
type pair struct {
@@ -131,30 +244,30 @@ func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
ch := make(chan pair, 1)
resolvedAddr := make(chan Addr, 1)
go func() {
- _, addri, err := resolveNetAddr("dial", net, addr)
+ ra, err := resolveAddr("dial", net, addr, noDeadline)
if err != nil {
ch <- pair{nil, err}
return
}
- resolvedAddr <- addri // in case we need it for OpError
- c, err := dialAddr(net, addr, addri)
+ resolvedAddr <- ra // in case we need it for OpError
+ c, err := dial(net, addr, noLocalAddr, ra, noDeadline)
ch <- pair{c, err}
}()
select {
case <-t.C:
// Try to use the real Addr in our OpError, if we resolved it
// before the timeout. Otherwise we just use stringAddr.
- var addri Addr
+ var ra Addr
select {
case a := <-resolvedAddr:
- addri = a
+ ra = a
default:
- addri = &stringAddr{net, addr}
+ ra = &stringAddr{net, addr}
}
err := &OpError{
Op: "dial",
Net: net,
- Addr: addri,
+ Addr: ra,
Err: &timeoutError{},
}
return nil, err
@@ -173,24 +286,16 @@ func (a stringAddr) String() string { return a.addr }
// Listen announces on the local network address laddr.
// The network string net must be a stream-oriented network:
-// "tcp", "tcp4", "tcp6", or "unix", or "unixpacket".
+// "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
func Listen(net, laddr string) (Listener, error) {
- afnet, a, err := resolveNetAddr("listen", net, laddr)
+ la, err := resolveAddr("listen", net, laddr, noDeadline)
if err != nil {
return nil, err
}
- switch afnet {
- case "tcp", "tcp4", "tcp6":
- var la *TCPAddr
- if a != nil {
- la = a.(*TCPAddr)
- }
+ switch la := la.(type) {
+ case *TCPAddr:
return ListenTCP(net, la)
- case "unix", "unixpacket":
- var la *UnixAddr
- if a != nil {
- la = a.(*UnixAddr)
- }
+ case *UnixAddr:
return ListenUnix(net, la)
}
return nil, UnknownNetworkError(net)
@@ -199,30 +304,18 @@ func Listen(net, laddr string) (Listener, error) {
// ListenPacket announces on the local network address laddr.
// The network string net must be a packet-oriented network:
// "udp", "udp4", "udp6", "ip", "ip4", "ip6" or "unixgram".
-func ListenPacket(net, addr string) (PacketConn, error) {
- afnet, a, err := resolveNetAddr("listen", net, addr)
+func ListenPacket(net, laddr string) (PacketConn, error) {
+ la, err := resolveAddr("listen", net, laddr, noDeadline)
if err != nil {
return nil, err
}
- switch afnet {
- case "udp", "udp4", "udp6":
- var la *UDPAddr
- if a != nil {
- la = a.(*UDPAddr)
- }
+ switch la := la.(type) {
+ case *UDPAddr:
return ListenUDP(net, la)
- case "ip", "ip4", "ip6":
- var la *IPAddr
- if a != nil {
- la = a.(*IPAddr)
- }
+ case *IPAddr:
return ListenIP(net, la)
- case "unixgram":
- var la *UnixAddr
- if a != nil {
- la = a.(*UnixAddr)
- }
- return DialUnix(net, la, nil)
+ case *UnixAddr:
+ return ListenUnixgram(net, la)
}
return nil, UnknownNetworkError(net)
}
diff --git a/src/pkg/net/dial_test.go b/src/pkg/net/dial_test.go
index 7212087fe..2303e8fa4 100644
--- a/src/pkg/net/dial_test.go
+++ b/src/pkg/net/dial_test.go
@@ -7,6 +7,9 @@ package net
import (
"flag"
"fmt"
+ "io"
+ "os"
+ "reflect"
"regexp"
"runtime"
"testing"
@@ -55,7 +58,7 @@ func TestDialTimeout(t *testing.T) {
// on our 386 builder, this Dial succeeds, connecting
// to an IIS web server somewhere. The data center
// or VM or firewall must be stealing the TCP connection.
- //
+ //
// IANA Service Name and Transport Protocol Port Number Registry
// <http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml>
go func() {
@@ -72,8 +75,7 @@ func TestDialTimeout(t *testing.T) {
// by default. FreeBSD likely works, but is untested.
// TODO(rsc):
// The timeout never happens on Windows. Why? Issue 3016.
- t.Logf("skipping test on %q; untested.", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q; untested.", runtime.GOOS)
}
connected := 0
@@ -105,8 +107,7 @@ func TestDialTimeout(t *testing.T) {
func TestSelfConnect(t *testing.T) {
if runtime.GOOS == "windows" {
// TODO(brainman): do not know why it hangs.
- t.Logf("skipping known-broken test on windows")
- return
+ t.Skip("skipping known-broken test on windows")
}
// Test that Dial does not honor self-connects.
// See the comment in DialTCP.
@@ -130,7 +131,7 @@ func TestSelfConnect(t *testing.T) {
n = 1000
}
switch runtime.GOOS {
- case "darwin", "freebsd", "openbsd", "windows":
+ case "darwin", "freebsd", "netbsd", "openbsd", "plan9", "windows":
// Non-Linux systems take a long time to figure
// out that there is nothing listening on localhost.
n = 100
@@ -222,3 +223,104 @@ func TestDialError(t *testing.T) {
}
}
}
+
+var invalidDialAndListenArgTests = []struct {
+ net string
+ addr string
+ err error
+}{
+ {"foo", "bar", &OpError{Op: "dial", Net: "foo", Addr: nil, Err: UnknownNetworkError("foo")}},
+ {"baz", "", &OpError{Op: "listen", Net: "baz", Addr: nil, Err: UnknownNetworkError("baz")}},
+ {"tcp", "", &OpError{Op: "dial", Net: "tcp", Addr: nil, Err: errMissingAddress}},
+}
+
+func TestInvalidDialAndListenArgs(t *testing.T) {
+ for _, tt := range invalidDialAndListenArgTests {
+ var err error
+ switch tt.err.(*OpError).Op {
+ case "dial":
+ _, err = Dial(tt.net, tt.addr)
+ case "listen":
+ _, err = Listen(tt.net, tt.addr)
+ }
+ if !reflect.DeepEqual(tt.err, err) {
+ t.Fatalf("got %#v; expected %#v", err, tt.err)
+ }
+ }
+}
+
+func TestDialTimeoutFDLeak(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ // TODO(bradfitz): test on other platforms
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ ln := newLocalListener(t)
+ defer ln.Close()
+
+ type connErr struct {
+ conn Conn
+ err error
+ }
+ dials := listenerBacklog + 100
+ // used to be listenerBacklog + 5, but was found to be unreliable, issue 4384.
+ maxGoodConnect := listenerBacklog + runtime.NumCPU()*10
+ resc := make(chan connErr)
+ for i := 0; i < dials; i++ {
+ go func() {
+ conn, err := DialTimeout("tcp", ln.Addr().String(), 500*time.Millisecond)
+ resc <- connErr{conn, err}
+ }()
+ }
+
+ var firstErr string
+ var ngood int
+ var toClose []io.Closer
+ for i := 0; i < dials; i++ {
+ ce := <-resc
+ if ce.err == nil {
+ ngood++
+ if ngood > maxGoodConnect {
+ t.Errorf("%d good connects; expected at most %d", ngood, maxGoodConnect)
+ }
+ toClose = append(toClose, ce.conn)
+ continue
+ }
+ err := ce.err
+ if firstErr == "" {
+ firstErr = err.Error()
+ } else if err.Error() != firstErr {
+ t.Fatalf("inconsistent error messages: first was %q, then later %q", firstErr, err)
+ }
+ }
+ for _, c := range toClose {
+ c.Close()
+ }
+ for i := 0; i < 100; i++ {
+ if got := numFD(); got < dials {
+ // Test passes.
+ return
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+ if got := numFD(); got >= dials {
+ t.Errorf("num fds after %d timeouts = %d; want <%d", dials, got, dials)
+ }
+}
+
+func numFD() int {
+ if runtime.GOOS == "linux" {
+ f, err := os.Open("/proc/self/fd")
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+ names, err := f.Readdirnames(0)
+ if err != nil {
+ panic(err)
+ }
+ return len(names)
+ }
+ // All tests using this should be skipped anyway, but:
+ panic("numFDs not implemented on " + runtime.GOOS)
+}
diff --git a/src/pkg/net/dialgoogle_test.go b/src/pkg/net/dialgoogle_test.go
index 03c449972..73a94f5bf 100644
--- a/src/pkg/net/dialgoogle_test.go
+++ b/src/pkg/net/dialgoogle_test.go
@@ -41,17 +41,6 @@ func doDial(t *testing.T, network, addr string) {
fd.Close()
}
-func TestLookupCNAME(t *testing.T) {
- if testing.Short() || !*testExternal {
- t.Logf("skipping test to avoid external network")
- return
- }
- cname, err := LookupCNAME("www.google.com")
- if !strings.HasSuffix(cname, ".l.google.com.") || err != nil {
- t.Errorf(`LookupCNAME("www.google.com.") = %q, %v, want "*.l.google.com.", nil`, cname, err)
- }
-}
-
var googleaddrsipv4 = []string{
"%d.%d.%d.%d:80",
"www.google.com:80",
@@ -67,8 +56,7 @@ var googleaddrsipv4 = []string{
func TestDialGoogleIPv4(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Logf("skipping test to avoid external network")
- return
+ t.Skip("skipping test to avoid external network")
}
// Insert an actual IPv4 address for google.com
@@ -123,12 +111,14 @@ var googleaddrsipv6 = []string{
func TestDialGoogleIPv6(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Logf("skipping test to avoid external network")
- return
+ t.Skip("skipping test to avoid external network")
}
// Only run tcp6 if the kernel will take it.
- if !*testIPv6 || !supportsIPv6 {
- return
+ if !supportsIPv6 {
+ t.Skip("skipping test; ipv6 is not supported")
+ }
+ if !*testIPv6 {
+ t.Skip("test disabled; use -ipv6 to enable")
}
// Insert an actual IPv6 address for ipv6.google.com
diff --git a/src/pkg/net/dnsclient.go b/src/pkg/net/dnsclient.go
index e69cb3188..76b192645 100644
--- a/src/pkg/net/dnsclient.go
+++ b/src/pkg/net/dnsclient.go
@@ -183,7 +183,7 @@ func (s byPriorityWeight) Less(i, j int) bool {
}
// shuffleByWeight shuffles SRV records by weight using the algorithm
-// described in RFC 2782.
+// described in RFC 2782.
func (addrs byPriorityWeight) shuffleByWeight() {
sum := 0
for _, addr := range addrs {
@@ -244,3 +244,8 @@ func (s byPref) sort() {
}
sort.Sort(s)
}
+
+// An NS represents a single DNS NS record.
+type NS struct {
+ Host string
+}
diff --git a/src/pkg/net/dnsclient_unix.go b/src/pkg/net/dnsclient_unix.go
index 18c39360e..9e21bb4a0 100644
--- a/src/pkg/net/dnsclient_unix.go
+++ b/src/pkg/net/dnsclient_unix.go
@@ -237,24 +237,30 @@ func goLookupIP(name string) (addrs []IP, err error) {
}
var records []dnsRR
var cname string
- cname, records, err = lookup(name, dnsTypeA)
- if err != nil {
- return
- }
+ var err4, err6 error
+ cname, records, err4 = lookup(name, dnsTypeA)
addrs = convertRR_A(records)
if cname != "" {
name = cname
}
- _, records, err = lookup(name, dnsTypeAAAA)
- if err != nil && len(addrs) > 0 {
- // Ignore error because A lookup succeeded.
- err = nil
+ _, records, err6 = lookup(name, dnsTypeAAAA)
+ if err4 != nil && err6 == nil {
+ // Ignore A error because AAAA lookup succeeded.
+ err4 = nil
}
- if err != nil {
- return
+ if err6 != nil && len(addrs) > 0 {
+ // Ignore AAAA error because A lookup succeeded.
+ err6 = nil
}
+ if err4 != nil {
+ return nil, err4
+ }
+ if err6 != nil {
+ return nil, err6
+ }
+
addrs = append(addrs, convertRR_AAAA(records)...)
- return
+ return addrs, nil
}
// goLookupCNAME is the native Go implementation of LookupCNAME.
diff --git a/src/pkg/net/dnsconfig.go b/src/pkg/net/dnsconfig_unix.go
index bb46cc900..bb46cc900 100644
--- a/src/pkg/net/dnsconfig.go
+++ b/src/pkg/net/dnsconfig_unix.go
diff --git a/src/pkg/net/dnsmsg.go b/src/pkg/net/dnsmsg.go
index b6ebe1173..161afb2a5 100644
--- a/src/pkg/net/dnsmsg.go
+++ b/src/pkg/net/dnsmsg.go
@@ -618,7 +618,7 @@ func printStruct(any dnsStruct) string {
s += name + "="
switch tag {
case "ipv4":
- i := val.(uint32)
+ i := *val.(*uint32)
s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String()
case "ipv6":
i := val.([]byte)
diff --git a/src/pkg/net/example_test.go b/src/pkg/net/example_test.go
index 1a1c2edfe..eefe84fa7 100644
--- a/src/pkg/net/example_test.go
+++ b/src/pkg/net/example_test.go
@@ -17,7 +17,7 @@ func ExampleListener() {
log.Fatal(err)
}
for {
- // Wait for a connection.
+ // Wait for a connection.
conn, err := l.Accept()
if err != nil {
log.Fatal(err)
diff --git a/src/pkg/net/fd_netbsd.go b/src/pkg/net/fd_bsd.go
index 35d84c30e..8bb1ae538 100644
--- a/src/pkg/net/fd_netbsd.go
+++ b/src/pkg/net/fd_bsd.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 freebsd netbsd openbsd
+
// Waiting for FDs via kqueue/kevent.
package net
@@ -31,6 +33,8 @@ func newpollster() (p *pollster, err error) {
return p, nil
}
+// First return value is whether the pollServer should be woken up.
+// This version always returns false.
func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {
// pollServer is locked.
@@ -62,7 +66,9 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {
return false, nil
}
-func (p *pollster) DelFD(fd int, mode int) {
+// Return value is whether the pollServer should be woken up.
+// This version always returns false.
+func (p *pollster) DelFD(fd int, mode int) bool {
// pollServer is locked.
var kmode int
@@ -75,6 +81,7 @@ func (p *pollster) DelFD(fd int, mode int) {
// EV_DELETE - delete event from kqueue list
syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE)
syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
+ return false
}
func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) {
diff --git a/src/pkg/net/fd_darwin.go b/src/pkg/net/fd_darwin.go
index 3dd33edc2..382465ba6 100644
--- a/src/pkg/net/fd_darwin.go
+++ b/src/pkg/net/fd_darwin.go
@@ -32,6 +32,8 @@ func newpollster() (p *pollster, err error) {
return p, nil
}
+// First return value is whether the pollServer should be woken up.
+// This version always returns false.
func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {
// pollServer is locked.
@@ -65,7 +67,9 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {
return false, nil
}
-func (p *pollster) DelFD(fd int, mode int) {
+// Return value is whether the pollServer should be woken up.
+// This version always returns false.
+func (p *pollster) DelFD(fd int, mode int) bool {
// pollServer is locked.
var kmode int
@@ -80,6 +84,7 @@ func (p *pollster) DelFD(fd int, mode int) {
// rather than waiting for real event
syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE|syscall.EV_RECEIPT)
syscall.Kevent(p.kq, p.kbuf[0:], p.kbuf[0:], nil)
+ return false
}
func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) {
diff --git a/src/pkg/net/fd_freebsd.go b/src/pkg/net/fd_freebsd.go
deleted file mode 100644
index 35d84c30e..000000000
--- a/src/pkg/net/fd_freebsd.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Waiting for FDs via kqueue/kevent.
-
-package net
-
-import (
- "os"
- "syscall"
-)
-
-type pollster struct {
- kq int
- eventbuf [10]syscall.Kevent_t
- events []syscall.Kevent_t
-
- // An event buffer for AddFD/DelFD.
- // Must hold pollServer lock.
- kbuf [1]syscall.Kevent_t
-}
-
-func newpollster() (p *pollster, err error) {
- p = new(pollster)
- if p.kq, err = syscall.Kqueue(); err != nil {
- return nil, os.NewSyscallError("kqueue", err)
- }
- syscall.CloseOnExec(p.kq)
- p.events = p.eventbuf[0:0]
- return p, nil
-}
-
-func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {
- // pollServer is locked.
-
- var kmode int
- if mode == 'r' {
- kmode = syscall.EVFILT_READ
- } else {
- kmode = syscall.EVFILT_WRITE
- }
- ev := &p.kbuf[0]
- // EV_ADD - add event to kqueue list
- // EV_ONESHOT - delete the event the first time it triggers
- flags := syscall.EV_ADD
- if !repeat {
- flags |= syscall.EV_ONESHOT
- }
- syscall.SetKevent(ev, fd, kmode, flags)
-
- n, err := syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
- if err != nil {
- return false, os.NewSyscallError("kevent", err)
- }
- if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {
- return false, os.NewSyscallError("kqueue phase error", err)
- }
- if ev.Data != 0 {
- return false, syscall.Errno(int(ev.Data))
- }
- return false, nil
-}
-
-func (p *pollster) DelFD(fd int, mode int) {
- // pollServer is locked.
-
- var kmode int
- if mode == 'r' {
- kmode = syscall.EVFILT_READ
- } else {
- kmode = syscall.EVFILT_WRITE
- }
- ev := &p.kbuf[0]
- // EV_DELETE - delete event from kqueue list
- syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE)
- syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
-}
-
-func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) {
- var t *syscall.Timespec
- for len(p.events) == 0 {
- if nsec > 0 {
- if t == nil {
- t = new(syscall.Timespec)
- }
- *t = syscall.NsecToTimespec(nsec)
- }
-
- s.Unlock()
- n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t)
- s.Lock()
-
- if err != nil {
- if err == syscall.EINTR {
- continue
- }
- return -1, 0, os.NewSyscallError("kevent", err)
- }
- if n == 0 {
- return -1, 0, nil
- }
- p.events = p.eventbuf[:n]
- }
- ev := &p.events[0]
- p.events = p.events[1:]
- fd = int(ev.Ident)
- if ev.Filter == syscall.EVFILT_READ {
- mode = 'r'
- } else {
- mode = 'w'
- }
- return fd, mode, nil
-}
-
-func (p *pollster) Close() error { return os.NewSyscallError("close", syscall.Close(p.kq)) }
diff --git a/src/pkg/net/fd_linux.go b/src/pkg/net/fd_linux.go
index 085e42307..03679196d 100644
--- a/src/pkg/net/fd_linux.go
+++ b/src/pkg/net/fd_linux.go
@@ -51,6 +51,8 @@ func newpollster() (p *pollster, err error) {
return p, nil
}
+// First return value is whether the pollServer should be woken up.
+// This version always returns false.
func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {
// pollServer is locked.
@@ -114,7 +116,9 @@ func (p *pollster) StopWaiting(fd int, bits uint) {
}
}
-func (p *pollster) DelFD(fd int, mode int) {
+// Return value is whether the pollServer should be woken up.
+// This version always returns false.
+func (p *pollster) DelFD(fd int, mode int) bool {
// pollServer is locked.
if mode == 'r' {
@@ -133,6 +137,7 @@ func (p *pollster) DelFD(fd int, mode int) {
i++
}
}
+ return false
}
func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) {
diff --git a/src/pkg/net/fd_openbsd.go b/src/pkg/net/fd_openbsd.go
deleted file mode 100644
index 35d84c30e..000000000
--- a/src/pkg/net/fd_openbsd.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Waiting for FDs via kqueue/kevent.
-
-package net
-
-import (
- "os"
- "syscall"
-)
-
-type pollster struct {
- kq int
- eventbuf [10]syscall.Kevent_t
- events []syscall.Kevent_t
-
- // An event buffer for AddFD/DelFD.
- // Must hold pollServer lock.
- kbuf [1]syscall.Kevent_t
-}
-
-func newpollster() (p *pollster, err error) {
- p = new(pollster)
- if p.kq, err = syscall.Kqueue(); err != nil {
- return nil, os.NewSyscallError("kqueue", err)
- }
- syscall.CloseOnExec(p.kq)
- p.events = p.eventbuf[0:0]
- return p, nil
-}
-
-func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {
- // pollServer is locked.
-
- var kmode int
- if mode == 'r' {
- kmode = syscall.EVFILT_READ
- } else {
- kmode = syscall.EVFILT_WRITE
- }
- ev := &p.kbuf[0]
- // EV_ADD - add event to kqueue list
- // EV_ONESHOT - delete the event the first time it triggers
- flags := syscall.EV_ADD
- if !repeat {
- flags |= syscall.EV_ONESHOT
- }
- syscall.SetKevent(ev, fd, kmode, flags)
-
- n, err := syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
- if err != nil {
- return false, os.NewSyscallError("kevent", err)
- }
- if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {
- return false, os.NewSyscallError("kqueue phase error", err)
- }
- if ev.Data != 0 {
- return false, syscall.Errno(int(ev.Data))
- }
- return false, nil
-}
-
-func (p *pollster) DelFD(fd int, mode int) {
- // pollServer is locked.
-
- var kmode int
- if mode == 'r' {
- kmode = syscall.EVFILT_READ
- } else {
- kmode = syscall.EVFILT_WRITE
- }
- ev := &p.kbuf[0]
- // EV_DELETE - delete event from kqueue list
- syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE)
- syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
-}
-
-func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) {
- var t *syscall.Timespec
- for len(p.events) == 0 {
- if nsec > 0 {
- if t == nil {
- t = new(syscall.Timespec)
- }
- *t = syscall.NsecToTimespec(nsec)
- }
-
- s.Unlock()
- n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t)
- s.Lock()
-
- if err != nil {
- if err == syscall.EINTR {
- continue
- }
- return -1, 0, os.NewSyscallError("kevent", err)
- }
- if n == 0 {
- return -1, 0, nil
- }
- p.events = p.eventbuf[:n]
- }
- ev := &p.events[0]
- p.events = p.events[1:]
- fd = int(ev.Ident)
- if ev.Filter == syscall.EVFILT_READ {
- mode = 'r'
- } else {
- mode = 'w'
- }
- return fd, mode, nil
-}
-
-func (p *pollster) Close() error { return os.NewSyscallError("close", syscall.Close(p.kq)) }
diff --git a/src/pkg/net/fd_plan9.go b/src/pkg/net/fd_plan9.go
new file mode 100644
index 000000000..169087999
--- /dev/null
+++ b/src/pkg/net/fd_plan9.go
@@ -0,0 +1,129 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "io"
+ "os"
+ "syscall"
+ "time"
+)
+
+// Network file descritor.
+type netFD struct {
+ proto, name, dir string
+ ctl, data *os.File
+ laddr, raddr Addr
+}
+
+var canCancelIO = true // used for testing current package
+
+func sysInit() {
+}
+
+func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
+ // On plan9, use the relatively inefficient
+ // goroutine-racing implementation.
+ return dialTimeoutRace(net, addr, timeout)
+}
+
+func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) *netFD {
+ return &netFD{proto, name, "/net/" + proto + "/" + name, ctl, data, laddr, raddr}
+}
+
+func (fd *netFD) ok() bool { return fd != nil && fd.ctl != nil }
+
+func (fd *netFD) Read(b []byte) (n int, err error) {
+ if !fd.ok() || fd.data == nil {
+ return 0, syscall.EINVAL
+ }
+ n, err = fd.data.Read(b)
+ if fd.proto == "udp" && err == io.EOF {
+ n = 0
+ err = nil
+ }
+ return
+}
+
+func (fd *netFD) Write(b []byte) (n int, err error) {
+ if !fd.ok() || fd.data == nil {
+ return 0, syscall.EINVAL
+ }
+ return fd.data.Write(b)
+}
+
+func (fd *netFD) CloseRead() error {
+ if !fd.ok() {
+ return syscall.EINVAL
+ }
+ return syscall.EPLAN9
+}
+
+func (fd *netFD) CloseWrite() error {
+ if !fd.ok() {
+ return syscall.EINVAL
+ }
+ return syscall.EPLAN9
+}
+
+func (fd *netFD) Close() error {
+ if !fd.ok() {
+ return syscall.EINVAL
+ }
+ err := fd.ctl.Close()
+ if fd.data != nil {
+ if err1 := fd.data.Close(); err1 != nil && err == nil {
+ err = err1
+ }
+ }
+ fd.ctl = nil
+ fd.data = nil
+ return err
+}
+
+// This method is only called via Conn.
+func (fd *netFD) dup() (*os.File, error) {
+ if !fd.ok() || fd.data == nil {
+ return nil, syscall.EINVAL
+ }
+ return fd.file(fd.data, fd.dir+"/data")
+}
+
+func (l *TCPListener) dup() (*os.File, error) {
+ if !l.fd.ok() {
+ return nil, syscall.EINVAL
+ }
+ return l.fd.file(l.fd.ctl, l.fd.dir+"/ctl")
+}
+
+func (fd *netFD) file(f *os.File, s string) (*os.File, error) {
+ syscall.ForkLock.RLock()
+ dfd, err := syscall.Dup(int(f.Fd()), -1)
+ syscall.ForkLock.RUnlock()
+ if err != nil {
+ return nil, &OpError{"dup", s, fd.laddr, err}
+ }
+ return os.NewFile(uintptr(dfd), s), nil
+}
+
+func setDeadline(fd *netFD, t time.Time) error {
+ return syscall.EPLAN9
+}
+
+func setReadDeadline(fd *netFD, t time.Time) error {
+ return syscall.EPLAN9
+}
+
+func setWriteDeadline(fd *netFD, t time.Time) error {
+ return syscall.EPLAN9
+}
+
+func setReadBuffer(fd *netFD, bytes int) error {
+ return syscall.EPLAN9
+}
+
+func setWriteBuffer(fd *netFD, bytes int) error {
+ return syscall.EPLAN9
+}
diff --git a/src/pkg/net/fd_posix_test.go b/src/pkg/net/fd_posix_test.go
new file mode 100644
index 000000000..8be0335d6
--- /dev/null
+++ b/src/pkg/net/fd_posix_test.go
@@ -0,0 +1,57 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd windows
+
+package net
+
+import (
+ "testing"
+ "time"
+)
+
+var deadlineSetTimeTests = []struct {
+ input time.Time
+ expected int64
+}{
+ {time.Time{}, 0},
+ {time.Date(2009, 11, 10, 23, 00, 00, 00, time.UTC), 1257894000000000000}, // 2009-11-10 23:00:00 +0000 UTC
+}
+
+func TestDeadlineSetTime(t *testing.T) {
+ for _, tt := range deadlineSetTimeTests {
+ var d deadline
+ d.setTime(tt.input)
+ actual := d.value()
+ expected := int64(0)
+ if !tt.input.IsZero() {
+ expected = tt.input.UnixNano()
+ }
+ if actual != expected {
+ t.Errorf("set/value failed: expected %v, actual %v", expected, actual)
+ }
+ }
+}
+
+var deadlineExpiredTests = []struct {
+ deadline time.Time
+ expired bool
+}{
+ // note, times are relative to the start of the test run, not
+ // the start of TestDeadlineExpired
+ {time.Now().Add(5 * time.Minute), false},
+ {time.Now().Add(-5 * time.Minute), true},
+ {time.Time{}, false}, // no deadline set
+}
+
+func TestDeadlineExpired(t *testing.T) {
+ for _, tt := range deadlineExpiredTests {
+ var d deadline
+ d.set(tt.deadline.UnixNano())
+ expired := d.expired()
+ if expired != tt.expired {
+ t.Errorf("expire failed: expected %v, actual %v", tt.expired, expired)
+ }
+ }
+}
diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd_unix.go
index 76c953b9b..0540df825 100644
--- a/src/pkg/net/fd.go
+++ b/src/pkg/net/fd_unix.go
@@ -7,9 +7,9 @@
package net
import (
- "errors"
"io"
"os"
+ "runtime"
"sync"
"syscall"
"time"
@@ -37,44 +37,24 @@ type netFD struct {
laddr Addr
raddr Addr
- // owned by client
- rdeadline int64
- rio sync.Mutex
- wdeadline int64
- wio sync.Mutex
+ // serialize access to Read and Write methods
+ rio, wio sync.Mutex
+
+ // read and write deadlines
+ rdeadline, wdeadline deadline
// owned by fd wait server
ncr, ncw int
+
+ // wait server
+ pollServer *pollServer
}
// A pollServer helps FDs determine when to retry a non-blocking
// read or write after they get EAGAIN. When an FD needs to wait,
-// send the fd on s.cr (for a read) or s.cw (for a write) to pass the
-// request to the poll server. Then receive on fd.cr/fd.cw.
+// call s.WaitRead() or s.WaitWrite() to pass the request to the poll server.
// When the pollServer finds that i/o on FD should be possible
-// again, it will send fd on fd.cr/fd.cw to wake any waiting processes.
-// This protocol is implemented as s.WaitRead() and s.WaitWrite().
-//
-// There is one subtlety: when sending on s.cr/s.cw, the
-// poll server is probably in a system call, waiting for an fd
-// to become ready. It's not looking at the request channels.
-// To resolve this, the poll server waits not just on the FDs it has
-// been given but also its own pipe. After sending on the
-// buffered channel s.cr/s.cw, WaitRead/WaitWrite writes a
-// byte to the pipe, causing the pollServer's poll system call to
-// return. In response to the pipe being readable, the pollServer
-// re-polls its request channels.
-//
-// Note that the ordering is "send request" and then "wake up server".
-// If the operations were reversed, there would be a race: the poll
-// server might wake up and look at the request channel, see that it
-// was empty, and go back to sleep, all before the requester managed
-// to send the request. Because the send must complete before the wakeup,
-// the request channel must be buffered. A buffer of size 1 is sufficient
-// for any request load. If many processes are trying to submit requests,
-// one will succeed, the pollServer will read the request, and then the
-// channel will be empty for the next process's request. A larger buffer
-// might help batch requests.
+// again, it will send on fd.cr/fd.cw to wake any waiting goroutines.
//
// To avoid races in closing, all fd operations are locked and
// refcounted. when netFD.Close() is called, it calls syscall.Shutdown
@@ -82,7 +62,6 @@ type netFD struct {
// will the fd be closed.
type pollServer struct {
- cr, cw chan *netFD // buffered >= 1
pr, pw *os.File
poll *pollster // low-level OS hooks
sync.Mutex // controls pending and deadline
@@ -103,11 +82,11 @@ func (s *pollServer) AddFD(fd *netFD, mode int) error {
key := intfd << 1
if mode == 'r' {
fd.ncr++
- t = fd.rdeadline
+ t = fd.rdeadline.value()
} else {
fd.ncw++
key++
- t = fd.wdeadline
+ t = fd.wdeadline.value()
}
s.pending[key] = fd
doWakeup := false
@@ -117,15 +96,11 @@ func (s *pollServer) AddFD(fd *netFD, mode int) error {
}
wake, err := s.poll.AddFD(intfd, mode, false)
+ s.Unlock()
if err != nil {
- panic("pollServer AddFD " + err.Error())
- }
- if wake {
- doWakeup = true
+ return &OpError{"addfd", fd.net, fd.laddr, err}
}
- s.Unlock()
-
- if doWakeup {
+ if wake || doWakeup {
s.Wakeup()
}
return nil
@@ -134,17 +109,24 @@ func (s *pollServer) AddFD(fd *netFD, mode int) error {
// Evict evicts fd from the pending list, unblocking
// any I/O running on fd. The caller must have locked
// pollserver.
-func (s *pollServer) Evict(fd *netFD) {
+// Return value is whether the pollServer should be woken up.
+func (s *pollServer) Evict(fd *netFD) bool {
+ doWakeup := false
if s.pending[fd.sysfd<<1] == fd {
s.WakeFD(fd, 'r', errClosing)
- s.poll.DelFD(fd.sysfd, 'r')
+ if s.poll.DelFD(fd.sysfd, 'r') {
+ doWakeup = true
+ }
delete(s.pending, fd.sysfd<<1)
}
if s.pending[fd.sysfd<<1|1] == fd {
s.WakeFD(fd, 'w', errClosing)
- s.poll.DelFD(fd.sysfd, 'w')
+ if s.poll.DelFD(fd.sysfd, 'w') {
+ doWakeup = true
+ }
delete(s.pending, fd.sysfd<<1|1)
}
+ return doWakeup
}
var wakeupbuf [1]byte
@@ -178,16 +160,12 @@ func (s *pollServer) WakeFD(fd *netFD, mode int, err error) {
}
}
-func (s *pollServer) Now() int64 {
- return time.Now().UnixNano()
-}
-
func (s *pollServer) CheckDeadlines() {
- now := s.Now()
+ now := time.Now().UnixNano()
// TODO(rsc): This will need to be handled more efficiently,
// probably with a heap indexed by wakeup time.
- var next_deadline int64
+ var nextDeadline int64
for key, fd := range s.pending {
var t int64
var mode int
@@ -197,27 +175,21 @@ func (s *pollServer) CheckDeadlines() {
mode = 'w'
}
if mode == 'r' {
- t = fd.rdeadline
+ t = fd.rdeadline.value()
} else {
- t = fd.wdeadline
+ t = fd.wdeadline.value()
}
if t > 0 {
if t <= now {
delete(s.pending, key)
- if mode == 'r' {
- s.poll.DelFD(fd.sysfd, mode)
- fd.rdeadline = -1
- } else {
- s.poll.DelFD(fd.sysfd, mode)
- fd.wdeadline = -1
- }
- s.WakeFD(fd, mode, nil)
- } else if next_deadline == 0 || t < next_deadline {
- next_deadline = t
+ s.poll.DelFD(fd.sysfd, mode)
+ s.WakeFD(fd, mode, errTimeout)
+ } else if nextDeadline == 0 || t < nextDeadline {
+ nextDeadline = t
}
}
}
- s.deadline = next_deadline
+ s.deadline = nextDeadline
}
func (s *pollServer) Run() {
@@ -225,15 +197,15 @@ func (s *pollServer) Run() {
s.Lock()
defer s.Unlock()
for {
- var t = s.deadline
- if t > 0 {
- t = t - s.Now()
- if t <= 0 {
+ var timeout int64 // nsec to wait for or 0 for none
+ if s.deadline > 0 {
+ timeout = s.deadline - time.Now().UnixNano()
+ if timeout <= 0 {
s.CheckDeadlines()
continue
}
}
- fd, mode, err := s.poll.WaitFD(s, t)
+ fd, mode, err := s.poll.WaitFD(s, timeout)
if err != nil {
print("pollServer WaitFD: ", err.Error(), "\n")
return
@@ -279,24 +251,56 @@ func (s *pollServer) WaitWrite(fd *netFD) error {
}
// Network FD methods.
-// All the network FDs use a single pollServer.
+// Spread network FDs over several pollServers.
+
+var pollMaxN int
+var pollservers []*pollServer
+var startServersOnce []func()
+
+var canCancelIO = true // used for testing current package
-var pollserver *pollServer
-var onceStartServer sync.Once
+func sysInit() {
+ pollMaxN = runtime.NumCPU()
+ if pollMaxN > 8 {
+ pollMaxN = 8 // No improvement then.
+ }
+ pollservers = make([]*pollServer, pollMaxN)
+ startServersOnce = make([]func(), pollMaxN)
+ for i := 0; i < pollMaxN; i++ {
+ k := i
+ once := new(sync.Once)
+ startServersOnce[i] = func() { once.Do(func() { startServer(k) }) }
+ }
+}
-func startServer() {
+func startServer(k int) {
p, err := newPollServer()
if err != nil {
- print("Start pollServer: ", err.Error(), "\n")
+ panic(err)
}
- pollserver = p
+ pollservers[k] = p
}
-func newFD(fd, family, sotype int, net string) (*netFD, error) {
- onceStartServer.Do(startServer)
- if err := syscall.SetNonblock(fd, true); err != nil {
+func server(fd int) *pollServer {
+ pollN := runtime.GOMAXPROCS(0)
+ if pollN > pollMaxN {
+ pollN = pollMaxN
+ }
+ k := fd % pollN
+ startServersOnce[k]()
+ return pollservers[k]
+}
+
+func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
+ deadline := time.Now().Add(timeout)
+ ra, err := resolveAddr("dial", net, addr, deadline)
+ if err != nil {
return nil, err
}
+ return dial(net, addr, noLocalAddr, ra, deadline)
+}
+
+func newFD(fd, family, sotype int, net string) (*netFD, error) {
netfd := &netFD{
sysfd: fd,
family: family,
@@ -305,26 +309,31 @@ func newFD(fd, family, sotype int, net string) (*netFD, error) {
}
netfd.cr = make(chan error, 1)
netfd.cw = make(chan error, 1)
+ netfd.pollServer = server(fd)
return netfd, nil
}
func (fd *netFD) setAddr(laddr, raddr Addr) {
fd.laddr = laddr
fd.raddr = raddr
+ fd.sysfile = os.NewFile(uintptr(fd.sysfd), fd.net)
+}
+
+func (fd *netFD) name() string {
var ls, rs string
- if laddr != nil {
- ls = laddr.String()
+ if fd.laddr != nil {
+ ls = fd.laddr.String()
}
- if raddr != nil {
- rs = raddr.String()
+ if fd.raddr != nil {
+ rs = fd.raddr.String()
}
- fd.sysfile = os.NewFile(uintptr(fd.sysfd), fd.net+":"+ls+"->"+rs)
+ return fd.net + ":" + ls + "->" + rs
}
func (fd *netFD) connect(ra syscall.Sockaddr) error {
err := syscall.Connect(fd.sysfd, ra)
if err == syscall.EINPROGRESS {
- if err = pollserver.WaitWrite(fd); err != nil {
+ if err = fd.pollServer.WaitWrite(fd); err != nil {
return err
}
var e int
@@ -339,15 +348,10 @@ func (fd *netFD) connect(ra syscall.Sockaddr) error {
return err
}
-var errClosing = errors.New("use of closed network connection")
-
// Add a reference to this fd.
// If closing==true, pollserver must be locked; mark the fd as closing.
// Returns an error if the fd cannot be used.
func (fd *netFD) incref(closing bool) error {
- if fd == nil {
- return errClosing
- }
fd.sysmu.Lock()
if fd.closing {
fd.sysmu.Unlock()
@@ -364,9 +368,6 @@ func (fd *netFD) incref(closing bool) error {
// Remove a reference to this FD and close if we've been asked to do so (and
// there are no references left.
func (fd *netFD) decref() {
- if fd == nil {
- return
- }
fd.sysmu.Lock()
fd.sysref--
if fd.closing && fd.sysref == 0 && fd.sysfile != nil {
@@ -378,9 +379,9 @@ func (fd *netFD) decref() {
}
func (fd *netFD) Close() error {
- pollserver.Lock() // needed for both fd.incref(true) and pollserver.Evict
- defer pollserver.Unlock()
+ fd.pollServer.Lock() // needed for both fd.incref(true) and pollserver.Evict
if err := fd.incref(true); err != nil {
+ fd.pollServer.Unlock()
return err
}
// Unblock any I/O. Once it all unblocks and returns,
@@ -388,8 +389,12 @@ func (fd *netFD) Close() error {
// the final decref will close fd.sysfd. This should happen
// fairly quickly, since all the I/O is non-blocking, and any
// attempts to block in the pollserver will return errClosing.
- pollserver.Evict(fd)
+ doWakeup := fd.pollServer.Evict(fd)
+ fd.pollServer.Unlock()
fd.decref()
+ if doWakeup {
+ fd.pollServer.Wakeup()
+ }
return nil
}
@@ -421,20 +426,20 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
}
defer fd.decref()
for {
- n, err = syscall.Read(int(fd.sysfd), p)
- if err == syscall.EAGAIN {
+ if fd.rdeadline.expired() {
err = errTimeout
- if fd.rdeadline >= 0 {
- if err = pollserver.WaitRead(fd); err == nil {
- continue
- }
- }
+ break
}
+ n, err = syscall.Read(int(fd.sysfd), p)
if err != nil {
n = 0
- } else if n == 0 && err == nil && fd.sotype != syscall.SOCK_DGRAM {
- err = io.EOF
+ if err == syscall.EAGAIN {
+ if err = fd.pollServer.WaitRead(fd); err == nil {
+ continue
+ }
+ }
}
+ err = chkReadErr(n, err, fd)
break
}
if err != nil && err != io.EOF {
@@ -451,18 +456,20 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
}
defer fd.decref()
for {
- n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0)
- if err == syscall.EAGAIN {
+ if fd.rdeadline.expired() {
err = errTimeout
- if fd.rdeadline >= 0 {
- if err = pollserver.WaitRead(fd); err == nil {
- continue
- }
- }
+ break
}
+ n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0)
if err != nil {
n = 0
+ if err == syscall.EAGAIN {
+ if err = fd.pollServer.WaitRead(fd); err == nil {
+ continue
+ }
+ }
}
+ err = chkReadErr(n, err, fd)
break
}
if err != nil && err != io.EOF {
@@ -479,41 +486,47 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S
}
defer fd.decref()
for {
- n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
- if err == syscall.EAGAIN {
+ if fd.rdeadline.expired() {
err = errTimeout
- if fd.rdeadline >= 0 {
- if err = pollserver.WaitRead(fd); err == nil {
+ break
+ }
+ n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
+ if err != nil {
+ // TODO(dfc) should n and oobn be set to 0
+ if err == syscall.EAGAIN {
+ if err = fd.pollServer.WaitRead(fd); err == nil {
continue
}
}
}
- if err == nil && n == 0 {
- err = io.EOF
- }
+ err = chkReadErr(n, err, fd)
break
}
if err != nil && err != io.EOF {
err = &OpError{"read", fd.net, fd.laddr, err}
- return
}
return
}
-func (fd *netFD) Write(p []byte) (int, error) {
+func chkReadErr(n int, err error, fd *netFD) error {
+ if n == 0 && err == nil && fd.sotype != syscall.SOCK_DGRAM && fd.sotype != syscall.SOCK_RAW {
+ return io.EOF
+ }
+ return err
+}
+
+func (fd *netFD) Write(p []byte) (nn int, err error) {
fd.wio.Lock()
defer fd.wio.Unlock()
if err := fd.incref(false); err != nil {
return 0, err
}
defer fd.decref()
- if fd.sysfile == nil {
- return 0, syscall.EINVAL
- }
-
- var err error
- nn := 0
for {
+ if fd.wdeadline.expired() {
+ err = errTimeout
+ break
+ }
var n int
n, err = syscall.Write(int(fd.sysfd), p[nn:])
if n > 0 {
@@ -523,11 +536,8 @@ func (fd *netFD) Write(p []byte) (int, error) {
break
}
if err == syscall.EAGAIN {
- err = errTimeout
- if fd.wdeadline >= 0 {
- if err = pollserver.WaitWrite(fd); err == nil {
- continue
- }
+ if err = fd.pollServer.WaitWrite(fd); err == nil {
+ continue
}
}
if err != nil {
@@ -553,13 +563,14 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
}
defer fd.decref()
for {
+ if fd.wdeadline.expired() {
+ err = errTimeout
+ break
+ }
err = syscall.Sendto(fd.sysfd, p, 0, sa)
if err == syscall.EAGAIN {
- err = errTimeout
- if fd.wdeadline >= 0 {
- if err = pollserver.WaitWrite(fd); err == nil {
- continue
- }
+ if err = fd.pollServer.WaitWrite(fd); err == nil {
+ continue
}
}
break
@@ -580,13 +591,14 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
}
defer fd.decref()
for {
+ if fd.wdeadline.expired() {
+ err = errTimeout
+ break
+ }
err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0)
if err == syscall.EAGAIN {
- err = errTimeout
- if fd.wdeadline >= 0 {
- if err = pollserver.WaitWrite(fd); err == nil {
- continue
- }
+ if err = fd.pollServer.WaitWrite(fd); err == nil {
+ continue
}
}
break
@@ -606,22 +618,14 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
}
defer fd.decref()
- // See ../syscall/exec.go for description of ForkLock.
- // It is okay to hold the lock across syscall.Accept
- // because we have put fd.sysfd into non-blocking mode.
var s int
var rsa syscall.Sockaddr
for {
- syscall.ForkLock.RLock()
- s, rsa, err = syscall.Accept(fd.sysfd)
+ s, rsa, err = accept(fd.sysfd)
if err != nil {
- syscall.ForkLock.RUnlock()
if err == syscall.EAGAIN {
- err = errTimeout
- if fd.rdeadline >= 0 {
- if err = pollserver.WaitRead(fd); err == nil {
- continue
- }
+ if err = fd.pollServer.WaitRead(fd); err == nil {
+ continue
}
} else if err == syscall.ECONNABORTED {
// This means that a socket on the listen queue was closed
@@ -632,11 +636,9 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
}
break
}
- syscall.CloseOnExec(s)
- syscall.ForkLock.RUnlock()
if netfd, err = newFD(s, fd.family, fd.sotype, fd.net); err != nil {
- syscall.Close(s)
+ closesocket(s)
return nil, err
}
lsa, _ := syscall.Getsockname(netfd.sysfd)
@@ -645,17 +647,24 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
}
func (fd *netFD) dup() (f *os.File, err error) {
+ syscall.ForkLock.RLock()
ns, err := syscall.Dup(fd.sysfd)
if err != nil {
+ syscall.ForkLock.RUnlock()
return nil, &OpError{"dup", fd.net, fd.laddr, err}
}
+ syscall.CloseOnExec(ns)
+ syscall.ForkLock.RUnlock()
// We want blocking mode for the new fd, hence the double negative.
+ // This also puts the old fd into blocking mode, meaning that
+ // I/O will block the thread instead of letting us use the epoll server.
+ // Everything will still work, just with more threads.
if err = syscall.SetNonblock(ns, false); err != nil {
return nil, &OpError{"setnonblock", fd.net, fd.laddr, err}
}
- return os.NewFile(uintptr(ns), fd.sysfile.Name()), nil
+ return os.NewFile(uintptr(ns), fd.name()), nil
}
func closesocket(s int) error {
diff --git a/src/pkg/net/fd_unix_test.go b/src/pkg/net/fd_unix_test.go
new file mode 100644
index 000000000..664ef1bf1
--- /dev/null
+++ b/src/pkg/net/fd_unix_test.go
@@ -0,0 +1,58 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd
+
+package net
+
+import (
+ "io"
+ "syscall"
+ "testing"
+)
+
+var chkReadErrTests = []struct {
+ n int
+ err error
+ fd *netFD
+ expected error
+}{
+
+ {100, nil, &netFD{sotype: syscall.SOCK_STREAM}, nil},
+ {100, io.EOF, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF},
+ {100, errClosing, &netFD{sotype: syscall.SOCK_STREAM}, errClosing},
+ {0, nil, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF},
+ {0, io.EOF, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF},
+ {0, errClosing, &netFD{sotype: syscall.SOCK_STREAM}, errClosing},
+
+ {100, nil, &netFD{sotype: syscall.SOCK_DGRAM}, nil},
+ {100, io.EOF, &netFD{sotype: syscall.SOCK_DGRAM}, io.EOF},
+ {100, errClosing, &netFD{sotype: syscall.SOCK_DGRAM}, errClosing},
+ {0, nil, &netFD{sotype: syscall.SOCK_DGRAM}, nil},
+ {0, io.EOF, &netFD{sotype: syscall.SOCK_DGRAM}, io.EOF},
+ {0, errClosing, &netFD{sotype: syscall.SOCK_DGRAM}, errClosing},
+
+ {100, nil, &netFD{sotype: syscall.SOCK_SEQPACKET}, nil},
+ {100, io.EOF, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF},
+ {100, errClosing, &netFD{sotype: syscall.SOCK_SEQPACKET}, errClosing},
+ {0, nil, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF},
+ {0, io.EOF, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF},
+ {0, errClosing, &netFD{sotype: syscall.SOCK_SEQPACKET}, errClosing},
+
+ {100, nil, &netFD{sotype: syscall.SOCK_RAW}, nil},
+ {100, io.EOF, &netFD{sotype: syscall.SOCK_RAW}, io.EOF},
+ {100, errClosing, &netFD{sotype: syscall.SOCK_RAW}, errClosing},
+ {0, nil, &netFD{sotype: syscall.SOCK_RAW}, nil},
+ {0, io.EOF, &netFD{sotype: syscall.SOCK_RAW}, io.EOF},
+ {0, errClosing, &netFD{sotype: syscall.SOCK_RAW}, errClosing},
+}
+
+func TestChkReadErr(t *testing.T) {
+ for _, tt := range chkReadErrTests {
+ actual := chkReadErr(tt.n, tt.err, tt.fd)
+ if actual != tt.expected {
+ t.Errorf("chkReadError(%v, %v, %v): expected %v, actual %v", tt.n, tt.err, tt.fd.sotype, tt.expected, actual)
+ }
+ }
+}
diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go
index 45f5c2d88..0e331b44d 100644
--- a/src/pkg/net/fd_windows.go
+++ b/src/pkg/net/fd_windows.go
@@ -17,19 +17,58 @@ import (
var initErr error
-func init() {
+// CancelIo Windows API cancels all outstanding IO for a particular
+// socket on current thread. To overcome that limitation, we run
+// special goroutine, locked to OS single thread, that both starts
+// and cancels IO. It means, there are 2 unavoidable thread switches
+// for every IO.
+// Some newer versions of Windows has new CancelIoEx API, that does
+// not have that limitation and can be used from any thread. This
+// package uses CancelIoEx API, if present, otherwise it fallback
+// to CancelIo.
+
+var canCancelIO bool // determines if CancelIoEx API is present
+
+func sysInit() {
var d syscall.WSAData
e := syscall.WSAStartup(uint32(0x202), &d)
if e != nil {
initErr = os.NewSyscallError("WSAStartup", e)
}
+ canCancelIO = syscall.LoadCancelIoEx() == nil
+ if syscall.LoadGetAddrInfo() == nil {
+ lookupPort = newLookupPort
+ lookupIP = newLookupIP
+ }
}
func closesocket(s syscall.Handle) error {
return syscall.Closesocket(s)
}
-// Interface for all io operations.
+func canUseConnectEx(net string) bool {
+ if net == "udp" || net == "udp4" || net == "udp6" {
+ // ConnectEx windows API does not support connectionless sockets.
+ return false
+ }
+ return syscall.LoadConnectEx() == nil
+}
+
+func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
+ if !canUseConnectEx(net) {
+ // Use the relatively inefficient goroutine-racing
+ // implementation of DialTimeout.
+ return dialTimeoutRace(net, addr, timeout)
+ }
+ deadline := time.Now().Add(timeout)
+ ra, err := resolveAddr("dial", net, addr, deadline)
+ if err != nil {
+ return nil, err
+ }
+ return dial(net, addr, noLocalAddr, ra, deadline)
+}
+
+// Interface for all IO operations.
type anOpIface interface {
Op() *anOp
Name() string
@@ -42,7 +81,7 @@ type ioResult struct {
err error
}
-// anOp implements functionality common to all io operations.
+// anOp implements functionality common to all IO operations.
type anOp struct {
// Used by IOCP interface, it must be first field
// of the struct, as our code rely on it.
@@ -75,7 +114,7 @@ func (o *anOp) Op() *anOp {
return o
}
-// bufOp is used by io operations that read / write
+// bufOp is used by IO operations that read / write
// data from / to client buffer.
type bufOp struct {
anOp
@@ -92,7 +131,7 @@ func (o *bufOp) Init(fd *netFD, buf []byte, mode int) {
}
}
-// resultSrv will retrieve all io completion results from
+// resultSrv will retrieve all IO completion results from
// iocp and send them to the correspondent waiting client
// goroutine via channel supplied in the request.
type resultSrv struct {
@@ -107,7 +146,7 @@ func (s *resultSrv) Run() {
r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE)
switch {
case r.err == nil:
- // Dequeued successfully completed io packet.
+ // Dequeued successfully completed IO packet.
case r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil:
// Wait has timed out (should not happen now, but might be used in the future).
panic("GetQueuedCompletionStatus timed out")
@@ -115,22 +154,23 @@ func (s *resultSrv) Run() {
// Failed to dequeue anything -> report the error.
panic("GetQueuedCompletionStatus failed " + r.err.Error())
default:
- // Dequeued failed io packet.
+ // Dequeued failed IO packet.
}
(*anOp)(unsafe.Pointer(o)).resultc <- r
}
}
-// ioSrv executes net io requests.
+// ioSrv executes net IO requests.
type ioSrv struct {
- submchan chan anOpIface // submit io requests
- canchan chan anOpIface // cancel io requests
+ submchan chan anOpIface // submit IO requests
+ canchan chan anOpIface // cancel IO requests
}
-// ProcessRemoteIO will execute submit io requests on behalf
+// ProcessRemoteIO will execute submit IO requests on behalf
// of other goroutines, all on a single os thread, so it can
// cancel them later. Results of all operations will be sent
// back to their requesters via channel supplied in request.
+// It is used only when the CancelIoEx API is unavailable.
func (s *ioSrv) ProcessRemoteIO() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
@@ -144,20 +184,30 @@ func (s *ioSrv) ProcessRemoteIO() {
}
}
-// ExecIO executes a single io operation. It either executes it
-// inline, or, if a deadline is employed, passes the request onto
+// ExecIO executes a single IO operation oi. It submits and cancels
+// IO in the current thread for systems where Windows CancelIoEx API
+// is available. Alternatively, it passes the request onto
// a special goroutine and waits for completion or cancels request.
// deadline is unix nanos.
func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
var err error
o := oi.Op()
+ // Calculate timeout delta.
+ var delta int64
if deadline != 0 {
+ delta = deadline - time.Now().UnixNano()
+ if delta <= 0 {
+ return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, errTimeout}
+ }
+ }
+ // Start IO.
+ if canCancelIO {
+ err = oi.Submit()
+ } else {
// Send request to a special dedicated thread,
- // so it can stop the io with CancelIO later.
+ // so it can stop the IO with CancelIO later.
s.submchan <- oi
err = <-o.errnoc
- } else {
- err = oi.Submit()
}
switch err {
case nil:
@@ -168,27 +218,46 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
default:
return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, err}
}
+ // Setup timer, if deadline is given.
+ var timer <-chan time.Time
+ if delta > 0 {
+ t := time.NewTimer(time.Duration(delta) * time.Nanosecond)
+ defer t.Stop()
+ timer = t.C
+ }
// Wait for our request to complete.
var r ioResult
- if deadline != 0 {
- dt := deadline - time.Now().UnixNano()
- if dt < 1 {
- dt = 1
- }
- timer := time.NewTimer(time.Duration(dt) * time.Nanosecond)
- defer timer.Stop()
- select {
- case r = <-o.resultc:
- case <-timer.C:
+ var cancelled, timeout bool
+ select {
+ case r = <-o.resultc:
+ case <-timer:
+ cancelled = true
+ timeout = true
+ case <-o.fd.closec:
+ cancelled = true
+ }
+ if cancelled {
+ // Cancel it.
+ if canCancelIO {
+ err := syscall.CancelIoEx(syscall.Handle(o.Op().fd.sysfd), &o.o)
+ // Assuming ERROR_NOT_FOUND is returned, if IO is completed.
+ if err != nil && err != syscall.ERROR_NOT_FOUND {
+ // TODO(brainman): maybe do something else, but panic.
+ panic(err)
+ }
+ } else {
s.canchan <- oi
<-o.errnoc
- r = <-o.resultc
- if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled
- r.err = syscall.EWOULDBLOCK
- }
}
- } else {
+ // Wait for IO to be canceled or complete successfully.
r = <-o.resultc
+ if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled
+ if timeout {
+ r.err = errTimeout
+ } else {
+ r.err = errClosing
+ }
+ }
}
if r.err != nil {
err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, r.err}
@@ -211,9 +280,13 @@ func startServer() {
go resultsrv.Run()
iosrv = new(ioSrv)
- iosrv.submchan = make(chan anOpIface)
- iosrv.canchan = make(chan anOpIface)
- go iosrv.ProcessRemoteIO()
+ if !canCancelIO {
+ // Only CancelIo API is available. Lets start special goroutine
+ // locked to an OS thread, that both starts and cancels IO.
+ iosrv.submchan = make(chan anOpIface)
+ iosrv.canchan = make(chan anOpIface)
+ go iosrv.ProcessRemoteIO()
+ }
}
// Network file descriptor.
@@ -233,12 +306,13 @@ type netFD struct {
raddr Addr
resultc [2]chan ioResult // read/write completion results
errnoc [2]chan error // read/write submit or cancel operation errors
+ closec chan bool // used by Close to cancel pending IO
+
+ // serialize access to Read and Write methods
+ rio, wio sync.Mutex
- // owned by client
- rdeadline int64
- rio sync.Mutex
- wdeadline int64
- wio sync.Mutex
+ // read and write deadlines
+ rdeadline, wdeadline deadline
}
func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD {
@@ -247,8 +321,8 @@ func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD {
family: family,
sotype: sotype,
net: net,
+ closec: make(chan bool),
}
- runtime.SetFinalizer(netfd, (*netFD).Close)
return netfd
}
@@ -267,13 +341,52 @@ func newFD(fd syscall.Handle, family, proto int, net string) (*netFD, error) {
func (fd *netFD) setAddr(laddr, raddr Addr) {
fd.laddr = laddr
fd.raddr = raddr
+ runtime.SetFinalizer(fd, (*netFD).closesocket)
}
-func (fd *netFD) connect(ra syscall.Sockaddr) error {
- return syscall.Connect(fd.sysfd, ra)
+// Make new connection.
+
+type connectOp struct {
+ anOp
+ ra syscall.Sockaddr
+}
+
+func (o *connectOp) Submit() error {
+ return syscall.ConnectEx(o.fd.sysfd, o.ra, nil, 0, nil, &o.o)
}
-var errClosing = errors.New("use of closed network connection")
+func (o *connectOp) Name() string {
+ return "ConnectEx"
+}
+
+func (fd *netFD) connect(ra syscall.Sockaddr) error {
+ if !canUseConnectEx(fd.net) {
+ return syscall.Connect(fd.sysfd, ra)
+ }
+ // ConnectEx windows API requires an unconnected, previously bound socket.
+ var la syscall.Sockaddr
+ switch ra.(type) {
+ case *syscall.SockaddrInet4:
+ la = &syscall.SockaddrInet4{}
+ case *syscall.SockaddrInet6:
+ la = &syscall.SockaddrInet6{}
+ default:
+ panic("unexpected type in connect")
+ }
+ if err := syscall.Bind(fd.sysfd, la); err != nil {
+ return err
+ }
+ // Call ConnectEx API.
+ var o connectOp
+ o.Init(fd, 'w')
+ o.ra = ra
+ _, err := iosrv.ExecIO(&o, fd.wdeadline.value())
+ if err != nil {
+ return err
+ }
+ // Refresh socket properties.
+ return syscall.Setsockopt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
+}
// Add a reference to this fd.
// If closing==true, mark the fd as closing.
@@ -299,24 +412,12 @@ func (fd *netFD) incref(closing bool) error {
// Remove a reference to this FD and close if we've been asked to do so (and
// there are no references left.
func (fd *netFD) decref() {
+ if fd == nil {
+ return
+ }
fd.sysmu.Lock()
fd.sysref--
- // NOTE(rsc): On Unix we check fd.sysref == 0 here before closing,
- // but on Windows we have no way to wake up the blocked I/O other
- // than closing the socket (or calling Shutdown, which breaks other
- // programs that might have a reference to the socket). So there is
- // a small race here that we might close fd.sysfd and then some other
- // goroutine might start a read of fd.sysfd (having read it before we
- // write InvalidHandle to it), which might refer to some other file
- // if the specific handle value gets reused. I think handle values on
- // Windows are not reused as aggressively as file descriptors on Unix,
- // so this might be tolerable.
- if fd.closing && fd.sysfd != syscall.InvalidHandle {
- // In case the user has set linger, switch to blocking mode so
- // the close blocks. As long as this doesn't happen often, we
- // can handle the extra OS processes. Otherwise we'll need to
- // use the resultsrv for Close too. Sigh.
- syscall.SetNonblock(fd.sysfd, false)
+ if fd.closing && fd.sysref == 0 && fd.sysfd != syscall.InvalidHandle {
closesocket(fd.sysfd)
fd.sysfd = syscall.InvalidHandle
// no need for a finalizer anymore
@@ -329,14 +430,22 @@ func (fd *netFD) Close() error {
if err := fd.incref(true); err != nil {
return err
}
- fd.decref()
+ defer fd.decref()
+ // unblock pending reader and writer
+ close(fd.closec)
+ // wait for both reader and writer to exit
+ fd.rio.Lock()
+ defer fd.rio.Unlock()
+ fd.wio.Lock()
+ defer fd.wio.Unlock()
return nil
}
func (fd *netFD) shutdown(how int) error {
- if fd == nil || fd.sysfd == syscall.InvalidHandle {
- return syscall.EINVAL
+ if err := fd.incref(false); err != nil {
+ return err
}
+ defer fd.decref()
err := syscall.Shutdown(fd.sysfd, how)
if err != nil {
return &OpError{"shutdown", fd.net, fd.laddr, err}
@@ -352,6 +461,10 @@ func (fd *netFD) CloseWrite() error {
return fd.shutdown(syscall.SHUT_WR)
}
+func (fd *netFD) closesocket() error {
+ return closesocket(fd.sysfd)
+}
+
// Read from network.
type readOp struct {
@@ -368,21 +481,15 @@ func (o *readOp) Name() string {
}
func (fd *netFD) Read(buf []byte) (int, error) {
- if fd == nil {
- return 0, syscall.EINVAL
- }
- fd.rio.Lock()
- defer fd.rio.Unlock()
if err := fd.incref(false); err != nil {
return 0, err
}
defer fd.decref()
- if fd.sysfd == syscall.InvalidHandle {
- return 0, syscall.EINVAL
- }
+ fd.rio.Lock()
+ defer fd.rio.Unlock()
var o readOp
o.Init(fd, buf, 'r')
- n, err := iosrv.ExecIO(&o, fd.rdeadline)
+ n, err := iosrv.ExecIO(&o, fd.rdeadline.value())
if err == nil && n == 0 {
err = io.EOF
}
@@ -407,22 +514,19 @@ func (o *readFromOp) Name() string {
}
func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
- if fd == nil {
- return 0, nil, syscall.EINVAL
- }
if len(buf) == 0 {
return 0, nil, nil
}
- fd.rio.Lock()
- defer fd.rio.Unlock()
if err := fd.incref(false); err != nil {
return 0, nil, err
}
defer fd.decref()
+ fd.rio.Lock()
+ defer fd.rio.Unlock()
var o readFromOp
o.Init(fd, buf, 'r')
o.rsan = int32(unsafe.Sizeof(o.rsa))
- n, err = iosrv.ExecIO(&o, fd.rdeadline)
+ n, err = iosrv.ExecIO(&o, fd.rdeadline.value())
if err != nil {
return 0, nil, err
}
@@ -446,18 +550,15 @@ func (o *writeOp) Name() string {
}
func (fd *netFD) Write(buf []byte) (int, error) {
- if fd == nil {
- return 0, syscall.EINVAL
- }
- fd.wio.Lock()
- defer fd.wio.Unlock()
if err := fd.incref(false); err != nil {
return 0, err
}
defer fd.decref()
+ fd.wio.Lock()
+ defer fd.wio.Unlock()
var o writeOp
o.Init(fd, buf, 'w')
- return iosrv.ExecIO(&o, fd.wdeadline)
+ return iosrv.ExecIO(&o, fd.wdeadline.value())
}
// WriteTo to network.
@@ -477,25 +578,19 @@ func (o *writeToOp) Name() string {
}
func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
- if fd == nil {
- return 0, syscall.EINVAL
- }
if len(buf) == 0 {
return 0, nil
}
- fd.wio.Lock()
- defer fd.wio.Unlock()
if err := fd.incref(false); err != nil {
return 0, err
}
defer fd.decref()
- if fd.sysfd == syscall.InvalidHandle {
- return 0, syscall.EINVAL
- }
+ fd.wio.Lock()
+ defer fd.wio.Unlock()
var o writeToOp
o.Init(fd, buf, 'w')
o.sa = sa
- return iosrv.ExecIO(&o, fd.wdeadline)
+ return iosrv.ExecIO(&o, fd.wdeadline.value())
}
// Accept new network connections.
@@ -524,19 +619,15 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
defer fd.decref()
// Get new socket.
- // See ../syscall/exec.go for description of ForkLock.
- syscall.ForkLock.RLock()
- s, err := syscall.Socket(fd.family, fd.sotype, 0)
+ s, err := sysSocket(fd.family, fd.sotype, 0)
if err != nil {
- syscall.ForkLock.RUnlock()
- return nil, err
+ return nil, &OpError{"socket", fd.net, fd.laddr, err}
}
- syscall.CloseOnExec(s)
- syscall.ForkLock.RUnlock()
// Associate our new socket with IOCP.
onceStartServer.Do(startServer)
if _, err := syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); err != nil {
+ closesocket(s)
return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, err}
}
@@ -544,7 +635,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
var o acceptOp
o.Init(fd, 'r')
o.newsock = s
- _, err = iosrv.ExecIO(&o, 0)
+ _, err = iosrv.ExecIO(&o, fd.rdeadline.value())
if err != nil {
closesocket(s)
return nil, err
@@ -554,7 +645,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
if err != nil {
closesocket(s)
- return nil, err
+ return nil, &OpError{"Setsockopt", fd.net, fd.laddr, err}
}
// Get local and peer addr out of AcceptEx buffer.
diff --git a/src/pkg/net/file_plan9.go b/src/pkg/net/file_plan9.go
index 04f7ee040..f6ee1c29e 100644
--- a/src/pkg/net/file_plan9.go
+++ b/src/pkg/net/file_plan9.go
@@ -5,24 +5,147 @@
package net
import (
+ "errors"
+ "io"
"os"
"syscall"
)
+func (fd *netFD) status(ln int) (string, error) {
+ if !fd.ok() {
+ return "", syscall.EINVAL
+ }
+
+ status, err := os.Open(fd.dir + "/status")
+ if err != nil {
+ return "", err
+ }
+ defer status.Close()
+ buf := make([]byte, ln)
+ n, err := io.ReadFull(status, buf[:])
+ if err != nil {
+ return "", err
+ }
+ return string(buf[:n]), nil
+}
+
+func newFileFD(f *os.File) (net *netFD, err error) {
+ var ctl *os.File
+ close := func(fd int) {
+ if err != nil {
+ syscall.Close(fd)
+ }
+ }
+
+ path, err := syscall.Fd2path(int(f.Fd()))
+ if err != nil {
+ return nil, os.NewSyscallError("fd2path", err)
+ }
+ comp := splitAtBytes(path, "/")
+ n := len(comp)
+ if n < 3 || comp[0] != "net" {
+ return nil, syscall.EPLAN9
+ }
+
+ name := comp[2]
+ switch file := comp[n-1]; file {
+ case "ctl", "clone":
+ syscall.ForkLock.RLock()
+ fd, err := syscall.Dup(int(f.Fd()), -1)
+ syscall.ForkLock.RUnlock()
+ if err != nil {
+ return nil, os.NewSyscallError("dup", err)
+ }
+ defer close(fd)
+
+ dir := "/net/" + comp[n-2]
+ ctl = os.NewFile(uintptr(fd), dir+"/"+file)
+ ctl.Seek(0, 0)
+ var buf [16]byte
+ n, err := ctl.Read(buf[:])
+ if err != nil {
+ return nil, err
+ }
+ name = string(buf[:n])
+ default:
+ if len(comp) < 4 {
+ return nil, errors.New("could not find control file for connection")
+ }
+ dir := "/net/" + comp[1] + "/" + name
+ ctl, err = os.OpenFile(dir+"/ctl", os.O_RDWR, 0)
+ if err != nil {
+ return nil, err
+ }
+ defer close(int(ctl.Fd()))
+ }
+ dir := "/net/" + comp[1] + "/" + name
+ laddr, err := readPlan9Addr(comp[1], dir+"/local")
+ if err != nil {
+ return nil, err
+ }
+ return newFD(comp[1], name, ctl, nil, laddr, nil), nil
+}
+
+func newFileConn(f *os.File) (c Conn, err error) {
+ fd, err := newFileFD(f)
+ if err != nil {
+ return nil, err
+ }
+ if !fd.ok() {
+ return nil, syscall.EINVAL
+ }
+
+ fd.data, err = os.OpenFile(fd.dir+"/data", os.O_RDWR, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ switch fd.laddr.(type) {
+ case *TCPAddr:
+ return newTCPConn(fd), nil
+ case *UDPAddr:
+ return newUDPConn(fd), nil
+ }
+ return nil, syscall.EPLAN9
+}
+
+func newFileListener(f *os.File) (l Listener, err error) {
+ fd, err := newFileFD(f)
+ if err != nil {
+ return nil, err
+ }
+ switch fd.laddr.(type) {
+ case *TCPAddr:
+ default:
+ return nil, syscall.EPLAN9
+ }
+
+ // check that file corresponds to a listener
+ s, err := fd.status(len("Listen"))
+ if err != nil {
+ return nil, err
+ }
+ if s != "Listen" {
+ return nil, errors.New("file does not represent a listener")
+ }
+
+ return &TCPListener{fd}, nil
+}
+
// FileConn returns a copy of the network connection corresponding to
// the open file f. It is the caller's responsibility to close f when
// finished. Closing c does not affect f, and closing f does not
// affect c.
func FileConn(f *os.File) (c Conn, err error) {
- return nil, syscall.EPLAN9
+ return newFileConn(f)
}
// FileListener returns a copy of the network listener corresponding
// to the open file f. It is the caller's responsibility to close l
-// when finished. Closing c does not affect l, and closing l does not
-// affect c.
+// when finished. Closing l does not affect f, and closing f does not
+// affect l.
func FileListener(f *os.File) (l Listener, err error) {
- return nil, syscall.EPLAN9
+ return newFileListener(f)
}
// FilePacketConn returns a copy of the packet network connection
diff --git a/src/pkg/net/file_test.go b/src/pkg/net/file_test.go
index 95c0b6699..acaf18851 100644
--- a/src/pkg/net/file_test.go
+++ b/src/pkg/net/file_test.go
@@ -89,9 +89,8 @@ var fileListenerTests = []struct {
func TestFileListener(t *testing.T) {
switch runtime.GOOS {
- case "plan9", "windows":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ case "windows":
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
for _, tt := range fileListenerTests {
@@ -181,8 +180,7 @@ var filePacketConnTests = []struct {
func TestFilePacketConn(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
for _, tt := range filePacketConnTests {
diff --git a/src/pkg/net/file.go b/src/pkg/net/file_unix.go
index fc6c6fad8..4c8403e40 100644
--- a/src/pkg/net/file.go
+++ b/src/pkg/net/file_unix.go
@@ -12,52 +12,62 @@ import (
)
func newFileFD(f *os.File) (*netFD, error) {
+ syscall.ForkLock.RLock()
fd, err := syscall.Dup(int(f.Fd()))
if err != nil {
+ syscall.ForkLock.RUnlock()
return nil, os.NewSyscallError("dup", err)
}
+ syscall.CloseOnExec(fd)
+ syscall.ForkLock.RUnlock()
+ if err = syscall.SetNonblock(fd, true); err != nil {
+ closesocket(fd)
+ return nil, err
+ }
- proto, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE)
+ sotype, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE)
if err != nil {
+ closesocket(fd)
return nil, os.NewSyscallError("getsockopt", err)
}
family := syscall.AF_UNSPEC
toAddr := sockaddrToTCP
- sa, _ := syscall.Getsockname(fd)
- switch sa.(type) {
+ lsa, _ := syscall.Getsockname(fd)
+ switch lsa.(type) {
default:
closesocket(fd)
return nil, syscall.EINVAL
case *syscall.SockaddrInet4:
family = syscall.AF_INET
- if proto == syscall.SOCK_DGRAM {
+ if sotype == syscall.SOCK_DGRAM {
toAddr = sockaddrToUDP
- } else if proto == syscall.SOCK_RAW {
+ } else if sotype == syscall.SOCK_RAW {
toAddr = sockaddrToIP
}
case *syscall.SockaddrInet6:
family = syscall.AF_INET6
- if proto == syscall.SOCK_DGRAM {
+ if sotype == syscall.SOCK_DGRAM {
toAddr = sockaddrToUDP
- } else if proto == syscall.SOCK_RAW {
+ } else if sotype == syscall.SOCK_RAW {
toAddr = sockaddrToIP
}
case *syscall.SockaddrUnix:
family = syscall.AF_UNIX
toAddr = sockaddrToUnix
- if proto == syscall.SOCK_DGRAM {
+ if sotype == syscall.SOCK_DGRAM {
toAddr = sockaddrToUnixgram
- } else if proto == syscall.SOCK_SEQPACKET {
+ } else if sotype == syscall.SOCK_SEQPACKET {
toAddr = sockaddrToUnixpacket
}
}
- laddr := toAddr(sa)
- sa, _ = syscall.Getpeername(fd)
- raddr := toAddr(sa)
+ laddr := toAddr(lsa)
+ rsa, _ := syscall.Getpeername(fd)
+ raddr := toAddr(rsa)
- netfd, err := newFD(fd, family, proto, laddr.Network())
+ netfd, err := newFD(fd, family, sotype, laddr.Network())
if err != nil {
+ closesocket(fd)
return nil, err
}
netfd.setAddr(laddr, raddr)
@@ -78,10 +88,10 @@ func FileConn(f *os.File) (c Conn, err error) {
return newTCPConn(fd), nil
case *UDPAddr:
return newUDPConn(fd), nil
- case *UnixAddr:
- return newUnixConn(fd), nil
case *IPAddr:
return newIPConn(fd), nil
+ case *UnixAddr:
+ return newUnixConn(fd), nil
}
fd.Close()
return nil, syscall.EINVAL
diff --git a/src/pkg/net/http/cgi/child.go b/src/pkg/net/http/cgi/child.go
index 1ba7bec5f..100b8b777 100644
--- a/src/pkg/net/http/cgi/child.go
+++ b/src/pkg/net/http/cgi/child.go
@@ -91,10 +91,19 @@ func RequestFromMap(params map[string]string) (*http.Request, error) {
// TODO: cookies. parsing them isn't exported, though.
+ uriStr := params["REQUEST_URI"]
+ if uriStr == "" {
+ // Fallback to SCRIPT_NAME, PATH_INFO and QUERY_STRING.
+ uriStr = params["SCRIPT_NAME"] + params["PATH_INFO"]
+ s := params["QUERY_STRING"]
+ if s != "" {
+ uriStr += "?" + s
+ }
+ }
if r.Host != "" {
// Hostname is provided, so we can reasonably construct a URL,
// even if we have to assume 'http' for the scheme.
- rawurl := "http://" + r.Host + params["REQUEST_URI"]
+ rawurl := "http://" + r.Host + uriStr
url, err := url.Parse(rawurl)
if err != nil {
return nil, errors.New("cgi: failed to parse host and REQUEST_URI into a URL: " + rawurl)
@@ -104,7 +113,6 @@ func RequestFromMap(params map[string]string) (*http.Request, error) {
// Fallback logic if we don't have a Host header or the URL
// failed to parse
if r.URL == nil {
- uriStr := params["REQUEST_URI"]
url, err := url.Parse(uriStr)
if err != nil {
return nil, errors.New("cgi: failed to parse REQUEST_URI into a URL: " + uriStr)
diff --git a/src/pkg/net/http/cgi/child_test.go b/src/pkg/net/http/cgi/child_test.go
index ec53ab851..74e068014 100644
--- a/src/pkg/net/http/cgi/child_test.go
+++ b/src/pkg/net/http/cgi/child_test.go
@@ -82,6 +82,28 @@ func TestRequestWithoutHost(t *testing.T) {
t.Fatalf("unexpected nil URL")
}
if g, e := req.URL.String(), "/path?a=b"; e != g {
- t.Errorf("expected URL %q; got %q", e, g)
+ t.Errorf("URL = %q; want %q", g, e)
+ }
+}
+
+func TestRequestWithoutRequestURI(t *testing.T) {
+ env := map[string]string{
+ "SERVER_PROTOCOL": "HTTP/1.1",
+ "HTTP_HOST": "example.com",
+ "REQUEST_METHOD": "GET",
+ "SCRIPT_NAME": "/dir/scriptname",
+ "PATH_INFO": "/p1/p2",
+ "QUERY_STRING": "a=1&b=2",
+ "CONTENT_LENGTH": "123",
+ }
+ req, err := RequestFromMap(env)
+ if err != nil {
+ t.Fatalf("RequestFromMap: %v", err)
+ }
+ if req.URL == nil {
+ t.Fatalf("unexpected nil URL")
+ }
+ if g, e := req.URL.String(), "http://example.com/dir/scriptname/p1/p2?a=1&b=2"; e != g {
+ t.Errorf("URL = %q; want %q", g, e)
}
}
diff --git a/src/pkg/net/http/cgi/host_test.go b/src/pkg/net/http/cgi/host_test.go
index 4db3d850c..8c16e6897 100644
--- a/src/pkg/net/http/cgi/host_test.go
+++ b/src/pkg/net/http/cgi/host_test.go
@@ -19,7 +19,6 @@ import (
"runtime"
"strconv"
"strings"
- "syscall"
"testing"
"time"
)
@@ -63,17 +62,25 @@ readlines:
}
for key, expected := range expectedMap {
- if got := m[key]; got != expected {
+ got := m[key]
+ if key == "cwd" {
+ // For Windows. golang.org/issue/4645.
+ fi1, _ := os.Stat(got)
+ fi2, _ := os.Stat(expected)
+ if os.SameFile(fi1, fi2) {
+ got = expected
+ }
+ }
+ if got != expected {
t.Errorf("for key %q got %q; expected %q", key, got, expected)
}
}
return rw
}
-var cgiTested = false
-var cgiWorks bool
+var cgiTested, cgiWorks bool
-func skipTest(t *testing.T) bool {
+func check(t *testing.T) {
if !cgiTested {
cgiTested = true
cgiWorks = exec.Command("./testdata/test.cgi").Run() == nil
@@ -81,16 +88,12 @@ func skipTest(t *testing.T) bool {
if !cgiWorks {
// No Perl on Windows, needed by test.cgi
// TODO: make the child process be Go, not Perl.
- t.Logf("Skipping test: test.cgi failed.")
- return true
+ t.Skip("Skipping test: test.cgi failed.")
}
- return false
}
func TestCGIBasicGet(t *testing.T) {
- if skipTest(t) {
- return
- }
+ check(t)
h := &Handler{
Path: "testdata/test.cgi",
Root: "/test.cgi",
@@ -124,9 +127,7 @@ func TestCGIBasicGet(t *testing.T) {
}
func TestCGIBasicGetAbsPath(t *testing.T) {
- if skipTest(t) {
- return
- }
+ check(t)
pwd, err := os.Getwd()
if err != nil {
t.Fatalf("getwd error: %v", err)
@@ -144,9 +145,7 @@ func TestCGIBasicGetAbsPath(t *testing.T) {
}
func TestPathInfo(t *testing.T) {
- if skipTest(t) {
- return
- }
+ check(t)
h := &Handler{
Path: "testdata/test.cgi",
Root: "/test.cgi",
@@ -163,9 +162,7 @@ func TestPathInfo(t *testing.T) {
}
func TestPathInfoDirRoot(t *testing.T) {
- if skipTest(t) {
- return
- }
+ check(t)
h := &Handler{
Path: "testdata/test.cgi",
Root: "/myscript/",
@@ -181,9 +178,7 @@ func TestPathInfoDirRoot(t *testing.T) {
}
func TestDupHeaders(t *testing.T) {
- if skipTest(t) {
- return
- }
+ check(t)
h := &Handler{
Path: "testdata/test.cgi",
}
@@ -203,9 +198,7 @@ func TestDupHeaders(t *testing.T) {
}
func TestPathInfoNoRoot(t *testing.T) {
- if skipTest(t) {
- return
- }
+ check(t)
h := &Handler{
Path: "testdata/test.cgi",
Root: "",
@@ -221,9 +214,7 @@ func TestPathInfoNoRoot(t *testing.T) {
}
func TestCGIBasicPost(t *testing.T) {
- if skipTest(t) {
- return
- }
+ check(t)
postReq := `POST /test.cgi?a=b HTTP/1.0
Host: example.com
Content-Type: application/x-www-form-urlencoded
@@ -250,9 +241,7 @@ func chunk(s string) string {
// The CGI spec doesn't allow chunked requests.
func TestCGIPostChunked(t *testing.T) {
- if skipTest(t) {
- return
- }
+ check(t)
postReq := `POST /test.cgi?a=b HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
@@ -273,9 +262,7 @@ Transfer-Encoding: chunked
}
func TestRedirect(t *testing.T) {
- if skipTest(t) {
- return
- }
+ check(t)
h := &Handler{
Path: "testdata/test.cgi",
Root: "/test.cgi",
@@ -290,9 +277,7 @@ func TestRedirect(t *testing.T) {
}
func TestInternalRedirect(t *testing.T) {
- if skipTest(t) {
- return
- }
+ check(t)
baseHandler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
fmt.Fprintf(rw, "basepath=%s\n", req.URL.Path)
fmt.Fprintf(rw, "remoteaddr=%s\n", req.RemoteAddr)
@@ -312,8 +297,9 @@ func TestInternalRedirect(t *testing.T) {
// TestCopyError tests that we kill the process if there's an error copying
// its output. (for example, from the client having gone away)
func TestCopyError(t *testing.T) {
- if skipTest(t) || runtime.GOOS == "windows" {
- return
+ check(t)
+ if runtime.GOOS == "windows" {
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
h := &Handler{
Path: "testdata/test.cgi",
@@ -353,11 +339,7 @@ func TestCopyError(t *testing.T) {
}
childRunning := func() bool {
- p, err := os.FindProcess(pid)
- if err != nil {
- return false
- }
- return p.Signal(syscall.Signal(0)) == nil
+ return isProcessRunning(t, pid)
}
if !childRunning() {
@@ -376,10 +358,10 @@ func TestCopyError(t *testing.T) {
}
func TestDirUnix(t *testing.T) {
- if skipTest(t) || runtime.GOOS == "windows" {
- return
+ check(t)
+ if runtime.GOOS == "windows" {
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
-
cwd, _ := os.Getwd()
h := &Handler{
Path: "testdata/test.cgi",
@@ -404,8 +386,8 @@ func TestDirUnix(t *testing.T) {
}
func TestDirWindows(t *testing.T) {
- if skipTest(t) || runtime.GOOS != "windows" {
- return
+ if runtime.GOOS != "windows" {
+ t.Skip("Skipping windows specific test.")
}
cgifile, _ := filepath.Abs("testdata/test.cgi")
@@ -414,7 +396,7 @@ func TestDirWindows(t *testing.T) {
var err error
perl, err = exec.LookPath("perl")
if err != nil {
- return
+ t.Skip("Skipping test: perl not found.")
}
perl, _ = filepath.Abs(perl)
@@ -456,7 +438,7 @@ func TestEnvOverride(t *testing.T) {
var err error
perl, err = exec.LookPath("perl")
if err != nil {
- return
+ t.Skipf("Skipping test: perl not found.")
}
perl, _ = filepath.Abs(perl)
diff --git a/src/pkg/net/http/cgi/plan9_test.go b/src/pkg/net/http/cgi/plan9_test.go
new file mode 100644
index 000000000..c8235831b
--- /dev/null
+++ b/src/pkg/net/http/cgi/plan9_test.go
@@ -0,0 +1,18 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build plan9
+
+package cgi
+
+import (
+ "os"
+ "strconv"
+ "testing"
+)
+
+func isProcessRunning(t *testing.T, pid int) bool {
+ _, err := os.Stat("/proc/" + strconv.Itoa(pid))
+ return err == nil
+}
diff --git a/src/pkg/net/http/cgi/posix_test.go b/src/pkg/net/http/cgi/posix_test.go
new file mode 100644
index 000000000..5ff9e7d5e
--- /dev/null
+++ b/src/pkg/net/http/cgi/posix_test.go
@@ -0,0 +1,21 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !plan9
+
+package cgi
+
+import (
+ "os"
+ "syscall"
+ "testing"
+)
+
+func isProcessRunning(t *testing.T, pid int) bool {
+ p, err := os.FindProcess(pid)
+ if err != nil {
+ return false
+ }
+ return p.Signal(syscall.Signal(0)) == nil
+}
diff --git a/src/pkg/net/http/cgi/testdata/test.cgi b/src/pkg/net/http/cgi/testdata/test.cgi
index b46b1330f..3214df6f0 100755
--- a/src/pkg/net/http/cgi/testdata/test.cgi
+++ b/src/pkg/net/http/cgi/testdata/test.cgi
@@ -8,6 +8,8 @@
use strict;
use Cwd;
+binmode STDOUT;
+
my $q = MiniCGI->new;
my $params = $q->Vars;
@@ -16,51 +18,44 @@ if ($params->{"loc"}) {
exit(0);
}
-my $NL = "\r\n";
-$NL = "\n" if $params->{mode} eq "NL";
-
-my $p = sub {
- print "$_[0]$NL";
-};
-
-# With carriage returns
-$p->("Content-Type: text/html");
-$p->("X-CGI-Pid: $$");
-$p->("X-Test-Header: X-Test-Value");
-$p->("");
+print "Content-Type: text/html\r\n";
+print "X-CGI-Pid: $$\r\n";
+print "X-Test-Header: X-Test-Value\r\n";
+print "\r\n";
if ($params->{"bigresponse"}) {
- for (1..1024) {
- print "A" x 1024, "\n";
+ # 17 MB, for OS X: golang.org/issue/4958
+ for (1..(17 * 1024)) {
+ print "A" x 1024, "\r\n";
}
exit 0;
}
-print "test=Hello CGI\n";
+print "test=Hello CGI\r\n";
foreach my $k (sort keys %$params) {
- print "param-$k=$params->{$k}\n";
+ print "param-$k=$params->{$k}\r\n";
}
foreach my $k (sort keys %ENV) {
- my $clean_env = $ENV{$k};
- $clean_env =~ s/[\n\r]//g;
- print "env-$k=$clean_env\n";
+ my $clean_env = $ENV{$k};
+ $clean_env =~ s/[\n\r]//g;
+ print "env-$k=$clean_env\r\n";
}
-# NOTE: don't call getcwd() for windows.
-# msys return /c/go/src/... not C:\go\...
-my $dir;
+# NOTE: msys perl returns /c/go/src/... not C:\go\....
+my $dir = getcwd();
if ($^O eq 'MSWin32' || $^O eq 'msys') {
- my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe';
- $cmd =~ s!\\!/!g;
- $dir = `$cmd /c cd`;
- chomp $dir;
-} else {
- $dir = getcwd();
+ if ($dir =~ /^.:/) {
+ $dir =~ s!/!\\!g;
+ } else {
+ my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe';
+ $cmd =~ s!\\!/!g;
+ $dir = `$cmd /c cd`;
+ chomp $dir;
+ }
}
-print "cwd=$dir\n";
-
+print "cwd=$dir\r\n";
# A minimal version of CGI.pm, for people without the perl-modules
# package installed. (CGI.pm used to be part of the Perl core, but
diff --git a/src/pkg/net/http/chunked.go b/src/pkg/net/http/chunked.go
index 60a478fd8..91db01724 100644
--- a/src/pkg/net/http/chunked.go
+++ b/src/pkg/net/http/chunked.go
@@ -11,10 +11,9 @@ package http
import (
"bufio"
- "bytes"
"errors"
+ "fmt"
"io"
- "strconv"
)
const maxLineLength = 4096 // assumed <= bufio.defaultBufSize
@@ -22,7 +21,7 @@ const maxLineLength = 4096 // assumed <= bufio.defaultBufSize
var ErrLineTooLong = errors.New("header line too long")
// newChunkedReader returns a new chunkedReader that translates the data read from r
-// out of HTTP "chunked" format before returning it.
+// out of HTTP "chunked" format before returning it.
// The chunkedReader returns io.EOF when the final 0-length chunk is read.
//
// newChunkedReader is not needed by normal applications. The http package
@@ -39,16 +38,17 @@ type chunkedReader struct {
r *bufio.Reader
n uint64 // unread bytes in chunk
err error
+ buf [2]byte
}
func (cr *chunkedReader) beginChunk() {
// chunk-size CRLF
- var line string
+ var line []byte
line, cr.err = readLine(cr.r)
if cr.err != nil {
return
}
- cr.n, cr.err = strconv.ParseUint(line, 16, 64)
+ cr.n, cr.err = parseHexUint(line)
if cr.err != nil {
return
}
@@ -74,9 +74,8 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
cr.n -= uint64(n)
if cr.n == 0 && cr.err == nil {
// end of chunk (CRLF)
- b := make([]byte, 2)
- if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil {
- if b[0] != '\r' || b[1] != '\n' {
+ if _, cr.err = io.ReadFull(cr.r, cr.buf[:]); cr.err == nil {
+ if cr.buf[0] != '\r' || cr.buf[1] != '\n' {
cr.err = errors.New("malformed chunked encoding")
}
}
@@ -88,7 +87,7 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
// Give up if the line exceeds maxLineLength.
// The returned bytes are a pointer into storage in
// the bufio, so they are only valid until the next bufio read.
-func readLineBytes(b *bufio.Reader) (p []byte, err error) {
+func readLine(b *bufio.Reader) (p []byte, err error) {
if p, err = b.ReadSlice('\n'); err != nil {
// We always know when EOF is coming.
// If the caller asked for a line, there should be a line.
@@ -102,20 +101,18 @@ func readLineBytes(b *bufio.Reader) (p []byte, err error) {
if len(p) >= maxLineLength {
return nil, ErrLineTooLong
}
-
- // Chop off trailing white space.
- p = bytes.TrimRight(p, " \r\t\n")
-
- return p, nil
+ return trimTrailingWhitespace(p), nil
}
-// readLineBytes, but convert the bytes into a string.
-func readLine(b *bufio.Reader) (s string, err error) {
- p, e := readLineBytes(b)
- if e != nil {
- return "", e
+func trimTrailingWhitespace(b []byte) []byte {
+ for len(b) > 0 && isASCIISpace(b[len(b)-1]) {
+ b = b[:len(b)-1]
}
- return string(p), nil
+ return b
+}
+
+func isASCIISpace(b byte) bool {
+ return b == ' ' || b == '\t' || b == '\n' || b == '\r'
}
// newChunkedWriter returns a new chunkedWriter that translates writes into HTTP
@@ -147,9 +144,7 @@ func (cw *chunkedWriter) Write(data []byte) (n int, err error) {
return 0, nil
}
- head := strconv.FormatInt(int64(len(data)), 16) + "\r\n"
-
- if _, err = io.WriteString(cw.Wire, head); err != nil {
+ if _, err = fmt.Fprintf(cw.Wire, "%x\r\n", len(data)); err != nil {
return 0, err
}
if n, err = cw.Wire.Write(data); err != nil {
@@ -168,3 +163,21 @@ func (cw *chunkedWriter) Close() error {
_, err := io.WriteString(cw.Wire, "0\r\n")
return err
}
+
+func parseHexUint(v []byte) (n uint64, err error) {
+ for _, b := range v {
+ n <<= 4
+ switch {
+ case '0' <= b && b <= '9':
+ b = b - '0'
+ case 'a' <= b && b <= 'f':
+ b = b - 'a' + 10
+ case 'A' <= b && b <= 'F':
+ b = b - 'A' + 10
+ default:
+ return 0, errors.New("invalid byte in chunk length")
+ }
+ n |= uint64(b)
+ }
+ return
+}
diff --git a/src/pkg/net/http/chunked_test.go b/src/pkg/net/http/chunked_test.go
index b77ee2ff2..0b18c7b55 100644
--- a/src/pkg/net/http/chunked_test.go
+++ b/src/pkg/net/http/chunked_test.go
@@ -9,7 +9,10 @@ package http
import (
"bytes"
+ "fmt"
+ "io"
"io/ioutil"
+ "runtime"
"testing"
)
@@ -37,3 +40,54 @@ func TestChunk(t *testing.T) {
t.Errorf("chunk reader read %q; want %q", g, e)
}
}
+
+func TestChunkReaderAllocs(t *testing.T) {
+ // temporarily set GOMAXPROCS to 1 as we are testing memory allocations
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
+ var buf bytes.Buffer
+ w := newChunkedWriter(&buf)
+ a, b, c := []byte("aaaaaa"), []byte("bbbbbbbbbbbb"), []byte("cccccccccccccccccccccccc")
+ w.Write(a)
+ w.Write(b)
+ w.Write(c)
+ w.Close()
+
+ r := newChunkedReader(&buf)
+ readBuf := make([]byte, len(a)+len(b)+len(c)+1)
+
+ var ms runtime.MemStats
+ runtime.ReadMemStats(&ms)
+ m0 := ms.Mallocs
+
+ n, err := io.ReadFull(r, readBuf)
+
+ runtime.ReadMemStats(&ms)
+ mallocs := ms.Mallocs - m0
+ if mallocs > 1 {
+ t.Errorf("%d mallocs; want <= 1", mallocs)
+ }
+
+ if n != len(readBuf)-1 {
+ t.Errorf("read %d bytes; want %d", n, len(readBuf)-1)
+ }
+ if err != io.ErrUnexpectedEOF {
+ t.Errorf("read error = %v; want ErrUnexpectedEOF", err)
+ }
+}
+
+func TestParseHexUint(t *testing.T) {
+ for i := uint64(0); i <= 1234; i++ {
+ line := []byte(fmt.Sprintf("%x", i))
+ got, err := parseHexUint(line)
+ if err != nil {
+ t.Fatalf("on %d: %v", i, err)
+ }
+ if got != i {
+ t.Errorf("for input %q = %d; want %d", line, got, i)
+ }
+ }
+ _, err := parseHexUint([]byte("bogus"))
+ if err == nil {
+ t.Error("expected error on bogus input")
+ }
+}
diff --git a/src/pkg/net/http/client.go b/src/pkg/net/http/client.go
index 54564e098..5ee0804c7 100644
--- a/src/pkg/net/http/client.go
+++ b/src/pkg/net/http/client.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// HTTP client. See RFC 2616.
-//
+//
// This is the high-level Client interface.
// The low-level implementation is in transport.go.
@@ -14,6 +14,7 @@ import (
"errors"
"fmt"
"io"
+ "log"
"net/url"
"strings"
)
@@ -32,17 +33,19 @@ type Client struct {
// CheckRedirect specifies the policy for handling redirects.
// If CheckRedirect is not nil, the client calls it before
- // following an HTTP redirect. The arguments req and via
- // are the upcoming request and the requests made already,
- // oldest first. If CheckRedirect returns an error, the client
- // returns that error instead of issue the Request req.
+ // following an HTTP redirect. The arguments req and via are
+ // the upcoming request and the requests made already, oldest
+ // first. If CheckRedirect returns an error, the Client's Get
+ // method returns both the previous Response and
+ // CheckRedirect's error (wrapped in a url.Error) instead of
+ // issuing the Request req.
//
// If CheckRedirect is nil, the Client uses its default policy,
// which is to stop after 10 consecutive requests.
CheckRedirect func(req *Request, via []*Request) error
- // Jar specifies the cookie jar.
- // If Jar is nil, cookies are not sent in requests and ignored
+ // Jar specifies the cookie jar.
+ // If Jar is nil, cookies are not sent in requests and ignored
// in responses.
Jar CookieJar
}
@@ -84,10 +87,32 @@ type readClose struct {
io.Closer
}
+func (c *Client) send(req *Request) (*Response, error) {
+ if c.Jar != nil {
+ for _, cookie := range c.Jar.Cookies(req.URL) {
+ req.AddCookie(cookie)
+ }
+ }
+ resp, err := send(req, c.Transport)
+ if err != nil {
+ return nil, err
+ }
+ if c.Jar != nil {
+ if rc := resp.Cookies(); len(rc) > 0 {
+ c.Jar.SetCookies(req.URL, rc)
+ }
+ }
+ return resp, err
+}
+
// Do sends an HTTP request and returns an HTTP response, following
// policy (e.g. redirects, cookies, auth) as configured on the client.
//
-// A non-nil response always contains a non-nil resp.Body.
+// An error is returned if caused by client policy (such as
+// CheckRedirect), or if there was an HTTP protocol error.
+// A non-2xx response doesn't cause an error.
+//
+// When err is nil, resp always contains a non-nil resp.Body.
//
// Callers should close resp.Body when done reading from it. If
// resp.Body is not closed, the Client's underlying RoundTripper
@@ -97,12 +122,16 @@ type readClose struct {
// Generally Get, Post, or PostForm will be used instead of Do.
func (c *Client) Do(req *Request) (resp *Response, err error) {
if req.Method == "GET" || req.Method == "HEAD" {
- return c.doFollowingRedirects(req)
+ return c.doFollowingRedirects(req, shouldRedirectGet)
}
- return send(req, c.Transport)
+ if req.Method == "POST" || req.Method == "PUT" {
+ return c.doFollowingRedirects(req, shouldRedirectPost)
+ }
+ return c.send(req)
}
-// send issues an HTTP request. Caller should close resp.Body when done reading from it.
+// send issues an HTTP request.
+// Caller should close resp.Body when done reading from it.
func send(req *Request, t RoundTripper) (resp *Response, err error) {
if t == nil {
t = DefaultTransport
@@ -130,12 +159,19 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) {
if u := req.URL.User; u != nil {
req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(u.String())))
}
- return t.RoundTrip(req)
+ resp, err = t.RoundTrip(req)
+ if err != nil {
+ if resp != nil {
+ log.Printf("RoundTripper returned a response & error; ignoring response")
+ }
+ return nil, err
+ }
+ return resp, nil
}
// True if the specified HTTP status code is one for which the Get utility should
// automatically redirect.
-func shouldRedirect(statusCode int) bool {
+func shouldRedirectGet(statusCode int) bool {
switch statusCode {
case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
return true
@@ -143,6 +179,16 @@ func shouldRedirect(statusCode int) bool {
return false
}
+// True if the specified HTTP status code is one for which the Post utility should
+// automatically redirect.
+func shouldRedirectPost(statusCode int) bool {
+ switch statusCode {
+ case StatusFound, StatusSeeOther:
+ return true
+ }
+ return false
+}
+
// Get issues a GET to the specified URL. If the response is one of the following
// redirect codes, Get follows the redirect, up to a maximum of 10 redirects:
//
@@ -151,10 +197,15 @@ func shouldRedirect(statusCode int) bool {
// 303 (See Other)
// 307 (Temporary Redirect)
//
-// Caller should close r.Body when done reading from it.
+// An error is returned if there were too many redirects or if there
+// was an HTTP protocol error. A non-2xx response doesn't cause an
+// error.
+//
+// When err is nil, resp always contains a non-nil resp.Body.
+// Caller should close resp.Body when done reading from it.
//
// Get is a wrapper around DefaultClient.Get.
-func Get(url string) (r *Response, err error) {
+func Get(url string) (resp *Response, err error) {
return DefaultClient.Get(url)
}
@@ -167,18 +218,21 @@ func Get(url string) (r *Response, err error) {
// 303 (See Other)
// 307 (Temporary Redirect)
//
-// Caller should close r.Body when done reading from it.
-func (c *Client) Get(url string) (r *Response, err error) {
+// An error is returned if the Client's CheckRedirect function fails
+// or if there was an HTTP protocol error. A non-2xx response doesn't
+// cause an error.
+//
+// When err is nil, resp always contains a non-nil resp.Body.
+// Caller should close resp.Body when done reading from it.
+func (c *Client) Get(url string) (resp *Response, err error) {
req, err := NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
- return c.doFollowingRedirects(req)
+ return c.doFollowingRedirects(req, shouldRedirectGet)
}
-func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err error) {
- // TODO: if/when we add cookie support, the redirected request shouldn't
- // necessarily supply the same cookies as the original.
+func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bool) (resp *Response, err error) {
var base *url.URL
redirectChecker := c.CheckRedirect
if redirectChecker == nil {
@@ -190,17 +244,16 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err error) {
return nil, errors.New("http: nil Request.URL")
}
- jar := c.Jar
- if jar == nil {
- jar = blackHoleJar{}
- }
-
req := ireq
urlStr := "" // next relative or absolute URL to fetch (after first request)
+ redirectFailed := false
for redirect := 0; ; redirect++ {
if redirect != 0 {
req = new(Request)
req.Method = ireq.Method
+ if ireq.Method == "POST" || ireq.Method == "PUT" {
+ req.Method = "GET"
+ }
req.Header = make(Header)
req.URL, err = base.Parse(urlStr)
if err != nil {
@@ -215,26 +268,21 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err error) {
err = redirectChecker(req, via)
if err != nil {
+ redirectFailed = true
break
}
}
}
- for _, cookie := range jar.Cookies(req.URL) {
- req.AddCookie(cookie)
- }
urlStr = req.URL.String()
- if r, err = send(req, c.Transport); err != nil {
+ if resp, err = c.send(req); err != nil {
break
}
- if c := r.Cookies(); len(c) > 0 {
- jar.SetCookies(req.URL, c)
- }
- if shouldRedirect(r.StatusCode) {
- r.Body.Close()
- if urlStr = r.Header.Get("Location"); urlStr == "" {
- err = errors.New(fmt.Sprintf("%d response missing Location header", r.StatusCode))
+ if shouldRedirect(resp.StatusCode) {
+ resp.Body.Close()
+ if urlStr = resp.Header.Get("Location"); urlStr == "" {
+ err = errors.New(fmt.Sprintf("%d response missing Location header", resp.StatusCode))
break
}
base = req.URL
@@ -245,12 +293,23 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err error) {
}
method := ireq.Method
- err = &url.Error{
+ urlErr := &url.Error{
Op: method[0:1] + strings.ToLower(method[1:]),
URL: urlStr,
Err: err,
}
- return
+
+ if redirectFailed {
+ // Special case for Go 1 compatibility: return both the response
+ // and an error if the CheckRedirect function failed.
+ // See http://golang.org/issue/3795
+ return resp, urlErr
+ }
+
+ if resp != nil {
+ resp.Body.Close()
+ }
+ return nil, urlErr
}
func defaultCheckRedirect(req *Request, via []*Request) error {
@@ -262,49 +321,42 @@ func defaultCheckRedirect(req *Request, via []*Request) error {
// Post issues a POST to the specified URL.
//
-// Caller should close r.Body when done reading from it.
+// Caller should close resp.Body when done reading from it.
//
// Post is a wrapper around DefaultClient.Post
-func Post(url string, bodyType string, body io.Reader) (r *Response, err error) {
+func Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
return DefaultClient.Post(url, bodyType, body)
}
// Post issues a POST to the specified URL.
//
-// Caller should close r.Body when done reading from it.
-func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error) {
+// Caller should close resp.Body when done reading from it.
+func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
req, err := NewRequest("POST", url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", bodyType)
- if c.Jar != nil {
- for _, cookie := range c.Jar.Cookies(req.URL) {
- req.AddCookie(cookie)
- }
- }
- r, err = send(req, c.Transport)
- if err == nil && c.Jar != nil {
- c.Jar.SetCookies(req.URL, r.Cookies())
- }
- return r, err
+ return c.doFollowingRedirects(req, shouldRedirectPost)
}
-// PostForm issues a POST to the specified URL,
-// with data's keys and values urlencoded as the request body.
+// PostForm issues a POST to the specified URL, with data's keys and
+// values URL-encoded as the request body.
//
-// Caller should close r.Body when done reading from it.
+// When err is nil, resp always contains a non-nil resp.Body.
+// Caller should close resp.Body when done reading from it.
//
// PostForm is a wrapper around DefaultClient.PostForm
-func PostForm(url string, data url.Values) (r *Response, err error) {
+func PostForm(url string, data url.Values) (resp *Response, err error) {
return DefaultClient.PostForm(url, data)
}
-// PostForm issues a POST to the specified URL,
+// PostForm issues a POST to the specified URL,
// with data's keys and values urlencoded as the request body.
//
-// Caller should close r.Body when done reading from it.
-func (c *Client) PostForm(url string, data url.Values) (r *Response, err error) {
+// When err is nil, resp always contains a non-nil resp.Body.
+// Caller should close resp.Body when done reading from it.
+func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) {
return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}
@@ -318,7 +370,7 @@ func (c *Client) PostForm(url string, data url.Values) (r *Response, err error)
// 307 (Temporary Redirect)
//
// Head is a wrapper around DefaultClient.Head
-func Head(url string) (r *Response, err error) {
+func Head(url string) (resp *Response, err error) {
return DefaultClient.Head(url)
}
@@ -330,10 +382,10 @@ func Head(url string) (r *Response, err error) {
// 302 (Found)
// 303 (See Other)
// 307 (Temporary Redirect)
-func (c *Client) Head(url string) (r *Response, err error) {
+func (c *Client) Head(url string) (resp *Response, err error) {
req, err := NewRequest("HEAD", url, nil)
if err != nil {
return nil, err
}
- return c.doFollowingRedirects(req)
+ return c.doFollowingRedirects(req, shouldRedirectGet)
}
diff --git a/src/pkg/net/http/client_test.go b/src/pkg/net/http/client_test.go
index 9b4261b9f..88649bb16 100644
--- a/src/pkg/net/http/client_test.go
+++ b/src/pkg/net/http/client_test.go
@@ -7,7 +7,9 @@
package http_test
import (
+ "bytes"
"crypto/tls"
+ "crypto/x509"
"errors"
"fmt"
"io"
@@ -53,6 +55,7 @@ func pedanticReadAll(r io.Reader) (b []byte, err error) {
}
func TestClient(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(robotsTxtHandler)
defer ts.Close()
@@ -70,6 +73,7 @@ func TestClient(t *testing.T) {
}
func TestClientHead(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(robotsTxtHandler)
defer ts.Close()
@@ -92,6 +96,7 @@ func (t *recordingTransport) RoundTrip(req *Request) (resp *Response, err error)
}
func TestGetRequestFormat(t *testing.T) {
+ defer checkLeakedTransports(t)
tr := &recordingTransport{}
client := &Client{Transport: tr}
url := "http://dummy.faketld/"
@@ -108,6 +113,7 @@ func TestGetRequestFormat(t *testing.T) {
}
func TestPostRequestFormat(t *testing.T) {
+ defer checkLeakedTransports(t)
tr := &recordingTransport{}
client := &Client{Transport: tr}
@@ -134,6 +140,7 @@ func TestPostRequestFormat(t *testing.T) {
}
func TestPostFormRequestFormat(t *testing.T) {
+ defer checkLeakedTransports(t)
tr := &recordingTransport{}
client := &Client{Transport: tr}
@@ -175,6 +182,7 @@ func TestPostFormRequestFormat(t *testing.T) {
}
func TestRedirects(t *testing.T) {
+ defer checkLeakedTransports(t)
var ts *httptest.Server
ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
n, _ := strconv.Atoi(r.FormValue("n"))
@@ -218,6 +226,10 @@ func TestRedirects(t *testing.T) {
return checkErr
}}
res, err := c.Get(ts.URL)
+ if err != nil {
+ t.Fatalf("Get error: %v", err)
+ }
+ res.Body.Close()
finalUrl := res.Request.URL.String()
if e, g := "<nil>", fmt.Sprintf("%v", err); e != g {
t.Errorf("with custom client, expected error %q, got %q", e, g)
@@ -231,9 +243,63 @@ func TestRedirects(t *testing.T) {
checkErr = errors.New("no redirects allowed")
res, err = c.Get(ts.URL)
- finalUrl = res.Request.URL.String()
- if e, g := "Get /?n=1: no redirects allowed", fmt.Sprintf("%v", err); e != g {
- t.Errorf("with redirects forbidden, expected error %q, got %q", e, g)
+ if urlError, ok := err.(*url.Error); !ok || urlError.Err != checkErr {
+ t.Errorf("with redirects forbidden, expected a *url.Error with our 'no redirects allowed' error inside; got %#v (%q)", err, err)
+ }
+ if res == nil {
+ t.Fatalf("Expected a non-nil Response on CheckRedirect failure (http://golang.org/issue/3795)")
+ }
+ res.Body.Close()
+ if res.Header.Get("Location") == "" {
+ t.Errorf("no Location header in Response")
+ }
+}
+
+func TestPostRedirects(t *testing.T) {
+ defer checkLeakedTransports(t)
+ var log struct {
+ sync.Mutex
+ bytes.Buffer
+ }
+ var ts *httptest.Server
+ ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ log.Lock()
+ fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI)
+ log.Unlock()
+ if v := r.URL.Query().Get("code"); v != "" {
+ code, _ := strconv.Atoi(v)
+ if code/100 == 3 {
+ w.Header().Set("Location", ts.URL)
+ }
+ w.WriteHeader(code)
+ }
+ }))
+ defer ts.Close()
+ tests := []struct {
+ suffix string
+ want int // response code
+ }{
+ {"/", 200},
+ {"/?code=301", 301},
+ {"/?code=302", 200},
+ {"/?code=303", 200},
+ {"/?code=404", 404},
+ }
+ for _, tt := range tests {
+ res, err := Post(ts.URL+tt.suffix, "text/plain", strings.NewReader("Some content"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if res.StatusCode != tt.want {
+ t.Errorf("POST %s: status code = %d; want %d", tt.suffix, res.StatusCode, tt.want)
+ }
+ }
+ log.Lock()
+ got := log.String()
+ log.Unlock()
+ want := "POST / POST /?code=301 POST /?code=302 GET / POST /?code=303 GET / POST /?code=404 "
+ if got != want {
+ t.Errorf("Log differs.\n Got: %q\nWant: %q", got, want)
}
}
@@ -279,6 +345,10 @@ func TestClientSendsCookieFromJar(t *testing.T) {
req, _ := NewRequest("GET", us, nil)
client.Do(req) // Note: doesn't hit network
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
+
+ req, _ = NewRequest("POST", us, nil)
+ client.Do(req) // Note: doesn't hit network
+ matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
}
// Just enough correctness for our redirect tests. Uses the URL.Host as the
@@ -291,6 +361,9 @@ type TestJar struct {
func (j *TestJar) SetCookies(u *url.URL, cookies []*Cookie) {
j.m.Lock()
defer j.m.Unlock()
+ if j.perURL == nil {
+ j.perURL = make(map[string][]*Cookie)
+ }
j.perURL[u.Host] = cookies
}
@@ -301,6 +374,7 @@ func (j *TestJar) Cookies(u *url.URL) []*Cookie {
}
func TestRedirectCookiesOnRequest(t *testing.T) {
+ defer checkLeakedTransports(t)
var ts *httptest.Server
ts = httptest.NewServer(echoCookiesRedirectHandler)
defer ts.Close()
@@ -318,14 +392,20 @@ func TestRedirectCookiesOnRequest(t *testing.T) {
}
func TestRedirectCookiesJar(t *testing.T) {
+ defer checkLeakedTransports(t)
var ts *httptest.Server
ts = httptest.NewServer(echoCookiesRedirectHandler)
defer ts.Close()
- c := &Client{}
- c.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
+ c := &Client{
+ Jar: new(TestJar),
+ }
u, _ := url.Parse(ts.URL)
c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]})
- resp, _ := c.Get(ts.URL)
+ resp, err := c.Get(ts.URL)
+ if err != nil {
+ t.Fatalf("Get: %v", err)
+ }
+ resp.Body.Close()
matchReturnedCookies(t, expectedCookies, resp.Cookies())
}
@@ -348,7 +428,72 @@ func matchReturnedCookies(t *testing.T, expected, given []*Cookie) {
}
}
+func TestJarCalls(t *testing.T) {
+ defer checkLeakedTransports(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ pathSuffix := r.RequestURI[1:]
+ if r.RequestURI == "/nosetcookie" {
+ return // dont set cookies for this path
+ }
+ SetCookie(w, &Cookie{Name: "name" + pathSuffix, Value: "val" + pathSuffix})
+ if r.RequestURI == "/" {
+ Redirect(w, r, "http://secondhost.fake/secondpath", 302)
+ }
+ }))
+ defer ts.Close()
+ jar := new(RecordingJar)
+ c := &Client{
+ Jar: jar,
+ Transport: &Transport{
+ Dial: func(_ string, _ string) (net.Conn, error) {
+ return net.Dial("tcp", ts.Listener.Addr().String())
+ },
+ },
+ }
+ _, err := c.Get("http://firsthost.fake/")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = c.Get("http://firsthost.fake/nosetcookie")
+ if err != nil {
+ t.Fatal(err)
+ }
+ got := jar.log.String()
+ want := `Cookies("http://firsthost.fake/")
+SetCookie("http://firsthost.fake/", [name=val])
+Cookies("http://secondhost.fake/secondpath")
+SetCookie("http://secondhost.fake/secondpath", [namesecondpath=valsecondpath])
+Cookies("http://firsthost.fake/nosetcookie")
+`
+ if got != want {
+ t.Errorf("Got Jar calls:\n%s\nWant:\n%s", got, want)
+ }
+}
+
+// RecordingJar keeps a log of calls made to it, without
+// tracking any cookies.
+type RecordingJar struct {
+ mu sync.Mutex
+ log bytes.Buffer
+}
+
+func (j *RecordingJar) SetCookies(u *url.URL, cookies []*Cookie) {
+ j.logf("SetCookie(%q, %v)\n", u, cookies)
+}
+
+func (j *RecordingJar) Cookies(u *url.URL) []*Cookie {
+ j.logf("Cookies(%q)\n", u)
+ return nil
+}
+
+func (j *RecordingJar) logf(format string, args ...interface{}) {
+ j.mu.Lock()
+ defer j.mu.Unlock()
+ fmt.Fprintf(&j.log, format, args...)
+}
+
func TestStreamingGet(t *testing.T) {
+ defer checkLeakedTransports(t)
say := make(chan string)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
w.(Flusher).Flush()
@@ -399,6 +544,7 @@ func (c *writeCountingConn) Write(p []byte) (int, error) {
// TestClientWrites verifies that client requests are buffered and we
// don't send a TCP packet per line of the http request + body.
func TestClientWrites(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
}))
defer ts.Close()
@@ -432,6 +578,7 @@ func TestClientWrites(t *testing.T) {
}
func TestClientInsecureTransport(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
w.Write([]byte("Hello"))
}))
@@ -446,15 +593,20 @@ func TestClientInsecureTransport(t *testing.T) {
InsecureSkipVerify: insecure,
},
}
+ defer tr.CloseIdleConnections()
c := &Client{Transport: tr}
- _, err := c.Get(ts.URL)
+ res, err := c.Get(ts.URL)
if (err == nil) != insecure {
t.Errorf("insecure=%v: got unexpected err=%v", insecure, err)
}
+ if res != nil {
+ res.Body.Close()
+ }
}
}
func TestClientErrorWithRequestURI(t *testing.T) {
+ defer checkLeakedTransports(t)
req, _ := NewRequest("GET", "http://localhost:1234/", nil)
req.RequestURI = "/this/field/is/illegal/and/should/error/"
_, err := DefaultClient.Do(req)
@@ -465,3 +617,87 @@ func TestClientErrorWithRequestURI(t *testing.T) {
t.Errorf("wanted error mentioning RequestURI; got error: %v", err)
}
}
+
+func newTLSTransport(t *testing.T, ts *httptest.Server) *Transport {
+ certs := x509.NewCertPool()
+ for _, c := range ts.TLS.Certificates {
+ roots, err := x509.ParseCertificates(c.Certificate[len(c.Certificate)-1])
+ if err != nil {
+ t.Fatalf("error parsing server's root cert: %v", err)
+ }
+ for _, root := range roots {
+ certs.AddCert(root)
+ }
+ }
+ return &Transport{
+ TLSClientConfig: &tls.Config{RootCAs: certs},
+ }
+}
+
+func TestClientWithCorrectTLSServerName(t *testing.T) {
+ defer checkLeakedTransports(t)
+ ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ if r.TLS.ServerName != "127.0.0.1" {
+ t.Errorf("expected client to set ServerName 127.0.0.1, got: %q", r.TLS.ServerName)
+ }
+ }))
+ defer ts.Close()
+
+ c := &Client{Transport: newTLSTransport(t, ts)}
+ if _, err := c.Get(ts.URL); err != nil {
+ t.Fatalf("expected successful TLS connection, got error: %v", err)
+ }
+}
+
+func TestClientWithIncorrectTLSServerName(t *testing.T) {
+ defer checkLeakedTransports(t)
+ ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
+ defer ts.Close()
+
+ trans := newTLSTransport(t, ts)
+ trans.TLSClientConfig.ServerName = "badserver"
+ c := &Client{Transport: trans}
+ _, err := c.Get(ts.URL)
+ if err == nil {
+ t.Fatalf("expected an error")
+ }
+ if !strings.Contains(err.Error(), "127.0.0.1") || !strings.Contains(err.Error(), "badserver") {
+ t.Errorf("wanted error mentioning 127.0.0.1 and badserver; got error: %v", err)
+ }
+}
+
+// Verify Response.ContentLength is populated. http://golang.org/issue/4126
+func TestClientHeadContentLength(t *testing.T) {
+ defer checkLeakedTransports(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ if v := r.FormValue("cl"); v != "" {
+ w.Header().Set("Content-Length", v)
+ }
+ }))
+ defer ts.Close()
+ tests := []struct {
+ suffix string
+ want int64
+ }{
+ {"/?cl=1234", 1234},
+ {"/?cl=0", 0},
+ {"", -1},
+ }
+ for _, tt := range tests {
+ req, _ := NewRequest("HEAD", ts.URL+tt.suffix, nil)
+ res, err := DefaultClient.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if res.ContentLength != tt.want {
+ t.Errorf("Content-Length = %d; want %d", res.ContentLength, tt.want)
+ }
+ bs, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(bs) != 0 {
+ t.Errorf("Unexpected content: %q", bs)
+ }
+ }
+}
diff --git a/src/pkg/net/http/cookie.go b/src/pkg/net/http/cookie.go
index 2e30bbff1..155b09223 100644
--- a/src/pkg/net/http/cookie.go
+++ b/src/pkg/net/http/cookie.go
@@ -26,7 +26,7 @@ type Cookie struct {
Expires time.Time
RawExpires string
- // MaxAge=0 means no 'Max-Age' attribute specified.
+ // MaxAge=0 means no 'Max-Age' attribute specified.
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
// MaxAge>0 means Max-Age attribute present and given in seconds
MaxAge int
@@ -258,10 +258,5 @@ func parseCookieValueUsing(raw string, validByte func(byte) bool) (string, bool)
}
func isCookieNameValid(raw string) bool {
- for _, c := range raw {
- if !isToken(byte(c)) {
- return false
- }
- }
- return true
+ return strings.IndexFunc(raw, isNotToken) < 0
}
diff --git a/src/pkg/net/http/cookie_test.go b/src/pkg/net/http/cookie_test.go
index 1e9186a05..f84f73936 100644
--- a/src/pkg/net/http/cookie_test.go
+++ b/src/pkg/net/http/cookie_test.go
@@ -217,7 +217,7 @@ var readCookiesTests = []struct {
func TestReadCookies(t *testing.T) {
for i, tt := range readCookiesTests {
- for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input
+ for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input
c := readCookies(tt.Header, tt.Filter)
if !reflect.DeepEqual(c, tt.Cookies) {
t.Errorf("#%d readCookies:\nhave: %s\nwant: %s\n", i, toJSON(c), toJSON(tt.Cookies))
diff --git a/src/pkg/net/http/cookiejar/jar.go b/src/pkg/net/http/cookiejar/jar.go
new file mode 100644
index 000000000..5d1aeb87f
--- /dev/null
+++ b/src/pkg/net/http/cookiejar/jar.go
@@ -0,0 +1,494 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package cookiejar implements an in-memory RFC 6265-compliant http.CookieJar.
+package cookiejar
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "net/http"
+ "net/url"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+)
+
+// PublicSuffixList provides the public suffix of a domain. For example:
+// - the public suffix of "example.com" is "com",
+// - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and
+// - the public suffix of "bar.pvt.k12.ma.us" is "pvt.k12.ma.us".
+//
+// Implementations of PublicSuffixList must be safe for concurrent use by
+// multiple goroutines.
+//
+// An implementation that always returns "" is valid and may be useful for
+// testing but it is not secure: it means that the HTTP server for foo.com can
+// set a cookie for bar.com.
+type PublicSuffixList interface {
+ // PublicSuffix returns the public suffix of domain.
+ //
+ // TODO: specify which of the caller and callee is responsible for IP
+ // addresses, for leading and trailing dots, for case sensitivity, and
+ // for IDN/Punycode.
+ PublicSuffix(domain string) string
+
+ // String returns a description of the source of this public suffix
+ // list. The description will typically contain something like a time
+ // stamp or version number.
+ String() string
+}
+
+// Options are the options for creating a new Jar.
+type Options struct {
+ // PublicSuffixList is the public suffix list that determines whether
+ // an HTTP server can set a cookie for a domain.
+ //
+ // A nil value is valid and may be useful for testing but it is not
+ // secure: it means that the HTTP server for foo.co.uk can set a cookie
+ // for bar.co.uk.
+ PublicSuffixList PublicSuffixList
+}
+
+// Jar implements the http.CookieJar interface from the net/http package.
+type Jar struct {
+ psList PublicSuffixList
+
+ // mu locks the remaining fields.
+ mu sync.Mutex
+
+ // entries is a set of entries, keyed by their eTLD+1 and subkeyed by
+ // their name/domain/path.
+ entries map[string]map[string]entry
+
+ // nextSeqNum is the next sequence number assigned to a new cookie
+ // created SetCookies.
+ nextSeqNum uint64
+}
+
+// New returns a new cookie jar. A nil *Options is equivalent to a zero
+// Options.
+func New(o *Options) (*Jar, error) {
+ jar := &Jar{
+ entries: make(map[string]map[string]entry),
+ }
+ if o != nil {
+ jar.psList = o.PublicSuffixList
+ }
+ return jar, nil
+}
+
+// entry is the internal representation of a cookie.
+//
+// This struct type is not used outside of this package per se, but the exported
+// fields are those of RFC 6265.
+type entry struct {
+ Name string
+ Value string
+ Domain string
+ Path string
+ Secure bool
+ HttpOnly bool
+ Persistent bool
+ HostOnly bool
+ Expires time.Time
+ Creation time.Time
+ LastAccess time.Time
+
+ // seqNum is a sequence number so that Cookies returns cookies in a
+ // deterministic order, even for cookies that have equal Path length and
+ // equal Creation time. This simplifies testing.
+ seqNum uint64
+}
+
+// Id returns the domain;path;name triple of e as an id.
+func (e *entry) id() string {
+ return fmt.Sprintf("%s;%s;%s", e.Domain, e.Path, e.Name)
+}
+
+// shouldSend determines whether e's cookie qualifies to be included in a
+// request to host/path. It is the caller's responsibility to check if the
+// cookie is expired.
+func (e *entry) shouldSend(https bool, host, path string) bool {
+ return e.domainMatch(host) && e.pathMatch(path) && (https || !e.Secure)
+}
+
+// domainMatch implements "domain-match" of RFC 6265 section 5.1.3.
+func (e *entry) domainMatch(host string) bool {
+ if e.Domain == host {
+ return true
+ }
+ return !e.HostOnly && hasDotSuffix(host, e.Domain)
+}
+
+// pathMatch implements "path-match" according to RFC 6265 section 5.1.4.
+func (e *entry) pathMatch(requestPath string) bool {
+ if requestPath == e.Path {
+ return true
+ }
+ if strings.HasPrefix(requestPath, e.Path) {
+ if e.Path[len(e.Path)-1] == '/' {
+ return true // The "/any/" matches "/any/path" case.
+ } else if requestPath[len(e.Path)] == '/' {
+ return true // The "/any" matches "/any/path" case.
+ }
+ }
+ return false
+}
+
+// hasDotSuffix returns whether s ends in "."+suffix.
+func hasDotSuffix(s, suffix string) bool {
+ return len(s) > len(suffix) && s[len(s)-len(suffix)-1] == '.' && s[len(s)-len(suffix):] == suffix
+}
+
+// byPathLength is a []entry sort.Interface that sorts according to RFC 6265
+// section 5.4 point 2: by longest path and then by earliest creation time.
+type byPathLength []entry
+
+func (s byPathLength) Len() int { return len(s) }
+
+func (s byPathLength) Less(i, j int) bool {
+ if len(s[i].Path) != len(s[j].Path) {
+ return len(s[i].Path) > len(s[j].Path)
+ }
+ if !s[i].Creation.Equal(s[j].Creation) {
+ return s[i].Creation.Before(s[j].Creation)
+ }
+ return s[i].seqNum < s[j].seqNum
+}
+
+func (s byPathLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// Cookies implements the Cookies method of the http.CookieJar interface.
+//
+// It returns an empty slice if the URL's scheme is not HTTP or HTTPS.
+func (j *Jar) Cookies(u *url.URL) (cookies []*http.Cookie) {
+ return j.cookies(u, time.Now())
+}
+
+// cookies is like Cookies but takes the current time as a parameter.
+func (j *Jar) cookies(u *url.URL, now time.Time) (cookies []*http.Cookie) {
+ if u.Scheme != "http" && u.Scheme != "https" {
+ return cookies
+ }
+ host, err := canonicalHost(u.Host)
+ if err != nil {
+ return cookies
+ }
+ key := jarKey(host, j.psList)
+
+ j.mu.Lock()
+ defer j.mu.Unlock()
+
+ submap := j.entries[key]
+ if submap == nil {
+ return cookies
+ }
+
+ https := u.Scheme == "https"
+ path := u.Path
+ if path == "" {
+ path = "/"
+ }
+
+ modified := false
+ var selected []entry
+ for id, e := range submap {
+ if e.Persistent && !e.Expires.After(now) {
+ delete(submap, id)
+ modified = true
+ continue
+ }
+ if !e.shouldSend(https, host, path) {
+ continue
+ }
+ e.LastAccess = now
+ submap[id] = e
+ selected = append(selected, e)
+ modified = true
+ }
+ if modified {
+ if len(submap) == 0 {
+ delete(j.entries, key)
+ } else {
+ j.entries[key] = submap
+ }
+ }
+
+ sort.Sort(byPathLength(selected))
+ for _, e := range selected {
+ cookies = append(cookies, &http.Cookie{Name: e.Name, Value: e.Value})
+ }
+
+ return cookies
+}
+
+// SetCookies implements the SetCookies method of the http.CookieJar interface.
+//
+// It does nothing if the URL's scheme is not HTTP or HTTPS.
+func (j *Jar) SetCookies(u *url.URL, cookies []*http.Cookie) {
+ j.setCookies(u, cookies, time.Now())
+}
+
+// setCookies is like SetCookies but takes the current time as parameter.
+func (j *Jar) setCookies(u *url.URL, cookies []*http.Cookie, now time.Time) {
+ if len(cookies) == 0 {
+ return
+ }
+ if u.Scheme != "http" && u.Scheme != "https" {
+ return
+ }
+ host, err := canonicalHost(u.Host)
+ if err != nil {
+ return
+ }
+ key := jarKey(host, j.psList)
+ defPath := defaultPath(u.Path)
+
+ j.mu.Lock()
+ defer j.mu.Unlock()
+
+ submap := j.entries[key]
+
+ modified := false
+ for _, cookie := range cookies {
+ e, remove, err := j.newEntry(cookie, now, defPath, host)
+ if err != nil {
+ continue
+ }
+ id := e.id()
+ if remove {
+ if submap != nil {
+ if _, ok := submap[id]; ok {
+ delete(submap, id)
+ modified = true
+ }
+ }
+ continue
+ }
+ if submap == nil {
+ submap = make(map[string]entry)
+ }
+
+ if old, ok := submap[id]; ok {
+ e.Creation = old.Creation
+ e.seqNum = old.seqNum
+ } else {
+ e.Creation = now
+ e.seqNum = j.nextSeqNum
+ j.nextSeqNum++
+ }
+ e.LastAccess = now
+ submap[id] = e
+ modified = true
+ }
+
+ if modified {
+ if len(submap) == 0 {
+ delete(j.entries, key)
+ } else {
+ j.entries[key] = submap
+ }
+ }
+}
+
+// canonicalHost strips port from host if present and returns the canonicalized
+// host name.
+func canonicalHost(host string) (string, error) {
+ var err error
+ host = strings.ToLower(host)
+ if hasPort(host) {
+ host, _, err = net.SplitHostPort(host)
+ if err != nil {
+ return "", err
+ }
+ }
+ if strings.HasSuffix(host, ".") {
+ // Strip trailing dot from fully qualified domain names.
+ host = host[:len(host)-1]
+ }
+ return toASCII(host)
+}
+
+// hasPort returns whether host contains a port number. host may be a host
+// name, an IPv4 or an IPv6 address.
+func hasPort(host string) bool {
+ colons := strings.Count(host, ":")
+ if colons == 0 {
+ return false
+ }
+ if colons == 1 {
+ return true
+ }
+ return host[0] == '[' && strings.Contains(host, "]:")
+}
+
+// jarKey returns the key to use for a jar.
+func jarKey(host string, psl PublicSuffixList) string {
+ if isIP(host) {
+ return host
+ }
+
+ var i int
+ if psl == nil {
+ i = strings.LastIndex(host, ".")
+ if i == -1 {
+ return host
+ }
+ } else {
+ suffix := psl.PublicSuffix(host)
+ if suffix == host {
+ return host
+ }
+ i = len(host) - len(suffix)
+ if i <= 0 || host[i-1] != '.' {
+ // The provided public suffix list psl is broken.
+ // Storing cookies under host is a safe stopgap.
+ return host
+ }
+ }
+ prevDot := strings.LastIndex(host[:i-1], ".")
+ return host[prevDot+1:]
+}
+
+// isIP returns whether host is an IP address.
+func isIP(host string) bool {
+ return net.ParseIP(host) != nil
+}
+
+// defaultPath returns the directory part of an URL's path according to
+// RFC 6265 section 5.1.4.
+func defaultPath(path string) string {
+ if len(path) == 0 || path[0] != '/' {
+ return "/" // Path is empty or malformed.
+ }
+
+ i := strings.LastIndex(path, "/") // Path starts with "/", so i != -1.
+ if i == 0 {
+ return "/" // Path has the form "/abc".
+ }
+ return path[:i] // Path is either of form "/abc/xyz" or "/abc/xyz/".
+}
+
+// newEntry creates an entry from a http.Cookie c. now is the current time and
+// is compared to c.Expires to determine deletion of c. defPath and host are the
+// default-path and the canonical host name of the URL c was received from.
+//
+// remove is whether the jar should delete this cookie, as it has already
+// expired with respect to now. In this case, e may be incomplete, but it will
+// be valid to call e.id (which depends on e's Name, Domain and Path).
+//
+// A malformed c.Domain will result in an error.
+func (j *Jar) newEntry(c *http.Cookie, now time.Time, defPath, host string) (e entry, remove bool, err error) {
+ e.Name = c.Name
+
+ if c.Path == "" || c.Path[0] != '/' {
+ e.Path = defPath
+ } else {
+ e.Path = c.Path
+ }
+
+ e.Domain, e.HostOnly, err = j.domainAndType(host, c.Domain)
+ if err != nil {
+ return e, false, err
+ }
+
+ // MaxAge takes precedence over Expires.
+ if c.MaxAge < 0 {
+ return e, true, nil
+ } else if c.MaxAge > 0 {
+ e.Expires = now.Add(time.Duration(c.MaxAge) * time.Second)
+ e.Persistent = true
+ } else {
+ if c.Expires.IsZero() {
+ e.Expires = endOfTime
+ e.Persistent = false
+ } else {
+ if !c.Expires.After(now) {
+ return e, true, nil
+ }
+ e.Expires = c.Expires
+ e.Persistent = true
+ }
+ }
+
+ e.Value = c.Value
+ e.Secure = c.Secure
+ e.HttpOnly = c.HttpOnly
+
+ return e, false, nil
+}
+
+var (
+ errIllegalDomain = errors.New("cookiejar: illegal cookie domain attribute")
+ errMalformedDomain = errors.New("cookiejar: malformed cookie domain attribute")
+ errNoHostname = errors.New("cookiejar: no host name available (IP only)")
+)
+
+// endOfTime is the time when session (non-persistent) cookies expire.
+// This instant is representable in most date/time formats (not just
+// Go's time.Time) and should be far enough in the future.
+var endOfTime = time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC)
+
+// domainAndType determines the cookie's domain and hostOnly attribute.
+func (j *Jar) domainAndType(host, domain string) (string, bool, error) {
+ if domain == "" {
+ // No domain attribute in the SetCookie header indicates a
+ // host cookie.
+ return host, true, nil
+ }
+
+ if isIP(host) {
+ // According to RFC 6265 domain-matching includes not being
+ // an IP address.
+ // TODO: This might be relaxed as in common browsers.
+ return "", false, errNoHostname
+ }
+
+ // From here on: If the cookie is valid, it is a domain cookie (with
+ // the one exception of a public suffix below).
+ // See RFC 6265 section 5.2.3.
+ if domain[0] == '.' {
+ domain = domain[1:]
+ }
+
+ if len(domain) == 0 || domain[0] == '.' {
+ // Received either "Domain=." or "Domain=..some.thing",
+ // both are illegal.
+ return "", false, errMalformedDomain
+ }
+ domain = strings.ToLower(domain)
+
+ if domain[len(domain)-1] == '.' {
+ // We received stuff like "Domain=www.example.com.".
+ // Browsers do handle such stuff (actually differently) but
+ // RFC 6265 seems to be clear here (e.g. section 4.1.2.3) in
+ // requiring a reject. 4.1.2.3 is not normative, but
+ // "Domain Matching" (5.1.3) and "Canonicalized Host Names"
+ // (5.1.2) are.
+ return "", false, errMalformedDomain
+ }
+
+ // See RFC 6265 section 5.3 #5.
+ if j.psList != nil {
+ if ps := j.psList.PublicSuffix(domain); ps != "" && !hasDotSuffix(domain, ps) {
+ if host == domain {
+ // This is the one exception in which a cookie
+ // with a domain attribute is a host cookie.
+ return host, true, nil
+ }
+ return "", false, errIllegalDomain
+ }
+ }
+
+ // The domain must domain-match host: www.mycompany.com cannot
+ // set cookies for .ourcompetitors.com.
+ if host != domain && !hasDotSuffix(host, domain) {
+ return "", false, errIllegalDomain
+ }
+
+ return domain, false, nil
+}
diff --git a/src/pkg/net/http/cookiejar/jar_test.go b/src/pkg/net/http/cookiejar/jar_test.go
new file mode 100644
index 000000000..3aa601586
--- /dev/null
+++ b/src/pkg/net/http/cookiejar/jar_test.go
@@ -0,0 +1,1267 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cookiejar
+
+import (
+ "fmt"
+ "net/http"
+ "net/url"
+ "sort"
+ "strings"
+ "testing"
+ "time"
+)
+
+// tNow is the synthetic current time used as now during testing.
+var tNow = time.Date(2013, 1, 1, 12, 0, 0, 0, time.UTC)
+
+// testPSL implements PublicSuffixList with just two rules: "co.uk"
+// and the default rule "*".
+type testPSL struct{}
+
+func (testPSL) String() string {
+ return "testPSL"
+}
+func (testPSL) PublicSuffix(d string) string {
+ if d == "co.uk" || strings.HasSuffix(d, ".co.uk") {
+ return "co.uk"
+ }
+ return d[strings.LastIndex(d, ".")+1:]
+}
+
+// newTestJar creates an empty Jar with testPSL as the public suffix list.
+func newTestJar() *Jar {
+ jar, err := New(&Options{PublicSuffixList: testPSL{}})
+ if err != nil {
+ panic(err)
+ }
+ return jar
+}
+
+var hasDotSuffixTests = [...]struct {
+ s, suffix string
+}{
+ {"", ""},
+ {"", "."},
+ {"", "x"},
+ {".", ""},
+ {".", "."},
+ {".", ".."},
+ {".", "x"},
+ {".", "x."},
+ {".", ".x"},
+ {".", ".x."},
+ {"x", ""},
+ {"x", "."},
+ {"x", ".."},
+ {"x", "x"},
+ {"x", "x."},
+ {"x", ".x"},
+ {"x", ".x."},
+ {".x", ""},
+ {".x", "."},
+ {".x", ".."},
+ {".x", "x"},
+ {".x", "x."},
+ {".x", ".x"},
+ {".x", ".x."},
+ {"x.", ""},
+ {"x.", "."},
+ {"x.", ".."},
+ {"x.", "x"},
+ {"x.", "x."},
+ {"x.", ".x"},
+ {"x.", ".x."},
+ {"com", ""},
+ {"com", "m"},
+ {"com", "om"},
+ {"com", "com"},
+ {"com", ".com"},
+ {"com", "x.com"},
+ {"com", "xcom"},
+ {"com", "xorg"},
+ {"com", "org"},
+ {"com", "rg"},
+ {"foo.com", ""},
+ {"foo.com", "m"},
+ {"foo.com", "om"},
+ {"foo.com", "com"},
+ {"foo.com", ".com"},
+ {"foo.com", "o.com"},
+ {"foo.com", "oo.com"},
+ {"foo.com", "foo.com"},
+ {"foo.com", ".foo.com"},
+ {"foo.com", "x.foo.com"},
+ {"foo.com", "xfoo.com"},
+ {"foo.com", "xfoo.org"},
+ {"foo.com", "foo.org"},
+ {"foo.com", "oo.org"},
+ {"foo.com", "o.org"},
+ {"foo.com", ".org"},
+ {"foo.com", "org"},
+ {"foo.com", "rg"},
+}
+
+func TestHasDotSuffix(t *testing.T) {
+ for _, tc := range hasDotSuffixTests {
+ got := hasDotSuffix(tc.s, tc.suffix)
+ want := strings.HasSuffix(tc.s, "."+tc.suffix)
+ if got != want {
+ t.Errorf("s=%q, suffix=%q: got %v, want %v", tc.s, tc.suffix, got, want)
+ }
+ }
+}
+
+var canonicalHostTests = map[string]string{
+ "www.example.com": "www.example.com",
+ "WWW.EXAMPLE.COM": "www.example.com",
+ "wWw.eXAmple.CoM": "www.example.com",
+ "www.example.com:80": "www.example.com",
+ "192.168.0.10": "192.168.0.10",
+ "192.168.0.5:8080": "192.168.0.5",
+ "2001:4860:0:2001::68": "2001:4860:0:2001::68",
+ "[2001:4860:0:::68]:8080": "2001:4860:0:::68",
+ "www.bücher.de": "www.xn--bcher-kva.de",
+ "www.example.com.": "www.example.com",
+ "[bad.unmatched.bracket:": "error",
+}
+
+func TestCanonicalHost(t *testing.T) {
+ for h, want := range canonicalHostTests {
+ got, err := canonicalHost(h)
+ if want == "error" {
+ if err == nil {
+ t.Errorf("%q: got nil error, want non-nil", h)
+ }
+ continue
+ }
+ if err != nil {
+ t.Errorf("%q: %v", h, err)
+ continue
+ }
+ if got != want {
+ t.Errorf("%q: got %q, want %q", h, got, want)
+ continue
+ }
+ }
+}
+
+var hasPortTests = map[string]bool{
+ "www.example.com": false,
+ "www.example.com:80": true,
+ "127.0.0.1": false,
+ "127.0.0.1:8080": true,
+ "2001:4860:0:2001::68": false,
+ "[2001::0:::68]:80": true,
+}
+
+func TestHasPort(t *testing.T) {
+ for host, want := range hasPortTests {
+ if got := hasPort(host); got != want {
+ t.Errorf("%q: got %t, want %t", host, got, want)
+ }
+ }
+}
+
+var jarKeyTests = map[string]string{
+ "foo.www.example.com": "example.com",
+ "www.example.com": "example.com",
+ "example.com": "example.com",
+ "com": "com",
+ "foo.www.bbc.co.uk": "bbc.co.uk",
+ "www.bbc.co.uk": "bbc.co.uk",
+ "bbc.co.uk": "bbc.co.uk",
+ "co.uk": "co.uk",
+ "uk": "uk",
+ "192.168.0.5": "192.168.0.5",
+}
+
+func TestJarKey(t *testing.T) {
+ for host, want := range jarKeyTests {
+ if got := jarKey(host, testPSL{}); got != want {
+ t.Errorf("%q: got %q, want %q", host, got, want)
+ }
+ }
+}
+
+var jarKeyNilPSLTests = map[string]string{
+ "foo.www.example.com": "example.com",
+ "www.example.com": "example.com",
+ "example.com": "example.com",
+ "com": "com",
+ "foo.www.bbc.co.uk": "co.uk",
+ "www.bbc.co.uk": "co.uk",
+ "bbc.co.uk": "co.uk",
+ "co.uk": "co.uk",
+ "uk": "uk",
+ "192.168.0.5": "192.168.0.5",
+}
+
+func TestJarKeyNilPSL(t *testing.T) {
+ for host, want := range jarKeyNilPSLTests {
+ if got := jarKey(host, nil); got != want {
+ t.Errorf("%q: got %q, want %q", host, got, want)
+ }
+ }
+}
+
+var isIPTests = map[string]bool{
+ "127.0.0.1": true,
+ "1.2.3.4": true,
+ "2001:4860:0:2001::68": true,
+ "example.com": false,
+ "1.1.1.300": false,
+ "www.foo.bar.net": false,
+ "123.foo.bar.net": false,
+}
+
+func TestIsIP(t *testing.T) {
+ for host, want := range isIPTests {
+ if got := isIP(host); got != want {
+ t.Errorf("%q: got %t, want %t", host, got, want)
+ }
+ }
+}
+
+var defaultPathTests = map[string]string{
+ "/": "/",
+ "/abc": "/",
+ "/abc/": "/abc",
+ "/abc/xyz": "/abc",
+ "/abc/xyz/": "/abc/xyz",
+ "/a/b/c.html": "/a/b",
+ "": "/",
+ "strange": "/",
+ "//": "/",
+ "/a//b": "/a/",
+ "/a/./b": "/a/.",
+ "/a/../b": "/a/..",
+}
+
+func TestDefaultPath(t *testing.T) {
+ for path, want := range defaultPathTests {
+ if got := defaultPath(path); got != want {
+ t.Errorf("%q: got %q, want %q", path, got, want)
+ }
+ }
+}
+
+var domainAndTypeTests = [...]struct {
+ host string // host Set-Cookie header was received from
+ domain string // domain attribute in Set-Cookie header
+ wantDomain string // expected domain of cookie
+ wantHostOnly bool // expected host-cookie flag
+ wantErr error // expected error
+}{
+ {"www.example.com", "", "www.example.com", true, nil},
+ {"127.0.0.1", "", "127.0.0.1", true, nil},
+ {"2001:4860:0:2001::68", "", "2001:4860:0:2001::68", true, nil},
+ {"www.example.com", "example.com", "example.com", false, nil},
+ {"www.example.com", ".example.com", "example.com", false, nil},
+ {"www.example.com", "www.example.com", "www.example.com", false, nil},
+ {"www.example.com", ".www.example.com", "www.example.com", false, nil},
+ {"foo.sso.example.com", "sso.example.com", "sso.example.com", false, nil},
+ {"bar.co.uk", "bar.co.uk", "bar.co.uk", false, nil},
+ {"foo.bar.co.uk", ".bar.co.uk", "bar.co.uk", false, nil},
+ {"127.0.0.1", "127.0.0.1", "", false, errNoHostname},
+ {"2001:4860:0:2001::68", "2001:4860:0:2001::68", "2001:4860:0:2001::68", false, errNoHostname},
+ {"www.example.com", ".", "", false, errMalformedDomain},
+ {"www.example.com", "..", "", false, errMalformedDomain},
+ {"www.example.com", "other.com", "", false, errIllegalDomain},
+ {"www.example.com", "com", "", false, errIllegalDomain},
+ {"www.example.com", ".com", "", false, errIllegalDomain},
+ {"foo.bar.co.uk", ".co.uk", "", false, errIllegalDomain},
+ {"127.www.0.0.1", "127.0.0.1", "", false, errIllegalDomain},
+ {"com", "", "com", true, nil},
+ {"com", "com", "com", true, nil},
+ {"com", ".com", "com", true, nil},
+ {"co.uk", "", "co.uk", true, nil},
+ {"co.uk", "co.uk", "co.uk", true, nil},
+ {"co.uk", ".co.uk", "co.uk", true, nil},
+}
+
+func TestDomainAndType(t *testing.T) {
+ jar := newTestJar()
+ for _, tc := range domainAndTypeTests {
+ domain, hostOnly, err := jar.domainAndType(tc.host, tc.domain)
+ if err != tc.wantErr {
+ t.Errorf("%q/%q: got %q error, want %q",
+ tc.host, tc.domain, err, tc.wantErr)
+ continue
+ }
+ if err != nil {
+ continue
+ }
+ if domain != tc.wantDomain || hostOnly != tc.wantHostOnly {
+ t.Errorf("%q/%q: got %q/%t want %q/%t",
+ tc.host, tc.domain, domain, hostOnly,
+ tc.wantDomain, tc.wantHostOnly)
+ }
+ }
+}
+
+// expiresIn creates an expires attribute delta seconds from tNow.
+func expiresIn(delta int) string {
+ t := tNow.Add(time.Duration(delta) * time.Second)
+ return "expires=" + t.Format(time.RFC1123)
+}
+
+// mustParseURL parses s to an URL and panics on error.
+func mustParseURL(s string) *url.URL {
+ u, err := url.Parse(s)
+ if err != nil || u.Scheme == "" || u.Host == "" {
+ panic(fmt.Sprintf("Unable to parse URL %s.", s))
+ }
+ return u
+}
+
+// jarTest encapsulates the following actions on a jar:
+// 1. Perform SetCookies with fromURL and the cookies from setCookies.
+// (Done at time tNow + 0 ms.)
+// 2. Check that the entries in the jar matches content.
+// (Done at time tNow + 1001 ms.)
+// 3. For each query in tests: Check that Cookies with toURL yields the
+// cookies in want.
+// (Query n done at tNow + (n+2)*1001 ms.)
+type jarTest struct {
+ description string // The description of what this test is supposed to test
+ fromURL string // The full URL of the request from which Set-Cookie headers where received
+ setCookies []string // All the cookies received from fromURL
+ content string // The whole (non-expired) content of the jar
+ queries []query // Queries to test the Jar.Cookies method
+}
+
+// query contains one test of the cookies returned from Jar.Cookies.
+type query struct {
+ toURL string // the URL in the Cookies call
+ want string // the expected list of cookies (order matters)
+}
+
+// run runs the jarTest.
+func (test jarTest) run(t *testing.T, jar *Jar) {
+ now := tNow
+
+ // Populate jar with cookies.
+ setCookies := make([]*http.Cookie, len(test.setCookies))
+ for i, cs := range test.setCookies {
+ cookies := (&http.Response{Header: http.Header{"Set-Cookie": {cs}}}).Cookies()
+ if len(cookies) != 1 {
+ panic(fmt.Sprintf("Wrong cookie line %q: %#v", cs, cookies))
+ }
+ setCookies[i] = cookies[0]
+ }
+ jar.setCookies(mustParseURL(test.fromURL), setCookies, now)
+ now = now.Add(1001 * time.Millisecond)
+
+ // Serialize non-expired entries in the form "name1=val1 name2=val2".
+ var cs []string
+ for _, submap := range jar.entries {
+ for _, cookie := range submap {
+ if !cookie.Expires.After(now) {
+ continue
+ }
+ cs = append(cs, cookie.Name+"="+cookie.Value)
+ }
+ }
+ sort.Strings(cs)
+ got := strings.Join(cs, " ")
+
+ // Make sure jar content matches our expectations.
+ if got != test.content {
+ t.Errorf("Test %q Content\ngot %q\nwant %q",
+ test.description, got, test.content)
+ }
+
+ // Test different calls to Cookies.
+ for i, query := range test.queries {
+ now = now.Add(1001 * time.Millisecond)
+ var s []string
+ for _, c := range jar.cookies(mustParseURL(query.toURL), now) {
+ s = append(s, c.Name+"="+c.Value)
+ }
+ if got := strings.Join(s, " "); got != query.want {
+ t.Errorf("Test %q #%d\ngot %q\nwant %q", test.description, i, got, query.want)
+ }
+ }
+}
+
+// basicsTests contains fundamental tests. Each jarTest has to be performed on
+// a fresh, empty Jar.
+var basicsTests = [...]jarTest{
+ {
+ "Retrieval of a plain host cookie.",
+ "http://www.host.test/",
+ []string{"A=a"},
+ "A=a",
+ []query{
+ {"http://www.host.test", "A=a"},
+ {"http://www.host.test/", "A=a"},
+ {"http://www.host.test/some/path", "A=a"},
+ {"https://www.host.test", "A=a"},
+ {"https://www.host.test/", "A=a"},
+ {"https://www.host.test/some/path", "A=a"},
+ {"ftp://www.host.test", ""},
+ {"ftp://www.host.test/", ""},
+ {"ftp://www.host.test/some/path", ""},
+ {"http://www.other.org", ""},
+ {"http://sibling.host.test", ""},
+ {"http://deep.www.host.test", ""},
+ },
+ },
+ {
+ "Secure cookies are not returned to http.",
+ "http://www.host.test/",
+ []string{"A=a; secure"},
+ "A=a",
+ []query{
+ {"http://www.host.test", ""},
+ {"http://www.host.test/", ""},
+ {"http://www.host.test/some/path", ""},
+ {"https://www.host.test", "A=a"},
+ {"https://www.host.test/", "A=a"},
+ {"https://www.host.test/some/path", "A=a"},
+ },
+ },
+ {
+ "Explicit path.",
+ "http://www.host.test/",
+ []string{"A=a; path=/some/path"},
+ "A=a",
+ []query{
+ {"http://www.host.test", ""},
+ {"http://www.host.test/", ""},
+ {"http://www.host.test/some", ""},
+ {"http://www.host.test/some/", ""},
+ {"http://www.host.test/some/path", "A=a"},
+ {"http://www.host.test/some/paths", ""},
+ {"http://www.host.test/some/path/foo", "A=a"},
+ {"http://www.host.test/some/path/foo/", "A=a"},
+ },
+ },
+ {
+ "Implicit path #1: path is a directory.",
+ "http://www.host.test/some/path/",
+ []string{"A=a"},
+ "A=a",
+ []query{
+ {"http://www.host.test", ""},
+ {"http://www.host.test/", ""},
+ {"http://www.host.test/some", ""},
+ {"http://www.host.test/some/", ""},
+ {"http://www.host.test/some/path", "A=a"},
+ {"http://www.host.test/some/paths", ""},
+ {"http://www.host.test/some/path/foo", "A=a"},
+ {"http://www.host.test/some/path/foo/", "A=a"},
+ },
+ },
+ {
+ "Implicit path #2: path is not a directory.",
+ "http://www.host.test/some/path/index.html",
+ []string{"A=a"},
+ "A=a",
+ []query{
+ {"http://www.host.test", ""},
+ {"http://www.host.test/", ""},
+ {"http://www.host.test/some", ""},
+ {"http://www.host.test/some/", ""},
+ {"http://www.host.test/some/path", "A=a"},
+ {"http://www.host.test/some/paths", ""},
+ {"http://www.host.test/some/path/foo", "A=a"},
+ {"http://www.host.test/some/path/foo/", "A=a"},
+ },
+ },
+ {
+ "Implicit path #3: no path in URL at all.",
+ "http://www.host.test",
+ []string{"A=a"},
+ "A=a",
+ []query{
+ {"http://www.host.test", "A=a"},
+ {"http://www.host.test/", "A=a"},
+ {"http://www.host.test/some/path", "A=a"},
+ },
+ },
+ {
+ "Cookies are sorted by path length.",
+ "http://www.host.test/",
+ []string{
+ "A=a; path=/foo/bar",
+ "B=b; path=/foo/bar/baz/qux",
+ "C=c; path=/foo/bar/baz",
+ "D=d; path=/foo"},
+ "A=a B=b C=c D=d",
+ []query{
+ {"http://www.host.test/foo/bar/baz/qux", "B=b C=c A=a D=d"},
+ {"http://www.host.test/foo/bar/baz/", "C=c A=a D=d"},
+ {"http://www.host.test/foo/bar", "A=a D=d"},
+ },
+ },
+ {
+ "Creation time determines sorting on same length paths.",
+ "http://www.host.test/",
+ []string{
+ "A=a; path=/foo/bar",
+ "X=x; path=/foo/bar",
+ "Y=y; path=/foo/bar/baz/qux",
+ "B=b; path=/foo/bar/baz/qux",
+ "C=c; path=/foo/bar/baz",
+ "W=w; path=/foo/bar/baz",
+ "Z=z; path=/foo",
+ "D=d; path=/foo"},
+ "A=a B=b C=c D=d W=w X=x Y=y Z=z",
+ []query{
+ {"http://www.host.test/foo/bar/baz/qux", "Y=y B=b C=c W=w A=a X=x Z=z D=d"},
+ {"http://www.host.test/foo/bar/baz/", "C=c W=w A=a X=x Z=z D=d"},
+ {"http://www.host.test/foo/bar", "A=a X=x Z=z D=d"},
+ },
+ },
+ {
+ "Sorting of same-name cookies.",
+ "http://www.host.test/",
+ []string{
+ "A=1; path=/",
+ "A=2; path=/path",
+ "A=3; path=/quux",
+ "A=4; path=/path/foo",
+ "A=5; domain=.host.test; path=/path",
+ "A=6; domain=.host.test; path=/quux",
+ "A=7; domain=.host.test; path=/path/foo",
+ },
+ "A=1 A=2 A=3 A=4 A=5 A=6 A=7",
+ []query{
+ {"http://www.host.test/path", "A=2 A=5 A=1"},
+ {"http://www.host.test/path/foo", "A=4 A=7 A=2 A=5 A=1"},
+ },
+ },
+ {
+ "Disallow domain cookie on public suffix.",
+ "http://www.bbc.co.uk",
+ []string{
+ "a=1",
+ "b=2; domain=co.uk",
+ },
+ "a=1",
+ []query{{"http://www.bbc.co.uk", "a=1"}},
+ },
+ {
+ "Host cookie on IP.",
+ "http://192.168.0.10",
+ []string{"a=1"},
+ "a=1",
+ []query{{"http://192.168.0.10", "a=1"}},
+ },
+ {
+ "Port is ignored #1.",
+ "http://www.host.test/",
+ []string{"a=1"},
+ "a=1",
+ []query{
+ {"http://www.host.test", "a=1"},
+ {"http://www.host.test:8080/", "a=1"},
+ },
+ },
+ {
+ "Port is ignored #2.",
+ "http://www.host.test:8080/",
+ []string{"a=1"},
+ "a=1",
+ []query{
+ {"http://www.host.test", "a=1"},
+ {"http://www.host.test:8080/", "a=1"},
+ {"http://www.host.test:1234/", "a=1"},
+ },
+ },
+}
+
+func TestBasics(t *testing.T) {
+ for _, test := range basicsTests {
+ jar := newTestJar()
+ test.run(t, jar)
+ }
+}
+
+// updateAndDeleteTests contains jarTests which must be performed on the same
+// Jar.
+var updateAndDeleteTests = [...]jarTest{
+ {
+ "Set initial cookies.",
+ "http://www.host.test",
+ []string{
+ "a=1",
+ "b=2; secure",
+ "c=3; httponly",
+ "d=4; secure; httponly"},
+ "a=1 b=2 c=3 d=4",
+ []query{
+ {"http://www.host.test", "a=1 c=3"},
+ {"https://www.host.test", "a=1 b=2 c=3 d=4"},
+ },
+ },
+ {
+ "Update value via http.",
+ "http://www.host.test",
+ []string{
+ "a=w",
+ "b=x; secure",
+ "c=y; httponly",
+ "d=z; secure; httponly"},
+ "a=w b=x c=y d=z",
+ []query{
+ {"http://www.host.test", "a=w c=y"},
+ {"https://www.host.test", "a=w b=x c=y d=z"},
+ },
+ },
+ {
+ "Clear Secure flag from a http.",
+ "http://www.host.test/",
+ []string{
+ "b=xx",
+ "d=zz; httponly"},
+ "a=w b=xx c=y d=zz",
+ []query{{"http://www.host.test", "a=w b=xx c=y d=zz"}},
+ },
+ {
+ "Delete all.",
+ "http://www.host.test/",
+ []string{
+ "a=1; max-Age=-1", // delete via MaxAge
+ "b=2; " + expiresIn(-10), // delete via Expires
+ "c=2; max-age=-1; " + expiresIn(-10), // delete via both
+ "d=4; max-age=-1; " + expiresIn(10)}, // MaxAge takes precedence
+ "",
+ []query{{"http://www.host.test", ""}},
+ },
+ {
+ "Refill #1.",
+ "http://www.host.test",
+ []string{
+ "A=1",
+ "A=2; path=/foo",
+ "A=3; domain=.host.test",
+ "A=4; path=/foo; domain=.host.test"},
+ "A=1 A=2 A=3 A=4",
+ []query{{"http://www.host.test/foo", "A=2 A=4 A=1 A=3"}},
+ },
+ {
+ "Refill #2.",
+ "http://www.google.com",
+ []string{
+ "A=6",
+ "A=7; path=/foo",
+ "A=8; domain=.google.com",
+ "A=9; path=/foo; domain=.google.com"},
+ "A=1 A=2 A=3 A=4 A=6 A=7 A=8 A=9",
+ []query{
+ {"http://www.host.test/foo", "A=2 A=4 A=1 A=3"},
+ {"http://www.google.com/foo", "A=7 A=9 A=6 A=8"},
+ },
+ },
+ {
+ "Delete A7.",
+ "http://www.google.com",
+ []string{"A=; path=/foo; max-age=-1"},
+ "A=1 A=2 A=3 A=4 A=6 A=8 A=9",
+ []query{
+ {"http://www.host.test/foo", "A=2 A=4 A=1 A=3"},
+ {"http://www.google.com/foo", "A=9 A=6 A=8"},
+ },
+ },
+ {
+ "Delete A4.",
+ "http://www.host.test",
+ []string{"A=; path=/foo; domain=host.test; max-age=-1"},
+ "A=1 A=2 A=3 A=6 A=8 A=9",
+ []query{
+ {"http://www.host.test/foo", "A=2 A=1 A=3"},
+ {"http://www.google.com/foo", "A=9 A=6 A=8"},
+ },
+ },
+ {
+ "Delete A6.",
+ "http://www.google.com",
+ []string{"A=; max-age=-1"},
+ "A=1 A=2 A=3 A=8 A=9",
+ []query{
+ {"http://www.host.test/foo", "A=2 A=1 A=3"},
+ {"http://www.google.com/foo", "A=9 A=8"},
+ },
+ },
+ {
+ "Delete A3.",
+ "http://www.host.test",
+ []string{"A=; domain=host.test; max-age=-1"},
+ "A=1 A=2 A=8 A=9",
+ []query{
+ {"http://www.host.test/foo", "A=2 A=1"},
+ {"http://www.google.com/foo", "A=9 A=8"},
+ },
+ },
+ {
+ "No cross-domain delete.",
+ "http://www.host.test",
+ []string{
+ "A=; domain=google.com; max-age=-1",
+ "A=; path=/foo; domain=google.com; max-age=-1"},
+ "A=1 A=2 A=8 A=9",
+ []query{
+ {"http://www.host.test/foo", "A=2 A=1"},
+ {"http://www.google.com/foo", "A=9 A=8"},
+ },
+ },
+ {
+ "Delete A8 and A9.",
+ "http://www.google.com",
+ []string{
+ "A=; domain=google.com; max-age=-1",
+ "A=; path=/foo; domain=google.com; max-age=-1"},
+ "A=1 A=2",
+ []query{
+ {"http://www.host.test/foo", "A=2 A=1"},
+ {"http://www.google.com/foo", ""},
+ },
+ },
+}
+
+func TestUpdateAndDelete(t *testing.T) {
+ jar := newTestJar()
+ for _, test := range updateAndDeleteTests {
+ test.run(t, jar)
+ }
+}
+
+func TestExpiration(t *testing.T) {
+ jar := newTestJar()
+ jarTest{
+ "Expiration.",
+ "http://www.host.test",
+ []string{
+ "a=1",
+ "b=2; max-age=3",
+ "c=3; " + expiresIn(3),
+ "d=4; max-age=5",
+ "e=5; " + expiresIn(5),
+ "f=6; max-age=100",
+ },
+ "a=1 b=2 c=3 d=4 e=5 f=6", // executed at t0 + 1001 ms
+ []query{
+ {"http://www.host.test", "a=1 b=2 c=3 d=4 e=5 f=6"}, // t0 + 2002 ms
+ {"http://www.host.test", "a=1 d=4 e=5 f=6"}, // t0 + 3003 ms
+ {"http://www.host.test", "a=1 d=4 e=5 f=6"}, // t0 + 4004 ms
+ {"http://www.host.test", "a=1 f=6"}, // t0 + 5005 ms
+ {"http://www.host.test", "a=1 f=6"}, // t0 + 6006 ms
+ },
+ }.run(t, jar)
+}
+
+//
+// Tests derived from Chromium's cookie_store_unittest.h.
+//
+
+// See http://src.chromium.org/viewvc/chrome/trunk/src/net/cookies/cookie_store_unittest.h?revision=159685&content-type=text/plain
+// Some of the original tests are in a bad condition (e.g.
+// DomainWithTrailingDotTest) or are not RFC 6265 conforming (e.g.
+// TestNonDottedAndTLD #1 and #6) and have not been ported.
+
+// chromiumBasicsTests contains fundamental tests. Each jarTest has to be
+// performed on a fresh, empty Jar.
+var chromiumBasicsTests = [...]jarTest{
+ {
+ "DomainWithTrailingDotTest.",
+ "http://www.google.com/",
+ []string{
+ "a=1; domain=.www.google.com.",
+ "b=2; domain=.www.google.com.."},
+ "",
+ []query{
+ {"http://www.google.com", ""},
+ },
+ },
+ {
+ "ValidSubdomainTest #1.",
+ "http://a.b.c.d.com",
+ []string{
+ "a=1; domain=.a.b.c.d.com",
+ "b=2; domain=.b.c.d.com",
+ "c=3; domain=.c.d.com",
+ "d=4; domain=.d.com"},
+ "a=1 b=2 c=3 d=4",
+ []query{
+ {"http://a.b.c.d.com", "a=1 b=2 c=3 d=4"},
+ {"http://b.c.d.com", "b=2 c=3 d=4"},
+ {"http://c.d.com", "c=3 d=4"},
+ {"http://d.com", "d=4"},
+ },
+ },
+ {
+ "ValidSubdomainTest #2.",
+ "http://a.b.c.d.com",
+ []string{
+ "a=1; domain=.a.b.c.d.com",
+ "b=2; domain=.b.c.d.com",
+ "c=3; domain=.c.d.com",
+ "d=4; domain=.d.com",
+ "X=bcd; domain=.b.c.d.com",
+ "X=cd; domain=.c.d.com"},
+ "X=bcd X=cd a=1 b=2 c=3 d=4",
+ []query{
+ {"http://b.c.d.com", "b=2 c=3 d=4 X=bcd X=cd"},
+ {"http://c.d.com", "c=3 d=4 X=cd"},
+ },
+ },
+ {
+ "InvalidDomainTest #1.",
+ "http://foo.bar.com",
+ []string{
+ "a=1; domain=.yo.foo.bar.com",
+ "b=2; domain=.foo.com",
+ "c=3; domain=.bar.foo.com",
+ "d=4; domain=.foo.bar.com.net",
+ "e=5; domain=ar.com",
+ "f=6; domain=.",
+ "g=7; domain=/",
+ "h=8; domain=http://foo.bar.com",
+ "i=9; domain=..foo.bar.com",
+ "j=10; domain=..bar.com",
+ "k=11; domain=.foo.bar.com?blah",
+ "l=12; domain=.foo.bar.com/blah",
+ "m=12; domain=.foo.bar.com:80",
+ "n=14; domain=.foo.bar.com:",
+ "o=15; domain=.foo.bar.com#sup",
+ },
+ "", // Jar is empty.
+ []query{{"http://foo.bar.com", ""}},
+ },
+ {
+ "InvalidDomainTest #2.",
+ "http://foo.com.com",
+ []string{"a=1; domain=.foo.com.com.com"},
+ "",
+ []query{{"http://foo.bar.com", ""}},
+ },
+ {
+ "DomainWithoutLeadingDotTest #1.",
+ "http://manage.hosted.filefront.com",
+ []string{"a=1; domain=filefront.com"},
+ "a=1",
+ []query{{"http://www.filefront.com", "a=1"}},
+ },
+ {
+ "DomainWithoutLeadingDotTest #2.",
+ "http://www.google.com",
+ []string{"a=1; domain=www.google.com"},
+ "a=1",
+ []query{
+ {"http://www.google.com", "a=1"},
+ {"http://sub.www.google.com", "a=1"},
+ {"http://something-else.com", ""},
+ },
+ },
+ {
+ "CaseInsensitiveDomainTest.",
+ "http://www.google.com",
+ []string{
+ "a=1; domain=.GOOGLE.COM",
+ "b=2; domain=.www.gOOgLE.coM"},
+ "a=1 b=2",
+ []query{{"http://www.google.com", "a=1 b=2"}},
+ },
+ {
+ "TestIpAddress #1.",
+ "http://1.2.3.4/foo",
+ []string{"a=1; path=/"},
+ "a=1",
+ []query{{"http://1.2.3.4/foo", "a=1"}},
+ },
+ {
+ "TestIpAddress #2.",
+ "http://1.2.3.4/foo",
+ []string{
+ "a=1; domain=.1.2.3.4",
+ "b=2; domain=.3.4"},
+ "",
+ []query{{"http://1.2.3.4/foo", ""}},
+ },
+ {
+ "TestIpAddress #3.",
+ "http://1.2.3.4/foo",
+ []string{"a=1; domain=1.2.3.4"},
+ "",
+ []query{{"http://1.2.3.4/foo", ""}},
+ },
+ {
+ "TestNonDottedAndTLD #2.",
+ "http://com./index.html",
+ []string{"a=1"},
+ "a=1",
+ []query{
+ {"http://com./index.html", "a=1"},
+ {"http://no-cookies.com./index.html", ""},
+ },
+ },
+ {
+ "TestNonDottedAndTLD #3.",
+ "http://a.b",
+ []string{
+ "a=1; domain=.b",
+ "b=2; domain=b"},
+ "",
+ []query{{"http://bar.foo", ""}},
+ },
+ {
+ "TestNonDottedAndTLD #4.",
+ "http://google.com",
+ []string{
+ "a=1; domain=.com",
+ "b=2; domain=com"},
+ "",
+ []query{{"http://google.com", ""}},
+ },
+ {
+ "TestNonDottedAndTLD #5.",
+ "http://google.co.uk",
+ []string{
+ "a=1; domain=.co.uk",
+ "b=2; domain=.uk"},
+ "",
+ []query{
+ {"http://google.co.uk", ""},
+ {"http://else.co.com", ""},
+ {"http://else.uk", ""},
+ },
+ },
+ {
+ "TestHostEndsWithDot.",
+ "http://www.google.com",
+ []string{
+ "a=1",
+ "b=2; domain=.www.google.com."},
+ "a=1",
+ []query{{"http://www.google.com", "a=1"}},
+ },
+ {
+ "PathTest",
+ "http://www.google.izzle",
+ []string{"a=1; path=/wee"},
+ "a=1",
+ []query{
+ {"http://www.google.izzle/wee", "a=1"},
+ {"http://www.google.izzle/wee/", "a=1"},
+ {"http://www.google.izzle/wee/war", "a=1"},
+ {"http://www.google.izzle/wee/war/more/more", "a=1"},
+ {"http://www.google.izzle/weehee", ""},
+ {"http://www.google.izzle/", ""},
+ },
+ },
+}
+
+func TestChromiumBasics(t *testing.T) {
+ for _, test := range chromiumBasicsTests {
+ jar := newTestJar()
+ test.run(t, jar)
+ }
+}
+
+// chromiumDomainTests contains jarTests which must be executed all on the
+// same Jar.
+var chromiumDomainTests = [...]jarTest{
+ {
+ "Fill #1.",
+ "http://www.google.izzle",
+ []string{"A=B"},
+ "A=B",
+ []query{{"http://www.google.izzle", "A=B"}},
+ },
+ {
+ "Fill #2.",
+ "http://www.google.izzle",
+ []string{"C=D; domain=.google.izzle"},
+ "A=B C=D",
+ []query{{"http://www.google.izzle", "A=B C=D"}},
+ },
+ {
+ "Verify A is a host cookie and not accessible from subdomain.",
+ "http://unused.nil",
+ []string{},
+ "A=B C=D",
+ []query{{"http://foo.www.google.izzle", "C=D"}},
+ },
+ {
+ "Verify domain cookies are found on proper domain.",
+ "http://www.google.izzle",
+ []string{"E=F; domain=.www.google.izzle"},
+ "A=B C=D E=F",
+ []query{{"http://www.google.izzle", "A=B C=D E=F"}},
+ },
+ {
+ "Leading dots in domain attributes are optional.",
+ "http://www.google.izzle",
+ []string{"G=H; domain=www.google.izzle"},
+ "A=B C=D E=F G=H",
+ []query{{"http://www.google.izzle", "A=B C=D E=F G=H"}},
+ },
+ {
+ "Verify domain enforcement works #1.",
+ "http://www.google.izzle",
+ []string{"K=L; domain=.bar.www.google.izzle"},
+ "A=B C=D E=F G=H",
+ []query{{"http://bar.www.google.izzle", "C=D E=F G=H"}},
+ },
+ {
+ "Verify domain enforcement works #2.",
+ "http://unused.nil",
+ []string{},
+ "A=B C=D E=F G=H",
+ []query{{"http://www.google.izzle", "A=B C=D E=F G=H"}},
+ },
+}
+
+func TestChromiumDomain(t *testing.T) {
+ jar := newTestJar()
+ for _, test := range chromiumDomainTests {
+ test.run(t, jar)
+ }
+
+}
+
+// chromiumDeletionTests must be performed all on the same Jar.
+var chromiumDeletionTests = [...]jarTest{
+ {
+ "Create session cookie a1.",
+ "http://www.google.com",
+ []string{"a=1"},
+ "a=1",
+ []query{{"http://www.google.com", "a=1"}},
+ },
+ {
+ "Delete sc a1 via MaxAge.",
+ "http://www.google.com",
+ []string{"a=1; max-age=-1"},
+ "",
+ []query{{"http://www.google.com", ""}},
+ },
+ {
+ "Create session cookie b2.",
+ "http://www.google.com",
+ []string{"b=2"},
+ "b=2",
+ []query{{"http://www.google.com", "b=2"}},
+ },
+ {
+ "Delete sc b2 via Expires.",
+ "http://www.google.com",
+ []string{"b=2; " + expiresIn(-10)},
+ "",
+ []query{{"http://www.google.com", ""}},
+ },
+ {
+ "Create persistent cookie c3.",
+ "http://www.google.com",
+ []string{"c=3; max-age=3600"},
+ "c=3",
+ []query{{"http://www.google.com", "c=3"}},
+ },
+ {
+ "Delete pc c3 via MaxAge.",
+ "http://www.google.com",
+ []string{"c=3; max-age=-1"},
+ "",
+ []query{{"http://www.google.com", ""}},
+ },
+ {
+ "Create persistent cookie d4.",
+ "http://www.google.com",
+ []string{"d=4; max-age=3600"},
+ "d=4",
+ []query{{"http://www.google.com", "d=4"}},
+ },
+ {
+ "Delete pc d4 via Expires.",
+ "http://www.google.com",
+ []string{"d=4; " + expiresIn(-10)},
+ "",
+ []query{{"http://www.google.com", ""}},
+ },
+}
+
+func TestChromiumDeletion(t *testing.T) {
+ jar := newTestJar()
+ for _, test := range chromiumDeletionTests {
+ test.run(t, jar)
+ }
+}
+
+// domainHandlingTests tests and documents the rules for domain handling.
+// Each test must be performed on an empty new Jar.
+var domainHandlingTests = [...]jarTest{
+ {
+ "Host cookie",
+ "http://www.host.test",
+ []string{"a=1"},
+ "a=1",
+ []query{
+ {"http://www.host.test", "a=1"},
+ {"http://host.test", ""},
+ {"http://bar.host.test", ""},
+ {"http://foo.www.host.test", ""},
+ {"http://other.test", ""},
+ {"http://test", ""},
+ },
+ },
+ {
+ "Domain cookie #1",
+ "http://www.host.test",
+ []string{"a=1; domain=host.test"},
+ "a=1",
+ []query{
+ {"http://www.host.test", "a=1"},
+ {"http://host.test", "a=1"},
+ {"http://bar.host.test", "a=1"},
+ {"http://foo.www.host.test", "a=1"},
+ {"http://other.test", ""},
+ {"http://test", ""},
+ },
+ },
+ {
+ "Domain cookie #2",
+ "http://www.host.test",
+ []string{"a=1; domain=.host.test"},
+ "a=1",
+ []query{
+ {"http://www.host.test", "a=1"},
+ {"http://host.test", "a=1"},
+ {"http://bar.host.test", "a=1"},
+ {"http://foo.www.host.test", "a=1"},
+ {"http://other.test", ""},
+ {"http://test", ""},
+ },
+ },
+ {
+ "Host cookie on IDNA domain #1",
+ "http://www.bücher.test",
+ []string{"a=1"},
+ "a=1",
+ []query{
+ {"http://www.bücher.test", "a=1"},
+ {"http://www.xn--bcher-kva.test", "a=1"},
+ {"http://bücher.test", ""},
+ {"http://xn--bcher-kva.test", ""},
+ {"http://bar.bücher.test", ""},
+ {"http://bar.xn--bcher-kva.test", ""},
+ {"http://foo.www.bücher.test", ""},
+ {"http://foo.www.xn--bcher-kva.test", ""},
+ {"http://other.test", ""},
+ {"http://test", ""},
+ },
+ },
+ {
+ "Host cookie on IDNA domain #2",
+ "http://www.xn--bcher-kva.test",
+ []string{"a=1"},
+ "a=1",
+ []query{
+ {"http://www.bücher.test", "a=1"},
+ {"http://www.xn--bcher-kva.test", "a=1"},
+ {"http://bücher.test", ""},
+ {"http://xn--bcher-kva.test", ""},
+ {"http://bar.bücher.test", ""},
+ {"http://bar.xn--bcher-kva.test", ""},
+ {"http://foo.www.bücher.test", ""},
+ {"http://foo.www.xn--bcher-kva.test", ""},
+ {"http://other.test", ""},
+ {"http://test", ""},
+ },
+ },
+ {
+ "Domain cookie on IDNA domain #1",
+ "http://www.bücher.test",
+ []string{"a=1; domain=xn--bcher-kva.test"},
+ "a=1",
+ []query{
+ {"http://www.bücher.test", "a=1"},
+ {"http://www.xn--bcher-kva.test", "a=1"},
+ {"http://bücher.test", "a=1"},
+ {"http://xn--bcher-kva.test", "a=1"},
+ {"http://bar.bücher.test", "a=1"},
+ {"http://bar.xn--bcher-kva.test", "a=1"},
+ {"http://foo.www.bücher.test", "a=1"},
+ {"http://foo.www.xn--bcher-kva.test", "a=1"},
+ {"http://other.test", ""},
+ {"http://test", ""},
+ },
+ },
+ {
+ "Domain cookie on IDNA domain #2",
+ "http://www.xn--bcher-kva.test",
+ []string{"a=1; domain=xn--bcher-kva.test"},
+ "a=1",
+ []query{
+ {"http://www.bücher.test", "a=1"},
+ {"http://www.xn--bcher-kva.test", "a=1"},
+ {"http://bücher.test", "a=1"},
+ {"http://xn--bcher-kva.test", "a=1"},
+ {"http://bar.bücher.test", "a=1"},
+ {"http://bar.xn--bcher-kva.test", "a=1"},
+ {"http://foo.www.bücher.test", "a=1"},
+ {"http://foo.www.xn--bcher-kva.test", "a=1"},
+ {"http://other.test", ""},
+ {"http://test", ""},
+ },
+ },
+ {
+ "Host cookie on TLD.",
+ "http://com",
+ []string{"a=1"},
+ "a=1",
+ []query{
+ {"http://com", "a=1"},
+ {"http://any.com", ""},
+ {"http://any.test", ""},
+ },
+ },
+ {
+ "Domain cookie on TLD becomes a host cookie.",
+ "http://com",
+ []string{"a=1; domain=com"},
+ "a=1",
+ []query{
+ {"http://com", "a=1"},
+ {"http://any.com", ""},
+ {"http://any.test", ""},
+ },
+ },
+ {
+ "Host cookie on public suffix.",
+ "http://co.uk",
+ []string{"a=1"},
+ "a=1",
+ []query{
+ {"http://co.uk", "a=1"},
+ {"http://uk", ""},
+ {"http://some.co.uk", ""},
+ {"http://foo.some.co.uk", ""},
+ {"http://any.uk", ""},
+ },
+ },
+ {
+ "Domain cookie on public suffix is ignored.",
+ "http://some.co.uk",
+ []string{"a=1; domain=co.uk"},
+ "",
+ []query{
+ {"http://co.uk", ""},
+ {"http://uk", ""},
+ {"http://some.co.uk", ""},
+ {"http://foo.some.co.uk", ""},
+ {"http://any.uk", ""},
+ },
+ },
+}
+
+func TestDomainHandling(t *testing.T) {
+ for _, test := range domainHandlingTests {
+ jar := newTestJar()
+ test.run(t, jar)
+ }
+}
diff --git a/src/pkg/net/http/cookiejar/punycode.go b/src/pkg/net/http/cookiejar/punycode.go
new file mode 100644
index 000000000..ea7ceb5ef
--- /dev/null
+++ b/src/pkg/net/http/cookiejar/punycode.go
@@ -0,0 +1,159 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cookiejar
+
+// This file implements the Punycode algorithm from RFC 3492.
+
+import (
+ "fmt"
+ "strings"
+ "unicode/utf8"
+)
+
+// These parameter values are specified in section 5.
+//
+// All computation is done with int32s, so that overflow behavior is identical
+// regardless of whether int is 32-bit or 64-bit.
+const (
+ base int32 = 36
+ damp int32 = 700
+ initialBias int32 = 72
+ initialN int32 = 128
+ skew int32 = 38
+ tmax int32 = 26
+ tmin int32 = 1
+)
+
+// encode encodes a string as specified in section 6.3 and prepends prefix to
+// the result.
+//
+// The "while h < length(input)" line in the specification becomes "for
+// remaining != 0" in the Go code, because len(s) in Go is in bytes, not runes.
+func encode(prefix, s string) (string, error) {
+ output := make([]byte, len(prefix), len(prefix)+1+2*len(s))
+ copy(output, prefix)
+ delta, n, bias := int32(0), initialN, initialBias
+ b, remaining := int32(0), int32(0)
+ for _, r := range s {
+ if r < 0x80 {
+ b++
+ output = append(output, byte(r))
+ } else {
+ remaining++
+ }
+ }
+ h := b
+ if b > 0 {
+ output = append(output, '-')
+ }
+ for remaining != 0 {
+ m := int32(0x7fffffff)
+ for _, r := range s {
+ if m > r && r >= n {
+ m = r
+ }
+ }
+ delta += (m - n) * (h + 1)
+ if delta < 0 {
+ return "", fmt.Errorf("cookiejar: invalid label %q", s)
+ }
+ n = m
+ for _, r := range s {
+ if r < n {
+ delta++
+ if delta < 0 {
+ return "", fmt.Errorf("cookiejar: invalid label %q", s)
+ }
+ continue
+ }
+ if r > n {
+ continue
+ }
+ q := delta
+ for k := base; ; k += base {
+ t := k - bias
+ if t < tmin {
+ t = tmin
+ } else if t > tmax {
+ t = tmax
+ }
+ if q < t {
+ break
+ }
+ output = append(output, encodeDigit(t+(q-t)%(base-t)))
+ q = (q - t) / (base - t)
+ }
+ output = append(output, encodeDigit(q))
+ bias = adapt(delta, h+1, h == b)
+ delta = 0
+ h++
+ remaining--
+ }
+ delta++
+ n++
+ }
+ return string(output), nil
+}
+
+func encodeDigit(digit int32) byte {
+ switch {
+ case 0 <= digit && digit < 26:
+ return byte(digit + 'a')
+ case 26 <= digit && digit < 36:
+ return byte(digit + ('0' - 26))
+ }
+ panic("cookiejar: internal error in punycode encoding")
+}
+
+// adapt is the bias adaptation function specified in section 6.1.
+func adapt(delta, numPoints int32, firstTime bool) int32 {
+ if firstTime {
+ delta /= damp
+ } else {
+ delta /= 2
+ }
+ delta += delta / numPoints
+ k := int32(0)
+ for delta > ((base-tmin)*tmax)/2 {
+ delta /= base - tmin
+ k += base
+ }
+ return k + (base-tmin+1)*delta/(delta+skew)
+}
+
+// Strictly speaking, the remaining code below deals with IDNA (RFC 5890 and
+// friends) and not Punycode (RFC 3492) per se.
+
+// acePrefix is the ASCII Compatible Encoding prefix.
+const acePrefix = "xn--"
+
+// toASCII converts a domain or domain label to its ASCII form. For example,
+// toASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
+// toASCII("golang") is "golang".
+func toASCII(s string) (string, error) {
+ if ascii(s) {
+ return s, nil
+ }
+ labels := strings.Split(s, ".")
+ for i, label := range labels {
+ if !ascii(label) {
+ a, err := encode(acePrefix, label)
+ if err != nil {
+ return "", err
+ }
+ labels[i] = a
+ }
+ }
+ return strings.Join(labels, "."), nil
+}
+
+func ascii(s string) bool {
+ for i := 0; i < len(s); i++ {
+ if s[i] >= utf8.RuneSelf {
+ return false
+ }
+ }
+ return true
+}
diff --git a/src/pkg/net/http/cookiejar/punycode_test.go b/src/pkg/net/http/cookiejar/punycode_test.go
new file mode 100644
index 000000000..0301de14e
--- /dev/null
+++ b/src/pkg/net/http/cookiejar/punycode_test.go
@@ -0,0 +1,161 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cookiejar
+
+import (
+ "testing"
+)
+
+var punycodeTestCases = [...]struct {
+ s, encoded string
+}{
+ {"", ""},
+ {"-", "--"},
+ {"-a", "-a-"},
+ {"-a-", "-a--"},
+ {"a", "a-"},
+ {"a-", "a--"},
+ {"a-b", "a-b-"},
+ {"books", "books-"},
+ {"bücher", "bcher-kva"},
+ {"Hello世界", "Hello-ck1hg65u"},
+ {"ü", "tda"},
+ {"üý", "tdac"},
+
+ // The test cases below come from RFC 3492 section 7.1 with Errata 3026.
+ {
+ // (A) Arabic (Egyptian).
+ "\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644" +
+ "\u0645\u0648\u0634\u0639\u0631\u0628\u064A\u061F",
+ "egbpdaj6bu4bxfgehfvwxn",
+ },
+ {
+ // (B) Chinese (simplified).
+ "\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2D\u6587",
+ "ihqwcrb4cv8a8dqg056pqjye",
+ },
+ {
+ // (C) Chinese (traditional).
+ "\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587",
+ "ihqwctvzc91f659drss3x8bo0yb",
+ },
+ {
+ // (D) Czech.
+ "\u0050\u0072\u006F\u010D\u0070\u0072\u006F\u0073\u0074" +
+ "\u011B\u006E\u0065\u006D\u006C\u0075\u0076\u00ED\u010D" +
+ "\u0065\u0073\u006B\u0079",
+ "Proprostnemluvesky-uyb24dma41a",
+ },
+ {
+ // (E) Hebrew.
+ "\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8" +
+ "\u05DC\u05D0\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2" +
+ "\u05D1\u05E8\u05D9\u05EA",
+ "4dbcagdahymbxekheh6e0a7fei0b",
+ },
+ {
+ // (F) Hindi (Devanagari).
+ "\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D" +
+ "\u0926\u0940\u0915\u094D\u092F\u094B\u0902\u0928\u0939" +
+ "\u0940\u0902\u092C\u094B\u0932\u0938\u0915\u0924\u0947" +
+ "\u0939\u0948\u0902",
+ "i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd",
+ },
+ {
+ // (G) Japanese (kanji and hiragana).
+ "\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092" +
+ "\u8A71\u3057\u3066\u304F\u308C\u306A\u3044\u306E\u304B",
+ "n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa",
+ },
+ {
+ // (H) Korean (Hangul syllables).
+ "\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774" +
+ "\uD55C\uAD6D\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74" +
+ "\uC5BC\uB9C8\uB098\uC88B\uC744\uAE4C",
+ "989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5j" +
+ "psd879ccm6fea98c",
+ },
+ {
+ // (I) Russian (Cyrillic).
+ "\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E" +
+ "\u043D\u0438\u043D\u0435\u0433\u043E\u0432\u043E\u0440" +
+ "\u044F\u0442\u043F\u043E\u0440\u0443\u0441\u0441\u043A" +
+ "\u0438",
+ "b1abfaaepdrnnbgefbadotcwatmq2g4l",
+ },
+ {
+ // (J) Spanish.
+ "\u0050\u006F\u0072\u0071\u0075\u00E9\u006E\u006F\u0070" +
+ "\u0075\u0065\u0064\u0065\u006E\u0073\u0069\u006D\u0070" +
+ "\u006C\u0065\u006D\u0065\u006E\u0074\u0065\u0068\u0061" +
+ "\u0062\u006C\u0061\u0072\u0065\u006E\u0045\u0073\u0070" +
+ "\u0061\u00F1\u006F\u006C",
+ "PorqunopuedensimplementehablarenEspaol-fmd56a",
+ },
+ {
+ // (K) Vietnamese.
+ "\u0054\u1EA1\u0069\u0073\u0061\u006F\u0068\u1ECD\u006B" +
+ "\u0068\u00F4\u006E\u0067\u0074\u0068\u1EC3\u0063\u0068" +
+ "\u1EC9\u006E\u00F3\u0069\u0074\u0069\u1EBF\u006E\u0067" +
+ "\u0056\u0069\u1EC7\u0074",
+ "TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g",
+ },
+ {
+ // (L) 3<nen>B<gumi><kinpachi><sensei>.
+ "\u0033\u5E74\u0042\u7D44\u91D1\u516B\u5148\u751F",
+ "3B-ww4c5e180e575a65lsy2b",
+ },
+ {
+ // (M) <amuro><namie>-with-SUPER-MONKEYS.
+ "\u5B89\u5BA4\u5948\u7F8E\u6075\u002D\u0077\u0069\u0074" +
+ "\u0068\u002D\u0053\u0055\u0050\u0045\u0052\u002D\u004D" +
+ "\u004F\u004E\u004B\u0045\u0059\u0053",
+ "-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n",
+ },
+ {
+ // (N) Hello-Another-Way-<sorezore><no><basho>.
+ "\u0048\u0065\u006C\u006C\u006F\u002D\u0041\u006E\u006F" +
+ "\u0074\u0068\u0065\u0072\u002D\u0057\u0061\u0079\u002D" +
+ "\u305D\u308C\u305E\u308C\u306E\u5834\u6240",
+ "Hello-Another-Way--fc4qua05auwb3674vfr0b",
+ },
+ {
+ // (O) <hitotsu><yane><no><shita>2.
+ "\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B\u0032",
+ "2-u9tlzr9756bt3uc0v",
+ },
+ {
+ // (P) Maji<de>Koi<suru>5<byou><mae>
+ "\u004D\u0061\u006A\u0069\u3067\u004B\u006F\u0069\u3059" +
+ "\u308B\u0035\u79D2\u524D",
+ "MajiKoi5-783gue6qz075azm5e",
+ },
+ {
+ // (Q) <pafii>de<runba>
+ "\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0",
+ "de-jg4avhby1noc0d",
+ },
+ {
+ // (R) <sono><supiido><de>
+ "\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067",
+ "d9juau41awczczp",
+ },
+ {
+ // (S) -> $1.00 <-
+ "\u002D\u003E\u0020\u0024\u0031\u002E\u0030\u0030\u0020" +
+ "\u003C\u002D",
+ "-> $1.00 <--",
+ },
+}
+
+func TestPunycode(t *testing.T) {
+ for _, tc := range punycodeTestCases {
+ if got, err := encode("", tc.s); err != nil {
+ t.Errorf(`encode("", %q): %v`, tc.s, err)
+ } else if got != tc.encoded {
+ t.Errorf(`encode("", %q): got %q, want %q`, tc.s, got, tc.encoded)
+ }
+ }
+}
diff --git a/src/pkg/net/http/example_test.go b/src/pkg/net/http/example_test.go
index ec814407d..22073eaf7 100644
--- a/src/pkg/net/http/example_test.go
+++ b/src/pkg/net/http/example_test.go
@@ -43,10 +43,10 @@ func ExampleGet() {
log.Fatal(err)
}
robots, err := ioutil.ReadAll(res.Body)
+ res.Body.Close()
if err != nil {
log.Fatal(err)
}
- res.Body.Close()
fmt.Printf("%s", robots)
}
diff --git a/src/pkg/net/http/export_test.go b/src/pkg/net/http/export_test.go
index 13640ca85..a7bca20a0 100644
--- a/src/pkg/net/http/export_test.go
+++ b/src/pkg/net/http/export_test.go
@@ -7,12 +7,25 @@
package http
-import "time"
+import (
+ "net"
+ "time"
+)
+
+func NewLoggingConn(baseName string, c net.Conn) net.Conn {
+ return newLoggingConn(baseName, c)
+}
+
+func (t *Transport) NumPendingRequestsForTesting() int {
+ t.reqMu.Lock()
+ defer t.reqMu.Unlock()
+ return len(t.reqConn)
+}
func (t *Transport) IdleConnKeysForTesting() (keys []string) {
keys = make([]string, 0)
- t.lk.Lock()
- defer t.lk.Unlock()
+ t.idleMu.Lock()
+ defer t.idleMu.Unlock()
if t.idleConn == nil {
return
}
@@ -23,8 +36,8 @@ func (t *Transport) IdleConnKeysForTesting() (keys []string) {
}
func (t *Transport) IdleConnCountForTesting(cacheKey string) int {
- t.lk.Lock()
- defer t.lk.Unlock()
+ t.idleMu.Lock()
+ defer t.idleMu.Unlock()
if t.idleConn == nil {
return 0
}
diff --git a/src/pkg/net/http/filetransport_test.go b/src/pkg/net/http/filetransport_test.go
index 039926b53..6f1a537e2 100644
--- a/src/pkg/net/http/filetransport_test.go
+++ b/src/pkg/net/http/filetransport_test.go
@@ -2,11 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package http_test
+package http
import (
"io/ioutil"
- "net/http"
"os"
"path/filepath"
"testing"
@@ -32,9 +31,9 @@ func TestFileTransport(t *testing.T) {
defer os.Remove(dname)
defer os.Remove(fname)
- tr := &http.Transport{}
- tr.RegisterProtocol("file", http.NewFileTransport(http.Dir(dname)))
- c := &http.Client{Transport: tr}
+ tr := &Transport{}
+ tr.RegisterProtocol("file", NewFileTransport(Dir(dname)))
+ c := &Client{Transport: tr}
fooURLs := []string{"file:///foo.txt", "file://../foo.txt"}
for _, urlstr := range fooURLs {
@@ -62,4 +61,5 @@ func TestFileTransport(t *testing.T) {
if res.StatusCode != 404 {
t.Errorf("for %s, StatusCode = %d, want 404", badURL, res.StatusCode)
}
+ res.Body.Close()
}
diff --git a/src/pkg/net/http/fs.go b/src/pkg/net/http/fs.go
index f35dd32c3..b6bea0dfa 100644
--- a/src/pkg/net/http/fs.go
+++ b/src/pkg/net/http/fs.go
@@ -11,6 +11,8 @@ import (
"fmt"
"io"
"mime"
+ "mime/multipart"
+ "net/textproto"
"os"
"path"
"path/filepath"
@@ -26,7 +28,8 @@ import (
type Dir string
func (d Dir) Open(name string) (File, error) {
- if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 {
+ if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
+ strings.Contains(name, "\x00") {
return nil, errors.New("http: invalid character in file path")
}
dir := string(d)
@@ -97,6 +100,9 @@ func dirList(w ResponseWriter, f File) {
// The content's Seek method must work: ServeContent uses
// a seek to the end of the content to determine its size.
//
+// If the caller has set w's ETag header, ServeContent uses it to
+// handle requests using If-Range and If-None-Match.
+//
// Note that *os.File implements the io.ReadSeeker interface.
func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) {
size, err := content.Seek(0, os.SEEK_END)
@@ -119,12 +125,17 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
if checkLastModified(w, r, modtime) {
return
}
+ rangeReq, done := checkETag(w, r)
+ if done {
+ return
+ }
code := StatusOK
// If Content-Type isn't set, use the file's extension to find it.
- if w.Header().Get("Content-Type") == "" {
- ctype := mime.TypeByExtension(filepath.Ext(name))
+ ctype := w.Header().Get("Content-Type")
+ if ctype == "" {
+ ctype = mime.TypeByExtension(filepath.Ext(name))
if ctype == "" {
// read a chunk to decide between utf-8 text and binary
var buf [1024]byte
@@ -141,18 +152,34 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
}
// handle Content-Range header.
- // TODO(adg): handle multiple ranges
sendSize := size
+ var sendContent io.Reader = content
if size >= 0 {
- ranges, err := parseRange(r.Header.Get("Range"), size)
- if err == nil && len(ranges) > 1 {
- err = errors.New("multiple ranges not supported")
- }
+ ranges, err := parseRange(rangeReq, size)
if err != nil {
Error(w, err.Error(), StatusRequestedRangeNotSatisfiable)
return
}
- if len(ranges) == 1 {
+ if sumRangesSize(ranges) >= size {
+ // The total number of bytes in all the ranges
+ // is larger than the size of the file by
+ // itself, so this is probably an attack, or a
+ // dumb client. Ignore the range request.
+ ranges = nil
+ }
+ switch {
+ case len(ranges) == 1:
+ // RFC 2616, Section 14.16:
+ // "When an HTTP message includes the content of a single
+ // range (for example, a response to a request for a
+ // single range, or to a request for a set of ranges
+ // that overlap without any holes), this content is
+ // transmitted with a Content-Range header, and a
+ // Content-Length header showing the number of bytes
+ // actually transferred.
+ // ...
+ // A response to a request for a single range MUST NOT
+ // be sent using the multipart/byteranges media type."
ra := ranges[0]
if _, err := content.Seek(ra.start, os.SEEK_SET); err != nil {
Error(w, err.Error(), StatusRequestedRangeNotSatisfiable)
@@ -160,7 +187,41 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
}
sendSize = ra.length
code = StatusPartialContent
- w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, size))
+ w.Header().Set("Content-Range", ra.contentRange(size))
+ case len(ranges) > 1:
+ for _, ra := range ranges {
+ if ra.start > size {
+ Error(w, err.Error(), StatusRequestedRangeNotSatisfiable)
+ return
+ }
+ }
+ sendSize = rangesMIMESize(ranges, ctype, size)
+ code = StatusPartialContent
+
+ pr, pw := io.Pipe()
+ mw := multipart.NewWriter(pw)
+ w.Header().Set("Content-Type", "multipart/byteranges; boundary="+mw.Boundary())
+ sendContent = pr
+ defer pr.Close() // cause writing goroutine to fail and exit if CopyN doesn't finish.
+ go func() {
+ for _, ra := range ranges {
+ part, err := mw.CreatePart(ra.mimeHeader(ctype, size))
+ if err != nil {
+ pw.CloseWithError(err)
+ return
+ }
+ if _, err := content.Seek(ra.start, os.SEEK_SET); err != nil {
+ pw.CloseWithError(err)
+ return
+ }
+ if _, err := io.CopyN(part, content, ra.length); err != nil {
+ pw.CloseWithError(err)
+ return
+ }
+ }
+ mw.Close()
+ pw.Close()
+ }()
}
w.Header().Set("Accept-Ranges", "bytes")
@@ -172,11 +233,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
w.WriteHeader(code)
if r.Method != "HEAD" {
- if sendSize == -1 {
- io.Copy(w, content)
- } else {
- io.CopyN(w, content, sendSize)
- }
+ io.CopyN(w, sendContent, sendSize)
}
}
@@ -190,6 +247,9 @@ func checkLastModified(w ResponseWriter, r *Request, modtime time.Time) bool {
// The Date-Modified header truncates sub-second precision, so
// use mtime < t+1s instead of mtime <= t to check for unmodified.
if t, err := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) {
+ h := w.Header()
+ delete(h, "Content-Type")
+ delete(h, "Content-Length")
w.WriteHeader(StatusNotModified)
return true
}
@@ -197,6 +257,58 @@ func checkLastModified(w ResponseWriter, r *Request, modtime time.Time) bool {
return false
}
+// checkETag implements If-None-Match and If-Range checks.
+// The ETag must have been previously set in the ResponseWriter's headers.
+//
+// The return value is the effective request "Range" header to use and
+// whether this request is now considered done.
+func checkETag(w ResponseWriter, r *Request) (rangeReq string, done bool) {
+ etag := w.Header().get("Etag")
+ rangeReq = r.Header.get("Range")
+
+ // Invalidate the range request if the entity doesn't match the one
+ // the client was expecting.
+ // "If-Range: version" means "ignore the Range: header unless version matches the
+ // current file."
+ // We only support ETag versions.
+ // The caller must have set the ETag on the response already.
+ if ir := r.Header.get("If-Range"); ir != "" && ir != etag {
+ // TODO(bradfitz): handle If-Range requests with Last-Modified
+ // times instead of ETags? I'd rather not, at least for
+ // now. That seems like a bug/compromise in the RFC 2616, and
+ // I've never heard of anybody caring about that (yet).
+ rangeReq = ""
+ }
+
+ if inm := r.Header.get("If-None-Match"); inm != "" {
+ // Must know ETag.
+ if etag == "" {
+ return rangeReq, false
+ }
+
+ // TODO(bradfitz): non-GET/HEAD requests require more work:
+ // sending a different status code on matches, and
+ // also can't use weak cache validators (those with a "W/
+ // prefix). But most users of ServeContent will be using
+ // it on GET or HEAD, so only support those for now.
+ if r.Method != "GET" && r.Method != "HEAD" {
+ return rangeReq, false
+ }
+
+ // TODO(bradfitz): deal with comma-separated or multiple-valued
+ // list of If-None-match values. For now just handle the common
+ // case of a single item.
+ if inm == etag || inm == "*" {
+ h := w.Header()
+ delete(h, "Content-Type")
+ delete(h, "Content-Length")
+ w.WriteHeader(StatusNotModified)
+ return "", true
+ }
+ }
+ return rangeReq, false
+}
+
// name is '/'-separated, not filepath.Separator.
func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirect bool) {
const indexPage = "/index.html"
@@ -243,9 +355,6 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
// use contents of index.html for directory, if present
if d.IsDir() {
- if checkLastModified(w, r, d.ModTime()) {
- return
- }
index := name + indexPage
ff, err := fs.Open(index)
if err == nil {
@@ -259,11 +368,16 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
}
}
+ // Still a directory? (we didn't find an index.html file)
if d.IsDir() {
+ if checkLastModified(w, r, d.ModTime()) {
+ return
+ }
dirList(w, f)
return
}
+ // serverContent will check modification time
serveContent(w, r, d.Name(), d.ModTime(), d.Size(), f)
}
@@ -312,6 +426,17 @@ type httpRange struct {
start, length int64
}
+func (r httpRange) contentRange(size int64) string {
+ return fmt.Sprintf("bytes %d-%d/%d", r.start, r.start+r.length-1, size)
+}
+
+func (r httpRange) mimeHeader(contentType string, size int64) textproto.MIMEHeader {
+ return textproto.MIMEHeader{
+ "Content-Range": {r.contentRange(size)},
+ "Content-Type": {contentType},
+ }
+}
+
// parseRange parses a Range header string as per RFC 2616.
func parseRange(s string, size int64) ([]httpRange, error) {
if s == "" {
@@ -323,11 +448,15 @@ func parseRange(s string, size int64) ([]httpRange, error) {
}
var ranges []httpRange
for _, ra := range strings.Split(s[len(b):], ",") {
+ ra = strings.TrimSpace(ra)
+ if ra == "" {
+ continue
+ }
i := strings.Index(ra, "-")
if i < 0 {
return nil, errors.New("invalid range")
}
- start, end := ra[:i], ra[i+1:]
+ start, end := strings.TrimSpace(ra[:i]), strings.TrimSpace(ra[i+1:])
var r httpRange
if start == "" {
// If no start is specified, end specifies the
@@ -365,3 +494,32 @@ func parseRange(s string, size int64) ([]httpRange, error) {
}
return ranges, nil
}
+
+// countingWriter counts how many bytes have been written to it.
+type countingWriter int64
+
+func (w *countingWriter) Write(p []byte) (n int, err error) {
+ *w += countingWriter(len(p))
+ return len(p), nil
+}
+
+// rangesMIMESize returns the nunber of bytes it takes to encode the
+// provided ranges as a multipart response.
+func rangesMIMESize(ranges []httpRange, contentType string, contentSize int64) (encSize int64) {
+ var w countingWriter
+ mw := multipart.NewWriter(&w)
+ for _, ra := range ranges {
+ mw.CreatePart(ra.mimeHeader(contentType, contentSize))
+ encSize += ra.length
+ }
+ mw.Close()
+ encSize += int64(w)
+ return
+}
+
+func sumRangesSize(ranges []httpRange) (size int64) {
+ for _, ra := range ranges {
+ size += ra.length
+ }
+ return
+}
diff --git a/src/pkg/net/http/fs_test.go b/src/pkg/net/http/fs_test.go
index 5aa93ce58..0dd6d0df9 100644
--- a/src/pkg/net/http/fs_test.go
+++ b/src/pkg/net/http/fs_test.go
@@ -10,12 +10,15 @@ import (
"fmt"
"io"
"io/ioutil"
+ "mime"
+ "mime/multipart"
"net"
. "net/http"
"net/http/httptest"
"net/url"
"os"
"os/exec"
+ "path"
"path/filepath"
"regexp"
"runtime"
@@ -25,24 +28,33 @@ import (
)
const (
- testFile = "testdata/file"
- testFileLength = 11
+ testFile = "testdata/file"
+ testFileLen = 11
)
+type wantRange struct {
+ start, end int64 // range [start,end)
+}
+
var ServeFileRangeTests = []struct {
- start, end int
- r string
- code int
+ r string
+ code int
+ ranges []wantRange
}{
- {0, testFileLength, "", StatusOK},
- {0, 5, "0-4", StatusPartialContent},
- {2, testFileLength, "2-", StatusPartialContent},
- {testFileLength - 5, testFileLength, "-5", StatusPartialContent},
- {3, 8, "3-7", StatusPartialContent},
- {0, 0, "20-", StatusRequestedRangeNotSatisfiable},
+ {r: "", code: StatusOK},
+ {r: "bytes=0-4", code: StatusPartialContent, ranges: []wantRange{{0, 5}}},
+ {r: "bytes=2-", code: StatusPartialContent, ranges: []wantRange{{2, testFileLen}}},
+ {r: "bytes=-5", code: StatusPartialContent, ranges: []wantRange{{testFileLen - 5, testFileLen}}},
+ {r: "bytes=3-7", code: StatusPartialContent, ranges: []wantRange{{3, 8}}},
+ {r: "bytes=20-", code: StatusRequestedRangeNotSatisfiable},
+ {r: "bytes=0-0,-2", code: StatusPartialContent, ranges: []wantRange{{0, 1}, {testFileLen - 2, testFileLen}}},
+ {r: "bytes=0-1,5-8", code: StatusPartialContent, ranges: []wantRange{{0, 2}, {5, 9}}},
+ {r: "bytes=0-1,5-", code: StatusPartialContent, ranges: []wantRange{{0, 2}, {5, testFileLen}}},
+ {r: "bytes=0-,1-,2-,3-,4-", code: StatusOK}, // ignore wasteful range request
}
func TestServeFile(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
ServeFile(w, r, "testdata/file")
}))
@@ -65,33 +77,86 @@ func TestServeFile(t *testing.T) {
// straight GET
_, body := getBody(t, "straight get", req)
- if !equal(body, file) {
+ if !bytes.Equal(body, file) {
t.Fatalf("body mismatch: got %q, want %q", body, file)
}
// Range tests
- for i, rt := range ServeFileRangeTests {
- req.Header.Set("Range", "bytes="+rt.r)
- if rt.r == "" {
- req.Header["Range"] = nil
+Cases:
+ for _, rt := range ServeFileRangeTests {
+ if rt.r != "" {
+ req.Header.Set("Range", rt.r)
}
- r, body := getBody(t, fmt.Sprintf("test %d", i), req)
- if r.StatusCode != rt.code {
- t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, r.StatusCode, rt.code)
+ resp, body := getBody(t, fmt.Sprintf("range test %q", rt.r), req)
+ if resp.StatusCode != rt.code {
+ t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, resp.StatusCode, rt.code)
}
if rt.code == StatusRequestedRangeNotSatisfiable {
continue
}
- h := fmt.Sprintf("bytes %d-%d/%d", rt.start, rt.end-1, testFileLength)
- if rt.r == "" {
- h = ""
+ wantContentRange := ""
+ if len(rt.ranges) == 1 {
+ rng := rt.ranges[0]
+ wantContentRange = fmt.Sprintf("bytes %d-%d/%d", rng.start, rng.end-1, testFileLen)
+ }
+ cr := resp.Header.Get("Content-Range")
+ if cr != wantContentRange {
+ t.Errorf("range=%q: Content-Range = %q, want %q", rt.r, cr, wantContentRange)
}
- cr := r.Header.Get("Content-Range")
- if cr != h {
- t.Errorf("header mismatch: range=%q: got %q, want %q", rt.r, cr, h)
+ ct := resp.Header.Get("Content-Type")
+ if len(rt.ranges) == 1 {
+ rng := rt.ranges[0]
+ wantBody := file[rng.start:rng.end]
+ if !bytes.Equal(body, wantBody) {
+ t.Errorf("range=%q: body = %q, want %q", rt.r, body, wantBody)
+ }
+ if strings.HasPrefix(ct, "multipart/byteranges") {
+ t.Errorf("range=%q content-type = %q; unexpected multipart/byteranges", rt.r, ct)
+ }
}
- if !equal(body, file[rt.start:rt.end]) {
- t.Errorf("body mismatch: range=%q: got %q, want %q", rt.r, body, file[rt.start:rt.end])
+ if len(rt.ranges) > 1 {
+ typ, params, err := mime.ParseMediaType(ct)
+ if err != nil {
+ t.Errorf("range=%q content-type = %q; %v", rt.r, ct, err)
+ continue
+ }
+ if typ != "multipart/byteranges" {
+ t.Errorf("range=%q content-type = %q; want multipart/byteranges", rt.r, typ)
+ continue
+ }
+ if params["boundary"] == "" {
+ t.Errorf("range=%q content-type = %q; lacks boundary", rt.r, ct)
+ continue
+ }
+ if g, w := resp.ContentLength, int64(len(body)); g != w {
+ t.Errorf("range=%q Content-Length = %d; want %d", rt.r, g, w)
+ continue
+ }
+ mr := multipart.NewReader(bytes.NewReader(body), params["boundary"])
+ for ri, rng := range rt.ranges {
+ part, err := mr.NextPart()
+ if err != nil {
+ t.Errorf("range=%q, reading part index %d: %v", rt.r, ri, err)
+ continue Cases
+ }
+ wantContentRange = fmt.Sprintf("bytes %d-%d/%d", rng.start, rng.end-1, testFileLen)
+ if g, w := part.Header.Get("Content-Range"), wantContentRange; g != w {
+ t.Errorf("range=%q: part Content-Range = %q; want %q", rt.r, g, w)
+ }
+ body, err := ioutil.ReadAll(part)
+ if err != nil {
+ t.Errorf("range=%q, reading part index %d body: %v", rt.r, ri, err)
+ continue Cases
+ }
+ wantBody := file[rng.start:rng.end]
+ if !bytes.Equal(body, wantBody) {
+ t.Errorf("range=%q: body = %q, want %q", rt.r, body, wantBody)
+ }
+ }
+ _, err = mr.NextPart()
+ if err != io.EOF {
+ t.Errorf("range=%q; expected final error io.EOF; got %v", rt.r, err)
+ }
}
}
}
@@ -105,6 +170,7 @@ var fsRedirectTestData = []struct {
}
func TestFSRedirect(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(StripPrefix("/test", FileServer(Dir("."))))
defer ts.Close()
@@ -129,6 +195,7 @@ func (fs *testFileSystem) Open(name string) (File, error) {
}
func TestFileServerCleans(t *testing.T) {
+ defer checkLeakedTransports(t)
ch := make(chan string, 1)
fs := FileServer(&testFileSystem{func(name string) (File, error) {
ch <- name
@@ -160,6 +227,7 @@ func mustRemoveAll(dir string) {
}
func TestFileServerImplicitLeadingSlash(t *testing.T) {
+ defer checkLeakedTransports(t)
tempDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("TempDir: %v", err)
@@ -193,8 +261,7 @@ func TestFileServerImplicitLeadingSlash(t *testing.T) {
func TestDirJoin(t *testing.T) {
wfi, err := os.Stat("/etc/hosts")
if err != nil {
- t.Logf("skipping test; no /etc/hosts file")
- return
+ t.Skip("skipping test; no /etc/hosts file")
}
test := func(d Dir, name string) {
f, err := d.Open(name)
@@ -239,6 +306,7 @@ func TestEmptyDirOpenCWD(t *testing.T) {
}
func TestServeFileContentType(t *testing.T) {
+ defer checkLeakedTransports(t)
const ctype = "icecream/chocolate"
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
if r.FormValue("override") == "1" {
@@ -255,12 +323,14 @@ func TestServeFileContentType(t *testing.T) {
if h := resp.Header.Get("Content-Type"); h != want {
t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
}
+ resp.Body.Close()
}
get("0", "text/plain; charset=utf-8")
get("1", ctype)
}
func TestServeFileMimeType(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
ServeFile(w, r, "testdata/style.css")
}))
@@ -269,6 +339,7 @@ func TestServeFileMimeType(t *testing.T) {
if err != nil {
t.Fatal(err)
}
+ resp.Body.Close()
want := "text/css; charset=utf-8"
if h := resp.Header.Get("Content-Type"); h != want {
t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
@@ -276,6 +347,7 @@ func TestServeFileMimeType(t *testing.T) {
}
func TestServeFileFromCWD(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
ServeFile(w, r, "fs_test.go")
}))
@@ -284,12 +356,14 @@ func TestServeFileFromCWD(t *testing.T) {
if err != nil {
t.Fatal(err)
}
+ r.Body.Close()
if r.StatusCode != 200 {
t.Fatalf("expected 200 OK, got %s", r.Status)
}
}
func TestServeFileWithContentEncoding(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("Content-Encoding", "foo")
ServeFile(w, r, "testdata/file")
@@ -299,12 +373,14 @@ func TestServeFileWithContentEncoding(t *testing.T) {
if err != nil {
t.Fatal(err)
}
+ resp.Body.Close()
if g, e := resp.ContentLength, int64(-1); g != e {
t.Errorf("Content-Length mismatch: got %d, want %d", g, e)
}
}
func TestServeIndexHtml(t *testing.T) {
+ defer checkLeakedTransports(t)
const want = "index.html says hello\n"
ts := httptest.NewServer(FileServer(Dir(".")))
defer ts.Close()
@@ -325,64 +401,289 @@ func TestServeIndexHtml(t *testing.T) {
}
}
-func TestServeContent(t *testing.T) {
- type req struct {
- name string
- modtime time.Time
- content io.ReadSeeker
- }
- ch := make(chan req, 1)
- ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
- p := <-ch
- ServeContent(w, r, p.name, p.modtime, p.content)
- }))
+func TestFileServerZeroByte(t *testing.T) {
+ defer checkLeakedTransports(t)
+ ts := httptest.NewServer(FileServer(Dir(".")))
defer ts.Close()
- css, err := os.Open("testdata/style.css")
+ res, err := Get(ts.URL + "/..\x00")
if err != nil {
t.Fatal(err)
}
- defer css.Close()
+ b, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal("reading Body:", err)
+ }
+ if res.StatusCode == 200 {
+ t.Errorf("got status 200; want an error. Body is:\n%s", string(b))
+ }
+}
+
+type fakeFileInfo struct {
+ dir bool
+ basename string
+ modtime time.Time
+ ents []*fakeFileInfo
+ contents string
+}
+
+func (f *fakeFileInfo) Name() string { return f.basename }
+func (f *fakeFileInfo) Sys() interface{} { return nil }
+func (f *fakeFileInfo) ModTime() time.Time { return f.modtime }
+func (f *fakeFileInfo) IsDir() bool { return f.dir }
+func (f *fakeFileInfo) Size() int64 { return int64(len(f.contents)) }
+func (f *fakeFileInfo) Mode() os.FileMode {
+ if f.dir {
+ return 0755 | os.ModeDir
+ }
+ return 0644
+}
+
+type fakeFile struct {
+ io.ReadSeeker
+ fi *fakeFileInfo
+ path string // as opened
+}
+
+func (f *fakeFile) Close() error { return nil }
+func (f *fakeFile) Stat() (os.FileInfo, error) { return f.fi, nil }
+func (f *fakeFile) Readdir(count int) ([]os.FileInfo, error) {
+ if !f.fi.dir {
+ return nil, os.ErrInvalid
+ }
+ var fis []os.FileInfo
+ for _, fi := range f.fi.ents {
+ fis = append(fis, fi)
+ }
+ return fis, nil
+}
+
+type fakeFS map[string]*fakeFileInfo
+
+func (fs fakeFS) Open(name string) (File, error) {
+ name = path.Clean(name)
+ f, ok := fs[name]
+ if !ok {
+ println("fake filesystem didn't find file", name)
+ return nil, os.ErrNotExist
+ }
+ return &fakeFile{ReadSeeker: strings.NewReader(f.contents), fi: f, path: name}, nil
+}
+
+func TestDirectoryIfNotModified(t *testing.T) {
+ defer checkLeakedTransports(t)
+ const indexContents = "I am a fake index.html file"
+ fileMod := time.Unix(1000000000, 0).UTC()
+ fileModStr := fileMod.Format(TimeFormat)
+ dirMod := time.Unix(123, 0).UTC()
+ indexFile := &fakeFileInfo{
+ basename: "index.html",
+ modtime: fileMod,
+ contents: indexContents,
+ }
+ fs := fakeFS{
+ "/": &fakeFileInfo{
+ dir: true,
+ modtime: dirMod,
+ ents: []*fakeFileInfo{indexFile},
+ },
+ "/index.html": indexFile,
+ }
+
+ ts := httptest.NewServer(FileServer(fs))
+ defer ts.Close()
- ch <- req{"style.css", time.Time{}, css}
res, err := Get(ts.URL)
if err != nil {
t.Fatal(err)
}
- if g, e := res.Header.Get("Content-Type"), "text/css; charset=utf-8"; g != e {
- t.Errorf("style.css: content type = %q, want %q", g, e)
+ b, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal(err)
}
- if g := res.Header.Get("Last-Modified"); g != "" {
- t.Errorf("want empty Last-Modified; got %q", g)
+ if string(b) != indexContents {
+ t.Fatalf("Got body %q; want %q", b, indexContents)
}
+ res.Body.Close()
+
+ lastMod := res.Header.Get("Last-Modified")
+ if lastMod != fileModStr {
+ t.Fatalf("initial Last-Modified = %q; want %q", lastMod, fileModStr)
+ }
+
+ req, _ := NewRequest("GET", ts.URL, nil)
+ req.Header.Set("If-Modified-Since", lastMod)
- fi, err := css.Stat()
+ res, err = DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
- ch <- req{"style.html", fi.ModTime(), css}
- res, err = Get(ts.URL)
+ if res.StatusCode != 304 {
+ t.Fatalf("Code after If-Modified-Since request = %v; want 304", res.StatusCode)
+ }
+ res.Body.Close()
+
+ // Advance the index.html file's modtime, but not the directory's.
+ indexFile.modtime = indexFile.modtime.Add(1 * time.Hour)
+
+ res, err = DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
- if g, e := res.Header.Get("Content-Type"), "text/html; charset=utf-8"; g != e {
- t.Errorf("style.html: content type = %q, want %q", g, e)
+ if res.StatusCode != 200 {
+ t.Fatalf("Code after second If-Modified-Since request = %v; want 200; res is %#v", res.StatusCode, res)
}
- if g := res.Header.Get("Last-Modified"); g == "" {
- t.Errorf("want non-empty last-modified")
+ res.Body.Close()
+}
+
+func mustStat(t *testing.T, fileName string) os.FileInfo {
+ fi, err := os.Stat(fileName)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return fi
+}
+
+func TestServeContent(t *testing.T) {
+ defer checkLeakedTransports(t)
+ type serveParam struct {
+ name string
+ modtime time.Time
+ content io.ReadSeeker
+ contentType string
+ etag string
+ }
+ servec := make(chan serveParam, 1)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ p := <-servec
+ if p.etag != "" {
+ w.Header().Set("ETag", p.etag)
+ }
+ if p.contentType != "" {
+ w.Header().Set("Content-Type", p.contentType)
+ }
+ ServeContent(w, r, p.name, p.modtime, p.content)
+ }))
+ defer ts.Close()
+
+ type testCase struct {
+ file string
+ modtime time.Time
+ serveETag string // optional
+ serveContentType string // optional
+ reqHeader map[string]string
+ wantLastMod string
+ wantContentType string
+ wantStatus int
+ }
+ htmlModTime := mustStat(t, "testdata/index.html").ModTime()
+ tests := map[string]testCase{
+ "no_last_modified": {
+ file: "testdata/style.css",
+ wantContentType: "text/css; charset=utf-8",
+ wantStatus: 200,
+ },
+ "with_last_modified": {
+ file: "testdata/index.html",
+ wantContentType: "text/html; charset=utf-8",
+ modtime: htmlModTime,
+ wantLastMod: htmlModTime.UTC().Format(TimeFormat),
+ wantStatus: 200,
+ },
+ "not_modified_modtime": {
+ file: "testdata/style.css",
+ modtime: htmlModTime,
+ reqHeader: map[string]string{
+ "If-Modified-Since": htmlModTime.UTC().Format(TimeFormat),
+ },
+ wantStatus: 304,
+ },
+ "not_modified_modtime_with_contenttype": {
+ file: "testdata/style.css",
+ serveContentType: "text/css", // explicit content type
+ modtime: htmlModTime,
+ reqHeader: map[string]string{
+ "If-Modified-Since": htmlModTime.UTC().Format(TimeFormat),
+ },
+ wantStatus: 304,
+ },
+ "not_modified_etag": {
+ file: "testdata/style.css",
+ serveETag: `"foo"`,
+ reqHeader: map[string]string{
+ "If-None-Match": `"foo"`,
+ },
+ wantStatus: 304,
+ },
+ "range_good": {
+ file: "testdata/style.css",
+ serveETag: `"A"`,
+ reqHeader: map[string]string{
+ "Range": "bytes=0-4",
+ },
+ wantStatus: StatusPartialContent,
+ wantContentType: "text/css; charset=utf-8",
+ },
+ // An If-Range resource for entity "A", but entity "B" is now current.
+ // The Range request should be ignored.
+ "range_no_match": {
+ file: "testdata/style.css",
+ serveETag: `"A"`,
+ reqHeader: map[string]string{
+ "Range": "bytes=0-4",
+ "If-Range": `"B"`,
+ },
+ wantStatus: 200,
+ wantContentType: "text/css; charset=utf-8",
+ },
+ }
+ for testName, tt := range tests {
+ f, err := os.Open(tt.file)
+ if err != nil {
+ t.Fatalf("test %q: %v", testName, err)
+ }
+ defer f.Close()
+
+ servec <- serveParam{
+ name: filepath.Base(tt.file),
+ content: f,
+ modtime: tt.modtime,
+ etag: tt.serveETag,
+ contentType: tt.serveContentType,
+ }
+ req, err := NewRequest("GET", ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for k, v := range tt.reqHeader {
+ req.Header.Set(k, v)
+ }
+ res, err := DefaultClient.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ io.Copy(ioutil.Discard, res.Body)
+ res.Body.Close()
+ if res.StatusCode != tt.wantStatus {
+ t.Errorf("test %q: status = %d; want %d", testName, res.StatusCode, tt.wantStatus)
+ }
+ if g, e := res.Header.Get("Content-Type"), tt.wantContentType; g != e {
+ t.Errorf("test %q: content-type = %q, want %q", testName, g, e)
+ }
+ if g, e := res.Header.Get("Last-Modified"), tt.wantLastMod; g != e {
+ t.Errorf("test %q: last-modified = %q, want %q", testName, g, e)
+ }
}
}
// verifies that sendfile is being used on Linux
func TestLinuxSendfile(t *testing.T) {
+ defer checkLeakedTransports(t)
if runtime.GOOS != "linux" {
- t.Logf("skipping; linux-only test")
- return
+ t.Skip("skipping; linux-only test")
}
- _, err := exec.LookPath("strace")
- if err != nil {
- t.Logf("skipping; strace not found in path")
- return
+ if _, err := exec.LookPath("strace"); err != nil {
+ t.Skip("skipping; strace not found in path")
}
ln, err := net.Listen("tcp", "127.0.0.1:0")
@@ -401,10 +702,8 @@ func TestLinuxSendfile(t *testing.T) {
child.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...)
child.Stdout = &buf
child.Stderr = &buf
- err = child.Start()
- if err != nil {
- t.Logf("skipping; failed to start straced child: %v", err)
- return
+ if err := child.Start(); err != nil {
+ t.Skipf("skipping; failed to start straced child: %v", err)
}
res, err := Get(fmt.Sprintf("http://%s/", ln.Addr()))
@@ -464,15 +763,3 @@ func TestLinuxSendfileChild(*testing.T) {
panic(err)
}
}
-
-func equal(a, b []byte) bool {
- if len(a) != len(b) {
- return false
- }
- for i := range a {
- if a[i] != b[i] {
- return false
- }
- }
- return true
-}
diff --git a/src/pkg/net/http/header.go b/src/pkg/net/http/header.go
index b107c312d..f479b7b4e 100644
--- a/src/pkg/net/http/header.go
+++ b/src/pkg/net/http/header.go
@@ -5,11 +5,11 @@
package http
import (
- "fmt"
"io"
"net/textproto"
"sort"
"strings"
+ "time"
)
// A Header represents the key-value pairs in an HTTP header.
@@ -36,6 +36,14 @@ func (h Header) Get(key string) string {
return textproto.MIMEHeader(h).Get(key)
}
+// get is like Get, but key must already be in CanonicalHeaderKey form.
+func (h Header) get(key string) string {
+ if v := h[key]; len(v) > 0 {
+ return v[0]
+ }
+ return ""
+}
+
// Del deletes the values associated with key.
func (h Header) Del(key string) {
textproto.MIMEHeader(h).Del(key)
@@ -46,24 +54,87 @@ func (h Header) Write(w io.Writer) error {
return h.WriteSubset(w, nil)
}
+func (h Header) clone() Header {
+ h2 := make(Header, len(h))
+ for k, vv := range h {
+ vv2 := make([]string, len(vv))
+ copy(vv2, vv)
+ h2[k] = vv2
+ }
+ return h2
+}
+
+var timeFormats = []string{
+ TimeFormat,
+ time.RFC850,
+ time.ANSIC,
+}
+
+// ParseTime parses a time header (such as the Date: header),
+// trying each of the three formats allowed by HTTP/1.1:
+// TimeFormat, time.RFC850, and time.ANSIC.
+func ParseTime(text string) (t time.Time, err error) {
+ for _, layout := range timeFormats {
+ t, err = time.Parse(layout, text)
+ if err == nil {
+ return
+ }
+ }
+ return
+}
+
var headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ")
+type writeStringer interface {
+ WriteString(string) (int, error)
+}
+
+// stringWriter implements WriteString on a Writer.
+type stringWriter struct {
+ w io.Writer
+}
+
+func (w stringWriter) WriteString(s string) (n int, err error) {
+ return w.w.Write([]byte(s))
+}
+
+type keyValues struct {
+ key string
+ values []string
+}
+
+type byKey []keyValues
+
+func (s byKey) Len() int { return len(s) }
+func (s byKey) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s byKey) Less(i, j int) bool { return s[i].key < s[j].key }
+
+func (h Header) sortedKeyValues(exclude map[string]bool) []keyValues {
+ kvs := make([]keyValues, 0, len(h))
+ for k, vv := range h {
+ if !exclude[k] {
+ kvs = append(kvs, keyValues{k, vv})
+ }
+ }
+ sort.Sort(byKey(kvs))
+ return kvs
+}
+
// WriteSubset writes a header in wire format.
// If exclude is not nil, keys where exclude[key] == true are not written.
func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error {
- keys := make([]string, 0, len(h))
- for k := range h {
- if exclude == nil || !exclude[k] {
- keys = append(keys, k)
- }
+ ws, ok := w.(writeStringer)
+ if !ok {
+ ws = stringWriter{w}
}
- sort.Strings(keys)
- for _, k := range keys {
- for _, v := range h[k] {
+ for _, kv := range h.sortedKeyValues(exclude) {
+ for _, v := range kv.values {
v = headerNewlineToSpace.Replace(v)
- v = strings.TrimSpace(v)
- if _, err := fmt.Fprintf(w, "%s: %s\r\n", k, v); err != nil {
- return err
+ v = textproto.TrimString(v)
+ for _, s := range []string{kv.key, ": ", v, "\r\n"} {
+ if _, err := ws.WriteString(s); err != nil {
+ return err
+ }
}
}
}
@@ -76,3 +147,43 @@ func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error {
// the rest are converted to lowercase. For example, the
// canonical key for "accept-encoding" is "Accept-Encoding".
func CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) }
+
+// hasToken returns whether token appears with v, ASCII
+// case-insensitive, with space or comma boundaries.
+// token must be all lowercase.
+// v may contain mixed cased.
+func hasToken(v, token string) bool {
+ if len(token) > len(v) || token == "" {
+ return false
+ }
+ if v == token {
+ return true
+ }
+ for sp := 0; sp <= len(v)-len(token); sp++ {
+ // Check that first character is good.
+ // The token is ASCII, so checking only a single byte
+ // is sufficient. We skip this potential starting
+ // position if both the first byte and its potential
+ // ASCII uppercase equivalent (b|0x20) don't match.
+ // False positives ('^' => '~') are caught by EqualFold.
+ if b := v[sp]; b != token[0] && b|0x20 != token[0] {
+ continue
+ }
+ // Check that start pos is on a valid token boundary.
+ if sp > 0 && !isTokenBoundary(v[sp-1]) {
+ continue
+ }
+ // Check that end pos is on a valid token boundary.
+ if endPos := sp + len(token); endPos != len(v) && !isTokenBoundary(v[endPos]) {
+ continue
+ }
+ if strings.EqualFold(v[sp:sp+len(token)], token) {
+ return true
+ }
+ }
+ return false
+}
+
+func isTokenBoundary(b byte) bool {
+ return b == ' ' || b == ',' || b == '\t'
+}
diff --git a/src/pkg/net/http/header_test.go b/src/pkg/net/http/header_test.go
index ccdee8a97..2313b5549 100644
--- a/src/pkg/net/http/header_test.go
+++ b/src/pkg/net/http/header_test.go
@@ -7,6 +7,7 @@ package http
import (
"bytes"
"testing"
+ "time"
)
var headerWriteTests = []struct {
@@ -67,6 +68,24 @@ var headerWriteTests = []struct {
nil,
"Blank: \r\nDouble-Blank: \r\nDouble-Blank: \r\n",
},
+ // Tests header sorting when over the insertion sort threshold side:
+ {
+ Header{
+ "k1": {"1a", "1b"},
+ "k2": {"2a", "2b"},
+ "k3": {"3a", "3b"},
+ "k4": {"4a", "4b"},
+ "k5": {"5a", "5b"},
+ "k6": {"6a", "6b"},
+ "k7": {"7a", "7b"},
+ "k8": {"8a", "8b"},
+ "k9": {"9a", "9b"},
+ },
+ map[string]bool{"k5": true},
+ "k1: 1a\r\nk1: 1b\r\nk2: 2a\r\nk2: 2b\r\nk3: 3a\r\nk3: 3b\r\n" +
+ "k4: 4a\r\nk4: 4b\r\nk6: 6a\r\nk6: 6b\r\n" +
+ "k7: 7a\r\nk7: 7b\r\nk8: 8a\r\nk8: 8b\r\nk9: 9a\r\nk9: 9b\r\n",
+ },
}
func TestHeaderWrite(t *testing.T) {
@@ -79,3 +98,107 @@ func TestHeaderWrite(t *testing.T) {
buf.Reset()
}
}
+
+var parseTimeTests = []struct {
+ h Header
+ err bool
+}{
+ {Header{"Date": {""}}, true},
+ {Header{"Date": {"invalid"}}, true},
+ {Header{"Date": {"1994-11-06T08:49:37Z00:00"}}, true},
+ {Header{"Date": {"Sun, 06 Nov 1994 08:49:37 GMT"}}, false},
+ {Header{"Date": {"Sunday, 06-Nov-94 08:49:37 GMT"}}, false},
+ {Header{"Date": {"Sun Nov 6 08:49:37 1994"}}, false},
+}
+
+func TestParseTime(t *testing.T) {
+ expect := time.Date(1994, 11, 6, 8, 49, 37, 0, time.UTC)
+ for i, test := range parseTimeTests {
+ d, err := ParseTime(test.h.Get("Date"))
+ if err != nil {
+ if !test.err {
+ t.Errorf("#%d:\n got err: %v", i, err)
+ }
+ continue
+ }
+ if test.err {
+ t.Errorf("#%d:\n should err", i)
+ continue
+ }
+ if !expect.Equal(d) {
+ t.Errorf("#%d:\n got: %v\nwant: %v", i, d, expect)
+ }
+ }
+}
+
+type hasTokenTest struct {
+ header string
+ token string
+ want bool
+}
+
+var hasTokenTests = []hasTokenTest{
+ {"", "", false},
+ {"", "foo", false},
+ {"foo", "foo", true},
+ {"foo ", "foo", true},
+ {" foo", "foo", true},
+ {" foo ", "foo", true},
+ {"foo,bar", "foo", true},
+ {"bar,foo", "foo", true},
+ {"bar, foo", "foo", true},
+ {"bar,foo, baz", "foo", true},
+ {"bar, foo,baz", "foo", true},
+ {"bar,foo, baz", "foo", true},
+ {"bar, foo, baz", "foo", true},
+ {"FOO", "foo", true},
+ {"FOO ", "foo", true},
+ {" FOO", "foo", true},
+ {" FOO ", "foo", true},
+ {"FOO,BAR", "foo", true},
+ {"BAR,FOO", "foo", true},
+ {"BAR, FOO", "foo", true},
+ {"BAR,FOO, baz", "foo", true},
+ {"BAR, FOO,BAZ", "foo", true},
+ {"BAR,FOO, BAZ", "foo", true},
+ {"BAR, FOO, BAZ", "foo", true},
+ {"foobar", "foo", false},
+ {"barfoo ", "foo", false},
+}
+
+func TestHasToken(t *testing.T) {
+ for _, tt := range hasTokenTests {
+ if hasToken(tt.header, tt.token) != tt.want {
+ t.Errorf("hasToken(%q, %q) = %v; want %v", tt.header, tt.token, !tt.want, tt.want)
+ }
+ }
+}
+
+var testHeader = Header{
+ "Content-Length": {"123"},
+ "Content-Type": {"text/plain"},
+ "Date": {"some date at some time Z"},
+ "Server": {"Go http package"},
+}
+
+var buf bytes.Buffer
+
+func BenchmarkHeaderWriteSubset(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ buf.Reset()
+ testHeader.WriteSubset(&buf, nil)
+ }
+}
+
+func TestHeaderWriteSubsetMallocs(t *testing.T) {
+ n := testing.AllocsPerRun(100, func() {
+ buf.Reset()
+ testHeader.WriteSubset(&buf, nil)
+ })
+ if n > 1 {
+ // TODO(bradfitz,rsc): once we can sort without allocating,
+ // make this an error. See http://golang.org/issue/3761
+ // t.Errorf("got %v allocs, want <= %v", n, 1)
+ }
+}
diff --git a/src/pkg/net/http/httptest/example_test.go b/src/pkg/net/http/httptest/example_test.go
new file mode 100644
index 000000000..239470d97
--- /dev/null
+++ b/src/pkg/net/http/httptest/example_test.go
@@ -0,0 +1,50 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package httptest_test
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "net/http/httptest"
+)
+
+func ExampleRecorder() {
+ handler := func(w http.ResponseWriter, r *http.Request) {
+ http.Error(w, "something failed", http.StatusInternalServerError)
+ }
+
+ req, err := http.NewRequest("GET", "http://example.com/foo", nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ w := httptest.NewRecorder()
+ handler(w, req)
+
+ fmt.Printf("%d - %s", w.Code, w.Body.String())
+ // Output: 500 - something failed
+}
+
+func ExampleServer() {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintln(w, "Hello, client")
+ }))
+ defer ts.Close()
+
+ res, err := http.Get(ts.URL)
+ if err != nil {
+ log.Fatal(err)
+ }
+ greeting, err := ioutil.ReadAll(res.Body)
+ res.Body.Close()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ fmt.Printf("%s", greeting)
+ // Output: Hello, client
+}
diff --git a/src/pkg/net/http/httptest/recorder.go b/src/pkg/net/http/httptest/recorder.go
index 9aa0d510b..5451f5423 100644
--- a/src/pkg/net/http/httptest/recorder.go
+++ b/src/pkg/net/http/httptest/recorder.go
@@ -17,6 +17,8 @@ type ResponseRecorder struct {
HeaderMap http.Header // the HTTP response headers
Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
Flushed bool
+
+ wroteHeader bool
}
// NewRecorder returns an initialized ResponseRecorder.
@@ -24,6 +26,7 @@ func NewRecorder() *ResponseRecorder {
return &ResponseRecorder{
HeaderMap: make(http.Header),
Body: new(bytes.Buffer),
+ Code: 200,
}
}
@@ -33,26 +36,37 @@ const DefaultRemoteAddr = "1.2.3.4"
// Header returns the response headers.
func (rw *ResponseRecorder) Header() http.Header {
- return rw.HeaderMap
+ m := rw.HeaderMap
+ if m == nil {
+ m = make(http.Header)
+ rw.HeaderMap = m
+ }
+ return m
}
// Write always succeeds and writes to rw.Body, if not nil.
func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
+ if !rw.wroteHeader {
+ rw.WriteHeader(200)
+ }
if rw.Body != nil {
rw.Body.Write(buf)
}
- if rw.Code == 0 {
- rw.Code = http.StatusOK
- }
return len(buf), nil
}
// WriteHeader sets rw.Code.
func (rw *ResponseRecorder) WriteHeader(code int) {
- rw.Code = code
+ if !rw.wroteHeader {
+ rw.Code = code
+ }
+ rw.wroteHeader = true
}
// Flush sets rw.Flushed to true.
func (rw *ResponseRecorder) Flush() {
+ if !rw.wroteHeader {
+ rw.WriteHeader(200)
+ }
rw.Flushed = true
}
diff --git a/src/pkg/net/http/httptest/recorder_test.go b/src/pkg/net/http/httptest/recorder_test.go
new file mode 100644
index 000000000..2b563260c
--- /dev/null
+++ b/src/pkg/net/http/httptest/recorder_test.go
@@ -0,0 +1,90 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package httptest
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+)
+
+func TestRecorder(t *testing.T) {
+ type checkFunc func(*ResponseRecorder) error
+ check := func(fns ...checkFunc) []checkFunc { return fns }
+
+ hasStatus := func(wantCode int) checkFunc {
+ return func(rec *ResponseRecorder) error {
+ if rec.Code != wantCode {
+ return fmt.Errorf("Status = %d; want %d", rec.Code, wantCode)
+ }
+ return nil
+ }
+ }
+ hasContents := func(want string) checkFunc {
+ return func(rec *ResponseRecorder) error {
+ if rec.Body.String() != want {
+ return fmt.Errorf("wrote = %q; want %q", rec.Body.String(), want)
+ }
+ return nil
+ }
+ }
+ hasFlush := func(want bool) checkFunc {
+ return func(rec *ResponseRecorder) error {
+ if rec.Flushed != want {
+ return fmt.Errorf("Flushed = %v; want %v", rec.Flushed, want)
+ }
+ return nil
+ }
+ }
+
+ tests := []struct {
+ name string
+ h func(w http.ResponseWriter, r *http.Request)
+ checks []checkFunc
+ }{
+ {
+ "200 default",
+ func(w http.ResponseWriter, r *http.Request) {},
+ check(hasStatus(200), hasContents("")),
+ },
+ {
+ "first code only",
+ func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(201)
+ w.WriteHeader(202)
+ w.Write([]byte("hi"))
+ },
+ check(hasStatus(201), hasContents("hi")),
+ },
+ {
+ "write sends 200",
+ func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("hi first"))
+ w.WriteHeader(201)
+ w.WriteHeader(202)
+ },
+ check(hasStatus(200), hasContents("hi first"), hasFlush(false)),
+ },
+ {
+ "flush",
+ func(w http.ResponseWriter, r *http.Request) {
+ w.(http.Flusher).Flush() // also sends a 200
+ w.WriteHeader(201)
+ },
+ check(hasStatus(200), hasFlush(true)),
+ },
+ }
+ r, _ := http.NewRequest("GET", "http://foo.com/", nil)
+ for _, tt := range tests {
+ h := http.HandlerFunc(tt.h)
+ rec := NewRecorder()
+ h.ServeHTTP(rec, r)
+ for _, check := range tt.checks {
+ if err := check(rec); err != nil {
+ t.Errorf("%s: %v", tt.name, err)
+ }
+ }
+ }
+}
diff --git a/src/pkg/net/http/httptest/server.go b/src/pkg/net/http/httptest/server.go
index 57cf0c941..7f265552f 100644
--- a/src/pkg/net/http/httptest/server.go
+++ b/src/pkg/net/http/httptest/server.go
@@ -21,7 +21,11 @@ import (
type Server struct {
URL string // base URL of form http://ipaddr:port with no trailing slash
Listener net.Listener
- TLS *tls.Config // nil if not using using TLS
+
+ // TLS is the optional TLS configuration, populated with a new config
+ // after TLS is started. If set on an unstarted server before StartTLS
+ // is called, existing fields are copied into the new config.
+ TLS *tls.Config
// Config may be changed after calling NewUnstartedServer and
// before Start or StartTLS.
@@ -36,13 +40,16 @@ type Server struct {
// accepted.
type historyListener struct {
net.Listener
- history []net.Conn
+ sync.Mutex // protects history
+ history []net.Conn
}
func (hs *historyListener) Accept() (c net.Conn, err error) {
c, err = hs.Listener.Accept()
if err == nil {
+ hs.Lock()
hs.history = append(hs.history, c)
+ hs.Unlock()
}
return
}
@@ -96,7 +103,7 @@ func (s *Server) Start() {
if s.URL != "" {
panic("Server already started")
}
- s.Listener = &historyListener{s.Listener, make([]net.Conn, 0)}
+ s.Listener = &historyListener{Listener: s.Listener}
s.URL = "http://" + s.Listener.Addr().String()
s.wrapHandler()
go s.Config.Serve(s.Listener)
@@ -116,13 +123,20 @@ func (s *Server) StartTLS() {
panic(fmt.Sprintf("httptest: NewTLSServer: %v", err))
}
- s.TLS = &tls.Config{
- NextProtos: []string{"http/1.1"},
- Certificates: []tls.Certificate{cert},
+ existingConfig := s.TLS
+ s.TLS = new(tls.Config)
+ if existingConfig != nil {
+ *s.TLS = *existingConfig
+ }
+ if s.TLS.NextProtos == nil {
+ s.TLS.NextProtos = []string{"http/1.1"}
+ }
+ if len(s.TLS.Certificates) == 0 {
+ s.TLS.Certificates = []tls.Certificate{cert}
}
tlsListener := tls.NewListener(s.Listener, s.TLS)
- s.Listener = &historyListener{tlsListener, make([]net.Conn, 0)}
+ s.Listener = &historyListener{Listener: tlsListener}
s.URL = "https://" + s.Listener.Addr().String()
s.wrapHandler()
go s.Config.Serve(s.Listener)
@@ -152,6 +166,10 @@ func NewTLSServer(handler http.Handler) *Server {
func (s *Server) Close() {
s.Listener.Close()
s.wg.Wait()
+ s.CloseClientConnections()
+ if t, ok := http.DefaultTransport.(*http.Transport); ok {
+ t.CloseIdleConnections()
+ }
}
// CloseClientConnections closes any currently open HTTP connections
@@ -161,9 +179,11 @@ func (s *Server) CloseClientConnections() {
if !ok {
return
}
+ hl.Lock()
for _, conn := range hl.history {
conn.Close()
}
+ hl.Unlock()
}
// waitGroupHandler wraps a handler, incrementing and decrementing a
@@ -180,28 +200,29 @@ func (h *waitGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.h.ServeHTTP(w, r)
}
-// localhostCert is a PEM-encoded TLS cert with SAN DNS names
+// localhostCert is a PEM-encoded TLS cert with SAN IPs
// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
// of ASN.1 time).
+// generated from src/pkg/crypto/tls:
+// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
-MIIBOTCB5qADAgECAgEAMAsGCSqGSIb3DQEBBTAAMB4XDTcwMDEwMTAwMDAwMFoX
-DTQ5MTIzMTIzNTk1OVowADBaMAsGCSqGSIb3DQEBAQNLADBIAkEAsuA5mAFMj6Q7
-qoBzcvKzIq4kzuT5epSp2AkcQfyBHm7K13Ws7u+0b5Vb9gqTf5cAiIKcrtrXVqkL
-8i1UQF6AzwIDAQABo08wTTAOBgNVHQ8BAf8EBAMCACQwDQYDVR0OBAYEBAECAwQw
-DwYDVR0jBAgwBoAEAQIDBDAbBgNVHREEFDASggkxMjcuMC4wLjGCBVs6OjFdMAsG
-CSqGSIb3DQEBBQNBAJH30zjLWRztrWpOCgJL8RQWLaKzhK79pVhAx6q/3NrF16C7
-+l1BRZstTwIGdoGId8BRpErK1TXkniFb95ZMynM=
------END CERTIFICATE-----
-`)
+MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
+bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
+bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa
+IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA
+AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
+EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
+AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk
+Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA==
+-----END CERTIFICATE-----`)
// localhostKey is the private key for localhostCert.
var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
-MIIBPQIBAAJBALLgOZgBTI+kO6qAc3LysyKuJM7k+XqUqdgJHEH8gR5uytd1rO7v
-tG+VW/YKk3+XAIiCnK7a11apC/ItVEBegM8CAwEAAQJBAI5sxq7naeR9ahyqRkJi
-SIv2iMxLuPEHaezf5CYOPWjSjBPyVhyRevkhtqEjF/WkgL7C2nWpYHsUcBDBQVF0
-3KECIQDtEGB2ulnkZAahl3WuJziXGLB+p8Wgx7wzSM6bHu1c6QIhAMEp++CaS+SJ
-/TrU0zwY/fW4SvQeb49BPZUF3oqR8Xz3AiEA1rAJHBzBgdOQKdE3ksMUPcnvNJSN
-poCcELmz2clVXtkCIQCLytuLV38XHToTipR4yMl6O+6arzAjZ56uq7m7ZRV0TwIh
-AM65XAOw8Dsg9Kq78aYXiOEDc5DL0sbFUu/SlmRcCg93
------END RSA PRIVATE KEY-----
-`)
+MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0
+0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV
+NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d
+AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW
+MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD
+EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA
+1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE=
+-----END RSA PRIVATE KEY-----`)
diff --git a/src/pkg/net/http/httputil/chunked.go b/src/pkg/net/http/httputil/chunked.go
index 29eaf3475..b66d40951 100644
--- a/src/pkg/net/http/httputil/chunked.go
+++ b/src/pkg/net/http/httputil/chunked.go
@@ -13,10 +13,9 @@ package httputil
import (
"bufio"
- "bytes"
"errors"
+ "fmt"
"io"
- "strconv"
)
const maxLineLength = 4096 // assumed <= bufio.defaultBufSize
@@ -24,7 +23,7 @@ const maxLineLength = 4096 // assumed <= bufio.defaultBufSize
var ErrLineTooLong = errors.New("header line too long")
// NewChunkedReader returns a new chunkedReader that translates the data read from r
-// out of HTTP "chunked" format before returning it.
+// out of HTTP "chunked" format before returning it.
// The chunkedReader returns io.EOF when the final 0-length chunk is read.
//
// NewChunkedReader is not needed by normal applications. The http package
@@ -41,16 +40,17 @@ type chunkedReader struct {
r *bufio.Reader
n uint64 // unread bytes in chunk
err error
+ buf [2]byte
}
func (cr *chunkedReader) beginChunk() {
// chunk-size CRLF
- var line string
+ var line []byte
line, cr.err = readLine(cr.r)
if cr.err != nil {
return
}
- cr.n, cr.err = strconv.ParseUint(line, 16, 64)
+ cr.n, cr.err = parseHexUint(line)
if cr.err != nil {
return
}
@@ -76,9 +76,8 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
cr.n -= uint64(n)
if cr.n == 0 && cr.err == nil {
// end of chunk (CRLF)
- b := make([]byte, 2)
- if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil {
- if b[0] != '\r' || b[1] != '\n' {
+ if _, cr.err = io.ReadFull(cr.r, cr.buf[:]); cr.err == nil {
+ if cr.buf[0] != '\r' || cr.buf[1] != '\n' {
cr.err = errors.New("malformed chunked encoding")
}
}
@@ -90,7 +89,7 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
// Give up if the line exceeds maxLineLength.
// The returned bytes are a pointer into storage in
// the bufio, so they are only valid until the next bufio read.
-func readLineBytes(b *bufio.Reader) (p []byte, err error) {
+func readLine(b *bufio.Reader) (p []byte, err error) {
if p, err = b.ReadSlice('\n'); err != nil {
// We always know when EOF is coming.
// If the caller asked for a line, there should be a line.
@@ -104,20 +103,18 @@ func readLineBytes(b *bufio.Reader) (p []byte, err error) {
if len(p) >= maxLineLength {
return nil, ErrLineTooLong
}
-
- // Chop off trailing white space.
- p = bytes.TrimRight(p, " \r\t\n")
-
- return p, nil
+ return trimTrailingWhitespace(p), nil
}
-// readLineBytes, but convert the bytes into a string.
-func readLine(b *bufio.Reader) (s string, err error) {
- p, e := readLineBytes(b)
- if e != nil {
- return "", e
+func trimTrailingWhitespace(b []byte) []byte {
+ for len(b) > 0 && isASCIISpace(b[len(b)-1]) {
+ b = b[:len(b)-1]
}
- return string(p), nil
+ return b
+}
+
+func isASCIISpace(b byte) bool {
+ return b == ' ' || b == '\t' || b == '\n' || b == '\r'
}
// NewChunkedWriter returns a new chunkedWriter that translates writes into HTTP
@@ -149,9 +146,7 @@ func (cw *chunkedWriter) Write(data []byte) (n int, err error) {
return 0, nil
}
- head := strconv.FormatInt(int64(len(data)), 16) + "\r\n"
-
- if _, err = io.WriteString(cw.Wire, head); err != nil {
+ if _, err = fmt.Fprintf(cw.Wire, "%x\r\n", len(data)); err != nil {
return 0, err
}
if n, err = cw.Wire.Write(data); err != nil {
@@ -170,3 +165,21 @@ func (cw *chunkedWriter) Close() error {
_, err := io.WriteString(cw.Wire, "0\r\n")
return err
}
+
+func parseHexUint(v []byte) (n uint64, err error) {
+ for _, b := range v {
+ n <<= 4
+ switch {
+ case '0' <= b && b <= '9':
+ b = b - '0'
+ case 'a' <= b && b <= 'f':
+ b = b - 'a' + 10
+ case 'A' <= b && b <= 'F':
+ b = b - 'A' + 10
+ default:
+ return 0, errors.New("invalid byte in chunk length")
+ }
+ n |= uint64(b)
+ }
+ return
+}
diff --git a/src/pkg/net/http/httputil/chunked_test.go b/src/pkg/net/http/httputil/chunked_test.go
index 155a32bdf..a06bffad5 100644
--- a/src/pkg/net/http/httputil/chunked_test.go
+++ b/src/pkg/net/http/httputil/chunked_test.go
@@ -11,7 +11,10 @@ package httputil
import (
"bytes"
+ "fmt"
+ "io"
"io/ioutil"
+ "runtime"
"testing"
)
@@ -39,3 +42,54 @@ func TestChunk(t *testing.T) {
t.Errorf("chunk reader read %q; want %q", g, e)
}
}
+
+func TestChunkReaderAllocs(t *testing.T) {
+ // temporarily set GOMAXPROCS to 1 as we are testing memory allocations
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
+ var buf bytes.Buffer
+ w := NewChunkedWriter(&buf)
+ a, b, c := []byte("aaaaaa"), []byte("bbbbbbbbbbbb"), []byte("cccccccccccccccccccccccc")
+ w.Write(a)
+ w.Write(b)
+ w.Write(c)
+ w.Close()
+
+ r := NewChunkedReader(&buf)
+ readBuf := make([]byte, len(a)+len(b)+len(c)+1)
+
+ var ms runtime.MemStats
+ runtime.ReadMemStats(&ms)
+ m0 := ms.Mallocs
+
+ n, err := io.ReadFull(r, readBuf)
+
+ runtime.ReadMemStats(&ms)
+ mallocs := ms.Mallocs - m0
+ if mallocs > 1 {
+ t.Errorf("%d mallocs; want <= 1", mallocs)
+ }
+
+ if n != len(readBuf)-1 {
+ t.Errorf("read %d bytes; want %d", n, len(readBuf)-1)
+ }
+ if err != io.ErrUnexpectedEOF {
+ t.Errorf("read error = %v; want ErrUnexpectedEOF", err)
+ }
+}
+
+func TestParseHexUint(t *testing.T) {
+ for i := uint64(0); i <= 1234; i++ {
+ line := []byte(fmt.Sprintf("%x", i))
+ got, err := parseHexUint(line)
+ if err != nil {
+ t.Fatalf("on %d: %v", i, err)
+ }
+ if got != i {
+ t.Errorf("for input %q = %d; want %d", line, got, i)
+ }
+ }
+ _, err := parseHexUint([]byte("bogus"))
+ if err == nil {
+ t.Error("expected error on bogus input")
+ }
+}
diff --git a/src/pkg/net/http/httputil/dump.go b/src/pkg/net/http/httputil/dump.go
index 892ef4ede..0b0035661 100644
--- a/src/pkg/net/http/httputil/dump.go
+++ b/src/pkg/net/http/httputil/dump.go
@@ -75,7 +75,7 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
// Use the actual Transport code to record what we would send
// on the wire, but not using TCP. Use a Transport with a
- // customer dialer that returns a fake net.Conn that waits
+ // custom dialer that returns a fake net.Conn that waits
// for the full input (and recording it), and then responds
// with a dummy response.
var buf bytes.Buffer // records the output
@@ -89,7 +89,7 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
t := &http.Transport{
Dial: func(net, addr string) (net.Conn, error) {
- return &dumpConn{io.MultiWriter(pw, &buf), dr}, nil
+ return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil
},
}
diff --git a/src/pkg/net/http/httputil/reverseproxy.go b/src/pkg/net/http/httputil/reverseproxy.go
index 9c4bd6e09..134c45299 100644
--- a/src/pkg/net/http/httputil/reverseproxy.go
+++ b/src/pkg/net/http/httputil/reverseproxy.go
@@ -17,6 +17,10 @@ import (
"time"
)
+// onExitFlushLoop is a callback set by tests to detect the state of the
+// flushLoop() goroutine.
+var onExitFlushLoop func()
+
// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
@@ -102,8 +106,14 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
outreq.Header.Del("Connection")
}
- if clientIp, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
- outreq.Header.Set("X-Forwarded-For", clientIp)
+ if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
+ // If we aren't the first proxy retain prior
+ // X-Forwarded-For information as a comma+space
+ // separated list and fold multiple headers into one.
+ if prior, ok := outreq.Header["X-Forwarded-For"]; ok {
+ clientIP = strings.Join(prior, ", ") + ", " + clientIP
+ }
+ outreq.Header.Set("X-Forwarded-For", clientIP)
}
res, err := transport.RoundTrip(outreq)
@@ -112,20 +122,29 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusInternalServerError)
return
}
+ defer res.Body.Close()
copyHeader(rw.Header(), res.Header)
rw.WriteHeader(res.StatusCode)
+ p.copyResponse(rw, res.Body)
+}
- if res.Body != nil {
- var dst io.Writer = rw
- if p.FlushInterval != 0 {
- if wf, ok := rw.(writeFlusher); ok {
- dst = &maxLatencyWriter{dst: wf, latency: p.FlushInterval}
+func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
+ if p.FlushInterval != 0 {
+ if wf, ok := dst.(writeFlusher); ok {
+ mlw := &maxLatencyWriter{
+ dst: wf,
+ latency: p.FlushInterval,
+ done: make(chan bool),
}
+ go mlw.flushLoop()
+ defer mlw.stop()
+ dst = mlw
}
- io.Copy(dst, res.Body)
}
+
+ io.Copy(dst, src)
}
type writeFlusher interface {
@@ -137,22 +156,14 @@ type maxLatencyWriter struct {
dst writeFlusher
latency time.Duration
- lk sync.Mutex // protects init of done, as well Write + Flush
+ lk sync.Mutex // protects Write + Flush
done chan bool
}
-func (m *maxLatencyWriter) Write(p []byte) (n int, err error) {
+func (m *maxLatencyWriter) Write(p []byte) (int, error) {
m.lk.Lock()
defer m.lk.Unlock()
- if m.done == nil {
- m.done = make(chan bool)
- go m.flushLoop()
- }
- n, err = m.dst.Write(p)
- if err != nil {
- m.done <- true
- }
- return
+ return m.dst.Write(p)
}
func (m *maxLatencyWriter) flushLoop() {
@@ -160,13 +171,18 @@ func (m *maxLatencyWriter) flushLoop() {
defer t.Stop()
for {
select {
+ case <-m.done:
+ if onExitFlushLoop != nil {
+ onExitFlushLoop()
+ }
+ return
case <-t.C:
m.lk.Lock()
m.dst.Flush()
m.lk.Unlock()
- case <-m.done:
- return
}
}
panic("unreached")
}
+
+func (m *maxLatencyWriter) stop() { m.done <- true }
diff --git a/src/pkg/net/http/httputil/reverseproxy_test.go b/src/pkg/net/http/httputil/reverseproxy_test.go
index 28e9c90ad..863927162 100644
--- a/src/pkg/net/http/httputil/reverseproxy_test.go
+++ b/src/pkg/net/http/httputil/reverseproxy_test.go
@@ -11,7 +11,9 @@ import (
"net/http"
"net/http/httptest"
"net/url"
+ "strings"
"testing"
+ "time"
)
func TestReverseProxy(t *testing.T) {
@@ -70,6 +72,47 @@ func TestReverseProxy(t *testing.T) {
}
}
+func TestXForwardedFor(t *testing.T) {
+ const prevForwardedFor = "client ip"
+ const backendResponse = "I am the backend"
+ const backendStatus = 404
+ backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.Header.Get("X-Forwarded-For") == "" {
+ t.Errorf("didn't get X-Forwarded-For header")
+ }
+ if !strings.Contains(r.Header.Get("X-Forwarded-For"), prevForwardedFor) {
+ t.Errorf("X-Forwarded-For didn't contain prior data")
+ }
+ w.WriteHeader(backendStatus)
+ w.Write([]byte(backendResponse))
+ }))
+ defer backend.Close()
+ backendURL, err := url.Parse(backend.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ proxyHandler := NewSingleHostReverseProxy(backendURL)
+ frontend := httptest.NewServer(proxyHandler)
+ defer frontend.Close()
+
+ getReq, _ := http.NewRequest("GET", frontend.URL, nil)
+ getReq.Host = "some-name"
+ getReq.Header.Set("Connection", "close")
+ getReq.Header.Set("X-Forwarded-For", prevForwardedFor)
+ getReq.Close = true
+ res, err := http.DefaultClient.Do(getReq)
+ if err != nil {
+ t.Fatalf("Get: %v", err)
+ }
+ if g, e := res.StatusCode, backendStatus; g != e {
+ t.Errorf("got res.StatusCode %d; expected %d", g, e)
+ }
+ bodyBytes, _ := ioutil.ReadAll(res.Body)
+ if g, e := string(bodyBytes), backendResponse; g != e {
+ t.Errorf("got body %q; expected %q", g, e)
+ }
+}
+
var proxyQueryTests = []struct {
baseSuffix string // suffix to add to backend URL
reqSuffix string // suffix to add to frontend's request URL
@@ -107,3 +150,44 @@ func TestReverseProxyQuery(t *testing.T) {
frontend.Close()
}
}
+
+func TestReverseProxyFlushInterval(t *testing.T) {
+ const expected = "hi"
+ backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte(expected))
+ }))
+ defer backend.Close()
+
+ backendURL, err := url.Parse(backend.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ proxyHandler := NewSingleHostReverseProxy(backendURL)
+ proxyHandler.FlushInterval = time.Microsecond
+
+ done := make(chan bool)
+ onExitFlushLoop = func() { done <- true }
+ defer func() { onExitFlushLoop = nil }()
+
+ frontend := httptest.NewServer(proxyHandler)
+ defer frontend.Close()
+
+ req, _ := http.NewRequest("GET", frontend.URL, nil)
+ req.Close = true
+ res, err := http.DefaultClient.Do(req)
+ if err != nil {
+ t.Fatalf("Get: %v", err)
+ }
+ defer res.Body.Close()
+ if bodyBytes, _ := ioutil.ReadAll(res.Body); string(bodyBytes) != expected {
+ t.Errorf("got body %q; expected %q", bodyBytes, expected)
+ }
+
+ select {
+ case <-done:
+ // OK
+ case <-time.After(5 * time.Second):
+ t.Error("maxLatencyWriter flushLoop() never exited")
+ }
+}
diff --git a/src/pkg/net/http/jar.go b/src/pkg/net/http/jar.go
index 2c2caa251..5c3de0dad 100644
--- a/src/pkg/net/http/jar.go
+++ b/src/pkg/net/http/jar.go
@@ -8,23 +8,20 @@ import (
"net/url"
)
-// A CookieJar manages storage and use of cookies in HTTP requests.
+// A CookieJar manages storage and use of cookies in HTTP requests.
//
// Implementations of CookieJar must be safe for concurrent use by multiple
// goroutines.
+//
+// The net/http/cookiejar package provides a CookieJar implementation.
type CookieJar interface {
- // SetCookies handles the receipt of the cookies in a reply for the
- // given URL. It may or may not choose to save the cookies, depending
- // on the jar's policy and implementation.
+ // SetCookies handles the receipt of the cookies in a reply for the
+ // given URL. It may or may not choose to save the cookies, depending
+ // on the jar's policy and implementation.
SetCookies(u *url.URL, cookies []*Cookie)
// Cookies returns the cookies to send in a request for the given URL.
- // It is up to the implementation to honor the standard cookie use
- // restrictions such as in RFC 6265.
+ // It is up to the implementation to honor the standard cookie use
+ // restrictions such as in RFC 6265.
Cookies(u *url.URL) []*Cookie
}
-
-type blackHoleJar struct{}
-
-func (blackHoleJar) SetCookies(u *url.URL, cookies []*Cookie) {}
-func (blackHoleJar) Cookies(u *url.URL) []*Cookie { return nil }
diff --git a/src/pkg/net/http/lex.go b/src/pkg/net/http/lex.go
index ffb393ccf..cb33318f4 100644
--- a/src/pkg/net/http/lex.go
+++ b/src/pkg/net/http/lex.go
@@ -6,131 +6,91 @@ package http
// This file deals with lexical matters of HTTP
-func isSeparator(c byte) bool {
- switch c {
- case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t':
- return true
- }
- return false
+var isTokenTable = [127]bool{
+ '!': true,
+ '#': true,
+ '$': true,
+ '%': true,
+ '&': true,
+ '\'': true,
+ '*': true,
+ '+': true,
+ '-': true,
+ '.': true,
+ '0': true,
+ '1': true,
+ '2': true,
+ '3': true,
+ '4': true,
+ '5': true,
+ '6': true,
+ '7': true,
+ '8': true,
+ '9': true,
+ 'A': true,
+ 'B': true,
+ 'C': true,
+ 'D': true,
+ 'E': true,
+ 'F': true,
+ 'G': true,
+ 'H': true,
+ 'I': true,
+ 'J': true,
+ 'K': true,
+ 'L': true,
+ 'M': true,
+ 'N': true,
+ 'O': true,
+ 'P': true,
+ 'Q': true,
+ 'R': true,
+ 'S': true,
+ 'T': true,
+ 'U': true,
+ 'W': true,
+ 'V': true,
+ 'X': true,
+ 'Y': true,
+ 'Z': true,
+ '^': true,
+ '_': true,
+ '`': true,
+ 'a': true,
+ 'b': true,
+ 'c': true,
+ 'd': true,
+ 'e': true,
+ 'f': true,
+ 'g': true,
+ 'h': true,
+ 'i': true,
+ 'j': true,
+ 'k': true,
+ 'l': true,
+ 'm': true,
+ 'n': true,
+ 'o': true,
+ 'p': true,
+ 'q': true,
+ 'r': true,
+ 's': true,
+ 't': true,
+ 'u': true,
+ 'v': true,
+ 'w': true,
+ 'x': true,
+ 'y': true,
+ 'z': true,
+ '|': true,
+ '~': true,
}
-func isCtl(c byte) bool { return (0 <= c && c <= 31) || c == 127 }
-
-func isChar(c byte) bool { return 0 <= c && c <= 127 }
-
-func isAnyText(c byte) bool { return !isCtl(c) }
-
-func isQdText(c byte) bool { return isAnyText(c) && c != '"' }
-
-func isToken(c byte) bool { return isChar(c) && !isCtl(c) && !isSeparator(c) }
-
-// Valid escaped sequences are not specified in RFC 2616, so for now, we assume
-// that they coincide with the common sense ones used by GO. Malformed
-// characters should probably not be treated as errors by a robust (forgiving)
-// parser, so we replace them with the '?' character.
-func httpUnquotePair(b byte) byte {
- // skip the first byte, which should always be '\'
- switch b {
- case 'a':
- return '\a'
- case 'b':
- return '\b'
- case 'f':
- return '\f'
- case 'n':
- return '\n'
- case 'r':
- return '\r'
- case 't':
- return '\t'
- case 'v':
- return '\v'
- case '\\':
- return '\\'
- case '\'':
- return '\''
- case '"':
- return '"'
- }
- return '?'
-}
-
-// raw must begin with a valid quoted string. Only the first quoted string is
-// parsed and is unquoted in result. eaten is the number of bytes parsed, or -1
-// upon failure.
-func httpUnquote(raw []byte) (eaten int, result string) {
- buf := make([]byte, len(raw))
- if raw[0] != '"' {
- return -1, ""
- }
- eaten = 1
- j := 0 // # of bytes written in buf
- for i := 1; i < len(raw); i++ {
- switch b := raw[i]; b {
- case '"':
- eaten++
- buf = buf[0:j]
- return i + 1, string(buf)
- case '\\':
- if len(raw) < i+2 {
- return -1, ""
- }
- buf[j] = httpUnquotePair(raw[i+1])
- eaten += 2
- j++
- i++
- default:
- if isQdText(b) {
- buf[j] = b
- } else {
- buf[j] = '?'
- }
- eaten++
- j++
- }
- }
- return -1, ""
+func isToken(r rune) bool {
+ i := int(r)
+ return i < len(isTokenTable) && isTokenTable[i]
}
-// This is a best effort parse, so errors are not returned, instead not all of
-// the input string might be parsed. result is always non-nil.
-func httpSplitFieldValue(fv string) (eaten int, result []string) {
- result = make([]string, 0, len(fv))
- raw := []byte(fv)
- i := 0
- chunk := ""
- for i < len(raw) {
- b := raw[i]
- switch {
- case b == '"':
- eaten, unq := httpUnquote(raw[i:len(raw)])
- if eaten < 0 {
- return i, result
- } else {
- i += eaten
- chunk += unq
- }
- case isSeparator(b):
- if chunk != "" {
- result = result[0 : len(result)+1]
- result[len(result)-1] = chunk
- chunk = ""
- }
- i++
- case isToken(b):
- chunk += string(b)
- i++
- case b == '\n' || b == '\r':
- i++
- default:
- chunk += "?"
- i++
- }
- }
- if chunk != "" {
- result = result[0 : len(result)+1]
- result[len(result)-1] = chunk
- chunk = ""
- }
- return i, result
+func isNotToken(r rune) bool {
+ return !isToken(r)
}
diff --git a/src/pkg/net/http/lex_test.go b/src/pkg/net/http/lex_test.go
index 5386f7534..6d9d294f7 100644
--- a/src/pkg/net/http/lex_test.go
+++ b/src/pkg/net/http/lex_test.go
@@ -8,63 +8,24 @@ import (
"testing"
)
-type lexTest struct {
- Raw string
- Parsed int // # of parsed characters
- Result []string
-}
+func isChar(c rune) bool { return c <= 127 }
-var lexTests = []lexTest{
- {
- Raw: `"abc"def,:ghi`,
- Parsed: 13,
- Result: []string{"abcdef", "ghi"},
- },
- // My understanding of the RFC is that escape sequences outside of
- // quotes are not interpreted?
- {
- Raw: `"\t"\t"\t"`,
- Parsed: 10,
- Result: []string{"\t", "t\t"},
- },
- {
- Raw: `"\yab"\r\n`,
- Parsed: 10,
- Result: []string{"?ab", "r", "n"},
- },
- {
- Raw: "ab\f",
- Parsed: 3,
- Result: []string{"ab?"},
- },
- {
- Raw: "\"ab \" c,de f, gh, ij\n\t\r",
- Parsed: 23,
- Result: []string{"ab ", "c", "de", "f", "gh", "ij"},
- },
-}
+func isCtl(c rune) bool { return c <= 31 || c == 127 }
-func min(x, y int) int {
- if x <= y {
- return x
+func isSeparator(c rune) bool {
+ switch c {
+ case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t':
+ return true
}
- return y
+ return false
}
-func TestSplitFieldValue(t *testing.T) {
- for k, l := range lexTests {
- parsed, result := httpSplitFieldValue(l.Raw)
- if parsed != l.Parsed {
- t.Errorf("#%d: Parsed %d, expected %d", k, parsed, l.Parsed)
- }
- if len(result) != len(l.Result) {
- t.Errorf("#%d: Result len %d, expected %d", k, len(result), len(l.Result))
- }
- for i := 0; i < min(len(result), len(l.Result)); i++ {
- if result[i] != l.Result[i] {
- t.Errorf("#%d: %d-th entry mismatch. Have {%s}, expect {%s}",
- k, i, result[i], l.Result[i])
- }
+func TestIsToken(t *testing.T) {
+ for i := 0; i <= 130; i++ {
+ r := rune(i)
+ expected := isChar(r) && !isCtl(r) && !isSeparator(r)
+ if isToken(r) != expected {
+ t.Errorf("isToken(0x%x) = %v", r, !expected)
}
}
}
diff --git a/src/pkg/net/http/npn_test.go b/src/pkg/net/http/npn_test.go
new file mode 100644
index 000000000..98b8930d0
--- /dev/null
+++ b/src/pkg/net/http/npn_test.go
@@ -0,0 +1,118 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http_test
+
+import (
+ "bufio"
+ "crypto/tls"
+ "fmt"
+ "io"
+ "io/ioutil"
+ . "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+)
+
+func TestNextProtoUpgrade(t *testing.T) {
+ ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ fmt.Fprintf(w, "path=%s,proto=", r.URL.Path)
+ if r.TLS != nil {
+ w.Write([]byte(r.TLS.NegotiatedProtocol))
+ }
+ if r.RemoteAddr == "" {
+ t.Error("request with no RemoteAddr")
+ }
+ if r.Body == nil {
+ t.Errorf("request with nil Body")
+ }
+ }))
+ ts.TLS = &tls.Config{
+ NextProtos: []string{"unhandled-proto", "tls-0.9"},
+ }
+ ts.Config.TLSNextProto = map[string]func(*Server, *tls.Conn, Handler){
+ "tls-0.9": handleTLSProtocol09,
+ }
+ ts.StartTLS()
+ defer ts.Close()
+
+ tr := newTLSTransport(t, ts)
+ defer tr.CloseIdleConnections()
+ c := &Client{Transport: tr}
+
+ // Normal request, without NPN.
+ {
+ res, err := c.Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want := "path=/,proto="; string(body) != want {
+ t.Errorf("plain request = %q; want %q", body, want)
+ }
+ }
+
+ // Request to an advertised but unhandled NPN protocol.
+ // Server will hang up.
+ {
+ tr.CloseIdleConnections()
+ tr.TLSClientConfig.NextProtos = []string{"unhandled-proto"}
+ _, err := c.Get(ts.URL)
+ if err == nil {
+ t.Errorf("expected error on unhandled-proto request")
+ }
+ }
+
+ // Request using the "tls-0.9" protocol, which we register here.
+ // It is HTTP/0.9 over TLS.
+ {
+ tlsConfig := newTLSTransport(t, ts).TLSClientConfig
+ tlsConfig.NextProtos = []string{"tls-0.9"}
+ conn, err := tls.Dial("tcp", ts.Listener.Addr().String(), tlsConfig)
+ if err != nil {
+ t.Fatal(err)
+ }
+ conn.Write([]byte("GET /foo\n"))
+ body, err := ioutil.ReadAll(conn)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want := "path=/foo,proto=tls-0.9"; string(body) != want {
+ t.Errorf("plain request = %q; want %q", body, want)
+ }
+ }
+}
+
+// handleTLSProtocol09 implements the HTTP/0.9 protocol over TLS, for the
+// TestNextProtoUpgrade test.
+func handleTLSProtocol09(srv *Server, conn *tls.Conn, h Handler) {
+ br := bufio.NewReader(conn)
+ line, err := br.ReadString('\n')
+ if err != nil {
+ return
+ }
+ line = strings.TrimSpace(line)
+ path := strings.TrimPrefix(line, "GET ")
+ if path == line {
+ return
+ }
+ req, _ := NewRequest("GET", path, nil)
+ req.Proto = "HTTP/0.9"
+ req.ProtoMajor = 0
+ req.ProtoMinor = 9
+ rw := &http09Writer{conn, make(Header)}
+ h.ServeHTTP(rw, req)
+}
+
+type http09Writer struct {
+ io.Writer
+ h Header
+}
+
+func (w http09Writer) Header() Header { return w.h }
+func (w http09Writer) WriteHeader(int) {} // no headers
diff --git a/src/pkg/net/http/pprof/pprof.go b/src/pkg/net/http/pprof/pprof.go
index 06fcde144..0c7548e3e 100644
--- a/src/pkg/net/http/pprof/pprof.go
+++ b/src/pkg/net/http/pprof/pprof.go
@@ -14,6 +14,14 @@
// To use pprof, link this package into your program:
// import _ "net/http/pprof"
//
+// If your application is not already running an http server, you
+// need to start one. Add "net/http" and "log" to your imports and
+// the following code to your main function:
+//
+// go func() {
+// log.Println(http.ListenAndServe("localhost:6060", nil))
+// }()
+//
// Then use the pprof tool to look at the heap profile:
//
// go tool pprof http://localhost:6060/debug/pprof/heap
@@ -22,9 +30,12 @@
//
// go tool pprof http://localhost:6060/debug/pprof/profile
//
-// Or to view all available profiles:
+// Or to look at the goroutine blocking profile:
+//
+// go tool pprof http://localhost:6060/debug/pprof/block
//
-// go tool pprof http://localhost:6060/debug/pprof/
+// To view all available profiles, open http://localhost:6060/debug/pprof/
+// in your browser.
//
// For a study of the facility in action, visit
//
@@ -161,7 +172,7 @@ func (name handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// listing the available profiles.
func Index(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/debug/pprof/") {
- name := r.URL.Path[len("/debug/pprof/"):]
+ name := strings.TrimPrefix(r.URL.Path, "/debug/pprof/")
if name != "" {
handler(name).ServeHTTP(w, r)
return
diff --git a/src/pkg/net/http/proxy_test.go b/src/pkg/net/http/proxy_test.go
index 5ecffafac..449ccaeea 100644
--- a/src/pkg/net/http/proxy_test.go
+++ b/src/pkg/net/http/proxy_test.go
@@ -25,13 +25,13 @@ var UseProxyTests = []struct {
{"[::2]", true}, // not a loopback address
{"barbaz.net", false}, // match as .barbaz.net
- {"foobar.com", false}, // have a port but match
+ {"foobar.com", false}, // have a port but match
{"foofoobar.com", true}, // not match as a part of foobar.com
{"baz.com", true}, // not match as a part of barbaz.com
{"localhost.net", true}, // not match as suffix of address
{"local.localhost", true}, // not match as prefix as address
{"barbarbaz.net", true}, // not match because NO_PROXY have a '.'
- {"www.foobar.com", true}, // not match because NO_PROXY is not .foobar.com
+ {"www.foobar.com", false}, // match because NO_PROXY includes "foobar.com"
}
func TestUseProxy(t *testing.T) {
diff --git a/src/pkg/net/http/range_test.go b/src/pkg/net/http/range_test.go
index 5274a81fa..ef911af7b 100644
--- a/src/pkg/net/http/range_test.go
+++ b/src/pkg/net/http/range_test.go
@@ -14,15 +14,34 @@ var ParseRangeTests = []struct {
r []httpRange
}{
{"", 0, nil},
+ {"", 1000, nil},
{"foo", 0, nil},
{"bytes=", 0, nil},
+ {"bytes=7", 10, nil},
+ {"bytes= 7 ", 10, nil},
+ {"bytes=1-", 0, nil},
{"bytes=5-4", 10, nil},
{"bytes=0-2,5-4", 10, nil},
+ {"bytes=2-5,4-3", 10, nil},
+ {"bytes=--5,4--3", 10, nil},
+ {"bytes=A-", 10, nil},
+ {"bytes=A- ", 10, nil},
+ {"bytes=A-Z", 10, nil},
+ {"bytes= -Z", 10, nil},
+ {"bytes=5-Z", 10, nil},
+ {"bytes=Ran-dom, garbage", 10, nil},
+ {"bytes=0x01-0x02", 10, nil},
+ {"bytes= ", 10, nil},
+ {"bytes= , , , ", 10, nil},
+
{"bytes=0-9", 10, []httpRange{{0, 10}}},
{"bytes=0-", 10, []httpRange{{0, 10}}},
{"bytes=5-", 10, []httpRange{{5, 5}}},
{"bytes=0-20", 10, []httpRange{{0, 10}}},
{"bytes=15-,0-5", 10, nil},
+ {"bytes=1-2,5-", 10, []httpRange{{1, 2}, {5, 5}}},
+ {"bytes=-2 , 7-", 11, []httpRange{{9, 2}, {7, 4}}},
+ {"bytes=0-0 ,2-2, 7-", 11, []httpRange{{0, 1}, {2, 1}, {7, 4}}},
{"bytes=-5", 10, []httpRange{{5, 5}}},
{"bytes=-15", 10, []httpRange{{0, 10}}},
{"bytes=0-499", 10000, []httpRange{{0, 500}}},
@@ -32,6 +51,9 @@ var ParseRangeTests = []struct {
{"bytes=0-0,-1", 10000, []httpRange{{0, 1}, {9999, 1}}},
{"bytes=500-600,601-999", 10000, []httpRange{{500, 101}, {601, 399}}},
{"bytes=500-700,601-999", 10000, []httpRange{{500, 201}, {601, 399}}},
+
+ // Match Apache laxity:
+ {"bytes= 1 -2 , 4- 5, 7 - 8 , ,,", 11, []httpRange{{1, 2}, {4, 2}, {7, 2}}},
}
func TestParseRange(t *testing.T) {
diff --git a/src/pkg/net/http/readrequest_test.go b/src/pkg/net/http/readrequest_test.go
index 2e03c658a..ffdd6a892 100644
--- a/src/pkg/net/http/readrequest_test.go
+++ b/src/pkg/net/http/readrequest_test.go
@@ -247,6 +247,54 @@ var reqTests = []reqTest{
noTrailer,
noError,
},
+
+ // SSDP Notify request. golang.org/issue/3692
+ {
+ "NOTIFY * HTTP/1.1\r\nServer: foo\r\n\r\n",
+ &Request{
+ Method: "NOTIFY",
+ URL: &url.URL{
+ Path: "*",
+ },
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: Header{
+ "Server": []string{"foo"},
+ },
+ Close: false,
+ ContentLength: 0,
+ RequestURI: "*",
+ },
+
+ noBody,
+ noTrailer,
+ noError,
+ },
+
+ // OPTIONS request. Similar to golang.org/issue/3692
+ {
+ "OPTIONS * HTTP/1.1\r\nServer: foo\r\n\r\n",
+ &Request{
+ Method: "OPTIONS",
+ URL: &url.URL{
+ Path: "*",
+ },
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: Header{
+ "Server": []string{"foo"},
+ },
+ Close: false,
+ ContentLength: 0,
+ RequestURI: "*",
+ },
+
+ noBody,
+ noTrailer,
+ noError,
+ },
}
func TestReadRequest(t *testing.T) {
diff --git a/src/pkg/net/http/request.go b/src/pkg/net/http/request.go
index f5bc6eb91..217f35b48 100644
--- a/src/pkg/net/http/request.go
+++ b/src/pkg/net/http/request.go
@@ -19,6 +19,7 @@ import (
"mime/multipart"
"net/textproto"
"net/url"
+ "strconv"
"strings"
)
@@ -70,7 +71,13 @@ var reqWriteExcludeHeader = map[string]bool{
// or to be sent by a client.
type Request struct {
Method string // GET, POST, PUT, etc.
- URL *url.URL
+
+ // URL is created from the URI supplied on the Request-Line
+ // as stored in RequestURI.
+ //
+ // For most requests, fields other than Path and RawQuery
+ // will be empty. (See RFC 2616, Section 5.1.2)
+ URL *url.URL
// The protocol version for incoming requests.
// Outgoing requests always use HTTP/1.1.
@@ -123,6 +130,7 @@ type Request struct {
// The host on which the URL is sought.
// Per RFC 2616, this is either the value of the Host: header
// or the host name given in the URL itself.
+ // It may be of the form "host:port".
Host string
// Form contains the parsed form data, including both the URL
@@ -131,6 +139,12 @@ type Request struct {
// The HTTP client ignores Form and uses Body instead.
Form url.Values
+ // PostForm contains the parsed form data from POST or PUT
+ // body parameters.
+ // This field is only available after ParseForm is called.
+ // The HTTP client ignores PostForm and uses Body instead.
+ PostForm url.Values
+
// MultipartForm is the parsed multipart form, including file uploads.
// This field is only available after ParseMultipartForm is called.
// The HTTP client ignores MultipartForm and uses Body instead.
@@ -317,11 +331,20 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
}
// TODO(bradfitz): escape at least newlines in ruri?
- bw := bufio.NewWriter(w)
- fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri)
+ // Wrap the writer in a bufio Writer if it's not already buffered.
+ // Don't always call NewWriter, as that forces a bytes.Buffer
+ // and other small bufio Writers to have a minimum 4k buffer
+ // size.
+ var bw *bufio.Writer
+ if _, ok := w.(io.ByteWriter); !ok {
+ bw = bufio.NewWriter(w)
+ w = bw
+ }
+
+ fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri)
// Header lines
- fmt.Fprintf(bw, "Host: %s\r\n", host)
+ fmt.Fprintf(w, "Host: %s\r\n", host)
// Use the defaultUserAgent unless the Header contains one, which
// may be blank to not send the header.
@@ -332,7 +355,7 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
}
}
if userAgent != "" {
- fmt.Fprintf(bw, "User-Agent: %s\r\n", userAgent)
+ fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent)
}
// Process Body,ContentLength,Close,Trailer
@@ -340,65 +363,61 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
if err != nil {
return err
}
- err = tw.WriteHeader(bw)
+ err = tw.WriteHeader(w)
if err != nil {
return err
}
// TODO: split long values? (If so, should share code with Conn.Write)
- err = req.Header.WriteSubset(bw, reqWriteExcludeHeader)
+ err = req.Header.WriteSubset(w, reqWriteExcludeHeader)
if err != nil {
return err
}
if extraHeaders != nil {
- err = extraHeaders.Write(bw)
+ err = extraHeaders.Write(w)
if err != nil {
return err
}
}
- io.WriteString(bw, "\r\n")
+ io.WriteString(w, "\r\n")
// Write body and trailer
- err = tw.WriteBody(bw)
+ err = tw.WriteBody(w)
if err != nil {
return err
}
- return bw.Flush()
-}
-
-// Convert decimal at s[i:len(s)] to integer,
-// returning value, string position where the digits stopped,
-// and whether there was a valid number (digits, not too big).
-func atoi(s string, i int) (n, i1 int, ok bool) {
- const Big = 1000000
- if i >= len(s) || s[i] < '0' || s[i] > '9' {
- return 0, 0, false
- }
- n = 0
- for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
- n = n*10 + int(s[i]-'0')
- if n > Big {
- return 0, 0, false
- }
+ if bw != nil {
+ return bw.Flush()
}
- return n, i, true
+ return nil
}
// ParseHTTPVersion parses a HTTP version string.
// "HTTP/1.0" returns (1, 0, true).
func ParseHTTPVersion(vers string) (major, minor int, ok bool) {
- if len(vers) < 5 || vers[0:5] != "HTTP/" {
+ const Big = 1000000 // arbitrary upper bound
+ switch vers {
+ case "HTTP/1.1":
+ return 1, 1, true
+ case "HTTP/1.0":
+ return 1, 0, true
+ }
+ if !strings.HasPrefix(vers, "HTTP/") {
return 0, 0, false
}
- major, i, ok := atoi(vers, 5)
- if !ok || i >= len(vers) || vers[i] != '.' {
+ dot := strings.Index(vers, ".")
+ if dot < 0 {
return 0, 0, false
}
- minor, i, ok = atoi(vers, i+1)
- if !ok || i != len(vers) {
+ major, err := strconv.Atoi(vers[5:dot])
+ if err != nil || major < 0 || major > Big {
+ return 0, 0, false
+ }
+ minor, err = strconv.Atoi(vers[dot+1:])
+ if err != nil || minor < 0 || minor > Big {
return 0, 0, false
}
return major, minor, true
@@ -426,10 +445,12 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
}
if body != nil {
switch v := body.(type) {
- case *strings.Reader:
- req.ContentLength = int64(v.Len())
case *bytes.Buffer:
req.ContentLength = int64(v.Len())
+ case *bytes.Reader:
+ req.ContentLength = int64(v.Len())
+ case *strings.Reader:
+ req.ContentLength = int64(v.Len())
}
}
@@ -513,9 +534,9 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) {
// the same. In the second case, any Host line is ignored.
req.Host = req.URL.Host
if req.Host == "" {
- req.Host = req.Header.Get("Host")
+ req.Host = req.Header.get("Host")
}
- req.Header.Del("Host")
+ delete(req.Header, "Host")
fixPragmaCacheControl(req.Header)
@@ -594,66 +615,97 @@ func (l *maxBytesReader) Close() error {
return l.r.Close()
}
-// ParseForm parses the raw query from the URL.
+func copyValues(dst, src url.Values) {
+ for k, vs := range src {
+ for _, value := range vs {
+ dst.Add(k, value)
+ }
+ }
+}
+
+func parsePostForm(r *Request) (vs url.Values, err error) {
+ if r.Body == nil {
+ err = errors.New("missing form body")
+ return
+ }
+ ct := r.Header.Get("Content-Type")
+ ct, _, err = mime.ParseMediaType(ct)
+ switch {
+ case ct == "application/x-www-form-urlencoded":
+ var reader io.Reader = r.Body
+ maxFormSize := int64(1<<63 - 1)
+ if _, ok := r.Body.(*maxBytesReader); !ok {
+ maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
+ reader = io.LimitReader(r.Body, maxFormSize+1)
+ }
+ b, e := ioutil.ReadAll(reader)
+ if e != nil {
+ if err == nil {
+ err = e
+ }
+ break
+ }
+ if int64(len(b)) > maxFormSize {
+ err = errors.New("http: POST too large")
+ return
+ }
+ vs, e = url.ParseQuery(string(b))
+ if err == nil {
+ err = e
+ }
+ case ct == "multipart/form-data":
+ // handled by ParseMultipartForm (which is calling us, or should be)
+ // TODO(bradfitz): there are too many possible
+ // orders to call too many functions here.
+ // Clean this up and write more tests.
+ // request_test.go contains the start of this,
+ // in TestRequestMultipartCallOrder.
+ }
+ return
+}
+
+// ParseForm parses the raw query from the URL and updates r.Form.
+//
+// For POST or PUT requests, it also parses the request body as a form and
+// put the results into both r.PostForm and r.Form.
+// POST and PUT body parameters take precedence over URL query string values
+// in r.Form.
//
-// For POST or PUT requests, it also parses the request body as a form.
// If the request Body's size has not already been limited by MaxBytesReader,
// the size is capped at 10MB.
//
// ParseMultipartForm calls ParseForm automatically.
// It is idempotent.
-func (r *Request) ParseForm() (err error) {
- if r.Form != nil {
- return
- }
- if r.URL != nil {
- r.Form, err = url.ParseQuery(r.URL.RawQuery)
+func (r *Request) ParseForm() error {
+ var err error
+ if r.PostForm == nil {
+ if r.Method == "POST" || r.Method == "PUT" {
+ r.PostForm, err = parsePostForm(r)
+ }
+ if r.PostForm == nil {
+ r.PostForm = make(url.Values)
+ }
}
- if r.Method == "POST" || r.Method == "PUT" {
- if r.Body == nil {
- return errors.New("missing form body")
+ if r.Form == nil {
+ if len(r.PostForm) > 0 {
+ r.Form = make(url.Values)
+ copyValues(r.Form, r.PostForm)
}
- ct := r.Header.Get("Content-Type")
- ct, _, err = mime.ParseMediaType(ct)
- switch {
- case ct == "application/x-www-form-urlencoded":
- var reader io.Reader = r.Body
- maxFormSize := int64(1<<63 - 1)
- if _, ok := r.Body.(*maxBytesReader); !ok {
- maxFormSize = int64(10 << 20) // 10 MB is a lot of text.
- reader = io.LimitReader(r.Body, maxFormSize+1)
- }
- b, e := ioutil.ReadAll(reader)
- if e != nil {
- if err == nil {
- err = e
- }
- break
- }
- if int64(len(b)) > maxFormSize {
- return errors.New("http: POST too large")
- }
- var newValues url.Values
- newValues, e = url.ParseQuery(string(b))
+ var newValues url.Values
+ if r.URL != nil {
+ var e error
+ newValues, e = url.ParseQuery(r.URL.RawQuery)
if err == nil {
err = e
}
- if r.Form == nil {
- r.Form = make(url.Values)
- }
- // Copy values into r.Form. TODO: make this smoother.
- for k, vs := range newValues {
- for _, value := range vs {
- r.Form.Add(k, value)
- }
- }
- case ct == "multipart/form-data":
- // handled by ParseMultipartForm (which is calling us, or should be)
- // TODO(bradfitz): there are too many possible
- // orders to call too many functions here.
- // Clean this up and write more tests.
- // request_test.go contains the start of this,
- // in TestRequestMultipartCallOrder.
+ }
+ if newValues == nil {
+ newValues = make(url.Values)
+ }
+ if r.Form == nil {
+ r.Form = newValues
+ } else {
+ copyValues(r.Form, newValues)
}
}
return err
@@ -699,7 +751,9 @@ func (r *Request) ParseMultipartForm(maxMemory int64) error {
}
// FormValue returns the first value for the named component of the query.
+// POST and PUT body parameters take precedence over URL query string values.
// FormValue calls ParseMultipartForm and ParseForm if necessary.
+// To access multiple values of the same key use ParseForm.
func (r *Request) FormValue(key string) string {
if r.Form == nil {
r.ParseMultipartForm(defaultMaxMemory)
@@ -710,6 +764,19 @@ func (r *Request) FormValue(key string) string {
return ""
}
+// PostFormValue returns the first value for the named component of the POST
+// or PUT request body. URL query parameters are ignored.
+// PostFormValue calls ParseMultipartForm and ParseForm if necessary.
+func (r *Request) PostFormValue(key string) string {
+ if r.PostForm == nil {
+ r.ParseMultipartForm(defaultMaxMemory)
+ }
+ if vs := r.PostForm[key]; len(vs) > 0 {
+ return vs[0]
+ }
+ return ""
+}
+
// FormFile returns the first file for the provided form key.
// FormFile calls ParseMultipartForm and ParseForm if necessary.
func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
@@ -732,12 +799,16 @@ func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, e
}
func (r *Request) expectsContinue() bool {
- return strings.ToLower(r.Header.Get("Expect")) == "100-continue"
+ return hasToken(r.Header.get("Expect"), "100-continue")
}
func (r *Request) wantsHttp10KeepAlive() bool {
if r.ProtoMajor != 1 || r.ProtoMinor != 0 {
return false
}
- return strings.Contains(strings.ToLower(r.Header.Get("Connection")), "keep-alive")
+ return hasToken(r.Header.get("Connection"), "keep-alive")
+}
+
+func (r *Request) wantsClose() bool {
+ return hasToken(r.Header.get("Connection"), "close")
}
diff --git a/src/pkg/net/http/request_test.go b/src/pkg/net/http/request_test.go
index 6e00b9bfd..00ad791de 100644
--- a/src/pkg/net/http/request_test.go
+++ b/src/pkg/net/http/request_test.go
@@ -30,8 +30,8 @@ func TestQuery(t *testing.T) {
}
func TestPostQuery(t *testing.T) {
- req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x",
- strings.NewReader("z=post&both=y"))
+ req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not",
+ strings.NewReader("z=post&both=y&prio=2&empty="))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
if q := req.FormValue("q"); q != "foo" {
@@ -40,8 +40,23 @@ func TestPostQuery(t *testing.T) {
if z := req.FormValue("z"); z != "post" {
t.Errorf(`req.FormValue("z") = %q, want "post"`, z)
}
- if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"x", "y"}) {
- t.Errorf(`req.FormValue("both") = %q, want ["x", "y"]`, both)
+ if bq, found := req.PostForm["q"]; found {
+ t.Errorf(`req.PostForm["q"] = %q, want no entry in map`, bq)
+ }
+ if bz := req.PostFormValue("z"); bz != "post" {
+ t.Errorf(`req.PostFormValue("z") = %q, want "post"`, bz)
+ }
+ if qs := req.Form["q"]; !reflect.DeepEqual(qs, []string{"foo", "bar"}) {
+ t.Errorf(`req.Form["q"] = %q, want ["foo", "bar"]`, qs)
+ }
+ if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"y", "x"}) {
+ t.Errorf(`req.Form["both"] = %q, want ["y", "x"]`, both)
+ }
+ if prio := req.FormValue("prio"); prio != "2" {
+ t.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio)
+ }
+ if empty := req.FormValue("empty"); empty != "" {
+ t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty)
}
}
@@ -76,6 +91,23 @@ func TestParseFormUnknownContentType(t *testing.T) {
}
}
+func TestParseFormInitializeOnError(t *testing.T) {
+ nilBody, _ := NewRequest("POST", "http://www.google.com/search?q=foo", nil)
+ tests := []*Request{
+ nilBody,
+ {Method: "GET", URL: nil},
+ }
+ for i, req := range tests {
+ err := req.ParseForm()
+ if req.Form == nil {
+ t.Errorf("%d. Form not initialized, error %v", i, err)
+ }
+ if req.PostForm == nil {
+ t.Errorf("%d. PostForm not initialized, error %v", i, err)
+ }
+ }
+}
+
func TestMultipartReader(t *testing.T) {
req := &Request{
Method: "POST",
@@ -129,7 +161,7 @@ func TestSetBasicAuth(t *testing.T) {
}
func TestMultipartRequest(t *testing.T) {
- // Test that we can read the values and files of a
+ // Test that we can read the values and files of a
// multipart request with FormValue and FormFile,
// and that ParseMultipartForm can be called multiple times.
req := newTestMultipartRequest(t)
@@ -196,6 +228,75 @@ func TestReadRequestErrors(t *testing.T) {
}
}
+func TestNewRequestHost(t *testing.T) {
+ req, err := NewRequest("GET", "http://localhost:1234/", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if req.Host != "localhost:1234" {
+ t.Errorf("Host = %q; want localhost:1234", req.Host)
+ }
+}
+
+func TestNewRequestContentLength(t *testing.T) {
+ readByte := func(r io.Reader) io.Reader {
+ var b [1]byte
+ r.Read(b[:])
+ return r
+ }
+ tests := []struct {
+ r io.Reader
+ want int64
+ }{
+ {bytes.NewReader([]byte("123")), 3},
+ {bytes.NewBuffer([]byte("1234")), 4},
+ {strings.NewReader("12345"), 5},
+ // Not detected:
+ {struct{ io.Reader }{strings.NewReader("xyz")}, 0},
+ {io.NewSectionReader(strings.NewReader("x"), 0, 6), 0},
+ {readByte(io.NewSectionReader(strings.NewReader("xy"), 0, 6)), 0},
+ }
+ for _, tt := range tests {
+ req, err := NewRequest("POST", "http://localhost/", tt.r)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if req.ContentLength != tt.want {
+ t.Errorf("ContentLength(%T) = %d; want %d", tt.r, req.ContentLength, tt.want)
+ }
+ }
+}
+
+type logWrites struct {
+ t *testing.T
+ dst *[]string
+}
+
+func (l logWrites) WriteByte(c byte) error {
+ l.t.Fatalf("unexpected WriteByte call")
+ return nil
+}
+
+func (l logWrites) Write(p []byte) (n int, err error) {
+ *l.dst = append(*l.dst, string(p))
+ return len(p), nil
+}
+
+func TestRequestWriteBufferedWriter(t *testing.T) {
+ got := []string{}
+ req, _ := NewRequest("GET", "http://foo.com/", nil)
+ req.Write(logWrites{t, &got})
+ want := []string{
+ "GET / HTTP/1.1\r\n",
+ "Host: foo.com\r\n",
+ "User-Agent: Go http package\r\n",
+ "\r\n",
+ }
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("Writes = %q\n Want = %q", got, want)
+ }
+}
+
func testMissingFile(t *testing.T, req *Request) {
f, fh, err := req.FormFile("missing")
if f != nil {
@@ -300,3 +401,81 @@ Content-Disposition: form-data; name="textb"
` + textbValue + `
--MyBoundary--
`
+
+func benchmarkReadRequest(b *testing.B, request string) {
+ request = request + "\n" // final \n
+ request = strings.Replace(request, "\n", "\r\n", -1) // expand \n to \r\n
+ b.SetBytes(int64(len(request)))
+ r := bufio.NewReader(&infiniteReader{buf: []byte(request)})
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := ReadRequest(r)
+ if err != nil {
+ b.Fatalf("failed to read request: %v", err)
+ }
+ }
+}
+
+// infiniteReader satisfies Read requests as if the contents of buf
+// loop indefinitely.
+type infiniteReader struct {
+ buf []byte
+ offset int
+}
+
+func (r *infiniteReader) Read(b []byte) (int, error) {
+ n := copy(b, r.buf[r.offset:])
+ r.offset = (r.offset + n) % len(r.buf)
+ return n, nil
+}
+
+func BenchmarkReadRequestChrome(b *testing.B) {
+ // https://github.com/felixge/node-http-perf/blob/master/fixtures/get.http
+ benchmarkReadRequest(b, `GET / HTTP/1.1
+Host: localhost:8080
+Connection: keep-alive
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17
+Accept-Encoding: gzip,deflate,sdch
+Accept-Language: en-US,en;q=0.8
+Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
+Cookie: __utma=1.1978842379.1323102373.1323102373.1323102373.1; EPi:NumberOfVisits=1,2012-02-28T13:42:18; CrmSession=5b707226b9563e1bc69084d07a107c98; plushContainerWidth=100%25; plushNoTopMenu=0; hudson_auto_refresh=false
+`)
+}
+
+func BenchmarkReadRequestCurl(b *testing.B) {
+ // curl http://localhost:8080/
+ benchmarkReadRequest(b, `GET / HTTP/1.1
+User-Agent: curl/7.27.0
+Host: localhost:8080
+Accept: */*
+`)
+}
+
+func BenchmarkReadRequestApachebench(b *testing.B) {
+ // ab -n 1 -c 1 http://localhost:8080/
+ benchmarkReadRequest(b, `GET / HTTP/1.0
+Host: localhost:8080
+User-Agent: ApacheBench/2.3
+Accept: */*
+`)
+}
+
+func BenchmarkReadRequestSiege(b *testing.B) {
+ // siege -r 1 -c 1 http://localhost:8080/
+ benchmarkReadRequest(b, `GET / HTTP/1.1
+Host: localhost:8080
+Accept: */*
+Accept-Encoding: gzip
+User-Agent: JoeDog/1.00 [en] (X11; I; Siege 2.70)
+Connection: keep-alive
+`)
+}
+
+func BenchmarkReadRequestWrk(b *testing.B) {
+ // wrk -t 1 -r 1 -c 1 http://localhost:8080/
+ benchmarkReadRequest(b, `GET / HTTP/1.1
+Host: localhost:8080
+`)
+}
diff --git a/src/pkg/net/http/requestwrite_test.go b/src/pkg/net/http/requestwrite_test.go
index fc3186f0c..bc637f18b 100644
--- a/src/pkg/net/http/requestwrite_test.go
+++ b/src/pkg/net/http/requestwrite_test.go
@@ -328,6 +328,69 @@ var reqWriteTests = []reqWriteTest{
"User-Agent: Go http package\r\n" +
"X-Foo: X-Bar\r\n\r\n",
},
+
+ // If no Request.Host and no Request.URL.Host, we send
+ // an empty Host header, and don't use
+ // Request.Header["Host"]. This is just testing that
+ // we don't change Go 1.0 behavior.
+ {
+ Req: Request{
+ Method: "GET",
+ Host: "",
+ URL: &url.URL{
+ Scheme: "http",
+ Host: "",
+ Path: "/search",
+ },
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: Header{
+ "Host": []string{"bad.example.com"},
+ },
+ },
+
+ WantWrite: "GET /search HTTP/1.1\r\n" +
+ "Host: \r\n" +
+ "User-Agent: Go http package\r\n\r\n",
+ },
+
+ // Opaque test #1 from golang.org/issue/4860
+ {
+ Req: Request{
+ Method: "GET",
+ URL: &url.URL{
+ Scheme: "http",
+ Host: "www.google.com",
+ Opaque: "/%2F/%2F/",
+ },
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: Header{},
+ },
+
+ WantWrite: "GET /%2F/%2F/ HTTP/1.1\r\n" +
+ "Host: www.google.com\r\n" +
+ "User-Agent: Go http package\r\n\r\n",
+ },
+
+ // Opaque test #2 from golang.org/issue/4860
+ {
+ Req: Request{
+ Method: "GET",
+ URL: &url.URL{
+ Scheme: "http",
+ Host: "x.google.com",
+ Opaque: "//y.google.com/%2F/%2F/",
+ },
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: Header{},
+ },
+
+ WantWrite: "GET http://y.google.com/%2F/%2F/ HTTP/1.1\r\n" +
+ "Host: x.google.com\r\n" +
+ "User-Agent: Go http package\r\n\r\n",
+ },
}
func TestRequestWrite(t *testing.T) {
diff --git a/src/pkg/net/http/response.go b/src/pkg/net/http/response.go
index 945ecd8a4..391ebbf6d 100644
--- a/src/pkg/net/http/response.go
+++ b/src/pkg/net/http/response.go
@@ -49,7 +49,7 @@ type Response struct {
Body io.ReadCloser
// ContentLength records the length of the associated content. The
- // value -1 indicates that the length is unknown. Unless RequestMethod
+ // value -1 indicates that the length is unknown. Unless Request.Method
// is "HEAD", values >= 0 indicate that the given number of bytes may
// be read from Body.
ContentLength int64
@@ -107,7 +107,6 @@ func ReadResponse(r *bufio.Reader, req *Request) (resp *Response, err error) {
resp = new(Response)
resp.Request = req
- resp.Request.Method = strings.ToUpper(resp.Request.Method)
// Parse the first line of the response.
line, err := tp.ReadLine()
@@ -179,7 +178,7 @@ func (r *Response) ProtoAtLeast(major, minor int) bool {
// StatusCode
// ProtoMajor
// ProtoMinor
-// RequestMethod
+// Request.Method
// TransferEncoding
// Trailer
// Body
@@ -188,11 +187,6 @@ func (r *Response) ProtoAtLeast(major, minor int) bool {
//
func (r *Response) Write(w io.Writer) error {
- // RequestMethod should be upper-case
- if r.Request != nil {
- r.Request.Method = strings.ToUpper(r.Request.Method)
- }
-
// Status line
text := r.Status
if text == "" {
@@ -204,9 +198,7 @@ func (r *Response) Write(w io.Writer) error {
}
protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
statusCode := strconv.Itoa(r.StatusCode) + " "
- if strings.HasPrefix(text, statusCode) {
- text = text[len(statusCode):]
- }
+ text = strings.TrimPrefix(text, statusCode)
io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n")
// Process Body,ContentLength,Close,Trailer
diff --git a/src/pkg/net/http/response_test.go b/src/pkg/net/http/response_test.go
index 6eed4887d..2f5f77369 100644
--- a/src/pkg/net/http/response_test.go
+++ b/src/pkg/net/http/response_test.go
@@ -124,7 +124,7 @@ var respTests = []respTest{
// Chunked response without Content-Length.
{
- "HTTP/1.0 200 OK\r\n" +
+ "HTTP/1.1 200 OK\r\n" +
"Transfer-Encoding: chunked\r\n" +
"\r\n" +
"0a\r\n" +
@@ -137,12 +137,12 @@ var respTests = []respTest{
Response{
Status: "200 OK",
StatusCode: 200,
- Proto: "HTTP/1.0",
+ Proto: "HTTP/1.1",
ProtoMajor: 1,
- ProtoMinor: 0,
+ ProtoMinor: 1,
Request: dummyReq("GET"),
Header: Header{},
- Close: true,
+ Close: false,
ContentLength: -1,
TransferEncoding: []string{"chunked"},
},
@@ -152,24 +152,24 @@ var respTests = []respTest{
// Chunked response with Content-Length.
{
- "HTTP/1.0 200 OK\r\n" +
+ "HTTP/1.1 200 OK\r\n" +
"Transfer-Encoding: chunked\r\n" +
"Content-Length: 10\r\n" +
"\r\n" +
"0a\r\n" +
- "Body here\n" +
+ "Body here\n\r\n" +
"0\r\n" +
"\r\n",
Response{
Status: "200 OK",
StatusCode: 200,
- Proto: "HTTP/1.0",
+ Proto: "HTTP/1.1",
ProtoMajor: 1,
- ProtoMinor: 0,
+ ProtoMinor: 1,
Request: dummyReq("GET"),
Header: Header{},
- Close: true,
+ Close: false,
ContentLength: -1, // TODO(rsc): Fix?
TransferEncoding: []string{"chunked"},
},
@@ -177,23 +177,88 @@ var respTests = []respTest{
"Body here\n",
},
- // Chunked response in response to a HEAD request (the "chunked" should
- // be ignored, as HEAD responses never have bodies)
+ // Chunked response in response to a HEAD request
{
- "HTTP/1.0 200 OK\r\n" +
+ "HTTP/1.1 200 OK\r\n" +
"Transfer-Encoding: chunked\r\n" +
"\r\n",
Response{
- Status: "200 OK",
- StatusCode: 200,
- Proto: "HTTP/1.0",
- ProtoMajor: 1,
- ProtoMinor: 0,
- Request: dummyReq("HEAD"),
- Header: Header{},
- Close: true,
- ContentLength: 0,
+ Status: "200 OK",
+ StatusCode: 200,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Request: dummyReq("HEAD"),
+ Header: Header{},
+ TransferEncoding: []string{"chunked"},
+ Close: false,
+ ContentLength: -1,
+ },
+
+ "",
+ },
+
+ // Content-Length in response to a HEAD request
+ {
+ "HTTP/1.0 200 OK\r\n" +
+ "Content-Length: 256\r\n" +
+ "\r\n",
+
+ Response{
+ Status: "200 OK",
+ StatusCode: 200,
+ Proto: "HTTP/1.0",
+ ProtoMajor: 1,
+ ProtoMinor: 0,
+ Request: dummyReq("HEAD"),
+ Header: Header{"Content-Length": {"256"}},
+ TransferEncoding: nil,
+ Close: true,
+ ContentLength: 256,
+ },
+
+ "",
+ },
+
+ // Content-Length in response to a HEAD request with HTTP/1.1
+ {
+ "HTTP/1.1 200 OK\r\n" +
+ "Content-Length: 256\r\n" +
+ "\r\n",
+
+ Response{
+ Status: "200 OK",
+ StatusCode: 200,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Request: dummyReq("HEAD"),
+ Header: Header{"Content-Length": {"256"}},
+ TransferEncoding: nil,
+ Close: false,
+ ContentLength: 256,
+ },
+
+ "",
+ },
+
+ // No Content-Length or Chunked in response to a HEAD request
+ {
+ "HTTP/1.0 200 OK\r\n" +
+ "\r\n",
+
+ Response{
+ Status: "200 OK",
+ StatusCode: 200,
+ Proto: "HTTP/1.0",
+ ProtoMajor: 1,
+ ProtoMinor: 0,
+ Request: dummyReq("HEAD"),
+ Header: Header{},
+ TransferEncoding: nil,
+ Close: true,
+ ContentLength: -1,
},
"",
@@ -259,16 +324,37 @@ var respTests = []respTest{
"",
},
+
+ // golang.org/issue/4767: don't special-case multipart/byteranges responses
+ {
+ `HTTP/1.1 206 Partial Content
+Connection: close
+Content-Type: multipart/byteranges; boundary=18a75608c8f47cef
+
+some body`,
+ Response{
+ Status: "206 Partial Content",
+ StatusCode: 206,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Request: dummyReq("GET"),
+ Header: Header{
+ "Content-Type": []string{"multipart/byteranges; boundary=18a75608c8f47cef"},
+ },
+ Close: true,
+ ContentLength: -1,
+ },
+
+ "some body",
+ },
}
func TestReadResponse(t *testing.T) {
- for i := range respTests {
- tt := &respTests[i]
- var braw bytes.Buffer
- braw.WriteString(tt.Raw)
- resp, err := ReadResponse(bufio.NewReader(&braw), tt.Resp.Request)
+ for i, tt := range respTests {
+ resp, err := ReadResponse(bufio.NewReader(strings.NewReader(tt.Raw)), tt.Resp.Request)
if err != nil {
- t.Errorf("#%d: %s", i, err)
+ t.Errorf("#%d: %v", i, err)
continue
}
rbody := resp.Body
@@ -276,7 +362,11 @@ func TestReadResponse(t *testing.T) {
diff(t, fmt.Sprintf("#%d Response", i), resp, &tt.Resp)
var bout bytes.Buffer
if rbody != nil {
- io.Copy(&bout, rbody)
+ _, err = io.Copy(&bout, rbody)
+ if err != nil {
+ t.Errorf("#%d: %v", i, err)
+ continue
+ }
rbody.Close()
}
body := bout.String()
@@ -286,6 +376,22 @@ func TestReadResponse(t *testing.T) {
}
}
+func TestWriteResponse(t *testing.T) {
+ for i, tt := range respTests {
+ resp, err := ReadResponse(bufio.NewReader(strings.NewReader(tt.Raw)), tt.Resp.Request)
+ if err != nil {
+ t.Errorf("#%d: %v", i, err)
+ continue
+ }
+ bout := bytes.NewBuffer(nil)
+ err = resp.Write(bout)
+ if err != nil {
+ t.Errorf("#%d: %v", i, err)
+ continue
+ }
+ }
+}
+
var readResponseCloseInMiddleTests = []struct {
chunked, compressed bool
}{
diff --git a/src/pkg/net/http/responsewrite_test.go b/src/pkg/net/http/responsewrite_test.go
index f8e63acf4..5c10e2161 100644
--- a/src/pkg/net/http/responsewrite_test.go
+++ b/src/pkg/net/http/responsewrite_test.go
@@ -15,83 +15,83 @@ type respWriteTest struct {
Raw string
}
-var respWriteTests = []respWriteTest{
- // HTTP/1.0, identity coding; no trailer
- {
- Response{
- StatusCode: 503,
- ProtoMajor: 1,
- ProtoMinor: 0,
- Request: dummyReq("GET"),
- Header: Header{},
- Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),
- ContentLength: 6,
- },
+func TestResponseWrite(t *testing.T) {
+ respWriteTests := []respWriteTest{
+ // HTTP/1.0, identity coding; no trailer
+ {
+ Response{
+ StatusCode: 503,
+ ProtoMajor: 1,
+ ProtoMinor: 0,
+ Request: dummyReq("GET"),
+ Header: Header{},
+ Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),
+ ContentLength: 6,
+ },
- "HTTP/1.0 503 Service Unavailable\r\n" +
- "Content-Length: 6\r\n\r\n" +
- "abcdef",
- },
- // Unchunked response without Content-Length.
- {
- Response{
- StatusCode: 200,
- ProtoMajor: 1,
- ProtoMinor: 0,
- Request: dummyReq("GET"),
- Header: Header{},
- Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),
- ContentLength: -1,
+ "HTTP/1.0 503 Service Unavailable\r\n" +
+ "Content-Length: 6\r\n\r\n" +
+ "abcdef",
},
- "HTTP/1.0 200 OK\r\n" +
- "\r\n" +
- "abcdef",
- },
- // HTTP/1.1, chunked coding; empty trailer; close
- {
- Response{
- StatusCode: 200,
- ProtoMajor: 1,
- ProtoMinor: 1,
- Request: dummyReq("GET"),
- Header: Header{},
- Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),
- ContentLength: 6,
- TransferEncoding: []string{"chunked"},
- Close: true,
+ // Unchunked response without Content-Length.
+ {
+ Response{
+ StatusCode: 200,
+ ProtoMajor: 1,
+ ProtoMinor: 0,
+ Request: dummyReq("GET"),
+ Header: Header{},
+ Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),
+ ContentLength: -1,
+ },
+ "HTTP/1.0 200 OK\r\n" +
+ "\r\n" +
+ "abcdef",
},
+ // HTTP/1.1, chunked coding; empty trailer; close
+ {
+ Response{
+ StatusCode: 200,
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Request: dummyReq("GET"),
+ Header: Header{},
+ Body: ioutil.NopCloser(bytes.NewBufferString("abcdef")),
+ ContentLength: 6,
+ TransferEncoding: []string{"chunked"},
+ Close: true,
+ },
- "HTTP/1.1 200 OK\r\n" +
- "Connection: close\r\n" +
- "Transfer-Encoding: chunked\r\n\r\n" +
- "6\r\nabcdef\r\n0\r\n\r\n",
- },
+ "HTTP/1.1 200 OK\r\n" +
+ "Connection: close\r\n" +
+ "Transfer-Encoding: chunked\r\n\r\n" +
+ "6\r\nabcdef\r\n0\r\n\r\n",
+ },
- // Header value with a newline character (Issue 914).
- // Also tests removal of leading and trailing whitespace.
- {
- Response{
- StatusCode: 204,
- ProtoMajor: 1,
- ProtoMinor: 1,
- Request: dummyReq("GET"),
- Header: Header{
- "Foo": []string{" Bar\nBaz "},
+ // Header value with a newline character (Issue 914).
+ // Also tests removal of leading and trailing whitespace.
+ {
+ Response{
+ StatusCode: 204,
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Request: dummyReq("GET"),
+ Header: Header{
+ "Foo": []string{" Bar\nBaz "},
+ },
+ Body: nil,
+ ContentLength: 0,
+ TransferEncoding: []string{"chunked"},
+ Close: true,
},
- Body: nil,
- ContentLength: 0,
- TransferEncoding: []string{"chunked"},
- Close: true,
- },
- "HTTP/1.1 204 No Content\r\n" +
- "Connection: close\r\n" +
- "Foo: Bar Baz\r\n" +
- "\r\n",
- },
-}
+ "HTTP/1.1 204 No Content\r\n" +
+ "Connection: close\r\n" +
+ "Foo: Bar Baz\r\n" +
+ "\r\n",
+ },
+ }
-func TestResponseWrite(t *testing.T) {
for i := range respWriteTests {
tt := &respWriteTests[i]
var braw bytes.Buffer
diff --git a/src/pkg/net/http/serve_test.go b/src/pkg/net/http/serve_test.go
index b6a6b4c77..3300fef59 100644
--- a/src/pkg/net/http/serve_test.go
+++ b/src/pkg/net/http/serve_test.go
@@ -20,8 +20,13 @@ import (
"net/http/httputil"
"net/url"
"os"
+ "os/exec"
"reflect"
+ "runtime"
+ "strconv"
"strings"
+ "sync"
+ "sync/atomic"
"syscall"
"testing"
"time"
@@ -62,6 +67,7 @@ func (a dummyAddr) String() string {
type testConn struct {
readBuf bytes.Buffer
writeBuf bytes.Buffer
+ closec chan bool // if non-nil, send value to it on close
}
func (c *testConn) Read(b []byte) (int, error) {
@@ -73,6 +79,10 @@ func (c *testConn) Write(b []byte) (int, error) {
}
func (c *testConn) Close() error {
+ select {
+ case c.closec <- true:
+ default:
+ }
return nil
}
@@ -168,13 +178,18 @@ var vtests = []struct {
{"http://someHost.com/someDir/apage", "someHost.com/someDir"},
{"http://otherHost.com/someDir/apage", "someDir"},
{"http://otherHost.com/aDir/apage", "Default"},
+ // redirections for trees
+ {"http://localhost/someDir", "/someDir/"},
+ {"http://someHost.com/someDir", "/someDir/"},
}
func TestHostHandlers(t *testing.T) {
+ defer checkLeakedTransports(t)
+ mux := NewServeMux()
for _, h := range handlers {
- Handle(h.pattern, stringHandler(h.msg))
+ mux.Handle(h.pattern, stringHandler(h.msg))
}
- ts := httptest.NewServer(nil)
+ ts := httptest.NewServer(mux)
defer ts.Close()
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
@@ -199,9 +214,19 @@ func TestHostHandlers(t *testing.T) {
t.Errorf("reading response: %v", err)
continue
}
- s := r.Header.Get("Result")
- if s != vt.expected {
- t.Errorf("Get(%q) = %q, want %q", vt.url, s, vt.expected)
+ switch r.StatusCode {
+ case StatusOK:
+ s := r.Header.Get("Result")
+ if s != vt.expected {
+ t.Errorf("Get(%q) = %q, want %q", vt.url, s, vt.expected)
+ }
+ case StatusMovedPermanently:
+ s := r.Header.Get("Location")
+ if s != vt.expected {
+ t.Errorf("Get(%q) = %q, want %q", vt.url, s, vt.expected)
+ }
+ default:
+ t.Errorf("Get(%q) unhandled status code %d", vt.url, r.StatusCode)
}
}
}
@@ -232,28 +257,22 @@ func TestMuxRedirectLeadingSlashes(t *testing.T) {
}
func TestServerTimeouts(t *testing.T) {
- // TODO(bradfitz): convert this to use httptest.Server
- l, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Fatalf("listen error: %v", err)
- }
- addr, _ := l.Addr().(*net.TCPAddr)
-
+ defer checkLeakedTransports(t)
reqNum := 0
- handler := HandlerFunc(func(res ResponseWriter, req *Request) {
+ ts := httptest.NewUnstartedServer(HandlerFunc(func(res ResponseWriter, req *Request) {
reqNum++
fmt.Fprintf(res, "req=%d", reqNum)
- })
-
- server := &Server{Handler: handler, ReadTimeout: 250 * time.Millisecond, WriteTimeout: 250 * time.Millisecond}
- go server.Serve(l)
-
- url := fmt.Sprintf("http://%s/", addr)
+ }))
+ ts.Config.ReadTimeout = 250 * time.Millisecond
+ ts.Config.WriteTimeout = 250 * time.Millisecond
+ ts.Start()
+ defer ts.Close()
// Hit the HTTP server successfully.
tr := &Transport{DisableKeepAlives: true} // they interfere with this test
+ defer tr.CloseIdleConnections()
c := &Client{Transport: tr}
- r, err := c.Get(url)
+ r, err := c.Get(ts.URL)
if err != nil {
t.Fatalf("http Get #1: %v", err)
}
@@ -266,13 +285,13 @@ func TestServerTimeouts(t *testing.T) {
// Slow client that should timeout.
t1 := time.Now()
- conn, err := net.Dial("tcp", addr.String())
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
if err != nil {
t.Fatalf("Dial: %v", err)
}
buf := make([]byte, 1)
n, err := conn.Read(buf)
- latency := time.Now().Sub(t1)
+ latency := time.Since(t1)
if n != 0 || err != io.EOF {
t.Errorf("Read = %v, %v, wanted %v, %v", n, err, 0, io.EOF)
}
@@ -283,7 +302,7 @@ func TestServerTimeouts(t *testing.T) {
// Hit the HTTP server successfully again, verifying that the
// previous slow connection didn't run our handler. (that we
// get "req=2", not "req=3")
- r, err = Get(url)
+ r, err = Get(ts.URL)
if err != nil {
t.Fatalf("http Get #2: %v", err)
}
@@ -293,11 +312,87 @@ func TestServerTimeouts(t *testing.T) {
t.Errorf("Get #2 got %q, want %q", string(got), expected)
}
- l.Close()
+ if !testing.Short() {
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ defer conn.Close()
+ go io.Copy(ioutil.Discard, conn)
+ for i := 0; i < 5; i++ {
+ _, err := conn.Write([]byte("GET / HTTP/1.1\r\nHost: foo\r\n\r\n"))
+ if err != nil {
+ t.Fatalf("on write %d: %v", i, err)
+ }
+ time.Sleep(ts.Config.ReadTimeout / 2)
+ }
+ }
+}
+
+// golang.org/issue/4741 -- setting only a write timeout that triggers
+// shouldn't cause a handler to block forever on reads (next HTTP
+// request) that will never happen.
+func TestOnlyWriteTimeout(t *testing.T) {
+ defer checkLeakedTransports(t)
+ var conn net.Conn
+ var afterTimeoutErrc = make(chan error, 1)
+ ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, req *Request) {
+ buf := make([]byte, 512<<10)
+ _, err := w.Write(buf)
+ if err != nil {
+ t.Errorf("handler Write error: %v", err)
+ return
+ }
+ conn.SetWriteDeadline(time.Now().Add(-30 * time.Second))
+ _, err = w.Write(buf)
+ afterTimeoutErrc <- err
+ }))
+ ts.Listener = trackLastConnListener{ts.Listener, &conn}
+ ts.Start()
+ defer ts.Close()
+
+ tr := &Transport{DisableKeepAlives: false}
+ defer tr.CloseIdleConnections()
+ c := &Client{Transport: tr}
+
+ errc := make(chan error)
+ go func() {
+ res, err := c.Get(ts.URL)
+ if err != nil {
+ errc <- err
+ return
+ }
+ _, err = io.Copy(ioutil.Discard, res.Body)
+ errc <- err
+ }()
+ select {
+ case err := <-errc:
+ if err == nil {
+ t.Errorf("expected an error from Get request")
+ }
+ case <-time.After(5 * time.Second):
+ t.Fatal("timeout waiting for Get error")
+ }
+ if err := <-afterTimeoutErrc; err == nil {
+ t.Error("expected write error after timeout")
+ }
+}
+
+// trackLastConnListener tracks the last net.Conn that was accepted.
+type trackLastConnListener struct {
+ net.Listener
+ last *net.Conn // destination
}
-// TestIdentityResponse verifies that a handler can unset
+func (l trackLastConnListener) Accept() (c net.Conn, err error) {
+ c, err = l.Listener.Accept()
+ *l.last = c
+ return
+}
+
+// TestIdentityResponse verifies that a handler can unset
func TestIdentityResponse(t *testing.T) {
+ defer checkLeakedTransports(t)
handler := HandlerFunc(func(rw ResponseWriter, req *Request) {
rw.Header().Set("Content-Length", "3")
rw.Header().Set("Transfer-Encoding", req.FormValue("te"))
@@ -343,10 +438,12 @@ func TestIdentityResponse(t *testing.T) {
// Verify that ErrContentLength is returned
url := ts.URL + "/?overwrite=1"
- _, err := Get(url)
+ res, err := Get(url)
if err != nil {
t.Fatalf("error with Get of %s: %v", url, err)
}
+ res.Body.Close()
+
// Verify that the connection is closed when the declared Content-Length
// is larger than what the handler wrote.
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
@@ -370,7 +467,8 @@ func TestIdentityResponse(t *testing.T) {
})
}
-func testTcpConnectionCloses(t *testing.T, req string, h Handler) {
+func testTCPConnectionCloses(t *testing.T, req string, h Handler) {
+ defer checkLeakedTransports(t)
s := httptest.NewServer(h)
defer s.Close()
@@ -386,17 +484,18 @@ func testTcpConnectionCloses(t *testing.T, req string, h Handler) {
}
r := bufio.NewReader(conn)
- _, err = ReadResponse(r, &Request{Method: "GET"})
+ res, err := ReadResponse(r, &Request{Method: "GET"})
if err != nil {
t.Fatal("ReadResponse error:", err)
}
- success := make(chan bool)
+ didReadAll := make(chan bool, 1)
go func() {
select {
case <-time.After(5 * time.Second):
- t.Fatal("body not closed after 5s")
- case <-success:
+ t.Error("body not closed after 5s")
+ return
+ case <-didReadAll:
}
}()
@@ -404,32 +503,43 @@ func testTcpConnectionCloses(t *testing.T, req string, h Handler) {
if err != nil {
t.Fatal("read error:", err)
}
+ didReadAll <- true
- success <- true
+ if !res.Close {
+ t.Errorf("Response.Close = false; want true")
+ }
}
// TestServeHTTP10Close verifies that HTTP/1.0 requests won't be kept alive.
func TestServeHTTP10Close(t *testing.T) {
- testTcpConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+ testTCPConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
ServeFile(w, r, "testdata/file")
}))
}
+// TestClientCanClose verifies that clients can also force a connection to close.
+func TestClientCanClose(t *testing.T) {
+ testTCPConnectionCloses(t, "GET / HTTP/1.1\r\nConnection: close\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+ // Nothing.
+ }))
+}
+
// TestHandlersCanSetConnectionClose verifies that handlers can force a connection to close,
// even for HTTP/1.1 requests.
func TestHandlersCanSetConnectionClose11(t *testing.T) {
- testTcpConnectionCloses(t, "GET / HTTP/1.1\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+ testTCPConnectionCloses(t, "GET / HTTP/1.1\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("Connection", "close")
}))
}
func TestHandlersCanSetConnectionClose10(t *testing.T) {
- testTcpConnectionCloses(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+ testTCPConnectionCloses(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("Connection", "close")
}))
}
func TestSetsRemoteAddr(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
fmt.Fprintf(w, "%s", r.RemoteAddr)
}))
@@ -450,11 +560,13 @@ func TestSetsRemoteAddr(t *testing.T) {
}
func TestChunkedResponseHeaders(t *testing.T) {
+ defer checkLeakedTransports(t)
log.SetOutput(ioutil.Discard) // is noisy otherwise
defer log.SetOutput(os.Stderr)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("Content-Length", "intentional gibberish") // we check that this is deleted
+ w.(Flusher).Flush()
fmt.Fprintf(w, "I am a chunked response.")
}))
defer ts.Close()
@@ -463,6 +575,7 @@ func TestChunkedResponseHeaders(t *testing.T) {
if err != nil {
t.Fatalf("Get error: %v", err)
}
+ defer res.Body.Close()
if g, e := res.ContentLength, int64(-1); g != e {
t.Errorf("expected ContentLength of %d; got %d", e, g)
}
@@ -478,6 +591,7 @@ func TestChunkedResponseHeaders(t *testing.T) {
// chunking in their response headers and aren't allowed to produce
// output.
func Test304Responses(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
w.WriteHeader(StatusNotModified)
_, err := w.Write([]byte("illegal body"))
@@ -507,6 +621,7 @@ func Test304Responses(t *testing.T) {
// allowed to produce output, and don't set a Content-Type since
// the real type of the body data cannot be inferred.
func TestHeadResponses(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
_, err := w.Write([]byte("Ignored body"))
if err != ErrBodyNotAllowed {
@@ -541,6 +656,7 @@ func TestHeadResponses(t *testing.T) {
}
func TestTLSHandshakeTimeout(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
ts.Config.ReadTimeout = 250 * time.Millisecond
ts.StartTLS()
@@ -560,6 +676,7 @@ func TestTLSHandshakeTimeout(t *testing.T) {
}
func TestTLSServer(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
if r.TLS != nil {
w.Header().Set("X-TLS-Set", "true")
@@ -642,6 +759,7 @@ var serverExpectTests = []serverExpectTest{
// Tests that the server responds to the "Expect" request header
// correctly.
func TestServerExpect(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
// Note using r.FormValue("readbody") because for POST
// requests that would read from r.Body, which we only
@@ -661,30 +779,51 @@ func TestServerExpect(t *testing.T) {
t.Fatalf("Dial: %v", err)
}
defer conn.Close()
- sendf := func(format string, args ...interface{}) {
- _, err := fmt.Fprintf(conn, format, args...)
- if err != nil {
- t.Fatalf("On test %#v, error writing %q: %v", test, format, err)
- }
- }
+
+ // Only send the body immediately if we're acting like an HTTP client
+ // that doesn't send 100-continue expectations.
+ writeBody := test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue"
+
go func() {
- sendf("POST /?readbody=%v HTTP/1.1\r\n"+
+ _, err := fmt.Fprintf(conn, "POST /?readbody=%v HTTP/1.1\r\n"+
"Connection: close\r\n"+
"Content-Length: %d\r\n"+
"Expect: %s\r\nHost: foo\r\n\r\n",
test.readBody, test.contentLength, test.expectation)
- if test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue" {
+ if err != nil {
+ t.Errorf("On test %#v, error writing request headers: %v", test, err)
+ return
+ }
+ if writeBody {
body := strings.Repeat("A", test.contentLength)
- sendf(body)
+ _, err = fmt.Fprint(conn, body)
+ if err != nil {
+ if !test.readBody {
+ // Server likely already hung up on us.
+ // See larger comment below.
+ t.Logf("On test %#v, acceptable error writing request body: %v", test, err)
+ return
+ }
+ t.Errorf("On test %#v, error writing request body: %v", test, err)
+ }
}
}()
bufr := bufio.NewReader(conn)
line, err := bufr.ReadString('\n')
if err != nil {
- t.Fatalf("ReadString: %v", err)
+ if writeBody && !test.readBody {
+ // This is an acceptable failure due to a possible TCP race:
+ // We were still writing data and the server hung up on us. A TCP
+ // implementation may send a RST if our request body data was known
+ // to be lost, which may trigger our reads to fail.
+ // See RFC 1122 page 88.
+ t.Logf("On test %#v, acceptable error from ReadString: %v", test, err)
+ return
+ }
+ t.Fatalf("On test %#v, ReadString: %v", test, err)
}
if !strings.Contains(line, test.expectedResponse) {
- t.Errorf("for test %#v got first line=%q", test, line)
+ t.Errorf("On test %#v, got first line = %q; want %q", test, line, test.expectedResponse)
}
}
@@ -714,6 +853,7 @@ func TestServerUnreadRequestBodyLittle(t *testing.T) {
t.Errorf("on request, read buffer length is %d; expected about 100 KB", conn.readBuf.Len())
}
rw.WriteHeader(200)
+ rw.(Flusher).Flush()
if g, e := conn.readBuf.Len(), 0; g != e {
t.Errorf("after WriteHeader, read buffer length is %d; want %d", g, e)
}
@@ -736,27 +876,28 @@ func TestServerUnreadRequestBodyLarge(t *testing.T) {
"Content-Length: %d\r\n"+
"\r\n", len(body))))
conn.readBuf.Write([]byte(body))
-
- done := make(chan bool)
+ conn.closec = make(chan bool, 1)
ls := &oneConnListener{conn}
go Serve(ls, HandlerFunc(func(rw ResponseWriter, req *Request) {
- defer close(done)
if conn.readBuf.Len() < len(body)/2 {
t.Errorf("on request, read buffer length is %d; expected about 1MB", conn.readBuf.Len())
}
rw.WriteHeader(200)
+ rw.(Flusher).Flush()
if conn.readBuf.Len() < len(body)/2 {
t.Errorf("post-WriteHeader, read buffer length is %d; expected about 1MB", conn.readBuf.Len())
}
- if c := rw.Header().Get("Connection"); c != "close" {
- t.Errorf(`Connection header = %q; want "close"`, c)
- }
}))
- <-done
+ <-conn.closec
+
+ if res := conn.writeBuf.String(); !strings.Contains(res, "Connection: close") {
+ t.Errorf("Expected a Connection: close header; got response: %s", res)
+ }
}
func TestTimeoutHandler(t *testing.T) {
+ defer checkLeakedTransports(t)
sendHi := make(chan bool, 1)
writeErrors := make(chan error, 1)
sayHi := HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -831,6 +972,7 @@ func TestRedirectMunging(t *testing.T) {
// the previous request's body, which is not optimal for zero-lengthed bodies,
// as the client would then see http.ErrBodyReadAfterClose and not 0, io.EOF.
func TestZeroLengthPostAndResponse(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
all, err := ioutil.ReadAll(r.Body)
if err != nil {
@@ -868,15 +1010,20 @@ func TestZeroLengthPostAndResponse(t *testing.T) {
}
}
+func TestHandlerPanicNil(t *testing.T) {
+ testHandlerPanic(t, false, nil)
+}
+
func TestHandlerPanic(t *testing.T) {
- testHandlerPanic(t, false)
+ testHandlerPanic(t, false, "intentional death for testing")
}
func TestHandlerPanicWithHijack(t *testing.T) {
- testHandlerPanic(t, true)
+ testHandlerPanic(t, true, "intentional death for testing")
}
-func testHandlerPanic(t *testing.T, withHijack bool) {
+func testHandlerPanic(t *testing.T, withHijack bool, panicValue interface{}) {
+ defer checkLeakedTransports(t)
// Unlike the other tests that set the log output to ioutil.Discard
// to quiet the output, this test uses a pipe. The pipe serves three
// purposes:
@@ -896,6 +1043,7 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
pr, pw := io.Pipe()
log.SetOutput(pw)
defer log.SetOutput(os.Stderr)
+ defer pw.Close()
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
if withHijack {
@@ -905,7 +1053,7 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
}
defer rwc.Close()
}
- panic("intentional death for testing")
+ panic(panicValue)
}))
defer ts.Close()
@@ -917,8 +1065,8 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
buf := make([]byte, 4<<10)
_, err := pr.Read(buf)
pr.Close()
- if err != nil {
- t.Fatal(err)
+ if err != nil && err != io.EOF {
+ t.Error(err)
}
done <- true
}()
@@ -928,6 +1076,10 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
t.Logf("expected an error")
}
+ if panicValue == nil {
+ return
+ }
+
select {
case <-done:
return
@@ -937,6 +1089,7 @@ func testHandlerPanic(t *testing.T, withHijack bool) {
}
func TestNoDate(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header()["Date"] = nil
}))
@@ -952,6 +1105,7 @@ func TestNoDate(t *testing.T) {
}
func TestStripPrefix(t *testing.T) {
+ defer checkLeakedTransports(t)
h := HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("X-Path", r.URL.Path)
})
@@ -965,6 +1119,7 @@ func TestStripPrefix(t *testing.T) {
if g, e := res.Header.Get("X-Path"), "/bar"; g != e {
t.Errorf("test 1: got %s, want %s", g, e)
}
+ res.Body.Close()
res, err = Get(ts.URL + "/bar")
if err != nil {
@@ -973,9 +1128,11 @@ func TestStripPrefix(t *testing.T) {
if g, e := res.StatusCode, 404; g != e {
t.Errorf("test 2: got status %v, want %v", g, e)
}
+ res.Body.Close()
}
func TestRequestLimit(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
t.Fatalf("didn't expect to get request in Handler")
}))
@@ -992,6 +1149,7 @@ func TestRequestLimit(t *testing.T) {
// we do support it (at least currently), so we expect a response below.
t.Fatalf("Do: %v", err)
}
+ defer res.Body.Close()
if res.StatusCode != 413 {
t.Fatalf("expected 413 response status; got: %d %s", res.StatusCode, res.Status)
}
@@ -1013,11 +1171,12 @@ type countReader struct {
func (cr countReader) Read(p []byte) (n int, err error) {
n, err = cr.r.Read(p)
- *cr.n += int64(n)
+ atomic.AddInt64(cr.n, int64(n))
return
}
func TestRequestBodyLimit(t *testing.T) {
+ defer checkLeakedTransports(t)
const limit = 1 << 20
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
r.Body = MaxBytesReader(w, r.Body, limit)
@@ -1031,8 +1190,8 @@ func TestRequestBodyLimit(t *testing.T) {
}))
defer ts.Close()
- nWritten := int64(0)
- req, _ := NewRequest("POST", ts.URL, io.LimitReader(countReader{neverEnding('a'), &nWritten}, limit*200))
+ nWritten := new(int64)
+ req, _ := NewRequest("POST", ts.URL, io.LimitReader(countReader{neverEnding('a'), nWritten}, limit*200))
// Send the POST, but don't care it succeeds or not. The
// remote side is going to reply and then close the TCP
@@ -1045,7 +1204,7 @@ func TestRequestBodyLimit(t *testing.T) {
// the remote side hung up on us before we wrote too much.
_, _ = DefaultClient.Do(req)
- if nWritten > limit*100 {
+ if atomic.LoadInt64(nWritten) > limit*100 {
t.Errorf("handler restricted the request body to %d bytes, but client managed to write %d",
limit, nWritten)
}
@@ -1054,6 +1213,7 @@ func TestRequestBodyLimit(t *testing.T) {
// TestClientWriteShutdown tests that if the client shuts down the write
// side of their TCP connection, the server doesn't send a 400 Bad Request.
func TestClientWriteShutdown(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
defer ts.Close()
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
@@ -1086,28 +1246,207 @@ func TestClientWriteShutdown(t *testing.T) {
// Tests that chunked server responses that write 1 byte at a time are
// buffered before chunk headers are added, not after chunk headers.
func TestServerBufferedChunking(t *testing.T) {
- if true {
- t.Logf("Skipping known broken test; see Issue 2357")
- return
- }
conn := new(testConn)
conn.readBuf.Write([]byte("GET / HTTP/1.1\r\n\r\n"))
- done := make(chan bool)
+ conn.closec = make(chan bool, 1)
ls := &oneConnListener{conn}
go Serve(ls, HandlerFunc(func(rw ResponseWriter, req *Request) {
- defer close(done)
- rw.Header().Set("Content-Type", "text/plain") // prevent sniffing, which buffers
+ rw.(Flusher).Flush() // force the Header to be sent, in chunking mode, not counting the length
rw.Write([]byte{'x'})
rw.Write([]byte{'y'})
rw.Write([]byte{'z'})
}))
- <-done
+ <-conn.closec
if !bytes.HasSuffix(conn.writeBuf.Bytes(), []byte("\r\n\r\n3\r\nxyz\r\n0\r\n\r\n")) {
t.Errorf("response didn't end with a single 3 byte 'xyz' chunk; got:\n%q",
conn.writeBuf.Bytes())
}
}
+// Tests that the server flushes its response headers out when it's
+// ignoring the response body and waits a bit before forcefully
+// closing the TCP connection, causing the client to get a RST.
+// See http://golang.org/issue/3595
+func TestServerGracefulClose(t *testing.T) {
+ defer checkLeakedTransports(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ Error(w, "bye", StatusUnauthorized)
+ }))
+ defer ts.Close()
+
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conn.Close()
+ const bodySize = 5 << 20
+ req := []byte(fmt.Sprintf("POST / HTTP/1.1\r\nHost: foo.com\r\nContent-Length: %d\r\n\r\n", bodySize))
+ for i := 0; i < bodySize; i++ {
+ req = append(req, 'x')
+ }
+ writeErr := make(chan error)
+ go func() {
+ _, err := conn.Write(req)
+ writeErr <- err
+ }()
+ br := bufio.NewReader(conn)
+ lineNum := 0
+ for {
+ line, err := br.ReadString('\n')
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatalf("ReadLine: %v", err)
+ }
+ lineNum++
+ if lineNum == 1 && !strings.Contains(line, "401 Unauthorized") {
+ t.Errorf("Response line = %q; want a 401", line)
+ }
+ }
+ // Wait for write to finish. This is a broken pipe on both
+ // Darwin and Linux, but checking this isn't the point of
+ // the test.
+ <-writeErr
+}
+
+func TestCaseSensitiveMethod(t *testing.T) {
+ defer checkLeakedTransports(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ if r.Method != "get" {
+ t.Errorf(`Got method %q; want "get"`, r.Method)
+ }
+ }))
+ defer ts.Close()
+ req, _ := NewRequest("get", ts.URL, nil)
+ res, err := DefaultClient.Do(req)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ res.Body.Close()
+}
+
+// TestContentLengthZero tests that for both an HTTP/1.0 and HTTP/1.1
+// request (both keep-alive), when a Handler never writes any
+// response, the net/http package adds a "Content-Length: 0" response
+// header.
+func TestContentLengthZero(t *testing.T) {
+ ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {}))
+ defer ts.Close()
+
+ for _, version := range []string{"HTTP/1.0", "HTTP/1.1"} {
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatalf("error dialing: %v", err)
+ }
+ _, err = fmt.Fprintf(conn, "GET / %v\r\nConnection: keep-alive\r\nHost: foo\r\n\r\n", version)
+ if err != nil {
+ t.Fatalf("error writing: %v", err)
+ }
+ req, _ := NewRequest("GET", "/", nil)
+ res, err := ReadResponse(bufio.NewReader(conn), req)
+ if err != nil {
+ t.Fatalf("error reading response: %v", err)
+ }
+ if te := res.TransferEncoding; len(te) > 0 {
+ t.Errorf("For version %q, Transfer-Encoding = %q; want none", version, te)
+ }
+ if cl := res.ContentLength; cl != 0 {
+ t.Errorf("For version %q, Content-Length = %v; want 0", version, cl)
+ }
+ conn.Close()
+ }
+}
+
+func TestCloseNotifier(t *testing.T) {
+ gotReq := make(chan bool, 1)
+ sawClose := make(chan bool, 1)
+ ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
+ gotReq <- true
+ cc := rw.(CloseNotifier).CloseNotify()
+ <-cc
+ sawClose <- true
+ }))
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatalf("error dialing: %v", err)
+ }
+ diec := make(chan bool)
+ go func() {
+ _, err = fmt.Fprintf(conn, "GET / HTTP/1.1\r\nConnection: keep-alive\r\nHost: foo\r\n\r\n")
+ if err != nil {
+ t.Fatal(err)
+ }
+ <-diec
+ conn.Close()
+ }()
+For:
+ for {
+ select {
+ case <-gotReq:
+ diec <- true
+ case <-sawClose:
+ break For
+ case <-time.After(5 * time.Second):
+ t.Fatal("timeout")
+ }
+ }
+ ts.Close()
+}
+
+func TestOptions(t *testing.T) {
+ uric := make(chan string, 2) // only expect 1, but leave space for 2
+ mux := NewServeMux()
+ mux.HandleFunc("/", func(w ResponseWriter, r *Request) {
+ uric <- r.RequestURI
+ })
+ ts := httptest.NewServer(mux)
+ defer ts.Close()
+
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conn.Close()
+
+ // An OPTIONS * request should succeed.
+ _, err = conn.Write([]byte("OPTIONS * HTTP/1.1\r\nHost: foo.com\r\n\r\n"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ br := bufio.NewReader(conn)
+ res, err := ReadResponse(br, &Request{Method: "OPTIONS"})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if res.StatusCode != 200 {
+ t.Errorf("Got non-200 response to OPTIONS *: %#v", res)
+ }
+
+ // A GET * request on a ServeMux should fail.
+ _, err = conn.Write([]byte("GET * HTTP/1.1\r\nHost: foo.com\r\n\r\n"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ res, err = ReadResponse(br, &Request{Method: "GET"})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if res.StatusCode != 400 {
+ t.Errorf("Got non-400 response to GET *: %#v", res)
+ }
+
+ res, err = Get(ts.URL + "/second")
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+ if got := <-uric; got != "/second" {
+ t.Errorf("Handler saw request for %q; want /second", got)
+ }
+}
+
// goTimeout runs f, failing t if f takes more than ns to complete.
func goTimeout(t *testing.T, d time.Duration, f func()) {
ch := make(chan bool, 2)
@@ -1184,3 +1523,100 @@ func BenchmarkClientServer(b *testing.B) {
b.StopTimer()
}
+
+func BenchmarkClientServerParallel4(b *testing.B) {
+ benchmarkClientServerParallel(b, 4)
+}
+
+func BenchmarkClientServerParallel64(b *testing.B) {
+ benchmarkClientServerParallel(b, 64)
+}
+
+func benchmarkClientServerParallel(b *testing.B, conc int) {
+ b.StopTimer()
+ ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
+ fmt.Fprintf(rw, "Hello world.\n")
+ }))
+ defer ts.Close()
+ b.StartTimer()
+
+ numProcs := runtime.GOMAXPROCS(-1) * conc
+ var wg sync.WaitGroup
+ wg.Add(numProcs)
+ n := int32(b.N)
+ for p := 0; p < numProcs; p++ {
+ go func() {
+ for atomic.AddInt32(&n, -1) >= 0 {
+ res, err := Get(ts.URL)
+ if err != nil {
+ b.Logf("Get: %v", err)
+ continue
+ }
+ all, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ b.Logf("ReadAll: %v", err)
+ continue
+ }
+ body := string(all)
+ if body != "Hello world.\n" {
+ panic("Got body: " + body)
+ }
+ }
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
+// A benchmark for profiling the server without the HTTP client code.
+// The client code runs in a subprocess.
+//
+// For use like:
+// $ go test -c
+// $ ./http.test -test.run=XX -test.bench=BenchmarkServer -test.benchtime=15s -test.cpuprofile=http.prof
+// $ go tool pprof http.test http.prof
+// (pprof) web
+func BenchmarkServer(b *testing.B) {
+ // Child process mode;
+ if url := os.Getenv("TEST_BENCH_SERVER_URL"); url != "" {
+ n, err := strconv.Atoi(os.Getenv("TEST_BENCH_CLIENT_N"))
+ if err != nil {
+ panic(err)
+ }
+ for i := 0; i < n; i++ {
+ res, err := Get(url)
+ if err != nil {
+ log.Panicf("Get: %v", err)
+ }
+ all, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ log.Panicf("ReadAll: %v", err)
+ }
+ body := string(all)
+ if body != "Hello world.\n" {
+ log.Panicf("Got body: %q", body)
+ }
+ }
+ os.Exit(0)
+ return
+ }
+
+ var res = []byte("Hello world.\n")
+ b.StopTimer()
+ ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
+ rw.Header().Set("Content-Type", "text/html; charset=utf-8")
+ rw.Write(res)
+ }))
+ defer ts.Close()
+ b.StartTimer()
+
+ cmd := exec.Command(os.Args[0], "-test.run=XXXX", "-test.bench=BenchmarkServer")
+ cmd.Env = append([]string{
+ fmt.Sprintf("TEST_BENCH_CLIENT_N=%d", b.N),
+ fmt.Sprintf("TEST_BENCH_SERVER_URL=%s", ts.URL),
+ }, os.Environ()...)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ b.Errorf("Test failure: %v, with output: %s", err, out)
+ }
+}
diff --git a/src/pkg/net/http/server.go b/src/pkg/net/http/server.go
index 0572b4ae3..b6ab78228 100644
--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -11,7 +11,6 @@ package http
import (
"bufio"
- "bytes"
"crypto/tls"
"errors"
"fmt"
@@ -21,7 +20,7 @@ import (
"net"
"net/url"
"path"
- "runtime/debug"
+ "runtime"
"strconv"
"strings"
"sync"
@@ -94,30 +93,188 @@ type Hijacker interface {
Hijack() (net.Conn, *bufio.ReadWriter, error)
}
+// The CloseNotifier interface is implemented by ResponseWriters which
+// allow detecting when the underlying connection has gone away.
+//
+// This mechanism can be used to cancel long operations on the server
+// if the client has disconnected before the response is ready.
+type CloseNotifier interface {
+ // CloseNotify returns a channel that receives a single value
+ // when the client connection has gone away.
+ CloseNotify() <-chan bool
+}
+
// A conn represents the server side of an HTTP connection.
type conn struct {
remoteAddr string // network address of remote side
server *Server // the Server on which the connection arrived
rwc net.Conn // i/o connection
- lr *io.LimitedReader // io.LimitReader(rwc)
- buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->rwc
- hijacked bool // connection has been hijacked by handler
+ sr switchReader // where the LimitReader reads from; usually the rwc
+ lr *io.LimitedReader // io.LimitReader(sr)
+ buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc
tlsState *tls.ConnectionState // or nil when not using TLS
- body []byte
+
+ mu sync.Mutex // guards the following
+ clientGone bool // if client has disconnected mid-request
+ closeNotifyc chan bool // made lazily
+ hijackedv bool // connection has been hijacked by handler
+}
+
+func (c *conn) hijacked() bool {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ return c.hijackedv
+}
+
+func (c *conn) hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.hijackedv {
+ return nil, nil, ErrHijacked
+ }
+ if c.closeNotifyc != nil {
+ return nil, nil, errors.New("http: Hijack is incompatible with use of CloseNotifier")
+ }
+ c.hijackedv = true
+ rwc = c.rwc
+ buf = c.buf
+ c.rwc = nil
+ c.buf = nil
+ return
+}
+
+func (c *conn) closeNotify() <-chan bool {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.closeNotifyc == nil {
+ c.closeNotifyc = make(chan bool)
+ if c.hijackedv {
+ // to obey the function signature, even though
+ // it'll never receive a value.
+ return c.closeNotifyc
+ }
+ pr, pw := io.Pipe()
+
+ readSource := c.sr.r
+ c.sr.Lock()
+ c.sr.r = pr
+ c.sr.Unlock()
+ go func() {
+ _, err := io.Copy(pw, readSource)
+ if err == nil {
+ err = io.EOF
+ }
+ pw.CloseWithError(err)
+ c.noteClientGone()
+ }()
+ }
+ return c.closeNotifyc
+}
+
+func (c *conn) noteClientGone() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.closeNotifyc != nil && !c.clientGone {
+ c.closeNotifyc <- true
+ }
+ c.clientGone = true
+}
+
+type switchReader struct {
+ sync.Mutex
+ r io.Reader
+}
+
+func (sr *switchReader) Read(p []byte) (n int, err error) {
+ sr.Lock()
+ r := sr.r
+ sr.Unlock()
+ return r.Read(p)
+}
+
+// This should be >= 512 bytes for DetectContentType,
+// but otherwise it's somewhat arbitrary.
+const bufferBeforeChunkingSize = 2048
+
+// chunkWriter writes to a response's conn buffer, and is the writer
+// wrapped by the response.bufw buffered writer.
+//
+// chunkWriter also is responsible for finalizing the Header, including
+// conditionally setting the Content-Type and setting a Content-Length
+// in cases where the handler's final output is smaller than the buffer
+// size. It also conditionally adds chunk headers, when in chunking mode.
+//
+// See the comment above (*response).Write for the entire write flow.
+type chunkWriter struct {
+ res *response
+ header Header // a deep copy of r.Header, once WriteHeader is called
+ wroteHeader bool // whether the header's been sent
+
+ // set by the writeHeader method:
+ chunking bool // using chunked transfer encoding for reply body
+}
+
+var crlf = []byte("\r\n")
+
+func (cw *chunkWriter) Write(p []byte) (n int, err error) {
+ if !cw.wroteHeader {
+ cw.writeHeader(p)
+ }
+ if cw.chunking {
+ _, err = fmt.Fprintf(cw.res.conn.buf, "%x\r\n", len(p))
+ if err != nil {
+ cw.res.conn.rwc.Close()
+ return
+ }
+ }
+ n, err = cw.res.conn.buf.Write(p)
+ if cw.chunking && err == nil {
+ _, err = cw.res.conn.buf.Write(crlf)
+ }
+ if err != nil {
+ cw.res.conn.rwc.Close()
+ }
+ return
+}
+
+func (cw *chunkWriter) flush() {
+ if !cw.wroteHeader {
+ cw.writeHeader(nil)
+ }
+ cw.res.conn.buf.Flush()
+}
+
+func (cw *chunkWriter) close() {
+ if !cw.wroteHeader {
+ cw.writeHeader(nil)
+ }
+ if cw.chunking {
+ // zero EOF chunk, trailer key/value pairs (currently
+ // unsupported in Go's server), followed by a blank
+ // line.
+ io.WriteString(cw.res.conn.buf, "0\r\n\r\n")
+ }
}
// A response represents the server side of an HTTP response.
type response struct {
conn *conn
req *Request // request for this response
- chunking bool // using chunked transfer encoding for reply body
- wroteHeader bool // reply header has been written
+ wroteHeader bool // reply header has been (logically) written
wroteContinue bool // 100 Continue response was written
- header Header // reply header parameters
- written int64 // number of bytes written in body
- contentLength int64 // explicitly-declared Content-Length; or -1
- status int // status code passed to WriteHeader
- needSniff bool // need to sniff to find Content-Type
+
+ w *bufio.Writer // buffers output in chunks to chunkWriter
+ cw *chunkWriter
+
+ // handlerHeader is the Header that Handlers get access to,
+ // which may be retained and mutated even after WriteHeader.
+ // handlerHeader is copied into cw.header at WriteHeader
+ // time, and privately mutated thereafter.
+ handlerHeader Header
+
+ written int64 // number of bytes written in body
+ contentLength int64 // explicitly-declared Content-Length; or -1
+ status int // status code passed to WriteHeader
// close connection after this reply. set on request and
// updated after response from handler if there's a
@@ -127,12 +284,14 @@ type response struct {
// requestBodyLimitHit is set by requestTooLarge when
// maxBytesReader hits its max size. It is checked in
- // WriteHeader, to make sure we don't consume the the
+ // WriteHeader, to make sure we don't consume the
// remaining request body to try to advance to the next HTTP
- // request. Instead, when this is set, we stop doing
+ // request. Instead, when this is set, we stop reading
// subsequent requests on this connection and stop reading
// input from it.
requestBodyLimitHit bool
+
+ handlerDone bool // set true when the handler exits
}
// requestTooLarge is called by maxBytesReader when too much input has
@@ -145,42 +304,68 @@ func (w *response) requestTooLarge() {
}
}
+// needsSniff returns whether a Content-Type still needs to be sniffed.
+func (w *response) needsSniff() bool {
+ return !w.cw.wroteHeader && w.handlerHeader.Get("Content-Type") == "" && w.written < sniffLen
+}
+
type writerOnly struct {
io.Writer
}
func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
- // Call WriteHeader before checking w.chunking if it hasn't
- // been called yet, since WriteHeader is what sets w.chunking.
if !w.wroteHeader {
w.WriteHeader(StatusOK)
}
- if !w.chunking && w.bodyAllowed() && !w.needSniff {
- w.Flush()
+
+ if w.needsSniff() {
+ n0, err := io.Copy(writerOnly{w}, io.LimitReader(src, sniffLen))
+ n += n0
+ if err != nil {
+ return n, err
+ }
+ }
+
+ w.w.Flush() // get rid of any previous writes
+ w.cw.flush() // make sure Header is written; flush data to rwc
+
+ // Now that cw has been flushed, its chunking field is guaranteed initialized.
+ if !w.cw.chunking && w.bodyAllowed() {
if rf, ok := w.conn.rwc.(io.ReaderFrom); ok {
- n, err = rf.ReadFrom(src)
- w.written += n
- return
+ n0, err := rf.ReadFrom(src)
+ n += n0
+ w.written += n0
+ return n, err
}
}
+
// Fall back to default io.Copy implementation.
// Use wrapper to hide w.ReadFrom from io.Copy.
- return io.Copy(writerOnly{w}, src)
+ n0, err := io.Copy(writerOnly{w}, src)
+ n += n0
+ return n, err
}
// noLimit is an effective infinite upper bound for io.LimitedReader
const noLimit int64 = (1 << 63) - 1
+// debugServerConnections controls whether all server connections are wrapped
+// with a verbose logging wrapper.
+const debugServerConnections = false
+
// Create new connection from rwc.
func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) {
c = new(conn)
c.remoteAddr = rwc.RemoteAddr().String()
c.server = srv
c.rwc = rwc
- c.body = make([]byte, sniffLen)
- c.lr = io.LimitReader(rwc, noLimit).(*io.LimitedReader)
+ if debugServerConnections {
+ c.rwc = newLoggingConn("server", c.rwc)
+ }
+ c.sr = switchReader{r: c.rwc}
+ c.lr = io.LimitReader(&c.sr, noLimit).(*io.LimitedReader)
br := bufio.NewReader(c.lr)
- bw := bufio.NewWriter(rwc)
+ bw := bufio.NewWriter(c.rwc)
c.buf = bufio.NewReadWriter(br, bw)
return c, nil
}
@@ -207,9 +392,9 @@ type expectContinueReader struct {
func (ecr *expectContinueReader) Read(p []byte) (n int, err error) {
if ecr.closed {
- return 0, errors.New("http: Read after Close on request Body")
+ return 0, ErrBodyReadAfterClose
}
- if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked {
+ if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() {
ecr.resp.wroteContinue = true
io.WriteString(ecr.resp.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n")
ecr.resp.conn.buf.Flush()
@@ -232,9 +417,19 @@ var errTooLarge = errors.New("http: request too large")
// Read next request from connection.
func (c *conn) readRequest() (w *response, err error) {
- if c.hijacked {
+ if c.hijacked() {
return nil, ErrHijacked
}
+
+ if d := c.server.ReadTimeout; d != 0 {
+ c.rwc.SetReadDeadline(time.Now().Add(d))
+ }
+ if d := c.server.WriteTimeout; d != 0 {
+ defer func() {
+ c.rwc.SetWriteDeadline(time.Now().Add(d))
+ }()
+ }
+
c.lr.N = int64(c.server.maxHeaderBytes()) + 4096 /* bufio slop */
var req *Request
if req, err = ReadRequest(c.buf.Reader); err != nil {
@@ -248,17 +443,20 @@ func (c *conn) readRequest() (w *response, err error) {
req.RemoteAddr = c.remoteAddr
req.TLS = c.tlsState
- w = new(response)
- w.conn = c
- w.req = req
- w.header = make(Header)
- w.contentLength = -1
- c.body = c.body[:0]
+ w = &response{
+ conn: c,
+ req: req,
+ handlerHeader: make(Header),
+ contentLength: -1,
+ cw: new(chunkWriter),
+ }
+ w.cw.res = w
+ w.w = bufio.NewWriterSize(w.cw, bufferBeforeChunkingSize)
return w, nil
}
func (w *response) Header() Header {
- return w.header
+ return w.handlerHeader
}
// maxPostHandlerReadBytes is the max number of Request.Body bytes not
@@ -273,7 +471,7 @@ func (w *response) Header() Header {
const maxPostHandlerReadBytes = 256 << 10
func (w *response) WriteHeader(code int) {
- if w.conn.hijacked {
+ if w.conn.hijacked() {
log.Print("http: response.WriteHeader on hijacked connection")
return
}
@@ -284,31 +482,68 @@ func (w *response) WriteHeader(code int) {
w.wroteHeader = true
w.status = code
- // Check for a explicit (and valid) Content-Length header.
- var hasCL bool
- var contentLength int64
- if clenStr := w.header.Get("Content-Length"); clenStr != "" {
- var err error
- contentLength, err = strconv.ParseInt(clenStr, 10, 64)
- if err == nil {
- hasCL = true
+ w.cw.header = w.handlerHeader.clone()
+
+ if cl := w.cw.header.get("Content-Length"); cl != "" {
+ v, err := strconv.ParseInt(cl, 10, 64)
+ if err == nil && v >= 0 {
+ w.contentLength = v
} else {
- log.Printf("http: invalid Content-Length of %q sent", clenStr)
- w.header.Del("Content-Length")
+ log.Printf("http: invalid Content-Length of %q", cl)
+ w.cw.header.Del("Content-Length")
+ }
+ }
+}
+
+// writeHeader finalizes the header sent to the client and writes it
+// to cw.res.conn.buf.
+//
+// p is not written by writeHeader, but is the first chunk of the body
+// that will be written. It is sniffed for a Content-Type if none is
+// set explicitly. It's also used to set the Content-Length, if the
+// total body size was small and the handler has already finished
+// running.
+func (cw *chunkWriter) writeHeader(p []byte) {
+ if cw.wroteHeader {
+ return
+ }
+ cw.wroteHeader = true
+
+ w := cw.res
+ code := w.status
+ done := w.handlerDone
+
+ // If the handler is done but never sent a Content-Length
+ // response header and this is our first (and last) write, set
+ // it, even to zero. This helps HTTP/1.0 clients keep their
+ // "keep-alive" connections alive.
+ if done && cw.header.get("Content-Length") == "" && w.req.Method != "HEAD" {
+ w.contentLength = int64(len(p))
+ cw.header.Set("Content-Length", strconv.Itoa(len(p)))
+ }
+
+ // If this was an HTTP/1.0 request with keep-alive and we sent a
+ // Content-Length back, we can make this a keep-alive response ...
+ if w.req.wantsHttp10KeepAlive() {
+ sentLength := cw.header.get("Content-Length") != ""
+ if sentLength && cw.header.get("Connection") == "keep-alive" {
+ w.closeAfterReply = false
}
}
+ // Check for a explicit (and valid) Content-Length header.
+ hasCL := w.contentLength != -1
+
if w.req.wantsHttp10KeepAlive() && (w.req.Method == "HEAD" || hasCL) {
- _, connectionHeaderSet := w.header["Connection"]
+ _, connectionHeaderSet := cw.header["Connection"]
if !connectionHeaderSet {
- w.header.Set("Connection", "keep-alive")
+ cw.header.Set("Connection", "keep-alive")
}
- } else if !w.req.ProtoAtLeast(1, 1) {
- // Client did not ask to keep connection alive.
+ } else if !w.req.ProtoAtLeast(1, 1) || w.req.wantsClose() {
w.closeAfterReply = true
}
- if w.header.Get("Connection") == "close" {
+ if cw.header.get("Connection") == "close" {
w.closeAfterReply = true
}
@@ -322,7 +557,7 @@ func (w *response) WriteHeader(code int) {
n, _ := io.CopyN(ioutil.Discard, w.req.Body, maxPostHandlerReadBytes+1)
if n >= maxPostHandlerReadBytes {
w.requestTooLarge()
- w.header.Set("Connection", "close")
+ cw.header.Set("Connection", "close")
} else {
w.req.Body.Close()
}
@@ -332,64 +567,67 @@ func (w *response) WriteHeader(code int) {
if code == StatusNotModified {
// Must not have body.
for _, header := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} {
- if w.header.Get(header) != "" {
- // TODO: return an error if WriteHeader gets a return parameter
- // or set a flag on w to make future Writes() write an error page?
- // for now just log and drop the header.
- log.Printf("http: StatusNotModified response with header %q defined", header)
- w.header.Del(header)
+ // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
+ if cw.header.get(header) != "" {
+ cw.header.Del(header)
}
}
} else {
// If no content type, apply sniffing algorithm to body.
- if w.header.Get("Content-Type") == "" && w.req.Method != "HEAD" {
- w.needSniff = true
+ if cw.header.get("Content-Type") == "" && w.req.Method != "HEAD" {
+ cw.header.Set("Content-Type", DetectContentType(p))
}
}
- if _, ok := w.header["Date"]; !ok {
- w.Header().Set("Date", time.Now().UTC().Format(TimeFormat))
+ if _, ok := cw.header["Date"]; !ok {
+ cw.header.Set("Date", time.Now().UTC().Format(TimeFormat))
}
- te := w.header.Get("Transfer-Encoding")
+ te := cw.header.get("Transfer-Encoding")
hasTE := te != ""
if hasCL && hasTE && te != "identity" {
// TODO: return an error if WriteHeader gets a return parameter
// For now just ignore the Content-Length.
log.Printf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d",
- te, contentLength)
- w.header.Del("Content-Length")
+ te, w.contentLength)
+ cw.header.Del("Content-Length")
hasCL = false
}
if w.req.Method == "HEAD" || code == StatusNotModified {
// do nothing
+ } else if code == StatusNoContent {
+ cw.header.Del("Transfer-Encoding")
} else if hasCL {
- w.contentLength = contentLength
- w.header.Del("Transfer-Encoding")
+ cw.header.Del("Transfer-Encoding")
} else if w.req.ProtoAtLeast(1, 1) {
// HTTP/1.1 or greater: use chunked transfer encoding
// to avoid closing the connection at EOF.
// TODO: this blows away any custom or stacked Transfer-Encoding they
// might have set. Deal with that as need arises once we have a valid
// use case.
- w.chunking = true
- w.header.Set("Transfer-Encoding", "chunked")
+ cw.chunking = true
+ cw.header.Set("Transfer-Encoding", "chunked")
} else {
// HTTP version < 1.1: cannot do chunked transfer
// encoding and we don't know the Content-Length so
// signal EOF by closing connection.
w.closeAfterReply = true
- w.header.Del("Transfer-Encoding") // in case already set
+ cw.header.Del("Transfer-Encoding") // in case already set
}
// Cannot use Content-Length with non-identity Transfer-Encoding.
- if w.chunking {
- w.header.Del("Content-Length")
+ if cw.chunking {
+ cw.header.Del("Content-Length")
}
if !w.req.ProtoAtLeast(1, 0) {
return
}
+
+ if w.closeAfterReply && !hasToken(cw.header.get("Connection"), "close") {
+ cw.header.Set("Connection", "close")
+ }
+
proto := "HTTP/1.0"
if w.req.ProtoAtLeast(1, 1) {
proto = "HTTP/1.1"
@@ -400,37 +638,8 @@ func (w *response) WriteHeader(code int) {
text = "status code " + codestring
}
io.WriteString(w.conn.buf, proto+" "+codestring+" "+text+"\r\n")
- w.header.Write(w.conn.buf)
-
- // If we need to sniff the body, leave the header open.
- // Otherwise, end it here.
- if !w.needSniff {
- io.WriteString(w.conn.buf, "\r\n")
- }
-}
-
-// sniff uses the first block of written data,
-// stored in w.conn.body, to decide the Content-Type
-// for the HTTP body.
-func (w *response) sniff() {
- if !w.needSniff {
- return
- }
- w.needSniff = false
-
- data := w.conn.body
- fmt.Fprintf(w.conn.buf, "Content-Type: %s\r\n\r\n", DetectContentType(data))
-
- if len(data) == 0 {
- return
- }
- if w.chunking {
- fmt.Fprintf(w.conn.buf, "%x\r\n", len(data))
- }
- _, err := w.conn.buf.Write(data)
- if w.chunking && err == nil {
- io.WriteString(w.conn.buf, "\r\n")
- }
+ cw.header.Write(w.conn.buf)
+ w.conn.buf.Write(crlf)
}
// bodyAllowed returns true if a Write is allowed for this response type.
@@ -442,8 +651,40 @@ func (w *response) bodyAllowed() bool {
return w.status != StatusNotModified && w.req.Method != "HEAD"
}
+// The Life Of A Write is like this:
+//
+// Handler starts. No header has been sent. The handler can either
+// write a header, or just start writing. Writing before sending a header
+// sends an implicity empty 200 OK header.
+//
+// If the handler didn't declare a Content-Length up front, we either
+// go into chunking mode or, if the handler finishes running before
+// the chunking buffer size, we compute a Content-Length and send that
+// in the header instead.
+//
+// Likewise, if the handler didn't set a Content-Type, we sniff that
+// from the initial chunk of output.
+//
+// The Writers are wired together like:
+//
+// 1. *response (the ResponseWriter) ->
+// 2. (*response).w, a *bufio.Writer of bufferBeforeChunkingSize bytes
+// 3. chunkWriter.Writer (whose writeHeader finalizes Content-Length/Type)
+// and which writes the chunk headers, if needed.
+// 4. conn.buf, a bufio.Writer of default (4kB) bytes
+// 5. the rwc, the net.Conn.
+//
+// TODO(bradfitz): short-circuit some of the buffering when the
+// initial header contains both a Content-Type and Content-Length.
+// Also short-circuit in (1) when the header's been sent and not in
+// chunking mode, writing directly to (4) instead, if (2) has no
+// buffered data. More generally, we could short-circuit from (1) to
+// (3) even in chunking mode if the write size from (1) is over some
+// threshold and nothing is in (2). The answer might be mostly making
+// bufferBeforeChunkingSize smaller and having bufio's fast-paths deal
+// with this instead.
func (w *response) Write(data []byte) (n int, err error) {
- if w.conn.hijacked {
+ if w.conn.hijacked() {
log.Print("http: response.Write on hijacked connection")
return 0, ErrHijacked
}
@@ -461,73 +702,20 @@ func (w *response) Write(data []byte) (n int, err error) {
if w.contentLength != -1 && w.written > w.contentLength {
return 0, ErrContentLength
}
-
- var m int
- if w.needSniff {
- // We need to sniff the beginning of the output to
- // determine the content type. Accumulate the
- // initial writes in w.conn.body.
- // Cap m so that append won't allocate.
- m = cap(w.conn.body) - len(w.conn.body)
- if m > len(data) {
- m = len(data)
- }
- w.conn.body = append(w.conn.body, data[:m]...)
- data = data[m:]
- if len(data) == 0 {
- // Copied everything into the buffer.
- // Wait for next write.
- return m, nil
- }
-
- // Filled the buffer; more data remains.
- // Sniff the content (flushes the buffer)
- // and then proceed with the remainder
- // of the data as a normal Write.
- // Calling sniff clears needSniff.
- w.sniff()
- }
-
- // TODO(rsc): if chunking happened after the buffering,
- // then there would be fewer chunk headers.
- // On the other hand, it would make hijacking more difficult.
- if w.chunking {
- fmt.Fprintf(w.conn.buf, "%x\r\n", len(data)) // TODO(rsc): use strconv not fmt
- }
- n, err = w.conn.buf.Write(data)
- if err == nil && w.chunking {
- if n != len(data) {
- err = io.ErrShortWrite
- }
- if err == nil {
- io.WriteString(w.conn.buf, "\r\n")
- }
- }
-
- return m + n, err
+ return w.w.Write(data)
}
func (w *response) finishRequest() {
- // If this was an HTTP/1.0 request with keep-alive and we sent a Content-Length
- // back, we can make this a keep-alive response ...
- if w.req.wantsHttp10KeepAlive() {
- sentLength := w.header.Get("Content-Length") != ""
- if sentLength && w.header.Get("Connection") == "keep-alive" {
- w.closeAfterReply = false
- }
- }
+ w.handlerDone = true
+
if !w.wroteHeader {
w.WriteHeader(StatusOK)
}
- if w.needSniff {
- w.sniff()
- }
- if w.chunking {
- io.WriteString(w.conn.buf, "0\r\n")
- // trailer key/value pairs, followed by blank line
- io.WriteString(w.conn.buf, "\r\n")
- }
+
+ w.w.Flush()
+ w.cw.close()
w.conn.buf.Flush()
+
// Close the body, unless we're about to close the whole TCP connection
// anyway.
if !w.closeAfterReply {
@@ -537,7 +725,7 @@ func (w *response) finishRequest() {
w.req.MultipartForm.RemoveAll()
}
- if w.contentLength != -1 && w.contentLength != w.written {
+ if w.contentLength != -1 && w.bodyAllowed() && w.contentLength != w.written {
// Did not write enough. Avoid getting out of sync.
w.closeAfterReply = true
}
@@ -547,66 +735,114 @@ func (w *response) Flush() {
if !w.wroteHeader {
w.WriteHeader(StatusOK)
}
- w.sniff()
- w.conn.buf.Flush()
+ w.w.Flush()
+ w.cw.flush()
}
-// Close the connection.
-func (c *conn) close() {
+func (c *conn) finalFlush() {
if c.buf != nil {
c.buf.Flush()
c.buf = nil
}
+}
+
+// Close the connection.
+func (c *conn) close() {
+ c.finalFlush()
if c.rwc != nil {
c.rwc.Close()
c.rwc = nil
}
}
+// rstAvoidanceDelay is the amount of time we sleep after closing the
+// write side of a TCP connection before closing the entire socket.
+// By sleeping, we increase the chances that the client sees our FIN
+// and processes its final data before they process the subsequent RST
+// from closing a connection with known unread data.
+// This RST seems to occur mostly on BSD systems. (And Windows?)
+// This timeout is somewhat arbitrary (~latency around the planet).
+const rstAvoidanceDelay = 500 * time.Millisecond
+
+// closeWrite flushes any outstanding data and sends a FIN packet (if
+// client is connected via TCP), signalling that we're done. We then
+// pause for a bit, hoping the client processes it before `any
+// subsequent RST.
+//
+// See http://golang.org/issue/3595
+func (c *conn) closeWriteAndWait() {
+ c.finalFlush()
+ if tcp, ok := c.rwc.(*net.TCPConn); ok {
+ tcp.CloseWrite()
+ }
+ time.Sleep(rstAvoidanceDelay)
+}
+
+// validNPN returns whether the proto is not a blacklisted Next
+// Protocol Negotiation protocol. Empty and built-in protocol types
+// are blacklisted and can't be overridden with alternate
+// implementations.
+func validNPN(proto string) bool {
+ switch proto {
+ case "", "http/1.1", "http/1.0":
+ return false
+ }
+ return true
+}
+
// Serve a new connection.
func (c *conn) serve() {
defer func() {
- err := recover()
- if err == nil {
- return
+ if err := recover(); err != nil {
+ const size = 4096
+ buf := make([]byte, size)
+ buf = buf[:runtime.Stack(buf, false)]
+ log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
}
-
- var buf bytes.Buffer
- fmt.Fprintf(&buf, "http: panic serving %v: %v\n", c.remoteAddr, err)
- buf.Write(debug.Stack())
- log.Print(buf.String())
-
- if c.rwc != nil { // may be nil if connection hijacked
- c.rwc.Close()
+ if !c.hijacked() {
+ c.close()
}
}()
if tlsConn, ok := c.rwc.(*tls.Conn); ok {
+ if d := c.server.ReadTimeout; d != 0 {
+ c.rwc.SetReadDeadline(time.Now().Add(d))
+ }
+ if d := c.server.WriteTimeout; d != 0 {
+ c.rwc.SetWriteDeadline(time.Now().Add(d))
+ }
if err := tlsConn.Handshake(); err != nil {
- c.close()
return
}
c.tlsState = new(tls.ConnectionState)
*c.tlsState = tlsConn.ConnectionState()
+ if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) {
+ if fn := c.server.TLSNextProto[proto]; fn != nil {
+ h := initNPNRequest{tlsConn, serverHandler{c.server}}
+ fn(c.server, tlsConn, h)
+ }
+ return
+ }
}
for {
w, err := c.readRequest()
if err != nil {
- msg := "400 Bad Request"
if err == errTooLarge {
// Their HTTP client may or may not be
// able to read this if we're
// responding to them and hanging up
// while they're still writing their
// request. Undefined behavior.
- msg = "413 Request Entity Too Large"
+ io.WriteString(c.rwc, "HTTP/1.1 413 Request Entity Too Large\r\n\r\n")
+ c.closeWriteAndWait()
+ break
} else if err == io.EOF {
break // Don't reply
} else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
break // Don't reply
}
- fmt.Fprintf(c.rwc, "HTTP/1.1 %s\r\n\r\n", msg)
+ io.WriteString(c.rwc, "HTTP/1.1 400 Bad Request\r\n\r\n")
break
}
@@ -624,59 +860,59 @@ func (c *conn) serve() {
break
}
req.Header.Del("Expect")
- } else if req.Header.Get("Expect") != "" {
- // TODO(bradfitz): let ServeHTTP handlers handle
- // requests with non-standard expectation[s]? Seems
- // theoretical at best, and doesn't fit into the
- // current ServeHTTP model anyway. We'd need to
- // make the ResponseWriter an optional
- // "ExpectReplier" interface or something.
- //
- // For now we'll just obey RFC 2616 14.20 which says
- // "If a server receives a request containing an
- // Expect field that includes an expectation-
- // extension that it does not support, it MUST
- // respond with a 417 (Expectation Failed) status."
- w.Header().Set("Connection", "close")
- w.WriteHeader(StatusExpectationFailed)
- w.finishRequest()
+ } else if req.Header.get("Expect") != "" {
+ w.sendExpectationFailed()
break
}
- handler := c.server.Handler
- if handler == nil {
- handler = DefaultServeMux
- }
-
// HTTP cannot have multiple simultaneous active requests.[*]
// Until the server replies to this request, it can't read another,
// so we might as well run the handler in this goroutine.
// [*] Not strictly true: HTTP pipelining. We could let them all process
// in parallel even if their responses need to be serialized.
- handler.ServeHTTP(w, w.req)
- if c.hijacked {
+ serverHandler{c.server}.ServeHTTP(w, w.req)
+ if c.hijacked() {
return
}
w.finishRequest()
if w.closeAfterReply {
+ if w.requestBodyLimitHit {
+ c.closeWriteAndWait()
+ }
break
}
}
- c.close()
+}
+
+func (w *response) sendExpectationFailed() {
+ // TODO(bradfitz): let ServeHTTP handlers handle
+ // requests with non-standard expectation[s]? Seems
+ // theoretical at best, and doesn't fit into the
+ // current ServeHTTP model anyway. We'd need to
+ // make the ResponseWriter an optional
+ // "ExpectReplier" interface or something.
+ //
+ // For now we'll just obey RFC 2616 14.20 which says
+ // "If a server receives a request containing an
+ // Expect field that includes an expectation-
+ // extension that it does not support, it MUST
+ // respond with a 417 (Expectation Failed) status."
+ w.Header().Set("Connection", "close")
+ w.WriteHeader(StatusExpectationFailed)
+ w.finishRequest()
}
// Hijack implements the Hijacker.Hijack method. Our response is both a ResponseWriter
// and a Hijacker.
func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
- if w.conn.hijacked {
- return nil, nil, ErrHijacked
+ if w.wroteHeader {
+ w.cw.flush()
}
- w.conn.hijacked = true
- rwc = w.conn.rwc
- buf = w.conn.buf
- w.conn.rwc = nil
- w.conn.buf = nil
- return
+ return w.conn.hijack()
+}
+
+func (w *response) CloseNotify() <-chan bool {
+ return w.conn.closeNotify()
}
// The HandlerFunc type is an adapter to allow the use of
@@ -817,13 +1053,13 @@ func RedirectHandler(url string, code int) Handler {
// patterns and calls the handler for the pattern that
// most closely matches the URL.
//
-// Patterns named fixed, rooted paths, like "/favicon.ico",
+// Patterns name fixed, rooted paths, like "/favicon.ico",
// or rooted subtrees, like "/images/" (note the trailing slash).
// Longer patterns take precedence over shorter ones, so that
// if there are handlers registered for both "/images/"
// and "/images/thumbnails/", the latter handler will be
// called for paths beginning "/images/thumbnails/" and the
-// former will receiver requests for any other paths in the
+// former will receive requests for any other paths in the
// "/images/" subtree.
//
// Patterns may optionally begin with a host name, restricting matches to
@@ -836,13 +1072,15 @@ func RedirectHandler(url string, code int) Handler {
// redirecting any request containing . or .. elements to an
// equivalent .- and ..-free URL.
type ServeMux struct {
- mu sync.RWMutex
- m map[string]muxEntry
+ mu sync.RWMutex
+ m map[string]muxEntry
+ hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {
explicit bool
h Handler
+ pattern string
}
// NewServeMux allocates and returns a new ServeMux.
@@ -883,8 +1121,7 @@ func cleanPath(p string) string {
// Find a handler on a handler map given a path string
// Most-specific (longest) pattern wins
-func (mux *ServeMux) match(path string) Handler {
- var h Handler
+func (mux *ServeMux) match(path string) (h Handler, pattern string) {
var n = 0
for k, v := range mux.m {
if !pathMatch(k, path) {
@@ -893,37 +1130,64 @@ func (mux *ServeMux) match(path string) Handler {
if h == nil || len(k) > n {
n = len(k)
h = v.h
+ pattern = v.pattern
+ }
+ }
+ return
+}
+
+// Handler returns the handler to use for the given request,
+// consulting r.Method, r.Host, and r.URL.Path. It always returns
+// a non-nil handler. If the path is not in its canonical form, the
+// handler will be an internally-generated handler that redirects
+// to the canonical path.
+//
+// Handler also returns the registered pattern that matches the
+// request or, in the case of internally-generated redirects,
+// the pattern that will match after following the redirect.
+//
+// If there is no registered handler that applies to the request,
+// Handler returns a ``page not found'' handler and an empty pattern.
+func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
+ if r.Method != "CONNECT" {
+ if p := cleanPath(r.URL.Path); p != r.URL.Path {
+ _, pattern = mux.handler(r.Host, p)
+ return RedirectHandler(p, StatusMovedPermanently), pattern
}
}
- return h
+
+ return mux.handler(r.Host, r.URL.Path)
}
-// handler returns the handler to use for the request r.
-func (mux *ServeMux) handler(r *Request) Handler {
+// handler is the main implementation of Handler.
+// The path is known to be in canonical form, except for CONNECT methods.
+func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
- h := mux.match(r.Host + r.URL.Path)
+ if mux.hosts {
+ h, pattern = mux.match(host + path)
+ }
if h == nil {
- h = mux.match(r.URL.Path)
+ h, pattern = mux.match(path)
}
if h == nil {
- h = NotFoundHandler()
+ h, pattern = NotFoundHandler(), ""
}
- return h
+ return
}
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
- // Clean path to canonical form and redirect.
- if p := cleanPath(r.URL.Path); p != r.URL.Path {
- w.Header().Set("Location", p)
- w.WriteHeader(StatusMovedPermanently)
+ if r.RequestURI == "*" {
+ w.Header().Set("Connection", "close")
+ w.WriteHeader(StatusBadRequest)
return
}
- mux.handler(r).ServeHTTP(w, r)
+ h, _ := mux.Handler(r)
+ h.ServeHTTP(w, r)
}
// Handle registers the handler for the given pattern.
@@ -942,14 +1206,26 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) {
panic("http: multiple registrations for " + pattern)
}
- mux.m[pattern] = muxEntry{explicit: true, h: handler}
+ mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}
+
+ if pattern[0] != '/' {
+ mux.hosts = true
+ }
// Helpful behavior:
// If pattern is /tree/, insert an implicit permanent redirect for /tree.
// It can be overridden by an explicit registration.
n := len(pattern)
if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
- mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(pattern, StatusMovedPermanently)}
+ // If pattern contains a host name, strip it and use remaining
+ // path for redirect.
+ path := pattern
+ if pattern[0] != '/' {
+ // In pattern, at least the last character is a '/', so
+ // strings.Index can't be -1.
+ path = pattern[strings.Index(pattern, "/"):]
+ }
+ mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern}
}
}
@@ -971,7 +1247,7 @@ func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
}
// Serve accepts incoming HTTP connections on the listener l,
-// creating a new service thread for each. The service threads
+// creating a new service goroutine for each. The service goroutines
// read requests and then call handler to reply to them.
// Handler is typically nil, in which case the DefaultServeMux is used.
func Serve(l net.Listener, handler Handler) error {
@@ -987,6 +1263,32 @@ type Server struct {
WriteTimeout time.Duration // maximum duration before timing out write of the response
MaxHeaderBytes int // maximum size of request headers, DefaultMaxHeaderBytes if 0
TLSConfig *tls.Config // optional TLS config, used by ListenAndServeTLS
+
+ // TLSNextProto optionally specifies a function to take over
+ // ownership of the provided TLS connection when an NPN
+ // protocol upgrade has occured. The map key is the protocol
+ // name negotiated. The Handler argument should be used to
+ // handle HTTP requests and will initialize the Request's TLS
+ // and RemoteAddr if not already set. The connection is
+ // automatically closed when the function returns.
+ TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
+}
+
+// serverHandler delegates to either the server's Handler or
+// DefaultServeMux and also handles "OPTIONS *" requests.
+type serverHandler struct {
+ srv *Server
+}
+
+func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
+ handler := sh.srv.Handler
+ if handler == nil {
+ handler = DefaultServeMux
+ }
+ if req.RequestURI == "*" && req.Method == "OPTIONS" {
+ handler = globalOptionsHandler{}
+ }
+ handler.ServeHTTP(rw, req)
}
// ListenAndServe listens on the TCP network address srv.Addr and then
@@ -1005,7 +1307,7 @@ func (srv *Server) ListenAndServe() error {
}
// Serve accepts incoming connections on the Listener l, creating a
-// new service thread for each. The service threads read requests and
+// new service goroutine for each. The service goroutines read requests and
// then call srv.Handler to reply to them.
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
@@ -1029,12 +1331,6 @@ func (srv *Server) Serve(l net.Listener) error {
return e
}
tempDelay = 0
- if srv.ReadTimeout != 0 {
- rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout))
- }
- if srv.WriteTimeout != 0 {
- rw.SetWriteDeadline(time.Now().Add(srv.WriteTimeout))
- }
c, err := srv.newConn(rw)
if err != nil {
continue
@@ -1150,7 +1446,7 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
// TimeoutHandler returns a Handler that runs h with the given time limit.
//
// The new Handler calls h.ServeHTTP to handle each request, but if a
-// call runs for more than ns nanoseconds, the handler responds with
+// call runs for longer than its time limit, the handler responds with
// a 503 Service Unavailable error and the given message in its body.
// (If msg is empty, a suitable default message will be sent.)
// After such a timeout, writes by h to its ResponseWriter will return
@@ -1180,7 +1476,7 @@ func (h *timeoutHandler) errorBody() string {
}
func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request) {
- done := make(chan bool)
+ done := make(chan bool, 1)
tw := &timeoutWriter{w: w}
go func() {
h.handler.ServeHTTP(tw, r)
@@ -1232,3 +1528,86 @@ func (tw *timeoutWriter) WriteHeader(code int) {
tw.mu.Unlock()
tw.w.WriteHeader(code)
}
+
+// globalOptionsHandler responds to "OPTIONS *" requests.
+type globalOptionsHandler struct{}
+
+func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request) {
+ w.Header().Set("Content-Length", "0")
+ if r.ContentLength != 0 {
+ // Read up to 4KB of OPTIONS body (as mentioned in the
+ // spec as being reserved for future use), but anything
+ // over that is considered a waste of server resources
+ // (or an attack) and we abort and close the connection,
+ // courtesy of MaxBytesReader's EOF behavior.
+ mb := MaxBytesReader(w, r.Body, 4<<10)
+ io.Copy(ioutil.Discard, mb)
+ }
+}
+
+// eofReader is a non-nil io.ReadCloser that always returns EOF.
+var eofReader = ioutil.NopCloser(strings.NewReader(""))
+
+// initNPNRequest is an HTTP handler that initializes certain
+// uninitialized fields in its *Request. Such partially-initialized
+// Requests come from NPN protocol handlers.
+type initNPNRequest struct {
+ c *tls.Conn
+ h serverHandler
+}
+
+func (h initNPNRequest) ServeHTTP(rw ResponseWriter, req *Request) {
+ if req.TLS == nil {
+ req.TLS = &tls.ConnectionState{}
+ *req.TLS = h.c.ConnectionState()
+ }
+ if req.Body == nil {
+ req.Body = eofReader
+ }
+ if req.RemoteAddr == "" {
+ req.RemoteAddr = h.c.RemoteAddr().String()
+ }
+ h.h.ServeHTTP(rw, req)
+}
+
+// loggingConn is used for debugging.
+type loggingConn struct {
+ name string
+ net.Conn
+}
+
+var (
+ uniqNameMu sync.Mutex
+ uniqNameNext = make(map[string]int)
+)
+
+func newLoggingConn(baseName string, c net.Conn) net.Conn {
+ uniqNameMu.Lock()
+ defer uniqNameMu.Unlock()
+ uniqNameNext[baseName]++
+ return &loggingConn{
+ name: fmt.Sprintf("%s-%d", baseName, uniqNameNext[baseName]),
+ Conn: c,
+ }
+}
+
+func (c *loggingConn) Write(p []byte) (n int, err error) {
+ log.Printf("%s.Write(%d) = ....", c.name, len(p))
+ n, err = c.Conn.Write(p)
+ log.Printf("%s.Write(%d) = %d, %v", c.name, len(p), n, err)
+ return
+}
+
+func (c *loggingConn) Read(p []byte) (n int, err error) {
+ log.Printf("%s.Read(%d) = ....", c.name, len(p))
+ n, err = c.Conn.Read(p)
+ log.Printf("%s.Read(%d) = %d, %v", c.name, len(p), n, err)
+ return
+}
+
+func (c *loggingConn) Close() (err error) {
+ log.Printf("%s.Close() = ...", c.name)
+ err = c.Conn.Close()
+ log.Printf("%s.Close() = %v", c.name, err)
+ return
+}
diff --git a/src/pkg/net/http/server_test.go b/src/pkg/net/http/server_test.go
new file mode 100644
index 000000000..8b4e8c6d6
--- /dev/null
+++ b/src/pkg/net/http/server_test.go
@@ -0,0 +1,95 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http
+
+import (
+ "net/url"
+ "testing"
+)
+
+var serveMuxRegister = []struct {
+ pattern string
+ h Handler
+}{
+ {"/dir/", serve(200)},
+ {"/search", serve(201)},
+ {"codesearch.google.com/search", serve(202)},
+ {"codesearch.google.com/", serve(203)},
+}
+
+// serve returns a handler that sends a response with the given code.
+func serve(code int) HandlerFunc {
+ return func(w ResponseWriter, r *Request) {
+ w.WriteHeader(code)
+ }
+}
+
+var serveMuxTests = []struct {
+ method string
+ host string
+ path string
+ code int
+ pattern string
+}{
+ {"GET", "google.com", "/", 404, ""},
+ {"GET", "google.com", "/dir", 301, "/dir/"},
+ {"GET", "google.com", "/dir/", 200, "/dir/"},
+ {"GET", "google.com", "/dir/file", 200, "/dir/"},
+ {"GET", "google.com", "/search", 201, "/search"},
+ {"GET", "google.com", "/search/", 404, ""},
+ {"GET", "google.com", "/search/foo", 404, ""},
+ {"GET", "codesearch.google.com", "/search", 202, "codesearch.google.com/search"},
+ {"GET", "codesearch.google.com", "/search/", 203, "codesearch.google.com/"},
+ {"GET", "codesearch.google.com", "/search/foo", 203, "codesearch.google.com/"},
+ {"GET", "codesearch.google.com", "/", 203, "codesearch.google.com/"},
+ {"GET", "images.google.com", "/search", 201, "/search"},
+ {"GET", "images.google.com", "/search/", 404, ""},
+ {"GET", "images.google.com", "/search/foo", 404, ""},
+ {"GET", "google.com", "/../search", 301, "/search"},
+ {"GET", "google.com", "/dir/..", 301, ""},
+ {"GET", "google.com", "/dir/..", 301, ""},
+ {"GET", "google.com", "/dir/./file", 301, "/dir/"},
+
+ // The /foo -> /foo/ redirect applies to CONNECT requests
+ // but the path canonicalization does not.
+ {"CONNECT", "google.com", "/dir", 301, "/dir/"},
+ {"CONNECT", "google.com", "/../search", 404, ""},
+ {"CONNECT", "google.com", "/dir/..", 200, "/dir/"},
+ {"CONNECT", "google.com", "/dir/..", 200, "/dir/"},
+ {"CONNECT", "google.com", "/dir/./file", 200, "/dir/"},
+}
+
+func TestServeMuxHandler(t *testing.T) {
+ mux := NewServeMux()
+ for _, e := range serveMuxRegister {
+ mux.Handle(e.pattern, e.h)
+ }
+
+ for _, tt := range serveMuxTests {
+ r := &Request{
+ Method: tt.method,
+ Host: tt.host,
+ URL: &url.URL{
+ Path: tt.path,
+ },
+ }
+ h, pattern := mux.Handler(r)
+ cs := &codeSaver{h: Header{}}
+ h.ServeHTTP(cs, r)
+ if pattern != tt.pattern || cs.code != tt.code {
+ t.Errorf("%s %s %s = %d, %q, want %d, %q", tt.method, tt.host, tt.path, cs.code, pattern, tt.code, tt.pattern)
+ }
+ }
+}
+
+// A codeSaver is a ResponseWriter that saves the code passed to WriteHeader.
+type codeSaver struct {
+ h Header
+ code int
+}
+
+func (cs *codeSaver) Header() Header { return cs.h }
+func (cs *codeSaver) Write(p []byte) (int, error) { return len(p), nil }
+func (cs *codeSaver) WriteHeader(code int) { cs.code = code }
diff --git a/src/pkg/net/http/transfer.go b/src/pkg/net/http/transfer.go
index 9e9d84172..43c6023a3 100644
--- a/src/pkg/net/http/transfer.go
+++ b/src/pkg/net/http/transfer.go
@@ -87,10 +87,8 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
// Sanitize Body,ContentLength,TransferEncoding
if t.ResponseToHEAD {
t.Body = nil
- t.TransferEncoding = nil
- // ContentLength is expected to hold Content-Length
- if t.ContentLength < 0 {
- return nil, ErrMissingContentLength
+ if chunked(t.TransferEncoding) {
+ t.ContentLength = -1
}
} else {
if !atLeastHTTP11 || t.Body == nil {
@@ -122,9 +120,6 @@ func (t *transferWriter) shouldSendContentLength() bool {
if t.ContentLength > 0 {
return true
}
- if t.ResponseToHEAD {
- return true
- }
// Many servers expect a Content-Length for these methods
if t.Method == "POST" || t.Method == "PUT" {
return true
@@ -199,10 +194,11 @@ func (t *transferWriter) WriteBody(w io.Writer) (err error) {
ncopy, err = io.Copy(w, t.Body)
} else {
ncopy, err = io.Copy(w, io.LimitReader(t.Body, t.ContentLength))
- nextra, err := io.Copy(ioutil.Discard, t.Body)
if err != nil {
return err
}
+ var nextra int64
+ nextra, err = io.Copy(ioutil.Discard, t.Body)
ncopy += nextra
}
if err != nil {
@@ -213,7 +209,7 @@ func (t *transferWriter) WriteBody(w io.Writer) (err error) {
}
}
- if t.ContentLength != -1 && t.ContentLength != ncopy {
+ if !t.ResponseToHEAD && t.ContentLength != -1 && t.ContentLength != ncopy {
return fmt.Errorf("http: Request.ContentLength=%d with Body length %d",
t.ContentLength, ncopy)
}
@@ -294,10 +290,19 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
return err
}
- t.ContentLength, err = fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
+ realLength, err := fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
if err != nil {
return err
}
+ if isResponse && t.RequestMethod == "HEAD" {
+ if n, err := parseContentLength(t.Header.get("Content-Length")); err != nil {
+ return err
+ } else {
+ t.ContentLength = n
+ }
+ } else {
+ t.ContentLength = realLength
+ }
// Trailer
t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding)
@@ -310,7 +315,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
// See RFC2616, section 4.4.
switch msg.(type) {
case *Response:
- if t.ContentLength == -1 &&
+ if realLength == -1 &&
!chunked(t.TransferEncoding) &&
bodyAllowedForStatus(t.StatusCode) {
// Unbounded body.
@@ -322,12 +327,16 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
// or close connection when finished, since multipart is not supported yet
switch {
case chunked(t.TransferEncoding):
- t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
- case t.ContentLength >= 0:
+ if noBodyExpected(t.RequestMethod) {
+ t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close}
+ } else {
+ t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
+ }
+ case realLength >= 0:
// TODO: limit the Content-Length. This is an easy DoS vector.
- t.Body = &body{Reader: io.LimitReader(r, t.ContentLength), closing: t.Close}
+ t.Body = &body{Reader: io.LimitReader(r, realLength), closing: t.Close}
default:
- // t.ContentLength < 0, i.e. "Content-Length" not mentioned in header
+ // realLength < 0, i.e. "Content-Length" not mentioned in header
if t.Close {
// Close semantics (i.e. HTTP/1.0)
t.Body = &body{Reader: r, closing: t.Close}
@@ -371,12 +380,6 @@ func fixTransferEncoding(requestMethod string, header Header) ([]string, error)
delete(header, "Transfer-Encoding")
- // Head responses have no bodies, so the transfer encoding
- // should be ignored.
- if requestMethod == "HEAD" {
- return nil, nil
- }
-
encodings := strings.Split(raw[0], ",")
te := make([]string, 0, len(encodings))
// TODO: Even though we only support "identity" and "chunked"
@@ -432,11 +435,11 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header,
}
// Logic based on Content-Length
- cl := strings.TrimSpace(header.Get("Content-Length"))
+ cl := strings.TrimSpace(header.get("Content-Length"))
if cl != "" {
- n, err := strconv.ParseInt(cl, 10, 64)
- if err != nil || n < 0 {
- return -1, &badStringError{"bad Content-Length", cl}
+ n, err := parseContentLength(cl)
+ if err != nil {
+ return -1, err
}
return n, nil
} else {
@@ -451,13 +454,6 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header,
return 0, nil
}
- // Logic based on media type. The purpose of the following code is just
- // to detect whether the unsupported "multipart/byteranges" is being
- // used. A proper Content-Type parser is needed in the future.
- if strings.Contains(strings.ToLower(header.Get("Content-Type")), "multipart/byteranges") {
- return -1, ErrNotSupported
- }
-
// Body-EOF logic based on other methods (like closing, or chunked coding)
return -1, nil
}
@@ -469,14 +465,14 @@ func shouldClose(major, minor int, header Header) bool {
if major < 1 {
return true
} else if major == 1 && minor == 0 {
- if !strings.Contains(strings.ToLower(header.Get("Connection")), "keep-alive") {
+ if !strings.Contains(strings.ToLower(header.get("Connection")), "keep-alive") {
return true
}
return false
} else {
// TODO: Should split on commas, toss surrounding white space,
// and check each field.
- if strings.ToLower(header.Get("Connection")) == "close" {
+ if strings.ToLower(header.get("Connection")) == "close" {
header.Del("Connection")
return true
}
@@ -486,7 +482,7 @@ func shouldClose(major, minor int, header Header) bool {
// Parse the trailer header
func fixTrailer(header Header, te []string) (Header, error) {
- raw := header.Get("Trailer")
+ raw := header.get("Trailer")
if raw == "" {
return nil, nil
}
@@ -525,11 +521,11 @@ type body struct {
res *response // response writer for server requests, else nil
}
-// ErrBodyReadAfterClose is returned when reading a Request Body after
-// the body has been closed. This typically happens when the body is
+// ErrBodyReadAfterClose is returned when reading a Request or Response
+// Body after the body has been closed. This typically happens when the body is
// read after an HTTP Handler calls WriteHeader or Write on its
// ResponseWriter.
-var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed request Body")
+var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body")
func (b *body) Read(p []byte) (n int, err error) {
if b.closed {
@@ -567,14 +563,22 @@ func seeUpcomingDoubleCRLF(r *bufio.Reader) bool {
return false
}
+var errTrailerEOF = errors.New("http: unexpected EOF reading trailer")
+
func (b *body) readTrailer() error {
// The common case, since nobody uses trailers.
- buf, _ := b.r.Peek(2)
+ buf, err := b.r.Peek(2)
if bytes.Equal(buf, singleCRLF) {
b.r.ReadByte()
b.r.ReadByte()
return nil
}
+ if len(buf) < 2 {
+ return errTrailerEOF
+ }
+ if err != nil {
+ return err
+ }
// Make sure there's a header terminator coming up, to prevent
// a DoS with an unbounded size Trailer. It's not easy to
@@ -590,6 +594,9 @@ func (b *body) readTrailer() error {
hdr, err := textproto.NewReader(b.r).ReadMIMEHeader()
if err != nil {
+ if err == io.EOF {
+ return errTrailerEOF
+ }
return err
}
switch rr := b.hdr.(type) {
@@ -630,3 +637,18 @@ func (b *body) Close() error {
}
return nil
}
+
+// parseContentLength trims whitespace from s and returns -1 if no value
+// is set, or the value if it's >= 0.
+func parseContentLength(cl string) (int64, error) {
+ cl = strings.TrimSpace(cl)
+ if cl == "" {
+ return -1, nil
+ }
+ n, err := strconv.ParseInt(cl, 10, 64)
+ if err != nil || n < 0 {
+ return 0, &badStringError{"bad Content-Length", cl}
+ }
+ return n, nil
+
+}
diff --git a/src/pkg/net/http/transfer_test.go b/src/pkg/net/http/transfer_test.go
new file mode 100644
index 000000000..8627a374c
--- /dev/null
+++ b/src/pkg/net/http/transfer_test.go
@@ -0,0 +1,37 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http
+
+import (
+ "bufio"
+ "strings"
+ "testing"
+)
+
+func TestBodyReadBadTrailer(t *testing.T) {
+ b := &body{
+ Reader: strings.NewReader("foobar"),
+ hdr: true, // force reading the trailer
+ r: bufio.NewReader(strings.NewReader("")),
+ }
+ buf := make([]byte, 7)
+ n, err := b.Read(buf[:3])
+ got := string(buf[:n])
+ if got != "foo" || err != nil {
+ t.Fatalf(`first Read = %d (%q), %v; want 3 ("foo")`, n, got, err)
+ }
+
+ n, err = b.Read(buf[:])
+ got = string(buf[:n])
+ if got != "bar" || err != nil {
+ t.Fatalf(`second Read = %d (%q), %v; want 3 ("bar")`, n, got, err)
+ }
+
+ n, err = b.Read(buf[:])
+ got = string(buf[:n])
+ if err == nil {
+ t.Errorf("final Read was successful (%q), expected error from trailer read", got)
+ }
+}
diff --git a/src/pkg/net/http/transport.go b/src/pkg/net/http/transport.go
index 6efe191eb..685d7d56c 100644
--- a/src/pkg/net/http/transport.go
+++ b/src/pkg/net/http/transport.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// HTTP client implementation. See RFC 2616.
-//
+//
// This is the low-level Transport implementation of RoundTripper.
// The high-level interface is in client.go.
@@ -24,13 +24,14 @@ import (
"os"
"strings"
"sync"
+ "time"
)
// DefaultTransport is the default implementation of Transport and is
-// used by DefaultClient. It establishes a new network connection for
-// each call to Do and uses HTTP proxies as directed by the
-// $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy)
-// environment variables.
+// used by DefaultClient. It establishes network connections as needed
+// and caches them for reuse by subsequent calls. It uses HTTP proxies
+// as directed by the $HTTP_PROXY and $NO_PROXY (or $http_proxy and
+// $no_proxy) environment variables.
var DefaultTransport RoundTripper = &Transport{Proxy: ProxyFromEnvironment}
// DefaultMaxIdleConnsPerHost is the default value of Transport's
@@ -41,8 +42,11 @@ const DefaultMaxIdleConnsPerHost = 2
// https, and http proxies (for either http or https with CONNECT).
// Transport can also cache connections for future re-use.
type Transport struct {
- lk sync.Mutex
+ idleMu sync.Mutex
idleConn map[string][]*persistConn
+ reqMu sync.Mutex
+ reqConn map[*Request]*persistConn
+ altMu sync.RWMutex
altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper
// TODO: tunable on global max cached connections
@@ -68,9 +72,15 @@ type Transport struct {
DisableCompression bool
// MaxIdleConnsPerHost, if non-zero, controls the maximum idle
- // (keep-alive) to keep to keep per-host. If zero,
+ // (keep-alive) to keep per-host. If zero,
// DefaultMaxIdleConnsPerHost is used.
MaxIdleConnsPerHost int
+
+ // ResponseHeaderTimeout, if non-zero, specifies the amount of
+ // time to wait for a server's response headers after fully
+ // writing the request (including its body, if any). This
+ // time does not include the time to read the response body.
+ ResponseHeaderTimeout time.Duration
}
// ProxyFromEnvironment returns the URL of the proxy to use for a
@@ -88,7 +98,7 @@ func ProxyFromEnvironment(req *Request) (*url.URL, error) {
return nil, nil
}
proxyURL, err := url.Parse(proxy)
- if err != nil || proxyURL.Scheme == "" {
+ if err != nil || !strings.HasPrefix(proxyURL.Scheme, "http") {
if u, err := url.Parse("http://" + proxy); err == nil {
proxyURL = u
err = nil
@@ -131,17 +141,20 @@ func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {
return nil, errors.New("http: nil Request.Header")
}
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
- t.lk.Lock()
+ t.altMu.RLock()
var rt RoundTripper
if t.altProto != nil {
rt = t.altProto[req.URL.Scheme]
}
- t.lk.Unlock()
+ t.altMu.RUnlock()
if rt == nil {
return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
}
return rt.RoundTrip(req)
}
+ if req.URL.Host == "" {
+ return nil, errors.New("http: no Host in request URL")
+ }
treq := &transportRequest{Request: req}
cm, err := t.connectMethodForRequest(treq)
if err != nil {
@@ -170,8 +183,8 @@ func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) {
if scheme == "http" || scheme == "https" {
panic("protocol " + scheme + " already registered")
}
- t.lk.Lock()
- defer t.lk.Unlock()
+ t.altMu.Lock()
+ defer t.altMu.Unlock()
if t.altProto == nil {
t.altProto = make(map[string]RoundTripper)
}
@@ -186,17 +199,29 @@ func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) {
// a "keep-alive" state. It does not interrupt any connections currently
// in use.
func (t *Transport) CloseIdleConnections() {
- t.lk.Lock()
- defer t.lk.Unlock()
- if t.idleConn == nil {
+ t.idleMu.Lock()
+ m := t.idleConn
+ t.idleConn = nil
+ t.idleMu.Unlock()
+ if m == nil {
return
}
- for _, conns := range t.idleConn {
+ for _, conns := range m {
for _, pconn := range conns {
pconn.close()
}
}
- t.idleConn = make(map[string][]*persistConn)
+}
+
+// CancelRequest cancels an in-flight request by closing its
+// connection.
+func (t *Transport) CancelRequest(req *Request) {
+ t.reqMu.Lock()
+ pc := t.reqConn[req]
+ t.reqMu.Unlock()
+ if pc != nil {
+ pc.conn.Close()
+ }
}
//
@@ -242,8 +267,6 @@ func (cm *connectMethod) proxyAuth() string {
// If pconn is no longer needed or not in a good state, putIdleConn
// returns false.
func (t *Transport) putIdleConn(pconn *persistConn) bool {
- t.lk.Lock()
- defer t.lk.Unlock()
if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 {
pconn.close()
return false
@@ -256,21 +279,32 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool {
if max == 0 {
max = DefaultMaxIdleConnsPerHost
}
+ t.idleMu.Lock()
+ if t.idleConn == nil {
+ t.idleConn = make(map[string][]*persistConn)
+ }
if len(t.idleConn[key]) >= max {
+ t.idleMu.Unlock()
pconn.close()
return false
}
+ for _, exist := range t.idleConn[key] {
+ if exist == pconn {
+ log.Fatalf("dup idle pconn %p in freelist", pconn)
+ }
+ }
t.idleConn[key] = append(t.idleConn[key], pconn)
+ t.idleMu.Unlock()
return true
}
func (t *Transport) getIdleConn(cm *connectMethod) (pconn *persistConn) {
- t.lk.Lock()
- defer t.lk.Unlock()
+ key := cm.String()
+ t.idleMu.Lock()
+ defer t.idleMu.Unlock()
if t.idleConn == nil {
- t.idleConn = make(map[string][]*persistConn)
+ return nil
}
- key := cm.String()
for {
pconns, ok := t.idleConn[key]
if !ok {
@@ -289,7 +323,20 @@ func (t *Transport) getIdleConn(cm *connectMethod) (pconn *persistConn) {
return
}
}
- return
+ panic("unreachable")
+}
+
+func (t *Transport) setReqConn(r *Request, pc *persistConn) {
+ t.reqMu.Lock()
+ defer t.reqMu.Unlock()
+ if t.reqConn == nil {
+ t.reqConn = make(map[*Request]*persistConn)
+ }
+ if pc != nil {
+ t.reqConn[r] = pc
+ } else {
+ delete(t.reqConn, r)
+ }
}
func (t *Transport) dial(network, addr string) (c net.Conn, err error) {
@@ -323,6 +370,8 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) {
cacheKey: cm.String(),
conn: conn,
reqch: make(chan requestAndChan, 50),
+ writech: make(chan writeRequest, 50),
+ closech: make(chan struct{}),
}
switch {
@@ -365,7 +414,18 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) {
if cm.targetScheme == "https" {
// Initiate TLS and check remote host name against certificate.
- conn = tls.Client(conn, t.TLSClientConfig)
+ cfg := t.TLSClientConfig
+ if cfg == nil || cfg.ServerName == "" {
+ host := cm.tlsHost()
+ if cfg == nil {
+ cfg = &tls.Config{ServerName: host}
+ } else {
+ clone := *cfg // shallow clone
+ clone.ServerName = host
+ cfg = &clone
+ }
+ }
+ conn = tls.Client(conn, cfg)
if err = conn.(*tls.Conn).Handshake(); err != nil {
return nil, err
}
@@ -380,6 +440,7 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) {
pconn.br = bufio.NewReader(pconn.conn)
pconn.bw = bufio.NewWriter(pconn.conn)
go pconn.readLoop()
+ go pconn.writeLoop()
return pconn, nil
}
@@ -421,7 +482,15 @@ func useProxy(addr string) bool {
if hasPort(p) {
p = p[:strings.LastIndex(p, ":")]
}
- if addr == p || (p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:])) {
+ if addr == p {
+ return false
+ }
+ if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) {
+ // no_proxy ".foo.com" matches "bar.foo.com" or "foo.com"
+ return false
+ }
+ if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' {
+ // no_proxy "foo.com" matches "bar.foo.com"
return false
}
}
@@ -484,25 +553,28 @@ type persistConn struct {
t *Transport
cacheKey string // its connectMethod.String()
conn net.Conn
+ closed bool // whether conn has been closed
br *bufio.Reader // from conn
bw *bufio.Writer // to conn
- reqch chan requestAndChan // written by roundTrip(); read by readLoop()
+ reqch chan requestAndChan // written by roundTrip; read by readLoop
+ writech chan writeRequest // written by roundTrip; read by writeLoop
+ closech chan struct{} // broadcast close when readLoop (TCP connection) closes
isProxy bool
+ lk sync.Mutex // guards following 3 fields
+ numExpectedResponses int
+ broken bool // an error has happened on this connection; marked broken so it's not reused.
// mutateHeaderFunc is an optional func to modify extra
// headers on each outbound request before it's written. (the
// original Request given to RoundTrip is not modified)
mutateHeaderFunc func(Header)
-
- lk sync.Mutex // guards numExpectedResponses and broken
- numExpectedResponses int
- broken bool // an error has happened on this connection; marked broken so it's not reused.
}
func (pc *persistConn) isBroken() bool {
pc.lk.Lock()
- defer pc.lk.Unlock()
- return pc.broken
+ b := pc.broken
+ pc.lk.Unlock()
+ return b
}
var remoteSideClosedFunc func(error) bool // or nil to use default
@@ -518,6 +590,7 @@ func remoteSideClosed(err error) bool {
}
func (pc *persistConn) readLoop() {
+ defer close(pc.closech)
alive := true
var lastbody io.ReadCloser // last response body, if any, read on this connection
@@ -544,12 +617,16 @@ func (pc *persistConn) readLoop() {
lastbody.Close() // assumed idempotent
lastbody = nil
}
- resp, err := ReadResponse(pc.br, rc.req)
+
+ var resp *Response
+ if err == nil {
+ resp, err = ReadResponse(pc.br, rc.req)
+ }
+ hasBody := resp != nil && rc.req.Method != "HEAD" && resp.ContentLength != 0
if err != nil {
pc.close()
} else {
- hasBody := rc.req.Method != "HEAD" && resp.ContentLength != 0
if rc.addedGzip && hasBody && resp.Header.Get("Content-Encoding") == "gzip" {
resp.Header.Del("Content-Encoding")
resp.Header.Del("Content-Length")
@@ -569,31 +646,37 @@ func (pc *persistConn) readLoop() {
alive = false
}
- hasBody := resp != nil && resp.ContentLength != 0
var waitForBodyRead chan bool
- if alive {
- if hasBody {
- lastbody = resp.Body
- waitForBodyRead = make(chan bool)
- resp.Body.(*bodyEOFSignal).fn = func() {
- if !pc.t.putIdleConn(pc) {
- alive = false
- }
- waitForBodyRead <- true
+ if hasBody {
+ lastbody = resp.Body
+ waitForBodyRead = make(chan bool, 1)
+ resp.Body.(*bodyEOFSignal).fn = func(err error) {
+ alive1 := alive
+ if err != nil {
+ alive1 = false
}
- } else {
- // When there's no response body, we immediately
- // reuse the TCP connection (putIdleConn), but
- // we need to prevent ClientConn.Read from
- // closing the Response.Body on the next
- // loop, otherwise it might close the body
- // before the client code has had a chance to
- // read it (even though it'll just be 0, EOF).
- lastbody = nil
-
- if !pc.t.putIdleConn(pc) {
- alive = false
+ if alive1 && !pc.t.putIdleConn(pc) {
+ alive1 = false
+ }
+ if !alive1 || pc.isBroken() {
+ pc.close()
}
+ waitForBodyRead <- alive1
+ }
+ }
+
+ if alive && !hasBody {
+ // When there's no response body, we immediately
+ // reuse the TCP connection (putIdleConn), but
+ // we need to prevent ClientConn.Read from
+ // closing the Response.Body on the next
+ // loop, otherwise it might close the body
+ // before the client code has had a chance to
+ // read it (even though it'll just be 0, EOF).
+ lastbody = nil
+
+ if !pc.t.putIdleConn(pc) {
+ alive = false
}
}
@@ -602,7 +685,35 @@ func (pc *persistConn) readLoop() {
// Wait for the just-returned response body to be fully consumed
// before we race and peek on the underlying bufio reader.
if waitForBodyRead != nil {
- <-waitForBodyRead
+ alive = <-waitForBodyRead
+ }
+
+ pc.t.setReqConn(rc.req, nil)
+
+ if !alive {
+ pc.close()
+ }
+ }
+}
+
+func (pc *persistConn) writeLoop() {
+ for {
+ select {
+ case wr := <-pc.writech:
+ if pc.isBroken() {
+ wr.ch <- errors.New("http: can't write HTTP request on broken connection")
+ continue
+ }
+ err := wr.req.Request.write(pc.bw, pc.isProxy, wr.req.extra)
+ if err == nil {
+ err = pc.bw.Flush()
+ }
+ if err != nil {
+ pc.markBroken()
+ }
+ wr.ch <- err
+ case <-pc.closech:
+ return
}
}
}
@@ -622,9 +733,24 @@ type requestAndChan struct {
addedGzip bool
}
+// A writeRequest is sent by the readLoop's goroutine to the
+// writeLoop's goroutine to write a request while the read loop
+// concurrently waits on both the write response and the server's
+// reply.
+type writeRequest struct {
+ req *transportRequest
+ ch chan<- error
+}
+
func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) {
- if pc.mutateHeaderFunc != nil {
- pc.mutateHeaderFunc(req.extraHeaders())
+ pc.t.setReqConn(req.Request, pc)
+ pc.lk.Lock()
+ pc.numExpectedResponses++
+ headerFn := pc.mutateHeaderFunc
+ pc.lk.Unlock()
+
+ if headerFn != nil {
+ headerFn(req.extraHeaders())
}
// Ask for a compressed version if the caller didn't set their
@@ -633,34 +759,84 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
// requested it.
requestedGzip := false
if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" {
- // Request gzip only, not deflate. Deflate is ambiguous and
+ // Request gzip only, not deflate. Deflate is ambiguous and
// not as universally supported anyway.
// See: http://www.gzip.org/zlib/zlib_faq.html#faq38
requestedGzip = true
req.extraHeaders().Set("Accept-Encoding", "gzip")
}
- pc.lk.Lock()
- pc.numExpectedResponses++
- pc.lk.Unlock()
+ // Write the request concurrently with waiting for a response,
+ // in case the server decides to reply before reading our full
+ // request body.
+ writeErrCh := make(chan error, 1)
+ pc.writech <- writeRequest{req, writeErrCh}
- err = req.Request.write(pc.bw, pc.isProxy, req.extra)
- if err != nil {
- pc.close()
- return
+ resc := make(chan responseAndError, 1)
+ pc.reqch <- requestAndChan{req.Request, resc, requestedGzip}
+
+ var re responseAndError
+ var pconnDeadCh = pc.closech
+ var failTicker <-chan time.Time
+ var respHeaderTimer <-chan time.Time
+WaitResponse:
+ for {
+ select {
+ case err := <-writeErrCh:
+ if err != nil {
+ re = responseAndError{nil, err}
+ pc.close()
+ break WaitResponse
+ }
+ if d := pc.t.ResponseHeaderTimeout; d > 0 {
+ respHeaderTimer = time.After(d)
+ }
+ case <-pconnDeadCh:
+ // The persist connection is dead. This shouldn't
+ // usually happen (only with Connection: close responses
+ // with no response bodies), but if it does happen it
+ // means either a) the remote server hung up on us
+ // prematurely, or b) the readLoop sent us a response &
+ // closed its closech at roughly the same time, and we
+ // selected this case first, in which case a response
+ // might still be coming soon.
+ //
+ // We can't avoid the select race in b) by using a unbuffered
+ // resc channel instead, because then goroutines can
+ // leak if we exit due to other errors.
+ pconnDeadCh = nil // avoid spinning
+ failTicker = time.After(100 * time.Millisecond) // arbitrary time to wait for resc
+ case <-failTicker:
+ re = responseAndError{err: errors.New("net/http: transport closed before response was received")}
+ break WaitResponse
+ case <-respHeaderTimer:
+ pc.close()
+ re = responseAndError{err: errors.New("net/http: timeout awaiting response headers")}
+ break WaitResponse
+ case re = <-resc:
+ break WaitResponse
+ }
}
- pc.bw.Flush()
- ch := make(chan responseAndError, 1)
- pc.reqch <- requestAndChan{req.Request, ch, requestedGzip}
- re := <-ch
pc.lk.Lock()
pc.numExpectedResponses--
pc.lk.Unlock()
+ if re.err != nil {
+ pc.t.setReqConn(req.Request, nil)
+ }
return re.res, re.err
}
+// markBroken marks a connection as broken (so it's not reused).
+// It differs from close in that it doesn't close the underlying
+// connection for use when it's still being read.
+func (pc *persistConn) markBroken() {
+ pc.lk.Lock()
+ defer pc.lk.Unlock()
+ pc.broken = true
+}
+
func (pc *persistConn) close() {
pc.lk.Lock()
defer pc.lk.Unlock()
@@ -669,7 +845,10 @@ func (pc *persistConn) close() {
func (pc *persistConn) closeLocked() {
pc.broken = true
- pc.conn.Close()
+ if !pc.closed {
+ pc.conn.Close()
+ pc.closed = true
+ }
pc.mutateHeaderFunc = nil
}
@@ -687,43 +866,62 @@ func canonicalAddr(url *url.URL) string {
return addr
}
-func responseIsKeepAlive(res *Response) bool {
- // TODO: implement. for now just always shutting down the connection.
- return false
-}
-
// bodyEOFSignal wraps a ReadCloser but runs fn (if non-nil) at most
-// once, right before the final Read() or Close() call returns, but after
-// EOF has been seen.
+// once, right before its final (error-producing) Read or Close call
+// returns.
type bodyEOFSignal struct {
- body io.ReadCloser
- fn func()
- isClosed bool
+ body io.ReadCloser
+ mu sync.Mutex // guards closed, rerr and fn
+ closed bool // whether Close has been called
+ rerr error // sticky Read error
+ fn func(error) // error will be nil on Read io.EOF
}
func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
- n, err = es.body.Read(p)
- if es.isClosed && n > 0 {
- panic("http: unexpected bodyEOFSignal Read after Close; see issue 1725")
+ es.mu.Lock()
+ closed, rerr := es.closed, es.rerr
+ es.mu.Unlock()
+ if closed {
+ return 0, errors.New("http: read on closed response body")
+ }
+ if rerr != nil {
+ return 0, rerr
}
- if err == io.EOF && es.fn != nil {
- es.fn()
- es.fn = nil
+
+ n, err = es.body.Read(p)
+ if err != nil {
+ es.mu.Lock()
+ defer es.mu.Unlock()
+ if es.rerr == nil {
+ es.rerr = err
+ }
+ es.condfn(err)
}
return
}
-func (es *bodyEOFSignal) Close() (err error) {
- if es.isClosed {
+func (es *bodyEOFSignal) Close() error {
+ es.mu.Lock()
+ defer es.mu.Unlock()
+ if es.closed {
return nil
}
- es.isClosed = true
- err = es.body.Close()
- if err == nil && es.fn != nil {
- es.fn()
- es.fn = nil
+ es.closed = true
+ err := es.body.Close()
+ es.condfn(err)
+ return err
+}
+
+// caller must hold es.mu.
+func (es *bodyEOFSignal) condfn(err error) {
+ if es.fn == nil {
+ return
}
- return
+ if err == io.EOF {
+ err = nil
+ }
+ es.fn(err)
+ es.fn = nil
}
type readFirstCloseBoth struct {
diff --git a/src/pkg/net/http/transport_test.go b/src/pkg/net/http/transport_test.go
index a9e401de5..68010e68b 100644
--- a/src/pkg/net/http/transport_test.go
+++ b/src/pkg/net/http/transport_test.go
@@ -13,6 +13,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "net"
. "net/http"
"net/http/httptest"
"net/url"
@@ -20,6 +21,7 @@ import (
"runtime"
"strconv"
"strings"
+ "sync"
"testing"
"time"
)
@@ -35,14 +37,78 @@ var hostPortHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
w.Write([]byte(r.RemoteAddr))
})
+// testCloseConn is a net.Conn tracked by a testConnSet.
+type testCloseConn struct {
+ net.Conn
+ set *testConnSet
+}
+
+func (c *testCloseConn) Close() error {
+ c.set.remove(c)
+ return c.Conn.Close()
+}
+
+// testConnSet tracks a set of TCP connections and whether they've
+// been closed.
+type testConnSet struct {
+ t *testing.T
+ closed map[net.Conn]bool
+ list []net.Conn // in order created
+ mutex sync.Mutex
+}
+
+func (tcs *testConnSet) insert(c net.Conn) {
+ tcs.mutex.Lock()
+ defer tcs.mutex.Unlock()
+ tcs.closed[c] = false
+ tcs.list = append(tcs.list, c)
+}
+
+func (tcs *testConnSet) remove(c net.Conn) {
+ tcs.mutex.Lock()
+ defer tcs.mutex.Unlock()
+ tcs.closed[c] = true
+}
+
+// some tests use this to manage raw tcp connections for later inspection
+func makeTestDial(t *testing.T) (*testConnSet, func(n, addr string) (net.Conn, error)) {
+ connSet := &testConnSet{
+ t: t,
+ closed: make(map[net.Conn]bool),
+ }
+ dial := func(n, addr string) (net.Conn, error) {
+ c, err := net.Dial(n, addr)
+ if err != nil {
+ return nil, err
+ }
+ tc := &testCloseConn{c, connSet}
+ connSet.insert(tc)
+ return tc, nil
+ }
+ return connSet, dial
+}
+
+func (tcs *testConnSet) check(t *testing.T) {
+ tcs.mutex.Lock()
+ defer tcs.mutex.Unlock()
+
+ for i, c := range tcs.list {
+ if !tcs.closed[c] {
+ t.Errorf("TCP connection #%d, %p (of %d total) was not closed", i+1, c, len(tcs.list))
+ }
+ }
+}
+
// Two subsequent requests and verify their response is the same.
// The response from the server is our own IP:port
func TestTransportKeepAlives(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(hostPortHandler)
defer ts.Close()
for _, disableKeepAlive := range []bool{false, true} {
tr := &Transport{DisableKeepAlives: disableKeepAlive}
+ defer tr.CloseIdleConnections()
c := &Client{Transport: tr}
fetch := func(n int) string {
@@ -69,11 +135,16 @@ func TestTransportKeepAlives(t *testing.T) {
}
func TestTransportConnectionCloseOnResponse(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(hostPortHandler)
defer ts.Close()
+ connSet, testDial := makeTestDial(t)
+
for _, connectionClose := range []bool{false, true} {
- tr := &Transport{}
+ tr := &Transport{
+ Dial: testDial,
+ }
c := &Client{Transport: tr}
fetch := func(n int) string {
@@ -92,8 +163,8 @@ func TestTransportConnectionCloseOnResponse(t *testing.T) {
if err != nil {
t.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose, n, err)
}
- body, err := ioutil.ReadAll(res.Body)
defer res.Body.Close()
+ body, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose, n, err)
}
@@ -107,15 +178,24 @@ func TestTransportConnectionCloseOnResponse(t *testing.T) {
t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
connectionClose, bodiesDiffer, body1, body2)
}
+
+ tr.CloseIdleConnections()
}
+
+ connSet.check(t)
}
func TestTransportConnectionCloseOnRequest(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(hostPortHandler)
defer ts.Close()
+ connSet, testDial := makeTestDial(t)
+
for _, connectionClose := range []bool{false, true} {
- tr := &Transport{}
+ tr := &Transport{
+ Dial: testDial,
+ }
c := &Client{Transport: tr}
fetch := func(n int) string {
@@ -149,10 +229,15 @@ func TestTransportConnectionCloseOnRequest(t *testing.T) {
t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
connectionClose, bodiesDiffer, body1, body2)
}
+
+ tr.CloseIdleConnections()
}
+
+ connSet.check(t)
}
func TestTransportIdleCacheKeys(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(hostPortHandler)
defer ts.Close()
@@ -185,6 +270,7 @@ func TestTransportIdleCacheKeys(t *testing.T) {
}
func TestTransportMaxPerHostIdleConns(t *testing.T) {
+ defer checkLeakedTransports(t)
resch := make(chan string)
gotReq := make(chan bool)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -201,7 +287,7 @@ func TestTransportMaxPerHostIdleConns(t *testing.T) {
c := &Client{Transport: tr}
// Start 3 outstanding requests and wait for the server to get them.
- // Their responses will hang until we we write to resch, though.
+ // Their responses will hang until we write to resch, though.
donech := make(chan bool)
doReq := func() {
resp, err := c.Get(ts.URL)
@@ -253,6 +339,7 @@ func TestTransportMaxPerHostIdleConns(t *testing.T) {
}
func TestTransportServerClosingUnexpectedly(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(hostPortHandler)
defer ts.Close()
@@ -309,9 +396,9 @@ func TestTransportServerClosingUnexpectedly(t *testing.T) {
// Test for http://golang.org/issue/2616 (appropriate issue number)
// This fails pretty reliably with GOMAXPROCS=100 or something high.
func TestStressSurpriseServerCloses(t *testing.T) {
+ defer checkLeakedTransports(t)
if testing.Short() {
- t.Logf("skipping test in short mode")
- return
+ t.Skip("skipping test in short mode")
}
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("Content-Length", "5")
@@ -365,6 +452,7 @@ func TestStressSurpriseServerCloses(t *testing.T) {
// TestTransportHeadResponses verifies that we deal with Content-Lengths
// with no bodies properly
func TestTransportHeadResponses(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
if r.Method != "HEAD" {
panic("expected HEAD; got " + r.Method)
@@ -384,7 +472,7 @@ func TestTransportHeadResponses(t *testing.T) {
if e, g := "123", res.Header.Get("Content-Length"); e != g {
t.Errorf("loop %d: expected Content-Length header of %q, got %q", i, e, g)
}
- if e, g := int64(0), res.ContentLength; e != g {
+ if e, g := int64(123), res.ContentLength; e != g {
t.Errorf("loop %d: expected res.ContentLength of %v, got %v", i, e, g)
}
}
@@ -393,6 +481,7 @@ func TestTransportHeadResponses(t *testing.T) {
// TestTransportHeadChunkedResponse verifies that we ignore chunked transfer-encoding
// on responses to HEAD requests.
func TestTransportHeadChunkedResponse(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
if r.Method != "HEAD" {
panic("expected HEAD; got " + r.Method)
@@ -434,6 +523,7 @@ var roundTripTests = []struct {
// Test that the modification made to the Request by the RoundTripper is cleaned up
func TestRoundTripGzip(t *testing.T) {
+ defer checkLeakedTransports(t)
const responseBody = "test response body"
ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
accept := req.Header.Get("Accept-Encoding")
@@ -490,6 +580,7 @@ func TestRoundTripGzip(t *testing.T) {
}
func TestTransportGzip(t *testing.T) {
+ defer checkLeakedTransports(t)
const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
const nRandBytes = 1024 * 1024
ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
@@ -582,6 +673,7 @@ func TestTransportGzip(t *testing.T) {
}
func TestTransportProxy(t *testing.T) {
+ defer checkLeakedTransports(t)
ch := make(chan string, 1)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
ch <- "real server"
@@ -610,6 +702,7 @@ func TestTransportProxy(t *testing.T) {
// but checks that we don't recurse forever, and checks that
// Content-Encoding is removed.
func TestTransportGzipRecursive(t *testing.T) {
+ defer checkLeakedTransports(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("Content-Encoding", "gzip")
w.Write(rgz)
@@ -636,6 +729,7 @@ func TestTransportGzipRecursive(t *testing.T) {
// tests that persistent goroutine connections shut down when no longer desired.
func TestTransportPersistConnLeak(t *testing.T) {
+ defer checkLeakedTransports(t)
gotReqCh := make(chan bool)
unblockCh := make(chan bool)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -698,8 +792,49 @@ func TestTransportPersistConnLeak(t *testing.T) {
}
}
+// golang.org/issue/4531: Transport leaks goroutines when
+// request.ContentLength is explicitly short
+func TestTransportPersistConnLeakShortBody(t *testing.T) {
+ defer checkLeakedTransports(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ }))
+ defer ts.Close()
+
+ tr := &Transport{}
+ c := &Client{Transport: tr}
+
+ n0 := runtime.NumGoroutine()
+ body := []byte("Hello")
+ for i := 0; i < 20; i++ {
+ req, err := NewRequest("POST", ts.URL, bytes.NewReader(body))
+ if err != nil {
+ t.Fatal(err)
+ }
+ req.ContentLength = int64(len(body) - 2) // explicitly short
+ _, err = c.Do(req)
+ if err == nil {
+ t.Fatal("Expect an error from writing too long of a body.")
+ }
+ }
+ nhigh := runtime.NumGoroutine()
+ tr.CloseIdleConnections()
+ time.Sleep(50 * time.Millisecond)
+ runtime.GC()
+ nfinal := runtime.NumGoroutine()
+
+ growth := nfinal - n0
+
+ // We expect 0 or 1 extra goroutine, empirically. Allow up to 5.
+ // Previously we were leaking one per numReq.
+ t.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0, nhigh, nfinal, growth)
+ if int(growth) > 5 {
+ t.Error("too many new goroutines")
+ }
+}
+
// This used to crash; http://golang.org/issue/3266
func TestTransportIdleConnCrash(t *testing.T) {
+ defer checkLeakedTransports(t)
tr := &Transport{}
c := &Client{Transport: tr}
@@ -724,6 +859,361 @@ func TestTransportIdleConnCrash(t *testing.T) {
<-didreq
}
+// Test that the transport doesn't close the TCP connection early,
+// before the response body has been read. This was a regression
+// which sadly lacked a triggering test. The large response body made
+// the old race easier to trigger.
+func TestIssue3644(t *testing.T) {
+ defer checkLeakedTransports(t)
+ const numFoos = 5000
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Connection", "close")
+ for i := 0; i < numFoos; i++ {
+ w.Write([]byte("foo "))
+ }
+ }))
+ defer ts.Close()
+ tr := &Transport{}
+ c := &Client{Transport: tr}
+ res, err := c.Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+ bs, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(bs) != numFoos*len("foo ") {
+ t.Errorf("unexpected response length")
+ }
+}
+
+// Test that a client receives a server's reply, even if the server doesn't read
+// the entire request body.
+func TestIssue3595(t *testing.T) {
+ defer checkLeakedTransports(t)
+ const deniedMsg = "sorry, denied."
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ Error(w, deniedMsg, StatusUnauthorized)
+ }))
+ defer ts.Close()
+ tr := &Transport{}
+ c := &Client{Transport: tr}
+ res, err := c.Post(ts.URL, "application/octet-stream", neverEnding('a'))
+ if err != nil {
+ t.Errorf("Post: %v", err)
+ return
+ }
+ got, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatalf("Body ReadAll: %v", err)
+ }
+ if !strings.Contains(string(got), deniedMsg) {
+ t.Errorf("Known bug: response %q does not contain %q", got, deniedMsg)
+ }
+}
+
+// From http://golang.org/issue/4454 ,
+// "client fails to handle requests with no body and chunked encoding"
+func TestChunkedNoContent(t *testing.T) {
+ defer checkLeakedTransports(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.WriteHeader(StatusNoContent)
+ }))
+ defer ts.Close()
+
+ for _, closeBody := range []bool{true, false} {
+ c := &Client{Transport: &Transport{}}
+ const n = 4
+ for i := 1; i <= n; i++ {
+ res, err := c.Get(ts.URL)
+ if err != nil {
+ t.Errorf("closingBody=%v, req %d/%d: %v", closeBody, i, n, err)
+ } else {
+ if closeBody {
+ res.Body.Close()
+ }
+ }
+ }
+ }
+}
+
+func TestTransportConcurrency(t *testing.T) {
+ defer checkLeakedTransports(t)
+ const maxProcs = 16
+ const numReqs = 500
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ fmt.Fprintf(w, "%v", r.FormValue("echo"))
+ }))
+ defer ts.Close()
+ tr := &Transport{}
+ c := &Client{Transport: tr}
+ reqs := make(chan string)
+ defer close(reqs)
+
+ var wg sync.WaitGroup
+ wg.Add(numReqs)
+ for i := 0; i < maxProcs*2; i++ {
+ go func() {
+ for req := range reqs {
+ res, err := c.Get(ts.URL + "/?echo=" + req)
+ if err != nil {
+ t.Errorf("error on req %s: %v", req, err)
+ wg.Done()
+ continue
+ }
+ all, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Errorf("read error on req %s: %v", req, err)
+ wg.Done()
+ continue
+ }
+ if string(all) != req {
+ t.Errorf("body of req %s = %q; want %q", req, all, req)
+ }
+ wg.Done()
+ res.Body.Close()
+ }
+ }()
+ }
+ for i := 0; i < numReqs; i++ {
+ reqs <- fmt.Sprintf("request-%d", i)
+ }
+ wg.Wait()
+}
+
+func TestIssue4191_InfiniteGetTimeout(t *testing.T) {
+ defer checkLeakedTransports(t)
+ const debug = false
+ mux := NewServeMux()
+ mux.HandleFunc("/get", func(w ResponseWriter, r *Request) {
+ io.Copy(w, neverEnding('a'))
+ })
+ ts := httptest.NewServer(mux)
+ timeout := 100 * time.Millisecond
+
+ client := &Client{
+ Transport: &Transport{
+ Dial: func(n, addr string) (net.Conn, error) {
+ conn, err := net.Dial(n, addr)
+ if err != nil {
+ return nil, err
+ }
+ conn.SetDeadline(time.Now().Add(timeout))
+ if debug {
+ conn = NewLoggingConn("client", conn)
+ }
+ return conn, nil
+ },
+ DisableKeepAlives: true,
+ },
+ }
+
+ getFailed := false
+ nRuns := 5
+ if testing.Short() {
+ nRuns = 1
+ }
+ for i := 0; i < nRuns; i++ {
+ if debug {
+ println("run", i+1, "of", nRuns)
+ }
+ sres, err := client.Get(ts.URL + "/get")
+ if err != nil {
+ if !getFailed {
+ // Make the timeout longer, once.
+ getFailed = true
+ t.Logf("increasing timeout")
+ i--
+ timeout *= 10
+ continue
+ }
+ t.Errorf("Error issuing GET: %v", err)
+ break
+ }
+ _, err = io.Copy(ioutil.Discard, sres.Body)
+ if err == nil {
+ t.Errorf("Unexpected successful copy")
+ break
+ }
+ }
+ if debug {
+ println("tests complete; waiting for handlers to finish")
+ }
+ ts.Close()
+}
+
+func TestIssue4191_InfiniteGetToPutTimeout(t *testing.T) {
+ defer checkLeakedTransports(t)
+ const debug = false
+ mux := NewServeMux()
+ mux.HandleFunc("/get", func(w ResponseWriter, r *Request) {
+ io.Copy(w, neverEnding('a'))
+ })
+ mux.HandleFunc("/put", func(w ResponseWriter, r *Request) {
+ defer r.Body.Close()
+ io.Copy(ioutil.Discard, r.Body)
+ })
+ ts := httptest.NewServer(mux)
+ timeout := 100 * time.Millisecond
+
+ client := &Client{
+ Transport: &Transport{
+ Dial: func(n, addr string) (net.Conn, error) {
+ conn, err := net.Dial(n, addr)
+ if err != nil {
+ return nil, err
+ }
+ conn.SetDeadline(time.Now().Add(timeout))
+ if debug {
+ conn = NewLoggingConn("client", conn)
+ }
+ return conn, nil
+ },
+ DisableKeepAlives: true,
+ },
+ }
+
+ getFailed := false
+ nRuns := 5
+ if testing.Short() {
+ nRuns = 1
+ }
+ for i := 0; i < nRuns; i++ {
+ if debug {
+ println("run", i+1, "of", nRuns)
+ }
+ sres, err := client.Get(ts.URL + "/get")
+ if err != nil {
+ if !getFailed {
+ // Make the timeout longer, once.
+ getFailed = true
+ t.Logf("increasing timeout")
+ i--
+ timeout *= 10
+ continue
+ }
+ t.Errorf("Error issuing GET: %v", err)
+ break
+ }
+ req, _ := NewRequest("PUT", ts.URL+"/put", sres.Body)
+ _, err = client.Do(req)
+ if err == nil {
+ sres.Body.Close()
+ t.Errorf("Unexpected successful PUT")
+ break
+ }
+ sres.Body.Close()
+ }
+ if debug {
+ println("tests complete; waiting for handlers to finish")
+ }
+ ts.Close()
+}
+
+func TestTransportResponseHeaderTimeout(t *testing.T) {
+ defer checkLeakedTransports(t)
+ if testing.Short() {
+ t.Skip("skipping timeout test in -short mode")
+ }
+ mux := NewServeMux()
+ mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) {})
+ mux.HandleFunc("/slow", func(w ResponseWriter, r *Request) {
+ time.Sleep(2 * time.Second)
+ })
+ ts := httptest.NewServer(mux)
+ defer ts.Close()
+
+ tr := &Transport{
+ ResponseHeaderTimeout: 500 * time.Millisecond,
+ }
+ defer tr.CloseIdleConnections()
+ c := &Client{Transport: tr}
+
+ tests := []struct {
+ path string
+ want int
+ wantErr string
+ }{
+ {path: "/fast", want: 200},
+ {path: "/slow", wantErr: "timeout awaiting response headers"},
+ {path: "/fast", want: 200},
+ }
+ for i, tt := range tests {
+ res, err := c.Get(ts.URL + tt.path)
+ if err != nil {
+ if strings.Contains(err.Error(), tt.wantErr) {
+ continue
+ }
+ t.Errorf("%d. unexpected error: %v", i, err)
+ continue
+ }
+ if tt.wantErr != "" {
+ t.Errorf("%d. no error. expected error: %v", i, tt.wantErr)
+ continue
+ }
+ if res.StatusCode != tt.want {
+ t.Errorf("%d for path %q status = %d; want %d", i, tt.path, res.StatusCode, tt.want)
+ }
+ }
+}
+
+func TestTransportCancelRequest(t *testing.T) {
+ defer checkLeakedTransports(t)
+ if testing.Short() {
+ t.Skip("skipping test in -short mode")
+ }
+ unblockc := make(chan bool)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ fmt.Fprintf(w, "Hello")
+ w.(Flusher).Flush() // send headers and some body
+ <-unblockc
+ }))
+ defer ts.Close()
+ defer close(unblockc)
+
+ tr := &Transport{}
+ defer tr.CloseIdleConnections()
+ c := &Client{Transport: tr}
+
+ req, _ := NewRequest("GET", ts.URL, nil)
+ res, err := c.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ go func() {
+ time.Sleep(1 * time.Second)
+ tr.CancelRequest(req)
+ }()
+ t0 := time.Now()
+ body, err := ioutil.ReadAll(res.Body)
+ d := time.Since(t0)
+
+ if err == nil {
+ t.Error("expected an error reading the body")
+ }
+ if string(body) != "Hello" {
+ t.Errorf("Body = %q; want Hello", body)
+ }
+ if d < 500*time.Millisecond {
+ t.Errorf("expected ~1 second delay; got %v", d)
+ }
+ // Verify no outstanding requests after readLoop/writeLoop
+ // goroutines shut down.
+ for tries := 3; tries > 0; tries-- {
+ n := tr.NumPendingRequestsForTesting()
+ if n == 0 {
+ break
+ }
+ time.Sleep(100 * time.Millisecond)
+ if tries == 1 {
+ t.Errorf("pending requests = %d; want 0", n)
+ }
+ }
+}
+
type fooProto struct{}
func (fooProto) RoundTrip(req *Request) (*Response, error) {
@@ -737,6 +1227,7 @@ func (fooProto) RoundTrip(req *Request) (*Response, error) {
}
func TestTransportAltProto(t *testing.T) {
+ defer checkLeakedTransports(t)
tr := &Transport{}
c := &Client{Transport: tr}
tr.RegisterProtocol("foo", fooProto{})
@@ -754,15 +1245,58 @@ func TestTransportAltProto(t *testing.T) {
}
}
-var proxyFromEnvTests = []struct {
+func TestTransportNoHost(t *testing.T) {
+ defer checkLeakedTransports(t)
+ tr := &Transport{}
+ _, err := tr.RoundTrip(&Request{
+ Header: make(Header),
+ URL: &url.URL{
+ Scheme: "http",
+ },
+ })
+ want := "http: no Host in request URL"
+ if got := fmt.Sprint(err); got != want {
+ t.Errorf("error = %v; want %q", err, want)
+ }
+}
+
+type proxyFromEnvTest struct {
+ req string // URL to fetch; blank means "http://example.com"
env string
- wanturl string
+ noenv string
+ want string
wanterr error
-}{
- {"127.0.0.1:8080", "http://127.0.0.1:8080", nil},
- {"http://127.0.0.1:8080", "http://127.0.0.1:8080", nil},
- {"https://127.0.0.1:8080", "https://127.0.0.1:8080", nil},
- {"", "<nil>", nil},
+}
+
+func (t proxyFromEnvTest) String() string {
+ var buf bytes.Buffer
+ if t.env != "" {
+ fmt.Fprintf(&buf, "http_proxy=%q", t.env)
+ }
+ if t.noenv != "" {
+ fmt.Fprintf(&buf, " no_proxy=%q", t.noenv)
+ }
+ req := "http://example.com"
+ if t.req != "" {
+ req = t.req
+ }
+ fmt.Fprintf(&buf, " req=%q", req)
+ return strings.TrimSpace(buf.String())
+}
+
+var proxyFromEnvTests = []proxyFromEnvTest{
+ {env: "127.0.0.1:8080", want: "http://127.0.0.1:8080"},
+ {env: "cache.corp.example.com:1234", want: "http://cache.corp.example.com:1234"},
+ {env: "cache.corp.example.com", want: "http://cache.corp.example.com"},
+ {env: "https://cache.corp.example.com", want: "https://cache.corp.example.com"},
+ {env: "http://127.0.0.1:8080", want: "http://127.0.0.1:8080"},
+ {env: "https://127.0.0.1:8080", want: "https://127.0.0.1:8080"},
+ {want: "<nil>"},
+ {noenv: "example.com", req: "http://example.com/", env: "proxy", want: "<nil>"},
+ {noenv: ".example.com", req: "http://example.com/", env: "proxy", want: "<nil>"},
+ {noenv: "ample.com", req: "http://example.com/", env: "proxy", want: "http://proxy"},
+ {noenv: "example.com", req: "http://foo.example.com/", env: "proxy", want: "<nil>"},
+ {noenv: ".foo.com", req: "http://example.com/", env: "proxy", want: "http://proxy"},
}
func TestProxyFromEnvironment(t *testing.T) {
@@ -770,16 +1304,21 @@ func TestProxyFromEnvironment(t *testing.T) {
os.Setenv("http_proxy", "")
os.Setenv("NO_PROXY", "")
os.Setenv("no_proxy", "")
- for i, tt := range proxyFromEnvTests {
+ for _, tt := range proxyFromEnvTests {
os.Setenv("HTTP_PROXY", tt.env)
- req, _ := NewRequest("GET", "http://example.com", nil)
+ os.Setenv("NO_PROXY", tt.noenv)
+ reqURL := tt.req
+ if reqURL == "" {
+ reqURL = "http://example.com"
+ }
+ req, _ := NewRequest("GET", reqURL, nil)
url, err := ProxyFromEnvironment(req)
if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e {
- t.Errorf("%d. got error = %q, want %q", i, g, e)
+ t.Errorf("%v: got error = %q, want %q", tt, g, e)
continue
}
- if got := fmt.Sprintf("%s", url); got != tt.wanturl {
- t.Errorf("%d. got URL = %q, want %q", i, url, tt.wanturl)
+ if got := fmt.Sprintf("%s", url); got != tt.want {
+ t.Errorf("%v: got URL = %q, want %q", tt, url, tt.want)
}
}
}
diff --git a/src/pkg/net/http/z_last_test.go b/src/pkg/net/http/z_last_test.go
new file mode 100644
index 000000000..44095a8d9
--- /dev/null
+++ b/src/pkg/net/http/z_last_test.go
@@ -0,0 +1,60 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package http_test
+
+import (
+ "net/http"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+)
+
+// Verify the other tests didn't leave any goroutines running.
+// This is in a file named z_last_test.go so it sorts at the end.
+func TestGoroutinesRunning(t *testing.T) {
+ n := runtime.NumGoroutine()
+ t.Logf("num goroutines = %d", n)
+ if n > 20 {
+ // Currently 14 on Linux (blocked in epoll_wait,
+ // waiting for on fds that are closed?), but give some
+ // slop for now.
+ buf := make([]byte, 1<<20)
+ buf = buf[:runtime.Stack(buf, true)]
+ t.Errorf("Too many goroutines:\n%s", buf)
+ }
+}
+
+func checkLeakedTransports(t *testing.T) {
+ http.DefaultTransport.(*http.Transport).CloseIdleConnections()
+ if testing.Short() {
+ return
+ }
+ buf := make([]byte, 1<<20)
+ var stacks string
+ var bad string
+ badSubstring := map[string]string{
+ ").readLoop(": "a Transport",
+ ").writeLoop(": "a Transport",
+ "created by net/http/httptest.(*Server).Start": "an httptest.Server",
+ "timeoutHandler": "a TimeoutHandler",
+ }
+ for i := 0; i < 4; i++ {
+ bad = ""
+ stacks = string(buf[:runtime.Stack(buf, true)])
+ for substr, what := range badSubstring {
+ if strings.Contains(stacks, substr) {
+ bad = what
+ }
+ }
+ if bad == "" {
+ return
+ }
+ // Bad stuff found, but goroutines might just still be
+ // shutting down, so give it some time.
+ time.Sleep(250 * time.Millisecond)
+ }
+ t.Errorf("Test appears to have leaked %s:\n%s", bad, stacks)
+}
diff --git a/src/pkg/net/interface.go b/src/pkg/net/interface.go
index ee23570a9..0713e9cd6 100644
--- a/src/pkg/net/interface.go
+++ b/src/pkg/net/interface.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Network interface identification
-
package net
import "errors"
@@ -66,7 +64,7 @@ func (ifi *Interface) Addrs() ([]Addr, error) {
if ifi == nil {
return nil, errInvalidInterface
}
- return interfaceAddrTable(ifi.Index)
+ return interfaceAddrTable(ifi)
}
// MulticastAddrs returns multicast, joined group addresses for
@@ -75,7 +73,7 @@ func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
if ifi == nil {
return nil, errInvalidInterface
}
- return interfaceMulticastAddrTable(ifi.Index)
+ return interfaceMulticastAddrTable(ifi)
}
// Interfaces returns a list of the system's network interfaces.
@@ -86,7 +84,7 @@ func Interfaces() ([]Interface, error) {
// InterfaceAddrs returns a list of the system's network interface
// addresses.
func InterfaceAddrs() ([]Addr, error) {
- return interfaceAddrTable(0)
+ return interfaceAddrTable(nil)
}
// InterfaceByIndex returns the interface specified by index.
@@ -98,8 +96,14 @@ func InterfaceByIndex(index int) (*Interface, error) {
if err != nil {
return nil, err
}
+ return interfaceByIndex(ift, index)
+}
+
+func interfaceByIndex(ift []Interface, index int) (*Interface, error) {
for _, ifi := range ift {
- return &ifi, nil
+ if index == ifi.Index {
+ return &ifi, nil
+ }
}
return nil, errNoSuchInterface
}
diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go
index 7f090d8d4..f58065a85 100644
--- a/src/pkg/net/interface_bsd.go
+++ b/src/pkg/net/interface_bsd.go
@@ -4,8 +4,6 @@
// +build darwin freebsd netbsd openbsd
-// Network interface identification for BSD variants
-
package net
import (
@@ -22,57 +20,60 @@ func interfaceTable(ifindex int) ([]Interface, error) {
if err != nil {
return nil, os.NewSyscallError("route rib", err)
}
-
msgs, err := syscall.ParseRoutingMessage(tab)
if err != nil {
return nil, os.NewSyscallError("route message", err)
}
+ return parseInterfaceTable(ifindex, msgs)
+}
+func parseInterfaceTable(ifindex int, msgs []syscall.RoutingMessage) ([]Interface, error) {
var ift []Interface
+loop:
for _, m := range msgs {
- switch v := m.(type) {
+ switch m := m.(type) {
case *syscall.InterfaceMessage:
- if ifindex == 0 || ifindex == int(v.Header.Index) {
- ifi, err := newLink(v)
+ if ifindex == 0 || ifindex == int(m.Header.Index) {
+ ifi, err := newLink(m)
if err != nil {
return nil, err
}
- ift = append(ift, ifi...)
+ ift = append(ift, *ifi)
+ if ifindex == int(m.Header.Index) {
+ break loop
+ }
}
}
}
return ift, nil
}
-func newLink(m *syscall.InterfaceMessage) ([]Interface, error) {
+func newLink(m *syscall.InterfaceMessage) (*Interface, error) {
sas, err := syscall.ParseRoutingSockaddr(m)
if err != nil {
return nil, os.NewSyscallError("route sockaddr", err)
}
-
- var ift []Interface
- for _, s := range sas {
- switch v := s.(type) {
+ ifi := &Interface{Index: int(m.Header.Index), Flags: linkFlags(m.Header.Flags)}
+ for _, sa := range sas {
+ switch sa := sa.(type) {
case *syscall.SockaddrDatalink:
// NOTE: SockaddrDatalink.Data is minimum work area,
// can be larger.
- m.Data = m.Data[unsafe.Offsetof(v.Data):]
- ifi := Interface{Index: int(m.Header.Index), Flags: linkFlags(m.Header.Flags)}
+ m.Data = m.Data[unsafe.Offsetof(sa.Data):]
var name [syscall.IFNAMSIZ]byte
- for i := 0; i < int(v.Nlen); i++ {
+ for i := 0; i < int(sa.Nlen); i++ {
name[i] = byte(m.Data[i])
}
- ifi.Name = string(name[:v.Nlen])
+ ifi.Name = string(name[:sa.Nlen])
ifi.MTU = int(m.Header.Data.Mtu)
- addr := make([]byte, v.Alen)
- for i := 0; i < int(v.Alen); i++ {
- addr[i] = byte(m.Data[int(v.Nlen)+i])
+ addr := make([]byte, sa.Alen)
+ for i := 0; i < int(sa.Alen); i++ {
+ addr[i] = byte(m.Data[int(sa.Nlen)+i])
}
- ifi.HardwareAddr = addr[:v.Alen]
- ift = append(ift, ifi)
+ ifi.HardwareAddr = addr[:sa.Alen]
}
}
- return ift, nil
+ return ifi, nil
}
func linkFlags(rawFlags int32) Flags {
@@ -95,68 +96,87 @@ func linkFlags(rawFlags int32) Flags {
return f
}
-// If the ifindex is zero, interfaceAddrTable returns addresses
-// for all network interfaces. Otherwise it returns addresses
-// for a specific interface.
-func interfaceAddrTable(ifindex int) ([]Addr, error) {
- tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex)
+// If the ifi is nil, interfaceAddrTable returns addresses for all
+// network interfaces. Otherwise it returns addresses for a specific
+// interface.
+func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
+ index := 0
+ if ifi != nil {
+ index = ifi.Index
+ }
+ tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, index)
if err != nil {
return nil, os.NewSyscallError("route rib", err)
}
-
msgs, err := syscall.ParseRoutingMessage(tab)
if err != nil {
return nil, os.NewSyscallError("route message", err)
}
-
+ var ift []Interface
+ if index == 0 {
+ ift, err = parseInterfaceTable(index, msgs)
+ if err != nil {
+ return nil, err
+ }
+ }
var ifat []Addr
for _, m := range msgs {
- switch v := m.(type) {
+ switch m := m.(type) {
case *syscall.InterfaceAddrMessage:
- if ifindex == 0 || ifindex == int(v.Header.Index) {
- ifa, err := newAddr(v)
+ if index == 0 || index == int(m.Header.Index) {
+ if index == 0 {
+ var err error
+ ifi, err = interfaceByIndex(ift, int(m.Header.Index))
+ if err != nil {
+ return nil, err
+ }
+ }
+ ifa, err := newAddr(ifi, m)
if err != nil {
return nil, err
}
- ifat = append(ifat, ifa)
+ if ifa != nil {
+ ifat = append(ifat, ifa)
+ }
}
}
}
return ifat, nil
}
-func newAddr(m *syscall.InterfaceAddrMessage) (Addr, error) {
+func newAddr(ifi *Interface, m *syscall.InterfaceAddrMessage) (Addr, error) {
sas, err := syscall.ParseRoutingSockaddr(m)
if err != nil {
return nil, os.NewSyscallError("route sockaddr", err)
}
-
ifa := &IPNet{}
- for i, s := range sas {
- switch v := s.(type) {
+ for i, sa := range sas {
+ switch sa := sa.(type) {
case *syscall.SockaddrInet4:
switch i {
case 0:
- ifa.Mask = IPv4Mask(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])
+ ifa.Mask = IPv4Mask(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])
case 1:
- ifa.IP = IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])
+ ifa.IP = IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])
}
case *syscall.SockaddrInet6:
switch i {
case 0:
ifa.Mask = make(IPMask, IPv6len)
- copy(ifa.Mask, v.Addr[:])
+ copy(ifa.Mask, sa.Addr[:])
case 1:
ifa.IP = make(IP, IPv6len)
- copy(ifa.IP, v.Addr[:])
+ copy(ifa.IP, sa.Addr[:])
// NOTE: KAME based IPv6 protcol stack usually embeds
// the interface index in the interface-local or link-
// local address as the kernel-internal form.
if ifa.IP.IsLinkLocalUnicast() {
- // remove embedded scope zone ID
+ ifa.Zone = ifi.Name
ifa.IP[2], ifa.IP[3] = 0, 0
}
}
+ default: // Sockaddrs contain syscall.SockaddrDatalink on NetBSD
+ return nil, nil
}
}
return ifa, nil
diff --git a/src/pkg/net/interface_bsd_test.go b/src/pkg/net/interface_bsd_test.go
new file mode 100644
index 000000000..aa1141903
--- /dev/null
+++ b/src/pkg/net/interface_bsd_test.go
@@ -0,0 +1,52 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd netbsd openbsd
+
+package net
+
+import (
+ "fmt"
+ "os/exec"
+)
+
+func (ti *testInterface) setBroadcast(suffix int) error {
+ ti.name = fmt.Sprintf("vlan%d", suffix)
+ xname, err := exec.LookPath("ifconfig")
+ if err != nil {
+ return err
+ }
+ ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+ Path: xname,
+ Args: []string{"ifconfig", ti.name, "create"},
+ })
+ ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{
+ Path: xname,
+ Args: []string{"ifconfig", ti.name, "destroy"},
+ })
+ return nil
+}
+
+func (ti *testInterface) setPointToPoint(suffix int, local, remote string) error {
+ ti.name = fmt.Sprintf("gif%d", suffix)
+ ti.local = local
+ ti.remote = remote
+ xname, err := exec.LookPath("ifconfig")
+ if err != nil {
+ return err
+ }
+ ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+ Path: xname,
+ Args: []string{"ifconfig", ti.name, "create"},
+ })
+ ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+ Path: xname,
+ Args: []string{"ifconfig", ti.name, "inet", ti.local, ti.remote},
+ })
+ ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{
+ Path: xname,
+ Args: []string{"ifconfig", ti.name, "destroy"},
+ })
+ return nil
+}
diff --git a/src/pkg/net/interface_darwin.go b/src/pkg/net/interface_darwin.go
index 0b5fb5fb9..83e483ba2 100644
--- a/src/pkg/net/interface_darwin.go
+++ b/src/pkg/net/interface_darwin.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Network interface identification for Darwin
-
package net
import (
@@ -11,26 +9,23 @@ import (
"syscall"
)
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces. Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
- tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST2, ifindex)
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
+ tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST2, ifi.Index)
if err != nil {
return nil, os.NewSyscallError("route rib", err)
}
-
msgs, err := syscall.ParseRoutingMessage(tab)
if err != nil {
return nil, os.NewSyscallError("route message", err)
}
-
var ifmat []Addr
for _, m := range msgs {
- switch v := m.(type) {
+ switch m := m.(type) {
case *syscall.InterfaceMulticastAddrMessage:
- if ifindex == 0 || ifindex == int(v.Header.Index) {
- ifma, err := newMulticastAddr(v)
+ if ifi.Index == int(m.Header.Index) {
+ ifma, err := newMulticastAddr(ifi, m)
if err != nil {
return nil, err
}
@@ -41,27 +36,25 @@ func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
return ifmat, nil
}
-func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) {
+func newMulticastAddr(ifi *Interface, m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) {
sas, err := syscall.ParseRoutingSockaddr(m)
if err != nil {
return nil, os.NewSyscallError("route sockaddr", err)
}
-
var ifmat []Addr
- for _, s := range sas {
- switch v := s.(type) {
+ for _, sa := range sas {
+ switch sa := sa.(type) {
case *syscall.SockaddrInet4:
- ifma := &IPAddr{IP: IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])}
+ ifma := &IPAddr{IP: IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])}
ifmat = append(ifmat, ifma.toAddr())
case *syscall.SockaddrInet6:
ifma := &IPAddr{IP: make(IP, IPv6len)}
- copy(ifma.IP, v.Addr[:])
+ copy(ifma.IP, sa.Addr[:])
// NOTE: KAME based IPv6 protcol stack usually embeds
// the interface index in the interface-local or link-
// local address as the kernel-internal form.
- if ifma.IP.IsInterfaceLocalMulticast() ||
- ifma.IP.IsLinkLocalMulticast() {
- // remove embedded scope zone ID
+ if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() {
+ ifma.Zone = ifi.Name
ifma.IP[2], ifma.IP[3] = 0, 0
}
ifmat = append(ifmat, ifma.toAddr())
diff --git a/src/pkg/net/interface_freebsd.go b/src/pkg/net/interface_freebsd.go
index 3cba28fc6..1bf5ae72b 100644
--- a/src/pkg/net/interface_freebsd.go
+++ b/src/pkg/net/interface_freebsd.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Network interface identification for FreeBSD
-
package net
import (
@@ -11,26 +9,23 @@ import (
"syscall"
)
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces. Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
- tab, err := syscall.RouteRIB(syscall.NET_RT_IFMALIST, ifindex)
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
+ tab, err := syscall.RouteRIB(syscall.NET_RT_IFMALIST, ifi.Index)
if err != nil {
return nil, os.NewSyscallError("route rib", err)
}
-
msgs, err := syscall.ParseRoutingMessage(tab)
if err != nil {
return nil, os.NewSyscallError("route message", err)
}
-
var ifmat []Addr
for _, m := range msgs {
- switch v := m.(type) {
+ switch m := m.(type) {
case *syscall.InterfaceMulticastAddrMessage:
- if ifindex == 0 || ifindex == int(v.Header.Index) {
- ifma, err := newMulticastAddr(v)
+ if ifi.Index == int(m.Header.Index) {
+ ifma, err := newMulticastAddr(ifi, m)
if err != nil {
return nil, err
}
@@ -41,27 +36,25 @@ func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
return ifmat, nil
}
-func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) {
+func newMulticastAddr(ifi *Interface, m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) {
sas, err := syscall.ParseRoutingSockaddr(m)
if err != nil {
return nil, os.NewSyscallError("route sockaddr", err)
}
-
var ifmat []Addr
- for _, s := range sas {
- switch v := s.(type) {
+ for _, sa := range sas {
+ switch sa := sa.(type) {
case *syscall.SockaddrInet4:
- ifma := &IPAddr{IP: IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])}
+ ifma := &IPAddr{IP: IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])}
ifmat = append(ifmat, ifma.toAddr())
case *syscall.SockaddrInet6:
ifma := &IPAddr{IP: make(IP, IPv6len)}
- copy(ifma.IP, v.Addr[:])
+ copy(ifma.IP, sa.Addr[:])
// NOTE: KAME based IPv6 protcol stack usually embeds
// the interface index in the interface-local or link-
// local address as the kernel-internal form.
- if ifma.IP.IsInterfaceLocalMulticast() ||
- ifma.IP.IsLinkLocalMulticast() {
- // remove embedded scope zone ID
+ if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() {
+ ifma.Zone = ifi.Name
ifma.IP[2], ifma.IP[3] = 0, 0
}
ifmat = append(ifmat, ifma.toAddr())
diff --git a/src/pkg/net/interface_linux.go b/src/pkg/net/interface_linux.go
index 825b20227..e66daef06 100644
--- a/src/pkg/net/interface_linux.go
+++ b/src/pkg/net/interface_linux.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Network interface identification for Linux
-
package net
import (
@@ -20,17 +18,16 @@ func interfaceTable(ifindex int) ([]Interface, error) {
if err != nil {
return nil, os.NewSyscallError("netlink rib", err)
}
-
msgs, err := syscall.ParseNetlinkMessage(tab)
if err != nil {
return nil, os.NewSyscallError("netlink message", err)
}
-
var ift []Interface
+loop:
for _, m := range msgs {
switch m.Header.Type {
case syscall.NLMSG_DONE:
- goto done
+ break loop
case syscall.RTM_NEWLINK:
ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0]))
if ifindex == 0 || ifindex == int(ifim.Index) {
@@ -38,17 +35,18 @@ func interfaceTable(ifindex int) ([]Interface, error) {
if err != nil {
return nil, os.NewSyscallError("netlink routeattr", err)
}
- ifi := newLink(ifim, attrs)
- ift = append(ift, ifi)
+ ift = append(ift, *newLink(ifim, attrs))
+ if ifindex == int(ifim.Index) {
+ break loop
+ }
}
}
}
-done:
return ift, nil
}
-func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) Interface {
- ifi := Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)}
+func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) *Interface {
+ ifi := &Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)}
for _, a := range attrs {
switch a.Attr.Type {
case syscall.IFLA_ADDRESS:
@@ -64,7 +62,7 @@ func newLink(ifim *syscall.IfInfomsg, attrs []syscall.NetlinkRouteAttr) Interfac
case syscall.IFLA_IFNAME:
ifi.Name = string(a.Value[:len(a.Value)-1])
case syscall.IFLA_MTU:
- ifi.MTU = int(uint32(a.Value[3])<<24 | uint32(a.Value[2])<<16 | uint32(a.Value[1])<<8 | uint32(a.Value[0]))
+ ifi.MTU = int(*(*uint32)(unsafe.Pointer(&a.Value[:4][0])))
}
}
return ifi
@@ -90,81 +88,87 @@ func linkFlags(rawFlags uint32) Flags {
return f
}
-// If the ifindex is zero, interfaceAddrTable returns addresses
-// for all network interfaces. Otherwise it returns addresses
-// for a specific interface.
-func interfaceAddrTable(ifindex int) ([]Addr, error) {
+// If the ifi is nil, interfaceAddrTable returns addresses for all
+// network interfaces. Otherwise it returns addresses for a specific
+// interface.
+func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
if err != nil {
return nil, os.NewSyscallError("netlink rib", err)
}
-
msgs, err := syscall.ParseNetlinkMessage(tab)
if err != nil {
return nil, os.NewSyscallError("netlink message", err)
}
-
- ifat, err := addrTable(msgs, ifindex)
+ var ift []Interface
+ if ifi == nil {
+ var err error
+ ift, err = interfaceTable(0)
+ if err != nil {
+ return nil, err
+ }
+ }
+ ifat, err := addrTable(ift, ifi, msgs)
if err != nil {
return nil, err
}
return ifat, nil
}
-func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, error) {
+func addrTable(ift []Interface, ifi *Interface, msgs []syscall.NetlinkMessage) ([]Addr, error) {
var ifat []Addr
+loop:
for _, m := range msgs {
switch m.Header.Type {
case syscall.NLMSG_DONE:
- goto done
+ break loop
case syscall.RTM_NEWADDR:
ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))
- if ifindex == 0 || ifindex == int(ifam.Index) {
+ if len(ift) != 0 || ifi.Index == int(ifam.Index) {
+ if len(ift) != 0 {
+ var err error
+ ifi, err = interfaceByIndex(ift, int(ifam.Index))
+ if err != nil {
+ return nil, err
+ }
+ }
attrs, err := syscall.ParseNetlinkRouteAttr(&m)
if err != nil {
return nil, os.NewSyscallError("netlink routeattr", err)
}
- ifat = append(ifat, newAddr(attrs, int(ifam.Family), int(ifam.Prefixlen)))
+ ifa := newAddr(ifi, ifam, attrs)
+ if ifa != nil {
+ ifat = append(ifat, ifa)
+ }
}
}
}
-done:
return ifat, nil
}
-func newAddr(attrs []syscall.NetlinkRouteAttr, family, pfxlen int) Addr {
- ifa := &IPNet{}
+func newAddr(ifi *Interface, ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) Addr {
for _, a := range attrs {
- switch a.Attr.Type {
- case syscall.IFA_ADDRESS:
- switch family {
+ if ifi.Flags&FlagPointToPoint != 0 && a.Attr.Type == syscall.IFA_LOCAL ||
+ ifi.Flags&FlagPointToPoint == 0 && a.Attr.Type == syscall.IFA_ADDRESS {
+ switch ifam.Family {
case syscall.AF_INET:
- ifa.IP = IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3])
- ifa.Mask = CIDRMask(pfxlen, 8*IPv4len)
+ return &IPNet{IP: IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv4len)}
case syscall.AF_INET6:
- ifa.IP = make(IP, IPv6len)
+ ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(ifam.Prefixlen), 8*IPv6len)}
copy(ifa.IP, a.Value[:])
- ifa.Mask = CIDRMask(pfxlen, 8*IPv6len)
+ if ifam.Scope == syscall.RT_SCOPE_HOST || ifam.Scope == syscall.RT_SCOPE_LINK {
+ ifa.Zone = ifi.Name
+ }
+ return ifa
}
}
}
- return ifa
+ return nil
}
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces. Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
- var (
- err error
- ifi *Interface
- )
- if ifindex > 0 {
- ifi, err = InterfaceByIndex(ifindex)
- if err != nil {
- return nil, err
- }
- }
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi)
ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi)
return append(ifmat4, ifmat6...), nil
@@ -176,7 +180,6 @@ func parseProcNetIGMP(path string, ifi *Interface) []Addr {
return nil
}
defer fd.close()
-
var (
ifmat []Addr
name string
@@ -193,10 +196,14 @@ func parseProcNetIGMP(path string, ifi *Interface) []Addr {
name = f[1]
case len(f[0]) == 8:
if ifi == nil || name == ifi.Name {
+ // The Linux kernel puts the IP
+ // address in /proc/net/igmp in native
+ // endianness.
for i := 0; i+1 < len(f[0]); i += 2 {
b[i/2], _ = xtoi2(f[0][i:i+2], 0)
}
- ifma := IPAddr{IP: IPv4(b[3], b[2], b[1], b[0])}
+ i := *(*uint32)(unsafe.Pointer(&b[:4][0]))
+ ifma := IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))}
ifmat = append(ifmat, ifma.toAddr())
}
}
@@ -210,7 +217,6 @@ func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
return nil
}
defer fd.close()
-
var ifmat []Addr
b := make([]byte, IPv6len)
for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
@@ -223,6 +229,9 @@ func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
b[i/2], _ = xtoi2(f[2][i:i+2], 0)
}
ifma := IPAddr{IP: IP{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]}}
+ if ifma.IP.IsInterfaceLocalMulticast() || ifma.IP.IsLinkLocalMulticast() {
+ ifma.Zone = ifi.Name
+ }
ifmat = append(ifmat, ifma.toAddr())
}
}
diff --git a/src/pkg/net/interface_linux_test.go b/src/pkg/net/interface_linux_test.go
index f14d1fe06..085d3de9d 100644
--- a/src/pkg/net/interface_linux_test.go
+++ b/src/pkg/net/interface_linux_test.go
@@ -4,7 +4,55 @@
package net
-import "testing"
+import (
+ "fmt"
+ "os/exec"
+ "testing"
+)
+
+func (ti *testInterface) setBroadcast(suffix int) error {
+ ti.name = fmt.Sprintf("gotest%d", suffix)
+ xname, err := exec.LookPath("ip")
+ if err != nil {
+ return err
+ }
+ ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+ Path: xname,
+ Args: []string{"ip", "link", "add", ti.name, "type", "dummy"},
+ })
+ ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{
+ Path: xname,
+ Args: []string{"ip", "link", "delete", ti.name, "type", "dummy"},
+ })
+ return nil
+}
+
+func (ti *testInterface) setPointToPoint(suffix int, local, remote string) error {
+ ti.name = fmt.Sprintf("gotest%d", suffix)
+ ti.local = local
+ ti.remote = remote
+ xname, err := exec.LookPath("ip")
+ if err != nil {
+ return err
+ }
+ ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+ Path: xname,
+ Args: []string{"ip", "tunnel", "add", ti.name, "mode", "gre", "local", local, "remote", remote},
+ })
+ ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{
+ Path: xname,
+ Args: []string{"ip", "tunnel", "del", ti.name, "mode", "gre", "local", local, "remote", remote},
+ })
+ xname, err = exec.LookPath("ifconfig")
+ if err != nil {
+ return err
+ }
+ ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+ Path: xname,
+ Args: []string{"ifconfig", ti.name, "inet", local, "dstaddr", remote},
+ })
+ return nil
+}
const (
numOfTestIPv4MCAddrs = 14
diff --git a/src/pkg/net/interface_netbsd.go b/src/pkg/net/interface_netbsd.go
index 4150e9ad5..c9ce5a7ac 100644
--- a/src/pkg/net/interface_netbsd.go
+++ b/src/pkg/net/interface_netbsd.go
@@ -2,13 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Network interface identification for NetBSD
-
package net
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces. Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
+ // TODO(mikio): Implement this like other platforms.
return nil, nil
}
diff --git a/src/pkg/net/interface_openbsd.go b/src/pkg/net/interface_openbsd.go
index d8adb4676..c9ce5a7ac 100644
--- a/src/pkg/net/interface_openbsd.go
+++ b/src/pkg/net/interface_openbsd.go
@@ -2,13 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Network interface identification for OpenBSD
-
package net
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces. Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
+ // TODO(mikio): Implement this like other platforms.
return nil, nil
}
diff --git a/src/pkg/net/interface_stub.go b/src/pkg/net/interface_stub.go
index d4d7ce9c7..a4eb731da 100644
--- a/src/pkg/net/interface_stub.go
+++ b/src/pkg/net/interface_stub.go
@@ -4,8 +4,6 @@
// +build plan9
-// Network interface identification
-
package net
// If the ifindex is zero, interfaceTable returns mappings of all
@@ -15,16 +13,15 @@ func interfaceTable(ifindex int) ([]Interface, error) {
return nil, nil
}
-// If the ifindex is zero, interfaceAddrTable returns addresses
-// for all network interfaces. Otherwise it returns addresses
-// for a specific interface.
-func interfaceAddrTable(ifindex int) ([]Addr, error) {
+// If the ifi is nil, interfaceAddrTable returns addresses for all
+// network interfaces. Otherwise it returns addresses for a specific
+// interface.
+func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
return nil, nil
}
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces. Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
return nil, nil
}
diff --git a/src/pkg/net/interface_test.go b/src/pkg/net/interface_test.go
index 0a33bfdb5..7fb342818 100644
--- a/src/pkg/net/interface_test.go
+++ b/src/pkg/net/interface_test.go
@@ -5,18 +5,24 @@
package net
import (
- "bytes"
+ "reflect"
"testing"
)
-func sameInterface(i, j *Interface) bool {
- if i == nil || j == nil {
- return false
+// loopbackInterface returns an available logical network interface
+// for loopback tests. It returns nil if no suitable interface is
+// found.
+func loopbackInterface() *Interface {
+ ift, err := Interfaces()
+ if err != nil {
+ return nil
}
- if i.Index == j.Index && i.Name == j.Name && bytes.Equal(i.HardwareAddr, j.HardwareAddr) {
- return true
+ for _, ifi := range ift {
+ if ifi.Flags&FlagLoopback != 0 && ifi.Flags&FlagUp != 0 {
+ return &ifi
+ }
}
- return false
+ return nil
}
func TestInterfaces(t *testing.T) {
@@ -24,24 +30,24 @@ func TestInterfaces(t *testing.T) {
if err != nil {
t.Fatalf("Interfaces failed: %v", err)
}
- t.Logf("table: len/cap = %v/%v\n", len(ift), cap(ift))
+ t.Logf("table: len/cap = %v/%v", len(ift), cap(ift))
for _, ifi := range ift {
ifxi, err := InterfaceByIndex(ifi.Index)
if err != nil {
- t.Fatalf("InterfaceByIndex(%q) failed: %v", ifi.Index, err)
+ t.Fatalf("InterfaceByIndex(%v) failed: %v", ifi.Index, err)
}
- if !sameInterface(ifxi, &ifi) {
- t.Fatalf("InterfaceByIndex(%q) = %v, want %v", ifi.Index, *ifxi, ifi)
+ if !reflect.DeepEqual(ifxi, &ifi) {
+ t.Fatalf("InterfaceByIndex(%v) = %v, want %v", ifi.Index, ifxi, ifi)
}
ifxn, err := InterfaceByName(ifi.Name)
if err != nil {
t.Fatalf("InterfaceByName(%q) failed: %v", ifi.Name, err)
}
- if !sameInterface(ifxn, &ifi) {
- t.Fatalf("InterfaceByName(%q) = %v, want %v", ifi.Name, *ifxn, ifi)
+ if !reflect.DeepEqual(ifxn, &ifi) {
+ t.Fatalf("InterfaceByName(%q) = %v, want %v", ifi.Name, ifxn, ifi)
}
- t.Logf("%q: flags %q, ifindex %v, mtu %v\n", ifi.Name, ifi.Flags.String(), ifi.Index, ifi.MTU)
+ t.Logf("%q: flags %q, ifindex %v, mtu %v", ifi.Name, ifi.Flags.String(), ifi.Index, ifi.MTU)
t.Logf("\thardware address %q", ifi.HardwareAddr.String())
testInterfaceAddrs(t, &ifi)
testInterfaceMulticastAddrs(t, &ifi)
@@ -53,7 +59,7 @@ func TestInterfaceAddrs(t *testing.T) {
if err != nil {
t.Fatalf("InterfaceAddrs failed: %v", err)
}
- t.Logf("table: len/cap = %v/%v\n", len(ifat), cap(ifat))
+ t.Logf("table: len/cap = %v/%v", len(ifat), cap(ifat))
testAddrs(t, ifat)
}
@@ -75,9 +81,13 @@ func testInterfaceMulticastAddrs(t *testing.T, ifi *Interface) {
func testAddrs(t *testing.T, ifat []Addr) {
for _, ifa := range ifat {
- switch ifa.(type) {
+ switch v := ifa.(type) {
case *IPAddr, *IPNet:
- t.Logf("\tinterface address %q\n", ifa.String())
+ if v == nil {
+ t.Errorf("\tunexpected value: %v", ifa)
+ } else {
+ t.Logf("\tinterface address %q", ifa.String())
+ }
default:
t.Errorf("\tunexpected type: %T", ifa)
}
@@ -86,11 +96,79 @@ func testAddrs(t *testing.T, ifat []Addr) {
func testMulticastAddrs(t *testing.T, ifmat []Addr) {
for _, ifma := range ifmat {
- switch ifma.(type) {
+ switch v := ifma.(type) {
case *IPAddr:
- t.Logf("\tjoined group address %q\n", ifma.String())
+ if v == nil {
+ t.Errorf("\tunexpected value: %v", ifma)
+ } else {
+ t.Logf("\tjoined group address %q", ifma.String())
+ }
default:
t.Errorf("\tunexpected type: %T", ifma)
}
}
}
+
+func BenchmarkInterfaces(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ if _, err := Interfaces(); err != nil {
+ b.Fatalf("Interfaces failed: %v", err)
+ }
+ }
+}
+
+func BenchmarkInterfaceByIndex(b *testing.B) {
+ ifi := loopbackInterface()
+ if ifi == nil {
+ b.Skip("loopback interface not found")
+ }
+ for i := 0; i < b.N; i++ {
+ if _, err := InterfaceByIndex(ifi.Index); err != nil {
+ b.Fatalf("InterfaceByIndex failed: %v", err)
+ }
+ }
+}
+
+func BenchmarkInterfaceByName(b *testing.B) {
+ ifi := loopbackInterface()
+ if ifi == nil {
+ b.Skip("loopback interface not found")
+ }
+ for i := 0; i < b.N; i++ {
+ if _, err := InterfaceByName(ifi.Name); err != nil {
+ b.Fatalf("InterfaceByName failed: %v", err)
+ }
+ }
+}
+
+func BenchmarkInterfaceAddrs(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ if _, err := InterfaceAddrs(); err != nil {
+ b.Fatalf("InterfaceAddrs failed: %v", err)
+ }
+ }
+}
+
+func BenchmarkInterfacesAndAddrs(b *testing.B) {
+ ifi := loopbackInterface()
+ if ifi == nil {
+ b.Skip("loopback interface not found")
+ }
+ for i := 0; i < b.N; i++ {
+ if _, err := ifi.Addrs(); err != nil {
+ b.Fatalf("Interface.Addrs failed: %v", err)
+ }
+ }
+}
+
+func BenchmarkInterfacesAndMulticastAddrs(b *testing.B) {
+ ifi := loopbackInterface()
+ if ifi == nil {
+ b.Skip("loopback interface not found")
+ }
+ for i := 0; i < b.N; i++ {
+ if _, err := ifi.MulticastAddrs(); err != nil {
+ b.Fatalf("Interface.MulticastAddrs failed: %v", err)
+ }
+ }
+}
diff --git a/src/pkg/net/interface_unix_test.go b/src/pkg/net/interface_unix_test.go
new file mode 100644
index 000000000..6dbd6e6e7
--- /dev/null
+++ b/src/pkg/net/interface_unix_test.go
@@ -0,0 +1,145 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd
+
+package net
+
+import (
+ "os"
+ "os/exec"
+ "runtime"
+ "testing"
+ "time"
+)
+
+type testInterface struct {
+ name string
+ local string
+ remote string
+ setupCmds []*exec.Cmd
+ teardownCmds []*exec.Cmd
+}
+
+func (ti *testInterface) setup() error {
+ for _, cmd := range ti.setupCmds {
+ if err := cmd.Run(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (ti *testInterface) teardown() error {
+ for _, cmd := range ti.teardownCmds {
+ if err := cmd.Run(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func TestPointToPointInterface(t *testing.T) {
+ switch runtime.GOOS {
+ case "darwin":
+ t.Skipf("skipping read test on %q", runtime.GOOS)
+ }
+ if os.Getuid() != 0 {
+ t.Skip("skipping test; must be root")
+ }
+
+ local, remote := "169.254.0.1", "169.254.0.254"
+ ip := ParseIP(remote)
+ for i := 0; i < 3; i++ {
+ ti := &testInterface{}
+ if err := ti.setPointToPoint(5963+i, local, remote); err != nil {
+ t.Skipf("test requries external command: %v", err)
+ }
+ if err := ti.setup(); err != nil {
+ t.Fatalf("testInterface.setup failed: %v", err)
+ } else {
+ time.Sleep(3 * time.Millisecond)
+ }
+ ift, err := Interfaces()
+ if err != nil {
+ ti.teardown()
+ t.Fatalf("Interfaces failed: %v", err)
+ }
+ for _, ifi := range ift {
+ if ti.name == ifi.Name {
+ ifat, err := ifi.Addrs()
+ if err != nil {
+ ti.teardown()
+ t.Fatalf("Interface.Addrs failed: %v", err)
+ }
+ for _, ifa := range ifat {
+ if ip.Equal(ifa.(*IPNet).IP) {
+ ti.teardown()
+ t.Fatalf("got %v; want %v", ip, local)
+ }
+ }
+ }
+ }
+ if err := ti.teardown(); err != nil {
+ t.Fatalf("testInterface.teardown failed: %v", err)
+ } else {
+ time.Sleep(3 * time.Millisecond)
+ }
+ }
+}
+
+func TestInterfaceArrivalAndDeparture(t *testing.T) {
+ if os.Getuid() != 0 {
+ t.Skip("skipping test; must be root")
+ }
+
+ for i := 0; i < 3; i++ {
+ ift1, err := Interfaces()
+ if err != nil {
+ t.Fatalf("Interfaces failed: %v", err)
+ }
+ ti := &testInterface{}
+ if err := ti.setBroadcast(5682 + i); err != nil {
+ t.Skipf("test requires external command: %v", err)
+ }
+ if err := ti.setup(); err != nil {
+ t.Fatalf("testInterface.setup failed: %v", err)
+ } else {
+ time.Sleep(3 * time.Millisecond)
+ }
+ ift2, err := Interfaces()
+ if err != nil {
+ ti.teardown()
+ t.Fatalf("Interfaces failed: %v", err)
+ }
+ if len(ift2) <= len(ift1) {
+ for _, ifi := range ift1 {
+ t.Logf("before: %v", ifi)
+ }
+ for _, ifi := range ift2 {
+ t.Logf("after: %v", ifi)
+ }
+ ti.teardown()
+ t.Fatalf("got %v; want gt %v", len(ift2), len(ift1))
+ }
+ if err := ti.teardown(); err != nil {
+ t.Fatalf("testInterface.teardown failed: %v", err)
+ } else {
+ time.Sleep(3 * time.Millisecond)
+ }
+ ift3, err := Interfaces()
+ if err != nil {
+ t.Fatalf("Interfaces failed: %v", err)
+ }
+ if len(ift3) >= len(ift2) {
+ for _, ifi := range ift2 {
+ t.Logf("before: %v", ifi)
+ }
+ for _, ifi := range ift3 {
+ t.Logf("after: %v", ifi)
+ }
+ t.Fatalf("got %v; want lt %v", len(ift3), len(ift2))
+ }
+ }
+}
diff --git a/src/pkg/net/interface_windows.go b/src/pkg/net/interface_windows.go
index 4368b3306..0759dc255 100644
--- a/src/pkg/net/interface_windows.go
+++ b/src/pkg/net/interface_windows.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Network interface identification for Windows
-
package net
import (
@@ -25,6 +23,9 @@ func getAdapterList() (*syscall.IpAdapterInfo, error) {
b := make([]byte, 1000)
l := uint32(len(b))
a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
+ // TODO(mikio): GetAdaptersInfo returns IP_ADAPTER_INFO that
+ // contains IPv4 address list only. We should use another API
+ // for fetching IPv6 stuff from the kernel.
err := syscall.GetAdaptersInfo(a, &l)
if err == syscall.ERROR_BUFFER_OVERFLOW {
b = make([]byte, l)
@@ -38,7 +39,7 @@ func getAdapterList() (*syscall.IpAdapterInfo, error) {
}
func getInterfaceList() ([]syscall.InterfaceInfo, error) {
- s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
+ s, err := sysSocket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
if err != nil {
return nil, os.NewSyscallError("Socket", err)
}
@@ -126,10 +127,10 @@ func interfaceTable(ifindex int) ([]Interface, error) {
return ift, nil
}
-// If the ifindex is zero, interfaceAddrTable returns addresses
-// for all network interfaces. Otherwise it returns addresses
-// for a specific interface.
-func interfaceAddrTable(ifindex int) ([]Addr, error) {
+// If the ifi is nil, interfaceAddrTable returns addresses for all
+// network interfaces. Otherwise it returns addresses for a specific
+// interface.
+func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
ai, err := getAdapterList()
if err != nil {
return nil, err
@@ -138,11 +139,10 @@ func interfaceAddrTable(ifindex int) ([]Addr, error) {
var ifat []Addr
for ; ai != nil; ai = ai.Next {
index := ai.Index
- if ifindex == 0 || ifindex == int(index) {
+ if ifi == nil || ifi.Index == int(index) {
ipl := &ai.IpAddressList
for ; ipl != nil; ipl = ipl.Next {
- ifa := IPAddr{}
- ifa.IP = parseIPv4(bytePtrToString(&ipl.IpAddress.String[0]))
+ ifa := IPAddr{IP: parseIPv4(bytePtrToString(&ipl.IpAddress.String[0]))}
ifat = append(ifat, ifa.toAddr())
}
}
@@ -150,9 +150,9 @@ func interfaceAddrTable(ifindex int) ([]Addr, error) {
return ifat, nil
}
-// If the ifindex is zero, interfaceMulticastAddrTable returns
-// addresses for all network interfaces. Otherwise it returns
-// addresses for a specific interface.
-func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
+ // TODO(mikio): Implement this like other platforms.
return nil, nil
}
diff --git a/src/pkg/net/ip.go b/src/pkg/net/ip.go
index 979d7acd5..d588e3a42 100644
--- a/src/pkg/net/ip.go
+++ b/src/pkg/net/ip.go
@@ -7,7 +7,7 @@
// IPv4 addresses are 4 bytes; IPv6 addresses are 16 bytes.
// An IPv4 address can be converted to an IPv6 address by
// adding a canonical prefix (10 zeros, 2 0xFFs).
-// This library accepts either size of byte array but always
+// This library accepts either size of byte slice but always
// returns 16-byte addresses.
package net
@@ -18,14 +18,14 @@ const (
IPv6len = 16
)
-// An IP is a single IP address, an array of bytes.
+// An IP is a single IP address, a slice of bytes.
// Functions in this package accept either 4-byte (IPv4)
-// or 16-byte (IPv6) arrays as input.
+// or 16-byte (IPv6) slices as input.
//
// Note that in this documentation, referring to an
// IP address as an IPv4 address or an IPv6 address
// is a semantic property of the address, not just the
-// length of the byte array: a 16-byte array can still
+// length of the byte slice: a 16-byte slice can still
// be an IPv4 address.
type IP []byte
@@ -36,6 +36,7 @@ type IPMask []byte
type IPNet struct {
IP IP // network number
Mask IPMask // network mask
+ Zone string // IPv6 scoped addressing zone
}
// IPv4 returns the IP address (in 16-byte form) of the
@@ -645,5 +646,5 @@ func ParseCIDR(s string) (IP, *IPNet, error) {
return nil, nil, &ParseError{"CIDR address", s}
}
m := CIDRMask(n, 8*iplen)
- return ip, &IPNet{ip.Mask(m), m}, nil
+ return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil
}
diff --git a/src/pkg/net/ip_test.go b/src/pkg/net/ip_test.go
index df647ef73..f8b7f067f 100644
--- a/src/pkg/net/ip_test.go
+++ b/src/pkg/net/ip_test.go
@@ -114,23 +114,23 @@ var parsecidrtests = []struct {
net *IPNet
err error
}{
- {"135.104.0.0/32", IPv4(135, 104, 0, 0), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 255)}, nil},
- {"0.0.0.0/24", IPv4(0, 0, 0, 0), &IPNet{IPv4(0, 0, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil},
- {"135.104.0.0/24", IPv4(135, 104, 0, 0), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil},
- {"135.104.0.1/32", IPv4(135, 104, 0, 1), &IPNet{IPv4(135, 104, 0, 1), IPv4Mask(255, 255, 255, 255)}, nil},
- {"135.104.0.1/24", IPv4(135, 104, 0, 1), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil},
- {"::1/128", ParseIP("::1"), &IPNet{ParseIP("::1"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))}, nil},
- {"abcd:2345::/127", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"))}, nil},
- {"abcd:2345::/65", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::"))}, nil},
- {"abcd:2345::/64", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff::"))}, nil},
- {"abcd:2345::/63", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:fffe::"))}, nil},
- {"abcd:2345::/33", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:8000::"))}, nil},
- {"abcd:2345::/32", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff::"))}, nil},
- {"abcd:2344::/31", ParseIP("abcd:2344::"), &IPNet{ParseIP("abcd:2344::"), IPMask(ParseIP("ffff:fffe::"))}, nil},
- {"abcd:2300::/24", ParseIP("abcd:2300::"), &IPNet{ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::"))}, nil},
- {"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::"))}, nil},
- {"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
- {"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
+ {"135.104.0.0/32", IPv4(135, 104, 0, 0), &IPNet{IP: IPv4(135, 104, 0, 0), Mask: IPv4Mask(255, 255, 255, 255)}, nil},
+ {"0.0.0.0/24", IPv4(0, 0, 0, 0), &IPNet{IP: IPv4(0, 0, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, nil},
+ {"135.104.0.0/24", IPv4(135, 104, 0, 0), &IPNet{IP: IPv4(135, 104, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, nil},
+ {"135.104.0.1/32", IPv4(135, 104, 0, 1), &IPNet{IP: IPv4(135, 104, 0, 1), Mask: IPv4Mask(255, 255, 255, 255)}, nil},
+ {"135.104.0.1/24", IPv4(135, 104, 0, 1), &IPNet{IP: IPv4(135, 104, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, nil},
+ {"::1/128", ParseIP("::1"), &IPNet{IP: ParseIP("::1"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))}, nil},
+ {"abcd:2345::/127", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"))}, nil},
+ {"abcd:2345::/65", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::"))}, nil},
+ {"abcd:2345::/64", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff::"))}, nil},
+ {"abcd:2345::/63", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:fffe::"))}, nil},
+ {"abcd:2345::/33", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:8000::"))}, nil},
+ {"abcd:2345::/32", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff::"))}, nil},
+ {"abcd:2344::/31", ParseIP("abcd:2344::"), &IPNet{IP: ParseIP("abcd:2344::"), Mask: IPMask(ParseIP("ffff:fffe::"))}, nil},
+ {"abcd:2300::/24", ParseIP("abcd:2300::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil},
+ {"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil},
+ {"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
+ {"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
{"192.168.1.1/255.255.255.0", nil, nil, &ParseError{"CIDR address", "192.168.1.1/255.255.255.0"}},
{"192.168.1.1/35", nil, nil, &ParseError{"CIDR address", "192.168.1.1/35"}},
{"2001:db8::1/-1", nil, nil, &ParseError{"CIDR address", "2001:db8::1/-1"}},
@@ -154,14 +154,14 @@ var ipnetcontainstests = []struct {
net *IPNet
ok bool
}{
- {IPv4(172, 16, 1, 1), &IPNet{IPv4(172, 16, 0, 0), CIDRMask(12, 32)}, true},
- {IPv4(172, 24, 0, 1), &IPNet{IPv4(172, 16, 0, 0), CIDRMask(13, 32)}, false},
- {IPv4(192, 168, 0, 3), &IPNet{IPv4(192, 168, 0, 0), IPv4Mask(0, 0, 255, 252)}, true},
- {IPv4(192, 168, 0, 4), &IPNet{IPv4(192, 168, 0, 0), IPv4Mask(0, 255, 0, 252)}, false},
- {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), CIDRMask(47, 128)}, true},
- {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:2::"), CIDRMask(47, 128)}, false},
- {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), IPMask(ParseIP("ffff:0:ffff::"))}, true},
- {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), IPMask(ParseIP("0:0:0:ffff::"))}, false},
+ {IPv4(172, 16, 1, 1), &IPNet{IP: IPv4(172, 16, 0, 0), Mask: CIDRMask(12, 32)}, true},
+ {IPv4(172, 24, 0, 1), &IPNet{IP: IPv4(172, 16, 0, 0), Mask: CIDRMask(13, 32)}, false},
+ {IPv4(192, 168, 0, 3), &IPNet{IP: IPv4(192, 168, 0, 0), Mask: IPv4Mask(0, 0, 255, 252)}, true},
+ {IPv4(192, 168, 0, 4), &IPNet{IP: IPv4(192, 168, 0, 0), Mask: IPv4Mask(0, 255, 0, 252)}, false},
+ {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:1::"), Mask: CIDRMask(47, 128)}, true},
+ {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:2::"), Mask: CIDRMask(47, 128)}, false},
+ {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:1::"), Mask: IPMask(ParseIP("ffff:0:ffff::"))}, true},
+ {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:1::"), Mask: IPMask(ParseIP("0:0:0:ffff::"))}, false},
}
func TestIPNetContains(t *testing.T) {
@@ -176,10 +176,10 @@ var ipnetstringtests = []struct {
in *IPNet
out string
}{
- {&IPNet{IPv4(192, 168, 1, 0), CIDRMask(26, 32)}, "192.168.1.0/26"},
- {&IPNet{IPv4(192, 168, 1, 0), IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"},
- {&IPNet{ParseIP("2001:db8::"), CIDRMask(55, 128)}, "2001:db8::/55"},
- {&IPNet{ParseIP("2001:db8::"), IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"},
+ {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: CIDRMask(26, 32)}, "192.168.1.0/26"},
+ {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"},
+ {&IPNet{IP: ParseIP("2001:db8::"), Mask: CIDRMask(55, 128)}, "2001:db8::/55"},
+ {&IPNet{IP: ParseIP("2001:db8::"), Mask: IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"},
}
func TestIPNetString(t *testing.T) {
@@ -233,27 +233,27 @@ var networknumberandmasktests = []struct {
in IPNet
out IPNet
}{
- {IPNet{v4addr, v4mask}, IPNet{v4addr, v4mask}},
- {IPNet{v4addr, v4mappedv6mask}, IPNet{v4addr, v4mask}},
- {IPNet{v4mappedv6addr, v4mappedv6mask}, IPNet{v4addr, v4mask}},
- {IPNet{v4mappedv6addr, v6mask}, IPNet{v4addr, v4maskzero}},
- {IPNet{v4addr, v6mask}, IPNet{v4addr, v4maskzero}},
- {IPNet{v6addr, v6mask}, IPNet{v6addr, v6mask}},
- {IPNet{v6addr, v4mappedv6mask}, IPNet{v6addr, v4mappedv6mask}},
- {in: IPNet{v6addr, v4mask}},
- {in: IPNet{v4addr, badmask}},
- {in: IPNet{v4mappedv6addr, badmask}},
- {in: IPNet{v6addr, badmask}},
- {in: IPNet{badaddr, v4mask}},
- {in: IPNet{badaddr, v4mappedv6mask}},
- {in: IPNet{badaddr, v6mask}},
- {in: IPNet{badaddr, badmask}},
+ {IPNet{IP: v4addr, Mask: v4mask}, IPNet{IP: v4addr, Mask: v4mask}},
+ {IPNet{IP: v4addr, Mask: v4mappedv6mask}, IPNet{IP: v4addr, Mask: v4mask}},
+ {IPNet{IP: v4mappedv6addr, Mask: v4mappedv6mask}, IPNet{IP: v4addr, Mask: v4mask}},
+ {IPNet{IP: v4mappedv6addr, Mask: v6mask}, IPNet{IP: v4addr, Mask: v4maskzero}},
+ {IPNet{IP: v4addr, Mask: v6mask}, IPNet{IP: v4addr, Mask: v4maskzero}},
+ {IPNet{IP: v6addr, Mask: v6mask}, IPNet{IP: v6addr, Mask: v6mask}},
+ {IPNet{IP: v6addr, Mask: v4mappedv6mask}, IPNet{IP: v6addr, Mask: v4mappedv6mask}},
+ {in: IPNet{IP: v6addr, Mask: v4mask}},
+ {in: IPNet{IP: v4addr, Mask: badmask}},
+ {in: IPNet{IP: v4mappedv6addr, Mask: badmask}},
+ {in: IPNet{IP: v6addr, Mask: badmask}},
+ {in: IPNet{IP: badaddr, Mask: v4mask}},
+ {in: IPNet{IP: badaddr, Mask: v4mappedv6mask}},
+ {in: IPNet{IP: badaddr, Mask: v6mask}},
+ {in: IPNet{IP: badaddr, Mask: badmask}},
}
func TestNetworkNumberAndMask(t *testing.T) {
for _, tt := range networknumberandmasktests {
ip, m := networkNumberAndMask(&tt.in)
- out := &IPNet{ip, m}
+ out := &IPNet{IP: ip, Mask: m}
if !reflect.DeepEqual(&tt.out, out) {
t.Errorf("networkNumberAndMask(%v) = %v; want %v", tt.in, out, &tt.out)
}
@@ -268,6 +268,29 @@ var splitjointests = []struct {
{"www.google.com", "80", "www.google.com:80"},
{"127.0.0.1", "1234", "127.0.0.1:1234"},
{"::1", "80", "[::1]:80"},
+ {"google.com", "https%foo", "google.com:https%foo"}, // Go 1.0 behavior
+ {"", "0", ":0"},
+ {"127.0.0.1", "", "127.0.0.1:"}, // Go 1.0 behaviour
+ {"www.google.com", "", "www.google.com:"}, // Go 1.0 behaviour
+}
+
+var splitfailuretests = []struct {
+ HostPort string
+ Err string
+}{
+ {"www.google.com", "missing port in address"},
+ {"127.0.0.1", "missing port in address"},
+ {"[::1]", "missing port in address"},
+ {"::1", "too many colons in address"},
+
+ // Test cases that didn't fail in Go 1.0
+ {"[foo:bar]", "missing port in address"},
+ {"[foo:bar]baz", "missing port in address"},
+ {"[foo]:[bar]:baz", "too many colons in address"},
+ {"[foo]bar:baz", "missing port in address"},
+ {"[foo]:[bar]baz", "unexpected '[' in address"},
+ {"foo[bar]:baz", "unexpected '[' in address"},
+ {"foo]bar:baz", "unexpected ']' in address"},
}
func TestSplitHostPort(t *testing.T) {
@@ -276,6 +299,16 @@ func TestSplitHostPort(t *testing.T) {
t.Errorf("SplitHostPort(%q) = %q, %q, %v; want %q, %q, nil", tt.Join, host, port, err, tt.Host, tt.Port)
}
}
+ for _, tt := range splitfailuretests {
+ if _, _, err := SplitHostPort(tt.HostPort); err == nil {
+ t.Errorf("SplitHostPort(%q) should have failed", tt.HostPort)
+ } else {
+ e := err.(*AddrError)
+ if e.Err != tt.Err {
+ t.Errorf("SplitHostPort(%q) = _, _, %q; want %q", tt.HostPort, e.Err, tt.Err)
+ }
+ }
+ }
}
func TestJoinHostPort(t *testing.T) {
diff --git a/src/pkg/net/ipraw_test.go b/src/pkg/net/ipraw_test.go
index 613620272..65defc7ea 100644
--- a/src/pkg/net/ipraw_test.go
+++ b/src/pkg/net/ipraw_test.go
@@ -2,205 +2,340 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !plan9
+
package net
import (
"bytes"
+ "errors"
"os"
- "syscall"
+ "reflect"
"testing"
"time"
)
-var icmpTests = []struct {
+var resolveIPAddrTests = []struct {
+ net string
+ litAddr string
+ addr *IPAddr
+ err error
+}{
+ {"ip", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+ {"ip4", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+ {"ip4:icmp", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+
+ {"ip", "::1", &IPAddr{IP: ParseIP("::1")}, nil},
+ {"ip6", "::1", &IPAddr{IP: ParseIP("::1")}, nil},
+ {"ip6:icmp", "::1", &IPAddr{IP: ParseIP("::1")}, nil},
+
+ {"", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, // Go 1.0 behavior
+ {"", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, // Go 1.0 behavior
+
+ {"l2tp", "127.0.0.1", nil, UnknownNetworkError("l2tp")},
+ {"l2tp:gre", "127.0.0.1", nil, UnknownNetworkError("l2tp:gre")},
+ {"tcp", "1.2.3.4:123", nil, UnknownNetworkError("tcp")},
+}
+
+func TestResolveIPAddr(t *testing.T) {
+ for _, tt := range resolveIPAddrTests {
+ addr, err := ResolveIPAddr(tt.net, tt.litAddr)
+ if err != tt.err {
+ t.Fatalf("ResolveIPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
+ }
+ if !reflect.DeepEqual(addr, tt.addr) {
+ t.Fatalf("got %#v; expected %#v", addr, tt.addr)
+ }
+ }
+}
+
+var icmpEchoTests = []struct {
net string
laddr string
raddr string
- ipv6 bool // test with underlying AF_INET6 socket
}{
- {"ip4:icmp", "", "127.0.0.1", false},
- {"ip6:icmp", "", "::1", true},
+ {"ip4:icmp", "0.0.0.0", "127.0.0.1"},
+ {"ip6:ipv6-icmp", "::", "::1"},
}
-func TestICMP(t *testing.T) {
+func TestConnICMPEcho(t *testing.T) {
if os.Getuid() != 0 {
- t.Logf("test disabled; must be root")
- return
+ t.Skip("skipping test; must be root")
}
- seqnum := 61455
- for _, tt := range icmpTests {
- if tt.ipv6 && !supportsIPv6 {
+ for i, tt := range icmpEchoTests {
+ net, _, err := parseNetwork(tt.net)
+ if err != nil {
+ t.Fatalf("parseNetwork failed: %v", err)
+ }
+ if net == "ip6" && !supportsIPv6 {
continue
}
- id := os.Getpid() & 0xffff
- seqnum++
- echo := newICMPEchoRequest(tt.net, id, seqnum, 128, []byte("Go Go Gadget Ping!!!"))
- exchangeICMPEcho(t, tt.net, tt.laddr, tt.raddr, echo)
- }
-}
-
-func exchangeICMPEcho(t *testing.T, net, laddr, raddr string, echo []byte) {
- c, err := ListenPacket(net, laddr)
- if err != nil {
- t.Errorf("ListenPacket(%q, %q) failed: %v", net, laddr, err)
- return
- }
- c.SetDeadline(time.Now().Add(100 * time.Millisecond))
- defer c.Close()
-
- ra, err := ResolveIPAddr(net, raddr)
- if err != nil {
- t.Errorf("ResolveIPAddr(%q, %q) failed: %v", net, raddr, err)
- return
- }
- waitForReady := make(chan bool)
- go icmpEchoTransponder(t, net, raddr, waitForReady)
- <-waitForReady
-
- _, err = c.WriteTo(echo, ra)
- if err != nil {
- t.Errorf("WriteTo failed: %v", err)
- return
- }
+ c, err := Dial(tt.net, tt.raddr)
+ if err != nil {
+ t.Fatalf("Dial failed: %v", err)
+ }
+ c.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ defer c.Close()
- reply := make([]byte, 256)
- for {
- _, _, err := c.ReadFrom(reply)
+ typ := icmpv4EchoRequest
+ if net == "ip6" {
+ typ = icmpv6EchoRequest
+ }
+ xid, xseq := os.Getpid()&0xffff, i+1
+ b, err := (&icmpMessage{
+ Type: typ, Code: 0,
+ Body: &icmpEcho{
+ ID: xid, Seq: xseq,
+ Data: bytes.Repeat([]byte("Go Go Gadget Ping!!!"), 3),
+ },
+ }).Marshal()
if err != nil {
- t.Errorf("ReadFrom failed: %v", err)
- return
+ t.Fatalf("icmpMessage.Marshal failed: %v", err)
}
- switch c.(*IPConn).fd.family {
- case syscall.AF_INET:
- if reply[0] != ICMP4_ECHO_REPLY {
- continue
+ if _, err := c.Write(b); err != nil {
+ t.Fatalf("Conn.Write failed: %v", err)
+ }
+ var m *icmpMessage
+ for {
+ if _, err := c.Read(b); err != nil {
+ t.Fatalf("Conn.Read failed: %v", err)
+ }
+ if net == "ip4" {
+ b = ipv4Payload(b)
}
- case syscall.AF_INET6:
- if reply[0] != ICMP6_ECHO_REPLY {
+ if m, err = parseICMPMessage(b); err != nil {
+ t.Fatalf("parseICMPMessage failed: %v", err)
+ }
+ switch m.Type {
+ case icmpv4EchoRequest, icmpv6EchoRequest:
continue
}
+ break
}
- xid, xseqnum := parseICMPEchoReply(echo)
- rid, rseqnum := parseICMPEchoReply(reply)
- if rid != xid || rseqnum != xseqnum {
- t.Errorf("ID = %v, Seqnum = %v, want ID = %v, Seqnum = %v", rid, rseqnum, xid, xseqnum)
- return
+ switch p := m.Body.(type) {
+ case *icmpEcho:
+ if p.ID != xid || p.Seq != xseq {
+ t.Fatalf("got id=%v, seqnum=%v; expected id=%v, seqnum=%v", p.ID, p.Seq, xid, xseq)
+ }
+ default:
+ t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, typ, 0)
}
- break
}
}
-func icmpEchoTransponder(t *testing.T, net, raddr string, waitForReady chan bool) {
- c, err := Dial(net, raddr)
- if err != nil {
- waitForReady <- true
- t.Errorf("Dial(%q, %q) failed: %v", net, raddr, err)
- return
+func TestPacketConnICMPEcho(t *testing.T) {
+ if os.Getuid() != 0 {
+ t.Skip("skipping test; must be root")
}
- c.SetDeadline(time.Now().Add(100 * time.Millisecond))
- defer c.Close()
- waitForReady <- true
- echo := make([]byte, 256)
- var nr int
- for {
- nr, err = c.Read(echo)
+ for i, tt := range icmpEchoTests {
+ net, _, err := parseNetwork(tt.net)
if err != nil {
- t.Errorf("Read failed: %v", err)
- return
+ t.Fatalf("parseNetwork failed: %v", err)
}
- switch c.(*IPConn).fd.family {
- case syscall.AF_INET:
- if echo[0] != ICMP4_ECHO_REQUEST {
- continue
+ if net == "ip6" && !supportsIPv6 {
+ continue
+ }
+
+ c, err := ListenPacket(tt.net, tt.laddr)
+ if err != nil {
+ t.Fatalf("ListenPacket failed: %v", err)
+ }
+ c.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ defer c.Close()
+
+ ra, err := ResolveIPAddr(tt.net, tt.raddr)
+ if err != nil {
+ t.Fatalf("ResolveIPAddr failed: %v", err)
+ }
+ typ := icmpv4EchoRequest
+ if net == "ip6" {
+ typ = icmpv6EchoRequest
+ }
+ xid, xseq := os.Getpid()&0xffff, i+1
+ b, err := (&icmpMessage{
+ Type: typ, Code: 0,
+ Body: &icmpEcho{
+ ID: xid, Seq: xseq,
+ Data: bytes.Repeat([]byte("Go Go Gadget Ping!!!"), 3),
+ },
+ }).Marshal()
+ if err != nil {
+ t.Fatalf("icmpMessage.Marshal failed: %v", err)
+ }
+ if _, err := c.WriteTo(b, ra); err != nil {
+ t.Fatalf("PacketConn.WriteTo failed: %v", err)
+ }
+ var m *icmpMessage
+ for {
+ if _, _, err := c.ReadFrom(b); err != nil {
+ t.Fatalf("PacketConn.ReadFrom failed: %v", err)
}
- case syscall.AF_INET6:
- if echo[0] != ICMP6_ECHO_REQUEST {
+ // TODO: fix issue 3944
+ //if net == "ip4" {
+ // b = ipv4Payload(b)
+ //}
+ if m, err = parseICMPMessage(b); err != nil {
+ t.Fatalf("parseICMPMessage failed: %v", err)
+ }
+ switch m.Type {
+ case icmpv4EchoRequest, icmpv6EchoRequest:
continue
}
+ break
+ }
+ switch p := m.Body.(type) {
+ case *icmpEcho:
+ if p.ID != xid || p.Seq != xseq {
+ t.Fatalf("got id=%v, seqnum=%v; expected id=%v, seqnum=%v", p.ID, p.Seq, xid, xseq)
+ }
+ default:
+ t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, typ, 0)
}
- break
- }
-
- switch c.(*IPConn).fd.family {
- case syscall.AF_INET:
- echo[0] = ICMP4_ECHO_REPLY
- case syscall.AF_INET6:
- echo[0] = ICMP6_ECHO_REPLY
}
+}
- _, err = c.Write(echo[:nr])
- if err != nil {
- t.Errorf("Write failed: %v", err)
- return
+func ipv4Payload(b []byte) []byte {
+ if len(b) < 20 {
+ return b
}
+ hdrlen := int(b[0]&0x0f) << 2
+ return b[hdrlen:]
}
const (
- ICMP4_ECHO_REQUEST = 8
- ICMP4_ECHO_REPLY = 0
- ICMP6_ECHO_REQUEST = 128
- ICMP6_ECHO_REPLY = 129
+ icmpv4EchoRequest = 8
+ icmpv4EchoReply = 0
+ icmpv6EchoRequest = 128
+ icmpv6EchoReply = 129
)
-func newICMPEchoRequest(net string, id, seqnum, msglen int, filler []byte) []byte {
- afnet, _, _ := parseDialNetwork(net)
- switch afnet {
- case "ip4":
- return newICMPv4EchoRequest(id, seqnum, msglen, filler)
- case "ip6":
- return newICMPv6EchoRequest(id, seqnum, msglen, filler)
- }
- return nil
+// icmpMessage represents an ICMP message.
+type icmpMessage struct {
+ Type int // type
+ Code int // code
+ Checksum int // checksum
+ Body icmpMessageBody // body
}
-func newICMPv4EchoRequest(id, seqnum, msglen int, filler []byte) []byte {
- b := newICMPInfoMessage(id, seqnum, msglen, filler)
- b[0] = ICMP4_ECHO_REQUEST
+// icmpMessageBody represents an ICMP message body.
+type icmpMessageBody interface {
+ Len() int
+ Marshal() ([]byte, error)
+}
- // calculate ICMP checksum
- cklen := len(b)
+// Marshal returns the binary enconding of the ICMP echo request or
+// reply message m.
+func (m *icmpMessage) Marshal() ([]byte, error) {
+ b := []byte{byte(m.Type), byte(m.Code), 0, 0}
+ if m.Body != nil && m.Body.Len() != 0 {
+ mb, err := m.Body.Marshal()
+ if err != nil {
+ return nil, err
+ }
+ b = append(b, mb...)
+ }
+ switch m.Type {
+ case icmpv6EchoRequest, icmpv6EchoReply:
+ return b, nil
+ }
+ csumcv := len(b) - 1 // checksum coverage
s := uint32(0)
- for i := 0; i < cklen-1; i += 2 {
+ for i := 0; i < csumcv; i += 2 {
s += uint32(b[i+1])<<8 | uint32(b[i])
}
- if cklen&1 == 1 {
- s += uint32(b[cklen-1])
+ if csumcv&1 == 0 {
+ s += uint32(b[csumcv])
}
- s = (s >> 16) + (s & 0xffff)
- s = s + (s >> 16)
- // place checksum back in header; using ^= avoids the
- // assumption the checksum bytes are zero
- b[2] ^= uint8(^s & 0xff)
- b[3] ^= uint8(^s >> 8)
+ s = s>>16 + s&0xffff
+ s = s + s>>16
+ // Place checksum back in header; using ^= avoids the
+ // assumption the checksum bytes are zero.
+ b[2] ^= byte(^s & 0xff)
+ b[3] ^= byte(^s >> 8)
+ return b, nil
+}
- return b
+// parseICMPMessage parses b as an ICMP message.
+func parseICMPMessage(b []byte) (*icmpMessage, error) {
+ msglen := len(b)
+ if msglen < 4 {
+ return nil, errors.New("message too short")
+ }
+ m := &icmpMessage{Type: int(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
+ if msglen > 4 {
+ var err error
+ switch m.Type {
+ case icmpv4EchoRequest, icmpv4EchoReply, icmpv6EchoRequest, icmpv6EchoReply:
+ m.Body, err = parseICMPEcho(b[4:])
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ return m, nil
+}
+
+// imcpEcho represenets an ICMP echo request or reply message body.
+type icmpEcho struct {
+ ID int // identifier
+ Seq int // sequence number
+ Data []byte // data
+}
+
+func (p *icmpEcho) Len() int {
+ if p == nil {
+ return 0
+ }
+ return 4 + len(p.Data)
}
-func newICMPv6EchoRequest(id, seqnum, msglen int, filler []byte) []byte {
- b := newICMPInfoMessage(id, seqnum, msglen, filler)
- b[0] = ICMP6_ECHO_REQUEST
- return b
+// Marshal returns the binary enconding of the ICMP echo request or
+// reply message body p.
+func (p *icmpEcho) Marshal() ([]byte, error) {
+ b := make([]byte, 4+len(p.Data))
+ b[0], b[1] = byte(p.ID>>8), byte(p.ID&0xff)
+ b[2], b[3] = byte(p.Seq>>8), byte(p.Seq&0xff)
+ copy(b[4:], p.Data)
+ return b, nil
}
-func newICMPInfoMessage(id, seqnum, msglen int, filler []byte) []byte {
- b := make([]byte, msglen)
- copy(b[8:], bytes.Repeat(filler, (msglen-8)/len(filler)+1))
- b[0] = 0 // type
- b[1] = 0 // code
- b[2] = 0 // checksum
- b[3] = 0 // checksum
- b[4] = uint8(id >> 8) // identifier
- b[5] = uint8(id & 0xff) // identifier
- b[6] = uint8(seqnum >> 8) // sequence number
- b[7] = uint8(seqnum & 0xff) // sequence number
- return b
+// parseICMPEcho parses b as an ICMP echo request or reply message
+// body.
+func parseICMPEcho(b []byte) (*icmpEcho, error) {
+ bodylen := len(b)
+ p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])}
+ if bodylen > 4 {
+ p.Data = make([]byte, bodylen-4)
+ copy(p.Data, b[4:])
+ }
+ return p, nil
}
-func parseICMPEchoReply(b []byte) (id, seqnum int) {
- id = int(b[4])<<8 | int(b[5])
- seqnum = int(b[6])<<8 | int(b[7])
- return
+var ipConnLocalNameTests = []struct {
+ net string
+ laddr *IPAddr
+}{
+ {"ip4:icmp", &IPAddr{IP: IPv4(127, 0, 0, 1)}},
+ {"ip4:icmp", &IPAddr{}},
+ {"ip4:icmp", nil},
+}
+
+func TestIPConnLocalName(t *testing.T) {
+ if os.Getuid() != 0 {
+ t.Skip("skipping test; must be root")
+ }
+
+ for _, tt := range ipConnLocalNameTests {
+ c, err := ListenIP(tt.net, tt.laddr)
+ if err != nil {
+ t.Fatalf("ListenIP failed: %v", err)
+ }
+ defer c.Close()
+ if la := c.LocalAddr(); la == nil {
+ t.Fatal("IPConn.LocalAddr failed")
+ }
+ }
}
diff --git a/src/pkg/net/iprawsock.go b/src/pkg/net/iprawsock.go
index b23213ee1..daccba366 100644
--- a/src/pkg/net/iprawsock.go
+++ b/src/pkg/net/iprawsock.go
@@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// (Raw) IP sockets
+// Raw IP sockets
package net
-// IPAddr represents the address of a IP end point.
+// IPAddr represents the address of an IP end point.
type IPAddr struct {
- IP IP
+ IP IP
+ Zone string // IPv6 scoped addressing zone
}
// Network returns the address's network name, "ip".
@@ -21,45 +22,25 @@ func (a *IPAddr) String() string {
return a.IP.String()
}
-// ResolveIPAddr parses addr as a IP address and resolves domain
+// ResolveIPAddr parses addr as an IP address and resolves domain
// names to numeric addresses on the network net, which must be
-// "ip", "ip4" or "ip6". A literal IPv6 host address must be
-// enclosed in square brackets, as in "[::]".
+// "ip", "ip4" or "ip6".
func ResolveIPAddr(net, addr string) (*IPAddr, error) {
- ip, err := hostToIP(net, addr)
+ if net == "" { // a hint wildcard for Go 1.0 undocumented behavior
+ net = "ip"
+ }
+ afnet, _, err := parseNetwork(net)
if err != nil {
return nil, err
}
- return &IPAddr{ip}, nil
-}
-
-// Convert "host" into IP address.
-func hostToIP(net, host string) (ip IP, err error) {
- var addr IP
- // Try as an IP address.
- addr = ParseIP(host)
- if addr == nil {
- filter := anyaddr
- if net != "" && net[len(net)-1] == '4' {
- filter = ipv4only
- }
- if net != "" && net[len(net)-1] == '6' {
- filter = ipv6only
- }
- // Not an IP address. Try as a DNS name.
- addrs, err1 := LookupHost(host)
- if err1 != nil {
- err = err1
- goto Error
- }
- addr = firstFavoriteAddr(filter, addrs)
- if addr == nil {
- // should not happen
- err = &AddrError{"LookupHost returned no suitable address", addrs[0]}
- goto Error
- }
+ switch afnet {
+ case "ip", "ip4", "ip6":
+ default:
+ return nil, UnknownNetworkError(net)
+ }
+ a, err := resolveInternetAddr(afnet, addr, noDeadline)
+ if err != nil {
+ return nil, err
}
- return addr, nil
-Error:
- return nil, err
+ return a.(*IPAddr), nil
}
diff --git a/src/pkg/net/iprawsock_plan9.go b/src/pkg/net/iprawsock_plan9.go
index 43719fc99..88e3b2c60 100644
--- a/src/pkg/net/iprawsock_plan9.go
+++ b/src/pkg/net/iprawsock_plan9.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// (Raw) IP sockets stubs for Plan 9
+// Raw IP sockets for Plan 9
package net
@@ -11,55 +11,13 @@ import (
"time"
)
-// IPConn is the implementation of the Conn and PacketConn
-// interfaces for IP network connections.
-type IPConn bool
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *IPConn) SetDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *IPConn) SetReadDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *IPConn) SetWriteDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// Implementation of the Conn interface - see Conn for documentation.
-
-// Read implements the Conn Read method.
-func (c *IPConn) Read(b []byte) (int, error) {
- return 0, syscall.EPLAN9
-}
-
-// Write implements the Conn Write method.
-func (c *IPConn) Write(b []byte) (int, error) {
- return 0, syscall.EPLAN9
-}
-
-// Close closes the IP connection.
-func (c *IPConn) Close() error {
- return syscall.EPLAN9
-}
-
-// LocalAddr returns the local network address.
-func (c *IPConn) LocalAddr() Addr {
- return nil
+// IPConn is the implementation of the Conn and PacketConn interfaces
+// for IP network connections.
+type IPConn struct {
+ conn
}
-// RemoteAddr returns the remote network address, a *IPAddr.
-func (c *IPConn) RemoteAddr() Addr {
- return nil
-}
-
-// IP-specific methods.
-
-// ReadFromIP reads a IP packet from c, copying the payload into b.
+// ReadFromIP reads an IP packet from c, copying the payload into b.
// It returns the number of bytes copied into b and the return address
// that was on the packet.
//
@@ -75,12 +33,21 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
return 0, nil, syscall.EPLAN9
}
-// WriteToIP writes a IP packet to addr via c, copying the payload from b.
+// ReadMsgIP reads a packet from c, copying the payload into b and the
+// associdated out-of-band data into oob. It returns the number of
+// bytes copied into b, the number of bytes copied into oob, the flags
+// that were set on the packet and the source address of the packet.
+func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err error) {
+ return 0, 0, 0, nil, syscall.EPLAN9
+}
+
+// WriteToIP writes an IP packet to addr via c, copying the payload
+// from b.
//
-// WriteToIP can be made to time out and return
-// an error with Timeout() == true after a fixed time limit;
-// see SetDeadline and SetWriteDeadline.
-// On packet-oriented connections, write timeouts are rare.
+// WriteToIP can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetWriteDeadline. On packet-oriented connections, write timeouts
+// are rare.
func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
return 0, syscall.EPLAN9
}
@@ -90,16 +57,28 @@ func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) {
return 0, syscall.EPLAN9
}
-// DialIP connects to the remote address raddr on the network protocol netProto,
-// which must be "ip", "ip4", or "ip6" followed by a colon and a protocol number or name.
+// WriteMsgIP writes a packet to addr via c, copying the payload from
+// b and the associated out-of-band data from oob. It returns the
+// number of payload and out-of-band bytes written.
+func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error) {
+ return 0, 0, syscall.EPLAN9
+}
+
+// DialIP connects to the remote address raddr on the network protocol
+// netProto, which must be "ip", "ip4", or "ip6" followed by a colon
+// and a protocol number or name.
func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
+ return dialIP(netProto, laddr, raddr, noDeadline)
+}
+
+func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) {
return nil, syscall.EPLAN9
}
-// ListenIP listens for incoming IP packets addressed to the
-// local address laddr. The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send IP
-// packets with per-packet addressing.
+// ListenIP listens for incoming IP packets addressed to the local
+// address laddr. The returned connection c's ReadFrom and WriteTo
+// methods can be used to receive and send IP packets with per-packet
+// addressing.
func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/src/pkg/net/iprawsock_posix.go b/src/pkg/net/iprawsock_posix.go
index 9fc7ecdb9..2ef4db19c 100644
--- a/src/pkg/net/iprawsock_posix.go
+++ b/src/pkg/net/iprawsock_posix.go
@@ -4,12 +4,11 @@
// +build darwin freebsd linux netbsd openbsd windows
-// (Raw) IP sockets
+// Raw IP sockets for POSIX
package net
import (
- "os"
"syscall"
"time"
)
@@ -17,9 +16,9 @@ import (
func sockaddrToIP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
- return &IPAddr{sa.Addr[0:]}
+ return &IPAddr{IP: sa.Addr[0:]}
case *syscall.SockaddrInet6:
- return &IPAddr{sa.Addr[0:]}
+ return &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
}
return nil
}
@@ -42,7 +41,7 @@ func (a *IPAddr) isWildcard() bool {
}
func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
- return ipToSockaddr(family, a.IP, 0)
+ return ipToSockaddr(family, a.IP, 0, a.Zone)
}
func (a *IPAddr) toAddr() sockaddr {
@@ -55,98 +54,12 @@ func (a *IPAddr) toAddr() sockaddr {
// IPConn is the implementation of the Conn and PacketConn
// interfaces for IP network connections.
type IPConn struct {
- fd *netFD
+ conn
}
-func newIPConn(fd *netFD) *IPConn { return &IPConn{fd} }
+func newIPConn(fd *netFD) *IPConn { return &IPConn{conn{fd}} }
-func (c *IPConn) ok() bool { return c != nil && c.fd != nil }
-
-// Implementation of the Conn interface - see Conn for documentation.
-
-// Read implements the Conn Read method.
-func (c *IPConn) Read(b []byte) (int, error) {
- n, _, err := c.ReadFrom(b)
- return n, err
-}
-
-// Write implements the Conn Write method.
-func (c *IPConn) Write(b []byte) (int, error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Write(b)
-}
-
-// Close closes the IP connection.
-func (c *IPConn) Close() error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return c.fd.Close()
-}
-
-// LocalAddr returns the local network address.
-func (c *IPConn) LocalAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.laddr
-}
-
-// RemoteAddr returns the remote network address, a *IPAddr.
-func (c *IPConn) RemoteAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.raddr
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *IPConn) SetDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setDeadline(c.fd, t)
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *IPConn) SetReadDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadDeadline(c.fd, t)
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *IPConn) SetWriteDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteDeadline(c.fd, t)
-}
-
-// SetReadBuffer sets the size of the operating system's
-// receive buffer associated with the connection.
-func (c *IPConn) SetReadBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadBuffer(c.fd, bytes)
-}
-
-// SetWriteBuffer sets the size of the operating system's
-// transmit buffer associated with the connection.
-func (c *IPConn) SetWriteBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteBuffer(c.fd, bytes)
-}
-
-// IP-specific methods.
-
-// ReadFromIP reads a IP packet from c, copying the payload into b.
+// ReadFromIP reads an IP packet from c, copying the payload into b.
// It returns the number of bytes copied into b and the return address
// that was on the packet.
//
@@ -163,14 +76,14 @@ func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) {
n, sa, err := c.fd.ReadFrom(b)
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
- addr = &IPAddr{sa.Addr[0:]}
+ addr = &IPAddr{IP: sa.Addr[0:]}
if len(b) >= IPv4len { // discard ipv4 header
hsize := (int(b[0]) & 0xf) * 4
copy(b, b[hsize:])
n -= hsize
}
case *syscall.SockaddrInet6:
- addr = &IPAddr{sa.Addr[0:]}
+ addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
}
return n, addr, err
}
@@ -180,11 +93,30 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
if !c.ok() {
return 0, nil, syscall.EINVAL
}
- n, uaddr, err := c.ReadFromIP(b)
- return n, uaddr.toAddr(), err
+ n, addr, err := c.ReadFromIP(b)
+ return n, addr.toAddr(), err
+}
+
+// ReadMsgIP reads a packet from c, copying the payload into b and the
+// associdated out-of-band data into oob. It returns the number of
+// bytes copied into b, the number of bytes copied into oob, the flags
+// that were set on the packet and the source address of the packet.
+func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err error) {
+ if !c.ok() {
+ return 0, 0, 0, nil, syscall.EINVAL
+ }
+ var sa syscall.Sockaddr
+ n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob)
+ switch sa := sa.(type) {
+ case *syscall.SockaddrInet4:
+ addr = &IPAddr{IP: sa.Addr[0:]}
+ case *syscall.SockaddrInet6:
+ addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
+ }
+ return
}
-// WriteToIP writes a IP packet to addr via c, copying the payload from b.
+// WriteToIP writes an IP packet to addr via c, copying the payload from b.
//
// WriteToIP can be made to time out and return
// an error with Timeout() == true after a fixed time limit;
@@ -213,22 +145,40 @@ func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) {
return c.WriteToIP(b, a)
}
+// WriteMsgIP writes a packet to addr via c, copying the payload from
+// b and the associated out-of-band data from oob. It returns the
+// number of payload and out-of-band bytes written.
+func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error) {
+ if !c.ok() {
+ return 0, 0, syscall.EINVAL
+ }
+ sa, err := addr.sockaddr(c.fd.family)
+ if err != nil {
+ return 0, 0, &OpError{"write", c.fd.net, addr, err}
+ }
+ return c.fd.WriteMsg(b, oob, sa)
+}
+
// DialIP connects to the remote address raddr on the network protocol netProto,
// which must be "ip", "ip4", or "ip6" followed by a colon and a protocol number or name.
func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
- net, proto, err := parseDialNetwork(netProto)
+ return dialIP(netProto, laddr, raddr, noDeadline)
+}
+
+func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) {
+ net, proto, err := parseNetwork(netProto)
if err != nil {
return nil, err
}
switch net {
case "ip", "ip4", "ip6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, UnknownNetworkError(netProto)
}
if raddr == nil {
return nil, &OpError{"dial", netProto, nil, errMissingAddress}
}
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_RAW, proto, "dial", sockaddrToIP)
+ fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_RAW, proto, "dial", sockaddrToIP)
if err != nil {
return nil, err
}
@@ -240,23 +190,18 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
// and WriteTo methods can be used to receive and send IP
// packets with per-packet addressing.
func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
- net, proto, err := parseDialNetwork(netProto)
+ net, proto, err := parseNetwork(netProto)
if err != nil {
return nil, err
}
switch net {
case "ip", "ip4", "ip6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, UnknownNetworkError(netProto)
}
- fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_RAW, proto, "listen", sockaddrToIP)
+ fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_RAW, proto, "listen", sockaddrToIP)
if err != nil {
return nil, err
}
return newIPConn(fd), nil
}
-
-// File returns a copy of the underlying os.File, set to blocking mode.
-// It is the caller's responsibility to close f when finished.
-// Closing c does not affect f, and closing f does not affect c.
-func (c *IPConn) File() (f *os.File, err error) { return c.fd.dup() }
diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go
index bfbce18a4..1ef489289 100644
--- a/src/pkg/net/ipsock.go
+++ b/src/pkg/net/ipsock.go
@@ -2,11 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// IP sockets
+// Internet protocol family sockets
package net
-var supportsIPv6, supportsIPv4map = probeIPv6Stack()
+import "time"
+
+var supportsIPv6, supportsIPv4map bool
+
+func init() {
+ sysInit()
+ supportsIPv6, supportsIPv4map = probeIPv6Stack()
+}
func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) {
if filter == nil {
@@ -65,25 +72,67 @@ func (e InvalidAddrError) Temporary() bool { return false }
// "host:port" or "[host]:port" into host and port.
// The latter form must be used when host contains a colon.
func SplitHostPort(hostport string) (host, port string, err error) {
+ host, port, _, err = splitHostPort(hostport)
+ return
+}
+
+func splitHostPort(hostport string) (host, port, zone string, err error) {
+ j, k := 0, 0
+
// The port starts after the last colon.
i := last(hostport, ':')
if i < 0 {
- err = &AddrError{"missing port in address", hostport}
- return
+ goto missingPort
}
- host, port = hostport[0:i], hostport[i+1:]
-
- // Can put brackets around host ...
- if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
- host = host[1 : len(host)-1]
+ if hostport[0] == '[' {
+ // Expect the first ']' just before the last ':'.
+ end := byteIndex(hostport, ']')
+ if end < 0 {
+ err = &AddrError{"missing ']' in address", hostport}
+ return
+ }
+ switch end + 1 {
+ case len(hostport):
+ // There can't be a ':' behind the ']' now.
+ goto missingPort
+ case i:
+ // The expected result.
+ default:
+ // Either ']' isn't followed by a colon, or it is
+ // followed by a colon that is not the last one.
+ if hostport[end+1] == ':' {
+ goto tooManyColons
+ }
+ goto missingPort
+ }
+ host = hostport[1:end]
+ j, k = 1, end+1 // there can't be a '[' resp. ']' before these positions
} else {
- // ... but if there are no brackets, no colons.
+ host = hostport[:i]
+
if byteIndex(host, ':') >= 0 {
- err = &AddrError{"too many colons in address", hostport}
- return
+ goto tooManyColons
}
}
+ if byteIndex(hostport[j:], '[') >= 0 {
+ err = &AddrError{"unexpected '[' in address", hostport}
+ return
+ }
+ if byteIndex(hostport[k:], ']') >= 0 {
+ err = &AddrError{"unexpected ']' in address", hostport}
+ return
+ }
+
+ port = hostport[i+1:]
+ return
+
+missingPort:
+ err = &AddrError{"missing port in address", hostport}
+ return
+
+tooManyColons:
+ err = &AddrError{"too many colons in address", hostport}
return
}
@@ -97,49 +146,84 @@ func JoinHostPort(host, port string) string {
return host + ":" + port
}
-// Convert "host:port" into IP address and port.
-func hostPortToIP(net, hostport string) (ip IP, iport int, err error) {
- host, port, err := SplitHostPort(hostport)
- if err != nil {
- return nil, 0, err
- }
-
- var addr IP
- if host != "" {
- // Try as an IP address.
- addr = ParseIP(host)
- if addr == nil {
- var filter func(IP) IP
- if net != "" && net[len(net)-1] == '4' {
- filter = ipv4only
+func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) {
+ var (
+ err error
+ host, port, zone string
+ portnum int
+ )
+ switch net {
+ case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
+ if addr != "" {
+ if host, port, zone, err = splitHostPort(addr); err != nil {
+ return nil, err
}
- if net != "" && net[len(net)-1] == '6' {
- filter = ipv6only
- }
- // Not an IP address. Try as a DNS name.
- addrs, err := LookupHost(host)
- if err != nil {
- return nil, 0, err
- }
- addr = firstFavoriteAddr(filter, addrs)
- if addr == nil {
- // should not happen
- return nil, 0, &AddrError{"LookupHost returned no suitable address", addrs[0]}
+ if portnum, err = parsePort(net, port); err != nil {
+ return nil, err
}
}
+ case "ip", "ip4", "ip6":
+ if addr != "" {
+ host = addr
+ }
+ default:
+ return nil, UnknownNetworkError(net)
}
-
- p, i, ok := dtoi(port, 0)
- if !ok || i != len(port) {
- p, err = LookupPort(net, port)
- if err != nil {
- return nil, 0, err
+ inetaddr := func(net string, ip IP, port int, zone string) Addr {
+ switch net {
+ case "tcp", "tcp4", "tcp6":
+ return &TCPAddr{IP: ip, Port: port, Zone: zone}
+ case "udp", "udp4", "udp6":
+ return &UDPAddr{IP: ip, Port: port, Zone: zone}
+ case "ip", "ip4", "ip6":
+ return &IPAddr{IP: ip, Zone: zone}
}
+ return nil
+ }
+ if host == "" {
+ return inetaddr(net, nil, portnum, zone), nil
}
- if p < 0 || p > 0xFFFF {
- return nil, 0, &AddrError{"invalid port", port}
+ // Try as an IP address.
+ if ip := ParseIP(host); ip != nil {
+ return inetaddr(net, ip, portnum, zone), nil
}
+ var filter func(IP) IP
+ if net != "" && net[len(net)-1] == '4' {
+ filter = ipv4only
+ }
+ if net != "" && net[len(net)-1] == '6' {
+ filter = ipv6only
+ }
+ // Try as a DNS name.
+ addrs, err := lookupHostDeadline(host, deadline)
+ if err != nil {
+ return nil, err
+ }
+ ip := firstFavoriteAddr(filter, addrs)
+ if ip == nil {
+ // should not happen
+ return nil, &AddrError{"LookupHost returned no suitable address", addrs[0]}
+ }
+ return inetaddr(net, ip, portnum, zone), nil
+}
- return addr, p, nil
+func zoneToString(zone int) string {
+ if zone == 0 {
+ return ""
+ }
+ if ifi, err := InterfaceByIndex(zone); err == nil {
+ return ifi.Name
+ }
+ return itod(uint(zone))
+}
+func zoneToInt(zone string) int {
+ if zone == "" {
+ return 0
+ }
+ if ifi, err := InterfaceByName(zone); err == nil {
+ return ifi.Index
+ }
+ n, _, _ := dtoi(zone, 0)
+ return n
}
diff --git a/src/pkg/net/ipsock_plan9.go b/src/pkg/net/ipsock_plan9.go
index eab0bf3e8..c7d542dab 100644
--- a/src/pkg/net/ipsock_plan9.go
+++ b/src/pkg/net/ipsock_plan9.go
@@ -2,21 +2,22 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// IP sockets stubs for Plan 9
+// Internet protocol family sockets for Plan 9
package net
import (
"errors"
- "io"
"os"
"syscall"
- "time"
)
-// probeIPv6Stack returns two boolean values. If the first boolean value is
-// true, kernel supports basic IPv6 functionality. If the second
-// boolean value is true, kernel supports IPv6 IPv4-mapping.
+// /sys/include/ape/sys/socket.h:/SOMAXCONN
+var listenerBacklog = 5
+
+// probeIPv6Stack returns two boolean values. If the first boolean
+// value is true, kernel supports basic IPv6 functionality. If the
+// second boolean value is true, kernel supports IPv6 IPv4-mapping.
func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
return false, false
}
@@ -48,6 +49,7 @@ func readPlan9Addr(proto, filename string) (addr Addr, err error) {
if err != nil {
return
}
+ defer f.Close()
n, err := f.Read(buf[:])
if err != nil {
return
@@ -58,110 +60,15 @@ func readPlan9Addr(proto, filename string) (addr Addr, err error) {
}
switch proto {
case "tcp":
- addr = &TCPAddr{ip, port}
+ addr = &TCPAddr{IP: ip, Port: port}
case "udp":
- addr = &UDPAddr{ip, port}
+ addr = &UDPAddr{IP: ip, Port: port}
default:
return nil, errors.New("unknown protocol " + proto)
}
return addr, nil
}
-type plan9Conn struct {
- proto, name, dir string
- ctl, data *os.File
- laddr, raddr Addr
-}
-
-func newPlan9Conn(proto, name string, ctl *os.File, laddr, raddr Addr) *plan9Conn {
- return &plan9Conn{proto, name, "/net/" + proto + "/" + name, ctl, nil, laddr, raddr}
-}
-
-func (c *plan9Conn) ok() bool { return c != nil && c.ctl != nil }
-
-// Implementation of the Conn interface - see Conn for documentation.
-
-// Read implements the Conn Read method.
-func (c *plan9Conn) Read(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- if c.data == nil {
- c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0)
- if err != nil {
- return 0, err
- }
- }
- n, err = c.data.Read(b)
- if c.proto == "udp" && err == io.EOF {
- n = 0
- err = nil
- }
- return
-}
-
-// Write implements the Conn Write method.
-func (c *plan9Conn) Write(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- if c.data == nil {
- c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0)
- if err != nil {
- return 0, err
- }
- }
- return c.data.Write(b)
-}
-
-// Close closes the connection.
-func (c *plan9Conn) Close() error {
- if !c.ok() {
- return syscall.EINVAL
- }
- err := c.ctl.Close()
- if err != nil {
- return err
- }
- if c.data != nil {
- err = c.data.Close()
- }
- c.ctl = nil
- c.data = nil
- return err
-}
-
-// LocalAddr returns the local network address.
-func (c *plan9Conn) LocalAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.laddr
-}
-
-// RemoteAddr returns the remote network address.
-func (c *plan9Conn) RemoteAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.raddr
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *plan9Conn) SetDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *plan9Conn) SetReadDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *plan9Conn) SetWriteDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string, err error) {
var (
ip IP
@@ -192,98 +99,95 @@ func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string,
var buf [16]byte
n, err := f.Read(buf[:])
if err != nil {
+ f.Close()
return
}
return f, dest, proto, string(buf[:n]), nil
}
-func dialPlan9(net string, laddr, raddr Addr) (c *plan9Conn, err error) {
+func netErr(e error) {
+ oe, ok := e.(*OpError)
+ if !ok {
+ return
+ }
+ if pe, ok := oe.Err.(*os.PathError); ok {
+ if _, ok = pe.Err.(syscall.ErrorString); ok {
+ oe.Err = pe.Err
+ }
+ }
+}
+
+func dialPlan9(net string, laddr, raddr Addr) (fd *netFD, err error) {
+ defer func() { netErr(err) }()
f, dest, proto, name, err := startPlan9(net, raddr)
if err != nil {
- return
+ return nil, &OpError{"dial", net, raddr, err}
}
_, err = f.WriteString("connect " + dest)
if err != nil {
- return
+ f.Close()
+ return nil, &OpError{"dial", f.Name(), raddr, err}
}
- laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local")
+ data, err := os.OpenFile("/net/"+proto+"/"+name+"/data", os.O_RDWR, 0)
if err != nil {
- return
+ f.Close()
+ return nil, &OpError{"dial", net, raddr, err}
}
- raddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/remote")
+ laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local")
if err != nil {
- return
+ data.Close()
+ f.Close()
+ return nil, &OpError{"dial", proto, raddr, err}
}
- return newPlan9Conn(proto, name, f, laddr, raddr), nil
-}
-
-type plan9Listener struct {
- proto, name, dir string
- ctl *os.File
- laddr Addr
+ return newFD(proto, name, f, data, laddr, raddr), nil
}
-func listenPlan9(net string, laddr Addr) (l *plan9Listener, err error) {
+func listenPlan9(net string, laddr Addr) (fd *netFD, err error) {
+ defer func() { netErr(err) }()
f, dest, proto, name, err := startPlan9(net, laddr)
if err != nil {
- return
+ return nil, &OpError{"listen", net, laddr, err}
}
_, err = f.WriteString("announce " + dest)
if err != nil {
- return
+ f.Close()
+ return nil, &OpError{"announce", proto, laddr, err}
}
laddr, err = readPlan9Addr(proto, "/net/"+proto+"/"+name+"/local")
if err != nil {
- return
+ f.Close()
+ return nil, &OpError{Op: "listen", Net: net, Err: err}
}
- l = new(plan9Listener)
- l.proto = proto
- l.name = name
- l.dir = "/net/" + proto + "/" + name
- l.ctl = f
- l.laddr = laddr
- return l, nil
+ return newFD(proto, name, f, nil, laddr, nil), nil
}
-func (l *plan9Listener) plan9Conn() *plan9Conn {
- return newPlan9Conn(l.proto, l.name, l.ctl, l.laddr, nil)
+func (l *netFD) netFD() *netFD {
+ return newFD(l.proto, l.name, l.ctl, l.data, l.laddr, l.raddr)
}
-func (l *plan9Listener) acceptPlan9() (c *plan9Conn, err error) {
+func (l *netFD) acceptPlan9() (fd *netFD, err error) {
+ defer func() { netErr(err) }()
f, err := os.Open(l.dir + "/listen")
if err != nil {
- return
+ return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err}
}
var buf [16]byte
n, err := f.Read(buf[:])
if err != nil {
- return
+ f.Close()
+ return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err}
}
name := string(buf[:n])
- laddr, err := readPlan9Addr(l.proto, l.dir+"/local")
- if err != nil {
- return
- }
- raddr, err := readPlan9Addr(l.proto, l.dir+"/remote")
+ data, err := os.OpenFile("/net/"+l.proto+"/"+name+"/data", os.O_RDWR, 0)
if err != nil {
- return
+ f.Close()
+ return nil, &OpError{"accept", l.proto, l.laddr, err}
}
- return newPlan9Conn(l.proto, name, f, laddr, raddr), nil
-}
-
-func (l *plan9Listener) Accept() (c Conn, err error) {
- c1, err := l.acceptPlan9()
+ raddr, err := readPlan9Addr(l.proto, "/net/"+l.proto+"/"+name+"/remote")
if err != nil {
- return
+ data.Close()
+ f.Close()
+ return nil, &OpError{"accept", l.proto, l.laddr, err}
}
- return c1, nil
+ return newFD(l.proto, name, f, data, l.laddr, raddr), nil
}
-
-func (l *plan9Listener) Close() error {
- if l == nil || l.ctl == nil {
- return syscall.EINVAL
- }
- return l.ctl.Close()
-}
-
-func (l *plan9Listener) Addr() Addr { return l.laddr }
diff --git a/src/pkg/net/ipsock_posix.go b/src/pkg/net/ipsock_posix.go
index ed313195c..4c37616ec 100644
--- a/src/pkg/net/ipsock_posix.go
+++ b/src/pkg/net/ipsock_posix.go
@@ -4,9 +4,14 @@
// +build darwin freebsd linux netbsd openbsd windows
+// Internet protocol family sockets for POSIX
+
package net
-import "syscall"
+import (
+ "syscall"
+ "time"
+)
// Should we try to use the IPv4 socket interface if we're
// only dealing with IPv4 sockets? As long as the host system
@@ -97,10 +102,13 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family
return syscall.AF_INET6, true
}
- if mode == "listen" && laddr.isWildcard() {
+ if mode == "listen" && (laddr == nil || laddr.isWildcard()) {
if supportsIPv4map {
return syscall.AF_INET6, false
}
+ if laddr == nil {
+ return syscall.AF_INET, false
+ }
return laddr.family(), false
}
@@ -122,7 +130,7 @@ type sockaddr interface {
sockaddr(family int) (syscall.Sockaddr, error)
}
-func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
+func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
var la, ra syscall.Sockaddr
family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
if laddr != nil {
@@ -135,7 +143,7 @@ func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode s
goto Error
}
}
- fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, toAddr)
+ fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, deadline, toAddr)
if err != nil {
goto Error
}
@@ -149,7 +157,7 @@ Error:
return nil, &OpError{mode, net, addr, err}
}
-func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) {
+func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) {
switch family {
case syscall.AF_INET:
if len(ip) == 0 {
@@ -158,12 +166,12 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) {
if ip = ip.To4(); ip == nil {
return nil, InvalidAddrError("non-IPv4 address")
}
- s := new(syscall.SockaddrInet4)
+ sa := new(syscall.SockaddrInet4)
for i := 0; i < IPv4len; i++ {
- s.Addr[i] = ip[i]
+ sa.Addr[i] = ip[i]
}
- s.Port = port
- return s, nil
+ sa.Port = port
+ return sa, nil
case syscall.AF_INET6:
if len(ip) == 0 {
ip = IPv6zero
@@ -177,12 +185,13 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) {
if ip = ip.To16(); ip == nil {
return nil, InvalidAddrError("non-IPv6 address")
}
- s := new(syscall.SockaddrInet6)
+ sa := new(syscall.SockaddrInet6)
for i := 0; i < IPv6len; i++ {
- s.Addr[i] = ip[i]
+ sa.Addr[i] = ip[i]
}
- s.Port = port
- return s, nil
+ sa.Port = port
+ sa.ZoneId = uint32(zoneToInt(zone))
+ return sa, nil
}
return nil, InvalidAddrError("unexpected socket family")
}
diff --git a/src/pkg/net/doc.go b/src/pkg/net/lookup.go
index 3a44e528e..bec93ec08 100644
--- a/src/pkg/net/doc.go
+++ b/src/pkg/net/lookup.go
@@ -4,12 +4,53 @@
package net
+import (
+ "time"
+)
+
// LookupHost looks up the given host using the local resolver.
// It returns an array of that host's addresses.
func LookupHost(host string) (addrs []string, err error) {
return lookupHost(host)
}
+func lookupHostDeadline(host string, deadline time.Time) (addrs []string, err error) {
+ if deadline.IsZero() {
+ return lookupHost(host)
+ }
+
+ // TODO(bradfitz): consider pushing the deadline down into the
+ // name resolution functions. But that involves fixing it for
+ // the native Go resolver, cgo, Windows, etc.
+ //
+ // In the meantime, just use a goroutine. Most users affected
+ // by http://golang.org/issue/2631 are due to TCP connections
+ // to unresponsive hosts, not DNS.
+ timeout := deadline.Sub(time.Now())
+ if timeout <= 0 {
+ err = errTimeout
+ return
+ }
+ t := time.NewTimer(timeout)
+ defer t.Stop()
+ type res struct {
+ addrs []string
+ err error
+ }
+ resc := make(chan res, 1)
+ go func() {
+ a, err := lookupHost(host)
+ resc <- res{a, err}
+ }()
+ select {
+ case <-t.C:
+ err = errTimeout
+ case r := <-resc:
+ addrs, err = r.addrs, r.err
+ }
+ return
+}
+
// LookupIP looks up host using the local resolver.
// It returns an array of that host's IPv4 and IPv6 addresses.
func LookupIP(host string) (addrs []IP, err error) {
@@ -47,6 +88,11 @@ func LookupMX(name string) (mx []*MX, err error) {
return lookupMX(name)
}
+// LookupNS returns the DNS NS records for the given domain name.
+func LookupNS(name string) (ns []*NS, err error) {
+ return lookupNS(name)
+}
+
// LookupTXT returns the DNS TXT records for the given domain name.
func LookupTXT(name string) (txt []string, err error) {
return lookupTXT(name)
diff --git a/src/pkg/net/lookup_plan9.go b/src/pkg/net/lookup_plan9.go
index 2c698304b..ae7cf7942 100644
--- a/src/pkg/net/lookup_plan9.go
+++ b/src/pkg/net/lookup_plan9.go
@@ -201,6 +201,21 @@ func lookupMX(name string) (mx []*MX, err error) {
return
}
+func lookupNS(name string) (ns []*NS, err error) {
+ lines, err := queryDNS(name, "ns")
+ if err != nil {
+ return
+ }
+ for _, line := range lines {
+ f := getFields(line)
+ if len(f) < 4 {
+ continue
+ }
+ ns = append(ns, &NS{f[3]})
+ }
+ return
+}
+
func lookupTXT(name string) (txt []string, err error) {
lines, err := queryDNS(name, "txt")
if err != nil {
diff --git a/src/pkg/net/lookup_test.go b/src/pkg/net/lookup_test.go
index 3a61dfb29..3355e4694 100644
--- a/src/pkg/net/lookup_test.go
+++ b/src/pkg/net/lookup_test.go
@@ -9,6 +9,7 @@ package net
import (
"flag"
+ "strings"
"testing"
)
@@ -16,8 +17,7 @@ var testExternal = flag.Bool("external", true, "allow use of external networks d
func TestGoogleSRV(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Logf("skipping test to avoid external network")
- return
+ t.Skip("skipping test to avoid external network")
}
_, addrs, err := LookupSRV("xmpp-server", "tcp", "google.com")
if err != nil {
@@ -39,8 +39,7 @@ func TestGoogleSRV(t *testing.T) {
func TestGmailMX(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Logf("skipping test to avoid external network")
- return
+ t.Skip("skipping test to avoid external network")
}
mx, err := LookupMX("gmail.com")
if err != nil {
@@ -51,10 +50,22 @@ func TestGmailMX(t *testing.T) {
}
}
+func TestGmailNS(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+ ns, err := LookupNS("gmail.com")
+ if err != nil {
+ t.Errorf("failed: %s", err)
+ }
+ if len(ns) == 0 {
+ t.Errorf("no results")
+ }
+}
+
func TestGmailTXT(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Logf("skipping test to avoid external network")
- return
+ t.Skip("skipping test to avoid external network")
}
txt, err := LookupTXT("gmail.com")
if err != nil {
@@ -67,8 +78,7 @@ func TestGmailTXT(t *testing.T) {
func TestGoogleDNSAddr(t *testing.T) {
if testing.Short() || !*testExternal {
- t.Logf("skipping test to avoid external network")
- return
+ t.Skip("skipping test to avoid external network")
}
names, err := LookupAddr("8.8.8.8")
if err != nil {
@@ -79,6 +89,16 @@ func TestGoogleDNSAddr(t *testing.T) {
}
}
+func TestLookupIANACNAME(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+ cname, err := LookupCNAME("www.iana.org")
+ if !strings.HasSuffix(cname, ".icann.org.") || err != nil {
+ t.Errorf(`LookupCNAME("www.iana.org.") = %q, %v, want "*.icann.org.", nil`, cname, err)
+ }
+}
+
var revAddrTests = []struct {
Addr string
Reverse string
diff --git a/src/pkg/net/lookup_unix.go b/src/pkg/net/lookup_unix.go
index d500a1240..fa98eed5f 100644
--- a/src/pkg/net/lookup_unix.go
+++ b/src/pkg/net/lookup_unix.go
@@ -119,6 +119,19 @@ func lookupMX(name string) (mx []*MX, err error) {
return
}
+func lookupNS(name string) (ns []*NS, err error) {
+ _, records, err := lookup(name, dnsTypeNS)
+ if err != nil {
+ return
+ }
+ ns = make([]*NS, len(records))
+ for i, r := range records {
+ r := r.(*dnsRR_NS)
+ ns[i] = &NS{r.Ns}
+ }
+ return
+}
+
func lookupTXT(name string) (txt []string, err error) {
_, records, err := lookup(name, dnsTypeTXT)
if err != nil {
diff --git a/src/pkg/net/lookup_windows.go b/src/pkg/net/lookup_windows.go
index 99783e975..3b29724f2 100644
--- a/src/pkg/net/lookup_windows.go
+++ b/src/pkg/net/lookup_windows.go
@@ -6,21 +6,17 @@ package net
import (
"os"
- "sync"
+ "runtime"
"syscall"
"unsafe"
)
var (
- protoentLock sync.Mutex
- hostentLock sync.Mutex
- serventLock sync.Mutex
+ lookupPort = oldLookupPort
+ lookupIP = oldLookupIP
)
-// lookupProtocol looks up IP protocol name and returns correspondent protocol number.
-func lookupProtocol(name string) (proto int, err error) {
- protoentLock.Lock()
- defer protoentLock.Unlock()
+func getprotobyname(name string) (proto int, err error) {
p, err := syscall.GetProtoByName(name)
if err != nil {
return 0, os.NewSyscallError("GetProtoByName", err)
@@ -28,6 +24,25 @@ func lookupProtocol(name string) (proto int, err error) {
return int(p.Proto), nil
}
+// lookupProtocol looks up IP protocol name and returns correspondent protocol number.
+func lookupProtocol(name string) (proto int, err error) {
+ // GetProtoByName return value is stored in thread local storage.
+ // Start new os thread before the call to prevent races.
+ type result struct {
+ proto int
+ err error
+ }
+ ch := make(chan result)
+ go func() {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+ proto, err := getprotobyname(name)
+ ch <- result{proto: proto, err: err}
+ }()
+ r := <-ch
+ return r.proto, r.err
+}
+
func lookupHost(name string) (addrs []string, err error) {
ips, err := LookupIP(name)
if err != nil {
@@ -40,9 +55,7 @@ func lookupHost(name string) (addrs []string, err error) {
return
}
-func lookupIP(name string) (addrs []IP, err error) {
- hostentLock.Lock()
- defer hostentLock.Unlock()
+func gethostbyname(name string) (addrs []IP, err error) {
h, err := syscall.GetHostByName(name)
if err != nil {
return nil, os.NewSyscallError("GetHostByName", err)
@@ -56,20 +69,65 @@ func lookupIP(name string) (addrs []IP, err error) {
}
addrs = addrs[0:i]
default: // TODO(vcc): Implement non IPv4 address lookups.
- return nil, os.NewSyscallError("LookupHost", syscall.EWINDOWS)
+ return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS)
}
return addrs, nil
}
-func lookupPort(network, service string) (port int, err error) {
+func oldLookupIP(name string) (addrs []IP, err error) {
+ // GetHostByName return value is stored in thread local storage.
+ // Start new os thread before the call to prevent races.
+ type result struct {
+ addrs []IP
+ err error
+ }
+ ch := make(chan result)
+ go func() {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+ addrs, err := gethostbyname(name)
+ ch <- result{addrs: addrs, err: err}
+ }()
+ r := <-ch
+ return r.addrs, r.err
+}
+
+func newLookupIP(name string) (addrs []IP, err error) {
+ hints := syscall.AddrinfoW{
+ Family: syscall.AF_UNSPEC,
+ Socktype: syscall.SOCK_STREAM,
+ Protocol: syscall.IPPROTO_IP,
+ }
+ var result *syscall.AddrinfoW
+ e := syscall.GetAddrInfoW(syscall.StringToUTF16Ptr(name), nil, &hints, &result)
+ if e != nil {
+ return nil, os.NewSyscallError("GetAddrInfoW", e)
+ }
+ defer syscall.FreeAddrInfoW(result)
+ addrs = make([]IP, 0, 5)
+ for ; result != nil; result = result.Next {
+ addr := unsafe.Pointer(result.Addr)
+ switch result.Family {
+ case syscall.AF_INET:
+ a := (*syscall.RawSockaddrInet4)(addr).Addr
+ addrs = append(addrs, IPv4(a[0], a[1], a[2], a[3]))
+ case syscall.AF_INET6:
+ a := (*syscall.RawSockaddrInet6)(addr).Addr
+ addrs = append(addrs, IP{a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]})
+ default:
+ return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS)
+ }
+ }
+ return addrs, nil
+}
+
+func getservbyname(network, service string) (port int, err error) {
switch network {
case "tcp4", "tcp6":
network = "tcp"
case "udp4", "udp6":
network = "udp"
}
- serventLock.Lock()
- defer serventLock.Unlock()
s, err := syscall.GetServByName(service, network)
if err != nil {
return 0, os.NewSyscallError("GetServByName", err)
@@ -77,6 +135,58 @@ func lookupPort(network, service string) (port int, err error) {
return int(syscall.Ntohs(s.Port)), nil
}
+func oldLookupPort(network, service string) (port int, err error) {
+ // GetServByName return value is stored in thread local storage.
+ // Start new os thread before the call to prevent races.
+ type result struct {
+ port int
+ err error
+ }
+ ch := make(chan result)
+ go func() {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+ port, err := getservbyname(network, service)
+ ch <- result{port: port, err: err}
+ }()
+ r := <-ch
+ return r.port, r.err
+}
+
+func newLookupPort(network, service string) (port int, err error) {
+ var stype int32
+ switch network {
+ case "tcp4", "tcp6":
+ stype = syscall.SOCK_STREAM
+ case "udp4", "udp6":
+ stype = syscall.SOCK_DGRAM
+ }
+ hints := syscall.AddrinfoW{
+ Family: syscall.AF_UNSPEC,
+ Socktype: stype,
+ Protocol: syscall.IPPROTO_IP,
+ }
+ var result *syscall.AddrinfoW
+ e := syscall.GetAddrInfoW(nil, syscall.StringToUTF16Ptr(service), &hints, &result)
+ if e != nil {
+ return 0, os.NewSyscallError("GetAddrInfoW", e)
+ }
+ defer syscall.FreeAddrInfoW(result)
+ if result == nil {
+ return 0, os.NewSyscallError("LookupPort", syscall.EINVAL)
+ }
+ addr := unsafe.Pointer(result.Addr)
+ switch result.Family {
+ case syscall.AF_INET:
+ a := (*syscall.RawSockaddrInet4)(addr)
+ return int(syscall.Ntohs(a.Port)), nil
+ case syscall.AF_INET6:
+ a := (*syscall.RawSockaddrInet6)(addr)
+ return int(syscall.Ntohs(a.Port)), nil
+ }
+ return 0, os.NewSyscallError("LookupPort", syscall.EINVAL)
+}
+
func lookupCNAME(name string) (cname string, err error) {
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil)
@@ -129,6 +239,21 @@ func lookupMX(name string) (mx []*MX, err error) {
return mx, nil
}
+func lookupNS(name string) (ns []*NS, err error) {
+ var r *syscall.DNSRecord
+ e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &r, nil)
+ if e != nil {
+ return nil, os.NewSyscallError("LookupNS", e)
+ }
+ defer syscall.DnsRecordListFree(r, 1)
+ ns = make([]*NS, 0, 10)
+ for p := r; p != nil && p.Type == syscall.DNS_TYPE_NS; p = p.Next {
+ v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
+ ns = append(ns, &NS{syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]) + "."})
+ }
+ return ns, nil
+}
+
func lookupTXT(name string) (txt []string, err error) {
var r *syscall.DNSRecord
e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil)
diff --git a/src/pkg/net/mail/message.go b/src/pkg/net/mail/message.go
index b610ccf3f..96c796e78 100644
--- a/src/pkg/net/mail/message.go
+++ b/src/pkg/net/mail/message.go
@@ -47,7 +47,8 @@ type Message struct {
}
// ReadMessage reads a message from r.
-// The headers are parsed, and the body of the message will be reading from r.
+// The headers are parsed, and the body of the message will be available
+// for reading from r.
func ReadMessage(r io.Reader) (msg *Message, err error) {
tp := textproto.NewReader(bufio.NewReader(r))
@@ -126,7 +127,7 @@ func (h Header) AddressList(key string) ([]*Address, error) {
if hdr == "" {
return nil, ErrHeaderNotPresent
}
- return newAddrParser(hdr).parseAddressList()
+ return ParseAddressList(hdr)
}
// Address represents a single mail address.
@@ -137,6 +138,16 @@ type Address struct {
Address string // user@domain
}
+// Parses a single RFC 5322 address, e.g. "Barry Gibbs <bg@example.com>"
+func ParseAddress(address string) (*Address, error) {
+ return newAddrParser(address).parseAddress()
+}
+
+// ParseAddressList parses the given string as a list of addresses.
+func ParseAddressList(list string) ([]*Address, error) {
+ return newAddrParser(list).parseAddressList()
+}
+
// String formats the address as a valid RFC 5322 address.
// If the address's name contains non-ASCII characters
// the name will be rendered according to RFC 2047.
diff --git a/src/pkg/net/mail/message_test.go b/src/pkg/net/mail/message_test.go
index fd17eb414..2e746f4a7 100644
--- a/src/pkg/net/mail/message_test.go
+++ b/src/pkg/net/mail/message_test.go
@@ -227,13 +227,24 @@ func TestAddressParsing(t *testing.T) {
},
}
for _, test := range tests {
- addrs, err := newAddrParser(test.addrsStr).parseAddressList()
+ if len(test.exp) == 1 {
+ addr, err := ParseAddress(test.addrsStr)
+ if err != nil {
+ t.Errorf("Failed parsing (single) %q: %v", test.addrsStr, err)
+ continue
+ }
+ if !reflect.DeepEqual([]*Address{addr}, test.exp) {
+ t.Errorf("Parse (single) of %q: got %+v, want %+v", test.addrsStr, addr, test.exp)
+ }
+ }
+
+ addrs, err := ParseAddressList(test.addrsStr)
if err != nil {
- t.Errorf("Failed parsing %q: %v", test.addrsStr, err)
+ t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err)
continue
}
if !reflect.DeepEqual(addrs, test.exp) {
- t.Errorf("Parse of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp)
+ t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp)
}
}
}
diff --git a/src/pkg/net/multicast_posix_test.go b/src/pkg/net/multicast_posix_test.go
new file mode 100644
index 000000000..ff1edaf83
--- /dev/null
+++ b/src/pkg/net/multicast_posix_test.go
@@ -0,0 +1,180 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !plan9
+
+package net
+
+import (
+ "errors"
+ "os"
+ "runtime"
+ "testing"
+)
+
+var multicastListenerTests = []struct {
+ net string
+ gaddr *UDPAddr
+ flags Flags
+ ipv6 bool // test with underlying AF_INET6 socket
+}{
+ // cf. RFC 4727: Experimental Values in IPv4, IPv6, ICMPv4, ICMPv6, UDP, and TCP Headers
+
+ {"udp", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, FlagUp | FlagLoopback, false},
+ {"udp", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, 0, false},
+ {"udp", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+ {"udp", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, 0, true},
+
+ {"udp4", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, FlagUp | FlagLoopback, false},
+ {"udp4", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, 0, false},
+
+ {"udp6", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+ {"udp6", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}, 0, true},
+ {"udp6", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+ {"udp6", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}, 0, true},
+ {"udp6", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+ {"udp6", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}, 0, true},
+ {"udp6", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+ {"udp6", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}, 0, true},
+ {"udp6", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+ {"udp6", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}, 0, true},
+ {"udp6", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+ {"udp6", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, 0, true},
+}
+
+// TestMulticastListener tests both single and double listen to a test
+// listener with same address family, same group address and same port.
+func TestMulticastListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "netbsd", "openbsd", "plan9", "solaris", "windows":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ case "linux":
+ if runtime.GOARCH == "arm" || runtime.GOARCH == "alpha" {
+ t.Skipf("skipping test on %q/%q", runtime.GOOS, runtime.GOARCH)
+ }
+ }
+
+ for _, tt := range multicastListenerTests {
+ if tt.ipv6 && (!*testIPv6 || !supportsIPv6 || os.Getuid() != 0) {
+ continue
+ }
+ ifi, err := availMulticastInterface(t, tt.flags)
+ if err != nil {
+ continue
+ }
+ c1, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
+ if err != nil {
+ t.Fatalf("First ListenMulticastUDP failed: %v", err)
+ }
+ checkMulticastListener(t, err, c1, tt.gaddr)
+ c2, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
+ if err != nil {
+ t.Fatalf("Second ListenMulticastUDP failed: %v", err)
+ }
+ checkMulticastListener(t, err, c2, tt.gaddr)
+ c2.Close()
+ c1.Close()
+ }
+}
+
+func TestSimpleMulticastListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ case "windows":
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test on windows to avoid firewall")
+ }
+ }
+
+ for _, tt := range multicastListenerTests {
+ if tt.ipv6 {
+ continue
+ }
+ tt.flags = FlagUp | FlagMulticast // for windows testing
+ ifi, err := availMulticastInterface(t, tt.flags)
+ if err != nil {
+ continue
+ }
+ c1, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
+ if err != nil {
+ t.Fatalf("First ListenMulticastUDP failed: %v", err)
+ }
+ checkSimpleMulticastListener(t, err, c1, tt.gaddr)
+ c2, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
+ if err != nil {
+ t.Fatalf("Second ListenMulticastUDP failed: %v", err)
+ }
+ checkSimpleMulticastListener(t, err, c2, tt.gaddr)
+ c2.Close()
+ c1.Close()
+ }
+}
+
+func checkMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) {
+ if !multicastRIBContains(t, gaddr.IP) {
+ t.Errorf("%q not found in RIB", gaddr.String())
+ return
+ }
+ la := c.LocalAddr()
+ if la == nil {
+ t.Error("LocalAddr failed")
+ return
+ }
+ if a, ok := la.(*UDPAddr); !ok || a.Port == 0 {
+ t.Errorf("got %v; expected a proper address with non-zero port number", la)
+ return
+ }
+}
+
+func checkSimpleMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) {
+ la := c.LocalAddr()
+ if la == nil {
+ t.Error("LocalAddr failed")
+ return
+ }
+ if a, ok := la.(*UDPAddr); !ok || a.Port == 0 {
+ t.Errorf("got %v; expected a proper address with non-zero port number", la)
+ return
+ }
+}
+
+func availMulticastInterface(t *testing.T, flags Flags) (*Interface, error) {
+ var ifi *Interface
+ if flags != Flags(0) {
+ ift, err := Interfaces()
+ if err != nil {
+ t.Fatalf("Interfaces failed: %v", err)
+ }
+ for _, x := range ift {
+ if x.Flags&flags == flags {
+ ifi = &x
+ break
+ }
+ }
+ if ifi == nil {
+ return nil, errors.New("an appropriate multicast interface not found")
+ }
+ }
+ return ifi, nil
+}
+
+func multicastRIBContains(t *testing.T, ip IP) bool {
+ ift, err := Interfaces()
+ if err != nil {
+ t.Fatalf("Interfaces failed: %v", err)
+ }
+ for _, ifi := range ift {
+ ifmat, err := ifi.MulticastAddrs()
+ if err != nil {
+ t.Fatalf("MulticastAddrs failed: %v", err)
+ }
+ for _, ifma := range ifmat {
+ if ifma.(*IPAddr).IP.Equal(ip) {
+ return true
+ }
+ }
+ }
+ return false
+}
diff --git a/src/pkg/net/multicast_test.go b/src/pkg/net/multicast_test.go
deleted file mode 100644
index 67261b1ee..000000000
--- a/src/pkg/net/multicast_test.go
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package net
-
-import (
- "errors"
- "os"
- "runtime"
- "syscall"
- "testing"
-)
-
-var multicastListenerTests = []struct {
- net string
- gaddr *UDPAddr
- flags Flags
- ipv6 bool // test with underlying AF_INET6 socket
-}{
- // cf. RFC 4727: Experimental Values in IPv4, IPv6, ICMPv4, ICMPv6, UDP, and TCP Headers
-
- {"udp", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false},
- {"udp", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, 0, false},
- {"udp", &UDPAddr{ParseIP("ff0e::114"), 12345}, FlagUp | FlagLoopback, true},
- {"udp", &UDPAddr{ParseIP("ff0e::114"), 12345}, 0, true},
-
- {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false},
- {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, 0, false},
-
- {"udp6", &UDPAddr{ParseIP("ff01::114"), 12345}, FlagUp | FlagLoopback, true},
- {"udp6", &UDPAddr{ParseIP("ff01::114"), 12345}, 0, true},
- {"udp6", &UDPAddr{ParseIP("ff02::114"), 12345}, FlagUp | FlagLoopback, true},
- {"udp6", &UDPAddr{ParseIP("ff02::114"), 12345}, 0, true},
- {"udp6", &UDPAddr{ParseIP("ff04::114"), 12345}, FlagUp | FlagLoopback, true},
- {"udp6", &UDPAddr{ParseIP("ff04::114"), 12345}, 0, true},
- {"udp6", &UDPAddr{ParseIP("ff05::114"), 12345}, FlagUp | FlagLoopback, true},
- {"udp6", &UDPAddr{ParseIP("ff05::114"), 12345}, 0, true},
- {"udp6", &UDPAddr{ParseIP("ff08::114"), 12345}, FlagUp | FlagLoopback, true},
- {"udp6", &UDPAddr{ParseIP("ff08::114"), 12345}, 0, true},
- {"udp6", &UDPAddr{ParseIP("ff0e::114"), 12345}, FlagUp | FlagLoopback, true},
- {"udp6", &UDPAddr{ParseIP("ff0e::114"), 12345}, 0, true},
-}
-
-// TestMulticastListener tests both single and double listen to a test
-// listener with same address family, same group address and same port.
-func TestMulticastListener(t *testing.T) {
- switch runtime.GOOS {
- case "netbsd", "openbsd", "plan9", "windows":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
- case "linux":
- if runtime.GOARCH == "arm" || runtime.GOARCH == "alpha" {
- t.Logf("skipping test on %q/%q", runtime.GOOS, runtime.GOARCH)
- return
- }
- }
-
- for _, tt := range multicastListenerTests {
- if tt.ipv6 && (!supportsIPv6 || os.Getuid() != 0) {
- continue
- }
- ifi, err := availMulticastInterface(t, tt.flags)
- if err != nil {
- continue
- }
- c1, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
- if err != nil {
- t.Fatalf("First ListenMulticastUDP failed: %v", err)
- }
- checkMulticastListener(t, err, c1, tt.gaddr)
- c2, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
- if err != nil {
- t.Fatalf("Second ListenMulticastUDP failed: %v", err)
- }
- checkMulticastListener(t, err, c2, tt.gaddr)
- c2.Close()
- switch c1.fd.family {
- case syscall.AF_INET:
- testIPv4MulticastSocketOptions(t, c1.fd, ifi)
- case syscall.AF_INET6:
- testIPv6MulticastSocketOptions(t, c1.fd, ifi)
- }
- c1.Close()
- }
-}
-
-func TestSimpleMulticastListener(t *testing.T) {
- switch runtime.GOOS {
- case "plan9":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
- case "windows":
- if testing.Short() || !*testExternal {
- t.Logf("skipping test on windows to avoid firewall")
- return
- }
- }
-
- for _, tt := range multicastListenerTests {
- if tt.ipv6 {
- continue
- }
- tt.flags = FlagUp | FlagMulticast // for windows testing
- ifi, err := availMulticastInterface(t, tt.flags)
- if err != nil {
- continue
- }
- c1, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
- if err != nil {
- t.Fatalf("First ListenMulticastUDP failed: %v", err)
- }
- checkSimpleMulticastListener(t, err, c1, tt.gaddr)
- c2, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
- if err != nil {
- t.Fatalf("Second ListenMulticastUDP failed: %v", err)
- }
- checkSimpleMulticastListener(t, err, c2, tt.gaddr)
- c2.Close()
- c1.Close()
- }
-}
-
-func checkMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) {
- if !multicastRIBContains(t, gaddr.IP) {
- t.Fatalf("%q not found in RIB", gaddr.String())
- }
- if c.LocalAddr().String() != gaddr.String() {
- t.Fatalf("LocalAddr returns %q, expected %q", c.LocalAddr().String(), gaddr.String())
- }
-}
-
-func checkSimpleMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) {
- if c.LocalAddr().String() != gaddr.String() {
- t.Fatalf("LocalAddr returns %q, expected %q", c.LocalAddr().String(), gaddr.String())
- }
-}
-
-func availMulticastInterface(t *testing.T, flags Flags) (*Interface, error) {
- var ifi *Interface
- if flags != Flags(0) {
- ift, err := Interfaces()
- if err != nil {
- t.Fatalf("Interfaces failed: %v", err)
- }
- for _, x := range ift {
- if x.Flags&flags == flags {
- ifi = &x
- break
- }
- }
- if ifi == nil {
- return nil, errors.New("an appropriate multicast interface not found")
- }
- }
- return ifi, nil
-}
-
-func multicastRIBContains(t *testing.T, ip IP) bool {
- ift, err := Interfaces()
- if err != nil {
- t.Fatalf("Interfaces failed: %v", err)
- }
- for _, ifi := range ift {
- ifmat, err := ifi.MulticastAddrs()
- if err != nil {
- t.Fatalf("MulticastAddrs failed: %v", err)
- }
- for _, ifma := range ifmat {
- if ifma.(*IPAddr).IP.Equal(ip) {
- return true
- }
- }
- }
- return false
-}
-
-func testIPv4MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) {
- _, err := ipv4MulticastInterface(fd)
- if err != nil {
- t.Fatalf("ipv4MulticastInterface failed: %v", err)
- }
- if ifi != nil {
- err = setIPv4MulticastInterface(fd, ifi)
- if err != nil {
- t.Fatalf("setIPv4MulticastInterface failed: %v", err)
- }
- }
- _, err = ipv4MulticastTTL(fd)
- if err != nil {
- t.Fatalf("ipv4MulticastTTL failed: %v", err)
- }
- err = setIPv4MulticastTTL(fd, 1)
- if err != nil {
- t.Fatalf("setIPv4MulticastTTL failed: %v", err)
- }
- _, err = ipv4MulticastLoopback(fd)
- if err != nil {
- t.Fatalf("ipv4MulticastLoopback failed: %v", err)
- }
- err = setIPv4MulticastLoopback(fd, false)
- if err != nil {
- t.Fatalf("setIPv4MulticastLoopback failed: %v", err)
- }
-}
-
-func testIPv6MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) {
- _, err := ipv6MulticastInterface(fd)
- if err != nil {
- t.Fatalf("ipv6MulticastInterface failed: %v", err)
- }
- if ifi != nil {
- err = setIPv6MulticastInterface(fd, ifi)
- if err != nil {
- t.Fatalf("setIPv6MulticastInterface failed: %v", err)
- }
- }
- _, err = ipv6MulticastHopLimit(fd)
- if err != nil {
- t.Fatalf("ipv6MulticastHopLimit failed: %v", err)
- }
- err = setIPv6MulticastHopLimit(fd, 1)
- if err != nil {
- t.Fatalf("setIPv6MulticastHopLimit failed: %v", err)
- }
- _, err = ipv6MulticastLoopback(fd)
- if err != nil {
- t.Fatalf("ipv6MulticastLoopback failed: %v", err)
- }
- err = setIPv6MulticastLoopback(fd, false)
- if err != nil {
- t.Fatalf("setIPv6MulticastLoopback failed: %v", err)
- }
-}
diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go
index 9ebcdbe99..72b2b646c 100644
--- a/src/pkg/net/net.go
+++ b/src/pkg/net/net.go
@@ -44,6 +44,10 @@ package net
import (
"errors"
+ "io"
+ "os"
+ "sync"
+ "syscall"
"time"
)
@@ -103,6 +107,105 @@ type Conn interface {
SetWriteDeadline(t time.Time) error
}
+type conn struct {
+ fd *netFD
+}
+
+func (c *conn) ok() bool { return c != nil && c.fd != nil }
+
+// Implementation of the Conn interface.
+
+// Read implements the Conn Read method.
+func (c *conn) Read(b []byte) (int, error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ return c.fd.Read(b)
+}
+
+// Write implements the Conn Write method.
+func (c *conn) Write(b []byte) (int, error) {
+ if !c.ok() {
+ return 0, syscall.EINVAL
+ }
+ return c.fd.Write(b)
+}
+
+// Close closes the connection.
+func (c *conn) Close() error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return c.fd.Close()
+}
+
+// LocalAddr returns the local network address.
+func (c *conn) LocalAddr() Addr {
+ if !c.ok() {
+ return nil
+ }
+ return c.fd.laddr
+}
+
+// RemoteAddr returns the remote network address.
+func (c *conn) RemoteAddr() Addr {
+ if !c.ok() {
+ return nil
+ }
+ return c.fd.raddr
+}
+
+// SetDeadline implements the Conn SetDeadline method.
+func (c *conn) SetDeadline(t time.Time) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return setDeadline(c.fd, t)
+}
+
+// SetReadDeadline implements the Conn SetReadDeadline method.
+func (c *conn) SetReadDeadline(t time.Time) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return setReadDeadline(c.fd, t)
+}
+
+// SetWriteDeadline implements the Conn SetWriteDeadline method.
+func (c *conn) SetWriteDeadline(t time.Time) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return setWriteDeadline(c.fd, t)
+}
+
+// SetReadBuffer sets the size of the operating system's
+// receive buffer associated with the connection.
+func (c *conn) SetReadBuffer(bytes int) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return setReadBuffer(c.fd, bytes)
+}
+
+// SetWriteBuffer sets the size of the operating system's
+// transmit buffer associated with the connection.
+func (c *conn) SetWriteBuffer(bytes int) error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return setWriteBuffer(c.fd, bytes)
+}
+
+// File sets the underlying os.File to blocking mode and returns a copy.
+// It is the caller's responsibility to close f when finished.
+// Closing c does not affect f, and closing f does not affect c.
+//
+// The returned os.File's file descriptor is different from the connection's.
+// Attempting to change properties of the original using this duplicate
+// may or may not have the desired effect.
+func (c *conn) File() (f *os.File, err error) { return c.fd.dup() }
+
// An Error represents a network error.
type Error interface {
error
@@ -173,11 +276,23 @@ type Listener interface {
var errMissingAddress = errors.New("missing address")
+// OpError is the error type usually returned by functions in the net
+// package. It describes the operation, network type, and address of
+// an error.
type OpError struct {
- Op string
- Net string
+ // Op is the operation which caused the error, such as
+ // "read" or "write".
+ Op string
+
+ // Net is the network type on which this error occurred,
+ // such as "tcp" or "udp6".
+ Net string
+
+ // Addr is the network address on which this error occurred.
Addr Addr
- Err error
+
+ // Err is the error that occurred during the operation.
+ Err error
}
func (e *OpError) Error() string {
@@ -204,6 +319,8 @@ func (e *OpError) Temporary() bool {
return ok && t.Temporary()
}
+var noDeadline = time.Time{}
+
type timeout interface {
Timeout() bool
}
@@ -221,6 +338,8 @@ func (e *timeoutError) Temporary() bool { return true }
var errTimeout error = &timeoutError{}
+var errClosing = errors.New("use of closed network connection")
+
type AddrError struct {
Err string
Addr string
@@ -262,3 +381,47 @@ func (e *DNSConfigError) Error() string {
func (e *DNSConfigError) Timeout() bool { return false }
func (e *DNSConfigError) Temporary() bool { return false }
+
+type writerOnly struct {
+ io.Writer
+}
+
+// Fallback implementation of io.ReaderFrom's ReadFrom, when sendfile isn't
+// applicable.
+func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) {
+ // Use wrapper to hide existing r.ReadFrom from io.Copy.
+ return io.Copy(writerOnly{w}, r)
+}
+
+// deadline is an atomically-accessed number of nanoseconds since 1970
+// or 0, if no deadline is set.
+type deadline struct {
+ sync.Mutex
+ val int64
+}
+
+func (d *deadline) expired() bool {
+ t := d.value()
+ return t > 0 && time.Now().UnixNano() >= t
+}
+
+func (d *deadline) value() (v int64) {
+ d.Lock()
+ v = d.val
+ d.Unlock()
+ return
+}
+
+func (d *deadline) set(v int64) {
+ d.Lock()
+ d.val = v
+ d.Unlock()
+}
+
+func (d *deadline) setTime(t time.Time) {
+ if t.IsZero() {
+ d.set(0)
+ } else {
+ d.set(t.UnixNano())
+ }
+}
diff --git a/src/pkg/net/net_test.go b/src/pkg/net/net_test.go
index fd145e1d7..1a512a5b1 100644
--- a/src/pkg/net/net_test.go
+++ b/src/pkg/net/net_test.go
@@ -6,6 +6,8 @@ package net
import (
"io"
+ "io/ioutil"
+ "os"
"runtime"
"testing"
"time"
@@ -13,18 +15,17 @@ import (
func TestShutdown(t *testing.T) {
if runtime.GOOS == "plan9" {
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
- l, err := Listen("tcp", "127.0.0.1:0")
+ ln, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
- if l, err = Listen("tcp6", "[::1]:0"); err != nil {
+ if ln, err = Listen("tcp6", "[::1]:0"); err != nil {
t.Fatalf("ListenTCP on :0: %v", err)
}
}
go func() {
- c, err := l.Accept()
+ c, err := ln.Accept()
if err != nil {
t.Fatalf("Accept: %v", err)
}
@@ -37,7 +38,7 @@ func TestShutdown(t *testing.T) {
c.Close()
}()
- c, err := Dial("tcp", l.Addr().String())
+ c, err := Dial("tcp", ln.Addr().String())
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -58,8 +59,61 @@ func TestShutdown(t *testing.T) {
}
}
+func TestShutdownUnix(t *testing.T) {
+ switch runtime.GOOS {
+ case "windows", "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+ f, err := ioutil.TempFile("", "go_net_unixtest")
+ if err != nil {
+ t.Fatalf("TempFile: %s", err)
+ }
+ f.Close()
+ tmpname := f.Name()
+ os.Remove(tmpname)
+ ln, err := Listen("unix", tmpname)
+ if err != nil {
+ t.Fatalf("ListenUnix on %s: %s", tmpname, err)
+ }
+ defer os.Remove(tmpname)
+
+ go func() {
+ c, err := ln.Accept()
+ if err != nil {
+ t.Fatalf("Accept: %v", err)
+ }
+ var buf [10]byte
+ n, err := c.Read(buf[:])
+ if n != 0 || err != io.EOF {
+ t.Fatalf("server Read = %d, %v; want 0, io.EOF", n, err)
+ }
+ c.Write([]byte("response"))
+ c.Close()
+ }()
+
+ c, err := Dial("unix", tmpname)
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ defer c.Close()
+
+ err = c.(*UnixConn).CloseWrite()
+ if err != nil {
+ t.Fatalf("CloseWrite: %v", err)
+ }
+ var buf [10]byte
+ n, err := c.Read(buf[:])
+ if err != nil {
+ t.Fatalf("client Read: %d, %v", n, err)
+ }
+ got := string(buf[:n])
+ if got != "response" {
+ t.Errorf("read = %q, want \"response\"", got)
+ }
+}
+
func TestTCPListenClose(t *testing.T) {
- l, err := Listen("tcp", "127.0.0.1:0")
+ ln, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Listen failed: %v", err)
}
@@ -67,11 +121,12 @@ func TestTCPListenClose(t *testing.T) {
done := make(chan bool, 1)
go func() {
time.Sleep(100 * time.Millisecond)
- l.Close()
+ ln.Close()
}()
go func() {
- _, err = l.Accept()
+ c, err := ln.Accept()
if err == nil {
+ c.Close()
t.Error("Accept succeeded")
} else {
t.Logf("Accept timeout error: %s (any error is fine)", err)
@@ -86,7 +141,11 @@ func TestTCPListenClose(t *testing.T) {
}
func TestUDPListenClose(t *testing.T) {
- l, err := ListenPacket("udp", "127.0.0.1:0")
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+ ln, err := ListenPacket("udp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Listen failed: %v", err)
}
@@ -95,10 +154,10 @@ func TestUDPListenClose(t *testing.T) {
done := make(chan bool, 1)
go func() {
time.Sleep(100 * time.Millisecond)
- l.Close()
+ ln.Close()
}()
go func() {
- _, _, err = l.ReadFrom(buf)
+ _, _, err = ln.ReadFrom(buf)
if err == nil {
t.Error("ReadFrom succeeded")
} else {
@@ -112,3 +171,46 @@ func TestUDPListenClose(t *testing.T) {
t.Fatal("timeout waiting for UDP close")
}
}
+
+func TestTCPClose(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+ l, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer l.Close()
+
+ read := func(r io.Reader) error {
+ var m [1]byte
+ _, err := r.Read(m[:])
+ return err
+ }
+
+ go func() {
+ c, err := Dial("tcp", l.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ go read(c)
+
+ time.Sleep(10 * time.Millisecond)
+ c.Close()
+ }()
+
+ c, err := l.Accept()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ for err == nil {
+ err = read(c)
+ }
+ if err != nil && err != io.EOF {
+ t.Fatal(err)
+ }
+}
diff --git a/src/pkg/net/newpollserver.go b/src/pkg/net/newpollserver_unix.go
index d34bb511f..618b5b10b 100644
--- a/src/pkg/net/newpollserver.go
+++ b/src/pkg/net/newpollserver_unix.go
@@ -13,8 +13,6 @@ import (
func newPollServer() (s *pollServer, err error) {
s = new(pollServer)
- s.cr = make(chan *netFD, 1)
- s.cw = make(chan *netFD, 1)
if s.pr, s.pw, err = os.Pipe(); err != nil {
return nil, err
}
diff --git a/src/pkg/net/packetconn_test.go b/src/pkg/net/packetconn_test.go
new file mode 100644
index 000000000..93c7a6472
--- /dev/null
+++ b/src/pkg/net/packetconn_test.go
@@ -0,0 +1,200 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements API tests across platforms and will never have a build
+// tag.
+
+package net
+
+import (
+ "os"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+)
+
+var packetConnTests = []struct {
+ net string
+ addr1 string
+ addr2 string
+}{
+ {"udp", "127.0.0.1:0", "127.0.0.1:0"},
+ {"ip:icmp", "127.0.0.1", "127.0.0.1"},
+ {"unixgram", testUnixAddr(), testUnixAddr()},
+}
+
+func TestPacketConn(t *testing.T) {
+ closer := func(c PacketConn, net, addr1, addr2 string) {
+ c.Close()
+ switch net {
+ case "unixgram":
+ os.Remove(addr1)
+ os.Remove(addr2)
+ }
+ }
+
+ for i, tt := range packetConnTests {
+ var wb []byte
+ netstr := strings.Split(tt.net, ":")
+ switch netstr[0] {
+ case "udp":
+ wb = []byte("UDP PACKETCONN TEST")
+ case "ip":
+ switch runtime.GOOS {
+ case "plan9":
+ continue
+ }
+ if os.Getuid() != 0 {
+ continue
+ }
+ var err error
+ wb, err = (&icmpMessage{
+ Type: icmpv4EchoRequest, Code: 0,
+ Body: &icmpEcho{
+ ID: os.Getpid() & 0xffff, Seq: i + 1,
+ Data: []byte("IP PACKETCONN TEST"),
+ },
+ }).Marshal()
+ if err != nil {
+ t.Fatalf("icmpMessage.Marshal failed: %v", err)
+ }
+ case "unixgram":
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ continue
+ }
+ wb = []byte("UNIXGRAM PACKETCONN TEST")
+ default:
+ continue
+ }
+
+ c1, err := ListenPacket(tt.net, tt.addr1)
+ if err != nil {
+ t.Fatalf("ListenPacket failed: %v", err)
+ }
+ defer closer(c1, netstr[0], tt.addr1, tt.addr2)
+ c1.LocalAddr()
+ c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+
+ c2, err := ListenPacket(tt.net, tt.addr2)
+ if err != nil {
+ t.Fatalf("ListenPacket failed: %v", err)
+ }
+ defer closer(c2, netstr[0], tt.addr1, tt.addr2)
+ c2.LocalAddr()
+ c2.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c2.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+
+ if _, err := c1.WriteTo(wb, c2.LocalAddr()); err != nil {
+ t.Fatalf("PacketConn.WriteTo failed: %v", err)
+ }
+ rb2 := make([]byte, 128)
+ if _, _, err := c2.ReadFrom(rb2); err != nil {
+ t.Fatalf("PacketConn.ReadFrom failed: %v", err)
+ }
+ if _, err := c2.WriteTo(wb, c1.LocalAddr()); err != nil {
+ t.Fatalf("PacketConn.WriteTo failed: %v", err)
+ }
+ rb1 := make([]byte, 128)
+ if _, _, err := c1.ReadFrom(rb1); err != nil {
+ t.Fatalf("PacketConn.ReadFrom failed: %v", err)
+ }
+ }
+}
+
+func TestConnAndPacketConn(t *testing.T) {
+ closer := func(c PacketConn, net, addr1, addr2 string) {
+ c.Close()
+ switch net {
+ case "unixgram":
+ os.Remove(addr1)
+ os.Remove(addr2)
+ }
+ }
+
+ for i, tt := range packetConnTests {
+ var wb []byte
+ netstr := strings.Split(tt.net, ":")
+ switch netstr[0] {
+ case "udp":
+ wb = []byte("UDP PACKETCONN TEST")
+ case "ip":
+ switch runtime.GOOS {
+ case "plan9":
+ continue
+ }
+ if os.Getuid() != 0 {
+ continue
+ }
+ var err error
+ wb, err = (&icmpMessage{
+ Type: icmpv4EchoRequest, Code: 0,
+ Body: &icmpEcho{
+ ID: os.Getpid() & 0xffff, Seq: i + 1,
+ Data: []byte("IP PACKETCONN TEST"),
+ },
+ }).Marshal()
+ if err != nil {
+ t.Fatalf("icmpMessage.Marshal failed: %v", err)
+ }
+ case "unixgram":
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ continue
+ }
+ wb = []byte("UNIXGRAM PACKETCONN TEST")
+ default:
+ continue
+ }
+
+ c1, err := ListenPacket(tt.net, tt.addr1)
+ if err != nil {
+ t.Fatalf("ListenPacket failed: %v", err)
+ }
+ defer closer(c1, netstr[0], tt.addr1, tt.addr2)
+ c1.LocalAddr()
+ c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+
+ c2, err := Dial(tt.net, c1.LocalAddr().String())
+ if err != nil {
+ t.Fatalf("Dial failed: %v", err)
+ }
+ defer c2.Close()
+ c2.LocalAddr()
+ c2.RemoteAddr()
+ c2.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ c2.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
+
+ if _, err := c2.Write(wb); err != nil {
+ t.Fatalf("Conn.Write failed: %v", err)
+ }
+ rb1 := make([]byte, 128)
+ if _, _, err := c1.ReadFrom(rb1); err != nil {
+ t.Fatalf("PacetConn.ReadFrom failed: %v", err)
+ }
+ var dst Addr
+ switch netstr[0] {
+ case "ip":
+ dst = &IPAddr{IP: IPv4(127, 0, 0, 1)}
+ case "unixgram":
+ continue
+ default:
+ dst = c2.LocalAddr()
+ }
+ if _, err := c1.WriteTo(wb, dst); err != nil {
+ t.Fatalf("PacketConn.WriteTo failed: %v", err)
+ }
+ rb2 := make([]byte, 128)
+ if _, err := c2.Read(rb2); err != nil {
+ t.Fatalf("Conn.Read failed: %v", err)
+ }
+ }
+}
diff --git a/src/pkg/net/parse_test.go b/src/pkg/net/parse_test.go
index 30fda45df..9df0c534b 100644
--- a/src/pkg/net/parse_test.go
+++ b/src/pkg/net/parse_test.go
@@ -15,8 +15,7 @@ func TestReadLine(t *testing.T) {
// /etc/services file does not exist on windows and Plan 9.
switch runtime.GOOS {
case "plan9", "windows":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
filename := "/etc/services" // a nice big file
diff --git a/src/pkg/net/port.go b/src/pkg/net/port.go
index 16780da11..c24f4ed5b 100644
--- a/src/pkg/net/port.go
+++ b/src/pkg/net/port.go
@@ -1,69 +1,24 @@
-// Copyright 2009 The Go Authors. All rights reserved.
+// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux netbsd openbsd
-
-// Read system port mappings from /etc/services
+// Network service port manipulations
package net
-import "sync"
-
-var services map[string]map[string]int
-var servicesError error
-var onceReadServices sync.Once
-
-func readServices() {
- services = make(map[string]map[string]int)
- var file *file
- if file, servicesError = open("/etc/services"); servicesError != nil {
- return
- }
- for line, ok := file.readLine(); ok; line, ok = file.readLine() {
- // "http 80/tcp www www-http # World Wide Web HTTP"
- if i := byteIndex(line, '#'); i >= 0 {
- line = line[0:i]
- }
- f := getFields(line)
- if len(f) < 2 {
- continue
- }
- portnet := f[1] // "tcp/80"
- port, j, ok := dtoi(portnet, 0)
- if !ok || port <= 0 || j >= len(portnet) || portnet[j] != '/' {
- continue
- }
- netw := portnet[j+1:] // "tcp"
- m, ok1 := services[netw]
- if !ok1 {
- m = make(map[string]int)
- services[netw] = m
- }
- for i := 0; i < len(f); i++ {
- if i != 1 { // f[1] was port/net
- m[f[i]] = port
- }
+// parsePort parses port as a network service port number for both
+// TCP and UDP.
+func parsePort(net, port string) (int, error) {
+ p, i, ok := dtoi(port, 0)
+ if !ok || i != len(port) {
+ var err error
+ p, err = LookupPort(net, port)
+ if err != nil {
+ return 0, err
}
}
- file.close()
-}
-
-// goLookupPort is the native Go implementation of LookupPort.
-func goLookupPort(network, service string) (port int, err error) {
- onceReadServices.Do(readServices)
-
- switch network {
- case "tcp4", "tcp6":
- network = "tcp"
- case "udp4", "udp6":
- network = "udp"
- }
-
- if m, ok := services[network]; ok {
- if port, ok = m[service]; ok {
- return
- }
+ if p < 0 || p > 0xFFFF {
+ return 0, &AddrError{"invalid port", port}
}
- return 0, &AddrError{"unknown port", network + "/" + service}
+ return p, nil
}
diff --git a/src/pkg/net/port_test.go b/src/pkg/net/port_test.go
index 329b169f3..9e8968f35 100644
--- a/src/pkg/net/port_test.go
+++ b/src/pkg/net/port_test.go
@@ -46,7 +46,7 @@ func TestLookupPort(t *testing.T) {
for i := 0; i < len(porttests); i++ {
tt := porttests[i]
if port, err := LookupPort(tt.netw, tt.name); port != tt.port || (err == nil) != tt.ok {
- t.Errorf("LookupPort(%q, %q) = %v, %s; want %v",
+ t.Errorf("LookupPort(%q, %q) = %v, %v; want %v",
tt.netw, tt.name, port, err, tt.port)
}
}
diff --git a/src/pkg/net/port_unix.go b/src/pkg/net/port_unix.go
new file mode 100644
index 000000000..16780da11
--- /dev/null
+++ b/src/pkg/net/port_unix.go
@@ -0,0 +1,69 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd
+
+// Read system port mappings from /etc/services
+
+package net
+
+import "sync"
+
+var services map[string]map[string]int
+var servicesError error
+var onceReadServices sync.Once
+
+func readServices() {
+ services = make(map[string]map[string]int)
+ var file *file
+ if file, servicesError = open("/etc/services"); servicesError != nil {
+ return
+ }
+ for line, ok := file.readLine(); ok; line, ok = file.readLine() {
+ // "http 80/tcp www www-http # World Wide Web HTTP"
+ if i := byteIndex(line, '#'); i >= 0 {
+ line = line[0:i]
+ }
+ f := getFields(line)
+ if len(f) < 2 {
+ continue
+ }
+ portnet := f[1] // "tcp/80"
+ port, j, ok := dtoi(portnet, 0)
+ if !ok || port <= 0 || j >= len(portnet) || portnet[j] != '/' {
+ continue
+ }
+ netw := portnet[j+1:] // "tcp"
+ m, ok1 := services[netw]
+ if !ok1 {
+ m = make(map[string]int)
+ services[netw] = m
+ }
+ for i := 0; i < len(f); i++ {
+ if i != 1 { // f[1] was port/net
+ m[f[i]] = port
+ }
+ }
+ }
+ file.close()
+}
+
+// goLookupPort is the native Go implementation of LookupPort.
+func goLookupPort(network, service string) (port int, err error) {
+ onceReadServices.Do(readServices)
+
+ switch network {
+ case "tcp4", "tcp6":
+ network = "tcp"
+ case "udp4", "udp6":
+ network = "udp"
+ }
+
+ if m, ok := services[network]; ok {
+ if port, ok = m[service]; ok {
+ return
+ }
+ }
+ return 0, &AddrError{"unknown port", network + "/" + service}
+}
diff --git a/src/pkg/net/protoconn_test.go b/src/pkg/net/protoconn_test.go
new file mode 100644
index 000000000..2fe7d1d1f
--- /dev/null
+++ b/src/pkg/net/protoconn_test.go
@@ -0,0 +1,358 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements API tests across platforms and will never have a build
+// tag.
+
+package net
+
+import (
+ "io/ioutil"
+ "os"
+ "runtime"
+ "testing"
+ "time"
+)
+
+// testUnixAddr uses ioutil.TempFile to get a name that is unique.
+func testUnixAddr() string {
+ f, err := ioutil.TempFile("", "nettest")
+ if err != nil {
+ panic(err)
+ }
+ addr := f.Name()
+ f.Close()
+ os.Remove(addr)
+ return addr
+}
+
+var condFatalf = func() func(*testing.T, string, ...interface{}) {
+ // A few APIs are not implemented yet on both Plan 9 and Windows.
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ return (*testing.T).Logf
+ }
+ return (*testing.T).Fatalf
+}()
+
+func TestTCPListenerSpecificMethods(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ la, err := ResolveTCPAddr("tcp4", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("ResolveTCPAddr failed: %v", err)
+ }
+ ln, err := ListenTCP("tcp4", la)
+ if err != nil {
+ t.Fatalf("ListenTCP failed: %v", err)
+ }
+ defer ln.Close()
+ ln.Addr()
+ ln.SetDeadline(time.Now().Add(30 * time.Nanosecond))
+
+ if c, err := ln.Accept(); err != nil {
+ if !err.(Error).Timeout() {
+ t.Fatalf("TCPListener.Accept failed: %v", err)
+ }
+ } else {
+ c.Close()
+ }
+ if c, err := ln.AcceptTCP(); err != nil {
+ if !err.(Error).Timeout() {
+ t.Fatalf("TCPListener.AcceptTCP failed: %v", err)
+ }
+ } else {
+ c.Close()
+ }
+
+ if f, err := ln.File(); err != nil {
+ condFatalf(t, "TCPListener.File failed: %v", err)
+ } else {
+ f.Close()
+ }
+}
+
+func TestTCPConnSpecificMethods(t *testing.T) {
+ la, err := ResolveTCPAddr("tcp4", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("ResolveTCPAddr failed: %v", err)
+ }
+ ln, err := ListenTCP("tcp4", la)
+ if err != nil {
+ t.Fatalf("ListenTCP failed: %v", err)
+ }
+ defer ln.Close()
+ ln.Addr()
+
+ done := make(chan int)
+ go transponder(t, ln, done)
+
+ ra, err := ResolveTCPAddr("tcp4", ln.Addr().String())
+ if err != nil {
+ t.Fatalf("ResolveTCPAddr failed: %v", err)
+ }
+ c, err := DialTCP("tcp4", nil, ra)
+ if err != nil {
+ t.Fatalf("DialTCP failed: %v", err)
+ }
+ defer c.Close()
+ c.SetKeepAlive(false)
+ c.SetLinger(0)
+ c.SetNoDelay(false)
+ c.LocalAddr()
+ c.RemoteAddr()
+ c.SetDeadline(time.Now().Add(someTimeout))
+ c.SetReadDeadline(time.Now().Add(someTimeout))
+ c.SetWriteDeadline(time.Now().Add(someTimeout))
+
+ if _, err := c.Write([]byte("TCPCONN TEST")); err != nil {
+ t.Fatalf("TCPConn.Write failed: %v", err)
+ }
+ rb := make([]byte, 128)
+ if _, err := c.Read(rb); err != nil {
+ t.Fatalf("TCPConn.Read failed: %v", err)
+ }
+
+ <-done
+}
+
+func TestUDPConnSpecificMethods(t *testing.T) {
+ la, err := ResolveUDPAddr("udp4", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("ResolveUDPAddr failed: %v", err)
+ }
+ c, err := ListenUDP("udp4", la)
+ if err != nil {
+ t.Fatalf("ListenUDP failed: %v", err)
+ }
+ defer c.Close()
+ c.LocalAddr()
+ c.RemoteAddr()
+ c.SetDeadline(time.Now().Add(someTimeout))
+ c.SetReadDeadline(time.Now().Add(someTimeout))
+ c.SetWriteDeadline(time.Now().Add(someTimeout))
+ c.SetReadBuffer(2048)
+ c.SetWriteBuffer(2048)
+
+ wb := []byte("UDPCONN TEST")
+ rb := make([]byte, 128)
+ if _, err := c.WriteToUDP(wb, c.LocalAddr().(*UDPAddr)); err != nil {
+ t.Fatalf("UDPConn.WriteToUDP failed: %v", err)
+ }
+ if _, _, err := c.ReadFromUDP(rb); err != nil {
+ t.Fatalf("UDPConn.ReadFromUDP failed: %v", err)
+ }
+ if _, _, err := c.WriteMsgUDP(wb, nil, c.LocalAddr().(*UDPAddr)); err != nil {
+ condFatalf(t, "UDPConn.WriteMsgUDP failed: %v", err)
+ }
+ if _, _, _, _, err := c.ReadMsgUDP(rb, nil); err != nil {
+ condFatalf(t, "UDPConn.ReadMsgUDP failed: %v", err)
+ }
+
+ if f, err := c.File(); err != nil {
+ condFatalf(t, "UDPConn.File failed: %v", err)
+ } else {
+ f.Close()
+ }
+}
+
+func TestIPConnSpecificMethods(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping read test on %q", runtime.GOOS)
+ }
+ if os.Getuid() != 0 {
+ t.Skipf("skipping test; must be root")
+ }
+
+ la, err := ResolveIPAddr("ip4", "127.0.0.1")
+ if err != nil {
+ t.Fatalf("ResolveIPAddr failed: %v", err)
+ }
+ c, err := ListenIP("ip4:icmp", la)
+ if err != nil {
+ t.Fatalf("ListenIP failed: %v", err)
+ }
+ defer c.Close()
+ c.LocalAddr()
+ c.RemoteAddr()
+ c.SetDeadline(time.Now().Add(someTimeout))
+ c.SetReadDeadline(time.Now().Add(someTimeout))
+ c.SetWriteDeadline(time.Now().Add(someTimeout))
+ c.SetReadBuffer(2048)
+ c.SetWriteBuffer(2048)
+
+ wb, err := (&icmpMessage{
+ Type: icmpv4EchoRequest, Code: 0,
+ Body: &icmpEcho{
+ ID: os.Getpid() & 0xffff, Seq: 1,
+ Data: []byte("IPCONN TEST "),
+ },
+ }).Marshal()
+ if err != nil {
+ t.Fatalf("icmpMessage.Marshal failed: %v", err)
+ }
+ rb := make([]byte, 20+128)
+ if _, err := c.WriteToIP(wb, c.LocalAddr().(*IPAddr)); err != nil {
+ t.Fatalf("IPConn.WriteToIP failed: %v", err)
+ }
+ if _, _, err := c.ReadFromIP(rb); err != nil {
+ t.Fatalf("IPConn.ReadFromIP failed: %v", err)
+ }
+ if _, _, err := c.WriteMsgIP(wb, nil, c.LocalAddr().(*IPAddr)); err != nil {
+ condFatalf(t, "IPConn.WriteMsgIP failed: %v", err)
+ }
+ if _, _, _, _, err := c.ReadMsgIP(rb, nil); err != nil {
+ condFatalf(t, "IPConn.ReadMsgIP failed: %v", err)
+ }
+
+ if f, err := c.File(); err != nil {
+ condFatalf(t, "IPConn.File failed: %v", err)
+ } else {
+ f.Close()
+ }
+}
+
+func TestUnixListenerSpecificMethods(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ t.Skipf("skipping read test on %q", runtime.GOOS)
+ }
+
+ addr := testUnixAddr()
+ la, err := ResolveUnixAddr("unix", addr)
+ if err != nil {
+ t.Fatalf("ResolveUnixAddr failed: %v", err)
+ }
+ ln, err := ListenUnix("unix", la)
+ if err != nil {
+ t.Fatalf("ListenUnix failed: %v", err)
+ }
+ defer ln.Close()
+ defer os.Remove(addr)
+ ln.Addr()
+ ln.SetDeadline(time.Now().Add(30 * time.Nanosecond))
+
+ if c, err := ln.Accept(); err != nil {
+ if !err.(Error).Timeout() {
+ t.Fatalf("UnixListener.Accept failed: %v", err)
+ }
+ } else {
+ c.Close()
+ }
+ if c, err := ln.AcceptUnix(); err != nil {
+ if !err.(Error).Timeout() {
+ t.Fatalf("UnixListener.AcceptUnix failed: %v", err)
+ }
+ } else {
+ c.Close()
+ }
+
+ if f, err := ln.File(); err != nil {
+ t.Fatalf("UnixListener.File failed: %v", err)
+ } else {
+ f.Close()
+ }
+}
+
+func TestUnixConnSpecificMethods(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9", "windows":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ addr1, addr2, addr3 := testUnixAddr(), testUnixAddr(), testUnixAddr()
+
+ a1, err := ResolveUnixAddr("unixgram", addr1)
+ if err != nil {
+ t.Fatalf("ResolveUnixAddr failed: %v", err)
+ }
+ c1, err := DialUnix("unixgram", a1, nil)
+ if err != nil {
+ t.Fatalf("DialUnix failed: %v", err)
+ }
+ defer c1.Close()
+ defer os.Remove(addr1)
+ c1.LocalAddr()
+ c1.RemoteAddr()
+ c1.SetDeadline(time.Now().Add(someTimeout))
+ c1.SetReadDeadline(time.Now().Add(someTimeout))
+ c1.SetWriteDeadline(time.Now().Add(someTimeout))
+ c1.SetReadBuffer(2048)
+ c1.SetWriteBuffer(2048)
+
+ a2, err := ResolveUnixAddr("unixgram", addr2)
+ if err != nil {
+ t.Fatalf("ResolveUnixAddr failed: %v", err)
+ }
+ c2, err := DialUnix("unixgram", a2, nil)
+ if err != nil {
+ t.Fatalf("DialUnix failed: %v", err)
+ }
+ defer c2.Close()
+ defer os.Remove(addr2)
+ c2.LocalAddr()
+ c2.RemoteAddr()
+ c2.SetDeadline(time.Now().Add(someTimeout))
+ c2.SetReadDeadline(time.Now().Add(someTimeout))
+ c2.SetWriteDeadline(time.Now().Add(someTimeout))
+ c2.SetReadBuffer(2048)
+ c2.SetWriteBuffer(2048)
+
+ a3, err := ResolveUnixAddr("unixgram", addr3)
+ if err != nil {
+ t.Fatalf("ResolveUnixAddr failed: %v", err)
+ }
+ c3, err := ListenUnixgram("unixgram", a3)
+ if err != nil {
+ t.Fatalf("ListenUnixgram failed: %v", err)
+ }
+ defer c3.Close()
+ defer os.Remove(addr3)
+ c3.LocalAddr()
+ c3.RemoteAddr()
+ c3.SetDeadline(time.Now().Add(someTimeout))
+ c3.SetReadDeadline(time.Now().Add(someTimeout))
+ c3.SetWriteDeadline(time.Now().Add(someTimeout))
+ c3.SetReadBuffer(2048)
+ c3.SetWriteBuffer(2048)
+
+ wb := []byte("UNIXCONN TEST")
+ rb1 := make([]byte, 128)
+ rb2 := make([]byte, 128)
+ rb3 := make([]byte, 128)
+ if _, _, err := c1.WriteMsgUnix(wb, nil, a2); err != nil {
+ t.Fatalf("UnixConn.WriteMsgUnix failed: %v", err)
+ }
+ if _, _, _, _, err := c2.ReadMsgUnix(rb2, nil); err != nil {
+ t.Fatalf("UnixConn.ReadMsgUnix failed: %v", err)
+ }
+ if _, err := c2.WriteToUnix(wb, a1); err != nil {
+ t.Fatalf("UnixConn.WriteToUnix failed: %v", err)
+ }
+ if _, _, err := c1.ReadFromUnix(rb1); err != nil {
+ t.Fatalf("UnixConn.ReadFromUnix failed: %v", err)
+ }
+ if _, err := c3.WriteToUnix(wb, a1); err != nil {
+ t.Fatalf("UnixConn.WriteToUnix failed: %v", err)
+ }
+ if _, _, err := c1.ReadFromUnix(rb1); err != nil {
+ t.Fatalf("UnixConn.ReadFromUnix failed: %v", err)
+ }
+ if _, err := c2.WriteToUnix(wb, a3); err != nil {
+ t.Fatalf("UnixConn.WriteToUnix failed: %v", err)
+ }
+ if _, _, err := c3.ReadFromUnix(rb3); err != nil {
+ t.Fatalf("UnixConn.ReadFromUnix failed: %v", err)
+ }
+
+ if f, err := c1.File(); err != nil {
+ t.Fatalf("UnixConn.File failed: %v", err)
+ } else {
+ f.Close()
+ }
+}
diff --git a/src/pkg/net/rpc/client.go b/src/pkg/net/rpc/client.go
index db2da8e44..4b0c9c3bb 100644
--- a/src/pkg/net/rpc/client.go
+++ b/src/pkg/net/rpc/client.go
@@ -71,7 +71,7 @@ func (client *Client) send(call *Call) {
// Register this call.
client.mutex.Lock()
- if client.shutdown {
+ if client.shutdown || client.closing {
call.Error = ErrShutdown
client.mutex.Unlock()
call.done()
@@ -88,10 +88,13 @@ func (client *Client) send(call *Call) {
err := client.codec.WriteRequest(&client.request, call.Args)
if err != nil {
client.mutex.Lock()
+ call = client.pending[seq]
delete(client.pending, seq)
client.mutex.Unlock()
- call.Error = err
- call.done()
+ if call != nil {
+ call.Error = err
+ call.done()
+ }
}
}
@@ -102,9 +105,6 @@ func (client *Client) input() {
response = Response{}
err = client.codec.ReadResponseHeader(&response)
if err != nil {
- if err == io.EOF && !client.closing {
- err = io.ErrUnexpectedEOF
- }
break
}
seq := response.Seq
@@ -113,12 +113,18 @@ func (client *Client) input() {
delete(client.pending, seq)
client.mutex.Unlock()
- if response.Error == "" {
- err = client.codec.ReadResponseBody(call.Reply)
+ switch {
+ case call == nil:
+ // We've got no pending call. That usually means that
+ // WriteRequest partially failed, and call was already
+ // removed; response is a server telling us about an
+ // error reading request body. We should still attempt
+ // to read error body, but there's no one to give it to.
+ err = client.codec.ReadResponseBody(nil)
if err != nil {
- call.Error = errors.New("reading body " + err.Error())
+ err = errors.New("reading error body: " + err.Error())
}
- } else {
+ case response.Error != "":
// We've got an error response. Give this to the request;
// any subsequent requests will get the ReadResponseBody
// error if there is one.
@@ -127,14 +133,27 @@ func (client *Client) input() {
if err != nil {
err = errors.New("reading error body: " + err.Error())
}
+ call.done()
+ default:
+ err = client.codec.ReadResponseBody(call.Reply)
+ if err != nil {
+ call.Error = errors.New("reading body " + err.Error())
+ }
+ call.done()
}
- call.done()
}
// Terminate pending calls.
client.sending.Lock()
client.mutex.Lock()
client.shutdown = true
closing := client.closing
+ if err == io.EOF {
+ if closing {
+ err = ErrShutdown
+ } else {
+ err = io.ErrUnexpectedEOF
+ }
+ }
for _, call := range client.pending {
call.Error = err
call.done()
@@ -213,7 +232,7 @@ func DialHTTP(network, address string) (*Client, error) {
return DialHTTPPath(network, address, DefaultRPCPath)
}
-// DialHTTPPath connects to an HTTP RPC server
+// DialHTTPPath connects to an HTTP RPC server
// at the specified network address and path.
func DialHTTPPath(network, address, path string) (*Client, error) {
var err error
diff --git a/src/pkg/net/rpc/jsonrpc/all_test.go b/src/pkg/net/rpc/jsonrpc/all_test.go
index e6c7441f0..3c7c4d48f 100644
--- a/src/pkg/net/rpc/jsonrpc/all_test.go
+++ b/src/pkg/net/rpc/jsonrpc/all_test.go
@@ -24,6 +24,12 @@ type Reply struct {
type Arith int
+type ArithAddResp struct {
+ Id interface{} `json:"id"`
+ Result Reply `json:"result"`
+ Error interface{} `json:"error"`
+}
+
func (t *Arith) Add(args *Args, reply *Reply) error {
reply.C = args.A + args.B
return nil
@@ -50,13 +56,39 @@ func init() {
rpc.Register(new(Arith))
}
-func TestServer(t *testing.T) {
- type addResp struct {
- Id interface{} `json:"id"`
- Result Reply `json:"result"`
- Error interface{} `json:"error"`
+func TestServerNoParams(t *testing.T) {
+ cli, srv := net.Pipe()
+ defer cli.Close()
+ go ServeConn(srv)
+ dec := json.NewDecoder(cli)
+
+ fmt.Fprintf(cli, `{"method": "Arith.Add", "id": "123"}`)
+ var resp ArithAddResp
+ if err := dec.Decode(&resp); err != nil {
+ t.Fatalf("Decode after no params: %s", err)
+ }
+ if resp.Error == nil {
+ t.Fatalf("Expected error, got nil")
+ }
+}
+
+func TestServerEmptyMessage(t *testing.T) {
+ cli, srv := net.Pipe()
+ defer cli.Close()
+ go ServeConn(srv)
+ dec := json.NewDecoder(cli)
+
+ fmt.Fprintf(cli, "{}")
+ var resp ArithAddResp
+ if err := dec.Decode(&resp); err != nil {
+ t.Fatalf("Decode after empty: %s", err)
}
+ if resp.Error == nil {
+ t.Fatalf("Expected error, got nil")
+ }
+}
+func TestServer(t *testing.T) {
cli, srv := net.Pipe()
defer cli.Close()
go ServeConn(srv)
@@ -65,7 +97,7 @@ func TestServer(t *testing.T) {
// Send hand-coded requests to server, parse responses.
for i := 0; i < 10; i++ {
fmt.Fprintf(cli, `{"method": "Arith.Add", "id": "\u%04d", "params": [{"A": %d, "B": %d}]}`, i, i, i+1)
- var resp addResp
+ var resp ArithAddResp
err := dec.Decode(&resp)
if err != nil {
t.Fatalf("Decode: %s", err)
@@ -80,15 +112,6 @@ func TestServer(t *testing.T) {
t.Fatalf("resp: bad result: %d+%d=%d", i, i+1, resp.Result.C)
}
}
-
- fmt.Fprintf(cli, "{}\n")
- var resp addResp
- if err := dec.Decode(&resp); err != nil {
- t.Fatalf("Decode after empty: %s", err)
- }
- if resp.Error == nil {
- t.Fatalf("Expected error, got nil")
- }
}
func TestClient(t *testing.T) {
@@ -108,7 +131,7 @@ func TestClient(t *testing.T) {
t.Errorf("Add: expected no error but got string %q", err.Error())
}
if reply.C != args.A+args.B {
- t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
+ t.Errorf("Add: got %d expected %d", reply.C, args.A+args.B)
}
args = &Args{7, 8}
@@ -118,7 +141,7 @@ func TestClient(t *testing.T) {
t.Errorf("Mul: expected no error but got string %q", err.Error())
}
if reply.C != args.A*args.B {
- t.Errorf("Mul: expected %d got %d", reply.C, args.A*args.B)
+ t.Errorf("Mul: got %d expected %d", reply.C, args.A*args.B)
}
// Out of order.
@@ -133,7 +156,7 @@ func TestClient(t *testing.T) {
t.Errorf("Add: expected no error but got string %q", addCall.Error.Error())
}
if addReply.C != args.A+args.B {
- t.Errorf("Add: expected %d got %d", addReply.C, args.A+args.B)
+ t.Errorf("Add: got %d expected %d", addReply.C, args.A+args.B)
}
mulCall = <-mulCall.Done
@@ -141,7 +164,7 @@ func TestClient(t *testing.T) {
t.Errorf("Mul: expected no error but got string %q", mulCall.Error.Error())
}
if mulReply.C != args.A*args.B {
- t.Errorf("Mul: expected %d got %d", mulReply.C, args.A*args.B)
+ t.Errorf("Mul: got %d expected %d", mulReply.C, args.A*args.B)
}
// Error test
diff --git a/src/pkg/net/rpc/jsonrpc/server.go b/src/pkg/net/rpc/jsonrpc/server.go
index 4c54553a7..5bc05fd0a 100644
--- a/src/pkg/net/rpc/jsonrpc/server.go
+++ b/src/pkg/net/rpc/jsonrpc/server.go
@@ -12,6 +12,8 @@ import (
"sync"
)
+var errMissingParams = errors.New("jsonrpc: request body missing params")
+
type serverCodec struct {
dec *json.Decoder // for reading JSON values
enc *json.Encoder // for writing JSON values
@@ -50,12 +52,8 @@ type serverRequest struct {
func (r *serverRequest) reset() {
r.Method = ""
- if r.Params != nil {
- *r.Params = (*r.Params)[0:0]
- }
- if r.Id != nil {
- *r.Id = (*r.Id)[0:0]
- }
+ r.Params = nil
+ r.Id = nil
}
type serverResponse struct {
@@ -88,6 +86,9 @@ func (c *serverCodec) ReadRequestBody(x interface{}) error {
if x == nil {
return nil
}
+ if c.req.Params == nil {
+ return errMissingParams
+ }
// JSON params is array value.
// RPC params is struct.
// Unmarshal into array containing struct for now.
diff --git a/src/pkg/net/rpc/server.go b/src/pkg/net/rpc/server.go
index 1680e2f0d..e71b6fb1a 100644
--- a/src/pkg/net/rpc/server.go
+++ b/src/pkg/net/rpc/server.go
@@ -24,12 +24,13 @@
where T, T1 and T2 can be marshaled by encoding/gob.
These requirements apply even if a different codec is used.
- (In future, these requirements may soften for custom codecs.)
+ (In the future, these requirements may soften for custom codecs.)
The method's first argument represents the arguments provided by the caller; the
second argument represents the result parameters to be returned to the caller.
The method's return value, if non-nil, is passed back as a string that the client
- sees as if created by errors.New.
+ sees as if created by errors.New. If an error is returned, the reply parameter
+ will not be sent back to the client.
The server may handle requests on a single connection by calling ServeConn. More
typically it will create a network listener and call Accept or, for an HTTP
@@ -111,7 +112,7 @@
// Asynchronous call
quotient := new(Quotient)
- divCall := client.Go("Arith.Divide", args, &quotient, nil)
+ divCall := client.Go("Arith.Divide", args, quotient, nil)
replyCall := <-divCall.Done // will be equal to divCall
// check errors, print, etc.
@@ -181,7 +182,7 @@ type Response struct {
// Server represents an RPC Server.
type Server struct {
- mu sync.Mutex // protects the serviceMap
+ mu sync.RWMutex // protects the serviceMap
serviceMap map[string]*service
reqLock sync.Mutex // protects freeReq
freeReq *Request
@@ -218,15 +219,15 @@ func isExportedOrBuiltinType(t reflect.Type) bool {
// - exported method
// - two arguments, both pointers to exported structs
// - one return value, of type error
-// It returns an error if the receiver is not an exported type or has no
-// suitable methods.
+// It returns an error if the receiver is not an exported type or has
+// no methods or unsuitable methods. It also logs the error using package log.
// The client accesses each method using a string of the form "Type.Method",
// where Type is the receiver's concrete type.
func (server *Server) Register(rcvr interface{}) error {
return server.register(rcvr, "", false)
}
-// RegisterName is like Register but uses the provided name for the type
+// RegisterName is like Register but uses the provided name for the type
// instead of the receiver's concrete type.
func (server *Server) RegisterName(name string, rcvr interface{}) error {
return server.register(rcvr, name, true)
@@ -260,8 +261,30 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
s.method = make(map[string]*methodType)
// Install the methods
- for m := 0; m < s.typ.NumMethod(); m++ {
- method := s.typ.Method(m)
+ s.method = suitableMethods(s.typ, true)
+
+ if len(s.method) == 0 {
+ str := ""
+ // To help the user, see if a pointer receiver would work.
+ method := suitableMethods(reflect.PtrTo(s.typ), false)
+ if len(method) != 0 {
+ str = "rpc.Register: type " + sname + " has no exported methods of suitable type (hint: pass a pointer to value of that type)"
+ } else {
+ str = "rpc.Register: type " + sname + " has no exported methods of suitable type"
+ }
+ log.Print(str)
+ return errors.New(str)
+ }
+ server.serviceMap[s.name] = s
+ return nil
+}
+
+// suitableMethods returns suitable Rpc methods of typ, it will report
+// error using log if reportErr is true.
+func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType {
+ methods := make(map[string]*methodType)
+ for m := 0; m < typ.NumMethod(); m++ {
+ method := typ.Method(m)
mtype := method.Type
mname := method.Name
// Method must be exported.
@@ -270,46 +293,51 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
}
// Method needs three ins: receiver, *args, *reply.
if mtype.NumIn() != 3 {
- log.Println("method", mname, "has wrong number of ins:", mtype.NumIn())
+ if reportErr {
+ log.Println("method", mname, "has wrong number of ins:", mtype.NumIn())
+ }
continue
}
// First arg need not be a pointer.
argType := mtype.In(1)
if !isExportedOrBuiltinType(argType) {
- log.Println(mname, "argument type not exported:", argType)
+ if reportErr {
+ log.Println(mname, "argument type not exported:", argType)
+ }
continue
}
// Second arg must be a pointer.
replyType := mtype.In(2)
if replyType.Kind() != reflect.Ptr {
- log.Println("method", mname, "reply type not a pointer:", replyType)
+ if reportErr {
+ log.Println("method", mname, "reply type not a pointer:", replyType)
+ }
continue
}
// Reply type must be exported.
if !isExportedOrBuiltinType(replyType) {
- log.Println("method", mname, "reply type not exported:", replyType)
+ if reportErr {
+ log.Println("method", mname, "reply type not exported:", replyType)
+ }
continue
}
// Method needs one out.
if mtype.NumOut() != 1 {
- log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
+ if reportErr {
+ log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
+ }
continue
}
// The return type of the method must be error.
if returnType := mtype.Out(0); returnType != typeOfError {
- log.Println("method", mname, "returns", returnType.String(), "not error")
+ if reportErr {
+ log.Println("method", mname, "returns", returnType.String(), "not error")
+ }
continue
}
- s.method[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType}
+ methods[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType}
}
-
- if len(s.method) == 0 {
- s := "rpc Register: type " + sname + " has no exported methods of suitable type"
- log.Print(s)
- return errors.New(s)
- }
- server.serviceMap[s.name] = s
- return nil
+ return methods
}
// A value sent as a placeholder for the server's response value when the server
@@ -538,9 +566,9 @@ func (server *Server) readRequestHeader(codec ServerCodec) (service *service, mt
return
}
// Look up the request.
- server.mu.Lock()
+ server.mu.RLock()
service = server.serviceMap[serviceMethod[0]]
- server.mu.Unlock()
+ server.mu.RUnlock()
if service == nil {
err = errors.New("rpc: can't find service " + req.ServiceMethod)
return
@@ -568,7 +596,7 @@ func (server *Server) Accept(lis net.Listener) {
// Register publishes the receiver's methods in the DefaultServer.
func Register(rcvr interface{}) error { return DefaultServer.Register(rcvr) }
-// RegisterName is like Register but uses the provided name for the type
+// RegisterName is like Register but uses the provided name for the type
// instead of the receiver's concrete type.
func RegisterName(name string, rcvr interface{}) error {
return DefaultServer.RegisterName(name, rcvr)
@@ -611,7 +639,7 @@ func ServeRequest(codec ServerCodec) error {
}
// Accept accepts connections on the listener and serves requests
-// to DefaultServer for each incoming connection.
+// to DefaultServer for each incoming connection.
// Accept blocks; the caller typically invokes it in a go statement.
func Accept(lis net.Listener) { DefaultServer.Accept(lis) }
diff --git a/src/pkg/net/rpc/server_test.go b/src/pkg/net/rpc/server_test.go
index 62c7b1e60..8a1530623 100644
--- a/src/pkg/net/rpc/server_test.go
+++ b/src/pkg/net/rpc/server_test.go
@@ -349,6 +349,7 @@ func testServeRequest(t *testing.T, server *Server) {
type ReplyNotPointer int
type ArgNotPublic int
type ReplyNotPublic int
+type NeedsPtrType int
type local struct{}
func (t *ReplyNotPointer) ReplyNotPointer(args *Args, reply Reply) error {
@@ -363,19 +364,29 @@ func (t *ReplyNotPublic) ReplyNotPublic(args *Args, reply *local) error {
return nil
}
+func (t *NeedsPtrType) NeedsPtrType(args *Args, reply *Reply) error {
+ return nil
+}
+
// Check that registration handles lots of bad methods and a type with no suitable methods.
func TestRegistrationError(t *testing.T) {
err := Register(new(ReplyNotPointer))
if err == nil {
- t.Errorf("expected error registering ReplyNotPointer")
+ t.Error("expected error registering ReplyNotPointer")
}
err = Register(new(ArgNotPublic))
if err == nil {
- t.Errorf("expected error registering ArgNotPublic")
+ t.Error("expected error registering ArgNotPublic")
}
err = Register(new(ReplyNotPublic))
if err == nil {
- t.Errorf("expected error registering ReplyNotPublic")
+ t.Error("expected error registering ReplyNotPublic")
+ }
+ err = Register(NeedsPtrType(0))
+ if err == nil {
+ t.Error("expected error registering NeedsPtrType")
+ } else if !strings.Contains(err.Error(), "pointer") {
+ t.Error("expected hint when registering NeedsPtrType")
}
}
@@ -434,7 +445,7 @@ func dialHTTP() (*Client, error) {
return DialHTTP("tcp", httpServerAddr)
}
-func countMallocs(dial func() (*Client, error), t *testing.T) uint64 {
+func countMallocs(dial func() (*Client, error), t *testing.T) float64 {
once.Do(startServer)
client, err := dial()
if err != nil {
@@ -442,11 +453,7 @@ func countMallocs(dial func() (*Client, error), t *testing.T) uint64 {
}
args := &Args{7, 8}
reply := new(Reply)
- memstats := new(runtime.MemStats)
- runtime.ReadMemStats(memstats)
- mallocs := 0 - memstats.Mallocs
- const count = 100
- for i := 0; i < count; i++ {
+ return testing.AllocsPerRun(100, func() {
err := client.Call("Arith.Add", args, reply)
if err != nil {
t.Errorf("Add: expected no error but got string %q", err.Error())
@@ -454,18 +461,15 @@ func countMallocs(dial func() (*Client, error), t *testing.T) uint64 {
if reply.C != args.A+args.B {
t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B)
}
- }
- runtime.ReadMemStats(memstats)
- mallocs += memstats.Mallocs
- return mallocs / count
+ })
}
func TestCountMallocs(t *testing.T) {
- fmt.Printf("mallocs per rpc round trip: %d\n", countMallocs(dialDirect, t))
+ fmt.Printf("mallocs per rpc round trip: %v\n", countMallocs(dialDirect, t))
}
func TestCountMallocsOverHTTP(t *testing.T) {
- fmt.Printf("mallocs per HTTP rpc round trip: %d\n", countMallocs(dialHTTP, t))
+ fmt.Printf("mallocs per HTTP rpc round trip: %v\n", countMallocs(dialHTTP, t))
}
type writeCrasher struct {
@@ -499,6 +503,44 @@ func TestClientWriteError(t *testing.T) {
w.done <- true
}
+func TestTCPClose(t *testing.T) {
+ once.Do(startServer)
+
+ client, err := dialHTTP()
+ if err != nil {
+ t.Fatalf("dialing: %v", err)
+ }
+ defer client.Close()
+
+ args := Args{17, 8}
+ var reply Reply
+ err = client.Call("Arith.Mul", args, &reply)
+ if err != nil {
+ t.Fatal("arith error:", err)
+ }
+ t.Logf("Arith: %d*%d=%d\n", args.A, args.B, reply)
+ if reply.C != args.A*args.B {
+ t.Errorf("Add: expected %d got %d", reply.C, args.A*args.B)
+ }
+}
+
+func TestErrorAfterClientClose(t *testing.T) {
+ once.Do(startServer)
+
+ client, err := dialHTTP()
+ if err != nil {
+ t.Fatalf("dialing: %v", err)
+ }
+ err = client.Close()
+ if err != nil {
+ t.Fatal("close error:", err)
+ }
+ err = client.Call("Arith.Add", &Args{7, 9}, new(Reply))
+ if err != ErrShutdown {
+ t.Errorf("Forever: expected ErrShutdown got %v", err)
+ }
+}
+
func benchmarkEndToEnd(dial func() (*Client, error), b *testing.B) {
b.StopTimer()
once.Do(startServer)
diff --git a/src/pkg/net/sendfile_freebsd.go b/src/pkg/net/sendfile_freebsd.go
new file mode 100644
index 000000000..8008bc3b5
--- /dev/null
+++ b/src/pkg/net/sendfile_freebsd.go
@@ -0,0 +1,105 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "io"
+ "os"
+ "syscall"
+)
+
+// maxSendfileSize is the largest chunk size we ask the kernel to copy
+// at a time.
+const maxSendfileSize int = 4 << 20
+
+// sendFile copies the contents of r to c using the sendfile
+// system call to minimize copies.
+//
+// if handled == true, sendFile returns the number of bytes copied and any
+// non-EOF error.
+//
+// if handled == false, sendFile performed no work.
+func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
+ // FreeBSD uses 0 as the "until EOF" value. If you pass in more bytes than the
+ // file contains, it will loop back to the beginning ad nauseum until it's sent
+ // exactly the number of bytes told to. As such, we need to know exactly how many
+ // bytes to send.
+ var remain int64 = 0
+
+ lr, ok := r.(*io.LimitedReader)
+ if ok {
+ remain, r = lr.N, lr.R
+ if remain <= 0 {
+ return 0, nil, true
+ }
+ }
+ f, ok := r.(*os.File)
+ if !ok {
+ return 0, nil, false
+ }
+
+ if remain == 0 {
+ fi, err := f.Stat()
+ if err != nil {
+ return 0, err, false
+ }
+
+ remain = fi.Size()
+ }
+
+ // The other quirk with FreeBSD's sendfile implementation is that it doesn't
+ // use the current position of the file -- if you pass it offset 0, it starts
+ // from offset 0. There's no way to tell it "start from current position", so
+ // we have to manage that explicitly.
+ pos, err := f.Seek(0, os.SEEK_CUR)
+ if err != nil {
+ return 0, err, false
+ }
+
+ c.wio.Lock()
+ defer c.wio.Unlock()
+ if err := c.incref(false); err != nil {
+ return 0, err, true
+ }
+ defer c.decref()
+
+ dst := c.sysfd
+ src := int(f.Fd())
+ for remain > 0 {
+ n := maxSendfileSize
+ if int64(n) > remain {
+ n = int(remain)
+ }
+ pos1 := pos
+ n, err1 := syscall.Sendfile(dst, src, &pos1, n)
+ if n > 0 {
+ pos += int64(n)
+ written += int64(n)
+ remain -= int64(n)
+ }
+ if n == 0 && err1 == nil {
+ break
+ }
+ if err1 == syscall.EAGAIN {
+ if err1 = c.pollServer.WaitWrite(c); err1 == nil {
+ continue
+ }
+ }
+ if err1 == syscall.EINTR {
+ continue
+ }
+ if err1 != nil {
+ // This includes syscall.ENOSYS (no kernel
+ // support) and syscall.EINVAL (fd types which
+ // don't implement sendfile together)
+ err = &OpError{"sendfile", c.net, c.raddr, err1}
+ break
+ }
+ }
+ if lr != nil {
+ lr.N = remain
+ }
+ return written, err, written > 0
+}
diff --git a/src/pkg/net/sendfile_linux.go b/src/pkg/net/sendfile_linux.go
index a0d530362..3357e6538 100644
--- a/src/pkg/net/sendfile_linux.go
+++ b/src/pkg/net/sendfile_linux.go
@@ -58,8 +58,8 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
if n == 0 && err1 == nil {
break
}
- if err1 == syscall.EAGAIN && c.wdeadline >= 0 {
- if err1 = pollserver.WaitWrite(c); err1 == nil {
+ if err1 == syscall.EAGAIN {
+ if err1 = c.pollServer.WaitWrite(c); err1 == nil {
continue
}
}
diff --git a/src/pkg/net/sendfile_stub.go b/src/pkg/net/sendfile_stub.go
index ff76ab9cf..3660849c1 100644
--- a/src/pkg/net/sendfile_stub.go
+++ b/src/pkg/net/sendfile_stub.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd netbsd openbsd
+// +build darwin netbsd openbsd
package net
diff --git a/src/pkg/net/sendfile_windows.go b/src/pkg/net/sendfile_windows.go
index f5a6d8804..2d64f2f5b 100644
--- a/src/pkg/net/sendfile_windows.go
+++ b/src/pkg/net/sendfile_windows.go
@@ -48,12 +48,12 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
return 0, nil, false
}
- c.wio.Lock()
- defer c.wio.Unlock()
if err := c.incref(false); err != nil {
return 0, err, true
}
defer c.decref()
+ c.wio.Lock()
+ defer c.wio.Unlock()
var o sendfileOp
o.Init(c, 'w')
diff --git a/src/pkg/net/server_test.go b/src/pkg/net/server_test.go
index 158b9477d..25c2be5a7 100644
--- a/src/pkg/net/server_test.go
+++ b/src/pkg/net/server_test.go
@@ -113,8 +113,7 @@ func TestStreamConnServer(t *testing.T) {
case "tcp", "tcp4", "tcp6":
_, port, err := SplitHostPort(taddr)
if err != nil {
- t.Errorf("SplitHostPort(%q) failed: %v", taddr, err)
- return
+ t.Fatalf("SplitHostPort(%q) failed: %v", taddr, err)
}
taddr = tt.caddr + ":" + port
}
@@ -142,8 +141,7 @@ var seqpacketConnServerTests = []struct {
func TestSeqpacketConnServer(t *testing.T) {
if runtime.GOOS != "linux" {
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
for _, tt := range seqpacketConnServerTests {
@@ -170,11 +168,11 @@ func TestSeqpacketConnServer(t *testing.T) {
}
func runStreamConnServer(t *testing.T, net, laddr string, listening chan<- string, done chan<- int) {
+ defer close(done)
l, err := Listen(net, laddr)
if err != nil {
t.Errorf("Listen(%q, %q) failed: %v", net, laddr, err)
listening <- "<nil>"
- done <- 1
return
}
defer l.Close()
@@ -189,13 +187,14 @@ func runStreamConnServer(t *testing.T, net, laddr string, listening chan<- strin
}
rw.Write(buf[0:n])
}
- done <- 1
+ close(done)
}
run:
for {
c, err := l.Accept()
if err != nil {
+ t.Logf("Accept failed: %v", err)
continue run
}
echodone := make(chan int)
@@ -204,14 +203,12 @@ run:
c.Close()
break run
}
- done <- 1
}
func runStreamConnClient(t *testing.T, net, taddr string, isEmpty bool) {
c, err := Dial(net, taddr)
if err != nil {
- t.Errorf("Dial(%q, %q) failed: %v", net, taddr, err)
- return
+ t.Fatalf("Dial(%q, %q) failed: %v", net, taddr, err)
}
defer c.Close()
c.SetReadDeadline(time.Now().Add(1 * time.Second))
@@ -221,14 +218,12 @@ func runStreamConnClient(t *testing.T, net, taddr string, isEmpty bool) {
wb = []byte("StreamConnClient by Dial\n")
}
if n, err := c.Write(wb); err != nil || n != len(wb) {
- t.Errorf("Write failed: %v, %v; want %v, <nil>", n, err, len(wb))
- return
+ t.Fatalf("Write failed: %v, %v; want %v, <nil>", n, err, len(wb))
}
rb := make([]byte, 1024)
if n, err := c.Read(rb[0:]); err != nil || n != len(wb) {
- t.Errorf("Read failed: %v, %v; want %v, <nil>", n, err, len(wb))
- return
+ t.Fatalf("Read failed: %v, %v; want %v, <nil>", n, err, len(wb))
}
// Send explicit ending for unixpacket.
@@ -334,8 +329,7 @@ func TestDatagramPacketConnServer(t *testing.T) {
case "udp", "udp4", "udp6":
_, port, err := SplitHostPort(taddr)
if err != nil {
- t.Errorf("SplitHostPort(%q) failed: %v", taddr, err)
- return
+ t.Fatalf("SplitHostPort(%q) failed: %v", taddr, err)
}
taddr = tt.caddr + ":" + port
tt.caddr += ":0"
@@ -398,14 +392,12 @@ func runDatagramConnClient(t *testing.T, net, laddr, taddr string, isEmpty bool)
case "udp", "udp4", "udp6":
c, err = Dial(net, taddr)
if err != nil {
- t.Errorf("Dial(%q, %q) failed: %v", net, taddr, err)
- return
+ t.Fatalf("Dial(%q, %q) failed: %v", net, taddr, err)
}
case "unixgram":
c, err = DialUnix(net, &UnixAddr{laddr, net}, &UnixAddr{taddr, net})
if err != nil {
- t.Errorf("DialUnix(%q, {%q, %q}) failed: %v", net, laddr, taddr, err)
- return
+ t.Fatalf("DialUnix(%q, {%q, %q}) failed: %v", net, laddr, taddr, err)
}
}
defer c.Close()
@@ -416,14 +408,12 @@ func runDatagramConnClient(t *testing.T, net, laddr, taddr string, isEmpty bool)
wb = []byte("DatagramConnClient by Dial\n")
}
if n, err := c.Write(wb[0:]); err != nil || n != len(wb) {
- t.Errorf("Write failed: %v, %v; want %v, <nil>", n, err, len(wb))
- return
+ t.Fatalf("Write failed: %v, %v; want %v, <nil>", n, err, len(wb))
}
rb := make([]byte, 1024)
if n, err := c.Read(rb[0:]); err != nil || n != len(wb) {
- t.Errorf("Read failed: %v, %v; want %v, <nil>", n, err, len(wb))
- return
+ t.Fatalf("Read failed: %v, %v; want %v, <nil>", n, err, len(wb))
}
}
@@ -434,20 +424,17 @@ func runDatagramPacketConnClient(t *testing.T, net, laddr, taddr string, isEmpty
case "udp", "udp4", "udp6":
ra, err = ResolveUDPAddr(net, taddr)
if err != nil {
- t.Errorf("ResolveUDPAddr(%q, %q) failed: %v", net, taddr, err)
- return
+ t.Fatalf("ResolveUDPAddr(%q, %q) failed: %v", net, taddr, err)
}
case "unixgram":
ra, err = ResolveUnixAddr(net, taddr)
if err != nil {
- t.Errorf("ResolveUxixAddr(%q, %q) failed: %v", net, taddr, err)
- return
+ t.Fatalf("ResolveUxixAddr(%q, %q) failed: %v", net, taddr, err)
}
}
c, err := ListenPacket(net, laddr)
if err != nil {
- t.Errorf("ListenPacket(%q, %q) faild: %v", net, laddr, err)
- return
+ t.Fatalf("ListenPacket(%q, %q) faild: %v", net, laddr, err)
}
defer c.Close()
c.SetReadDeadline(time.Now().Add(1 * time.Second))
@@ -457,13 +444,11 @@ func runDatagramPacketConnClient(t *testing.T, net, laddr, taddr string, isEmpty
wb = []byte("DatagramPacketConnClient by ListenPacket\n")
}
if n, err := c.WriteTo(wb[0:], ra); err != nil || n != len(wb) {
- t.Errorf("WriteTo(%v) failed: %v, %v; want %v, <nil>", ra, n, err, len(wb))
- return
+ t.Fatalf("WriteTo(%v) failed: %v, %v; want %v, <nil>", ra, n, err, len(wb))
}
rb := make([]byte, 1024)
if n, _, err := c.ReadFrom(rb[0:]); err != nil || n != len(wb) {
- t.Errorf("ReadFrom failed: %v, %v; want %v, <nil>", n, err, len(wb))
- return
+ t.Fatalf("ReadFrom failed: %v, %v; want %v, <nil>", n, err, len(wb))
}
}
diff --git a/src/pkg/net/smtp/smtp.go b/src/pkg/net/smtp/smtp.go
index 59f6449f0..4b9177877 100644
--- a/src/pkg/net/smtp/smtp.go
+++ b/src/pkg/net/smtp/smtp.go
@@ -13,6 +13,7 @@ package smtp
import (
"crypto/tls"
"encoding/base64"
+ "errors"
"io"
"net"
"net/textproto"
@@ -33,7 +34,10 @@ type Client struct {
// map of supported extensions
ext map[string]string
// supported auth mechanisms
- auth []string
+ auth []string
+ localName string // the name to use in HELO/EHLO
+ didHello bool // whether we've said HELO/EHLO
+ helloError error // the error from the hello
}
// Dial returns a new Client connected to an SMTP server at addr.
@@ -55,12 +59,33 @@ func NewClient(conn net.Conn, host string) (*Client, error) {
text.Close()
return nil, err
}
- c := &Client{Text: text, conn: conn, serverName: host}
- err = c.ehlo()
- if err != nil {
- err = c.helo()
+ c := &Client{Text: text, conn: conn, serverName: host, localName: "localhost"}
+ return c, nil
+}
+
+// hello runs a hello exchange if needed.
+func (c *Client) hello() error {
+ if !c.didHello {
+ c.didHello = true
+ err := c.ehlo()
+ if err != nil {
+ c.helloError = c.helo()
+ }
+ }
+ return c.helloError
+}
+
+// Hello sends a HELO or EHLO to the server as the given host name.
+// Calling this method is only necessary if the client needs control
+// over the host name used. The client will introduce itself as "localhost"
+// automatically otherwise. If Hello is called, it must be called before
+// any of the other methods.
+func (c *Client) Hello(localName string) error {
+ if c.didHello {
+ return errors.New("smtp: Hello called after other methods")
}
- return c, err
+ c.localName = localName
+ return c.hello()
}
// cmd is a convenience function that sends a command and returns the response
@@ -79,14 +104,14 @@ func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, s
// server does not support ehlo.
func (c *Client) helo() error {
c.ext = nil
- _, _, err := c.cmd(250, "HELO localhost")
+ _, _, err := c.cmd(250, "HELO %s", c.localName)
return err
}
// ehlo sends the EHLO (extended hello) greeting to the server. It
// should be the preferred greeting for servers that support it.
func (c *Client) ehlo() error {
- _, msg, err := c.cmd(250, "EHLO localhost")
+ _, msg, err := c.cmd(250, "EHLO %s", c.localName)
if err != nil {
return err
}
@@ -113,6 +138,9 @@ func (c *Client) ehlo() error {
// StartTLS sends the STARTTLS command and encrypts all further communication.
// Only servers that advertise the STARTTLS extension support this function.
func (c *Client) StartTLS(config *tls.Config) error {
+ if err := c.hello(); err != nil {
+ return err
+ }
_, _, err := c.cmd(220, "STARTTLS")
if err != nil {
return err
@@ -128,6 +156,9 @@ func (c *Client) StartTLS(config *tls.Config) error {
// does not necessarily indicate an invalid address. Many servers
// will not verify addresses for security reasons.
func (c *Client) Verify(addr string) error {
+ if err := c.hello(); err != nil {
+ return err
+ }
_, _, err := c.cmd(250, "VRFY %s", addr)
return err
}
@@ -136,6 +167,9 @@ func (c *Client) Verify(addr string) error {
// A failed authentication closes the connection.
// Only servers that advertise the AUTH extension support this function.
func (c *Client) Auth(a Auth) error {
+ if err := c.hello(); err != nil {
+ return err
+ }
encoding := base64.StdEncoding
mech, resp, err := a.Start(&ServerInfo{c.serverName, c.tls, c.auth})
if err != nil {
@@ -178,6 +212,9 @@ func (c *Client) Auth(a Auth) error {
// parameter.
// This initiates a mail transaction and is followed by one or more Rcpt calls.
func (c *Client) Mail(from string) error {
+ if err := c.hello(); err != nil {
+ return err
+ }
cmdStr := "MAIL FROM:<%s>"
if c.ext != nil {
if _, ok := c.ext["8BITMIME"]; ok {
@@ -227,6 +264,9 @@ func SendMail(addr string, a Auth, from string, to []string, msg []byte) error {
if err != nil {
return err
}
+ if err := c.hello(); err != nil {
+ return err
+ }
if ok, _ := c.Extension("STARTTLS"); ok {
if err = c.StartTLS(nil); err != nil {
return err
@@ -267,6 +307,9 @@ func SendMail(addr string, a Auth, from string, to []string, msg []byte) error {
// Extension also returns a string that contains any parameters the
// server specifies for the extension.
func (c *Client) Extension(ext string) (bool, string) {
+ if err := c.hello(); err != nil {
+ return false, ""
+ }
if c.ext == nil {
return false, ""
}
@@ -278,12 +321,18 @@ func (c *Client) Extension(ext string) (bool, string) {
// Reset sends the RSET command to the server, aborting the current mail
// transaction.
func (c *Client) Reset() error {
+ if err := c.hello(); err != nil {
+ return err
+ }
_, _, err := c.cmd(250, "RSET")
return err
}
// Quit sends the QUIT command and closes the connection to the server.
func (c *Client) Quit() error {
+ if err := c.hello(); err != nil {
+ return err
+ }
_, _, err := c.cmd(221, "QUIT")
if err != nil {
return err
diff --git a/src/pkg/net/smtp/smtp_test.go b/src/pkg/net/smtp/smtp_test.go
index c315d185c..8317428cb 100644
--- a/src/pkg/net/smtp/smtp_test.go
+++ b/src/pkg/net/smtp/smtp_test.go
@@ -69,14 +69,14 @@ func (f faker) SetReadDeadline(time.Time) error { return nil }
func (f faker) SetWriteDeadline(time.Time) error { return nil }
func TestBasic(t *testing.T) {
- basicServer = strings.Join(strings.Split(basicServer, "\n"), "\r\n")
- basicClient = strings.Join(strings.Split(basicClient, "\n"), "\r\n")
+ server := strings.Join(strings.Split(basicServer, "\n"), "\r\n")
+ client := strings.Join(strings.Split(basicClient, "\n"), "\r\n")
var cmdbuf bytes.Buffer
bcmdbuf := bufio.NewWriter(&cmdbuf)
var fake faker
- fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(basicServer)), bcmdbuf)
- c := &Client{Text: textproto.NewConn(fake)}
+ fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
+ c := &Client{Text: textproto.NewConn(fake), localName: "localhost"}
if err := c.helo(); err != nil {
t.Fatalf("HELO failed: %s", err)
@@ -88,6 +88,7 @@ func TestBasic(t *testing.T) {
t.Fatalf("Second EHLO failed: %s", err)
}
+ c.didHello = true
if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" {
t.Fatalf("Expected AUTH supported")
}
@@ -143,8 +144,8 @@ Goodbye.`
bcmdbuf.Flush()
actualcmds := cmdbuf.String()
- if basicClient != actualcmds {
- t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, basicClient)
+ if client != actualcmds {
+ t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
}
}
@@ -187,8 +188,8 @@ QUIT
`
func TestNewClient(t *testing.T) {
- newClientServer = strings.Join(strings.Split(newClientServer, "\n"), "\r\n")
- newClientClient = strings.Join(strings.Split(newClientClient, "\n"), "\r\n")
+ server := strings.Join(strings.Split(newClientServer, "\n"), "\r\n")
+ client := strings.Join(strings.Split(newClientClient, "\n"), "\r\n")
var cmdbuf bytes.Buffer
bcmdbuf := bufio.NewWriter(&cmdbuf)
@@ -197,7 +198,7 @@ func TestNewClient(t *testing.T) {
return cmdbuf.String()
}
var fake faker
- fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClientServer)), bcmdbuf)
+ fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
c, err := NewClient(fake, "fake.host")
if err != nil {
t.Fatalf("NewClient: %v\n(after %v)", err, out())
@@ -213,8 +214,8 @@ func TestNewClient(t *testing.T) {
}
actualcmds := out()
- if newClientClient != actualcmds {
- t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClientClient)
+ if client != actualcmds {
+ t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
}
}
@@ -231,13 +232,13 @@ QUIT
`
func TestNewClient2(t *testing.T) {
- newClient2Server = strings.Join(strings.Split(newClient2Server, "\n"), "\r\n")
- newClient2Client = strings.Join(strings.Split(newClient2Client, "\n"), "\r\n")
+ server := strings.Join(strings.Split(newClient2Server, "\n"), "\r\n")
+ client := strings.Join(strings.Split(newClient2Client, "\n"), "\r\n")
var cmdbuf bytes.Buffer
bcmdbuf := bufio.NewWriter(&cmdbuf)
var fake faker
- fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClient2Server)), bcmdbuf)
+ fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
c, err := NewClient(fake, "fake.host")
if err != nil {
t.Fatalf("NewClient: %v", err)
@@ -251,8 +252,8 @@ func TestNewClient2(t *testing.T) {
bcmdbuf.Flush()
actualcmds := cmdbuf.String()
- if newClient2Client != actualcmds {
- t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClient2Client)
+ if client != actualcmds {
+ t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
}
}
@@ -269,3 +270,199 @@ var newClient2Client = `EHLO localhost
HELO localhost
QUIT
`
+
+func TestHello(t *testing.T) {
+
+ if len(helloServer) != len(helloClient) {
+ t.Fatalf("Hello server and client size mismatch")
+ }
+
+ for i := 0; i < len(helloServer); i++ {
+ server := strings.Join(strings.Split(baseHelloServer+helloServer[i], "\n"), "\r\n")
+ client := strings.Join(strings.Split(baseHelloClient+helloClient[i], "\n"), "\r\n")
+ var cmdbuf bytes.Buffer
+ bcmdbuf := bufio.NewWriter(&cmdbuf)
+ var fake faker
+ fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
+ c, err := NewClient(fake, "fake.host")
+ if err != nil {
+ t.Fatalf("NewClient: %v", err)
+ }
+ c.localName = "customhost"
+ err = nil
+
+ switch i {
+ case 0:
+ err = c.Hello("customhost")
+ case 1:
+ err = c.StartTLS(nil)
+ if err.Error() == "502 Not implemented" {
+ err = nil
+ }
+ case 2:
+ err = c.Verify("test@example.com")
+ case 3:
+ c.tls = true
+ c.serverName = "smtp.google.com"
+ err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com"))
+ case 4:
+ err = c.Mail("test@example.com")
+ case 5:
+ ok, _ := c.Extension("feature")
+ if ok {
+ t.Errorf("Expected FEATURE not to be supported")
+ }
+ case 6:
+ err = c.Reset()
+ case 7:
+ err = c.Quit()
+ case 8:
+ err = c.Verify("test@example.com")
+ if err != nil {
+ err = c.Hello("customhost")
+ if err != nil {
+ t.Errorf("Want error, got none")
+ }
+ }
+ default:
+ t.Fatalf("Unhandled command")
+ }
+
+ if err != nil {
+ t.Errorf("Command %d failed: %v", i, err)
+ }
+
+ bcmdbuf.Flush()
+ actualcmds := cmdbuf.String()
+ if client != actualcmds {
+ t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client)
+ }
+ }
+}
+
+var baseHelloServer = `220 hello world
+502 EH?
+250-mx.google.com at your service
+250 FEATURE
+`
+
+var helloServer = []string{
+ "",
+ "502 Not implemented\n",
+ "250 User is valid\n",
+ "235 Accepted\n",
+ "250 Sender ok\n",
+ "",
+ "250 Reset ok\n",
+ "221 Goodbye\n",
+ "250 Sender ok\n",
+}
+
+var baseHelloClient = `EHLO customhost
+HELO customhost
+`
+
+var helloClient = []string{
+ "",
+ "STARTTLS\n",
+ "VRFY test@example.com\n",
+ "AUTH PLAIN AHVzZXIAcGFzcw==\n",
+ "MAIL FROM:<test@example.com>\n",
+ "",
+ "RSET\n",
+ "QUIT\n",
+ "VRFY test@example.com\n",
+}
+
+func TestSendMail(t *testing.T) {
+ server := strings.Join(strings.Split(sendMailServer, "\n"), "\r\n")
+ client := strings.Join(strings.Split(sendMailClient, "\n"), "\r\n")
+ var cmdbuf bytes.Buffer
+ bcmdbuf := bufio.NewWriter(&cmdbuf)
+ l, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("Unable to to create listener: %v", err)
+ }
+ defer l.Close()
+
+ // prevent data race on bcmdbuf
+ var done = make(chan struct{})
+ go func(data []string) {
+
+ defer close(done)
+
+ conn, err := l.Accept()
+ if err != nil {
+ t.Errorf("Accept error: %v", err)
+ return
+ }
+ defer conn.Close()
+
+ tc := textproto.NewConn(conn)
+ for i := 0; i < len(data) && data[i] != ""; i++ {
+ tc.PrintfLine(data[i])
+ for len(data[i]) >= 4 && data[i][3] == '-' {
+ i++
+ tc.PrintfLine(data[i])
+ }
+ if data[i] == "221 Goodbye" {
+ return
+ }
+ read := false
+ for !read || data[i] == "354 Go ahead" {
+ msg, err := tc.ReadLine()
+ bcmdbuf.Write([]byte(msg + "\r\n"))
+ read = true
+ if err != nil {
+ t.Errorf("Read error: %v", err)
+ return
+ }
+ if data[i] == "354 Go ahead" && msg == "." {
+ break
+ }
+ }
+ }
+ }(strings.Split(server, "\r\n"))
+
+ err = SendMail(l.Addr().String(), nil, "test@example.com", []string{"other@example.com"}, []byte(strings.Replace(`From: test@example.com
+To: other@example.com
+Subject: SendMail test
+
+SendMail is working for me.
+`, "\n", "\r\n", -1)))
+
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+
+ <-done
+ bcmdbuf.Flush()
+ actualcmds := cmdbuf.String()
+ if client != actualcmds {
+ t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client)
+ }
+}
+
+var sendMailServer = `220 hello world
+502 EH?
+250 mx.google.com at your service
+250 Sender ok
+250 Receiver ok
+354 Go ahead
+250 Data ok
+221 Goodbye
+`
+
+var sendMailClient = `EHLO localhost
+HELO localhost
+MAIL FROM:<test@example.com>
+RCPT TO:<other@example.com>
+DATA
+From: test@example.com
+To: other@example.com
+Subject: SendMail test
+
+SendMail is working for me.
+.
+QUIT
+`
diff --git a/src/pkg/net/sock.go b/src/pkg/net/sock.go
deleted file mode 100644
index 3ae16054e..000000000
--- a/src/pkg/net/sock.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build darwin freebsd linux netbsd openbsd windows
-
-// Sockets
-
-package net
-
-import (
- "io"
- "syscall"
-)
-
-var listenerBacklog = maxListenerBacklog()
-
-// Generic socket creation.
-func socket(net string, f, t, p int, ipv6only bool, la, ra syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
- // See ../syscall/exec.go for description of ForkLock.
- syscall.ForkLock.RLock()
- s, err := syscall.Socket(f, t, p)
- if err != nil {
- syscall.ForkLock.RUnlock()
- return nil, err
- }
- syscall.CloseOnExec(s)
- syscall.ForkLock.RUnlock()
-
- err = setDefaultSockopts(s, f, t, ipv6only)
- if err != nil {
- closesocket(s)
- return nil, err
- }
-
- var bla syscall.Sockaddr
- if la != nil {
- bla, err = listenerSockaddr(s, f, la, toAddr)
- if err != nil {
- closesocket(s)
- return nil, err
- }
- err = syscall.Bind(s, bla)
- if err != nil {
- closesocket(s)
- return nil, err
- }
- }
-
- if fd, err = newFD(s, f, t, net); err != nil {
- closesocket(s)
- return nil, err
- }
-
- if ra != nil {
- if err = fd.connect(ra); err != nil {
- closesocket(s)
- fd.Close()
- return nil, err
- }
- fd.isConnected = true
- }
-
- sa, _ := syscall.Getsockname(s)
- var laddr Addr
- if la != nil && bla != la {
- laddr = toAddr(la)
- } else {
- laddr = toAddr(sa)
- }
- sa, _ = syscall.Getpeername(s)
- raddr := toAddr(sa)
-
- fd.setAddr(laddr, raddr)
- return fd, nil
-}
-
-type writerOnly struct {
- io.Writer
-}
-
-// Fallback implementation of io.ReaderFrom's ReadFrom, when sendfile isn't
-// applicable.
-func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) {
- // Use wrapper to hide existing r.ReadFrom from io.Copy.
- return io.Copy(writerOnly{w}, r)
-}
diff --git a/src/pkg/net/sock_bsd.go b/src/pkg/net/sock_bsd.go
index 2607b04c7..3205f9404 100644
--- a/src/pkg/net/sock_bsd.go
+++ b/src/pkg/net/sock_bsd.go
@@ -4,8 +4,6 @@
// +build darwin freebsd netbsd openbsd
-// Sockets for BSD variants
-
package net
import (
@@ -31,32 +29,3 @@ func maxListenerBacklog() int {
}
return int(n)
}
-
-func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) {
- a := toAddr(la)
- if a == nil {
- return la, nil
- }
- switch v := a.(type) {
- case *TCPAddr, *UnixAddr:
- err := setDefaultListenerSockopts(s)
- if err != nil {
- return nil, err
- }
- case *UDPAddr:
- if v.IP.IsMulticast() {
- err := setDefaultMulticastSockopts(s)
- if err != nil {
- return nil, err
- }
- switch f {
- case syscall.AF_INET:
- v.IP = IPv4zero
- case syscall.AF_INET6:
- v.IP = IPv6unspecified
- }
- return v.sockaddr(f)
- }
- }
- return la, nil
-}
diff --git a/src/pkg/net/sock_cloexec.go b/src/pkg/net/sock_cloexec.go
new file mode 100644
index 000000000..12d0f3488
--- /dev/null
+++ b/src/pkg/net/sock_cloexec.go
@@ -0,0 +1,69 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements sysSocket and accept for platforms that
+// provide a fast path for setting SetNonblock and CloseOnExec.
+
+// +build linux
+
+package net
+
+import "syscall"
+
+// Wrapper around the socket system call that marks the returned file
+// descriptor as nonblocking and close-on-exec.
+func sysSocket(f, t, p int) (int, error) {
+ s, err := syscall.Socket(f, t|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, p)
+ // The SOCK_NONBLOCK and SOCK_CLOEXEC flags were introduced in
+ // Linux 2.6.27. If we get an EINVAL error, fall back to
+ // using socket without them.
+ if err == nil || err != syscall.EINVAL {
+ return s, err
+ }
+
+ // See ../syscall/exec_unix.go for description of ForkLock.
+ syscall.ForkLock.RLock()
+ s, err = syscall.Socket(f, t, p)
+ if err == nil {
+ syscall.CloseOnExec(s)
+ }
+ syscall.ForkLock.RUnlock()
+ if err != nil {
+ return -1, err
+ }
+ if err = syscall.SetNonblock(s, true); err != nil {
+ syscall.Close(s)
+ return -1, err
+ }
+ return s, nil
+}
+
+// Wrapper around the accept system call that marks the returned file
+// descriptor as nonblocking and close-on-exec.
+func accept(fd int) (int, syscall.Sockaddr, error) {
+ nfd, sa, err := syscall.Accept4(fd, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
+ // The accept4 system call was introduced in Linux 2.6.28. If
+ // we get an ENOSYS error, fall back to using accept.
+ if err == nil || err != syscall.ENOSYS {
+ return nfd, sa, err
+ }
+
+ // See ../syscall/exec_unix.go for description of ForkLock.
+ // It is probably okay to hold the lock across syscall.Accept
+ // because we have put fd.sysfd into non-blocking mode.
+ // However, a call to the File method will put it back into
+ // blocking mode. We can't take that risk, so no use of ForkLock here.
+ nfd, sa, err = syscall.Accept(fd)
+ if err == nil {
+ syscall.CloseOnExec(nfd)
+ }
+ if err != nil {
+ return -1, nil, err
+ }
+ if err = syscall.SetNonblock(nfd, true); err != nil {
+ syscall.Close(nfd)
+ return -1, nil, err
+ }
+ return nfd, sa, nil
+}
diff --git a/src/pkg/net/sock_linux.go b/src/pkg/net/sock_linux.go
index e509d9397..8bbd74ddc 100644
--- a/src/pkg/net/sock_linux.go
+++ b/src/pkg/net/sock_linux.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Sockets for Linux
-
package net
import "syscall"
@@ -25,32 +23,3 @@ func maxListenerBacklog() int {
}
return n
}
-
-func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) {
- a := toAddr(la)
- if a == nil {
- return la, nil
- }
- switch v := a.(type) {
- case *TCPAddr, *UnixAddr:
- err := setDefaultListenerSockopts(s)
- if err != nil {
- return nil, err
- }
- case *UDPAddr:
- if v.IP.IsMulticast() {
- err := setDefaultMulticastSockopts(s)
- if err != nil {
- return nil, err
- }
- switch f {
- case syscall.AF_INET:
- v.IP = IPv4zero
- case syscall.AF_INET6:
- v.IP = IPv6unspecified
- }
- return v.sockaddr(f)
- }
- }
- return la, nil
-}
diff --git a/src/pkg/net/sock_posix.go b/src/pkg/net/sock_posix.go
new file mode 100644
index 000000000..b50a892b1
--- /dev/null
+++ b/src/pkg/net/sock_posix.go
@@ -0,0 +1,67 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd windows
+
+package net
+
+import (
+ "syscall"
+ "time"
+)
+
+var listenerBacklog = maxListenerBacklog()
+
+// Generic POSIX socket creation.
+func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
+ s, err := sysSocket(f, t, p)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = setDefaultSockopts(s, f, t, ipv6only); err != nil {
+ closesocket(s)
+ return nil, err
+ }
+
+ if ulsa != nil {
+ // We provide a socket that listens to a wildcard
+ // address with reusable UDP port when the given ulsa
+ // is an appropriate UDP multicast address prefix.
+ // This makes it possible for a single UDP listener
+ // to join multiple different group addresses, for
+ // multiple UDP listeners that listen on the same UDP
+ // port to join the same group address.
+ if ulsa, err = listenerSockaddr(s, f, ulsa, toAddr); err != nil {
+ closesocket(s)
+ return nil, err
+ }
+ if err = syscall.Bind(s, ulsa); err != nil {
+ closesocket(s)
+ return nil, err
+ }
+ }
+
+ if fd, err = newFD(s, f, t, net); err != nil {
+ closesocket(s)
+ return nil, err
+ }
+
+ if ursa != nil {
+ fd.wdeadline.setTime(deadline)
+ if err = fd.connect(ursa); err != nil {
+ closesocket(s)
+ return nil, err
+ }
+ fd.isConnected = true
+ fd.wdeadline.set(0)
+ }
+
+ lsa, _ := syscall.Getsockname(s)
+ laddr := toAddr(lsa)
+ rsa, _ := syscall.Getpeername(s)
+ raddr := toAddr(rsa)
+ fd.setAddr(laddr, raddr)
+ return fd, nil
+}
diff --git a/src/pkg/net/sock_unix.go b/src/pkg/net/sock_unix.go
new file mode 100644
index 000000000..b0d6d4900
--- /dev/null
+++ b/src/pkg/net/sock_unix.go
@@ -0,0 +1,36 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd
+
+package net
+
+import "syscall"
+
+func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) {
+ a := toAddr(la)
+ if a == nil {
+ return la, nil
+ }
+ switch a := a.(type) {
+ case *TCPAddr, *UnixAddr:
+ if err := setDefaultListenerSockopts(s); err != nil {
+ return nil, err
+ }
+ case *UDPAddr:
+ if a.IP.IsMulticast() {
+ if err := setDefaultMulticastSockopts(s); err != nil {
+ return nil, err
+ }
+ switch f {
+ case syscall.AF_INET:
+ a.IP = IPv4zero
+ case syscall.AF_INET6:
+ a.IP = IPv6unspecified
+ }
+ return a.sockaddr(f)
+ }
+ }
+ return la, nil
+}
diff --git a/src/pkg/net/sock_windows.go b/src/pkg/net/sock_windows.go
index cce6181c9..a77c48437 100644
--- a/src/pkg/net/sock_windows.go
+++ b/src/pkg/net/sock_windows.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Sockets for Windows
-
package net
import "syscall"
@@ -18,26 +16,35 @@ func listenerSockaddr(s syscall.Handle, f int, la syscall.Sockaddr, toAddr func(
if a == nil {
return la, nil
}
- switch v := a.(type) {
+ switch a := a.(type) {
case *TCPAddr, *UnixAddr:
- err := setDefaultListenerSockopts(s)
- if err != nil {
+ if err := setDefaultListenerSockopts(s); err != nil {
return nil, err
}
case *UDPAddr:
- if v.IP.IsMulticast() {
- err := setDefaultMulticastSockopts(s)
- if err != nil {
+ if a.IP.IsMulticast() {
+ if err := setDefaultMulticastSockopts(s); err != nil {
return nil, err
}
switch f {
case syscall.AF_INET:
- v.IP = IPv4zero
+ a.IP = IPv4zero
case syscall.AF_INET6:
- v.IP = IPv6unspecified
+ a.IP = IPv6unspecified
}
- return v.sockaddr(f)
+ return a.sockaddr(f)
}
}
return la, nil
}
+
+func sysSocket(f, t, p int) (syscall.Handle, error) {
+ // See ../syscall/exec_unix.go for description of ForkLock.
+ syscall.ForkLock.RLock()
+ s, err := syscall.Socket(f, t, p)
+ if err == nil {
+ syscall.CloseOnExec(s)
+ }
+ syscall.ForkLock.RUnlock()
+ return s, err
+}
diff --git a/src/pkg/net/sockopt.go b/src/pkg/net/sockopt_posix.go
index 0cd19266f..fe371fe0c 100644
--- a/src/pkg/net/sockopt.go
+++ b/src/pkg/net/sockopt_posix.go
@@ -119,45 +119,22 @@ func setWriteBuffer(fd *netFD, bytes int) error {
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes))
}
+// TODO(dfc) these unused error returns could be removed
+
func setReadDeadline(fd *netFD, t time.Time) error {
- if t.IsZero() {
- fd.rdeadline = 0
- } else {
- fd.rdeadline = t.UnixNano()
- }
+ fd.rdeadline.setTime(t)
return nil
}
func setWriteDeadline(fd *netFD, t time.Time) error {
- if t.IsZero() {
- fd.wdeadline = 0
- } else {
- fd.wdeadline = t.UnixNano()
- }
+ fd.wdeadline.setTime(t)
return nil
}
func setDeadline(fd *netFD, t time.Time) error {
- if err := setReadDeadline(fd, t); err != nil {
- return err
- }
- return setWriteDeadline(fd, t)
-}
-
-func setReuseAddr(fd *netFD, reuse bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse)))
-}
-
-func setDontRoute(fd *netFD, dontroute bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute)))
+ setReadDeadline(fd, t)
+ setWriteDeadline(fd, t)
+ return nil
}
func setKeepAlive(fd *netFD, keepalive bool) error {
diff --git a/src/pkg/net/sockoptip.go b/src/pkg/net/sockoptip.go
deleted file mode 100644
index 1fcad4018..000000000
--- a/src/pkg/net/sockoptip.go
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build darwin freebsd linux netbsd openbsd windows
-
-// IP-level socket options
-
-package net
-
-import (
- "os"
- "syscall"
-)
-
-func ipv4TOS(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TOS)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv4TOS(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TOS, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4TTL(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TTL)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv4TTL(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_TTL, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error {
- mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
- if err := setIPv4MreqToInterface(mreq, ifi); err != nil {
- return err
- }
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq))
-}
-
-func leaveIPv4Group(fd *netFD, ifi *Interface, ip IP) error {
- mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
- if err := setIPv4MreqToInterface(mreq, ifi); err != nil {
- return err
- }
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_DROP_MEMBERSHIP, mreq))
-}
-
-func ipv6HopLimit(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv6HopLimit(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv6MulticastInterface(fd *netFD) (*Interface, error) {
- if err := fd.incref(false); err != nil {
- return nil, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- if v == 0 {
- return nil, nil
- }
- ifi, err := InterfaceByIndex(v)
- if err != nil {
- return nil, err
- }
- return ifi, nil
-}
-
-func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error {
- var v int
- if ifi != nil {
- v = ifi.Index
- }
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv6MulticastHopLimit(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_HOPS)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv6MulticastHopLimit(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_HOPS, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv6MulticastLoopback(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv6MulticastLoopback(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error {
- mreq := &syscall.IPv6Mreq{}
- copy(mreq.Multiaddr[:], ip)
- if ifi != nil {
- mreq.Interface = uint32(ifi.Index)
- }
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq))
-}
-
-func leaveIPv6Group(fd *netFD, ifi *Interface, ip IP) error {
- mreq := &syscall.IPv6Mreq{}
- copy(mreq.Multiaddr[:], ip)
- if ifi != nil {
- mreq.Interface = uint32(ifi.Index)
- }
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_LEAVE_GROUP, mreq))
-}
diff --git a/src/pkg/net/sockoptip_bsd.go b/src/pkg/net/sockoptip_bsd.go
index 19e2b142e..263f85521 100644
--- a/src/pkg/net/sockoptip_bsd.go
+++ b/src/pkg/net/sockoptip_bsd.go
@@ -4,8 +4,6 @@
// +build darwin freebsd netbsd openbsd
-// IP-level socket options for BSD variants
-
package net
import (
@@ -13,48 +11,30 @@ import (
"syscall"
)
-func ipv4MulticastTTL(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL)
+func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
+ ip, err := interfaceToIPv4Addr(ifi)
if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
+ return os.NewSyscallError("setsockopt", err)
}
- return int(v), nil
-}
-
-func setIPv4MulticastTTL(fd *netFD, v int) error {
+ var a [4]byte
+ copy(a[:], ip.To4())
if err := fd.incref(false); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, byte(v))
+ err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, a)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
-func ipv6TrafficClass(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv6TrafficClass(fd *netFD, v int) error {
+func setIPv4MulticastLoopback(fd *netFD, v bool) error {
if err := fd.incref(false); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, v)
+ err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v)))
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
diff --git a/src/pkg/net/sockoptip_darwin.go b/src/pkg/net/sockoptip_darwin.go
deleted file mode 100644
index 52b237c4b..000000000
--- a/src/pkg/net/sockoptip_darwin.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// IP-level socket options for Darwin
-
-package net
-
-import (
- "os"
- "syscall"
-)
-
-func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
- if err := fd.incref(false); err != nil {
- return nil, err
- }
- defer fd.decref()
- a, err := syscall.GetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- return ipv4AddrToInterface(IPv4(a[0], a[1], a[2], a[3]))
-}
-
-func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
- ip, err := interfaceToIPv4Addr(ifi)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- var x [4]byte
- copy(x[:], ip.To4())
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4MulticastLoopback(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4MulticastLoopback(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4ReceiveInterface(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4ReceiveInterface(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
diff --git a/src/pkg/net/sockoptip_freebsd.go b/src/pkg/net/sockoptip_freebsd.go
deleted file mode 100644
index 4a3bc2e82..000000000
--- a/src/pkg/net/sockoptip_freebsd.go
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// IP-level socket options for FreeBSD
-
-package net
-
-import (
- "os"
- "syscall"
-)
-
-func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
- if err := fd.incref(false); err != nil {
- return nil, err
- }
- defer fd.decref()
- mreq, err := syscall.GetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- if int(mreq.Ifindex) == 0 {
- return nil, nil
- }
- return InterfaceByIndex(int(mreq.Ifindex))
-}
-
-func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
- var v int32
- if ifi != nil {
- v = int32(ifi.Index)
- }
- mreq := &syscall.IPMreqn{Ifindex: v}
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4MulticastLoopback(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4MulticastLoopback(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4ReceiveInterface(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4ReceiveInterface(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
diff --git a/src/pkg/net/sockoptip_linux.go b/src/pkg/net/sockoptip_linux.go
index 169718f14..225fb0c4c 100644
--- a/src/pkg/net/sockoptip_linux.go
+++ b/src/pkg/net/sockoptip_linux.go
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// IP-level socket options for Linux
-
package net
import (
@@ -11,21 +9,6 @@ import (
"syscall"
)
-func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
- if err := fd.incref(false); err != nil {
- return nil, err
- }
- defer fd.decref()
- mreq, err := syscall.GetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- if int(mreq.Ifindex) == 0 {
- return nil, nil
- }
- return InterfaceByIndex(int(mreq.Ifindex))
-}
-
func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
var v int32
if ifi != nil {
@@ -43,42 +26,6 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
return nil
}
-func ipv4MulticastTTL(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL)
- if err != nil {
- return -1, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv4MulticastTTL(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4MulticastLoopback(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
func setIPv4MulticastLoopback(fd *netFD, v bool) error {
if err := fd.incref(false); err != nil {
return err
@@ -90,51 +37,3 @@ func setIPv4MulticastLoopback(fd *netFD, v bool) error {
}
return nil
}
-
-func ipv4ReceiveInterface(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_PKTINFO)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4ReceiveInterface(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_PKTINFO, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv6TrafficClass(fd *netFD) (int, error) {
- if err := fd.incref(false); err != nil {
- return 0, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS)
- if err != nil {
- return 0, os.NewSyscallError("getsockopt", err)
- }
- return v, nil
-}
-
-func setIPv6TrafficClass(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
diff --git a/src/pkg/net/sockoptip_netbsd.go b/src/pkg/net/sockoptip_netbsd.go
deleted file mode 100644
index 446d92aa3..000000000
--- a/src/pkg/net/sockoptip_netbsd.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// IP-level socket options for NetBSD
-
-package net
-
-import "syscall"
-
-func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
- // TODO: Implement this
- return nil, syscall.EAFNOSUPPORT
-}
-
-func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
- // TODO: Implement this
- return syscall.EAFNOSUPPORT
-}
-
-func ipv4MulticastLoopback(fd *netFD) (bool, error) {
- // TODO: Implement this
- return false, syscall.EAFNOSUPPORT
-}
-
-func setIPv4MulticastLoopback(fd *netFD, v bool) error {
- // TODO: Implement this
- return syscall.EAFNOSUPPORT
-}
-
-func ipv4ReceiveInterface(fd *netFD) (bool, error) {
- // TODO: Implement this
- return false, syscall.EAFNOSUPPORT
-}
-
-func setIPv4ReceiveInterface(fd *netFD, v bool) error {
- // TODO: Implement this
- return syscall.EAFNOSUPPORT
-}
diff --git a/src/pkg/net/sockoptip_openbsd.go b/src/pkg/net/sockoptip_openbsd.go
deleted file mode 100644
index f3e42f1a9..000000000
--- a/src/pkg/net/sockoptip_openbsd.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// IP-level socket options for OpenBSD
-
-package net
-
-import (
- "os"
- "syscall"
-)
-
-func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
- if err := fd.incref(false); err != nil {
- return nil, err
- }
- defer fd.decref()
- a, err := syscall.GetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- return ipv4AddrToInterface(IPv4(a[0], a[1], a[2], a[3]))
-}
-
-func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
- ip, err := interfaceToIPv4Addr(ifi)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- var x [4]byte
- copy(x[:], ip.To4())
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4MulticastLoopback(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4MulticastLoopback(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v)))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
-
-func ipv4ReceiveInterface(fd *netFD) (bool, error) {
- if err := fd.incref(false); err != nil {
- return false, err
- }
- defer fd.decref()
- v, err := syscall.GetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF)
- if err != nil {
- return false, os.NewSyscallError("getsockopt", err)
- }
- return v == 1, nil
-}
-
-func setIPv4ReceiveInterface(fd *netFD, v bool) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_RECVIF, boolint(v))
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-}
diff --git a/src/pkg/net/sockoptip_posix.go b/src/pkg/net/sockoptip_posix.go
new file mode 100644
index 000000000..e4c56a0e4
--- /dev/null
+++ b/src/pkg/net/sockoptip_posix.go
@@ -0,0 +1,73 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd windows
+
+package net
+
+import (
+ "os"
+ "syscall"
+)
+
+func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error {
+ mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}}
+ if err := setIPv4MreqToInterface(mreq, ifi); err != nil {
+ return err
+ }
+ if err := fd.incref(false); err != nil {
+ return err
+ }
+ defer fd.decref()
+ err := syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)
+ if err != nil {
+ return os.NewSyscallError("setsockopt", err)
+ }
+ return nil
+}
+
+func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error {
+ var v int
+ if ifi != nil {
+ v = ifi.Index
+ }
+ if err := fd.incref(false); err != nil {
+ return err
+ }
+ defer fd.decref()
+ err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v)
+ if err != nil {
+ return os.NewSyscallError("setsockopt", err)
+ }
+ return nil
+}
+
+func setIPv6MulticastLoopback(fd *netFD, v bool) error {
+ if err := fd.incref(false); err != nil {
+ return err
+ }
+ defer fd.decref()
+ err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v))
+ if err != nil {
+ return os.NewSyscallError("setsockopt", err)
+ }
+ return nil
+}
+
+func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error {
+ mreq := &syscall.IPv6Mreq{}
+ copy(mreq.Multiaddr[:], ip)
+ if ifi != nil {
+ mreq.Interface = uint32(ifi.Index)
+ }
+ if err := fd.incref(false); err != nil {
+ return err
+ }
+ defer fd.decref()
+ err := syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq)
+ if err != nil {
+ return os.NewSyscallError("setsockopt", err)
+ }
+ return nil
+}
diff --git a/src/pkg/net/sockoptip_windows.go b/src/pkg/net/sockoptip_windows.go
index b9db3334d..3e248441a 100644
--- a/src/pkg/net/sockoptip_windows.go
+++ b/src/pkg/net/sockoptip_windows.go
@@ -2,90 +2,41 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// IP-level socket options for Windows
-
package net
import (
"os"
"syscall"
+ "unsafe"
)
-func ipv4MulticastInterface(fd *netFD) (*Interface, error) {
- // TODO: Implement this
- return nil, syscall.EWINDOWS
-}
-
func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
ip, err := interfaceToIPv4Addr(ifi)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
- var x [4]byte
- copy(x[:], ip.To4())
+ var a [4]byte
+ copy(a[:], ip.To4())
if err := fd.incref(false); err != nil {
return err
}
defer fd.decref()
- err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, x)
+ err = syscall.Setsockopt(fd.sysfd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_IF), (*byte)(unsafe.Pointer(&a[0])), 4)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
-func ipv4MulticastTTL(fd *netFD) (int, error) {
- // TODO: Implement this
- return -1, syscall.EWINDOWS
-}
-
-func setIPv4MulticastTTL(fd *netFD, v int) error {
- if err := fd.incref(false); err != nil {
- return err
- }
- defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, v)
- if err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- return nil
-
-}
-
-func ipv4MulticastLoopback(fd *netFD) (bool, error) {
- // TODO: Implement this
- return false, syscall.EWINDOWS
-}
-
func setIPv4MulticastLoopback(fd *netFD, v bool) error {
if err := fd.incref(false); err != nil {
return err
}
defer fd.decref()
- err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))
+ vv := int32(boolint(v))
+ err := syscall.Setsockopt(fd.sysfd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_LOOP), (*byte)(unsafe.Pointer(&vv)), 4)
if err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
-
-}
-
-func ipv4ReceiveInterface(fd *netFD) (bool, error) {
- // TODO: Implement this
- return false, syscall.EWINDOWS
-}
-
-func setIPv4ReceiveInterface(fd *netFD, v bool) error {
- // TODO: Implement this
- return syscall.EWINDOWS
-}
-
-func ipv6TrafficClass(fd *netFD) (int, error) {
- // TODO: Implement this
- return 0, syscall.EWINDOWS
-}
-
-func setIPv6TrafficClass(fd *netFD, v int) error {
- // TODO: Implement this
- return syscall.EWINDOWS
}
diff --git a/src/pkg/net/sys_cloexec.go b/src/pkg/net/sys_cloexec.go
new file mode 100644
index 000000000..17e874908
--- /dev/null
+++ b/src/pkg/net/sys_cloexec.go
@@ -0,0 +1,54 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements sysSocket and accept for platforms that do not
+// provide a fast path for setting SetNonblock and CloseOnExec.
+
+// +build darwin freebsd netbsd openbsd
+
+package net
+
+import "syscall"
+
+// Wrapper around the socket system call that marks the returned file
+// descriptor as nonblocking and close-on-exec.
+func sysSocket(f, t, p int) (int, error) {
+ // See ../syscall/exec_unix.go for description of ForkLock.
+ syscall.ForkLock.RLock()
+ s, err := syscall.Socket(f, t, p)
+ if err == nil {
+ syscall.CloseOnExec(s)
+ }
+ syscall.ForkLock.RUnlock()
+ if err != nil {
+ return -1, err
+ }
+ if err = syscall.SetNonblock(s, true); err != nil {
+ syscall.Close(s)
+ return -1, err
+ }
+ return s, nil
+}
+
+// Wrapper around the accept system call that marks the returned file
+// descriptor as nonblocking and close-on-exec.
+func accept(fd int) (int, syscall.Sockaddr, error) {
+ // See ../syscall/exec_unix.go for description of ForkLock.
+ // It is probably okay to hold the lock across syscall.Accept
+ // because we have put fd.sysfd into non-blocking mode.
+ // However, a call to the File method will put it back into
+ // blocking mode. We can't take that risk, so no use of ForkLock here.
+ nfd, sa, err := syscall.Accept(fd)
+ if err == nil {
+ syscall.CloseOnExec(nfd)
+ }
+ if err != nil {
+ return -1, nil, err
+ }
+ if err = syscall.SetNonblock(nfd, true); err != nil {
+ syscall.Close(nfd)
+ return -1, nil, err
+ }
+ return nfd, sa, nil
+}
diff --git a/src/pkg/net/tcp_test.go b/src/pkg/net/tcp_test.go
new file mode 100644
index 000000000..6c4485a94
--- /dev/null
+++ b/src/pkg/net/tcp_test.go
@@ -0,0 +1,206 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "reflect"
+ "runtime"
+ "testing"
+ "time"
+)
+
+func BenchmarkTCP4OneShot(b *testing.B) {
+ benchmarkTCP(b, false, false, "127.0.0.1:0")
+}
+
+func BenchmarkTCP4OneShotTimeout(b *testing.B) {
+ benchmarkTCP(b, false, true, "127.0.0.1:0")
+}
+
+func BenchmarkTCP4Persistent(b *testing.B) {
+ benchmarkTCP(b, true, false, "127.0.0.1:0")
+}
+
+func BenchmarkTCP4PersistentTimeout(b *testing.B) {
+ benchmarkTCP(b, true, true, "127.0.0.1:0")
+}
+
+func BenchmarkTCP6OneShot(b *testing.B) {
+ if !supportsIPv6 {
+ b.Skip("ipv6 is not supported")
+ }
+ benchmarkTCP(b, false, false, "[::1]:0")
+}
+
+func BenchmarkTCP6OneShotTimeout(b *testing.B) {
+ if !supportsIPv6 {
+ b.Skip("ipv6 is not supported")
+ }
+ benchmarkTCP(b, false, true, "[::1]:0")
+}
+
+func BenchmarkTCP6Persistent(b *testing.B) {
+ if !supportsIPv6 {
+ b.Skip("ipv6 is not supported")
+ }
+ benchmarkTCP(b, true, false, "[::1]:0")
+}
+
+func BenchmarkTCP6PersistentTimeout(b *testing.B) {
+ if !supportsIPv6 {
+ b.Skip("ipv6 is not supported")
+ }
+ benchmarkTCP(b, true, true, "[::1]:0")
+}
+
+func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
+ const msgLen = 512
+ conns := b.N
+ numConcurrent := runtime.GOMAXPROCS(-1) * 16
+ msgs := 1
+ if persistent {
+ conns = numConcurrent
+ msgs = b.N / conns
+ if msgs == 0 {
+ msgs = 1
+ }
+ if conns > b.N {
+ conns = b.N
+ }
+ }
+ sendMsg := func(c Conn, buf []byte) bool {
+ n, err := c.Write(buf)
+ if n != len(buf) || err != nil {
+ b.Logf("Write failed: %v", err)
+ return false
+ }
+ return true
+ }
+ recvMsg := func(c Conn, buf []byte) bool {
+ for read := 0; read != len(buf); {
+ n, err := c.Read(buf)
+ read += n
+ if err != nil {
+ b.Logf("Read failed: %v", err)
+ return false
+ }
+ }
+ return true
+ }
+ ln, err := Listen("tcp", laddr)
+ if err != nil {
+ b.Fatalf("Listen failed: %v", err)
+ }
+ defer ln.Close()
+ // Acceptor.
+ go func() {
+ for {
+ c, err := ln.Accept()
+ if err != nil {
+ break
+ }
+ // Server connection.
+ go func(c Conn) {
+ defer c.Close()
+ if timeout {
+ c.SetDeadline(time.Now().Add(time.Hour)) // Not intended to fire.
+ }
+ var buf [msgLen]byte
+ for m := 0; m < msgs; m++ {
+ if !recvMsg(c, buf[:]) || !sendMsg(c, buf[:]) {
+ break
+ }
+ }
+ }(c)
+ }
+ }()
+ sem := make(chan bool, numConcurrent)
+ for i := 0; i < conns; i++ {
+ sem <- true
+ // Client connection.
+ go func() {
+ defer func() {
+ <-sem
+ }()
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ b.Logf("Dial failed: %v", err)
+ return
+ }
+ defer c.Close()
+ if timeout {
+ c.SetDeadline(time.Now().Add(time.Hour)) // Not intended to fire.
+ }
+ var buf [msgLen]byte
+ for m := 0; m < msgs; m++ {
+ if !sendMsg(c, buf[:]) || !recvMsg(c, buf[:]) {
+ break
+ }
+ }
+ }()
+ }
+ for i := 0; i < cap(sem); i++ {
+ sem <- true
+ }
+}
+
+var resolveTCPAddrTests = []struct {
+ net string
+ litAddr string
+ addr *TCPAddr
+ err error
+}{
+ {"tcp", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil},
+ {"tcp4", "127.0.0.1:65535", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil},
+
+ {"tcp", "[::1]:1", &TCPAddr{IP: ParseIP("::1"), Port: 1}, nil},
+ {"tcp6", "[::1]:65534", &TCPAddr{IP: ParseIP("::1"), Port: 65534}, nil},
+
+ {"", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
+ {"", "[::1]:0", &TCPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior
+
+ {"http", "127.0.0.1:0", nil, UnknownNetworkError("http")},
+}
+
+func TestResolveTCPAddr(t *testing.T) {
+ for _, tt := range resolveTCPAddrTests {
+ addr, err := ResolveTCPAddr(tt.net, tt.litAddr)
+ if err != tt.err {
+ t.Fatalf("ResolveTCPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
+ }
+ if !reflect.DeepEqual(addr, tt.addr) {
+ t.Fatalf("got %#v; expected %#v", addr, tt.addr)
+ }
+ }
+}
+
+var tcpListenerNameTests = []struct {
+ net string
+ laddr *TCPAddr
+}{
+ {"tcp4", &TCPAddr{IP: IPv4(127, 0, 0, 1)}},
+ {"tcp4", &TCPAddr{}},
+ {"tcp4", nil},
+}
+
+func TestTCPListenerName(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+
+ for _, tt := range tcpListenerNameTests {
+ ln, err := ListenTCP(tt.net, tt.laddr)
+ if err != nil {
+ t.Errorf("ListenTCP failed: %v", err)
+ return
+ }
+ defer ln.Close()
+ la := ln.Addr()
+ if a, ok := la.(*TCPAddr); !ok || a.Port == 0 {
+ t.Errorf("got %v; expected a proper address with non-zero port number", la)
+ return
+ }
+ }
+}
diff --git a/src/pkg/net/tcpsock.go b/src/pkg/net/tcpsock.go
index 47fbf2919..d5158b22d 100644
--- a/src/pkg/net/tcpsock.go
+++ b/src/pkg/net/tcpsock.go
@@ -10,6 +10,7 @@ package net
type TCPAddr struct {
IP IP
Port int
+ Zone string // IPv6 scoped addressing zone
}
// Network returns the address's network name, "tcp".
@@ -28,9 +29,16 @@ func (a *TCPAddr) String() string {
// "tcp4" or "tcp6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]:80".
func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
- ip, port, err := hostPortToIP(net, addr)
+ switch net {
+ case "tcp", "tcp4", "tcp6":
+ case "": // a hint wildcard for Go 1.0 undocumented behavior
+ net = "tcp"
+ default:
+ return nil, UnknownNetworkError(net)
+ }
+ a, err := resolveInternetAddr(net, addr, noDeadline)
if err != nil {
return nil, err
}
- return &TCPAddr{ip, port}, nil
+ return a.(*TCPAddr), nil
}
diff --git a/src/pkg/net/tcpsock_plan9.go b/src/pkg/net/tcpsock_plan9.go
index 35f56966e..ed3664603 100644
--- a/src/pkg/net/tcpsock_plan9.go
+++ b/src/pkg/net/tcpsock_plan9.go
@@ -2,34 +2,30 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// TCP for Plan 9
+// TCP sockets for Plan 9
package net
import (
+ "io"
+ "os"
"syscall"
"time"
)
-// TCPConn is an implementation of the Conn interface
-// for TCP network connections.
+// TCPConn is an implementation of the Conn interface for TCP network
+// connections.
type TCPConn struct {
- plan9Conn
+ conn
}
-// SetDeadline implements the Conn SetDeadline method.
-func (c *TCPConn) SetDeadline(t time.Time) error {
- return syscall.EPLAN9
+func newTCPConn(fd *netFD) *TCPConn {
+ return &TCPConn{conn{fd}}
}
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *TCPConn) SetReadDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *TCPConn) SetWriteDeadline(t time.Time) error {
- return syscall.EPLAN9
+// ReadFrom implements the io.ReaderFrom ReadFrom method.
+func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
+ return genericReadFrom(c, r)
}
// CloseRead shuts down the reading side of the TCP connection.
@@ -38,7 +34,7 @@ func (c *TCPConn) CloseRead() error {
if !c.ok() {
return syscall.EINVAL
}
- return syscall.EPLAN9
+ return c.fd.CloseRead()
}
// CloseWrite shuts down the writing side of the TCP connection.
@@ -47,51 +43,142 @@ func (c *TCPConn) CloseWrite() error {
if !c.ok() {
return syscall.EINVAL
}
+ return c.fd.CloseWrite()
+}
+
+// SetLinger sets the behavior of Close() on a connection which still
+// has data waiting to be sent or to be acknowledged.
+//
+// If sec < 0 (the default), Close returns immediately and the
+// operating system finishes sending the data in the background.
+//
+// If sec == 0, Close returns immediately and the operating system
+// discards any unsent or unacknowledged data.
+//
+// If sec > 0, Close blocks for at most sec seconds waiting for data
+// to be sent and acknowledged.
+func (c *TCPConn) SetLinger(sec int) error {
+ return syscall.EPLAN9
+}
+
+// SetKeepAlive sets whether the operating system should send
+// keepalive messages on the connection.
+func (c *TCPConn) SetKeepAlive(keepalive bool) error {
+ return syscall.EPLAN9
+}
+
+// SetNoDelay controls whether the operating system should delay
+// packet transmission in hopes of sending fewer packets (Nagle's
+// algorithm). The default is true (no delay), meaning that data is
+// sent as soon as possible after a Write.
+func (c *TCPConn) SetNoDelay(noDelay bool) error {
return syscall.EPLAN9
}
// DialTCP connects to the remote address raddr on the network net,
-// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
-// as the local address for the connection.
-func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err error) {
+// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is
+// used as the local address for the connection.
+func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
+ return dialTCP(net, laddr, raddr, noDeadline)
+}
+
+func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
+ if !deadline.IsZero() {
+ panic("net.dialTCP: deadline not implemented on Plan 9")
+ }
switch net {
case "tcp", "tcp4", "tcp6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, &OpError{"dial", net, raddr, UnknownNetworkError(net)}
}
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
- c1, err := dialPlan9(net, laddr, raddr)
+ fd, err := dialPlan9(net, laddr, raddr)
if err != nil {
- return
+ return nil, err
}
- return &TCPConn{*c1}, nil
+ return newTCPConn(fd), nil
}
-// TCPListener is a TCP network listener.
-// Clients should typically use variables of type Listener
-// instead of assuming TCP.
+// TCPListener is a TCP network listener. Clients should typically
+// use variables of type Listener instead of assuming TCP.
type TCPListener struct {
- plan9Listener
+ fd *netFD
+}
+
+// AcceptTCP accepts the next incoming call and returns the new
+// connection and the remote address.
+func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
+ if l == nil || l.fd == nil || l.fd.ctl == nil {
+ return nil, syscall.EINVAL
+ }
+ fd, err := l.fd.acceptPlan9()
+ if err != nil {
+ return nil, err
+ }
+ return newTCPConn(fd), nil
+}
+
+// Accept implements the Accept method in the Listener interface; it
+// waits for the next call and returns a generic Conn.
+func (l *TCPListener) Accept() (Conn, error) {
+ if l == nil || l.fd == nil || l.fd.ctl == nil {
+ return nil, syscall.EINVAL
+ }
+ c, err := l.AcceptTCP()
+ if err != nil {
+ return nil, err
+ }
+ return c, nil
}
-// ListenTCP announces on the TCP address laddr and returns a TCP listener.
-// Net must be "tcp", "tcp4", or "tcp6".
-// If laddr has a port of 0, it means to listen on some available port.
-// The caller can use l.Addr() to retrieve the chosen address.
-func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err error) {
+// Close stops listening on the TCP address.
+// Already Accepted connections are not closed.
+func (l *TCPListener) Close() error {
+ if l == nil || l.fd == nil || l.fd.ctl == nil {
+ return syscall.EINVAL
+ }
+ if _, err := l.fd.ctl.WriteString("hangup"); err != nil {
+ l.fd.ctl.Close()
+ return &OpError{"close", l.fd.ctl.Name(), l.fd.laddr, err}
+ }
+ return l.fd.ctl.Close()
+}
+
+// Addr returns the listener's network address, a *TCPAddr.
+func (l *TCPListener) Addr() Addr { return l.fd.laddr }
+
+// SetDeadline sets the deadline associated with the listener.
+// A zero time value disables the deadline.
+func (l *TCPListener) SetDeadline(t time.Time) error {
+ if l == nil || l.fd == nil || l.fd.ctl == nil {
+ return syscall.EINVAL
+ }
+ return setDeadline(l.fd, t)
+}
+
+// File returns a copy of the underlying os.File, set to blocking
+// mode. It is the caller's responsibility to close f when finished.
+// Closing l does not affect f, and closing f does not affect l.
+func (l *TCPListener) File() (f *os.File, err error) { return l.dup() }
+
+// ListenTCP announces on the TCP address laddr and returns a TCP
+// listener. Net must be "tcp", "tcp4", or "tcp6". If laddr has a
+// port of 0, it means to listen on some available port. The caller
+// can use l.Addr() to retrieve the chosen address.
+func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
switch net {
case "tcp", "tcp4", "tcp6":
default:
- return nil, UnknownNetworkError(net)
+ return nil, &OpError{"listen", net, laddr, UnknownNetworkError(net)}
}
if laddr == nil {
- return nil, &OpError{"listen", net, nil, errMissingAddress}
+ laddr = &TCPAddr{}
}
- l1, err := listenPlan9(net, laddr)
+ fd, err := listenPlan9(net, laddr)
if err != nil {
- return
+ return nil, err
}
- return &TCPListener{*l1}, nil
+ return &TCPListener{fd}, nil
}
diff --git a/src/pkg/net/tcpsock_posix.go b/src/pkg/net/tcpsock_posix.go
index e6b1937fb..bd5a2a287 100644
--- a/src/pkg/net/tcpsock_posix.go
+++ b/src/pkg/net/tcpsock_posix.go
@@ -23,14 +23,9 @@ import (
func sockaddrToTCP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
- return &TCPAddr{sa.Addr[0:], sa.Port}
+ return &TCPAddr{IP: sa.Addr[0:], Port: sa.Port}
case *syscall.SockaddrInet6:
- return &TCPAddr{sa.Addr[0:], sa.Port}
- default:
- if sa != nil {
- // Diagnose when we will turn a non-nil sockaddr into a nil.
- panic("unexpected type in sockaddrToTCP")
- }
+ return &TCPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
}
return nil
}
@@ -53,7 +48,7 @@ func (a *TCPAddr) isWildcard() bool {
}
func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
- return ipToSockaddr(family, a.IP, a.Port)
+ return ipToSockaddr(family, a.IP, a.Port, a.Zone)
}
func (a *TCPAddr) toAddr() sockaddr {
@@ -66,27 +61,15 @@ func (a *TCPAddr) toAddr() sockaddr {
// TCPConn is an implementation of the Conn interface
// for TCP network connections.
type TCPConn struct {
- fd *netFD
+ conn
}
func newTCPConn(fd *netFD) *TCPConn {
- c := &TCPConn{fd}
+ c := &TCPConn{conn{fd}}
c.SetNoDelay(true)
return c
}
-func (c *TCPConn) ok() bool { return c != nil && c.fd != nil }
-
-// Implementation of the Conn interface - see Conn for documentation.
-
-// Read implements the Conn Read method.
-func (c *TCPConn) Read(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Read(b)
-}
-
// ReadFrom implements the io.ReaderFrom ReadFrom method.
func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
if n, err, handled := sendFile(c.fd, r); handled {
@@ -95,22 +78,6 @@ func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
return genericReadFrom(c, r)
}
-// Write implements the Conn Write method.
-func (c *TCPConn) Write(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Write(b)
-}
-
-// Close closes the TCP connection.
-func (c *TCPConn) Close() error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return c.fd.Close()
-}
-
// CloseRead shuts down the reading side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseRead() error {
@@ -129,64 +96,6 @@ func (c *TCPConn) CloseWrite() error {
return c.fd.CloseWrite()
}
-// LocalAddr returns the local network address, a *TCPAddr.
-func (c *TCPConn) LocalAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.laddr
-}
-
-// RemoteAddr returns the remote network address, a *TCPAddr.
-func (c *TCPConn) RemoteAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.raddr
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *TCPConn) SetDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setDeadline(c.fd, t)
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *TCPConn) SetReadDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadDeadline(c.fd, t)
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *TCPConn) SetWriteDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteDeadline(c.fd, t)
-}
-
-// SetReadBuffer sets the size of the operating system's
-// receive buffer associated with the connection.
-func (c *TCPConn) SetReadBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadBuffer(c.fd, bytes)
-}
-
-// SetWriteBuffer sets the size of the operating system's
-// transmit buffer associated with the connection.
-func (c *TCPConn) SetWriteBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteBuffer(c.fd, bytes)
-}
-
// SetLinger sets the behavior of Close() on a connection
// which still has data waiting to be sent or to be acknowledged.
//
@@ -225,20 +134,23 @@ func (c *TCPConn) SetNoDelay(noDelay bool) error {
return setNoDelay(c.fd, noDelay)
}
-// File returns a copy of the underlying os.File, set to blocking mode.
-// It is the caller's responsibility to close f when finished.
-// Closing c does not affect f, and closing f does not affect c.
-func (c *TCPConn) File() (f *os.File, err error) { return c.fd.dup() }
-
// DialTCP connects to the remote address raddr on the network net,
// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
// as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
+ switch net {
+ case "tcp", "tcp4", "tcp6":
+ default:
+ return nil, UnknownNetworkError(net)
+ }
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
+ return dialTCP(net, laddr, raddr, noDeadline)
+}
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
+func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
+ fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
// TCP has a rarely used mechanism called a 'simultaneous connection' in
// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
@@ -257,9 +169,18 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
// use the result. See also:
// http://golang.org/issue/2690
// http://stackoverflow.com/questions/4949858/
- for i := 0; i < 2 && err == nil && laddr == nil && selfConnect(fd); i++ {
- fd.Close()
- fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
+ //
+ // The opposite can also happen: if we ask the kernel to pick an appropriate
+ // originating local address, sometimes it picks one that is already in use.
+ // So if the error is EADDRNOTAVAIL, we have to try again too, just for
+ // a different reason.
+ //
+ // The kernel socket code is no doubt enjoying watching us squirm.
+ for i := 0; i < 2 && (laddr == nil || laddr.Port == 0) && (selfConnect(fd, err) || spuriousENOTAVAIL(err)); i++ {
+ if err == nil {
+ fd.Close()
+ }
+ fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
}
if err != nil {
@@ -268,7 +189,12 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
return newTCPConn(fd), nil
}
-func selfConnect(fd *netFD) bool {
+func selfConnect(fd *netFD, err error) bool {
+ // If the connect failed, we clearly didn't connect to ourselves.
+ if err != nil {
+ return false
+ }
+
// The socket constructor can return an fd with raddr nil under certain
// unknown conditions. The errors in the calls there to Getpeername
// are discarded, but we can't catch the problem there because those
@@ -285,6 +211,11 @@ func selfConnect(fd *netFD) bool {
return l.Port == r.Port && l.IP.Equal(r.IP)
}
+func spuriousENOTAVAIL(err error) bool {
+ e, ok := err.(*OpError)
+ return ok && e.Err == syscall.EADDRNOTAVAIL
+}
+
// TCPListener is a TCP network listener.
// Clients should typically use variables of type Listener
// instead of assuming TCP.
@@ -292,29 +223,10 @@ type TCPListener struct {
fd *netFD
}
-// ListenTCP announces on the TCP address laddr and returns a TCP listener.
-// Net must be "tcp", "tcp4", or "tcp6".
-// If laddr has a port of 0, it means to listen on some available port.
-// The caller can use l.Addr() to retrieve the chosen address.
-func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
- fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
- if err != nil {
- return nil, err
- }
- err = syscall.Listen(fd.sysfd, listenerBacklog)
- if err != nil {
- closesocket(fd.sysfd)
- return nil, &OpError{"listen", net, laddr, err}
- }
- l := new(TCPListener)
- l.fd = fd
- return l, nil
-}
-
// AcceptTCP accepts the next incoming call and returns the new connection
// and the remote address.
func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) {
- if l == nil || l.fd == nil || l.fd.sysfd < 0 {
+ if l == nil || l.fd == nil {
return nil, syscall.EINVAL
}
fd, err := l.fd.accept(sockaddrToTCP)
@@ -359,3 +271,28 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
// It is the caller's responsibility to close f when finished.
// Closing l does not affect f, and closing f does not affect l.
func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }
+
+// ListenTCP announces on the TCP address laddr and returns a TCP listener.
+// Net must be "tcp", "tcp4", or "tcp6".
+// If laddr has a port of 0, it means to listen on some available port.
+// The caller can use l.Addr() to retrieve the chosen address.
+func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
+ switch net {
+ case "tcp", "tcp4", "tcp6":
+ default:
+ return nil, UnknownNetworkError(net)
+ }
+ if laddr == nil {
+ laddr = &TCPAddr{}
+ }
+ fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
+ if err != nil {
+ return nil, err
+ }
+ err = syscall.Listen(fd.sysfd, listenerBacklog)
+ if err != nil {
+ closesocket(fd.sysfd)
+ return nil, &OpError{"listen", net, laddr, err}
+ }
+ return &TCPListener{fd}, nil
+}
diff --git a/src/pkg/net/textproto/reader.go b/src/pkg/net/textproto/reader.go
index 125feb3e8..b61bea862 100644
--- a/src/pkg/net/textproto/reader.go
+++ b/src/pkg/net/textproto/reader.go
@@ -128,6 +128,17 @@ func (r *Reader) readContinuedLineSlice() ([]byte, error) {
return line, nil
}
+ // Optimistically assume that we have started to buffer the next line
+ // and it starts with an ASCII letter (the next header key), so we can
+ // avoid copying that buffered data around in memory and skipping over
+ // non-existent whitespace.
+ if r.R.Buffered() > 1 {
+ peek, err := r.R.Peek(1)
+ if err == nil && isASCIILetter(peek[0]) {
+ return trim(line), nil
+ }
+ }
+
// ReadByte or the next readLineSlice will flush the read buffer;
// copy the slice into buf.
r.buf = append(r.buf[:0], trim(line)...)
@@ -445,23 +456,25 @@ func (r *Reader) ReadDotLines() ([]string, error) {
// }
//
func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
- m := make(MIMEHeader)
+ m := make(MIMEHeader, 4)
for {
kv, err := r.readContinuedLineSlice()
if len(kv) == 0 {
return m, err
}
- // Key ends at first colon; must not have spaces.
+ // Key ends at first colon; should not have spaces but
+ // they appear in the wild, violating specs, so we
+ // remove them if present.
i := bytes.IndexByte(kv, ':')
if i < 0 {
return m, ProtocolError("malformed MIME header line: " + string(kv))
}
- key := string(kv[0:i])
- if strings.Index(key, " ") >= 0 {
- key = strings.TrimRight(key, " ")
+ endKey := i
+ for endKey > 0 && kv[endKey-1] == ' ' {
+ endKey--
}
- key = CanonicalMIMEHeaderKey(key)
+ key := canonicalMIMEHeaderKey(kv[:endKey])
// Skip initial spaces in value.
i++ // skip colon
@@ -484,41 +497,107 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
// letter and any letter following a hyphen to upper case;
// the rest are converted to lowercase. For example, the
// canonical key for "accept-encoding" is "Accept-Encoding".
+// MIME header keys are assumed to be ASCII only.
func CanonicalMIMEHeaderKey(s string) string {
// Quick check for canonical encoding.
- needUpper := true
+ upper := true
for i := 0; i < len(s); i++ {
c := s[i]
- if needUpper && 'a' <= c && c <= 'z' {
- goto MustRewrite
+ if upper && 'a' <= c && c <= 'z' {
+ return canonicalMIMEHeaderKey([]byte(s))
}
- if !needUpper && 'A' <= c && c <= 'Z' {
- goto MustRewrite
+ if !upper && 'A' <= c && c <= 'Z' {
+ return canonicalMIMEHeaderKey([]byte(s))
}
- needUpper = c == '-'
+ upper = c == '-'
}
return s
+}
+
+const toLower = 'a' - 'A'
-MustRewrite:
- // Canonicalize: first letter upper case
- // and upper case after each dash.
- // (Host, User-Agent, If-Modified-Since).
- // MIME headers are ASCII only, so no Unicode issues.
- a := []byte(s)
+// canonicalMIMEHeaderKey is like CanonicalMIMEHeaderKey but is
+// allowed to mutate the provided byte slice before returning the
+// string.
+func canonicalMIMEHeaderKey(a []byte) string {
+ // Look for it in commonHeaders , so that we can avoid an
+ // allocation by sharing the strings among all users
+ // of textproto. If we don't find it, a has been canonicalized
+ // so just return string(a).
upper := true
- for i, v := range a {
- if v == ' ' {
+ lo := 0
+ hi := len(commonHeaders)
+ for i := 0; i < len(a); i++ {
+ // Canonicalize: first letter upper case
+ // and upper case after each dash.
+ // (Host, User-Agent, If-Modified-Since).
+ // MIME headers are ASCII only, so no Unicode issues.
+ if a[i] == ' ' {
a[i] = '-'
upper = true
continue
}
- if upper && 'a' <= v && v <= 'z' {
- a[i] = v + 'A' - 'a'
+ c := a[i]
+ if upper && 'a' <= c && c <= 'z' {
+ c -= toLower
+ } else if !upper && 'A' <= c && c <= 'Z' {
+ c += toLower
}
- if !upper && 'A' <= v && v <= 'Z' {
- a[i] = v + 'a' - 'A'
+ a[i] = c
+ upper = c == '-' // for next time
+
+ if lo < hi {
+ for lo < hi && (len(commonHeaders[lo]) <= i || commonHeaders[lo][i] < c) {
+ lo++
+ }
+ for hi > lo && commonHeaders[hi-1][i] > c {
+ hi--
+ }
}
- upper = v == '-'
+ }
+ if lo < hi && len(commonHeaders[lo]) == len(a) {
+ return commonHeaders[lo]
}
return string(a)
}
+
+var commonHeaders = []string{
+ "Accept",
+ "Accept-Charset",
+ "Accept-Encoding",
+ "Accept-Language",
+ "Accept-Ranges",
+ "Cache-Control",
+ "Cc",
+ "Connection",
+ "Content-Id",
+ "Content-Language",
+ "Content-Length",
+ "Content-Transfer-Encoding",
+ "Content-Type",
+ "Date",
+ "Dkim-Signature",
+ "Etag",
+ "Expires",
+ "From",
+ "Host",
+ "If-Modified-Since",
+ "If-None-Match",
+ "In-Reply-To",
+ "Last-Modified",
+ "Location",
+ "Message-Id",
+ "Mime-Version",
+ "Pragma",
+ "Received",
+ "Return-Path",
+ "Server",
+ "Set-Cookie",
+ "Subject",
+ "To",
+ "User-Agent",
+ "Via",
+ "X-Forwarded-For",
+ "X-Imforwards",
+ "X-Powered-By",
+}
diff --git a/src/pkg/net/textproto/reader_test.go b/src/pkg/net/textproto/reader_test.go
index 7c5d16227..26987f611 100644
--- a/src/pkg/net/textproto/reader_test.go
+++ b/src/pkg/net/textproto/reader_test.go
@@ -6,6 +6,7 @@ package textproto
import (
"bufio"
+ "bytes"
"io"
"reflect"
"strings"
@@ -23,6 +24,7 @@ var canonicalHeaderKeyTests = []canonicalHeaderKeyTest{
{"uSER-aGENT", "User-Agent"},
{"user-agent", "User-Agent"},
{"USER-AGENT", "User-Agent"},
+ {"üser-agenT", "üser-Agent"}, // non-ASCII unchanged
}
func TestCanonicalMIMEHeaderKey(t *testing.T) {
@@ -239,3 +241,95 @@ func TestRFC959Lines(t *testing.T) {
}
}
}
+
+func TestCommonHeaders(t *testing.T) {
+ // need to disable the commonHeaders-based optimization
+ // during this check, or we'd not be testing anything
+ oldch := commonHeaders
+ commonHeaders = []string{}
+ defer func() { commonHeaders = oldch }()
+
+ last := ""
+ for _, h := range oldch {
+ if last > h {
+ t.Errorf("%v is out of order", h)
+ }
+ if last == h {
+ t.Errorf("%v is duplicated", h)
+ }
+ if canon := CanonicalMIMEHeaderKey(h); h != canon {
+ t.Errorf("%v is not canonical", h)
+ }
+ last = h
+ }
+}
+
+var clientHeaders = strings.Replace(`Host: golang.org
+Connection: keep-alive
+Cache-Control: max-age=0
+Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
+User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3
+Accept-Encoding: gzip,deflate,sdch
+Accept-Language: en-US,en;q=0.8,fr-CH;q=0.6
+Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
+COOKIE: __utma=000000000.0000000000.0000000000.0000000000.0000000000.00; __utmb=000000000.0.00.0000000000; __utmc=000000000; __utmz=000000000.0000000000.00.0.utmcsr=code.google.com|utmccn=(referral)|utmcmd=referral|utmcct=/p/go/issues/detail
+Non-Interned: test
+
+`, "\n", "\r\n", -1)
+
+var serverHeaders = strings.Replace(`Content-Type: text/html; charset=utf-8
+Content-Encoding: gzip
+Date: Thu, 27 Sep 2012 09:03:33 GMT
+Server: Google Frontend
+Cache-Control: private
+Content-Length: 2298
+VIA: 1.1 proxy.example.com:80 (XXX/n.n.n-nnn)
+Connection: Close
+Non-Interned: test
+
+`, "\n", "\r\n", -1)
+
+func BenchmarkReadMIMEHeader(b *testing.B) {
+ var buf bytes.Buffer
+ br := bufio.NewReader(&buf)
+ r := NewReader(br)
+ for i := 0; i < b.N; i++ {
+ var want int
+ var find string
+ if (i & 1) == 1 {
+ buf.WriteString(clientHeaders)
+ want = 10
+ find = "Cookie"
+ } else {
+ buf.WriteString(serverHeaders)
+ want = 9
+ find = "Via"
+ }
+ h, err := r.ReadMIMEHeader()
+ if err != nil {
+ b.Fatal(err)
+ }
+ if len(h) != want {
+ b.Fatalf("wrong number of headers: got %d, want %d", len(h), want)
+ }
+ if _, ok := h[find]; !ok {
+ b.Fatalf("did not find key %s", find)
+ }
+ }
+}
+
+func BenchmarkUncommon(b *testing.B) {
+ var buf bytes.Buffer
+ br := bufio.NewReader(&buf)
+ r := NewReader(br)
+ for i := 0; i < b.N; i++ {
+ buf.WriteString("uncommon-header-for-benchmark: foo\r\n\r\n")
+ h, err := r.ReadMIMEHeader()
+ if err != nil {
+ b.Fatal(err)
+ }
+ if _, ok := h["Uncommon-Header-For-Benchmark"]; !ok {
+ b.Fatal("Missing result header.")
+ }
+ }
+}
diff --git a/src/pkg/net/textproto/textproto.go b/src/pkg/net/textproto/textproto.go
index ad5840cf7..eb6ced1c5 100644
--- a/src/pkg/net/textproto/textproto.go
+++ b/src/pkg/net/textproto/textproto.go
@@ -121,3 +121,34 @@ func (c *Conn) Cmd(format string, args ...interface{}) (id uint, err error) {
}
return id, nil
}
+
+// TrimString returns s without leading and trailing ASCII space.
+func TrimString(s string) string {
+ for len(s) > 0 && isASCIISpace(s[0]) {
+ s = s[1:]
+ }
+ for len(s) > 0 && isASCIISpace(s[len(s)-1]) {
+ s = s[:len(s)-1]
+ }
+ return s
+}
+
+// TrimBytes returns b without leading and trailing ASCII space.
+func TrimBytes(b []byte) []byte {
+ for len(b) > 0 && isASCIISpace(b[0]) {
+ b = b[1:]
+ }
+ for len(b) > 0 && isASCIISpace(b[len(b)-1]) {
+ b = b[:len(b)-1]
+ }
+ return b
+}
+
+func isASCIISpace(b byte) bool {
+ return b == ' ' || b == '\t' || b == '\n' || b == '\r'
+}
+
+func isASCIILetter(b byte) bool {
+ b |= 0x20 // make lower case
+ return 'a' <= b && b <= 'z'
+}
diff --git a/src/pkg/net/timeout_test.go b/src/pkg/net/timeout_test.go
index 672fb7241..0260efcc0 100644
--- a/src/pkg/net/timeout_test.go
+++ b/src/pkg/net/timeout_test.go
@@ -6,11 +6,187 @@ package net
import (
"fmt"
+ "io"
+ "io/ioutil"
"runtime"
"testing"
"time"
)
+func isTimeout(err error) bool {
+ e, ok := err.(Error)
+ return ok && e.Timeout()
+}
+
+type copyRes struct {
+ n int64
+ err error
+ d time.Duration
+}
+
+func TestAcceptTimeout(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ ln := newLocalListener(t).(*TCPListener)
+ defer ln.Close()
+ ln.SetDeadline(time.Now().Add(-1 * time.Second))
+ if _, err := ln.Accept(); !isTimeout(err) {
+ t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
+ }
+ if _, err := ln.Accept(); !isTimeout(err) {
+ t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
+ }
+ ln.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ if _, err := ln.Accept(); !isTimeout(err) {
+ t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
+ }
+ if _, err := ln.Accept(); !isTimeout(err) {
+ t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
+ }
+ ln.SetDeadline(noDeadline)
+ errc := make(chan error)
+ go func() {
+ _, err := ln.Accept()
+ errc <- err
+ }()
+ time.Sleep(100 * time.Millisecond)
+ select {
+ case err := <-errc:
+ t.Fatalf("Expected Accept() to not return, but it returned with %v\n", err)
+ default:
+ }
+ ln.Close()
+ switch nerr := <-errc; err := nerr.(type) {
+ case *OpError:
+ if err.Err != errClosing {
+ t.Fatalf("Accept: expected err %v, got %v", errClosing, err)
+ }
+ default:
+ if err != errClosing {
+ t.Fatalf("Accept: expected err %v, got %v", errClosing, err)
+ }
+ }
+}
+
+func TestReadTimeout(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ ln := newLocalListener(t)
+ defer ln.Close()
+ c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr))
+ if err != nil {
+ t.Fatalf("Connect: %v", err)
+ }
+ defer c.Close()
+ c.SetDeadline(time.Now().Add(time.Hour))
+ c.SetReadDeadline(time.Now().Add(-1 * time.Second))
+ buf := make([]byte, 1)
+ if _, err = c.Read(buf); !isTimeout(err) {
+ t.Fatalf("Read: expected err %v, got %v", errTimeout, err)
+ }
+ if _, err = c.Read(buf); !isTimeout(err) {
+ t.Fatalf("Read: expected err %v, got %v", errTimeout, err)
+ }
+ c.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ if _, err = c.Read(buf); !isTimeout(err) {
+ t.Fatalf("Read: expected err %v, got %v", errTimeout, err)
+ }
+ if _, err = c.Read(buf); !isTimeout(err) {
+ t.Fatalf("Read: expected err %v, got %v", errTimeout, err)
+ }
+ c.SetReadDeadline(noDeadline)
+ c.SetWriteDeadline(time.Now().Add(-1 * time.Second))
+ errc := make(chan error)
+ go func() {
+ _, err := c.Read(buf)
+ errc <- err
+ }()
+ time.Sleep(100 * time.Millisecond)
+ select {
+ case err := <-errc:
+ t.Fatalf("Expected Read() to not return, but it returned with %v\n", err)
+ default:
+ }
+ c.Close()
+ switch nerr := <-errc; err := nerr.(type) {
+ case *OpError:
+ if err.Err != errClosing {
+ t.Fatalf("Read: expected err %v, got %v", errClosing, err)
+ }
+ default:
+ if err != errClosing {
+ t.Fatalf("Read: expected err %v, got %v", errClosing, err)
+ }
+ }
+}
+
+func TestWriteTimeout(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ ln := newLocalListener(t)
+ defer ln.Close()
+ c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr))
+ if err != nil {
+ t.Fatalf("Connect: %v", err)
+ }
+ defer c.Close()
+ c.SetDeadline(time.Now().Add(time.Hour))
+ c.SetWriteDeadline(time.Now().Add(-1 * time.Second))
+ buf := make([]byte, 4096)
+ writeUntilTimeout := func() {
+ for {
+ _, err := c.Write(buf)
+ if err != nil {
+ if isTimeout(err) {
+ return
+ }
+ t.Fatalf("Write: expected err %v, got %v", errTimeout, err)
+ }
+ }
+ }
+ writeUntilTimeout()
+ c.SetDeadline(time.Now().Add(10 * time.Millisecond))
+ writeUntilTimeout()
+ writeUntilTimeout()
+ c.SetWriteDeadline(noDeadline)
+ c.SetReadDeadline(time.Now().Add(-1 * time.Second))
+ errc := make(chan error)
+ go func() {
+ for {
+ _, err := c.Write(buf)
+ if err != nil {
+ errc <- err
+ }
+ }
+ }()
+ time.Sleep(100 * time.Millisecond)
+ select {
+ case err := <-errc:
+ t.Fatalf("Expected Write() to not return, but it returned with %v\n", err)
+ default:
+ }
+ c.Close()
+ switch nerr := <-errc; err := nerr.(type) {
+ case *OpError:
+ if err.Err != errClosing {
+ t.Fatalf("Write: expected err %v, got %v", errClosing, err)
+ }
+ default:
+ if err != errClosing {
+ t.Fatalf("Write: expected err %v, got %v", errClosing, err)
+ }
+ }
+}
+
func testTimeout(t *testing.T, net, addr string, readFrom bool) {
c, err := Dial(net, addr)
if err != nil {
@@ -59,8 +235,7 @@ func testTimeout(t *testing.T, net, addr string, readFrom bool) {
func TestTimeoutUDP(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
// set up a listener that won't talk back
@@ -77,8 +252,7 @@ func TestTimeoutUDP(t *testing.T) {
func TestTimeoutTCP(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
// set up a listener that won't talk back
@@ -94,8 +268,7 @@ func TestTimeoutTCP(t *testing.T) {
func TestDeadlineReset(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
ln, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
@@ -104,7 +277,7 @@ func TestDeadlineReset(t *testing.T) {
defer ln.Close()
tl := ln.(*TCPListener)
tl.SetDeadline(time.Now().Add(1 * time.Minute))
- tl.SetDeadline(time.Time{}) // reset it
+ tl.SetDeadline(noDeadline) // reset it
errc := make(chan error, 1)
go func() {
_, err := ln.Accept()
@@ -119,3 +292,356 @@ func TestDeadlineReset(t *testing.T) {
t.Errorf("unexpected return from Accept; err=%v", err)
}
}
+
+func TestTimeoutAccept(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+ ln, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+ tl := ln.(*TCPListener)
+ tl.SetDeadline(time.Now().Add(100 * time.Millisecond))
+ errc := make(chan error, 1)
+ go func() {
+ _, err := ln.Accept()
+ errc <- err
+ }()
+ select {
+ case <-time.After(1 * time.Second):
+ // Accept shouldn't block indefinitely
+ t.Errorf("Accept didn't return in an expected time")
+ case <-errc:
+ // Pass.
+ }
+}
+
+func TestReadWriteDeadline(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ if !canCancelIO {
+ t.Skip("skipping test on this system")
+ }
+ const (
+ readTimeout = 50 * time.Millisecond
+ writeTimeout = 250 * time.Millisecond
+ )
+ checkTimeout := func(command string, start time.Time, should time.Duration) {
+ is := time.Now().Sub(start)
+ d := is - should
+ if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d {
+ t.Errorf("%s timeout test failed: is=%v should=%v\n", command, is, should)
+ }
+ }
+
+ ln, err := Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("ListenTCP on :0: %v", err)
+ }
+ defer ln.Close()
+
+ lnquit := make(chan bool)
+
+ go func() {
+ c, err := ln.Accept()
+ if err != nil {
+ t.Fatalf("Accept: %v", err)
+ }
+ defer c.Close()
+ lnquit <- true
+ }()
+
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ defer c.Close()
+
+ start := time.Now()
+ err = c.SetReadDeadline(start.Add(readTimeout))
+ if err != nil {
+ t.Fatalf("SetReadDeadline: %v", err)
+ }
+ err = c.SetWriteDeadline(start.Add(writeTimeout))
+ if err != nil {
+ t.Fatalf("SetWriteDeadline: %v", err)
+ }
+
+ quit := make(chan bool)
+
+ go func() {
+ var buf [10]byte
+ _, err := c.Read(buf[:])
+ if err == nil {
+ t.Errorf("Read should not succeed")
+ }
+ checkTimeout("Read", start, readTimeout)
+ quit <- true
+ }()
+
+ go func() {
+ var buf [10000]byte
+ for {
+ _, err := c.Write(buf[:])
+ if err != nil {
+ break
+ }
+ }
+ checkTimeout("Write", start, writeTimeout)
+ quit <- true
+ }()
+
+ <-quit
+ <-quit
+ <-lnquit
+}
+
+type neverEnding byte
+
+func (b neverEnding) Read(p []byte) (n int, err error) {
+ for i := range p {
+ p[i] = byte(b)
+ }
+ return len(p), nil
+}
+
+func TestVariousDeadlines1Proc(t *testing.T) {
+ testVariousDeadlines(t, 1)
+}
+
+func TestVariousDeadlines4Proc(t *testing.T) {
+ testVariousDeadlines(t, 4)
+}
+
+func testVariousDeadlines(t *testing.T, maxProcs int) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))
+ ln := newLocalListener(t)
+ defer ln.Close()
+ acceptc := make(chan error, 1)
+
+ // The server, with no timeouts of its own, sending bytes to clients
+ // as fast as it can.
+ servec := make(chan copyRes)
+ go func() {
+ for {
+ c, err := ln.Accept()
+ if err != nil {
+ acceptc <- err
+ return
+ }
+ go func() {
+ t0 := time.Now()
+ n, err := io.Copy(c, neverEnding('a'))
+ d := time.Since(t0)
+ c.Close()
+ servec <- copyRes{n, err, d}
+ }()
+ }
+ }()
+
+ for _, timeout := range []time.Duration{
+ 1 * time.Nanosecond,
+ 2 * time.Nanosecond,
+ 5 * time.Nanosecond,
+ 50 * time.Nanosecond,
+ 100 * time.Nanosecond,
+ 200 * time.Nanosecond,
+ 500 * time.Nanosecond,
+ 750 * time.Nanosecond,
+ 1 * time.Microsecond,
+ 5 * time.Microsecond,
+ 25 * time.Microsecond,
+ 250 * time.Microsecond,
+ 500 * time.Microsecond,
+ 1 * time.Millisecond,
+ 5 * time.Millisecond,
+ 100 * time.Millisecond,
+ 250 * time.Millisecond,
+ 500 * time.Millisecond,
+ 1 * time.Second,
+ } {
+ numRuns := 3
+ if testing.Short() {
+ numRuns = 1
+ if timeout > 500*time.Microsecond {
+ continue
+ }
+ }
+ for run := 0; run < numRuns; run++ {
+ name := fmt.Sprintf("%v run %d/%d", timeout, run+1, numRuns)
+ t.Log(name)
+
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ clientc := make(chan copyRes)
+ go func() {
+ t0 := time.Now()
+ c.SetDeadline(t0.Add(timeout))
+ n, err := io.Copy(ioutil.Discard, c)
+ d := time.Since(t0)
+ c.Close()
+ clientc <- copyRes{n, err, d}
+ }()
+
+ const tooLong = 2000 * time.Millisecond
+ select {
+ case res := <-clientc:
+ if isTimeout(res.err) {
+ t.Logf("for %v, good client timeout after %v, reading %d bytes", name, res.d, res.n)
+ } else {
+ t.Fatalf("for %v: client Copy = %d, %v (want timeout)", name, res.n, res.err)
+ }
+ case <-time.After(tooLong):
+ t.Fatalf("for %v: timeout (%v) waiting for client to timeout (%v) reading", name, tooLong, timeout)
+ }
+
+ select {
+ case res := <-servec:
+ t.Logf("for %v: server in %v wrote %d, %v", name, res.d, res.n, res.err)
+ case err := <-acceptc:
+ t.Fatalf("for %v: server Accept = %v", name, err)
+ case <-time.After(tooLong):
+ t.Fatalf("for %v, timeout waiting for server to finish writing", name)
+ }
+ }
+ }
+}
+
+// TestReadDeadlineDataAvailable tests that read deadlines work, even
+// if there's data ready to be read.
+func TestReadDeadlineDataAvailable(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ ln := newLocalListener(t)
+ defer ln.Close()
+
+ servec := make(chan copyRes)
+ const msg = "data client shouldn't read, even though it it'll be waiting"
+ go func() {
+ c, err := ln.Accept()
+ if err != nil {
+ t.Fatalf("Accept: %v", err)
+ }
+ defer c.Close()
+ n, err := c.Write([]byte(msg))
+ servec <- copyRes{n: int64(n), err: err}
+ }()
+
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ defer c.Close()
+ if res := <-servec; res.err != nil || res.n != int64(len(msg)) {
+ t.Fatalf("unexpected server Write: n=%d, err=%d; want n=%d, err=nil", res.n, res.err, len(msg))
+ }
+ c.SetReadDeadline(time.Now().Add(-5 * time.Second)) // in the psat.
+ buf := make([]byte, len(msg)/2)
+ n, err := c.Read(buf)
+ if n > 0 || !isTimeout(err) {
+ t.Fatalf("client read = %d (%q) err=%v; want 0, timeout", n, buf[:n], err)
+ }
+}
+
+// TestWriteDeadlineBufferAvailable tests that write deadlines work, even
+// if there's buffer space available to write.
+func TestWriteDeadlineBufferAvailable(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ ln := newLocalListener(t)
+ defer ln.Close()
+
+ servec := make(chan copyRes)
+ go func() {
+ c, err := ln.Accept()
+ if err != nil {
+ t.Fatalf("Accept: %v", err)
+ }
+ defer c.Close()
+ c.SetWriteDeadline(time.Now().Add(-5 * time.Second)) // in the past
+ n, err := c.Write([]byte{'x'})
+ servec <- copyRes{n: int64(n), err: err}
+ }()
+
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ defer c.Close()
+ res := <-servec
+ if res.n != 0 {
+ t.Errorf("Write = %d; want 0", res.n)
+ }
+ if !isTimeout(res.err) {
+ t.Errorf("Write error = %v; want timeout", res.err)
+ }
+}
+
+// TestProlongTimeout tests concurrent deadline modification.
+// Known to cause data races in the past.
+func TestProlongTimeout(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ ln := newLocalListener(t)
+ defer ln.Close()
+ connected := make(chan bool)
+ go func() {
+ s, err := ln.Accept()
+ connected <- true
+ if err != nil {
+ t.Fatalf("ln.Accept: %v", err)
+ }
+ defer s.Close()
+ s.SetDeadline(time.Now().Add(time.Hour))
+ go func() {
+ var buf [4096]byte
+ for {
+ _, err := s.Write(buf[:])
+ if err != nil {
+ break
+ }
+ s.SetDeadline(time.Now().Add(time.Hour))
+ }
+ }()
+ buf := make([]byte, 1)
+ for {
+ _, err := s.Read(buf)
+ if err != nil {
+ break
+ }
+ s.SetDeadline(time.Now().Add(time.Hour))
+ }
+ }()
+ c, err := Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatalf("DialTCP: %v", err)
+ }
+ defer c.Close()
+ <-connected
+ for i := 0; i < 1024; i++ {
+ var buf [1]byte
+ c.Write(buf[:])
+ }
+}
diff --git a/src/pkg/net/udp_test.go b/src/pkg/net/udp_test.go
index f80d3b5a9..220422e13 100644
--- a/src/pkg/net/udp_test.go
+++ b/src/pkg/net/udp_test.go
@@ -5,15 +5,45 @@
package net
import (
+ "reflect"
"runtime"
"testing"
)
+var resolveUDPAddrTests = []struct {
+ net string
+ litAddr string
+ addr *UDPAddr
+ err error
+}{
+ {"udp", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil},
+ {"udp4", "127.0.0.1:65535", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil},
+
+ {"udp", "[::1]:1", &UDPAddr{IP: ParseIP("::1"), Port: 1}, nil},
+ {"udp6", "[::1]:65534", &UDPAddr{IP: ParseIP("::1"), Port: 65534}, nil},
+
+ {"", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
+ {"", "[::1]:0", &UDPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior
+
+ {"sip", "127.0.0.1:0", nil, UnknownNetworkError("sip")},
+}
+
+func TestResolveUDPAddr(t *testing.T) {
+ for _, tt := range resolveUDPAddrTests {
+ addr, err := ResolveUDPAddr(tt.net, tt.litAddr)
+ if err != tt.err {
+ t.Fatalf("ResolveUDPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
+ }
+ if !reflect.DeepEqual(addr, tt.addr) {
+ t.Fatalf("got %#v; expected %#v", addr, tt.addr)
+ }
+ }
+}
+
func TestWriteToUDP(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
l, err := ListenPacket("udp", "127.0.0.1:0")
@@ -87,3 +117,32 @@ func testWriteToPacketConn(t *testing.T, raddr string) {
t.Fatal("Write should fail")
}
}
+
+var udpConnLocalNameTests = []struct {
+ net string
+ laddr *UDPAddr
+}{
+ {"udp4", &UDPAddr{IP: IPv4(127, 0, 0, 1)}},
+ {"udp4", &UDPAddr{}},
+ {"udp4", nil},
+}
+
+func TestUDPConnLocalName(t *testing.T) {
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+
+ for _, tt := range udpConnLocalNameTests {
+ c, err := ListenUDP(tt.net, tt.laddr)
+ if err != nil {
+ t.Errorf("ListenUDP failed: %v", err)
+ return
+ }
+ defer c.Close()
+ la := c.LocalAddr()
+ if a, ok := la.(*UDPAddr); !ok || a.Port == 0 {
+ t.Errorf("got %v; expected a proper address with non-zero port number", la)
+ return
+ }
+ }
+}
diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go
index b3520cf09..6e5e90268 100644
--- a/src/pkg/net/udpsock.go
+++ b/src/pkg/net/udpsock.go
@@ -6,10 +6,15 @@
package net
+import "errors"
+
+var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP")
+
// UDPAddr represents the address of a UDP end point.
type UDPAddr struct {
IP IP
Port int
+ Zone string // IPv6 scoped addressing zone
}
// Network returns the address's network name, "udp".
@@ -28,9 +33,16 @@ func (a *UDPAddr) String() string {
// "udp4" or "udp6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]:80".
func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
- ip, port, err := hostPortToIP(net, addr)
+ switch net {
+ case "udp", "udp4", "udp6":
+ case "": // a hint wildcard for Go 1.0 undocumented behavior
+ net = "udp"
+ default:
+ return nil, UnknownNetworkError(net)
+ }
+ a, err := resolveInternetAddr(net, addr, noDeadline)
if err != nil {
return nil, err
}
- return &UDPAddr{ip, port}, nil
+ return a.(*UDPAddr), nil
}
diff --git a/src/pkg/net/udpsock_plan9.go b/src/pkg/net/udpsock_plan9.go
index 4f298a42f..2a7e3d19c 100644
--- a/src/pkg/net/udpsock_plan9.go
+++ b/src/pkg/net/udpsock_plan9.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// UDP for Plan 9
+// UDP sockets for Plan 9
package net
@@ -16,44 +16,26 @@ import (
// UDPConn is the implementation of the Conn and PacketConn
// interfaces for UDP network connections.
type UDPConn struct {
- plan9Conn
+ conn
}
-// SetDeadline implements the Conn SetDeadline method.
-func (c *UDPConn) SetDeadline(t time.Time) error {
- return syscall.EPLAN9
+func newUDPConn(fd *netFD) *UDPConn {
+ return &UDPConn{conn{fd}}
}
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *UDPConn) SetReadDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *UDPConn) SetWriteDeadline(t time.Time) error {
- return syscall.EPLAN9
-}
-
-// UDP-specific methods.
-
// ReadFromUDP reads a UDP packet from c, copying the payload into b.
// It returns the number of bytes copied into b and the return address
// that was on the packet.
//
-// ReadFromUDP can be made to time out and return an error with Timeout() == true
-// after a fixed time limit; see SetDeadline and SetReadDeadline.
+// ReadFromUDP can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetReadDeadline.
func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
- if !c.ok() {
+ if !c.ok() || c.fd.data == nil {
return 0, nil, syscall.EINVAL
}
- if c.data == nil {
- c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0)
- if err != nil {
- return 0, nil, err
- }
- }
buf := make([]byte, udpHeaderSize+len(b))
- m, err := c.data.Read(buf)
+ m, err := c.fd.data.Read(buf)
if err != nil {
return
}
@@ -64,62 +46,80 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
h, buf := unmarshalUDPHeader(buf)
n = copy(b, buf)
- return n, &UDPAddr{h.raddr, int(h.rport)}, nil
+ return n, &UDPAddr{IP: h.raddr, Port: int(h.rport)}, nil
}
// ReadFrom implements the PacketConn ReadFrom method.
-func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
+func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
if !c.ok() {
return 0, nil, syscall.EINVAL
}
return c.ReadFromUDP(b)
}
-// WriteToUDP writes a UDP packet to addr via c, copying the payload from b.
+// ReadMsgUDP reads a packet from c, copying the payload into b and
+// the associdated out-of-band data into oob. It returns the number
+// of bytes copied into b, the number of bytes copied into oob, the
+// flags that were set on the packet and the source address of the
+// packet.
+func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) {
+ return 0, 0, 0, nil, syscall.EPLAN9
+}
+
+// WriteToUDP writes a UDP packet to addr via c, copying the payload
+// from b.
//
-// WriteToUDP can be made to time out and return
-// an error with Timeout() == true after a fixed time limit;
-// see SetDeadline and SetWriteDeadline.
-// On packet-oriented connections, write timeouts are rare.
-func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) {
- if !c.ok() {
+// WriteToUDP can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetWriteDeadline. On packet-oriented connections, write timeouts
+// are rare.
+func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
+ if !c.ok() || c.fd.data == nil {
return 0, syscall.EINVAL
}
- if c.data == nil {
- c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0)
- if err != nil {
- return 0, err
- }
- }
h := new(udpHeader)
h.raddr = addr.IP.To16()
- h.laddr = c.laddr.(*UDPAddr).IP.To16()
+ h.laddr = c.fd.laddr.(*UDPAddr).IP.To16()
h.ifcaddr = IPv6zero // ignored (receive only)
h.rport = uint16(addr.Port)
- h.lport = uint16(c.laddr.(*UDPAddr).Port)
+ h.lport = uint16(c.fd.laddr.(*UDPAddr).Port)
buf := make([]byte, udpHeaderSize+len(b))
i := copy(buf, h.Bytes())
copy(buf[i:], b)
- return c.data.Write(buf)
+ return c.fd.data.Write(buf)
}
// WriteTo implements the PacketConn WriteTo method.
-func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err error) {
+func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
if !c.ok() {
return 0, syscall.EINVAL
}
a, ok := addr.(*UDPAddr)
if !ok {
- return 0, &OpError{"write", c.dir, addr, syscall.EINVAL}
+ return 0, &OpError{"write", c.fd.dir, addr, syscall.EINVAL}
}
return c.WriteToUDP(b, a)
}
+// WriteMsgUDP writes a packet to addr via c, copying the payload from
+// b and the associated out-of-band data from oob. It returns the
+// number of payload and out-of-band bytes written.
+func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) {
+ return 0, 0, syscall.EPLAN9
+}
+
// DialUDP connects to the remote address raddr on the network net,
-// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used
-// as the local address for the connection.
-func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) {
+// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is
+// used as the local address for the connection.
+func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
+ return dialUDP(net, laddr, raddr, noDeadline)
+}
+
+func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) {
+ if !deadline.IsZero() {
+ panic("net.dialUDP: deadline not implemented on Plan 9")
+ }
switch net {
case "udp", "udp4", "udp6":
default:
@@ -128,11 +128,11 @@ func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) {
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
- c1, err := dialPlan9(net, laddr, raddr)
+ fd, err := dialPlan9(net, laddr, raddr)
if err != nil {
- return
+ return nil, err
}
- return &UDPConn{*c1}, nil
+ return newUDPConn(fd), nil
}
const udpHeaderSize = 16*3 + 2*2
@@ -163,34 +163,38 @@ func unmarshalUDPHeader(b []byte) (*udpHeader, []byte) {
return h, b
}
-// ListenUDP listens for incoming UDP packets addressed to the
-// local address laddr. The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send UDP
-// packets with per-packet addressing.
-func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err error) {
+// ListenUDP listens for incoming UDP packets addressed to the local
+// address laddr. The returned connection c's ReadFrom and WriteTo
+// methods can be used to receive and send UDP packets with per-packet
+// addressing.
+func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
switch net {
case "udp", "udp4", "udp6":
default:
return nil, UnknownNetworkError(net)
}
if laddr == nil {
- return nil, &OpError{"listen", net, nil, errMissingAddress}
+ laddr = &UDPAddr{}
}
l, err := listenPlan9(net, laddr)
if err != nil {
- return
+ return nil, err
}
_, err = l.ctl.WriteString("headers")
if err != nil {
- return
+ return nil, err
+ }
+ l.data, err = os.OpenFile(l.dir+"/data", os.O_RDWR, 0)
+ if err != nil {
+ return nil, err
}
- return &UDPConn{*l.plan9Conn()}, nil
+ return newUDPConn(l.netFD()), nil
}
// ListenMulticastUDP listens for incoming multicast UDP packets
-// addressed to the group address gaddr on ifi, which specifies
-// the interface to join. ListenMulticastUDP uses default
-// multicast interface if ifi is nil.
+// addressed to the group address gaddr on ifi, which specifies the
+// interface to join. ListenMulticastUDP uses default multicast
+// interface if ifi is nil.
func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/src/pkg/net/udpsock_posix.go b/src/pkg/net/udpsock_posix.go
index 9c6b6d393..385cd902e 100644
--- a/src/pkg/net/udpsock_posix.go
+++ b/src/pkg/net/udpsock_posix.go
@@ -4,25 +4,21 @@
// +build darwin freebsd linux netbsd openbsd windows
-// UDP sockets
+// UDP sockets for POSIX
package net
import (
- "errors"
- "os"
"syscall"
"time"
)
-var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP")
-
func sockaddrToUDP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
- return &UDPAddr{sa.Addr[0:], sa.Port}
+ return &UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
case *syscall.SockaddrInet6:
- return &UDPAddr{sa.Addr[0:], sa.Port}
+ return &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
}
return nil
}
@@ -45,7 +41,7 @@ func (a *UDPAddr) isWildcard() bool {
}
func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
- return ipToSockaddr(family, a.IP, a.Port)
+ return ipToSockaddr(family, a.IP, a.Port, a.Zone)
}
func (a *UDPAddr) toAddr() sockaddr {
@@ -58,98 +54,10 @@ func (a *UDPAddr) toAddr() sockaddr {
// UDPConn is the implementation of the Conn and PacketConn
// interfaces for UDP network connections.
type UDPConn struct {
- fd *netFD
-}
-
-func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{fd} }
-
-func (c *UDPConn) ok() bool { return c != nil && c.fd != nil }
-
-// Implementation of the Conn interface - see Conn for documentation.
-
-// Read implements the Conn Read method.
-func (c *UDPConn) Read(b []byte) (int, error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Read(b)
-}
-
-// Write implements the Conn Write method.
-func (c *UDPConn) Write(b []byte) (int, error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Write(b)
-}
-
-// Close closes the UDP connection.
-func (c *UDPConn) Close() error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return c.fd.Close()
-}
-
-// LocalAddr returns the local network address.
-func (c *UDPConn) LocalAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.laddr
-}
-
-// RemoteAddr returns the remote network address, a *UDPAddr.
-func (c *UDPConn) RemoteAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.raddr
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *UDPConn) SetDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setDeadline(c.fd, t)
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *UDPConn) SetReadDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadDeadline(c.fd, t)
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *UDPConn) SetWriteDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteDeadline(c.fd, t)
-}
-
-// SetReadBuffer sets the size of the operating system's
-// receive buffer associated with the connection.
-func (c *UDPConn) SetReadBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadBuffer(c.fd, bytes)
-}
-
-// SetWriteBuffer sets the size of the operating system's
-// transmit buffer associated with the connection.
-func (c *UDPConn) SetWriteBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteBuffer(c.fd, bytes)
+ conn
}
-// UDP-specific methods.
+func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} }
// ReadFromUDP reads a UDP packet from c, copying the payload into b.
// It returns the number of bytes copied into b and the return address
@@ -164,9 +72,9 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
n, sa, err := c.fd.ReadFrom(b)
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
- addr = &UDPAddr{sa.Addr[0:], sa.Port}
+ addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
case *syscall.SockaddrInet6:
- addr = &UDPAddr{sa.Addr[0:], sa.Port}
+ addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
}
return
}
@@ -176,8 +84,28 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
if !c.ok() {
return 0, nil, syscall.EINVAL
}
- n, uaddr, err := c.ReadFromUDP(b)
- return n, uaddr.toAddr(), err
+ n, addr, err := c.ReadFromUDP(b)
+ return n, addr.toAddr(), err
+}
+
+// ReadMsgUDP reads a packet from c, copying the payload into b and
+// the associdated out-of-band data into oob. It returns the number
+// of bytes copied into b, the number of bytes copied into oob, the
+// flags that were set on the packet and the source address of the
+// packet.
+func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) {
+ if !c.ok() {
+ return 0, 0, 0, nil, syscall.EINVAL
+ }
+ var sa syscall.Sockaddr
+ n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob)
+ switch sa := sa.(type) {
+ case *syscall.SockaddrInet4:
+ addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
+ case *syscall.SockaddrInet6:
+ addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
+ }
+ return
}
// WriteToUDP writes a UDP packet to addr via c, copying the payload from b.
@@ -212,15 +140,31 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
return c.WriteToUDP(b, a)
}
-// File returns a copy of the underlying os.File, set to blocking mode.
-// It is the caller's responsibility to close f when finished.
-// Closing c does not affect f, and closing f does not affect c.
-func (c *UDPConn) File() (f *os.File, err error) { return c.fd.dup() }
+// WriteMsgUDP writes a packet to addr via c, copying the payload from
+// b and the associated out-of-band data from oob. It returns the
+// number of payload and out-of-band bytes written.
+func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) {
+ if !c.ok() {
+ return 0, 0, syscall.EINVAL
+ }
+ if c.fd.isConnected {
+ return 0, 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected}
+ }
+ sa, err := addr.sockaddr(c.fd.family)
+ if err != nil {
+ return 0, 0, &OpError{"write", c.fd.net, addr, err}
+ }
+ return c.fd.WriteMsg(b, oob, sa)
+}
// DialUDP connects to the remote address raddr on the network net,
// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used
// as the local address for the connection.
func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
+ return dialUDP(net, laddr, raddr, noDeadline)
+}
+
+func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) {
switch net {
case "udp", "udp4", "udp6":
default:
@@ -229,7 +173,7 @@ func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP)
+ fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP)
if err != nil {
return nil, err
}
@@ -247,9 +191,9 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
return nil, UnknownNetworkError(net)
}
if laddr == nil {
- return nil, &OpError{"listen", net, nil, errMissingAddress}
+ laddr = &UDPAddr{}
}
- fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
+ fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
if err != nil {
return nil, err
}
@@ -267,25 +211,22 @@ func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, e
return nil, UnknownNetworkError(net)
}
if gaddr == nil || gaddr.IP == nil {
- return nil, &OpError{"listenmulticast", net, nil, errMissingAddress}
+ return nil, &OpError{"listen", net, nil, errMissingAddress}
}
- fd, err := internetSocket(net, gaddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
+ fd, err := internetSocket(net, gaddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
if err != nil {
return nil, err
}
c := newUDPConn(fd)
- ip4 := gaddr.IP.To4()
- if ip4 != nil {
- err := listenIPv4MulticastUDP(c, ifi, ip4)
- if err != nil {
+ if ip4 := gaddr.IP.To4(); ip4 != nil {
+ if err := listenIPv4MulticastUDP(c, ifi, ip4); err != nil {
c.Close()
- return nil, err
+ return nil, &OpError{"listen", net, &IPAddr{IP: ip4}, err}
}
} else {
- err := listenIPv6MulticastUDP(c, ifi, gaddr.IP)
- if err != nil {
+ if err := listenIPv6MulticastUDP(c, ifi, gaddr.IP); err != nil {
c.Close()
- return nil, err
+ return nil, &OpError{"listen", net, &IPAddr{IP: gaddr.IP}, err}
}
}
return c, nil
@@ -293,17 +234,14 @@ func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, e
func listenIPv4MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error {
if ifi != nil {
- err := setIPv4MulticastInterface(c.fd, ifi)
- if err != nil {
+ if err := setIPv4MulticastInterface(c.fd, ifi); err != nil {
return err
}
}
- err := setIPv4MulticastLoopback(c.fd, false)
- if err != nil {
+ if err := setIPv4MulticastLoopback(c.fd, false); err != nil {
return err
}
- err = joinIPv4GroupUDP(c, ifi, ip)
- if err != nil {
+ if err := joinIPv4Group(c.fd, ifi, ip); err != nil {
return err
}
return nil
@@ -311,50 +249,15 @@ func listenIPv4MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error {
func listenIPv6MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error {
if ifi != nil {
- err := setIPv6MulticastInterface(c.fd, ifi)
- if err != nil {
+ if err := setIPv6MulticastInterface(c.fd, ifi); err != nil {
return err
}
}
- err := setIPv6MulticastLoopback(c.fd, false)
- if err != nil {
+ if err := setIPv6MulticastLoopback(c.fd, false); err != nil {
return err
}
- err = joinIPv6GroupUDP(c, ifi, ip)
- if err != nil {
+ if err := joinIPv6Group(c.fd, ifi, ip); err != nil {
return err
}
return nil
}
-
-func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
- err := joinIPv4Group(c.fd, ifi, ip)
- if err != nil {
- return &OpError{"joinipv4group", c.fd.net, &IPAddr{ip}, err}
- }
- return nil
-}
-
-func leaveIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
- err := leaveIPv4Group(c.fd, ifi, ip)
- if err != nil {
- return &OpError{"leaveipv4group", c.fd.net, &IPAddr{ip}, err}
- }
- return nil
-}
-
-func joinIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
- err := joinIPv6Group(c.fd, ifi, ip)
- if err != nil {
- return &OpError{"joinipv6group", c.fd.net, &IPAddr{ip}, err}
- }
- return nil
-}
-
-func leaveIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
- err := leaveIPv6Group(c.fd, ifi, ip)
- if err != nil {
- return &OpError{"leaveipv6group", c.fd.net, &IPAddr{ip}, err}
- }
- return nil
-}
diff --git a/src/pkg/net/unicast_test.go b/src/pkg/net/unicast_posix_test.go
index e5dd013db..a8855cab7 100644
--- a/src/pkg/net/unicast_test.go
+++ b/src/pkg/net/unicast_posix_test.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 !plan9
+
package net
import (
@@ -44,8 +46,7 @@ var listenerTests = []struct {
func TestTCPListener(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
for _, tt := range listenerTests {
@@ -59,13 +60,6 @@ func TestTCPListener(t *testing.T) {
checkFirstListener(t, tt.net, tt.laddr+":"+port, l1)
l2, err := Listen(tt.net, tt.laddr+":"+port)
checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2)
- fd := l1.(*TCPListener).fd
- switch fd.family {
- case syscall.AF_INET:
- testIPv4UnicastSocketOptions(t, fd)
- case syscall.AF_INET6:
- testIPv6UnicastSocketOptions(t, fd)
- }
l1.Close()
}
}
@@ -76,8 +70,7 @@ func TestTCPListener(t *testing.T) {
func TestUDPListener(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
toudpnet := func(net string) string {
@@ -104,13 +97,6 @@ func TestUDPListener(t *testing.T) {
checkFirstListener(t, tt.net, tt.laddr+":"+port, l1)
l2, err := ListenPacket(tt.net, tt.laddr+":"+port)
checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2)
- fd := l1.(*UDPConn).fd
- switch fd.family {
- case syscall.AF_INET:
- testIPv4UnicastSocketOptions(t, fd)
- case syscall.AF_INET6:
- testIPv6UnicastSocketOptions(t, fd)
- }
l1.Close()
}
}
@@ -118,7 +104,7 @@ func TestUDPListener(t *testing.T) {
func TestSimpleTCPListener(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Logf("skipping test on %q", runtime.GOOS)
+ t.Skipf("skipping test on %q", runtime.GOOS)
return
}
@@ -140,7 +126,7 @@ func TestSimpleTCPListener(t *testing.T) {
func TestSimpleUDPListener(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Logf("skipping test on %q", runtime.GOOS)
+ t.Skipf("skipping test on %q", runtime.GOOS)
return
}
@@ -183,9 +169,9 @@ var dualStackListenerTests = []struct {
// Test cases and expected results for the attemping 2nd listen on the same port
// 1st listen 2nd listen darwin freebsd linux openbsd
// ------------------------------------------------------------------------------------
- // "tcp" "" "tcp" "" - - - -
- // "tcp" "" "tcp" "0.0.0.0" - - - -
- // "tcp" "0.0.0.0" "tcp" "" - - - -
+ // "tcp" "" "tcp" "" - - - -
+ // "tcp" "" "tcp" "0.0.0.0" - - - -
+ // "tcp" "0.0.0.0" "tcp" "" - - - -
// ------------------------------------------------------------------------------------
// "tcp" "" "tcp" "[::]" - - - ok
// "tcp" "[::]" "tcp" "" - - - ok
@@ -242,8 +228,7 @@ var dualStackListenerTests = []struct {
func TestDualStackTCPListener(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
if !supportsIPv6 {
return
@@ -275,8 +260,7 @@ func TestDualStackTCPListener(t *testing.T) {
func TestDualStackUDPListener(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
if !supportsIPv6 {
return
@@ -468,44 +452,6 @@ func checkDualStackAddrFamily(t *testing.T, net, laddr string, fd *netFD) {
}
}
-func testIPv4UnicastSocketOptions(t *testing.T, fd *netFD) {
- _, err := ipv4TOS(fd)
- if err != nil {
- t.Fatalf("ipv4TOS failed: %v", err)
- }
- err = setIPv4TOS(fd, 1)
- if err != nil {
- t.Fatalf("setIPv4TOS failed: %v", err)
- }
- _, err = ipv4TTL(fd)
- if err != nil {
- t.Fatalf("ipv4TTL failed: %v", err)
- }
- err = setIPv4TTL(fd, 1)
- if err != nil {
- t.Fatalf("setIPv4TTL failed: %v", err)
- }
-}
-
-func testIPv6UnicastSocketOptions(t *testing.T, fd *netFD) {
- _, err := ipv6TrafficClass(fd)
- if err != nil {
- t.Fatalf("ipv6TrafficClass failed: %v", err)
- }
- err = setIPv6TrafficClass(fd, 1)
- if err != nil {
- t.Fatalf("setIPv6TrafficClass failed: %v", err)
- }
- _, err = ipv6HopLimit(fd)
- if err != nil {
- t.Fatalf("ipv6HopLimit failed: %v", err)
- }
- err = setIPv6HopLimit(fd, 1)
- if err != nil {
- t.Fatalf("setIPv6HopLimit failed: %v", err)
- }
-}
-
var prohibitionaryDialArgTests = []struct {
net string
addr string
@@ -517,8 +463,7 @@ var prohibitionaryDialArgTests = []struct {
func TestProhibitionaryDialArgs(t *testing.T) {
switch runtime.GOOS {
case "plan9":
- t.Logf("skipping test on %q", runtime.GOOS)
- return
+ t.Skipf("skipping test on %q", runtime.GOOS)
}
// This test requires both IPv6 and IPv6 IPv4-mapping functionality.
if !supportsIPv4map || testing.Short() || !*testExternal {
@@ -536,3 +481,36 @@ func TestProhibitionaryDialArgs(t *testing.T) {
}
}
}
+
+func TestWildWildcardListener(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ if testing.Short() || !*testExternal {
+ t.Skip("skipping test to avoid external network")
+ }
+
+ defer func() {
+ if recover() != nil {
+ t.Fatalf("panicked")
+ }
+ }()
+
+ if ln, err := Listen("tcp", ""); err == nil {
+ ln.Close()
+ }
+ if ln, err := ListenPacket("udp", ""); err == nil {
+ ln.Close()
+ }
+ if ln, err := ListenTCP("tcp", nil); err == nil {
+ ln.Close()
+ }
+ if ln, err := ListenUDP("udp", nil); err == nil {
+ ln.Close()
+ }
+ if ln, err := ListenIP("ip:icmp", nil); err == nil {
+ ln.Close()
+ }
+}
diff --git a/src/pkg/net/unix_test.go b/src/pkg/net/unix_test.go
new file mode 100644
index 000000000..2eaabe86e
--- /dev/null
+++ b/src/pkg/net/unix_test.go
@@ -0,0 +1,144 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !plan9,!windows
+
+package net
+
+import (
+ "bytes"
+ "os"
+ "reflect"
+ "runtime"
+ "syscall"
+ "testing"
+ "time"
+)
+
+func TestReadUnixgramWithUnnamedSocket(t *testing.T) {
+ addr := testUnixAddr()
+ la, err := ResolveUnixAddr("unixgram", addr)
+ if err != nil {
+ t.Fatalf("ResolveUnixAddr failed: %v", err)
+ }
+ c, err := ListenUnixgram("unixgram", la)
+ if err != nil {
+ t.Fatalf("ListenUnixgram failed: %v", err)
+ }
+ defer func() {
+ c.Close()
+ os.Remove(addr)
+ }()
+
+ off := make(chan bool)
+ data := [5]byte{1, 2, 3, 4, 5}
+
+ go func() {
+ defer func() { off <- true }()
+ s, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
+ if err != nil {
+ t.Errorf("syscall.Socket failed: %v", err)
+ return
+ }
+ defer syscall.Close(s)
+ rsa := &syscall.SockaddrUnix{Name: addr}
+ if err := syscall.Sendto(s, data[:], 0, rsa); err != nil {
+ t.Errorf("syscall.Sendto failed: %v", err)
+ return
+ }
+ }()
+
+ <-off
+ b := make([]byte, 64)
+ c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ n, from, err := c.ReadFrom(b)
+ if err != nil {
+ t.Errorf("UnixConn.ReadFrom failed: %v", err)
+ return
+ }
+ if from != nil {
+ t.Errorf("neighbor address is %v", from)
+ }
+ if !bytes.Equal(b[:n], data[:]) {
+ t.Errorf("got %v, want %v", b[:n], data[:])
+ return
+ }
+}
+
+func TestReadUnixgramWithZeroBytesBuffer(t *testing.T) {
+ // issue 4352: Recvfrom failed with "address family not
+ // supported by protocol family" if zero-length buffer provided
+
+ addr := testUnixAddr()
+ la, err := ResolveUnixAddr("unixgram", addr)
+ if err != nil {
+ t.Fatalf("ResolveUnixAddr failed: %v", err)
+ }
+ c, err := ListenUnixgram("unixgram", la)
+ if err != nil {
+ t.Fatalf("ListenUnixgram failed: %v", err)
+ }
+ defer func() {
+ c.Close()
+ os.Remove(addr)
+ }()
+
+ off := make(chan bool)
+ go func() {
+ defer func() { off <- true }()
+ c, err := DialUnix("unixgram", nil, la)
+ if err != nil {
+ t.Errorf("DialUnix failed: %v", err)
+ return
+ }
+ defer c.Close()
+ if _, err := c.Write([]byte{1, 2, 3, 4, 5}); err != nil {
+ t.Errorf("UnixConn.Write failed: %v", err)
+ return
+ }
+ }()
+
+ <-off
+ c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ var peer Addr
+ if _, peer, err = c.ReadFrom(nil); err != nil {
+ t.Errorf("UnixConn.ReadFrom failed: %v", err)
+ return
+ }
+ if peer != nil {
+ t.Errorf("peer adddress is %v", peer)
+ }
+}
+
+func TestUnixAutobind(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip("skipping: autobind is linux only")
+ }
+
+ laddr := &UnixAddr{Name: "", Net: "unixgram"}
+ c1, err := ListenUnixgram("unixgram", laddr)
+ if err != nil {
+ t.Fatalf("ListenUnixgram failed: %v", err)
+ }
+ defer c1.Close()
+
+ // retrieve the autobind address
+ autoAddr := c1.LocalAddr().(*UnixAddr)
+ if len(autoAddr.Name) <= 1 {
+ t.Fatalf("Invalid autobind address: %v", autoAddr)
+ }
+ if autoAddr.Name[0] != '@' {
+ t.Fatalf("Invalid autobind address: %v", autoAddr)
+ }
+
+ c2, err := DialUnix("unixgram", nil, autoAddr)
+ if err != nil {
+ t.Fatalf("DialUnix failed: %v", err)
+ }
+ defer c2.Close()
+
+ if !reflect.DeepEqual(c1.LocalAddr(), c2.RemoteAddr()) {
+ t.Fatalf("Expected autobind address %v, got %v", c1.LocalAddr(), c2.RemoteAddr())
+ }
+}
diff --git a/src/pkg/net/unixsock_plan9.go b/src/pkg/net/unixsock_plan9.go
index 7b4ae6bd1..00a0be5b0 100644
--- a/src/pkg/net/unixsock_plan9.go
+++ b/src/pkg/net/unixsock_plan9.go
@@ -7,100 +7,135 @@
package net
import (
+ "os"
"syscall"
"time"
)
-// UnixConn is an implementation of the Conn interface
-// for connections to Unix domain sockets.
-type UnixConn bool
-
-// Implementation of the Conn interface - see Conn for documentation.
+// UnixConn is an implementation of the Conn interface for connections
+// to Unix domain sockets.
+type UnixConn struct {
+ conn
+}
-// Read implements the Conn Read method.
-func (c *UnixConn) Read(b []byte) (n int, err error) {
- return 0, syscall.EPLAN9
+// ReadFromUnix reads a packet from c, copying the payload into b. It
+// returns the number of bytes copied into b and the source address of
+// the packet.
+//
+// ReadFromUnix can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetReadDeadline.
+func (c *UnixConn) ReadFromUnix(b []byte) (int, *UnixAddr, error) {
+ return 0, nil, syscall.EPLAN9
}
-// Write implements the Conn Write method.
-func (c *UnixConn) Write(b []byte) (n int, err error) {
- return 0, syscall.EPLAN9
+// ReadFrom implements the PacketConn ReadFrom method.
+func (c *UnixConn) ReadFrom(b []byte) (int, Addr, error) {
+ return 0, nil, syscall.EPLAN9
}
-// Close closes the Unix domain connection.
-func (c *UnixConn) Close() error {
- return syscall.EPLAN9
+// ReadMsgUnix reads a packet from c, copying the payload into b and
+// the associated out-of-band data into oob. It returns the number of
+// bytes copied into b, the number of bytes copied into oob, the flags
+// that were set on the packet, and the source address of the packet.
+func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
+ return 0, 0, 0, nil, syscall.EPLAN9
}
-// LocalAddr returns the local network address, a *UnixAddr.
-// Unlike in other protocols, LocalAddr is usually nil for dialed connections.
-func (c *UnixConn) LocalAddr() Addr {
- return nil
+// WriteToUnix writes a packet to addr via c, copying the payload from b.
+//
+// WriteToUnix can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetWriteDeadline. On packet-oriented connections, write timeouts
+// are rare.
+func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (int, error) {
+ return 0, syscall.EPLAN9
}
-// RemoteAddr returns the remote network address, a *UnixAddr.
-// Unlike in other protocols, RemoteAddr is usually nil for connections
-// accepted by a listener.
-func (c *UnixConn) RemoteAddr() Addr {
- return nil
+// WriteTo implements the PacketConn WriteTo method.
+func (c *UnixConn) WriteTo(b []byte, addr Addr) (int, error) {
+ return 0, syscall.EPLAN9
}
-// SetDeadline implements the Conn SetDeadline method.
-func (c *UnixConn) SetDeadline(t time.Time) error {
- return syscall.EPLAN9
+// WriteMsgUnix writes a packet to addr via c, copying the payload
+// from b and the associated out-of-band data from oob. It returns
+// the number of payload and out-of-band bytes written.
+func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
+ return 0, 0, syscall.EPLAN9
}
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *UnixConn) SetReadDeadline(t time.Time) error {
+// CloseRead shuts down the reading side of the Unix domain connection.
+// Most callers should just use Close.
+func (c *UnixConn) CloseRead() error {
return syscall.EPLAN9
}
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *UnixConn) SetWriteDeadline(t time.Time) error {
+// CloseWrite shuts down the writing side of the Unix domain connection.
+// Most callers should just use Close.
+func (c *UnixConn) CloseWrite() error {
return syscall.EPLAN9
}
-// ReadFrom implements the PacketConn ReadFrom method.
-func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
- err = syscall.EPLAN9
- return
+// DialUnix connects to the remote address raddr on the network net,
+// which must be "unix", "unixgram" or "unixpacket". If laddr is not
+// nil, it is used as the local address for the connection.
+func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
+ return dialUnix(net, laddr, raddr, noDeadline)
}
-// WriteTo implements the PacketConn WriteTo method.
-func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) {
- err = syscall.EPLAN9
- return
+func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
+ return nil, syscall.EPLAN9
}
-// DialUnix connects to the remote address raddr on the network net,
-// which must be "unix" or "unixgram". If laddr is not nil, it is used
-// as the local address for the connection.
-func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err error) {
+// UnixListener is a Unix domain socket listener. Clients should
+// typically use variables of type Listener instead of assuming Unix
+// domain sockets.
+type UnixListener struct{}
+
+// ListenUnix announces on the Unix domain socket laddr and returns a
+// Unix listener. The network net must be "unix" or "unixpacket".
+func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
return nil, syscall.EPLAN9
}
-// UnixListener is a Unix domain socket listener.
-// Clients should typically use variables of type Listener
-// instead of assuming Unix domain sockets.
-type UnixListener bool
-
-// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener.
-// Net must be "unix" (stream sockets).
-func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err error) {
+// AcceptUnix accepts the next incoming call and returns the new
+// connection and the remote address.
+func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
return nil, syscall.EPLAN9
}
-// Accept implements the Accept method in the Listener interface;
-// it waits for the next call and returns a generic Conn.
-func (l *UnixListener) Accept() (c Conn, err error) {
+// Accept implements the Accept method in the Listener interface; it
+// waits for the next call and returns a generic Conn.
+func (l *UnixListener) Accept() (Conn, error) {
return nil, syscall.EPLAN9
}
-// Close stops listening on the Unix address.
-// Already accepted connections are not closed.
+// Close stops listening on the Unix address. Already accepted
+// connections are not closed.
func (l *UnixListener) Close() error {
return syscall.EPLAN9
}
// Addr returns the listener's network address.
func (l *UnixListener) Addr() Addr { return nil }
+
+// SetDeadline sets the deadline associated with the listener.
+// A zero time value disables the deadline.
+func (l *UnixListener) SetDeadline(t time.Time) error {
+ return syscall.EPLAN9
+}
+
+// File returns a copy of the underlying os.File, set to blocking
+// mode. It is the caller's responsibility to close f when finished.
+// Closing l does not affect f, and closing f does not affect l.
+func (l *UnixListener) File() (*os.File, error) {
+ return nil, syscall.EPLAN9
+}
+
+// ListenUnixgram listens for incoming Unix datagram packets addressed
+// to the local address laddr. The returned connection c's ReadFrom
+// and WriteTo methods can be used to receive and send packets with
+// per-packet addressing. The network net must be "unixgram".
+func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) {
+ return nil, syscall.EPLAN9
+}
diff --git a/src/pkg/net/unixsock_posix.go b/src/pkg/net/unixsock_posix.go
index 57d784c71..6d6ce3f5e 100644
--- a/src/pkg/net/unixsock_posix.go
+++ b/src/pkg/net/unixsock_posix.go
@@ -9,29 +9,27 @@
package net
import (
+ "errors"
"os"
"syscall"
"time"
)
-func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err error) {
+func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (*netFD, error) {
var sotype int
switch net {
- default:
- return nil, UnknownNetworkError(net)
case "unix":
sotype = syscall.SOCK_STREAM
case "unixgram":
sotype = syscall.SOCK_DGRAM
case "unixpacket":
sotype = syscall.SOCK_SEQPACKET
+ default:
+ return nil, UnknownNetworkError(net)
}
var la, ra syscall.Sockaddr
switch mode {
- default:
- panic("unixSocket mode " + mode)
-
case "dial":
if laddr != nil {
la = &syscall.SockaddrUnix{Name: laddr.Name}
@@ -41,15 +39,10 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err
} else if sotype != syscall.SOCK_DGRAM || laddr == nil {
return nil, &OpError{Op: mode, Net: net, Err: errMissingAddress}
}
-
case "listen":
- if laddr == nil {
- return nil, &OpError{mode, net, nil, errMissingAddress}
- }
la = &syscall.SockaddrUnix{Name: laddr.Name}
- if raddr != nil {
- return nil, &OpError{Op: mode, Net: net, Addr: raddr, Err: &AddrError{Err: "unexpected remote address", Addr: raddr.String()}}
- }
+ default:
+ return nil, errors.New("unknown mode: " + mode)
}
f := sockaddrToUnix
@@ -59,15 +52,16 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err
f = sockaddrToUnixpacket
}
- fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, f)
+ fd, err := socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f)
if err != nil {
- goto Error
+ goto error
}
return fd, nil
-Error:
+error:
addr := raddr
- if mode == "listen" {
+ switch mode {
+ case "listen":
addr = laddr
}
return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: err}
@@ -108,110 +102,21 @@ func sotypeToNet(sotype int) string {
return ""
}
-// UnixConn is an implementation of the Conn interface
-// for connections to Unix domain sockets.
+// UnixConn is an implementation of the Conn interface for connections
+// to Unix domain sockets.
type UnixConn struct {
- fd *netFD
-}
-
-func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{fd} }
-
-func (c *UnixConn) ok() bool { return c != nil && c.fd != nil }
-
-// Implementation of the Conn interface - see Conn for documentation.
-
-// Read implements the Conn Read method.
-func (c *UnixConn) Read(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Read(b)
-}
-
-// Write implements the Conn Write method.
-func (c *UnixConn) Write(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Write(b)
-}
-
-// Close closes the Unix domain connection.
-func (c *UnixConn) Close() error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return c.fd.Close()
-}
-
-// LocalAddr returns the local network address, a *UnixAddr.
-// Unlike in other protocols, LocalAddr is usually nil for dialed connections.
-func (c *UnixConn) LocalAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.laddr
+ conn
}
-// RemoteAddr returns the remote network address, a *UnixAddr.
-// Unlike in other protocols, RemoteAddr is usually nil for connections
-// accepted by a listener.
-func (c *UnixConn) RemoteAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.raddr
-}
+func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{conn{fd}} }
-// SetDeadline implements the Conn SetDeadline method.
-func (c *UnixConn) SetDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setDeadline(c.fd, t)
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *UnixConn) SetReadDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadDeadline(c.fd, t)
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *UnixConn) SetWriteDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteDeadline(c.fd, t)
-}
-
-// SetReadBuffer sets the size of the operating system's
-// receive buffer associated with the connection.
-func (c *UnixConn) SetReadBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadBuffer(c.fd, bytes)
-}
-
-// SetWriteBuffer sets the size of the operating system's
-// transmit buffer associated with the connection.
-func (c *UnixConn) SetWriteBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteBuffer(c.fd, bytes)
-}
-
-// ReadFromUnix reads a packet from c, copying the payload into b.
-// It returns the number of bytes copied into b and the source address
-// of the packet.
+// ReadFromUnix reads a packet from c, copying the payload into b. It
+// returns the number of bytes copied into b and the source address of
+// the packet.
//
-// ReadFromUnix can be made to time out and return
-// an error with Timeout() == true after a fixed time limit;
-// see SetDeadline and SetReadDeadline.
+// ReadFromUnix can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetReadDeadline.
func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) {
if !c.ok() {
return 0, nil, syscall.EINVAL
@@ -219,26 +124,46 @@ func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) {
n, sa, err := c.fd.ReadFrom(b)
switch sa := sa.(type) {
case *syscall.SockaddrUnix:
- addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)}
+ if sa.Name != "" {
+ addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)}
+ }
}
return
}
// ReadFrom implements the PacketConn ReadFrom method.
-func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
+func (c *UnixConn) ReadFrom(b []byte) (int, Addr, error) {
if !c.ok() {
return 0, nil, syscall.EINVAL
}
- n, uaddr, err := c.ReadFromUnix(b)
- return n, uaddr.toAddr(), err
+ n, addr, err := c.ReadFromUnix(b)
+ return n, addr.toAddr(), err
+}
+
+// ReadMsgUnix reads a packet from c, copying the payload into b and
+// the associated out-of-band data into oob. It returns the number of
+// bytes copied into b, the number of bytes copied into oob, the flags
+// that were set on the packet, and the source address of the packet.
+func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
+ if !c.ok() {
+ return 0, 0, 0, nil, syscall.EINVAL
+ }
+ n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob)
+ switch sa := sa.(type) {
+ case *syscall.SockaddrUnix:
+ if sa.Name != "" {
+ addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)}
+ }
+ }
+ return
}
// WriteToUnix writes a packet to addr via c, copying the payload from b.
//
-// WriteToUnix can be made to time out and return
-// an error with Timeout() == true after a fixed time limit;
-// see SetDeadline and SetWriteDeadline.
-// On packet-oriented connections, write timeouts are rare.
+// WriteToUnix can be made to time out and return an error with
+// Timeout() == true after a fixed time limit; see SetDeadline and
+// SetWriteDeadline. On packet-oriented connections, write timeouts
+// are rare.
func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) {
if !c.ok() {
return 0, syscall.EINVAL
@@ -262,26 +187,9 @@ func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) {
return c.WriteToUnix(b, a)
}
-// ReadMsgUnix reads a packet from c, copying the payload into b
-// and the associated out-of-band data into oob.
-// It returns the number of bytes copied into b, the number of
-// bytes copied into oob, the flags that were set on the packet,
-// and the source address of the packet.
-func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
- if !c.ok() {
- return 0, 0, 0, nil, syscall.EINVAL
- }
- n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob)
- switch sa := sa.(type) {
- case *syscall.SockaddrUnix:
- addr = &UnixAddr{sa.Name, sotypeToNet(c.fd.sotype)}
- }
- return
-}
-
-// WriteMsgUnix writes a packet to addr via c, copying the payload from b
-// and the associated out-of-band data from oob. It returns the number
-// of payload and out-of-band bytes written.
+// WriteMsgUnix writes a packet to addr via c, copying the payload
+// from b and the associated out-of-band data from oob. It returns
+// the number of payload and out-of-band bytes written.
func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
if !c.ok() {
return 0, 0, syscall.EINVAL
@@ -296,40 +204,64 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err
return c.fd.WriteMsg(b, oob, nil)
}
-// File returns a copy of the underlying os.File, set to blocking mode.
-// It is the caller's responsibility to close f when finished.
-// Closing c does not affect f, and closing f does not affect c.
-func (c *UnixConn) File() (f *os.File, err error) { return c.fd.dup() }
+// CloseRead shuts down the reading side of the Unix domain connection.
+// Most callers should just use Close.
+func (c *UnixConn) CloseRead() error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return c.fd.CloseRead()
+}
+
+// CloseWrite shuts down the writing side of the Unix domain connection.
+// Most callers should just use Close.
+func (c *UnixConn) CloseWrite() error {
+ if !c.ok() {
+ return syscall.EINVAL
+ }
+ return c.fd.CloseWrite()
+}
// DialUnix connects to the remote address raddr on the network net,
-// which must be "unix" or "unixgram". If laddr is not nil, it is used
-// as the local address for the connection.
+// which must be "unix", "unixgram" or "unixpacket". If laddr is not
+// nil, it is used as the local address for the connection.
func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
- fd, err := unixSocket(net, laddr, raddr, "dial")
+ return dialUnix(net, laddr, raddr, noDeadline)
+}
+
+func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
+ switch net {
+ case "unix", "unixgram", "unixpacket":
+ default:
+ return nil, UnknownNetworkError(net)
+ }
+ fd, err := unixSocket(net, laddr, raddr, "dial", deadline)
if err != nil {
return nil, err
}
return newUnixConn(fd), nil
}
-// UnixListener is a Unix domain socket listener.
-// Clients should typically use variables of type Listener
-// instead of assuming Unix domain sockets.
+// UnixListener is a Unix domain socket listener. Clients should
+// typically use variables of type Listener instead of assuming Unix
+// domain sockets.
type UnixListener struct {
fd *netFD
path string
}
-// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener.
-// Net must be "unix" (stream sockets).
+// ListenUnix announces on the Unix domain socket laddr and returns a
+// Unix listener. The network net must be "unix" or "unixpacket".
func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
- if net != "unix" && net != "unixgram" && net != "unixpacket" {
+ switch net {
+ case "unix", "unixpacket":
+ default:
return nil, UnknownNetworkError(net)
}
- if laddr != nil {
- laddr = &UnixAddr{laddr.Name, net} // make our own copy
+ if laddr == nil {
+ return nil, &OpError{"listen", net, nil, errMissingAddress}
}
- fd, err := unixSocket(net, laddr, nil, "listen")
+ fd, err := unixSocket(net, laddr, nil, "listen", noDeadline)
if err != nil {
return nil, err
}
@@ -341,8 +273,8 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
return &UnixListener{fd, laddr.Name}, nil
}
-// AcceptUnix accepts the next incoming call and returns the new connection
-// and the remote address.
+// AcceptUnix accepts the next incoming call and returns the new
+// connection and the remote address.
func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
if l == nil || l.fd == nil {
return nil, syscall.EINVAL
@@ -355,8 +287,8 @@ func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
return c, nil
}
-// Accept implements the Accept method in the Listener interface;
-// it waits for the next call and returns a generic Conn.
+// Accept implements the Accept method in the Listener interface; it
+// waits for the next call and returns a generic Conn.
func (l *UnixListener) Accept() (c Conn, err error) {
c1, err := l.AcceptUnix()
if err != nil {
@@ -365,8 +297,8 @@ func (l *UnixListener) Accept() (c Conn, err error) {
return c1, nil
}
-// Close stops listening on the Unix address.
-// Already accepted connections are not closed.
+// Close stops listening on the Unix address. Already accepted
+// connections are not closed.
func (l *UnixListener) Close() error {
if l == nil || l.fd == nil {
return syscall.EINVAL
@@ -385,9 +317,7 @@ func (l *UnixListener) Close() error {
if l.path[0] != '@' {
syscall.Unlink(l.path)
}
- err := l.fd.Close()
- l.fd = nil
- return err
+ return l.fd.Close()
}
// Addr returns the listener's network address.
@@ -402,16 +332,16 @@ func (l *UnixListener) SetDeadline(t time.Time) (err error) {
return setDeadline(l.fd, t)
}
-// File returns a copy of the underlying os.File, set to blocking mode.
-// It is the caller's responsibility to close f when finished.
+// File returns a copy of the underlying os.File, set to blocking
+// mode. It is the caller's responsibility to close f when finished.
// Closing l does not affect f, and closing f does not affect l.
func (l *UnixListener) File() (f *os.File, err error) { return l.fd.dup() }
-// ListenUnixgram listens for incoming Unix datagram packets addressed to the
-// local address laddr. The returned connection c's ReadFrom
-// and WriteTo methods can be used to receive and send UDP
-// packets with per-packet addressing. The network net must be "unixgram".
-func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
+// ListenUnixgram listens for incoming Unix datagram packets addressed
+// to the local address laddr. The returned connection c's ReadFrom
+// and WriteTo methods can be used to receive and send packets with
+// per-packet addressing. The network net must be "unixgram".
+func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) {
switch net {
case "unixgram":
default:
@@ -420,9 +350,9 @@ func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) {
if laddr == nil {
return nil, &OpError{"listen", net, nil, errMissingAddress}
}
- fd, err := unixSocket(net, laddr, nil, "listen")
+ fd, err := unixSocket(net, laddr, nil, "listen", noDeadline)
if err != nil {
return nil, err
}
- return newUDPConn(fd), nil
+ return newUnixConn(fd), nil
}
diff --git a/src/pkg/net/url/url.go b/src/pkg/net/url/url.go
index 17bf0d3a3..a39964ea1 100644
--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -7,7 +7,9 @@
package url
import (
+ "bytes"
"errors"
+ "sort"
"strconv"
"strings"
)
@@ -218,11 +220,18 @@ func escape(s string, mode encoding) string {
//
// scheme:opaque[?query][#fragment]
//
+// Note that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.
+// A consequence is that it is impossible to tell which slashes in the Path were
+// slashes in the raw URL and which were %2f. This distinction is rarely important,
+// but when it is a client must use other routines to parse the raw URL or construct
+// the parsed URL. For example, an HTTP server can consult req.RequestURI, and
+// an HTTP client can use URL{Host: "example.com", Opaque: "//example.com/Go%2f"}
+// instead of URL{Host: "example.com", Path: "/Go/"}.
type URL struct {
Scheme string
Opaque string // encoded opaque data
User *Userinfo // username and password information
- Host string
+ Host string // host or host:port
Path string
RawQuery string // encoded query values, without '?'
Fragment string // fragment for references, without '#'
@@ -359,11 +368,17 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) {
}
url = new(URL)
+ if rawurl == "*" {
+ url.Path = "*"
+ return
+ }
+
// Split off possible leading "http:", "mailto:", etc.
// Cannot contain escaped characters.
if url.Scheme, rest, err = getscheme(rawurl); err != nil {
goto Error
}
+ url.Scheme = strings.ToLower(url.Scheme)
rest, url.RawQuery = split(rest, '?', true)
@@ -379,7 +394,7 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) {
}
}
- if (url.Scheme != "" || !viaRequest) && strings.HasPrefix(rest, "//") && !strings.HasPrefix(rest, "///") {
+ if (url.Scheme != "" || !viaRequest && !strings.HasPrefix(rest, "///")) && strings.HasPrefix(rest, "//") {
var authority string
authority, rest = split(rest[2:], '/', false)
url.User, url.Host, err = parseAuthority(authority)
@@ -427,30 +442,35 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) {
// String reassembles the URL into a valid URL string.
func (u *URL) String() string {
- // TODO: Rewrite to use bytes.Buffer
- result := ""
+ var buf bytes.Buffer
if u.Scheme != "" {
- result += u.Scheme + ":"
+ buf.WriteString(u.Scheme)
+ buf.WriteByte(':')
}
if u.Opaque != "" {
- result += u.Opaque
+ buf.WriteString(u.Opaque)
} else {
- if u.Host != "" || u.User != nil {
- result += "//"
+ if u.Scheme != "" || u.Host != "" || u.User != nil {
+ buf.WriteString("//")
if u := u.User; u != nil {
- result += u.String() + "@"
+ buf.WriteString(u.String())
+ buf.WriteByte('@')
+ }
+ if h := u.Host; h != "" {
+ buf.WriteString(h)
}
- result += u.Host
}
- result += escape(u.Path, encodePath)
+ buf.WriteString(escape(u.Path, encodePath))
}
if u.RawQuery != "" {
- result += "?" + u.RawQuery
+ buf.WriteByte('?')
+ buf.WriteString(u.RawQuery)
}
if u.Fragment != "" {
- result += "#" + escape(u.Fragment, encodeFragment)
+ buf.WriteByte('#')
+ buf.WriteString(escape(u.Fragment, encodeFragment))
}
- return result
+ return buf.String()
}
// Values maps a string key to a list of values.
@@ -519,12 +539,16 @@ func parseQuery(m Values, query string) (err error) {
}
key, err1 := QueryUnescape(key)
if err1 != nil {
- err = err1
+ if err == nil {
+ err = err1
+ }
continue
}
value, err1 = QueryUnescape(value)
if err1 != nil {
- err = err1
+ if err == nil {
+ err = err1
+ }
continue
}
m[key] = append(m[key], value)
@@ -538,14 +562,24 @@ func (v Values) Encode() string {
if v == nil {
return ""
}
- parts := make([]string, 0, len(v)) // will be large enough for most uses
- for k, vs := range v {
+ var buf bytes.Buffer
+ keys := make([]string, 0, len(v))
+ for k := range v {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ vs := v[k]
prefix := QueryEscape(k) + "="
for _, v := range vs {
- parts = append(parts, prefix+QueryEscape(v))
+ if buf.Len() > 0 {
+ buf.WriteByte('&')
+ }
+ buf.WriteString(prefix)
+ buf.WriteString(QueryEscape(v))
}
}
- return strings.Join(parts, "&")
+ return buf.String()
}
// resolvePath applies special path segments from refs and applies
@@ -556,23 +590,33 @@ func resolvePath(basepath string, refpath string) string {
if len(base) == 0 {
base = []string{""}
}
+
+ rm := true
for idx, ref := range refs {
switch {
case ref == ".":
- base[len(base)-1] = ""
+ if idx == 0 {
+ base[len(base)-1] = ""
+ rm = true
+ } else {
+ rm = false
+ }
case ref == "..":
newLen := len(base) - 1
if newLen < 1 {
newLen = 1
}
base = base[0:newLen]
- base[len(base)-1] = ""
+ if rm {
+ base[len(base)-1] = ""
+ }
default:
if idx == 0 || base[len(base)-1] == "" {
base[len(base)-1] = ref
} else {
base = append(base, ref)
}
+ rm = false
}
}
return strings.Join(base, "/")
@@ -650,6 +694,10 @@ func (u *URL) RequestURI() string {
if result == "" {
result = "/"
}
+ } else {
+ if strings.HasPrefix(result, "//") {
+ result = u.Scheme + ":" + result
+ }
}
if u.RawQuery != "" {
result += "?" + u.RawQuery
diff --git a/src/pkg/net/url/url_test.go b/src/pkg/net/url/url_test.go
index 75e8abe4e..4c4f406c2 100644
--- a/src/pkg/net/url/url_test.go
+++ b/src/pkg/net/url/url_test.go
@@ -7,6 +7,7 @@ package url
import (
"fmt"
"reflect"
+ "strings"
"testing"
)
@@ -121,14 +122,14 @@ var urltests = []URLTest{
},
"http:%2f%2fwww.google.com/?q=go+language",
},
- // non-authority
+ // non-authority with path
{
"mailto:/webmaster@golang.org",
&URL{
Scheme: "mailto",
Path: "/webmaster@golang.org",
},
- "",
+ "mailto:///webmaster@golang.org", // unfortunate compromise
},
// non-authority
{
@@ -241,6 +242,24 @@ var urltests = []URLTest{
},
"http://www.google.com/?q=go+language#foo&bar",
},
+ {
+ "file:///home/adg/rabbits",
+ &URL{
+ Scheme: "file",
+ Host: "",
+ Path: "/home/adg/rabbits",
+ },
+ "file:///home/adg/rabbits",
+ },
+ // case-insensitive scheme
+ {
+ "MaIlTo:webmaster@golang.org",
+ &URL{
+ Scheme: "mailto",
+ Opaque: "webmaster@golang.org",
+ },
+ "mailto:webmaster@golang.org",
+ },
}
// more useful string for debugging than fmt's struct printer
@@ -270,13 +289,37 @@ func DoTest(t *testing.T, parse func(string) (*URL, error), name string, tests [
}
}
+func BenchmarkString(b *testing.B) {
+ b.StopTimer()
+ b.ReportAllocs()
+ for _, tt := range urltests {
+ u, err := Parse(tt.in)
+ if err != nil {
+ b.Errorf("Parse(%q) returned error %s", tt.in, err)
+ continue
+ }
+ if tt.roundtrip == "" {
+ continue
+ }
+ b.StartTimer()
+ var g string
+ for i := 0; i < b.N; i++ {
+ g = u.String()
+ }
+ b.StopTimer()
+ if w := tt.roundtrip; g != w {
+ b.Errorf("Parse(%q).String() == %q, want %q", tt.in, g, w)
+ }
+ }
+}
+
func TestParse(t *testing.T) {
DoTest(t, Parse, "Parse", urltests)
}
const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path"
-var parseRequestUrlTests = []struct {
+var parseRequestURLTests = []struct {
url string
expectedValid bool
}{
@@ -288,10 +331,11 @@ var parseRequestUrlTests = []struct {
{"//not.a.user@%66%6f%6f.com/just/a/path/also", true},
{"foo.html", false},
{"../dir/", false},
+ {"*", true},
}
func TestParseRequestURI(t *testing.T) {
- for _, test := range parseRequestUrlTests {
+ for _, test := range parseRequestURLTests {
_, err := ParseRequestURI(test.url)
valid := err == nil
if valid != test.expectedValid {
@@ -453,20 +497,24 @@ func TestEscape(t *testing.T) {
//}
type EncodeQueryTest struct {
- m Values
- expected string
- expected1 string
+ m Values
+ expected string
}
var encodeQueryTests = []EncodeQueryTest{
- {nil, "", ""},
- {Values{"q": {"puppies"}, "oe": {"utf8"}}, "q=puppies&oe=utf8", "oe=utf8&q=puppies"},
- {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7", "q=dogs&q=%26&q=7"},
+ {nil, ""},
+ {Values{"q": {"puppies"}, "oe": {"utf8"}}, "oe=utf8&q=puppies"},
+ {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7"},
+ {Values{
+ "a": {"a1", "a2", "a3"},
+ "b": {"b1", "b2", "b3"},
+ "c": {"c1", "c2", "c3"},
+ }, "a=a1&a=a2&a=a3&b=b1&b=b2&b=b3&c=c1&c=c2&c=c3"},
}
func TestEncodeQuery(t *testing.T) {
for _, tt := range encodeQueryTests {
- if q := tt.m.Encode(); q != tt.expected && q != tt.expected1 {
+ if q := tt.m.Encode(); q != tt.expected {
t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected)
}
}
@@ -531,6 +579,15 @@ var resolveReferenceTests = []struct {
{"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"},
{"http://foo.com/bar", "..", "http://foo.com/"},
{"http://foo.com/bar/baz", "./..", "http://foo.com/"},
+ // ".." in the middle (issue 3560)
+ {"http://foo.com/bar/baz", "quux/dotdot/../tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/../tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/.././tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/./../tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/././../../tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/./.././../tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/dotdot/./../../.././././tail", "http://foo.com/bar/quux/tail"},
+ {"http://foo.com/bar/baz", "quux/./dotdot/../dotdot/../dot/./tail/..", "http://foo.com/bar/quux/dot"},
// "." and ".." in the base aren't special
{"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/./dotdot/../baz"},
@@ -741,6 +798,24 @@ var requritests = []RequestURITest{
},
"/a%20b",
},
+ // golang.org/issue/4860 variant 1
+ {
+ &URL{
+ Scheme: "http",
+ Host: "example.com",
+ Opaque: "/%2F/%2F/",
+ },
+ "/%2F/%2F/",
+ },
+ // golang.org/issue/4860 variant 2
+ {
+ &URL{
+ Scheme: "http",
+ Host: "example.com",
+ Opaque: "//other.example.com/%2F/%2F/",
+ },
+ "http://other.example.com/%2F/%2F/",
+ },
{
&URL{
Scheme: "http",
@@ -775,3 +850,13 @@ func TestRequestURI(t *testing.T) {
}
}
}
+
+func TestParseFailure(t *testing.T) {
+ // Test that the first parse error is returned.
+ const url = "%gh&%ij"
+ _, err := ParseQuery(url)
+ errStr := fmt.Sprint(err)
+ if !strings.Contains(errStr, "%gh") {
+ t.Errorf(`ParseQuery(%q) returned error %q, want something containing %q"`, url, errStr, "%gh")
+ }
+}
diff --git a/src/pkg/os/dir_plan9.go b/src/pkg/os/dir_plan9.go
index 7fa4c7f44..8195c02a4 100644
--- a/src/pkg/os/dir_plan9.go
+++ b/src/pkg/os/dir_plan9.go
@@ -5,15 +5,11 @@
package os
import (
- "errors"
"io"
"syscall"
)
-var errShortStat = errors.New("short stat message")
-var errBadStat = errors.New("bad stat message format")
-
-func (file *File) readdir(n int) (fi []FileInfo, err error) {
+func (file *File) readdir(n int) ([]FileInfo, error) {
// If this file has no dirinfo, create one.
if file.dirinfo == nil {
file.dirinfo = new(dirInfo)
@@ -24,44 +20,47 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
size = 100
n = -1
}
- result := make([]FileInfo, 0, size) // Empty with room to grow.
+ fi := make([]FileInfo, 0, size) // Empty with room to grow.
for n != 0 {
- // Refill the buffer if necessary
+ // Refill the buffer if necessary.
if d.bufp >= d.nbuf {
- d.bufp = 0
- var e error
- d.nbuf, e = file.Read(d.buf[:])
- if e != nil && e != io.EOF {
- return result, &PathError{"readdir", file.name, e}
- }
- if e == io.EOF {
- break
+ nb, err := file.Read(d.buf[:])
+
+ // Update the buffer state before checking for errors.
+ d.bufp, d.nbuf = 0, nb
+
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return fi, &PathError{"readdir", file.name, err}
}
- if d.nbuf < syscall.STATFIXLEN {
- return result, &PathError{"readdir", file.name, errShortStat}
+ if nb < syscall.STATFIXLEN {
+ return fi, &PathError{"readdir", file.name, syscall.ErrShortStat}
}
}
- // Get a record from buffer
- m, _ := gbit16(d.buf[d.bufp:])
- m += 2
+ // Get a record from the buffer.
+ b := d.buf[d.bufp:]
+ m := int(uint16(b[0])|uint16(b[1])<<8) + 2
if m < syscall.STATFIXLEN {
- return result, &PathError{"readdir", file.name, errShortStat}
+ return fi, &PathError{"readdir", file.name, syscall.ErrShortStat}
}
- dir, e := UnmarshalDir(d.buf[d.bufp : d.bufp+int(m)])
- if e != nil {
- return result, &PathError{"readdir", file.name, e}
+
+ dir, err := syscall.UnmarshalDir(b[:m])
+ if err != nil {
+ return fi, &PathError{"readdir", file.name, err}
}
- result = append(result, fileInfoFromStat(dir))
+ fi = append(fi, fileInfoFromStat(dir))
- d.bufp += int(m)
+ d.bufp += m
n--
}
- if n >= 0 && len(result) == 0 {
- return result, io.EOF
+ if n >= 0 && len(fi) == 0 {
+ return fi, io.EOF
}
- return result, nil
+ return fi, nil
}
func (file *File) readdirnames(n int) (names []string, err error) {
@@ -72,205 +71,3 @@ func (file *File) readdirnames(n int) (names []string, err error) {
}
return
}
-
-type Dir struct {
- // system-modified data
- Type uint16 // server type
- Dev uint32 // server subtype
- // file data
- Qid Qid // unique id from server
- Mode uint32 // permissions
- Atime uint32 // last read time
- Mtime uint32 // last write time
- Length uint64 // file length
- Name string // last element of path
- Uid string // owner name
- Gid string // group name
- Muid string // last modifier name
-}
-
-type Qid struct {
- Path uint64 // the file server's unique identification for the file
- Vers uint32 // version number for given Path
- Type uint8 // the type of the file (syscall.QTDIR for example)
-}
-
-var nullDir = Dir{
- ^uint16(0),
- ^uint32(0),
- Qid{^uint64(0), ^uint32(0), ^uint8(0)},
- ^uint32(0),
- ^uint32(0),
- ^uint32(0),
- ^uint64(0),
- "",
- "",
- "",
- "",
-}
-
-// Null assigns members of d with special "don't care" values indicating
-// they should not be written by syscall.Wstat.
-func (d *Dir) Null() {
- *d = nullDir
-}
-
-// pdir appends a 9P Stat message based on the contents of Dir d to a byte slice b.
-func pdir(b []byte, d *Dir) []byte {
- n := len(b)
- b = pbit16(b, 0) // length, filled in later
- b = pbit16(b, d.Type)
- b = pbit32(b, d.Dev)
- b = pqid(b, d.Qid)
- b = pbit32(b, d.Mode)
- b = pbit32(b, d.Atime)
- b = pbit32(b, d.Mtime)
- b = pbit64(b, d.Length)
- b = pstring(b, d.Name)
- b = pstring(b, d.Uid)
- b = pstring(b, d.Gid)
- b = pstring(b, d.Muid)
- pbit16(b[0:n], uint16(len(b)-(n+2)))
- return b
-}
-
-// UnmarshalDir reads a 9P Stat message from a 9P protocol message stored in b,
-// returning the corresponding Dir struct.
-func UnmarshalDir(b []byte) (d *Dir, err error) {
- n := uint16(0)
- n, b = gbit16(b)
-
- if int(n) != len(b) {
- return nil, errBadStat
- }
-
- d = new(Dir)
- d.Type, b = gbit16(b)
- d.Dev, b = gbit32(b)
- d.Qid, b = gqid(b)
- d.Mode, b = gbit32(b)
- d.Atime, b = gbit32(b)
- d.Mtime, b = gbit32(b)
- d.Length, b = gbit64(b)
- d.Name, b = gstring(b)
- d.Uid, b = gstring(b)
- d.Gid, b = gstring(b)
- d.Muid, b = gstring(b)
-
- if len(b) != 0 {
- return nil, errBadStat
- }
-
- return d, nil
-}
-
-// gqid reads the qid part of a 9P Stat message from a 9P protocol message stored in b,
-// returning the corresponding Qid struct and the remaining slice of b.
-func gqid(b []byte) (Qid, []byte) {
- var q Qid
- q.Path, b = gbit64(b)
- q.Vers, b = gbit32(b)
- q.Type, b = gbit8(b)
- return q, b
-}
-
-// pqid appends a Qid struct q to a 9P message b.
-func pqid(b []byte, q Qid) []byte {
- b = pbit64(b, q.Path)
- b = pbit32(b, q.Vers)
- b = pbit8(b, q.Type)
- return b
-}
-
-// gbit8 reads a byte-sized numeric value from a 9P protocol message stored in b,
-// returning the value and the remaining slice of b.
-func gbit8(b []byte) (uint8, []byte) {
- return uint8(b[0]), b[1:]
-}
-
-// gbit16 reads a 16-bit numeric value from a 9P protocol message stored in b,
-// returning the value and the remaining slice of b.
-func gbit16(b []byte) (uint16, []byte) {
- return uint16(b[0]) | uint16(b[1])<<8, b[2:]
-}
-
-// gbit32 reads a 32-bit numeric value from a 9P protocol message stored in b,
-// returning the value and the remaining slice of b.
-func gbit32(b []byte) (uint32, []byte) {
- return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
-}
-
-// gbit64 reads a 64-bit numeric value from a 9P protocol message stored in b,
-// returning the value and the remaining slice of b.
-func gbit64(b []byte) (uint64, []byte) {
- lo, b := gbit32(b)
- hi, b := gbit32(b)
- return uint64(hi)<<32 | uint64(lo), b
-}
-
-// gstring reads a string from a 9P protocol message stored in b,
-// returning the value as a Go string and the remaining slice of b.
-func gstring(b []byte) (string, []byte) {
- n, b := gbit16(b)
- return string(b[0:n]), b[n:]
-}
-
-// pbit8 appends a byte-sized numeric value x to a 9P message b.
-func pbit8(b []byte, x uint8) []byte {
- n := len(b)
- if n+1 > cap(b) {
- nb := make([]byte, n, 100+2*cap(b))
- copy(nb, b)
- b = nb
- }
- b = b[0 : n+1]
- b[n] = x
- return b
-}
-
-// pbit16 appends a 16-bit numeric value x to a 9P message b.
-func pbit16(b []byte, x uint16) []byte {
- n := len(b)
- if n+2 > cap(b) {
- nb := make([]byte, n, 100+2*cap(b))
- copy(nb, b)
- b = nb
- }
- b = b[0 : n+2]
- b[n] = byte(x)
- b[n+1] = byte(x >> 8)
- return b
-}
-
-// pbit32 appends a 32-bit numeric value x to a 9P message b.
-func pbit32(b []byte, x uint32) []byte {
- n := len(b)
- if n+4 > cap(b) {
- nb := make([]byte, n, 100+2*cap(b))
- copy(nb, b)
- b = nb
- }
- b = b[0 : n+4]
- b[n] = byte(x)
- b[n+1] = byte(x >> 8)
- b[n+2] = byte(x >> 16)
- b[n+3] = byte(x >> 24)
- return b
-}
-
-// pbit64 appends a 64-bit numeric value x to a 9P message b.
-func pbit64(b []byte, x uint64) []byte {
- b = pbit32(b, uint32(x))
- b = pbit32(b, uint32(x>>32))
- return b
-}
-
-// pstring appends a Go string s to a 9P message b.
-func pstring(b []byte, s string) []byte {
- if len(s) >= 1<<16 {
- panic(errors.New("string too long"))
- }
- b = pbit16(b, uint16(len(s)))
- b = append(b, s...)
- return b
-}
diff --git a/src/pkg/os/doc.go b/src/pkg/os/doc.go
index 6a531e0d7..2cc17530c 100644
--- a/src/pkg/os/doc.go
+++ b/src/pkg/os/doc.go
@@ -79,6 +79,8 @@ func (p *ProcessState) Sys() interface{} {
// SysUsage returns system-dependent resource usage information about
// the exited process. Convert it to the appropriate underlying
// type, such as *syscall.Rusage on Unix, to access its contents.
+// (On Unix, *syscall.Rusage matches struct rusage as defined in the
+// getrusage(2) manual page.)
func (p *ProcessState) SysUsage() interface{} {
return p.sysUsage()
}
@@ -89,7 +91,7 @@ func Hostname() (name string, err error) {
}
// Readdir reads the contents of the directory associated with file and
-// returns an array of up to n FileInfo values, as would be returned
+// returns a slice of up to n FileInfo values, as would be returned
// by Lstat, in directory order. Subsequent calls on the same file will yield
// further FileInfos.
//
diff --git a/src/pkg/os/env.go b/src/pkg/os/env.go
index eb265f241..db7fc72b8 100644
--- a/src/pkg/os/env.go
+++ b/src/pkg/os/env.go
@@ -9,7 +9,7 @@ package os
import "syscall"
// Expand replaces ${var} or $var in the string based on the mapping function.
-// Invocations of undefined variables are replaced with the empty string.
+// For example, os.ExpandEnv(s) is equivalent to os.Expand(s, os.Getenv).
func Expand(s string, mapping func(string) string) string {
buf := make([]byte, 0, 2*len(s))
// ${} is all ASCII, so bytes are fine for this operation.
diff --git a/src/pkg/os/env_unix_test.go b/src/pkg/os/env_unix_test.go
new file mode 100644
index 000000000..7eb4dc0ff
--- /dev/null
+++ b/src/pkg/os/env_unix_test.go
@@ -0,0 +1,30 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd
+
+package os_test
+
+import (
+ . "os"
+ "testing"
+)
+
+var setenvEinvalTests = []struct {
+ k, v string
+}{
+ {"", ""}, // empty key
+ {"k=v", ""}, // '=' in key
+ {"\x00", ""}, // '\x00' in key
+ {"k", "\x00"}, // '\x00' in value
+}
+
+func TestSetenvUnixEinval(t *testing.T) {
+ for _, tt := range setenvEinvalTests {
+ err := Setenv(tt.k, tt.v)
+ if err == nil {
+ t.Errorf(`Setenv(%q, %q) == nil, want error`, tt.k, tt.v)
+ }
+ }
+}
diff --git a/src/pkg/os/error.go b/src/pkg/os/error.go
index b88e49400..a7977ff19 100644
--- a/src/pkg/os/error.go
+++ b/src/pkg/os/error.go
@@ -43,7 +43,7 @@ func NewSyscallError(syscall string, err error) error {
return &SyscallError{syscall, err}
}
-// IsExist returns whether the error is known to report that a file or directory
+// IsExist returns whether the error is known to report that a file or directory
// already exists. It is satisfied by ErrExist as well as some syscall errors.
func IsExist(err error) bool {
return isExist(err)
diff --git a/src/pkg/os/error_plan9.go b/src/pkg/os/error_plan9.go
index 3c9dfb0b1..85260c82a 100644
--- a/src/pkg/os/error_plan9.go
+++ b/src/pkg/os/error_plan9.go
@@ -5,21 +5,36 @@
package os
func isExist(err error) bool {
- if pe, ok := err.(*PathError); ok {
+ switch pe := err.(type) {
+ case nil:
+ return false
+ case *PathError:
+ err = pe.Err
+ case *LinkError:
err = pe.Err
}
return contains(err.Error(), " exists")
}
func isNotExist(err error) bool {
- if pe, ok := err.(*PathError); ok {
+ switch pe := err.(type) {
+ case nil:
+ return false
+ case *PathError:
+ err = pe.Err
+ case *LinkError:
err = pe.Err
}
return contains(err.Error(), "does not exist")
}
func isPermission(err error) bool {
- if pe, ok := err.(*PathError); ok {
+ switch pe := err.(type) {
+ case nil:
+ return false
+ case *PathError:
+ err = pe.Err
+ case *LinkError:
err = pe.Err
}
return contains(err.Error(), "permission denied")
diff --git a/src/pkg/os/error_posix.go b/src/pkg/os/error_posix.go
index 1685c1f21..81b626aec 100644
--- a/src/pkg/os/error_posix.go
+++ b/src/pkg/os/error_posix.go
@@ -9,21 +9,36 @@ package os
import "syscall"
func isExist(err error) bool {
- if pe, ok := err.(*PathError); ok {
+ switch pe := err.(type) {
+ case nil:
+ return false
+ case *PathError:
+ err = pe.Err
+ case *LinkError:
err = pe.Err
}
return err == syscall.EEXIST || err == ErrExist
}
func isNotExist(err error) bool {
- if pe, ok := err.(*PathError); ok {
+ switch pe := err.(type) {
+ case nil:
+ return false
+ case *PathError:
+ err = pe.Err
+ case *LinkError:
err = pe.Err
}
return err == syscall.ENOENT || err == ErrNotExist
}
func isPermission(err error) bool {
- if pe, ok := err.(*PathError); ok {
+ switch pe := err.(type) {
+ case nil:
+ return false
+ case *PathError:
+ err = pe.Err
+ case *LinkError:
err = pe.Err
}
return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
diff --git a/src/pkg/os/error_test.go b/src/pkg/os/error_test.go
index 42f846fa3..02ed2351c 100644
--- a/src/pkg/os/error_test.go
+++ b/src/pkg/os/error_test.go
@@ -79,3 +79,54 @@ func checkErrorPredicate(predName string, pred func(error) bool, err error) stri
}
return ""
}
+
+var isExistTests = []struct {
+ err error
+ is bool
+ isnot bool
+}{
+ {&os.PathError{Err: os.ErrInvalid}, false, false},
+ {&os.PathError{Err: os.ErrPermission}, false, false},
+ {&os.PathError{Err: os.ErrExist}, true, false},
+ {&os.PathError{Err: os.ErrNotExist}, false, true},
+ {&os.LinkError{Err: os.ErrInvalid}, false, false},
+ {&os.LinkError{Err: os.ErrPermission}, false, false},
+ {&os.LinkError{Err: os.ErrExist}, true, false},
+ {&os.LinkError{Err: os.ErrNotExist}, false, true},
+ {nil, false, false},
+}
+
+func TestIsExist(t *testing.T) {
+ for _, tt := range isExistTests {
+ if is := os.IsExist(tt.err); is != tt.is {
+ t.Errorf("os.IsExist(%T %v) = %v, want %v", tt.err, tt.err, is, tt.is)
+ }
+ if isnot := os.IsNotExist(tt.err); isnot != tt.isnot {
+ t.Errorf("os.IsNotExist(%T %v) = %v, want %v", tt.err, tt.err, isnot, tt.isnot)
+ }
+ }
+}
+
+func TestErrPathNUL(t *testing.T) {
+ f, err := ioutil.TempFile("", "_Go_ErrPathNUL\x00")
+ if err == nil {
+ f.Close()
+ t.Fatal("TempFile should have failed")
+ }
+ f, err = ioutil.TempFile("", "_Go_ErrPathNUL")
+ if err != nil {
+ t.Fatalf("open ErrPathNUL tempfile: %s", err)
+ }
+ defer os.Remove(f.Name())
+ defer f.Close()
+ f2, err := os.OpenFile(f.Name(), os.O_RDWR, 0600)
+ if err != nil {
+ t.Fatalf("open ErrPathNUL: %s", err)
+ }
+ f2.Close()
+ f2, err = os.OpenFile(f.Name()+"\x00", os.O_RDWR, 0600)
+ if err == nil {
+ f2.Close()
+ t.Fatal("Open should have failed")
+ }
+}
diff --git a/src/pkg/os/error_windows.go b/src/pkg/os/error_windows.go
index fbb0d4f3f..83db6c078 100644
--- a/src/pkg/os/error_windows.go
+++ b/src/pkg/os/error_windows.go
@@ -7,7 +7,12 @@ package os
import "syscall"
func isExist(err error) bool {
- if pe, ok := err.(*PathError); ok {
+ switch pe := err.(type) {
+ case nil:
+ return false
+ case *PathError:
+ err = pe.Err
+ case *LinkError:
err = pe.Err
}
return err == syscall.ERROR_ALREADY_EXISTS ||
@@ -15,7 +20,12 @@ func isExist(err error) bool {
}
func isNotExist(err error) bool {
- if pe, ok := err.(*PathError); ok {
+ switch pe := err.(type) {
+ case nil:
+ return false
+ case *PathError:
+ err = pe.Err
+ case *LinkError:
err = pe.Err
}
return err == syscall.ERROR_FILE_NOT_FOUND ||
@@ -23,7 +33,12 @@ func isNotExist(err error) bool {
}
func isPermission(err error) bool {
- if pe, ok := err.(*PathError); ok {
+ switch pe := err.(type) {
+ case nil:
+ return false
+ case *PathError:
+ err = pe.Err
+ case *LinkError:
err = pe.Err
}
return err == syscall.ERROR_ACCESS_DENIED || err == ErrPermission
diff --git a/src/pkg/os/error_windows_test.go b/src/pkg/os/error_windows_test.go
new file mode 100644
index 000000000..3e6504f8d
--- /dev/null
+++ b/src/pkg/os/error_windows_test.go
@@ -0,0 +1,47 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os_test
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+func TestErrIsExistAfterRename(t *testing.T) {
+ dir, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatalf("Create temp directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ src := filepath.Join(dir, "src")
+ dest := filepath.Join(dir, "dest")
+
+ f, err := os.Create(src)
+ if err != nil {
+ t.Fatalf("Create file %v: %v", src, err)
+ }
+ f.Close()
+ err = os.Rename(src, dest)
+ if err != nil {
+ t.Fatalf("Rename %v to %v: %v", src, dest, err)
+ }
+
+ f, err = os.Create(src)
+ if err != nil {
+ t.Fatalf("Create file %v: %v", src, err)
+ }
+ f.Close()
+ err = os.Rename(src, dest)
+ if err == nil {
+ t.Fatal("Rename should have failed")
+ }
+ if s := checkErrorPredicate("os.IsExist", os.IsExist, err); s != "" {
+ t.Fatal(s)
+ return
+ }
+}
diff --git a/src/pkg/os/exec.go b/src/pkg/os/exec.go
index 531b87ca5..5aea3098b 100644
--- a/src/pkg/os/exec.go
+++ b/src/pkg/os/exec.go
@@ -6,6 +6,7 @@ package os
import (
"runtime"
+ "sync/atomic"
"syscall"
)
@@ -13,7 +14,7 @@ import (
type Process struct {
Pid int
handle uintptr
- done bool // process has been successfully waited on
+ isdone uint32 // process has been successfully waited on, non zero if true
}
func newProcess(pid int, handle uintptr) *Process {
@@ -22,6 +23,14 @@ func newProcess(pid int, handle uintptr) *Process {
return p
}
+func (p *Process) setDone() {
+ atomic.StoreUint32(&p.isdone, 1)
+}
+
+func (p *Process) done() bool {
+ return atomic.LoadUint32(&p.isdone) > 0
+}
+
// ProcAttr holds the attributes that will be applied to a new process
// started by StartProcess.
type ProcAttr struct {
@@ -54,14 +63,6 @@ type Signal interface {
Signal() // to distinguish from other Stringers
}
-// The only signal values guaranteed to be present on all systems
-// are Interrupt (send the process an interrupt) and
-// Kill (force the process to exit).
-var (
- Interrupt Signal = syscall.SIGINT
- Kill Signal = syscall.SIGKILL
-)
-
// Getpid returns the process id of the caller.
func Getpid() int { return syscall.Getpid() }
diff --git a/src/pkg/os/exec/exec.go b/src/pkg/os/exec/exec.go
index 9a8e18170..8368491b0 100644
--- a/src/pkg/os/exec/exec.go
+++ b/src/pkg/os/exec/exec.go
@@ -16,7 +16,7 @@ import (
"syscall"
)
-// Error records the name of a binary that failed to be be executed
+// Error records the name of a binary that failed to be executed
// and the reason it failed.
type Error struct {
Name string
@@ -37,7 +37,7 @@ type Cmd struct {
// Args holds command line arguments, including the command as Args[0].
// If the Args field is empty or nil, Run uses {Path}.
- //
+ //
// In typical use, both Path and Args are set by calling Command.
Args []string
@@ -143,6 +143,9 @@ func (c *Cmd) argv() []string {
func (c *Cmd) stdin() (f *os.File, err error) {
if c.Stdin == nil {
f, err = os.Open(os.DevNull)
+ if err != nil {
+ return
+ }
c.closeAfterStart = append(c.closeAfterStart, f)
return
}
@@ -182,6 +185,9 @@ func (c *Cmd) stderr() (f *os.File, err error) {
func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) {
if w == nil {
f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0)
+ if err != nil {
+ return
+ }
c.closeAfterStart = append(c.closeAfterStart, f)
return
}
diff --git a/src/pkg/os/exec/exec_test.go b/src/pkg/os/exec/exec_test.go
index 52f4bce3a..611ac0267 100644
--- a/src/pkg/os/exec/exec_test.go
+++ b/src/pkg/os/exec/exec_test.go
@@ -14,6 +14,7 @@ import (
"net/http"
"net/http/httptest"
"os"
+ "path/filepath"
"runtime"
"strconv"
"strings"
@@ -83,10 +84,16 @@ func TestNoExistBinary(t *testing.T) {
func TestExitStatus(t *testing.T) {
// Test that exit values are returned correctly
- err := helperCommand("exit", "42").Run()
+ cmd := helperCommand("exit", "42")
+ err := cmd.Run()
+ want := "exit status 42"
+ switch runtime.GOOS {
+ case "plan9":
+ want = fmt.Sprintf("exit status: '%s %d: 42'", filepath.Base(cmd.Path), cmd.ProcessState.Pid())
+ }
if werr, ok := err.(*ExitError); ok {
- if s, e := werr.Error(), "exit status 42"; s != e {
- t.Errorf("from exit 42 got exit %q, want %q", s, e)
+ if s := werr.Error(); s != want {
+ t.Errorf("from exit 42 got exit %q, want %q", s, want)
}
} else {
t.Fatalf("expected *ExitError from exit 42; got %T: %v", err, err)
@@ -144,18 +151,36 @@ func TestPipes(t *testing.T) {
check("Wait", err)
}
+var testedAlreadyLeaked = false
+
+// basefds returns the number of expected file descriptors
+// to be present in a process at start.
+func basefds() uintptr {
+ n := os.Stderr.Fd() + 1
+
+ // Go runtime for 32-bit Plan 9 requires that /dev/bintime
+ // be kept open.
+ // See ../../runtime/time_plan9_386.c:/^runtime·nanotime
+ if runtime.GOOS == "plan9" && runtime.GOARCH == "386" {
+ n++
+ }
+ return n
+}
+
func TestExtraFiles(t *testing.T) {
if runtime.GOOS == "windows" {
- t.Logf("no operating system support; skipping")
- return
+ t.Skip("no operating system support; skipping")
}
// Ensure that file descriptors have not already been leaked into
// our environment.
- for fd := os.Stderr.Fd() + 1; fd <= 101; fd++ {
- err := os.NewFile(fd, "").Close()
- if err == nil {
- t.Logf("Something already leaked - closed fd %d", fd)
+ if !testedAlreadyLeaked {
+ testedAlreadyLeaked = true
+ for fd := basefds(); fd <= 101; fd++ {
+ err := os.NewFile(fd, "").Close()
+ if err == nil {
+ t.Logf("Something already leaked - closed fd %d", fd)
+ }
}
}
@@ -167,6 +192,18 @@ func TestExtraFiles(t *testing.T) {
}
defer ln.Close()
+ // Make sure duplicated fds don't leak to the child.
+ f, err := ln.(*net.TCPListener).File()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ ln2, err := net.FileListener(f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln2.Close()
+
// Force TLS root certs to be loaded (which might involve
// cgo), to make sure none of that potential C code leaks fds.
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -193,13 +230,65 @@ func TestExtraFiles(t *testing.T) {
}
c := helperCommand("read3")
+ var stdout, stderr bytes.Buffer
+ c.Stdout = &stdout
+ c.Stderr = &stderr
c.ExtraFiles = []*os.File{tf}
- bs, err := c.CombinedOutput()
+ err = c.Run()
if err != nil {
- t.Fatalf("CombinedOutput: %v; output %q", err, bs)
+ t.Fatalf("Run: %v; stdout %q, stderr %q", err, stdout.Bytes(), stderr.Bytes())
}
- if string(bs) != text {
- t.Errorf("got %q; want %q", string(bs), text)
+ if stdout.String() != text {
+ t.Errorf("got stdout %q, stderr %q; want %q on stdout", stdout.String(), stderr.String(), text)
+ }
+}
+
+func TestExtraFilesRace(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("no operating system support; skipping")
+ }
+ listen := func() net.Listener {
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ return ln
+ }
+ listenerFile := func(ln net.Listener) *os.File {
+ f, err := ln.(*net.TCPListener).File()
+ if err != nil {
+ t.Fatal(err)
+ }
+ return f
+ }
+ runCommand := func(c *Cmd, out chan<- string) {
+ bout, err := c.CombinedOutput()
+ if err != nil {
+ out <- "ERROR:" + err.Error()
+ } else {
+ out <- string(bout)
+ }
+ }
+
+ for i := 0; i < 10; i++ {
+ la := listen()
+ ca := helperCommand("describefiles")
+ ca.ExtraFiles = []*os.File{listenerFile(la)}
+ lb := listen()
+ cb := helperCommand("describefiles")
+ cb.ExtraFiles = []*os.File{listenerFile(lb)}
+ ares := make(chan string)
+ bres := make(chan string)
+ go runCommand(ca, ares)
+ go runCommand(cb, bres)
+ if got, want := <-ares, fmt.Sprintf("fd3: listener %s\n", la.Addr()); got != want {
+ t.Errorf("iteration %d, process A got:\n%s\nwant:\n%s\n", i, got, want)
+ }
+ if got, want := <-bres, fmt.Sprintf("fd3: listener %s\n", lb.Addr()); got != want {
+ t.Errorf("iteration %d, process B got:\n%s\nwant:\n%s\n", i, got, want)
+ }
+ la.Close()
+ lb.Close()
}
}
@@ -287,10 +376,15 @@ func TestHelperProcess(*testing.T) {
// TODO(bradfitz): broken? Sometimes.
// http://golang.org/issue/2603
// Skip this additional part of the test for now.
+ case "netbsd":
+ // TODO(jsing): This currently fails on NetBSD due to
+ // the cloned file descriptors that result from opening
+ // /dev/urandom.
+ // http://golang.org/issue/3955
default:
// Now verify that there are no other open fds.
var files []*os.File
- for wantfd := os.Stderr.Fd() + 2; wantfd <= 100; wantfd++ {
+ for wantfd := basefds() + 1; wantfd <= 100; wantfd++ {
f, err := os.Open(os.Args[0])
if err != nil {
fmt.Printf("error opening file with expected fd %d: %v", wantfd, err)
@@ -314,10 +408,20 @@ func TestHelperProcess(*testing.T) {
// what we do with fd3 as long as we refer to it;
// closing it is the easy choice.
fd3.Close()
- os.Stderr.Write(bs)
+ os.Stdout.Write(bs)
case "exit":
n, _ := strconv.Atoi(args[0])
os.Exit(n)
+ case "describefiles":
+ for fd := uintptr(3); fd < 25; fd++ {
+ f := os.NewFile(fd, fmt.Sprintf("fd-%d", fd))
+ ln, err := net.FileListener(f)
+ if err == nil {
+ fmt.Printf("fd%d: listener %s\n", fd, ln.Addr())
+ ln.Close()
+ }
+ }
+ os.Exit(0)
default:
fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd)
os.Exit(2)
diff --git a/src/pkg/os/exec/lp_plan9.go b/src/pkg/os/exec/lp_plan9.go
index 0e229e03e..6846a35c8 100644
--- a/src/pkg/os/exec/lp_plan9.go
+++ b/src/pkg/os/exec/lp_plan9.go
@@ -8,7 +8,6 @@ import (
"errors"
"os"
"strings"
- "syscall"
)
// ErrNotFound is the error resulting if a path search failed to find an executable file.
@@ -22,7 +21,7 @@ func findExecutable(file string) error {
if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
return nil
}
- return syscall.EPERM
+ return os.ErrPermission
}
// LookPath searches for an executable binary named file
diff --git a/src/pkg/os/exec/lp_unix.go b/src/pkg/os/exec/lp_unix.go
index 216322199..1d1ec07da 100644
--- a/src/pkg/os/exec/lp_unix.go
+++ b/src/pkg/os/exec/lp_unix.go
@@ -42,6 +42,9 @@ func LookPath(file string) (string, error) {
return "", &Error{file, err}
}
pathenv := os.Getenv("PATH")
+ if pathenv == "" {
+ return "", &Error{file, ErrNotFound}
+ }
for _, dir := range strings.Split(pathenv, ":") {
if dir == "" {
// Unix shell semantics: path element "" means "."
diff --git a/src/pkg/os/exec/lp_unix_test.go b/src/pkg/os/exec/lp_unix_test.go
new file mode 100644
index 000000000..625d78486
--- /dev/null
+++ b/src/pkg/os/exec/lp_unix_test.go
@@ -0,0 +1,55 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd
+
+package exec
+
+import (
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+func TestLookPathUnixEmptyPath(t *testing.T) {
+ tmp, err := ioutil.TempDir("", "TestLookPathUnixEmptyPath")
+ if err != nil {
+ t.Fatal("TempDir failed: ", err)
+ }
+ defer os.RemoveAll(tmp)
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatal("Getwd failed: ", err)
+ }
+ err = os.Chdir(tmp)
+ if err != nil {
+ t.Fatal("Chdir failed: ", err)
+ }
+ defer os.Chdir(wd)
+
+ f, err := os.OpenFile("exec_me", os.O_CREATE|os.O_EXCL, 0700)
+ if err != nil {
+ t.Fatal("OpenFile failed: ", err)
+ }
+ err = f.Close()
+ if err != nil {
+ t.Fatal("Close failed: ", err)
+ }
+
+ pathenv := os.Getenv("PATH")
+ defer os.Setenv("PATH", pathenv)
+
+ err = os.Setenv("PATH", "")
+ if err != nil {
+ t.Fatal("Setenv failed: ", err)
+ }
+
+ path, err := LookPath("exec_me")
+ if err == nil {
+ t.Fatal("LookPath found exec_me in empty $PATH")
+ }
+ if path != "" {
+ t.Fatalf("LookPath path == %q when err != nil", path)
+ }
+}
diff --git a/src/pkg/os/exec/lp_windows.go b/src/pkg/os/exec/lp_windows.go
index d8351d7e6..7c7289bce 100644
--- a/src/pkg/os/exec/lp_windows.go
+++ b/src/pkg/os/exec/lp_windows.go
@@ -72,7 +72,7 @@ func LookPath(file string) (f string, err error) {
return
}
if pathenv := os.Getenv(`PATH`); pathenv != `` {
- for _, dir := range strings.Split(pathenv, `;`) {
+ for _, dir := range splitList(pathenv) {
if f, err = findExecutable(dir+`\`+file, exts); err == nil {
return
}
@@ -80,3 +80,36 @@ func LookPath(file string) (f string, err error) {
}
return ``, &Error{file, ErrNotFound}
}
+
+func splitList(path string) []string {
+ // The same implementation is used in SplitList in path/filepath;
+ // consider changing path/filepath when changing this.
+
+ if path == "" {
+ return []string{}
+ }
+
+ // Split path, respecting but preserving quotes.
+ list := []string{}
+ start := 0
+ quo := false
+ for i := 0; i < len(path); i++ {
+ switch c := path[i]; {
+ case c == '"':
+ quo = !quo
+ case c == os.PathListSeparator && !quo:
+ list = append(list, path[start:i])
+ start = i + 1
+ }
+ }
+ list = append(list, path[start:])
+
+ // Remove quotes.
+ for i, s := range list {
+ if strings.Contains(s, `"`) {
+ list[i] = strings.Replace(s, `"`, ``, -1)
+ }
+ }
+
+ return list
+}
diff --git a/src/pkg/os/exec_plan9.go b/src/pkg/os/exec_plan9.go
index 41cc8c26f..2bd5b6888 100644
--- a/src/pkg/os/exec_plan9.go
+++ b/src/pkg/os/exec_plan9.go
@@ -11,6 +11,14 @@ import (
"time"
)
+// The only signal values guaranteed to be present on all systems
+// are Interrupt (send the process an interrupt) and Kill (force
+// the process to exit).
+var (
+ Interrupt Signal = syscall.Note("interrupt")
+ Kill Signal = syscall.Note("kill")
+)
+
func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
sysattr := &syscall.ProcAttr{
Dir: attr.Dir,
@@ -30,35 +38,35 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
return newProcess(pid, h), nil
}
-// Plan9Note implements the Signal interface on Plan 9.
-type Plan9Note string
-
-func (note Plan9Note) String() string {
- return string(note)
+func (p *Process) writeProcFile(file string, data string) error {
+ f, e := OpenFile("/proc/"+itoa(p.Pid)+"/"+file, O_WRONLY, 0)
+ if e != nil {
+ return e
+ }
+ defer f.Close()
+ _, e = f.Write([]byte(data))
+ return e
}
func (p *Process) signal(sig Signal) error {
- if p.done {
+ if p.done() {
return errors.New("os: process already finished")
}
-
- f, e := OpenFile("/proc/"+itoa(p.Pid)+"/note", O_WRONLY, 0)
- if e != nil {
+ if sig == Kill {
+ // Special-case the kill signal since it doesn't use /proc/$pid/note.
+ return p.Kill()
+ }
+ if e := p.writeProcFile("note", sig.String()); e != nil {
return NewSyscallError("signal", e)
}
- defer f.Close()
- _, e = f.Write([]byte(sig.String()))
- return e
+ return nil
}
func (p *Process) kill() error {
- f, e := OpenFile("/proc/"+itoa(p.Pid)+"/ctl", O_WRONLY, 0)
- if e != nil {
+ if e := p.writeProcFile("ctl", "kill"); e != nil {
return NewSyscallError("kill", e)
}
- defer f.Close()
- _, e = f.Write([]byte("kill"))
- return e
+ return nil
}
func (p *Process) wait() (ps *ProcessState, err error) {
@@ -67,20 +75,12 @@ func (p *Process) wait() (ps *ProcessState, err error) {
if p.Pid == -1 {
return nil, ErrInvalid
}
-
- for true {
- err = syscall.Await(&waitmsg)
-
- if err != nil {
- return nil, NewSyscallError("wait", err)
- }
-
- if waitmsg.Pid == p.Pid {
- p.done = true
- break
- }
+ err = syscall.WaitProcess(p.Pid, &waitmsg)
+ if err != nil {
+ return nil, NewSyscallError("wait", err)
}
+ p.setDone()
ps = &ProcessState{
pid: waitmsg.Pid,
status: &waitmsg,
diff --git a/src/pkg/os/exec_posix.go b/src/pkg/os/exec_posix.go
index 70351cfb3..f7b10f3c6 100644
--- a/src/pkg/os/exec_posix.go
+++ b/src/pkg/os/exec_posix.go
@@ -10,10 +10,19 @@ import (
"syscall"
)
+// The only signal values guaranteed to be present on all systems
+// are Interrupt (send the process an interrupt) and Kill (force
+// the process to exit).
+var (
+ Interrupt Signal = syscall.SIGINT
+ Kill Signal = syscall.SIGKILL
+)
+
func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
- // Double-check existence of the directory we want
+ // If there is no SysProcAttr (ie. no Chroot or changed
+ // UID/GID), double-check existence of the directory we want
// to chdir into. We can make the error clearer this way.
- if attr != nil && attr.Dir != "" {
+ if attr != nil && attr.Sys == nil && attr.Dir != "" {
if _, err := Stat(attr.Dir); err != nil {
pe := err.(*PathError)
pe.Op = "chdir"
@@ -109,9 +118,9 @@ func (p *ProcessState) String() string {
case status.Exited():
res = "exit status " + itod(status.ExitStatus())
case status.Signaled():
- res = "signal " + itod(int(status.Signal()))
+ res = "signal: " + status.Signal().String()
case status.Stopped():
- res = "stop signal " + itod(int(status.StopSignal()))
+ res = "stop signal: " + status.StopSignal().String()
if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 {
res += " (trap " + itod(status.TrapCause()) + ")"
}
diff --git a/src/pkg/os/exec_unix.go b/src/pkg/os/exec_unix.go
index ecfe5353b..fa3ba8a19 100644
--- a/src/pkg/os/exec_unix.go
+++ b/src/pkg/os/exec_unix.go
@@ -24,7 +24,7 @@ func (p *Process) wait() (ps *ProcessState, err error) {
return nil, NewSyscallError("wait", e)
}
if pid1 != 0 {
- p.done = true
+ p.setDone()
}
ps = &ProcessState{
pid: pid1,
@@ -35,7 +35,7 @@ func (p *Process) wait() (ps *ProcessState, err error) {
}
func (p *Process) signal(sig Signal) error {
- if p.done {
+ if p.done() {
return errors.New("os: process already finished")
}
s, ok := sig.(syscall.Signal)
diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go
index 5beca4a65..4aa2ade63 100644
--- a/src/pkg/os/exec_windows.go
+++ b/src/pkg/os/exec_windows.go
@@ -32,7 +32,7 @@ func (p *Process) wait() (ps *ProcessState, err error) {
if e != nil {
return nil, NewSyscallError("GetProcessTimes", e)
}
- p.done = true
+ p.setDone()
// NOTE(brainman): It seems that sometimes process is not dead
// when WaitForSingleObject returns. But we do not know any
// other way to wait for it. Sleeping for a while seems to do
@@ -43,7 +43,7 @@ func (p *Process) wait() (ps *ProcessState, err error) {
}
func (p *Process) signal(sig Signal) error {
- if p.done {
+ if p.done() {
return errors.New("os: process already finished")
}
if sig == Kill {
diff --git a/src/pkg/os/file.go b/src/pkg/os/file.go
index 4acf35d67..32cac6d89 100644
--- a/src/pkg/os/file.go
+++ b/src/pkg/os/file.go
@@ -9,7 +9,7 @@
// if a call that takes a file name fails, such as Open or Stat, the error
// will include the failing file name when printed and will be of type
// *PathError, which may be unpacked for more information.
-//
+//
// The os interface is intended to be uniform across all operating systems.
// Features not generally available appear in the system-specific package syscall.
//
@@ -185,7 +185,7 @@ func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
}
// WriteString is like Write, but writes the contents of string s rather than
-// an array of bytes.
+// a slice of bytes.
func (f *File) WriteString(s string) (ret int, err error) {
if f == nil {
return 0, ErrInvalid
diff --git a/src/pkg/os/file_plan9.go b/src/pkg/os/file_plan9.go
index cb0e9ef92..d6d39a899 100644
--- a/src/pkg/os/file_plan9.go
+++ b/src/pkg/os/file_plan9.go
@@ -5,14 +5,11 @@
package os
import (
- "errors"
"runtime"
"syscall"
"time"
)
-var ErrPlan9 = errors.New("unimplemented on Plan 9")
-
// File represents an open file descriptor.
type File struct {
*file
@@ -107,7 +104,6 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
append = true
}
- syscall.ForkLock.RLock()
if (create && trunc) || excl {
fd, e = syscall.Create(name, flag, syscallMode(perm))
} else {
@@ -120,7 +116,6 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
}
}
}
- syscall.ForkLock.RUnlock()
if e != nil {
return nil, &PathError{"open", name, e}
@@ -137,8 +132,8 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
// Close closes the File, rendering it unusable for I/O.
// It returns an error, if any.
-func (file *File) Close() error {
- return file.file.close()
+func (f *File) Close() error {
+ return f.file.close()
}
func (file *file) close() error {
@@ -159,8 +154,8 @@ func (file *file) close() error {
}
// Stat returns the FileInfo structure describing file.
-// It returns the FileInfo and an error, if any.
-func (f *File) Stat() (FileInfo, error) {
+// If there is an error, it will be of type *PathError.
+func (f *File) Stat() (fi FileInfo, err error) {
d, err := dirstat(f)
if err != nil {
return nil, err
@@ -170,14 +165,20 @@ func (f *File) Stat() (FileInfo, error) {
// Truncate changes the size of the file.
// It does not change the I/O offset.
+// If there is an error, it will be of type *PathError.
func (f *File) Truncate(size int64) error {
- var d Dir
- d.Null()
+ var d syscall.Dir
- d.Length = uint64(size)
+ d.Null()
+ d.Length = size
- if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil {
- return &PathError{"truncate", f.name, e}
+ var buf [syscall.STATFIXLEN]byte
+ n, err := d.Marshal(buf[:])
+ if err != nil {
+ return &PathError{"truncate", f.name, err}
+ }
+ if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
+ return &PathError{"truncate", f.name, err}
}
return nil
}
@@ -187,7 +188,7 @@ const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | Mod
// Chmod changes the mode of the file to mode.
// If there is an error, it will be of type *PathError.
func (f *File) Chmod(mode FileMode) error {
- var d Dir
+ var d syscall.Dir
odir, e := dirstat(f)
if e != nil {
@@ -195,8 +196,14 @@ func (f *File) Chmod(mode FileMode) error {
}
d.Null()
d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
- if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil {
- return &PathError{"chmod", f.name, e}
+
+ var buf [syscall.STATFIXLEN]byte
+ n, err := d.Marshal(buf[:])
+ if err != nil {
+ return &PathError{"chmod", f.name, err}
+ }
+ if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
+ return &PathError{"chmod", f.name, err}
}
return nil
}
@@ -208,12 +215,16 @@ func (f *File) Sync() (err error) {
if f == nil {
return ErrInvalid
}
-
- var d Dir
+ var d syscall.Dir
d.Null()
- if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil {
- return NewSyscallError("fsync", e)
+ var buf [syscall.STATFIXLEN]byte
+ n, err := d.Marshal(buf[:])
+ if err != nil {
+ return NewSyscallError("fsync", err)
+ }
+ if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
+ return NewSyscallError("fsync", err)
}
return nil
}
@@ -233,13 +244,23 @@ func (f *File) pread(b []byte, off int64) (n int, err error) {
// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
+// Since Plan 9 preserves message boundaries, never allow
+// a zero-byte write.
func (f *File) write(b []byte) (n int, err error) {
+ if len(b) == 0 {
+ return 0, nil
+ }
return syscall.Write(f.fd, b)
}
// pwrite writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
+// Since Plan 9 preserves message boundaries, never allow
+// a zero-byte write.
func (f *File) pwrite(b []byte, off int64) (n int, err error) {
+ if len(b) == 0 {
+ return 0, nil
+ }
return syscall.Pwrite(f.fd, b, off)
}
@@ -255,13 +276,18 @@ func (f *File) seek(offset int64, whence int) (ret int64, err error) {
// If the file is a symbolic link, it changes the size of the link's target.
// If there is an error, it will be of type *PathError.
func Truncate(name string, size int64) error {
- var d Dir
- d.Null()
+ var d syscall.Dir
- d.Length = uint64(size)
+ d.Null()
+ d.Length = size
- if e := syscall.Wstat(name, pdir(nil, &d)); e != nil {
- return &PathError{"truncate", name, e}
+ var buf [syscall.STATFIXLEN]byte
+ n, err := d.Marshal(buf[:])
+ if err != nil {
+ return &PathError{"truncate", name, err}
+ }
+ if err = syscall.Wstat(name, buf[:n]); err != nil {
+ return &PathError{"truncate", name, err}
}
return nil
}
@@ -277,21 +303,27 @@ func Remove(name string) error {
// Rename renames a file.
func Rename(oldname, newname string) error {
- var d Dir
- d.Null()
+ var d syscall.Dir
+ d.Null()
d.Name = newname
- if e := syscall.Wstat(oldname, pdir(nil, &d)); e != nil {
- return &PathError{"rename", oldname, e}
+ buf := make([]byte, syscall.STATFIXLEN+len(d.Name))
+ n, err := d.Marshal(buf[:])
+ if err != nil {
+ return &PathError{"rename", oldname, err}
+ }
+ if err = syscall.Wstat(oldname, buf[:n]); err != nil {
+ return &PathError{"rename", oldname, err}
}
return nil
}
// Chmod changes the mode of the named file to mode.
+// If the file is a symbolic link, it changes the mode of the link's target.
// If there is an error, it will be of type *PathError.
func Chmod(name string, mode FileMode) error {
- var d Dir
+ var d syscall.Dir
odir, e := dirstat(name)
if e != nil {
@@ -299,8 +331,14 @@ func Chmod(name string, mode FileMode) error {
}
d.Null()
d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
- if e := syscall.Wstat(name, pdir(nil, &d)); e != nil {
- return &PathError{"chmod", name, e}
+
+ var buf [syscall.STATFIXLEN]byte
+ n, err := d.Marshal(buf[:])
+ if err != nil {
+ return &PathError{"chmod", name, err}
+ }
+ if err = syscall.Wstat(name, buf[:n]); err != nil {
+ return &PathError{"chmod", name, err}
}
return nil
}
@@ -310,19 +348,27 @@ func Chmod(name string, mode FileMode) error {
//
// The underlying filesystem may truncate or round the values to a
// less precise time unit.
+// If there is an error, it will be of type *PathError.
func Chtimes(name string, atime time.Time, mtime time.Time) error {
- var d Dir
- d.Null()
+ var d syscall.Dir
+ d.Null()
d.Atime = uint32(atime.Unix())
d.Mtime = uint32(mtime.Unix())
- if e := syscall.Wstat(name, pdir(nil, &d)); e != nil {
- return &PathError{"chtimes", name, e}
+ var buf [syscall.STATFIXLEN]byte
+ n, err := d.Marshal(buf[:])
+ if err != nil {
+ return &PathError{"chtimes", name, err}
+ }
+ if err = syscall.Wstat(name, buf[:n]); err != nil {
+ return &PathError{"chtimes", name, err}
}
return nil
}
+// Pipe returns a connected pair of Files; reads from r return bytes
+// written to w. It returns the files and an error, if any.
func Pipe() (r *File, w *File, err error) {
var p [2]int
@@ -338,32 +384,42 @@ func Pipe() (r *File, w *File, err error) {
// not supported on Plan 9
-// Link creates a hard link.
+// Link creates newname as a hard link to the oldname file.
// If there is an error, it will be of type *LinkError.
func Link(oldname, newname string) error {
- return &LinkError{"link", oldname, newname, ErrPlan9}
+ return &LinkError{"link", oldname, newname, syscall.EPLAN9}
}
// Symlink creates newname as a symbolic link to oldname.
// If there is an error, it will be of type *LinkError.
func Symlink(oldname, newname string) error {
- return &LinkError{"symlink", oldname, newname, ErrPlan9}
+ return &LinkError{"symlink", oldname, newname, syscall.EPLAN9}
}
+// Readlink returns the destination of the named symbolic link.
+// If there is an error, it will be of type *PathError.
func Readlink(name string) (string, error) {
- return "", ErrPlan9
+ return "", &PathError{"readlink", name, syscall.EPLAN9}
}
+// Chown changes the numeric uid and gid of the named file.
+// If the file is a symbolic link, it changes the uid and gid of the link's target.
+// If there is an error, it will be of type *PathError.
func Chown(name string, uid, gid int) error {
- return ErrPlan9
+ return &PathError{"chown", name, syscall.EPLAN9}
}
+// Lchown changes the numeric uid and gid of the named file.
+// If the file is a symbolic link, it changes the uid and gid of the link itself.
+// If there is an error, it will be of type *PathError.
func Lchown(name string, uid, gid int) error {
- return ErrPlan9
+ return &PathError{"lchown", name, syscall.EPLAN9}
}
+// Chown changes the numeric uid and gid of the named file.
+// If there is an error, it will be of type *PathError.
func (f *File) Chown(uid, gid int) error {
- return ErrPlan9
+ return &PathError{"chown", f.name, syscall.EPLAN9}
}
// TempDir returns the default directory to use for temporary files.
diff --git a/src/pkg/os/file_posix.go b/src/pkg/os/file_posix.go
index 073bd56a4..b979fed97 100644
--- a/src/pkg/os/file_posix.go
+++ b/src/pkg/os/file_posix.go
@@ -13,17 +13,6 @@ import (
func sigpipe() // implemented in package runtime
-func epipecheck(file *File, e error) {
- if e == syscall.EPIPE {
- file.nepipe++
- if file.nepipe >= 10 {
- sigpipe()
- }
- } else {
- file.nepipe = 0
- }
-}
-
// Link creates newname as a hard link to the oldname file.
// If there is an error, it will be of type *LinkError.
func Link(oldname, newname string) error {
@@ -164,12 +153,10 @@ func (f *File) Sync() (err error) {
// less precise time unit.
// If there is an error, it will be of type *PathError.
func Chtimes(name string, atime time.Time, mtime time.Time) error {
- var utimes [2]syscall.Timeval
- atime_ns := atime.Unix()*1e9 + int64(atime.Nanosecond())
- mtime_ns := mtime.Unix()*1e9 + int64(mtime.Nanosecond())
- utimes[0] = syscall.NsecToTimeval(atime_ns)
- utimes[1] = syscall.NsecToTimeval(mtime_ns)
- if e := syscall.Utimes(name, utimes[0:]); e != nil {
+ var utimes [2]syscall.Timespec
+ utimes[0] = syscall.NsecToTimespec(atime.UnixNano())
+ utimes[1] = syscall.NsecToTimespec(mtime.UnixNano())
+ if e := syscall.UtimesNano(name, utimes[0:]); e != nil {
return &PathError{"chtimes", name, e}
}
return nil
diff --git a/src/pkg/os/file_unix.go b/src/pkg/os/file_unix.go
index 6271c3189..4f59c94cb 100644
--- a/src/pkg/os/file_unix.go
+++ b/src/pkg/os/file_unix.go
@@ -8,6 +8,7 @@ package os
import (
"runtime"
+ "sync/atomic"
"syscall"
)
@@ -24,7 +25,7 @@ type file struct {
fd int
name string
dirinfo *dirInfo // nil unless directory being read
- nepipe int // number of consecutive EPIPE in Write
+ nepipe int32 // number of consecutive EPIPE in Write
}
// Fd returns the integer Unix file descriptor referencing the open file.
@@ -53,6 +54,16 @@ type dirInfo struct {
bufp int // location of next record in buf.
}
+func epipecheck(file *File, e error) {
+ if e == syscall.EPIPE {
+ if atomic.AddInt32(&file.nepipe, 1) >= 10 {
+ sigpipe()
+ }
+ } else {
+ atomic.StoreInt32(&file.nepipe, 0)
+ }
+}
+
// DevNull is the name of the operating system's ``null device.''
// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
const DevNull = "/dev/null"
@@ -263,25 +274,6 @@ func basename(name string) string {
return name
}
-// Pipe returns a connected pair of Files; reads from r return bytes written to w.
-// It returns the files and an error, if any.
-func Pipe() (r *File, w *File, err error) {
- var p [2]int
-
- // See ../syscall/exec.go for description of lock.
- syscall.ForkLock.RLock()
- e := syscall.Pipe(p[0:])
- if e != nil {
- syscall.ForkLock.RUnlock()
- return nil, nil, NewSyscallError("pipe", e)
- }
- syscall.CloseOnExec(p[0])
- syscall.CloseOnExec(p[1])
- syscall.ForkLock.RUnlock()
-
- return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
-}
-
// TempDir returns the default directory to use for temporary files.
func TempDir() string {
dir := Getenv("TMPDIR")
diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go
index 88fa77bb8..2eba7a475 100644
--- a/src/pkg/os/file_windows.go
+++ b/src/pkg/os/file_windows.go
@@ -10,6 +10,8 @@ import (
"sync"
"syscall"
"unicode/utf16"
+ "unicode/utf8"
+ "unsafe"
)
// File represents an open file descriptor.
@@ -25,8 +27,12 @@ type file struct {
fd syscall.Handle
name string
dirinfo *dirInfo // nil unless directory being read
- nepipe int // number of consecutive EPIPE in Write
l sync.Mutex // used to implement windows pread/pwrite
+
+ // only for console io
+ isConsole bool
+ lastbits []byte // first few bytes of the last incomplete rune in last write
+ readbuf []rune // input console buffer
}
// Fd returns the Windows handle referencing the open file.
@@ -37,15 +43,25 @@ func (file *File) Fd() uintptr {
return uintptr(file.fd)
}
+// newFile returns a new File with the given file handle and name.
+// Unlike NewFile, it does not check that h is syscall.InvalidHandle.
+func newFile(h syscall.Handle, name string) *File {
+ f := &File{&file{fd: h, name: name}}
+ var m uint32
+ if syscall.GetConsoleMode(f.fd, &m) == nil {
+ f.isConsole = true
+ }
+ runtime.SetFinalizer(f.file, (*file).close)
+ return f
+}
+
// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd uintptr, name string) *File {
h := syscall.Handle(fd)
if h == syscall.InvalidHandle {
return nil
}
- f := &File{&file{fd: h, name: name}}
- runtime.SetFinalizer(f.file, (*file).close)
- return f
+ return newFile(h, name)
}
// Auxiliary information if the File describes a directory
@@ -53,6 +69,10 @@ type dirInfo struct {
data syscall.Win32finddata
needdata bool
path string
+ isempty bool // set if FindFirstFile returns ERROR_FILE_NOT_FOUND
+}
+
+func epipecheck(file *File, e error) {
}
const DevNull = "NUL"
@@ -62,30 +82,45 @@ func (f *file) isdir() bool { return f != nil && f.dirinfo != nil }
func openFile(name string, flag int, perm FileMode) (file *File, err error) {
r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
if e != nil {
- return nil, &PathError{"open", name, e}
- }
-
- // There's a race here with fork/exec, which we are
- // content to live with. See ../syscall/exec.go
- if syscall.O_CLOEXEC == 0 { // O_CLOEXEC not supported
- syscall.CloseOnExec(r)
+ return nil, e
}
-
return NewFile(uintptr(r), name), nil
}
func openDir(name string) (file *File, err error) {
+ maskp, e := syscall.UTF16PtrFromString(name + `\*`)
+ if e != nil {
+ return nil, e
+ }
d := new(dirInfo)
- r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+`\*`), &d.data)
+ r, e := syscall.FindFirstFile(maskp, &d.data)
if e != nil {
- return nil, &PathError{"open", name, e}
+ // FindFirstFile returns ERROR_FILE_NOT_FOUND when
+ // no matching files can be found. Then, if directory
+ // exists, we should proceed.
+ if e != syscall.ERROR_FILE_NOT_FOUND {
+ return nil, e
+ }
+ var fa syscall.Win32FileAttributeData
+ namep, e := syscall.UTF16PtrFromString(name)
+ if e != nil {
+ return nil, e
+ }
+ e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
+ if e != nil {
+ return nil, e
+ }
+ if fa.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 {
+ return nil, e
+ }
+ d.isempty = true
}
d.path = name
if !isAbs(d.path) {
cwd, _ := Getwd()
d.path = cwd + `\` + d.path
}
- f := NewFile(uintptr(r), name)
+ f := newFile(r, name)
f.dirinfo = d
return f, nil
}
@@ -112,7 +147,7 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
if e == nil {
return r, nil
}
- return nil, e
+ return nil, &PathError{"open", name, e}
}
// Close closes the File, rendering it unusable for I/O.
@@ -122,7 +157,14 @@ func (file *File) Close() error {
}
func (file *file) close() error {
- if file == nil || file.fd == syscall.InvalidHandle {
+ if file == nil {
+ return syscall.EINVAL
+ }
+ if file.isdir() && file.dirinfo.isempty {
+ // "special" empty directories
+ return nil
+ }
+ if file.fd == syscall.InvalidHandle {
return syscall.EINVAL
}
var e error
@@ -143,12 +185,15 @@ func (file *file) close() error {
}
func (file *File) readdir(n int) (fi []FileInfo, err error) {
- if file == nil || file.fd == syscall.InvalidHandle {
+ if file == nil {
return nil, syscall.EINVAL
}
if !file.isdir() {
return nil, &PathError{"Readdir", file.name, syscall.ENOTDIR}
}
+ if !file.dirinfo.isempty && file.fd == syscall.InvalidHandle {
+ return nil, syscall.EINVAL
+ }
wantAll := n <= 0
size := n
if wantAll {
@@ -157,7 +202,7 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
}
fi = make([]FileInfo, 0, size) // Empty with room to grow.
d := &file.dirinfo.data
- for n != 0 {
+ for n != 0 && !file.dirinfo.isempty {
if file.dirinfo.needdata {
e := syscall.FindNextFile(syscall.Handle(file.fd), d)
if e != nil {
@@ -178,11 +223,16 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
continue
}
f := &fileStat{
- name: name,
- size: mkSize(d.FileSizeHigh, d.FileSizeLow),
- modTime: mkModTime(d.LastWriteTime),
- mode: mkMode(d.FileAttributes),
- sys: mkSys(file.dirinfo.path+`\`+name, d.LastAccessTime, d.CreationTime),
+ name: name,
+ sys: syscall.Win32FileAttributeData{
+ FileAttributes: d.FileAttributes,
+ CreationTime: d.CreationTime,
+ LastAccessTime: d.LastAccessTime,
+ LastWriteTime: d.LastWriteTime,
+ FileSizeHigh: d.FileSizeHigh,
+ FileSizeLow: d.FileSizeLow,
+ },
+ path: file.dirinfo.path + `\` + name,
}
n--
fi = append(fi, f)
@@ -193,11 +243,48 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
return fi, nil
}
+// readConsole reads utf16 charcters from console File,
+// encodes them into utf8 and stores them in buffer b.
+// It returns the number of utf8 bytes read and an error, if any.
+func (f *File) readConsole(b []byte) (n int, err error) {
+ if len(b) == 0 {
+ return 0, nil
+ }
+ if len(f.readbuf) == 0 {
+ // get more input data from os
+ wchars := make([]uint16, len(b))
+ var p *uint16
+ if len(b) > 0 {
+ p = &wchars[0]
+ }
+ var nw uint32
+ err := syscall.ReadConsole(f.fd, p, uint32(len(wchars)), &nw, nil)
+ if err != nil {
+ return 0, err
+ }
+ f.readbuf = utf16.Decode(wchars[:nw])
+ }
+ for i, r := range f.readbuf {
+ if utf8.RuneLen(r) > len(b) {
+ f.readbuf = f.readbuf[i:]
+ return n, nil
+ }
+ nr := utf8.EncodeRune(b, r)
+ b = b[nr:]
+ n += nr
+ }
+ f.readbuf = nil
+ return n, nil
+}
+
// read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
func (f *File) read(b []byte) (n int, err error) {
f.l.Lock()
defer f.l.Unlock()
+ if f.isConsole {
+ return f.readConsole(b)
+ }
return syscall.Read(f.fd, b)
}
@@ -224,11 +311,57 @@ func (f *File) pread(b []byte, off int64) (n int, err error) {
return int(done), nil
}
+// writeConsole writes len(b) bytes to the console File.
+// It returns the number of bytes written and an error, if any.
+func (f *File) writeConsole(b []byte) (n int, err error) {
+ n = len(b)
+ runes := make([]rune, 0, 256)
+ if len(f.lastbits) > 0 {
+ b = append(f.lastbits, b...)
+ f.lastbits = nil
+
+ }
+ for len(b) >= utf8.UTFMax || utf8.FullRune(b) {
+ r, l := utf8.DecodeRune(b)
+ runes = append(runes, r)
+ b = b[l:]
+ }
+ if len(b) > 0 {
+ f.lastbits = make([]byte, len(b))
+ copy(f.lastbits, b)
+ }
+ // syscall.WriteConsole seems to fail, if given large buffer.
+ // So limit the buffer to 16000 characters. This number was
+ // discovered by experimenting with syscall.WriteConsole.
+ const maxWrite = 16000
+ for len(runes) > 0 {
+ m := len(runes)
+ if m > maxWrite {
+ m = maxWrite
+ }
+ chunk := runes[:m]
+ runes = runes[m:]
+ uint16s := utf16.Encode(chunk)
+ for len(uint16s) > 0 {
+ var written uint32
+ err = syscall.WriteConsole(f.fd, &uint16s[0], uint32(len(uint16s)), &written, nil)
+ if err != nil {
+ return 0, nil
+ }
+ uint16s = uint16s[written:]
+ }
+ }
+ return n, nil
+}
+
// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
func (f *File) write(b []byte) (n int, err error) {
f.l.Lock()
defer f.l.Unlock()
+ if f.isConsole {
+ return f.writeConsole(b)
+ }
return syscall.Write(f.fd, b)
}
@@ -282,11 +415,14 @@ func Truncate(name string, size int64) error {
// Remove removes the named file or directory.
// If there is an error, it will be of type *PathError.
func Remove(name string) error {
- p := &syscall.StringToUTF16(name)[0]
+ p, e := syscall.UTF16PtrFromString(name)
+ if e != nil {
+ return &PathError{"remove", name, e}
+ }
// Go file interface forces us to know whether
// name is a file or directory. Try both.
- e := syscall.DeleteFile(p)
+ e = syscall.DeleteFile(p)
if e == nil {
return nil
}
diff --git a/src/pkg/os/getwd.go b/src/pkg/os/getwd.go
index 81d8fed92..1b2212306 100644
--- a/src/pkg/os/getwd.go
+++ b/src/pkg/os/getwd.go
@@ -5,9 +5,15 @@
package os
import (
+ "sync"
"syscall"
)
+var getwdCache struct {
+ sync.Mutex
+ dir string
+}
+
// Getwd returns a rooted path name corresponding to the
// current directory. If the current directory can be
// reached via multiple paths (due to symbolic links),
@@ -35,6 +41,17 @@ func Getwd() (pwd string, err error) {
}
}
+ // Apply same kludge but to cached dir instead of $PWD.
+ getwdCache.Lock()
+ pwd = getwdCache.dir
+ getwdCache.Unlock()
+ if len(pwd) > 0 {
+ d, err := Stat(pwd)
+ if err == nil && SameFile(dot, d) {
+ return pwd, nil
+ }
+ }
+
// Root is a special case because it has no parent
// and ends in a slash.
root, err := Stat("/")
@@ -88,5 +105,11 @@ func Getwd() (pwd string, err error) {
// Set up for next round.
dot = pd
}
+
+ // Save answer as hint to avoid the expensive path next time.
+ getwdCache.Lock()
+ getwdCache.dir = pwd
+ getwdCache.Unlock()
+
return pwd, nil
}
diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go
index dec80cc09..29706015d 100644
--- a/src/pkg/os/os_test.go
+++ b/src/pkg/os/os_test.go
@@ -6,6 +6,7 @@ package os_test
import (
"bytes"
+ "flag"
"fmt"
"io"
"io/ioutil"
@@ -40,7 +41,6 @@ var sysdir = func() (sd *sysDir) {
sd = &sysDir{
Getenv("SystemRoot") + "\\system32\\drivers\\etc",
[]string{
- "hosts",
"networks",
"protocol",
"services",
@@ -69,10 +69,10 @@ var sysdir = func() (sd *sysDir) {
func size(name string, t *testing.T) int64 {
file, err := Open(name)
- defer file.Close()
if err != nil {
t.Fatal("open failed:", err)
}
+ defer file.Close()
var buf [100]byte
len := 0
for {
@@ -134,10 +134,10 @@ func TestStat(t *testing.T) {
func TestFstat(t *testing.T) {
path := sfdir + "/" + sfname
file, err1 := Open(path)
- defer file.Close()
if err1 != nil {
t.Fatal("open failed:", err1)
}
+ defer file.Close()
dir, err2 := file.Stat()
if err2 != nil {
t.Fatal("fstat failed:", err2)
@@ -189,10 +189,10 @@ func TestRead0(t *testing.T) {
func testReaddirnames(dir string, contents []string, t *testing.T) {
file, err := Open(dir)
- defer file.Close()
if err != nil {
t.Fatalf("open %q failed: %v", dir, err)
}
+ defer file.Close()
s, err2 := file.Readdirnames(-1)
if err2 != nil {
t.Fatalf("readdirnames %q failed: %v", dir, err2)
@@ -218,10 +218,10 @@ func testReaddirnames(dir string, contents []string, t *testing.T) {
func testReaddir(dir string, contents []string, t *testing.T) {
file, err := Open(dir)
- defer file.Close()
if err != nil {
t.Fatalf("open %q failed: %v", dir, err)
}
+ defer file.Close()
s, err2 := file.Readdir(-1)
if err2 != nil {
t.Fatalf("readdir %q failed: %v", dir, err2)
@@ -285,10 +285,10 @@ func TestReaddirnamesOneAtATime(t *testing.T) {
dir = "/bin"
}
file, err := Open(dir)
- defer file.Close()
if err != nil {
t.Fatalf("open %q failed: %v", dir, err)
}
+ defer file.Close()
all, err1 := file.Readdirnames(-1)
if err1 != nil {
t.Fatalf("readdirnames %q failed: %v", dir, err1)
@@ -310,8 +310,7 @@ func TestReaddirnamesOneAtATime(t *testing.T) {
func TestReaddirNValues(t *testing.T) {
if testing.Short() {
- t.Logf("test.short; skipping")
- return
+ t.Skip("test.short; skipping")
}
dir, err := ioutil.TempDir("", "")
if err != nil {
@@ -535,8 +534,10 @@ func exec(t *testing.T, dir, cmd string, args []string, expect string) {
var b bytes.Buffer
io.Copy(&b, r)
output := b.String()
- // Accept /usr prefix because Solaris /bin is symlinked to /usr/bin.
- if output != expect && output != "/usr"+expect {
+
+ fi1, _ := Stat(strings.TrimSpace(output))
+ fi2, _ := Stat(expect)
+ if !SameFile(fi1, fi2) {
t.Errorf("exec %q returned %q wanted %q",
strings.Join(append([]string{cmd}, args...), " "), output, expect)
}
@@ -544,15 +545,13 @@ func exec(t *testing.T, dir, cmd string, args []string, expect string) {
}
func TestStartProcess(t *testing.T) {
- var dir, cmd, le string
+ var dir, cmd string
var args []string
if runtime.GOOS == "windows" {
- le = "\r\n"
cmd = Getenv("COMSPEC")
dir = Getenv("SystemRoot")
args = []string{"/c", "cd"}
} else {
- le = "\n"
cmd = "/bin/pwd"
dir = "/"
args = []string{}
@@ -560,9 +559,9 @@ func TestStartProcess(t *testing.T) {
cmddir, cmdbase := filepath.Split(cmd)
args = append([]string{cmdbase}, args...)
// Test absolute executable path.
- exec(t, dir, cmd, args, dir+le)
+ exec(t, dir, cmd, args, dir)
// Test relative executable path.
- exec(t, cmddir, cmdbase, args, filepath.Clean(cmddir)+le)
+ exec(t, cmddir, cmdbase, args, cmddir)
}
func checkMode(t *testing.T, path string, mode FileMode) {
@@ -1066,3 +1065,52 @@ func TestDevNullFile(t *testing.T) {
t.Fatalf("wrong file size have %d want 0", fi.Size())
}
}
+
+var testLargeWrite = flag.Bool("large_write", false, "run TestLargeWriteToConsole test that floods console with output")
+
+func TestLargeWriteToConsole(t *testing.T) {
+ if !*testLargeWrite {
+ t.Skip("skipping console-flooding test; enable with -large_write")
+ }
+ b := make([]byte, 32000)
+ for i := range b {
+ b[i] = '.'
+ }
+ b[len(b)-1] = '\n'
+ n, err := Stdout.Write(b)
+ if err != nil {
+ t.Fatalf("Write to os.Stdout failed: %v", err)
+ }
+ if n != len(b) {
+ t.Errorf("Write to os.Stdout should return %d; got %d", len(b), n)
+ }
+ n, err = Stderr.Write(b)
+ if err != nil {
+ t.Fatalf("Write to os.Stderr failed: %v", err)
+ }
+ if n != len(b) {
+ t.Errorf("Write to os.Stderr should return %d; got %d", len(b), n)
+ }
+}
+
+func TestStatDirModeExec(t *testing.T) {
+ const mode = 0111
+
+ path, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatalf("Failed to create temp directory: %v", err)
+ }
+ defer RemoveAll(path)
+
+ if err := Chmod(path, 0777); err != nil {
+ t.Fatalf("Chmod %q 0777: %v", path, err)
+ }
+
+ dir, err := Stat(path)
+ if err != nil {
+ t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
+ }
+ if dir.Mode()&mode != mode {
+ t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode()&mode, mode)
+ }
+}
diff --git a/src/pkg/os/path_plan9.go b/src/pkg/os/path_plan9.go
index 3121b7bc7..64bad500a 100644
--- a/src/pkg/os/path_plan9.go
+++ b/src/pkg/os/path_plan9.go
@@ -5,8 +5,8 @@
package os
const (
- PathSeparator = '/' // OS-specific path separator
- PathListSeparator = 0 // OS-specific path list separator
+ PathSeparator = '/' // OS-specific path separator
+ PathListSeparator = '\000' // OS-specific path list separator
)
// IsPathSeparator returns true if c is a directory separator character.
diff --git a/src/pkg/os/path_test.go b/src/pkg/os/path_test.go
index c1e3fb354..16c4120dc 100644
--- a/src/pkg/os/path_test.go
+++ b/src/pkg/os/path_test.go
@@ -5,6 +5,7 @@
package os_test
import (
+ "io/ioutil"
. "os"
"path/filepath"
"runtime"
@@ -167,24 +168,26 @@ func TestRemoveAll(t *testing.T) {
func TestMkdirAllWithSymlink(t *testing.T) {
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
- t.Log("Skipping test: symlinks don't exist under Windows/Plan 9")
- return
+ t.Skip("Skipping test: symlinks don't exist under Windows/Plan 9")
}
- tmpDir := TempDir()
+ tmpDir, err := ioutil.TempDir("", "TestMkdirAllWithSymlink-")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer RemoveAll(tmpDir)
+
dir := tmpDir + "/dir"
- err := Mkdir(dir, 0755)
+ err = Mkdir(dir, 0755)
if err != nil {
t.Fatalf("Mkdir %s: %s", dir, err)
}
- defer RemoveAll(dir)
link := tmpDir + "/link"
err = Symlink("dir", link)
if err != nil {
t.Fatalf("Symlink %s: %s", link, err)
}
- defer RemoveAll(link)
path := link + "/foo"
err = MkdirAll(path, 0755)
diff --git a/src/pkg/os/pipe_bsd.go b/src/pkg/os/pipe_bsd.go
new file mode 100644
index 000000000..a2ce9a39f
--- /dev/null
+++ b/src/pkg/os/pipe_bsd.go
@@ -0,0 +1,28 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd netbsd openbsd
+
+package os
+
+import "syscall"
+
+// Pipe returns a connected pair of Files; reads from r return bytes written to w.
+// It returns the files and an error, if any.
+func Pipe() (r *File, w *File, err error) {
+ var p [2]int
+
+ // See ../syscall/exec.go for description of lock.
+ syscall.ForkLock.RLock()
+ e := syscall.Pipe(p[0:])
+ if e != nil {
+ syscall.ForkLock.RUnlock()
+ return nil, nil, NewSyscallError("pipe", e)
+ }
+ syscall.CloseOnExec(p[0])
+ syscall.CloseOnExec(p[1])
+ syscall.ForkLock.RUnlock()
+
+ return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
+}
diff --git a/src/pkg/os/pipe_linux.go b/src/pkg/os/pipe_linux.go
new file mode 100644
index 000000000..9bafad84f
--- /dev/null
+++ b/src/pkg/os/pipe_linux.go
@@ -0,0 +1,33 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import "syscall"
+
+// Pipe returns a connected pair of Files; reads from r return bytes written to w.
+// It returns the files and an error, if any.
+func Pipe() (r *File, w *File, err error) {
+ var p [2]int
+
+ e := syscall.Pipe2(p[0:], syscall.O_CLOEXEC)
+ // pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it
+ // might not be implemented.
+ if e == syscall.ENOSYS {
+ // See ../syscall/exec.go for description of lock.
+ syscall.ForkLock.RLock()
+ e = syscall.Pipe(p[0:])
+ if e != nil {
+ syscall.ForkLock.RUnlock()
+ return nil, nil, NewSyscallError("pipe", e)
+ }
+ syscall.CloseOnExec(p[0])
+ syscall.CloseOnExec(p[1])
+ syscall.ForkLock.RUnlock()
+ } else if e != nil {
+ return nil, nil, NewSyscallError("pipe2", e)
+ }
+
+ return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
+}
diff --git a/src/pkg/os/proc.go b/src/pkg/os/proc.go
index 61545f445..38c436ec5 100644
--- a/src/pkg/os/proc.go
+++ b/src/pkg/os/proc.go
@@ -31,4 +31,6 @@ func Getgroups() ([]int, error) {
// Exit causes the current program to exit with the given status code.
// Conventionally, code zero indicates success, non-zero an error.
+// The program terminates immediately; deferred functions are
+// not run.
func Exit(code int) { syscall.Exit(code) }
diff --git a/src/pkg/os/signal/example_test.go b/src/pkg/os/signal/example_test.go
new file mode 100644
index 000000000..600ed315d
--- /dev/null
+++ b/src/pkg/os/signal/example_test.go
@@ -0,0 +1,19 @@
+package signal_test
+
+import (
+ "fmt"
+ "os"
+ "os/signal"
+)
+
+func ExampleNotify() {
+ // Set up channel on which to send signal notifications.
+ // We must use a buffered channel or risk missing the signal
+ // if we're not ready to receive when the signal is sent.
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, os.Interrupt, os.Kill)
+
+ // Block until a signal is received.
+ s := <-c
+ fmt.Println("Got signal:", s)
+}
diff --git a/src/pkg/os/signal/signal_test.go b/src/pkg/os/signal/signal_test.go
index 3494f8c34..509b273aa 100644
--- a/src/pkg/os/signal/signal_test.go
+++ b/src/pkg/os/signal/signal_test.go
@@ -8,6 +8,7 @@ package signal
import (
"os"
+ "runtime"
"syscall"
"testing"
"time"
@@ -58,3 +59,43 @@ func TestSignal(t *testing.T) {
// The first SIGHUP should be waiting for us on c.
waitSig(t, c, syscall.SIGHUP)
}
+
+func TestStress(t *testing.T) {
+ dur := 3 * time.Second
+ if testing.Short() {
+ dur = 100 * time.Millisecond
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+ done := make(chan bool)
+ finished := make(chan bool)
+ go func() {
+ sig := make(chan os.Signal, 1)
+ Notify(sig, syscall.SIGUSR1)
+ Loop:
+ for {
+ select {
+ case <-sig:
+ case <-done:
+ break Loop
+ }
+ }
+ finished <- true
+ }()
+ go func() {
+ Loop:
+ for {
+ select {
+ case <-done:
+ break Loop
+ default:
+ syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
+ runtime.Gosched()
+ }
+ }
+ finished <- true
+ }()
+ time.Sleep(dur)
+ close(done)
+ <-finished
+ <-finished
+}
diff --git a/src/pkg/os/signal/signal_windows_test.go b/src/pkg/os/signal/signal_windows_test.go
index 8d807ff7b..26712f35b 100644
--- a/src/pkg/os/signal/signal_windows_test.go
+++ b/src/pkg/os/signal/signal_windows_test.go
@@ -5,16 +5,16 @@
package signal
import (
- "flag"
+ "bytes"
"os"
+ "os/exec"
+ "path/filepath"
"syscall"
"testing"
"time"
)
-var runCtrlBreakTest = flag.Bool("run_ctlbrk_test", false, "force to run Ctrl+Break test")
-
-func sendCtrlBreak(t *testing.T) {
+func sendCtrlBreak(t *testing.T, pid int) {
d, e := syscall.LoadDLL("kernel32.dll")
if e != nil {
t.Fatalf("LoadDLL: %v\n", e)
@@ -23,29 +23,74 @@ func sendCtrlBreak(t *testing.T) {
if e != nil {
t.Fatalf("FindProc: %v\n", e)
}
- r, _, e := p.Call(0, 0)
+ r, _, e := p.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid))
if r == 0 {
t.Fatalf("GenerateConsoleCtrlEvent: %v\n", e)
}
}
func TestCtrlBreak(t *testing.T) {
- if !*runCtrlBreakTest {
- t.Logf("test disabled; use -run_ctlbrk_test to enable")
- return
- }
- go func() {
- time.Sleep(1 * time.Second)
- sendCtrlBreak(t)
- }()
+ // create source file
+ const source = `
+package main
+
+import (
+ "log"
+ "os"
+ "os/signal"
+ "time"
+)
+
+
+func main() {
c := make(chan os.Signal, 10)
- Notify(c)
+ signal.Notify(c)
select {
case s := <-c:
if s != os.Interrupt {
- t.Fatalf("Wrong signal received: got %q, want %q\n", s, os.Interrupt)
+ log.Fatalf("Wrong signal received: got %q, want %q\n", s, os.Interrupt)
}
case <-time.After(3 * time.Second):
- t.Fatalf("Timeout waiting for Ctrl+Break\n")
+ log.Fatalf("Timeout waiting for Ctrl+Break\n")
+ }
+}
+`
+ name := filepath.Join(os.TempDir(), "ctlbreak")
+ src := name + ".go"
+ defer os.Remove(src)
+ f, err := os.Create(src)
+ if err != nil {
+ t.Fatalf("Failed to create %v: %v", src, err)
+ }
+ defer f.Close()
+ f.Write([]byte(source))
+
+ // compile it
+ exe := name + ".exe"
+ defer os.Remove(exe)
+ o, err := exec.Command("go", "build", "-o", exe, src).CombinedOutput()
+ if err != nil {
+ t.Fatalf("Failed to compile: %v\n%v", err, string(o))
+ }
+
+ // run it
+ cmd := exec.Command(exe)
+ var b bytes.Buffer
+ cmd.Stdout = &b
+ cmd.Stderr = &b
+ cmd.SysProcAttr = &syscall.SysProcAttr{
+ CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
+ }
+ err = cmd.Start()
+ if err != nil {
+ t.Fatalf("Start failed: %v", err)
+ }
+ go func() {
+ time.Sleep(1 * time.Second)
+ sendCtrlBreak(t, cmd.Process.Pid)
+ }()
+ err = cmd.Wait()
+ if err != nil {
+ t.Fatalf("Program exited with error: %v\n%v", err, string(b.Bytes()))
}
}
diff --git a/src/pkg/os/stat_darwin.go b/src/pkg/os/stat_darwin.go
index 2e5967d5c..0eea52201 100644
--- a/src/pkg/os/stat_darwin.go
+++ b/src/pkg/os/stat_darwin.go
@@ -9,9 +9,9 @@ import (
"time"
)
-func sameFile(sys1, sys2 interface{}) bool {
- stat1 := sys1.(*syscall.Stat_t)
- stat2 := sys2.(*syscall.Stat_t)
+func sameFile(fs1, fs2 *fileStat) bool {
+ stat1 := fs1.sys.(*syscall.Stat_t)
+ stat2 := fs2.sys.(*syscall.Stat_t)
return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
}
diff --git a/src/pkg/os/stat_freebsd.go b/src/pkg/os/stat_freebsd.go
index 6ba84f438..2ffb60fe2 100644
--- a/src/pkg/os/stat_freebsd.go
+++ b/src/pkg/os/stat_freebsd.go
@@ -9,9 +9,9 @@ import (
"time"
)
-func sameFile(sys1, sys2 interface{}) bool {
- stat1 := sys1.(*syscall.Stat_t)
- stat2 := sys2.(*syscall.Stat_t)
+func sameFile(fs1, fs2 *fileStat) bool {
+ stat1 := fs1.sys.(*syscall.Stat_t)
+ stat2 := fs2.sys.(*syscall.Stat_t)
return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
}
diff --git a/src/pkg/os/stat_linux.go b/src/pkg/os/stat_linux.go
index 00506b2b6..605c1d9b6 100644
--- a/src/pkg/os/stat_linux.go
+++ b/src/pkg/os/stat_linux.go
@@ -9,9 +9,9 @@ import (
"time"
)
-func sameFile(sys1, sys2 interface{}) bool {
- stat1 := sys1.(*syscall.Stat_t)
- stat2 := sys2.(*syscall.Stat_t)
+func sameFile(fs1, fs2 *fileStat) bool {
+ stat1 := fs1.sys.(*syscall.Stat_t)
+ stat2 := fs2.sys.(*syscall.Stat_t)
return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
}
diff --git a/src/pkg/os/stat_netbsd.go b/src/pkg/os/stat_netbsd.go
index 00506b2b6..2ffb60fe2 100644
--- a/src/pkg/os/stat_netbsd.go
+++ b/src/pkg/os/stat_netbsd.go
@@ -9,9 +9,9 @@ import (
"time"
)
-func sameFile(sys1, sys2 interface{}) bool {
- stat1 := sys1.(*syscall.Stat_t)
- stat2 := sys2.(*syscall.Stat_t)
+func sameFile(fs1, fs2 *fileStat) bool {
+ stat1 := fs1.sys.(*syscall.Stat_t)
+ stat2 := fs2.sys.(*syscall.Stat_t)
return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
}
@@ -19,7 +19,7 @@ func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &fileStat{
name: basename(name),
size: int64(st.Size),
- modTime: timespecToTime(st.Mtim),
+ modTime: timespecToTime(st.Mtimespec),
sys: st,
}
fs.mode = FileMode(st.Mode & 0777)
@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time {
// For testing.
func atime(fi FileInfo) time.Time {
- return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim)
+ return timespecToTime(fi.Sys().(*syscall.Stat_t).Atimespec)
}
diff --git a/src/pkg/os/stat_openbsd.go b/src/pkg/os/stat_openbsd.go
index 00506b2b6..605c1d9b6 100644
--- a/src/pkg/os/stat_openbsd.go
+++ b/src/pkg/os/stat_openbsd.go
@@ -9,9 +9,9 @@ import (
"time"
)
-func sameFile(sys1, sys2 interface{}) bool {
- stat1 := sys1.(*syscall.Stat_t)
- stat2 := sys2.(*syscall.Stat_t)
+func sameFile(fs1, fs2 *fileStat) bool {
+ stat1 := fs1.sys.(*syscall.Stat_t)
+ stat2 := fs2.sys.(*syscall.Stat_t)
return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
}
diff --git a/src/pkg/os/stat_plan9.go b/src/pkg/os/stat_plan9.go
index a7990a359..25c9a8c14 100644
--- a/src/pkg/os/stat_plan9.go
+++ b/src/pkg/os/stat_plan9.go
@@ -9,13 +9,13 @@ import (
"time"
)
-func sameFile(sys1, sys2 interface{}) bool {
- a := sys1.(*Dir)
- b := sys2.(*Dir)
+func sameFile(fs1, fs2 *fileStat) bool {
+ a := fs1.sys.(*syscall.Dir)
+ b := fs2.sys.(*syscall.Dir)
return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev
}
-func fileInfoFromStat(d *Dir) FileInfo {
+func fileInfoFromStat(d *syscall.Dir) FileInfo {
fs := &fileStat{
name: d.Name,
size: int64(d.Length),
@@ -38,8 +38,8 @@ func fileInfoFromStat(d *Dir) FileInfo {
return fs
}
-// arg is an open *File or a path string.
-func dirstat(arg interface{}) (d *Dir, err error) {
+// arg is an open *File or a path string.
+func dirstat(arg interface{}) (*syscall.Dir, error) {
var name string
// This is big enough for most stat messages
@@ -50,41 +50,45 @@ func dirstat(arg interface{}) (d *Dir, err error) {
buf := make([]byte, size)
var n int
+ var err error
switch a := arg.(type) {
case *File:
name = a.name
n, err = syscall.Fstat(a.fd, buf)
case string:
name = a
- n, err = syscall.Stat(name, buf)
+ n, err = syscall.Stat(a, buf)
+ default:
+ panic("phase error in dirstat")
}
if err != nil {
return nil, &PathError{"stat", name, err}
}
if n < syscall.STATFIXLEN {
- return nil, &PathError{"stat", name, errShortStat}
+ return nil, &PathError{"stat", name, syscall.ErrShortStat}
}
// Pull the real size out of the stat message.
- s, _ := gbit16(buf)
- size = int(s)
+ size = int(uint16(buf[0]) | uint16(buf[1])<<8)
// If the stat message is larger than our buffer we will
// go around the loop and allocate one that is big enough.
- if size <= n {
- d, err = UnmarshalDir(buf[:n])
- if err != nil {
- return nil, &PathError{"stat", name, err}
- }
- return
+ if size > n {
+ continue
}
+
+ d, err := syscall.UnmarshalDir(buf[:n])
+ if err != nil {
+ return nil, &PathError{"stat", name, err}
+ }
+ return d, nil
}
- return nil, &PathError{"stat", name, errBadStat}
+ return nil, &PathError{"stat", name, syscall.ErrBadStat}
}
-// Stat returns a FileInfo structure describing the named file.
+// Stat returns a FileInfo describing the named file.
// If there is an error, it will be of type *PathError.
-func Stat(name string) (FileInfo, error) {
+func Stat(name string) (fi FileInfo, err error) {
d, err := dirstat(name)
if err != nil {
return nil, err
@@ -92,15 +96,15 @@ func Stat(name string) (FileInfo, error) {
return fileInfoFromStat(d), nil
}
-// Lstat returns the FileInfo structure describing the named file.
-// If the file is a symbolic link (though Plan 9 does not have symbolic links),
-// the returned FileInfo describes the symbolic link. Lstat makes no attempt to follow the link.
+// Lstat returns a FileInfo describing the named file.
+// If the file is a symbolic link, the returned FileInfo
+// describes the symbolic link. Lstat makes no attempt to follow the link.
// If there is an error, it will be of type *PathError.
-func Lstat(name string) (FileInfo, error) {
+func Lstat(name string) (fi FileInfo, err error) {
return Stat(name)
}
// For testing.
func atime(fi FileInfo) time.Time {
- return time.Unix(int64(fi.Sys().(*Dir).Atime), 0)
+ return time.Unix(int64(fi.Sys().(*syscall.Dir).Atime), 0)
}
diff --git a/src/pkg/os/stat_windows.go b/src/pkg/os/stat_windows.go
index 75351c805..8394c2b32 100644
--- a/src/pkg/os/stat_windows.go
+++ b/src/pkg/os/stat_windows.go
@@ -5,9 +5,7 @@
package os
import (
- "sync"
"syscall"
- "time"
"unsafe"
)
@@ -22,7 +20,7 @@ func (file *File) Stat() (fi FileInfo, err error) {
return Stat(file.name)
}
if file.name == DevNull {
- return statDevNull()
+ return &devNullStat, nil
}
var d syscall.ByHandleFileInformation
e := syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &d)
@@ -30,11 +28,18 @@ func (file *File) Stat() (fi FileInfo, err error) {
return nil, &PathError{"GetFileInformationByHandle", file.name, e}
}
return &fileStat{
- name: basename(file.name),
- size: mkSize(d.FileSizeHigh, d.FileSizeLow),
- modTime: mkModTime(d.LastWriteTime),
- mode: mkMode(d.FileAttributes),
- sys: mkSysFromFI(&d),
+ name: basename(file.name),
+ sys: syscall.Win32FileAttributeData{
+ FileAttributes: d.FileAttributes,
+ CreationTime: d.CreationTime,
+ LastAccessTime: d.LastAccessTime,
+ LastWriteTime: d.LastWriteTime,
+ FileSizeHigh: d.FileSizeHigh,
+ FileSizeLow: d.FileSizeLow,
+ },
+ vol: d.VolumeSerialNumber,
+ idxhi: d.FileIndexHigh,
+ idxlo: d.FileIndexLow,
}, nil
}
@@ -45,25 +50,23 @@ func Stat(name string) (fi FileInfo, err error) {
return nil, &PathError{"Stat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)}
}
if name == DevNull {
- return statDevNull()
+ return &devNullStat, nil
+ }
+ fs := &fileStat{name: basename(name)}
+ namep, e := syscall.UTF16PtrFromString(name)
+ if e != nil {
+ return nil, &PathError{"Stat", name, e}
}
- var d syscall.Win32FileAttributeData
- e := syscall.GetFileAttributesEx(syscall.StringToUTF16Ptr(name), syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&d)))
+ e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fs.sys)))
if e != nil {
return nil, &PathError{"GetFileAttributesEx", name, e}
}
- path := name
- if !isAbs(path) {
+ fs.path = name
+ if !isAbs(fs.path) {
cwd, _ := Getwd()
- path = cwd + `\` + path
+ fs.path = cwd + `\` + fs.path
}
- return &fileStat{
- name: basename(name),
- size: mkSize(d.FileSizeHigh, d.FileSizeLow),
- modTime: mkModTime(d.LastWriteTime),
- mode: mkMode(d.FileAttributes),
- sys: mkSys(path, d.LastAccessTime, d.CreationTime),
- }, nil
+ return fs, nil
}
// Lstat returns the FileInfo structure describing the named file.
@@ -75,22 +78,6 @@ func Lstat(name string) (fi FileInfo, err error) {
return Stat(name)
}
-// statDevNull return FileInfo structure describing DevNull file ("NUL").
-// It creates invented data, since none of windows api will return
-// that information.
-func statDevNull() (fi FileInfo, err error) {
- return &fileStat{
- name: DevNull,
- mode: ModeDevice | ModeCharDevice | 0666,
- sys: &winSys{
- // hopefully this will work for SameFile
- vol: 0,
- idxhi: 0,
- idxlo: 0,
- },
- }, nil
-}
-
// basename removes trailing slashes and the leading
// directory name and drive letter from path name.
func basename(name string) string {
@@ -168,91 +155,3 @@ func volumeName(path string) (v string) {
}
return ""
}
-
-type winSys struct {
- sync.Mutex
- path string
- atime, ctime syscall.Filetime
- vol, idxhi, idxlo uint32
-}
-
-func mkSize(hi, lo uint32) int64 {
- return int64(hi)<<32 + int64(lo)
-}
-
-func mkModTime(mtime syscall.Filetime) time.Time {
- return time.Unix(0, mtime.Nanoseconds())
-}
-
-func mkMode(fa uint32) (m FileMode) {
- if fa&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
- m |= ModeDir
- }
- if fa&syscall.FILE_ATTRIBUTE_READONLY != 0 {
- m |= 0444
- } else {
- m |= 0666
- }
- return m
-}
-
-func mkSys(path string, atime, ctime syscall.Filetime) *winSys {
- return &winSys{
- path: path,
- atime: atime,
- ctime: ctime,
- }
-}
-
-func mkSysFromFI(i *syscall.ByHandleFileInformation) *winSys {
- return &winSys{
- atime: i.LastAccessTime,
- ctime: i.CreationTime,
- vol: i.VolumeSerialNumber,
- idxhi: i.FileIndexHigh,
- idxlo: i.FileIndexLow,
- }
-}
-
-func (s *winSys) loadFileId() error {
- if s.path == "" {
- // already done
- return nil
- }
- s.Lock()
- defer s.Unlock()
- h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(s.path), 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
- if e != nil {
- return e
- }
- defer syscall.CloseHandle(h)
- var i syscall.ByHandleFileInformation
- e = syscall.GetFileInformationByHandle(syscall.Handle(h), &i)
- if e != nil {
- return e
- }
- s.path = ""
- s.vol = i.VolumeSerialNumber
- s.idxhi = i.FileIndexHigh
- s.idxlo = i.FileIndexLow
- return nil
-}
-
-func sameFile(sys1, sys2 interface{}) bool {
- s1 := sys1.(*winSys)
- s2 := sys2.(*winSys)
- e := s1.loadFileId()
- if e != nil {
- panic(e)
- }
- e = s2.loadFileId()
- if e != nil {
- panic(e)
- }
- return s1.vol == s2.vol && s1.idxhi == s2.idxhi && s1.idxlo == s2.idxlo
-}
-
-// For testing.
-func atime(fi FileInfo) time.Time {
- return time.Unix(0, fi.Sys().(*winSys).atime.Nanoseconds())
-}
diff --git a/src/pkg/os/types.go b/src/pkg/os/types.go
index 0c95c9cec..473d431d4 100644
--- a/src/pkg/os/types.go
+++ b/src/pkg/os/types.go
@@ -12,7 +12,7 @@ import (
// Getpagesize returns the underlying system's memory page size.
func Getpagesize() int { return syscall.Getpagesize() }
-// A FileInfo describes a file and is returned by Stat and Lstat
+// A FileInfo describes a file and is returned by Stat and Lstat.
type FileInfo interface {
Name() string // base name of the file
Size() int64 // length in bytes for regular files; system-dependent for others
@@ -88,26 +88,19 @@ func (m FileMode) IsDir() bool {
return m&ModeDir != 0
}
+// IsRegular reports whether m describes a regular file.
+// That is, it tests that no mode type bits are set.
+func (m FileMode) IsRegular() bool {
+ return m&ModeType == 0
+}
+
// Perm returns the Unix permission bits in m.
func (m FileMode) Perm() FileMode {
return m & ModePerm
}
-// A fileStat is the implementation of FileInfo returned by Stat and Lstat.
-type fileStat struct {
- name string
- size int64
- mode FileMode
- modTime time.Time
- sys interface{}
-}
-
-func (fs *fileStat) Name() string { return fs.name }
-func (fs *fileStat) Size() int64 { return fs.size }
-func (fs *fileStat) Mode() FileMode { return fs.mode }
-func (fs *fileStat) ModTime() time.Time { return fs.modTime }
-func (fs *fileStat) IsDir() bool { return fs.mode.IsDir() }
-func (fs *fileStat) Sys() interface{} { return fs.sys }
+func (fs *fileStat) Name() string { return fs.name }
+func (fs *fileStat) IsDir() bool { return fs.Mode().IsDir() }
// SameFile reports whether fi1 and fi2 describe the same file.
// For example, on Unix this means that the device and inode fields
@@ -121,5 +114,5 @@ func SameFile(fi1, fi2 FileInfo) bool {
if !ok1 || !ok2 {
return false
}
- return sameFile(fs1.sys, fs2.sys)
+ return sameFile(fs1, fs2)
}
diff --git a/src/pkg/os/types_notwin.go b/src/pkg/os/types_notwin.go
new file mode 100644
index 000000000..ea1a07393
--- /dev/null
+++ b/src/pkg/os/types_notwin.go
@@ -0,0 +1,25 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !windows
+
+package os
+
+import (
+ "time"
+)
+
+// A fileStat is the implementation of FileInfo returned by Stat and Lstat.
+type fileStat struct {
+ name string
+ size int64
+ mode FileMode
+ modTime time.Time
+ sys interface{}
+}
+
+func (fs *fileStat) Size() int64 { return fs.size }
+func (fs *fileStat) Mode() FileMode { return fs.mode }
+func (fs *fileStat) ModTime() time.Time { return fs.modTime }
+func (fs *fileStat) Sys() interface{} { return fs.sys }
diff --git a/src/pkg/os/types_windows.go b/src/pkg/os/types_windows.go
new file mode 100644
index 000000000..38901681e
--- /dev/null
+++ b/src/pkg/os/types_windows.go
@@ -0,0 +1,104 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import (
+ "sync"
+ "syscall"
+ "time"
+)
+
+// A fileStat is the implementation of FileInfo returned by Stat and Lstat.
+type fileStat struct {
+ name string
+ sys syscall.Win32FileAttributeData
+
+ // used to implement SameFile
+ sync.Mutex
+ path string
+ vol uint32
+ idxhi uint32
+ idxlo uint32
+}
+
+func (fs *fileStat) Size() int64 {
+ return int64(fs.sys.FileSizeHigh)<<32 + int64(fs.sys.FileSizeLow)
+}
+
+func (fs *fileStat) Mode() (m FileMode) {
+ if fs == &devNullStat {
+ return ModeDevice | ModeCharDevice | 0666
+ }
+ if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
+ m |= ModeDir | 0111
+ }
+ if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
+ m |= 0444
+ } else {
+ m |= 0666
+ }
+ return m
+}
+
+func (fs *fileStat) ModTime() time.Time {
+ return time.Unix(0, fs.sys.LastWriteTime.Nanoseconds())
+}
+
+// Sys returns syscall.Win32FileAttributeData for file fs.
+func (fs *fileStat) Sys() interface{} { return &fs.sys }
+
+func (fs *fileStat) loadFileId() error {
+ fs.Lock()
+ defer fs.Unlock()
+ if fs.path == "" {
+ // already done
+ return nil
+ }
+ pathp, err := syscall.UTF16PtrFromString(fs.path)
+ if err != nil {
+ return err
+ }
+ h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
+ if err != nil {
+ return err
+ }
+ defer syscall.CloseHandle(h)
+ var i syscall.ByHandleFileInformation
+ err = syscall.GetFileInformationByHandle(syscall.Handle(h), &i)
+ if err != nil {
+ return err
+ }
+ fs.path = ""
+ fs.vol = i.VolumeSerialNumber
+ fs.idxhi = i.FileIndexHigh
+ fs.idxlo = i.FileIndexLow
+ return nil
+}
+
+// devNullStat is fileStat structure describing DevNull file ("NUL").
+var devNullStat = fileStat{
+ name: DevNull,
+ // hopefully this will work for SameFile
+ vol: 0,
+ idxhi: 0,
+ idxlo: 0,
+}
+
+func sameFile(fs1, fs2 *fileStat) bool {
+ e := fs1.loadFileId()
+ if e != nil {
+ return false
+ }
+ e = fs2.loadFileId()
+ if e != nil {
+ return false
+ }
+ return fs1.vol == fs2.vol && fs1.idxhi == fs2.idxhi && fs1.idxlo == fs2.idxlo
+}
+
+// For testing.
+func atime(fi FileInfo) time.Time {
+ return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
+}
diff --git a/src/pkg/os/user/lookup.go b/src/pkg/os/user/lookup.go
new file mode 100644
index 000000000..09f00c7bd
--- /dev/null
+++ b/src/pkg/os/user/lookup.go
@@ -0,0 +1,22 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package user
+
+// Current returns the current user.
+func Current() (*User, error) {
+ return current()
+}
+
+// Lookup looks up a user by username. If the user cannot be found, the
+// returned error is of type UnknownUserError.
+func Lookup(username string) (*User, error) {
+ return lookup(username)
+}
+
+// LookupId looks up a user by userid. If the user cannot be found, the
+// returned error is of type UnknownUserIdError.
+func LookupId(uid string) (*User, error) {
+ return lookupId(uid)
+}
diff --git a/src/pkg/os/user/lookup_stubs.go b/src/pkg/os/user/lookup_stubs.go
index 415f869f2..ad06907b5 100644
--- a/src/pkg/os/user/lookup_stubs.go
+++ b/src/pkg/os/user/lookup_stubs.go
@@ -15,14 +15,14 @@ func init() {
implemented = false
}
-func Current() (*User, error) {
+func current() (*User, error) {
return nil, fmt.Errorf("user: Current not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
-func Lookup(username string) (*User, error) {
+func lookup(username string) (*User, error) {
return nil, fmt.Errorf("user: Lookup not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
-func LookupId(string) (*User, error) {
+func lookupId(uid string) (*User, error) {
return nil, fmt.Errorf("user: LookupId not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
diff --git a/src/pkg/os/user/lookup_unix.go b/src/pkg/os/user/lookup_unix.go
index 241957c33..609542263 100644
--- a/src/pkg/os/user/lookup_unix.go
+++ b/src/pkg/os/user/lookup_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin freebsd linux
+// +build darwin freebsd linux netbsd openbsd
// +build cgo
package user
@@ -29,28 +29,23 @@ static int mygetpwuid_r(int uid, struct passwd *pwd,
*/
import "C"
-// Current returns the current user.
-func Current() (*User, error) {
- return lookup(syscall.Getuid(), "", false)
+func current() (*User, error) {
+ return lookupUnix(syscall.Getuid(), "", false)
}
-// Lookup looks up a user by username. If the user cannot be found,
-// the returned error is of type UnknownUserError.
-func Lookup(username string) (*User, error) {
- return lookup(-1, username, true)
+func lookup(username string) (*User, error) {
+ return lookupUnix(-1, username, true)
}
-// LookupId looks up a user by userid. If the user cannot be found,
-// the returned error is of type UnknownUserIdError.
-func LookupId(uid string) (*User, error) {
+func lookupId(uid string) (*User, error) {
i, e := strconv.Atoi(uid)
if e != nil {
return nil, e
}
- return lookup(i, "", false)
+ return lookupUnix(i, "", false)
}
-func lookup(uid int, username string, lookupByName bool) (*User, error) {
+func lookupUnix(uid int, username string, lookupByName bool) (*User, error) {
var pwd C.struct_passwd
var result *C.struct_passwd
diff --git a/src/pkg/os/user/lookup_windows.go b/src/pkg/os/user/lookup_windows.go
index 993687115..a0a8a4ec1 100644
--- a/src/pkg/os/user/lookup_windows.go
+++ b/src/pkg/os/user/lookup_windows.go
@@ -16,13 +16,21 @@ func lookupFullName(domain, username, domainAndUser string) (string, error) {
syscall.NameSamCompatible, syscall.NameDisplay, 50)
if e != nil {
// domain lookup failed, perhaps this pc is not part of domain
- d := syscall.StringToUTF16Ptr(domain)
- u := syscall.StringToUTF16Ptr(username)
- var p *byte
- e := syscall.NetUserGetInfo(d, u, 10, &p)
+ d, e := syscall.UTF16PtrFromString(domain)
+ if e != nil {
+ return "", e
+ }
+ u, e := syscall.UTF16PtrFromString(username)
if e != nil {
return "", e
}
+ var p *byte
+ e = syscall.NetUserGetInfo(d, u, 10, &p)
+ if e != nil {
+ // path executed when a domain user is disconnected from the domain
+ // pretend username is fullname
+ return username, nil
+ }
defer syscall.NetApiBufferFree(p)
i := (*syscall.UserInfo10)(unsafe.Pointer(p))
if i.FullName == nil {
@@ -60,8 +68,7 @@ func newUser(usid *syscall.SID, gid, dir string) (*User, error) {
return u, nil
}
-// Current returns the current user.
-func Current() (*User, error) {
+func current() (*User, error) {
t, e := syscall.OpenCurrentProcessToken()
if e != nil {
return nil, e
@@ -95,8 +102,7 @@ func newUserFromSid(usid *syscall.SID) (*User, error) {
return newUser(usid, gid, dir)
}
-// Lookup looks up a user by username.
-func Lookup(username string) (*User, error) {
+func lookup(username string) (*User, error) {
sid, _, t, e := syscall.LookupSID("", username)
if e != nil {
return nil, e
@@ -107,8 +113,7 @@ func Lookup(username string) (*User, error) {
return newUserFromSid(sid)
}
-// LookupId looks up a user by userid.
-func LookupId(uid string) (*User, error) {
+func lookupId(uid string) (*User, error) {
sid, e := syscall.StringToSid(uid)
if e != nil {
return nil, e
diff --git a/src/pkg/os/user/user_test.go b/src/pkg/os/user/user_test.go
index b812ebce7..444a9aacd 100644
--- a/src/pkg/os/user/user_test.go
+++ b/src/pkg/os/user/user_test.go
@@ -5,41 +5,34 @@
package user
import (
- "os"
"runtime"
"testing"
)
-func skip(t *testing.T) bool {
+func check(t *testing.T) {
if !implemented {
- t.Logf("user: not implemented; skipping tests")
- return true
+ t.Skip("user: not implemented; skipping tests")
}
-
switch runtime.GOOS {
case "linux", "freebsd", "darwin", "windows":
- return false
+ // test supported
+ default:
+ t.Skipf("user: Lookup not implemented on %q; skipping test", runtime.GOOS)
}
-
- t.Logf("user: Lookup not implemented on %s; skipping test", runtime.GOOS)
- return true
}
func TestCurrent(t *testing.T) {
- if skip(t) {
- return
- }
+ check(t)
u, err := Current()
if err != nil {
t.Fatalf("Current: %v", err)
}
- fi, err := os.Stat(u.HomeDir)
- if err != nil || !fi.IsDir() {
- t.Errorf("expected a valid HomeDir; stat(%q): err=%v", u.HomeDir, err)
+ if u.HomeDir == "" {
+ t.Errorf("didn't get a HomeDir")
}
if u.Username == "" {
- t.Fatalf("didn't get a username")
+ t.Errorf("didn't get a username")
}
}
@@ -55,8 +48,7 @@ func compare(t *testing.T, want, got *User) {
}
// TODO(brainman): fix it once we know how.
if runtime.GOOS == "windows" {
- t.Log("skipping Gid and HomeDir comparisons")
- return
+ t.Skip("skipping Gid and HomeDir comparisons")
}
if want.Gid != got.Gid {
t.Errorf("got Gid=%q; want %q", got.Gid, want.Gid)
@@ -67,9 +59,7 @@ func compare(t *testing.T, want, got *User) {
}
func TestLookup(t *testing.T) {
- if skip(t) {
- return
- }
+ check(t)
want, err := Current()
if err != nil {
@@ -83,9 +73,7 @@ func TestLookup(t *testing.T) {
}
func TestLookupId(t *testing.T) {
- if skip(t) {
- return
- }
+ check(t)
want, err := Current()
if err != nil {
diff --git a/src/pkg/path/filepath/example_unix_test.go b/src/pkg/path/filepath/example_unix_test.go
new file mode 100644
index 000000000..f3fe076c3
--- /dev/null
+++ b/src/pkg/path/filepath/example_unix_test.go
@@ -0,0 +1,39 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !windows,!plan9
+
+package filepath_test
+
+import (
+ "fmt"
+ "path/filepath"
+)
+
+func ExampleSplitList() {
+ fmt.Println("On Unix:", filepath.SplitList("/a/b/c:/usr/bin"))
+ // Output:
+ // On Unix: [/a/b/c /usr/bin]
+}
+
+func ExampleRel() {
+ paths := []string{
+ "/a/b/c",
+ "/b/c",
+ "./b/c",
+ }
+ base := "/a"
+
+ fmt.Println("On Unix:")
+ for _, p := range paths {
+ rel, err := filepath.Rel(base, p)
+ fmt.Printf("%q: %q %v\n", p, rel, err)
+ }
+
+ // Output:
+ // On Unix:
+ // "/a/b/c": "b/c" <nil>
+ // "/b/c": "../b/c" <nil>
+ // "./b/c": "" Rel: can't make b/c relative to /a
+}
diff --git a/src/pkg/path/filepath/match_test.go b/src/pkg/path/filepath/match_test.go
index 7b0ea8017..f1bc60e35 100644
--- a/src/pkg/path/filepath/match_test.go
+++ b/src/pkg/path/filepath/match_test.go
@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package filepath_test
+package filepath
import (
- . "path/filepath"
"runtime"
"strings"
"testing"
diff --git a/src/pkg/path/filepath/path.go b/src/pkg/path/filepath/path.go
index 815021bd0..f8c7e4b2f 100644
--- a/src/pkg/path/filepath/path.go
+++ b/src/pkg/path/filepath/path.go
@@ -13,6 +13,45 @@ import (
"strings"
)
+// A lazybuf is a lazily constructed path buffer.
+// It supports append, reading previously appended bytes,
+// and retrieving the final string. It does not allocate a buffer
+// to hold the output until that output diverges from s.
+type lazybuf struct {
+ path string
+ buf []byte
+ w int
+ volAndPath string
+ volLen int
+}
+
+func (b *lazybuf) index(i int) byte {
+ if b.buf != nil {
+ return b.buf[i]
+ }
+ return b.path[i]
+}
+
+func (b *lazybuf) append(c byte) {
+ if b.buf == nil {
+ if b.w < len(b.path) && b.path[b.w] == c {
+ b.w++
+ return
+ }
+ b.buf = make([]byte, len(b.path))
+ copy(b.buf, b.path[:b.w])
+ }
+ b.buf[b.w] = c
+ b.w++
+}
+
+func (b *lazybuf) string() string {
+ if b.buf == nil {
+ return b.volAndPath[:b.volLen+b.w]
+ }
+ return b.volAndPath[:b.volLen] + string(b.buf[:b.w])
+}
+
const (
Separator = os.PathSeparator
ListSeparator = os.PathListSeparator
@@ -40,14 +79,15 @@ const (
// Getting Dot-Dot Right,''
// http://plan9.bell-labs.com/sys/doc/lexnames.html
func Clean(path string) string {
- vol := VolumeName(path)
- path = path[len(vol):]
+ originalPath := path
+ volLen := volumeNameLen(path)
+ path = path[volLen:]
if path == "" {
- if len(vol) > 1 && vol[1] != ':' {
+ if volLen > 1 && originalPath[1] != ':' {
// should be UNC
- return FromSlash(vol)
+ return FromSlash(originalPath)
}
- return vol + "."
+ return originalPath + "."
}
rooted := os.IsPathSeparator(path[0])
@@ -57,11 +97,11 @@ func Clean(path string) string {
// dotdot is index in buf where .. must stop, either because
// it is the leading slash or it is a leading ../../.. prefix.
n := len(path)
- buf := []byte(path)
- r, w, dotdot := 0, 0, 0
+ out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
+ r, dotdot := 0, 0
if rooted {
- buf[0] = Separator
- r, w, dotdot = 1, 1, 1
+ out.append(Separator)
+ r, dotdot = 1, 1
}
for r < n {
@@ -76,46 +116,40 @@ func Clean(path string) string {
// .. element: remove to last separator
r += 2
switch {
- case w > dotdot:
+ case out.w > dotdot:
// can backtrack
- w--
- for w > dotdot && !os.IsPathSeparator(buf[w]) {
- w--
+ out.w--
+ for out.w > dotdot && !os.IsPathSeparator(out.index(out.w)) {
+ out.w--
}
case !rooted:
// cannot backtrack, but not rooted, so append .. element.
- if w > 0 {
- buf[w] = Separator
- w++
+ if out.w > 0 {
+ out.append(Separator)
}
- buf[w] = '.'
- w++
- buf[w] = '.'
- w++
- dotdot = w
+ out.append('.')
+ out.append('.')
+ dotdot = out.w
}
default:
// real path element.
// add slash if needed
- if rooted && w != 1 || !rooted && w != 0 {
- buf[w] = Separator
- w++
+ if rooted && out.w != 1 || !rooted && out.w != 0 {
+ out.append(Separator)
}
// copy element
for ; r < n && !os.IsPathSeparator(path[r]); r++ {
- buf[w] = path[r]
- w++
+ out.append(path[r])
}
}
}
// Turn empty string into "."
- if w == 0 {
- buf[w] = '.'
- w++
+ if out.w == 0 {
+ out.append('.')
}
- return FromSlash(vol + string(buf[0:w]))
+ return FromSlash(out.string())
}
// ToSlash returns the result of replacing each separator character
@@ -142,10 +176,7 @@ func FromSlash(path string) string {
// usually found in PATH or GOPATH environment variables.
// Unlike strings.Split, SplitList returns an empty slice when passed an empty string.
func SplitList(path string) []string {
- if path == "" {
- return []string{}
- }
- return strings.Split(path, string(ListSeparator))
+ return splitList(path)
}
// Split splits path immediately following the final Separator,
@@ -291,13 +322,18 @@ func Rel(basepath, targpath string) (string, error) {
var SkipDir = errors.New("skip this directory")
// WalkFunc is the type of the function called for each file or directory
-// visited by Walk. If there was a problem walking to the file or directory
-// named by path, the incoming error will describe the problem and the
-// function can decide how to handle that error (and Walk will not descend
-// into that directory). If an error is returned, processing stops. The
-// sole exception is that if path is a directory and the function returns the
-// special value SkipDir, the contents of the directory are skipped
-// and processing continues as usual on the next file.
+// visited by Walk. The path argument contains the argument to Walk as a
+// prefix; that is, if Walk is called with "dir", which is a directory
+// containing the file "a", the walk function will be called with argument
+// "dir/a". The info argument is the os.FileInfo for the named path.
+//
+// If there was a problem walking to the file or directory named by path, the
+// incoming error will describe the problem and the function can decide how
+// to handle that error (and Walk will not descend into that directory). If
+// an error is returned, processing stops. The sole exception is that if path
+// is a directory and the function returns the special value SkipDir, the
+// contents of the directory are skipped and processing continues as usual on
+// the next file.
type WalkFunc func(path string, info os.FileInfo, err error) error
// walk recursively descends path, calling w.
@@ -335,6 +371,7 @@ func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
// and directories are filtered by walkFn. The files are walked in lexical
// order, which makes the output deterministic but means that for very
// large directories Walk can be inefficient.
+// Walk does not follow symbolic links.
func Walk(root string, walkFn WalkFunc) error {
info, err := os.Lstat(root)
if err != nil {
@@ -397,7 +434,8 @@ func Base(path string) string {
}
// Dir returns all but the last element of path, typically the path's directory.
-// Trailing path separators are removed before processing.
+// After dropping the final element, the path is Cleaned and trailing
+// slashes are removed.
// If the path is empty, Dir returns ".".
// If the path consists entirely of separators, Dir returns a single separator.
// The returned path does not end in a separator unless it is the root directory.
@@ -417,3 +455,11 @@ func Dir(path string) string {
}
return vol + dir
}
+
+// VolumeName returns leading volume name.
+// Given "C:\foo\bar" it returns "C:" under windows.
+// Given "\\host\share\foo" it returns "\\host\share".
+// On other platforms it returns "".
+func VolumeName(path string) (v string) {
+ return path[:volumeNameLen(path)]
+}
diff --git a/src/pkg/path/filepath/path_plan9.go b/src/pkg/path/filepath/path_plan9.go
index 59a5812dd..12e85aae0 100644
--- a/src/pkg/path/filepath/path_plan9.go
+++ b/src/pkg/path/filepath/path_plan9.go
@@ -11,13 +11,20 @@ func IsAbs(path string) bool {
return strings.HasPrefix(path, "/") || strings.HasPrefix(path, "#")
}
-// VolumeName returns the leading volume name on Windows.
-// It returns "" elsewhere.
-func VolumeName(path string) string {
- return ""
+// volumeNameLen returns length of the leading volume name on Windows.
+// It returns 0 elsewhere.
+func volumeNameLen(path string) int {
+ return 0
}
// HasPrefix exists for historical compatibility and should not be used.
func HasPrefix(p, prefix string) bool {
return strings.HasPrefix(p, prefix)
}
+
+func splitList(path string) []string {
+ if path == "" {
+ return []string{}
+ }
+ return strings.Split(path, string(ListSeparator))
+}
diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go
index 070905fd3..e768ad32f 100644
--- a/src/pkg/path/filepath/path_test.go
+++ b/src/pkg/path/filepath/path_test.go
@@ -20,7 +20,6 @@ type PathTest struct {
var cleantests = []PathTest{
// Already clean
- {"", "."},
{"abc", "abc"},
{"abc/def", "abc/def"},
{"a/b/c", "a/b/c"},
@@ -31,6 +30,9 @@ var cleantests = []PathTest{
{"/abc", "/abc"},
{"/", "/"},
+ // Empty is current dir
+ {"", "."},
+
// Remove trailing slash
{"abc/", "abc"},
{"abc/def/", "abc/def"},
@@ -61,6 +63,7 @@ var cleantests = []PathTest{
{"abc/def/../../..", ".."},
{"/abc/def/../../..", "/"},
{"abc/def/../../../ghi/jkl/../../../mno", "../../mno"},
+ {"/../abc", "/abc"},
// Combinations
{"abc/./../def", "def"},
@@ -99,6 +102,16 @@ func TestClean(t *testing.T) {
if s := filepath.Clean(test.path); s != test.result {
t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
}
+ if s := filepath.Clean(test.result); s != test.result {
+ t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result)
+ }
+ }
+
+ for _, test := range tests {
+ allocs := testing.AllocsPerRun(100, func() { filepath.Clean(test.result) })
+ if allocs > 0 {
+ t.Errorf("Clean(%q): %v allocs, want zero", test.result, allocs)
+ }
}
}
@@ -135,10 +148,36 @@ var splitlisttests = []SplitListTest{
{string([]byte{lsep, 'a', lsep, 'b'}), []string{"", "a", "b"}},
}
+var winsplitlisttests = []SplitListTest{
+ // quoted
+ {`"a"`, []string{`a`}},
+
+ // semicolon
+ {`";"`, []string{`;`}},
+ {`"a;b"`, []string{`a;b`}},
+ {`";";`, []string{`;`, ``}},
+ {`;";"`, []string{``, `;`}},
+
+ // partially quoted
+ {`a";"b`, []string{`a;b`}},
+ {`a; ""b`, []string{`a`, ` b`}},
+ {`"a;b`, []string{`a;b`}},
+ {`""a;b`, []string{`a`, `b`}},
+ {`"""a;b`, []string{`a;b`}},
+ {`""""a;b`, []string{`a`, `b`}},
+ {`a";b`, []string{`a;b`}},
+ {`a;b";c`, []string{`a`, `b;c`}},
+ {`"a";b";c`, []string{`a`, `b;c`}},
+}
+
func TestSplitList(t *testing.T) {
- for _, test := range splitlisttests {
+ tests := splitlisttests
+ if runtime.GOOS == "windows" {
+ tests = append(tests, winsplitlisttests...)
+ }
+ for _, test := range tests {
if l := filepath.SplitList(test.list); !reflect.DeepEqual(l, test.result) {
- t.Errorf("SplitList(%q) = %s, want %s", test.list, l, test.result)
+ t.Errorf("SplitList(%#q) = %#q, want %#q", test.list, l, test.result)
}
}
}
@@ -684,10 +723,15 @@ func TestAbs(t *testing.T) {
}
defer os.RemoveAll(root)
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatal("getwd failed: ", err)
+ }
err = os.Chdir(root)
if err != nil {
t.Fatal("chdir failed: ", err)
}
+ defer os.Chdir(wd)
for _, dir := range absTestDirs {
err = os.Mkdir(dir, 0777)
@@ -871,7 +915,7 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) {
}
func TestBug3486(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=3486
- root, err := filepath.EvalSymlinks(os.Getenv("GOROOT"))
+ root, err := filepath.EvalSymlinks(runtime.GOROOT())
if err != nil {
t.Fatal(err)
}
diff --git a/src/pkg/path/filepath/path_unix.go b/src/pkg/path/filepath/path_unix.go
index 305e30727..cff7b2c65 100644
--- a/src/pkg/path/filepath/path_unix.go
+++ b/src/pkg/path/filepath/path_unix.go
@@ -13,13 +13,20 @@ func IsAbs(path string) bool {
return strings.HasPrefix(path, "/")
}
-// VolumeName returns the leading volume name on Windows.
-// It returns "" elsewhere.
-func VolumeName(path string) string {
- return ""
+// volumeNameLen returns length of the leading volume name on Windows.
+// It returns 0 elsewhere.
+func volumeNameLen(path string) int {
+ return 0
}
// HasPrefix exists for historical compatibility and should not be used.
func HasPrefix(p, prefix string) bool {
return strings.HasPrefix(p, prefix)
}
+
+func splitList(path string) []string {
+ if path == "" {
+ return []string{}
+ }
+ return strings.Split(path, string(ListSeparator))
+}
diff --git a/src/pkg/path/filepath/path_windows.go b/src/pkg/path/filepath/path_windows.go
index 3dcd03021..e99997257 100644
--- a/src/pkg/path/filepath/path_windows.go
+++ b/src/pkg/path/filepath/path_windows.go
@@ -14,29 +14,27 @@ func isSlash(c uint8) bool {
// IsAbs returns true if the path is absolute.
func IsAbs(path string) (b bool) {
- v := VolumeName(path)
- if v == "" {
+ l := volumeNameLen(path)
+ if l == 0 {
return false
}
- path = path[len(v):]
+ path = path[l:]
if path == "" {
return false
}
return isSlash(path[0])
}
-// VolumeName returns leading volume name.
-// Given "C:\foo\bar" it returns "C:" under windows.
-// Given "\\host\share\foo" it returns "\\host\share".
-// On other platforms it returns "".
-func VolumeName(path string) (v string) {
+// volumeNameLen returns length of the leading volume name on Windows.
+// It returns 0 elsewhere.
+func volumeNameLen(path string) int {
if len(path) < 2 {
- return ""
+ return 0
}
// with drive letter
c := path[0]
if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
- return path[:2]
+ return 2
}
// is it UNC
if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
@@ -56,13 +54,13 @@ func VolumeName(path string) (v string) {
break
}
}
- return path[:n]
+ return n
}
break
}
}
}
- return ""
+ return 0
}
// HasPrefix exists for historical compatibility and should not be used.
@@ -72,3 +70,36 @@ func HasPrefix(p, prefix string) bool {
}
return strings.HasPrefix(strings.ToLower(p), strings.ToLower(prefix))
}
+
+func splitList(path string) []string {
+ // The same implementation is used in LookPath in os/exec;
+ // consider changing os/exec when changing this.
+
+ if path == "" {
+ return []string{}
+ }
+
+ // Split path, respecting but preserving quotes.
+ list := []string{}
+ start := 0
+ quo := false
+ for i := 0; i < len(path); i++ {
+ switch c := path[i]; {
+ case c == '"':
+ quo = !quo
+ case c == ListSeparator && !quo:
+ list = append(list, path[start:i])
+ start = i + 1
+ }
+ }
+ list = append(list, path[start:])
+
+ // Remove quotes.
+ for i, s := range list {
+ if strings.Contains(s, `"`) {
+ list[i] = strings.Replace(s, `"`, ``, -1)
+ }
+ }
+
+ return list
+}
diff --git a/src/pkg/path/filepath/path_windows_test.go b/src/pkg/path/filepath/path_windows_test.go
new file mode 100644
index 000000000..d8926adde
--- /dev/null
+++ b/src/pkg/path/filepath/path_windows_test.go
@@ -0,0 +1,89 @@
+package filepath_test
+
+import (
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "reflect"
+ "testing"
+)
+
+func TestWinSplitListTestsAreValid(t *testing.T) {
+ comspec := os.Getenv("ComSpec")
+ if comspec == "" {
+ t.Fatal("%ComSpec% must be set")
+ }
+
+ for ti, tt := range winsplitlisttests {
+ testWinSplitListTestIsValid(t, ti, tt, comspec)
+ }
+}
+
+func testWinSplitListTestIsValid(t *testing.T, ti int, tt SplitListTest,
+ comspec string) {
+
+ const (
+ cmdfile = `printdir.cmd`
+ perm os.FileMode = 0700
+ )
+
+ tmp, err := ioutil.TempDir("", "testWinSplitListTestIsValid")
+ if err != nil {
+ t.Fatalf("TempDir failed: %v", err)
+ }
+ defer os.RemoveAll(tmp)
+
+ for i, d := range tt.result {
+ if d == "" {
+ continue
+ }
+ if cd := filepath.Clean(d); filepath.VolumeName(cd) != "" ||
+ cd[0] == '\\' || cd == ".." || (len(cd) >= 3 && cd[0:3] == `..\`) {
+ t.Errorf("%d,%d: %#q refers outside working directory", ti, i, d)
+ return
+ }
+ dd := filepath.Join(tmp, d)
+ if _, err := os.Stat(dd); err == nil {
+ t.Errorf("%d,%d: %#q already exists", ti, i, d)
+ return
+ }
+ if err = os.MkdirAll(dd, perm); err != nil {
+ t.Errorf("%d,%d: MkdirAll(%#q) failed: %v", ti, i, dd, err)
+ return
+ }
+ fn, data := filepath.Join(dd, cmdfile), []byte("@echo "+d+"\r\n")
+ if err = ioutil.WriteFile(fn, data, perm); err != nil {
+ t.Errorf("%d,%d: WriteFile(%#q) failed: %v", ti, i, fn, err)
+ return
+ }
+ }
+
+ for i, d := range tt.result {
+ if d == "" {
+ continue
+ }
+ exp := []byte(d + "\r\n")
+ cmd := &exec.Cmd{
+ Path: comspec,
+ Args: []string{`/c`, cmdfile},
+ Env: []string{`Path=` + tt.list},
+ Dir: tmp,
+ }
+ out, err := cmd.CombinedOutput()
+ switch {
+ case err != nil:
+ t.Errorf("%d,%d: execution error %v\n%q", ti, i, err, out)
+ return
+ case !reflect.DeepEqual(out, exp):
+ t.Errorf("%d,%d: expected %#q, got %#q", ti, i, exp, out)
+ return
+ default:
+ // unshadow cmdfile in next directory
+ err = os.Remove(filepath.Join(tmp, d, cmdfile))
+ if err != nil {
+ t.Fatalf("Remove test command failed: %v", err)
+ }
+ }
+ }
+}
diff --git a/src/pkg/path/filepath/symlink_windows.go b/src/pkg/path/filepath/symlink_windows.go
index 1ee939928..9adc8a48a 100644
--- a/src/pkg/path/filepath/symlink_windows.go
+++ b/src/pkg/path/filepath/symlink_windows.go
@@ -9,7 +9,10 @@ import (
)
func toShort(path string) (string, error) {
- p := syscall.StringToUTF16(path)
+ p, err := syscall.UTF16FromString(path)
+ if err != nil {
+ return "", err
+ }
b := p // GetShortPathName says we can reuse buffer
n, err := syscall.GetShortPathName(&p[0], &b[0], uint32(len(b)))
if err != nil {
@@ -26,7 +29,10 @@ func toShort(path string) (string, error) {
}
func toLong(path string) (string, error) {
- p := syscall.StringToUTF16(path)
+ p, err := syscall.UTF16FromString(path)
+ if err != nil {
+ return "", err
+ }
b := p // GetLongPathName says we can reuse buffer
n, err := syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
if err != nil {
diff --git a/src/pkg/path/path.go b/src/pkg/path/path.go
index a7e041568..bdb85c6b9 100644
--- a/src/pkg/path/path.go
+++ b/src/pkg/path/path.go
@@ -10,6 +10,43 @@ import (
"strings"
)
+// A lazybuf is a lazily constructed path buffer.
+// It supports append, reading previously appended bytes,
+// and retrieving the final string. It does not allocate a buffer
+// to hold the output until that output diverges from s.
+type lazybuf struct {
+ s string
+ buf []byte
+ w int
+}
+
+func (b *lazybuf) index(i int) byte {
+ if b.buf != nil {
+ return b.buf[i]
+ }
+ return b.s[i]
+}
+
+func (b *lazybuf) append(c byte) {
+ if b.buf == nil {
+ if b.w < len(b.s) && b.s[b.w] == c {
+ b.w++
+ return
+ }
+ b.buf = make([]byte, len(b.s))
+ copy(b.buf, b.s[:b.w])
+ }
+ b.buf[b.w] = c
+ b.w++
+}
+
+func (b *lazybuf) string() string {
+ if b.buf == nil {
+ return b.s[:b.w]
+ }
+ return string(b.buf[:b.w])
+}
+
// Clean returns the shortest path name equivalent to path
// by purely lexical processing. It applies the following rules
// iteratively until no further processing can be done:
@@ -42,10 +79,11 @@ func Clean(path string) string {
// writing to buf; w is index of next byte to write.
// dotdot is index in buf where .. must stop, either because
// it is the leading slash or it is a leading ../../.. prefix.
- buf := []byte(path)
- r, w, dotdot := 0, 0, 0
+ out := lazybuf{s: path}
+ r, dotdot := 0, 0
if rooted {
- r, w, dotdot = 1, 1, 1
+ out.append('/')
+ r, dotdot = 1, 1
}
for r < n {
@@ -60,46 +98,40 @@ func Clean(path string) string {
// .. element: remove to last /
r += 2
switch {
- case w > dotdot:
+ case out.w > dotdot:
// can backtrack
- w--
- for w > dotdot && buf[w] != '/' {
- w--
+ out.w--
+ for out.w > dotdot && out.index(out.w) != '/' {
+ out.w--
}
case !rooted:
// cannot backtrack, but not rooted, so append .. element.
- if w > 0 {
- buf[w] = '/'
- w++
+ if out.w > 0 {
+ out.append('/')
}
- buf[w] = '.'
- w++
- buf[w] = '.'
- w++
- dotdot = w
+ out.append('.')
+ out.append('.')
+ dotdot = out.w
}
default:
// real path element.
// add slash if needed
- if rooted && w != 1 || !rooted && w != 0 {
- buf[w] = '/'
- w++
+ if rooted && out.w != 1 || !rooted && out.w != 0 {
+ out.append('/')
}
// copy element
for ; r < n && path[r] != '/'; r++ {
- buf[w] = path[r]
- w++
+ out.append(path[r])
}
}
}
// Turn empty string into "."
- if w == 0 {
- buf[w] = '.'
- w++
+ if out.w == 0 {
+ return "."
}
- return string(buf[0:w])
+ return out.string()
}
// Split splits path immediately following the final slash.
@@ -166,7 +198,8 @@ func IsAbs(path string) bool {
}
// Dir returns all but the last element of path, typically the path's directory.
-// The path is Cleaned and trailing slashes are removed before processing.
+// After dropping the final element using Split, the path is Cleaned and trailing
+// slashes are removed.
// If the path is empty, Dir returns ".".
// If the path consists entirely of slashes followed by non-slash bytes, Dir
// returns a single slash. In any other case, the returned path does not end in a
diff --git a/src/pkg/path/path_test.go b/src/pkg/path/path_test.go
index 77f080433..220ec1a0b 100644
--- a/src/pkg/path/path_test.go
+++ b/src/pkg/path/path_test.go
@@ -67,6 +67,16 @@ func TestClean(t *testing.T) {
if s := Clean(test.path); s != test.result {
t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
}
+ if s := Clean(test.result); s != test.result {
+ t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result)
+ }
+ }
+
+ for _, test := range cleantests {
+ allocs := testing.AllocsPerRun(100, func() { Clean(test.result) })
+ if allocs > 0 {
+ t.Errorf("Clean(%q): %v allocs, want zero", test.result, allocs)
+ }
}
}
@@ -181,6 +191,7 @@ var dirtests = []PathTest{
{"x/", "x"},
{"abc", "."},
{"abc/def", "abc"},
+ {"abc////def", "abc"},
{"a/b/.x", "a/b"},
{"a/b/c.", "a/b"},
{"a/b/c.x", "a/b"},
diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go
index e33140563..6f006db18 100644
--- a/src/pkg/reflect/all_test.go
+++ b/src/pkg/reflect/all_test.go
@@ -7,12 +7,15 @@ package reflect_test
import (
"bytes"
"encoding/base64"
+ "flag"
"fmt"
"io"
+ "math/rand"
"os"
. "reflect"
- "runtime"
+ "sync"
"testing"
+ "time"
"unsafe"
)
@@ -1053,26 +1056,393 @@ func TestChan(t *testing.T) {
if l, m := cv.Len(), cv.Cap(); l != len(c) || m != cap(c) {
t.Errorf("Len/Cap = %d/%d want %d/%d", l, m, len(c), cap(c))
}
+}
+// caseInfo describes a single case in a select test.
+type caseInfo struct {
+ desc string
+ canSelect bool
+ recv Value
+ closed bool
+ helper func()
+ panic bool
}
+var allselect = flag.Bool("allselect", false, "exhaustive select test")
+
+func TestSelect(t *testing.T) {
+ selectWatch.once.Do(func() { go selectWatcher() })
+
+ var x exhaustive
+ nch := 0
+ newop := func(n int, cap int) (ch, val Value) {
+ nch++
+ if nch%101%2 == 1 {
+ c := make(chan int, cap)
+ ch = ValueOf(c)
+ val = ValueOf(n)
+ } else {
+ c := make(chan string, cap)
+ ch = ValueOf(c)
+ val = ValueOf(fmt.Sprint(n))
+ }
+ return
+ }
+
+ for n := 0; x.Next(); n++ {
+ if testing.Short() && n >= 1000 {
+ break
+ }
+ if n >= 100000 && !*allselect {
+ break
+ }
+ if n%100000 == 0 && testing.Verbose() {
+ println("TestSelect", n)
+ }
+ var cases []SelectCase
+ var info []caseInfo
+
+ // Ready send.
+ if x.Maybe() {
+ ch, val := newop(len(cases), 1)
+ cases = append(cases, SelectCase{
+ Dir: SelectSend,
+ Chan: ch,
+ Send: val,
+ })
+ info = append(info, caseInfo{desc: "ready send", canSelect: true})
+ }
+
+ // Ready recv.
+ if x.Maybe() {
+ ch, val := newop(len(cases), 1)
+ ch.Send(val)
+ cases = append(cases, SelectCase{
+ Dir: SelectRecv,
+ Chan: ch,
+ })
+ info = append(info, caseInfo{desc: "ready recv", canSelect: true, recv: val})
+ }
+
+ // Blocking send.
+ if x.Maybe() {
+ ch, val := newop(len(cases), 0)
+ cases = append(cases, SelectCase{
+ Dir: SelectSend,
+ Chan: ch,
+ Send: val,
+ })
+ // Let it execute?
+ if x.Maybe() {
+ f := func() { ch.Recv() }
+ info = append(info, caseInfo{desc: "blocking send", helper: f})
+ } else {
+ info = append(info, caseInfo{desc: "blocking send"})
+ }
+ }
+
+ // Blocking recv.
+ if x.Maybe() {
+ ch, val := newop(len(cases), 0)
+ cases = append(cases, SelectCase{
+ Dir: SelectRecv,
+ Chan: ch,
+ })
+ // Let it execute?
+ if x.Maybe() {
+ f := func() { ch.Send(val) }
+ info = append(info, caseInfo{desc: "blocking recv", recv: val, helper: f})
+ } else {
+ info = append(info, caseInfo{desc: "blocking recv"})
+ }
+ }
+
+ // Zero Chan send.
+ if x.Maybe() {
+ // Maybe include value to send.
+ var val Value
+ if x.Maybe() {
+ val = ValueOf(100)
+ }
+ cases = append(cases, SelectCase{
+ Dir: SelectSend,
+ Send: val,
+ })
+ info = append(info, caseInfo{desc: "zero Chan send"})
+ }
+
+ // Zero Chan receive.
+ if x.Maybe() {
+ cases = append(cases, SelectCase{
+ Dir: SelectRecv,
+ })
+ info = append(info, caseInfo{desc: "zero Chan recv"})
+ }
+
+ // nil Chan send.
+ if x.Maybe() {
+ cases = append(cases, SelectCase{
+ Dir: SelectSend,
+ Chan: ValueOf((chan int)(nil)),
+ Send: ValueOf(101),
+ })
+ info = append(info, caseInfo{desc: "nil Chan send"})
+ }
+
+ // nil Chan recv.
+ if x.Maybe() {
+ cases = append(cases, SelectCase{
+ Dir: SelectRecv,
+ Chan: ValueOf((chan int)(nil)),
+ })
+ info = append(info, caseInfo{desc: "nil Chan recv"})
+ }
+
+ // closed Chan send.
+ if x.Maybe() {
+ ch := make(chan int)
+ close(ch)
+ cases = append(cases, SelectCase{
+ Dir: SelectSend,
+ Chan: ValueOf(ch),
+ Send: ValueOf(101),
+ })
+ info = append(info, caseInfo{desc: "closed Chan send", canSelect: true, panic: true})
+ }
+
+ // closed Chan recv.
+ if x.Maybe() {
+ ch, val := newop(len(cases), 0)
+ ch.Close()
+ val = Zero(val.Type())
+ cases = append(cases, SelectCase{
+ Dir: SelectRecv,
+ Chan: ch,
+ })
+ info = append(info, caseInfo{desc: "closed Chan recv", canSelect: true, closed: true, recv: val})
+ }
+
+ var helper func() // goroutine to help the select complete
+
+ // Add default? Must be last case here, but will permute.
+ // Add the default if the select would otherwise
+ // block forever, and maybe add it anyway.
+ numCanSelect := 0
+ canProceed := false
+ canBlock := true
+ canPanic := false
+ helpers := []int{}
+ for i, c := range info {
+ if c.canSelect {
+ canProceed = true
+ canBlock = false
+ numCanSelect++
+ if c.panic {
+ canPanic = true
+ }
+ } else if c.helper != nil {
+ canProceed = true
+ helpers = append(helpers, i)
+ }
+ }
+ if !canProceed || x.Maybe() {
+ cases = append(cases, SelectCase{
+ Dir: SelectDefault,
+ })
+ info = append(info, caseInfo{desc: "default", canSelect: canBlock})
+ numCanSelect++
+ } else if canBlock {
+ // Select needs to communicate with another goroutine.
+ cas := &info[helpers[x.Choose(len(helpers))]]
+ helper = cas.helper
+ cas.canSelect = true
+ numCanSelect++
+ }
+
+ // Permute cases and case info.
+ // Doing too much here makes the exhaustive loop
+ // too exhausting, so just do two swaps.
+ for loop := 0; loop < 2; loop++ {
+ i := x.Choose(len(cases))
+ j := x.Choose(len(cases))
+ cases[i], cases[j] = cases[j], cases[i]
+ info[i], info[j] = info[j], info[i]
+ }
+
+ if helper != nil {
+ // We wait before kicking off a goroutine to satisfy a blocked select.
+ // The pause needs to be big enough to let the select block before
+ // we run the helper, but if we lose that race once in a while it's okay: the
+ // select will just proceed immediately. Not a big deal.
+ // For short tests we can grow [sic] the timeout a bit without fear of taking too long
+ pause := 10 * time.Microsecond
+ if testing.Short() {
+ pause = 100 * time.Microsecond
+ }
+ time.AfterFunc(pause, helper)
+ }
+
+ // Run select.
+ i, recv, recvOK, panicErr := runSelect(cases, info)
+ if panicErr != nil && !canPanic {
+ t.Fatalf("%s\npanicked unexpectedly: %v", fmtSelect(info), panicErr)
+ }
+ if panicErr == nil && canPanic && numCanSelect == 1 {
+ t.Fatalf("%s\nselected #%d incorrectly (should panic)", fmtSelect(info), i)
+ }
+ if panicErr != nil {
+ continue
+ }
+
+ cas := info[i]
+ if !cas.canSelect {
+ recvStr := ""
+ if recv.IsValid() {
+ recvStr = fmt.Sprintf(", received %v, %v", recv.Interface(), recvOK)
+ }
+ t.Fatalf("%s\nselected #%d incorrectly%s", fmtSelect(info), i, recvStr)
+ continue
+ }
+ if cas.panic {
+ t.Fatalf("%s\nselected #%d incorrectly (case should panic)", fmtSelect(info), i)
+ continue
+ }
+
+ if cases[i].Dir == SelectRecv {
+ if !recv.IsValid() {
+ t.Fatalf("%s\nselected #%d but got %v, %v, want %v, %v", fmtSelect(info), i, recv, recvOK, cas.recv.Interface(), !cas.closed)
+ }
+ if !cas.recv.IsValid() {
+ t.Fatalf("%s\nselected #%d but internal error: missing recv value", fmtSelect(info), i)
+ }
+ if recv.Interface() != cas.recv.Interface() || recvOK != !cas.closed {
+ if recv.Interface() == cas.recv.Interface() && recvOK == !cas.closed {
+ t.Fatalf("%s\nselected #%d, got %#v, %v, and DeepEqual is broken on %T", fmtSelect(info), i, recv.Interface(), recvOK, recv.Interface())
+ }
+ t.Fatalf("%s\nselected #%d but got %#v, %v, want %#v, %v", fmtSelect(info), i, recv.Interface(), recvOK, cas.recv.Interface(), !cas.closed)
+ }
+ } else {
+ if recv.IsValid() || recvOK {
+ t.Fatalf("%s\nselected #%d but got %v, %v, want %v, %v", fmtSelect(info), i, recv, recvOK, Value{}, false)
+ }
+ }
+ }
+}
+
+// selectWatch and the selectWatcher are a watchdog mechanism for running Select.
+// If the selectWatcher notices that the select has been blocked for >1 second, it prints
+// an error describing the select and panics the entire test binary.
+var selectWatch struct {
+ sync.Mutex
+ once sync.Once
+ now time.Time
+ info []caseInfo
+}
+
+func selectWatcher() {
+ for {
+ time.Sleep(1 * time.Second)
+ selectWatch.Lock()
+ if selectWatch.info != nil && time.Since(selectWatch.now) > 1*time.Second {
+ fmt.Fprintf(os.Stderr, "TestSelect:\n%s blocked indefinitely\n", fmtSelect(selectWatch.info))
+ panic("select stuck")
+ }
+ selectWatch.Unlock()
+ }
+}
+
+// runSelect runs a single select test.
+// It returns the values returned by Select but also returns
+// a panic value if the Select panics.
+func runSelect(cases []SelectCase, info []caseInfo) (chosen int, recv Value, recvOK bool, panicErr interface{}) {
+ defer func() {
+ panicErr = recover()
+
+ selectWatch.Lock()
+ selectWatch.info = nil
+ selectWatch.Unlock()
+ }()
+
+ selectWatch.Lock()
+ selectWatch.now = time.Now()
+ selectWatch.info = info
+ selectWatch.Unlock()
+
+ chosen, recv, recvOK = Select(cases)
+ return
+}
+
+// fmtSelect formats the information about a single select test.
+func fmtSelect(info []caseInfo) string {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "\nselect {\n")
+ for i, cas := range info {
+ fmt.Fprintf(&buf, "%d: %s", i, cas.desc)
+ if cas.recv.IsValid() {
+ fmt.Fprintf(&buf, " val=%#v", cas.recv.Interface())
+ }
+ if cas.canSelect {
+ fmt.Fprintf(&buf, " canselect")
+ }
+ if cas.panic {
+ fmt.Fprintf(&buf, " panic")
+ }
+ fmt.Fprintf(&buf, "\n")
+ }
+ fmt.Fprintf(&buf, "}")
+ return buf.String()
+}
+
+type two [2]uintptr
+
// Difficult test for function call because of
// implicit padding between arguments.
-func dummy(b byte, c int, d byte) (i byte, j int, k byte) {
- return b, c, d
+func dummy(b byte, c int, d byte, e two, f byte, g float32, h byte) (i byte, j int, k byte, l two, m byte, n float32, o byte) {
+ return b, c, d, e, f, g, h
}
func TestFunc(t *testing.T) {
- ret := ValueOf(dummy).Call([]Value{ValueOf(byte(10)), ValueOf(20), ValueOf(byte(30))})
- if len(ret) != 3 {
- t.Fatalf("Call returned %d values, want 3", len(ret))
+ ret := ValueOf(dummy).Call([]Value{
+ ValueOf(byte(10)),
+ ValueOf(20),
+ ValueOf(byte(30)),
+ ValueOf(two{40, 50}),
+ ValueOf(byte(60)),
+ ValueOf(float32(70)),
+ ValueOf(byte(80)),
+ })
+ if len(ret) != 7 {
+ t.Fatalf("Call returned %d values, want 7", len(ret))
}
i := byte(ret[0].Uint())
j := int(ret[1].Int())
k := byte(ret[2].Uint())
- if i != 10 || j != 20 || k != 30 {
- t.Errorf("Call returned %d, %d, %d; want 10, 20, 30", i, j, k)
+ l := ret[3].Interface().(two)
+ m := byte(ret[4].Uint())
+ n := float32(ret[5].Float())
+ o := byte(ret[6].Uint())
+
+ if i != 10 || j != 20 || k != 30 || l != (two{40, 50}) || m != 60 || n != 70 || o != 80 {
+ t.Errorf("Call returned %d, %d, %d, %v, %d, %g, %d; want 10, 20, 30, [40, 50], 60, 70, 80", i, j, k, l, m, n, o)
+ }
+}
+
+func TestMakeFunc(t *testing.T) {
+ f := dummy
+ fv := MakeFunc(TypeOf(f), func(in []Value) []Value { return in })
+ ValueOf(&f).Elem().Set(fv)
+
+ // Call g with small arguments so that there is
+ // something predictable (and different from the
+ // correct results) in those positions on the stack.
+ g := dummy
+ g(1, 2, 3, two{4, 5}, 6, 7, 8)
+
+ // Call constructed function f.
+ i, j, k, l, m, n, o := f(10, 20, 30, two{40, 50}, 60, 70, 80)
+ if i != 10 || j != 20 || k != 30 || l != (two{40, 50}) || m != 60 || n != 70 || o != 80 {
+ t.Errorf("Call returned %d, %d, %d, %v, %d, %g, %d; want 10, 20, 30, [40, 50], 60, 70, 80", i, j, k, l, m, n, o)
}
}
@@ -1123,7 +1493,7 @@ func TestMethod(t *testing.T) {
}
// Curried method of value.
- tfunc := TypeOf(func(int) int(nil))
+ tfunc := TypeOf((func(int) int)(nil))
v := ValueOf(p).Method(1)
if tt := v.Type(); tt != tfunc {
t.Errorf("Value Method Type is %s; want %s", tt, tfunc)
@@ -1225,7 +1595,7 @@ func TestAnonymousFields(t *testing.T) {
var t1 T1
type1 := TypeOf(t1)
if field, ok = type1.FieldByName("int"); !ok {
- t.Error("no field 'int'")
+ t.Fatal("no field 'int'")
}
if field.Index[0] != 1 {
t.Error("field index should be 1; is", field.Index)
@@ -1282,6 +1652,61 @@ type S4 struct {
A int
}
+// The X in S6 and S7 annihilate, but they also block the X in S8.S9.
+type S5 struct {
+ S6
+ S7
+ S8
+}
+
+type S6 struct {
+ X int
+}
+
+type S7 S6
+
+type S8 struct {
+ S9
+}
+
+type S9 struct {
+ X int
+ Y int
+}
+
+// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9.
+type S10 struct {
+ S11
+ S12
+ S13
+}
+
+type S11 struct {
+ S6
+}
+
+type S12 struct {
+ S6
+}
+
+type S13 struct {
+ S8
+}
+
+// The X in S15.S11.S1 and S16.S11.S1 annihilate.
+type S14 struct {
+ S15
+ S16
+}
+
+type S15 struct {
+ S11
+}
+
+type S16 struct {
+ S11
+}
+
var fieldTests = []FTest{
{struct{}{}, "", nil, 0},
{struct{}{}, "Foo", nil, 0},
@@ -1303,6 +1728,11 @@ var fieldTests = []FTest{
{S3{E: 'e'}, "E", []int{3}, 'e'},
{S4{A: 'a'}, "A", []int{1}, 'a'},
{S4{}, "B", nil, 0},
+ {S5{}, "X", nil, 0},
+ {S5{}, "Y", []int{2, 0, 1}, 0},
+ {S10{}, "X", nil, 0},
+ {S10{}, "Y", []int{2, 0, 0, 1}, 0},
+ {S14{}, "X", nil, 0},
}
func TestFieldByIndex(t *testing.T) {
@@ -1346,7 +1776,7 @@ func TestFieldByName(t *testing.T) {
if test.index != nil {
// Verify field depth and index.
if len(f.Index) != len(test.index) {
- t.Errorf("%s.%s depth %d; want %d", s.Name(), test.name, len(f.Index), len(test.index))
+ t.Errorf("%s.%s depth %d; want %d: %v vs %v", s.Name(), test.name, len(f.Index), len(test.index), f.Index, test.index)
} else {
for i, x := range f.Index {
if x != test.index[i] {
@@ -1384,7 +1814,30 @@ func TestImportPath(t *testing.T) {
path string
}{
{TypeOf(&base64.Encoding{}).Elem(), "encoding/base64"},
+ {TypeOf(int(0)), ""},
+ {TypeOf(int8(0)), ""},
+ {TypeOf(int16(0)), ""},
+ {TypeOf(int32(0)), ""},
+ {TypeOf(int64(0)), ""},
{TypeOf(uint(0)), ""},
+ {TypeOf(uint8(0)), ""},
+ {TypeOf(uint16(0)), ""},
+ {TypeOf(uint32(0)), ""},
+ {TypeOf(uint64(0)), ""},
+ {TypeOf(uintptr(0)), ""},
+ {TypeOf(float32(0)), ""},
+ {TypeOf(float64(0)), ""},
+ {TypeOf(complex64(0)), ""},
+ {TypeOf(complex128(0)), ""},
+ {TypeOf(byte(0)), ""},
+ {TypeOf(rune(0)), ""},
+ {TypeOf([]byte(nil)), ""},
+ {TypeOf([]rune(nil)), ""},
+ {TypeOf(string("")), ""},
+ {TypeOf((*interface{})(nil)).Elem(), ""},
+ {TypeOf((*byte)(nil)), ""},
+ {TypeOf((*rune)(nil)), ""},
+ {TypeOf((*int64)(nil)), ""},
{TypeOf(map[string]int{}), ""},
{TypeOf((*error)(nil)).Elem(), ""},
}
@@ -1558,21 +2011,13 @@ func TestAddr(t *testing.T) {
}
func noAlloc(t *testing.T, n int, f func(int)) {
- // once to prime everything
- f(-1)
- memstats := new(runtime.MemStats)
- runtime.ReadMemStats(memstats)
- oldmallocs := memstats.Mallocs
-
- for j := 0; j < n; j++ {
- f(j)
- }
- // A few allocs may happen in the testing package when GOMAXPROCS > 1, so don't
- // require zero mallocs.
- runtime.ReadMemStats(memstats)
- mallocs := memstats.Mallocs - oldmallocs
- if mallocs > 5 {
- t.Fatalf("%d mallocs after %d iterations", mallocs, n)
+ i := -1
+ allocs := testing.AllocsPerRun(n, func() {
+ f(i)
+ i++
+ })
+ if allocs > 0 {
+ t.Errorf("%d iterations: got %v mallocs, want 0", n, allocs)
}
}
@@ -1596,6 +2041,24 @@ func TestSmallNegativeInt(t *testing.T) {
}
}
+func TestIndex(t *testing.T) {
+ xs := []byte{1, 2, 3, 4, 5, 6, 7, 8}
+ v := ValueOf(xs).Index(3).Interface().(byte)
+ if v != xs[3] {
+ t.Errorf("xs.Index(3) = %v; expected %v", v, xs[3])
+ }
+ xa := [8]byte{10, 20, 30, 40, 50, 60, 70, 80}
+ v = ValueOf(xa).Index(2).Interface().(byte)
+ if v != xa[2] {
+ t.Errorf("xa.Index(2) = %v; expected %v", v, xa[2])
+ }
+ s := "0123456789"
+ v = ValueOf(s).Index(3).Interface().(byte)
+ if v != s[3] {
+ t.Errorf("s.Index(3) = %v; expected %v", v, s[3])
+ }
+}
+
func TestSlice(t *testing.T) {
xs := []int{1, 2, 3, 4, 5, 6, 7, 8}
v := ValueOf(xs).Slice(3, 5).Interface().([]int)
@@ -1608,7 +2071,6 @@ func TestSlice(t *testing.T) {
if !DeepEqual(v[0:5], xs[3:]) {
t.Errorf("xs.Slice(3, 5)[0:5] = %v", v[0:5])
}
-
xa := [8]int{10, 20, 30, 40, 50, 60, 70, 80}
v = ValueOf(&xa).Elem().Slice(2, 5).Interface().([]int)
if len(v) != 3 {
@@ -1620,6 +2082,11 @@ func TestSlice(t *testing.T) {
if !DeepEqual(v[0:6], xa[2:]) {
t.Errorf("xs.Slice(2, 5)[0:6] = %v", v[0:6])
}
+ s := "0123456789"
+ vs := ValueOf(s).Slice(3, 5).Interface().(string)
+ if vs != s[3:5] {
+ t.Errorf("s.Slice(3, 5) = %q; expected %q", vs, s[3:5])
+ }
}
func TestVariadic(t *testing.T) {
@@ -1760,3 +2227,761 @@ func TestAlias(t *testing.T) {
t.Errorf("aliasing: old=%q new=%q, want hello, world", oldvalue, newvalue)
}
}
+
+var V = ValueOf
+
+func EmptyInterfaceV(x interface{}) Value {
+ return ValueOf(&x).Elem()
+}
+
+func ReaderV(x io.Reader) Value {
+ return ValueOf(&x).Elem()
+}
+
+func ReadWriterV(x io.ReadWriter) Value {
+ return ValueOf(&x).Elem()
+}
+
+type Empty struct{}
+type MyString string
+type MyBytes []byte
+type MyRunes []int32
+type MyFunc func()
+type MyByte byte
+
+var convertTests = []struct {
+ in Value
+ out Value
+}{
+ // numbers
+ /*
+ Edit .+1,/\*\//-1>cat >/tmp/x.go && go run /tmp/x.go
+
+ package main
+
+ import "fmt"
+
+ var numbers = []string{
+ "int8", "uint8", "int16", "uint16",
+ "int32", "uint32", "int64", "uint64",
+ "int", "uint", "uintptr",
+ "float32", "float64",
+ }
+
+ func main() {
+ // all pairs but in an unusual order,
+ // to emit all the int8, uint8 cases
+ // before n grows too big.
+ n := 1
+ for i, f := range numbers {
+ for _, g := range numbers[i:] {
+ fmt.Printf("\t{V(%s(%d)), V(%s(%d))},\n", f, n, g, n)
+ n++
+ if f != g {
+ fmt.Printf("\t{V(%s(%d)), V(%s(%d))},\n", g, n, f, n)
+ n++
+ }
+ }
+ }
+ }
+ */
+ {V(int8(1)), V(int8(1))},
+ {V(int8(2)), V(uint8(2))},
+ {V(uint8(3)), V(int8(3))},
+ {V(int8(4)), V(int16(4))},
+ {V(int16(5)), V(int8(5))},
+ {V(int8(6)), V(uint16(6))},
+ {V(uint16(7)), V(int8(7))},
+ {V(int8(8)), V(int32(8))},
+ {V(int32(9)), V(int8(9))},
+ {V(int8(10)), V(uint32(10))},
+ {V(uint32(11)), V(int8(11))},
+ {V(int8(12)), V(int64(12))},
+ {V(int64(13)), V(int8(13))},
+ {V(int8(14)), V(uint64(14))},
+ {V(uint64(15)), V(int8(15))},
+ {V(int8(16)), V(int(16))},
+ {V(int(17)), V(int8(17))},
+ {V(int8(18)), V(uint(18))},
+ {V(uint(19)), V(int8(19))},
+ {V(int8(20)), V(uintptr(20))},
+ {V(uintptr(21)), V(int8(21))},
+ {V(int8(22)), V(float32(22))},
+ {V(float32(23)), V(int8(23))},
+ {V(int8(24)), V(float64(24))},
+ {V(float64(25)), V(int8(25))},
+ {V(uint8(26)), V(uint8(26))},
+ {V(uint8(27)), V(int16(27))},
+ {V(int16(28)), V(uint8(28))},
+ {V(uint8(29)), V(uint16(29))},
+ {V(uint16(30)), V(uint8(30))},
+ {V(uint8(31)), V(int32(31))},
+ {V(int32(32)), V(uint8(32))},
+ {V(uint8(33)), V(uint32(33))},
+ {V(uint32(34)), V(uint8(34))},
+ {V(uint8(35)), V(int64(35))},
+ {V(int64(36)), V(uint8(36))},
+ {V(uint8(37)), V(uint64(37))},
+ {V(uint64(38)), V(uint8(38))},
+ {V(uint8(39)), V(int(39))},
+ {V(int(40)), V(uint8(40))},
+ {V(uint8(41)), V(uint(41))},
+ {V(uint(42)), V(uint8(42))},
+ {V(uint8(43)), V(uintptr(43))},
+ {V(uintptr(44)), V(uint8(44))},
+ {V(uint8(45)), V(float32(45))},
+ {V(float32(46)), V(uint8(46))},
+ {V(uint8(47)), V(float64(47))},
+ {V(float64(48)), V(uint8(48))},
+ {V(int16(49)), V(int16(49))},
+ {V(int16(50)), V(uint16(50))},
+ {V(uint16(51)), V(int16(51))},
+ {V(int16(52)), V(int32(52))},
+ {V(int32(53)), V(int16(53))},
+ {V(int16(54)), V(uint32(54))},
+ {V(uint32(55)), V(int16(55))},
+ {V(int16(56)), V(int64(56))},
+ {V(int64(57)), V(int16(57))},
+ {V(int16(58)), V(uint64(58))},
+ {V(uint64(59)), V(int16(59))},
+ {V(int16(60)), V(int(60))},
+ {V(int(61)), V(int16(61))},
+ {V(int16(62)), V(uint(62))},
+ {V(uint(63)), V(int16(63))},
+ {V(int16(64)), V(uintptr(64))},
+ {V(uintptr(65)), V(int16(65))},
+ {V(int16(66)), V(float32(66))},
+ {V(float32(67)), V(int16(67))},
+ {V(int16(68)), V(float64(68))},
+ {V(float64(69)), V(int16(69))},
+ {V(uint16(70)), V(uint16(70))},
+ {V(uint16(71)), V(int32(71))},
+ {V(int32(72)), V(uint16(72))},
+ {V(uint16(73)), V(uint32(73))},
+ {V(uint32(74)), V(uint16(74))},
+ {V(uint16(75)), V(int64(75))},
+ {V(int64(76)), V(uint16(76))},
+ {V(uint16(77)), V(uint64(77))},
+ {V(uint64(78)), V(uint16(78))},
+ {V(uint16(79)), V(int(79))},
+ {V(int(80)), V(uint16(80))},
+ {V(uint16(81)), V(uint(81))},
+ {V(uint(82)), V(uint16(82))},
+ {V(uint16(83)), V(uintptr(83))},
+ {V(uintptr(84)), V(uint16(84))},
+ {V(uint16(85)), V(float32(85))},
+ {V(float32(86)), V(uint16(86))},
+ {V(uint16(87)), V(float64(87))},
+ {V(float64(88)), V(uint16(88))},
+ {V(int32(89)), V(int32(89))},
+ {V(int32(90)), V(uint32(90))},
+ {V(uint32(91)), V(int32(91))},
+ {V(int32(92)), V(int64(92))},
+ {V(int64(93)), V(int32(93))},
+ {V(int32(94)), V(uint64(94))},
+ {V(uint64(95)), V(int32(95))},
+ {V(int32(96)), V(int(96))},
+ {V(int(97)), V(int32(97))},
+ {V(int32(98)), V(uint(98))},
+ {V(uint(99)), V(int32(99))},
+ {V(int32(100)), V(uintptr(100))},
+ {V(uintptr(101)), V(int32(101))},
+ {V(int32(102)), V(float32(102))},
+ {V(float32(103)), V(int32(103))},
+ {V(int32(104)), V(float64(104))},
+ {V(float64(105)), V(int32(105))},
+ {V(uint32(106)), V(uint32(106))},
+ {V(uint32(107)), V(int64(107))},
+ {V(int64(108)), V(uint32(108))},
+ {V(uint32(109)), V(uint64(109))},
+ {V(uint64(110)), V(uint32(110))},
+ {V(uint32(111)), V(int(111))},
+ {V(int(112)), V(uint32(112))},
+ {V(uint32(113)), V(uint(113))},
+ {V(uint(114)), V(uint32(114))},
+ {V(uint32(115)), V(uintptr(115))},
+ {V(uintptr(116)), V(uint32(116))},
+ {V(uint32(117)), V(float32(117))},
+ {V(float32(118)), V(uint32(118))},
+ {V(uint32(119)), V(float64(119))},
+ {V(float64(120)), V(uint32(120))},
+ {V(int64(121)), V(int64(121))},
+ {V(int64(122)), V(uint64(122))},
+ {V(uint64(123)), V(int64(123))},
+ {V(int64(124)), V(int(124))},
+ {V(int(125)), V(int64(125))},
+ {V(int64(126)), V(uint(126))},
+ {V(uint(127)), V(int64(127))},
+ {V(int64(128)), V(uintptr(128))},
+ {V(uintptr(129)), V(int64(129))},
+ {V(int64(130)), V(float32(130))},
+ {V(float32(131)), V(int64(131))},
+ {V(int64(132)), V(float64(132))},
+ {V(float64(133)), V(int64(133))},
+ {V(uint64(134)), V(uint64(134))},
+ {V(uint64(135)), V(int(135))},
+ {V(int(136)), V(uint64(136))},
+ {V(uint64(137)), V(uint(137))},
+ {V(uint(138)), V(uint64(138))},
+ {V(uint64(139)), V(uintptr(139))},
+ {V(uintptr(140)), V(uint64(140))},
+ {V(uint64(141)), V(float32(141))},
+ {V(float32(142)), V(uint64(142))},
+ {V(uint64(143)), V(float64(143))},
+ {V(float64(144)), V(uint64(144))},
+ {V(int(145)), V(int(145))},
+ {V(int(146)), V(uint(146))},
+ {V(uint(147)), V(int(147))},
+ {V(int(148)), V(uintptr(148))},
+ {V(uintptr(149)), V(int(149))},
+ {V(int(150)), V(float32(150))},
+ {V(float32(151)), V(int(151))},
+ {V(int(152)), V(float64(152))},
+ {V(float64(153)), V(int(153))},
+ {V(uint(154)), V(uint(154))},
+ {V(uint(155)), V(uintptr(155))},
+ {V(uintptr(156)), V(uint(156))},
+ {V(uint(157)), V(float32(157))},
+ {V(float32(158)), V(uint(158))},
+ {V(uint(159)), V(float64(159))},
+ {V(float64(160)), V(uint(160))},
+ {V(uintptr(161)), V(uintptr(161))},
+ {V(uintptr(162)), V(float32(162))},
+ {V(float32(163)), V(uintptr(163))},
+ {V(uintptr(164)), V(float64(164))},
+ {V(float64(165)), V(uintptr(165))},
+ {V(float32(166)), V(float32(166))},
+ {V(float32(167)), V(float64(167))},
+ {V(float64(168)), V(float32(168))},
+ {V(float64(169)), V(float64(169))},
+
+ // truncation
+ {V(float64(1.5)), V(int(1))},
+
+ // complex
+ {V(complex64(1i)), V(complex64(1i))},
+ {V(complex64(2i)), V(complex128(2i))},
+ {V(complex128(3i)), V(complex64(3i))},
+ {V(complex128(4i)), V(complex128(4i))},
+
+ // string
+ {V(string("hello")), V(string("hello"))},
+ {V(string("bytes1")), V([]byte("bytes1"))},
+ {V([]byte("bytes2")), V(string("bytes2"))},
+ {V([]byte("bytes3")), V([]byte("bytes3"))},
+ {V(string("runes♝")), V([]rune("runes♝"))},
+ {V([]rune("runes♕")), V(string("runes♕"))},
+ {V([]rune("runes🙈🙉🙊")), V([]rune("runes🙈🙉🙊"))},
+ {V(int('a')), V(string("a"))},
+ {V(int8('a')), V(string("a"))},
+ {V(int16('a')), V(string("a"))},
+ {V(int32('a')), V(string("a"))},
+ {V(int64('a')), V(string("a"))},
+ {V(uint('a')), V(string("a"))},
+ {V(uint8('a')), V(string("a"))},
+ {V(uint16('a')), V(string("a"))},
+ {V(uint32('a')), V(string("a"))},
+ {V(uint64('a')), V(string("a"))},
+ {V(uintptr('a')), V(string("a"))},
+ {V(int(-1)), V(string("\uFFFD"))},
+ {V(int8(-2)), V(string("\uFFFD"))},
+ {V(int16(-3)), V(string("\uFFFD"))},
+ {V(int32(-4)), V(string("\uFFFD"))},
+ {V(int64(-5)), V(string("\uFFFD"))},
+ {V(uint(0x110001)), V(string("\uFFFD"))},
+ {V(uint32(0x110002)), V(string("\uFFFD"))},
+ {V(uint64(0x110003)), V(string("\uFFFD"))},
+ {V(uintptr(0x110004)), V(string("\uFFFD"))},
+
+ // named string
+ {V(MyString("hello")), V(string("hello"))},
+ {V(string("hello")), V(MyString("hello"))},
+ {V(string("hello")), V(string("hello"))},
+ {V(MyString("hello")), V(MyString("hello"))},
+ {V(MyString("bytes1")), V([]byte("bytes1"))},
+ {V([]byte("bytes2")), V(MyString("bytes2"))},
+ {V([]byte("bytes3")), V([]byte("bytes3"))},
+ {V(MyString("runes♝")), V([]rune("runes♝"))},
+ {V([]rune("runes♕")), V(MyString("runes♕"))},
+ {V([]rune("runes🙈🙉🙊")), V([]rune("runes🙈🙉🙊"))},
+ {V([]rune("runes🙈🙉🙊")), V(MyRunes("runes🙈🙉🙊"))},
+ {V(MyRunes("runes🙈🙉🙊")), V([]rune("runes🙈🙉🙊"))},
+ {V(int('a')), V(MyString("a"))},
+ {V(int8('a')), V(MyString("a"))},
+ {V(int16('a')), V(MyString("a"))},
+ {V(int32('a')), V(MyString("a"))},
+ {V(int64('a')), V(MyString("a"))},
+ {V(uint('a')), V(MyString("a"))},
+ {V(uint8('a')), V(MyString("a"))},
+ {V(uint16('a')), V(MyString("a"))},
+ {V(uint32('a')), V(MyString("a"))},
+ {V(uint64('a')), V(MyString("a"))},
+ {V(uintptr('a')), V(MyString("a"))},
+ {V(int(-1)), V(MyString("\uFFFD"))},
+ {V(int8(-2)), V(MyString("\uFFFD"))},
+ {V(int16(-3)), V(MyString("\uFFFD"))},
+ {V(int32(-4)), V(MyString("\uFFFD"))},
+ {V(int64(-5)), V(MyString("\uFFFD"))},
+ {V(uint(0x110001)), V(MyString("\uFFFD"))},
+ {V(uint32(0x110002)), V(MyString("\uFFFD"))},
+ {V(uint64(0x110003)), V(MyString("\uFFFD"))},
+ {V(uintptr(0x110004)), V(MyString("\uFFFD"))},
+
+ // named []byte
+ {V(string("bytes1")), V(MyBytes("bytes1"))},
+ {V(MyBytes("bytes2")), V(string("bytes2"))},
+ {V(MyBytes("bytes3")), V(MyBytes("bytes3"))},
+ {V(MyString("bytes1")), V(MyBytes("bytes1"))},
+ {V(MyBytes("bytes2")), V(MyString("bytes2"))},
+
+ // named []rune
+ {V(string("runes♝")), V(MyRunes("runes♝"))},
+ {V(MyRunes("runes♕")), V(string("runes♕"))},
+ {V(MyRunes("runes🙈🙉🙊")), V(MyRunes("runes🙈🙉🙊"))},
+ {V(MyString("runes♝")), V(MyRunes("runes♝"))},
+ {V(MyRunes("runes♕")), V(MyString("runes♕"))},
+
+ // named types and equal underlying types
+ {V(new(int)), V(new(integer))},
+ {V(new(integer)), V(new(int))},
+ {V(Empty{}), V(struct{}{})},
+ {V(new(Empty)), V(new(struct{}))},
+ {V(struct{}{}), V(Empty{})},
+ {V(new(struct{})), V(new(Empty))},
+ {V(Empty{}), V(Empty{})},
+ {V(MyBytes{}), V([]byte{})},
+ {V([]byte{}), V(MyBytes{})},
+ {V((func())(nil)), V(MyFunc(nil))},
+ {V((MyFunc)(nil)), V((func())(nil))},
+
+ // can convert *byte and *MyByte
+ {V((*byte)(nil)), V((*MyByte)(nil))},
+ {V((*MyByte)(nil)), V((*byte)(nil))},
+
+ // cannot convert mismatched array sizes
+ {V([2]byte{}), V([2]byte{})},
+ {V([3]byte{}), V([3]byte{})},
+
+ // cannot convert other instances
+ {V((**byte)(nil)), V((**byte)(nil))},
+ {V((**MyByte)(nil)), V((**MyByte)(nil))},
+ {V((chan byte)(nil)), V((chan byte)(nil))},
+ {V((chan MyByte)(nil)), V((chan MyByte)(nil))},
+ {V(([]byte)(nil)), V(([]byte)(nil))},
+ {V(([]MyByte)(nil)), V(([]MyByte)(nil))},
+ {V((map[int]byte)(nil)), V((map[int]byte)(nil))},
+ {V((map[int]MyByte)(nil)), V((map[int]MyByte)(nil))},
+ {V((map[byte]int)(nil)), V((map[byte]int)(nil))},
+ {V((map[MyByte]int)(nil)), V((map[MyByte]int)(nil))},
+ {V([2]byte{}), V([2]byte{})},
+ {V([2]MyByte{}), V([2]MyByte{})},
+
+ // other
+ {V((***int)(nil)), V((***int)(nil))},
+ {V((***byte)(nil)), V((***byte)(nil))},
+ {V((***int32)(nil)), V((***int32)(nil))},
+ {V((***int64)(nil)), V((***int64)(nil))},
+ {V((chan int)(nil)), V((<-chan int)(nil))},
+ {V((chan int)(nil)), V((chan<- int)(nil))},
+ {V((chan string)(nil)), V((<-chan string)(nil))},
+ {V((chan string)(nil)), V((chan<- string)(nil))},
+ {V((chan byte)(nil)), V((chan byte)(nil))},
+ {V((chan MyByte)(nil)), V((chan MyByte)(nil))},
+ {V((map[int]bool)(nil)), V((map[int]bool)(nil))},
+ {V((map[int]byte)(nil)), V((map[int]byte)(nil))},
+ {V((map[uint]bool)(nil)), V((map[uint]bool)(nil))},
+ {V([]uint(nil)), V([]uint(nil))},
+ {V([]int(nil)), V([]int(nil))},
+ {V(new(interface{})), V(new(interface{}))},
+ {V(new(io.Reader)), V(new(io.Reader))},
+ {V(new(io.Writer)), V(new(io.Writer))},
+
+ // interfaces
+ {V(int(1)), EmptyInterfaceV(int(1))},
+ {V(string("hello")), EmptyInterfaceV(string("hello"))},
+ {V(new(bytes.Buffer)), ReaderV(new(bytes.Buffer))},
+ {ReadWriterV(new(bytes.Buffer)), ReaderV(new(bytes.Buffer))},
+ {V(new(bytes.Buffer)), ReadWriterV(new(bytes.Buffer))},
+}
+
+func TestConvert(t *testing.T) {
+ canConvert := map[[2]Type]bool{}
+ all := map[Type]bool{}
+
+ for _, tt := range convertTests {
+ t1 := tt.in.Type()
+ if !t1.ConvertibleTo(t1) {
+ t.Errorf("(%s).ConvertibleTo(%s) = false, want true", t1, t1)
+ continue
+ }
+
+ t2 := tt.out.Type()
+ if !t1.ConvertibleTo(t2) {
+ t.Errorf("(%s).ConvertibleTo(%s) = false, want true", t1, t2)
+ continue
+ }
+
+ all[t1] = true
+ all[t2] = true
+ canConvert[[2]Type{t1, t2}] = true
+
+ v1 := tt.in
+ vout1 := v1.Convert(t1)
+ out1 := vout1.Interface()
+ if vout1.Type() != tt.in.Type() || !DeepEqual(out1, tt.in.Interface()) {
+ t.Errorf("ValueOf(%T(%v)).Convert(%s) = %T(%v), want %T(%v)", tt.in.Interface(), tt.in.Interface(), t1, out1, out1, tt.in.Interface(), tt.in.Interface())
+ }
+
+ vout := v1.Convert(t2)
+ out := vout.Interface()
+ if vout.Type() != tt.out.Type() || !DeepEqual(out, tt.out.Interface()) {
+ t.Errorf("ValueOf(%T(%v)).Convert(%s) = %T(%v), want %T(%v)", tt.in.Interface(), tt.in.Interface(), t2, out, out, tt.out.Interface(), tt.out.Interface())
+ }
+
+ if IsRO(v1) {
+ t.Errorf("table entry %v is RO, should not be", v1)
+ }
+ if IsRO(vout1) {
+ t.Errorf("self-conversion output %v is RO, should not be", vout1)
+ }
+ if IsRO(vout) {
+ t.Errorf("conversion output %v is RO, should not be", vout)
+ }
+ if !IsRO(MakeRO(v1).Convert(t1)) {
+ t.Errorf("RO self-conversion output %v is not RO, should be", v1)
+ }
+ if !IsRO(MakeRO(v1).Convert(t2)) {
+ t.Errorf("RO conversion output %v is not RO, should be", v1)
+ }
+ }
+
+ // Assume that of all the types we saw during the tests,
+ // if there wasn't an explicit entry for a conversion between
+ // a pair of types, then it's not to be allowed. This checks for
+ // things like 'int64' converting to '*int'.
+ for t1 := range all {
+ for t2 := range all {
+ expectOK := t1 == t2 || canConvert[[2]Type{t1, t2}] || t2.Kind() == Interface && t2.NumMethod() == 0
+ if ok := t1.ConvertibleTo(t2); ok != expectOK {
+ t.Errorf("(%s).ConvertibleTo(%s) = %v, want %v", t1, t2, ok, expectOK)
+ }
+ }
+ }
+}
+
+func TestOverflow(t *testing.T) {
+ if ovf := V(float64(0)).OverflowFloat(1e300); ovf {
+ t.Errorf("%v wrongly overflows float64", 1e300)
+ }
+
+ maxFloat32 := float64((1<<24 - 1) << (127 - 23))
+ if ovf := V(float32(0)).OverflowFloat(maxFloat32); ovf {
+ t.Errorf("%v wrongly overflows float32", maxFloat32)
+ }
+ ovfFloat32 := float64((1<<24-1)<<(127-23) + 1<<(127-52))
+ if ovf := V(float32(0)).OverflowFloat(ovfFloat32); !ovf {
+ t.Errorf("%v should overflow float32", ovfFloat32)
+ }
+ if ovf := V(float32(0)).OverflowFloat(-ovfFloat32); !ovf {
+ t.Errorf("%v should overflow float32", -ovfFloat32)
+ }
+
+ maxInt32 := int64(0x7fffffff)
+ if ovf := V(int32(0)).OverflowInt(maxInt32); ovf {
+ t.Errorf("%v wrongly overflows int32", maxInt32)
+ }
+ if ovf := V(int32(0)).OverflowInt(-1 << 31); ovf {
+ t.Errorf("%v wrongly overflows int32", -int64(1)<<31)
+ }
+ ovfInt32 := int64(1 << 31)
+ if ovf := V(int32(0)).OverflowInt(ovfInt32); !ovf {
+ t.Errorf("%v should overflow int32", ovfInt32)
+ }
+
+ maxUint32 := uint64(0xffffffff)
+ if ovf := V(uint32(0)).OverflowUint(maxUint32); ovf {
+ t.Errorf("%v wrongly overflows uint32", maxUint32)
+ }
+ ovfUint32 := uint64(1 << 32)
+ if ovf := V(uint32(0)).OverflowUint(ovfUint32); !ovf {
+ t.Errorf("%v should overflow uint32", ovfUint32)
+ }
+}
+
+func checkSameType(t *testing.T, x, y interface{}) {
+ if TypeOf(x) != TypeOf(y) {
+ t.Errorf("did not find preexisting type for %s (vs %s)", TypeOf(x), TypeOf(y))
+ }
+}
+
+func TestArrayOf(t *testing.T) {
+ // check construction and use of type not in binary
+ type T int
+ at := ArrayOf(10, TypeOf(T(1)))
+ v := New(at).Elem()
+ for i := 0; i < v.Len(); i++ {
+ v.Index(i).Set(ValueOf(T(i)))
+ }
+ s := fmt.Sprint(v.Interface())
+ want := "[0 1 2 3 4 5 6 7 8 9]"
+ if s != want {
+ t.Errorf("constructed array = %s, want %s", s, want)
+ }
+
+ // check that type already in binary is found
+ checkSameType(t, Zero(ArrayOf(5, TypeOf(T(1)))).Interface(), [5]T{})
+}
+
+func TestSliceOf(t *testing.T) {
+ // check construction and use of type not in binary
+ type T int
+ st := SliceOf(TypeOf(T(1)))
+ v := MakeSlice(st, 10, 10)
+ for i := 0; i < v.Len(); i++ {
+ v.Index(i).Set(ValueOf(T(i)))
+ }
+ s := fmt.Sprint(v.Interface())
+ want := "[0 1 2 3 4 5 6 7 8 9]"
+ if s != want {
+ t.Errorf("constructed slice = %s, want %s", s, want)
+ }
+
+ // check that type already in binary is found
+ type T1 int
+ checkSameType(t, Zero(SliceOf(TypeOf(T1(1)))).Interface(), []T1{})
+}
+
+func TestChanOf(t *testing.T) {
+ // check construction and use of type not in binary
+ type T string
+ ct := ChanOf(BothDir, TypeOf(T("")))
+ v := MakeChan(ct, 2)
+ v.Send(ValueOf(T("hello")))
+ v.Send(ValueOf(T("world")))
+
+ sv1, _ := v.Recv()
+ sv2, _ := v.Recv()
+ s1 := sv1.String()
+ s2 := sv2.String()
+ if s1 != "hello" || s2 != "world" {
+ t.Errorf("constructed chan: have %q, %q, want %q, %q", s1, s2, "hello", "world")
+ }
+
+ // check that type already in binary is found
+ type T1 int
+ checkSameType(t, Zero(ChanOf(BothDir, TypeOf(T1(1)))).Interface(), (chan T1)(nil))
+}
+
+func TestMapOf(t *testing.T) {
+ // check construction and use of type not in binary
+ type K string
+ type V float64
+
+ v := MakeMap(MapOf(TypeOf(K("")), TypeOf(V(0))))
+ v.SetMapIndex(ValueOf(K("a")), ValueOf(V(1)))
+
+ s := fmt.Sprint(v.Interface())
+ want := "map[a:1]"
+ if s != want {
+ t.Errorf("constructed map = %s, want %s", s, want)
+ }
+
+ // check that type already in binary is found
+ checkSameType(t, Zero(MapOf(TypeOf(V(0)), TypeOf(K("")))).Interface(), map[V]K(nil))
+}
+
+type B1 struct {
+ X int
+ Y int
+ Z int
+}
+
+func BenchmarkFieldByName1(b *testing.B) {
+ t := TypeOf(B1{})
+ for i := 0; i < b.N; i++ {
+ t.FieldByName("Z")
+ }
+}
+
+func BenchmarkFieldByName2(b *testing.B) {
+ t := TypeOf(S3{})
+ for i := 0; i < b.N; i++ {
+ t.FieldByName("B")
+ }
+}
+
+type R0 struct {
+ *R1
+ *R2
+ *R3
+ *R4
+}
+
+type R1 struct {
+ *R5
+ *R6
+ *R7
+ *R8
+}
+
+type R2 R1
+type R3 R1
+type R4 R1
+
+type R5 struct {
+ *R9
+ *R10
+ *R11
+ *R12
+}
+
+type R6 R5
+type R7 R5
+type R8 R5
+
+type R9 struct {
+ *R13
+ *R14
+ *R15
+ *R16
+}
+
+type R10 R9
+type R11 R9
+type R12 R9
+
+type R13 struct {
+ *R17
+ *R18
+ *R19
+ *R20
+}
+
+type R14 R13
+type R15 R13
+type R16 R13
+
+type R17 struct {
+ *R21
+ *R22
+ *R23
+ *R24
+}
+
+type R18 R17
+type R19 R17
+type R20 R17
+
+type R21 struct {
+ X int
+}
+
+type R22 R21
+type R23 R21
+type R24 R21
+
+func TestEmbed(t *testing.T) {
+ typ := TypeOf(R0{})
+ f, ok := typ.FieldByName("X")
+ if ok {
+ t.Fatalf(`FieldByName("X") should fail, returned %v`, f.Index)
+ }
+}
+
+func BenchmarkFieldByName3(b *testing.B) {
+ t := TypeOf(R0{})
+ for i := 0; i < b.N; i++ {
+ t.FieldByName("X")
+ }
+}
+
+// An exhaustive is a mechanism for writing exhaustive or stochastic tests.
+// The basic usage is:
+//
+// for x.Next() {
+// ... code using x.Maybe() or x.Choice(n) to create test cases ...
+// }
+//
+// Each iteration of the loop returns a different set of results, until all
+// possible result sets have been explored. It is okay for different code paths
+// to make different method call sequences on x, but there must be no
+// other source of non-determinism in the call sequences.
+//
+// When faced with a new decision, x chooses randomly. Future explorations
+// of that path will choose successive values for the result. Thus, stopping
+// the loop after a fixed number of iterations gives somewhat stochastic
+// testing.
+//
+// Example:
+//
+// for x.Next() {
+// v := make([]bool, x.Choose(4))
+// for i := range v {
+// v[i] = x.Maybe()
+// }
+// fmt.Println(v)
+// }
+//
+// prints (in some order):
+//
+// []
+// [false]
+// [true]
+// [false false]
+// [false true]
+// ...
+// [true true]
+// [false false false]
+// ...
+// [true true true]
+// [false false false false]
+// ...
+// [true true true true]
+//
+type exhaustive struct {
+ r *rand.Rand
+ pos int
+ last []choice
+}
+
+type choice struct {
+ off int
+ n int
+ max int
+}
+
+func (x *exhaustive) Next() bool {
+ if x.r == nil {
+ x.r = rand.New(rand.NewSource(time.Now().UnixNano()))
+ }
+ x.pos = 0
+ if x.last == nil {
+ x.last = []choice{}
+ return true
+ }
+ for i := len(x.last) - 1; i >= 0; i-- {
+ c := &x.last[i]
+ if c.n+1 < c.max {
+ c.n++
+ x.last = x.last[:i+1]
+ return true
+ }
+ }
+ return false
+}
+
+func (x *exhaustive) Choose(max int) int {
+ if x.pos >= len(x.last) {
+ x.last = append(x.last, choice{x.r.Intn(max), 0, max})
+ }
+ c := &x.last[x.pos]
+ x.pos++
+ if c.max != max {
+ panic("inconsistent use of exhaustive tester")
+ }
+ return (c.n + c.off) % max
+}
+
+func (x *exhaustive) Maybe() bool {
+ return x.Choose(2) == 1
+}
diff --git a/src/pkg/reflect/asm_386.s b/src/pkg/reflect/asm_386.s
new file mode 100644
index 000000000..27d3fa21d
--- /dev/null
+++ b/src/pkg/reflect/asm_386.s
@@ -0,0 +1,13 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// makeFuncStub is the code half of the function returned by MakeFunc.
+// See the comment on the declaration of makeFuncStub in value.go
+// for more details.
+TEXT ·makeFuncStub(SB),7,$8
+ MOVL DX, 0(SP)
+ LEAL arg+0(FP), CX
+ MOVL CX, 4(SP)
+ CALL ·callReflect(SB)
+ RET
diff --git a/src/pkg/reflect/asm_amd64.s b/src/pkg/reflect/asm_amd64.s
new file mode 100644
index 000000000..d51d982a9
--- /dev/null
+++ b/src/pkg/reflect/asm_amd64.s
@@ -0,0 +1,13 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// makeFuncStub is the code half of the function returned by MakeFunc.
+// See the comment on the declaration of makeFuncStub in value.go
+// for more details.
+TEXT ·makeFuncStub(SB),7,$16
+ MOVQ DX, 0(SP)
+ LEAQ arg+0(FP), CX
+ MOVQ CX, 8(SP)
+ CALL ·callReflect(SB)
+ RET
diff --git a/src/pkg/reflect/asm_arm.s b/src/pkg/reflect/asm_arm.s
new file mode 100644
index 000000000..db487f8a5
--- /dev/null
+++ b/src/pkg/reflect/asm_arm.s
@@ -0,0 +1,13 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// makeFuncStub is jumped to by the code generated by MakeFunc.
+// See the comment on the declaration of makeFuncStub in value.go
+// for more details.
+TEXT ·makeFuncStub(SB),7,$8
+ MOVW R7, 4(R13)
+ MOVW $arg+0(FP), R1
+ MOVW R1, 8(R13)
+ BL ·callReflect(SB)
+ RET
diff --git a/src/pkg/reflect/deepequal.go b/src/pkg/reflect/deepequal.go
index c12e90f36..db047963e 100644
--- a/src/pkg/reflect/deepequal.go
+++ b/src/pkg/reflect/deepequal.go
@@ -122,9 +122,12 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool
panic("Not reached")
}
-// DeepEqual tests for deep equality. It uses normal == equality where possible
-// but will scan members of arrays, slices, maps, and fields of structs. It correctly
-// handles recursive types. Functions are equal only if they are both nil.
+// DeepEqual tests for deep equality. It uses normal == equality where
+// possible but will scan elements of arrays, slices, maps, and fields of
+// structs. In maps, keys are compared with == but elements use deep
+// equality. DeepEqual correctly handles recursive types. Functions are equal
+// only if they are both nil.
+// An empty slice is not equal to a nil slice.
func DeepEqual(a1, a2 interface{}) bool {
if a1 == nil || a2 == nil {
return a1 == a2
diff --git a/src/pkg/reflect/example_test.go b/src/pkg/reflect/example_test.go
new file mode 100644
index 000000000..62455c00a
--- /dev/null
+++ b/src/pkg/reflect/example_test.go
@@ -0,0 +1,52 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package reflect_test
+
+import (
+ "fmt"
+ "reflect"
+)
+
+func ExampleMakeFunc() {
+ // swap is the implementation passed to MakeFunc.
+ // It must work in terms of reflect.Values so that it is possible
+ // to write code without knowing beforehand what the types
+ // will be.
+ swap := func(in []reflect.Value) []reflect.Value {
+ return []reflect.Value{in[1], in[0]}
+ }
+
+ // makeSwap expects fptr to be a pointer to a nil function.
+ // It sets that pointer to a new function created with MakeFunc.
+ // When the function is invoked, reflect turns the arguments
+ // into Values, calls swap, and then turns swap's result slice
+ // into the values returned by the new function.
+ makeSwap := func(fptr interface{}) {
+ // fptr is a pointer to a function.
+ // Obtain the function value itself (likely nil) as a reflect.Value
+ // so that we can query its type and then set the value.
+ fn := reflect.ValueOf(fptr).Elem()
+
+ // Make a function of the right type.
+ v := reflect.MakeFunc(fn.Type(), swap)
+
+ // Assign it to the value fn represents.
+ fn.Set(v)
+ }
+
+ // Make and call a swap function for ints.
+ var intSwap func(int, int) (int, int)
+ makeSwap(&intSwap)
+ fmt.Println(intSwap(0, 1))
+
+ // Make and call a swap function for float64s.
+ var floatSwap func(float64, float64) (float64, float64)
+ makeSwap(&floatSwap)
+ fmt.Println(floatSwap(2.72, 3.14))
+
+ // Output:
+ // 1 0
+ // 3.14 2.72
+}
diff --git a/src/pkg/reflect/export_test.go b/src/pkg/reflect/export_test.go
new file mode 100644
index 000000000..cd8cf2cf2
--- /dev/null
+++ b/src/pkg/reflect/export_test.go
@@ -0,0 +1,18 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package reflect
+
+// MakeRO returns a copy of v with the read-only flag set.
+func MakeRO(v Value) Value {
+ v.flag |= flagRO
+ return v
+}
+
+// IsRO reports whether v's read-only flag is set.
+func IsRO(v Value) bool {
+ return v.flag&flagRO != 0
+}
+
+var ArrayOf = arrayOf
diff --git a/src/pkg/reflect/makefunc.go b/src/pkg/reflect/makefunc.go
new file mode 100644
index 000000000..024f938f1
--- /dev/null
+++ b/src/pkg/reflect/makefunc.go
@@ -0,0 +1,67 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// MakeFunc implementation.
+
+package reflect
+
+import (
+ "unsafe"
+)
+
+// makeFuncImpl is the closure value implementing the function
+// returned by MakeFunc.
+type makeFuncImpl struct {
+ code uintptr
+ typ *funcType
+ fn func([]Value) []Value
+}
+
+// MakeFunc returns a new function of the given Type
+// that wraps the function fn. When called, that new function
+// does the following:
+//
+// - converts its arguments to a list of Values args.
+// - runs results := fn(args).
+// - returns the results as a slice of Values, one per formal result.
+//
+// The implementation fn can assume that the argument Value slice
+// has the number and type of arguments given by typ.
+// If typ describes a variadic function, the final Value is itself
+// a slice representing the variadic arguments, as in the
+// body of a variadic function. The result Value slice returned by fn
+// must have the number and type of results given by typ.
+//
+// The Value.Call method allows the caller to invoke a typed function
+// in terms of Values; in contrast, MakeFunc allows the caller to implement
+// a typed function in terms of Values.
+//
+// The Examples section of the documentation includes an illustration
+// of how to use MakeFunc to build a swap function for different types.
+//
+func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
+ if typ.Kind() != Func {
+ panic("reflect: call of MakeFunc with non-Func type")
+ }
+
+ t := typ.common()
+ ftyp := (*funcType)(unsafe.Pointer(t))
+
+ // indirect Go func value (dummy) to obtain
+ // actual code address. (A Go func is a pointer
+ // to a C function pointer. http://golang.org/s/go11func.)
+ dummy := makeFuncStub
+ code := **(**uintptr)(unsafe.Pointer(&dummy))
+
+ impl := &makeFuncImpl{code: code, typ: ftyp, fn: fn}
+
+ return Value{t, unsafe.Pointer(impl), flag(Func) << flagKindShift}
+}
+
+// makeFuncStub is an assembly function that is the code half of
+// the function returned from MakeFunc. It expects a *callReflectFunc
+// as its context register, and its job is to invoke callReflect(ctxt, frame)
+// where ctxt is the context register and frame is a pointer to the first
+// word in the passed-in argument frame.
+func makeFuncStub()
diff --git a/src/pkg/reflect/set_test.go b/src/pkg/reflect/set_test.go
index 8135a4cd1..85dc55e68 100644
--- a/src/pkg/reflect/set_test.go
+++ b/src/pkg/reflect/set_test.go
@@ -81,11 +81,11 @@ func TestImplicitMapConversion(t *testing.T) {
t.Errorf("#5 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m)
}
if p := mv.MapIndex(ValueOf(b1)).Elem().Pointer(); p != uintptr(unsafe.Pointer(b2)) {
- t.Errorf("#5 MapIndex(b1) = %p want %p", p, b2)
+ t.Errorf("#5 MapIndex(b1) = %#x want %p", p, b2)
}
}
{
- // convert channel direction
+ // convert channel direction
m := make(map[<-chan int]chan int)
mv := ValueOf(m)
c1 := make(chan int)
@@ -96,7 +96,7 @@ func TestImplicitMapConversion(t *testing.T) {
t.Errorf("#6 after SetMapIndex(c1, c2): %p (!= %p), %t (map=%v)", x, c2, ok, m)
}
if p := mv.MapIndex(ValueOf(c1)).Pointer(); p != ValueOf(c2).Pointer() {
- t.Errorf("#6 MapIndex(c1) = %p want %p", p, c2)
+ t.Errorf("#6 MapIndex(c1) = %#x want %p", p, c2)
}
}
{
@@ -115,7 +115,7 @@ func TestImplicitMapConversion(t *testing.T) {
t.Errorf("#7 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m)
}
if p := mv.MapIndex(ValueOf(b1)).Pointer(); p != uintptr(unsafe.Pointer(b2)) {
- t.Errorf("#7 MapIndex(b1) = %p want %p", p, b2)
+ t.Errorf("#7 MapIndex(b1) = %#x want %p", p, b2)
}
}
diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go
index 060bde3af..94a7521a7 100644
--- a/src/pkg/reflect/type.go
+++ b/src/pkg/reflect/type.go
@@ -92,6 +92,9 @@ type Type interface {
// AssignableTo returns true if a value of the type is assignable to type u.
AssignableTo(u Type) bool
+ // ConvertibleTo returns true if a value of the type is convertible to type u.
+ ConvertibleTo(u Type) bool
+
// Methods applicable only to some types, depending on Kind.
// The methods allowed for each kind are:
//
@@ -181,11 +184,16 @@ type Type interface {
// It panics if i is not in the range [0, NumOut()).
Out(i int) Type
- runtimeType() *runtimeType
- common() *commonType
+ common() *rtype
uncommon() *uncommonType
}
+/*
+ * These data structures are known to the compiler (../../cmd/gc/reflect.c).
+ * A few are known to ../runtime/type.go to convey to debuggers.
+ * They are also known to ../runtime/type.h.
+ */
+
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint
@@ -220,41 +228,30 @@ const (
UnsafePointer
)
-/*
- * These data structures are known to the compiler (../../cmd/gc/reflect.c).
- * A few are known to ../runtime/type.go to convey to debuggers.
- */
-
-// The compiler can only construct empty interface values at
-// compile time; non-empty interface values get created
-// during initialization. Type is an empty interface
-// so that the compiler can lay out references as data.
-// The underlying type is *reflect.ArrayType and so on.
-type runtimeType interface{}
-
-// commonType is the common implementation of most values.
+// rtype is the common implementation of most values.
// It is embedded in other, public struct types, but always
// with a unique tag like `reflect:"array"` or `reflect:"ptr"`
// so that code cannot convert from, say, *arrayType to *ptrType.
-type commonType struct {
- size uintptr // size in bytes
- hash uint32 // hash of type; avoids computation in hash tables
- _ uint8 // unused/padding
- align uint8 // alignment of variable with this type
- fieldAlign uint8 // alignment of struct field with this type
- kind uint8 // enumeration for C
- alg *uintptr // algorithm table (../runtime/runtime.h:/Alg)
- string *string // string form; unnecessary but undeniably useful
- *uncommonType // (relatively) uncommon fields
- ptrToThis *runtimeType // pointer to this type, if used in binary or has methods
+type rtype struct {
+ size uintptr // size in bytes
+ hash uint32 // hash of type; avoids computation in hash tables
+ _ uint8 // unused/padding
+ align uint8 // alignment of variable with this type
+ fieldAlign uint8 // alignment of struct field with this type
+ kind uint8 // enumeration for C
+ alg *uintptr // algorithm table (../runtime/runtime.h:/Alg)
+ gc uintptr // garbage collection data
+ string *string // string form; unnecessary but undeniably useful
+ *uncommonType // (relatively) uncommon fields
+ ptrToThis *rtype // type for pointer to this type, if used in binary or has methods
}
// Method on non-interface type
type method struct {
name *string // name of method
pkgPath *string // nil for exported Names; otherwise import path
- mtyp *runtimeType // method type (without receiver)
- typ *runtimeType // .(*FuncType) underneath (with receiver)
+ mtyp *rtype // method type (without receiver)
+ typ *rtype // .(*FuncType) underneath (with receiver)
ifn unsafe.Pointer // fn used in interface call (one-word receiver)
tfn unsafe.Pointer // fn used for normal method call
}
@@ -280,72 +277,72 @@ const (
// arrayType represents a fixed array type.
type arrayType struct {
- commonType `reflect:"array"`
- elem *runtimeType // array element type
- slice *runtimeType // slice type
- len uintptr
+ rtype `reflect:"array"`
+ elem *rtype // array element type
+ slice *rtype // slice type
+ len uintptr
}
// chanType represents a channel type.
type chanType struct {
- commonType `reflect:"chan"`
- elem *runtimeType // channel element type
- dir uintptr // channel direction (ChanDir)
+ rtype `reflect:"chan"`
+ elem *rtype // channel element type
+ dir uintptr // channel direction (ChanDir)
}
// funcType represents a function type.
type funcType struct {
- commonType `reflect:"func"`
- dotdotdot bool // last input parameter is ...
- in []*runtimeType // input parameter types
- out []*runtimeType // output parameter types
+ rtype `reflect:"func"`
+ dotdotdot bool // last input parameter is ...
+ in []*rtype // input parameter types
+ out []*rtype // output parameter types
}
// imethod represents a method on an interface type
type imethod struct {
- name *string // name of method
- pkgPath *string // nil for exported Names; otherwise import path
- typ *runtimeType // .(*FuncType) underneath
+ name *string // name of method
+ pkgPath *string // nil for exported Names; otherwise import path
+ typ *rtype // .(*FuncType) underneath
}
// interfaceType represents an interface type.
type interfaceType struct {
- commonType `reflect:"interface"`
- methods []imethod // sorted by hash
+ rtype `reflect:"interface"`
+ methods []imethod // sorted by hash
}
// mapType represents a map type.
type mapType struct {
- commonType `reflect:"map"`
- key *runtimeType // map key type
- elem *runtimeType // map element (value) type
+ rtype `reflect:"map"`
+ key *rtype // map key type
+ elem *rtype // map element (value) type
}
// ptrType represents a pointer type.
type ptrType struct {
- commonType `reflect:"ptr"`
- elem *runtimeType // pointer element (pointed at) type
+ rtype `reflect:"ptr"`
+ elem *rtype // pointer element (pointed at) type
}
// sliceType represents a slice type.
type sliceType struct {
- commonType `reflect:"slice"`
- elem *runtimeType // slice element type
+ rtype `reflect:"slice"`
+ elem *rtype // slice element type
}
// Struct field
type structField struct {
- name *string // nil for embedded fields
- pkgPath *string // nil for exported Names; otherwise import path
- typ *runtimeType // type of field
- tag *string // nil if no tag
- offset uintptr // byte offset of field within struct
+ name *string // nil for embedded fields
+ pkgPath *string // nil for exported Names; otherwise import path
+ typ *rtype // type of field
+ tag *string // nil if no tag
+ offset uintptr // byte offset of field within struct
}
// structType represents a struct type.
type structType struct {
- commonType `reflect:"struct"`
- fields []structField // sorted by offset
+ rtype `reflect:"struct"`
+ fields []structField // sorted by offset
}
/*
@@ -359,7 +356,7 @@ type Method struct {
// PkgPath is the package path that qualifies a lower case (unexported)
// method name. It is empty for upper case (exported) method names.
// The combination of PkgPath and Name uniquely identifies a method
- // in a method set.
+ // in a method set.
// See http://golang.org/ref/spec#Uniqueness_of_identifiers
Name string
PkgPath string
@@ -428,18 +425,11 @@ func (t *uncommonType) Name() string {
return *t.name
}
-func (t *commonType) toType() Type {
- if t == nil {
- return nil
- }
- return t
-}
-
-func (t *commonType) String() string { return *t.string }
+func (t *rtype) String() string { return *t.string }
-func (t *commonType) Size() uintptr { return t.size }
+func (t *rtype) Size() uintptr { return t.size }
-func (t *commonType) Bits() int {
+func (t *rtype) Bits() int {
if t == nil {
panic("reflect: Bits of nil Type")
}
@@ -450,13 +440,13 @@ func (t *commonType) Bits() int {
return int(t.size) * 8
}
-func (t *commonType) Align() int { return int(t.align) }
+func (t *rtype) Align() int { return int(t.align) }
-func (t *commonType) FieldAlign() int { return int(t.fieldAlign) }
+func (t *rtype) FieldAlign() int { return int(t.fieldAlign) }
-func (t *commonType) Kind() Kind { return Kind(t.kind & kindMask) }
+func (t *rtype) Kind() Kind { return Kind(t.kind & kindMask) }
-func (t *commonType) common() *commonType { return t }
+func (t *rtype) common() *rtype { return t }
func (t *uncommonType) Method(i int) (m Method) {
if t == nil || i < 0 || i >= len(t.methods) {
@@ -471,9 +461,9 @@ func (t *uncommonType) Method(i int) (m Method) {
m.PkgPath = *p.pkgPath
fl |= flagRO
}
- mt := toCommonType(p.typ)
+ mt := p.typ
m.Type = mt
- fn := p.tfn
+ fn := unsafe.Pointer(&p.tfn)
m.Func = Value{mt, fn, fl}
m.Index = i
return
@@ -502,8 +492,8 @@ func (t *uncommonType) MethodByName(name string) (m Method, ok bool) {
// TODO(rsc): 6g supplies these, but they are not
// as efficient as they could be: they have commonType
-// as the receiver instead of *commonType.
-func (t *commonType) NumMethod() int {
+// as the receiver instead of *rtype.
+func (t *rtype) NumMethod() int {
if t.Kind() == Interface {
tt := (*interfaceType)(unsafe.Pointer(t))
return tt.NumMethod()
@@ -511,7 +501,7 @@ func (t *commonType) NumMethod() int {
return t.uncommonType.NumMethod()
}
-func (t *commonType) Method(i int) (m Method) {
+func (t *rtype) Method(i int) (m Method) {
if t.Kind() == Interface {
tt := (*interfaceType)(unsafe.Pointer(t))
return tt.Method(i)
@@ -519,7 +509,7 @@ func (t *commonType) Method(i int) (m Method) {
return t.uncommonType.Method(i)
}
-func (t *commonType) MethodByName(name string) (m Method, ok bool) {
+func (t *rtype) MethodByName(name string) (m Method, ok bool) {
if t.Kind() == Interface {
tt := (*interfaceType)(unsafe.Pointer(t))
return tt.MethodByName(name)
@@ -527,15 +517,15 @@ func (t *commonType) MethodByName(name string) (m Method, ok bool) {
return t.uncommonType.MethodByName(name)
}
-func (t *commonType) PkgPath() string {
+func (t *rtype) PkgPath() string {
return t.uncommonType.PkgPath()
}
-func (t *commonType) Name() string {
+func (t *rtype) Name() string {
return t.uncommonType.Name()
}
-func (t *commonType) ChanDir() ChanDir {
+func (t *rtype) ChanDir() ChanDir {
if t.Kind() != Chan {
panic("reflect: ChanDir of non-chan type")
}
@@ -543,7 +533,7 @@ func (t *commonType) ChanDir() ChanDir {
return ChanDir(tt.dir)
}
-func (t *commonType) IsVariadic() bool {
+func (t *rtype) IsVariadic() bool {
if t.Kind() != Func {
panic("reflect: IsVariadic of non-func type")
}
@@ -551,7 +541,7 @@ func (t *commonType) IsVariadic() bool {
return tt.dotdotdot
}
-func (t *commonType) Elem() Type {
+func (t *rtype) Elem() Type {
switch t.Kind() {
case Array:
tt := (*arrayType)(unsafe.Pointer(t))
@@ -572,7 +562,7 @@ func (t *commonType) Elem() Type {
panic("reflect: Elem of invalid type")
}
-func (t *commonType) Field(i int) StructField {
+func (t *rtype) Field(i int) StructField {
if t.Kind() != Struct {
panic("reflect: Field of non-struct type")
}
@@ -580,7 +570,7 @@ func (t *commonType) Field(i int) StructField {
return tt.Field(i)
}
-func (t *commonType) FieldByIndex(index []int) StructField {
+func (t *rtype) FieldByIndex(index []int) StructField {
if t.Kind() != Struct {
panic("reflect: FieldByIndex of non-struct type")
}
@@ -588,7 +578,7 @@ func (t *commonType) FieldByIndex(index []int) StructField {
return tt.FieldByIndex(index)
}
-func (t *commonType) FieldByName(name string) (StructField, bool) {
+func (t *rtype) FieldByName(name string) (StructField, bool) {
if t.Kind() != Struct {
panic("reflect: FieldByName of non-struct type")
}
@@ -596,7 +586,7 @@ func (t *commonType) FieldByName(name string) (StructField, bool) {
return tt.FieldByName(name)
}
-func (t *commonType) FieldByNameFunc(match func(string) bool) (StructField, bool) {
+func (t *rtype) FieldByNameFunc(match func(string) bool) (StructField, bool) {
if t.Kind() != Struct {
panic("reflect: FieldByNameFunc of non-struct type")
}
@@ -604,7 +594,7 @@ func (t *commonType) FieldByNameFunc(match func(string) bool) (StructField, bool
return tt.FieldByNameFunc(match)
}
-func (t *commonType) In(i int) Type {
+func (t *rtype) In(i int) Type {
if t.Kind() != Func {
panic("reflect: In of non-func type")
}
@@ -612,7 +602,7 @@ func (t *commonType) In(i int) Type {
return toType(tt.in[i])
}
-func (t *commonType) Key() Type {
+func (t *rtype) Key() Type {
if t.Kind() != Map {
panic("reflect: Key of non-map type")
}
@@ -620,7 +610,7 @@ func (t *commonType) Key() Type {
return toType(tt.key)
}
-func (t *commonType) Len() int {
+func (t *rtype) Len() int {
if t.Kind() != Array {
panic("reflect: Len of non-array type")
}
@@ -628,7 +618,7 @@ func (t *commonType) Len() int {
return int(tt.len)
}
-func (t *commonType) NumField() int {
+func (t *rtype) NumField() int {
if t.Kind() != Struct {
panic("reflect: NumField of non-struct type")
}
@@ -636,7 +626,7 @@ func (t *commonType) NumField() int {
return len(tt.fields)
}
-func (t *commonType) NumIn() int {
+func (t *rtype) NumIn() int {
if t.Kind() != Func {
panic("reflect: NumIn of non-func type")
}
@@ -644,7 +634,7 @@ func (t *commonType) NumIn() int {
return len(tt.in)
}
-func (t *commonType) NumOut() int {
+func (t *rtype) NumOut() int {
if t.Kind() != Func {
panic("reflect: NumOut of non-func type")
}
@@ -652,7 +642,7 @@ func (t *commonType) NumOut() int {
return len(tt.out)
}
-func (t *commonType) Out(i int) Type {
+func (t *rtype) Out(i int) Type {
if t.Kind() != Func {
panic("reflect: Out of non-func type")
}
@@ -718,7 +708,7 @@ type StructField struct {
Tag StructTag // field tag string
Offset uintptr // offset within struct, in bytes
Index []int // index sequence for Type.FieldByIndex
- Anonymous bool // is an anonymous field
+ Anonymous bool // is an embedded field
}
// A StructTag is the tag string in a struct field.
@@ -822,7 +812,7 @@ func (t *structType) Field(i int) (f StructField) {
// FieldByIndex returns the nested field corresponding to index.
func (t *structType) FieldByIndex(index []int) (f StructField) {
- f.Type = Type(t.toType())
+ f.Type = toType(&t.rtype)
for i, x := range index {
if i > 0 {
ft := f.Type
@@ -836,107 +826,143 @@ func (t *structType) FieldByIndex(index []int) (f StructField) {
return
}
-const inf = 1 << 30 // infinity - no struct has that many nesting levels
-
-func (t *structType) fieldByNameFunc(match func(string) bool, mark map[*structType]bool, depth int) (ff StructField, fd int) {
- fd = inf // field depth
+// A fieldScan represents an item on the fieldByNameFunc scan work list.
+type fieldScan struct {
+ typ *structType
+ index []int
+}
- if mark[t] {
- // Struct already seen.
- return
- }
- mark[t] = true
-
- var fi int // field index
- n := 0 // number of matching fields at depth fd
-L:
- for i := range t.fields {
- f := t.Field(i)
- d := inf
- switch {
- case match(f.Name):
- // Matching top-level field.
- d = depth
- case f.Anonymous:
- ft := f.Type
- if ft.Kind() == Ptr {
- ft = ft.Elem()
+// FieldByNameFunc returns the struct field with a name that satisfies the
+// match function and a boolean to indicate if the field was found.
+func (t *structType) FieldByNameFunc(match func(string) bool) (result StructField, ok bool) {
+ // This uses the same condition that the Go language does: there must be a unique instance
+ // of the match at a given depth level. If there are multiple instances of a match at the
+ // same depth, they annihilate each other and inhibit any possible match at a lower level.
+ // The algorithm is breadth first search, one depth level at a time.
+
+ // The current and next slices are work queues:
+ // current lists the fields to visit on this depth level,
+ // and next lists the fields on the next lower level.
+ current := []fieldScan{}
+ next := []fieldScan{{typ: t}}
+
+ // nextCount records the number of times an embedded type has been
+ // encountered and considered for queueing in the 'next' slice.
+ // We only queue the first one, but we increment the count on each.
+ // If a struct type T can be reached more than once at a given depth level,
+ // then it annihilates itself and need not be considered at all when we
+ // process that next depth level.
+ var nextCount map[*structType]int
+
+ // visited records the structs that have been considered already.
+ // Embedded pointer fields can create cycles in the graph of
+ // reachable embedded types; visited avoids following those cycles.
+ // It also avoids duplicated effort: if we didn't find the field in an
+ // embedded type T at level 2, we won't find it in one at level 4 either.
+ visited := map[*structType]bool{}
+
+ for len(next) > 0 {
+ current, next = next, current[:0]
+ count := nextCount
+ nextCount = nil
+
+ // Process all the fields at this depth, now listed in 'current'.
+ // The loop queues embedded fields found in 'next', for processing during the next
+ // iteration. The multiplicity of the 'current' field counts is recorded
+ // in 'count'; the multiplicity of the 'next' field counts is recorded in 'nextCount'.
+ for _, scan := range current {
+ t := scan.typ
+ if visited[t] {
+ // We've looked through this type before, at a higher level.
+ // That higher level would shadow the lower level we're now at,
+ // so this one can't be useful to us. Ignore it.
+ continue
}
- switch {
- case match(ft.Name()):
- // Matching anonymous top-level field.
- d = depth
- case fd > depth:
- // No top-level field yet; look inside nested structs.
- if ft.Kind() == Struct {
- st := (*structType)(unsafe.Pointer(ft.(*commonType)))
- f, d = st.fieldByNameFunc(match, mark, depth+1)
+ visited[t] = true
+ for i := range t.fields {
+ f := &t.fields[i]
+ // Find name and type for field f.
+ var fname string
+ var ntyp *rtype
+ if f.name != nil {
+ fname = *f.name
+ } else {
+ // Anonymous field of type T or *T.
+ // Name taken from type.
+ ntyp = f.typ
+ if ntyp.Kind() == Ptr {
+ ntyp = ntyp.Elem().common()
+ }
+ fname = ntyp.Name()
}
- }
- }
- switch {
- case d < fd:
- // Found field at shallower depth.
- ff, fi, fd = f, i, d
- n = 1
- case d == fd:
- // More than one matching field at the same depth (or d, fd == inf).
- // Same as no field found at this depth.
- n++
- if d == depth {
- // Impossible to find a field at lower depth.
- break L
- }
- }
- }
+ // Does it match?
+ if match(fname) {
+ // Potential match
+ if count[t] > 1 || ok {
+ // Name appeared multiple times at this level: annihilate.
+ return StructField{}, false
+ }
+ result = t.Field(i)
+ result.Index = nil
+ result.Index = append(result.Index, scan.index...)
+ result.Index = append(result.Index, i)
+ ok = true
+ continue
+ }
- if n == 1 {
- // Found matching field.
- if depth >= len(ff.Index) {
- ff.Index = make([]int, depth+1)
+ // Queue embedded struct fields for processing with next level,
+ // but only if we haven't seen a match yet at this level and only
+ // if the embedded types haven't already been queued.
+ if ok || ntyp == nil || ntyp.Kind() != Struct {
+ continue
+ }
+ styp := (*structType)(unsafe.Pointer(ntyp))
+ if nextCount[styp] > 0 {
+ nextCount[styp] = 2 // exact multiple doesn't matter
+ continue
+ }
+ if nextCount == nil {
+ nextCount = map[*structType]int{}
+ }
+ nextCount[styp] = 1
+ if count[t] > 1 {
+ nextCount[styp] = 2 // exact multiple doesn't matter
+ }
+ var index []int
+ index = append(index, scan.index...)
+ index = append(index, i)
+ next = append(next, fieldScan{styp, index})
+ }
}
- if len(ff.Index) > 1 {
- ff.Index[depth] = fi
+ if ok {
+ break
}
- } else {
- // None or more than one matching field found.
- fd = inf
}
-
- delete(mark, t)
return
}
// FieldByName returns the struct field with the given name
// and a boolean to indicate if the field was found.
func (t *structType) FieldByName(name string) (f StructField, present bool) {
- return t.FieldByNameFunc(func(s string) bool { return s == name })
-}
-
-// FieldByNameFunc returns the struct field with a name that satisfies the
-// match function and a boolean to indicate if the field was found.
-func (t *structType) FieldByNameFunc(match func(string) bool) (f StructField, present bool) {
- if ff, fd := t.fieldByNameFunc(match, make(map[*structType]bool), 0); fd < inf {
- ff.Index = ff.Index[0 : fd+1]
- f, present = ff, true
- }
- return
-}
-
-// Convert runtime type to reflect type.
-func toCommonType(p *runtimeType) *commonType {
- if p == nil {
- return nil
+ // Quick check for top-level name, or struct without anonymous fields.
+ hasAnon := false
+ if name != "" {
+ for i := range t.fields {
+ tf := &t.fields[i]
+ if tf.name == nil {
+ hasAnon = true
+ continue
+ }
+ if *tf.name == name {
+ return t.Field(i), true
+ }
+ }
}
- return (*p).(*commonType)
-}
-
-func toType(p *runtimeType) Type {
- if p == nil {
- return nil
+ if !hasAnon {
+ return
}
- return (*p).(*commonType)
+ return t.FieldByNameFunc(func(s string) bool { return s == name })
}
// TypeOf returns the reflection Type of the value in the interface{}.
@@ -949,28 +975,18 @@ func TypeOf(i interface{}) Type {
// ptrMap is the cache for PtrTo.
var ptrMap struct {
sync.RWMutex
- m map[*commonType]*ptrType
-}
-
-func (t *commonType) runtimeType() *runtimeType {
- // The runtimeType always precedes the commonType in memory.
- // Adjust pointer to find it.
- var rt struct {
- i runtimeType
- ct commonType
- }
- return (*runtimeType)(unsafe.Pointer(uintptr(unsafe.Pointer(t)) - unsafe.Offsetof(rt.ct)))
+ m map[*rtype]*ptrType
}
// PtrTo returns the pointer type with element t.
// For example, if t represents type Foo, PtrTo(t) represents *Foo.
func PtrTo(t Type) Type {
- return t.(*commonType).ptrTo()
+ return t.(*rtype).ptrTo()
}
-func (ct *commonType) ptrTo() *commonType {
- if p := ct.ptrToThis; p != nil {
- return toCommonType(p)
+func (t *rtype) ptrTo() *rtype {
+ if p := t.ptrToThis; p != nil {
+ return p
}
// Otherwise, synthesize one.
@@ -980,36 +996,31 @@ func (ct *commonType) ptrTo() *commonType {
// the type structures in read-only memory.
ptrMap.RLock()
if m := ptrMap.m; m != nil {
- if p := m[ct]; p != nil {
+ if p := m[t]; p != nil {
ptrMap.RUnlock()
- return &p.commonType
+ return &p.rtype
}
}
ptrMap.RUnlock()
ptrMap.Lock()
if ptrMap.m == nil {
- ptrMap.m = make(map[*commonType]*ptrType)
+ ptrMap.m = make(map[*rtype]*ptrType)
}
- p := ptrMap.m[ct]
+ p := ptrMap.m[t]
if p != nil {
// some other goroutine won the race and created it
ptrMap.Unlock()
- return &p.commonType
+ return &p.rtype
}
- var rt struct {
- i runtimeType
- ptrType
- }
- rt.i = &rt.commonType
-
- // initialize p using *byte's ptrType as a prototype.
- p = &rt.ptrType
- var ibyte interface{} = (*byte)(nil)
- bp := (*ptrType)(unsafe.Pointer((**(**runtimeType)(unsafe.Pointer(&ibyte))).(*commonType)))
- *p = *bp
+ // Create a new ptrType starting with the description
+ // of an *unsafe.Pointer.
+ p = new(ptrType)
+ var iptr interface{} = (*unsafe.Pointer)(nil)
+ prototype := *(**ptrType)(unsafe.Pointer(&iptr))
+ *p = *prototype
- s := "*" + *ct.string
+ s := "*" + *t.string
p.string = &s
// For the type structures linked into the binary, the
@@ -1017,37 +1028,53 @@ func (ct *commonType) ptrTo() *commonType {
// Create a good hash for the new string by using
// the FNV-1 hash's mixing function to combine the
// old hash and the new "*".
- p.hash = ct.hash*16777619 ^ '*'
+ p.hash = fnv1(t.hash, '*')
p.uncommonType = nil
p.ptrToThis = nil
- p.elem = (*runtimeType)(unsafe.Pointer(uintptr(unsafe.Pointer(ct)) - unsafe.Offsetof(rt.ptrType)))
+ p.elem = t
- ptrMap.m[ct] = p
+ ptrMap.m[t] = p
ptrMap.Unlock()
- return &p.commonType
+ return &p.rtype
+}
+
+// fnv1 incorporates the list of bytes into the hash x using the FNV-1 hash function.
+func fnv1(x uint32, list ...byte) uint32 {
+ for _, b := range list {
+ x = x*16777619 ^ uint32(b)
+ }
+ return x
}
-func (t *commonType) Implements(u Type) bool {
+func (t *rtype) Implements(u Type) bool {
if u == nil {
panic("reflect: nil type passed to Type.Implements")
}
if u.Kind() != Interface {
panic("reflect: non-interface type passed to Type.Implements")
}
- return implements(u.(*commonType), t)
+ return implements(u.(*rtype), t)
}
-func (t *commonType) AssignableTo(u Type) bool {
+func (t *rtype) AssignableTo(u Type) bool {
if u == nil {
panic("reflect: nil type passed to Type.AssignableTo")
}
- uu := u.(*commonType)
+ uu := u.(*rtype)
return directlyAssignable(uu, t) || implements(uu, t)
}
+func (t *rtype) ConvertibleTo(u Type) bool {
+ if u == nil {
+ panic("reflect: nil type passed to Type.ConvertibleTo")
+ }
+ uu := u.(*rtype)
+ return convertOp(uu, t) != nil
+}
+
// implements returns true if the type V implements the interface type T.
-func implements(T, V *commonType) bool {
+func implements(T, V *rtype) bool {
if T.Kind() != Interface {
return false
}
@@ -1105,7 +1132,7 @@ func implements(T, V *commonType) bool {
// http://golang.org/doc/go_spec.html#Assignability
// Ignoring the interface rules (implemented elsewhere)
// and the ideal constant rules (no ideal constants at run time).
-func directlyAssignable(T, V *commonType) bool {
+func directlyAssignable(T, V *rtype) bool {
// x's type V is identical to T?
if T == V {
return true
@@ -1117,10 +1144,28 @@ func directlyAssignable(T, V *commonType) bool {
return false
}
- // x's type T and V have identical underlying types.
- // Since at least one is unnamed, only the composite types
- // need to be considered.
- switch T.Kind() {
+ // x's type T and V must have identical underlying types.
+ return haveIdenticalUnderlyingType(T, V)
+}
+
+func haveIdenticalUnderlyingType(T, V *rtype) bool {
+ if T == V {
+ return true
+ }
+
+ kind := T.Kind()
+ if kind != V.Kind() {
+ return false
+ }
+
+ // Non-composite types of equal kind have same underlying type
+ // (the predefined instance of the type).
+ if Bool <= kind && kind <= Complex128 || kind == String || kind == UnsafePointer {
+ return true
+ }
+
+ // Composite types.
+ switch kind {
case Array:
return T.Elem() == V.Elem() && T.Len() == V.Len()
@@ -1178,8 +1223,19 @@ func directlyAssignable(T, V *commonType) bool {
for i := range t.fields {
tf := &t.fields[i]
vf := &v.fields[i]
- if tf.name != vf.name || tf.pkgPath != vf.pkgPath ||
- tf.typ != vf.typ || tf.tag != vf.tag || tf.offset != vf.offset {
+ if tf.name != vf.name && (tf.name == nil || vf.name == nil || *tf.name != *vf.name) {
+ return false
+ }
+ if tf.pkgPath != vf.pkgPath && (tf.pkgPath == nil || vf.pkgPath == nil || *tf.pkgPath != *vf.pkgPath) {
+ return false
+ }
+ if tf.typ != vf.typ {
+ return false
+ }
+ if tf.tag != vf.tag && (tf.tag == nil || vf.tag == nil || *tf.tag != *vf.tag) {
+ return false
+ }
+ if tf.offset != vf.offset {
return false
}
}
@@ -1188,3 +1244,295 @@ func directlyAssignable(T, V *commonType) bool {
return false
}
+
+// typelinks is implemented in package runtime.
+// It retuns a slice of all the 'typelink' information in the binary,
+// which is to say a slice of known types, sorted by string.
+// Note that strings are not unique identifiers for types:
+// there can be more than one with a given string.
+// Only types we might want to look up are included:
+// channels, maps, slices, and arrays.
+func typelinks() []*rtype
+
+// typesByString returns the subslice of typelinks() whose elements have
+// the given string representation.
+// It may be empty (no known types with that string) or may have
+// multiple elements (multiple types with that string).
+func typesByString(s string) []*rtype {
+ typ := typelinks()
+
+ // We are looking for the first index i where the string becomes >= s.
+ // This is a copy of sort.Search, with f(h) replaced by (*typ[h].string >= s).
+ i, j := 0, len(typ)
+ for i < j {
+ h := i + (j-i)/2 // avoid overflow when computing h
+ // i ≤ h < j
+ if !(*typ[h].string >= s) {
+ i = h + 1 // preserves f(i-1) == false
+ } else {
+ j = h // preserves f(j) == true
+ }
+ }
+ // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i.
+
+ // Having found the first, linear scan forward to find the last.
+ // We could do a second binary search, but the caller is going
+ // to do a linear scan anyway.
+ j = i
+ for j < len(typ) && *typ[j].string == s {
+ j++
+ }
+
+ // This slice will be empty if the string is not found.
+ return typ[i:j]
+}
+
+// The lookupCache caches ChanOf, MapOf, and SliceOf lookups.
+var lookupCache struct {
+ sync.RWMutex
+ m map[cacheKey]*rtype
+}
+
+// A cacheKey is the key for use in the lookupCache.
+// Four values describe any of the types we are looking for:
+// type kind, one or two subtypes, and an extra integer.
+type cacheKey struct {
+ kind Kind
+ t1 *rtype
+ t2 *rtype
+ extra uintptr
+}
+
+// cacheGet looks for a type under the key k in the lookupCache.
+// If it finds one, it returns that type.
+// If not, it returns nil with the cache locked.
+// The caller is expected to use cachePut to unlock the cache.
+func cacheGet(k cacheKey) Type {
+ lookupCache.RLock()
+ t := lookupCache.m[k]
+ lookupCache.RUnlock()
+ if t != nil {
+ return t
+ }
+
+ lookupCache.Lock()
+ t = lookupCache.m[k]
+ if t != nil {
+ lookupCache.Unlock()
+ return t
+ }
+
+ if lookupCache.m == nil {
+ lookupCache.m = make(map[cacheKey]*rtype)
+ }
+
+ return nil
+}
+
+// cachePut stores the given type in the cache, unlocks the cache,
+// and returns the type. It is expected that the cache is locked
+// because cacheGet returned nil.
+func cachePut(k cacheKey, t *rtype) Type {
+ lookupCache.m[k] = t
+ lookupCache.Unlock()
+ return t
+}
+
+// ChanOf returns the channel type with the given direction and element type.
+// For example, if t represents int, ChanOf(RecvDir, t) represents <-chan int.
+//
+// The gc runtime imposes a limit of 64 kB on channel element types.
+// If t's size is equal to or exceeds this limit, ChanOf panics.
+func ChanOf(dir ChanDir, t Type) Type {
+ typ := t.(*rtype)
+
+ // Look in cache.
+ ckey := cacheKey{Chan, typ, nil, uintptr(dir)}
+ if ch := cacheGet(ckey); ch != nil {
+ return ch
+ }
+
+ // This restriction is imposed by the gc compiler and the runtime.
+ if typ.size >= 1<<16 {
+ lookupCache.Unlock()
+ panic("reflect.ChanOf: element size too large")
+ }
+
+ // Look in known types.
+ // TODO: Precedence when constructing string.
+ var s string
+ switch dir {
+ default:
+ lookupCache.Unlock()
+ panic("reflect.ChanOf: invalid dir")
+ case SendDir:
+ s = "chan<- " + *typ.string
+ case RecvDir:
+ s = "<-chan " + *typ.string
+ case BothDir:
+ s = "chan " + *typ.string
+ }
+ for _, tt := range typesByString(s) {
+ ch := (*chanType)(unsafe.Pointer(tt))
+ if ch.elem == typ && ch.dir == uintptr(dir) {
+ return cachePut(ckey, tt)
+ }
+ }
+
+ // Make a channel type.
+ var ichan interface{} = (chan unsafe.Pointer)(nil)
+ prototype := *(**chanType)(unsafe.Pointer(&ichan))
+ ch := new(chanType)
+ *ch = *prototype
+ ch.string = &s
+ ch.hash = fnv1(typ.hash, 'c', byte(dir))
+ ch.elem = typ
+ ch.uncommonType = nil
+ ch.ptrToThis = nil
+
+ return cachePut(ckey, &ch.rtype)
+}
+
+// MapOf returns the map type with the given key and element types.
+// For example, if k represents int and e represents string,
+// MapOf(k, e) represents map[int]string.
+//
+// If the key type is not a valid map key type (that is, if it does
+// not implement Go's == operator), MapOf panics. TODO(rsc).
+func MapOf(key, elem Type) Type {
+ ktyp := key.(*rtype)
+ etyp := elem.(*rtype)
+
+ // TODO: Check for invalid key types.
+
+ // Look in cache.
+ ckey := cacheKey{Map, ktyp, etyp, 0}
+ if mt := cacheGet(ckey); mt != nil {
+ return mt
+ }
+
+ // Look in known types.
+ s := "map[" + *ktyp.string + "]" + *etyp.string
+ for _, tt := range typesByString(s) {
+ mt := (*mapType)(unsafe.Pointer(tt))
+ if mt.key == ktyp && mt.elem == etyp {
+ return cachePut(ckey, tt)
+ }
+ }
+
+ // Make a map type.
+ var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil)
+ prototype := *(**mapType)(unsafe.Pointer(&imap))
+ mt := new(mapType)
+ *mt = *prototype
+ mt.string = &s
+ mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash))
+ mt.key = ktyp
+ mt.elem = etyp
+ mt.uncommonType = nil
+ mt.ptrToThis = nil
+
+ return cachePut(ckey, &mt.rtype)
+}
+
+// SliceOf returns the slice type with element type t.
+// For example, if t represents int, SliceOf(t) represents []int.
+func SliceOf(t Type) Type {
+ typ := t.(*rtype)
+
+ // Look in cache.
+ ckey := cacheKey{Slice, typ, nil, 0}
+ if slice := cacheGet(ckey); slice != nil {
+ return slice
+ }
+
+ // Look in known types.
+ s := "[]" + *typ.string
+ for _, tt := range typesByString(s) {
+ slice := (*sliceType)(unsafe.Pointer(tt))
+ if slice.elem == typ {
+ return cachePut(ckey, tt)
+ }
+ }
+
+ // Make a slice type.
+ var islice interface{} = ([]unsafe.Pointer)(nil)
+ prototype := *(**sliceType)(unsafe.Pointer(&islice))
+ slice := new(sliceType)
+ *slice = *prototype
+ slice.string = &s
+ slice.hash = fnv1(typ.hash, '[')
+ slice.elem = typ
+ slice.uncommonType = nil
+ slice.ptrToThis = nil
+
+ return cachePut(ckey, &slice.rtype)
+}
+
+// ArrayOf returns the array type with the given count and element type.
+// For example, if t represents int, ArrayOf(5, t) represents [5]int.
+//
+// If the resulting type would be larger than the available address space,
+// ArrayOf panics.
+//
+// TODO(rsc): Unexported for now. Export once the alg field is set correctly
+// for the type. This may require significant work.
+func arrayOf(count int, elem Type) Type {
+ typ := elem.(*rtype)
+ slice := SliceOf(elem)
+
+ // Look in cache.
+ ckey := cacheKey{Array, typ, nil, uintptr(count)}
+ if slice := cacheGet(ckey); slice != nil {
+ return slice
+ }
+
+ // Look in known types.
+ s := "[" + strconv.Itoa(count) + "]" + *typ.string
+ for _, tt := range typesByString(s) {
+ slice := (*sliceType)(unsafe.Pointer(tt))
+ if slice.elem == typ {
+ return cachePut(ckey, tt)
+ }
+ }
+
+ // Make an array type.
+ var iarray interface{} = [1]unsafe.Pointer{}
+ prototype := *(**arrayType)(unsafe.Pointer(&iarray))
+ array := new(arrayType)
+ *array = *prototype
+ array.string = &s
+ array.hash = fnv1(typ.hash, '[')
+ for n := uint32(count); n > 0; n >>= 8 {
+ array.hash = fnv1(array.hash, byte(n))
+ }
+ array.hash = fnv1(array.hash, ']')
+ array.elem = typ
+ max := ^uintptr(0) / typ.size
+ if uintptr(count) > max {
+ panic("reflect.ArrayOf: array size would exceed virtual address space")
+ }
+ array.size = typ.size * uintptr(count)
+ array.align = typ.align
+ array.fieldAlign = typ.fieldAlign
+ // TODO: array.alg
+ // TODO: array.gc
+ array.uncommonType = nil
+ array.ptrToThis = nil
+ array.len = uintptr(count)
+ array.slice = slice.(*rtype)
+
+ return cachePut(ckey, &array.rtype)
+}
+
+// toType converts from a *rtype to a Type that can be returned
+// to the client of package reflect. In gc, the only concern is that
+// a nil *rtype must be replaced by a nil Type, but in gccgo this
+// function takes care of ensuring that multiple *rtype for the same
+// type are coalesced into a single Type.
+func toType(t *rtype) Type {
+ if t == nil {
+ return nil
+ }
+ return t
+}
diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index 79476ad22..c87812c46 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -60,7 +60,7 @@ func memmove(adst, asrc unsafe.Pointer, n uintptr) {
// direct operations.
type Value struct {
// typ holds the type of the value represented by a Value.
- typ *commonType
+ typ *rtype
// val holds the 1-word representation of the value.
// If flag's flagIndir bit is set, then val is a pointer to the data.
@@ -211,7 +211,7 @@ func storeIword(p unsafe.Pointer, w iword, n uintptr) {
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
- typ *runtimeType
+ typ *rtype
word iword
}
@@ -219,8 +219,8 @@ type emptyInterface struct {
type nonEmptyInterface struct {
// see ../runtime/iface.c:/Itab
itab *struct {
- ityp *runtimeType // static interface type
- typ *runtimeType // dynamic concrete type
+ ityp *rtype // static interface type
+ typ *rtype // dynamic concrete type
link unsafe.Pointer
bad int32
unused int32
@@ -302,6 +302,17 @@ func (v Value) Bytes() []byte {
return *(*[]byte)(v.val)
}
+// runes returns v's underlying value.
+// It panics if v's underlying value is not a slice of runes (int32s).
+func (v Value) runes() []rune {
+ v.mustBe(Slice)
+ if v.typ.Elem().Kind() != Int32 {
+ panic("reflect.Value.Bytes of non-rune slice")
+ }
+ // Slice is always bigger than a word; assume flagIndir.
+ return *(*[]rune)(v.val)
+}
+
// CanAddr returns true if the value's address can be obtained with Addr.
// Such values are called addressable. A value is addressable if it is
// an element of a slice, an element of an addressable array,
@@ -335,7 +346,7 @@ func (v Value) Call(in []Value) []Value {
}
// CallSlice calls the variadic function v with the input arguments in,
-// assigning the slice in[len(in)-1] to v's final variadic argument.
+// assigning the slice in[len(in)-1] to v's final variadic argument.
// For example, if len(in) == 3, v.Call(in) represents the Go call v(in[0], in[1], in[2]...).
// Call panics if v's Kind is not Func or if v is not variadic.
// It returns the output results as Values.
@@ -365,12 +376,12 @@ func (v Value) call(method string, in []Value) []Value {
if m.pkgPath != nil {
panic(method + " of unexported method")
}
- t = toCommonType(m.typ)
+ t = m.typ
iface := (*nonEmptyInterface)(v.val)
if iface.itab == nil {
panic(method + " of method on nil interface value")
}
- fn = iface.itab.fun[i]
+ fn = unsafe.Pointer(&iface.itab.fun[i])
rcvr = iface.word
} else {
ut := v.typ.uncommon()
@@ -381,8 +392,8 @@ func (v Value) call(method string, in []Value) []Value {
if m.pkgPath != nil {
panic(method + " of unexported method")
}
- fn = m.ifn
- t = toCommonType(m.mtyp)
+ fn = unsafe.Pointer(&m.ifn)
+ t = m.mtyp
rcvr = v.iword()
}
} else if v.flag&flagIndir != 0 {
@@ -490,9 +501,9 @@ func (v Value) call(method string, in []Value) []Value {
// TODO(rsc): revisit when reference counting happens.
// The values are holding up the in references for us,
// but something must be done for the out references.
- // For now make everything look like a pointer by pretending
- // to allocate a []*int.
- args := make([]*int, size/ptrSize)
+ // For now make everything look like a pointer by allocating
+ // a []unsafe.Pointer.
+ args := make([]unsafe.Pointer, size/ptrSize)
ptr := uintptr(unsafe.Pointer(&args[0]))
off := uintptr(0)
if v.flag&flagMethod != 0 {
@@ -502,7 +513,7 @@ func (v Value) call(method string, in []Value) []Value {
}
for i, v := range in {
v.mustBeExported()
- targ := t.In(i).(*commonType)
+ targ := t.In(i).(*rtype)
a := uintptr(targ.align)
off = (off + a - 1) &^ (a - 1)
n := targ.size
@@ -536,6 +547,85 @@ func (v Value) call(method string, in []Value) []Value {
return ret
}
+// callReflect is the call implementation used by a function
+// returned by MakeFunc. In many ways it is the opposite of the
+// method Value.call above. The method above converts a call using Values
+// into a call of a function with a concrete argument frame, while
+// callReflect converts a call of a function with a concrete argument
+// frame into a call using Values.
+// It is in this file so that it can be next to the call method above.
+// The remainder of the MakeFunc implementation is in makefunc.go.
+func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {
+ ftyp := ctxt.typ
+ f := ctxt.fn
+
+ // Copy argument frame into Values.
+ ptr := frame
+ off := uintptr(0)
+ in := make([]Value, 0, len(ftyp.in))
+ for _, arg := range ftyp.in {
+ typ := arg
+ off += -off & uintptr(typ.align-1)
+ v := Value{typ, nil, flag(typ.Kind()) << flagKindShift}
+ if typ.size <= ptrSize {
+ // value fits in word.
+ v.val = unsafe.Pointer(loadIword(unsafe.Pointer(uintptr(ptr)+off), typ.size))
+ } else {
+ // value does not fit in word.
+ // Must make a copy, because f might keep a reference to it,
+ // and we cannot let f keep a reference to the stack frame
+ // after this function returns, not even a read-only reference.
+ v.val = unsafe_New(typ)
+ memmove(v.val, unsafe.Pointer(uintptr(ptr)+off), typ.size)
+ v.flag |= flagIndir
+ }
+ in = append(in, v)
+ off += typ.size
+ }
+
+ // Call underlying function.
+ out := f(in)
+ if len(out) != len(ftyp.out) {
+ panic("reflect: wrong return count from function created by MakeFunc")
+ }
+
+ // Copy results back into argument frame.
+ if len(ftyp.out) > 0 {
+ off += -off & (ptrSize - 1)
+ for i, arg := range ftyp.out {
+ typ := arg
+ v := out[i]
+ if v.typ != typ {
+ panic("reflect: function created by MakeFunc using " + funcName(f) +
+ " returned wrong type: have " +
+ out[i].typ.String() + " for " + typ.String())
+ }
+ if v.flag&flagRO != 0 {
+ panic("reflect: function created by MakeFunc using " + funcName(f) +
+ " returned value obtained from unexported field")
+ }
+ off += -off & uintptr(typ.align-1)
+ addr := unsafe.Pointer(uintptr(ptr) + off)
+ if v.flag&flagIndir == 0 {
+ storeIword(addr, iword(v.val), typ.size)
+ } else {
+ memmove(addr, v.val, typ.size)
+ }
+ off += typ.size
+ }
+ }
+}
+
+// funcName returns the name of f, for use in error messages.
+func funcName(f func([]Value) []Value) string {
+ pc := *(*uintptr)(unsafe.Pointer(&f))
+ rf := runtime.FuncForPC(pc)
+ if rf != nil {
+ return rf.Name()
+ }
+ return "closure"
+}
+
// Cap returns v's capacity.
// It panics if v's Kind is not Array, Chan, or Slice.
func (v Value) Cap() int {
@@ -586,7 +676,7 @@ func (v Value) Elem() Value {
switch k {
case Interface:
var (
- typ *commonType
+ typ *rtype
val unsafe.Pointer
)
if v.typ.NumMethod() == 0 {
@@ -595,7 +685,7 @@ func (v Value) Elem() Value {
// nil interface value
return Value{}
}
- typ = toCommonType(eface.typ)
+ typ = eface.typ
val = unsafe.Pointer(eface.word)
} else {
iface := (*nonEmptyInterface)(v.val)
@@ -603,7 +693,7 @@ func (v Value) Elem() Value {
// nil interface value
return Value{}
}
- typ = toCommonType(iface.itab.typ)
+ typ = iface.itab.typ
val = unsafe.Pointer(iface.word)
}
fl := v.flag & flagRO
@@ -623,7 +713,7 @@ func (v Value) Elem() Value {
return Value{}
}
tt := (*ptrType)(unsafe.Pointer(v.typ))
- typ := toCommonType(tt.elem)
+ typ := tt.elem
fl := v.flag&flagRO | flagIndir | flagAddr
fl |= flag(typ.Kind() << flagKindShift)
return Value{typ, val, fl}
@@ -640,7 +730,7 @@ func (v Value) Field(i int) Value {
panic("reflect: Field index out of range")
}
field := &tt.fields[i]
- typ := toCommonType(field.typ)
+ typ := field.typ
// Inherit permission bits from v.
fl := v.flag & (flagRO | flagIndir | flagAddr)
@@ -723,8 +813,10 @@ func (v Value) Float() float64 {
panic(&ValueError{"reflect.Value.Float", k})
}
+var uint8Type = TypeOf(uint8(0)).(*rtype)
+
// Index returns v's i'th element.
-// It panics if v's Kind is not Array or Slice or i is out of range.
+// It panics if v's Kind is not Array, Slice, or String or i is out of range.
func (v Value) Index(i int) Value {
k := v.kind()
switch k {
@@ -733,7 +825,7 @@ func (v Value) Index(i int) Value {
if i < 0 || i > int(tt.len) {
panic("reflect: array index out of range")
}
- typ := toCommonType(tt.elem)
+ typ := tt.elem
fl := v.flag & (flagRO | flagIndir | flagAddr) // bits same as overall array
fl |= flag(typ.Kind()) << flagKindShift
offset := uintptr(i) * typ.size
@@ -761,10 +853,19 @@ func (v Value) Index(i int) Value {
panic("reflect: slice index out of range")
}
tt := (*sliceType)(unsafe.Pointer(v.typ))
- typ := toCommonType(tt.elem)
+ typ := tt.elem
fl |= flag(typ.Kind()) << flagKindShift
val := unsafe.Pointer(s.Data + uintptr(i)*typ.size)
return Value{typ, val, fl}
+
+ case String:
+ fl := v.flag&flagRO | flag(Uint8<<flagKindShift)
+ s := (*StringHeader)(v.val)
+ if i < 0 || i >= s.Len {
+ panic("reflect: string index out of range")
+ }
+ val := *(*byte)(unsafe.Pointer(s.Data + uintptr(i)))
+ return Value{uint8Type, unsafe.Pointer(uintptr(val)), fl}
}
panic(&ValueError{"reflect.Value.Index", k})
}
@@ -826,7 +927,7 @@ func valueInterface(v Value, safe bool) interface{} {
if safe && v.flag&flagRO != 0 {
// Do not allow access to unexported values via Interface,
- // because they might be pointers that should not be
+ // because they might be pointers that should not be
// writable or methods or function that should not be callable.
panic("reflect.Value.Interface: cannot return value obtained from unexported field or method")
}
@@ -846,7 +947,7 @@ func valueInterface(v Value, safe bool) interface{} {
// Non-interface value.
var eface emptyInterface
- eface.typ = v.typ.runtimeType()
+ eface.typ = v.typ
eface.word = v.iword()
if v.flag&flagIndir != 0 && v.typ.size > ptrSize {
@@ -919,9 +1020,9 @@ func (v Value) Len() int {
tt := (*arrayType)(unsafe.Pointer(v.typ))
return int(tt.len)
case Chan:
- return int(chanlen(v.iword()))
+ return chanlen(v.iword())
case Map:
- return int(maplen(v.iword()))
+ return maplen(v.iword())
case Slice:
// Slice is bigger than a word; assume flagIndir.
return (*SliceHeader)(v.val).Len
@@ -947,13 +1048,13 @@ func (v Value) MapIndex(key Value) Value {
// considered unexported. This is consistent with the
// behavior for structs, which allow read but not write
// of unexported fields.
- key = key.assignTo("reflect.Value.MapIndex", toCommonType(tt.key), nil)
+ key = key.assignTo("reflect.Value.MapIndex", tt.key, nil)
- word, ok := mapaccess(v.typ.runtimeType(), v.iword(), key.iword())
+ word, ok := mapaccess(v.typ, v.iword(), key.iword())
if !ok {
return Value{}
}
- typ := toCommonType(tt.elem)
+ typ := tt.elem
fl := (v.flag | key.flag) & flagRO
if typ.size > ptrSize {
fl |= flagIndir
@@ -969,7 +1070,7 @@ func (v Value) MapIndex(key Value) Value {
func (v Value) MapKeys() []Value {
v.mustBe(Map)
tt := (*mapType)(unsafe.Pointer(v.typ))
- keyType := toCommonType(tt.key)
+ keyType := tt.key
fl := v.flag & flagRO
fl |= flag(keyType.Kind()) << flagKindShift
@@ -978,11 +1079,11 @@ func (v Value) MapKeys() []Value {
}
m := v.iword()
- mlen := int32(0)
+ mlen := int(0)
if m != nil {
mlen = maplen(m)
}
- it := mapiterinit(v.typ.runtimeType(), m)
+ it := mapiterinit(v.typ, m)
a := make([]Value, mlen)
var i int
for i = 0; i < len(a); i++ {
@@ -1081,7 +1182,7 @@ func overflowFloat32(x float64) bool {
if x < 0 {
x = -x
}
- return math.MaxFloat32 <= x && x <= math.MaxFloat64
+ return math.MaxFloat32 < x && x <= math.MaxFloat64
}
// OverflowInt returns true if the int64 x cannot be represented by v's type.
@@ -1115,18 +1216,35 @@ func (v Value) OverflowUint(x uint64) bool {
// code using reflect cannot obtain unsafe.Pointers
// without importing the unsafe package explicitly.
// It panics if v's Kind is not Chan, Func, Map, Ptr, Slice, or UnsafePointer.
+//
+// If v's Kind is Func, the returned pointer is an underlying
+// code pointer, but not necessarily enough to identify a
+// single function uniquely. The only guarantee is that the
+// result is zero if and only if v is a nil func Value.
func (v Value) Pointer() uintptr {
k := v.kind()
switch k {
- case Chan, Func, Map, Ptr, UnsafePointer:
- if k == Func && v.flag&flagMethod != 0 {
+ case Chan, Map, Ptr, UnsafePointer:
+ p := v.val
+ if v.flag&flagIndir != 0 {
+ p = *(*unsafe.Pointer)(p)
+ }
+ return uintptr(p)
+ case Func:
+ if v.flag&flagMethod != 0 {
panic("reflect.Value.Pointer of method Value")
}
p := v.val
if v.flag&flagIndir != 0 {
p = *(*unsafe.Pointer)(p)
}
+ // Non-nil func value points at data block.
+ // First word of data block is actual code.
+ if p != nil {
+ p = *(*unsafe.Pointer)(p)
+ }
return uintptr(p)
+
case Slice:
return (*SliceHeader)(v.val).Data
}
@@ -1151,9 +1269,9 @@ func (v Value) recv(nb bool) (val Value, ok bool) {
if ChanDir(tt.dir)&RecvDir == 0 {
panic("recv on send-only channel")
}
- word, selected, ok := chanrecv(v.typ.runtimeType(), v.iword(), nb)
+ word, selected, ok := chanrecv(v.typ, v.iword(), nb)
if selected {
- typ := toCommonType(tt.elem)
+ typ := tt.elem
fl := flag(typ.Kind()) << flagKindShift
if typ.size > ptrSize {
fl |= flagIndir
@@ -1180,8 +1298,8 @@ func (v Value) send(x Value, nb bool) (selected bool) {
panic("send on recv-only channel")
}
x.mustBeExported()
- x = x.assignTo("reflect.Value.Send", toCommonType(tt.elem), nil)
- return chansend(v.typ.runtimeType(), v.iword(), x.iword(), nb)
+ x = x.assignTo("reflect.Value.Send", tt.elem, nil)
+ return chansend(v.typ, v.iword(), x.iword(), nb)
}
// Set assigns x to the value v.
@@ -1221,6 +1339,17 @@ func (v Value) SetBytes(x []byte) {
*(*[]byte)(v.val) = x
}
+// setRunes sets v's underlying value.
+// It panics if v's underlying value is not a slice of runes (int32s).
+func (v Value) setRunes(x []rune) {
+ v.mustBeAssignable()
+ v.mustBe(Slice)
+ if v.typ.Elem().Kind() != Int32 {
+ panic("reflect.Value.setRunes of non-rune slice")
+ }
+ *(*[]rune)(v.val) = x
+}
+
// SetComplex sets v's underlying value to x.
// It panics if v's Kind is not Complex64 or Complex128, or if CanSet() is false.
func (v Value) SetComplex(x complex128) {
@@ -1292,12 +1421,12 @@ func (v Value) SetMapIndex(key, val Value) {
v.mustBeExported()
key.mustBeExported()
tt := (*mapType)(unsafe.Pointer(v.typ))
- key = key.assignTo("reflect.Value.SetMapIndex", toCommonType(tt.key), nil)
+ key = key.assignTo("reflect.Value.SetMapIndex", tt.key, nil)
if val.typ != nil {
val.mustBeExported()
- val = val.assignTo("reflect.Value.SetMapIndex", toCommonType(tt.elem), nil)
+ val = val.assignTo("reflect.Value.SetMapIndex", tt.elem, nil)
}
- mapassign(v.typ.runtimeType(), v.iword(), key.iword(), val.iword(), val.typ != nil)
+ mapassign(v.typ, v.iword(), key.iword(), val.iword(), val.typ != nil)
}
// SetUint sets v's underlying value to x.
@@ -1339,7 +1468,7 @@ func (v Value) SetString(x string) {
}
// Slice returns a slice of v.
-// It panics if v's Kind is not Array or Slice.
+// It panics if v's Kind is not Array, Slice or String, or if v is an unaddressable array.
func (v Value) Slice(beg, end int) Value {
var (
cap int
@@ -1349,31 +1478,44 @@ func (v Value) Slice(beg, end int) Value {
switch k := v.kind(); k {
default:
panic(&ValueError{"reflect.Value.Slice", k})
+
case Array:
if v.flag&flagAddr == 0 {
panic("reflect.Value.Slice: slice of unaddressable array")
}
tt := (*arrayType)(unsafe.Pointer(v.typ))
cap = int(tt.len)
- typ = (*sliceType)(unsafe.Pointer(toCommonType(tt.slice)))
+ typ = (*sliceType)(unsafe.Pointer(tt.slice))
base = v.val
+
case Slice:
typ = (*sliceType)(unsafe.Pointer(v.typ))
s := (*SliceHeader)(v.val)
base = unsafe.Pointer(s.Data)
cap = s.Cap
+ case String:
+ s := (*StringHeader)(v.val)
+ if beg < 0 || end < beg || end > s.Len {
+ panic("reflect.Value.Slice: string slice index out of bounds")
+ }
+ var x string
+ val := (*StringHeader)(unsafe.Pointer(&x))
+ val.Data = s.Data + uintptr(beg)
+ val.Len = end - beg
+ return Value{v.typ, unsafe.Pointer(&x), v.flag}
}
+
if beg < 0 || end < beg || end > cap {
panic("reflect.Value.Slice: slice index out of bounds")
}
// Declare slice so that gc can see the base pointer in it.
- var x []byte
+ var x []unsafe.Pointer
// Reinterpret as *SliceHeader to edit.
s := (*SliceHeader)(unsafe.Pointer(&x))
- s.Data = uintptr(base) + uintptr(beg)*toCommonType(typ.elem).Size()
+ s.Data = uintptr(base) + uintptr(beg)*typ.elem.Size()
s.Len = end - beg
s.Cap = cap - beg
@@ -1439,7 +1581,7 @@ func (v Value) Type() Type {
panic("reflect: broken Value")
}
m := &tt.methods[i]
- return toCommonType(m.typ)
+ return m.typ
}
// Method on concrete type.
ut := v.typ.uncommon()
@@ -1447,7 +1589,7 @@ func (v Value) Type() Type {
panic("reflect: broken Value")
}
m := &ut.methods[i]
- return toCommonType(m.mtyp)
+ return m.mtyp
}
// Uint returns v's underlying value, as a uint64.
@@ -1618,13 +1760,148 @@ func Copy(dst, src Value) int {
return n
}
+// A runtimeSelect is a single case passed to rselect.
+// This must match ../runtime/chan.c:/runtimeSelect
+type runtimeSelect struct {
+ dir uintptr // 0, SendDir, or RecvDir
+ typ *rtype // channel type
+ ch iword // interface word for channel
+ val iword // interface word for value (for SendDir)
+}
+
+// rselect runs a select. It returns the index of the chosen case,
+// and if the case was a receive, the interface word of the received
+// value and the conventional OK bool to indicate whether the receive
+// corresponds to a sent value.
+func rselect([]runtimeSelect) (chosen int, recv iword, recvOK bool)
+
+// A SelectDir describes the communication direction of a select case.
+type SelectDir int
+
+// NOTE: These values must match ../runtime/chan.c:/SelectDir.
+
+const (
+ _ SelectDir = iota
+ SelectSend // case Chan <- Send
+ SelectRecv // case <-Chan:
+ SelectDefault // default
+)
+
+// A SelectCase describes a single case in a select operation.
+// The kind of case depends on Dir, the communication direction.
+//
+// If Dir is SelectDefault, the case represents a default case.
+// Chan and Send must be zero Values.
+//
+// If Dir is SelectSend, the case represents a send operation.
+// Normally Chan's underlying value must be a channel, and Send's underlying value must be
+// assignable to the channel's element type. As a special case, if Chan is a zero Value,
+// then the case is ignored, and the field Send will also be ignored and may be either zero
+// or non-zero.
+//
+// If Dir is SelectRecv, the case represents a receive operation.
+// Normally Chan's underlying value must be a channel and Send must be a zero Value.
+// If Chan is a zero Value, then the case is ignored, but Send must still be a zero Value.
+// When a receive operation is selected, the received Value is returned by Select.
+//
+type SelectCase struct {
+ Dir SelectDir // direction of case
+ Chan Value // channel to use (for send or receive)
+ Send Value // value to send (for send)
+}
+
+// Select executes a select operation described by the list of cases.
+// Like the Go select statement, it blocks until at least one of the cases
+// can proceed, makes a uniform pseudo-random choice,
+// and then executes that case. It returns the index of the chosen case
+// and, if that case was a receive operation, the value received and a
+// boolean indicating whether the value corresponds to a send on the channel
+// (as opposed to a zero value received because the channel is closed).
+func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) {
+ // NOTE: Do not trust that caller is not modifying cases data underfoot.
+ // The range is safe because the caller cannot modify our copy of the len
+ // and each iteration makes its own copy of the value c.
+ runcases := make([]runtimeSelect, len(cases))
+ haveDefault := false
+ for i, c := range cases {
+ rc := &runcases[i]
+ rc.dir = uintptr(c.Dir)
+ switch c.Dir {
+ default:
+ panic("reflect.Select: invalid Dir")
+
+ case SelectDefault: // default
+ if haveDefault {
+ panic("reflect.Select: multiple default cases")
+ }
+ haveDefault = true
+ if c.Chan.IsValid() {
+ panic("reflect.Select: default case has Chan value")
+ }
+ if c.Send.IsValid() {
+ panic("reflect.Select: default case has Send value")
+ }
+
+ case SelectSend:
+ ch := c.Chan
+ if !ch.IsValid() {
+ break
+ }
+ ch.mustBe(Chan)
+ ch.mustBeExported()
+ tt := (*chanType)(unsafe.Pointer(ch.typ))
+ if ChanDir(tt.dir)&SendDir == 0 {
+ panic("reflect.Select: SendDir case using recv-only channel")
+ }
+ rc.ch = ch.iword()
+ rc.typ = &tt.rtype
+ v := c.Send
+ if !v.IsValid() {
+ panic("reflect.Select: SendDir case missing Send value")
+ }
+ v.mustBeExported()
+ v = v.assignTo("reflect.Select", tt.elem, nil)
+ rc.val = v.iword()
+
+ case SelectRecv:
+ if c.Send.IsValid() {
+ panic("reflect.Select: RecvDir case has Send value")
+ }
+ ch := c.Chan
+ if !ch.IsValid() {
+ break
+ }
+ ch.mustBe(Chan)
+ ch.mustBeExported()
+ tt := (*chanType)(unsafe.Pointer(ch.typ))
+ rc.typ = &tt.rtype
+ if ChanDir(tt.dir)&RecvDir == 0 {
+ panic("reflect.Select: RecvDir case using send-only channel")
+ }
+ rc.ch = ch.iword()
+ }
+ }
+
+ chosen, word, recvOK := rselect(runcases)
+ if runcases[chosen].dir == uintptr(SelectRecv) {
+ tt := (*chanType)(unsafe.Pointer(runcases[chosen].typ))
+ typ := tt.elem
+ fl := flag(typ.Kind()) << flagKindShift
+ if typ.size > ptrSize {
+ fl |= flagIndir
+ }
+ recv = Value{typ, unsafe.Pointer(word), fl}
+ }
+ return chosen, recv, recvOK
+}
+
/*
* constructors
*/
// implemented in package runtime
-func unsafe_New(Type) unsafe.Pointer
-func unsafe_NewArray(Type, int) unsafe.Pointer
+func unsafe_New(*rtype) unsafe.Pointer
+func unsafe_NewArray(*rtype, int) unsafe.Pointer
// MakeSlice creates a new zero-initialized slice value
// for the specified slice type, length, and capacity.
@@ -1643,11 +1920,11 @@ func MakeSlice(typ Type, len, cap int) Value {
}
// Declare slice so that gc can see the base pointer in it.
- var x []byte
+ var x []unsafe.Pointer
// Reinterpret as *SliceHeader to edit.
s := (*SliceHeader)(unsafe.Pointer(&x))
- s.Data = uintptr(unsafe_NewArray(typ.Elem(), cap))
+ s.Data = uintptr(unsafe_NewArray(typ.Elem().(*rtype), cap))
s.Len = len
s.Cap = cap
@@ -1665,7 +1942,7 @@ func MakeChan(typ Type, buffer int) Value {
if typ.ChanDir() != BothDir {
panic("reflect.MakeChan: unidirectional channel type")
}
- ch := makechan(typ.runtimeType(), uint32(buffer))
+ ch := makechan(typ.(*rtype), uint64(buffer))
return Value{typ.common(), unsafe.Pointer(ch), flag(Chan) << flagKindShift}
}
@@ -1674,7 +1951,7 @@ func MakeMap(typ Type) Value {
if typ.Kind() != Map {
panic("reflect.MakeMap of non-map type")
}
- m := makemap(typ.runtimeType())
+ m := makemap(typ.(*rtype))
return Value{typ.common(), unsafe.Pointer(m), flag(Map) << flagKindShift}
}
@@ -1705,7 +1982,7 @@ func ValueOf(i interface{}) Value {
// For an interface value with the noAddr bit set,
// the representation is identical to an empty interface.
eface := *(*emptyInterface)(unsafe.Pointer(&i))
- typ := toCommonType(eface.typ)
+ typ := eface.typ
fl := flag(typ.Kind()) << flagKindShift
if typ.size > ptrSize {
fl |= flagIndir
@@ -1713,10 +1990,11 @@ func ValueOf(i interface{}) Value {
return Value{typ, unsafe.Pointer(eface.word), fl}
}
-// Zero returns a Value representing a zero value for the specified type.
+// Zero returns a Value representing the zero value for the specified type.
// The result is different from the zero value of the Value struct,
// which represents no value at all.
// For example, Zero(TypeOf(42)) returns a Value with Kind Int and value 0.
+// The returned value is neither addressable nor settable.
func Zero(typ Type) Value {
if typ == nil {
panic("reflect: Zero(nil)")
@@ -1726,7 +2004,7 @@ func Zero(typ Type) Value {
if t.size <= ptrSize {
return Value{t, nil, fl}
}
- return Value{t, unsafe_New(typ), fl | flagIndir}
+ return Value{t, unsafe_New(typ.(*rtype)), fl | flagIndir}
}
// New returns a Value representing a pointer to a new zero value
@@ -1735,7 +2013,7 @@ func New(typ Type) Value {
if typ == nil {
panic("reflect: New(nil)")
}
- ptr := unsafe_New(typ)
+ ptr := unsafe_New(typ.(*rtype))
fl := flag(Ptr) << flagKindShift
return Value{typ.common().ptrTo(), ptr, fl}
}
@@ -1750,7 +2028,7 @@ func NewAt(typ Type, p unsafe.Pointer) Value {
// assignTo returns a value v that can be assigned directly to typ.
// It panics if v is not assignable to typ.
// For a conversion to an interface type, target is a suggested scratch space to use.
-func (v Value) assignTo(context string, dst *commonType, target *interface{}) Value {
+func (v Value) assignTo(context string, dst *rtype, target *interface{}) Value {
if v.flag&flagMethod != 0 {
panic(context + ": cannot assign method value to type " + dst.String())
}
@@ -1772,7 +2050,7 @@ func (v Value) assignTo(context string, dst *commonType, target *interface{}) Va
if dst.NumMethod() == 0 {
*target = x
} else {
- ifaceE2I(dst.runtimeType(), x, unsafe.Pointer(target))
+ ifaceE2I(dst, x, unsafe.Pointer(target))
}
return Value{dst, unsafe.Pointer(target), flagIndir | flag(Interface)<<flagKindShift}
}
@@ -1781,24 +2059,320 @@ func (v Value) assignTo(context string, dst *commonType, target *interface{}) Va
panic(context + ": value of type " + v.typ.String() + " is not assignable to type " + dst.String())
}
+// Convert returns the value v converted to type t.
+// If the usual Go conversion rules do not allow conversion
+// of the value v to type t, Convert panics.
+func (v Value) Convert(t Type) Value {
+ if v.flag&flagMethod != 0 {
+ panic("reflect.Value.Convert: cannot convert method values")
+ }
+ op := convertOp(t.common(), v.typ)
+ if op == nil {
+ panic("reflect.Value.Convert: value of type " + v.typ.String() + " cannot be converted to type " + t.String())
+ }
+ return op(v, t)
+}
+
+// convertOp returns the function to convert a value of type src
+// to a value of type dst. If the conversion is illegal, convertOp returns nil.
+func convertOp(dst, src *rtype) func(Value, Type) Value {
+ switch src.Kind() {
+ case Int, Int8, Int16, Int32, Int64:
+ switch dst.Kind() {
+ case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
+ return cvtInt
+ case Float32, Float64:
+ return cvtIntFloat
+ case String:
+ return cvtIntString
+ }
+
+ case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
+ switch dst.Kind() {
+ case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
+ return cvtUint
+ case Float32, Float64:
+ return cvtUintFloat
+ case String:
+ return cvtUintString
+ }
+
+ case Float32, Float64:
+ switch dst.Kind() {
+ case Int, Int8, Int16, Int32, Int64:
+ return cvtFloatInt
+ case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
+ return cvtFloatUint
+ case Float32, Float64:
+ return cvtFloat
+ }
+
+ case Complex64, Complex128:
+ switch dst.Kind() {
+ case Complex64, Complex128:
+ return cvtComplex
+ }
+
+ case String:
+ if dst.Kind() == Slice && dst.Elem().PkgPath() == "" {
+ switch dst.Elem().Kind() {
+ case Uint8:
+ return cvtStringBytes
+ case Int32:
+ return cvtStringRunes
+ }
+ }
+
+ case Slice:
+ if dst.Kind() == String && src.Elem().PkgPath() == "" {
+ switch src.Elem().Kind() {
+ case Uint8:
+ return cvtBytesString
+ case Int32:
+ return cvtRunesString
+ }
+ }
+ }
+
+ // dst and src have same underlying type.
+ if haveIdenticalUnderlyingType(dst, src) {
+ return cvtDirect
+ }
+
+ // dst and src are unnamed pointer types with same underlying base type.
+ if dst.Kind() == Ptr && dst.Name() == "" &&
+ src.Kind() == Ptr && src.Name() == "" &&
+ haveIdenticalUnderlyingType(dst.Elem().common(), src.Elem().common()) {
+ return cvtDirect
+ }
+
+ if implements(dst, src) {
+ if src.Kind() == Interface {
+ return cvtI2I
+ }
+ return cvtT2I
+ }
+
+ return nil
+}
+
+// makeInt returns a Value of type t equal to bits (possibly truncated),
+// where t is a signed or unsigned int type.
+func makeInt(f flag, bits uint64, t Type) Value {
+ typ := t.common()
+ if typ.size > ptrSize {
+ // Assume ptrSize >= 4, so this must be uint64.
+ ptr := unsafe_New(typ)
+ *(*uint64)(unsafe.Pointer(ptr)) = bits
+ return Value{typ, ptr, f | flag(typ.Kind())<<flagKindShift}
+ }
+ var w iword
+ switch typ.size {
+ case 1:
+ *(*uint8)(unsafe.Pointer(&w)) = uint8(bits)
+ case 2:
+ *(*uint16)(unsafe.Pointer(&w)) = uint16(bits)
+ case 4:
+ *(*uint32)(unsafe.Pointer(&w)) = uint32(bits)
+ case 8:
+ *(*uint64)(unsafe.Pointer(&w)) = uint64(bits)
+ }
+ return Value{typ, unsafe.Pointer(w), f | flag(typ.Kind())<<flagKindShift}
+}
+
+// makeFloat returns a Value of type t equal to v (possibly truncated to float32),
+// where t is a float32 or float64 type.
+func makeFloat(f flag, v float64, t Type) Value {
+ typ := t.common()
+ if typ.size > ptrSize {
+ // Assume ptrSize >= 4, so this must be float64.
+ ptr := unsafe_New(typ)
+ *(*float64)(unsafe.Pointer(ptr)) = v
+ return Value{typ, ptr, f | flag(typ.Kind())<<flagKindShift}
+ }
+
+ var w iword
+ switch typ.size {
+ case 4:
+ *(*float32)(unsafe.Pointer(&w)) = float32(v)
+ case 8:
+ *(*float64)(unsafe.Pointer(&w)) = v
+ }
+ return Value{typ, unsafe.Pointer(w), f | flag(typ.Kind())<<flagKindShift}
+}
+
+// makeComplex returns a Value of type t equal to v (possibly truncated to complex64),
+// where t is a complex64 or complex128 type.
+func makeComplex(f flag, v complex128, t Type) Value {
+ typ := t.common()
+ if typ.size > ptrSize {
+ ptr := unsafe_New(typ)
+ switch typ.size {
+ case 8:
+ *(*complex64)(unsafe.Pointer(ptr)) = complex64(v)
+ case 16:
+ *(*complex128)(unsafe.Pointer(ptr)) = v
+ }
+ return Value{typ, ptr, f | flag(typ.Kind())<<flagKindShift}
+ }
+
+ // Assume ptrSize <= 8 so this must be complex64.
+ var w iword
+ *(*complex64)(unsafe.Pointer(&w)) = complex64(v)
+ return Value{typ, unsafe.Pointer(w), f | flag(typ.Kind())<<flagKindShift}
+}
+
+func makeString(f flag, v string, t Type) Value {
+ ret := New(t).Elem()
+ ret.SetString(v)
+ ret.flag = ret.flag&^flagAddr | f
+ return ret
+}
+
+func makeBytes(f flag, v []byte, t Type) Value {
+ ret := New(t).Elem()
+ ret.SetBytes(v)
+ ret.flag = ret.flag&^flagAddr | f
+ return ret
+}
+
+func makeRunes(f flag, v []rune, t Type) Value {
+ ret := New(t).Elem()
+ ret.setRunes(v)
+ ret.flag = ret.flag&^flagAddr | f
+ return ret
+}
+
+// These conversion functions are returned by convertOp
+// for classes of conversions. For example, the first function, cvtInt,
+// takes any value v of signed int type and returns the value converted
+// to type t, where t is any signed or unsigned int type.
+
+// convertOp: intXX -> [u]intXX
+func cvtInt(v Value, t Type) Value {
+ return makeInt(v.flag&flagRO, uint64(v.Int()), t)
+}
+
+// convertOp: uintXX -> [u]intXX
+func cvtUint(v Value, t Type) Value {
+ return makeInt(v.flag&flagRO, v.Uint(), t)
+}
+
+// convertOp: floatXX -> intXX
+func cvtFloatInt(v Value, t Type) Value {
+ return makeInt(v.flag&flagRO, uint64(int64(v.Float())), t)
+}
+
+// convertOp: floatXX -> uintXX
+func cvtFloatUint(v Value, t Type) Value {
+ return makeInt(v.flag&flagRO, uint64(v.Float()), t)
+}
+
+// convertOp: intXX -> floatXX
+func cvtIntFloat(v Value, t Type) Value {
+ return makeFloat(v.flag&flagRO, float64(v.Int()), t)
+}
+
+// convertOp: uintXX -> floatXX
+func cvtUintFloat(v Value, t Type) Value {
+ return makeFloat(v.flag&flagRO, float64(v.Uint()), t)
+}
+
+// convertOp: floatXX -> floatXX
+func cvtFloat(v Value, t Type) Value {
+ return makeFloat(v.flag&flagRO, v.Float(), t)
+}
+
+// convertOp: complexXX -> complexXX
+func cvtComplex(v Value, t Type) Value {
+ return makeComplex(v.flag&flagRO, v.Complex(), t)
+}
+
+// convertOp: intXX -> string
+func cvtIntString(v Value, t Type) Value {
+ return makeString(v.flag&flagRO, string(v.Int()), t)
+}
+
+// convertOp: uintXX -> string
+func cvtUintString(v Value, t Type) Value {
+ return makeString(v.flag&flagRO, string(v.Uint()), t)
+}
+
+// convertOp: []byte -> string
+func cvtBytesString(v Value, t Type) Value {
+ return makeString(v.flag&flagRO, string(v.Bytes()), t)
+}
+
+// convertOp: string -> []byte
+func cvtStringBytes(v Value, t Type) Value {
+ return makeBytes(v.flag&flagRO, []byte(v.String()), t)
+}
+
+// convertOp: []rune -> string
+func cvtRunesString(v Value, t Type) Value {
+ return makeString(v.flag&flagRO, string(v.runes()), t)
+}
+
+// convertOp: string -> []rune
+func cvtStringRunes(v Value, t Type) Value {
+ return makeRunes(v.flag&flagRO, []rune(v.String()), t)
+}
+
+// convertOp: direct copy
+func cvtDirect(v Value, typ Type) Value {
+ f := v.flag
+ t := typ.common()
+ val := v.val
+ if f&flagAddr != 0 {
+ // indirect, mutable word - make a copy
+ ptr := unsafe_New(t)
+ memmove(ptr, val, t.size)
+ val = ptr
+ f &^= flagAddr
+ }
+ return Value{t, val, v.flag&flagRO | f}
+}
+
+// convertOp: concrete -> interface
+func cvtT2I(v Value, typ Type) Value {
+ target := new(interface{})
+ x := valueInterface(v, false)
+ if typ.NumMethod() == 0 {
+ *target = x
+ } else {
+ ifaceE2I(typ.(*rtype), x, unsafe.Pointer(target))
+ }
+ return Value{typ.common(), unsafe.Pointer(target), v.flag&flagRO | flagIndir | flag(Interface)<<flagKindShift}
+}
+
+// convertOp: interface -> interface
+func cvtI2I(v Value, typ Type) Value {
+ if v.IsNil() {
+ ret := Zero(typ)
+ ret.flag |= v.flag & flagRO
+ return ret
+ }
+ return cvtT2I(v.Elem(), typ)
+}
+
// implemented in ../pkg/runtime
-func chancap(ch iword) int32
+func chancap(ch iword) int
func chanclose(ch iword)
-func chanlen(ch iword) int32
-func chanrecv(t *runtimeType, ch iword, nb bool) (val iword, selected, received bool)
-func chansend(t *runtimeType, ch iword, val iword, nb bool) bool
-
-func makechan(typ *runtimeType, size uint32) (ch iword)
-func makemap(t *runtimeType) (m iword)
-func mapaccess(t *runtimeType, m iword, key iword) (val iword, ok bool)
-func mapassign(t *runtimeType, m iword, key, val iword, ok bool)
-func mapiterinit(t *runtimeType, m iword) *byte
+func chanlen(ch iword) int
+func chanrecv(t *rtype, ch iword, nb bool) (val iword, selected, received bool)
+func chansend(t *rtype, ch iword, val iword, nb bool) bool
+
+func makechan(typ *rtype, size uint64) (ch iword)
+func makemap(t *rtype) (m iword)
+func mapaccess(t *rtype, m iword, key iword) (val iword, ok bool)
+func mapassign(t *rtype, m iword, key, val iword, ok bool)
+func mapiterinit(t *rtype, m iword) *byte
func mapiterkey(it *byte) (key iword, ok bool)
func mapiternext(it *byte)
-func maplen(m iword) int32
+func maplen(m iword) int
func call(fn, arg unsafe.Pointer, n uint32)
-func ifaceE2I(t *runtimeType, src interface{}, dst unsafe.Pointer)
+func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer)
// Dummy annotation marking that the value x escapes,
// for use in cases where the reflect code is so clever that
diff --git a/src/pkg/regexp/all_test.go b/src/pkg/regexp/all_test.go
index f7b41a674..9c4d64f58 100644
--- a/src/pkg/regexp/all_test.go
+++ b/src/pkg/regexp/all_test.go
@@ -5,6 +5,7 @@
package regexp
import (
+ "reflect"
"strings"
"testing"
)
@@ -29,53 +30,52 @@ var good_re = []string{
`\!\\`,
}
-/*
type stringError struct {
re string
- err error
+ err string
}
var bad_re = []stringError{
- {`*`, ErrBareClosure},
- {`+`, ErrBareClosure},
- {`?`, ErrBareClosure},
- {`(abc`, ErrUnmatchedLpar},
- {`abc)`, ErrUnmatchedRpar},
- {`x[a-z`, ErrUnmatchedLbkt},
- {`abc]`, ErrUnmatchedRbkt},
- {`[z-a]`, ErrBadRange},
- {`abc\`, ErrExtraneousBackslash},
- {`a**`, ErrBadClosure},
- {`a*+`, ErrBadClosure},
- {`a??`, ErrBadClosure},
- {`\x`, ErrBadBackslash},
-}
-*/
-
-func compileTest(t *testing.T, expr string, error error) *Regexp {
+ {`*`, "missing argument to repetition operator: `*`"},
+ {`+`, "missing argument to repetition operator: `+`"},
+ {`?`, "missing argument to repetition operator: `?`"},
+ {`(abc`, "missing closing ): `(abc`"},
+ {`abc)`, "unexpected ): `abc)`"},
+ {`x[a-z`, "missing closing ]: `[a-z`"},
+ {`[z-a]`, "invalid character class range: `z-a`"},
+ {`abc\`, "trailing backslash at end of expression"},
+ {`a**`, "invalid nested repetition operator: `**`"},
+ {`a*+`, "invalid nested repetition operator: `*+`"},
+ {`\x`, "invalid escape sequence: `\\x`"},
+}
+
+func compileTest(t *testing.T, expr string, error string) *Regexp {
re, err := Compile(expr)
- if err != error {
+ if error == "" && err != nil {
t.Error("compiling `", expr, "`; unexpected error: ", err.Error())
}
+ if error != "" && err == nil {
+ t.Error("compiling `", expr, "`; missing error")
+ } else if error != "" && !strings.Contains(err.Error(), error) {
+ t.Error("compiling `", expr, "`; wrong error: ", err.Error(), "; want ", error)
+ }
return re
}
func TestGoodCompile(t *testing.T) {
for i := 0; i < len(good_re); i++ {
- compileTest(t, good_re[i], nil)
+ compileTest(t, good_re[i], "")
}
}
-/*
func TestBadCompile(t *testing.T) {
for i := 0; i < len(bad_re); i++ {
compileTest(t, bad_re[i].re, bad_re[i].err)
}
}
-*/
func matchTest(t *testing.T, test *FindTest) {
- re := compileTest(t, test.pat, nil)
+ re := compileTest(t, test.pat, "")
if re == nil {
return
}
@@ -196,6 +196,10 @@ var replaceTests = []ReplaceTest{
{"a+", "${oops", "aaa", "${oops"},
{"a+", "$$", "aaa", "$"},
{"a+", "$", "aaa", "$"},
+
+ // Substitution when subexpression isn't found
+ {"(x)?", "$1", "123", "123"},
+ {"abc", "$1", "123", "123"},
}
var replaceLiteralTests = []ReplaceTest{
@@ -416,6 +420,59 @@ func TestSubexp(t *testing.T) {
}
}
+var splitTests = []struct {
+ s string
+ r string
+ n int
+ out []string
+}{
+ {"foo:and:bar", ":", -1, []string{"foo", "and", "bar"}},
+ {"foo:and:bar", ":", 1, []string{"foo:and:bar"}},
+ {"foo:and:bar", ":", 2, []string{"foo", "and:bar"}},
+ {"foo:and:bar", "foo", -1, []string{"", ":and:bar"}},
+ {"foo:and:bar", "bar", -1, []string{"foo:and:", ""}},
+ {"foo:and:bar", "baz", -1, []string{"foo:and:bar"}},
+ {"baabaab", "a", -1, []string{"b", "", "b", "", "b"}},
+ {"baabaab", "a*", -1, []string{"b", "b", "b"}},
+ {"baabaab", "ba*", -1, []string{"", "", "", ""}},
+ {"foobar", "f*b*", -1, []string{"", "o", "o", "a", "r"}},
+ {"foobar", "f+.*b+", -1, []string{"", "ar"}},
+ {"foobooboar", "o{2}", -1, []string{"f", "b", "boar"}},
+ {"a,b,c,d,e,f", ",", 3, []string{"a", "b", "c,d,e,f"}},
+ {"a,b,c,d,e,f", ",", 0, nil},
+ {",", ",", -1, []string{"", ""}},
+ {",,,", ",", -1, []string{"", "", "", ""}},
+ {"", ",", -1, []string{""}},
+ {"", ".*", -1, []string{""}},
+ {"", ".+", -1, []string{""}},
+ {"", "", -1, []string{}},
+ {"foobar", "", -1, []string{"f", "o", "o", "b", "a", "r"}},
+ {"abaabaccadaaae", "a*", 5, []string{"", "b", "b", "c", "cadaaae"}},
+ {":x:y:z:", ":", -1, []string{"", "x", "y", "z", ""}},
+}
+
+func TestSplit(t *testing.T) {
+ for i, test := range splitTests {
+ re, err := Compile(test.r)
+ if err != nil {
+ t.Errorf("#%d: %q: compile error: %s", i, test.r, err.Error())
+ continue
+ }
+
+ split := re.Split(test.s, test.n)
+ if !reflect.DeepEqual(split, test.out) {
+ t.Errorf("#%d: %q: got %q; want %q", i, test.r, split, test.out)
+ }
+
+ if QuoteMeta(test.r) == test.r {
+ strsplit := strings.SplitN(test.s, test.r, test.n)
+ if !reflect.DeepEqual(split, strsplit) {
+ t.Errorf("#%d: Split(%q, %q, %d): regexp vs strings mismatch\nregexp=%q\nstrings=%q", i, test.s, test.r, test.n, split, strsplit)
+ }
+ }
+ }
+}
+
func BenchmarkLiteral(b *testing.B) {
x := strings.Repeat("x", 50) + "y"
b.StopTimer()
diff --git a/src/pkg/regexp/example_test.go b/src/pkg/regexp/example_test.go
new file mode 100644
index 000000000..b0ad9d340
--- /dev/null
+++ b/src/pkg/regexp/example_test.go
@@ -0,0 +1,144 @@
+package regexp_test
+
+import (
+ "fmt"
+ "regexp"
+)
+
+func Example() {
+ // Compile the expression once, usually at init time.
+ // Use raw strings to avoid having to quote the backslashes.
+ var validID = regexp.MustCompile(`^[a-z]+\[[0-9]+\]$`)
+
+ fmt.Println(validID.MatchString("adam[23]"))
+ fmt.Println(validID.MatchString("eve[7]"))
+ fmt.Println(validID.MatchString("Job[48]"))
+ fmt.Println(validID.MatchString("snakey"))
+ // Output:
+ // true
+ // true
+ // false
+ // false
+}
+
+func ExampleMatchString() {
+ matched, err := regexp.MatchString("foo.*", "seafood")
+ fmt.Println(matched, err)
+ matched, err = regexp.MatchString("bar.*", "seafood")
+ fmt.Println(matched, err)
+ matched, err = regexp.MatchString("a(b", "seafood")
+ fmt.Println(matched, err)
+ // Output:
+ // true <nil>
+ // false <nil>
+ // false error parsing regexp: missing closing ): `a(b`
+}
+
+func ExampleRegexp_FindString() {
+ re := regexp.MustCompile("fo.?")
+ fmt.Printf("%q\n", re.FindString("seafood"))
+ fmt.Printf("%q\n", re.FindString("meat"))
+ // Output:
+ // "foo"
+ // ""
+}
+
+func ExampleRegexp_FindStringIndex() {
+ re := regexp.MustCompile("ab?")
+ fmt.Println(re.FindStringIndex("tablett"))
+ fmt.Println(re.FindStringIndex("foo") == nil)
+ // Output:
+ // [1 3]
+ // true
+}
+
+func ExampleRegexp_FindStringSubmatch() {
+ re := regexp.MustCompile("a(x*)b(y|z)c")
+ fmt.Printf("%q\n", re.FindStringSubmatch("-axxxbyc-"))
+ fmt.Printf("%q\n", re.FindStringSubmatch("-abzc-"))
+ // Output:
+ // ["axxxbyc" "xxx" "y"]
+ // ["abzc" "" "z"]
+}
+
+func ExampleRegexp_FindAllString() {
+ re := regexp.MustCompile("a.")
+ fmt.Println(re.FindAllString("paranormal", -1))
+ fmt.Println(re.FindAllString("paranormal", 2))
+ fmt.Println(re.FindAllString("graal", -1))
+ fmt.Println(re.FindAllString("none", -1))
+ // Output:
+ // [ar an al]
+ // [ar an]
+ // [aa]
+ // []
+}
+
+func ExampleRegexp_FindAllStringSubmatch() {
+ re := regexp.MustCompile("a(x*)b")
+ fmt.Printf("%q\n", re.FindAllStringSubmatch("-ab-", -1))
+ fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxb-", -1))
+ fmt.Printf("%q\n", re.FindAllStringSubmatch("-ab-axb-", -1))
+ fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxb-ab-", -1))
+ // Output:
+ // [["ab" ""]]
+ // [["axxb" "xx"]]
+ // [["ab" ""] ["axb" "x"]]
+ // [["axxb" "xx"] ["ab" ""]]
+}
+
+func ExampleRegexp_FindAllStringSubmatchIndex() {
+ re := regexp.MustCompile("a(x*)b")
+ // Indices:
+ // 01234567 012345678
+ // -ab-axb- -axxb-ab-
+ fmt.Println(re.FindAllStringSubmatchIndex("-ab-", -1))
+ fmt.Println(re.FindAllStringSubmatchIndex("-axxb-", -1))
+ fmt.Println(re.FindAllStringSubmatchIndex("-ab-axb-", -1))
+ fmt.Println(re.FindAllStringSubmatchIndex("-axxb-ab-", -1))
+ fmt.Println(re.FindAllStringSubmatchIndex("-foo-", -1))
+ // Output:
+ // [[1 3 2 2]]
+ // [[1 5 2 4]]
+ // [[1 3 2 2] [4 7 5 6]]
+ // [[1 5 2 4] [6 8 7 7]]
+ // []
+}
+
+func ExampleRegexp_ReplaceAllLiteralString() {
+ re := regexp.MustCompile("a(x*)b")
+ fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "T"))
+ fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "$1"))
+ fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "${1}"))
+ // Output:
+ // -T-T-
+ // -$1-$1-
+ // -${1}-${1}-
+}
+
+func ExampleRegexp_ReplaceAllString() {
+ re := regexp.MustCompile("a(x*)b")
+ fmt.Println(re.ReplaceAllString("-ab-axxb-", "T"))
+ fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1"))
+ fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1W"))
+ fmt.Println(re.ReplaceAllString("-ab-axxb-", "${1}W"))
+ // Output:
+ // -T-T-
+ // --xx-
+ // ---
+ // -W-xxW-
+}
+
+func ExampleRegexp_SubexpNames() {
+ re := regexp.MustCompile("(?P<first>[a-zA-Z]+) (?P<last>[a-zA-Z]+)")
+ fmt.Println(re.MatchString("Alan Turing"))
+ fmt.Printf("%q\n", re.SubexpNames())
+ reversed := fmt.Sprintf("${%s} ${%s}", re.SubexpNames()[2], re.SubexpNames()[1])
+ fmt.Println(reversed)
+ fmt.Println(re.ReplaceAllString("Alan Turing", reversed))
+ // Output:
+ // true
+ // ["" "first" "last"]
+ // ${last} ${first}
+ // Turing Alan
+}
diff --git a/src/pkg/regexp/exec_test.go b/src/pkg/regexp/exec_test.go
index e668574a5..9dfaed713 100644
--- a/src/pkg/regexp/exec_test.go
+++ b/src/pkg/regexp/exec_test.go
@@ -69,8 +69,7 @@ func TestRE2Search(t *testing.T) {
func TestRE2Exhaustive(t *testing.T) {
if testing.Short() {
- t.Log("skipping TestRE2Exhaustive during short test")
- return
+ t.Skip("skipping TestRE2Exhaustive during short test")
}
testRE2(t, "testdata/re2-exhaustive.txt.bz2")
}
@@ -90,7 +89,7 @@ func testRE2(t *testing.T, file string) {
txt = f
}
lineno := 0
- r := bufio.NewReader(txt)
+ scanner := bufio.NewScanner(txt)
var (
str []string
input []string
@@ -100,16 +99,8 @@ func testRE2(t *testing.T, file string) {
nfail int
ncase int
)
- for {
- line, err := r.ReadString('\n')
- if err != nil {
- if err == io.EOF {
- break
- }
- t.Fatalf("%s:%d: %v", file, lineno, err)
- }
- line = line[:len(line)-1] // chop \n
- lineno++
+ for lineno := 1; scanner.Scan(); lineno++ {
+ line := scanner.Text()
switch {
case line == "":
t.Fatalf("%s:%d: unexpected blank line", file, lineno)
@@ -205,6 +196,9 @@ func testRE2(t *testing.T, file string) {
t.Fatalf("%s:%d: out of sync: %s\n", file, lineno, line)
}
}
+ if err := scanner.Err(); err != nil {
+ t.Fatalf("%s:%d: %v", file, lineno, err)
+ }
if len(input) != 0 {
t.Fatalf("%s:%d: out of sync: have %d strings left at EOF", file, lineno, len(input))
}
@@ -405,14 +399,14 @@ Reading:
// implementation. If the first character is not [BEASKLP] then the
// specification is a global control line. One or more of [BEASKLP] may be
// specified; the test will be repeated for each mode.
- //
+ //
// B basic BRE (grep, ed, sed)
// E REG_EXTENDED ERE (egrep)
// A REG_AUGMENTED ARE (egrep with negation)
// S REG_SHELL SRE (sh glob)
// K REG_SHELL|REG_AUGMENTED KRE (ksh glob)
// L REG_LITERAL LRE (fgrep)
- //
+ //
// a REG_LEFT|REG_RIGHT implicit ^...$
// b REG_NOTBOL lhs does not match ^
// c REG_COMMENT ignore space and #...\n
@@ -442,23 +436,23 @@ Reading:
// $ expand C \c escapes in fields 2 and 3
// / field 2 is a regsubcomp() expression
// = field 3 is a regdecomp() expression
- //
+ //
// Field 1 control lines:
- //
+ //
// C set LC_COLLATE and LC_CTYPE to locale in field 2
- //
+ //
// ?test ... output field 5 if passed and != EXPECTED, silent otherwise
// &test ... output field 5 if current and previous passed
// |test ... output field 5 if current passed and previous failed
// ; ... output field 2 if previous failed
// {test ... skip if failed until }
// } end of skip
- //
+ //
// : comment comment copied as output NOTE
// :comment:test :comment: ignored
// N[OTE] comment comment copied as output NOTE
// T[EST] comment comment
- //
+ //
// number use number for nmatch (20 by default)
flag := field[0]
switch flag[0] {
@@ -501,7 +495,7 @@ Reading:
// Field 2: the regular expression pattern; SAME uses the pattern from
// the previous specification.
- //
+ //
if field[1] == "SAME" {
field[1] = lastRegexp
}
@@ -707,3 +701,17 @@ func BenchmarkMatchHard_1K(b *testing.B) { benchmark(b, hard, 1<<10) }
func BenchmarkMatchHard_32K(b *testing.B) { benchmark(b, hard, 32<<10) }
func BenchmarkMatchHard_1M(b *testing.B) { benchmark(b, hard, 1<<20) }
func BenchmarkMatchHard_32M(b *testing.B) { benchmark(b, hard, 32<<20) }
+
+func TestLongest(t *testing.T) {
+ re, err := Compile(`a(|b)`)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if g, w := re.FindString("ab"), "a"; g != w {
+ t.Errorf("first match was %q, want %q", g, w)
+ }
+ re.Longest()
+ if g, w := re.FindString("ab"), "ab"; g != w {
+ t.Errorf("longest match was %q, want %q", g, w)
+ }
+}
diff --git a/src/pkg/regexp/regexp.go b/src/pkg/regexp/regexp.go
index 87e6b1c61..3aa16dec6 100644
--- a/src/pkg/regexp/regexp.go
+++ b/src/pkg/regexp/regexp.go
@@ -130,6 +130,14 @@ func CompilePOSIX(expr string) (*Regexp, error) {
return compile(expr, syntax.POSIX, true)
}
+// Longest makes future searches prefer the leftmost-longest match.
+// That is, when matching against text, the regexp returns a match that
+// begins as early as possible in the input (leftmost), and among those
+// it chooses a match that is as long as possible.
+func (re *Regexp) Longest() {
+ re.longest = true
+}
+
func compile(expr string, mode syntax.Flags, longest bool) (*Regexp, error) {
re, err := syntax.Parse(expr, mode)
if err != nil {
@@ -441,7 +449,7 @@ func (re *Regexp) ReplaceAllLiteralString(src, repl string) string {
}
// ReplaceAllStringFunc returns a copy of src in which all matches of the
-// Regexp have been replaced by the return value of of function repl applied
+// Regexp have been replaced by the return value of function repl applied
// to the matched substring. The replacement returned by repl is substituted
// directly, without using Expand.
func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string {
@@ -539,7 +547,7 @@ func (re *Regexp) ReplaceAllLiteral(src, repl []byte) []byte {
}
// ReplaceAllFunc returns a copy of src in which all matches of the
-// Regexp have been replaced by the return value of of function repl applied
+// Regexp have been replaced by the return value of function repl applied
// to the matched byte slice. The replacement returned by repl is substituted
// directly, without using Expand.
func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte {
@@ -686,8 +694,9 @@ func (re *Regexp) FindStringIndex(s string) (loc []int) {
// FindReaderIndex returns a two-element slice of integers defining the
// location of the leftmost match of the regular expression in text read from
-// the RuneReader. The match itself is at s[loc[0]:loc[1]]. A return
-// value of nil indicates no match.
+// the RuneReader. The match text was found in the input stream at
+// byte offset loc[0] through loc[1]-1.
+// A return value of nil indicates no match.
func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int) {
a := re.doExecute(r, nil, "", 0, 2)
if a == nil {
@@ -719,7 +728,7 @@ func (re *Regexp) FindSubmatch(b []byte) [][]byte {
// append, Expand replaces variables in the template with corresponding
// matches drawn from src. The match slice should have been returned by
// FindSubmatchIndex.
-//
+//
// In the template, a variable is denoted by a substring of the form
// $name or ${name}, where name is a non-empty sequence of letters,
// digits, and underscores. A purely numeric name like $1 refers to
@@ -727,10 +736,10 @@ func (re *Regexp) FindSubmatch(b []byte) [][]byte {
// capturing parentheses named with the (?P<name>...) syntax. A
// reference to an out of range or unmatched index or a name that is not
// present in the regular expression is replaced with an empty slice.
-//
+//
// In the $name form, name is taken to be as long as possible: $1x is
// equivalent to ${1x}, not ${1}x, and, $10 is equivalent to ${10}, not ${1}0.
-//
+//
// To insert a literal $ in the output, use $$ in the template.
func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte {
return re.expand(dst, string(template), src, "", match)
@@ -766,7 +775,7 @@ func (re *Regexp) expand(dst []byte, template string, bsrc []byte, src string, m
}
template = rest
if num >= 0 {
- if 2*num+1 < len(match) {
+ if 2*num+1 < len(match) && match[2*num] >= 0 {
if bsrc != nil {
dst = append(dst, bsrc[match[2*num]:match[2*num+1]]...)
} else {
@@ -1047,3 +1056,52 @@ func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int {
}
return result
}
+
+// Split slices s into substrings separated by the expression and returns a slice of
+// the substrings between those expression matches.
+//
+// The slice returned by this method consists of all the substrings of s
+// not contained in the slice returned by FindAllString. When called on an expression
+// that contains no metacharacters, it is equivalent to strings.SplitN.
+//
+// Example:
+// s := regexp.MustCompile("a*").Split("abaabaccadaaae", 5)
+// // s: ["", "b", "b", "c", "cadaaae"]
+//
+// The count determines the number of substrings to return:
+// n > 0: at most n substrings; the last substring will be the unsplit remainder.
+// n == 0: the result is nil (zero substrings)
+// n < 0: all substrings
+func (re *Regexp) Split(s string, n int) []string {
+
+ if n == 0 {
+ return nil
+ }
+
+ if len(re.expr) > 0 && len(s) == 0 {
+ return []string{""}
+ }
+
+ matches := re.FindAllStringIndex(s, n)
+ strings := make([]string, 0, len(matches))
+
+ beg := 0
+ end := 0
+ for _, match := range matches {
+ if n > 0 && len(strings) >= n-1 {
+ break
+ }
+
+ end = match[0]
+ if match[1] != 0 {
+ strings = append(strings, s[beg:end])
+ }
+ beg = match[1]
+ }
+
+ if end != len(s) {
+ strings = append(strings, s[beg:])
+ }
+
+ return strings
+}
diff --git a/src/pkg/regexp/syntax/compile.go b/src/pkg/regexp/syntax/compile.go
index 41955bfc2..95f6f1569 100644
--- a/src/pkg/regexp/syntax/compile.go
+++ b/src/pkg/regexp/syntax/compile.go
@@ -10,10 +10,10 @@ import "unicode"
// Because the pointers haven't been filled in yet, we can reuse their storage
// to hold the list. It's kind of sleazy, but works well in practice.
// See http://swtch.com/~rsc/regexp/regexp1.html for inspiration.
-//
+//
// These aren't really pointers: they're integers, so we can reinterpret them
// this way without using package unsafe. A value l denotes
-// p.inst[l>>1].Out (l&1==0) or .Arg (l&1==1).
+// p.inst[l>>1].Out (l&1==0) or .Arg (l&1==1).
// l == 0 denotes the empty list, okay because we start every program
// with a fail instruction, so we'll never want to point at its output link.
type patchList uint32
diff --git a/src/pkg/regexp/syntax/doc.go b/src/pkg/regexp/syntax/doc.go
new file mode 100644
index 000000000..843a6f6a4
--- /dev/null
+++ b/src/pkg/regexp/syntax/doc.go
@@ -0,0 +1,127 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// DO NOT EDIT. This file is generated by mksyntaxgo from the RE2 distribution.
+
+/*
+Package syntax parses regular expressions into parse trees and compiles
+parse trees into programs. Most clients of regular expressions will use the
+facilities of package regexp (such as Compile and Match) instead of this package.
+
+Syntax
+
+The regular expression syntax understood by this package when parsing with the Perl flag is as follows.
+Parts of the syntax can be disabled by passing alternate flags to Parse.
+
+
+Single characters:
+ . any character, possibly including newline (flag s=true)
+ [xyz] character class
+ [^xyz] negated character class
+ \d Perl character class
+ \D negated Perl character class
+ [:alpha:] ASCII character class
+ [:^alpha:] negated ASCII character class
+ \pN Unicode character class (one-letter name)
+ \p{Greek} Unicode character class
+ \PN negated Unicode character class (one-letter name)
+ \P{Greek} negated Unicode character class
+
+Composites:
+ xy x followed by y
+ x|y x or y (prefer x)
+
+Repetitions:
+ x* zero or more x, prefer more
+ x+ one or more x, prefer more
+ x? zero or one x, prefer one
+ x{n,m} n or n+1 or ... or m x, prefer more
+ x{n,} n or more x, prefer more
+ x{n} exactly n x
+ x*? zero or more x, prefer fewer
+ x+? one or more x, prefer fewer
+ x?? zero or one x, prefer zero
+ x{n,m}? n or n+1 or ... or m x, prefer fewer
+ x{n,}? n or more x, prefer fewer
+ x{n}? exactly n x
+
+Grouping:
+ (re) numbered capturing group
+ (?P<name>re) named & numbered capturing group
+ (?:re) non-capturing group
+ (?flags) set flags within current group; non-capturing
+ (?flags:re) set flags during re; non-capturing
+
+ Flag syntax is xyz (set) or -xyz (clear) or xy-z (set xy, clear z). The flags are:
+
+ i case-insensitive (default false)
+ m multi-line mode: ^ and $ match begin/end line in addition to begin/end text (default false)
+ s let . match \n (default false)
+ U ungreedy: swap meaning of x* and x*?, x+ and x+?, etc (default false)
+
+Empty strings:
+ ^ at beginning of text or line (flag m=true)
+ $ at end of text (like \z not \Z) or line (flag m=true)
+ \A at beginning of text
+ \b at word boundary (\w on one side and \W, \A, or \z on the other)
+ \B not a word boundary
+ \z at end of text
+
+Escape sequences:
+ \a bell (== \007)
+ \f form feed (== \014)
+ \t horizontal tab (== \011)
+ \n newline (== \012)
+ \r carriage return (== \015)
+ \v vertical tab character (== \013)
+ \* literal *, for any punctuation character *
+ \123 octal character code (up to three digits)
+ \x7F hex character code (exactly two digits)
+ \x{10FFFF} hex character code
+ \Q...\E literal text ... even if ... has punctuation
+
+Character class elements:
+ x single character
+ A-Z character range (inclusive)
+ \d Perl character class
+ [:foo:] ASCII character class foo
+ \p{Foo} Unicode character class Foo
+ \pF Unicode character class F (one-letter name)
+
+Named character classes as character class elements:
+ [\d] digits (== \d)
+ [^\d] not digits (== \D)
+ [\D] not digits (== \D)
+ [^\D] not not digits (== \d)
+ [[:name:]] named ASCII class inside character class (== [:name:])
+ [^[:name:]] named ASCII class inside negated character class (== [:^name:])
+ [\p{Name}] named Unicode property inside character class (== \p{Name})
+ [^\p{Name}] named Unicode property inside negated character class (== \P{Name})
+
+Perl character classes:
+ \d digits (== [0-9])
+ \D not digits (== [^0-9])
+ \s whitespace (== [\t\n\f\r ])
+ \S not whitespace (== [^\t\n\f\r ])
+ \w word characters (== [0-9A-Za-z_])
+ \W not word characters (== [^0-9A-Za-z_])
+
+ASCII character classes:
+ [:alnum:] alphanumeric (== [0-9A-Za-z])
+ [:alpha:] alphabetic (== [A-Za-z])
+ [:ascii:] ASCII (== [\x00-\x7F])
+ [:blank:] blank (== [\t ])
+ [:cntrl:] control (== [\x00-\x1F\x7F])
+ [:digit:] digits (== [0-9])
+ [:graph:] graphical (== [!-~] == [A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])
+ [:lower:] lower case (== [a-z])
+ [:print:] printable (== [ -~] == [ [:graph:]])
+ [:punct:] punctuation (== [!-/:-@[-`{-~])
+ [:space:] whitespace (== [\t\n\v\f\r ])
+ [:upper:] upper case (== [A-Z])
+ [:word:] word characters (== [0-9A-Za-z_])
+ [:xdigit:] hex digit (== [0-9A-Fa-f])
+
+*/
+package syntax
diff --git a/src/pkg/regexp/syntax/parse.go b/src/pkg/regexp/syntax/parse.go
index 4924e9453..30e0e8b7f 100644
--- a/src/pkg/regexp/syntax/parse.go
+++ b/src/pkg/regexp/syntax/parse.go
@@ -2,10 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package syntax parses regular expressions into parse trees and compiles
-// parse trees into programs. Most clients of regular expressions will use
-// the facilities of package regexp (such as Compile and Match) instead of
-// this package.
package syntax
import (
@@ -46,11 +42,9 @@ const (
ErrMissingParen ErrorCode = "missing closing )"
ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator"
ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression"
+ ErrUnexpectedParen ErrorCode = "unexpected )"
)
-// TODO: Export for Go 1.1.
-const errUnexpectedParen ErrorCode = "unexpected )"
-
func (e ErrorCode) String() string {
return string(e)
}
@@ -470,7 +464,7 @@ func (p *parser) factor(sub []*Regexp, flags Flags) []*Regexp {
// Construct factored form: prefix(suffix1|suffix2|...)
prefix := first
for j := start; j < i; j++ {
- reuse := j != start // prefix came from sub[start]
+ reuse := j != start // prefix came from sub[start]
sub[j] = p.removeLeadingRegexp(sub[j], reuse)
}
suffix := p.collapse(sub[start:i], OpAlternate) // recurse
@@ -1171,13 +1165,13 @@ func (p *parser) parseRightParen() error {
n := len(p.stack)
if n < 2 {
- return &Error{errUnexpectedParen, p.wholeRegexp}
+ return &Error{ErrUnexpectedParen, p.wholeRegexp}
}
re1 := p.stack[n-1]
re2 := p.stack[n-2]
p.stack = p.stack[:n-2]
if re2.Op != opLeftParen {
- return &Error{errUnexpectedParen, p.wholeRegexp}
+ return &Error{ErrUnexpectedParen, p.wholeRegexp}
}
// Restore flags at time of paren.
p.flags = re2.Flags
diff --git a/src/pkg/runtime/alg.c b/src/pkg/runtime/alg.c
index 36973eba3..ad85b43ae 100644
--- a/src/pkg/runtime/alg.c
+++ b/src/pkg/runtime/alg.c
@@ -19,13 +19,13 @@ runtime·memhash(uintptr *h, uintptr s, void *a)
uintptr hash;
b = a;
- hash = M0;
+ hash = M0 ^ *h;
while(s > 0) {
hash = (hash ^ *b) * M1;
b++;
s--;
}
- *h = (*h ^ hash) * M1;
+ *h = hash;
}
void
@@ -316,7 +316,7 @@ runtime·strhash(uintptr *h, uintptr s, void *a)
void
runtime·strequal(bool *eq, uintptr s, void *a, void *b)
{
- int32 alen;
+ intgo alen;
USED(s);
alen = ((String*)a)->len;
@@ -324,6 +324,10 @@ runtime·strequal(bool *eq, uintptr s, void *a, void *b)
*eq = false;
return;
}
+ if(((String*)a)->str == ((String*)b)->str) {
+ *eq = true;
+ return;
+ }
runtime·memequal(eq, alen, ((String*)a)->str, ((String*)b)->str);
}
@@ -351,7 +355,7 @@ void
runtime·interhash(uintptr *h, uintptr s, void *a)
{
USED(s);
- *h = (*h ^ runtime·ifacehash(*(Iface*)a)) * M1;
+ *h = runtime·ifacehash(*(Iface*)a, *h ^ M0) * M1;
}
void
@@ -385,7 +389,7 @@ void
runtime·nilinterhash(uintptr *h, uintptr s, void *a)
{
USED(s);
- *h = (*h ^ runtime·efacehash(*(Eface*)a)) * M1;
+ *h = runtime·efacehash(*(Eface*)a, *h ^ M0) * M1;
}
void
@@ -469,10 +473,11 @@ void
runtime·equal(Type *t, ...)
{
byte *x, *y;
- bool *ret;
+ uintptr ret;
x = (byte*)(&t+1);
y = x + t->size;
- ret = (bool*)(y + t->size);
- t->alg->equal(ret, t->size, x, y);
+ ret = (uintptr)(y + t->size);
+ ret = ROUND(ret, Structrnd);
+ t->alg->equal((bool*)ret, t->size, x, y);
}
diff --git a/src/pkg/runtime/append_test.go b/src/pkg/runtime/append_test.go
index b8552224e..36390181e 100644
--- a/src/pkg/runtime/append_test.go
+++ b/src/pkg/runtime/append_test.go
@@ -19,6 +19,67 @@ func BenchmarkAppend(b *testing.B) {
}
}
+func benchmarkAppendBytes(b *testing.B, length int) {
+ b.StopTimer()
+ x := make([]byte, 0, N)
+ y := make([]byte, length)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ x = x[0:0]
+ x = append(x, y...)
+ }
+}
+
+func BenchmarkAppend1Byte(b *testing.B) {
+ benchmarkAppendBytes(b, 1)
+}
+
+func BenchmarkAppend4Bytes(b *testing.B) {
+ benchmarkAppendBytes(b, 4)
+}
+
+func BenchmarkAppend8Bytes(b *testing.B) {
+ benchmarkAppendBytes(b, 8)
+}
+
+func BenchmarkAppend16Bytes(b *testing.B) {
+ benchmarkAppendBytes(b, 16)
+}
+
+func BenchmarkAppend32Bytes(b *testing.B) {
+ benchmarkAppendBytes(b, 32)
+}
+
+func benchmarkAppendStr(b *testing.B, str string) {
+ b.StopTimer()
+ x := make([]byte, 0, N)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ x = x[0:0]
+ x = append(x, str...)
+ }
+}
+
+func BenchmarkAppendStr1Byte(b *testing.B) {
+ benchmarkAppendStr(b, "1")
+}
+
+func BenchmarkAppendStr4Bytes(b *testing.B) {
+ benchmarkAppendStr(b, "1234")
+}
+
+func BenchmarkAppendStr8Bytes(b *testing.B) {
+ benchmarkAppendStr(b, "12345678")
+}
+
+func BenchmarkAppendStr16Bytes(b *testing.B) {
+ benchmarkAppendStr(b, "1234567890123456")
+}
+
+func BenchmarkAppendStr32Bytes(b *testing.B) {
+ benchmarkAppendStr(b, "12345678901234567890123456789012")
+}
+
func BenchmarkAppendSpecialCase(b *testing.B) {
b.StopTimer()
x := make([]int, 0, N)
@@ -50,3 +111,13 @@ func TestSideEffectOrder(t *testing.T) {
t.Error("append failed: ", x[0], x[1])
}
}
+
+func TestAppendOverlap(t *testing.T) {
+ x := []byte("1234")
+ x = append(x[1:], x...) // p > q in runtime·appendslice.
+ got := string(x)
+ want := "2341234"
+ if got != want {
+ t.Errorf("overlap failed: got %q want %q", got, want)
+ }
+}
diff --git a/src/pkg/runtime/arch_386.h b/src/pkg/runtime/arch_386.h
index a0798f99e..4df795f71 100644
--- a/src/pkg/runtime/arch_386.h
+++ b/src/pkg/runtime/arch_386.h
@@ -1,4 +1,6 @@
enum {
thechar = '8',
- CacheLineSize = 64
+ BigEndian = 0,
+ CacheLineSize = 64,
+ appendCrossover = 16
};
diff --git a/src/pkg/runtime/arch_amd64.h b/src/pkg/runtime/arch_amd64.h
index dd1cfc18d..e83dc9105 100644
--- a/src/pkg/runtime/arch_amd64.h
+++ b/src/pkg/runtime/arch_amd64.h
@@ -1,4 +1,6 @@
enum {
thechar = '6',
- CacheLineSize = 64
+ BigEndian = 0,
+ CacheLineSize = 64,
+ appendCrossover = 16
};
diff --git a/src/pkg/runtime/arch_arm.h b/src/pkg/runtime/arch_arm.h
index c1a7a0f37..f6af58514 100644
--- a/src/pkg/runtime/arch_arm.h
+++ b/src/pkg/runtime/arch_arm.h
@@ -1,4 +1,6 @@
enum {
thechar = '5',
- CacheLineSize = 32
+ BigEndian = 0,
+ CacheLineSize = 32,
+ appendCrossover = 16
};
diff --git a/src/pkg/runtime/asm_386.s b/src/pkg/runtime/asm_386.s
index 21bd293ab..96f04e0ae 100644
--- a/src/pkg/runtime/asm_386.s
+++ b/src/pkg/runtime/asm_386.s
@@ -14,22 +14,22 @@ TEXT _rt0_386(SB),7,$0
MOVL BX, 124(SP)
// set default stack bounds.
- // initcgo may update stackguard.
+ // _cgo_init may update stackguard.
MOVL $runtime·g0(SB), BP
LEAL (-64*1024+104)(SP), BX
MOVL BX, g_stackguard(BP)
MOVL SP, g_stackbase(BP)
- // if there is an initcgo, call it to let it
+ // if there is an _cgo_init, call it to let it
// initialize and to set up GS. if not,
// we set up GS ourselves.
- MOVL initcgo(SB), AX
+ MOVL _cgo_init(SB), AX
TESTL AX, AX
JZ needtls
PUSHL BP
CALL AX
POPL BP
- // skip runtime·ldt0setup(SB) and tls test after initcgo for non-windows
+ // skip runtime·ldt0setup(SB) and tls test after _cgo_init for non-windows
CMPL runtime·iswindows(SB), $0
JEQ ok
needtls:
@@ -75,7 +75,7 @@ ok:
CALL runtime·schedinit(SB)
// create a new goroutine to start program
- PUSHL $runtime·main(SB) // entry
+ PUSHL $runtime·main·f(SB) // entry
PUSHL $0 // arg size
CALL runtime·newproc(SB)
POPL AX
@@ -87,6 +87,9 @@ ok:
INT $3
RET
+DATA runtime·main·f+0(SB)/4,$runtime·main(SB)
+GLOBL runtime·main·f(SB),8,$4
+
TEXT runtime·breakpoint(SB),7,$0
INT $3
RET
@@ -131,22 +134,40 @@ TEXT runtime·gogo(SB), 7, $0
MOVL gobuf_pc(BX), BX
JMP BX
-// void gogocall(Gobuf*, void (*fn)(void))
+// void gogocall(Gobuf*, void (*fn)(void), uintptr r0)
// restore state from Gobuf but then call fn.
// (call fn, returning to state in Gobuf)
TEXT runtime·gogocall(SB), 7, $0
+ MOVL 12(SP), DX // context
MOVL 8(SP), AX // fn
MOVL 4(SP), BX // gobuf
- MOVL gobuf_g(BX), DX
+ MOVL gobuf_g(BX), DI
get_tls(CX)
- MOVL DX, g(CX)
- MOVL 0(DX), CX // make sure g != nil
+ MOVL DI, g(CX)
+ MOVL 0(DI), CX // make sure g != nil
MOVL gobuf_sp(BX), SP // restore SP
MOVL gobuf_pc(BX), BX
PUSHL BX
JMP AX
POPL BX // not reached
+// void gogocallfn(Gobuf*, FuncVal*)
+// restore state from Gobuf but then call fn.
+// (call fn, returning to state in Gobuf)
+TEXT runtime·gogocallfn(SB), 7, $0
+ MOVL 8(SP), DX // fn
+ MOVL 4(SP), BX // gobuf
+ MOVL gobuf_g(BX), DI
+ get_tls(CX)
+ MOVL DI, g(CX)
+ MOVL 0(DI), CX // make sure g != nil
+ MOVL gobuf_sp(BX), SP // restore SP
+ MOVL gobuf_pc(BX), BX
+ PUSHL BX
+ MOVL 0(DX), BX
+ JMP BX
+ POPL BX // not reached
+
// void mcall(void (*fn)(G*))
// Switch to m->g0's stack, call fn(g).
// Fn must never return. It should gogo(&g->sched)
@@ -189,11 +210,13 @@ TEXT runtime·morestack(SB),7,$0
CMPL g(CX), SI
JNE 2(PC)
INT $3
+
+ MOVL DX, m_cret(BX)
- // frame size in DX
+ // frame size in DI
// arg size in AX
// Save in m.
- MOVL DX, m_moreframesize(BX)
+ MOVL DI, m_moreframesize(BX)
MOVL AX, m_moreargsize(BX)
// Called from f.
@@ -299,6 +322,33 @@ TEXT runtime·cas(SB), 7, $0
MOVL $1, AX
RET
+// bool runtime·cas64(uint64 *val, uint64 *old, uint64 new)
+// Atomically:
+// if(*val == *old){
+// *val = new;
+// return 1;
+// } else {
+// *old = *val
+// return 0;
+// }
+TEXT runtime·cas64(SB), 7, $0
+ MOVL 4(SP), BP
+ MOVL 8(SP), SI
+ MOVL 0(SI), AX
+ MOVL 4(SI), DX
+ MOVL 12(SP), BX
+ MOVL 16(SP), CX
+ LOCK
+ CMPXCHG8B 0(BP)
+ JNZ cas64_fail
+ MOVL $1, AX
+ RET
+cas64_fail:
+ MOVL AX, 0(SI)
+ MOVL DX, 4(SI)
+ MOVL $0, AX
+ RET
+
// bool casp(void **p, void *old, void *new)
// Atomically:
// if(*p == old){
@@ -357,17 +407,49 @@ TEXT runtime·atomicstore(SB), 7, $0
XCHGL AX, 0(BX)
RET
+// uint64 atomicload64(uint64 volatile* addr);
+// so actually
+// void atomicload64(uint64 *res, uint64 volatile *addr);
+TEXT runtime·atomicload64(SB), 7, $0
+ MOVL 4(SP), BX
+ MOVL 8(SP), AX
+ // MOVQ (%EAX), %MM0
+ BYTE $0x0f; BYTE $0x6f; BYTE $0x00
+ // MOVQ %MM0, 0(%EBX)
+ BYTE $0x0f; BYTE $0x7f; BYTE $0x03
+ // EMMS
+ BYTE $0x0F; BYTE $0x77
+ RET
+
+// void runtime·atomicstore64(uint64 volatile* addr, uint64 v);
+TEXT runtime·atomicstore64(SB), 7, $0
+ MOVL 4(SP), AX
+ // MOVQ and EMMS were introduced on the Pentium MMX.
+ // MOVQ 0x8(%ESP), %MM0
+ BYTE $0x0f; BYTE $0x6f; BYTE $0x44; BYTE $0x24; BYTE $0x08
+ // MOVQ %MM0, (%EAX)
+ BYTE $0x0f; BYTE $0x7f; BYTE $0x00
+ // EMMS
+ BYTE $0x0F; BYTE $0x77
+ // This is essentially a no-op, but it provides required memory fencing.
+ // It can be replaced with MFENCE, but MFENCE was introduced only on the Pentium4 (SSE2).
+ MOVL $0, AX
+ LOCK
+ XADDL AX, (SP)
+ RET
+
// void jmpdefer(fn, sp);
// called from deferreturn.
// 1. pop the caller
// 2. sub 5 bytes from the callers return
// 3. jmp to the argument
TEXT runtime·jmpdefer(SB), 7, $0
- MOVL 4(SP), AX // fn
+ MOVL 4(SP), DX // fn
MOVL 8(SP), BX // caller sp
LEAL -4(BX), SP // caller sp after CALL
SUBL $5, (SP) // return to CALL again
- JMP AX // but first run the deferred function
+ MOVL 0(DX), BX
+ JMP BX // but first run the deferred function
// Dummy function to use in saved gobuf.PC,
// to match SP pointing at a return address.
@@ -416,23 +498,49 @@ TEXT runtime·asmcgocall(SB),7,$0
RET
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
-// See cgocall.c for more details.
+// Turn the fn into a Go func (by taking its address) and call
+// cgocallback_gofunc.
TEXT runtime·cgocallback(SB),7,$12
- MOVL fn+0(FP), AX
- MOVL frame+4(FP), BX
- MOVL framesize+8(FP), DX
+ LEAL fn+0(FP), AX
+ MOVL AX, 0(SP)
+ MOVL frame+4(FP), AX
+ MOVL AX, 4(SP)
+ MOVL framesize+8(FP), AX
+ MOVL AX, 8(SP)
+ MOVL $runtime·cgocallback_gofunc(SB), AX
+ CALL AX
+ RET
- // Save current m->g0->sched.sp on stack and then set it to SP.
+// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
+// See cgocall.c for more details.
+TEXT runtime·cgocallback_gofunc(SB),7,$12
+ // If m is nil, Go did not create the current thread.
+ // Call needm to obtain one for temporary use.
+ // In this case, we're running on the thread stack, so there's
+ // lots of space, but the linker doesn't know. Hide the call from
+ // the linker analysis by using an indirect call through AX.
get_tls(CX)
+#ifdef GOOS_windows
+ CMPL CX, $0
+ JNE 3(PC)
+ PUSHL $0
+ JMP needm
+#endif
MOVL m(CX), BP
-
- // If m is nil, it is almost certainly because we have been called
- // on a thread that Go did not create. We're going to crash as
- // soon as we try to use m; instead, try to print a nice error and exit.
+ PUSHL BP
CMPL BP, $0
- JNE 2(PC)
- CALL runtime·badcallback(SB)
+ JNE havem
+needm:
+ MOVL $runtime·needm(SB), AX
+ CALL AX
+ get_tls(CX)
+ MOVL m(CX), BP
+havem:
+ // Now there's a valid m, and we're running on its m->g0.
+ // Save current m->g0->sched.sp on stack and then set it to SP.
+ // Save current sp in m->g0->sched.sp in preparation for
+ // switch back to m->curg stack.
MOVL m_g0(BP), SI
PUSHL (g_sched+gobuf_sp)(SI)
MOVL SP, (g_sched+gobuf_sp)(SI)
@@ -451,6 +559,10 @@ TEXT runtime·cgocallback(SB),7,$12
// a frame size of 12, the same amount that we use below),
// so that the traceback will seamlessly trace back into
// the earlier calls.
+ MOVL fn+0(FP), AX
+ MOVL frame+4(FP), BX
+ MOVL framesize+8(FP), DX
+
MOVL m_curg(BP), SI
MOVL SI, g(CX)
MOVL (g_sched+gobuf_sp)(SI), DI // prepare stack as DI
@@ -488,10 +600,38 @@ TEXT runtime·cgocallback(SB),7,$12
MOVL SI, g(CX)
MOVL (g_sched+gobuf_sp)(SI), SP
POPL (g_sched+gobuf_sp)(SI)
+
+ // If the m on entry was nil, we called needm above to borrow an m
+ // for the duration of the call. Since the call is over, return it with dropm.
+ POPL BP
+ CMPL BP, $0
+ JNE 3(PC)
+ MOVL $runtime·dropm(SB), AX
+ CALL AX
// Done!
RET
+// void setmg(M*, G*); set m and g. for use by needm.
+TEXT runtime·setmg(SB), 7, $0
+#ifdef GOOS_windows
+ MOVL mm+0(FP), AX
+ CMPL AX, $0
+ JNE settls
+ MOVL $0, 0x14(FS)
+ RET
+settls:
+ LEAL m_tls(AX), AX
+ MOVL AX, 0x14(FS)
+#endif
+ MOVL mm+0(FP), AX
+ get_tls(CX)
+ MOVL mm+0(FP), AX
+ MOVL AX, m(CX)
+ MOVL gg+4(FP), BX
+ MOVL BX, g(CX)
+ RET
+
// check that SP is in range [g->stackbase, g->stackguard)
TEXT runtime·stackcheck(SB), 7, $0
get_tls(CX)
diff --git a/src/pkg/runtime/asm_amd64.s b/src/pkg/runtime/asm_amd64.s
index d41ab96d0..987958498 100644
--- a/src/pkg/runtime/asm_amd64.s
+++ b/src/pkg/runtime/asm_amd64.s
@@ -14,14 +14,14 @@ TEXT _rt0_amd64(SB),7,$-8
MOVQ BX, 24(SP)
// create istack out of the given (operating system) stack.
- // initcgo may update stackguard.
+ // _cgo_init may update stackguard.
MOVQ $runtime·g0(SB), DI
LEAQ (-64*1024+104)(SP), BX
MOVQ BX, g_stackguard(DI)
MOVQ SP, g_stackbase(DI)
- // if there is an initcgo, call it.
- MOVQ initcgo(SB), AX
+ // if there is an _cgo_init, call it.
+ MOVQ _cgo_init(SB), AX
TESTQ AX, AX
JZ needtls
// g0 already in DI
@@ -31,6 +31,10 @@ TEXT _rt0_amd64(SB),7,$-8
JEQ ok
needtls:
+ // skip TLS setup on Plan 9
+ CMPL runtime·isplan9(SB), $1
+ JEQ ok
+
LEAQ runtime·tls0(SB), DI
CALL runtime·settls(SB)
@@ -64,7 +68,7 @@ ok:
CALL runtime·schedinit(SB)
// create a new goroutine to start program
- PUSHQ $runtime·main(SB) // entry
+ PUSHQ $runtime·main·f(SB) // entry
PUSHQ $0 // arg size
CALL runtime·newproc(SB)
POPQ AX
@@ -76,6 +80,9 @@ ok:
MOVL $0xf1, 0xf1 // crash
RET
+DATA runtime·main·f+0(SB)/8,$runtime·main(SB)
+GLOBL runtime·main·f(SB),8,$8
+
TEXT runtime·breakpoint(SB),7,$0
BYTE $0xcc
RET
@@ -114,22 +121,40 @@ TEXT runtime·gogo(SB), 7, $0
MOVQ gobuf_pc(BX), BX
JMP BX
-// void gogocall(Gobuf*, void (*fn)(void))
+// void gogocall(Gobuf*, void (*fn)(void), uintptr r0)
// restore state from Gobuf but then call fn.
// (call fn, returning to state in Gobuf)
TEXT runtime·gogocall(SB), 7, $0
+ MOVQ 24(SP), DX // context
MOVQ 16(SP), AX // fn
MOVQ 8(SP), BX // gobuf
- MOVQ gobuf_g(BX), DX
+ MOVQ gobuf_g(BX), DI
get_tls(CX)
- MOVQ DX, g(CX)
- MOVQ 0(DX), CX // make sure g != nil
+ MOVQ DI, g(CX)
+ MOVQ 0(DI), CX // make sure g != nil
MOVQ gobuf_sp(BX), SP // restore SP
MOVQ gobuf_pc(BX), BX
PUSHQ BX
JMP AX
POPQ BX // not reached
+// void gogocallfn(Gobuf*, FuncVal*)
+// restore state from Gobuf but then call fn.
+// (call fn, returning to state in Gobuf)
+TEXT runtime·gogocallfn(SB), 7, $0
+ MOVQ 16(SP), DX // fn
+ MOVQ 8(SP), BX // gobuf
+ MOVQ gobuf_g(BX), AX
+ get_tls(CX)
+ MOVQ AX, g(CX)
+ MOVQ 0(AX), CX // make sure g != nil
+ MOVQ gobuf_sp(BX), SP // restore SP
+ MOVQ gobuf_pc(BX), BX
+ PUSHQ BX
+ MOVQ 0(DX), BX
+ JMP BX
+ POPQ BX // not reached
+
// void mcall(void (*fn)(G*))
// Switch to m->g0's stack, call fn(g).
// Fn must never return. It should gogo(&g->sched)
@@ -171,6 +196,8 @@ TEXT runtime·morestack(SB),7,$0
CMPQ g(CX), SI
JNE 2(PC)
INT $3
+
+ MOVQ DX, m_cret(BX)
// Called from f.
// Set m->morebuf to f's caller.
@@ -344,6 +371,30 @@ TEXT runtime·cas(SB), 7, $0
MOVL $1, AX
RET
+// bool runtime·cas64(uint64 *val, uint64 *old, uint64 new)
+// Atomically:
+// if(*val == *old){
+// *val = new;
+// return 1;
+// } else {
+// *old = *val
+// return 0;
+// }
+TEXT runtime·cas64(SB), 7, $0
+ MOVQ 8(SP), BX
+ MOVQ 16(SP), BP
+ MOVQ 0(BP), AX
+ MOVQ 24(SP), CX
+ LOCK
+ CMPXCHGQ CX, 0(BX)
+ JNZ cas64_fail
+ MOVL $1, AX
+ RET
+cas64_fail:
+ MOVQ AX, 0(BP)
+ MOVL $0, AX
+ RET
+
// bool casp(void **val, void *old, void *new)
// Atomically:
// if(*val == old){
@@ -376,6 +427,15 @@ TEXT runtime·xadd(SB), 7, $0
ADDL CX, AX
RET
+TEXT runtime·xadd64(SB), 7, $0
+ MOVQ 8(SP), BX
+ MOVQ 16(SP), AX
+ MOVQ AX, CX
+ LOCK
+ XADDQ AX, 0(BX)
+ ADDQ CX, AX
+ RET
+
TEXT runtime·xchg(SB), 7, $0
MOVQ 8(SP), BX
MOVL 16(SP), AX
@@ -402,17 +462,24 @@ TEXT runtime·atomicstore(SB), 7, $0
XCHGL AX, 0(BX)
RET
+TEXT runtime·atomicstore64(SB), 7, $0
+ MOVQ 8(SP), BX
+ MOVQ 16(SP), AX
+ XCHGQ AX, 0(BX)
+ RET
+
// void jmpdefer(fn, sp);
// called from deferreturn.
// 1. pop the caller
// 2. sub 5 bytes from the callers return
// 3. jmp to the argument
TEXT runtime·jmpdefer(SB), 7, $0
- MOVQ 8(SP), AX // fn
+ MOVQ 8(SP), DX // fn
MOVQ 16(SP), BX // caller sp
LEAQ -8(BX), SP // caller sp after CALL
SUBQ $5, (SP) // return to CALL again
- JMP AX // but first run the deferred function
+ MOVQ 0(DX), BX
+ JMP BX // but first run the deferred function
// Dummy function to use in saved gobuf.PC,
// to match SP pointing at a return address.
@@ -446,39 +513,67 @@ TEXT runtime·asmcgocall(SB),7,$0
MOVQ (g_sched+gobuf_sp)(SI), SP
// Now on a scheduling stack (a pthread-created stack).
- SUBQ $48, SP
+ // Make sure we have enough room for 4 stack-backed fast-call
+ // registers as per windows amd64 calling convention.
+ SUBQ $64, SP
ANDQ $~15, SP // alignment for gcc ABI
- MOVQ DI, 32(SP) // save g
- MOVQ DX, 24(SP) // save SP
+ MOVQ DI, 48(SP) // save g
+ MOVQ DX, 40(SP) // save SP
MOVQ BX, DI // DI = first argument in AMD64 ABI
MOVQ BX, CX // CX = first argument in Win64
CALL AX
// Restore registers, g, stack pointer.
get_tls(CX)
- MOVQ 32(SP), DI
+ MOVQ 48(SP), DI
MOVQ DI, g(CX)
- MOVQ 24(SP), SP
+ MOVQ 40(SP), SP
RET
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
-// See cgocall.c for more details.
+// Turn the fn into a Go func (by taking its address) and call
+// cgocallback_gofunc.
TEXT runtime·cgocallback(SB),7,$24
- MOVQ fn+0(FP), AX
- MOVQ frame+8(FP), BX
- MOVQ framesize+16(FP), DX
+ LEAQ fn+0(FP), AX
+ MOVQ AX, 0(SP)
+ MOVQ frame+8(FP), AX
+ MOVQ AX, 8(SP)
+ MOVQ framesize+16(FP), AX
+ MOVQ AX, 16(SP)
+ MOVQ $runtime·cgocallback_gofunc(SB), AX
+ CALL AX
+ RET
- // Save current m->g0->sched.sp on stack and then set it to SP.
+// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
+// See cgocall.c for more details.
+TEXT runtime·cgocallback_gofunc(SB),7,$24
+ // If m is nil, Go did not create the current thread.
+ // Call needm to obtain one for temporary use.
+ // In this case, we're running on the thread stack, so there's
+ // lots of space, but the linker doesn't know. Hide the call from
+ // the linker analysis by using an indirect call through AX.
get_tls(CX)
+#ifdef GOOS_windows
+ CMPQ CX, $0
+ JNE 3(PC)
+ PUSHQ $0
+ JMP needm
+#endif
MOVQ m(CX), BP
-
- // If m is nil, it is almost certainly because we have been called
- // on a thread that Go did not create. We're going to crash as
- // soon as we try to use m; instead, try to print a nice error and exit.
+ PUSHQ BP
CMPQ BP, $0
- JNE 2(PC)
- CALL runtime·badcallback(SB)
+ JNE havem
+needm:
+ MOVQ $runtime·needm(SB), AX
+ CALL AX
+ get_tls(CX)
+ MOVQ m(CX), BP
+havem:
+ // Now there's a valid m, and we're running on its m->g0.
+ // Save current m->g0->sched.sp on stack and then set it to SP.
+ // Save current sp in m->g0->sched.sp in preparation for
+ // switch back to m->curg stack.
MOVQ m_g0(BP), SI
PUSHQ (g_sched+gobuf_sp)(SI)
MOVQ SP, (g_sched+gobuf_sp)(SI)
@@ -497,6 +592,10 @@ TEXT runtime·cgocallback(SB),7,$24
// a frame size of 24, the same amount that we use below),
// so that the traceback will seamlessly trace back into
// the earlier calls.
+ MOVQ fn+0(FP), AX
+ MOVQ frame+8(FP), BX
+ MOVQ framesize+16(FP), DX
+
MOVQ m_curg(BP), SI
MOVQ SI, g(CX)
MOVQ (g_sched+gobuf_sp)(SI), DI // prepare stack as DI
@@ -534,10 +633,37 @@ TEXT runtime·cgocallback(SB),7,$24
MOVQ SI, g(CX)
MOVQ (g_sched+gobuf_sp)(SI), SP
POPQ (g_sched+gobuf_sp)(SI)
+
+ // If the m on entry was nil, we called needm above to borrow an m
+ // for the duration of the call. Since the call is over, return it with dropm.
+ POPQ BP
+ CMPQ BP, $0
+ JNE 3(PC)
+ MOVQ $runtime·dropm(SB), AX
+ CALL AX
// Done!
RET
+// void setmg(M*, G*); set m and g. for use by needm.
+TEXT runtime·setmg(SB), 7, $0
+ MOVQ mm+0(FP), AX
+#ifdef GOOS_windows
+ CMPQ AX, $0
+ JNE settls
+ MOVQ $0, 0x28(GS)
+ RET
+settls:
+ LEAQ m_tls(AX), AX
+ MOVQ AX, 0x28(GS)
+#endif
+ get_tls(CX)
+ MOVQ mm+0(FP), AX
+ MOVQ AX, m(CX)
+ MOVQ gg+8(FP), BX
+ MOVQ BX, g(CX)
+ RET
+
// check that SP is in range [g->stackbase, g->stackguard)
TEXT runtime·stackcheck(SB), 7, $0
get_tls(CX)
diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s
index 423fda7a0..45b53541b 100644
--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -31,6 +31,13 @@ TEXT _rt0_arm(SB),7,$-4
MOVW R13, g_stackbase(g)
BL runtime·emptyfunc(SB) // fault if stack check is wrong
+ // if there is an _cgo_init, call it.
+ MOVW _cgo_init(SB), R2
+ CMP $0, R2
+ MOVW.NE g, R0 // first argument of _cgo_init is g
+ BL.NE (R2) // will clobber R0-R3
+
+ BL runtime·checkgoarm(SB)
BL runtime·check(SB)
// saved argc, argv
@@ -43,7 +50,7 @@ TEXT _rt0_arm(SB),7,$-4
BL runtime·schedinit(SB)
// create a new goroutine to start program
- MOVW $runtime·main(SB), R0
+ MOVW $runtime·main·f(SB), R0
MOVW.W R0, -4(R13)
MOVW $8, R0
MOVW.W R0, -4(R13)
@@ -58,24 +65,25 @@ TEXT _rt0_arm(SB),7,$-4
MOVW $1234, R0
MOVW $1000, R1
MOVW R0, (R1) // fail hard
- B runtime·_dep_dummy(SB) // Never reached
-
-// TODO(kaib): remove these once i actually understand how the linker removes symbols
-// pull in dummy dependencies
-TEXT runtime·_dep_dummy(SB),7,$0
- BL _div(SB)
- BL _divu(SB)
- BL _mod(SB)
- BL _modu(SB)
- BL _modu(SB)
- BL _sfloat(SB)
+
+DATA runtime·main·f+0(SB)/4,$runtime·main(SB)
+GLOBL runtime·main·f(SB),8,$4
TEXT runtime·breakpoint(SB),7,$0
- // no breakpoint yet; let program exit
+ // gdb won't skip this breakpoint instruction automatically,
+ // so you must manually "set $pc+=4" to skip it and continue.
+ WORD $0xe1200071 // BKPT 0x0001
RET
+GLOBL runtime·goarm(SB), $4
TEXT runtime·asminit(SB),7,$0
- // No per-thread init.
+ // disable runfast (flush-to-zero) mode of vfp if runtime.goarm > 5
+ MOVW runtime·goarm(SB), R11
+ CMP $5, R11
+ BLE 4(PC)
+ WORD $0xeef1ba10 // vmrs r11, fpscr
+ BIC $(1<<24), R11
+ WORD $0xeee1ba10 // vmsr fpscr, r11
RET
/*
@@ -95,26 +103,49 @@ TEXT runtime·gosave(SB), 7, $-4
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), 7, $-4
MOVW 0(FP), R1 // gobuf
- MOVW 4(FP), R0 // return 2nd arg
MOVW gobuf_g(R1), g
MOVW 0(g), R2 // make sure g != nil
+ MOVW _cgo_save_gm(SB), R2
+ CMP $0, R2 // if in Cgo, we have to save g and m
+ BL.NE (R2) // this call will clobber R0
+ MOVW 4(FP), R0 // return 2nd arg
MOVW gobuf_sp(R1), SP // restore SP
MOVW gobuf_pc(R1), PC
-// void gogocall(Gobuf*, void (*fn)(void))
+// void gogocall(Gobuf*, void (*fn)(void), uintptr r7)
// restore state from Gobuf but then call fn.
// (call fn, returning to state in Gobuf)
// using frame size $-4 means do not save LR on stack.
TEXT runtime·gogocall(SB), 7, $-4
- MOVW 0(FP), R0 // gobuf
+ MOVW 0(FP), R3 // gobuf
MOVW 4(FP), R1 // fn
- MOVW 8(FP), R2 // fp offset
- MOVW gobuf_g(R0), g
- MOVW 0(g), R3 // make sure g != nil
- MOVW gobuf_sp(R0), SP // restore SP
- MOVW gobuf_pc(R0), LR
+ MOVW gobuf_g(R3), g
+ MOVW 0(g), R0 // make sure g != nil
+ MOVW _cgo_save_gm(SB), R0
+ CMP $0, R0 // if in Cgo, we have to save g and m
+ BL.NE (R0) // this call will clobber R0
+ MOVW 8(FP), R7 // context
+ MOVW gobuf_sp(R3), SP // restore SP
+ MOVW gobuf_pc(R3), LR
MOVW R1, PC
+// void gogocallfn(Gobuf*, FuncVal*)
+// restore state from Gobuf but then call fn.
+// (call fn, returning to state in Gobuf)
+// using frame size $-4 means do not save LR on stack.
+TEXT runtime·gogocallfn(SB), 7, $-4
+ MOVW 0(FP), R3 // gobuf
+ MOVW 4(FP), R1 // fn
+ MOVW gobuf_g(R3), g
+ MOVW 0(g), R0 // make sure g != nil
+ MOVW _cgo_save_gm(SB), R0
+ CMP $0, R0 // if in Cgo, we have to save g and m
+ BL.NE (R0) // this call will clobber R0
+ MOVW gobuf_sp(R3), SP // restore SP
+ MOVW gobuf_pc(R3), LR
+ MOVW R1, R7
+ MOVW 0(R1), PC
+
// void mcall(void (*fn)(G*))
// Switch to m->g0's stack, call fn(g).
// Fn must never return. It should gogo(&g->sched)
@@ -157,6 +188,7 @@ TEXT runtime·morestack(SB),7,$-4
BL.EQ runtime·abort(SB)
// Save in m.
+ MOVW R7, m_cret(m) // function context
MOVW R1, m_moreframesize(m)
MOVW R2, m_moreargsize(m)
@@ -228,28 +260,175 @@ TEXT runtime·lessstack(SB), 7, $-4
TEXT runtime·jmpdefer(SB), 7, $0
MOVW 0(SP), LR
MOVW $-4(LR), LR // BL deferreturn
- MOVW fn+0(FP), R0
+ MOVW fn+0(FP), R7
MOVW argp+4(FP), SP
MOVW $-4(SP), SP // SP is 4 below argp, due to saved LR
- B (R0)
+ MOVW 0(R7), R1
+ B (R1)
+
+// Dummy function to use in saved gobuf.PC,
+// to match SP pointing at a return address.
+// The gobuf.PC is unused by the contortions here
+// but setting it to return will make the traceback code work.
+TEXT return<>(SB),7,$0
+ RET
+// asmcgocall(void(*fn)(void*), void *arg)
+// Call fn(arg) on the scheduler stack,
+// aligned appropriately for the gcc ABI.
+// See cgocall.c for more details.
TEXT runtime·asmcgocall(SB),7,$0
- B runtime·cgounimpl(SB)
+ MOVW fn+0(FP), R1
+ MOVW arg+4(FP), R0
+ MOVW R13, R2
+ MOVW g, R5
+
+ // Figure out if we need to switch to m->g0 stack.
+ // We get called to create new OS threads too, and those
+ // come in on the m->g0 stack already.
+ MOVW m_g0(m), R3
+ CMP R3, g
+ BEQ 7(PC)
+ MOVW R13, (g_sched + gobuf_sp)(g)
+ MOVW $return<>(SB), R4
+ MOVW R4, (g_sched+gobuf_pc)(g)
+ MOVW g, (g_sched+gobuf_g)(g)
+ MOVW R3, g
+ MOVW (g_sched+gobuf_sp)(g), R13
+
+ // Now on a scheduling stack (a pthread-created stack).
+ SUB $24, R13
+ BIC $0x7, R13 // alignment for gcc ABI
+ MOVW R5, 20(R13) // save old g
+ MOVW R2, 16(R13) // save old SP
+ // R0 already contains the first argument
+ BL (R1)
+
+ // Restore registers, g, stack pointer.
+ MOVW 20(R13), g
+ MOVW 16(R13), R13
+ RET
-TEXT runtime·cgocallback(SB),7,$0
- B runtime·cgounimpl(SB)
+// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
+// Turn the fn into a Go func (by taking its address) and call
+// cgocallback_gofunc.
+TEXT runtime·cgocallback(SB),7,$12
+ MOVW $fn+0(FP), R0
+ MOVW R0, 4(R13)
+ MOVW frame+4(FP), R0
+ MOVW R0, 8(R13)
+ MOVW framesize+8(FP), R0
+ MOVW R0, 12(R13)
+ MOVW $runtime·cgocallback_gofunc(SB), R0
+ BL (R0)
+ RET
+
+// cgocallback_gofunc(void (*fn)(void*), void *frame, uintptr framesize)
+// See cgocall.c for more details.
+TEXT runtime·cgocallback_gofunc(SB),7,$16
+ // Load m and g from thread-local storage.
+ MOVW _cgo_load_gm(SB), R0
+ CMP $0, R0
+ BL.NE (R0)
+
+ // If m is nil, Go did not create the current thread.
+ // Call needm to obtain one for temporary use.
+ // In this case, we're running on the thread stack, so there's
+ // lots of space, but the linker doesn't know. Hide the call from
+ // the linker analysis by using an indirect call.
+ MOVW m, savedm-16(SP)
+ CMP $0, m
+ B.NE havem
+ MOVW $runtime·needm(SB), R0
+ BL (R0)
+
+havem:
+ // Now there's a valid m, and we're running on its m->g0.
+ // Save current m->g0->sched.sp on stack and then set it to SP.
+ // Save current sp in m->g0->sched.sp in preparation for
+ // switch back to m->curg stack.
+ MOVW fn+0(FP), R0
+ MOVW frame+4(FP), R1
+ MOVW framesize+8(FP), R2
+
+ MOVW m_g0(m), R3
+ MOVW (g_sched+gobuf_sp)(R3), R4
+ MOVW.W R4, -4(R13)
+ MOVW R13, (g_sched+gobuf_sp)(R3)
+
+ // Switch to m->curg stack and call runtime.cgocallbackg
+ // with the three arguments. Because we are taking over
+ // the execution of m->curg but *not* resuming what had
+ // been running, we need to save that information (m->curg->gobuf)
+ // so that we can restore it when we're done.
+ // We can restore m->curg->gobuf.sp easily, because calling
+ // runtime.cgocallbackg leaves SP unchanged upon return.
+ // To save m->curg->gobuf.pc, we push it onto the stack.
+ // This has the added benefit that it looks to the traceback
+ // routine like cgocallbackg is going to return to that
+ // PC (because we defined cgocallbackg to have
+ // a frame size of 16, the same amount that we use below),
+ // so that the traceback will seamlessly trace back into
+ // the earlier calls.
+
+ // Save current m->g0->sched.sp on stack and then set it to SP.
+ MOVW m_curg(m), g
+ MOVW (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
+
+ // Push gobuf.pc
+ MOVW (g_sched+gobuf_pc)(g), R5
+ SUB $4, R4
+ MOVW R5, 0(R4)
+
+ // Push arguments to cgocallbackg.
+ // Frame size here must match the frame size above
+ // to trick traceback routines into doing the right thing.
+ SUB $16, R4
+ MOVW R0, 4(R4)
+ MOVW R1, 8(R4)
+ MOVW R2, 12(R4)
+
+ // Switch stack and make the call.
+ MOVW R4, R13
+ BL runtime·cgocallbackg(SB)
+
+ // Restore g->gobuf (== m->curg->gobuf) from saved values.
+ MOVW 16(R13), R5
+ MOVW R5, (g_sched+gobuf_pc)(g)
+ ADD $(16+4), R13 // SP clobbered! It is ok!
+ MOVW R13, (g_sched+gobuf_sp)(g)
+
+ // Switch back to m->g0's stack and restore m->g0->sched.sp.
+ // (Unlike m->curg, the g0 goroutine never uses sched.pc,
+ // so we do not have to restore it.)
+ MOVW m_g0(m), g
+ MOVW (g_sched+gobuf_sp)(g), R13
+ // POP R6
+ MOVW 0(R13), R6
+ ADD $4, R13
+ MOVW R6, (g_sched+gobuf_sp)(g)
+
+ // If the m on entry was nil, we called needm above to borrow an m
+ // for the duration of the call. Since the call is over, return it with dropm.
+ MOVW savedm-16(SP), R6
+ CMP $0, R6
+ B.NE 3(PC)
+ MOVW $runtime·dropm(SB), R0
+ BL (R0)
+
+ // Done!
+ RET
+
+// void setmg(M*, G*); set m and g. for use by needm.
+TEXT runtime·setmg(SB), 7, $-4
+ MOVW mm+0(FP), m
+ MOVW gg+4(FP), g
+
+ // Save m and g to thread-local storage.
+ MOVW _cgo_save_gm(SB), R0
+ CMP $0, R0
+ BL.NE (R0)
-TEXT runtime·memclr(SB),7,$20
- MOVW 0(FP), R0
- MOVW $0, R1 // c = 0
- MOVW R1, -16(SP)
- MOVW 4(FP), R1 // n
- MOVW R1, -12(SP)
- MOVW m, -8(SP) // Save m and g
- MOVW g, -4(SP)
- BL runtime·memset(SB)
- MOVW -8(SP), m // Restore m and g, memset clobbers them
- MOVW -4(SP), g
RET
TEXT runtime·getcallerpc(SB),7,$-4
@@ -269,16 +448,6 @@ TEXT runtime·getcallersp(SB),7,$-4
TEXT runtime·emptyfunc(SB),0,$0
RET
-// int64 runtime·cputicks(), so really
-// void runtime·cputicks(int64 *ticks)
-// stubbed: return int64(0)
-TEXT runtime·cputicks(SB),7,$0
- MOVW 0(FP), R1
- MOVW $0, R0
- MOVW R0, 0(R1)
- MOVW R0, 4(R1)
- RET
-
TEXT runtime·abort(SB),7,$-4
MOVW $0, R0
MOVW (R0), R1
diff --git a/src/pkg/runtime/atomic_386.c b/src/pkg/runtime/atomic_386.c
index a4f2a114f..79b7cbf96 100644
--- a/src/pkg/runtime/atomic_386.c
+++ b/src/pkg/runtime/atomic_386.c
@@ -17,3 +17,16 @@ runtime·atomicloadp(void* volatile* addr)
{
return *addr;
}
+
+#pragma textflag 7
+uint64
+runtime·xadd64(uint64 volatile* addr, int64 v)
+{
+ uint64 old;
+
+ old = *addr;
+ while(!runtime·cas64(addr, &old, old+v)) {
+ // nothing
+ }
+ return old+v;
+}
diff --git a/src/pkg/runtime/atomic_amd64.c b/src/pkg/runtime/atomic_amd64.c
index a4f2a114f..e92d8ec21 100644
--- a/src/pkg/runtime/atomic_amd64.c
+++ b/src/pkg/runtime/atomic_amd64.c
@@ -12,6 +12,13 @@ runtime·atomicload(uint32 volatile* addr)
}
#pragma textflag 7
+uint64
+runtime·atomicload64(uint64 volatile* addr)
+{
+ return *addr;
+}
+
+#pragma textflag 7
void*
runtime·atomicloadp(void* volatile* addr)
{
diff --git a/src/pkg/runtime/atomic_arm.c b/src/pkg/runtime/atomic_arm.c
index 52e4059ae..0b54840cc 100644
--- a/src/pkg/runtime/atomic_arm.c
+++ b/src/pkg/runtime/atomic_arm.c
@@ -3,6 +3,14 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
+#include "arch_GOARCH.h"
+
+static union {
+ Lock l;
+ byte pad [CacheLineSize];
+} locktab[57];
+
+#define LOCK(addr) (&locktab[((uintptr)(addr)>>3)%nelem(locktab)].l)
// Atomic add and return new value.
#pragma textflag 7
@@ -80,4 +88,56 @@ runtime·atomicstore(uint32 volatile* addr, uint32 v)
if(runtime·cas(addr, old, v))
return;
}
-} \ No newline at end of file
+}
+
+#pragma textflag 7
+bool
+runtime·cas64(uint64 volatile *addr, uint64 *old, uint64 new)
+{
+ bool res;
+
+ runtime·lock(LOCK(addr));
+ if(*addr == *old) {
+ *addr = new;
+ res = true;
+ } else {
+ *old = *addr;
+ res = false;
+ }
+ runtime·unlock(LOCK(addr));
+ return res;
+}
+
+#pragma textflag 7
+uint64
+runtime·xadd64(uint64 volatile *addr, int64 delta)
+{
+ uint64 res;
+
+ runtime·lock(LOCK(addr));
+ res = *addr + delta;
+ *addr = res;
+ runtime·unlock(LOCK(addr));
+ return res;
+}
+
+#pragma textflag 7
+uint64
+runtime·atomicload64(uint64 volatile *addr)
+{
+ uint64 res;
+
+ runtime·lock(LOCK(addr));
+ res = *addr;
+ runtime·unlock(LOCK(addr));
+ return res;
+}
+
+#pragma textflag 7
+void
+runtime·atomicstore64(uint64 volatile *addr, uint64 v)
+{
+ runtime·lock(LOCK(addr));
+ *addr = v;
+ runtime·unlock(LOCK(addr));
+}
diff --git a/src/pkg/runtime/callback_windows_386.c b/src/pkg/runtime/callback_windows_386.c
index fcd292fbc..880588da6 100644
--- a/src/pkg/runtime/callback_windows_386.c
+++ b/src/pkg/runtime/callback_windows_386.c
@@ -4,6 +4,7 @@
#include "runtime.h"
#include "type.h"
+#include "typekind.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
@@ -79,7 +80,7 @@ runtime·compilecallback(Eface fn, bool cleanstack)
// MOVL fn, AX
*p++ = 0xb8;
- *(uint32*)p = (uint32)fn.data;
+ *(uint32*)p = (uint32)(fn.data);
p += 4;
// MOVL argsize, DX
diff --git a/src/pkg/runtime/callback_windows_amd64.c b/src/pkg/runtime/callback_windows_amd64.c
index 99d7cb9e3..1a4779291 100644
--- a/src/pkg/runtime/callback_windows_amd64.c
+++ b/src/pkg/runtime/callback_windows_amd64.c
@@ -4,6 +4,7 @@
#include "runtime.h"
#include "type.h"
+#include "typekind.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
@@ -77,7 +78,7 @@ runtime·compilecallback(Eface fn, bool /*cleanstack*/)
// MOVQ fn, AX
*p++ = 0x48;
*p++ = 0xb8;
- *(uint64*)p = (uint64)fn.data;
+ *(uint64*)p = (uint64)(fn.data);
p += 8;
// PUSH AX
*p++ = 0x50;
diff --git a/src/pkg/runtime/cgo/asm_386.s b/src/pkg/runtime/cgo/asm_386.s
new file mode 100644
index 000000000..7faaa4097
--- /dev/null
+++ b/src/pkg/runtime/cgo/asm_386.s
@@ -0,0 +1,29 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * void crosscall2(void (*fn)(void*, int32), void*, int32)
+ * Save registers and call fn with two arguments.
+ */
+TEXT crosscall2(SB),7,$0
+ PUSHL BP
+ MOVL SP, BP
+ PUSHL BX
+ PUSHL SI
+ PUSHL DI
+
+ SUBL $8, SP
+ MOVL 16(BP), AX
+ MOVL AX, 4(SP)
+ MOVL 12(BP), AX
+ MOVL AX, 0(SP)
+ MOVL 8(BP), AX
+ CALL AX
+ ADDL $8, SP
+
+ POPL DI
+ POPL SI
+ POPL BX
+ POPL BP
+ RET
diff --git a/src/pkg/runtime/cgo/asm_amd64.s b/src/pkg/runtime/cgo/asm_amd64.s
new file mode 100644
index 000000000..53f7148a2
--- /dev/null
+++ b/src/pkg/runtime/cgo/asm_amd64.s
@@ -0,0 +1,45 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * void crosscall2(void (*fn)(void*, int32), void*, int32)
+ * Save registers and call fn with two arguments.
+ */
+TEXT crosscall2(SB),7,$0
+ SUBQ $0x58, SP /* keeps stack pointer 32-byte aligned */
+ MOVQ BX, 0x10(SP)
+ MOVQ BP, 0x18(SP)
+ MOVQ R12, 0x20(SP)
+ MOVQ R13, 0x28(SP)
+ MOVQ R14, 0x30(SP)
+ MOVQ R15, 0x38(SP)
+
+#ifdef GOOS_windows
+ // Win64 save RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15
+ MOVQ DI, 0x40(SP)
+ MOVQ SI, 0x48(SP)
+
+ MOVQ DX, 0(SP) /* arg */
+ MOVQ R8, 8(SP) /* argsize (includes padding) */
+
+ CALL CX /* fn */
+
+ MOVQ 0x40(SP), DI
+ MOVQ 0x48(SP), SI
+#else
+ MOVQ SI, 0(SP) /* arg */
+ MOVQ DX, 8(SP) /* argsize (includes padding) */
+
+ CALL DI /* fn */
+#endif
+
+ MOVQ 0x10(SP), BX
+ MOVQ 0x18(SP), BP
+ MOVQ 0x20(SP), R12
+ MOVQ 0x28(SP), R13
+ MOVQ 0x30(SP), R14
+ MOVQ 0x38(SP), R15
+
+ ADDQ $0x58, SP
+ RET
diff --git a/src/pkg/runtime/cgo/asm_arm.s b/src/pkg/runtime/cgo/asm_arm.s
new file mode 100644
index 000000000..a6ea0dc07
--- /dev/null
+++ b/src/pkg/runtime/cgo/asm_arm.s
@@ -0,0 +1,23 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * void crosscall2(void (*fn)(void*, int32), void*, int32)
+ * Save registers and call fn with two arguments.
+ */
+TEXT crosscall2(SB),7,$-4
+ /*
+ * We still need to save all callee save register as before, and then
+ * push 2 args for fn (R1 and R2).
+ * Also note that at procedure entry in 5c/5g world, 4(R13) will be the
+ * first arg, so we must push another dummy reg (R0) for 0(R13).
+ * Additionally, cgo_tls_set_gm will clobber R0, so we need to save R0
+ * nevertheless.
+ */
+ MOVM.WP [R0, R1, R2, R4, R5, R6, R7, R8, R9, R10, R11, R12, R14], (R13)
+ MOVW _cgo_load_gm(SB), R0
+ BL (R0)
+ MOVW PC, R14
+ MOVW 0(R13), PC
+ MOVM.IAW (R13), [R0, R1, R2, R4, R5, R6, R7, R8, R9, R10, R11, R12, PC]
diff --git a/src/pkg/runtime/cgo/callbacks.c b/src/pkg/runtime/cgo/callbacks.c
index f36fb3fd7..51bd529ec 100644
--- a/src/pkg/runtime/cgo/callbacks.c
+++ b/src/pkg/runtime/cgo/callbacks.c
@@ -33,7 +33,13 @@
static void
_cgo_allocate_internal(uintptr len, byte *ret)
{
+ CgoMal *c;
+
ret = runtime·mal(len);
+ c = runtime·mal(sizeof(*c));
+ c->next = m->cgomal;
+ c->alloc = ret;
+ m->cgomal = c;
FLUSH(&ret);
}
@@ -71,3 +77,19 @@ _cgo_panic(void *a, int32 n)
{
runtime·cgocallback((void(*)(void))_cgo_panic_internal, a, n);
}
+
+#pragma cgo_import_static x_cgo_init
+extern void x_cgo_init(G*);
+void (*_cgo_init)(G*) = x_cgo_init;
+
+#pragma cgo_import_static x_cgo_malloc
+extern void x_cgo_malloc(void*);
+void (*_cgo_malloc)(void*) = x_cgo_malloc;
+
+#pragma cgo_import_static x_cgo_free
+extern void x_cgo_free(void*);
+void (*_cgo_free)(void*) = x_cgo_free;
+
+#pragma cgo_import_static x_cgo_thread_start
+extern void x_cgo_thread_start(void*);
+void (*_cgo_thread_start)(void*) = x_cgo_thread_start;
diff --git a/src/pkg/runtime/cgo/cgo.go b/src/pkg/runtime/cgo/cgo.go
index 414f3da36..e0d538668 100644
--- a/src/pkg/runtime/cgo/cgo.go
+++ b/src/pkg/runtime/cgo/cgo.go
@@ -18,6 +18,8 @@ package cgo
#cgo openbsd LDFLAGS: -lpthread
#cgo windows LDFLAGS: -lm -mthreads
+#cgo CFLAGS: -Wall -Werror
+
*/
import "C"
diff --git a/src/pkg/runtime/cgo/cgo_arm.c b/src/pkg/runtime/cgo/cgo_arm.c
new file mode 100644
index 000000000..d23f53e77
--- /dev/null
+++ b/src/pkg/runtime/cgo/cgo_arm.c
@@ -0,0 +1,12 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#pragma cgo_import_static x_cgo_load_gm
+extern void x_cgo_load_gm(void);
+void (*_cgo_load_gm)(void) = x_cgo_load_gm;
+
+#pragma cgo_import_static x_cgo_save_gm
+extern void x_cgo_save_gm(void);
+void (*_cgo_save_gm)(void) = x_cgo_save_gm;
+
diff --git a/src/pkg/runtime/cgo/gcc_386.S b/src/pkg/runtime/cgo/gcc_386.S
index 9abab7ebd..94ba5842f 100644
--- a/src/pkg/runtime/cgo/gcc_386.S
+++ b/src/pkg/runtime/cgo/gcc_386.S
@@ -35,31 +35,6 @@ EXT(crosscall_386):
popl %ebp
ret
-/*
- * void crosscall2(void (*fn)(void*, int32), void*, int32)
- *
- * Save registers and call fn with two arguments.
- */
-.globl EXT(crosscall2)
-EXT(crosscall2):
- pushl %ebp
- movl %esp, %ebp
- pushl %ebx
- pushl %esi
- pushl %edi
-
- pushl 16(%ebp)
- pushl 12(%ebp)
- mov 8(%ebp), %eax
- call *%eax
- addl $8,%esp
-
- popl %edi
- popl %esi
- popl %ebx
- popl %ebp
- ret
-
.globl EXT(__stack_chk_fail_local)
EXT(__stack_chk_fail_local):
1:
diff --git a/src/pkg/runtime/cgo/gcc_amd64.S b/src/pkg/runtime/cgo/gcc_amd64.S
index 706ee6b58..81b270195 100644
--- a/src/pkg/runtime/cgo/gcc_amd64.S
+++ b/src/pkg/runtime/cgo/gcc_amd64.S
@@ -19,9 +19,6 @@
* are callee-save so they must be saved explicitly.
* The standard x86-64 ABI passes the three arguments m, g, fn
* in %rdi, %rsi, %rdx.
- *
- * Also need to set %r15 to g and %r14 to m (see ../pkg/runtime/mkasmh.sh)
- * during the call.
*/
.globl EXT(crosscall_amd64)
EXT(crosscall_amd64):
@@ -45,48 +42,3 @@ EXT(crosscall_amd64):
popq %rbp
popq %rbx
ret
-
-/*
- * void crosscall2(void (*fn)(void*, int32), void *arg, int32 argsize)
- *
- * Save registers and call fn with two arguments. fn is a Go function
- * which takes parameters on the stack rather than in registers.
- */
-.globl EXT(crosscall2)
-EXT(crosscall2):
- subq $0x58, %rsp /* keeps stack pointer 32-byte aligned */
- movq %rbx, 0x10(%rsp)
- movq %rbp, 0x18(%rsp)
- movq %r12, 0x20(%rsp)
- movq %r13, 0x28(%rsp)
- movq %r14, 0x30(%rsp)
- movq %r15, 0x38(%rsp)
-
-#if defined(_WIN64)
- // Win64 save RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15
- movq %rdi, 0x40(%rsp)
- movq %rsi, 0x48(%rsp)
-
- movq %rdx, 0(%rsp) /* arg */
- movq %r8, 8(%rsp) /* argsize (includes padding) */
-
- call *%rcx /* fn */
-#else
- movq %rsi, 0(%rsp) /* arg */
- movq %rdx, 8(%rsp) /* argsize (includes padding) */
-
- call *%rdi /* fn */
-#endif
-
- movq 0x10(%rsp), %rbx
- movq 0x18(%rsp), %rbp
- movq 0x20(%rsp), %r12
- movq 0x28(%rsp), %r13
- movq 0x30(%rsp), %r14
- movq 0x38(%rsp), %r15
-#if defined(__WIN64)
- movq 0x40(%rsp), %rdi
- movq 0x48(%rsp), %rsi
-#endif
- addq $0x58, %rsp
- ret
diff --git a/src/pkg/runtime/cgo/gcc_arm.S b/src/pkg/runtime/cgo/gcc_arm.S
index 32d862984..809fcb9a0 100644
--- a/src/pkg/runtime/cgo/gcc_arm.S
+++ b/src/pkg/runtime/cgo/gcc_arm.S
@@ -1 +1,36 @@
-/* unimplemented */
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * Apple still insists on underscore prefixes for C function names.
+ */
+#if defined(__APPLE__)
+#define EXT(s) _##s
+#else
+#define EXT(s) s
+#endif
+
+/*
+ * void crosscall_arm2(void (*fn)(void), void *g, void *m)
+ *
+ * Calling into the 5c tool chain, where all registers are caller save.
+ * Called from standard ARM EABI, where r4-r11 are callee-save, so they
+ * must be saved explicitly.
+ */
+.globl EXT(crosscall_arm2)
+EXT(crosscall_arm2):
+ push {r4, r5, r6, r7, r8, r9, r10, r11, ip, lr}
+ mov r10, r1 // g
+ mov r9, r2 // m
+ mov r3, r0 // save r0, cgo_tls_set_gm will clobber it
+ bl EXT(x_cgo_save_gm) // save current g and m into TLS variable
+ mov lr, pc
+ mov pc, r3
+ pop {r4, r5, r6, r7, r8, r9, r10, r11, ip, pc}
+
+.globl EXT(__stack_chk_fail_local)
+EXT(__stack_chk_fail_local):
+1:
+ b 1b
+
diff --git a/src/pkg/runtime/cgo/gcc_darwin_386.c b/src/pkg/runtime/cgo/gcc_darwin_386.c
index 2c30c666f..ad9fb5abf 100644
--- a/src/pkg/runtime/cgo/gcc_darwin_386.c
+++ b/src/pkg/runtime/cgo/gcc_darwin_386.c
@@ -101,8 +101,8 @@ inittls(void)
pthread_key_delete(tofree[i]);
}
-static void
-xinitcgo(G *g)
+void
+x_cgo_init(G *g)
{
pthread_attr_t attr;
size_t size;
@@ -115,10 +115,9 @@ xinitcgo(G *g)
inittls();
}
-void (*initcgo)(G*) = xinitcgo;
void
-libcgo_sys_thread_start(ThreadStart *ts)
+_cgo_sys_thread_start(ThreadStart *ts)
{
pthread_attr_t attr;
sigset_t ign, oset;
@@ -127,14 +126,14 @@ libcgo_sys_thread_start(ThreadStart *ts)
int err;
sigfillset(&ign);
- sigprocmask(SIG_SETMASK, &ign, &oset);
+ pthread_sigmask(SIG_SETMASK, &ign, &oset);
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
ts->g->stackguard = size;
err = pthread_create(&p, &attr, threadentry, ts);
- sigprocmask(SIG_SETMASK, &oset, nil);
+ pthread_sigmask(SIG_SETMASK, &oset, nil);
if (err != 0) {
fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
@@ -153,7 +152,7 @@ threadentry(void *v)
ts.g->stackbase = (uintptr)&ts;
/*
- * libcgo_sys_thread_start set stackguard to stack size;
+ * _cgo_sys_thread_start set stackguard to stack size;
* change to actual guard pointer.
*/
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
diff --git a/src/pkg/runtime/cgo/gcc_darwin_amd64.c b/src/pkg/runtime/cgo/gcc_darwin_amd64.c
index 89dc7a4e8..65d381633 100644
--- a/src/pkg/runtime/cgo/gcc_darwin_amd64.c
+++ b/src/pkg/runtime/cgo/gcc_darwin_amd64.c
@@ -72,7 +72,7 @@ inittls(void)
}
void
-xinitcgo(G *g)
+x_cgo_init(G *g)
{
pthread_attr_t attr;
size_t size;
@@ -85,10 +85,9 @@ xinitcgo(G *g)
inittls();
}
-void (*initcgo)(G*) = xinitcgo;
void
-libcgo_sys_thread_start(ThreadStart *ts)
+_cgo_sys_thread_start(ThreadStart *ts)
{
pthread_attr_t attr;
sigset_t ign, oset;
@@ -97,14 +96,14 @@ libcgo_sys_thread_start(ThreadStart *ts)
int err;
sigfillset(&ign);
- sigprocmask(SIG_SETMASK, &ign, &oset);
+ pthread_sigmask(SIG_SETMASK, &ign, &oset);
pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
ts->g->stackguard = size;
err = pthread_create(&p, &attr, threadentry, ts);
- sigprocmask(SIG_SETMASK, &oset, nil);
+ pthread_sigmask(SIG_SETMASK, &oset, nil);
if (err != 0) {
fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
@@ -123,7 +122,7 @@ threadentry(void *v)
ts.g->stackbase = (uintptr)&ts;
/*
- * libcgo_sys_thread_start set stackguard to stack size;
+ * _cgo_sys_thread_start set stackguard to stack size;
* change to actual guard pointer.
*/
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
diff --git a/src/pkg/runtime/cgo/gcc_freebsd_386.c b/src/pkg/runtime/cgo/gcc_freebsd_386.c
index 2c97e2a33..7c62a1bc4 100644
--- a/src/pkg/runtime/cgo/gcc_freebsd_386.c
+++ b/src/pkg/runtime/cgo/gcc_freebsd_386.c
@@ -6,12 +6,13 @@
#include <sys/signalvar.h>
#include <pthread.h>
#include <signal.h>
+#include <string.h>
#include "libcgo.h"
static void* threadentry(void*);
-static void
-xinitcgo(G *g)
+void
+x_cgo_init(G *g)
{
pthread_attr_t attr;
size_t size;
@@ -22,10 +23,9 @@ xinitcgo(G *g)
pthread_attr_destroy(&attr);
}
-void (*initcgo)(G*) = xinitcgo;
void
-libcgo_sys_thread_start(ThreadStart *ts)
+_cgo_sys_thread_start(ThreadStart *ts)
{
pthread_attr_t attr;
sigset_t ign, oset;
@@ -60,7 +60,7 @@ threadentry(void *v)
ts.g->stackbase = (uintptr)&ts;
/*
- * libcgo_sys_thread_start set stackguard to stack size;
+ * _cgo_sys_thread_start set stackguard to stack size;
* change to actual guard pointer.
*/
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
diff --git a/src/pkg/runtime/cgo/gcc_freebsd_amd64.c b/src/pkg/runtime/cgo/gcc_freebsd_amd64.c
index 3beb4d7bb..6be8bd251 100644
--- a/src/pkg/runtime/cgo/gcc_freebsd_amd64.c
+++ b/src/pkg/runtime/cgo/gcc_freebsd_amd64.c
@@ -6,12 +6,13 @@
#include <sys/signalvar.h>
#include <pthread.h>
#include <signal.h>
+#include <string.h>
#include "libcgo.h"
static void* threadentry(void*);
-static void
-xinitcgo(G *g)
+void
+x_cgo_init(G *g)
{
pthread_attr_t attr;
size_t size;
@@ -22,10 +23,9 @@ xinitcgo(G *g)
pthread_attr_destroy(&attr);
}
-void (*initcgo)(G*) = xinitcgo;
void
-libcgo_sys_thread_start(ThreadStart *ts)
+_cgo_sys_thread_start(ThreadStart *ts)
{
pthread_attr_t attr;
sigset_t ign, oset;
@@ -61,7 +61,7 @@ threadentry(void *v)
ts.g->stackbase = (uintptr)&ts;
/*
- * libcgo_sys_thread_start set stackguard to stack size;
+ * _cgo_sys_thread_start set stackguard to stack size;
* change to actual guard pointer.
*/
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
diff --git a/src/pkg/runtime/cgo/gcc_freebsd_arm.c b/src/pkg/runtime/cgo/gcc_freebsd_arm.c
new file mode 100644
index 000000000..3bcb0b270
--- /dev/null
+++ b/src/pkg/runtime/cgo/gcc_freebsd_arm.c
@@ -0,0 +1,114 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <pthread.h>
+#include <string.h>
+#include "libcgo.h"
+
+static void *threadentry(void*);
+
+// We have to resort to TLS variable to save g(R10) and
+// m(R9). One reason is that external code might trigger
+// SIGSEGV, and our runtime.sigtramp don't even know we
+// are in external code, and will continue to use R10/R9,
+// this might as well result in another SIGSEGV.
+// Note: all three functions will clobber R0, and the last
+// two can be called from 5c ABI code.
+void __aeabi_read_tp(void) __attribute__((naked));
+void x_cgo_save_gm(void) __attribute__((naked));
+void x_cgo_load_gm(void) __attribute__((naked));
+
+void
+__aeabi_read_tp(void)
+{
+ // read @ 0xffff1000
+ __asm__ __volatile__ (
+ "ldr r0, =0xffff1000\n\t"
+ "ldr r0, [r0]\n\t"
+ "mov pc, lr\n\t"
+ );
+}
+
+// g (R10) at 8(TP), m (R9) at 12(TP)
+void
+x_cgo_load_gm(void)
+{
+ __asm__ __volatile__ (
+ "push {lr}\n\t"
+ "bl __aeabi_read_tp\n\t"
+ "ldr r10, [r0, #8]\n\t"
+ "ldr r9, [r0, #12]\n\t"
+ "pop {pc}\n\t"
+ );
+}
+
+void
+x_cgo_save_gm(void)
+{
+ __asm__ __volatile__ (
+ "push {lr}\n\t"
+ "bl __aeabi_read_tp\n\t"
+ "str r10, [r0, #8]\n\t"
+ "str r9, [r0, #12]\n\t"
+ "pop {pc}\n\t"
+ );
+}
+
+void
+x_cgo_init(G *g)
+{
+ pthread_attr_t attr;
+ size_t size;
+ x_cgo_save_gm(); // save g and m for the initial thread
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ g->stackguard = (uintptr)&attr - size + 4096;
+ pthread_attr_destroy(&attr);
+}
+
+
+void
+_cgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ pthread_t p;
+ size_t size;
+ int err;
+
+ // Not sure why the memset is necessary here,
+ // but without it, we get a bogus stack size
+ // out of pthread_attr_getstacksize. C'est la Linux.
+ memset(&attr, 0, sizeof attr);
+ pthread_attr_init(&attr);
+ size = 0;
+ pthread_attr_getstacksize(&attr, &size);
+ ts->g->stackguard = size;
+ err = pthread_create(&p, &attr, threadentry, ts);
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
+}
+
+extern void crosscall_arm2(void (*fn)(void), void *g, void *m);
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * _cgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096 * 2;
+
+ crosscall_arm2(ts.fn, (void *)ts.g, (void *)ts.m);
+ return nil;
+}
diff --git a/src/pkg/runtime/cgo/gcc_linux_386.c b/src/pkg/runtime/cgo/gcc_linux_386.c
index 7d84acc11..9357a63f7 100644
--- a/src/pkg/runtime/cgo/gcc_linux_386.c
+++ b/src/pkg/runtime/cgo/gcc_linux_386.c
@@ -9,8 +9,8 @@
static void *threadentry(void*);
-static void
-xinitcgo(G *g)
+void
+x_cgo_init(G *g)
{
pthread_attr_t attr;
size_t size;
@@ -21,10 +21,9 @@ xinitcgo(G *g)
pthread_attr_destroy(&attr);
}
-void (*initcgo)(G*) = xinitcgo;
void
-libcgo_sys_thread_start(ThreadStart *ts)
+_cgo_sys_thread_start(ThreadStart *ts)
{
pthread_attr_t attr;
sigset_t ign, oset;
@@ -64,7 +63,7 @@ threadentry(void *v)
ts.g->stackbase = (uintptr)&ts;
/*
- * libcgo_sys_thread_start set stackguard to stack size;
+ * _cgo_sys_thread_start set stackguard to stack size;
* change to actual guard pointer.
*/
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
diff --git a/src/pkg/runtime/cgo/gcc_linux_amd64.c b/src/pkg/runtime/cgo/gcc_linux_amd64.c
index 28cbf78c5..bc76117d3 100644
--- a/src/pkg/runtime/cgo/gcc_linux_amd64.c
+++ b/src/pkg/runtime/cgo/gcc_linux_amd64.c
@@ -10,7 +10,7 @@
static void* threadentry(void*);
void
-xinitcgo(G* g)
+x_cgo_init(G* g)
{
pthread_attr_t attr;
size_t size;
@@ -21,10 +21,9 @@ xinitcgo(G* g)
pthread_attr_destroy(&attr);
}
-void (*initcgo)(G*) = xinitcgo;
void
-libcgo_sys_thread_start(ThreadStart *ts)
+_cgo_sys_thread_start(ThreadStart *ts)
{
pthread_attr_t attr;
sigset_t ign, oset;
@@ -59,7 +58,7 @@ threadentry(void *v)
ts.g->stackbase = (uintptr)&ts;
/*
- * libcgo_sys_thread_start set stackguard to stack size;
+ * _cgo_sys_thread_start set stackguard to stack size;
* change to actual guard pointer.
*/
ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
diff --git a/src/pkg/runtime/cgo/gcc_linux_arm.c b/src/pkg/runtime/cgo/gcc_linux_arm.c
index 8397c75bb..46a1126ad 100644
--- a/src/pkg/runtime/cgo/gcc_linux_arm.c
+++ b/src/pkg/runtime/cgo/gcc_linux_arm.c
@@ -2,19 +2,113 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include <pthread.h>
+#include <string.h>
#include "libcgo.h"
-static void
-xinitcgo(G *g)
+static void *threadentry(void*);
+
+// We have to resort to TLS variable to save g(R10) and
+// m(R9). One reason is that external code might trigger
+// SIGSEGV, and our runtime.sigtramp don't even know we
+// are in external code, and will continue to use R10/R9,
+// this might as well result in another SIGSEGV.
+// Note: all three functions will clobber R0, and the last
+// two can be called from 5c ABI code.
+void __aeabi_read_tp(void) __attribute__((naked));
+void x_cgo_save_gm(void) __attribute__((naked));
+void x_cgo_load_gm(void) __attribute__((naked));
+
+void
+__aeabi_read_tp(void)
+{
+ // b __kuser_get_tls @ 0xffff0fe0
+ __asm__ __volatile__ (
+ "mvn r0, #0xf000\n\t"
+ "sub pc, r0, #31\n\t"
+ "nop\n\tnop\n\t"
+ );
+}
+
+// g (R10) at 8(TP), m (R9) at 12(TP)
+void
+x_cgo_load_gm(void)
+{
+ __asm__ __volatile__ (
+ "push {lr}\n\t"
+ "bl __aeabi_read_tp\n\t"
+ "ldr r10, [r0, #8]\n\t"
+ "ldr r9, [r0, #12]\n\t"
+ "pop {pc}\n\t"
+ );
+}
+
+void
+x_cgo_save_gm(void)
+{
+ __asm__ __volatile__ (
+ "push {lr}\n\t"
+ "bl __aeabi_read_tp\n\t"
+ "str r10, [r0, #8]\n\t"
+ "str r9, [r0, #12]\n\t"
+ "pop {pc}\n\t"
+ );
+}
+
+void
+x_cgo_init(G *g)
{
- // unimplemented
+ pthread_attr_t attr;
+ size_t size;
+ x_cgo_save_gm(); // save g and m for the initial thread
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ g->stackguard = (uintptr)&attr - size + 4096;
+ pthread_attr_destroy(&attr);
}
-void (*initcgo)(G*) = xinitcgo;
void
-libcgo_sys_thread_start(ThreadStart *ts)
+_cgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ pthread_t p;
+ size_t size;
+ int err;
+
+ // Not sure why the memset is necessary here,
+ // but without it, we get a bogus stack size
+ // out of pthread_attr_getstacksize. C'est la Linux.
+ memset(&attr, 0, sizeof attr);
+ pthread_attr_init(&attr);
+ size = 0;
+ pthread_attr_getstacksize(&attr, &size);
+ ts->g->stackguard = size;
+ err = pthread_create(&p, &attr, threadentry, ts);
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
+}
+
+extern void crosscall_arm2(void (*fn)(void), void *g, void *m);
+static void*
+threadentry(void *v)
{
- // unimplemented
- *(int*)0 = 0;
+ ThreadStart ts;
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * _cgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096 * 2;
+
+ crosscall_arm2(ts.fn, (void *)ts.g, (void *)ts.m);
+ return nil;
}
diff --git a/src/pkg/runtime/cgo/gcc_netbsd_386.c b/src/pkg/runtime/cgo/gcc_netbsd_386.c
new file mode 100644
index 000000000..09b271df4
--- /dev/null
+++ b/src/pkg/runtime/cgo/gcc_netbsd_386.c
@@ -0,0 +1,80 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <sys/types.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include "libcgo.h"
+
+static void* threadentry(void*);
+
+void
+x_cgo_init(G *g)
+{
+ pthread_attr_t attr;
+ size_t size;
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ g->stackguard = (uintptr)&attr - size + 4096;
+ pthread_attr_destroy(&attr);
+}
+
+
+void
+_cgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ sigset_t ign, oset;
+ pthread_t p;
+ size_t size;
+ int err;
+
+ sigfillset(&ign);
+ sigprocmask(SIG_SETMASK, &ign, &oset);
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ ts->g->stackguard = size;
+ err = pthread_create(&p, &attr, threadentry, ts);
+
+ sigprocmask(SIG_SETMASK, &oset, nil);
+
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
+}
+
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * _cgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
+
+ /*
+ * Set specific keys. On NetBSD/ELF, the thread local storage
+ * is just before %gs:0. Our dynamic 8.out's reserve 8 bytes
+ * for the two words g and m at %gs:-8 and %gs:-4.
+ */
+ asm volatile (
+ "movl %0, %%gs:-8\n" // MOVL g, -8(GS)
+ "movl %1, %%gs:-4\n" // MOVL m, -4(GS)
+ :: "r"(ts.g), "r"(ts.m)
+ );
+
+ crosscall_386(ts.fn);
+ return nil;
+}
diff --git a/src/pkg/runtime/cgo/gcc_netbsd_amd64.c b/src/pkg/runtime/cgo/gcc_netbsd_amd64.c
new file mode 100644
index 000000000..080c59ba4
--- /dev/null
+++ b/src/pkg/runtime/cgo/gcc_netbsd_amd64.c
@@ -0,0 +1,80 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <sys/types.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include "libcgo.h"
+
+static void* threadentry(void*);
+
+void
+x_cgo_init(G *g)
+{
+ pthread_attr_t attr;
+ size_t size;
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ g->stackguard = (uintptr)&attr - size + 4096;
+ pthread_attr_destroy(&attr);
+}
+
+
+void
+_cgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ sigset_t ign, oset;
+ pthread_t p;
+ size_t size;
+ int err;
+
+ sigfillset(&ign);
+ sigprocmask(SIG_SETMASK, &ign, &oset);
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+
+ ts->g->stackguard = size;
+ err = pthread_create(&p, &attr, threadentry, ts);
+
+ sigprocmask(SIG_SETMASK, &oset, nil);
+
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
+}
+
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * _cgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
+
+ /*
+ * Set specific keys. On NetBSD/ELF, the thread local storage
+ * is just before %fs:0. Our dynamic 6.out's reserve 16 bytes
+ * for the two words g and m at %fs:-16 and %fs:-8.
+ */
+ asm volatile (
+ "movq %0, %%fs:-16\n" // MOVL g, -16(FS)
+ "movq %1, %%fs:-8\n" // MOVL m, -8(FS)
+ :: "r"(ts.g), "r"(ts.m)
+ );
+ crosscall_amd64(ts.fn);
+ return nil;
+}
diff --git a/src/pkg/runtime/cgo/gcc_netbsd_arm.c b/src/pkg/runtime/cgo/gcc_netbsd_arm.c
new file mode 100644
index 000000000..d93b531e7
--- /dev/null
+++ b/src/pkg/runtime/cgo/gcc_netbsd_arm.c
@@ -0,0 +1,122 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <sys/types.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include "libcgo.h"
+
+static void *threadentry(void*);
+
+// We have to resort to TLS variable to save g(R10) and
+// m(R9). One reason is that external code might trigger
+// SIGSEGV, and our runtime.sigtramp don't even know we
+// are in external code, and will continue to use R10/R9,
+// this might as well result in another SIGSEGV.
+// Note: all three functions will clobber R0, and the last
+// two can be called from 5c ABI code.
+void __aeabi_read_tp(void) __attribute__((naked));
+void x_cgo_save_gm(void) __attribute__((naked));
+void x_cgo_load_gm(void) __attribute__((naked));
+
+void
+__aeabi_read_tp(void)
+{
+ // this function is only allowed to clobber r0
+ __asm__ __volatile__ (
+ "mrc p15, 0, r0, c13, c0, 3\n\t"
+ "cmp r0, #0\n\t"
+ "movne pc, lr\n\t"
+ "push {r1,r2,r3,r12}\n\t"
+ "svc 0x00a0013c\n\t" // _lwp_getprivate
+ "pop {r1,r2,r3,r12}\n\t"
+ "mov pc, lr\n\t"
+ );
+}
+
+// g (R10) at 8(TP), m (R9) at 12(TP)
+void
+x_cgo_load_gm(void)
+{
+ __asm__ __volatile__ (
+ "push {lr}\n\t"
+ "bl __aeabi_read_tp\n\t"
+ "ldr r10, [r0, #8]\n\t"
+ "ldr r9, [r0, #12]\n\t"
+ "pop {pc}\n\t"
+ );
+}
+
+void
+x_cgo_save_gm(void)
+{
+ __asm__ __volatile__ (
+ "push {lr}\n\t"
+ "bl __aeabi_read_tp\n\t"
+ "str r10, [r0, #8]\n\t"
+ "str r9, [r0, #12]\n\t"
+ "pop {pc}\n\t"
+ );
+}
+
+void
+x_cgo_init(G *g)
+{
+ pthread_attr_t attr;
+ size_t size;
+ x_cgo_save_gm(); // save g and m for the initial thread
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ g->stackguard = (uintptr)&attr - size + 4096;
+ pthread_attr_destroy(&attr);
+}
+
+
+void
+_cgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ sigset_t ign, oset;
+ pthread_t p;
+ size_t size;
+ int err;
+
+ sigfillset(&ign);
+ sigprocmask(SIG_SETMASK, &ign, &oset);
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ ts->g->stackguard = size;
+ err = pthread_create(&p, &attr, threadentry, ts);
+
+ sigprocmask(SIG_SETMASK, &oset, nil);
+
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
+}
+
+extern void crosscall_arm2(void (*fn)(void), void *g, void *m);
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * _cgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096 * 2;
+
+ crosscall_arm2(ts.fn, (void *)ts.g, (void *)ts.m);
+ return nil;
+}
diff --git a/src/pkg/runtime/cgo/gcc_openbsd_386.c b/src/pkg/runtime/cgo/gcc_openbsd_386.c
new file mode 100644
index 000000000..86c1365ad
--- /dev/null
+++ b/src/pkg/runtime/cgo/gcc_openbsd_386.c
@@ -0,0 +1,169 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <sys/types.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include "libcgo.h"
+
+static void* threadentry(void*);
+
+// TCB_SIZE is sizeof(struct thread_control_block),
+// as defined in /usr/src/lib/librthread/tcb.h
+#define TCB_SIZE (4 * sizeof(void *))
+#define TLS_SIZE (2 * sizeof(void *))
+
+void *__get_tcb(void);
+void __set_tcb(void *);
+
+static int (*sys_pthread_create)(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg);
+
+struct thread_args {
+ void *(*func)(void *);
+ void *arg;
+};
+
+static void
+tcb_fixup(int mainthread)
+{
+ void *newtcb, *oldtcb;
+
+ // The OpenBSD ld.so(1) does not currently support PT_TLS. As a result,
+ // we need to allocate our own TLS space while preserving the existing
+ // TCB that has been setup via librthread.
+
+ newtcb = malloc(TCB_SIZE + TLS_SIZE);
+ if(newtcb == NULL)
+ abort();
+
+ // The signal trampoline expects the TLS slots to be zeroed.
+ bzero(newtcb, TLS_SIZE);
+
+ oldtcb = __get_tcb();
+ bcopy(oldtcb, newtcb + TLS_SIZE, TCB_SIZE);
+ __set_tcb(newtcb + TLS_SIZE);
+
+ // The main thread TCB is a static allocation - do not try to free it.
+ if(!mainthread)
+ free(oldtcb);
+}
+
+static void *
+thread_start_wrapper(void *arg)
+{
+ struct thread_args args = *(struct thread_args *)arg;
+
+ free(arg);
+ tcb_fixup(0);
+
+ return args.func(args.arg);
+}
+
+int
+pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg)
+{
+ struct thread_args *p;
+
+ p = malloc(sizeof(*p));
+ if(p == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ p->func = start_routine;
+ p->arg = arg;
+
+ return sys_pthread_create(thread, attr, thread_start_wrapper, p);
+}
+
+void
+x_cgo_init(G *g)
+{
+ pthread_attr_t attr;
+ size_t size;
+ void *handle;
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ g->stackguard = (uintptr)&attr - size + 4096;
+ pthread_attr_destroy(&attr);
+
+ // Locate symbol for the system pthread_create function.
+ handle = dlopen("libpthread.so", RTLD_LAZY);
+ if(handle == NULL) {
+ fprintf(stderr, "dlopen: failed to load libpthread: %s\n", dlerror());
+ abort();
+ }
+ sys_pthread_create = dlsym(handle, "pthread_create");
+ if(sys_pthread_create == NULL) {
+ fprintf(stderr, "dlsym: failed to find pthread_create: %s\n", dlerror());
+ abort();
+ }
+ dlclose(handle);
+
+ tcb_fixup(1);
+}
+
+
+void
+_cgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ sigset_t ign, oset;
+ pthread_t p;
+ size_t size;
+ int err;
+
+ sigfillset(&ign);
+ sigprocmask(SIG_SETMASK, &ign, &oset);
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ ts->g->stackguard = size;
+ err = sys_pthread_create(&p, &attr, threadentry, ts);
+
+ sigprocmask(SIG_SETMASK, &oset, nil);
+
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
+}
+
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ tcb_fixup(0);
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * _cgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
+
+ /*
+ * Set specific keys. On OpenBSD/ELF, the thread local storage
+ * is just before %gs:0. Our dynamic 8.out's reserve 8 bytes
+ * for the two words g and m at %gs:-8 and %gs:-4.
+ */
+ asm volatile (
+ "movl %0, %%gs:-8\n" // MOVL g, -8(GS)
+ "movl %1, %%gs:-4\n" // MOVL m, -4(GS)
+ :: "r"(ts.g), "r"(ts.m)
+ );
+
+ crosscall_386(ts.fn);
+ return nil;
+}
diff --git a/src/pkg/runtime/cgo/gcc_openbsd_amd64.c b/src/pkg/runtime/cgo/gcc_openbsd_amd64.c
new file mode 100644
index 000000000..d3a5e36b0
--- /dev/null
+++ b/src/pkg/runtime/cgo/gcc_openbsd_amd64.c
@@ -0,0 +1,169 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <sys/types.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include "libcgo.h"
+
+static void* threadentry(void*);
+
+// TCB_SIZE is sizeof(struct thread_control_block),
+// as defined in /usr/src/lib/librthread/tcb.h
+#define TCB_SIZE (4 * sizeof(void *))
+#define TLS_SIZE (2 * sizeof(void *))
+
+void *__get_tcb(void);
+void __set_tcb(void *);
+
+static int (*sys_pthread_create)(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg);
+
+struct thread_args {
+ void *(*func)(void *);
+ void *arg;
+};
+
+static void
+tcb_fixup(int mainthread)
+{
+ void *newtcb, *oldtcb;
+
+ // The OpenBSD ld.so(1) does not currently support PT_TLS. As a result,
+ // we need to allocate our own TLS space while preserving the existing
+ // TCB that has been setup via librthread.
+
+ newtcb = malloc(TCB_SIZE + TLS_SIZE);
+ if(newtcb == NULL)
+ abort();
+
+ // The signal trampoline expects the TLS slots to be zeroed.
+ bzero(newtcb, TLS_SIZE);
+
+ oldtcb = __get_tcb();
+ bcopy(oldtcb, newtcb + TLS_SIZE, TCB_SIZE);
+ __set_tcb(newtcb + TLS_SIZE);
+
+ // The main thread TCB is a static allocation - do not try to free it.
+ if(!mainthread)
+ free(oldtcb);
+}
+
+static void *
+thread_start_wrapper(void *arg)
+{
+ struct thread_args args = *(struct thread_args *)arg;
+
+ free(arg);
+ tcb_fixup(0);
+
+ return args.func(args.arg);
+}
+
+int
+pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *arg)
+{
+ struct thread_args *p;
+
+ p = malloc(sizeof(*p));
+ if(p == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ p->func = start_routine;
+ p->arg = arg;
+
+ return sys_pthread_create(thread, attr, thread_start_wrapper, p);
+}
+
+void
+x_cgo_init(G *g)
+{
+ pthread_attr_t attr;
+ size_t size;
+ void *handle;
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+ g->stackguard = (uintptr)&attr - size + 4096;
+ pthread_attr_destroy(&attr);
+
+ // Locate symbol for the system pthread_create function.
+ handle = dlopen("libpthread.so", RTLD_LAZY);
+ if(handle == NULL) {
+ fprintf(stderr, "dlopen: failed to load libpthread: %s\n", dlerror());
+ abort();
+ }
+ sys_pthread_create = dlsym(handle, "pthread_create");
+ if(sys_pthread_create == NULL) {
+ fprintf(stderr, "dlsym: failed to find pthread_create: %s\n", dlerror());
+ abort();
+ }
+ dlclose(handle);
+
+ tcb_fixup(1);
+}
+
+
+void
+_cgo_sys_thread_start(ThreadStart *ts)
+{
+ pthread_attr_t attr;
+ sigset_t ign, oset;
+ pthread_t p;
+ size_t size;
+ int err;
+
+ sigfillset(&ign);
+ sigprocmask(SIG_SETMASK, &ign, &oset);
+
+ pthread_attr_init(&attr);
+ pthread_attr_getstacksize(&attr, &size);
+
+ ts->g->stackguard = size;
+ err = sys_pthread_create(&p, &attr, threadentry, ts);
+
+ sigprocmask(SIG_SETMASK, &oset, nil);
+
+ if (err != 0) {
+ fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err));
+ abort();
+ }
+}
+
+static void*
+threadentry(void *v)
+{
+ ThreadStart ts;
+
+ tcb_fixup(0);
+
+ ts = *(ThreadStart*)v;
+ free(v);
+
+ ts.g->stackbase = (uintptr)&ts;
+
+ /*
+ * _cgo_sys_thread_start set stackguard to stack size;
+ * change to actual guard pointer.
+ */
+ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096;
+
+ /*
+ * Set specific keys. On OpenBSD/ELF, the thread local storage
+ * is just before %fs:0. Our dynamic 6.out's reserve 16 bytes
+ * for the two words g and m at %fs:-16 and %fs:-8.
+ */
+ asm volatile (
+ "movq %0, %%fs:-16\n" // MOVL g, -16(FS)
+ "movq %1, %%fs:-8\n" // MOVL m, -8(FS)
+ :: "r"(ts.g), "r"(ts.m)
+ );
+ crosscall_amd64(ts.fn);
+ return nil;
+}
diff --git a/src/pkg/runtime/cgo/gcc_setenv.c b/src/pkg/runtime/cgo/gcc_setenv.c
index 7da4ad915..a0938166d 100644
--- a/src/pkg/runtime/cgo/gcc_setenv.c
+++ b/src/pkg/runtime/cgo/gcc_setenv.c
@@ -1,4 +1,4 @@
-// Copyright 20111 The Go Authors. All rights reserved.
+// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -9,10 +9,8 @@
#include <stdlib.h>
/* Stub for calling setenv */
-static void
-xlibcgo_setenv(char **arg)
+void
+x_cgo_setenv(char **arg)
{
setenv(arg[0], arg[1], 1);
}
-
-void (*libcgo_setenv)(char**) = xlibcgo_setenv;
diff --git a/src/pkg/runtime/cgo/gcc_util.c b/src/pkg/runtime/cgo/gcc_util.c
index e06b6f64d..20913d736 100644
--- a/src/pkg/runtime/cgo/gcc_util.c
+++ b/src/pkg/runtime/cgo/gcc_util.c
@@ -5,7 +5,7 @@
#include "libcgo.h"
/* Stub for calling malloc from Go */
-static void
+void
x_cgo_malloc(void *p)
{
struct a {
@@ -16,10 +16,8 @@ x_cgo_malloc(void *p)
a->ret = malloc(a->n);
}
-void (*_cgo_malloc)(void*) = x_cgo_malloc;
-
/* Stub for calling free from Go */
-static void
+void
x_cgo_free(void *p)
{
struct a {
@@ -29,11 +27,9 @@ x_cgo_free(void *p)
free(a->arg);
}
-void (*_cgo_free)(void*) = x_cgo_free;
-
/* Stub for creating a new thread */
-static void
-xlibcgo_thread_start(ThreadStart *arg)
+void
+x_cgo_thread_start(ThreadStart *arg)
{
ThreadStart *ts;
@@ -45,7 +41,5 @@ xlibcgo_thread_start(ThreadStart *arg)
}
*ts = *arg;
- libcgo_sys_thread_start(ts); /* OS-dependent half */
+ _cgo_sys_thread_start(ts); /* OS-dependent half */
}
-
-void (*libcgo_thread_start)(ThreadStart*) = xlibcgo_thread_start;
diff --git a/src/pkg/runtime/cgo/gcc_windows_386.c b/src/pkg/runtime/cgo/gcc_windows_386.c
index 2b940d362..02eab12e5 100644
--- a/src/pkg/runtime/cgo/gcc_windows_386.c
+++ b/src/pkg/runtime/cgo/gcc_windows_386.c
@@ -4,31 +4,31 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
+#include <process.h>
#include "libcgo.h"
-static void *threadentry(void*);
+static void threadentry(void*);
/* 1MB is default stack size for 32-bit Windows.
Allocation granularity on Windows is typically 64 KB.
The constant is also hardcoded in cmd/ld/pe.c (keep synchronized). */
#define STACKSIZE (1*1024*1024)
-static void
-xinitcgo(G *g)
+void
+x_cgo_init(G *g)
{
int tmp;
g->stackguard = (uintptr)&tmp - STACKSIZE + 8*1024;
}
-void (*initcgo)(G*) = xinitcgo;
void
-libcgo_sys_thread_start(ThreadStart *ts)
+_cgo_sys_thread_start(ThreadStart *ts)
{
_beginthread(threadentry, 0, ts);
}
-static void*
+static void
threadentry(void *v)
{
ThreadStart ts;
@@ -55,5 +55,4 @@ threadentry(void *v)
crosscall_386(ts.fn);
LocalFree(tls0);
- return nil;
}
diff --git a/src/pkg/runtime/cgo/gcc_windows_amd64.c b/src/pkg/runtime/cgo/gcc_windows_amd64.c
index 0d2f5d233..f7695a1cc 100644
--- a/src/pkg/runtime/cgo/gcc_windows_amd64.c
+++ b/src/pkg/runtime/cgo/gcc_windows_amd64.c
@@ -4,31 +4,31 @@
#define WIN64_LEAN_AND_MEAN
#include <windows.h>
+#include <process.h>
#include "libcgo.h"
-static void *threadentry(void*);
+static void threadentry(void*);
/* 2MB is default stack size for 64-bit Windows.
Allocation granularity on Windows is typically 64 KB.
The constant is also hardcoded in cmd/ld/pe.c (keep synchronized). */
#define STACKSIZE (2*1024*1024)
-static void
-xinitcgo(G *g)
+void
+x_cgo_init(G *g)
{
int tmp;
g->stackguard = (uintptr)&tmp - STACKSIZE + 8*1024;
}
-void (*initcgo)(G*) = xinitcgo;
void
-libcgo_sys_thread_start(ThreadStart *ts)
+_cgo_sys_thread_start(ThreadStart *ts)
{
_beginthread(threadentry, 0, ts);
}
-static void*
+static void
threadentry(void *v)
{
ThreadStart ts;
@@ -53,5 +53,4 @@ threadentry(void *v)
);
crosscall_amd64(ts.fn);
- return nil;
}
diff --git a/src/pkg/runtime/cgo/libcgo.h b/src/pkg/runtime/cgo/libcgo.h
index c31d19d76..41a371c27 100644
--- a/src/pkg/runtime/cgo/libcgo.h
+++ b/src/pkg/runtime/cgo/libcgo.h
@@ -26,7 +26,7 @@ struct G
};
/*
- * Arguments to the libcgo_thread_start call.
+ * Arguments to the _cgo_thread_start call.
* Also known to ../pkg/runtime/runtime.h.
*/
typedef struct ThreadStart ThreadStart;
@@ -40,14 +40,14 @@ struct ThreadStart
/*
* Called by 5c/6c/8c world.
* Makes a local copy of the ThreadStart and
- * calls libcgo_sys_thread_start(ts).
+ * calls _cgo_sys_thread_start(ts).
*/
-extern void (*libcgo_thread_start)(ThreadStart *ts);
+extern void (*_cgo_thread_start)(ThreadStart *ts);
/*
* Creates the new operating system thread (OS, arch dependent).
*/
-void libcgo_sys_thread_start(ThreadStart *ts);
+void _cgo_sys_thread_start(ThreadStart *ts);
/*
* Call fn in the 6c world.
diff --git a/src/pkg/runtime/cgo/netbsd.c b/src/pkg/runtime/cgo/netbsd.c
new file mode 100644
index 000000000..b6403f686
--- /dev/null
+++ b/src/pkg/runtime/cgo/netbsd.c
@@ -0,0 +1,13 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Supply environ and __progname, because we don't
+// link against the standard NetBSD crt0.o and the
+// libc dynamic library needs them.
+
+char *environ[1];
+char *__progname;
+
+#pragma dynexport environ environ
+#pragma dynexport __progname __progname
diff --git a/src/pkg/runtime/cgo/openbsd.c b/src/pkg/runtime/cgo/openbsd.c
new file mode 100644
index 000000000..84e9f9eff
--- /dev/null
+++ b/src/pkg/runtime/cgo/openbsd.c
@@ -0,0 +1,21 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Supply environ, __progname and __guard_local, because
+// we don't link against the standard OpenBSD crt0.o and
+// the libc dynamic library needs them.
+
+char *environ[1];
+char *__progname;
+long __guard_local;
+
+#pragma dynexport environ environ
+#pragma dynexport __progname __progname
+
+// This is normally marked as hidden and placed in the
+// .openbsd.randomdata section.
+#pragma dynexport __guard_local __guard_local
+
+// We override pthread_create to support PT_TLS.
+#pragma dynexport pthread_create pthread_create
diff --git a/src/pkg/runtime/cgo/setenv.c b/src/pkg/runtime/cgo/setenv.c
new file mode 100644
index 000000000..4c47cdb00
--- /dev/null
+++ b/src/pkg/runtime/cgo/setenv.c
@@ -0,0 +1,10 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd
+
+#pragma cgo_import_static x_cgo_setenv
+
+void x_cgo_setenv(char**);
+void (*_cgo_setenv)(char**) = x_cgo_setenv;
diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c
index 7a26538ec..590bf9b67 100644
--- a/src/pkg/runtime/cgocall.c
+++ b/src/pkg/runtime/cgocall.c
@@ -6,6 +6,7 @@
#include "arch_GOARCH.h"
#include "stack.h"
#include "cgocall.h"
+#include "race.h"
// Cgo call and callback support.
//
@@ -41,7 +42,7 @@
// know about packages). The gcc-compiled C function f calls GoF.
//
// GoF calls crosscall2(_cgoexp_GoF, frame, framesize). Crosscall2
-// (in cgo/$GOOS.S, a gcc-compiled assembly file) is a two-argument
+// (in cgo/gcc_$GOARCH.S, a gcc-compiled assembly file) is a two-argument
// adapter from the gcc function call ABI to the 6c function call ABI.
// It is called from gcc to call 6c functions. In this case it calls
// _cgoexp_GoF(frame, framesize), still running on m->g0's stack
@@ -82,43 +83,55 @@
// _cgoexp_GoF immediately returns to crosscall2, which restores the
// callee-save registers for gcc and returns to GoF, which returns to f.
-void *initcgo; /* filled in by dynamic linker when Cgo is available */
+void *_cgo_init; /* filled in by dynamic linker when Cgo is available */
+static int64 cgosync; /* represents possible synchronization in C code */
+
+// These two are only used by the architecture where TLS based storage isn't
+// the default for g and m (e.g., ARM)
+void *_cgo_load_gm; /* filled in by dynamic linker when Cgo is available */
+void *_cgo_save_gm; /* filled in by dynamic linker when Cgo is available */
-static void unlockm(void);
static void unwindm(void);
// Call from Go to C.
+static FuncVal unlockOSThread = { runtime·unlockOSThread };
+
void
runtime·cgocall(void (*fn)(void*), void *arg)
{
Defer d;
+ if(m->racecall) {
+ runtime·asmcgocall(fn, arg);
+ return;
+ }
+
if(!runtime·iscgo && !Windows)
runtime·throw("cgocall unavailable");
if(fn == 0)
runtime·throw("cgocall nil");
+ if(raceenabled)
+ runtime·racereleasemerge(&cgosync);
+
m->ncgocall++;
/*
* Lock g to m to ensure we stay on the same stack if we do a
- * cgo callback.
+ * cgo callback. Add entry to defer stack in case of panic.
*/
- d.nofree = false;
- if(m->lockedg == nil) {
- m->lockedg = g;
- g->lockedm = m;
-
- // Add entry to defer stack in case of panic.
- d.fn = (byte*)unlockm;
- d.siz = 0;
- d.link = g->defer;
- d.argp = (void*)-1; // unused because unlockm never recovers
- d.nofree = true;
- g->defer = &d;
- }
+ runtime·lockOSThread();
+ d.fn = &unlockOSThread;
+ d.siz = 0;
+ d.link = g->defer;
+ d.argp = (void*)-1; // unused because unlockm never recovers
+ d.special = true;
+ d.free = false;
+ g->defer = &d;
+
+ m->ncgo++;
/*
* Announce we are entering a system call
@@ -135,29 +148,31 @@ runtime·cgocall(void (*fn)(void*), void *arg)
runtime·asmcgocall(fn, arg);
runtime·exitsyscall();
- if(d.nofree) {
- if(g->defer != &d || d.fn != (byte*)unlockm)
- runtime·throw("runtime: bad defer entry in cgocallback");
- g->defer = d.link;
- unlockm();
+ m->ncgo--;
+ if(m->ncgo == 0) {
+ // We are going back to Go and are not in a recursive
+ // call. Let the GC collect any memory allocated via
+ // _cgo_allocate that is no longer referenced.
+ m->cgomal = nil;
}
-}
-static void
-unlockm(void)
-{
- m->lockedg = nil;
- g->lockedm = nil;
+ if(g->defer != &d || d.fn != &unlockOSThread)
+ runtime·throw("runtime: bad defer entry in cgocallback");
+ g->defer = d.link;
+ runtime·unlockOSThread();
+
+ if(raceenabled)
+ runtime·raceacquire(&cgosync);
}
void
runtime·NumCgoCall(int64 ret)
{
- M *m;
+ M *mp;
ret = 0;
- for(m=runtime·atomicloadp(&runtime·allm); m; m=m->alllink)
- ret += m->ncgocall;
+ for(mp=runtime·atomicloadp(&runtime·allm); mp; mp=mp->alllink)
+ ret += mp->ncgocall;
FLUSH(&ret);
}
@@ -188,31 +203,50 @@ runtime·cfree(void *p)
// Call from C back to Go.
+static FuncVal unwindmf = {unwindm};
+
void
-runtime·cgocallbackg(void (*fn)(void), void *arg, uintptr argsize)
+runtime·cgocallbackg(FuncVal *fn, void *arg, uintptr argsize)
{
Defer d;
+ if(m->racecall) {
+ reflect·call(fn, arg, argsize);
+ return;
+ }
+
if(g != m->curg)
runtime·throw("runtime: bad g in cgocallback");
runtime·exitsyscall(); // coming out of cgo call
+ if(m->needextram) {
+ m->needextram = 0;
+ runtime·newextram();
+ }
+
// Add entry to defer stack in case of panic.
- d.fn = (byte*)unwindm;
+ d.fn = &unwindmf;
d.siz = 0;
d.link = g->defer;
d.argp = (void*)-1; // unused because unwindm never recovers
- d.nofree = true;
+ d.special = true;
+ d.free = false;
g->defer = &d;
+ if(raceenabled)
+ runtime·raceacquire(&cgosync);
+
// Invoke callback.
- reflect·call((byte*)fn, arg, argsize);
+ reflect·call(fn, arg, argsize);
+
+ if(raceenabled)
+ runtime·racereleasemerge(&cgosync);
// Pop defer.
// Do not unwind m->g0->sched.sp.
// Our caller, cgocallback, will do that.
- if(g->defer != &d || d.fn != (byte*)unwindm)
+ if(g->defer != &d || d.fn != &unwindmf)
runtime·throw("runtime: bad defer entry in cgocallback");
g->defer = d.link;
@@ -229,7 +263,8 @@ unwindm(void)
runtime·throw("runtime: unwindm not implemented");
case '8':
case '6':
- m->g0->sched.sp = *(void**)m->g0->sched.sp;
+ case '5':
+ m->g0->sched.sp = *(uintptr*)m->g0->sched.sp;
break;
}
}
diff --git a/src/pkg/runtime/chan.c b/src/pkg/runtime/chan.c
index ef27144ef..32995c6dd 100644
--- a/src/pkg/runtime/chan.c
+++ b/src/pkg/runtime/chan.c
@@ -3,7 +3,10 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
+#include "arch_GOARCH.h"
#include "type.h"
+#include "race.h"
+#include "malloc.h"
#define MAXALIGN 7
#define NOSELGEN 1
@@ -20,6 +23,7 @@ struct SudoG
G* g; // g and selgen constitute
uint32 selgen; // a weak pointer to g
SudoG* link;
+ int64 releasetime;
byte* elem; // data element
};
@@ -29,21 +33,25 @@ struct WaitQ
SudoG* last;
};
+// The garbage collector is assuming that Hchan can only contain pointers into the stack
+// and cannot contain pointers into the heap.
struct Hchan
{
- uint32 qcount; // total data in the q
- uint32 dataqsiz; // size of the circular q
+ uintgo qcount; // total data in the q
+ uintgo dataqsiz; // size of the circular q
uint16 elemsize;
bool closed;
uint8 elemalign;
Alg* elemalg; // interface for element type
- uint32 sendx; // send index
- uint32 recvx; // receive index
+ uintgo sendx; // send index
+ uintgo recvx; // receive index
WaitQ recvq; // list of recv waiters
WaitQ sendq; // list of send waiters
Lock;
};
+uint32 runtime·Hchansize = sizeof(Hchan);
+
// Buffer follows Hchan immediately in memory.
// chanbuf(c, i) is pointer to the i'th slot in the buffer.
#define chanbuf(c, i) ((byte*)((c)+1)+(uintptr)(c)->elemsize*(i))
@@ -79,17 +87,22 @@ static void dequeueg(WaitQ*);
static SudoG* dequeue(WaitQ*);
static void enqueue(WaitQ*, SudoG*);
static void destroychan(Hchan*);
+static void racesync(Hchan*, SudoG*);
Hchan*
runtime·makechan_c(ChanType *t, int64 hint)
{
Hchan *c;
- int32 n;
+ uintptr n;
Type *elem;
-
+
elem = t->elem;
- if(hint < 0 || (int32)hint != hint || (elem->size > 0 && hint > ((uintptr)-1) / elem->size))
+ // compiler checks this but be safe.
+ if(elem->size >= (1<<16))
+ runtime·throw("makechan: invalid channel element type");
+
+ if(hint < 0 || (intgo)hint != hint || (elem->size > 0 && hint > MaxMem / elem->size))
runtime·panicstring("makechan: size out of range");
// calculate rounded size of Hchan
@@ -103,18 +116,19 @@ runtime·makechan_c(ChanType *t, int64 hint)
c->elemalg = elem->alg;
c->elemalign = elem->align;
c->dataqsiz = hint;
+ runtime·settype(c, (uintptr)t | TypeInfo_Chan);
if(debug)
- runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; elemalign=%d; dataqsiz=%d\n",
- c, (int64)elem->size, elem->alg, elem->align, c->dataqsiz);
+ runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; elemalign=%d; dataqsiz=%D\n",
+ c, (int64)elem->size, elem->alg, elem->align, (int64)c->dataqsiz);
return c;
}
// For reflect
-// func makechan(typ *ChanType, size uint32) (chan)
+// func makechan(typ *ChanType, size uint64) (chan)
void
-reflect·makechan(ChanType *t, uint32 size, Hchan *c)
+reflect·makechan(ChanType *t, uint64 size, Hchan *c)
{
c = runtime·makechan_c(t, size);
FLUSH(&c);
@@ -143,11 +157,12 @@ runtime·makechan(ChanType *t, int64 hint, Hchan *ret)
* the operation; we'll see that it's now closed.
*/
void
-runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
+runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres, void *pc)
{
SudoG *sg;
SudoG mysg;
G* gp;
+ int64 t0;
if(c == nil) {
USED(t);
@@ -155,9 +170,7 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
*pres = false;
return;
}
- g->status = Gwaiting;
- g->waitreason = "chan send (nil chan)";
- runtime·gosched();
+ runtime·park(nil, nil, "chan send (nil chan)");
return; // not reached
}
@@ -170,7 +183,17 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
runtime·prints("\n");
}
+ t0 = 0;
+ mysg.releasetime = 0;
+ if(runtime·blockprofilerate > 0) {
+ t0 = runtime·cputicks();
+ mysg.releasetime = -1;
+ }
+
runtime·lock(c);
+ // TODO(dvyukov): add similar instrumentation to select.
+ if(raceenabled)
+ runtime·racereadpc(c, pc, runtime·chansend);
if(c->closed)
goto closed;
@@ -179,12 +202,16 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
sg = dequeue(&c->recvq);
if(sg != nil) {
+ if(raceenabled)
+ racesync(c, sg);
runtime·unlock(c);
-
+
gp = sg->g;
gp->param = sg;
if(sg->elem != nil)
c->elemalg->copy(c->elemsize, sg->elem, ep);
+ if(sg->releasetime)
+ sg->releasetime = runtime·cputicks();
runtime·ready(gp);
if(pres != nil)
@@ -202,11 +229,8 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
mysg.g = g;
mysg.selgen = NOSELGEN;
g->param = nil;
- g->status = Gwaiting;
- g->waitreason = "chan send";
enqueue(&c->sendq, &mysg);
- runtime·unlock(c);
- runtime·gosched();
+ runtime·park(runtime·unlock, c, "chan send");
if(g->param == nil) {
runtime·lock(c);
@@ -215,6 +239,9 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
goto closed;
}
+ if(mysg.releasetime > 0)
+ runtime·blockevent(mysg.releasetime - t0, 2);
+
return;
asynch:
@@ -230,15 +257,16 @@ asynch:
mysg.g = g;
mysg.elem = nil;
mysg.selgen = NOSELGEN;
- g->status = Gwaiting;
- g->waitreason = "chan send";
enqueue(&c->sendq, &mysg);
- runtime·unlock(c);
- runtime·gosched();
+ runtime·park(runtime·unlock, c, "chan send");
runtime·lock(c);
goto asynch;
}
+
+ if(raceenabled)
+ runtime·racerelease(chanbuf(c, c->sendx));
+
c->elemalg->copy(c->elemsize, chanbuf(c, c->sendx), ep);
if(++c->sendx == c->dataqsiz)
c->sendx = 0;
@@ -248,11 +276,15 @@ asynch:
if(sg != nil) {
gp = sg->g;
runtime·unlock(c);
+ if(sg->releasetime)
+ sg->releasetime = runtime·cputicks();
runtime·ready(gp);
} else
runtime·unlock(c);
if(pres != nil)
*pres = true;
+ if(mysg.releasetime > 0)
+ runtime·blockevent(mysg.releasetime - t0, 2);
return;
closed:
@@ -267,6 +299,7 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive
SudoG *sg;
SudoG mysg;
G *gp;
+ int64 t0;
if(runtime·gcwaiting)
runtime·gosched();
@@ -280,12 +313,17 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive
*selected = false;
return;
}
- g->status = Gwaiting;
- g->waitreason = "chan receive (nil chan)";
- runtime·gosched();
+ runtime·park(nil, nil, "chan receive (nil chan)");
return; // not reached
}
+ t0 = 0;
+ mysg.releasetime = 0;
+ if(runtime·blockprofilerate > 0) {
+ t0 = runtime·cputicks();
+ mysg.releasetime = -1;
+ }
+
runtime·lock(c);
if(c->dataqsiz > 0)
goto asynch;
@@ -295,12 +333,16 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive
sg = dequeue(&c->sendq);
if(sg != nil) {
+ if(raceenabled)
+ racesync(c, sg);
runtime·unlock(c);
if(ep != nil)
c->elemalg->copy(c->elemsize, ep, sg->elem);
gp = sg->g;
gp->param = sg;
+ if(sg->releasetime)
+ sg->releasetime = runtime·cputicks();
runtime·ready(gp);
if(selected != nil)
@@ -320,11 +362,8 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive
mysg.g = g;
mysg.selgen = NOSELGEN;
g->param = nil;
- g->status = Gwaiting;
- g->waitreason = "chan receive";
enqueue(&c->recvq, &mysg);
- runtime·unlock(c);
- runtime·gosched();
+ runtime·park(runtime·unlock, c, "chan receive");
if(g->param == nil) {
runtime·lock(c);
@@ -335,6 +374,8 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive
if(received != nil)
*received = true;
+ if(mysg.releasetime > 0)
+ runtime·blockevent(mysg.releasetime - t0, 2);
return;
asynch:
@@ -352,15 +393,16 @@ asynch:
mysg.g = g;
mysg.elem = nil;
mysg.selgen = NOSELGEN;
- g->status = Gwaiting;
- g->waitreason = "chan receive";
enqueue(&c->recvq, &mysg);
- runtime·unlock(c);
- runtime·gosched();
+ runtime·park(runtime·unlock, c, "chan receive");
runtime·lock(c);
goto asynch;
}
+
+ if(raceenabled)
+ runtime·raceacquire(chanbuf(c, c->recvx));
+
if(ep != nil)
c->elemalg->copy(c->elemsize, ep, chanbuf(c, c->recvx));
c->elemalg->copy(c->elemsize, chanbuf(c, c->recvx), nil);
@@ -372,6 +414,8 @@ asynch:
if(sg != nil) {
gp = sg->g;
runtime·unlock(c);
+ if(sg->releasetime)
+ sg->releasetime = runtime·cputicks();
runtime·ready(gp);
} else
runtime·unlock(c);
@@ -380,6 +424,8 @@ asynch:
*selected = true;
if(received != nil)
*received = true;
+ if(mysg.releasetime > 0)
+ runtime·blockevent(mysg.releasetime - t0, 2);
return;
closed:
@@ -389,7 +435,11 @@ closed:
*selected = true;
if(received != nil)
*received = false;
+ if(raceenabled)
+ runtime·raceacquire(c);
runtime·unlock(c);
+ if(mysg.releasetime > 0)
+ runtime·blockevent(mysg.releasetime - t0, 2);
}
// chansend1(hchan *chan any, elem any);
@@ -397,7 +447,7 @@ closed:
void
runtime·chansend1(ChanType *t, Hchan* c, ...)
{
- runtime·chansend(t, c, (byte*)(&c+1), nil);
+ runtime·chansend(t, c, (byte*)(&c+1), nil, runtime·getcallerpc(&t));
}
// chanrecv1(hchan *chan any) (elem any);
@@ -446,8 +496,8 @@ runtime·selectnbsend(ChanType *t, Hchan *c, ...)
byte *ae, *ap;
ae = (byte*)(&c + 1);
- ap = ae + runtime·rnd(t->elem->size, Structrnd);
- runtime·chansend(t, c, ae, ap);
+ ap = ae + ROUND(t->elem->size, Structrnd);
+ runtime·chansend(t, c, ae, ap, runtime·getcallerpc(&t));
}
// func selectnbrecv(elem *any, c chan any) bool
@@ -474,7 +524,7 @@ void
runtime·selectnbrecv(ChanType *t, byte *v, Hchan *c, bool selected)
{
runtime·chanrecv(t, c, v, &selected, nil);
-}
+}
// func selectnbrecv2(elem *any, ok *bool, c chan any) bool
//
@@ -500,7 +550,7 @@ void
runtime·selectnbrecv2(ChanType *t, byte *v, bool *received, Hchan *c, bool selected)
{
runtime·chanrecv(t, c, v, &selected, received);
-}
+}
// For reflect:
// func chansend(c chan, val iword, nb bool) (selected bool)
@@ -509,12 +559,13 @@ runtime·selectnbrecv2(ChanType *t, byte *v, bool *received, Hchan *c, bool sele
//
// The "uintptr selected" is really "bool selected" but saying
// uintptr gets us the right alignment for the output parameter block.
+#pragma textflag 7
void
reflect·chansend(ChanType *t, Hchan *c, uintptr val, bool nb, uintptr selected)
{
bool *sp;
byte *vp;
-
+
if(nb) {
selected = false;
sp = (bool*)&selected;
@@ -527,7 +578,7 @@ reflect·chansend(ChanType *t, Hchan *c, uintptr val, bool nb, uintptr selected)
vp = (byte*)&val;
else
vp = (byte*)val;
- runtime·chansend(t, c, vp, sp);
+ runtime·chansend(t, c, vp, sp, runtime·getcallerpc(&t));
}
// For reflect:
@@ -571,7 +622,7 @@ runtime·newselect(int32 size, ...)
int32 o;
Select **selp;
- o = runtime·rnd(sizeof(size), Structrnd);
+ o = ROUND(sizeof(size), Structrnd);
selp = (Select**)((byte*)&size + o);
newselect(size, selp);
}
@@ -619,7 +670,7 @@ runtime·selectsend(Select *sel, Hchan *c, void *elem, bool selected)
// nil cases do not compete
if(c == nil)
return;
-
+
selectsend(sel, c, runtime·getcallerpc(&sel), elem, (byte*)&selected - (byte*)&sel);
}
@@ -628,7 +679,7 @@ selectsend(Select *sel, Hchan *c, void *pc, void *elem, int32 so)
{
int32 i;
Scase *cas;
-
+
i = sel->ncase;
if(i >= sel->tcase)
runtime·throw("selectsend: too many cases");
@@ -774,9 +825,7 @@ selunlock(Select *sel)
void
runtime·block(void)
{
- g->status = Gwaiting; // forever
- g->waitreason = "select (no cases)";
- runtime·gosched();
+ runtime·park(nil, nil, "select (no cases)"); // forever
}
static void* selectgo(Select**);
@@ -796,7 +845,7 @@ static void*
selectgo(Select **selp)
{
Select *sel;
- uint32 o, i, j;
+ uint32 o, i, j, k;
Scase *cas, *dfl;
Hchan *c;
SudoG *sg;
@@ -830,12 +879,42 @@ selectgo(Select **selp)
}
// sort the cases by Hchan address to get the locking order.
+ // simple heap sort, to guarantee n log n time and constant stack footprint.
for(i=0; i<sel->ncase; i++) {
- c = sel->scase[i].chan;
- for(j=i; j>0 && sel->lockorder[j-1] >= c; j--)
- sel->lockorder[j] = sel->lockorder[j-1];
+ j = i;
+ c = sel->scase[j].chan;
+ while(j > 0 && sel->lockorder[k=(j-1)/2] < c) {
+ sel->lockorder[j] = sel->lockorder[k];
+ j = k;
+ }
+ sel->lockorder[j] = c;
+ }
+ for(i=sel->ncase; i-->0; ) {
+ c = sel->lockorder[i];
+ sel->lockorder[i] = sel->lockorder[0];
+ j = 0;
+ for(;;) {
+ k = j*2+1;
+ if(k >= i)
+ break;
+ if(k+1 < i && sel->lockorder[k] < sel->lockorder[k+1])
+ k++;
+ if(c < sel->lockorder[k]) {
+ sel->lockorder[j] = sel->lockorder[k];
+ j = k;
+ continue;
+ }
+ break;
+ }
sel->lockorder[j] = c;
}
+ /*
+ for(i=0; i+1<sel->ncase; i++)
+ if(sel->lockorder[i] > sel->lockorder[i+1]) {
+ runtime·printf("i=%d %p %p\n", i, sel->lockorder[i], sel->lockorder[i+1]);
+ runtime·throw("select: broken sort");
+ }
+ */
sellock(sel);
loop:
@@ -899,7 +978,7 @@ loop:
case CaseRecv:
enqueue(&c->recvq, sg);
break;
-
+
case CaseSend:
enqueue(&c->sendq, sg);
break;
@@ -907,10 +986,7 @@ loop:
}
g->param = nil;
- g->status = Gwaiting;
- g->waitreason = "select";
- selunlock(sel);
- runtime·gosched();
+ runtime·park((void(*)(Lock*))selunlock, (Lock*)sel, "select");
sellock(sel);
sg = g->param;
@@ -951,6 +1027,8 @@ loop:
asyncrecv:
// can receive from buffer
+ if(raceenabled)
+ runtime·raceacquire(chanbuf(c, c->recvx));
if(cas->receivedp != nil)
*cas->receivedp = true;
if(cas->sg.elem != nil)
@@ -971,6 +1049,8 @@ asyncrecv:
asyncsend:
// can send to buffer
+ if(raceenabled)
+ runtime·racerelease(chanbuf(c, c->sendx));
c->elemalg->copy(c->elemsize, chanbuf(c, c->sendx), cas->sg.elem);
if(++c->sendx == c->dataqsiz)
c->sendx = 0;
@@ -987,6 +1067,8 @@ asyncsend:
syncrecv:
// can receive from sleeping sender (sg)
+ if(raceenabled)
+ racesync(c, sg);
selunlock(sel);
if(debug)
runtime·printf("syncrecv: sel=%p c=%p o=%d\n", sel, c, o);
@@ -1006,10 +1088,14 @@ rclose:
*cas->receivedp = false;
if(cas->sg.elem != nil)
c->elemalg->copy(c->elemsize, cas->sg.elem, nil);
+ if(raceenabled)
+ runtime·raceacquire(c);
goto retc;
syncsend:
// can send to sleeping receiver (sg)
+ if(raceenabled)
+ racesync(c, sg);
selunlock(sel);
if(debug)
runtime·printf("syncsend: sel=%p c=%p o=%d\n", sel, c, o);
@@ -1020,11 +1106,17 @@ syncsend:
runtime·ready(gp);
retc:
- // return to pc corresponding to chosen case
+ // return pc corresponding to chosen case.
+ // Set boolean passed during select creation
+ // (at offset selp + cas->so) to true.
+ // If cas->so == 0, this is a reflect-driven select and we
+ // don't need to update the boolean.
pc = cas->pc;
- as = (byte*)selp + cas->so;
+ if(cas->so > 0) {
+ as = (byte*)selp + cas->so;
+ *as = true;
+ }
runtime·free(sel);
- *as = true;
return pc;
sclose:
@@ -1034,7 +1126,89 @@ sclose:
return nil; // not reached
}
+// This struct must match ../reflect/value.go:/runtimeSelect.
+typedef struct runtimeSelect runtimeSelect;
+struct runtimeSelect
+{
+ uintptr dir;
+ ChanType *typ;
+ Hchan *ch;
+ uintptr val;
+};
+
+// This enum must match ../reflect/value.go:/SelectDir.
+enum SelectDir {
+ SelectSend = 1,
+ SelectRecv,
+ SelectDefault,
+};
+
+// func rselect(cases []runtimeSelect) (chosen int, word uintptr, recvOK bool)
+void
+reflect·rselect(Slice cases, intgo chosen, uintptr word, bool recvOK)
+{
+ int32 i;
+ Select *sel;
+ runtimeSelect* rcase, *rc;
+ void *elem;
+ void *recvptr;
+ uintptr maxsize;
+
+ chosen = -1;
+ word = 0;
+ recvOK = false;
+
+ maxsize = 0;
+ rcase = (runtimeSelect*)cases.array;
+ for(i=0; i<cases.len; i++) {
+ rc = &rcase[i];
+ if(rc->dir == SelectRecv && rc->ch != nil && maxsize < rc->typ->elem->size)
+ maxsize = rc->typ->elem->size;
+ }
+
+ recvptr = nil;
+ if(maxsize > sizeof(void*))
+ recvptr = runtime·mal(maxsize);
+
+ newselect(cases.len, &sel);
+ for(i=0; i<cases.len; i++) {
+ rc = &rcase[i];
+ switch(rc->dir) {
+ case SelectDefault:
+ selectdefault(sel, (void*)i, 0);
+ break;
+ case SelectSend:
+ if(rc->ch == nil)
+ break;
+ if(rc->typ->elem->size > sizeof(void*))
+ elem = (void*)rc->val;
+ else
+ elem = (void*)&rc->val;
+ selectsend(sel, rc->ch, (void*)i, elem, 0);
+ break;
+ case SelectRecv:
+ if(rc->ch == nil)
+ break;
+ if(rc->typ->elem->size > sizeof(void*))
+ elem = recvptr;
+ else
+ elem = &word;
+ selectrecv(sel, rc->ch, (void*)i, elem, &recvOK, 0);
+ break;
+ }
+ }
+
+ chosen = (intgo)(uintptr)selectgo(&sel);
+ if(rcase[chosen].dir == SelectRecv && rcase[chosen].typ->elem->size > sizeof(void*))
+ word = (uintptr)recvptr;
+
+ FLUSH(&chosen);
+ FLUSH(&word);
+ FLUSH(&recvOK);
+}
+
// closechan(sel *byte);
+#pragma textflag 7
void
runtime·closechan(Hchan *c)
{
@@ -1053,6 +1227,11 @@ runtime·closechan(Hchan *c)
runtime·panicstring("close of closed channel");
}
+ if(raceenabled) {
+ runtime·racewritepc(c, runtime·getcallerpc(&c), runtime·closechan);
+ runtime·racerelease(c);
+ }
+
c->closed = true;
// release all readers
@@ -1087,9 +1266,9 @@ reflect·chanclose(Hchan *c)
}
// For reflect
-// func chanlen(c chan) (len int32)
+// func chanlen(c chan) (len int)
void
-reflect·chanlen(Hchan *c, int32 len)
+reflect·chanlen(Hchan *c, intgo len)
{
if(c == nil)
len = 0;
@@ -1099,9 +1278,9 @@ reflect·chanlen(Hchan *c, int32 len)
}
// For reflect
-// func chancap(c chan) (cap int32)
+// func chancap(c chan) int
void
-reflect·chancap(Hchan *c, int32 cap)
+reflect·chancap(Hchan *c, intgo cap)
{
if(c == nil)
cap = 0;
@@ -1160,3 +1339,12 @@ enqueue(WaitQ *q, SudoG *sgp)
q->last->link = sgp;
q->last = sgp;
}
+
+static void
+racesync(Hchan *c, SudoG *sg)
+{
+ runtime·racerelease(chanbuf(c, 0));
+ runtime·raceacquireg(sg->g, chanbuf(c, 0));
+ runtime·racereleaseg(sg->g, chanbuf(c, 0));
+ runtime·raceacquire(chanbuf(c, 0));
+}
diff --git a/src/pkg/runtime/closure_386.c b/src/pkg/runtime/closure_386.c
deleted file mode 100644
index b4d867711..000000000
--- a/src/pkg/runtime/closure_386.c
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "runtime.h"
-
-#pragma textflag 7
-// func closure(siz int32,
-// fn func(arg0, arg1, arg2 *ptr, callerpc uintptr, xxx) yyy,
-// arg0, arg1, arg2 *ptr) (func(xxx) yyy)
-void
-runtime·closure(int32 siz, byte *fn, byte *arg0)
-{
- byte *p, *q, **ret;
- int32 i, n;
- int32 pcrel;
-
- if(siz < 0 || siz%4 != 0)
- runtime·throw("bad closure size");
-
- ret = (byte**)((byte*)&arg0 + siz);
-
- if(siz > 100) {
- // TODO(rsc): implement stack growth preamble?
- runtime·throw("closure too big");
- }
-
- // compute size of new fn.
- // must match code laid out below.
- n = 6+5+2+1; // SUBL MOVL MOVL CLD
- if(siz <= 4*4)
- n += 1*siz/4; // MOVSL MOVSL...
- else
- n += 6+2; // MOVL REP MOVSL
- n += 5; // CALL
- n += 6+1; // ADDL RET
-
- // store args aligned after code, so gc can find them.
- n += siz;
- if(n%4)
- n += 4 - n%4;
-
- p = runtime·mal(n);
- *ret = p;
- q = p + n - siz;
-
- if(siz > 0) {
- runtime·memmove(q, (byte*)&arg0, siz);
-
- // SUBL $siz, SP
- *p++ = 0x81;
- *p++ = 0xec;
- *(uint32*)p = siz;
- p += 4;
-
- // MOVL $q, SI
- *p++ = 0xbe;
- *(byte**)p = q;
- p += 4;
-
- // MOVL SP, DI
- *p++ = 0x89;
- *p++ = 0xe7;
-
- // CLD
- *p++ = 0xfc;
-
- if(siz <= 4*4) {
- for(i=0; i<siz; i+=4) {
- // MOVSL
- *p++ = 0xa5;
- }
- } else {
- // MOVL $(siz/4), CX [32-bit immediate siz/4]
- *p++ = 0xc7;
- *p++ = 0xc1;
- *(uint32*)p = siz/4;
- p += 4;
-
- // REP; MOVSL
- *p++ = 0xf3;
- *p++ = 0xa5;
- }
- }
-
- // call fn
- pcrel = fn - (p+5);
- // direct call with pc-relative offset
- // CALL fn
- *p++ = 0xe8;
- *(int32*)p = pcrel;
- p += 4;
-
- // ADDL $siz, SP
- *p++ = 0x81;
- *p++ = 0xc4;
- *(uint32*)p = siz;
- p += 4;
-
- // RET
- *p++ = 0xc3;
-
- if(p > q)
- runtime·throw("bad math in sys.closure");
-}
diff --git a/src/pkg/runtime/closure_amd64.c b/src/pkg/runtime/closure_amd64.c
deleted file mode 100644
index 481b4a888..000000000
--- a/src/pkg/runtime/closure_amd64.c
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "runtime.h"
-
-#pragma textflag 7
-// func closure(siz int32,
-// fn func(arg0, arg1, arg2 *ptr, callerpc uintptr, xxx) yyy,
-// arg0, arg1, arg2 *ptr) (func(xxx) yyy)
-void
-runtime·closure(int32 siz, byte *fn, byte *arg0)
-{
- byte *p, *q, **ret;
- int32 i, n;
- int64 pcrel;
-
- if(siz < 0 || siz%8 != 0)
- runtime·throw("bad closure size");
-
- ret = (byte**)((byte*)&arg0 + siz);
-
- if(siz > 100) {
- // TODO(rsc): implement stack growth preamble?
- runtime·throw("closure too big");
- }
-
- // compute size of new fn.
- // must match code laid out below.
- n = 7+10+3; // SUBQ MOVQ MOVQ
- if(siz <= 4*8)
- n += 2*siz/8; // MOVSQ MOVSQ...
- else
- n += 7+3; // MOVQ REP MOVSQ
- n += 12; // CALL worst case; sometimes only 5
- n += 7+1; // ADDQ RET
-
- // store args aligned after code, so gc can find them.
- n += siz;
- if(n%8)
- n += 8 - n%8;
-
- p = runtime·mal(n);
- *ret = p;
- q = p + n - siz;
-
- if(siz > 0) {
- runtime·memmove(q, (byte*)&arg0, siz);
-
- // SUBQ $siz, SP
- *p++ = 0x48;
- *p++ = 0x81;
- *p++ = 0xec;
- *(uint32*)p = siz;
- p += 4;
-
- // MOVQ $q, SI
- *p++ = 0x48;
- *p++ = 0xbe;
- *(byte**)p = q;
- p += 8;
-
- // MOVQ SP, DI
- *p++ = 0x48;
- *p++ = 0x89;
- *p++ = 0xe7;
-
- if(siz <= 4*8) {
- for(i=0; i<siz; i+=8) {
- // MOVSQ
- *p++ = 0x48;
- *p++ = 0xa5;
- }
- } else {
- // MOVQ $(siz/8), CX [32-bit immediate siz/8]
- *p++ = 0x48;
- *p++ = 0xc7;
- *p++ = 0xc1;
- *(uint32*)p = siz/8;
- p += 4;
-
- // REP; MOVSQ
- *p++ = 0xf3;
- *p++ = 0x48;
- *p++ = 0xa5;
- }
- }
-
- // call fn
- pcrel = fn - (p+5);
- if((int32)pcrel == pcrel) {
- // can use direct call with pc-relative offset
- // CALL fn
- *p++ = 0xe8;
- *(int32*)p = pcrel;
- p += 4;
- } else {
- // MOVQ $fn, CX [64-bit immediate fn]
- *p++ = 0x48;
- *p++ = 0xb9;
- *(byte**)p = fn;
- p += 8;
-
- // CALL *CX
- *p++ = 0xff;
- *p++ = 0xd1;
- }
-
- // ADDQ $siz, SP
- *p++ = 0x48;
- *p++ = 0x81;
- *p++ = 0xc4;
- *(uint32*)p = siz;
- p += 4;
-
- // RET
- *p++ = 0xc3;
-
- if(p > q)
- runtime·throw("bad math in sys.closure");
-}
-
-
diff --git a/src/pkg/runtime/closure_arm.c b/src/pkg/runtime/closure_arm.c
deleted file mode 100644
index 119e91b61..000000000
--- a/src/pkg/runtime/closure_arm.c
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "runtime.h"
-
-/*
- There are two bits of magic:
- - The signature of the compiler generated function uses two stack frames
- as arguments (callerpc separates these frames)
- - size determines how many arguments runtime.closure actually has
- starting at arg0.
-
- Example closure with 3 captured variables:
- func closure(siz int32,
- fn func(arg0, arg1, arg2 *ptr, callerpc uintptr, xxx) yyy,
- arg0, arg1, arg2 *ptr) (func(xxx) yyy)
-
- Code generated:
- src R0
- dst R1
- end R3
- tmp R4
- frame = siz+4
-
-//skip loop for 0 size closures
- MOVW.W R14,-frame(R13)
-
- MOVW $vars(PC), R0
- MOVW $4(SP), R1
- MOVW $siz(R0), R3
-loop: MOVW.P 4(R0), R4
- MOVW.P R4, 4(R1)
- CMP R0, R3
- BNE loop
-
- MOVW 8(PC), R0
- BL (R0) // 2 words
- MOVW.P frame(R13),R15
-fptr: WORD *fn
-vars: WORD arg0
- WORD arg1
- WORD arg2
-*/
-
-extern void runtime·cacheflush(byte* start, byte* end);
-
-#pragma textflag 7
-void
-runtime·closure(int32 siz, byte *fn, byte *arg0)
-{
- byte *p, *q, **ret;
- uint32 *pc;
- int32 n;
-
- if(siz < 0 || siz%4 != 0)
- runtime·throw("bad closure size");
-
- ret = (byte**)((byte*)&arg0 + siz);
-
- if(siz > 100) {
- // TODO(kaib): implement stack growth preamble?
- runtime·throw("closure too big");
- }
-
- // size of new fn.
- // must match code laid out below.
- if (siz > 0)
- n = 6 * 4 + 7 * 4;
- else
- n = 6 * 4;
-
- // store args aligned after code, so gc can find them.
- n += siz;
-
- p = runtime·mal(n);
- *ret = p;
- q = p + n - siz;
-
- pc = (uint32*)p;
-
- // MOVW.W R14,-frame(R13)
- *pc++ = 0xe52de000 | (siz + 4);
-
- if(siz > 0) {
- runtime·memmove(q, (byte*)&arg0, siz);
-
- // MOVW $vars(PC), R0
- *pc = 0xe28f0000 | (int32)(q - (byte*)pc - 8);
- pc++;
-
- // MOVW $4(SP), R1
- *pc++ = 0xe28d1004;
-
- // MOVW $siz(R0), R3
- *pc++ = 0xe2803000 | siz;
-
- // MOVW.P 4(R0), R4
- *pc++ = 0xe4904004;
- // MOVW.P R4, 4(R1)
- *pc++ = 0xe4814004;
- // CMP R0, R3
- *pc++ = 0xe1530000;
- // BNE loop
- *pc++ = 0x1afffffb;
- }
-
- // MOVW fptr(PC), R0
- *pc = 0xe59f0008 | (int32)((q - 4) -(byte*) pc - 8);
- pc++;
-
- // BL (R0)
- *pc++ = 0xe28fe000;
- *pc++ = 0xe280f000;
-
- // MOVW.P frame(R13),R15
- *pc++ = 0xe49df000 | (siz + 4);
-
- // WORD *fn
- *pc++ = (uint32)fn;
-
- p = (byte*)pc;
-
- if(p > q)
- runtime·throw("bad math in sys.closure");
-
- runtime·cacheflush(*ret, q+siz);
-}
-
diff --git a/src/pkg/runtime/complex.c b/src/pkg/runtime/complex.c
index eeb943940..395e70fe3 100644
--- a/src/pkg/runtime/complex.c
+++ b/src/pkg/runtime/complex.c
@@ -13,28 +13,30 @@ runtime·complex128div(Complex128 n, Complex128 d, Complex128 q)
float64 a, b, ratio, denom;
// Special cases as in C99.
- ninf = runtime·isInf(n.real, 0) || runtime·isInf(n.imag, 0);
- dinf = runtime·isInf(d.real, 0) || runtime·isInf(d.imag, 0);
+ ninf = n.real == runtime·posinf || n.real == runtime·neginf ||
+ n.imag == runtime·posinf || n.imag == runtime·neginf;
+ dinf = d.real == runtime·posinf || d.real == runtime·neginf ||
+ d.imag == runtime·posinf || d.imag == runtime·neginf;
- nnan = !ninf && (runtime·isNaN(n.real) || runtime·isNaN(n.imag));
- dnan = !dinf && (runtime·isNaN(d.real) || runtime·isNaN(d.imag));
+ nnan = !ninf && (ISNAN(n.real) || ISNAN(n.imag));
+ dnan = !dinf && (ISNAN(d.real) || ISNAN(d.imag));
if(nnan || dnan) {
- q.real = runtime·NaN();
- q.imag = runtime·NaN();
- } else if(ninf && !dinf && !dnan) {
- q.real = runtime·Inf(0);
- q.imag = runtime·Inf(0);
- } else if(!ninf && !nnan && dinf) {
+ q.real = runtime·nan;
+ q.imag = runtime·nan;
+ } else if(ninf && !dinf) {
+ q.real = runtime·posinf;
+ q.imag = runtime·posinf;
+ } else if(!ninf && dinf) {
q.real = 0;
q.imag = 0;
} else if(d.real == 0 && d.imag == 0) {
if(n.real == 0 && n.imag == 0) {
- q.real = runtime·NaN();
- q.imag = runtime·NaN();
+ q.real = runtime·nan;
+ q.imag = runtime·nan;
} else {
- q.real = runtime·Inf(0);
- q.imag = runtime·Inf(0);
+ q.real = runtime·posinf;
+ q.imag = runtime·posinf;
}
} else {
// Standard complex arithmetic, factored to avoid unnecessary overflow.
diff --git a/src/pkg/runtime/complex_test.go b/src/pkg/runtime/complex_test.go
new file mode 100644
index 000000000..f41e6a357
--- /dev/null
+++ b/src/pkg/runtime/complex_test.go
@@ -0,0 +1,67 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime_test
+
+import (
+ "math/cmplx"
+ "testing"
+)
+
+var result complex128
+
+func BenchmarkComplex128DivNormal(b *testing.B) {
+ d := 15 + 2i
+ n := 32 + 3i
+ res := 0i
+ for i := 0; i < b.N; i++ {
+ n += 0.1i
+ res += n / d
+ }
+ result = res
+}
+
+func BenchmarkComplex128DivNisNaN(b *testing.B) {
+ d := cmplx.NaN()
+ n := 32 + 3i
+ res := 0i
+ for i := 0; i < b.N; i++ {
+ n += 0.1i
+ res += n / d
+ }
+ result = res
+}
+
+func BenchmarkComplex128DivDisNaN(b *testing.B) {
+ d := 15 + 2i
+ n := cmplx.NaN()
+ res := 0i
+ for i := 0; i < b.N; i++ {
+ d += 0.1i
+ res += n / d
+ }
+ result = res
+}
+
+func BenchmarkComplex128DivNisInf(b *testing.B) {
+ d := 15 + 2i
+ n := cmplx.Inf()
+ res := 0i
+ for i := 0; i < b.N; i++ {
+ d += 0.1i
+ res += n / d
+ }
+ result = res
+}
+
+func BenchmarkComplex128DivDisInf(b *testing.B) {
+ d := cmplx.Inf()
+ n := 32 + 3i
+ res := 0i
+ for i := 0; i < b.N; i++ {
+ n += 0.1i
+ res += n / d
+ }
+ result = res
+}
diff --git a/src/pkg/runtime/cpuprof.c b/src/pkg/runtime/cpuprof.c
index 05fa0cf61..9a0606a22 100644
--- a/src/pkg/runtime/cpuprof.c
+++ b/src/pkg/runtime/cpuprof.c
@@ -99,6 +99,7 @@ struct Profile {
uint32 wtoggle;
bool wholding; // holding & need to release a log half
bool flushing; // flushing hash table - profile is over
+ bool eod_sent; // special end-of-data record sent; => flushing
};
static Lock lk;
@@ -109,16 +110,20 @@ static void add(Profile*, uintptr*, int32);
static bool evict(Profile*, Entry*);
static bool flushlog(Profile*);
+static uintptr eod[3] = {0, 1, 0};
+
// LostProfileData is a no-op function used in profiles
// to mark the number of profiling stack traces that were
// discarded due to slow data writers.
-static void LostProfileData(void) {
+static void
+LostProfileData(void)
+{
}
// SetCPUProfileRate sets the CPU profiling rate.
// The user documentation is in debug.go.
void
-runtime·SetCPUProfileRate(int32 hz)
+runtime·SetCPUProfileRate(intgo hz)
{
uintptr *p;
uintptr n;
@@ -163,6 +168,7 @@ runtime·SetCPUProfileRate(int32 hz)
prof->wholding = false;
prof->wtoggle = 0;
prof->flushing = false;
+ prof->eod_sent = false;
runtime·noteclear(&prof->wait);
runtime·setcpuprofilerate(tick, hz);
@@ -356,7 +362,7 @@ getprofile(Profile *p)
return ret;
// Wait for new log.
- runtime·entersyscall();
+ runtime·entersyscallblock();
runtime·notesleep(&p->wait);
runtime·exitsyscall();
runtime·noteclear(&p->wait);
@@ -409,6 +415,16 @@ breakflush:
}
// Made it through the table without finding anything to log.
+ if(!p->eod_sent) {
+ // We may not have space to append this to the partial log buf,
+ // so we always return a new slice for the end-of-data marker.
+ p->eod_sent = true;
+ ret.array = (byte*)eod;
+ ret.len = sizeof eod;
+ ret.cap = ret.len;
+ return ret;
+ }
+
// Finally done. Clean up and return nil.
p->flushing = false;
if(!runtime·cas(&p->handoff, p->handoff, 0))
diff --git a/src/pkg/runtime/crash_cgo_test.go b/src/pkg/runtime/crash_cgo_test.go
new file mode 100644
index 000000000..8ccea8f37
--- /dev/null
+++ b/src/pkg/runtime/crash_cgo_test.go
@@ -0,0 +1,88 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build cgo
+
+package runtime_test
+
+import (
+ "testing"
+)
+
+func TestCgoCrashHandler(t *testing.T) {
+ testCrashHandler(t, true)
+}
+
+func TestCgoSignalDeadlock(t *testing.T) {
+ got := executeTest(t, cgoSignalDeadlockSource, nil)
+ want := "OK\n"
+ if got != want {
+ t.Fatalf("expected %q, but got %q", want, got)
+ }
+}
+
+const cgoSignalDeadlockSource = `
+package main
+
+import "C"
+
+import (
+ "fmt"
+ "runtime"
+ "time"
+)
+
+func main() {
+ runtime.GOMAXPROCS(100)
+ ping := make(chan bool)
+ go func() {
+ for i := 0; ; i++ {
+ runtime.Gosched()
+ select {
+ case done := <-ping:
+ if done {
+ ping <- true
+ return
+ }
+ ping <- true
+ default:
+ }
+ func() {
+ defer func() {
+ recover()
+ }()
+ var s *string
+ *s = ""
+ }()
+ }
+ }()
+ time.Sleep(time.Millisecond)
+ for i := 0; i < 64; i++ {
+ go func() {
+ runtime.LockOSThread()
+ select {}
+ }()
+ go func() {
+ runtime.LockOSThread()
+ select {}
+ }()
+ time.Sleep(time.Millisecond)
+ ping <- false
+ select {
+ case <-ping:
+ case <-time.After(time.Second):
+ fmt.Printf("HANG\n")
+ return
+ }
+ }
+ ping <- true
+ select {
+ case <-ping:
+ case <-time.After(time.Second):
+ fmt.Printf("HANG\n")
+ return
+ }
+ fmt.Printf("OK\n")
+}
+`
diff --git a/src/pkg/runtime/crash_test.go b/src/pkg/runtime/crash_test.go
new file mode 100644
index 000000000..5f84cb5a2
--- /dev/null
+++ b/src/pkg/runtime/crash_test.go
@@ -0,0 +1,177 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime_test
+
+import (
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+ "text/template"
+)
+
+func executeTest(t *testing.T, templ string, data interface{}) string {
+ checkStaleRuntime(t)
+
+ st := template.Must(template.New("crashSource").Parse(templ))
+
+ dir, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatalf("failed to create temp directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ src := filepath.Join(dir, "main.go")
+ f, err := os.Create(src)
+ if err != nil {
+ t.Fatalf("failed to create %v: %v", src, err)
+ }
+ err = st.Execute(f, data)
+ if err != nil {
+ f.Close()
+ t.Fatalf("failed to execute template: %v", err)
+ }
+ f.Close()
+
+ got, _ := exec.Command("go", "run", src).CombinedOutput()
+ return string(got)
+}
+
+func checkStaleRuntime(t *testing.T) {
+ // 'go run' uses the installed copy of runtime.a, which may be out of date.
+ out, err := exec.Command("go", "list", "-f", "{{.Stale}}", "runtime").CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to execute 'go list': %v\n%v", err, string(out))
+ }
+ if string(out) != "false\n" {
+ t.Fatalf("Stale runtime.a. Run 'go install runtime'.")
+ }
+}
+
+func testCrashHandler(t *testing.T, cgo bool) {
+ type crashTest struct {
+ Cgo bool
+ }
+ got := executeTest(t, crashSource, &crashTest{Cgo: cgo})
+ want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
+ if got != want {
+ t.Fatalf("expected %q, but got %q", want, got)
+ }
+}
+
+func TestCrashHandler(t *testing.T) {
+ testCrashHandler(t, false)
+}
+
+func testDeadlock(t *testing.T, source string) {
+ got := executeTest(t, source, nil)
+ want := "fatal error: all goroutines are asleep - deadlock!\n"
+ if !strings.HasPrefix(got, want) {
+ t.Fatalf("expected %q, but got %q", want, got)
+ }
+}
+
+func TestSimpleDeadlock(t *testing.T) {
+ testDeadlock(t, simpleDeadlockSource)
+}
+
+func TestInitDeadlock(t *testing.T) {
+ testDeadlock(t, initDeadlockSource)
+}
+
+func TestLockedDeadlock(t *testing.T) {
+ testDeadlock(t, lockedDeadlockSource)
+}
+
+func TestLockedDeadlock2(t *testing.T) {
+ testDeadlock(t, lockedDeadlockSource2)
+}
+
+const crashSource = `
+package main
+
+import (
+ "fmt"
+ "runtime"
+)
+
+{{if .Cgo}}
+import "C"
+{{end}}
+
+func test(name string) {
+ defer func() {
+ if x := recover(); x != nil {
+ fmt.Printf(" recovered")
+ }
+ fmt.Printf(" done\n")
+ }()
+ fmt.Printf("%s:", name)
+ var s *string
+ _ = *s
+ fmt.Print("SHOULD NOT BE HERE")
+}
+
+func testInNewThread(name string) {
+ c := make(chan bool)
+ go func() {
+ runtime.LockOSThread()
+ test(name)
+ c <- true
+ }()
+ <-c
+}
+
+func main() {
+ runtime.LockOSThread()
+ test("main")
+ testInNewThread("new-thread")
+ testInNewThread("second-new-thread")
+ test("main-again")
+}
+`
+
+const simpleDeadlockSource = `
+package main
+func main() {
+ select {}
+}
+`
+
+const initDeadlockSource = `
+package main
+func init() {
+ select {}
+}
+func main() {
+}
+`
+
+const lockedDeadlockSource = `
+package main
+import "runtime"
+func main() {
+ runtime.LockOSThread()
+ select {}
+}
+`
+
+const lockedDeadlockSource2 = `
+package main
+import (
+ "runtime"
+ "time"
+)
+func main() {
+ go func() {
+ runtime.LockOSThread()
+ select {}
+ }()
+ time.Sleep(time.Millisecond)
+ select {}
+}
+`
diff --git a/src/pkg/runtime/debug.go b/src/pkg/runtime/debug.go
index b802fc63f..d82afb08e 100644
--- a/src/pkg/runtime/debug.go
+++ b/src/pkg/runtime/debug.go
@@ -4,7 +4,7 @@
package runtime
-// Breakpoint() executes a breakpoint trap.
+// Breakpoint executes a breakpoint trap.
func Breakpoint()
// LockOSThread wires the calling goroutine to its current operating system thread.
@@ -125,6 +125,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool)
// blocking until data is available. If profiling is turned off and all the profile
// data accumulated while it was on has been returned, CPUProfile returns nil.
// The caller must save the returned data before calling CPUProfile again.
+//
// Most clients should use the runtime/pprof package or
// the testing package's -test.cpuprofile flag instead of calling
// CPUProfile directly.
@@ -133,11 +134,37 @@ func CPUProfile() []byte
// SetCPUProfileRate sets the CPU profiling rate to hz samples per second.
// If hz <= 0, SetCPUProfileRate turns off profiling.
// If the profiler is on, the rate cannot be changed without first turning it off.
+//
// Most clients should use the runtime/pprof package or
// the testing package's -test.cpuprofile flag instead of calling
// SetCPUProfileRate directly.
func SetCPUProfileRate(hz int)
+// SetBlockProfileRate controls the fraction of goroutine blocking events
+// that are reported in the blocking profile. The profiler aims to sample
+// an average of one blocking event per rate nanoseconds spent blocked.
+//
+// To include every blocking event in the profile, pass rate = 1.
+// To turn off profiling entirely, pass rate <= 0.
+func SetBlockProfileRate(rate int)
+
+// BlockProfileRecord describes blocking events originated
+// at a particular call sequence (stack trace).
+type BlockProfileRecord struct {
+ Count int64
+ Cycles int64
+ StackRecord
+}
+
+// BlockProfile returns n, the number of records in the current blocking profile.
+// If len(p) >= n, BlockProfile copies the profile into p and returns n, true.
+// If len(p) < n, BlockProfile does not change p and returns n, false.
+//
+// Most clients should use the runtime/pprof package or
+// the testing package's -test.blockprofile flag instead
+// of calling BlockProfile directly.
+func BlockProfile(p []BlockProfileRecord) (n int, ok bool)
+
// Stack formats a stack trace of the calling goroutine into buf
// and returns the number of bytes written to buf.
// If all is true, Stack formats stack traces of all other goroutines
diff --git a/src/pkg/runtime/debug/debug.c b/src/pkg/runtime/debug/debug.c
new file mode 100644
index 000000000..a7292c477
--- /dev/null
+++ b/src/pkg/runtime/debug/debug.c
@@ -0,0 +1,9 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Nothing to see here.
+// This file exists so that the go command knows that parts of the
+// package are implemented in C, so that it does not instruct the
+// Go compiler to complain about extern declarations.
+// The actual implementations are in package runtime.
diff --git a/src/pkg/runtime/debug/garbage.go b/src/pkg/runtime/debug/garbage.go
new file mode 100644
index 000000000..8f3026426
--- /dev/null
+++ b/src/pkg/runtime/debug/garbage.go
@@ -0,0 +1,101 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package debug
+
+import (
+ "runtime"
+ "sort"
+ "time"
+)
+
+// GCStats collect information about recent garbage collections.
+type GCStats struct {
+ LastGC time.Time // time of last collection
+ NumGC int64 // number of garbage collections
+ PauseTotal time.Duration // total pause for all collections
+ Pause []time.Duration // pause history, most recent first
+ PauseQuantiles []time.Duration
+}
+
+// Implemented in package runtime.
+func readGCStats(*[]time.Duration)
+func enableGC(bool) bool
+func setGCPercent(int) int
+func freeOSMemory()
+
+// ReadGCStats reads statistics about garbage collection into stats.
+// The number of entries in the pause history is system-dependent;
+// stats.Pause slice will be reused if large enough, reallocated otherwise.
+// ReadGCStats may use the full capacity of the stats.Pause slice.
+// If stats.PauseQuantiles is non-empty, ReadGCStats fills it with quantiles
+// summarizing the distribution of pause time. For example, if
+// len(stats.PauseQuantiles) is 5, it will be filled with the minimum,
+// 25%, 50%, 75%, and maximum pause times.
+func ReadGCStats(stats *GCStats) {
+ // Create a buffer with space for at least two copies of the
+ // pause history tracked by the runtime. One will be returned
+ // to the caller and the other will be used as a temporary buffer
+ // for computing quantiles.
+ const maxPause = len(((*runtime.MemStats)(nil)).PauseNs)
+ if cap(stats.Pause) < 2*maxPause {
+ stats.Pause = make([]time.Duration, 2*maxPause)
+ }
+
+ // readGCStats fills in the pause history (up to maxPause entries)
+ // and then three more: Unix ns time of last GC, number of GC,
+ // and total pause time in nanoseconds. Here we depend on the
+ // fact that time.Duration's native unit is nanoseconds, so the
+ // pauses and the total pause time do not need any conversion.
+ readGCStats(&stats.Pause)
+ n := len(stats.Pause) - 3
+ stats.LastGC = time.Unix(0, int64(stats.Pause[n]))
+ stats.NumGC = int64(stats.Pause[n+1])
+ stats.PauseTotal = stats.Pause[n+2]
+ stats.Pause = stats.Pause[:n]
+
+ if len(stats.PauseQuantiles) > 0 {
+ if n == 0 {
+ for i := range stats.PauseQuantiles {
+ stats.PauseQuantiles[i] = 0
+ }
+ } else {
+ // There's room for a second copy of the data in stats.Pause.
+ // See the allocation at the top of the function.
+ sorted := stats.Pause[n : n+n]
+ copy(sorted, stats.Pause)
+ sort.Sort(byDuration(sorted))
+ nq := len(stats.PauseQuantiles) - 1
+ for i := 0; i < nq; i++ {
+ stats.PauseQuantiles[i] = sorted[len(sorted)*i/nq]
+ }
+ stats.PauseQuantiles[nq] = sorted[len(sorted)-1]
+ }
+ }
+}
+
+type byDuration []time.Duration
+
+func (x byDuration) Len() int { return len(x) }
+func (x byDuration) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x byDuration) Less(i, j int) bool { return x[i] < x[j] }
+
+// SetGCPercent sets the garbage collection target percentage:
+// a collection is triggered when the ratio of freshly allocated data
+// to live data remaining after the previous collection reaches this percentage.
+// SetGCPercent returns the previous setting.
+// The initial setting is the value of the GOGC environment variable
+// at startup, or 100 if the variable is not set.
+// A negative percentage disables garbage collection.
+func SetGCPercent(percent int) int {
+ return setGCPercent(percent)
+}
+
+// FreeOSMemory forces a garbage collection followed by an
+// attempt to return as much memory to the operating system
+// as possible. (Even if this is not called, the runtime gradually
+// returns memory to the operating system in a background task.)
+func FreeOSMemory() {
+ freeOSMemory()
+}
diff --git a/src/pkg/runtime/debug/garbage_test.go b/src/pkg/runtime/debug/garbage_test.go
new file mode 100644
index 000000000..b93cfee56
--- /dev/null
+++ b/src/pkg/runtime/debug/garbage_test.go
@@ -0,0 +1,100 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package debug
+
+import (
+ "runtime"
+ "testing"
+ "time"
+)
+
+func TestReadGCStats(t *testing.T) {
+ var stats GCStats
+ var mstats runtime.MemStats
+ var min, max time.Duration
+
+ // First ReadGCStats will allocate, second should not,
+ // especially if we follow up with an explicit garbage collection.
+ stats.PauseQuantiles = make([]time.Duration, 10)
+ ReadGCStats(&stats)
+ runtime.GC()
+
+ // Assume these will return same data: no GC during ReadGCStats.
+ ReadGCStats(&stats)
+ runtime.ReadMemStats(&mstats)
+
+ if stats.NumGC != int64(mstats.NumGC) {
+ t.Errorf("stats.NumGC = %d, but mstats.NumGC = %d", stats.NumGC, mstats.NumGC)
+ }
+ if stats.PauseTotal != time.Duration(mstats.PauseTotalNs) {
+ t.Errorf("stats.PauseTotal = %d, but mstats.PauseTotalNs = %d", stats.PauseTotal, mstats.PauseTotalNs)
+ }
+ if stats.LastGC.UnixNano() != int64(mstats.LastGC) {
+ t.Errorf("stats.LastGC.UnixNano = %d, but mstats.LastGC = %d", stats.LastGC.UnixNano(), mstats.LastGC)
+ }
+ n := int(mstats.NumGC)
+ if n > len(mstats.PauseNs) {
+ n = len(mstats.PauseNs)
+ }
+ if len(stats.Pause) != n {
+ t.Errorf("len(stats.Pause) = %d, want %d", len(stats.Pause), n)
+ } else {
+ off := (int(mstats.NumGC) + len(mstats.PauseNs) - 1) % len(mstats.PauseNs)
+ for i := 0; i < n; i++ {
+ dt := stats.Pause[i]
+ if dt != time.Duration(mstats.PauseNs[off]) {
+ t.Errorf("stats.Pause[%d] = %d, want %d", i, dt, mstats.PauseNs[off])
+ }
+ if max < dt {
+ max = dt
+ }
+ if min > dt || i == 0 {
+ min = dt
+ }
+ off = (off + len(mstats.PauseNs) - 1) % len(mstats.PauseNs)
+ }
+ }
+
+ q := stats.PauseQuantiles
+ nq := len(q)
+ if q[0] != min || q[nq-1] != max {
+ t.Errorf("stats.PauseQuantiles = [%d, ..., %d], want [%d, ..., %d]", q[0], q[nq-1], min, max)
+ }
+
+ for i := 0; i < nq-1; i++ {
+ if q[i] > q[i+1] {
+ t.Errorf("stats.PauseQuantiles[%d]=%d > stats.PauseQuantiles[%d]=%d", i, q[i], i+1, q[i+1])
+ }
+ }
+}
+
+var big = make([]byte, 1<<20)
+
+func TestFreeOSMemory(t *testing.T) {
+ var ms1, ms2 runtime.MemStats
+
+ if big == nil {
+ t.Skip("test is not reliable when run multiple times")
+ }
+ big = nil
+ runtime.GC()
+ runtime.ReadMemStats(&ms1)
+ FreeOSMemory()
+ runtime.ReadMemStats(&ms2)
+ if ms1.HeapReleased >= ms2.HeapReleased {
+ t.Errorf("released before=%d; released after=%d; did not go up", ms1.HeapReleased, ms2.HeapReleased)
+ }
+}
+
+func TestSetGCPercent(t *testing.T) {
+ // Test that the variable is being set and returned correctly.
+ // Assume the percentage itself is implemented fine during GC,
+ // which is harder to test.
+ old := SetGCPercent(123)
+ new := SetGCPercent(old)
+ if new != 123 {
+ t.Errorf("SetGCPercent(123); SetGCPercent(x) = %d, want 123", new)
+ }
+}
diff --git a/src/pkg/runtime/debug/stack.go b/src/pkg/runtime/debug/stack.go
index a533a5c3b..2896b2141 100644
--- a/src/pkg/runtime/debug/stack.go
+++ b/src/pkg/runtime/debug/stack.go
@@ -29,6 +29,8 @@ func PrintStack() {
// For each routine, it includes the source line information and PC value,
// then attempts to discover, for Go functions, the calling function or
// method and the text of the line containing the invocation.
+//
+// This function is deprecated. Use package runtime's Stack instead.
func Stack() []byte {
return stack()
}
diff --git a/src/pkg/runtime/debug/stack_test.go b/src/pkg/runtime/debug/stack_test.go
index f33f5072b..bbd662618 100644
--- a/src/pkg/runtime/debug/stack_test.go
+++ b/src/pkg/runtime/debug/stack_test.go
@@ -36,7 +36,7 @@ func (t T) method() []byte {
func TestStack(t *testing.T) {
b := T(0).method()
lines := strings.Split(string(b), "\n")
- if len(lines) <= 6 {
+ if len(lines) < 6 {
t.Fatal("too few lines")
}
n := 0
diff --git a/src/pkg/runtime/defs_freebsd.go b/src/pkg/runtime/defs_freebsd.go
index 306e32197..084022715 100644
--- a/src/pkg/runtime/defs_freebsd.go
+++ b/src/pkg/runtime/defs_freebsd.go
@@ -7,8 +7,9 @@
/*
Input to cgo.
-GOARCH=amd64 cgo -cdefs defs.go >amd64/defs.h
-GOARCH=386 cgo -cdefs defs.go >386/defs.h
+GOARCH=amd64 go tool cgo -cdefs defs_freebsd.go >defs_freebsd_amd64.h
+GOARCH=386 go tool cgo -cdefs defs_freebsd.go >defs_freebsd_386.h
+GOARCH=arm go tool cgo -cdefs defs_freebsd.go >defs_freebsd_arm.h
*/
package runtime
@@ -38,6 +39,8 @@ const (
MAP_PRIVATE = C.MAP_PRIVATE
MAP_FIXED = C.MAP_FIXED
+ MADV_FREE = C.MADV_FREE
+
SA_SIGINFO = C.SA_SIGINFO
SA_RESTART = C.SA_RESTART
SA_ONSTACK = C.SA_ONSTACK
@@ -104,7 +107,6 @@ type Rtprio C.struct_rtprio
type ThrParam C.struct_thr_param
type Sigaltstack C.struct_sigaltstack
type Sigset C.struct___sigset
-type Sigval C.union_sigval
type StackT C.stack_t
type Siginfo C.siginfo_t
diff --git a/src/pkg/runtime/defs_freebsd_386.h b/src/pkg/runtime/defs_freebsd_386.h
index 29fcb8b57..d00c852c6 100644
--- a/src/pkg/runtime/defs_freebsd_386.h
+++ b/src/pkg/runtime/defs_freebsd_386.h
@@ -1,193 +1,190 @@
-// godefs -f -m32 defs.c
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_freebsd.go
-// MACHINE GENERATED - DO NOT EDIT.
-// Constants
enum {
- PROT_NONE = 0,
- PROT_READ = 0x1,
- PROT_WRITE = 0x2,
- PROT_EXEC = 0x4,
- MAP_ANON = 0x1000,
- MAP_PRIVATE = 0x2,
- MAP_FIXED = 0x10,
- SA_SIGINFO = 0x40,
- SA_RESTART = 0x2,
- SA_ONSTACK = 0x1,
- UMTX_OP_WAIT = 0x2,
- UMTX_OP_WAKE = 0x3,
- EINTR = 0x4,
- SIGHUP = 0x1,
- SIGINT = 0x2,
- SIGQUIT = 0x3,
- SIGILL = 0x4,
- SIGTRAP = 0x5,
- SIGABRT = 0x6,
- SIGEMT = 0x7,
- SIGFPE = 0x8,
- SIGKILL = 0x9,
- SIGBUS = 0xa,
- SIGSEGV = 0xb,
- SIGSYS = 0xc,
- SIGPIPE = 0xd,
- SIGALRM = 0xe,
- SIGTERM = 0xf,
- SIGURG = 0x10,
- SIGSTOP = 0x11,
- SIGTSTP = 0x12,
- SIGCONT = 0x13,
- SIGCHLD = 0x14,
- SIGTTIN = 0x15,
- SIGTTOU = 0x16,
- SIGIO = 0x17,
- SIGXCPU = 0x18,
- SIGXFSZ = 0x19,
- SIGVTALRM = 0x1a,
- SIGPROF = 0x1b,
- SIGWINCH = 0x1c,
- SIGINFO = 0x1d,
- SIGUSR1 = 0x1e,
- SIGUSR2 = 0x1f,
- FPE_INTDIV = 0x2,
- FPE_INTOVF = 0x1,
- FPE_FLTDIV = 0x3,
- FPE_FLTOVF = 0x4,
- FPE_FLTUND = 0x5,
- FPE_FLTRES = 0x6,
- FPE_FLTINV = 0x7,
- FPE_FLTSUB = 0x8,
- BUS_ADRALN = 0x1,
- BUS_ADRERR = 0x2,
- BUS_OBJERR = 0x3,
- SEGV_MAPERR = 0x1,
- SEGV_ACCERR = 0x2,
- ITIMER_REAL = 0,
- ITIMER_VIRTUAL = 0x1,
- ITIMER_PROF = 0x2,
+ PROT_NONE = 0x0,
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+ PROT_EXEC = 0x4,
+
+ MAP_ANON = 0x1000,
+ MAP_PRIVATE = 0x2,
+ MAP_FIXED = 0x10,
+
+ MADV_FREE = 0x5,
+
+ SA_SIGINFO = 0x40,
+ SA_RESTART = 0x2,
+ SA_ONSTACK = 0x1,
+
+ UMTX_OP_WAIT = 0x2,
+ UMTX_OP_WAKE = 0x3,
+
+ EINTR = 0x4,
+
+ SIGHUP = 0x1,
+ SIGINT = 0x2,
+ SIGQUIT = 0x3,
+ SIGILL = 0x4,
+ SIGTRAP = 0x5,
+ SIGABRT = 0x6,
+ SIGEMT = 0x7,
+ SIGFPE = 0x8,
+ SIGKILL = 0x9,
+ SIGBUS = 0xa,
+ SIGSEGV = 0xb,
+ SIGSYS = 0xc,
+ SIGPIPE = 0xd,
+ SIGALRM = 0xe,
+ SIGTERM = 0xf,
+ SIGURG = 0x10,
+ SIGSTOP = 0x11,
+ SIGTSTP = 0x12,
+ SIGCONT = 0x13,
+ SIGCHLD = 0x14,
+ SIGTTIN = 0x15,
+ SIGTTOU = 0x16,
+ SIGIO = 0x17,
+ SIGXCPU = 0x18,
+ SIGXFSZ = 0x19,
+ SIGVTALRM = 0x1a,
+ SIGPROF = 0x1b,
+ SIGWINCH = 0x1c,
+ SIGINFO = 0x1d,
+ SIGUSR1 = 0x1e,
+ SIGUSR2 = 0x1f,
+
+ FPE_INTDIV = 0x2,
+ FPE_INTOVF = 0x1,
+ FPE_FLTDIV = 0x3,
+ FPE_FLTOVF = 0x4,
+ FPE_FLTUND = 0x5,
+ FPE_FLTRES = 0x6,
+ FPE_FLTINV = 0x7,
+ FPE_FLTSUB = 0x8,
+
+ BUS_ADRALN = 0x1,
+ BUS_ADRERR = 0x2,
+ BUS_OBJERR = 0x3,
+
+ SEGV_MAPERR = 0x1,
+ SEGV_ACCERR = 0x2,
+
+ ITIMER_REAL = 0x0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
};
-// Types
+typedef struct Rtprio Rtprio;
+typedef struct ThrParam ThrParam;
+typedef struct Sigaltstack Sigaltstack;
+typedef struct Sigset Sigset;
+typedef struct StackT StackT;
+typedef struct Siginfo Siginfo;
+typedef struct Mcontext Mcontext;
+typedef struct Ucontext Ucontext;
+typedef struct Timespec Timespec;
+typedef struct Timeval Timeval;
+typedef struct Itimerval Itimerval;
+
#pragma pack on
-typedef struct Rtprio Rtprio;
struct Rtprio {
- uint16 type;
- uint16 prio;
+ uint16 type;
+ uint16 prio;
};
-
-typedef struct ThrParam ThrParam;
struct ThrParam {
- void *start_func;
- void *arg;
- int8 *stack_base;
- uint32 stack_size;
- int8 *tls_base;
- uint32 tls_size;
- int32 *child_tid;
- int32 *parent_tid;
- int32 flags;
- Rtprio *rtp;
- void* spare[3];
+ void *start_func;
+ byte *arg;
+ int8 *stack_base;
+ uint32 stack_size;
+ int8 *tls_base;
+ uint32 tls_size;
+ int32 *child_tid;
+ int32 *parent_tid;
+ int32 flags;
+ Rtprio *rtp;
+ void *spare[3];
};
-
-typedef struct Sigaltstack Sigaltstack;
struct Sigaltstack {
- int8 *ss_sp;
- uint32 ss_size;
- int32 ss_flags;
+ int8 *ss_sp;
+ uint32 ss_size;
+ int32 ss_flags;
};
-
-typedef struct Sigset Sigset;
struct Sigset {
- uint32 __bits[4];
-};
-
-typedef union Sigval Sigval;
-union Sigval {
- int32 sival_int;
- void *sival_ptr;
- int32 sigval_int;
- void *sigval_ptr;
+ uint32 __bits[4];
};
-
-typedef struct StackT StackT;
struct StackT {
- int8 *ss_sp;
- uint32 ss_size;
- int32 ss_flags;
+ int8 *ss_sp;
+ uint32 ss_size;
+ int32 ss_flags;
};
-typedef struct Siginfo Siginfo;
struct Siginfo {
- int32 si_signo;
- int32 si_errno;
- int32 si_code;
- int32 si_pid;
- uint32 si_uid;
- int32 si_status;
- void *si_addr;
- Sigval si_value;
- byte _reason[32];
+ int32 si_signo;
+ int32 si_errno;
+ int32 si_code;
+ int32 si_pid;
+ uint32 si_uid;
+ int32 si_status;
+ byte *si_addr;
+ byte si_value[4];
+ byte _reason[32];
};
-typedef struct Mcontext Mcontext;
struct Mcontext {
- int32 mc_onstack;
- int32 mc_gs;
- int32 mc_fs;
- int32 mc_es;
- int32 mc_ds;
- int32 mc_edi;
- int32 mc_esi;
- int32 mc_ebp;
- int32 mc_isp;
- int32 mc_ebx;
- int32 mc_edx;
- int32 mc_ecx;
- int32 mc_eax;
- int32 mc_trapno;
- int32 mc_err;
- int32 mc_eip;
- int32 mc_cs;
- int32 mc_eflags;
- int32 mc_esp;
- int32 mc_ss;
- int32 mc_len;
- int32 mc_fpformat;
- int32 mc_ownedfp;
- int32 mc_spare1[1];
- int32 mc_fpstate[128];
- int32 mc_fsbase;
- int32 mc_gsbase;
- int32 mc_spare2[6];
+ int32 mc_onstack;
+ int32 mc_gs;
+ int32 mc_fs;
+ int32 mc_es;
+ int32 mc_ds;
+ int32 mc_edi;
+ int32 mc_esi;
+ int32 mc_ebp;
+ int32 mc_isp;
+ int32 mc_ebx;
+ int32 mc_edx;
+ int32 mc_ecx;
+ int32 mc_eax;
+ int32 mc_trapno;
+ int32 mc_err;
+ int32 mc_eip;
+ int32 mc_cs;
+ int32 mc_eflags;
+ int32 mc_esp;
+ int32 mc_ss;
+ int32 mc_len;
+ int32 mc_fpformat;
+ int32 mc_ownedfp;
+ int32 mc_spare1[1];
+ int32 mc_fpstate[128];
+ int32 mc_fsbase;
+ int32 mc_gsbase;
+ int32 mc_spare2[6];
};
-
-typedef struct Ucontext Ucontext;
struct Ucontext {
- Sigset uc_sigmask;
- Mcontext uc_mcontext;
- Ucontext *uc_link;
- StackT uc_stack;
- int32 uc_flags;
- int32 __spare__[4];
- byte pad_godefs_0[12];
+ Sigset uc_sigmask;
+ Mcontext uc_mcontext;
+ Ucontext *uc_link;
+ StackT uc_stack;
+ int32 uc_flags;
+ int32 __spare__[4];
+ byte Pad_cgo_0[12];
};
-typedef struct Timespec Timespec;
struct Timespec {
- int32 tv_sec;
- int32 tv_nsec;
+ int32 tv_sec;
+ int32 tv_nsec;
};
-
-typedef struct Timeval Timeval;
struct Timeval {
- int32 tv_sec;
- int32 tv_usec;
+ int32 tv_sec;
+ int32 tv_usec;
};
-
-typedef struct Itimerval Itimerval;
struct Itimerval {
- Timeval it_interval;
- Timeval it_value;
+ Timeval it_interval;
+ Timeval it_value;
};
+
+
#pragma pack off
diff --git a/src/pkg/runtime/defs_freebsd_amd64.h b/src/pkg/runtime/defs_freebsd_amd64.h
index 8a222dca4..6348c0482 100644
--- a/src/pkg/runtime/defs_freebsd_amd64.h
+++ b/src/pkg/runtime/defs_freebsd_amd64.h
@@ -1,204 +1,201 @@
-// godefs -f -m64 defs.c
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_freebsd.go
-// MACHINE GENERATED - DO NOT EDIT.
-// Constants
enum {
- PROT_NONE = 0,
- PROT_READ = 0x1,
- PROT_WRITE = 0x2,
- PROT_EXEC = 0x4,
- MAP_ANON = 0x1000,
- MAP_PRIVATE = 0x2,
- MAP_FIXED = 0x10,
- SA_SIGINFO = 0x40,
- SA_RESTART = 0x2,
- SA_ONSTACK = 0x1,
- UMTX_OP_WAIT = 0x2,
- UMTX_OP_WAKE = 0x3,
- EINTR = 0x4,
- SIGHUP = 0x1,
- SIGINT = 0x2,
- SIGQUIT = 0x3,
- SIGILL = 0x4,
- SIGTRAP = 0x5,
- SIGABRT = 0x6,
- SIGEMT = 0x7,
- SIGFPE = 0x8,
- SIGKILL = 0x9,
- SIGBUS = 0xa,
- SIGSEGV = 0xb,
- SIGSYS = 0xc,
- SIGPIPE = 0xd,
- SIGALRM = 0xe,
- SIGTERM = 0xf,
- SIGURG = 0x10,
- SIGSTOP = 0x11,
- SIGTSTP = 0x12,
- SIGCONT = 0x13,
- SIGCHLD = 0x14,
- SIGTTIN = 0x15,
- SIGTTOU = 0x16,
- SIGIO = 0x17,
- SIGXCPU = 0x18,
- SIGXFSZ = 0x19,
- SIGVTALRM = 0x1a,
- SIGPROF = 0x1b,
- SIGWINCH = 0x1c,
- SIGINFO = 0x1d,
- SIGUSR1 = 0x1e,
- SIGUSR2 = 0x1f,
- FPE_INTDIV = 0x2,
- FPE_INTOVF = 0x1,
- FPE_FLTDIV = 0x3,
- FPE_FLTOVF = 0x4,
- FPE_FLTUND = 0x5,
- FPE_FLTRES = 0x6,
- FPE_FLTINV = 0x7,
- FPE_FLTSUB = 0x8,
- BUS_ADRALN = 0x1,
- BUS_ADRERR = 0x2,
- BUS_OBJERR = 0x3,
- SEGV_MAPERR = 0x1,
- SEGV_ACCERR = 0x2,
- ITIMER_REAL = 0,
- ITIMER_VIRTUAL = 0x1,
- ITIMER_PROF = 0x2,
+ PROT_NONE = 0x0,
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+ PROT_EXEC = 0x4,
+
+ MAP_ANON = 0x1000,
+ MAP_PRIVATE = 0x2,
+ MAP_FIXED = 0x10,
+
+ MADV_FREE = 0x5,
+
+ SA_SIGINFO = 0x40,
+ SA_RESTART = 0x2,
+ SA_ONSTACK = 0x1,
+
+ UMTX_OP_WAIT = 0x2,
+ UMTX_OP_WAKE = 0x3,
+
+ EINTR = 0x4,
+
+ SIGHUP = 0x1,
+ SIGINT = 0x2,
+ SIGQUIT = 0x3,
+ SIGILL = 0x4,
+ SIGTRAP = 0x5,
+ SIGABRT = 0x6,
+ SIGEMT = 0x7,
+ SIGFPE = 0x8,
+ SIGKILL = 0x9,
+ SIGBUS = 0xa,
+ SIGSEGV = 0xb,
+ SIGSYS = 0xc,
+ SIGPIPE = 0xd,
+ SIGALRM = 0xe,
+ SIGTERM = 0xf,
+ SIGURG = 0x10,
+ SIGSTOP = 0x11,
+ SIGTSTP = 0x12,
+ SIGCONT = 0x13,
+ SIGCHLD = 0x14,
+ SIGTTIN = 0x15,
+ SIGTTOU = 0x16,
+ SIGIO = 0x17,
+ SIGXCPU = 0x18,
+ SIGXFSZ = 0x19,
+ SIGVTALRM = 0x1a,
+ SIGPROF = 0x1b,
+ SIGWINCH = 0x1c,
+ SIGINFO = 0x1d,
+ SIGUSR1 = 0x1e,
+ SIGUSR2 = 0x1f,
+
+ FPE_INTDIV = 0x2,
+ FPE_INTOVF = 0x1,
+ FPE_FLTDIV = 0x3,
+ FPE_FLTOVF = 0x4,
+ FPE_FLTUND = 0x5,
+ FPE_FLTRES = 0x6,
+ FPE_FLTINV = 0x7,
+ FPE_FLTSUB = 0x8,
+
+ BUS_ADRALN = 0x1,
+ BUS_ADRERR = 0x2,
+ BUS_OBJERR = 0x3,
+
+ SEGV_MAPERR = 0x1,
+ SEGV_ACCERR = 0x2,
+
+ ITIMER_REAL = 0x0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
};
-// Types
+typedef struct Rtprio Rtprio;
+typedef struct ThrParam ThrParam;
+typedef struct Sigaltstack Sigaltstack;
+typedef struct Sigset Sigset;
+typedef struct StackT StackT;
+typedef struct Siginfo Siginfo;
+typedef struct Mcontext Mcontext;
+typedef struct Ucontext Ucontext;
+typedef struct Timespec Timespec;
+typedef struct Timeval Timeval;
+typedef struct Itimerval Itimerval;
+
#pragma pack on
-typedef struct Rtprio Rtprio;
struct Rtprio {
- uint16 type;
- uint16 prio;
+ uint16 type;
+ uint16 prio;
};
-
-typedef struct ThrParam ThrParam;
struct ThrParam {
- void *start_func;
- void *arg;
- int8 *stack_base;
- uint64 stack_size;
- int8 *tls_base;
- uint64 tls_size;
- int64 *child_tid;
- int64 *parent_tid;
- int32 flags;
- byte pad_godefs_0[4];
- Rtprio *rtp;
- void* spare[3];
+ void *start_func;
+ byte *arg;
+ int8 *stack_base;
+ uint64 stack_size;
+ int8 *tls_base;
+ uint64 tls_size;
+ int64 *child_tid;
+ int64 *parent_tid;
+ int32 flags;
+ byte Pad_cgo_0[4];
+ Rtprio *rtp;
+ void *spare[3];
};
-
-typedef struct Sigaltstack Sigaltstack;
struct Sigaltstack {
- int8 *ss_sp;
- uint64 ss_size;
- int32 ss_flags;
- byte pad_godefs_0[4];
+ int8 *ss_sp;
+ uint64 ss_size;
+ int32 ss_flags;
+ byte Pad_cgo_0[4];
};
-
-typedef struct Sigset Sigset;
struct Sigset {
- uint32 __bits[4];
-};
-
-typedef union Sigval Sigval;
-union Sigval {
- int32 sival_int;
- void *sival_ptr;
- int32 sigval_int;
- void *sigval_ptr;
+ uint32 __bits[4];
};
-
-typedef struct StackT StackT;
struct StackT {
- int8 *ss_sp;
- uint64 ss_size;
- int32 ss_flags;
- byte pad_godefs_0[4];
+ int8 *ss_sp;
+ uint64 ss_size;
+ int32 ss_flags;
+ byte Pad_cgo_0[4];
};
-typedef struct Siginfo Siginfo;
struct Siginfo {
- int32 si_signo;
- int32 si_errno;
- int32 si_code;
- int32 si_pid;
- uint32 si_uid;
- int32 si_status;
- void *si_addr;
- Sigval si_value;
- byte _reason[40];
+ int32 si_signo;
+ int32 si_errno;
+ int32 si_code;
+ int32 si_pid;
+ uint32 si_uid;
+ int32 si_status;
+ byte *si_addr;
+ byte si_value[8];
+ byte _reason[40];
};
-typedef struct Mcontext Mcontext;
struct Mcontext {
- int64 mc_onstack;
- int64 mc_rdi;
- int64 mc_rsi;
- int64 mc_rdx;
- int64 mc_rcx;
- int64 mc_r8;
- int64 mc_r9;
- int64 mc_rax;
- int64 mc_rbx;
- int64 mc_rbp;
- int64 mc_r10;
- int64 mc_r11;
- int64 mc_r12;
- int64 mc_r13;
- int64 mc_r14;
- int64 mc_r15;
- uint32 mc_trapno;
- uint16 mc_fs;
- uint16 mc_gs;
- int64 mc_addr;
- uint32 mc_flags;
- uint16 mc_es;
- uint16 mc_ds;
- int64 mc_err;
- int64 mc_rip;
- int64 mc_cs;
- int64 mc_rflags;
- int64 mc_rsp;
- int64 mc_ss;
- int64 mc_len;
- int64 mc_fpformat;
- int64 mc_ownedfp;
- int64 mc_fpstate[64];
- int64 mc_fsbase;
- int64 mc_gsbase;
- int64 mc_spare[6];
+ int64 mc_onstack;
+ int64 mc_rdi;
+ int64 mc_rsi;
+ int64 mc_rdx;
+ int64 mc_rcx;
+ int64 mc_r8;
+ int64 mc_r9;
+ int64 mc_rax;
+ int64 mc_rbx;
+ int64 mc_rbp;
+ int64 mc_r10;
+ int64 mc_r11;
+ int64 mc_r12;
+ int64 mc_r13;
+ int64 mc_r14;
+ int64 mc_r15;
+ uint32 mc_trapno;
+ uint16 mc_fs;
+ uint16 mc_gs;
+ int64 mc_addr;
+ uint32 mc_flags;
+ uint16 mc_es;
+ uint16 mc_ds;
+ int64 mc_err;
+ int64 mc_rip;
+ int64 mc_cs;
+ int64 mc_rflags;
+ int64 mc_rsp;
+ int64 mc_ss;
+ int64 mc_len;
+ int64 mc_fpformat;
+ int64 mc_ownedfp;
+ int64 mc_fpstate[64];
+ int64 mc_fsbase;
+ int64 mc_gsbase;
+ int64 mc_spare[6];
};
-
-typedef struct Ucontext Ucontext;
struct Ucontext {
- Sigset uc_sigmask;
- Mcontext uc_mcontext;
- Ucontext *uc_link;
- StackT uc_stack;
- int32 uc_flags;
- int32 __spare__[4];
- byte pad_godefs_0[12];
+ Sigset uc_sigmask;
+ Mcontext uc_mcontext;
+ Ucontext *uc_link;
+ StackT uc_stack;
+ int32 uc_flags;
+ int32 __spare__[4];
+ byte Pad_cgo_0[12];
};
-typedef struct Timespec Timespec;
struct Timespec {
- int64 tv_sec;
- int64 tv_nsec;
+ int64 tv_sec;
+ int64 tv_nsec;
};
-
-typedef struct Timeval Timeval;
struct Timeval {
- int64 tv_sec;
- int64 tv_usec;
+ int64 tv_sec;
+ int64 tv_usec;
};
-
-typedef struct Itimerval Itimerval;
struct Itimerval {
- Timeval it_interval;
- Timeval it_value;
+ Timeval it_interval;
+ Timeval it_value;
};
+
+
#pragma pack off
diff --git a/src/pkg/runtime/defs_freebsd_arm.h b/src/pkg/runtime/defs_freebsd_arm.h
new file mode 100644
index 000000000..a744b808f
--- /dev/null
+++ b/src/pkg/runtime/defs_freebsd_arm.h
@@ -0,0 +1,163 @@
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_freebsd.go
+
+
+enum {
+ PROT_NONE = 0x0,
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+ PROT_EXEC = 0x4,
+
+ MAP_ANON = 0x1000,
+ MAP_PRIVATE = 0x2,
+ MAP_FIXED = 0x10,
+
+ MADV_FREE = 0x5,
+
+ SA_SIGINFO = 0x40,
+ SA_RESTART = 0x2,
+ SA_ONSTACK = 0x1,
+
+ UMTX_OP_WAIT = 0x2,
+ UMTX_OP_WAKE = 0x3,
+
+ EINTR = 0x4,
+
+ SIGHUP = 0x1,
+ SIGINT = 0x2,
+ SIGQUIT = 0x3,
+ SIGILL = 0x4,
+ SIGTRAP = 0x5,
+ SIGABRT = 0x6,
+ SIGEMT = 0x7,
+ SIGFPE = 0x8,
+ SIGKILL = 0x9,
+ SIGBUS = 0xa,
+ SIGSEGV = 0xb,
+ SIGSYS = 0xc,
+ SIGPIPE = 0xd,
+ SIGALRM = 0xe,
+ SIGTERM = 0xf,
+ SIGURG = 0x10,
+ SIGSTOP = 0x11,
+ SIGTSTP = 0x12,
+ SIGCONT = 0x13,
+ SIGCHLD = 0x14,
+ SIGTTIN = 0x15,
+ SIGTTOU = 0x16,
+ SIGIO = 0x17,
+ SIGXCPU = 0x18,
+ SIGXFSZ = 0x19,
+ SIGVTALRM = 0x1a,
+ SIGPROF = 0x1b,
+ SIGWINCH = 0x1c,
+ SIGINFO = 0x1d,
+ SIGUSR1 = 0x1e,
+ SIGUSR2 = 0x1f,
+
+ FPE_INTDIV = 0x2,
+ FPE_INTOVF = 0x1,
+ FPE_FLTDIV = 0x3,
+ FPE_FLTOVF = 0x4,
+ FPE_FLTUND = 0x5,
+ FPE_FLTRES = 0x6,
+ FPE_FLTINV = 0x7,
+ FPE_FLTSUB = 0x8,
+
+ BUS_ADRALN = 0x1,
+ BUS_ADRERR = 0x2,
+ BUS_OBJERR = 0x3,
+
+ SEGV_MAPERR = 0x1,
+ SEGV_ACCERR = 0x2,
+
+ ITIMER_REAL = 0x0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
+};
+
+typedef struct Rtprio Rtprio;
+typedef struct ThrParam ThrParam;
+typedef struct Sigaltstack Sigaltstack;
+typedef struct Sigset Sigset;
+typedef struct StackT StackT;
+typedef struct Siginfo Siginfo;
+typedef struct Mcontext Mcontext;
+typedef struct Ucontext Ucontext;
+typedef struct Timespec Timespec;
+typedef struct Timeval Timeval;
+typedef struct Itimerval Itimerval;
+
+#pragma pack on
+
+struct Rtprio {
+ uint16 type;
+ uint16 prio;
+};
+struct ThrParam {
+ void *start_func;
+ byte *arg;
+ uint8 *stack_base;
+ uint32 stack_size;
+ uint8 *tls_base;
+ uint32 tls_size;
+ int32 *child_tid;
+ int32 *parent_tid;
+ int32 flags;
+ Rtprio *rtp;
+ void *spare[3];
+};
+struct Sigaltstack {
+ uint8 *ss_sp;
+ uint32 ss_size;
+ int32 ss_flags;
+};
+struct Sigset {
+ uint32 __bits[4];
+};
+struct StackT {
+ uint8 *ss_sp;
+ uint32 ss_size;
+ int32 ss_flags;
+};
+
+struct Siginfo {
+ int32 si_signo;
+ int32 si_errno;
+ int32 si_code;
+ int32 si_pid;
+ uint32 si_uid;
+ int32 si_status;
+ byte *si_addr;
+ byte si_value[4];
+ byte _reason[32];
+};
+
+struct Mcontext {
+ uint32 __gregs[17];
+ byte __fpu[140];
+};
+struct Ucontext {
+ Sigset uc_sigmask;
+ Mcontext uc_mcontext;
+ Ucontext *uc_link;
+ StackT uc_stack;
+ int32 uc_flags;
+ int32 __spare__[4];
+};
+
+struct Timespec {
+ int64 tv_sec;
+ int32 tv_nsec;
+};
+struct Timeval {
+ int64 tv_sec;
+ int32 tv_usec;
+};
+struct Itimerval {
+ Timeval it_interval;
+ Timeval it_value;
+};
+
+
+#pragma pack off
diff --git a/src/pkg/runtime/defs_linux_386.h b/src/pkg/runtime/defs_linux_386.h
index 02760f987..e257a6f85 100644
--- a/src/pkg/runtime/defs_linux_386.h
+++ b/src/pkg/runtime/defs_linux_386.h
@@ -132,7 +132,7 @@ struct Sigaction {
void *k_sa_handler;
uint32 sa_flags;
void *sa_restorer;
- uint32 sa_mask;
+ uint64 sa_mask;
};
struct Siginfo {
int32 si_signo;
diff --git a/src/pkg/runtime/defs_linux_arm.h b/src/pkg/runtime/defs_linux_arm.h
index 9e5c83a07..f72ec3d1b 100644
--- a/src/pkg/runtime/defs_linux_arm.h
+++ b/src/pkg/runtime/defs_linux_arm.h
@@ -143,6 +143,6 @@ struct Sigaction {
void *sa_handler;
uint32 sa_flags;
void *sa_restorer;
- uint32 sa_mask;
+ uint64 sa_mask;
};
#pragma pack off
diff --git a/src/pkg/runtime/defs_netbsd.go b/src/pkg/runtime/defs_netbsd.go
index 47c30cf10..53e061041 100644
--- a/src/pkg/runtime/defs_netbsd.go
+++ b/src/pkg/runtime/defs_netbsd.go
@@ -7,18 +7,21 @@
/*
Input to cgo.
-GOARCH=amd64 cgo -cdefs defs.go >amd64/defs.h
-GOARCH=386 cgo -cdefs defs.go >386/defs.h
+GOARCH=amd64 go tool cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go >defs_netbsd_amd64.h
+GOARCH=386 go tool cgo -cdefs defs_netbsd.go defs_netbsd_386.go >defs_netbsd_386.h
*/
+// +godefs map __fpregset_t [644]byte
+
package runtime
/*
#include <sys/types.h>
#include <sys/mman.h>
+#include <sys/signal.h>
#include <sys/time.h>
+#include <sys/ucontext.h>
#include <sys/unistd.h>
-#include <sys/signal.h>
#include <errno.h>
#include <signal.h>
*/
@@ -34,6 +37,8 @@ const (
MAP_PRIVATE = C.MAP_PRIVATE
MAP_FIXED = C.MAP_FIXED
+ MADV_FREE = C.MADV_FREE
+
SA_SIGINFO = C.SA_SIGINFO
SA_RESTART = C.SA_RESTART
SA_ONSTACK = C.SA_ONSTACK
@@ -95,8 +100,7 @@ const (
type Sigaltstack C.struct_sigaltstack
type Sigset C.sigset_t
-type Siginfo C.siginfo_t
-type Sigval C.union_sigval
+type Siginfo C.struct__ksiginfo
type StackT C.stack_t
@@ -104,8 +108,5 @@ type Timespec C.struct_timespec
type Timeval C.struct_timeval
type Itimerval C.struct_itimerval
-// This is a hack to avoid pulling in machine/fpu.h.
-type sfxsave64 struct{}
-type usavefpu struct{}
-
-type Sigcontext C.struct_sigcontext
+type McontextT C.mcontext_t
+type UcontextT C.ucontext_t
diff --git a/src/pkg/runtime/defs_netbsd_386.go b/src/pkg/runtime/defs_netbsd_386.go
new file mode 100644
index 000000000..e9e36608e
--- /dev/null
+++ b/src/pkg/runtime/defs_netbsd_386.go
@@ -0,0 +1,42 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+/*
+Input to cgo.
+
+GOARCH=amd64 go tool cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go >defs_netbsd_amd64.h
+GOARCH=386 go tool cgo -cdefs defs_netbsd.go defs_netbsd_386.go >defs_netbsd_386.h
+*/
+
+package runtime
+
+/*
+#include <sys/types.h>
+#include <machine/mcontext.h>
+*/
+import "C"
+
+const (
+ REG_GS = C._REG_GS
+ REG_FS = C._REG_FS
+ REG_ES = C._REG_ES
+ REG_DS = C._REG_DS
+ REG_EDI = C._REG_EDI
+ REG_ESI = C._REG_ESI
+ REG_EBP = C._REG_EBP
+ REG_ESP = C._REG_ESP
+ REG_EBX = C._REG_EBX
+ REG_EDX = C._REG_EDX
+ REG_ECX = C._REG_ECX
+ REG_EAX = C._REG_EAX
+ REG_TRAPNO = C._REG_TRAPNO
+ REG_ERR = C._REG_ERR
+ REG_EIP = C._REG_EIP
+ REG_CS = C._REG_CS
+ REG_EFL = C._REG_EFL
+ REG_UESP = C._REG_UESP
+ REG_SS = C._REG_SS
+)
diff --git a/src/pkg/runtime/defs_netbsd_386.h b/src/pkg/runtime/defs_netbsd_386.h
index aff87fb3b..04c380e3f 100644
--- a/src/pkg/runtime/defs_netbsd_386.h
+++ b/src/pkg/runtime/defs_netbsd_386.h
@@ -1,146 +1,163 @@
-// godefs -f -m32 defs.c
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_netbsd.go defs_netbsd_386.go
-// MACHINE GENERATED - DO NOT EDIT.
-// Constants
enum {
- PROT_NONE = 0,
- PROT_READ = 0x1,
- PROT_WRITE = 0x2,
- PROT_EXEC = 0x4,
- MAP_ANON = 0x1000,
- MAP_PRIVATE = 0x2,
- MAP_FIXED = 0x10,
- SA_SIGINFO = 0x40,
- SA_RESTART = 0x2,
- SA_ONSTACK = 0x1,
- EINTR = 0x4,
- SIGHUP = 0x1,
- SIGINT = 0x2,
- SIGQUIT = 0x3,
- SIGILL = 0x4,
- SIGTRAP = 0x5,
- SIGABRT = 0x6,
- SIGEMT = 0x7,
- SIGFPE = 0x8,
- SIGKILL = 0x9,
- SIGBUS = 0xa,
- SIGSEGV = 0xb,
- SIGSYS = 0xc,
- SIGPIPE = 0xd,
- SIGALRM = 0xe,
- SIGTERM = 0xf,
- SIGURG = 0x10,
- SIGSTOP = 0x11,
- SIGTSTP = 0x12,
- SIGCONT = 0x13,
- SIGCHLD = 0x14,
- SIGTTIN = 0x15,
- SIGTTOU = 0x16,
- SIGIO = 0x17,
- SIGXCPU = 0x18,
- SIGXFSZ = 0x19,
- SIGVTALRM = 0x1a,
- SIGPROF = 0x1b,
- SIGWINCH = 0x1c,
- SIGINFO = 0x1d,
- SIGUSR1 = 0x1e,
- SIGUSR2 = 0x1f,
- FPE_INTDIV = 0x1,
- FPE_INTOVF = 0x2,
- FPE_FLTDIV = 0x3,
- FPE_FLTOVF = 0x4,
- FPE_FLTUND = 0x5,
- FPE_FLTRES = 0x6,
- FPE_FLTINV = 0x7,
- FPE_FLTSUB = 0x8,
- BUS_ADRALN = 0x1,
- BUS_ADRERR = 0x2,
- BUS_OBJERR = 0x3,
- SEGV_MAPERR = 0x1,
- SEGV_ACCERR = 0x2,
- ITIMER_REAL = 0,
- ITIMER_VIRTUAL = 0x1,
- ITIMER_PROF = 0x2,
+ PROT_NONE = 0x0,
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+ PROT_EXEC = 0x4,
+
+ MAP_ANON = 0x1000,
+ MAP_PRIVATE = 0x2,
+ MAP_FIXED = 0x10,
+
+ MADV_FREE = 0x6,
+
+ SA_SIGINFO = 0x40,
+ SA_RESTART = 0x2,
+ SA_ONSTACK = 0x1,
+
+ EINTR = 0x4,
+
+ SIGHUP = 0x1,
+ SIGINT = 0x2,
+ SIGQUIT = 0x3,
+ SIGILL = 0x4,
+ SIGTRAP = 0x5,
+ SIGABRT = 0x6,
+ SIGEMT = 0x7,
+ SIGFPE = 0x8,
+ SIGKILL = 0x9,
+ SIGBUS = 0xa,
+ SIGSEGV = 0xb,
+ SIGSYS = 0xc,
+ SIGPIPE = 0xd,
+ SIGALRM = 0xe,
+ SIGTERM = 0xf,
+ SIGURG = 0x10,
+ SIGSTOP = 0x11,
+ SIGTSTP = 0x12,
+ SIGCONT = 0x13,
+ SIGCHLD = 0x14,
+ SIGTTIN = 0x15,
+ SIGTTOU = 0x16,
+ SIGIO = 0x17,
+ SIGXCPU = 0x18,
+ SIGXFSZ = 0x19,
+ SIGVTALRM = 0x1a,
+ SIGPROF = 0x1b,
+ SIGWINCH = 0x1c,
+ SIGINFO = 0x1d,
+ SIGUSR1 = 0x1e,
+ SIGUSR2 = 0x1f,
+
+ FPE_INTDIV = 0x1,
+ FPE_INTOVF = 0x2,
+ FPE_FLTDIV = 0x3,
+ FPE_FLTOVF = 0x4,
+ FPE_FLTUND = 0x5,
+ FPE_FLTRES = 0x6,
+ FPE_FLTINV = 0x7,
+ FPE_FLTSUB = 0x8,
+
+ BUS_ADRALN = 0x1,
+ BUS_ADRERR = 0x2,
+ BUS_OBJERR = 0x3,
+
+ SEGV_MAPERR = 0x1,
+ SEGV_ACCERR = 0x2,
+
+ ITIMER_REAL = 0x0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
};
-// Types
+typedef struct Sigaltstack Sigaltstack;
+typedef struct Sigset Sigset;
+typedef struct Siginfo Siginfo;
+typedef struct StackT StackT;
+typedef struct Timespec Timespec;
+typedef struct Timeval Timeval;
+typedef struct Itimerval Itimerval;
+typedef struct McontextT McontextT;
+typedef struct UcontextT UcontextT;
+
#pragma pack on
-typedef struct Sigaltstack Sigaltstack;
struct Sigaltstack {
- void *ss_sp;
- uint32 ss_size;
- int32 ss_flags;
+ byte *ss_sp;
+ uint32 ss_size;
+ int32 ss_flags;
};
-
-typedef uint32 Sigset;
-
-typedef struct Siginfo Siginfo;
-struct Siginfo {
- int32 si_signo;
- int32 si_code;
- int32 si_errno;
- byte _data[116];
+struct Sigset {
+ uint32 __bits[4];
};
-
-typedef union Sigval Sigval;
-union Sigval {
- int32 sival_int;
- void *sival_ptr;
+struct Siginfo {
+ int32 _signo;
+ int32 _code;
+ int32 _errno;
+ byte _reason[20];
};
-typedef struct StackT StackT;
struct StackT {
- void *ss_sp;
- uint32 ss_size;
- int32 ss_flags;
+ byte *ss_sp;
+ uint32 ss_size;
+ int32 ss_flags;
};
-typedef struct Timespec Timespec;
struct Timespec {
- int32 tv_sec;
- int32 tv_nsec;
+ int64 tv_sec;
+ int32 tv_nsec;
};
-
-typedef struct Timeval Timeval;
struct Timeval {
- int32 tv_sec;
- int32 tv_usec;
+ int64 tv_sec;
+ int32 tv_usec;
};
-
-typedef struct Itimerval Itimerval;
struct Itimerval {
- Timeval it_interval;
- Timeval it_value;
+ Timeval it_interval;
+ Timeval it_value;
};
-typedef void sfxsave64;
-
-typedef void usavefpu;
-
-typedef struct Sigcontext Sigcontext;
-struct Sigcontext {
- int32 sc_gs;
- int32 sc_fs;
- int32 sc_es;
- int32 sc_ds;
- int32 sc_edi;
- int32 sc_esi;
- int32 sc_ebp;
- int32 sc_ebx;
- int32 sc_edx;
- int32 sc_ecx;
- int32 sc_eax;
- int32 sc_eip;
- int32 sc_cs;
- int32 sc_eflags;
- int32 sc_esp;
- int32 sc_ss;
- int32 sc_onstack;
- int32 sc_mask;
- int32 sc_trapno;
- int32 sc_err;
- usavefpu *sc_fpstate;
+struct McontextT {
+ int32 __gregs[19];
+ byte __fpregs[644];
+ int32 _mc_tlsbase;
+};
+struct UcontextT {
+ uint32 uc_flags;
+ UcontextT *uc_link;
+ Sigset uc_sigmask;
+ StackT uc_stack;
+ McontextT uc_mcontext;
+ int32 __uc_pad[4];
};
+
+
#pragma pack off
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_netbsd.go defs_netbsd_386.go
+
+
+enum {
+ REG_GS = 0x0,
+ REG_FS = 0x1,
+ REG_ES = 0x2,
+ REG_DS = 0x3,
+ REG_EDI = 0x4,
+ REG_ESI = 0x5,
+ REG_EBP = 0x6,
+ REG_ESP = 0x7,
+ REG_EBX = 0x8,
+ REG_EDX = 0x9,
+ REG_ECX = 0xa,
+ REG_EAX = 0xb,
+ REG_TRAPNO = 0xc,
+ REG_ERR = 0xd,
+ REG_EIP = 0xe,
+ REG_CS = 0xf,
+ REG_EFL = 0x10,
+ REG_UESP = 0x11,
+ REG_SS = 0x12,
+};
+
diff --git a/src/pkg/runtime/defs_netbsd_amd64.go b/src/pkg/runtime/defs_netbsd_amd64.go
new file mode 100644
index 000000000..68f586b2f
--- /dev/null
+++ b/src/pkg/runtime/defs_netbsd_amd64.go
@@ -0,0 +1,49 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+/*
+Input to cgo.
+
+GOARCH=amd64 go tool cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go >defs_netbsd_amd64.h
+GOARCH=386 go tool cgo -cdefs defs_netbsd.go defs_netbsd_386.go >defs_netbsd_386.h
+*/
+
+package runtime
+
+/*
+#include <sys/types.h>
+#include <machine/mcontext.h>
+*/
+import "C"
+
+const (
+ REG_RDI = C._REG_RDI
+ REG_RSI = C._REG_RSI
+ REG_RDX = C._REG_RDX
+ REG_RCX = C._REG_RCX
+ REG_R8 = C._REG_R8
+ REG_R9 = C._REG_R9
+ REG_R10 = C._REG_R10
+ REG_R11 = C._REG_R11
+ REG_R12 = C._REG_R12
+ REG_R13 = C._REG_R13
+ REG_R14 = C._REG_R14
+ REG_R15 = C._REG_R15
+ REG_RBP = C._REG_RBP
+ REG_RBX = C._REG_RBX
+ REG_RAX = C._REG_RAX
+ REG_GS = C._REG_GS
+ REG_FS = C._REG_FS
+ REG_ES = C._REG_ES
+ REG_DS = C._REG_DS
+ REG_TRAPNO = C._REG_TRAPNO
+ REG_ERR = C._REG_ERR
+ REG_RIP = C._REG_RIP
+ REG_CS = C._REG_CS
+ REG_RFLAGS = C._REG_RFLAGS
+ REG_RSP = C._REG_RSP
+ REG_SS = C._REG_SS
+)
diff --git a/src/pkg/runtime/defs_netbsd_amd64.h b/src/pkg/runtime/defs_netbsd_amd64.h
index 27bf4b9d6..3d3f576d3 100644
--- a/src/pkg/runtime/defs_netbsd_amd64.h
+++ b/src/pkg/runtime/defs_netbsd_amd64.h
@@ -1,158 +1,174 @@
-// godefs -f -m64 defs.c
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go
-// MACHINE GENERATED - DO NOT EDIT.
-// Constants
enum {
- PROT_NONE = 0,
- PROT_READ = 0x1,
- PROT_WRITE = 0x2,
- PROT_EXEC = 0x4,
- MAP_ANON = 0x1000,
- MAP_PRIVATE = 0x2,
- MAP_FIXED = 0x10,
- SA_SIGINFO = 0x40,
- SA_RESTART = 0x2,
- SA_ONSTACK = 0x1,
- EINTR = 0x4,
- SIGHUP = 0x1,
- SIGINT = 0x2,
- SIGQUIT = 0x3,
- SIGILL = 0x4,
- SIGTRAP = 0x5,
- SIGABRT = 0x6,
- SIGEMT = 0x7,
- SIGFPE = 0x8,
- SIGKILL = 0x9,
- SIGBUS = 0xa,
- SIGSEGV = 0xb,
- SIGSYS = 0xc,
- SIGPIPE = 0xd,
- SIGALRM = 0xe,
- SIGTERM = 0xf,
- SIGURG = 0x10,
- SIGSTOP = 0x11,
- SIGTSTP = 0x12,
- SIGCONT = 0x13,
- SIGCHLD = 0x14,
- SIGTTIN = 0x15,
- SIGTTOU = 0x16,
- SIGIO = 0x17,
- SIGXCPU = 0x18,
- SIGXFSZ = 0x19,
- SIGVTALRM = 0x1a,
- SIGPROF = 0x1b,
- SIGWINCH = 0x1c,
- SIGINFO = 0x1d,
- SIGUSR1 = 0x1e,
- SIGUSR2 = 0x1f,
- FPE_INTDIV = 0x1,
- FPE_INTOVF = 0x2,
- FPE_FLTDIV = 0x3,
- FPE_FLTOVF = 0x4,
- FPE_FLTUND = 0x5,
- FPE_FLTRES = 0x6,
- FPE_FLTINV = 0x7,
- FPE_FLTSUB = 0x8,
- BUS_ADRALN = 0x1,
- BUS_ADRERR = 0x2,
- BUS_OBJERR = 0x3,
- SEGV_MAPERR = 0x1,
- SEGV_ACCERR = 0x2,
- ITIMER_REAL = 0,
- ITIMER_VIRTUAL = 0x1,
- ITIMER_PROF = 0x2,
+ PROT_NONE = 0x0,
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+ PROT_EXEC = 0x4,
+
+ MAP_ANON = 0x1000,
+ MAP_PRIVATE = 0x2,
+ MAP_FIXED = 0x10,
+
+ MADV_FREE = 0x6,
+
+ SA_SIGINFO = 0x40,
+ SA_RESTART = 0x2,
+ SA_ONSTACK = 0x1,
+
+ EINTR = 0x4,
+
+ SIGHUP = 0x1,
+ SIGINT = 0x2,
+ SIGQUIT = 0x3,
+ SIGILL = 0x4,
+ SIGTRAP = 0x5,
+ SIGABRT = 0x6,
+ SIGEMT = 0x7,
+ SIGFPE = 0x8,
+ SIGKILL = 0x9,
+ SIGBUS = 0xa,
+ SIGSEGV = 0xb,
+ SIGSYS = 0xc,
+ SIGPIPE = 0xd,
+ SIGALRM = 0xe,
+ SIGTERM = 0xf,
+ SIGURG = 0x10,
+ SIGSTOP = 0x11,
+ SIGTSTP = 0x12,
+ SIGCONT = 0x13,
+ SIGCHLD = 0x14,
+ SIGTTIN = 0x15,
+ SIGTTOU = 0x16,
+ SIGIO = 0x17,
+ SIGXCPU = 0x18,
+ SIGXFSZ = 0x19,
+ SIGVTALRM = 0x1a,
+ SIGPROF = 0x1b,
+ SIGWINCH = 0x1c,
+ SIGINFO = 0x1d,
+ SIGUSR1 = 0x1e,
+ SIGUSR2 = 0x1f,
+
+ FPE_INTDIV = 0x1,
+ FPE_INTOVF = 0x2,
+ FPE_FLTDIV = 0x3,
+ FPE_FLTOVF = 0x4,
+ FPE_FLTUND = 0x5,
+ FPE_FLTRES = 0x6,
+ FPE_FLTINV = 0x7,
+ FPE_FLTSUB = 0x8,
+
+ BUS_ADRALN = 0x1,
+ BUS_ADRERR = 0x2,
+ BUS_OBJERR = 0x3,
+
+ SEGV_MAPERR = 0x1,
+ SEGV_ACCERR = 0x2,
+
+ ITIMER_REAL = 0x0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
};
-// Types
+typedef struct Sigaltstack Sigaltstack;
+typedef struct Sigset Sigset;
+typedef struct Siginfo Siginfo;
+typedef struct StackT StackT;
+typedef struct Timespec Timespec;
+typedef struct Timeval Timeval;
+typedef struct Itimerval Itimerval;
+typedef struct McontextT McontextT;
+typedef struct UcontextT UcontextT;
+
#pragma pack on
-typedef struct Sigaltstack Sigaltstack;
struct Sigaltstack {
- void *ss_sp;
- uint64 ss_size;
- int32 ss_flags;
- byte pad_godefs_0[4];
+ byte *ss_sp;
+ uint64 ss_size;
+ int32 ss_flags;
+ byte Pad_cgo_0[4];
};
-
-typedef uint32 Sigset;
-
-typedef struct Siginfo Siginfo;
-struct Siginfo {
- int32 si_signo;
- int32 si_code;
- int32 si_errno;
- byte pad_godefs_0[4];
- byte _data[120];
+struct Sigset {
+ uint32 __bits[4];
};
-
-typedef union Sigval Sigval;
-union Sigval {
- int32 sival_int;
- void *sival_ptr;
+struct Siginfo {
+ int32 _signo;
+ int32 _code;
+ int32 _errno;
+ int32 _pad;
+ byte _reason[24];
};
-typedef struct StackT StackT;
struct StackT {
- void *ss_sp;
- uint64 ss_size;
- int32 ss_flags;
- byte pad_godefs_0[4];
+ byte *ss_sp;
+ uint64 ss_size;
+ int32 ss_flags;
+ byte Pad_cgo_0[4];
};
-typedef struct Timespec Timespec;
struct Timespec {
- int32 tv_sec;
- byte pad_godefs_0[4];
- int64 tv_nsec;
+ int64 tv_sec;
+ int64 tv_nsec;
};
-
-typedef struct Timeval Timeval;
struct Timeval {
- int64 tv_sec;
- int64 tv_usec;
+ int64 tv_sec;
+ int32 tv_usec;
+ byte Pad_cgo_0[4];
};
-
-typedef struct Itimerval Itimerval;
struct Itimerval {
- Timeval it_interval;
- Timeval it_value;
+ Timeval it_interval;
+ Timeval it_value;
};
-typedef void sfxsave64;
-
-typedef void usavefpu;
-
-typedef struct Sigcontext Sigcontext;
-struct Sigcontext {
- int64 sc_rdi;
- int64 sc_rsi;
- int64 sc_rdx;
- int64 sc_rcx;
- int64 sc_r8;
- int64 sc_r9;
- int64 sc_r10;
- int64 sc_r11;
- int64 sc_r12;
- int64 sc_r13;
- int64 sc_r14;
- int64 sc_r15;
- int64 sc_rbp;
- int64 sc_rbx;
- int64 sc_rax;
- int64 sc_gs;
- int64 sc_fs;
- int64 sc_es;
- int64 sc_ds;
- int64 sc_trapno;
- int64 sc_err;
- int64 sc_rip;
- int64 sc_cs;
- int64 sc_rflags;
- int64 sc_rsp;
- int64 sc_ss;
- sfxsave64 *sc_fpstate;
- int32 sc_onstack;
- int32 sc_mask;
+struct McontextT {
+ uint64 __gregs[26];
+ uint64 _mc_tlsbase;
+ int8 __fpregs[512];
+};
+struct UcontextT {
+ uint32 uc_flags;
+ byte Pad_cgo_0[4];
+ UcontextT *uc_link;
+ Sigset uc_sigmask;
+ StackT uc_stack;
+ McontextT uc_mcontext;
};
+
+
#pragma pack off
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go
+
+
+enum {
+ REG_RDI = 0x0,
+ REG_RSI = 0x1,
+ REG_RDX = 0x2,
+ REG_RCX = 0x3,
+ REG_R8 = 0x4,
+ REG_R9 = 0x5,
+ REG_R10 = 0x6,
+ REG_R11 = 0x7,
+ REG_R12 = 0x8,
+ REG_R13 = 0x9,
+ REG_R14 = 0xa,
+ REG_R15 = 0xb,
+ REG_RBP = 0xc,
+ REG_RBX = 0xd,
+ REG_RAX = 0xe,
+ REG_GS = 0xf,
+ REG_FS = 0x10,
+ REG_ES = 0x11,
+ REG_DS = 0x12,
+ REG_TRAPNO = 0x13,
+ REG_ERR = 0x14,
+ REG_RIP = 0x15,
+ REG_CS = 0x16,
+ REG_RFLAGS = 0x17,
+ REG_RSP = 0x18,
+ REG_SS = 0x19,
+};
+
diff --git a/src/pkg/runtime/defs_netbsd_arm.h b/src/pkg/runtime/defs_netbsd_arm.h
new file mode 100644
index 000000000..f67475c76
--- /dev/null
+++ b/src/pkg/runtime/defs_netbsd_arm.h
@@ -0,0 +1,140 @@
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_netbsd.go
+
+
+enum {
+ PROT_NONE = 0x0,
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+ PROT_EXEC = 0x4,
+
+ MAP_ANON = 0x1000,
+ MAP_PRIVATE = 0x2,
+ MAP_FIXED = 0x10,
+
+ MADV_FREE = 0x6,
+
+ SA_SIGINFO = 0x40,
+ SA_RESTART = 0x2,
+ SA_ONSTACK = 0x1,
+
+ EINTR = 0x4,
+
+ SIGHUP = 0x1,
+ SIGINT = 0x2,
+ SIGQUIT = 0x3,
+ SIGILL = 0x4,
+ SIGTRAP = 0x5,
+ SIGABRT = 0x6,
+ SIGEMT = 0x7,
+ SIGFPE = 0x8,
+ SIGKILL = 0x9,
+ SIGBUS = 0xa,
+ SIGSEGV = 0xb,
+ SIGSYS = 0xc,
+ SIGPIPE = 0xd,
+ SIGALRM = 0xe,
+ SIGTERM = 0xf,
+ SIGURG = 0x10,
+ SIGSTOP = 0x11,
+ SIGTSTP = 0x12,
+ SIGCONT = 0x13,
+ SIGCHLD = 0x14,
+ SIGTTIN = 0x15,
+ SIGTTOU = 0x16,
+ SIGIO = 0x17,
+ SIGXCPU = 0x18,
+ SIGXFSZ = 0x19,
+ SIGVTALRM = 0x1a,
+ SIGPROF = 0x1b,
+ SIGWINCH = 0x1c,
+ SIGINFO = 0x1d,
+ SIGUSR1 = 0x1e,
+ SIGUSR2 = 0x1f,
+
+ FPE_INTDIV = 0x1,
+ FPE_INTOVF = 0x2,
+ FPE_FLTDIV = 0x3,
+ FPE_FLTOVF = 0x4,
+ FPE_FLTUND = 0x5,
+ FPE_FLTRES = 0x6,
+ FPE_FLTINV = 0x7,
+ FPE_FLTSUB = 0x8,
+
+ BUS_ADRALN = 0x1,
+ BUS_ADRERR = 0x2,
+ BUS_OBJERR = 0x3,
+
+ SEGV_MAPERR = 0x1,
+ SEGV_ACCERR = 0x2,
+
+ ITIMER_REAL = 0x0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
+};
+
+typedef struct Sigaltstack Sigaltstack;
+typedef struct Sigset Sigset;
+typedef struct Siginfo Siginfo;
+typedef struct StackT StackT;
+typedef struct Timespec Timespec;
+typedef struct Timeval Timeval;
+typedef struct Itimerval Itimerval;
+typedef struct McontextT McontextT;
+typedef struct UcontextT UcontextT;
+
+#pragma pack on
+
+struct Sigaltstack {
+ byte *ss_sp;
+ uint32 ss_size;
+ int32 ss_flags;
+};
+struct Sigset {
+ uint32 __bits[4];
+};
+struct Siginfo {
+ int32 _signo;
+ int32 _code;
+ int32 _errno;
+ byte _reason[20];
+};
+
+struct StackT {
+ byte *ss_sp;
+ uint32 ss_size;
+ int32 ss_flags;
+};
+
+struct Timespec {
+ int64 tv_sec;
+ int32 tv_nsec;
+};
+struct Timeval {
+ int64 tv_sec;
+ int32 tv_usec;
+};
+struct Itimerval {
+ Timeval it_interval;
+ Timeval it_value;
+};
+
+struct McontextT {
+ uint32 __gregs[17];
+#ifdef __ARM_EABI__
+ byte __fpu[4+8*32+4];
+#else
+ byte __fpu[4+4*33+4];
+#endif
+ uint32 _mc_tlsbase;
+};
+struct UcontextT {
+ uint32 uc_flags;
+ UcontextT *uc_link;
+ Sigset uc_sigmask;
+ StackT uc_stack;
+ McontextT uc_mcontext;
+ int32 __uc_pad[2];
+};
+
+#pragma pack off
diff --git a/src/pkg/runtime/defs_openbsd.go b/src/pkg/runtime/defs_openbsd.go
index 47c30cf10..ff94b9405 100644
--- a/src/pkg/runtime/defs_openbsd.go
+++ b/src/pkg/runtime/defs_openbsd.go
@@ -7,8 +7,8 @@
/*
Input to cgo.
-GOARCH=amd64 cgo -cdefs defs.go >amd64/defs.h
-GOARCH=386 cgo -cdefs defs.go >386/defs.h
+GOARCH=amd64 go tool cgo -cdefs defs_openbsd.go >defs_openbsd_amd64.h
+GOARCH=386 go tool cgo -cdefs defs_openbsd.go >defs_openbsd_386.h
*/
package runtime
@@ -34,6 +34,8 @@ const (
MAP_PRIVATE = C.MAP_PRIVATE
MAP_FIXED = C.MAP_FIXED
+ MADV_FREE = C.MADV_FREE
+
SA_SIGINFO = C.SA_SIGINFO
SA_RESTART = C.SA_RESTART
SA_ONSTACK = C.SA_ONSTACK
@@ -93,9 +95,12 @@ const (
ITIMER_PROF = C.ITIMER_PROF
)
+type Tfork C.struct___tfork
+
type Sigaltstack C.struct_sigaltstack
-type Sigset C.sigset_t
+type Sigcontext C.struct_sigcontext
type Siginfo C.siginfo_t
+type Sigset C.sigset_t
type Sigval C.union_sigval
type StackT C.stack_t
@@ -103,9 +108,3 @@ type StackT C.stack_t
type Timespec C.struct_timespec
type Timeval C.struct_timeval
type Itimerval C.struct_itimerval
-
-// This is a hack to avoid pulling in machine/fpu.h.
-type sfxsave64 struct{}
-type usavefpu struct{}
-
-type Sigcontext C.struct_sigcontext
diff --git a/src/pkg/runtime/defs_openbsd_386.h b/src/pkg/runtime/defs_openbsd_386.h
index aff87fb3b..323bb084a 100644
--- a/src/pkg/runtime/defs_openbsd_386.h
+++ b/src/pkg/runtime/defs_openbsd_386.h
@@ -1,146 +1,150 @@
-// godefs -f -m32 defs.c
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_openbsd.go
-// MACHINE GENERATED - DO NOT EDIT.
-// Constants
enum {
- PROT_NONE = 0,
- PROT_READ = 0x1,
- PROT_WRITE = 0x2,
- PROT_EXEC = 0x4,
- MAP_ANON = 0x1000,
- MAP_PRIVATE = 0x2,
- MAP_FIXED = 0x10,
- SA_SIGINFO = 0x40,
- SA_RESTART = 0x2,
- SA_ONSTACK = 0x1,
- EINTR = 0x4,
- SIGHUP = 0x1,
- SIGINT = 0x2,
- SIGQUIT = 0x3,
- SIGILL = 0x4,
- SIGTRAP = 0x5,
- SIGABRT = 0x6,
- SIGEMT = 0x7,
- SIGFPE = 0x8,
- SIGKILL = 0x9,
- SIGBUS = 0xa,
- SIGSEGV = 0xb,
- SIGSYS = 0xc,
- SIGPIPE = 0xd,
- SIGALRM = 0xe,
- SIGTERM = 0xf,
- SIGURG = 0x10,
- SIGSTOP = 0x11,
- SIGTSTP = 0x12,
- SIGCONT = 0x13,
- SIGCHLD = 0x14,
- SIGTTIN = 0x15,
- SIGTTOU = 0x16,
- SIGIO = 0x17,
- SIGXCPU = 0x18,
- SIGXFSZ = 0x19,
- SIGVTALRM = 0x1a,
- SIGPROF = 0x1b,
- SIGWINCH = 0x1c,
- SIGINFO = 0x1d,
- SIGUSR1 = 0x1e,
- SIGUSR2 = 0x1f,
- FPE_INTDIV = 0x1,
- FPE_INTOVF = 0x2,
- FPE_FLTDIV = 0x3,
- FPE_FLTOVF = 0x4,
- FPE_FLTUND = 0x5,
- FPE_FLTRES = 0x6,
- FPE_FLTINV = 0x7,
- FPE_FLTSUB = 0x8,
- BUS_ADRALN = 0x1,
- BUS_ADRERR = 0x2,
- BUS_OBJERR = 0x3,
- SEGV_MAPERR = 0x1,
- SEGV_ACCERR = 0x2,
- ITIMER_REAL = 0,
- ITIMER_VIRTUAL = 0x1,
- ITIMER_PROF = 0x2,
-};
+ PROT_NONE = 0x0,
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+ PROT_EXEC = 0x4,
-// Types
-#pragma pack on
+ MAP_ANON = 0x1000,
+ MAP_PRIVATE = 0x2,
+ MAP_FIXED = 0x10,
-typedef struct Sigaltstack Sigaltstack;
-struct Sigaltstack {
- void *ss_sp;
- uint32 ss_size;
- int32 ss_flags;
-};
+ MADV_FREE = 0x6,
+
+ SA_SIGINFO = 0x40,
+ SA_RESTART = 0x2,
+ SA_ONSTACK = 0x1,
+
+ EINTR = 0x4,
+
+ SIGHUP = 0x1,
+ SIGINT = 0x2,
+ SIGQUIT = 0x3,
+ SIGILL = 0x4,
+ SIGTRAP = 0x5,
+ SIGABRT = 0x6,
+ SIGEMT = 0x7,
+ SIGFPE = 0x8,
+ SIGKILL = 0x9,
+ SIGBUS = 0xa,
+ SIGSEGV = 0xb,
+ SIGSYS = 0xc,
+ SIGPIPE = 0xd,
+ SIGALRM = 0xe,
+ SIGTERM = 0xf,
+ SIGURG = 0x10,
+ SIGSTOP = 0x11,
+ SIGTSTP = 0x12,
+ SIGCONT = 0x13,
+ SIGCHLD = 0x14,
+ SIGTTIN = 0x15,
+ SIGTTOU = 0x16,
+ SIGIO = 0x17,
+ SIGXCPU = 0x18,
+ SIGXFSZ = 0x19,
+ SIGVTALRM = 0x1a,
+ SIGPROF = 0x1b,
+ SIGWINCH = 0x1c,
+ SIGINFO = 0x1d,
+ SIGUSR1 = 0x1e,
+ SIGUSR2 = 0x1f,
+
+ FPE_INTDIV = 0x1,
+ FPE_INTOVF = 0x2,
+ FPE_FLTDIV = 0x3,
+ FPE_FLTOVF = 0x4,
+ FPE_FLTUND = 0x5,
+ FPE_FLTRES = 0x6,
+ FPE_FLTINV = 0x7,
+ FPE_FLTSUB = 0x8,
+
+ BUS_ADRALN = 0x1,
+ BUS_ADRERR = 0x2,
+ BUS_OBJERR = 0x3,
-typedef uint32 Sigset;
+ SEGV_MAPERR = 0x1,
+ SEGV_ACCERR = 0x2,
+ ITIMER_REAL = 0x0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
+};
+
+typedef struct Tfork Tfork;
+typedef struct Sigaltstack Sigaltstack;
+typedef struct Sigcontext Sigcontext;
typedef struct Siginfo Siginfo;
-struct Siginfo {
- int32 si_signo;
- int32 si_code;
- int32 si_errno;
- byte _data[116];
+typedef struct StackT StackT;
+typedef struct Timespec Timespec;
+typedef struct Timeval Timeval;
+typedef struct Itimerval Itimerval;
+
+#pragma pack on
+
+struct Tfork {
+ byte *tf_tcb;
+ int32 *tf_tid;
+ byte *tf_stack;
};
-typedef union Sigval Sigval;
-union Sigval {
- int32 sival_int;
- void *sival_ptr;
+struct Sigaltstack {
+ byte *ss_sp;
+ uint32 ss_size;
+ int32 ss_flags;
};
+struct Sigcontext {
+ int32 sc_gs;
+ int32 sc_fs;
+ int32 sc_es;
+ int32 sc_ds;
+ int32 sc_edi;
+ int32 sc_esi;
+ int32 sc_ebp;
+ int32 sc_ebx;
+ int32 sc_edx;
+ int32 sc_ecx;
+ int32 sc_eax;
+ int32 sc_eip;
+ int32 sc_cs;
+ int32 sc_eflags;
+ int32 sc_esp;
+ int32 sc_ss;
+ int32 sc_onstack;
+ int32 sc_mask;
+ int32 sc_trapno;
+ int32 sc_err;
+ void *sc_fpstate;
+};
+struct Siginfo {
+ int32 si_signo;
+ int32 si_code;
+ int32 si_errno;
+ byte _data[116];
+};
+typedef uint32 Sigset;
+typedef byte Sigval[4];
-typedef struct StackT StackT;
struct StackT {
- void *ss_sp;
- uint32 ss_size;
- int32 ss_flags;
+ byte *ss_sp;
+ uint32 ss_size;
+ int32 ss_flags;
};
-typedef struct Timespec Timespec;
struct Timespec {
- int32 tv_sec;
- int32 tv_nsec;
+ int32 tv_sec;
+ int32 tv_nsec;
};
-
-typedef struct Timeval Timeval;
struct Timeval {
- int32 tv_sec;
- int32 tv_usec;
+ int32 tv_sec;
+ int32 tv_usec;
};
-
-typedef struct Itimerval Itimerval;
struct Itimerval {
- Timeval it_interval;
- Timeval it_value;
+ Timeval it_interval;
+ Timeval it_value;
};
-typedef void sfxsave64;
-typedef void usavefpu;
-
-typedef struct Sigcontext Sigcontext;
-struct Sigcontext {
- int32 sc_gs;
- int32 sc_fs;
- int32 sc_es;
- int32 sc_ds;
- int32 sc_edi;
- int32 sc_esi;
- int32 sc_ebp;
- int32 sc_ebx;
- int32 sc_edx;
- int32 sc_ecx;
- int32 sc_eax;
- int32 sc_eip;
- int32 sc_cs;
- int32 sc_eflags;
- int32 sc_esp;
- int32 sc_ss;
- int32 sc_onstack;
- int32 sc_mask;
- int32 sc_trapno;
- int32 sc_err;
- usavefpu *sc_fpstate;
-};
#pragma pack off
diff --git a/src/pkg/runtime/defs_openbsd_amd64.h b/src/pkg/runtime/defs_openbsd_amd64.h
index 27bf4b9d6..429cc99f0 100644
--- a/src/pkg/runtime/defs_openbsd_amd64.h
+++ b/src/pkg/runtime/defs_openbsd_amd64.h
@@ -1,158 +1,162 @@
-// godefs -f -m64 defs.c
+// Created by cgo -cdefs - DO NOT EDIT
+// cgo -cdefs defs_openbsd.go
-// MACHINE GENERATED - DO NOT EDIT.
-// Constants
enum {
- PROT_NONE = 0,
- PROT_READ = 0x1,
- PROT_WRITE = 0x2,
- PROT_EXEC = 0x4,
- MAP_ANON = 0x1000,
- MAP_PRIVATE = 0x2,
- MAP_FIXED = 0x10,
- SA_SIGINFO = 0x40,
- SA_RESTART = 0x2,
- SA_ONSTACK = 0x1,
- EINTR = 0x4,
- SIGHUP = 0x1,
- SIGINT = 0x2,
- SIGQUIT = 0x3,
- SIGILL = 0x4,
- SIGTRAP = 0x5,
- SIGABRT = 0x6,
- SIGEMT = 0x7,
- SIGFPE = 0x8,
- SIGKILL = 0x9,
- SIGBUS = 0xa,
- SIGSEGV = 0xb,
- SIGSYS = 0xc,
- SIGPIPE = 0xd,
- SIGALRM = 0xe,
- SIGTERM = 0xf,
- SIGURG = 0x10,
- SIGSTOP = 0x11,
- SIGTSTP = 0x12,
- SIGCONT = 0x13,
- SIGCHLD = 0x14,
- SIGTTIN = 0x15,
- SIGTTOU = 0x16,
- SIGIO = 0x17,
- SIGXCPU = 0x18,
- SIGXFSZ = 0x19,
- SIGVTALRM = 0x1a,
- SIGPROF = 0x1b,
- SIGWINCH = 0x1c,
- SIGINFO = 0x1d,
- SIGUSR1 = 0x1e,
- SIGUSR2 = 0x1f,
- FPE_INTDIV = 0x1,
- FPE_INTOVF = 0x2,
- FPE_FLTDIV = 0x3,
- FPE_FLTOVF = 0x4,
- FPE_FLTUND = 0x5,
- FPE_FLTRES = 0x6,
- FPE_FLTINV = 0x7,
- FPE_FLTSUB = 0x8,
- BUS_ADRALN = 0x1,
- BUS_ADRERR = 0x2,
- BUS_OBJERR = 0x3,
- SEGV_MAPERR = 0x1,
- SEGV_ACCERR = 0x2,
- ITIMER_REAL = 0,
- ITIMER_VIRTUAL = 0x1,
- ITIMER_PROF = 0x2,
-};
+ PROT_NONE = 0x0,
+ PROT_READ = 0x1,
+ PROT_WRITE = 0x2,
+ PROT_EXEC = 0x4,
-// Types
-#pragma pack on
+ MAP_ANON = 0x1000,
+ MAP_PRIVATE = 0x2,
+ MAP_FIXED = 0x10,
-typedef struct Sigaltstack Sigaltstack;
-struct Sigaltstack {
- void *ss_sp;
- uint64 ss_size;
- int32 ss_flags;
- byte pad_godefs_0[4];
-};
+ MADV_FREE = 0x6,
+
+ SA_SIGINFO = 0x40,
+ SA_RESTART = 0x2,
+ SA_ONSTACK = 0x1,
+
+ EINTR = 0x4,
+
+ SIGHUP = 0x1,
+ SIGINT = 0x2,
+ SIGQUIT = 0x3,
+ SIGILL = 0x4,
+ SIGTRAP = 0x5,
+ SIGABRT = 0x6,
+ SIGEMT = 0x7,
+ SIGFPE = 0x8,
+ SIGKILL = 0x9,
+ SIGBUS = 0xa,
+ SIGSEGV = 0xb,
+ SIGSYS = 0xc,
+ SIGPIPE = 0xd,
+ SIGALRM = 0xe,
+ SIGTERM = 0xf,
+ SIGURG = 0x10,
+ SIGSTOP = 0x11,
+ SIGTSTP = 0x12,
+ SIGCONT = 0x13,
+ SIGCHLD = 0x14,
+ SIGTTIN = 0x15,
+ SIGTTOU = 0x16,
+ SIGIO = 0x17,
+ SIGXCPU = 0x18,
+ SIGXFSZ = 0x19,
+ SIGVTALRM = 0x1a,
+ SIGPROF = 0x1b,
+ SIGWINCH = 0x1c,
+ SIGINFO = 0x1d,
+ SIGUSR1 = 0x1e,
+ SIGUSR2 = 0x1f,
+
+ FPE_INTDIV = 0x1,
+ FPE_INTOVF = 0x2,
+ FPE_FLTDIV = 0x3,
+ FPE_FLTOVF = 0x4,
+ FPE_FLTUND = 0x5,
+ FPE_FLTRES = 0x6,
+ FPE_FLTINV = 0x7,
+ FPE_FLTSUB = 0x8,
+
+ BUS_ADRALN = 0x1,
+ BUS_ADRERR = 0x2,
+ BUS_OBJERR = 0x3,
-typedef uint32 Sigset;
+ SEGV_MAPERR = 0x1,
+ SEGV_ACCERR = 0x2,
+ ITIMER_REAL = 0x0,
+ ITIMER_VIRTUAL = 0x1,
+ ITIMER_PROF = 0x2,
+};
+
+typedef struct Tfork Tfork;
+typedef struct Sigaltstack Sigaltstack;
+typedef struct Sigcontext Sigcontext;
typedef struct Siginfo Siginfo;
-struct Siginfo {
- int32 si_signo;
- int32 si_code;
- int32 si_errno;
- byte pad_godefs_0[4];
- byte _data[120];
+typedef struct StackT StackT;
+typedef struct Timespec Timespec;
+typedef struct Timeval Timeval;
+typedef struct Itimerval Itimerval;
+
+#pragma pack on
+
+struct Tfork {
+ byte *tf_tcb;
+ int32 *tf_tid;
+ byte *tf_stack;
};
-typedef union Sigval Sigval;
-union Sigval {
- int32 sival_int;
- void *sival_ptr;
+struct Sigaltstack {
+ byte *ss_sp;
+ uint64 ss_size;
+ int32 ss_flags;
+ byte Pad_cgo_0[4];
};
+struct Sigcontext {
+ int64 sc_rdi;
+ int64 sc_rsi;
+ int64 sc_rdx;
+ int64 sc_rcx;
+ int64 sc_r8;
+ int64 sc_r9;
+ int64 sc_r10;
+ int64 sc_r11;
+ int64 sc_r12;
+ int64 sc_r13;
+ int64 sc_r14;
+ int64 sc_r15;
+ int64 sc_rbp;
+ int64 sc_rbx;
+ int64 sc_rax;
+ int64 sc_gs;
+ int64 sc_fs;
+ int64 sc_es;
+ int64 sc_ds;
+ int64 sc_trapno;
+ int64 sc_err;
+ int64 sc_rip;
+ int64 sc_cs;
+ int64 sc_rflags;
+ int64 sc_rsp;
+ int64 sc_ss;
+ void *sc_fpstate;
+ int32 sc_onstack;
+ int32 sc_mask;
+};
+struct Siginfo {
+ int32 si_signo;
+ int32 si_code;
+ int32 si_errno;
+ byte Pad_cgo_0[4];
+ byte _data[120];
+};
+typedef uint32 Sigset;
+typedef byte Sigval[8];
-typedef struct StackT StackT;
struct StackT {
- void *ss_sp;
- uint64 ss_size;
- int32 ss_flags;
- byte pad_godefs_0[4];
+ byte *ss_sp;
+ uint64 ss_size;
+ int32 ss_flags;
+ byte Pad_cgo_0[4];
};
-typedef struct Timespec Timespec;
struct Timespec {
- int32 tv_sec;
- byte pad_godefs_0[4];
- int64 tv_nsec;
+ int32 tv_sec;
+ byte Pad_cgo_0[4];
+ int64 tv_nsec;
};
-
-typedef struct Timeval Timeval;
struct Timeval {
- int64 tv_sec;
- int64 tv_usec;
+ int64 tv_sec;
+ int64 tv_usec;
};
-
-typedef struct Itimerval Itimerval;
struct Itimerval {
- Timeval it_interval;
- Timeval it_value;
+ Timeval it_interval;
+ Timeval it_value;
};
-typedef void sfxsave64;
-typedef void usavefpu;
-
-typedef struct Sigcontext Sigcontext;
-struct Sigcontext {
- int64 sc_rdi;
- int64 sc_rsi;
- int64 sc_rdx;
- int64 sc_rcx;
- int64 sc_r8;
- int64 sc_r9;
- int64 sc_r10;
- int64 sc_r11;
- int64 sc_r12;
- int64 sc_r13;
- int64 sc_r14;
- int64 sc_r15;
- int64 sc_rbp;
- int64 sc_rbx;
- int64 sc_rax;
- int64 sc_gs;
- int64 sc_fs;
- int64 sc_es;
- int64 sc_ds;
- int64 sc_trapno;
- int64 sc_err;
- int64 sc_rip;
- int64 sc_cs;
- int64 sc_rflags;
- int64 sc_rsp;
- int64 sc_ss;
- sfxsave64 *sc_fpstate;
- int32 sc_onstack;
- int32 sc_mask;
-};
#pragma pack off
diff --git a/src/pkg/runtime/defs_plan9_386.h b/src/pkg/runtime/defs_plan9_386.h
index 58fd9d94d..bde299dee 100644
--- a/src/pkg/runtime/defs_plan9_386.h
+++ b/src/pkg/runtime/defs_plan9_386.h
@@ -1,2 +1,29 @@
-// nothing to see here
-#define tos_pid 48
+#define PAGESIZE 0x1000
+
+typedef struct Ureg Ureg;
+
+struct Ureg
+{
+ uint32 di; /* general registers */
+ uint32 si; /* ... */
+ uint32 bp; /* ... */
+ uint32 nsp;
+ uint32 bx; /* ... */
+ uint32 dx; /* ... */
+ uint32 cx; /* ... */
+ uint32 ax; /* ... */
+ uint32 gs; /* data segments */
+ uint32 fs; /* ... */
+ uint32 es; /* ... */
+ uint32 ds; /* ... */
+ uint32 trap; /* trap type */
+ uint32 ecode; /* error code (or zero) */
+ uint32 pc; /* pc */
+ uint32 cs; /* old context */
+ uint32 flags; /* old flags */
+ union {
+ uint32 usp;
+ uint32 sp;
+ };
+ uint32 ss; /* old stack segment */
+};
diff --git a/src/pkg/runtime/defs_plan9_amd64.h b/src/pkg/runtime/defs_plan9_amd64.h
new file mode 100644
index 000000000..d8fec67eb
--- /dev/null
+++ b/src/pkg/runtime/defs_plan9_amd64.h
@@ -0,0 +1,34 @@
+#define PAGESIZE 0x200000ULL
+
+typedef struct Ureg Ureg;
+
+struct Ureg {
+ uint64 ax;
+ uint64 bx;
+ uint64 cx;
+ uint64 dx;
+ uint64 si;
+ uint64 di;
+ uint64 bp;
+ uint64 r8;
+ uint64 r9;
+ uint64 r10;
+ uint64 r11;
+ uint64 r12;
+ uint64 r13;
+ uint64 r14;
+ uint64 r15;
+
+ uint16 ds;
+ uint16 es;
+ uint16 fs;
+ uint16 gs;
+
+ uint64 type;
+ uint64 error; /* error code (or zero) */
+ uint64 ip; /* pc */
+ uint64 cs; /* old context */
+ uint64 flags; /* old flags */
+ uint64 sp; /* sp */
+ uint64 ss; /* old stack segment */
+};
diff --git a/src/pkg/runtime/env_plan9.c b/src/pkg/runtime/env_plan9.c
new file mode 100644
index 000000000..848d73303
--- /dev/null
+++ b/src/pkg/runtime/env_plan9.c
@@ -0,0 +1,33 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "runtime.h"
+#include "os_GOOS.h"
+
+byte*
+runtime·getenv(int8 *s)
+{
+ int32 fd, len, n, r;
+ byte file[128];
+ byte *p;
+
+ len = runtime·findnull((byte*)s);
+ if(len > sizeof file-6)
+ return nil;
+
+ runtime·memclr(file, sizeof file);
+ runtime·memmove((void*)file, (void*)"/env/", 5);
+ runtime·memmove((void*)(file+5), (void*)s, len);
+
+ fd = runtime·open(file, OREAD);
+ if(fd < 0)
+ return nil;
+ n = runtime·seek(fd, 0, 2);
+ p = runtime·malloc(n+1);
+ r = runtime·pread(fd, p, n, 0);
+ runtime·close(fd);
+ if(r < 0)
+ return nil;
+ return p;
+}
diff --git a/src/pkg/runtime/env_posix.c b/src/pkg/runtime/env_posix.c
new file mode 100644
index 000000000..8333811fb
--- /dev/null
+++ b/src/pkg/runtime/env_posix.c
@@ -0,0 +1,61 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd windows
+
+#include "runtime.h"
+
+Slice syscall·envs;
+
+byte*
+runtime·getenv(int8 *s)
+{
+ int32 i, j, len;
+ byte *v, *bs;
+ String* envv;
+ int32 envc;
+
+ bs = (byte*)s;
+ len = runtime·findnull(bs);
+ envv = (String*)syscall·envs.array;
+ envc = syscall·envs.len;
+ for(i=0; i<envc; i++){
+ if(envv[i].len <= len)
+ continue;
+ v = envv[i].str;
+ for(j=0; j<len; j++)
+ if(bs[j] != v[j])
+ goto nomatch;
+ if(v[len] != '=')
+ goto nomatch;
+ return v+len+1;
+ nomatch:;
+ }
+ return nil;
+}
+
+void (*_cgo_setenv)(byte**);
+
+// Update the C environment if cgo is loaded.
+// Called from syscall.Setenv.
+void
+syscall·setenv_c(String k, String v)
+{
+ byte *arg[2];
+
+ if(_cgo_setenv == nil)
+ return;
+
+ arg[0] = runtime·malloc(k.len + 1);
+ runtime·memmove(arg[0], k.str, k.len);
+ arg[0][k.len] = 0;
+
+ arg[1] = runtime·malloc(v.len + 1);
+ runtime·memmove(arg[1], v.str, v.len);
+ arg[1][v.len] = 0;
+
+ runtime·asmcgocall((void*)_cgo_setenv, arg);
+ runtime·free(arg[0]);
+ runtime·free(arg[1]);
+}
diff --git a/src/pkg/runtime/export_test.go b/src/pkg/runtime/export_test.go
index 51921135b..062aea248 100644
--- a/src/pkg/runtime/export_test.go
+++ b/src/pkg/runtime/export_test.go
@@ -25,3 +25,45 @@ var Entersyscall = entersyscall
var Exitsyscall = exitsyscall
var LockedOSThread = golockedOSThread
var Stackguard = stackguard
+
+type LFNode struct {
+ Next *LFNode
+ Pushcnt uintptr
+}
+
+func lfstackpush(head *uint64, node *LFNode)
+func lfstackpop2(head *uint64) *LFNode
+
+var LFStackPush = lfstackpush
+var LFStackPop = lfstackpop2
+
+type ParFor struct {
+ body *byte
+ done uint32
+ Nthr uint32
+ nthrmax uint32
+ thrseq uint32
+ Cnt uint32
+ Ctx *byte
+ wait bool
+}
+
+func parforalloc2(nthrmax uint32) *ParFor
+func parforsetup2(desc *ParFor, nthr, n uint32, ctx *byte, wait bool, body func(*ParFor, uint32))
+func parfordo(desc *ParFor)
+func parforiters(desc *ParFor, tid uintptr) (uintptr, uintptr)
+
+var NewParFor = parforalloc2
+var ParForSetup = parforsetup2
+var ParForDo = parfordo
+
+func ParForIters(desc *ParFor, tid uint32) (uint32, uint32) {
+ begin, end := parforiters(desc, uintptr(tid))
+ return uint32(begin), uint32(end)
+}
+
+func testSchedLocalQueue()
+func testSchedLocalQueueSteal()
+
+var TestSchedLocalQueue1 = testSchedLocalQueue
+var TestSchedLocalQueueSteal1 = testSchedLocalQueueSteal
diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go
index d93259d7b..fbaffd1d5 100644
--- a/src/pkg/runtime/extern.go
+++ b/src/pkg/runtime/extern.go
@@ -42,8 +42,8 @@ type Func struct { // Keep in sync with runtime.h:struct Func
pc0 uintptr // starting pc, ln for table
ln0 int32
frame int32 // stack frame size
- args int32 // number of 32-bit in/out args
- locals int32 // number of 32-bit locals
+ args int32 // in/out args size
+ locals int32 // locals size
}
// FuncForPC returns a *Func describing the function that contains the
@@ -67,7 +67,7 @@ func (f *Func) FileLine(pc uintptr) (file string, line int) {
// implemented in symtab.c
func funcline_go(*Func, uintptr) (string, int)
-// mid returns the current os thread (m) id.
+// mid returns the current OS thread (m) id.
func mid() uint32
// SetFinalizer sets the finalizer associated with x to f.
diff --git a/src/pkg/runtime/float.c b/src/pkg/runtime/float.c
index f481519f6..42082e434 100644
--- a/src/pkg/runtime/float.c
+++ b/src/pkg/runtime/float.c
@@ -4,170 +4,7 @@
#include "runtime.h"
-static uint64 uvnan = 0x7FF0000000000001ULL;
-static uint64 uvinf = 0x7FF0000000000000ULL;
-static uint64 uvneginf = 0xFFF0000000000000ULL;
-
-uint32
-runtime·float32tobits(float32 f)
-{
- // The obvious cast-and-pointer code is technically
- // not valid, and gcc miscompiles it. Use a union instead.
- union {
- float32 f;
- uint32 i;
- } u;
- u.f = f;
- return u.i;
-}
-
-uint64
-runtime·float64tobits(float64 f)
-{
- // The obvious cast-and-pointer code is technically
- // not valid, and gcc miscompiles it. Use a union instead.
- union {
- float64 f;
- uint64 i;
- } u;
- u.f = f;
- return u.i;
-}
-
-float64
-runtime·float64frombits(uint64 i)
-{
- // The obvious cast-and-pointer code is technically
- // not valid, and gcc miscompiles it. Use a union instead.
- union {
- float64 f;
- uint64 i;
- } u;
- u.i = i;
- return u.f;
-}
-
-float32
-runtime·float32frombits(uint32 i)
-{
- // The obvious cast-and-pointer code is technically
- // not valid, and gcc miscompiles it. Use a union instead.
- union {
- float32 f;
- uint32 i;
- } u;
- u.i = i;
- return u.f;
-}
-
-bool
-runtime·isInf(float64 f, int32 sign)
-{
- uint64 x;
-
- x = runtime·float64tobits(f);
- if(sign == 0)
- return x == uvinf || x == uvneginf;
- if(sign > 0)
- return x == uvinf;
- return x == uvneginf;
-}
-
-float64
-runtime·NaN(void)
-{
- return runtime·float64frombits(uvnan);
-}
-
-bool
-runtime·isNaN(float64 f)
-{
- uint64 x;
-
- x = runtime·float64tobits(f);
- return ((uint32)(x>>52) & 0x7FF) == 0x7FF && !runtime·isInf(f, 0);
-}
-
-float64
-runtime·Inf(int32 sign)
-{
- if(sign >= 0)
- return runtime·float64frombits(uvinf);
- else
- return runtime·float64frombits(uvneginf);
-}
-
-enum
-{
- MASK = 0x7ffL,
- SHIFT = 64-11-1,
- BIAS = 1022L,
-};
-
-float64
-runtime·frexp(float64 d, int32 *ep)
-{
- uint64 x;
-
- if(d == 0) {
- *ep = 0;
- return 0;
- }
- x = runtime·float64tobits(d);
- *ep = (int32)((x >> SHIFT) & MASK) - BIAS;
- x &= ~((uint64)MASK << SHIFT);
- x |= (uint64)BIAS << SHIFT;
- return runtime·float64frombits(x);
-}
-
-float64
-runtime·ldexp(float64 d, int32 e)
-{
- uint64 x;
-
- if(d == 0)
- return 0;
- x = runtime·float64tobits(d);
- e += (int32)(x >> SHIFT) & MASK;
- if(e <= 0)
- return 0; /* underflow */
- if(e >= MASK){ /* overflow */
- if(d < 0)
- return runtime·Inf(-1);
- return runtime·Inf(1);
- }
- x &= ~((uint64)MASK << SHIFT);
- x |= (uint64)e << SHIFT;
- return runtime·float64frombits(x);
-}
-
-float64
-runtime·modf(float64 d, float64 *ip)
-{
- float64 dd;
- uint64 x;
- int32 e;
-
- if(d < 1) {
- if(d < 0) {
- d = runtime·modf(-d, ip);
- *ip = -*ip;
- return -d;
- }
- *ip = 0;
- return d;
- }
-
- x = runtime·float64tobits(d);
- e = (int32)((x >> SHIFT) & MASK) - BIAS;
-
- /*
- * Keep the top 11+e bits; clear the rest.
- */
- if(e <= 64-11)
- x &= ~(((uint64)1 << (64LL-11LL-e))-1);
- dd = runtime·float64frombits(x);
- *ip = dd;
- return d - dd;
-}
-
+// used as float64 via runtime· names
+uint64 ·nan = 0x7FF8000000000001ULL;
+uint64 ·posinf = 0x7FF0000000000000ULL;
+uint64 ·neginf = 0xFFF0000000000000ULL;
diff --git a/src/pkg/runtime/gc_test.go b/src/pkg/runtime/gc_test.go
index 65894a6fd..e1e1b1d01 100644
--- a/src/pkg/runtime/gc_test.go
+++ b/src/pkg/runtime/gc_test.go
@@ -5,37 +5,80 @@
package runtime_test
import (
+ "os"
"runtime"
"testing"
)
func TestGcSys(t *testing.T) {
+ if os.Getenv("GOGC") == "off" {
+ t.Fatalf("GOGC=off in environment; test cannot pass")
+ }
+ data := struct{ Short bool }{testing.Short()}
+ got := executeTest(t, testGCSysSource, &data)
+ want := "OK\n"
+ if got != want {
+ t.Fatalf("expected %q, but got %q", want, got)
+ }
+}
+
+const testGCSysSource = `
+package main
+
+import (
+ "fmt"
+ "runtime"
+)
+
+func main() {
+ runtime.GOMAXPROCS(1)
memstats := new(runtime.MemStats)
runtime.GC()
runtime.ReadMemStats(memstats)
sys := memstats.Sys
+ runtime.MemProfileRate = 0 // disable profiler
+
itercount := 1000000
- if testing.Short() {
- itercount = 100000
- }
+{{if .Short}}
+ itercount = 100000
+{{end}}
for i := 0; i < itercount; i++ {
workthegc()
}
// Should only be using a few MB.
+ // We allocated 100 MB or (if not short) 1 GB.
runtime.ReadMemStats(memstats)
if sys > memstats.Sys {
sys = 0
} else {
sys = memstats.Sys - sys
}
- t.Logf("used %d extra bytes", sys)
- if sys > 4<<20 {
- t.Fatalf("using too much memory: %d bytes", sys)
+ if sys > 16<<20 {
+ fmt.Printf("using too much memory: %d bytes\n", sys)
+ return
}
+ fmt.Printf("OK\n")
}
func workthegc() []byte {
return make([]byte, 1029)
}
+`
+
+func TestGcDeepNesting(t *testing.T) {
+ type T [2][2][2][2][2][2][2][2][2][2]*int
+ a := new(T)
+
+ // Prevent the compiler from applying escape analysis.
+ // This makes sure new(T) is allocated on heap, not on the stack.
+ t.Logf("%p", a)
+
+ a[0][0][0][0][0][0][0][0][0][0] = new(int)
+ *a[0][0][0][0][0][0][0][0][0][0] = 13
+ runtime.GC()
+ if *a[0][0][0][0][0][0][0][0][0][0] != 13 {
+ t.Fail()
+ }
+}
diff --git a/src/pkg/runtime/hashmap.c b/src/pkg/runtime/hashmap.c
index 63ed4e2a3..37111daa9 100644
--- a/src/pkg/runtime/hashmap.c
+++ b/src/pkg/runtime/hashmap.c
@@ -3,8 +3,11 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
+#include "arch_GOARCH.h"
+#include "malloc.h"
#include "hashmap.h"
#include "type.h"
+#include "race.h"
/* Hmap flag values */
#define IndirectVal (1<<0) /* storing pointers to values */
@@ -13,7 +16,7 @@
#define CanFreeKey (1<<3) /* okay to free pointers to keys */
struct Hmap { /* a hash table; initialize with hash_init() */
- uint32 count; /* elements in table - must be first */
+ uintgo count; /* elements in table - must be first */
uint8 datasize; /* amount of data to store in entry */
uint8 flag;
uint8 valoff; /* offset of value in key+value data block */
@@ -78,7 +81,7 @@ hash_subtable_new (Hmap *h, int32 power, int32 used)
max_probes = 1 << power;
}
bytes += limit_bytes - elemsize;
- st = malloc (offsetof (struct hash_subtable, entry[0]) + bytes);
+ st = runtime·mallocgc(offsetof (struct hash_subtable, entry[0]) + bytes, UseSpanType ? FlagNoPointers : 0, 1, 1);
st->power = power;
st->used = used;
st->datasize = h->datasize;
@@ -115,7 +118,7 @@ hash_init (Hmap *h, int32 datasize, int64 hint)
if(datasize < sizeof (void *))
datasize = sizeof (void *);
- datasize = runtime·rnd(datasize, sizeof (void *));
+ datasize = ROUND(datasize, sizeof (void *));
init_sizes (hint, &init_power);
h->datasize = datasize;
assert (h->datasize == datasize);
@@ -273,7 +276,7 @@ hash_lookup (MapType *t, Hmap *h, void *data, void **pres)
struct hash_entry *end_e;
void *key;
bool eq;
-
+
hash = h->hash0;
(*t->key->alg->hash) (&hash, t->key->size, data);
hash &= ~HASH_MASK;
@@ -416,8 +419,12 @@ hash_insert_internal (MapType *t, struct hash_subtable **pst, int32 flags, hash_
*pres = ins_e->data;
return (1);
}
- assert (e_hash != hash || (flags & HASH_REHASH) == 0);
- hash += (e_hash == hash); /* adjust hash if it collides */
+ if (e_hash == hash) { /* adjust hash if it collides */
+ assert ((flags & HASH_REHASH) == 0);
+ hash++;
+ if ((hash & HASH_MASK) == HASH_SUBHASH)
+ runtime·throw("runtime: map hash collision overflow");
+ }
ins_e = HASH_OFFSET (ins_e, elemsize);
ins_i++;
if (e_hash <= hash) { /* set e to insertion point */
@@ -462,7 +469,7 @@ hash_insert (MapType *t, Hmap *h, void *data, void **pres)
{
uintptr hash;
int32 rc;
-
+
hash = h->hash0;
(*t->key->alg->hash) (&hash, t->key->size, data);
rc = hash_insert_internal (t, &h->st, 0, hash, h, data, pres);
@@ -618,7 +625,7 @@ hash_iter_init (MapType *t, Hmap *h, struct hash_iter *it)
it->subtable_state[0].e = h->st->entry;
it->subtable_state[0].start = h->st->entry;
it->subtable_state[0].last = h->st->last;
-
+
// fastrand1 returns 31 useful bits.
// We don't care about not having a bottom bit but we
// do want top bits.
@@ -700,6 +707,82 @@ hash_visit (Hmap *h, void (*data_visit) (void *arg, int32 level, void *data), vo
hash_visit_internal (h->st, 0, 0, data_visit, arg);
}
+// Initialize the iterator.
+// Returns false if Hmap contains no pointers (in which case the iterator is not initialized).
+bool
+hash_gciter_init (Hmap *h, struct hash_gciter *it)
+{
+ // GC during map initialization
+ if(h->st == nil)
+ return false;
+
+ it->elemsize = h->datasize + offsetof (struct hash_entry, data[0]);
+ it->flag = h->flag;
+ it->valoff = h->valoff;
+ it->i = 0;
+ it->st = h->st;
+ it->subtable_state[it->i].e = h->st->entry;
+ it->subtable_state[it->i].last = h->st->last;
+ return true;
+}
+
+// Returns true and fills *data with subtable/key/value data,
+// or returns false if the iterator has terminated.
+bool
+hash_gciter_next (struct hash_gciter *it, struct hash_gciter_data *data)
+{
+ struct hash_entry *e;
+ struct hash_gciter_sub *sub;
+
+ data->st = nil;
+ data->key_data = nil;
+ data->val_data = nil;
+
+ // pointer to the first-level table
+ if(it->st != nil) {
+ data->st = it->st;
+ it->st = nil;
+ return true;
+ }
+
+popped:
+ sub = &it->subtable_state[it->i];
+ e = sub->e;
+ while (e <= sub->last) {
+ if ((e->hash & HASH_MASK) == HASH_SUBHASH) {
+ struct hash_subtable *st = *(struct hash_subtable **)e->data;
+ data->st = st;
+ sub->e = HASH_OFFSET (e, it->elemsize);
+
+ // push
+ it->i++;
+ assert (it->i < nelem(it->subtable_state));
+ sub++;
+ sub->e = st->entry;
+ sub->last = st->last;
+
+ return true;
+ }
+ if(e->hash != HASH_NIL) {
+ void *key_data = e->data;
+ void *val_data = (byte*)e->data + it->valoff;
+ data->key_data = key_data;
+ data->val_data = val_data;
+ data->indirectkey = (it->flag & IndirectKey) != 0;
+ data->indirectval = (it->flag & IndirectVal) != 0;
+ sub->e = HASH_OFFSET (e, it->elemsize);
+ return true;
+ }
+ e = HASH_OFFSET (e, it->elemsize);
+ }
+ if(it->i != 0) {
+ // pop
+ it->i--;
+ goto popped;
+ }
+ return false;
+}
+
//
/// interfaces to go runtime
//
@@ -724,14 +807,13 @@ hash_keyptr(Hmap *h, void *p)
static int32 debug = 0;
-// makemap(typ *Type, hint uint32) (hmap *map[any]any);
Hmap*
runtime·makemap_c(MapType *typ, int64 hint)
{
Hmap *h;
Type *key, *val;
uintptr ksize, vsize;
-
+
key = typ->key;
val = typ->elem;
@@ -744,8 +826,15 @@ runtime·makemap_c(MapType *typ, int64 hint)
h = runtime·mal(sizeof(*h));
h->flag |= CanFreeTable; /* until reflect gets involved, free is okay */
- ksize = runtime·rnd(key->size, sizeof(void*));
- vsize = runtime·rnd(val->size, sizeof(void*));
+ if(UseSpanType) {
+ if(false) {
+ runtime·printf("makemap %S: %p\n", *typ->string, h);
+ }
+ runtime·settype(h, (uintptr)typ | TypeInfo_Map);
+ }
+
+ ksize = ROUND(key->size, sizeof(void*));
+ vsize = ROUND(val->size, sizeof(void*));
if(ksize > MaxData || vsize > MaxData || ksize+vsize > MaxData) {
// Either key is too big, or value is, or combined they are.
// Prefer to keep the key if possible, because we look at
@@ -828,8 +917,11 @@ runtime·mapaccess1(MapType *t, Hmap *h, ...)
byte *ak, *av;
bool pres;
+ if(raceenabled && h != nil)
+ runtime·racereadpc(h, runtime·getcallerpc(&t), runtime·mapaccess1);
+
ak = (byte*)(&h + 1);
- av = ak + runtime·rnd(t->key->size, Structrnd);
+ av = ak + ROUND(t->key->size, Structrnd);
runtime·mapaccess(t, h, ak, av, &pres);
@@ -853,8 +945,11 @@ runtime·mapaccess2(MapType *t, Hmap *h, ...)
{
byte *ak, *av, *ap;
+ if(raceenabled && h != nil)
+ runtime·racereadpc(h, runtime·getcallerpc(&t), runtime·mapaccess2);
+
ak = (byte*)(&h + 1);
- av = ak + runtime·rnd(t->key->size, Structrnd);
+ av = ak + ROUND(t->key->size, Structrnd);
ap = av + t->elem->size;
runtime·mapaccess(t, h, ak, av, ap);
@@ -881,6 +976,9 @@ reflect·mapaccess(MapType *t, Hmap *h, uintptr key, uintptr val, bool pres)
{
byte *ak, *av;
+ if(raceenabled && h != nil)
+ runtime·racereadpc(h, runtime·getcallerpc(&t), reflect·mapaccess);
+
if(t->key->size <= sizeof(key))
ak = (byte*)&key;
else
@@ -951,8 +1049,10 @@ runtime·mapassign1(MapType *t, Hmap *h, ...)
if(h == nil)
runtime·panicstring("assignment to entry in nil map");
+ if(raceenabled)
+ runtime·racewritepc(h, runtime·getcallerpc(&t), runtime·mapassign1);
ak = (byte*)(&h + 1);
- av = ak + runtime·rnd(t->key->size, t->elem->align);
+ av = ak + ROUND(t->key->size, t->elem->align);
runtime·mapassign(t, h, ak, av);
}
@@ -965,8 +1065,10 @@ runtime·mapdelete(MapType *t, Hmap *h, ...)
byte *ak;
if(h == nil)
- runtime·panicstring("deletion of entry in nil map");
+ return;
+ if(raceenabled)
+ runtime·racewritepc(h, runtime·getcallerpc(&t), runtime·mapdelete);
ak = (byte*)(&h + 1);
runtime·mapassign(t, h, ak, nil);
@@ -990,6 +1092,8 @@ reflect·mapassign(MapType *t, Hmap *h, uintptr key, uintptr val, bool pres)
if(h == nil)
runtime·panicstring("assignment to entry in nil map");
+ if(raceenabled)
+ runtime·racewritepc(h, runtime·getcallerpc(&t), reflect·mapassign);
if(t->key->size <= sizeof(key))
ak = (byte*)&key;
else
@@ -1011,6 +1115,8 @@ runtime·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it)
it->data = nil;
return;
}
+ if(raceenabled)
+ runtime·racereadpc(h, runtime·getcallerpc(&t), runtime·mapiterinit);
hash_iter_init(t, h, it);
it->data = hash_next(it);
if(debug) {
@@ -1054,6 +1160,8 @@ reflect·mapiterinit(MapType *t, Hmap *h, struct hash_iter *it)
void
runtime·mapiternext(struct hash_iter *it)
{
+ if(raceenabled)
+ runtime·racereadpc(it->h, runtime·getcallerpc(&it), runtime·mapiternext);
if(runtime·gcwaiting)
runtime·gosched();
@@ -1148,15 +1256,18 @@ reflect·mapiterkey(struct hash_iter *it, uintptr key, bool ok)
}
// For reflect:
-// func maplen(h map) (len int32)
+// func maplen(h map) (len int)
// Like len(m) in the actual language, we treat the nil map as length 0.
void
-reflect·maplen(Hmap *h, int32 len)
+reflect·maplen(Hmap *h, intgo len)
{
if(h == nil)
len = 0;
- else
+ else {
len = h->count;
+ if(raceenabled)
+ runtime·racereadpc(h, runtime·getcallerpc(&h), reflect·maplen);
+ }
FLUSH(&len);
}
@@ -1171,7 +1282,7 @@ runtime·mapiter2(struct hash_iter *it, ...)
t = it->t;
ak = (byte*)(&it + 1);
- av = ak + runtime·rnd(t->key->size, t->elem->align);
+ av = ak + ROUND(t->key->size, t->elem->align);
res = it->data;
if(res == nil)
diff --git a/src/pkg/runtime/hashmap.h b/src/pkg/runtime/hashmap.h
index 4c10cf6ef..9b82f299e 100644
--- a/src/pkg/runtime/hashmap.h
+++ b/src/pkg/runtime/hashmap.h
@@ -63,7 +63,6 @@
}
*/
-#define malloc runtime·mal
#define memset(a,b,c) runtime·memclr((byte*)(a), (uint32)(c))
#define memcpy(a,b,c) runtime·memmove((byte*)(a),(byte*)(b),(uint32)(c))
#define assert(a) if(!(a)) runtime·throw("hashmap assert")
@@ -143,7 +142,7 @@ struct hash_iter {
Remove all sub-tables associated with *h.
This undoes the effects of hash_init().
If other memory pointed to by user data must be freed, the caller is
- responsible for doiing do by iterating over *h first; see
+ responsible for doing so by iterating over *h first; see
hash_iter_init()/hash_next(). */
// void hash_destroy (struct hash *h);
@@ -152,7 +151,7 @@ struct hash_iter {
/* Initialize *it from *h. */
// void hash_iter_init (struct hash *h, struct hash_iter *it);
-/* Return the next used entry in the table which which *it was initialized. */
+/* Return the next used entry in the table with which *it was initialized. */
// void *hash_next (struct hash_iter *it);
/*---- test interface ----*/
@@ -160,3 +159,27 @@ struct hash_iter {
whether used or not. "level" is the subtable level, 0 means first level. */
/* TESTING ONLY: DO NOT USE THIS ROUTINE IN NORMAL CODE */
// void hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg);
+
+/* Used by the garbage collector */
+struct hash_gciter
+{
+ int32 elemsize;
+ uint8 flag;
+ uint8 valoff;
+ uint32 i; /* stack pointer in subtable_state */
+ struct hash_subtable *st;
+ struct hash_gciter_sub {
+ struct hash_entry *e; /* pointer into subtable */
+ struct hash_entry *last; /* last entry in subtable */
+ } subtable_state[4];
+};
+struct hash_gciter_data
+{
+ struct hash_subtable *st; /* subtable pointer, or nil */
+ uint8 *key_data; /* key data, or nil */
+ uint8 *val_data; /* value data, or nil */
+ bool indirectkey; /* storing pointers to keys */
+ bool indirectval; /* storing pointers to values */
+};
+bool hash_gciter_init (struct Hmap *h, struct hash_gciter *it);
+bool hash_gciter_next (struct hash_gciter *it, struct hash_gciter_data *data);
diff --git a/src/pkg/runtime/iface.c b/src/pkg/runtime/iface.c
index 2b60c4f23..370edffb8 100644
--- a/src/pkg/runtime/iface.c
+++ b/src/pkg/runtime/iface.c
@@ -5,6 +5,7 @@
#include "runtime.h"
#include "arch_GOARCH.h"
#include "type.h"
+#include "typekind.h"
#include "malloc.h"
void
@@ -19,19 +20,6 @@ runtime·printeface(Eface e)
runtime·printf("(%p,%p)", e.type, e.data);
}
-/*
- * layout of Itab known to compilers
- */
-struct Itab
-{
- InterfaceType* inter;
- Type* type;
- Itab* link;
- int32 bad;
- int32 unused;
- void (*fun[])(void);
-};
-
static Itab* hash[1009];
static Lock ifacelock;
@@ -182,19 +170,37 @@ copyout(Type *t, void **src, void *dst)
alg->copy(size, dst, *src);
}
-// func convT2I(typ *byte, typ2 *byte, elem any) (ret any)
#pragma textflag 7
void
-runtime·convT2I(Type *t, InterfaceType *inter, ...)
+runtime·typ2Itab(Type *t, InterfaceType *inter, Itab **cache, Itab *ret)
+{
+ Itab *tab;
+
+ tab = itab(inter, t, 0);
+ runtime·atomicstorep(cache, tab);
+ ret = tab;
+ FLUSH(&ret);
+}
+
+// func convT2I(typ *byte, typ2 *byte, cache **byte, elem any) (ret any)
+#pragma textflag 7
+void
+runtime·convT2I(Type *t, InterfaceType *inter, Itab **cache, ...)
{
byte *elem;
Iface *ret;
+ Itab *tab;
int32 wid;
- elem = (byte*)(&inter+1);
+ elem = (byte*)(&cache+1);
wid = t->size;
- ret = (Iface*)(elem + runtime·rnd(wid, Structrnd));
- ret->tab = itab(inter, t, 0);
+ ret = (Iface*)(elem + ROUND(wid, Structrnd));
+ tab = runtime·atomicloadp(cache);
+ if(!tab) {
+ tab = itab(inter, t, 0);
+ runtime·atomicstorep(cache, tab);
+ }
+ ret->tab = tab;
copyin(t, elem, &ret->data);
}
@@ -209,7 +215,7 @@ runtime·convT2E(Type *t, ...)
elem = (byte*)(&t+1);
wid = t->size;
- ret = (Eface*)(elem + runtime·rnd(wid, Structrnd));
+ ret = (Eface*)(elem + ROUND(wid, Structrnd));
ret->type = t;
copyin(t, elem, &ret->data);
}
@@ -272,6 +278,13 @@ runtime·assertI2T2(Type *t, Iface i, ...)
copyout(t, &i.data, ret);
}
+void
+runtime·assertI2TOK(Type *t, Iface i, bool ok)
+{
+ ok = i.tab!=nil && i.tab->type==t;
+ FLUSH(&ok);
+}
+
static void assertE2Tret(Type *t, Eface e, byte *ret);
// func ifaceE2T(typ *byte, iface any) (ret any)
@@ -328,6 +341,13 @@ runtime·assertE2T2(Type *t, Eface e, ...)
copyout(t, &e.data, ret);
}
+void
+runtime·assertE2TOK(Type *t, Eface e, bool ok)
+{
+ ok = t==e.type;
+ FLUSH(&ok);
+}
+
// func convI2E(elem any) (ret any)
void
runtime·convI2E(Iface i, Eface ret)
@@ -387,7 +407,7 @@ void
runtime·convI2I(InterfaceType* inter, Iface i, Iface ret)
{
Itab *tab;
-
+
ret.data = i.data;
if((tab = i.tab) == nil)
ret.tab = nil;
@@ -526,10 +546,10 @@ runtime·assertE2E2(InterfaceType* inter, Eface e, Eface ret, bool ok)
}
static uintptr
-ifacehash1(void *data, Type *t)
+ifacehash1(void *data, Type *t, uintptr h)
{
Alg *alg;
- uintptr size, h;
+ uintptr size;
Eface err;
if(t == nil)
@@ -543,7 +563,6 @@ ifacehash1(void *data, Type *t)
runtime·newErrorString(runtime·catstring(runtime·gostringnocopy((byte*)"hash of unhashable type "), *t->string), &err);
runtime·panic(err);
}
- h = 0;
if(size <= sizeof(data))
alg->hash(&h, size, &data);
else
@@ -552,17 +571,17 @@ ifacehash1(void *data, Type *t)
}
uintptr
-runtime·ifacehash(Iface a)
+runtime·ifacehash(Iface a, uintptr h)
{
if(a.tab == nil)
- return 0;
- return ifacehash1(a.data, a.tab->type);
+ return h;
+ return ifacehash1(a.data, a.tab->type, h);
}
uintptr
-runtime·efacehash(Eface a)
+runtime·efacehash(Eface a, uintptr h)
{
- return ifacehash1(a.data, a.type);
+ return ifacehash1(a.data, a.type, h);
}
static bool
@@ -666,39 +685,54 @@ reflect·unsafe_Typeof(Eface e, Eface ret)
}
void
-reflect·unsafe_New(Eface typ, void *ret)
+reflect·unsafe_New(Type *t, void *ret)
{
- Type *t;
+ uint32 flag;
- // Reflect library has reinterpreted typ
- // as its own kind of type structure.
- // We know that the pointer to the original
- // type structure sits before the data pointer.
- t = (Type*)((Eface*)typ.data-1);
+ flag = t->kind&KindNoPointers ? FlagNoPointers : 0;
+ ret = runtime·mallocgc(t->size, flag, 1, 1);
+
+ if(UseSpanType && !flag) {
+ if(false) {
+ runtime·printf("unsafe_New %S: %p\n", *t->string, ret);
+ }
+ runtime·settype(ret, (uintptr)t | TypeInfo_SingleObject);
+ }
- if(t->kind&KindNoPointers)
- ret = runtime·mallocgc(t->size, FlagNoPointers, 1, 1);
- else
- ret = runtime·mal(t->size);
FLUSH(&ret);
}
void
-reflect·unsafe_NewArray(Eface typ, uint32 n, void *ret)
+reflect·unsafe_NewArray(Type *t, intgo n, void *ret)
{
uint64 size;
- Type *t;
- // Reflect library has reinterpreted typ
- // as its own kind of type structure.
- // We know that the pointer to the original
- // type structure sits before the data pointer.
- t = (Type*)((Eface*)typ.data-1);
-
size = n*t->size;
- if(t->kind&KindNoPointers)
+ if(size == 0)
+ ret = (byte*)&runtime·zerobase;
+ else if(t->kind&KindNoPointers)
ret = runtime·mallocgc(size, FlagNoPointers, 1, 1);
- else
- ret = runtime·mal(size);
+ else {
+ ret = runtime·mallocgc(size, 0, 1, 1);
+
+ if(UseSpanType) {
+ if(false) {
+ runtime·printf("unsafe_NewArray [%D]%S: %p\n", (int64)n, *t->string, ret);
+ }
+ runtime·settype(ret, (uintptr)t | TypeInfo_Array);
+ }
+ }
+
+ FLUSH(&ret);
+}
+
+void
+reflect·typelinks(Slice ret)
+{
+ extern Type *typelink[], *etypelink[];
+ static int32 first = 1;
+ ret.array = (byte*)typelink;
+ ret.len = etypelink - typelink;
+ ret.cap = ret.len;
FLUSH(&ret);
}
diff --git a/src/pkg/runtime/iface_test.go b/src/pkg/runtime/iface_test.go
new file mode 100644
index 000000000..bca0ea0ee
--- /dev/null
+++ b/src/pkg/runtime/iface_test.go
@@ -0,0 +1,138 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime_test
+
+import (
+ "testing"
+)
+
+type I1 interface {
+ Method1()
+}
+
+type I2 interface {
+ Method1()
+ Method2()
+}
+
+type TS uint16
+type TM uintptr
+type TL [2]uintptr
+
+func (TS) Method1() {}
+func (TS) Method2() {}
+func (TM) Method1() {}
+func (TM) Method2() {}
+func (TL) Method1() {}
+func (TL) Method2() {}
+
+var (
+ e interface{}
+ e_ interface{}
+ i1 I1
+ i2 I2
+ ts TS
+ tm TM
+ tl TL
+)
+
+func BenchmarkConvT2ESmall(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ e = ts
+ }
+}
+
+func BenchmarkConvT2EUintptr(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ e = tm
+ }
+}
+
+func BenchmarkConvT2ELarge(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ e = tl
+ }
+}
+
+func BenchmarkConvT2ISmall(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ i1 = ts
+ }
+}
+
+func BenchmarkConvT2IUintptr(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ i1 = tm
+ }
+}
+
+func BenchmarkConvT2ILarge(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ i1 = tl
+ }
+}
+
+func BenchmarkConvI2E(b *testing.B) {
+ i2 = tm
+ for i := 0; i < b.N; i++ {
+ e = i2
+ }
+}
+
+func BenchmarkConvI2I(b *testing.B) {
+ i2 = tm
+ for i := 0; i < b.N; i++ {
+ i1 = i2
+ }
+}
+
+func BenchmarkAssertE2T(b *testing.B) {
+ e = tm
+ for i := 0; i < b.N; i++ {
+ tm = e.(TM)
+ }
+}
+
+func BenchmarkAssertE2TLarge(b *testing.B) {
+ e = tl
+ for i := 0; i < b.N; i++ {
+ tl = e.(TL)
+ }
+}
+
+func BenchmarkAssertE2I(b *testing.B) {
+ e = tm
+ for i := 0; i < b.N; i++ {
+ i1 = e.(I1)
+ }
+}
+
+func BenchmarkAssertI2T(b *testing.B) {
+ i1 = tm
+ for i := 0; i < b.N; i++ {
+ tm = i1.(TM)
+ }
+}
+
+func BenchmarkAssertI2I(b *testing.B) {
+ i1 = tm
+ for i := 0; i < b.N; i++ {
+ i2 = i1.(I2)
+ }
+}
+
+func BenchmarkAssertI2E(b *testing.B) {
+ i1 = tm
+ for i := 0; i < b.N; i++ {
+ e = i1.(interface{})
+ }
+}
+
+func BenchmarkAssertE2E(b *testing.B) {
+ e = tm
+ for i := 0; i < b.N; i++ {
+ e_ = e
+ }
+}
diff --git a/src/pkg/runtime/lfstack.c b/src/pkg/runtime/lfstack.c
new file mode 100644
index 000000000..1d48491aa
--- /dev/null
+++ b/src/pkg/runtime/lfstack.c
@@ -0,0 +1,65 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Lock-free stack.
+
+#include "runtime.h"
+#include "arch_GOARCH.h"
+
+#ifdef _64BIT
+// Amd64 uses 48-bit virtual addresses, 47-th bit is used as kernel/user flag.
+// So we use 17msb of pointers as ABA counter.
+# define PTR_BITS 47
+#else
+# define PTR_BITS 32
+#endif
+#define PTR_MASK ((1ull<<PTR_BITS)-1)
+#define CNT_MASK (0ull-1)
+
+void
+runtime·lfstackpush(uint64 *head, LFNode *node)
+{
+ uint64 old, new;
+
+ if((uintptr)node != ((uintptr)node&PTR_MASK)) {
+ runtime·printf("p=%p\n", node);
+ runtime·throw("runtime·lfstackpush: invalid pointer");
+ }
+
+ node->pushcnt++;
+ new = (uint64)(uintptr)node|(((uint64)node->pushcnt&CNT_MASK)<<PTR_BITS);
+ old = runtime·atomicload64(head);
+ for(;;) {
+ node->next = (LFNode*)(uintptr)(old&PTR_MASK);
+ if(runtime·cas64(head, &old, new))
+ break;
+ }
+}
+
+LFNode*
+runtime·lfstackpop(uint64 *head)
+{
+ LFNode *node, *node2;
+ uint64 old, new;
+
+ old = runtime·atomicload64(head);
+ for(;;) {
+ if(old == 0)
+ return nil;
+ node = (LFNode*)(uintptr)(old&PTR_MASK);
+ node2 = runtime·atomicloadp(&node->next);
+ new = 0;
+ if(node2 != nil)
+ new = (uint64)(uintptr)node2|(((uint64)node2->pushcnt&CNT_MASK)<<PTR_BITS);
+ if(runtime·cas64(head, &old, new))
+ return node;
+ }
+}
+
+void
+runtime·lfstackpop2(uint64 *head, LFNode *node)
+{
+ node = runtime·lfstackpop(head);
+ FLUSH(&node);
+}
diff --git a/src/pkg/runtime/lfstack_test.go b/src/pkg/runtime/lfstack_test.go
new file mode 100644
index 000000000..505aae605
--- /dev/null
+++ b/src/pkg/runtime/lfstack_test.go
@@ -0,0 +1,130 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime_test
+
+import (
+ "math/rand"
+ . "runtime"
+ "testing"
+ "unsafe"
+)
+
+type MyNode struct {
+ LFNode
+ data int
+}
+
+func fromMyNode(node *MyNode) *LFNode {
+ return (*LFNode)(unsafe.Pointer(node))
+}
+
+func toMyNode(node *LFNode) *MyNode {
+ return (*MyNode)(unsafe.Pointer(node))
+}
+
+func TestLFStack(t *testing.T) {
+ stack := new(uint64)
+ // Need to keep additional referenfces to nodes, the stack is not all that type-safe.
+ var nodes []*MyNode
+
+ // Check the stack is initially empty.
+ if LFStackPop(stack) != nil {
+ t.Fatalf("stack is not empty")
+ }
+
+ // Push one element.
+ node := &MyNode{data: 42}
+ nodes = append(nodes, node)
+ LFStackPush(stack, fromMyNode(node))
+
+ // Push another.
+ node = &MyNode{data: 43}
+ nodes = append(nodes, node)
+ LFStackPush(stack, fromMyNode(node))
+
+ // Pop one element.
+ node = toMyNode(LFStackPop(stack))
+ if node == nil {
+ t.Fatalf("stack is empty")
+ }
+ if node.data != 43 {
+ t.Fatalf("no lifo")
+ }
+
+ // Pop another.
+ node = toMyNode(LFStackPop(stack))
+ if node == nil {
+ t.Fatalf("stack is empty")
+ }
+ if node.data != 42 {
+ t.Fatalf("no lifo")
+ }
+
+ // Check the stack is empty again.
+ if LFStackPop(stack) != nil {
+ t.Fatalf("stack is not empty")
+ }
+ if *stack != 0 {
+ t.Fatalf("stack is not empty")
+ }
+}
+
+func TestLFStackStress(t *testing.T) {
+ const K = 100
+ P := 4 * GOMAXPROCS(-1)
+ N := 100000
+ if testing.Short() {
+ N /= 10
+ }
+ // Create 2 stacks.
+ stacks := [2]*uint64{new(uint64), new(uint64)}
+ // Need to keep additional referenfces to nodes, the stack is not all that type-safe.
+ var nodes []*MyNode
+ // Push K elements randomly onto the stacks.
+ sum := 0
+ for i := 0; i < K; i++ {
+ sum += i
+ node := &MyNode{data: i}
+ nodes = append(nodes, node)
+ LFStackPush(stacks[i%2], fromMyNode(node))
+ }
+ c := make(chan bool, P)
+ for p := 0; p < P; p++ {
+ go func() {
+ r := rand.New(rand.NewSource(rand.Int63()))
+ // Pop a node from a random stack, then push it onto a random stack.
+ for i := 0; i < N; i++ {
+ node := toMyNode(LFStackPop(stacks[r.Intn(2)]))
+ if node != nil {
+ LFStackPush(stacks[r.Intn(2)], fromMyNode(node))
+ }
+ }
+ c <- true
+ }()
+ }
+ for i := 0; i < P; i++ {
+ <-c
+ }
+ // Pop all elements from both stacks, and verify that nothing lost.
+ sum2 := 0
+ cnt := 0
+ for i := 0; i < 2; i++ {
+ for {
+ node := toMyNode(LFStackPop(stacks[i]))
+ if node == nil {
+ break
+ }
+ cnt++
+ sum2 += node.data
+ node.Next = nil
+ }
+ }
+ if cnt != K {
+ t.Fatalf("Wrong number of nodes %d/%d", cnt, K)
+ }
+ if sum2 != sum {
+ t.Fatalf("Wrong sum %d/%d", sum2, sum)
+ }
+}
diff --git a/src/pkg/runtime/lock_futex.c b/src/pkg/runtime/lock_futex.c
index b4465bff1..9b1f5f6db 100644
--- a/src/pkg/runtime/lock_futex.c
+++ b/src/pkg/runtime/lock_futex.c
@@ -111,7 +111,8 @@ runtime·noteclear(Note *n)
void
runtime·notewakeup(Note *n)
{
- runtime·xchg(&n->key, 1);
+ if(runtime·xchg(&n->key, 1))
+ runtime·throw("notewakeup - double wakeup");
runtime·futexwakeup(&n->key, 1);
}
diff --git a/src/pkg/runtime/malloc.goc b/src/pkg/runtime/malloc.goc
index 9ae3a9d61..ac131b3af 100644
--- a/src/pkg/runtime/malloc.goc
+++ b/src/pkg/runtime/malloc.goc
@@ -9,17 +9,18 @@
package runtime
#include "runtime.h"
#include "arch_GOARCH.h"
-#include "stack.h"
#include "malloc.h"
-#include "defs_GOOS_GOARCH.h"
#include "type.h"
+#include "typekind.h"
+#include "race.h"
-#pragma dataflag 16 /* mark mheap as 'no pointers', hiding from garbage collector */
-MHeap runtime·mheap;
+MHeap *runtime·mheap;
-extern MStats mstats; // defined in extern.go
+int32 runtime·checking;
-extern volatile int32 runtime·MemProfileRate;
+extern MStats mstats; // defined in zruntime_def_$GOOS_$GOARCH.go
+
+extern volatile intgo runtime·MemProfileRate;
// Allocate an object of at least size bytes.
// Small objects are allocated from the per-thread cache's free lists.
@@ -27,7 +28,8 @@ extern volatile int32 runtime·MemProfileRate;
void*
runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed)
{
- int32 sizeclass, rate;
+ int32 sizeclass;
+ intgo rate;
MCache *c;
uintptr npages;
MSpan *s;
@@ -41,6 +43,9 @@ runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed)
if(size == 0)
size = 1;
+ if(DebugTypeAtBlockEnd)
+ size += sizeof(uintptr);
+
c = m->mcache;
c->local_nmalloc++;
if(size <= MaxSmallSize) {
@@ -60,7 +65,7 @@ runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed)
npages = size >> PageShift;
if((size & PageMask) != 0)
npages++;
- s = runtime·MHeap_Alloc(&runtime·mheap, npages, 0, 1);
+ s = runtime·MHeap_Alloc(runtime·mheap, npages, 0, 1, zeroed);
if(s == nil)
runtime·throw("out of memory");
size = npages<<PageShift;
@@ -71,9 +76,20 @@ runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed)
// setup for mark sweep
runtime·markspan(v, 0, 0, true);
}
+
+ if (sizeof(void*) == 4 && c->local_total_alloc >= (1<<30)) {
+ // purge cache stats to prevent overflow
+ runtime·lock(runtime·mheap);
+ runtime·purgecachedstats(c);
+ runtime·unlock(runtime·mheap);
+ }
+
if(!(flag & FlagNoGC))
runtime·markallocated(v, size, (flag&FlagNoPointers) != 0);
+ if(DebugTypeAtBlockEnd)
+ *(uintptr*)((uintptr)v+size-sizeof(uintptr)) = 0;
+
m->mallocing = 0;
if(!(flag & FlagNoProfiling) && (rate = runtime·MemProfileRate) > 0) {
@@ -95,6 +111,11 @@ runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed)
if(dogc && mstats.heap_alloc >= mstats.next_gc)
runtime·gc(0);
+
+ if(raceenabled) {
+ runtime·racemalloc(v, size, m->racepc);
+ m->racepc = nil;
+ }
return v;
}
@@ -130,6 +151,9 @@ runtime·free(void *v)
}
prof = runtime·blockspecial(v);
+ if(raceenabled)
+ runtime·racefree(v);
+
// Find size class for v.
sizeclass = s->sizeclass;
c = m->mcache;
@@ -141,7 +165,7 @@ runtime·free(void *v)
// they might coalesce v into other spans and change the bitmap further.
runtime·markfreed(v, size);
runtime·unmarkspan(v, 1<<PageShift);
- runtime·MHeap_Free(&runtime·mheap, s, 1);
+ runtime·MHeap_Free(runtime·mheap, s, 1);
} else {
// Small object.
size = runtime·class_to_size[sizeclass];
@@ -169,7 +193,14 @@ runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
MSpan *s;
m->mcache->local_nlookup++;
- s = runtime·MHeap_LookupMaybe(&runtime·mheap, v);
+ if (sizeof(void*) == 4 && m->mcache->local_nlookup >= (1<<30)) {
+ // purge cache stats to prevent overflow
+ runtime·lock(runtime·mheap);
+ runtime·purgecachedstats(m->mcache);
+ runtime·unlock(runtime·mheap);
+ }
+
+ s = runtime·MHeap_LookupMaybe(runtime·mheap, v);
if(sp)
*sp = s;
if(s == nil) {
@@ -196,7 +227,7 @@ runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
return 0;
}
- n = runtime·class_to_size[s->sizeclass];
+ n = s->elemsize;
if(base) {
i = ((byte*)v - p)/n;
*base = p + i*n;
@@ -210,14 +241,15 @@ runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
MCache*
runtime·allocmcache(void)
{
- int32 rate;
+ intgo rate;
MCache *c;
- runtime·lock(&runtime·mheap);
- c = runtime·FixAlloc_Alloc(&runtime·mheap.cachealloc);
- mstats.mcache_inuse = runtime·mheap.cachealloc.inuse;
- mstats.mcache_sys = runtime·mheap.cachealloc.sys;
- runtime·unlock(&runtime·mheap);
+ runtime·lock(runtime·mheap);
+ c = runtime·FixAlloc_Alloc(&runtime·mheap->cachealloc);
+ mstats.mcache_inuse = runtime·mheap->cachealloc.inuse;
+ mstats.mcache_sys = runtime·mheap->cachealloc.sys;
+ runtime·unlock(runtime·mheap);
+ runtime·memclr((byte*)c, sizeof(*c));
// Set first allocation sample size.
rate = runtime·MemProfileRate;
@@ -230,12 +262,19 @@ runtime·allocmcache(void)
}
void
-runtime·purgecachedstats(M* m)
+runtime·freemcache(MCache *c)
{
- MCache *c;
+ runtime·MCache_ReleaseAll(c);
+ runtime·lock(runtime·mheap);
+ runtime·purgecachedstats(c);
+ runtime·FixAlloc_Free(&runtime·mheap->cachealloc, c);
+ runtime·unlock(runtime·mheap);
+}
+void
+runtime·purgecachedstats(MCache *c)
+{
// Protected by either heap or GC lock.
- c = m->mcache;
mstats.heap_alloc += c->local_cachealloc;
c->local_cachealloc = 0;
mstats.heap_objects += c->local_objects;
@@ -274,6 +313,9 @@ runtime·mallocinit(void)
USED(arena_size);
USED(bitmap_size);
+ if((runtime·mheap = runtime·SysAlloc(sizeof(*runtime·mheap))) == nil)
+ runtime·throw("runtime: cannot allocate heap metadata");
+
runtime·InitSizes();
limit = runtime·memlimit();
@@ -283,32 +325,30 @@ runtime·mallocinit(void)
// enough to hold 4 bits per allocated word.
if(sizeof(void*) == 8 && (limit == 0 || limit > (1<<30))) {
// On a 64-bit machine, allocate from a single contiguous reservation.
- // 16 GB should be big enough for now.
+ // 128 GB (MaxMem) should be big enough for now.
//
// The code will work with the reservation at any address, but ask
- // SysReserve to use 0x000000f800000000 if possible.
- // Allocating a 16 GB region takes away 36 bits, and the amd64
+ // SysReserve to use 0x000000c000000000 if possible.
+ // Allocating a 128 GB region takes away 37 bits, and the amd64
// doesn't let us choose the top 17 bits, so that leaves the 11 bits
- // in the middle of 0x00f8 for us to choose. Choosing 0x00f8 means
- // that the valid memory addresses will begin 0x00f8, 0x00f9, 0x00fa, 0x00fb.
- // None of the bytes f8 f9 fa fb can appear in valid UTF-8, and
- // they are otherwise as far from ff (likely a common byte) as possible.
- // Choosing 0x00 for the leading 6 bits was more arbitrary, but it
- // is not a common ASCII code point either. Using 0x11f8 instead
+ // in the middle of 0x00c0 for us to choose. Choosing 0x00c0 means
+ // that the valid memory addresses will begin 0x00c0, 0x00c1, ..., 0x0x00df.
+ // In little-endian, that's c0 00, c1 00, ..., df 00. None of those are valid
+ // UTF-8 sequences, and they are otherwise as far away from
+ // ff (likely a common byte) as possible. An earlier attempt to use 0x11f8
// caused out of memory errors on OS X during thread allocations.
// These choices are both for debuggability and to reduce the
// odds of the conservative garbage collector not collecting memory
// because some non-pointer block of memory had a bit pattern
// that matched a memory address.
//
- // Actually we reserve 17 GB (because the bitmap ends up being 1 GB)
- // but it hardly matters: fc is not valid UTF-8 either, and we have to
- // allocate 15 GB before we get that far.
+ // Actually we reserve 136 GB (because the bitmap ends up being 8 GB)
+ // but it hardly matters: e0 00 is not valid UTF-8 either.
//
// If this fails we fall back to the 32 bit memory mechanism
- arena_size = 16LL<<30;
+ arena_size = MaxMem;
bitmap_size = arena_size / (sizeof(void*)*8/4);
- p = runtime·SysReserve((void*)(0x00f8ULL<<32), bitmap_size + arena_size);
+ p = runtime·SysReserve((void*)(0x00c0ULL<<32), bitmap_size + arena_size);
}
if (p == nil) {
// On a 32-bit machine, we can't typically get away
@@ -354,13 +394,13 @@ runtime·mallocinit(void)
if((uintptr)p & (((uintptr)1<<PageShift)-1))
runtime·throw("runtime: SysReserve returned unaligned address");
- runtime·mheap.bitmap = p;
- runtime·mheap.arena_start = p + bitmap_size;
- runtime·mheap.arena_used = runtime·mheap.arena_start;
- runtime·mheap.arena_end = runtime·mheap.arena_start + arena_size;
+ runtime·mheap->bitmap = p;
+ runtime·mheap->arena_start = p + bitmap_size;
+ runtime·mheap->arena_used = runtime·mheap->arena_start;
+ runtime·mheap->arena_end = runtime·mheap->arena_start + arena_size;
// Initialize the rest of the allocator.
- runtime·MHeap_Init(&runtime·mheap, runtime·SysAlloc);
+ runtime·MHeap_Init(runtime·mheap, runtime·SysAlloc);
m->mcache = runtime·allocmcache();
// See if it works.
@@ -394,6 +434,8 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n)
runtime·SysMap(p, n);
h->arena_used += n;
runtime·MHeap_MapBits(h);
+ if(raceenabled)
+ runtime·racemapshadow(p, n);
return p;
}
@@ -420,11 +462,231 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n)
if(h->arena_used > h->arena_end)
h->arena_end = h->arena_used;
runtime·MHeap_MapBits(h);
+ if(raceenabled)
+ runtime·racemapshadow(p, n);
}
return p;
}
+static Lock settype_lock;
+
+void
+runtime·settype_flush(M *mp, bool sysalloc)
+{
+ uintptr *buf, *endbuf;
+ uintptr size, ofs, j, t;
+ uintptr ntypes, nbytes2, nbytes3;
+ uintptr *data2;
+ byte *data3;
+ bool sysalloc3;
+ void *v;
+ uintptr typ, p;
+ MSpan *s;
+
+ buf = mp->settype_buf;
+ endbuf = buf + mp->settype_bufsize;
+
+ runtime·lock(&settype_lock);
+ while(buf < endbuf) {
+ v = (void*)*buf;
+ *buf = 0;
+ buf++;
+ typ = *buf;
+ buf++;
+
+ // (Manually inlined copy of runtime·MHeap_Lookup)
+ p = (uintptr)v>>PageShift;
+ if(sizeof(void*) == 8)
+ p -= (uintptr)runtime·mheap->arena_start >> PageShift;
+ s = runtime·mheap->map[p];
+
+ if(s->sizeclass == 0) {
+ s->types.compression = MTypes_Single;
+ s->types.data = typ;
+ continue;
+ }
+
+ size = s->elemsize;
+ ofs = ((uintptr)v - (s->start<<PageShift)) / size;
+
+ switch(s->types.compression) {
+ case MTypes_Empty:
+ ntypes = (s->npages << PageShift) / size;
+ nbytes3 = 8*sizeof(uintptr) + 1*ntypes;
+
+ if(!sysalloc) {
+ data3 = runtime·mallocgc(nbytes3, FlagNoPointers, 0, 1);
+ } else {
+ data3 = runtime·SysAlloc(nbytes3);
+ if(data3 == nil)
+ runtime·throw("runtime: cannot allocate memory");
+ if(0) runtime·printf("settype(0->3): SysAlloc(%x) --> %p\n", (uint32)nbytes3, data3);
+ }
+
+ s->types.compression = MTypes_Bytes;
+ s->types.sysalloc = sysalloc;
+ s->types.data = (uintptr)data3;
+
+ ((uintptr*)data3)[1] = typ;
+ data3[8*sizeof(uintptr) + ofs] = 1;
+ break;
+
+ case MTypes_Words:
+ ((uintptr*)s->types.data)[ofs] = typ;
+ break;
+
+ case MTypes_Bytes:
+ data3 = (byte*)s->types.data;
+ for(j=1; j<8; j++) {
+ if(((uintptr*)data3)[j] == typ) {
+ break;
+ }
+ if(((uintptr*)data3)[j] == 0) {
+ ((uintptr*)data3)[j] = typ;
+ break;
+ }
+ }
+ if(j < 8) {
+ data3[8*sizeof(uintptr) + ofs] = j;
+ } else {
+ ntypes = (s->npages << PageShift) / size;
+ nbytes2 = ntypes * sizeof(uintptr);
+
+ if(!sysalloc) {
+ data2 = runtime·mallocgc(nbytes2, FlagNoPointers, 0, 1);
+ } else {
+ data2 = runtime·SysAlloc(nbytes2);
+ if(data2 == nil)
+ runtime·throw("runtime: cannot allocate memory");
+ if(0) runtime·printf("settype.(3->2): SysAlloc(%x) --> %p\n", (uint32)nbytes2, data2);
+ }
+
+ sysalloc3 = s->types.sysalloc;
+
+ s->types.compression = MTypes_Words;
+ s->types.sysalloc = sysalloc;
+ s->types.data = (uintptr)data2;
+
+ // Move the contents of data3 to data2. Then deallocate data3.
+ for(j=0; j<ntypes; j++) {
+ t = data3[8*sizeof(uintptr) + j];
+ t = ((uintptr*)data3)[t];
+ data2[j] = t;
+ }
+ if(sysalloc3) {
+ nbytes3 = 8*sizeof(uintptr) + 1*ntypes;
+ if(0) runtime·printf("settype.(3->2): SysFree(%p,%x)\n", data3, (uint32)nbytes3);
+ runtime·SysFree(data3, nbytes3);
+ }
+
+ data2[ofs] = typ;
+ }
+ break;
+ }
+ }
+ runtime·unlock(&settype_lock);
+
+ mp->settype_bufsize = 0;
+}
+
+// It is forbidden to use this function if it is possible that
+// explicit deallocation via calling runtime·free(v) may happen.
+void
+runtime·settype(void *v, uintptr t)
+{
+ M *mp;
+ uintptr *buf;
+ uintptr i;
+ MSpan *s;
+
+ if(t == 0)
+ runtime·throw("settype: zero type");
+
+ mp = m;
+ buf = mp->settype_buf;
+ i = mp->settype_bufsize;
+ buf[i+0] = (uintptr)v;
+ buf[i+1] = t;
+ i += 2;
+ mp->settype_bufsize = i;
+
+ if(i == nelem(mp->settype_buf)) {
+ runtime·settype_flush(mp, false);
+ }
+
+ if(DebugTypeAtBlockEnd) {
+ s = runtime·MHeap_Lookup(runtime·mheap, v);
+ *(uintptr*)((uintptr)v+s->elemsize-sizeof(uintptr)) = t;
+ }
+}
+
+void
+runtime·settype_sysfree(MSpan *s)
+{
+ uintptr ntypes, nbytes;
+
+ if(!s->types.sysalloc)
+ return;
+
+ nbytes = (uintptr)-1;
+
+ switch (s->types.compression) {
+ case MTypes_Words:
+ ntypes = (s->npages << PageShift) / s->elemsize;
+ nbytes = ntypes * sizeof(uintptr);
+ break;
+ case MTypes_Bytes:
+ ntypes = (s->npages << PageShift) / s->elemsize;
+ nbytes = 8*sizeof(uintptr) + 1*ntypes;
+ break;
+ }
+
+ if(nbytes != (uintptr)-1) {
+ if(0) runtime·printf("settype: SysFree(%p,%x)\n", (void*)s->types.data, (uint32)nbytes);
+ runtime·SysFree((void*)s->types.data, nbytes);
+ }
+}
+
+uintptr
+runtime·gettype(void *v)
+{
+ MSpan *s;
+ uintptr t, ofs;
+ byte *data;
+
+ s = runtime·MHeap_LookupMaybe(runtime·mheap, v);
+ if(s != nil) {
+ t = 0;
+ switch(s->types.compression) {
+ case MTypes_Empty:
+ break;
+ case MTypes_Single:
+ t = s->types.data;
+ break;
+ case MTypes_Words:
+ ofs = (uintptr)v - (s->start<<PageShift);
+ t = ((uintptr*)s->types.data)[ofs/s->elemsize];
+ break;
+ case MTypes_Bytes:
+ ofs = (uintptr)v - (s->start<<PageShift);
+ data = (byte*)s->types.data;
+ t = data[8*sizeof(uintptr) + ofs/s->elemsize];
+ t = ((uintptr*)data)[t];
+ break;
+ default:
+ runtime·throw("runtime·gettype: invalid compression kind");
+ }
+ if(0) {
+ runtime·lock(&settype_lock);
+ runtime·printf("%p -> %d,%X\n", v, (int32)s->types.compression, (int64)t);
+ runtime·unlock(&settype_lock);
+ }
+ return t;
+ }
+ return 0;
+}
+
// Runtime stubs.
void*
@@ -433,46 +695,63 @@ runtime·mal(uintptr n)
return runtime·mallocgc(n, 0, 1, 1);
}
-func new(typ *Type) (ret *uint8) {
- uint32 flag = typ->kind&KindNoPointers ? FlagNoPointers : 0;
- ret = runtime·mallocgc(typ->size, flag, 1, 1);
+#pragma textflag 7
+void
+runtime·new(Type *typ, uint8 *ret)
+{
+ uint32 flag;
+
+ if(raceenabled)
+ m->racepc = runtime·getcallerpc(&typ);
+
+ if(typ->size == 0) {
+ // All 0-length allocations use this pointer.
+ // The language does not require the allocations to
+ // have distinct values.
+ ret = (uint8*)&runtime·zerobase;
+ } else {
+ flag = typ->kind&KindNoPointers ? FlagNoPointers : 0;
+ ret = runtime·mallocgc(typ->size, flag, 1, 1);
+
+ if(UseSpanType && !flag) {
+ if(false) {
+ runtime·printf("new %S: %p\n", *typ->string, ret);
+ }
+ runtime·settype(ret, (uintptr)typ | TypeInfo_SingleObject);
+ }
+ }
+
FLUSH(&ret);
}
+// same as runtime·new, but callable from C
void*
-runtime·stackalloc(uint32 n)
+runtime·cnew(Type *typ)
{
- // Stackalloc must be called on scheduler stack, so that we
- // never try to grow the stack during the code that stackalloc runs.
- // Doing so would cause a deadlock (issue 1547).
- if(g != m->g0)
- runtime·throw("stackalloc not on scheduler stack");
-
- // Stack allocator uses malloc/free most of the time,
- // but if we're in the middle of malloc and need stack,
- // we have to do something else to avoid deadlock.
- // In that case, we fall back on a fixed-size free-list
- // allocator, assuming that inside malloc all the stack
- // frames are small, so that all the stack allocations
- // will be a single size, the minimum (right now, 5k).
- if(m->mallocing || m->gcing || n == FixedStack) {
- if(n != FixedStack) {
- runtime·printf("stackalloc: in malloc, size=%d want %d", FixedStack, n);
- runtime·throw("stackalloc");
+ uint32 flag;
+ void *ret;
+
+ if(raceenabled)
+ m->racepc = runtime·getcallerpc(&typ);
+
+ if(typ->size == 0) {
+ // All 0-length allocations use this pointer.
+ // The language does not require the allocations to
+ // have distinct values.
+ ret = (uint8*)&runtime·zerobase;
+ } else {
+ flag = typ->kind&KindNoPointers ? FlagNoPointers : 0;
+ ret = runtime·mallocgc(typ->size, flag, 1, 1);
+
+ if(UseSpanType && !flag) {
+ if(false) {
+ runtime·printf("new %S: %p\n", *typ->string, ret);
+ }
+ runtime·settype(ret, (uintptr)typ | TypeInfo_SingleObject);
}
- return runtime·FixAlloc_Alloc(m->stackalloc);
}
- return runtime·mallocgc(n, FlagNoProfiling|FlagNoGC, 0, 0);
-}
-void
-runtime·stackfree(void *v, uintptr n)
-{
- if(m->mallocing || m->gcing || n == FixedStack) {
- runtime·FixAlloc_Free(m->stackalloc, v);
- return;
- }
- runtime·free(v);
+ return ret;
}
func GC() {
@@ -483,7 +762,8 @@ func SetFinalizer(obj Eface, finalizer Eface) {
byte *base;
uintptr size;
FuncType *ft;
- int32 i, nret;
+ int32 i;
+ uintptr nret;
Type *t;
if(obj.type == nil) {
diff --git a/src/pkg/runtime/malloc.h b/src/pkg/runtime/malloc.h
index d846f6810..38122bf8a 100644
--- a/src/pkg/runtime/malloc.h
+++ b/src/pkg/runtime/malloc.h
@@ -85,6 +85,8 @@ typedef struct MHeap MHeap;
typedef struct MSpan MSpan;
typedef struct MStats MStats;
typedef struct MLink MLink;
+typedef struct MTypes MTypes;
+typedef struct GCStats GCStats;
enum
{
@@ -113,21 +115,30 @@ enum
HeapAllocChunk = 1<<20, // Chunk size for heap growth
// Number of bits in page to span calculations (4k pages).
- // On 64-bit, we limit the arena to 16G, so 22 bits suffices.
- // On 32-bit, we don't bother limiting anything: 20 bits for 4G.
+ // On 64-bit, we limit the arena to 128GB, or 37 bits.
+ // On 32-bit, we don't bother limiting anything, so we use the full 32-bit address.
#ifdef _64BIT
- MHeapMap_Bits = 22,
+ MHeapMap_Bits = 37 - PageShift,
#else
- MHeapMap_Bits = 20,
+ MHeapMap_Bits = 32 - PageShift,
#endif
// Max number of threads to run garbage collection.
// 2, 3, and 4 are all plausible maximums depending
// on the hardware details of the machine. The garbage
- // collector scales well to 4 cpus.
- MaxGcproc = 4,
+ // collector scales well to 8 cpus.
+ MaxGcproc = 8,
};
+// Maximum memory allocation size, a hint for callers.
+// This must be a #define instead of an enum because it
+// is so large.
+#ifdef _64BIT
+#define MaxMem (1ULL<<(MHeapMap_Bits+PageShift)) /* 128 GB */
+#else
+#define MaxMem ((uintptr)-1)
+#endif
+
// A generic linked list of blocks. (Typically the block is bigger than sizeof(MLink).)
struct MLink
{
@@ -188,7 +199,7 @@ void runtime·FixAlloc_Free(FixAlloc *f, void *p);
// Statistics.
-// Shared with Go: if you edit this structure, also edit extern.go.
+// Shared with Go: if you edit this structure, also edit type MemStats in mem.go.
struct MStats
{
// General statistics.
@@ -219,7 +230,7 @@ struct MStats
uint64 buckhash_sys; // profiling bucket hash table
// Statistics about garbage collector.
- // Protected by stopping the world during GC.
+ // Protected by mheap or stopping the world during GC.
uint64 next_gc; // next GC (in heap_alloc time)
uint64 last_gc; // last GC (in absolute time)
uint64 pause_total_ns;
@@ -239,7 +250,6 @@ struct MStats
#define mstats runtime·memStats /* name shared with Go */
extern MStats mstats;
-
// Size classes. Computed and initialized by InitSizes.
//
// SizeToClass(0 <= n <= MaxSmallSize) returns the size class,
@@ -273,19 +283,19 @@ struct MCacheList
struct MCache
{
MCacheList list[NumSizeClasses];
- uint64 size;
- int64 local_cachealloc; // bytes allocated (or freed) from cache since last lock of heap
- int64 local_objects; // objects allocated (or freed) from cache since last lock of heap
- int64 local_alloc; // bytes allocated (or freed) since last lock of heap
- int64 local_total_alloc; // bytes allocated (even if freed) since last lock of heap
- int64 local_nmalloc; // number of mallocs since last lock of heap
- int64 local_nfree; // number of frees since last lock of heap
- int64 local_nlookup; // number of pointer lookups since last lock of heap
+ uintptr size;
+ intptr local_cachealloc; // bytes allocated (or freed) from cache since last lock of heap
+ intptr local_objects; // objects allocated (or freed) from cache since last lock of heap
+ intptr local_alloc; // bytes allocated (or freed) since last lock of heap
+ uintptr local_total_alloc; // bytes allocated (even if freed) since last lock of heap
+ uintptr local_nmalloc; // number of mallocs since last lock of heap
+ uintptr local_nfree; // number of frees since last lock of heap
+ uintptr local_nlookup; // number of pointer lookups since last lock of heap
int32 next_sample; // trigger heap sample after allocating this many bytes
// Statistics about allocation size classes since last lock of heap
struct {
- int64 nmalloc;
- int64 nfree;
+ uintptr nmalloc;
+ uintptr nfree;
} local_by_size[NumSizeClasses];
};
@@ -294,6 +304,44 @@ void* runtime·MCache_Alloc(MCache *c, int32 sizeclass, uintptr size, int32 zero
void runtime·MCache_Free(MCache *c, void *p, int32 sizeclass, uintptr size);
void runtime·MCache_ReleaseAll(MCache *c);
+// MTypes describes the types of blocks allocated within a span.
+// The compression field describes the layout of the data.
+//
+// MTypes_Empty:
+// All blocks are free, or no type information is available for
+// allocated blocks.
+// The data field has no meaning.
+// MTypes_Single:
+// The span contains just one block.
+// The data field holds the type information.
+// The sysalloc field has no meaning.
+// MTypes_Words:
+// The span contains multiple blocks.
+// The data field points to an array of type [NumBlocks]uintptr,
+// and each element of the array holds the type of the corresponding
+// block.
+// MTypes_Bytes:
+// The span contains at most seven different types of blocks.
+// The data field points to the following structure:
+// struct {
+// type [8]uintptr // type[0] is always 0
+// index [NumBlocks]byte
+// }
+// The type of the i-th block is: data.type[data.index[i]]
+enum
+{
+ MTypes_Empty = 0,
+ MTypes_Single = 1,
+ MTypes_Words = 2,
+ MTypes_Bytes = 3,
+};
+struct MTypes
+{
+ byte compression; // one of MTypes_*
+ bool sysalloc; // whether (void*)data is from runtime·SysAlloc
+ uintptr data;
+};
+
// An MSpan is a run of pages.
enum
{
@@ -306,16 +354,17 @@ struct MSpan
{
MSpan *next; // in a span linked list
MSpan *prev; // in a span linked list
- MSpan *allnext; // in the list of all spans
PageID start; // starting page number
uintptr npages; // number of pages in span
MLink *freelist; // list of free objects
uint32 ref; // number of allocated objects in this span
- uint32 sizeclass; // size class
+ int32 sizeclass; // size class
+ uintptr elemsize; // computed from sizeclass or from npages
uint32 state; // MSpanInUse etc
int64 unusedsince; // First time spotted by GC in MSpanFree state
uintptr npreleased; // number of pages released to the OS
byte *limit; // end of data in span
+ MTypes types; // types of allocated objects in this span
};
void runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages);
@@ -342,6 +391,7 @@ struct MCentral
void runtime·MCentral_Init(MCentral *c, int32 sizeclass);
int32 runtime·MCentral_AllocList(MCentral *c, int32 n, MLink **first);
void runtime·MCentral_FreeList(MCentral *c, int32 n, MLink *first);
+void runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end);
// Main malloc heap.
// The heap itself is the "free[]" and "large" arrays,
@@ -351,7 +401,9 @@ struct MHeap
Lock;
MSpan free[MaxMHeapList]; // free lists of given length
MSpan large; // free lists length >= MaxMHeapList
- MSpan *allspans;
+ MSpan **allspans;
+ uint32 nspan;
+ uint32 nspancap;
// span lookup
MSpan *map[1<<MHeapMap_Bits];
@@ -375,10 +427,10 @@ struct MHeap
FixAlloc spanalloc; // allocator for Span*
FixAlloc cachealloc; // allocator for MCache*
};
-extern MHeap runtime·mheap;
+extern MHeap *runtime·mheap;
void runtime·MHeap_Init(MHeap *h, void *(*allocator)(uintptr));
-MSpan* runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct);
+MSpan* runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct, int32 zeroed);
void runtime·MHeap_Free(MHeap *h, MSpan *s, int32 acct);
MSpan* runtime·MHeap_Lookup(MHeap *h, void *v);
MSpan* runtime·MHeap_LookupMaybe(MHeap *h, void *v);
@@ -394,12 +446,18 @@ void runtime·markallocated(void *v, uintptr n, bool noptr);
void runtime·checkallocated(void *v, uintptr n);
void runtime·markfreed(void *v, uintptr n);
void runtime·checkfreed(void *v, uintptr n);
-int32 runtime·checking;
+extern int32 runtime·checking;
void runtime·markspan(void *v, uintptr size, uintptr n, bool leftover);
void runtime·unmarkspan(void *v, uintptr size);
bool runtime·blockspecial(void*);
void runtime·setblockspecial(void*, bool);
-void runtime·purgecachedstats(M*);
+void runtime·purgecachedstats(MCache*);
+void* runtime·cnew(Type*);
+
+void runtime·settype(void*, uintptr);
+void runtime·settype_flush(M*, bool);
+void runtime·settype_sysfree(MSpan*);
+uintptr runtime·gettype(void*);
enum
{
@@ -412,8 +470,26 @@ enum
void runtime·MProf_Malloc(void*, uintptr);
void runtime·MProf_Free(void*, uintptr);
void runtime·MProf_GC(void);
-int32 runtime·helpgc(bool*);
+int32 runtime·gcprocs(void);
+void runtime·helpgc(int32 nproc);
void runtime·gchelper(void);
-bool runtime·getfinalizer(void *p, bool del, void (**fn)(void*), int32 *nret);
+bool runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret);
void runtime·walkfintab(void (*fn)(void*));
+
+enum
+{
+ TypeInfo_SingleObject = 0,
+ TypeInfo_Array = 1,
+ TypeInfo_Map = 2,
+ TypeInfo_Chan = 3,
+
+ // Enables type information at the end of blocks allocated from heap
+ DebugTypeAtBlockEnd = 0,
+};
+
+// defined in mgc0.go
+void runtime·gc_m_ptr(Eface*);
+void runtime·gc_itab_ptr(Eface*);
+
+void runtime·memorydump(void);
diff --git a/src/pkg/runtime/mallocrep1.go b/src/pkg/runtime/mallocrep1.go
index 41c104c0b..bc33e3a6b 100644
--- a/src/pkg/runtime/mallocrep1.go
+++ b/src/pkg/runtime/mallocrep1.go
@@ -39,6 +39,7 @@ func OkAmount(size, n uintptr) bool {
}
func AllocAndFree(size, count int) {
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
if *chatty {
fmt.Printf("size=%d count=%d ...\n", size, count)
}
diff --git a/src/pkg/runtime/mcache.c b/src/pkg/runtime/mcache.c
index 518e00c12..64803e703 100644
--- a/src/pkg/runtime/mcache.c
+++ b/src/pkg/runtime/mcache.c
@@ -21,7 +21,7 @@ runtime·MCache_Alloc(MCache *c, int32 sizeclass, uintptr size, int32 zeroed)
l = &c->list[sizeclass];
if(l->list == nil) {
// Replenish using central lists.
- n = runtime·MCentral_AllocList(&runtime·mheap.central[sizeclass],
+ n = runtime·MCentral_AllocList(&runtime·mheap->central[sizeclass],
runtime·class_to_transfercount[sizeclass], &first);
if(n == 0)
runtime·throw("out of memory");
@@ -43,11 +43,6 @@ runtime·MCache_Alloc(MCache *c, int32 sizeclass, uintptr size, int32 zeroed)
// block is zeroed iff second word is zero ...
if(size > sizeof(uintptr) && ((uintptr*)v)[1] != 0)
runtime·memclr((byte*)v, size);
- else {
- // ... except for the link pointer
- // that we used above; zero that.
- v->next = nil;
- }
}
c->local_cachealloc += size;
c->local_objects++;
@@ -74,7 +69,7 @@ ReleaseN(MCache *c, MCacheList *l, int32 n, int32 sizeclass)
c->size -= n*runtime·class_to_size[sizeclass];
// Return them to central free list.
- runtime·MCentral_FreeList(&runtime·mheap.central[sizeclass], n, first);
+ runtime·MCentral_FreeList(&runtime·mheap->central[sizeclass], n, first);
}
void
diff --git a/src/pkg/runtime/mcentral.c b/src/pkg/runtime/mcentral.c
index ff0c2d11a..ac8b5aa0d 100644
--- a/src/pkg/runtime/mcentral.c
+++ b/src/pkg/runtime/mcentral.c
@@ -34,12 +34,13 @@ runtime·MCentral_Init(MCentral *c, int32 sizeclass)
// Allocate up to n objects from the central free list.
// Return the number of objects allocated.
// The objects are linked together by their first words.
-// On return, *pstart points at the first object and *pend at the last.
+// On return, *pstart points at the first object.
int32
runtime·MCentral_AllocList(MCentral *c, int32 n, MLink **pfirst)
{
- MLink *first, *last, *v;
- int32 i;
+ MSpan *s;
+ MLink *first, *last;
+ int32 cap, avail, i;
runtime·lock(c);
// Replenish central list if empty.
@@ -50,47 +51,37 @@ runtime·MCentral_AllocList(MCentral *c, int32 n, MLink **pfirst)
return 0;
}
}
+ s = c->nonempty.next;
+ cap = (s->npages << PageShift) / s->elemsize;
+ avail = cap - s->ref;
+ if(avail < n)
+ n = avail;
- // Copy from list, up to n.
// First one is guaranteed to work, because we just grew the list.
- first = MCentral_Alloc(c);
+ first = s->freelist;
last = first;
- for(i=1; i<n && (v = MCentral_Alloc(c)) != nil; i++) {
- last->next = v;
- last = v;
+ for(i=1; i<n; i++) {
+ last = last->next;
}
+ s->freelist = last->next;
last->next = nil;
- c->nfree -= i;
-
- runtime·unlock(c);
- *pfirst = first;
- return i;
-}
+ s->ref += n;
+ c->nfree -= n;
-// Helper: allocate one object from the central free list.
-static void*
-MCentral_Alloc(MCentral *c)
-{
- MSpan *s;
- MLink *v;
-
- if(runtime·MSpanList_IsEmpty(&c->nonempty))
- return nil;
- s = c->nonempty.next;
- s->ref++;
- v = s->freelist;
- s->freelist = v->next;
- if(s->freelist == nil) {
+ if(n == avail) {
+ if(s->freelist != nil || s->ref != cap) {
+ runtime·throw("invalid freelist");
+ }
runtime·MSpanList_Remove(s);
runtime·MSpanList_Insert(&c->empty, s);
}
- return v;
+
+ runtime·unlock(c);
+ *pfirst = first;
+ return n;
}
// Free n objects back into the central free list.
-// Return the number of objects allocated.
-// The objects are linked together by their first words.
-// On return, *pstart points at the first object and *pend at the last.
void
runtime·MCentral_FreeList(MCentral *c, int32 n, MLink *start)
{
@@ -118,7 +109,7 @@ MCentral_Free(MCentral *c, void *v)
int32 size;
// Find span for v.
- s = runtime·MHeap_Lookup(&runtime·mheap, v);
+ s = runtime·MHeap_Lookup(runtime·mheap, v);
if(s == nil || s->ref == 0)
runtime·throw("invalid free");
@@ -143,11 +134,47 @@ MCentral_Free(MCentral *c, void *v)
s->freelist = nil;
c->nfree -= (s->npages << PageShift) / size;
runtime·unlock(c);
- runtime·MHeap_Free(&runtime·mheap, s, 0);
+ runtime·MHeap_Free(runtime·mheap, s, 0);
runtime·lock(c);
}
}
+// Free n objects from a span s back into the central free list c.
+// Called from GC.
+void
+runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end)
+{
+ int32 size;
+
+ runtime·lock(c);
+
+ // Move to nonempty if necessary.
+ if(s->freelist == nil) {
+ runtime·MSpanList_Remove(s);
+ runtime·MSpanList_Insert(&c->nonempty, s);
+ }
+
+ // Add the objects back to s's free list.
+ end->next = s->freelist;
+ s->freelist = start;
+ s->ref -= n;
+ c->nfree += n;
+
+ // If s is completely freed, return it to the heap.
+ if(s->ref == 0) {
+ size = runtime·class_to_size[c->sizeclass];
+ runtime·MSpanList_Remove(s);
+ *(uintptr*)(s->start<<PageShift) = 1; // needs zeroing
+ s->freelist = nil;
+ c->nfree -= (s->npages << PageShift) / size;
+ runtime·unlock(c);
+ runtime·unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift);
+ runtime·MHeap_Free(runtime·mheap, s, 0);
+ } else {
+ runtime·unlock(c);
+ }
+}
+
void
runtime·MGetSizeClassInfo(int32 sizeclass, uintptr *sizep, int32 *npagesp, int32 *nobj)
{
@@ -174,7 +201,7 @@ MCentral_Grow(MCentral *c)
runtime·unlock(c);
runtime·MGetSizeClassInfo(c->sizeclass, &size, &npages, &n);
- s = runtime·MHeap_Alloc(&runtime·mheap, npages, c->sizeclass, 0);
+ s = runtime·MHeap_Alloc(runtime·mheap, npages, c->sizeclass, 0, 1);
if(s == nil) {
// TODO(rsc): Log out of memory
runtime·lock(c);
diff --git a/src/pkg/runtime/mem.go b/src/pkg/runtime/mem.go
index 76680086c..79edc5a60 100644
--- a/src/pkg/runtime/mem.go
+++ b/src/pkg/runtime/mem.go
@@ -6,6 +6,9 @@ package runtime
import "unsafe"
+// Note: the MemStats struct should be kept in sync with
+// struct MStats in malloc.h
+
// A MemStats records statistics about the memory allocator.
type MemStats struct {
// General statistics.
@@ -39,7 +42,7 @@ type MemStats struct {
NextGC uint64 // next run in HeapAlloc time (bytes)
LastGC uint64 // last run in absolute time (ns)
PauseTotalNs uint64
- PauseNs [256]uint64 // most recent GC pause times
+ PauseNs [256]uint64 // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256]
NumGC uint32
EnableGC bool
DebugGC bool
diff --git a/src/pkg/runtime/mem_darwin.c b/src/pkg/runtime/mem_darwin.c
index cde5601cf..04e719394 100644
--- a/src/pkg/runtime/mem_darwin.c
+++ b/src/pkg/runtime/mem_darwin.c
@@ -14,7 +14,7 @@ runtime·SysAlloc(uintptr n)
void *v;
mstats.sys += n;
- v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
+ v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(v < (void*)4096)
return nil;
return v;
@@ -51,7 +51,7 @@ runtime·SysMap(void *v, uintptr n)
void *p;
mstats.sys += n;
- p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
+ p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
if(p == (void*)-ENOMEM)
runtime·throw("runtime: out of memory");
if(p != v)
diff --git a/src/pkg/runtime/mem_freebsd.c b/src/pkg/runtime/mem_freebsd.c
index d1c22583d..f217e9db1 100644
--- a/src/pkg/runtime/mem_freebsd.c
+++ b/src/pkg/runtime/mem_freebsd.c
@@ -14,7 +14,7 @@ runtime·SysAlloc(uintptr n)
void *v;
mstats.sys += n;
- v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
+ v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(v < (void*)4096)
return nil;
return v;
@@ -23,9 +23,7 @@ runtime·SysAlloc(uintptr n)
void
runtime·SysUnused(void *v, uintptr n)
{
- USED(v);
- USED(n);
- // TODO(rsc): call madvise MADV_DONTNEED
+ runtime·madvise(v, n, MADV_FREE);
}
void
@@ -61,7 +59,7 @@ runtime·SysMap(void *v, uintptr n)
// On 64-bit, we don't actually have v reserved, so tread carefully.
if(sizeof(void*) == 8) {
- p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
+ p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(p == (void*)-ENOMEM)
runtime·throw("runtime: out of memory");
if(p != v) {
@@ -71,7 +69,7 @@ runtime·SysMap(void *v, uintptr n)
return;
}
- p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
+ p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
if(p == (void*)-ENOMEM)
runtime·throw("runtime: out of memory");
if(p != v)
diff --git a/src/pkg/runtime/mem_linux.c b/src/pkg/runtime/mem_linux.c
index b3e79cc41..ebcec1e86 100644
--- a/src/pkg/runtime/mem_linux.c
+++ b/src/pkg/runtime/mem_linux.c
@@ -10,6 +10,7 @@
enum
{
+ EAGAIN = 11,
ENOMEM = 12,
_PAGE_SIZE = 4096,
};
@@ -56,13 +57,17 @@ runtime·SysAlloc(uintptr n)
void *p;
mstats.sys += n;
- p = runtime·mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
+ p = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(p < (void*)4096) {
if(p == (void*)EACCES) {
runtime·printf("runtime: mmap: access denied\n");
runtime·printf("if you're running SELinux, enable execmem for this process.\n");
runtime·exit(2);
}
+ if(p == (void*)EAGAIN) {
+ runtime·printf("runtime: mmap: too much locked memory (check 'ulimit -l').\n");
+ runtime·exit(2);
+ }
return nil;
}
return p;
@@ -113,7 +118,7 @@ runtime·SysMap(void *v, uintptr n)
// On 64-bit, we don't actually have v reserved, so tread carefully.
if(sizeof(void*) == 8 && (uintptr)v >= 0xffffffffU) {
- p = mmap_fixed(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
+ p = mmap_fixed(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(p == (void*)ENOMEM)
runtime·throw("runtime: out of memory");
if(p != v) {
@@ -123,7 +128,7 @@ runtime·SysMap(void *v, uintptr n)
return;
}
- p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
+ p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
if(p == (void*)ENOMEM)
runtime·throw("runtime: out of memory");
if(p != v)
diff --git a/src/pkg/runtime/mem_netbsd.c b/src/pkg/runtime/mem_netbsd.c
index 34ff31d90..77ce04c4e 100644
--- a/src/pkg/runtime/mem_netbsd.c
+++ b/src/pkg/runtime/mem_netbsd.c
@@ -19,7 +19,7 @@ runtime·SysAlloc(uintptr n)
void *v;
mstats.sys += n;
- v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
+ v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(v < (void*)4096)
return nil;
return v;
@@ -28,9 +28,7 @@ runtime·SysAlloc(uintptr n)
void
runtime·SysUnused(void *v, uintptr n)
{
- USED(v);
- USED(n);
- // TODO(rsc): call madvise MADV_DONTNEED
+ runtime·madvise(v, n, MADV_FREE);
}
void
@@ -67,7 +65,7 @@ runtime·SysMap(void *v, uintptr n)
// On 64-bit, we don't actually have v reserved, so tread carefully.
if(sizeof(void*) == 8) {
- p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
+ p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(p == (void*)-ENOMEM)
runtime·throw("runtime: out of memory");
if(p != v) {
@@ -77,7 +75,7 @@ runtime·SysMap(void *v, uintptr n)
return;
}
- p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
+ p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
if(p == (void*)-ENOMEM)
runtime·throw("runtime: out of memory");
if(p != v)
diff --git a/src/pkg/runtime/mem_openbsd.c b/src/pkg/runtime/mem_openbsd.c
index 34ff31d90..77ce04c4e 100644
--- a/src/pkg/runtime/mem_openbsd.c
+++ b/src/pkg/runtime/mem_openbsd.c
@@ -19,7 +19,7 @@ runtime·SysAlloc(uintptr n)
void *v;
mstats.sys += n;
- v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
+ v = runtime·mmap(nil, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(v < (void*)4096)
return nil;
return v;
@@ -28,9 +28,7 @@ runtime·SysAlloc(uintptr n)
void
runtime·SysUnused(void *v, uintptr n)
{
- USED(v);
- USED(n);
- // TODO(rsc): call madvise MADV_DONTNEED
+ runtime·madvise(v, n, MADV_FREE);
}
void
@@ -67,7 +65,7 @@ runtime·SysMap(void *v, uintptr n)
// On 64-bit, we don't actually have v reserved, so tread carefully.
if(sizeof(void*) == 8) {
- p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
+ p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
if(p == (void*)-ENOMEM)
runtime·throw("runtime: out of memory");
if(p != v) {
@@ -77,7 +75,7 @@ runtime·SysMap(void *v, uintptr n)
return;
}
- p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
+ p = runtime·mmap(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0);
if(p == (void*)-ENOMEM)
runtime·throw("runtime: out of memory");
if(p != v)
diff --git a/src/pkg/runtime/mem_plan9.c b/src/pkg/runtime/mem_plan9.c
index 15cbc176b..26ca367f1 100644
--- a/src/pkg/runtime/mem_plan9.c
+++ b/src/pkg/runtime/mem_plan9.c
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
+#include "defs_GOOS_GOARCH.h"
#include "arch_GOARCH.h"
#include "malloc.h"
#include "os_GOOS.h"
@@ -13,14 +14,14 @@ static Lock memlock;
enum
{
- Round = 4095
+ Round = PAGESIZE-1
};
void*
runtime·SysAlloc(uintptr nbytes)
{
uintptr bl;
-
+
runtime·lock(&memlock);
mstats.sys += nbytes;
// Plan 9 sbrk from /sys/src/libc/9sys/sbrk.c
diff --git a/src/pkg/runtime/memset_arm.s b/src/pkg/runtime/memclr_arm.s
index 974b8da7a..afc529d90 100644
--- a/src/pkg/runtime/memset_arm.s
+++ b/src/pkg/runtime/memclr_arm.s
@@ -23,35 +23,32 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-TO = 1
-TOE = 2
-N = 3
-TMP = 3 /* N and TMP don't overlap */
-
-// TODO(kaib): memset clobbers R9 and R10 (m and g). This makes the
-// registers unpredictable if (when) memset SIGSEGV's. Fix it by
-// moving the R4-R11 register bank.
-TEXT runtime·memset(SB), $0
- MOVW R0, R(TO)
- MOVW data+4(FP), R(4)
- MOVW n+8(FP), R(N)
+TO = 8
+TOE = 11
+N = 12
+TMP = 12 /* N and TMP don't overlap */
+
+TEXT runtime·memclr(SB),7,$0
+ MOVW ptr+0(FP), R(TO)
+ MOVW n+4(FP), R(N)
+ MOVW $0, R(0)
ADD R(N), R(TO), R(TOE) /* to end pointer */
CMP $4, R(N) /* need at least 4 bytes to copy */
BLT _1tail
- AND $0xFF, R(4) /* it's a byte */
- SLL $8, R(4), R(TMP) /* replicate to a word */
- ORR R(TMP), R(4)
- SLL $16, R(4), R(TMP)
- ORR R(TMP), R(4)
+ AND $0xFF, R(0) /* it's a byte */
+ SLL $8, R(0), R(TMP) /* replicate to a word */
+ ORR R(TMP), R(0)
+ SLL $16, R(0), R(TMP)
+ ORR R(TMP), R(0)
_4align: /* align on 4 */
AND.S $3, R(TO), R(TMP)
BEQ _4aligned
- MOVBU.P R(4), 1(R(TO)) /* implicit write back */
+ MOVBU.P R(0), 1(R(TO)) /* implicit write back */
B _4align
_4aligned:
@@ -59,19 +56,19 @@ _4aligned:
CMP R(TMP), R(TO)
BHS _4tail
- MOVW R4, R5 /* replicate */
- MOVW R4, R6
- MOVW R4, R7
- MOVW R4, R8
- MOVW R4, R9
- MOVW R4, R10
- MOVW R4, R11
+ MOVW R0, R1 /* replicate */
+ MOVW R0, R2
+ MOVW R0, R3
+ MOVW R0, R4
+ MOVW R0, R5
+ MOVW R0, R6
+ MOVW R0, R7
_f32loop:
CMP R(TMP), R(TO)
BHS _4tail
- MOVM.IA.W [R4-R11], (R(TO))
+ MOVM.IA.W [R0-R7], (R(TO))
B _f32loop
_4tail:
@@ -80,14 +77,14 @@ _4loop:
CMP R(TMP), R(TO)
BHS _1tail
- MOVW.P R(4), 4(R(TO)) /* implicit write back */
+ MOVW.P R(0), 4(R(TO)) /* implicit write back */
B _4loop
_1tail:
CMP R(TO), R(TOE)
BEQ _return
- MOVBU.P R(4), 1(R(TO)) /* implicit write back */
+ MOVBU.P R(0), 1(R(TO)) /* implicit write back */
B _1tail
_return:
diff --git a/src/pkg/runtime/memmove_arm.s b/src/pkg/runtime/memmove_arm.s
index 5c0e57404..c5d7e9d70 100644
--- a/src/pkg/runtime/memmove_arm.s
+++ b/src/pkg/runtime/memmove_arm.s
@@ -23,19 +23,40 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+// TE or TS are spilled to the stack during bulk register moves.
TS = 0
-TE = 1
-FROM = 2
-N = 3
-TMP = 3 /* N and TMP don't overlap */
-TMP1 = 4
-
-// TODO(kaib): This can be done with the existing registers of LR is re-used. Same for memset.
-TEXT runtime·memmove(SB), 7, $8
- // save g and m
- MOVW R9, 4(R13)
- MOVW R10, 8(R13)
-
+TE = 8
+
+// Warning: the linker will use R11 to synthesize certain instructions. Please
+// take care and double check with objdump.
+FROM = 11
+N = 12
+TMP = 12 /* N and TMP don't overlap */
+TMP1 = 5
+
+RSHIFT = 5
+LSHIFT = 6
+OFFSET = 7
+
+BR0 = 0 /* shared with TS */
+BW0 = 1
+BR1 = 1
+BW1 = 2
+BR2 = 2
+BW2 = 3
+BR3 = 3
+BW3 = 4
+
+FW0 = 1
+FR0 = 2
+FW1 = 2
+FR1 = 3
+FW2 = 3
+FR2 = 4
+FW3 = 4
+FR3 = 8 /* shared with TE */
+
+TEXT runtime·memmove(SB), 7, $4
_memmove:
MOVW to+0(FP), R(TS)
MOVW from+4(FP), R(FROM)
@@ -64,15 +85,17 @@ _b4aligned: /* is source now aligned? */
BNE _bunaligned
ADD $31, R(TS), R(TMP) /* do 32-byte chunks if possible */
+ MOVW R(TS), savedts+4(SP)
_b32loop:
CMP R(TMP), R(TE)
BLS _b4tail
- MOVM.DB.W (R(FROM)), [R4-R11]
- MOVM.DB.W [R4-R11], (R(TE))
+ MOVM.DB.W (R(FROM)), [R0-R7]
+ MOVM.DB.W [R0-R7], (R(TE))
B _b32loop
_b4tail: /* do remaining words if possible */
+ MOVW savedts+4(SP), R(TS)
ADD $3, R(TS), R(TMP)
_b4loop:
CMP R(TMP), R(TE)
@@ -107,22 +130,24 @@ _f4aligned: /* is source now aligned? */
BNE _funaligned
SUB $31, R(TE), R(TMP) /* do 32-byte chunks if possible */
+ MOVW R(TE), savedte+4(SP)
_f32loop:
CMP R(TMP), R(TS)
BHS _f4tail
- MOVM.IA.W (R(FROM)), [R4-R11]
- MOVM.IA.W [R4-R11], (R(TS))
+ MOVM.IA.W (R(FROM)), [R1-R8]
+ MOVM.IA.W [R1-R8], (R(TS))
B _f32loop
_f4tail:
+ MOVW savedte+4(SP), R(TE)
SUB $3, R(TE), R(TMP) /* do remaining words if possible */
_f4loop:
CMP R(TMP), R(TS)
BHS _f1tail
MOVW.P 4(R(FROM)), R(TMP1) /* implicit write back */
- MOVW.P R4, 4(R(TS)) /* implicit write back */
+ MOVW.P R(TMP1), 4(R(TS)) /* implicit write back */
B _f4loop
_f1tail:
@@ -134,25 +159,9 @@ _f1tail:
B _f1tail
_return:
- // restore g and m
- MOVW 4(R13), R9
- MOVW 8(R13), R10
MOVW to+0(FP), R0
RET
-RSHIFT = 4
-LSHIFT = 5
-OFFSET = 6
-
-BR0 = 7
-BW0 = 8
-BR1 = 8
-BW1 = 9
-BR2 = 9
-BW2 = 10
-BR3 = 10
-BW3 = 11
-
_bunaligned:
CMP $2, R(TMP) /* is R(TMP) < 2 ? */
@@ -172,7 +181,8 @@ _bunaligned:
CMP R(TMP), R(TE)
BLS _b1tail
- AND $~0x03, R(FROM) /* align source */
+ BIC $3, R(FROM) /* align source */
+ MOVW R(TS), savedts+4(SP)
MOVW (R(FROM)), R(BR0) /* prime first block register */
_bu16loop:
@@ -196,18 +206,10 @@ _bu16loop:
B _bu16loop
_bu1tail:
+ MOVW savedts+4(SP), R(TS)
ADD R(OFFSET), R(FROM)
B _b1tail
-FW0 = 7
-FR0 = 8
-FW1 = 8
-FR1 = 9
-FW2 = 9
-FR2 = 10
-FW3 = 10
-FR3 = 11
-
_funaligned:
CMP $2, R(TMP)
@@ -227,7 +229,8 @@ _funaligned:
CMP R(TMP), R(TS)
BHS _f1tail
- AND $~0x03, R(FROM) /* align source */
+ BIC $3, R(FROM) /* align source */
+ MOVW R(TE), savedte+4(SP)
MOVW.P 4(R(FROM)), R(FR3) /* prime last block register, implicit write back */
_fu16loop:
@@ -235,7 +238,7 @@ _fu16loop:
BHS _fu1tail
MOVW R(FR3)>>R(RSHIFT), R(FW0)
- MOVM.IA.W (R(FROM)), [R(FR0)-R(FR3)]
+ MOVM.IA.W (R(FROM)), [R(FR0),R(FR1),R(FR2),R(FR3)]
ORR R(FR0)<<R(LSHIFT), R(FW0)
MOVW R(FR0)>>R(RSHIFT), R(FW1)
@@ -247,9 +250,10 @@ _fu16loop:
MOVW R(FR2)>>R(RSHIFT), R(FW3)
ORR R(FR3)<<R(LSHIFT), R(FW3)
- MOVM.IA.W [R(FW0)-R(FW3)], (R(TS))
+ MOVM.IA.W [R(FW0),R(FW1),R(FW2),R(FW3)], (R(TS))
B _fu16loop
_fu1tail:
+ MOVW savedte+4(SP), R(TE)
SUB R(OFFSET), R(FROM)
B _f1tail
diff --git a/src/pkg/runtime/mfinal.c b/src/pkg/runtime/mfinal.c
index 1fa5ea401..2f5e4277d 100644
--- a/src/pkg/runtime/mfinal.c
+++ b/src/pkg/runtime/mfinal.c
@@ -11,8 +11,8 @@ enum { debug = 0 };
typedef struct Fin Fin;
struct Fin
{
- void (*fn)(void*);
- int32 nret;
+ FuncVal *fn;
+ uintptr nret;
};
// Finalizer hash table. Direct hash, linear scan, at most 3/4 full.
@@ -42,7 +42,7 @@ static struct {
} fintab[TABSZ];
static void
-addfintab(Fintab *t, void *k, void (*fn)(void*), int32 nret)
+addfintab(Fintab *t, void *k, FuncVal *fn, uintptr nret)
{
int32 i, j;
@@ -137,7 +137,7 @@ resizefintab(Fintab *tab)
}
bool
-runtime·addfinalizer(void *p, void (*f)(void*), int32 nret)
+runtime·addfinalizer(void *p, FuncVal *f, uintptr nret)
{
Fintab *tab;
byte *base;
@@ -175,7 +175,7 @@ runtime·addfinalizer(void *p, void (*f)(void*), int32 nret)
// get finalizer; if del, delete finalizer.
// caller is responsible for updating RefHasFinalizer (special) bit.
bool
-runtime·getfinalizer(void *p, bool del, void (**fn)(void*), int32 *nret)
+runtime·getfinalizer(void *p, bool del, FuncVal **fn, uintptr *nret)
{
Fintab *tab;
bool res;
diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c
index e8fb266f4..010f9cd96 100644
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -8,15 +8,28 @@
#include "arch_GOARCH.h"
#include "malloc.h"
#include "stack.h"
+#include "mgc0.h"
+#include "race.h"
+#include "type.h"
+#include "typekind.h"
+#include "hashmap.h"
enum {
Debug = 0,
- PtrSize = sizeof(void*),
DebugMark = 0, // run second pass to check mark
+ CollectStats = 0,
// Four bits per word (see #defines below).
wordsPerBitmapWord = sizeof(void*)*8/4,
bitShift = sizeof(void*)*8/4,
+
+ handoffThreshold = 4,
+ IntermediateBufferCapacity = 64,
+
+ // Bits in type information
+ PRECISE = 1,
+ LOOP = 2,
+ PC_BITS = PRECISE | LOOP,
};
// Bits in per-word bitmap.
@@ -67,25 +80,34 @@ enum {
//
uint32 runtime·worldsema = 1;
-// TODO: Make these per-M.
-static uint64 nhandoff;
-
static int32 gctrace;
+typedef struct Obj Obj;
+struct Obj
+{
+ byte *p; // data pointer
+ uintptr n; // size of data in bytes
+ uintptr ti; // type info
+};
+
+// The size of Workbuf is N*PageSize.
typedef struct Workbuf Workbuf;
struct Workbuf
{
- Workbuf *next;
+#define SIZE (2*PageSize-sizeof(LFNode)-sizeof(uintptr))
+ LFNode node; // must be first
uintptr nobj;
- byte *obj[512-2];
+ Obj obj[SIZE/sizeof(Obj) - 1];
+ uint8 _padding[SIZE%sizeof(Obj) + sizeof(Obj)];
+#undef SIZE
};
typedef struct Finalizer Finalizer;
struct Finalizer
{
- void (*fn)(void*);
+ FuncVal *fn;
void *arg;
- int32 nret;
+ uintptr nret;
};
typedef struct FinBlock FinBlock;
@@ -99,9 +121,13 @@ struct FinBlock
};
extern byte data[];
-extern byte etext[];
+extern byte edata[];
+extern byte bss[];
extern byte ebss[];
+extern byte gcdata[];
+extern byte gcbss[];
+
static G *fing;
static FinBlock *finq; // list of finalizers that are to be executed
static FinBlock *finc; // cache of free blocks
@@ -116,89 +142,244 @@ static void putempty(Workbuf*);
static Workbuf* handoff(Workbuf*);
static struct {
- Lock fmu;
- Workbuf *full;
- Lock emu;
- Workbuf *empty;
+ uint64 full; // lock-free list of full blocks
+ uint64 empty; // lock-free list of empty blocks
+ byte pad0[CacheLineSize]; // prevents false-sharing between full/empty and nproc/nwait
uint32 nproc;
volatile uint32 nwait;
volatile uint32 ndone;
+ volatile uint32 debugmarkdone;
Note alldone;
- Lock markgate;
- Lock sweepgate;
- MSpan *spans;
+ ParFor *markfor;
+ ParFor *sweepfor;
Lock;
byte *chunk;
uintptr nchunk;
+
+ Obj *roots;
+ uint32 nroot;
+ uint32 rootcap;
} work;
-// scanblock scans a block of n bytes starting at pointer b for references
-// to other objects, scanning any it finds recursively until there are no
-// unscanned objects left. Instead of using an explicit recursion, it keeps
-// a work list in the Workbuf* structures and loops in the main function
-// body. Keeping an explicit work list is easier on the stack allocator and
-// more efficient.
+enum {
+ GC_DEFAULT_PTR = GC_NUM_INSTR,
+ GC_MAP_NEXT,
+ GC_CHAN,
+
+ GC_NUM_INSTR2
+};
+
+static struct {
+ struct {
+ uint64 sum;
+ uint64 cnt;
+ } ptr;
+ uint64 nbytes;
+ struct {
+ uint64 sum;
+ uint64 cnt;
+ uint64 notype;
+ uint64 typelookup;
+ } obj;
+ uint64 rescan;
+ uint64 rescanbytes;
+ uint64 instr[GC_NUM_INSTR2];
+ uint64 putempty;
+ uint64 getfull;
+} gcstats;
+
+// markonly marks an object. It returns true if the object
+// has been marked by this function, false otherwise.
+// This function isn't thread-safe and doesn't append the object to any buffer.
+static bool
+markonly(void *obj)
+{
+ byte *p;
+ uintptr *bitp, bits, shift, x, xbits, off;
+ MSpan *s;
+ PageID k;
+
+ // Words outside the arena cannot be pointers.
+ if(obj < runtime·mheap->arena_start || obj >= runtime·mheap->arena_used)
+ return false;
+
+ // obj may be a pointer to a live object.
+ // Try to find the beginning of the object.
+
+ // Round down to word boundary.
+ obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1));
+
+ // Find bits for this word.
+ off = (uintptr*)obj - (uintptr*)runtime·mheap->arena_start;
+ bitp = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
+ shift = off % wordsPerBitmapWord;
+ xbits = *bitp;
+ bits = xbits >> shift;
+
+ // Pointing at the beginning of a block?
+ if((bits & (bitAllocated|bitBlockBoundary)) != 0)
+ goto found;
+
+ // Otherwise consult span table to find beginning.
+ // (Manually inlined copy of MHeap_LookupMaybe.)
+ k = (uintptr)obj>>PageShift;
+ x = k;
+ if(sizeof(void*) == 8)
+ x -= (uintptr)runtime·mheap->arena_start>>PageShift;
+ s = runtime·mheap->map[x];
+ if(s == nil || k < s->start || k - s->start >= s->npages || s->state != MSpanInUse)
+ return false;
+ p = (byte*)((uintptr)s->start<<PageShift);
+ if(s->sizeclass == 0) {
+ obj = p;
+ } else {
+ if((byte*)obj >= (byte*)s->limit)
+ return false;
+ uintptr size = s->elemsize;
+ int32 i = ((byte*)obj - p)/size;
+ obj = p+i*size;
+ }
+
+ // Now that we know the object header, reload bits.
+ off = (uintptr*)obj - (uintptr*)runtime·mheap->arena_start;
+ bitp = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
+ shift = off % wordsPerBitmapWord;
+ xbits = *bitp;
+ bits = xbits >> shift;
+
+found:
+ // Now we have bits, bitp, and shift correct for
+ // obj pointing at the base of the object.
+ // Only care about allocated and not marked.
+ if((bits & (bitAllocated|bitMarked)) != bitAllocated)
+ return false;
+ *bitp |= bitMarked<<shift;
+
+ // The object is now marked
+ return true;
+}
+
+// PtrTarget and BitTarget are structures used by intermediate buffers.
+// The intermediate buffers hold GC data before it
+// is moved/flushed to the work buffer (Workbuf).
+// The size of an intermediate buffer is very small,
+// such as 32 or 64 elements.
+typedef struct PtrTarget PtrTarget;
+struct PtrTarget
+{
+ void *p;
+ uintptr ti;
+};
+
+typedef struct BitTarget BitTarget;
+struct BitTarget
+{
+ void *p;
+ uintptr ti;
+ uintptr *bitp, shift;
+};
+
+typedef struct BufferList BufferList;
+struct BufferList
+{
+ PtrTarget ptrtarget[IntermediateBufferCapacity];
+ BitTarget bittarget[IntermediateBufferCapacity];
+ Obj obj[IntermediateBufferCapacity];
+ BufferList *next;
+};
+static BufferList *bufferList;
+
+static Lock lock;
+static Type *itabtype;
+
+static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj);
+
+// flushptrbuf moves data from the PtrTarget buffer to the work buffer.
+// The PtrTarget buffer contains blocks irrespective of whether the blocks have been marked or scanned,
+// while the work buffer contains blocks which have been marked
+// and are prepared to be scanned by the garbage collector.
+//
+// _wp, _wbuf, _nobj are input/output parameters and are specifying the work buffer.
+// bitbuf holds temporary data generated by this function.
+//
+// A simplified drawing explaining how the todo-list moves from a structure to another:
+//
+// scanblock
+// (find pointers)
+// Obj ------> PtrTarget (pointer targets)
+// ↑ |
+// | | flushptrbuf (1st part,
+// | | find block start)
+// | ↓
+// `--------- BitTarget (pointer targets and the corresponding locations in bitmap)
+// flushptrbuf
+// (2nd part, mark and enqueue)
static void
-scanblock(byte *b, int64 n)
+flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj, BitTarget *bitbuf)
{
- byte *obj, *arena_start, *arena_used, *p;
- void **vp;
- uintptr size, *bitp, bits, shift, i, j, x, xbits, off, nobj, nproc;
+ byte *p, *arena_start, *obj;
+ uintptr size, *bitp, bits, shift, j, x, xbits, off, nobj, ti, n;
MSpan *s;
PageID k;
- void **wp;
+ Obj *wp;
Workbuf *wbuf;
- bool keepworking;
+ PtrTarget *ptrbuf_end;
+ BitTarget *bitbufpos, *bt;
- if((int64)(uintptr)n != n || n < 0) {
- runtime·printf("scanblock %p %D\n", b, n);
- runtime·throw("scanblock");
- }
+ arena_start = runtime·mheap->arena_start;
- // Memory arena parameters.
- arena_start = runtime·mheap.arena_start;
- arena_used = runtime·mheap.arena_used;
- nproc = work.nproc;
+ wp = *_wp;
+ wbuf = *_wbuf;
+ nobj = *_nobj;
- wbuf = nil; // current work buffer
- wp = nil; // storage for next queued pointer (write pointer)
- nobj = 0; // number of queued objects
+ ptrbuf_end = *ptrbufpos;
+ n = ptrbuf_end - ptrbuf;
+ *ptrbufpos = ptrbuf;
- // Scanblock helpers pass b==nil.
- // The main proc needs to return to make more
- // calls to scanblock. But if work.nproc==1 then
- // might as well process blocks as soon as we
- // have them.
- keepworking = b == nil || work.nproc == 1;
+ if(CollectStats) {
+ runtime·xadd64(&gcstats.ptr.sum, n);
+ runtime·xadd64(&gcstats.ptr.cnt, 1);
+ }
- // Align b to a word boundary.
- off = (uintptr)b & (PtrSize-1);
- if(off != 0) {
- b += PtrSize - off;
- n -= PtrSize - off;
+ // If buffer is nearly full, get a new one.
+ if(wbuf == nil || nobj+n >= nelem(wbuf->obj)) {
+ if(wbuf != nil)
+ wbuf->nobj = nobj;
+ wbuf = getempty(wbuf);
+ wp = wbuf->obj;
+ nobj = 0;
+
+ if(n >= nelem(wbuf->obj))
+ runtime·throw("ptrbuf has to be smaller than WorkBuf");
}
- for(;;) {
- // Each iteration scans the block b of length n, queueing pointers in
- // the work buffer.
- if(Debug > 1)
- runtime·printf("scanblock %p %D\n", b, n);
+ // TODO(atom): This block is a branch of an if-then-else statement.
+ // The single-threaded branch may be added in a next CL.
+ {
+ // Multi-threaded version.
- vp = (void**)b;
- n >>= (2+PtrSize/8); /* n /= PtrSize (4 or 8) */
- for(i=0; i<n; i++) {
- obj = (byte*)vp[i];
+ bitbufpos = bitbuf;
- // Words outside the arena cannot be pointers.
- if((byte*)obj < arena_start || (byte*)obj >= arena_used)
- continue;
+ while(ptrbuf < ptrbuf_end) {
+ obj = ptrbuf->p;
+ ti = ptrbuf->ti;
+ ptrbuf++;
+
+ // obj belongs to interval [mheap.arena_start, mheap.arena_used).
+ if(Debug > 1) {
+ if(obj < runtime·mheap->arena_start || obj >= runtime·mheap->arena_used)
+ runtime·throw("object is outside of mheap");
+ }
// obj may be a pointer to a live object.
// Try to find the beginning of the object.
// Round down to word boundary.
- obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1));
+ if(((uintptr)obj & ((uintptr)PtrSize-1)) != 0) {
+ obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1));
+ ti = 0;
+ }
// Find bits for this word.
off = (uintptr*)obj - (uintptr*)arena_start;
@@ -211,6 +392,8 @@ scanblock(byte *b, int64 n)
if((bits & (bitAllocated|bitBlockBoundary)) != 0)
goto found;
+ ti = 0;
+
// Pointing just past the beginning?
// Scan backward a little to find a block boundary.
for(j=shift; j-->0; ) {
@@ -228,16 +411,16 @@ scanblock(byte *b, int64 n)
x = k;
if(sizeof(void*) == 8)
x -= (uintptr)arena_start>>PageShift;
- s = runtime·mheap.map[x];
+ s = runtime·mheap->map[x];
if(s == nil || k < s->start || k - s->start >= s->npages || s->state != MSpanInUse)
continue;
- p = (byte*)((uintptr)s->start<<PageShift);
+ p = (byte*)((uintptr)s->start<<PageShift);
if(s->sizeclass == 0) {
obj = p;
} else {
if((byte*)obj >= (byte*)s->limit)
continue;
- size = runtime·class_to_size[s->sizeclass];
+ size = s->elemsize;
int32 i = ((byte*)obj - p)/size;
obj = p+i*size;
}
@@ -255,80 +438,606 @@ scanblock(byte *b, int64 n)
// Only care about allocated and not marked.
if((bits & (bitAllocated|bitMarked)) != bitAllocated)
continue;
- if(nproc == 1)
- *bitp |= bitMarked<<shift;
- else {
- for(;;) {
- x = *bitp;
- if(x & (bitMarked<<shift))
- goto continue_obj;
- if(runtime·casp((void**)bitp, (void*)x, (void*)(x|(bitMarked<<shift))))
- break;
- }
- }
+
+ *bitbufpos++ = (BitTarget){obj, ti, bitp, shift};
+ }
+
+ runtime·lock(&lock);
+ for(bt=bitbuf; bt<bitbufpos; bt++){
+ xbits = *bt->bitp;
+ bits = xbits >> bt->shift;
+ if((bits & bitMarked) != 0)
+ continue;
+
+ // Mark the block
+ *bt->bitp = xbits | (bitMarked << bt->shift);
// If object has no pointers, don't need to scan further.
if((bits & bitNoPointers) != 0)
continue;
- // If another proc wants a pointer, give it some.
- if(nobj > 4 && work.nwait > 0 && work.full == nil) {
+ obj = bt->p;
+
+ // Ask span about size class.
+ // (Manually inlined copy of MHeap_Lookup.)
+ x = (uintptr)obj >> PageShift;
+ if(sizeof(void*) == 8)
+ x -= (uintptr)arena_start>>PageShift;
+ s = runtime·mheap->map[x];
+
+ PREFETCH(obj);
+
+ *wp = (Obj){obj, s->elemsize, bt->ti};
+ wp++;
+ nobj++;
+ }
+ runtime·unlock(&lock);
+
+ // If another proc wants a pointer, give it some.
+ if(work.nwait > 0 && nobj > handoffThreshold && work.full == 0) {
+ wbuf->nobj = nobj;
+ wbuf = handoff(wbuf);
+ nobj = wbuf->nobj;
+ wp = wbuf->obj + nobj;
+ }
+ }
+
+ *_wp = wp;
+ *_wbuf = wbuf;
+ *_nobj = nobj;
+}
+
+static void
+flushobjbuf(Obj *objbuf, Obj **objbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj)
+{
+ uintptr nobj, off;
+ Obj *wp, obj;
+ Workbuf *wbuf;
+ Obj *objbuf_end;
+
+ wp = *_wp;
+ wbuf = *_wbuf;
+ nobj = *_nobj;
+
+ objbuf_end = *objbufpos;
+ *objbufpos = objbuf;
+
+ while(objbuf < objbuf_end) {
+ obj = *objbuf++;
+
+ // Align obj.b to a word boundary.
+ off = (uintptr)obj.p & (PtrSize-1);
+ if(off != 0) {
+ obj.p += PtrSize - off;
+ obj.n -= PtrSize - off;
+ obj.ti = 0;
+ }
+
+ if(obj.p == nil || obj.n == 0)
+ continue;
+
+ // If buffer is full, get a new one.
+ if(wbuf == nil || nobj >= nelem(wbuf->obj)) {
+ if(wbuf != nil)
wbuf->nobj = nobj;
- wbuf = handoff(wbuf);
- nobj = wbuf->nobj;
- wp = wbuf->obj + nobj;
+ wbuf = getempty(wbuf);
+ wp = wbuf->obj;
+ nobj = 0;
+ }
+
+ *wp = obj;
+ wp++;
+ nobj++;
+ }
+
+ // If another proc wants a pointer, give it some.
+ if(work.nwait > 0 && nobj > handoffThreshold && work.full == 0) {
+ wbuf->nobj = nobj;
+ wbuf = handoff(wbuf);
+ nobj = wbuf->nobj;
+ wp = wbuf->obj + nobj;
+ }
+
+ *_wp = wp;
+ *_wbuf = wbuf;
+ *_nobj = nobj;
+}
+
+// Program that scans the whole block and treats every block element as a potential pointer
+static uintptr defaultProg[2] = {PtrSize, GC_DEFAULT_PTR};
+
+// Hashmap iterator program
+static uintptr mapProg[2] = {0, GC_MAP_NEXT};
+
+// Hchan program
+static uintptr chanProg[2] = {0, GC_CHAN};
+
+// Local variables of a program fragment or loop
+typedef struct Frame Frame;
+struct Frame {
+ uintptr count, elemsize, b;
+ uintptr *loop_or_ret;
+};
+
+// scanblock scans a block of n bytes starting at pointer b for references
+// to other objects, scanning any it finds recursively until there are no
+// unscanned objects left. Instead of using an explicit recursion, it keeps
+// a work list in the Workbuf* structures and loops in the main function
+// body. Keeping an explicit work list is easier on the stack allocator and
+// more efficient.
+//
+// wbuf: current work buffer
+// wp: storage for next queued pointer (write pointer)
+// nobj: number of queued objects
+static void
+scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
+{
+ byte *b, *arena_start, *arena_used;
+ uintptr n, i, end_b, elemsize, size, ti, objti, count, type;
+ uintptr *pc, precise_type, nominal_size;
+ uintptr *map_ret, mapkey_size, mapval_size, mapkey_ti, mapval_ti;
+ void *obj;
+ Type *t;
+ Slice *sliceptr;
+ Frame *stack_ptr, stack_top, stack[GC_STACK_CAPACITY+4];
+ BufferList *scanbuffers;
+ PtrTarget *ptrbuf, *ptrbuf_end, *ptrbufpos;
+ BitTarget *bitbuf;
+ Obj *objbuf, *objbuf_end, *objbufpos;
+ Eface *eface;
+ Iface *iface;
+ Hmap *hmap;
+ MapType *maptype;
+ bool didmark, mapkey_kind, mapval_kind;
+ struct hash_gciter map_iter;
+ struct hash_gciter_data d;
+ Hchan *chan;
+ ChanType *chantype;
+
+ if(sizeof(Workbuf) % PageSize != 0)
+ runtime·throw("scanblock: size of Workbuf is suboptimal");
+
+ // Memory arena parameters.
+ arena_start = runtime·mheap->arena_start;
+ arena_used = runtime·mheap->arena_used;
+
+ stack_ptr = stack+nelem(stack)-1;
+
+ precise_type = false;
+ nominal_size = 0;
+
+ // Allocate ptrbuf, bitbuf
+ {
+ runtime·lock(&lock);
+
+ if(bufferList == nil) {
+ bufferList = runtime·SysAlloc(sizeof(*bufferList));
+ if(bufferList == nil)
+ runtime·throw("runtime: cannot allocate memory");
+ bufferList->next = nil;
+ }
+ scanbuffers = bufferList;
+ bufferList = bufferList->next;
+
+ ptrbuf = &scanbuffers->ptrtarget[0];
+ ptrbuf_end = &scanbuffers->ptrtarget[0] + nelem(scanbuffers->ptrtarget);
+ bitbuf = &scanbuffers->bittarget[0];
+ objbuf = &scanbuffers->obj[0];
+ objbuf_end = &scanbuffers->obj[0] + nelem(scanbuffers->obj);
+
+ runtime·unlock(&lock);
+ }
+
+ ptrbufpos = ptrbuf;
+ objbufpos = objbuf;
+
+ // (Silence the compiler)
+ map_ret = nil;
+ mapkey_size = mapval_size = 0;
+ mapkey_kind = mapval_kind = false;
+ mapkey_ti = mapval_ti = 0;
+ chan = nil;
+ chantype = nil;
+
+ goto next_block;
+
+ for(;;) {
+ // Each iteration scans the block b of length n, queueing pointers in
+ // the work buffer.
+ if(Debug > 1) {
+ runtime·printf("scanblock %p %D\n", b, (int64)n);
+ }
+
+ if(CollectStats) {
+ runtime·xadd64(&gcstats.nbytes, n);
+ runtime·xadd64(&gcstats.obj.sum, nobj);
+ runtime·xadd64(&gcstats.obj.cnt, 1);
+ }
+
+ if(ti != 0) {
+ pc = (uintptr*)(ti & ~(uintptr)PC_BITS);
+ precise_type = (ti & PRECISE);
+ stack_top.elemsize = pc[0];
+ if(!precise_type)
+ nominal_size = pc[0];
+ if(ti & LOOP) {
+ stack_top.count = 0; // 0 means an infinite number of iterations
+ stack_top.loop_or_ret = pc+1;
+ } else {
+ stack_top.count = 1;
}
+ } else if(UseSpanType) {
+ if(CollectStats)
+ runtime·xadd64(&gcstats.obj.notype, 1);
+
+ type = runtime·gettype(b);
+ if(type != 0) {
+ if(CollectStats)
+ runtime·xadd64(&gcstats.obj.typelookup, 1);
+
+ t = (Type*)(type & ~(uintptr)(PtrSize-1));
+ switch(type & (PtrSize-1)) {
+ case TypeInfo_SingleObject:
+ pc = (uintptr*)t->gc;
+ precise_type = true; // type information about 'b' is precise
+ stack_top.count = 1;
+ stack_top.elemsize = pc[0];
+ break;
+ case TypeInfo_Array:
+ pc = (uintptr*)t->gc;
+ if(pc[0] == 0)
+ goto next_block;
+ precise_type = true; // type information about 'b' is precise
+ stack_top.count = 0; // 0 means an infinite number of iterations
+ stack_top.elemsize = pc[0];
+ stack_top.loop_or_ret = pc+1;
+ break;
+ case TypeInfo_Map:
+ hmap = (Hmap*)b;
+ maptype = (MapType*)t;
+ if(hash_gciter_init(hmap, &map_iter)) {
+ mapkey_size = maptype->key->size;
+ mapkey_kind = maptype->key->kind;
+ mapkey_ti = (uintptr)maptype->key->gc | PRECISE;
+ mapval_size = maptype->elem->size;
+ mapval_kind = maptype->elem->kind;
+ mapval_ti = (uintptr)maptype->elem->gc | PRECISE;
+
+ map_ret = 0;
+ pc = mapProg;
+ } else {
+ goto next_block;
+ }
+ break;
+ case TypeInfo_Chan:
+ chan = (Hchan*)b;
+ chantype = (ChanType*)t;
+ pc = chanProg;
+ break;
+ default:
+ runtime·throw("scanblock: invalid type");
+ return;
+ }
+ } else {
+ pc = defaultProg;
+ }
+ } else {
+ pc = defaultProg;
+ }
+
+ pc++;
+ stack_top.b = (uintptr)b;
- // If buffer is full, get a new one.
- if(wbuf == nil || nobj >= nelem(wbuf->obj)) {
- if(wbuf != nil)
- wbuf->nobj = nobj;
- wbuf = getempty(wbuf);
- wp = wbuf->obj;
- nobj = 0;
+ end_b = (uintptr)b + n - PtrSize;
+
+ for(;;) {
+ if(CollectStats)
+ runtime·xadd64(&gcstats.instr[pc[0]], 1);
+
+ obj = nil;
+ objti = 0;
+ switch(pc[0]) {
+ case GC_PTR:
+ obj = *(void**)(stack_top.b + pc[1]);
+ objti = pc[2];
+ pc += 3;
+ break;
+
+ case GC_SLICE:
+ sliceptr = (Slice*)(stack_top.b + pc[1]);
+ if(sliceptr->cap != 0) {
+ obj = sliceptr->array;
+ objti = pc[2] | PRECISE | LOOP;
}
- *wp++ = obj;
- nobj++;
- continue_obj:;
+ pc += 3;
+ break;
+
+ case GC_APTR:
+ obj = *(void**)(stack_top.b + pc[1]);
+ pc += 2;
+ break;
+
+ case GC_STRING:
+ obj = *(void**)(stack_top.b + pc[1]);
+ pc += 2;
+ break;
+
+ case GC_EFACE:
+ eface = (Eface*)(stack_top.b + pc[1]);
+ pc += 2;
+ if(eface->type != nil && (eface->data >= arena_start && eface->data < arena_used)) {
+ t = eface->type;
+ if(t->size <= sizeof(void*)) {
+ if((t->kind & KindNoPointers))
+ break;
+
+ obj = eface->data;
+ if((t->kind & ~KindNoPointers) == KindPtr)
+ objti = (uintptr)((PtrType*)t)->elem->gc;
+ } else {
+ obj = eface->data;
+ objti = (uintptr)t->gc;
+ }
+ }
+ break;
+
+ case GC_IFACE:
+ iface = (Iface*)(stack_top.b + pc[1]);
+ pc += 2;
+ if(iface->tab == nil)
+ break;
+
+ // iface->tab
+ if((void*)iface->tab >= arena_start && (void*)iface->tab < arena_used) {
+ *ptrbufpos++ = (PtrTarget){iface->tab, (uintptr)itabtype->gc};
+ if(ptrbufpos == ptrbuf_end)
+ flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf);
+ }
+
+ // iface->data
+ if(iface->data >= arena_start && iface->data < arena_used) {
+ t = iface->tab->type;
+ if(t->size <= sizeof(void*)) {
+ if((t->kind & KindNoPointers))
+ break;
+
+ obj = iface->data;
+ if((t->kind & ~KindNoPointers) == KindPtr)
+ objti = (uintptr)((PtrType*)t)->elem->gc;
+ } else {
+ obj = iface->data;
+ objti = (uintptr)t->gc;
+ }
+ }
+ break;
+
+ case GC_DEFAULT_PTR:
+ while((i = stack_top.b) <= end_b) {
+ stack_top.b += PtrSize;
+ obj = *(byte**)i;
+ if(obj >= arena_start && obj < arena_used) {
+ *ptrbufpos++ = (PtrTarget){obj, 0};
+ if(ptrbufpos == ptrbuf_end)
+ flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf);
+ }
+ }
+ goto next_block;
+
+ case GC_END:
+ if(--stack_top.count != 0) {
+ // Next iteration of a loop if possible.
+ elemsize = stack_top.elemsize;
+ stack_top.b += elemsize;
+ if(stack_top.b + elemsize <= end_b+PtrSize) {
+ pc = stack_top.loop_or_ret;
+ continue;
+ }
+ i = stack_top.b;
+ } else {
+ // Stack pop if possible.
+ if(stack_ptr+1 < stack+nelem(stack)) {
+ pc = stack_top.loop_or_ret;
+ stack_top = *(++stack_ptr);
+ continue;
+ }
+ i = (uintptr)b + nominal_size;
+ }
+ if(!precise_type) {
+ // Quickly scan [b+i,b+n) for possible pointers.
+ for(; i<=end_b; i+=PtrSize) {
+ if(*(byte**)i != nil) {
+ // Found a value that may be a pointer.
+ // Do a rescan of the entire block.
+ enqueue((Obj){b, n, 0}, &wbuf, &wp, &nobj);
+ if(CollectStats) {
+ runtime·xadd64(&gcstats.rescan, 1);
+ runtime·xadd64(&gcstats.rescanbytes, n);
+ }
+ break;
+ }
+ }
+ }
+ goto next_block;
+
+ case GC_ARRAY_START:
+ i = stack_top.b + pc[1];
+ count = pc[2];
+ elemsize = pc[3];
+ pc += 4;
+
+ // Stack push.
+ *stack_ptr-- = stack_top;
+ stack_top = (Frame){count, elemsize, i, pc};
+ continue;
+
+ case GC_ARRAY_NEXT:
+ if(--stack_top.count != 0) {
+ stack_top.b += stack_top.elemsize;
+ pc = stack_top.loop_or_ret;
+ } else {
+ // Stack pop.
+ stack_top = *(++stack_ptr);
+ pc += 1;
+ }
+ continue;
+
+ case GC_CALL:
+ // Stack push.
+ *stack_ptr-- = stack_top;
+ stack_top = (Frame){1, 0, stack_top.b + pc[1], pc+3 /*return address*/};
+ pc = (uintptr*)((byte*)pc + *(int32*)(pc+2)); // target of the CALL instruction
+ continue;
+
+ case GC_MAP_PTR:
+ hmap = *(Hmap**)(stack_top.b + pc[1]);
+ if(hmap == nil) {
+ pc += 3;
+ continue;
+ }
+ runtime·lock(&lock);
+ didmark = markonly(hmap);
+ runtime·unlock(&lock);
+ if(didmark) {
+ maptype = (MapType*)pc[2];
+ if(hash_gciter_init(hmap, &map_iter)) {
+ mapkey_size = maptype->key->size;
+ mapkey_kind = maptype->key->kind;
+ mapkey_ti = (uintptr)maptype->key->gc | PRECISE;
+ mapval_size = maptype->elem->size;
+ mapval_kind = maptype->elem->kind;
+ mapval_ti = (uintptr)maptype->elem->gc | PRECISE;
+
+ // Start mapProg.
+ map_ret = pc+3;
+ pc = mapProg+1;
+ } else {
+ pc += 3;
+ }
+ } else {
+ pc += 3;
+ }
+ continue;
+
+ case GC_MAP_NEXT:
+ // Add all keys and values to buffers, mark all subtables.
+ while(hash_gciter_next(&map_iter, &d)) {
+ // buffers: reserve space for 2 objects.
+ if(ptrbufpos+2 >= ptrbuf_end)
+ flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf);
+ if(objbufpos+2 >= objbuf_end)
+ flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj);
+
+ if(d.st != nil) {
+ runtime·lock(&lock);
+ markonly(d.st);
+ runtime·unlock(&lock);
+ }
+ if(d.key_data != nil) {
+ if(!(mapkey_kind & KindNoPointers) || d.indirectkey) {
+ if(!d.indirectkey)
+ *objbufpos++ = (Obj){d.key_data, mapkey_size, mapkey_ti};
+ else
+ *ptrbufpos++ = (PtrTarget){*(void**)d.key_data, mapkey_ti};
+ }
+ if(!(mapval_kind & KindNoPointers) || d.indirectval) {
+ if(!d.indirectval)
+ *objbufpos++ = (Obj){d.val_data, mapval_size, mapval_ti};
+ else
+ *ptrbufpos++ = (PtrTarget){*(void**)d.val_data, mapval_ti};
+ }
+ }
+ }
+ if(map_ret == 0)
+ goto next_block;
+ pc = map_ret;
+ continue;
+
+ case GC_REGION:
+ obj = (void*)(stack_top.b + pc[1]);
+ size = pc[2];
+ objti = pc[3];
+ pc += 4;
+
+ *objbufpos++ = (Obj){obj, size, objti};
+ if(objbufpos == objbuf_end)
+ flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj);
+ break;
+
+ case GC_CHAN:
+ // There are no heap pointers in struct Hchan,
+ // so we can ignore the leading sizeof(Hchan) bytes.
+ if(!(chantype->elem->kind & KindNoPointers)) {
+ // Channel's buffer follows Hchan immediately in memory.
+ // Size of buffer (cap(c)) is second int in the chan struct.
+ n = ((uintgo*)chan)[1];
+ if(n > 0) {
+ // TODO(atom): split into two chunks so that only the
+ // in-use part of the circular buffer is scanned.
+ // (Channel routines zero the unused part, so the current
+ // code does not lead to leaks, it's just a little inefficient.)
+ *objbufpos++ = (Obj){(byte*)chan+runtime·Hchansize, n*chantype->elem->size,
+ (uintptr)chantype->elem->gc | PRECISE | LOOP};
+ if(objbufpos == objbuf_end)
+ flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj);
+ }
+ }
+ goto next_block;
+
+ default:
+ runtime·throw("scanblock: invalid GC instruction");
+ return;
}
+ if(obj >= arena_start && obj < arena_used) {
+ *ptrbufpos++ = (PtrTarget){obj, objti};
+ if(ptrbufpos == ptrbuf_end)
+ flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf);
+ }
+ }
+
+ next_block:
// Done scanning [b, b+n). Prepare for the next iteration of
- // the loop by setting b and n to the parameters for the next block.
+ // the loop by setting b, n, ti to the parameters for the next block.
- // Fetch b from the work buffer.
if(nobj == 0) {
- if(!keepworking) {
- putempty(wbuf);
- return;
+ flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf);
+ flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj);
+
+ if(nobj == 0) {
+ if(!keepworking) {
+ if(wbuf)
+ putempty(wbuf);
+ goto endscan;
+ }
+ // Emptied our buffer: refill.
+ wbuf = getfull(wbuf);
+ if(wbuf == nil)
+ goto endscan;
+ nobj = wbuf->nobj;
+ wp = wbuf->obj + wbuf->nobj;
}
- // Emptied our buffer: refill.
- wbuf = getfull(wbuf);
- if(wbuf == nil)
- return;
- nobj = wbuf->nobj;
- wp = wbuf->obj + wbuf->nobj;
}
- b = *--wp;
- nobj--;
- // Ask span about size class.
- // (Manually inlined copy of MHeap_Lookup.)
- x = (uintptr)b>>PageShift;
- if(sizeof(void*) == 8)
- x -= (uintptr)arena_start>>PageShift;
- s = runtime·mheap.map[x];
- if(s->sizeclass == 0)
- n = s->npages<<PageShift;
- else
- n = runtime·class_to_size[s->sizeclass];
+ // Fetch b from the work buffer.
+ --wp;
+ b = wp->p;
+ n = wp->n;
+ ti = wp->ti;
+ nobj--;
}
+
+endscan:
+ runtime·lock(&lock);
+ scanbuffers->next = bufferList;
+ bufferList = scanbuffers;
+ runtime·unlock(&lock);
}
// debug_scanblock is the debug copy of scanblock.
// it is simpler, slower, single-threaded, recursive,
// and uses bitSpecial as the mark bit.
static void
-debug_scanblock(byte *b, int64 n)
+debug_scanblock(byte *b, uintptr n)
{
byte *obj, *p;
void **vp;
@@ -338,8 +1047,8 @@ debug_scanblock(byte *b, int64 n)
if(!DebugMark)
runtime·throw("debug_scanblock without DebugMark");
- if((int64)(uintptr)n != n || n < 0) {
- runtime·printf("debug_scanblock %p %D\n", b, n);
+ if((intptr)n < 0) {
+ runtime·printf("debug_scanblock %p %D\n", b, (int64)n);
runtime·throw("debug_scanblock");
}
@@ -356,33 +1065,31 @@ debug_scanblock(byte *b, int64 n)
obj = (byte*)vp[i];
// Words outside the arena cannot be pointers.
- if((byte*)obj < runtime·mheap.arena_start || (byte*)obj >= runtime·mheap.arena_used)
+ if((byte*)obj < runtime·mheap->arena_start || (byte*)obj >= runtime·mheap->arena_used)
continue;
// Round down to word boundary.
obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1));
// Consult span table to find beginning.
- s = runtime·MHeap_LookupMaybe(&runtime·mheap, obj);
+ s = runtime·MHeap_LookupMaybe(runtime·mheap, obj);
if(s == nil)
continue;
-
p = (byte*)((uintptr)s->start<<PageShift);
+ size = s->elemsize;
if(s->sizeclass == 0) {
obj = p;
- size = (uintptr)s->npages<<PageShift;
} else {
if((byte*)obj >= (byte*)s->limit)
continue;
- size = runtime·class_to_size[s->sizeclass];
int32 i = ((byte*)obj - p)/size;
obj = p+i*size;
}
// Now that we know the object header, reload bits.
- off = (uintptr*)obj - (uintptr*)runtime·mheap.arena_start;
- bitp = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)obj - (uintptr*)runtime·mheap->arena_start;
+ bitp = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
xbits = *bitp;
bits = xbits >> shift;
@@ -404,53 +1111,98 @@ debug_scanblock(byte *b, int64 n)
}
}
+// Append obj to the work buffer.
+// _wbuf, _wp, _nobj are input/output parameters and are specifying the work buffer.
+static void
+enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj)
+{
+ uintptr nobj, off;
+ Obj *wp;
+ Workbuf *wbuf;
+
+ if(Debug > 1)
+ runtime·printf("append obj(%p %D %p)\n", obj.p, (int64)obj.n, obj.ti);
+
+ // Align obj.b to a word boundary.
+ off = (uintptr)obj.p & (PtrSize-1);
+ if(off != 0) {
+ obj.p += PtrSize - off;
+ obj.n -= PtrSize - off;
+ obj.ti = 0;
+ }
+
+ if(obj.p == nil || obj.n == 0)
+ return;
+
+ // Load work buffer state
+ wp = *_wp;
+ wbuf = *_wbuf;
+ nobj = *_nobj;
+
+ // If another proc wants a pointer, give it some.
+ if(work.nwait > 0 && nobj > handoffThreshold && work.full == 0) {
+ wbuf->nobj = nobj;
+ wbuf = handoff(wbuf);
+ nobj = wbuf->nobj;
+ wp = wbuf->obj + nobj;
+ }
+
+ // If buffer is full, get a new one.
+ if(wbuf == nil || nobj >= nelem(wbuf->obj)) {
+ if(wbuf != nil)
+ wbuf->nobj = nobj;
+ wbuf = getempty(wbuf);
+ wp = wbuf->obj;
+ nobj = 0;
+ }
+
+ *wp = obj;
+ wp++;
+ nobj++;
+
+ // Save work buffer state
+ *_wp = wp;
+ *_wbuf = wbuf;
+ *_nobj = nobj;
+}
+
+static void
+markroot(ParFor *desc, uint32 i)
+{
+ Obj *wp;
+ Workbuf *wbuf;
+ uintptr nobj;
+
+ USED(&desc);
+ wp = nil;
+ wbuf = nil;
+ nobj = 0;
+ enqueue(work.roots[i], &wbuf, &wp, &nobj);
+ scanblock(wbuf, wp, nobj, false);
+}
+
// Get an empty work buffer off the work.empty list,
// allocating new buffers as needed.
static Workbuf*
getempty(Workbuf *b)
{
- if(work.nproc == 1) {
- // Put b on full list.
- if(b != nil) {
- b->next = work.full;
- work.full = b;
+ if(b != nil)
+ runtime·lfstackpush(&work.full, &b->node);
+ b = (Workbuf*)runtime·lfstackpop(&work.empty);
+ if(b == nil) {
+ // Need to allocate.
+ runtime·lock(&work);
+ if(work.nchunk < sizeof *b) {
+ work.nchunk = 1<<20;
+ work.chunk = runtime·SysAlloc(work.nchunk);
+ if(work.chunk == nil)
+ runtime·throw("runtime: cannot allocate memory");
}
- // Grab from empty list if possible.
- b = work.empty;
- if(b != nil) {
- work.empty = b->next;
- goto haveb;
- }
- } else {
- // Put b on full list.
- if(b != nil) {
- runtime·lock(&work.fmu);
- b->next = work.full;
- work.full = b;
- runtime·unlock(&work.fmu);
- }
- // Grab from empty list if possible.
- runtime·lock(&work.emu);
- b = work.empty;
- if(b != nil)
- work.empty = b->next;
- runtime·unlock(&work.emu);
- if(b != nil)
- goto haveb;
+ b = (Workbuf*)work.chunk;
+ work.chunk += sizeof *b;
+ work.nchunk -= sizeof *b;
+ runtime·unlock(&work);
}
-
- // Need to allocate.
- runtime·lock(&work);
- if(work.nchunk < sizeof *b) {
- work.nchunk = 1<<20;
- work.chunk = runtime·SysAlloc(work.nchunk);
- }
- b = (Workbuf*)work.chunk;
- work.chunk += sizeof *b;
- work.nchunk -= sizeof *b;
- runtime·unlock(&work);
-
-haveb:
b->nobj = 0;
return b;
}
@@ -458,19 +1210,10 @@ haveb:
static void
putempty(Workbuf *b)
{
- if(b == nil)
- return;
+ if(CollectStats)
+ runtime·xadd64(&gcstats.putempty, 1);
- if(work.nproc == 1) {
- b->next = work.empty;
- work.empty = b;
- return;
- }
-
- runtime·lock(&work.emu);
- b->next = work.empty;
- work.empty = b;
- runtime·unlock(&work.emu);
+ runtime·lfstackpush(&work.empty, &b->node);
}
// Get a full work buffer off the work.full list, or return nil.
@@ -478,63 +1221,37 @@ static Workbuf*
getfull(Workbuf *b)
{
int32 i;
- Workbuf *b1;
-
- if(work.nproc == 1) {
- // Put b on empty list.
- if(b != nil) {
- b->next = work.empty;
- work.empty = b;
- }
- // Grab from full list if possible.
- // Since work.nproc==1, no one else is
- // going to give us work.
- b = work.full;
- if(b != nil)
- work.full = b->next;
- return b;
- }
- putempty(b);
+ if(CollectStats)
+ runtime·xadd64(&gcstats.getfull, 1);
- // Grab buffer from full list if possible.
- for(;;) {
- b1 = work.full;
- if(b1 == nil)
- break;
- runtime·lock(&work.fmu);
- if(work.full != nil) {
- b1 = work.full;
- work.full = b1->next;
- runtime·unlock(&work.fmu);
- return b1;
- }
- runtime·unlock(&work.fmu);
- }
+ if(b != nil)
+ runtime·lfstackpush(&work.empty, &b->node);
+ b = (Workbuf*)runtime·lfstackpop(&work.full);
+ if(b != nil || work.nproc == 1)
+ return b;
runtime·xadd(&work.nwait, +1);
for(i=0;; i++) {
- b1 = work.full;
- if(b1 != nil) {
- runtime·lock(&work.fmu);
- if(work.full != nil) {
- runtime·xadd(&work.nwait, -1);
- b1 = work.full;
- work.full = b1->next;
- runtime·unlock(&work.fmu);
- return b1;
- }
- runtime·unlock(&work.fmu);
- continue;
+ if(work.full != 0) {
+ runtime·xadd(&work.nwait, -1);
+ b = (Workbuf*)runtime·lfstackpop(&work.full);
+ if(b != nil)
+ return b;
+ runtime·xadd(&work.nwait, +1);
}
if(work.nwait == work.nproc)
return nil;
- if(i < 10)
+ if(i < 10) {
+ m->gcstats.nprocyield++;
runtime·procyield(20);
- else if(i < 20)
+ } else if(i < 20) {
+ m->gcstats.nosyield++;
runtime·osyield();
- else
+ } else {
+ m->gcstats.nsleep++;
runtime·usleep(100);
+ }
}
}
@@ -550,20 +1267,40 @@ handoff(Workbuf *b)
b->nobj -= n;
b1->nobj = n;
runtime·memmove(b1->obj, b->obj+b->nobj, n*sizeof b1->obj[0]);
- nhandoff += n;
+ m->gcstats.nhandoff++;
+ m->gcstats.nhandoffcnt += n;
// Put b on full list - let first half of b get stolen.
- runtime·lock(&work.fmu);
- b->next = work.full;
- work.full = b;
- runtime·unlock(&work.fmu);
-
+ runtime·lfstackpush(&work.full, &b->node);
return b1;
}
-// Scanstack calls scanblock on each of gp's stack segments.
static void
-scanstack(void (*scanblock)(byte*, int64), G *gp)
+addroot(Obj obj)
+{
+ uint32 cap;
+ Obj *new;
+
+ if(work.nroot >= work.rootcap) {
+ cap = PageSize/sizeof(Obj);
+ if(cap < 2*work.rootcap)
+ cap = 2*work.rootcap;
+ new = (Obj*)runtime·SysAlloc(cap*sizeof(Obj));
+ if(new == nil)
+ runtime·throw("runtime: cannot allocate memory");
+ if(work.roots != nil) {
+ runtime·memmove(new, work.roots, work.rootcap*sizeof(Obj));
+ runtime·SysFree(work.roots, work.rootcap*sizeof(Obj));
+ }
+ work.roots = new;
+ work.rootcap = cap;
+ }
+ work.roots[work.nroot] = obj;
+ work.nroot++;
+}
+
+static void
+addstackroots(G *gp)
{
M *mp;
int32 n;
@@ -571,7 +1308,7 @@ scanstack(void (*scanblock)(byte*, int64), G *gp)
byte *sp, *guard;
stk = (Stktop*)gp->stackbase;
- guard = gp->stackguard;
+ guard = (byte*)gp->stackguard;
if(gp == g) {
// Scanning our own stack: start at &gp.
@@ -582,72 +1319,82 @@ scanstack(void (*scanblock)(byte*, int64), G *gp)
} else {
// Scanning another goroutine's stack.
// The goroutine is usually asleep (the world is stopped).
- sp = gp->sched.sp;
+ sp = (byte*)gp->sched.sp;
// The exception is that if the goroutine is about to enter or might
// have just exited a system call, it may be executing code such
// as schedlock and may have needed to start a new stack segment.
// Use the stack segment and stack pointer at the time of
// the system call instead, since that won't change underfoot.
- if(gp->gcstack != nil) {
+ if(gp->gcstack != (uintptr)nil) {
stk = (Stktop*)gp->gcstack;
- sp = gp->gcsp;
- guard = gp->gcguard;
+ sp = (byte*)gp->gcsp;
+ guard = (byte*)gp->gcguard;
}
}
- if(Debug > 1)
- runtime·printf("scanstack %d %p\n", gp->goid, sp);
n = 0;
while(stk) {
if(sp < guard-StackGuard || (byte*)stk < sp) {
- runtime·printf("scanstack inconsistent: g%d#%d sp=%p not in [%p,%p]\n", gp->goid, n, sp, guard-StackGuard, stk);
+ runtime·printf("scanstack inconsistent: g%D#%d sp=%p not in [%p,%p]\n", gp->goid, n, sp, guard-StackGuard, stk);
runtime·throw("scanstack");
}
- scanblock(sp, (byte*)stk - sp);
- sp = stk->gobuf.sp;
+ addroot((Obj){sp, (byte*)stk - sp, 0});
+ sp = (byte*)stk->gobuf.sp;
guard = stk->stackguard;
stk = (Stktop*)stk->stackbase;
n++;
}
}
-// Markfin calls scanblock on the blocks that have finalizers:
-// the things pointed at cannot be freed until the finalizers have run.
static void
-markfin(void *v)
+addfinroots(void *v)
{
uintptr size;
+ void *base;
size = 0;
- if(!runtime·mlookup(v, &v, &size, nil) || !runtime·blockspecial(v))
+ if(!runtime·mlookup(v, &base, &size, nil) || !runtime·blockspecial(base))
runtime·throw("mark - finalizer inconsistency");
// do not mark the finalizer block itself. just mark the things it points at.
- scanblock(v, size);
+ addroot((Obj){base, size, 0});
}
static void
-debug_markfin(void *v)
-{
- uintptr size;
-
- if(!runtime·mlookup(v, &v, &size, nil))
- runtime·throw("debug_mark - finalizer inconsistency");
- debug_scanblock(v, size);
-}
-
-// Mark
-static void
-mark(void (*scan)(byte*, int64))
+addroots(void)
{
G *gp;
FinBlock *fb;
+ MSpan *s, **allspans;
+ uint32 spanidx;
+
+ work.nroot = 0;
+
+ // data & bss
+ // TODO(atom): load balancing
+ addroot((Obj){data, edata - data, (uintptr)gcdata});
+ addroot((Obj){bss, ebss - bss, (uintptr)gcbss});
+
+ // MSpan.types
+ allspans = runtime·mheap->allspans;
+ for(spanidx=0; spanidx<runtime·mheap->nspan; spanidx++) {
+ s = allspans[spanidx];
+ if(s->state == MSpanInUse) {
+ switch(s->types.compression) {
+ case MTypes_Empty:
+ case MTypes_Single:
+ break;
+ case MTypes_Words:
+ case MTypes_Bytes:
+ // TODO(atom): consider using defaultProg instead of 0
+ addroot((Obj){(byte*)&s->types.data, sizeof(void*), 0});
+ break;
+ }
+ }
+ }
- // mark data+bss.
- scan(data, ebss - data);
-
- // mark stacks
+ // stacks
for(gp=runtime·allg; gp!=nil; gp=gp->alllink) {
switch(gp->status){
default:
@@ -658,37 +1405,30 @@ mark(void (*scan)(byte*, int64))
case Grunning:
if(gp != g)
runtime·throw("mark - world not stopped");
- scanstack(scan, gp);
+ addstackroots(gp);
break;
case Grunnable:
case Gsyscall:
case Gwaiting:
- scanstack(scan, gp);
+ addstackroots(gp);
break;
}
}
- // mark things pointed at by objects with finalizers
- if(scan == debug_scanblock)
- runtime·walkfintab(debug_markfin);
- else
- runtime·walkfintab(markfin);
+ runtime·walkfintab(addfinroots);
for(fb=allfin; fb; fb=fb->alllink)
- scanblock((byte*)fb->fin, fb->cnt*sizeof(fb->fin[0]));
-
- // in multiproc mode, join in the queued work.
- scan(nil, 0);
+ addroot((Obj){(byte*)fb->fin, fb->cnt*sizeof(fb->fin[0]), 0});
}
static bool
handlespecial(byte *p, uintptr size)
{
- void (*fn)(void*);
- int32 nret;
+ FuncVal *fn;
+ uintptr nret;
FinBlock *block;
Finalizer *f;
-
+
if(!runtime·getfinalizer(p, true, &fn, &nret)) {
runtime·setblockspecial(p, false);
runtime·MProf_Free(p, size);
@@ -699,6 +1439,8 @@ handlespecial(byte *p, uintptr size)
if(finq == nil || finq->cnt == finq->cap) {
if(finc == nil) {
finc = runtime·SysAlloc(PageSize);
+ if(finc == nil)
+ runtime·throw("runtime: cannot allocate memory");
finc->cap = (PageSize - sizeof(FinBlock)) / sizeof(Finalizer) + 1;
finc->alllink = allfin;
allfin = finc;
@@ -713,124 +1455,225 @@ handlespecial(byte *p, uintptr size)
f->fn = fn;
f->nret = nret;
f->arg = p;
- runtime·unlock(&finlock);
+ runtime·unlock(&finlock);
return true;
}
// Sweep frees or collects finalizers for blocks not marked in the mark phase.
// It clears the mark bits in preparation for the next GC round.
static void
-sweep(void)
+sweepspan(ParFor *desc, uint32 idx)
{
- MSpan *s;
int32 cl, n, npages;
uintptr size;
byte *p;
MCache *c;
byte *arena_start;
- int64 now;
+ MLink head, *end;
+ int32 nfree;
+ byte *type_data;
+ byte compression;
+ uintptr type_data_inc;
+ MSpan *s;
- arena_start = runtime·mheap.arena_start;
- now = runtime·nanotime();
+ USED(&desc);
+ s = runtime·mheap->allspans[idx];
+ if(s->state != MSpanInUse)
+ return;
+ arena_start = runtime·mheap->arena_start;
+ p = (byte*)(s->start << PageShift);
+ cl = s->sizeclass;
+ size = s->elemsize;
+ if(cl == 0) {
+ n = 1;
+ } else {
+ // Chunk full of small blocks.
+ npages = runtime·class_to_allocnpages[cl];
+ n = (npages << PageShift) / size;
+ }
+ nfree = 0;
+ end = &head;
+ c = m->mcache;
+
+ type_data = (byte*)s->types.data;
+ type_data_inc = sizeof(uintptr);
+ compression = s->types.compression;
+ switch(compression) {
+ case MTypes_Bytes:
+ type_data += 8*sizeof(uintptr);
+ type_data_inc = 1;
+ break;
+ }
- for(;;) {
- s = work.spans;
- if(s == nil)
- break;
- if(!runtime·casp(&work.spans, s, s->allnext))
- continue;
+ // Sweep through n objects of given size starting at p.
+ // This thread owns the span now, so it can manipulate
+ // the block bitmap without atomic operations.
+ for(; n > 0; n--, p += size, type_data+=type_data_inc) {
+ uintptr off, *bitp, shift, bits;
- // Stamp newly unused spans. The scavenger will use that
- // info to potentially give back some pages to the OS.
- if(s->state == MSpanFree && s->unusedsince == 0)
- s->unusedsince = now;
+ off = (uintptr*)p - (uintptr*)arena_start;
+ bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
+ shift = off % wordsPerBitmapWord;
+ bits = *bitp>>shift;
- if(s->state != MSpanInUse)
+ if((bits & bitAllocated) == 0)
continue;
- p = (byte*)(s->start << PageShift);
- cl = s->sizeclass;
+ if((bits & bitMarked) != 0) {
+ if(DebugMark) {
+ if(!(bits & bitSpecial))
+ runtime·printf("found spurious mark on %p\n", p);
+ *bitp &= ~(bitSpecial<<shift);
+ }
+ *bitp &= ~(bitMarked<<shift);
+ continue;
+ }
+
+ // Special means it has a finalizer or is being profiled.
+ // In DebugMark mode, the bit has been coopted so
+ // we have to assume all blocks are special.
+ if(DebugMark || (bits & bitSpecial) != 0) {
+ if(handlespecial(p, size))
+ continue;
+ }
+
+ // Mark freed; restore block boundary bit.
+ *bitp = (*bitp & ~(bitMask<<shift)) | (bitBlockBoundary<<shift);
+
if(cl == 0) {
- size = s->npages<<PageShift;
- n = 1;
+ // Free large span.
+ runtime·unmarkspan(p, 1<<PageShift);
+ *(uintptr*)p = 1; // needs zeroing
+ runtime·MHeap_Free(runtime·mheap, s, 1);
+ c->local_alloc -= size;
+ c->local_nfree++;
} else {
- // Chunk full of small blocks.
- size = runtime·class_to_size[cl];
- npages = runtime·class_to_allocnpages[cl];
- n = (npages << PageShift) / size;
+ // Free small object.
+ switch(compression) {
+ case MTypes_Words:
+ *(uintptr*)type_data = 0;
+ break;
+ case MTypes_Bytes:
+ *(byte*)type_data = 0;
+ break;
+ }
+ if(size > sizeof(uintptr))
+ ((uintptr*)p)[1] = 1; // mark as "needs to be zeroed"
+
+ end->next = (MLink*)p;
+ end = (MLink*)p;
+ nfree++;
}
+ }
- // Sweep through n objects of given size starting at p.
- // This thread owns the span now, so it can manipulate
- // the block bitmap without atomic operations.
- for(; n > 0; n--, p += size) {
- uintptr off, *bitp, shift, bits;
+ if(nfree) {
+ c->local_by_size[cl].nfree += nfree;
+ c->local_alloc -= size * nfree;
+ c->local_nfree += nfree;
+ c->local_cachealloc -= nfree * size;
+ c->local_objects -= nfree;
+ runtime·MCentral_FreeSpan(&runtime·mheap->central[cl], s, nfree, head.next, end);
+ }
+}
- off = (uintptr*)p - (uintptr*)arena_start;
- bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
- shift = off % wordsPerBitmapWord;
- bits = *bitp>>shift;
+static void
+dumpspan(uint32 idx)
+{
+ int32 sizeclass, n, npages, i, column;
+ uintptr size;
+ byte *p;
+ byte *arena_start;
+ MSpan *s;
+ bool allocated, special;
- if((bits & bitAllocated) == 0)
- continue;
+ s = runtime·mheap->allspans[idx];
+ if(s->state != MSpanInUse)
+ return;
+ arena_start = runtime·mheap->arena_start;
+ p = (byte*)(s->start << PageShift);
+ sizeclass = s->sizeclass;
+ size = s->elemsize;
+ if(sizeclass == 0) {
+ n = 1;
+ } else {
+ npages = runtime·class_to_allocnpages[sizeclass];
+ n = (npages << PageShift) / size;
+ }
+
+ runtime·printf("%p .. %p:\n", p, p+n*size);
+ column = 0;
+ for(; n>0; n--, p+=size) {
+ uintptr off, *bitp, shift, bits;
- if((bits & bitMarked) != 0) {
- if(DebugMark) {
- if(!(bits & bitSpecial))
- runtime·printf("found spurious mark on %p\n", p);
- *bitp &= ~(bitSpecial<<shift);
- }
- *bitp &= ~(bitMarked<<shift);
- continue;
- }
+ off = (uintptr*)p - (uintptr*)arena_start;
+ bitp = (uintptr*)arena_start - off/wordsPerBitmapWord - 1;
+ shift = off % wordsPerBitmapWord;
+ bits = *bitp>>shift;
- // Special means it has a finalizer or is being profiled.
- // In DebugMark mode, the bit has been coopted so
- // we have to assume all blocks are special.
- if(DebugMark || (bits & bitSpecial) != 0) {
- if(handlespecial(p, size))
- continue;
+ allocated = ((bits & bitAllocated) != 0);
+ special = ((bits & bitSpecial) != 0);
+
+ for(i=0; i<size; i+=sizeof(void*)) {
+ if(column == 0) {
+ runtime·printf("\t");
+ }
+ if(i == 0) {
+ runtime·printf(allocated ? "(" : "[");
+ runtime·printf(special ? "@" : "");
+ runtime·printf("%p: ", p+i);
+ } else {
+ runtime·printf(" ");
}
- // Mark freed; restore block boundary bit.
- *bitp = (*bitp & ~(bitMask<<shift)) | (bitBlockBoundary<<shift);
+ runtime·printf("%p", *(void**)(p+i));
- c = m->mcache;
- if(s->sizeclass == 0) {
- // Free large span.
- runtime·unmarkspan(p, 1<<PageShift);
- *(uintptr*)p = 1; // needs zeroing
- runtime·MHeap_Free(&runtime·mheap, s, 1);
- } else {
- // Free small object.
- if(size > sizeof(uintptr))
- ((uintptr*)p)[1] = 1; // mark as "needs to be zeroed"
- c->local_by_size[s->sizeclass].nfree++;
- runtime·MCache_Free(c, p, s->sizeclass, size);
+ if(i+sizeof(void*) >= size) {
+ runtime·printf(allocated ? ") " : "] ");
+ }
+
+ column++;
+ if(column == 8) {
+ runtime·printf("\n");
+ column = 0;
}
- c->local_alloc -= size;
- c->local_nfree++;
}
}
+ runtime·printf("\n");
+}
+
+// A debugging function to dump the contents of memory
+void
+runtime·memorydump(void)
+{
+ uint32 spanidx;
+
+ for(spanidx=0; spanidx<runtime·mheap->nspan; spanidx++) {
+ dumpspan(spanidx);
+ }
}
void
runtime·gchelper(void)
{
- // Wait until main proc is ready for mark help.
- runtime·lock(&work.markgate);
- runtime·unlock(&work.markgate);
- scanblock(nil, 0);
+ // parallel mark for over gc roots
+ runtime·parfordo(work.markfor);
+
+ // help other threads scan secondary blocks
+ scanblock(nil, nil, 0, true);
- // Wait until main proc is ready for sweep help.
- runtime·lock(&work.sweepgate);
- runtime·unlock(&work.sweepgate);
- sweep();
+ if(DebugMark) {
+ // wait while the main thread executes mark(debug_scanblock)
+ while(runtime·atomicload(&work.debugmarkdone) == 0)
+ runtime·usleep(10);
+ }
+ runtime·parfordo(work.sweepfor);
if(runtime·xadd(&work.ndone, +1) == work.nproc-1)
runtime·notewakeup(&work.alldone);
}
+#define GcpercentUnknown (-2)
+
// Initialized from $GOGC. GOGC=off means no gc.
//
// Next gc is after we've allocated an extra amount of
@@ -840,33 +1683,36 @@ runtime·gchelper(void)
// proportion to the allocation cost. Adjusting gcpercent
// just changes the linear constant (and also the amount of
// extra memory used).
-static int32 gcpercent = -2;
+static int32 gcpercent = GcpercentUnknown;
static void
-stealcache(void)
+cachestats(GCStats *stats)
{
- M *m;
-
- for(m=runtime·allm; m; m=m->alllink)
- runtime·MCache_ReleaseAll(m->mcache);
-}
-
-static void
-cachestats(void)
-{
- M *m;
+ M *mp;
MCache *c;
+ P *p, **pp;
int32 i;
uint64 stacks_inuse;
- uint64 stacks_sys;
+ uint64 *src, *dst;
+ if(stats)
+ runtime·memclr((byte*)stats, sizeof(*stats));
stacks_inuse = 0;
- stacks_sys = 0;
- for(m=runtime·allm; m; m=m->alllink) {
- runtime·purgecachedstats(m);
- stacks_inuse += m->stackalloc->inuse;
- stacks_sys += m->stackalloc->sys;
- c = m->mcache;
+ for(mp=runtime·allm; mp; mp=mp->alllink) {
+ stacks_inuse += mp->stackinuse*FixedStack;
+ if(stats) {
+ src = (uint64*)&mp->gcstats;
+ dst = (uint64*)stats;
+ for(i=0; i<sizeof(*stats)/sizeof(uint64); i++)
+ dst[i] += src[i];
+ runtime·memclr((byte*)&mp->gcstats, sizeof(mp->gcstats));
+ }
+ }
+ for(pp=runtime·allp; p=*pp; pp++) {
+ c = p->mcache;
+ if(c==nil)
+ continue;
+ runtime·purgecachedstats(c);
for(i=0; i<nelem(c->local_by_size); i++) {
mstats.by_size[i].nmalloc += c->local_by_size[i].nmalloc;
c->local_by_size[i].nmalloc = 0;
@@ -875,16 +1721,42 @@ cachestats(void)
}
}
mstats.stacks_inuse = stacks_inuse;
- mstats.stacks_sys = stacks_sys;
+}
+
+// Structure of arguments passed to function gc().
+// This allows the arguments to be passed via reflect·call.
+struct gc_args
+{
+ int32 force;
+};
+
+static void gc(struct gc_args *args);
+
+static int32
+readgogc(void)
+{
+ byte *p;
+
+ p = runtime·getenv("GOGC");
+ if(p == nil || p[0] == '\0')
+ return 100;
+ if(runtime·strcmp(p, (byte*)"off") == 0)
+ return -1;
+ return runtime·atoi(p);
}
void
runtime·gc(int32 force)
{
- int64 t0, t1, t2, t3;
- uint64 heap0, heap1, obj0, obj1;
byte *p;
- bool extra;
+ struct gc_args a, *ap;
+ FuncVal gcv;
+
+ // The atomic operations are not atomic if the uint64s
+ // are not aligned on uint64 boundaries. This has been
+ // a problem in the past.
+ if((((uintptr)&work.empty) & 7) != 0)
+ runtime·throw("runtime: gc work buffer is misaligned");
// The gc is turned off (via enablegc) until
// the bootstrap has completed.
@@ -897,14 +1769,8 @@ runtime·gc(int32 force)
if(!mstats.enablegc || m->locks > 0 || runtime·panicking)
return;
- if(gcpercent == -2) { // first time through
- p = runtime·getenv("GOGC");
- if(p == nil || p[0] == '\0')
- gcpercent = 100;
- else if(runtime·strcmp(p, (byte*)"off") == 0)
- gcpercent = -1;
- else
- gcpercent = runtime·atoi(p);
+ if(gcpercent == GcpercentUnknown) { // first time through
+ gcpercent = readgogc();
p = runtime·getenv("GOGCTRACE");
if(p != nil)
@@ -913,103 +1779,171 @@ runtime·gc(int32 force)
if(gcpercent < 0)
return;
+ // Run gc on a bigger stack to eliminate
+ // a potentially large number of calls to runtime·morestack.
+ a.force = force;
+ ap = &a;
+ m->moreframesize_minalloc = StackBig;
+ gcv.fn = (void*)gc;
+ reflect·call(&gcv, (byte*)&ap, sizeof(ap));
+
+ if(gctrace > 1 && !force) {
+ a.force = 1;
+ gc(&a);
+ }
+}
+
+static FuncVal runfinqv = {runfinq};
+
+static void
+gc(struct gc_args *args)
+{
+ int64 t0, t1, t2, t3, t4;
+ uint64 heap0, heap1, obj0, obj1, ninstr;
+ GCStats stats;
+ M *mp;
+ uint32 i;
+ Eface eface;
+
runtime·semacquire(&runtime·worldsema);
- if(!force && mstats.heap_alloc < mstats.next_gc) {
+ if(!args->force && mstats.heap_alloc < mstats.next_gc) {
runtime·semrelease(&runtime·worldsema);
return;
}
t0 = runtime·nanotime();
- nhandoff = 0;
m->gcing = 1;
runtime·stoptheworld();
- cachestats();
- heap0 = mstats.heap_alloc;
- obj0 = mstats.nmalloc - mstats.nfree;
+ if(CollectStats)
+ runtime·memclr((byte*)&gcstats, sizeof(gcstats));
- runtime·lock(&work.markgate);
- runtime·lock(&work.sweepgate);
+ for(mp=runtime·allm; mp; mp=mp->alllink)
+ runtime·settype_flush(mp, false);
- extra = false;
- work.nproc = 1;
- if(runtime·gomaxprocs > 1 && runtime·ncpu > 1) {
- runtime·noteclear(&work.alldone);
- work.nproc += runtime·helpgc(&extra);
+ heap0 = 0;
+ obj0 = 0;
+ if(gctrace) {
+ cachestats(nil);
+ heap0 = mstats.heap_alloc;
+ obj0 = mstats.nmalloc - mstats.nfree;
}
+
+ m->locks++; // disable gc during mallocs in parforalloc
+ if(work.markfor == nil)
+ work.markfor = runtime·parforalloc(MaxGcproc);
+ if(work.sweepfor == nil)
+ work.sweepfor = runtime·parforalloc(MaxGcproc);
+ m->locks--;
+
+ if(itabtype == nil) {
+ // get C pointer to the Go type "itab"
+ runtime·gc_itab_ptr(&eface);
+ itabtype = ((PtrType*)eface.type)->elem;
+ }
+
work.nwait = 0;
work.ndone = 0;
+ work.debugmarkdone = 0;
+ work.nproc = runtime·gcprocs();
+ addroots();
+ runtime·parforsetup(work.markfor, work.nproc, work.nroot, nil, false, markroot);
+ runtime·parforsetup(work.sweepfor, work.nproc, runtime·mheap->nspan, nil, true, sweepspan);
+ if(work.nproc > 1) {
+ runtime·noteclear(&work.alldone);
+ runtime·helpgc(work.nproc);
+ }
- runtime·unlock(&work.markgate); // let the helpers in
- mark(scanblock);
- if(DebugMark)
- mark(debug_scanblock);
t1 = runtime·nanotime();
- work.spans = runtime·mheap.allspans;
- runtime·unlock(&work.sweepgate); // let the helpers in
- sweep();
+ runtime·parfordo(work.markfor);
+ scanblock(nil, nil, 0, true);
+
+ if(DebugMark) {
+ for(i=0; i<work.nroot; i++)
+ debug_scanblock(work.roots[i].p, work.roots[i].n);
+ runtime·atomicstore(&work.debugmarkdone, 1);
+ }
+ t2 = runtime·nanotime();
+
+ runtime·parfordo(work.sweepfor);
+ t3 = runtime·nanotime();
+
if(work.nproc > 1)
runtime·notesleep(&work.alldone);
- t2 = runtime·nanotime();
- stealcache();
- cachestats();
+ cachestats(&stats);
+
+ stats.nprocyield += work.sweepfor->nprocyield;
+ stats.nosyield += work.sweepfor->nosyield;
+ stats.nsleep += work.sweepfor->nsleep;
mstats.next_gc = mstats.heap_alloc+mstats.heap_alloc*gcpercent/100;
m->gcing = 0;
- m->locks++; // disable gc during the mallocs in newproc
if(finq != nil) {
+ m->locks++; // disable gc during the mallocs in newproc
// kick off or wake up goroutine to run queued finalizers
if(fing == nil)
- fing = runtime·newproc1((byte*)runfinq, nil, 0, 0, runtime·gc);
+ fing = runtime·newproc1(&runfinqv, nil, 0, 0, runtime·gc);
else if(fingwait) {
fingwait = 0;
runtime·ready(fing);
}
+ m->locks--;
}
- m->locks--;
- cachestats();
heap1 = mstats.heap_alloc;
obj1 = mstats.nmalloc - mstats.nfree;
- t3 = runtime·nanotime();
- mstats.last_gc = t3;
- mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t3 - t0;
- mstats.pause_total_ns += t3 - t0;
+ t4 = runtime·nanotime();
+ mstats.last_gc = t4;
+ mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t4 - t0;
+ mstats.pause_total_ns += t4 - t0;
mstats.numgc++;
if(mstats.debuggc)
- runtime·printf("pause %D\n", t3-t0);
+ runtime·printf("pause %D\n", t4-t0);
if(gctrace) {
- runtime·printf("gc%d(%d): %D+%D+%D ms %D -> %D MB %D -> %D (%D-%D) objects %D handoff\n",
- mstats.numgc, work.nproc, (t1-t0)/1000000, (t2-t1)/1000000, (t3-t2)/1000000,
+ runtime·printf("gc%d(%d): %D+%D+%D ms, %D -> %D MB %D -> %D (%D-%D) objects,"
+ " %D(%D) handoff, %D(%D) steal, %D/%D/%D yields\n",
+ mstats.numgc, work.nproc, (t2-t1)/1000000, (t3-t2)/1000000, (t1-t0+t4-t3)/1000000,
heap0>>20, heap1>>20, obj0, obj1,
mstats.nmalloc, mstats.nfree,
- nhandoff);
+ stats.nhandoff, stats.nhandoffcnt,
+ work.sweepfor->nsteal, work.sweepfor->nstealcnt,
+ stats.nprocyield, stats.nosyield, stats.nsleep);
+ if(CollectStats) {
+ runtime·printf("scan: %D bytes, %D objects, %D untyped, %D types from MSpan\n",
+ gcstats.nbytes, gcstats.obj.cnt, gcstats.obj.notype, gcstats.obj.typelookup);
+ if(gcstats.ptr.cnt != 0)
+ runtime·printf("avg ptrbufsize: %D (%D/%D)\n",
+ gcstats.ptr.sum/gcstats.ptr.cnt, gcstats.ptr.sum, gcstats.ptr.cnt);
+ if(gcstats.obj.cnt != 0)
+ runtime·printf("avg nobj: %D (%D/%D)\n",
+ gcstats.obj.sum/gcstats.obj.cnt, gcstats.obj.sum, gcstats.obj.cnt);
+ runtime·printf("rescans: %D, %D bytes\n", gcstats.rescan, gcstats.rescanbytes);
+
+ runtime·printf("instruction counts:\n");
+ ninstr = 0;
+ for(i=0; i<nelem(gcstats.instr); i++) {
+ runtime·printf("\t%d:\t%D\n", i, gcstats.instr[i]);
+ ninstr += gcstats.instr[i];
+ }
+ runtime·printf("\ttotal:\t%D\n", ninstr);
+
+ runtime·printf("putempty: %D, getfull: %D\n", gcstats.putempty, gcstats.getfull);
+ }
}
-
+
runtime·MProf_GC();
runtime·semrelease(&runtime·worldsema);
+ runtime·starttheworld();
- // If we could have used another helper proc, start one now,
- // in the hope that it will be available next time.
- // It would have been even better to start it before the collection,
- // but doing so requires allocating memory, so it's tricky to
- // coordinate. This lazy approach works out in practice:
- // we don't mind if the first couple gc rounds don't have quite
- // the maximum number of procs.
- runtime·starttheworld(extra);
-
- // give the queued finalizers, if any, a chance to run
- if(finq != nil)
+ // give the queued finalizers, if any, a chance to run
+ if(finq != nil)
runtime·gosched();
-
- if(gctrace > 1 && !force)
- runtime·gc(1);
}
void
@@ -1022,11 +1956,56 @@ runtime·ReadMemStats(MStats *stats)
runtime·semacquire(&runtime·worldsema);
m->gcing = 1;
runtime·stoptheworld();
- cachestats();
+ cachestats(nil);
*stats = mstats;
m->gcing = 0;
runtime·semrelease(&runtime·worldsema);
- runtime·starttheworld(false);
+ runtime·starttheworld();
+}
+
+void
+runtime∕debug·readGCStats(Slice *pauses)
+{
+ uint64 *p;
+ uint32 i, n;
+
+ // Calling code in runtime/debug should make the slice large enough.
+ if(pauses->cap < nelem(mstats.pause_ns)+3)
+ runtime·throw("runtime: short slice passed to readGCStats");
+
+ // Pass back: pauses, last gc (absolute time), number of gc, total pause ns.
+ p = (uint64*)pauses->array;
+ runtime·lock(runtime·mheap);
+ n = mstats.numgc;
+ if(n > nelem(mstats.pause_ns))
+ n = nelem(mstats.pause_ns);
+
+ // The pause buffer is circular. The most recent pause is at
+ // pause_ns[(numgc-1)%nelem(pause_ns)], and then backward
+ // from there to go back farther in time. We deliver the times
+ // most recent first (in p[0]).
+ for(i=0; i<n; i++)
+ p[i] = mstats.pause_ns[(mstats.numgc-1-i)%nelem(mstats.pause_ns)];
+
+ p[n] = mstats.last_gc;
+ p[n+1] = mstats.numgc;
+ p[n+2] = mstats.pause_total_ns;
+ runtime·unlock(runtime·mheap);
+ pauses->len = n+3;
+}
+
+void
+runtime∕debug·setGCPercent(intgo in, intgo out)
+{
+ runtime·lock(runtime·mheap);
+ if(gcpercent == GcpercentUnknown)
+ gcpercent = readgogc();
+ out = gcpercent;
+ if(in < 0)
+ in = -1;
+ gcpercent = in;
+ runtime·unlock(runtime·mheap);
+ FLUSH(&out);
}
static void
@@ -1050,11 +2029,11 @@ runfinq(void)
finq = nil;
if(fb == nil) {
fingwait = 1;
- g->status = Gwaiting;
- g->waitreason = "finalizer wait";
- runtime·gosched();
+ runtime·park(nil, nil, "finalizer wait");
continue;
}
+ if(raceenabled)
+ runtime·racefingo();
for(; fb; fb=next) {
next = fb->next;
for(i=0; i<fb->cnt; i++) {
@@ -1066,7 +2045,7 @@ runfinq(void)
framecap = framesz;
}
*(void**)frame = f->arg;
- reflect·call((byte*)f->fn, frame, sizeof(uintptr) + f->nret);
+ reflect·call(f->fn, frame, sizeof(uintptr) + f->nret);
f->fn = nil;
f->arg = nil;
}
@@ -1088,11 +2067,11 @@ runtime·markallocated(void *v, uintptr n, bool noptr)
if(0)
runtime·printf("markallocated %p+%p\n", v, n);
- if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start)
+ if((byte*)v+n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start)
runtime·throw("markallocated: bad pointer");
- off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset
- b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start; // word offset
+ b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
for(;;) {
@@ -1120,11 +2099,11 @@ runtime·markfreed(void *v, uintptr n)
if(0)
runtime·printf("markallocated %p+%p\n", v, n);
- if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start)
+ if((byte*)v+n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start)
runtime·throw("markallocated: bad pointer");
- off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset
- b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start; // word offset
+ b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
for(;;) {
@@ -1150,11 +2129,11 @@ runtime·checkfreed(void *v, uintptr n)
if(!runtime·checking)
return;
- if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start)
+ if((byte*)v+n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start)
return; // not allocated, so okay
- off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start; // word offset
- b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start; // word offset
+ b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
bits = *b>>shift;
@@ -1173,7 +2152,7 @@ runtime·markspan(void *v, uintptr size, uintptr n, bool leftover)
uintptr *b, off, shift;
byte *p;
- if((byte*)v+size*n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start)
+ if((byte*)v+size*n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start)
runtime·throw("markspan: bad pointer");
p = v;
@@ -1184,8 +2163,8 @@ runtime·markspan(void *v, uintptr size, uintptr n, bool leftover)
// the entire span, and each bitmap word has bits for only
// one span, so no other goroutines are changing these
// bitmap words.
- off = (uintptr*)p - (uintptr*)runtime·mheap.arena_start; // word offset
- b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)p - (uintptr*)runtime·mheap->arena_start; // word offset
+ b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
*b = (*b & ~(bitMask<<shift)) | (bitBlockBoundary<<shift);
}
@@ -1197,14 +2176,14 @@ runtime·unmarkspan(void *v, uintptr n)
{
uintptr *p, *b, off;
- if((byte*)v+n > (byte*)runtime·mheap.arena_used || (byte*)v < runtime·mheap.arena_start)
+ if((byte*)v+n > (byte*)runtime·mheap->arena_used || (byte*)v < runtime·mheap->arena_start)
runtime·throw("markspan: bad pointer");
p = v;
- off = p - (uintptr*)runtime·mheap.arena_start; // word offset
+ off = p - (uintptr*)runtime·mheap->arena_start; // word offset
if(off % wordsPerBitmapWord != 0)
runtime·throw("markspan: unaligned pointer");
- b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
+ b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
n /= PtrSize;
if(n%wordsPerBitmapWord != 0)
runtime·throw("unmarkspan: unaligned length");
@@ -1225,8 +2204,8 @@ runtime·blockspecial(void *v)
if(DebugMark)
return true;
- off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start;
- b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start;
+ b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
return (*b & (bitSpecial<<shift)) != 0;
@@ -1240,8 +2219,8 @@ runtime·setblockspecial(void *v, bool s)
if(DebugMark)
return;
- off = (uintptr*)v - (uintptr*)runtime·mheap.arena_start;
- b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
+ off = (uintptr*)v - (uintptr*)runtime·mheap->arena_start;
+ b = (uintptr*)runtime·mheap->arena_start - off/wordsPerBitmapWord - 1;
shift = off % wordsPerBitmapWord;
for(;;) {
diff --git a/src/pkg/runtime/mgc0.go b/src/pkg/runtime/mgc0.go
new file mode 100644
index 000000000..b15054662
--- /dev/null
+++ b/src/pkg/runtime/mgc0.go
@@ -0,0 +1,15 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+// Called from C. Returns the Go type *m.
+func gc_m_ptr(ret *interface{}) {
+ *ret = (*m)(nil)
+}
+
+// Called from C. Returns the Go type *itab.
+func gc_itab_ptr(ret *interface{}) {
+ *ret = (*itab)(nil)
+}
diff --git a/src/pkg/runtime/mgc0.h b/src/pkg/runtime/mgc0.h
new file mode 100644
index 000000000..87b604a36
--- /dev/null
+++ b/src/pkg/runtime/mgc0.h
@@ -0,0 +1,43 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Garbage collector (GC)
+
+// GC instruction opcodes.
+//
+// The opcode of an instruction is followed by zero or more
+// arguments to the instruction.
+//
+// Meaning of arguments:
+// off Offset (in bytes) from the start of the current object
+// objgc Pointer to GC info of an object
+// objgcrel Offset to GC info of an object
+// len Length of an array
+// elemsize Size (in bytes) of an element
+// size Size (in bytes)
+enum {
+ GC_END, // End of object, loop or subroutine. Args: none
+ GC_PTR, // A typed pointer. Args: (off, objgc)
+ GC_APTR, // Pointer to an arbitrary object. Args: (off)
+ GC_ARRAY_START, // Start an array with a fixed length. Args: (off, len, elemsize)
+ GC_ARRAY_NEXT, // The next element of an array. Args: none
+ GC_CALL, // Call a subroutine. Args: (off, objgcrel)
+ GC_MAP_PTR, // Go map. Args: (off, MapType*)
+ GC_STRING, // Go string. Args: (off)
+ GC_EFACE, // interface{}. Args: (off)
+ GC_IFACE, // interface{...}. Args: (off)
+ GC_SLICE, // Go slice. Args: (off, objgc)
+ GC_REGION, // A region/part of the current object. Args: (off, size, objgc)
+
+ GC_NUM_INSTR, // Number of instruction opcodes
+};
+
+enum {
+ // Size of GC's fixed stack.
+ //
+ // The current GC implementation permits:
+ // - at most 1 stack allocation because of GC_CALL
+ // - at most GC_STACK_CAPACITY allocations because of GC_ARRAY_START
+ GC_STACK_CAPACITY = 8,
+};
diff --git a/src/pkg/runtime/mheap.c b/src/pkg/runtime/mheap.c
index c877bfca9..f45149d63 100644
--- a/src/pkg/runtime/mheap.c
+++ b/src/pkg/runtime/mheap.c
@@ -27,11 +27,26 @@ RecordSpan(void *vh, byte *p)
{
MHeap *h;
MSpan *s;
+ MSpan **all;
+ uint32 cap;
h = vh;
s = (MSpan*)p;
- s->allnext = h->allspans;
- h->allspans = s;
+ if(h->nspan >= h->nspancap) {
+ cap = 64*1024/sizeof(all[0]);
+ if(cap < h->nspancap*3/2)
+ cap = h->nspancap*3/2;
+ all = (MSpan**)runtime·SysAlloc(cap*sizeof(all[0]));
+ if(all == nil)
+ runtime·throw("runtime: cannot allocate memory");
+ if(h->allspans) {
+ runtime·memmove(all, h->allspans, h->nspancap*sizeof(all[0]));
+ runtime·SysFree(h->allspans, h->nspancap*sizeof(all[0]));
+ }
+ h->allspans = all;
+ h->nspancap = cap;
+ }
+ h->allspans[h->nspan++] = s;
}
// Initialize the heap; fetch memory using alloc.
@@ -53,12 +68,12 @@ runtime·MHeap_Init(MHeap *h, void *(*alloc)(uintptr))
// Allocate a new span of npage pages from the heap
// and record its size class in the HeapMap and HeapMapCache.
MSpan*
-runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct)
+runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct, int32 zeroed)
{
MSpan *s;
runtime·lock(h);
- runtime·purgecachedstats(m);
+ runtime·purgecachedstats(m->mcache);
s = MHeap_AllocLocked(h, npage, sizeclass);
if(s != nil) {
mstats.heap_inuse += npage<<PageShift;
@@ -68,6 +83,8 @@ runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct)
}
}
runtime·unlock(h);
+ if(s != nil && *(uintptr*)(s->start<<PageShift) != 0 && zeroed)
+ runtime·memclr((byte*)(s->start<<PageShift), s->npages<<PageShift);
return s;
}
@@ -123,14 +140,15 @@ HaveSpan:
*(uintptr*)(t->start<<PageShift) = *(uintptr*)(s->start<<PageShift); // copy "needs zeroing" mark
t->state = MSpanInUse;
MHeap_FreeLocked(h, t);
+ t->unusedsince = s->unusedsince; // preserve age
}
-
- if(*(uintptr*)(s->start<<PageShift) != 0)
- runtime·memclr((byte*)(s->start<<PageShift), s->npages<<PageShift);
+ s->unusedsince = 0;
// Record span info, because gc needs to be
// able to map interior pointer to containing span.
s->sizeclass = sizeclass;
+ s->elemsize = (sizeclass==0 ? s->npages<<PageShift : runtime·class_to_size[sizeclass]);
+ s->types.compression = MTypes_Empty;
p = s->start;
if(sizeof(void*) == 8)
p -= ((uintptr)h->arena_start>>PageShift);
@@ -259,7 +277,7 @@ void
runtime·MHeap_Free(MHeap *h, MSpan *s, int32 acct)
{
runtime·lock(h);
- runtime·purgecachedstats(m);
+ runtime·purgecachedstats(m->mcache);
mstats.heap_inuse -= s->npages<<PageShift;
if(acct) {
mstats.heap_alloc -= s->npages<<PageShift;
@@ -276,16 +294,22 @@ MHeap_FreeLocked(MHeap *h, MSpan *s)
MSpan *t;
PageID p;
+ if(s->types.sysalloc)
+ runtime·settype_sysfree(s);
+ s->types.compression = MTypes_Empty;
+
if(s->state != MSpanInUse || s->ref != 0) {
runtime·printf("MHeap_FreeLocked - span %p ptr %p state %d ref %d\n", s, s->start<<PageShift, s->state, s->ref);
runtime·throw("MHeap_FreeLocked - invalid free");
}
mstats.heap_idle += s->npages<<PageShift;
s->state = MSpanFree;
- s->unusedsince = 0;
- s->npreleased = 0;
runtime·MSpanList_Remove(s);
sp = (uintptr*)(s->start<<PageShift);
+ // Stamp newly unused spans. The scavenger will use that
+ // info to potentially give back some pages to the OS.
+ s->unusedsince = runtime·nanotime();
+ s->npreleased = 0;
// Coalesce with earlier, later spans.
p = s->start;
@@ -325,6 +349,52 @@ MHeap_FreeLocked(MHeap *h, MSpan *s)
runtime·MSpanList_Insert(&h->large, s);
}
+static void
+forcegchelper(Note *note)
+{
+ runtime·gc(1);
+ runtime·notewakeup(note);
+}
+
+static uintptr
+scavengelist(MSpan *list, uint64 now, uint64 limit)
+{
+ uintptr released, sumreleased;
+ MSpan *s;
+
+ if(runtime·MSpanList_IsEmpty(list))
+ return 0;
+
+ sumreleased = 0;
+ for(s=list->next; s != list; s=s->next) {
+ if((now - s->unusedsince) > limit) {
+ released = (s->npages - s->npreleased) << PageShift;
+ mstats.heap_released += released;
+ sumreleased += released;
+ s->npreleased = s->npages;
+ runtime·SysUnused((void*)(s->start << PageShift), s->npages << PageShift);
+ }
+ }
+ return sumreleased;
+}
+
+static uintptr
+scavenge(uint64 now, uint64 limit)
+{
+ uint32 i;
+ uintptr sumreleased;
+ MHeap *h;
+
+ h = runtime·mheap;
+ sumreleased = 0;
+ for(i=0; i < nelem(h->free); i++)
+ sumreleased += scavengelist(&h->free[i], now, limit);
+ sumreleased += scavengelist(&h->large, now, limit);
+ return sumreleased;
+}
+
+static FuncVal forcegchelperv = {(void(*)(void))forcegchelper};
+
// Release (part of) unused memory to OS.
// Goroutine created at startup.
// Loop forever.
@@ -332,13 +402,12 @@ void
runtime·MHeap_Scavenger(void)
{
MHeap *h;
- MSpan *s, *list;
uint64 tick, now, forcegc, limit;
- uint32 k, i;
- uintptr released, sumreleased;
+ uint32 k;
+ uintptr sumreleased;
byte *env;
bool trace;
- Note note;
+ Note note, *notep;
// If we go two minutes without a garbage collection, force one to run.
forcegc = 2*60*1e9;
@@ -356,10 +425,10 @@ runtime·MHeap_Scavenger(void)
if(env != nil)
trace = runtime·atoi(env) > 0;
- h = &runtime·mheap;
+ h = runtime·mheap;
for(k=0;; k++) {
runtime·noteclear(&note);
- runtime·entersyscall();
+ runtime·entersyscallblock();
runtime·notetsleep(&note, tick);
runtime·exitsyscall();
@@ -367,30 +436,21 @@ runtime·MHeap_Scavenger(void)
now = runtime·nanotime();
if(now - mstats.last_gc > forcegc) {
runtime·unlock(h);
- runtime·gc(1);
+ // The scavenger can not block other goroutines,
+ // otherwise deadlock detector can fire spuriously.
+ // GC blocks other goroutines via the runtime·worldsema.
+ runtime·noteclear(&note);
+ notep = &note;
+ runtime·newproc1(&forcegchelperv, (byte*)&notep, sizeof(notep), 0, runtime·MHeap_Scavenger);
+ runtime·entersyscallblock();
+ runtime·notesleep(&note);
+ runtime·exitsyscall();
+ if(trace)
+ runtime·printf("scvg%d: GC forced\n", k);
runtime·lock(h);
now = runtime·nanotime();
- if (trace)
- runtime·printf("scvg%d: GC forced\n", k);
- }
- sumreleased = 0;
- for(i=0; i < nelem(h->free)+1; i++) {
- if(i < nelem(h->free))
- list = &h->free[i];
- else
- list = &h->large;
- if(runtime·MSpanList_IsEmpty(list))
- continue;
- for(s=list->next; s != list; s=s->next) {
- if(s->unusedsince != 0 && (now - s->unusedsince) > limit) {
- released = (s->npages - s->npreleased) << PageShift;
- mstats.heap_released += released;
- sumreleased += released;
- s->npreleased = s->npages;
- runtime·SysUnused((void*)(s->start << PageShift), s->npages << PageShift);
- }
- }
}
+ sumreleased = scavenge(now, limit);
runtime·unlock(h);
if(trace) {
@@ -403,6 +463,15 @@ runtime·MHeap_Scavenger(void)
}
}
+void
+runtime∕debug·freeOSMemory(void)
+{
+ runtime·gc(1);
+ runtime·lock(runtime·mheap);
+ scavenge(~(uintptr)0, 0);
+ runtime·unlock(runtime·mheap);
+}
+
// Initialize a new span with the given start and npages.
void
runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages)
@@ -414,9 +483,11 @@ runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages)
span->freelist = nil;
span->ref = 0;
span->sizeclass = 0;
+ span->elemsize = 0;
span->state = 0;
span->unusedsince = 0;
span->npreleased = 0;
+ span->types.compression = MTypes_Empty;
}
// Initialize an empty doubly-linked list.
diff --git a/src/pkg/runtime/mprof.goc b/src/pkg/runtime/mprof.goc
index 0bbce8583..ebc1e3e66 100644
--- a/src/pkg/runtime/mprof.goc
+++ b/src/pkg/runtime/mprof.goc
@@ -13,23 +13,73 @@ package runtime
#include "type.h"
// NOTE(rsc): Everything here could use cas if contention became an issue.
-static Lock proflock;
+static Lock proflock, alloclock;
-// Per-call-stack allocation information.
+// All memory allocations are local and do not escape outside of the profiler.
+// The profiler is forbidden from referring to garbage-collected memory.
+
+static byte *pool; // memory allocation pool
+static uintptr poolfree; // number of bytes left in the pool
+enum {
+ Chunk = 32*PageSize, // initial size of the pool
+};
+
+// Memory allocation local to this file.
+// There is no way to return the allocated memory back to the OS.
+static void*
+allocate(uintptr size)
+{
+ void *v;
+
+ if(size == 0)
+ return nil;
+
+ if(size >= Chunk/2)
+ return runtime·SysAlloc(size);
+
+ runtime·lock(&alloclock);
+ if(size > poolfree) {
+ pool = runtime·SysAlloc(Chunk);
+ if(pool == nil)
+ runtime·throw("runtime: cannot allocate memory");
+ poolfree = Chunk;
+ }
+ v = pool;
+ pool += size;
+ poolfree -= size;
+ runtime·unlock(&alloclock);
+ return v;
+}
+
+enum { MProf, BProf }; // profile types
+
+// Per-call-stack profiling information.
// Lookup by hashing call stack into a linked-list hash table.
typedef struct Bucket Bucket;
struct Bucket
{
Bucket *next; // next in hash list
- Bucket *allnext; // next in list of all buckets
- uintptr allocs;
- uintptr frees;
- uintptr alloc_bytes;
- uintptr free_bytes;
- uintptr recent_allocs; // since last gc
- uintptr recent_frees;
- uintptr recent_alloc_bytes;
- uintptr recent_free_bytes;
+ Bucket *allnext; // next in list of all mbuckets/bbuckets
+ int32 typ;
+ union
+ {
+ struct // typ == MProf
+ {
+ uintptr allocs;
+ uintptr frees;
+ uintptr alloc_bytes;
+ uintptr free_bytes;
+ uintptr recent_allocs; // since last gc
+ uintptr recent_frees;
+ uintptr recent_alloc_bytes;
+ uintptr recent_free_bytes;
+ };
+ struct // typ == BProf
+ {
+ int64 count;
+ int64 cycles;
+ };
+ };
uintptr hash;
uintptr nstk;
uintptr stk[1];
@@ -38,12 +88,13 @@ enum {
BuckHashSize = 179999,
};
static Bucket **buckhash;
-static Bucket *buckets;
+static Bucket *mbuckets; // memory profile buckets
+static Bucket *bbuckets; // blocking profile buckets
static uintptr bucketmem;
// Return the bucket for stk[0:nstk], allocating new bucket if needed.
static Bucket*
-stkbucket(uintptr *stk, int32 nstk, bool alloc)
+stkbucket(int32 typ, uintptr *stk, int32 nstk, bool alloc)
{
int32 i;
uintptr h;
@@ -51,6 +102,8 @@ stkbucket(uintptr *stk, int32 nstk, bool alloc)
if(buckhash == nil) {
buckhash = runtime·SysAlloc(BuckHashSize*sizeof buckhash[0]);
+ if(buckhash == nil)
+ runtime·throw("runtime: cannot allocate memory");
mstats.buckhash_sys += BuckHashSize*sizeof buckhash[0];
}
@@ -66,33 +119,39 @@ stkbucket(uintptr *stk, int32 nstk, bool alloc)
i = h%BuckHashSize;
for(b = buckhash[i]; b; b=b->next)
- if(b->hash == h && b->nstk == nstk &&
+ if(b->typ == typ && b->hash == h && b->nstk == nstk &&
runtime·mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) == 0)
return b;
if(!alloc)
return nil;
- b = runtime·mallocgc(sizeof *b + nstk*sizeof stk[0], FlagNoProfiling, 0, 1);
+ b = allocate(sizeof *b + nstk*sizeof stk[0]);
+ if(b == nil)
+ runtime·throw("runtime: cannot allocate memory");
bucketmem += sizeof *b + nstk*sizeof stk[0];
runtime·memmove(b->stk, stk, nstk*sizeof stk[0]);
+ b->typ = typ;
b->hash = h;
b->nstk = nstk;
b->next = buckhash[i];
buckhash[i] = b;
- b->allnext = buckets;
- buckets = b;
+ if(typ == MProf) {
+ b->allnext = mbuckets;
+ mbuckets = b;
+ } else {
+ b->allnext = bbuckets;
+ bbuckets = b;
+ }
return b;
}
-// Record that a gc just happened: all the 'recent' statistics are now real.
-void
-runtime·MProf_GC(void)
+static void
+MProf_GC(void)
{
Bucket *b;
-
- runtime·lock(&proflock);
- for(b=buckets; b; b=b->allnext) {
+
+ for(b=mbuckets; b; b=b->allnext) {
b->allocs += b->recent_allocs;
b->frees += b->recent_frees;
b->alloc_bytes += b->recent_alloc_bytes;
@@ -102,25 +161,39 @@ runtime·MProf_GC(void)
b->recent_alloc_bytes = 0;
b->recent_free_bytes = 0;
}
+}
+
+// Record that a gc just happened: all the 'recent' statistics are now real.
+void
+runtime·MProf_GC(void)
+{
+ runtime·lock(&proflock);
+ MProf_GC();
runtime·unlock(&proflock);
}
// Map from pointer to Bucket* that allocated it.
// Three levels:
-// Linked-list hash table for top N-20 bits.
-// Array index for next 13 bits.
-// Linked list for next 7 bits.
+// Linked-list hash table for top N-AddrHashShift bits.
+// Array index for next AddrDenseBits bits.
+// Linked list for next AddrHashShift-AddrDenseBits bits.
// This is more efficient than using a general map,
// because of the typical clustering of the pointer keys.
typedef struct AddrHash AddrHash;
typedef struct AddrEntry AddrEntry;
+enum {
+ AddrHashBits = 12, // good for 4GB of used address space
+ AddrHashShift = 20, // each AddrHash knows about 1MB of address space
+ AddrDenseBits = 8, // good for a profiling rate of 4096 bytes
+};
+
struct AddrHash
{
AddrHash *next; // next in top-level hash table linked list
uintptr addr; // addr>>20
- AddrEntry *dense[1<<13];
+ AddrEntry *dense[1<<AddrDenseBits];
};
struct AddrEntry
@@ -130,10 +203,7 @@ struct AddrEntry
Bucket *b;
};
-enum {
- AddrHashBits = 12 // 1MB per entry, so good for 4GB of used address space
-};
-static AddrHash *addrhash[1<<AddrHashBits];
+static AddrHash **addrhash; // points to (AddrHash*)[1<<AddrHashBits]
static AddrEntry *addrfree;
static uintptr addrmem;
@@ -155,29 +225,29 @@ setaddrbucket(uintptr addr, Bucket *b)
AddrHash *ah;
AddrEntry *e;
- h = (uint32)((addr>>20)*HashMultiplier) >> (32-AddrHashBits);
+ h = (uint32)((addr>>AddrHashShift)*HashMultiplier) >> (32-AddrHashBits);
for(ah=addrhash[h]; ah; ah=ah->next)
- if(ah->addr == (addr>>20))
+ if(ah->addr == (addr>>AddrHashShift))
goto found;
- ah = runtime·mallocgc(sizeof *ah, FlagNoProfiling, 0, 1);
+ ah = allocate(sizeof *ah);
addrmem += sizeof *ah;
ah->next = addrhash[h];
- ah->addr = addr>>20;
+ ah->addr = addr>>AddrHashShift;
addrhash[h] = ah;
found:
if((e = addrfree) == nil) {
- e = runtime·mallocgc(64*sizeof *e, FlagNoProfiling, 0, 0);
+ e = allocate(64*sizeof *e);
addrmem += 64*sizeof *e;
for(i=0; i+1<64; i++)
e[i].next = &e[i+1];
e[63].next = nil;
}
addrfree = e->next;
- e->addr = (uint32)~(addr & ((1<<20)-1));
+ e->addr = (uint32)~(addr & ((1<<AddrHashShift)-1));
e->b = b;
- h = (addr>>7)&(nelem(ah->dense)-1); // entry in dense is top 13 bits of low 20.
+ h = (addr>>(AddrHashShift-AddrDenseBits))&(nelem(ah->dense)-1); // entry in dense is top 8 bits of low 20.
e->next = ah->dense[h];
ah->dense[h] = e;
}
@@ -191,16 +261,16 @@ getaddrbucket(uintptr addr)
AddrEntry *e, **l;
Bucket *b;
- h = (uint32)((addr>>20)*HashMultiplier) >> (32-AddrHashBits);
+ h = (uint32)((addr>>AddrHashShift)*HashMultiplier) >> (32-AddrHashBits);
for(ah=addrhash[h]; ah; ah=ah->next)
- if(ah->addr == (addr>>20))
+ if(ah->addr == (addr>>AddrHashShift))
goto found;
return nil;
found:
- h = (addr>>7)&(nelem(ah->dense)-1); // entry in dense is top 13 bits of low 20.
+ h = (addr>>(AddrHashShift-AddrDenseBits))&(nelem(ah->dense)-1); // entry in dense is top 8 bits of low 20.
for(l=&ah->dense[h]; (e=*l) != nil; l=&e->next) {
- if(e->addr == (uint32)~(addr & ((1<<20)-1))) {
+ if(e->addr == (uint32)~(addr & ((1<<AddrHashShift)-1))) {
*l = e->next;
b = e->b;
e->next = addrfree;
@@ -225,7 +295,7 @@ runtime·MProf_Malloc(void *p, uintptr size)
m->nomemprof++;
nstk = runtime·callers(1, stk, 32);
runtime·lock(&proflock);
- b = stkbucket(stk, nstk, true);
+ b = stkbucket(MProf, stk, nstk, true);
b->recent_allocs++;
b->recent_alloc_bytes += size;
setaddrbucket((uintptr)p, b);
@@ -253,9 +323,37 @@ runtime·MProf_Free(void *p, uintptr size)
m->nomemprof--;
}
+int64 runtime·blockprofilerate; // in CPU ticks
+
+void
+runtime·SetBlockProfileRate(intgo rate)
+{
+ runtime·atomicstore64((uint64*)&runtime·blockprofilerate, rate * runtime·tickspersecond() / (1000*1000*1000));
+}
+
+void
+runtime·blockevent(int64 cycles, int32 skip)
+{
+ int32 nstk;
+ int64 rate;
+ uintptr stk[32];
+ Bucket *b;
-// Go interface to profile data. (Declared in extern.go)
-// Assumes Go sizeof(int) == sizeof(int32)
+ if(cycles <= 0)
+ return;
+ rate = runtime·atomicload64((uint64*)&runtime·blockprofilerate);
+ if(rate <= 0 || (rate > cycles && runtime·fastrand1()%rate > cycles))
+ return;
+
+ nstk = runtime·callers(skip, stk, 32);
+ runtime·lock(&proflock);
+ b = stkbucket(BProf, stk, nstk, true);
+ b->count++;
+ b->cycles += cycles;
+ runtime·unlock(&proflock);
+}
+
+// Go interface to profile data. (Declared in debug.go)
// Must match MemProfileRecord in debug.go.
typedef struct Record Record;
@@ -281,52 +379,101 @@ record(Record *r, Bucket *b)
r->stk[i] = 0;
}
-func MemProfile(p Slice, include_inuse_zero bool) (n int32, ok bool) {
+func MemProfile(p Slice, include_inuse_zero bool) (n int, ok bool) {
Bucket *b;
Record *r;
+ bool clear;
runtime·lock(&proflock);
n = 0;
- for(b=buckets; b; b=b->allnext)
+ clear = true;
+ for(b=mbuckets; b; b=b->allnext) {
if(include_inuse_zero || b->alloc_bytes != b->free_bytes)
n++;
+ if(b->allocs != 0 || b->frees != 0)
+ clear = false;
+ }
+ if(clear) {
+ // Absolutely no data, suggesting that a garbage collection
+ // has not yet happened. In order to allow profiling when
+ // garbage collection is disabled from the beginning of execution,
+ // accumulate stats as if a GC just happened, and recount buckets.
+ MProf_GC();
+ n = 0;
+ for(b=mbuckets; b; b=b->allnext)
+ if(include_inuse_zero || b->alloc_bytes != b->free_bytes)
+ n++;
+ }
ok = false;
if(n <= p.len) {
ok = true;
r = (Record*)p.array;
- for(b=buckets; b; b=b->allnext)
+ for(b=mbuckets; b; b=b->allnext)
if(include_inuse_zero || b->alloc_bytes != b->free_bytes)
record(r++, b);
}
runtime·unlock(&proflock);
}
+// Must match BlockProfileRecord in debug.go.
+typedef struct BRecord BRecord;
+struct BRecord {
+ int64 count;
+ int64 cycles;
+ uintptr stk[32];
+};
+
+func BlockProfile(p Slice) (n int, ok bool) {
+ Bucket *b;
+ BRecord *r;
+ int32 i;
+
+ runtime·lock(&proflock);
+ n = 0;
+ for(b=bbuckets; b; b=b->allnext)
+ n++;
+ ok = false;
+ if(n <= p.len) {
+ ok = true;
+ r = (BRecord*)p.array;
+ for(b=bbuckets; b; b=b->allnext, r++) {
+ r->count = b->count;
+ r->cycles = b->cycles;
+ for(i=0; i<b->nstk && i<nelem(r->stk); i++)
+ r->stk[i] = b->stk[i];
+ for(; i<nelem(r->stk); i++)
+ r->stk[i] = 0;
+ }
+ }
+ runtime·unlock(&proflock);
+}
+
// Must match StackRecord in debug.go.
typedef struct TRecord TRecord;
struct TRecord {
uintptr stk[32];
};
-func ThreadCreateProfile(p Slice) (n int32, ok bool) {
+func ThreadCreateProfile(p Slice) (n int, ok bool) {
TRecord *r;
- M *first, *m;
+ M *first, *mp;
first = runtime·atomicloadp(&runtime·allm);
n = 0;
- for(m=first; m; m=m->alllink)
+ for(mp=first; mp; mp=mp->alllink)
n++;
ok = false;
if(n <= p.len) {
ok = true;
r = (TRecord*)p.array;
- for(m=first; m; m=m->alllink) {
- runtime·memmove(r->stk, m->createstack, sizeof r->stk);
+ for(mp=first; mp; mp=mp->alllink) {
+ runtime·memmove(r->stk, mp->createstack, sizeof r->stk);
r++;
}
}
}
-func Stack(b Slice, all bool) (n int32) {
+func Stack(b Slice, all bool) (n int) {
byte *pc, *sp;
sp = runtime·getcallersp(&b);
@@ -355,21 +502,21 @@ func Stack(b Slice, all bool) (n int32) {
if(all) {
m->gcing = 0;
runtime·semrelease(&runtime·worldsema);
- runtime·starttheworld(false);
+ runtime·starttheworld();
}
}
static void
-saveg(byte *pc, byte *sp, G *g, TRecord *r)
+saveg(byte *pc, byte *sp, G *gp, TRecord *r)
{
int32 n;
- n = runtime·gentraceback(pc, sp, 0, g, 0, r->stk, nelem(r->stk));
+ n = runtime·gentraceback(pc, sp, 0, gp, 0, r->stk, nelem(r->stk));
if(n < nelem(r->stk))
r->stk[n] = 0;
}
-func GoroutineProfile(b Slice) (n int32, ok bool) {
+func GoroutineProfile(b Slice) (n int, ok bool) {
byte *pc, *sp;
TRecord *r;
G *gp;
@@ -392,13 +539,18 @@ func GoroutineProfile(b Slice) (n int32, ok bool) {
for(gp = runtime·allg; gp != nil; gp = gp->alllink) {
if(gp == g || gp->status == Gdead)
continue;
- saveg(gp->sched.pc, gp->sched.sp, gp, r++);
+ saveg(gp->sched.pc, (byte*)gp->sched.sp, gp, r++);
}
}
m->gcing = 0;
runtime·semrelease(&runtime·worldsema);
- runtime·starttheworld(false);
+ runtime·starttheworld();
}
}
+void
+runtime·mprofinit(void)
+{
+ addrhash = allocate((1<<AddrHashBits)*sizeof *addrhash);
+}
diff --git a/src/pkg/runtime/os_darwin.h b/src/pkg/runtime/os_darwin.h
index eb5d2daa3..5fcb717cb 100644
--- a/src/pkg/runtime/os_darwin.h
+++ b/src/pkg/runtime/os_darwin.h
@@ -4,9 +4,11 @@
#define SIG_DFL ((void*)0)
#define SIG_IGN ((void*)1)
+#define SIGHUP 1
+#define SS_DISABLE 4
int32 runtime·bsdthread_create(void*, M*, G*, void(*)(void));
-void runtime·bsdthread_register(void);
+int32 runtime·bsdthread_register(void);
int32 runtime·mach_msg_trap(MachHeader*, int32, uint32, uint32, uint32, uint32, uint32);
uint32 runtime·mach_reply_port(void);
int32 runtime·mach_semacquire(uint32, int64);
diff --git a/src/pkg/runtime/os_freebsd.h b/src/pkg/runtime/os_freebsd.h
index 5e8de5434..a37ad7cd8 100644
--- a/src/pkg/runtime/os_freebsd.h
+++ b/src/pkg/runtime/os_freebsd.h
@@ -1,5 +1,7 @@
#define SIG_DFL ((void*)0)
#define SIG_IGN ((void*)1)
+#define SIGHUP 1
+#define SS_DISABLE 4
int32 runtime·thr_new(ThrParam*, int32);
void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp);
@@ -15,7 +17,7 @@ int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
void runtime·raisesigpipe(void);
#define NSIG 33
-#define SI_USER 0
+#define SI_USER 0x10001
#define RLIMIT_AS 10
typedef struct Rlimit Rlimit;
diff --git a/src/pkg/runtime/os_linux.h b/src/pkg/runtime/os_linux.h
index 87daa3bb1..a23fe0f73 100644
--- a/src/pkg/runtime/os_linux.h
+++ b/src/pkg/runtime/os_linux.h
@@ -4,13 +4,15 @@
#define SIG_DFL ((void*)0)
#define SIG_IGN ((void*)1)
+#define SIGHUP 1
+#define SS_DISABLE 2
// Linux-specific system calls
int32 runtime·futex(uint32*, int32, uint32, Timespec*, uint32*, uint32);
int32 runtime·clone(int32, void*, M*, G*, void(*)(void));
struct Sigaction;
-void runtime·rt_sigaction(uintptr, struct Sigaction*, void*, uintptr);
+int32 runtime·rt_sigaction(uintptr, struct Sigaction*, void*, uintptr);
void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool);
void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp);
diff --git a/src/pkg/runtime/os_netbsd.h b/src/pkg/runtime/os_netbsd.h
index 4ecf78d88..19d72fd25 100644
--- a/src/pkg/runtime/os_netbsd.h
+++ b/src/pkg/runtime/os_netbsd.h
@@ -4,18 +4,29 @@
#define SIG_DFL ((void*)0)
#define SIG_IGN ((void*)1)
+#define SIGHUP 1
+#define SS_DISABLE 4
+
+#define SIG_BLOCK 1
+#define SIG_UNBLOCK 2
+#define SIG_SETMASK 3
struct sigaction;
-void runtime·sigpanic(void);
-void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
-void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
+void runtime·raisesigpipe(void);
void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool);
void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp);
+void runtime·sigpanic(void);
+
void runtime·setitimer(int32, Itimerval*, Itimerval*);
+void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
+void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
+void runtime·sigprocmask(int32, Sigset*, Sigset*);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
-void runtime·raisesigpipe(void);
-
#define NSIG 33
#define SI_USER 0
+
+// From NetBSD's <sys/ucontext.h>
+#define _UC_SIGMASK 0x01
+#define _UC_CPU 0x04
diff --git a/src/pkg/runtime/os_openbsd.h b/src/pkg/runtime/os_openbsd.h
index 4ecf78d88..a599aad05 100644
--- a/src/pkg/runtime/os_openbsd.h
+++ b/src/pkg/runtime/os_openbsd.h
@@ -4,18 +4,25 @@
#define SIG_DFL ((void*)0)
#define SIG_IGN ((void*)1)
+#define SIGHUP 1
+#define SS_DISABLE 4
+
+#define SIG_BLOCK 1
+#define SIG_UNBLOCK 2
+#define SIG_SETMASK 3
struct sigaction;
+void runtime·raisesigpipe(void);
+void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool);
void runtime·sigpanic(void);
-void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
+
+void runtime·setitimer(int32, Itimerval*, Itimerval*);
void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
-void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool);
+void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp);
-void runtime·setitimer(int32, Itimerval*, Itimerval*);
+Sigset runtime·sigprocmask(int32, Sigset);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
-void runtime·raisesigpipe(void);
-
#define NSIG 33
#define SI_USER 0
diff --git a/src/pkg/runtime/os_plan9.h b/src/pkg/runtime/os_plan9.h
index cc6343c8e..c2cdf5b44 100644
--- a/src/pkg/runtime/os_plan9.h
+++ b/src/pkg/runtime/os_plan9.h
@@ -7,13 +7,22 @@ int32 runtime·open(uint8 *file, int32 mode);
int32 runtime·pread(int32 fd, void *buf, int32 nbytes, int64 offset);
int32 runtime·pwrite(int32 fd, void *buf, int32 nbytes, int64 offset);
int32 runtime·read(int32 fd, void *buf, int32 nbytes);
+int64 runtime·seek(int32 fd, int64 offset, int32 whence);
int32 runtime·close(int32 fd);
void runtime·exits(int8* msg);
-int32 runtime·brk_(void*);
+intptr runtime·brk_(void*);
int32 runtime·sleep(int32 ms);
-int32 runtime·rfork(int32 flags, void *stk, M *m, G *g, void (*fn)(void));
+int32 runtime·rfork(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void));
int32 runtime·plan9_semacquire(uint32 *addr, int32 block);
+int32 runtime·plan9_tsemacquire(uint32 *addr, int32 ms);
int32 runtime·plan9_semrelease(uint32 *addr, int32 count);
+int32 runtime·notify(void (*fn)(void*, int8*));
+int32 runtime·noted(int32);
+void runtime·sigtramp(void*, int8*);
+int32 runtime·sighandler(void*, int8*, G*);
+void runtime·sigpanic(void);
+void runtime·goexitsall(int8*);
+void runtime·setfpmasks(void);
/* open */
enum
@@ -45,6 +54,13 @@ enum
RFNOMNT = (1<<14)
};
+/* notify */
+enum
+{
+ NCONT = 0,
+ NDFLT = 1
+};
+
typedef struct Tos Tos;
typedef intptr Plink;
@@ -66,4 +82,5 @@ struct Tos {
/* top of stack is here */
};
-#define NSIG 1
+#define NSIG 5 /* number of signals in runtime·SigTab array */
+#define ERRMAX 128 /* max length of note string */
diff --git a/src/pkg/runtime/os_windows.h b/src/pkg/runtime/os_windows.h
index 9d387b7ad..cf0ecb68e 100644
--- a/src/pkg/runtime/os_windows.h
+++ b/src/pkg/runtime/os_windows.h
@@ -28,5 +28,8 @@ uint32 runtime·ctrlhandler(uint32 type);
byte *runtime·compilecallback(Eface fn, bool cleanstack);
void *runtime·callbackasm(void);
+void runtime·install_exception_handler(void);
+void runtime·remove_exception_handler(void);
+
// TODO(brainman): should not need those
#define NSIG 65
diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c
new file mode 100644
index 000000000..2f553f417
--- /dev/null
+++ b/src/pkg/runtime/panic.c
@@ -0,0 +1,487 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "runtime.h"
+#include "arch_GOARCH.h"
+#include "stack.h"
+
+// Code related to defer, panic and recover.
+
+uint32 runtime·panicking;
+static Lock paniclk;
+
+enum
+{
+ DeferChunkSize = 2048
+};
+
+// Allocate a Defer, usually as part of the larger frame of deferred functions.
+// Each defer must be released with both popdefer and freedefer.
+static Defer*
+newdefer(int32 siz)
+{
+ int32 total;
+ DeferChunk *c;
+ Defer *d;
+
+ c = g->dchunk;
+ total = sizeof(*d) + ROUND(siz, sizeof(uintptr)) - sizeof(d->args);
+ if(c == nil || total > DeferChunkSize - c->off) {
+ if(total > DeferChunkSize / 2) {
+ // Not worth putting in any chunk.
+ // Allocate a separate block.
+ d = runtime·malloc(total);
+ d->siz = siz;
+ d->special = 1;
+ d->free = 1;
+ d->link = g->defer;
+ g->defer = d;
+ return d;
+ }
+
+ // Cannot fit in current chunk.
+ // Switch to next chunk, allocating if necessary.
+ c = g->dchunknext;
+ if(c == nil)
+ c = runtime·malloc(DeferChunkSize);
+ c->prev = g->dchunk;
+ c->off = sizeof(*c);
+ g->dchunk = c;
+ g->dchunknext = nil;
+ }
+
+ d = (Defer*)((byte*)c + c->off);
+ c->off += total;
+ d->siz = siz;
+ d->special = 0;
+ d->free = 0;
+ d->link = g->defer;
+ g->defer = d;
+ return d;
+}
+
+// Pop the current defer from the defer stack.
+// Its contents are still valid until the goroutine begins executing again.
+// In particular it is safe to call reflect.call(d->fn, d->argp, d->siz) after
+// popdefer returns.
+static void
+popdefer(void)
+{
+ Defer *d;
+ DeferChunk *c;
+ int32 total;
+
+ d = g->defer;
+ if(d == nil)
+ runtime·throw("runtime: popdefer nil");
+ g->defer = d->link;
+ if(d->special) {
+ // Nothing else to do.
+ return;
+ }
+ total = sizeof(*d) + ROUND(d->siz, sizeof(uintptr)) - sizeof(d->args);
+ c = g->dchunk;
+ if(c == nil || (byte*)d+total != (byte*)c+c->off)
+ runtime·throw("runtime: popdefer phase error");
+ c->off -= total;
+ if(c->off == sizeof(*c)) {
+ // Chunk now empty, so pop from stack.
+ // Save in dchunknext both to help with pingponging between frames
+ // and to make sure d is still valid on return.
+ if(g->dchunknext != nil)
+ runtime·free(g->dchunknext);
+ g->dchunknext = c;
+ g->dchunk = c->prev;
+ }
+}
+
+// Free the given defer.
+// For defers in the per-goroutine chunk this just clears the saved arguments.
+// For large defers allocated on the heap, this frees them.
+// The defer cannot be used after this call.
+static void
+freedefer(Defer *d)
+{
+ if(d->special) {
+ if(d->free)
+ runtime·free(d);
+ } else {
+ runtime·memclr((byte*)d->args, d->siz);
+ }
+}
+
+// Create a new deferred function fn with siz bytes of arguments.
+// The compiler turns a defer statement into a call to this.
+// Cannot split the stack because it assumes that the arguments
+// are available sequentially after &fn; they would not be
+// copied if a stack split occurred. It's OK for this to call
+// functions that split the stack.
+#pragma textflag 7
+uintptr
+runtime·deferproc(int32 siz, FuncVal *fn, ...)
+{
+ Defer *d;
+
+ d = newdefer(siz);
+ d->fn = fn;
+ d->pc = runtime·getcallerpc(&siz);
+ if(thechar == '5')
+ d->argp = (byte*)(&fn+2); // skip caller's saved link register
+ else
+ d->argp = (byte*)(&fn+1);
+ runtime·memmove(d->args, d->argp, d->siz);
+
+ // deferproc returns 0 normally.
+ // a deferred func that stops a panic
+ // makes the deferproc return 1.
+ // the code the compiler generates always
+ // checks the return value and jumps to the
+ // end of the function if deferproc returns != 0.
+ return 0;
+}
+
+// Run a deferred function if there is one.
+// The compiler inserts a call to this at the end of any
+// function which calls defer.
+// If there is a deferred function, this will call runtime·jmpdefer,
+// which will jump to the deferred function such that it appears
+// to have been called by the caller of deferreturn at the point
+// just before deferreturn was called. The effect is that deferreturn
+// is called again and again until there are no more deferred functions.
+// Cannot split the stack because we reuse the caller's frame to
+// call the deferred function.
+#pragma textflag 7
+void
+runtime·deferreturn(uintptr arg0)
+{
+ Defer *d;
+ byte *argp;
+ FuncVal *fn;
+
+ d = g->defer;
+ if(d == nil)
+ return;
+ argp = (byte*)&arg0;
+ if(d->argp != argp)
+ return;
+ runtime·memmove(argp, d->args, d->siz);
+ fn = d->fn;
+ popdefer();
+ freedefer(d);
+ runtime·jmpdefer(fn, argp);
+}
+
+// Run all deferred functions for the current goroutine.
+static void
+rundefer(void)
+{
+ Defer *d;
+
+ while((d = g->defer) != nil) {
+ popdefer();
+ reflect·call(d->fn, (byte*)d->args, d->siz);
+ freedefer(d);
+ }
+}
+
+// Print all currently active panics. Used when crashing.
+static void
+printpanics(Panic *p)
+{
+ if(p->link) {
+ printpanics(p->link);
+ runtime·printf("\t");
+ }
+ runtime·printf("panic: ");
+ runtime·printany(p->arg);
+ if(p->recovered)
+ runtime·printf(" [recovered]");
+ runtime·printf("\n");
+}
+
+static void recovery(G*);
+
+// The implementation of the predeclared function panic.
+void
+runtime·panic(Eface e)
+{
+ Defer *d;
+ Panic *p;
+ void *pc, *argp;
+
+ p = runtime·mal(sizeof *p);
+ p->arg = e;
+ p->link = g->panic;
+ p->stackbase = (byte*)g->stackbase;
+ g->panic = p;
+
+ for(;;) {
+ d = g->defer;
+ if(d == nil)
+ break;
+ // take defer off list in case of recursive panic
+ popdefer();
+ g->ispanic = true; // rock for newstack, where reflect.call ends up
+ argp = d->argp;
+ pc = d->pc;
+ reflect·call(d->fn, (byte*)d->args, d->siz);
+ freedefer(d);
+ if(p->recovered) {
+ g->panic = p->link;
+ if(g->panic == nil) // must be done with signal
+ g->sig = 0;
+ runtime·free(p);
+ // Pass information about recovering frame to recovery.
+ g->sigcode0 = (uintptr)argp;
+ g->sigcode1 = (uintptr)pc;
+ runtime·mcall(recovery);
+ runtime·throw("recovery failed"); // mcall should not return
+ }
+ }
+
+ // ran out of deferred calls - old-school panic now
+ runtime·startpanic();
+ printpanics(g->panic);
+ runtime·dopanic(0);
+}
+
+// Unwind the stack after a deferred function calls recover
+// after a panic. Then arrange to continue running as though
+// the caller of the deferred function returned normally.
+static void
+recovery(G *gp)
+{
+ void *argp;
+ void *pc;
+
+ // Info about defer passed in G struct.
+ argp = (void*)gp->sigcode0;
+ pc = (void*)gp->sigcode1;
+
+ // Unwind to the stack frame with d's arguments in it.
+ runtime·unwindstack(gp, argp);
+
+ // Make the deferproc for this d return again,
+ // this time returning 1. The calling function will
+ // jump to the standard return epilogue.
+ // The -2*sizeof(uintptr) makes up for the
+ // two extra words that are on the stack at
+ // each call to deferproc.
+ // (The pc we're returning to does pop pop
+ // before it tests the return value.)
+ // On the arm there are 2 saved LRs mixed in too.
+ if(thechar == '5')
+ gp->sched.sp = (uintptr)argp - 4*sizeof(uintptr);
+ else
+ gp->sched.sp = (uintptr)argp - 2*sizeof(uintptr);
+ gp->sched.pc = pc;
+ runtime·gogo(&gp->sched, 1);
+}
+
+// Free stack frames until we hit the last one
+// or until we find the one that contains the sp.
+void
+runtime·unwindstack(G *gp, byte *sp)
+{
+ Stktop *top;
+ byte *stk;
+
+ // Must be called from a different goroutine, usually m->g0.
+ if(g == gp)
+ runtime·throw("unwindstack on self");
+
+ while((top = (Stktop*)gp->stackbase) != nil && top->stackbase != nil) {
+ stk = (byte*)gp->stackguard - StackGuard;
+ if(stk <= sp && sp < (byte*)gp->stackbase)
+ break;
+ gp->stackbase = (uintptr)top->stackbase;
+ gp->stackguard = (uintptr)top->stackguard;
+ if(top->free != 0)
+ runtime·stackfree(stk, top->free);
+ }
+
+ if(sp != nil && (sp < (byte*)gp->stackguard - StackGuard || (byte*)gp->stackbase < sp)) {
+ runtime·printf("recover: %p not in [%p, %p]\n", sp, gp->stackguard - StackGuard, gp->stackbase);
+ runtime·throw("bad unwindstack");
+ }
+}
+
+// The implementation of the predeclared function recover.
+// Cannot split the stack because it needs to reliably
+// find the stack segment of its caller.
+#pragma textflag 7
+void
+runtime·recover(byte *argp, Eface ret)
+{
+ Stktop *top, *oldtop;
+ Panic *p;
+
+ // Must be a panic going on.
+ if((p = g->panic) == nil || p->recovered)
+ goto nomatch;
+
+ // Frame must be at the top of the stack segment,
+ // because each deferred call starts a new stack
+ // segment as a side effect of using reflect.call.
+ // (There has to be some way to remember the
+ // variable argument frame size, and the segment
+ // code already takes care of that for us, so we
+ // reuse it.)
+ //
+ // As usual closures complicate things: the fp that
+ // the closure implementation function claims to have
+ // is where the explicit arguments start, after the
+ // implicit pointer arguments and PC slot.
+ // If we're on the first new segment for a closure,
+ // then fp == top - top->args is correct, but if
+ // the closure has its own big argument frame and
+ // allocated a second segment (see below),
+ // the fp is slightly above top - top->args.
+ // That condition can't happen normally though
+ // (stack pointers go down, not up), so we can accept
+ // any fp between top and top - top->args as
+ // indicating the top of the segment.
+ top = (Stktop*)g->stackbase;
+ if(argp < (byte*)top - top->argsize || (byte*)top < argp)
+ goto nomatch;
+
+ // The deferred call makes a new segment big enough
+ // for the argument frame but not necessarily big
+ // enough for the function's local frame (size unknown
+ // at the time of the call), so the function might have
+ // made its own segment immediately. If that's the
+ // case, back top up to the older one, the one that
+ // reflect.call would have made for the panic.
+ //
+ // The fp comparison here checks that the argument
+ // frame that was copied during the split (the top->args
+ // bytes above top->fp) abuts the old top of stack.
+ // This is a correct test for both closure and non-closure code.
+ oldtop = (Stktop*)top->stackbase;
+ if(oldtop != nil && top->argp == (byte*)oldtop - top->argsize)
+ top = oldtop;
+
+ // Now we have the segment that was created to
+ // run this call. It must have been marked as a panic segment.
+ if(!top->panic)
+ goto nomatch;
+
+ // Okay, this is the top frame of a deferred call
+ // in response to a panic. It can see the panic argument.
+ p->recovered = 1;
+ ret = p->arg;
+ FLUSH(&ret);
+ return;
+
+nomatch:
+ ret.type = nil;
+ ret.data = nil;
+ FLUSH(&ret);
+}
+
+void
+runtime·startpanic(void)
+{
+ if(m->mcache == nil) // can happen if called from signal handler or throw
+ m->mcache = runtime·allocmcache();
+ if(m->dying) {
+ runtime·printf("panic during panic\n");
+ runtime·exit(3);
+ }
+ m->dying = 1;
+ runtime·xadd(&runtime·panicking, 1);
+ runtime·lock(&paniclk);
+}
+
+void
+runtime·dopanic(int32 unused)
+{
+ static bool didothers;
+
+ if(g->sig != 0)
+ runtime·printf("[signal %x code=%p addr=%p pc=%p]\n",
+ g->sig, g->sigcode0, g->sigcode1, g->sigpc);
+
+ if(runtime·gotraceback()){
+ if(g != m->g0) {
+ runtime·printf("\n");
+ runtime·goroutineheader(g);
+ runtime·traceback(runtime·getcallerpc(&unused), runtime·getcallersp(&unused), 0, g);
+ }
+ if(!didothers) {
+ didothers = true;
+ runtime·tracebackothers(g);
+ }
+ }
+ runtime·unlock(&paniclk);
+ if(runtime·xadd(&runtime·panicking, -1) != 0) {
+ // Some other m is panicking too.
+ // Let it print what it needs to print.
+ // Wait forever without chewing up cpu.
+ // It will exit when it's done.
+ static Lock deadlock;
+ runtime·lock(&deadlock);
+ runtime·lock(&deadlock);
+ }
+
+ runtime·exit(2);
+}
+
+void
+runtime·panicindex(void)
+{
+ runtime·panicstring("index out of range");
+}
+
+void
+runtime·panicslice(void)
+{
+ runtime·panicstring("slice bounds out of range");
+}
+
+void
+runtime·throwreturn(void)
+{
+ // can only happen if compiler is broken
+ runtime·throw("no return at end of a typed function - compiler is broken");
+}
+
+void
+runtime·throwinit(void)
+{
+ // can only happen with linker skew
+ runtime·throw("recursive call during initialization - linker skew");
+}
+
+void
+runtime·throw(int8 *s)
+{
+ if(m->throwing == 0)
+ m->throwing = 1;
+ runtime·startpanic();
+ runtime·printf("fatal error: %s\n", s);
+ runtime·dopanic(0);
+ *(int32*)0 = 0; // not reached
+ runtime·exit(1); // even more not reached
+}
+
+void
+runtime·panicstring(int8 *s)
+{
+ Eface err;
+
+ if(m->gcing) {
+ runtime·printf("panic: %s\n", s);
+ runtime·throw("panic during gc");
+ }
+ runtime·newErrorString(runtime·gostringnocopy((byte*)s), &err);
+ runtime·panic(err);
+}
+
+void
+runtime·Goexit(void)
+{
+ rundefer();
+ runtime·goexit();
+}
diff --git a/src/pkg/runtime/parfor.c b/src/pkg/runtime/parfor.c
new file mode 100644
index 000000000..aa5537d02
--- /dev/null
+++ b/src/pkg/runtime/parfor.c
@@ -0,0 +1,215 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Parallel for algorithm.
+
+#include "runtime.h"
+#include "arch_GOARCH.h"
+
+struct ParForThread
+{
+ // the thread's iteration space [32lsb, 32msb)
+ uint64 pos;
+ // stats
+ uint64 nsteal;
+ uint64 nstealcnt;
+ uint64 nprocyield;
+ uint64 nosyield;
+ uint64 nsleep;
+ byte pad[CacheLineSize];
+};
+
+ParFor*
+runtime·parforalloc(uint32 nthrmax)
+{
+ ParFor *desc;
+
+ // The ParFor object is followed by CacheLineSize padding
+ // and then nthrmax ParForThread.
+ desc = (ParFor*)runtime·malloc(sizeof(ParFor) + CacheLineSize + nthrmax * sizeof(ParForThread));
+ desc->thr = (ParForThread*)((byte*)(desc+1) + CacheLineSize);
+ desc->nthrmax = nthrmax;
+ return desc;
+}
+
+// For testing from Go
+// func parforalloc2(nthrmax uint32) *ParFor
+void
+runtime·parforalloc2(uint32 nthrmax, ParFor *desc)
+{
+ desc = runtime·parforalloc(nthrmax);
+ FLUSH(&desc);
+}
+
+void
+runtime·parforsetup(ParFor *desc, uint32 nthr, uint32 n, void *ctx, bool wait, void (*body)(ParFor*, uint32))
+{
+ uint32 i, begin, end;
+
+ if(desc == nil || nthr == 0 || nthr > desc->nthrmax || body == nil) {
+ runtime·printf("desc=%p nthr=%d count=%d body=%p\n", desc, nthr, n, body);
+ runtime·throw("parfor: invalid args");
+ }
+
+ desc->body = body;
+ desc->done = 0;
+ desc->nthr = nthr;
+ desc->thrseq = 0;
+ desc->cnt = n;
+ desc->ctx = ctx;
+ desc->wait = wait;
+ desc->nsteal = 0;
+ desc->nstealcnt = 0;
+ desc->nprocyield = 0;
+ desc->nosyield = 0;
+ desc->nsleep = 0;
+ for(i=0; i<nthr; i++) {
+ begin = (uint64)n*i / nthr;
+ end = (uint64)n*(i+1) / nthr;
+ desc->thr[i].pos = (uint64)begin | (((uint64)end)<<32);
+ }
+}
+
+// For testing from Go
+// func parforsetup2(desc *ParFor, nthr, n uint32, ctx *byte, wait bool, body func(*ParFor, uint32))
+void
+runtime·parforsetup2(ParFor *desc, uint32 nthr, uint32 n, void *ctx, bool wait, void *body)
+{
+ runtime·parforsetup(desc, nthr, n, ctx, wait, *(void(**)(ParFor*, uint32))body);
+}
+
+void
+runtime·parfordo(ParFor *desc)
+{
+ ParForThread *me;
+ uint32 tid, begin, end, begin2, try, victim, i;
+ uint64 *mypos, *victimpos, pos, newpos;
+ void (*body)(ParFor*, uint32);
+ bool idle;
+
+ // Obtain 0-based thread index.
+ tid = runtime·xadd(&desc->thrseq, 1) - 1;
+ if(tid >= desc->nthr) {
+ runtime·printf("tid=%d nthr=%d\n", tid, desc->nthr);
+ runtime·throw("parfor: invalid tid");
+ }
+
+ // If single-threaded, just execute the for serially.
+ if(desc->nthr==1) {
+ for(i=0; i<desc->cnt; i++)
+ desc->body(desc, i);
+ return;
+ }
+
+ body = desc->body;
+ me = &desc->thr[tid];
+ mypos = &me->pos;
+ for(;;) {
+ for(;;) {
+ // While there is local work,
+ // bump low index and execute the iteration.
+ pos = runtime·xadd64(mypos, 1);
+ begin = (uint32)pos-1;
+ end = (uint32)(pos>>32);
+ if(begin < end) {
+ body(desc, begin);
+ continue;
+ }
+ break;
+ }
+
+ // Out of work, need to steal something.
+ idle = false;
+ for(try=0;; try++) {
+ // If we don't see any work for long enough,
+ // increment the done counter...
+ if(try > desc->nthr*4 && !idle) {
+ idle = true;
+ runtime·xadd(&desc->done, 1);
+ }
+ // ...if all threads have incremented the counter,
+ // we are done.
+ if(desc->done + !idle == desc->nthr) {
+ if(!idle)
+ runtime·xadd(&desc->done, 1);
+ goto exit;
+ }
+ // Choose a random victim for stealing.
+ victim = runtime·fastrand1() % (desc->nthr-1);
+ if(victim >= tid)
+ victim++;
+ victimpos = &desc->thr[victim].pos;
+ pos = runtime·atomicload64(victimpos);
+ for(;;) {
+ // See if it has any work.
+ begin = (uint32)pos;
+ end = (uint32)(pos>>32);
+ if(begin+1 >= end) {
+ begin = end = 0;
+ break;
+ }
+ if(idle) {
+ runtime·xadd(&desc->done, -1);
+ idle = false;
+ }
+ begin2 = begin + (end-begin)/2;
+ newpos = (uint64)begin | (uint64)begin2<<32;
+ if(runtime·cas64(victimpos, &pos, newpos)) {
+ begin = begin2;
+ break;
+ }
+ }
+ if(begin < end) {
+ // Has successfully stolen some work.
+ if(idle)
+ runtime·throw("parfor: should not be idle");
+ runtime·atomicstore64(mypos, (uint64)begin | (uint64)end<<32);
+ me->nsteal++;
+ me->nstealcnt += end-begin;
+ break;
+ }
+ // Backoff.
+ if(try < desc->nthr) {
+ // nothing
+ } else if (try < 4*desc->nthr) {
+ me->nprocyield++;
+ runtime·procyield(20);
+ // If a caller asked not to wait for the others, exit now
+ // (assume that most work is already done at this point).
+ } else if (!desc->wait) {
+ if(!idle)
+ runtime·xadd(&desc->done, 1);
+ goto exit;
+ } else if (try < 6*desc->nthr) {
+ me->nosyield++;
+ runtime·osyield();
+ } else {
+ me->nsleep++;
+ runtime·usleep(1);
+ }
+ }
+ }
+exit:
+ runtime·xadd64(&desc->nsteal, me->nsteal);
+ runtime·xadd64(&desc->nstealcnt, me->nstealcnt);
+ runtime·xadd64(&desc->nprocyield, me->nprocyield);
+ runtime·xadd64(&desc->nosyield, me->nosyield);
+ runtime·xadd64(&desc->nsleep, me->nsleep);
+ me->nsteal = 0;
+ me->nstealcnt = 0;
+ me->nprocyield = 0;
+ me->nosyield = 0;
+ me->nsleep = 0;
+}
+
+// For testing from Go
+// func parforiters(desc *ParFor, tid uintptr) (uintptr, uintptr)
+void
+runtime·parforiters(ParFor *desc, uintptr tid, uintptr start, uintptr end)
+{
+ start = (uint32)desc->thr[tid].pos;
+ end = (uint32)(desc->thr[tid].pos>>32);
+ FLUSH(&start);
+ FLUSH(&end);
+}
diff --git a/src/pkg/runtime/parfor_test.go b/src/pkg/runtime/parfor_test.go
new file mode 100644
index 000000000..4c69a68ce
--- /dev/null
+++ b/src/pkg/runtime/parfor_test.go
@@ -0,0 +1,144 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The race detector does not understand ParFor synchronization.
+// +build !race
+
+package runtime_test
+
+import (
+ . "runtime"
+ "testing"
+ "unsafe"
+)
+
+var gdata []uint64
+
+// Simple serial sanity test for parallelfor.
+func TestParFor(t *testing.T) {
+ const P = 1
+ const N = 20
+ data := make([]uint64, N)
+ for i := uint64(0); i < N; i++ {
+ data[i] = i
+ }
+ desc := NewParFor(P)
+ // Avoid making func a closure: parfor cannot invoke them.
+ // Since it doesn't happen in the C code, it's not worth doing
+ // just for the test.
+ gdata = data
+ ParForSetup(desc, P, N, nil, true, func(desc *ParFor, i uint32) {
+ data := gdata
+ data[i] = data[i]*data[i] + 1
+ })
+ ParForDo(desc)
+ for i := uint64(0); i < N; i++ {
+ if data[i] != i*i+1 {
+ t.Fatalf("Wrong element %d: %d", i, data[i])
+ }
+ }
+}
+
+// Test that nonblocking parallelfor does not block.
+func TestParFor2(t *testing.T) {
+ const P = 7
+ const N = 1003
+ data := make([]uint64, N)
+ for i := uint64(0); i < N; i++ {
+ data[i] = i
+ }
+ desc := NewParFor(P)
+ ParForSetup(desc, P, N, (*byte)(unsafe.Pointer(&data)), false, func(desc *ParFor, i uint32) {
+ d := *(*[]uint64)(unsafe.Pointer(desc.Ctx))
+ d[i] = d[i]*d[i] + 1
+ })
+ for p := 0; p < P; p++ {
+ ParForDo(desc)
+ }
+ for i := uint64(0); i < N; i++ {
+ if data[i] != i*i+1 {
+ t.Fatalf("Wrong element %d: %d", i, data[i])
+ }
+ }
+}
+
+// Test that iterations are properly distributed.
+func TestParForSetup(t *testing.T) {
+ const P = 11
+ const N = 101
+ desc := NewParFor(P)
+ for n := uint32(0); n < N; n++ {
+ for p := uint32(1); p <= P; p++ {
+ ParForSetup(desc, p, n, nil, true, func(desc *ParFor, i uint32) {})
+ sum := uint32(0)
+ size0 := uint32(0)
+ end0 := uint32(0)
+ for i := uint32(0); i < p; i++ {
+ begin, end := ParForIters(desc, i)
+ size := end - begin
+ sum += size
+ if i == 0 {
+ size0 = size
+ if begin != 0 {
+ t.Fatalf("incorrect begin: %d (n=%d, p=%d)", begin, n, p)
+ }
+ } else {
+ if size != size0 && size != size0+1 {
+ t.Fatalf("incorrect size: %d/%d (n=%d, p=%d)", size, size0, n, p)
+ }
+ if begin != end0 {
+ t.Fatalf("incorrect begin/end: %d/%d (n=%d, p=%d)", begin, end0, n, p)
+ }
+ }
+ end0 = end
+ }
+ if sum != n {
+ t.Fatalf("incorrect sum: %d/%d (p=%d)", sum, n, p)
+ }
+ }
+ }
+}
+
+// Test parallel parallelfor.
+func TestParForParallel(t *testing.T) {
+ if GOARCH != "amd64" {
+ t.Log("temporarily disabled, see http://golang.org/issue/4155")
+ return
+ }
+
+ N := uint64(1e7)
+ if testing.Short() {
+ N /= 10
+ }
+ data := make([]uint64, N)
+ for i := uint64(0); i < N; i++ {
+ data[i] = i
+ }
+ P := GOMAXPROCS(-1)
+ c := make(chan bool, P)
+ desc := NewParFor(uint32(P))
+ gdata = data
+ ParForSetup(desc, uint32(P), uint32(N), nil, false, func(desc *ParFor, i uint32) {
+ data := gdata
+ data[i] = data[i]*data[i] + 1
+ })
+ for p := 1; p < P; p++ {
+ go func() {
+ ParForDo(desc)
+ c <- true
+ }()
+ }
+ ParForDo(desc)
+ for p := 1; p < P; p++ {
+ <-c
+ }
+ for i := uint64(0); i < N; i++ {
+ if data[i] != i*i+1 {
+ t.Fatalf("Wrong element %d: %d", i, data[i])
+ }
+ }
+
+ data, desc = nil, nil
+ GC()
+}
diff --git a/src/pkg/runtime/pprof/pprof.go b/src/pkg/runtime/pprof/pprof.go
index f67e8a8f9..32c1098b9 100644
--- a/src/pkg/runtime/pprof/pprof.go
+++ b/src/pkg/runtime/pprof/pprof.go
@@ -36,8 +36,9 @@ import (
// goroutine - stack traces of all current goroutines
// heap - a sampling of all heap allocations
// threadcreate - stack traces that led to the creation of new OS threads
+// block - stack traces that led to blocking on synchronization primitives
//
-// These predefine profiles maintain themselves and panic on an explicit
+// These predefined profiles maintain themselves and panic on an explicit
// Add or Remove method call.
//
// The CPU profile is not available as a Profile. It has a special API,
@@ -76,6 +77,12 @@ var heapProfile = &Profile{
write: writeHeap,
}
+var blockProfile = &Profile{
+ name: "block",
+ count: countBlock,
+ write: writeBlock,
+}
+
func lockProfiles() {
profiles.mu.Lock()
if profiles.m == nil {
@@ -84,6 +91,7 @@ func lockProfiles() {
"goroutine": goroutineProfile,
"threadcreate": threadcreateProfile,
"heap": heapProfile,
+ "block": blockProfile,
}
}
}
@@ -310,21 +318,33 @@ func printCountProfile(w io.Writer, debug int, name string, p countProfile) erro
// for a single stack trace.
func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) {
show := allFrames
- for _, pc := range stk {
+ wasPanic := false
+ for i, pc := range stk {
f := runtime.FuncForPC(pc)
if f == nil {
show = true
fmt.Fprintf(w, "#\t%#x\n", pc)
+ wasPanic = false
} else {
- file, line := f.FileLine(pc)
+ tracepc := pc
+ // Back up to call instruction.
+ if i > 0 && pc > f.Entry() && !wasPanic {
+ if runtime.GOARCH == "386" || runtime.GOARCH == "amd64" {
+ tracepc--
+ } else {
+ tracepc -= 4 // arm, etc
+ }
+ }
+ file, line := f.FileLine(tracepc)
name := f.Name()
// Hide runtime.goexit and any runtime functions at the beginning.
// This is useful mainly for allocation traces.
+ wasPanic = name == "runtime.panic"
if name == "runtime.goexit" || !show && strings.HasPrefix(name, "runtime.") {
continue
}
show = true
- fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", pc, f.Name(), pc-f.Entry(), file, line)
+ fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", pc, name, pc-f.Entry(), file, line)
}
}
if !show {
@@ -352,26 +372,26 @@ func WriteHeapProfile(w io.Writer) error {
// countHeap returns the number of records in the heap profile.
func countHeap() int {
- n, _ := runtime.MemProfile(nil, false)
+ n, _ := runtime.MemProfile(nil, true)
return n
}
-// writeHeapProfile writes the current runtime heap profile to w.
+// writeHeap writes the current runtime heap profile to w.
func writeHeap(w io.Writer, debug int) error {
- // Find out how many records there are (MemProfile(nil, false)),
+ // Find out how many records there are (MemProfile(nil, true)),
// allocate that many records, and get the data.
// There's a race—more records might be added between
// the two calls—so allocate a few extra records for safety
// and also try again if we're very unlucky.
// The loop should only execute one iteration in the common case.
var p []runtime.MemProfileRecord
- n, ok := runtime.MemProfile(nil, false)
+ n, ok := runtime.MemProfile(nil, true)
for {
// Allocate room for a slightly bigger profile,
// in case a few more entries have been added
// since the call to MemProfile.
p = make([]runtime.MemProfileRecord, n+50)
- n, ok = runtime.MemProfile(p, false)
+ n, ok = runtime.MemProfile(p, true)
if ok {
p = p[0:n]
break
@@ -431,11 +451,14 @@ func writeHeap(w io.Writer, debug int) error {
fmt.Fprintf(w, "# Sys = %d\n", s.Sys)
fmt.Fprintf(w, "# Lookups = %d\n", s.Lookups)
fmt.Fprintf(w, "# Mallocs = %d\n", s.Mallocs)
+ fmt.Fprintf(w, "# Frees = %d\n", s.Frees)
fmt.Fprintf(w, "# HeapAlloc = %d\n", s.HeapAlloc)
fmt.Fprintf(w, "# HeapSys = %d\n", s.HeapSys)
fmt.Fprintf(w, "# HeapIdle = %d\n", s.HeapIdle)
fmt.Fprintf(w, "# HeapInuse = %d\n", s.HeapInuse)
+ fmt.Fprintf(w, "# HeapReleased = %d\n", s.HeapReleased)
+ fmt.Fprintf(w, "# HeapObjects = %d\n", s.HeapObjects)
fmt.Fprintf(w, "# Stack = %d / %d\n", s.StackInuse, s.StackSys)
fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys)
@@ -597,3 +620,60 @@ func StopCPUProfile() {
runtime.SetCPUProfileRate(0)
<-cpu.done
}
+
+type byCycles []runtime.BlockProfileRecord
+
+func (x byCycles) Len() int { return len(x) }
+func (x byCycles) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x byCycles) Less(i, j int) bool { return x[i].Cycles > x[j].Cycles }
+
+// countBlock returns the number of records in the blocking profile.
+func countBlock() int {
+ n, _ := runtime.BlockProfile(nil)
+ return n
+}
+
+// writeBlock writes the current blocking profile to w.
+func writeBlock(w io.Writer, debug int) error {
+ var p []runtime.BlockProfileRecord
+ n, ok := runtime.BlockProfile(nil)
+ for {
+ p = make([]runtime.BlockProfileRecord, n+50)
+ n, ok = runtime.BlockProfile(p)
+ if ok {
+ p = p[:n]
+ break
+ }
+ }
+
+ sort.Sort(byCycles(p))
+
+ b := bufio.NewWriter(w)
+ var tw *tabwriter.Writer
+ w = b
+ if debug > 0 {
+ tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
+ w = tw
+ }
+
+ fmt.Fprintf(w, "--- contention:\n")
+ fmt.Fprintf(w, "cycles/second=%v\n", runtime_cyclesPerSecond())
+ for i := range p {
+ r := &p[i]
+ fmt.Fprintf(w, "%v %v @", r.Cycles, r.Count)
+ for _, pc := range r.Stack() {
+ fmt.Fprintf(w, " %#x", pc)
+ }
+ fmt.Fprint(w, "\n")
+ if debug > 0 {
+ printStackRecord(w, r.Stack(), false)
+ }
+ }
+
+ if tw != nil {
+ tw.Flush()
+ }
+ return b.Flush()
+}
+
+func runtime_cyclesPerSecond() int64
diff --git a/src/pkg/runtime/pprof/pprof_test.go b/src/pkg/runtime/pprof/pprof_test.go
index 82bb2a292..6d5764f4a 100644
--- a/src/pkg/runtime/pprof/pprof_test.go
+++ b/src/pkg/runtime/pprof/pprof_test.go
@@ -26,8 +26,7 @@ func TestCPUProfile(t *testing.T) {
t.Logf("uname -a: %v", vers)
// Lion uses "Darwin Kernel Version 11".
if strings.Contains(vers, "Darwin Kernel Version 10") && strings.Contains(vers, "RELEASE_X86_64") {
- t.Logf("skipping test on known-broken kernel (64-bit Leopard / Snow Leopard)")
- return
+ t.Skip("skipping test on known-broken kernel (64-bit Leopard / Snow Leopard)")
}
case "plan9":
// unimplemented
@@ -49,19 +48,25 @@ func TestCPUProfile(t *testing.T) {
// Convert []byte to []uintptr.
bytes := prof.Bytes()
+ l := len(bytes) / int(unsafe.Sizeof(uintptr(0)))
val := *(*[]uintptr)(unsafe.Pointer(&bytes))
- val = val[:len(bytes)/int(unsafe.Sizeof(uintptr(0)))]
+ val = val[:l]
- if len(val) < 10 {
+ if l < 13 {
t.Fatalf("profile too short: %#x", val)
}
- if val[0] != 0 || val[1] != 3 || val[2] != 0 || val[3] != 1e6/100 || val[4] != 0 {
- t.Fatalf("unexpected header %#x", val[:5])
+
+ hd, val, tl := val[:5], val[5:l-3], val[l-3:]
+ if hd[0] != 0 || hd[1] != 3 || hd[2] != 0 || hd[3] != 1e6/100 || hd[4] != 0 {
+ t.Fatalf("unexpected header %#x", hd)
+ }
+
+ if tl[0] != 0 || tl[1] != 1 || tl[2] != 0 {
+ t.Fatalf("malformed end-of-data marker %#x", tl)
}
// Check that profile is well formed and contains ChecksumIEEE.
found := false
- val = val[5:]
for len(val) > 0 {
if len(val) < 2 || val[0] < 1 || val[1] < 1 || uintptr(len(val)) < 2+val[1] {
t.Fatalf("malformed profile. leftover: %#x", val)
diff --git a/src/pkg/runtime/print.c b/src/pkg/runtime/print.c
index 6702c3cde..5b601599b 100644
--- a/src/pkg/runtime/print.c
+++ b/src/pkg/runtime/print.c
@@ -18,10 +18,10 @@ gwrite(void *v, int32 n)
runtime·write(2, v, n);
return;
}
-
+
if(g->writenbuf == 0)
return;
-
+
if(n > g->writenbuf)
n = g->writenbuf;
runtime·memmove(g->writebuf, v, n);
@@ -84,40 +84,41 @@ vprintf(int8 *s, byte *base)
narg = 0;
switch(*p) {
case 't':
+ case 'c':
narg = arg + 1;
break;
case 'd': // 32-bit
case 'x':
- arg = runtime·rnd(arg, 4);
+ arg = ROUND(arg, 4);
narg = arg + 4;
break;
case 'D': // 64-bit
case 'U':
case 'X':
case 'f':
- arg = runtime·rnd(arg, sizeof(uintptr));
+ arg = ROUND(arg, sizeof(uintptr));
narg = arg + 8;
break;
case 'C':
- arg = runtime·rnd(arg, sizeof(uintptr));
+ arg = ROUND(arg, sizeof(uintptr));
narg = arg + 16;
break;
case 'p': // pointer-sized
case 's':
- arg = runtime·rnd(arg, sizeof(uintptr));
+ arg = ROUND(arg, sizeof(uintptr));
narg = arg + sizeof(uintptr);
break;
case 'S': // pointer-aligned but bigger
- arg = runtime·rnd(arg, sizeof(uintptr));
+ arg = ROUND(arg, sizeof(uintptr));
narg = arg + sizeof(String);
break;
case 'a': // pointer-aligned but bigger
- arg = runtime·rnd(arg, sizeof(uintptr));
+ arg = ROUND(arg, sizeof(uintptr));
narg = arg + sizeof(Slice);
break;
case 'i': // pointer-aligned but bigger
case 'e':
- arg = runtime·rnd(arg, sizeof(uintptr));
+ arg = ROUND(arg, sizeof(uintptr));
narg = arg + sizeof(Eface);
break;
}
@@ -126,6 +127,9 @@ vprintf(int8 *s, byte *base)
case 'a':
runtime·printslice(*(Slice*)v);
break;
+ case 'c':
+ runtime·printbyte(*(int8*)v);
+ break;
case 'd':
runtime·printint(*(int32*)v);
break;
@@ -203,21 +207,27 @@ runtime·printbool(bool v)
}
void
+runtime·printbyte(int8 c)
+{
+ gwrite(&c, 1);
+}
+
+void
runtime·printfloat(float64 v)
{
byte buf[20];
int32 e, s, i, n;
float64 h;
- if(runtime·isNaN(v)) {
+ if(ISNAN(v)) {
gwrite("NaN", 3);
return;
}
- if(runtime·isInf(v, 1)) {
+ if(v == runtime·posinf) {
gwrite("+Inf", 4);
return;
}
- if(runtime·isInf(v, -1)) {
+ if(v == runtime·neginf) {
gwrite("-Inf", 4);
return;
}
@@ -343,7 +353,7 @@ runtime·printstring(String v)
extern uint32 runtime·maxstring;
if(v.len > runtime·maxstring) {
- gwrite("[invalid string]", 16);
+ gwrite("[string too long]", 17);
return;
}
if(v.len > 0)
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 04a992628..4ce0a718c 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -4,169 +4,112 @@
#include "runtime.h"
#include "arch_GOARCH.h"
-#include "defs_GOOS_GOARCH.h"
#include "malloc.h"
-#include "os_GOOS.h"
#include "stack.h"
+#include "race.h"
+#include "type.h"
-bool runtime·iscgo;
-
-static void unwindstack(G*, byte*);
-static void schedule(G*);
-
-typedef struct Sched Sched;
-
-M runtime·m0;
-G runtime·g0; // idle goroutine for m0
-
-static int32 debug = 0;
-
-int32 runtime·gcwaiting;
-
-// Go scheduler
-//
-// The go scheduler's job is to match ready-to-run goroutines (`g's)
-// with waiting-for-work schedulers (`m's). If there are ready g's
-// and no waiting m's, ready() will start a new m running in a new
-// OS thread, so that all ready g's can run simultaneously, up to a limit.
-// For now, m's never go away.
-//
-// By default, Go keeps only one kernel thread (m) running user code
-// at a single time; other threads may be blocked in the operating system.
-// Setting the environment variable $GOMAXPROCS or calling
-// runtime.GOMAXPROCS() will change the number of user threads
-// allowed to execute simultaneously. $GOMAXPROCS is thus an
-// approximation of the maximum number of cores to use.
+// Goroutine scheduler
+// The scheduler's job is to distribute ready-to-run goroutines over worker threads.
//
-// Even a program that can run without deadlock in a single process
-// might use more m's if given the chance. For example, the prime
-// sieve will use as many m's as there are primes (up to runtime·sched.mmax),
-// allowing different stages of the pipeline to execute in parallel.
-// We could revisit this choice, only kicking off new m's for blocking
-// system calls, but that would limit the amount of parallel computation
-// that go would try to do.
+// The main concepts are:
+// G - goroutine.
+// M - worker thread, or machine.
+// P - processor, a resource that is required to execute Go code.
+// M must have an associated P to execute Go code, however it can be
+// blocked or in a syscall w/o an associated P.
//
-// In general, one could imagine all sorts of refinements to the
-// scheduler, but the goal now is just to get something working on
-// Linux and OS X.
+// Design doc at http://golang.org/s/go11sched.
+typedef struct Sched Sched;
struct Sched {
Lock;
- G *gfree; // available g's (status == Gdead)
- int32 goidgen;
+ uint64 goidgen;
- G *ghead; // g's waiting to run
- G *gtail;
- int32 gwait; // number of g's waiting to run
- int32 gcount; // number of g's that are alive
- int32 grunning; // number of g's running on cpu or in syscall
+ M* midle; // idle m's waiting for work
+ int32 nmidle; // number of idle m's waiting for work
+ int32 mlocked; // number of locked m's waiting for work
+ int32 mcount; // number of m's that have been created
- M *mhead; // m's waiting for work
- int32 mwait; // number of m's waiting for work
- int32 mcount; // number of m's that have been created
+ P* pidle; // idle P's
+ uint32 npidle;
+ uint32 nmspinning;
- volatile uint32 atomic; // atomic scheduling word (see below)
+ // Global runnable queue.
+ G* runqhead;
+ G* runqtail;
+ int32 runqsize;
- int32 profilehz; // cpu profiling rate
+ // Global cache of dead G's.
+ Lock gflock;
+ G* gfree;
- bool init; // running initialization
- bool lockmain; // init called runtime.LockOSThread
+ int32 stopwait;
+ Note stopnote;
+ uint32 sysmonwait;
+ Note sysmonnote;
- Note stopped; // one g can set waitstop and wait here for m's to stop
+ int32 profilehz; // cpu profiling rate
};
-// The atomic word in sched is an atomic uint32 that
-// holds these fields.
-//
-// [15 bits] mcpu number of m's executing on cpu
-// [15 bits] mcpumax max number of m's allowed on cpu
-// [1 bit] waitstop some g is waiting on stopped
-// [1 bit] gwaiting gwait != 0
-//
-// These fields are the information needed by entersyscall
-// and exitsyscall to decide whether to coordinate with the
-// scheduler. Packing them into a single machine word lets
-// them use a fast path with a single atomic read/write and
-// no lock/unlock. This greatly reduces contention in
-// syscall- or cgo-heavy multithreaded programs.
-//
-// Except for entersyscall and exitsyscall, the manipulations
-// to these fields only happen while holding the schedlock,
-// so the routines holding schedlock only need to worry about
-// what entersyscall and exitsyscall do, not the other routines
-// (which also use the schedlock).
-//
-// In particular, entersyscall and exitsyscall only read mcpumax,
-// waitstop, and gwaiting. They never write them. Thus, writes to those
-// fields can be done (holding schedlock) without fear of write conflicts.
-// There may still be logic conflicts: for example, the set of waitstop must
-// be conditioned on mcpu >= mcpumax or else the wait may be a
-// spurious sleep. The Promela model in proc.p verifies these accesses.
-enum {
- mcpuWidth = 15,
- mcpuMask = (1<<mcpuWidth) - 1,
- mcpuShift = 0,
- mcpumaxShift = mcpuShift + mcpuWidth,
- waitstopShift = mcpumaxShift + mcpuWidth,
- gwaitingShift = waitstopShift+1,
-
- // The max value of GOMAXPROCS is constrained
- // by the max value we can store in the bit fields
- // of the atomic word. Reserve a few high values
- // so that we can detect accidental decrement
- // beyond zero.
- maxgomaxprocs = mcpuMask - 10,
-};
-
-#define atomic_mcpu(v) (((v)>>mcpuShift)&mcpuMask)
-#define atomic_mcpumax(v) (((v)>>mcpumaxShift)&mcpuMask)
-#define atomic_waitstop(v) (((v)>>waitstopShift)&1)
-#define atomic_gwaiting(v) (((v)>>gwaitingShift)&1)
-
-Sched runtime·sched;
-int32 runtime·gomaxprocs;
-bool runtime·singleproc;
-
-static bool canaddmcpu(void);
-
-// An m that is waiting for notewakeup(&m->havenextg). This may
-// only be accessed while the scheduler lock is held. This is used to
-// minimize the number of times we call notewakeup while the scheduler
-// lock is held, since the m will normally move quickly to lock the
-// scheduler itself, producing lock contention.
-static M* mwakeup;
-
-// Scheduling helpers. Sched must be locked.
-static void gput(G*); // put/get on ghead/gtail
-static G* gget(void);
-static void mput(M*); // put/get on mhead
-static M* mget(G*);
-static void gfput(G*); // put/get on gfree
-static G* gfget(void);
-static void matchmg(void); // match m's to g's
-static void readylocked(G*); // ready, but sched is locked
-static void mnextg(M*, G*);
-static void mcommoninit(M*);
-
-void
-setmcpumax(uint32 n)
-{
- uint32 v, w;
-
- for(;;) {
- v = runtime·sched.atomic;
- w = v;
- w &= ~(mcpuMask<<mcpumaxShift);
- w |= n<<mcpumaxShift;
- if(runtime·cas(&runtime·sched.atomic, v, w))
- break;
- }
-}
+// The max value of GOMAXPROCS.
+// There are no fundamental restrictions on the value.
+enum { MaxGomaxprocs = 1<<8 };
+Sched runtime·sched;
+int32 runtime·gomaxprocs;
+bool runtime·singleproc;
+bool runtime·iscgo;
+uint32 runtime·gcwaiting;
+M runtime·m0;
+G runtime·g0; // idle goroutine for m0
+G* runtime·allg;
+G* runtime·lastg;
+M* runtime·allm;
+M* runtime·extram;
+int8* runtime·goos;
+int32 runtime·ncpu;
+static int32 newprocs;
// Keep trace of scavenger's goroutine for deadlock detection.
static G *scvg;
+void runtime·mstart(void);
+static void runqput(P*, G*);
+static G* runqget(P*);
+static void runqgrow(P*);
+static G* runqsteal(P*, P*);
+static void mput(M*);
+static M* mget(void);
+static void mcommoninit(M*);
+static void schedule(void);
+static void procresize(int32);
+static void acquirep(P*);
+static P* releasep(void);
+static void newm(void(*)(void), P*);
+static void goidle(void);
+static void stopm(void);
+static void startm(P*, bool);
+static void handoffp(P*);
+static void wakep(void);
+static void stoplockedm(void);
+static void startlockedm(G*);
+static void sysmon(void);
+static uint32 retake(uint32*);
+static void inclocked(int32);
+static void checkdead(void);
+static void exitsyscall0(G*);
+static void park0(G*);
+static void gosched0(G*);
+static void goexit0(G*);
+static void gfput(P*, G*);
+static G* gfget(P*);
+static void gfpurge(P*);
+static void globrunqput(G*);
+static G* globrunqget(P*);
+static P* pidleget(void);
+static void pidleput(P*);
+
// The bootstrap sequence is:
//
// call osinit
@@ -178,10 +121,11 @@ static G *scvg;
void
runtime·schedinit(void)
{
- int32 n;
+ int32 n, procs;
byte *p;
m->nomemprof++;
+ runtime·mprofinit();
runtime·mallocinit();
mcommoninit(m);
@@ -193,93 +137,70 @@ runtime·schedinit(void)
// so that we don't need to call malloc when we crash.
// runtime·findfunc(0);
- runtime·gomaxprocs = 1;
+ procs = 1;
p = runtime·getenv("GOMAXPROCS");
- if(p != nil && (n = runtime·atoi(p)) != 0) {
- if(n > maxgomaxprocs)
- n = maxgomaxprocs;
- runtime·gomaxprocs = n;
+ if(p != nil && (n = runtime·atoi(p)) > 0) {
+ if(n > MaxGomaxprocs)
+ n = MaxGomaxprocs;
+ procs = n;
}
- // wait for the main goroutine to start before taking
- // GOMAXPROCS into account.
- setmcpumax(1);
- runtime·singleproc = runtime·gomaxprocs == 1;
-
- canaddmcpu(); // mcpu++ to account for bootstrap m
- m->helpgc = 1; // flag to tell schedule() to mcpu--
- runtime·sched.grunning++;
+ runtime·allp = runtime·malloc((MaxGomaxprocs+1)*sizeof(runtime·allp[0]));
+ procresize(procs);
mstats.enablegc = 1;
m->nomemprof--;
+
+ if(raceenabled)
+ g->racectx = runtime·raceinit();
}
extern void main·init(void);
extern void main·main(void);
+static FuncVal scavenger = {runtime·MHeap_Scavenger};
+
// The main goroutine.
void
runtime·main(void)
{
+ newm(sysmon, nil);
+
// Lock the main goroutine onto this, the main OS thread,
// during initialization. Most programs won't care, but a few
// do require certain calls to be made by the main thread.
// Those can arrange for main.main to run in the main thread
// by calling runtime.LockOSThread during initialization
// to preserve the lock.
- runtime·LockOSThread();
- // From now on, newgoroutines may use non-main threads.
- setmcpumax(runtime·gomaxprocs);
- runtime·sched.init = true;
- scvg = runtime·newproc1((byte*)runtime·MHeap_Scavenger, nil, 0, 0, runtime·main);
+ runtime·lockOSThread();
+ if(m != &runtime·m0)
+ runtime·throw("runtime·main not on m0");
+ scvg = runtime·newproc1(&scavenger, nil, 0, 0, runtime·main);
+ scvg->issystem = true;
main·init();
- runtime·sched.init = false;
- if(!runtime·sched.lockmain)
- runtime·UnlockOSThread();
-
- // The deadlock detection has false negatives.
- // Let scvg start up, to eliminate the false negative
- // for the trivial program func main() { select{} }.
- runtime·gosched();
+ runtime·unlockOSThread();
main·main();
+ if(raceenabled)
+ runtime·racefini();
+
+ // Make racy client program work: if panicking on
+ // another goroutine at the same time as main returns,
+ // let the other goroutine finish printing the panic trace.
+ // Once it does, it will exit. See issue 3934.
+ if(runtime·panicking)
+ runtime·park(nil, nil, "panicwait");
+
runtime·exit(0);
for(;;)
*(int32*)runtime·main = 0;
}
-// Lock the scheduler.
-static void
-schedlock(void)
-{
- runtime·lock(&runtime·sched);
-}
-
-// Unlock the scheduler.
-static void
-schedunlock(void)
-{
- M *m;
-
- m = mwakeup;
- mwakeup = nil;
- runtime·unlock(&runtime·sched);
- if(m != nil)
- runtime·notewakeup(&m->havenextg);
-}
-
-void
-runtime·goexit(void)
-{
- g->status = Gmoribund;
- runtime·gosched();
-}
-
void
-runtime·goroutineheader(G *g)
+runtime·goroutineheader(G *gp)
{
int8 *status;
- switch(g->status) {
+ switch(gp->status) {
case Gidle:
status = "idle";
break;
@@ -293,609 +214,748 @@ runtime·goroutineheader(G *g)
status = "syscall";
break;
case Gwaiting:
- if(g->waitreason)
- status = g->waitreason;
+ if(gp->waitreason)
+ status = gp->waitreason;
else
status = "waiting";
break;
- case Gmoribund:
- status = "moribund";
- break;
default:
status = "???";
break;
}
- runtime·printf("goroutine %d [%s]:\n", g->goid, status);
+ runtime·printf("goroutine %D [%s]:\n", gp->goid, status);
}
void
runtime·tracebackothers(G *me)
{
- G *g;
+ G *gp;
+ int32 traceback;
- for(g = runtime·allg; g != nil; g = g->alllink) {
- if(g == me || g->status == Gdead)
+ traceback = runtime·gotraceback();
+ for(gp = runtime·allg; gp != nil; gp = gp->alllink) {
+ if(gp == me || gp->status == Gdead)
+ continue;
+ if(gp->issystem && traceback < 2)
continue;
runtime·printf("\n");
- runtime·goroutineheader(g);
- runtime·traceback(g->sched.pc, g->sched.sp, 0, g);
+ runtime·goroutineheader(gp);
+ runtime·traceback(gp->sched.pc, (byte*)gp->sched.sp, 0, gp);
}
}
-// Mark this g as m's idle goroutine.
-// This functionality might be used in environments where programs
-// are limited to a single thread, to simulate a select-driven
-// network server. It is not exposed via the standard runtime API.
-void
-runtime·idlegoroutine(void)
-{
- if(g->idlem != nil)
- runtime·throw("g is already an idle goroutine");
- g->idlem = m;
-}
-
static void
-mcommoninit(M *m)
+mcommoninit(M *mp)
{
- m->id = runtime·sched.mcount++;
- m->fastrand = 0x49f6428aUL + m->id + runtime·cputicks();
- m->stackalloc = runtime·malloc(sizeof(*m->stackalloc));
- runtime·FixAlloc_Init(m->stackalloc, FixedStack, runtime·SysAlloc, nil, nil);
+ // If there is no mcache runtime·callers() will crash,
+ // and we are most likely in sysmon thread so the stack is senseless anyway.
+ if(m->mcache)
+ runtime·callers(1, mp->createstack, nelem(mp->createstack));
- if(m->mcache == nil)
- m->mcache = runtime·allocmcache();
+ mp->fastrand = 0x49f6428aUL + mp->id + runtime·cputicks();
- runtime·callers(1, m->createstack, nelem(m->createstack));
+ runtime·lock(&runtime·sched);
+ mp->id = runtime·sched.mcount++;
+
+ runtime·mpreinit(mp);
// Add to runtime·allm so garbage collector doesn't free m
// when it is just in a register or thread-local storage.
- m->alllink = runtime·allm;
+ mp->alllink = runtime·allm;
// runtime·NumCgoCall() iterates over allm w/o schedlock,
// so we need to publish it safely.
- runtime·atomicstorep(&runtime·allm, m);
+ runtime·atomicstorep(&runtime·allm, mp);
+ runtime·unlock(&runtime·sched);
}
-// Try to increment mcpu. Report whether succeeded.
-static bool
-canaddmcpu(void)
+// Mark gp ready to run.
+void
+runtime·ready(G *gp)
{
- uint32 v;
-
- for(;;) {
- v = runtime·sched.atomic;
- if(atomic_mcpu(v) >= atomic_mcpumax(v))
- return 0;
- if(runtime·cas(&runtime·sched.atomic, v, v+(1<<mcpuShift)))
- return 1;
+ // Mark runnable.
+ if(gp->status != Gwaiting) {
+ runtime·printf("goroutine %D has status %d\n", gp->goid, gp->status);
+ runtime·throw("bad g->status in ready");
}
+ gp->status = Grunnable;
+ runqput(m->p, gp);
+ if(runtime·atomicload(&runtime·sched.npidle) != 0 && runtime·atomicload(&runtime·sched.nmspinning) == 0) // TODO: fast atomic
+ wakep();
}
-// Put on `g' queue. Sched must be locked.
-static void
-gput(G *g)
+int32
+runtime·gcprocs(void)
{
- M *m;
-
- // If g is wired, hand it off directly.
- if((m = g->lockedm) != nil && canaddmcpu()) {
- mnextg(m, g);
- return;
- }
+ int32 n;
- // If g is the idle goroutine for an m, hand it off.
- if(g->idlem != nil) {
- if(g->idlem->idleg != nil) {
- runtime·printf("m%d idle out of sync: g%d g%d\n",
- g->idlem->id,
- g->idlem->idleg->goid, g->goid);
- runtime·throw("runtime: double idle");
- }
- g->idlem->idleg = g;
- return;
- }
+ // Figure out how many CPUs to use during GC.
+ // Limited by gomaxprocs, number of actual CPUs, and MaxGcproc.
+ runtime·lock(&runtime·sched);
+ n = runtime·gomaxprocs;
+ if(n > runtime·ncpu)
+ n = runtime·ncpu;
+ if(n > MaxGcproc)
+ n = MaxGcproc;
+ if(n > runtime·sched.nmidle+1) // one M is currently running
+ n = runtime·sched.nmidle+1;
+ runtime·unlock(&runtime·sched);
+ return n;
+}
- g->schedlink = nil;
- if(runtime·sched.ghead == nil)
- runtime·sched.ghead = g;
- else
- runtime·sched.gtail->schedlink = g;
- runtime·sched.gtail = g;
+static bool
+needaddgcproc(void)
+{
+ int32 n;
- // increment gwait.
- // if it transitions to nonzero, set atomic gwaiting bit.
- if(runtime·sched.gwait++ == 0)
- runtime·xadd(&runtime·sched.atomic, 1<<gwaitingShift);
+ runtime·lock(&runtime·sched);
+ n = runtime·gomaxprocs;
+ if(n > runtime·ncpu)
+ n = runtime·ncpu;
+ if(n > MaxGcproc)
+ n = MaxGcproc;
+ n -= runtime·sched.nmidle+1; // one M is currently running
+ runtime·unlock(&runtime·sched);
+ return n > 0;
}
-// Report whether gget would return something.
-static bool
-haveg(void)
+void
+runtime·helpgc(int32 nproc)
{
- return runtime·sched.ghead != nil || m->idleg != nil;
+ M *mp;
+ int32 n, pos;
+
+ runtime·lock(&runtime·sched);
+ pos = 0;
+ for(n = 1; n < nproc; n++) { // one M is currently running
+ if(runtime·allp[pos]->mcache == m->mcache)
+ pos++;
+ mp = mget();
+ if(mp == nil)
+ runtime·throw("runtime·gcprocs inconsistency");
+ mp->helpgc = 1;
+ mp->mcache = runtime·allp[pos]->mcache;
+ pos++;
+ runtime·notewakeup(&mp->park);
+ }
+ runtime·unlock(&runtime·sched);
}
-// Get from `g' queue. Sched must be locked.
-static G*
-gget(void)
+void
+runtime·stoptheworld(void)
{
- G *g;
+ int32 i;
+ uint32 s;
+ P *p;
+ bool wait;
- g = runtime·sched.ghead;
- if(g){
- runtime·sched.ghead = g->schedlink;
- if(runtime·sched.ghead == nil)
- runtime·sched.gtail = nil;
- // decrement gwait.
- // if it transitions to zero, clear atomic gwaiting bit.
- if(--runtime·sched.gwait == 0)
- runtime·xadd(&runtime·sched.atomic, -1<<gwaitingShift);
- } else if(m->idleg != nil) {
- g = m->idleg;
- m->idleg = nil;
+ runtime·lock(&runtime·sched);
+ runtime·sched.stopwait = runtime·gomaxprocs;
+ runtime·atomicstore((uint32*)&runtime·gcwaiting, 1);
+ // stop current P
+ m->p->status = Pgcstop;
+ runtime·sched.stopwait--;
+ // try to retake all P's in Psyscall status
+ for(i = 0; i < runtime·gomaxprocs; i++) {
+ p = runtime·allp[i];
+ s = p->status;
+ if(s == Psyscall && runtime·cas(&p->status, s, Pgcstop))
+ runtime·sched.stopwait--;
+ }
+ // stop idle P's
+ while(p = pidleget()) {
+ p->status = Pgcstop;
+ runtime·sched.stopwait--;
+ }
+ wait = runtime·sched.stopwait > 0;
+ runtime·unlock(&runtime·sched);
+
+ // wait for remaining P's to stop voluntary
+ if(wait) {
+ runtime·notesleep(&runtime·sched.stopnote);
+ runtime·noteclear(&runtime·sched.stopnote);
+ }
+ if(runtime·sched.stopwait)
+ runtime·throw("stoptheworld: not stopped");
+ for(i = 0; i < runtime·gomaxprocs; i++) {
+ p = runtime·allp[i];
+ if(p->status != Pgcstop)
+ runtime·throw("stoptheworld: not stopped");
}
- return g;
}
-// Put on `m' list. Sched must be locked.
static void
-mput(M *m)
+mhelpgc(void)
{
- m->schedlink = runtime·sched.mhead;
- runtime·sched.mhead = m;
- runtime·sched.mwait++;
+ m->helpgc = 1;
}
-// Get an `m' to run `g'. Sched must be locked.
-static M*
-mget(G *g)
+void
+runtime·starttheworld(void)
{
- M *m;
+ P *p;
+ M *mp;
+ bool add;
- // if g has its own m, use it.
- if(g && (m = g->lockedm) != nil)
- return m;
+ add = needaddgcproc();
+ runtime·lock(&runtime·sched);
+ if(newprocs) {
+ procresize(newprocs);
+ newprocs = 0;
+ } else
+ procresize(runtime·gomaxprocs);
+ runtime·gcwaiting = 0;
- // otherwise use general m pool.
- if((m = runtime·sched.mhead) != nil){
- runtime·sched.mhead = m->schedlink;
- runtime·sched.mwait--;
+ while(p = pidleget()) {
+ // procresize() puts p's with work at the beginning of the list.
+ // Once we reach a p without a run queue, the rest don't have one either.
+ if(p->runqhead == p->runqtail) {
+ pidleput(p);
+ break;
+ }
+ mp = mget();
+ if(mp == nil) {
+ pidleput(p);
+ break;
+ }
+ if(mp->nextp)
+ runtime·throw("starttheworld: inconsistent mp->nextp");
+ mp->nextp = p;
+ runtime·notewakeup(&mp->park);
+ }
+ if(runtime·sched.sysmonwait) {
+ runtime·sched.sysmonwait = false;
+ runtime·notewakeup(&runtime·sched.sysmonnote);
+ }
+ runtime·unlock(&runtime·sched);
+
+ if(add) {
+ // If GC could have used another helper proc, start one now,
+ // in the hope that it will be available next time.
+ // It would have been even better to start it before the collection,
+ // but doing so requires allocating memory, so it's tricky to
+ // coordinate. This lazy approach works out in practice:
+ // we don't mind if the first couple gc rounds don't have quite
+ // the maximum number of procs.
+ newm(mhelpgc, nil);
}
- return m;
}
-// Mark g ready to run.
+// Called to start an M.
void
-runtime·ready(G *g)
+runtime·mstart(void)
{
- schedlock();
- readylocked(g);
- schedunlock();
-}
+ // It is used by windows-386 only. Unfortunately, seh needs
+ // to be located on os stack, and mstart runs on os stack
+ // for both m0 and m.
+ SEH seh;
-// Mark g ready to run. Sched is already locked.
-// G might be running already and about to stop.
-// The sched lock protects g->status from changing underfoot.
-static void
-readylocked(G *g)
-{
- if(g->m){
- // Running on another machine.
- // Ready it when it stops.
- g->readyonstop = 1;
- return;
+ if(g != m->g0)
+ runtime·throw("bad runtime·mstart");
+
+ // Record top of stack for use by mcall.
+ // Once we call schedule we're never coming back,
+ // so other calls can reuse this stack space.
+ runtime·gosave(&m->g0->sched);
+ m->g0->sched.pc = (void*)-1; // make sure it is never used
+ m->seh = &seh;
+ runtime·asminit();
+ runtime·minit();
+
+ // Install signal handlers; after minit so that minit can
+ // prepare the thread to be able to handle the signals.
+ if(m == &runtime·m0) {
+ runtime·initsig();
+ if(runtime·iscgo)
+ runtime·newextram();
}
+
+ if(m->mstartfn)
+ m->mstartfn();
- // Mark runnable.
- if(g->status == Grunnable || g->status == Grunning) {
- runtime·printf("goroutine %d has status %d\n", g->goid, g->status);
- runtime·throw("bad g->status in ready");
+ if(m->helpgc) {
+ m->helpgc = false;
+ stopm();
+ } else if(m != &runtime·m0) {
+ acquirep(m->nextp);
+ m->nextp = nil;
}
- g->status = Grunnable;
+ schedule();
- gput(g);
- matchmg();
+ // TODO(brainman): This point is never reached, because scheduler
+ // does not release os threads at the moment. But once this path
+ // is enabled, we must remove our seh here.
}
-static void
-nop(void)
-{
-}
+// When running with cgo, we call _cgo_thread_start
+// to start threads for us so that we can play nicely with
+// foreign code.
+void (*_cgo_thread_start)(void*);
-// Same as readylocked but a different symbol so that
-// debuggers can set a breakpoint here and catch all
-// new goroutines.
-static void
-newprocreadylocked(G *g)
+typedef struct CgoThreadStart CgoThreadStart;
+struct CgoThreadStart
{
- nop(); // avoid inlining in 6l
- readylocked(g);
-}
+ M *m;
+ G *g;
+ void (*fn)(void);
+};
-// Pass g to m for running.
-// Caller has already incremented mcpu.
-static void
-mnextg(M *m, G *g)
+// Allocate a new m unassociated with any thread.
+// Can use p for allocation context if needed.
+M*
+runtime·allocm(P *p)
{
- runtime·sched.grunning++;
- m->nextg = g;
- if(m->waitnextg) {
- m->waitnextg = 0;
- if(mwakeup != nil)
- runtime·notewakeup(&mwakeup->havenextg);
- mwakeup = m;
+ M *mp;
+ static Type *mtype; // The Go type M
+
+ m->locks++; // disable GC because it can be called from sysmon
+ if(m->p == nil)
+ acquirep(p); // temporarily borrow p for mallocs in this function
+ if(mtype == nil) {
+ Eface e;
+ runtime·gc_m_ptr(&e);
+ mtype = ((PtrType*)e.type)->elem;
}
-}
-// Get the next goroutine that m should run.
-// Sched must be locked on entry, is unlocked on exit.
-// Makes sure that at most $GOMAXPROCS g's are
-// running on cpus (not in system calls) at any given time.
-static G*
-nextgandunlock(void)
-{
- G *gp;
- uint32 v;
+ mp = runtime·cnew(mtype);
+ mcommoninit(mp);
-top:
- if(atomic_mcpu(runtime·sched.atomic) >= maxgomaxprocs)
- runtime·throw("negative mcpu");
-
- // If there is a g waiting as m->nextg, the mcpu++
- // happened before it was passed to mnextg.
- if(m->nextg != nil) {
- gp = m->nextg;
- m->nextg = nil;
- schedunlock();
- return gp;
- }
+ // In case of cgo, pthread_create will make us a stack.
+ // Windows will layout sched stack on OS stack.
+ if(runtime·iscgo || Windows)
+ mp->g0 = runtime·malg(-1);
+ else
+ mp->g0 = runtime·malg(8192);
- if(m->lockedg != nil) {
- // We can only run one g, and it's not available.
- // Make sure some other cpu is running to handle
- // the ordinary run queue.
- if(runtime·sched.gwait != 0) {
- matchmg();
- // m->lockedg might have been on the queue.
- if(m->nextg != nil) {
- gp = m->nextg;
- m->nextg = nil;
- schedunlock();
- return gp;
- }
- }
- } else {
- // Look for work on global queue.
- while(haveg() && canaddmcpu()) {
- gp = gget();
- if(gp == nil)
- runtime·throw("gget inconsistency");
-
- if(gp->lockedm) {
- mnextg(gp->lockedm, gp);
- continue;
- }
- runtime·sched.grunning++;
- schedunlock();
- return gp;
- }
+ if(p == m->p)
+ releasep();
+ m->locks--;
- // The while loop ended either because the g queue is empty
- // or because we have maxed out our m procs running go
- // code (mcpu >= mcpumax). We need to check that
- // concurrent actions by entersyscall/exitsyscall cannot
- // invalidate the decision to end the loop.
- //
- // We hold the sched lock, so no one else is manipulating the
- // g queue or changing mcpumax. Entersyscall can decrement
- // mcpu, but if does so when there is something on the g queue,
- // the gwait bit will be set, so entersyscall will take the slow path
- // and use the sched lock. So it cannot invalidate our decision.
- //
- // Wait on global m queue.
- mput(m);
- }
-
- // Look for deadlock situation.
- // There is a race with the scavenger that causes false negatives:
- // if the scavenger is just starting, then we have
- // scvg != nil && grunning == 0 && gwait == 0
- // and we do not detect a deadlock. It is possible that we should
- // add that case to the if statement here, but it is too close to Go 1
- // to make such a subtle change. Instead, we work around the
- // false negative in trivial programs by calling runtime.gosched
- // from the main goroutine just before main.main.
- // See runtime·main above.
- //
- // On a related note, it is also possible that the scvg == nil case is
- // wrong and should include gwait, but that does not happen in
- // standard Go programs, which all start the scavenger.
- //
- if((scvg == nil && runtime·sched.grunning == 0) ||
- (scvg != nil && runtime·sched.grunning == 1 && runtime·sched.gwait == 0 &&
- (scvg->status == Grunning || scvg->status == Gsyscall))) {
- runtime·throw("all goroutines are asleep - deadlock!");
- }
-
- m->nextg = nil;
- m->waitnextg = 1;
- runtime·noteclear(&m->havenextg);
-
- // Stoptheworld is waiting for all but its cpu to go to stop.
- // Entersyscall might have decremented mcpu too, but if so
- // it will see the waitstop and take the slow path.
- // Exitsyscall never increments mcpu beyond mcpumax.
- v = runtime·atomicload(&runtime·sched.atomic);
- if(atomic_waitstop(v) && atomic_mcpu(v) <= atomic_mcpumax(v)) {
- // set waitstop = 0 (known to be 1)
- runtime·xadd(&runtime·sched.atomic, -1<<waitstopShift);
- runtime·notewakeup(&runtime·sched.stopped);
- }
- schedunlock();
-
- runtime·notesleep(&m->havenextg);
- if(m->helpgc) {
- runtime·gchelper();
- m->helpgc = 0;
- runtime·lock(&runtime·sched);
- goto top;
- }
- if((gp = m->nextg) == nil)
- runtime·throw("bad m->nextg in nextgoroutine");
- m->nextg = nil;
- return gp;
+ return mp;
}
-int32
-runtime·helpgc(bool *extra)
+static M* lockextra(bool nilokay);
+static void unlockextra(M*);
+
+// needm is called when a cgo callback happens on a
+// thread without an m (a thread not created by Go).
+// In this case, needm is expected to find an m to use
+// and return with m, g initialized correctly.
+// Since m and g are not set now (likely nil, but see below)
+// needm is limited in what routines it can call. In particular
+// it can only call nosplit functions (textflag 7) and cannot
+// do any scheduling that requires an m.
+//
+// In order to avoid needing heavy lifting here, we adopt
+// the following strategy: there is a stack of available m's
+// that can be stolen. Using compare-and-swap
+// to pop from the stack has ABA races, so we simulate
+// a lock by doing an exchange (via casp) to steal the stack
+// head and replace the top pointer with MLOCKED (1).
+// This serves as a simple spin lock that we can use even
+// without an m. The thread that locks the stack in this way
+// unlocks the stack by storing a valid stack head pointer.
+//
+// In order to make sure that there is always an m structure
+// available to be stolen, we maintain the invariant that there
+// is always one more than needed. At the beginning of the
+// program (if cgo is in use) the list is seeded with a single m.
+// If needm finds that it has taken the last m off the list, its job
+// is - once it has installed its own m so that it can do things like
+// allocate memory - to create a spare m and put it on the list.
+//
+// Each of these extra m's also has a g0 and a curg that are
+// pressed into service as the scheduling stack and current
+// goroutine for the duration of the cgo callback.
+//
+// When the callback is done with the m, it calls dropm to
+// put the m back on the list.
+#pragma textflag 7
+void
+runtime·needm(byte x)
{
M *mp;
- int32 n, max;
- // Figure out how many CPUs to use.
- // Limited by gomaxprocs, number of actual CPUs, and MaxGcproc.
- max = runtime·gomaxprocs;
- if(max > runtime·ncpu)
- max = runtime·ncpu;
- if(max > MaxGcproc)
- max = MaxGcproc;
+ // Lock extra list, take head, unlock popped list.
+ // nilokay=false is safe here because of the invariant above,
+ // that the extra list always contains or will soon contain
+ // at least one m.
+ mp = lockextra(false);
+
+ // Set needextram when we've just emptied the list,
+ // so that the eventual call into cgocallbackg will
+ // allocate a new m for the extra list. We delay the
+ // allocation until then so that it can be done
+ // after exitsyscall makes sure it is okay to be
+ // running at all (that is, there's no garbage collection
+ // running right now).
+ mp->needextram = mp->schedlink == nil;
+ unlockextra(mp->schedlink);
+
+ // Install m and g (= m->g0) and set the stack bounds
+ // to match the current stack. We don't actually know
+ // how big the stack is, like we don't know how big any
+ // scheduling stack is, but we assume there's at least 32 kB,
+ // which is more than enough for us.
+ runtime·setmg(mp, mp->g0);
+ g->stackbase = (uintptr)(&x + 1024);
+ g->stackguard = (uintptr)(&x - 32*1024);
+
+ // On windows/386, we need to put an SEH frame (two words)
+ // somewhere on the current stack. We are called
+ // from needm, and we know there is some available
+ // space one word into the argument frame. Use that.
+ m->seh = (SEH*)((uintptr*)&x + 1);
+
+ // Initialize this thread to use the m.
+ runtime·asminit();
+ runtime·minit();
+}
- // We're going to use one CPU no matter what.
- // Figure out the max number of additional CPUs.
- max--;
+// newextram allocates an m and puts it on the extra list.
+// It is called with a working local m, so that it can do things
+// like call schedlock and allocate.
+void
+runtime·newextram(void)
+{
+ M *mp, *mnext;
+ G *gp;
+ // Create extra goroutine locked to extra m.
+ // The goroutine is the context in which the cgo callback will run.
+ // The sched.pc will never be returned to, but setting it to
+ // runtime.goexit makes clear to the traceback routines where
+ // the goroutine stack ends.
+ mp = runtime·allocm(nil);
+ gp = runtime·malg(4096);
+ gp->sched.pc = (void*)runtime·goexit;
+ gp->sched.sp = gp->stackbase;
+ gp->sched.g = gp;
+ gp->status = Gsyscall;
+ mp->curg = gp;
+ mp->locked = LockInternal;
+ mp->lockedg = gp;
+ gp->lockedm = mp;
+ // put on allg for garbage collector
runtime·lock(&runtime·sched);
- n = 0;
- while(n < max && (mp = mget(nil)) != nil) {
- n++;
- mp->helpgc = 1;
- mp->waitnextg = 0;
- runtime·notewakeup(&mp->havenextg);
- }
+ if(runtime·lastg == nil)
+ runtime·allg = gp;
+ else
+ runtime·lastg->alllink = gp;
+ runtime·lastg = gp;
runtime·unlock(&runtime·sched);
- if(extra)
- *extra = n != max;
- return n;
+ gp->goid = runtime·xadd64(&runtime·sched.goidgen, 1);
+ if(raceenabled)
+ gp->racectx = runtime·racegostart(runtime·newextram);
+
+ // Add m to the extra list.
+ mnext = lockextra(true);
+ mp->schedlink = mnext;
+ unlockextra(mp);
}
+// dropm is called when a cgo callback has called needm but is now
+// done with the callback and returning back into the non-Go thread.
+// It puts the current m back onto the extra list.
+//
+// The main expense here is the call to signalstack to release the
+// m's signal stack, and then the call to needm on the next callback
+// from this thread. It is tempting to try to save the m for next time,
+// which would eliminate both these costs, but there might not be
+// a next time: the current thread (which Go does not control) might exit.
+// If we saved the m for that thread, there would be an m leak each time
+// such a thread exited. Instead, we acquire and release an m on each
+// call. These should typically not be scheduling operations, just a few
+// atomics, so the cost should be small.
+//
+// TODO(rsc): An alternative would be to allocate a dummy pthread per-thread
+// variable using pthread_key_create. Unlike the pthread keys we already use
+// on OS X, this dummy key would never be read by Go code. It would exist
+// only so that we could register at thread-exit-time destructor.
+// That destructor would put the m back onto the extra list.
+// This is purely a performance optimization. The current version,
+// in which dropm happens on each cgo call, is still correct too.
+// We may have to keep the current version on systems with cgo
+// but without pthreads, like Windows.
void
-runtime·stoptheworld(void)
+runtime·dropm(void)
{
- uint32 v;
+ M *mp, *mnext;
- schedlock();
- runtime·gcwaiting = 1;
+ // Undo whatever initialization minit did during needm.
+ runtime·unminit();
- setmcpumax(1);
+ // Clear m and g, and return m to the extra list.
+ // After the call to setmg we can only call nosplit functions.
+ mp = m;
+ runtime·setmg(nil, nil);
- // while mcpu > 1
- for(;;) {
- v = runtime·sched.atomic;
- if(atomic_mcpu(v) <= 1)
- break;
+ mnext = lockextra(true);
+ mp->schedlink = mnext;
+ unlockextra(mp);
+}
- // It would be unsafe for multiple threads to be using
- // the stopped note at once, but there is only
- // ever one thread doing garbage collection.
- runtime·noteclear(&runtime·sched.stopped);
- if(atomic_waitstop(v))
- runtime·throw("invalid waitstop");
+#define MLOCKED ((M*)1)
- // atomic { waitstop = 1 }, predicated on mcpu <= 1 check above
- // still being true.
- if(!runtime·cas(&runtime·sched.atomic, v, v+(1<<waitstopShift)))
- continue;
+// lockextra locks the extra list and returns the list head.
+// The caller must unlock the list by storing a new list head
+// to runtime.extram. If nilokay is true, then lockextra will
+// return a nil list head if that's what it finds. If nilokay is false,
+// lockextra will keep waiting until the list head is no longer nil.
+#pragma textflag 7
+static M*
+lockextra(bool nilokay)
+{
+ M *mp;
+ void (*yield)(void);
- schedunlock();
- runtime·notesleep(&runtime·sched.stopped);
- schedlock();
+ for(;;) {
+ mp = runtime·atomicloadp(&runtime·extram);
+ if(mp == MLOCKED) {
+ yield = runtime·osyield;
+ yield();
+ continue;
+ }
+ if(mp == nil && !nilokay) {
+ runtime·usleep(1);
+ continue;
+ }
+ if(!runtime·casp(&runtime·extram, mp, MLOCKED)) {
+ yield = runtime·osyield;
+ yield();
+ continue;
+ }
+ break;
}
- runtime·singleproc = runtime·gomaxprocs == 1;
- schedunlock();
+ return mp;
}
-void
-runtime·starttheworld(bool extra)
+#pragma textflag 7
+static void
+unlockextra(M *mp)
{
- M *m;
-
- schedlock();
- runtime·gcwaiting = 0;
- setmcpumax(runtime·gomaxprocs);
- matchmg();
- if(extra && canaddmcpu()) {
- // Start a new m that will (we hope) be idle
- // and so available to help when the next
- // garbage collection happens.
- // canaddmcpu above did mcpu++
- // (necessary, because m will be doing various
- // initialization work so is definitely running),
- // but m is not running a specific goroutine,
- // so set the helpgc flag as a signal to m's
- // first schedule(nil) to mcpu-- and grunning--.
- m = runtime·newm();
- m->helpgc = 1;
- runtime·sched.grunning++;
- }
- schedunlock();
+ runtime·atomicstorep(&runtime·extram, mp);
}
-// Called to start an M.
-void
-runtime·mstart(void)
+
+// Create a new m. It will start off with a call to fn, or else the scheduler.
+static void
+newm(void(*fn)(void), P *p)
{
- if(g != m->g0)
- runtime·throw("bad runtime·mstart");
+ M *mp;
- // Record top of stack for use by mcall.
- // Once we call schedule we're never coming back,
- // so other calls can reuse this stack space.
- runtime·gosave(&m->g0->sched);
- m->g0->sched.pc = (void*)-1; // make sure it is never used
- runtime·asminit();
- runtime·minit();
+ mp = runtime·allocm(p);
+ mp->nextp = p;
+ mp->mstartfn = fn;
- // Install signal handlers; after minit so that minit can
- // prepare the thread to be able to handle the signals.
- if(m == &runtime·m0)
- runtime·initsig();
+ if(runtime·iscgo) {
+ CgoThreadStart ts;
- schedule(nil);
+ if(_cgo_thread_start == nil)
+ runtime·throw("_cgo_thread_start missing");
+ ts.m = mp;
+ ts.g = mp->g0;
+ ts.fn = runtime·mstart;
+ runtime·asmcgocall(_cgo_thread_start, &ts);
+ return;
+ }
+ runtime·newosproc(mp, (byte*)mp->g0->stackbase);
}
-// When running with cgo, we call libcgo_thread_start
-// to start threads for us so that we can play nicely with
-// foreign code.
-void (*libcgo_thread_start)(void*);
+// Stops execution of the current m until new work is available.
+// Returns with acquired P.
+static void
+stopm(void)
+{
+ if(m->locks)
+ runtime·throw("stopm holding locks");
+ if(m->p)
+ runtime·throw("stopm holding p");
+ if(m->spinning) {
+ m->spinning = false;
+ runtime·xadd(&runtime·sched.nmspinning, -1);
+ }
-typedef struct CgoThreadStart CgoThreadStart;
-struct CgoThreadStart
+retry:
+ runtime·lock(&runtime·sched);
+ mput(m);
+ runtime·unlock(&runtime·sched);
+ runtime·notesleep(&m->park);
+ runtime·noteclear(&m->park);
+ if(m->helpgc) {
+ m->helpgc = 0;
+ runtime·gchelper();
+ m->mcache = nil;
+ goto retry;
+ }
+ acquirep(m->nextp);
+ m->nextp = nil;
+}
+
+static void
+mspinning(void)
{
- M *m;
- G *g;
- void (*fn)(void);
-};
+ m->spinning = true;
+}
-// Kick off new m's as needed (up to mcpumax).
-// Sched is locked.
+// Schedules some M to run the p (creates an M if necessary).
+// If p==nil, tries to get an idle P, if no idle P's returns false.
static void
-matchmg(void)
+startm(P *p, bool spinning)
{
- G *gp;
M *mp;
+ void (*fn)(void);
- if(m->mallocing || m->gcing)
+ runtime·lock(&runtime·sched);
+ if(p == nil) {
+ p = pidleget();
+ if(p == nil) {
+ runtime·unlock(&runtime·sched);
+ if(spinning)
+ runtime·xadd(&runtime·sched.nmspinning, -1);
+ return;
+ }
+ }
+ mp = mget();
+ runtime·unlock(&runtime·sched);
+ if(mp == nil) {
+ fn = nil;
+ if(spinning)
+ fn = mspinning;
+ newm(fn, p);
return;
-
- while(haveg() && canaddmcpu()) {
- gp = gget();
- if(gp == nil)
- runtime·throw("gget inconsistency");
-
- // Find the m that will run gp.
- if((mp = mget(gp)) == nil)
- mp = runtime·newm();
- mnextg(mp, gp);
}
+ if(mp->spinning)
+ runtime·throw("startm: m is spinning");
+ if(mp->nextp)
+ runtime·throw("startm: m has p");
+ mp->spinning = spinning;
+ mp->nextp = p;
+ runtime·notewakeup(&mp->park);
}
-// Create a new m. It will start off with a call to runtime·mstart.
-M*
-runtime·newm(void)
+// Hands off P from syscall or locked M.
+static void
+handoffp(P *p)
{
- M *m;
+ // if it has local work, start it straight away
+ if(p->runqhead != p->runqtail || runtime·sched.runqsize) {
+ startm(p, false);
+ return;
+ }
+ // no local work, check that there are no spinning/idle M's,
+ // otherwise our help is not required
+ if(runtime·atomicload(&runtime·sched.nmspinning) + runtime·atomicload(&runtime·sched.npidle) == 0 && // TODO: fast atomic
+ runtime·cas(&runtime·sched.nmspinning, 0, 1)) {
+ startm(p, true);
+ return;
+ }
+ runtime·lock(&runtime·sched);
+ if(runtime·gcwaiting) {
+ p->status = Pgcstop;
+ if(--runtime·sched.stopwait == 0)
+ runtime·notewakeup(&runtime·sched.stopnote);
+ runtime·unlock(&runtime·sched);
+ return;
+ }
+ if(runtime·sched.runqsize) {
+ runtime·unlock(&runtime·sched);
+ startm(p, false);
+ return;
+ }
+ pidleput(p);
+ runtime·unlock(&runtime·sched);
+}
- m = runtime·malloc(sizeof(M));
- mcommoninit(m);
+// Tries to add one more P to execute G's.
+// Called when a G is made runnable (newproc, ready).
+static void
+wakep(void)
+{
+ // be conservative about spinning threads
+ if(!runtime·cas(&runtime·sched.nmspinning, 0, 1))
+ return;
+ startm(nil, true);
+}
- if(runtime·iscgo) {
- CgoThreadStart ts;
+// Stops execution of the current m that is locked to a g until the g is runnable again.
+// Returns with acquired P.
+static void
+stoplockedm(void)
+{
+ P *p;
- if(libcgo_thread_start == nil)
- runtime·throw("libcgo_thread_start missing");
- // pthread_create will make us a stack.
- m->g0 = runtime·malg(-1);
- ts.m = m;
- ts.g = m->g0;
- ts.fn = runtime·mstart;
- runtime·asmcgocall(libcgo_thread_start, &ts);
- } else {
- if(Windows)
- // windows will layout sched stack on os stack
- m->g0 = runtime·malg(-1);
- else
- m->g0 = runtime·malg(8192);
- runtime·newosproc(m, m->g0, m->g0->stackbase, runtime·mstart);
+ if(m->lockedg == nil || m->lockedg->lockedm != m)
+ runtime·throw("stoplockedm: inconsistent locking");
+ if(m->p) {
+ // Schedule another M to run this p.
+ p = releasep();
+ handoffp(p);
}
+ inclocked(1);
+ // Wait until another thread schedules lockedg again.
+ runtime·notesleep(&m->park);
+ runtime·noteclear(&m->park);
+ if(m->lockedg->status != Grunnable)
+ runtime·throw("stoplockedm: not runnable");
+ acquirep(m->nextp);
+ m->nextp = nil;
+}
+
+// Schedules the locked m to run the locked gp.
+static void
+startlockedm(G *gp)
+{
+ M *mp;
+ P *p;
+
+ mp = gp->lockedm;
+ if(mp == m)
+ runtime·throw("startlockedm: locked to me");
+ if(mp->nextp)
+ runtime·throw("startlockedm: m has p");
+ // directly handoff current P to the locked m
+ inclocked(-1);
+ p = releasep();
+ mp->nextp = p;
+ runtime·notewakeup(&mp->park);
+ stopm();
+}
+
+// Stops the current m for stoptheworld.
+// Returns when the world is restarted.
+static void
+gcstopm(void)
+{
+ P *p;
- return m;
+ if(!runtime·gcwaiting)
+ runtime·throw("gcstopm: not waiting for gc");
+ if(m->spinning) {
+ m->spinning = false;
+ runtime·xadd(&runtime·sched.nmspinning, -1);
+ }
+ p = releasep();
+ runtime·lock(&runtime·sched);
+ p->status = Pgcstop;
+ if(--runtime·sched.stopwait == 0)
+ runtime·notewakeup(&runtime·sched.stopnote);
+ runtime·unlock(&runtime·sched);
+ stopm();
}
-// One round of scheduler: find a goroutine and run it.
-// The argument is the goroutine that was running before
-// schedule was called, or nil if this is the first call.
+// Schedules gp to run on the current M.
// Never returns.
static void
-schedule(G *gp)
+execute(G *gp)
{
int32 hz;
- uint32 v;
-
- schedlock();
- if(gp != nil) {
- // Just finished running gp.
- gp->m = nil;
- runtime·sched.grunning--;
-
- // atomic { mcpu-- }
- v = runtime·xadd(&runtime·sched.atomic, -1<<mcpuShift);
- if(atomic_mcpu(v) > maxgomaxprocs)
- runtime·throw("negative mcpu in scheduler");
-
- switch(gp->status){
- case Grunnable:
- case Gdead:
- // Shouldn't have been running!
- runtime·throw("bad gp->status in sched");
- case Grunning:
- gp->status = Grunnable;
- gput(gp);
- break;
- case Gmoribund:
- gp->status = Gdead;
- if(gp->lockedm) {
- gp->lockedm = nil;
- m->lockedg = nil;
- }
- gp->idlem = nil;
- unwindstack(gp, nil);
- gfput(gp);
- if(--runtime·sched.gcount == 0)
- runtime·exit(0);
- break;
- }
- if(gp->readyonstop){
- gp->readyonstop = 0;
- readylocked(gp);
- }
- } else if(m->helpgc) {
- // Bootstrap m or new m started by starttheworld.
- // atomic { mcpu-- }
- v = runtime·xadd(&runtime·sched.atomic, -1<<mcpuShift);
- if(atomic_mcpu(v) > maxgomaxprocs)
- runtime·throw("negative mcpu in scheduler");
- // Compensate for increment in starttheworld().
- runtime·sched.grunning--;
- m->helpgc = 0;
- } else if(m->nextg != nil) {
- // New m started by matchmg.
- } else {
- runtime·throw("invalid m state in scheduler");
- }
- // Find (or wait for) g to run. Unlocks runtime·sched.
- gp = nextgandunlock();
- gp->readyonstop = 0;
+ if(gp->status != Grunnable) {
+ runtime·printf("execute: bad g status %d\n", gp->status);
+ runtime·throw("execute: bad g status");
+ }
gp->status = Grunning;
+ m->p->tick++;
m->curg = gp;
gp->m = m;
@@ -904,27 +964,209 @@ schedule(G *gp)
if(m->profilehz != hz)
runtime·resetcpuprofiler(hz);
- if(gp->sched.pc == (byte*)runtime·goexit) { // kickoff
- runtime·gogocall(&gp->sched, (void(*)(void))gp->entry);
- }
+ if(gp->sched.pc == (byte*)runtime·goexit) // kickoff
+ runtime·gogocallfn(&gp->sched, gp->fnstart);
runtime·gogo(&gp->sched, 0);
}
-// Enter scheduler. If g->status is Grunning,
-// re-queues g and runs everyone else who is waiting
-// before running g again. If g->status is Gmoribund,
-// kills off g.
-// Cannot split stack because it is called from exitsyscall.
-// See comment below.
-#pragma textflag 7
+// Finds a runnable goroutine to execute.
+// Tries to steal from other P's and get g from global queue.
+static G*
+findrunnable(void)
+{
+ G *gp;
+ P *p;
+ int32 i;
+
+top:
+ if(runtime·gcwaiting) {
+ gcstopm();
+ goto top;
+ }
+ // local runq
+ gp = runqget(m->p);
+ if(gp)
+ return gp;
+ // global runq
+ if(runtime·sched.runqsize) {
+ runtime·lock(&runtime·sched);
+ gp = globrunqget(m->p);
+ runtime·unlock(&runtime·sched);
+ if(gp)
+ return gp;
+ }
+ // If number of spinning M's >= number of busy P's, block.
+ // This is necessary to prevent excessive CPU consumption
+ // when GOMAXPROCS>>1 but the program parallelism is low.
+ if(!m->spinning && 2 * runtime·atomicload(&runtime·sched.nmspinning) >= runtime·gomaxprocs - runtime·atomicload(&runtime·sched.npidle)) // TODO: fast atomic
+ goto stop;
+ if(!m->spinning) {
+ m->spinning = true;
+ runtime·xadd(&runtime·sched.nmspinning, 1);
+ }
+ // random steal from other P's
+ for(i = 0; i < 2*runtime·gomaxprocs; i++) {
+ if(runtime·gcwaiting)
+ goto top;
+ p = runtime·allp[runtime·fastrand1()%runtime·gomaxprocs];
+ if(p == m->p)
+ gp = runqget(p);
+ else
+ gp = runqsteal(m->p, p);
+ if(gp)
+ return gp;
+ }
+stop:
+ // return P and block
+ runtime·lock(&runtime·sched);
+ if(runtime·gcwaiting) {
+ runtime·unlock(&runtime·sched);
+ goto top;
+ }
+ if(runtime·sched.runqsize) {
+ gp = globrunqget(m->p);
+ runtime·unlock(&runtime·sched);
+ return gp;
+ }
+ p = releasep();
+ pidleput(p);
+ runtime·unlock(&runtime·sched);
+ if(m->spinning) {
+ m->spinning = false;
+ runtime·xadd(&runtime·sched.nmspinning, -1);
+ }
+ // check all runqueues once again
+ for(i = 0; i < runtime·gomaxprocs; i++) {
+ p = runtime·allp[i];
+ if(p && p->runqhead != p->runqtail) {
+ runtime·lock(&runtime·sched);
+ p = pidleget();
+ runtime·unlock(&runtime·sched);
+ if(p) {
+ acquirep(p);
+ goto top;
+ }
+ break;
+ }
+ }
+ stopm();
+ goto top;
+}
+
+// One round of scheduler: find a runnable goroutine and execute it.
+// Never returns.
+static void
+schedule(void)
+{
+ G *gp;
+
+ if(m->locks)
+ runtime·throw("schedule: holding locks");
+
+top:
+ if(runtime·gcwaiting) {
+ gcstopm();
+ goto top;
+ }
+
+ gp = runqget(m->p);
+ if(gp == nil)
+ gp = findrunnable();
+
+ if(m->spinning) {
+ m->spinning = false;
+ runtime·xadd(&runtime·sched.nmspinning, -1);
+ }
+
+ // M wakeup policy is deliberately somewhat conservative (see nmspinning handling),
+ // so see if we need to wakeup another M here.
+ if (m->p->runqhead != m->p->runqtail &&
+ runtime·atomicload(&runtime·sched.nmspinning) == 0 &&
+ runtime·atomicload(&runtime·sched.npidle) > 0) // TODO: fast atomic
+ wakep();
+
+ if(gp->lockedm) {
+ startlockedm(gp);
+ goto top;
+ }
+
+ execute(gp);
+}
+
+// Puts the current goroutine into a waiting state and unlocks the lock.
+// The goroutine can be made runnable again by calling runtime·ready(gp).
+void
+runtime·park(void(*unlockf)(Lock*), Lock *lock, int8 *reason)
+{
+ m->waitlock = lock;
+ m->waitunlockf = unlockf;
+ g->waitreason = reason;
+ runtime·mcall(park0);
+}
+
+// runtime·park continuation on g0.
+static void
+park0(G *gp)
+{
+ gp->status = Gwaiting;
+ gp->m = nil;
+ m->curg = nil;
+ if(m->waitunlockf) {
+ m->waitunlockf(m->waitlock);
+ m->waitunlockf = nil;
+ }
+ if(m->lockedg) {
+ stoplockedm();
+ execute(gp); // Never returns.
+ }
+ schedule();
+}
+
+// Scheduler yield.
void
runtime·gosched(void)
{
- if(m->locks != 0)
- runtime·throw("gosched holding locks");
- if(g == m->g0)
- runtime·throw("gosched of g0");
- runtime·mcall(schedule);
+ runtime·mcall(gosched0);
+}
+
+// runtime·gosched continuation on g0.
+static void
+gosched0(G *gp)
+{
+ gp->status = Grunnable;
+ gp->m = nil;
+ m->curg = nil;
+ runtime·lock(&runtime·sched);
+ globrunqput(gp);
+ runtime·unlock(&runtime·sched);
+ if(m->lockedg) {
+ stoplockedm();
+ execute(gp); // Never returns.
+ }
+ schedule();
+}
+
+// Finishes execution of the current goroutine.
+void
+runtime·goexit(void)
+{
+ if(raceenabled)
+ runtime·racegoend();
+ runtime·mcall(goexit0);
+}
+
+// runtime·goexit continuation on g0.
+static void
+goexit0(G *gp)
+{
+ gp->status = Gdead;
+ gp->m = nil;
+ gp->lockedm = nil;
+ m->curg = nil;
+ m->lockedg = nil;
+ runtime·unwindstack(gp, nil);
+ gfput(m->p, gp);
+ schedule();
}
// The goroutine g is about to enter a system call.
@@ -935,21 +1177,19 @@ runtime·gosched(void)
// Entersyscall cannot split the stack: the runtime·gosave must
// make g->sched refer to the caller's stack segment, because
// entersyscall is going to return immediately after.
-// It's okay to call matchmg and notewakeup even after
-// decrementing mcpu, because we haven't released the
-// sched lock yet, so the garbage collector cannot be running.
#pragma textflag 7
void
-runtime·entersyscall(void)
+·entersyscall(int32 dummy)
{
- uint32 v;
-
if(m->profilehz > 0)
runtime·setprof(false);
// Leave SP around for gc and traceback.
- runtime·gosave(&g->sched);
+ g->sched.sp = (uintptr)runtime·getcallersp(&dummy);
+ g->sched.pc = runtime·getcallerpc(&dummy);
+ g->sched.g = g;
g->gcsp = g->sched.sp;
+ g->gcpc = g->sched.pc;
g->gcstack = g->stackbase;
g->gcguard = g->stackguard;
g->status = Gsyscall;
@@ -959,34 +1199,61 @@ runtime·entersyscall(void)
runtime·throw("entersyscall");
}
- // Fast path.
- // The slow path inside the schedlock/schedunlock will get
- // through without stopping if it does:
- // mcpu--
- // gwait not true
- // waitstop && mcpu <= mcpumax not true
- // If we can do the same with a single atomic add,
- // then we can skip the locks.
- v = runtime·xadd(&runtime·sched.atomic, -1<<mcpuShift);
- if(!atomic_gwaiting(v) && (!atomic_waitstop(v) || atomic_mcpu(v) > atomic_mcpumax(v)))
- return;
-
- schedlock();
- v = runtime·atomicload(&runtime·sched.atomic);
- if(atomic_gwaiting(v)) {
- matchmg();
- v = runtime·atomicload(&runtime·sched.atomic);
+ if(runtime·atomicload(&runtime·sched.sysmonwait)) { // TODO: fast atomic
+ runtime·lock(&runtime·sched);
+ if(runtime·atomicload(&runtime·sched.sysmonwait)) {
+ runtime·atomicstore(&runtime·sched.sysmonwait, 0);
+ runtime·notewakeup(&runtime·sched.sysmonnote);
+ }
+ runtime·unlock(&runtime·sched);
+ runtime·gosave(&g->sched); // re-save for traceback
}
- if(atomic_waitstop(v) && atomic_mcpu(v) <= atomic_mcpumax(v)) {
- runtime·xadd(&runtime·sched.atomic, -1<<waitstopShift);
- runtime·notewakeup(&runtime·sched.stopped);
+
+ m->mcache = nil;
+ m->p->tick++;
+ m->p->m = nil;
+ runtime·atomicstore(&m->p->status, Psyscall);
+ if(runtime·gcwaiting) {
+ runtime·lock(&runtime·sched);
+ if (runtime·sched.stopwait > 0 && runtime·cas(&m->p->status, Psyscall, Pgcstop)) {
+ if(--runtime·sched.stopwait == 0)
+ runtime·notewakeup(&runtime·sched.stopnote);
+ }
+ runtime·unlock(&runtime·sched);
+ runtime·gosave(&g->sched); // re-save for traceback
}
+}
- // Re-save sched in case one of the calls
- // (notewakeup, matchmg) triggered something using it.
- runtime·gosave(&g->sched);
+// The same as runtime·entersyscall(), but with a hint that the syscall is blocking.
+#pragma textflag 7
+void
+·entersyscallblock(int32 dummy)
+{
+ P *p;
- schedunlock();
+ if(m->profilehz > 0)
+ runtime·setprof(false);
+
+ // Leave SP around for gc and traceback.
+ g->sched.sp = (uintptr)runtime·getcallersp(&dummy);
+ g->sched.pc = runtime·getcallerpc(&dummy);
+ g->sched.g = g;
+ g->gcsp = g->sched.sp;
+ g->gcpc = g->sched.pc;
+ g->gcstack = g->stackbase;
+ g->gcguard = g->stackguard;
+ g->status = Gsyscall;
+ if(g->gcsp < g->gcguard-StackGuard || g->gcstack < g->gcsp) {
+ // runtime·printf("entersyscallblock inconsistent %p [%p,%p]\n",
+ // g->gcsp, g->gcguard-StackGuard, g->gcstack);
+ runtime·throw("entersyscallblock");
+ }
+
+ p = releasep();
+ handoffp(p);
+ if(g == scvg) // do not consider blocked scavenger for deadlock detection
+ inclocked(1);
+ runtime·gosave(&g->sched); // re-save for traceback
}
// The goroutine g exited its system call.
@@ -996,177 +1263,81 @@ runtime·entersyscall(void)
void
runtime·exitsyscall(void)
{
- uint32 v;
+ P *p;
- // Fast path.
- // If we can do the mcpu++ bookkeeping and
- // find that we still have mcpu <= mcpumax, then we can
- // start executing Go code immediately, without having to
- // schedlock/schedunlock.
- v = runtime·xadd(&runtime·sched.atomic, (1<<mcpuShift));
- if(m->profilehz == runtime·sched.profilehz && atomic_mcpu(v) <= atomic_mcpumax(v)) {
+ // Check whether the profiler needs to be turned on.
+ if(m->profilehz > 0)
+ runtime·setprof(true);
+
+ // Try to re-acquire the last P.
+ if(m->p && m->p->status == Psyscall && runtime·cas(&m->p->status, Psyscall, Prunning)) {
// There's a cpu for us, so we can run.
+ m->mcache = m->p->mcache;
+ m->p->m = m;
+ m->p->tick++;
g->status = Grunning;
// Garbage collector isn't running (since we are),
- // so okay to clear gcstack.
- g->gcstack = nil;
-
- if(m->profilehz > 0)
- runtime·setprof(true);
+ // so okay to clear gcstack and gcsp.
+ g->gcstack = (uintptr)nil;
+ g->gcsp = (uintptr)nil;
return;
}
- // Tell scheduler to put g back on the run queue:
- // mostly equivalent to g->status = Grunning,
- // but keeps the garbage collector from thinking
- // that g is running right now, which it's not.
- g->readyonstop = 1;
+ if(g == scvg) // do not consider blocked scavenger for deadlock detection
+ inclocked(-1);
+ // Try to get any other idle P.
+ m->p = nil;
+ if(runtime·sched.pidle) {
+ runtime·lock(&runtime·sched);
+ p = pidleget();
+ runtime·unlock(&runtime·sched);
+ if(p) {
+ acquirep(p);
+ g->gcstack = (uintptr)nil;
+ g->gcsp = (uintptr)nil;
+ return;
+ }
+ }
- // All the cpus are taken.
- // The scheduler will ready g and put this m to sleep.
- // When the scheduler takes g away from m,
- // it will undo the runtime·sched.mcpu++ above.
- runtime·gosched();
+ // Call the scheduler.
+ runtime·mcall(exitsyscall0);
- // Gosched returned, so we're allowed to run now.
+ // Scheduler returned, so we're allowed to run now.
// Delete the gcstack information that we left for
// the garbage collector during the system call.
// Must wait until now because until gosched returns
// we don't know for sure that the garbage collector
// is not running.
- g->gcstack = nil;
+ g->gcstack = (uintptr)nil;
+ g->gcsp = (uintptr)nil;
}
-// Called from runtime·lessstack when returning from a function which
-// allocated a new stack segment. The function's return value is in
-// m->cret.
-void
-runtime·oldstack(void)
+// runtime·exitsyscall slow path on g0.
+// Failed to acquire P, enqueue gp as runnable.
+static void
+exitsyscall0(G *gp)
{
- Stktop *top, old;
- uint32 argsize;
- uintptr cret;
- byte *sp;
- G *g1;
- int32 goid;
-
-//printf("oldstack m->cret=%p\n", m->cret);
-
- g1 = m->curg;
- top = (Stktop*)g1->stackbase;
- sp = (byte*)top;
- old = *top;
- argsize = old.argsize;
- if(argsize > 0) {
- sp -= argsize;
- runtime·memmove(top->argp, sp, argsize);
- }
- goid = old.gobuf.g->goid; // fault if g is bad, before gogo
- USED(goid);
-
- if(old.free != 0)
- runtime·stackfree(g1->stackguard - StackGuard, old.free);
- g1->stackbase = old.stackbase;
- g1->stackguard = old.stackguard;
-
- cret = m->cret;
- m->cret = 0; // drop reference
- runtime·gogo(&old.gobuf, cret);
-}
-
-// Called from reflect·call or from runtime·morestack when a new
-// stack segment is needed. Allocate a new stack big enough for
-// m->moreframesize bytes, copy m->moreargsize bytes to the new frame,
-// and then act as though runtime·lessstack called the function at
-// m->morepc.
-void
-runtime·newstack(void)
-{
- int32 framesize, argsize;
- Stktop *top;
- byte *stk, *sp;
- G *g1;
- Gobuf label;
- bool reflectcall;
- uintptr free;
-
- framesize = m->moreframesize;
- argsize = m->moreargsize;
- g1 = m->curg;
-
- if(m->morebuf.sp < g1->stackguard - StackGuard) {
- runtime·printf("runtime: split stack overflow: %p < %p\n", m->morebuf.sp, g1->stackguard - StackGuard);
- runtime·throw("runtime: split stack overflow");
- }
- if(argsize % sizeof(uintptr) != 0) {
- runtime·printf("runtime: stack split with misaligned argsize %d\n", argsize);
- runtime·throw("runtime: stack split argsize");
- }
-
- reflectcall = framesize==1;
- if(reflectcall)
- framesize = 0;
-
- if(reflectcall && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > g1->stackguard) {
- // special case: called from reflect.call (framesize==1)
- // to call code with an arbitrary argument size,
- // and we have enough space on the current stack.
- // the new Stktop* is necessary to unwind, but
- // we don't need to create a new segment.
- top = (Stktop*)(m->morebuf.sp - sizeof(*top));
- stk = g1->stackguard - StackGuard;
- free = 0;
- } else {
- // allocate new segment.
- framesize += argsize;
- framesize += StackExtra; // room for more functions, Stktop.
- if(framesize < StackMin)
- framesize = StackMin;
- framesize += StackSystem;
- stk = runtime·stackalloc(framesize);
- top = (Stktop*)(stk+framesize-sizeof(*top));
- free = framesize;
- }
-
-//runtime·printf("newstack framesize=%d argsize=%d morepc=%p moreargp=%p gobuf=%p, %p top=%p old=%p\n",
-//framesize, argsize, m->morepc, m->moreargp, m->morebuf.pc, m->morebuf.sp, top, g1->stackbase);
-
- top->stackbase = g1->stackbase;
- top->stackguard = g1->stackguard;
- top->gobuf = m->morebuf;
- top->argp = m->moreargp;
- top->argsize = argsize;
- top->free = free;
- m->moreargp = nil;
- m->morebuf.pc = nil;
- m->morebuf.sp = nil;
-
- // copy flag from panic
- top->panic = g1->ispanic;
- g1->ispanic = false;
-
- g1->stackbase = (byte*)top;
- g1->stackguard = stk + StackGuard;
-
- sp = (byte*)top;
- if(argsize > 0) {
- sp -= argsize;
- runtime·memmove(sp, top->argp, argsize);
+ P *p;
+
+ gp->status = Grunnable;
+ gp->m = nil;
+ m->curg = nil;
+ runtime·lock(&runtime·sched);
+ p = pidleget();
+ if(p == nil)
+ globrunqput(gp);
+ runtime·unlock(&runtime·sched);
+ if(p) {
+ acquirep(p);
+ execute(gp); // Never returns.
}
- if(thechar == '5') {
- // caller would have saved its LR below args.
- sp -= sizeof(void*);
- *(void**)sp = nil;
+ if(m->lockedg) {
+ // Wait until another thread schedules gp and so m again.
+ stoplockedm();
+ execute(gp); // Never returns.
}
-
- // Continue as if lessstack had just called m->morepc
- // (the PC that decided to grow the stack).
- label.sp = sp;
- label.pc = (byte*)runtime·lessstack;
- label.g = m->curg;
- runtime·gogocall(&label, m->morepc);
-
- *(int32*)345 = 123; // never return
+ stopm();
+ schedule(); // Never returns.
}
// Hook used by runtime·malg to call runtime·stackalloc on the
@@ -1204,10 +1375,10 @@ runtime·malg(int32 stacksize)
stk = g->param;
g->param = nil;
}
- newg->stack0 = stk;
- newg->stackguard = stk + StackGuard;
- newg->stackbase = stk + StackSystem + stacksize - sizeof(Stktop);
- runtime·memclr(newg->stackbase, sizeof(Stktop));
+ newg->stack0 = (uintptr)stk;
+ newg->stackguard = (uintptr)stk + StackGuard;
+ newg->stackbase = (uintptr)stk + StackSystem + stacksize - sizeof(Stktop);
+ runtime·memclr((byte*)newg->stackbase, sizeof(Stktop));
}
return newg;
}
@@ -1221,7 +1392,7 @@ runtime·malg(int32 stacksize)
// functions that split the stack.
#pragma textflag 7
void
-runtime·newproc(int32 siz, byte* fn, ...)
+runtime·newproc(int32 siz, FuncVal* fn, ...)
{
byte *argp;
@@ -1237,7 +1408,7 @@ runtime·newproc(int32 siz, byte* fn, ...)
// address of the go statement that created this. The new g is put
// on the queue of g's waiting to run.
G*
-runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
+runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
{
byte *sp;
G *newg;
@@ -1254,23 +1425,21 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
if(siz > StackMin - 1024)
runtime·throw("runtime.newproc: function arguments too large for new goroutine");
- schedlock();
-
- if((newg = gfget()) != nil){
+ if((newg = gfget(m->p)) != nil) {
if(newg->stackguard - StackGuard != newg->stack0)
runtime·throw("invalid stack in newg");
} else {
newg = runtime·malg(StackMin);
+ runtime·lock(&runtime·sched);
if(runtime·lastg == nil)
runtime·allg = newg;
else
runtime·lastg->alllink = newg;
runtime·lastg = newg;
+ runtime·unlock(&runtime·sched);
}
- newg->status = Gwaiting;
- newg->waitreason = "new goroutine";
- sp = newg->stackbase;
+ sp = (byte*)newg->stackbase;
sp -= siz;
runtime·memmove(sp, argp, narg);
if(thechar == '5') {
@@ -1279,318 +1448,88 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
*(void**)sp = nil;
}
- newg->sched.sp = sp;
+ newg->sched.sp = (uintptr)sp;
newg->sched.pc = (byte*)runtime·goexit;
newg->sched.g = newg;
- newg->entry = fn;
+ newg->fnstart = fn;
newg->gopc = (uintptr)callerpc;
-
- runtime·sched.gcount++;
- runtime·sched.goidgen++;
- newg->goid = runtime·sched.goidgen;
-
- newprocreadylocked(newg);
- schedunlock();
-
+ newg->status = Grunnable;
+ newg->goid = runtime·xadd64(&runtime·sched.goidgen, 1);
+ if(raceenabled)
+ newg->racectx = runtime·racegostart(callerpc);
+ runqput(m->p, newg);
+
+ if(runtime·atomicload(&runtime·sched.npidle) != 0 && runtime·atomicload(&runtime·sched.nmspinning) == 0 && fn->fn != runtime·main) // TODO: fast atomic
+ wakep();
return newg;
-//printf(" goid=%d\n", newg->goid);
}
-// Create a new deferred function fn with siz bytes of arguments.
-// The compiler turns a defer statement into a call to this.
-// Cannot split the stack because it assumes that the arguments
-// are available sequentially after &fn; they would not be
-// copied if a stack split occurred. It's OK for this to call
-// functions that split the stack.
-#pragma textflag 7
-uintptr
-runtime·deferproc(int32 siz, byte* fn, ...)
-{
- Defer *d;
-
- d = runtime·malloc(sizeof(*d) + siz - sizeof(d->args));
- d->fn = fn;
- d->siz = siz;
- d->pc = runtime·getcallerpc(&siz);
- if(thechar == '5')
- d->argp = (byte*)(&fn+2); // skip caller's saved link register
- else
- d->argp = (byte*)(&fn+1);
- runtime·memmove(d->args, d->argp, d->siz);
-
- d->link = g->defer;
- g->defer = d;
-
- // deferproc returns 0 normally.
- // a deferred func that stops a panic
- // makes the deferproc return 1.
- // the code the compiler generates always
- // checks the return value and jumps to the
- // end of the function if deferproc returns != 0.
- return 0;
-}
-
-// Run a deferred function if there is one.
-// The compiler inserts a call to this at the end of any
-// function which calls defer.
-// If there is a deferred function, this will call runtime·jmpdefer,
-// which will jump to the deferred function such that it appears
-// to have been called by the caller of deferreturn at the point
-// just before deferreturn was called. The effect is that deferreturn
-// is called again and again until there are no more deferred functions.
-// Cannot split the stack because we reuse the caller's frame to
-// call the deferred function.
-#pragma textflag 7
-void
-runtime·deferreturn(uintptr arg0)
-{
- Defer *d;
- byte *argp, *fn;
-
- d = g->defer;
- if(d == nil)
- return;
- argp = (byte*)&arg0;
- if(d->argp != argp)
- return;
- runtime·memmove(argp, d->args, d->siz);
- g->defer = d->link;
- fn = d->fn;
- if(!d->nofree)
- runtime·free(d);
- runtime·jmpdefer(fn, argp);
-}
-
-// Run all deferred functions for the current goroutine.
+// Put on gfree list.
+// If local list is too long, transfer a batch to the global list.
static void
-rundefer(void)
+gfput(P *p, G *gp)
{
- Defer *d;
-
- while((d = g->defer) != nil) {
- g->defer = d->link;
- reflect·call(d->fn, d->args, d->siz);
- if(!d->nofree)
- runtime·free(d);
+ if(gp->stackguard - StackGuard != gp->stack0)
+ runtime·throw("invalid stack in gfput");
+ gp->schedlink = p->gfree;
+ p->gfree = gp;
+ p->gfreecnt++;
+ if(p->gfreecnt >= 64) {
+ runtime·lock(&runtime·sched.gflock);
+ while(p->gfreecnt >= 32) {
+ p->gfreecnt--;
+ gp = p->gfree;
+ p->gfree = gp->schedlink;
+ gp->schedlink = runtime·sched.gfree;
+ runtime·sched.gfree = gp;
+ }
+ runtime·unlock(&runtime·sched.gflock);
}
}
-// Free stack frames until we hit the last one
-// or until we find the one that contains the argp.
-static void
-unwindstack(G *gp, byte *sp)
+// Get from gfree list.
+// If local list is empty, grab a batch from global list.
+static G*
+gfget(P *p)
{
- Stktop *top;
- byte *stk;
-
- // Must be called from a different goroutine, usually m->g0.
- if(g == gp)
- runtime·throw("unwindstack on self");
+ G *gp;
- while((top = (Stktop*)gp->stackbase) != nil && top->stackbase != nil) {
- stk = gp->stackguard - StackGuard;
- if(stk <= sp && sp < gp->stackbase)
- break;
- gp->stackbase = top->stackbase;
- gp->stackguard = top->stackguard;
- if(top->free != 0)
- runtime·stackfree(stk, top->free);
+retry:
+ gp = p->gfree;
+ if(gp == nil && runtime·sched.gfree) {
+ runtime·lock(&runtime·sched.gflock);
+ while(p->gfreecnt < 32 && runtime·sched.gfree) {
+ p->gfreecnt++;
+ gp = runtime·sched.gfree;
+ runtime·sched.gfree = gp->schedlink;
+ gp->schedlink = p->gfree;
+ p->gfree = gp;
+ }
+ runtime·unlock(&runtime·sched.gflock);
+ goto retry;
}
-
- if(sp != nil && (sp < gp->stackguard - StackGuard || gp->stackbase < sp)) {
- runtime·printf("recover: %p not in [%p, %p]\n", sp, gp->stackguard - StackGuard, gp->stackbase);
- runtime·throw("bad unwindstack");
+ if(gp) {
+ p->gfree = gp->schedlink;
+ p->gfreecnt--;
}
+ return gp;
}
-// Print all currently active panics. Used when crashing.
+// Purge all cached G's from gfree list to the global list.
static void
-printpanics(Panic *p)
-{
- if(p->link) {
- printpanics(p->link);
- runtime·printf("\t");
- }
- runtime·printf("panic: ");
- runtime·printany(p->arg);
- if(p->recovered)
- runtime·printf(" [recovered]");
- runtime·printf("\n");
-}
-
-static void recovery(G*);
-
-// The implementation of the predeclared function panic.
-void
-runtime·panic(Eface e)
+gfpurge(P *p)
{
- Defer *d;
- Panic *p;
-
- p = runtime·mal(sizeof *p);
- p->arg = e;
- p->link = g->panic;
- p->stackbase = g->stackbase;
- g->panic = p;
+ G *gp;
- for(;;) {
- d = g->defer;
- if(d == nil)
- break;
- // take defer off list in case of recursive panic
- g->defer = d->link;
- g->ispanic = true; // rock for newstack, where reflect.call ends up
- reflect·call(d->fn, d->args, d->siz);
- if(p->recovered) {
- g->panic = p->link;
- if(g->panic == nil) // must be done with signal
- g->sig = 0;
- runtime·free(p);
- // put recovering defer back on list
- // for scheduler to find.
- d->link = g->defer;
- g->defer = d;
- runtime·mcall(recovery);
- runtime·throw("recovery failed"); // mcall should not return
- }
- if(!d->nofree)
- runtime·free(d);
+ runtime·lock(&runtime·sched.gflock);
+ while(p->gfreecnt) {
+ p->gfreecnt--;
+ gp = p->gfree;
+ p->gfree = gp->schedlink;
+ gp->schedlink = runtime·sched.gfree;
+ runtime·sched.gfree = gp;
}
-
- // ran out of deferred calls - old-school panic now
- runtime·startpanic();
- printpanics(g->panic);
- runtime·dopanic(0);
-}
-
-// Unwind the stack after a deferred function calls recover
-// after a panic. Then arrange to continue running as though
-// the caller of the deferred function returned normally.
-static void
-recovery(G *gp)
-{
- Defer *d;
-
- // Rewind gp's stack; we're running on m->g0's stack.
- d = gp->defer;
- gp->defer = d->link;
-
- // Unwind to the stack frame with d's arguments in it.
- unwindstack(gp, d->argp);
-
- // Make the deferproc for this d return again,
- // this time returning 1. The calling function will
- // jump to the standard return epilogue.
- // The -2*sizeof(uintptr) makes up for the
- // two extra words that are on the stack at
- // each call to deferproc.
- // (The pc we're returning to does pop pop
- // before it tests the return value.)
- // On the arm there are 2 saved LRs mixed in too.
- if(thechar == '5')
- gp->sched.sp = (byte*)d->argp - 4*sizeof(uintptr);
- else
- gp->sched.sp = (byte*)d->argp - 2*sizeof(uintptr);
- gp->sched.pc = d->pc;
- if(!d->nofree)
- runtime·free(d);
- runtime·gogo(&gp->sched, 1);
-}
-
-// The implementation of the predeclared function recover.
-// Cannot split the stack because it needs to reliably
-// find the stack segment of its caller.
-#pragma textflag 7
-void
-runtime·recover(byte *argp, Eface ret)
-{
- Stktop *top, *oldtop;
- Panic *p;
-
- // Must be a panic going on.
- if((p = g->panic) == nil || p->recovered)
- goto nomatch;
-
- // Frame must be at the top of the stack segment,
- // because each deferred call starts a new stack
- // segment as a side effect of using reflect.call.
- // (There has to be some way to remember the
- // variable argument frame size, and the segment
- // code already takes care of that for us, so we
- // reuse it.)
- //
- // As usual closures complicate things: the fp that
- // the closure implementation function claims to have
- // is where the explicit arguments start, after the
- // implicit pointer arguments and PC slot.
- // If we're on the first new segment for a closure,
- // then fp == top - top->args is correct, but if
- // the closure has its own big argument frame and
- // allocated a second segment (see below),
- // the fp is slightly above top - top->args.
- // That condition can't happen normally though
- // (stack pointers go down, not up), so we can accept
- // any fp between top and top - top->args as
- // indicating the top of the segment.
- top = (Stktop*)g->stackbase;
- if(argp < (byte*)top - top->argsize || (byte*)top < argp)
- goto nomatch;
-
- // The deferred call makes a new segment big enough
- // for the argument frame but not necessarily big
- // enough for the function's local frame (size unknown
- // at the time of the call), so the function might have
- // made its own segment immediately. If that's the
- // case, back top up to the older one, the one that
- // reflect.call would have made for the panic.
- //
- // The fp comparison here checks that the argument
- // frame that was copied during the split (the top->args
- // bytes above top->fp) abuts the old top of stack.
- // This is a correct test for both closure and non-closure code.
- oldtop = (Stktop*)top->stackbase;
- if(oldtop != nil && top->argp == (byte*)oldtop - top->argsize)
- top = oldtop;
-
- // Now we have the segment that was created to
- // run this call. It must have been marked as a panic segment.
- if(!top->panic)
- goto nomatch;
-
- // Okay, this is the top frame of a deferred call
- // in response to a panic. It can see the panic argument.
- p->recovered = 1;
- ret = p->arg;
- FLUSH(&ret);
- return;
-
-nomatch:
- ret.type = nil;
- ret.data = nil;
- FLUSH(&ret);
-}
-
-
-// Put on gfree list. Sched must be locked.
-static void
-gfput(G *g)
-{
- if(g->stackguard - StackGuard != g->stack0)
- runtime·throw("invalid stack in gfput");
- g->schedlink = runtime·sched.gfree;
- runtime·sched.gfree = g;
-}
-
-// Get from gfree list. Sched must be locked.
-static G*
-gfget(void)
-{
- G *g;
-
- g = runtime·sched.gfree;
- if(g)
- runtime·sched.gfree = g->schedlink;
- return g;
+ runtime·unlock(&runtime·sched.gflock);
}
void
@@ -1600,80 +1539,85 @@ runtime·Breakpoint(void)
}
void
-runtime·Goexit(void)
-{
- rundefer();
- runtime·goexit();
-}
-
-void
runtime·Gosched(void)
{
runtime·gosched();
}
// Implementation of runtime.GOMAXPROCS.
-// delete when scheduler is stronger
+// delete when scheduler is even stronger
int32
runtime·gomaxprocsfunc(int32 n)
{
int32 ret;
- uint32 v;
- schedlock();
+ if(n > MaxGomaxprocs)
+ n = MaxGomaxprocs;
+ runtime·lock(&runtime·sched);
ret = runtime·gomaxprocs;
- if(n <= 0)
- n = ret;
- if(n > maxgomaxprocs)
- n = maxgomaxprocs;
- runtime·gomaxprocs = n;
- if(runtime·gomaxprocs > 1)
- runtime·singleproc = false;
- if(runtime·gcwaiting != 0) {
- if(atomic_mcpumax(runtime·sched.atomic) != 1)
- runtime·throw("invalid mcpumax during gc");
- schedunlock();
+ if(n <= 0 || n == ret) {
+ runtime·unlock(&runtime·sched);
return ret;
}
+ runtime·unlock(&runtime·sched);
- setmcpumax(n);
+ runtime·semacquire(&runtime·worldsema);
+ m->gcing = 1;
+ runtime·stoptheworld();
+ newprocs = n;
+ m->gcing = 0;
+ runtime·semrelease(&runtime·worldsema);
+ runtime·starttheworld();
- // If there are now fewer allowed procs
- // than procs running, stop.
- v = runtime·atomicload(&runtime·sched.atomic);
- if(atomic_mcpu(v) > n) {
- schedunlock();
- runtime·gosched();
- return ret;
- }
- // handle more procs
- matchmg();
- schedunlock();
return ret;
}
-void
-runtime·LockOSThread(void)
+static void
+LockOSThread(void)
{
- if(m == &runtime·m0 && runtime·sched.init) {
- runtime·sched.lockmain = true;
- return;
- }
m->lockedg = g;
g->lockedm = m;
}
void
-runtime·UnlockOSThread(void)
+runtime·LockOSThread(void)
{
- if(m == &runtime·m0 && runtime·sched.init) {
- runtime·sched.lockmain = false;
+ m->locked |= LockExternal;
+ LockOSThread();
+}
+
+void
+runtime·lockOSThread(void)
+{
+ m->locked += LockInternal;
+ LockOSThread();
+}
+
+static void
+UnlockOSThread(void)
+{
+ if(m->locked != 0)
return;
- }
m->lockedg = nil;
g->lockedm = nil;
}
+void
+runtime·UnlockOSThread(void)
+{
+ m->locked &= ~LockExternal;
+ UnlockOSThread();
+}
+
+void
+runtime·unlockOSThread(void)
+{
+ if(m->locked < LockInternal)
+ runtime·throw("runtime: internal error: misuse of lockOSThread/unlockOSThread");
+ m->locked -= LockInternal;
+ UnlockOSThread();
+}
+
bool
runtime·lockedOSThread(void)
{
@@ -1697,16 +1641,31 @@ runtime·mid(uint32 ret)
}
void
-runtime·NumGoroutine(int32 ret)
+runtime·NumGoroutine(intgo ret)
{
- ret = runtime·sched.gcount;
+ ret = runtime·gcount();
FLUSH(&ret);
}
int32
runtime·gcount(void)
{
- return runtime·sched.gcount;
+ G *gp;
+ int32 n, s;
+
+ n = 0;
+ runtime·lock(&runtime·sched);
+ // TODO(dvyukov): runtime.NumGoroutine() is O(N).
+ // We do not want to increment/decrement centralized counter in newproc/goexit,
+ // just to make runtime.NumGoroutine() faster.
+ // Compromise solution is to introduce per-P counters of active goroutines.
+ for(gp = runtime·allg; gp; gp = gp->alllink) {
+ s = gp->status;
+ if(s == Grunnable || s == Grunning || s == Gsyscall || s == Gwaiting)
+ n++;
+ }
+ runtime·unlock(&runtime·sched);
+ return n;
}
int32
@@ -1740,6 +1699,9 @@ runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp)
{
int32 n;
+ // Windows does profiling in a dedicated thread w/o m.
+ if(!Windows && (m == nil || m->mcache == nil))
+ return;
if(prof.fn == nil || prof.hz == 0)
return;
@@ -1783,27 +1745,533 @@ runtime·setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz)
runtime·resetcpuprofiler(hz);
}
-void (*libcgo_setenv)(byte**);
+// Change number of processors. The world is stopped, sched is locked.
+static void
+procresize(int32 new)
+{
+ int32 i, old;
+ G *gp;
+ P *p;
+
+ old = runtime·gomaxprocs;
+ if(old < 0 || old > MaxGomaxprocs || new <= 0 || new >MaxGomaxprocs)
+ runtime·throw("procresize: invalid arg");
+ // initialize new P's
+ for(i = 0; i < new; i++) {
+ p = runtime·allp[i];
+ if(p == nil) {
+ p = (P*)runtime·mallocgc(sizeof(*p), 0, 0, 1);
+ p->status = Pgcstop;
+ runtime·atomicstorep(&runtime·allp[i], p);
+ }
+ if(p->mcache == nil) {
+ if(old==0 && i==0)
+ p->mcache = m->mcache; // bootstrap
+ else
+ p->mcache = runtime·allocmcache();
+ }
+ if(p->runq == nil) {
+ p->runqsize = 128;
+ p->runq = (G**)runtime·mallocgc(p->runqsize*sizeof(G*), 0, 0, 1);
+ }
+ }
+
+ // redistribute runnable G's evenly
+ for(i = 0; i < old; i++) {
+ p = runtime·allp[i];
+ while(gp = runqget(p))
+ globrunqput(gp);
+ }
+ // start at 1 because current M already executes some G and will acquire allp[0] below,
+ // so if we have a spare G we want to put it into allp[1].
+ for(i = 1; runtime·sched.runqhead; i++) {
+ gp = runtime·sched.runqhead;
+ runtime·sched.runqhead = gp->schedlink;
+ runqput(runtime·allp[i%new], gp);
+ }
+ runtime·sched.runqtail = nil;
+ runtime·sched.runqsize = 0;
+
+ // free unused P's
+ for(i = new; i < old; i++) {
+ p = runtime·allp[i];
+ runtime·freemcache(p->mcache);
+ p->mcache = nil;
+ gfpurge(p);
+ p->status = Pdead;
+ // can't free P itself because it can be referenced by an M in syscall
+ }
+
+ if(m->p)
+ m->p->m = nil;
+ m->p = nil;
+ m->mcache = nil;
+ p = runtime·allp[0];
+ p->m = nil;
+ p->status = Pidle;
+ acquirep(p);
+ for(i = new-1; i > 0; i--) {
+ p = runtime·allp[i];
+ p->status = Pidle;
+ pidleput(p);
+ }
+ runtime·singleproc = new == 1;
+ runtime·atomicstore((uint32*)&runtime·gomaxprocs, new);
+}
+
+// Associate p and the current m.
+static void
+acquirep(P *p)
+{
+ if(m->p || m->mcache)
+ runtime·throw("acquirep: already in go");
+ if(p->m || p->status != Pidle) {
+ runtime·printf("acquirep: p->m=%p(%d) p->status=%d\n", p->m, p->m ? p->m->id : 0, p->status);
+ runtime·throw("acquirep: invalid p state");
+ }
+ m->mcache = p->mcache;
+ m->p = p;
+ p->m = m;
+ p->status = Prunning;
+}
-// Update the C environment if cgo is loaded.
-// Called from syscall.Setenv.
-void
-syscall·setenv_c(String k, String v)
+// Disassociate p and the current m.
+static P*
+releasep(void)
{
- byte *arg[2];
+ P *p;
+
+ if(m->p == nil || m->mcache == nil)
+ runtime·throw("releasep: invalid arg");
+ p = m->p;
+ if(p->m != m || p->mcache != m->mcache || p->status != Prunning) {
+ runtime·printf("releasep: m=%p m->p=%p p->m=%p m->mcache=%p p->mcache=%p p->status=%d\n",
+ m, m->p, p->m, m->mcache, p->mcache, p->status);
+ runtime·throw("releasep: invalid p state");
+ }
+ m->p = nil;
+ m->mcache = nil;
+ p->m = nil;
+ p->status = Pidle;
+ return p;
+}
- if(libcgo_setenv == nil)
+static void
+inclocked(int32 v)
+{
+ runtime·lock(&runtime·sched);
+ runtime·sched.mlocked += v;
+ if(v > 0)
+ checkdead();
+ runtime·unlock(&runtime·sched);
+}
+
+// Check for deadlock situation.
+// The check is based on number of running M's, if 0 -> deadlock.
+static void
+checkdead(void)
+{
+ G *gp;
+ int32 run, grunning, s;
+
+ // -1 for sysmon
+ run = runtime·sched.mcount - runtime·sched.nmidle - runtime·sched.mlocked - 1;
+ if(run > 0)
return;
+ if(run < 0) {
+ runtime·printf("checkdead: nmidle=%d mlocked=%d mcount=%d\n",
+ runtime·sched.nmidle, runtime·sched.mlocked, runtime·sched.mcount);
+ runtime·throw("checkdead: inconsistent counts");
+ }
+ grunning = 0;
+ for(gp = runtime·allg; gp; gp = gp->alllink) {
+ if(gp == scvg)
+ continue;
+ s = gp->status;
+ if(s == Gwaiting)
+ grunning++;
+ else if(s == Grunnable || s == Grunning || s == Gsyscall) {
+ runtime·printf("checkdead: find g %D in status %d\n", gp->goid, s);
+ runtime·throw("checkdead: runnable g");
+ }
+ }
+ if(grunning == 0) // possible if main goroutine calls runtime·Goexit()
+ runtime·exit(0);
+ m->throwing = -1; // do not dump full stacks
+ runtime·throw("all goroutines are asleep - deadlock!");
+}
+
+static void
+sysmon(void)
+{
+ uint32 idle, delay;
+ uint32 ticks[MaxGomaxprocs];
+
+ idle = 0; // how many cycles in succession we had not wokeup somebody
+ delay = 0;
+ for(;;) {
+ if(idle == 0) // start with 20us sleep...
+ delay = 20;
+ else if(idle > 50) // start doubling the sleep after 1ms...
+ delay *= 2;
+ if(delay > 10*1000) // up to 10ms
+ delay = 10*1000;
+ runtime·usleep(delay);
+ if(runtime·gcwaiting || runtime·atomicload(&runtime·sched.npidle) == runtime·gomaxprocs) { // TODO: fast atomic
+ runtime·lock(&runtime·sched);
+ if(runtime·atomicload(&runtime·gcwaiting) || runtime·atomicload(&runtime·sched.npidle) == runtime·gomaxprocs) {
+ runtime·atomicstore(&runtime·sched.sysmonwait, 1);
+ runtime·unlock(&runtime·sched);
+ runtime·notesleep(&runtime·sched.sysmonnote);
+ runtime·noteclear(&runtime·sched.sysmonnote);
+ idle = 0;
+ delay = 20;
+ } else
+ runtime·unlock(&runtime·sched);
+ }
+ if(retake(ticks))
+ idle = 0;
+ else
+ idle++;
+ }
+}
+
+static uint32
+retake(uint32 *ticks)
+{
+ uint32 i, s, n;
+ int64 t;
+ P *p;
+
+ n = 0;
+ for(i = 0; i < runtime·gomaxprocs; i++) {
+ p = runtime·allp[i];
+ if(p==nil)
+ continue;
+ t = p->tick;
+ if(ticks[i] != t) {
+ ticks[i] = t;
+ continue;
+ }
+ s = p->status;
+ if(s != Psyscall)
+ continue;
+ if(p->runqhead == p->runqtail && runtime·atomicload(&runtime·sched.nmspinning) + runtime·atomicload(&runtime·sched.npidle) > 0) // TODO: fast atomic
+ continue;
+ // Need to increment number of locked M's before the CAS.
+ // Otherwise the M from which we retake can exit the syscall,
+ // increment nmidle and report deadlock.
+ inclocked(-1);
+ if(runtime·cas(&p->status, s, Pidle)) {
+ n++;
+ handoffp(p);
+ }
+ inclocked(1);
+ }
+ return n;
+}
+
+// Put mp on midle list.
+// Sched must be locked.
+static void
+mput(M *mp)
+{
+ mp->schedlink = runtime·sched.midle;
+ runtime·sched.midle = mp;
+ runtime·sched.nmidle++;
+ checkdead();
+}
+
+// Try to get an m from midle list.
+// Sched must be locked.
+static M*
+mget(void)
+{
+ M *mp;
+
+ if((mp = runtime·sched.midle) != nil){
+ runtime·sched.midle = mp->schedlink;
+ runtime·sched.nmidle--;
+ }
+ return mp;
+}
+
+// Put gp on the global runnable queue.
+// Sched must be locked.
+static void
+globrunqput(G *gp)
+{
+ gp->schedlink = nil;
+ if(runtime·sched.runqtail)
+ runtime·sched.runqtail->schedlink = gp;
+ else
+ runtime·sched.runqhead = gp;
+ runtime·sched.runqtail = gp;
+ runtime·sched.runqsize++;
+}
+
+// Try get a batch of G's from the global runnable queue.
+// Sched must be locked.
+static G*
+globrunqget(P *p)
+{
+ G *gp, *gp1;
+ int32 n;
+
+ if(runtime·sched.runqsize == 0)
+ return nil;
+ n = runtime·sched.runqsize/runtime·gomaxprocs+1;
+ if(n > runtime·sched.runqsize)
+ n = runtime·sched.runqsize;
+ runtime·sched.runqsize -= n;
+ if(runtime·sched.runqsize == 0)
+ runtime·sched.runqtail = nil;
+ gp = runtime·sched.runqhead;
+ runtime·sched.runqhead = gp->schedlink;
+ n--;
+ while(n--) {
+ gp1 = runtime·sched.runqhead;
+ runtime·sched.runqhead = gp1->schedlink;
+ runqput(p, gp1);
+ }
+ return gp;
+}
+
+// Put p to on pidle list.
+// Sched must be locked.
+static void
+pidleput(P *p)
+{
+ p->link = runtime·sched.pidle;
+ runtime·sched.pidle = p;
+ runtime·xadd(&runtime·sched.npidle, 1); // TODO: fast atomic
+}
+
+// Try get a p from pidle list.
+// Sched must be locked.
+static P*
+pidleget(void)
+{
+ P *p;
- arg[0] = runtime·malloc(k.len + 1);
- runtime·memmove(arg[0], k.str, k.len);
- arg[0][k.len] = 0;
+ p = runtime·sched.pidle;
+ if(p) {
+ runtime·sched.pidle = p->link;
+ runtime·xadd(&runtime·sched.npidle, -1); // TODO: fast atomic
+ }
+ return p;
+}
- arg[1] = runtime·malloc(v.len + 1);
- runtime·memmove(arg[1], v.str, v.len);
- arg[1][v.len] = 0;
+// Put g on local runnable queue.
+// TODO(dvyukov): consider using lock-free queue.
+static void
+runqput(P *p, G *gp)
+{
+ int32 h, t, s;
+
+ runtime·lock(p);
+retry:
+ h = p->runqhead;
+ t = p->runqtail;
+ s = p->runqsize;
+ if(t == h-1 || (h == 0 && t == s-1)) {
+ runqgrow(p);
+ goto retry;
+ }
+ p->runq[t++] = gp;
+ if(t == s)
+ t = 0;
+ p->runqtail = t;
+ runtime·unlock(p);
+}
+
+// Get g from local runnable queue.
+static G*
+runqget(P *p)
+{
+ G *gp;
+ int32 t, h, s;
+
+ if(p->runqhead == p->runqtail)
+ return nil;
+ runtime·lock(p);
+ h = p->runqhead;
+ t = p->runqtail;
+ s = p->runqsize;
+ if(t == h) {
+ runtime·unlock(p);
+ return nil;
+ }
+ gp = p->runq[h++];
+ if(h == s)
+ h = 0;
+ p->runqhead = h;
+ runtime·unlock(p);
+ return gp;
+}
- runtime·asmcgocall((void*)libcgo_setenv, arg);
- runtime·free(arg[0]);
- runtime·free(arg[1]);
+// Grow local runnable queue.
+// TODO(dvyukov): consider using fixed-size array
+// and transfer excess to the global list (local queue can grow way too big).
+static void
+runqgrow(P *p)
+{
+ G **q;
+ int32 s, t, h, t2;
+
+ h = p->runqhead;
+ t = p->runqtail;
+ s = p->runqsize;
+ t2 = 0;
+ q = runtime·malloc(2*s*sizeof(*q));
+ while(t != h) {
+ q[t2++] = p->runq[h++];
+ if(h == s)
+ h = 0;
+ }
+ runtime·free(p->runq);
+ p->runq = q;
+ p->runqhead = 0;
+ p->runqtail = t2;
+ p->runqsize = 2*s;
}
+
+// Steal half of elements from local runnable queue of p2
+// and put onto local runnable queue of p.
+// Returns one of the stolen elements (or nil if failed).
+static G*
+runqsteal(P *p, P *p2)
+{
+ G *gp, *gp1;
+ int32 t, h, s, t2, h2, s2, c, i;
+
+ if(p2->runqhead == p2->runqtail)
+ return nil;
+ // sort locks to prevent deadlocks
+ if(p < p2)
+ runtime·lock(p);
+ runtime·lock(p2);
+ if(p2->runqhead == p2->runqtail) {
+ runtime·unlock(p2);
+ if(p < p2)
+ runtime·unlock(p);
+ return nil;
+ }
+ if(p >= p2)
+ runtime·lock(p);
+ // now we've locked both queues and know the victim is not empty
+ h = p->runqhead;
+ t = p->runqtail;
+ s = p->runqsize;
+ h2 = p2->runqhead;
+ t2 = p2->runqtail;
+ s2 = p2->runqsize;
+ gp = p2->runq[h2++]; // return value
+ if(h2 == s2)
+ h2 = 0;
+ // steal roughly half
+ if(t2 > h2)
+ c = (t2 - h2) / 2;
+ else
+ c = (s2 - h2 + t2) / 2;
+ // copy
+ for(i = 0; i != c; i++) {
+ // the target queue is full?
+ if(t == h-1 || (h == 0 && t == s-1))
+ break;
+ // the victim queue is empty?
+ if(t2 == h2)
+ break;
+ gp1 = p2->runq[h2++];
+ if(h2 == s2)
+ h2 = 0;
+ p->runq[t++] = gp1;
+ if(t == s)
+ t = 0;
+ }
+ p->runqtail = t;
+ p2->runqhead = h2;
+ runtime·unlock(p2);
+ runtime·unlock(p);
+ return gp;
+}
+
+void
+runtime·testSchedLocalQueue(void)
+{
+ P p;
+ G gs[1000];
+ int32 i, j;
+
+ runtime·memclr((byte*)&p, sizeof(p));
+ p.runqsize = 1;
+ p.runqhead = 0;
+ p.runqtail = 0;
+ p.runq = runtime·malloc(p.runqsize*sizeof(*p.runq));
+
+ for(i = 0; i < nelem(gs); i++) {
+ if(runqget(&p) != nil)
+ runtime·throw("runq is not empty initially");
+ for(j = 0; j < i; j++)
+ runqput(&p, &gs[i]);
+ for(j = 0; j < i; j++) {
+ if(runqget(&p) != &gs[i]) {
+ runtime·printf("bad element at iter %d/%d\n", i, j);
+ runtime·throw("bad element");
+ }
+ }
+ if(runqget(&p) != nil)
+ runtime·throw("runq is not empty afterwards");
+ }
+}
+
+void
+runtime·testSchedLocalQueueSteal(void)
+{
+ P p1, p2;
+ G gs[1000], *gp;
+ int32 i, j, s;
+
+ runtime·memclr((byte*)&p1, sizeof(p1));
+ p1.runqsize = 1;
+ p1.runqhead = 0;
+ p1.runqtail = 0;
+ p1.runq = runtime·malloc(p1.runqsize*sizeof(*p1.runq));
+
+ runtime·memclr((byte*)&p2, sizeof(p2));
+ p2.runqsize = nelem(gs);
+ p2.runqhead = 0;
+ p2.runqtail = 0;
+ p2.runq = runtime·malloc(p2.runqsize*sizeof(*p2.runq));
+
+ for(i = 0; i < nelem(gs); i++) {
+ for(j = 0; j < i; j++) {
+ gs[j].sig = 0;
+ runqput(&p1, &gs[j]);
+ }
+ gp = runqsteal(&p2, &p1);
+ s = 0;
+ if(gp) {
+ s++;
+ gp->sig++;
+ }
+ while(gp = runqget(&p2)) {
+ s++;
+ gp->sig++;
+ }
+ while(gp = runqget(&p1))
+ gp->sig++;
+ for(j = 0; j < i; j++) {
+ if(gs[j].sig != 1) {
+ runtime·printf("bad element %d(%d) at iter %d\n", j, gs[j].sig, i);
+ runtime·throw("bad element");
+ }
+ }
+ if(s != i/2 && s != i/2+1) {
+ runtime·printf("bad steal %d, want %d or %d, iter %d\n",
+ s, i/2, i/2+1, i);
+ runtime·throw("bad steal");
+ }
+ }
+}
+
diff --git a/src/pkg/runtime/proc_test.go b/src/pkg/runtime/proc_test.go
index 32111080a..21fb9c2f7 100644
--- a/src/pkg/runtime/proc_test.go
+++ b/src/pkg/runtime/proc_test.go
@@ -5,9 +5,11 @@
package runtime_test
import (
+ "math"
"runtime"
"sync/atomic"
"testing"
+ "time"
)
var stop = make(chan bool, 1)
@@ -22,8 +24,7 @@ func perpetuumMobile() {
func TestStopTheWorldDeadlock(t *testing.T) {
if testing.Short() {
- t.Logf("skipping during short test")
- return
+ t.Skip("skipping during short test")
}
maxprocs := runtime.GOMAXPROCS(3)
compl := make(chan bool, 2)
@@ -46,6 +47,66 @@ func TestStopTheWorldDeadlock(t *testing.T) {
runtime.GOMAXPROCS(maxprocs)
}
+func TestYieldProgress(t *testing.T) {
+ testYieldProgress(t, false)
+}
+
+func TestYieldLockedProgress(t *testing.T) {
+ testYieldProgress(t, true)
+}
+
+func testYieldProgress(t *testing.T, locked bool) {
+ c := make(chan bool)
+ cack := make(chan bool)
+ go func() {
+ if locked {
+ runtime.LockOSThread()
+ }
+ for {
+ select {
+ case <-c:
+ cack <- true
+ return
+ default:
+ runtime.Gosched()
+ }
+ }
+ }()
+ time.Sleep(10 * time.Millisecond)
+ c <- true
+ <-cack
+}
+
+func TestYieldLocked(t *testing.T) {
+ const N = 10
+ c := make(chan bool)
+ go func() {
+ runtime.LockOSThread()
+ for i := 0; i < N; i++ {
+ runtime.Gosched()
+ time.Sleep(time.Millisecond)
+ }
+ c <- true
+ // runtime.UnlockOSThread() is deliberately omitted
+ }()
+ <-c
+}
+
+func TestBlockLocked(t *testing.T) {
+ const N = 10
+ c := make(chan bool)
+ go func() {
+ runtime.LockOSThread()
+ for i := 0; i < N; i++ {
+ c <- true
+ }
+ runtime.UnlockOSThread()
+ }()
+ for i := 0; i < N; i++ {
+ <-c
+ }
+}
+
func stackGrowthRecursive(i int) {
var pad [128]uint64
if i != 0 && pad[0] == 0 {
@@ -53,7 +114,15 @@ func stackGrowthRecursive(i int) {
}
}
-func BenchmarkStackGrowth(b *testing.B) {
+func TestSchedLocalQueue(t *testing.T) {
+ runtime.TestSchedLocalQueue1()
+}
+
+func TestSchedLocalQueueSteal(t *testing.T) {
+ runtime.TestSchedLocalQueueSteal1()
+}
+
+func benchmarkStackGrowth(b *testing.B, rec int) {
const CallsPerSched = 1000
procs := runtime.GOMAXPROCS(-1)
N := int32(b.N / CallsPerSched)
@@ -63,7 +132,7 @@ func BenchmarkStackGrowth(b *testing.B) {
for atomic.AddInt32(&N, -1) >= 0 {
runtime.Gosched()
for g := 0; g < CallsPerSched; g++ {
- stackGrowthRecursive(10)
+ stackGrowthRecursive(rec)
}
}
c <- true
@@ -74,32 +143,33 @@ func BenchmarkStackGrowth(b *testing.B) {
}
}
+func BenchmarkStackGrowth(b *testing.B) {
+ benchmarkStackGrowth(b, 10)
+}
+
+func BenchmarkStackGrowthDeep(b *testing.B) {
+ benchmarkStackGrowth(b, 1024)
+}
+
func BenchmarkSyscall(b *testing.B) {
- const CallsPerSched = 1000
- procs := runtime.GOMAXPROCS(-1)
- N := int32(b.N / CallsPerSched)
- c := make(chan bool, procs)
- for p := 0; p < procs; p++ {
- go func() {
- for atomic.AddInt32(&N, -1) >= 0 {
- runtime.Gosched()
- for g := 0; g < CallsPerSched; g++ {
- runtime.Entersyscall()
- runtime.Exitsyscall()
- }
- }
- c <- true
- }()
- }
- for p := 0; p < procs; p++ {
- <-c
- }
+ benchmarkSyscall(b, 0, 1)
}
func BenchmarkSyscallWork(b *testing.B) {
+ benchmarkSyscall(b, 100, 1)
+}
+
+func BenchmarkSyscallExcess(b *testing.B) {
+ benchmarkSyscall(b, 0, 4)
+}
+
+func BenchmarkSyscallExcessWork(b *testing.B) {
+ benchmarkSyscall(b, 100, 4)
+}
+
+func benchmarkSyscall(b *testing.B, work, excess int) {
const CallsPerSched = 1000
- const LocalWork = 100
- procs := runtime.GOMAXPROCS(-1)
+ procs := runtime.GOMAXPROCS(-1) * excess
N := int32(b.N / CallsPerSched)
c := make(chan bool, procs)
for p := 0; p < procs; p++ {
@@ -109,7 +179,7 @@ func BenchmarkSyscallWork(b *testing.B) {
runtime.Gosched()
for g := 0; g < CallsPerSched; g++ {
runtime.Entersyscall()
- for i := 0; i < LocalWork; i++ {
+ for i := 0; i < work; i++ {
foo *= 2
foo /= 2
}
@@ -123,3 +193,93 @@ func BenchmarkSyscallWork(b *testing.B) {
<-c
}
}
+
+func BenchmarkCreateGoroutines(b *testing.B) {
+ benchmarkCreateGoroutines(b, 1)
+}
+
+func BenchmarkCreateGoroutinesParallel(b *testing.B) {
+ benchmarkCreateGoroutines(b, runtime.GOMAXPROCS(-1))
+}
+
+func benchmarkCreateGoroutines(b *testing.B, procs int) {
+ c := make(chan bool)
+ var f func(n int)
+ f = func(n int) {
+ if n == 0 {
+ c <- true
+ return
+ }
+ go f(n - 1)
+ }
+ for i := 0; i < procs; i++ {
+ go f(b.N / procs)
+ }
+ for i := 0; i < procs; i++ {
+ <-c
+ }
+}
+
+type Matrix [][]float64
+
+func BenchmarkMatmult(b *testing.B) {
+ b.StopTimer()
+ // matmult is O(N**3) but testing expects O(b.N),
+ // so we need to take cube root of b.N
+ n := int(math.Cbrt(float64(b.N))) + 1
+ A := makeMatrix(n)
+ B := makeMatrix(n)
+ C := makeMatrix(n)
+ b.StartTimer()
+ matmult(nil, A, B, C, 0, n, 0, n, 0, n, 8)
+}
+
+func makeMatrix(n int) Matrix {
+ m := make(Matrix, n)
+ for i := 0; i < n; i++ {
+ m[i] = make([]float64, n)
+ for j := 0; j < n; j++ {
+ m[i][j] = float64(i*n + j)
+ }
+ }
+ return m
+}
+
+func matmult(done chan<- struct{}, A, B, C Matrix, i0, i1, j0, j1, k0, k1, threshold int) {
+ di := i1 - i0
+ dj := j1 - j0
+ dk := k1 - k0
+ if di >= dj && di >= dk && di >= threshold {
+ // divide in two by y axis
+ mi := i0 + di/2
+ done1 := make(chan struct{}, 1)
+ go matmult(done1, A, B, C, i0, mi, j0, j1, k0, k1, threshold)
+ matmult(nil, A, B, C, mi, i1, j0, j1, k0, k1, threshold)
+ <-done1
+ } else if dj >= dk && dj >= threshold {
+ // divide in two by x axis
+ mj := j0 + dj/2
+ done1 := make(chan struct{}, 1)
+ go matmult(done1, A, B, C, i0, i1, j0, mj, k0, k1, threshold)
+ matmult(nil, A, B, C, i0, i1, mj, j1, k0, k1, threshold)
+ <-done1
+ } else if dk >= threshold {
+ // divide in two by "k" axis
+ // deliberately not parallel because of data races
+ mk := k0 + dk/2
+ matmult(nil, A, B, C, i0, i1, j0, j1, k0, mk, threshold)
+ matmult(nil, A, B, C, i0, i1, j0, j1, mk, k1, threshold)
+ } else {
+ // the matrices are small enough, compute directly
+ for i := i0; i < i1; i++ {
+ for j := j0; j < j1; j++ {
+ for k := k0; k < k1; k++ {
+ C[i][j] += A[i][k] * B[k][j]
+ }
+ }
+ }
+ }
+ if done != nil {
+ done <- struct{}{}
+ }
+}
diff --git a/src/pkg/runtime/race.c b/src/pkg/runtime/race.c
new file mode 100644
index 000000000..cfd97041a
--- /dev/null
+++ b/src/pkg/runtime/race.c
@@ -0,0 +1,350 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Implementation of the race detector API.
+// +build race
+
+#include "runtime.h"
+#include "arch_GOARCH.h"
+#include "malloc.h"
+#include "race.h"
+
+void runtime∕race·Initialize(uintptr *racectx);
+void runtime∕race·MapShadow(void *addr, uintptr size);
+void runtime∕race·Finalize(void);
+void runtime∕race·FinalizerGoroutine(uintptr racectx);
+void runtime∕race·Read(uintptr racectx, void *addr, void *pc);
+void runtime∕race·Write(uintptr racectx, void *addr, void *pc);
+void runtime∕race·ReadRange(uintptr racectx, void *addr, uintptr sz, uintptr step, void *pc);
+void runtime∕race·WriteRange(uintptr racectx, void *addr, uintptr sz, uintptr step, void *pc);
+void runtime∕race·FuncEnter(uintptr racectx, void *pc);
+void runtime∕race·FuncExit(uintptr racectx);
+void runtime∕race·Malloc(uintptr racectx, void *p, uintptr sz, void *pc);
+void runtime∕race·Free(void *p);
+void runtime∕race·GoStart(uintptr racectx, uintptr *chracectx, void *pc);
+void runtime∕race·GoEnd(uintptr racectx);
+void runtime∕race·Acquire(uintptr racectx, void *addr);
+void runtime∕race·Release(uintptr racectx, void *addr);
+void runtime∕race·ReleaseMerge(uintptr racectx, void *addr);
+
+extern byte noptrdata[];
+extern byte enoptrbss[];
+
+static bool onstack(uintptr argp);
+
+uintptr
+runtime·raceinit(void)
+{
+ uintptr racectx;
+
+ m->racecall = true;
+ runtime∕race·Initialize(&racectx);
+ runtime∕race·MapShadow(noptrdata, enoptrbss - noptrdata);
+ m->racecall = false;
+ return racectx;
+}
+
+void
+runtime·racefini(void)
+{
+ m->racecall = true;
+ runtime∕race·Finalize();
+ m->racecall = false;
+}
+
+void
+runtime·racemapshadow(void *addr, uintptr size)
+{
+ m->racecall = true;
+ runtime∕race·MapShadow(addr, size);
+ m->racecall = false;
+}
+
+// Called from instrumented code.
+// If we split stack, getcallerpc() can return runtime·lessstack().
+#pragma textflag 7
+void
+runtime·racewrite(uintptr addr)
+{
+ if(!onstack(addr)) {
+ m->racecall = true;
+ runtime∕race·Write(g->racectx, (void*)addr, runtime·getcallerpc(&addr));
+ m->racecall = false;
+ }
+}
+
+// Called from instrumented code.
+// If we split stack, getcallerpc() can return runtime·lessstack().
+#pragma textflag 7
+void
+runtime·raceread(uintptr addr)
+{
+ if(!onstack(addr)) {
+ m->racecall = true;
+ runtime∕race·Read(g->racectx, (void*)addr, runtime·getcallerpc(&addr));
+ m->racecall = false;
+ }
+}
+
+// Called from runtime·racefuncenter (assembly).
+#pragma textflag 7
+void
+runtime·racefuncenter1(uintptr pc)
+{
+ // If the caller PC is lessstack, use slower runtime·callers
+ // to walk across the stack split to find the real caller.
+ if(pc == (uintptr)runtime·lessstack)
+ runtime·callers(2, &pc, 1);
+
+ m->racecall = true;
+ runtime∕race·FuncEnter(g->racectx, (void*)pc);
+ m->racecall = false;
+}
+
+// Called from instrumented code.
+#pragma textflag 7
+void
+runtime·racefuncexit(void)
+{
+ m->racecall = true;
+ runtime∕race·FuncExit(g->racectx);
+ m->racecall = false;
+}
+
+void
+runtime·racemalloc(void *p, uintptr sz, void *pc)
+{
+ // use m->curg because runtime·stackalloc() is called from g0
+ if(m->curg == nil)
+ return;
+ m->racecall = true;
+ runtime∕race·Malloc(m->curg->racectx, p, sz, pc);
+ m->racecall = false;
+}
+
+void
+runtime·racefree(void *p)
+{
+ m->racecall = true;
+ runtime∕race·Free(p);
+ m->racecall = false;
+}
+
+uintptr
+runtime·racegostart(void *pc)
+{
+ uintptr racectx;
+
+ m->racecall = true;
+ runtime∕race·GoStart(g->racectx, &racectx, pc);
+ m->racecall = false;
+ return racectx;
+}
+
+void
+runtime·racegoend(void)
+{
+ m->racecall = true;
+ runtime∕race·GoEnd(g->racectx);
+ m->racecall = false;
+}
+
+static void
+memoryaccess(void *addr, uintptr callpc, uintptr pc, bool write)
+{
+ uintptr racectx;
+
+ if(!onstack((uintptr)addr)) {
+ m->racecall = true;
+ racectx = g->racectx;
+ if(callpc) {
+ if(callpc == (uintptr)runtime·lessstack)
+ runtime·callers(3, &callpc, 1);
+ runtime∕race·FuncEnter(racectx, (void*)callpc);
+ }
+ if(write)
+ runtime∕race·Write(racectx, addr, (void*)pc);
+ else
+ runtime∕race·Read(racectx, addr, (void*)pc);
+ if(callpc)
+ runtime∕race·FuncExit(racectx);
+ m->racecall = false;
+ }
+}
+
+void
+runtime·racewritepc(void *addr, void *callpc, void *pc)
+{
+ memoryaccess(addr, (uintptr)callpc, (uintptr)pc, true);
+}
+
+void
+runtime·racereadpc(void *addr, void *callpc, void *pc)
+{
+ memoryaccess(addr, (uintptr)callpc, (uintptr)pc, false);
+}
+
+static void
+rangeaccess(void *addr, uintptr size, uintptr step, uintptr callpc, uintptr pc, bool write)
+{
+ uintptr racectx;
+
+ if(!onstack((uintptr)addr)) {
+ m->racecall = true;
+ racectx = g->racectx;
+ if(callpc) {
+ if(callpc == (uintptr)runtime·lessstack)
+ runtime·callers(3, &callpc, 1);
+ runtime∕race·FuncEnter(racectx, (void*)callpc);
+ }
+ if(write)
+ runtime∕race·WriteRange(racectx, addr, size, step, (void*)pc);
+ else
+ runtime∕race·ReadRange(racectx, addr, size, step, (void*)pc);
+ if(callpc)
+ runtime∕race·FuncExit(racectx);
+ m->racecall = false;
+ }
+}
+
+void
+runtime·racewriterangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc)
+{
+ rangeaccess(addr, sz, step, (uintptr)callpc, (uintptr)pc, true);
+}
+
+void
+runtime·racereadrangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc)
+{
+ rangeaccess(addr, sz, step, (uintptr)callpc, (uintptr)pc, false);
+}
+
+void
+runtime·raceacquire(void *addr)
+{
+ runtime·raceacquireg(g, addr);
+}
+
+void
+runtime·raceacquireg(G *gp, void *addr)
+{
+ if(g->raceignore)
+ return;
+ m->racecall = true;
+ runtime∕race·Acquire(gp->racectx, addr);
+ m->racecall = false;
+}
+
+void
+runtime·racerelease(void *addr)
+{
+ runtime·racereleaseg(g, addr);
+}
+
+void
+runtime·racereleaseg(G *gp, void *addr)
+{
+ if(g->raceignore)
+ return;
+ m->racecall = true;
+ runtime∕race·Release(gp->racectx, addr);
+ m->racecall = false;
+}
+
+void
+runtime·racereleasemerge(void *addr)
+{
+ runtime·racereleasemergeg(g, addr);
+}
+
+void
+runtime·racereleasemergeg(G *gp, void *addr)
+{
+ if(g->raceignore)
+ return;
+ m->racecall = true;
+ runtime∕race·ReleaseMerge(gp->racectx, addr);
+ m->racecall = false;
+}
+
+void
+runtime·racefingo(void)
+{
+ m->racecall = true;
+ runtime∕race·FinalizerGoroutine(g->racectx);
+ m->racecall = false;
+}
+
+// func RaceAcquire(addr unsafe.Pointer)
+void
+runtime·RaceAcquire(void *addr)
+{
+ runtime·raceacquire(addr);
+}
+
+// func RaceRelease(addr unsafe.Pointer)
+void
+runtime·RaceRelease(void *addr)
+{
+ runtime·racerelease(addr);
+}
+
+// func RaceReleaseMerge(addr unsafe.Pointer)
+void
+runtime·RaceReleaseMerge(void *addr)
+{
+ runtime·racereleasemerge(addr);
+}
+
+// func RaceSemacquire(s *uint32)
+void runtime·RaceSemacquire(uint32 *s)
+{
+ runtime·semacquire(s);
+}
+
+// func RaceSemrelease(s *uint32)
+void runtime·RaceSemrelease(uint32 *s)
+{
+ runtime·semrelease(s);
+}
+
+// func RaceRead(addr unsafe.Pointer)
+#pragma textflag 7
+void
+runtime·RaceRead(void *addr)
+{
+ memoryaccess(addr, 0, (uintptr)runtime·getcallerpc(&addr), false);
+}
+
+// func RaceWrite(addr unsafe.Pointer)
+#pragma textflag 7
+void
+runtime·RaceWrite(void *addr)
+{
+ memoryaccess(addr, 0, (uintptr)runtime·getcallerpc(&addr), true);
+}
+
+// func RaceDisable()
+void runtime·RaceDisable(void)
+{
+ g->raceignore++;
+}
+
+// func RaceEnable()
+void runtime·RaceEnable(void)
+{
+ g->raceignore--;
+}
+
+static bool
+onstack(uintptr argp)
+{
+ // noptrdata, data, bss, noptrbss
+ // the layout is in ../../cmd/ld/data.c
+ if((byte*)argp >= noptrdata && (byte*)argp < enoptrbss)
+ return false;
+ if((byte*)argp >= runtime·mheap->arena_start && (byte*)argp < runtime·mheap->arena_used)
+ return false;
+ return true;
+}
diff --git a/src/pkg/runtime/race.go b/src/pkg/runtime/race.go
new file mode 100644
index 000000000..1d64ba389
--- /dev/null
+++ b/src/pkg/runtime/race.go
@@ -0,0 +1,29 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build race
+
+// Public race detection API, present iff build with -race.
+
+package runtime
+
+import (
+ "unsafe"
+)
+
+// RaceDisable disables handling of race events in the current goroutine.
+func RaceDisable()
+
+// RaceEnable re-enables handling of race events in the current goroutine.
+func RaceEnable()
+
+func RaceAcquire(addr unsafe.Pointer)
+func RaceRelease(addr unsafe.Pointer)
+func RaceReleaseMerge(addr unsafe.Pointer)
+
+func RaceRead(addr unsafe.Pointer)
+func RaceWrite(addr unsafe.Pointer)
+
+func RaceSemacquire(s *uint32)
+func RaceSemrelease(s *uint32)
diff --git a/src/pkg/runtime/race.h b/src/pkg/runtime/race.h
new file mode 100644
index 000000000..432a8a97d
--- /dev/null
+++ b/src/pkg/runtime/race.h
@@ -0,0 +1,33 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Definitions related to data race detection.
+
+#ifdef RACE
+enum { raceenabled = 1 };
+#else
+enum { raceenabled = 0 };
+#endif
+
+// Initialize race detection subsystem.
+uintptr runtime·raceinit(void);
+// Finalize race detection subsystem, does not return.
+void runtime·racefini(void);
+
+void runtime·racemapshadow(void *addr, uintptr size);
+void runtime·racemalloc(void *p, uintptr sz, void *pc);
+void runtime·racefree(void *p);
+uintptr runtime·racegostart(void *pc);
+void runtime·racegoend(void);
+void runtime·racewritepc(void *addr, void *callpc, void *pc);
+void runtime·racereadpc(void *addr, void *callpc, void *pc);
+void runtime·racewriterangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc);
+void runtime·racereadrangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc);
+void runtime·racefingo(void);
+void runtime·raceacquire(void *addr);
+void runtime·raceacquireg(G *gp, void *addr);
+void runtime·racerelease(void *addr);
+void runtime·racereleaseg(G *gp, void *addr);
+void runtime·racereleasemerge(void *addr);
+void runtime·racereleasemergeg(G *gp, void *addr);
diff --git a/src/pkg/runtime/race/README b/src/pkg/runtime/race/README
new file mode 100644
index 000000000..8bedb09cd
--- /dev/null
+++ b/src/pkg/runtime/race/README
@@ -0,0 +1,11 @@
+runtime/race package contains the data race detector runtime library.
+It is based on ThreadSanitizer race detector, that is currently a part of
+the LLVM project.
+
+To update the .syso files you need to:
+$ svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk
+$ cd compiler-rt/lib/tsan/go
+$ ./buildgo.sh
+
+Tested with gcc 4.6.1 and 4.7.0. On Windows it's built with 64-bit MinGW.
+
diff --git a/src/pkg/runtime/race/race.go b/src/pkg/runtime/race/race.go
new file mode 100644
index 000000000..b0a5c9a50
--- /dev/null
+++ b/src/pkg/runtime/race/race.go
@@ -0,0 +1,122 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build race,linux,amd64 race,darwin,amd64 race,windows,amd64
+
+// Package race provides low-level facilities for data race detection.
+package race
+
+/*
+void __tsan_init(void **racectx);
+void __tsan_fini(void);
+void __tsan_map_shadow(void *addr, void *size);
+void __tsan_go_start(void *racectx, void **chracectx, void *pc);
+void __tsan_go_end(void *racectx);
+void __tsan_read(void *racectx, void *addr, void *pc);
+void __tsan_write(void *racectx, void *addr, void *pc);
+void __tsan_read_range(void *racectx, void *addr, long sz, long step, void *pc);
+void __tsan_write_range(void *racectx, void *addr, long sz, long step, void *pc);
+void __tsan_func_enter(void *racectx, void *pc);
+void __tsan_func_exit(void *racectx);
+void __tsan_malloc(void *racectx, void *p, long sz, void *pc);
+void __tsan_free(void *p);
+void __tsan_acquire(void *racectx, void *addr);
+void __tsan_release(void *racectx, void *addr);
+void __tsan_release_merge(void *racectx, void *addr);
+void __tsan_finalizer_goroutine(void *racectx);
+*/
+import "C"
+
+import (
+ "runtime"
+ "unsafe"
+)
+
+func Initialize(racectx *uintptr) {
+ C.__tsan_init((*unsafe.Pointer)(unsafe.Pointer(racectx)))
+}
+
+func Finalize() {
+ C.__tsan_fini()
+}
+
+func MapShadow(addr, size uintptr) {
+ C.__tsan_map_shadow(unsafe.Pointer(addr), unsafe.Pointer(size))
+}
+
+func FinalizerGoroutine(racectx uintptr) {
+ C.__tsan_finalizer_goroutine(unsafe.Pointer(racectx))
+}
+
+func Read(racectx uintptr, addr, pc uintptr) {
+ C.__tsan_read(unsafe.Pointer(racectx), unsafe.Pointer(addr), unsafe.Pointer(pc))
+}
+
+func Write(racectx uintptr, addr, pc uintptr) {
+ C.__tsan_write(unsafe.Pointer(racectx), unsafe.Pointer(addr), unsafe.Pointer(pc))
+}
+
+func ReadRange(racectx uintptr, addr, sz, step, pc uintptr) {
+ C.__tsan_read_range(unsafe.Pointer(racectx), unsafe.Pointer(addr),
+ C.long(sz), C.long(step), unsafe.Pointer(pc))
+}
+
+func WriteRange(racectx uintptr, addr, sz, step, pc uintptr) {
+ C.__tsan_write_range(unsafe.Pointer(racectx), unsafe.Pointer(addr),
+ C.long(sz), C.long(step), unsafe.Pointer(pc))
+}
+
+func FuncEnter(racectx uintptr, pc uintptr) {
+ C.__tsan_func_enter(unsafe.Pointer(racectx), unsafe.Pointer(pc))
+}
+
+func FuncExit(racectx uintptr) {
+ C.__tsan_func_exit(unsafe.Pointer(racectx))
+}
+
+func Malloc(racectx uintptr, p, sz, pc uintptr) {
+ C.__tsan_malloc(unsafe.Pointer(racectx), unsafe.Pointer(p), C.long(sz), unsafe.Pointer(pc))
+}
+
+func Free(p uintptr) {
+ C.__tsan_free(unsafe.Pointer(p))
+}
+
+func GoStart(racectx uintptr, chracectx *uintptr, pc uintptr) {
+ C.__tsan_go_start(unsafe.Pointer(racectx), (*unsafe.Pointer)(unsafe.Pointer(chracectx)), unsafe.Pointer(pc))
+}
+
+func GoEnd(racectx uintptr) {
+ C.__tsan_go_end(unsafe.Pointer(racectx))
+}
+
+func Acquire(racectx uintptr, addr uintptr) {
+ C.__tsan_acquire(unsafe.Pointer(racectx), unsafe.Pointer(addr))
+}
+
+func Release(racectx uintptr, addr uintptr) {
+ C.__tsan_release(unsafe.Pointer(racectx), unsafe.Pointer(addr))
+}
+
+func ReleaseMerge(racectx uintptr, addr uintptr) {
+ C.__tsan_release_merge(unsafe.Pointer(racectx), unsafe.Pointer(addr))
+}
+
+//export __tsan_symbolize
+func __tsan_symbolize(pc uintptr, fun, file **C.char, line, off *C.int) C.int {
+ f := runtime.FuncForPC(pc)
+ if f == nil {
+ *fun = C.CString("??")
+ *file = C.CString("-")
+ *line = 0
+ *off = C.int(pc)
+ return 1
+ }
+ fi, l := f.FileLine(pc)
+ *fun = C.CString(f.Name())
+ *file = C.CString(fi)
+ *line = C.int(l)
+ *off = C.int(pc - f.Entry())
+ return 1
+}
diff --git a/src/pkg/runtime/race/race_darwin_amd64.syso b/src/pkg/runtime/race/race_darwin_amd64.syso
new file mode 100644
index 000000000..24a00497c
--- /dev/null
+++ b/src/pkg/runtime/race/race_darwin_amd64.syso
Binary files differ
diff --git a/src/pkg/runtime/race/race_linux_amd64.syso b/src/pkg/runtime/race/race_linux_amd64.syso
new file mode 100644
index 000000000..b15091ba8
--- /dev/null
+++ b/src/pkg/runtime/race/race_linux_amd64.syso
Binary files differ
diff --git a/src/pkg/runtime/race/race_test.go b/src/pkg/runtime/race/race_test.go
new file mode 100644
index 000000000..c77569c37
--- /dev/null
+++ b/src/pkg/runtime/race/race_test.go
@@ -0,0 +1,157 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build race
+
+// This program is used to verify the race detector
+// by running the tests and parsing their output.
+// It does not check stack correctness, completeness or anything else:
+// it merely verifies that if a test is expected to be racy
+// then the race is detected.
+package race_test
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+var (
+ passedTests = 0
+ totalTests = 0
+ falsePos = 0
+ falseNeg = 0
+ failingPos = 0
+ failingNeg = 0
+ failed = false
+)
+
+const (
+ visibleLen = 40
+ testPrefix = "=== RUN Test"
+)
+
+func TestRace(t *testing.T) {
+ testOutput, err := runTests()
+ if err != nil {
+ t.Fatalf("Failed to run tests: %v\n%v", err, string(testOutput))
+ }
+ reader := bufio.NewReader(bytes.NewBuffer(testOutput))
+
+ funcName := ""
+ var tsanLog []string
+ for {
+ s, err := nextLine(reader)
+ if err != nil {
+ fmt.Printf("%s\n", processLog(funcName, tsanLog))
+ break
+ }
+ if strings.HasPrefix(s, testPrefix) {
+ fmt.Printf("%s\n", processLog(funcName, tsanLog))
+ tsanLog = make([]string, 0, 100)
+ funcName = s[len(testPrefix):]
+ } else {
+ tsanLog = append(tsanLog, s)
+ }
+ }
+
+ fmt.Printf("\nPassed %d of %d tests (%.02f%%, %d+, %d-)\n",
+ passedTests, totalTests, 100*float64(passedTests)/float64(totalTests), falsePos, falseNeg)
+ fmt.Printf("%d expected failures (%d has not fail)\n", failingPos+failingNeg, failingNeg)
+ if failed {
+ t.Fail()
+ }
+}
+
+// nextLine is a wrapper around bufio.Reader.ReadString.
+// It reads a line up to the next '\n' character. Error
+// is non-nil if there are no lines left, and nil
+// otherwise.
+func nextLine(r *bufio.Reader) (string, error) {
+ s, err := r.ReadString('\n')
+ if err != nil {
+ if err != io.EOF {
+ log.Fatalf("nextLine: expected EOF, received %v", err)
+ }
+ return s, err
+ }
+ return s[:len(s)-1], nil
+}
+
+// processLog verifies whether the given ThreadSanitizer's log
+// contains a race report, checks this information against
+// the name of the testcase and returns the result of this
+// comparison.
+func processLog(testName string, tsanLog []string) string {
+ if !strings.HasPrefix(testName, "Race") && !strings.HasPrefix(testName, "NoRace") {
+ return ""
+ }
+ gotRace := false
+ for _, s := range tsanLog {
+ if strings.Contains(s, "DATA RACE") {
+ gotRace = true
+ break
+ }
+ }
+
+ failing := strings.Contains(testName, "Failing")
+ expRace := !strings.HasPrefix(testName, "No")
+ for len(testName) < visibleLen {
+ testName += " "
+ }
+ if expRace == gotRace {
+ passedTests++
+ totalTests++
+ if failing {
+ failed = true
+ failingNeg++
+ }
+ return fmt.Sprintf("%s .", testName)
+ }
+ pos := ""
+ if expRace {
+ falseNeg++
+ } else {
+ falsePos++
+ pos = "+"
+ }
+ if failing {
+ failingPos++
+ } else {
+ failed = true
+ }
+ totalTests++
+ return fmt.Sprintf("%s %s%s", testName, "FAILED", pos)
+}
+
+// runTests assures that the package and its dependencies is
+// built with instrumentation enabled and returns the output of 'go test'
+// which includes possible data race reports from ThreadSanitizer.
+func runTests() ([]byte, error) {
+ tests, err := filepath.Glob("./testdata/*_test.go")
+ if err != nil {
+ return nil, err
+ }
+ args := []string{"test", "-race", "-v"}
+ args = append(args, tests...)
+ cmd := exec.Command("go", args...)
+ // The following flags turn off heuristics that suppress seemingly identical reports.
+ // It is required because the tests contain a lot of data races on the same addresses
+ // (the tests are simple and the memory is constantly reused).
+ for _, env := range os.Environ() {
+ if strings.HasPrefix(env, "GOMAXPROCS=") {
+ continue
+ }
+ cmd.Env = append(cmd.Env, env)
+ }
+ cmd.Env = append(cmd.Env, `GORACE="suppress_equal_stacks=0 suppress_equal_addresses=0 exitcode=0"`)
+ return cmd.CombinedOutput()
+}
diff --git a/src/pkg/runtime/race/race_windows_amd64.syso b/src/pkg/runtime/race/race_windows_amd64.syso
new file mode 100644
index 000000000..0a3a58354
--- /dev/null
+++ b/src/pkg/runtime/race/race_windows_amd64.syso
Binary files differ
diff --git a/src/pkg/runtime/race/testdata/atomic_test.go b/src/pkg/runtime/race/testdata/atomic_test.go
new file mode 100644
index 000000000..0c5c2c008
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/atomic_test.go
@@ -0,0 +1,271 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "runtime"
+ "sync/atomic"
+ "testing"
+ "unsafe"
+)
+
+func TestNoRaceAtomicAddInt64(t *testing.T) {
+ var x1, x2 int8
+ var s int64
+ ch := make(chan bool, 2)
+ go func() {
+ x1 = 1
+ if atomic.AddInt64(&s, 1) == 2 {
+ x2 = 1
+ }
+ ch <- true
+ }()
+ go func() {
+ x2 = 1
+ if atomic.AddInt64(&s, 1) == 2 {
+ x1 = 1
+ }
+ ch <- true
+ }()
+ <-ch
+ <-ch
+}
+
+func TestRaceAtomicAddInt64(t *testing.T) {
+ var x1, x2 int8
+ var s int64
+ ch := make(chan bool, 2)
+ go func() {
+ x1 = 1
+ if atomic.AddInt64(&s, 1) == 1 {
+ x2 = 1
+ }
+ ch <- true
+ }()
+ go func() {
+ x2 = 1
+ if atomic.AddInt64(&s, 1) == 1 {
+ x1 = 1
+ }
+ ch <- true
+ }()
+ <-ch
+ <-ch
+}
+
+func TestNoRaceAtomicAddInt32(t *testing.T) {
+ var x1, x2 int8
+ var s int32
+ ch := make(chan bool, 2)
+ go func() {
+ x1 = 1
+ if atomic.AddInt32(&s, 1) == 2 {
+ x2 = 1
+ }
+ ch <- true
+ }()
+ go func() {
+ x2 = 1
+ if atomic.AddInt32(&s, 1) == 2 {
+ x1 = 1
+ }
+ ch <- true
+ }()
+ <-ch
+ <-ch
+}
+
+func TestNoRaceAtomicLoadAddInt32(t *testing.T) {
+ var x int64
+ var s int32
+ go func() {
+ x = 2
+ atomic.AddInt32(&s, 1)
+ }()
+ for atomic.LoadInt32(&s) != 1 {
+ runtime.Gosched()
+ }
+ x = 1
+}
+
+func TestNoRaceAtomicLoadStoreInt32(t *testing.T) {
+ var x int64
+ var s int32
+ go func() {
+ x = 2
+ atomic.StoreInt32(&s, 1)
+ }()
+ for atomic.LoadInt32(&s) != 1 {
+ runtime.Gosched()
+ }
+ x = 1
+}
+
+func TestNoRaceAtomicStoreCASInt32(t *testing.T) {
+ var x int64
+ var s int32
+ go func() {
+ x = 2
+ atomic.StoreInt32(&s, 1)
+ }()
+ for !atomic.CompareAndSwapInt32(&s, 1, 0) {
+ runtime.Gosched()
+ }
+ x = 1
+}
+
+func TestNoRaceAtomicCASLoadInt32(t *testing.T) {
+ var x int64
+ var s int32
+ go func() {
+ x = 2
+ if !atomic.CompareAndSwapInt32(&s, 0, 1) {
+ panic("")
+ }
+ }()
+ for atomic.LoadInt32(&s) != 1 {
+ runtime.Gosched()
+ }
+ x = 1
+}
+
+func TestNoRaceAtomicCASCASInt32(t *testing.T) {
+ var x int64
+ var s int32
+ go func() {
+ x = 2
+ if !atomic.CompareAndSwapInt32(&s, 0, 1) {
+ panic("")
+ }
+ }()
+ for !atomic.CompareAndSwapInt32(&s, 1, 0) {
+ runtime.Gosched()
+ }
+ x = 1
+}
+
+func TestNoRaceAtomicCASCASInt32_2(t *testing.T) {
+ var x1, x2 int8
+ var s int32
+ ch := make(chan bool, 2)
+ go func() {
+ x1 = 1
+ if !atomic.CompareAndSwapInt32(&s, 0, 1) {
+ x2 = 1
+ }
+ ch <- true
+ }()
+ go func() {
+ x2 = 1
+ if !atomic.CompareAndSwapInt32(&s, 0, 1) {
+ x1 = 1
+ }
+ ch <- true
+ }()
+ <-ch
+ <-ch
+}
+
+func TestNoRaceAtomicLoadInt64(t *testing.T) {
+ var x int32
+ var s int64
+ go func() {
+ x = 2
+ atomic.AddInt64(&s, 1)
+ }()
+ for atomic.LoadInt64(&s) != 1 {
+ runtime.Gosched()
+ }
+ x = 1
+}
+
+func TestNoRaceAtomicCASCASUInt64(t *testing.T) {
+ var x int64
+ var s uint64
+ go func() {
+ x = 2
+ if !atomic.CompareAndSwapUint64(&s, 0, 1) {
+ panic("")
+ }
+ }()
+ for !atomic.CompareAndSwapUint64(&s, 1, 0) {
+ runtime.Gosched()
+ }
+ x = 1
+}
+
+func TestNoRaceAtomicLoadStorePointer(t *testing.T) {
+ var x int64
+ var s unsafe.Pointer
+ var y int = 2
+ var p unsafe.Pointer = unsafe.Pointer(&y)
+ go func() {
+ x = 2
+ atomic.StorePointer(&s, p)
+ }()
+ for atomic.LoadPointer(&s) != p {
+ runtime.Gosched()
+ }
+ x = 1
+}
+
+func TestNoRaceAtomicStoreCASUint64(t *testing.T) {
+ var x int64
+ var s uint64
+ go func() {
+ x = 2
+ atomic.StoreUint64(&s, 1)
+ }()
+ for !atomic.CompareAndSwapUint64(&s, 1, 0) {
+ runtime.Gosched()
+ }
+ x = 1
+}
+
+// Races with non-atomic loads are not detected.
+func TestRaceFailingAtomicStoreLoad(t *testing.T) {
+ c := make(chan bool)
+ var a uint64
+ go func() {
+ atomic.StoreUint64(&a, 1)
+ c <- true
+ }()
+ _ = a
+ <-c
+}
+
+func TestRaceAtomicLoadStore(t *testing.T) {
+ c := make(chan bool)
+ var a uint64
+ go func() {
+ _ = atomic.LoadUint64(&a)
+ c <- true
+ }()
+ a = 1
+ <-c
+}
+
+// Races with non-atomic loads are not detected.
+func TestRaceFailingAtomicAddLoad(t *testing.T) {
+ c := make(chan bool)
+ var a uint64
+ go func() {
+ atomic.AddUint64(&a, 1)
+ c <- true
+ }()
+ _ = a
+ <-c
+}
+
+func TestRaceAtomicAddStore(t *testing.T) {
+ c := make(chan bool)
+ var a uint64
+ go func() {
+ atomic.AddUint64(&a, 1)
+ c <- true
+ }()
+ a = 42
+ <-c
+}
diff --git a/src/pkg/runtime/race/testdata/cgo_test.go b/src/pkg/runtime/race/testdata/cgo_test.go
new file mode 100644
index 000000000..ba7e7b562
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/cgo_test.go
@@ -0,0 +1,20 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "os"
+ "os/exec"
+ "testing"
+)
+
+func TestNoRaceCgoSync(t *testing.T) {
+ cmd := exec.Command("go", "run", "-race", "cgo_test_main.go")
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ t.Fatalf("program exited with error: %v\n", err)
+ }
+}
diff --git a/src/pkg/runtime/race/testdata/cgo_test_main.go b/src/pkg/runtime/race/testdata/cgo_test_main.go
new file mode 100644
index 000000000..620cea18b
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/cgo_test_main.go
@@ -0,0 +1,30 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+int sync;
+
+void Notify(void)
+{
+ __sync_fetch_and_add(&sync, 1);
+}
+
+void Wait(void)
+{
+ while(__sync_fetch_and_add(&sync, 0) == 0) {}
+}
+*/
+import "C"
+
+func main() {
+ data := 0
+ go func() {
+ data = 1
+ C.Notify()
+ }()
+ C.Wait()
+ _ = data
+}
diff --git a/src/pkg/runtime/race/testdata/chan_test.go b/src/pkg/runtime/race/testdata/chan_test.go
new file mode 100644
index 000000000..2332f097e
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/chan_test.go
@@ -0,0 +1,457 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "runtime"
+ "testing"
+ "time"
+)
+
+func TestNoRaceChanSync(t *testing.T) {
+ v := 0
+ c := make(chan int)
+ go func() {
+ v = 1
+ c <- 0
+ }()
+ <-c
+ v = 2
+}
+
+func TestNoRaceChanSyncRev(t *testing.T) {
+ v := 0
+ c := make(chan int)
+ go func() {
+ c <- 0
+ v = 2
+ }()
+ v = 1
+ <-c
+}
+
+func TestNoRaceChanAsync(t *testing.T) {
+ v := 0
+ c := make(chan int, 10)
+ go func() {
+ v = 1
+ c <- 0
+ }()
+ <-c
+ v = 2
+}
+
+func TestRaceChanAsyncRev(t *testing.T) {
+ v := 0
+ c := make(chan int, 10)
+ go func() {
+ c <- 0
+ v = 1
+ }()
+ v = 2
+ <-c
+}
+
+func TestNoRaceChanAsyncCloseRecv(t *testing.T) {
+ v := 0
+ c := make(chan int, 10)
+ go func() {
+ v = 1
+ close(c)
+ }()
+ func() {
+ defer func() {
+ recover()
+ v = 2
+ }()
+ <-c
+ }()
+}
+
+func TestNoRaceChanAsyncCloseRecv2(t *testing.T) {
+ v := 0
+ c := make(chan int, 10)
+ go func() {
+ v = 1
+ close(c)
+ }()
+ _, _ = <-c
+ v = 2
+}
+
+func TestNoRaceChanAsyncCloseRecv3(t *testing.T) {
+ v := 0
+ c := make(chan int, 10)
+ go func() {
+ v = 1
+ close(c)
+ }()
+ for _ = range c {
+ }
+ v = 2
+}
+
+func TestNoRaceChanSyncCloseRecv(t *testing.T) {
+ v := 0
+ c := make(chan int)
+ go func() {
+ v = 1
+ close(c)
+ }()
+ func() {
+ defer func() {
+ recover()
+ v = 2
+ }()
+ <-c
+ }()
+}
+
+func TestNoRaceChanSyncCloseRecv2(t *testing.T) {
+ v := 0
+ c := make(chan int)
+ go func() {
+ v = 1
+ close(c)
+ }()
+ _, _ = <-c
+ v = 2
+}
+
+func TestNoRaceChanSyncCloseRecv3(t *testing.T) {
+ v := 0
+ c := make(chan int)
+ go func() {
+ v = 1
+ close(c)
+ }()
+ for _ = range c {
+ }
+ v = 2
+}
+
+func TestRaceChanSyncCloseSend(t *testing.T) {
+ v := 0
+ c := make(chan int)
+ go func() {
+ v = 1
+ close(c)
+ }()
+ func() {
+ defer func() {
+ recover()
+ }()
+ c <- 0
+ }()
+ v = 2
+}
+
+func TestRaceChanAsyncCloseSend(t *testing.T) {
+ v := 0
+ c := make(chan int, 10)
+ go func() {
+ v = 1
+ close(c)
+ }()
+ func() {
+ defer func() {
+ recover()
+ }()
+ for {
+ c <- 0
+ }
+ }()
+ v = 2
+}
+
+func TestRaceChanCloseClose(t *testing.T) {
+ compl := make(chan bool, 2)
+ v1 := 0
+ v2 := 0
+ c := make(chan int)
+ go func() {
+ defer func() {
+ if recover() != nil {
+ v2 = 2
+ }
+ compl <- true
+ }()
+ v1 = 1
+ close(c)
+ }()
+ go func() {
+ defer func() {
+ if recover() != nil {
+ v1 = 2
+ }
+ compl <- true
+ }()
+ v2 = 1
+ close(c)
+ }()
+ <-compl
+ <-compl
+}
+
+func TestRaceChanSendLen(t *testing.T) {
+ v := 0
+ c := make(chan int, 10)
+ go func() {
+ v = 1
+ c <- 1
+ }()
+ for len(c) == 0 {
+ runtime.Gosched()
+ }
+ v = 2
+}
+
+func TestRaceChanRecvLen(t *testing.T) {
+ v := 0
+ c := make(chan int, 10)
+ c <- 1
+ go func() {
+ v = 1
+ <-c
+ }()
+ for len(c) != 0 {
+ runtime.Gosched()
+ }
+ v = 2
+}
+
+func TestRaceChanSendSend(t *testing.T) {
+ compl := make(chan bool, 2)
+ v1 := 0
+ v2 := 0
+ c := make(chan int, 1)
+ go func() {
+ v1 = 1
+ select {
+ case c <- 1:
+ default:
+ v2 = 2
+ }
+ compl <- true
+ }()
+ go func() {
+ v2 = 1
+ select {
+ case c <- 1:
+ default:
+ v1 = 2
+ }
+ compl <- true
+ }()
+ <-compl
+ <-compl
+}
+
+func TestNoRaceChanPtr(t *testing.T) {
+ type msg struct {
+ x int
+ }
+ c := make(chan *msg)
+ go func() {
+ c <- &msg{1}
+ }()
+ m := <-c
+ m.x = 2
+}
+
+func TestRaceChanWrongSend(t *testing.T) {
+ v1 := 0
+ v2 := 0
+ c := make(chan int, 2)
+ go func() {
+ v1 = 1
+ c <- 1
+ }()
+ go func() {
+ v2 = 2
+ c <- 2
+ }()
+ time.Sleep(1e7)
+ if <-c == 1 {
+ v2 = 3
+ } else {
+ v1 = 3
+ }
+}
+
+func TestRaceChanWrongClose(t *testing.T) {
+ v1 := 0
+ v2 := 0
+ c := make(chan int, 1)
+ go func() {
+ defer func() {
+ recover()
+ }()
+ v1 = 1
+ c <- 1
+ }()
+ go func() {
+ time.Sleep(1e7)
+ v2 = 2
+ close(c)
+ }()
+ time.Sleep(2e7)
+ if _, who := <-c; who {
+ v2 = 2
+ } else {
+ v1 = 2
+ }
+}
+
+func TestRaceChanSendClose(t *testing.T) {
+ compl := make(chan bool, 2)
+ c := make(chan int, 1)
+ go func() {
+ defer func() {
+ recover()
+ }()
+ c <- 1
+ compl <- true
+ }()
+ go func() {
+ time.Sleep(1e7)
+ close(c)
+ compl <- true
+ }()
+ <-compl
+ <-compl
+}
+
+func TestNoRaceProducerConsumerUnbuffered(t *testing.T) {
+ type Task struct {
+ f func()
+ done chan bool
+ }
+
+ queue := make(chan Task)
+
+ go func() {
+ t := <-queue
+ t.f()
+ t.done <- true
+ }()
+
+ doit := func(f func()) {
+ done := make(chan bool, 1)
+ queue <- Task{f, done}
+ <-done
+ }
+
+ x := 0
+ doit(func() {
+ x = 1
+ })
+ _ = x
+}
+
+func TestRaceChanItselfSend(t *testing.T) {
+ compl := make(chan bool, 1)
+ c := make(chan int, 10)
+ go func() {
+ c <- 0
+ compl <- true
+ }()
+ c = make(chan int, 20)
+ <-compl
+}
+
+func TestRaceChanItselfRecv(t *testing.T) {
+ compl := make(chan bool, 1)
+ c := make(chan int, 10)
+ c <- 1
+ go func() {
+ <-c
+ compl <- true
+ }()
+ time.Sleep(1e7)
+ c = make(chan int, 20)
+ <-compl
+}
+
+func TestRaceChanItselfNil(t *testing.T) {
+ c := make(chan int, 10)
+ go func() {
+ c <- 0
+ }()
+ time.Sleep(1e7)
+ c = nil
+ _ = c
+}
+
+func TestRaceChanItselfClose(t *testing.T) {
+ compl := make(chan bool, 1)
+ c := make(chan int)
+ go func() {
+ close(c)
+ compl <- true
+ }()
+ c = make(chan int)
+ <-compl
+}
+
+func TestRaceChanItselfLen(t *testing.T) {
+ compl := make(chan bool, 1)
+ c := make(chan int)
+ go func() {
+ _ = len(c)
+ compl <- true
+ }()
+ c = make(chan int)
+ <-compl
+}
+
+func TestRaceChanItselfCap(t *testing.T) {
+ compl := make(chan bool, 1)
+ c := make(chan int)
+ go func() {
+ _ = cap(c)
+ compl <- true
+ }()
+ c = make(chan int)
+ <-compl
+}
+
+func TestRaceChanCloseLen(t *testing.T) {
+ v := 0
+ c := make(chan int, 10)
+ c <- 0
+ go func() {
+ v = 1
+ close(c)
+ }()
+ time.Sleep(1e7)
+ _ = len(c)
+ v = 2
+}
+
+func TestRaceChanSameCell(t *testing.T) {
+ c := make(chan int, 1)
+ v := 0
+ go func() {
+ v = 1
+ c <- 42
+ <-c
+ }()
+ time.Sleep(1e7)
+ c <- 43
+ <-c
+ _ = v
+}
+
+func TestRaceChanCloseSend(t *testing.T) {
+ compl := make(chan bool, 1)
+ c := make(chan int, 10)
+ go func() {
+ close(c)
+ compl <- true
+ }()
+ c <- 0
+ <-compl
+}
diff --git a/src/pkg/runtime/race/testdata/comp_test.go b/src/pkg/runtime/race/testdata/comp_test.go
new file mode 100644
index 000000000..754e4db6d
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/comp_test.go
@@ -0,0 +1,132 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "testing"
+)
+
+type P struct {
+ x, y int
+}
+
+type S struct {
+ s1, s2 P
+}
+
+func TestNoRaceComp(t *testing.T) {
+ c := make(chan bool, 1)
+ var s S
+ go func() {
+ s.s2.x = 1
+ c <- true
+ }()
+ s.s2.y = 2
+ <-c
+}
+
+func TestNoRaceComp2(t *testing.T) {
+ c := make(chan bool, 1)
+ var s S
+ go func() {
+ s.s1.x = 1
+ c <- true
+ }()
+ s.s1.y = 2
+ <-c
+}
+
+func TestRaceComp(t *testing.T) {
+ c := make(chan bool, 1)
+ var s S
+ go func() {
+ s.s2.y = 1
+ c <- true
+ }()
+ s.s2.y = 2
+ <-c
+}
+
+func TestRaceComp2(t *testing.T) {
+ c := make(chan bool, 1)
+ var s S
+ go func() {
+ s.s1.x = 1
+ c <- true
+ }()
+ s = S{}
+ <-c
+}
+
+func TestRaceComp3(t *testing.T) {
+ c := make(chan bool, 1)
+ var s S
+ go func() {
+ s.s2.y = 1
+ c <- true
+ }()
+ s = S{}
+ <-c
+}
+
+func TestRaceCompArray(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]S, 10)
+ x := 4
+ go func() {
+ s[x].s2.y = 1
+ c <- true
+ }()
+ x = 5
+ <-c
+}
+
+type Ptr struct {
+ s1, s2 *P
+}
+
+func TestNoRaceCompPtr(t *testing.T) {
+ c := make(chan bool, 1)
+ p := Ptr{&P{}, &P{}}
+ go func() {
+ p.s1.x = 1
+ c <- true
+ }()
+ p.s1.y = 2
+ <-c
+}
+
+func TestNoRaceCompPtr2(t *testing.T) {
+ c := make(chan bool, 1)
+ p := Ptr{&P{}, &P{}}
+ go func() {
+ p.s1.x = 1
+ c <- true
+ }()
+ _ = p
+ <-c
+}
+
+func TestRaceCompPtr(t *testing.T) {
+ c := make(chan bool, 1)
+ p := Ptr{&P{}, &P{}}
+ go func() {
+ p.s2.x = 1
+ c <- true
+ }()
+ p.s2.x = 2
+ <-c
+}
+
+func TestRaceCompPtr2(t *testing.T) {
+ c := make(chan bool, 1)
+ p := Ptr{&P{}, &P{}}
+ go func() {
+ p.s2.x = 1
+ c <- true
+ }()
+ p.s2 = &P{}
+ <-c
+}
diff --git a/src/pkg/runtime/race/testdata/finalizer_test.go b/src/pkg/runtime/race/testdata/finalizer_test.go
new file mode 100644
index 000000000..2b2607689
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/finalizer_test.go
@@ -0,0 +1,67 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "runtime"
+ "sync"
+ "testing"
+ "time"
+)
+
+func TestNoRaceFin(t *testing.T) {
+ c := make(chan bool)
+ go func() {
+ x := new(int)
+ runtime.SetFinalizer(x, func(x *int) {
+ *x = 42
+ })
+ *x = 66
+ c <- true
+ }()
+ <-c
+ runtime.GC()
+ time.Sleep(1e8)
+}
+
+var finVar struct {
+ sync.Mutex
+ cnt int
+}
+
+func TestNoRaceFinGlobal(t *testing.T) {
+ c := make(chan bool)
+ go func() {
+ x := new(int)
+ runtime.SetFinalizer(x, func(x *int) {
+ finVar.Lock()
+ finVar.cnt++
+ finVar.Unlock()
+ })
+ c <- true
+ }()
+ <-c
+ runtime.GC()
+ time.Sleep(1e8)
+ finVar.Lock()
+ finVar.cnt++
+ finVar.Unlock()
+}
+
+func TestRaceFin(t *testing.T) {
+ c := make(chan bool)
+ y := 0
+ go func() {
+ x := new(int)
+ runtime.SetFinalizer(x, func(x *int) {
+ y = 42
+ })
+ c <- true
+ }()
+ <-c
+ runtime.GC()
+ time.Sleep(1e8)
+ y = 66
+}
diff --git a/src/pkg/runtime/race/testdata/io_test.go b/src/pkg/runtime/race/testdata/io_test.go
new file mode 100644
index 000000000..9eb3552dc
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/io_test.go
@@ -0,0 +1,69 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+)
+
+func TestNoRaceIOFile(t *testing.T) {
+ x := 0
+ path, _ := ioutil.TempDir("", "race_test")
+ fname := filepath.Join(path, "data")
+ go func() {
+ x = 42
+ f, _ := os.Create(fname)
+ f.Write([]byte("done"))
+ f.Close()
+ }()
+ for {
+ f, err := os.Open(fname)
+ if err != nil {
+ time.Sleep(1e6)
+ continue
+ }
+ buf := make([]byte, 100)
+ count, err := f.Read(buf)
+ if count == 0 {
+ time.Sleep(1e6)
+ continue
+ }
+ break
+ }
+ _ = x
+}
+
+func TestNoRaceIOHttp(t *testing.T) {
+ x := 0
+ go func() {
+ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ x = 41
+ fmt.Fprintf(w, "test")
+ x = 42
+ })
+ err := http.ListenAndServe(":23651", nil)
+ if err != nil {
+ t.Fatalf("http.ListenAndServe: %v", err)
+ }
+ }()
+ time.Sleep(1e7)
+ x = 1
+ _, err := http.Get("http://127.0.0.1:23651")
+ if err != nil {
+ t.Fatalf("http.Get: %v", err)
+ }
+ x = 2
+ _, err = http.Get("http://127.0.0.1:23651")
+ if err != nil {
+ t.Fatalf("http.Get: %v", err)
+ }
+ x = 3
+}
diff --git a/src/pkg/runtime/race/testdata/map_test.go b/src/pkg/runtime/race/testdata/map_test.go
new file mode 100644
index 000000000..6f86a50b7
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/map_test.go
@@ -0,0 +1,163 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "testing"
+)
+
+func TestRaceMapRW(t *testing.T) {
+ m := make(map[int]int)
+ ch := make(chan bool, 1)
+ go func() {
+ _ = m[1]
+ ch <- true
+ }()
+ m[1] = 1
+ <-ch
+}
+
+func TestRaceMapRW2(t *testing.T) {
+ m := make(map[int]int)
+ ch := make(chan bool, 1)
+ go func() {
+ _, _ = m[1]
+ ch <- true
+ }()
+ m[1] = 1
+ <-ch
+}
+
+func TestRaceMapRWArray(t *testing.T) {
+ // Check instrumentation of unaddressable arrays (issue 4578).
+ m := make(map[int][2]int)
+ ch := make(chan bool, 1)
+ go func() {
+ _ = m[1][1]
+ ch <- true
+ }()
+ m[2] = [2]int{1, 2}
+ <-ch
+}
+
+func TestNoRaceMapRR(t *testing.T) {
+ m := make(map[int]int)
+ ch := make(chan bool, 1)
+ go func() {
+ _, _ = m[1]
+ ch <- true
+ }()
+ _ = m[1]
+ <-ch
+}
+
+func TestRaceMapRange(t *testing.T) {
+ m := make(map[int]int)
+ ch := make(chan bool, 1)
+ go func() {
+ for _ = range m {
+ }
+ ch <- true
+ }()
+ m[1] = 1
+ <-ch
+}
+
+func TestRaceMapRange2(t *testing.T) {
+ m := make(map[int]int)
+ ch := make(chan bool, 1)
+ go func() {
+ for _ = range m {
+ }
+ ch <- true
+ }()
+ m[1] = 1
+ <-ch
+}
+
+func TestNoRaceMapRangeRange(t *testing.T) {
+ m := make(map[int]int)
+ // now the map is not empty and range triggers an event
+ // should work without this (as in other tests)
+ // so it is suspicious if this test passes and others don't
+ m[0] = 0
+ ch := make(chan bool, 1)
+ go func() {
+ for _ = range m {
+ }
+ ch <- true
+ }()
+ for _ = range m {
+ }
+ <-ch
+}
+
+// Map len is not instrumented.
+func TestRaceFailingMapLen(t *testing.T) {
+ m := make(map[string]bool)
+ ch := make(chan bool, 1)
+ go func() {
+ _ = len(m)
+ ch <- true
+ }()
+ m[""] = true
+ <-ch
+}
+
+func TestRaceMapDelete(t *testing.T) {
+ m := make(map[string]bool)
+ ch := make(chan bool, 1)
+ go func() {
+ delete(m, "")
+ ch <- true
+ }()
+ m[""] = true
+ <-ch
+}
+
+// Map len is not instrumented.
+func TestRaceFailingMapLenDelete(t *testing.T) {
+ m := make(map[string]bool)
+ ch := make(chan bool, 1)
+ go func() {
+ delete(m, "a")
+ ch <- true
+ }()
+ _ = len(m)
+ <-ch
+}
+
+func TestRaceMapVariable(t *testing.T) {
+ ch := make(chan bool, 1)
+ m := make(map[int]int)
+ go func() {
+ m = make(map[int]int)
+ ch <- true
+ }()
+ m = make(map[int]int)
+ <-ch
+}
+
+func TestRaceMapVariable2(t *testing.T) {
+ ch := make(chan bool, 1)
+ m := make(map[int]int)
+ go func() {
+ m[1] = 1
+ ch <- true
+ }()
+ m = make(map[int]int)
+ <-ch
+}
+
+func TestRaceMapVariable3(t *testing.T) {
+ ch := make(chan bool, 1)
+ m := make(map[int]int)
+ go func() {
+ _ = m[1]
+ ch <- true
+ }()
+ m = make(map[int]int)
+ <-ch
+}
diff --git a/src/pkg/runtime/race/testdata/mop_test.go b/src/pkg/runtime/race/testdata/mop_test.go
new file mode 100644
index 000000000..f2daa3730
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/mop_test.go
@@ -0,0 +1,1384 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "errors"
+ "fmt"
+ "runtime"
+ "sync"
+ "testing"
+ "time"
+ "unsafe"
+)
+
+type Point struct {
+ x, y int
+}
+
+type NamedPoint struct {
+ name string
+ p Point
+}
+
+type DummyWriter struct {
+ state int
+}
+type Writer interface {
+ Write(p []byte) (n int)
+}
+
+func (d DummyWriter) Write(p []byte) (n int) {
+ return 0
+}
+
+var GlobalX, GlobalY int = 0, 0
+var GlobalCh chan int = make(chan int, 2)
+
+func GlobalFunc1() {
+ GlobalY = GlobalX
+ GlobalCh <- 1
+}
+
+func GlobalFunc2() {
+ GlobalX = 1
+ GlobalCh <- 1
+}
+
+func TestRaceIntRWGlobalFuncs(t *testing.T) {
+ go GlobalFunc1()
+ go GlobalFunc2()
+ <-GlobalCh
+ <-GlobalCh
+}
+
+func TestRaceIntRWClosures(t *testing.T) {
+ var x, y int
+ ch := make(chan int, 2)
+
+ go func() {
+ y = x
+ ch <- 1
+ }()
+ go func() {
+ x = 1
+ ch <- 1
+ }()
+ <-ch
+ <-ch
+}
+
+func TestNoRaceIntRWClosures(t *testing.T) {
+ var x, y int
+ ch := make(chan int, 1)
+
+ go func() {
+ y = x
+ ch <- 1
+ }()
+ <-ch
+ go func() {
+ x = 1
+ ch <- 1
+ }()
+ <-ch
+
+}
+
+func TestRaceInt32RWClosures(t *testing.T) {
+ var x, y int32
+ ch := make(chan bool, 2)
+
+ go func() {
+ y = x
+ ch <- true
+ }()
+ go func() {
+ x = 1
+ ch <- true
+ }()
+ <-ch
+ <-ch
+}
+
+func TestNoRaceCase(t *testing.T) {
+ var y int
+ for x := -1; x <= 1; x++ {
+ switch {
+ case x < 0:
+ y = -1
+ case x == 0:
+ y = 0
+ case x > 0:
+ y = 1
+ }
+ }
+ y++
+}
+
+func TestRaceCaseCondition(t *testing.T) {
+ var x int = 0
+ ch := make(chan int, 2)
+
+ go func() {
+ x = 2
+ ch <- 1
+ }()
+ go func() {
+ switch x < 2 {
+ case true:
+ x = 1
+ //case false:
+ // x = 5
+ }
+ ch <- 1
+ }()
+ <-ch
+ <-ch
+}
+
+func TestRaceCaseCondition2(t *testing.T) {
+ // switch body is rearranged by the compiler so the tests
+ // passes even if we don't instrument '<'
+ var x int = 0
+ ch := make(chan int, 2)
+
+ go func() {
+ x = 2
+ ch <- 1
+ }()
+ go func() {
+ switch x < 2 {
+ case true:
+ x = 1
+ case false:
+ x = 5
+ }
+ ch <- 1
+ }()
+ <-ch
+ <-ch
+}
+
+func TestRaceCaseBody(t *testing.T) {
+ var x, y int
+ ch := make(chan int, 2)
+
+ go func() {
+ y = x
+ ch <- 1
+ }()
+ go func() {
+ switch {
+ default:
+ x = 1
+ case x == 100:
+ x = -x
+ }
+ ch <- 1
+ }()
+ <-ch
+ <-ch
+}
+
+func TestNoRaceCaseFallthrough(t *testing.T) {
+ var x, y, z int
+ ch := make(chan int, 2)
+ z = 1
+
+ go func() {
+ y = x
+ ch <- 1
+ }()
+ go func() {
+ switch {
+ case z == 1:
+ case z == 2:
+ x = 2
+ }
+ ch <- 1
+ }()
+ <-ch
+ <-ch
+}
+
+func TestRaceCaseFallthrough(t *testing.T) {
+ var x, y, z int
+ ch := make(chan int, 2)
+ z = 1
+
+ go func() {
+ y = x
+ ch <- 1
+ }()
+ go func() {
+ switch {
+ case z == 1:
+ fallthrough
+ case z == 2:
+ x = 2
+ }
+ ch <- 1
+ }()
+
+ <-ch
+ <-ch
+}
+
+func TestNoRaceRange(t *testing.T) {
+ ch := make(chan int, 3)
+ a := [...]int{1, 2, 3}
+ for _, v := range a {
+ ch <- v
+ }
+ close(ch)
+}
+
+func TestRaceRange(t *testing.T) {
+ const N = 2
+ var a [N]int
+ var x, y int
+ done := make(chan bool, N)
+ for i, v := range a {
+ go func(i int) {
+ // we don't want a write-vs-write race
+ // so there is no array b here
+ if i == 0 {
+ x = v
+ } else {
+ y = v
+ }
+ done <- true
+ }(i)
+ }
+ for i := 0; i < N; i++ {
+ <-done
+ }
+}
+
+func TestRacePlus(t *testing.T) {
+ var x, y, z int
+ ch := make(chan int, 2)
+
+ go func() {
+ y = x + z
+ ch <- 1
+ }()
+ go func() {
+ y = x + z + z
+ ch <- 1
+ }()
+ <-ch
+ <-ch
+}
+
+func TestRacePlus2(t *testing.T) {
+ var x, y, z int
+ ch := make(chan int, 2)
+
+ go func() {
+ x = 1
+ ch <- 1
+ }()
+ go func() {
+ y = +x + z
+ ch <- 1
+ }()
+ <-ch
+ <-ch
+}
+
+func TestNoRacePlus(t *testing.T) {
+ var x, y, z, f int
+ ch := make(chan int, 2)
+
+ go func() {
+ y = x + z
+ ch <- 1
+ }()
+ go func() {
+ f = z + x
+ ch <- 1
+ }()
+ <-ch
+ <-ch
+}
+
+// May crash if the instrumentation is reckless.
+func TestNoRaceEnoughRegisters(t *testing.T) {
+ // from erf.go
+ const (
+ sa1 = 1
+ sa2 = 2
+ sa3 = 3
+ sa4 = 4
+ sa5 = 5
+ sa6 = 6
+ sa7 = 7
+ sa8 = 8
+ )
+ var s, S float64
+ s = 3.1415
+ S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8)))))))
+ s = S
+}
+
+// emptyFunc should not be inlined.
+func emptyFunc(x int) {
+ if false {
+ fmt.Println(x)
+ }
+}
+
+func TestRaceFuncArgument(t *testing.T) {
+ var x int
+ ch := make(chan bool, 1)
+ go func() {
+ emptyFunc(x)
+ ch <- true
+ }()
+ x = 1
+ <-ch
+}
+
+func TestRaceFuncArgument2(t *testing.T) {
+ var x int
+ ch := make(chan bool, 2)
+ go func() {
+ x = 42
+ ch <- true
+ }()
+ go func(y int) {
+ ch <- true
+ }(x)
+ <-ch
+ <-ch
+}
+
+func TestRaceSprint(t *testing.T) {
+ var x int
+ ch := make(chan bool, 1)
+ go func() {
+ fmt.Sprint(x)
+ ch <- true
+ }()
+ x = 1
+ <-ch
+}
+
+// Not implemented.
+func TestRaceFailingArrayCopy(t *testing.T) {
+ ch := make(chan bool, 1)
+ var a [5]int
+ go func() {
+ a[3] = 1
+ ch <- true
+ }()
+ a = [5]int{1, 2, 3, 4, 5}
+ <-ch
+}
+
+func TestRaceStructRW(t *testing.T) {
+ p := Point{0, 0}
+ ch := make(chan bool, 1)
+ go func() {
+ p = Point{1, 1}
+ ch <- true
+ }()
+ q := p
+ <-ch
+ p = q
+}
+
+func TestRaceStructFieldRW1(t *testing.T) {
+ p := Point{0, 0}
+ ch := make(chan bool, 1)
+ go func() {
+ p.x = 1
+ ch <- true
+ }()
+ _ = p.x
+ <-ch
+}
+
+func TestNoRaceStructFieldRW1(t *testing.T) {
+ // Same struct, different variables, no
+ // pointers. The layout is known (at compile time?) ->
+ // no read on p
+ // writes on x and y
+ p := Point{0, 0}
+ ch := make(chan bool, 1)
+ go func() {
+ p.x = 1
+ ch <- true
+ }()
+ p.y = 1
+ <-ch
+ _ = p
+}
+
+func TestNoRaceStructFieldRW2(t *testing.T) {
+ // Same as NoRaceStructFieldRW1
+ // but p is a pointer, so there is a read on p
+ p := Point{0, 0}
+ ch := make(chan bool, 1)
+ go func() {
+ p.x = 1
+ ch <- true
+ }()
+ p.y = 1
+ <-ch
+ _ = p
+}
+
+func TestRaceStructFieldRW2(t *testing.T) {
+ p := &Point{0, 0}
+ ch := make(chan bool, 1)
+ go func() {
+ p.x = 1
+ ch <- true
+ }()
+ _ = p.x
+ <-ch
+}
+
+func TestRaceStructFieldRW3(t *testing.T) {
+ p := NamedPoint{name: "a", p: Point{0, 0}}
+ ch := make(chan bool, 1)
+ go func() {
+ p.p.x = 1
+ ch <- true
+ }()
+ _ = p.p.x
+ <-ch
+}
+
+func TestRaceEfaceWW(t *testing.T) {
+ var a, b interface{}
+ ch := make(chan bool, 1)
+ go func() {
+ a = 1
+ ch <- true
+ }()
+ a = 2
+ <-ch
+ _, _ = a, b
+}
+
+func TestRaceIfaceWW(t *testing.T) {
+ var a, b Writer
+ ch := make(chan bool, 1)
+ go func() {
+ a = DummyWriter{1}
+ ch <- true
+ }()
+ a = DummyWriter{2}
+ <-ch
+ b = a
+ a = b
+}
+
+func TestRaceEfaceConv(t *testing.T) {
+ c := make(chan bool)
+ v := 0
+ go func() {
+ go func(x interface{}) {
+ }(v)
+ c <- true
+ }()
+ v = 42
+ <-c
+}
+
+type OsFile struct{}
+
+func (*OsFile) Read() {
+}
+
+type IoReader interface {
+ Read()
+}
+
+func TestRaceIfaceConv(t *testing.T) {
+ c := make(chan bool)
+ f := &OsFile{}
+ go func() {
+ go func(x IoReader) {
+ }(f)
+ c <- true
+ }()
+ f = &OsFile{}
+ <-c
+}
+
+func TestRaceError(t *testing.T) {
+ ch := make(chan bool, 1)
+ var err error
+ go func() {
+ err = nil
+ ch <- true
+ }()
+ _ = err
+ <-ch
+}
+
+func TestRaceIntptrRW(t *testing.T) {
+ var x, y int
+ var p *int = &x
+ ch := make(chan bool, 1)
+ go func() {
+ *p = 5
+ ch <- true
+ }()
+ y = *p
+ x = y
+ <-ch
+}
+
+func TestRaceStringRW(t *testing.T) {
+ ch := make(chan bool, 1)
+ s := ""
+ go func() {
+ s = "abacaba"
+ ch <- true
+ }()
+ _ = s
+ <-ch
+}
+
+func TestRaceStringPtrRW(t *testing.T) {
+ ch := make(chan bool, 1)
+ var x string
+ p := &x
+ go func() {
+ *p = "a"
+ ch <- true
+ }()
+ _ = *p
+ <-ch
+}
+
+func TestRaceFloat64WW(t *testing.T) {
+ var x, y float64
+ ch := make(chan bool, 1)
+ go func() {
+ x = 1.0
+ ch <- true
+ }()
+ x = 2.0
+ <-ch
+
+ y = x
+ x = y
+}
+
+func TestRaceComplex128WW(t *testing.T) {
+ var x, y complex128
+ ch := make(chan bool, 1)
+ go func() {
+ x = 2 + 2i
+ ch <- true
+ }()
+ x = 4 + 4i
+ <-ch
+
+ y = x
+ x = y
+}
+
+func TestRaceUnsafePtrRW(t *testing.T) {
+ var x, y, z int
+ x, y, z = 1, 2, 3
+ var p unsafe.Pointer = unsafe.Pointer(&x)
+ ch := make(chan bool, 1)
+ go func() {
+ p = (unsafe.Pointer)(&z)
+ ch <- true
+ }()
+ y = *(*int)(p)
+ x = y
+ <-ch
+}
+
+func TestRaceFuncVariableRW(t *testing.T) {
+ var f func(x int) int
+ f = func(x int) int {
+ return x * x
+ }
+ ch := make(chan bool, 1)
+ go func() {
+ f = func(x int) int {
+ return x
+ }
+ ch <- true
+ }()
+ y := f(1)
+ <-ch
+ x := y
+ y = x
+}
+
+func TestRaceFuncVariableWW(t *testing.T) {
+ var f func(x int) int
+ ch := make(chan bool, 1)
+ go func() {
+ f = func(x int) int {
+ return x
+ }
+ ch <- true
+ }()
+ f = func(x int) int {
+ return x * x
+ }
+ <-ch
+}
+
+// This one should not belong to mop_test
+func TestRacePanic(t *testing.T) {
+ var x int
+ var zero int = 0
+ ch := make(chan bool, 2)
+ go func() {
+ defer func() {
+ err := recover()
+ if err == nil {
+ panic("should be panicking")
+ }
+ x = 1
+ ch <- true
+ }()
+ var y int = 1 / zero
+ zero = y
+ }()
+ go func() {
+ defer func() {
+ err := recover()
+ if err == nil {
+ panic("should be panicking")
+ }
+ x = 2
+ ch <- true
+ }()
+ var y int = 1 / zero
+ zero = y
+ }()
+
+ <-ch
+ <-ch
+ if zero != 0 {
+ panic("zero has changed")
+ }
+}
+
+func TestNoRaceBlank(t *testing.T) {
+ var a [5]int
+ ch := make(chan bool, 1)
+ go func() {
+ _, _ = a[0], a[1]
+ ch <- true
+ }()
+ _, _ = a[2], a[3]
+ <-ch
+ a[1] = a[0]
+}
+
+func TestRaceAppendRW(t *testing.T) {
+ a := make([]int, 10)
+ ch := make(chan bool)
+ go func() {
+ _ = append(a, 1)
+ ch <- true
+ }()
+ a[0] = 1
+ <-ch
+}
+
+func TestRaceAppendLenRW(t *testing.T) {
+ a := make([]int, 0)
+ ch := make(chan bool)
+ go func() {
+ a = append(a, 1)
+ ch <- true
+ }()
+ _ = len(a)
+ <-ch
+}
+
+func TestRaceAppendCapRW(t *testing.T) {
+ a := make([]int, 0)
+ ch := make(chan string)
+ go func() {
+ a = append(a, 1)
+ ch <- ""
+ }()
+ _ = cap(a)
+ <-ch
+}
+
+func TestNoRaceFuncArgsRW(t *testing.T) {
+ ch := make(chan byte, 1)
+ var x byte
+ go func(y byte) {
+ _ = y
+ ch <- 0
+ }(x)
+ x = 1
+ <-ch
+}
+
+func TestRaceFuncArgsRW(t *testing.T) {
+ ch := make(chan byte, 1)
+ var x byte
+ go func(y *byte) {
+ _ = *y
+ ch <- 0
+ }(&x)
+ x = 1
+ <-ch
+}
+
+// from the mailing list, slightly modified
+// unprotected concurrent access to seen[]
+func TestRaceCrawl(t *testing.T) {
+ url := "dummyurl"
+ depth := 3
+ seen := make(map[string]bool)
+ ch := make(chan int, 100)
+ var wg sync.WaitGroup
+ var crawl func(string, int)
+ crawl = func(u string, d int) {
+ nurl := 0
+ defer func() {
+ ch <- nurl
+ }()
+ seen[u] = true
+ if d <= 0 {
+ return
+ }
+ urls := [...]string{"a", "b", "c"}
+ for _, uu := range urls {
+ if _, ok := seen[uu]; !ok {
+ wg.Add(1)
+ go crawl(uu, d-1)
+ nurl++
+ }
+ }
+ wg.Done()
+ }
+ wg.Add(1)
+ go crawl(url, depth)
+ wg.Wait()
+}
+
+func TestRaceIndirection(t *testing.T) {
+ ch := make(chan struct{}, 1)
+ var y int
+ var x *int = &y
+ go func() {
+ *x = 1
+ ch <- struct{}{}
+ }()
+ *x = 2
+ <-ch
+ _ = *x
+}
+
+func TestRaceRune(t *testing.T) {
+ c := make(chan bool)
+ var x rune
+ go func() {
+ x = 1
+ c <- true
+ }()
+ _ = x
+ <-c
+}
+
+func TestRaceEmptyInterface1(t *testing.T) {
+ c := make(chan bool)
+ var x interface{}
+ go func() {
+ x = nil
+ c <- true
+ }()
+ _ = x
+ <-c
+}
+
+func TestRaceEmptyInterface2(t *testing.T) {
+ c := make(chan bool)
+ var x interface{}
+ go func() {
+ x = &Point{}
+ c <- true
+ }()
+ _ = x
+ <-c
+}
+
+func TestRaceTLS(t *testing.T) {
+ comm := make(chan *int)
+ done := make(chan bool, 2)
+ go func() {
+ var x int
+ comm <- &x
+ x = 1
+ x = *(<-comm)
+ done <- true
+ }()
+ go func() {
+ p := <-comm
+ *p = 2
+ comm <- p
+ done <- true
+ }()
+ <-done
+ <-done
+}
+
+func TestNoRaceHeapReallocation(t *testing.T) {
+ // It is possible that a future implementation
+ // of memory allocation will ruin this test.
+ // Increasing n might help in this case, so
+ // this test is a bit more generic than most of the
+ // others.
+ const n = 2
+ done := make(chan bool, n)
+ empty := func(p *int) {}
+ for i := 0; i < n; i++ {
+ ms := i
+ go func() {
+ <-time.After(time.Duration(ms) * time.Millisecond)
+ runtime.GC()
+ var x int
+ empty(&x) // x goes to the heap
+ done <- true
+ }()
+ }
+ for i := 0; i < n; i++ {
+ <-done
+ }
+}
+
+func TestRaceAnd(t *testing.T) {
+ c := make(chan bool)
+ x, y := 0, 0
+ go func() {
+ x = 1
+ c <- true
+ }()
+ if x == 1 && y == 1 {
+ }
+ <-c
+}
+
+// OANDAND is not instrumented in the compiler.
+func TestRaceFailingAnd2(t *testing.T) {
+ c := make(chan bool)
+ x, y := 0, 0
+ go func() {
+ x = 1
+ c <- true
+ }()
+ if y == 0 && x == 1 {
+ }
+ <-c
+}
+
+func TestNoRaceAnd(t *testing.T) {
+ c := make(chan bool)
+ x, y := 0, 0
+ go func() {
+ x = 1
+ c <- true
+ }()
+ if y == 1 && x == 1 {
+ }
+ <-c
+}
+
+func TestRaceOr(t *testing.T) {
+ c := make(chan bool)
+ x, y := 0, 0
+ go func() {
+ x = 1
+ c <- true
+ }()
+ if x == 1 || y == 1 {
+ }
+ <-c
+}
+
+// OOROR is not instrumented in the compiler.
+func TestRaceFailingOr2(t *testing.T) {
+ c := make(chan bool)
+ x, y := 0, 0
+ go func() {
+ x = 1
+ c <- true
+ }()
+ if y == 1 || x == 1 {
+ }
+ <-c
+}
+
+func TestNoRaceOr(t *testing.T) {
+ c := make(chan bool)
+ x, y := 0, 0
+ go func() {
+ x = 1
+ c <- true
+ }()
+ if y == 0 || x == 1 {
+ }
+ <-c
+}
+
+func TestNoRaceShortCalc(t *testing.T) {
+ c := make(chan bool)
+ x, y := 0, 0
+ go func() {
+ y = 1
+ c <- true
+ }()
+ if x == 0 || y == 0 {
+ }
+ <-c
+}
+
+func TestNoRaceShortCalc2(t *testing.T) {
+ c := make(chan bool)
+ x, y := 0, 0
+ go func() {
+ y = 1
+ c <- true
+ }()
+ if x == 1 && y == 0 {
+ }
+ <-c
+}
+
+func TestRaceFuncItself(t *testing.T) {
+ c := make(chan bool)
+ f := func() {}
+ go func() {
+ f()
+ c <- true
+ }()
+ f = func() {}
+ <-c
+}
+
+func TestNoRaceFuncUnlock(t *testing.T) {
+ ch := make(chan bool, 1)
+ var mu sync.Mutex
+ x := 0
+ go func() {
+ mu.Lock()
+ x = 42
+ mu.Unlock()
+ ch <- true
+ }()
+ x = func(mu *sync.Mutex) int {
+ mu.Lock()
+ return 43
+ }(&mu)
+ mu.Unlock()
+ <-ch
+}
+
+func TestRaceStructInit(t *testing.T) {
+ type X struct {
+ x, y int
+ }
+ c := make(chan bool, 1)
+ y := 0
+ go func() {
+ y = 42
+ c <- true
+ }()
+ x := X{x: y}
+ _ = x
+ <-c
+}
+
+func TestRaceArrayInit(t *testing.T) {
+ c := make(chan bool, 1)
+ y := 0
+ go func() {
+ y = 42
+ c <- true
+ }()
+ x := []int{0, y, 42}
+ _ = x
+ <-c
+}
+
+func TestRaceMapInit(t *testing.T) {
+ c := make(chan bool, 1)
+ y := 0
+ go func() {
+ y = 42
+ c <- true
+ }()
+ x := map[int]int{0: 42, y: 42}
+ _ = x
+ <-c
+}
+
+func TestRaceMapInit2(t *testing.T) {
+ c := make(chan bool, 1)
+ y := 0
+ go func() {
+ y = 42
+ c <- true
+ }()
+ x := map[int]int{0: 42, 42: y}
+ _ = x
+ <-c
+}
+
+type Inter interface {
+ Foo(x int)
+}
+type InterImpl struct {
+ x, y int
+}
+
+func (p InterImpl) Foo(x int) {
+ // prevent inlining
+ z := 42
+ x = 85
+ y := x / z
+ z = y * z
+ x = z * y
+ _, _, _ = x, y, z
+}
+
+func TestRaceInterCall(t *testing.T) {
+ c := make(chan bool, 1)
+ p := InterImpl{}
+ var x Inter = p
+ go func() {
+ p2 := InterImpl{}
+ x = p2
+ c <- true
+ }()
+ x.Foo(0)
+ <-c
+}
+
+func TestRaceInterCall2(t *testing.T) {
+ c := make(chan bool, 1)
+ p := InterImpl{}
+ var x Inter = p
+ z := 0
+ go func() {
+ z = 42
+ c <- true
+ }()
+ x.Foo(z)
+ <-c
+}
+
+func TestRaceFuncCall(t *testing.T) {
+ c := make(chan bool, 1)
+ f := func(x, y int) {}
+ x, y := 0, 0
+ go func() {
+ y = 42
+ c <- true
+ }()
+ f(x, y)
+ <-c
+}
+
+func TestRaceMethodCall(t *testing.T) {
+ c := make(chan bool, 1)
+ i := InterImpl{}
+ x := 0
+ go func() {
+ x = 42
+ c <- true
+ }()
+ i.Foo(x)
+ <-c
+}
+
+func TestRaceMethodCall2(t *testing.T) {
+ c := make(chan bool, 1)
+ i := &InterImpl{}
+ go func() {
+ i = &InterImpl{}
+ c <- true
+ }()
+ i.Foo(0)
+ <-c
+}
+
+func TestRacePanicArg(t *testing.T) {
+ c := make(chan bool, 1)
+ err := errors.New("err")
+ go func() {
+ err = errors.New("err2")
+ c <- true
+ }()
+ defer func() {
+ recover()
+ <-c
+ }()
+ panic(err)
+}
+
+func TestRaceDeferArg(t *testing.T) {
+ c := make(chan bool, 1)
+ x := 0
+ go func() {
+ x = 42
+ c <- true
+ }()
+ func() {
+ defer func(x int) {
+ }(x)
+ }()
+ <-c
+}
+
+type DeferT int
+
+func (d DeferT) Foo() {
+}
+
+func TestRaceDeferArg2(t *testing.T) {
+ c := make(chan bool, 1)
+ var x DeferT
+ go func() {
+ var y DeferT
+ x = y
+ c <- true
+ }()
+ func() {
+ defer x.Foo()
+ }()
+ <-c
+}
+
+func TestNoRaceAddrExpr(t *testing.T) {
+ c := make(chan bool, 1)
+ x := 0
+ go func() {
+ x = 42
+ c <- true
+ }()
+ _ = &x
+ <-c
+}
+
+type AddrT struct {
+ _ [256]byte
+ x int
+}
+
+type AddrT2 struct {
+ _ [512]byte
+ p *AddrT
+}
+
+func TestRaceAddrExpr(t *testing.T) {
+ c := make(chan bool, 1)
+ a := AddrT2{p: &AddrT{x: 42}}
+ go func() {
+ a.p = &AddrT{x: 43}
+ c <- true
+ }()
+ _ = &a.p.x
+ <-c
+}
+
+func TestRaceTypeAssert(t *testing.T) {
+ c := make(chan bool, 1)
+ x := 0
+ var i interface{} = x
+ go func() {
+ y := 0
+ i = y
+ c <- true
+ }()
+ _ = i.(int)
+ <-c
+}
+
+func TestRaceBlockAs(t *testing.T) {
+ c := make(chan bool, 1)
+ var x, y int
+ go func() {
+ x = 42
+ c <- true
+ }()
+ x, y = y, x
+ <-c
+}
+
+func TestRaceSliceSlice(t *testing.T) {
+ c := make(chan bool, 1)
+ x := make([]int, 10)
+ go func() {
+ x = make([]int, 20)
+ c <- true
+ }()
+ _ = x[2:3]
+ <-c
+}
+
+func TestRaceSliceSlice2(t *testing.T) {
+ c := make(chan bool, 1)
+ x := make([]int, 10)
+ i := 2
+ go func() {
+ i = 3
+ c <- true
+ }()
+ _ = x[i:4]
+ <-c
+}
+
+// http://golang.org/issue/4453
+func TestRaceFailingSliceStruct(t *testing.T) {
+ type X struct {
+ x, y int
+ }
+ c := make(chan bool, 1)
+ x := make([]X, 10)
+ go func() {
+ y := make([]X, 10)
+ copy(y, x)
+ c <- true
+ }()
+ x[1].y = 42
+ <-c
+}
+
+func TestRaceStructInd(t *testing.T) {
+ c := make(chan bool, 1)
+ type Item struct {
+ x, y int
+ }
+ i := Item{}
+ go func(p *Item) {
+ *p = Item{}
+ c <- true
+ }(&i)
+ i.y = 42
+ <-c
+}
+
+func TestRaceAsFunc1(t *testing.T) {
+ var s []byte
+ c := make(chan bool, 1)
+ go func() {
+ var err error
+ s, err = func() ([]byte, error) {
+ t := []byte("hello world")
+ return t, nil
+ }()
+ c <- true
+ _ = err
+ }()
+ _ = string(s)
+ <-c
+}
+
+func TestRaceAsFunc2(t *testing.T) {
+ c := make(chan bool, 1)
+ x := 0
+ go func() {
+ func(x int) {
+ }(x)
+ c <- true
+ }()
+ x = 42
+ <-c
+}
+
+func TestRaceAsFunc3(t *testing.T) {
+ c := make(chan bool, 1)
+ var mu sync.Mutex
+ x := 0
+ go func() {
+ func(x int) {
+ mu.Lock()
+ }(x) // Read of x must be outside of the mutex.
+ mu.Unlock()
+ c <- true
+ }()
+ mu.Lock()
+ x = 42
+ mu.Unlock()
+ <-c
+}
+
+func TestNoRaceAsFunc4(t *testing.T) {
+ c := make(chan bool, 1)
+ var mu sync.Mutex
+ x := 0
+ go func() {
+ x = func() int { // Write of x must be under the mutex.
+ mu.Lock()
+ return 42
+ }()
+ mu.Unlock()
+ c <- true
+ }()
+ mu.Lock()
+ x = 42
+ mu.Unlock()
+ <-c
+}
+
+func TestRaceHeapParam(t *testing.T) {
+ x := func() (x int) {
+ go func() {
+ x = 42
+ }()
+ return
+ }()
+ _ = x
+}
+
+func TestNoRaceEmptyStruct(t *testing.T) {
+ type Empty struct{}
+ type X struct {
+ y int64
+ Empty
+ }
+ type Y struct {
+ x X
+ y int64
+ }
+ c := make(chan X)
+ var y Y
+ go func() {
+ x := y.x
+ c <- x
+ }()
+ y.y = 42
+ <-c
+}
+
+func TestRaceNestedStruct(t *testing.T) {
+ type X struct {
+ x, y int
+ }
+ type Y struct {
+ x X
+ }
+ c := make(chan Y)
+ var y Y
+ go func() {
+ c <- y
+ }()
+ y.x.y = 42
+ <-c
+}
diff --git a/src/pkg/runtime/race/testdata/mutex_test.go b/src/pkg/runtime/race/testdata/mutex_test.go
new file mode 100644
index 000000000..3cf03ae6b
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/mutex_test.go
@@ -0,0 +1,138 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "sync"
+ "testing"
+ "time"
+)
+
+func TestNoRaceMutex(t *testing.T) {
+ var mu sync.Mutex
+ var x int16 = 0
+ ch := make(chan bool, 2)
+ go func() {
+ mu.Lock()
+ defer mu.Unlock()
+ x = 1
+ ch <- true
+ }()
+ go func() {
+ mu.Lock()
+ x = 2
+ mu.Unlock()
+ ch <- true
+ }()
+ <-ch
+ <-ch
+}
+
+func TestRaceMutex(t *testing.T) {
+ var mu sync.Mutex
+ var x int16 = 0
+ ch := make(chan bool, 2)
+ go func() {
+ x = 1
+ mu.Lock()
+ defer mu.Unlock()
+ ch <- true
+ }()
+ go func() {
+ x = 2
+ mu.Lock()
+ mu.Unlock()
+ ch <- true
+ }()
+ <-ch
+ <-ch
+}
+
+func TestRaceMutex2(t *testing.T) {
+ var mu1 sync.Mutex
+ var mu2 sync.Mutex
+ var x int8 = 0
+ ch := make(chan bool, 2)
+ go func() {
+ mu1.Lock()
+ defer mu1.Unlock()
+ x = 1
+ ch <- true
+ }()
+ go func() {
+ mu2.Lock()
+ x = 2
+ mu2.Unlock()
+ ch <- true
+ }()
+ <-ch
+ <-ch
+}
+
+func TestNoRaceMutexPureHappensBefore(t *testing.T) {
+ var mu sync.Mutex
+ var x int16 = 0
+ ch := make(chan bool, 2)
+ go func() {
+ x = 1
+ mu.Lock()
+ mu.Unlock()
+ ch <- true
+ }()
+ go func() {
+ <-time.After(1e5)
+ mu.Lock()
+ mu.Unlock()
+ x = 1
+ ch <- true
+ }()
+ <-ch
+ <-ch
+}
+
+func TestNoRaceMutexSemaphore(t *testing.T) {
+ var mu sync.Mutex
+ ch := make(chan bool, 2)
+ x := 0
+ mu.Lock()
+ go func() {
+ x = 1
+ mu.Unlock()
+ ch <- true
+ }()
+ go func() {
+ mu.Lock()
+ x = 2
+ mu.Unlock()
+ ch <- true
+ }()
+ <-ch
+ <-ch
+}
+
+// from doc/go_mem.html
+func TestNoRaceMutexExampleFromHtml(t *testing.T) {
+ var l sync.Mutex
+ a := ""
+
+ l.Lock()
+ go func() {
+ a = "hello, world"
+ l.Unlock()
+ }()
+ l.Lock()
+ _ = a
+}
+
+func TestRaceMutexOverwrite(t *testing.T) {
+ c := make(chan bool, 1)
+ var mu sync.Mutex
+ go func() {
+ mu = sync.Mutex{}
+ c <- true
+ }()
+ mu.Lock()
+ <-c
+}
diff --git a/src/pkg/runtime/race/testdata/regression_test.go b/src/pkg/runtime/race/testdata/regression_test.go
new file mode 100644
index 000000000..afe8cc5ec
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/regression_test.go
@@ -0,0 +1,150 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Code patterns that caused problems in the past.
+
+package race_test
+
+import (
+ "testing"
+)
+
+type LogImpl struct {
+ x int
+}
+
+func NewLog() (l LogImpl) {
+ c := make(chan bool)
+ go func() {
+ _ = l
+ c <- true
+ }()
+ l = LogImpl{}
+ <-c
+ return
+}
+
+var _ LogImpl = NewLog()
+
+func MakeMap() map[int]int {
+ return make(map[int]int)
+}
+
+func InstrumentMapLen() {
+ _ = len(MakeMap())
+}
+
+func InstrumentMapLen2() {
+ m := make(map[int]map[int]int)
+ _ = len(m[0])
+}
+
+func InstrumentMapLen3() {
+ m := make(map[int]*map[int]int)
+ _ = len(*m[0])
+}
+
+type Rect struct {
+ x, y int
+}
+
+type Image struct {
+ min, max Rect
+}
+
+func NewImage() Image {
+ var pleaseDoNotInlineMe stack
+ pleaseDoNotInlineMe.push(1)
+ _ = pleaseDoNotInlineMe.pop()
+ return Image{}
+}
+
+func AddrOfTemp() {
+ _ = NewImage().min
+}
+
+type TypeID int
+
+func (t *TypeID) encodeType(x int) (tt TypeID, err error) {
+ switch x {
+ case 0:
+ return t.encodeType(x * x)
+ }
+ return 0, nil
+}
+
+type stack []int
+
+func (s *stack) push(x int) {
+ *s = append(*s, x)
+}
+
+func (s *stack) pop() int {
+ i := len(*s)
+ n := (*s)[i-1]
+ *s = (*s)[:i-1]
+ return n
+}
+
+func TestNoRaceStackPushPop(t *testing.T) {
+ var s stack
+ go func(s *stack) {}(&s)
+ s.push(1)
+ x := s.pop()
+ _ = x
+}
+
+type RpcChan struct {
+ c chan bool
+}
+
+var makeChanCalls int
+
+func makeChan() *RpcChan {
+ var pleaseDoNotInlineMe stack
+ pleaseDoNotInlineMe.push(1)
+ _ = pleaseDoNotInlineMe.pop()
+
+ makeChanCalls++
+ c := &RpcChan{make(chan bool, 1)}
+ c.c <- true
+ return c
+}
+
+func call() bool {
+ x := <-makeChan().c
+ return x
+}
+
+func TestNoRaceRpcChan(t *testing.T) {
+ makeChanCalls = 0
+ _ = call()
+ if makeChanCalls != 1 {
+ t.Fatalf("makeChanCalls %d, expected 1\n", makeChanCalls)
+ }
+}
+
+func divInSlice() {
+ v := make([]int64, 10)
+ i := 1
+ _ = v[(i*4)/3]
+}
+
+func TestNoRaceReturn(t *testing.T) {
+ c := make(chan int)
+ noRaceReturn(c)
+ <-c
+}
+
+// Return used to do an implicit a = a, causing a read/write race
+// with the goroutine. Compiler has an optimization to avoid that now.
+// See issue 4014.
+func noRaceReturn(c chan int) (a, b int) {
+ a = 42
+ go func() {
+ _ = a
+ c <- 1
+ }()
+ return a, 10
+}
diff --git a/src/pkg/runtime/race/testdata/rwmutex_test.go b/src/pkg/runtime/race/testdata/rwmutex_test.go
new file mode 100644
index 000000000..85cb5df3c
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/rwmutex_test.go
@@ -0,0 +1,134 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "sync"
+ "testing"
+ "time"
+)
+
+func TestRaceMutexRWMutex(t *testing.T) {
+ var mu1 sync.Mutex
+ var mu2 sync.RWMutex
+ var x int16 = 0
+ ch := make(chan bool, 2)
+ go func() {
+ mu1.Lock()
+ defer mu1.Unlock()
+ x = 1
+ ch <- true
+ }()
+ go func() {
+ mu2.Lock()
+ x = 2
+ mu2.Unlock()
+ ch <- true
+ }()
+ <-ch
+ <-ch
+}
+
+func TestNoRaceRWMutex(t *testing.T) {
+ var mu sync.RWMutex
+ var x, y int64 = 0, 1
+ ch := make(chan bool, 2)
+ go func() {
+ mu.Lock()
+ defer mu.Unlock()
+ x = 2
+ ch <- true
+ }()
+ go func() {
+ mu.RLock()
+ y = x
+ mu.RUnlock()
+ ch <- true
+ }()
+ <-ch
+ <-ch
+}
+
+func TestRaceRWMutexMultipleReaders(t *testing.T) {
+ var mu sync.RWMutex
+ var x, y int64 = 0, 1
+ ch := make(chan bool, 3)
+ go func() {
+ mu.Lock()
+ defer mu.Unlock()
+ x = 2
+ ch <- true
+ }()
+ go func() {
+ mu.RLock()
+ y = x + 1
+ mu.RUnlock()
+ ch <- true
+ }()
+ go func() {
+ mu.RLock()
+ y = x + 2
+ mu.RUnlock()
+ ch <- true
+ }()
+ <-ch
+ <-ch
+ <-ch
+ _ = y
+}
+
+func TestNoRaceRWMutexMultipleReaders(t *testing.T) {
+ var mu sync.RWMutex
+ x := int64(0)
+ ch := make(chan bool, 3)
+ go func() {
+ mu.Lock()
+ defer mu.Unlock()
+ x = 2
+ ch <- true
+ }()
+ go func() {
+ mu.RLock()
+ y := x + 1
+ _ = y
+ mu.RUnlock()
+ ch <- true
+ }()
+ go func() {
+ mu.RLock()
+ y := x + 2
+ _ = y
+ mu.RUnlock()
+ ch <- true
+ }()
+ <-ch
+ <-ch
+ <-ch
+}
+
+func TestNoRaceRWMutexTransitive(t *testing.T) {
+ var mu sync.RWMutex
+ x := int64(0)
+ ch := make(chan bool, 2)
+ go func() {
+ mu.RLock()
+ _ = x
+ mu.RUnlock()
+ ch <- true
+ }()
+ go func() {
+ time.Sleep(1e7)
+ mu.RLock()
+ _ = x
+ mu.RUnlock()
+ ch <- true
+ }()
+ time.Sleep(2e7)
+ mu.Lock()
+ x = 42
+ mu.Unlock()
+ <-ch
+ <-ch
+}
diff --git a/src/pkg/runtime/race/testdata/select_test.go b/src/pkg/runtime/race/testdata/select_test.go
new file mode 100644
index 000000000..4a3a23647
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/select_test.go
@@ -0,0 +1,286 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "runtime"
+ "testing"
+)
+
+func TestNoRaceSelect1(t *testing.T) {
+ var x int
+ compl := make(chan bool)
+ c := make(chan bool)
+ c1 := make(chan bool)
+
+ go func() {
+ x = 1
+ // At least two channels are needed because
+ // otherwise the compiler optimizes select out.
+ // See comment in runtime/chan.c:^selectgo.
+ select {
+ case c <- true:
+ case c1 <- true:
+ }
+ compl <- true
+ }()
+ select {
+ case <-c:
+ case c1 <- true:
+ }
+ x = 2
+ <-compl
+}
+
+func TestNoRaceSelect2(t *testing.T) {
+ var x int
+ compl := make(chan bool)
+ c := make(chan bool)
+ c1 := make(chan bool)
+ go func() {
+ select {
+ case <-c:
+ case <-c1:
+ }
+ x = 1
+ compl <- true
+ }()
+ x = 2
+ close(c)
+ runtime.Gosched()
+ <-compl
+}
+
+func TestNoRaceSelect3(t *testing.T) {
+ var x int
+ compl := make(chan bool)
+ c := make(chan bool, 10)
+ c1 := make(chan bool)
+ go func() {
+ x = 1
+ select {
+ case c <- true:
+ case <-c1:
+ }
+ compl <- true
+ }()
+ <-c
+ x = 2
+ <-compl
+}
+
+func TestNoRaceSelect4(t *testing.T) {
+ type Task struct {
+ f func()
+ done chan bool
+ }
+
+ queue := make(chan Task)
+ dummy := make(chan bool)
+
+ go func() {
+ for {
+ select {
+ case t := <-queue:
+ t.f()
+ t.done <- true
+ }
+ }
+ }()
+
+ doit := func(f func()) {
+ done := make(chan bool, 1)
+ select {
+ case queue <- Task{f, done}:
+ case <-dummy:
+ }
+ select {
+ case <-done:
+ case <-dummy:
+ }
+ }
+
+ var x int
+ doit(func() {
+ x = 1
+ })
+ _ = x
+}
+
+func TestNoRaceSelect5(t *testing.T) {
+ test := func(sel, needSched bool) {
+ var x int
+ ch := make(chan bool)
+ c1 := make(chan bool)
+
+ done := make(chan bool, 2)
+ go func() {
+ if needSched {
+ runtime.Gosched()
+ }
+ // println(1)
+ x = 1
+ if sel {
+ select {
+ case ch <- true:
+ case <-c1:
+ }
+ } else {
+ ch <- true
+ }
+ done <- true
+ }()
+
+ go func() {
+ // println(2)
+ if sel {
+ select {
+ case <-ch:
+ case <-c1:
+ }
+ } else {
+ <-ch
+ }
+ x = 1
+ done <- true
+ }()
+ <-done
+ <-done
+ }
+
+ test(true, true)
+ test(true, false)
+ test(false, true)
+ test(false, false)
+}
+
+func TestRaceSelect1(t *testing.T) {
+ var x int
+ compl := make(chan bool, 2)
+ c := make(chan bool)
+ c1 := make(chan bool)
+
+ go func() {
+ <-c
+ <-c
+ }()
+ f := func() {
+ select {
+ case c <- true:
+ case c1 <- true:
+ }
+ x = 1
+ compl <- true
+ }
+ go f()
+ go f()
+ <-compl
+ <-compl
+}
+
+func TestRaceSelect2(t *testing.T) {
+ var x int
+ compl := make(chan bool)
+ c := make(chan bool)
+ c1 := make(chan bool)
+ go func() {
+ x = 1
+ select {
+ case <-c:
+ case <-c1:
+ }
+ compl <- true
+ }()
+ close(c)
+ x = 2
+ <-compl
+}
+
+func TestRaceSelect3(t *testing.T) {
+ var x int
+ compl := make(chan bool)
+ c := make(chan bool)
+ c1 := make(chan bool)
+ go func() {
+ x = 1
+ select {
+ case c <- true:
+ case c1 <- true:
+ }
+ compl <- true
+ }()
+ x = 2
+ select {
+ case <-c:
+ }
+ <-compl
+}
+
+func TestRaceSelect4(t *testing.T) {
+ done := make(chan bool, 1)
+ var x int
+ go func() {
+ select {
+ default:
+ x = 2
+ }
+ done <- true
+ }()
+ _ = x
+ <-done
+}
+
+// The idea behind this test:
+// there are two variables, access to one
+// of them is synchronized, access to the other
+// is not.
+// Select must (unconditionaly) choose the non-synchronized variable
+// thus causing exactly one race.
+// Currently this test doesn't look like it accomplishes
+// this goal.
+func TestRaceSelect5(t *testing.T) {
+ done := make(chan bool, 1)
+ c1 := make(chan bool, 1)
+ c2 := make(chan bool)
+ var x, y int
+ go func() {
+ select {
+ case c1 <- true:
+ x = 1
+ case c2 <- true:
+ y = 1
+ }
+ done <- true
+ }()
+ _ = x
+ _ = y
+ <-done
+}
+
+// select statements may introduce
+// flakiness: whether this test contains
+// a race depends on the scheduling
+// (some may argue that the code contains
+// this race by definition)
+/*
+func TestFlakyDefault(t *testing.T) {
+ var x int
+ c := make(chan bool, 1)
+ done := make(chan bool, 1)
+ go func() {
+ select {
+ case <-c:
+ x = 2
+ default:
+ x = 3
+ }
+ done <- true
+ }()
+ x = 1
+ c <- true
+ _ = x
+ <-done
+}
+*/
diff --git a/src/pkg/runtime/race/testdata/slice_test.go b/src/pkg/runtime/race/testdata/slice_test.go
new file mode 100644
index 000000000..773463662
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/slice_test.go
@@ -0,0 +1,465 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "testing"
+)
+
+func TestRaceSliceRW(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]int, 2)
+ go func() {
+ a[1] = 1
+ ch <- true
+ }()
+ _ = a[1]
+ <-ch
+}
+
+func TestNoRaceSliceRW(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]int, 2)
+ go func() {
+ a[0] = 1
+ ch <- true
+ }()
+ _ = a[1]
+ <-ch
+}
+
+func TestRaceSliceWW(t *testing.T) {
+ a := make([]int, 10)
+ ch := make(chan bool, 1)
+ go func() {
+ a[1] = 1
+ ch <- true
+ }()
+ a[1] = 2
+ <-ch
+}
+
+func TestNoRaceArrayWW(t *testing.T) {
+ var a [5]int
+ ch := make(chan bool, 1)
+ go func() {
+ a[0] = 1
+ ch <- true
+ }()
+ a[1] = 2
+ <-ch
+}
+
+func TestRaceArrayWW(t *testing.T) {
+ var a [5]int
+ ch := make(chan bool, 1)
+ go func() {
+ a[1] = 1
+ ch <- true
+ }()
+ a[1] = 2
+ <-ch
+}
+
+func TestNoRaceSliceWriteLen(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]bool, 1)
+ go func() {
+ a[0] = true
+ ch <- true
+ }()
+ _ = len(a)
+ <-ch
+}
+
+func TestNoRaceSliceWriteCap(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]uint64, 100)
+ go func() {
+ a[50] = 123
+ ch <- true
+ }()
+ _ = cap(a)
+ <-ch
+}
+
+func TestRaceSliceCopyRead(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]int, 10)
+ b := make([]int, 10)
+ go func() {
+ _ = a[5]
+ ch <- true
+ }()
+ copy(a, b)
+ <-ch
+}
+
+func TestNoRaceSliceWriteCopy(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]int, 10)
+ b := make([]int, 10)
+ go func() {
+ a[5] = 1
+ ch <- true
+ }()
+ copy(a[:5], b[:5])
+ <-ch
+}
+
+func TestRaceSliceCopyWrite2(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]int, 10)
+ b := make([]int, 10)
+ go func() {
+ b[5] = 1
+ ch <- true
+ }()
+ copy(a, b)
+ <-ch
+}
+
+func TestRaceSliceCopyWrite3(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]byte, 10)
+ go func() {
+ a[7] = 1
+ ch <- true
+ }()
+ copy(a, "qwertyqwerty")
+ <-ch
+}
+
+func TestNoRaceSliceCopyRead(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]int, 10)
+ b := make([]int, 10)
+ go func() {
+ _ = b[5]
+ ch <- true
+ }()
+ copy(a, b)
+ <-ch
+}
+
+func TestNoRaceSliceWriteSlice2(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]float64, 10)
+ go func() {
+ a[2] = 1.0
+ ch <- true
+ }()
+ _ = a[0:5]
+ <-ch
+}
+
+func TestRaceSliceWriteSlice(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]float64, 10)
+ go func() {
+ a[2] = 1.0
+ ch <- true
+ }()
+ a = a[5:10]
+ <-ch
+}
+
+func TestNoRaceSliceWriteSlice(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]float64, 10)
+ go func() {
+ a[2] = 1.0
+ ch <- true
+ }()
+ _ = a[5:10]
+ <-ch
+}
+
+func TestNoRaceSliceLenCap(t *testing.T) {
+ ch := make(chan bool, 1)
+ a := make([]struct{}, 10)
+ go func() {
+ _ = len(a)
+ ch <- true
+ }()
+ _ = cap(a)
+ <-ch
+}
+
+func TestNoRaceStructSlicesRangeWrite(t *testing.T) {
+ type Str struct {
+ a []int
+ b []int
+ }
+ ch := make(chan bool, 1)
+ var s Str
+ s.a = make([]int, 10)
+ s.b = make([]int, 10)
+ go func() {
+ for _ = range s.a {
+ }
+ ch <- true
+ }()
+ s.b[5] = 5
+ <-ch
+}
+
+func TestRaceSliceDifferent(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ s2 := s
+ go func() {
+ s[3] = 3
+ c <- true
+ }()
+ // false negative because s2 is PAUTO w/o PHEAP
+ // so we do not instrument it
+ s2[3] = 3
+ <-c
+}
+
+func TestRaceSliceRangeWrite(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ go func() {
+ s[3] = 3
+ c <- true
+ }()
+ for _, v := range s {
+ _ = v
+ }
+ <-c
+}
+
+func TestNoRaceSliceRangeWrite(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ go func() {
+ s[3] = 3
+ c <- true
+ }()
+ for _ = range s {
+ }
+ <-c
+}
+
+func TestRaceSliceRangeAppend(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ go func() {
+ s = append(s, 3)
+ c <- true
+ }()
+ for _ = range s {
+ }
+ <-c
+}
+
+func TestNoRaceSliceRangeAppend(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ go func() {
+ _ = append(s, 3)
+ c <- true
+ }()
+ for _ = range s {
+ }
+ <-c
+}
+
+func TestRaceSliceVarWrite(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ go func() {
+ s[3] = 3
+ c <- true
+ }()
+ s = make([]int, 20)
+ <-c
+}
+
+func TestRaceSliceVarRead(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ go func() {
+ _ = s[3]
+ c <- true
+ }()
+ s = make([]int, 20)
+ <-c
+}
+
+func TestRaceSliceVarRange(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ go func() {
+ for _ = range s {
+ }
+ c <- true
+ }()
+ s = make([]int, 20)
+ <-c
+}
+
+func TestRaceSliceVarAppend(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ go func() {
+ _ = append(s, 10)
+ c <- true
+ }()
+ s = make([]int, 20)
+ <-c
+}
+
+func TestRaceSliceVarCopy(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ go func() {
+ s2 := make([]int, 10)
+ copy(s, s2)
+ c <- true
+ }()
+ s = make([]int, 20)
+ <-c
+}
+
+func TestRaceSliceVarCopy2(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ go func() {
+ s2 := make([]int, 10)
+ copy(s2, s)
+ c <- true
+ }()
+ s = make([]int, 20)
+ <-c
+}
+
+// Not implemented.
+func TestRaceFailingSliceAppend(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10, 20)
+ go func() {
+ _ = append(s, 1)
+ c <- true
+ }()
+ _ = append(s, 2)
+ <-c
+}
+
+func TestRaceSliceAppendWrite(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ go func() {
+ _ = append(s, 1)
+ c <- true
+ }()
+ s[0] = 42
+ <-c
+}
+
+func TestRaceSliceAppendSlice(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ go func() {
+ s2 := make([]int, 10)
+ _ = append(s, s2...)
+ c <- true
+ }()
+ s[0] = 42
+ <-c
+}
+
+func TestRaceSliceAppendSlice2(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ s2foobar := make([]int, 10)
+ go func() {
+ _ = append(s, s2foobar...)
+ c <- true
+ }()
+ s2foobar[5] = 42
+ <-c
+}
+
+func TestRaceSliceAppendString(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]byte, 10)
+ go func() {
+ _ = append(s, "qwerty"...)
+ c <- true
+ }()
+ s[0] = 42
+ <-c
+}
+
+func TestNoRaceSliceIndexAccess(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ v := 0
+ go func() {
+ _ = v
+ c <- true
+ }()
+ s[v] = 1
+ <-c
+}
+
+func TestNoRaceSliceIndexAccess2(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ v := 0
+ go func() {
+ _ = v
+ c <- true
+ }()
+ _ = s[v]
+ <-c
+}
+
+func TestRaceSliceIndexAccess(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ v := 0
+ go func() {
+ v = 1
+ c <- true
+ }()
+ s[v] = 1
+ <-c
+}
+
+func TestRaceSliceIndexAccess2(t *testing.T) {
+ c := make(chan bool, 1)
+ s := make([]int, 10)
+ v := 0
+ go func() {
+ v = 1
+ c <- true
+ }()
+ _ = s[v]
+ <-c
+}
+
+func TestRaceSliceByteToString(t *testing.T) {
+ c := make(chan string)
+ s := make([]byte, 10)
+ go func() {
+ c <- string(s)
+ }()
+ s[0] = 42
+ <-c
+}
+
+func TestRaceSliceRuneToString(t *testing.T) {
+ c := make(chan string)
+ s := make([]rune, 10)
+ go func() {
+ c <- string(s)
+ }()
+ s[9] = 42
+ <-c
+}
diff --git a/src/pkg/runtime/race/testdata/sync_test.go b/src/pkg/runtime/race/testdata/sync_test.go
new file mode 100644
index 000000000..e80ba3b74
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/sync_test.go
@@ -0,0 +1,197 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "sync"
+ "testing"
+ "time"
+)
+
+func TestNoRaceCond(t *testing.T) { // tsan's test02
+ ch := make(chan bool, 1)
+ var x int = 0
+ var mu sync.Mutex
+ var cond *sync.Cond = sync.NewCond(&mu)
+ var condition int = 0
+ var waker func()
+ waker = func() {
+ x = 1
+ mu.Lock()
+ condition = 1
+ cond.Signal()
+ mu.Unlock()
+ }
+
+ var waiter func()
+ waiter = func() {
+ go waker()
+ cond.L.Lock()
+ for condition != 1 {
+ cond.Wait()
+ }
+ cond.L.Unlock()
+ x = 2
+ ch <- true
+ }
+ go waiter()
+ <-ch
+}
+
+func TestRaceCond(t *testing.T) { // tsan's test50
+ ch := make(chan bool, 2)
+
+ var x int = 0
+ var mu sync.Mutex
+ var condition int = 0
+ var cond *sync.Cond = sync.NewCond(&mu)
+
+ var waker func() = func() {
+ <-time.After(1e5)
+ x = 1
+ mu.Lock()
+ condition = 1
+ cond.Signal()
+ mu.Unlock()
+ <-time.After(1e5)
+ mu.Lock()
+ x = 3
+ mu.Unlock()
+ ch <- true
+ }
+
+ var waiter func() = func() {
+ mu.Lock()
+ for condition != 1 {
+ cond.Wait()
+ }
+ mu.Unlock()
+ x = 2
+ ch <- true
+ }
+ x = 0
+ go waker()
+ go waiter()
+ <-ch
+ <-ch
+}
+
+// We do not currently automatically
+// parse this test. It is intended that the creation
+// stack is observed manually not to contain
+// off-by-one errors
+func TestRaceAnnounceThreads(t *testing.T) {
+ const N = 7
+ allDone := make(chan bool, N)
+
+ var x int
+
+ var f, g, h func()
+ f = func() {
+ x = 1
+ go g()
+ go func() {
+ x = 1
+ allDone <- true
+ }()
+ x = 2
+ allDone <- true
+ }
+
+ g = func() {
+ for i := 0; i < 2; i++ {
+ go func() {
+ x = 1
+ allDone <- true
+ }()
+ allDone <- true
+ }
+ }
+
+ h = func() {
+ x = 1
+ x = 2
+ go f()
+ allDone <- true
+ }
+
+ go h()
+
+ for i := 0; i < N; i++ {
+ <-allDone
+ }
+}
+
+func TestNoRaceAfterFunc1(t *testing.T) {
+ i := 2
+ c := make(chan bool)
+ var f func()
+ f = func() {
+ i--
+ if i >= 0 {
+ time.AfterFunc(0, f)
+ } else {
+ c <- true
+ }
+ }
+
+ time.AfterFunc(0, f)
+ <-c
+}
+
+func TestNoRaceAfterFunc2(t *testing.T) {
+ var x int
+ timer := time.AfterFunc(10, func() {
+ x = 1
+ })
+ defer timer.Stop()
+ _ = x
+}
+
+func TestNoRaceAfterFunc3(t *testing.T) {
+ c := make(chan bool, 1)
+ x := 0
+ time.AfterFunc(1e7, func() {
+ x = 1
+ c <- true
+ })
+ <-c
+}
+
+func TestRaceAfterFunc3(t *testing.T) {
+ c := make(chan bool, 2)
+ x := 0
+ time.AfterFunc(1e7, func() {
+ x = 1
+ c <- true
+ })
+ time.AfterFunc(2e7, func() {
+ x = 2
+ c <- true
+ })
+ <-c
+ <-c
+}
+
+// This test's output is intended to be
+// observed manually. One should check
+// that goroutine creation stack is
+// comprehensible.
+func TestRaceGoroutineCreationStack(t *testing.T) {
+ var x int
+ var ch = make(chan bool, 1)
+
+ f1 := func() {
+ x = 1
+ ch <- true
+ }
+ f2 := func() { go f1() }
+ f3 := func() { go f2() }
+ f4 := func() { go f3() }
+
+ go f4()
+ x = 2
+ <-ch
+}
diff --git a/src/pkg/runtime/race/testdata/waitgroup_test.go b/src/pkg/runtime/race/testdata/waitgroup_test.go
new file mode 100644
index 000000000..7ea21fa7e
--- /dev/null
+++ b/src/pkg/runtime/race/testdata/waitgroup_test.go
@@ -0,0 +1,232 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+ "runtime"
+ "sync"
+ "testing"
+ "time"
+)
+
+func TestNoRaceWaitGroup(t *testing.T) {
+ var x int
+ var wg sync.WaitGroup
+ n := 1
+ for i := 0; i < n; i++ {
+ wg.Add(1)
+ j := i
+ go func() {
+ x = j
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
+func TestRaceWaitGroup(t *testing.T) {
+ var x int
+ var wg sync.WaitGroup
+ n := 2
+ for i := 0; i < n; i++ {
+ wg.Add(1)
+ j := i
+ go func() {
+ x = j
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
+func TestNoRaceWaitGroup2(t *testing.T) {
+ var x int
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ x = 1
+ wg.Done()
+ }()
+ wg.Wait()
+ x = 2
+}
+
+// incrementing counter in Add and locking wg's mutex
+func TestRaceWaitGroupAsMutex(t *testing.T) {
+ var x int
+ var wg sync.WaitGroup
+ c := make(chan bool, 2)
+ go func() {
+ wg.Wait()
+ time.Sleep(100 * time.Millisecond)
+ wg.Add(+1)
+ x = 1
+ wg.Add(-1)
+ c <- true
+ }()
+ go func() {
+ wg.Wait()
+ time.Sleep(100 * time.Millisecond)
+ wg.Add(+1)
+ x = 2
+ wg.Add(-1)
+ c <- true
+ }()
+ <-c
+ <-c
+}
+
+// Incorrect usage: Add is too late.
+func TestRaceWaitGroupWrongWait(t *testing.T) {
+ c := make(chan bool, 2)
+ var x int
+ var wg sync.WaitGroup
+ go func() {
+ wg.Add(1)
+ runtime.Gosched()
+ x = 1
+ wg.Done()
+ c <- true
+ }()
+ go func() {
+ wg.Add(1)
+ runtime.Gosched()
+ x = 2
+ wg.Done()
+ c <- true
+ }()
+ wg.Wait()
+ <-c
+ <-c
+}
+
+// A common WaitGroup misuse that can potentially be caught be the race detector.
+// For this simple case we must emulate Add() as read on &wg and Wait() as write on &wg.
+// However it will have false positives if there are several concurrent Wait() calls.
+func TestRaceFailingWaitGroupWrongAdd(t *testing.T) {
+ c := make(chan bool, 2)
+ var wg sync.WaitGroup
+ go func() {
+ wg.Add(1)
+ wg.Done()
+ c <- true
+ }()
+ go func() {
+ wg.Add(1)
+ wg.Done()
+ c <- true
+ }()
+ wg.Wait()
+ <-c
+ <-c
+}
+
+func TestNoRaceWaitGroupMultipleWait(t *testing.T) {
+ c := make(chan bool, 2)
+ var wg sync.WaitGroup
+ go func() {
+ wg.Wait()
+ c <- true
+ }()
+ go func() {
+ wg.Wait()
+ c <- true
+ }()
+ wg.Wait()
+ <-c
+ <-c
+}
+
+func TestNoRaceWaitGroupMultipleWait2(t *testing.T) {
+ c := make(chan bool, 2)
+ var wg sync.WaitGroup
+ wg.Add(2)
+ go func() {
+ wg.Done()
+ wg.Wait()
+ c <- true
+ }()
+ go func() {
+ wg.Done()
+ wg.Wait()
+ c <- true
+ }()
+ wg.Wait()
+ <-c
+ <-c
+}
+
+// Correct usage but still a race
+func TestRaceWaitGroup2(t *testing.T) {
+ var x int
+ var wg sync.WaitGroup
+ wg.Add(2)
+ go func() {
+ x = 1
+ wg.Done()
+ }()
+ go func() {
+ x = 2
+ wg.Done()
+ }()
+ wg.Wait()
+}
+
+func TestNoRaceWaitGroupPanicRecover(t *testing.T) {
+ var x int
+ var wg sync.WaitGroup
+ defer func() {
+ err := recover()
+ if err != "sync: negative WaitGroup counter" {
+ t.Fatalf("Unexpected panic: %#v", err)
+ }
+ x = 2
+ }()
+ x = 1
+ wg.Add(-1)
+}
+
+// TODO: this is actually a panic-synchronization test, not a
+// WaitGroup test. Move it to another *_test file
+// Is it possible to get a race by synchronization via panic?
+func TestNoRaceWaitGroupPanicRecover2(t *testing.T) {
+ var x int
+ var wg sync.WaitGroup
+ ch := make(chan bool, 1)
+ var f func() = func() {
+ x = 2
+ ch <- true
+ }
+ go func() {
+ defer func() {
+ err := recover()
+ if err != "sync: negative WaitGroup counter" {
+ }
+ go f()
+ }()
+ x = 1
+ wg.Add(-1)
+ }()
+
+ <-ch
+}
+
+func TestNoRaceWaitGroupTransitive(t *testing.T) {
+ x, y := 0, 0
+ var wg sync.WaitGroup
+ wg.Add(2)
+ go func() {
+ x = 42
+ wg.Done()
+ }()
+ go func() {
+ time.Sleep(1e7)
+ y = 42
+ wg.Done()
+ }()
+ wg.Wait()
+ _ = x
+ _ = y
+}
diff --git a/src/pkg/runtime/race0.c b/src/pkg/runtime/race0.c
new file mode 100644
index 000000000..1c5f05a7e
--- /dev/null
+++ b/src/pkg/runtime/race0.c
@@ -0,0 +1,133 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Stub implementation of the race detector API.
+// +build !race
+
+#include "runtime.h"
+
+uintptr
+runtime·raceinit(void)
+{
+ return 0;
+}
+
+void
+runtime·racefini(void)
+{
+}
+
+
+void
+runtime·racemapshadow(void *addr, uintptr size)
+{
+ USED(addr);
+ USED(size);
+}
+
+void
+runtime·racewritepc(void *addr, void *callpc, void *pc)
+{
+ USED(addr);
+ USED(callpc);
+ USED(pc);
+}
+
+void
+runtime·racereadpc(void *addr, void *callpc, void *pc)
+{
+ USED(addr);
+ USED(callpc);
+ USED(pc);
+}
+
+void
+runtime·racewriterangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc)
+{
+ USED(addr);
+ USED(sz);
+ USED(step);
+ USED(callpc);
+ USED(pc);
+}
+
+void
+runtime·racereadrangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc)
+{
+ USED(addr);
+ USED(sz);
+ USED(step);
+ USED(callpc);
+ USED(pc);
+}
+
+void
+runtime·raceacquire(void *addr)
+{
+ USED(addr);
+}
+
+void
+runtime·raceacquireg(G *gp, void *addr)
+{
+ USED(gp);
+ USED(addr);
+}
+
+void
+runtime·racerelease(void *addr)
+{
+ USED(addr);
+}
+
+void
+runtime·racereleaseg(G *gp, void *addr)
+{
+ USED(gp);
+ USED(addr);
+}
+
+void
+runtime·racereleasemerge(void *addr)
+{
+ USED(addr);
+}
+
+void
+runtime·racereleasemergeg(G *gp, void *addr)
+{
+ USED(gp);
+ USED(addr);
+}
+
+void
+runtime·racefingo(void)
+{
+}
+
+void
+runtime·racemalloc(void *p, uintptr sz, void *pc)
+{
+ USED(p);
+ USED(sz);
+ USED(pc);
+}
+
+void
+runtime·racefree(void *p)
+{
+ USED(p);
+}
+
+uintptr
+runtime·racegostart(void *pc)
+{
+ USED(pc);
+ return 0;
+}
+
+void
+runtime·racegoend(void)
+{
+}
diff --git a/src/pkg/runtime/race_amd64.s b/src/pkg/runtime/race_amd64.s
new file mode 100644
index 000000000..83e300905
--- /dev/null
+++ b/src/pkg/runtime/race_amd64.s
@@ -0,0 +1,14 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build race
+
+// func runtime·racefuncenter(pc uintptr)
+TEXT runtime·racefuncenter(SB), 7, $16
+ MOVQ DX, saved-8(SP) // save function entry context (for closures)
+ MOVQ pc+0(FP), DX
+ MOVQ DX, arg-16(SP)
+ CALL runtime·racefuncenter1(SB)
+ MOVQ saved-8(SP), DX
+ RET
diff --git a/src/pkg/runtime/rt0_freebsd_arm.s b/src/pkg/runtime/rt0_freebsd_arm.s
new file mode 100644
index 000000000..085fccf9d
--- /dev/null
+++ b/src/pkg/runtime/rt0_freebsd_arm.s
@@ -0,0 +1,8 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// FreeBSD and Linux use the same linkage to main
+
+TEXT _rt0_arm_freebsd(SB),7,$-4
+ B _rt0_arm(SB)
diff --git a/src/pkg/runtime/rt0_linux_arm.s b/src/pkg/runtime/rt0_linux_arm.s
index e08cf907d..a648160cf 100644
--- a/src/pkg/runtime/rt0_linux_arm.s
+++ b/src/pkg/runtime/rt0_linux_arm.s
@@ -20,15 +20,27 @@ TEXT _rt0_arm_linux(SB),7,$-4
MOVM.DB.W [R0-R3], (R13)
MOVW $4, R0 // SIGILL
MOVW R13, R1 // sa
- MOVW $0, R2 // old_sa
+ SUB $16, R13
+ MOVW R13, R2 // old_sa
MOVW $8, R3 // c
MOVW $174, R7 // sys_sigaction
BL oabi_syscall<>(SB)
- ADD $16, R13
+
// do an EABI syscall
MOVW $20, R7 // sys_getpid
- SWI $0 // this will trigger SIGILL on OABI systems
+ SWI $0 // this will trigger SIGILL on OABI systems
+
+ MOVW $4, R0 // SIGILL
+ MOVW R13, R1 // sa
+ MOVW $0, R2 // old_sa
+ MOVW $8, R3 // c
+ MOVW $174, R7 // sys_sigaction
+ SWI $0 // restore signal handler
+ ADD $32, R13
+ SUB $4, R13 // fake a stack frame for runtime·setup_auxv
+ BL runtime·setup_auxv(SB)
+ ADD $4, R13
B _rt0_arm(SB)
TEXT bad_abi<>(SB),7,$-4
diff --git a/src/pkg/runtime/rt0_netbsd_arm.s b/src/pkg/runtime/rt0_netbsd_arm.s
new file mode 100644
index 000000000..8c1588f2e
--- /dev/null
+++ b/src/pkg/runtime/rt0_netbsd_arm.s
@@ -0,0 +1,8 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// FreeBSD/NetBSD and Linux use the same linkage to main
+
+TEXT _rt0_arm_netbsd(SB),7,$-4
+ B _rt0_arm(SB)
diff --git a/src/pkg/runtime/rt0_plan9_386.s b/src/pkg/runtime/rt0_plan9_386.s
index b56c8b325..56f3a0f6c 100644
--- a/src/pkg/runtime/rt0_plan9_386.s
+++ b/src/pkg/runtime/rt0_plan9_386.s
@@ -25,6 +25,7 @@ argv_fix:
ADDL $4, BP
LOOP argv_fix
+ CALL runtime·asminit(SB)
JMP _rt0_386(SB)
DATA runtime·isplan9(SB)/4, $1
diff --git a/src/pkg/runtime/rt0_plan9_amd64.s b/src/pkg/runtime/rt0_plan9_amd64.s
new file mode 100644
index 000000000..2b1fa2ae1
--- /dev/null
+++ b/src/pkg/runtime/rt0_plan9_amd64.s
@@ -0,0 +1,11 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+TEXT _rt0_amd64_plan9(SB),7, $0
+ MOVQ $_rt0_amd64(SB), AX
+ MOVQ SP, DI
+ JMP AX
+
+DATA runtime·isplan9(SB)/4, $1
+GLOBL runtime·isplan9(SB), $4
diff --git a/src/pkg/runtime/rt0_windows_386.s b/src/pkg/runtime/rt0_windows_386.s
index 3b023de2f..a06aa787e 100644
--- a/src/pkg/runtime/rt0_windows_386.s
+++ b/src/pkg/runtime/rt0_windows_386.s
@@ -3,11 +3,6 @@
// license that can be found in the LICENSE file.
TEXT _rt0_386_windows(SB),7,$0
- // Set up SEH frame for bootstrap m
- PUSHL $runtime·sigtramp(SB)
- PUSHL 0(FS)
- MOVL SP, 0(FS)
-
JMP _rt0_386(SB)
DATA runtime·iswindows(SB)/4, $1
diff --git a/src/pkg/runtime/rune.c b/src/pkg/runtime/rune.c
index 86ee76ddd..ed867269d 100644
--- a/src/pkg/runtime/rune.c
+++ b/src/pkg/runtime/rune.c
@@ -47,6 +47,9 @@ enum
Runeerror = 0xFFFD,
Runeself = 0x80,
+ SurrogateMin = 0xD800,
+ SurrogateMax = 0xDFFF,
+
Bad = Runeerror,
Runemax = 0x10FFFF, /* maximum rune value */
@@ -128,6 +131,8 @@ runtime·charntorune(int32 *rune, uint8 *str, int32 length)
l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
if(l <= Rune2)
goto bad;
+ if (SurrogateMin <= l && l <= SurrogateMax)
+ goto bad;
*rune = l;
return 3;
}
@@ -193,13 +198,15 @@ runtime·runetochar(byte *str, int32 rune) /* note: in original, arg2 was point
}
/*
- * If the Rune is out of range, convert it to the error rune.
+ * If the Rune is out of range or a surrogate half, convert it to the error rune.
* Do this test here because the error rune encodes to three bytes.
* Doing it earlier would duplicate work, since an out of range
* Rune wouldn't have fit in one or two bytes.
*/
if (c > Runemax)
c = Runeerror;
+ if (SurrogateMin <= c && c <= SurrogateMax)
+ c = Runeerror;
/*
* three character sequence
diff --git a/src/pkg/runtime/runtime-gdb.py b/src/pkg/runtime/runtime-gdb.py
index 629c39e98..eff9a4003 100644
--- a/src/pkg/runtime/runtime-gdb.py
+++ b/src/pkg/runtime/runtime-gdb.py
@@ -149,8 +149,8 @@ goobjfile.pretty_printers.extend([makematcher(k) for k in vars().values() if has
#
# For reference, this is what we're trying to do:
-# eface: p *(*(struct 'runtime.commonType'*)'main.e'->type_->data)->string
-# iface: p *(*(struct 'runtime.commonType'*)'main.s'->tab->Type->data)->string
+# eface: p *(*(struct 'runtime.rtype'*)'main.e'->type_->data)->string
+# iface: p *(*(struct 'runtime.rtype'*)'main.s'->tab->Type->data)->string
#
# interface types can't be recognized by their name, instead we check
# if they have the expected fields. Unfortunately the mapping of
@@ -186,8 +186,7 @@ def lookup_type(name):
except:
pass
-_rctp_type = gdb.lookup_type("struct runtime.commonType").pointer()
-_rtp_type = gdb.lookup_type("struct runtime._type").pointer()
+_rctp_type = gdb.lookup_type("struct runtime.rtype").pointer()
def iface_commontype(obj):
if is_iface(obj):
@@ -196,18 +195,13 @@ def iface_commontype(obj):
go_type_ptr = obj['_type']
else:
return
-
- # sanity check: reflection type description ends in a loop.
- tt = go_type_ptr['_type'].cast(_rtp_type).dereference()['_type']
- if tt != tt.cast(_rtp_type).dereference()['_type']:
- return
- return go_type_ptr['ptr'].cast(_rctp_type).dereference()
+ return go_type_ptr.cast(_rctp_type).dereference()
def iface_dtype(obj):
"Decode type of the data field of an eface or iface struct."
- # known issue: dtype_name decoded from runtime.commonType is "nested.Foo"
+ # known issue: dtype_name decoded from runtime.rtype is "nested.Foo"
# but the dwarf table lists it as "full/path/to/nested.Foo"
dynamic_go_type = iface_commontype(obj)
@@ -381,6 +375,7 @@ class GoroutineCmd(gdb.Command):
def invoke(self, arg, from_tty):
goid, cmd = arg.split(None, 1)
+ goid = gdb.parse_and_eval(goid)
pc, sp = find_goroutine(int(goid))
if not pc:
print "No such goroutine: ", goid
diff --git a/src/pkg/runtime/runtime.c b/src/pkg/runtime/runtime.c
index ebb5544fb..4d57cbafd 100644
--- a/src/pkg/runtime/runtime.c
+++ b/src/pkg/runtime/runtime.c
@@ -3,15 +3,12 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
-#include "stack.h"
+#include "arch_GOARCH.h"
enum {
maxround = sizeof(uintptr),
};
-uint32 runtime·panicking;
-void (*runtime·destroylock)(Lock*);
-
/*
* We assume that all architectures turn faults and the like
* into apparent calls to runtime.sigpanic. If we see a "call"
@@ -31,103 +28,6 @@ runtime·gotraceback(void)
return runtime·atoi(p);
}
-static Lock paniclk;
-
-void
-runtime·startpanic(void)
-{
- if(m->dying) {
- runtime·printf("panic during panic\n");
- runtime·exit(3);
- }
- m->dying = 1;
- runtime·xadd(&runtime·panicking, 1);
- runtime·lock(&paniclk);
-}
-
-void
-runtime·dopanic(int32 unused)
-{
- static bool didothers;
-
- if(g->sig != 0)
- runtime·printf("[signal %x code=%p addr=%p pc=%p]\n",
- g->sig, g->sigcode0, g->sigcode1, g->sigpc);
-
- if(runtime·gotraceback()){
- if(g != m->g0) {
- runtime·printf("\n");
- runtime·goroutineheader(g);
- runtime·traceback(runtime·getcallerpc(&unused), runtime·getcallersp(&unused), 0, g);
- }
- if(!didothers) {
- didothers = true;
- runtime·tracebackothers(g);
- }
- }
- runtime·unlock(&paniclk);
- if(runtime·xadd(&runtime·panicking, -1) != 0) {
- // Some other m is panicking too.
- // Let it print what it needs to print.
- // Wait forever without chewing up cpu.
- // It will exit when it's done.
- static Lock deadlock;
- runtime·lock(&deadlock);
- runtime·lock(&deadlock);
- }
-
- runtime·exit(2);
-}
-
-void
-runtime·panicindex(void)
-{
- runtime·panicstring("index out of range");
-}
-
-void
-runtime·panicslice(void)
-{
- runtime·panicstring("slice bounds out of range");
-}
-
-void
-runtime·throwreturn(void)
-{
- // can only happen if compiler is broken
- runtime·throw("no return at end of a typed function - compiler is broken");
-}
-
-void
-runtime·throwinit(void)
-{
- // can only happen with linker skew
- runtime·throw("recursive call during initialization - linker skew");
-}
-
-void
-runtime·throw(int8 *s)
-{
- runtime·startpanic();
- runtime·printf("throw: %s\n", s);
- runtime·dopanic(0);
- *(int32*)0 = 0; // not reached
- runtime·exit(1); // even more not reached
-}
-
-void
-runtime·panicstring(int8 *s)
-{
- Eface err;
-
- if(m->gcing) {
- runtime·printf("panic: %s\n", s);
- runtime·throw("panic during gc");
- }
- runtime·newErrorString(runtime·gostringnocopy((byte*)s), &err);
- runtime·panic(err);
-}
-
int32
runtime·mcmp(byte *s1, byte *s2, uint32 n)
{
@@ -155,30 +55,21 @@ runtime·mchr(byte *p, byte c, byte *ep)
return nil;
}
-uint32
-runtime·rnd(uint32 n, uint32 m)
-{
- uint32 r;
-
- if(m > maxround)
- m = maxround;
- r = n % m;
- if(r)
- n += m-r;
- return n;
-}
-
static int32 argc;
static uint8** argv;
Slice os·Args;
Slice syscall·envs;
+void (*runtime·sysargs)(int32, uint8**);
+
void
runtime·args(int32 c, uint8 **v)
{
argc = c;
argv = v;
+ if(runtime·sysargs != nil)
+ runtime·sysargs(c, v);
}
int32 runtime·isplan9;
@@ -219,33 +110,6 @@ runtime·goenvs_unix(void)
syscall·envs.cap = n;
}
-byte*
-runtime·getenv(int8 *s)
-{
- int32 i, j, len;
- byte *v, *bs;
- String* envv;
- int32 envc;
-
- bs = (byte*)s;
- len = runtime·findnull(bs);
- envv = (String*)syscall·envs.array;
- envc = syscall·envs.len;
- for(i=0; i<envc; i++){
- if(envv[i].len <= len)
- continue;
- v = envv[i].str;
- for(j=0; j<len; j++)
- if(bs[j] != v[j])
- goto nomatch;
- if(v[len] != '=')
- goto nomatch;
- return v+len+1;
- nomatch:;
- }
- return nil;
-}
-
void
runtime·getgoroot(String out)
{
@@ -267,6 +131,33 @@ runtime·atoi(byte *p)
return n;
}
+static void
+TestAtomic64(void)
+{
+ uint64 z64, x64;
+
+ z64 = 42;
+ x64 = 0;
+ PREFETCH(&z64);
+ if(runtime·cas64(&z64, &x64, 1))
+ runtime·throw("cas64 failed");
+ if(x64 != 42)
+ runtime·throw("cas64 failed");
+ if(!runtime·cas64(&z64, &x64, 1))
+ runtime·throw("cas64 failed");
+ if(x64 != 42 || z64 != 1)
+ runtime·throw("cas64 failed");
+ if(runtime·atomicload64(&z64) != 1)
+ runtime·throw("load64 failed");
+ runtime·atomicstore64(&z64, (1ull<<40)+1);
+ if(runtime·atomicload64(&z64) != (1ull<<40)+1)
+ runtime·throw("store64 failed");
+ if(runtime·xadd64(&z64, (1ull<<40)+1) != (2ull<<40)+2)
+ runtime·throw("xadd64 failed");
+ if(runtime·atomicload64(&z64) != (2ull<<40)+2)
+ runtime·throw("xadd64 failed");
+}
+
void
runtime·check(void)
{
@@ -342,10 +233,12 @@ runtime·check(void)
runtime·throw("float32nan2");
if(!(i != i1))
runtime·throw("float32nan3");
+
+ TestAtomic64();
}
void
-runtime·Caller(int32 skip, uintptr retpc, String retfile, int32 retline, bool retbool)
+runtime·Caller(intgo skip, uintptr retpc, String retfile, intgo retline, bool retbool)
{
Func *f, *g;
uintptr pc;
@@ -382,7 +275,7 @@ runtime·Caller(int32 skip, uintptr retpc, String retfile, int32 retline, bool r
}
void
-runtime·Callers(int32 skip, Slice pc, int32 retn)
+runtime·Callers(intgo skip, Slice pc, intgo retn)
{
// runtime.callers uses pc.array==nil as a signal
// to print a stack trace. Pick off 0-length pc here
@@ -413,3 +306,40 @@ runtime·fastrand1(void)
m->fastrand = x;
return x;
}
+
+static Lock ticksLock;
+static int64 ticks;
+
+int64
+runtime·tickspersecond(void)
+{
+ int64 res, t0, t1, c0, c1;
+
+ res = (int64)runtime·atomicload64((uint64*)&ticks);
+ if(res != 0)
+ return ticks;
+ runtime·lock(&ticksLock);
+ res = ticks;
+ if(res == 0) {
+ t0 = runtime·nanotime();
+ c0 = runtime·cputicks();
+ runtime·usleep(100*1000);
+ t1 = runtime·nanotime();
+ c1 = runtime·cputicks();
+ if(t1 == t0)
+ t1++;
+ res = (c1-c0)*1000*1000*1000/(t1-t0);
+ if(res == 0)
+ res++;
+ runtime·atomicstore64((uint64*)&ticks, res);
+ }
+ runtime·unlock(&ticksLock);
+ return res;
+}
+
+void
+runtime∕pprof·runtime_cyclesPerSecond(int64 res)
+{
+ res = runtime·tickspersecond();
+ FLUSH(&res);
+}
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 6f5aea11d..08f43a69b 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -19,9 +19,13 @@ typedef double float64;
#ifdef _64BIT
typedef uint64 uintptr;
typedef int64 intptr;
+typedef int64 intgo; // Go's int
+typedef uint64 uintgo; // Go's uint
#else
typedef uint32 uintptr;
-typedef int32 intptr;
+typedef int32 intptr;
+typedef int32 intgo; // Go's int
+typedef uint32 uintgo; // Go's uint
#endif
/*
@@ -48,44 +52,53 @@ typedef struct G G;
typedef struct Gobuf Gobuf;
typedef union Lock Lock;
typedef struct M M;
+typedef struct P P;
typedef struct Mem Mem;
typedef union Note Note;
typedef struct Slice Slice;
typedef struct Stktop Stktop;
typedef struct String String;
+typedef struct FuncVal FuncVal;
typedef struct SigTab SigTab;
typedef struct MCache MCache;
typedef struct FixAlloc FixAlloc;
typedef struct Iface Iface;
typedef struct Itab Itab;
+typedef struct InterfaceType InterfaceType;
typedef struct Eface Eface;
typedef struct Type Type;
typedef struct ChanType ChanType;
typedef struct MapType MapType;
typedef struct Defer Defer;
+typedef struct DeferChunk DeferChunk;
typedef struct Panic Panic;
typedef struct Hmap Hmap;
typedef struct Hchan Hchan;
typedef struct Complex64 Complex64;
typedef struct Complex128 Complex128;
typedef struct WinCall WinCall;
+typedef struct SEH SEH;
typedef struct Timers Timers;
typedef struct Timer Timer;
+typedef struct GCStats GCStats;
+typedef struct LFNode LFNode;
+typedef struct ParFor ParFor;
+typedef struct ParForThread ParForThread;
+typedef struct CgoMal CgoMal;
/*
- * per-cpu declaration.
- * "extern register" is a special storage class implemented by 6c, 8c, etc.
- * on machines with lots of registers, it allocates a register that will not be
- * used in generated code. on the x86, it allocates a slot indexed by a
- * segment register.
+ * Per-CPU declaration.
*
- * amd64: allocated downwards from R15
- * x86: allocated upwards from 0(GS)
- * arm: allocated downwards from R10
+ * "extern register" is a special storage class implemented by 6c, 8c, etc.
+ * On the ARM, it is an actual register; elsewhere it is a slot in thread-
+ * local storage indexed by a segment register. See zasmhdr in
+ * src/cmd/dist/buildruntime.c for details, and be aware that the linker may
+ * make further OS-specific changes to the compiler's output. For example,
+ * 6l/linux rewrites 0(GS) as -16(FS).
*
- * every C file linked into a Go program must include runtime.h
- * so that the C compiler knows to avoid other uses of these registers.
- * the Go compilers know to avoid them.
+ * Every C file linked into a Go program must include runtime.h so that the
+ * C compiler (6c, 8c, etc.) knows to avoid other uses of these dedicated
+ * registers. The Go compiler (6g, 8g, etc.) knows to avoid them.
*/
extern register G* g;
extern register M* m;
@@ -105,14 +118,34 @@ enum
Grunning,
Gsyscall,
Gwaiting,
- Gmoribund,
+ Gmoribund_unused, // currently unused, but hardcoded in gdb scripts
Gdead,
};
enum
{
+ // P status
+ Pidle,
+ Prunning,
+ Psyscall,
+ Pgcstop,
+ Pdead,
+};
+enum
+{
true = 1,
false = 0,
};
+enum
+{
+ PtrSize = sizeof(void*),
+};
+enum
+{
+ // Per-M stack segment cache size.
+ StackCacheSize = 32,
+ // Global <-> per-M stack segment cache transfer batch size.
+ StackCacheBatch = 16,
+};
/*
* structures
@@ -130,7 +163,12 @@ union Note
struct String
{
byte* str;
- int32 len;
+ intgo len;
+};
+struct FuncVal
+{
+ void (*fn)(void);
+ // variable-size, fn-specific data here
};
struct Iface
{
@@ -156,47 +194,61 @@ struct Complex128
struct Slice
{ // must not move anything
byte* array; // actual data
- uint32 len; // number of elements
- uint32 cap; // allocated number of elements
+ uintgo len; // number of elements
+ uintgo cap; // allocated number of elements
};
struct Gobuf
{
// The offsets of these fields are known to (hard-coded in) libmach.
- byte* sp;
+ uintptr sp;
byte* pc;
G* g;
};
+struct GCStats
+{
+ // the struct must consist of only uint64's,
+ // because it is casted to uint64[].
+ uint64 nhandoff;
+ uint64 nhandoffcnt;
+ uint64 nprocyield;
+ uint64 nosyield;
+ uint64 nsleep;
+};
struct G
{
- byte* stackguard; // cannot move - also known to linker, libmach, runtime/cgo
- byte* stackbase; // cannot move - also known to libmach, runtime/cgo
+ uintptr stackguard; // cannot move - also known to linker, libmach, runtime/cgo
+ uintptr stackbase; // cannot move - also known to libmach, runtime/cgo
Defer* defer;
Panic* panic;
Gobuf sched;
- byte* gcstack; // if status==Gsyscall, gcstack = stackbase to use during gc
- byte* gcsp; // if status==Gsyscall, gcsp = sched.sp to use during gc
- byte* gcguard; // if status==Gsyscall, gcguard = stackguard to use during gc
- byte* stack0;
- byte* entry; // initial function
+ uintptr gcstack; // if status==Gsyscall, gcstack = stackbase to use during gc
+ uintptr gcsp; // if status==Gsyscall, gcsp = sched.sp to use during gc
+ byte* gcpc; // if status==Gsyscall, gcpc = sched.pc to use during gc
+ uintptr gcguard; // if status==Gsyscall, gcguard = stackguard to use during gc
+ uintptr stack0;
+ FuncVal* fnstart; // initial function
G* alllink; // on allg
void* param; // passed parameter on wakeup
int16 status;
- int32 goid;
+ int64 goid;
uint32 selgen; // valid sudog pointer
int8* waitreason; // if status==Gwaiting
G* schedlink;
- bool readyonstop;
bool ispanic;
+ bool issystem;
+ int8 raceignore; // ignore race detection events
M* m; // for debuggers, but offset not hard-coded
M* lockedm;
- M* idlem;
int32 sig;
int32 writenbuf;
byte* writebuf;
+ DeferChunk *dchunk;
+ DeferChunk *dchunknext;
uintptr sigcode0;
uintptr sigcode1;
uintptr sigpc;
uintptr gopc; // pc of go statement that created this goroutine
+ uintptr racectx;
uintptr end[];
};
struct M
@@ -213,43 +265,98 @@ struct M
uintptr cret; // return value from C
uint64 procid; // for debuggers, but offset not hard-coded
G* gsignal; // signal-handling G
- uint32 tls[8]; // thread-local storage (for 386 extern register)
+ uintptr tls[4]; // thread-local storage (for x86 extern register)
+ void (*mstartfn)(void);
G* curg; // current running goroutine
+ P* p; // attached P for executing Go code (nil if not executing Go code)
+ P* nextp;
int32 id;
int32 mallocing;
+ int32 throwing;
int32 gcing;
int32 locks;
int32 nomemprof;
- int32 waitnextg;
int32 dying;
int32 profilehz;
int32 helpgc;
+ bool blockingsyscall;
+ bool spinning;
uint32 fastrand;
- uint64 ncgocall;
- Note havenextg;
- G* nextg;
+ uint64 ncgocall; // number of cgo calls in total
+ int32 ncgo; // number of cgo calls currently in progress
+ CgoMal* cgomal;
+ Note park;
M* alllink; // on allm
M* schedlink;
uint32 machport; // Return address for Mach IPC (OS X)
MCache *mcache;
- FixAlloc *stackalloc;
+ int32 stackinuse;
+ uint32 stackcachepos;
+ uint32 stackcachecnt;
+ void* stackcache[StackCacheSize];
G* lockedg;
- G* idleg;
uintptr createstack[32]; // Stack that created this thread.
uint32 freglo[16]; // D[i] lsb and F[i]
uint32 freghi[16]; // D[i] msb and F[i+16]
uint32 fflag; // floating point compare flags
+ uint32 locked; // tracking for LockOSThread
M* nextwaitm; // next M waiting for lock
uintptr waitsema; // semaphore for parking on locks
uint32 waitsemacount;
uint32 waitsemalock;
+ GCStats gcstats;
+ bool racecall;
+ bool needextram;
+ void* racepc;
+ void (*waitunlockf)(Lock*);
+ Lock* waitlock;
+ uint32 moreframesize_minalloc;
+
+ uintptr settype_buf[1024];
+ uintptr settype_bufsize;
#ifdef GOOS_windows
void* thread; // thread handle
#endif
+#ifdef GOOS_plan9
+ int8* notesig;
+#endif
+ SEH* seh;
uintptr end[];
};
+struct P
+{
+ Lock;
+
+ uint32 status; // one of Pidle/Prunning/...
+ P* link;
+ uint32 tick; // incremented on every scheduler or system call
+ M* m; // back-link to associated M (nil if idle)
+ MCache* mcache;
+
+ // Queue of runnable goroutines.
+ G** runq;
+ int32 runqhead;
+ int32 runqtail;
+ int32 runqsize;
+
+ // Available G's (status == Gdead)
+ G* gfree;
+ int32 gfreecnt;
+
+ byte pad[64];
+};
+
+// The m->locked word holds a single bit saying whether
+// external calls to LockOSThread are in effect, and then a counter
+// of the internal nesting depth of lockOSThread / unlockOSThread.
+enum
+{
+ LockExternal = 1,
+ LockInternal = 2,
+};
+
struct Stktop
{
// The offsets of these fields are known to (hard-coded in) libmach.
@@ -288,8 +395,19 @@ struct Func
uintptr pc0; // starting pc, ln for table
int32 ln0;
int32 frame; // stack frame size
- int32 args; // number of 32-bit in/out args
- int32 locals; // number of 32-bit locals
+ int32 args; // in/out args size
+ int32 locals; // locals size
+};
+
+// layout of Itab known to compilers
+struct Itab
+{
+ InterfaceType* inter;
+ Type* type;
+ Itab* link;
+ int32 bad;
+ int32 unused;
+ void (*fun[])(void);
};
struct WinCall
@@ -301,6 +419,11 @@ struct WinCall
uintptr r2;
uintptr err; // error number
};
+struct SEH
+{
+ void* prev;
+ void* handler;
+};
#ifdef GOOS_windows
enum {
@@ -335,10 +458,46 @@ struct Timer
// a well-behaved function and not block.
int64 when;
int64 period;
- void (*f)(int64, Eface);
+ FuncVal *fv;
Eface arg;
};
+// Lock-free stack node.
+struct LFNode
+{
+ LFNode *next;
+ uintptr pushcnt;
+};
+
+// Parallel for descriptor.
+struct ParFor
+{
+ void (*body)(ParFor*, uint32); // executed for each element
+ uint32 done; // number of idle threads
+ uint32 nthr; // total number of threads
+ uint32 nthrmax; // maximum number of threads
+ uint32 thrseq; // thread id sequencer
+ uint32 cnt; // iteration space [0, cnt)
+ void *ctx; // arbitrary user context
+ bool wait; // if true, wait while all threads finish processing,
+ // otherwise parfor may return while other threads are still working
+ ParForThread *thr; // array of thread descriptors
+ // stats
+ uint64 nsteal;
+ uint64 nstealcnt;
+ uint64 nprocyield;
+ uint64 nosyield;
+ uint64 nsleep;
+};
+
+// Track memory allocated by code not written in Go during a cgo call,
+// so that the garbage collector can see them.
+struct CgoMal
+{
+ CgoMal *next;
+ byte *alloc;
+};
+
/*
* defined macros
* you need super-gopher-guru privilege
@@ -347,6 +506,7 @@ struct Timer
#define nelem(x) (sizeof(x)/sizeof((x)[0]))
#define nil ((void*)0)
#define offsetof(s,m) (uint32)(&(((s*)0)->m))
+#define ROUND(x, n) (((x)+(n)-1)&~((n)-1)) /* all-caps to mark as macro: it evaluates n twice */
/*
* known to compiler
@@ -430,12 +590,19 @@ void runtime·nilintercopy(uintptr, void*, void*);
struct Defer
{
int32 siz;
- bool nofree;
+ bool special; // not part of defer frame
+ bool free; // if special, free when done
byte* argp; // where args were copied from
byte* pc;
- byte* fn;
+ FuncVal* fn;
Defer* link;
- byte args[8]; // padded to actual size
+ void* args[1]; // padded to actual size
+};
+
+struct DeferChunk
+{
+ DeferChunk *prev;
+ uintptr off;
};
/*
@@ -453,16 +620,21 @@ struct Panic
* external data
*/
extern String runtime·emptystring;
-G* runtime·allg;
-G* runtime·lastg;
-M* runtime·allm;
+extern uintptr runtime·zerobase;
+extern G* runtime·allg;
+extern G* runtime·lastg;
+extern M* runtime·allm;
+extern P** runtime·allp;
extern int32 runtime·gomaxprocs;
extern bool runtime·singleproc;
extern uint32 runtime·panicking;
-extern int32 runtime·gcwaiting; // gc is waiting to run
-int8* runtime·goos;
-int32 runtime·ncpu;
+extern uint32 runtime·gcwaiting; // gc is waiting to run
+extern int8* runtime·goos;
+extern int32 runtime·ncpu;
extern bool runtime·iscgo;
+extern void (*runtime·sysargs)(int32, uint8**);
+extern uint32 runtime·maxstring;
+extern uint32 runtime·Hchansize;
/*
* common functions and data
@@ -481,7 +653,8 @@ int32 runtime·charntorune(int32*, uint8*, int32);
#define FLUSH(x) USED(x)
void runtime·gogo(Gobuf*, uintptr);
-void runtime·gogocall(Gobuf*, void(*)(void));
+void runtime·gogocall(Gobuf*, void(*)(void), uintptr);
+void runtime·gogocallfn(Gobuf*, FuncVal*);
void runtime·gosave(Gobuf*);
void runtime·lessstack(void);
void runtime·goargs(void);
@@ -490,7 +663,6 @@ void runtime·goenvs_unix(void);
void* runtime·getu(void);
void runtime·throw(int8*);
void runtime·panicstring(int8*);
-uint32 runtime·rnd(uint32, uint32);
void runtime·prints(int8*);
void runtime·printf(int8*, ...);
byte* runtime·mchr(byte*, byte, byte*);
@@ -499,8 +671,8 @@ void runtime·memmove(void*, void*, uint32);
void* runtime·mal(uintptr);
String runtime·catstring(String, String);
String runtime·gostring(byte*);
-String runtime·gostringn(byte*, int32);
-Slice runtime·gobytes(byte*, int32);
+String runtime·gostringn(byte*, intgo);
+Slice runtime·gobytes(byte*, intgo);
String runtime·gostringnocopy(byte*);
String runtime·gostringw(uint16*);
void runtime·initsig(void);
@@ -512,38 +684,47 @@ void runtime·tracebackothers(G*);
int32 runtime·write(int32, void*, int32);
int32 runtime·mincore(void*, uintptr, byte*);
bool runtime·cas(uint32*, uint32, uint32);
+bool runtime·cas64(uint64*, uint64*, uint64);
bool runtime·casp(void**, void*, void*);
// Don't confuse with XADD x86 instruction,
// this one is actually 'addx', that is, add-and-fetch.
uint32 runtime·xadd(uint32 volatile*, int32);
+uint64 runtime·xadd64(uint64 volatile*, int64);
uint32 runtime·xchg(uint32 volatile*, uint32);
uint32 runtime·atomicload(uint32 volatile*);
void runtime·atomicstore(uint32 volatile*, uint32);
+void runtime·atomicstore64(uint64 volatile*, uint64);
+uint64 runtime·atomicload64(uint64 volatile*);
void* runtime·atomicloadp(void* volatile*);
void runtime·atomicstorep(void* volatile*, void*);
-void runtime·jmpdefer(byte*, void*);
+void runtime·jmpdefer(FuncVal*, void*);
void runtime·exit1(int32);
void runtime·ready(G*);
byte* runtime·getenv(int8*);
int32 runtime·atoi(byte*);
-void runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void));
-void runtime·signalstack(byte*, int32);
+void runtime·newosproc(M *mp, void *stk);
+void runtime·mstart(void);
G* runtime·malg(int32);
void runtime·asminit(void);
+void runtime·mpreinit(M*);
void runtime·minit(void);
+void runtime·unminit(void);
+void runtime·signalstack(byte*, int32);
Func* runtime·findfunc(uintptr);
int32 runtime·funcline(Func*, uintptr);
void* runtime·stackalloc(uint32);
void runtime·stackfree(void*, uintptr);
MCache* runtime·allocmcache(void);
+void runtime·freemcache(MCache*);
void runtime·mallocinit(void);
+void runtime·mprofinit(void);
bool runtime·ifaceeq_c(Iface, Iface);
bool runtime·efaceeq_c(Eface, Eface);
-uintptr runtime·ifacehash(Iface);
-uintptr runtime·efacehash(Eface);
+uintptr runtime·ifacehash(Iface, uintptr);
+uintptr runtime·efacehash(Eface, uintptr);
void* runtime·malloc(uintptr size);
void runtime·free(void *v);
-bool runtime·addfinalizer(void*, void(*fn)(void*), int32);
+bool runtime·addfinalizer(void*, FuncVal *fn, uintptr);
void runtime·runpanic(Panic*);
void* runtime·getcallersp(void*);
int32 runtime·mcount(void);
@@ -551,27 +732,35 @@ int32 runtime·gcount(void);
void runtime·mcall(void(*)(G*));
uint32 runtime·fastrand1(void);
+void runtime·setmg(M*, G*);
+void runtime·newextram(void);
void runtime·exit(int32);
void runtime·breakpoint(void);
void runtime·gosched(void);
-void runtime·tsleep(int64);
+void runtime·park(void(*)(Lock*), Lock*, int8*);
+void runtime·tsleep(int64, int8*);
M* runtime·newm(void);
void runtime·goexit(void);
void runtime·asmcgocall(void (*fn)(void*), void*);
void runtime·entersyscall(void);
+void runtime·entersyscallblock(void);
void runtime·exitsyscall(void);
-G* runtime·newproc1(byte*, byte*, int32, int32, void*);
+G* runtime·newproc1(FuncVal*, byte*, int32, int32, void*);
bool runtime·sigsend(int32 sig);
int32 runtime·callers(int32, uintptr*, int32);
int32 runtime·gentraceback(byte*, byte*, byte*, G*, int32, uintptr*, int32);
int64 runtime·nanotime(void);
void runtime·dopanic(int32);
void runtime·startpanic(void);
+void runtime·unwindstack(G*, byte*);
void runtime·sigprof(uint8 *pc, uint8 *sp, uint8 *lr, G *gp);
void runtime·resetcpuprofiler(int32);
void runtime·setcpuprofilerate(void(*)(uintptr*, int32), int32);
void runtime·usleep(uint32);
int64 runtime·cputicks(void);
+int64 runtime·tickspersecond(void);
+void runtime·blockevent(int64, int32);
+extern int64 runtime·blockprofilerate;
#pragma varargck argpos runtime·printf 1
#pragma varargck type "d" int32
@@ -589,7 +778,7 @@ int64 runtime·cputicks(void);
#pragma varargck type "S" String
void runtime·stoptheworld(void);
-void runtime·starttheworld(bool);
+void runtime·starttheworld(void);
extern uint32 runtime·worldsema;
/*
@@ -636,6 +825,27 @@ void runtime·futexsleep(uint32*, uint32, int64);
void runtime·futexwakeup(uint32*, uint32);
/*
+ * Lock-free stack.
+ * Initialize uint64 head to 0, compare with 0 to test for emptiness.
+ * The stack does not keep pointers to nodes,
+ * so they can be garbage collected if there are no other pointers to nodes.
+ */
+void runtime·lfstackpush(uint64 *head, LFNode *node);
+LFNode* runtime·lfstackpop(uint64 *head);
+
+/*
+ * Parallel for over [0, n).
+ * body() is executed for each iteration.
+ * nthr - total number of worker threads.
+ * ctx - arbitrary user context.
+ * if wait=true, threads return from parfor() when all work is done;
+ * otherwise, threads can return while other threads are still finishing processing.
+ */
+ParFor* runtime·parforalloc(uint32 nthrmax);
+void runtime·parforsetup(ParFor *desc, uint32 nthr, uint32 n, void *ctx, bool wait, void (*body)(ParFor*, uint32));
+void runtime·parfordo(ParFor *desc);
+
+/*
* This is consistent across Linux and BSD.
* If a new OS is added that is different, move this to
* $GOOS/$GOARCH/defs.h.
@@ -645,6 +855,9 @@ void runtime·futexwakeup(uint32*, uint32);
/*
* low level C-called
*/
+// for mmap, we only pass the lower 32 bits of file offset to the
+// assembly routine; the higher bits (if required), should be provided
+// by the assembly routine as 0.
uint8* runtime·mmap(byte*, uintptr, int32, int32, int32, uint32);
void runtime·munmap(byte*, uintptr);
void runtime·madvise(byte*, uintptr, int32);
@@ -656,6 +869,7 @@ void* runtime·getcallerpc(void*);
* runtime go-called
*/
void runtime·printbool(bool);
+void runtime·printbyte(int8);
void runtime·printfloat(float64);
void runtime·printint(int64);
void runtime·printiface(Iface);
@@ -667,7 +881,7 @@ void runtime·printuint(uint64);
void runtime·printhex(uint64);
void runtime·printslice(Slice);
void runtime·printcomplex(Complex128);
-void reflect·call(byte*, byte*, uint32);
+void reflect·call(FuncVal*, byte*, uint32);
void runtime·panic(Eface);
void runtime·panicindex(void);
void runtime·panicslice(void);
@@ -708,8 +922,8 @@ void runtime·semrelease(uint32*);
int32 runtime·gomaxprocsfunc(int32 n);
void runtime·procyield(uint32);
void runtime·osyield(void);
-void runtime·LockOSThread(void);
-void runtime·UnlockOSThread(void);
+void runtime·lockOSThread(void);
+void runtime·unlockOSThread(void);
void runtime·mapassign(MapType*, Hmap*, byte*, byte*);
void runtime·mapaccess(MapType*, Hmap*, byte*, byte*, bool*);
@@ -719,13 +933,11 @@ void runtime·mapiterkeyvalue(struct hash_iter*, void*, void*);
Hmap* runtime·makemap_c(MapType*, int64);
Hchan* runtime·makechan_c(ChanType*, int64);
-void runtime·chansend(ChanType*, Hchan*, byte*, bool*);
+void runtime·chansend(ChanType*, Hchan*, byte*, bool*, void*);
void runtime·chanrecv(ChanType*, Hchan*, byte*, bool*, bool*);
-int32 runtime·chanlen(Hchan*);
-int32 runtime·chancap(Hchan*);
-bool runtime·showframe(Func*);
+bool runtime·showframe(Func*, bool);
-void runtime·ifaceE2I(struct InterfaceType*, Eface, Iface*);
+void runtime·ifaceE2I(InterfaceType*, Eface, Iface*);
uintptr runtime·memlimit(void);
@@ -738,3 +950,17 @@ uintptr runtime·memlimit(void);
// is forced to deliver the signal to a thread that's actually running.
// This is a no-op on other systems.
void runtime·setprof(bool);
+
+// float.c
+extern float64 runtime·nan;
+extern float64 runtime·posinf;
+extern float64 runtime·neginf;
+extern uint64 ·nan;
+extern uint64 ·posinf;
+extern uint64 ·neginf;
+#define ISNAN(f) ((f) != (f))
+
+enum
+{
+ UseSpanType = 1,
+};
diff --git a/src/pkg/runtime/runtime1.goc b/src/pkg/runtime/runtime1.goc
index 667131c1e..d2c38dfef 100644
--- a/src/pkg/runtime/runtime1.goc
+++ b/src/pkg/runtime/runtime1.goc
@@ -5,10 +5,10 @@
package runtime
#include "runtime.h"
-func GOMAXPROCS(n int32) (ret int32) {
+func GOMAXPROCS(n int) (ret int) {
ret = runtime·gomaxprocsfunc(n);
}
-func NumCPU() (ret int32) {
+func NumCPU() (ret int) {
ret = runtime·ncpu;
}
diff --git a/src/pkg/runtime/runtime_test.go b/src/pkg/runtime/runtime_test.go
index d68b363e9..e45879349 100644
--- a/src/pkg/runtime/runtime_test.go
+++ b/src/pkg/runtime/runtime_test.go
@@ -38,3 +38,44 @@ func BenchmarkIfaceCmpNil100(b *testing.B) {
}
}
}
+
+func BenchmarkDefer(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ defer1()
+ }
+}
+
+func defer1() {
+ defer func(x, y, z int) {
+ if recover() != nil || x != 1 || y != 2 || z != 3 {
+ panic("bad recover")
+ }
+ }(1, 2, 3)
+ return
+}
+
+func BenchmarkDefer10(b *testing.B) {
+ for i := 0; i < b.N/10; i++ {
+ defer2()
+ }
+}
+
+func defer2() {
+ for i := 0; i < 10; i++ {
+ defer func(x, y, z int) {
+ if recover() != nil || x != 1 || y != 2 || z != 3 {
+ panic("bad recover")
+ }
+ }(1, 2, 3)
+ }
+}
+
+func BenchmarkDeferMany(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ defer func(x, y, z int) {
+ if recover() != nil || x != 1 || y != 2 || z != 3 {
+ panic("bad recover")
+ }
+ }(1, 2, 3)
+ }
+}
diff --git a/src/pkg/runtime/sema.goc b/src/pkg/runtime/sema.goc
index 2300c56aa..c4b5247b3 100644
--- a/src/pkg/runtime/sema.goc
+++ b/src/pkg/runtime/sema.goc
@@ -24,30 +24,33 @@ package sync
typedef struct Sema Sema;
struct Sema
{
- uint32 volatile *addr;
- G *g;
- Sema *prev;
- Sema *next;
+ uint32 volatile* addr;
+ G* g;
+ int64 releasetime;
+ Sema* prev;
+ Sema* next;
};
typedef struct SemaRoot SemaRoot;
struct SemaRoot
{
- Lock;
- Sema *head;
- Sema *tail;
+ Lock;
+ Sema* head;
+ Sema* tail;
// Number of waiters. Read w/o the lock.
- uint32 volatile nwait;
+ uint32 volatile nwait;
};
// Prime to not correlate with any user patterns.
#define SEMTABLESZ 251
-static union
+union semtable
{
SemaRoot;
uint8 pad[CacheLineSize];
-} semtable[SEMTABLESZ];
+};
+#pragma dataflag 16 /* mark semtable as 'no pointers', hiding from garbage collector */
+static union semtable semtable[SEMTABLESZ];
static SemaRoot*
semroot(uint32 *addr)
@@ -95,12 +98,13 @@ cansemacquire(uint32 *addr)
return 0;
}
-void
-runtime·semacquire(uint32 volatile *addr)
+static void
+semacquireimpl(uint32 volatile *addr, int32 profile)
{
- Sema s;
+ Sema s; // Needs to be allocated on stack, otherwise garbage collector could deallocate it
SemaRoot *root;
-
+ int64 t0;
+
// Easy case.
if(cansemacquire(addr))
return;
@@ -112,6 +116,12 @@ runtime·semacquire(uint32 volatile *addr)
// sleep
// (waiter descriptor is dequeued by signaler)
root = semroot(addr);
+ t0 = 0;
+ s.releasetime = 0;
+ if(profile && runtime·blockprofilerate > 0) {
+ t0 = runtime·cputicks();
+ s.releasetime = -1;
+ }
for(;;) {
runtime·lock(root);
// Add ourselves to nwait to disable "easy case" in semrelease.
@@ -125,16 +135,22 @@ runtime·semacquire(uint32 volatile *addr)
// Any semrelease after the cansemacquire knows we're waiting
// (we set nwait above), so go to sleep.
semqueue(root, addr, &s);
- g->status = Gwaiting;
- g->waitreason = "semacquire";
- runtime·unlock(root);
- runtime·gosched();
- if(cansemacquire(addr))
+ runtime·park(runtime·unlock, root, "semacquire");
+ if(cansemacquire(addr)) {
+ if(t0)
+ runtime·blockevent(s.releasetime - t0, 3);
return;
+ }
}
}
void
+runtime·semacquire(uint32 volatile *addr)
+{
+ semacquireimpl(addr, 0);
+}
+
+void
runtime·semrelease(uint32 volatile *addr)
{
Sema *s;
@@ -165,12 +181,15 @@ runtime·semrelease(uint32 volatile *addr)
}
}
runtime·unlock(root);
- if(s)
+ if(s) {
+ if(s->releasetime)
+ s->releasetime = runtime·cputicks();
runtime·ready(s->g);
+ }
}
func runtime_Semacquire(addr *uint32) {
- runtime·semacquire(addr);
+ semacquireimpl(addr, 1);
}
func runtime_Semrelease(addr *uint32) {
diff --git a/src/pkg/runtime/signal_darwin_386.c b/src/pkg/runtime/signal_darwin_386.c
index 9e986352b..132ca931b 100644
--- a/src/pkg/runtime/signal_darwin_386.c
+++ b/src/pkg/runtime/signal_darwin_386.c
@@ -47,7 +47,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
t = &runtime·sigtab[sig];
if(info->si_code != SI_USER && (t->flags & SigPanic)) {
- if(gp == nil)
+ if(gp == nil || gp == m->g0)
goto Throw;
// Work around Leopard bug that doesn't set FPE_INTDIV.
// Look at instruction to see if it is a divide.
@@ -101,7 +101,11 @@ Throw:
runtime·printf("%s\n", runtime·sigtab[sig].name);
}
- runtime·printf("pc: %x\n", r->eip);
+ runtime·printf("PC=%x\n", r->eip);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
@@ -121,6 +125,8 @@ runtime·signalstack(byte *p, int32 n)
st.ss_sp = p;
st.ss_size = n;
st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
runtime·sigaltstack(&st, nil);
}
@@ -129,6 +135,15 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
{
Sigaction sa;
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ runtime·sigaction(i, nil, &sa);
+ if(*(void**)sa.__sigaction_u == SIG_IGN)
+ return;
+ }
+
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
if(restart)
diff --git a/src/pkg/runtime/signal_darwin_amd64.c b/src/pkg/runtime/signal_darwin_amd64.c
index d9c5f48e7..4b7256bf4 100644
--- a/src/pkg/runtime/signal_darwin_amd64.c
+++ b/src/pkg/runtime/signal_darwin_amd64.c
@@ -55,7 +55,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
t = &runtime·sigtab[sig];
if(info->si_code != SI_USER && (t->flags & SigPanic)) {
- if(gp == nil)
+ if(gp == nil || gp == m->g0)
goto Throw;
// Work around Leopard bug that doesn't set FPE_INTDIV.
// Look at instruction to see if it is a divide.
@@ -111,7 +111,11 @@ Throw:
runtime·printf("%s\n", runtime·sigtab[sig].name);
}
- runtime·printf("pc: %X\n", r->rip);
+ runtime·printf("PC=%X\n", r->rip);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
@@ -131,6 +135,8 @@ runtime·signalstack(byte *p, int32 n)
st.ss_sp = p;
st.ss_size = n;
st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
runtime·sigaltstack(&st, nil);
}
@@ -139,6 +145,15 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
{
Sigaction sa;
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ runtime·sigaction(i, nil, &sa);
+ if(*(void**)sa.__sigaction_u == SIG_IGN)
+ return;
+ }
+
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
if(restart)
diff --git a/src/pkg/runtime/signal_freebsd_386.c b/src/pkg/runtime/signal_freebsd_386.c
index 80da95d98..254e5e277 100644
--- a/src/pkg/runtime/signal_freebsd_386.c
+++ b/src/pkg/runtime/signal_freebsd_386.c
@@ -15,7 +15,7 @@ typedef struct sigaction {
void (*__sa_sigaction)(int32, Siginfo*, void *);
} __sigaction_u; /* signal handler */
int32 sa_flags; /* see signal options below */
- int64 sa_mask; /* signal mask to apply */
+ Sigset sa_mask; /* signal mask to apply */
} Sigaction;
void
@@ -54,7 +54,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
t = &runtime·sigtab[sig];
if(info->si_code != SI_USER && (t->flags & SigPanic)) {
- if(gp == nil)
+ if(gp == nil || gp == m->g0)
goto Throw;
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
@@ -97,6 +97,10 @@ Throw:
runtime·printf("%s\n", runtime·sigtab[sig].name);
runtime·printf("PC=%X\n", r->mc_eip);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
@@ -116,6 +120,8 @@ runtime·signalstack(byte *p, int32 n)
st.ss_sp = (int8*)p;
st.ss_size = n;
st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
runtime·sigaltstack(&st, nil);
}
@@ -124,11 +130,23 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
{
Sigaction sa;
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ runtime·sigaction(i, nil, &sa);
+ if(sa.__sigaction_u.__sa_sigaction == SIG_IGN)
+ return;
+ }
+
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
if(restart)
sa.sa_flags |= SA_RESTART;
- sa.sa_mask = ~0ULL;
+ sa.sa_mask.__bits[0] = ~(uint32)0;
+ sa.sa_mask.__bits[1] = ~(uint32)0;
+ sa.sa_mask.__bits[2] = ~(uint32)0;
+ sa.sa_mask.__bits[3] = ~(uint32)0;
if (fn == runtime·sighandler)
fn = (void*)runtime·sigtramp;
sa.__sigaction_u.__sa_sigaction = (void*)fn;
diff --git a/src/pkg/runtime/signal_freebsd_amd64.c b/src/pkg/runtime/signal_freebsd_amd64.c
index e4307682f..7dbf36075 100644
--- a/src/pkg/runtime/signal_freebsd_amd64.c
+++ b/src/pkg/runtime/signal_freebsd_amd64.c
@@ -15,7 +15,7 @@ typedef struct sigaction {
void (*__sa_sigaction)(int32, Siginfo*, void *);
} __sigaction_u; /* signal handler */
int32 sa_flags; /* see signal options below */
- int64 sa_mask; /* signal mask to apply */
+ Sigset sa_mask; /* signal mask to apply */
} Sigaction;
void
@@ -62,7 +62,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
t = &runtime·sigtab[sig];
if(info->si_code != SI_USER && (t->flags & SigPanic)) {
- if(gp == nil)
+ if(gp == nil || gp == m->g0)
goto Throw;
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
@@ -105,6 +105,10 @@ Throw:
runtime·printf("%s\n", runtime·sigtab[sig].name);
runtime·printf("PC=%X\n", r->mc_rip);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
@@ -124,6 +128,8 @@ runtime·signalstack(byte *p, int32 n)
st.ss_sp = (int8*)p;
st.ss_size = n;
st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
runtime·sigaltstack(&st, nil);
}
@@ -132,11 +138,23 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
{
Sigaction sa;
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ runtime·sigaction(i, nil, &sa);
+ if(sa.__sigaction_u.__sa_sigaction == SIG_IGN)
+ return;
+ }
+
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
if(restart)
sa.sa_flags |= SA_RESTART;
- sa.sa_mask = ~0ULL;
+ sa.sa_mask.__bits[0] = ~(uint32)0;
+ sa.sa_mask.__bits[1] = ~(uint32)0;
+ sa.sa_mask.__bits[2] = ~(uint32)0;
+ sa.sa_mask.__bits[3] = ~(uint32)0;
if (fn == runtime·sighandler)
fn = (void*)runtime·sigtramp;
sa.__sigaction_u.__sa_sigaction = (void*)fn;
diff --git a/src/pkg/runtime/signal_freebsd_arm.c b/src/pkg/runtime/signal_freebsd_arm.c
new file mode 100644
index 000000000..50c3221bb
--- /dev/null
+++ b/src/pkg/runtime/signal_freebsd_arm.c
@@ -0,0 +1,193 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "runtime.h"
+#include "defs_GOOS_GOARCH.h"
+#include "signals_GOOS.h"
+#include "os_GOOS.h"
+
+#define r0 __gregs[0]
+#define r1 __gregs[1]
+#define r2 __gregs[2]
+#define r3 __gregs[3]
+#define r4 __gregs[4]
+#define r5 __gregs[5]
+#define r6 __gregs[6]
+#define r7 __gregs[7]
+#define r8 __gregs[8]
+#define r9 __gregs[9]
+#define r10 __gregs[10]
+#define r11 __gregs[11]
+#define r12 __gregs[12]
+#define r13 __gregs[13]
+#define r14 __gregs[14]
+#define r15 __gregs[15]
+#define cpsr __gregs[16]
+
+void
+runtime·dumpregs(Mcontext *r)
+{
+ runtime·printf("r0 %x\n", r->r0);
+ runtime·printf("r1 %x\n", r->r1);
+ runtime·printf("r2 %x\n", r->r2);
+ runtime·printf("r3 %x\n", r->r3);
+ runtime·printf("r4 %x\n", r->r4);
+ runtime·printf("r5 %x\n", r->r5);
+ runtime·printf("r6 %x\n", r->r6);
+ runtime·printf("r7 %x\n", r->r7);
+ runtime·printf("r8 %x\n", r->r8);
+ runtime·printf("r9 %x\n", r->r9);
+ runtime·printf("r10 %x\n", r->r10);
+ runtime·printf("fp %x\n", r->r11);
+ runtime·printf("ip %x\n", r->r12);
+ runtime·printf("sp %x\n", r->r13);
+ runtime·printf("lr %x\n", r->r14);
+ runtime·printf("pc %x\n", r->r15);
+ runtime·printf("cpsr %x\n", r->cpsr);
+}
+
+extern void runtime·sigtramp(void);
+
+typedef struct sigaction {
+ union {
+ void (*__sa_handler)(int32);
+ void (*__sa_sigaction)(int32, Siginfo*, void *);
+ } __sigaction_u; /* signal handler */
+ int32 sa_flags; /* see signal options below */
+ Sigset sa_mask; /* signal mask to apply */
+} Sigaction;
+
+void
+runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
+{
+ Ucontext *uc;
+ Mcontext *r;
+ SigTab *t;
+
+ uc = context;
+ r = &uc->uc_mcontext;
+
+ if(sig == SIGPROF) {
+ runtime·sigprof((uint8*)r->r15, (uint8*)r->r13, (uint8*)r->r14, gp);
+ return;
+ }
+
+ t = &runtime·sigtab[sig];
+ if(info->si_code != SI_USER && (t->flags & SigPanic)) {
+ if(gp == nil || gp == m->g0)
+ goto Throw;
+ // Make it look like a call to the signal func.
+ // Have to pass arguments out of band since
+ // augmenting the stack frame would break
+ // the unwinding code.
+ gp->sig = sig;
+ gp->sigcode0 = info->si_code;
+ gp->sigcode1 = (uintptr)info->si_addr;
+ gp->sigpc = r->r15;
+
+ // Only push runtime·sigpanic if r->mc_rip != 0.
+ // If r->mc_rip == 0, probably panicked because of a
+ // call to a nil func. Not pushing that onto sp will
+ // make the trace look like a call to runtime·sigpanic instead.
+ // (Otherwise the trace will end at runtime·sigpanic and we
+ // won't get to see who faulted.)
+ if(r->r15 != 0)
+ r->r14 = r->r15;
+ // In case we are panicking from external C code
+ r->r10 = (uintptr)gp;
+ r->r9 = (uintptr)m;
+ r->r15 = (uintptr)runtime·sigpanic;
+ return;
+ }
+
+ if(info->si_code == SI_USER || (t->flags & SigNotify))
+ if(runtime·sigsend(sig))
+ return;
+ if(t->flags & SigKill)
+ runtime·exit(2);
+ if(!(t->flags & SigThrow))
+ return;
+
+Throw:
+ runtime·startpanic();
+
+ if(sig < 0 || sig >= NSIG)
+ runtime·printf("Signal %d\n", sig);
+ else
+ runtime·printf("%s\n", runtime·sigtab[sig].name);
+
+ runtime·printf("PC=%x\n", r->r15);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
+ runtime·printf("\n");
+
+ if(runtime·gotraceback()){
+ runtime·traceback((void*)r->r15, (void*)r->r13, (void*)r->r14, gp);
+ runtime·tracebackothers(gp);
+ runtime·printf("\n");
+ runtime·dumpregs(r);
+ }
+
+// breakpoint();
+ runtime·exit(2);
+}
+
+void
+runtime·signalstack(byte *p, int32 n)
+{
+ Sigaltstack st;
+
+ st.ss_sp = (uint8*)p;
+ st.ss_size = n;
+ st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
+ runtime·sigaltstack(&st, nil);
+}
+
+void
+runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
+{
+ Sigaction sa;
+
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ runtime·sigaction(i, nil, &sa);
+ if(sa.__sigaction_u.__sa_sigaction == SIG_IGN)
+ return;
+ }
+
+ runtime·memclr((byte*)&sa, sizeof sa);
+ sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
+ if(restart)
+ sa.sa_flags |= SA_RESTART;
+ sa.sa_mask.__bits[0] = ~(uint32)0;
+ sa.sa_mask.__bits[1] = ~(uint32)0;
+ sa.sa_mask.__bits[2] = ~(uint32)0;
+ sa.sa_mask.__bits[3] = ~(uint32)0;
+ if (fn == runtime·sighandler)
+ fn = (void*)runtime·sigtramp;
+ sa.__sigaction_u.__sa_sigaction = (void*)fn;
+ runtime·sigaction(i, &sa, nil);
+}
+
+void
+runtime·checkgoarm(void)
+{
+ // TODO(minux)
+}
+
+#pragma textflag 7
+int64
+runtime·cputicks(void)
+{
+ // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1().
+ // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
+ // TODO: need more entropy to better seed fastrand1.
+ return runtime·nanotime();
+}
diff --git a/src/pkg/runtime/signal_linux_386.c b/src/pkg/runtime/signal_linux_386.c
index b154ad887..9b45ec3bd 100644
--- a/src/pkg/runtime/signal_linux_386.c
+++ b/src/pkg/runtime/signal_linux_386.c
@@ -50,7 +50,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
t = &runtime·sigtab[sig];
if(info->si_code != SI_USER && (t->flags & SigPanic)) {
- if(gp == nil)
+ if(gp == nil || gp == m->g0)
goto Throw;
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
@@ -93,6 +93,10 @@ Throw:
runtime·printf("%s\n", runtime·sigtab[sig].name);
runtime·printf("PC=%X\n", r->eip);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
@@ -112,6 +116,8 @@ runtime·signalstack(byte *p, int32 n)
st.ss_sp = p;
st.ss_size = n;
st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
runtime·sigaltstack(&st, nil);
}
@@ -120,6 +126,16 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
{
Sigaction sa;
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ if(runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)) != 0)
+ runtime·throw("rt_sigaction read failure");
+ if(sa.k_sa_handler == SIG_IGN)
+ return;
+ }
+
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER;
if(restart)
@@ -129,7 +145,8 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
if(fn == runtime·sighandler)
fn = (void*)runtime·sigtramp;
sa.k_sa_handler = fn;
- runtime·rt_sigaction(i, &sa, nil, 8);
+ if(runtime·rt_sigaction(i, &sa, nil, sizeof(sa.sa_mask)) != 0)
+ runtime·throw("rt_sigaction failure");
}
#define AT_NULL 0
diff --git a/src/pkg/runtime/signal_linux_amd64.c b/src/pkg/runtime/signal_linux_amd64.c
index 14095ba61..c4e39a6ab 100644
--- a/src/pkg/runtime/signal_linux_amd64.c
+++ b/src/pkg/runtime/signal_linux_amd64.c
@@ -60,7 +60,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
t = &runtime·sigtab[sig];
if(info->si_code != SI_USER && (t->flags & SigPanic)) {
- if(gp == nil)
+ if(gp == nil || gp == m->g0)
goto Throw;
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
@@ -103,6 +103,10 @@ Throw:
runtime·printf("%s\n", runtime·sigtab[sig].name);
runtime·printf("PC=%X\n", r->rip);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
@@ -122,6 +126,8 @@ runtime·signalstack(byte *p, int32 n)
st.ss_sp = p;
st.ss_size = n;
st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
runtime·sigaltstack(&st, nil);
}
@@ -130,14 +136,27 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
{
Sigaction sa;
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ if(runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)) != 0)
+ runtime·throw("rt_sigaction read failure");
+ if(sa.sa_handler == SIG_IGN)
+ return;
+ }
+
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER;
if(restart)
sa.sa_flags |= SA_RESTART;
sa.sa_mask = ~0ULL;
+ // TODO(adonovan): Linux manpage says "sa_restorer element is
+ // obsolete and should not be used". Avoid it here, and test.
sa.sa_restorer = (void*)runtime·sigreturn;
if(fn == runtime·sighandler)
fn = (void*)runtime·sigtramp;
sa.sa_handler = fn;
- runtime·rt_sigaction(i, &sa, nil, 8);
+ if(runtime·rt_sigaction(i, &sa, nil, sizeof(sa.sa_mask)) != 0)
+ runtime·throw("rt_sigaction failure");
}
diff --git a/src/pkg/runtime/signal_linux_arm.c b/src/pkg/runtime/signal_linux_arm.c
index 176a4ce56..c26caa7cd 100644
--- a/src/pkg/runtime/signal_linux_arm.c
+++ b/src/pkg/runtime/signal_linux_arm.c
@@ -57,7 +57,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
t = &runtime·sigtab[sig];
if(info->si_code != SI_USER && (t->flags & SigPanic)) {
- if(gp == nil)
+ if(gp == nil || gp == m->g0)
goto Throw;
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
@@ -68,13 +68,22 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
gp->sigcode1 = r->fault_address;
gp->sigpc = r->arm_pc;
- // If this is a leaf function, we do smash LR,
- // but we're not going back there anyway.
- // Don't bother smashing if r->arm_pc is 0,
- // which is probably a call to a nil func: the
- // old link register is more useful in the stack trace.
+ // We arrange lr, and pc to pretend the panicking
+ // function calls sigpanic directly.
+ // Always save LR to stack so that panics in leaf
+ // functions are correctly handled. This smashes
+ // the stack frame but we're not going back there
+ // anyway.
+ r->arm_sp -= 4;
+ *(uint32 *)r->arm_sp = r->arm_lr;
+ // Don't bother saving PC if it's zero, which is
+ // probably a call to a nil func: the old link register
+ // is more useful in the stack trace.
if(r->arm_pc != 0)
r->arm_lr = r->arm_pc;
+ // In case we are panicking from external C code
+ r->arm_r10 = (uintptr)gp;
+ r->arm_r9 = (uintptr)m;
r->arm_pc = (uintptr)runtime·sigpanic;
return;
}
@@ -98,6 +107,10 @@ Throw:
runtime·printf("%s\n", runtime·sigtab[sig].name);
runtime·printf("PC=%x\n", r->arm_pc);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
@@ -119,6 +132,8 @@ runtime·signalstack(byte *p, int32 n)
st.ss_sp = p;
st.ss_size = n;
st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
runtime·sigaltstack(&st, nil);
}
@@ -127,6 +142,16 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
{
Sigaction sa;
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ if(runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)) != 0)
+ runtime·throw("rt_sigaction read failure");
+ if(sa.sa_handler == SIG_IGN)
+ return;
+ }
+
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER;
if(restart)
@@ -136,5 +161,81 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
if(fn == runtime·sighandler)
fn = (void*)runtime·sigtramp;
sa.sa_handler = fn;
- runtime·rt_sigaction(i, &sa, nil, 8);
+ if(runtime·rt_sigaction(i, &sa, nil, sizeof(sa.sa_mask)) != 0)
+ runtime·throw("rt_sigaction failure");
+}
+
+#define AT_NULL 0
+#define AT_PLATFORM 15 // introduced in at least 2.6.11
+#define AT_HWCAP 16 // introduced in at least 2.6.11
+#define AT_RANDOM 25 // introduced in 2.6.29
+#define HWCAP_VFP (1 << 6) // introduced in at least 2.6.11
+#define HWCAP_VFPv3 (1 << 13) // introduced in 2.6.30
+static uint32 runtime·randomNumber;
+uint8 runtime·armArch = 6; // we default to ARMv6
+uint32 runtime·hwcap; // set by setup_auxv
+uint8 runtime·goarm; // set by 5l
+
+void
+runtime·checkgoarm(void)
+{
+ if(runtime·goarm > 5 && !(runtime·hwcap & HWCAP_VFP)) {
+ runtime·printf("runtime: this CPU has no floating point hardware, so it cannot run\n");
+ runtime·printf("this GOARM=%d binary. Recompile using GOARM=5.\n", runtime·goarm);
+ runtime·exit(1);
+ }
+ if(runtime·goarm > 6 && !(runtime·hwcap & HWCAP_VFPv3)) {
+ runtime·printf("runtime: this CPU has no VFPv3 floating point hardware, so it cannot run\n");
+ runtime·printf("this GOARM=%d binary. Recompile using GOARM=6.\n", runtime·goarm);
+ runtime·exit(1);
+ }
+}
+
+#pragma textflag 7
+void
+runtime·setup_auxv(int32 argc, void *argv_list)
+{
+ byte **argv;
+ byte **envp;
+ byte *rnd;
+ uint32 *auxv;
+ uint32 t;
+
+ argv = &argv_list;
+
+ // skip envp to get to ELF auxiliary vector.
+ for(envp = &argv[argc+1]; *envp != nil; envp++)
+ ;
+ envp++;
+
+ for(auxv=(uint32*)envp; auxv[0] != AT_NULL; auxv += 2) {
+ switch(auxv[0]) {
+ case AT_RANDOM: // kernel provided 16-byte worth of random data
+ if(auxv[1]) {
+ rnd = (byte*)auxv[1];
+ runtime·randomNumber = rnd[4] | rnd[5]<<8 | rnd[6]<<16 | rnd[7]<<24;
+ }
+ break;
+ case AT_PLATFORM: // v5l, v6l, v7l
+ if(auxv[1]) {
+ t = *(uint8*)(auxv[1]+1);
+ if(t >= '5' && t <= '7')
+ runtime·armArch = t - '0';
+ }
+ break;
+ case AT_HWCAP: // CPU capability bit flags
+ runtime·hwcap = auxv[1];
+ break;
+ }
+ }
+}
+
+#pragma textflag 7
+int64
+runtime·cputicks(void)
+{
+ // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1().
+ // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
+ // runtime·randomNumber provides better seeding of fastrand1.
+ return runtime·nanotime() + runtime·randomNumber;
}
diff --git a/src/pkg/runtime/signal_netbsd_386.c b/src/pkg/runtime/signal_netbsd_386.c
index 39d829484..08744c425 100644
--- a/src/pkg/runtime/signal_netbsd_386.c
+++ b/src/pkg/runtime/signal_netbsd_386.c
@@ -7,76 +7,79 @@
#include "signals_GOOS.h"
#include "os_GOOS.h"
+extern void runtime·lwp_tramp(void);
extern void runtime·sigtramp(void);
typedef struct sigaction {
union {
- void (*__sa_handler)(int32);
- void (*__sa_sigaction)(int32, Siginfo*, void *);
- } __sigaction_u; /* signal handler */
- uint32 sa_mask; /* signal mask to apply */
+ void (*_sa_handler)(int32);
+ void (*_sa_sigaction)(int32, Siginfo*, void *);
+ } _sa_u; /* signal handler */
+ uint32 sa_mask[4]; /* signal mask to apply */
int32 sa_flags; /* see signal options below */
} Sigaction;
void
-runtime·dumpregs(Sigcontext *r)
+runtime·dumpregs(McontextT *mc)
{
- runtime·printf("eax %x\n", r->sc_eax);
- runtime·printf("ebx %x\n", r->sc_ebx);
- runtime·printf("ecx %x\n", r->sc_ecx);
- runtime·printf("edx %x\n", r->sc_edx);
- runtime·printf("edi %x\n", r->sc_edi);
- runtime·printf("esi %x\n", r->sc_esi);
- runtime·printf("ebp %x\n", r->sc_ebp);
- runtime·printf("esp %x\n", r->sc_esp);
- runtime·printf("eip %x\n", r->sc_eip);
- runtime·printf("eflags %x\n", r->sc_eflags);
- runtime·printf("cs %x\n", r->sc_cs);
- runtime·printf("fs %x\n", r->sc_fs);
- runtime·printf("gs %x\n", r->sc_gs);
+ runtime·printf("eax %x\n", mc->__gregs[REG_EAX]);
+ runtime·printf("ebx %x\n", mc->__gregs[REG_EBX]);
+ runtime·printf("ecx %x\n", mc->__gregs[REG_ECX]);
+ runtime·printf("edx %x\n", mc->__gregs[REG_EDX]);
+ runtime·printf("edi %x\n", mc->__gregs[REG_EDI]);
+ runtime·printf("esi %x\n", mc->__gregs[REG_ESI]);
+ runtime·printf("ebp %x\n", mc->__gregs[REG_EBP]);
+ runtime·printf("esp %x\n", mc->__gregs[REG_UESP]);
+ runtime·printf("eip %x\n", mc->__gregs[REG_EIP]);
+ runtime·printf("eflags %x\n", mc->__gregs[REG_EFL]);
+ runtime·printf("cs %x\n", mc->__gregs[REG_CS]);
+ runtime·printf("fs %x\n", mc->__gregs[REG_FS]);
+ runtime·printf("gs %x\n", mc->__gregs[REG_GS]);
}
void
runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
{
- Sigcontext *r = context;
+ UcontextT *uc = context;
+ McontextT *mc = &uc->uc_mcontext;
uintptr *sp;
SigTab *t;
if(sig == SIGPROF) {
- runtime·sigprof((uint8*)r->sc_eip, (uint8*)r->sc_esp, nil, gp);
+ runtime·sigprof((uint8*)mc->__gregs[REG_EIP],
+ (uint8*)mc->__gregs[REG_UESP], nil, gp);
return;
}
t = &runtime·sigtab[sig];
- if(info->si_code != SI_USER && (t->flags & SigPanic)) {
- if(gp == nil)
+ if(info->_code != SI_USER && (t->flags & SigPanic)) {
+ if(gp == nil || gp == m->g0)
goto Throw;
// Make it look like a call to the signal func.
- // Have to pass arguments out of band since
+ // We need to pass arguments out of band since
// augmenting the stack frame would break
// the unwinding code.
gp->sig = sig;
- gp->sigcode0 = info->si_code;
- gp->sigcode1 = *(uintptr*)((byte*)info + 12); /* si_addr */
- gp->sigpc = r->sc_eip;
-
- // Only push runtime·sigpanic if r->sc_eip != 0.
- // If r->sc_eip == 0, probably panicked because of a
- // call to a nil func. Not pushing that onto sp will
- // make the trace look like a call to runtime·sigpanic instead.
- // (Otherwise the trace will end at runtime·sigpanic and we
- // won't get to see who faulted.)
- if(r->sc_eip != 0) {
- sp = (uintptr*)r->sc_esp;
- *--sp = r->sc_eip;
- r->sc_esp = (uintptr)sp;
+ gp->sigcode0 = info->_code;
+ gp->sigcode1 = *(uintptr*)&info->_reason[0]; /* _addr */
+ gp->sigpc = mc->__gregs[REG_EIP];
+
+ // Only push runtime·sigpanic if __gregs[REG_EIP] != 0.
+ // If __gregs[REG_EIP] == 0, probably panicked because of a
+ // call to a nil func. Not pushing that onto sp will make the
+ // trace look like a call to runtime·sigpanic instead.
+ // (Otherwise the trace will end at runtime·sigpanic
+ // and we won't get to see who faulted.)
+ if(mc->__gregs[REG_EIP] != 0) {
+ sp = (uintptr*)mc->__gregs[REG_UESP];
+ *--sp = mc->__gregs[REG_EIP];
+ mc->__gregs[REG_UESP] = (uintptr)sp;
}
- r->sc_eip = (uintptr)runtime·sigpanic;
+ mc->__gregs[REG_EIP] = (uintptr)runtime·sigpanic;
return;
}
- if(info->si_code == SI_USER || (t->flags & SigNotify))
+ if(info->_code == SI_USER || (t->flags & SigNotify))
if(runtime·sigsend(sig))
return;
if(t->flags & SigKill)
@@ -92,13 +95,18 @@ Throw:
else
runtime·printf("%s\n", runtime·sigtab[sig].name);
- runtime·printf("PC=%X\n", r->sc_eip);
+ runtime·printf("PC=%X\n", mc->__gregs[REG_EIP]);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
- runtime·traceback((void*)r->sc_eip, (void*)r->sc_esp, 0, gp);
+ runtime·traceback((void*)mc->__gregs[REG_EIP],
+ (void*)mc->__gregs[REG_UESP], 0, gp);
runtime·tracebackothers(gp);
- runtime·dumpregs(r);
+ runtime·dumpregs(mc);
}
runtime·exit(2);
@@ -109,9 +117,11 @@ runtime·signalstack(byte *p, int32 n)
{
Sigaltstack st;
- st.ss_sp = (int8*)p;
+ st.ss_sp = p;
st.ss_size = n;
st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
runtime·sigaltstack(&st, nil);
}
@@ -120,13 +130,35 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
{
Sigaction sa;
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ runtime·sigaction(i, nil, &sa);
+ if(sa._sa_u._sa_sigaction == SIG_IGN)
+ return;
+ }
+
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
if(restart)
sa.sa_flags |= SA_RESTART;
- sa.sa_mask = ~0ULL;
+ sa.sa_mask[0] = ~0U;
+ sa.sa_mask[1] = ~0U;
+ sa.sa_mask[2] = ~0U;
+ sa.sa_mask[3] = ~0U;
if (fn == runtime·sighandler)
fn = (void*)runtime·sigtramp;
- sa.__sigaction_u.__sa_sigaction = (void*)fn;
+ sa._sa_u._sa_sigaction = (void*)fn;
runtime·sigaction(i, &sa, nil);
}
+
+void
+runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void))
+{
+ mc->__gregs[REG_EIP] = (uint32)runtime·lwp_tramp;
+ mc->__gregs[REG_UESP] = (uint32)stack;
+ mc->__gregs[REG_EBX] = (uint32)mp;
+ mc->__gregs[REG_EDX] = (uint32)gp;
+ mc->__gregs[REG_ESI] = (uint32)fn;
+}
diff --git a/src/pkg/runtime/signal_netbsd_amd64.c b/src/pkg/runtime/signal_netbsd_amd64.c
index 8b4f624e7..46afb682b 100644
--- a/src/pkg/runtime/signal_netbsd_amd64.c
+++ b/src/pkg/runtime/signal_netbsd_amd64.c
@@ -7,85 +7,86 @@
#include "signals_GOOS.h"
#include "os_GOOS.h"
+extern void runtime·lwp_tramp(void);
extern void runtime·sigtramp(void);
typedef struct sigaction {
union {
- void (*__sa_handler)(int32);
- void (*__sa_sigaction)(int32, Siginfo*, void *);
- } __sigaction_u; /* signal handler */
- uint32 sa_mask; /* signal mask to apply */
+ void (*_sa_handler)(int32);
+ void (*_sa_sigaction)(int32, Siginfo*, void *);
+ } _sa_u; /* signal handler */
+ uint32 sa_mask[4]; /* signal mask to apply */
int32 sa_flags; /* see signal options below */
} Sigaction;
void
-runtime·dumpregs(Sigcontext *r)
+runtime·dumpregs(McontextT *mc)
{
- runtime·printf("rax %X\n", r->sc_rax);
- runtime·printf("rbx %X\n", r->sc_rbx);
- runtime·printf("rcx %X\n", r->sc_rcx);
- runtime·printf("rdx %X\n", r->sc_rdx);
- runtime·printf("rdi %X\n", r->sc_rdi);
- runtime·printf("rsi %X\n", r->sc_rsi);
- runtime·printf("rbp %X\n", r->sc_rbp);
- runtime·printf("rsp %X\n", r->sc_rsp);
- runtime·printf("r8 %X\n", r->sc_r8);
- runtime·printf("r9 %X\n", r->sc_r9);
- runtime·printf("r10 %X\n", r->sc_r10);
- runtime·printf("r11 %X\n", r->sc_r11);
- runtime·printf("r12 %X\n", r->sc_r12);
- runtime·printf("r13 %X\n", r->sc_r13);
- runtime·printf("r14 %X\n", r->sc_r14);
- runtime·printf("r15 %X\n", r->sc_r15);
- runtime·printf("rip %X\n", r->sc_rip);
- runtime·printf("rflags %X\n", r->sc_rflags);
- runtime·printf("cs %X\n", r->sc_cs);
- runtime·printf("fs %X\n", r->sc_fs);
- runtime·printf("gs %X\n", r->sc_gs);
+ runtime·printf("rax %X\n", mc->__gregs[REG_RAX]);
+ runtime·printf("rbx %X\n", mc->__gregs[REG_RBX]);
+ runtime·printf("rcx %X\n", mc->__gregs[REG_RCX]);
+ runtime·printf("rdx %X\n", mc->__gregs[REG_RDX]);
+ runtime·printf("rdi %X\n", mc->__gregs[REG_RDI]);
+ runtime·printf("rsi %X\n", mc->__gregs[REG_RSI]);
+ runtime·printf("rbp %X\n", mc->__gregs[REG_RBP]);
+ runtime·printf("rsp %X\n", mc->__gregs[REG_RSP]);
+ runtime·printf("r8 %X\n", mc->__gregs[REG_R8]);
+ runtime·printf("r9 %X\n", mc->__gregs[REG_R9]);
+ runtime·printf("r10 %X\n", mc->__gregs[REG_R10]);
+ runtime·printf("r11 %X\n", mc->__gregs[REG_R11]);
+ runtime·printf("r12 %X\n", mc->__gregs[REG_R12]);
+ runtime·printf("r13 %X\n", mc->__gregs[REG_R13]);
+ runtime·printf("r14 %X\n", mc->__gregs[REG_R14]);
+ runtime·printf("r15 %X\n", mc->__gregs[REG_R15]);
+ runtime·printf("rip %X\n", mc->__gregs[REG_RIP]);
+ runtime·printf("rflags %X\n", mc->__gregs[REG_RFLAGS]);
+ runtime·printf("cs %X\n", mc->__gregs[REG_CS]);
+ runtime·printf("fs %X\n", mc->__gregs[REG_FS]);
+ runtime·printf("gs %X\n", mc->__gregs[REG_GS]);
}
void
runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
{
- Sigcontext *r = context;
+ UcontextT *uc = context;
+ McontextT *mc = &uc->uc_mcontext;
uintptr *sp;
SigTab *t;
if(sig == SIGPROF) {
- runtime·sigprof((uint8*)r->sc_rip,
- (uint8*)r->sc_rsp, nil, gp);
+ runtime·sigprof((uint8*)mc->__gregs[REG_RIP],
+ (uint8*)mc->__gregs[REG_RSP], nil, gp);
return;
}
t = &runtime·sigtab[sig];
- if(info->si_code != SI_USER && (t->flags & SigPanic)) {
- if(gp == nil)
+ if(info->_code != SI_USER && (t->flags & SigPanic)) {
+ if(gp == nil || gp == m->g0)
goto Throw;
// Make it look like a call to the signal func.
- // Have to pass arguments out of band since
- // augmenting the stack frame would break
- // the unwinding code.
+ // We need to pass arguments out of band since augmenting the
+ // stack frame would break the unwinding code.
gp->sig = sig;
- gp->sigcode0 = info->si_code;
- gp->sigcode1 = *(uintptr*)((byte*)info + 16); /* si_addr */
- gp->sigpc = r->sc_rip;
-
- // Only push runtime·sigpanic if r->mc_rip != 0.
- // If r->mc_rip == 0, probably panicked because of a
- // call to a nil func. Not pushing that onto sp will
- // make the trace look like a call to runtime·sigpanic instead.
- // (Otherwise the trace will end at runtime·sigpanic and we
- // won't get to see who faulted.)
- if(r->sc_rip != 0) {
- sp = (uintptr*)r->sc_rsp;
- *--sp = r->sc_rip;
- r->sc_rsp = (uintptr)sp;
+ gp->sigcode0 = info->_code;
+ gp->sigcode1 = *(uintptr*)&info->_reason[0]; /* _addr */
+ gp->sigpc = mc->__gregs[REG_RIP];
+
+ // Only push runtime·sigpanic if __gregs[REG_RIP] != 0.
+ // If __gregs[REG_RIP] == 0, probably panicked because of a
+ // call to a nil func. Not pushing that onto sp will make the
+ // trace look like a call to runtime·sigpanic instead.
+ // (Otherwise the trace will end at runtime·sigpanic
+ // and we won't get to see who faulted.)
+ if(mc->__gregs[REG_RIP] != 0) {
+ sp = (uintptr*)mc->__gregs[REG_RSP];
+ *--sp = mc->__gregs[REG_RIP];
+ mc->__gregs[REG_RSP] = (uintptr)sp;
}
- r->sc_rip = (uintptr)runtime·sigpanic;
+ mc->__gregs[REG_RIP] = (uintptr)runtime·sigpanic;
return;
}
- if(info->si_code == SI_USER || (t->flags & SigNotify))
+ if(info->_code == SI_USER || (t->flags & SigNotify))
if(runtime·sigsend(sig))
return;
if(t->flags & SigKill)
@@ -101,13 +102,18 @@ Throw:
else
runtime·printf("%s\n", runtime·sigtab[sig].name);
- runtime·printf("PC=%X\n", r->sc_rip);
+ runtime·printf("PC=%X\n", mc->__gregs[REG_RIP]);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
- runtime·traceback((void*)r->sc_rip, (void*)r->sc_rsp, 0, gp);
+ runtime·traceback((void*)mc->__gregs[REG_RIP],
+ (void*)mc->__gregs[REG_RSP], 0, gp);
runtime·tracebackothers(gp);
- runtime·dumpregs(r);
+ runtime·dumpregs(mc);
}
runtime·exit(2);
@@ -118,9 +124,11 @@ runtime·signalstack(byte *p, int32 n)
{
Sigaltstack st;
- st.ss_sp = (int8*)p;
+ st.ss_sp = p;
st.ss_size = n;
st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
runtime·sigaltstack(&st, nil);
}
@@ -129,13 +137,36 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
{
Sigaction sa;
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ runtime·sigaction(i, nil, &sa);
+ if(sa._sa_u._sa_sigaction == SIG_IGN)
+ return;
+ }
+
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
if(restart)
sa.sa_flags |= SA_RESTART;
- sa.sa_mask = ~0ULL;
+ sa.sa_mask[0] = ~0U;
+ sa.sa_mask[1] = ~0U;
+ sa.sa_mask[2] = ~0U;
+ sa.sa_mask[3] = ~0U;
if (fn == runtime·sighandler)
fn = (void*)runtime·sigtramp;
- sa.__sigaction_u.__sa_sigaction = (void*)fn;
+ sa._sa_u._sa_sigaction = (void*)fn;
runtime·sigaction(i, &sa, nil);
}
+
+void
+runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void))
+{
+ // Machine dependent mcontext initialisation for LWP.
+ mc->__gregs[REG_RIP] = (uint64)runtime·lwp_tramp;
+ mc->__gregs[REG_RSP] = (uint64)stack;
+ mc->__gregs[REG_R8] = (uint64)mp;
+ mc->__gregs[REG_R9] = (uint64)gp;
+ mc->__gregs[REG_R12] = (uint64)fn;
+}
diff --git a/src/pkg/runtime/signal_netbsd_arm.c b/src/pkg/runtime/signal_netbsd_arm.c
new file mode 100644
index 000000000..97f62687b
--- /dev/null
+++ b/src/pkg/runtime/signal_netbsd_arm.c
@@ -0,0 +1,208 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "runtime.h"
+#include "defs_GOOS_GOARCH.h"
+#include "signals_GOOS.h"
+#include "os_GOOS.h"
+
+#define r0 __gregs[0]
+#define r1 __gregs[1]
+#define r2 __gregs[2]
+#define r3 __gregs[3]
+#define r4 __gregs[4]
+#define r5 __gregs[5]
+#define r6 __gregs[6]
+#define r7 __gregs[7]
+#define r8 __gregs[8]
+#define r9 __gregs[9]
+#define r10 __gregs[10]
+#define r11 __gregs[11]
+#define r12 __gregs[12]
+#define r13 __gregs[13]
+#define r14 __gregs[14]
+#define r15 __gregs[15]
+#define cpsr __gregs[16]
+
+void
+runtime·dumpregs(McontextT *r)
+{
+ runtime·printf("r0 %x\n", r->r0);
+ runtime·printf("r1 %x\n", r->r1);
+ runtime·printf("r2 %x\n", r->r2);
+ runtime·printf("r3 %x\n", r->r3);
+ runtime·printf("r4 %x\n", r->r4);
+ runtime·printf("r5 %x\n", r->r5);
+ runtime·printf("r6 %x\n", r->r6);
+ runtime·printf("r7 %x\n", r->r7);
+ runtime·printf("r8 %x\n", r->r8);
+ runtime·printf("r9 %x\n", r->r9);
+ runtime·printf("r10 %x\n", r->r10);
+ runtime·printf("fp %x\n", r->r11);
+ runtime·printf("ip %x\n", r->r12);
+ runtime·printf("sp %x\n", r->r13);
+ runtime·printf("lr %x\n", r->r14);
+ runtime·printf("pc %x\n", r->r15);
+ runtime·printf("cpsr %x\n", r->cpsr);
+}
+
+extern void runtime·lwp_tramp(void);
+extern void runtime·sigtramp(void);
+
+typedef struct sigaction {
+ union {
+ void (*_sa_handler)(int32);
+ void (*_sa_sigaction)(int32, Siginfo*, void *);
+ } _sa_u; /* signal handler */
+ uint32 sa_mask[4]; /* signal mask to apply */
+ int32 sa_flags; /* see signal options below */
+} Sigaction;
+
+void
+runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
+{
+ UcontextT *uc;
+ McontextT *r;
+ SigTab *t;
+
+ uc = context;
+ r = &uc->uc_mcontext;
+
+ if(sig == SIGPROF) {
+ runtime·sigprof((uint8*)r->r15, (uint8*)r->r13, (uint8*)r->r14, gp);
+ return;
+ }
+
+ t = &runtime·sigtab[sig];
+ if(info->_code != SI_USER && (t->flags & SigPanic)) {
+ if(gp == nil || gp == m->g0)
+ goto Throw;
+ // Make it look like a call to the signal func.
+ // We have to pass arguments out of band since
+ // augmenting the stack frame would break
+ // the unwinding code.
+ gp->sig = sig;
+ gp->sigcode0 = info->_code;
+ gp->sigcode1 = *(uintptr*)&info->_reason[0]; /* _addr */
+ gp->sigpc = r->r15;
+
+ // We arrange lr, and pc to pretend the panicking
+ // function calls sigpanic directly.
+ // Always save LR to stack so that panics in leaf
+ // functions are correctly handled. This smashes
+ // the stack frame but we're not going back there
+ // anyway.
+ r->r13 -= 4;
+ *(uint32 *)r->r13 = r->r14;
+ // Don't bother saving PC if it's zero, which is
+ // probably a call to a nil func: the old link register
+ // is more useful in the stack trace.
+ if(r->r15 != 0)
+ r->r14 = r->r15;
+ // In case we are panicking from external C code
+ r->r10 = (uintptr)gp;
+ r->r9 = (uintptr)m;
+ r->r15 = (uintptr)runtime·sigpanic;
+ return;
+ }
+
+ if(info->_code == SI_USER || (t->flags & SigNotify))
+ if(runtime·sigsend(sig))
+ return;
+ if(t->flags & SigKill)
+ runtime·exit(2);
+ if(!(t->flags & SigThrow))
+ return;
+
+Throw:
+ runtime·startpanic();
+
+ if(sig < 0 || sig >= NSIG)
+ runtime·printf("Signal %d\n", sig);
+ else
+ runtime·printf("%s\n", runtime·sigtab[sig].name);
+
+ runtime·printf("PC=%x\n", r->r15);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
+ runtime·printf("\n");
+
+ if(runtime·gotraceback()){
+ runtime·traceback((void*)r->r15, (void*)r->r13, (void*)r->r14, gp);
+ runtime·tracebackothers(gp);
+ runtime·printf("\n");
+ runtime·dumpregs(r);
+ }
+
+// breakpoint();
+ runtime·exit(2);
+}
+
+void
+runtime·signalstack(byte *p, int32 n)
+{
+ Sigaltstack st;
+
+ st.ss_sp = (uint8*)p;
+ st.ss_size = n;
+ st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
+ runtime·sigaltstack(&st, nil);
+}
+
+void
+runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
+{
+ Sigaction sa;
+
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ runtime·sigaction(i, nil, &sa);
+ if(sa._sa_u._sa_sigaction == SIG_IGN)
+ return;
+ }
+
+ runtime·memclr((byte*)&sa, sizeof sa);
+ sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
+ if(restart)
+ sa.sa_flags |= SA_RESTART;
+ sa.sa_mask[0] = ~0U;
+ sa.sa_mask[1] = ~0U;
+ sa.sa_mask[2] = ~0U;
+ sa.sa_mask[3] = ~0U;
+ if (fn == runtime·sighandler)
+ fn = (void*)runtime·sigtramp;
+ sa._sa_u._sa_sigaction = (void*)fn;
+ runtime·sigaction(i, &sa, nil);
+}
+
+void
+runtime·lwp_mcontext_init(McontextT *mc, void *stack, M *mp, G *gp, void (*fn)(void))
+{
+ mc->r15 = (uint32)runtime·lwp_tramp;
+ mc->r13 = (uint32)stack;
+ mc->r0 = (uint32)mp;
+ mc->r1 = (uint32)gp;
+ mc->r2 = (uint32)fn;
+}
+
+void
+runtime·checkgoarm(void)
+{
+ // TODO(minux)
+}
+
+#pragma textflag 7
+int64
+runtime·cputicks() {
+ // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1().
+ // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
+ // TODO: need more entropy to better seed fastrand1.
+ return runtime·nanotime();
+}
diff --git a/src/pkg/runtime/signal_openbsd_386.c b/src/pkg/runtime/signal_openbsd_386.c
index 39d829484..516797c8d 100644
--- a/src/pkg/runtime/signal_openbsd_386.c
+++ b/src/pkg/runtime/signal_openbsd_386.c
@@ -50,7 +50,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
t = &runtime·sigtab[sig];
if(info->si_code != SI_USER && (t->flags & SigPanic)) {
- if(gp == nil)
+ if(gp == nil || gp == m->g0)
goto Throw;
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
@@ -93,6 +93,10 @@ Throw:
runtime·printf("%s\n", runtime·sigtab[sig].name);
runtime·printf("PC=%X\n", r->sc_eip);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
@@ -109,9 +113,11 @@ runtime·signalstack(byte *p, int32 n)
{
Sigaltstack st;
- st.ss_sp = (int8*)p;
+ st.ss_sp = p;
st.ss_size = n;
st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
runtime·sigaltstack(&st, nil);
}
@@ -120,6 +126,15 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
{
Sigaction sa;
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ runtime·sigaction(i, nil, &sa);
+ if(sa.__sigaction_u.__sa_sigaction == SIG_IGN)
+ return;
+ }
+
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
if(restart)
diff --git a/src/pkg/runtime/signal_openbsd_amd64.c b/src/pkg/runtime/signal_openbsd_amd64.c
index 8b4f624e7..0d0db770b 100644
--- a/src/pkg/runtime/signal_openbsd_amd64.c
+++ b/src/pkg/runtime/signal_openbsd_amd64.c
@@ -59,7 +59,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
t = &runtime·sigtab[sig];
if(info->si_code != SI_USER && (t->flags & SigPanic)) {
- if(gp == nil)
+ if(gp == nil || gp == m->g0)
goto Throw;
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
@@ -70,8 +70,8 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
gp->sigcode1 = *(uintptr*)((byte*)info + 16); /* si_addr */
gp->sigpc = r->sc_rip;
- // Only push runtime·sigpanic if r->mc_rip != 0.
- // If r->mc_rip == 0, probably panicked because of a
+ // Only push runtime·sigpanic if r->sc_rip != 0.
+ // If r->sc_rip == 0, probably panicked because of a
// call to a nil func. Not pushing that onto sp will
// make the trace look like a call to runtime·sigpanic instead.
// (Otherwise the trace will end at runtime·sigpanic and we
@@ -102,6 +102,10 @@ Throw:
runtime·printf("%s\n", runtime·sigtab[sig].name);
runtime·printf("PC=%X\n", r->sc_rip);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
@@ -118,9 +122,11 @@ runtime·signalstack(byte *p, int32 n)
{
Sigaltstack st;
- st.ss_sp = (int8*)p;
+ st.ss_sp = p;
st.ss_size = n;
st.ss_flags = 0;
+ if(p == nil)
+ st.ss_flags = SS_DISABLE;
runtime·sigaltstack(&st, nil);
}
@@ -129,12 +135,21 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
{
Sigaction sa;
+ // If SIGHUP handler is SIG_IGN, assume running
+ // under nohup and do not set explicit handler.
+ if(i == SIGHUP) {
+ runtime·memclr((byte*)&sa, sizeof sa);
+ runtime·sigaction(i, nil, &sa);
+ if(sa.__sigaction_u.__sa_sigaction == SIG_IGN)
+ return;
+ }
+
runtime·memclr((byte*)&sa, sizeof sa);
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
if(restart)
sa.sa_flags |= SA_RESTART;
- sa.sa_mask = ~0ULL;
- if (fn == runtime·sighandler)
+ sa.sa_mask = ~0U;
+ if(fn == runtime·sighandler)
fn = (void*)runtime·sigtramp;
sa.__sigaction_u.__sa_sigaction = (void*)fn;
runtime·sigaction(i, &sa, nil);
diff --git a/src/pkg/runtime/signal_plan9_386.c b/src/pkg/runtime/signal_plan9_386.c
index d26688516..17bc11749 100644
--- a/src/pkg/runtime/signal_plan9_386.c
+++ b/src/pkg/runtime/signal_plan9_386.c
@@ -3,6 +3,107 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
+#include "defs_GOOS_GOARCH.h"
+#include "os_GOOS.h"
+#include "signals_GOOS.h"
+
+void
+runtime·dumpregs(Ureg *u)
+{
+ runtime·printf("ax %X\n", u->ax);
+ runtime·printf("bx %X\n", u->bx);
+ runtime·printf("cx %X\n", u->cx);
+ runtime·printf("dx %X\n", u->dx);
+ runtime·printf("di %X\n", u->di);
+ runtime·printf("si %X\n", u->si);
+ runtime·printf("bp %X\n", u->bp);
+ runtime·printf("sp %X\n", u->sp);
+ runtime·printf("pc %X\n", u->pc);
+ runtime·printf("flags %X\n", u->flags);
+ runtime·printf("cs %X\n", u->cs);
+ runtime·printf("fs %X\n", u->fs);
+ runtime·printf("gs %X\n", u->gs);
+}
+
+int32
+runtime·sighandler(void *v, int8 *s, G *gp)
+{
+ Ureg *ureg;
+ uintptr *sp;
+ SigTab *sig, *nsig;
+ int32 len, i;
+
+ if(!s)
+ return NCONT;
+
+ len = runtime·findnull((byte*)s);
+ if(len <= 4 || runtime·mcmp((byte*)s, (byte*)"sys:", 4) != 0)
+ return NDFLT;
+
+ nsig = nil;
+ sig = runtime·sigtab;
+ for(i=0; i < NSIG; i++) {
+ if(runtime·strstr((byte*)s, (byte*)sig->name)) {
+ nsig = sig;
+ break;
+ }
+ sig++;
+ }
+
+ if(nsig == nil)
+ return NDFLT;
+
+ ureg = v;
+ if(nsig->flags & SigPanic) {
+ if(gp == nil || m->notesig == 0)
+ goto Throw;
+
+ // Save error string from sigtramp's stack,
+ // into gsignal->sigcode0, so we can reliably
+ // access it from the panic routines.
+ if(len > ERRMAX)
+ len = ERRMAX;
+ runtime·memmove((void*)m->notesig, (void*)s, len);
+
+ gp->sig = i;
+ gp->sigpc = ureg->pc;
+
+ // Only push runtime·sigpanic if ureg->pc != 0.
+ // If ureg->pc == 0, probably panicked because of a
+ // call to a nil func. Not pushing that onto sp will
+ // make the trace look like a call to runtime·sigpanic instead.
+ // (Otherwise the trace will end at runtime·sigpanic and we
+ // won't get to see who faulted.)
+ if(ureg->pc != 0) {
+ sp = (uintptr*)ureg->sp;
+ *--sp = ureg->pc;
+ ureg->sp = (uint32)sp;
+ }
+ ureg->pc = (uintptr)runtime·sigpanic;
+ return NCONT;
+ }
+
+ if(!(nsig->flags & SigThrow))
+ return NDFLT;
+
+Throw:
+ runtime·startpanic();
+
+ runtime·printf("%s\n", s);
+ runtime·printf("PC=%X\n", ureg->pc);
+ runtime·printf("\n");
+
+ if(runtime·gotraceback()) {
+ runtime·traceback((void*)ureg->pc, (void*)ureg->sp, 0, gp);
+ runtime·tracebackothers(gp);
+ runtime·dumpregs(ureg);
+ }
+ runtime·goexitsall("");
+ runtime·exits(s);
+
+ return 0;
+}
+
void
runtime·sigenable(uint32 sig)
diff --git a/src/pkg/runtime/signal_plan9_amd64.c b/src/pkg/runtime/signal_plan9_amd64.c
new file mode 100644
index 000000000..e4f946abc
--- /dev/null
+++ b/src/pkg/runtime/signal_plan9_amd64.c
@@ -0,0 +1,127 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "runtime.h"
+#include "defs_GOOS_GOARCH.h"
+#include "os_GOOS.h"
+#include "signals_GOOS.h"
+
+void
+runtime·dumpregs(Ureg *u)
+{
+ runtime·printf("ax %X\n", u->ax);
+ runtime·printf("bx %X\n", u->bx);
+ runtime·printf("cx %X\n", u->cx);
+ runtime·printf("dx %X\n", u->dx);
+ runtime·printf("di %X\n", u->di);
+ runtime·printf("si %X\n", u->si);
+ runtime·printf("bp %X\n", u->bp);
+ runtime·printf("sp %X\n", u->sp);
+ runtime·printf("r8 %X\n", u->r8);
+ runtime·printf("r9 %X\n", u->r9);
+ runtime·printf("r10 %X\n", u->r10);
+ runtime·printf("r11 %X\n", u->r11);
+ runtime·printf("r12 %X\n", u->r12);
+ runtime·printf("r13 %X\n", u->r13);
+ runtime·printf("r14 %X\n", u->r14);
+ runtime·printf("r15 %X\n", u->r15);
+ runtime·printf("ip %X\n", u->ip);
+ runtime·printf("flags %X\n", u->flags);
+ runtime·printf("cs %X\n", (uint64)u->cs);
+ runtime·printf("fs %X\n", (uint64)u->fs);
+ runtime·printf("gs %X\n", (uint64)u->gs);
+}
+
+int32
+runtime·sighandler(void *v, int8 *s, G *gp)
+{
+ Ureg *ureg;
+ uintptr *sp;
+ SigTab *sig, *nsig;
+ int32 len, i;
+
+ if(!s)
+ return NCONT;
+
+ len = runtime·findnull((byte*)s);
+ if(len <= 4 || runtime·mcmp((byte*)s, (byte*)"sys:", 4) != 0)
+ return NDFLT;
+
+ nsig = nil;
+ sig = runtime·sigtab;
+ for(i=0; i < NSIG; i++) {
+ if(runtime·strstr((byte*)s, (byte*)sig->name)) {
+ nsig = sig;
+ break;
+ }
+ sig++;
+ }
+
+ if(nsig == nil)
+ return NDFLT;
+
+ ureg = v;
+ if(nsig->flags & SigPanic) {
+ if(gp == nil || m->notesig == 0)
+ goto Throw;
+
+ // Save error string from sigtramp's stack,
+ // into gsignal->sigcode0, so we can reliably
+ // access it from the panic routines.
+ if(len > ERRMAX)
+ len = ERRMAX;
+ runtime·memmove((void*)m->notesig, (void*)s, len);
+
+ gp->sig = i;
+ gp->sigpc = ureg->ip;
+
+ // Only push runtime·sigpanic if ureg->ip != 0.
+ // If ureg->ip == 0, probably panicked because of a
+ // call to a nil func. Not pushing that onto sp will
+ // make the trace look like a call to runtime·sigpanic instead.
+ // (Otherwise the trace will end at runtime·sigpanic and we
+ // won't get to see who faulted.)
+ if(ureg->ip != 0) {
+ sp = (uintptr*)ureg->sp;
+ *--sp = ureg->ip;
+ ureg->sp = (uint64)sp;
+ }
+ ureg->ip = (uintptr)runtime·sigpanic;
+ return NCONT;
+ }
+
+ if(!(nsig->flags & SigThrow))
+ return NDFLT;
+
+Throw:
+ runtime·startpanic();
+
+ runtime·printf("%s\n", s);
+ runtime·printf("PC=%X\n", ureg->ip);
+ runtime·printf("\n");
+
+ if(runtime·gotraceback()) {
+ runtime·traceback((void*)ureg->ip, (void*)ureg->sp, 0, gp);
+ runtime·tracebackothers(gp);
+ runtime·dumpregs(ureg);
+ }
+ runtime·goexitsall("");
+ runtime·exits(s);
+
+ return 0;
+}
+
+void
+runtime·sigenable(uint32 sig)
+{
+ USED(sig);
+}
+
+void
+runtime·resetcpuprofiler(int32 hz)
+{
+ // TODO: Enable profiling interrupts.
+
+ m->profilehz = hz;
+}
diff --git a/src/pkg/runtime/signal_windows_386.c b/src/pkg/runtime/signal_windows_386.c
index a248374db..d76d5bf4b 100644
--- a/src/pkg/runtime/signal_windows_386.c
+++ b/src/pkg/runtime/signal_windows_386.c
@@ -68,11 +68,15 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
info->ExceptionInformation[0], info->ExceptionInformation[1]);
runtime·printf("PC=%x\n", r->Eip);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
- runtime·traceback((void*)r->Eip, (void*)r->Esp, 0, m->curg);
- runtime·tracebackothers(m->curg);
+ runtime·traceback((void*)r->Eip, (void*)r->Esp, 0, gp);
+ runtime·tracebackothers(gp);
runtime·dumpregs(r);
}
diff --git a/src/pkg/runtime/signal_windows_amd64.c b/src/pkg/runtime/signal_windows_amd64.c
index 1cdf1cac4..3729aa57b 100644
--- a/src/pkg/runtime/signal_windows_amd64.c
+++ b/src/pkg/runtime/signal_windows_amd64.c
@@ -75,6 +75,10 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
info->ExceptionInformation[0], info->ExceptionInformation[1]);
runtime·printf("PC=%X\n", r->Rip);
+ if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
+ runtime·printf("signal arrived during cgo execution\n");
+ gp = m->lockedg;
+ }
runtime·printf("\n");
if(runtime·gotraceback()){
diff --git a/src/pkg/runtime/signals_linux.h b/src/pkg/runtime/signals_linux.h
index 345a6c5d1..9c3567007 100644
--- a/src/pkg/runtime/signals_linux.h
+++ b/src/pkg/runtime/signals_linux.h
@@ -42,7 +42,7 @@ SigTab runtime·sigtab[] = {
/* 30 */ N, "SIGPWR: power failure restart",
/* 31 */ N, "SIGSYS: bad system call",
/* 32 */ N, "signal 32",
- /* 33 */ N, "signal 33",
+ /* 33 */ 0, "signal 33", /* SIGSETXID; see issue 3871 */
/* 34 */ N, "signal 34",
/* 35 */ N, "signal 35",
/* 36 */ N, "signal 36",
diff --git a/src/pkg/runtime/signals_netbsd.h b/src/pkg/runtime/signals_netbsd.h
index 4d27e050d..7140de86f 100644
--- a/src/pkg/runtime/signals_netbsd.h
+++ b/src/pkg/runtime/signals_netbsd.h
@@ -9,16 +9,16 @@
#define D SigDefault
SigTab runtime·sigtab[] = {
- /* 0 */ 0, "SIGNONE: no trap",
- /* 1 */ N+K, "SIGHUP: terminal line hangup",
- /* 2 */ N+K, "SIGINT: interrupt",
- /* 3 */ N+T, "SIGQUIT: quit",
- /* 4 */ T, "SIGILL: illegal instruction",
- /* 5 */ T, "SIGTRAP: trace trap",
- /* 6 */ N+T, "SIGABRT: abort",
- /* 7 */ T, "SIGEMT: emulate instruction executed",
- /* 8 */ P, "SIGFPE: floating-point exception",
- /* 9 */ 0, "SIGKILL: kill",
+ /* 0 */ 0, "SIGNONE: no trap",
+ /* 1 */ N+K, "SIGHUP: terminal line hangup",
+ /* 2 */ N+K, "SIGINT: interrupt",
+ /* 3 */ N+T, "SIGQUIT: quit",
+ /* 4 */ T, "SIGILL: illegal instruction",
+ /* 5 */ T, "SIGTRAP: trace trap",
+ /* 6 */ N+T, "SIGABRT: abort",
+ /* 7 */ T, "SIGEMT: emulate instruction executed",
+ /* 8 */ P, "SIGFPE: floating-point exception",
+ /* 9 */ 0, "SIGKILL: kill",
/* 10 */ P, "SIGBUS: bus error",
/* 11 */ P, "SIGSEGV: segmentation violation",
/* 12 */ T, "SIGSYS: bad system call",
diff --git a/src/pkg/runtime/signals_openbsd.h b/src/pkg/runtime/signals_openbsd.h
index 4d27e050d..7140de86f 100644
--- a/src/pkg/runtime/signals_openbsd.h
+++ b/src/pkg/runtime/signals_openbsd.h
@@ -9,16 +9,16 @@
#define D SigDefault
SigTab runtime·sigtab[] = {
- /* 0 */ 0, "SIGNONE: no trap",
- /* 1 */ N+K, "SIGHUP: terminal line hangup",
- /* 2 */ N+K, "SIGINT: interrupt",
- /* 3 */ N+T, "SIGQUIT: quit",
- /* 4 */ T, "SIGILL: illegal instruction",
- /* 5 */ T, "SIGTRAP: trace trap",
- /* 6 */ N+T, "SIGABRT: abort",
- /* 7 */ T, "SIGEMT: emulate instruction executed",
- /* 8 */ P, "SIGFPE: floating-point exception",
- /* 9 */ 0, "SIGKILL: kill",
+ /* 0 */ 0, "SIGNONE: no trap",
+ /* 1 */ N+K, "SIGHUP: terminal line hangup",
+ /* 2 */ N+K, "SIGINT: interrupt",
+ /* 3 */ N+T, "SIGQUIT: quit",
+ /* 4 */ T, "SIGILL: illegal instruction",
+ /* 5 */ T, "SIGTRAP: trace trap",
+ /* 6 */ N+T, "SIGABRT: abort",
+ /* 7 */ T, "SIGEMT: emulate instruction executed",
+ /* 8 */ P, "SIGFPE: floating-point exception",
+ /* 9 */ 0, "SIGKILL: kill",
/* 10 */ P, "SIGBUS: bus error",
/* 11 */ P, "SIGSEGV: segmentation violation",
/* 12 */ T, "SIGSYS: bad system call",
diff --git a/src/pkg/runtime/signals_plan9.h b/src/pkg/runtime/signals_plan9.h
index 5df757613..0f1165e2a 100644
--- a/src/pkg/runtime/signals_plan9.h
+++ b/src/pkg/runtime/signals_plan9.h
@@ -1 +1,24 @@
-// nothing to see here
+#define N SigNotify
+#define T SigThrow
+#define P SigPanic
+
+SigTab runtime·sigtab[] = {
+ P, "sys: fp:",
+
+ // Go libraries expect to be able
+ // to recover from memory
+ // read/write errors, so we flag
+ // those as panics. All other traps
+ // are generally more serious and
+ // should immediately throw an
+ // exception.
+ P, "sys: trap: fault read addr",
+ P, "sys: trap: fault write addr",
+ T, "sys: trap:",
+
+ N, "sys: bad sys call",
+};
+
+#undef N
+#undef T
+#undef P
diff --git a/src/pkg/runtime/sigqueue.goc b/src/pkg/runtime/sigqueue.goc
index b49fdba86..ab5f312e4 100644
--- a/src/pkg/runtime/sigqueue.goc
+++ b/src/pkg/runtime/sigqueue.goc
@@ -5,36 +5,24 @@
// This file implements runtime support for signal handling.
//
// Most synchronization primitives are not available from
-// the signal handler (it cannot block and cannot use locks)
+// the signal handler (it cannot block, allocate memory, or use locks)
// so the handler communicates with a processing goroutine
// via struct sig, below.
//
-// Ownership for sig.Note passes back and forth between
-// the signal handler and the signal goroutine in rounds.
-// The initial state is that sig.note is cleared (setup by signal_enable).
-// At the beginning of each round, mask == 0.
-// The round goes through three stages:
-//
-// (In parallel)
-// 1a) One or more signals arrive and are handled
-// by sigsend using cas to set bits in sig.mask.
-// The handler that changes sig.mask from zero to non-zero
-// calls notewakeup(&sig).
-// 1b) Sigrecv calls notesleep(&sig) to wait for the wakeup.
-//
-// 2) Having received the wakeup, sigrecv knows that sigsend
-// will not send another wakeup, so it can noteclear(&sig)
-// to prepare for the next round. (Sigsend may still be adding
-// signals to sig.mask at this point, which is fine.)
-//
-// 3) Sigrecv uses cas to grab the current sig.mask and zero it,
-// triggering the next round.
-//
-// The signal handler takes ownership of the note by atomically
-// changing mask from a zero to non-zero value. It gives up
-// ownership by calling notewakeup. The signal goroutine takes
-// ownership by returning from notesleep (caused by the notewakeup)
-// and gives up ownership by clearing mask.
+// sigsend() is called by the signal handler to queue a new signal.
+// signal_recv() is called by the Go program to receive a newly queued signal.
+// Synchronization between sigsend() and signal_recv() is based on the sig.state
+// variable. It can be in 3 states: 0, HASWAITER and HASSIGNAL.
+// HASWAITER means that signal_recv() is blocked on sig.Note and there are no
+// new pending signals.
+// HASSIGNAL means that sig.mask *may* contain new pending signals,
+// signal_recv() can't be blocked in this state.
+// 0 means that there are no new pending signals and signal_recv() is not blocked.
+// Transitions between states are done atomically with CAS.
+// When signal_recv() is unblocked, it resets sig.Note and rechecks sig.mask.
+// If several sigsend()'s and signal_recv() execute concurrently, it can lead to
+// unnecessary rechecks of sig.mask, but must not lead to missed signals
+// nor deadlocks.
package runtime
#include "runtime.h"
@@ -45,15 +33,20 @@ static struct {
Note;
uint32 mask[(NSIG+31)/32];
uint32 wanted[(NSIG+31)/32];
- uint32 kick;
+ uint32 state;
bool inuse;
} sig;
+enum {
+ HASWAITER = 1,
+ HASSIGNAL = 2,
+};
+
// Called from sighandler to send a signal back out of the signal handling thread.
bool
runtime·sigsend(int32 s)
{
- uint32 bit, mask;
+ uint32 bit, mask, old, new;
if(!sig.inuse || s < 0 || s >= 32*nelem(sig.wanted) || !(sig.wanted[s/32]&(1U<<(s&31))))
return false;
@@ -65,8 +58,20 @@ runtime·sigsend(int32 s)
if(runtime·cas(&sig.mask[s/32], mask, mask|bit)) {
// Added to queue.
// Only send a wakeup if the receiver needs a kick.
- if(runtime·cas(&sig.kick, 1, 0))
- runtime·notewakeup(&sig);
+ for(;;) {
+ old = runtime·atomicload(&sig.state);
+ if(old == HASSIGNAL)
+ break;
+ if(old == HASWAITER)
+ new = 0;
+ else // if(old == 0)
+ new = HASSIGNAL;
+ if(runtime·cas(&sig.state, old, new)) {
+ if (old == HASWAITER)
+ runtime·notewakeup(&sig);
+ break;
+ }
+ }
break;
}
}
@@ -77,7 +82,7 @@ runtime·sigsend(int32 s)
// Must only be called from a single goroutine at a time.
func signal_recv() (m uint32) {
static uint32 recv[nelem(sig.mask)];
- int32 i, more;
+ uint32 i, old, new;
for(;;) {
// Serve from local copy if there are bits left.
@@ -89,15 +94,27 @@ func signal_recv() (m uint32) {
}
}
- // Get a new local copy.
- // Ask for a kick if more signals come in
- // during or after our check (before the sleep).
- if(sig.kick == 0) {
- runtime·noteclear(&sig);
- runtime·cas(&sig.kick, 0, 1);
+ // Check and update sig.state.
+ for(;;) {
+ old = runtime·atomicload(&sig.state);
+ if(old == HASWAITER)
+ runtime·throw("inconsistent state in signal_recv");
+ if(old == HASSIGNAL)
+ new = 0;
+ else // if(old == 0)
+ new = HASWAITER;
+ if(runtime·cas(&sig.state, old, new)) {
+ if (new == HASWAITER) {
+ runtime·entersyscallblock();
+ runtime·notesleep(&sig);
+ runtime·exitsyscall();
+ runtime·noteclear(&sig);
+ }
+ break;
+ }
}
- more = 0;
+ // Get a new local copy.
for(i=0; i<nelem(sig.mask); i++) {
for(;;) {
m = sig.mask[i];
@@ -105,16 +122,7 @@ func signal_recv() (m uint32) {
break;
}
recv[i] = m;
- if(m != 0)
- more = 1;
}
- if(more)
- continue;
-
- // Sleep waiting for more.
- runtime·entersyscall();
- runtime·notesleep(&sig);
- runtime·exitsyscall();
}
done:;
diff --git a/src/pkg/runtime/slice.c b/src/pkg/runtime/slice.c
index e5726c93b..354c54c86 100644
--- a/src/pkg/runtime/slice.c
+++ b/src/pkg/runtime/slice.c
@@ -5,22 +5,30 @@
#include "runtime.h"
#include "arch_GOARCH.h"
#include "type.h"
+#include "typekind.h"
#include "malloc.h"
+#include "race.h"
-static int32 debug = 0;
+static bool debug = 0;
-static void makeslice1(SliceType*, int32, int32, Slice*);
-static void growslice1(SliceType*, Slice, int32, Slice *);
- void runtime·copy(Slice to, Slice fm, uintptr width, int32 ret);
+static void makeslice1(SliceType*, intgo, intgo, Slice*);
+static void growslice1(SliceType*, Slice, intgo, Slice *);
+ void runtime·copy(Slice to, Slice fm, uintptr width, intgo ret);
// see also unsafe·NewArray
// makeslice(typ *Type, len, cap int64) (ary []any);
void
runtime·makeslice(SliceType *t, int64 len, int64 cap, Slice ret)
{
- if(len < 0 || (int32)len != len)
+ // NOTE: The len > MaxMem/elemsize check here is not strictly necessary,
+ // but it produces a 'len out of range' error instead of a 'cap out of range' error
+ // when someone does make([]T, bignumber). 'cap out of range' is true too,
+ // but since the cap is only being supplied implicitly, saying len is clearer.
+ // See issue 4085.
+ if(len < 0 || (intgo)len != len || t->elem->size > 0 && len > MaxMem / t->elem->size)
runtime·panicstring("makeslice: len out of range");
- if(cap < len || (int32)cap != cap || t->elem->size > 0 && cap > ((uintptr)-1) / t->elem->size)
+
+ if(cap < len || (intgo)cap != cap || t->elem->size > 0 && cap > MaxMem / t->elem->size)
runtime·panicstring("makeslice: cap out of range");
makeslice1(t, len, cap, &ret);
@@ -35,10 +43,10 @@ runtime·makeslice(SliceType *t, int64 len, int64 cap, Slice ret)
// Dummy word to use as base pointer for make([]T, 0).
// Since you cannot take the address of such a slice,
// you can't tell that they all have the same base pointer.
-static uintptr zerobase;
+uintptr runtime·zerobase;
static void
-makeslice1(SliceType *t, int32 len, int32 cap, Slice *ret)
+makeslice1(SliceType *t, intgo len, intgo cap, Slice *ret)
{
uintptr size;
@@ -47,22 +55,34 @@ makeslice1(SliceType *t, int32 len, int32 cap, Slice *ret)
ret->len = len;
ret->cap = cap;
- if(cap == 0)
- ret->array = (byte*)&zerobase;
+ if(size == 0)
+ ret->array = (byte*)&runtime·zerobase;
else if((t->elem->kind&KindNoPointers))
ret->array = runtime·mallocgc(size, FlagNoPointers, 1, 1);
- else
- ret->array = runtime·mal(size);
+ else {
+ ret->array = runtime·mallocgc(size, 0, 1, 1);
+
+ if(UseSpanType) {
+ if(false) {
+ runtime·printf("new slice [%D]%S: %p\n", (int64)cap, *t->elem->string, ret->array);
+ }
+ runtime·settype(ret->array, (uintptr)t->elem | TypeInfo_Array);
+ }
+ }
}
// appendslice(type *Type, x, y, []T) []T
+#pragma textflag 7
void
runtime·appendslice(SliceType *t, Slice x, Slice y, Slice ret)
{
- int32 m;
+ intgo m;
uintptr w;
+ void *pc;
+ uint8 *p, *q;
m = x.len+y.len;
+ w = t->elem->size;
if(m < x.len)
runtime·throw("append: slice overflow");
@@ -72,30 +92,83 @@ runtime·appendslice(SliceType *t, Slice x, Slice y, Slice ret)
else
ret = x;
- w = t->elem->size;
- runtime·memmove(ret.array + ret.len*w, y.array, y.len*w);
+ if(raceenabled) {
+ // Don't mark read/writes on the newly allocated slice.
+ pc = runtime·getcallerpc(&t);
+ // read x[:len]
+ if(m > x.cap)
+ runtime·racereadrangepc(x.array, x.len*w, w, pc, runtime·appendslice);
+ // read y
+ runtime·racereadrangepc(y.array, y.len*w, w, pc, runtime·appendslice);
+ // write x[len(x):len(x)+len(y)]
+ if(m <= x.cap)
+ runtime·racewriterangepc(ret.array+ret.len*w, y.len*w, w, pc, runtime·appendslice);
+ }
+
+ // A very common case is appending bytes. Small appends can avoid the overhead of memmove.
+ // We can generalize a bit here, and just pick small-sized appends.
+ p = ret.array+ret.len*w;
+ q = y.array;
+ w *= y.len;
+ if(w <= appendCrossover) {
+ if(p <= q || w <= p-q) // No overlap.
+ while(w-- > 0)
+ *p++ = *q++;
+ else {
+ p += w;
+ q += w;
+ while(w-- > 0)
+ *--p = *--q;
+ }
+ } else {
+ runtime·memmove(p, q, w);
+ }
ret.len += y.len;
FLUSH(&ret);
}
// appendstr([]byte, string) []byte
+#pragma textflag 7
void
runtime·appendstr(SliceType *t, Slice x, String y, Slice ret)
{
- int32 m;
+ intgo m;
+ void *pc;
+ uintptr w;
+ uint8 *p, *q;
m = x.len+y.len;
if(m < x.len)
- runtime·throw("append: slice overflow");
+ runtime·throw("append: string overflow");
if(m > x.cap)
growslice1(t, x, m, &ret);
else
ret = x;
- runtime·memmove(ret.array + ret.len, y.str, y.len);
+ if(raceenabled) {
+ // Don't mark read/writes on the newly allocated slice.
+ pc = runtime·getcallerpc(&t);
+ // read x[:len]
+ if(m > x.cap)
+ runtime·racereadrangepc(x.array, x.len, 1, pc, runtime·appendstr);
+ // write x[len(x):len(x)+len(y)]
+ if(m <= x.cap)
+ runtime·racewriterangepc(ret.array+ret.len, y.len, 1, pc, runtime·appendstr);
+ }
+
+ // Small appends can avoid the overhead of memmove.
+ w = y.len;
+ p = ret.array+ret.len;
+ q = y.str;
+ if(w <= appendCrossover) {
+ while(w-- > 0)
+ *p++ = *q++;
+ } else {
+ runtime·memmove(p, q, w);
+ }
ret.len += y.len;
FLUSH(&ret);
}
@@ -106,15 +179,21 @@ void
runtime·growslice(SliceType *t, Slice old, int64 n, Slice ret)
{
int64 cap;
+ void *pc;
if(n < 1)
runtime·panicstring("growslice: invalid n");
cap = old.cap + n;
- if((int32)cap != cap || cap > ((uintptr)-1) / t->elem->size)
+ if((intgo)cap != cap || cap < old.cap || (t->elem->size > 0 && cap > MaxMem/t->elem->size))
runtime·panicstring("growslice: cap out of range");
+ if(raceenabled) {
+ pc = runtime·getcallerpc(&t);
+ runtime·racereadrangepc(old.array, old.len*t->elem->size, t->elem->size, pc, runtime·growslice);
+ }
+
growslice1(t, old, cap, &ret);
FLUSH(&ret);
@@ -128,12 +207,17 @@ runtime·growslice(SliceType *t, Slice old, int64 n, Slice ret)
}
static void
-growslice1(SliceType *t, Slice x, int32 newcap, Slice *ret)
+growslice1(SliceType *t, Slice x, intgo newcap, Slice *ret)
{
- int32 m;
+ intgo m;
m = x.cap;
- if(m == 0)
+
+ // Using newcap directly for m+m < newcap handles
+ // both the case where m == 0 and also the case where
+ // m+m/4 wraps around, in which case the loop
+ // below might never terminate.
+ if(m+m < newcap)
m = newcap;
else {
do {
@@ -147,153 +231,13 @@ growslice1(SliceType *t, Slice x, int32 newcap, Slice *ret)
runtime·memmove(ret->array, x.array, ret->len * t->elem->size);
}
-// sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any);
-void
-runtime·sliceslice(Slice old, uint64 lb, uint64 hb, uint64 width, Slice ret)
-{
- if(hb > old.cap || lb > hb) {
- if(debug) {
- runtime·prints("runtime.sliceslice: old=");
- runtime·printslice(old);
- runtime·prints("; lb=");
- runtime·printint(lb);
- runtime·prints("; hb=");
- runtime·printint(hb);
- runtime·prints("; width=");
- runtime·printint(width);
- runtime·prints("\n");
-
- runtime·prints("oldarray: nel=");
- runtime·printint(old.len);
- runtime·prints("; cap=");
- runtime·printint(old.cap);
- runtime·prints("\n");
- }
- runtime·panicslice();
- }
-
- // new array is inside old array
- ret.len = hb - lb;
- ret.cap = old.cap - lb;
- ret.array = old.array + lb*width;
-
- FLUSH(&ret);
-
- if(debug) {
- runtime·prints("runtime.sliceslice: old=");
- runtime·printslice(old);
- runtime·prints("; lb=");
- runtime·printint(lb);
- runtime·prints("; hb=");
- runtime·printint(hb);
- runtime·prints("; width=");
- runtime·printint(width);
- runtime·prints("; ret=");
- runtime·printslice(ret);
- runtime·prints("\n");
- }
-}
-
-// sliceslice1(old []any, lb uint64, width uint64) (ary []any);
-void
-runtime·sliceslice1(Slice old, uint64 lb, uint64 width, Slice ret)
-{
- if(lb > old.len) {
- if(debug) {
- runtime·prints("runtime.sliceslice: old=");
- runtime·printslice(old);
- runtime·prints("; lb=");
- runtime·printint(lb);
- runtime·prints("; width=");
- runtime·printint(width);
- runtime·prints("\n");
-
- runtime·prints("oldarray: nel=");
- runtime·printint(old.len);
- runtime·prints("; cap=");
- runtime·printint(old.cap);
- runtime·prints("\n");
- }
- runtime·panicslice();
- }
-
- // new array is inside old array
- ret.len = old.len - lb;
- ret.cap = old.cap - lb;
- ret.array = old.array + lb*width;
-
- FLUSH(&ret);
-
- if(debug) {
- runtime·prints("runtime.sliceslice: old=");
- runtime·printslice(old);
- runtime·prints("; lb=");
- runtime·printint(lb);
- runtime·prints("; width=");
- runtime·printint(width);
- runtime·prints("; ret=");
- runtime·printslice(ret);
- runtime·prints("\n");
- }
-}
-
-// slicearray(old *any, nel uint64, lb uint64, hb uint64, width uint64) (ary []any);
+// copy(to any, fr any, wid uintptr) int
+#pragma textflag 7
void
-runtime·slicearray(byte* old, uint64 nel, uint64 lb, uint64 hb, uint64 width, Slice ret)
+runtime·copy(Slice to, Slice fm, uintptr width, intgo ret)
{
- if(nel > 0 && old == nil) {
- // crash if old == nil.
- // could give a better message
- // but this is consistent with all the in-line checks
- // that the compiler inserts for other uses.
- *old = 0;
- }
-
- if(hb > nel || lb > hb) {
- if(debug) {
- runtime·prints("runtime.slicearray: old=");
- runtime·printpointer(old);
- runtime·prints("; nel=");
- runtime·printint(nel);
- runtime·prints("; lb=");
- runtime·printint(lb);
- runtime·prints("; hb=");
- runtime·printint(hb);
- runtime·prints("; width=");
- runtime·printint(width);
- runtime·prints("\n");
- }
- runtime·panicslice();
- }
-
- // new array is inside old array
- ret.len = hb-lb;
- ret.cap = nel-lb;
- ret.array = old + lb*width;
-
- FLUSH(&ret);
-
- if(debug) {
- runtime·prints("runtime.slicearray: old=");
- runtime·printpointer(old);
- runtime·prints("; nel=");
- runtime·printint(nel);
- runtime·prints("; lb=");
- runtime·printint(lb);
- runtime·prints("; hb=");
- runtime·printint(hb);
- runtime·prints("; width=");
- runtime·printint(width);
- runtime·prints("; ret=");
- runtime·printslice(ret);
- runtime·prints("\n");
- }
-}
+ void *pc;
-// copy(to any, fr any, wid uint32) int
-void
-runtime·copy(Slice to, Slice fm, uintptr width, int32 ret)
-{
if(fm.len == 0 || to.len == 0 || width == 0) {
ret = 0;
goto out;
@@ -303,6 +247,12 @@ runtime·copy(Slice to, Slice fm, uintptr width, int32 ret)
if(to.len < ret)
ret = to.len;
+ if(raceenabled) {
+ pc = runtime·getcallerpc(&to);
+ runtime·racewriterangepc(to.array, ret*width, width, pc, runtime·copy);
+ runtime·racereadrangepc(fm.array, ret*width, width, pc, runtime·copy);
+ }
+
if(ret == 1 && width == 1) { // common case worth about 2x to do here
*to.array = *fm.array; // known to be a byte pointer
} else {
@@ -325,9 +275,12 @@ out:
}
}
+#pragma textflag 7
void
-runtime·slicestringcopy(Slice to, String fm, int32 ret)
+runtime·slicestringcopy(Slice to, String fm, intgo ret)
{
+ void *pc;
+
if(fm.len == 0 || to.len == 0) {
ret = 0;
goto out;
@@ -337,6 +290,11 @@ runtime·slicestringcopy(Slice to, String fm, int32 ret)
if(to.len < ret)
ret = to.len;
+ if(raceenabled) {
+ pc = runtime·getcallerpc(&to);
+ runtime·racewriterangepc(to.array, ret, 1, pc, runtime·slicestringcopy);
+ }
+
runtime·memmove(to.array, fm.str, ret);
out:
diff --git a/src/pkg/runtime/softfloat_arm.c b/src/pkg/runtime/softfloat_arm.c
index bd73cb15b..9a5440630 100644
--- a/src/pkg/runtime/softfloat_arm.c
+++ b/src/pkg/runtime/softfloat_arm.c
@@ -420,6 +420,23 @@ stage3: // regd, regm are 4bit variables
regd, regm, m->freghi[regd], m->freglo[regd]);
break;
+ case 0xeeb00bc0: // D[regd] = abs D[regm]
+ m->freglo[regd] = m->freglo[regm];
+ m->freghi[regd] = m->freghi[regm] & ((1<<31)-1);
+
+ if(trace)
+ runtime·printf("*** D[%d] = abs D[%d] %x-%x\n",
+ regd, regm, m->freghi[regd], m->freglo[regd]);
+ break;
+
+ case 0xeeb00ac0: // F[regd] = abs F[regm]
+ m->freglo[regd] = m->freglo[regm] & ((1<<31)-1);
+
+ if(trace)
+ runtime·printf("*** F[%d] = abs F[%d] %x\n",
+ regd, regm, m->freglo[regd]);
+ break;
+
case 0xeeb40bc0: // D[regd] :: D[regm] (CMPD)
runtime·fcmp64c(getd(regd), getd(regm), &cmp, &nan);
m->fflag = fstatus(nan, cmp);
diff --git a/src/pkg/runtime/stack.c b/src/pkg/runtime/stack.c
new file mode 100644
index 000000000..e9a35672f
--- /dev/null
+++ b/src/pkg/runtime/stack.c
@@ -0,0 +1,282 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "runtime.h"
+#include "arch_GOARCH.h"
+#include "malloc.h"
+#include "stack.h"
+
+typedef struct StackCacheNode StackCacheNode;
+struct StackCacheNode
+{
+ StackCacheNode *next;
+ void* batch[StackCacheBatch-1];
+};
+
+static StackCacheNode *stackcache;
+static Lock stackcachemu;
+
+// stackcacherefill/stackcacherelease implement a global cache of stack segments.
+// The cache is required to prevent unlimited growth of per-thread caches.
+static void
+stackcacherefill(void)
+{
+ StackCacheNode *n;
+ int32 i, pos;
+
+ runtime·lock(&stackcachemu);
+ n = stackcache;
+ if(n)
+ stackcache = n->next;
+ runtime·unlock(&stackcachemu);
+ if(n == nil) {
+ n = (StackCacheNode*)runtime·SysAlloc(FixedStack*StackCacheBatch);
+ if(n == nil)
+ runtime·throw("out of memory (stackcacherefill)");
+ runtime·xadd64(&mstats.stacks_sys, FixedStack*StackCacheBatch);
+ for(i = 0; i < StackCacheBatch-1; i++)
+ n->batch[i] = (byte*)n + (i+1)*FixedStack;
+ }
+ pos = m->stackcachepos;
+ for(i = 0; i < StackCacheBatch-1; i++) {
+ m->stackcache[pos] = n->batch[i];
+ pos = (pos + 1) % StackCacheSize;
+ }
+ m->stackcache[pos] = n;
+ pos = (pos + 1) % StackCacheSize;
+ m->stackcachepos = pos;
+ m->stackcachecnt += StackCacheBatch;
+}
+
+static void
+stackcacherelease(void)
+{
+ StackCacheNode *n;
+ uint32 i, pos;
+
+ pos = (m->stackcachepos - m->stackcachecnt) % StackCacheSize;
+ n = (StackCacheNode*)m->stackcache[pos];
+ pos = (pos + 1) % StackCacheSize;
+ for(i = 0; i < StackCacheBatch-1; i++) {
+ n->batch[i] = m->stackcache[pos];
+ pos = (pos + 1) % StackCacheSize;
+ }
+ m->stackcachecnt -= StackCacheBatch;
+ runtime·lock(&stackcachemu);
+ n->next = stackcache;
+ stackcache = n;
+ runtime·unlock(&stackcachemu);
+}
+
+void*
+runtime·stackalloc(uint32 n)
+{
+ uint32 pos;
+ void *v;
+
+ // Stackalloc must be called on scheduler stack, so that we
+ // never try to grow the stack during the code that stackalloc runs.
+ // Doing so would cause a deadlock (issue 1547).
+ if(g != m->g0)
+ runtime·throw("stackalloc not on scheduler stack");
+
+ // Stack allocator uses malloc/free most of the time,
+ // but if we're in the middle of malloc and need stack,
+ // we have to do something else to avoid deadlock.
+ // In that case, we fall back on a fixed-size free-list
+ // allocator, assuming that inside malloc all the stack
+ // frames are small, so that all the stack allocations
+ // will be a single size, the minimum (right now, 5k).
+ if(n == FixedStack || m->mallocing || m->gcing) {
+ if(n != FixedStack) {
+ runtime·printf("stackalloc: in malloc, size=%d want %d\n", FixedStack, n);
+ runtime·throw("stackalloc");
+ }
+ if(m->stackcachecnt == 0)
+ stackcacherefill();
+ pos = m->stackcachepos;
+ pos = (pos - 1) % StackCacheSize;
+ v = m->stackcache[pos];
+ m->stackcachepos = pos;
+ m->stackcachecnt--;
+ m->stackinuse++;
+ return v;
+ }
+ return runtime·mallocgc(n, FlagNoProfiling|FlagNoGC, 0, 0);
+}
+
+void
+runtime·stackfree(void *v, uintptr n)
+{
+ uint32 pos;
+
+ if(n == FixedStack || m->mallocing || m->gcing) {
+ if(m->stackcachecnt == StackCacheSize)
+ stackcacherelease();
+ pos = m->stackcachepos;
+ m->stackcache[pos] = v;
+ m->stackcachepos = (pos + 1) % StackCacheSize;
+ m->stackcachecnt++;
+ m->stackinuse--;
+ return;
+ }
+ runtime·free(v);
+}
+
+// Called from runtime·lessstack when returning from a function which
+// allocated a new stack segment. The function's return value is in
+// m->cret.
+void
+runtime·oldstack(void)
+{
+ Stktop *top;
+ Gobuf label;
+ uint32 argsize;
+ uintptr cret;
+ byte *sp, *old;
+ uintptr *src, *dst, *dstend;
+ G *gp;
+ int64 goid;
+
+//printf("oldstack m->cret=%p\n", m->cret);
+
+ gp = m->curg;
+ top = (Stktop*)gp->stackbase;
+ old = (byte*)gp->stackguard - StackGuard;
+ sp = (byte*)top;
+ argsize = top->argsize;
+ if(argsize > 0) {
+ sp -= argsize;
+ dst = (uintptr*)top->argp;
+ dstend = dst + argsize/sizeof(*dst);
+ src = (uintptr*)sp;
+ while(dst < dstend)
+ *dst++ = *src++;
+ }
+ goid = top->gobuf.g->goid; // fault if g is bad, before gogo
+ USED(goid);
+
+ label = top->gobuf;
+ gp->stackbase = (uintptr)top->stackbase;
+ gp->stackguard = (uintptr)top->stackguard;
+ if(top->free != 0)
+ runtime·stackfree(old, top->free);
+
+ cret = m->cret;
+ m->cret = 0; // drop reference
+ runtime·gogo(&label, cret);
+}
+
+// Called from reflect·call or from runtime·morestack when a new
+// stack segment is needed. Allocate a new stack big enough for
+// m->moreframesize bytes, copy m->moreargsize bytes to the new frame,
+// and then act as though runtime·lessstack called the function at
+// m->morepc.
+void
+runtime·newstack(void)
+{
+ int32 framesize, minalloc, argsize;
+ Stktop *top;
+ byte *stk, *sp;
+ uintptr *src, *dst, *dstend;
+ G *gp;
+ Gobuf label;
+ bool reflectcall;
+ uintptr free;
+
+ framesize = m->moreframesize;
+ argsize = m->moreargsize;
+ gp = m->curg;
+
+ if(m->morebuf.sp < gp->stackguard - StackGuard) {
+ runtime·printf("runtime: split stack overflow: %p < %p\n", m->morebuf.sp, gp->stackguard - StackGuard);
+ runtime·throw("runtime: split stack overflow");
+ }
+ if(argsize % sizeof(uintptr) != 0) {
+ runtime·printf("runtime: stack split with misaligned argsize %d\n", argsize);
+ runtime·throw("runtime: stack split argsize");
+ }
+
+ minalloc = 0;
+ reflectcall = framesize==1;
+ if(reflectcall) {
+ framesize = 0;
+ // moreframesize_minalloc is only set in runtime·gc(),
+ // that calls newstack via reflect·call().
+ minalloc = m->moreframesize_minalloc;
+ m->moreframesize_minalloc = 0;
+ if(framesize < minalloc)
+ framesize = minalloc;
+ }
+
+ if(reflectcall && minalloc == 0 && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > gp->stackguard) {
+ // special case: called from reflect.call (framesize==1)
+ // to call code with an arbitrary argument size,
+ // and we have enough space on the current stack.
+ // the new Stktop* is necessary to unwind, but
+ // we don't need to create a new segment.
+ top = (Stktop*)(m->morebuf.sp - sizeof(*top));
+ stk = (byte*)gp->stackguard - StackGuard;
+ free = 0;
+ } else {
+ // allocate new segment.
+ framesize += argsize;
+ framesize += StackExtra; // room for more functions, Stktop.
+ if(framesize < StackMin)
+ framesize = StackMin;
+ framesize += StackSystem;
+ stk = runtime·stackalloc(framesize);
+ top = (Stktop*)(stk+framesize-sizeof(*top));
+ free = framesize;
+ }
+
+ if(0) {
+ runtime·printf("newstack framesize=%d argsize=%d morepc=%p moreargp=%p gobuf=%p, %p top=%p old=%p\n",
+ framesize, argsize, m->morepc, m->moreargp, m->morebuf.pc, m->morebuf.sp, top, gp->stackbase);
+ }
+
+ top->stackbase = (byte*)gp->stackbase;
+ top->stackguard = (byte*)gp->stackguard;
+ top->gobuf = m->morebuf;
+ top->argp = m->moreargp;
+ top->argsize = argsize;
+ top->free = free;
+ m->moreargp = nil;
+ m->morebuf.pc = nil;
+ m->morebuf.sp = (uintptr)nil;
+
+ // copy flag from panic
+ top->panic = gp->ispanic;
+ gp->ispanic = false;
+
+ gp->stackbase = (uintptr)top;
+ gp->stackguard = (uintptr)stk + StackGuard;
+
+ sp = (byte*)top;
+ if(argsize > 0) {
+ sp -= argsize;
+ dst = (uintptr*)sp;
+ dstend = dst + argsize/sizeof(*dst);
+ src = (uintptr*)top->argp;
+ while(dst < dstend)
+ *dst++ = *src++;
+ }
+ if(thechar == '5') {
+ // caller would have saved its LR below args.
+ sp -= sizeof(void*);
+ *(void**)sp = nil;
+ }
+
+ // Continue as if lessstack had just called m->morepc
+ // (the PC that decided to grow the stack).
+ label.sp = (uintptr)sp;
+ label.pc = (byte*)runtime·lessstack;
+ label.g = m->curg;
+ if(reflectcall)
+ runtime·gogocallfn(&label, (FuncVal*)m->morepc);
+ else
+ runtime·gogocall(&label, m->morepc, m->cret);
+
+ *(int32*)345 = 123; // never return
+}
diff --git a/src/pkg/runtime/stack.h b/src/pkg/runtime/stack.h
index d42385d6c..06b0c568c 100644
--- a/src/pkg/runtime/stack.h
+++ b/src/pkg/runtime/stack.h
@@ -55,13 +55,19 @@ functions to make sure that this limit cannot be violated.
enum {
// StackSystem is a number of additional bytes to add
// to each stack below the usual guard area for OS-specific
- // purposes like signal handling. Used on Windows because
- // it does not use a separate stack.
+ // purposes like signal handling. Used on Windows and on
+ // Plan 9 because they do not use a separate stack.
#ifdef GOOS_windows
StackSystem = 512 * sizeof(uintptr),
#else
+#ifdef GOOS_plan9
+ // The size of the note handler frame varies among architectures,
+ // but 512 bytes should be enough for every implementation.
+ StackSystem = 512,
+#else
StackSystem = 0,
-#endif
+#endif // Plan 9
+#endif // Windows
// The amount of extra stack to allocate beyond the size
// needed for the single frame that triggered the split.
diff --git a/src/pkg/runtime/stack_test.go b/src/pkg/runtime/stack_test.go
index 01c6b6e2f..759f7c46e 100644
--- a/src/pkg/runtime/stack_test.go
+++ b/src/pkg/runtime/stack_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Test stack split logic by calling functions of every frame size
+// Test stack split logic by calling functions of every frame size
// from near 0 up to and beyond the default segment size (4k).
// Each of those functions reports its SP + stack limit, and then
// the test (the caller) checks that those make sense. By not
@@ -34,6 +34,7 @@ package runtime_test
import (
. "runtime"
"testing"
+ "time"
"unsafe"
)
@@ -1526,3 +1527,51 @@ func stack4988() (uintptr, uintptr) { var buf [4988]byte; use(buf[:]); return St
func stack4992() (uintptr, uintptr) { var buf [4992]byte; use(buf[:]); return Stackguard() }
func stack4996() (uintptr, uintptr) { var buf [4996]byte; use(buf[:]); return Stackguard() }
func stack5000() (uintptr, uintptr) { var buf [5000]byte; use(buf[:]); return Stackguard() }
+
+// TestStackMem measures per-thread stack segment cache behavior.
+// The test consumed up to 500MB in the past.
+func TestStackMem(t *testing.T) {
+ const (
+ BatchSize = 32
+ BatchCount = 512
+ ArraySize = 1024
+ RecursionDepth = 128
+ )
+ if testing.Short() {
+ return
+ }
+ defer GOMAXPROCS(GOMAXPROCS(BatchSize))
+ s0 := new(MemStats)
+ ReadMemStats(s0)
+ for b := 0; b < BatchCount; b++ {
+ c := make(chan bool, BatchSize)
+ for i := 0; i < BatchSize; i++ {
+ go func() {
+ var f func(k int, a [ArraySize]byte)
+ f = func(k int, a [ArraySize]byte) {
+ if k == 0 {
+ time.Sleep(time.Millisecond)
+ return
+ }
+ f(k-1, a)
+ }
+ f(RecursionDepth, [ArraySize]byte{})
+ c <- true
+ }()
+ }
+ for i := 0; i < BatchSize; i++ {
+ <-c
+ }
+ }
+ s1 := new(MemStats)
+ ReadMemStats(s1)
+ consumed := s1.StackSys - s0.StackSys
+ t.Logf("Consumed %vMB for stack mem", consumed>>20)
+ estimate := uint64(8 * BatchSize * ArraySize * RecursionDepth) // 8 is to reduce flakiness.
+ if consumed > estimate {
+ t.Fatalf("Stack mem: want %v, got %v", estimate, consumed)
+ }
+ if s1.StackInuse > 4<<20 {
+ t.Fatalf("Stack inuse: want %v, got %v", 4<<20, s1.StackInuse)
+ }
+}
diff --git a/src/pkg/runtime/string.goc b/src/pkg/runtime/string.goc
index 7e1f8a1e8..c0d3f2bde 100644
--- a/src/pkg/runtime/string.goc
+++ b/src/pkg/runtime/string.goc
@@ -6,6 +6,7 @@ package runtime
#include "runtime.h"
#include "arch_GOARCH.h"
#include "malloc.h"
+#include "race.h"
String runtime·emptystring;
@@ -33,10 +34,10 @@ runtime·findnullw(uint16 *s)
return l;
}
-uint32 runtime·maxstring = 256;
+uint32 runtime·maxstring = 256; // a hint for print
static String
-gostringsize(int32 l)
+gostringsize(intgo l)
{
String s;
uint32 ms;
@@ -58,7 +59,7 @@ gostringsize(int32 l)
String
runtime·gostring(byte *str)
{
- int32 l;
+ intgo l;
String s;
l = runtime·findnull(str);
@@ -68,7 +69,7 @@ runtime·gostring(byte *str)
}
String
-runtime·gostringn(byte *str, int32 l)
+runtime·gostringn(byte *str, intgo l)
{
String s;
@@ -78,7 +79,7 @@ runtime·gostringn(byte *str, int32 l)
}
Slice
-runtime·gobytes(byte *p, int32 n)
+runtime·gobytes(byte *p, intgo n)
{
Slice sl;
@@ -102,7 +103,7 @@ runtime·gostringnocopy(byte *str)
String
runtime·gostringw(uint16 *str)
{
- int32 n1, n2, i;
+ intgo n1, n2, i;
byte buf[8];
String s;
@@ -139,17 +140,26 @@ runtime·catstring(String s1, String s2)
}
static String
-concatstring(int32 n, String *s)
+concatstring(intgo n, String *s)
{
- int32 i, l;
+ intgo i, l, count;
String out;
l = 0;
+ count = 0;
for(i=0; i<n; i++) {
if(l + s[i].len < l)
runtime·throw("string concatenation too long");
l += s[i].len;
+ if(s[i].len > 0) {
+ count++;
+ out = s[i];
+ }
}
+ if(count == 0)
+ return runtime·emptystring;
+ if(count == 1) // zero or one non-empty string in concatenation
+ return out;
out = gostringsize(l);
l = 0;
@@ -163,14 +173,14 @@ concatstring(int32 n, String *s)
#pragma textflag 7
// s1 is the first of n strings.
// the output string follows.
-func concatstring(n int32, s1 String) {
+func concatstring(n int, s1 String) {
(&s1)[n] = concatstring(n, &s1);
}
static int32
cmpstring(String s1, String s2)
{
- uint32 i, l;
+ uintgo i, l;
byte c1, c2;
l = s1.len;
@@ -191,14 +201,34 @@ cmpstring(String s1, String s2)
return 0;
}
-func cmpstring(s1 String, s2 String) (v int32) {
+func cmpstring(s1 String, s2 String) (v int) {
v = cmpstring(s1, s2);
}
+func eqstring(s1 String, s2 String) (v bool) {
+ uintgo i, l;
+
+ if(s1.len != s2.len) {
+ v = false;
+ return;
+ }
+ if(s1.str == s2.str) {
+ v = true;
+ return;
+ }
+ l = s1.len;
+ for(i=0; i<l; i++)
+ if(s1.str[i] != s2.str[i]) {
+ v = false;
+ return;
+ }
+ v = true;
+}
+
int32
runtime·strcmp(byte *s1, byte *s2)
{
- uint32 i;
+ uintptr i;
byte c1, c2;
for(i=0;; i++) {
@@ -235,31 +265,6 @@ runtime·strstr(byte *s1, byte *s2)
return nil;
}
-func slicestring(si String, lindex int32, hindex int32) (so String) {
- int32 l;
-
- if(lindex < 0 || lindex > si.len ||
- hindex < lindex || hindex > si.len) {
- runtime·panicslice();
- }
-
- l = hindex-lindex;
- so.str = si.str + lindex;
- so.len = l;
-}
-
-func slicestring1(si String, lindex int32) (so String) {
- int32 l;
-
- if(lindex < 0 || lindex > si.len) {
- runtime·panicslice();
- }
-
- l = si.len-lindex;
- so.str = si.str + lindex;
- so.len = l;
-}
-
func intstring(v int64) (s String) {
s = gostringsize(8);
s.len = runtime·runetochar(s.str, v);
@@ -267,6 +272,12 @@ func intstring(v int64) (s String) {
}
func slicebytetostring(b Slice) (s String) {
+ void *pc;
+
+ if(raceenabled) {
+ pc = runtime·getcallerpc(&b);
+ runtime·racereadrangepc(b.array, b.len, 1, pc, runtime·slicebytetostring);
+ }
s = gostringsize(b.len);
runtime·memmove(s.str, b.array, s.len);
}
@@ -279,10 +290,15 @@ func stringtoslicebyte(s String) (b Slice) {
}
func slicerunetostring(b Slice) (s String) {
- int32 siz1, siz2, i;
+ intgo siz1, siz2, i;
int32 *a;
byte dum[8];
+ void *pc;
+ if(raceenabled) {
+ pc = runtime·getcallerpc(&b);
+ runtime·racereadrangepc(b.array, b.len*sizeof(*a), sizeof(*a), pc, runtime·slicerunetostring);
+ }
a = (int32*)b.array;
siz1 = 0;
for(i=0; i<b.len; i++) {
@@ -302,7 +318,7 @@ func slicerunetostring(b Slice) (s String) {
}
func stringtoslicerune(s String) (b Slice) {
- int32 n;
+ intgo n;
int32 dum, *r;
uint8 *p, *ep;
@@ -330,7 +346,7 @@ enum
Runeself = 0x80,
};
-func stringiter(s String, k int32) (retk int32) {
+func stringiter(s String, k int) (retk int) {
int32 l;
if(k >= s.len) {
@@ -351,7 +367,7 @@ func stringiter(s String, k int32) (retk int32) {
out:
}
-func stringiter2(s String, k int32) (retk int32, retv int32) {
+func stringiter2(s String, k int) (retk int, retv int32) {
if(k >= s.len) {
// retk=0 is end of iteration
retk = 0;
diff --git a/src/pkg/runtime/string_test.go b/src/pkg/runtime/string_test.go
new file mode 100644
index 000000000..8f13f0f42
--- /dev/null
+++ b/src/pkg/runtime/string_test.go
@@ -0,0 +1,45 @@
+package runtime_test
+
+import (
+ "testing"
+)
+
+func BenchmarkCompareStringEqual(b *testing.B) {
+ bytes := []byte("Hello Gophers!")
+ s1, s2 := string(bytes), string(bytes)
+ for i := 0; i < b.N; i++ {
+ if s1 != s2 {
+ b.Fatal("s1 != s2")
+ }
+ }
+}
+
+func BenchmarkCompareStringIdentical(b *testing.B) {
+ s1 := "Hello Gophers!"
+ s2 := s1
+ for i := 0; i < b.N; i++ {
+ if s1 != s2 {
+ b.Fatal("s1 != s2")
+ }
+ }
+}
+
+func BenchmarkCompareStringSameLength(b *testing.B) {
+ s1 := "Hello Gophers!"
+ s2 := "Hello, Gophers"
+ for i := 0; i < b.N; i++ {
+ if s1 == s2 {
+ b.Fatal("s1 == s2")
+ }
+ }
+}
+
+func BenchmarkCompareStringDifferentLength(b *testing.B) {
+ s1 := "Hello Gophers!"
+ s2 := "Hello, Gophers!"
+ for i := 0; i < b.N; i++ {
+ if s1 == s2 {
+ b.Fatal("s1 == s2")
+ }
+ }
+}
diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c
index f29276bd7..d7221c476 100644
--- a/src/pkg/runtime/symtab.c
+++ b/src/pkg/runtime/symtab.c
@@ -2,20 +2,59 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Runtime symbol table access. Work in progress.
-// The Plan 9 symbol table is not in a particularly convenient form.
-// The routines here massage it into a more usable form; eventually
-// we'll change 6l to do this for us, but it is easier to experiment
-// here than to change 6l and all the other tools.
+// Runtime symbol table parsing.
//
-// The symbol table also needs to be better integrated with the type
-// strings table in the future. This is just a quick way to get started
-// and figure out exactly what we want.
+// The Go tools use a symbol table derived from the Plan 9 symbol table
+// format. The symbol table is kept in its own section treated as
+// read-only memory when the binary is running: the binary consults the
+// table.
+//
+// The format used by Go 1.0 was basically the Plan 9 format. Each entry
+// is variable sized but had this format:
+//
+// 4-byte value, big endian
+// 1-byte type ([A-Za-z] + 0x80)
+// name, NUL terminated (or for 'z' and 'Z' entries, double-NUL terminated)
+// 4-byte Go type address, big endian (new in Go)
+//
+// In order to support greater interoperation with standard toolchains,
+// Go 1.1 uses a more flexible yet smaller encoding of the entries.
+// The overall structure is unchanged from Go 1.0 and, for that matter,
+// from Plan 9.
+//
+// The Go 1.1 table is a re-encoding of the data in a Go 1.0 table.
+// To identify a new table as new, it begins one of two eight-byte
+// sequences:
+//
+// FF FF FF FD 00 00 00 xx - big endian new table
+// FD FF FF FF 00 00 00 xx - little endian new table
+//
+// This sequence was chosen because old tables stop at an entry with type
+// 0, so old code reading a new table will see only an empty table. The
+// first four bytes are the target-endian encoding of 0xfffffffd. The
+// final xx gives AddrSize, the width of a full-width address.
+//
+// After that header, each entry is encoded as follows.
+//
+// 1-byte type (0-51 + two flag bits)
+// AddrSize-byte value, host-endian OR varint-encoded value
+// AddrSize-byte Go type address OR nothing
+// [n] name, terminated as before
+//
+// The type byte comes first, but 'A' encodes as 0 and 'a' as 26, so that
+// the type itself is only in the low 6 bits. The upper two bits specify
+// the format of the next two fields. If the 0x40 bit is set, the value
+// is encoded as an full-width 4- or 8-byte target-endian word. Otherwise
+// the value is a varint-encoded number. If the 0x80 bit is set, the Go
+// type is present, again as a 4- or 8-byte target-endian word. If not,
+// there is no Go type in this entry. The NUL-terminated name ends the
+// entry.
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "arch_GOARCH.h"
+#include "malloc.h"
extern byte pclntab[], epclntab[], symtab[], esymtab[];
@@ -28,24 +67,100 @@ struct Sym
// byte *gotype;
};
+static uintptr mainoffset;
+
+// A dynamically allocated string containing multiple substrings.
+// Individual strings are slices of hugestring.
+static String hugestring;
+static int32 hugestring_len;
+
+extern void main·main(void);
+
+static uintptr
+readword(byte **pp, byte *ep)
+{
+ byte *p;
+
+ p = *pp;
+ if(ep - p < sizeof(void*)) {
+ *pp = ep;
+ return 0;
+ }
+ *pp = p + sizeof(void*);
+
+ // Hairy, but only one of these four cases gets compiled.
+ if(sizeof(void*) == 8) {
+ if(BigEndian) {
+ return ((uint64)p[0]<<56) | ((uint64)p[1]<<48) | ((uint64)p[2]<<40) | ((uint64)p[3]<<32) |
+ ((uint64)p[4]<<24) | ((uint64)p[5]<<16) | ((uint64)p[6]<<8) | ((uint64)p[7]);
+ }
+ return ((uint64)p[7]<<56) | ((uint64)p[6]<<48) | ((uint64)p[5]<<40) | ((uint64)p[4]<<32) |
+ ((uint64)p[3]<<24) | ((uint64)p[2]<<16) | ((uint64)p[1]<<8) | ((uint64)p[0]);
+ }
+ if(BigEndian) {
+ return ((uint32)p[0]<<24) | ((uint32)p[1]<<16) | ((uint32)p[2]<<8) | ((uint32)p[3]);
+ }
+ return ((uint32)p[3]<<24) | ((uint32)p[2]<<16) | ((uint32)p[1]<<8) | ((uint32)p[0]);
+}
+
// Walk over symtab, calling fn(&s) for each symbol.
static void
walksymtab(void (*fn)(Sym*))
{
byte *p, *ep, *q;
Sym s;
+ int32 widevalue, havetype, shift;
p = symtab;
ep = esymtab;
+
+ // Table must begin with correct magic number.
+ if(ep - p < 8 || p[4] != 0x00 || p[5] != 0x00 || p[6] != 0x00 || p[7] != sizeof(void*))
+ return;
+ if(BigEndian) {
+ if(p[0] != 0xff || p[1] != 0xff || p[2] != 0xff || p[3] != 0xfd)
+ return;
+ } else {
+ if(p[0] != 0xfd || p[1] != 0xff || p[2] != 0xff || p[3] != 0xff)
+ return;
+ }
+ p += 8;
+
while(p < ep) {
- if(p + 7 > ep)
- break;
- s.value = ((uint32)p[0]<<24) | ((uint32)p[1]<<16) | ((uint32)p[2]<<8) | ((uint32)p[3]);
+ s.symtype = p[0]&0x3F;
+ widevalue = p[0]&0x40;
+ havetype = p[0]&0x80;
+ if(s.symtype < 26)
+ s.symtype += 'A';
+ else
+ s.symtype += 'a' - 26;
+ p++;
+
+ // Value, either full-width or varint-encoded.
+ if(widevalue) {
+ s.value = readword(&p, ep);
+ } else {
+ s.value = 0;
+ shift = 0;
+ while(p < ep && (p[0]&0x80) != 0) {
+ s.value |= (uintptr)(p[0]&0x7F)<<shift;
+ shift += 7;
+ p++;
+ }
+ if(p >= ep)
+ break;
+ s.value |= (uintptr)p[0]<<shift;
+ p++;
+ }
+
+ // Go type, if present. Ignored but must skip over.
+ if(havetype)
+ readword(&p, ep);
- if(!(p[4]&0x80))
+ // Name.
+ if(ep - p < 2)
break;
- s.symtype = p[4] & ~0x80;
- p += 5;
+
s.name = p;
if(s.symtype == 'z' || s.symtype == 'Z') {
// path reference string - skip first byte,
@@ -65,7 +180,7 @@ walksymtab(void (*fn)(Sym*))
break;
p = q+1;
}
- p += 4; // go type
+
fn(&s);
}
}
@@ -80,12 +195,13 @@ static int32 nfname;
static uint32 funcinit;
static Lock funclock;
+static uintptr lastvalue;
static void
dofunc(Sym *sym)
{
Func *f;
-
+
switch(sym->symtype) {
case 't':
case 'T':
@@ -93,6 +209,11 @@ dofunc(Sym *sym)
case 'L':
if(runtime·strcmp(sym->name, (byte*)"etext") == 0)
break;
+ if(sym->value < lastvalue) {
+ runtime·printf("symbols out of order: %p before %p\n", lastvalue, sym->value);
+ runtime·throw("malformed symbol table");
+ }
+ lastvalue = sym->value;
if(func == nil) {
nfunc++;
break;
@@ -104,24 +225,24 @@ dofunc(Sym *sym)
f->frame = -sizeof(uintptr);
break;
case 'm':
- if(nfunc > 0 && func != nil)
- func[nfunc-1].frame += sym->value;
- break;
- case 'p':
- if(nfunc > 0 && func != nil) {
- f = &func[nfunc-1];
- // args counts 32-bit words.
- // sym->value is the arg's offset.
- // don't know width of this arg, so assume it is 64 bits.
- if(f->args < sym->value/4 + 2)
- f->args = sym->value/4 + 2;
+ if(nfunc <= 0 || func == nil)
+ break;
+ if(runtime·strcmp(sym->name, (byte*)".frame") == 0)
+ func[nfunc-1].frame = sym->value;
+ else if(runtime·strcmp(sym->name, (byte*)".locals") == 0)
+ func[nfunc-1].locals = sym->value;
+ else if(runtime·strcmp(sym->name, (byte*)".args") == 0)
+ func[nfunc-1].args = sym->value;
+ else {
+ runtime·printf("invalid 'm' symbol named '%s'\n", sym->name);
+ runtime·throw("mangled symbol table");
}
break;
case 'f':
if(fname == nil) {
if(sym->value >= nfname) {
if(sym->value >= 0x10000) {
- runtime·printf("invalid symbol file index %p\n", sym->value);
+ runtime·printf("runtime: invalid symbol file index %p\n", sym->value);
runtime·throw("mangled symbol table");
}
nfname = sym->value+1;
@@ -135,14 +256,15 @@ dofunc(Sym *sym)
// put together the path name for a z entry.
// the f entries have been accumulated into fname already.
-static void
+// returns the length of the path name.
+static int32
makepath(byte *buf, int32 nbuf, byte *path)
{
int32 n, len;
byte *p, *ep, *q;
if(nbuf <= 0)
- return;
+ return 0;
p = buf;
ep = buf + nbuf;
@@ -163,6 +285,26 @@ makepath(byte *buf, int32 nbuf, byte *path)
runtime·memmove(p, q, len+1);
p += len;
}
+ return p - buf;
+}
+
+// appends p to hugestring
+static String
+gostringn(byte *p, int32 l)
+{
+ String s;
+
+ if(l == 0)
+ return runtime·emptystring;
+ if(hugestring.str == nil) {
+ hugestring_len += l;
+ return runtime·emptystring;
+ }
+ s.str = hugestring.str + hugestring.len;
+ s.len = l;
+ hugestring.len += s.len;
+ runtime·memmove(s.str, p, l);
+ return s;
}
// walk symtab accumulating path names for use by pc/ln table.
@@ -181,11 +323,13 @@ dosrcline(Sym *sym)
static int32 incstart;
static int32 nfunc, nfile, nhist;
Func *f;
- int32 i;
+ int32 i, l;
switch(sym->symtype) {
case 't':
case 'T':
+ if(hugestring.str == nil)
+ break;
if(runtime·strcmp(sym->name, (byte*)"etext") == 0)
break;
f = &func[nfunc++];
@@ -200,23 +344,23 @@ dosrcline(Sym *sym)
case 'z':
if(sym->value == 1) {
// entry for main source file for a new object.
- makepath(srcbuf, sizeof srcbuf, sym->name+1);
+ l = makepath(srcbuf, sizeof srcbuf, sym->name+1);
nhist = 0;
nfile = 0;
if(nfile == nelem(files))
return;
- files[nfile].srcstring = runtime·gostring(srcbuf);
+ files[nfile].srcstring = gostringn(srcbuf, l);
files[nfile].aline = 0;
files[nfile++].delta = 0;
} else {
// push or pop of included file.
- makepath(srcbuf, sizeof srcbuf, sym->name+1);
+ l = makepath(srcbuf, sizeof srcbuf, sym->name+1);
if(srcbuf[0] != '\0') {
if(nhist++ == 0)
incstart = sym->value;
if(nhist == 0 && nfile < nelem(files)) {
// new top-level file
- files[nfile].srcstring = runtime·gostring(srcbuf);
+ files[nfile].srcstring = gostringn(srcbuf, l);
files[nfile].aline = sym->value;
// this is "line 0"
files[nfile++].delta = sym->value - 1;
@@ -275,7 +419,7 @@ splitpcln(void)
line += *p++;
else
line -= *p++ - 64;
-
+
// pc, line now match.
// Because the state machine begins at pc==entry and line==0,
// it can happen - just at the beginning! - that the update may
@@ -297,7 +441,7 @@ splitpcln(void)
while(f < ef && pc >= (f+1)->entry);
f->pcln.array = p;
// pc0 and ln0 are the starting values for
- // the loop over f->pcln, so pc must be
+ // the loop over f->pcln, so pc must be
// adjusted by the same pcquant update
// that we're going to do as we continue our loop.
f->pc0 = pc + pcquant;
@@ -323,11 +467,11 @@ runtime·funcline(Func *f, uintptr targetpc)
uintptr pc;
int32 line;
int32 pcquant;
-
+
enum {
debug = 0
};
-
+
switch(thechar) {
case '5':
pcquant = 4;
@@ -354,7 +498,7 @@ runtime·funcline(Func *f, uintptr targetpc)
if(debug && !runtime·panicking)
runtime·printf("pc<%p targetpc=%p line=%d\n", pc, targetpc, line);
-
+
// If the pc has advanced too far or we're out of data,
// stop and the last known line number.
if(pc > targetpc || p >= ep)
@@ -382,7 +526,7 @@ runtime·funcline(Func *f, uintptr targetpc)
}
void
-runtime·funcline_go(Func *f, uintptr targetpc, String retfile, int32 retline)
+runtime·funcline_go(Func *f, uintptr targetpc, String retfile, intgo retline)
{
retfile = f->src;
retline = runtime·funcline(f, targetpc);
@@ -406,20 +550,30 @@ buildfuncs(void)
// count funcs, fnames
nfunc = 0;
nfname = 0;
+ lastvalue = 0;
walksymtab(dofunc);
- // initialize tables
- func = runtime·mal((nfunc+1)*sizeof func[0]);
+ // Initialize tables.
+ // Can use FlagNoPointers - all pointers either point into sections of the executable
+ // or point into hugestring.
+ func = runtime·mallocgc((nfunc+1)*sizeof func[0], FlagNoPointers, 0, 1);
func[nfunc].entry = (uint64)etext;
- fname = runtime·mal(nfname*sizeof fname[0]);
+ fname = runtime·mallocgc(nfname*sizeof fname[0], FlagNoPointers, 0, 1);
nfunc = 0;
+ lastvalue = 0;
walksymtab(dofunc);
// split pc/ln table by func
splitpcln();
// record src file and line info for each func
- walksymtab(dosrcline);
+ walksymtab(dosrcline); // pass 1: determine hugestring_len
+ hugestring.str = runtime·mallocgc(hugestring_len, FlagNoPointers, 0, 0);
+ hugestring.len = 0;
+ walksymtab(dosrcline); // pass 2: fill and use hugestring
+
+ if(hugestring.len != hugestring_len)
+ runtime·throw("buildfunc: problem in initialization procedure");
m->nomemprof--;
}
@@ -482,7 +636,7 @@ static bool
hasprefix(String s, int8 *p)
{
int32 i;
-
+
for(i=0; i<s.len; i++) {
if(p[i] == 0)
return 1;
@@ -496,7 +650,7 @@ static bool
contains(String s, int8 *p)
{
int32 i;
-
+
if(p[0] == 0)
return 1;
for(i=0; i<s.len; i++) {
@@ -509,11 +663,13 @@ contains(String s, int8 *p)
}
bool
-runtime·showframe(Func *f)
+runtime·showframe(Func *f, bool current)
{
static int32 traceback = -1;
-
+
+ if(current && m->throwing > 0)
+ return 1;
if(traceback < 0)
traceback = runtime·gotraceback();
- return traceback > 1 || contains(f->name, ".") && !hasprefix(f->name, "runtime.");
+ return traceback > 1 || f != nil && contains(f->name, ".") && !hasprefix(f->name, "runtime.");
}
diff --git a/src/pkg/runtime/sys_darwin_386.s b/src/pkg/runtime/sys_darwin_386.s
index 3cf3506ad..8a938f9f4 100644
--- a/src/pkg/runtime/sys_darwin_386.s
+++ b/src/pkg/runtime/sys_darwin_386.s
@@ -47,8 +47,7 @@ TEXT runtime·mmap(SB),7,$0
TEXT runtime·madvise(SB),7,$0
MOVL $75, AX
INT $0x80
- JAE 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ // ignore failure - maybe pages are locked
RET
TEXT runtime·munmap(SB),7,$0
@@ -63,40 +62,133 @@ TEXT runtime·setitimer(SB),7,$0
INT $0x80
RET
-// func now() (sec int64, nsec int32)
-TEXT time·now(SB), 7, $32
- LEAL 12(SP), AX // must be non-nil, unused
- MOVL AX, 4(SP)
- MOVL $0, 8(SP) // time zone pointer
- MOVL $116, AX
- INT $0x80
- MOVL DX, BX
+// OS X comm page time offsets
+// http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/i386/cpu_capabilities.h
+#define cpu_capabilities 0x20
+#define nt_tsc_base 0x50
+#define nt_scale 0x58
+#define nt_shift 0x5c
+#define nt_ns_base 0x60
+#define nt_generation 0x68
+#define gtod_generation 0x6c
+#define gtod_ns_base 0x70
+#define gtod_sec_base 0x78
+
+// called from assembly
+// 64-bit unix nanoseconds returned in DX:AX.
+// I'd much rather write this in C but we need
+// assembly for the 96-bit multiply and RDTSC.
+TEXT runtime·now(SB),7,$40
+ MOVL $0xffff0000, BP /* comm page base */
+
+ // Test for slow CPU. If so, the math is completely
+ // different, and unimplemented here, so use the
+ // system call.
+ MOVL cpu_capabilities(BP), AX
+ TESTL $0x4000, AX
+ JNZ systime
+
+ // Loop trying to take a consistent snapshot
+ // of the time parameters.
+timeloop:
+ MOVL gtod_generation(BP), BX
+ TESTL BX, BX
+ JZ systime
+ MOVL nt_generation(BP), CX
+ TESTL CX, CX
+ JZ timeloop
+ RDTSC
+ MOVL nt_tsc_base(BP), SI
+ MOVL (nt_tsc_base+4)(BP), DI
+ MOVL SI, 0(SP)
+ MOVL DI, 4(SP)
+ MOVL nt_scale(BP), SI
+ MOVL SI, 8(SP)
+ MOVL nt_ns_base(BP), SI
+ MOVL (nt_ns_base+4)(BP), DI
+ MOVL SI, 12(SP)
+ MOVL DI, 16(SP)
+ CMPL nt_generation(BP), CX
+ JNE timeloop
+ MOVL gtod_ns_base(BP), SI
+ MOVL (gtod_ns_base+4)(BP), DI
+ MOVL SI, 20(SP)
+ MOVL DI, 24(SP)
+ MOVL gtod_sec_base(BP), SI
+ MOVL (gtod_sec_base+4)(BP), DI
+ MOVL SI, 28(SP)
+ MOVL DI, 32(SP)
+ CMPL gtod_generation(BP), BX
+ JNE timeloop
+
+ // Gathered all the data we need. Compute time.
+ // ((tsc - nt_tsc_base) * nt_scale) >> 32 + nt_ns_base - gtod_ns_base + gtod_sec_base*1e9
+ // The multiply and shift extracts the top 64 bits of the 96-bit product.
+ SUBL 0(SP), AX // DX:AX = (tsc - nt_tsc_base)
+ SBBL 4(SP), DX
+
+ // We have x = tsc - nt_tsc_base - DX:AX to be
+ // multiplied by y = nt_scale = 8(SP), keeping the top 64 bits of the 96-bit product.
+ // x*y = (x&0xffffffff)*y + (x&0xffffffff00000000)*y
+ // (x*y)>>32 = ((x&0xffffffff)*y)>>32 + (x>>32)*y
+ MOVL DX, CX // SI = (x&0xffffffff)*y >> 32
+ MOVL $0, DX
+ MULL 8(SP)
+ MOVL DX, SI
- // sec is in AX, usec in BX
- MOVL AX, sec+0(FP)
- MOVL $0, sec+4(FP)
- IMULL $1000, BX
- MOVL BX, nsec+8(FP)
+ MOVL CX, AX // DX:AX = (x>>32)*y
+ MOVL $0, DX
+ MULL 8(SP)
+
+ ADDL SI, AX // DX:AX += (x&0xffffffff)*y >> 32
+ ADCL $0, DX
+
+ // DX:AX is now ((tsc - nt_tsc_base) * nt_scale) >> 32.
+ ADDL 12(SP), AX // DX:AX += nt_ns_base
+ ADCL 16(SP), DX
+ SUBL 20(SP), AX // DX:AX -= gtod_ns_base
+ SBBL 24(SP), DX
+ MOVL AX, SI // DI:SI = DX:AX
+ MOVL DX, DI
+ MOVL 28(SP), AX // DX:AX = gtod_sec_base*1e9
+ MOVL 32(SP), DX
+ MOVL $1000000000, CX
+ MULL CX
+ ADDL SI, AX // DX:AX += DI:SI
+ ADCL DI, DX
RET
-// int64 nanotime(void) so really
-// void nanotime(int64 *nsec)
-TEXT runtime·nanotime(SB), 7, $32
+systime:
+ // Fall back to system call (usually first call in this thread)
LEAL 12(SP), AX // must be non-nil, unused
MOVL AX, 4(SP)
MOVL $0, 8(SP) // time zone pointer
MOVL $116, AX
INT $0x80
- MOVL DX, BX
-
- // sec is in AX, usec in BX
+ // sec is in AX, usec in DX
// convert to DX:AX nsec
+ MOVL DX, BX
MOVL $1000000000, CX
MULL CX
IMULL $1000, BX
ADDL BX, AX
ADCL $0, DX
+ RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB),7,$0
+ CALL runtime·now(SB)
+ MOVL $1000000000, CX
+ DIVL CX
+ MOVL AX, sec+0(FP)
+ MOVL $0, sec+4(FP)
+ MOVL DX, nsec+8(FP)
+ RET
+// int64 nanotime(void) so really
+// void nanotime(int64 *nsec)
+TEXT runtime·nanotime(SB),7,$0
+ CALL runtime·now(SB)
MOVL ret+0(FP), DI
MOVL AX, 0(DI)
MOVL DX, 4(DI)
@@ -120,8 +212,8 @@ TEXT runtime·sigaction(SB),7,$0
// It is called with the following arguments on the stack:
// 0(FP) "return address" - ignored
// 4(FP) actual handler
-// 8(FP) siginfo style - ignored
-// 12(FP) signal number
+// 8(FP) signal number
+// 12(FP) siginfo style
// 16(FP) siginfo
// 20(FP) context
TEXT runtime·sigtramp(SB),7,$40
@@ -130,8 +222,11 @@ TEXT runtime·sigtramp(SB),7,$40
// check that m exists
MOVL m(CX), BP
CMPL BP, $0
- JNE 2(PC)
+ JNE 5(PC)
+ MOVL sig+8(FP), BX
+ MOVL BX, 0(SP)
CALL runtime·badsignal(SB)
+ RET
// save g
MOVL g(CX), DI
@@ -196,7 +291,7 @@ TEXT runtime·usleep(SB),7,$32
INT $0x80
RET
-// void bsdthread_create(void *stk, M *m, G *g, void (*fn)(void))
+// void bsdthread_create(void *stk, M *mp, G *gp, void (*fn)(void))
// System call args are: func arg stack pthread flags.
TEXT runtime·bsdthread_create(SB),7,$32
MOVL $360, AX
@@ -268,8 +363,10 @@ TEXT runtime·bsdthread_register(SB),7,$40
MOVL $0, 20(SP) // targetconc_ptr
MOVL $0, 24(SP) // dispatchqueue_offset
INT $0x80
- JAE 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ JAE 3(PC)
+ NEGL AX
+ RET
+ MOVL $0, AX
RET
// Invoke Mach system call.
diff --git a/src/pkg/runtime/sys_darwin_amd64.s b/src/pkg/runtime/sys_darwin_amd64.s
index 90571baae..4e43a76c3 100644
--- a/src/pkg/runtime/sys_darwin_amd64.s
+++ b/src/pkg/runtime/sys_darwin_amd64.s
@@ -61,30 +61,63 @@ TEXT runtime·madvise(SB), 7, $0
MOVL 24(SP), DX // arg 3 advice
MOVL $(0x2000000+75), AX // syscall entry madvise
SYSCALL
- JCC 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ // ignore failure - maybe pages are locked
RET
-// func now() (sec int64, nsec int32)
-TEXT time·now(SB), 7, $32
- MOVQ SP, DI // must be non-nil, unused
- MOVQ $0, SI
- MOVL $(0x2000000+116), AX
- SYSCALL
-
- // sec is in AX, usec in DX
- MOVQ AX, sec+0(FP)
- IMULQ $1000, DX
- MOVL DX, nsec+8(FP)
- RET
+// OS X comm page time offsets
+// http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/i386/cpu_capabilities.h
+#define nt_tsc_base 0x50
+#define nt_scale 0x58
+#define nt_shift 0x5c
+#define nt_ns_base 0x60
+#define nt_generation 0x68
+#define gtod_generation 0x6c
+#define gtod_ns_base 0x70
+#define gtod_sec_base 0x78
// int64 nanotime(void)
TEXT runtime·nanotime(SB), 7, $32
+ MOVQ $0x7fffffe00000, BP /* comm page base */
+ // Loop trying to take a consistent snapshot
+ // of the time parameters.
+timeloop:
+ MOVL gtod_generation(BP), R8
+ TESTL R8, R8
+ JZ systime
+ MOVL nt_generation(BP), R9
+ TESTL R9, R9
+ JZ timeloop
+ RDTSC
+ MOVQ nt_tsc_base(BP), R10
+ MOVL nt_scale(BP), R11
+ MOVQ nt_ns_base(BP), R12
+ CMPL nt_generation(BP), R9
+ JNE timeloop
+ MOVQ gtod_ns_base(BP), R13
+ MOVQ gtod_sec_base(BP), R14
+ CMPL gtod_generation(BP), R8
+ JNE timeloop
+
+ // Gathered all the data we need. Compute time.
+ // ((tsc - nt_tsc_base) * nt_scale) >> 32 + nt_ns_base - gtod_ns_base + gtod_sec_base*1e9
+ // The multiply and shift extracts the top 64 bits of the 96-bit product.
+ SHLQ $32, DX
+ ADDQ DX, AX
+ SUBQ R10, AX
+ MULQ R11
+ SHRQ $32, AX:DX
+ ADDQ R12, AX
+ SUBQ R13, AX
+ IMULQ $1000000000, R14
+ ADDQ R14, AX
+ RET
+
+systime:
+ // Fall back to system call (usually first call in this thread).
MOVQ SP, DI // must be non-nil, unused
MOVQ $0, SI
MOVL $(0x2000000+116), AX
SYSCALL
-
// sec is in AX, usec in DX
// return nsec in AX
IMULQ $1000000000, AX
@@ -92,6 +125,25 @@ TEXT runtime·nanotime(SB), 7, $32
ADDQ DX, AX
RET
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB),7,$0
+ CALL runtime·nanotime(SB)
+
+ // generated code for
+ // func f(x uint64) (uint64, uint64) { return x/1000000000, x%100000000 }
+ // adapted to reduce duplication
+ MOVQ AX, CX
+ MOVQ $1360296554856532783, AX
+ MULQ CX
+ ADDQ CX, DX
+ RCRQ $1, DX
+ SHRQ $29, DX
+ MOVQ DX, sec+0(FP)
+ IMULQ $1000000000, DX
+ SUBQ DX, CX
+ MOVL CX, nsec+8(FP)
+ RET
+
TEXT runtime·sigprocmask(SB),7,$0
MOVL 8(SP), DI
MOVQ 16(SP), SI
@@ -120,8 +172,10 @@ TEXT runtime·sigtramp(SB),7,$64
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
- JNE 2(PC)
+ JNE 4(PC)
+ MOVL DX, 0(SP)
CALL runtime·badsignal(SB)
+ RET
// save g
MOVQ g(BX), R10
@@ -199,7 +253,7 @@ TEXT runtime·usleep(SB),7,$16
SYSCALL
RET
-// void bsdthread_create(void *stk, M *m, G *g, void (*fn)(void))
+// void bsdthread_create(void *stk, M *mp, G *gp, void (*fn)(void))
TEXT runtime·bsdthread_create(SB),7,$0
// Set up arguments to bsdthread_create system call.
// The ones in quotes pass through to the thread callback
@@ -265,8 +319,10 @@ TEXT runtime·bsdthread_register(SB),7,$0
MOVQ $0, R9 // dispatchqueue_offset
MOVQ $(0x2000000+366), AX // bsdthread_register
SYSCALL
- JCC 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ JCC 3(PC)
+ NEGL AX
+ RET
+ MOVL $0, AX
RET
// Mach system calls use 0x1000000 instead of the BSD's 0x2000000.
diff --git a/src/pkg/runtime/sys_freebsd_386.s b/src/pkg/runtime/sys_freebsd_386.s
index a72d8972b..d5370267a 100644
--- a/src/pkg/runtime/sys_freebsd_386.s
+++ b/src/pkg/runtime/sys_freebsd_386.s
@@ -39,6 +39,7 @@ TEXT runtime·thr_start(SB),7,$0
MOVL AX, m(CX)
CALL runtime·stackcheck(SB) // smashes AX
CALL runtime·mstart(SB)
+
MOVL 0, AX // crash (not reached)
// Exit the entire program (like C exit)
@@ -89,7 +90,7 @@ TEXT runtime·mmap(SB),7,$32
MOVSL
MOVSL
MOVSL
- MOVL $0, AX // top 64 bits of file offset
+ MOVL $0, AX // top 32 bits of file offset
STOSL
MOVL $477, AX
INT $0x80
@@ -102,6 +103,12 @@ TEXT runtime·munmap(SB),7,$-4
MOVL $0xf1, 0xf1 // crash
RET
+TEXT runtime·madvise(SB),7,$-4
+ MOVL $75, AX // madvise
+ INT $0x80
+ // ignore failure - maybe pages are locked
+ RET
+
TEXT runtime·setitimer(SB), 7, $-4
MOVL $83, AX
INT $0x80
@@ -109,40 +116,38 @@ TEXT runtime·setitimer(SB), 7, $-4
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
- MOVL $116, AX
+ MOVL $232, AX
LEAL 12(SP), BX
- MOVL BX, 4(SP)
- MOVL $0, 8(SP)
+ MOVL $0, 4(SP)
+ MOVL BX, 8(SP)
INT $0x80
MOVL 12(SP), AX // sec
- MOVL 16(SP), BX // usec
+ MOVL 16(SP), BX // nsec
- // sec is in AX, usec in BX
+ // sec is in AX, nsec in BX
MOVL AX, sec+0(FP)
MOVL $0, sec+4(FP)
- IMULL $1000, BX
MOVL BX, nsec+8(FP)
RET
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
TEXT runtime·nanotime(SB), 7, $32
- MOVL $116, AX
+ MOVL $232, AX
LEAL 12(SP), BX
- MOVL BX, 4(SP)
- MOVL $0, 8(SP)
+ MOVL $0, 4(SP)
+ MOVL BX, 8(SP)
INT $0x80
MOVL 12(SP), AX // sec
- MOVL 16(SP), BX // usec
+ MOVL 16(SP), BX // nsec
- // sec is in AX, usec in BX
+ // sec is in AX, nsec in BX
// convert to DX:AX nsec
MOVL $1000000000, CX
MULL CX
- IMULL $1000, BX
ADDL BX, AX
ADCL $0, DX
-
+
MOVL ret+0(FP), DI
MOVL AX, 0(DI)
MOVL DX, 4(DI)
@@ -162,8 +167,11 @@ TEXT runtime·sigtramp(SB),7,$44
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
- JNE 2(PC)
+ JNE 5(PC)
+ MOVL signo+0(FP), BX
+ MOVL BX, 0(SP)
CALL runtime·badsignal(SB)
+ RET
// save g
MOVL g(CX), DI
diff --git a/src/pkg/runtime/sys_freebsd_amd64.s b/src/pkg/runtime/sys_freebsd_amd64.s
index 36e034a80..40c6237e2 100644
--- a/src/pkg/runtime/sys_freebsd_amd64.s
+++ b/src/pkg/runtime/sys_freebsd_amd64.s
@@ -38,8 +38,9 @@ TEXT runtime·thr_start(SB),7,$0
MOVQ m_g0(R13), DI
MOVQ DI, g(CX)
- CALL runtime·stackcheck(SB)
- CALL runtime·mstart(SB)
+ CALL runtime·stackcheck(SB)
+ CALL runtime·mstart(SB)
+
MOVQ 0, AX // crash (not reached)
// Exit the entire program (like C exit)
@@ -94,31 +95,29 @@ TEXT runtime·setitimer(SB), 7, $-8
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
- MOVL $116, AX
- LEAQ 8(SP), DI
- MOVQ $0, SI
+ MOVL $232, AX
+ MOVQ $0, DI
+ LEAQ 8(SP), SI
SYSCALL
MOVQ 8(SP), AX // sec
- MOVL 16(SP), DX // usec
+ MOVQ 16(SP), DX // nsec
- // sec is in AX, usec in DX
+ // sec is in AX, nsec in DX
MOVQ AX, sec+0(FP)
- IMULQ $1000, DX
MOVL DX, nsec+8(FP)
RET
TEXT runtime·nanotime(SB), 7, $32
- MOVL $116, AX
- LEAQ 8(SP), DI
- MOVQ $0, SI
+ MOVL $232, AX
+ MOVQ $0, DI
+ LEAQ 8(SP), SI
SYSCALL
MOVQ 8(SP), AX // sec
- MOVL 16(SP), DX // usec
+ MOVQ 16(SP), DX // nsec
- // sec is in AX, usec in DX
+ // sec is in AX, nsec in DX
// return nsec in AX
IMULQ $1000000000, AX
- IMULQ $1000, DX
ADDQ DX, AX
RET
@@ -138,8 +137,10 @@ TEXT runtime·sigtramp(SB),7,$64
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
- JNE 2(PC)
+ JNE 4(PC)
+ MOVQ DI, 0(SP)
CALL runtime·badsignal(SB)
+ RET
// save g
MOVQ g(BX), R10
@@ -182,6 +183,15 @@ TEXT runtime·munmap(SB),7,$0
MOVL $0xf1, 0xf1 // crash
RET
+TEXT runtime·madvise(SB),7,$0
+ MOVQ 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVQ 24(SP), DX
+ MOVQ $75, AX // madvise
+ SYSCALL
+ // ignore failure - maybe pages are locked
+ RET
+
TEXT runtime·sigaltstack(SB),7,$-8
MOVQ new+8(SP), DI
MOVQ old+16(SP), SI
diff --git a/src/pkg/runtime/sys_freebsd_arm.s b/src/pkg/runtime/sys_freebsd_arm.s
new file mode 100644
index 000000000..77050e8d0
--- /dev/null
+++ b/src/pkg/runtime/sys_freebsd_arm.s
@@ -0,0 +1,260 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// System calls and other sys.stuff for ARM, FreeBSD
+// /usr/src/sys/kern/syscalls.master for syscall numbers.
+//
+
+#include "zasm_GOOS_GOARCH.h"
+
+TEXT runtime·sys_umtx_op(SB),7,$0
+ MOVW 0(FP), R0
+ MOVW 4(FP), R1
+ MOVW 8(FP), R2
+ MOVW 12(FP), R3
+ ADD $20, R13 // arg 5 is passed on stack
+ SWI $454
+ SUB $20, R13
+ // BCS error
+ RET
+
+TEXT runtime·thr_new(SB),7,$0
+ MOVW 0(FP), R0
+ MOVW 4(FP), R1
+ SWI $455
+ RET
+
+TEXT runtime·thr_start(SB),7,$0
+ MOVW R0, R9 // m
+
+ // TODO(minux): set up TLS?
+
+ // set up g
+ MOVW m_g0(R9), R10
+ BL runtime·emptyfunc(SB) // fault if stack check is wrong
+ BL runtime·mstart(SB)
+
+ MOVW $2, R9 // crash (not reached)
+ MOVW R9, (R9)
+ RET
+
+// Exit the entire program (like C exit)
+TEXT runtime·exit(SB),7,$-8
+ MOVW 0(FP), R0 // arg 1 exit status
+ SWI $1
+ MOVW.CS $0, R9 // crash on syscall failure
+ MOVW.CS R9, (R9)
+ RET
+
+TEXT runtime·exit1(SB),7,$-8
+ MOVW 0(FP), R0 // arg 1 exit status
+ SWI $431
+ MOVW.CS $0, R9 // crash on syscall failure
+ MOVW.CS R9, (R9)
+ RET
+
+TEXT runtime·write(SB),7,$-8
+ MOVW 0(FP), R0 // arg 1 fd
+ MOVW 4(FP), R1 // arg 2 buf
+ MOVW 8(FP), R2 // arg 3 count
+ SWI $4
+ RET
+
+TEXT runtime·getrlimit(SB),7,$-8
+ MOVW 0(FP), R0
+ MOVW 4(FP), R1
+ MOVW 8(FP), R2
+ SWI $194
+ RET
+
+TEXT runtime·raisesigpipe(SB),7,$8
+ // thr_self(&4(R13))
+ MOVW $4(R13), R0 // arg 1 &4(R13)
+ SWI $432
+ // thr_kill(self, SIGPIPE)
+ MOVW 4(R13), R0 // arg 1 id
+ MOVW $13, R1 // arg 2 SIGPIPE
+ SWI $433
+ RET
+
+TEXT runtime·setitimer(SB), 7, $-8
+ MOVW 0(FP), R0
+ MOVW 4(FP), R1
+ MOVW 8(FP), R2
+ SWI $83
+ RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB), 7, $32
+ MOVW $0, R0 // CLOCK_REALTIME
+ MOVW $8(R13), R1
+ SWI $232 // clock_gettime
+
+ MOVW 8(R13), R0 // sec.low
+ MOVW 12(R13), R1 // sec.high
+ MOVW 16(R13), R2 // nsec
+
+ MOVW R0, 0(FP)
+ MOVW R1, 4(FP)
+ MOVW R2, 8(FP)
+ RET
+
+// int64 nanotime(void) so really
+// void nanotime(int64 *nsec)
+TEXT runtime·nanotime(SB), 7, $32
+ MOVW $0, R0 // CLOCK_REALTIME
+ MOVW $8(R13), R1
+ SWI $232 // clock_gettime
+
+ MOVW 8(R13), R0 // sec.low
+ MOVW 12(R13), R4 // sec.high
+ MOVW 16(R13), R2 // nsec
+
+ MOVW $1000000000, R3
+ MULLU R0, R3, (R1, R0)
+ MUL R3, R4
+ ADD.S R2, R0
+ ADC R4, R1
+
+ MOVW 0(FP), R3
+ MOVW R0, 0(R3)
+ MOVW R1, 4(R3)
+ RET
+
+TEXT runtime·sigaction(SB),7,$-8
+ MOVW 0(FP), R0 // arg 1 sig
+ MOVW 4(FP), R1 // arg 2 act
+ MOVW 8(FP), R2 // arg 3 oact
+ SWI $416
+ MOVW.CS $0, R9 // crash on syscall failure
+ MOVW.CS R9, (R9)
+ RET
+
+TEXT runtime·sigtramp(SB),7,$24
+ // this might be called in external code context,
+ // where g and m are not set.
+ // first save R0, because _cgo_load_gm will clobber it
+ // TODO(adonovan): call runtime·badsignal if m=0, like other platforms?
+ MOVW R0, 4(R13) // signum
+ MOVW _cgo_load_gm(SB), R0
+ CMP $0, R0
+ BL.NE (R0)
+
+ // save g
+ MOVW R10, R4
+ MOVW R10, 20(R13)
+
+ // g = m->signal
+ MOVW m_gsignal(R9), R10
+
+ // R0 is already saved
+ MOVW R1, 8(R13) // info
+ MOVW R2, 12(R13) // context
+ MOVW R4, 16(R13) // oldg
+
+ BL runtime·sighandler(SB)
+
+ // restore g
+ MOVW 20(R13), R10
+ RET
+
+TEXT runtime·mmap(SB),7,$12
+ MOVW 0(FP), R0 // arg 1 addr
+ MOVW 4(FP), R1 // arg 2 len
+ MOVW 8(FP), R2 // arg 3 prot
+ MOVW 12(FP), R3 // arg 4 flags
+ // arg 5 (fid) and arg6 (offset_lo, offset_hi) are passed on stack
+ // note the C runtime only passes the 32-bit offset_lo to us
+ MOVW 16(FP), R4 // arg 5
+ MOVW R4, 4(R13)
+ MOVW 20(FP), R5 // arg 6 lower 32-bit
+ MOVW R5, 8(R13)
+ MOVW $0, R6 // higher 32-bit for arg 6
+ MOVW R6, 12(R13)
+ ADD $4, R13 // pass arg 5 and arg 6 on stack
+ SWI $477
+ SUB $4, R13
+ RET
+
+TEXT runtime·munmap(SB),7,$0
+ MOVW 0(FP), R0 // arg 1 addr
+ MOVW 4(FP), R1 // arg 2 len
+ SWI $73
+ MOVW.CS $0, R9 // crash on syscall failure
+ MOVW.CS R9, (R9)
+ RET
+
+TEXT runtime·madvise(SB),7,$0
+ MOVW 0(FP), R0 // arg 1 addr
+ MOVW 4(FP), R1 // arg 2 len
+ MOVW 8(FP), R2 // arg 3 flags
+ SWI $75
+ // ignore failure - maybe pages are locked
+ RET
+
+TEXT runtime·sigaltstack(SB),7,$-8
+ MOVW new+0(FP), R0
+ MOVW old+4(FP), R1
+ SWI $53
+ MOVW.CS $0, R9 // crash on syscall failure
+ MOVW.CS R9, (R9)
+ RET
+
+TEXT runtime·usleep(SB),7,$16
+ MOVW usec+0(FP), R0
+ MOVW R0, R2
+ MOVW $1000000, R1
+ DIV R1, R0
+ // 0(R13) is the saved LR, don't use it
+ MOVW R0, 4(R13) // tv_sec.low
+ MOVW $0, R0
+ MOVW R0, 8(R13) // tv_sec.high
+ MOD R1, R2
+ MOVW $1000, R1
+ MUL R1, R2
+ MOVW R2, 12(R13) // tv_nsec
+
+ MOVW $4(R13), R0 // arg 1 - rqtp
+ MOVW $0, R1 // arg 2 - rmtp
+ SWI $240 // sys_nanosleep
+ RET
+
+TEXT runtime·sysctl(SB),7,$0
+ MOVW 0(FP), R0 // arg 1 - name
+ MOVW 4(FP), R1 // arg 2 - namelen
+ MOVW 8(FP), R2 // arg 3 - oldp
+ MOVW 12(FP), R3 // arg 4 - oldlenp
+ // arg 5 (newp) and arg 6 (newlen) are passed on stack
+ ADD $20, R13
+ SWI $202 // sys___sysctl
+ SUB.CS $0, R0, R0
+ SUB $20, R13
+ RET
+
+TEXT runtime·osyield(SB),7,$-4
+ SWI $331 // sys_sched_yield
+ RET
+
+TEXT runtime·sigprocmask(SB),7,$0
+ MOVW $3, R0 // arg 1 - how (SIG_SETMASK)
+ MOVW 0(FP), R1 // arg 2 - set
+ MOVW 4(FP), R2 // arg 3 - oset
+ SWI $340 // sys_sigprocmask
+ MOVW.CS $0, R9 // crash on syscall failure
+ MOVW.CS R9, (R9)
+ RET
+
+TEXT runtime·casp(SB),7,$0
+ B runtime·cas(SB)
+
+// TODO(minux): this is only valid for ARMv6+
+// bool armcas(int32 *val, int32 old, int32 new)
+// Atomically:
+// if(*val == old){
+// *val = new;
+// return 1;
+// }else
+// return 0;
+TEXT runtime·cas(SB),7,$0
+ B runtime·armcas(SB)
diff --git a/src/pkg/runtime/sys_linux_386.s b/src/pkg/runtime/sys_linux_386.s
index 602d9ddac..f27fd4713 100644
--- a/src/pkg/runtime/sys_linux_386.s
+++ b/src/pkg/runtime/sys_linux_386.s
@@ -104,40 +104,38 @@ TEXT runtime·mincore(SB),7,$0-24
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
- MOVL $78, AX // syscall - gettimeofday
- LEAL 8(SP), BX
- MOVL $0, CX
+ MOVL $265, AX // syscall - clock_gettime
+ MOVL $0, BX
+ LEAL 8(SP), CX
MOVL $0, DX
CALL *runtime·_vdso(SB)
MOVL 8(SP), AX // sec
- MOVL 12(SP), BX // usec
+ MOVL 12(SP), BX // nsec
- // sec is in AX, usec in BX
+ // sec is in AX, nsec in BX
MOVL AX, sec+0(FP)
MOVL $0, sec+4(FP)
- IMULL $1000, BX
MOVL BX, nsec+8(FP)
RET
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
TEXT runtime·nanotime(SB), 7, $32
- MOVL $78, AX // syscall - gettimeofday
- LEAL 8(SP), BX
- MOVL $0, CX
+ MOVL $265, AX // syscall - clock_gettime
+ MOVL $0, BX
+ LEAL 8(SP), CX
MOVL $0, DX
CALL *runtime·_vdso(SB)
MOVL 8(SP), AX // sec
- MOVL 12(SP), BX // usec
+ MOVL 12(SP), BX // nsec
- // sec is in AX, usec in BX
+ // sec is in AX, nsec in BX
// convert to DX:AX nsec
MOVL $1000000000, CX
MULL CX
- IMULL $1000, BX
ADDL BX, AX
ADCL $0, DX
-
+
MOVL ret+0(FP), DI
MOVL AX, 0(DI)
MOVL DX, 4(DI)
@@ -170,8 +168,11 @@ TEXT runtime·sigtramp(SB),7,$44
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
- JNE 2(PC)
+ JNE 5(PC)
+ MOVL sig+0(FP), BX
+ MOVL BX, 0(SP)
CALL runtime·badsignal(SB)
+ RET
// save g
MOVL g(CX), DI
@@ -240,9 +241,7 @@ TEXT runtime·madvise(SB),7,$0
MOVL 8(SP), CX
MOVL 12(SP), DX
CALL *runtime·_vdso(SB)
- CMPL AX, $0xfffff001
- JLS 2(PC)
- INT $3
+ // ignore failure - maybe pages are locked
RET
// int32 futex(int32 *uaddr, int32 op, int32 val,
@@ -258,7 +257,7 @@ TEXT runtime·futex(SB),7,$0
CALL *runtime·_vdso(SB)
RET
-// int32 clone(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
+// int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
TEXT runtime·clone(SB),7,$0
MOVL $120, AX // clone
MOVL flags+4(SP), BX
@@ -266,7 +265,7 @@ TEXT runtime·clone(SB),7,$0
MOVL $0, DX // parent tid ptr
MOVL $0, DI // child tid ptr
- // Copy m, g, fn off parent stack for use by child.
+ // Copy mp, gp, fn off parent stack for use by child.
SUBL $16, CX
MOVL mm+12(SP), SI
MOVL SI, 0(CX)
@@ -423,3 +422,11 @@ TEXT runtime·osyield(SB),7,$0
MOVL $158, AX
CALL *runtime·_vdso(SB)
RET
+
+TEXT runtime·sched_getaffinity(SB),7,$0
+ MOVL $242, AX // syscall - sched_getaffinity
+ MOVL 4(SP), BX
+ MOVL 8(SP), CX
+ MOVL 12(SP), DX
+ CALL *runtime·_vdso(SB)
+ RET
diff --git a/src/pkg/runtime/sys_linux_amd64.s b/src/pkg/runtime/sys_linux_amd64.s
index 657ab7e0b..e45943758 100644
--- a/src/pkg/runtime/sys_linux_amd64.s
+++ b/src/pkg/runtime/sys_linux_amd64.s
@@ -101,32 +101,61 @@ TEXT runtime·mincore(SB),7,$0-24
RET
// func now() (sec int64, nsec int32)
-TEXT time·now(SB), 7, $32
- LEAQ 8(SP), DI
- MOVQ $0, SI
- MOVQ $0xffffffffff600000, AX
+TEXT time·now(SB),7,$16
+ // Be careful. We're calling a function with gcc calling convention here.
+ // We're guaranteed 128 bytes on entry, and we've taken 16, and the
+ // call uses another 8.
+ // That leaves 104 for the gettime code to use. Hope that's enough!
+ MOVQ runtime·__vdso_clock_gettime_sym(SB), AX
+ CMPQ AX, $0
+ JEQ fallback_gtod
+ MOVL $0, DI // CLOCK_REALTIME
+ LEAQ 0(SP), SI
CALL AX
- MOVQ 8(SP), AX // sec
- MOVL 16(SP), DX // usec
-
- // sec is in AX, usec in DX
+ MOVQ 0(SP), AX // sec
+ MOVQ 8(SP), DX // nsec
MOVQ AX, sec+0(FP)
- IMULQ $1000, DX
MOVL DX, nsec+8(FP)
RET
-
-TEXT runtime·nanotime(SB), 7, $32
- LEAQ 8(SP), DI
+fallback_gtod:
+ LEAQ 0(SP), DI
MOVQ $0, SI
- MOVQ $0xffffffffff600000, AX
+ MOVQ runtime·__vdso_gettimeofday_sym(SB), AX
CALL AX
- MOVQ 8(SP), AX // sec
- MOVL 16(SP), DX // usec
+ MOVQ 0(SP), AX // sec
+ MOVL 8(SP), DX // usec
+ IMULQ $1000, DX
+ MOVQ AX, sec+0(FP)
+ MOVL DX, nsec+8(FP)
+ RET
- // sec is in AX, usec in DX
+TEXT runtime·nanotime(SB),7,$16
+ // Duplicate time.now here to avoid using up precious stack space.
+ // See comment above in time.now.
+ MOVQ runtime·__vdso_clock_gettime_sym(SB), AX
+ CMPQ AX, $0
+ JEQ fallback_gtod_nt
+ MOVL $0, DI // CLOCK_REALTIME
+ LEAQ 0(SP), SI
+ CALL AX
+ MOVQ 0(SP), AX // sec
+ MOVQ 8(SP), DX // nsec
+ // sec is in AX, nsec in DX
// return nsec in AX
IMULQ $1000000000, AX
+ ADDQ DX, AX
+ RET
+fallback_gtod_nt:
+ LEAQ 0(SP), DI
+ MOVQ $0, SI
+ MOVQ runtime·__vdso_gettimeofday_sym(SB), AX
+ CALL AX
+ MOVQ 0(SP), AX // sec
+ MOVL 8(SP), DX // usec
IMULQ $1000, DX
+ // sec is in AX, nsec in DX
+ // return nsec in AX
+ IMULQ $1000000000, AX
ADDQ DX, AX
RET
@@ -157,8 +186,10 @@ TEXT runtime·sigtramp(SB),7,$64
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
- JNE 2(PC)
+ JNE 4(PC)
+ MOVQ DI, 0(SP)
CALL runtime·badsignal(SB)
+ RET
// save g
MOVQ g(BX), R10
@@ -219,9 +250,7 @@ TEXT runtime·madvise(SB),7,$0
MOVQ 24(SP), DX
MOVQ $28, AX // madvise
SYSCALL
- CMPQ AX, $0xfffffffffffff001
- JLS 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ // ignore failure - maybe pages are locked
RET
// int64 futex(int32 *uaddr, int32 op, int32 val,
@@ -237,12 +266,12 @@ TEXT runtime·futex(SB),7,$0
SYSCALL
RET
-// int64 clone(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
+// int64 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
TEXT runtime·clone(SB),7,$0
MOVL flags+8(SP), DI
MOVQ stack+16(SP), SI
- // Copy m, g, fn off parent stack for use by child.
+ // Copy mp, gp, fn off parent stack for use by child.
// Careful: Linux system call clobbers CX and R11.
MOVQ mm+24(SP), R8
MOVQ gg+32(SP), R9
@@ -310,3 +339,11 @@ TEXT runtime·osyield(SB),7,$0
MOVL $24, AX
SYSCALL
RET
+
+TEXT runtime·sched_getaffinity(SB),7,$0
+ MOVQ 8(SP), DI
+ MOVL 16(SP), SI
+ MOVQ 24(SP), DX
+ MOVL $204, AX // syscall entry
+ SYSCALL
+ RET
diff --git a/src/pkg/runtime/sys_linux_arm.s b/src/pkg/runtime/sys_linux_arm.s
index 03e173d26..8bae2933f 100644
--- a/src/pkg/runtime/sys_linux_arm.s
+++ b/src/pkg/runtime/sys_linux_arm.s
@@ -34,9 +34,10 @@
#define SYS_sched_yield (SYS_BASE + 158)
#define SYS_select (SYS_BASE + 142) // newselect
#define SYS_ugetrlimit (SYS_BASE + 191)
+#define SYS_sched_getaffinity (SYS_BASE + 242)
+#define SYS_clock_gettime (SYS_BASE + 263)
#define ARM_BASE (SYS_BASE + 0x0f0000)
-#define SYS_ARM_cacheflush (ARM_BASE + 2)
TEXT runtime·open(SB),7,$0
MOVW 0(FP), R0
@@ -131,10 +132,7 @@ TEXT runtime·madvise(SB),7,$0
MOVW 8(FP), R2
MOVW $SYS_madvise, R7
SWI $0
- MOVW $0xfffff001, R6
- CMP R6, R0
- MOVW.HI $0, R9 // crash on syscall failure
- MOVW.HI R9, (R9)
+ // ignore failure - maybe pages are locked
RET
TEXT runtime·setitimer(SB),7,$0
@@ -154,41 +152,37 @@ TEXT runtime·mincore(SB),7,$0
RET
TEXT time·now(SB), 7, $32
- MOVW $8(R13), R0 // timeval
- MOVW $0, R1 // zone
- MOVW $SYS_gettimeofday, R7
+ MOVW $0, R0 // CLOCK_REALTIME
+ MOVW $8(R13), R1 // timespec
+ MOVW $SYS_clock_gettime, R7
SWI $0
MOVW 8(R13), R0 // sec
- MOVW 12(R13), R2 // usec
+ MOVW 12(R13), R2 // nsec
MOVW R0, 0(FP)
MOVW $0, R1
MOVW R1, 4(FP)
- MOVW $1000, R3
- MUL R3, R2
MOVW R2, 8(FP)
RET
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
TEXT runtime·nanotime(SB),7,$32
- MOVW $8(R13), R0 // timeval
- MOVW $0, R1 // zone
- MOVW $SYS_gettimeofday, R7
+ MOVW $0, R0 // CLOCK_REALTIME
+ MOVW $8(R13), R1 // timespec
+ MOVW $SYS_clock_gettime, R7
SWI $0
MOVW 8(R13), R0 // sec
- MOVW 12(R13), R2 // usec
+ MOVW 12(R13), R2 // nsec
MOVW $1000000000, R3
MULLU R0, R3, (R1, R0)
- MOVW $1000, R3
MOVW $0, R4
- MUL R3, R2
ADD.S R2, R0
ADC R4, R1
-
+
MOVW 0(FP), R3
MOVW R0, 0(R3)
MOVW R1, 4(R3)
@@ -208,7 +202,7 @@ TEXT runtime·futex(SB),7,$0
RET
-// int32 clone(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
+// int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
TEXT runtime·clone(SB),7,$0
MOVW flags+0(FP), R0
MOVW stack+4(FP), R1
@@ -217,7 +211,7 @@ TEXT runtime·clone(SB),7,$0
MOVW $0, R4 // child tid ptr
MOVW $0, R5
- // Copy m, g, fn off parent stack for use by child.
+ // Copy mp, gp, fn off parent stack for use by child.
// TODO(kaib): figure out which registers are clobbered by clone and avoid stack copying
MOVW $-16(R1), R1
MOVW mm+8(FP), R6
@@ -272,15 +266,6 @@ TEXT runtime·clone(SB),7,$0
MOVW $1005, R1
MOVW R0, (R1)
-
-TEXT runtime·cacheflush(SB),7,$0
- MOVW 0(FP), R0
- MOVW 4(FP), R1
- MOVW $0, R2
- MOVW $SYS_ARM_cacheflush, R7
- SWI $0
- RET
-
TEXT runtime·sigaltstack(SB),7,$0
MOVW 0(FP), R0
MOVW 4(FP), R1
@@ -293,6 +278,15 @@ TEXT runtime·sigaltstack(SB),7,$0
RET
TEXT runtime·sigtramp(SB),7,$24
+ // this might be called in external code context,
+ // where g and m are not set.
+ // first save R0, because _cgo_load_gm will clobber it
+ // TODO(adonovan): call runtime·badsignal if m=0, like other platforms?
+ MOVW R0, 4(R13)
+ MOVW _cgo_load_gm(SB), R0
+ CMP $0, R0
+ BL.NE (R0)
+
// save g
MOVW g, R3
MOVW g, 20(R13)
@@ -301,7 +295,7 @@ TEXT runtime·sigtramp(SB),7,$24
MOVW m_gsignal(m), g
// copy arguments for call to sighandler
- MOVW R0, 4(R13)
+ // R0 is already saved above
MOVW R1, 8(R13)
MOVW R2, 12(R13)
MOVW R3, 16(R13)
@@ -385,3 +379,11 @@ TEXT runtime·osyield(SB),7,$0
MOVW $SYS_sched_yield, R7
SWI $0
RET
+
+TEXT runtime·sched_getaffinity(SB),7,$0
+ MOVW 0(FP), R0
+ MOVW 4(FP), R1
+ MOVW 8(FP), R2
+ MOVW $SYS_sched_getaffinity, R7
+ SWI $0
+ RET
diff --git a/src/pkg/runtime/sys_netbsd_386.s b/src/pkg/runtime/sys_netbsd_386.s
index 11f8c7aaa..3d3d31273 100644
--- a/src/pkg/runtime/sys_netbsd_386.s
+++ b/src/pkg/runtime/sys_netbsd_386.s
@@ -12,14 +12,14 @@
TEXT runtime·exit(SB),7,$-4
MOVL $1, AX
INT $0x80
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·exit1(SB),7,$-4
- MOVL $302, AX // sys_threxit
+ MOVL $310, AX // sys__lwp_exit
INT $0x80
JAE 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·write(SB),7,$-4
@@ -27,31 +27,32 @@ TEXT runtime·write(SB),7,$-4
INT $0x80
RET
-TEXT runtime·usleep(SB),7,$20
+TEXT runtime·usleep(SB),7,$24
MOVL $0, DX
MOVL usec+0(FP), AX
MOVL $1000000, CX
DIVL CX
- MOVL AX, 12(SP) // tv_sec
+ MOVL AX, 12(SP) // tv_sec - l32
+ MOVL $0, 16(SP) // tv_sec - h32
MOVL $1000, AX
MULL DX
- MOVL AX, 16(SP) // tv_nsec
+ MOVL AX, 20(SP) // tv_nsec
MOVL $0, 0(SP)
LEAL 12(SP), AX
MOVL AX, 4(SP) // arg 1 - rqtp
MOVL $0, 8(SP) // arg 2 - rmtp
- MOVL $240, AX // sys_nanosleep
+ MOVL $430, AX // sys_nanosleep
INT $0x80
RET
TEXT runtime·raisesigpipe(SB),7,$12
- MOVL $299, AX // sys_getthrid
+ MOVL $311, AX // sys__lwp_self
INT $0x80
MOVL $0, 0(SP)
- MOVL AX, 4(SP) // arg 1 - pid
- MOVL $13, 8(SP) // arg 2 - signum == SIGPIPE
- MOVL $37, AX // sys_kill
+ MOVL AX, 4(SP) // arg 1 - target
+ MOVL $13, 8(SP) // arg 2 - signo == SIGPIPE
+ MOVL $318, AX // sys__lwp_kill
INT $0x80
RET
@@ -67,7 +68,7 @@ TEXT runtime·mmap(SB),7,$36
MOVL $0, AX
STOSL // arg 6 - pad
MOVSL // arg 7 - offset
- MOVL $0, AX // top 64 bits of file offset
+ MOVL $0, AX // top 32 bits of file offset
STOSL
MOVL $197, AX // sys_mmap
INT $0x80
@@ -79,60 +80,100 @@ TEXT runtime·munmap(SB),7,$-4
MOVL $73, AX // sys_munmap
INT $0x80
JAE 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·madvise(SB),7,$-4
+ MOVL $75, AX // sys_madvise
+ INT $0x80
+ // ignore failure - maybe pages are locked
RET
TEXT runtime·setitimer(SB),7,$-4
- MOVL $83, AX
+ MOVL $425, AX // sys_setitimer
INT $0x80
RET
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
- MOVL $116, AX
LEAL 12(SP), BX
- MOVL BX, 4(SP)
- MOVL $0, 8(SP)
+ MOVL $0, 4(SP) // arg 1 - clock_id
+ MOVL BX, 8(SP) // arg 2 - tp
+ MOVL $427, AX // sys_clock_gettime
INT $0x80
- MOVL 12(SP), AX // sec
- MOVL 16(SP), BX // usec
- // sec is in AX, usec in BX
+ MOVL 12(SP), AX // sec - l32
MOVL AX, sec+0(FP)
- MOVL $0, sec+4(FP)
- IMULL $1000, BX
+ MOVL 16(SP), AX // sec - h32
+ MOVL AX, sec+4(FP)
+
+ MOVL 20(SP), BX // nsec
MOVL BX, nsec+8(FP)
RET
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
TEXT runtime·nanotime(SB),7,$32
- MOVL $116, AX
LEAL 12(SP), BX
- MOVL BX, 4(SP)
- MOVL $0, 8(SP)
+ MOVL $0, 4(SP) // arg 1 - clock_id
+ MOVL BX, 8(SP) // arg 2 - tp
+ MOVL $427, AX // sys_clock_gettime
INT $0x80
- MOVL 12(SP), AX // sec
- MOVL 16(SP), BX // usec
-
- // sec is in AX, usec in BX
- // convert to DX:AX nsec
- MOVL $1000000000, CX
- MULL CX
- IMULL $1000, BX
+
+ MOVL 16(SP), CX // sec - h32
+ IMULL $1000000000, CX
+
+ MOVL 12(SP), AX // sec - l32
+ MOVL $1000000000, BX
+ MULL BX // result in dx:ax
+
+ MOVL 20(SP), BX // nsec
ADDL BX, AX
- ADCL $0, DX
-
+ ADCL CX, DX // add high bits with carry
+
MOVL ret+0(FP), DI
MOVL AX, 0(DI)
MOVL DX, 4(DI)
RET
-TEXT runtime·sigaction(SB),7,$-4
- MOVL $46, AX // sys_sigaction
+TEXT runtime·getcontext(SB),7,$-4
+ MOVL $307, AX // sys_getcontext
+ INT $0x80
+ JAE 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·sigprocmask(SB),7,$-4
+ MOVL $293, AX // sys_sigprocmask
+ INT $0x80
+ JAE 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·sigreturn_tramp(SB),7,$0
+ LEAL 140(SP), AX // Load address of ucontext
+ MOVL AX, 4(SP)
+ MOVL $308, AX // sys_setcontext
+ INT $0x80
+ MOVL $-1, 4(SP) // Something failed...
+ MOVL $1, AX // sys_exit
+ INT $0x80
+
+TEXT runtime·sigaction(SB),7,$24
+ LEAL arg0+0(FP), SI
+ LEAL 4(SP), DI
+ CLD
+ MOVSL // arg 1 - sig
+ MOVSL // arg 2 - act
+ MOVSL // arg 3 - oact
+ LEAL runtime·sigreturn_tramp(SB), AX
+ STOSL // arg 4 - tramp
+ MOVL $2, AX
+ STOSL // arg 5 - vers
+ MOVL $340, AX // sys___sigaction_sigtramp
INT $0x80
JAE 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·sigtramp(SB),7,$44
@@ -141,13 +182,16 @@ TEXT runtime·sigtramp(SB),7,$44
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
- JNE 2(PC)
+ JNE 5(PC)
+ MOVL signo+0(FP), BX
+ MOVL BX, 0(SP)
CALL runtime·badsignal(SB)
+ RET
// save g
MOVL g(CX), DI
MOVL DI, 20(SP)
-
+
// g = m->gsignal
MOVL m_gsignal(BX), BX
MOVL BX, g(CX)
@@ -167,67 +211,24 @@ TEXT runtime·sigtramp(SB),7,$44
get_tls(CX)
MOVL 20(SP), BX
MOVL BX, g(CX)
-
- // call sigreturn
- MOVL context+8(FP), AX
- MOVL $0, 0(SP) // syscall gap
- MOVL AX, 4(SP) // arg 1 - sigcontext
- MOVL $103, AX // sys_sigreturn
- INT $0x80
- MOVL $0xf1, 0xf1 // crash
RET
-// int32 rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
-TEXT runtime·rfork_thread(SB),7,$8
- MOVL flags+8(SP), AX
- MOVL stack+12(SP), CX
-
- // Copy m, g, fn off parent stack for use by child.
- SUBL $16, CX
- MOVL mm+16(SP), SI
- MOVL SI, 0(CX)
- MOVL gg+20(SP), SI
- MOVL SI, 4(CX)
- MOVL fn+24(SP), SI
- MOVL SI, 8(CX)
- MOVL $1234, 12(CX)
- MOVL CX, SI
-
- MOVL $0, 0(SP) // syscall gap
- MOVL AX, 4(SP) // arg 1 - flags
- MOVL $251, AX // sys_rfork
+// int32 lwp_create(void *context, uintptr flags, void *lwpid);
+TEXT runtime·lwp_create(SB),7,$16
+ MOVL $0, 0(SP)
+ MOVL context+0(FP), AX
+ MOVL AX, 4(SP) // arg 1 - context
+ MOVL flags+4(FP), AX
+ MOVL AX, 8(SP) // arg 2 - flags
+ MOVL lwpid+8(FP), AX
+ MOVL AX, 12(SP) // arg 3 - lwpid
+ MOVL $309, AX // sys__lwp_create
INT $0x80
-
- // Return if rfork syscall failed
- JCC 4(PC)
+ JCC 2(PC)
NEGL AX
- MOVL AX, 48(SP)
RET
- // In parent, return.
- CMPL AX, $0
- JEQ 3(PC)
- MOVL AX, 48(SP)
- RET
-
- // In child, on new stack.
- MOVL SI, SP
-
- // Paranoia: check that SP is as we expect.
- MOVL 12(SP), BP
- CMPL BP, $1234
- JEQ 2(PC)
- INT $3
-
- // Reload registers
- MOVL 0(SP), BX // m
- MOVL 4(SP), DX // g
- MOVL 8(SP), SI // fn
-
- // Initialize m->procid to thread ID
- MOVL $299, AX // sys_getthrid
- INT $0x80
- MOVL AX, m_procid(BX)
+TEXT runtime·lwp_tramp(SB),7,$0
// Set FS to point at m->tls
LEAL m_tls(BX), BP
@@ -236,7 +237,7 @@ TEXT runtime·rfork_thread(SB),7,$8
CALL runtime·settls(SB)
POPL AX
POPAL
-
+
// Now segment is established. Initialize m, g.
get_tls(AX)
MOVL DX, g(AX)
@@ -259,7 +260,7 @@ TEXT runtime·rfork_thread(SB),7,$8
RET
TEXT runtime·sigaltstack(SB),7,$-8
- MOVL $288, AX // sys_sigaltstack
+ MOVL $281, AX // sys___sigaltstack14
MOVL new+4(SP), BX
MOVL old+8(SP), CX
INT $0x80
@@ -277,30 +278,33 @@ TEXT runtime·setldt(SB),7,$8
TEXT runtime·settls(SB),7,$16
// adjust for ELF: wants to use -8(GS) and -4(GS) for g and m
- MOVL 20(SP), CX
+ MOVL base+0(FP), CX
ADDL $8, CX
- MOVL CX, 0(CX)
MOVL $0, 0(SP) // syscall gap
- MOVL $9, 4(SP) // I386_SET_GSBASE (machine/sysarch.h)
- MOVL CX, 8(SP) // pointer to base
- MOVL $165, AX // sys_sysarch
+ MOVL CX, 4(SP) // arg 1 - ptr
+ MOVL $317, AX // sys__lwp_setprivate
INT $0x80
JCC 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·osyield(SB),7,$-4
- MOVL $298, AX // sys_sched_yield
+ MOVL $350, AX // sys_sched_yield
+ INT $0x80
+ RET
+
+TEXT runtime·lwp_park(SB),7,$-4
+ MOVL $434, AX // sys__lwp_park
INT $0x80
RET
-TEXT runtime·thrsleep(SB),7,$-4
- MOVL $300, AX // sys_thrsleep
+TEXT runtime·lwp_unpark(SB),7,$-4
+ MOVL $321, AX // sys__lwp_unpark
INT $0x80
RET
-TEXT runtime·thrwakeup(SB),7,$-4
- MOVL $301, AX // sys_thrwakeup
+TEXT runtime·lwp_self(SB),7,$-4
+ MOVL $311, AX // sys__lwp_self
INT $0x80
RET
diff --git a/src/pkg/runtime/sys_netbsd_amd64.s b/src/pkg/runtime/sys_netbsd_amd64.s
index 0b83cd4d8..e73e83ded 100644
--- a/src/pkg/runtime/sys_netbsd_amd64.s
+++ b/src/pkg/runtime/sys_netbsd_amd64.s
@@ -8,42 +8,24 @@
#include "zasm_GOOS_GOARCH.h"
-// int64 rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
-TEXT runtime·rfork_thread(SB),7,$0
- MOVL flags+8(SP), DI
- MOVQ stack+16(SP), SI
-
- // Copy m, g, fn off parent stack for use by child.
- MOVQ mm+24(SP), R8
- MOVQ gg+32(SP), R9
- MOVQ fn+40(SP), R12
-
- MOVL $251, AX // sys_rfork
+// int32 lwp_create(void *context, uintptr flags, void *lwpid)
+TEXT runtime·lwp_create(SB),7,$0
+ MOVQ context+0(FP), DI
+ MOVQ flags+8(FP), SI
+ MOVQ lwpid+16(FP), DX
+ MOVL $309, AX // sys__lwp_create
SYSCALL
-
- // Return if rfork syscall failed
- JCC 3(PC)
+ JCC 2(PC)
NEGL AX
RET
- // In parent, return.
- CMPL AX, $0
- JEQ 2(PC)
- RET
-
- // In child, on new stack.
- MOVQ SI, SP
-
- // Initialize m->procid to thread ID
- MOVL $299, AX // sys_getthrid
- SYSCALL
- MOVQ AX, m_procid(R8)
-
+TEXT runtime·lwp_tramp(SB),7,$0
+
// Set FS to point at m->tls.
LEAQ m_tls(R8), DI
CALL runtime·settls(SB)
- // In child, set up new stack
+ // Set up new stack.
get_tls(CX)
MOVQ R8, m(CX)
MOVQ R9, g(CX)
@@ -52,29 +34,34 @@ TEXT runtime·rfork_thread(SB),7,$0
// Call fn
CALL R12
- // It shouldn't return. If it does, exit
- MOVL $302, AX // sys_threxit
+ // It shouldn't return. If it does, exit.
+ MOVL $310, AX // sys__lwp_exit
SYSCALL
JMP -3(PC) // keep exiting
TEXT runtime·osyield(SB),7,$0
- MOVL $298, AX // sys_sched_yield
+ MOVL $350, AX // sys_sched_yield
SYSCALL
RET
-TEXT runtime·thrsleep(SB),7,$0
- MOVQ 8(SP), DI // arg 1 - ident
- MOVL 16(SP), SI // arg 2 - clock_id
- MOVQ 24(SP), DX // arg 3 - tp
- MOVQ 32(SP), R10 // arg 4 - lock
- MOVL $300, AX // sys_thrsleep
+TEXT runtime·lwp_park(SB),7,$0
+ MOVQ 8(SP), DI // arg 1 - abstime
+ MOVL 16(SP), SI // arg 2 - unpark
+ MOVQ 24(SP), DX // arg 3 - hint
+ MOVQ 32(SP), R10 // arg 4 - unparkhint
+ MOVL $434, AX // sys__lwp_park
SYSCALL
RET
-TEXT runtime·thrwakeup(SB),7,$0
- MOVQ 8(SP), DI // arg 1 - ident
- MOVL 16(SP), SI // arg 2 - n
- MOVL $301, AX // sys_thrwakeup
+TEXT runtime·lwp_unpark(SB),7,$0
+ MOVQ 8(SP), DI // arg 1 - lwp
+ MOVL 16(SP), SI // arg 2 - hint
+ MOVL $321, AX // sys__lwp_unpark
+ SYSCALL
+ RET
+
+TEXT runtime·lwp_self(SB),7,$0
+ MOVL $311, AX // sys__lwp_self
SYSCALL
RET
@@ -83,13 +70,13 @@ TEXT runtime·exit(SB),7,$-8
MOVL 8(SP), DI // arg 1 - exit status
MOVL $1, AX // sys_exit
SYSCALL
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·exit1(SB),7,$-8
- MOVL $302, AX // sys_threxit
+ MOVL $310, AX // sys__lwp_exit
SYSCALL
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·write(SB),7,$-8
@@ -112,16 +99,16 @@ TEXT runtime·usleep(SB),7,$16
MOVQ SP, DI // arg 1 - rqtp
MOVQ $0, SI // arg 2 - rmtp
- MOVL $240, AX // sys_nanosleep
+ MOVL $430, AX // sys_nanosleep
SYSCALL
RET
TEXT runtime·raisesigpipe(SB),7,$16
- MOVL $299, AX // sys_getthrid
+ MOVL $311, AX // sys__lwp_self
SYSCALL
- MOVQ AX, DI // arg 1 - pid
- MOVQ $13, SI // arg 2 - signum == SIGPIPE
- MOVL $37, AX // sys_kill
+ MOVQ AX, DI // arg 1 - target
+ MOVQ $13, SI // arg 2 - signo == SIGPIPE
+ MOVL $318, AX // sys__lwp_kill
SYSCALL
RET
@@ -129,72 +116,101 @@ TEXT runtime·setitimer(SB),7,$-8
MOVL 8(SP), DI // arg 1 - which
MOVQ 16(SP), SI // arg 2 - itv
MOVQ 24(SP), DX // arg 3 - oitv
- MOVL $83, AX // sys_setitimer
+ MOVL $425, AX // sys_setitimer
SYSCALL
RET
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
- LEAQ 8(SP), DI // arg 1 - tp
- MOVQ $0, SI // arg 2 - tzp
- MOVL $116, AX // sys_gettimeofday
+ MOVQ $0, DI // arg 1 - clock_id
+ LEAQ 8(SP), SI // arg 2 - tp
+ MOVL $427, AX // sys_clock_gettime
SYSCALL
MOVQ 8(SP), AX // sec
- MOVL 16(SP), DX // usec
+ MOVL 16(SP), DX // nsec
- // sec is in AX, usec in DX
+ // sec is in AX, nsec in DX
MOVQ AX, sec+0(FP)
- IMULQ $1000, DX
MOVL DX, nsec+8(FP)
RET
TEXT runtime·nanotime(SB),7,$32
- LEAQ 8(SP), DI // arg 1 - tp
- MOVQ $0, SI // arg 2 - tzp
- MOVL $116, AX // sys_gettimeofday
+ MOVQ $0, DI // arg 1 - clock_id
+ LEAQ 8(SP), SI // arg 2 - tp
+ MOVL $427, AX // sys_clock_gettime
SYSCALL
MOVQ 8(SP), AX // sec
- MOVL 16(SP), DX // usec
+ MOVL 16(SP), DX // nsec
- // sec is in AX, usec in DX
+ // sec is in AX, nsec in DX
// return nsec in AX
IMULQ $1000000000, AX
- IMULQ $1000, DX
ADDQ DX, AX
RET
+TEXT runtime·getcontext(SB),7,$-8
+ MOVQ 8(SP), DI // arg 1 - context
+ MOVL $307, AX // sys_getcontext
+ SYSCALL
+ JCC 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·sigprocmask(SB),7,$0
+ MOVL 8(SP), DI // arg 1 - how
+ MOVQ 16(SP), SI // arg 2 - set
+ MOVQ 24(SP), DX // arg 3 - oset
+ MOVL $293, AX // sys_sigprocmask
+ SYSCALL
+ JCC 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·sigreturn_tramp(SB),7,$-8
+ MOVQ R15, DI // Load address of ucontext
+ MOVQ $308, AX // sys_setcontext
+ SYSCALL
+ MOVQ $-1, DI // Something failed...
+ MOVL $1, AX // sys_exit
+ SYSCALL
+
TEXT runtime·sigaction(SB),7,$-8
MOVL 8(SP), DI // arg 1 - signum
MOVQ 16(SP), SI // arg 2 - nsa
MOVQ 24(SP), DX // arg 3 - osa
- MOVL $46, AX
+ // arg 4 - tramp
+ LEAQ runtime·sigreturn_tramp(SB), R10
+ MOVQ $2, R8 // arg 5 - vers
+ MOVL $340, AX // sys___sigaction_sigtramp
SYSCALL
JCC 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·sigtramp(SB),7,$64
get_tls(BX)
-
+
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
- JNE 2(PC)
+ JNE 4(PC)
+ MOVQ DI, 0(SP)
CALL runtime·badsignal(SB)
+ RET
// save g
MOVQ g(BX), R10
MOVQ R10, 40(SP)
-
+
// g = m->signal
MOVQ m_gsignal(BP), BP
MOVQ BP, g(BX)
-
+
MOVQ DI, 0(SP)
MOVQ SI, 8(SP)
MOVQ DX, 16(SP)
MOVQ R10, 24(SP)
-
+
CALL runtime·sighandler(SB)
// restore g
@@ -213,7 +229,7 @@ TEXT runtime·mmap(SB),7,$0
SUBQ $16, SP
MOVQ R9, 8(SP) // arg 7 - offset (passed on stack)
MOVQ $0, R9 // arg 6 - pad
- MOVL $197, AX
+ MOVL $197, AX // sys_mmap
SYSCALL
JCC 2(PC)
NEGL AX
@@ -226,29 +242,36 @@ TEXT runtime·munmap(SB),7,$0
MOVL $73, AX // sys_munmap
SYSCALL
JCC 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+
+TEXT runtime·madvise(SB),7,$0
+ MOVQ addr+0(FP), DI // arg 1 - addr
+ MOVQ len+8(FP), SI // arg 2 - len
+ MOVQ behav+16(FP), DX // arg 3 - behav
+ MOVQ $75, AX // sys_madvise
+ SYSCALL
+ // ignore failure - maybe pages are locked
RET
TEXT runtime·sigaltstack(SB),7,$-8
MOVQ new+8(SP), DI // arg 1 - nss
MOVQ old+16(SP), SI // arg 2 - oss
- MOVQ $288, AX // sys_sigaltstack
+ MOVQ $281, AX // sys___sigaltstack14
SYSCALL
JCC 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
// set tls base to DI
TEXT runtime·settls(SB),7,$8
// adjust for ELF: wants to use -16(FS) and -8(FS) for g and m
- ADDQ $16, DI
- MOVQ DI, 0(SP)
- MOVQ SP, SI
- MOVQ $12, DI // AMD64_SET_FSBASE (machine/sysarch.h)
- MOVQ $165, AX // sys_sysarch
+ ADDQ $16, DI // arg 1 - ptr
+ MOVQ $317, AX // sys__lwp_setprivate
SYSCALL
JCC 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·sysctl(SB),7,$0
diff --git a/src/pkg/runtime/sys_netbsd_arm.s b/src/pkg/runtime/sys_netbsd_arm.s
new file mode 100644
index 000000000..4a119c5de
--- /dev/null
+++ b/src/pkg/runtime/sys_netbsd_arm.s
@@ -0,0 +1,280 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// System calls and other sys.stuff for ARM, NetBSD
+// /usr/src/sys/kern/syscalls.master for syscall numbers.
+//
+
+#include "zasm_GOOS_GOARCH.h"
+
+// Exit the entire program (like C exit)
+TEXT runtime·exit(SB),7,$-4
+ MOVW 0(FP), R0 // arg 1 exit status
+ SWI $0xa00001
+ MOVW.CS $0, R9 // crash on syscall failure
+ MOVW.CS R9, (R9)
+ RET
+
+TEXT runtime·exit1(SB),7,$-4
+ SWI $0xa00136 // sys__lwp_exit
+ MOVW $1, R9 // crash
+ MOVW R9, (R9)
+ RET
+
+TEXT runtime·write(SB),7,$-4
+ MOVW 0(FP), R0 // arg 1 - fd
+ MOVW 4(FP), R1 // arg 2 - buf
+ MOVW 8(FP), R2 // arg 3 - nbyte
+ SWI $0xa00004 // sys_write
+ RET
+
+// int32 lwp_create(void *context, uintptr flags, void *lwpid)
+TEXT runtime·lwp_create(SB),7,$0
+ MOVW context+0(FP), R0
+ MOVW flags+4(FP), R1
+ MOVW lwpid+8(FP), R2
+ SWI $0xa00135 // sys__lwp_create
+ RET
+
+TEXT runtime·osyield(SB),7,$0
+ SWI $0xa0015e // sys_sched_yield
+ RET
+
+TEXT runtime·lwp_park(SB),7,$0
+ MOVW 0(FP), R0 // arg 1 - abstime
+ MOVW 4(FP), R1 // arg 2 - unpark
+ MOVW 8(FP), R2 // arg 3 - hint
+ MOVW 12(FP), R3 // arg 4 - unparkhint
+ SWI $0xa001b2 // sys__lwp_park
+ RET
+
+TEXT runtime·lwp_unpark(SB),7,$0
+ MOVW 0(FP), R0 // arg 1 - lwp
+ MOVW 4(FP), R1 // arg 2 - hint
+ SWI $0xa00141 // sys__lwp_unpark
+ RET
+
+TEXT runtime·lwp_self(SB),7,$0
+ SWI $0xa00137 // sys__lwp_self
+ RET
+
+TEXT runtime·lwp_tramp(SB),7,$0
+ MOVW R0, R9 // m
+ MOVW R1, R10 // g
+
+ BL runtime·emptyfunc(SB) // fault if stack check is wrong
+ BL (R2)
+ MOVW $2, R9 // crash (not reached)
+ MOVW R9, (R9)
+ RET
+
+TEXT runtime·usleep(SB),7,$16
+ MOVW usec+0(FP), R0
+ MOVW R0, R2
+ MOVW $1000000, R1
+ DIV R1, R0
+ // 0(R13) is the saved LR, don't use it
+ MOVW R0, 4(R13) // tv_sec.low
+ MOVW $0, R0
+ MOVW R0, 8(R13) // tv_sec.high
+ MOD R1, R2
+ MOVW $1000, R1
+ MUL R1, R2
+ MOVW R2, 12(R13) // tv_nsec
+
+ MOVW $4(R13), R0 // arg 1 - rqtp
+ MOVW $0, R1 // arg 2 - rmtp
+ SWI $0xa001ae // sys_nanosleep
+ RET
+
+TEXT runtime·raisesigpipe(SB),7,$16
+ SWI $0xa00137 // sys__lwp_self, the returned R0 is arg 1
+ MOVW $13, R1 // arg 2 - signo == SIGPIPE
+ SWI $0xa0013e // sys__lwp_kill
+ RET
+
+TEXT runtime·setitimer(SB),7,$-4
+ MOVW 0(FP), R0 // arg 1 - which
+ MOVW 4(FP), R1 // arg 2 - itv
+ MOVW 8(FP), R2 // arg 3 - oitv
+ SWI $0xa001a9 // sys_setitimer
+ RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB), 7, $32
+ MOVW $0, R0 // CLOCK_REALTIME
+ MOVW $8(R13), R1
+ SWI $0xa001ab // clock_gettime
+
+ MOVW 8(R13), R0 // sec.low
+ MOVW 12(R13), R1 // sec.high
+ MOVW 16(R13), R2 // nsec
+
+ MOVW R0, 0(FP)
+ MOVW R1, 4(FP)
+ MOVW R2, 8(FP)
+ RET
+
+// int64 nanotime(void) so really
+// void nanotime(int64 *nsec)
+TEXT runtime·nanotime(SB), 7, $32
+ MOVW $0, R0 // CLOCK_REALTIME
+ MOVW $8(R13), R1
+ SWI $0xa001ab // clock_gettime
+
+ MOVW 8(R13), R0 // sec.low
+ MOVW 12(R13), R4 // sec.high
+ MOVW 16(R13), R2 // nsec
+
+ MOVW $1000000000, R3
+ MULLU R0, R3, (R1, R0)
+ MUL R3, R4
+ ADD.S R2, R0
+ ADC R4, R1
+
+ MOVW 0(FP), R3
+ MOVW R0, 0(R3)
+ MOVW R1, 4(R3)
+ RET
+
+TEXT runtime·getcontext(SB),7,$-4
+ MOVW 0(FP), R0 // arg 1 - context
+ SWI $0xa00133 // sys_getcontext
+ MOVW.CS $0, R9 // crash on syscall failure
+ MOVW.CS R9, (R9)
+ RET
+
+TEXT runtime·sigprocmask(SB),7,$0
+ MOVW 0(FP), R0 // arg 1 - how
+ MOVW 4(FP), R1 // arg 2 - set
+ MOVW 8(FP), R2 // arg 3 - oset
+ SWI $0xa00125 // sys_sigprocmask
+ MOVW.CS $0, R9 // crash on syscall failure
+ MOVW.CS R9, (R9)
+ RET
+
+TEXT runtime·sigreturn_tramp(SB),7,$-4
+ // in runtime·sigtramp, we saved ucontext into m->tls[0],
+ // here we just load it and call sys_setcontext
+ MOVW m_tls(m), R0
+ SWI $0xa00134 // sys_setcontext
+ // something failed, we have to exit
+ MOVW $0x4242, R0 // magic return number
+ SWI $0xa00001 // sys_exit
+ B -2(PC) // continue exit
+
+TEXT runtime·sigaction(SB),7,$4
+ MOVW 0(FP), R0 // arg 1 - signum
+ MOVW 4(FP), R1 // arg 2 - nsa
+ MOVW 8(FP), R2 // arg 3 - osa
+ MOVW $runtime·sigreturn_tramp(SB), R3 // arg 4 - tramp
+ MOVW $2, R4 // arg 5 - vers
+ MOVW R4, 4(R13)
+ ADD $4, R13 // pass arg 5 on stack
+ SWI $0xa00154 // sys___sigaction_sigtramp
+ SUB $4, R13
+ MOVW.CS $3, R9 // crash on syscall failure
+ MOVW.CS R9, (R9)
+ RET
+
+TEXT runtime·sigtramp(SB),7,$24
+ // this might be called in external code context,
+ // where g and m are not set.
+ // first save R0, because _cgo_load_gm will clobber it
+ // TODO(adonovan): call runtime·badsignal if m=0, like other platforms?
+ MOVW R0, 4(R13) // signum
+ MOVW _cgo_load_gm(SB), R0
+ CMP $0, R0
+ BL.NE (R0)
+
+ // save g
+ MOVW R10, R4
+ MOVW R10, 20(R13)
+
+ // g = m->signal
+ MOVW m_gsignal(R9), R10
+
+ // R0 is already saved
+ MOVW R1, 8(R13) // info
+ MOVW R2, 12(R13) // context
+ MOVW R4, 16(R13) // gp
+ // we also save the ucontext into m->tls[0] for easy
+ // signal return
+ MOVW R2, m_tls(m)
+
+ BL runtime·sighandler(SB)
+
+ // restore g
+ MOVW 20(R13), R10
+ RET
+
+TEXT runtime·mmap(SB),7,$12
+ MOVW 0(FP), R0 // arg 1 - addr
+ MOVW 4(FP), R1 // arg 2 - len
+ MOVW 8(FP), R2 // arg 3 - prot
+ MOVW 12(FP), R3 // arg 4 - flags
+ // arg 5 (fid) and arg6 (offset_lo, offset_hi) are passed on stack
+ // note the C runtime only passes the 32-bit offset_lo to us
+ MOVW 16(FP), R4 // arg 5
+ MOVW R4, 4(R13)
+ MOVW 20(FP), R5 // arg 6 lower 32-bit
+ MOVW R5, 8(R13)
+ MOVW $0, R6 // higher 32-bit for arg 6
+ MOVW R6, 12(R13)
+ ADD $4, R13 // pass arg 5 and arg 6 on stack
+ SWI $0xa000c5 // sys_mmap
+ SUB $4, R13
+ RET
+
+TEXT runtime·munmap(SB),7,$0
+ MOVW 0(FP), R0 // arg 1 - addr
+ MOVW 4(FP), R1 // arg 2 - len
+ SWI $0xa00049 // sys_munmap
+ MOVW.CS $0, R9 // crash on syscall failure
+ MOVW.CS R9, (R9)
+ RET
+
+TEXT runtime·madvise(SB),7,$0
+ MOVW 0(FP), R0 // arg 1 - addr
+ MOVW 4(FP), R1 // arg 2 - len
+ MOVW 8(FP), R2 // arg 3 - behav
+ SWI $0xa0004b // sys_madvise
+ // ignore failure - maybe pages are locked
+ RET
+
+TEXT runtime·sigaltstack(SB),7,$-4
+ MOVW 0(FP), R0 // arg 1 - nss
+ MOVW 4(FP), R1 // arg 2 - oss
+ SWI $0xa00119 // sys___sigaltstack14
+ MOVW.CS $0, R9 // crash on syscall failure
+ MOVW.CS R9, (R9)
+ RET
+
+TEXT runtime·sysctl(SB),7,$8
+ MOVW 0(FP), R0 // arg 1 - name
+ MOVW 4(FP), R1 // arg 2 - namelen
+ MOVW 8(FP), R2 // arg 3 - oldp
+ MOVW 12(FP), R3 // arg 4 - oldlenp
+ MOVW 16(FP), R4 // arg 5 - newp
+ MOVW R4, 4(R13)
+ MOVW 20(FP), R4 // arg 6 - newlen
+ MOVW R4, 8(R13)
+ ADD $4, R13 // pass arg 5 and 6 on stack
+ SWI $0xa000ca // sys___sysctl
+ SUB $4, R13
+ RET
+
+TEXT runtime·casp(SB),7,$0
+ B runtime·cas(SB)
+
+// TODO(minux): this is only valid for ARMv6+
+// bool armcas(int32 *val, int32 old, int32 new)
+// Atomically:
+// if(*val == old){
+// *val = new;
+// return 1;
+// }else
+// return 0;
+TEXT runtime·cas(SB),7,$0
+ B runtime·armcas(SB)
diff --git a/src/pkg/runtime/sys_openbsd_386.s b/src/pkg/runtime/sys_openbsd_386.s
index 593b4a9df..c62e0f949 100644
--- a/src/pkg/runtime/sys_openbsd_386.s
+++ b/src/pkg/runtime/sys_openbsd_386.s
@@ -12,14 +12,16 @@
TEXT runtime·exit(SB),7,$-4
MOVL $1, AX
INT $0x80
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
-TEXT runtime·exit1(SB),7,$-4
- MOVL $302, AX // sys_threxit
+TEXT runtime·exit1(SB),7,$8
+ MOVL $0, 0(SP)
+ MOVL $0, 4(SP) // arg 1 - notdead
+ MOVL $302, AX // sys___threxit
INT $0x80
JAE 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·write(SB),7,$-4
@@ -67,7 +69,7 @@ TEXT runtime·mmap(SB),7,$36
MOVL $0, AX
STOSL // arg 6 - pad
MOVSL // arg 7 - offset
- MOVL $0, AX // top 64 bits of file offset
+ MOVL $0, AX // top 32 bits of file offset
STOSL
MOVL $197, AX // sys_mmap
INT $0x80
@@ -79,7 +81,14 @@ TEXT runtime·munmap(SB),7,$-4
MOVL $73, AX // sys_munmap
INT $0x80
JAE 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·madvise(SB),7,$-4
+ MOVL $75, AX // sys_madvise
+ INT $0x80
+ JAE 2(PC)
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·setitimer(SB),7,$-4
@@ -89,40 +98,38 @@ TEXT runtime·setitimer(SB),7,$-4
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
- MOVL $116, AX
+ MOVL $232, AX
LEAL 12(SP), BX
- MOVL BX, 4(SP)
- MOVL $0, 8(SP)
+ MOVL $0, 4(SP)
+ MOVL BX, 8(SP)
INT $0x80
MOVL 12(SP), AX // sec
- MOVL 16(SP), BX // usec
+ MOVL 16(SP), BX // nsec
- // sec is in AX, usec in BX
+ // sec is in AX, nsec in BX
MOVL AX, sec+0(FP)
MOVL $0, sec+4(FP)
- IMULL $1000, BX
MOVL BX, nsec+8(FP)
RET
// int64 nanotime(void) so really
// void nanotime(int64 *nsec)
TEXT runtime·nanotime(SB),7,$32
- MOVL $116, AX
+ MOVL $232, AX
LEAL 12(SP), BX
- MOVL BX, 4(SP)
- MOVL $0, 8(SP)
+ MOVL $0, 4(SP)
+ MOVL BX, 8(SP)
INT $0x80
MOVL 12(SP), AX // sec
- MOVL 16(SP), BX // usec
+ MOVL 16(SP), BX // nsec
- // sec is in AX, usec in BX
+ // sec is in AX, nsec in BX
// convert to DX:AX nsec
MOVL $1000000000, CX
MULL CX
- IMULL $1000, BX
ADDL BX, AX
ADCL $0, DX
-
+
MOVL ret+0(FP), DI
MOVL AX, 0(DI)
MOVL DX, 4(DI)
@@ -132,7 +139,15 @@ TEXT runtime·sigaction(SB),7,$-4
MOVL $46, AX // sys_sigaction
INT $0x80
JAE 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·sigprocmask(SB),7,$-4
+ MOVL $48, AX // sys_sigprocmask
+ INT $0x80
+ JAE 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ MOVL AX, oset+0(FP)
RET
TEXT runtime·sigtramp(SB),7,$44
@@ -141,8 +156,11 @@ TEXT runtime·sigtramp(SB),7,$44
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
- JNE 2(PC)
+ JNE 5(PC)
+ MOVL signo+0(FP), BX
+ MOVL BX, 0(SP)
CALL runtime·badsignal(SB)
+ RET
// save g
MOVL g(CX), DI
@@ -174,62 +192,59 @@ TEXT runtime·sigtramp(SB),7,$44
MOVL AX, 4(SP) // arg 1 - sigcontext
MOVL $103, AX // sys_sigreturn
INT $0x80
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
-// int32 rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
-TEXT runtime·rfork_thread(SB),7,$8
- MOVL flags+8(SP), AX
- MOVL stack+12(SP), CX
+// int32 tfork(void *param, uintptr psize, M *mp, G *gp, void (*fn)(void));
+TEXT runtime·tfork(SB),7,$12
- // Copy m, g, fn off parent stack for use by child.
+ // Copy mp, gp and fn from the parent stack onto the child stack.
+ MOVL params+4(FP), AX
+ MOVL 8(AX), CX // tf_stack
SUBL $16, CX
- MOVL mm+16(SP), SI
+ MOVL CX, 8(AX)
+ MOVL mm+12(FP), SI
MOVL SI, 0(CX)
- MOVL gg+20(SP), SI
+ MOVL gg+16(FP), SI
MOVL SI, 4(CX)
- MOVL fn+24(SP), SI
+ MOVL fn+20(FP), SI
MOVL SI, 8(CX)
MOVL $1234, 12(CX)
- MOVL CX, SI
MOVL $0, 0(SP) // syscall gap
- MOVL AX, 4(SP) // arg 1 - flags
- MOVL $251, AX // sys_rfork
+ MOVL params+4(FP), AX
+ MOVL AX, 4(SP) // arg 1 - param
+ MOVL psize+8(FP), AX
+ MOVL AX, 8(SP) // arg 2 - psize
+ MOVL $8, AX // sys___tfork
INT $0x80
- // Return if rfork syscall failed
- JCC 4(PC)
+ // Return if tfork syscall failed.
+ JCC 5(PC)
NEGL AX
- MOVL AX, 48(SP)
+ MOVL ret+0(FP), DX
+ MOVL AX, 0(DX)
RET
// In parent, return.
CMPL AX, $0
- JEQ 3(PC)
- MOVL AX, 48(SP)
+ JEQ 4(PC)
+ MOVL ret+0(FP), DX
+ MOVL AX, 0(DX)
RET
- // In child, on new stack.
- MOVL SI, SP
-
// Paranoia: check that SP is as we expect.
MOVL 12(SP), BP
CMPL BP, $1234
JEQ 2(PC)
INT $3
- // Reload registers
+ // Reload registers.
MOVL 0(SP), BX // m
MOVL 4(SP), DX // g
MOVL 8(SP), SI // fn
- // Initialize m->procid to thread ID
- MOVL $299, AX // sys_getthrid
- INT $0x80
- MOVL AX, m_procid(BX)
-
- // Set FS to point at m->tls
+ // Set FS to point at m->tls.
LEAL m_tls(BX), BP
PUSHAL // save registers
PUSHL BP
@@ -246,12 +261,12 @@ TEXT runtime·rfork_thread(SB),7,$8
MOVL 0(DX), DX // paranoia; check they are not nil
MOVL 0(BX), BX
- // more paranoia; check that stack splitting code works
+ // More paranoia; check that stack splitting code works.
PUSHAL
CALL runtime·emptyfunc(SB)
POPAL
- // Call fn
+ // Call fn.
CALL SI
CALL runtime·exit1(SB)
@@ -268,25 +283,23 @@ TEXT runtime·sigaltstack(SB),7,$-8
INT $3
RET
-TEXT runtime·setldt(SB),7,$8
+TEXT runtime·setldt(SB),7,$4
// Under OpenBSD we set the GS base instead of messing with the LDT.
- MOVL 16(SP), AX // tls0
+ MOVL tls0+4(FP), AX
MOVL AX, 0(SP)
CALL runtime·settls(SB)
RET
-TEXT runtime·settls(SB),7,$16
+TEXT runtime·settls(SB),7,$8
// adjust for ELF: wants to use -8(GS) and -4(GS) for g and m
- MOVL 20(SP), CX
+ MOVL tlsbase+0(FP), CX
ADDL $8, CX
- MOVL CX, 0(CX)
MOVL $0, 0(SP) // syscall gap
- MOVL $9, 4(SP) // I386_SET_GSBASE (machine/sysarch.h)
- MOVL CX, 8(SP) // pointer to base
- MOVL $165, AX // sys_sysarch
+ MOVL CX, 4(SP) // arg 1 - tcb
+ MOVL $329, AX // sys___set_tcb
INT $0x80
JCC 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·osyield(SB),7,$-4
@@ -295,12 +308,12 @@ TEXT runtime·osyield(SB),7,$-4
RET
TEXT runtime·thrsleep(SB),7,$-4
- MOVL $300, AX // sys_thrsleep
+ MOVL $300, AX // sys___thrsleep
INT $0x80
RET
TEXT runtime·thrwakeup(SB),7,$-4
- MOVL $301, AX // sys_thrwakeup
+ MOVL $301, AX // sys___thrwakeup
INT $0x80
RET
diff --git a/src/pkg/runtime/sys_openbsd_amd64.s b/src/pkg/runtime/sys_openbsd_amd64.s
index d2d48e6b5..8a736507f 100644
--- a/src/pkg/runtime/sys_openbsd_amd64.s
+++ b/src/pkg/runtime/sys_openbsd_amd64.s
@@ -8,20 +8,20 @@
#include "zasm_GOOS_GOARCH.h"
-// int64 rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
-TEXT runtime·rfork_thread(SB),7,$0
- MOVL flags+8(SP), DI
- MOVQ stack+16(SP), SI
+// int64 tfork(void *param, uintptr psize, M *mp, G *gp, void (*fn)(void));
+TEXT runtime·tfork(SB),7,$32
- // Copy m, g, fn off parent stack for use by child.
- MOVQ mm+24(SP), R8
- MOVQ gg+32(SP), R9
- MOVQ fn+40(SP), R12
+ // Copy mp, gp and fn off parent stack for use by child.
+ MOVQ mm+16(FP), R8
+ MOVQ gg+24(FP), R9
+ MOVQ fn+32(FP), R12
- MOVL $251, AX // sys_rfork
+ MOVQ param+0(FP), DI
+ MOVQ psize+8(FP), SI
+ MOVL $8, AX // sys___tfork
SYSCALL
- // Return if rfork syscall failed
+ // Return if tfork syscall failed.
JCC 3(PC)
NEGL AX
RET
@@ -31,19 +31,11 @@ TEXT runtime·rfork_thread(SB),7,$0
JEQ 2(PC)
RET
- // In child, on new stack.
- MOVQ SI, SP
-
- // Initialize m->procid to thread ID
- MOVL $299, AX // sys_getthrid
- SYSCALL
- MOVQ AX, m_procid(R8)
-
// Set FS to point at m->tls.
LEAQ m_tls(R8), DI
CALL runtime·settls(SB)
- // In child, set up new stack
+ // In child, set up new stack.
get_tls(CX)
MOVQ R8, m(CX)
MOVQ R9, g(CX)
@@ -53,12 +45,13 @@ TEXT runtime·rfork_thread(SB),7,$0
CALL R12
// It shouldn't return. If it does, exit
- MOVL $302, AX // sys_threxit
+ MOVQ $0, DI // arg 1 - notdead
+ MOVL $302, AX // sys___threxit
SYSCALL
JMP -3(PC) // keep exiting
TEXT runtime·osyield(SB),7,$0
- MOVL $298, AX // sys_sched_yield
+ MOVL $298, AX // sys_sched_yield
SYSCALL
RET
@@ -67,14 +60,15 @@ TEXT runtime·thrsleep(SB),7,$0
MOVL 16(SP), SI // arg 2 - clock_id
MOVQ 24(SP), DX // arg 3 - tp
MOVQ 32(SP), R10 // arg 4 - lock
- MOVL $300, AX // sys_thrsleep
+ MOVQ 40(SP), R8 // arg 5 - abort
+ MOVL $300, AX // sys___thrsleep
SYSCALL
RET
TEXT runtime·thrwakeup(SB),7,$0
MOVQ 8(SP), DI // arg 1 - ident
MOVL 16(SP), SI // arg 2 - n
- MOVL $301, AX // sys_thrwakeup
+ MOVL $301, AX // sys___thrwakeup
SYSCALL
RET
@@ -83,13 +77,14 @@ TEXT runtime·exit(SB),7,$-8
MOVL 8(SP), DI // arg 1 - exit status
MOVL $1, AX // sys_exit
SYSCALL
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·exit1(SB),7,$-8
- MOVL $302, AX // sys_threxit
+ MOVQ $0, DI // arg 1 - notdead
+ MOVL $302, AX // sys___threxit
SYSCALL
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·write(SB),7,$-8
@@ -135,31 +130,29 @@ TEXT runtime·setitimer(SB),7,$-8
// func now() (sec int64, nsec int32)
TEXT time·now(SB), 7, $32
- LEAQ 8(SP), DI // arg 1 - tp
- MOVQ $0, SI // arg 2 - tzp
- MOVL $116, AX // sys_gettimeofday
+ MOVQ $0, DI // arg 1 - clock_id
+ LEAQ 8(SP), SI // arg 2 - tp
+ MOVL $232, AX // sys_clock_gettime
SYSCALL
- MOVQ 8(SP), AX // sec
- MOVL 16(SP), DX // usec
+ MOVL 8(SP), AX // sec
+ MOVQ 16(SP), DX // nsec
- // sec is in AX, usec in DX
+ // sec is in AX, nsec in DX
MOVQ AX, sec+0(FP)
- IMULQ $1000, DX
MOVL DX, nsec+8(FP)
RET
TEXT runtime·nanotime(SB),7,$32
- LEAQ 8(SP), DI // arg 1 - tp
- MOVQ $0, SI // arg 2 - tzp
- MOVL $116, AX // sys_gettimeofday
+ MOVQ $0, DI // arg 1 - clock_id
+ LEAQ 8(SP), SI // arg 2 - tp
+ MOVL $232, AX // sys_clock_gettime
SYSCALL
- MOVQ 8(SP), AX // sec
- MOVL 16(SP), DX // usec
+ MOVL 8(SP), AX // sec
+ MOVQ 16(SP), DX // nsec
- // sec is in AX, usec in DX
+ // sec is in AX, nsec in DX
// return nsec in AX
IMULQ $1000000000, AX
- IMULQ $1000, DX
ADDQ DX, AX
RET
@@ -170,7 +163,17 @@ TEXT runtime·sigaction(SB),7,$-8
MOVL $46, AX
SYSCALL
JCC 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·sigprocmask(SB),7,$0
+ MOVL 8(SP), DI // arg 1 - how
+ MOVL 12(SP), SI // arg 2 - set
+ MOVL $48, AX // sys_sigprocmask
+ SYSCALL
+ JCC 2(PC)
+ MOVL $0xf1, 0xf1 // crash
+ MOVL AX, oset+0(FP) // Return oset
RET
TEXT runtime·sigtramp(SB),7,$64
@@ -179,8 +182,10 @@ TEXT runtime·sigtramp(SB),7,$64
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
- JNE 2(PC)
+ JNE 4(PC)
+ MOVQ DI, 0(SP)
CALL runtime·badsignal(SB)
+ RET
// save g
MOVQ g(BX), R10
@@ -226,7 +231,16 @@ TEXT runtime·munmap(SB),7,$0
MOVL $73, AX // sys_munmap
SYSCALL
JCC 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
+ RET
+
+TEXT runtime·madvise(SB),7,$0
+ MOVQ addr+0(FP), DI // arg 1 - addr
+ MOVQ len+8(FP), SI // arg 2 - len
+ MOVQ behav+16(FP), DX // arg 3 - behav
+ MOVQ $75, AX // sys_madvise
+ SYSCALL
+ // ignore failure - maybe pages are locked
RET
TEXT runtime·sigaltstack(SB),7,$-8
@@ -235,20 +249,17 @@ TEXT runtime·sigaltstack(SB),7,$-8
MOVQ $288, AX // sys_sigaltstack
SYSCALL
JCC 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
// set tls base to DI
-TEXT runtime·settls(SB),7,$8
+TEXT runtime·settls(SB),7,$0
// adjust for ELF: wants to use -16(FS) and -8(FS) for g and m
ADDQ $16, DI
- MOVQ DI, 0(SP)
- MOVQ SP, SI
- MOVQ $12, DI // AMD64_SET_FSBASE (machine/sysarch.h)
- MOVQ $165, AX // sys_sysarch
+ MOVQ $329, AX // sys___settcb
SYSCALL
JCC 2(PC)
- MOVL $0xf1, 0xf1 // crash
+ MOVL $0xf1, 0xf1 // crash
RET
TEXT runtime·sysctl(SB),7,$0
@@ -260,7 +271,7 @@ TEXT runtime·sysctl(SB),7,$0
MOVQ 48(SP), R9 // arg 6 - newlen
MOVQ $202, AX // sys___sysctl
SYSCALL
- JCC 3(PC)
+ JCC 3(PC)
NEGL AX
RET
MOVL $0, AX
diff --git a/src/pkg/runtime/sys_plan9_386.s b/src/pkg/runtime/sys_plan9_386.s
index 94c36aa41..3385b083a 100644
--- a/src/pkg/runtime/sys_plan9_386.s
+++ b/src/pkg/runtime/sys_plan9_386.s
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-#include "defs_GOOS_GOARCH.h"
#include "zasm_GOOS_GOARCH.h"
// setldt(int entry, int address, int limit)
@@ -24,9 +23,19 @@ TEXT runtime·pwrite(SB),7,$0
INT $64
RET
+TEXT runtime·seek(SB),7,$0
+ MOVL $39, AX
+ INT $64
+ CMPL AX, $-1
+ JNE 4(PC)
+ MOVL a+0(FP), CX
+ MOVL AX, 0(CX)
+ MOVL AX, 4(CX)
+ RET
+
TEXT runtime·close(SB),7,$0
MOVL $4, AX
- INT $64
+ INT $64
RET
TEXT runtime·exits(SB),7,$0
@@ -48,6 +57,21 @@ TEXT runtime·plan9_semacquire(SB),7,$0
MOVL $37, AX
INT $64
RET
+
+TEXT runtime·plan9_tsemacquire(SB),7,$0
+ MOVL $52, AX
+ INT $64
+ RET
+
+TEXT runtime·notify(SB),7,$0
+ MOVL $28, AX
+ INT $64
+ RET
+
+TEXT runtime·noted(SB),7,$0
+ MOVL $29, AX
+ INT $64
+ RET
TEXT runtime·plan9_semrelease(SB),7,$0
MOVL $38, AX
@@ -77,9 +101,8 @@ TEXT runtime·rfork(SB),7,$0
MOVL DX, g(AX)
MOVL BX, m(AX)
- // Initialize AX from _tos->pid
- MOVL _tos(SB), AX
- MOVL tos_pid(AX), AX
+ // Initialize AX from TOS struct.
+ MOVL procid(AX), AX
MOVL AX, m_procid(BX) // save pid as m->procid
CALL runtime·stackcheck(SB) // smashes AX, CX
@@ -95,3 +118,55 @@ TEXT runtime·rfork(SB),7,$0
CALL SI // fn()
CALL runtime·exit(SB)
RET
+
+// void sigtramp(void *ureg, int8 *note)
+TEXT runtime·sigtramp(SB),7,$0
+ get_tls(AX)
+
+ // check that m exists
+ MOVL m(AX), BX
+ CMPL BX, $0
+ JNE 3(PC)
+ CALL runtime·badsignal(SB) // will exit
+ RET
+
+ // save args
+ MOVL ureg+4(SP), CX
+ MOVL note+8(SP), DX
+
+ // change stack
+ MOVL m_gsignal(BX), BP
+ MOVL g_stackbase(BP), BP
+ MOVL BP, SP
+
+ // make room for args and g
+ SUBL $16, SP
+
+ // save g
+ MOVL g(AX), BP
+ MOVL BP, 12(SP)
+
+ // g = m->gsignal
+ MOVL m_gsignal(BX), DI
+ MOVL DI, g(AX)
+
+ // load args and call sighandler
+ MOVL CX, 0(SP)
+ MOVL DX, 4(SP)
+ MOVL BP, 8(SP)
+
+ CALL runtime·sighandler(SB)
+
+ // restore g
+ get_tls(BX)
+ MOVL 12(SP), BP
+ MOVL BP, g(BX)
+
+ // call noted(AX)
+ MOVL AX, 0(SP)
+ CALL runtime·noted(SB)
+ RET
+
+// Only used by the 64-bit runtime.
+TEXT runtime·setfpmasks(SB),7,$0
+ RET
diff --git a/src/pkg/runtime/sys_plan9_amd64.s b/src/pkg/runtime/sys_plan9_amd64.s
new file mode 100644
index 000000000..b34f98a68
--- /dev/null
+++ b/src/pkg/runtime/sys_plan9_amd64.s
@@ -0,0 +1,208 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "zasm_GOOS_GOARCH.h"
+
+// setldt(int entry, int address, int limit)
+TEXT runtime·setldt(SB),7,$0
+ RET
+
+TEXT runtime·open(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $14, BP
+ SYSCALL
+ RET
+
+TEXT runtime·pread(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $50, BP
+ SYSCALL
+ RET
+
+TEXT runtime·pwrite(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $51, BP
+ SYSCALL
+ RET
+
+// int32 _seek(int64*, int32, int64, int32)
+TEXT _seek<>(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $39, BP
+ SYSCALL
+ RET
+
+// int64 seek(int32, int64, int32)
+TEXT runtime·seek(SB),7,$56
+ LEAQ new+48(SP), CX
+ MOVQ CX, 0(SP)
+ MOVQ fd+0(FP), CX
+ MOVQ CX, 8(SP)
+ MOVQ off+8(FP), CX
+ MOVQ CX, 16(SP)
+ MOVQ whence+16(FP), CX
+ MOVQ CX, 24(SP)
+ CALL _seek<>(SB)
+ CMPL AX, $0
+ JGE 2(PC)
+ MOVQ $-1, new+48(SP)
+ MOVQ new+48(SP), AX
+ RET
+
+TEXT runtime·close(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $4, BP
+ SYSCALL
+ RET
+
+TEXT runtime·exits(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $8, BP
+ SYSCALL
+ RET
+
+TEXT runtime·brk_(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $24, BP
+ SYSCALL
+ RET
+
+TEXT runtime·sleep(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $17, BP
+ SYSCALL
+ RET
+
+TEXT runtime·plan9_semacquire(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $37, BP
+ SYSCALL
+ RET
+
+TEXT runtime·plan9_tsemacquire(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $52, BP
+ SYSCALL
+ RET
+
+TEXT runtime·notify(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $28, BP
+ SYSCALL
+ RET
+
+TEXT runtime·noted(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $29, BP
+ SYSCALL
+ RET
+
+TEXT runtime·plan9_semrelease(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $38, BP
+ SYSCALL
+ RET
+
+TEXT runtime·nanotime(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $60, BP
+ SYSCALL
+ RET
+
+TEXT runtime·rfork(SB),7,$0
+ MOVQ $0x8000, AX
+ MOVQ $19, BP // rfork
+ SYSCALL
+
+ // In parent, return.
+ CMPQ AX, $0
+ JEQ 2(PC)
+ RET
+
+ // In child on forked stack.
+ MOVQ mm+24(SP), BX // m
+ MOVQ gg+32(SP), DX // g
+ MOVQ fn+40(SP), SI // fn
+
+ // set SP to be on the new child stack
+ MOVQ stack+16(SP), CX
+ MOVQ CX, SP
+
+ // Initialize m, g.
+ get_tls(AX)
+ MOVQ DX, g(AX)
+ MOVQ BX, m(AX)
+
+ // Initialize AX from pid in TLS.
+ MOVQ procid(AX), AX
+ MOVQ AX, m_procid(BX) // save pid as m->procid
+
+ CALL runtime·stackcheck(SB) // smashes AX, CX
+
+ MOVQ 0(DX), DX // paranoia; check they are not nil
+ MOVQ 0(BX), BX
+
+ CALL SI // fn()
+ CALL runtime·exit(SB)
+ RET
+
+// This is needed by asm_amd64.s
+TEXT runtime·settls(SB),7,$0
+ RET
+
+// void sigtramp(void *ureg, int8 *note)
+TEXT runtime·sigtramp(SB),7,$0
+ get_tls(AX)
+
+ // check that m exists
+ MOVQ m(AX), BX
+ CMPQ BX, $0
+ JNE 3(PC)
+ CALL runtime·badsignal(SB) // will exit
+ RET
+
+ // save args
+ MOVQ ureg+8(SP), CX
+ MOVQ note+16(SP), DX
+
+ // change stack
+ MOVQ m_gsignal(BX), R10
+ MOVQ g_stackbase(R10), BP
+ MOVQ BP, SP
+
+ // make room for args and g
+ SUBQ $32, SP
+
+ // save g
+ MOVQ g(AX), BP
+ MOVQ BP, 24(SP)
+
+ // g = m->gsignal
+ MOVQ R10, g(AX)
+
+ // load args and call sighandler
+ MOVQ CX, 0(SP)
+ MOVQ DX, 8(SP)
+ MOVQ BP, 16(SP)
+
+ CALL runtime·sighandler(SB)
+
+ // restore g
+ get_tls(BX)
+ MOVQ 24(SP), R10
+ MOVQ R10, g(BX)
+
+ // call noted(AX)
+ MOVQ AX, 0(SP)
+ CALL runtime·noted(SB)
+ RET
+
+TEXT runtime·setfpmasks(SB),7,$8
+ STMXCSR 0(SP)
+ MOVL 0(SP), AX
+ ANDL $~0x3F, AX
+ ORL $(0x3F<<7), AX
+ MOVL AX, 0(SP)
+ LDMXCSR 0(SP)
+ RET
diff --git a/src/pkg/runtime/sys_windows_386.s b/src/pkg/runtime/sys_windows_386.s
index d5646bfea..ca59f0a1d 100644
--- a/src/pkg/runtime/sys_windows_386.s
+++ b/src/pkg/runtime/sys_windows_386.s
@@ -216,7 +216,7 @@ TEXT runtime·callbackasm+0(SB),7,$0
CLD
- CALL runtime·cgocallback(SB)
+ CALL runtime·cgocallback_gofunc(SB)
POPL AX
POPL CX
@@ -243,11 +243,6 @@ TEXT runtime·tstart(SB),7,$0
MOVL newm+4(SP), CX // m
MOVL m_g0(CX), DX // g
- // Set up SEH frame
- PUSHL $runtime·sigtramp(SB)
- PUSHL 0(FS)
- MOVL SP, 0(FS)
-
// Layout new m scheduler stack on os stack.
MOVL SP, AX
MOVL AX, g_stackbase(DX)
@@ -264,14 +259,8 @@ TEXT runtime·tstart(SB),7,$0
CLD
CALL runtime·stackcheck(SB) // clobbers AX,CX
-
CALL runtime·mstart(SB)
- // Pop SEH frame
- MOVL 0(FS), SP
- POPL 0(FS)
- POPL CX
-
RET
// uint32 tstart_stdcall(M *newm);
@@ -296,3 +285,32 @@ TEXT runtime·setldt(SB),7,$0
MOVL address+4(FP), CX
MOVL CX, 0x14(FS)
RET
+
+// void install_exception_handler()
+TEXT runtime·install_exception_handler(SB),7,$0
+ get_tls(CX)
+ MOVL m(CX), CX // m
+
+ // Set up SEH frame
+ MOVL m_seh(CX), DX
+ MOVL $runtime·sigtramp(SB), AX
+ MOVL AX, seh_handler(DX)
+ MOVL 0(FS), AX
+ MOVL AX, seh_prev(DX)
+
+ // Install it
+ MOVL DX, 0(FS)
+
+ RET
+
+// void remove_exception_handler()
+TEXT runtime·remove_exception_handler(SB),7,$0
+ get_tls(CX)
+ MOVL m(CX), CX // m
+
+ // Remove SEH frame
+ MOVL m_seh(CX), DX
+ MOVL seh_prev(DX), AX
+ MOVL AX, 0(FS)
+
+ RET
diff --git a/src/pkg/runtime/sys_windows_amd64.s b/src/pkg/runtime/sys_windows_amd64.s
index 11909cda2..fe88f3b75 100644
--- a/src/pkg/runtime/sys_windows_amd64.s
+++ b/src/pkg/runtime/sys_windows_amd64.s
@@ -272,13 +272,13 @@ TEXT runtime·callbackasm(SB),7,$0
MOVQ R15, 0(SP)
// prepare call stack. use SUBQ to hide from stack frame checks
- // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
+ // cgocallback(Go func, void *frame, uintptr framesize)
SUBQ $24, SP
MOVQ DX, 16(SP) // uintptr framesize
MOVQ CX, 8(SP) // void *frame
- MOVQ AX, 0(SP) // void (*fn)(void*)
+ MOVQ AX, 0(SP) // Go func
CLD
- CALL runtime·cgocallback(SB)
+ CALL runtime·cgocallback_gofunc(SB)
MOVQ 0(SP), AX
MOVQ 8(SP), CX
MOVQ 16(SP), DX
@@ -328,7 +328,6 @@ TEXT runtime·tstart_stdcall(SB),7,$0
// Someday the convention will be D is always cleared.
CLD
- CALL runtime·setstacklimits(SB)
CALL runtime·stackcheck(SB) // clobbers AX,CX
CALL runtime·mstart(SB)
@@ -337,6 +336,13 @@ TEXT runtime·tstart_stdcall(SB),7,$0
// set tls base to DI
TEXT runtime·settls(SB),7,$0
- CALL runtime·setstacklimits(SB)
MOVQ DI, 0x28(GS)
RET
+
+// void install_exception_handler()
+TEXT runtime·install_exception_handler(SB),7,$0
+ CALL runtime·setstacklimits(SB)
+ RET
+
+TEXT runtime·remove_exception_handler(SB),7,$0
+ RET
diff --git a/src/pkg/runtime/syscall_windows_test.go b/src/pkg/runtime/syscall_windows_test.go
index c8327fdef..f04d2cd54 100644
--- a/src/pkg/runtime/syscall_windows_test.go
+++ b/src/pkg/runtime/syscall_windows_test.go
@@ -112,9 +112,10 @@ func Test64BitReturnStdCall(t *testing.T) {
func TestCDecl(t *testing.T) {
var buf [50]byte
+ fmtp, _ := syscall.BytePtrFromString("%d %d %d")
a, _, _ := GetDLL(t, "user32.dll").Proc("wsprintfA").Call(
uintptr(unsafe.Pointer(&buf[0])),
- uintptr(unsafe.Pointer(syscall.StringBytePtr("%d %d %d"))),
+ uintptr(unsafe.Pointer(fmtp)),
1000, 2000, 3000)
if string(buf[:a]) != "1000 2000 3000" {
t.Error("cdecl USER32.wsprintfA returns", a, "buf=", buf[:a])
diff --git a/src/pkg/runtime/thread_darwin.c b/src/pkg/runtime/thread_darwin.c
index 6a83e48a3..adb1ffe6a 100644
--- a/src/pkg/runtime/thread_darwin.c
+++ b/src/pkg/runtime/thread_darwin.c
@@ -9,8 +9,8 @@
extern SigTab runtime·sigtab[];
-static Sigset sigset_all = ~(Sigset)0;
static Sigset sigset_none;
+static Sigset sigset_all = ~(Sigset)0;
static Sigset sigset_prof = 1<<(SIGPROF-1);
static void
@@ -50,11 +50,8 @@ runtime·semacreate(void)
void
runtime·osinit(void)
{
- // Register our thread-creation callback (see sys_darwin_{amd64,386}.s)
- // but only if we're not using cgo. If we are using cgo we need
- // to let the C pthread libary install its own thread-creation callback.
- if(!runtime·iscgo)
- runtime·bsdthread_register();
+ // bsdthread_register delayed until end of goenvs so that we
+ // can look at the environment first.
// Use sysctl to fetch hw.ncpu.
uint32 mib[2];
@@ -75,22 +72,34 @@ void
runtime·goenvs(void)
{
runtime·goenvs_unix();
+
+ // Register our thread-creation callback (see sys_darwin_{amd64,386}.s)
+ // but only if we're not using cgo. If we are using cgo we need
+ // to let the C pthread libary install its own thread-creation callback.
+ if(!runtime·iscgo) {
+ if(runtime·bsdthread_register() != 0) {
+ if(runtime·getenv("DYLD_INSERT_LIBRARIES"))
+ runtime·throw("runtime: bsdthread_register error (unset DYLD_INSERT_LIBRARIES)");
+ runtime·throw("runtime: bsdthread_register error");
+ }
+ }
+
}
void
-runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
+runtime·newosproc(M *mp, void *stk)
{
int32 errno;
Sigset oset;
- m->tls[0] = m->id; // so 386 asm can find it
+ mp->tls[0] = mp->id; // so 386 asm can find it
if(0){
- runtime·printf("newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n",
- stk, m, g, fn, m->id, m->tls[0], &m);
+ runtime·printf("newosproc stk=%p m=%p g=%p id=%d/%d ostk=%p\n",
+ stk, mp, mp->g0, mp->id, (int32)mp->tls[0], &mp);
}
runtime·sigprocmask(SIG_SETMASK, &sigset_all, &oset);
- errno = runtime·bsdthread_create(stk, m, g, fn);
+ errno = runtime·bsdthread_create(stk, mp, mp->g0, runtime·mstart);
runtime·sigprocmask(SIG_SETMASK, &oset, nil);
if(errno < 0) {
@@ -100,17 +109,30 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
}
// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+void
+runtime·mpreinit(M *mp)
+{
+ mp->gsignal = runtime·malg(32*1024); // OS X wants >=8K, Linux >=2K
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, can not allocate memory.
void
runtime·minit(void)
{
// Initialize signal handling.
- m->gsignal = runtime·malg(32*1024); // OS X wants >=8K, Linux >=2K
- runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024);
+ runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024);
- if(m->profilehz > 0)
- runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil);
- else
- runtime·sigprocmask(SIG_SETMASK, &sigset_prof, nil);
+ runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil);
+ runtime·setprof(m->profilehz > 0);
+}
+
+// Called from dropm to undo the effect of an minit.
+void
+runtime·unminit(void)
+{
+ runtime·signalstack(nil, 0);
}
// Mach IPC, to get at semaphores
@@ -431,10 +453,11 @@ runtime·sigpanic(void)
runtime·panicstring(runtime·sigtab[g->sig].name);
}
-// TODO(rsc): place holder to fix build.
+#pragma textflag 7
void
runtime·osyield(void)
{
+ runtime·usleep(1);
}
uintptr
@@ -488,12 +511,22 @@ runtime·badcallback(void)
runtime·write(2, badcallback, sizeof badcallback - 1);
}
-static int8 badsignal[] = "runtime: signal received on thread not created by Go.\n";
+static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
-runtime·badsignal(void)
+runtime·badsignal(int32 sig)
{
+ if (sig == SIGPROF) {
+ return; // Ignore SIGPROFs intended for a non-Go thread.
+ }
runtime·write(2, badsignal, sizeof badsignal - 1);
+ if (0 <= sig && sig < NSIG) {
+ // Call runtime·findnull dynamically to circumvent static stack size check.
+ static int32 (*findnull)(byte*) = runtime·findnull;
+ runtime·write(2, runtime·sigtab[sig].name, findnull((byte*)runtime·sigtab[sig].name));
+ }
+ runtime·write(2, "\n", 1);
+ runtime·exit(1);
}
diff --git a/src/pkg/runtime/thread_freebsd.c b/src/pkg/runtime/thread_freebsd.c
index 4c546178f..3ae14ee0a 100644
--- a/src/pkg/runtime/thread_freebsd.c
+++ b/src/pkg/runtime/thread_freebsd.c
@@ -13,8 +13,8 @@ extern int32 runtime·sys_umtx_op(uint32*, int32, uint32, void*, void*);
#define CTL_HW 6
#define HW_NCPU 3
+static Sigset sigset_none;
static Sigset sigset_all = { ~(uint32)0, ~(uint32)0, ~(uint32)0, ~(uint32)0, };
-static Sigset sigset_none = { 0, 0, 0, 0, };
static int32
getncpu(void)
@@ -77,32 +77,33 @@ runtime·futexwakeup(uint32 *addr, uint32 cnt)
void runtime·thr_start(void*);
void
-runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
+runtime·newosproc(M *mp, void *stk)
{
ThrParam param;
Sigset oset;
- USED(fn); // thr_start assumes fn == mstart
- USED(g); // thr_start assumes g == m->g0
-
if(0){
- runtime·printf("newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n",
- stk, m, g, fn, m->id, m->tls[0], &m);
+ runtime·printf("newosproc stk=%p m=%p g=%p id=%d/%d ostk=%p\n",
+ stk, mp, mp->g0, mp->id, (int32)mp->tls[0], &mp);
}
runtime·sigprocmask(&sigset_all, &oset);
runtime·memclr((byte*)&param, sizeof param);
param.start_func = runtime·thr_start;
- param.arg = m;
- param.stack_base = (int8*)g->stackbase;
- param.stack_size = (byte*)stk - (byte*)g->stackbase;
- param.child_tid = (intptr*)&m->procid;
+ param.arg = (byte*)mp;
+
+ // NOTE(rsc): This code is confused. stackbase is the top of the stack
+ // and is equal to stk. However, it's working, so I'm not changing it.
+ param.stack_base = (void*)mp->g0->stackbase;
+ param.stack_size = (byte*)stk - (byte*)mp->g0->stackbase;
+
+ param.child_tid = (intptr*)&mp->procid;
param.parent_tid = nil;
- param.tls_base = (int8*)&m->tls[0];
- param.tls_size = sizeof m->tls;
+ param.tls_base = (void*)&mp->tls[0];
+ param.tls_size = sizeof mp->tls;
- m->tls[0] = m->id; // so 386 asm can find it
+ mp->tls[0] = mp->id; // so 386 asm can find it
runtime·thr_new(&param, sizeof param);
runtime·sigprocmask(&oset, nil);
@@ -121,15 +122,30 @@ runtime·goenvs(void)
}
// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+void
+runtime·mpreinit(M *mp)
+{
+ mp->gsignal = runtime·malg(32*1024);
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, can not allocate memory.
void
runtime·minit(void)
{
// Initialize signal handling
- m->gsignal = runtime·malg(32*1024);
- runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024);
+ runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024);
runtime·sigprocmask(&sigset_none, nil);
}
+// Called from dropm to undo the effect of an minit.
+void
+runtime·unminit(void)
+{
+ runtime·signalstack(nil, 0);
+}
+
void
runtime·sigpanic(void)
{
@@ -206,12 +222,22 @@ runtime·badcallback(void)
runtime·write(2, badcallback, sizeof badcallback - 1);
}
-static int8 badsignal[] = "runtime: signal received on thread not created by Go.\n";
+static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
-runtime·badsignal(void)
+runtime·badsignal(int32 sig)
{
+ if (sig == SIGPROF) {
+ return; // Ignore SIGPROFs intended for a non-Go thread.
+ }
runtime·write(2, badsignal, sizeof badsignal - 1);
+ if (0 <= sig && sig < NSIG) {
+ // Call runtime·findnull dynamically to circumvent static stack size check.
+ static int32 (*findnull)(byte*) = runtime·findnull;
+ runtime·write(2, runtime·sigtab[sig].name, findnull((byte*)runtime·sigtab[sig].name));
+ }
+ runtime·write(2, "\n", 1);
+ runtime·exit(1);
}
diff --git a/src/pkg/runtime/thread_linux.c b/src/pkg/runtime/thread_linux.c
index 858be7036..78ddef878 100644
--- a/src/pkg/runtime/thread_linux.c
+++ b/src/pkg/runtime/thread_linux.c
@@ -13,8 +13,8 @@ int32 runtime·open(uint8*, int32, int32);
int32 runtime·close(int32);
int32 runtime·read(int32, void*, int32);
-static Sigset sigset_all = { ~(uint32)0, ~(uint32)0 };
static Sigset sigset_none;
+static Sigset sigset_all = { ~(uint32)0, ~(uint32)0 };
// Linux futex.
//
@@ -80,33 +80,23 @@ runtime·futexwakeup(uint32 *addr, uint32 cnt)
*(int32*)0x1006 = 0x1006;
}
+extern runtime·sched_getaffinity(uintptr pid, uintptr len, uintptr *buf);
static int32
getproccount(void)
{
- int32 fd, rd, cnt, cpustrlen;
- byte *cpustr, *pos, *bufpos;
- byte buf[256];
+ uintptr buf[16], t;
+ int32 r, cnt, i;
- fd = runtime·open((byte*)"/proc/stat", O_RDONLY|O_CLOEXEC, 0);
- if(fd == -1)
- return 1;
cnt = 0;
- bufpos = buf;
- cpustr = (byte*)"\ncpu";
- cpustrlen = runtime·findnull(cpustr);
- for(;;) {
- rd = runtime·read(fd, bufpos, sizeof(buf)-cpustrlen);
- if(rd == -1)
- break;
- bufpos[rd] = 0;
- for(pos=buf; pos=runtime·strstr(pos, cpustr); cnt++, pos++) {
- }
- if(rd < cpustrlen)
- break;
- runtime·memmove(buf, bufpos+rd-cpustrlen+1, cpustrlen-1);
- bufpos = buf+cpustrlen-1;
+ r = runtime·sched_getaffinity(0, sizeof(buf), buf);
+ if(r > 0)
+ for(i = 0; i < r/sizeof(buf[0]); i++) {
+ t = buf[i];
+ t = t - ((t >> 1) & 0x5555555555555555ULL);
+ t = (t & 0x3333333333333333ULL) + ((t >> 2) & 0x3333333333333333ULL);
+ cnt += (int32)((((t + (t >> 4)) & 0xF0F0F0F0F0F0F0FULL) * 0x101010101010101ULL) >> 56);
}
- runtime·close(fd);
+
return cnt ? cnt : 1;
}
@@ -134,7 +124,7 @@ enum
};
void
-runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
+runtime·newosproc(M *mp, void *stk)
{
int32 ret;
int32 flags;
@@ -150,16 +140,16 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
| CLONE_THREAD /* revisit - okay for now */
;
- m->tls[0] = m->id; // so 386 asm can find it
+ mp->tls[0] = mp->id; // so 386 asm can find it
if(0){
- runtime·printf("newosproc stk=%p m=%p g=%p fn=%p clone=%p id=%d/%d ostk=%p\n",
- stk, m, g, fn, runtime·clone, m->id, m->tls[0], &m);
+ runtime·printf("newosproc stk=%p m=%p g=%p clone=%p id=%d/%d ostk=%p\n",
+ stk, mp, mp->g0, runtime·clone, mp->id, (int32)mp->tls[0], &mp);
}
// Disable signals during clone, so that the new thread starts
// with signals disabled. It will enable them in minit.
runtime·rtsigprocmask(SIG_SETMASK, &sigset_all, &oset, sizeof oset);
- ret = runtime·clone(flags, stk, m, g, fn);
+ ret = runtime·clone(flags, stk, mp, mp->g0, runtime·mstart);
runtime·rtsigprocmask(SIG_SETMASK, &oset, nil, sizeof oset);
if(ret < 0) {
@@ -181,13 +171,28 @@ runtime·goenvs(void)
}
// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+void
+runtime·mpreinit(M *mp)
+{
+ mp->gsignal = runtime·malg(32*1024); // OS X wants >=8K, Linux >=2K
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, can not allocate memory.
void
runtime·minit(void)
{
// Initialize signal handling.
- m->gsignal = runtime·malg(32*1024); // OS X wants >=8K, Linux >=2K
- runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024);
- runtime·rtsigprocmask(SIG_SETMASK, &sigset_none, nil, sizeof sigset_none);
+ runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024);
+ runtime·rtsigprocmask(SIG_SETMASK, &sigset_none, nil, sizeof(Sigset));
+}
+
+// Called from dropm to undo the effect of an minit.
+void
+runtime·unminit(void)
+{
+ runtime·signalstack(nil, 0);
}
void
@@ -266,12 +271,22 @@ runtime·badcallback(void)
runtime·write(2, badcallback, sizeof badcallback - 1);
}
-static int8 badsignal[] = "runtime: signal received on thread not created by Go.\n";
+static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
-runtime·badsignal(void)
+runtime·badsignal(int32 sig)
{
+ if (sig == SIGPROF) {
+ return; // Ignore SIGPROFs intended for a non-Go thread.
+ }
runtime·write(2, badsignal, sizeof badsignal - 1);
+ if (0 <= sig && sig < NSIG) {
+ // Call runtime·findnull dynamically to circumvent static stack size check.
+ static int32 (*findnull)(byte*) = runtime·findnull;
+ runtime·write(2, runtime·sigtab[sig].name, findnull((byte*)runtime·sigtab[sig].name));
+ }
+ runtime·write(2, "\n", 1);
+ runtime·exit(1);
}
diff --git a/src/pkg/runtime/thread_netbsd.c b/src/pkg/runtime/thread_netbsd.c
index 1b2df85cd..f333c6dd8 100644
--- a/src/pkg/runtime/thread_netbsd.c
+++ b/src/pkg/runtime/thread_netbsd.c
@@ -11,7 +11,7 @@ enum
ESRCH = 3,
ENOTSUP = 91,
- // From NetBSD's sys/time.h
+ // From NetBSD's <sys/time.h>
CLOCK_REALTIME = 0,
CLOCK_VIRTUAL = 1,
CLOCK_PROF = 2,
@@ -20,9 +20,15 @@ enum
extern SigTab runtime·sigtab[];
-extern int64 runtime·rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
-extern int32 runtime·thrsleep(void *ident, int32 clock_id, void *tsp, void *lock);
-extern int32 runtime·thrwakeup(void *ident, int32 n);
+static Sigset sigset_none;
+static Sigset sigset_all = { ~(uint32)0, ~(uint32)0, ~(uint32)0, ~(uint32)0, };
+
+extern void runtime·getcontext(UcontextT *context);
+extern int32 runtime·lwp_create(UcontextT *context, uintptr flags, void *lwpid);
+extern void runtime·lwp_mcontext_init(void *mc, void *stack, M *mp, G *gp, void (*fn)(void));
+extern int32 runtime·lwp_park(Timespec *abstime, int32 unpark, void *hint, void *unparkhint);
+extern int32 runtime·lwp_unpark(int32 lwp, void *hint);
+extern int32 runtime·lwp_self(void);
// From NetBSD's <sys/sysctl.h>
#define CTL_HW 6
@@ -68,13 +74,30 @@ runtime·semasleep(int64 ns)
if(m->waitsemacount == 0) {
// sleep until semaphore != 0 or timeout.
// thrsleep unlocks m->waitsemalock.
- if(ns < 0)
- runtime·thrsleep(&m->waitsemacount, 0, nil, &m->waitsemalock);
- else {
+ if(ns < 0) {
+ // TODO(jsing) - potential deadlock!
+ //
+ // There is a potential deadlock here since we
+ // have to release the waitsemalock mutex
+ // before we call lwp_park() to suspend the
+ // thread. This allows another thread to
+ // release the lock and call lwp_unpark()
+ // before the thread is actually suspended.
+ // If this occurs the current thread will end
+ // up sleeping indefinitely. Unfortunately
+ // the NetBSD kernel does not appear to provide
+ // a mechanism for unlocking the userspace
+ // mutex once the thread is actually parked.
+ runtime·atomicstore(&m->waitsemalock, 0);
+ runtime·lwp_park(nil, 0, &m->waitsemacount, nil);
+ } else {
ns += runtime·nanotime();
ts.tv_sec = ns/1000000000LL;
ts.tv_nsec = ns%1000000000LL;
- runtime·thrsleep(&m->waitsemacount, CLOCK_REALTIME, &ts, &m->waitsemalock);
+ // TODO(jsing) - potential deadlock!
+ // See above for details.
+ runtime·atomicstore(&m->waitsemalock, 0);
+ runtime·lwp_park(&ts, 0, &m->waitsemacount, nil);
}
// reacquire lock
while(runtime·xchg(&m->waitsemalock, 1))
@@ -112,39 +135,41 @@ runtime·semawakeup(M *mp)
while(runtime·xchg(&mp->waitsemalock, 1))
runtime·osyield();
mp->waitsemacount++;
- ret = runtime·thrwakeup(&mp->waitsemacount, 1);
+ // TODO(jsing) - potential deadlock, see semasleep() for details.
+ // Confirm that LWP is parked before unparking...
+ ret = runtime·lwp_unpark(mp->procid, &mp->waitsemacount);
if(ret != 0 && ret != ESRCH)
runtime·printf("thrwakeup addr=%p sem=%d ret=%d\n", &mp->waitsemacount, mp->waitsemacount, ret);
// spin-mutex unlock
runtime·atomicstore(&mp->waitsemalock, 0);
}
-// From NetBSD's sys/param.h
-#define RFPROC (1<<4) /* change child (else changes curproc) */
-#define RFMEM (1<<5) /* share `address space' */
-#define RFNOWAIT (1<<6) /* parent need not wait() on child */
-#define RFTHREAD (1<<13) /* create a thread, not a process */
-
void
-runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
+runtime·newosproc(M *mp, void *stk)
{
- int32 flags;
+ UcontextT uc;
int32 ret;
- flags = RFPROC | RFTHREAD | RFMEM | RFNOWAIT;
-
- if (0) {
+ if(0) {
runtime·printf(
- "newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n",
- stk, m, g, fn, m->id, m->tls[0], &m);
+ "newosproc stk=%p m=%p g=%p id=%d/%d ostk=%p\n",
+ stk, mp, mp->g0, mp->id, (int32)mp->tls[0], &mp);
}
- m->tls[0] = m->id; // so 386 asm can find it
+ mp->tls[0] = mp->id; // so 386 asm can find it
+
+ runtime·getcontext(&uc);
+
+ uc.uc_flags = _UC_SIGMASK | _UC_CPU;
+ uc.uc_link = nil;
+ uc.uc_sigmask = sigset_all;
+
+ runtime·lwp_mcontext_init(&uc.uc_mcontext, stk, mp, mp->g0, runtime·mstart);
- if((ret = runtime·rfork_thread(flags, stk, m, g, fn)) < 0) {
+ ret = runtime·lwp_create(&uc, 0, &mp->procid);
+
+ if(ret < 0) {
runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount() - 1, -ret);
- if (ret == -ENOTSUP)
- runtime·printf("runtime: is kern.rthreads disabled?\n");
runtime·throw("runtime.newosproc");
}
}
@@ -162,12 +187,30 @@ runtime·goenvs(void)
}
// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+void
+runtime·mpreinit(M *mp)
+{
+ mp->gsignal = runtime·malg(32*1024);
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, can not allocate memory.
void
runtime·minit(void)
{
+ m->procid = runtime·lwp_self();
+
// Initialize signal handling
- m->gsignal = runtime·malg(32*1024);
- runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024);
+ runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024);
+ runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil);
+}
+
+// Called from dropm to undo the effect of an minit.
+void
+runtime·unminit(void)
+{
+ runtime·signalstack(nil, 0);
}
void
@@ -224,12 +267,22 @@ runtime·badcallback(void)
runtime·write(2, badcallback, sizeof badcallback - 1);
}
-static int8 badsignal[] = "runtime: signal received on thread not created by Go.\n";
+static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
-runtime·badsignal(void)
+runtime·badsignal(int32 sig)
{
+ if (sig == SIGPROF) {
+ return; // Ignore SIGPROFs intended for a non-Go thread.
+ }
runtime·write(2, badsignal, sizeof badsignal - 1);
+ if (0 <= sig && sig < NSIG) {
+ // Call runtime·findnull dynamically to circumvent static stack size check.
+ static int32 (*findnull)(byte*) = runtime·findnull;
+ runtime·write(2, runtime·sigtab[sig].name, findnull((byte*)runtime·sigtab[sig].name));
+ }
+ runtime·write(2, "\n", 1);
+ runtime·exit(1);
}
diff --git a/src/pkg/runtime/thread_openbsd.c b/src/pkg/runtime/thread_openbsd.c
index d0f947210..700c48147 100644
--- a/src/pkg/runtime/thread_openbsd.c
+++ b/src/pkg/runtime/thread_openbsd.c
@@ -20,8 +20,11 @@ enum
extern SigTab runtime·sigtab[];
-extern int64 runtime·rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void));
-extern int32 runtime·thrsleep(void *ident, int32 clock_id, void *tsp, void *lock);
+static Sigset sigset_none;
+static Sigset sigset_all = ~(Sigset)0;
+
+extern int64 runtime·tfork(void *param, uintptr psize, M *mp, G *gp, void (*fn)(void));
+extern int32 runtime·thrsleep(void *ident, int32 clock_id, void *tsp, void *lock, const int32 *abort);
extern int32 runtime·thrwakeup(void *ident, int32 n);
// From OpenBSD's <sys/sysctl.h>
@@ -69,12 +72,12 @@ runtime·semasleep(int64 ns)
// sleep until semaphore != 0 or timeout.
// thrsleep unlocks m->waitsemalock.
if(ns < 0)
- runtime·thrsleep(&m->waitsemacount, 0, nil, &m->waitsemalock);
+ runtime·thrsleep(&m->waitsemacount, 0, nil, &m->waitsemalock, nil);
else {
ns += runtime·nanotime();
ts.tv_sec = ns/1000000000LL;
ts.tv_nsec = ns%1000000000LL;
- runtime·thrsleep(&m->waitsemacount, CLOCK_REALTIME, &ts, &m->waitsemalock);
+ runtime·thrsleep(&m->waitsemacount, CLOCK_REALTIME, &ts, &m->waitsemalock, nil);
}
// reacquire lock
while(runtime·xchg(&m->waitsemalock, 1))
@@ -119,29 +122,30 @@ runtime·semawakeup(M *mp)
runtime·atomicstore(&mp->waitsemalock, 0);
}
-// From OpenBSD's sys/param.h
-#define RFPROC (1<<4) /* change child (else changes curproc) */
-#define RFMEM (1<<5) /* share `address space' */
-#define RFNOWAIT (1<<6) /* parent need not wait() on child */
-#define RFTHREAD (1<<13) /* create a thread, not a process */
-
void
-runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
+runtime·newosproc(M *mp, void *stk)
{
- int32 flags;
+ Tfork param;
+ Sigset oset;
int32 ret;
- flags = RFPROC | RFTHREAD | RFMEM | RFNOWAIT;
-
- if (0) {
+ if(0) {
runtime·printf(
- "newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n",
- stk, m, g, fn, m->id, m->tls[0], &m);
+ "newosproc stk=%p m=%p g=%p id=%d/%d ostk=%p\n",
+ stk, mp, mp->g0, mp->id, (int32)mp->tls[0], &mp);
}
- m->tls[0] = m->id; // so 386 asm can find it
+ mp->tls[0] = mp->id; // so 386 asm can find it
+
+ param.tf_tcb = (byte*)&mp->tls[0];
+ param.tf_tid = (int32*)&mp->procid;
+ param.tf_stack = stk;
- if((ret = runtime·rfork_thread(flags, stk, m, g, fn)) < 0) {
+ oset = runtime·sigprocmask(SIG_SETMASK, sigset_all);
+ ret = runtime·tfork((byte*)&param, sizeof(param), mp, mp->g0, runtime·mstart);
+ runtime·sigprocmask(SIG_SETMASK, oset);
+
+ if(ret < 0) {
runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount() - 1, -ret);
if (ret == -ENOTSUP)
runtime·printf("runtime: is kern.rthreads disabled?\n");
@@ -162,12 +166,28 @@ runtime·goenvs(void)
}
// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+void
+runtime·mpreinit(M *mp)
+{
+ mp->gsignal = runtime·malg(32*1024);
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, can not allocate memory.
void
runtime·minit(void)
{
// Initialize signal handling
- m->gsignal = runtime·malg(32*1024);
- runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024);
+ runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024);
+ runtime·sigprocmask(SIG_SETMASK, sigset_none);
+}
+
+// Called from dropm to undo the effect of an minit.
+void
+runtime·unminit(void)
+{
+ runtime·signalstack(nil, 0);
}
void
@@ -224,12 +244,22 @@ runtime·badcallback(void)
runtime·write(2, badcallback, sizeof badcallback - 1);
}
-static int8 badsignal[] = "runtime: signal received on thread not created by Go.\n";
+static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
-runtime·badsignal(void)
+runtime·badsignal(int32 sig)
{
+ if (sig == SIGPROF) {
+ return; // Ignore SIGPROFs intended for a non-Go thread.
+ }
runtime·write(2, badsignal, sizeof badsignal - 1);
+ if (0 <= sig && sig < NSIG) {
+ // Call runtime·findnull dynamically to circumvent static stack size check.
+ static int32 (*findnull)(byte*) = runtime·findnull;
+ runtime·write(2, runtime·sigtab[sig].name, findnull((byte*)runtime·sigtab[sig].name));
+ }
+ runtime·write(2, "\n", 1);
+ runtime·exit(1);
}
diff --git a/src/pkg/runtime/thread_plan9.c b/src/pkg/runtime/thread_plan9.c
index 3b0dca69f..7f94623e7 100644
--- a/src/pkg/runtime/thread_plan9.c
+++ b/src/pkg/runtime/thread_plan9.c
@@ -7,12 +7,37 @@
#include "arch_GOARCH.h"
int8 *goos = "plan9";
+extern SigTab runtime·sigtab[];
+int32 runtime·postnote(int32, int8*);
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+void
+runtime·mpreinit(M *mp)
+{
+ // Initialize stack and goroutine for note handling.
+ mp->gsignal = runtime·malg(32*1024);
+ mp->notesig = (int8*)runtime·malloc(ERRMAX*sizeof(int8));
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, can not allocate memory.
void
runtime·minit(void)
{
+ // Mask all SSE floating-point exceptions
+ // when running on the 64-bit kernel.
+ runtime·setfpmasks();
+}
+
+// Called from dropm to undo the effect of an minit.
+void
+runtime·unminit(void)
+{
}
+
static int32
getproccount(void)
{
@@ -36,10 +61,30 @@ getproccount(void)
return ncpu > 0 ? ncpu : 1;
}
+static int32
+getpid(void)
+{
+ byte b[20], *c;
+ int32 fd;
+
+ runtime·memclr(b, sizeof(b));
+ fd = runtime·open((byte*)"#c/pid", 0);
+ if(fd >= 0) {
+ runtime·read(fd, b, sizeof(b));
+ runtime·close(fd);
+ }
+ c = b;
+ while(*c == ' ' || *c == '\t')
+ c++;
+ return runtime·atoi(c);
+}
+
void
runtime·osinit(void)
{
runtime·ncpu = getproccount();
+ m->procid = getpid();
+ runtime·notify(runtime·sigtramp);
}
void
@@ -52,6 +97,7 @@ runtime·initsig(void)
{
}
+#pragma textflag 7
void
runtime·osyield(void)
{
@@ -69,34 +115,6 @@ runtime·usleep(uint32 µs)
runtime·sleep(ms);
}
-int64
-runtime·nanotime(void)
-{
- static int32 fd = -1;
- byte b[8];
- uint32 hi, lo;
-
- // As long as all goroutines share the same file
- // descriptor table we can get away with using
- // just a static fd. Without a lock the file can
- // be opened twice but that's okay.
- //
- // Using /dev/bintime gives us a latency on the
- // order of ten microseconds between two calls.
- //
- // The naïve implementation (without the cached
- // file descriptor) is roughly four times slower
- // in 9vx on a 2.16 GHz Intel Core 2 Duo.
-
- if(fd < 0 && (fd = runtime·open((byte*)"/dev/bintime", OREAD|OCEXEC)) < 0)
- return 0;
- if(runtime·pread(fd, b, sizeof b, 0) != sizeof b)
- return 0;
- hi = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
- lo = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
- return (int64)hi<<32 | (int64)lo;
-}
-
void
time·now(int64 sec, int32 nsec)
{
@@ -109,48 +127,111 @@ time·now(int64 sec, int32 nsec)
FLUSH(&nsec);
}
-extern Tos *_tos;
void
-runtime·exit(int32)
+runtime·itoa(int32 n, byte *p, uint32 len)
{
- int32 fd;
+ byte *q, c;
+ uint32 i;
+
+ if(len <= 1)
+ return;
+
+ runtime·memclr(p, len);
+ q = p;
+
+ if(n==0) {
+ *q++ = '0';
+ USED(q);
+ return;
+ }
+ if(n < 0) {
+ *q++ = '-';
+ p++;
+ n = -n;
+ }
+ for(i=0; n > 0 && i < len; i++) {
+ *q++ = '0' + (n%10);
+ n = n/10;
+ }
+ for(q--; q >= p; ) {
+ c = *p;
+ *p++ = *q;
+ *q-- = c;
+ }
+}
+
+void
+runtime·goexitsall(int8 *status)
+{
+ M *mp;
+ int32 pid;
+
+ pid = getpid();
+ for(mp=runtime·atomicloadp(&runtime·allm); mp; mp=mp->alllink)
+ if(mp->procid != pid)
+ runtime·postnote(mp->procid, status);
+}
+
+int32
+runtime·postnote(int32 pid, int8* msg)
+{
+ int32 fd, len;
uint8 buf[128];
uint8 tmp[16];
uint8 *p, *q;
- int32 pid;
runtime·memclr(buf, sizeof buf);
- runtime·memclr(tmp, sizeof tmp);
- pid = _tos->pid;
- /* build path string /proc/pid/notepg */
- for(q=tmp; pid > 0;) {
- *q++ = '0' + (pid%10);
- pid = pid/10;
- }
+ /* build path string /proc/pid/note */
+ q = tmp;
p = buf;
+ runtime·itoa(pid, tmp, sizeof tmp);
runtime·memmove((void*)p, (void*)"/proc/", 6);
- p += 6;
- for(q--; q >= tmp;)
- *p++ = *q--;
- runtime·memmove((void*)p, (void*)"/notepg", 7);
+ for(p += 6; *p++ = *q++; );
+ p--;
+ runtime·memmove((void*)p, (void*)"/note", 5);
- /* post interrupt note */
fd = runtime·open(buf, OWRITE);
- runtime·write(fd, "interrupt", 9);
- runtime·exits(nil);
+ if(fd < 0)
+ return -1;
+
+ len = runtime·findnull((byte*)msg);
+ if(runtime·write(fd, msg, len) != len) {
+ runtime·close(fd);
+ return -1;
+ }
+ runtime·close(fd);
+ return 0;
}
void
-runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
+runtime·exit(int32 e)
{
- m->tls[0] = m->id; // so 386 asm can find it
+ byte tmp[16];
+ int8 *status;
+
+ if(e == 0)
+ status = "";
+ else {
+ /* build error string */
+ runtime·itoa(e, tmp, sizeof tmp);
+ status = (int8*)tmp;
+ }
+
+ runtime·goexitsall(status);
+ runtime·exits(status);
+}
+
+void
+runtime·newosproc(M *mp, void *stk)
+{
+ mp->tls[0] = mp->id; // so 386 asm can find it
if(0){
- runtime·printf("newosproc stk=%p m=%p g=%p fn=%p rfork=%p id=%d/%d ostk=%p\n",
- stk, m, g, fn, runtime·rfork, m->id, m->tls[0], &m);
+ runtime·printf("newosproc stk=%p m=%p g=%p rfork=%p id=%d/%d ostk=%p\n",
+ stk, mp, mp->g0, runtime·rfork, mp->id, (int32)mp->tls[0], &mp);
}
- if(runtime·rfork(RFPROC|RFMEM|RFNOWAIT, stk, m, g, fn) < 0)
+ if(runtime·rfork(RFPROC|RFMEM|RFNOWAIT, stk, mp, mp->g0, runtime·mstart) < 0)
runtime·throw("newosproc: rfork failed");
}
@@ -167,36 +248,18 @@ runtime·semasleep(int64 ns)
int32 ms;
if(ns >= 0) {
- // TODO: Plan 9 needs a new system call, tsemacquire.
- // The kernel implementation is the same as semacquire
- // except with a tsleep and check for timeout.
- // It would be great if the implementation returned the
- // value that was added to the semaphore, so that on
- // timeout the return value would be 0, on success 1.
- // Then the error string does not have to be parsed
- // to detect timeout.
- //
- // If a negative time indicates no timeout, then
- // semacquire can be implemented (in the kernel)
- // as tsemacquire(p, v, -1).
- runtime·throw("semasleep: timed sleep not implemented on Plan 9");
-
- /*
- if(ns < 0)
- ms = -1;
- else if(ns/1000 > 0x7fffffffll)
+ if(ns/1000000 > 0x7fffffffll)
ms = 0x7fffffff;
else
- ms = ns/1000;
- ret = runtime·plan9_tsemacquire(&m->waitsemacount, 1, ms);
+ ms = ns/1000000;
+ ret = runtime·plan9_tsemacquire(&m->waitsemacount, ms);
if(ret == 1)
return 0; // success
return -1; // timeout or interrupted
- */
}
while(runtime·plan9_semacquire(&m->waitsemacount, 1) < 0) {
- /* interrupted; try again */
+ /* interrupted; try again (c.f. lock_sema.c) */
}
return 0; // success
}
@@ -213,15 +276,15 @@ os·sigpipe(void)
runtime·throw("too many writes on closed pipe");
}
-/*
- * placeholder - once notes are implemented,
- * a signal generating a panic must appear as
- * a call to this function for correct handling by
- * traceback.
- */
void
runtime·sigpanic(void)
{
+ if(g->sigpc == 0)
+ runtime·panicstring("call of nil func value");
+ runtime·panicstring(m->notesig);
+
+ if(g->sig == 1 || g->sig == 2)
+ runtime·throw("fault");
}
int32
@@ -266,4 +329,5 @@ void
runtime·badsignal(void)
{
runtime·pwrite(2, badsignal, sizeof badsignal - 1, -1LL);
+ runtime·exits(badsignal);
}
diff --git a/src/pkg/runtime/thread_windows.c b/src/pkg/runtime/thread_windows.c
index f684d3733..ae4e82e50 100644
--- a/src/pkg/runtime/thread_windows.c
+++ b/src/pkg/runtime/thread_windows.c
@@ -135,6 +135,7 @@ runtime·write(int32 fd, void *buf, int32 n)
return written;
}
+#pragma textflag 7
void
runtime·osyield(void)
{
@@ -186,28 +187,43 @@ runtime·semacreate(void)
#define STACK_SIZE_PARAM_IS_A_RESERVATION ((uintptr)0x00010000)
void
-runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
+runtime·newosproc(M *mp, void *stk)
{
void *thandle;
USED(stk);
- USED(g); // assuming g = m->g0
- USED(fn); // assuming fn = mstart
thandle = runtime·stdcall(runtime·CreateThread, 6,
- nil, (uintptr)0x20000, runtime·tstart_stdcall, m,
+ nil, (uintptr)0x20000, runtime·tstart_stdcall, mp,
STACK_SIZE_PARAM_IS_A_RESERVATION, nil);
if(thandle == nil) {
runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), runtime·getlasterror());
runtime·throw("runtime.newosproc");
}
- runtime·atomicstorep(&m->thread, thandle);
+ runtime·atomicstorep(&mp->thread, thandle);
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+void
+runtime·mpreinit(M *mp)
+{
+ USED(mp);
}
// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, can not allocate memory.
void
runtime·minit(void)
{
+ runtime·install_exception_handler();
+}
+
+// Called from dropm to undo the effect of an minit.
+void
+runtime·unminit(void)
+{
+ runtime·remove_exception_handler();
}
int64
diff --git a/src/pkg/runtime/time.goc b/src/pkg/runtime/time.goc
index b18902f00..2babb173d 100644
--- a/src/pkg/runtime/time.goc
+++ b/src/pkg/runtime/time.goc
@@ -11,6 +11,7 @@ package time
#include "os_GOOS.h"
#include "arch_GOARCH.h"
#include "malloc.h"
+#include "race.h"
static Timers timers;
static void addtimer(Timer*);
@@ -23,14 +24,16 @@ static bool deltimer(Timer*);
// Sleep puts the current goroutine to sleep for at least ns nanoseconds.
func Sleep(ns int64) {
- g->status = Gwaiting;
- g->waitreason = "sleep";
- runtime·tsleep(ns);
+ runtime·tsleep(ns, "sleep");
}
// startTimer adds t to the timer heap.
func startTimer(t *Timer) {
+ if(raceenabled)
+ runtime·racerelease(t);
+ runtime·lock(&timers);
addtimer(t);
+ runtime·unlock(&timers);
}
// stopTimer removes t from the timer heap if it is there.
@@ -54,27 +57,28 @@ ready(int64 now, Eface e)
runtime·ready(e.data);
}
+static FuncVal readyv = {(void(*)(void))ready};
+
// Put the current goroutine to sleep for ns nanoseconds.
-// The caller must have set g->status and g->waitreason.
void
-runtime·tsleep(int64 ns)
+runtime·tsleep(int64 ns, int8 *reason)
{
Timer t;
- if(ns <= 0) {
- g->status = Grunning;
- g->waitreason = nil;
+ if(ns <= 0)
return;
- }
t.when = runtime·nanotime() + ns;
t.period = 0;
- t.f = ready;
+ t.fv = &readyv;
t.arg.data = g;
+ runtime·lock(&timers);
addtimer(&t);
- runtime·gosched();
+ runtime·park(runtime·unlock, &timers, reason);
}
+static FuncVal timerprocv = {timerproc};
+
// Add a timer to the heap and start or kick the timer proc
// if the new timer is earlier than any of the others.
static void
@@ -83,7 +87,6 @@ addtimer(Timer *t)
int32 n;
Timer **nt;
- runtime·lock(&timers);
if(timers.len >= timers.cap) {
// Grow slice.
n = 16;
@@ -109,9 +112,10 @@ addtimer(Timer *t)
runtime·ready(timers.timerproc);
}
}
- if(timers.timerproc == nil)
- timers.timerproc = runtime·newproc1((byte*)timerproc, nil, 0, 0, addtimer);
- runtime·unlock(&timers);
+ if(timers.timerproc == nil) {
+ timers.timerproc = runtime·newproc1(&timerprocv, nil, 0, 0, addtimer);
+ timers.timerproc->issystem = true;
+ }
}
// Delete timer t from the heap.
@@ -182,26 +186,25 @@ timerproc(void)
siftdown(0);
t->i = -1; // mark as removed
}
- f = t->f;
+ f = (void*)t->fv->fn;
arg = t->arg;
runtime·unlock(&timers);
+ if(raceenabled)
+ runtime·raceacquire(t);
f(now, arg);
runtime·lock(&timers);
}
if(delta < 0) {
// No timers left - put goroutine to sleep.
timers.rescheduling = true;
- g->status = Gwaiting;
- g->waitreason = "timer goroutine (idle)";
- runtime·unlock(&timers);
- runtime·gosched();
+ runtime·park(runtime·unlock, &timers, "timer goroutine (idle)");
continue;
}
// At least one timer pending. Sleep until then.
timers.sleeping = true;
runtime·noteclear(&timers.waitnote);
runtime·unlock(&timers);
- runtime·entersyscall();
+ runtime·entersyscallblock();
runtime·notetsleep(&timers.waitnote, delta);
runtime·exitsyscall();
}
diff --git a/src/pkg/runtime/time_plan9_386.c b/src/pkg/runtime/time_plan9_386.c
new file mode 100644
index 000000000..a29d45715
--- /dev/null
+++ b/src/pkg/runtime/time_plan9_386.c
@@ -0,0 +1,34 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "runtime.h"
+#include "os_GOOS.h"
+
+int64
+runtime·nanotime(void)
+{
+ static int32 fd = -1;
+ byte b[8];
+ uint32 hi, lo;
+
+ // As long as all goroutines share the same file
+ // descriptor table we can get away with using
+ // just a static fd. Without a lock the file can
+ // be opened twice but that's okay.
+ //
+ // Using /dev/bintime gives us a latency on the
+ // order of ten microseconds between two calls.
+ //
+ // The naïve implementation (without the cached
+ // file descriptor) is roughly four times slower
+ // in 9vx on a 2.16 GHz Intel Core 2 Duo.
+
+ if(fd < 0 && (fd = runtime·open((byte*)"/dev/bintime", OREAD|OCEXEC)) < 0)
+ return 0;
+ if(runtime·pread(fd, b, sizeof b, 0) != sizeof b)
+ return 0;
+ hi = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
+ lo = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
+ return (int64)hi<<32 | (int64)lo;
+}
diff --git a/src/pkg/runtime/traceback_arm.c b/src/pkg/runtime/traceback_arm.c
index 22e0bc3a6..dd85cc02c 100644
--- a/src/pkg/runtime/traceback_arm.c
+++ b/src/pkg/runtime/traceback_arm.c
@@ -17,23 +17,23 @@ void _divu(void);
void _modu(void);
int32
-runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, int32 max)
+runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max)
{
int32 i, n, iter;
uintptr pc, lr, tracepc, x;
- byte *fp, *p;
+ byte *fp;
bool waspanic;
Stktop *stk;
Func *f;
-
+
pc = (uintptr)pc0;
lr = (uintptr)lr0;
fp = nil;
waspanic = false;
// If the PC is goexit, the goroutine hasn't started yet.
- if(pc == (uintptr)runtime·goexit) {
- pc = (uintptr)g->entry;
+ if(pc == (uintptr)runtime·goexit && gp->fnstart != nil) {
+ pc = (uintptr)gp->fnstart->fn;
lr = (uintptr)runtime·goexit;
}
@@ -45,7 +45,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
}
n = 0;
- stk = (Stktop*)g->stackbase;
+ stk = (Stktop*)gp->stackbase;
for(iter = 0; iter < 100 && n < max; iter++) { // iter avoids looping forever
// Typically:
// pc is the PC of the running function.
@@ -57,52 +57,17 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
if(pc == (uintptr)runtime·lessstack) {
// Hit top of stack segment. Unwind to next segment.
pc = (uintptr)stk->gobuf.pc;
- sp = stk->gobuf.sp;
+ sp = (byte*)stk->gobuf.sp;
lr = 0;
fp = nil;
- if(pcbuf == nil)
+ if(pcbuf == nil && runtime·showframe(nil, gp == m->curg))
runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase;
continue;
}
- if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil) {
- // Dangerous, but worthwhile: see if this is a closure by
- // decoding the instruction stream.
- //
- // We check p < p+4 to avoid wrapping and faulting if
- // we have lost track of where we are.
- p = (byte*)pc;
- if((pc&3) == 0 && p < p+4 &&
- runtime·mheap.arena_start < p &&
- p+4 < runtime·mheap.arena_used) {
- x = *(uintptr*)p;
- if((x&0xfffff000) == 0xe49df000) {
- // End of closure:
- // MOVW.P frame(R13), R15
- pc = *(uintptr*)sp;
- lr = 0;
- sp += x & 0xfff;
- fp = nil;
- continue;
- }
- if((x&0xfffff000) == 0xe52de000 && lr == (uintptr)runtime·goexit) {
- // Beginning of closure.
- // Closure at top of stack, not yet started.
- p += 5*4;
- if((x&0xfff) != 4) {
- // argument copying
- p += 7*4;
- }
- if((byte*)pc < p && p < p+4 && p+4 < runtime·mheap.arena_used) {
- pc = *(uintptr*)p;
- fp = nil;
- continue;
- }
- }
- }
+ if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil)
break;
- }
// Found an actual function.
if(lr == 0)
@@ -118,15 +83,17 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
else if(pcbuf != nil)
pcbuf[n++] = pc;
else {
- if(runtime·showframe(f)) {
+ if(runtime·showframe(f, gp == m->curg)) {
// Print during crash.
// main(0x1, 0x2, 0x3)
// /home/rsc/go/src/runtime/x.go:23 +0xf
tracepc = pc; // back up to CALL instruction for funcline.
if(n > 0 && pc > f->entry && !waspanic)
tracepc -= sizeof(uintptr);
+ if(m->throwing && gp == m->curg)
+ runtime·printf("[fp=%p] ", fp);
runtime·printf("%S(", f->name);
- for(i = 0; i < f->args; i++) {
+ for(i = 0; i < f->args/sizeof(uintptr); i++) {
if(i != 0)
runtime·prints(", ");
runtime·printhex(((uintptr*)fp)[1+i]);
@@ -146,22 +113,22 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
waspanic = f->entry == (uintptr)runtime·sigpanic;
- if(pcbuf == nil && f->entry == (uintptr)runtime·newstack && g == m->g0) {
- runtime·printf("----- newstack called from goroutine %d -----\n", m->curg->goid);
+ if(pcbuf == nil && f->entry == (uintptr)runtime·newstack && gp == m->g0) {
+ runtime·printf("----- newstack called from goroutine %D -----\n", m->curg->goid);
pc = (uintptr)m->morepc;
sp = (byte*)m->moreargp - sizeof(void*);
lr = (uintptr)m->morebuf.pc;
- fp = m->morebuf.sp;
- g = m->curg;
- stk = (Stktop*)g->stackbase;
+ fp = (byte*)m->morebuf.sp;
+ gp = m->curg;
+ stk = (Stktop*)gp->stackbase;
continue;
}
- if(pcbuf == nil && f->entry == (uintptr)runtime·lessstack && g == m->g0) {
- runtime·printf("----- lessstack called from goroutine %d -----\n", m->curg->goid);
- g = m->curg;
- stk = (Stktop*)g->stackbase;
- sp = stk->gobuf.sp;
+ if(pcbuf == nil && f->entry == (uintptr)runtime·lessstack && gp == m->g0) {
+ runtime·printf("----- lessstack called from goroutine %D -----\n", m->curg->goid);
+ gp = m->curg;
+ stk = (Stktop*)gp->stackbase;
+ sp = (byte*)stk->gobuf.sp;
pc = (uintptr)stk->gobuf.pc;
fp = nil;
lr = 0;
@@ -182,9 +149,21 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
// If this was deferproc or newproc, the caller had an extra 12.
if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc)
sp += 12;
+
+ // sighandler saves the lr on stack before faking a call to sigpanic
+ if(waspanic) {
+ x = *(uintptr *)sp;
+ sp += 4;
+ f = runtime·findfunc(pc);
+ if (f == nil) {
+ pc = x;
+ } else if (f->frame == 0)
+ lr = x;
+ }
}
- if(pcbuf == nil && (pc = g->gopc) != 0 && (f = runtime·findfunc(pc)) != nil && g->goid != 1) {
+ if(pcbuf == nil && (pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil
+ && runtime·showframe(f, gp == m->curg) && gp->goid != 1) {
runtime·printf("created by %S\n", f->name);
tracepc = pc; // back up to CALL instruction for funcline.
if(n > 0 && pc > f->entry)
@@ -199,9 +178,15 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
}
void
-runtime·traceback(byte *pc0, byte *sp, byte *lr, G *g)
+runtime·traceback(byte *pc0, byte *sp, byte *lr, G *gp)
{
- runtime·gentraceback(pc0, sp, lr, g, 0, nil, 100);
+ if(gp->status == Gsyscall) {
+ // Override signal registers if blocked in system call.
+ pc0 = gp->sched.pc;
+ sp = (byte*)gp->sched.sp;
+ lr = nil;
+ }
+ runtime·gentraceback(pc0, sp, lr, gp, 0, nil, 100);
}
// func caller(n int) (pc uintptr, file string, line int, ok bool)
diff --git a/src/pkg/runtime/traceback_x86.c b/src/pkg/runtime/traceback_x86.c
index be35bab00..72603ae8e 100644
--- a/src/pkg/runtime/traceback_x86.c
+++ b/src/pkg/runtime/traceback_x86.c
@@ -8,7 +8,6 @@
#include "arch_GOARCH.h"
#include "malloc.h"
-static uintptr isclosureentry(uintptr);
void runtime·deferproc(void);
void runtime·newproc(void);
void runtime·newstack(void);
@@ -23,9 +22,8 @@ void runtime·sigpanic(void);
// A little clunky to merge the two but avoids duplicating
// the code and all its subtlety.
int32
-runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr *pcbuf, int32 max)
+runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max)
{
- byte *p;
int32 i, n, iter, sawnewstack;
uintptr pc, lr, tracepc;
byte *fp;
@@ -40,29 +38,22 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
waspanic = false;
// If the PC is goexit, the goroutine hasn't started yet.
- if(pc0 == g->sched.pc && sp == g->sched.sp && pc0 == (byte*)runtime·goexit) {
+ if(pc0 == gp->sched.pc && sp == (byte*)gp->sched.sp && pc0 == (byte*)runtime·goexit && gp->fnstart != nil) {
fp = sp;
lr = pc;
- pc = (uintptr)g->entry;
+ pc = (uintptr)gp->fnstart->fn;
}
// If the PC is zero, it's likely a nil function call.
// Start in the caller's frame.
if(pc == 0) {
- pc = lr;
- lr = 0;
- }
-
- // If the PC is zero, it's likely a nil function call.
- // Start in the caller's frame.
- if(pc == 0) {
pc = *(uintptr*)sp;
sp += sizeof(uintptr);
}
n = 0;
sawnewstack = 0;
- stk = (Stktop*)g->stackbase;
+ stk = (Stktop*)gp->stackbase;
for(iter = 0; iter < 100 && n < max; iter++) { // iter avoids looping forever
// Typically:
// pc is the PC of the running function.
@@ -74,41 +65,16 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
if(pc == (uintptr)runtime·lessstack) {
// Hit top of stack segment. Unwind to next segment.
pc = (uintptr)stk->gobuf.pc;
- sp = stk->gobuf.sp;
+ sp = (byte*)stk->gobuf.sp;
lr = 0;
fp = nil;
- if(pcbuf == nil)
+ if(pcbuf == nil && runtime·showframe(nil, gp == m->curg))
runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase;
continue;
}
- if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil) {
- // Dangerous, but worthwhile: see if this is a closure:
- // ADDQ $wwxxyyzz, SP; RET
- // [48] 81 c4 zz yy xx ww c3
- // The 0x48 byte is only on amd64.
- p = (byte*)pc;
- // We check p < p+8 to avoid wrapping and faulting if we lose track.
- if(runtime·mheap.arena_start < p && p < p+8 && p+8 < runtime·mheap.arena_used && // pointer in allocated memory
- (sizeof(uintptr) != 8 || *p++ == 0x48) && // skip 0x48 byte on amd64
- p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) {
- sp += *(uint32*)(p+2);
- pc = *(uintptr*)sp;
- sp += sizeof(uintptr);
- lr = 0;
- fp = nil;
- continue;
- }
-
- // Closure at top of stack, not yet started.
- if(lr == (uintptr)runtime·goexit && (pc = isclosureentry(pc)) != 0) {
- fp = sp;
- continue;
- }
-
- // Unknown pc: stop.
+ if(pc <= 0x1000 || (f = runtime·findfunc(pc)) == nil)
break;
- }
// Found an actual function.
if(fp == nil) {
@@ -126,7 +92,7 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
else if(pcbuf != nil)
pcbuf[n++] = pc;
else {
- if(runtime·showframe(f)) {
+ if(runtime·showframe(f, gp == m->curg)) {
// Print during crash.
// main(0x1, 0x2, 0x3)
// /home/rsc/go/src/runtime/x.go:23 +0xf
@@ -134,8 +100,10 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
tracepc = pc; // back up to CALL instruction for funcline.
if(n > 0 && pc > f->entry && !waspanic)
tracepc--;
+ if(m->throwing && gp == m->curg)
+ runtime·printf("[fp=%p] ", fp);
runtime·printf("%S(", f->name);
- for(i = 0; i < f->args; i++) {
+ for(i = 0; i < f->args/sizeof(uintptr); i++) {
if(i != 0)
runtime·prints(", ");
runtime·printhex(((uintptr*)fp)[i]);
@@ -161,27 +129,27 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
if(f->entry == (uintptr)runtime·newstack)
sawnewstack = 1;
- if(pcbuf == nil && f->entry == (uintptr)runtime·morestack && g == m->g0 && sawnewstack) {
+ if(pcbuf == nil && f->entry == (uintptr)runtime·morestack && gp == m->g0 && sawnewstack) {
// The fact that we saw newstack means that morestack
// has managed to record its information in m, so we can
// use it to keep unwinding the stack.
- runtime·printf("----- morestack called from goroutine %d -----\n", m->curg->goid);
+ runtime·printf("----- morestack called from goroutine %D -----\n", m->curg->goid);
pc = (uintptr)m->morepc;
- sp = m->morebuf.sp - sizeof(void*);
+ sp = (byte*)m->morebuf.sp - sizeof(void*);
lr = (uintptr)m->morebuf.pc;
- fp = m->morebuf.sp;
+ fp = (byte*)m->morebuf.sp;
sawnewstack = 0;
- g = m->curg;
- stk = (Stktop*)g->stackbase;
+ gp = m->curg;
+ stk = (Stktop*)gp->stackbase;
continue;
}
- if(pcbuf == nil && f->entry == (uintptr)runtime·lessstack && g == m->g0) {
+ if(pcbuf == nil && f->entry == (uintptr)runtime·lessstack && gp == m->g0) {
// Lessstack is running on scheduler stack. Switch to original goroutine.
- runtime·printf("----- lessstack called from goroutine %d -----\n", m->curg->goid);
- g = m->curg;
- stk = (Stktop*)g->stackbase;
- sp = stk->gobuf.sp;
+ runtime·printf("----- lessstack called from goroutine %D -----\n", m->curg->goid);
+ gp = m->curg;
+ stk = (Stktop*)gp->stackbase;
+ sp = (byte*)stk->gobuf.sp;
pc = (uintptr)stk->gobuf.pc;
fp = nil;
lr = 0;
@@ -196,7 +164,8 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
}
// Show what created goroutine, except main goroutine (goid 1).
- if(pcbuf == nil && (pc = g->gopc) != 0 && (f = runtime·findfunc(pc)) != nil && g->goid != 1) {
+ if(pcbuf == nil && (pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil
+ && runtime·showframe(f, gp == m->curg) && gp->goid != 1) {
runtime·printf("created by %S\n", f->name);
tracepc = pc; // back up to CALL instruction for funcline.
if(n > 0 && pc > f->entry)
@@ -211,9 +180,14 @@ runtime·gentraceback(byte *pc0, byte *sp, byte *lr0, G *g, int32 skip, uintptr
}
void
-runtime·traceback(byte *pc0, byte *sp, byte*, G *g)
+runtime·traceback(byte *pc0, byte *sp, byte*, G *gp)
{
- runtime·gentraceback(pc0, sp, nil, g, 0, nil, 100);
+ if(gp->status == Gsyscall) {
+ // Override signal registers if blocked in system call.
+ pc0 = gp->sched.pc;
+ sp = (byte*)gp->sched.sp;
+ }
+ runtime·gentraceback(pc0, sp, nil, gp, 0, nil, 100);
}
int32
@@ -227,77 +201,3 @@ runtime·callers(int32 skip, uintptr *pcbuf, int32 m)
return runtime·gentraceback(pc, sp, nil, g, skip, pcbuf, m);
}
-
-static uintptr
-isclosureentry(uintptr pc)
-{
- byte *p;
- int32 i, siz;
-
- p = (byte*)pc;
- if(p < runtime·mheap.arena_start || p+32 > runtime·mheap.arena_used)
- return 0;
-
- if(*p == 0xe8) {
- // CALL fn
- return pc+5+*(int32*)(p+1);
- }
-
- if(sizeof(uintptr) == 8 && p[0] == 0x48 && p[1] == 0xb9 && p[10] == 0xff && p[11] == 0xd1) {
- // MOVQ $fn, CX; CALL *CX
- return *(uintptr*)(p+2);
- }
-
- // SUBQ $siz, SP
- if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0x81 || *p++ != 0xec)
- return 0;
- siz = *(uint32*)p;
- p += 4;
-
- // MOVQ $q, SI
- if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0xbe)
- return 0;
- p += sizeof(uintptr);
-
- // MOVQ SP, DI
- if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0x89 || *p++ != 0xe7)
- return 0;
-
- // CLD on 32-bit
- if(sizeof(uintptr) == 4 && *p++ != 0xfc)
- return 0;
-
- if(siz <= 4*sizeof(uintptr)) {
- // MOVSQ...
- for(i=0; i<siz; i+=sizeof(uintptr))
- if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0xa5)
- return 0;
- } else {
- // MOVQ $(siz/8), CX [32-bit immediate siz/8]
- if((sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0xc7 || *p++ != 0xc1)
- return 0;
- p += 4;
-
- // REP MOVSQ
- if(*p++ != 0xf3 || (sizeof(uintptr) == 8 && *p++ != 0x48) || *p++ != 0xa5)
- return 0;
- }
-
- // CALL fn
- if(*p == 0xe8) {
- p++;
- return (uintptr)p+4 + *(int32*)p;
- }
-
- // MOVQ $fn, CX; CALL *CX
- if(sizeof(uintptr) != 8 || *p++ != 0x48 || *p++ != 0xb9)
- return 0;
-
- pc = *(uintptr*)p;
- p += 8;
-
- if(*p++ != 0xff || *p != 0xd1)
- return 0;
-
- return pc;
-}
diff --git a/src/pkg/runtime/type.go b/src/pkg/runtime/type.go
index 6af6b237f..374754afa 100644
--- a/src/pkg/runtime/type.go
+++ b/src/pkg/runtime/type.go
@@ -14,24 +14,25 @@ package runtime
import "unsafe"
-type commonType struct {
+type rtype struct {
size uintptr
hash uint32
_ uint8
align uint8
fieldAlign uint8
kind uint8
- alg *uintptr
+ alg unsafe.Pointer
+ gc unsafe.Pointer
string *string
*uncommonType
- ptrToThis *interface{}
+ ptrToThis *rtype
}
type _method struct {
name *string
pkgPath *string
- mtyp *interface{}
- typ *interface{}
+ mtyp *rtype
+ typ *rtype
ifn unsafe.Pointer
tfn unsafe.Pointer
}
@@ -45,10 +46,10 @@ type uncommonType struct {
type _imethod struct {
name *string
pkgPath *string
- typ *interface{}
+ typ *rtype
}
type interfaceType struct {
- commonType
+ rtype
methods []_imethod
}
diff --git a/src/pkg/runtime/type.h b/src/pkg/runtime/type.h
index c1d9facd1..769a8071b 100644
--- a/src/pkg/runtime/type.h
+++ b/src/pkg/runtime/type.h
@@ -5,21 +5,20 @@
/*
* Runtime type representation; master is type.go
*
- * The *Types here correspond 1-1 to type.go's *Type's, but are
- * prefixed with an extra header of 2 pointers, corresponding to the
- * interface{} structure, which itself is called type Type again on
- * the Go side.
+ * The Type*s here correspond 1-1 to type.go's *rtype.
*/
-typedef struct CommonType CommonType;
+typedef struct Type Type;
typedef struct UncommonType UncommonType;
typedef struct InterfaceType InterfaceType;
typedef struct Method Method;
typedef struct IMethod IMethod;
typedef struct SliceType SliceType;
typedef struct FuncType FuncType;
+typedef struct PtrType PtrType;
-struct CommonType
+// Needs to be in sync with typekind.h/CommonSize
+struct Type
{
uintptr size;
uint32 hash;
@@ -28,42 +27,12 @@ struct CommonType
uint8 fieldAlign;
uint8 kind;
Alg *alg;
+ void *gc;
String *string;
UncommonType *x;
Type *ptrto;
};
-enum {
- KindBool = 1,
- KindInt,
- KindInt8,
- KindInt16,
- KindInt32,
- KindInt64,
- KindUint,
- KindUint8,
- KindUint16,
- KindUint32,
- KindUint64,
- KindUintptr,
- KindFloat32,
- KindFloat64,
- KindComplex64,
- KindComplex128,
- KindArray,
- KindChan,
- KindFunc,
- KindInterface,
- KindMap,
- KindPtr,
- KindSlice,
- KindString,
- KindStruct,
- KindUnsafePointer,
-
- KindNoPointers = 1<<7,
-};
-
struct Method
{
String *name;
@@ -82,13 +51,6 @@ struct UncommonType
Method m[];
};
-struct Type
-{
- void *type; // interface{} value
- void *ptr;
- CommonType;
-};
-
struct IMethod
{
String *name;
@@ -130,3 +92,9 @@ struct FuncType
Slice in;
Slice out;
};
+
+struct PtrType
+{
+ Type;
+ Type *elem;
+};
diff --git a/src/pkg/runtime/typekind.h b/src/pkg/runtime/typekind.h
new file mode 100644
index 000000000..9bae2a871
--- /dev/null
+++ b/src/pkg/runtime/typekind.h
@@ -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.
+
+// PtrSize vs sizeof(void*): This file is also included from src/cmd/ld/...
+// which defines PtrSize to be different from sizeof(void*) when crosscompiling.
+
+enum {
+ KindBool = 1,
+ KindInt,
+ KindInt8,
+ KindInt16,
+ KindInt32,
+ KindInt64,
+ KindUint,
+ KindUint8,
+ KindUint16,
+ KindUint32,
+ KindUint64,
+ KindUintptr,
+ KindFloat32,
+ KindFloat64,
+ KindComplex64,
+ KindComplex128,
+ KindArray,
+ KindChan,
+ KindFunc,
+ KindInterface,
+ KindMap,
+ KindPtr,
+ KindSlice,
+ KindString,
+ KindStruct,
+ KindUnsafePointer,
+
+ KindNoPointers = 1<<7,
+
+ // size of Type structure.
+ CommonSize = 6*PtrSize + 8,
+};
+
diff --git a/src/pkg/runtime/vdso_linux_amd64.c b/src/pkg/runtime/vdso_linux_amd64.c
new file mode 100644
index 000000000..ab68c23c3
--- /dev/null
+++ b/src/pkg/runtime/vdso_linux_amd64.c
@@ -0,0 +1,331 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "runtime.h"
+
+#define AT_SYSINFO_EHDR 33
+#define AT_NULL 0 /* End of vector */
+#define PT_LOAD 1 /* Loadable program segment */
+#define PT_DYNAMIC 2 /* Dynamic linking information */
+#define DT_NULL 0 /* Marks end of dynamic section */
+#define DT_STRTAB 5 /* Address of string table */
+#define DT_SYMTAB 6 /* Address of symbol table */
+#define DT_VERSYM 0x6ffffff0
+#define DT_VERDEF 0x6ffffffc
+
+#define VER_FLG_BASE 0x1 /* Version definition of file itself */
+#define SHN_UNDEF 0 /* Undefined section */
+#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
+#define STT_FUNC 2 /* Symbol is a code object */
+#define STB_GLOBAL 1 /* Global symbol */
+#define STB_WEAK 2 /* Weak symbol */
+
+/* How to extract and insert information held in the st_info field. */
+#define ELF64_ST_BIND(val) (((byte) (val)) >> 4)
+#define ELF64_ST_TYPE(val) ((val) & 0xf)
+
+#define EI_NIDENT (16)
+
+typedef uint16 Elf64_Half;
+typedef uint32 Elf64_Word;
+typedef int32 Elf64_Sword;
+typedef uint64 Elf64_Xword;
+typedef int64 Elf64_Sxword;
+typedef uint64 Elf64_Addr;
+typedef uint64 Elf64_Off;
+typedef uint16 Elf64_Section;
+typedef Elf64_Half Elf64_Versym;
+
+
+typedef struct
+{
+ Elf64_Word st_name;
+ byte st_info;
+ byte st_other;
+ Elf64_Section st_shndx;
+ Elf64_Addr st_value;
+ Elf64_Xword st_size;
+} Elf64_Sym;
+
+typedef struct
+{
+ Elf64_Half vd_version; /* Version revision */
+ Elf64_Half vd_flags; /* Version information */
+ Elf64_Half vd_ndx; /* Version Index */
+ Elf64_Half vd_cnt; /* Number of associated aux entries */
+ Elf64_Word vd_hash; /* Version name hash value */
+ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */
+ Elf64_Word vd_next; /* Offset in bytes to next verdef entry */
+} Elf64_Verdef;
+
+typedef struct
+{
+ byte e_ident[EI_NIDENT]; /* Magic number and other info */
+ Elf64_Half e_type; /* Object file type */
+ Elf64_Half e_machine; /* Architecture */
+ Elf64_Word e_version; /* Object file version */
+ Elf64_Addr e_entry; /* Entry point virtual address */
+ Elf64_Off e_phoff; /* Program header table file offset */
+ Elf64_Off e_shoff; /* Section header table file offset */
+ Elf64_Word e_flags; /* Processor-specific flags */
+ Elf64_Half e_ehsize; /* ELF header size in bytes */
+ Elf64_Half e_phentsize; /* Program header table entry size */
+ Elf64_Half e_phnum; /* Program header table entry count */
+ Elf64_Half e_shentsize; /* Section header table entry size */
+ Elf64_Half e_shnum; /* Section header table entry count */
+ Elf64_Half e_shstrndx; /* Section header string table index */
+} Elf64_Ehdr;
+
+typedef struct
+{
+ Elf64_Word p_type; /* Segment type */
+ Elf64_Word p_flags; /* Segment flags */
+ Elf64_Off p_offset; /* Segment file offset */
+ Elf64_Addr p_vaddr; /* Segment virtual address */
+ Elf64_Addr p_paddr; /* Segment physical address */
+ Elf64_Xword p_filesz; /* Segment size in file */
+ Elf64_Xword p_memsz; /* Segment size in memory */
+ Elf64_Xword p_align; /* Segment alignment */
+} Elf64_Phdr;
+
+typedef struct
+{
+ Elf64_Word sh_name; /* Section name (string tbl index) */
+ Elf64_Word sh_type; /* Section type */
+ Elf64_Xword sh_flags; /* Section flags */
+ Elf64_Addr sh_addr; /* Section virtual addr at execution */
+ Elf64_Off sh_offset; /* Section file offset */
+ Elf64_Xword sh_size; /* Section size in bytes */
+ Elf64_Word sh_link; /* Link to another section */
+ Elf64_Word sh_info; /* Additional section information */
+ Elf64_Xword sh_addralign; /* Section alignment */
+ Elf64_Xword sh_entsize; /* Entry size if section holds table */
+} Elf64_Shdr;
+
+typedef struct
+{
+ Elf64_Sxword d_tag; /* Dynamic entry type */
+ union
+ {
+ Elf64_Xword d_val; /* Integer value */
+ Elf64_Addr d_ptr; /* Address value */
+ } d_un;
+} Elf64_Dyn;
+
+typedef struct
+{
+ Elf64_Word vda_name; /* Version or dependency names */
+ Elf64_Word vda_next; /* Offset in bytes to next verdaux entry */
+} Elf64_Verdaux;
+
+typedef struct
+{
+ uint64 a_type; /* Entry type */
+ union
+ {
+ uint64 a_val; /* Integer value */
+ } a_un;
+} Elf64_auxv_t;
+
+
+typedef struct {
+ byte* name;
+ void** var_ptr;
+} symbol_key;
+
+typedef struct {
+ byte* version;
+ int32 ver_hash;
+} version_key;
+
+struct vdso_info {
+ bool valid;
+
+ /* Load information */
+ uintptr load_addr;
+ uintptr load_offset; /* load_addr - recorded vaddr */
+
+ /* Symbol table */
+ int32 num_sym;
+ Elf64_Sym *symtab;
+ const byte *symstrings;
+
+ /* Version table */
+ Elf64_Versym *versym;
+ Elf64_Verdef *verdef;
+};
+
+static version_key linux26 = { (byte*)"LINUX_2.6", 0x3ae75f6 };
+
+// initialize with vsyscall fallbacks
+void* runtime·__vdso_time_sym = (void*)0xffffffffff600400ULL;
+void* runtime·__vdso_gettimeofday_sym = (void*)0xffffffffff600000ULL;
+void* runtime·__vdso_clock_gettime_sym = (void*)0;
+
+#define SYM_KEYS_COUNT 3
+static symbol_key sym_keys[] = {
+ { (byte*)"__vdso_time", &runtime·__vdso_time_sym },
+ { (byte*)"__vdso_gettimeofday", &runtime·__vdso_gettimeofday_sym },
+ { (byte*)"__vdso_clock_gettime", &runtime·__vdso_clock_gettime_sym },
+};
+
+static void
+vdso_init_from_sysinfo_ehdr(struct vdso_info *vdso_info, Elf64_Ehdr* hdr)
+{
+ uint64 i;
+ bool found_vaddr = false;
+
+ vdso_info->load_addr = (uintptr) hdr;
+
+ Elf64_Phdr *pt = (Elf64_Phdr*)(vdso_info->load_addr + hdr->e_phoff);
+ Elf64_Shdr *sh = (Elf64_Shdr*)(vdso_info->load_addr + hdr->e_shoff);
+ Elf64_Dyn *dyn = 0;
+
+ for(i=0; i<hdr->e_shnum; i++) {
+ if(sh[i].sh_type == SHT_DYNSYM) {
+ vdso_info->num_sym = sh[i].sh_size / sizeof(Elf64_Sym);
+ }
+ }
+
+ // We need two things from the segment table: the load offset
+ // and the dynamic table.
+ for(i=0; i<hdr->e_phnum; i++) {
+ if(pt[i].p_type == PT_LOAD && found_vaddr == false) {
+ found_vaddr = true;
+ vdso_info->load_offset = (uintptr)hdr
+ + (uintptr)pt[i].p_offset
+ - (uintptr)pt[i].p_vaddr;
+ } else if(pt[i].p_type == PT_DYNAMIC) {
+ dyn = (Elf64_Dyn*)((uintptr)hdr + pt[i].p_offset);
+ }
+ }
+
+ if(found_vaddr == false || dyn == nil)
+ return; // Failed
+
+ // Fish out the useful bits of the dynamic table.
+ for(i=0; dyn[i].d_tag!=DT_NULL; i++) {
+ switch(dyn[i].d_tag) {
+ case DT_STRTAB:
+ vdso_info->symstrings = (const byte *)
+ ((uintptr)dyn[i].d_un.d_ptr
+ + vdso_info->load_offset);
+ break;
+ case DT_SYMTAB:
+ vdso_info->symtab = (Elf64_Sym *)
+ ((uintptr)dyn[i].d_un.d_ptr
+ + vdso_info->load_offset);
+ break;
+ case DT_VERSYM:
+ vdso_info->versym = (Elf64_Versym *)
+ ((uintptr)dyn[i].d_un.d_ptr
+ + vdso_info->load_offset);
+ break;
+ case DT_VERDEF:
+ vdso_info->verdef = (Elf64_Verdef *)
+ ((uintptr)dyn[i].d_un.d_ptr
+ + vdso_info->load_offset);
+ break;
+ }
+ }
+ if(vdso_info->symstrings == nil || vdso_info->symtab == nil)
+ return; // Failed
+
+ if(vdso_info->verdef == nil)
+ vdso_info->versym = 0;
+
+ // That's all we need.
+ vdso_info->valid = true;
+}
+
+static int32
+vdso_find_version(struct vdso_info *vdso_info, version_key* ver)
+{
+ if(vdso_info->valid == false) {
+ return 0;
+ }
+ Elf64_Verdef *def = vdso_info->verdef;
+ while(true) {
+ if((def->vd_flags & VER_FLG_BASE) == 0) {
+ Elf64_Verdaux *aux = (Elf64_Verdaux*)((byte *)def + def->vd_aux);
+ if(def->vd_hash == ver->ver_hash &&
+ runtime·strcmp(ver->version, vdso_info->symstrings + aux->vda_name) == 0) {
+ return def->vd_ndx & 0x7fff;
+ }
+ }
+
+ if(def->vd_next == 0) {
+ break;
+ }
+ def = (Elf64_Verdef *)((byte *)def + def->vd_next);
+ }
+ return 0;
+}
+
+static void
+vdso_parse_symbols(struct vdso_info *vdso_info, int32 version)
+{
+ int32 i, j;
+
+ if(vdso_info->valid == false)
+ return;
+
+ for(i=0; i<vdso_info->num_sym; i++) {
+ Elf64_Sym *sym = &vdso_info->symtab[i];
+
+ // Check for a defined global or weak function w/ right name.
+ if(ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
+ continue;
+ if(ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
+ ELF64_ST_BIND(sym->st_info) != STB_WEAK)
+ continue;
+ if(sym->st_shndx == SHN_UNDEF)
+ continue;
+
+ for(j=0; j<SYM_KEYS_COUNT; j++) {
+ if(runtime·strcmp(sym_keys[j].name, vdso_info->symstrings + sym->st_name) != 0)
+ continue;
+
+ // Check symbol version.
+ if(vdso_info->versym != nil && version != 0
+ && vdso_info->versym[i] & 0x7fff != version)
+ continue;
+
+ *sym_keys[j].var_ptr = (void *)(vdso_info->load_offset + sym->st_value);
+ }
+ }
+}
+
+static void
+runtime·linux_setup_vdso(int32 argc, uint8** argv)
+{
+ struct vdso_info vdso_info;
+
+ // skip argvc
+ byte **p = argv;
+ p = &p[argc+1];
+
+ // skip envp to get to ELF auxiliary vector.
+ for(; *p!=0; p++) {}
+
+ // skip NULL separator
+ p++;
+
+ // now, p points to auxv
+ Elf64_auxv_t *elf_auxv = (Elf64_auxv_t*) p;
+
+ for(int32 i=0; elf_auxv[i].a_type!=AT_NULL; i++) {
+ if(elf_auxv[i].a_type == AT_SYSINFO_EHDR) {
+ if(elf_auxv[i].a_un.a_val == 0) {
+ // Something went wrong
+ return;
+ }
+ vdso_init_from_sysinfo_ehdr(&vdso_info, (Elf64_Ehdr*)elf_auxv[i].a_un.a_val);
+ vdso_parse_symbols(&vdso_info, vdso_find_version(&vdso_info, &linux26));
+ return;
+ }
+ }
+}
+
+void (*runtime·sysargs)(int32, uint8**) = runtime·linux_setup_vdso;
diff --git a/src/pkg/runtime/vlop_arm.s b/src/pkg/runtime/vlop_arm.s
index fc679f0ee..0dedc316a 100644
--- a/src/pkg/runtime/vlop_arm.s
+++ b/src/pkg/runtime/vlop_arm.s
@@ -23,9 +23,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#define UMULL(Rs,Rm,Rhi,Rlo,S) WORD $((14<<28)|(4<<21)|(S<<20)|(Rhi<<16)|(Rlo<<12)|(Rs<<8)|(9<<4)|Rm)
-#define UMLAL(Rs,Rm,Rhi,Rlo,S) WORD $((14<<28)|(5<<21)|(S<<20)|(Rhi<<16)|(Rlo<<12)|(Rs<<8)|(9<<4)|Rm)
-#define MUL(Rs,Rm,Rd,S) WORD $((14<<28)|(0<<21)|(S<<20)|(Rd<<16)|(Rs<<8)|(9<<4)|Rm)
arg=0
/* replaced use of R10 by R11 because the former can be the data segment base register */
@@ -36,136 +33,15 @@ TEXT _mulv(SB), $0
MOVW 8(FP), R11 /* h0 */
MOVW 12(FP), R4 /* l1 */
MOVW 16(FP), R5 /* h1 */
- UMULL(4, 2, 7, 6, 0)
- MUL(11, 4, 8, 0)
+ MULLU R4, R2, (R7,R6)
+ MUL R11, R4, R8
ADD R8, R7
- MUL(2, 5, 8, 0)
+ MUL R2, R5, R8
ADD R8, R7
MOVW R6, 0(R(arg))
MOVW R7, 4(R(arg))
RET
-
-Q = 0
-N = 1
-D = 2
-CC = 3
-TMP = 11
-
-TEXT save<>(SB), 7, $0
- MOVW R(Q), 0(FP)
- MOVW R(N), 4(FP)
- MOVW R(D), 8(FP)
- MOVW R(CC), 12(FP)
-
- MOVW R(TMP), R(Q) /* numerator */
- MOVW 20(FP), R(D) /* denominator */
- CMP $0, R(D)
- BNE s1
- BL runtime·panicdivide(SB)
-/* MOVW -1(R(D)), R(TMP) /* divide by zero fault */
-s1: RET
-
-TEXT rest<>(SB), 7, $0
- MOVW 0(FP), R(Q)
- MOVW 4(FP), R(N)
- MOVW 8(FP), R(D)
- MOVW 12(FP), R(CC)
-/*
- * return to caller
- * of rest<>
- */
- MOVW 0(R13), R14
- ADD $20, R13
- B (R14)
-
-TEXT div<>(SB), 7, $0
- MOVW $32, R(CC)
-/*
- * skip zeros 8-at-a-time
- */
-e1:
- AND.S $(0xff<<24),R(Q), R(N)
- BNE e2
- SLL $8, R(Q)
- SUB.S $8, R(CC)
- BNE e1
- RET
-e2:
- MOVW $0, R(N)
-
-loop:
-/*
- * shift R(N||Q) left one
- */
- SLL $1, R(N)
- CMP $0, R(Q)
- ORR.LT $1, R(N)
- SLL $1, R(Q)
-
-/*
- * compare numerator to denominator
- * if less, subtract and set quotient bit
- */
- CMP R(D), R(N)
- ORR.HS $1, R(Q)
- SUB.HS R(D), R(N)
- SUB.S $1, R(CC)
- BNE loop
- RET
-
-TEXT _div(SB), 7, $16
- BL save<>(SB)
- CMP $0, R(Q)
- BGE d1
- RSB $0, R(Q), R(Q)
- CMP $0, R(D)
- BGE d2
- RSB $0, R(D), R(D)
-d0:
- BL div<>(SB) /* none/both neg */
- MOVW R(Q), R(TMP)
- B out
-d1:
- CMP $0, R(D)
- BGE d0
- RSB $0, R(D), R(D)
-d2:
- BL div<>(SB) /* one neg */
- RSB $0, R(Q), R(TMP)
- B out
-
-TEXT _mod(SB), 7, $16
- BL save<>(SB)
- CMP $0, R(D)
- RSB.LT $0, R(D), R(D)
- CMP $0, R(Q)
- BGE m1
- RSB $0, R(Q), R(Q)
- BL div<>(SB) /* neg numerator */
- RSB $0, R(N), R(TMP)
- B out
-m1:
- BL div<>(SB) /* pos numerator */
- MOVW R(N), R(TMP)
- B out
-
-TEXT _divu(SB), 7, $16
- BL save<>(SB)
- BL div<>(SB)
- MOVW R(Q), R(TMP)
- B out
-
-TEXT _modu(SB), 7, $16
- BL save<>(SB)
- BL div<>(SB)
- MOVW R(N), R(TMP)
- B out
-
-out:
- BL rest<>(SB)
- B out
-
// trampoline for _sfloat2. passes LR as arg0 and
// saves registers R0-R13 and CPSR on the stack. R0-R12 and CPSR flags can
// be changed by _sfloat2.
@@ -186,5 +62,189 @@ TEXT _sfloat(SB), 7, $64 // 4 arg + 14*4 saved regs + cpsr
MOVM.IA.W (R0), [R1-R12]
MOVW 8(R13), R0
RET
-
+// func udiv(n, d uint32) (q, r uint32)
+// Reference:
+// Sloss, Andrew et. al; ARM System Developer's Guide: Designing and Optimizing System Software
+// Morgan Kaufmann; 1 edition (April 8, 2004), ISBN 978-1558608740
+q = 0 // input d, output q
+r = 1 // input n, output r
+s = 2 // three temporary variables
+m = 3
+a = 11
+// Please be careful when changing this, it is pretty fragile:
+// 1, don't use unconditional branch as the linker is free to reorder the blocks;
+// 2. if a == 11, beware that the linker will use R11 if you use certain instructions.
+TEXT udiv<>(SB),7,$-4
+ CLZ R(q), R(s) // find normalizing shift
+ MOVW.S R(q)<<R(s), R(a)
+ ADD R(a)>>25, PC, R(a) // most significant 7 bits of divisor
+ MOVBU.NE (4*36-64)(R(a)), R(a) // 36 == number of inst. between fast_udiv_tab and begin
+
+begin:
+ SUB.S $7, R(s)
+ RSB $0, R(q), R(m) // m = -q
+ MOVW.PL R(a)<<R(s), R(q)
+
+ // 1st Newton iteration
+ MUL.PL R(m), R(q), R(a) // a = -q*d
+ BMI udiv_by_large_d
+ MULAWT R(a), R(q), R(q), R(q) // q approx q-(q*q*d>>32)
+ TEQ R(m)->1, R(m) // check for d=0 or d=1
+
+ // 2nd Newton iteration
+ MUL.NE R(m), R(q), R(a)
+ MOVW.NE $0, R(s)
+ MULAL.NE R(q), R(a), (R(q),R(s))
+ BEQ udiv_by_0_or_1
+
+ // q now accurate enough for a remainder r, 0<=r<3*d
+ MULLU R(q), R(r), (R(q),R(s)) // q = (r * q) >> 32
+ ADD R(m), R(r), R(r) // r = n - d
+ MULA R(m), R(q), R(r), R(r) // r = n - (q+1)*d
+
+ // since 0 <= n-q*d < 3*d; thus -d <= r < 2*d
+ CMN R(m), R(r) // t = r-d
+ SUB.CS R(m), R(r), R(r) // if (t<-d || t>=0) r=r+d
+ ADD.CC $1, R(q)
+ ADD.PL R(m)<<1, R(r)
+ ADD.PL $2, R(q)
+
+ // return, can't use RET here or fast_udiv_tab will be dropped during linking
+ MOVW R14, R15
+
+udiv_by_large_d:
+ // at this point we know d>=2^(31-6)=2^25
+ SUB $4, R(a), R(a)
+ RSB $0, R(s), R(s)
+ MOVW R(a)>>R(s), R(q)
+ MULLU R(q), R(r), (R(q),R(s))
+ MULA R(m), R(q), R(r), R(r)
+
+ // q now accurate enough for a remainder r, 0<=r<4*d
+ CMN R(r)>>1, R(m) // if(r/2 >= d)
+ ADD.CS R(m)<<1, R(r)
+ ADD.CS $2, R(q)
+ CMN R(r), R(m)
+ ADD.CS R(m), R(r)
+ ADD.CS $1, R(q)
+
+ // return, can't use RET here or fast_udiv_tab will be dropped during linking
+ MOVW R14, R15
+
+udiv_by_0_or_1:
+ // carry set if d==1, carry clear if d==0
+ MOVW.CS R(r), R(q)
+ MOVW.CS $0, R(r)
+ BL.CC runtime·panicdivide(SB) // no way back
+
+ // return, can't use RET here or fast_udiv_tab will be dropped during linking
+ MOVW R14, R15
+
+fast_udiv_tab:
+ // var tab [64]byte
+ // tab[0] = 255; for i := 1; i <= 63; i++ { tab[i] = (1<<14)/(64+i) }
+ // laid out here as little-endian uint32s
+ WORD $0xf4f8fcff
+ WORD $0xe6eaedf0
+ WORD $0xdadde0e3
+ WORD $0xcfd2d4d7
+ WORD $0xc5c7cacc
+ WORD $0xbcbec0c3
+ WORD $0xb4b6b8ba
+ WORD $0xacaeb0b2
+ WORD $0xa5a7a8aa
+ WORD $0x9fa0a2a3
+ WORD $0x999a9c9d
+ WORD $0x93949697
+ WORD $0x8e8f9092
+ WORD $0x898a8c8d
+ WORD $0x85868788
+ WORD $0x81828384
+
+// The linker will pass numerator in R(TMP), and it also
+// expects the result in R(TMP)
+TMP = 11
+
+TEXT _divu(SB), 7, $16
+ MOVW R(q), 4(R13)
+ MOVW R(r), 8(R13)
+ MOVW R(s), 12(R13)
+ MOVW R(m), 16(R13)
+
+ MOVW R(TMP), R(r) /* numerator */
+ MOVW 0(FP), R(q) /* denominator */
+ BL udiv<>(SB)
+ MOVW R(q), R(TMP)
+ MOVW 4(R13), R(q)
+ MOVW 8(R13), R(r)
+ MOVW 12(R13), R(s)
+ MOVW 16(R13), R(m)
+ RET
+
+TEXT _modu(SB), 7, $16
+ MOVW R(q), 4(R13)
+ MOVW R(r), 8(R13)
+ MOVW R(s), 12(R13)
+ MOVW R(m), 16(R13)
+
+ MOVW R(TMP), R(r) /* numerator */
+ MOVW 0(FP), R(q) /* denominator */
+ BL udiv<>(SB)
+ MOVW R(r), R(TMP)
+ MOVW 4(R13), R(q)
+ MOVW 8(R13), R(r)
+ MOVW 12(R13), R(s)
+ MOVW 16(R13), R(m)
+ RET
+
+TEXT _div(SB),7,$16
+ MOVW R(q), 4(R13)
+ MOVW R(r), 8(R13)
+ MOVW R(s), 12(R13)
+ MOVW R(m), 16(R13)
+ MOVW R(TMP), R(r) /* numerator */
+ MOVW 0(FP), R(q) /* denominator */
+ CMP $0, R(r)
+ BGE d1
+ RSB $0, R(r), R(r)
+ CMP $0, R(q)
+ BGE d2
+ RSB $0, R(q), R(q)
+d0:
+ BL udiv<>(SB) /* none/both neg */
+ MOVW R(q), R(TMP)
+ B out
+d1:
+ CMP $0, R(q)
+ BGE d0
+ RSB $0, R(q), R(q)
+d2:
+ BL udiv<>(SB) /* one neg */
+ RSB $0, R(q), R(TMP)
+ B out
+
+TEXT _mod(SB),7,$16
+ MOVW R(q), 4(R13)
+ MOVW R(r), 8(R13)
+ MOVW R(s), 12(R13)
+ MOVW R(m), 16(R13)
+ MOVW R(TMP), R(r) /* numerator */
+ MOVW 0(FP), R(q) /* denominator */
+ CMP $0, R(q)
+ RSB.LT $0, R(q), R(q)
+ CMP $0, R(r)
+ BGE m1
+ RSB $0, R(r), R(r)
+ BL udiv<>(SB) /* neg numerator */
+ RSB $0, R(r), R(TMP)
+ B out
+m1:
+ BL udiv<>(SB) /* pos numerator */
+ MOVW R(r), R(TMP)
+out:
+ MOVW 4(R13), R(q)
+ MOVW 8(R13), R(r)
+ MOVW 12(R13), R(s)
+ MOVW 16(R13), R(m)
+ RET
diff --git a/src/pkg/runtime/vlop_arm_test.go b/src/pkg/runtime/vlop_arm_test.go
new file mode 100644
index 000000000..cd28419ad
--- /dev/null
+++ b/src/pkg/runtime/vlop_arm_test.go
@@ -0,0 +1,70 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime_test
+
+import "testing"
+
+// arm soft division benchmarks adapted from
+// http://ridiculousfish.com/files/division_benchmarks.tar.gz
+
+const numeratorsSize = 1 << 21
+
+var numerators = randomNumerators()
+
+type randstate struct {
+ hi, lo uint32
+}
+
+func (r *randstate) rand() uint32 {
+ r.hi = r.hi<<16 + r.hi>>16
+ r.hi += r.lo
+ r.lo += r.hi
+ return r.hi
+}
+
+func randomNumerators() []uint32 {
+ numerators := make([]uint32, numeratorsSize)
+ random := &randstate{2147483563, 2147483563 ^ 0x49616E42}
+ for i := range numerators {
+ numerators[i] = random.rand()
+ }
+ return numerators
+}
+
+func bmUint32Div(divisor uint32, b *testing.B) {
+ var sum uint32
+ for i := 0; i < b.N; i++ {
+ sum += numerators[i&(numeratorsSize-1)] / divisor
+ }
+}
+
+func BenchmarkUint32Div7(b *testing.B) { bmUint32Div(7, b) }
+func BenchmarkUint32Div37(b *testing.B) { bmUint32Div(37, b) }
+func BenchmarkUint32Div123(b *testing.B) { bmUint32Div(123, b) }
+func BenchmarkUint32Div763(b *testing.B) { bmUint32Div(763, b) }
+func BenchmarkUint32Div1247(b *testing.B) { bmUint32Div(1247, b) }
+func BenchmarkUint32Div9305(b *testing.B) { bmUint32Div(9305, b) }
+func BenchmarkUint32Div13307(b *testing.B) { bmUint32Div(13307, b) }
+func BenchmarkUint32Div52513(b *testing.B) { bmUint32Div(52513, b) }
+func BenchmarkUint32Div60978747(b *testing.B) { bmUint32Div(60978747, b) }
+func BenchmarkUint32Div106956295(b *testing.B) { bmUint32Div(106956295, b) }
+
+func bmUint32Mod(divisor uint32, b *testing.B) {
+ var sum uint32
+ for i := 0; i < b.N; i++ {
+ sum += numerators[i&(numeratorsSize-1)] % divisor
+ }
+}
+
+func BenchmarkUint32Mod7(b *testing.B) { bmUint32Mod(7, b) }
+func BenchmarkUint32Mod37(b *testing.B) { bmUint32Mod(37, b) }
+func BenchmarkUint32Mod123(b *testing.B) { bmUint32Mod(123, b) }
+func BenchmarkUint32Mod763(b *testing.B) { bmUint32Mod(763, b) }
+func BenchmarkUint32Mod1247(b *testing.B) { bmUint32Mod(1247, b) }
+func BenchmarkUint32Mod9305(b *testing.B) { bmUint32Mod(9305, b) }
+func BenchmarkUint32Mod13307(b *testing.B) { bmUint32Mod(13307, b) }
+func BenchmarkUint32Mod52513(b *testing.B) { bmUint32Mod(52513, b) }
+func BenchmarkUint32Mod60978747(b *testing.B) { bmUint32Mod(60978747, b) }
+func BenchmarkUint32Mod106956295(b *testing.B) { bmUint32Mod(106956295, b) }
diff --git a/src/pkg/sort/example_keys_test.go b/src/pkg/sort/example_keys_test.go
new file mode 100644
index 000000000..a8e47e492
--- /dev/null
+++ b/src/pkg/sort/example_keys_test.go
@@ -0,0 +1,96 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sort_test
+
+import (
+ "fmt"
+ "sort"
+)
+
+// A couple of type definitions to make the units clear.
+type earthMass float64
+type au float64
+
+// A Planet defines the properties of a solar system object.
+type Planet struct {
+ name string
+ mass earthMass
+ distance au
+}
+
+// By is the type of a "less" function that defines the ordering of its Planet arguments.
+type By func(p1, p2 *Planet) bool
+
+// Sort is a method on the function type, By, that sorts the argument slice according to the function.
+func (by By) Sort(planets []Planet) {
+ ps := &planetSorter{
+ planets: planets,
+ by: by, // The Sort method's receiver is the function (closure) that defines the sort order.
+ }
+ sort.Sort(ps)
+}
+
+// planetSorter joins a By function and a slice of Planets to be sorted.
+type planetSorter struct {
+ planets []Planet
+ by func(p1, p2 *Planet) bool // Closure used in the Less method.
+}
+
+// Len is part of sort.Interface.
+func (s *planetSorter) Len() int {
+ return len(s.planets)
+}
+
+// Swap is part of sort.Interface.
+func (s *planetSorter) Swap(i, j int) {
+ s.planets[i], s.planets[j] = s.planets[j], s.planets[i]
+}
+
+// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
+func (s *planetSorter) Less(i, j int) bool {
+ return s.by(&s.planets[i], &s.planets[j])
+}
+
+var planets = []Planet{
+ {"Mercury", 0.055, 0.4},
+ {"Venus", 0.815, 0.7},
+ {"Earth", 1.0, 1.0},
+ {"Mars", 0.107, 1.5},
+}
+
+// ExampleSortKeys demonstrates a technique for sorting a struct type using programmable sort criteria.
+func Example_sortKeys() {
+ // Closures that order the Planet structure.
+ name := func(p1, p2 *Planet) bool {
+ return p1.name < p2.name
+ }
+ mass := func(p1, p2 *Planet) bool {
+ return p1.mass < p2.mass
+ }
+ distance := func(p1, p2 *Planet) bool {
+ return p1.distance < p2.distance
+ }
+ decreasingDistance := func(p1, p2 *Planet) bool {
+ return !distance(p1, p2)
+ }
+
+ // Sort the planets by the various criteria.
+ By(name).Sort(planets)
+ fmt.Println("By name:", planets)
+
+ By(mass).Sort(planets)
+ fmt.Println("By mass:", planets)
+
+ By(distance).Sort(planets)
+ fmt.Println("By distance:", planets)
+
+ By(decreasingDistance).Sort(planets)
+ fmt.Println("By decreasing distance:", planets)
+
+ // Output: By name: [{Earth 1 1} {Mars 0.107 1.5} {Mercury 0.055 0.4} {Venus 0.815 0.7}]
+ // By mass: [{Mercury 0.055 0.4} {Mars 0.107 1.5} {Venus 0.815 0.7} {Earth 1 1}]
+ // By distance: [{Mercury 0.055 0.4} {Venus 0.815 0.7} {Earth 1 1} {Mars 0.107 1.5}]
+ // By decreasing distance: [{Mars 0.107 1.5} {Earth 1 1} {Venus 0.815 0.7} {Mercury 0.055 0.4}]
+}
diff --git a/src/pkg/sort/example_reverse_test.go b/src/pkg/sort/example_reverse_test.go
deleted file mode 100644
index 7c7f05bf3..000000000
--- a/src/pkg/sort/example_reverse_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package sort_test
-
-import (
- "fmt"
- "sort"
-)
-
-// Reverse embeds a sort.Interface value and implements a reverse sort over
-// that value.
-type Reverse struct {
- // This embedded Interface permits Reverse to use the methods of
- // another Interface implementation.
- sort.Interface
-}
-
-// Less returns the opposite of the embedded implementation's Less method.
-func (r Reverse) Less(i, j int) bool {
- return r.Interface.Less(j, i)
-}
-
-func ExampleInterface_reverse() {
- s := []int{5, 2, 6, 3, 1, 4} // unsorted
- sort.Sort(Reverse{sort.IntSlice(s)})
- fmt.Println(s)
- // Output: [6 5 4 3 2 1]
-}
diff --git a/src/pkg/sort/example_test.go b/src/pkg/sort/example_test.go
index f57d02546..f7372bec3 100644
--- a/src/pkg/sort/example_test.go
+++ b/src/pkg/sort/example_test.go
@@ -15,3 +15,10 @@ func ExampleInts() {
fmt.Println(s)
// Output: [1 2 3 4 5 6]
}
+
+func ExampleReverse() {
+ s := []int{5, 2, 6, 3, 1, 4} // unsorted
+ sort.Sort(sort.Reverse(sort.IntSlice(s)))
+ fmt.Println(s)
+ // Output: [6 5 4 3 2 1]
+}
diff --git a/src/pkg/sort/search.go b/src/pkg/sort/search.go
index 4f0ce55c3..8a2c1c33b 100644
--- a/src/pkg/sort/search.go
+++ b/src/pkg/sort/search.go
@@ -7,11 +7,13 @@
package sort
// Search uses binary search to find and return the smallest index i
-// in [0, n) at which f(i) is true, assuming that on the range [0, n),
+// in [0, n) at which f(i) is true, assuming that on the range [0, n),
// f(i) == true implies f(i+1) == true. That is, Search requires that
// f is false for some (possibly empty) prefix of the input range [0, n)
// and then true for the (possibly empty) remainder; Search returns
// the first true index. If there is no such index, Search returns n.
+// (Note that the "not found" return value is not -1 as in, for instance,
+// strings.Index).
// Search calls f(i) only for i in the range [0, n).
//
// A common use of Search is to find the index i for a value x in
@@ -74,22 +76,28 @@ func Search(n int, f func(int) bool) int {
// Convenience wrappers for common cases.
// SearchInts searches for x in a sorted slice of ints and returns the index
-// as specified by Search. The slice must be sorted in ascending order.
+// as specified by Search. The return value is the index to insert x if x is
+// not present (it could be len(a)).
+// The slice must be sorted in ascending order.
//
func SearchInts(a []int, x int) int {
return Search(len(a), func(i int) bool { return a[i] >= x })
}
// SearchFloat64s searches for x in a sorted slice of float64s and returns the index
-// as specified by Search. The slice must be sorted in ascending order.
-//
+// as specified by Search. The return value is the index to insert x if x is not
+// present (it could be len(a)).
+// The slice must be sorted in ascending order.
+//
func SearchFloat64s(a []float64, x float64) int {
return Search(len(a), func(i int) bool { return a[i] >= x })
}
-// SearchStrings searches for x slice a sorted slice of strings and returns the index
-// as specified by Search. The slice must be sorted in ascending order.
-//
+// SearchStrings searches for x in a sorted slice of strings and returns the index
+// as specified by Search. The return value is the index to insert x if x is not
+// present (it could be len(a)).
+// The slice must be sorted in ascending order.
+//
func SearchStrings(a []string, x string) int {
return Search(len(a), func(i int) bool { return a[i] >= x })
}
diff --git a/src/pkg/sort/search_test.go b/src/pkg/sort/search_test.go
index 07295ffa9..4d8d6d930 100644
--- a/src/pkg/sort/search_test.go
+++ b/src/pkg/sort/search_test.go
@@ -117,6 +117,28 @@ func TestSearchWrappers(t *testing.T) {
}
}
+func runSearchWrappers() {
+ SearchInts(data, 11)
+ SearchFloat64s(fdata, 2.1)
+ SearchStrings(sdata, "")
+ IntSlice(data).Search(0)
+ Float64Slice(fdata).Search(2.0)
+ StringSlice(sdata).Search("x")
+}
+
+func TestSearchWrappersDontAlloc(t *testing.T) {
+ allocs := testing.AllocsPerRun(100, runSearchWrappers)
+ if allocs != 0 {
+ t.Errorf("expected no allocs for runSearchWrappers, got %v", allocs)
+ }
+}
+
+func BenchmarkSearchWrappers(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ runSearchWrappers()
+ }
+}
+
// Abstract exhaustive test: all sizes up to 100,
// all possible return values. If there are any small
// corner cases, this test exercises them.
diff --git a/src/pkg/sort/sort.go b/src/pkg/sort/sort.go
index 62a4d55e7..e10961992 100644
--- a/src/pkg/sort/sort.go
+++ b/src/pkg/sort/sort.go
@@ -124,26 +124,31 @@ func doPivot(data Interface, lo, hi int) (midlo, midhi int) {
// into the middle of the slice.
pivot := lo
a, b, c, d := lo+1, lo+1, hi, hi
- for b < c {
- if data.Less(b, pivot) { // data[b] < pivot
- b++
- continue
- }
- if !data.Less(pivot, b) { // data[b] = pivot
- data.Swap(a, b)
- a++
- b++
- continue
+ for {
+ for b < c {
+ if data.Less(b, pivot) { // data[b] < pivot
+ b++
+ } else if !data.Less(pivot, b) { // data[b] = pivot
+ data.Swap(a, b)
+ a++
+ b++
+ } else {
+ break
+ }
}
- if data.Less(pivot, c-1) { // data[c-1] > pivot
- c--
- continue
+ for b < c {
+ if data.Less(pivot, c-1) { // data[c-1] > pivot
+ c--
+ } else if !data.Less(c-1, pivot) { // data[c-1] = pivot
+ data.Swap(c-1, d-1)
+ c--
+ d--
+ } else {
+ break
+ }
}
- if !data.Less(c-1, pivot) { // data[c-1] = pivot
- data.Swap(c-1, d-1)
- c--
- d--
- continue
+ if b >= c {
+ break
}
// data[b] > pivot; data[c-1] < pivot
data.Swap(b, c-1)
@@ -197,6 +202,22 @@ func Sort(data Interface) {
quickSort(data, 0, n, maxDepth)
}
+type reverse struct {
+ // This embedded Interface permits Reverse to use the methods of
+ // another Interface implementation.
+ Interface
+}
+
+// Less returns the opposite of the embedded implementation's Less method.
+func (r reverse) Less(i, j int) bool {
+ return r.Interface.Less(j, i)
+}
+
+// Reverse returns the reverse order for data.
+func Reverse(data Interface) Interface {
+ return &reverse{data}
+}
+
// IsSorted reports whether data is sorted.
func IsSorted(data Interface) bool {
n := data.Len()
diff --git a/src/pkg/sort/sort_test.go b/src/pkg/sort/sort_test.go
index ee8a9d0e8..5daf8482b 100644
--- a/src/pkg/sort/sort_test.go
+++ b/src/pkg/sort/sort_test.go
@@ -92,6 +92,23 @@ func TestSortLarge_Random(t *testing.T) {
}
}
+func TestReverseSortIntSlice(t *testing.T) {
+ data := ints
+ data1 := ints
+ a := IntSlice(data[0:])
+ Sort(a)
+ r := IntSlice(data1[0:])
+ Sort(Reverse(r))
+ for i := 0; i < len(data); i++ {
+ if a[i] != r[len(data)-1-i] {
+ t.Errorf("reverse sort didn't sort")
+ }
+ if i > len(data)/2 {
+ break
+ }
+ }
+}
+
func BenchmarkSortString1K(b *testing.B) {
b.StopTimer()
for i := 0; i < b.N; i++ {
@@ -151,15 +168,18 @@ const (
)
type testingData struct {
- desc string
- t *testing.T
- data []int
- maxswap int // number of swaps allowed
- nswap int
+ desc string
+ t *testing.T
+ data []int
+ maxswap int // number of swaps allowed
+ ncmp, nswap int
}
-func (d *testingData) Len() int { return len(d.data) }
-func (d *testingData) Less(i, j int) bool { return d.data[i] < d.data[j] }
+func (d *testingData) Len() int { return len(d.data) }
+func (d *testingData) Less(i, j int) bool {
+ d.ncmp++
+ return d.data[i] < d.data[j]
+}
func (d *testingData) Swap(i, j int) {
if d.nswap >= d.maxswap {
d.t.Errorf("%s: used %d swaps sorting slice of %d", d.desc, d.nswap, len(d.data))
@@ -192,8 +212,7 @@ func testBentleyMcIlroy(t *testing.T, sort func(Interface)) {
dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"}
modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"}
var tmp1, tmp2 [1025]int
- for ni := 0; ni < len(sizes); ni++ {
- n := sizes[ni]
+ for _, n := range sizes {
for m := 1; m < 2*n; m *= 2 {
for dist := 0; dist < _NDist; dist++ {
j := 0
@@ -259,8 +278,10 @@ func testBentleyMcIlroy(t *testing.T, sort func(Interface)) {
}
desc := fmt.Sprintf("n=%d m=%d dist=%s mode=%s", n, m, dists[dist], modes[mode])
- d := &testingData{desc, t, mdata[0:n], n * lg(n) * 12 / 10, 0}
+ d := &testingData{desc: desc, t: t, data: mdata[0:n], maxswap: n * lg(n) * 12 / 10}
sort(d)
+ // Uncomment if you are trying to improve the number of compares/swaps.
+ //t.Logf("%s: ncmp=%d, nswp=%d", desc, d.ncmp, d.nswap)
// If we were testing C qsort, we'd have to make a copy
// of the slice and sort it ourselves and then compare
diff --git a/src/pkg/strconv/atof.go b/src/pkg/strconv/atof.go
index cd3031b0e..b4fe97d12 100644
--- a/src/pkg/strconv/atof.go
+++ b/src/pkg/strconv/atof.go
@@ -37,17 +37,28 @@ func equalIgnoreCase(s1, s2 string) bool {
}
func special(s string) (f float64, ok bool) {
- switch {
- case equalIgnoreCase(s, "nan"):
- return math.NaN(), true
- case equalIgnoreCase(s, "-inf"),
- equalIgnoreCase(s, "-infinity"):
- return math.Inf(-1), true
- case equalIgnoreCase(s, "+inf"),
- equalIgnoreCase(s, "+infinity"),
- equalIgnoreCase(s, "inf"),
- equalIgnoreCase(s, "infinity"):
- return math.Inf(1), true
+ if len(s) == 0 {
+ return
+ }
+ switch s[0] {
+ default:
+ return
+ case '+':
+ if equalIgnoreCase(s, "+inf") || equalIgnoreCase(s, "+infinity") {
+ return math.Inf(1), true
+ }
+ case '-':
+ if equalIgnoreCase(s, "-inf") || equalIgnoreCase(s, "-infinity") {
+ return math.Inf(-1), true
+ }
+ case 'n', 'N':
+ if equalIgnoreCase(s, "nan") {
+ return math.NaN(), true
+ }
+ case 'i', 'I':
+ if equalIgnoreCase(s, "inf") || equalIgnoreCase(s, "infinity") {
+ return math.Inf(1), true
+ }
}
return
}
@@ -142,6 +153,105 @@ func (b *decimal) set(s string) (ok bool) {
return
}
+// readFloat reads a decimal mantissa and exponent from a float
+// string representation. It sets ok to false if the number could
+// not fit return types or is invalid.
+func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) {
+ const uint64digits = 19
+ i := 0
+
+ // optional sign
+ if i >= len(s) {
+ return
+ }
+ switch {
+ case s[i] == '+':
+ i++
+ case s[i] == '-':
+ neg = true
+ i++
+ }
+
+ // digits
+ sawdot := false
+ sawdigits := false
+ nd := 0
+ ndMant := 0
+ dp := 0
+ for ; i < len(s); i++ {
+ switch c := s[i]; true {
+ case c == '.':
+ if sawdot {
+ return
+ }
+ sawdot = true
+ dp = nd
+ continue
+
+ case '0' <= c && c <= '9':
+ sawdigits = true
+ if c == '0' && nd == 0 { // ignore leading zeros
+ dp--
+ continue
+ }
+ nd++
+ if ndMant < uint64digits {
+ mantissa *= 10
+ mantissa += uint64(c - '0')
+ ndMant++
+ } else if s[i] != '0' {
+ trunc = true
+ }
+ continue
+ }
+ break
+ }
+ if !sawdigits {
+ return
+ }
+ if !sawdot {
+ dp = nd
+ }
+
+ // optional exponent moves decimal point.
+ // if we read a very large, very long number,
+ // just be sure to move the decimal point by
+ // a lot (say, 100000). it doesn't matter if it's
+ // not the exact number.
+ if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
+ i++
+ if i >= len(s) {
+ return
+ }
+ esign := 1
+ if s[i] == '+' {
+ i++
+ } else if s[i] == '-' {
+ i++
+ esign = -1
+ }
+ if i >= len(s) || s[i] < '0' || s[i] > '9' {
+ return
+ }
+ e := 0
+ for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
+ if e < 10000 {
+ e = e*10 + int(s[i]) - '0'
+ }
+ }
+ dp += e * esign
+ }
+
+ if i != len(s) {
+ return
+ }
+
+ exp = dp - ndMant
+ ok = true
+ return
+
+}
+
// decimal power of ten to binary power of two.
var powtab = []int{1, 3, 6, 9, 13, 16, 19, 23, 26}
@@ -243,19 +353,6 @@ out:
return bits, overflow
}
-// Compute exact floating-point integer from d's digits.
-// Caller is responsible for avoiding overflow.
-func (d *decimal) atof64int() float64 {
- f := 0.0
- for i := 0; i < d.nd; i++ {
- f = f*10 + float64(d.d[i]-'0')
- }
- if d.neg {
- f = -f
- }
- return f
-}
-
func (d *decimal) atof32int() float32 {
f := float32(0)
for i := 0; i < d.nd; i++ {
@@ -267,18 +364,6 @@ func (d *decimal) atof32int() float32 {
return f
}
-// Reads a uint64 decimal mantissa, which might be truncated.
-func (d *decimal) atou64() (mant uint64, digits int) {
- const uint64digits = 19
- for i, c := range d.d[:d.nd] {
- if i == uint64digits {
- return mant, i
- }
- mant = 10*mant + uint64(c-'0')
- }
- return mant, d.nd
-}
-
// Exact powers of 10.
var float64pow10 = []float64{
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
@@ -287,69 +372,74 @@ var float64pow10 = []float64{
}
var float32pow10 = []float32{1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10}
-// If possible to convert decimal d to 64-bit float f exactly,
+// If possible to convert decimal representation to 64-bit float f exactly,
// entirely in floating-point math, do so, avoiding the expense of decimalToFloatBits.
// Three common cases:
// value is exact integer
// value is exact integer * exact power of ten
// value is exact integer / exact power of ten
// These all produce potentially inexact but correctly rounded answers.
-func (d *decimal) atof64() (f float64, ok bool) {
- // Exact integers are <= 10^15.
- // Exact powers of ten are <= 10^22.
- if d.nd > 15 {
+func atof64exact(mantissa uint64, exp int, neg bool) (f float64, ok bool) {
+ if mantissa>>float64info.mantbits != 0 {
return
}
+ f = float64(mantissa)
+ if neg {
+ f = -f
+ }
switch {
- case d.dp == d.nd: // int
- f := d.atof64int()
+ case exp == 0:
+ // an integer.
return f, true
-
- case d.dp > d.nd && d.dp <= 15+22: // int * 10^k
- f := d.atof64int()
- k := d.dp - d.nd
+ // Exact integers are <= 10^15.
+ // Exact powers of ten are <= 10^22.
+ case exp > 0 && exp <= 15+22: // int * 10^k
// If exponent is big but number of digits is not,
// can move a few zeros into the integer part.
- if k > 22 {
- f *= float64pow10[k-22]
- k = 22
+ if exp > 22 {
+ f *= float64pow10[exp-22]
+ exp = 22
}
- return f * float64pow10[k], true
-
- case d.dp < d.nd && d.nd-d.dp <= 22: // int / 10^k
- f := d.atof64int()
- return f / float64pow10[d.nd-d.dp], true
+ if f > 1e15 || f < -1e15 {
+ // the exponent was really too large.
+ return
+ }
+ return f * float64pow10[exp], true
+ case exp < 0 && exp >= -22: // int / 10^k
+ return f / float64pow10[-exp], true
}
return
}
-// If possible to convert decimal d to 32-bit float f exactly,
+// If possible to compute mantissa*10^exp to 32-bit float f exactly,
// entirely in floating-point math, do so, avoiding the machinery above.
-func (d *decimal) atof32() (f float32, ok bool) {
- // Exact integers are <= 10^7.
- // Exact powers of ten are <= 10^10.
- if d.nd > 7 {
+func atof32exact(mantissa uint64, exp int, neg bool) (f float32, ok bool) {
+ if mantissa>>float32info.mantbits != 0 {
return
}
+ f = float32(mantissa)
+ if neg {
+ f = -f
+ }
switch {
- case d.dp == d.nd: // int
- f := d.atof32int()
+ case exp == 0:
return f, true
-
- case d.dp > d.nd && d.dp <= 7+10: // int * 10^k
- f := d.atof32int()
- k := d.dp - d.nd
+ // Exact integers are <= 10^7.
+ // Exact powers of ten are <= 10^10.
+ case exp > 0 && exp <= 7+10: // int * 10^k
// If exponent is big but number of digits is not,
// can move a few zeros into the integer part.
- if k > 10 {
- f *= float32pow10[k-10]
- k = 10
+ if exp > 10 {
+ f *= float32pow10[exp-10]
+ exp = 10
}
- return f * float32pow10[k], true
-
- case d.dp < d.nd && d.nd-d.dp <= 10: // int / 10^k
- f := d.atof32int()
- return f / float32pow10[d.nd-d.dp], true
+ if f > 1e7 || f < -1e7 {
+ // the exponent was really too large.
+ return
+ }
+ return f * float32pow10[exp], true
+ case exp < 0 && exp >= -10: // int / 10^k
+ return f / float32pow10[-exp], true
}
return
}
@@ -361,15 +451,32 @@ func atof32(s string) (f float32, err error) {
return float32(val), nil
}
+ if optimize {
+ // Parse mantissa and exponent.
+ mantissa, exp, neg, trunc, ok := readFloat(s)
+ if ok {
+ // Try pure floating-point arithmetic conversion.
+ if !trunc {
+ if f, ok := atof32exact(mantissa, exp, neg); ok {
+ return f, nil
+ }
+ }
+ // Try another fast path.
+ ext := new(extFloat)
+ if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float32info); ok {
+ b, ovf := ext.floatBits(&float32info)
+ f = math.Float32frombits(uint32(b))
+ if ovf {
+ err = rangeError(fnParseFloat, s)
+ }
+ return f, err
+ }
+ }
+ }
var d decimal
if !d.set(s) {
return 0, syntaxError(fnParseFloat, s)
}
- if optimize {
- if f, ok := d.atof32(); ok {
- return f, nil
- }
- }
b, ovf := d.floatBits(&float32info)
f = math.Float32frombits(uint32(b))
if ovf {
@@ -383,26 +490,32 @@ func atof64(s string) (f float64, err error) {
return val, nil
}
- var d decimal
- if !d.set(s) {
- return 0, syntaxError(fnParseFloat, s)
- }
if optimize {
- if f, ok := d.atof64(); ok {
- return f, nil
- }
-
- // Try another fast path.
- ext := new(extFloat)
- if ok := ext.AssignDecimal(&d); ok {
- b, ovf := ext.floatBits()
- f = math.Float64frombits(b)
- if ovf {
- err = rangeError(fnParseFloat, s)
+ // Parse mantissa and exponent.
+ mantissa, exp, neg, trunc, ok := readFloat(s)
+ if ok {
+ // Try pure floating-point arithmetic conversion.
+ if !trunc {
+ if f, ok := atof64exact(mantissa, exp, neg); ok {
+ return f, nil
+ }
+ }
+ // Try another fast path.
+ ext := new(extFloat)
+ if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float64info); ok {
+ b, ovf := ext.floatBits(&float64info)
+ f = math.Float64frombits(b)
+ if ovf {
+ err = rangeError(fnParseFloat, s)
+ }
+ return f, err
}
- return f, err
}
}
+ var d decimal
+ if !d.set(s) {
+ return 0, syntaxError(fnParseFloat, s)
+ }
b, ovf := d.floatBits(&float64info)
f = math.Float64frombits(b)
if ovf {
diff --git a/src/pkg/strconv/atof_test.go b/src/pkg/strconv/atof_test.go
index 599502382..ba4933218 100644
--- a/src/pkg/strconv/atof_test.go
+++ b/src/pkg/strconv/atof_test.go
@@ -110,6 +110,7 @@ var atoftests = []atofTest{
{"1e", "0", ErrSyntax},
{"1e-", "0", ErrSyntax},
{".e-1", "0", ErrSyntax},
+ {"1\x00.2", "0", ErrSyntax},
// http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
{"2.2250738585072012e-308", "2.2250738585072014e-308", nil},
@@ -134,6 +135,54 @@ var atoftests = []atofTest{
{"1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1", "1.0000000000000002", nil},
}
+var atof32tests = []atofTest{
+ // Exactly halfway between 1 and the next float32.
+ // Round to even (down).
+ {"1.000000059604644775390625", "1", nil},
+ // Slightly lower.
+ {"1.000000059604644775390624", "1", nil},
+ // Slightly higher.
+ {"1.000000059604644775390626", "1.0000001", nil},
+ // Slightly higher, but you have to read all the way to the end.
+ {"1.000000059604644775390625" + strings.Repeat("0", 10000) + "1", "1.0000001", nil},
+
+ // largest float32: (1<<128) * (1 - 2^-24)
+ {"340282346638528859811704183484516925440", "3.4028235e+38", nil},
+ {"-340282346638528859811704183484516925440", "-3.4028235e+38", nil},
+ // next float32 - too large
+ {"3.4028236e38", "+Inf", ErrRange},
+ {"-3.4028236e38", "-Inf", ErrRange},
+ // the border is 3.40282356779...e+38
+ // borderline - okay
+ {"3.402823567e38", "3.4028235e+38", nil},
+ {"-3.402823567e38", "-3.4028235e+38", nil},
+ // borderline - too large
+ {"3.4028235678e38", "+Inf", ErrRange},
+ {"-3.4028235678e38", "-Inf", ErrRange},
+
+ // Denormals: less than 2^-126
+ {"1e-38", "1e-38", nil},
+ {"1e-39", "1e-39", nil},
+ {"1e-40", "1e-40", nil},
+ {"1e-41", "1e-41", nil},
+ {"1e-42", "1e-42", nil},
+ {"1e-43", "1e-43", nil},
+ {"1e-44", "1e-44", nil},
+ {"6e-45", "6e-45", nil}, // 4p-149 = 5.6e-45
+ {"5e-45", "6e-45", nil},
+ // Smallest denormal
+ {"1e-45", "1e-45", nil}, // 1p-149 = 1.4e-45
+ {"2e-45", "1e-45", nil},
+
+ // 2^92 = 8388608p+69 = 4951760157141521099596496896 (4.9517602e27)
+ // is an exact power of two that needs 8 decimal digits to be correctly
+ // parsed back.
+ // The float32 before is 16777215p+68 = 4.95175986e+27
+ // The halfway is 4.951760009. A bad algorithm that thinks the previous
+ // float32 is 8388607p+69 will shorten incorrectly to 4.95176e+27.
+ {"4951760157141521099596496896", "4.9517602e+27", nil},
+}
+
type atofSimpleTest struct {
x float64
s string
@@ -154,6 +203,12 @@ func init() {
test.err = &NumError{"ParseFloat", test.in, test.err}
}
}
+ for i := range atof32tests {
+ test := &atof32tests[i]
+ if test.err != nil {
+ test.err = &NumError{"ParseFloat", test.in, test.err}
+ }
+ }
// Generate random inputs for tests and benchmarks
rand.Seed(time.Now().UnixNano())
@@ -206,6 +261,19 @@ func testAtof(t *testing.T, opt bool) {
}
}
}
+ for _, test := range atof32tests {
+ out, err := ParseFloat(test.in, 32)
+ out32 := float32(out)
+ if float64(out32) != out {
+ t.Errorf("ParseFloat(%v, 32) = %v, not a float32 (closest is %v)", test.in, out, float64(out32))
+ continue
+ }
+ outs := FormatFloat(float64(out32), 'g', -1, 32)
+ if outs != test.out || !reflect.DeepEqual(err, test.err) {
+ t.Errorf("ParseFloat(%v, 32) = %v, %v want %v, %v # %v",
+ test.in, out32, err, test.out, test.err, out)
+ }
+ }
SetOptimize(oldopt)
}
@@ -264,6 +332,35 @@ func TestRoundTrip(t *testing.T) {
}
}
+// TestRoundTrip32 tries a fraction of all finite positive float32 values.
+func TestRoundTrip32(t *testing.T) {
+ step := uint32(997)
+ if testing.Short() {
+ step = 99991
+ }
+ count := 0
+ for i := uint32(0); i < 0xff<<23; i += step {
+ f := math.Float32frombits(i)
+ if i&1 == 1 {
+ f = -f // negative
+ }
+ s := FormatFloat(float64(f), 'g', -1, 32)
+
+ parsed, err := ParseFloat(s, 32)
+ parsed32 := float32(parsed)
+ switch {
+ case err != nil:
+ t.Errorf("ParseFloat(%q, 32) gave error %s", s, err)
+ case float64(parsed32) != parsed:
+ t.Errorf("ParseFloat(%q, 32) = %v, not a float32 (nearest is %v)", s, parsed, parsed32)
+ case parsed32 != f:
+ t.Errorf("ParseFloat(%q, 32) = %b (expected %b)", s, parsed32, f)
+ }
+ count++
+ }
+ t.Logf("tested %d float32's", count)
+}
+
func BenchmarkAtof64Decimal(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseFloat("33909", 64)
@@ -299,3 +396,35 @@ func BenchmarkAtof64RandomFloats(b *testing.B) {
ParseFloat(benchmarksRandomNormal[i%1024], 64)
}
}
+
+func BenchmarkAtof32Decimal(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("33909", 32)
+ }
+}
+
+func BenchmarkAtof32Float(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("339.778", 32)
+ }
+}
+
+func BenchmarkAtof32FloatExp(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ ParseFloat("12.3456e32", 32)
+ }
+}
+
+var float32strings [4096]string
+
+func BenchmarkAtof32Random(b *testing.B) {
+ n := uint32(997)
+ for i := range float32strings {
+ n = (99991*n + 42) % (0xff << 23)
+ float32strings[i] = FormatFloat(float64(math.Float32frombits(n)), 'g', -1, 32)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ ParseFloat(float32strings[i%4096], 32)
+ }
+}
diff --git a/src/pkg/strconv/atoi.go b/src/pkg/strconv/atoi.go
index 59ef264d1..21c690096 100644
--- a/src/pkg/strconv/atoi.go
+++ b/src/pkg/strconv/atoi.go
@@ -20,7 +20,7 @@ type NumError struct {
}
func (e *NumError) Error() string {
- return "strconv." + e.Func + ": " + `parsing "` + e.Num + `": ` + e.Err.Error()
+ return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
}
func syntaxError(fn, str string) *NumError {
@@ -44,7 +44,7 @@ func cutoff64(base int) uint64 {
}
// ParseUint is like ParseInt but for unsigned numbers.
-func ParseUint(s string, b int, bitSize int) (n uint64, err error) {
+func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
var cutoff, maxVal uint64
if bitSize == 0 {
@@ -57,32 +57,32 @@ func ParseUint(s string, b int, bitSize int) (n uint64, err error) {
err = ErrSyntax
goto Error
- case 2 <= b && b <= 36:
+ case 2 <= base && base <= 36:
// valid base; nothing to do
- case b == 0:
+ case base == 0:
// Look for octal, hex prefix.
switch {
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
- b = 16
+ base = 16
s = s[2:]
if len(s) < 1 {
err = ErrSyntax
goto Error
}
case s[0] == '0':
- b = 8
+ base = 8
default:
- b = 10
+ base = 10
}
default:
- err = errors.New("invalid base " + Itoa(b))
+ err = errors.New("invalid base " + Itoa(base))
goto Error
}
n = 0
- cutoff = cutoff64(b)
+ cutoff = cutoff64(base)
maxVal = 1<<uint(bitSize) - 1
for i := 0; i < len(s); i++ {
@@ -100,19 +100,19 @@ func ParseUint(s string, b int, bitSize int) (n uint64, err error) {
err = ErrSyntax
goto Error
}
- if int(v) >= b {
+ if int(v) >= base {
n = 0
err = ErrSyntax
goto Error
}
if n >= cutoff {
- // n*b overflows
+ // n*base overflows
n = 1<<64 - 1
err = ErrRange
goto Error
}
- n *= uint64(b)
+ n *= uint64(base)
n1 := n + uint64(v)
if n1 < n || n1 > maxVal {
diff --git a/src/pkg/strconv/atoi_test.go b/src/pkg/strconv/atoi_test.go
index d0e7b61db..940757307 100644
--- a/src/pkg/strconv/atoi_test.go
+++ b/src/pkg/strconv/atoi_test.go
@@ -5,6 +5,7 @@
package strconv_test
import (
+ "errors"
"reflect"
. "strconv"
"testing"
@@ -146,6 +147,16 @@ var atoi32tests = []atoi32Test{
{"-2147483649", -1 << 31, ErrRange},
}
+type numErrorTest struct {
+ num, want string
+}
+
+var numErrorTests = []numErrorTest{
+ {"0", `strconv.ParseFloat: parsing "0": failed`},
+ {"`", "strconv.ParseFloat: parsing \"`\": failed"},
+ {"1\x00.2", `strconv.ParseFloat: parsing "1\x00.2": failed`},
+}
+
func init() {
// The atoi routines return NumErrors wrapping
// the error and the string. Convert the tables above.
@@ -277,6 +288,19 @@ func TestParseInt(t *testing.T) {
}
}
+func TestNumError(t *testing.T) {
+ for _, test := range numErrorTests {
+ err := &NumError{
+ Func: "ParseFloat",
+ Num: test.num,
+ Err: errors.New("failed"),
+ }
+ if got := err.Error(); got != test.want {
+ t.Errorf(`(&NumError{"ParseFloat", %q, "failed"}).Error() = %v, want %v`, test.num, got, test.want)
+ }
+ }
+}
+
func BenchmarkAtoi(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseInt("12345678", 10, 0)
diff --git a/src/pkg/strconv/decimal.go b/src/pkg/strconv/decimal.go
index a75071dcc..42601283d 100644
--- a/src/pkg/strconv/decimal.go
+++ b/src/pkg/strconv/decimal.go
@@ -79,7 +79,7 @@ func trim(a *decimal) {
// Assign v to a.
func (a *decimal) Assign(v uint64) {
- var buf [50]byte
+ var buf [24]byte
// Write reversed decimal in buf.
n := 0
diff --git a/src/pkg/strconv/extfloat.go b/src/pkg/strconv/extfloat.go
index aa5e5607c..b7eaaa61b 100644
--- a/src/pkg/strconv/extfloat.go
+++ b/src/pkg/strconv/extfloat.go
@@ -4,8 +4,6 @@
package strconv
-import "math"
-
// An extFloat represents an extended floating-point number, with more
// precision than a float64. It does not try to save bits: the
// number represented by the structure is mant*(2^exp), with a negative
@@ -127,8 +125,7 @@ var powersOfTen = [...]extFloat{
// floatBits returns the bits of the float64 that best approximates
// the extFloat passed as receiver. Overflow is set to true if
// the resulting float64 is ±Inf.
-func (f *extFloat) floatBits() (bits uint64, overflow bool) {
- flt := &float64info
+func (f *extFloat) floatBits(flt *floatInfo) (bits uint64, overflow bool) {
f.Normalize()
exp := f.exp + 63
@@ -140,7 +137,7 @@ func (f *extFloat) floatBits() (bits uint64, overflow bool) {
exp += n
}
- // Extract 1+flt.mantbits bits.
+ // Extract 1+flt.mantbits bits from the 64-bit mantissa.
mant := f.mant >> (63 - flt.mantbits)
if f.mant&(1<<(62-flt.mantbits)) != 0 {
// Round up.
@@ -155,22 +152,14 @@ func (f *extFloat) floatBits() (bits uint64, overflow bool) {
// Infinities.
if exp-flt.bias >= 1<<flt.expbits-1 {
- goto overflow
- }
-
- // Denormalized?
- if mant&(1<<flt.mantbits) == 0 {
+ // ±Inf
+ mant = 0
+ exp = 1<<flt.expbits - 1 + flt.bias
+ overflow = true
+ } else if mant&(1<<flt.mantbits) == 0 {
+ // Denormalized?
exp = flt.bias
}
- goto out
-
-overflow:
- // ±Inf
- mant = 0
- exp = 1<<flt.expbits - 1 + flt.bias
- overflow = true
-
-out:
// Assemble bits.
bits = mant & (uint64(1)<<flt.mantbits - 1)
bits |= uint64((exp-flt.bias)&(1<<flt.expbits-1)) << flt.mantbits
@@ -180,40 +169,24 @@ out:
return
}
-// Assign sets f to the value of x.
-func (f *extFloat) Assign(x float64) {
- if x < 0 {
- x = -x
- f.neg = true
- }
- x, f.exp = math.Frexp(x)
- f.mant = uint64(x * float64(1<<64))
- f.exp -= 64
-}
-
-// AssignComputeBounds sets f to the value of x and returns
+// AssignComputeBounds sets f to the floating point value
+// defined by mant, exp and precision given by flt. It returns
// lower, upper such that any number in the closed interval
-// [lower, upper] is converted back to x.
-func (f *extFloat) AssignComputeBounds(x float64) (lower, upper extFloat) {
- // Special cases.
- bits := math.Float64bits(x)
- flt := &float64info
- neg := bits>>(flt.expbits+flt.mantbits) != 0
- expBiased := int(bits>>flt.mantbits) & (1<<flt.expbits - 1)
- mant := bits & (uint64(1)<<flt.mantbits - 1)
-
- if expBiased == 0 {
- // denormalized.
- f.mant = mant
- f.exp = 1 + flt.bias - int(flt.mantbits)
- } else {
- f.mant = mant | 1<<flt.mantbits
- f.exp = expBiased + flt.bias - int(flt.mantbits)
- }
+// [lower, upper] is converted back to the same floating point number.
+func (f *extFloat) AssignComputeBounds(mant uint64, exp int, neg bool, flt *floatInfo) (lower, upper extFloat) {
+ f.mant = mant
+ f.exp = exp - int(flt.mantbits)
f.neg = neg
+ if f.exp <= 0 && mant == (mant>>uint(-f.exp))<<uint(-f.exp) {
+ // An exact integer
+ f.mant >>= uint(-f.exp)
+ f.exp = 0
+ return *f, *f
+ }
+ expBiased := exp - flt.bias
upper = extFloat{mant: 2*f.mant + 1, exp: f.exp - 1, neg: f.neg}
- if mant != 0 || expBiased == 1 {
+ if mant != 1<<flt.mantbits || expBiased == 1 {
lower = extFloat{mant: 2*f.mant - 1, exp: f.exp - 1, neg: f.neg}
} else {
lower = extFloat{mant: 4*f.mant - 1, exp: f.exp - 2, neg: f.neg}
@@ -223,20 +196,38 @@ func (f *extFloat) AssignComputeBounds(x float64) (lower, upper extFloat) {
// Normalize normalizes f so that the highest bit of the mantissa is
// set, and returns the number by which the mantissa was left-shifted.
-func (f *extFloat) Normalize() uint {
- if f.mant == 0 {
+func (f *extFloat) Normalize() (shift uint) {
+ mant, exp := f.mant, f.exp
+ if mant == 0 {
return 0
}
- exp_before := f.exp
- for f.mant < (1 << 55) {
- f.mant <<= 8
- f.exp -= 8
+ if mant>>(64-32) == 0 {
+ mant <<= 32
+ exp -= 32
+ }
+ if mant>>(64-16) == 0 {
+ mant <<= 16
+ exp -= 16
}
- for f.mant < (1 << 63) {
- f.mant <<= 1
- f.exp -= 1
+ if mant>>(64-8) == 0 {
+ mant <<= 8
+ exp -= 8
}
- return uint(exp_before - f.exp)
+ if mant>>(64-4) == 0 {
+ mant <<= 4
+ exp -= 4
+ }
+ if mant>>(64-2) == 0 {
+ mant <<= 2
+ exp -= 2
+ }
+ if mant>>(64-1) == 0 {
+ mant <<= 1
+ exp -= 1
+ }
+ shift = uint(f.exp - exp)
+ f.mant, f.exp = mant, exp
+ return
}
// Multiply sets f to the product f*g: the result is correctly rounded,
@@ -264,24 +255,22 @@ var uint64pow10 = [...]uint64{
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
}
-// AssignDecimal sets f to an approximate value of the decimal d. It
+// AssignDecimal sets f to an approximate value mantissa*10^exp. It
// returns true if the value represented by f is guaranteed to be the
-// best approximation of d after being rounded to a float64.
-func (f *extFloat) AssignDecimal(d *decimal) (ok bool) {
+// best approximation of d after being rounded to a float64 or
+// float32 depending on flt.
+func (f *extFloat) AssignDecimal(mantissa uint64, exp10 int, neg bool, trunc bool, flt *floatInfo) (ok bool) {
const uint64digits = 19
const errorscale = 8
- mant10, digits := d.atou64()
- exp10 := d.dp - digits
errors := 0 // An upper bound for error, computed in errorscale*ulp.
-
- if digits < d.nd {
+ if trunc {
// the decimal number was truncated.
errors += errorscale / 2
}
- f.mant = mant10
+ f.mant = mantissa
f.exp = 0
- f.neg = d.neg
+ f.neg = neg
// Multiply by powers of ten.
i := (exp10 - firstPowerOfTen) / stepPowerOfTen
@@ -291,9 +280,9 @@ func (f *extFloat) AssignDecimal(d *decimal) (ok bool) {
adjExp := (exp10 - firstPowerOfTen) % stepPowerOfTen
// We multiply by exp%step
- if digits+adjExp <= uint64digits {
- // We can multiply the mantissa
- f.mant *= uint64(float64pow10[adjExp])
+ if adjExp < uint64digits && mantissa < uint64pow10[uint64digits-adjExp] {
+ // We can multiply the mantissa exactly.
+ f.mant *= uint64pow10[adjExp]
f.Normalize()
} else {
f.Normalize()
@@ -318,10 +307,10 @@ func (f *extFloat) AssignDecimal(d *decimal) (ok bool) {
// The 64 bits mantissa is 1 + 52 bits for float64 + 11 extra bits.
//
// In many cases the approximation will be good enough.
- const denormalExp = -1023 - 63
- flt := &float64info
+ denormalExp := flt.bias - 63
var extrabits uint
if f.exp <= denormalExp {
+ // f.mant * 2^f.exp is smaller than 2^(flt.bias+1).
extrabits = uint(63 - flt.mantbits + 1 + uint(denormalExp-f.exp))
} else {
extrabits = uint(63 - flt.mantbits)
@@ -344,16 +333,17 @@ func (f *extFloat) AssignDecimal(d *decimal) (ok bool) {
// f by an approximate power of ten 10^-exp, and returns exp10, so
// that f*10^exp10 has the same value as the old f, up to an ulp,
// as well as the index of 10^-exp in the powersOfTen table.
-// The arguments expMin and expMax constrain the final value of the
-// binary exponent of f.
-func (f *extFloat) frexp10(expMin, expMax int) (exp10, index int) {
- // it is illegal to call this function with a too restrictive exponent range.
- if expMax-expMin <= 25 {
- panic("strconv: invalid exponent range")
- }
+func (f *extFloat) frexp10() (exp10, index int) {
+ // The constants expMin and expMax constrain the final value of the
+ // binary exponent of f. We want a small integral part in the result
+ // because finding digits of an integer requires divisions, whereas
+ // digits of the fractional part can be found by repeatedly multiplying
+ // by 10.
+ const expMin = -60
+ const expMax = -32
// Find power of ten such that x * 10^n has a binary exponent
- // between expMin and expMax
- approxExp10 := -(f.exp + 100) * 28 / 93 // log(10)/log(2) is close to 93/28.
+ // between expMin and expMax.
+ approxExp10 := ((expMin+expMax)/2 - f.exp) * 28 / 93 // log(10)/log(2) is close to 93/28.
i := (approxExp10 - firstPowerOfTen) / stepPowerOfTen
Loop:
for {
@@ -375,26 +365,202 @@ Loop:
}
// frexp10Many applies a common shift by a power of ten to a, b, c.
-func frexp10Many(expMin, expMax int, a, b, c *extFloat) (exp10 int) {
- exp10, i := c.frexp10(expMin, expMax)
+func frexp10Many(a, b, c *extFloat) (exp10 int) {
+ exp10, i := c.frexp10()
a.Multiply(powersOfTen[i])
b.Multiply(powersOfTen[i])
return
}
+// FixedDecimal stores in d the first n significant digits
+// of the decimal representation of f. It returns false
+// if it cannot be sure of the answer.
+func (f *extFloat) FixedDecimal(d *decimalSlice, n int) bool {
+ if f.mant == 0 {
+ d.nd = 0
+ d.dp = 0
+ d.neg = f.neg
+ return true
+ }
+ if n == 0 {
+ panic("strconv: internal error: extFloat.FixedDecimal called with n == 0")
+ }
+ // Multiply by an appropriate power of ten to have a reasonable
+ // number to process.
+ f.Normalize()
+ exp10, _ := f.frexp10()
+
+ shift := uint(-f.exp)
+ integer := uint32(f.mant >> shift)
+ fraction := f.mant - (uint64(integer) << shift)
+ ε := uint64(1) // ε is the uncertainty we have on the mantissa of f.
+
+ // Write exactly n digits to d.
+ needed := n // how many digits are left to write.
+ integerDigits := 0 // the number of decimal digits of integer.
+ pow10 := uint64(1) // the power of ten by which f was scaled.
+ for i, pow := 0, uint64(1); i < 20; i++ {
+ if pow > uint64(integer) {
+ integerDigits = i
+ break
+ }
+ pow *= 10
+ }
+ rest := integer
+ if integerDigits > needed {
+ // the integral part is already large, trim the last digits.
+ pow10 = uint64pow10[integerDigits-needed]
+ integer /= uint32(pow10)
+ rest -= integer * uint32(pow10)
+ } else {
+ rest = 0
+ }
+
+ // Write the digits of integer: the digits of rest are omitted.
+ var buf [32]byte
+ pos := len(buf)
+ for v := integer; v > 0; {
+ v1 := v / 10
+ v -= 10 * v1
+ pos--
+ buf[pos] = byte(v + '0')
+ v = v1
+ }
+ for i := pos; i < len(buf); i++ {
+ d.d[i-pos] = buf[i]
+ }
+ nd := len(buf) - pos
+ d.nd = nd
+ d.dp = integerDigits + exp10
+ needed -= nd
+
+ if needed > 0 {
+ if rest != 0 || pow10 != 1 {
+ panic("strconv: internal error, rest != 0 but needed > 0")
+ }
+ // Emit digits for the fractional part. Each time, 10*fraction
+ // fits in a uint64 without overflow.
+ for needed > 0 {
+ fraction *= 10
+ ε *= 10 // the uncertainty scales as we multiply by ten.
+ if 2*ε > 1<<shift {
+ // the error is so large it could modify which digit to write, abort.
+ return false
+ }
+ digit := fraction >> shift
+ d.d[nd] = byte(digit + '0')
+ fraction -= digit << shift
+ nd++
+ needed--
+ }
+ d.nd = nd
+ }
+
+ // We have written a truncation of f (a numerator / 10^d.dp). The remaining part
+ // can be interpreted as a small number (< 1) to be added to the last digit of the
+ // numerator.
+ //
+ // If rest > 0, the amount is:
+ // (rest<<shift | fraction) / (pow10 << shift)
+ // fraction being known with a ±ε uncertainty.
+ // The fact that n > 0 guarantees that pow10 << shift does not overflow a uint64.
+ //
+ // If rest = 0, pow10 == 1 and the amount is
+ // fraction / (1 << shift)
+ // fraction being known with a ±ε uncertainty.
+ //
+ // We pass this information to the rounding routine for adjustment.
+
+ ok := adjustLastDigitFixed(d, uint64(rest)<<shift|fraction, pow10, shift, ε)
+ if !ok {
+ return false
+ }
+ // Trim trailing zeros.
+ for i := d.nd - 1; i >= 0; i-- {
+ if d.d[i] != '0' {
+ d.nd = i + 1
+ break
+ }
+ }
+ return true
+}
+
+// adjustLastDigitFixed assumes d contains the representation of the integral part
+// of some number, whose fractional part is num / (den << shift). The numerator
+// num is only known up to an uncertainty of size ε, assumed to be less than
+// (den << shift)/2.
+//
+// It will increase the last digit by one to account for correct rounding, typically
+// when the fractional part is greater than 1/2, and will return false if ε is such
+// that no correct answer can be given.
+func adjustLastDigitFixed(d *decimalSlice, num, den uint64, shift uint, ε uint64) bool {
+ if num > den<<shift {
+ panic("strconv: num > den<<shift in adjustLastDigitFixed")
+ }
+ if 2*ε > den<<shift {
+ panic("strconv: ε > (den<<shift)/2")
+ }
+ if 2*(num+ε) < den<<shift {
+ return true
+ }
+ if 2*(num-ε) > den<<shift {
+ // increment d by 1.
+ i := d.nd - 1
+ for ; i >= 0; i-- {
+ if d.d[i] == '9' {
+ d.nd--
+ } else {
+ break
+ }
+ }
+ if i < 0 {
+ d.d[0] = '1'
+ d.nd = 1
+ d.dp++
+ } else {
+ d.d[i]++
+ }
+ return true
+ }
+ return false
+}
+
// ShortestDecimal stores in d the shortest decimal representation of f
// which belongs to the open interval (lower, upper), where f is supposed
// to lie. It returns false whenever the result is unsure. The implementation
// uses the Grisu3 algorithm.
-func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool {
+func (f *extFloat) ShortestDecimal(d *decimalSlice, lower, upper *extFloat) bool {
if f.mant == 0 {
- d.d[0] = '0'
- d.nd = 1
+ d.nd = 0
d.dp = 0
d.neg = f.neg
+ return true
+ }
+ if f.exp == 0 && *lower == *f && *lower == *upper {
+ // an exact integer.
+ var buf [24]byte
+ n := len(buf) - 1
+ for v := f.mant; v > 0; {
+ v1 := v / 10
+ v -= 10 * v1
+ buf[n] = byte(v + '0')
+ n--
+ v = v1
+ }
+ nd := len(buf) - n - 1
+ for i := 0; i < nd; i++ {
+ d.d[i] = buf[n+1+i]
+ }
+ d.nd, d.dp = nd, nd
+ for d.nd > 0 && d.d[d.nd-1] == '0' {
+ d.nd--
+ }
+ if d.nd == 0 {
+ d.dp = 0
+ }
+ d.neg = f.neg
+ return true
}
- const minExp = -60
- const maxExp = -32
upper.Normalize()
// Uniformize exponents.
if f.exp > upper.exp {
@@ -406,7 +572,7 @@ func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool {
lower.exp = upper.exp
}
- exp10 := frexp10Many(minExp, maxExp, lower, f, upper)
+ exp10 := frexp10Many(lower, f, upper)
// Take a safety margin due to rounding in frexp10Many, but we lose precision.
upper.mant++
lower.mant--
@@ -424,10 +590,12 @@ func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool {
// Count integral digits: there are at most 10.
var integerDigits int
- for i, pow := range uint64pow10 {
- if uint64(integer) >= pow {
- integerDigits = i + 1
+ for i, pow := 0, uint64(1); i < 20; i++ {
+ if pow > uint64(integer) {
+ integerDigits = i
+ break
}
+ pow *= 10
}
for i := 0; i < integerDigits; i++ {
pow := uint64pow10[integerDigits-i-1]
@@ -471,11 +639,11 @@ func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool {
return false
}
-// adjustLastDigit modifies d = x-currentDiff*ε, to get closest to
+// adjustLastDigit modifies d = x-currentDiff*ε, to get closest to
// d = x-targetDiff*ε, without becoming smaller than x-maxDiff*ε.
// It assumes that a decimal digit is worth ulpDecimal*ε, and that
// all data is known with a error estimate of ulpBinary*ε.
-func adjustLastDigit(d *decimal, currentDiff, targetDiff, maxDiff, ulpDecimal, ulpBinary uint64) bool {
+func adjustLastDigit(d *decimalSlice, currentDiff, targetDiff, maxDiff, ulpDecimal, ulpBinary uint64) bool {
if ulpDecimal < 2*ulpBinary {
// Approximation is too wide.
return false
diff --git a/src/pkg/strconv/fp_test.go b/src/pkg/strconv/fp_test.go
index 171defa44..6de2f8bc6 100644
--- a/src/pkg/strconv/fp_test.go
+++ b/src/pkg/strconv/fp_test.go
@@ -7,7 +7,6 @@ package strconv_test
import (
"bufio"
"fmt"
- "io"
"os"
"strconv"
"strings"
@@ -96,31 +95,22 @@ func myatof32(s string) (f float32, ok bool) {
}
func TestFp(t *testing.T) {
- f, err := os.Open("testfp.txt")
+ f, err := os.Open("testdata/testfp.txt")
if err != nil {
- t.Fatal("testfp: open testfp.txt:", err)
+ t.Fatal("testfp: open testdata/testfp.txt:", err)
}
defer f.Close()
- b := bufio.NewReader(f)
+ s := bufio.NewScanner(f)
- lineno := 0
- for {
- line, err2 := b.ReadString('\n')
- if err2 == io.EOF {
- break
- }
- if err2 != nil {
- t.Fatal("testfp: read testfp.txt: " + err2.Error())
- }
- line = line[0 : len(line)-1]
- lineno++
+ for lineno := 1; s.Scan(); lineno++ {
+ line := s.Text()
if len(line) == 0 || line[0] == '#' {
continue
}
a := strings.Split(line, " ")
if len(a) != 4 {
- t.Error("testfp.txt:", lineno, ": wrong field count")
+ t.Error("testdata/testfp.txt:", lineno, ": wrong field count")
continue
}
var s string
@@ -130,22 +120,25 @@ func TestFp(t *testing.T) {
var ok bool
v, ok = myatof64(a[2])
if !ok {
- t.Error("testfp.txt:", lineno, ": cannot atof64 ", a[2])
+ t.Error("testdata/testfp.txt:", lineno, ": cannot atof64 ", a[2])
continue
}
s = fmt.Sprintf(a[1], v)
case "float32":
v1, ok := myatof32(a[2])
if !ok {
- t.Error("testfp.txt:", lineno, ": cannot atof32 ", a[2])
+ t.Error("testdata/testfp.txt:", lineno, ": cannot atof32 ", a[2])
continue
}
s = fmt.Sprintf(a[1], v1)
v = float64(v1)
}
if s != a[3] {
- t.Error("testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ",
+ t.Error("testdata/testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ",
"want ", a[3], " got ", s)
}
}
+ if s.Err() != nil {
+ t.Fatal("testfp: read testdata/testfp.txt: ", s.Err())
+ }
}
diff --git a/src/pkg/strconv/ftoa.go b/src/pkg/strconv/ftoa.go
index 8eefbee79..1a9c41b85 100644
--- a/src/pkg/strconv/ftoa.go
+++ b/src/pkg/strconv/ftoa.go
@@ -98,42 +98,79 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
return fmtB(dst, neg, mant, exp, flt)
}
+ if !optimize {
+ return bigFtoa(dst, prec, fmt, neg, mant, exp, flt)
+ }
+
+ var digs decimalSlice
+ ok := false
// Negative precision means "only as much as needed to be exact."
shortest := prec < 0
-
- d := new(decimal)
if shortest {
- ok := false
- if optimize && bitSize == 64 {
- // Try Grisu3 algorithm.
- f := new(extFloat)
- lower, upper := f.AssignComputeBounds(val)
- ok = f.ShortestDecimal(d, &lower, &upper)
- }
+ // Try Grisu3 algorithm.
+ f := new(extFloat)
+ lower, upper := f.AssignComputeBounds(mant, exp, neg, flt)
+ var buf [32]byte
+ digs.d = buf[:]
+ ok = f.ShortestDecimal(&digs, &lower, &upper)
if !ok {
- // Create exact decimal representation.
- // The shift is exp - flt.mantbits because mant is a 1-bit integer
- // followed by a flt.mantbits fraction, and we are treating it as
- // a 1+flt.mantbits-bit integer.
- d.Assign(mant)
- d.Shift(exp - int(flt.mantbits))
- roundShortest(d, mant, exp, flt)
+ return bigFtoa(dst, prec, fmt, neg, mant, exp, flt)
}
// Precision for shortest representation mode.
- if prec < 0 {
- switch fmt {
- case 'e', 'E':
- prec = d.nd - 1
- case 'f':
- prec = max(d.nd-d.dp, 0)
- case 'g', 'G':
- prec = d.nd
+ switch fmt {
+ case 'e', 'E':
+ prec = digs.nd - 1
+ case 'f':
+ prec = max(digs.nd-digs.dp, 0)
+ case 'g', 'G':
+ prec = digs.nd
+ }
+ } else if fmt != 'f' {
+ // Fixed number of digits.
+ digits := prec
+ switch fmt {
+ case 'e', 'E':
+ digits++
+ case 'g', 'G':
+ if prec == 0 {
+ prec = 1
}
+ digits = prec
+ }
+ if digits <= 15 {
+ // try fast algorithm when the number of digits is reasonable.
+ var buf [24]byte
+ digs.d = buf[:]
+ f := extFloat{mant, exp - int(flt.mantbits), neg}
+ ok = f.FixedDecimal(&digs, digits)
+ }
+ }
+ if !ok {
+ return bigFtoa(dst, prec, fmt, neg, mant, exp, flt)
+ }
+ return formatDigits(dst, shortest, neg, digs, prec, fmt)
+}
+
+// bigFtoa uses multiprecision computations to format a float.
+func bigFtoa(dst []byte, prec int, fmt byte, neg bool, mant uint64, exp int, flt *floatInfo) []byte {
+ d := new(decimal)
+ d.Assign(mant)
+ d.Shift(exp - int(flt.mantbits))
+ var digs decimalSlice
+ shortest := prec < 0
+ if shortest {
+ roundShortest(d, mant, exp, flt)
+ digs = decimalSlice{d: d.d[:], nd: d.nd, dp: d.dp}
+ // Precision for shortest representation mode.
+ switch fmt {
+ case 'e', 'E':
+ prec = digs.nd - 1
+ case 'f':
+ prec = max(digs.nd-digs.dp, 0)
+ case 'g', 'G':
+ prec = digs.nd
}
} else {
- // Create exact decimal representation.
- d.Assign(mant)
- d.Shift(exp - int(flt.mantbits))
// Round appropriately.
switch fmt {
case 'e', 'E':
@@ -146,18 +183,22 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
}
d.Round(prec)
}
+ digs = decimalSlice{d: d.d[:], nd: d.nd, dp: d.dp}
}
+ return formatDigits(dst, shortest, neg, digs, prec, fmt)
+}
+func formatDigits(dst []byte, shortest bool, neg bool, digs decimalSlice, prec int, fmt byte) []byte {
switch fmt {
case 'e', 'E':
- return fmtE(dst, neg, d, prec, fmt)
+ return fmtE(dst, neg, digs, prec, fmt)
case 'f':
- return fmtF(dst, neg, d, prec)
+ return fmtF(dst, neg, digs, prec)
case 'g', 'G':
// trailing fractional zeros in 'e' form will be trimmed.
eprec := prec
- if eprec > d.nd && d.nd >= d.dp {
- eprec = d.nd
+ if eprec > digs.nd && digs.nd >= digs.dp {
+ eprec = digs.nd
}
// %e is used if the exponent from the conversion
// is less than -4 or greater than or equal to the precision.
@@ -165,17 +206,17 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
if shortest {
eprec = 6
}
- exp := d.dp - 1
+ exp := digs.dp - 1
if exp < -4 || exp >= eprec {
- if prec > d.nd {
- prec = d.nd
+ if prec > digs.nd {
+ prec = digs.nd
}
- return fmtE(dst, neg, d, prec-1, fmt+'e'-'g')
+ return fmtE(dst, neg, digs, prec-1, fmt+'e'-'g')
}
- if prec > d.dp {
- prec = d.nd
+ if prec > digs.dp {
+ prec = digs.nd
}
- return fmtF(dst, neg, d, max(prec-d.dp, 0))
+ return fmtF(dst, neg, digs, max(prec-digs.dp, 0))
}
// unknown format
@@ -214,7 +255,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
// d = mant << (exp - mantbits)
// Next highest floating point number is mant+1 << exp-mantbits.
- // Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1.
+ // Our upper bound is halfway between, mant*2+1 << exp-mantbits-1.
upper := new(decimal)
upper.Assign(mant*2 + 1)
upper.Shift(exp - int(flt.mantbits) - 1)
@@ -224,7 +265,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
// unless mant-1 drops the significant bit and exp is not the minimum exp,
// in which case the next lowest is mant*2-1 << exp-mantbits-1.
// Either way, call it mantlo << explo-mantbits.
- // Our lower bound is halfway inbetween, mantlo*2+1 << explo-mantbits-1.
+ // Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1.
var mantlo uint64
var explo int
if mant > 1<<flt.mantbits || exp == minexp {
@@ -283,8 +324,14 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
}
}
+type decimalSlice struct {
+ d []byte
+ nd, dp int
+ neg bool
+}
+
// %e: -d.ddddde±dd
-func fmtE(dst []byte, neg bool, d *decimal, prec int, fmt byte) []byte {
+func fmtE(dst []byte, neg bool, d decimalSlice, prec int, fmt byte) []byte {
// sign
if neg {
dst = append(dst, '-')
@@ -300,12 +347,15 @@ func fmtE(dst []byte, neg bool, d *decimal, prec int, fmt byte) []byte {
// .moredigits
if prec > 0 {
dst = append(dst, '.')
- for i := 1; i <= prec; i++ {
- ch = '0'
- if i < d.nd {
- ch = d.d[i]
- }
- dst = append(dst, ch)
+ i := 1
+ m := d.nd + prec + 1 - max(d.nd, prec+1)
+ for i < m {
+ dst = append(dst, d.d[i])
+ i++
+ }
+ for i <= prec {
+ dst = append(dst, '0')
+ i++
}
}
@@ -335,17 +385,20 @@ func fmtE(dst []byte, neg bool, d *decimal, prec int, fmt byte) []byte {
i--
buf[i] = byte(exp + '0')
- // leading zeroes
- if i > len(buf)-2 {
- i--
- buf[i] = '0'
+ switch i {
+ case 0:
+ dst = append(dst, buf[0], buf[1], buf[2])
+ case 1:
+ dst = append(dst, buf[1], buf[2])
+ case 2:
+ // leading zeroes
+ dst = append(dst, '0', buf[2])
}
-
- return append(dst, buf[i:]...)
+ return dst
}
// %f: -ddddddd.ddddd
-func fmtF(dst []byte, neg bool, d *decimal, prec int) []byte {
+func fmtF(dst []byte, neg bool, d decimalSlice, prec int) []byte {
// sign
if neg {
dst = append(dst, '-')
diff --git a/src/pkg/strconv/ftoa_test.go b/src/pkg/strconv/ftoa_test.go
index 7d8617a85..39b861547 100644
--- a/src/pkg/strconv/ftoa_test.go
+++ b/src/pkg/strconv/ftoa_test.go
@@ -163,6 +163,7 @@ func TestFtoaRandom(t *testing.T) {
for i := 0; i < N; i++ {
bits := uint64(rand.Uint32())<<32 | uint64(rand.Uint32())
x := math.Float64frombits(bits)
+
shortFast := FormatFloat(x, 'g', -1, 64)
SetOptimize(false)
shortSlow := FormatFloat(x, 'g', -1, 64)
@@ -170,23 +171,15 @@ func TestFtoaRandom(t *testing.T) {
if shortSlow != shortFast {
t.Errorf("%b printed as %s, want %s", x, shortFast, shortSlow)
}
- }
-}
-func TestAppendFloatDoesntAllocate(t *testing.T) {
- n := numAllocations(func() {
- var buf [64]byte
- AppendFloat(buf[:0], 1.23, 'g', 5, 64)
- })
- want := 1 // TODO(bradfitz): this might be 0, once escape analysis is better
- if n != want {
- t.Errorf("with local buffer, did %d allocations, want %d", n, want)
- }
- n = numAllocations(func() {
- AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64)
- })
- if n != 0 {
- t.Errorf("with reused buffer, did %d allocations, want 0", n)
+ prec := rand.Intn(12) + 5
+ shortFast = FormatFloat(x, 'e', prec, 64)
+ SetOptimize(false)
+ shortSlow = FormatFloat(x, 'e', prec, 64)
+ SetOptimize(true)
+ if shortSlow != shortFast {
+ t.Errorf("%b printed as %s, want %s", x, shortFast, shortSlow)
+ }
}
}
@@ -220,37 +213,28 @@ func BenchmarkFormatFloatBig(b *testing.B) {
}
}
-func BenchmarkAppendFloatDecimal(b *testing.B) {
- dst := make([]byte, 0, 30)
+func benchmarkAppendFloat(b *testing.B, f float64, fmt byte, prec, bitSize int) {
+ dst := make([]byte, 30)
for i := 0; i < b.N; i++ {
- AppendFloat(dst, 33909, 'g', -1, 64)
+ AppendFloat(dst[:0], f, fmt, prec, bitSize)
}
}
-func BenchmarkAppendFloat(b *testing.B) {
- dst := make([]byte, 0, 30)
- for i := 0; i < b.N; i++ {
- AppendFloat(dst, 339.7784, 'g', -1, 64)
- }
-}
-
-func BenchmarkAppendFloatExp(b *testing.B) {
- dst := make([]byte, 0, 30)
- for i := 0; i < b.N; i++ {
- AppendFloat(dst, -5.09e75, 'g', -1, 64)
- }
+func BenchmarkAppendFloatDecimal(b *testing.B) { benchmarkAppendFloat(b, 33909, 'g', -1, 64) }
+func BenchmarkAppendFloat(b *testing.B) { benchmarkAppendFloat(b, 339.7784, 'g', -1, 64) }
+func BenchmarkAppendFloatExp(b *testing.B) { benchmarkAppendFloat(b, -5.09e75, 'g', -1, 64) }
+func BenchmarkAppendFloatNegExp(b *testing.B) { benchmarkAppendFloat(b, -5.11e-95, 'g', -1, 64) }
+func BenchmarkAppendFloatBig(b *testing.B) {
+ benchmarkAppendFloat(b, 123456789123456789123456789, 'g', -1, 64)
}
-func BenchmarkAppendFloatNegExp(b *testing.B) {
- dst := make([]byte, 0, 30)
- for i := 0; i < b.N; i++ {
- AppendFloat(dst, -5.11e-95, 'g', -1, 64)
- }
-}
+func BenchmarkAppendFloat32Integer(b *testing.B) { benchmarkAppendFloat(b, 33909, 'g', -1, 32) }
+func BenchmarkAppendFloat32ExactFraction(b *testing.B) { benchmarkAppendFloat(b, 3.375, 'g', -1, 32) }
+func BenchmarkAppendFloat32Point(b *testing.B) { benchmarkAppendFloat(b, 339.7784, 'g', -1, 32) }
+func BenchmarkAppendFloat32Exp(b *testing.B) { benchmarkAppendFloat(b, -5.09e25, 'g', -1, 32) }
+func BenchmarkAppendFloat32NegExp(b *testing.B) { benchmarkAppendFloat(b, -5.11e-25, 'g', -1, 32) }
-func BenchmarkAppendFloatBig(b *testing.B) {
- dst := make([]byte, 0, 30)
- for i := 0; i < b.N; i++ {
- AppendFloat(dst, 123456789123456789123456789, 'g', -1, 64)
- }
-}
+func BenchmarkAppendFloat64Fixed1(b *testing.B) { benchmarkAppendFloat(b, 123456, 'e', 3, 64) }
+func BenchmarkAppendFloat64Fixed2(b *testing.B) { benchmarkAppendFloat(b, 123.456, 'e', 3, 64) }
+func BenchmarkAppendFloat64Fixed3(b *testing.B) { benchmarkAppendFloat(b, 1.23456e+78, 'e', 3, 64) }
+func BenchmarkAppendFloat64Fixed4(b *testing.B) { benchmarkAppendFloat(b, 1.23456e-78, 'e', 3, 64) }
diff --git a/src/pkg/strconv/isprint.go b/src/pkg/strconv/isprint.go
index a03a07bfb..db5f0fbae 100644
--- a/src/pkg/strconv/isprint.go
+++ b/src/pkg/strconv/isprint.go
@@ -3,7 +3,7 @@
package strconv
-// (474+134+42)*2 + (180)*4 = 2020 bytes
+// (470+136+60)*2 + (218)*4 = 2204 bytes
var isPrint16 = []uint16{
0x0020, 0x007e,
@@ -12,7 +12,7 @@ var isPrint16 = []uint16{
0x0384, 0x0527,
0x0531, 0x0556,
0x0559, 0x058a,
- 0x0591, 0x05c7,
+ 0x058f, 0x05c7,
0x05d0, 0x05ea,
0x05f0, 0x05f4,
0x0606, 0x061b,
@@ -23,7 +23,8 @@ var isPrint16 = []uint16{
0x0800, 0x082d,
0x0830, 0x085b,
0x085e, 0x085e,
- 0x0900, 0x098c,
+ 0x08a0, 0x08ac,
+ 0x08e4, 0x098c,
0x098f, 0x0990,
0x0993, 0x09b2,
0x09b6, 0x09b9,
@@ -99,12 +100,12 @@ var isPrint16 = []uint16{
0x0eaa, 0x0ebd,
0x0ec0, 0x0ecd,
0x0ed0, 0x0ed9,
- 0x0edc, 0x0edd,
+ 0x0edc, 0x0edf,
0x0f00, 0x0f6c,
0x0f71, 0x0fda,
- 0x1000, 0x10c5,
- 0x10d0, 0x10fc,
- 0x1100, 0x124d,
+ 0x1000, 0x10c7,
+ 0x10cd, 0x10cd,
+ 0x10d0, 0x124d,
0x1250, 0x125d,
0x1260, 0x128d,
0x1290, 0x12b5,
@@ -120,8 +121,7 @@ var isPrint16 = []uint16{
0x1720, 0x1736,
0x1740, 0x1753,
0x1760, 0x1773,
- 0x1780, 0x17b3,
- 0x17b6, 0x17dd,
+ 0x1780, 0x17dd,
0x17e0, 0x17e9,
0x17f0, 0x17f9,
0x1800, 0x180d,
@@ -145,13 +145,12 @@ var isPrint16 = []uint16{
0x1aa0, 0x1aad,
0x1b00, 0x1b4b,
0x1b50, 0x1b7c,
- 0x1b80, 0x1baa,
- 0x1bae, 0x1bb9,
- 0x1bc0, 0x1bf3,
+ 0x1b80, 0x1bf3,
0x1bfc, 0x1c37,
0x1c3b, 0x1c49,
0x1c4d, 0x1c7f,
- 0x1cd0, 0x1cf2,
+ 0x1cc0, 0x1cc7,
+ 0x1cd0, 0x1cf6,
0x1d00, 0x1de6,
0x1dfc, 0x1f15,
0x1f18, 0x1f1d,
@@ -165,7 +164,7 @@ var isPrint16 = []uint16{
0x2030, 0x205e,
0x2070, 0x2071,
0x2074, 0x209c,
- 0x20a0, 0x20b9,
+ 0x20a0, 0x20ba,
0x20d0, 0x20f0,
0x2100, 0x2189,
0x2190, 0x23f3,
@@ -173,12 +172,13 @@ var isPrint16 = []uint16{
0x2440, 0x244a,
0x2460, 0x2b4c,
0x2b50, 0x2b59,
- 0x2c00, 0x2cf1,
- 0x2cf9, 0x2d25,
- 0x2d30, 0x2d65,
+ 0x2c00, 0x2cf3,
+ 0x2cf9, 0x2d27,
+ 0x2d2d, 0x2d2d,
+ 0x2d30, 0x2d67,
0x2d6f, 0x2d70,
0x2d7f, 0x2d96,
- 0x2da0, 0x2e31,
+ 0x2da0, 0x2e3b,
0x2e80, 0x2ef3,
0x2f00, 0x2fd5,
0x2ff0, 0x2ffb,
@@ -188,16 +188,15 @@ var isPrint16 = []uint16{
0x3131, 0x31ba,
0x31c0, 0x31e3,
0x31f0, 0x4db5,
- 0x4dc0, 0x9fcb,
+ 0x4dc0, 0x9fcc,
0xa000, 0xa48c,
0xa490, 0xa4c6,
0xa4d0, 0xa62b,
- 0xa640, 0xa673,
- 0xa67c, 0xa697,
- 0xa6a0, 0xa6f7,
- 0xa700, 0xa791,
- 0xa7a0, 0xa7a9,
- 0xa7fa, 0xa82b,
+ 0xa640, 0xa697,
+ 0xa69f, 0xa6f7,
+ 0xa700, 0xa793,
+ 0xa7a0, 0xa7aa,
+ 0xa7f8, 0xa82b,
0xa830, 0xa839,
0xa840, 0xa877,
0xa880, 0xa8c4,
@@ -212,7 +211,7 @@ var isPrint16 = []uint16{
0xaa50, 0xaa59,
0xaa5c, 0xaa7b,
0xaa80, 0xaac2,
- 0xaadb, 0xaadf,
+ 0xaadb, 0xaaf6,
0xab01, 0xab06,
0xab09, 0xab0e,
0xab11, 0xab16,
@@ -222,8 +221,7 @@ var isPrint16 = []uint16{
0xac00, 0xd7a3,
0xd7b0, 0xd7c6,
0xd7cb, 0xd7fb,
- 0xf900, 0xfa2d,
- 0xfa30, 0xfa6d,
+ 0xf900, 0xfa6d,
0xfa70, 0xfad9,
0xfb00, 0xfb06,
0xfb13, 0xfb17,
@@ -252,8 +250,11 @@ var isNotPrint16 = []uint16{
0x03a2,
0x0560,
0x0588,
+ 0x0590,
0x06dd,
0x083f,
+ 0x08a1,
+ 0x08ff,
0x0978,
0x0980,
0x0984,
@@ -275,7 +276,6 @@ var isNotPrint16 = []uint16{
0x0ab4,
0x0ac6,
0x0aca,
- 0x0af0,
0x0b04,
0x0b29,
0x0b31,
@@ -327,6 +327,7 @@ var isNotPrint16 = []uint16{
0x0f98,
0x0fbd,
0x0fcd,
+ 0x10c6,
0x1249,
0x1257,
0x1259,
@@ -351,10 +352,9 @@ var isNotPrint16 = []uint16{
0x1ff5,
0x208f,
0x2700,
- 0x27cb,
- 0x27cd,
0x2c2f,
0x2c5f,
+ 0x2d26,
0x2da7,
0x2daf,
0x2db7,
@@ -406,6 +406,8 @@ var isPrint32 = []uint32{
0x010900, 0x01091b,
0x01091f, 0x010939,
0x01093f, 0x01093f,
+ 0x010980, 0x0109b7,
+ 0x0109be, 0x0109bf,
0x010a00, 0x010a06,
0x010a0c, 0x010a33,
0x010a38, 0x010a3a,
@@ -421,11 +423,21 @@ var isPrint32 = []uint32{
0x011000, 0x01104d,
0x011052, 0x01106f,
0x011080, 0x0110c1,
+ 0x0110d0, 0x0110e8,
+ 0x0110f0, 0x0110f9,
+ 0x011100, 0x011143,
+ 0x011180, 0x0111c8,
+ 0x0111d0, 0x0111d9,
+ 0x011680, 0x0116b7,
+ 0x0116c0, 0x0116c9,
0x012000, 0x01236e,
0x012400, 0x012462,
0x012470, 0x012473,
0x013000, 0x01342e,
0x016800, 0x016a38,
+ 0x016f00, 0x016f44,
+ 0x016f50, 0x016f7e,
+ 0x016f8f, 0x016f9f,
0x01b000, 0x01b001,
0x01d000, 0x01d0f5,
0x01d100, 0x01d126,
@@ -442,13 +454,21 @@ var isPrint32 = []uint32{
0x01d54a, 0x01d6a5,
0x01d6a8, 0x01d7cb,
0x01d7ce, 0x01d7ff,
+ 0x01ee00, 0x01ee24,
+ 0x01ee27, 0x01ee3b,
+ 0x01ee42, 0x01ee42,
+ 0x01ee47, 0x01ee54,
+ 0x01ee57, 0x01ee64,
+ 0x01ee67, 0x01ee9b,
+ 0x01eea1, 0x01eebb,
+ 0x01eef0, 0x01eef1,
0x01f000, 0x01f02b,
0x01f030, 0x01f093,
0x01f0a0, 0x01f0ae,
0x01f0b1, 0x01f0be,
0x01f0c1, 0x01f0df,
0x01f100, 0x01f10a,
- 0x01f110, 0x01f169,
+ 0x01f110, 0x01f16b,
0x01f170, 0x01f19a,
0x01f1e6, 0x01f202,
0x01f210, 0x01f23a,
@@ -461,10 +481,9 @@ var isPrint32 = []uint32{
0x01f3e0, 0x01f3f0,
0x01f400, 0x01f4fc,
0x01f500, 0x01f53d,
+ 0x01f540, 0x01f543,
0x01f550, 0x01f567,
- 0x01f5fb, 0x01f625,
- 0x01f628, 0x01f62d,
- 0x01f630, 0x01f640,
+ 0x01f5fb, 0x01f640,
0x01f645, 0x01f64f,
0x01f680, 0x01f6c5,
0x01f700, 0x01f773,
@@ -489,6 +508,7 @@ var isNotPrint32 = []uint16{ // add 0x10000 to each entry
0x0a14,
0x0a18,
0x10bd,
+ 0x1135,
0xd455,
0xd49d,
0xd4ad,
@@ -502,6 +522,32 @@ var isNotPrint32 = []uint16{ // add 0x10000 to each entry
0xd53f,
0xd545,
0xd551,
+ 0xee04,
+ 0xee20,
+ 0xee23,
+ 0xee28,
+ 0xee33,
+ 0xee38,
+ 0xee3a,
+ 0xee48,
+ 0xee4a,
+ 0xee4c,
+ 0xee50,
+ 0xee53,
+ 0xee58,
+ 0xee5a,
+ 0xee5c,
+ 0xee5e,
+ 0xee60,
+ 0xee63,
+ 0xee6b,
+ 0xee73,
+ 0xee78,
+ 0xee7d,
+ 0xee7f,
+ 0xee8a,
+ 0xeea4,
+ 0xeeaa,
0xf0d0,
0xf12f,
0xf336,
@@ -509,13 +555,4 @@ var isNotPrint32 = []uint16{ // add 0x10000 to each entry
0xf43f,
0xf441,
0xf4f8,
- 0xf600,
- 0xf611,
- 0xf615,
- 0xf617,
- 0xf619,
- 0xf61b,
- 0xf61f,
- 0xf62c,
- 0xf634,
}
diff --git a/src/pkg/strconv/itoa_test.go b/src/pkg/strconv/itoa_test.go
index 1486ee214..e0213ae9a 100644
--- a/src/pkg/strconv/itoa_test.go
+++ b/src/pkg/strconv/itoa_test.go
@@ -5,7 +5,6 @@
package strconv_test
import (
- "runtime"
. "strconv"
"testing"
)
@@ -126,35 +125,6 @@ func TestUitoa(t *testing.T) {
}
}
-func numAllocations(f func()) int {
- runtime.GC()
- memstats := new(runtime.MemStats)
- runtime.ReadMemStats(memstats)
- n0 := memstats.Mallocs
- f()
- runtime.ReadMemStats(memstats)
- return int(memstats.Mallocs - n0)
-}
-
-var globalBuf [64]byte
-
-func TestAppendUintDoesntAllocate(t *testing.T) {
- n := numAllocations(func() {
- var buf [64]byte
- AppendInt(buf[:0], 123, 10)
- })
- want := 1 // TODO(bradfitz): this might be 0, once escape analysis is better
- if n != want {
- t.Errorf("with local buffer, did %d allocations, want %d", n, want)
- }
- n = numAllocations(func() {
- AppendInt(globalBuf[:0], 123, 10)
- })
- if n != 0 {
- t.Errorf("with reused buffer, did %d allocations, want 0", n)
- }
-}
-
func BenchmarkFormatInt(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, test := range itob64tests {
diff --git a/src/pkg/strconv/quote.go b/src/pkg/strconv/quote.go
index 8a73f9d3b..8cbef88b5 100644
--- a/src/pkg/strconv/quote.go
+++ b/src/pkg/strconv/quote.go
@@ -139,8 +139,9 @@ func AppendQuoteRuneToASCII(dst []byte, r rune) []byte {
return append(dst, QuoteRuneToASCII(r)...)
}
-// CanBackquote returns whether the string s would be
-// a valid Go string literal if enclosed in backquotes.
+// CanBackquote reports whether the string s can be represented
+// unchanged as a single-line backquoted string without control
+// characters other than space and tab.
func CanBackquote(s string) bool {
for i := 0; i < len(s); i++ {
if (s[i] < ' ' && s[i] != '\t') || s[i] == '`' {
diff --git a/src/pkg/strconv/strconv_test.go b/src/pkg/strconv/strconv_test.go
new file mode 100644
index 000000000..c3c538926
--- /dev/null
+++ b/src/pkg/strconv/strconv_test.go
@@ -0,0 +1,52 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package strconv_test
+
+import (
+ . "strconv"
+ "strings"
+ "testing"
+)
+
+var (
+ globalBuf [64]byte
+ nextToOne = "1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1"
+
+ mallocTest = []struct {
+ count int
+ desc string
+ fn func()
+ }{
+ // TODO(bradfitz): this might be 0, once escape analysis is better
+ {1, `AppendInt(localBuf[:0], 123, 10)`, func() {
+ var localBuf [64]byte
+ AppendInt(localBuf[:0], 123, 10)
+ }},
+ {0, `AppendInt(globalBuf[:0], 123, 10)`, func() { AppendInt(globalBuf[:0], 123, 10) }},
+ // TODO(bradfitz): this might be 0, once escape analysis is better
+ {1, `AppendFloat(localBuf[:0], 1.23, 'g', 5, 64)`, func() {
+ var localBuf [64]byte
+ AppendFloat(localBuf[:0], 1.23, 'g', 5, 64)
+ }},
+ {0, `AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64)`, func() { AppendFloat(globalBuf[:0], 1.23, 'g', 5, 64) }},
+ {0, `ParseFloat("123.45", 64)`, func() { ParseFloat("123.45", 64) }},
+ {0, `ParseFloat("123.456789123456789", 64)`, func() { ParseFloat("123.456789123456789", 64) }},
+ {0, `ParseFloat("1.000000000000000111022302462515654042363166809082031251", 64)`, func() {
+ ParseFloat("1.000000000000000111022302462515654042363166809082031251", 64)
+ }},
+ {0, `ParseFloat("1.0000000000000001110223024625156540423631668090820312500...001", 64)`, func() {
+ ParseFloat(nextToOne, 64)
+ }},
+ }
+)
+
+func TestCountMallocs(t *testing.T) {
+ for _, mt := range mallocTest {
+ allocs := testing.AllocsPerRun(100, mt.fn)
+ if max := float64(mt.count); allocs > max {
+ t.Errorf("%s: %v allocs, want <=%v", mt.desc, allocs, max)
+ }
+ }
+}
diff --git a/src/pkg/strconv/testfp.txt b/src/pkg/strconv/testdata/testfp.txt
index 08d3c4ef0..08d3c4ef0 100644
--- a/src/pkg/strconv/testfp.txt
+++ b/src/pkg/strconv/testdata/testfp.txt
diff --git a/src/pkg/strings/example_test.go b/src/pkg/strings/example_test.go
index 733caf5f2..36e0a42fb 100644
--- a/src/pkg/strings/example_test.go
+++ b/src/pkg/strings/example_test.go
@@ -179,3 +179,19 @@ func ExampleToLower() {
fmt.Println(strings.ToLower("Gopher"))
// Output: gopher
}
+
+func ExampleTrimSuffix() {
+ var s = "Hello, goodbye, etc!"
+ s = strings.TrimSuffix(s, "goodbye, etc!")
+ s = strings.TrimSuffix(s, "planet")
+ fmt.Print(s, "world!")
+ // Output: Hello, world!
+}
+
+func ExampleTrimPrefix() {
+ var s = "Goodbye,, world!"
+ s = strings.TrimPrefix(s, "Goodbye,")
+ s = strings.TrimPrefix(s, "Howdy,")
+ fmt.Print("Hello" + s)
+ // Output: Hello, world!
+}
diff --git a/src/pkg/strings/export_test.go b/src/pkg/strings/export_test.go
index dcfec513c..17c806aa5 100644
--- a/src/pkg/strings/export_test.go
+++ b/src/pkg/strings/export_test.go
@@ -7,3 +7,39 @@ package strings
func (r *Replacer) Replacer() interface{} {
return r.r
}
+
+func (r *Replacer) PrintTrie() string {
+ gen := r.r.(*genericReplacer)
+ return gen.printNode(&gen.root, 0)
+}
+
+func (r *genericReplacer) printNode(t *trieNode, depth int) (s string) {
+ if t.priority > 0 {
+ s += "+"
+ } else {
+ s += "-"
+ }
+ s += "\n"
+
+ if t.prefix != "" {
+ s += Repeat(".", depth) + t.prefix
+ s += r.printNode(t.next, depth+len(t.prefix))
+ } else if t.table != nil {
+ for b, m := range r.mapping {
+ if int(m) != r.tableSize && t.table[m] != nil {
+ s += Repeat(".", depth) + string([]byte{byte(b)})
+ s += r.printNode(t.table[m], depth+1)
+ }
+ }
+ }
+ return
+}
+
+func StringFind(pattern, text string) int {
+ return makeStringFinder(pattern).next(text)
+}
+
+func DumpTables(pattern string) ([]int, []int) {
+ finder := makeStringFinder(pattern)
+ return finder.badCharSkip[:], finder.goodSuffixSkip
+}
diff --git a/src/pkg/strings/reader.go b/src/pkg/strings/reader.go
index 856980555..11240efc0 100644
--- a/src/pkg/strings/reader.go
+++ b/src/pkg/strings/reader.go
@@ -10,7 +10,7 @@ import (
"unicode/utf8"
)
-// A Reader implements the io.Reader, io.ReaderAt, io.Seeker,
+// A Reader implements the io.Reader, io.ReaderAt, io.Seeker, io.WriterTo,
// io.ByteScanner, and io.RuneScanner interfaces by reading
// from a string.
type Reader struct {
@@ -120,6 +120,25 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {
return abs, nil
}
+// WriteTo implements the io.WriterTo interface.
+func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
+ r.prevRune = -1
+ if r.i >= len(r.s) {
+ return 0, nil
+ }
+ s := r.s[r.i:]
+ m, err := io.WriteString(w, s)
+ if m > len(s) {
+ panic("strings.Reader.WriteTo: invalid WriteString count")
+ }
+ r.i += m
+ n = int64(m)
+ if m != len(s) && err == nil {
+ err = io.ErrShortWrite
+ }
+ return
+}
+
// NewReader returns a new Reader reading from s.
// It is similar to bytes.NewBufferString but more efficient and read-only.
func NewReader(s string) *Reader { return &Reader{s, 0, -1} }
diff --git a/src/pkg/strings/reader_test.go b/src/pkg/strings/reader_test.go
index a99ae2a0e..4fdddcdb5 100644
--- a/src/pkg/strings/reader_test.go
+++ b/src/pkg/strings/reader_test.go
@@ -5,6 +5,7 @@
package strings_test
import (
+ "bytes"
"fmt"
"io"
"os"
@@ -86,3 +87,25 @@ func TestReaderAt(t *testing.T) {
}
}
}
+
+func TestWriteTo(t *testing.T) {
+ const str = "0123456789"
+ for i := 0; i <= len(str); i++ {
+ s := str[i:]
+ r := strings.NewReader(s)
+ var b bytes.Buffer
+ n, err := r.WriteTo(&b)
+ if expect := int64(len(s)); n != expect {
+ t.Errorf("got %v; want %v", n, expect)
+ }
+ if err != nil {
+ t.Errorf("for length %d: got error = %v; want nil", len(s), err)
+ }
+ if b.String() != s {
+ t.Errorf("got string %q; want %q", b.String(), s)
+ }
+ if r.Len() != 0 {
+ t.Errorf("reader contains %v bytes; want 0", r.Len())
+ }
+ }
+}
diff --git a/src/pkg/strings/replace.go b/src/pkg/strings/replace.go
index f53a96ee0..f63b1792c 100644
--- a/src/pkg/strings/replace.go
+++ b/src/pkg/strings/replace.go
@@ -33,47 +33,45 @@ func NewReplacer(oldnew ...string) *Replacer {
panic("strings.NewReplacer: odd argument count")
}
- // Possible implementations.
- var (
- bb byteReplacer
- bs byteStringReplacer
- gen genericReplacer
- )
-
- allOldBytes, allNewBytes := true, true
- for len(oldnew) > 0 {
- old, new := oldnew[0], oldnew[1]
- oldnew = oldnew[2:]
- if len(old) != 1 {
- allOldBytes = false
+ if len(oldnew) == 2 && len(oldnew[0]) > 1 {
+ return &Replacer{r: makeSingleStringReplacer(oldnew[0], oldnew[1])}
+ }
+
+ allNewBytes := true
+ for i := 0; i < len(oldnew); i += 2 {
+ if len(oldnew[i]) != 1 {
+ return &Replacer{r: makeGenericReplacer(oldnew)}
}
- if len(new) != 1 {
+ if len(oldnew[i+1]) != 1 {
allNewBytes = false
}
+ }
- // generic
- gen.p = append(gen.p, pair{old, new})
-
- // byte -> string
- if allOldBytes {
- bs.old.set(old[0])
- bs.new[old[0]] = []byte(new)
- }
-
- // byte -> byte
- if allOldBytes && allNewBytes {
- bb.old.set(old[0])
- bb.new[old[0]] = new[0]
+ if allNewBytes {
+ bb := &byteReplacer{}
+ for i := 0; i < len(oldnew); i += 2 {
+ o, n := oldnew[i][0], oldnew[i+1][0]
+ if bb.old[o>>5]&uint32(1<<(o&31)) != 0 {
+ // Later old->new maps do not override previous ones with the same old string.
+ continue
+ }
+ bb.old.set(o)
+ bb.new[o] = n
}
+ return &Replacer{r: bb}
}
- if allOldBytes && allNewBytes {
- return &Replacer{r: &bb}
- }
- if allOldBytes {
- return &Replacer{r: &bs}
+ bs := &byteStringReplacer{}
+ for i := 0; i < len(oldnew); i += 2 {
+ o, new := oldnew[i][0], oldnew[i+1]
+ if bs.old[o>>5]&uint32(1<<(o&31)) != 0 {
+ // Later old->new maps do not override previous ones with the same old string.
+ continue
+ }
+ bs.old.set(o)
+ bs.new[o] = []byte(new)
}
- return &Replacer{r: &gen}
+ return &Replacer{r: bs}
}
// Replace returns a copy of s with all replacements performed.
@@ -86,79 +84,326 @@ func (r *Replacer) WriteString(w io.Writer, s string) (n int, err error) {
return r.r.WriteString(w, s)
}
-// genericReplacer is the fully generic (and least optimized) algorithm.
+// trieNode is a node in a lookup trie for prioritized key/value pairs. Keys
+// and values may be empty. For example, the trie containing keys "ax", "ay",
+// "bcbc", "x" and "xy" could have eight nodes:
+//
+// n0 -
+// n1 a-
+// n2 .x+
+// n3 .y+
+// n4 b-
+// n5 .cbc+
+// n6 x+
+// n7 .y+
+//
+// n0 is the root node, and its children are n1, n4 and n6; n1's children are
+// n2 and n3; n4's child is n5; n6's child is n7. Nodes n0, n1 and n4 (marked
+// with a trailing "-") are partial keys, and nodes n2, n3, n5, n6 and n7
+// (marked with a trailing "+") are complete keys.
+type trieNode struct {
+ // value is the value of the trie node's key/value pair. It is empty if
+ // this node is not a complete key.
+ value string
+ // priority is the priority (higher is more important) of the trie node's
+ // key/value pair; keys are not necessarily matched shortest- or longest-
+ // first. Priority is positive if this node is a complete key, and zero
+ // otherwise. In the example above, positive/zero priorities are marked
+ // with a trailing "+" or "-".
+ priority int
+
+ // A trie node may have zero, one or more child nodes:
+ // * if the remaining fields are zero, there are no children.
+ // * if prefix and next are non-zero, there is one child in next.
+ // * if table is non-zero, it defines all the children.
+ //
+ // Prefixes are preferred over tables when there is one child, but the
+ // root node always uses a table for lookup efficiency.
+
+ // prefix is the difference in keys between this trie node and the next.
+ // In the example above, node n4 has prefix "cbc" and n4's next node is n5.
+ // Node n5 has no children and so has zero prefix, next and table fields.
+ prefix string
+ next *trieNode
+
+ // table is a lookup table indexed by the next byte in the key, after
+ // remapping that byte through genericReplacer.mapping to create a dense
+ // index. In the example above, the keys only use 'a', 'b', 'c', 'x' and
+ // 'y', which remap to 0, 1, 2, 3 and 4. All other bytes remap to 5, and
+ // genericReplacer.tableSize will be 5. Node n0's table will be
+ // []*trieNode{ 0:n1, 1:n4, 3:n6 }, where the 0, 1 and 3 are the remapped
+ // 'a', 'b' and 'x'.
+ table []*trieNode
+}
+
+func (t *trieNode) add(key, val string, priority int, r *genericReplacer) {
+ if key == "" {
+ if t.priority == 0 {
+ t.value = val
+ t.priority = priority
+ }
+ return
+ }
+
+ if t.prefix != "" {
+ // Need to split the prefix among multiple nodes.
+ var n int // length of the longest common prefix
+ for ; n < len(t.prefix) && n < len(key); n++ {
+ if t.prefix[n] != key[n] {
+ break
+ }
+ }
+ if n == len(t.prefix) {
+ t.next.add(key[n:], val, priority, r)
+ } else if n == 0 {
+ // First byte differs, start a new lookup table here. Looking up
+ // what is currently t.prefix[0] will lead to prefixNode, and
+ // looking up key[0] will lead to keyNode.
+ var prefixNode *trieNode
+ if len(t.prefix) == 1 {
+ prefixNode = t.next
+ } else {
+ prefixNode = &trieNode{
+ prefix: t.prefix[1:],
+ next: t.next,
+ }
+ }
+ keyNode := new(trieNode)
+ t.table = make([]*trieNode, r.tableSize)
+ t.table[r.mapping[t.prefix[0]]] = prefixNode
+ t.table[r.mapping[key[0]]] = keyNode
+ t.prefix = ""
+ t.next = nil
+ keyNode.add(key[1:], val, priority, r)
+ } else {
+ // Insert new node after the common section of the prefix.
+ next := &trieNode{
+ prefix: t.prefix[n:],
+ next: t.next,
+ }
+ t.prefix = t.prefix[:n]
+ t.next = next
+ next.add(key[n:], val, priority, r)
+ }
+ } else if t.table != nil {
+ // Insert into existing table.
+ m := r.mapping[key[0]]
+ if t.table[m] == nil {
+ t.table[m] = new(trieNode)
+ }
+ t.table[m].add(key[1:], val, priority, r)
+ } else {
+ t.prefix = key
+ t.next = new(trieNode)
+ t.next.add("", val, priority, r)
+ }
+}
+
+func (r *genericReplacer) lookup(s string, ignoreRoot bool) (val string, keylen int, found bool) {
+ // Iterate down the trie to the end, and grab the value and keylen with
+ // the highest priority.
+ bestPriority := 0
+ node := &r.root
+ n := 0
+ for node != nil {
+ if node.priority > bestPriority && !(ignoreRoot && node == &r.root) {
+ bestPriority = node.priority
+ val = node.value
+ keylen = n
+ found = true
+ }
+
+ if s == "" {
+ break
+ }
+ if node.table != nil {
+ index := r.mapping[s[0]]
+ if int(index) == r.tableSize {
+ break
+ }
+ node = node.table[index]
+ s = s[1:]
+ n++
+ } else if node.prefix != "" && HasPrefix(s, node.prefix) {
+ n += len(node.prefix)
+ s = s[len(node.prefix):]
+ node = node.next
+ } else {
+ break
+ }
+ }
+ return
+}
+
+// genericReplacer is the fully generic algorithm.
// It's used as a fallback when nothing faster can be used.
type genericReplacer struct {
- p []pair
+ root trieNode
+ // tableSize is the size of a trie node's lookup table. It is the number
+ // of unique key bytes.
+ tableSize int
+ // mapping maps from key bytes to a dense index for trieNode.table.
+ mapping [256]byte
}
-type pair struct{ old, new string }
+func makeGenericReplacer(oldnew []string) *genericReplacer {
+ r := new(genericReplacer)
+ // Find each byte used, then assign them each an index.
+ for i := 0; i < len(oldnew); i += 2 {
+ key := oldnew[i]
+ for j := 0; j < len(key); j++ {
+ r.mapping[key[j]] = 1
+ }
+ }
+
+ for _, b := range r.mapping {
+ r.tableSize += int(b)
+ }
+
+ var index byte
+ for i, b := range r.mapping {
+ if b == 0 {
+ r.mapping[i] = byte(r.tableSize)
+ } else {
+ r.mapping[i] = index
+ index++
+ }
+ }
+ // Ensure root node uses a lookup table (for performance).
+ r.root.table = make([]*trieNode, r.tableSize)
-type appendSliceWriter struct {
- b []byte
+ for i := 0; i < len(oldnew); i += 2 {
+ r.root.add(oldnew[i], oldnew[i+1], len(oldnew)-i, r)
+ }
+ return r
}
+type appendSliceWriter []byte
+
+// Write writes to the buffer to satisfy io.Writer.
func (w *appendSliceWriter) Write(p []byte) (int, error) {
- w.b = append(w.b, p...)
+ *w = append(*w, p...)
return len(p), nil
}
+// WriteString writes to the buffer without string->[]byte->string allocations.
+func (w *appendSliceWriter) WriteString(s string) (int, error) {
+ *w = append(*w, s...)
+ return len(s), nil
+}
+
+type stringWriterIface interface {
+ WriteString(string) (int, error)
+}
+
+type stringWriter struct {
+ w io.Writer
+}
+
+func (w stringWriter) WriteString(s string) (int, error) {
+ return w.w.Write([]byte(s))
+}
+
+func getStringWriter(w io.Writer) stringWriterIface {
+ sw, ok := w.(stringWriterIface)
+ if !ok {
+ sw = stringWriter{w}
+ }
+ return sw
+}
+
func (r *genericReplacer) Replace(s string) string {
- // TODO(bradfitz): optimized version
- n, _ := r.WriteString(discard, s)
- w := appendSliceWriter{make([]byte, 0, n)}
- r.WriteString(&w, s)
- return string(w.b)
+ buf := make(appendSliceWriter, 0, len(s))
+ r.WriteString(&buf, s)
+ return string(buf)
}
func (r *genericReplacer) WriteString(w io.Writer, s string) (n int, err error) {
- lastEmpty := false // the last replacement was of the empty string
-Input:
- // TODO(bradfitz): optimized version
- for i := 0; i < len(s); {
- for _, p := range r.p {
- if p.old == "" && lastEmpty {
- // Don't let old match twice in a row.
- // (it doesn't advance the input and
- // would otherwise loop forever)
- continue
+ sw := getStringWriter(w)
+ var last, wn int
+ var prevMatchEmpty bool
+ for i := 0; i <= len(s); {
+ // Ignore the empty match iff the previous loop found the empty match.
+ val, keylen, match := r.lookup(s[i:], prevMatchEmpty)
+ prevMatchEmpty = match && keylen == 0
+ if match {
+ wn, err = sw.WriteString(s[last:i])
+ n += wn
+ if err != nil {
+ return
}
- if HasPrefix(s[i:], p.old) {
- if p.new != "" {
- wn, err := w.Write([]byte(p.new))
- n += wn
- if err != nil {
- return n, err
- }
- }
- i += len(p.old)
- lastEmpty = p.old == ""
- continue Input
+ wn, err = sw.WriteString(val)
+ n += wn
+ if err != nil {
+ return
}
- }
- wn, err := w.Write([]byte{s[i]})
- n += wn
- if err != nil {
- return n, err
+ i += keylen
+ last = i
+ continue
}
i++
}
+ if last != len(s) {
+ wn, err = sw.WriteString(s[last:])
+ n += wn
+ }
+ return
+}
- // Final empty match at end.
- for _, p := range r.p {
- if p.old == "" {
- if p.new != "" {
- wn, err := w.Write([]byte(p.new))
- n += wn
- if err != nil {
- return n, err
- }
- }
+// singleStringReplacer is the implementation that's used when there is only
+// one string to replace (and that string has more than one byte).
+type singleStringReplacer struct {
+ finder *stringFinder
+ // value is the new string that replaces that pattern when it's found.
+ value string
+}
+
+func makeSingleStringReplacer(pattern string, value string) *singleStringReplacer {
+ return &singleStringReplacer{finder: makeStringFinder(pattern), value: value}
+}
+
+func (r *singleStringReplacer) Replace(s string) string {
+ var buf []byte
+ i := 0
+ for {
+ match := r.finder.next(s[i:])
+ if match == -1 {
break
}
+ buf = append(buf, s[i:i+match]...)
+ buf = append(buf, r.value...)
+ i += match + len(r.finder.pattern)
+ }
+ if buf == nil {
+ return s
}
+ buf = append(buf, s[i:]...)
+ return string(buf)
+}
- return n, nil
+func (r *singleStringReplacer) WriteString(w io.Writer, s string) (n int, err error) {
+ sw := getStringWriter(w)
+ var i, wn int
+ for {
+ match := r.finder.next(s[i:])
+ if match == -1 {
+ break
+ }
+ wn, err = sw.WriteString(s[i : i+match])
+ n += wn
+ if err != nil {
+ return
+ }
+ wn, err = sw.WriteString(r.value)
+ n += wn
+ if err != nil {
+ return
+ }
+ i += match + len(r.finder.pattern)
+ }
+ wn, err = sw.WriteString(s[i:])
+ n += wn
+ return
}
// byteReplacer is the implementation that's used when all the "old"
@@ -301,12 +546,3 @@ func (r *byteStringReplacer) WriteString(w io.Writer, s string) (n int, err erro
}
return n, nil
}
-
-// strings is too low-level to import io/ioutil
-var discard io.Writer = devNull(0)
-
-type devNull int
-
-func (devNull) Write(p []byte) (int, error) {
- return len(p), nil
-}
diff --git a/src/pkg/strings/replace_test.go b/src/pkg/strings/replace_test.go
index 23c7e2e53..d33dea95b 100644
--- a/src/pkg/strings/replace_test.go
+++ b/src/pkg/strings/replace_test.go
@@ -7,105 +7,390 @@ package strings_test
import (
"bytes"
"fmt"
- "log"
. "strings"
"testing"
)
-var _ = log.Printf
-
-type ReplacerTest struct {
- r *Replacer
- in string
- out string
-}
+var htmlEscaper = NewReplacer(
+ "&", "&amp;",
+ "<", "&lt;",
+ ">", "&gt;",
+ `"`, "&quot;",
+ "'", "&apos;",
+)
-var htmlEscaper = NewReplacer("&", "&amp;", "<", "&lt;", ">", "&gt;", "\"", "&quot;")
+var htmlUnescaper = NewReplacer(
+ "&amp;", "&",
+ "&lt;", "<",
+ "&gt;", ">",
+ "&quot;", `"`,
+ "&apos;", "'",
+)
// The http package's old HTML escaping function.
-func oldhtmlEscape(s string) string {
+func oldHTMLEscape(s string) string {
s = Replace(s, "&", "&amp;", -1)
s = Replace(s, "<", "&lt;", -1)
s = Replace(s, ">", "&gt;", -1)
- s = Replace(s, "\"", "&quot;", -1)
+ s = Replace(s, `"`, "&quot;", -1)
s = Replace(s, "'", "&apos;", -1)
return s
}
-var replacer = NewReplacer("aaa", "3[aaa]", "aa", "2[aa]", "a", "1[a]", "i", "i",
- "longerst", "most long", "longer", "medium", "long", "short",
- "X", "Y", "Y", "Z")
-
var capitalLetters = NewReplacer("a", "A", "b", "B")
-var blankToXReplacer = NewReplacer("", "X", "o", "O")
+// TestReplacer tests the replacer implementations.
+func TestReplacer(t *testing.T) {
+ type testCase struct {
+ r *Replacer
+ in, out string
+ }
+ var testCases []testCase
-var ReplacerTests = []ReplacerTest{
- // byte->string
- {htmlEscaper, "No changes", "No changes"},
- {htmlEscaper, "I <3 escaping & stuff", "I &lt;3 escaping &amp; stuff"},
- {htmlEscaper, "&&&", "&amp;&amp;&amp;"},
+ // str converts 0xff to "\xff". This isn't just string(b) since that converts to UTF-8.
+ str := func(b byte) string {
+ return string([]byte{b})
+ }
+ var s []string
- // generic
- {replacer, "fooaaabar", "foo3[aaa]b1[a]r"},
- {replacer, "long, longerst, longer", "short, most long, medium"},
- {replacer, "XiX", "YiY"},
+ // inc maps "\x00"->"\x01", ..., "a"->"b", "b"->"c", ..., "\xff"->"\x00".
+ s = nil
+ for i := 0; i < 256; i++ {
+ s = append(s, str(byte(i)), str(byte(i+1)))
+ }
+ inc := NewReplacer(s...)
- // byte->byte
- {capitalLetters, "brad", "BrAd"},
- {capitalLetters, Repeat("a", (32<<10)+123), Repeat("A", (32<<10)+123)},
+ // Test cases with 1-byte old strings, 1-byte new strings.
+ testCases = append(testCases,
+ testCase{capitalLetters, "brad", "BrAd"},
+ testCase{capitalLetters, Repeat("a", (32<<10)+123), Repeat("A", (32<<10)+123)},
+ testCase{capitalLetters, "", ""},
- // hitting "" special case
- {blankToXReplacer, "oo", "XOXOX"},
-}
+ testCase{inc, "brad", "csbe"},
+ testCase{inc, "\x00\xff", "\x01\x00"},
+ testCase{inc, "", ""},
-func TestReplacer(t *testing.T) {
- for i, tt := range ReplacerTests {
- if s := tt.r.Replace(tt.in); s != tt.out {
- t.Errorf("%d. Replace(%q) = %q, want %q", i, tt.in, s, tt.out)
+ testCase{NewReplacer("a", "1", "a", "2"), "brad", "br1d"},
+ )
+
+ // repeat maps "a"->"a", "b"->"bb", "c"->"ccc", ...
+ s = nil
+ for i := 0; i < 256; i++ {
+ n := i + 1 - 'a'
+ if n < 1 {
+ n = 1
+ }
+ s = append(s, str(byte(i)), Repeat(str(byte(i)), n))
+ }
+ repeat := NewReplacer(s...)
+
+ // Test cases with 1-byte old strings, variable length new strings.
+ testCases = append(testCases,
+ testCase{htmlEscaper, "No changes", "No changes"},
+ testCase{htmlEscaper, "I <3 escaping & stuff", "I &lt;3 escaping &amp; stuff"},
+ testCase{htmlEscaper, "&&&", "&amp;&amp;&amp;"},
+ testCase{htmlEscaper, "", ""},
+
+ testCase{repeat, "brad", "bbrrrrrrrrrrrrrrrrrradddd"},
+ testCase{repeat, "abba", "abbbba"},
+ testCase{repeat, "", ""},
+
+ testCase{NewReplacer("a", "11", "a", "22"), "brad", "br11d"},
+ )
+
+ // The remaining test cases have variable length old strings.
+
+ testCases = append(testCases,
+ testCase{htmlUnescaper, "&amp;amp;", "&amp;"},
+ testCase{htmlUnescaper, "&lt;b&gt;HTML&apos;s neat&lt;/b&gt;", "<b>HTML's neat</b>"},
+ testCase{htmlUnescaper, "", ""},
+
+ testCase{NewReplacer("a", "1", "a", "2", "xxx", "xxx"), "brad", "br1d"},
+
+ testCase{NewReplacer("a", "1", "aa", "2", "aaa", "3"), "aaaa", "1111"},
+
+ testCase{NewReplacer("aaa", "3", "aa", "2", "a", "1"), "aaaa", "31"},
+ )
+
+ // gen1 has multiple old strings of variable length. There is no
+ // overall non-empty common prefix, but some pairwise common prefixes.
+ gen1 := NewReplacer(
+ "aaa", "3[aaa]",
+ "aa", "2[aa]",
+ "a", "1[a]",
+ "i", "i",
+ "longerst", "most long",
+ "longer", "medium",
+ "long", "short",
+ "xx", "xx",
+ "x", "X",
+ "X", "Y",
+ "Y", "Z",
+ )
+ testCases = append(testCases,
+ testCase{gen1, "fooaaabar", "foo3[aaa]b1[a]r"},
+ testCase{gen1, "long, longerst, longer", "short, most long, medium"},
+ testCase{gen1, "xxxxx", "xxxxX"},
+ testCase{gen1, "XiX", "YiY"},
+ testCase{gen1, "", ""},
+ )
+
+ // gen2 has multiple old strings with no pairwise common prefix.
+ gen2 := NewReplacer(
+ "roses", "red",
+ "violets", "blue",
+ "sugar", "sweet",
+ )
+ testCases = append(testCases,
+ testCase{gen2, "roses are red, violets are blue...", "red are red, blue are blue..."},
+ testCase{gen2, "", ""},
+ )
+
+ // gen3 has multiple old strings with an overall common prefix.
+ gen3 := NewReplacer(
+ "abracadabra", "poof",
+ "abracadabrakazam", "splat",
+ "abraham", "lincoln",
+ "abrasion", "scrape",
+ "abraham", "isaac",
+ )
+ testCases = append(testCases,
+ testCase{gen3, "abracadabrakazam abraham", "poofkazam lincoln"},
+ testCase{gen3, "abrasion abracad", "scrape abracad"},
+ testCase{gen3, "abba abram abrasive", "abba abram abrasive"},
+ testCase{gen3, "", ""},
+ )
+
+ // foo{1,2,3,4} have multiple old strings with an overall common prefix
+ // and 1- or 2- byte extensions from the common prefix.
+ foo1 := NewReplacer(
+ "foo1", "A",
+ "foo2", "B",
+ "foo3", "C",
+ )
+ foo2 := NewReplacer(
+ "foo1", "A",
+ "foo2", "B",
+ "foo31", "C",
+ "foo32", "D",
+ )
+ foo3 := NewReplacer(
+ "foo11", "A",
+ "foo12", "B",
+ "foo31", "C",
+ "foo32", "D",
+ )
+ foo4 := NewReplacer(
+ "foo12", "B",
+ "foo32", "D",
+ )
+ testCases = append(testCases,
+ testCase{foo1, "fofoofoo12foo32oo", "fofooA2C2oo"},
+ testCase{foo1, "", ""},
+
+ testCase{foo2, "fofoofoo12foo32oo", "fofooA2Doo"},
+ testCase{foo2, "", ""},
+
+ testCase{foo3, "fofoofoo12foo32oo", "fofooBDoo"},
+ testCase{foo3, "", ""},
+
+ testCase{foo4, "fofoofoo12foo32oo", "fofooBDoo"},
+ testCase{foo4, "", ""},
+ )
+
+ // genAll maps "\x00\x01\x02...\xfe\xff" to "[all]", amongst other things.
+ allBytes := make([]byte, 256)
+ for i := range allBytes {
+ allBytes[i] = byte(i)
+ }
+ allString := string(allBytes)
+ genAll := NewReplacer(
+ allString, "[all]",
+ "\xff", "[ff]",
+ "\x00", "[00]",
+ )
+ testCases = append(testCases,
+ testCase{genAll, allString, "[all]"},
+ testCase{genAll, "a\xff" + allString + "\x00", "a[ff][all][00]"},
+ testCase{genAll, "", ""},
+ )
+
+ // Test cases with empty old strings.
+
+ blankToX1 := NewReplacer("", "X")
+ blankToX2 := NewReplacer("", "X", "", "")
+ blankHighPriority := NewReplacer("", "X", "o", "O")
+ blankLowPriority := NewReplacer("o", "O", "", "X")
+ blankNoOp1 := NewReplacer("", "")
+ blankNoOp2 := NewReplacer("", "", "", "A")
+ blankFoo := NewReplacer("", "X", "foobar", "R", "foobaz", "Z")
+ testCases = append(testCases,
+ testCase{blankToX1, "foo", "XfXoXoX"},
+ testCase{blankToX1, "", "X"},
+
+ testCase{blankToX2, "foo", "XfXoXoX"},
+ testCase{blankToX2, "", "X"},
+
+ testCase{blankHighPriority, "oo", "XOXOX"},
+ testCase{blankHighPriority, "ii", "XiXiX"},
+ testCase{blankHighPriority, "oiio", "XOXiXiXOX"},
+ testCase{blankHighPriority, "iooi", "XiXOXOXiX"},
+ testCase{blankHighPriority, "", "X"},
+
+ testCase{blankLowPriority, "oo", "OOX"},
+ testCase{blankLowPriority, "ii", "XiXiX"},
+ testCase{blankLowPriority, "oiio", "OXiXiOX"},
+ testCase{blankLowPriority, "iooi", "XiOOXiX"},
+ testCase{blankLowPriority, "", "X"},
+
+ testCase{blankNoOp1, "foo", "foo"},
+ testCase{blankNoOp1, "", ""},
+
+ testCase{blankNoOp2, "foo", "foo"},
+ testCase{blankNoOp2, "", ""},
+
+ testCase{blankFoo, "foobarfoobaz", "XRXZX"},
+ testCase{blankFoo, "foobar-foobaz", "XRX-XZX"},
+ testCase{blankFoo, "", "X"},
+ )
+
+ // single string replacer
+
+ abcMatcher := NewReplacer("abc", "[match]")
+
+ testCases = append(testCases,
+ testCase{abcMatcher, "", ""},
+ testCase{abcMatcher, "ab", "ab"},
+ testCase{abcMatcher, "abcd", "[match]d"},
+ testCase{abcMatcher, "cabcabcdabca", "c[match][match]d[match]a"},
+ )
+
+ // No-arg test cases.
+
+ nop := NewReplacer()
+ testCases = append(testCases,
+ testCase{nop, "abc", "abc"},
+ testCase{nop, "", ""},
+ )
+
+ // Run the test cases.
+
+ for i, tc := range testCases {
+ if s := tc.r.Replace(tc.in); s != tc.out {
+ t.Errorf("%d. Replace(%q) = %q, want %q", i, tc.in, s, tc.out)
}
var buf bytes.Buffer
- n, err := tt.r.WriteString(&buf, tt.in)
+ n, err := tc.r.WriteString(&buf, tc.in)
if err != nil {
t.Errorf("%d. WriteString: %v", i, err)
continue
}
got := buf.String()
- if got != tt.out {
- t.Errorf("%d. WriteString(%q) wrote %q, want %q", i, tt.in, got, tt.out)
+ if got != tc.out {
+ t.Errorf("%d. WriteString(%q) wrote %q, want %q", i, tc.in, got, tc.out)
continue
}
- if n != len(tt.out) {
+ if n != len(tc.out) {
t.Errorf("%d. WriteString(%q) wrote correct string but reported %d bytes; want %d (%q)",
- i, tt.in, n, len(tt.out), tt.out)
+ i, tc.in, n, len(tc.out), tc.out)
}
}
}
-// pickAlgorithmTest is a test that verifies that given input for a
-// Replacer that we pick the correct algorithm.
-type pickAlgorithmTest struct {
- r *Replacer
- want string // name of algorithm
+// TestPickAlgorithm tests that NewReplacer picks the correct algorithm.
+func TestPickAlgorithm(t *testing.T) {
+ testCases := []struct {
+ r *Replacer
+ want string
+ }{
+ {capitalLetters, "*strings.byteReplacer"},
+ {htmlEscaper, "*strings.byteStringReplacer"},
+ {NewReplacer("12", "123"), "*strings.singleStringReplacer"},
+ {NewReplacer("1", "12"), "*strings.byteStringReplacer"},
+ {NewReplacer("", "X"), "*strings.genericReplacer"},
+ {NewReplacer("a", "1", "b", "12", "cde", "123"), "*strings.genericReplacer"},
+ }
+ for i, tc := range testCases {
+ got := fmt.Sprintf("%T", tc.r.Replacer())
+ if got != tc.want {
+ t.Errorf("%d. algorithm = %s, want %s", i, got, tc.want)
+ }
+ }
}
-var pickAlgorithmTests = []pickAlgorithmTest{
- {capitalLetters, "*strings.byteReplacer"},
- {NewReplacer("12", "123"), "*strings.genericReplacer"},
- {NewReplacer("1", "12"), "*strings.byteStringReplacer"},
- {htmlEscaper, "*strings.byteStringReplacer"},
-}
+// TestGenericTrieBuilding verifies the structure of the generated trie. There
+// is one node per line, and the key ending with the current line is in the
+// trie if it ends with a "+".
+func TestGenericTrieBuilding(t *testing.T) {
+ testCases := []struct{ in, out string }{
+ {"abc;abdef;abdefgh;xx;xy;z", `-
+ a-
+ .b-
+ ..c+
+ ..d-
+ ...ef+
+ .....gh+
+ x-
+ .x+
+ .y+
+ z+
+ `},
+ {"abracadabra;abracadabrakazam;abraham;abrasion", `-
+ a-
+ .bra-
+ ....c-
+ .....adabra+
+ ...........kazam+
+ ....h-
+ .....am+
+ ....s-
+ .....ion+
+ `},
+ {"aaa;aa;a;i;longerst;longer;long;xx;x;X;Y", `-
+ X+
+ Y+
+ a+
+ .a+
+ ..a+
+ i+
+ l-
+ .ong+
+ ....er+
+ ......st+
+ x+
+ .x+
+ `},
+ {"foo;;foo;foo1", `+
+ f-
+ .oo+
+ ...1+
+ `},
+ }
-func TestPickAlgorithm(t *testing.T) {
- for i, tt := range pickAlgorithmTests {
- got := fmt.Sprintf("%T", tt.r.Replacer())
- if got != tt.want {
- t.Errorf("%d. algorithm = %s, want %s", i, got, tt.want)
+ for _, tc := range testCases {
+ keys := Split(tc.in, ";")
+ args := make([]string, len(keys)*2)
+ for i, key := range keys {
+ args[i*2] = key
+ }
+
+ got := NewReplacer(args...).PrintTrie()
+ // Remove tabs from tc.out
+ wantbuf := make([]byte, 0, len(tc.out))
+ for i := 0; i < len(tc.out); i++ {
+ if tc.out[i] != '\t' {
+ wantbuf = append(wantbuf, tc.out[i])
+ }
+ }
+ want := string(wantbuf)
+
+ if got != want {
+ t.Errorf("PrintTrie(%q)\ngot\n%swant\n%s", tc.in, got, want)
}
}
}
-func BenchmarkGenericMatch(b *testing.B) {
+func BenchmarkGenericNoMatch(b *testing.B) {
str := Repeat("A", 100) + Repeat("B", 100)
generic := NewReplacer("a", "A", "b", "B", "12", "123") // varying lengths forces generic
for i := 0; i < b.N; i++ {
@@ -113,6 +398,42 @@ func BenchmarkGenericMatch(b *testing.B) {
}
}
+func BenchmarkGenericMatch1(b *testing.B) {
+ str := Repeat("a", 100) + Repeat("b", 100)
+ generic := NewReplacer("a", "A", "b", "B", "12", "123")
+ for i := 0; i < b.N; i++ {
+ generic.Replace(str)
+ }
+}
+
+func BenchmarkGenericMatch2(b *testing.B) {
+ str := Repeat("It&apos;s &lt;b&gt;HTML&lt;/b&gt;!", 100)
+ for i := 0; i < b.N; i++ {
+ htmlUnescaper.Replace(str)
+ }
+}
+
+func benchmarkSingleString(b *testing.B, pattern, text string) {
+ r := NewReplacer(pattern, "[match]")
+ b.SetBytes(int64(len(text)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ r.Replace(text)
+ }
+}
+
+func BenchmarkSingleMaxSkipping(b *testing.B) {
+ benchmarkSingleString(b, Repeat("b", 25), Repeat("a", 10000))
+}
+
+func BenchmarkSingleLongSuffixFail(b *testing.B) {
+ benchmarkSingleString(b, "b"+Repeat("a", 500), Repeat("a", 1002))
+}
+
+func BenchmarkSingleMatch(b *testing.B) {
+ benchmarkSingleString(b, "abcdef", Repeat("abcdefghijklmno", 1000))
+}
+
func BenchmarkByteByteNoMatch(b *testing.B) {
str := Repeat("A", 100) + Repeat("B", 100)
for i := 0; i < b.N; i++ {
@@ -144,7 +465,7 @@ func BenchmarkHTMLEscapeNew(b *testing.B) {
func BenchmarkHTMLEscapeOld(b *testing.B) {
str := "I <3 to escape HTML & other text too."
for i := 0; i < b.N; i++ {
- oldhtmlEscape(str)
+ oldHTMLEscape(str)
}
}
diff --git a/src/pkg/strings/search.go b/src/pkg/strings/search.go
new file mode 100644
index 000000000..f77c879c5
--- /dev/null
+++ b/src/pkg/strings/search.go
@@ -0,0 +1,124 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package strings
+
+// stringFinder efficiently finds strings in a source text. It's implemented
+// using the Boyer-Moore string search algorithm:
+// http://en.wikipedia.org/wiki/Boyer-Moore_string_search_algorithm
+// http://www.cs.utexas.edu/~moore/publications/fstrpos.pdf (note: this aged
+// document uses 1-based indexing)
+type stringFinder struct {
+ // pattern is the string that we are searching for in the text.
+ pattern string
+
+ // badCharSkip[b] contains the distance between the last byte of pattern
+ // and the rightmost occurrence of b in pattern. If b is not in pattern,
+ // badCharSkip[b] is len(pattern).
+ //
+ // Whenever a mismatch is found with byte b in the text, we can safely
+ // shift the matching frame at least badCharSkip[b] until the next time
+ // the matching char could be in alignment.
+ badCharSkip [256]int
+
+ // goodSuffixSkip[i] defines how far we can shift the matching frame given
+ // that the suffix pattern[i+1:] matches, but the byte pattern[i] does
+ // not. There are two cases to consider:
+ //
+ // 1. The matched suffix occurs elsewhere in pattern (with a different
+ // byte preceding it that we might possibly match). In this case, we can
+ // shift the matching frame to align with the next suffix chunk. For
+ // example, the pattern "mississi" has the suffix "issi" next occurring
+ // (in right-to-left order) at index 1, so goodSuffixSkip[3] ==
+ // shift+len(suffix) == 3+4 == 7.
+ //
+ // 2. If the matched suffix does not occur elsewhere in pattern, then the
+ // matching frame may share part of its prefix with the end of the
+ // matching suffix. In this case, goodSuffixSkip[i] will contain how far
+ // to shift the frame to align this portion of the prefix to the
+ // suffix. For example, in the pattern "abcxxxabc", when the first
+ // mismatch from the back is found to be in position 3, the matching
+ // suffix "xxabc" is not found elsewhere in the pattern. However, its
+ // rightmost "abc" (at position 6) is a prefix of the whole pattern, so
+ // goodSuffixSkip[3] == shift+len(suffix) == 6+5 == 11.
+ goodSuffixSkip []int
+}
+
+func makeStringFinder(pattern string) *stringFinder {
+ f := &stringFinder{
+ pattern: pattern,
+ goodSuffixSkip: make([]int, len(pattern)),
+ }
+ // last is the index of the last character in the pattern.
+ last := len(pattern) - 1
+
+ // Build bad character table.
+ // Bytes not in the pattern can skip one pattern's length.
+ for i := range f.badCharSkip {
+ f.badCharSkip[i] = len(pattern)
+ }
+ // The loop condition is < instead of <= so that the last byte does not
+ // have a zero distance to itself. Finding this byte out of place implies
+ // that it is not in the last position.
+ for i := 0; i < last; i++ {
+ f.badCharSkip[pattern[i]] = last - i
+ }
+
+ // Build good suffix table.
+ // First pass: set each value to the next index which starts a prefix of
+ // pattern.
+ lastPrefix := last
+ for i := last; i >= 0; i-- {
+ if HasPrefix(pattern, pattern[i+1:]) {
+ lastPrefix = i + 1
+ }
+ // lastPrefix is the shift, and (last-i) is len(suffix).
+ f.goodSuffixSkip[i] = lastPrefix + last - i
+ }
+ // Second pass: find repeats of pattern's suffix starting from the front.
+ for i := 0; i < last; i++ {
+ lenSuffix := longestCommonSuffix(pattern, pattern[1:i+1])
+ if pattern[i-lenSuffix] != pattern[last-lenSuffix] {
+ // (last-i) is the shift, and lenSuffix is len(suffix).
+ f.goodSuffixSkip[last-lenSuffix] = lenSuffix + last - i
+ }
+ }
+
+ return f
+}
+
+func longestCommonSuffix(a, b string) (i int) {
+ for ; i < len(a) && i < len(b); i++ {
+ if a[len(a)-1-i] != b[len(b)-1-i] {
+ break
+ }
+ }
+ return
+}
+
+// next returns the index in text of the first occurrence of the pattern. If
+// the pattern is not found, it returns -1.
+func (f *stringFinder) next(text string) int {
+ i := len(f.pattern) - 1
+ for i < len(text) {
+ // Compare backwards from the end until the first unmatching character.
+ j := len(f.pattern) - 1
+ for j >= 0 && text[i] == f.pattern[j] {
+ i--
+ j--
+ }
+ if j < 0 {
+ return i + 1 // match
+ }
+ i += max(f.badCharSkip[text[i]], f.goodSuffixSkip[j])
+ }
+ return -1
+}
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
diff --git a/src/pkg/strings/search_test.go b/src/pkg/strings/search_test.go
new file mode 100644
index 000000000..966c05e65
--- /dev/null
+++ b/src/pkg/strings/search_test.go
@@ -0,0 +1,90 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package strings_test
+
+import (
+ "reflect"
+ . "strings"
+ "testing"
+)
+
+func TestFinderNext(t *testing.T) {
+ testCases := []struct {
+ pat, text string
+ index int
+ }{
+ {"", "", 0},
+ {"", "abc", 0},
+ {"abc", "", -1},
+ {"abc", "abc", 0},
+ {"d", "abcdefg", 3},
+ {"nan", "banana", 2},
+ {"pan", "anpanman", 2},
+ {"nnaaman", "anpanmanam", -1},
+ {"abcd", "abc", -1},
+ {"abcd", "bcd", -1},
+ {"bcd", "abcd", 1},
+ {"abc", "acca", -1},
+ {"aa", "aaa", 0},
+ {"baa", "aaaaa", -1},
+ {"at that", "which finally halts. at that point", 22},
+ }
+
+ for _, tc := range testCases {
+ got := StringFind(tc.pat, tc.text)
+ want := tc.index
+ if got != want {
+ t.Errorf("stringFind(%q, %q) got %d, want %d\n", tc.pat, tc.text, got, want)
+ }
+ }
+}
+
+func TestFinderCreation(t *testing.T) {
+ testCases := []struct {
+ pattern string
+ bad [256]int
+ suf []int
+ }{
+ {
+ "abc",
+ [256]int{'a': 2, 'b': 1, 'c': 3},
+ []int{5, 4, 1},
+ },
+ {
+ "mississi",
+ [256]int{'i': 3, 'm': 7, 's': 1},
+ []int{15, 14, 13, 7, 11, 10, 7, 1},
+ },
+ // From http://www.cs.utexas.edu/~moore/publications/fstrpos.pdf
+ {
+ "abcxxxabc",
+ [256]int{'a': 2, 'b': 1, 'c': 6, 'x': 3},
+ []int{14, 13, 12, 11, 10, 9, 11, 10, 1},
+ },
+ {
+ "abyxcdeyx",
+ [256]int{'a': 8, 'b': 7, 'c': 4, 'd': 3, 'e': 2, 'y': 1, 'x': 5},
+ []int{17, 16, 15, 14, 13, 12, 7, 10, 1},
+ },
+ }
+
+ for _, tc := range testCases {
+ bad, good := DumpTables(tc.pattern)
+
+ for i, got := range bad {
+ want := tc.bad[i]
+ if want == 0 {
+ want = len(tc.pattern)
+ }
+ if got != want {
+ t.Errorf("boyerMoore(%q) bad['%c']: got %d want %d", tc.pattern, i, got, want)
+ }
+ }
+
+ if !reflect.DeepEqual(good, tc.suf) {
+ t.Errorf("boyerMoore(%q) got %v want %v", tc.pattern, good, tc.suf)
+ }
+ }
+}
diff --git a/src/pkg/strings/strings.go b/src/pkg/strings/strings.go
index b411ba5d8..ccf415e69 100644
--- a/src/pkg/strings/strings.go
+++ b/src/pkg/strings/strings.go
@@ -36,27 +36,69 @@ func explode(s string, n int) []string {
return a
}
+// primeRK is the prime base used in Rabin-Karp algorithm.
+const primeRK = 16777619
+
+// hashstr returns the hash and the appropriate multiplicative
+// factor for use in Rabin-Karp algorithm.
+func hashstr(sep string) (uint32, uint32) {
+ hash := uint32(0)
+ for i := 0; i < len(sep); i++ {
+ hash = hash*primeRK + uint32(sep[i])
+
+ }
+ var pow, sq uint32 = 1, primeRK
+ for i := len(sep); i > 0; i >>= 1 {
+ if i&1 != 0 {
+ pow *= sq
+ }
+ sq *= sq
+ }
+ return hash, pow
+}
+
// Count counts the number of non-overlapping instances of sep in s.
func Count(s, sep string) int {
- if sep == "" {
- return utf8.RuneCountInString(s) + 1
- }
- c := sep[0]
- l := len(sep)
n := 0
- if l == 1 {
+ // special cases
+ switch {
+ case len(sep) == 0:
+ return utf8.RuneCountInString(s) + 1
+ case len(sep) == 1:
// special case worth making fast
+ c := sep[0]
for i := 0; i < len(s); i++ {
if s[i] == c {
n++
}
}
return n
+ case len(sep) > len(s):
+ return 0
+ case len(sep) == len(s):
+ if sep == s {
+ return 1
+ }
+ return 0
}
- for i := 0; i+l <= len(s); i++ {
- if s[i] == c && s[i:i+l] == sep {
+ hashsep, pow := hashstr(sep)
+ h := uint32(0)
+ for i := 0; i < len(sep); i++ {
+ h = h*primeRK + uint32(s[i])
+ }
+ lastmatch := 0
+ if h == hashsep && s[:len(sep)] == sep {
+ n++
+ lastmatch = len(sep)
+ }
+ for i := len(sep); i < len(s); {
+ h *= primeRK
+ h += uint32(s[i])
+ h -= pow * uint32(s[i-len(sep)])
+ i++
+ if h == hashsep && lastmatch <= i-len(sep) && s[i-len(sep):i] == sep {
n++
- i += l - 1
+ lastmatch = i
}
}
return n
@@ -80,11 +122,11 @@ func ContainsRune(s string, r rune) bool {
// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
func Index(s, sep string) int {
n := len(sep)
- if n == 0 {
+ switch {
+ case n == 0:
return 0
- }
- c := sep[0]
- if n == 1 {
+ case n == 1:
+ c := sep[0]
// special case worth making fast
for i := 0; i < len(s); i++ {
if s[i] == c {
@@ -92,11 +134,30 @@ func Index(s, sep string) int {
}
}
return -1
+ case n == len(s):
+ if sep == s {
+ return 0
+ }
+ return -1
+ case n > len(s):
+ return -1
}
- // n > 1
- for i := 0; i+n <= len(s); i++ {
- if s[i] == c && s[i:i+n] == sep {
- return i
+ // Hash sep.
+ hashsep, pow := hashstr(sep)
+ var h uint32
+ for i := 0; i < n; i++ {
+ h = h*primeRK + uint32(s[i])
+ }
+ if h == hashsep && s[:n] == sep {
+ return 0
+ }
+ for i := n; i < len(s); {
+ h *= primeRK
+ h += uint32(s[i])
+ h -= pow * uint32(s[i-n])
+ i++
+ if h == hashsep && s[i-n:i] == sep {
+ return i - n
}
}
return -1
@@ -244,7 +305,8 @@ func SplitAfter(s, sep string) []string {
}
// Fields splits the string s around each instance of one or more consecutive white space
-// characters, returning an array of substrings of s or an empty list if s contains only white space.
+// characters, as defined by unicode.IsSpace, returning an array of substrings of s or an
+// empty list if s contains only white space.
func Fields(s string) []string {
return FieldsFunc(s, unicode.IsSpace)
}
@@ -558,6 +620,24 @@ func TrimSpace(s string) string {
return TrimFunc(s, unicode.IsSpace)
}
+// TrimPrefix returns s without the provided leading prefix string.
+// If s doesn't start with prefix, s is returned unchanged.
+func TrimPrefix(s, prefix string) string {
+ if HasPrefix(s, prefix) {
+ return s[len(prefix):]
+ }
+ return s
+}
+
+// TrimSuffix returns s without the provided trailing suffix string.
+// If s doesn't end with suffix, s is returned unchanged.
+func TrimSuffix(s, suffix string) string {
+ if HasSuffix(s, suffix) {
+ return s[:len(s)-len(suffix)]
+ }
+ return s
+}
+
// Replace returns a copy of the string s with the first n
// non-overlapping instances of old replaced by new.
// If n < 0, there is no limit on the number of replacements.
diff --git a/src/pkg/strings/strings_test.go b/src/pkg/strings/strings_test.go
index 54046d68a..09de49e5f 100644
--- a/src/pkg/strings/strings_test.go
+++ b/src/pkg/strings/strings_test.go
@@ -7,6 +7,7 @@ package strings_test
import (
"bytes"
"io"
+ "math/rand"
"reflect"
. "strings"
"testing"
@@ -311,6 +312,13 @@ var FieldsFuncTests = []FieldsTest{
}
func TestFieldsFunc(t *testing.T) {
+ for _, tt := range fieldstests {
+ a := FieldsFunc(tt.s, unicode.IsSpace)
+ if !eq(a, tt.a) {
+ t.Errorf("FieldsFunc(%q, unicode.IsSpace) = %v; want %v", tt.s, a, tt.a)
+ continue
+ }
+ }
pred := func(c rune) bool { return c == 'X' }
for _, tt := range FieldsFuncTests {
a := FieldsFunc(tt.s, pred)
@@ -488,8 +496,8 @@ func TestSpecialCase(t *testing.T) {
func TestTrimSpace(t *testing.T) { runStringTests(t, TrimSpace, "TrimSpace", trimSpaceTests) }
var trimTests = []struct {
- f string
- in, cutset, out string
+ f string
+ in, arg, out string
}{
{"Trim", "abba", "a", "bb"},
{"Trim", "abba", "ab", ""},
@@ -512,6 +520,10 @@ var trimTests = []struct {
{"TrimRight", "", "123", ""},
{"TrimRight", "", "", ""},
{"TrimRight", "☺\xc0", "☺", "☺\xc0"},
+ {"TrimPrefix", "aabb", "a", "abb"},
+ {"TrimPrefix", "aabb", "b", "aabb"},
+ {"TrimSuffix", "aabb", "a", "aabb"},
+ {"TrimSuffix", "aabb", "b", "aab"},
}
func TestTrim(t *testing.T) {
@@ -525,12 +537,16 @@ func TestTrim(t *testing.T) {
f = TrimLeft
case "TrimRight":
f = TrimRight
+ case "TrimPrefix":
+ f = TrimPrefix
+ case "TrimSuffix":
+ f = TrimSuffix
default:
t.Errorf("Undefined trim function %s", name)
}
- actual := f(tc.in, tc.cutset)
+ actual := f(tc.in, tc.arg)
if actual != tc.out {
- t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out)
+ t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out)
}
}
}
@@ -951,7 +967,7 @@ var ContainsRuneTests = []struct {
func TestContainsRune(t *testing.T) {
for _, ct := range ContainsRuneTests {
if ContainsRune(ct.str, ct.r) != ct.expected {
- t.Errorf("ContainsRune(%s, %s) = %v, want %v",
+ t.Errorf("ContainsRune(%q, %q) = %v, want %v",
ct.str, ct.r, !ct.expected, ct.expected)
}
}
@@ -984,3 +1000,98 @@ func TestEqualFold(t *testing.T) {
}
}
}
+
+func makeBenchInputHard() string {
+ tokens := [...]string{
+ "<a>", "<p>", "<b>", "<strong>",
+ "</a>", "</p>", "</b>", "</strong>",
+ "hello", "world",
+ }
+ x := make([]byte, 0, 1<<20)
+ for len(x) < 1<<20 {
+ i := rand.Intn(len(tokens))
+ x = append(x, tokens[i]...)
+ }
+ return string(x)
+}
+
+var benchInputHard = makeBenchInputHard()
+
+func benchmarkIndexHard(b *testing.B, sep string) {
+ for i := 0; i < b.N; i++ {
+ Index(benchInputHard, sep)
+ }
+}
+
+func benchmarkCountHard(b *testing.B, sep string) {
+ for i := 0; i < b.N; i++ {
+ Count(benchInputHard, sep)
+ }
+}
+
+func BenchmarkIndexHard1(b *testing.B) { benchmarkIndexHard(b, "<>") }
+func BenchmarkIndexHard2(b *testing.B) { benchmarkIndexHard(b, "</pre>") }
+func BenchmarkIndexHard3(b *testing.B) { benchmarkIndexHard(b, "<b>hello world</b>") }
+
+func BenchmarkCountHard1(b *testing.B) { benchmarkCountHard(b, "<>") }
+func BenchmarkCountHard2(b *testing.B) { benchmarkCountHard(b, "</pre>") }
+func BenchmarkCountHard3(b *testing.B) { benchmarkCountHard(b, "<b>hello world</b>") }
+
+var benchInputTorture = Repeat("ABC", 1<<10) + "123" + Repeat("ABC", 1<<10)
+var benchNeedleTorture = Repeat("ABC", 1<<10+1)
+
+func BenchmarkIndexTorture(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Index(benchInputTorture, benchNeedleTorture)
+ }
+}
+
+func BenchmarkCountTorture(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Count(benchInputTorture, benchNeedleTorture)
+ }
+}
+
+func BenchmarkCountTortureOverlapping(b *testing.B) {
+ A := Repeat("ABC", 1<<20)
+ B := Repeat("ABC", 1<<10)
+ for i := 0; i < b.N; i++ {
+ Count(A, B)
+ }
+}
+
+var makeFieldsInput = func() string {
+ x := make([]byte, 1<<20)
+ // Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space.
+ for i := range x {
+ switch rand.Intn(10) {
+ case 0:
+ x[i] = ' '
+ case 1:
+ if i > 0 && x[i-1] == 'x' {
+ copy(x[i-1:], "χ")
+ break
+ }
+ fallthrough
+ default:
+ x[i] = 'x'
+ }
+ }
+ return string(x)
+}
+
+var fieldsInput = makeFieldsInput()
+
+func BenchmarkFields(b *testing.B) {
+ b.SetBytes(int64(len(fieldsInput)))
+ for i := 0; i < b.N; i++ {
+ Fields(fieldsInput)
+ }
+}
+
+func BenchmarkFieldsFunc(b *testing.B) {
+ b.SetBytes(int64(len(fieldsInput)))
+ for i := 0; i < b.N; i++ {
+ FieldsFunc(fieldsInput, unicode.IsSpace)
+ }
+}
diff --git a/src/pkg/sync/atomic/64bit_arm.go b/src/pkg/sync/atomic/64bit_arm.go
new file mode 100644
index 000000000..f070e78bd
--- /dev/null
+++ b/src/pkg/sync/atomic/64bit_arm.go
@@ -0,0 +1,36 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package atomic
+
+func loadUint64(addr *uint64) (val uint64) {
+ for {
+ val = *addr
+ if CompareAndSwapUint64(addr, val, val) {
+ break
+ }
+ }
+ return
+}
+
+func storeUint64(addr *uint64, val uint64) {
+ for {
+ old := *addr
+ if CompareAndSwapUint64(addr, old, val) {
+ break
+ }
+ }
+ return
+}
+
+func addUint64(val *uint64, delta uint64) (new uint64) {
+ for {
+ old := *val
+ new = old + delta
+ if CompareAndSwapUint64(val, old, new) {
+ break
+ }
+ }
+ return
+}
diff --git a/src/pkg/sync/atomic/asm_386.s b/src/pkg/sync/atomic/asm_386.s
index a406852f4..7a98a61d8 100644
--- a/src/pkg/sync/atomic/asm_386.s
+++ b/src/pkg/sync/atomic/asm_386.s
@@ -2,17 +2,19 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !race
+
TEXT ·CompareAndSwapInt32(SB),7,$0
JMP ·CompareAndSwapUint32(SB)
TEXT ·CompareAndSwapUint32(SB),7,$0
- MOVL valptr+0(FP), BP
+ MOVL addr+0(FP), BP
MOVL old+4(FP), AX
MOVL new+8(FP), CX
// CMPXCHGL was introduced on the 486.
LOCK
CMPXCHGL CX, 0(BP)
- SETEQ ret+12(FP)
+ SETEQ swapped+12(FP)
RET
TEXT ·CompareAndSwapUintptr(SB),7,$0
@@ -25,29 +27,29 @@ TEXT ·CompareAndSwapInt64(SB),7,$0
JMP ·CompareAndSwapUint64(SB)
TEXT ·CompareAndSwapUint64(SB),7,$0
- MOVL valptr+0(FP), BP
- MOVL oldlo+4(FP), AX
- MOVL oldhi+8(FP), DX
- MOVL newlo+12(FP), BX
- MOVL newhi+16(FP), CX
+ MOVL addr+0(FP), BP
+ MOVL old+4(FP), AX
+ MOVL old+8(FP), DX
+ MOVL new+12(FP), BX
+ MOVL new+16(FP), CX
// CMPXCHG8B was introduced on the Pentium.
LOCK
CMPXCHG8B 0(BP)
- SETEQ ret+20(FP)
+ SETEQ swapped+20(FP)
RET
TEXT ·AddInt32(SB),7,$0
JMP ·AddUint32(SB)
TEXT ·AddUint32(SB),7,$0
- MOVL valptr+0(FP), BP
+ MOVL addr+0(FP), BP
MOVL delta+4(FP), AX
MOVL AX, CX
// XADD was introduced on the 486.
LOCK
XADDL AX, 0(BP)
ADDL AX, CX
- MOVL CX, ret+8(FP)
+ MOVL CX, new+8(FP)
RET
TEXT ·AddUintptr(SB),7,$0
@@ -58,24 +60,24 @@ TEXT ·AddInt64(SB),7,$0
TEXT ·AddUint64(SB),7,$0
// no XADDQ so use CMPXCHG8B loop
- MOVL valptr+0(FP), BP
+ MOVL addr+0(FP), BP
// DI:SI = delta
- MOVL deltalo+4(FP), SI
- MOVL deltahi+8(FP), DI
- // DX:AX = *valptr
+ MOVL delta+4(FP), SI
+ MOVL delta+8(FP), DI
+ // DX:AX = *addr
MOVL 0(BP), AX
MOVL 4(BP), DX
addloop:
- // CX:BX = DX:AX (*valptr) + DI:SI (delta)
+ // CX:BX = DX:AX (*addr) + DI:SI (delta)
MOVL AX, BX
MOVL DX, CX
ADDL SI, BX
ADCL DI, CX
- // if *valptr == DX:AX {
- // *valptr = CX:BX
+ // if *addr == DX:AX {
+ // *addr = CX:BX
// } else {
- // DX:AX = *valptr
+ // DX:AX = *addr
// }
// all in one instruction
LOCK
@@ -85,24 +87,24 @@ addloop:
// success
// return CX:BX
- MOVL BX, retlo+12(FP)
- MOVL CX, rethi+16(FP)
+ MOVL BX, new+12(FP)
+ MOVL CX, new+16(FP)
RET
TEXT ·LoadInt32(SB),7,$0
JMP ·LoadUint32(SB)
TEXT ·LoadUint32(SB),7,$0
- MOVL addrptr+0(FP), AX
+ MOVL addr+0(FP), AX
MOVL 0(AX), AX
- MOVL AX, ret+4(FP)
+ MOVL AX, val+4(FP)
RET
TEXT ·LoadInt64(SB),7,$0
JMP ·LoadUint64(SB)
TEXT ·LoadUint64(SB),7,$0
- MOVL addrptr+0(FP), AX
+ MOVL addr+0(FP), AX
// MOVQ and EMMS were introduced on the Pentium MMX.
// MOVQ (%EAX), %MM0
BYTE $0x0f; BYTE $0x6f; BYTE $0x00
@@ -121,7 +123,7 @@ TEXT ·StoreInt32(SB),7,$0
JMP ·StoreUint32(SB)
TEXT ·StoreUint32(SB),7,$0
- MOVL addrptr+0(FP), BP
+ MOVL addr+0(FP), BP
MOVL val+4(FP), AX
XCHGL AX, 0(BP)
RET
@@ -130,7 +132,7 @@ TEXT ·StoreInt64(SB),7,$0
JMP ·StoreUint64(SB)
TEXT ·StoreUint64(SB),7,$0
- MOVL addrptr+0(FP), AX
+ MOVL addr+0(FP), AX
// MOVQ and EMMS were introduced on the Pentium MMX.
// MOVQ 0x8(%ESP), %MM0
BYTE $0x0f; BYTE $0x6f; BYTE $0x44; BYTE $0x24; BYTE $0x08
diff --git a/src/pkg/sync/atomic/asm_amd64.s b/src/pkg/sync/atomic/asm_amd64.s
index 6f8bde068..58bda9e4f 100644
--- a/src/pkg/sync/atomic/asm_amd64.s
+++ b/src/pkg/sync/atomic/asm_amd64.s
@@ -2,16 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !race
+
TEXT ·CompareAndSwapInt32(SB),7,$0
JMP ·CompareAndSwapUint32(SB)
TEXT ·CompareAndSwapUint32(SB),7,$0
- MOVQ valptr+0(FP), BP
+ MOVQ addr+0(FP), BP
MOVL old+8(FP), AX
MOVL new+12(FP), CX
LOCK
CMPXCHGL CX, 0(BP)
- SETEQ ret+16(FP)
+ SETEQ swapped+16(FP)
RET
TEXT ·CompareAndSwapUintptr(SB),7,$0
@@ -24,25 +26,25 @@ TEXT ·CompareAndSwapInt64(SB),7,$0
JMP ·CompareAndSwapUint64(SB)
TEXT ·CompareAndSwapUint64(SB),7,$0
- MOVQ valptr+0(FP), BP
+ MOVQ addr+0(FP), BP
MOVQ old+8(FP), AX
MOVQ new+16(FP), CX
LOCK
CMPXCHGQ CX, 0(BP)
- SETEQ ret+24(FP)
+ SETEQ swapped+24(FP)
RET
TEXT ·AddInt32(SB),7,$0
JMP ·AddUint32(SB)
TEXT ·AddUint32(SB),7,$0
- MOVQ valptr+0(FP), BP
+ MOVQ addr+0(FP), BP
MOVL delta+8(FP), AX
MOVL AX, CX
LOCK
XADDL AX, 0(BP)
ADDL AX, CX
- MOVL CX, ret+16(FP)
+ MOVL CX, new+16(FP)
RET
TEXT ·AddUintptr(SB),7,$0
@@ -52,47 +54,47 @@ TEXT ·AddInt64(SB),7,$0
JMP ·AddUint64(SB)
TEXT ·AddUint64(SB),7,$0
- MOVQ valptr+0(FP), BP
+ MOVQ addr+0(FP), BP
MOVQ delta+8(FP), AX
MOVQ AX, CX
LOCK
XADDQ AX, 0(BP)
ADDQ AX, CX
- MOVQ CX, ret+16(FP)
+ MOVQ CX, new+16(FP)
RET
TEXT ·LoadInt32(SB),7,$0
JMP ·LoadUint32(SB)
TEXT ·LoadUint32(SB),7,$0
- MOVQ addrptr+0(FP), AX
+ MOVQ addr+0(FP), AX
MOVL 0(AX), AX
- MOVL AX, ret+8(FP)
+ MOVL AX, val+8(FP)
RET
TEXT ·LoadInt64(SB),7,$0
JMP ·LoadUint64(SB)
TEXT ·LoadUint64(SB),7,$0
- MOVQ addrptr+0(FP), AX
+ MOVQ addr+0(FP), AX
MOVQ 0(AX), AX
- MOVQ AX, ret+8(FP)
+ MOVQ AX, val+8(FP)
RET
TEXT ·LoadUintptr(SB),7,$0
JMP ·LoadPointer(SB)
TEXT ·LoadPointer(SB),7,$0
- MOVQ addrptr+0(FP), AX
+ MOVQ addr+0(FP), AX
MOVQ 0(AX), AX
- MOVQ AX, ret+8(FP)
+ MOVQ AX, val+8(FP)
RET
TEXT ·StoreInt32(SB),7,$0
JMP ·StoreUint32(SB)
TEXT ·StoreUint32(SB),7,$0
- MOVQ addrptr+0(FP), BP
+ MOVQ addr+0(FP), BP
MOVL val+8(FP), AX
XCHGL AX, 0(BP)
RET
@@ -101,7 +103,7 @@ TEXT ·StoreInt64(SB),7,$0
JMP ·StoreUint64(SB)
TEXT ·StoreUint64(SB),7,$0
- MOVQ addrptr+0(FP), BP
+ MOVQ addr+0(FP), BP
MOVQ val+8(FP), AX
XCHGQ AX, 0(BP)
RET
@@ -110,7 +112,7 @@ TEXT ·StoreUintptr(SB),7,$0
JMP ·StorePointer(SB)
TEXT ·StorePointer(SB),7,$0
- MOVQ addrptr+0(FP), BP
+ MOVQ addr+0(FP), BP
MOVQ val+8(FP), AX
XCHGQ AX, 0(BP)
RET
diff --git a/src/pkg/sync/atomic/asm_arm.s b/src/pkg/sync/atomic/asm_arm.s
index 2d10a922b..4faf5b5d9 100644
--- a/src/pkg/sync/atomic/asm_arm.s
+++ b/src/pkg/sync/atomic/asm_arm.s
@@ -2,10 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !race
+
// ARM atomic operations, for use by asm_$(GOOS)_arm.s.
TEXT ·armCompareAndSwapUint32(SB),7,$0
- MOVW valptr+0(FP), R1
+ MOVW addr+0(FP), R1
MOVW old+4(FP), R2
MOVW new+8(FP), R3
casloop:
@@ -26,7 +28,7 @@ casfail:
TEXT ·armCompareAndSwapUint64(SB),7,$0
BL fastCheck64<>(SB)
- MOVW valptr+0(FP), R1
+ MOVW addr+0(FP), R1
MOVW oldlo+4(FP), R2
MOVW oldhi+8(FP), R3
MOVW newlo+12(FP), R4
@@ -50,7 +52,7 @@ cas64fail:
RET
TEXT ·armAddUint32(SB),7,$0
- MOVW valptr+0(FP), R1
+ MOVW addr+0(FP), R1
MOVW delta+4(FP), R2
addloop:
// LDREX and STREX were introduced in ARM 6.
@@ -64,7 +66,7 @@ addloop:
TEXT ·armAddUint64(SB),7,$0
BL fastCheck64<>(SB)
- MOVW valptr+0(FP), R1
+ MOVW addr+0(FP), R1
MOVW deltalo+4(FP), R2
MOVW deltahi+8(FP), R3
add64loop:
@@ -81,7 +83,7 @@ add64loop:
TEXT ·armLoadUint64(SB),7,$0
BL fastCheck64<>(SB)
- MOVW addrptr+0(FP), R1
+ MOVW addr+0(FP), R1
load64loop:
LDREXD (R1), R2 // loads R2 and R3
STREXD R2, (R1), R0 // stores R2 and R3
@@ -93,7 +95,7 @@ load64loop:
TEXT ·armStoreUint64(SB),7,$0
BL fastCheck64<>(SB)
- MOVW addrptr+0(FP), R1
+ MOVW addr+0(FP), R1
MOVW vallo+4(FP), R2
MOVW valhi+8(FP), R3
store64loop:
diff --git a/src/pkg/sync/atomic/asm_freebsd_arm.s b/src/pkg/sync/atomic/asm_freebsd_arm.s
new file mode 100644
index 000000000..6590921b0
--- /dev/null
+++ b/src/pkg/sync/atomic/asm_freebsd_arm.s
@@ -0,0 +1,89 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// FreeBSD/ARM atomic operations.
+// TODO(minux): this only supports ARMv6K or higher.
+
+TEXT ·CompareAndSwapInt32(SB),7,$0
+ B ·CompareAndSwapUint32(SB)
+
+TEXT ·CompareAndSwapUint32(SB),7,$0
+ B ·armCompareAndSwapUint32(SB)
+
+TEXT ·CompareAndSwapUintptr(SB),7,$0
+ B ·CompareAndSwapUint32(SB)
+
+TEXT ·CompareAndSwapPointer(SB),7,$0
+ B ·CompareAndSwapUint32(SB)
+
+TEXT ·AddInt32(SB),7,$0
+ B ·AddUint32(SB)
+
+TEXT ·AddUint32(SB),7,$0
+ B ·armAddUint32(SB)
+
+TEXT ·AddUintptr(SB),7,$0
+ B ·AddUint32(SB)
+
+TEXT ·CompareAndSwapInt64(SB),7,$0
+ B ·CompareAndSwapUint64(SB)
+
+TEXT ·CompareAndSwapUint64(SB),7,$-4
+ B ·armCompareAndSwapUint64(SB)
+
+TEXT ·AddInt64(SB),7,$0
+ B ·addUint64(SB)
+
+TEXT ·AddUint64(SB),7,$0
+ B ·addUint64(SB)
+
+TEXT ·LoadInt32(SB),7,$0
+ B ·LoadUint32(SB)
+
+TEXT ·LoadUint32(SB),7,$0
+ MOVW addr+0(FP), R1
+load32loop:
+ LDREX (R1), R2 // loads R2
+ STREX R2, (R1), R0 // stores R2
+ CMP $0, R0
+ BNE load32loop
+ MOVW R2, val+4(FP)
+ RET
+
+TEXT ·LoadInt64(SB),7,$0
+ B ·loadUint64(SB)
+
+TEXT ·LoadUint64(SB),7,$0
+ B ·loadUint64(SB)
+
+TEXT ·LoadUintptr(SB),7,$0
+ B ·LoadUint32(SB)
+
+TEXT ·LoadPointer(SB),7,$0
+ B ·LoadUint32(SB)
+
+TEXT ·StoreInt32(SB),7,$0
+ B ·StoreUint32(SB)
+
+TEXT ·StoreUint32(SB),7,$0
+ MOVW addr+0(FP), R1
+ MOVW val+4(FP), R2
+storeloop:
+ LDREX (R1), R4 // loads R4
+ STREX R2, (R1), R0 // stores R2
+ CMP $0, R0
+ BNE storeloop
+ RET
+
+TEXT ·StoreInt64(SB),7,$0
+ B ·storeUint64(SB)
+
+TEXT ·StoreUint64(SB),7,$0
+ B ·storeUint64(SB)
+
+TEXT ·StoreUintptr(SB),7,$0
+ B ·StoreUint32(SB)
+
+TEXT ·StorePointer(SB),7,$0
+ B ·StoreUint32(SB)
diff --git a/src/pkg/sync/atomic/asm_linux_arm.s b/src/pkg/sync/atomic/asm_linux_arm.s
index 25dc85804..098acf35b 100644
--- a/src/pkg/sync/atomic/asm_linux_arm.s
+++ b/src/pkg/sync/atomic/asm_linux_arm.s
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !race
+
// Linux/ARM atomic operations.
// Because there is so much variation in ARM devices,
@@ -9,7 +11,7 @@
// implementation at address 0xffff0fc0. Caller sets:
// R0 = old value
// R1 = new value
-// R2 = valptr
+// R2 = addr
// LR = return address
// The function returns with CS true if the swap happened.
// http://lxr.linux.no/linux+v2.6.37.2/arch/arm/kernel/entry-armv.S#L850
@@ -27,7 +29,7 @@ TEXT ·CompareAndSwapInt32(SB),7,$0
// Implement using kernel cas for portability.
TEXT ·CompareAndSwapUint32(SB),7,$0
- MOVW valptr+0(FP), R2
+ MOVW addr+0(FP), R2
MOVW old+4(FP), R0
casagain:
MOVW new+8(FP), R1
@@ -39,7 +41,7 @@ casret:
RET
cascheck:
// Kernel lies; double-check.
- MOVW valptr+0(FP), R2
+ MOVW addr+0(FP), R2
MOVW old+4(FP), R0
MOVW 0(R2), R3
CMP R0, R3
@@ -58,7 +60,7 @@ TEXT ·AddInt32(SB),7,$0
// Implement using kernel cas for portability.
TEXT ·AddUint32(SB),7,$0
- MOVW valptr+0(FP), R2
+ MOVW addr+0(FP), R2
MOVW delta+4(FP), R4
addloop1:
MOVW 0(R2), R0
@@ -72,26 +74,75 @@ addloop1:
TEXT ·AddUintptr(SB),7,$0
B ·AddUint32(SB)
-// The kernel provides no 64-bit compare-and-swap,
-// so use native ARM instructions, which will only work on
-// ARM 11 and later devices.
+TEXT cas64<>(SB),7,$0
+ MOVW $0xffff0f60, PC // __kuser_cmpxchg64: Linux-3.1 and above
+
+TEXT kernelCAS64<>(SB),7,$0
+ // int (*__kuser_cmpxchg64_t)(const int64_t *oldval, const int64_t *newval, volatile int64_t *ptr);
+ MOVW addr+0(FP), R2 // ptr
+ MOVW $4(FP), R0 // oldval
+ MOVW $12(FP), R1 // newval
+ BL cas64<>(SB)
+ MOVW.CS $1, R0 // C is set if the kernel has changed *ptr
+ MOVW.CC $0, R0
+ MOVW R0, 20(FP)
+ RET
+
+TEXT generalCAS64<>(SB),7,$20
+ // bool runtime·cas64(uint64 volatile *addr, uint64 *old, uint64 new)
+ MOVW addr+0(FP), R0
+ MOVW R0, 4(R13)
+ MOVW $4(FP), R1 // oldval
+ MOVW R1, 8(R13)
+ MOVW newlo+12(FP), R2
+ MOVW R2, 12(R13)
+ MOVW newhi+16(FP), R3
+ MOVW R3, 16(R13)
+ BL runtime·cas64(SB)
+ MOVW R0, 20(FP)
+ RET
+
+GLOBL armCAS64(SB), $4
+
+TEXT setupAndCallCAS64<>(SB),7,$-4
+ MOVW $0xffff0ffc, R0 // __kuser_helper_version
+ MOVW (R0), R0
+ // __kuser_cmpxchg64 only present if helper version >= 5
+ CMP $5, R0
+ MOVW.CS $kernelCAS64<>(SB), R1
+ MOVW.CS R1, armCAS64(SB)
+ MOVW.CS R1, PC
+ MOVB runtime·armArch(SB), R0
+ // LDREXD, STREXD only present on ARMv6K or higher
+ CMP $6, R0 // TODO(minux): how to differentiate ARMv6 with ARMv6K?
+ MOVW.CS $·armCompareAndSwapUint64(SB), R1
+ MOVW.CS R1, armCAS64(SB)
+ MOVW.CS R1, PC
+ // we are out of luck, can only use runtime's emulated 64-bit cas
+ MOVW $generalCAS64<>(SB), R1
+ MOVW R1, armCAS64(SB)
+ MOVW R1, PC
+
TEXT ·CompareAndSwapInt64(SB),7,$0
- B ·armCompareAndSwapUint64(SB)
+ B ·CompareAndSwapUint64(SB)
-TEXT ·CompareAndSwapUint64(SB),7,$0
- B ·armCompareAndSwapUint64(SB)
+TEXT ·CompareAndSwapUint64(SB),7,$-4
+ MOVW armCAS64(SB), R0
+ CMP $0, R0
+ MOVW.NE R0, PC
+ B setupAndCallCAS64<>(SB)
TEXT ·AddInt64(SB),7,$0
- B ·armAddUint64(SB)
+ B ·addUint64(SB)
TEXT ·AddUint64(SB),7,$0
- B ·armAddUint64(SB)
+ B ·addUint64(SB)
TEXT ·LoadInt32(SB),7,$0
B ·LoadUint32(SB)
TEXT ·LoadUint32(SB),7,$0
- MOVW addrptr+0(FP), R2
+ MOVW addr+0(FP), R2
loadloop1:
MOVW 0(R2), R0
MOVW R0, R1
@@ -101,10 +152,10 @@ loadloop1:
RET
TEXT ·LoadInt64(SB),7,$0
- B ·armLoadUint64(SB)
+ B ·loadUint64(SB)
TEXT ·LoadUint64(SB),7,$0
- B ·armLoadUint64(SB)
+ B ·loadUint64(SB)
TEXT ·LoadUintptr(SB),7,$0
B ·LoadUint32(SB)
@@ -116,7 +167,7 @@ TEXT ·StoreInt32(SB),7,$0
B ·StoreUint32(SB)
TEXT ·StoreUint32(SB),7,$0
- MOVW addrptr+0(FP), R2
+ MOVW addr+0(FP), R2
MOVW val+4(FP), R1
storeloop1:
MOVW 0(R2), R0
@@ -125,10 +176,10 @@ storeloop1:
RET
TEXT ·StoreInt64(SB),7,$0
- B ·armStoreUint64(SB)
+ B ·storeUint64(SB)
TEXT ·StoreUint64(SB),7,$0
- B ·armStoreUint64(SB)
+ B ·storeUint64(SB)
TEXT ·StoreUintptr(SB),7,$0
B ·StoreUint32(SB)
diff --git a/src/pkg/sync/atomic/asm_netbsd_arm.s b/src/pkg/sync/atomic/asm_netbsd_arm.s
new file mode 100644
index 000000000..677f3daaa
--- /dev/null
+++ b/src/pkg/sync/atomic/asm_netbsd_arm.s
@@ -0,0 +1,89 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// NetBSD/ARM atomic operations.
+// TODO(minux): this only supports ARMv6K or higher.
+
+TEXT ·CompareAndSwapInt32(SB),7,$0
+ B ·CompareAndSwapUint32(SB)
+
+TEXT ·CompareAndSwapUint32(SB),7,$0
+ B ·armCompareAndSwapUint32(SB)
+
+TEXT ·CompareAndSwapUintptr(SB),7,$0
+ B ·CompareAndSwapUint32(SB)
+
+TEXT ·CompareAndSwapPointer(SB),7,$0
+ B ·CompareAndSwapUint32(SB)
+
+TEXT ·AddInt32(SB),7,$0
+ B ·AddUint32(SB)
+
+TEXT ·AddUint32(SB),7,$0
+ B ·armAddUint32(SB)
+
+TEXT ·AddUintptr(SB),7,$0
+ B ·AddUint32(SB)
+
+TEXT ·CompareAndSwapInt64(SB),7,$0
+ B ·CompareAndSwapUint64(SB)
+
+TEXT ·CompareAndSwapUint64(SB),7,$-4
+ B ·armCompareAndSwapUint64(SB)
+
+TEXT ·AddInt64(SB),7,$0
+ B ·addUint64(SB)
+
+TEXT ·AddUint64(SB),7,$0
+ B ·addUint64(SB)
+
+TEXT ·LoadInt32(SB),7,$0
+ B ·LoadUint32(SB)
+
+TEXT ·LoadUint32(SB),7,$0
+ MOVW addr+0(FP), R1
+load32loop:
+ LDREX (R1), R2 // loads R2
+ STREX R2, (R1), R0 // stores R2
+ CMP $0, R0
+ BNE load32loop
+ MOVW R2, val+4(FP)
+ RET
+
+TEXT ·LoadInt64(SB),7,$0
+ B ·loadUint64(SB)
+
+TEXT ·LoadUint64(SB),7,$0
+ B ·loadUint64(SB)
+
+TEXT ·LoadUintptr(SB),7,$0
+ B ·LoadUint32(SB)
+
+TEXT ·LoadPointer(SB),7,$0
+ B ·LoadUint32(SB)
+
+TEXT ·StoreInt32(SB),7,$0
+ B ·StoreUint32(SB)
+
+TEXT ·StoreUint32(SB),7,$0
+ MOVW addr+0(FP), R1
+ MOVW val+4(FP), R2
+storeloop:
+ LDREX (R1), R4 // loads R4
+ STREX R2, (R1), R0 // stores R2
+ CMP $0, R0
+ BNE storeloop
+ RET
+
+TEXT ·StoreInt64(SB),7,$0
+ B ·storeUint64(SB)
+
+TEXT ·StoreUint64(SB),7,$0
+ B ·storeUint64(SB)
+
+TEXT ·StoreUintptr(SB),7,$0
+ B ·StoreUint32(SB)
+
+TEXT ·StorePointer(SB),7,$0
+ B ·StoreUint32(SB)
diff --git a/src/pkg/sync/atomic/atomic_test.go b/src/pkg/sync/atomic/atomic_test.go
index f60d997ce..3e105561c 100644
--- a/src/pkg/sync/atomic/atomic_test.go
+++ b/src/pkg/sync/atomic/atomic_test.go
@@ -82,8 +82,7 @@ func TestAddUint32(t *testing.T) {
func TestAddInt64(t *testing.T) {
if test64err != nil {
- t.Logf("Skipping 64-bit tests: %v", test64err)
- return
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
}
var x struct {
before int64
@@ -107,8 +106,7 @@ func TestAddInt64(t *testing.T) {
func TestAddUint64(t *testing.T) {
if test64err != nil {
- t.Logf("Skipping 64-bit tests: %v", test64err)
- return
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
}
var x struct {
before uint64
@@ -213,8 +211,7 @@ func TestCompareAndSwapUint32(t *testing.T) {
func TestCompareAndSwapInt64(t *testing.T) {
if test64err != nil {
- t.Logf("Skipping 64-bit tests: %v", test64err)
- return
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
}
var x struct {
before int64
@@ -246,8 +243,7 @@ func TestCompareAndSwapInt64(t *testing.T) {
func TestCompareAndSwapUint64(t *testing.T) {
if test64err != nil {
- t.Logf("Skipping 64-bit tests: %v", test64err)
- return
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
}
var x struct {
before uint64
@@ -381,8 +377,7 @@ func TestLoadUint32(t *testing.T) {
func TestLoadInt64(t *testing.T) {
if test64err != nil {
- t.Logf("Skipping 64-bit tests: %v", test64err)
- return
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
}
var x struct {
before int64
@@ -405,8 +400,7 @@ func TestLoadInt64(t *testing.T) {
func TestLoadUint64(t *testing.T) {
if test64err != nil {
- t.Logf("Skipping 64-bit tests: %v", test64err)
- return
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
}
var x struct {
before uint64
@@ -515,8 +509,7 @@ func TestStoreUint32(t *testing.T) {
func TestStoreInt64(t *testing.T) {
if test64err != nil {
- t.Logf("Skipping 64-bit tests: %v", test64err)
- return
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
}
var x struct {
before int64
@@ -540,8 +533,7 @@ func TestStoreInt64(t *testing.T) {
func TestStoreUint64(t *testing.T) {
if test64err != nil {
- t.Logf("Skipping 64-bit tests: %v", test64err)
- return
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
}
var x struct {
before uint64
@@ -640,73 +632,73 @@ func init() {
}
}
-func hammerAddInt32(uval *uint32, count int) {
- val := (*int32)(unsafe.Pointer(uval))
+func hammerAddInt32(uaddr *uint32, count int) {
+ addr := (*int32)(unsafe.Pointer(uaddr))
for i := 0; i < count; i++ {
- AddInt32(val, 1)
+ AddInt32(addr, 1)
}
}
-func hammerAddUint32(val *uint32, count int) {
+func hammerAddUint32(addr *uint32, count int) {
for i := 0; i < count; i++ {
- AddUint32(val, 1)
+ AddUint32(addr, 1)
}
}
-func hammerAddUintptr32(uval *uint32, count int) {
+func hammerAddUintptr32(uaddr *uint32, count int) {
// only safe when uintptr is 32-bit.
// not called on 64-bit systems.
- val := (*uintptr)(unsafe.Pointer(uval))
+ addr := (*uintptr)(unsafe.Pointer(uaddr))
for i := 0; i < count; i++ {
- AddUintptr(val, 1)
+ AddUintptr(addr, 1)
}
}
-func hammerCompareAndSwapInt32(uval *uint32, count int) {
- val := (*int32)(unsafe.Pointer(uval))
+func hammerCompareAndSwapInt32(uaddr *uint32, count int) {
+ addr := (*int32)(unsafe.Pointer(uaddr))
for i := 0; i < count; i++ {
for {
- v := *val
- if CompareAndSwapInt32(val, v, v+1) {
+ v := *addr
+ if CompareAndSwapInt32(addr, v, v+1) {
break
}
}
}
}
-func hammerCompareAndSwapUint32(val *uint32, count int) {
+func hammerCompareAndSwapUint32(addr *uint32, count int) {
for i := 0; i < count; i++ {
for {
- v := *val
- if CompareAndSwapUint32(val, v, v+1) {
+ v := *addr
+ if CompareAndSwapUint32(addr, v, v+1) {
break
}
}
}
}
-func hammerCompareAndSwapUintptr32(uval *uint32, count int) {
+func hammerCompareAndSwapUintptr32(uaddr *uint32, count int) {
// only safe when uintptr is 32-bit.
// not called on 64-bit systems.
- val := (*uintptr)(unsafe.Pointer(uval))
+ addr := (*uintptr)(unsafe.Pointer(uaddr))
for i := 0; i < count; i++ {
for {
- v := *val
- if CompareAndSwapUintptr(val, v, v+1) {
+ v := *addr
+ if CompareAndSwapUintptr(addr, v, v+1) {
break
}
}
}
}
-func hammerCompareAndSwapPointer32(uval *uint32, count int) {
+func hammerCompareAndSwapPointer32(uaddr *uint32, count int) {
// only safe when uintptr is 32-bit.
// not called on 64-bit systems.
- val := (*unsafe.Pointer)(unsafe.Pointer(uval))
+ addr := (*unsafe.Pointer)(unsafe.Pointer(uaddr))
for i := 0; i < count; i++ {
for {
- v := *val
- if CompareAndSwapPointer(val, v, unsafe.Pointer(uintptr(v)+1)) {
+ v := *addr
+ if CompareAndSwapPointer(addr, v, unsafe.Pointer(uintptr(v)+1)) {
break
}
}
@@ -765,73 +757,73 @@ func init() {
}
}
-func hammerAddInt64(uval *uint64, count int) {
- val := (*int64)(unsafe.Pointer(uval))
+func hammerAddInt64(uaddr *uint64, count int) {
+ addr := (*int64)(unsafe.Pointer(uaddr))
for i := 0; i < count; i++ {
- AddInt64(val, 1)
+ AddInt64(addr, 1)
}
}
-func hammerAddUint64(val *uint64, count int) {
+func hammerAddUint64(addr *uint64, count int) {
for i := 0; i < count; i++ {
- AddUint64(val, 1)
+ AddUint64(addr, 1)
}
}
-func hammerAddUintptr64(uval *uint64, count int) {
+func hammerAddUintptr64(uaddr *uint64, count int) {
// only safe when uintptr is 64-bit.
// not called on 32-bit systems.
- val := (*uintptr)(unsafe.Pointer(uval))
+ addr := (*uintptr)(unsafe.Pointer(uaddr))
for i := 0; i < count; i++ {
- AddUintptr(val, 1)
+ AddUintptr(addr, 1)
}
}
-func hammerCompareAndSwapInt64(uval *uint64, count int) {
- val := (*int64)(unsafe.Pointer(uval))
+func hammerCompareAndSwapInt64(uaddr *uint64, count int) {
+ addr := (*int64)(unsafe.Pointer(uaddr))
for i := 0; i < count; i++ {
for {
- v := *val
- if CompareAndSwapInt64(val, v, v+1) {
+ v := *addr
+ if CompareAndSwapInt64(addr, v, v+1) {
break
}
}
}
}
-func hammerCompareAndSwapUint64(val *uint64, count int) {
+func hammerCompareAndSwapUint64(addr *uint64, count int) {
for i := 0; i < count; i++ {
for {
- v := *val
- if CompareAndSwapUint64(val, v, v+1) {
+ v := *addr
+ if CompareAndSwapUint64(addr, v, v+1) {
break
}
}
}
}
-func hammerCompareAndSwapUintptr64(uval *uint64, count int) {
+func hammerCompareAndSwapUintptr64(uaddr *uint64, count int) {
// only safe when uintptr is 64-bit.
// not called on 32-bit systems.
- val := (*uintptr)(unsafe.Pointer(uval))
+ addr := (*uintptr)(unsafe.Pointer(uaddr))
for i := 0; i < count; i++ {
for {
- v := *val
- if CompareAndSwapUintptr(val, v, v+1) {
+ v := *addr
+ if CompareAndSwapUintptr(addr, v, v+1) {
break
}
}
}
}
-func hammerCompareAndSwapPointer64(uval *uint64, count int) {
+func hammerCompareAndSwapPointer64(uaddr *uint64, count int) {
// only safe when uintptr is 64-bit.
// not called on 32-bit systems.
- val := (*unsafe.Pointer)(unsafe.Pointer(uval))
+ addr := (*unsafe.Pointer)(unsafe.Pointer(uaddr))
for i := 0; i < count; i++ {
for {
- v := *val
- if CompareAndSwapPointer(val, v, unsafe.Pointer(uintptr(v)+1)) {
+ v := *addr
+ if CompareAndSwapPointer(addr, v, unsafe.Pointer(uintptr(v)+1)) {
break
}
}
@@ -840,8 +832,7 @@ func hammerCompareAndSwapPointer64(uval *uint64, count int) {
func TestHammer64(t *testing.T) {
if test64err != nil {
- t.Logf("Skipping 64-bit tests: %v", test64err)
- return
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
}
const p = 4
n := 100000
@@ -871,9 +862,9 @@ func TestHammer64(t *testing.T) {
}
}
-func hammerStoreLoadInt32(t *testing.T, valp unsafe.Pointer) {
- val := (*int32)(valp)
- v := LoadInt32(val)
+func hammerStoreLoadInt32(t *testing.T, paddr unsafe.Pointer) {
+ addr := (*int32)(paddr)
+ v := LoadInt32(addr)
vlo := v & ((1 << 16) - 1)
vhi := v >> 16
if vlo != vhi {
@@ -883,12 +874,12 @@ func hammerStoreLoadInt32(t *testing.T, valp unsafe.Pointer) {
if vlo == 1e4 {
new = 0
}
- StoreInt32(val, new)
+ StoreInt32(addr, new)
}
-func hammerStoreLoadUint32(t *testing.T, valp unsafe.Pointer) {
- val := (*uint32)(valp)
- v := LoadUint32(val)
+func hammerStoreLoadUint32(t *testing.T, paddr unsafe.Pointer) {
+ addr := (*uint32)(paddr)
+ v := LoadUint32(addr)
vlo := v & ((1 << 16) - 1)
vhi := v >> 16
if vlo != vhi {
@@ -898,38 +889,38 @@ func hammerStoreLoadUint32(t *testing.T, valp unsafe.Pointer) {
if vlo == 1e4 {
new = 0
}
- StoreUint32(val, new)
+ StoreUint32(addr, new)
}
-func hammerStoreLoadInt64(t *testing.T, valp unsafe.Pointer) {
- val := (*int64)(valp)
- v := LoadInt64(val)
+func hammerStoreLoadInt64(t *testing.T, paddr unsafe.Pointer) {
+ addr := (*int64)(paddr)
+ v := LoadInt64(addr)
vlo := v & ((1 << 32) - 1)
vhi := v >> 32
if vlo != vhi {
t.Fatalf("Int64: %#x != %#x", vlo, vhi)
}
new := v + 1 + 1<<32
- StoreInt64(val, new)
+ StoreInt64(addr, new)
}
-func hammerStoreLoadUint64(t *testing.T, valp unsafe.Pointer) {
- val := (*uint64)(valp)
- v := LoadUint64(val)
+func hammerStoreLoadUint64(t *testing.T, paddr unsafe.Pointer) {
+ addr := (*uint64)(paddr)
+ v := LoadUint64(addr)
vlo := v & ((1 << 32) - 1)
vhi := v >> 32
if vlo != vhi {
t.Fatalf("Uint64: %#x != %#x", vlo, vhi)
}
new := v + 1 + 1<<32
- StoreUint64(val, new)
+ StoreUint64(addr, new)
}
-func hammerStoreLoadUintptr(t *testing.T, valp unsafe.Pointer) {
- val := (*uintptr)(valp)
+func hammerStoreLoadUintptr(t *testing.T, paddr unsafe.Pointer) {
+ addr := (*uintptr)(paddr)
var test64 uint64 = 1 << 50
arch32 := uintptr(test64) == 0
- v := LoadUintptr(val)
+ v := LoadUintptr(addr)
new := v
if arch32 {
vlo := v & ((1 << 16) - 1)
@@ -950,14 +941,14 @@ func hammerStoreLoadUintptr(t *testing.T, valp unsafe.Pointer) {
inc := uint64(1 + 1<<32)
new = v + uintptr(inc)
}
- StoreUintptr(val, new)
+ StoreUintptr(addr, new)
}
-func hammerStoreLoadPointer(t *testing.T, valp unsafe.Pointer) {
- val := (*unsafe.Pointer)(valp)
+func hammerStoreLoadPointer(t *testing.T, paddr unsafe.Pointer) {
+ addr := (*unsafe.Pointer)(paddr)
var test64 uint64 = 1 << 50
arch32 := uintptr(test64) == 0
- v := uintptr(LoadPointer(val))
+ v := uintptr(LoadPointer(addr))
new := v
if arch32 {
vlo := v & ((1 << 16) - 1)
@@ -978,7 +969,7 @@ func hammerStoreLoadPointer(t *testing.T, valp unsafe.Pointer) {
inc := uint64(1 + 1<<32)
new = v + uintptr(inc)
}
- StorePointer(val, unsafe.Pointer(new))
+ StorePointer(addr, unsafe.Pointer(new))
}
func TestHammerStoreLoad(t *testing.T) {
@@ -1013,8 +1004,7 @@ func TestHammerStoreLoad(t *testing.T) {
func TestStoreLoadSeqCst32(t *testing.T) {
if runtime.NumCPU() == 1 {
- t.Logf("Skipping test on %v processor machine", runtime.NumCPU())
- return
+ t.Skipf("Skipping test on %v processor machine", runtime.NumCPU())
}
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
N := int32(1e3)
@@ -1054,12 +1044,10 @@ func TestStoreLoadSeqCst32(t *testing.T) {
func TestStoreLoadSeqCst64(t *testing.T) {
if runtime.NumCPU() == 1 {
- t.Logf("Skipping test on %v processor machine", runtime.NumCPU())
- return
+ t.Skipf("Skipping test on %v processor machine", runtime.NumCPU())
}
if test64err != nil {
- t.Logf("Skipping 64-bit tests: %v", test64err)
- return
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
}
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
N := int64(1e3)
@@ -1099,8 +1087,7 @@ func TestStoreLoadSeqCst64(t *testing.T) {
func TestStoreLoadRelAcq32(t *testing.T) {
if runtime.NumCPU() == 1 {
- t.Logf("Skipping test on %v processor machine", runtime.NumCPU())
- return
+ t.Skipf("Skipping test on %v processor machine", runtime.NumCPU())
}
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
N := int32(1e3)
@@ -1132,7 +1119,7 @@ func TestStoreLoadRelAcq32(t *testing.T) {
d1 := X.data1
d2 := X.data2
if d1 != i || d2 != float32(i) {
- t.Fatalf("incorrect data: %d/%d (%d)", d1, d2, i)
+ t.Fatalf("incorrect data: %d/%g (%d)", d1, d2, i)
}
}
}
@@ -1145,12 +1132,10 @@ func TestStoreLoadRelAcq32(t *testing.T) {
func TestStoreLoadRelAcq64(t *testing.T) {
if runtime.NumCPU() == 1 {
- t.Logf("Skipping test on %v processor machine", runtime.NumCPU())
- return
+ t.Skipf("Skipping test on %v processor machine", runtime.NumCPU())
}
if test64err != nil {
- t.Logf("Skipping 64-bit tests: %v", test64err)
- return
+ t.Skipf("Skipping 64-bit tests: %v", test64err)
}
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
N := int64(1e3)
@@ -1182,7 +1167,7 @@ func TestStoreLoadRelAcq64(t *testing.T) {
d1 := X.data1
d2 := X.data2
if d1 != i || d2 != float64(i) {
- t.Fatalf("incorrect data: %d/%d (%d)", d1, d2, i)
+ t.Fatalf("incorrect data: %d/%g (%d)", d1, d2, i)
}
}
}
diff --git a/src/pkg/sync/atomic/doc.go b/src/pkg/sync/atomic/doc.go
index ecb4808ce..27a12c984 100644
--- a/src/pkg/sync/atomic/doc.go
+++ b/src/pkg/sync/atomic/doc.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !race
+
// Package atomic provides low-level atomic memory primitives
// useful for implementing synchronization algorithms.
//
@@ -14,54 +16,67 @@
// The compare-and-swap operation, implemented by the CompareAndSwapT
// functions, is the atomic equivalent of:
//
-// if *val == old {
-// *val = new
+// if *addr == old {
+// *addr = new
// return true
// }
// return false
//
+// The add operation, implemented by the AddT functions, is the atomic
+// equivalent of:
+//
+// *addr += delta
+// return *addr
+//
+// The load and store operations, implemented by the LoadT and StoreT
+// functions, are the atomic equivalents of "return *addr" and
+// "*addr = val".
+//
package atomic
import (
"unsafe"
)
-// BUG(rsc): On ARM, the 64-bit functions use instructions unavailable before ARM 11.
+// BUG(rsc): On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
//
-// On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
+// On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit
+// alignment of 64-bit words accessed atomically. The first word in a global
+// variable or in an allocated struct or slice can be relied upon to be
+// 64-bit aligned.
// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value.
-func CompareAndSwapInt32(val *int32, old, new int32) (swapped bool)
+func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
// CompareAndSwapInt64 executes the compare-and-swap operation for an int64 value.
-func CompareAndSwapInt64(val *int64, old, new int64) (swapped bool)
+func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
// CompareAndSwapUint32 executes the compare-and-swap operation for a uint32 value.
-func CompareAndSwapUint32(val *uint32, old, new uint32) (swapped bool)
+func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
// CompareAndSwapUint64 executes the compare-and-swap operation for a uint64 value.
-func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool)
+func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
// CompareAndSwapUintptr executes the compare-and-swap operation for a uintptr value.
-func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool)
+func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
// CompareAndSwapPointer executes the compare-and-swap operation for a unsafe.Pointer value.
-func CompareAndSwapPointer(val *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
+func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
-// AddInt32 atomically adds delta to *val and returns the new value.
-func AddInt32(val *int32, delta int32) (new int32)
+// AddInt32 atomically adds delta to *addr and returns the new value.
+func AddInt32(addr *int32, delta int32) (new int32)
-// AddUint32 atomically adds delta to *val and returns the new value.
-func AddUint32(val *uint32, delta uint32) (new uint32)
+// AddUint32 atomically adds delta to *addr and returns the new value.
+func AddUint32(addr *uint32, delta uint32) (new uint32)
-// AddInt64 atomically adds delta to *val and returns the new value.
-func AddInt64(val *int64, delta int64) (new int64)
+// AddInt64 atomically adds delta to *addr and returns the new value.
+func AddInt64(addr *int64, delta int64) (new int64)
-// AddUint64 atomically adds delta to *val and returns the new value.
-func AddUint64(val *uint64, delta uint64) (new uint64)
+// AddUint64 atomically adds delta to *addr and returns the new value.
+func AddUint64(addr *uint64, delta uint64) (new uint64)
-// AddUintptr atomically adds delta to *val and returns the new value.
-func AddUintptr(val *uintptr, delta uintptr) (new uintptr)
+// AddUintptr atomically adds delta to *addr and returns the new value.
+func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
// LoadInt32 atomically loads *addr.
func LoadInt32(addr *int32) (val int32)
diff --git a/src/pkg/sync/atomic/race.go b/src/pkg/sync/atomic/race.go
new file mode 100644
index 000000000..242bbf298
--- /dev/null
+++ b/src/pkg/sync/atomic/race.go
@@ -0,0 +1,213 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build race
+
+package atomic
+
+import (
+ "runtime"
+ "unsafe"
+)
+
+// We use runtime.RaceRead() inside of atomic operations to catch races
+// between atomic and non-atomic operations. It will also catch races
+// between Mutex.Lock() and mutex overwrite (mu = Mutex{}). Since we use
+// only RaceRead() we won't catch races with non-atomic loads.
+// Otherwise (if we use RaceWrite()) we will report races
+// between atomic operations (false positives).
+
+var mtx uint32 = 1 // same for all
+
+func CompareAndSwapInt32(val *int32, old, new int32) bool {
+ return CompareAndSwapUint32((*uint32)(unsafe.Pointer(val)), uint32(old), uint32(new))
+}
+
+func CompareAndSwapUint32(val *uint32, old, new uint32) (swapped bool) {
+ swapped = false
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(val))
+ runtime.RaceAcquire(unsafe.Pointer(val))
+ if *val == old {
+ *val = new
+ swapped = true
+ runtime.RaceReleaseMerge(unsafe.Pointer(val))
+ }
+ runtime.RaceSemrelease(&mtx)
+ return
+}
+
+func CompareAndSwapInt64(val *int64, old, new int64) bool {
+ return CompareAndSwapUint64((*uint64)(unsafe.Pointer(val)), uint64(old), uint64(new))
+}
+
+func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool) {
+ swapped = false
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(val))
+ runtime.RaceAcquire(unsafe.Pointer(val))
+ if *val == old {
+ *val = new
+ swapped = true
+ runtime.RaceReleaseMerge(unsafe.Pointer(val))
+ }
+ runtime.RaceSemrelease(&mtx)
+ return
+}
+
+func CompareAndSwapPointer(val *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) {
+ swapped = false
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(val))
+ runtime.RaceAcquire(unsafe.Pointer(val))
+ if *val == old {
+ *val = new
+ swapped = true
+ runtime.RaceReleaseMerge(unsafe.Pointer(val))
+ }
+ runtime.RaceSemrelease(&mtx)
+ return
+}
+
+func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool) {
+ swapped = false
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(val))
+ runtime.RaceAcquire(unsafe.Pointer(val))
+ if *val == old {
+ *val = new
+ swapped = true
+ runtime.RaceReleaseMerge(unsafe.Pointer(val))
+ }
+ runtime.RaceSemrelease(&mtx)
+ return
+}
+
+func AddInt32(val *int32, delta int32) int32 {
+ return int32(AddUint32((*uint32)(unsafe.Pointer(val)), uint32(delta)))
+}
+
+func AddUint32(val *uint32, delta uint32) (new uint32) {
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(val))
+ runtime.RaceAcquire(unsafe.Pointer(val))
+ *val = *val + delta
+ new = *val
+ runtime.RaceReleaseMerge(unsafe.Pointer(val))
+ runtime.RaceSemrelease(&mtx)
+
+ return
+}
+
+func AddInt64(val *int64, delta int64) int64 {
+ return int64(AddUint64((*uint64)(unsafe.Pointer(val)), uint64(delta)))
+}
+
+func AddUint64(val *uint64, delta uint64) (new uint64) {
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(val))
+ runtime.RaceAcquire(unsafe.Pointer(val))
+ *val = *val + delta
+ new = *val
+ runtime.RaceReleaseMerge(unsafe.Pointer(val))
+ runtime.RaceSemrelease(&mtx)
+
+ return
+}
+
+func AddUintptr(val *uintptr, delta uintptr) (new uintptr) {
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(val))
+ runtime.RaceAcquire(unsafe.Pointer(val))
+ *val = *val + delta
+ new = *val
+ runtime.RaceReleaseMerge(unsafe.Pointer(val))
+ runtime.RaceSemrelease(&mtx)
+
+ return
+}
+
+func LoadInt32(addr *int32) int32 {
+ return int32(LoadUint32((*uint32)(unsafe.Pointer(addr))))
+}
+
+func LoadUint32(addr *uint32) (val uint32) {
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(addr))
+ runtime.RaceAcquire(unsafe.Pointer(addr))
+ val = *addr
+ runtime.RaceSemrelease(&mtx)
+ return
+}
+
+func LoadInt64(addr *int64) int64 {
+ return int64(LoadUint64((*uint64)(unsafe.Pointer(addr))))
+}
+
+func LoadUint64(addr *uint64) (val uint64) {
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(addr))
+ runtime.RaceAcquire(unsafe.Pointer(addr))
+ val = *addr
+ runtime.RaceSemrelease(&mtx)
+ return
+}
+
+func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) {
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(addr))
+ runtime.RaceAcquire(unsafe.Pointer(addr))
+ val = *addr
+ runtime.RaceSemrelease(&mtx)
+ return
+}
+
+func LoadUintptr(addr *uintptr) (val uintptr) {
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(addr))
+ runtime.RaceAcquire(unsafe.Pointer(addr))
+ val = *addr
+ runtime.RaceSemrelease(&mtx)
+ return
+}
+
+func StoreInt32(addr *int32, val int32) {
+ StoreUint32((*uint32)(unsafe.Pointer(addr)), uint32(val))
+}
+
+func StoreUint32(addr *uint32, val uint32) {
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(addr))
+ *addr = val
+ runtime.RaceRelease(unsafe.Pointer(addr))
+ runtime.RaceSemrelease(&mtx)
+}
+
+func StoreInt64(addr *int64, val int64) {
+ StoreUint64((*uint64)(unsafe.Pointer(addr)), uint64(val))
+}
+
+func StoreUint64(addr *uint64, val uint64) {
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(addr))
+ *addr = val
+ runtime.RaceRelease(unsafe.Pointer(addr))
+ runtime.RaceSemrelease(&mtx)
+}
+
+func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) {
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(addr))
+ *addr = val
+ runtime.RaceRelease(unsafe.Pointer(addr))
+ runtime.RaceSemrelease(&mtx)
+}
+
+func StoreUintptr(addr *uintptr, val uintptr) {
+ runtime.RaceSemacquire(&mtx)
+ runtime.RaceRead(unsafe.Pointer(addr))
+ *addr = val
+ runtime.RaceRelease(unsafe.Pointer(addr))
+ runtime.RaceSemrelease(&mtx)
+}
diff --git a/src/pkg/sync/cond.go b/src/pkg/sync/cond.go
index 1fc3deaf1..491b98569 100644
--- a/src/pkg/sync/cond.go
+++ b/src/pkg/sync/cond.go
@@ -56,6 +56,9 @@ func NewCond(l Locker) *Cond {
// c.L.Unlock()
//
func (c *Cond) Wait() {
+ if raceenabled {
+ raceDisable()
+ }
c.m.Lock()
if c.newSema == nil {
c.newSema = new(uint32)
@@ -63,6 +66,9 @@ func (c *Cond) Wait() {
s := c.newSema
c.newWaiters++
c.m.Unlock()
+ if raceenabled {
+ raceEnable()
+ }
c.L.Unlock()
runtime_Semacquire(s)
c.L.Lock()
@@ -73,6 +79,9 @@ func (c *Cond) Wait() {
// It is allowed but not required for the caller to hold c.L
// during the call.
func (c *Cond) Signal() {
+ if raceenabled {
+ raceDisable()
+ }
c.m.Lock()
if c.oldWaiters == 0 && c.newWaiters > 0 {
// Retire old generation; rename new to old.
@@ -86,6 +95,9 @@ func (c *Cond) Signal() {
runtime_Semrelease(c.oldSema)
}
c.m.Unlock()
+ if raceenabled {
+ raceEnable()
+ }
}
// Broadcast wakes all goroutines waiting on c.
@@ -93,6 +105,9 @@ func (c *Cond) Signal() {
// It is allowed but not required for the caller to hold c.L
// during the call.
func (c *Cond) Broadcast() {
+ if raceenabled {
+ raceDisable()
+ }
c.m.Lock()
// Wake both generations.
if c.oldWaiters > 0 {
@@ -109,4 +124,7 @@ func (c *Cond) Broadcast() {
c.newSema = nil
}
c.m.Unlock()
+ if raceenabled {
+ raceEnable()
+ }
}
diff --git a/src/pkg/sync/example_test.go b/src/pkg/sync/example_test.go
index 156492400..031c87f03 100644
--- a/src/pkg/sync/example_test.go
+++ b/src/pkg/sync/example_test.go
@@ -24,10 +24,10 @@ func ExampleWaitGroup() {
wg.Add(1)
// Launch a goroutine to fetch the URL.
go func(url string) {
+ // Decrement the counter when the goroutine completes.
+ defer wg.Done()
// Fetch the URL.
http.Get(url)
- // Decrement the counter.
- wg.Done()
}(url)
}
// Wait for all HTTP fetches to complete.
@@ -37,7 +37,7 @@ func ExampleWaitGroup() {
func ExampleOnce() {
var once sync.Once
onceBody := func() {
- fmt.Printf("Only once\n")
+ fmt.Println("Only once")
}
done := make(chan bool)
for i := 0; i < 10; i++ {
diff --git a/src/pkg/sync/mutex.go b/src/pkg/sync/mutex.go
index 9494cc3f8..b4629ebca 100644
--- a/src/pkg/sync/mutex.go
+++ b/src/pkg/sync/mutex.go
@@ -10,7 +10,10 @@
// Values containing the types defined in this package should not be copied.
package sync
-import "sync/atomic"
+import (
+ "sync/atomic"
+ "unsafe"
+)
// A Mutex is a mutual exclusion lock.
// Mutexes can be created as part of other structures;
@@ -38,6 +41,9 @@ const (
func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
+ if raceenabled {
+ raceAcquire(unsafe.Pointer(m))
+ }
return
}
@@ -61,6 +67,10 @@ func (m *Mutex) Lock() {
awoke = true
}
}
+
+ if raceenabled {
+ raceAcquire(unsafe.Pointer(m))
+ }
}
// Unlock unlocks m.
@@ -70,6 +80,10 @@ func (m *Mutex) Lock() {
// It is allowed for one goroutine to lock a Mutex and then
// arrange for another goroutine to unlock it.
func (m *Mutex) Unlock() {
+ if raceenabled {
+ raceRelease(unsafe.Pointer(m))
+ }
+
// Fast path: drop lock bit.
new := atomic.AddInt32(&m.state, -mutexLocked)
if (new+mutexLocked)&mutexLocked == 0 {
diff --git a/src/pkg/sync/once.go b/src/pkg/sync/once.go
index 04b714a3e..1699e86a9 100644
--- a/src/pkg/sync/once.go
+++ b/src/pkg/sync/once.go
@@ -38,6 +38,6 @@ func (o *Once) Do(f func()) {
defer o.m.Unlock()
if o.done == 0 {
f()
- atomic.CompareAndSwapUint32(&o.done, 0, 1)
+ atomic.StoreUint32(&o.done, 1)
}
}
diff --git a/src/pkg/sync/once_test.go b/src/pkg/sync/once_test.go
index 37075af17..183069a1a 100644
--- a/src/pkg/sync/once_test.go
+++ b/src/pkg/sync/once_test.go
@@ -17,8 +17,11 @@ func (o *one) Increment() {
*o++
}
-func run(once *Once, o *one, c chan bool) {
+func run(t *testing.T, once *Once, o *one, c chan bool) {
once.Do(func() { o.Increment() })
+ if v := *o; v != 1 {
+ t.Errorf("once failed inside run: %d is not 1", v)
+ }
c <- true
}
@@ -28,14 +31,34 @@ func TestOnce(t *testing.T) {
c := make(chan bool)
const N = 10
for i := 0; i < N; i++ {
- go run(once, o, c)
+ go run(t, once, o, c)
}
for i := 0; i < N; i++ {
<-c
}
if *o != 1 {
- t.Errorf("once failed: %d is not 1", *o)
+ t.Errorf("once failed outside run: %d is not 1", *o)
+ }
+}
+
+func TestOncePanic(t *testing.T) {
+ once := new(Once)
+ for i := 0; i < 2; i++ {
+ func() {
+ defer func() {
+ if recover() == nil {
+ t.Fatalf("Once.Do() has not panic'ed")
+ }
+ }()
+ once.Do(func() {
+ panic("failed")
+ })
+ }()
}
+ once.Do(func() {})
+ once.Do(func() {
+ t.Fatalf("Once called twice")
+ })
}
func BenchmarkOnce(b *testing.B) {
diff --git a/src/pkg/sync/race.go b/src/pkg/sync/race.go
new file mode 100644
index 000000000..d9431af6f
--- /dev/null
+++ b/src/pkg/sync/race.go
@@ -0,0 +1,34 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build race
+
+package sync
+
+import (
+ "runtime"
+ "unsafe"
+)
+
+const raceenabled = true
+
+func raceAcquire(addr unsafe.Pointer) {
+ runtime.RaceAcquire(addr)
+}
+
+func raceRelease(addr unsafe.Pointer) {
+ runtime.RaceRelease(addr)
+}
+
+func raceReleaseMerge(addr unsafe.Pointer) {
+ runtime.RaceReleaseMerge(addr)
+}
+
+func raceDisable() {
+ runtime.RaceDisable()
+}
+
+func raceEnable() {
+ runtime.RaceEnable()
+}
diff --git a/src/pkg/sync/race0.go b/src/pkg/sync/race0.go
new file mode 100644
index 000000000..bef14f974
--- /dev/null
+++ b/src/pkg/sync/race0.go
@@ -0,0 +1,28 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !race
+
+package sync
+
+import (
+ "unsafe"
+)
+
+const raceenabled = false
+
+func raceAcquire(addr unsafe.Pointer) {
+}
+
+func raceRelease(addr unsafe.Pointer) {
+}
+
+func raceReleaseMerge(addr unsafe.Pointer) {
+}
+
+func raceDisable() {
+}
+
+func raceEnable() {
+}
diff --git a/src/pkg/sync/rwmutex.go b/src/pkg/sync/rwmutex.go
index 782a9c319..b494c6435 100644
--- a/src/pkg/sync/rwmutex.go
+++ b/src/pkg/sync/rwmutex.go
@@ -4,7 +4,10 @@
package sync
-import "sync/atomic"
+import (
+ "sync/atomic"
+ "unsafe"
+)
// An RWMutex is a reader/writer mutual exclusion lock.
// The lock can be held by an arbitrary number of readers
@@ -24,10 +27,17 @@ const rwmutexMaxReaders = 1 << 30
// RLock locks rw for reading.
func (rw *RWMutex) RLock() {
+ if raceenabled {
+ raceDisable()
+ }
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// A writer is pending, wait for it.
runtime_Semacquire(&rw.readerSem)
}
+ if raceenabled {
+ raceEnable()
+ raceAcquire(unsafe.Pointer(&rw.readerSem))
+ }
}
// RUnlock undoes a single RLock call;
@@ -35,6 +45,10 @@ func (rw *RWMutex) RLock() {
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) RUnlock() {
+ if raceenabled {
+ raceReleaseMerge(unsafe.Pointer(&rw.writerSem))
+ raceDisable()
+ }
if atomic.AddInt32(&rw.readerCount, -1) < 0 {
// A writer is pending.
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
@@ -42,6 +56,9 @@ func (rw *RWMutex) RUnlock() {
runtime_Semrelease(&rw.writerSem)
}
}
+ if raceenabled {
+ raceEnable()
+ }
}
// Lock locks rw for writing.
@@ -51,6 +68,9 @@ func (rw *RWMutex) RUnlock() {
// a blocked Lock call excludes new readers from acquiring
// the lock.
func (rw *RWMutex) Lock() {
+ if raceenabled {
+ raceDisable()
+ }
// First, resolve competition with other writers.
rw.w.Lock()
// Announce to readers there is a pending writer.
@@ -59,6 +79,11 @@ func (rw *RWMutex) Lock() {
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_Semacquire(&rw.writerSem)
}
+ if raceenabled {
+ raceEnable()
+ raceAcquire(unsafe.Pointer(&rw.readerSem))
+ raceAcquire(unsafe.Pointer(&rw.writerSem))
+ }
}
// Unlock unlocks rw for writing. It is a run-time error if rw is
@@ -68,6 +93,12 @@ func (rw *RWMutex) Lock() {
// goroutine. One goroutine may RLock (Lock) an RWMutex and then
// arrange for another goroutine to RUnlock (Unlock) it.
func (rw *RWMutex) Unlock() {
+ if raceenabled {
+ raceRelease(unsafe.Pointer(&rw.readerSem))
+ raceRelease(unsafe.Pointer(&rw.writerSem))
+ raceDisable()
+ }
+
// Announce to readers there is no active writer.
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
// Unblock blocked readers, if any.
@@ -76,6 +107,9 @@ func (rw *RWMutex) Unlock() {
}
// Allow other writers to proceed.
rw.w.Unlock()
+ if raceenabled {
+ raceEnable()
+ }
}
// RLocker returns a Locker interface that implements
diff --git a/src/pkg/sync/waitgroup.go b/src/pkg/sync/waitgroup.go
index 0165b1ffb..1277f1c6d 100644
--- a/src/pkg/sync/waitgroup.go
+++ b/src/pkg/sync/waitgroup.go
@@ -4,7 +4,10 @@
package sync
-import "sync/atomic"
+import (
+ "sync/atomic"
+ "unsafe"
+)
// A WaitGroup waits for a collection of goroutines to finish.
// The main goroutine calls Add to set the number of
@@ -31,11 +34,22 @@ type WaitGroup struct {
// G3: Wait() // G1 still hasn't run, G3 finds sema == 1, unblocked! Bug.
// Add adds delta, which may be negative, to the WaitGroup counter.
-// If the counter becomes zero, all goroutines blocked on Wait() are released.
+// If the counter becomes zero, all goroutines blocked on Wait are released.
+// If the counter goes negative, Add panics.
+//
+// Note that calls with positive delta must happen before the call to Wait,
+// or else Wait may wait for too small a group. Typically this means the calls
+// to Add should execute before the statement creating the goroutine or
+// other event to be waited for. See the WaitGroup example.
func (wg *WaitGroup) Add(delta int) {
+ if raceenabled {
+ raceReleaseMerge(unsafe.Pointer(wg))
+ raceDisable()
+ defer raceEnable()
+ }
v := atomic.AddInt32(&wg.counter, int32(delta))
if v < 0 {
- panic("sync: negative WaitGroup count")
+ panic("sync: negative WaitGroup counter")
}
if v > 0 || atomic.LoadInt32(&wg.waiters) == 0 {
return
@@ -56,7 +70,14 @@ func (wg *WaitGroup) Done() {
// Wait blocks until the WaitGroup counter is zero.
func (wg *WaitGroup) Wait() {
+ if raceenabled {
+ raceDisable()
+ }
if atomic.LoadInt32(&wg.counter) == 0 {
+ if raceenabled {
+ raceEnable()
+ raceAcquire(unsafe.Pointer(wg))
+ }
return
}
wg.m.Lock()
@@ -67,7 +88,15 @@ func (wg *WaitGroup) Wait() {
// to avoid missing an Add.
if atomic.LoadInt32(&wg.counter) == 0 {
atomic.AddInt32(&wg.waiters, -1)
+ if raceenabled {
+ raceEnable()
+ raceAcquire(unsafe.Pointer(wg))
+ raceDisable()
+ }
wg.m.Unlock()
+ if raceenabled {
+ raceEnable()
+ }
return
}
if wg.sema == nil {
@@ -76,4 +105,8 @@ func (wg *WaitGroup) Wait() {
s := wg.sema
wg.m.Unlock()
runtime_Semacquire(s)
+ if raceenabled {
+ raceEnable()
+ raceAcquire(unsafe.Pointer(wg))
+ }
}
diff --git a/src/pkg/sync/waitgroup_test.go b/src/pkg/sync/waitgroup_test.go
index 34430fc21..84c4cfc37 100644
--- a/src/pkg/sync/waitgroup_test.go
+++ b/src/pkg/sync/waitgroup_test.go
@@ -50,7 +50,7 @@ func TestWaitGroup(t *testing.T) {
func TestWaitGroupMisuse(t *testing.T) {
defer func() {
err := recover()
- if err != "sync: negative WaitGroup count" {
+ if err != "sync: negative WaitGroup counter" {
t.Fatalf("Unexpected panic: %#v", err)
}
}()
diff --git a/src/pkg/syscall/asm_freebsd_amd64.s b/src/pkg/syscall/asm_freebsd_amd64.s
index 022db697a..fbf917804 100644
--- a/src/pkg/syscall/asm_freebsd_amd64.s
+++ b/src/pkg/syscall/asm_freebsd_amd64.s
@@ -8,6 +8,7 @@
// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64);
// func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64);
+// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64)
// Trap # in AX, args in DI SI DX, return in AX DX
TEXT ·Syscall(SB),7,$0
@@ -56,6 +57,39 @@ ok6:
CALL runtime·exitsyscall(SB)
RET
+TEXT ·Syscall9(SB),7,$0
+ CALL runtime·entersyscall(SB)
+ MOVQ 8(SP), AX
+ MOVQ 16(SP), DI
+ MOVQ 24(SP), SI
+ MOVQ 32(SP), DX
+ MOVQ 40(SP), R10
+ MOVQ 48(SP), R8
+ MOVQ 56(SP), R9
+
+ // shift around the last three arguments so they're at the
+ // top of the stack when the syscall is called.
+ MOVQ 64(SP), R11 // arg 7
+ MOVQ R11, 8(SP)
+ MOVQ 72(SP), R11 // arg 8
+ MOVQ R11, 16(SP)
+ MOVQ 80(SP), R11 // arg 9
+ MOVQ R11, 24(SP)
+
+ SYSCALL
+ JCC ok9
+ MOVQ $-1, 88(SP) // r1
+ MOVQ $0, 96(SP) // r2
+ MOVQ AX, 104(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+ok9:
+ MOVQ AX, 88(SP) // r1
+ MOVQ DX, 96(SP) // r2
+ MOVQ $0, 104(SP) // errno
+ CALL runtime·exitsyscall(SB)
+ RET
+
TEXT ·RawSyscall(SB),7,$0
MOVQ 16(SP), DI
MOVQ 24(SP), SI
diff --git a/src/pkg/syscall/asm_freebsd_arm.s b/src/pkg/syscall/asm_freebsd_arm.s
new file mode 100644
index 000000000..ab08ab5f5
--- /dev/null
+++ b/src/pkg/syscall/asm_freebsd_arm.s
@@ -0,0 +1,124 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//
+// System call support for ARM, FreeBSD
+//
+
+// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32);
+// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
+// func Syscall9(trap int32, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int32)
+
+TEXT ·Syscall(SB),7,$0
+ BL runtime·entersyscall(SB)
+ MOVW 0(FP), R0 // sigcall num
+ MOVW 4(FP), R1 // a1
+ MOVW 8(FP), R2 // a2
+ MOVW 12(FP), R3 // a3
+ SWI $0 // syscall
+ MOVW $0, R2
+ BCS error
+ MOVW R0, 16(FP) // r1
+ MOVW R1, 20(FP) // r2
+ MOVW R2, 24(FP) // err
+ BL runtime·exitsyscall(SB)
+ RET
+error:
+ MOVW $-1, R3
+ MOVW R3, 16(FP) // r1
+ MOVW R2, 20(FP) // r2
+ MOVW R0, 24(FP) // err
+ BL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·Syscall6(SB),7,$0
+ BL runtime·entersyscall(SB)
+ MOVW 0(FP), R0 // sigcall num
+ MOVW 4(FP), R1 // a1
+ MOVW 8(FP), R2 // a2
+ MOVW 12(FP), R3 // a3
+ MOVW R13, R4
+ MOVW $16(FP), R13 // a4 to a6 are passed on stack
+ SWI $0 // syscall
+ MOVW R4, R13
+ MOVW $0, R2
+ BCS error6
+ MOVW R0, 28(FP) // r1
+ MOVW R1, 32(FP) // r2
+ MOVW R2, 36(FP) // err
+ BL runtime·exitsyscall(SB)
+ RET
+error6:
+ MOVW $-1, R3
+ MOVW R3, 28(FP) // r1
+ MOVW R2, 32(FP) // r2
+ MOVW R0, 36(FP) // err
+ BL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·Syscall9(SB),7,$0
+ BL runtime·entersyscall(SB)
+ MOVW 0(FP), R0 // sigcall num
+ MOVW 4(FP), R1 // a1
+ MOVW 8(FP), R2 // a2
+ MOVW 12(FP), R3 // a3
+ MOVW R13, R4
+ MOVW $16(FP), R13 // a4 to a9 are passed on stack
+ SWI $0 // syscall
+ MOVW R4, R13
+ MOVW $0, R2
+ BCS error9
+ MOVW R0, 40(FP) // r1
+ MOVW R1, 44(FP) // r2
+ MOVW R2, 48(FP) // err
+ BL runtime·exitsyscall(SB)
+ RET
+error9:
+ MOVW $-1, R3
+ MOVW R3, 40(FP) // r1
+ MOVW R2, 44(FP) // r2
+ MOVW R0, 48(FP) // err
+ BL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·RawSyscall(SB),7,$0
+ MOVW 0(FP), R0 // sigcall num
+ MOVW 4(FP), R1 // a1
+ MOVW 8(FP), R2 // a2
+ MOVW 12(FP), R3 // a3
+ SWI $0 // syscall
+ MOVW $0, R2
+ BCS errorr
+ MOVW R0, 16(FP) // r1
+ MOVW R1, 20(FP) // r2
+ MOVW R2, 24(FP) // err
+ RET
+errorr:
+ MOVW $-1, R3
+ MOVW R3, 16(FP) // r1
+ MOVW R2, 20(FP) // r2
+ MOVW R0, 24(FP) // err
+ RET
+
+TEXT ·RawSyscall6(SB),7,$0
+ MOVW 0(FP), R0 // sigcall num
+ MOVW 4(FP), R1 // a1
+ MOVW 8(FP), R2 // a2
+ MOVW 12(FP), R3 // a3
+ MOVW R13, R4
+ MOVW $16(FP), R13 // a4 to a9 are passed on stack
+ SWI $0 // syscall
+ MOVW R4, R13
+ MOVW $0, R2
+ BCS errorr6
+ MOVW R0, 28(FP) // r1
+ MOVW R1, 32(FP) // r2
+ MOVW R2, 36(FP) // err
+ RET
+errorr6:
+ MOVW $-1, R3
+ MOVW R3, 28(FP) // r1
+ MOVW R2, 32(FP) // r2
+ MOVW R0, 36(FP) // err
+ RET
diff --git a/src/pkg/syscall/asm_linux_amd64.s b/src/pkg/syscall/asm_linux_amd64.s
index fdc233ca5..1a1fdb06a 100644
--- a/src/pkg/syscall/asm_linux_amd64.s
+++ b/src/pkg/syscall/asm_linux_amd64.s
@@ -108,7 +108,7 @@ ok2:
TEXT ·Gettimeofday(SB),7,$0
MOVQ 8(SP), DI
MOVQ $0, SI
- MOVQ $0xffffffffff600000, AX
+ MOVQ runtime·__vdso_gettimeofday_sym(SB), AX
CALL AX
CMPQ AX, $0xfffffffffffff001
@@ -122,7 +122,7 @@ ok7:
TEXT ·Time(SB),7,$0
MOVQ 8(SP), DI
- MOVQ $0xffffffffff600400, AX
+ MOVQ runtime·__vdso_time_sym(SB), AX
CALL AX
MOVQ AX, 16(SP) // tt
MOVQ $0, 24(SP) // errno
diff --git a/src/pkg/syscall/asm_netbsd_arm.s b/src/pkg/syscall/asm_netbsd_arm.s
new file mode 100644
index 000000000..e42da334a
--- /dev/null
+++ b/src/pkg/syscall/asm_netbsd_arm.s
@@ -0,0 +1,124 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//
+// System call support for ARM, NetBSD
+//
+
+// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32);
+// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
+// func Syscall9(trap int32, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int32)
+
+TEXT ·Syscall(SB),7,$0
+ BL runtime·entersyscall(SB)
+ MOVW 0(FP), R0 // sigcall num
+ MOVW 4(FP), R1 // a1
+ MOVW 8(FP), R2 // a2
+ MOVW 12(FP), R3 // a3
+ SWI $0 // syscall
+ MOVW $0, R2
+ BCS error
+ MOVW R0, 16(FP) // r1
+ MOVW R1, 20(FP) // r2
+ MOVW R2, 24(FP) // err
+ BL runtime·exitsyscall(SB)
+ RET
+error:
+ MOVW $-1, R3
+ MOVW R3, 16(FP) // r1
+ MOVW R2, 20(FP) // r2
+ MOVW R0, 24(FP) // err
+ BL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·Syscall6(SB),7,$0
+ BL runtime·entersyscall(SB)
+ MOVW 0(FP), R0 // sigcall num
+ MOVW 4(FP), R1 // a1
+ MOVW 8(FP), R2 // a2
+ MOVW 12(FP), R3 // a3
+ MOVW R13, R4
+ MOVW $16(FP), R13 // a4 to a6 are passed on stack
+ SWI $0 // syscall
+ MOVW R4, R13
+ MOVW $0, R2
+ BCS error6
+ MOVW R0, 28(FP) // r1
+ MOVW R1, 32(FP) // r2
+ MOVW R2, 36(FP) // err
+ BL runtime·exitsyscall(SB)
+ RET
+error6:
+ MOVW $-1, R3
+ MOVW R3, 28(FP) // r1
+ MOVW R2, 32(FP) // r2
+ MOVW R0, 36(FP) // err
+ BL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·Syscall9(SB),7,$0
+ BL runtime·entersyscall(SB)
+ MOVW 0(FP), R0 // sigcall num
+ MOVW 4(FP), R1 // a1
+ MOVW 8(FP), R2 // a2
+ MOVW 12(FP), R3 // a3
+ MOVW R13, R4
+ MOVW $16(FP), R13 // a4 to a9 are passed on stack
+ SWI $0 // syscall
+ MOVW R4, R13
+ MOVW $0, R2
+ BCS error9
+ MOVW R0, 40(FP) // r1
+ MOVW R1, 44(FP) // r2
+ MOVW R2, 48(FP) // err
+ BL runtime·exitsyscall(SB)
+ RET
+error9:
+ MOVW $-1, R3
+ MOVW R3, 40(FP) // r1
+ MOVW R2, 44(FP) // r2
+ MOVW R0, 48(FP) // err
+ BL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·RawSyscall(SB),7,$0
+ MOVW 0(FP), R0 // sigcall num
+ MOVW 4(FP), R1 // a1
+ MOVW 8(FP), R2 // a2
+ MOVW 12(FP), R3 // a3
+ SWI $0 // syscall
+ MOVW $0, R2
+ BCS errorr
+ MOVW R0, 16(FP) // r1
+ MOVW R1, 20(FP) // r2
+ MOVW R2, 24(FP) // err
+ RET
+errorr:
+ MOVW $-1, R3
+ MOVW R3, 16(FP) // r1
+ MOVW R2, 20(FP) // r2
+ MOVW R0, 24(FP) // err
+ RET
+
+TEXT ·RawSyscall6(SB),7,$0
+ MOVW 0(FP), R0 // sigcall num
+ MOVW 4(FP), R1 // a1
+ MOVW 8(FP), R2 // a2
+ MOVW 12(FP), R3 // a3
+ MOVW R13, R4
+ MOVW $16(FP), R13 // a4 to a9 are passed on stack
+ SWI $0 // syscall
+ MOVW R4, R13
+ MOVW $0, R2
+ BCS errorr6
+ MOVW R0, 28(FP) // r1
+ MOVW R1, 32(FP) // r2
+ MOVW R2, 36(FP) // err
+ RET
+errorr6:
+ MOVW $-1, R3
+ MOVW R3, 28(FP) // r1
+ MOVW R2, 32(FP) // r2
+ MOVW R0, 36(FP) // err
+ RET
diff --git a/src/pkg/syscall/asm_plan9_386.s b/src/pkg/syscall/asm_plan9_386.s
index 86ebedccc..2ba137608 100644
--- a/src/pkg/syscall/asm_plan9_386.s
+++ b/src/pkg/syscall/asm_plan9_386.s
@@ -149,3 +149,11 @@ copyresult6:
MOVSL
MOVSL
RET
+
+//func exit(code int)
+// Import runtime·exit for cleanly exiting.
+TEXT ·exit(SB),7,$4
+ MOVL code+0(FP), AX
+ MOVL AX, 0(SP)
+ CALL runtime·exit(SB)
+ RET
diff --git a/src/pkg/syscall/asm_plan9_amd64.s b/src/pkg/syscall/asm_plan9_amd64.s
new file mode 100644
index 000000000..e363b743c
--- /dev/null
+++ b/src/pkg/syscall/asm_plan9_amd64.s
@@ -0,0 +1,165 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//
+// System call support for Plan 9
+//
+
+//func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err string)
+//func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err string)
+//func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
+//func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
+
+// Trap # in BP, args on stack above caller pc.
+// NxM requires that Plan 9 system calls be
+// marked with $0x8000 in AX.
+TEXT ·Syscall(SB),7,$0
+ CALL runtime·entersyscall(SB)
+ MOVQ $0x8000, AX // for NxM
+ MOVQ 8(SP), BP // syscall entry
+ // slide args down on top of system call number
+ LEAQ 16(SP), SI
+ LEAQ 8(SP), DI
+ CLD
+ MOVSQ
+ MOVSQ
+ MOVSQ
+ SYSCALL
+ MOVQ AX, r1+40(SP)
+ MOVQ $0, r2+48(SP)
+ CMPL AX, $-1
+ JNE ok3
+
+ SUBQ $16, SP
+ CALL syscall·errstr(SB)
+ MOVQ SP, SI
+ ADDQ $16, SP
+ JMP copyresult3
+
+ok3:
+ LEAQ runtime·emptystring(SB), SI
+
+copyresult3:
+ LEAQ err+56(SP), DI
+
+ CLD
+ MOVSQ
+ MOVSQ
+
+ CALL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·Syscall6(SB),7,$0
+ CALL runtime·entersyscall(SB)
+ MOVQ $0x8000, AX // for NxM
+ MOVQ 8(SP), BP // syscall entry
+ // slide args down on top of system call number
+ LEAQ 16(SP), SI
+ LEAQ 8(SP), DI
+ CLD
+ MOVSQ
+ MOVSQ
+ MOVSQ
+ MOVSQ
+ MOVSQ
+ MOVSQ
+ SYSCALL
+ MOVQ AX, r1+64(SP)
+ MOVQ $0, r2+72(SP)
+ CMPL AX, $-1
+ JNE ok4
+
+ SUBQ $16, SP
+ CALL syscall·errstr(SB)
+ MOVQ SP, SI
+ ADDQ $16, SP
+ JMP copyresult4
+
+ok4:
+ LEAQ runtime·emptystring(SB), SI
+
+copyresult4:
+ LEAQ err+80(SP), DI
+
+ CLD
+ MOVSQ
+ MOVSQ
+
+ CALL runtime·exitsyscall(SB)
+ RET
+
+TEXT ·RawSyscall(SB),7,$0
+ MOVQ $0x8000, AX // for NxM
+ MOVQ 8(SP), BP // syscall entry
+ // slide args down on top of system call number
+ LEAQ 16(SP), SI
+ LEAQ 8(SP), DI
+ CLD
+ MOVSQ
+ MOVSQ
+ MOVSQ
+ SYSCALL
+ MOVQ AX, r1+40(SP)
+ MOVQ AX, r2+48(SP)
+ MOVQ AX, err+56(SP)
+ RET
+
+TEXT ·RawSyscall6(SB),7,$0
+ MOVQ $0x8000, AX // for NxM
+ MOVQ 8(SP), BP // syscall entry
+ // slide args down on top of system call number
+ LEAQ 16(SP), SI
+ LEAQ 8(SP), DI
+ CLD
+ MOVSQ
+ MOVSQ
+ MOVSQ
+ MOVSQ
+ MOVSQ
+ MOVSQ
+ SYSCALL
+ MOVQ AX, r1+64(SP)
+ MOVQ AX, r2+72(SP)
+ MOVQ AX, err+80(SP)
+ RET
+
+#define SYS_SEEK 39 /* from zsysnum_plan9_amd64.go */
+
+//func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
+TEXT ·seek(SB),7,$0
+ LEAQ newoffset+40(SP), AX
+ MOVQ AX, placeholder+8(SP)
+
+ MOVQ $0x8000, AX // for NxM
+ MOVQ $SYS_SEEK, BP // syscall entry
+ SYSCALL
+
+ CMPL AX, $-1
+ JNE ok6
+ MOVQ $-1, newoffset+40(SP)
+
+ SUBQ $16, SP
+ CALL syscall·errstr(SB)
+ MOVQ SP, SI
+ ADDQ $16, SP
+ JMP copyresult6
+
+ok6:
+ LEAQ runtime·emptystring(SB), SI
+
+copyresult6:
+ LEAQ err+48(SP), DI
+
+ CLD
+ MOVSQ
+ MOVSQ
+ RET
+
+//func exit(code int)
+// Import runtime·exit for cleanly exiting.
+TEXT ·exit(SB),7,$8
+ MOVQ code+0(FP), AX
+ MOVQ AX, 0(SP)
+ CALL runtime·exit(SB)
+ RET
diff --git a/src/pkg/syscall/asm_windows_386.s b/src/pkg/syscall/asm_windows_386.s
index a7b95643d..8b52fa985 100644
--- a/src/pkg/syscall/asm_windows_386.s
+++ b/src/pkg/syscall/asm_windows_386.s
@@ -3,5 +3,5 @@
// license that can be found in the LICENSE file.
//
-// System calls for 386, Windows are implemented in ../runtime/windows/syscall.goc
+// System calls for 386, Windows are implemented in ../runtime/syscall_windows.goc
//
diff --git a/src/pkg/syscall/asm_windows_amd64.s b/src/pkg/syscall/asm_windows_amd64.s
index 8b38710c7..5813404d1 100644
--- a/src/pkg/syscall/asm_windows_amd64.s
+++ b/src/pkg/syscall/asm_windows_amd64.s
@@ -3,5 +3,5 @@
// license that can be found in the LICENSE file.
//
-// System calls for amd64, Windows are implemented in ../runtime/windows/syscall.goc
+// System calls for amd64, Windows are implemented in ../runtime/syscall_windows.goc
//
diff --git a/src/pkg/syscall/creds_test.go b/src/pkg/syscall/creds_test.go
new file mode 100644
index 000000000..b1894c66b
--- /dev/null
+++ b/src/pkg/syscall/creds_test.go
@@ -0,0 +1,113 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+
+package syscall_test
+
+import (
+ "bytes"
+ "net"
+ "os"
+ "syscall"
+ "testing"
+)
+
+// TestSCMCredentials tests the sending and receiving of credentials
+// (PID, UID, GID) in an ancillary message between two UNIX
+// sockets. The SO_PASSCRED socket option is enabled on the sending
+// socket for this to work.
+func TestSCMCredentials(t *testing.T) {
+ fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0)
+ if err != nil {
+ t.Fatalf("Socketpair: %v", err)
+ }
+ defer syscall.Close(fds[0])
+ defer syscall.Close(fds[1])
+
+ err = syscall.SetsockoptInt(fds[0], syscall.SOL_SOCKET, syscall.SO_PASSCRED, 1)
+ if err != nil {
+ t.Fatalf("SetsockoptInt: %v", err)
+ }
+
+ srvFile := os.NewFile(uintptr(fds[0]), "server")
+ defer srvFile.Close()
+ srv, err := net.FileConn(srvFile)
+ if err != nil {
+ t.Errorf("FileConn: %v", err)
+ return
+ }
+ defer srv.Close()
+
+ cliFile := os.NewFile(uintptr(fds[1]), "client")
+ defer cliFile.Close()
+ cli, err := net.FileConn(cliFile)
+ if err != nil {
+ t.Errorf("FileConn: %v", err)
+ return
+ }
+ defer cli.Close()
+
+ var ucred syscall.Ucred
+ if os.Getuid() != 0 {
+ ucred.Pid = int32(os.Getpid())
+ ucred.Uid = 0
+ ucred.Gid = 0
+ oob := syscall.UnixCredentials(&ucred)
+ _, _, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil)
+ if err.(*net.OpError).Err != syscall.EPERM {
+ t.Fatalf("WriteMsgUnix failed with %v, want EPERM", err)
+ }
+ }
+
+ ucred.Pid = int32(os.Getpid())
+ ucred.Uid = uint32(os.Getuid())
+ ucred.Gid = uint32(os.Getgid())
+ oob := syscall.UnixCredentials(&ucred)
+
+ // this is going to send a dummy byte
+ n, oobn, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil)
+ if err != nil {
+ t.Fatalf("WriteMsgUnix: %v", err)
+ }
+ if n != 0 {
+ t.Fatalf("WriteMsgUnix n = %d, want 0", n)
+ }
+ if oobn != len(oob) {
+ t.Fatalf("WriteMsgUnix oobn = %d, want %d", oobn, len(oob))
+ }
+
+ oob2 := make([]byte, 10*len(oob))
+ n, oobn2, flags, _, err := srv.(*net.UnixConn).ReadMsgUnix(nil, oob2)
+ if err != nil {
+ t.Fatalf("ReadMsgUnix: %v", err)
+ }
+ if flags != 0 {
+ t.Fatalf("ReadMsgUnix flags = 0x%x, want 0", flags)
+ }
+ if n != 1 {
+ t.Fatalf("ReadMsgUnix n = %d, want 1 (dummy byte)", n)
+ }
+ if oobn2 != oobn {
+ // without SO_PASSCRED set on the socket, ReadMsgUnix will
+ // return zero oob bytes
+ t.Fatalf("ReadMsgUnix oobn = %d, want %d", oobn2, oobn)
+ }
+ oob2 = oob2[:oobn2]
+ if !bytes.Equal(oob, oob2) {
+ t.Fatal("ReadMsgUnix oob bytes don't match")
+ }
+
+ scm, err := syscall.ParseSocketControlMessage(oob2)
+ if err != nil {
+ t.Fatalf("ParseSocketControlMessage: %v", err)
+ }
+ newUcred, err := syscall.ParseUnixCredentials(&scm[0])
+ if err != nil {
+ t.Fatalf("ParseUnixCredentials: %v", err)
+ }
+ if *newUcred != ucred {
+ t.Fatalf("ParseUnixCredentials = %+v, want %+v", newUcred, ucred)
+ }
+}
diff --git a/src/pkg/syscall/dir_plan9.go b/src/pkg/syscall/dir_plan9.go
new file mode 100644
index 000000000..b7ab4cd10
--- /dev/null
+++ b/src/pkg/syscall/dir_plan9.go
@@ -0,0 +1,205 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Plan 9 directory marshalling. See intro(5).
+
+package syscall
+
+import "errors"
+
+var (
+ ErrShortStat = errors.New("stat buffer too short")
+ ErrBadStat = errors.New("malformed stat buffer")
+)
+
+// A Qid represents a 9P server's unique identification for a file.
+type Qid struct {
+ Path uint64 // the file server's unique identification for the file
+ Vers uint32 // version number for given Path
+ Type uint8 // the type of the file (syscall.QTDIR for example)
+}
+
+// A Dir contains the metadata for a file.
+type Dir struct {
+ // system-modified data
+ Type uint16 // server type
+ Dev uint32 // server subtype
+
+ // file data
+ Qid Qid // unique id from server
+ Mode uint32 // permissions
+ Atime uint32 // last read time
+ Mtime uint32 // last write time
+ Length int64 // file length
+ Name string // last element of path
+ Uid string // owner name
+ Gid string // group name
+ Muid string // last modifier name
+}
+
+var nullDir = Dir{
+ Type: ^uint16(0),
+ Dev: ^uint32(0),
+ Qid: Qid{
+ Path: ^uint64(0),
+ Vers: ^uint32(0),
+ Type: ^uint8(0),
+ },
+ Mode: ^uint32(0),
+ Atime: ^uint32(0),
+ Mtime: ^uint32(0),
+ Length: ^int64(0),
+}
+
+// Null assigns special "don't touch" values to members of d to
+// avoid modifiying them during syscall.Wstat.
+func (d *Dir) Null() { *d = nullDir }
+
+// Marshal encodes a 9P stat message corresponding to d into b
+//
+// If there isn't enough space in b for a stat message, ErrShortStat is returned.
+func (d *Dir) Marshal(b []byte) (n int, err error) {
+ n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
+ if n > len(b) {
+ return n, ErrShortStat
+ }
+
+ b = pbit16(b, uint16(n)-2)
+ b = pbit16(b, d.Type)
+ b = pbit32(b, d.Dev)
+ b = pbit8(b, d.Qid.Type)
+ b = pbit32(b, d.Qid.Vers)
+ b = pbit64(b, d.Qid.Path)
+ b = pbit32(b, d.Mode)
+ b = pbit32(b, d.Atime)
+ b = pbit32(b, d.Mtime)
+ b = pbit64(b, uint64(d.Length))
+ b = pstring(b, d.Name)
+ b = pstring(b, d.Uid)
+ b = pstring(b, d.Gid)
+ b = pstring(b, d.Muid)
+
+ return n, nil
+}
+
+// UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir.
+//
+// If b is too small to hold a valid stat message, ErrShortStat is returned.
+//
+// If the stat message itself is invalid, ErrBadStat is returned.
+func UnmarshalDir(b []byte) (*Dir, error) {
+ if len(b) < STATFIXLEN {
+ return nil, ErrShortStat
+ }
+ size, buf := gbit16(b)
+ if len(b) != int(size)+2 {
+ return nil, ErrBadStat
+ }
+ b = buf
+
+ var d Dir
+ d.Type, b = gbit16(b)
+ d.Dev, b = gbit32(b)
+ d.Qid.Type, b = gbit8(b)
+ d.Qid.Vers, b = gbit32(b)
+ d.Qid.Path, b = gbit64(b)
+ d.Mode, b = gbit32(b)
+ d.Atime, b = gbit32(b)
+ d.Mtime, b = gbit32(b)
+
+ n, b := gbit64(b)
+ d.Length = int64(n)
+
+ var ok bool
+ if d.Name, b, ok = gstring(b); !ok {
+ return nil, ErrBadStat
+ }
+ if d.Uid, b, ok = gstring(b); !ok {
+ return nil, ErrBadStat
+ }
+ if d.Gid, b, ok = gstring(b); !ok {
+ return nil, ErrBadStat
+ }
+ if d.Muid, b, ok = gstring(b); !ok {
+ return nil, ErrBadStat
+ }
+
+ return &d, nil
+}
+
+// pbit8 copies the 8-bit number v to b and returns the remaining slice of b.
+func pbit8(b []byte, v uint8) []byte {
+ b[0] = byte(v)
+ return b[1:]
+}
+
+// pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b.
+func pbit16(b []byte, v uint16) []byte {
+ b[0] = byte(v)
+ b[1] = byte(v >> 8)
+ return b[2:]
+}
+
+// pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b.
+func pbit32(b []byte, v uint32) []byte {
+ b[0] = byte(v)
+ b[1] = byte(v >> 8)
+ b[2] = byte(v >> 16)
+ b[3] = byte(v >> 24)
+ return b[4:]
+}
+
+// pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b.
+func pbit64(b []byte, v uint64) []byte {
+ b[0] = byte(v)
+ b[1] = byte(v >> 8)
+ b[2] = byte(v >> 16)
+ b[3] = byte(v >> 24)
+ b[4] = byte(v >> 32)
+ b[5] = byte(v >> 40)
+ b[6] = byte(v >> 48)
+ b[7] = byte(v >> 56)
+ return b[8:]
+}
+
+// pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and
+// returning the remaining slice of b..
+func pstring(b []byte, s string) []byte {
+ b = pbit16(b, uint16(len(s)))
+ n := copy(b, s)
+ return b[n:]
+}
+
+// gbit8 reads an 8-bit number from b and returns it with the remaining slice of b.
+func gbit8(b []byte) (uint8, []byte) {
+ return uint8(b[0]), b[1:]
+}
+
+// gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b.
+func gbit16(b []byte) (uint16, []byte) {
+ return uint16(b[0]) | uint16(b[1])<<8, b[2:]
+}
+
+// gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b.
+func gbit32(b []byte) (uint32, []byte) {
+ return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
+}
+
+// gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b.
+func gbit64(b []byte) (uint64, []byte) {
+ lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
+ hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24
+ return uint64(lo) | uint64(hi)<<32, b[8:]
+}
+
+// gstring reads a string from b, prefixed with a 16-bit length in little-endian order.
+// It returns the string with the remaining slice of b and a boolean. If the length is
+// greater than the number of bytes in b, the boolean will be false.
+func gstring(b []byte) (string, []byte, bool) {
+ n, b := gbit16(b)
+ if int(n) > len(b) {
+ return "", b, false
+ }
+ return string(b[:n]), b[n:], true
+}
diff --git a/src/pkg/syscall/dll_windows.go b/src/pkg/syscall/dll_windows.go
index 2a45991dd..d29e9921c 100644
--- a/src/pkg/syscall/dll_windows.go
+++ b/src/pkg/syscall/dll_windows.go
@@ -6,6 +6,8 @@ package syscall
import (
"sync"
+ "sync/atomic"
+ "unsafe"
)
// DLLError describes reasons for DLL load failures.
@@ -17,13 +19,13 @@ type DLLError struct {
func (e *DLLError) Error() string { return e.Msg }
-// Implemented in ../runtime/windows/syscall.goc.
+// Implemented in ../runtime/syscall_windows.goc.
func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno)
func Syscall15(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2 uintptr, err Errno)
-func loadlibrary(filename *uint16) (handle, err Errno)
+func loadlibrary(filename *uint16) (handle uintptr, err Errno)
func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)
// A DLL implements access to a single DLL.
@@ -34,7 +36,11 @@ type DLL struct {
// LoadDLL loads DLL file into memory.
func LoadDLL(name string) (dll *DLL, err error) {
- h, e := loadlibrary(StringToUTF16Ptr(name))
+ namep, err := UTF16PtrFromString(name)
+ if err != nil {
+ return nil, err
+ }
+ h, e := loadlibrary(namep)
if e != 0 {
return nil, &DLLError{
Err: e,
@@ -61,7 +67,11 @@ func MustLoadDLL(name string) *DLL {
// FindProc searches DLL d for procedure named name and returns *Proc
// if found. It returns an error if search fails.
func (d *DLL) FindProc(name string) (proc *Proc, err error) {
- a, e := getprocaddress(uintptr(d.Handle), StringBytePtr(name))
+ namep, err := BytePtrFromString(name)
+ if err != nil {
+ return nil, err
+ }
+ a, e := getprocaddress(uintptr(d.Handle), namep)
if e != 0 {
return nil, &DLLError{
Err: e,
@@ -104,8 +114,14 @@ func (p *Proc) Addr() uintptr {
return p.addr
}
-// Call executes procedure p with arguments a.
-func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, err error) {
+// Call executes procedure p with arguments a. It will panic, if more then 15 arguments
+// are supplied.
+//
+// The returned error is always non-nil, constructed from the result of GetLastError.
+// Callers must inspect the primary return value to decide whether an error occurred
+// (according to the semantics of the specific function being called) before consulting
+// the error. The error will be guaranteed to contain syscall.Errno.
+func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
switch len(a) {
case 0:
return Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
@@ -158,7 +174,9 @@ type LazyDLL struct {
// Load loads DLL file d.Name into memory. It returns an error if fails.
// Load will not try to load DLL, if it is already loaded into memory.
func (d *LazyDLL) Load() error {
- if d.dll == nil {
+ // Non-racy version of:
+ // if d.dll == nil {
+ if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) == nil {
d.mu.Lock()
defer d.mu.Unlock()
if d.dll == nil {
@@ -166,7 +184,9 @@ func (d *LazyDLL) Load() error {
if e != nil {
return e
}
- d.dll = dll
+ // Non-racy version of:
+ // d.dll = dll
+ atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
}
}
return nil
@@ -209,7 +229,9 @@ type LazyProc struct {
// an error if search fails. Find will not search procedure,
// if it is already found and loaded into memory.
func (p *LazyProc) Find() error {
- if p.proc == nil {
+ // Non-racy version of:
+ // if p.proc == nil {
+ if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
p.mu.Lock()
defer p.mu.Unlock()
if p.proc == nil {
@@ -221,7 +243,9 @@ func (p *LazyProc) Find() error {
if e != nil {
return e
}
- p.proc = proc
+ // Non-racy version of:
+ // p.proc = proc
+ atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
}
}
return nil
@@ -242,8 +266,14 @@ func (p *LazyProc) Addr() uintptr {
return p.proc.Addr()
}
-// Call executes procedure p with arguments a.
-func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, err error) {
+// Call executes procedure p with arguments a. It will panic, if more then 15 arguments
+// are supplied.
+//
+// The returned error is always non-nil, constructed from the result of GetLastError.
+// Callers must inspect the primary return value to decide whether an error occurred
+// (according to the semantics of the specific function being called) before consulting
+// the error. The error will be guaranteed to contain syscall.Errno.
+func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
p.mustFind()
return p.proc.Call(a...)
}
diff --git a/src/pkg/syscall/env_plan9.go b/src/pkg/syscall/env_plan9.go
index 2848d9b32..9587ab5af 100644
--- a/src/pkg/syscall/env_plan9.go
+++ b/src/pkg/syscall/env_plan9.go
@@ -12,14 +12,20 @@ import (
)
var (
- // envOnce guards initialization by copyenv, which populates env.
+ // envOnce guards copyenv, which populates env.
envOnce sync.Once
- // envLock guards env.
+ // envLock guards env and envs.
envLock sync.RWMutex
// env maps from an environment variable to its value.
- env map[string]string
+ env = make(map[string]string)
+
+ // envs contains elements of env in the form "key=value".
+ envs []string
+
+ errZeroLengthKey = errors.New("zero length key")
+ errShortWrite = errors.New("i/o count too small")
)
func readenv(key string) (string, error) {
@@ -47,12 +53,18 @@ func writeenv(key, value string) error {
return err
}
defer Close(fd)
- _, err = Write(fd, []byte(value))
- return err
+ b := []byte(value)
+ n, err := Write(fd, b)
+ if err != nil {
+ return err
+ }
+ if n != len(b) {
+ return errShortWrite
+ }
+ return nil
}
func copyenv() {
- env = make(map[string]string)
fd, err := Open("/env", O_RDONLY)
if err != nil {
return
@@ -62,17 +74,20 @@ func copyenv() {
if err != nil {
return
}
+ envs = make([]string, len(files))
+ i := 0
for _, key := range files {
v, err := readenv(key)
if err != nil {
continue
}
env[key] = v
+ envs[i] = key + "=" + v
+ i++
}
}
func Getenv(key string) (value string, found bool) {
- envOnce.Do(copyenv)
if len(key) == 0 {
return "", false
}
@@ -80,17 +95,21 @@ func Getenv(key string) (value string, found bool) {
envLock.RLock()
defer envLock.RUnlock()
- v, ok := env[key]
- if !ok {
+ if v, ok := env[key]; ok {
+ return v, true
+ }
+ v, err := readenv(key)
+ if err != nil {
return "", false
}
+ env[key] = v
+ envs = append(envs, key+"="+v)
return v, true
}
func Setenv(key, value string) error {
- envOnce.Do(copyenv)
if len(key) == 0 {
- return errors.New("zero length key")
+ return errZeroLengthKey
}
envLock.Lock()
@@ -101,28 +120,23 @@ func Setenv(key, value string) error {
return err
}
env[key] = value
+ envs = append(envs, key+"="+value)
return nil
}
func Clearenv() {
- envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv
-
envLock.Lock()
defer envLock.Unlock()
env = make(map[string]string)
+ envs = []string{}
RawSyscall(SYS_RFORK, RFCENVG, 0, 0)
}
func Environ() []string {
- envOnce.Do(copyenv)
envLock.RLock()
defer envLock.RUnlock()
- a := make([]string, len(env))
- i := 0
- for k, v := range env {
- a[i] = k + "=" + v
- i++
- }
- return a
+
+ envOnce.Do(copyenv)
+ return append([]string(nil), envs...)
}
diff --git a/src/pkg/syscall/env_unix.go b/src/pkg/syscall/env_unix.go
index 8b1868c27..8573d79c7 100644
--- a/src/pkg/syscall/env_unix.go
+++ b/src/pkg/syscall/env_unix.go
@@ -71,6 +71,16 @@ func Setenv(key, value string) error {
if len(key) == 0 {
return EINVAL
}
+ for i := 0; i < len(key); i++ {
+ if key[i] == '=' || key[i] == 0 {
+ return EINVAL
+ }
+ }
+ for i := 0; i < len(value); i++ {
+ if value[i] == 0 {
+ return EINVAL
+ }
+ }
envLock.Lock()
defer envLock.Unlock()
diff --git a/src/pkg/syscall/env_windows.go b/src/pkg/syscall/env_windows.go
index 8308f10a2..39bd5022e 100644
--- a/src/pkg/syscall/env_windows.go
+++ b/src/pkg/syscall/env_windows.go
@@ -12,14 +12,18 @@ import (
)
func Getenv(key string) (value string, found bool) {
+ keyp, err := UTF16PtrFromString(key)
+ if err != nil {
+ return "", false
+ }
b := make([]uint16, 100)
- n, e := GetEnvironmentVariable(StringToUTF16Ptr(key), &b[0], uint32(len(b)))
+ n, e := GetEnvironmentVariable(keyp, &b[0], uint32(len(b)))
if n == 0 && e == ERROR_ENVVAR_NOT_FOUND {
return "", false
}
if n > uint32(len(b)) {
b = make([]uint16, n)
- n, e = GetEnvironmentVariable(StringToUTF16Ptr(key), &b[0], uint32(len(b)))
+ n, e = GetEnvironmentVariable(keyp, &b[0], uint32(len(b)))
if n > uint32(len(b)) {
n = 0
}
@@ -32,10 +36,18 @@ func Getenv(key string) (value string, found bool) {
func Setenv(key, value string) error {
var v *uint16
+ var err error
if len(value) > 0 {
- v = StringToUTF16Ptr(value)
+ v, err = UTF16PtrFromString(value)
+ if err != nil {
+ return err
+ }
+ }
+ keyp, err := UTF16PtrFromString(key)
+ if err != nil {
+ return err
}
- e := SetEnvironmentVariable(StringToUTF16Ptr(key), v)
+ e := SetEnvironmentVariable(keyp, v)
if e != nil {
return e
}
diff --git a/src/pkg/syscall/exec_bsd.go b/src/pkg/syscall/exec_bsd.go
index 9c3af5ec9..318cbc060 100644
--- a/src/pkg/syscall/exec_bsd.go
+++ b/src/pkg/syscall/exec_bsd.go
@@ -221,3 +221,17 @@ childerror:
// and this shuts up the compiler.
panic("unreached")
}
+
+// Try to open a pipe with O_CLOEXEC set on both file descriptors.
+func forkExecPipe(p []int) error {
+ err := Pipe(p)
+ if err != nil {
+ return err
+ }
+ _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC)
+ if err != nil {
+ return err
+ }
+ _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC)
+ return err
+}
diff --git a/src/pkg/syscall/exec_linux.go b/src/pkg/syscall/exec_linux.go
index 70f3e6217..a8dc672b8 100644
--- a/src/pkg/syscall/exec_linux.go
+++ b/src/pkg/syscall/exec_linux.go
@@ -16,8 +16,9 @@ type SysProcAttr struct {
Ptrace bool // Enable tracing.
Setsid bool // Create session.
Setpgid bool // Set process group ID to new pid (SYSV setpgrp)
- Setctty bool // Set controlling terminal to fd 0
+ Setctty bool // Set controlling terminal to fd Ctty (only meaningful if Setsid is set)
Noctty bool // Detach fd 0 from controlling terminal
+ Ctty int // Controlling TTY fd (Linux only)
Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only)
}
@@ -206,9 +207,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
}
}
- // Make fd 0 the tty
- if sys.Setctty {
- _, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCSCTTY), 0)
+ // Set the controlling TTY to Ctty
+ if sys.Setctty && sys.Ctty >= 0 {
+ _, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
if err1 != 0 {
goto childerror
}
@@ -232,3 +233,20 @@ childerror:
// and this shuts up the compiler.
panic("unreached")
}
+
+// Try to open a pipe with O_CLOEXEC set on both file descriptors.
+func forkExecPipe(p []int) (err error) {
+ err = Pipe2(p, O_CLOEXEC)
+ // pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it
+ // might not be implemented.
+ if err == ENOSYS {
+ if err = Pipe(p); err != nil {
+ return
+ }
+ if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != nil {
+ return
+ }
+ _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC)
+ }
+ return
+}
diff --git a/src/pkg/syscall/exec_plan9.go b/src/pkg/syscall/exec_plan9.go
index 7e4e180fa..ebd57f3e3 100644
--- a/src/pkg/syscall/exec_plan9.go
+++ b/src/pkg/syscall/exec_plan9.go
@@ -7,6 +7,7 @@
package syscall
import (
+ "runtime"
"sync"
"unsafe"
)
@@ -60,8 +61,9 @@ import (
var ForkLock sync.RWMutex
-// Convert array of string to array
-// of NUL-terminated byte pointer.
+// StringSlicePtr is deprecated. Use SlicePtrFromStrings instead.
+// If any string contains a NUL byte this function panics instead
+// of returning an error.
func StringSlicePtr(ss []string) []*byte {
bb := make([]*byte, len(ss)+1)
for i := 0; i < len(ss); i++ {
@@ -71,81 +73,81 @@ func StringSlicePtr(ss []string) []*byte {
return bb
}
-// gbit16 reads a 16-bit numeric value from a 9P protocol message stored in b,
-// returning the value and the remaining slice of b.
-func gbit16(b []byte) (uint16, []byte) {
- return uint16(b[0]) | uint16(b[1])<<8, b[2:]
-}
-
-// gstring reads a string from a 9P protocol message stored in b,
-// returning the value as a Go string and the remaining slice of b.
-func gstring(b []byte) (string, []byte) {
- n, b := gbit16(b)
- return string(b[0:n]), b[n:]
+// SlicePtrFromStrings converts a slice of strings to a slice of
+// pointers to NUL-terminated byte slices. If any string contains
+// a NUL byte, it returns (nil, EINVAL).
+func SlicePtrFromStrings(ss []string) ([]*byte, error) {
+ var err error
+ bb := make([]*byte, len(ss)+1)
+ for i := 0; i < len(ss); i++ {
+ bb[i], err = BytePtrFromString(ss[i])
+ if err != nil {
+ return nil, err
+ }
+ }
+ bb[len(ss)] = nil
+ return bb, nil
}
// readdirnames returns the names of files inside the directory represented by dirfd.
func readdirnames(dirfd int) (names []string, err error) {
- result := make([]string, 0, 100)
+ names = make([]string, 0, 100)
var buf [STATMAX]byte
for {
n, e := Read(dirfd, buf[:])
if e != nil {
- return []string{}, e
+ return nil, e
}
if n == 0 {
break
}
-
for i := 0; i < n; {
m, _ := gbit16(buf[i:])
m += 2
if m < STATFIXLEN {
- return []string{}, NewError("malformed stat buffer")
+ return nil, ErrBadStat
}
- name, _ := gstring(buf[i+41:])
- result = append(result, name)
-
+ s, _, ok := gstring(buf[i+41:])
+ if !ok {
+ return nil, ErrBadStat
+ }
+ names = append(names, s)
i += int(m)
}
}
- return []string{}, nil
+ return
}
// readdupdevice returns a list of currently opened fds (excluding stdin, stdout, stderr) from the dup device #d.
// ForkLock should be write locked before calling, so that no new fds would be created while the fd list is being read.
func readdupdevice() (fds []int, err error) {
dupdevfd, err := Open("#d", O_RDONLY)
-
if err != nil {
return
}
defer Close(dupdevfd)
- fileNames, err := readdirnames(dupdevfd)
+ names, err := readdirnames(dupdevfd)
if err != nil {
return
}
- fds = make([]int, 0, len(fileNames)>>1)
- for _, fdstr := range fileNames {
- if l := len(fdstr); l > 2 && fdstr[l-3] == 'c' && fdstr[l-2] == 't' && fdstr[l-1] == 'l' {
+ fds = make([]int, 0, len(names)/2)
+ for _, name := range names {
+ if n := len(name); n > 3 && name[n-3:n] == "ctl" {
continue
}
-
- fd := int(atoi([]byte(fdstr)))
-
- if fd == 0 || fd == 1 || fd == 2 || fd == dupdevfd {
+ fd := int(atoi([]byte(name)))
+ switch fd {
+ case 0, 1, 2, dupdevfd:
continue
}
-
fds = append(fds, fd)
}
-
- return fds[0:len(fds)], nil
+ return
}
var startupFds []int
@@ -196,7 +198,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at
r1, _, _ = RawSyscall(SYS_RFORK, uintptr(RFPROC|RFFDG|RFREND|clearenv|rflag), 0, 0)
if r1 != 0 {
- if int(r1) == -1 {
+ if int32(r1) == -1 {
return 0, NewError(errstr())
}
// parent; return PID
@@ -208,7 +210,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at
// Close fds we don't need.
for i = 0; i < len(fdsToClose); i++ {
r1, _, _ = RawSyscall(SYS_CLOSE, uintptr(fdsToClose[i]), 0, 0)
- if int(r1) == -1 {
+ if int32(r1) == -1 {
goto childerror
}
}
@@ -218,7 +220,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at
for i = 0; i < len(envv); i++ {
r1, _, _ = RawSyscall(SYS_CREATE, uintptr(unsafe.Pointer(envv[i].name)), uintptr(O_WRONLY), uintptr(0666))
- if int(r1) == -1 {
+ if int32(r1) == -1 {
goto childerror
}
@@ -227,13 +229,13 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at
r1, _, _ = RawSyscall6(SYS_PWRITE, uintptr(envfd), uintptr(unsafe.Pointer(envv[i].value)), uintptr(envv[i].nvalue),
^uintptr(0), ^uintptr(0), 0)
- if int(r1) == -1 || int(r1) != envv[i].nvalue {
+ if int32(r1) == -1 || int(r1) != envv[i].nvalue {
goto childerror
}
r1, _, _ = RawSyscall(SYS_CLOSE, uintptr(envfd), 0, 0)
- if int(r1) == -1 {
+ if int32(r1) == -1 {
goto childerror
}
}
@@ -242,7 +244,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at
// Chdir
if dir != nil {
r1, _, _ = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
- if int(r1) == -1 {
+ if int32(r1) == -1 {
goto childerror
}
}
@@ -252,7 +254,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at
nextfd = int(len(fd))
if pipe < nextfd {
r1, _, _ = RawSyscall(SYS_DUP, uintptr(pipe), uintptr(nextfd), 0)
- if int(r1) == -1 {
+ if int32(r1) == -1 {
goto childerror
}
pipe = nextfd
@@ -261,7 +263,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at
for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] < int(i) {
r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(nextfd), 0)
- if int(r1) == -1 {
+ if int32(r1) == -1 {
goto childerror
}
@@ -282,12 +284,17 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, at
if fd[i] == int(i) {
continue
}
-
r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(i), 0)
- if int(r1) == -1 {
+ if int32(r1) == -1 {
goto childerror
}
- RawSyscall(SYS_CLOSE, uintptr(fd[i]), 0, 0)
+ }
+
+ // Pass 3: close fd[i] if it was moved in the previous pass.
+ for i = 0; i < len(fd); i++ {
+ if fd[i] >= 0 && fd[i] != int(i) {
+ RawSyscall(SYS_CLOSE, uintptr(fd[i]), 0, 0)
+ }
}
// Time to exec.
@@ -374,12 +381,21 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
p[1] = -1
// Convert args to C form.
- argv0p := StringBytePtr(argv0)
- argvp := StringSlicePtr(argv)
+ argv0p, err := BytePtrFromString(argv0)
+ if err != nil {
+ return 0, err
+ }
+ argvp, err := SlicePtrFromStrings(argv)
+ if err != nil {
+ return 0, err
+ }
var dir *byte
if attr.Dir != "" {
- dir = StringBytePtr(attr.Dir)
+ dir, err = BytePtrFromString(attr.Dir)
+ if err != nil {
+ return 0, err
+ }
}
var envvParsed []envItem
if attr.Env != nil {
@@ -390,7 +406,13 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
i++
}
- envvParsed = append(envvParsed, envItem{StringBytePtr("/env/" + v[:i]), StringBytePtr(v[i+1:]), len(v) - i})
+ envname, err := BytePtrFromString("/env/" + v[:i])
+ if err != nil {
+ return 0, err
+ }
+ envvalue := make([]byte, len(v)-i)
+ copy(envvalue, v[i+1:])
+ envvParsed = append(envvParsed, envItem{envname, &envvalue[0], len(v) - i})
}
}
@@ -400,44 +422,37 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
// get a list of open fds, excluding stdin,stdout and stderr that need to be closed in the child.
// no new fds can be created while we hold the ForkLock for writing.
openFds, e := readdupdevice()
-
if e != nil {
ForkLock.Unlock()
return 0, e
}
fdsToClose := make([]int, 0, len(openFds))
- // exclude fds opened from startup from the list of fds to be closed.
for _, fd := range openFds {
- isReserved := false
- for _, reservedFd := range startupFds {
- if fd == reservedFd {
- isReserved = true
+ doClose := true
+
+ // exclude files opened at startup.
+ for _, sfd := range startupFds {
+ if fd == sfd {
+ doClose = false
break
}
}
- if !isReserved {
- fdsToClose = append(fdsToClose, fd)
- }
- }
-
- // exclude fds requested by the caller from the list of fds to be closed.
- for _, fd := range openFds {
- isReserved := false
- for _, reservedFd := range attr.Files {
- if fd == int(reservedFd) {
- isReserved = true
+ // exclude files explicitly requested by the caller.
+ for _, rfd := range attr.Files {
+ if fd == int(rfd) {
+ doClose = false
break
}
}
- if !isReserved {
+ if doClose {
fdsToClose = append(fdsToClose, fd)
}
}
- // Allocate child status pipe close on exec.
+ // Allocate child status pipe close on exec.
e = cexecPipe(p[:])
if e != nil {
@@ -480,14 +495,75 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
return pid, nil
}
+type waitErr struct {
+ Waitmsg
+ err error
+}
+
+var procs struct {
+ sync.Mutex
+ waits map[int]chan *waitErr
+}
+
+// startProcess starts a new goroutine, tied to the OS
+// thread, which runs the process and subsequently waits
+// for it to finish, communicating the process stats back
+// to any goroutines that may have been waiting on it.
+//
+// Such a dedicated goroutine is needed because on
+// Plan 9, only the parent thread can wait for a child,
+// whereas goroutines tend to jump OS threads (e.g.,
+// between starting a process and running Wait(), the
+// goroutine may have been rescheduled).
+func startProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
+ type forkRet struct {
+ pid int
+ err error
+ }
+
+ forkc := make(chan forkRet, 1)
+ go func() {
+ runtime.LockOSThread()
+ var ret forkRet
+
+ ret.pid, ret.err = forkExec(argv0, argv, attr)
+ // If fork fails there is nothing to wait for.
+ if ret.err != nil || ret.pid == 0 {
+ forkc <- ret
+ return
+ }
+
+ waitc := make(chan *waitErr, 1)
+
+ // Mark that the process is running.
+ procs.Lock()
+ if procs.waits == nil {
+ procs.waits = make(map[int]chan *waitErr)
+ }
+ procs.waits[ret.pid] = waitc
+ procs.Unlock()
+
+ forkc <- ret
+
+ var w waitErr
+ for w.err == nil && w.Pid != ret.pid {
+ w.err = Await(&w.Waitmsg)
+ }
+ waitc <- &w
+ close(waitc)
+ }()
+ ret := <-forkc
+ return ret.pid, ret.err
+}
+
// Combination of fork and exec, careful to be thread safe.
func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
- return forkExec(argv0, argv, attr)
+ return startProcess(argv0, argv, attr)
}
// StartProcess wraps ForkExec for package os.
func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
- pid, err = forkExec(argv0, argv, attr)
+ pid, err = startProcess(argv0, argv, attr)
return pid, 0, err
}
@@ -495,7 +571,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
func Exec(argv0 string, argv []string, envv []string) (err error) {
if envv != nil {
r1, _, _ := RawSyscall(SYS_RFORK, RFCENVG, 0, 0)
- if int(r1) == -1 {
+ if int32(r1) == -1 {
return NewError(errstr())
}
@@ -519,10 +595,50 @@ func Exec(argv0 string, argv []string, envv []string) (err error) {
}
}
+ argv0p, err := BytePtrFromString(argv0)
+ if err != nil {
+ return err
+ }
+ argvp, err := SlicePtrFromStrings(argv)
+ if err != nil {
+ return err
+ }
_, _, e1 := Syscall(SYS_EXEC,
- uintptr(unsafe.Pointer(StringBytePtr(argv0))),
- uintptr(unsafe.Pointer(&StringSlicePtr(argv)[0])),
+ uintptr(unsafe.Pointer(argv0p)),
+ uintptr(unsafe.Pointer(&argvp[0])),
0)
return e1
}
+
+// WaitProcess waits until the pid of a
+// running process is found in the queue of
+// wait messages. It is used in conjunction
+// with ForkExec/StartProcess to wait for a
+// running process to exit.
+func WaitProcess(pid int, w *Waitmsg) (err error) {
+ procs.Lock()
+ ch := procs.waits[pid]
+ procs.Unlock()
+
+ var wmsg *waitErr
+ if ch != nil {
+ wmsg = <-ch
+ procs.Lock()
+ if procs.waits[pid] == ch {
+ delete(procs.waits, pid)
+ }
+ procs.Unlock()
+ }
+ if wmsg == nil {
+ // ch was missing or ch is closed
+ return NewError("process not found")
+ }
+ if wmsg.err != nil {
+ return wmsg.err
+ }
+ if w != nil {
+ *w = wmsg.Waitmsg
+ }
+ return nil
+}
diff --git a/src/pkg/syscall/exec_unix.go b/src/pkg/syscall/exec_unix.go
index dfaa0374a..01b76c106 100644
--- a/src/pkg/syscall/exec_unix.go
+++ b/src/pkg/syscall/exec_unix.go
@@ -63,8 +63,9 @@ import (
var ForkLock sync.RWMutex
-// Convert array of string to array
-// of NUL-terminated byte pointer.
+// StringSlicePtr is deprecated. Use SlicePtrFromStrings instead.
+// If any string contains a NUL byte this function panics instead
+// of returning an error.
func StringSlicePtr(ss []string) []*byte {
bb := make([]*byte, len(ss)+1)
for i := 0; i < len(ss); i++ {
@@ -74,6 +75,22 @@ func StringSlicePtr(ss []string) []*byte {
return bb
}
+// SlicePtrFromStrings converts a slice of strings to a slice of
+// pointers to NUL-terminated byte slices. If any string contains
+// a NUL byte, it returns (nil, EINVAL).
+func SlicePtrFromStrings(ss []string) ([]*byte, error) {
+ var err error
+ bb := make([]*byte, len(ss)+1)
+ for i := 0; i < len(ss); i++ {
+ bb[i], err = BytePtrFromString(ss[i])
+ if err != nil {
+ return nil, err
+ }
+ }
+ bb[len(ss)] = nil
+ return bb, nil
+}
+
func CloseOnExec(fd int) { fcntl(fd, F_SETFD, FD_CLOEXEC) }
func SetNonblock(fd int, nonblocking bool) (err error) {
@@ -128,9 +145,18 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
p[1] = -1
// Convert args to C form.
- argv0p := StringBytePtr(argv0)
- argvp := StringSlicePtr(argv)
- envvp := StringSlicePtr(attr.Env)
+ argv0p, err := BytePtrFromString(argv0)
+ if err != nil {
+ return 0, err
+ }
+ argvp, err := SlicePtrFromStrings(argv)
+ if err != nil {
+ return 0, err
+ }
+ envvp, err := SlicePtrFromStrings(attr.Env)
+ if err != nil {
+ return 0, err
+ }
if runtime.GOOS == "freebsd" && len(argv[0]) > len(argv0) {
argvp[0] = argv0p
@@ -138,11 +164,17 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
var chroot *byte
if sys.Chroot != "" {
- chroot = StringBytePtr(sys.Chroot)
+ chroot, err = BytePtrFromString(sys.Chroot)
+ if err != nil {
+ return 0, err
+ }
}
var dir *byte
if attr.Dir != "" {
- dir = StringBytePtr(attr.Dir)
+ dir, err = BytePtrFromString(attr.Dir)
+ if err != nil {
+ return 0, err
+ }
}
// Acquire the fork lock so that no other threads
@@ -151,13 +183,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
ForkLock.Lock()
// Allocate child status pipe close on exec.
- if err = Pipe(p[0:]); err != nil {
- goto error
- }
- if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != nil {
- goto error
- }
- if _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC); err != nil {
+ if err = forkExecPipe(p[:]); err != nil {
goto error
}
@@ -171,7 +197,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
// Read child error status from pipe.
Close(p[1])
- n, err = read(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1)))
+ n, err = readlen(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1)))
Close(p[0])
if err != nil || n != 0 {
if n == int(unsafe.Sizeof(err1)) {
@@ -215,9 +241,21 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
// Ordinary exec.
func Exec(argv0 string, argv []string, envv []string) (err error) {
+ argv0p, err := BytePtrFromString(argv0)
+ if err != nil {
+ return err
+ }
+ argvp, err := SlicePtrFromStrings(argv)
+ if err != nil {
+ return err
+ }
+ envvp, err := SlicePtrFromStrings(envv)
+ if err != nil {
+ return err
+ }
_, _, err1 := RawSyscall(SYS_EXECVE,
- uintptr(unsafe.Pointer(StringBytePtr(argv0))),
- uintptr(unsafe.Pointer(&StringSlicePtr(argv)[0])),
- uintptr(unsafe.Pointer(&StringSlicePtr(envv)[0])))
+ uintptr(unsafe.Pointer(argv0p)),
+ uintptr(unsafe.Pointer(&argvp[0])),
+ uintptr(unsafe.Pointer(&envvp[0])))
return Errno(err1)
}
diff --git a/src/pkg/syscall/exec_windows.go b/src/pkg/syscall/exec_windows.go
index 4dc4d059d..82abc0715 100644
--- a/src/pkg/syscall/exec_windows.go
+++ b/src/pkg/syscall/exec_windows.go
@@ -132,7 +132,10 @@ func SetNonblock(fd Handle, nonblocking bool) (err error) {
// getFullPath retrieves the full path of the specified file.
// Just a wrapper for Windows GetFullPathName api.
func getFullPath(name string) (path string, err error) {
- p := StringToUTF16Ptr(name)
+ p, err := UTF16PtrFromString(name)
+ if err != nil {
+ return "", err
+ }
buf := make([]uint16, 100)
n, err := GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
if err != nil {
@@ -225,8 +228,9 @@ type ProcAttr struct {
}
type SysProcAttr struct {
- HideWindow bool
- CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess
+ HideWindow bool
+ CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess
+ CreationFlags uint32
}
var zeroProcAttr ProcAttr
@@ -261,7 +265,10 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
return 0, 0, err
}
}
- argv0p := StringToUTF16Ptr(argv0)
+ argv0p, err := UTF16PtrFromString(argv0)
+ if err != nil {
+ return 0, 0, err
+ }
var cmdline string
// Windows CreateProcess takes the command line as a single string:
@@ -275,12 +282,18 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
var argvp *uint16
if len(cmdline) != 0 {
- argvp = StringToUTF16Ptr(cmdline)
+ argvp, err = UTF16PtrFromString(cmdline)
+ if err != nil {
+ return 0, 0, err
+ }
}
var dirp *uint16
if len(attr.Dir) != 0 {
- dirp = StringToUTF16Ptr(attr.Dir)
+ dirp, err = UTF16PtrFromString(attr.Dir)
+ if err != nil {
+ return 0, 0, err
+ }
}
// Acquire the fork lock so that no other threads
@@ -313,7 +326,8 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
pi := new(ProcessInformation)
- err = CreateProcess(argv0p, argvp, nil, nil, true, CREATE_UNICODE_ENVIRONMENT, createEnvBlock(attr.Env), dirp, si, pi)
+ flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT
+ err = CreateProcess(argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi)
if err != nil {
return 0, 0, err
}
diff --git a/src/pkg/syscall/mkall.sh b/src/pkg/syscall/mkall.sh
index 9e2d98f0d..3900a93ab 100755
--- a/src/pkg/syscall/mkall.sh
+++ b/src/pkg/syscall/mkall.sh
@@ -87,7 +87,8 @@ case "$1" in
-syscalls)
for i in zsyscall*go
do
- sed 1q $i | sed 's;^// ;;' | sh | gofmt >_$i && mv _$i $i
+ sed 1q $i | sed 's;^// ;;' | sh > _$i && gofmt < _$i > $i
+ rm _$i
done
exit 0
;;
@@ -131,6 +132,12 @@ freebsd_amd64)
mksysnum="curl -s 'http://svn.freebsd.org/base/head/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
+freebsd_arm)
+ mkerrors="$mkerrors"
+ mksyscall="./mksyscall.pl -l32 -arm"
+ mksysnum="curl -s 'http://svn.freebsd.org/base/head/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
+ mktypes="GOARCH=$GOARCH go tool cgo -godefs"
+ ;;
linux_386)
mkerrors="$mkerrors -m32"
mksyscall="./mksyscall.pl -l32"
@@ -138,8 +145,13 @@ linux_386)
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
linux_amd64)
+ unistd_h=$(ls -1 /usr/include/asm/unistd_64.h /usr/include/x86_64-linux-gnu/asm/unistd_64.h 2>/dev/null | head -1)
+ if [ "$unistd_h" = "" ]; then
+ echo >&2 cannot find unistd_64.h
+ exit 1
+ fi
mkerrors="$mkerrors -m64"
- mksysnum="./mksysnum_linux.pl /usr/include/asm/unistd_64.h"
+ mksysnum="./mksysnum_linux.pl $unistd_h"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
linux_arm)
diff --git a/src/pkg/syscall/mkerrors.sh b/src/pkg/syscall/mkerrors.sh
index 9aacec29c..e8abb6774 100755
--- a/src/pkg/syscall/mkerrors.sh
+++ b/src/pkg/syscall/mkerrors.sh
@@ -83,6 +83,10 @@ includes_Linux='
#include <net/if_arp.h>
#include <net/route.h>
#include <netpacket/packet.h>
+
+#ifndef MSG_FASTOPEN
+#define MSG_FASTOPEN 0x20000000
+#endif
'
includes_NetBSD='
@@ -104,6 +108,9 @@ includes_NetBSD='
#include <netinet/ip.h>
#include <netinet/ip_mroute.h>
#include <netinet/if_ether.h>
+
+// Needed since <sys/param.h> refers to it...
+#define schedppq 1
'
includes_OpenBSD='
@@ -174,22 +181,37 @@ ccflags="$@"
$2 !~ /^EQUIV_/ &&
$2 !~ /^EXPR_/ &&
$2 ~ /^E[A-Z0-9_]+$/ ||
+ $2 ~ /^B[0-9_]+$/ ||
+ $2 ~ /^V[A-Z0-9]+$/ ||
+ $2 ~ /^CS[A-Z0-9]/ ||
+ $2 ~ /^I(SIG|CANON|CRNL|EXTEN|MAXBEL|STRIP|UTF8)$/ ||
+ $2 ~ /^IGN/ ||
+ $2 ~ /^IX(ON|ANY|OFF)$/ ||
+ $2 ~ /^IN(LCR|PCK)$/ ||
+ $2 ~ /(^FLU?SH)|(FLU?SH$)/ ||
+ $2 ~ /^C(LOCAL|READ)$/ ||
+ $2 == "BRKINT" ||
+ $2 == "HUPCL" ||
+ $2 == "PENDIN" ||
+ $2 == "TOSTOP" ||
+ $2 ~ /^PAR/ ||
$2 ~ /^SIG[^_]/ ||
+ $2 ~ /^O[CNPFP][A-Z]+[^_][A-Z]+$/ ||
$2 ~ /^IN_/ ||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
$2 == "SOMAXCONN" ||
$2 == "NAME_MAX" ||
$2 == "IFNAMSIZ" ||
- $2 == "CTL_NET" ||
- $2 == "CTL_MAXNAME" ||
+ $2 ~ /^CTL_(MAXNAME|NET|QUERY)$/ ||
+ $2 ~ /^SYSCTL_VERS/ ||
$2 ~ /^(MS|MNT)_/ ||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
$2 ~ /^(O|F|FD|NAME|S|PTRACE|PT)_/ ||
$2 ~ /^LINUX_REBOOT_CMD_/ ||
$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
$2 !~ "NLA_TYPE_MASK" &&
- $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ ||
+ $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ ||
$2 ~ /^SIOC/ ||
$2 ~ /^TIOC/ ||
$2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ ||
diff --git a/src/pkg/syscall/mksyscall.pl b/src/pkg/syscall/mksyscall.pl
index 09949688e..df47958ea 100755
--- a/src/pkg/syscall/mksyscall.pl
+++ b/src/pkg/syscall/mksyscall.pl
@@ -110,6 +110,16 @@ while(<>) {
my $out_decl = @out ? sprintf(" (%s)", join(', ', @out)) : "";
$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out_decl;
+ # Check if err return available
+ my $errvar = "";
+ foreach my $p (@out) {
+ my ($name, $type) = parseparam($p);
+ if($type eq "error") {
+ $errvar = $name;
+ last;
+ }
+ }
+
# Prepare arguments to Syscall.
my @args = ();
my $n = 0;
@@ -117,8 +127,18 @@ while(<>) {
my ($name, $type) = parseparam($p);
if($type =~ /^\*/) {
push @args, "uintptr(unsafe.Pointer($name))";
+ } elsif($type eq "string" && $errvar ne "") {
+ $text .= "\tvar _p$n *byte\n";
+ $text .= "\t_p$n, $errvar = BytePtrFromString($name)\n";
+ $text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
+ push @args, "uintptr(unsafe.Pointer(_p$n))";
+ $n++;
} elsif($type eq "string") {
- push @args, "uintptr(unsafe.Pointer(StringBytePtr($name)))";
+ print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
+ $text .= "\tvar _p$n *byte\n";
+ $text .= "\t_p$n, _ = BytePtrFromString($name)\n";
+ push @args, "uintptr(unsafe.Pointer(_p$n))";
+ $n++;
} elsif($type =~ /^\[\](.*)/) {
# Convert slice into pointer, length.
# Have to be careful not to take address of &a[0] if len == 0:
@@ -237,7 +257,7 @@ while(<>) {
$text .= $body;
if ($plan9 && $ret[2] eq "e1") {
- $text .= "\tif int(r0) == -1 {\n";
+ $text .= "\tif int32(r0) == -1 {\n";
$text .= "\t\terr = e1\n";
$text .= "\t}\n";
} elsif ($do_errno) {
diff --git a/src/pkg/syscall/mksyscall_windows.pl b/src/pkg/syscall/mksyscall_windows.pl
index 1fb51125b..0e015cb70 100755
--- a/src/pkg/syscall/mksyscall_windows.pl
+++ b/src/pkg/syscall/mksyscall_windows.pl
@@ -118,7 +118,8 @@ while(<>) {
}
# Decide which version of api is used: ascii or unicode.
- my $strconvfunc = $sysname !~ /W$/ ? "StringBytePtr" : "StringToUTF16Ptr";
+ my $strconvfunc = $sysname !~ /W$/ ? "BytePtrFromString" : "UTF16PtrFromString";
+ my $strconvtype = $sysname !~ /W$/ ? "*byte" : "*uint16";
# Winapi proc address variable.
$vars .= "\t$sysvarname = $modvname.NewProc(\"$sysname\")\n";
@@ -133,6 +134,16 @@ while(<>) {
}
$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out;
+ # Check if err return available
+ my $errvar = "";
+ foreach my $p (@out) {
+ my ($name, $type) = parseparam($p);
+ if($type eq "error") {
+ $errvar = $name;
+ last;
+ }
+ }
+
# Prepare arguments to Syscall.
my @args = ();
my $n = 0;
@@ -141,8 +152,18 @@ while(<>) {
my ($name, $type) = parseparam($p);
if($type =~ /^\*/) {
push @args, "uintptr(unsafe.Pointer($name))";
+ } elsif($type eq "string" && $errvar ne "") {
+ $text .= "\tvar _p$n $strconvtype\n";
+ $text .= "\t_p$n, $errvar = $strconvfunc($name)\n";
+ $text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
+ push @args, "uintptr(unsafe.Pointer(_p$n))";
+ $n++;
} elsif($type eq "string") {
- push @args, "uintptr(unsafe.Pointer($strconvfunc($name)))";
+ print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
+ $text .= "\tvar _p$n $strconvtype\n";
+ $text .= "\t_p$n, _ = $strconvfunc($name)\n";
+ push @args, "uintptr(unsafe.Pointer(_p$n))";
+ $n++;
} elsif($type =~ /^\[\](.*)/) {
# Convert slice into pointer, length.
# Have to be careful not to take address of &a[0] if len == 0:
@@ -245,7 +266,7 @@ while(<>) {
$failexpr = "!$name";
} elsif($name eq "err") {
$ret[$i] = "r1";
- $failexpr = "int(r1) $failcond";
+ $failexpr = "r1 $failcond";
} else {
$failexpr = "$name $failcond";
}
diff --git a/src/pkg/syscall/netlink_linux.go b/src/pkg/syscall/netlink_linux.go
index dc0f68470..49550ea2f 100644
--- a/src/pkg/syscall/netlink_linux.go
+++ b/src/pkg/syscall/netlink_linux.go
@@ -6,9 +6,7 @@
package syscall
-import (
- "unsafe"
-)
+import "unsafe"
// Round the length of a netlink message up to align it properly.
func nlmAlignOf(msglen int) int {
@@ -21,8 +19,8 @@ func rtaAlignOf(attrlen int) int {
return (attrlen + RTA_ALIGNTO - 1) & ^(RTA_ALIGNTO - 1)
}
-// NetlinkRouteRequest represents the request message to receive
-// routing and link states from the kernel.
+// NetlinkRouteRequest represents a request message to receive routing
+// and link states from the kernel.
type NetlinkRouteRequest struct {
Header NlMsghdr
Data RtGenmsg
@@ -30,29 +28,18 @@ type NetlinkRouteRequest struct {
func (rr *NetlinkRouteRequest) toWireFormat() []byte {
b := make([]byte, rr.Header.Len)
- b[0] = byte(rr.Header.Len)
- b[1] = byte(rr.Header.Len >> 8)
- b[2] = byte(rr.Header.Len >> 16)
- b[3] = byte(rr.Header.Len >> 24)
- b[4] = byte(rr.Header.Type)
- b[5] = byte(rr.Header.Type >> 8)
- b[6] = byte(rr.Header.Flags)
- b[7] = byte(rr.Header.Flags >> 8)
- b[8] = byte(rr.Header.Seq)
- b[9] = byte(rr.Header.Seq >> 8)
- b[10] = byte(rr.Header.Seq >> 16)
- b[11] = byte(rr.Header.Seq >> 24)
- b[12] = byte(rr.Header.Pid)
- b[13] = byte(rr.Header.Pid >> 8)
- b[14] = byte(rr.Header.Pid >> 16)
- b[15] = byte(rr.Header.Pid >> 24)
+ *(*uint32)(unsafe.Pointer(&b[0:4][0])) = rr.Header.Len
+ *(*uint16)(unsafe.Pointer(&b[4:6][0])) = rr.Header.Type
+ *(*uint16)(unsafe.Pointer(&b[6:8][0])) = rr.Header.Flags
+ *(*uint32)(unsafe.Pointer(&b[8:12][0])) = rr.Header.Seq
+ *(*uint32)(unsafe.Pointer(&b[12:16][0])) = rr.Header.Pid
b[16] = byte(rr.Data.Family)
return b
}
func newNetlinkRouteRequest(proto, seq, family int) []byte {
rr := &NetlinkRouteRequest{}
- rr.Header.Len = NLMSG_HDRLEN + SizeofRtGenmsg
+ rr.Header.Len = uint32(NLMSG_HDRLEN + SizeofRtGenmsg)
rr.Header.Type = uint16(proto)
rr.Header.Flags = NLM_F_DUMP | NLM_F_REQUEST
rr.Header.Seq = uint32(seq)
@@ -60,167 +47,131 @@ func newNetlinkRouteRequest(proto, seq, family int) []byte {
return rr.toWireFormat()
}
-// NetlinkRIB returns routing information base, as known as RIB,
-// which consists of network facility information, states and
-// parameters.
+// NetlinkRIB returns routing information base, as known as RIB, which
+// consists of network facility information, states and parameters.
func NetlinkRIB(proto, family int) ([]byte, error) {
- var (
- lsanl SockaddrNetlink
- tab []byte
- )
-
- s, e := Socket(AF_NETLINK, SOCK_RAW, 0)
- if e != nil {
- return nil, e
+ s, err := Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)
+ if err != nil {
+ return nil, err
}
defer Close(s)
-
- lsanl.Family = AF_NETLINK
- e = Bind(s, &lsanl)
- if e != nil {
- return nil, e
+ lsa := &SockaddrNetlink{Family: AF_NETLINK}
+ if err := Bind(s, lsa); err != nil {
+ return nil, err
}
-
- seq := 1
- wb := newNetlinkRouteRequest(proto, seq, family)
- e = Sendto(s, wb, 0, &lsanl)
- if e != nil {
- return nil, e
+ wb := newNetlinkRouteRequest(proto, 1, family)
+ if err := Sendto(s, wb, 0, lsa); err != nil {
+ return nil, err
}
-
+ var tab []byte
+done:
for {
- var (
- rb []byte
- nr int
- lsa Sockaddr
- )
-
- rb = make([]byte, Getpagesize())
- nr, _, e = Recvfrom(s, rb, 0)
- if e != nil {
- return nil, e
+ rb := make([]byte, Getpagesize())
+ nr, _, err := Recvfrom(s, rb, 0)
+ if err != nil {
+ return nil, err
}
if nr < NLMSG_HDRLEN {
return nil, EINVAL
}
rb = rb[:nr]
tab = append(tab, rb...)
-
- msgs, _ := ParseNetlinkMessage(rb)
+ msgs, err := ParseNetlinkMessage(rb)
+ if err != nil {
+ return nil, err
+ }
for _, m := range msgs {
- if lsa, e = Getsockname(s); e != nil {
- return nil, e
+ lsa, err := Getsockname(s)
+ if err != nil {
+ return nil, err
}
switch v := lsa.(type) {
case *SockaddrNetlink:
- if m.Header.Seq != uint32(seq) || m.Header.Pid != v.Pid {
+ if m.Header.Seq != 1 || m.Header.Pid != v.Pid {
return nil, EINVAL
}
default:
return nil, EINVAL
}
if m.Header.Type == NLMSG_DONE {
- goto done
+ break done
}
if m.Header.Type == NLMSG_ERROR {
return nil, EINVAL
}
}
}
-
-done:
return tab, nil
}
-// NetlinkMessage represents the netlink message.
+// NetlinkMessage represents a netlink message.
type NetlinkMessage struct {
Header NlMsghdr
Data []byte
}
-// ParseNetlinkMessage parses buf as netlink messages and returns
-// the slice containing the NetlinkMessage structs.
-func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, error) {
- var (
- h *NlMsghdr
- dbuf []byte
- dlen int
- e error
- msgs []NetlinkMessage
- )
-
- for len(buf) >= NLMSG_HDRLEN {
- h, dbuf, dlen, e = netlinkMessageHeaderAndData(buf)
- if e != nil {
- break
+// ParseNetlinkMessage parses b as an array of netlink messages and
+// returns the slice containing the NetlinkMessage structures.
+func ParseNetlinkMessage(b []byte) ([]NetlinkMessage, error) {
+ var msgs []NetlinkMessage
+ for len(b) >= NLMSG_HDRLEN {
+ h, dbuf, dlen, err := netlinkMessageHeaderAndData(b)
+ if err != nil {
+ return nil, err
}
- m := NetlinkMessage{}
- m.Header = *h
- m.Data = dbuf[:h.Len-NLMSG_HDRLEN]
+ m := NetlinkMessage{Header: *h, Data: dbuf[:int(h.Len)-NLMSG_HDRLEN]}
msgs = append(msgs, m)
- buf = buf[dlen:]
+ b = b[dlen:]
}
-
- return msgs, e
+ return msgs, nil
}
-func netlinkMessageHeaderAndData(buf []byte) (*NlMsghdr, []byte, int, error) {
- h := (*NlMsghdr)(unsafe.Pointer(&buf[0]))
- if h.Len < NLMSG_HDRLEN || int(h.Len) > len(buf) {
+func netlinkMessageHeaderAndData(b []byte) (*NlMsghdr, []byte, int, error) {
+ h := (*NlMsghdr)(unsafe.Pointer(&b[0]))
+ if int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(b) {
return nil, nil, 0, EINVAL
}
- return h, buf[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil
+ return h, b[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil
}
-// NetlinkRouteAttr represents the netlink route attribute.
+// NetlinkRouteAttr represents a netlink route attribute.
type NetlinkRouteAttr struct {
Attr RtAttr
Value []byte
}
-// ParseNetlinkRouteAttr parses msg's payload as netlink route
-// attributes and returns the slice containing the NetlinkRouteAttr
-// structs.
-func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, error) {
- var (
- buf []byte
- a *RtAttr
- alen int
- vbuf []byte
- e error
- attrs []NetlinkRouteAttr
- )
-
- switch msg.Header.Type {
+// ParseNetlinkRouteAttr parses m's payload as an array of netlink
+// route attributes and returns the slice containing the
+// NetlinkRouteAttr structures.
+func ParseNetlinkRouteAttr(m *NetlinkMessage) ([]NetlinkRouteAttr, error) {
+ var b []byte
+ switch m.Header.Type {
case RTM_NEWLINK, RTM_DELLINK:
- buf = msg.Data[SizeofIfInfomsg:]
+ b = m.Data[SizeofIfInfomsg:]
case RTM_NEWADDR, RTM_DELADDR:
- buf = msg.Data[SizeofIfAddrmsg:]
+ b = m.Data[SizeofIfAddrmsg:]
case RTM_NEWROUTE, RTM_DELROUTE:
- buf = msg.Data[SizeofRtMsg:]
+ b = m.Data[SizeofRtMsg:]
default:
return nil, EINVAL
}
-
- for len(buf) >= SizeofRtAttr {
- a, vbuf, alen, e = netlinkRouteAttrAndValue(buf)
- if e != nil {
- break
+ var attrs []NetlinkRouteAttr
+ for len(b) >= SizeofRtAttr {
+ a, vbuf, alen, err := netlinkRouteAttrAndValue(b)
+ if err != nil {
+ return nil, err
}
- ra := NetlinkRouteAttr{}
- ra.Attr = *a
- ra.Value = vbuf[:a.Len-SizeofRtAttr]
+ ra := NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-SizeofRtAttr]}
attrs = append(attrs, ra)
- buf = buf[alen:]
+ b = b[alen:]
}
-
return attrs, nil
}
-func netlinkRouteAttrAndValue(buf []byte) (*RtAttr, []byte, int, error) {
- h := (*RtAttr)(unsafe.Pointer(&buf[0]))
- if h.Len < SizeofRtAttr || int(h.Len) > len(buf) {
+func netlinkRouteAttrAndValue(b []byte) (*RtAttr, []byte, int, error) {
+ a := (*RtAttr)(unsafe.Pointer(&b[0]))
+ if int(a.Len) < SizeofRtAttr || int(a.Len) > len(b) {
return nil, nil, 0, EINVAL
}
- return h, buf[SizeofRtAttr:], rtaAlignOf(int(h.Len)), nil
+ return a, b[SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
}
diff --git a/src/pkg/syscall/passfd_test.go b/src/pkg/syscall/passfd_test.go
new file mode 100644
index 000000000..079c9303b
--- /dev/null
+++ b/src/pkg/syscall/passfd_test.go
@@ -0,0 +1,201 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux darwin freebsd netbsd openbsd
+
+package syscall_test
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "os"
+ "os/exec"
+ "runtime"
+ "syscall"
+ "testing"
+ "time"
+)
+
+// TestPassFD tests passing a file descriptor over a Unix socket.
+//
+// This test involved both a parent and child process. The parent
+// process is invoked as a normal test, with "go test", which then
+// runs the child process by running the current test binary with args
+// "-test.run=^TestPassFD$" and an environment variable used to signal
+// that the test should become the child process instead.
+func TestPassFD(t *testing.T) {
+ if runtime.GOOS == "openbsd" {
+ t.Skip("issue 4956")
+ }
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ passFDChild()
+ return
+ }
+
+ tempDir, err := ioutil.TempDir("", "TestPassFD")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tempDir)
+
+ fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0)
+ if err != nil {
+ t.Fatalf("Socketpair: %v", err)
+ }
+ defer syscall.Close(fds[0])
+ defer syscall.Close(fds[1])
+ writeFile := os.NewFile(uintptr(fds[0]), "child-writes")
+ readFile := os.NewFile(uintptr(fds[1]), "parent-reads")
+ defer writeFile.Close()
+ defer readFile.Close()
+
+ cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir)
+ cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...)
+ cmd.ExtraFiles = []*os.File{writeFile}
+
+ out, err := cmd.CombinedOutput()
+ if len(out) > 0 || err != nil {
+ t.Fatalf("child process: %q, %v", out, err)
+ }
+
+ c, err := net.FileConn(readFile)
+ if err != nil {
+ t.Fatalf("FileConn: %v", err)
+ }
+ defer c.Close()
+
+ uc, ok := c.(*net.UnixConn)
+ if !ok {
+ t.Fatalf("unexpected FileConn type; expected UnixConn, got %T", c)
+ }
+
+ buf := make([]byte, 32) // expect 1 byte
+ oob := make([]byte, 32) // expect 24 bytes
+ closeUnix := time.AfterFunc(5*time.Second, func() {
+ t.Logf("timeout reading from unix socket")
+ uc.Close()
+ })
+ _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob)
+ closeUnix.Stop()
+
+ scms, err := syscall.ParseSocketControlMessage(oob[:oobn])
+ if err != nil {
+ t.Fatalf("ParseSocketControlMessage: %v", err)
+ }
+ if len(scms) != 1 {
+ t.Fatalf("expected 1 SocketControlMessage; got scms = %#v", scms)
+ }
+ scm := scms[0]
+ gotFds, err := syscall.ParseUnixRights(&scm)
+ if err != nil {
+ t.Fatalf("syscall.ParseUnixRights: %v", err)
+ }
+ if len(gotFds) != 1 {
+ t.Fatalf("wanted 1 fd; got %#v", gotFds)
+ }
+
+ f := os.NewFile(uintptr(gotFds[0]), "fd-from-child")
+ defer f.Close()
+
+ got, err := ioutil.ReadAll(f)
+ want := "Hello from child process!\n"
+ if string(got) != want {
+ t.Errorf("child process ReadAll: %q, %v; want %q", got, err, want)
+ }
+}
+
+// passFDChild is the child process used by TestPassFD.
+func passFDChild() {
+ defer os.Exit(0)
+
+ // Look for our fd. It should be fd 3, but we work around an fd leak
+ // bug here (http://golang.org/issue/2603) to let it be elsewhere.
+ var uc *net.UnixConn
+ for fd := uintptr(3); fd <= 10; fd++ {
+ f := os.NewFile(fd, "unix-conn")
+ var ok bool
+ netc, _ := net.FileConn(f)
+ uc, ok = netc.(*net.UnixConn)
+ if ok {
+ break
+ }
+ }
+ if uc == nil {
+ fmt.Println("failed to find unix fd")
+ return
+ }
+
+ // Make a file f to send to our parent process on uc.
+ // We make it in tempDir, which our parent will clean up.
+ flag.Parse()
+ tempDir := flag.Arg(0)
+ f, err := ioutil.TempFile(tempDir, "")
+ if err != nil {
+ fmt.Printf("TempFile: %v", err)
+ return
+ }
+
+ f.Write([]byte("Hello from child process!\n"))
+ f.Seek(0, 0)
+
+ rights := syscall.UnixRights(int(f.Fd()))
+ dummyByte := []byte("x")
+ n, oobn, err := uc.WriteMsgUnix(dummyByte, rights, nil)
+ if err != nil {
+ fmt.Printf("WriteMsgUnix: %v", err)
+ return
+ }
+ if n != 1 || oobn != len(rights) {
+ fmt.Printf("WriteMsgUnix = %d, %d; want 1, %d", n, oobn, len(rights))
+ return
+ }
+}
+
+// TestUnixRightsRoundtrip tests that UnixRights, ParseSocketControlMessage,
+// and ParseUnixRights are able to successfully round-trip lists of file descriptors.
+func TestUnixRightsRoundtrip(t *testing.T) {
+ testCases := [...][][]int{
+ {{42}},
+ {{1, 2}},
+ {{3, 4, 5}},
+ {{}},
+ {{1, 2}, {3, 4, 5}, {}, {7}},
+ }
+ for _, testCase := range testCases {
+ b := []byte{}
+ var n int
+ for _, fds := range testCase {
+ // Last assignment to n wins
+ n = len(b) + syscall.CmsgLen(4*len(fds))
+ b = append(b, syscall.UnixRights(fds...)...)
+ }
+ // Truncate b
+ b = b[:n]
+
+ scms, err := syscall.ParseSocketControlMessage(b)
+ if err != nil {
+ t.Fatalf("ParseSocketControlMessage: %v", err)
+ }
+ if len(scms) != len(testCase) {
+ t.Fatalf("expected %v SocketControlMessage; got scms = %#v", len(testCase), scms)
+ }
+ for i, scm := range scms {
+ gotFds, err := syscall.ParseUnixRights(&scm)
+ if err != nil {
+ t.Fatalf("ParseUnixRights: %v", err)
+ }
+ wantFds := testCase[i]
+ if len(gotFds) != len(wantFds) {
+ t.Fatalf("expected %v fds, got %#v", len(wantFds), gotFds)
+ }
+ for j, fd := range gotFds {
+ if fd != wantFds[j] {
+ t.Fatalf("expected fd %v, got %v", wantFds[j], fd)
+ }
+ }
+ }
+ }
+}
diff --git a/src/pkg/syscall/race.go b/src/pkg/syscall/race.go
new file mode 100644
index 000000000..81778846f
--- /dev/null
+++ b/src/pkg/syscall/race.go
@@ -0,0 +1,22 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build race
+
+package syscall
+
+import (
+ "runtime"
+ "unsafe"
+)
+
+const raceenabled = true
+
+func raceAcquire(addr unsafe.Pointer) {
+ runtime.RaceAcquire(addr)
+}
+
+func raceReleaseMerge(addr unsafe.Pointer) {
+ runtime.RaceReleaseMerge(addr)
+}
diff --git a/src/pkg/syscall/race0.go b/src/pkg/syscall/race0.go
new file mode 100644
index 000000000..e94fb47af
--- /dev/null
+++ b/src/pkg/syscall/race0.go
@@ -0,0 +1,19 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !race
+
+package syscall
+
+import (
+ "unsafe"
+)
+
+const raceenabled = false
+
+func raceAcquire(addr unsafe.Pointer) {
+}
+
+func raceReleaseMerge(addr unsafe.Pointer) {
+}
diff --git a/src/pkg/syscall/route_bsd.go b/src/pkg/syscall/route_bsd.go
index e17d976b1..62c5ce1a3 100644
--- a/src/pkg/syscall/route_bsd.go
+++ b/src/pkg/syscall/route_bsd.go
@@ -8,9 +8,7 @@
package syscall
-import (
- "unsafe"
-)
+import "unsafe"
// Round the length of a raw sockaddr up to align it properly.
func rsaAlignOf(salen int) int {
@@ -31,7 +29,6 @@ func rsaAlignOf(salen int) int {
// parameters.
func RouteRIB(facility, param int) ([]byte, error) {
mib := []_C_int{CTL_NET, AF_ROUTE, 0, 0, _C_int(facility), _C_int(param)}
-
// Find size.
n := uintptr(0)
if err := sysctl(mib, nil, &n, nil, 0); err != nil {
@@ -40,12 +37,10 @@ func RouteRIB(facility, param int) ([]byte, error) {
if n == 0 {
return nil, nil
}
-
tab := make([]byte, n)
if err := sysctl(mib, &tab[0], &n, nil, 0); err != nil {
return nil, err
}
-
return tab[:n], nil
}
@@ -76,13 +71,12 @@ func (m *RouteMessage) sockaddr() []Sockaddr {
af int
sas [4]Sockaddr
)
-
- buf := m.Data[:]
+ b := m.Data[:]
for i := uint(0); i < RTAX_MAX; i++ {
if m.Header.Addrs&rtaRtMask&(1<<i) == 0 {
continue
}
- rsa := (*RawSockaddr)(unsafe.Pointer(&buf[0]))
+ rsa := (*RawSockaddr)(unsafe.Pointer(&b[0]))
switch i {
case RTAX_DST, RTAX_GATEWAY:
sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
@@ -96,14 +90,14 @@ func (m *RouteMessage) sockaddr() []Sockaddr {
case RTAX_NETMASK, RTAX_GENMASK:
switch af {
case AF_INET:
- rsa4 := (*RawSockaddrInet4)(unsafe.Pointer(&buf[0]))
+ rsa4 := (*RawSockaddrInet4)(unsafe.Pointer(&b[0]))
sa := new(SockaddrInet4)
for j := 0; rsa4.Len > 0 && j < int(rsa4.Len)-int(unsafe.Offsetof(rsa4.Addr)); j++ {
sa.Addr[j] = rsa4.Addr[j]
}
sas[i] = sa
case AF_INET6:
- rsa6 := (*RawSockaddrInet6)(unsafe.Pointer(&buf[0]))
+ rsa6 := (*RawSockaddrInet6)(unsafe.Pointer(&b[0]))
sa := new(SockaddrInet6)
for j := 0; rsa6.Len > 0 && j < int(rsa6.Len)-int(unsafe.Offsetof(rsa6.Addr)); j++ {
sa.Addr[j] = rsa6.Addr[j]
@@ -111,9 +105,8 @@ func (m *RouteMessage) sockaddr() []Sockaddr {
sas[i] = sa
}
}
- buf = buf[rsaAlignOf(int(rsa.Len)):]
+ b = b[rsaAlignOf(int(rsa.Len)):]
}
-
return sas[:]
}
@@ -148,13 +141,12 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
if m.Header.Addrs&rtaIfaMask == 0 {
return nil
}
-
- buf := m.Data[:]
+ b := m.Data[:]
for i := uint(0); i < RTAX_MAX; i++ {
if m.Header.Addrs&rtaIfaMask&(1<<i) == 0 {
continue
}
- rsa := (*RawSockaddr)(unsafe.Pointer(&buf[0]))
+ rsa := (*RawSockaddr)(unsafe.Pointer(&b[0]))
switch i {
case RTAX_IFA:
sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
@@ -174,22 +166,21 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
case RTAX_BRD:
// nothing to do
}
- buf = buf[rsaAlignOf(int(rsa.Len)):]
+ b = b[rsaAlignOf(int(rsa.Len)):]
}
-
return sas
}
-// ParseRoutingMessage parses buf as routing messages and returns
-// the slice containing the RoutingMessage interfaces.
-func ParseRoutingMessage(buf []byte) (msgs []RoutingMessage, err error) {
- for len(buf) >= anyMessageLen {
- any := (*anyMessage)(unsafe.Pointer(&buf[0]))
+// ParseRoutingMessage parses b as routing messages and returns the
+// slice containing the RoutingMessage interfaces.
+func ParseRoutingMessage(b []byte) (msgs []RoutingMessage, err error) {
+ for len(b) >= anyMessageLen {
+ any := (*anyMessage)(unsafe.Pointer(&b[0]))
if any.Version != RTM_VERSION {
return nil, EINVAL
}
- msgs = append(msgs, any.toRoutingMessage(buf))
- buf = buf[any.Msglen:]
+ msgs = append(msgs, any.toRoutingMessage(b))
+ b = b[any.Msglen:]
}
return msgs, nil
}
diff --git a/src/pkg/syscall/route_darwin.go b/src/pkg/syscall/route_darwin.go
index 410e70a13..ad2790723 100644
--- a/src/pkg/syscall/route_darwin.go
+++ b/src/pkg/syscall/route_darwin.go
@@ -6,36 +6,22 @@
package syscall
-import (
- "unsafe"
-)
+import "unsafe"
-func (any *anyMessage) toRoutingMessage(buf []byte) RoutingMessage {
+func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage {
switch any.Type {
case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE:
p := (*RouteMessage)(unsafe.Pointer(any))
- rtm := &RouteMessage{}
- rtm.Header = p.Header
- rtm.Data = buf[SizeofRtMsghdr:any.Msglen]
- return rtm
+ return &RouteMessage{Header: p.Header, Data: b[SizeofRtMsghdr:any.Msglen]}
case RTM_IFINFO:
p := (*InterfaceMessage)(unsafe.Pointer(any))
- ifm := &InterfaceMessage{}
- ifm.Header = p.Header
- ifm.Data = buf[SizeofIfMsghdr:any.Msglen]
- return ifm
+ return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
case RTM_NEWADDR, RTM_DELADDR:
p := (*InterfaceAddrMessage)(unsafe.Pointer(any))
- ifam := &InterfaceAddrMessage{}
- ifam.Header = p.Header
- ifam.Data = buf[SizeofIfaMsghdr:any.Msglen]
- return ifam
+ return &InterfaceAddrMessage{Header: p.Header, Data: b[SizeofIfaMsghdr:any.Msglen]}
case RTM_NEWMADDR2, RTM_DELMADDR:
p := (*InterfaceMulticastAddrMessage)(unsafe.Pointer(any))
- ifmam := &InterfaceMulticastAddrMessage{}
- ifmam.Header = p.Header
- ifmam.Data = buf[SizeofIfmaMsghdr2:any.Msglen]
- return ifmam
+ return &InterfaceMulticastAddrMessage{Header: p.Header, Data: b[SizeofIfmaMsghdr2:any.Msglen]}
}
return nil
}
@@ -53,13 +39,12 @@ func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) {
if m.Header.Addrs&rtaIfmaMask == 0 {
return nil
}
-
- buf := m.Data[:]
+ b := m.Data[:]
for i := uint(0); i < RTAX_MAX; i++ {
if m.Header.Addrs&rtaIfmaMask&(1<<i) == 0 {
continue
}
- rsa := (*RawSockaddr)(unsafe.Pointer(&buf[0]))
+ rsa := (*RawSockaddr)(unsafe.Pointer(&b[0]))
switch i {
case RTAX_IFA:
sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
@@ -70,8 +55,7 @@ func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) {
case RTAX_GATEWAY, RTAX_IFP:
// nothing to do
}
- buf = buf[rsaAlignOf(int(rsa.Len)):]
+ b = b[rsaAlignOf(int(rsa.Len)):]
}
-
return sas
}
diff --git a/src/pkg/syscall/route_freebsd.go b/src/pkg/syscall/route_freebsd.go
index 094e17044..d8f80316b 100644
--- a/src/pkg/syscall/route_freebsd.go
+++ b/src/pkg/syscall/route_freebsd.go
@@ -6,40 +6,37 @@
package syscall
-import (
- "unsafe"
-)
+import "unsafe"
-func (any *anyMessage) toRoutingMessage(buf []byte) RoutingMessage {
+func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage {
switch any.Type {
case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE:
p := (*RouteMessage)(unsafe.Pointer(any))
- rtm := &RouteMessage{}
- rtm.Header = p.Header
- rtm.Data = buf[SizeofRtMsghdr:any.Msglen]
- return rtm
+ return &RouteMessage{Header: p.Header, Data: b[SizeofRtMsghdr:any.Msglen]}
case RTM_IFINFO:
p := (*InterfaceMessage)(unsafe.Pointer(any))
- ifm := &InterfaceMessage{}
- ifm.Header = p.Header
- ifm.Data = buf[SizeofIfMsghdr:any.Msglen]
- return ifm
+ return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
+ case RTM_IFANNOUNCE:
+ p := (*InterfaceAnnounceMessage)(unsafe.Pointer(any))
+ return &InterfaceAnnounceMessage{Header: p.Header}
case RTM_NEWADDR, RTM_DELADDR:
p := (*InterfaceAddrMessage)(unsafe.Pointer(any))
- ifam := &InterfaceAddrMessage{}
- ifam.Header = p.Header
- ifam.Data = buf[SizeofIfaMsghdr:any.Msglen]
- return ifam
+ return &InterfaceAddrMessage{Header: p.Header, Data: b[SizeofIfaMsghdr:any.Msglen]}
case RTM_NEWMADDR, RTM_DELMADDR:
p := (*InterfaceMulticastAddrMessage)(unsafe.Pointer(any))
- ifmam := &InterfaceMulticastAddrMessage{}
- ifmam.Header = p.Header
- ifmam.Data = buf[SizeofIfmaMsghdr:any.Msglen]
- return ifmam
+ return &InterfaceMulticastAddrMessage{Header: p.Header, Data: b[SizeofIfmaMsghdr:any.Msglen]}
}
return nil
}
+// InterfaceAnnounceMessage represents a routing message containing
+// network interface arrival and depature information.
+type InterfaceAnnounceMessage struct {
+ Header IfAnnounceMsghdr
+}
+
+func (m *InterfaceAnnounceMessage) sockaddr() (sas []Sockaddr) { return nil }
+
// InterfaceMulticastAddrMessage represents a routing message
// containing network interface address entries.
type InterfaceMulticastAddrMessage struct {
@@ -53,13 +50,12 @@ func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) {
if m.Header.Addrs&rtaIfmaMask == 0 {
return nil
}
-
- buf := m.Data[:]
+ b := m.Data[:]
for i := uint(0); i < RTAX_MAX; i++ {
if m.Header.Addrs&rtaIfmaMask&(1<<i) == 0 {
continue
}
- rsa := (*RawSockaddr)(unsafe.Pointer(&buf[0]))
+ rsa := (*RawSockaddr)(unsafe.Pointer(&b[0]))
switch i {
case RTAX_IFA:
sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
@@ -70,8 +66,7 @@ func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) {
case RTAX_GATEWAY, RTAX_IFP:
// nothing to do
}
- buf = buf[rsaAlignOf(int(rsa.Len)):]
+ b = b[rsaAlignOf(int(rsa.Len)):]
}
-
return sas
}
diff --git a/src/pkg/syscall/route_netbsd.go b/src/pkg/syscall/route_netbsd.go
index d6d9031bc..a6baa02f8 100644
--- a/src/pkg/syscall/route_netbsd.go
+++ b/src/pkg/syscall/route_netbsd.go
@@ -6,30 +6,30 @@
package syscall
-import (
- "unsafe"
-)
+import "unsafe"
-func (any *anyMessage) toRoutingMessage(buf []byte) RoutingMessage {
+func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage {
switch any.Type {
case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE:
p := (*RouteMessage)(unsafe.Pointer(any))
- rtm := &RouteMessage{}
- rtm.Header = p.Header
- rtm.Data = buf[SizeofRtMsghdr:any.Msglen]
- return rtm
+ return &RouteMessage{Header: p.Header, Data: b[SizeofRtMsghdr:any.Msglen]}
case RTM_IFINFO:
p := (*InterfaceMessage)(unsafe.Pointer(any))
- ifm := &InterfaceMessage{}
- ifm.Header = p.Header
- ifm.Data = buf[SizeofIfMsghdr:any.Msglen]
- return ifm
+ return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
+ case RTM_IFANNOUNCE:
+ p := (*InterfaceAnnounceMessage)(unsafe.Pointer(any))
+ return &InterfaceAnnounceMessage{Header: p.Header}
case RTM_NEWADDR, RTM_DELADDR:
p := (*InterfaceAddrMessage)(unsafe.Pointer(any))
- ifam := &InterfaceAddrMessage{}
- ifam.Header = p.Header
- ifam.Data = buf[SizeofIfaMsghdr:any.Msglen]
- return ifam
+ return &InterfaceAddrMessage{Header: p.Header, Data: b[SizeofIfaMsghdr:any.Msglen]}
}
return nil
}
+
+// InterfaceAnnounceMessage represents a routing message containing
+// network interface arrival and depature information.
+type InterfaceAnnounceMessage struct {
+ Header IfAnnounceMsghdr
+}
+
+func (m *InterfaceAnnounceMessage) sockaddr() (sas []Sockaddr) { return nil }
diff --git a/src/pkg/syscall/route_openbsd.go b/src/pkg/syscall/route_openbsd.go
index 30e1cac46..223c15779 100644
--- a/src/pkg/syscall/route_openbsd.go
+++ b/src/pkg/syscall/route_openbsd.go
@@ -6,30 +6,30 @@
package syscall
-import (
- "unsafe"
-)
+import "unsafe"
-func (any *anyMessage) toRoutingMessage(buf []byte) RoutingMessage {
+func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage {
switch any.Type {
case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE:
p := (*RouteMessage)(unsafe.Pointer(any))
- rtm := &RouteMessage{}
- rtm.Header = p.Header
- rtm.Data = buf[SizeofRtMsghdr:any.Msglen]
- return rtm
+ return &RouteMessage{Header: p.Header, Data: b[SizeofRtMsghdr:any.Msglen]}
case RTM_IFINFO:
p := (*InterfaceMessage)(unsafe.Pointer(any))
- ifm := &InterfaceMessage{}
- ifm.Header = p.Header
- ifm.Data = buf[SizeofIfMsghdr:any.Msglen]
- return ifm
+ return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
+ case RTM_IFANNOUNCE:
+ p := (*InterfaceAnnounceMessage)(unsafe.Pointer(any))
+ return &InterfaceAnnounceMessage{Header: p.Header}
case RTM_NEWADDR, RTM_DELADDR:
p := (*InterfaceAddrMessage)(unsafe.Pointer(any))
- ifam := &InterfaceAddrMessage{}
- ifam.Header = p.Header
- ifam.Data = buf[SizeofIfaMsghdr:any.Msglen]
- return ifam
+ return &InterfaceAddrMessage{Header: p.Header, Data: b[SizeofIfaMsghdr:any.Msglen]}
}
return nil
}
+
+// InterfaceAnnounceMessage represents a routing message containing
+// network interface arrival and depature information.
+type InterfaceAnnounceMessage struct {
+ Header IfAnnounceMsghdr
+}
+
+func (m *InterfaceAnnounceMessage) sockaddr() (sas []Sockaddr) { return nil }
diff --git a/src/pkg/syscall/security_windows.go b/src/pkg/syscall/security_windows.go
index bd40fe586..017b27014 100644
--- a/src/pkg/syscall/security_windows.go
+++ b/src/pkg/syscall/security_windows.go
@@ -37,10 +37,13 @@ const (
// TranslateAccountName converts a directory service
// object name from one format to another.
func TranslateAccountName(username string, from, to uint32, initSize int) (string, error) {
- u := StringToUTF16Ptr(username)
+ u, e := UTF16PtrFromString(username)
+ if e != nil {
+ return "", e
+ }
b := make([]uint16, 50)
n := uint32(len(b))
- e := TranslateName(u, from, to, &b[0], &n)
+ e = TranslateName(u, from, to, &b[0], &n)
if e != nil {
if e != ERROR_INSUFFICIENT_BUFFER {
return "", e
@@ -67,7 +70,7 @@ type UserInfo10 struct {
const (
// do not reorder
- SidTypeUser = 1 << iota
+ SidTypeUser = 1 + iota
SidTypeGroup
SidTypeDomain
SidTypeAlias
@@ -94,7 +97,11 @@ type SID struct{}
// sid into a valid, functional sid.
func StringToSid(s string) (*SID, error) {
var sid *SID
- e := ConvertStringSidToSid(StringToUTF16Ptr(s), &sid)
+ p, e := UTF16PtrFromString(s)
+ if e != nil {
+ return nil, e
+ }
+ e = ConvertStringSidToSid(p, &sid)
if e != nil {
return nil, e
}
@@ -109,17 +116,23 @@ func LookupSID(system, account string) (sid *SID, domain string, accType uint32,
if len(account) == 0 {
return nil, "", 0, EINVAL
}
- acc := StringToUTF16Ptr(account)
+ acc, e := UTF16PtrFromString(account)
+ if e != nil {
+ return nil, "", 0, e
+ }
var sys *uint16
if len(system) > 0 {
- sys = StringToUTF16Ptr(system)
+ sys, e = UTF16PtrFromString(system)
+ if e != nil {
+ return nil, "", 0, e
+ }
}
db := make([]uint16, 50)
dn := uint32(len(db))
b := make([]byte, 50)
n := uint32(len(b))
sid = (*SID)(unsafe.Pointer(&b[0]))
- e := LookupAccountName(sys, acc, sid, &n, &db[0], &dn, &accType)
+ e = LookupAccountName(sys, acc, sid, &n, &db[0], &dn, &accType)
if e != nil {
if e != ERROR_INSUFFICIENT_BUFFER {
return nil, "", 0, e
@@ -170,7 +183,10 @@ func (sid *SID) Copy() (*SID, error) {
func (sid *SID) LookupAccount(system string) (account, domain string, accType uint32, err error) {
var sys *uint16
if len(system) > 0 {
- sys = StringToUTF16Ptr(system)
+ sys, err = UTF16PtrFromString(system)
+ if err != nil {
+ return "", "", 0, err
+ }
}
b := make([]uint16, 50)
n := uint32(len(b))
diff --git a/src/pkg/syscall/sockcmsg_linux.go b/src/pkg/syscall/sockcmsg_linux.go
index 0b4caa1d0..a2e26a1f4 100644
--- a/src/pkg/syscall/sockcmsg_linux.go
+++ b/src/pkg/syscall/sockcmsg_linux.go
@@ -6,33 +6,31 @@
package syscall
-import (
- "unsafe"
-)
+import "unsafe"
// UnixCredentials encodes credentials into a socket control message
// for sending to another process. This can be used for
// authentication.
func UnixCredentials(ucred *Ucred) []byte {
- buf := make([]byte, CmsgSpace(SizeofUcred))
- cmsg := (*Cmsghdr)(unsafe.Pointer(&buf[0]))
- cmsg.Level = SOL_SOCKET
- cmsg.Type = SCM_CREDENTIALS
- cmsg.SetLen(CmsgLen(SizeofUcred))
- *((*Ucred)(cmsgData(cmsg))) = *ucred
- return buf
+ b := make([]byte, CmsgSpace(SizeofUcred))
+ h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
+ h.Level = SOL_SOCKET
+ h.Type = SCM_CREDENTIALS
+ h.SetLen(CmsgLen(SizeofUcred))
+ *((*Ucred)(cmsgData(h))) = *ucred
+ return b
}
// ParseUnixCredentials decodes a socket control message that contains
// credentials in a Ucred structure. To receive such a message, the
// SO_PASSCRED option must be enabled on the socket.
-func ParseUnixCredentials(msg *SocketControlMessage) (*Ucred, error) {
- if msg.Header.Level != SOL_SOCKET {
+func ParseUnixCredentials(m *SocketControlMessage) (*Ucred, error) {
+ if m.Header.Level != SOL_SOCKET {
return nil, EINVAL
}
- if msg.Header.Type != SCM_CREDENTIALS {
+ if m.Header.Type != SCM_CREDENTIALS {
return nil, EINVAL
}
- ucred := *(*Ucred)(unsafe.Pointer(&msg.Data[0]))
+ ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
return &ucred, nil
}
diff --git a/src/pkg/syscall/sockcmsg_unix.go b/src/pkg/syscall/sockcmsg_unix.go
index d279decb6..bc7cf2098 100644
--- a/src/pkg/syscall/sockcmsg_unix.go
+++ b/src/pkg/syscall/sockcmsg_unix.go
@@ -8,11 +8,9 @@
package syscall
-import (
- "unsafe"
-)
+import "unsafe"
-// Round the length of a raw sockaddr up to align it propery.
+// Round the length of a raw sockaddr up to align it properly.
func cmsgAlignOf(salen int) int {
salign := sizeofPtr
// NOTE: It seems like 64-bit Darwin kernel still requires 32-bit
@@ -20,9 +18,6 @@ func cmsgAlignOf(salen int) int {
if darwinAMD64 {
salign = 4
}
- if salen == 0 {
- return salign
- }
return (salen + salign - 1) & ^(salign - 1)
}
@@ -38,77 +33,70 @@ func CmsgSpace(datalen int) int {
return cmsgAlignOf(SizeofCmsghdr) + cmsgAlignOf(datalen)
}
-func cmsgData(cmsg *Cmsghdr) unsafe.Pointer {
- return unsafe.Pointer(uintptr(unsafe.Pointer(cmsg)) + SizeofCmsghdr)
+func cmsgData(h *Cmsghdr) unsafe.Pointer {
+ return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(SizeofCmsghdr)))
}
+// SocketControlMessage represents a socket control message.
type SocketControlMessage struct {
Header Cmsghdr
Data []byte
}
-func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, error) {
- var (
- h *Cmsghdr
- dbuf []byte
- e error
- cmsgs []SocketControlMessage
- )
-
- for len(buf) >= CmsgLen(0) {
- h, dbuf, e = socketControlMessageHeaderAndData(buf)
- if e != nil {
- break
+// ParseSocketControlMessage parses b as an array of socket control
+// messages.
+func ParseSocketControlMessage(b []byte) ([]SocketControlMessage, error) {
+ var msgs []SocketControlMessage
+ i := 0
+ for i+CmsgLen(0) <= len(b) {
+ h, dbuf, err := socketControlMessageHeaderAndData(b[i:])
+ if err != nil {
+ return nil, err
}
- m := SocketControlMessage{}
- m.Header = *h
- m.Data = dbuf[:int(h.Len)-cmsgAlignOf(SizeofCmsghdr)]
- cmsgs = append(cmsgs, m)
- buf = buf[cmsgAlignOf(int(h.Len)):]
+ m := SocketControlMessage{Header: *h, Data: dbuf}
+ msgs = append(msgs, m)
+ i += cmsgAlignOf(int(h.Len))
}
-
- return cmsgs, e
+ return msgs, nil
}
-func socketControlMessageHeaderAndData(buf []byte) (*Cmsghdr, []byte, error) {
- h := (*Cmsghdr)(unsafe.Pointer(&buf[0]))
- if h.Len < SizeofCmsghdr || int(h.Len) > len(buf) {
+func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) {
+ h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
+ if h.Len < SizeofCmsghdr || int(h.Len) > len(b) {
return nil, nil, EINVAL
}
- return h, buf[cmsgAlignOf(SizeofCmsghdr):], nil
+ return h, b[cmsgAlignOf(SizeofCmsghdr):h.Len], nil
}
// UnixRights encodes a set of open file descriptors into a socket
// control message for sending to another process.
func UnixRights(fds ...int) []byte {
datalen := len(fds) * 4
- buf := make([]byte, CmsgSpace(datalen))
- cmsg := (*Cmsghdr)(unsafe.Pointer(&buf[0]))
- cmsg.Level = SOL_SOCKET
- cmsg.Type = SCM_RIGHTS
- cmsg.SetLen(CmsgLen(datalen))
-
- data := uintptr(cmsgData(cmsg))
+ b := make([]byte, CmsgSpace(datalen))
+ h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
+ h.Level = SOL_SOCKET
+ h.Type = SCM_RIGHTS
+ h.SetLen(CmsgLen(datalen))
+ data := uintptr(cmsgData(h))
for _, fd := range fds {
*(*int32)(unsafe.Pointer(data)) = int32(fd)
data += 4
}
-
- return buf
+ return b
}
// ParseUnixRights decodes a socket control message that contains an
// integer array of open file descriptors from another process.
-func ParseUnixRights(msg *SocketControlMessage) ([]int, error) {
- if msg.Header.Level != SOL_SOCKET {
+func ParseUnixRights(m *SocketControlMessage) ([]int, error) {
+ if m.Header.Level != SOL_SOCKET {
return nil, EINVAL
}
- if msg.Header.Type != SCM_RIGHTS {
+ if m.Header.Type != SCM_RIGHTS {
return nil, EINVAL
}
- fds := make([]int, len(msg.Data)>>2)
- for i, j := 0, 0; i < len(msg.Data); i += 4 {
- fds[j] = int(*(*int32)(unsafe.Pointer(&msg.Data[i])))
+ fds := make([]int, len(m.Data)>>2)
+ for i, j := 0, 0; i < len(m.Data); i += 4 {
+ fds[j] = int(*(*int32)(unsafe.Pointer(&m.Data[i])))
j++
}
return fds, nil
diff --git a/src/pkg/syscall/syscall.go b/src/pkg/syscall/syscall.go
index 335559fc3..f7473fd5a 100644
--- a/src/pkg/syscall/syscall.go
+++ b/src/pkg/syscall/syscall.go
@@ -3,10 +3,15 @@
// license that can be found in the LICENSE file.
// Package syscall contains an interface to the low-level operating system
-// primitives. The details vary depending on the underlying system.
-// Its primary use is inside other packages that provide a more portable
-// interface to the system, such as "os", "time" and "net". Use those
-// packages rather than this one if you can.
+// primitives. The details vary depending on the underlying system, and
+// by default, godoc will display the syscall documentation for the current
+// system. If you want godoc to display syscall documentation for another
+// system, set $GOOS and $GOARCH to the desired system. For example, if
+// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
+// to freebsd and $GOARCH to arm.
+// The primary use of syscall is inside other packages that provide a more
+// portable interface to the system, such as "os", "time" and "net". Use
+// those packages rather than this one if you can.
// For details of the functions and data types in this package consult
// the manuals for the appropriate operating system.
// These calls return err == nil to indicate success; otherwise
@@ -14,18 +19,47 @@
// On most systems, that error has type syscall.Errno.
package syscall
-// StringByteSlice returns a NUL-terminated slice of bytes
-// containing the text of s.
+// StringByteSlice is deprecated. Use ByteSliceFromString instead.
+// If s contains a NUL byte this function panics instead of
+// returning an error.
func StringByteSlice(s string) []byte {
+ a, err := ByteSliceFromString(s)
+ if err != nil {
+ panic("syscall: string with NUL passed to StringByteSlice")
+ }
+ return a
+}
+
+// ByteSliceFromString returns a NUL-terminated slice of bytes
+// containing the text of s. If s contains a NUL byte at any
+// location, it returns (nil, EINVAL).
+func ByteSliceFromString(s string) ([]byte, error) {
+ for i := 0; i < len(s); i++ {
+ if s[i] == 0 {
+ return nil, EINVAL
+ }
+ }
a := make([]byte, len(s)+1)
copy(a, s)
- return a
+ return a, nil
}
-// StringBytePtr returns a pointer to a NUL-terminated array of bytes
-// containing the text of s.
+// StringBytePtr is deprecated. Use BytePtrFromString instead.
+// If s contains a NUL byte this function panics instead of
+// returning an error.
func StringBytePtr(s string) *byte { return &StringByteSlice(s)[0] }
+// BytePtrFromString returns a pointer to a NUL-terminated array of
+// bytes containing the text of s. If s contains a NUL byte at any
+// location, it returns (nil, EINVAL).
+func BytePtrFromString(s string) (*byte, error) {
+ a, err := ByteSliceFromString(s)
+ if err != nil {
+ return nil, err
+ }
+ return &a[0], nil
+}
+
// Single-word zero for use when we need a valid pointer to 0 bytes.
// See mksyscall.pl.
var _zero uintptr
diff --git a/src/pkg/syscall/syscall_bsd.go b/src/pkg/syscall/syscall_bsd.go
index c1a822aa1..a1e0d153f 100644
--- a/src/pkg/syscall/syscall_bsd.go
+++ b/src/pkg/syscall/syscall_bsd.go
@@ -20,6 +20,7 @@ import (
/*
* Pseudo-system calls
*/
+
// The const provides a compile-time constant so clients
// can adjust to whether there is a working Getwd and avoid
// even linking this function into the binary. See ../os/getwd.go.
@@ -304,6 +305,14 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) {
if err != nil {
return
}
+ if runtime.GOOS == "darwin" && len == 0 {
+ // Accepted socket has no address.
+ // This is likely due to a bug in xnu kernels,
+ // where instead of ECONNABORTED error socket
+ // is accepted, but has no address.
+ Close(nfd)
+ return 0, nil, ECONNABORTED
+ }
sa, err = anyToSockaddr(&rsa)
if err != nil {
Close(nfd)
@@ -354,10 +363,15 @@ func Socket(domain, typ, proto int) (fd int, err error) {
return
}
-//sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (err error)
+//sysnb socketpair(domain int, typ int, proto int, fd *[2]int32) (err error)
func Socketpair(domain, typ, proto int) (fd [2]int, err error) {
- err = socketpair(domain, typ, proto, &fd)
+ var fdx [2]int32
+ err = socketpair(domain, typ, proto, &fdx)
+ if err == nil {
+ fd[0] = int(fdx[0])
+ fd[1] = int(fdx[1])
+ }
return
}
@@ -437,7 +451,9 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
if n, err = recvfrom(fd, p, flags, &rsa, &len); err != nil {
return
}
- from, err = anyToSockaddr(&rsa)
+ if rsa.Addr.Family != AF_UNSPEC {
+ from, err = anyToSockaddr(&rsa)
+ }
return
}
@@ -553,16 +569,7 @@ func Sysctl(name string) (value string, err error) {
return "", err
}
if n == 0 {
- // TODO(jsing): Remove after OpenBSD 5.2 release.
- // Work around a bug that was fixed after OpenBSD 5.0.
- // The length for kern.hostname and kern.domainname is always
- // returned as 0 when a nil value is passed for oldp.
- if runtime.GOOS == "openbsd" && (name == "kern.hostname" || name == "kern.domainname") {
- // MAXHOSTNAMELEN
- n = 256
- } else {
- return "", nil
- }
+ return "", nil
}
// Read into buffer of that size.
@@ -605,6 +612,21 @@ func Utimes(path string, tv []Timeval) (err error) {
return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}
+func UtimesNano(path string, ts []Timespec) error {
+ // TODO: The BSDs can do utimensat with SYS_UTIMENSAT but it
+ // isn't supported by darwin so this uses utimes instead
+ if len(ts) != 2 {
+ return EINVAL
+ }
+ // Not as efficient as it could be because Timespec and
+ // Timeval have different types in the different OSes
+ tv := [2]Timeval{
+ NsecToTimeval(TimespecToNsec(ts[0])),
+ NsecToTimeval(TimespecToNsec(ts[1])),
+ }
+ return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
+}
+
//sys futimes(fd int, timeval *[2]Timeval) (err error)
func Futimes(fd int, tv []Timeval) (err error) {
if len(tv) != 2 {
diff --git a/src/pkg/syscall/syscall_darwin.go b/src/pkg/syscall/syscall_darwin.go
index c712f94d4..7fe406aaa 100644
--- a/src/pkg/syscall/syscall_darwin.go
+++ b/src/pkg/syscall/syscall_darwin.go
@@ -41,7 +41,10 @@ func nametomib(name string) (mib []_C_int, err error) {
n := uintptr(CTL_MAXNAME) * siz
p := (*byte)(unsafe.Pointer(&buf[0]))
- bytes := StringByteSlice(name)
+ bytes, err := ByteSliceFromString(name)
+ if err != nil {
+ return nil, err
+ }
// Magic sysctl: "setting" 0.3 to a string name
// lets you read back the array of integers form.
@@ -94,7 +97,7 @@ func Pipe(p []int) (err error) {
}
// TODO
-func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
return -1, ENOSYS
}
@@ -159,7 +162,7 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1)
//sys Pathconf(path string, name int) (val int, err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error)
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
-//sys Read(fd int, p []byte) (n int, err error)
+//sys read(fd int, p []byte) (n int, err error)
//sys Readlink(path string, buf []byte) (n int, err error)
//sys Rename(from string, to string) (err error)
//sys Revoke(path string) (err error)
@@ -188,11 +191,11 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1)
//sys Undelete(path string) (err error)
//sys Unlink(path string) (err error)
//sys Unmount(path string, flags int) (err error)
-//sys Write(fd int, p []byte) (n int, err error)
+//sys write(fd int, p []byte) (n int, err error)
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
//sys munmap(addr uintptr, length uintptr) (err error)
-//sys read(fd int, buf *byte, nbuf int) (n int, err error)
-//sys write(fd int, buf *byte, nbuf int) (n int, err error)
+//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ
+//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE
/*
* Unimplemented
@@ -331,7 +334,7 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1)
// __pthread_canceled
// __semwait_signal
// Proc_info
-// Sendfile
+// sendfile
// Stat64_extended
// Lstat64_extended
// Fstat64_extended
diff --git a/src/pkg/syscall/syscall_freebsd.go b/src/pkg/syscall/syscall_freebsd.go
index 6556ea8de..c7ffe223e 100644
--- a/src/pkg/syscall/syscall_freebsd.go
+++ b/src/pkg/syscall/syscall_freebsd.go
@@ -41,7 +41,10 @@ func nametomib(name string) (mib []_C_int, err error) {
n := uintptr(CTL_MAXNAME) * siz
p := (*byte)(unsafe.Pointer(&buf[0]))
- bytes := StringByteSlice(name)
+ bytes, err := ByteSliceFromString(name)
+ if err != nil {
+ return nil, err
+ }
// Magic sysctl: "setting" 0.3 to a string name
// lets you read back the array of integers form.
@@ -89,11 +92,6 @@ func Pipe(p []int) (err error) {
return
}
-// TODO
-func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
- return -1, ENOSYS
-}
-
func GetsockoptIPMreqn(fd, level, opt int) (*IPMreqn, error) {
var value IPMreqn
vallen := _Socklen(SizeofIPMreqn)
@@ -160,7 +158,7 @@ func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) {
//sys Pathconf(path string, name int) (val int, err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error)
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
-//sys Read(fd int, p []byte) (n int, err error)
+//sys read(fd int, p []byte) (n int, err error)
//sys Readlink(path string, buf []byte) (n int, err error)
//sys Rename(from string, to string) (err error)
//sys Revoke(path string) (err error)
@@ -188,11 +186,11 @@ func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) {
//sys Undelete(path string) (err error)
//sys Unlink(path string) (err error)
//sys Unmount(path string, flags int) (err error)
-//sys Write(fd int, p []byte) (n int, err error)
+//sys write(fd int, p []byte) (n int, err error)
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
//sys munmap(addr uintptr, length uintptr) (err error)
-//sys read(fd int, buf *byte, nbuf int) (n int, err error)
-//sys write(fd int, buf *byte, nbuf int) (n int, err error)
+//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ
+//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE
/*
* Unimplemented
@@ -331,7 +329,6 @@ func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) {
// __pthread_canceled
// __semwait_signal
// Proc_info
-// Sendfile
// Stat64_extended
// Lstat64_extended
// Fstat64_extended
diff --git a/src/pkg/syscall/syscall_freebsd_386.go b/src/pkg/syscall/syscall_freebsd_386.go
index fa322c596..ebd3d4c9c 100644
--- a/src/pkg/syscall/syscall_freebsd_386.go
+++ b/src/pkg/syscall/syscall_freebsd_386.go
@@ -4,6 +4,8 @@
package syscall
+import "unsafe"
+
func Getpagesize() int { return 4096 }
func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
@@ -41,4 +43,16 @@ func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+ var writtenOut uint64 = 0
+ _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0)
+
+ written = int(writtenOut)
+
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) // sic
diff --git a/src/pkg/syscall/syscall_freebsd_amd64.go b/src/pkg/syscall/syscall_freebsd_amd64.go
index 8c1ddf6db..70c2ffb03 100644
--- a/src/pkg/syscall/syscall_freebsd_amd64.go
+++ b/src/pkg/syscall/syscall_freebsd_amd64.go
@@ -4,6 +4,8 @@
package syscall
+import "unsafe"
+
func Getpagesize() int { return 4096 }
func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
@@ -40,3 +42,17 @@ func (msghdr *Msghdr) SetControllen(length int) {
func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}
+
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+ var writtenOut uint64 = 0
+ _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0, 0)
+
+ written = int(writtenOut)
+
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
diff --git a/src/pkg/syscall/syscall_freebsd_arm.go b/src/pkg/syscall/syscall_freebsd_arm.go
new file mode 100644
index 000000000..ab72871dd
--- /dev/null
+++ b/src/pkg/syscall/syscall_freebsd_arm.go
@@ -0,0 +1,58 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syscall
+
+import "unsafe"
+
+func Getpagesize() int { return 4096 }
+
+func TimespecToNsec(ts Timespec) int64 { return ts.Sec*1e9 + int64(ts.Nsec) }
+
+func NsecToTimespec(nsec int64) (ts Timespec) {
+ ts.Sec = nsec / 1e9
+ ts.Nsec = int32(nsec % 1e9)
+ return
+}
+
+func TimevalToNsec(tv Timeval) int64 { return tv.Sec*1e9 + int64(tv.Usec)*1e3 }
+
+func NsecToTimeval(nsec int64) (tv Timeval) {
+ nsec += 999 // round up to microsecond
+ tv.Usec = int32(nsec % 1e9 / 1e3)
+ tv.Sec = nsec / 1e9
+ return
+}
+
+func SetKevent(k *Kevent_t, fd, mode, flags int) {
+ k.Ident = uint32(fd)
+ k.Filter = int16(mode)
+ k.Flags = uint16(flags)
+}
+
+func (iov *Iovec) SetLen(length int) {
+ iov.Len = uint32(length)
+}
+
+func (msghdr *Msghdr) SetControllen(length int) {
+ msghdr.Controllen = uint32(length)
+}
+
+func (cmsg *Cmsghdr) SetLen(length int) {
+ cmsg.Len = uint32(length)
+}
+
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+ var writtenOut uint64 = 0
+ _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0)
+
+ written = int(writtenOut)
+
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) // sic
diff --git a/src/pkg/syscall/syscall_linux.go b/src/pkg/syscall/syscall_linux.go
index 89fb68192..4f458be73 100644
--- a/src/pkg/syscall/syscall_linux.go
+++ b/src/pkg/syscall/syscall_linux.go
@@ -18,16 +18,19 @@ import "unsafe"
*/
//sys open(path string, mode int, perm uint32) (fd int, err error)
+
func Open(path string, mode int, perm uint32) (fd int, err error) {
return open(path, mode|O_LARGEFILE, perm)
}
//sys openat(dirfd int, path string, flags int, mode uint32) (fd int, err error)
+
func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
return openat(dirfd, path, flags|O_LARGEFILE, mode)
}
//sysnb pipe(p *[2]_C_int) (err error)
+
func Pipe(p []int) (err error) {
if len(p) != 2 {
return EINVAL
@@ -39,7 +42,21 @@ func Pipe(p []int) (err error) {
return
}
+//sysnb pipe2(p *[2]_C_int, flags int) (err error)
+
+func Pipe2(p []int, flags int) (err error) {
+ if len(p) != 2 {
+ return EINVAL
+ }
+ var pp [2]_C_int
+ err = pipe2(&pp, flags)
+ p[0] = int(pp[0])
+ p[1] = int(pp[1])
+ return
+}
+
//sys utimes(path string, times *[2]Timeval) (err error)
+
func Utimes(path string, tv []Timeval) (err error) {
if len(tv) != 2 {
return EINVAL
@@ -47,12 +64,37 @@ func Utimes(path string, tv []Timeval) (err error) {
return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}
+//sys utimensat(dirfd int, path string, times *[2]Timespec) (err error)
+
+func UtimesNano(path string, ts []Timespec) (err error) {
+ if len(ts) != 2 {
+ return EINVAL
+ }
+ err = utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])))
+ if err != ENOSYS {
+ return err
+ }
+ // If the utimensat syscall isn't available (utimensat was added to Linux
+ // in 2.6.22, Released, 8 July 2007) then fall back to utimes
+ var tv [2]Timeval
+ for i := 0; i < 2; i++ {
+ tv[i].Sec = ts[i].Sec
+ tv[i].Usec = ts[i].Nsec / 1000
+ }
+ return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
+}
+
//sys futimesat(dirfd int, path *byte, times *[2]Timeval) (err error)
+
func Futimesat(dirfd int, path string, tv []Timeval) (err error) {
if len(tv) != 2 {
return EINVAL
}
- return futimesat(dirfd, StringBytePtr(path), (*[2]Timeval)(unsafe.Pointer(&tv[0])))
+ pathp, err := BytePtrFromString(path)
+ if err != nil {
+ return err
+ }
+ return futimesat(dirfd, pathp, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}
func Futimes(fd int, tv []Timeval) (err error) {
@@ -64,6 +106,7 @@ func Futimes(fd int, tv []Timeval) (err error) {
const ImplementsGetwd = true
//sys Getcwd(buf []byte) (n int, err error)
+
func Getwd() (wd string, err error) {
var buf [PathMax]byte
n, err := Getcwd(buf[0:])
@@ -173,6 +216,7 @@ func (w WaitStatus) TrapCause() int {
}
//sys wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error)
+
func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
var status _C_int
wpid, err = wait4(pid, &status, options, rusage)
@@ -244,7 +288,7 @@ type SockaddrUnix struct {
func (sa *SockaddrUnix) sockaddr() (uintptr, _Socklen, error) {
name := sa.Name
n := len(name)
- if n >= len(sa.raw.Path) || n == 0 {
+ if n >= len(sa.raw.Path) {
return 0, 0, EINVAL
}
sa.raw.Family = AF_UNIX
@@ -252,7 +296,10 @@ func (sa *SockaddrUnix) sockaddr() (uintptr, _Socklen, error) {
sa.raw.Path[i] = int8(name[i])
}
// length is family (uint16), name, NUL.
- sl := 2 + _Socklen(n) + 1
+ sl := _Socklen(2)
+ if n > 0 {
+ sl += _Socklen(n) + 1
+ }
if sa.raw.Path[0] == '@' {
sa.raw.Path[0] = 0
// Don't count trailing NUL for abstract address.
@@ -392,6 +439,21 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) {
return
}
+func Accept4(fd int, flags int) (nfd int, sa Sockaddr, err error) {
+ var rsa RawSockaddrAny
+ var len _Socklen = SizeofSockaddrAny
+ nfd, err = accept4(fd, &rsa, &len, flags)
+ if err != nil {
+ return
+ }
+ sa, err = anyToSockaddr(&rsa)
+ if err != nil {
+ Close(nfd)
+ nfd = 0
+ }
+ return
+}
+
func Getsockname(fd int) (sa Sockaddr, err error) {
var rsa RawSockaddrAny
var len _Socklen = SizeofSockaddrAny
@@ -435,7 +497,12 @@ func Socket(domain, typ, proto int) (fd int, err error) {
}
func Socketpair(domain, typ, proto int) (fd [2]int, err error) {
- err = socketpair(domain, typ, proto, &fd)
+ var fdx [2]int32
+ err = socketpair(domain, typ, proto, &fdx)
+ if err == nil {
+ fd[0] = int(fdx[0])
+ fd[1] = int(fdx[1])
+ }
return
}
@@ -473,6 +540,13 @@ func GetsockoptIPv6Mreq(fd, level, opt int) (*IPv6Mreq, error) {
return &value, err
}
+func GetsockoptUcred(fd, level, opt int) (*Ucred, error) {
+ var value Ucred
+ vallen := _Socklen(SizeofUcred)
+ err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen)
+ return &value, err
+}
+
func SetsockoptInt(fd, level, opt int, value int) (err error) {
var n = int32(value)
return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), 4)
@@ -512,7 +586,9 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
if n, err = recvfrom(fd, p, flags, &rsa, &len); err != nil {
return
}
- from, err = anyToSockaddr(&rsa)
+ if rsa.Addr.Family != AF_UNSPEC {
+ from, err = anyToSockaddr(&rsa)
+ }
return
}
@@ -731,6 +807,10 @@ func PtraceCont(pid int, signal int) (err error) {
return ptrace(PTRACE_CONT, pid, 0, uintptr(signal))
}
+func PtraceSyscall(pid int, signal int) (err error) {
+ return ptrace(PTRACE_SYSCALL, pid, 0, uintptr(signal))
+}
+
func PtraceSingleStep(pid int) (err error) { return ptrace(PTRACE_SINGLESTEP, pid, 0, 0) }
func PtraceAttach(pid int) (err error) { return ptrace(PTRACE_ATTACH, pid, 0, 0) }
@@ -738,6 +818,7 @@ func PtraceAttach(pid int) (err error) { return ptrace(PTRACE_ATTACH, pid, 0, 0)
func PtraceDetach(pid int) (err error) { return ptrace(PTRACE_DETACH, pid, 0, 0) }
//sys reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error)
+
func Reboot(cmd int) (err error) {
return reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, "")
}
@@ -777,13 +858,18 @@ func ParseDirent(buf []byte, max int, names []string) (consumed int, count int,
}
//sys mount(source string, target string, fstype string, flags uintptr, data *byte) (err error)
+
func Mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
// Certain file systems get rather angry and EINVAL if you give
// them an empty string of data, rather than NULL.
if data == "" {
return mount(source, target, fstype, flags, nil)
}
- return mount(source, target, fstype, flags, StringBytePtr(data))
+ datap, err := BytePtrFromString(data)
+ if err != nil {
+ return err
+ }
+ return mount(source, target, fstype, flags, datap)
}
// Sendto
@@ -823,9 +909,9 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sysnb Getpgrp() (pid int)
//sysnb Getpid() (pid int)
//sysnb Getppid() (ppid int)
-//sysnb Getrlimit(resource int, rlim *Rlimit) (err error)
//sysnb Getrusage(who int, rusage *Rusage) (err error)
//sysnb Gettid() (tid int)
+//sys Getxattr(path string, attr string, dest []byte) (sz int, err error)
//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error)
//sysnb InotifyInit() (fd int, err error)
//sysnb InotifyInit1(flags int) (fd int, err error)
@@ -833,6 +919,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sysnb Kill(pid int, sig Signal) (err error)
//sys Klogctl(typ int, buf []byte) (n int, err error) = SYS_SYSLOG
//sys Link(oldpath string, newpath string) (err error)
+//sys Listxattr(path string, dest []byte) (sz int, err error)
//sys Mkdir(path string, mode uint32) (err error)
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
//sys Mknod(path string, mode uint32, dev int) (err error)
@@ -840,18 +927,20 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
//sys Pause() (err error)
//sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT
-//sys Read(fd int, p []byte) (n int, err error)
+//sysnb prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) = SYS_PRLIMIT64
+//sys read(fd int, p []byte) (n int, err error)
//sys Readlink(path string, buf []byte) (n int, err error)
+//sys Removexattr(path string, attr string) (err error)
//sys Rename(oldpath string, newpath string) (err error)
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys Rmdir(path string) (err error)
//sys Setdomainname(p []byte) (err error)
//sys Sethostname(p []byte) (err error)
//sysnb Setpgid(pid int, pgid int) (err error)
-//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
//sysnb Setsid() (pid int, err error)
//sysnb Settimeofday(tv *Timeval) (err error)
//sysnb Setuid(uid int) (err error)
+//sys Setxattr(path string, attr string, data []byte, flags int) (err error)
//sys Symlink(oldpath string, newpath string) (err error)
//sys Sync()
//sysnb Sysinfo(info *Sysinfo_t) (err error)
@@ -866,10 +955,10 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sys Unshare(flags int) (err error)
//sys Ustat(dev int, ubuf *Ustat_t) (err error)
//sys Utime(path string, buf *Utimbuf) (err error)
-//sys Write(fd int, p []byte) (n int, err error)
+//sys write(fd int, p []byte) (n int, err error)
//sys exitThread(code int) (err error) = SYS_EXIT
-//sys read(fd int, p *byte, np int) (n int, err error)
-//sys write(fd int, p *byte, np int) (n int, err error)
+//sys readlen(fd int, p *byte, np int) (n int, err error) = SYS_READ
+//sys writelen(fd int, p *byte, np int) (n int, err error) = SYS_WRITE
// mmap varies by architecture; see syscall_linux_*.go.
//sys munmap(addr uintptr, length uintptr) (err error)
@@ -931,7 +1020,6 @@ func Munmap(b []byte) (err error) {
// Getitimer
// Getpmsg
// Getpriority
-// Getxattr
// IoCancel
// IoDestroy
// IoGetevents
@@ -943,7 +1031,6 @@ func Munmap(b []byte) (err error) {
// KexecLoad
// Keyctl
// Lgetxattr
-// Listxattr
// Llistxattr
// LookupDcookie
// Lremovexattr
@@ -981,7 +1068,6 @@ func Munmap(b []byte) (err error) {
// Readahead
// Readv
// RemapFilePages
-// Removexattr
// RequestKey
// RestartSyscall
// RtSigaction
@@ -1010,7 +1096,6 @@ func Munmap(b []byte) (err error) {
// SetThreadArea
// SetTidAddress
// Setpriority
-// Setxattr
// Shmat
// Shmctl
// Shmdt
@@ -1036,5 +1121,4 @@ func Munmap(b []byte) (err error) {
// Vmsplice
// Vserver
// Waitid
-// Writev
// _Sysctl
diff --git a/src/pkg/syscall/syscall_linux_386.go b/src/pkg/syscall/syscall_linux_386.go
index b833db8a7..a0ded43dc 100644
--- a/src/pkg/syscall/syscall_linux_386.go
+++ b/src/pkg/syscall/syscall_linux_386.go
@@ -41,7 +41,7 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
-//sys Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
+//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
//sys Setfsgid(gid int) (err error) = SYS_SETFSGID32
//sys Setfsuid(uid int) (err error) = SYS_SETFSUID32
//sysnb Setgid(gid int) (err error) = SYS_SETGID32
@@ -67,6 +67,69 @@ func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int6
return mmap2(addr, length, prot, flags, fd, page)
}
+type rlimit32 struct {
+ Cur uint32
+ Max uint32
+}
+
+//sysnb getrlimit(resource int, rlim *rlimit32) (err error) = SYS_GETRLIMIT
+
+const rlimInf32 = ^uint32(0)
+const rlimInf64 = ^uint64(0)
+
+func Getrlimit(resource int, rlim *Rlimit) (err error) {
+ err = prlimit(0, resource, rlim, nil)
+ if err != ENOSYS {
+ return err
+ }
+
+ rl := rlimit32{}
+ err = getrlimit(resource, &rl)
+ if err != nil {
+ return
+ }
+
+ if rl.Cur == rlimInf32 {
+ rlim.Cur = rlimInf64
+ } else {
+ rlim.Cur = uint64(rl.Cur)
+ }
+
+ if rl.Max == rlimInf32 {
+ rlim.Max = rlimInf64
+ } else {
+ rlim.Max = uint64(rl.Max)
+ }
+ return
+}
+
+//sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT
+
+func Setrlimit(resource int, rlim *Rlimit) (err error) {
+ err = prlimit(0, resource, nil, rlim)
+ if err != ENOSYS {
+ return err
+ }
+
+ rl := rlimit32{}
+ if rlim.Cur == rlimInf64 {
+ rl.Cur = rlimInf32
+ } else if rlim.Cur < uint64(rlimInf32) {
+ rl.Cur = uint32(rlim.Cur)
+ } else {
+ return EINVAL
+ }
+ if rlim.Max == rlimInf64 {
+ rl.Max = rlimInf32
+ } else if rlim.Max < uint64(rlimInf32) {
+ rl.Max = uint32(rlim.Max)
+ } else {
+ return EINVAL
+ }
+
+ return setrlimit(resource, &rl)
+}
+
// Underlying system call writes to newoffset via pointer.
// Implemented in assembly to avoid allocation.
func Seek(fd int, offset int64, whence int) (newoffset int64, err error)
@@ -101,6 +164,9 @@ const (
_GETSOCKOPT = 15
_SENDMSG = 16
_RECVMSG = 17
+ _ACCEPT4 = 18
+ _RECVMMSG = 19
+ _SENDMMSG = 20
)
func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err Errno)
@@ -114,6 +180,14 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
return
}
+func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) {
+ fd, e := socketcall(_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0)
+ if e != 0 {
+ err = e
+ }
+ return
+}
+
func getsockname(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
_, e := rawsocketcall(_GETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0)
if e != 0 {
@@ -130,7 +204,7 @@ func getpeername(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
return
}
-func socketpair(domain int, typ int, flags int, fd *[2]int) (err error) {
+func socketpair(domain int, typ int, flags int, fd *[2]int32) (err error) {
_, e := rawsocketcall(_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(flags), uintptr(unsafe.Pointer(fd)), 0, 0)
if e != 0 {
err = e
@@ -243,7 +317,11 @@ func Fstatfs(fd int, buf *Statfs_t) (err error) {
}
func Statfs(path string, buf *Statfs_t) (err error) {
- _, _, e := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(StringBytePtr(path))), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf)))
+ pathp, err := BytePtrFromString(path)
+ if err != nil {
+ return err
+ }
+ _, _, e := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(pathp)), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf)))
if e != 0 {
err = e
}
diff --git a/src/pkg/syscall/syscall_linux_amd64.go b/src/pkg/syscall/syscall_linux_amd64.go
index 18b36b78e..f4b73b20e 100644
--- a/src/pkg/syscall/syscall_linux_amd64.go
+++ b/src/pkg/syscall/syscall_linux_amd64.go
@@ -12,6 +12,7 @@ package syscall
//sysnb Getegid() (egid int)
//sysnb Geteuid() (euid int)
//sysnb Getgid() (gid int)
+//sysnb Getrlimit(resource int, rlim *Rlimit) (err error)
//sysnb Getuid() (uid int)
//sys Ioperm(from int, num int, on int) (err error)
//sys Iopl(level int) (err error)
@@ -22,13 +23,14 @@ package syscall
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error)
-//sys Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
+//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
//sys Setfsgid(gid int) (err error)
//sys Setfsuid(uid int) (err error)
//sysnb Setgid(gid int) (err error)
//sysnb Setregid(rgid int, egid int) (err error)
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
+//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
//sysnb Setreuid(ruid int, euid int) (err error)
//sys Shutdown(fd int, how int) (err error)
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
@@ -37,6 +39,7 @@ package syscall
//sys SyncFileRange(fd int, off int64, n int64, flags int) (err error)
//sys Truncate(path string, length int64) (err error)
//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error)
+//sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error)
//sys bind(s int, addr uintptr, addrlen _Socklen) (err error)
//sys connect(s int, addr uintptr, addrlen _Socklen) (err error)
//sysnb getgroups(n int, list *_Gid_t) (nn int, err error)
@@ -44,7 +47,7 @@ package syscall
//sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error)
//sys setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (err error)
//sysnb socket(domain int, typ int, proto int) (fd int, err error)
-//sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (err error)
+//sysnb socketpair(domain int, typ int, proto int, fd *[2]int32) (err error)
//sysnb getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error)
//sysnb getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error)
//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error)
diff --git a/src/pkg/syscall/syscall_linux_arm.go b/src/pkg/syscall/syscall_linux_arm.go
index 2f2907945..7839d5288 100644
--- a/src/pkg/syscall/syscall_linux_arm.go
+++ b/src/pkg/syscall/syscall_linux_arm.go
@@ -4,6 +4,8 @@
package syscall
+import "unsafe"
+
func Getpagesize() int { return 4096 }
func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
@@ -26,6 +28,7 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
func Seek(fd int, offset int64, whence int) (newoffset int64, err error)
//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error)
+//sys accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error)
//sys bind(s int, addr uintptr, addrlen _Socklen) (err error)
//sys connect(s int, addr uintptr, addrlen _Socklen) (err error)
//sysnb getgroups(n int, list *_Gid_t) (nn int, err error) = SYS_GETGROUPS32
@@ -37,7 +40,7 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error)
//sysnb getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error)
//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error)
//sys sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (err error)
-//sysnb socketpair(domain int, typ int, flags int, fd *[2]int) (err error)
+//sysnb socketpair(domain int, typ int, flags int, fd *[2]int32) (err error)
//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
//sys sendmsg(s int, msg *Msghdr, flags int) (err error)
@@ -46,7 +49,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error)
//sys Chown(path string, uid int, gid int) (err error) = SYS_CHOWN32
//sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
-//sys Fstatfs(fd int, buf *Statfs_t) (err error) = SYS_FSTATFS64
//sysnb Getegid() (egid int) = SYS_GETEGID32
//sysnb Geteuid() (euid int) = SYS_GETEUID32
//sysnb Getgid() (gid int) = SYS_GETGID32
@@ -54,7 +56,7 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error)
//sys Lchown(path string, uid int, gid int) (err error) = SYS_LCHOWN32
//sys Listen(s int, n int) (err error)
//sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
-//sys Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
+//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT
//sys Setfsgid(gid int) (err error) = SYS_SETFSGID32
//sys Setfsuid(uid int) (err error) = SYS_SETFSUID32
@@ -66,7 +68,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error)
//sys Shutdown(fd int, how int) (err error)
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error)
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64
-//sys Statfs(path string, buf *Statfs_t) (err error) = SYS_STATFS64
// Vsyscalls on amd64.
//sysnb Gettimeofday(tv *Timeval) (err error)
@@ -79,6 +80,26 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error)
//sys mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error)
+func Fstatfs(fd int, buf *Statfs_t) (err error) {
+ _, _, e := Syscall(SYS_FSTATFS64, uintptr(fd), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf)))
+ if e != 0 {
+ err = e
+ }
+ return
+}
+
+func Statfs(path string, buf *Statfs_t) (err error) {
+ pathp, err := BytePtrFromString(path)
+ if err != nil {
+ return err
+ }
+ _, _, e := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(pathp)), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf)))
+ if e != 0 {
+ err = e
+ }
+ return
+}
+
func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) {
page := uintptr(offset / 4096)
if offset != int64(page)*4096 {
@@ -87,10 +108,72 @@ func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int6
return mmap2(addr, length, prot, flags, fd, page)
}
-// TODO(kaib): add support for tracing
-func (r *PtraceRegs) PC() uint64 { return 0 }
+type rlimit32 struct {
+ Cur uint32
+ Max uint32
+}
+
+//sysnb getrlimit(resource int, rlim *rlimit32) (err error) = SYS_GETRLIMIT
+
+const rlimInf32 = ^uint32(0)
+const rlimInf64 = ^uint64(0)
+
+func Getrlimit(resource int, rlim *Rlimit) (err error) {
+ err = prlimit(0, resource, rlim, nil)
+ if err != ENOSYS {
+ return err
+ }
+
+ rl := rlimit32{}
+ err = getrlimit(resource, &rl)
+ if err != nil {
+ return
+ }
+
+ if rl.Cur == rlimInf32 {
+ rlim.Cur = rlimInf64
+ } else {
+ rlim.Cur = uint64(rl.Cur)
+ }
+
+ if rl.Max == rlimInf32 {
+ rlim.Max = rlimInf64
+ } else {
+ rlim.Max = uint64(rl.Max)
+ }
+ return
+}
+
+//sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT
+
+func Setrlimit(resource int, rlim *Rlimit) (err error) {
+ err = prlimit(0, resource, nil, rlim)
+ if err != ENOSYS {
+ return err
+ }
+
+ rl := rlimit32{}
+ if rlim.Cur == rlimInf64 {
+ rl.Cur = rlimInf32
+ } else if rlim.Cur < uint64(rlimInf32) {
+ rl.Cur = uint32(rlim.Cur)
+ } else {
+ return EINVAL
+ }
+ if rlim.Max == rlimInf64 {
+ rl.Max = rlimInf32
+ } else if rlim.Max < uint64(rlimInf32) {
+ rl.Max = uint32(rlim.Max)
+ } else {
+ return EINVAL
+ }
+
+ return setrlimit(resource, &rl)
+}
+
+func (r *PtraceRegs) PC() uint64 { return uint64(r.Uregs[15]) }
-func (r *PtraceRegs) SetPC(pc uint64) {}
+func (r *PtraceRegs) SetPC(pc uint64) { r.Uregs[15] = uint32(pc) }
func (iov *Iovec) SetLen(length int) {
iov.Len = uint32(length)
diff --git a/src/pkg/syscall/syscall_netbsd.go b/src/pkg/syscall/syscall_netbsd.go
index 3f6d16f56..b0fa1112d 100644
--- a/src/pkg/syscall/syscall_netbsd.go
+++ b/src/pkg/syscall/syscall_netbsd.go
@@ -22,14 +22,72 @@ type SockaddrDatalink struct {
Nlen uint8
Alen uint8
Slen uint8
- Data [24]int8
+ Data [12]int8
raw RawSockaddrDatalink
}
func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
+func sysctlNodes(mib []_C_int) (nodes []Sysctlnode, err error) {
+ var olen uintptr
+
+ // Get a list of all sysctl nodes below the given MIB by performing
+ // a sysctl for the given MIB with CTL_QUERY appended.
+ mib = append(mib, CTL_QUERY)
+ qnode := Sysctlnode{Flags: SYSCTL_VERS_1}
+ qp := (*byte)(unsafe.Pointer(&qnode))
+ sz := unsafe.Sizeof(qnode)
+ if err = sysctl(mib, nil, &olen, qp, sz); err != nil {
+ return nil, err
+ }
+
+ // Now that we know the size, get the actual nodes.
+ nodes = make([]Sysctlnode, olen/sz)
+ np := (*byte)(unsafe.Pointer(&nodes[0]))
+ if err = sysctl(mib, np, &olen, qp, sz); err != nil {
+ return nil, err
+ }
+
+ return nodes, nil
+}
+
func nametomib(name string) (mib []_C_int, err error) {
- return nil, EINVAL
+
+ // Split name into components.
+ var parts []string
+ last := 0
+ for i := 0; i < len(name); i++ {
+ if name[i] == '.' {
+ parts = append(parts, name[last:i])
+ last = i + 1
+ }
+ }
+ parts = append(parts, name[last:])
+
+ // Discover the nodes and construct the MIB OID.
+ for partno, part := range parts {
+ nodes, err := sysctlNodes(mib)
+ if err != nil {
+ return nil, err
+ }
+ for _, node := range nodes {
+ n := make([]byte, 0)
+ for i := range node.Name {
+ if node.Name[i] != 0 {
+ n = append(n, byte(node.Name[i]))
+ }
+ }
+ if string(n) == part {
+ mib = append(mib, _C_int(node.Num))
+ break
+ }
+ }
+ if len(mib) != partno+1 {
+ return nil, EINVAL
+ }
+ }
+
+ return mib, nil
}
// ParseDirent parses up to max directory entries in buf,
@@ -60,15 +118,12 @@ func ParseDirent(buf []byte, max int, names []string) (consumed int, count int,
return origlen - len(buf), count, names
}
-//sysnb pipe2(p *[2]_C_int, flags _C_int) (err error)
+//sysnb pipe() (fd1 int, fd2 int, err error)
func Pipe(p []int) (err error) {
if len(p) != 2 {
return EINVAL
}
- var pp [2]_C_int
- err = pipe2(&pp, 0)
- p[0] = int(pp[0])
- p[1] = int(pp[1])
+ p[0], p[1], err = pipe()
return
}
@@ -78,7 +133,7 @@ func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
}
// TODO
-func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
return -1, ENOSYS
}
@@ -133,7 +188,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys Pathconf(path string, name int) (val int, err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error)
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
-//sys Read(fd int, p []byte) (n int, err error)
+//sys read(fd int, p []byte) (n int, err error)
//sys Readlink(path string, buf []byte) (n int, err error)
//sys Rename(from string, to string) (err error)
//sys Revoke(path string) (err error)
@@ -158,11 +213,11 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys Umask(newmask int) (oldmask int)
//sys Unlink(path string) (err error)
//sys Unmount(path string, flags int) (err error)
-//sys Write(fd int, p []byte) (n int, err error)
+//sys write(fd int, p []byte) (n int, err error)
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
//sys munmap(addr uintptr, length uintptr) (err error)
-//sys read(fd int, buf *byte, nbuf int) (n int, err error)
-//sys write(fd int, buf *byte, nbuf int) (n int, err error)
+//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ
+//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE
/*
* Unimplemented
diff --git a/src/pkg/syscall/syscall_netbsd_386.go b/src/pkg/syscall/syscall_netbsd_386.go
index 3c4c693c9..2dbff07f1 100644
--- a/src/pkg/syscall/syscall_netbsd_386.go
+++ b/src/pkg/syscall/syscall_netbsd_386.go
@@ -9,7 +9,7 @@ func Getpagesize() int { return 4096 }
func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
func NsecToTimespec(nsec int64) (ts Timespec) {
- ts.Sec = int32(nsec / 1e9)
+ ts.Sec = int64(nsec / 1e9)
ts.Nsec = int32(nsec % 1e9)
return
}
@@ -19,14 +19,14 @@ func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)
func NsecToTimeval(nsec int64) (tv Timeval) {
nsec += 999 // round up to microsecond
tv.Usec = int32(nsec % 1e9 / 1e3)
- tv.Sec = int32(nsec / 1e9)
+ tv.Sec = int64(nsec / 1e9)
return
}
func SetKevent(k *Kevent_t, fd, mode, flags int) {
k.Ident = uint32(fd)
- k.Filter = int16(mode)
- k.Flags = uint16(flags)
+ k.Filter = uint32(mode)
+ k.Flags = uint32(flags)
}
func (iov *Iovec) SetLen(length int) {
diff --git a/src/pkg/syscall/syscall_netbsd_amd64.go b/src/pkg/syscall/syscall_netbsd_amd64.go
index 17485b12c..5784db99a 100644
--- a/src/pkg/syscall/syscall_netbsd_amd64.go
+++ b/src/pkg/syscall/syscall_netbsd_amd64.go
@@ -9,8 +9,8 @@ func Getpagesize() int { return 4096 }
func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
func NsecToTimespec(nsec int64) (ts Timespec) {
- ts.Sec = int32(nsec / 1e9)
- ts.Nsec = nsec % 1e9
+ ts.Sec = int64(nsec / 1e9)
+ ts.Nsec = int64(nsec % 1e9)
return
}
@@ -18,15 +18,15 @@ func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)
func NsecToTimeval(nsec int64) (tv Timeval) {
nsec += 999 // round up to microsecond
- tv.Usec = nsec % 1e9 / 1e3
+ tv.Usec = int32(nsec % 1e9 / 1e3)
tv.Sec = int64(nsec / 1e9)
return
}
func SetKevent(k *Kevent_t, fd, mode, flags int) {
k.Ident = uint64(fd)
- k.Filter = int16(mode)
- k.Flags = uint16(flags)
+ k.Filter = uint32(mode)
+ k.Flags = uint32(flags)
}
func (iov *Iovec) SetLen(length int) {
diff --git a/src/pkg/syscall/syscall_netbsd_arm.go b/src/pkg/syscall/syscall_netbsd_arm.go
new file mode 100644
index 000000000..659698aac
--- /dev/null
+++ b/src/pkg/syscall/syscall_netbsd_arm.go
@@ -0,0 +1,42 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syscall
+
+func Getpagesize() int { return 4096 }
+
+func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
+
+func NsecToTimespec(nsec int64) (ts Timespec) {
+ ts.Sec = int64(nsec / 1e9)
+ ts.Nsec = int32(nsec % 1e9)
+ return
+}
+
+func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
+
+func NsecToTimeval(nsec int64) (tv Timeval) {
+ nsec += 999 // round up to microsecond
+ tv.Usec = int32(nsec % 1e9 / 1e3)
+ tv.Sec = int64(nsec / 1e9)
+ return
+}
+
+func SetKevent(k *Kevent_t, fd, mode, flags int) {
+ k.Ident = uint32(fd)
+ k.Filter = uint32(mode)
+ k.Flags = uint32(flags)
+}
+
+func (iov *Iovec) SetLen(length int) {
+ iov.Len = uint32(length)
+}
+
+func (msghdr *Msghdr) SetControllen(length int) {
+ msghdr.Controllen = uint32(length)
+}
+
+func (cmsg *Cmsghdr) SetLen(length int) {
+ cmsg.Len = uint32(length)
+}
diff --git a/src/pkg/syscall/syscall_openbsd.go b/src/pkg/syscall/syscall_openbsd.go
index 7cd119108..a146dca2c 100644
--- a/src/pkg/syscall/syscall_openbsd.go
+++ b/src/pkg/syscall/syscall_openbsd.go
@@ -91,7 +91,7 @@ func Pipe(p []int) (err error) {
}
// TODO
-func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
return -1, ENOSYS
}
@@ -149,7 +149,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys Pathconf(path string, name int) (val int, err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error)
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
-//sys Read(fd int, p []byte) (n int, err error)
+//sys read(fd int, p []byte) (n int, err error)
//sys Readlink(path string, buf []byte) (n int, err error)
//sys Rename(from string, to string) (err error)
//sys Revoke(path string) (err error)
@@ -176,11 +176,11 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys Umask(newmask int) (oldmask int)
//sys Unlink(path string) (err error)
//sys Unmount(path string, flags int) (err error)
-//sys Write(fd int, p []byte) (n int, err error)
+//sys write(fd int, p []byte) (n int, err error)
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
//sys munmap(addr uintptr, length uintptr) (err error)
-//sys read(fd int, buf *byte, nbuf int) (n int, err error)
-//sys write(fd int, buf *byte, nbuf int) (n int, err error)
+//sys readlen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_READ
+//sys writelen(fd int, buf *byte, nbuf int) (n int, err error) = SYS_WRITE
/*
* Unimplemented
diff --git a/src/pkg/syscall/syscall_plan9.go b/src/pkg/syscall/syscall_plan9.go
index 122a96f26..bc2505758 100644
--- a/src/pkg/syscall/syscall_plan9.go
+++ b/src/pkg/syscall/syscall_plan9.go
@@ -23,13 +23,28 @@ func (e ErrorString) Error() string { return string(e) }
// NewError converts s to an ErrorString, which satisfies the Error interface.
func NewError(s string) error { return ErrorString(s) }
+func (e ErrorString) Temporary() bool {
+ return e == EINTR || e == EMFILE || e.Timeout()
+}
+
+func (e ErrorString) Timeout() bool {
+ return e == EBUSY || e == ETIMEDOUT
+}
+
+// A Note is a string describing a process note.
+// It implements the os.Signal interface.
+type Note string
+
+func (n Note) Signal() {}
+
+func (n Note) String() string {
+ return string(n)
+}
+
var (
Stdin = 0
Stdout = 1
Stderr = 2
-
- EAFNOSUPPORT = NewError("address family not supported by protocol")
- EISDIR = NewError("file is a directory")
)
// For testing: clients can set this flag to force
@@ -67,25 +82,10 @@ func errstr() string {
return cstring(buf[:])
}
-func Getpagesize() int { return 4096 }
+// Implemented in assembly to import from runtime.
+func exit(int)
-//sys exits(msg *byte)
-func Exits(msg *string) {
- if msg == nil {
- exits(nil)
- }
-
- exits(StringBytePtr(*msg))
-}
-
-func Exit(code int) {
- if code == 0 {
- Exits(nil)
- }
-
- msg := itoa(code)
- Exits(&msg)
-}
+func Exit(code int) { exit(code) }
func readnum(path string) (uint, error) {
var b [12]byte
@@ -120,13 +120,22 @@ func Getppid() (ppid int) {
}
func Read(fd int, p []byte) (n int, err error) {
- return Pread(fd, p, -1)
+ n, err = Pread(fd, p, -1)
+ if raceenabled && err == nil {
+ raceAcquire(unsafe.Pointer(&ioSync))
+ }
+ return
}
func Write(fd int, p []byte) (n int, err error) {
+ if raceenabled {
+ raceReleaseMerge(unsafe.Pointer(&ioSync))
+ }
return Pwrite(fd, p, -1)
}
+var ioSync int64
+
func Getwd() (wd string, err error) {
fd, e := Open(".", O_RDONLY)
@@ -240,19 +249,27 @@ func Await(w *Waitmsg) (err error) {
}
func Unmount(name, old string) (err error) {
- oldp := uintptr(unsafe.Pointer(StringBytePtr(old)))
+ oldp, err := BytePtrFromString(old)
+ if err != nil {
+ return err
+ }
+ oldptr := uintptr(unsafe.Pointer(oldp))
var r0 uintptr
var e ErrorString
// bind(2) man page: If name is zero, everything bound or mounted upon old is unbound or unmounted.
if name == "" {
- r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldp, 0)
+ r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldptr, 0)
} else {
- r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(name))), oldp, 0)
+ namep, err := BytePtrFromString(name)
+ if err != nil {
+ return err
+ }
+ r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(namep)), oldptr, 0)
}
- if int(r0) == -1 {
+ if int32(r0) == -1 {
err = e
}
return
@@ -300,29 +317,12 @@ func DecodeBintime(b []byte) (nsec int64, err error) {
return
}
-func Gettimeofday(tv *Timeval) (err error) {
- // TODO(paulzhol):
- // avoid reopening a file descriptor for /dev/bintime on each call,
- // use lower-level calls to avoid allocation.
-
- var b [8]byte
- var nsec int64
-
- fd, e := Open("/dev/bintime", O_RDONLY)
+func Gettimeofday(tv *Timeval) error {
+ nsec, e := nanotime()
if e != nil {
return e
}
- defer Close(fd)
-
- if _, e = Pread(fd, b[:], 0); e != nil {
- return e
- }
-
- if nsec, e = DecodeBintime(b[:]); e != nil {
- return e
- }
*tv = NsecToTimeval(nsec)
-
return e
}
@@ -335,14 +335,6 @@ func Getgroups() (gids []int, err error) {
return make([]int, 0), nil
}
-type Signal int
-
-func (s Signal) Signal() {}
-
-func (s Signal) String() string {
- return ""
-}
-
//sys Dup(oldfd int, newfd int) (fd int, err error)
//sys Open(path string, mode int) (fd int, err error)
//sys Create(path string, mode int, perm uint32) (fd int, err error)
diff --git a/src/pkg/syscall/syscall_plan9_386.go b/src/pkg/syscall/syscall_plan9_386.go
index e82b540b4..7357e0ba6 100644
--- a/src/pkg/syscall/syscall_plan9_386.go
+++ b/src/pkg/syscall/syscall_plan9_386.go
@@ -3,3 +3,30 @@
// license that can be found in the LICENSE file.
package syscall
+
+func Getpagesize() int { return 0x1000 }
+
+func nanotime() (nsec int64, err error) {
+ // TODO(paulzhol):
+ // avoid reopening a file descriptor for /dev/bintime on each call,
+ // use lower-level calls to avoid allocation.
+
+ var b [8]byte
+ nsec = -1
+
+ fd, err := Open("/dev/bintime", O_RDONLY)
+ if err != nil {
+ return
+ }
+ defer Close(fd)
+
+ if _, err = Pread(fd, b[:], 0); err != nil {
+ return
+ }
+
+ if nsec, err = DecodeBintime(b[:]); err != nil {
+ return -1, err
+ }
+
+ return
+}
diff --git a/src/pkg/syscall/syscall_plan9_amd64.go b/src/pkg/syscall/syscall_plan9_amd64.go
new file mode 100644
index 000000000..9387db3e5
--- /dev/null
+++ b/src/pkg/syscall/syscall_plan9_amd64.go
@@ -0,0 +1,14 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syscall
+
+func Getpagesize() int { return 0x200000 }
+
+// Used by Gettimeofday, which expects
+// an error return value.
+func nanotime() (int64, error) {
+ r1, _, _ := RawSyscall(SYS_NANOTIME, 0, 0, 0)
+ return int64(r1), nil
+}
diff --git a/src/pkg/syscall/syscall_unix.go b/src/pkg/syscall/syscall_unix.go
index d4e02f68a..fee1fc491 100644
--- a/src/pkg/syscall/syscall_unix.go
+++ b/src/pkg/syscall/syscall_unix.go
@@ -127,3 +127,27 @@ func (s Signal) String() string {
}
return "signal " + itoa(int(s))
}
+
+func Read(fd int, p []byte) (n int, err error) {
+ n, err = read(fd, p)
+ if raceenabled && err == nil {
+ raceAcquire(unsafe.Pointer(&ioSync))
+ }
+ return
+}
+
+func Write(fd int, p []byte) (n int, err error) {
+ if raceenabled {
+ raceReleaseMerge(unsafe.Pointer(&ioSync))
+ }
+ return write(fd, p)
+}
+
+func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+ if raceenabled {
+ raceReleaseMerge(unsafe.Pointer(&ioSync))
+ }
+ return sendfile(outfd, infd, offset, count)
+}
+
+var ioSync int64
diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go
index 6b544f1d4..d7c3265a1 100644
--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -7,6 +7,8 @@
package syscall
import (
+ errorspkg "errors"
+ "sync"
"unicode/utf16"
"unsafe"
)
@@ -15,46 +17,28 @@ type Handle uintptr
const InvalidHandle = ^Handle(0)
-/*
-
-small demo to detect version of windows you are running:
-
-package main
-
-import (
- "syscall"
-)
-
-func abort(funcname string, err error) {
- panic(funcname + " failed: " + err.Error())
-}
-
-func print_version(v uint32) {
- major := byte(v)
- minor := uint8(v >> 8)
- build := uint16(v >> 16)
- print("windows version ", major, ".", minor, " (Build ", build, ")\n")
-}
-
-func main() {
- h, err := syscall.LoadLibrary("kernel32.dll")
- if err != nil {
- abort("LoadLibrary", err)
- }
- defer syscall.FreeLibrary(h)
- proc, err := syscall.GetProcAddress(h, "GetVersion")
+// StringToUTF16 is deprecated. Use UTF16FromString instead.
+// If s contains a NUL byte this function panics instead of
+// returning an error.
+func StringToUTF16(s string) []uint16 {
+ a, err := UTF16FromString(s)
if err != nil {
- abort("GetProcAddress", err)
+ panic("syscall: string with NUL passed to StringToUTF16")
}
- r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
- print_version(uint32(r))
+ return a
}
-*/
-
-// StringToUTF16 returns the UTF-16 encoding of the UTF-8 string s,
-// with a terminating NUL added.
-func StringToUTF16(s string) []uint16 { return utf16.Encode([]rune(s + "\x00")) }
+// UTF16FromString returns the UTF-16 encoding of the UTF-8 string
+// s, with a terminating NUL added. If s contains a NUL byte at any
+// location, it returns (nil, EINVAL).
+func UTF16FromString(s string) ([]uint16, error) {
+ for i := 0; i < len(s); i++ {
+ if s[i] == 0 {
+ return nil, EINVAL
+ }
+ }
+ return utf16.Encode([]rune(s + "\x00")), nil
+}
// UTF16ToString returns the UTF-8 encoding of the UTF-16 sequence s,
// with a terminating NUL removed.
@@ -68,10 +52,22 @@ func UTF16ToString(s []uint16) string {
return string(utf16.Decode(s))
}
-// StringToUTF16Ptr returns pointer to the UTF-16 encoding of
-// the UTF-8 string s, with a terminating NUL added.
+// StringToUTF16Ptr is deprecated. Use UTF16PtrFromString instead.
+// If s contains a NUL byte this function panics instead of
+// returning an error.
func StringToUTF16Ptr(s string) *uint16 { return &StringToUTF16(s)[0] }
+// UTF16PtrFromString returns pointer to the UTF-16 encoding of
+// the UTF-8 string s, with a terminating NUL added. If s
+// contains a NUL byte at any location, it returns (nil, EINVAL).
+func UTF16PtrFromString(s string) (*uint16, error) {
+ a, err := UTF16FromString(s)
+ if err != nil {
+ return nil, err
+ }
+ return &a[0], nil
+}
+
func Getpagesize() int { return 4096 }
// Errno is the Windows error number.
@@ -90,9 +86,10 @@ func (e Errno) Error() string {
b := make([]uint16, 300)
n, err := FormatMessage(flags, 0, uint32(e), langid(LANG_ENGLISH, SUBLANG_ENGLISH_US), b, nil)
if err != nil {
- // TODO(brainman): Call FormatMessage again asking for "native" error message.
- // http://code.google.com/p/go/issues/detail?id=3376 must be resolved first.
- return "winapi error #" + itoa(int(e))
+ n, err = FormatMessage(flags, 0, uint32(e), 0, b, nil)
+ if err != nil {
+ return "winapi error #" + itoa(int(e))
+ }
}
// trim terminating \r and \n
for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- {
@@ -111,7 +108,7 @@ func (e Errno) Timeout() bool {
// Converts a Go function to a function pointer conforming
// to the stdcall calling convention. This is useful when
// interoperating with Windows code requiring callbacks.
-// Implemented in ../runtime/windows/syscall.goc
+// Implemented in ../runtime/syscall_windows.goc
func NewCallback(fn interface{}) uintptr
// windows api calls
@@ -147,6 +144,7 @@ func NewCallback(fn interface{}) uintptr
//sys GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (err error)
//sys PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlapped *Overlapped) (err error)
//sys CancelIo(s Handle) (err error)
+//sys CancelIoEx(s Handle, o *Overlapped) (err error)
//sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (err error) = CreateProcessW
//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err error)
//sys TerminateProcess(handle Handle, exitcode uint32) (err error)
@@ -201,7 +199,10 @@ func NewCallback(fn interface{}) uintptr
//sys RegQueryInfoKey(key Handle, class *uint16, classLen *uint32, reserved *uint32, subkeysLen *uint32, maxSubkeyLen *uint32, maxClassLen *uint32, valuesLen *uint32, maxValueNameLen *uint32, maxValueLen *uint32, saLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegQueryInfoKeyW
//sys RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW
//sys RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegQueryValueExW
-//sys getCurrentProcessId() (pid uint32) = kernel32.getCurrentProcessId
+//sys getCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
+//sys GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode
+//sys WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) = kernel32.WriteConsoleW
+//sys ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) = kernel32.ReadConsoleW
// syscall interface implementation for other packages
@@ -218,6 +219,10 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) {
if len(path) == 0 {
return InvalidHandle, ERROR_FILE_NOT_FOUND
}
+ pathp, err := UTF16PtrFromString(path)
+ if err != nil {
+ return InvalidHandle, err
+ }
var access uint32
switch mode & (O_RDONLY | O_WRONLY | O_RDWR) {
case O_RDONLY:
@@ -252,7 +257,7 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) {
default:
createmode = OPEN_EXISTING
}
- h, e := CreateFile(StringToUTF16Ptr(path), access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0)
+ h, e := CreateFile(pathp, access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0)
return h, e
}
@@ -266,10 +271,16 @@ func Read(fd Handle, p []byte) (n int, err error) {
}
return 0, e
}
+ if raceenabled {
+ raceAcquire(unsafe.Pointer(&ioSync))
+ }
return int(done), nil
}
func Write(fd Handle, p []byte) (n int, err error) {
+ if raceenabled {
+ raceReleaseMerge(unsafe.Pointer(&ioSync))
+ }
var done uint32
e := WriteFile(fd, p, &done, nil)
if e != nil {
@@ -278,6 +289,8 @@ func Write(fd Handle, p []byte) (n int, err error) {
return int(done), nil
}
+var ioSync int64
+
func Seek(fd Handle, offset int64, whence int) (newoffset int64, err error) {
var w uint32
switch whence {
@@ -330,24 +343,46 @@ func Getwd() (wd string, err error) {
}
func Chdir(path string) (err error) {
- return SetCurrentDirectory(&StringToUTF16(path)[0])
+ pathp, err := UTF16PtrFromString(path)
+ if err != nil {
+ return err
+ }
+ return SetCurrentDirectory(pathp)
}
func Mkdir(path string, mode uint32) (err error) {
- return CreateDirectory(&StringToUTF16(path)[0], nil)
+ pathp, err := UTF16PtrFromString(path)
+ if err != nil {
+ return err
+ }
+ return CreateDirectory(pathp, nil)
}
func Rmdir(path string) (err error) {
- return RemoveDirectory(&StringToUTF16(path)[0])
+ pathp, err := UTF16PtrFromString(path)
+ if err != nil {
+ return err
+ }
+ return RemoveDirectory(pathp)
}
func Unlink(path string) (err error) {
- return DeleteFile(&StringToUTF16(path)[0])
+ pathp, err := UTF16PtrFromString(path)
+ if err != nil {
+ return err
+ }
+ return DeleteFile(pathp)
}
func Rename(oldpath, newpath string) (err error) {
- from := &StringToUTF16(oldpath)[0]
- to := &StringToUTF16(newpath)[0]
+ from, err := UTF16PtrFromString(oldpath)
+ if err != nil {
+ return err
+ }
+ to, err := UTF16PtrFromString(newpath)
+ if err != nil {
+ return err
+ }
return MoveFile(from, to)
}
@@ -403,7 +438,11 @@ func Utimes(path string, tv []Timeval) (err error) {
if len(tv) != 2 {
return EINVAL
}
- h, e := CreateFile(StringToUTF16Ptr(path),
+ pathp, e := UTF16PtrFromString(path)
+ if e != nil {
+ return e
+ }
+ h, e := CreateFile(pathp,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
if e != nil {
@@ -415,6 +454,26 @@ func Utimes(path string, tv []Timeval) (err error) {
return SetFileTime(h, nil, &a, &w)
}
+func UtimesNano(path string, ts []Timespec) (err error) {
+ if len(ts) != 2 {
+ return EINVAL
+ }
+ pathp, e := UTF16PtrFromString(path)
+ if e != nil {
+ return e
+ }
+ h, e := CreateFile(pathp,
+ FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE, nil,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
+ if e != nil {
+ return e
+ }
+ defer Close(h)
+ a := NsecToFiletime(TimespecToNsec(ts[0]))
+ w := NsecToFiletime(TimespecToNsec(ts[1]))
+ return SetFileTime(h, nil, &a, &w)
+}
+
func Fsync(fd Handle) (err error) {
return FlushFileBuffers(fd)
}
@@ -423,7 +482,10 @@ func Chmod(path string, mode uint32) (err error) {
if mode == 0 {
return EINVAL
}
- p := StringToUTF16Ptr(path)
+ p, e := UTF16PtrFromString(path)
+ if e != nil {
+ return e
+ }
attrs, e := GetFileAttributes(p)
if e != nil {
return e
@@ -436,32 +498,41 @@ func Chmod(path string, mode uint32) (err error) {
return SetFileAttributes(p, attrs)
}
+func LoadCancelIoEx() error {
+ return procCancelIoEx.Find()
+}
+
// net api calls
+const socket_error = uintptr(^uint32(0))
+
//sys WSAStartup(verreq uint32, data *WSAData) (sockerr error) = ws2_32.WSAStartup
-//sys WSACleanup() (err error) [failretval==-1] = ws2_32.WSACleanup
-//sys WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) [failretval==-1] = ws2_32.WSAIoctl
+//sys WSACleanup() (err error) [failretval==socket_error] = ws2_32.WSACleanup
+//sys WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) [failretval==socket_error] = ws2_32.WSAIoctl
//sys socket(af int32, typ int32, protocol int32) (handle Handle, err error) [failretval==InvalidHandle] = ws2_32.socket
-//sys Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) [failretval==-1] = ws2_32.setsockopt
-//sys bind(s Handle, name uintptr, namelen int32) (err error) [failretval==-1] = ws2_32.bind
-//sys connect(s Handle, name uintptr, namelen int32) (err error) [failretval==-1] = ws2_32.connect
-//sys getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) [failretval==-1] = ws2_32.getsockname
-//sys getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) [failretval==-1] = ws2_32.getpeername
-//sys listen(s Handle, backlog int32) (err error) [failretval==-1] = ws2_32.listen
-//sys shutdown(s Handle, how int32) (err error) [failretval==-1] = ws2_32.shutdown
-//sys Closesocket(s Handle) (err error) [failretval==-1] = ws2_32.closesocket
+//sys Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) [failretval==socket_error] = ws2_32.setsockopt
+//sys Getsockopt(s Handle, level int32, optname int32, optval *byte, optlen *int32) (err error) [failretval==socket_error] = ws2_32.getsockopt
+//sys bind(s Handle, name uintptr, namelen int32) (err error) [failretval==socket_error] = ws2_32.bind
+//sys connect(s Handle, name uintptr, namelen int32) (err error) [failretval==socket_error] = ws2_32.connect
+//sys getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) [failretval==socket_error] = ws2_32.getsockname
+//sys getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) [failretval==socket_error] = ws2_32.getpeername
+//sys listen(s Handle, backlog int32) (err error) [failretval==socket_error] = ws2_32.listen
+//sys shutdown(s Handle, how int32) (err error) [failretval==socket_error] = ws2_32.shutdown
+//sys Closesocket(s Handle) (err error) [failretval==socket_error] = ws2_32.closesocket
//sys AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (err error) = mswsock.AcceptEx
//sys GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, lrsa **RawSockaddrAny, lrsalen *int32, rrsa **RawSockaddrAny, rrsalen *int32) = mswsock.GetAcceptExSockaddrs
-//sys WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) [failretval==-1] = ws2_32.WSARecv
-//sys WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (err error) [failretval==-1] = ws2_32.WSASend
-//sys WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (err error) [failretval==-1] = ws2_32.WSARecvFrom
-//sys WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (err error) [failretval==-1] = ws2_32.WSASendTo
+//sys WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSARecv
+//sys WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSASend
+//sys WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSARecvFrom
+//sys WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSASendTo
//sys GetHostByName(name string) (h *Hostent, err error) [failretval==nil] = ws2_32.gethostbyname
//sys GetServByName(name string, proto string) (s *Servent, err error) [failretval==nil] = ws2_32.getservbyname
//sys Ntohs(netshort uint16) (u uint16) = ws2_32.ntohs
//sys GetProtoByName(name string) (p *Protoent, err error) [failretval==nil] = ws2_32.getprotobyname
//sys DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status error) = dnsapi.DnsQuery_W
//sys DnsRecordListFree(rl *DNSRecord, freetype uint32) = dnsapi.DnsRecordListFree
+//sys GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) = ws2_32.GetAddrInfoW
+//sys FreeAddrInfoW(addrinfo *AddrinfoW) = ws2_32.FreeAddrInfoW
//sys GetIfEntry(pIfRow *MibIfRow) (errcode error) = iphlpapi.GetIfEntry
//sys GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) = iphlpapi.GetAdaptersInfo
@@ -476,6 +547,14 @@ type RawSockaddrInet4 struct {
Zero [8]uint8
}
+type RawSockaddrInet6 struct {
+ Family uint16
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
type RawSockaddr struct {
Family uint16
Data [14]int8
@@ -514,11 +593,22 @@ type SockaddrInet6 struct {
Port int
ZoneId uint32
Addr [16]byte
+ raw RawSockaddrInet6
}
func (sa *SockaddrInet6) sockaddr() (uintptr, int32, error) {
- // TODO(brainman): implement SockaddrInet6.sockaddr()
- return 0, 0, EWINDOWS
+ if sa.Port < 0 || sa.Port > 0xFFFF {
+ return 0, 0, EINVAL
+ }
+ sa.raw.Family = AF_INET6
+ p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
+ p[0] = byte(sa.Port >> 8)
+ p[1] = byte(sa.Port)
+ sa.raw.Scope_id = sa.ZoneId
+ for i := 0; i < len(sa.Addr); i++ {
+ sa.raw.Addr[i] = sa.Addr[i]
+ }
+ return uintptr(unsafe.Pointer(&sa.raw)), int32(unsafe.Sizeof(sa.raw)), nil
}
type SockaddrUnix struct {
@@ -546,7 +636,15 @@ func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, error) {
return sa, nil
case AF_INET6:
- return nil, EWINDOWS
+ pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa))
+ sa := new(SockaddrInet6)
+ p := (*[2]byte)(unsafe.Pointer(&pp.Port))
+ sa.Port = int(p[0])<<8 + int(p[1])
+ sa.ZoneId = pp.Scope_id
+ for i := 0; i < len(sa.Addr); i++ {
+ sa.Addr[i] = pp.Addr[i]
+ }
+ return sa, nil
}
return nil, EAFNOSUPPORT
}
@@ -613,6 +711,60 @@ func WSASendto(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32
return WSASendTo(s, bufs, bufcnt, sent, flags, (*RawSockaddrAny)(unsafe.Pointer(rsa)), l, overlapped, croutine)
}
+func LoadGetAddrInfo() error {
+ return procGetAddrInfoW.Find()
+}
+
+var connectExFunc struct {
+ once sync.Once
+ addr uintptr
+ err error
+}
+
+func LoadConnectEx() error {
+ connectExFunc.once.Do(func() {
+ var s Handle
+ s, connectExFunc.err = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
+ if connectExFunc.err != nil {
+ return
+ }
+ defer CloseHandle(s)
+ var n uint32
+ connectExFunc.err = WSAIoctl(s,
+ SIO_GET_EXTENSION_FUNCTION_POINTER,
+ (*byte)(unsafe.Pointer(&WSAID_CONNECTEX)),
+ uint32(unsafe.Sizeof(WSAID_CONNECTEX)),
+ (*byte)(unsafe.Pointer(&connectExFunc.addr)),
+ uint32(unsafe.Sizeof(connectExFunc.addr)),
+ &n, nil, 0)
+ })
+ return connectExFunc.err
+}
+
+func connectEx(s Handle, name uintptr, namelen int32, sendBuf *byte, sendDataLen uint32, bytesSent *uint32, overlapped *Overlapped) (err error) {
+ r1, _, e1 := Syscall9(connectExFunc.addr, 7, uintptr(s), uintptr(name), uintptr(namelen), uintptr(unsafe.Pointer(sendBuf)), uintptr(sendDataLen), uintptr(unsafe.Pointer(bytesSent)), uintptr(unsafe.Pointer(overlapped)), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func ConnectEx(fd Handle, sa Sockaddr, sendBuf *byte, sendDataLen uint32, bytesSent *uint32, overlapped *Overlapped) error {
+ err := LoadConnectEx()
+ if err != nil {
+ return errorspkg.New("failed to find ConnectEx: " + err.Error())
+ }
+ ptr, n, err := sa.sockaddr()
+ if err != nil {
+ return err
+ }
+ return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped)
+}
+
// Invented structures to support what package os expects.
type Rusage struct {
CreationTime Filetime
@@ -650,6 +802,14 @@ type Timespec struct {
Nsec int64
}
+func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
+
+func NsecToTimespec(nsec int64) (ts Timespec) {
+ ts.Sec = nsec / 1e9
+ ts.Nsec = nsec % 1e9
+ return
+}
+
// TODO(brainman): fix all needed for net
func Accept(fd Handle) (nfd Handle, sa Sockaddr, err error) { return 0, nil, EWINDOWS }
@@ -659,11 +819,23 @@ func Recvfrom(fd Handle, p []byte, flags int) (n int, from Sockaddr, err error)
func Sendto(fd Handle, p []byte, flags int, to Sockaddr) (err error) { return EWINDOWS }
func SetsockoptTimeval(fd Handle, level, opt int, tv *Timeval) (err error) { return EWINDOWS }
+// The Linger struct is wrong but we only noticed after Go 1.
+// sysLinger is the real system call structure.
+
+// BUG(brainman): The definition of Linger is not appropriate for direct use
+// with Setsockopt and Getsockopt.
+// Use SetsockoptLinger instead.
+
type Linger struct {
Onoff int32
Linger int32
}
+type sysLinger struct {
+ Onoff uint16
+ Linger uint16
+}
+
type IPMreq struct {
Multiaddr [4]byte /* in_addr */
Interface [4]byte /* in_addr */
@@ -674,8 +846,13 @@ type IPv6Mreq struct {
Interface uint32
}
-func GetsockoptInt(fd Handle, level, opt int) (int, error) { return -1, EWINDOWS }
-func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) { return EWINDOWS }
+func GetsockoptInt(fd Handle, level, opt int) (int, error) { return -1, EWINDOWS }
+
+func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) {
+ sys := sysLinger{Onoff: uint16(l.Onoff), Linger: uint16(l.Linger)}
+ return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&sys)), int32(unsafe.Sizeof(sys)))
+}
+
func SetsockoptInet4Addr(fd Handle, level, opt int, value [4]byte) (err error) {
return Setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&value[0])), 4)
}
diff --git a/src/pkg/syscall/syscall_windows_test.go b/src/pkg/syscall/syscall_windows_test.go
index 79cd8f869..86842f2ad 100644
--- a/src/pkg/syscall/syscall_windows_test.go
+++ b/src/pkg/syscall/syscall_windows_test.go
@@ -35,7 +35,8 @@ func TestWin32finddata(t *testing.T) {
var want byte = 2 // it is unlikely to have this character in the filename
x := X{got: want}
- h, err := syscall.FindFirstFile(syscall.StringToUTF16Ptr(path), &(x.fd))
+ pathp, _ := syscall.UTF16PtrFromString(path)
+ h, err := syscall.FindFirstFile(pathp, &(x.fd))
if err != nil {
t.Fatalf("FindFirstFile failed: %v", err)
}
@@ -48,3 +49,24 @@ func TestWin32finddata(t *testing.T) {
t.Fatalf("memory corruption: want=%d got=%d", want, x.got)
}
}
+
+func abort(funcname string, err error) {
+ panic(funcname + " failed: " + err.Error())
+}
+
+func ExampleLoadLibrary() {
+ h, err := syscall.LoadLibrary("kernel32.dll")
+ if err != nil {
+ abort("LoadLibrary", err)
+ }
+ defer syscall.FreeLibrary(h)
+ proc, err := syscall.GetProcAddress(h, "GetVersion")
+ if err != nil {
+ abort("GetProcAddress", err)
+ }
+ r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
+ major := byte(r)
+ minor := uint8(r >> 8)
+ build := uint16(r >> 16)
+ print("windows version ", major, ".", minor, " (Build ", build, ")\n")
+}
diff --git a/src/pkg/syscall/types_darwin.go b/src/pkg/syscall/types_darwin.go
index b9c65e3c7..098bbff6f 100644
--- a/src/pkg/syscall/types_darwin.go
+++ b/src/pkg/syscall/types_darwin.go
@@ -20,6 +20,7 @@ package syscall
#include <dirent.h>
#include <fcntl.h>
#include <signal.h>
+#include <termios.h>
#include <unistd.h>
#include <mach/mach.h>
#include <mach/message.h>
@@ -149,6 +150,8 @@ type Msghdr C.struct_msghdr
type Cmsghdr C.struct_cmsghdr
+type Inet4Pktinfo C.struct_in_pktinfo
+
type Inet6Pktinfo C.struct_in6_pktinfo
const (
@@ -162,6 +165,7 @@ const (
SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
SizeofMsghdr = C.sizeof_struct_msghdr
SizeofCmsghdr = C.sizeof_struct_cmsghdr
+ SizeofInet4Pktinfo = C.sizeof_struct_in_pktinfo
SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
)
@@ -226,3 +230,7 @@ type BpfProgram C.struct_bpf_program
type BpfInsn C.struct_bpf_insn
type BpfHdr C.struct_bpf_hdr
+
+// Terminal handling
+
+type Termios C.struct_termios
diff --git a/src/pkg/syscall/types_freebsd.go b/src/pkg/syscall/types_freebsd.go
index 6e1dd4310..7d4923d5a 100644
--- a/src/pkg/syscall/types_freebsd.go
+++ b/src/pkg/syscall/types_freebsd.go
@@ -193,12 +193,13 @@ type FdSet C.fd_set
// Routing and interface messages
const (
- SizeofIfMsghdr = C.sizeof_struct_if_msghdr
- SizeofIfData = C.sizeof_struct_if_data
- SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr
- SizeofIfmaMsghdr = C.sizeof_struct_ifma_msghdr
- SizeofRtMsghdr = C.sizeof_struct_rt_msghdr
- SizeofRtMetrics = C.sizeof_struct_rt_metrics
+ SizeofIfMsghdr = C.sizeof_struct_if_msghdr
+ SizeofIfData = C.sizeof_struct_if_data
+ SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr
+ SizeofIfmaMsghdr = C.sizeof_struct_ifma_msghdr
+ SizeofIfAnnounceMsghdr = C.sizeof_struct_if_announcemsghdr
+ SizeofRtMsghdr = C.sizeof_struct_rt_msghdr
+ SizeofRtMetrics = C.sizeof_struct_rt_metrics
)
type IfMsghdr C.struct_if_msghdr
@@ -209,6 +210,8 @@ type IfaMsghdr C.struct_ifa_msghdr
type IfmaMsghdr C.struct_ifma_msghdr
+type IfAnnounceMsghdr C.struct_if_announcemsghdr
+
type RtMsghdr C.struct_rt_msghdr
type RtMetrics C.struct_rt_metrics
diff --git a/src/pkg/syscall/types_linux.go b/src/pkg/syscall/types_linux.go
index 0030960b5..1514cbc95 100644
--- a/src/pkg/syscall/types_linux.go
+++ b/src/pkg/syscall/types_linux.go
@@ -73,7 +73,7 @@ struct sockaddr_any {
char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)];
};
-// copied from /usr/include/linux/un.h
+// copied from /usr/include/linux/un.h
struct my_sockaddr_un {
sa_family_t sun_family;
#ifdef __ARM_EABI__
@@ -195,6 +195,8 @@ type Inet6Pktinfo C.struct_in6_pktinfo
type Ucred C.struct_ucred
+type TCPInfo C.struct_tcp_info
+
const (
SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in
SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
@@ -211,86 +213,103 @@ const (
SizeofInet4Pktinfo = C.sizeof_struct_in_pktinfo
SizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo
SizeofUcred = C.sizeof_struct_ucred
+ SizeofTCPInfo = C.sizeof_struct_tcp_info
)
// Netlink routing and interface messages
const (
- IFA_UNSPEC = C.IFA_UNSPEC
- IFA_ADDRESS = C.IFA_ADDRESS
- IFA_LOCAL = C.IFA_LOCAL
- IFA_LABEL = C.IFA_LABEL
- IFA_BROADCAST = C.IFA_BROADCAST
- IFA_ANYCAST = C.IFA_ANYCAST
- IFA_CACHEINFO = C.IFA_CACHEINFO
- IFA_MULTICAST = C.IFA_MULTICAST
- IFLA_UNSPEC = C.IFLA_UNSPEC
- IFLA_ADDRESS = C.IFLA_ADDRESS
- IFLA_BROADCAST = C.IFLA_BROADCAST
- IFLA_IFNAME = C.IFLA_IFNAME
- IFLA_MTU = C.IFLA_MTU
- IFLA_LINK = C.IFLA_LINK
- IFLA_QDISC = C.IFLA_QDISC
- IFLA_STATS = C.IFLA_STATS
- IFLA_COST = C.IFLA_COST
- IFLA_PRIORITY = C.IFLA_PRIORITY
- IFLA_MASTER = C.IFLA_MASTER
- IFLA_WIRELESS = C.IFLA_WIRELESS
- IFLA_PROTINFO = C.IFLA_PROTINFO
- IFLA_TXQLEN = C.IFLA_TXQLEN
- IFLA_MAP = C.IFLA_MAP
- IFLA_WEIGHT = C.IFLA_WEIGHT
- IFLA_OPERSTATE = C.IFLA_OPERSTATE
- IFLA_LINKMODE = C.IFLA_LINKMODE
- IFLA_LINKINFO = C.IFLA_LINKINFO
- IFLA_NET_NS_PID = C.IFLA_NET_NS_PID
- IFLA_IFALIAS = C.IFLA_IFALIAS
- IFLA_MAX = C.IFLA_MAX
- RT_SCOPE_UNIVERSE = C.RT_SCOPE_UNIVERSE
- RT_SCOPE_SITE = C.RT_SCOPE_SITE
- RT_SCOPE_LINK = C.RT_SCOPE_LINK
- RT_SCOPE_HOST = C.RT_SCOPE_HOST
- RT_SCOPE_NOWHERE = C.RT_SCOPE_NOWHERE
- RT_TABLE_UNSPEC = C.RT_TABLE_UNSPEC
- RT_TABLE_COMPAT = C.RT_TABLE_COMPAT
- RT_TABLE_DEFAULT = C.RT_TABLE_DEFAULT
- RT_TABLE_MAIN = C.RT_TABLE_MAIN
- RT_TABLE_LOCAL = C.RT_TABLE_LOCAL
- RT_TABLE_MAX = C.RT_TABLE_MAX
- RTA_UNSPEC = C.RTA_UNSPEC
- RTA_DST = C.RTA_DST
- RTA_SRC = C.RTA_SRC
- RTA_IIF = C.RTA_IIF
- RTA_OIF = C.RTA_OIF
- RTA_GATEWAY = C.RTA_GATEWAY
- RTA_PRIORITY = C.RTA_PRIORITY
- RTA_PREFSRC = C.RTA_PREFSRC
- RTA_METRICS = C.RTA_METRICS
- RTA_MULTIPATH = C.RTA_MULTIPATH
- RTA_FLOW = C.RTA_FLOW
- RTA_CACHEINFO = C.RTA_CACHEINFO
- RTA_TABLE = C.RTA_TABLE
- RTN_UNSPEC = C.RTN_UNSPEC
- RTN_UNICAST = C.RTN_UNICAST
- RTN_LOCAL = C.RTN_LOCAL
- RTN_BROADCAST = C.RTN_BROADCAST
- RTN_ANYCAST = C.RTN_ANYCAST
- RTN_MULTICAST = C.RTN_MULTICAST
- RTN_BLACKHOLE = C.RTN_BLACKHOLE
- RTN_UNREACHABLE = C.RTN_UNREACHABLE
- RTN_PROHIBIT = C.RTN_PROHIBIT
- RTN_THROW = C.RTN_THROW
- RTN_NAT = C.RTN_NAT
- RTN_XRESOLVE = C.RTN_XRESOLVE
- SizeofNlMsghdr = C.sizeof_struct_nlmsghdr
- SizeofNlMsgerr = C.sizeof_struct_nlmsgerr
- SizeofRtGenmsg = C.sizeof_struct_rtgenmsg
- SizeofNlAttr = C.sizeof_struct_nlattr
- SizeofRtAttr = C.sizeof_struct_rtattr
- SizeofIfInfomsg = C.sizeof_struct_ifinfomsg
- SizeofIfAddrmsg = C.sizeof_struct_ifaddrmsg
- SizeofRtMsg = C.sizeof_struct_rtmsg
- SizeofRtNexthop = C.sizeof_struct_rtnexthop
+ IFA_UNSPEC = C.IFA_UNSPEC
+ IFA_ADDRESS = C.IFA_ADDRESS
+ IFA_LOCAL = C.IFA_LOCAL
+ IFA_LABEL = C.IFA_LABEL
+ IFA_BROADCAST = C.IFA_BROADCAST
+ IFA_ANYCAST = C.IFA_ANYCAST
+ IFA_CACHEINFO = C.IFA_CACHEINFO
+ IFA_MULTICAST = C.IFA_MULTICAST
+ IFLA_UNSPEC = C.IFLA_UNSPEC
+ IFLA_ADDRESS = C.IFLA_ADDRESS
+ IFLA_BROADCAST = C.IFLA_BROADCAST
+ IFLA_IFNAME = C.IFLA_IFNAME
+ IFLA_MTU = C.IFLA_MTU
+ IFLA_LINK = C.IFLA_LINK
+ IFLA_QDISC = C.IFLA_QDISC
+ IFLA_STATS = C.IFLA_STATS
+ IFLA_COST = C.IFLA_COST
+ IFLA_PRIORITY = C.IFLA_PRIORITY
+ IFLA_MASTER = C.IFLA_MASTER
+ IFLA_WIRELESS = C.IFLA_WIRELESS
+ IFLA_PROTINFO = C.IFLA_PROTINFO
+ IFLA_TXQLEN = C.IFLA_TXQLEN
+ IFLA_MAP = C.IFLA_MAP
+ IFLA_WEIGHT = C.IFLA_WEIGHT
+ IFLA_OPERSTATE = C.IFLA_OPERSTATE
+ IFLA_LINKMODE = C.IFLA_LINKMODE
+ IFLA_LINKINFO = C.IFLA_LINKINFO
+ IFLA_NET_NS_PID = C.IFLA_NET_NS_PID
+ IFLA_IFALIAS = C.IFLA_IFALIAS
+ IFLA_MAX = C.IFLA_MAX
+ RT_SCOPE_UNIVERSE = C.RT_SCOPE_UNIVERSE
+ RT_SCOPE_SITE = C.RT_SCOPE_SITE
+ RT_SCOPE_LINK = C.RT_SCOPE_LINK
+ RT_SCOPE_HOST = C.RT_SCOPE_HOST
+ RT_SCOPE_NOWHERE = C.RT_SCOPE_NOWHERE
+ RT_TABLE_UNSPEC = C.RT_TABLE_UNSPEC
+ RT_TABLE_COMPAT = C.RT_TABLE_COMPAT
+ RT_TABLE_DEFAULT = C.RT_TABLE_DEFAULT
+ RT_TABLE_MAIN = C.RT_TABLE_MAIN
+ RT_TABLE_LOCAL = C.RT_TABLE_LOCAL
+ RT_TABLE_MAX = C.RT_TABLE_MAX
+ RTA_UNSPEC = C.RTA_UNSPEC
+ RTA_DST = C.RTA_DST
+ RTA_SRC = C.RTA_SRC
+ RTA_IIF = C.RTA_IIF
+ RTA_OIF = C.RTA_OIF
+ RTA_GATEWAY = C.RTA_GATEWAY
+ RTA_PRIORITY = C.RTA_PRIORITY
+ RTA_PREFSRC = C.RTA_PREFSRC
+ RTA_METRICS = C.RTA_METRICS
+ RTA_MULTIPATH = C.RTA_MULTIPATH
+ RTA_FLOW = C.RTA_FLOW
+ RTA_CACHEINFO = C.RTA_CACHEINFO
+ RTA_TABLE = C.RTA_TABLE
+ RTN_UNSPEC = C.RTN_UNSPEC
+ RTN_UNICAST = C.RTN_UNICAST
+ RTN_LOCAL = C.RTN_LOCAL
+ RTN_BROADCAST = C.RTN_BROADCAST
+ RTN_ANYCAST = C.RTN_ANYCAST
+ RTN_MULTICAST = C.RTN_MULTICAST
+ RTN_BLACKHOLE = C.RTN_BLACKHOLE
+ RTN_UNREACHABLE = C.RTN_UNREACHABLE
+ RTN_PROHIBIT = C.RTN_PROHIBIT
+ RTN_THROW = C.RTN_THROW
+ RTN_NAT = C.RTN_NAT
+ RTN_XRESOLVE = C.RTN_XRESOLVE
+ RTNLGRP_NONE = C.RTNLGRP_NONE
+ RTNLGRP_LINK = C.RTNLGRP_LINK
+ RTNLGRP_NOTIFY = C.RTNLGRP_NOTIFY
+ RTNLGRP_NEIGH = C.RTNLGRP_NEIGH
+ RTNLGRP_TC = C.RTNLGRP_TC
+ RTNLGRP_IPV4_IFADDR = C.RTNLGRP_IPV4_IFADDR
+ RTNLGRP_IPV4_MROUTE = C.RTNLGRP_IPV4_MROUTE
+ RTNLGRP_IPV4_ROUTE = C.RTNLGRP_IPV4_ROUTE
+ RTNLGRP_IPV4_RULE = C.RTNLGRP_IPV4_RULE
+ RTNLGRP_IPV6_IFADDR = C.RTNLGRP_IPV6_IFADDR
+ RTNLGRP_IPV6_MROUTE = C.RTNLGRP_IPV6_MROUTE
+ RTNLGRP_IPV6_ROUTE = C.RTNLGRP_IPV6_ROUTE
+ RTNLGRP_IPV6_IFINFO = C.RTNLGRP_IPV6_IFINFO
+ RTNLGRP_IPV6_PREFIX = C.RTNLGRP_IPV6_PREFIX
+ RTNLGRP_IPV6_RULE = C.RTNLGRP_IPV6_RULE
+ RTNLGRP_ND_USEROPT = C.RTNLGRP_ND_USEROPT
+ SizeofNlMsghdr = C.sizeof_struct_nlmsghdr
+ SizeofNlMsgerr = C.sizeof_struct_nlmsgerr
+ SizeofRtGenmsg = C.sizeof_struct_rtgenmsg
+ SizeofNlAttr = C.sizeof_struct_nlattr
+ SizeofRtAttr = C.sizeof_struct_rtattr
+ SizeofIfInfomsg = C.sizeof_struct_ifinfomsg
+ SizeofIfAddrmsg = C.sizeof_struct_ifaddrmsg
+ SizeofRtMsg = C.sizeof_struct_rtmsg
+ SizeofRtNexthop = C.sizeof_struct_rtnexthop
)
type NlMsghdr C.struct_nlmsghdr
@@ -345,6 +364,10 @@ type Ustat_t C.struct_ustat
type EpollEvent C.struct_my_epoll_event
+const (
+ _AT_FDCWD = C.AT_FDCWD
+)
+
// Terminal handling
type Termios C.struct_termios
diff --git a/src/pkg/syscall/types_netbsd.go b/src/pkg/syscall/types_netbsd.go
index 518688833..4906a99ef 100644
--- a/src/pkg/syscall/types_netbsd.go
+++ b/src/pkg/syscall/types_netbsd.go
@@ -31,6 +31,7 @@ package syscall
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/un.h>
@@ -97,23 +98,6 @@ type _Gid_t C.gid_t
// Files
-const ( // Directory mode bits
- S_IFMT = C.S_IFMT
- S_IFIFO = C.S_IFIFO
- S_IFCHR = C.S_IFCHR
- S_IFDIR = C.S_IFDIR
- S_IFBLK = C.S_IFBLK
- S_IFREG = C.S_IFREG
- S_IFLNK = C.S_IFLNK
- S_IFSOCK = C.S_IFSOCK
- S_ISUID = C.S_ISUID
- S_ISGID = C.S_ISGID
- S_ISVTX = C.S_ISVTX
- S_IRUSR = C.S_IRUSR
- S_IWUSR = C.S_IWUSR
- S_IXUSR = C.S_IXUSR
-)
-
type Stat_t C.struct_stat
type Statfs_t C.struct_statfs
@@ -187,11 +171,12 @@ type FdSet C.fd_set
// Routing and interface messages
const (
- SizeofIfMsghdr = C.sizeof_struct_if_msghdr
- SizeofIfData = C.sizeof_struct_if_data
- SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr
- SizeofRtMsghdr = C.sizeof_struct_rt_msghdr
- SizeofRtMetrics = C.sizeof_struct_rt_metrics
+ SizeofIfMsghdr = C.sizeof_struct_if_msghdr
+ SizeofIfData = C.sizeof_struct_if_data
+ SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr
+ SizeofIfAnnounceMsghdr = C.sizeof_struct_if_announcemsghdr
+ SizeofRtMsghdr = C.sizeof_struct_rt_msghdr
+ SizeofRtMetrics = C.sizeof_struct_rt_metrics
)
type IfMsghdr C.struct_if_msghdr
@@ -200,6 +185,8 @@ type IfData C.struct_if_data
type IfaMsghdr C.struct_ifa_msghdr
+type IfAnnounceMsghdr C.struct_if_announcemsghdr
+
type RtMsghdr C.struct_rt_msghdr
type RtMetrics C.struct_rt_metrics
@@ -227,3 +214,7 @@ type BpfInsn C.struct_bpf_insn
type BpfHdr C.struct_bpf_hdr
type BpfTimeval C.struct_bpf_timeval
+
+// Sysctl
+
+type Sysctlnode C.struct_sysctlnode
diff --git a/src/pkg/syscall/types_openbsd.go b/src/pkg/syscall/types_openbsd.go
index 518688833..2430a166b 100644
--- a/src/pkg/syscall/types_openbsd.go
+++ b/src/pkg/syscall/types_openbsd.go
@@ -187,11 +187,12 @@ type FdSet C.fd_set
// Routing and interface messages
const (
- SizeofIfMsghdr = C.sizeof_struct_if_msghdr
- SizeofIfData = C.sizeof_struct_if_data
- SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr
- SizeofRtMsghdr = C.sizeof_struct_rt_msghdr
- SizeofRtMetrics = C.sizeof_struct_rt_metrics
+ SizeofIfMsghdr = C.sizeof_struct_if_msghdr
+ SizeofIfData = C.sizeof_struct_if_data
+ SizeofIfaMsghdr = C.sizeof_struct_ifa_msghdr
+ SizeofIfAnnounceMsghdr = C.sizeof_struct_if_announcemsghdr
+ SizeofRtMsghdr = C.sizeof_struct_rt_msghdr
+ SizeofRtMetrics = C.sizeof_struct_rt_metrics
)
type IfMsghdr C.struct_if_msghdr
@@ -200,6 +201,8 @@ type IfData C.struct_if_data
type IfaMsghdr C.struct_ifa_msghdr
+type IfAnnounceMsghdr C.struct_if_announcemsghdr
+
type RtMsghdr C.struct_rt_msghdr
type RtMetrics C.struct_rt_metrics
diff --git a/src/pkg/syscall/zerrors_darwin_386.go b/src/pkg/syscall/zerrors_darwin_386.go
index 09d7b0fe9..cdb860543 100644
--- a/src/pkg/syscall/zerrors_darwin_386.go
+++ b/src/pkg/syscall/zerrors_darwin_386.go
@@ -43,6 +43,29 @@ const (
AF_SYSTEM = 0x20
AF_UNIX = 0x1
AF_UNSPEC = 0x0
+ B0 = 0x0
+ B110 = 0x6e
+ B115200 = 0x1c200
+ B1200 = 0x4b0
+ B134 = 0x86
+ B14400 = 0x3840
+ B150 = 0x96
+ B1800 = 0x708
+ B19200 = 0x4b00
+ B200 = 0xc8
+ B230400 = 0x38400
+ B2400 = 0x960
+ B28800 = 0x7080
+ B300 = 0x12c
+ B38400 = 0x9600
+ B4800 = 0x12c0
+ B50 = 0x32
+ B57600 = 0xe100
+ B600 = 0x258
+ B7200 = 0x1c20
+ B75 = 0x4b
+ B76800 = 0x12c00
+ B9600 = 0x2580
BIOCFLUSH = 0x20004268
BIOCGBLEN = 0x40044266
BIOCGDLT = 0x4004426a
@@ -108,6 +131,20 @@ const (
BPF_TXA = 0x80
BPF_W = 0x0
BPF_X = 0x8
+ BRKINT = 0x2
+ CFLUSH = 0xf
+ CLOCAL = 0x8000
+ CREAD = 0x800
+ CS5 = 0x0
+ CS6 = 0x100
+ CS7 = 0x200
+ CS8 = 0x300
+ CSIZE = 0x300
+ CSTART = 0x11
+ CSTATUS = 0x14
+ CSTOP = 0x13
+ CSTOPB = 0x400
+ CSUSP = 0x1a
CTL_MAXNAME = 0xc
CTL_NET = 0x4
DLT_APPLE_IP_OVER_IEEE1394 = 0x8a
@@ -186,6 +223,7 @@ const (
EXTPROC = 0x800
FD_CLOEXEC = 0x1
FD_SETSIZE = 0x400
+ FLUSHO = 0x800000
F_ADDFILESIGS = 0x3d
F_ADDSIGS = 0x3b
F_ALLOCATEALL = 0x4
@@ -233,6 +271,10 @@ const (
F_VOLPOSMODE = 0x4
F_WRITEBOOTSTRAP = 0x2f
F_WRLCK = 0x3
+ HUPCL = 0x4000
+ ICANON = 0x100
+ ICRNL = 0x100
+ IEXTEN = 0x400
IFF_ALLMULTI = 0x200
IFF_ALTPHYS = 0x4000
IFF_BROADCAST = 0x2
@@ -318,6 +360,12 @@ const (
IFT_X25DDN = 0x4
IFT_X25PLE = 0x28
IFT_XETHER = 0x1a
+ IGNBRK = 0x1
+ IGNCR = 0x80
+ IGNPAR = 0x4
+ IMAXBEL = 0x2000
+ INLCR = 0x40
+ INPCK = 0x10
IN_CLASSA_HOST = 0xffffff
IN_CLASSA_MAX = 0x80
IN_CLASSA_NET = 0xff000000
@@ -560,6 +608,12 @@ const (
IP_TRAFFIC_MGT_BACKGROUND = 0x41
IP_TTL = 0x4
IP_UNBLOCK_SOURCE = 0x49
+ ISIG = 0x80
+ ISTRIP = 0x20
+ IUTF8 = 0x4000
+ IXANY = 0x800
+ IXOFF = 0x400
+ IXON = 0x200
LOCK_EX = 0x2
LOCK_NB = 0x4
LOCK_SH = 0x1
@@ -619,6 +673,7 @@ const (
NET_RT_MAXID = 0xa
NET_RT_STAT = 0x4
NET_RT_TRASH = 0x5
+ NOFLSH = 0x80000000
NOTE_ABSOLUTE = 0x8
NOTE_ATTRIB = 0x8
NOTE_CHILD = 0x4
@@ -655,6 +710,14 @@ const (
NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000
NOTE_VM_PRESSURE_TERMINATE = 0x40000000
NOTE_WRITE = 0x2
+ OCRNL = 0x10
+ OFDEL = 0x20000
+ OFILL = 0x80
+ ONLCR = 0x2
+ ONLRET = 0x40
+ ONOCR = 0x20
+ ONOEOT = 0x8
+ OPOST = 0x1
O_ACCMODE = 0x3
O_ALERT = 0x20000000
O_APPEND = 0x8
@@ -679,6 +742,10 @@ const (
O_SYNC = 0x80
O_TRUNC = 0x400
O_WRONLY = 0x1
+ PARENB = 0x1000
+ PARMRK = 0x8
+ PARODD = 0x2000
+ PENDIN = 0x20000000
PROT_EXEC = 0x4
PROT_NONE = 0x0
PROT_READ = 0x1
@@ -937,6 +1004,9 @@ const (
S_IXGRP = 0x8
S_IXOTH = 0x1
S_IXUSR = 0x40
+ TCIFLUSH = 0x1
+ TCIOFLUSH = 0x3
+ TCOFLUSH = 0x2
TCP_CONNECTIONTIMEOUT = 0x20
TCP_KEEPALIVE = 0x10
TCP_MAXHLEN = 0x3c
@@ -953,6 +1023,7 @@ const (
TCP_NOPUSH = 0x4
TCP_RXT_CONNDROPTIME = 0x80
TCP_RXT_FINDROP = 0x100
+ TCSAFLUSH = 0x2
TIOCCBRK = 0x2000747a
TIOCCDTR = 0x20007478
TIOCCONS = 0x80047462
@@ -1022,6 +1093,28 @@ const (
TIOCSWINSZ = 0x80087467
TIOCTIMESTAMP = 0x40087459
TIOCUCNTL = 0x80047466
+ TOSTOP = 0x400000
+ VDISCARD = 0xf
+ VDSUSP = 0xb
+ VEOF = 0x0
+ VEOL = 0x1
+ VEOL2 = 0x2
+ VERASE = 0x3
+ VINTR = 0x8
+ VKILL = 0x5
+ VLNEXT = 0xe
+ VMIN = 0x10
+ VQUIT = 0x9
+ VREPRINT = 0x6
+ VSTART = 0xc
+ VSTATUS = 0x12
+ VSTOP = 0xd
+ VSUSP = 0xa
+ VT0 = 0x0
+ VT1 = 0x10000
+ VTDLY = 0x10000
+ VTIME = 0x11
+ VWERASE = 0x4
WCONTINUED = 0x10
WCOREFLAG = 0x80
WEXITED = 0x4
diff --git a/src/pkg/syscall/zerrors_darwin_amd64.go b/src/pkg/syscall/zerrors_darwin_amd64.go
index 7fa5cfcd2..8ffcbcc04 100644
--- a/src/pkg/syscall/zerrors_darwin_amd64.go
+++ b/src/pkg/syscall/zerrors_darwin_amd64.go
@@ -43,6 +43,29 @@ const (
AF_SYSTEM = 0x20
AF_UNIX = 0x1
AF_UNSPEC = 0x0
+ B0 = 0x0
+ B110 = 0x6e
+ B115200 = 0x1c200
+ B1200 = 0x4b0
+ B134 = 0x86
+ B14400 = 0x3840
+ B150 = 0x96
+ B1800 = 0x708
+ B19200 = 0x4b00
+ B200 = 0xc8
+ B230400 = 0x38400
+ B2400 = 0x960
+ B28800 = 0x7080
+ B300 = 0x12c
+ B38400 = 0x9600
+ B4800 = 0x12c0
+ B50 = 0x32
+ B57600 = 0xe100
+ B600 = 0x258
+ B7200 = 0x1c20
+ B75 = 0x4b
+ B76800 = 0x12c00
+ B9600 = 0x2580
BIOCFLUSH = 0x20004268
BIOCGBLEN = 0x40044266
BIOCGDLT = 0x4004426a
@@ -108,6 +131,20 @@ const (
BPF_TXA = 0x80
BPF_W = 0x0
BPF_X = 0x8
+ BRKINT = 0x2
+ CFLUSH = 0xf
+ CLOCAL = 0x8000
+ CREAD = 0x800
+ CS5 = 0x0
+ CS6 = 0x100
+ CS7 = 0x200
+ CS8 = 0x300
+ CSIZE = 0x300
+ CSTART = 0x11
+ CSTATUS = 0x14
+ CSTOP = 0x13
+ CSTOPB = 0x400
+ CSUSP = 0x1a
CTL_MAXNAME = 0xc
CTL_NET = 0x4
DLT_APPLE_IP_OVER_IEEE1394 = 0x8a
@@ -186,6 +223,7 @@ const (
EXTPROC = 0x800
FD_CLOEXEC = 0x1
FD_SETSIZE = 0x400
+ FLUSHO = 0x800000
F_ADDFILESIGS = 0x3d
F_ADDSIGS = 0x3b
F_ALLOCATEALL = 0x4
@@ -233,6 +271,10 @@ const (
F_VOLPOSMODE = 0x4
F_WRITEBOOTSTRAP = 0x2f
F_WRLCK = 0x3
+ HUPCL = 0x4000
+ ICANON = 0x100
+ ICRNL = 0x100
+ IEXTEN = 0x400
IFF_ALLMULTI = 0x200
IFF_ALTPHYS = 0x4000
IFF_BROADCAST = 0x2
@@ -318,6 +360,12 @@ const (
IFT_X25DDN = 0x4
IFT_X25PLE = 0x28
IFT_XETHER = 0x1a
+ IGNBRK = 0x1
+ IGNCR = 0x80
+ IGNPAR = 0x4
+ IMAXBEL = 0x2000
+ INLCR = 0x40
+ INPCK = 0x10
IN_CLASSA_HOST = 0xffffff
IN_CLASSA_MAX = 0x80
IN_CLASSA_NET = 0xff000000
@@ -560,6 +608,12 @@ const (
IP_TRAFFIC_MGT_BACKGROUND = 0x41
IP_TTL = 0x4
IP_UNBLOCK_SOURCE = 0x49
+ ISIG = 0x80
+ ISTRIP = 0x20
+ IUTF8 = 0x4000
+ IXANY = 0x800
+ IXOFF = 0x400
+ IXON = 0x200
LOCK_EX = 0x2
LOCK_NB = 0x4
LOCK_SH = 0x1
@@ -619,6 +673,7 @@ const (
NET_RT_MAXID = 0xa
NET_RT_STAT = 0x4
NET_RT_TRASH = 0x5
+ NOFLSH = 0x80000000
NOTE_ABSOLUTE = 0x8
NOTE_ATTRIB = 0x8
NOTE_CHILD = 0x4
@@ -655,6 +710,14 @@ const (
NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000
NOTE_VM_PRESSURE_TERMINATE = 0x40000000
NOTE_WRITE = 0x2
+ OCRNL = 0x10
+ OFDEL = 0x20000
+ OFILL = 0x80
+ ONLCR = 0x2
+ ONLRET = 0x40
+ ONOCR = 0x20
+ ONOEOT = 0x8
+ OPOST = 0x1
O_ACCMODE = 0x3
O_ALERT = 0x20000000
O_APPEND = 0x8
@@ -679,6 +742,10 @@ const (
O_SYNC = 0x80
O_TRUNC = 0x400
O_WRONLY = 0x1
+ PARENB = 0x1000
+ PARMRK = 0x8
+ PARODD = 0x2000
+ PENDIN = 0x20000000
PROT_EXEC = 0x4
PROT_NONE = 0x0
PROT_READ = 0x1
@@ -937,6 +1004,9 @@ const (
S_IXGRP = 0x8
S_IXOTH = 0x1
S_IXUSR = 0x40
+ TCIFLUSH = 0x1
+ TCIOFLUSH = 0x3
+ TCOFLUSH = 0x2
TCP_CONNECTIONTIMEOUT = 0x20
TCP_KEEPALIVE = 0x10
TCP_MAXHLEN = 0x3c
@@ -953,6 +1023,7 @@ const (
TCP_NOPUSH = 0x4
TCP_RXT_CONNDROPTIME = 0x80
TCP_RXT_FINDROP = 0x100
+ TCSAFLUSH = 0x2
TIOCCBRK = 0x2000747a
TIOCCDTR = 0x20007478
TIOCCONS = 0x80047462
@@ -1022,6 +1093,28 @@ const (
TIOCSWINSZ = 0x80087467
TIOCTIMESTAMP = 0x40107459
TIOCUCNTL = 0x80047466
+ TOSTOP = 0x400000
+ VDISCARD = 0xf
+ VDSUSP = 0xb
+ VEOF = 0x0
+ VEOL = 0x1
+ VEOL2 = 0x2
+ VERASE = 0x3
+ VINTR = 0x8
+ VKILL = 0x5
+ VLNEXT = 0xe
+ VMIN = 0x10
+ VQUIT = 0x9
+ VREPRINT = 0x6
+ VSTART = 0xc
+ VSTATUS = 0x12
+ VSTOP = 0xd
+ VSUSP = 0xa
+ VT0 = 0x0
+ VT1 = 0x10000
+ VTDLY = 0x10000
+ VTIME = 0x11
+ VWERASE = 0x4
WCONTINUED = 0x10
WCOREFLAG = 0x80
WEXITED = 0x4
diff --git a/src/pkg/syscall/zerrors_freebsd_386.go b/src/pkg/syscall/zerrors_freebsd_386.go
index cc00f0dcf..9cf82f90e 100644
--- a/src/pkg/syscall/zerrors_freebsd_386.go
+++ b/src/pkg/syscall/zerrors_freebsd_386.go
@@ -369,6 +369,8 @@ const (
F_UNLCK = 0x2
F_UNLCKSYS = 0x4
F_WRLCK = 0x3
+ IFAN_ARRIVAL = 0x0
+ IFAN_DEPARTURE = 0x1
IFF_ALLMULTI = 0x200
IFF_ALTPHYS = 0x4000
IFF_BROADCAST = 0x2
diff --git a/src/pkg/syscall/zerrors_freebsd_amd64.go b/src/pkg/syscall/zerrors_freebsd_amd64.go
index b7b27b56f..732570659 100644
--- a/src/pkg/syscall/zerrors_freebsd_amd64.go
+++ b/src/pkg/syscall/zerrors_freebsd_amd64.go
@@ -369,6 +369,8 @@ const (
F_UNLCK = 0x2
F_UNLCKSYS = 0x4
F_WRLCK = 0x3
+ IFAN_ARRIVAL = 0x0
+ IFAN_DEPARTURE = 0x1
IFF_ALLMULTI = 0x200
IFF_ALTPHYS = 0x4000
IFF_BROADCAST = 0x2
diff --git a/src/pkg/syscall/zerrors_freebsd_arm.go b/src/pkg/syscall/zerrors_freebsd_arm.go
new file mode 100644
index 000000000..92cade265
--- /dev/null
+++ b/src/pkg/syscall/zerrors_freebsd_arm.go
@@ -0,0 +1,1652 @@
+// mkerrors.sh
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs -- _const.go
+
+package syscall
+
+const (
+ //O_CLOEXEC = 0x100000
+ AF_APPLETALK = 0x10
+ AF_ARP = 0x23
+ AF_ATM = 0x1e
+ AF_BLUETOOTH = 0x24
+ AF_CCITT = 0xa
+ AF_CHAOS = 0x5
+ AF_CNT = 0x15
+ AF_COIP = 0x14
+ AF_DATAKIT = 0x9
+ AF_DECnet = 0xc
+ AF_DLI = 0xd
+ AF_E164 = 0x1a
+ AF_ECMA = 0x8
+ AF_HYLINK = 0xf
+ AF_IEEE80211 = 0x25
+ AF_IMPLINK = 0x3
+ AF_INET = 0x2
+ AF_INET6 = 0x1c
+ AF_IPX = 0x17
+ AF_ISDN = 0x1a
+ AF_ISO = 0x7
+ AF_LAT = 0xe
+ AF_LINK = 0x12
+ AF_LOCAL = 0x1
+ AF_MAX = 0x26
+ AF_NATM = 0x1d
+ AF_NETBIOS = 0x6
+ AF_NETGRAPH = 0x20
+ AF_OSI = 0x7
+ AF_PUP = 0x4
+ AF_ROUTE = 0x11
+ AF_SCLUSTER = 0x22
+ AF_SIP = 0x18
+ AF_SLOW = 0x21
+ AF_SNA = 0xb
+ AF_UNIX = 0x1
+ AF_UNSPEC = 0x0
+ AF_VENDOR00 = 0x27
+ AF_VENDOR01 = 0x29
+ AF_VENDOR02 = 0x2b
+ AF_VENDOR03 = 0x2d
+ AF_VENDOR04 = 0x2f
+ AF_VENDOR05 = 0x31
+ AF_VENDOR06 = 0x33
+ AF_VENDOR07 = 0x35
+ AF_VENDOR08 = 0x37
+ AF_VENDOR09 = 0x39
+ AF_VENDOR10 = 0x3b
+ AF_VENDOR11 = 0x3d
+ AF_VENDOR12 = 0x3f
+ AF_VENDOR13 = 0x41
+ AF_VENDOR14 = 0x43
+ AF_VENDOR15 = 0x45
+ AF_VENDOR16 = 0x47
+ AF_VENDOR17 = 0x49
+ AF_VENDOR18 = 0x4b
+ AF_VENDOR19 = 0x4d
+ AF_VENDOR20 = 0x4f
+ AF_VENDOR21 = 0x51
+ AF_VENDOR22 = 0x53
+ AF_VENDOR23 = 0x55
+ AF_VENDOR24 = 0x57
+ AF_VENDOR25 = 0x59
+ AF_VENDOR26 = 0x5b
+ AF_VENDOR27 = 0x5d
+ AF_VENDOR28 = 0x5f
+ AF_VENDOR29 = 0x61
+ AF_VENDOR30 = 0x63
+ AF_VENDOR31 = 0x65
+ AF_VENDOR32 = 0x67
+ AF_VENDOR33 = 0x69
+ AF_VENDOR34 = 0x6b
+ AF_VENDOR35 = 0x6d
+ AF_VENDOR36 = 0x6f
+ AF_VENDOR37 = 0x71
+ AF_VENDOR38 = 0x73
+ AF_VENDOR39 = 0x75
+ AF_VENDOR40 = 0x77
+ AF_VENDOR41 = 0x79
+ AF_VENDOR42 = 0x7b
+ AF_VENDOR43 = 0x7d
+ AF_VENDOR44 = 0x7f
+ AF_VENDOR45 = 0x81
+ AF_VENDOR46 = 0x83
+ AF_VENDOR47 = 0x85
+ B0 = 0x0
+ B110 = 0x6e
+ B115200 = 0x1c200
+ B1200 = 0x4b0
+ B134 = 0x86
+ B14400 = 0x3840
+ B150 = 0x96
+ B1800 = 0x708
+ B19200 = 0x4b00
+ B200 = 0xc8
+ B230400 = 0x38400
+ B2400 = 0x960
+ B28800 = 0x7080
+ B300 = 0x12c
+ B38400 = 0x9600
+ B460800 = 0x70800
+ B4800 = 0x12c0
+ B50 = 0x32
+ B57600 = 0xe100
+ B600 = 0x258
+ B7200 = 0x1c20
+ B75 = 0x4b
+ B76800 = 0x12c00
+ B921600 = 0xe1000
+ B9600 = 0x2580
+ BIOCFEEDBACK = 0x8004427c
+ BIOCFLUSH = 0x20004268
+ BIOCGBLEN = 0x40044266
+ BIOCGDIRECTION = 0x40044276
+ BIOCGDLT = 0x4004426a
+ BIOCGDLTLIST = 0xc0084279
+ BIOCGETBUFMODE = 0x4004427d
+ BIOCGETIF = 0x4020426b
+ BIOCGETZMAX = 0x4004427f
+ BIOCGHDRCMPLT = 0x40044274
+ BIOCGRSIG = 0x40044272
+ BIOCGRTIMEOUT = 0x400c426e
+ BIOCGSEESENT = 0x40044276
+ BIOCGSTATS = 0x4008426f
+ BIOCGTSTAMP = 0x40044283
+ BIOCIMMEDIATE = 0x80044270
+ BIOCLOCK = 0x2000427a
+ BIOCPROMISC = 0x20004269
+ BIOCROTZBUF = 0x400c4280
+ BIOCSBLEN = 0xc0044266
+ BIOCSDIRECTION = 0x80044277
+ BIOCSDLT = 0x80044278
+ BIOCSETBUFMODE = 0x8004427e
+ BIOCSETF = 0x80084267
+ BIOCSETFNR = 0x80084282
+ BIOCSETIF = 0x8020426c
+ BIOCSETWF = 0x8008427b
+ BIOCSETZBUF = 0x800c4281
+ BIOCSHDRCMPLT = 0x80044275
+ BIOCSRSIG = 0x80044273
+ BIOCSRTIMEOUT = 0x800c426d
+ BIOCSSEESENT = 0x80044277
+ BIOCSTSTAMP = 0x80044284
+ BIOCVERSION = 0x40044271
+ BPF_A = 0x10
+ BPF_ABS = 0x20
+ BPF_ADD = 0x0
+ BPF_ALIGNMENT = 0x4
+ BPF_ALU = 0x4
+ BPF_AND = 0x50
+ BPF_B = 0x10
+ BPF_BUFMODE_BUFFER = 0x1
+ BPF_BUFMODE_ZBUF = 0x2
+ BPF_DIV = 0x30
+ BPF_H = 0x8
+ BPF_IMM = 0x0
+ BPF_IND = 0x40
+ BPF_JA = 0x0
+ BPF_JEQ = 0x10
+ BPF_JGE = 0x30
+ BPF_JGT = 0x20
+ BPF_JMP = 0x5
+ BPF_JSET = 0x40
+ BPF_K = 0x0
+ BPF_LD = 0x0
+ BPF_LDX = 0x1
+ BPF_LEN = 0x80
+ BPF_LSH = 0x60
+ BPF_MAJOR_VERSION = 0x1
+ BPF_MAXBUFSIZE = 0x80000
+ BPF_MAXINSNS = 0x200
+ BPF_MEM = 0x60
+ BPF_MEMWORDS = 0x10
+ BPF_MINBUFSIZE = 0x20
+ BPF_MINOR_VERSION = 0x1
+ BPF_MISC = 0x7
+ BPF_MSH = 0xa0
+ BPF_MUL = 0x20
+ BPF_NEG = 0x80
+ BPF_OR = 0x40
+ BPF_RELEASE = 0x30bb6
+ BPF_RET = 0x6
+ BPF_RSH = 0x70
+ BPF_ST = 0x2
+ BPF_STX = 0x3
+ BPF_SUB = 0x10
+ BPF_TAX = 0x0
+ BPF_TXA = 0x80
+ BPF_T_BINTIME = 0x2
+ BPF_T_BINTIME_FAST = 0x102
+ BPF_T_BINTIME_MONOTONIC = 0x202
+ BPF_T_BINTIME_MONOTONIC_FAST = 0x302
+ BPF_T_FAST = 0x100
+ BPF_T_FLAG_MASK = 0x300
+ BPF_T_FORMAT_MASK = 0x3
+ BPF_T_MICROTIME = 0x0
+ BPF_T_MICROTIME_FAST = 0x100
+ BPF_T_MICROTIME_MONOTONIC = 0x200
+ BPF_T_MICROTIME_MONOTONIC_FAST = 0x300
+ BPF_T_MONOTONIC = 0x200
+ BPF_T_MONOTONIC_FAST = 0x300
+ BPF_T_NANOTIME = 0x1
+ BPF_T_NANOTIME_FAST = 0x101
+ BPF_T_NANOTIME_MONOTONIC = 0x201
+ BPF_T_NANOTIME_MONOTONIC_FAST = 0x301
+ BPF_T_NONE = 0x3
+ BPF_T_NORMAL = 0x0
+ BPF_W = 0x0
+ BPF_X = 0x8
+ BRKINT = 0x2
+ CFLUSH = 0xf
+ CLOCAL = 0x8000
+ CREAD = 0x800
+ CS5 = 0x0
+ CS6 = 0x100
+ CS7 = 0x200
+ CS8 = 0x300
+ CSIZE = 0x300
+ CSTART = 0x11
+ CSTATUS = 0x14
+ CSTOP = 0x13
+ CSTOPB = 0x400
+ CSUSP = 0x1a
+ CTL_MAXNAME = 0x18
+ CTL_NET = 0x4
+ DLT_A429 = 0xb8
+ DLT_A653_ICM = 0xb9
+ DLT_AIRONET_HEADER = 0x78
+ DLT_AOS = 0xde
+ DLT_APPLE_IP_OVER_IEEE1394 = 0x8a
+ DLT_ARCNET = 0x7
+ DLT_ARCNET_LINUX = 0x81
+ DLT_ATM_CLIP = 0x13
+ DLT_ATM_RFC1483 = 0xb
+ DLT_AURORA = 0x7e
+ DLT_AX25 = 0x3
+ DLT_AX25_KISS = 0xca
+ DLT_BACNET_MS_TP = 0xa5
+ DLT_BLUETOOTH_HCI_H4 = 0xbb
+ DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 0xc9
+ DLT_CAN20B = 0xbe
+ DLT_CAN_SOCKETCAN = 0xe3
+ DLT_CHAOS = 0x5
+ DLT_CHDLC = 0x68
+ DLT_CISCO_IOS = 0x76
+ DLT_C_HDLC = 0x68
+ DLT_C_HDLC_WITH_DIR = 0xcd
+ DLT_DBUS = 0xe7
+ DLT_DECT = 0xdd
+ DLT_DOCSIS = 0x8f
+ DLT_DVB_CI = 0xeb
+ DLT_ECONET = 0x73
+ DLT_EN10MB = 0x1
+ DLT_EN3MB = 0x2
+ DLT_ENC = 0x6d
+ DLT_ERF = 0xc5
+ DLT_ERF_ETH = 0xaf
+ DLT_ERF_POS = 0xb0
+ DLT_FC_2 = 0xe0
+ DLT_FC_2_WITH_FRAME_DELIMS = 0xe1
+ DLT_FDDI = 0xa
+ DLT_FLEXRAY = 0xd2
+ DLT_FRELAY = 0x6b
+ DLT_FRELAY_WITH_DIR = 0xce
+ DLT_GCOM_SERIAL = 0xad
+ DLT_GCOM_T1E1 = 0xac
+ DLT_GPF_F = 0xab
+ DLT_GPF_T = 0xaa
+ DLT_GPRS_LLC = 0xa9
+ DLT_GSMTAP_ABIS = 0xda
+ DLT_GSMTAP_UM = 0xd9
+ DLT_HHDLC = 0x79
+ DLT_IBM_SN = 0x92
+ DLT_IBM_SP = 0x91
+ DLT_IEEE802 = 0x6
+ DLT_IEEE802_11 = 0x69
+ DLT_IEEE802_11_RADIO = 0x7f
+ DLT_IEEE802_11_RADIO_AVS = 0xa3
+ DLT_IEEE802_15_4 = 0xc3
+ DLT_IEEE802_15_4_LINUX = 0xbf
+ DLT_IEEE802_15_4_NOFCS = 0xe6
+ DLT_IEEE802_15_4_NONASK_PHY = 0xd7
+ DLT_IEEE802_16_MAC_CPS = 0xbc
+ DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1
+ DLT_IPFILTER = 0x74
+ DLT_IPMB = 0xc7
+ DLT_IPMB_LINUX = 0xd1
+ DLT_IPNET = 0xe2
+ DLT_IPOIB = 0xf2
+ DLT_IPV4 = 0xe4
+ DLT_IPV6 = 0xe5
+ DLT_IP_OVER_FC = 0x7a
+ DLT_JUNIPER_ATM1 = 0x89
+ DLT_JUNIPER_ATM2 = 0x87
+ DLT_JUNIPER_ATM_CEMIC = 0xee
+ DLT_JUNIPER_CHDLC = 0xb5
+ DLT_JUNIPER_ES = 0x84
+ DLT_JUNIPER_ETHER = 0xb2
+ DLT_JUNIPER_FIBRECHANNEL = 0xea
+ DLT_JUNIPER_FRELAY = 0xb4
+ DLT_JUNIPER_GGSN = 0x85
+ DLT_JUNIPER_ISM = 0xc2
+ DLT_JUNIPER_MFR = 0x86
+ DLT_JUNIPER_MLFR = 0x83
+ DLT_JUNIPER_MLPPP = 0x82
+ DLT_JUNIPER_MONITOR = 0xa4
+ DLT_JUNIPER_PIC_PEER = 0xae
+ DLT_JUNIPER_PPP = 0xb3
+ DLT_JUNIPER_PPPOE = 0xa7
+ DLT_JUNIPER_PPPOE_ATM = 0xa8
+ DLT_JUNIPER_SERVICES = 0x88
+ DLT_JUNIPER_SRX_E2E = 0xe9
+ DLT_JUNIPER_ST = 0xc8
+ DLT_JUNIPER_VP = 0xb7
+ DLT_JUNIPER_VS = 0xe8
+ DLT_LAPB_WITH_DIR = 0xcf
+ DLT_LAPD = 0xcb
+ DLT_LIN = 0xd4
+ DLT_LINUX_EVDEV = 0xd8
+ DLT_LINUX_IRDA = 0x90
+ DLT_LINUX_LAPD = 0xb1
+ DLT_LINUX_PPP_WITHDIRECTION = 0xa6
+ DLT_LINUX_SLL = 0x71
+ DLT_LOOP = 0x6c
+ DLT_LTALK = 0x72
+ DLT_MATCHING_MAX = 0xf2
+ DLT_MATCHING_MIN = 0x68
+ DLT_MFR = 0xb6
+ DLT_MOST = 0xd3
+ DLT_MPLS = 0xdb
+ DLT_MTP2 = 0x8c
+ DLT_MTP2_WITH_PHDR = 0x8b
+ DLT_MTP3 = 0x8d
+ DLT_MUX27010 = 0xec
+ DLT_NETANALYZER = 0xf0
+ DLT_NETANALYZER_TRANSPARENT = 0xf1
+ DLT_NFLOG = 0xef
+ DLT_NULL = 0x0
+ DLT_PCI_EXP = 0x7d
+ DLT_PFLOG = 0x75
+ DLT_PFSYNC = 0x79
+ DLT_PPI = 0xc0
+ DLT_PPP = 0x9
+ DLT_PPP_BSDOS = 0x10
+ DLT_PPP_ETHER = 0x33
+ DLT_PPP_PPPD = 0xa6
+ DLT_PPP_SERIAL = 0x32
+ DLT_PPP_WITH_DIR = 0xcc
+ DLT_PPP_WITH_DIRECTION = 0xa6
+ DLT_PRISM_HEADER = 0x77
+ DLT_PRONET = 0x4
+ DLT_RAIF1 = 0xc6
+ DLT_RAW = 0xc
+ DLT_RIO = 0x7c
+ DLT_SCCP = 0x8e
+ DLT_SITA = 0xc4
+ DLT_SLIP = 0x8
+ DLT_SLIP_BSDOS = 0xf
+ DLT_STANAG_5066_D_PDU = 0xed
+ DLT_SUNATM = 0x7b
+ DLT_SYMANTEC_FIREWALL = 0x63
+ DLT_TZSP = 0x80
+ DLT_USB = 0xba
+ DLT_USB_LINUX = 0xbd
+ DLT_USB_LINUX_MMAPPED = 0xdc
+ DLT_USER0 = 0x93
+ DLT_USER1 = 0x94
+ DLT_USER10 = 0x9d
+ DLT_USER11 = 0x9e
+ DLT_USER12 = 0x9f
+ DLT_USER13 = 0xa0
+ DLT_USER14 = 0xa1
+ DLT_USER15 = 0xa2
+ DLT_USER2 = 0x95
+ DLT_USER3 = 0x96
+ DLT_USER4 = 0x97
+ DLT_USER5 = 0x98
+ DLT_USER6 = 0x99
+ DLT_USER7 = 0x9a
+ DLT_USER8 = 0x9b
+ DLT_USER9 = 0x9c
+ DLT_WIHART = 0xdf
+ DLT_X2E_SERIAL = 0xd5
+ DLT_X2E_XORAYA = 0xd6
+ DT_BLK = 0x6
+ DT_CHR = 0x2
+ DT_DIR = 0x4
+ DT_FIFO = 0x1
+ DT_LNK = 0xa
+ DT_REG = 0x8
+ DT_SOCK = 0xc
+ DT_UNKNOWN = 0x0
+ DT_WHT = 0xe
+ ECHO = 0x8
+ ECHOCTL = 0x40
+ ECHOE = 0x2
+ ECHOK = 0x4
+ ECHOKE = 0x1
+ ECHONL = 0x10
+ ECHOPRT = 0x20
+ EVFILT_AIO = -0x3
+ EVFILT_FS = -0x9
+ EVFILT_LIO = -0xa
+ EVFILT_PROC = -0x5
+ EVFILT_READ = -0x1
+ EVFILT_SIGNAL = -0x6
+ EVFILT_SYSCOUNT = 0xb
+ EVFILT_TIMER = -0x7
+ EVFILT_USER = -0xb
+ EVFILT_VNODE = -0x4
+ EVFILT_WRITE = -0x2
+ EV_ADD = 0x1
+ EV_CLEAR = 0x20
+ EV_DELETE = 0x2
+ EV_DISABLE = 0x8
+ EV_DISPATCH = 0x80
+ EV_ENABLE = 0x4
+ EV_EOF = 0x8000
+ EV_ERROR = 0x4000
+ EV_FLAG1 = 0x2000
+ EV_ONESHOT = 0x10
+ EV_RECEIPT = 0x40
+ EV_SYSFLAGS = 0xf000
+ EXTA = 0x4b00
+ EXTB = 0x9600
+ EXTPROC = 0x800
+ FD_CLOEXEC = 0x1
+ FD_SETSIZE = 0x400
+ FLUSHO = 0x800000
+ F_CANCEL = 0x5
+ F_DUP2FD = 0xa
+ F_DUP2FD_CLOEXEC = 0x12
+ F_DUPFD = 0x0
+ F_DUPFD_CLOEXEC = 0x11
+ F_GETFD = 0x1
+ F_GETFL = 0x3
+ F_GETLK = 0xb
+ F_GETOWN = 0x5
+ F_OGETLK = 0x7
+ F_OK = 0x0
+ F_OSETLK = 0x8
+ F_OSETLKW = 0x9
+ F_RDAHEAD = 0x10
+ F_RDLCK = 0x1
+ F_READAHEAD = 0xf
+ F_SETFD = 0x2
+ F_SETFL = 0x4
+ F_SETLK = 0xc
+ F_SETLKW = 0xd
+ F_SETLK_REMOTE = 0xe
+ F_SETOWN = 0x6
+ F_UNLCK = 0x2
+ F_UNLCKSYS = 0x4
+ F_WRLCK = 0x3
+ HUPCL = 0x4000
+ ICANON = 0x100
+ ICRNL = 0x100
+ IEXTEN = 0x400
+ IFAN_ARRIVAL = 0x0
+ IFAN_DEPARTURE = 0x1
+ IFF_ALLMULTI = 0x200
+ IFF_ALTPHYS = 0x4000
+ IFF_BROADCAST = 0x2
+ IFF_CANTCHANGE = 0x218f72
+ IFF_CANTCONFIG = 0x10000
+ IFF_DEBUG = 0x4
+ IFF_DRV_OACTIVE = 0x400
+ IFF_DRV_RUNNING = 0x40
+ IFF_DYING = 0x200000
+ IFF_LINK0 = 0x1000
+ IFF_LINK1 = 0x2000
+ IFF_LINK2 = 0x4000
+ IFF_LOOPBACK = 0x8
+ IFF_MONITOR = 0x40000
+ IFF_MULTICAST = 0x8000
+ IFF_NOARP = 0x80
+ IFF_OACTIVE = 0x400
+ IFF_POINTOPOINT = 0x10
+ IFF_PPROMISC = 0x20000
+ IFF_PROMISC = 0x100
+ IFF_RENAMING = 0x400000
+ IFF_RUNNING = 0x40
+ IFF_SIMPLEX = 0x800
+ IFF_SMART = 0x20
+ IFF_STATICARP = 0x80000
+ IFF_UP = 0x1
+ IFNAMSIZ = 0x10
+ IFT_1822 = 0x2
+ IFT_A12MPPSWITCH = 0x82
+ IFT_AAL2 = 0xbb
+ IFT_AAL5 = 0x31
+ IFT_ADSL = 0x5e
+ IFT_AFLANE8023 = 0x3b
+ IFT_AFLANE8025 = 0x3c
+ IFT_ARAP = 0x58
+ IFT_ARCNET = 0x23
+ IFT_ARCNETPLUS = 0x24
+ IFT_ASYNC = 0x54
+ IFT_ATM = 0x25
+ IFT_ATMDXI = 0x69
+ IFT_ATMFUNI = 0x6a
+ IFT_ATMIMA = 0x6b
+ IFT_ATMLOGICAL = 0x50
+ IFT_ATMRADIO = 0xbd
+ IFT_ATMSUBINTERFACE = 0x86
+ IFT_ATMVCIENDPT = 0xc2
+ IFT_ATMVIRTUAL = 0x95
+ IFT_BGPPOLICYACCOUNTING = 0xa2
+ IFT_BRIDGE = 0xd1
+ IFT_BSC = 0x53
+ IFT_CCTEMUL = 0x3d
+ IFT_CEPT = 0x13
+ IFT_CES = 0x85
+ IFT_CHANNEL = 0x46
+ IFT_CNR = 0x55
+ IFT_COFFEE = 0x84
+ IFT_COMPOSITELINK = 0x9b
+ IFT_DCN = 0x8d
+ IFT_DIGITALPOWERLINE = 0x8a
+ IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba
+ IFT_DLSW = 0x4a
+ IFT_DOCSCABLEDOWNSTREAM = 0x80
+ IFT_DOCSCABLEMACLAYER = 0x7f
+ IFT_DOCSCABLEUPSTREAM = 0x81
+ IFT_DS0 = 0x51
+ IFT_DS0BUNDLE = 0x52
+ IFT_DS1FDL = 0xaa
+ IFT_DS3 = 0x1e
+ IFT_DTM = 0x8c
+ IFT_DVBASILN = 0xac
+ IFT_DVBASIOUT = 0xad
+ IFT_DVBRCCDOWNSTREAM = 0x93
+ IFT_DVBRCCMACLAYER = 0x92
+ IFT_DVBRCCUPSTREAM = 0x94
+ IFT_ENC = 0xf4
+ IFT_EON = 0x19
+ IFT_EPLRS = 0x57
+ IFT_ESCON = 0x49
+ IFT_ETHER = 0x6
+ IFT_FAITH = 0xf2
+ IFT_FAST = 0x7d
+ IFT_FASTETHER = 0x3e
+ IFT_FASTETHERFX = 0x45
+ IFT_FDDI = 0xf
+ IFT_FIBRECHANNEL = 0x38
+ IFT_FRAMERELAYINTERCONNECT = 0x3a
+ IFT_FRAMERELAYMPI = 0x5c
+ IFT_FRDLCIENDPT = 0xc1
+ IFT_FRELAY = 0x20
+ IFT_FRELAYDCE = 0x2c
+ IFT_FRF16MFRBUNDLE = 0xa3
+ IFT_FRFORWARD = 0x9e
+ IFT_G703AT2MB = 0x43
+ IFT_G703AT64K = 0x42
+ IFT_GIF = 0xf0
+ IFT_GIGABITETHERNET = 0x75
+ IFT_GR303IDT = 0xb2
+ IFT_GR303RDT = 0xb1
+ IFT_H323GATEKEEPER = 0xa4
+ IFT_H323PROXY = 0xa5
+ IFT_HDH1822 = 0x3
+ IFT_HDLC = 0x76
+ IFT_HDSL2 = 0xa8
+ IFT_HIPERLAN2 = 0xb7
+ IFT_HIPPI = 0x2f
+ IFT_HIPPIINTERFACE = 0x39
+ IFT_HOSTPAD = 0x5a
+ IFT_HSSI = 0x2e
+ IFT_HY = 0xe
+ IFT_IBM370PARCHAN = 0x48
+ IFT_IDSL = 0x9a
+ IFT_IEEE1394 = 0x90
+ IFT_IEEE80211 = 0x47
+ IFT_IEEE80212 = 0x37
+ IFT_IEEE8023ADLAG = 0xa1
+ IFT_IFGSN = 0x91
+ IFT_IMT = 0xbe
+ IFT_INFINIBAND = 0xc7
+ IFT_INTERLEAVE = 0x7c
+ IFT_IP = 0x7e
+ IFT_IPFORWARD = 0x8e
+ IFT_IPOVERATM = 0x72
+ IFT_IPOVERCDLC = 0x6d
+ IFT_IPOVERCLAW = 0x6e
+ IFT_IPSWITCH = 0x4e
+ IFT_IPXIP = 0xf9
+ IFT_ISDN = 0x3f
+ IFT_ISDNBASIC = 0x14
+ IFT_ISDNPRIMARY = 0x15
+ IFT_ISDNS = 0x4b
+ IFT_ISDNU = 0x4c
+ IFT_ISO88022LLC = 0x29
+ IFT_ISO88023 = 0x7
+ IFT_ISO88024 = 0x8
+ IFT_ISO88025 = 0x9
+ IFT_ISO88025CRFPINT = 0x62
+ IFT_ISO88025DTR = 0x56
+ IFT_ISO88025FIBER = 0x73
+ IFT_ISO88026 = 0xa
+ IFT_ISUP = 0xb3
+ IFT_L2VLAN = 0x87
+ IFT_L3IPVLAN = 0x88
+ IFT_L3IPXVLAN = 0x89
+ IFT_LAPB = 0x10
+ IFT_LAPD = 0x4d
+ IFT_LAPF = 0x77
+ IFT_LOCALTALK = 0x2a
+ IFT_LOOP = 0x18
+ IFT_MEDIAMAILOVERIP = 0x8b
+ IFT_MFSIGLINK = 0xa7
+ IFT_MIOX25 = 0x26
+ IFT_MODEM = 0x30
+ IFT_MPC = 0x71
+ IFT_MPLS = 0xa6
+ IFT_MPLSTUNNEL = 0x96
+ IFT_MSDSL = 0x8f
+ IFT_MVL = 0xbf
+ IFT_MYRINET = 0x63
+ IFT_NFAS = 0xaf
+ IFT_NSIP = 0x1b
+ IFT_OPTICALCHANNEL = 0xc3
+ IFT_OPTICALTRANSPORT = 0xc4
+ IFT_OTHER = 0x1
+ IFT_P10 = 0xc
+ IFT_P80 = 0xd
+ IFT_PARA = 0x22
+ IFT_PFLOG = 0xf6
+ IFT_PFSYNC = 0xf7
+ IFT_PLC = 0xae
+ IFT_POS = 0xab
+ IFT_PPP = 0x17
+ IFT_PPPMULTILINKBUNDLE = 0x6c
+ IFT_PROPBWAP2MP = 0xb8
+ IFT_PROPCNLS = 0x59
+ IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5
+ IFT_PROPDOCSWIRELESSMACLAYER = 0xb4
+ IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6
+ IFT_PROPMUX = 0x36
+ IFT_PROPVIRTUAL = 0x35
+ IFT_PROPWIRELESSP2P = 0x9d
+ IFT_PTPSERIAL = 0x16
+ IFT_PVC = 0xf1
+ IFT_QLLC = 0x44
+ IFT_RADIOMAC = 0xbc
+ IFT_RADSL = 0x5f
+ IFT_REACHDSL = 0xc0
+ IFT_RFC1483 = 0x9f
+ IFT_RS232 = 0x21
+ IFT_RSRB = 0x4f
+ IFT_SDLC = 0x11
+ IFT_SDSL = 0x60
+ IFT_SHDSL = 0xa9
+ IFT_SIP = 0x1f
+ IFT_SLIP = 0x1c
+ IFT_SMDSDXI = 0x2b
+ IFT_SMDSICIP = 0x34
+ IFT_SONET = 0x27
+ IFT_SONETOVERHEADCHANNEL = 0xb9
+ IFT_SONETPATH = 0x32
+ IFT_SONETVT = 0x33
+ IFT_SRP = 0x97
+ IFT_SS7SIGLINK = 0x9c
+ IFT_STACKTOSTACK = 0x6f
+ IFT_STARLAN = 0xb
+ IFT_STF = 0xd7
+ IFT_T1 = 0x12
+ IFT_TDLC = 0x74
+ IFT_TERMPAD = 0x5b
+ IFT_TR008 = 0xb0
+ IFT_TRANSPHDLC = 0x7b
+ IFT_TUNNEL = 0x83
+ IFT_ULTRA = 0x1d
+ IFT_USB = 0xa0
+ IFT_V11 = 0x40
+ IFT_V35 = 0x2d
+ IFT_V36 = 0x41
+ IFT_V37 = 0x78
+ IFT_VDSL = 0x61
+ IFT_VIRTUALIPADDRESS = 0x70
+ IFT_VOICEEM = 0x64
+ IFT_VOICEENCAP = 0x67
+ IFT_VOICEFXO = 0x65
+ IFT_VOICEFXS = 0x66
+ IFT_VOICEOVERATM = 0x98
+ IFT_VOICEOVERFRAMERELAY = 0x99
+ IFT_VOICEOVERIP = 0x68
+ IFT_X213 = 0x5d
+ IFT_X25 = 0x5
+ IFT_X25DDN = 0x4
+ IFT_X25HUNTGROUP = 0x7a
+ IFT_X25MLP = 0x79
+ IFT_X25PLE = 0x28
+ IFT_XETHER = 0x1a
+ IGNBRK = 0x1
+ IGNCR = 0x80
+ IGNPAR = 0x4
+ IMAXBEL = 0x2000
+ INLCR = 0x40
+ INPCK = 0x10
+ IN_CLASSA_HOST = 0xffffff
+ IN_CLASSA_MAX = 0x80
+ IN_CLASSA_NET = 0xff000000
+ IN_CLASSA_NSHIFT = 0x18
+ IN_CLASSB_HOST = 0xffff
+ IN_CLASSB_MAX = 0x10000
+ IN_CLASSB_NET = 0xffff0000
+ IN_CLASSB_NSHIFT = 0x10
+ IN_CLASSC_HOST = 0xff
+ IN_CLASSC_NET = 0xffffff00
+ IN_CLASSC_NSHIFT = 0x8
+ IN_CLASSD_HOST = 0xfffffff
+ IN_CLASSD_NET = 0xf0000000
+ IN_CLASSD_NSHIFT = 0x1c
+ IN_LOOPBACKNET = 0x7f
+ IN_RFC3021_MASK = 0xfffffffe
+ IPPROTO_3PC = 0x22
+ IPPROTO_ADFS = 0x44
+ IPPROTO_AH = 0x33
+ IPPROTO_AHIP = 0x3d
+ IPPROTO_APES = 0x63
+ IPPROTO_ARGUS = 0xd
+ IPPROTO_AX25 = 0x5d
+ IPPROTO_BHA = 0x31
+ IPPROTO_BLT = 0x1e
+ IPPROTO_BRSATMON = 0x4c
+ IPPROTO_CARP = 0x70
+ IPPROTO_CFTP = 0x3e
+ IPPROTO_CHAOS = 0x10
+ IPPROTO_CMTP = 0x26
+ IPPROTO_CPHB = 0x49
+ IPPROTO_CPNX = 0x48
+ IPPROTO_DDP = 0x25
+ IPPROTO_DGP = 0x56
+ IPPROTO_DIVERT = 0x102
+ IPPROTO_DONE = 0x101
+ IPPROTO_DSTOPTS = 0x3c
+ IPPROTO_EGP = 0x8
+ IPPROTO_EMCON = 0xe
+ IPPROTO_ENCAP = 0x62
+ IPPROTO_EON = 0x50
+ IPPROTO_ESP = 0x32
+ IPPROTO_ETHERIP = 0x61
+ IPPROTO_FRAGMENT = 0x2c
+ IPPROTO_GGP = 0x3
+ IPPROTO_GMTP = 0x64
+ IPPROTO_GRE = 0x2f
+ IPPROTO_HELLO = 0x3f
+ IPPROTO_HMP = 0x14
+ IPPROTO_HOPOPTS = 0x0
+ IPPROTO_ICMP = 0x1
+ IPPROTO_ICMPV6 = 0x3a
+ IPPROTO_IDP = 0x16
+ IPPROTO_IDPR = 0x23
+ IPPROTO_IDRP = 0x2d
+ IPPROTO_IGMP = 0x2
+ IPPROTO_IGP = 0x55
+ IPPROTO_IGRP = 0x58
+ IPPROTO_IL = 0x28
+ IPPROTO_INLSP = 0x34
+ IPPROTO_INP = 0x20
+ IPPROTO_IP = 0x0
+ IPPROTO_IPCOMP = 0x6c
+ IPPROTO_IPCV = 0x47
+ IPPROTO_IPEIP = 0x5e
+ IPPROTO_IPIP = 0x4
+ IPPROTO_IPPC = 0x43
+ IPPROTO_IPV4 = 0x4
+ IPPROTO_IPV6 = 0x29
+ IPPROTO_IRTP = 0x1c
+ IPPROTO_KRYPTOLAN = 0x41
+ IPPROTO_LARP = 0x5b
+ IPPROTO_LEAF1 = 0x19
+ IPPROTO_LEAF2 = 0x1a
+ IPPROTO_MAX = 0x100
+ IPPROTO_MAXID = 0x34
+ IPPROTO_MEAS = 0x13
+ IPPROTO_MH = 0x87
+ IPPROTO_MHRP = 0x30
+ IPPROTO_MICP = 0x5f
+ IPPROTO_MOBILE = 0x37
+ IPPROTO_MPLS = 0x89
+ IPPROTO_MTP = 0x5c
+ IPPROTO_MUX = 0x12
+ IPPROTO_ND = 0x4d
+ IPPROTO_NHRP = 0x36
+ IPPROTO_NONE = 0x3b
+ IPPROTO_NSP = 0x1f
+ IPPROTO_NVPII = 0xb
+ IPPROTO_OLD_DIVERT = 0xfe
+ IPPROTO_OSPFIGP = 0x59
+ IPPROTO_PFSYNC = 0xf0
+ IPPROTO_PGM = 0x71
+ IPPROTO_PIGP = 0x9
+ IPPROTO_PIM = 0x67
+ IPPROTO_PRM = 0x15
+ IPPROTO_PUP = 0xc
+ IPPROTO_PVP = 0x4b
+ IPPROTO_RAW = 0xff
+ IPPROTO_RCCMON = 0xa
+ IPPROTO_RDP = 0x1b
+ IPPROTO_ROUTING = 0x2b
+ IPPROTO_RSVP = 0x2e
+ IPPROTO_RVD = 0x42
+ IPPROTO_SATEXPAK = 0x40
+ IPPROTO_SATMON = 0x45
+ IPPROTO_SCCSP = 0x60
+ IPPROTO_SCTP = 0x84
+ IPPROTO_SDRP = 0x2a
+ IPPROTO_SEND = 0x103
+ IPPROTO_SEP = 0x21
+ IPPROTO_SKIP = 0x39
+ IPPROTO_SPACER = 0x7fff
+ IPPROTO_SRPC = 0x5a
+ IPPROTO_ST = 0x7
+ IPPROTO_SVMTP = 0x52
+ IPPROTO_SWIPE = 0x35
+ IPPROTO_TCF = 0x57
+ IPPROTO_TCP = 0x6
+ IPPROTO_TLSP = 0x38
+ IPPROTO_TP = 0x1d
+ IPPROTO_TPXX = 0x27
+ IPPROTO_TRUNK1 = 0x17
+ IPPROTO_TRUNK2 = 0x18
+ IPPROTO_TTP = 0x54
+ IPPROTO_UDP = 0x11
+ IPPROTO_VINES = 0x53
+ IPPROTO_VISA = 0x46
+ IPPROTO_VMTP = 0x51
+ IPPROTO_WBEXPAK = 0x4f
+ IPPROTO_WBMON = 0x4e
+ IPPROTO_WSN = 0x4a
+ IPPROTO_XNET = 0xf
+ IPPROTO_XTP = 0x24
+ IPV6_AUTOFLOWLABEL = 0x3b
+ IPV6_BINDANY = 0x40
+ IPV6_BINDV6ONLY = 0x1b
+ IPV6_CHECKSUM = 0x1a
+ IPV6_DEFAULT_MULTICAST_HOPS = 0x1
+ IPV6_DEFAULT_MULTICAST_LOOP = 0x1
+ IPV6_DEFHLIM = 0x40
+ IPV6_DONTFRAG = 0x3e
+ IPV6_DSTOPTS = 0x32
+ IPV6_FAITH = 0x1d
+ IPV6_FLOWINFO_MASK = 0xffffff0f
+ IPV6_FLOWLABEL_MASK = 0xffff0f00
+ IPV6_FRAGTTL = 0x78
+ IPV6_FW_ADD = 0x1e
+ IPV6_FW_DEL = 0x1f
+ IPV6_FW_FLUSH = 0x20
+ IPV6_FW_GET = 0x22
+ IPV6_FW_ZERO = 0x21
+ IPV6_HLIMDEC = 0x1
+ IPV6_HOPLIMIT = 0x2f
+ IPV6_HOPOPTS = 0x31
+ IPV6_IPSEC_POLICY = 0x1c
+ IPV6_JOIN_GROUP = 0xc
+ IPV6_LEAVE_GROUP = 0xd
+ IPV6_MAXHLIM = 0xff
+ IPV6_MAXOPTHDR = 0x800
+ IPV6_MAXPACKET = 0xffff
+ IPV6_MAX_GROUP_SRC_FILTER = 0x200
+ IPV6_MAX_MEMBERSHIPS = 0xfff
+ IPV6_MAX_SOCK_SRC_FILTER = 0x80
+ IPV6_MIN_MEMBERSHIPS = 0x1f
+ IPV6_MMTU = 0x500
+ IPV6_MSFILTER = 0x4a
+ IPV6_MULTICAST_HOPS = 0xa
+ IPV6_MULTICAST_IF = 0x9
+ IPV6_MULTICAST_LOOP = 0xb
+ IPV6_NEXTHOP = 0x30
+ IPV6_PATHMTU = 0x2c
+ IPV6_PKTINFO = 0x2e
+ IPV6_PORTRANGE = 0xe
+ IPV6_PORTRANGE_DEFAULT = 0x0
+ IPV6_PORTRANGE_HIGH = 0x1
+ IPV6_PORTRANGE_LOW = 0x2
+ IPV6_PREFER_TEMPADDR = 0x3f
+ IPV6_RECVDSTOPTS = 0x28
+ IPV6_RECVHOPLIMIT = 0x25
+ IPV6_RECVHOPOPTS = 0x27
+ IPV6_RECVPATHMTU = 0x2b
+ IPV6_RECVPKTINFO = 0x24
+ IPV6_RECVRTHDR = 0x26
+ IPV6_RECVTCLASS = 0x39
+ IPV6_RTHDR = 0x33
+ IPV6_RTHDRDSTOPTS = 0x23
+ IPV6_RTHDR_LOOSE = 0x0
+ IPV6_RTHDR_STRICT = 0x1
+ IPV6_RTHDR_TYPE_0 = 0x0
+ IPV6_SOCKOPT_RESERVED1 = 0x3
+ IPV6_TCLASS = 0x3d
+ IPV6_UNICAST_HOPS = 0x4
+ IPV6_USE_MIN_MTU = 0x2a
+ IPV6_V6ONLY = 0x1b
+ IPV6_VERSION = 0x60
+ IPV6_VERSION_MASK = 0xf0
+ IP_ADD_MEMBERSHIP = 0xc
+ IP_ADD_SOURCE_MEMBERSHIP = 0x46
+ IP_BINDANY = 0x18
+ IP_BLOCK_SOURCE = 0x48
+ IP_DEFAULT_MULTICAST_LOOP = 0x1
+ IP_DEFAULT_MULTICAST_TTL = 0x1
+ IP_DF = 0x4000
+ IP_DONTFRAG = 0x43
+ IP_DROP_MEMBERSHIP = 0xd
+ IP_DROP_SOURCE_MEMBERSHIP = 0x47
+ IP_DUMMYNET3 = 0x31
+ IP_DUMMYNET_CONFIGURE = 0x3c
+ IP_DUMMYNET_DEL = 0x3d
+ IP_DUMMYNET_FLUSH = 0x3e
+ IP_DUMMYNET_GET = 0x40
+ IP_FAITH = 0x16
+ IP_FW3 = 0x30
+ IP_FW_ADD = 0x32
+ IP_FW_DEL = 0x33
+ IP_FW_FLUSH = 0x34
+ IP_FW_GET = 0x36
+ IP_FW_NAT_CFG = 0x38
+ IP_FW_NAT_DEL = 0x39
+ IP_FW_NAT_GET_CONFIG = 0x3a
+ IP_FW_NAT_GET_LOG = 0x3b
+ IP_FW_RESETLOG = 0x37
+ IP_FW_TABLE_ADD = 0x28
+ IP_FW_TABLE_DEL = 0x29
+ IP_FW_TABLE_FLUSH = 0x2a
+ IP_FW_TABLE_GETSIZE = 0x2b
+ IP_FW_TABLE_LIST = 0x2c
+ IP_FW_ZERO = 0x35
+ IP_HDRINCL = 0x2
+ IP_IPSEC_POLICY = 0x15
+ IP_MAXPACKET = 0xffff
+ IP_MAX_GROUP_SRC_FILTER = 0x200
+ IP_MAX_MEMBERSHIPS = 0xfff
+ IP_MAX_SOCK_MUTE_FILTER = 0x80
+ IP_MAX_SOCK_SRC_FILTER = 0x80
+ IP_MAX_SOURCE_FILTER = 0x400
+ IP_MF = 0x2000
+ IP_MINTTL = 0x42
+ IP_MIN_MEMBERSHIPS = 0x1f
+ IP_MSFILTER = 0x4a
+ IP_MSS = 0x240
+ IP_MULTICAST_IF = 0x9
+ IP_MULTICAST_LOOP = 0xb
+ IP_MULTICAST_TTL = 0xa
+ IP_MULTICAST_VIF = 0xe
+ IP_OFFMASK = 0x1fff
+ IP_ONESBCAST = 0x17
+ IP_OPTIONS = 0x1
+ IP_PORTRANGE = 0x13
+ IP_PORTRANGE_DEFAULT = 0x0
+ IP_PORTRANGE_HIGH = 0x1
+ IP_PORTRANGE_LOW = 0x2
+ IP_RECVDSTADDR = 0x7
+ IP_RECVIF = 0x14
+ IP_RECVOPTS = 0x5
+ IP_RECVRETOPTS = 0x6
+ IP_RECVTOS = 0x44
+ IP_RECVTTL = 0x41
+ IP_RETOPTS = 0x8
+ IP_RF = 0x8000
+ IP_RSVP_OFF = 0x10
+ IP_RSVP_ON = 0xf
+ IP_RSVP_VIF_OFF = 0x12
+ IP_RSVP_VIF_ON = 0x11
+ IP_SENDSRCADDR = 0x7
+ IP_TOS = 0x3
+ IP_TTL = 0x4
+ IP_UNBLOCK_SOURCE = 0x49
+ ISIG = 0x80
+ ISTRIP = 0x20
+ IXANY = 0x800
+ IXOFF = 0x400
+ IXON = 0x200
+ LOCK_EX = 0x2
+ LOCK_NB = 0x4
+ LOCK_SH = 0x1
+ LOCK_UN = 0x8
+ MSG_COMPAT = 0x8000
+ MSG_CTRUNC = 0x20
+ MSG_DONTROUTE = 0x4
+ MSG_DONTWAIT = 0x80
+ MSG_EOF = 0x100
+ MSG_EOR = 0x8
+ MSG_NBIO = 0x4000
+ MSG_NOSIGNAL = 0x20000
+ MSG_NOTIFICATION = 0x2000
+ MSG_OOB = 0x1
+ MSG_PEEK = 0x2
+ MSG_TRUNC = 0x10
+ MSG_WAITALL = 0x40
+ NET_RT_DUMP = 0x1
+ NET_RT_FLAGS = 0x2
+ NET_RT_IFLIST = 0x3
+ NET_RT_IFLISTL = 0x5
+ NET_RT_IFMALIST = 0x4
+ NET_RT_MAXID = 0x6
+ NOFLSH = 0x80000000
+ NOTE_ATTRIB = 0x8
+ NOTE_CHILD = 0x4
+ NOTE_DELETE = 0x1
+ NOTE_EXEC = 0x20000000
+ NOTE_EXIT = 0x80000000
+ NOTE_EXTEND = 0x4
+ NOTE_FFAND = 0x40000000
+ NOTE_FFCOPY = 0xc0000000
+ NOTE_FFCTRLMASK = 0xc0000000
+ NOTE_FFLAGSMASK = 0xffffff
+ NOTE_FFNOP = 0x0
+ NOTE_FFOR = 0x80000000
+ NOTE_FORK = 0x40000000
+ NOTE_LINK = 0x10
+ NOTE_LOWAT = 0x1
+ NOTE_PCTRLMASK = 0xf0000000
+ NOTE_PDATAMASK = 0xfffff
+ NOTE_RENAME = 0x20
+ NOTE_REVOKE = 0x40
+ NOTE_TRACK = 0x1
+ NOTE_TRACKERR = 0x2
+ NOTE_TRIGGER = 0x1000000
+ NOTE_WRITE = 0x2
+ OCRNL = 0x10
+ ONLCR = 0x2
+ ONLRET = 0x40
+ ONOCR = 0x20
+ ONOEOT = 0x8
+ OPOST = 0x1
+ O_ACCMODE = 0x3
+ O_APPEND = 0x8
+ O_ASYNC = 0x40
+ O_CREAT = 0x200
+ O_DIRECT = 0x10000
+ O_DIRECTORY = 0x20000
+ O_EXCL = 0x800
+ O_EXEC = 0x40000
+ O_EXLOCK = 0x20
+ O_FSYNC = 0x80
+ O_NDELAY = 0x4
+ O_NOCTTY = 0x8000
+ O_NOFOLLOW = 0x100
+ O_NONBLOCK = 0x4
+ O_RDONLY = 0x0
+ O_RDWR = 0x2
+ O_SHLOCK = 0x10
+ O_SYNC = 0x80
+ O_TRUNC = 0x400
+ O_TTY_INIT = 0x80000
+ O_WRONLY = 0x1
+ PARENB = 0x1000
+ PARMRK = 0x8
+ PARODD = 0x2000
+ PENDIN = 0x20000000
+ RLIMIT_AS = 0xa
+ RLIMIT_CORE = 0x4
+ RLIMIT_CPU = 0x0
+ RLIMIT_DATA = 0x2
+ RLIMIT_FSIZE = 0x1
+ RLIMIT_NOFILE = 0x8
+ RLIMIT_STACK = 0x3
+ RLIM_INFINITY = 0x7fffffffffffffff
+ RTAX_AUTHOR = 0x6
+ RTAX_BRD = 0x7
+ RTAX_DST = 0x0
+ RTAX_GATEWAY = 0x1
+ RTAX_GENMASK = 0x3
+ RTAX_IFA = 0x5
+ RTAX_IFP = 0x4
+ RTAX_MAX = 0x8
+ RTAX_NETMASK = 0x2
+ RTA_AUTHOR = 0x40
+ RTA_BRD = 0x80
+ RTA_DST = 0x1
+ RTA_GATEWAY = 0x2
+ RTA_GENMASK = 0x8
+ RTA_IFA = 0x20
+ RTA_IFP = 0x10
+ RTA_NETMASK = 0x4
+ RTF_BLACKHOLE = 0x1000
+ RTF_BROADCAST = 0x400000
+ RTF_DONE = 0x40
+ RTF_DYNAMIC = 0x10
+ RTF_FMASK = 0x1004d808
+ RTF_GATEWAY = 0x2
+ RTF_HOST = 0x4
+ RTF_LLDATA = 0x400
+ RTF_LLINFO = 0x400
+ RTF_LOCAL = 0x200000
+ RTF_MODIFIED = 0x20
+ RTF_MULTICAST = 0x800000
+ RTF_PINNED = 0x100000
+ RTF_PRCLONING = 0x10000
+ RTF_PROTO1 = 0x8000
+ RTF_PROTO2 = 0x4000
+ RTF_PROTO3 = 0x40000
+ RTF_REJECT = 0x8
+ RTF_RNH_LOCKED = 0x40000000
+ RTF_STATIC = 0x800
+ RTF_STICKY = 0x10000000
+ RTF_UP = 0x1
+ RTF_XRESOLVE = 0x200
+ RTM_ADD = 0x1
+ RTM_CHANGE = 0x3
+ RTM_DELADDR = 0xd
+ RTM_DELETE = 0x2
+ RTM_DELMADDR = 0x10
+ RTM_GET = 0x4
+ RTM_IEEE80211 = 0x12
+ RTM_IFANNOUNCE = 0x11
+ RTM_IFINFO = 0xe
+ RTM_LOCK = 0x8
+ RTM_LOSING = 0x5
+ RTM_MISS = 0x7
+ RTM_NEWADDR = 0xc
+ RTM_NEWMADDR = 0xf
+ RTM_OLDADD = 0x9
+ RTM_OLDDEL = 0xa
+ RTM_REDIRECT = 0x6
+ RTM_RESOLVE = 0xb
+ RTM_RTTUNIT = 0xf4240
+ RTM_VERSION = 0x5
+ RTV_EXPIRE = 0x4
+ RTV_HOPCOUNT = 0x2
+ RTV_MTU = 0x1
+ RTV_RPIPE = 0x8
+ RTV_RTT = 0x40
+ RTV_RTTVAR = 0x80
+ RTV_SPIPE = 0x10
+ RTV_SSTHRESH = 0x20
+ RTV_WEIGHT = 0x100
+ RT_CACHING_CONTEXT = 0x1
+ RT_DEFAULT_FIB = 0x0
+ RT_NORTREF = 0x2
+ RUSAGE_CHILDREN = -0x1
+ RUSAGE_SELF = 0x0
+ RUSAGE_THREAD = 0x1
+ SCM_BINTIME = 0x4
+ SCM_CREDS = 0x3
+ SCM_RIGHTS = 0x1
+ SCM_TIMESTAMP = 0x2
+ SHUT_RD = 0x0
+ SHUT_RDWR = 0x2
+ SHUT_WR = 0x1
+ SIOCADDMULTI = 0x80206931
+ SIOCADDRT = 0x8030720a
+ SIOCAIFADDR = 0x8044692b
+ SIOCAIFGROUP = 0x80246987
+ SIOCALIFADDR = 0x8118691b
+ SIOCATMARK = 0x40047307
+ SIOCDELMULTI = 0x80206932
+ SIOCDELRT = 0x8030720b
+ SIOCDIFADDR = 0x80206919
+ SIOCDIFGROUP = 0x80246989
+ SIOCDIFPHYADDR = 0x80206949
+ SIOCDLIFADDR = 0x8118691d
+ SIOCGDRVSPEC = 0xc01c697b
+ SIOCGETSGCNT = 0xc0147210
+ SIOCGETVIFCNT = 0xc014720f
+ SIOCGHIWAT = 0x40047301
+ SIOCGIFADDR = 0xc0206921
+ SIOCGIFBRDADDR = 0xc0206923
+ SIOCGIFCAP = 0xc020691f
+ SIOCGIFCONF = 0xc0086924
+ SIOCGIFDESCR = 0xc020692a
+ SIOCGIFDSTADDR = 0xc0206922
+ SIOCGIFFIB = 0xc020695c
+ SIOCGIFFLAGS = 0xc0206911
+ SIOCGIFGENERIC = 0xc020693a
+ SIOCGIFGMEMB = 0xc024698a
+ SIOCGIFGROUP = 0xc0246988
+ SIOCGIFINDEX = 0xc0206920
+ SIOCGIFMAC = 0xc0206926
+ SIOCGIFMEDIA = 0xc0286938
+ SIOCGIFMETRIC = 0xc0206917
+ SIOCGIFMTU = 0xc0206933
+ SIOCGIFNETMASK = 0xc0206925
+ SIOCGIFPDSTADDR = 0xc0206948
+ SIOCGIFPHYS = 0xc0206935
+ SIOCGIFPSRCADDR = 0xc0206947
+ SIOCGIFSTATUS = 0xc334693b
+ SIOCGLIFADDR = 0xc118691c
+ SIOCGLIFPHYADDR = 0xc118694b
+ SIOCGLOWAT = 0x40047303
+ SIOCGPGRP = 0x40047309
+ SIOCGPRIVATE_0 = 0xc0206950
+ SIOCGPRIVATE_1 = 0xc0206951
+ SIOCIFCREATE = 0xc020697a
+ SIOCIFCREATE2 = 0xc020697c
+ SIOCIFDESTROY = 0x80206979
+ SIOCIFGCLONERS = 0xc00c6978
+ SIOCSDRVSPEC = 0x801c697b
+ SIOCSHIWAT = 0x80047300
+ SIOCSIFADDR = 0x8020690c
+ SIOCSIFBRDADDR = 0x80206913
+ SIOCSIFCAP = 0x8020691e
+ SIOCSIFDESCR = 0x80206929
+ SIOCSIFDSTADDR = 0x8020690e
+ SIOCSIFFIB = 0x8020695d
+ SIOCSIFFLAGS = 0x80206910
+ SIOCSIFGENERIC = 0x80206939
+ SIOCSIFLLADDR = 0x8020693c
+ SIOCSIFMAC = 0x80206927
+ SIOCSIFMEDIA = 0xc0206937
+ SIOCSIFMETRIC = 0x80206918
+ SIOCSIFMTU = 0x80206934
+ SIOCSIFNAME = 0x80206928
+ SIOCSIFNETMASK = 0x80206916
+ SIOCSIFPHYADDR = 0x80446946
+ SIOCSIFPHYS = 0x80206936
+ SIOCSIFRVNET = 0xc020695b
+ SIOCSIFVNET = 0xc020695a
+ SIOCSLIFPHYADDR = 0x8118694a
+ SIOCSLOWAT = 0x80047302
+ SIOCSPGRP = 0x80047308
+ SOCK_DGRAM = 0x2
+ SOCK_MAXADDRLEN = 0xff
+ SOCK_RAW = 0x3
+ SOCK_RDM = 0x4
+ SOCK_SEQPACKET = 0x5
+ SOCK_STREAM = 0x1
+ SOL_SOCKET = 0xffff
+ SOMAXCONN = 0x80
+ SO_ACCEPTCONN = 0x2
+ SO_ACCEPTFILTER = 0x1000
+ SO_BINTIME = 0x2000
+ SO_BROADCAST = 0x20
+ SO_DEBUG = 0x1
+ SO_DONTROUTE = 0x10
+ SO_ERROR = 0x1007
+ SO_KEEPALIVE = 0x8
+ SO_LABEL = 0x1009
+ SO_LINGER = 0x80
+ SO_LISTENINCQLEN = 0x1013
+ SO_LISTENQLEN = 0x1012
+ SO_LISTENQLIMIT = 0x1011
+ SO_NOSIGPIPE = 0x800
+ SO_NO_DDP = 0x8000
+ SO_NO_OFFLOAD = 0x4000
+ SO_OOBINLINE = 0x100
+ SO_PEERLABEL = 0x1010
+ SO_PROTOCOL = 0x1016
+ SO_PROTOTYPE = 0x1016
+ SO_RCVBUF = 0x1002
+ SO_RCVLOWAT = 0x1004
+ SO_RCVTIMEO = 0x1006
+ SO_REUSEADDR = 0x4
+ SO_REUSEPORT = 0x200
+ SO_SETFIB = 0x1014
+ SO_SNDBUF = 0x1001
+ SO_SNDLOWAT = 0x1003
+ SO_SNDTIMEO = 0x1005
+ SO_TIMESTAMP = 0x400
+ SO_TYPE = 0x1008
+ SO_USELOOPBACK = 0x40
+ SO_USER_COOKIE = 0x1015
+ TCIFLUSH = 0x1
+ TCIOFLUSH = 0x3
+ TCOFLUSH = 0x2
+ TCP_CA_NAME_MAX = 0x10
+ TCP_CONGESTION = 0x40
+ TCP_INFO = 0x20
+ TCP_KEEPCNT = 0x400
+ TCP_KEEPIDLE = 0x100
+ TCP_KEEPINIT = 0x80
+ TCP_KEEPINTVL = 0x200
+ TCP_MAXBURST = 0x4
+ TCP_MAXHLEN = 0x3c
+ TCP_MAXOLEN = 0x28
+ TCP_MAXSEG = 0x2
+ TCP_MAXWIN = 0xffff
+ TCP_MAX_SACK = 0x4
+ TCP_MAX_WINSHIFT = 0xe
+ TCP_MD5SIG = 0x10
+ TCP_MINMSS = 0xd8
+ TCP_MSS = 0x218
+ TCP_NODELAY = 0x1
+ TCP_NOOPT = 0x8
+ TCP_NOPUSH = 0x4
+ TCSAFLUSH = 0x2
+ TIOCCBRK = 0x2000747a
+ TIOCCDTR = 0x20007478
+ TIOCCONS = 0x80047462
+ TIOCDRAIN = 0x2000745e
+ TIOCEXCL = 0x2000740d
+ TIOCEXT = 0x80047460
+ TIOCFLUSH = 0x80047410
+ TIOCGDRAINWAIT = 0x40047456
+ TIOCGETA = 0x402c7413
+ TIOCGETD = 0x4004741a
+ TIOCGPGRP = 0x40047477
+ TIOCGPTN = 0x4004740f
+ TIOCGSID = 0x40047463
+ TIOCGWINSZ = 0x40087468
+ TIOCMBIC = 0x8004746b
+ TIOCMBIS = 0x8004746c
+ TIOCMGDTRWAIT = 0x4004745a
+ TIOCMGET = 0x4004746a
+ TIOCMSDTRWAIT = 0x8004745b
+ TIOCMSET = 0x8004746d
+ TIOCM_CAR = 0x40
+ TIOCM_CD = 0x40
+ TIOCM_CTS = 0x20
+ TIOCM_DCD = 0x40
+ TIOCM_DSR = 0x100
+ TIOCM_DTR = 0x2
+ TIOCM_LE = 0x1
+ TIOCM_RI = 0x80
+ TIOCM_RNG = 0x80
+ TIOCM_RTS = 0x4
+ TIOCM_SR = 0x10
+ TIOCM_ST = 0x8
+ TIOCNOTTY = 0x20007471
+ TIOCNXCL = 0x2000740e
+ TIOCOUTQ = 0x40047473
+ TIOCPKT = 0x80047470
+ TIOCPKT_DATA = 0x0
+ TIOCPKT_DOSTOP = 0x20
+ TIOCPKT_FLUSHREAD = 0x1
+ TIOCPKT_FLUSHWRITE = 0x2
+ TIOCPKT_IOCTL = 0x40
+ TIOCPKT_NOSTOP = 0x10
+ TIOCPKT_START = 0x8
+ TIOCPKT_STOP = 0x4
+ TIOCPTMASTER = 0x2000741c
+ TIOCSBRK = 0x2000747b
+ TIOCSCTTY = 0x20007461
+ TIOCSDRAINWAIT = 0x80047457
+ TIOCSDTR = 0x20007479
+ TIOCSETA = 0x802c7414
+ TIOCSETAF = 0x802c7416
+ TIOCSETAW = 0x802c7415
+ TIOCSETD = 0x8004741b
+ TIOCSIG = 0x2004745f
+ TIOCSPGRP = 0x80047476
+ TIOCSTART = 0x2000746e
+ TIOCSTAT = 0x20007465
+ TIOCSTI = 0x80017472
+ TIOCSTOP = 0x2000746f
+ TIOCSWINSZ = 0x80087467
+ TIOCTIMESTAMP = 0x400c7459
+ TIOCUCNTL = 0x80047466
+ TOSTOP = 0x400000
+ VDISCARD = 0xf
+ VDSUSP = 0xb
+ VEOF = 0x0
+ VEOL = 0x1
+ VEOL2 = 0x2
+ VERASE = 0x3
+ VERASE2 = 0x7
+ VINTR = 0x8
+ VKILL = 0x5
+ VLNEXT = 0xe
+ VMIN = 0x10
+ VQUIT = 0x9
+ VREPRINT = 0x6
+ VSTART = 0xc
+ VSTATUS = 0x12
+ VSTOP = 0xd
+ VSUSP = 0xa
+ VTIME = 0x11
+ VWERASE = 0x4
+ WCONTINUED = 0x4
+ WCOREFLAG = 0x80
+ WLINUXCLONE = 0x80000000
+ WNOHANG = 0x1
+ WNOWAIT = 0x8
+ WSTOPPED = 0x2
+ WUNTRACED = 0x2
+)
+
+// Errors
+const (
+ E2BIG = Errno(0x7)
+ EACCES = Errno(0xd)
+ EADDRINUSE = Errno(0x30)
+ EADDRNOTAVAIL = Errno(0x31)
+ EAFNOSUPPORT = Errno(0x2f)
+ EAGAIN = Errno(0x23)
+ EALREADY = Errno(0x25)
+ EAUTH = Errno(0x50)
+ EBADF = Errno(0x9)
+ EBADMSG = Errno(0x59)
+ EBADRPC = Errno(0x48)
+ EBUSY = Errno(0x10)
+ ECANCELED = Errno(0x55)
+ ECAPMODE = Errno(0x5e)
+ ECHILD = Errno(0xa)
+ ECONNABORTED = Errno(0x35)
+ ECONNREFUSED = Errno(0x3d)
+ ECONNRESET = Errno(0x36)
+ EDEADLK = Errno(0xb)
+ EDESTADDRREQ = Errno(0x27)
+ EDOM = Errno(0x21)
+ EDOOFUS = Errno(0x58)
+ EDQUOT = Errno(0x45)
+ EEXIST = Errno(0x11)
+ EFAULT = Errno(0xe)
+ EFBIG = Errno(0x1b)
+ EFTYPE = Errno(0x4f)
+ EHOSTDOWN = Errno(0x40)
+ EHOSTUNREACH = Errno(0x41)
+ EIDRM = Errno(0x52)
+ EILSEQ = Errno(0x56)
+ EINPROGRESS = Errno(0x24)
+ EINTR = Errno(0x4)
+ EINVAL = Errno(0x16)
+ EIO = Errno(0x5)
+ EISCONN = Errno(0x38)
+ EISDIR = Errno(0x15)
+ ELAST = Errno(0x5e)
+ ELOOP = Errno(0x3e)
+ EMFILE = Errno(0x18)
+ EMLINK = Errno(0x1f)
+ EMSGSIZE = Errno(0x28)
+ EMULTIHOP = Errno(0x5a)
+ ENAMETOOLONG = Errno(0x3f)
+ ENEEDAUTH = Errno(0x51)
+ ENETDOWN = Errno(0x32)
+ ENETRESET = Errno(0x34)
+ ENETUNREACH = Errno(0x33)
+ ENFILE = Errno(0x17)
+ ENOATTR = Errno(0x57)
+ ENOBUFS = Errno(0x37)
+ ENODEV = Errno(0x13)
+ ENOENT = Errno(0x2)
+ ENOEXEC = Errno(0x8)
+ ENOLCK = Errno(0x4d)
+ ENOLINK = Errno(0x5b)
+ ENOMEM = Errno(0xc)
+ ENOMSG = Errno(0x53)
+ ENOPROTOOPT = Errno(0x2a)
+ ENOSPC = Errno(0x1c)
+ ENOSYS = Errno(0x4e)
+ ENOTBLK = Errno(0xf)
+ ENOTCAPABLE = Errno(0x5d)
+ ENOTCONN = Errno(0x39)
+ ENOTDIR = Errno(0x14)
+ ENOTEMPTY = Errno(0x42)
+ ENOTSOCK = Errno(0x26)
+ ENOTSUP = Errno(0x2d)
+ ENOTTY = Errno(0x19)
+ ENXIO = Errno(0x6)
+ EOPNOTSUPP = Errno(0x2d)
+ EOVERFLOW = Errno(0x54)
+ EPERM = Errno(0x1)
+ EPFNOSUPPORT = Errno(0x2e)
+ EPIPE = Errno(0x20)
+ EPROCLIM = Errno(0x43)
+ EPROCUNAVAIL = Errno(0x4c)
+ EPROGMISMATCH = Errno(0x4b)
+ EPROGUNAVAIL = Errno(0x4a)
+ EPROTO = Errno(0x5c)
+ EPROTONOSUPPORT = Errno(0x2b)
+ EPROTOTYPE = Errno(0x29)
+ ERANGE = Errno(0x22)
+ EREMOTE = Errno(0x47)
+ EROFS = Errno(0x1e)
+ ERPCMISMATCH = Errno(0x49)
+ ESHUTDOWN = Errno(0x3a)
+ ESOCKTNOSUPPORT = Errno(0x2c)
+ ESPIPE = Errno(0x1d)
+ ESRCH = Errno(0x3)
+ ESTALE = Errno(0x46)
+ ETIMEDOUT = Errno(0x3c)
+ ETOOMANYREFS = Errno(0x3b)
+ ETXTBSY = Errno(0x1a)
+ EUSERS = Errno(0x44)
+ EWOULDBLOCK = Errno(0x23)
+ EXDEV = Errno(0x12)
+)
+
+// Signals
+const (
+ SIGABRT = Signal(0x6)
+ SIGALRM = Signal(0xe)
+ SIGBUS = Signal(0xa)
+ SIGCHLD = Signal(0x14)
+ SIGCONT = Signal(0x13)
+ SIGEMT = Signal(0x7)
+ SIGFPE = Signal(0x8)
+ SIGHUP = Signal(0x1)
+ SIGILL = Signal(0x4)
+ SIGINFO = Signal(0x1d)
+ SIGINT = Signal(0x2)
+ SIGIO = Signal(0x17)
+ SIGIOT = Signal(0x6)
+ SIGKILL = Signal(0x9)
+ SIGLIBRT = Signal(0x21)
+ SIGLWP = Signal(0x20)
+ SIGPIPE = Signal(0xd)
+ SIGPROF = Signal(0x1b)
+ SIGQUIT = Signal(0x3)
+ SIGSEGV = Signal(0xb)
+ SIGSTOP = Signal(0x11)
+ SIGSYS = Signal(0xc)
+ SIGTERM = Signal(0xf)
+ SIGTHR = Signal(0x20)
+ SIGTRAP = Signal(0x5)
+ SIGTSTP = Signal(0x12)
+ SIGTTIN = Signal(0x15)
+ SIGTTOU = Signal(0x16)
+ SIGURG = Signal(0x10)
+ SIGUSR1 = Signal(0x1e)
+ SIGUSR2 = Signal(0x1f)
+ SIGVTALRM = Signal(0x1a)
+ SIGWINCH = Signal(0x1c)
+ SIGXCPU = Signal(0x18)
+ SIGXFSZ = Signal(0x19)
+)
+
+// Error table
+var errors = [...]string{
+ 1: "operation not permitted",
+ 2: "no such file or directory",
+ 3: "no such process",
+ 4: "interrupted system call",
+ 5: "input/output error",
+ 6: "device not configured",
+ 7: "argument list too long",
+ 8: "exec format error",
+ 9: "bad file descriptor",
+ 10: "no child processes",
+ 11: "resource deadlock avoided",
+ 12: "cannot allocate memory",
+ 13: "permission denied",
+ 14: "bad address",
+ 15: "block device required",
+ 16: "device busy",
+ 17: "file exists",
+ 18: "cross-device link",
+ 19: "operation not supported by device",
+ 20: "not a directory",
+ 21: "is a directory",
+ 22: "invalid argument",
+ 23: "too many open files in system",
+ 24: "too many open files",
+ 25: "inappropriate ioctl for device",
+ 26: "text file busy",
+ 27: "file too large",
+ 28: "no space left on device",
+ 29: "illegal seek",
+ 30: "read-only file system",
+ 31: "too many links",
+ 32: "broken pipe",
+ 33: "numerical argument out of domain",
+ 34: "result too large",
+ 35: "resource temporarily unavailable",
+ 36: "operation now in progress",
+ 37: "operation already in progress",
+ 38: "socket operation on non-socket",
+ 39: "destination address required",
+ 40: "message too long",
+ 41: "protocol wrong type for socket",
+ 42: "protocol not available",
+ 43: "protocol not supported",
+ 44: "socket type not supported",
+ 45: "operation not supported",
+ 46: "protocol family not supported",
+ 47: "address family not supported by protocol family",
+ 48: "address already in use",
+ 49: "can't assign requested address",
+ 50: "network is down",
+ 51: "network is unreachable",
+ 52: "network dropped connection on reset",
+ 53: "software caused connection abort",
+ 54: "connection reset by peer",
+ 55: "no buffer space available",
+ 56: "socket is already connected",
+ 57: "socket is not connected",
+ 58: "can't send after socket shutdown",
+ 59: "too many references: can't splice",
+ 60: "operation timed out",
+ 61: "connection refused",
+ 62: "too many levels of symbolic links",
+ 63: "file name too long",
+ 64: "host is down",
+ 65: "no route to host",
+ 66: "directory not empty",
+ 67: "too many processes",
+ 68: "too many users",
+ 69: "disc quota exceeded",
+ 70: "stale NFS file handle",
+ 71: "too many levels of remote in path",
+ 72: "RPC struct is bad",
+ 73: "RPC version wrong",
+ 74: "RPC prog. not avail",
+ 75: "program version wrong",
+ 76: "bad procedure for program",
+ 77: "no locks available",
+ 78: "function not implemented",
+ 79: "inappropriate file type or format",
+ 80: "authentication error",
+ 81: "need authenticator",
+ 82: "identifier removed",
+ 83: "no message of desired type",
+ 84: "value too large to be stored in data type",
+ 85: "operation canceled",
+ 86: "illegal byte sequence",
+ 87: "attribute not found",
+ 88: "programming error",
+ 89: "bad message",
+ 90: "multihop attempted",
+ 91: "link has been severed",
+ 92: "protocol error",
+ 93: "capabilities insufficient",
+ 94: "not permitted in capability mode",
+}
+
+// Signal table
+var signals = [...]string{
+ 1: "hangup",
+ 2: "interrupt",
+ 3: "quit",
+ 4: "illegal instruction",
+ 5: "trace/BPT trap",
+ 6: "abort trap",
+ 7: "EMT trap",
+ 8: "floating point exception",
+ 9: "killed",
+ 10: "bus error",
+ 11: "segmentation fault",
+ 12: "bad system call",
+ 13: "broken pipe",
+ 14: "alarm clock",
+ 15: "terminated",
+ 16: "urgent I/O condition",
+ 17: "suspended (signal)",
+ 18: "suspended",
+ 19: "continued",
+ 20: "child exited",
+ 21: "stopped (tty input)",
+ 22: "stopped (tty output)",
+ 23: "I/O possible",
+ 24: "cputime limit exceeded",
+ 25: "filesize limit exceeded",
+ 26: "virtual timer expired",
+ 27: "profiling timer expired",
+ 28: "window size changes",
+ 29: "information request",
+ 30: "user defined signal 1",
+ 31: "user defined signal 2",
+ 32: "unknown signal",
+ 33: "unknown signal",
+}
diff --git a/src/pkg/syscall/zerrors_linux_386.go b/src/pkg/syscall/zerrors_linux_386.go
index 065da8f45..a689942b8 100644
--- a/src/pkg/syscall/zerrors_linux_386.go
+++ b/src/pkg/syscall/zerrors_linux_386.go
@@ -528,6 +528,7 @@ const (
MSG_DONTWAIT = 0x40
MSG_EOR = 0x80
MSG_ERRQUEUE = 0x2000
+ MSG_FASTOPEN = 0x20000000
MSG_FIN = 0x200
MSG_MORE = 0x8000
MSG_NOSIGNAL = 0x4000
diff --git a/src/pkg/syscall/zerrors_linux_amd64.go b/src/pkg/syscall/zerrors_linux_amd64.go
index 4e4918452..a1ac1773b 100644
--- a/src/pkg/syscall/zerrors_linux_amd64.go
+++ b/src/pkg/syscall/zerrors_linux_amd64.go
@@ -528,6 +528,7 @@ const (
MSG_DONTWAIT = 0x40
MSG_EOR = 0x80
MSG_ERRQUEUE = 0x2000
+ MSG_FASTOPEN = 0x20000000
MSG_FIN = 0x200
MSG_MORE = 0x8000
MSG_NOSIGNAL = 0x4000
diff --git a/src/pkg/syscall/zerrors_linux_arm.go b/src/pkg/syscall/zerrors_linux_arm.go
index 9b99cf83f..0730f2140 100644
--- a/src/pkg/syscall/zerrors_linux_arm.go
+++ b/src/pkg/syscall/zerrors_linux_arm.go
@@ -523,6 +523,7 @@ const (
MSG_DONTWAIT = 0x40
MSG_EOR = 0x80
MSG_ERRQUEUE = 0x2000
+ MSG_FASTOPEN = 0x20000000
MSG_FIN = 0x200
MSG_MORE = 0x8000
MSG_NOSIGNAL = 0x4000
diff --git a/src/pkg/syscall/zerrors_netbsd_386.go b/src/pkg/syscall/zerrors_netbsd_386.go
index 52dc69a64..68051f9f8 100644
--- a/src/pkg/syscall/zerrors_netbsd_386.go
+++ b/src/pkg/syscall/zerrors_netbsd_386.go
@@ -8,7 +8,8 @@ package syscall
const (
AF_APPLETALK = 0x10
- AF_BLUETOOTH = 0x20
+ AF_ARP = 0x1c
+ AF_BLUETOOTH = 0x1f
AF_CCITT = 0xa
AF_CHAOS = 0x5
AF_CNT = 0x15
@@ -18,67 +19,93 @@ const (
AF_DLI = 0xd
AF_E164 = 0x1a
AF_ECMA = 0x8
- AF_ENCAP = 0x1c
AF_HYLINK = 0xf
+ AF_IEEE80211 = 0x20
AF_IMPLINK = 0x3
AF_INET = 0x2
AF_INET6 = 0x18
AF_IPX = 0x17
AF_ISDN = 0x1a
AF_ISO = 0x7
- AF_KEY = 0x1e
AF_LAT = 0xe
AF_LINK = 0x12
AF_LOCAL = 0x1
- AF_MAX = 0x24
+ AF_MAX = 0x23
AF_MPLS = 0x21
AF_NATM = 0x1b
AF_NS = 0x6
+ AF_OROUTE = 0x11
AF_OSI = 0x7
AF_PUP = 0x4
- AF_ROUTE = 0x11
- AF_SIP = 0x1d
+ AF_ROUTE = 0x22
AF_SNA = 0xb
AF_UNIX = 0x1
AF_UNSPEC = 0x0
+ ARPHRD_ARCNET = 0x7
ARPHRD_ETHER = 0x1
ARPHRD_FRELAY = 0xf
ARPHRD_IEEE1394 = 0x18
ARPHRD_IEEE802 = 0x6
+ ARPHRD_STRIP = 0x17
+ B0 = 0x0
+ B110 = 0x6e
+ B115200 = 0x1c200
+ B1200 = 0x4b0
+ B134 = 0x86
+ B14400 = 0x3840
+ B150 = 0x96
+ B1800 = 0x708
+ B19200 = 0x4b00
+ B200 = 0xc8
+ B230400 = 0x38400
+ B2400 = 0x960
+ B28800 = 0x7080
+ B300 = 0x12c
+ B38400 = 0x9600
+ B460800 = 0x70800
+ B4800 = 0x12c0
+ B50 = 0x32
+ B57600 = 0xe100
+ B600 = 0x258
+ B7200 = 0x1c20
+ B75 = 0x4b
+ B76800 = 0x12c00
+ B921600 = 0xe1000
+ B9600 = 0x2580
+ BIOCFEEDBACK = 0x8004427d
BIOCFLUSH = 0x20004268
BIOCGBLEN = 0x40044266
- BIOCGDIRFILT = 0x4004427c
BIOCGDLT = 0x4004426a
- BIOCGDLTLIST = 0xc008427b
- BIOCGETIF = 0x4020426b
- BIOCGFILDROP = 0x40044278
+ BIOCGDLTLIST = 0xc0084277
+ BIOCGETIF = 0x4090426b
+ BIOCGFEEDBACK = 0x4004427c
BIOCGHDRCMPLT = 0x40044274
- BIOCGRSIG = 0x40044273
- BIOCGRTIMEOUT = 0x4008426e
- BIOCGSTATS = 0x4008426f
+ BIOCGRTIMEOUT = 0x400c427b
+ BIOCGSEESENT = 0x40044278
+ BIOCGSTATS = 0x4080426f
+ BIOCGSTATSOLD = 0x4008426f
BIOCIMMEDIATE = 0x80044270
- BIOCLOCK = 0x20004276
BIOCPROMISC = 0x20004269
BIOCSBLEN = 0xc0044266
- BIOCSDIRFILT = 0x8004427d
- BIOCSDLT = 0x8004427a
+ BIOCSDLT = 0x80044276
BIOCSETF = 0x80084267
- BIOCSETIF = 0x8020426c
- BIOCSETWF = 0x80084277
- BIOCSFILDROP = 0x80044279
+ BIOCSETIF = 0x8090426c
+ BIOCSFEEDBACK = 0x8004427d
BIOCSHDRCMPLT = 0x80044275
- BIOCSRSIG = 0x80044272
- BIOCSRTIMEOUT = 0x8008426d
+ BIOCSRTIMEOUT = 0x800c427a
+ BIOCSSEESENT = 0x80044279
+ BIOCSTCPF = 0x80084272
+ BIOCSUDPF = 0x80084273
BIOCVERSION = 0x40044271
BPF_A = 0x10
BPF_ABS = 0x20
BPF_ADD = 0x0
BPF_ALIGNMENT = 0x4
+ BPF_ALIGNMENT32 = 0x4
BPF_ALU = 0x4
BPF_AND = 0x50
BPF_B = 0x10
- BPF_DIRECTION_IN = 0x1
- BPF_DIRECTION_OUT = 0x2
+ BPF_DFLTBUFSIZE = 0x100000
BPF_DIV = 0x30
BPF_H = 0x8
BPF_IMM = 0x0
@@ -95,7 +122,7 @@ const (
BPF_LEN = 0x80
BPF_LSH = 0x60
BPF_MAJOR_VERSION = 0x1
- BPF_MAXBUFSIZE = 0x200000
+ BPF_MAXBUFSIZE = 0x1000000
BPF_MAXINSNS = 0x200
BPF_MEM = 0x60
BPF_MEMWORDS = 0x10
@@ -116,31 +143,151 @@ const (
BPF_TXA = 0x80
BPF_W = 0x0
BPF_X = 0x8
+ BRKINT = 0x2
+ CFLUSH = 0xf
+ CLOCAL = 0x8000
+ CREAD = 0x800
+ CS5 = 0x0
+ CS6 = 0x100
+ CS7 = 0x200
+ CS8 = 0x300
+ CSIZE = 0x300
+ CSTART = 0x11
+ CSTATUS = 0x14
+ CSTOP = 0x13
+ CSTOPB = 0x400
+ CSUSP = 0x1a
CTL_MAXNAME = 0xc
CTL_NET = 0x4
+ CTL_QUERY = -0x2
+ DIOCBSFLUSH = 0x20006478
+ DLT_A429 = 0xb8
+ DLT_A653_ICM = 0xb9
+ DLT_AIRONET_HEADER = 0x78
+ DLT_AOS = 0xde
+ DLT_APPLE_IP_OVER_IEEE1394 = 0x8a
DLT_ARCNET = 0x7
+ DLT_ARCNET_LINUX = 0x81
+ DLT_ATM_CLIP = 0x13
DLT_ATM_RFC1483 = 0xb
+ DLT_AURORA = 0x7e
DLT_AX25 = 0x3
+ DLT_AX25_KISS = 0xca
+ DLT_BACNET_MS_TP = 0xa5
+ DLT_BLUETOOTH_HCI_H4 = 0xbb
+ DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 0xc9
+ DLT_CAN20B = 0xbe
+ DLT_CAN_SOCKETCAN = 0xe3
DLT_CHAOS = 0x5
+ DLT_CISCO_IOS = 0x76
+ DLT_C_HDLC = 0x68
+ DLT_C_HDLC_WITH_DIR = 0xcd
+ DLT_DECT = 0xdd
+ DLT_DOCSIS = 0x8f
+ DLT_ECONET = 0x73
DLT_EN10MB = 0x1
DLT_EN3MB = 0x2
- DLT_ENC = 0xd
+ DLT_ENC = 0x6d
+ DLT_ERF = 0xc5
+ DLT_ERF_ETH = 0xaf
+ DLT_ERF_POS = 0xb0
+ DLT_FC_2 = 0xe0
+ DLT_FC_2_WITH_FRAME_DELIMS = 0xe1
DLT_FDDI = 0xa
+ DLT_FLEXRAY = 0xd2
+ DLT_FRELAY = 0x6b
+ DLT_FRELAY_WITH_DIR = 0xce
+ DLT_GCOM_SERIAL = 0xad
+ DLT_GCOM_T1E1 = 0xac
+ DLT_GPF_F = 0xab
+ DLT_GPF_T = 0xaa
+ DLT_GPRS_LLC = 0xa9
+ DLT_GSMTAP_ABIS = 0xda
+ DLT_GSMTAP_UM = 0xd9
+ DLT_HDLC = 0x10
+ DLT_HHDLC = 0x79
+ DLT_HIPPI = 0xf
+ DLT_IBM_SN = 0x92
+ DLT_IBM_SP = 0x91
DLT_IEEE802 = 0x6
DLT_IEEE802_11 = 0x69
DLT_IEEE802_11_RADIO = 0x7f
- DLT_LOOP = 0xc
+ DLT_IEEE802_11_RADIO_AVS = 0xa3
+ DLT_IEEE802_15_4 = 0xc3
+ DLT_IEEE802_15_4_LINUX = 0xbf
+ DLT_IEEE802_15_4_NONASK_PHY = 0xd7
+ DLT_IEEE802_16_MAC_CPS = 0xbc
+ DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1
+ DLT_IPMB = 0xc7
+ DLT_IPMB_LINUX = 0xd1
+ DLT_IPNET = 0xe2
+ DLT_IPV4 = 0xe4
+ DLT_IPV6 = 0xe5
+ DLT_IP_OVER_FC = 0x7a
+ DLT_JUNIPER_ATM1 = 0x89
+ DLT_JUNIPER_ATM2 = 0x87
+ DLT_JUNIPER_CHDLC = 0xb5
+ DLT_JUNIPER_ES = 0x84
+ DLT_JUNIPER_ETHER = 0xb2
+ DLT_JUNIPER_FRELAY = 0xb4
+ DLT_JUNIPER_GGSN = 0x85
+ DLT_JUNIPER_ISM = 0xc2
+ DLT_JUNIPER_MFR = 0x86
+ DLT_JUNIPER_MLFR = 0x83
+ DLT_JUNIPER_MLPPP = 0x82
+ DLT_JUNIPER_MONITOR = 0xa4
+ DLT_JUNIPER_PIC_PEER = 0xae
+ DLT_JUNIPER_PPP = 0xb3
+ DLT_JUNIPER_PPPOE = 0xa7
+ DLT_JUNIPER_PPPOE_ATM = 0xa8
+ DLT_JUNIPER_SERVICES = 0x88
+ DLT_JUNIPER_ST = 0xc8
+ DLT_JUNIPER_VP = 0xb7
+ DLT_LAPB_WITH_DIR = 0xcf
+ DLT_LAPD = 0xcb
+ DLT_LIN = 0xd4
+ DLT_LINUX_EVDEV = 0xd8
+ DLT_LINUX_IRDA = 0x90
+ DLT_LINUX_LAPD = 0xb1
+ DLT_LINUX_SLL = 0x71
+ DLT_LOOP = 0x6c
+ DLT_LTALK = 0x72
+ DLT_MFR = 0xb6
+ DLT_MOST = 0xd3
DLT_MPLS = 0xdb
+ DLT_MTP2 = 0x8c
+ DLT_MTP2_WITH_PHDR = 0x8b
+ DLT_MTP3 = 0x8d
DLT_NULL = 0x0
+ DLT_PCI_EXP = 0x7d
DLT_PFLOG = 0x75
DLT_PFSYNC = 0x12
+ DLT_PPI = 0xc0
DLT_PPP = 0x9
- DLT_PPP_BSDOS = 0x10
+ DLT_PPP_BSDOS = 0xe
DLT_PPP_ETHER = 0x33
+ DLT_PPP_PPPD = 0xa6
+ DLT_PPP_SERIAL = 0x32
+ DLT_PPP_WITH_DIR = 0xcc
+ DLT_PRISM_HEADER = 0x77
DLT_PRONET = 0x4
- DLT_RAW = 0xe
+ DLT_RAIF1 = 0xc6
+ DLT_RAW = 0xc
+ DLT_RAWAF_MASK = 0x2240000
+ DLT_RIO = 0x7c
+ DLT_SCCP = 0x8e
+ DLT_SITA = 0xc4
DLT_SLIP = 0x8
- DLT_SLIP_BSDOS = 0xf
+ DLT_SLIP_BSDOS = 0xd
+ DLT_SUNATM = 0x7b
+ DLT_SYMANTEC_FIREWALL = 0x63
+ DLT_TZSP = 0x80
+ DLT_USB = 0xba
+ DLT_USB_LINUX = 0xbd
+ DLT_USB_LINUX_MMAPPED = 0xdc
+ DLT_WIHART = 0xdf
+ DLT_X2E_SERIAL = 0xd5
+ DLT_X2E_XORAYA = 0xd6
DT_BLK = 0x6
DT_CHR = 0x2
DT_DIR = 0x4
@@ -149,15 +296,33 @@ const (
DT_REG = 0x8
DT_SOCK = 0xc
DT_UNKNOWN = 0x0
- EFER_LMA = 0x400
- EFER_LME = 0x100
- EFER_NXE = 0x800
- EFER_SCE = 0x1
- EMT_TAGOVF = 0x1
- EMUL_ENABLED = 0x1
- EMUL_NATIVE = 0x2
+ DT_WHT = 0xe
+ ECHO = 0x8
+ ECHOCTL = 0x40
+ ECHOE = 0x2
+ ECHOK = 0x4
+ ECHOKE = 0x1
+ ECHONL = 0x10
+ ECHOPRT = 0x20
+ EMUL_LINUX = 0x1
+ EMUL_LINUX32 = 0x5
+ EMUL_MAXID = 0x6
+ EN_SW_CTL_INF = 0x1000
+ EN_SW_CTL_PREC = 0x300
+ EN_SW_CTL_ROUND = 0xc00
+ EN_SW_DATACHAIN = 0x80
+ EN_SW_DENORM = 0x2
+ EN_SW_INVOP = 0x1
+ EN_SW_OVERFLOW = 0x8
+ EN_SW_PRECLOSS = 0x20
+ EN_SW_UNDERFLOW = 0x10
+ EN_SW_ZERODIV = 0x4
+ ETHERCAP_JUMBO_MTU = 0x4
+ ETHERCAP_VLAN_HWTAGGING = 0x2
+ ETHERCAP_VLAN_MTU = 0x1
ETHERMIN = 0x2e
ETHERMTU = 0x5dc
+ ETHERMTU_JUMBO = 0x2328
ETHERTYPE_8023 = 0x4
ETHERTYPE_AARP = 0x80f3
ETHERTYPE_ACCTON = 0x8390
@@ -165,7 +330,6 @@ const (
ETHERTYPE_ALPHA = 0x814a
ETHERTYPE_AMBER = 0x6008
ETHERTYPE_AMOEBA = 0x8145
- ETHERTYPE_AOE = 0x88a2
ETHERTYPE_APOLLO = 0x80f7
ETHERTYPE_APOLLODOMAIN = 0x8019
ETHERTYPE_APPLETALK = 0x809b
@@ -236,7 +400,6 @@ const (
ETHERTYPE_LAT = 0x6004
ETHERTYPE_LBACK = 0x9000
ETHERTYPE_LITTLE = 0x8060
- ETHERTYPE_LLDP = 0x88cc
ETHERTYPE_LOGICRAFT = 0x8148
ETHERTYPE_LOOPBACK = 0x9000
ETHERTYPE_MATRA = 0x807a
@@ -284,7 +447,6 @@ const (
ETHERTYPE_PRIMENTS = 0x7031
ETHERTYPE_PUP = 0x200
ETHERTYPE_PUPAT = 0x200
- ETHERTYPE_QINQ = 0x88a8
ETHERTYPE_RACAL = 0x7030
ETHERTYPE_RATIONAL = 0x8150
ETHERTYPE_RAWFR = 0x6559
@@ -301,7 +463,7 @@ const (
ETHERTYPE_SG_NETGAMES = 0x8014
ETHERTYPE_SG_RESV = 0x8015
ETHERTYPE_SIMNET = 0x5208
- ETHERTYPE_SLOW = 0x8809
+ ETHERTYPE_SLOWPROTOCOLS = 0x8809
ETHERTYPE_SNA = 0x80d5
ETHERTYPE_SNMP = 0x814c
ETHERTYPE_SONIX = 0xfaf5
@@ -344,24 +506,24 @@ const (
ETHERTYPE_XNSSM = 0x9001
ETHERTYPE_XTP = 0x817d
ETHER_ADDR_LEN = 0x6
- ETHER_ALIGN = 0x2
ETHER_CRC_LEN = 0x4
ETHER_CRC_POLY_BE = 0x4c11db6
ETHER_CRC_POLY_LE = 0xedb88320
ETHER_HDR_LEN = 0xe
- ETHER_MAX_DIX_LEN = 0x600
ETHER_MAX_LEN = 0x5ee
+ ETHER_MAX_LEN_JUMBO = 0x233a
ETHER_MIN_LEN = 0x40
+ ETHER_PPPOE_ENCAP_LEN = 0x8
ETHER_TYPE_LEN = 0x2
ETHER_VLAN_ENCAP_LEN = 0x4
- EVFILT_AIO = -0x3
- EVFILT_PROC = -0x5
- EVFILT_READ = -0x1
- EVFILT_SIGNAL = -0x6
+ EVFILT_AIO = 0x2
+ EVFILT_PROC = 0x4
+ EVFILT_READ = 0x0
+ EVFILT_SIGNAL = 0x5
EVFILT_SYSCOUNT = 0x7
- EVFILT_TIMER = -0x7
- EVFILT_VNODE = -0x4
- EVFILT_WRITE = -0x2
+ EVFILT_TIMER = 0x6
+ EVFILT_VNODE = 0x3
+ EVFILT_WRITE = 0x1
EV_ADD = 0x1
EV_CLEAR = 0x20
EV_DELETE = 0x2
@@ -376,25 +538,46 @@ const (
EXTB = 0x9600
EXTPROC = 0x800
FD_CLOEXEC = 0x1
- FD_SETSIZE = 0x400
+ FD_SETSIZE = 0x100
+ FLUSHO = 0x800000
+ F_CLOSEM = 0xa
F_DUPFD = 0x0
- F_DUPFD_CLOEXEC = 0xa
+ F_DUPFD_CLOEXEC = 0xc
+ F_FSCTL = -0x80000000
+ F_FSDIRMASK = 0x70000000
+ F_FSIN = 0x10000000
+ F_FSINOUT = 0x30000000
+ F_FSOUT = 0x20000000
+ F_FSPRIV = 0x8000
+ F_FSVOID = 0x40000000
F_GETFD = 0x1
F_GETFL = 0x3
F_GETLK = 0x7
+ F_GETNOSIGPIPE = 0xd
F_GETOWN = 0x5
+ F_MAXFD = 0xb
+ F_OK = 0x0
+ F_PARAM_MASK = 0xfff
+ F_PARAM_MAX = 0xfff
F_RDLCK = 0x1
F_SETFD = 0x2
F_SETFL = 0x4
F_SETLK = 0x8
F_SETLKW = 0x9
+ F_SETNOSIGPIPE = 0xe
F_SETOWN = 0x6
F_UNLCK = 0x2
F_WRLCK = 0x3
+ HUPCL = 0x4000
+ ICANON = 0x100
+ ICRNL = 0x100
+ IEXTEN = 0x400
+ IFAN_ARRIVAL = 0x0
+ IFAN_DEPARTURE = 0x1
IFA_ROUTE = 0x1
IFF_ALLMULTI = 0x200
IFF_BROADCAST = 0x2
- IFF_CANTCHANGE = 0x8e52
+ IFF_CANTCHANGE = 0x8f52
IFF_DEBUG = 0x4
IFF_LINK0 = 0x1000
IFF_LINK1 = 0x2000
@@ -431,10 +614,9 @@ const (
IFT_ATMVCIENDPT = 0xc2
IFT_ATMVIRTUAL = 0x95
IFT_BGPPOLICYACCOUNTING = 0xa2
- IFT_BLUETOOTH = 0xf8
IFT_BRIDGE = 0xd1
IFT_BSC = 0x53
- IFT_CARP = 0xf7
+ IFT_CARP = 0xf8
IFT_CCTEMUL = 0x3d
IFT_CEPT = 0x13
IFT_CES = 0x85
@@ -455,19 +637,17 @@ const (
IFT_DS1FDL = 0xaa
IFT_DS3 = 0x1e
IFT_DTM = 0x8c
- IFT_DUMMY = 0xf1
IFT_DVBASILN = 0xac
IFT_DVBASIOUT = 0xad
IFT_DVBRCCDOWNSTREAM = 0x93
IFT_DVBRCCMACLAYER = 0x92
IFT_DVBRCCUPSTREAM = 0x94
IFT_ECONET = 0xce
- IFT_ENC = 0xf4
IFT_EON = 0x19
IFT_EPLRS = 0x57
IFT_ESCON = 0x49
IFT_ETHER = 0x6
- IFT_FAITH = 0xf3
+ IFT_FAITH = 0xf2
IFT_FAST = 0x7d
IFT_FASTETHER = 0x3e
IFT_FASTETHERFX = 0x45
@@ -555,7 +735,6 @@ const (
IFT_P80 = 0xd
IFT_PARA = 0x22
IFT_PFLOG = 0xf5
- IFT_PFLOW = 0xf9
IFT_PFSYNC = 0xf6
IFT_PLC = 0xae
IFT_PON155 = 0xcf
@@ -573,7 +752,7 @@ const (
IFT_PROPVIRTUAL = 0x35
IFT_PROPWIRELESSP2P = 0x9d
IFT_PTPSERIAL = 0x16
- IFT_PVC = 0xf2
+ IFT_PVC = 0xf1
IFT_Q2931 = 0xc9
IFT_QLLC = 0x44
IFT_RADIOMAC = 0xbc
@@ -599,6 +778,7 @@ const (
IFT_SS7SIGLINK = 0x9c
IFT_STACKTOSTACK = 0x6f
IFT_STARLAN = 0xb
+ IFT_STF = 0xd7
IFT_T1 = 0x12
IFT_TDLC = 0x74
IFT_TELINK = 0xc8
@@ -633,6 +813,12 @@ const (
IFT_X25MLP = 0x79
IFT_X25PLE = 0x28
IFT_XETHER = 0x1a
+ IGNBRK = 0x1
+ IGNCR = 0x80
+ IGNPAR = 0x4
+ IMAXBEL = 0x2000
+ INLCR = 0x40
+ INPCK = 0x10
IN_CLASSA_HOST = 0xffffff
IN_CLASSA_MAX = 0x80
IN_CLASSA_NET = 0xff000000
@@ -648,12 +834,8 @@ const (
IN_CLASSD_NET = 0xf0000000
IN_CLASSD_NSHIFT = 0x1c
IN_LOOPBACKNET = 0x7f
- IN_RFC3021_HOST = 0x1
- IN_RFC3021_NET = 0xfffffffe
- IN_RFC3021_NSHIFT = 0x1f
IPPROTO_AH = 0x33
IPPROTO_CARP = 0x70
- IPPROTO_DIVERT = 0x102
IPPROTO_DONE = 0x101
IPPROTO_DSTOPTS = 0x3c
IPPROTO_EGP = 0x8
@@ -674,10 +856,10 @@ const (
IPPROTO_IPIP = 0x4
IPPROTO_IPV4 = 0x4
IPPROTO_IPV6 = 0x29
+ IPPROTO_IPV6_ICMP = 0x3a
IPPROTO_MAX = 0x100
- IPPROTO_MAXID = 0x103
+ IPPROTO_MAXID = 0x34
IPPROTO_MOBILE = 0x37
- IPPROTO_MPLS = 0x89
IPPROTO_NONE = 0x3b
IPPROTO_PFSYNC = 0xf0
IPPROTO_PIM = 0x67
@@ -688,16 +870,13 @@ const (
IPPROTO_TCP = 0x6
IPPROTO_TP = 0x1d
IPPROTO_UDP = 0x11
- IPV6_AUTH_LEVEL = 0x35
- IPV6_AUTOFLOWLABEL = 0x3b
+ IPPROTO_VRRP = 0x70
IPV6_CHECKSUM = 0x1a
IPV6_DEFAULT_MULTICAST_HOPS = 0x1
IPV6_DEFAULT_MULTICAST_LOOP = 0x1
IPV6_DEFHLIM = 0x40
IPV6_DONTFRAG = 0x3e
IPV6_DSTOPTS = 0x32
- IPV6_ESP_NETWORK_LEVEL = 0x37
- IPV6_ESP_TRANS_LEVEL = 0x36
IPV6_FAITH = 0x1d
IPV6_FLOWINFO_MASK = 0xffffff0f
IPV6_FLOWLABEL_MASK = 0xffff0f00
@@ -705,7 +884,7 @@ const (
IPV6_HLIMDEC = 0x1
IPV6_HOPLIMIT = 0x2f
IPV6_HOPOPTS = 0x31
- IPV6_IPCOMP_LEVEL = 0x3c
+ IPV6_IPSEC_POLICY = 0x1c
IPV6_JOIN_GROUP = 0xc
IPV6_LEAVE_GROUP = 0xd
IPV6_MAXHLIM = 0xff
@@ -715,9 +894,7 @@ const (
IPV6_MULTICAST_IF = 0x9
IPV6_MULTICAST_LOOP = 0xb
IPV6_NEXTHOP = 0x30
- IPV6_OPTIONS = 0x1
IPV6_PATHMTU = 0x2c
- IPV6_PIPEX = 0x3f
IPV6_PKTINFO = 0x2e
IPV6_PORTRANGE = 0xe
IPV6_PORTRANGE_DEFAULT = 0x0
@@ -730,7 +907,6 @@ const (
IPV6_RECVPKTINFO = 0x24
IPV6_RECVRTHDR = 0x26
IPV6_RECVTCLASS = 0x39
- IPV6_RTABLE = 0x1021
IPV6_RTHDR = 0x33
IPV6_RTHDRDSTOPTS = 0x23
IPV6_RTHDR_LOOSE = 0x0
@@ -744,74 +920,105 @@ const (
IPV6_VERSION = 0x60
IPV6_VERSION_MASK = 0xf0
IP_ADD_MEMBERSHIP = 0xc
- IP_AUTH_LEVEL = 0x14
IP_DEFAULT_MULTICAST_LOOP = 0x1
IP_DEFAULT_MULTICAST_TTL = 0x1
IP_DF = 0x4000
IP_DROP_MEMBERSHIP = 0xd
- IP_ESP_NETWORK_LEVEL = 0x16
- IP_ESP_TRANS_LEVEL = 0x15
+ IP_EF = 0x8000
+ IP_ERRORMTU = 0x15
IP_HDRINCL = 0x2
- IP_IPCOMP_LEVEL = 0x1d
- IP_IPSEC_LOCAL_AUTH = 0x1b
- IP_IPSEC_LOCAL_CRED = 0x19
- IP_IPSEC_LOCAL_ID = 0x17
- IP_IPSEC_REMOTE_AUTH = 0x1c
- IP_IPSEC_REMOTE_CRED = 0x1a
- IP_IPSEC_REMOTE_ID = 0x18
+ IP_IPSEC_POLICY = 0x16
IP_MAXPACKET = 0xffff
- IP_MAX_MEMBERSHIPS = 0xfff
+ IP_MAX_MEMBERSHIPS = 0x14
IP_MF = 0x2000
- IP_MINTTL = 0x20
- IP_MIN_MEMBERSHIPS = 0xf
+ IP_MINFRAGSIZE = 0x45
+ IP_MINTTL = 0x18
IP_MSS = 0x240
IP_MULTICAST_IF = 0x9
IP_MULTICAST_LOOP = 0xb
IP_MULTICAST_TTL = 0xa
IP_OFFMASK = 0x1fff
IP_OPTIONS = 0x1
- IP_PIPEX = 0x22
IP_PORTRANGE = 0x13
IP_PORTRANGE_DEFAULT = 0x0
IP_PORTRANGE_HIGH = 0x1
IP_PORTRANGE_LOW = 0x2
IP_RECVDSTADDR = 0x7
- IP_RECVDSTPORT = 0x21
- IP_RECVIF = 0x1e
+ IP_RECVIF = 0x14
IP_RECVOPTS = 0x5
IP_RECVRETOPTS = 0x6
- IP_RECVRTABLE = 0x23
- IP_RECVTTL = 0x1f
+ IP_RECVTTL = 0x17
IP_RETOPTS = 0x8
IP_RF = 0x8000
- IP_RTABLE = 0x1021
IP_TOS = 0x3
IP_TTL = 0x4
- MAP_ET_KVAGUARD = 0x10
+ ISIG = 0x80
+ ISTRIP = 0x20
+ IXANY = 0x800
+ IXOFF = 0x400
+ IXON = 0x200
+ LOCK_EX = 0x2
+ LOCK_NB = 0x4
+ LOCK_SH = 0x1
+ LOCK_UN = 0x8
MSG_BCAST = 0x100
+ MSG_CMSG_CLOEXEC = 0x800
+ MSG_CONTROLMBUF = 0x2000000
MSG_CTRUNC = 0x20
MSG_DONTROUTE = 0x4
MSG_DONTWAIT = 0x80
MSG_EOR = 0x8
+ MSG_IOVUSRSPACE = 0x4000000
+ MSG_LENUSRSPACE = 0x8000000
MSG_MCAST = 0x200
+ MSG_NAMEMBUF = 0x1000000
+ MSG_NBIO = 0x1000
+ MSG_NOSIGNAL = 0x400
MSG_OOB = 0x1
MSG_PEEK = 0x2
MSG_TRUNC = 0x10
+ MSG_USERFLAGS = 0xffffff
MSG_WAITALL = 0x40
- NAME_MAX = 0xff
+ NAME_MAX = 0x1ff
NET_RT_DUMP = 0x1
NET_RT_FLAGS = 0x2
- NET_RT_IFLIST = 0x3
+ NET_RT_IFLIST = 0x5
NET_RT_MAXID = 0x6
- NET_RT_STATS = 0x4
- NET_RT_TABLE = 0x5
+ NET_RT_OIFLIST = 0x4
+ NET_RT_OOIFLIST = 0x3
+ NOFLSH = 0x80000000
+ NOTE_ATTRIB = 0x8
+ NOTE_CHILD = 0x4
+ NOTE_DELETE = 0x1
+ NOTE_EXEC = 0x20000000
+ NOTE_EXIT = 0x80000000
+ NOTE_EXTEND = 0x4
+ NOTE_FORK = 0x40000000
+ NOTE_LINK = 0x10
+ NOTE_LOWAT = 0x1
+ NOTE_PCTRLMASK = 0xf0000000
+ NOTE_PDATAMASK = 0xfffff
+ NOTE_RENAME = 0x20
+ NOTE_REVOKE = 0x40
+ NOTE_TRACK = 0x1
+ NOTE_TRACKERR = 0x2
+ NOTE_WRITE = 0x2
+ OCRNL = 0x10
+ OFIOGETBMAP = 0xc004667a
+ ONLCR = 0x2
+ ONLRET = 0x40
+ ONOCR = 0x20
+ ONOEOT = 0x8
+ OPOST = 0x1
O_ACCMODE = 0x3
+ O_ALT_IO = 0x40000
O_APPEND = 0x8
O_ASYNC = 0x40
- O_CLOEXEC = 0x10000
+ O_CLOEXEC = 0x400000
O_CREAT = 0x200
- O_DIRECTORY = 0x20000
- O_DSYNC = 0x80
+ O_DIRECT = 0x80000
+ O_DIRECTORY = 0x200000
+ O_DSYNC = 0x10000
O_EXCL = 0x800
O_EXLOCK = 0x20
O_FSYNC = 0x80
@@ -819,13 +1026,27 @@ const (
O_NOCTTY = 0x8000
O_NOFOLLOW = 0x100
O_NONBLOCK = 0x4
+ O_NOSIGPIPE = 0x1000000
O_RDONLY = 0x0
O_RDWR = 0x2
- O_RSYNC = 0x80
+ O_RSYNC = 0x20000
O_SHLOCK = 0x10
O_SYNC = 0x80
O_TRUNC = 0x400
O_WRONLY = 0x1
+ PARENB = 0x1000
+ PARMRK = 0x8
+ PARODD = 0x2000
+ PENDIN = 0x20000000
+ PRI_IOFLUSH = 0x7c
+ RLIMIT_AS = 0xa
+ RLIMIT_CORE = 0x4
+ RLIMIT_CPU = 0x0
+ RLIMIT_DATA = 0x2
+ RLIMIT_FSIZE = 0x1
+ RLIMIT_NOFILE = 0x8
+ RLIMIT_STACK = 0x3
+ RLIM_INFINITY = 0x7fffffffffffffff
RTAX_AUTHOR = 0x6
RTAX_BRD = 0x7
RTAX_DST = 0x0
@@ -833,11 +1054,9 @@ const (
RTAX_GENMASK = 0x3
RTAX_IFA = 0x5
RTAX_IFP = 0x4
- RTAX_LABEL = 0xa
- RTAX_MAX = 0xb
+ RTAX_MAX = 0x9
RTAX_NETMASK = 0x2
- RTAX_SRC = 0x8
- RTAX_SRCMASK = 0x9
+ RTAX_TAG = 0x8
RTA_AUTHOR = 0x40
RTA_BRD = 0x80
RTA_DST = 0x1
@@ -845,52 +1064,48 @@ const (
RTA_GENMASK = 0x8
RTA_IFA = 0x20
RTA_IFP = 0x10
- RTA_LABEL = 0x400
RTA_NETMASK = 0x4
- RTA_SRC = 0x100
- RTA_SRCMASK = 0x200
- RTF_ANNOUNCE = 0x4000
+ RTA_TAG = 0x100
+ RTF_ANNOUNCE = 0x20000
RTF_BLACKHOLE = 0x1000
- RTF_CLONED = 0x10000
+ RTF_CLONED = 0x2000
RTF_CLONING = 0x100
RTF_DONE = 0x40
RTF_DYNAMIC = 0x10
- RTF_FMASK = 0x8f808
RTF_GATEWAY = 0x2
RTF_HOST = 0x4
- RTF_JUMBO = 0x80000
RTF_LLINFO = 0x400
RTF_MASK = 0x80
RTF_MODIFIED = 0x20
- RTF_MPATH = 0x40000
- RTF_MPLS = 0x100000
- RTF_PERMANENT_ARP = 0x2000
RTF_PROTO1 = 0x8000
RTF_PROTO2 = 0x4000
- RTF_PROTO3 = 0x2000
RTF_REJECT = 0x8
- RTF_SOURCE = 0x20000
+ RTF_SRC = 0x10000
RTF_STATIC = 0x800
- RTF_TUNNEL = 0x100000
RTF_UP = 0x1
- RTF_USETRAILERS = 0x8000
RTF_XRESOLVE = 0x200
RTM_ADD = 0x1
RTM_CHANGE = 0x3
+ RTM_CHGADDR = 0x15
RTM_DELADDR = 0xd
RTM_DELETE = 0x2
- RTM_DESYNC = 0x10
RTM_GET = 0x4
- RTM_IFANNOUNCE = 0xf
- RTM_IFINFO = 0xe
+ RTM_IEEE80211 = 0x11
+ RTM_IFANNOUNCE = 0x10
+ RTM_IFINFO = 0x14
+ RTM_LLINFO_UPD = 0x13
RTM_LOCK = 0x8
RTM_LOSING = 0x5
- RTM_MAXSIZE = 0x800
RTM_MISS = 0x7
RTM_NEWADDR = 0xc
+ RTM_OIFINFO = 0xf
+ RTM_OLDADD = 0x9
+ RTM_OLDDEL = 0xa
+ RTM_OOIFINFO = 0xe
RTM_REDIRECT = 0x6
RTM_RESOLVE = 0xb
RTM_RTTUNIT = 0xf4240
+ RTM_SETGATE = 0x12
RTM_VERSION = 0x4
RTV_EXPIRE = 0x4
RTV_HOPCOUNT = 0x2
@@ -900,159 +1115,82 @@ const (
RTV_RTTVAR = 0x80
RTV_SPIPE = 0x10
RTV_SSTHRESH = 0x20
- RT_TABLEID_MAX = 0xff
- SCM_CREDS = 0x2
+ RUSAGE_CHILDREN = -0x1
+ RUSAGE_SELF = 0x0
+ SCM_CREDS = 0x4
SCM_RIGHTS = 0x1
- SCM_TIMESTAMP = 0x4
+ SCM_TIMESTAMP = 0x8
SHUT_RD = 0x0
SHUT_RDWR = 0x2
SHUT_WR = 0x1
- SIGABRT = 0x6
- SIGALRM = 0xe
- SIGBUS = 0xa
- SIGCHLD = 0x14
- SIGCONT = 0x13
- SIGEMT = 0x7
- SIGFPE = 0x8
- SIGHUP = 0x1
- SIGILL = 0x4
- SIGINFO = 0x1d
- SIGINT = 0x2
- SIGIO = 0x17
- SIGIOT = 0x6
- SIGKILL = 0x9
- SIGPIPE = 0xd
- SIGPROF = 0x1b
- SIGQUIT = 0x3
- SIGSEGV = 0xb
- SIGSTOP = 0x11
- SIGSYS = 0xc
- SIGTERM = 0xf
- SIGTHR = 0x20
- SIGTRAP = 0x5
- SIGTSTP = 0x12
- SIGTTIN = 0x15
- SIGTTOU = 0x16
- SIGURG = 0x10
- SIGUSR1 = 0x1e
- SIGUSR2 = 0x1f
- SIGVTALRM = 0x1a
- SIGWINCH = 0x1c
- SIGXCPU = 0x18
- SIGXFSZ = 0x19
- SIOCADDMULTI = 0x80206931
+ SIOCADDMULTI = 0x80906931
+ SIOCADDRT = 0x8030720a
SIOCAIFADDR = 0x8040691a
- SIOCAIFGROUP = 0x80246987
- SIOCALIFADDR = 0x8218691c
+ SIOCALIFADDR = 0x8118691c
SIOCATMARK = 0x40047307
- SIOCBRDGADD = 0x8054693c
- SIOCBRDGADDS = 0x80546941
- SIOCBRDGARL = 0x806e694d
- SIOCBRDGDADDR = 0x80286947
- SIOCBRDGDEL = 0x8054693d
- SIOCBRDGDELS = 0x80546942
- SIOCBRDGFLUSH = 0x80546948
- SIOCBRDGFRL = 0x806e694e
- SIOCBRDGGCACHE = 0xc0146941
- SIOCBRDGGFD = 0xc0146952
- SIOCBRDGGHT = 0xc0146951
- SIOCBRDGGIFFLGS = 0xc054693e
- SIOCBRDGGMA = 0xc0146953
- SIOCBRDGGPARAM = 0xc0386958
- SIOCBRDGGPRI = 0xc0146950
- SIOCBRDGGRL = 0xc028694f
- SIOCBRDGGSIFS = 0xc054693c
- SIOCBRDGGTO = 0xc0146946
- SIOCBRDGIFS = 0xc0546942
- SIOCBRDGRTS = 0xc0186943
- SIOCBRDGSADDR = 0xc0286944
- SIOCBRDGSCACHE = 0x80146940
- SIOCBRDGSFD = 0x80146952
- SIOCBRDGSHT = 0x80146951
- SIOCBRDGSIFCOST = 0x80546955
- SIOCBRDGSIFFLGS = 0x8054693f
- SIOCBRDGSIFPRIO = 0x80546954
- SIOCBRDGSMA = 0x80146953
- SIOCBRDGSPRI = 0x80146950
- SIOCBRDGSPROTO = 0x8014695a
- SIOCBRDGSTO = 0x80146945
- SIOCBRDGSTXHC = 0x80146959
- SIOCDELMULTI = 0x80206932
- SIOCDIFADDR = 0x80206919
- SIOCDIFGROUP = 0x80246989
- SIOCDIFPHYADDR = 0x80206949
- SIOCDLIFADDR = 0x8218691e
- SIOCGETKALIVE = 0xc01869a4
- SIOCGETLABEL = 0x8020699a
- SIOCGETPFLOW = 0xc02069fe
- SIOCGETPFSYNC = 0xc02069f8
+ SIOCDELMULTI = 0x80906932
+ SIOCDELRT = 0x8030720b
+ SIOCDIFADDR = 0x80906919
+ SIOCDIFPHYADDR = 0x80906949
+ SIOCDLIFADDR = 0x8118691e
+ SIOCGDRVSPEC = 0xc01c697b
+ SIOCGETPFSYNC = 0xc09069f8
SIOCGETSGCNT = 0xc0147534
SIOCGETVIFCNT = 0xc0147533
- SIOCGETVLANPRIO = 0xc0206990
SIOCGHIWAT = 0x40047301
- SIOCGIFADDR = 0xc0206921
- SIOCGIFASYNCMAP = 0xc020697c
- SIOCGIFBRDADDR = 0xc0206923
- SIOCGIFCONF = 0xc0086924
- SIOCGIFDATA = 0xc020691b
- SIOCGIFDESCR = 0xc0206981
- SIOCGIFDSTADDR = 0xc0206922
- SIOCGIFFLAGS = 0xc0206911
- SIOCGIFGATTR = 0xc024698b
- SIOCGIFGENERIC = 0xc020693a
- SIOCGIFGMEMB = 0xc024698a
- SIOCGIFGROUP = 0xc0246988
+ SIOCGIFADDR = 0xc0906921
+ SIOCGIFADDRPREF = 0xc0946920
+ SIOCGIFALIAS = 0xc040691b
+ SIOCGIFBRDADDR = 0xc0906923
+ SIOCGIFCAP = 0xc0206976
+ SIOCGIFCONF = 0xc0086926
+ SIOCGIFDATA = 0xc0946985
+ SIOCGIFDLT = 0xc0906977
+ SIOCGIFDSTADDR = 0xc0906922
+ SIOCGIFFLAGS = 0xc0906911
+ SIOCGIFGENERIC = 0xc090693a
SIOCGIFMEDIA = 0xc0286936
- SIOCGIFMETRIC = 0xc0206917
- SIOCGIFMTU = 0xc020697e
- SIOCGIFNETMASK = 0xc0206925
- SIOCGIFPDSTADDR = 0xc0206948
- SIOCGIFPRIORITY = 0xc020699c
- SIOCGIFPSRCADDR = 0xc0206947
- SIOCGIFRDOMAIN = 0xc02069a0
- SIOCGIFRTLABEL = 0xc0206983
- SIOCGIFTIMESLOT = 0xc0206986
- SIOCGIFXFLAGS = 0xc020699e
- SIOCGLIFADDR = 0xc218691d
- SIOCGLIFPHYADDR = 0xc218694b
- SIOCGLIFPHYRTABLE = 0xc02069a2
+ SIOCGIFMETRIC = 0xc0906917
+ SIOCGIFMTU = 0xc090697e
+ SIOCGIFNETMASK = 0xc0906925
+ SIOCGIFPDSTADDR = 0xc0906948
+ SIOCGIFPSRCADDR = 0xc0906947
+ SIOCGLIFADDR = 0xc118691d
+ SIOCGLIFPHYADDR = 0xc118694b
+ SIOCGLINKSTR = 0xc01c6987
SIOCGLOWAT = 0x40047303
SIOCGPGRP = 0x40047309
- SIOCGVH = 0xc02069f6
- SIOCIFCREATE = 0x8020697a
- SIOCIFDESTROY = 0x80206979
+ SIOCGVH = 0xc0906983
+ SIOCIFCREATE = 0x8090697a
+ SIOCIFDESTROY = 0x80906979
SIOCIFGCLONERS = 0xc00c6978
- SIOCSETKALIVE = 0x801869a3
- SIOCSETLABEL = 0x80206999
- SIOCSETPFLOW = 0x802069fd
- SIOCSETPFSYNC = 0x802069f7
- SIOCSETVLANPRIO = 0x8020698f
+ SIOCINITIFADDR = 0xc0446984
+ SIOCSDRVSPEC = 0x801c697b
+ SIOCSETPFSYNC = 0x809069f7
SIOCSHIWAT = 0x80047300
- SIOCSIFADDR = 0x8020690c
- SIOCSIFASYNCMAP = 0x8020697d
- SIOCSIFBRDADDR = 0x80206913
- SIOCSIFDESCR = 0x80206980
- SIOCSIFDSTADDR = 0x8020690e
- SIOCSIFFLAGS = 0x80206910
- SIOCSIFGATTR = 0x8024698c
- SIOCSIFGENERIC = 0x80206939
- SIOCSIFLLADDR = 0x8020691f
- SIOCSIFMEDIA = 0xc0206935
- SIOCSIFMETRIC = 0x80206918
- SIOCSIFMTU = 0x8020697f
- SIOCSIFNETMASK = 0x80206916
+ SIOCSIFADDR = 0x8090690c
+ SIOCSIFADDRPREF = 0x8094691f
+ SIOCSIFBRDADDR = 0x80906913
+ SIOCSIFCAP = 0x80206975
+ SIOCSIFDSTADDR = 0x8090690e
+ SIOCSIFFLAGS = 0x80906910
+ SIOCSIFGENERIC = 0x80906939
+ SIOCSIFMEDIA = 0xc0906935
+ SIOCSIFMETRIC = 0x80906918
+ SIOCSIFMTU = 0x8090697f
+ SIOCSIFNETMASK = 0x80906916
SIOCSIFPHYADDR = 0x80406946
- SIOCSIFPRIORITY = 0x8020699b
- SIOCSIFRDOMAIN = 0x8020699f
- SIOCSIFRTLABEL = 0x80206982
- SIOCSIFTIMESLOT = 0x80206985
- SIOCSIFXFLAGS = 0x8020699d
- SIOCSLIFPHYADDR = 0x8218694a
- SIOCSLIFPHYRTABLE = 0x802069a1
+ SIOCSLIFPHYADDR = 0x8118694a
+ SIOCSLINKSTR = 0x801c6988
SIOCSLOWAT = 0x80047302
SIOCSPGRP = 0x80047308
- SIOCSVH = 0xc02069f5
+ SIOCSVH = 0xc0906982
+ SIOCZIFDATA = 0xc0946986
+ SOCK_CLOEXEC = 0x10000000
SOCK_DGRAM = 0x2
+ SOCK_FLAGS_MASK = 0xf0000000
+ SOCK_NONBLOCK = 0x20000000
+ SOCK_NOSIGPIPE = 0x40000000
SOCK_RAW = 0x3
SOCK_RDM = 0x4
SOCK_SEQPACKET = 0x5
@@ -1060,63 +1198,107 @@ const (
SOL_SOCKET = 0xffff
SOMAXCONN = 0x80
SO_ACCEPTCONN = 0x2
- SO_BINDANY = 0x1000
+ SO_ACCEPTFILTER = 0x1000
SO_BROADCAST = 0x20
SO_DEBUG = 0x1
SO_DONTROUTE = 0x10
SO_ERROR = 0x1007
- SO_JUMBO = 0x400
SO_KEEPALIVE = 0x8
SO_LINGER = 0x80
- SO_NETPROC = 0x1020
+ SO_NOHEADER = 0x100a
+ SO_NOSIGPIPE = 0x800
SO_OOBINLINE = 0x100
- SO_PEERCRED = 0x1022
+ SO_OVERFLOWED = 0x1009
SO_RCVBUF = 0x1002
SO_RCVLOWAT = 0x1004
- SO_RCVTIMEO = 0x1006
+ SO_RCVTIMEO = 0x100c
SO_REUSEADDR = 0x4
SO_REUSEPORT = 0x200
- SO_RTABLE = 0x1021
SO_SNDBUF = 0x1001
SO_SNDLOWAT = 0x1003
- SO_SNDTIMEO = 0x1005
- SO_SPLICE = 0x1023
- SO_TIMESTAMP = 0x800
+ SO_SNDTIMEO = 0x100b
+ SO_TIMESTAMP = 0x2000
SO_TYPE = 0x1008
SO_USELOOPBACK = 0x40
+ SYSCTL_VERSION = 0x1000000
+ SYSCTL_VERS_0 = 0x0
+ SYSCTL_VERS_1 = 0x1000000
+ SYSCTL_VERS_MASK = 0xff000000
+ S_ARCH1 = 0x10000
+ S_ARCH2 = 0x20000
+ S_BLKSIZE = 0x200
+ S_IEXEC = 0x40
+ S_IFBLK = 0x6000
+ S_IFCHR = 0x2000
+ S_IFDIR = 0x4000
+ S_IFIFO = 0x1000
+ S_IFLNK = 0xa000
+ S_IFMT = 0xf000
+ S_IFREG = 0x8000
+ S_IFSOCK = 0xc000
+ S_IFWHT = 0xe000
+ S_IREAD = 0x100
+ S_IRGRP = 0x20
+ S_IROTH = 0x4
+ S_IRUSR = 0x100
+ S_IRWXG = 0x38
+ S_IRWXO = 0x7
+ S_IRWXU = 0x1c0
+ S_ISGID = 0x400
+ S_ISTXT = 0x200
+ S_ISUID = 0x800
+ S_ISVTX = 0x200
+ S_IWGRP = 0x10
+ S_IWOTH = 0x2
+ S_IWRITE = 0x80
+ S_IWUSR = 0x80
+ S_IXGRP = 0x8
+ S_IXOTH = 0x1
+ S_IXUSR = 0x40
+ S_LOGIN_SET = 0x1
+ TCIFLUSH = 0x1
+ TCIOFLUSH = 0x3
+ TCOFLUSH = 0x2
+ TCP_CONGCTL = 0x20
+ TCP_KEEPCNT = 0x6
+ TCP_KEEPIDLE = 0x3
+ TCP_KEEPINIT = 0x7
+ TCP_KEEPINTVL = 0x5
TCP_MAXBURST = 0x4
TCP_MAXSEG = 0x2
TCP_MAXWIN = 0xffff
- TCP_MAX_SACK = 0x3
TCP_MAX_WINSHIFT = 0xe
- TCP_MD5SIG = 0x4
- TCP_MSS = 0x200
+ TCP_MD5SIG = 0x10
+ TCP_MINMSS = 0xd8
+ TCP_MSS = 0x218
TCP_NODELAY = 0x1
- TCP_NSTATES = 0xb
- TCP_SACK_ENABLE = 0x8
+ TCSAFLUSH = 0x2
TIOCCBRK = 0x2000747a
TIOCCDTR = 0x20007478
TIOCCONS = 0x80047462
+ TIOCDCDTIMESTAMP = 0x400c7458
TIOCDRAIN = 0x2000745e
TIOCEXCL = 0x2000740d
TIOCEXT = 0x80047460
+ TIOCFLAG_CDTRCTS = 0x10
TIOCFLAG_CLOCAL = 0x2
TIOCFLAG_CRTSCTS = 0x4
TIOCFLAG_MDMBUF = 0x8
- TIOCFLAG_PPS = 0x10
TIOCFLAG_SOFTCAR = 0x1
TIOCFLUSH = 0x80047410
TIOCGETA = 0x402c7413
TIOCGETD = 0x4004741a
TIOCGFLAGS = 0x4004745d
+ TIOCGLINED = 0x40207442
TIOCGPGRP = 0x40047477
- TIOCGTSTAMP = 0x4008745b
+ TIOCGQSIZE = 0x40047481
+ TIOCGRANTPT = 0x20007447
+ TIOCGSID = 0x40047463
+ TIOCGSIZE = 0x40087468
TIOCGWINSZ = 0x40087468
TIOCMBIC = 0x8004746b
TIOCMBIS = 0x8004746c
TIOCMGET = 0x4004746a
- TIOCMODG = 0x4004746a
- TIOCMODS = 0x8004746d
TIOCMSET = 0x8004746d
TIOCM_CAR = 0x40
TIOCM_CD = 0x40
@@ -1141,6 +1323,9 @@ const (
TIOCPKT_NOSTOP = 0x10
TIOCPKT_START = 0x8
TIOCPKT_STOP = 0x4
+ TIOCPTMGET = 0x40287446
+ TIOCPTSNAME = 0x40287448
+ TIOCRCVFRAME = 0x80047445
TIOCREMOTE = 0x80047469
TIOCSBRK = 0x2000747b
TIOCSCTTY = 0x20007461
@@ -1150,19 +1335,46 @@ const (
TIOCSETAW = 0x802c7415
TIOCSETD = 0x8004741b
TIOCSFLAGS = 0x8004745c
- TIOCSIG = 0x8004745f
+ TIOCSIG = 0x2000745f
+ TIOCSLINED = 0x80207443
TIOCSPGRP = 0x80047476
+ TIOCSQSIZE = 0x80047480
+ TIOCSSIZE = 0x80087467
TIOCSTART = 0x2000746e
TIOCSTAT = 0x80047465
TIOCSTI = 0x80017472
TIOCSTOP = 0x2000746f
- TIOCSTSTAMP = 0x8008745a
TIOCSWINSZ = 0x80087467
TIOCUCNTL = 0x80047466
+ TIOCXMTFRAME = 0x80047444
+ TOSTOP = 0x400000
+ VDISCARD = 0xf
+ VDSUSP = 0xb
+ VEOF = 0x0
+ VEOL = 0x1
+ VEOL2 = 0x2
+ VERASE = 0x3
+ VINTR = 0x8
+ VKILL = 0x5
+ VLNEXT = 0xe
+ VMIN = 0x10
+ VQUIT = 0x9
+ VREPRINT = 0x6
+ VSTART = 0xc
+ VSTATUS = 0x12
+ VSTOP = 0xd
+ VSUSP = 0xa
+ VTIME = 0x11
+ VWERASE = 0x4
+ WALL = 0x8
+ WALLSIG = 0x8
WALTSIG = 0x4
- WCONTINUED = 0x8
+ WCLONE = 0x4
WCOREFLAG = 0x80
WNOHANG = 0x1
+ WNOWAIT = 0x10000
+ WNOZOMBIE = 0x20000
+ WOPTSCHECKED = 0x40000
WSTOPPED = 0x7f
WUNTRACED = 0x2
)
@@ -1178,17 +1390,11 @@ const (
EALREADY = Errno(0x25)
EAUTH = Errno(0x50)
EBADF = Errno(0x9)
+ EBADMSG = Errno(0x58)
EBADRPC = Errno(0x48)
EBUSY = Errno(0x10)
- ECANCELED = Errno(0x58)
+ ECANCELED = Errno(0x57)
ECHILD = Errno(0xa)
- ECHO = Errno(0x8)
- ECHOCTL = Errno(0x40)
- ECHOE = Errno(0x2)
- ECHOK = Errno(0x4)
- ECHOKE = Errno(0x1)
- ECHONL = Errno(0x10)
- ECHOPRT = Errno(0x20)
ECONNABORTED = Errno(0x35)
ECONNREFUSED = Errno(0x3d)
ECONNRESET = Errno(0x36)
@@ -1202,50 +1408,51 @@ const (
EFTYPE = Errno(0x4f)
EHOSTDOWN = Errno(0x40)
EHOSTUNREACH = Errno(0x41)
- EIDRM = Errno(0x59)
- EILSEQ = Errno(0x54)
+ EIDRM = Errno(0x52)
+ EILSEQ = Errno(0x55)
EINPROGRESS = Errno(0x24)
EINTR = Errno(0x4)
EINVAL = Errno(0x16)
EIO = Errno(0x5)
- EIPSEC = Errno(0x52)
EISCONN = Errno(0x38)
EISDIR = Errno(0x15)
- ELAST = Errno(0x5b)
+ ELAST = Errno(0x60)
ELOOP = Errno(0x3e)
- EMEDIUMTYPE = Errno(0x56)
EMFILE = Errno(0x18)
EMLINK = Errno(0x1f)
EMSGSIZE = Errno(0x28)
+ EMULTIHOP = Errno(0x5e)
ENAMETOOLONG = Errno(0x3f)
- ENDRUNDISC = Errno(0x9)
ENEEDAUTH = Errno(0x51)
ENETDOWN = Errno(0x32)
ENETRESET = Errno(0x34)
ENETUNREACH = Errno(0x33)
ENFILE = Errno(0x17)
- ENOATTR = Errno(0x53)
+ ENOATTR = Errno(0x5d)
ENOBUFS = Errno(0x37)
+ ENODATA = Errno(0x59)
ENODEV = Errno(0x13)
ENOENT = Errno(0x2)
ENOEXEC = Errno(0x8)
ENOLCK = Errno(0x4d)
- ENOMEDIUM = Errno(0x55)
+ ENOLINK = Errno(0x5f)
ENOMEM = Errno(0xc)
- ENOMSG = Errno(0x5a)
+ ENOMSG = Errno(0x53)
ENOPROTOOPT = Errno(0x2a)
ENOSPC = Errno(0x1c)
+ ENOSR = Errno(0x5a)
+ ENOSTR = Errno(0x5b)
ENOSYS = Errno(0x4e)
ENOTBLK = Errno(0xf)
ENOTCONN = Errno(0x39)
ENOTDIR = Errno(0x14)
ENOTEMPTY = Errno(0x42)
ENOTSOCK = Errno(0x26)
- ENOTSUP = Errno(0x5b)
+ ENOTSUP = Errno(0x56)
ENOTTY = Errno(0x19)
ENXIO = Errno(0x6)
EOPNOTSUPP = Errno(0x2d)
- EOVERFLOW = Errno(0x57)
+ EOVERFLOW = Errno(0x54)
EPERM = Errno(0x1)
EPFNOSUPPORT = Errno(0x2e)
EPIPE = Errno(0x20)
@@ -1253,6 +1460,7 @@ const (
EPROCUNAVAIL = Errno(0x4c)
EPROGMISMATCH = Errno(0x4b)
EPROGUNAVAIL = Errno(0x4a)
+ EPROTO = Errno(0x60)
EPROTONOSUPPORT = Errno(0x2b)
EPROTOTYPE = Errno(0x29)
ERANGE = Errno(0x22)
@@ -1264,6 +1472,7 @@ const (
ESPIPE = Errno(0x1d)
ESRCH = Errno(0x3)
ESTALE = Errno(0x46)
+ ETIME = Errno(0x5c)
ETIMEDOUT = Errno(0x3c)
ETOOMANYREFS = Errno(0x3b)
ETXTBSY = Errno(0x1a)
@@ -1272,6 +1481,43 @@ const (
EXDEV = Errno(0x12)
)
+// Signals
+const (
+ SIGABRT = Signal(0x6)
+ SIGALRM = Signal(0xe)
+ SIGBUS = Signal(0xa)
+ SIGCHLD = Signal(0x14)
+ SIGCONT = Signal(0x13)
+ SIGEMT = Signal(0x7)
+ SIGFPE = Signal(0x8)
+ SIGHUP = Signal(0x1)
+ SIGILL = Signal(0x4)
+ SIGINFO = Signal(0x1d)
+ SIGINT = Signal(0x2)
+ SIGIO = Signal(0x17)
+ SIGIOT = Signal(0x6)
+ SIGKILL = Signal(0x9)
+ SIGPIPE = Signal(0xd)
+ SIGPROF = Signal(0x1b)
+ SIGPWR = Signal(0x20)
+ SIGQUIT = Signal(0x3)
+ SIGSEGV = Signal(0xb)
+ SIGSTOP = Signal(0x11)
+ SIGSYS = Signal(0xc)
+ SIGTERM = Signal(0xf)
+ SIGTRAP = Signal(0x5)
+ SIGTSTP = Signal(0x12)
+ SIGTTIN = Signal(0x15)
+ SIGTTOU = Signal(0x16)
+ SIGURG = Signal(0x10)
+ SIGUSR1 = Signal(0x1e)
+ SIGUSR2 = Signal(0x1f)
+ SIGVTALRM = Signal(0x1a)
+ SIGWINCH = Signal(0x1c)
+ SIGXCPU = Signal(0x18)
+ SIGXFSZ = Signal(0x19)
+)
+
// Error table
var errors = [...]string{
1: "operation not permitted",
@@ -1307,7 +1553,7 @@ var errors = [...]string{
31: "too many links",
32: "broken pipe",
33: "numerical argument out of domain",
- 34: "result too large",
+ 34: "result too large or too small",
35: "resource temporarily unavailable",
36: "operation now in progress",
37: "operation already in progress",
@@ -1315,7 +1561,7 @@ var errors = [...]string{
39: "destination address required",
40: "message too long",
41: "protocol wrong type for socket",
- 42: "protocol not available",
+ 42: "protocol option not available",
43: "protocol not supported",
44: "socket type not supported",
45: "operation not supported",
@@ -1355,14 +1601,55 @@ var errors = [...]string{
79: "inappropriate file type or format",
80: "authentication error",
81: "need authenticator",
- 82: "IPsec processing failure",
- 83: "attribute not found",
- 84: "illegal byte sequence",
- 85: "no medium found",
- 86: "wrong medium type",
- 87: "value too large to be stored in data type",
- 88: "operation canceled",
- 89: "identifier removed",
- 90: "no message of desired type",
- 91: "not supported",
+ 82: "identifier removed",
+ 83: "no message of desired type",
+ 84: "value too large to be stored in data type",
+ 85: "illegal byte sequence",
+ 86: "not supported",
+ 87: "operation Canceled",
+ 88: "bad or Corrupt message",
+ 89: "no message available",
+ 90: "no STREAM resources",
+ 91: "not a STREAM",
+ 92: "STREAM ioctl timeout",
+ 93: "attribute not found",
+ 94: "multihop attempted",
+ 95: "link has been severed",
+ 96: "protocol error",
+}
+
+// Signal table
+var signals = [...]string{
+ 1: "hangup",
+ 2: "interrupt",
+ 3: "quit",
+ 4: "illegal instruction",
+ 5: "trace/BPT trap",
+ 6: "abort trap",
+ 7: "EMT trap",
+ 8: "floating point exception",
+ 9: "killed",
+ 10: "bus error",
+ 11: "segmentation fault",
+ 12: "bad system call",
+ 13: "broken pipe",
+ 14: "alarm clock",
+ 15: "terminated",
+ 16: "urgent I/O condition",
+ 17: "stopped (signal)",
+ 18: "stopped",
+ 19: "continued",
+ 20: "child exited",
+ 21: "stopped (tty input)",
+ 22: "stopped (tty output)",
+ 23: "I/O possible",
+ 24: "cputime limit exceeded",
+ 25: "filesize limit exceeded",
+ 26: "virtual timer expired",
+ 27: "profiling timer expired",
+ 28: "window size changes",
+ 29: "information request",
+ 30: "user defined signal 1",
+ 31: "user defined signal 2",
+ 32: "power fail/restart",
}
diff --git a/src/pkg/syscall/zerrors_netbsd_amd64.go b/src/pkg/syscall/zerrors_netbsd_amd64.go
index c6367fbbc..f719528a7 100644
--- a/src/pkg/syscall/zerrors_netbsd_amd64.go
+++ b/src/pkg/syscall/zerrors_netbsd_amd64.go
@@ -8,7 +8,8 @@ package syscall
const (
AF_APPLETALK = 0x10
- AF_BLUETOOTH = 0x20
+ AF_ARP = 0x1c
+ AF_BLUETOOTH = 0x1f
AF_CCITT = 0xa
AF_CHAOS = 0x5
AF_CNT = 0x15
@@ -18,67 +19,93 @@ const (
AF_DLI = 0xd
AF_E164 = 0x1a
AF_ECMA = 0x8
- AF_ENCAP = 0x1c
AF_HYLINK = 0xf
+ AF_IEEE80211 = 0x20
AF_IMPLINK = 0x3
AF_INET = 0x2
AF_INET6 = 0x18
AF_IPX = 0x17
AF_ISDN = 0x1a
AF_ISO = 0x7
- AF_KEY = 0x1e
AF_LAT = 0xe
AF_LINK = 0x12
AF_LOCAL = 0x1
- AF_MAX = 0x24
+ AF_MAX = 0x23
AF_MPLS = 0x21
AF_NATM = 0x1b
AF_NS = 0x6
+ AF_OROUTE = 0x11
AF_OSI = 0x7
AF_PUP = 0x4
- AF_ROUTE = 0x11
- AF_SIP = 0x1d
+ AF_ROUTE = 0x22
AF_SNA = 0xb
AF_UNIX = 0x1
AF_UNSPEC = 0x0
+ ARPHRD_ARCNET = 0x7
ARPHRD_ETHER = 0x1
ARPHRD_FRELAY = 0xf
ARPHRD_IEEE1394 = 0x18
ARPHRD_IEEE802 = 0x6
+ ARPHRD_STRIP = 0x17
+ B0 = 0x0
+ B110 = 0x6e
+ B115200 = 0x1c200
+ B1200 = 0x4b0
+ B134 = 0x86
+ B14400 = 0x3840
+ B150 = 0x96
+ B1800 = 0x708
+ B19200 = 0x4b00
+ B200 = 0xc8
+ B230400 = 0x38400
+ B2400 = 0x960
+ B28800 = 0x7080
+ B300 = 0x12c
+ B38400 = 0x9600
+ B460800 = 0x70800
+ B4800 = 0x12c0
+ B50 = 0x32
+ B57600 = 0xe100
+ B600 = 0x258
+ B7200 = 0x1c20
+ B75 = 0x4b
+ B76800 = 0x12c00
+ B921600 = 0xe1000
+ B9600 = 0x2580
+ BIOCFEEDBACK = 0x8004427d
BIOCFLUSH = 0x20004268
BIOCGBLEN = 0x40044266
- BIOCGDIRFILT = 0x4004427c
BIOCGDLT = 0x4004426a
- BIOCGDLTLIST = 0xc010427b
- BIOCGETIF = 0x4020426b
- BIOCGFILDROP = 0x40044278
+ BIOCGDLTLIST = 0xc0104277
+ BIOCGETIF = 0x4090426b
+ BIOCGFEEDBACK = 0x4004427c
BIOCGHDRCMPLT = 0x40044274
- BIOCGRSIG = 0x40044273
- BIOCGRTIMEOUT = 0x4010426e
- BIOCGSTATS = 0x4008426f
+ BIOCGRTIMEOUT = 0x4010427b
+ BIOCGSEESENT = 0x40044278
+ BIOCGSTATS = 0x4080426f
+ BIOCGSTATSOLD = 0x4008426f
BIOCIMMEDIATE = 0x80044270
- BIOCLOCK = 0x20004276
BIOCPROMISC = 0x20004269
BIOCSBLEN = 0xc0044266
- BIOCSDIRFILT = 0x8004427d
- BIOCSDLT = 0x8004427a
+ BIOCSDLT = 0x80044276
BIOCSETF = 0x80104267
- BIOCSETIF = 0x8020426c
- BIOCSETWF = 0x80104277
- BIOCSFILDROP = 0x80044279
+ BIOCSETIF = 0x8090426c
+ BIOCSFEEDBACK = 0x8004427d
BIOCSHDRCMPLT = 0x80044275
- BIOCSRSIG = 0x80044272
- BIOCSRTIMEOUT = 0x8010426d
+ BIOCSRTIMEOUT = 0x8010427a
+ BIOCSSEESENT = 0x80044279
+ BIOCSTCPF = 0x80104272
+ BIOCSUDPF = 0x80104273
BIOCVERSION = 0x40044271
BPF_A = 0x10
BPF_ABS = 0x20
BPF_ADD = 0x0
- BPF_ALIGNMENT = 0x4
+ BPF_ALIGNMENT = 0x8
+ BPF_ALIGNMENT32 = 0x4
BPF_ALU = 0x4
BPF_AND = 0x50
BPF_B = 0x10
- BPF_DIRECTION_IN = 0x1
- BPF_DIRECTION_OUT = 0x2
+ BPF_DFLTBUFSIZE = 0x100000
BPF_DIV = 0x30
BPF_H = 0x8
BPF_IMM = 0x0
@@ -95,7 +122,7 @@ const (
BPF_LEN = 0x80
BPF_LSH = 0x60
BPF_MAJOR_VERSION = 0x1
- BPF_MAXBUFSIZE = 0x200000
+ BPF_MAXBUFSIZE = 0x1000000
BPF_MAXINSNS = 0x200
BPF_MEM = 0x60
BPF_MEMWORDS = 0x10
@@ -116,31 +143,151 @@ const (
BPF_TXA = 0x80
BPF_W = 0x0
BPF_X = 0x8
+ BRKINT = 0x2
+ CFLUSH = 0xf
+ CLOCAL = 0x8000
+ CREAD = 0x800
+ CS5 = 0x0
+ CS6 = 0x100
+ CS7 = 0x200
+ CS8 = 0x300
+ CSIZE = 0x300
+ CSTART = 0x11
+ CSTATUS = 0x14
+ CSTOP = 0x13
+ CSTOPB = 0x400
+ CSUSP = 0x1a
CTL_MAXNAME = 0xc
CTL_NET = 0x4
+ CTL_QUERY = -0x2
+ DIOCBSFLUSH = 0x20006478
+ DLT_A429 = 0xb8
+ DLT_A653_ICM = 0xb9
+ DLT_AIRONET_HEADER = 0x78
+ DLT_AOS = 0xde
+ DLT_APPLE_IP_OVER_IEEE1394 = 0x8a
DLT_ARCNET = 0x7
+ DLT_ARCNET_LINUX = 0x81
+ DLT_ATM_CLIP = 0x13
DLT_ATM_RFC1483 = 0xb
+ DLT_AURORA = 0x7e
DLT_AX25 = 0x3
+ DLT_AX25_KISS = 0xca
+ DLT_BACNET_MS_TP = 0xa5
+ DLT_BLUETOOTH_HCI_H4 = 0xbb
+ DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 0xc9
+ DLT_CAN20B = 0xbe
+ DLT_CAN_SOCKETCAN = 0xe3
DLT_CHAOS = 0x5
+ DLT_CISCO_IOS = 0x76
+ DLT_C_HDLC = 0x68
+ DLT_C_HDLC_WITH_DIR = 0xcd
+ DLT_DECT = 0xdd
+ DLT_DOCSIS = 0x8f
+ DLT_ECONET = 0x73
DLT_EN10MB = 0x1
DLT_EN3MB = 0x2
- DLT_ENC = 0xd
+ DLT_ENC = 0x6d
+ DLT_ERF = 0xc5
+ DLT_ERF_ETH = 0xaf
+ DLT_ERF_POS = 0xb0
+ DLT_FC_2 = 0xe0
+ DLT_FC_2_WITH_FRAME_DELIMS = 0xe1
DLT_FDDI = 0xa
+ DLT_FLEXRAY = 0xd2
+ DLT_FRELAY = 0x6b
+ DLT_FRELAY_WITH_DIR = 0xce
+ DLT_GCOM_SERIAL = 0xad
+ DLT_GCOM_T1E1 = 0xac
+ DLT_GPF_F = 0xab
+ DLT_GPF_T = 0xaa
+ DLT_GPRS_LLC = 0xa9
+ DLT_GSMTAP_ABIS = 0xda
+ DLT_GSMTAP_UM = 0xd9
+ DLT_HDLC = 0x10
+ DLT_HHDLC = 0x79
+ DLT_HIPPI = 0xf
+ DLT_IBM_SN = 0x92
+ DLT_IBM_SP = 0x91
DLT_IEEE802 = 0x6
DLT_IEEE802_11 = 0x69
DLT_IEEE802_11_RADIO = 0x7f
- DLT_LOOP = 0xc
+ DLT_IEEE802_11_RADIO_AVS = 0xa3
+ DLT_IEEE802_15_4 = 0xc3
+ DLT_IEEE802_15_4_LINUX = 0xbf
+ DLT_IEEE802_15_4_NONASK_PHY = 0xd7
+ DLT_IEEE802_16_MAC_CPS = 0xbc
+ DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1
+ DLT_IPMB = 0xc7
+ DLT_IPMB_LINUX = 0xd1
+ DLT_IPNET = 0xe2
+ DLT_IPV4 = 0xe4
+ DLT_IPV6 = 0xe5
+ DLT_IP_OVER_FC = 0x7a
+ DLT_JUNIPER_ATM1 = 0x89
+ DLT_JUNIPER_ATM2 = 0x87
+ DLT_JUNIPER_CHDLC = 0xb5
+ DLT_JUNIPER_ES = 0x84
+ DLT_JUNIPER_ETHER = 0xb2
+ DLT_JUNIPER_FRELAY = 0xb4
+ DLT_JUNIPER_GGSN = 0x85
+ DLT_JUNIPER_ISM = 0xc2
+ DLT_JUNIPER_MFR = 0x86
+ DLT_JUNIPER_MLFR = 0x83
+ DLT_JUNIPER_MLPPP = 0x82
+ DLT_JUNIPER_MONITOR = 0xa4
+ DLT_JUNIPER_PIC_PEER = 0xae
+ DLT_JUNIPER_PPP = 0xb3
+ DLT_JUNIPER_PPPOE = 0xa7
+ DLT_JUNIPER_PPPOE_ATM = 0xa8
+ DLT_JUNIPER_SERVICES = 0x88
+ DLT_JUNIPER_ST = 0xc8
+ DLT_JUNIPER_VP = 0xb7
+ DLT_LAPB_WITH_DIR = 0xcf
+ DLT_LAPD = 0xcb
+ DLT_LIN = 0xd4
+ DLT_LINUX_EVDEV = 0xd8
+ DLT_LINUX_IRDA = 0x90
+ DLT_LINUX_LAPD = 0xb1
+ DLT_LINUX_SLL = 0x71
+ DLT_LOOP = 0x6c
+ DLT_LTALK = 0x72
+ DLT_MFR = 0xb6
+ DLT_MOST = 0xd3
DLT_MPLS = 0xdb
+ DLT_MTP2 = 0x8c
+ DLT_MTP2_WITH_PHDR = 0x8b
+ DLT_MTP3 = 0x8d
DLT_NULL = 0x0
+ DLT_PCI_EXP = 0x7d
DLT_PFLOG = 0x75
DLT_PFSYNC = 0x12
+ DLT_PPI = 0xc0
DLT_PPP = 0x9
- DLT_PPP_BSDOS = 0x10
+ DLT_PPP_BSDOS = 0xe
DLT_PPP_ETHER = 0x33
+ DLT_PPP_PPPD = 0xa6
+ DLT_PPP_SERIAL = 0x32
+ DLT_PPP_WITH_DIR = 0xcc
+ DLT_PRISM_HEADER = 0x77
DLT_PRONET = 0x4
- DLT_RAW = 0xe
+ DLT_RAIF1 = 0xc6
+ DLT_RAW = 0xc
+ DLT_RAWAF_MASK = 0x2240000
+ DLT_RIO = 0x7c
+ DLT_SCCP = 0x8e
+ DLT_SITA = 0xc4
DLT_SLIP = 0x8
- DLT_SLIP_BSDOS = 0xf
+ DLT_SLIP_BSDOS = 0xd
+ DLT_SUNATM = 0x7b
+ DLT_SYMANTEC_FIREWALL = 0x63
+ DLT_TZSP = 0x80
+ DLT_USB = 0xba
+ DLT_USB_LINUX = 0xbd
+ DLT_USB_LINUX_MMAPPED = 0xdc
+ DLT_WIHART = 0xdf
+ DLT_X2E_SERIAL = 0xd5
+ DLT_X2E_XORAYA = 0xd6
DT_BLK = 0x6
DT_CHR = 0x2
DT_DIR = 0x4
@@ -149,15 +296,23 @@ const (
DT_REG = 0x8
DT_SOCK = 0xc
DT_UNKNOWN = 0x0
- EFER_LMA = 0x400
- EFER_LME = 0x100
- EFER_NXE = 0x800
- EFER_SCE = 0x1
- EMT_TAGOVF = 0x1
- EMUL_ENABLED = 0x1
- EMUL_NATIVE = 0x2
+ DT_WHT = 0xe
+ ECHO = 0x8
+ ECHOCTL = 0x40
+ ECHOE = 0x2
+ ECHOK = 0x4
+ ECHOKE = 0x1
+ ECHONL = 0x10
+ ECHOPRT = 0x20
+ EMUL_LINUX = 0x1
+ EMUL_LINUX32 = 0x5
+ EMUL_MAXID = 0x6
+ ETHERCAP_JUMBO_MTU = 0x4
+ ETHERCAP_VLAN_HWTAGGING = 0x2
+ ETHERCAP_VLAN_MTU = 0x1
ETHERMIN = 0x2e
ETHERMTU = 0x5dc
+ ETHERMTU_JUMBO = 0x2328
ETHERTYPE_8023 = 0x4
ETHERTYPE_AARP = 0x80f3
ETHERTYPE_ACCTON = 0x8390
@@ -165,7 +320,6 @@ const (
ETHERTYPE_ALPHA = 0x814a
ETHERTYPE_AMBER = 0x6008
ETHERTYPE_AMOEBA = 0x8145
- ETHERTYPE_AOE = 0x88a2
ETHERTYPE_APOLLO = 0x80f7
ETHERTYPE_APOLLODOMAIN = 0x8019
ETHERTYPE_APPLETALK = 0x809b
@@ -236,7 +390,6 @@ const (
ETHERTYPE_LAT = 0x6004
ETHERTYPE_LBACK = 0x9000
ETHERTYPE_LITTLE = 0x8060
- ETHERTYPE_LLDP = 0x88cc
ETHERTYPE_LOGICRAFT = 0x8148
ETHERTYPE_LOOPBACK = 0x9000
ETHERTYPE_MATRA = 0x807a
@@ -284,7 +437,6 @@ const (
ETHERTYPE_PRIMENTS = 0x7031
ETHERTYPE_PUP = 0x200
ETHERTYPE_PUPAT = 0x200
- ETHERTYPE_QINQ = 0x88a8
ETHERTYPE_RACAL = 0x7030
ETHERTYPE_RATIONAL = 0x8150
ETHERTYPE_RAWFR = 0x6559
@@ -301,7 +453,7 @@ const (
ETHERTYPE_SG_NETGAMES = 0x8014
ETHERTYPE_SG_RESV = 0x8015
ETHERTYPE_SIMNET = 0x5208
- ETHERTYPE_SLOW = 0x8809
+ ETHERTYPE_SLOWPROTOCOLS = 0x8809
ETHERTYPE_SNA = 0x80d5
ETHERTYPE_SNMP = 0x814c
ETHERTYPE_SONIX = 0xfaf5
@@ -344,24 +496,24 @@ const (
ETHERTYPE_XNSSM = 0x9001
ETHERTYPE_XTP = 0x817d
ETHER_ADDR_LEN = 0x6
- ETHER_ALIGN = 0x2
ETHER_CRC_LEN = 0x4
ETHER_CRC_POLY_BE = 0x4c11db6
ETHER_CRC_POLY_LE = 0xedb88320
ETHER_HDR_LEN = 0xe
- ETHER_MAX_DIX_LEN = 0x600
ETHER_MAX_LEN = 0x5ee
+ ETHER_MAX_LEN_JUMBO = 0x233a
ETHER_MIN_LEN = 0x40
+ ETHER_PPPOE_ENCAP_LEN = 0x8
ETHER_TYPE_LEN = 0x2
ETHER_VLAN_ENCAP_LEN = 0x4
- EVFILT_AIO = -0x3
- EVFILT_PROC = -0x5
- EVFILT_READ = -0x1
- EVFILT_SIGNAL = -0x6
+ EVFILT_AIO = 0x2
+ EVFILT_PROC = 0x4
+ EVFILT_READ = 0x0
+ EVFILT_SIGNAL = 0x5
EVFILT_SYSCOUNT = 0x7
- EVFILT_TIMER = -0x7
- EVFILT_VNODE = -0x4
- EVFILT_WRITE = -0x2
+ EVFILT_TIMER = 0x6
+ EVFILT_VNODE = 0x3
+ EVFILT_WRITE = 0x1
EV_ADD = 0x1
EV_CLEAR = 0x20
EV_DELETE = 0x2
@@ -376,25 +528,46 @@ const (
EXTB = 0x9600
EXTPROC = 0x800
FD_CLOEXEC = 0x1
- FD_SETSIZE = 0x400
+ FD_SETSIZE = 0x100
+ FLUSHO = 0x800000
+ F_CLOSEM = 0xa
F_DUPFD = 0x0
- F_DUPFD_CLOEXEC = 0xa
+ F_DUPFD_CLOEXEC = 0xc
+ F_FSCTL = -0x80000000
+ F_FSDIRMASK = 0x70000000
+ F_FSIN = 0x10000000
+ F_FSINOUT = 0x30000000
+ F_FSOUT = 0x20000000
+ F_FSPRIV = 0x8000
+ F_FSVOID = 0x40000000
F_GETFD = 0x1
F_GETFL = 0x3
F_GETLK = 0x7
+ F_GETNOSIGPIPE = 0xd
F_GETOWN = 0x5
+ F_MAXFD = 0xb
+ F_OK = 0x0
+ F_PARAM_MASK = 0xfff
+ F_PARAM_MAX = 0xfff
F_RDLCK = 0x1
F_SETFD = 0x2
F_SETFL = 0x4
F_SETLK = 0x8
F_SETLKW = 0x9
+ F_SETNOSIGPIPE = 0xe
F_SETOWN = 0x6
F_UNLCK = 0x2
F_WRLCK = 0x3
+ HUPCL = 0x4000
+ ICANON = 0x100
+ ICRNL = 0x100
+ IEXTEN = 0x400
+ IFAN_ARRIVAL = 0x0
+ IFAN_DEPARTURE = 0x1
IFA_ROUTE = 0x1
IFF_ALLMULTI = 0x200
IFF_BROADCAST = 0x2
- IFF_CANTCHANGE = 0x8e52
+ IFF_CANTCHANGE = 0x8f52
IFF_DEBUG = 0x4
IFF_LINK0 = 0x1000
IFF_LINK1 = 0x2000
@@ -431,10 +604,9 @@ const (
IFT_ATMVCIENDPT = 0xc2
IFT_ATMVIRTUAL = 0x95
IFT_BGPPOLICYACCOUNTING = 0xa2
- IFT_BLUETOOTH = 0xf8
IFT_BRIDGE = 0xd1
IFT_BSC = 0x53
- IFT_CARP = 0xf7
+ IFT_CARP = 0xf8
IFT_CCTEMUL = 0x3d
IFT_CEPT = 0x13
IFT_CES = 0x85
@@ -455,19 +627,17 @@ const (
IFT_DS1FDL = 0xaa
IFT_DS3 = 0x1e
IFT_DTM = 0x8c
- IFT_DUMMY = 0xf1
IFT_DVBASILN = 0xac
IFT_DVBASIOUT = 0xad
IFT_DVBRCCDOWNSTREAM = 0x93
IFT_DVBRCCMACLAYER = 0x92
IFT_DVBRCCUPSTREAM = 0x94
IFT_ECONET = 0xce
- IFT_ENC = 0xf4
IFT_EON = 0x19
IFT_EPLRS = 0x57
IFT_ESCON = 0x49
IFT_ETHER = 0x6
- IFT_FAITH = 0xf3
+ IFT_FAITH = 0xf2
IFT_FAST = 0x7d
IFT_FASTETHER = 0x3e
IFT_FASTETHERFX = 0x45
@@ -555,7 +725,6 @@ const (
IFT_P80 = 0xd
IFT_PARA = 0x22
IFT_PFLOG = 0xf5
- IFT_PFLOW = 0xf9
IFT_PFSYNC = 0xf6
IFT_PLC = 0xae
IFT_PON155 = 0xcf
@@ -573,7 +742,7 @@ const (
IFT_PROPVIRTUAL = 0x35
IFT_PROPWIRELESSP2P = 0x9d
IFT_PTPSERIAL = 0x16
- IFT_PVC = 0xf2
+ IFT_PVC = 0xf1
IFT_Q2931 = 0xc9
IFT_QLLC = 0x44
IFT_RADIOMAC = 0xbc
@@ -599,6 +768,7 @@ const (
IFT_SS7SIGLINK = 0x9c
IFT_STACKTOSTACK = 0x6f
IFT_STARLAN = 0xb
+ IFT_STF = 0xd7
IFT_T1 = 0x12
IFT_TDLC = 0x74
IFT_TELINK = 0xc8
@@ -633,6 +803,12 @@ const (
IFT_X25MLP = 0x79
IFT_X25PLE = 0x28
IFT_XETHER = 0x1a
+ IGNBRK = 0x1
+ IGNCR = 0x80
+ IGNPAR = 0x4
+ IMAXBEL = 0x2000
+ INLCR = 0x40
+ INPCK = 0x10
IN_CLASSA_HOST = 0xffffff
IN_CLASSA_MAX = 0x80
IN_CLASSA_NET = 0xff000000
@@ -648,12 +824,8 @@ const (
IN_CLASSD_NET = 0xf0000000
IN_CLASSD_NSHIFT = 0x1c
IN_LOOPBACKNET = 0x7f
- IN_RFC3021_HOST = 0x1
- IN_RFC3021_NET = 0xfffffffe
- IN_RFC3021_NSHIFT = 0x1f
IPPROTO_AH = 0x33
IPPROTO_CARP = 0x70
- IPPROTO_DIVERT = 0x102
IPPROTO_DONE = 0x101
IPPROTO_DSTOPTS = 0x3c
IPPROTO_EGP = 0x8
@@ -674,10 +846,10 @@ const (
IPPROTO_IPIP = 0x4
IPPROTO_IPV4 = 0x4
IPPROTO_IPV6 = 0x29
+ IPPROTO_IPV6_ICMP = 0x3a
IPPROTO_MAX = 0x100
- IPPROTO_MAXID = 0x103
+ IPPROTO_MAXID = 0x34
IPPROTO_MOBILE = 0x37
- IPPROTO_MPLS = 0x89
IPPROTO_NONE = 0x3b
IPPROTO_PFSYNC = 0xf0
IPPROTO_PIM = 0x67
@@ -688,16 +860,13 @@ const (
IPPROTO_TCP = 0x6
IPPROTO_TP = 0x1d
IPPROTO_UDP = 0x11
- IPV6_AUTH_LEVEL = 0x35
- IPV6_AUTOFLOWLABEL = 0x3b
+ IPPROTO_VRRP = 0x70
IPV6_CHECKSUM = 0x1a
IPV6_DEFAULT_MULTICAST_HOPS = 0x1
IPV6_DEFAULT_MULTICAST_LOOP = 0x1
IPV6_DEFHLIM = 0x40
IPV6_DONTFRAG = 0x3e
IPV6_DSTOPTS = 0x32
- IPV6_ESP_NETWORK_LEVEL = 0x37
- IPV6_ESP_TRANS_LEVEL = 0x36
IPV6_FAITH = 0x1d
IPV6_FLOWINFO_MASK = 0xffffff0f
IPV6_FLOWLABEL_MASK = 0xffff0f00
@@ -705,7 +874,7 @@ const (
IPV6_HLIMDEC = 0x1
IPV6_HOPLIMIT = 0x2f
IPV6_HOPOPTS = 0x31
- IPV6_IPCOMP_LEVEL = 0x3c
+ IPV6_IPSEC_POLICY = 0x1c
IPV6_JOIN_GROUP = 0xc
IPV6_LEAVE_GROUP = 0xd
IPV6_MAXHLIM = 0xff
@@ -715,9 +884,7 @@ const (
IPV6_MULTICAST_IF = 0x9
IPV6_MULTICAST_LOOP = 0xb
IPV6_NEXTHOP = 0x30
- IPV6_OPTIONS = 0x1
IPV6_PATHMTU = 0x2c
- IPV6_PIPEX = 0x3f
IPV6_PKTINFO = 0x2e
IPV6_PORTRANGE = 0xe
IPV6_PORTRANGE_DEFAULT = 0x0
@@ -730,7 +897,6 @@ const (
IPV6_RECVPKTINFO = 0x24
IPV6_RECVRTHDR = 0x26
IPV6_RECVTCLASS = 0x39
- IPV6_RTABLE = 0x1021
IPV6_RTHDR = 0x33
IPV6_RTHDRDSTOPTS = 0x23
IPV6_RTHDR_LOOSE = 0x0
@@ -744,74 +910,105 @@ const (
IPV6_VERSION = 0x60
IPV6_VERSION_MASK = 0xf0
IP_ADD_MEMBERSHIP = 0xc
- IP_AUTH_LEVEL = 0x14
IP_DEFAULT_MULTICAST_LOOP = 0x1
IP_DEFAULT_MULTICAST_TTL = 0x1
IP_DF = 0x4000
IP_DROP_MEMBERSHIP = 0xd
- IP_ESP_NETWORK_LEVEL = 0x16
- IP_ESP_TRANS_LEVEL = 0x15
+ IP_EF = 0x8000
+ IP_ERRORMTU = 0x15
IP_HDRINCL = 0x2
- IP_IPCOMP_LEVEL = 0x1d
- IP_IPSEC_LOCAL_AUTH = 0x1b
- IP_IPSEC_LOCAL_CRED = 0x19
- IP_IPSEC_LOCAL_ID = 0x17
- IP_IPSEC_REMOTE_AUTH = 0x1c
- IP_IPSEC_REMOTE_CRED = 0x1a
- IP_IPSEC_REMOTE_ID = 0x18
+ IP_IPSEC_POLICY = 0x16
IP_MAXPACKET = 0xffff
- IP_MAX_MEMBERSHIPS = 0xfff
+ IP_MAX_MEMBERSHIPS = 0x14
IP_MF = 0x2000
- IP_MINTTL = 0x20
- IP_MIN_MEMBERSHIPS = 0xf
+ IP_MINFRAGSIZE = 0x45
+ IP_MINTTL = 0x18
IP_MSS = 0x240
IP_MULTICAST_IF = 0x9
IP_MULTICAST_LOOP = 0xb
IP_MULTICAST_TTL = 0xa
IP_OFFMASK = 0x1fff
IP_OPTIONS = 0x1
- IP_PIPEX = 0x22
IP_PORTRANGE = 0x13
IP_PORTRANGE_DEFAULT = 0x0
IP_PORTRANGE_HIGH = 0x1
IP_PORTRANGE_LOW = 0x2
IP_RECVDSTADDR = 0x7
- IP_RECVDSTPORT = 0x21
- IP_RECVIF = 0x1e
+ IP_RECVIF = 0x14
IP_RECVOPTS = 0x5
IP_RECVRETOPTS = 0x6
- IP_RECVRTABLE = 0x23
- IP_RECVTTL = 0x1f
+ IP_RECVTTL = 0x17
IP_RETOPTS = 0x8
IP_RF = 0x8000
- IP_RTABLE = 0x1021
IP_TOS = 0x3
IP_TTL = 0x4
- MAP_ET_KVAGUARD = 0x10
+ ISIG = 0x80
+ ISTRIP = 0x20
+ IXANY = 0x800
+ IXOFF = 0x400
+ IXON = 0x200
+ LOCK_EX = 0x2
+ LOCK_NB = 0x4
+ LOCK_SH = 0x1
+ LOCK_UN = 0x8
MSG_BCAST = 0x100
+ MSG_CMSG_CLOEXEC = 0x800
+ MSG_CONTROLMBUF = 0x2000000
MSG_CTRUNC = 0x20
MSG_DONTROUTE = 0x4
MSG_DONTWAIT = 0x80
MSG_EOR = 0x8
+ MSG_IOVUSRSPACE = 0x4000000
+ MSG_LENUSRSPACE = 0x8000000
MSG_MCAST = 0x200
+ MSG_NAMEMBUF = 0x1000000
+ MSG_NBIO = 0x1000
+ MSG_NOSIGNAL = 0x400
MSG_OOB = 0x1
MSG_PEEK = 0x2
MSG_TRUNC = 0x10
+ MSG_USERFLAGS = 0xffffff
MSG_WAITALL = 0x40
- NAME_MAX = 0xff
+ NAME_MAX = 0x1ff
NET_RT_DUMP = 0x1
NET_RT_FLAGS = 0x2
- NET_RT_IFLIST = 0x3
+ NET_RT_IFLIST = 0x5
NET_RT_MAXID = 0x6
- NET_RT_STATS = 0x4
- NET_RT_TABLE = 0x5
+ NET_RT_OIFLIST = 0x4
+ NET_RT_OOIFLIST = 0x3
+ NOFLSH = 0x80000000
+ NOTE_ATTRIB = 0x8
+ NOTE_CHILD = 0x4
+ NOTE_DELETE = 0x1
+ NOTE_EXEC = 0x20000000
+ NOTE_EXIT = 0x80000000
+ NOTE_EXTEND = 0x4
+ NOTE_FORK = 0x40000000
+ NOTE_LINK = 0x10
+ NOTE_LOWAT = 0x1
+ NOTE_PCTRLMASK = 0xf0000000
+ NOTE_PDATAMASK = 0xfffff
+ NOTE_RENAME = 0x20
+ NOTE_REVOKE = 0x40
+ NOTE_TRACK = 0x1
+ NOTE_TRACKERR = 0x2
+ NOTE_WRITE = 0x2
+ OCRNL = 0x10
+ OFIOGETBMAP = 0xc004667a
+ ONLCR = 0x2
+ ONLRET = 0x40
+ ONOCR = 0x20
+ ONOEOT = 0x8
+ OPOST = 0x1
O_ACCMODE = 0x3
+ O_ALT_IO = 0x40000
O_APPEND = 0x8
O_ASYNC = 0x40
- O_CLOEXEC = 0x10000
+ O_CLOEXEC = 0x400000
O_CREAT = 0x200
- O_DIRECTORY = 0x20000
- O_DSYNC = 0x80
+ O_DIRECT = 0x80000
+ O_DIRECTORY = 0x200000
+ O_DSYNC = 0x10000
O_EXCL = 0x800
O_EXLOCK = 0x20
O_FSYNC = 0x80
@@ -819,13 +1016,27 @@ const (
O_NOCTTY = 0x8000
O_NOFOLLOW = 0x100
O_NONBLOCK = 0x4
+ O_NOSIGPIPE = 0x1000000
O_RDONLY = 0x0
O_RDWR = 0x2
- O_RSYNC = 0x80
+ O_RSYNC = 0x20000
O_SHLOCK = 0x10
O_SYNC = 0x80
O_TRUNC = 0x400
O_WRONLY = 0x1
+ PARENB = 0x1000
+ PARMRK = 0x8
+ PARODD = 0x2000
+ PENDIN = 0x20000000
+ PRI_IOFLUSH = 0x7c
+ RLIMIT_AS = 0xa
+ RLIMIT_CORE = 0x4
+ RLIMIT_CPU = 0x0
+ RLIMIT_DATA = 0x2
+ RLIMIT_FSIZE = 0x1
+ RLIMIT_NOFILE = 0x8
+ RLIMIT_STACK = 0x3
+ RLIM_INFINITY = 0x7fffffffffffffff
RTAX_AUTHOR = 0x6
RTAX_BRD = 0x7
RTAX_DST = 0x0
@@ -833,11 +1044,9 @@ const (
RTAX_GENMASK = 0x3
RTAX_IFA = 0x5
RTAX_IFP = 0x4
- RTAX_LABEL = 0xa
- RTAX_MAX = 0xb
+ RTAX_MAX = 0x9
RTAX_NETMASK = 0x2
- RTAX_SRC = 0x8
- RTAX_SRCMASK = 0x9
+ RTAX_TAG = 0x8
RTA_AUTHOR = 0x40
RTA_BRD = 0x80
RTA_DST = 0x1
@@ -845,52 +1054,48 @@ const (
RTA_GENMASK = 0x8
RTA_IFA = 0x20
RTA_IFP = 0x10
- RTA_LABEL = 0x400
RTA_NETMASK = 0x4
- RTA_SRC = 0x100
- RTA_SRCMASK = 0x200
- RTF_ANNOUNCE = 0x4000
+ RTA_TAG = 0x100
+ RTF_ANNOUNCE = 0x20000
RTF_BLACKHOLE = 0x1000
- RTF_CLONED = 0x10000
+ RTF_CLONED = 0x2000
RTF_CLONING = 0x100
RTF_DONE = 0x40
RTF_DYNAMIC = 0x10
- RTF_FMASK = 0x8f808
RTF_GATEWAY = 0x2
RTF_HOST = 0x4
- RTF_JUMBO = 0x80000
RTF_LLINFO = 0x400
RTF_MASK = 0x80
RTF_MODIFIED = 0x20
- RTF_MPATH = 0x40000
- RTF_MPLS = 0x100000
- RTF_PERMANENT_ARP = 0x2000
RTF_PROTO1 = 0x8000
RTF_PROTO2 = 0x4000
- RTF_PROTO3 = 0x2000
RTF_REJECT = 0x8
- RTF_SOURCE = 0x20000
+ RTF_SRC = 0x10000
RTF_STATIC = 0x800
- RTF_TUNNEL = 0x100000
RTF_UP = 0x1
- RTF_USETRAILERS = 0x8000
RTF_XRESOLVE = 0x200
RTM_ADD = 0x1
RTM_CHANGE = 0x3
+ RTM_CHGADDR = 0x15
RTM_DELADDR = 0xd
RTM_DELETE = 0x2
- RTM_DESYNC = 0x10
RTM_GET = 0x4
- RTM_IFANNOUNCE = 0xf
- RTM_IFINFO = 0xe
+ RTM_IEEE80211 = 0x11
+ RTM_IFANNOUNCE = 0x10
+ RTM_IFINFO = 0x14
+ RTM_LLINFO_UPD = 0x13
RTM_LOCK = 0x8
RTM_LOSING = 0x5
- RTM_MAXSIZE = 0x800
RTM_MISS = 0x7
RTM_NEWADDR = 0xc
+ RTM_OIFINFO = 0xf
+ RTM_OLDADD = 0x9
+ RTM_OLDDEL = 0xa
+ RTM_OOIFINFO = 0xe
RTM_REDIRECT = 0x6
RTM_RESOLVE = 0xb
RTM_RTTUNIT = 0xf4240
+ RTM_SETGATE = 0x12
RTM_VERSION = 0x4
RTV_EXPIRE = 0x4
RTV_HOPCOUNT = 0x2
@@ -900,159 +1105,82 @@ const (
RTV_RTTVAR = 0x80
RTV_SPIPE = 0x10
RTV_SSTHRESH = 0x20
- RT_TABLEID_MAX = 0xff
- SCM_CREDS = 0x2
+ RUSAGE_CHILDREN = -0x1
+ RUSAGE_SELF = 0x0
+ SCM_CREDS = 0x4
SCM_RIGHTS = 0x1
- SCM_TIMESTAMP = 0x4
+ SCM_TIMESTAMP = 0x8
SHUT_RD = 0x0
SHUT_RDWR = 0x2
SHUT_WR = 0x1
- SIGABRT = 0x6
- SIGALRM = 0xe
- SIGBUS = 0xa
- SIGCHLD = 0x14
- SIGCONT = 0x13
- SIGEMT = 0x7
- SIGFPE = 0x8
- SIGHUP = 0x1
- SIGILL = 0x4
- SIGINFO = 0x1d
- SIGINT = 0x2
- SIGIO = 0x17
- SIGIOT = 0x6
- SIGKILL = 0x9
- SIGPIPE = 0xd
- SIGPROF = 0x1b
- SIGQUIT = 0x3
- SIGSEGV = 0xb
- SIGSTOP = 0x11
- SIGSYS = 0xc
- SIGTERM = 0xf
- SIGTHR = 0x20
- SIGTRAP = 0x5
- SIGTSTP = 0x12
- SIGTTIN = 0x15
- SIGTTOU = 0x16
- SIGURG = 0x10
- SIGUSR1 = 0x1e
- SIGUSR2 = 0x1f
- SIGVTALRM = 0x1a
- SIGWINCH = 0x1c
- SIGXCPU = 0x18
- SIGXFSZ = 0x19
- SIOCADDMULTI = 0x80206931
+ SIOCADDMULTI = 0x80906931
+ SIOCADDRT = 0x8038720a
SIOCAIFADDR = 0x8040691a
- SIOCAIFGROUP = 0x80286987
- SIOCALIFADDR = 0x8218691c
+ SIOCALIFADDR = 0x8118691c
SIOCATMARK = 0x40047307
- SIOCBRDGADD = 0x8058693c
- SIOCBRDGADDS = 0x80586941
- SIOCBRDGARL = 0x806e694d
- SIOCBRDGDADDR = 0x80286947
- SIOCBRDGDEL = 0x8058693d
- SIOCBRDGDELS = 0x80586942
- SIOCBRDGFLUSH = 0x80586948
- SIOCBRDGFRL = 0x806e694e
- SIOCBRDGGCACHE = 0xc0146941
- SIOCBRDGGFD = 0xc0146952
- SIOCBRDGGHT = 0xc0146951
- SIOCBRDGGIFFLGS = 0xc058693e
- SIOCBRDGGMA = 0xc0146953
- SIOCBRDGGPARAM = 0xc0406958
- SIOCBRDGGPRI = 0xc0146950
- SIOCBRDGGRL = 0xc030694f
- SIOCBRDGGSIFS = 0xc058693c
- SIOCBRDGGTO = 0xc0146946
- SIOCBRDGIFS = 0xc0586942
- SIOCBRDGRTS = 0xc0206943
- SIOCBRDGSADDR = 0xc0286944
- SIOCBRDGSCACHE = 0x80146940
- SIOCBRDGSFD = 0x80146952
- SIOCBRDGSHT = 0x80146951
- SIOCBRDGSIFCOST = 0x80586955
- SIOCBRDGSIFFLGS = 0x8058693f
- SIOCBRDGSIFPRIO = 0x80586954
- SIOCBRDGSMA = 0x80146953
- SIOCBRDGSPRI = 0x80146950
- SIOCBRDGSPROTO = 0x8014695a
- SIOCBRDGSTO = 0x80146945
- SIOCBRDGSTXHC = 0x80146959
- SIOCDELMULTI = 0x80206932
- SIOCDIFADDR = 0x80206919
- SIOCDIFGROUP = 0x80286989
- SIOCDIFPHYADDR = 0x80206949
- SIOCDLIFADDR = 0x8218691e
- SIOCGETKALIVE = 0xc01869a4
- SIOCGETLABEL = 0x8020699a
- SIOCGETPFLOW = 0xc02069fe
- SIOCGETPFSYNC = 0xc02069f8
+ SIOCDELMULTI = 0x80906932
+ SIOCDELRT = 0x8038720b
+ SIOCDIFADDR = 0x80906919
+ SIOCDIFPHYADDR = 0x80906949
+ SIOCDLIFADDR = 0x8118691e
+ SIOCGDRVSPEC = 0xc028697b
+ SIOCGETPFSYNC = 0xc09069f8
SIOCGETSGCNT = 0xc0207534
SIOCGETVIFCNT = 0xc0287533
- SIOCGETVLANPRIO = 0xc0206990
SIOCGHIWAT = 0x40047301
- SIOCGIFADDR = 0xc0206921
- SIOCGIFASYNCMAP = 0xc020697c
- SIOCGIFBRDADDR = 0xc0206923
- SIOCGIFCONF = 0xc0106924
- SIOCGIFDATA = 0xc020691b
- SIOCGIFDESCR = 0xc0206981
- SIOCGIFDSTADDR = 0xc0206922
- SIOCGIFFLAGS = 0xc0206911
- SIOCGIFGATTR = 0xc028698b
- SIOCGIFGENERIC = 0xc020693a
- SIOCGIFGMEMB = 0xc028698a
- SIOCGIFGROUP = 0xc0286988
+ SIOCGIFADDR = 0xc0906921
+ SIOCGIFADDRPREF = 0xc0986920
+ SIOCGIFALIAS = 0xc040691b
+ SIOCGIFBRDADDR = 0xc0906923
+ SIOCGIFCAP = 0xc0206976
+ SIOCGIFCONF = 0xc0106926
+ SIOCGIFDATA = 0xc0986985
+ SIOCGIFDLT = 0xc0906977
+ SIOCGIFDSTADDR = 0xc0906922
+ SIOCGIFFLAGS = 0xc0906911
+ SIOCGIFGENERIC = 0xc090693a
SIOCGIFMEDIA = 0xc0306936
- SIOCGIFMETRIC = 0xc0206917
- SIOCGIFMTU = 0xc020697e
- SIOCGIFNETMASK = 0xc0206925
- SIOCGIFPDSTADDR = 0xc0206948
- SIOCGIFPRIORITY = 0xc020699c
- SIOCGIFPSRCADDR = 0xc0206947
- SIOCGIFRDOMAIN = 0xc02069a0
- SIOCGIFRTLABEL = 0xc0206983
- SIOCGIFTIMESLOT = 0xc0206986
- SIOCGIFXFLAGS = 0xc020699e
- SIOCGLIFADDR = 0xc218691d
- SIOCGLIFPHYADDR = 0xc218694b
- SIOCGLIFPHYRTABLE = 0xc02069a2
+ SIOCGIFMETRIC = 0xc0906917
+ SIOCGIFMTU = 0xc090697e
+ SIOCGIFNETMASK = 0xc0906925
+ SIOCGIFPDSTADDR = 0xc0906948
+ SIOCGIFPSRCADDR = 0xc0906947
+ SIOCGLIFADDR = 0xc118691d
+ SIOCGLIFPHYADDR = 0xc118694b
+ SIOCGLINKSTR = 0xc0286987
SIOCGLOWAT = 0x40047303
SIOCGPGRP = 0x40047309
- SIOCGVH = 0xc02069f6
- SIOCIFCREATE = 0x8020697a
- SIOCIFDESTROY = 0x80206979
+ SIOCGVH = 0xc0906983
+ SIOCIFCREATE = 0x8090697a
+ SIOCIFDESTROY = 0x80906979
SIOCIFGCLONERS = 0xc0106978
- SIOCSETKALIVE = 0x801869a3
- SIOCSETLABEL = 0x80206999
- SIOCSETPFLOW = 0x802069fd
- SIOCSETPFSYNC = 0x802069f7
- SIOCSETVLANPRIO = 0x8020698f
+ SIOCINITIFADDR = 0xc0706984
+ SIOCSDRVSPEC = 0x8028697b
+ SIOCSETPFSYNC = 0x809069f7
SIOCSHIWAT = 0x80047300
- SIOCSIFADDR = 0x8020690c
- SIOCSIFASYNCMAP = 0x8020697d
- SIOCSIFBRDADDR = 0x80206913
- SIOCSIFDESCR = 0x80206980
- SIOCSIFDSTADDR = 0x8020690e
- SIOCSIFFLAGS = 0x80206910
- SIOCSIFGATTR = 0x8028698c
- SIOCSIFGENERIC = 0x80206939
- SIOCSIFLLADDR = 0x8020691f
- SIOCSIFMEDIA = 0xc0206935
- SIOCSIFMETRIC = 0x80206918
- SIOCSIFMTU = 0x8020697f
- SIOCSIFNETMASK = 0x80206916
+ SIOCSIFADDR = 0x8090690c
+ SIOCSIFADDRPREF = 0x8098691f
+ SIOCSIFBRDADDR = 0x80906913
+ SIOCSIFCAP = 0x80206975
+ SIOCSIFDSTADDR = 0x8090690e
+ SIOCSIFFLAGS = 0x80906910
+ SIOCSIFGENERIC = 0x80906939
+ SIOCSIFMEDIA = 0xc0906935
+ SIOCSIFMETRIC = 0x80906918
+ SIOCSIFMTU = 0x8090697f
+ SIOCSIFNETMASK = 0x80906916
SIOCSIFPHYADDR = 0x80406946
- SIOCSIFPRIORITY = 0x8020699b
- SIOCSIFRDOMAIN = 0x8020699f
- SIOCSIFRTLABEL = 0x80206982
- SIOCSIFTIMESLOT = 0x80206985
- SIOCSIFXFLAGS = 0x8020699d
- SIOCSLIFPHYADDR = 0x8218694a
- SIOCSLIFPHYRTABLE = 0x802069a1
+ SIOCSLIFPHYADDR = 0x8118694a
+ SIOCSLINKSTR = 0x80286988
SIOCSLOWAT = 0x80047302
SIOCSPGRP = 0x80047308
- SIOCSVH = 0xc02069f5
+ SIOCSVH = 0xc0906982
+ SIOCZIFDATA = 0xc0986986
+ SOCK_CLOEXEC = 0x10000000
SOCK_DGRAM = 0x2
+ SOCK_FLAGS_MASK = 0xf0000000
+ SOCK_NONBLOCK = 0x20000000
+ SOCK_NOSIGPIPE = 0x40000000
SOCK_RAW = 0x3
SOCK_RDM = 0x4
SOCK_SEQPACKET = 0x5
@@ -1060,63 +1188,107 @@ const (
SOL_SOCKET = 0xffff
SOMAXCONN = 0x80
SO_ACCEPTCONN = 0x2
- SO_BINDANY = 0x1000
+ SO_ACCEPTFILTER = 0x1000
SO_BROADCAST = 0x20
SO_DEBUG = 0x1
SO_DONTROUTE = 0x10
SO_ERROR = 0x1007
- SO_JUMBO = 0x400
SO_KEEPALIVE = 0x8
SO_LINGER = 0x80
- SO_NETPROC = 0x1020
+ SO_NOHEADER = 0x100a
+ SO_NOSIGPIPE = 0x800
SO_OOBINLINE = 0x100
- SO_PEERCRED = 0x1022
+ SO_OVERFLOWED = 0x1009
SO_RCVBUF = 0x1002
SO_RCVLOWAT = 0x1004
- SO_RCVTIMEO = 0x1006
+ SO_RCVTIMEO = 0x100c
SO_REUSEADDR = 0x4
SO_REUSEPORT = 0x200
- SO_RTABLE = 0x1021
SO_SNDBUF = 0x1001
SO_SNDLOWAT = 0x1003
- SO_SNDTIMEO = 0x1005
- SO_SPLICE = 0x1023
- SO_TIMESTAMP = 0x800
+ SO_SNDTIMEO = 0x100b
+ SO_TIMESTAMP = 0x2000
SO_TYPE = 0x1008
SO_USELOOPBACK = 0x40
+ SYSCTL_VERSION = 0x1000000
+ SYSCTL_VERS_0 = 0x0
+ SYSCTL_VERS_1 = 0x1000000
+ SYSCTL_VERS_MASK = 0xff000000
+ S_ARCH1 = 0x10000
+ S_ARCH2 = 0x20000
+ S_BLKSIZE = 0x200
+ S_IEXEC = 0x40
+ S_IFBLK = 0x6000
+ S_IFCHR = 0x2000
+ S_IFDIR = 0x4000
+ S_IFIFO = 0x1000
+ S_IFLNK = 0xa000
+ S_IFMT = 0xf000
+ S_IFREG = 0x8000
+ S_IFSOCK = 0xc000
+ S_IFWHT = 0xe000
+ S_IREAD = 0x100
+ S_IRGRP = 0x20
+ S_IROTH = 0x4
+ S_IRUSR = 0x100
+ S_IRWXG = 0x38
+ S_IRWXO = 0x7
+ S_IRWXU = 0x1c0
+ S_ISGID = 0x400
+ S_ISTXT = 0x200
+ S_ISUID = 0x800
+ S_ISVTX = 0x200
+ S_IWGRP = 0x10
+ S_IWOTH = 0x2
+ S_IWRITE = 0x80
+ S_IWUSR = 0x80
+ S_IXGRP = 0x8
+ S_IXOTH = 0x1
+ S_IXUSR = 0x40
+ S_LOGIN_SET = 0x1
+ TCIFLUSH = 0x1
+ TCIOFLUSH = 0x3
+ TCOFLUSH = 0x2
+ TCP_CONGCTL = 0x20
+ TCP_KEEPCNT = 0x6
+ TCP_KEEPIDLE = 0x3
+ TCP_KEEPINIT = 0x7
+ TCP_KEEPINTVL = 0x5
TCP_MAXBURST = 0x4
TCP_MAXSEG = 0x2
TCP_MAXWIN = 0xffff
- TCP_MAX_SACK = 0x3
TCP_MAX_WINSHIFT = 0xe
- TCP_MD5SIG = 0x4
- TCP_MSS = 0x200
+ TCP_MD5SIG = 0x10
+ TCP_MINMSS = 0xd8
+ TCP_MSS = 0x218
TCP_NODELAY = 0x1
- TCP_NSTATES = 0xb
- TCP_SACK_ENABLE = 0x8
+ TCSAFLUSH = 0x2
TIOCCBRK = 0x2000747a
TIOCCDTR = 0x20007478
TIOCCONS = 0x80047462
+ TIOCDCDTIMESTAMP = 0x40107458
TIOCDRAIN = 0x2000745e
TIOCEXCL = 0x2000740d
TIOCEXT = 0x80047460
+ TIOCFLAG_CDTRCTS = 0x10
TIOCFLAG_CLOCAL = 0x2
TIOCFLAG_CRTSCTS = 0x4
TIOCFLAG_MDMBUF = 0x8
- TIOCFLAG_PPS = 0x10
TIOCFLAG_SOFTCAR = 0x1
TIOCFLUSH = 0x80047410
TIOCGETA = 0x402c7413
TIOCGETD = 0x4004741a
TIOCGFLAGS = 0x4004745d
+ TIOCGLINED = 0x40207442
TIOCGPGRP = 0x40047477
- TIOCGTSTAMP = 0x4010745b
+ TIOCGQSIZE = 0x40047481
+ TIOCGRANTPT = 0x20007447
+ TIOCGSID = 0x40047463
+ TIOCGSIZE = 0x40087468
TIOCGWINSZ = 0x40087468
TIOCMBIC = 0x8004746b
TIOCMBIS = 0x8004746c
TIOCMGET = 0x4004746a
- TIOCMODG = 0x4004746a
- TIOCMODS = 0x8004746d
TIOCMSET = 0x8004746d
TIOCM_CAR = 0x40
TIOCM_CD = 0x40
@@ -1141,6 +1313,9 @@ const (
TIOCPKT_NOSTOP = 0x10
TIOCPKT_START = 0x8
TIOCPKT_STOP = 0x4
+ TIOCPTMGET = 0x40287446
+ TIOCPTSNAME = 0x40287448
+ TIOCRCVFRAME = 0x80087445
TIOCREMOTE = 0x80047469
TIOCSBRK = 0x2000747b
TIOCSCTTY = 0x20007461
@@ -1150,19 +1325,46 @@ const (
TIOCSETAW = 0x802c7415
TIOCSETD = 0x8004741b
TIOCSFLAGS = 0x8004745c
- TIOCSIG = 0x8004745f
+ TIOCSIG = 0x2000745f
+ TIOCSLINED = 0x80207443
TIOCSPGRP = 0x80047476
+ TIOCSQSIZE = 0x80047480
+ TIOCSSIZE = 0x80087467
TIOCSTART = 0x2000746e
TIOCSTAT = 0x80047465
TIOCSTI = 0x80017472
TIOCSTOP = 0x2000746f
- TIOCSTSTAMP = 0x8008745a
TIOCSWINSZ = 0x80087467
TIOCUCNTL = 0x80047466
+ TIOCXMTFRAME = 0x80087444
+ TOSTOP = 0x400000
+ VDISCARD = 0xf
+ VDSUSP = 0xb
+ VEOF = 0x0
+ VEOL = 0x1
+ VEOL2 = 0x2
+ VERASE = 0x3
+ VINTR = 0x8
+ VKILL = 0x5
+ VLNEXT = 0xe
+ VMIN = 0x10
+ VQUIT = 0x9
+ VREPRINT = 0x6
+ VSTART = 0xc
+ VSTATUS = 0x12
+ VSTOP = 0xd
+ VSUSP = 0xa
+ VTIME = 0x11
+ VWERASE = 0x4
+ WALL = 0x8
+ WALLSIG = 0x8
WALTSIG = 0x4
- WCONTINUED = 0x8
+ WCLONE = 0x4
WCOREFLAG = 0x80
WNOHANG = 0x1
+ WNOWAIT = 0x10000
+ WNOZOMBIE = 0x20000
+ WOPTSCHECKED = 0x40000
WSTOPPED = 0x7f
WUNTRACED = 0x2
)
@@ -1178,17 +1380,11 @@ const (
EALREADY = Errno(0x25)
EAUTH = Errno(0x50)
EBADF = Errno(0x9)
+ EBADMSG = Errno(0x58)
EBADRPC = Errno(0x48)
EBUSY = Errno(0x10)
- ECANCELED = Errno(0x58)
+ ECANCELED = Errno(0x57)
ECHILD = Errno(0xa)
- ECHO = Errno(0x8)
- ECHOCTL = Errno(0x40)
- ECHOE = Errno(0x2)
- ECHOK = Errno(0x4)
- ECHOKE = Errno(0x1)
- ECHONL = Errno(0x10)
- ECHOPRT = Errno(0x20)
ECONNABORTED = Errno(0x35)
ECONNREFUSED = Errno(0x3d)
ECONNRESET = Errno(0x36)
@@ -1202,50 +1398,51 @@ const (
EFTYPE = Errno(0x4f)
EHOSTDOWN = Errno(0x40)
EHOSTUNREACH = Errno(0x41)
- EIDRM = Errno(0x59)
- EILSEQ = Errno(0x54)
+ EIDRM = Errno(0x52)
+ EILSEQ = Errno(0x55)
EINPROGRESS = Errno(0x24)
EINTR = Errno(0x4)
EINVAL = Errno(0x16)
EIO = Errno(0x5)
- EIPSEC = Errno(0x52)
EISCONN = Errno(0x38)
EISDIR = Errno(0x15)
- ELAST = Errno(0x5b)
+ ELAST = Errno(0x60)
ELOOP = Errno(0x3e)
- EMEDIUMTYPE = Errno(0x56)
EMFILE = Errno(0x18)
EMLINK = Errno(0x1f)
EMSGSIZE = Errno(0x28)
+ EMULTIHOP = Errno(0x5e)
ENAMETOOLONG = Errno(0x3f)
- ENDRUNDISC = Errno(0x9)
ENEEDAUTH = Errno(0x51)
ENETDOWN = Errno(0x32)
ENETRESET = Errno(0x34)
ENETUNREACH = Errno(0x33)
ENFILE = Errno(0x17)
- ENOATTR = Errno(0x53)
+ ENOATTR = Errno(0x5d)
ENOBUFS = Errno(0x37)
+ ENODATA = Errno(0x59)
ENODEV = Errno(0x13)
ENOENT = Errno(0x2)
ENOEXEC = Errno(0x8)
ENOLCK = Errno(0x4d)
- ENOMEDIUM = Errno(0x55)
+ ENOLINK = Errno(0x5f)
ENOMEM = Errno(0xc)
- ENOMSG = Errno(0x5a)
+ ENOMSG = Errno(0x53)
ENOPROTOOPT = Errno(0x2a)
ENOSPC = Errno(0x1c)
+ ENOSR = Errno(0x5a)
+ ENOSTR = Errno(0x5b)
ENOSYS = Errno(0x4e)
ENOTBLK = Errno(0xf)
ENOTCONN = Errno(0x39)
ENOTDIR = Errno(0x14)
ENOTEMPTY = Errno(0x42)
ENOTSOCK = Errno(0x26)
- ENOTSUP = Errno(0x5b)
+ ENOTSUP = Errno(0x56)
ENOTTY = Errno(0x19)
ENXIO = Errno(0x6)
EOPNOTSUPP = Errno(0x2d)
- EOVERFLOW = Errno(0x57)
+ EOVERFLOW = Errno(0x54)
EPERM = Errno(0x1)
EPFNOSUPPORT = Errno(0x2e)
EPIPE = Errno(0x20)
@@ -1253,6 +1450,7 @@ const (
EPROCUNAVAIL = Errno(0x4c)
EPROGMISMATCH = Errno(0x4b)
EPROGUNAVAIL = Errno(0x4a)
+ EPROTO = Errno(0x60)
EPROTONOSUPPORT = Errno(0x2b)
EPROTOTYPE = Errno(0x29)
ERANGE = Errno(0x22)
@@ -1264,6 +1462,7 @@ const (
ESPIPE = Errno(0x1d)
ESRCH = Errno(0x3)
ESTALE = Errno(0x46)
+ ETIME = Errno(0x5c)
ETIMEDOUT = Errno(0x3c)
ETOOMANYREFS = Errno(0x3b)
ETXTBSY = Errno(0x1a)
@@ -1272,6 +1471,43 @@ const (
EXDEV = Errno(0x12)
)
+// Signals
+const (
+ SIGABRT = Signal(0x6)
+ SIGALRM = Signal(0xe)
+ SIGBUS = Signal(0xa)
+ SIGCHLD = Signal(0x14)
+ SIGCONT = Signal(0x13)
+ SIGEMT = Signal(0x7)
+ SIGFPE = Signal(0x8)
+ SIGHUP = Signal(0x1)
+ SIGILL = Signal(0x4)
+ SIGINFO = Signal(0x1d)
+ SIGINT = Signal(0x2)
+ SIGIO = Signal(0x17)
+ SIGIOT = Signal(0x6)
+ SIGKILL = Signal(0x9)
+ SIGPIPE = Signal(0xd)
+ SIGPROF = Signal(0x1b)
+ SIGPWR = Signal(0x20)
+ SIGQUIT = Signal(0x3)
+ SIGSEGV = Signal(0xb)
+ SIGSTOP = Signal(0x11)
+ SIGSYS = Signal(0xc)
+ SIGTERM = Signal(0xf)
+ SIGTRAP = Signal(0x5)
+ SIGTSTP = Signal(0x12)
+ SIGTTIN = Signal(0x15)
+ SIGTTOU = Signal(0x16)
+ SIGURG = Signal(0x10)
+ SIGUSR1 = Signal(0x1e)
+ SIGUSR2 = Signal(0x1f)
+ SIGVTALRM = Signal(0x1a)
+ SIGWINCH = Signal(0x1c)
+ SIGXCPU = Signal(0x18)
+ SIGXFSZ = Signal(0x19)
+)
+
// Error table
var errors = [...]string{
1: "operation not permitted",
@@ -1307,7 +1543,7 @@ var errors = [...]string{
31: "too many links",
32: "broken pipe",
33: "numerical argument out of domain",
- 34: "result too large",
+ 34: "result too large or too small",
35: "resource temporarily unavailable",
36: "operation now in progress",
37: "operation already in progress",
@@ -1315,7 +1551,7 @@ var errors = [...]string{
39: "destination address required",
40: "message too long",
41: "protocol wrong type for socket",
- 42: "protocol not available",
+ 42: "protocol option not available",
43: "protocol not supported",
44: "socket type not supported",
45: "operation not supported",
@@ -1355,14 +1591,55 @@ var errors = [...]string{
79: "inappropriate file type or format",
80: "authentication error",
81: "need authenticator",
- 82: "IPsec processing failure",
- 83: "attribute not found",
- 84: "illegal byte sequence",
- 85: "no medium found",
- 86: "wrong medium type",
- 87: "value too large to be stored in data type",
- 88: "operation canceled",
- 89: "identifier removed",
- 90: "no message of desired type",
- 91: "not supported",
+ 82: "identifier removed",
+ 83: "no message of desired type",
+ 84: "value too large to be stored in data type",
+ 85: "illegal byte sequence",
+ 86: "not supported",
+ 87: "operation Canceled",
+ 88: "bad or Corrupt message",
+ 89: "no message available",
+ 90: "no STREAM resources",
+ 91: "not a STREAM",
+ 92: "STREAM ioctl timeout",
+ 93: "attribute not found",
+ 94: "multihop attempted",
+ 95: "link has been severed",
+ 96: "protocol error",
+}
+
+// Signal table
+var signals = [...]string{
+ 1: "hangup",
+ 2: "interrupt",
+ 3: "quit",
+ 4: "illegal instruction",
+ 5: "trace/BPT trap",
+ 6: "abort trap",
+ 7: "EMT trap",
+ 8: "floating point exception",
+ 9: "killed",
+ 10: "bus error",
+ 11: "segmentation fault",
+ 12: "bad system call",
+ 13: "broken pipe",
+ 14: "alarm clock",
+ 15: "terminated",
+ 16: "urgent I/O condition",
+ 17: "stopped (signal)",
+ 18: "stopped",
+ 19: "continued",
+ 20: "child exited",
+ 21: "stopped (tty input)",
+ 22: "stopped (tty output)",
+ 23: "I/O possible",
+ 24: "cputime limit exceeded",
+ 25: "filesize limit exceeded",
+ 26: "virtual timer expired",
+ 27: "profiling timer expired",
+ 28: "window size changes",
+ 29: "information request",
+ 30: "user defined signal 1",
+ 31: "user defined signal 2",
+ 32: "power fail/restart",
}
diff --git a/src/pkg/syscall/zerrors_netbsd_arm.go b/src/pkg/syscall/zerrors_netbsd_arm.go
new file mode 100644
index 000000000..3899f6ba6
--- /dev/null
+++ b/src/pkg/syscall/zerrors_netbsd_arm.go
@@ -0,0 +1,1644 @@
+// mkerrors.sh -marm
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs -- -marm _const.go
+
+package syscall
+
+const (
+ AF_APPLETALK = 0x10
+ AF_ARP = 0x1c
+ AF_BLUETOOTH = 0x1f
+ AF_CCITT = 0xa
+ AF_CHAOS = 0x5
+ AF_CNT = 0x15
+ AF_COIP = 0x14
+ AF_DATAKIT = 0x9
+ AF_DECnet = 0xc
+ AF_DLI = 0xd
+ AF_E164 = 0x1a
+ AF_ECMA = 0x8
+ AF_HYLINK = 0xf
+ AF_IEEE80211 = 0x20
+ AF_IMPLINK = 0x3
+ AF_INET = 0x2
+ AF_INET6 = 0x18
+ AF_IPX = 0x17
+ AF_ISDN = 0x1a
+ AF_ISO = 0x7
+ AF_LAT = 0xe
+ AF_LINK = 0x12
+ AF_LOCAL = 0x1
+ AF_MAX = 0x23
+ AF_MPLS = 0x21
+ AF_NATM = 0x1b
+ AF_NS = 0x6
+ AF_OROUTE = 0x11
+ AF_OSI = 0x7
+ AF_PUP = 0x4
+ AF_ROUTE = 0x22
+ AF_SNA = 0xb
+ AF_UNIX = 0x1
+ AF_UNSPEC = 0x0
+ ARPHRD_ARCNET = 0x7
+ ARPHRD_ETHER = 0x1
+ ARPHRD_FRELAY = 0xf
+ ARPHRD_IEEE1394 = 0x18
+ ARPHRD_IEEE802 = 0x6
+ ARPHRD_STRIP = 0x17
+ B0 = 0x0
+ B110 = 0x6e
+ B115200 = 0x1c200
+ B1200 = 0x4b0
+ B134 = 0x86
+ B14400 = 0x3840
+ B150 = 0x96
+ B1800 = 0x708
+ B19200 = 0x4b00
+ B200 = 0xc8
+ B230400 = 0x38400
+ B2400 = 0x960
+ B28800 = 0x7080
+ B300 = 0x12c
+ B38400 = 0x9600
+ B460800 = 0x70800
+ B4800 = 0x12c0
+ B50 = 0x32
+ B57600 = 0xe100
+ B600 = 0x258
+ B7200 = 0x1c20
+ B75 = 0x4b
+ B76800 = 0x12c00
+ B921600 = 0xe1000
+ B9600 = 0x2580
+ BIOCFEEDBACK = 0x8004427d
+ BIOCFLUSH = 0x20004268
+ BIOCGBLEN = 0x40044266
+ BIOCGDLT = 0x4004426a
+ BIOCGDLTLIST = 0xc0084277
+ BIOCGETIF = 0x4090426b
+ BIOCGFEEDBACK = 0x4004427c
+ BIOCGHDRCMPLT = 0x40044274
+ BIOCGRTIMEOUT = 0x400c427b
+ BIOCGSEESENT = 0x40044278
+ BIOCGSTATS = 0x4080426f
+ BIOCGSTATSOLD = 0x4008426f
+ BIOCIMMEDIATE = 0x80044270
+ BIOCPROMISC = 0x20004269
+ BIOCSBLEN = 0xc0044266
+ BIOCSDLT = 0x80044276
+ BIOCSETF = 0x80084267
+ BIOCSETIF = 0x8090426c
+ BIOCSFEEDBACK = 0x8004427d
+ BIOCSHDRCMPLT = 0x80044275
+ BIOCSRTIMEOUT = 0x800c427a
+ BIOCSSEESENT = 0x80044279
+ BIOCSTCPF = 0x80084272
+ BIOCSUDPF = 0x80084273
+ BIOCVERSION = 0x40044271
+ BPF_A = 0x10
+ BPF_ABS = 0x20
+ BPF_ADD = 0x0
+ BPF_ALIGNMENT = 0x4
+ BPF_ALIGNMENT32 = 0x4
+ BPF_ALU = 0x4
+ BPF_AND = 0x50
+ BPF_B = 0x10
+ BPF_DFLTBUFSIZE = 0x100000
+ BPF_DIV = 0x30
+ BPF_H = 0x8
+ BPF_IMM = 0x0
+ BPF_IND = 0x40
+ BPF_JA = 0x0
+ BPF_JEQ = 0x10
+ BPF_JGE = 0x30
+ BPF_JGT = 0x20
+ BPF_JMP = 0x5
+ BPF_JSET = 0x40
+ BPF_K = 0x0
+ BPF_LD = 0x0
+ BPF_LDX = 0x1
+ BPF_LEN = 0x80
+ BPF_LSH = 0x60
+ BPF_MAJOR_VERSION = 0x1
+ BPF_MAXBUFSIZE = 0x1000000
+ BPF_MAXINSNS = 0x200
+ BPF_MEM = 0x60
+ BPF_MEMWORDS = 0x10
+ BPF_MINBUFSIZE = 0x20
+ BPF_MINOR_VERSION = 0x1
+ BPF_MISC = 0x7
+ BPF_MSH = 0xa0
+ BPF_MUL = 0x20
+ BPF_NEG = 0x80
+ BPF_OR = 0x40
+ BPF_RELEASE = 0x30bb6
+ BPF_RET = 0x6
+ BPF_RSH = 0x70
+ BPF_ST = 0x2
+ BPF_STX = 0x3
+ BPF_SUB = 0x10
+ BPF_TAX = 0x0
+ BPF_TXA = 0x80
+ BPF_W = 0x0
+ BPF_X = 0x8
+ BRKINT = 0x2
+ CFLUSH = 0xf
+ CLOCAL = 0x8000
+ CREAD = 0x800
+ CS5 = 0x0
+ CS6 = 0x100
+ CS7 = 0x200
+ CS8 = 0x300
+ CSIZE = 0x300
+ CSTART = 0x11
+ CSTATUS = 0x14
+ CSTOP = 0x13
+ CSTOPB = 0x400
+ CSUSP = 0x1a
+ CTL_MAXNAME = 0xc
+ CTL_NET = 0x4
+ CTL_QUERY = -0x2
+ DIOCBSFLUSH = 0x20006478
+ DLT_A429 = 0xb8
+ DLT_A653_ICM = 0xb9
+ DLT_AIRONET_HEADER = 0x78
+ DLT_AOS = 0xde
+ DLT_APPLE_IP_OVER_IEEE1394 = 0x8a
+ DLT_ARCNET = 0x7
+ DLT_ARCNET_LINUX = 0x81
+ DLT_ATM_CLIP = 0x13
+ DLT_ATM_RFC1483 = 0xb
+ DLT_AURORA = 0x7e
+ DLT_AX25 = 0x3
+ DLT_AX25_KISS = 0xca
+ DLT_BACNET_MS_TP = 0xa5
+ DLT_BLUETOOTH_HCI_H4 = 0xbb
+ DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 0xc9
+ DLT_CAN20B = 0xbe
+ DLT_CAN_SOCKETCAN = 0xe3
+ DLT_CHAOS = 0x5
+ DLT_CISCO_IOS = 0x76
+ DLT_C_HDLC = 0x68
+ DLT_C_HDLC_WITH_DIR = 0xcd
+ DLT_DECT = 0xdd
+ DLT_DOCSIS = 0x8f
+ DLT_ECONET = 0x73
+ DLT_EN10MB = 0x1
+ DLT_EN3MB = 0x2
+ DLT_ENC = 0x6d
+ DLT_ERF = 0xc5
+ DLT_ERF_ETH = 0xaf
+ DLT_ERF_POS = 0xb0
+ DLT_FC_2 = 0xe0
+ DLT_FC_2_WITH_FRAME_DELIMS = 0xe1
+ DLT_FDDI = 0xa
+ DLT_FLEXRAY = 0xd2
+ DLT_FRELAY = 0x6b
+ DLT_FRELAY_WITH_DIR = 0xce
+ DLT_GCOM_SERIAL = 0xad
+ DLT_GCOM_T1E1 = 0xac
+ DLT_GPF_F = 0xab
+ DLT_GPF_T = 0xaa
+ DLT_GPRS_LLC = 0xa9
+ DLT_GSMTAP_ABIS = 0xda
+ DLT_GSMTAP_UM = 0xd9
+ DLT_HDLC = 0x10
+ DLT_HHDLC = 0x79
+ DLT_HIPPI = 0xf
+ DLT_IBM_SN = 0x92
+ DLT_IBM_SP = 0x91
+ DLT_IEEE802 = 0x6
+ DLT_IEEE802_11 = 0x69
+ DLT_IEEE802_11_RADIO = 0x7f
+ DLT_IEEE802_11_RADIO_AVS = 0xa3
+ DLT_IEEE802_15_4 = 0xc3
+ DLT_IEEE802_15_4_LINUX = 0xbf
+ DLT_IEEE802_15_4_NONASK_PHY = 0xd7
+ DLT_IEEE802_16_MAC_CPS = 0xbc
+ DLT_IEEE802_16_MAC_CPS_RADIO = 0xc1
+ DLT_IPMB = 0xc7
+ DLT_IPMB_LINUX = 0xd1
+ DLT_IPNET = 0xe2
+ DLT_IPV4 = 0xe4
+ DLT_IPV6 = 0xe5
+ DLT_IP_OVER_FC = 0x7a
+ DLT_JUNIPER_ATM1 = 0x89
+ DLT_JUNIPER_ATM2 = 0x87
+ DLT_JUNIPER_CHDLC = 0xb5
+ DLT_JUNIPER_ES = 0x84
+ DLT_JUNIPER_ETHER = 0xb2
+ DLT_JUNIPER_FRELAY = 0xb4
+ DLT_JUNIPER_GGSN = 0x85
+ DLT_JUNIPER_ISM = 0xc2
+ DLT_JUNIPER_MFR = 0x86
+ DLT_JUNIPER_MLFR = 0x83
+ DLT_JUNIPER_MLPPP = 0x82
+ DLT_JUNIPER_MONITOR = 0xa4
+ DLT_JUNIPER_PIC_PEER = 0xae
+ DLT_JUNIPER_PPP = 0xb3
+ DLT_JUNIPER_PPPOE = 0xa7
+ DLT_JUNIPER_PPPOE_ATM = 0xa8
+ DLT_JUNIPER_SERVICES = 0x88
+ DLT_JUNIPER_ST = 0xc8
+ DLT_JUNIPER_VP = 0xb7
+ DLT_LAPB_WITH_DIR = 0xcf
+ DLT_LAPD = 0xcb
+ DLT_LIN = 0xd4
+ DLT_LINUX_EVDEV = 0xd8
+ DLT_LINUX_IRDA = 0x90
+ DLT_LINUX_LAPD = 0xb1
+ DLT_LINUX_SLL = 0x71
+ DLT_LOOP = 0x6c
+ DLT_LTALK = 0x72
+ DLT_MFR = 0xb6
+ DLT_MOST = 0xd3
+ DLT_MPLS = 0xdb
+ DLT_MTP2 = 0x8c
+ DLT_MTP2_WITH_PHDR = 0x8b
+ DLT_MTP3 = 0x8d
+ DLT_NULL = 0x0
+ DLT_PCI_EXP = 0x7d
+ DLT_PFLOG = 0x75
+ DLT_PFSYNC = 0x12
+ DLT_PPI = 0xc0
+ DLT_PPP = 0x9
+ DLT_PPP_BSDOS = 0xe
+ DLT_PPP_ETHER = 0x33
+ DLT_PPP_PPPD = 0xa6
+ DLT_PPP_SERIAL = 0x32
+ DLT_PPP_WITH_DIR = 0xcc
+ DLT_PRISM_HEADER = 0x77
+ DLT_PRONET = 0x4
+ DLT_RAIF1 = 0xc6
+ DLT_RAW = 0xc
+ DLT_RAWAF_MASK = 0x2240000
+ DLT_RIO = 0x7c
+ DLT_SCCP = 0x8e
+ DLT_SITA = 0xc4
+ DLT_SLIP = 0x8
+ DLT_SLIP_BSDOS = 0xd
+ DLT_SUNATM = 0x7b
+ DLT_SYMANTEC_FIREWALL = 0x63
+ DLT_TZSP = 0x80
+ DLT_USB = 0xba
+ DLT_USB_LINUX = 0xbd
+ DLT_USB_LINUX_MMAPPED = 0xdc
+ DLT_WIHART = 0xdf
+ DLT_X2E_SERIAL = 0xd5
+ DLT_X2E_XORAYA = 0xd6
+ DT_BLK = 0x6
+ DT_CHR = 0x2
+ DT_DIR = 0x4
+ DT_FIFO = 0x1
+ DT_LNK = 0xa
+ DT_REG = 0x8
+ DT_SOCK = 0xc
+ DT_UNKNOWN = 0x0
+ DT_WHT = 0xe
+ ECHO = 0x8
+ ECHOCTL = 0x40
+ ECHOE = 0x2
+ ECHOK = 0x4
+ ECHOKE = 0x1
+ ECHONL = 0x10
+ ECHOPRT = 0x20
+ EMUL_LINUX = 0x1
+ EMUL_LINUX32 = 0x5
+ EMUL_MAXID = 0x6
+ ETHERCAP_JUMBO_MTU = 0x4
+ ETHERCAP_VLAN_HWTAGGING = 0x2
+ ETHERCAP_VLAN_MTU = 0x1
+ ETHERMIN = 0x2e
+ ETHERMTU = 0x5dc
+ ETHERMTU_JUMBO = 0x2328
+ ETHERTYPE_8023 = 0x4
+ ETHERTYPE_AARP = 0x80f3
+ ETHERTYPE_ACCTON = 0x8390
+ ETHERTYPE_AEONIC = 0x8036
+ ETHERTYPE_ALPHA = 0x814a
+ ETHERTYPE_AMBER = 0x6008
+ ETHERTYPE_AMOEBA = 0x8145
+ ETHERTYPE_APOLLO = 0x80f7
+ ETHERTYPE_APOLLODOMAIN = 0x8019
+ ETHERTYPE_APPLETALK = 0x809b
+ ETHERTYPE_APPLITEK = 0x80c7
+ ETHERTYPE_ARGONAUT = 0x803a
+ ETHERTYPE_ARP = 0x806
+ ETHERTYPE_AT = 0x809b
+ ETHERTYPE_ATALK = 0x809b
+ ETHERTYPE_ATOMIC = 0x86df
+ ETHERTYPE_ATT = 0x8069
+ ETHERTYPE_ATTSTANFORD = 0x8008
+ ETHERTYPE_AUTOPHON = 0x806a
+ ETHERTYPE_AXIS = 0x8856
+ ETHERTYPE_BCLOOP = 0x9003
+ ETHERTYPE_BOFL = 0x8102
+ ETHERTYPE_CABLETRON = 0x7034
+ ETHERTYPE_CHAOS = 0x804
+ ETHERTYPE_COMDESIGN = 0x806c
+ ETHERTYPE_COMPUGRAPHIC = 0x806d
+ ETHERTYPE_COUNTERPOINT = 0x8062
+ ETHERTYPE_CRONUS = 0x8004
+ ETHERTYPE_CRONUSVLN = 0x8003
+ ETHERTYPE_DCA = 0x1234
+ ETHERTYPE_DDE = 0x807b
+ ETHERTYPE_DEBNI = 0xaaaa
+ ETHERTYPE_DECAM = 0x8048
+ ETHERTYPE_DECCUST = 0x6006
+ ETHERTYPE_DECDIAG = 0x6005
+ ETHERTYPE_DECDNS = 0x803c
+ ETHERTYPE_DECDTS = 0x803e
+ ETHERTYPE_DECEXPER = 0x6000
+ ETHERTYPE_DECLAST = 0x8041
+ ETHERTYPE_DECLTM = 0x803f
+ ETHERTYPE_DECMUMPS = 0x6009
+ ETHERTYPE_DECNETBIOS = 0x8040
+ ETHERTYPE_DELTACON = 0x86de
+ ETHERTYPE_DIDDLE = 0x4321
+ ETHERTYPE_DLOG1 = 0x660
+ ETHERTYPE_DLOG2 = 0x661
+ ETHERTYPE_DN = 0x6003
+ ETHERTYPE_DOGFIGHT = 0x1989
+ ETHERTYPE_DSMD = 0x8039
+ ETHERTYPE_ECMA = 0x803
+ ETHERTYPE_ENCRYPT = 0x803d
+ ETHERTYPE_ES = 0x805d
+ ETHERTYPE_EXCELAN = 0x8010
+ ETHERTYPE_EXPERDATA = 0x8049
+ ETHERTYPE_FLIP = 0x8146
+ ETHERTYPE_FLOWCONTROL = 0x8808
+ ETHERTYPE_FRARP = 0x808
+ ETHERTYPE_GENDYN = 0x8068
+ ETHERTYPE_HAYES = 0x8130
+ ETHERTYPE_HIPPI_FP = 0x8180
+ ETHERTYPE_HITACHI = 0x8820
+ ETHERTYPE_HP = 0x8005
+ ETHERTYPE_IEEEPUP = 0xa00
+ ETHERTYPE_IEEEPUPAT = 0xa01
+ ETHERTYPE_IMLBL = 0x4c42
+ ETHERTYPE_IMLBLDIAG = 0x424c
+ ETHERTYPE_IP = 0x800
+ ETHERTYPE_IPAS = 0x876c
+ ETHERTYPE_IPV6 = 0x86dd
+ ETHERTYPE_IPX = 0x8137
+ ETHERTYPE_IPXNEW = 0x8037
+ ETHERTYPE_KALPANA = 0x8582
+ ETHERTYPE_LANBRIDGE = 0x8038
+ ETHERTYPE_LANPROBE = 0x8888
+ ETHERTYPE_LAT = 0x6004
+ ETHERTYPE_LBACK = 0x9000
+ ETHERTYPE_LITTLE = 0x8060
+ ETHERTYPE_LOGICRAFT = 0x8148
+ ETHERTYPE_LOOPBACK = 0x9000
+ ETHERTYPE_MATRA = 0x807a
+ ETHERTYPE_MAX = 0xffff
+ ETHERTYPE_MERIT = 0x807c
+ ETHERTYPE_MICP = 0x873a
+ ETHERTYPE_MOPDL = 0x6001
+ ETHERTYPE_MOPRC = 0x6002
+ ETHERTYPE_MOTOROLA = 0x818d
+ ETHERTYPE_MPLS = 0x8847
+ ETHERTYPE_MPLS_MCAST = 0x8848
+ ETHERTYPE_MUMPS = 0x813f
+ ETHERTYPE_NBPCC = 0x3c04
+ ETHERTYPE_NBPCLAIM = 0x3c09
+ ETHERTYPE_NBPCLREQ = 0x3c05
+ ETHERTYPE_NBPCLRSP = 0x3c06
+ ETHERTYPE_NBPCREQ = 0x3c02
+ ETHERTYPE_NBPCRSP = 0x3c03
+ ETHERTYPE_NBPDG = 0x3c07
+ ETHERTYPE_NBPDGB = 0x3c08
+ ETHERTYPE_NBPDLTE = 0x3c0a
+ ETHERTYPE_NBPRAR = 0x3c0c
+ ETHERTYPE_NBPRAS = 0x3c0b
+ ETHERTYPE_NBPRST = 0x3c0d
+ ETHERTYPE_NBPSCD = 0x3c01
+ ETHERTYPE_NBPVCD = 0x3c00
+ ETHERTYPE_NBS = 0x802
+ ETHERTYPE_NCD = 0x8149
+ ETHERTYPE_NESTAR = 0x8006
+ ETHERTYPE_NETBEUI = 0x8191
+ ETHERTYPE_NOVELL = 0x8138
+ ETHERTYPE_NS = 0x600
+ ETHERTYPE_NSAT = 0x601
+ ETHERTYPE_NSCOMPAT = 0x807
+ ETHERTYPE_NTRAILER = 0x10
+ ETHERTYPE_OS9 = 0x7007
+ ETHERTYPE_OS9NET = 0x7009
+ ETHERTYPE_PACER = 0x80c6
+ ETHERTYPE_PAE = 0x888e
+ ETHERTYPE_PCS = 0x4242
+ ETHERTYPE_PLANNING = 0x8044
+ ETHERTYPE_PPP = 0x880b
+ ETHERTYPE_PPPOE = 0x8864
+ ETHERTYPE_PPPOEDISC = 0x8863
+ ETHERTYPE_PRIMENTS = 0x7031
+ ETHERTYPE_PUP = 0x200
+ ETHERTYPE_PUPAT = 0x200
+ ETHERTYPE_RACAL = 0x7030
+ ETHERTYPE_RATIONAL = 0x8150
+ ETHERTYPE_RAWFR = 0x6559
+ ETHERTYPE_RCL = 0x1995
+ ETHERTYPE_RDP = 0x8739
+ ETHERTYPE_RETIX = 0x80f2
+ ETHERTYPE_REVARP = 0x8035
+ ETHERTYPE_SCA = 0x6007
+ ETHERTYPE_SECTRA = 0x86db
+ ETHERTYPE_SECUREDATA = 0x876d
+ ETHERTYPE_SGITW = 0x817e
+ ETHERTYPE_SG_BOUNCE = 0x8016
+ ETHERTYPE_SG_DIAG = 0x8013
+ ETHERTYPE_SG_NETGAMES = 0x8014
+ ETHERTYPE_SG_RESV = 0x8015
+ ETHERTYPE_SIMNET = 0x5208
+ ETHERTYPE_SLOWPROTOCOLS = 0x8809
+ ETHERTYPE_SNA = 0x80d5
+ ETHERTYPE_SNMP = 0x814c
+ ETHERTYPE_SONIX = 0xfaf5
+ ETHERTYPE_SPIDER = 0x809f
+ ETHERTYPE_SPRITE = 0x500
+ ETHERTYPE_STP = 0x8181
+ ETHERTYPE_TALARIS = 0x812b
+ ETHERTYPE_TALARISMC = 0x852b
+ ETHERTYPE_TCPCOMP = 0x876b
+ ETHERTYPE_TCPSM = 0x9002
+ ETHERTYPE_TEC = 0x814f
+ ETHERTYPE_TIGAN = 0x802f
+ ETHERTYPE_TRAIL = 0x1000
+ ETHERTYPE_TRANSETHER = 0x6558
+ ETHERTYPE_TYMSHARE = 0x802e
+ ETHERTYPE_UBBST = 0x7005
+ ETHERTYPE_UBDEBUG = 0x900
+ ETHERTYPE_UBDIAGLOOP = 0x7002
+ ETHERTYPE_UBDL = 0x7000
+ ETHERTYPE_UBNIU = 0x7001
+ ETHERTYPE_UBNMC = 0x7003
+ ETHERTYPE_VALID = 0x1600
+ ETHERTYPE_VARIAN = 0x80dd
+ ETHERTYPE_VAXELN = 0x803b
+ ETHERTYPE_VEECO = 0x8067
+ ETHERTYPE_VEXP = 0x805b
+ ETHERTYPE_VGLAB = 0x8131
+ ETHERTYPE_VINES = 0xbad
+ ETHERTYPE_VINESECHO = 0xbaf
+ ETHERTYPE_VINESLOOP = 0xbae
+ ETHERTYPE_VITAL = 0xff00
+ ETHERTYPE_VLAN = 0x8100
+ ETHERTYPE_VLTLMAN = 0x8080
+ ETHERTYPE_VPROD = 0x805c
+ ETHERTYPE_VURESERVED = 0x8147
+ ETHERTYPE_WATERLOO = 0x8130
+ ETHERTYPE_WELLFLEET = 0x8103
+ ETHERTYPE_X25 = 0x805
+ ETHERTYPE_X75 = 0x801
+ ETHERTYPE_XNSSM = 0x9001
+ ETHERTYPE_XTP = 0x817d
+ ETHER_ADDR_LEN = 0x6
+ ETHER_CRC_LEN = 0x4
+ ETHER_CRC_POLY_BE = 0x4c11db6
+ ETHER_CRC_POLY_LE = 0xedb88320
+ ETHER_HDR_LEN = 0xe
+ ETHER_MAX_LEN = 0x5ee
+ ETHER_MAX_LEN_JUMBO = 0x233a
+ ETHER_MIN_LEN = 0x40
+ ETHER_PPPOE_ENCAP_LEN = 0x8
+ ETHER_TYPE_LEN = 0x2
+ ETHER_VLAN_ENCAP_LEN = 0x4
+ EVFILT_AIO = 0x2
+ EVFILT_PROC = 0x4
+ EVFILT_READ = 0x0
+ EVFILT_SIGNAL = 0x5
+ EVFILT_SYSCOUNT = 0x7
+ EVFILT_TIMER = 0x6
+ EVFILT_VNODE = 0x3
+ EVFILT_WRITE = 0x1
+ EV_ADD = 0x1
+ EV_CLEAR = 0x20
+ EV_DELETE = 0x2
+ EV_DISABLE = 0x8
+ EV_ENABLE = 0x4
+ EV_EOF = 0x8000
+ EV_ERROR = 0x4000
+ EV_FLAG1 = 0x2000
+ EV_ONESHOT = 0x10
+ EV_SYSFLAGS = 0xf000
+ EXTA = 0x4b00
+ EXTB = 0x9600
+ EXTPROC = 0x800
+ FD_CLOEXEC = 0x1
+ FD_SETSIZE = 0x100
+ FLUSHO = 0x800000
+ F_CLOSEM = 0xa
+ F_DUPFD = 0x0
+ F_DUPFD_CLOEXEC = 0xc
+ F_FSCTL = -0x80000000
+ F_FSDIRMASK = 0x70000000
+ F_FSIN = 0x10000000
+ F_FSINOUT = 0x30000000
+ F_FSOUT = 0x20000000
+ F_FSPRIV = 0x8000
+ F_FSVOID = 0x40000000
+ F_GETFD = 0x1
+ F_GETFL = 0x3
+ F_GETLK = 0x7
+ F_GETNOSIGPIPE = 0xd
+ F_GETOWN = 0x5
+ F_MAXFD = 0xb
+ F_OK = 0x0
+ F_PARAM_MASK = 0xfff
+ F_PARAM_MAX = 0xfff
+ F_RDLCK = 0x1
+ F_SETFD = 0x2
+ F_SETFL = 0x4
+ F_SETLK = 0x8
+ F_SETLKW = 0x9
+ F_SETNOSIGPIPE = 0xe
+ F_SETOWN = 0x6
+ F_UNLCK = 0x2
+ F_WRLCK = 0x3
+ HUPCL = 0x4000
+ ICANON = 0x100
+ ICRNL = 0x100
+ IEXTEN = 0x400
+ IFAN_ARRIVAL = 0x0
+ IFAN_DEPARTURE = 0x1
+ IFA_ROUTE = 0x1
+ IFF_ALLMULTI = 0x200
+ IFF_BROADCAST = 0x2
+ IFF_CANTCHANGE = 0x8f52
+ IFF_DEBUG = 0x4
+ IFF_LINK0 = 0x1000
+ IFF_LINK1 = 0x2000
+ IFF_LINK2 = 0x4000
+ IFF_LOOPBACK = 0x8
+ IFF_MULTICAST = 0x8000
+ IFF_NOARP = 0x80
+ IFF_NOTRAILERS = 0x20
+ IFF_OACTIVE = 0x400
+ IFF_POINTOPOINT = 0x10
+ IFF_PROMISC = 0x100
+ IFF_RUNNING = 0x40
+ IFF_SIMPLEX = 0x800
+ IFF_UP = 0x1
+ IFNAMSIZ = 0x10
+ IFT_1822 = 0x2
+ IFT_A12MPPSWITCH = 0x82
+ IFT_AAL2 = 0xbb
+ IFT_AAL5 = 0x31
+ IFT_ADSL = 0x5e
+ IFT_AFLANE8023 = 0x3b
+ IFT_AFLANE8025 = 0x3c
+ IFT_ARAP = 0x58
+ IFT_ARCNET = 0x23
+ IFT_ARCNETPLUS = 0x24
+ IFT_ASYNC = 0x54
+ IFT_ATM = 0x25
+ IFT_ATMDXI = 0x69
+ IFT_ATMFUNI = 0x6a
+ IFT_ATMIMA = 0x6b
+ IFT_ATMLOGICAL = 0x50
+ IFT_ATMRADIO = 0xbd
+ IFT_ATMSUBINTERFACE = 0x86
+ IFT_ATMVCIENDPT = 0xc2
+ IFT_ATMVIRTUAL = 0x95
+ IFT_BGPPOLICYACCOUNTING = 0xa2
+ IFT_BRIDGE = 0xd1
+ IFT_BSC = 0x53
+ IFT_CARP = 0xf8
+ IFT_CCTEMUL = 0x3d
+ IFT_CEPT = 0x13
+ IFT_CES = 0x85
+ IFT_CHANNEL = 0x46
+ IFT_CNR = 0x55
+ IFT_COFFEE = 0x84
+ IFT_COMPOSITELINK = 0x9b
+ IFT_DCN = 0x8d
+ IFT_DIGITALPOWERLINE = 0x8a
+ IFT_DIGITALWRAPPEROVERHEADCHANNEL = 0xba
+ IFT_DLSW = 0x4a
+ IFT_DOCSCABLEDOWNSTREAM = 0x80
+ IFT_DOCSCABLEMACLAYER = 0x7f
+ IFT_DOCSCABLEUPSTREAM = 0x81
+ IFT_DOCSCABLEUPSTREAMCHANNEL = 0xcd
+ IFT_DS0 = 0x51
+ IFT_DS0BUNDLE = 0x52
+ IFT_DS1FDL = 0xaa
+ IFT_DS3 = 0x1e
+ IFT_DTM = 0x8c
+ IFT_DVBASILN = 0xac
+ IFT_DVBASIOUT = 0xad
+ IFT_DVBRCCDOWNSTREAM = 0x93
+ IFT_DVBRCCMACLAYER = 0x92
+ IFT_DVBRCCUPSTREAM = 0x94
+ IFT_ECONET = 0xce
+ IFT_EON = 0x19
+ IFT_EPLRS = 0x57
+ IFT_ESCON = 0x49
+ IFT_ETHER = 0x6
+ IFT_FAITH = 0xf2
+ IFT_FAST = 0x7d
+ IFT_FASTETHER = 0x3e
+ IFT_FASTETHERFX = 0x45
+ IFT_FDDI = 0xf
+ IFT_FIBRECHANNEL = 0x38
+ IFT_FRAMERELAYINTERCONNECT = 0x3a
+ IFT_FRAMERELAYMPI = 0x5c
+ IFT_FRDLCIENDPT = 0xc1
+ IFT_FRELAY = 0x20
+ IFT_FRELAYDCE = 0x2c
+ IFT_FRF16MFRBUNDLE = 0xa3
+ IFT_FRFORWARD = 0x9e
+ IFT_G703AT2MB = 0x43
+ IFT_G703AT64K = 0x42
+ IFT_GIF = 0xf0
+ IFT_GIGABITETHERNET = 0x75
+ IFT_GR303IDT = 0xb2
+ IFT_GR303RDT = 0xb1
+ IFT_H323GATEKEEPER = 0xa4
+ IFT_H323PROXY = 0xa5
+ IFT_HDH1822 = 0x3
+ IFT_HDLC = 0x76
+ IFT_HDSL2 = 0xa8
+ IFT_HIPERLAN2 = 0xb7
+ IFT_HIPPI = 0x2f
+ IFT_HIPPIINTERFACE = 0x39
+ IFT_HOSTPAD = 0x5a
+ IFT_HSSI = 0x2e
+ IFT_HY = 0xe
+ IFT_IBM370PARCHAN = 0x48
+ IFT_IDSL = 0x9a
+ IFT_IEEE1394 = 0x90
+ IFT_IEEE80211 = 0x47
+ IFT_IEEE80212 = 0x37
+ IFT_IEEE8023ADLAG = 0xa1
+ IFT_IFGSN = 0x91
+ IFT_IMT = 0xbe
+ IFT_INFINIBAND = 0xc7
+ IFT_INTERLEAVE = 0x7c
+ IFT_IP = 0x7e
+ IFT_IPFORWARD = 0x8e
+ IFT_IPOVERATM = 0x72
+ IFT_IPOVERCDLC = 0x6d
+ IFT_IPOVERCLAW = 0x6e
+ IFT_IPSWITCH = 0x4e
+ IFT_ISDN = 0x3f
+ IFT_ISDNBASIC = 0x14
+ IFT_ISDNPRIMARY = 0x15
+ IFT_ISDNS = 0x4b
+ IFT_ISDNU = 0x4c
+ IFT_ISO88022LLC = 0x29
+ IFT_ISO88023 = 0x7
+ IFT_ISO88024 = 0x8
+ IFT_ISO88025 = 0x9
+ IFT_ISO88025CRFPINT = 0x62
+ IFT_ISO88025DTR = 0x56
+ IFT_ISO88025FIBER = 0x73
+ IFT_ISO88026 = 0xa
+ IFT_ISUP = 0xb3
+ IFT_L2VLAN = 0x87
+ IFT_L3IPVLAN = 0x88
+ IFT_L3IPXVLAN = 0x89
+ IFT_LAPB = 0x10
+ IFT_LAPD = 0x4d
+ IFT_LAPF = 0x77
+ IFT_LINEGROUP = 0xd2
+ IFT_LOCALTALK = 0x2a
+ IFT_LOOP = 0x18
+ IFT_MEDIAMAILOVERIP = 0x8b
+ IFT_MFSIGLINK = 0xa7
+ IFT_MIOX25 = 0x26
+ IFT_MODEM = 0x30
+ IFT_MPC = 0x71
+ IFT_MPLS = 0xa6
+ IFT_MPLSTUNNEL = 0x96
+ IFT_MSDSL = 0x8f
+ IFT_MVL = 0xbf
+ IFT_MYRINET = 0x63
+ IFT_NFAS = 0xaf
+ IFT_NSIP = 0x1b
+ IFT_OPTICALCHANNEL = 0xc3
+ IFT_OPTICALTRANSPORT = 0xc4
+ IFT_OTHER = 0x1
+ IFT_P10 = 0xc
+ IFT_P80 = 0xd
+ IFT_PARA = 0x22
+ IFT_PFLOG = 0xf5
+ IFT_PFSYNC = 0xf6
+ IFT_PLC = 0xae
+ IFT_PON155 = 0xcf
+ IFT_PON622 = 0xd0
+ IFT_POS = 0xab
+ IFT_PPP = 0x17
+ IFT_PPPMULTILINKBUNDLE = 0x6c
+ IFT_PROPATM = 0xc5
+ IFT_PROPBWAP2MP = 0xb8
+ IFT_PROPCNLS = 0x59
+ IFT_PROPDOCSWIRELESSDOWNSTREAM = 0xb5
+ IFT_PROPDOCSWIRELESSMACLAYER = 0xb4
+ IFT_PROPDOCSWIRELESSUPSTREAM = 0xb6
+ IFT_PROPMUX = 0x36
+ IFT_PROPVIRTUAL = 0x35
+ IFT_PROPWIRELESSP2P = 0x9d
+ IFT_PTPSERIAL = 0x16
+ IFT_PVC = 0xf1
+ IFT_Q2931 = 0xc9
+ IFT_QLLC = 0x44
+ IFT_RADIOMAC = 0xbc
+ IFT_RADSL = 0x5f
+ IFT_REACHDSL = 0xc0
+ IFT_RFC1483 = 0x9f
+ IFT_RS232 = 0x21
+ IFT_RSRB = 0x4f
+ IFT_SDLC = 0x11
+ IFT_SDSL = 0x60
+ IFT_SHDSL = 0xa9
+ IFT_SIP = 0x1f
+ IFT_SIPSIG = 0xcc
+ IFT_SIPTG = 0xcb
+ IFT_SLIP = 0x1c
+ IFT_SMDSDXI = 0x2b
+ IFT_SMDSICIP = 0x34
+ IFT_SONET = 0x27
+ IFT_SONETOVERHEADCHANNEL = 0xb9
+ IFT_SONETPATH = 0x32
+ IFT_SONETVT = 0x33
+ IFT_SRP = 0x97
+ IFT_SS7SIGLINK = 0x9c
+ IFT_STACKTOSTACK = 0x6f
+ IFT_STARLAN = 0xb
+ IFT_STF = 0xd7
+ IFT_T1 = 0x12
+ IFT_TDLC = 0x74
+ IFT_TELINK = 0xc8
+ IFT_TERMPAD = 0x5b
+ IFT_TR008 = 0xb0
+ IFT_TRANSPHDLC = 0x7b
+ IFT_TUNNEL = 0x83
+ IFT_ULTRA = 0x1d
+ IFT_USB = 0xa0
+ IFT_V11 = 0x40
+ IFT_V35 = 0x2d
+ IFT_V36 = 0x41
+ IFT_V37 = 0x78
+ IFT_VDSL = 0x61
+ IFT_VIRTUALIPADDRESS = 0x70
+ IFT_VIRTUALTG = 0xca
+ IFT_VOICEDID = 0xd5
+ IFT_VOICEEM = 0x64
+ IFT_VOICEEMFGD = 0xd3
+ IFT_VOICEENCAP = 0x67
+ IFT_VOICEFGDEANA = 0xd4
+ IFT_VOICEFXO = 0x65
+ IFT_VOICEFXS = 0x66
+ IFT_VOICEOVERATM = 0x98
+ IFT_VOICEOVERCABLE = 0xc6
+ IFT_VOICEOVERFRAMERELAY = 0x99
+ IFT_VOICEOVERIP = 0x68
+ IFT_X213 = 0x5d
+ IFT_X25 = 0x5
+ IFT_X25DDN = 0x4
+ IFT_X25HUNTGROUP = 0x7a
+ IFT_X25MLP = 0x79
+ IFT_X25PLE = 0x28
+ IFT_XETHER = 0x1a
+ IGNBRK = 0x1
+ IGNCR = 0x80
+ IGNPAR = 0x4
+ IMAXBEL = 0x2000
+ INLCR = 0x40
+ INPCK = 0x10
+ IN_CLASSA_HOST = 0xffffff
+ IN_CLASSA_MAX = 0x80
+ IN_CLASSA_NET = 0xff000000
+ IN_CLASSA_NSHIFT = 0x18
+ IN_CLASSB_HOST = 0xffff
+ IN_CLASSB_MAX = 0x10000
+ IN_CLASSB_NET = 0xffff0000
+ IN_CLASSB_NSHIFT = 0x10
+ IN_CLASSC_HOST = 0xff
+ IN_CLASSC_NET = 0xffffff00
+ IN_CLASSC_NSHIFT = 0x8
+ IN_CLASSD_HOST = 0xfffffff
+ IN_CLASSD_NET = 0xf0000000
+ IN_CLASSD_NSHIFT = 0x1c
+ IN_LOOPBACKNET = 0x7f
+ IPPROTO_AH = 0x33
+ IPPROTO_CARP = 0x70
+ IPPROTO_DONE = 0x101
+ IPPROTO_DSTOPTS = 0x3c
+ IPPROTO_EGP = 0x8
+ IPPROTO_ENCAP = 0x62
+ IPPROTO_EON = 0x50
+ IPPROTO_ESP = 0x32
+ IPPROTO_ETHERIP = 0x61
+ IPPROTO_FRAGMENT = 0x2c
+ IPPROTO_GGP = 0x3
+ IPPROTO_GRE = 0x2f
+ IPPROTO_HOPOPTS = 0x0
+ IPPROTO_ICMP = 0x1
+ IPPROTO_ICMPV6 = 0x3a
+ IPPROTO_IDP = 0x16
+ IPPROTO_IGMP = 0x2
+ IPPROTO_IP = 0x0
+ IPPROTO_IPCOMP = 0x6c
+ IPPROTO_IPIP = 0x4
+ IPPROTO_IPV4 = 0x4
+ IPPROTO_IPV6 = 0x29
+ IPPROTO_IPV6_ICMP = 0x3a
+ IPPROTO_MAX = 0x100
+ IPPROTO_MAXID = 0x34
+ IPPROTO_MOBILE = 0x37
+ IPPROTO_NONE = 0x3b
+ IPPROTO_PFSYNC = 0xf0
+ IPPROTO_PIM = 0x67
+ IPPROTO_PUP = 0xc
+ IPPROTO_RAW = 0xff
+ IPPROTO_ROUTING = 0x2b
+ IPPROTO_RSVP = 0x2e
+ IPPROTO_TCP = 0x6
+ IPPROTO_TP = 0x1d
+ IPPROTO_UDP = 0x11
+ IPPROTO_VRRP = 0x70
+ IPV6_CHECKSUM = 0x1a
+ IPV6_DEFAULT_MULTICAST_HOPS = 0x1
+ IPV6_DEFAULT_MULTICAST_LOOP = 0x1
+ IPV6_DEFHLIM = 0x40
+ IPV6_DONTFRAG = 0x3e
+ IPV6_DSTOPTS = 0x32
+ IPV6_FAITH = 0x1d
+ IPV6_FLOWINFO_MASK = 0xffffff0f
+ IPV6_FLOWLABEL_MASK = 0xffff0f00
+ IPV6_FRAGTTL = 0x78
+ IPV6_HLIMDEC = 0x1
+ IPV6_HOPLIMIT = 0x2f
+ IPV6_HOPOPTS = 0x31
+ IPV6_IPSEC_POLICY = 0x1c
+ IPV6_JOIN_GROUP = 0xc
+ IPV6_LEAVE_GROUP = 0xd
+ IPV6_MAXHLIM = 0xff
+ IPV6_MAXPACKET = 0xffff
+ IPV6_MMTU = 0x500
+ IPV6_MULTICAST_HOPS = 0xa
+ IPV6_MULTICAST_IF = 0x9
+ IPV6_MULTICAST_LOOP = 0xb
+ IPV6_NEXTHOP = 0x30
+ IPV6_PATHMTU = 0x2c
+ IPV6_PKTINFO = 0x2e
+ IPV6_PORTRANGE = 0xe
+ IPV6_PORTRANGE_DEFAULT = 0x0
+ IPV6_PORTRANGE_HIGH = 0x1
+ IPV6_PORTRANGE_LOW = 0x2
+ IPV6_RECVDSTOPTS = 0x28
+ IPV6_RECVHOPLIMIT = 0x25
+ IPV6_RECVHOPOPTS = 0x27
+ IPV6_RECVPATHMTU = 0x2b
+ IPV6_RECVPKTINFO = 0x24
+ IPV6_RECVRTHDR = 0x26
+ IPV6_RECVTCLASS = 0x39
+ IPV6_RTHDR = 0x33
+ IPV6_RTHDRDSTOPTS = 0x23
+ IPV6_RTHDR_LOOSE = 0x0
+ IPV6_RTHDR_STRICT = 0x1
+ IPV6_RTHDR_TYPE_0 = 0x0
+ IPV6_SOCKOPT_RESERVED1 = 0x3
+ IPV6_TCLASS = 0x3d
+ IPV6_UNICAST_HOPS = 0x4
+ IPV6_USE_MIN_MTU = 0x2a
+ IPV6_V6ONLY = 0x1b
+ IPV6_VERSION = 0x60
+ IPV6_VERSION_MASK = 0xf0
+ IP_ADD_MEMBERSHIP = 0xc
+ IP_DEFAULT_MULTICAST_LOOP = 0x1
+ IP_DEFAULT_MULTICAST_TTL = 0x1
+ IP_DF = 0x4000
+ IP_DROP_MEMBERSHIP = 0xd
+ IP_EF = 0x8000
+ IP_ERRORMTU = 0x15
+ IP_HDRINCL = 0x2
+ IP_IPSEC_POLICY = 0x16
+ IP_MAXPACKET = 0xffff
+ IP_MAX_MEMBERSHIPS = 0x14
+ IP_MF = 0x2000
+ IP_MINFRAGSIZE = 0x45
+ IP_MINTTL = 0x18
+ IP_MSS = 0x240
+ IP_MULTICAST_IF = 0x9
+ IP_MULTICAST_LOOP = 0xb
+ IP_MULTICAST_TTL = 0xa
+ IP_OFFMASK = 0x1fff
+ IP_OPTIONS = 0x1
+ IP_PORTRANGE = 0x13
+ IP_PORTRANGE_DEFAULT = 0x0
+ IP_PORTRANGE_HIGH = 0x1
+ IP_PORTRANGE_LOW = 0x2
+ IP_RECVDSTADDR = 0x7
+ IP_RECVIF = 0x14
+ IP_RECVOPTS = 0x5
+ IP_RECVRETOPTS = 0x6
+ IP_RECVTTL = 0x17
+ IP_RETOPTS = 0x8
+ IP_RF = 0x8000
+ IP_TOS = 0x3
+ IP_TTL = 0x4
+ ISIG = 0x80
+ ISTRIP = 0x20
+ IXANY = 0x800
+ IXOFF = 0x400
+ IXON = 0x200
+ LOCK_EX = 0x2
+ LOCK_NB = 0x4
+ LOCK_SH = 0x1
+ LOCK_UN = 0x8
+ MSG_BCAST = 0x100
+ MSG_CMSG_CLOEXEC = 0x800
+ MSG_CONTROLMBUF = 0x2000000
+ MSG_CTRUNC = 0x20
+ MSG_DONTROUTE = 0x4
+ MSG_DONTWAIT = 0x80
+ MSG_EOR = 0x8
+ MSG_IOVUSRSPACE = 0x4000000
+ MSG_LENUSRSPACE = 0x8000000
+ MSG_MCAST = 0x200
+ MSG_NAMEMBUF = 0x1000000
+ MSG_NBIO = 0x1000
+ MSG_NOSIGNAL = 0x400
+ MSG_OOB = 0x1
+ MSG_PEEK = 0x2
+ MSG_TRUNC = 0x10
+ MSG_USERFLAGS = 0xffffff
+ MSG_WAITALL = 0x40
+ NAME_MAX = 0x1ff
+ NET_RT_DUMP = 0x1
+ NET_RT_FLAGS = 0x2
+ NET_RT_IFLIST = 0x5
+ NET_RT_MAXID = 0x6
+ NET_RT_OIFLIST = 0x4
+ NET_RT_OOIFLIST = 0x3
+ NOFLSH = 0x80000000
+ NOTE_ATTRIB = 0x8
+ NOTE_CHILD = 0x4
+ NOTE_DELETE = 0x1
+ NOTE_EXEC = 0x20000000
+ NOTE_EXIT = 0x80000000
+ NOTE_EXTEND = 0x4
+ NOTE_FORK = 0x40000000
+ NOTE_LINK = 0x10
+ NOTE_LOWAT = 0x1
+ NOTE_PCTRLMASK = 0xf0000000
+ NOTE_PDATAMASK = 0xfffff
+ NOTE_RENAME = 0x20
+ NOTE_REVOKE = 0x40
+ NOTE_TRACK = 0x1
+ NOTE_TRACKERR = 0x2
+ NOTE_WRITE = 0x2
+ OCRNL = 0x10
+ OFIOGETBMAP = 0xc004667a
+ ONLCR = 0x2
+ ONLRET = 0x40
+ ONOCR = 0x20
+ ONOEOT = 0x8
+ OPOST = 0x1
+ O_ACCMODE = 0x3
+ O_ALT_IO = 0x40000
+ O_APPEND = 0x8
+ O_ASYNC = 0x40
+ O_CLOEXEC = 0x400000
+ O_CREAT = 0x200
+ O_DIRECT = 0x80000
+ O_DIRECTORY = 0x200000
+ O_DSYNC = 0x10000
+ O_EXCL = 0x800
+ O_EXLOCK = 0x20
+ O_FSYNC = 0x80
+ O_NDELAY = 0x4
+ O_NOCTTY = 0x8000
+ O_NOFOLLOW = 0x100
+ O_NONBLOCK = 0x4
+ O_NOSIGPIPE = 0x1000000
+ O_RDONLY = 0x0
+ O_RDWR = 0x2
+ O_RSYNC = 0x20000
+ O_SHLOCK = 0x10
+ O_SYNC = 0x80
+ O_TRUNC = 0x400
+ O_WRONLY = 0x1
+ PARENB = 0x1000
+ PARMRK = 0x8
+ PARODD = 0x2000
+ PENDIN = 0x20000000
+ PRI_IOFLUSH = 0x7c
+ RLIMIT_AS = 0xa
+ RLIMIT_CORE = 0x4
+ RLIMIT_CPU = 0x0
+ RLIMIT_DATA = 0x2
+ RLIMIT_FSIZE = 0x1
+ RLIMIT_NOFILE = 0x8
+ RLIMIT_STACK = 0x3
+ RLIM_INFINITY = 0x7fffffffffffffff
+ RTAX_AUTHOR = 0x6
+ RTAX_BRD = 0x7
+ RTAX_DST = 0x0
+ RTAX_GATEWAY = 0x1
+ RTAX_GENMASK = 0x3
+ RTAX_IFA = 0x5
+ RTAX_IFP = 0x4
+ RTAX_MAX = 0x9
+ RTAX_NETMASK = 0x2
+ RTAX_TAG = 0x8
+ RTA_AUTHOR = 0x40
+ RTA_BRD = 0x80
+ RTA_DST = 0x1
+ RTA_GATEWAY = 0x2
+ RTA_GENMASK = 0x8
+ RTA_IFA = 0x20
+ RTA_IFP = 0x10
+ RTA_NETMASK = 0x4
+ RTA_TAG = 0x100
+ RTF_ANNOUNCE = 0x20000
+ RTF_BLACKHOLE = 0x1000
+ RTF_CLONED = 0x2000
+ RTF_CLONING = 0x100
+ RTF_DONE = 0x40
+ RTF_DYNAMIC = 0x10
+ RTF_GATEWAY = 0x2
+ RTF_HOST = 0x4
+ RTF_LLINFO = 0x400
+ RTF_MASK = 0x80
+ RTF_MODIFIED = 0x20
+ RTF_PROTO1 = 0x8000
+ RTF_PROTO2 = 0x4000
+ RTF_REJECT = 0x8
+ RTF_SRC = 0x10000
+ RTF_STATIC = 0x800
+ RTF_UP = 0x1
+ RTF_XRESOLVE = 0x200
+ RTM_ADD = 0x1
+ RTM_CHANGE = 0x3
+ RTM_CHGADDR = 0x15
+ RTM_DELADDR = 0xd
+ RTM_DELETE = 0x2
+ RTM_GET = 0x4
+ RTM_IEEE80211 = 0x11
+ RTM_IFANNOUNCE = 0x10
+ RTM_IFINFO = 0x14
+ RTM_LLINFO_UPD = 0x13
+ RTM_LOCK = 0x8
+ RTM_LOSING = 0x5
+ RTM_MISS = 0x7
+ RTM_NEWADDR = 0xc
+ RTM_OIFINFO = 0xf
+ RTM_OLDADD = 0x9
+ RTM_OLDDEL = 0xa
+ RTM_OOIFINFO = 0xe
+ RTM_REDIRECT = 0x6
+ RTM_RESOLVE = 0xb
+ RTM_RTTUNIT = 0xf4240
+ RTM_SETGATE = 0x12
+ RTM_VERSION = 0x4
+ RTV_EXPIRE = 0x4
+ RTV_HOPCOUNT = 0x2
+ RTV_MTU = 0x1
+ RTV_RPIPE = 0x8
+ RTV_RTT = 0x40
+ RTV_RTTVAR = 0x80
+ RTV_SPIPE = 0x10
+ RTV_SSTHRESH = 0x20
+ RUSAGE_CHILDREN = -0x1
+ RUSAGE_SELF = 0x0
+ SCM_CREDS = 0x4
+ SCM_RIGHTS = 0x1
+ SCM_TIMESTAMP = 0x8
+ SHUT_RD = 0x0
+ SHUT_RDWR = 0x2
+ SHUT_WR = 0x1
+ SIOCADDMULTI = 0x80906931
+ SIOCADDRT = 0x8030720a
+ SIOCAIFADDR = 0x8040691a
+ SIOCALIFADDR = 0x8118691c
+ SIOCATMARK = 0x40047307
+ SIOCDELMULTI = 0x80906932
+ SIOCDELRT = 0x8030720b
+ SIOCDIFADDR = 0x80906919
+ SIOCDIFPHYADDR = 0x80906949
+ SIOCDLIFADDR = 0x8118691e
+ SIOCGDRVSPEC = 0xc01c697b
+ SIOCGETPFSYNC = 0xc09069f8
+ SIOCGETSGCNT = 0xc0147534
+ SIOCGETVIFCNT = 0xc0147533
+ SIOCGHIWAT = 0x40047301
+ SIOCGIFADDR = 0xc0906921
+ SIOCGIFADDRPREF = 0xc0946920
+ SIOCGIFALIAS = 0xc040691b
+ SIOCGIFBRDADDR = 0xc0906923
+ SIOCGIFCAP = 0xc0206976
+ SIOCGIFCONF = 0xc0086926
+ SIOCGIFDATA = 0xc0946985
+ SIOCGIFDLT = 0xc0906977
+ SIOCGIFDSTADDR = 0xc0906922
+ SIOCGIFFLAGS = 0xc0906911
+ SIOCGIFGENERIC = 0xc090693a
+ SIOCGIFMEDIA = 0xc0286936
+ SIOCGIFMETRIC = 0xc0906917
+ SIOCGIFMTU = 0xc090697e
+ SIOCGIFNETMASK = 0xc0906925
+ SIOCGIFPDSTADDR = 0xc0906948
+ SIOCGIFPSRCADDR = 0xc0906947
+ SIOCGLIFADDR = 0xc118691d
+ SIOCGLIFPHYADDR = 0xc118694b
+ SIOCGLINKSTR = 0xc01c6987
+ SIOCGLOWAT = 0x40047303
+ SIOCGPGRP = 0x40047309
+ SIOCGVH = 0xc0906983
+ SIOCIFCREATE = 0x8090697a
+ SIOCIFDESTROY = 0x80906979
+ SIOCIFGCLONERS = 0xc00c6978
+ SIOCINITIFADDR = 0xc0446984
+ SIOCSDRVSPEC = 0x801c697b
+ SIOCSETPFSYNC = 0x809069f7
+ SIOCSHIWAT = 0x80047300
+ SIOCSIFADDR = 0x8090690c
+ SIOCSIFADDRPREF = 0x8094691f
+ SIOCSIFBRDADDR = 0x80906913
+ SIOCSIFCAP = 0x80206975
+ SIOCSIFDSTADDR = 0x8090690e
+ SIOCSIFFLAGS = 0x80906910
+ SIOCSIFGENERIC = 0x80906939
+ SIOCSIFMEDIA = 0xc0906935
+ SIOCSIFMETRIC = 0x80906918
+ SIOCSIFMTU = 0x8090697f
+ SIOCSIFNETMASK = 0x80906916
+ SIOCSIFPHYADDR = 0x80406946
+ SIOCSLIFPHYADDR = 0x8118694a
+ SIOCSLINKSTR = 0x801c6988
+ SIOCSLOWAT = 0x80047302
+ SIOCSPGRP = 0x80047308
+ SIOCSVH = 0xc0906982
+ SIOCZIFDATA = 0xc0946986
+ SOCK_CLOEXEC = 0x10000000
+ SOCK_DGRAM = 0x2
+ SOCK_FLAGS_MASK = 0xf0000000
+ SOCK_NONBLOCK = 0x20000000
+ SOCK_NOSIGPIPE = 0x40000000
+ SOCK_RAW = 0x3
+ SOCK_RDM = 0x4
+ SOCK_SEQPACKET = 0x5
+ SOCK_STREAM = 0x1
+ SOL_SOCKET = 0xffff
+ SOMAXCONN = 0x80
+ SO_ACCEPTCONN = 0x2
+ SO_ACCEPTFILTER = 0x1000
+ SO_BROADCAST = 0x20
+ SO_DEBUG = 0x1
+ SO_DONTROUTE = 0x10
+ SO_ERROR = 0x1007
+ SO_KEEPALIVE = 0x8
+ SO_LINGER = 0x80
+ SO_NOHEADER = 0x100a
+ SO_NOSIGPIPE = 0x800
+ SO_OOBINLINE = 0x100
+ SO_OVERFLOWED = 0x1009
+ SO_RCVBUF = 0x1002
+ SO_RCVLOWAT = 0x1004
+ SO_RCVTIMEO = 0x100c
+ SO_REUSEADDR = 0x4
+ SO_REUSEPORT = 0x200
+ SO_SNDBUF = 0x1001
+ SO_SNDLOWAT = 0x1003
+ SO_SNDTIMEO = 0x100b
+ SO_TIMESTAMP = 0x2000
+ SO_TYPE = 0x1008
+ SO_USELOOPBACK = 0x40
+ SYSCTL_VERSION = 0x1000000
+ SYSCTL_VERS_0 = 0x0
+ SYSCTL_VERS_1 = 0x1000000
+ SYSCTL_VERS_MASK = 0xff000000
+ S_ARCH1 = 0x10000
+ S_ARCH2 = 0x20000
+ S_BLKSIZE = 0x200
+ S_IEXEC = 0x40
+ S_IFBLK = 0x6000
+ S_IFCHR = 0x2000
+ S_IFDIR = 0x4000
+ S_IFIFO = 0x1000
+ S_IFLNK = 0xa000
+ S_IFMT = 0xf000
+ S_IFREG = 0x8000
+ S_IFSOCK = 0xc000
+ S_IFWHT = 0xe000
+ S_IREAD = 0x100
+ S_IRGRP = 0x20
+ S_IROTH = 0x4
+ S_IRUSR = 0x100
+ S_IRWXG = 0x38
+ S_IRWXO = 0x7
+ S_IRWXU = 0x1c0
+ S_ISGID = 0x400
+ S_ISTXT = 0x200
+ S_ISUID = 0x800
+ S_ISVTX = 0x200
+ S_IWGRP = 0x10
+ S_IWOTH = 0x2
+ S_IWRITE = 0x80
+ S_IWUSR = 0x80
+ S_IXGRP = 0x8
+ S_IXOTH = 0x1
+ S_IXUSR = 0x40
+ TCIFLUSH = 0x1
+ TCIOFLUSH = 0x3
+ TCOFLUSH = 0x2
+ TCP_CONGCTL = 0x20
+ TCP_KEEPCNT = 0x6
+ TCP_KEEPIDLE = 0x3
+ TCP_KEEPINIT = 0x7
+ TCP_KEEPINTVL = 0x5
+ TCP_MAXBURST = 0x4
+ TCP_MAXSEG = 0x2
+ TCP_MAXWIN = 0xffff
+ TCP_MAX_WINSHIFT = 0xe
+ TCP_MD5SIG = 0x10
+ TCP_MINMSS = 0xd8
+ TCP_MSS = 0x218
+ TCP_NODELAY = 0x1
+ TCSAFLUSH = 0x2
+ TIOCCBRK = 0x2000747a
+ TIOCCDTR = 0x20007478
+ TIOCCONS = 0x80047462
+ TIOCDCDTIMESTAMP = 0x400c7458
+ TIOCDRAIN = 0x2000745e
+ TIOCEXCL = 0x2000740d
+ TIOCEXT = 0x80047460
+ TIOCFLAG_CDTRCTS = 0x10
+ TIOCFLAG_CLOCAL = 0x2
+ TIOCFLAG_CRTSCTS = 0x4
+ TIOCFLAG_MDMBUF = 0x8
+ TIOCFLAG_SOFTCAR = 0x1
+ TIOCFLUSH = 0x80047410
+ TIOCGETA = 0x402c7413
+ TIOCGETD = 0x4004741a
+ TIOCGFLAGS = 0x4004745d
+ TIOCGLINED = 0x40207442
+ TIOCGPGRP = 0x40047477
+ TIOCGQSIZE = 0x40047481
+ TIOCGRANTPT = 0x20007447
+ TIOCGSID = 0x40047463
+ TIOCGSIZE = 0x40087468
+ TIOCGWINSZ = 0x40087468
+ TIOCMBIC = 0x8004746b
+ TIOCMBIS = 0x8004746c
+ TIOCMGET = 0x4004746a
+ TIOCMSET = 0x8004746d
+ TIOCM_CAR = 0x40
+ TIOCM_CD = 0x40
+ TIOCM_CTS = 0x20
+ TIOCM_DSR = 0x100
+ TIOCM_DTR = 0x2
+ TIOCM_LE = 0x1
+ TIOCM_RI = 0x80
+ TIOCM_RNG = 0x80
+ TIOCM_RTS = 0x4
+ TIOCM_SR = 0x10
+ TIOCM_ST = 0x8
+ TIOCNOTTY = 0x20007471
+ TIOCNXCL = 0x2000740e
+ TIOCOUTQ = 0x40047473
+ TIOCPKT = 0x80047470
+ TIOCPKT_DATA = 0x0
+ TIOCPKT_DOSTOP = 0x20
+ TIOCPKT_FLUSHREAD = 0x1
+ TIOCPKT_FLUSHWRITE = 0x2
+ TIOCPKT_IOCTL = 0x40
+ TIOCPKT_NOSTOP = 0x10
+ TIOCPKT_START = 0x8
+ TIOCPKT_STOP = 0x4
+ TIOCPTMGET = 0x48087446
+ TIOCPTSNAME = 0x48087448
+ TIOCRCVFRAME = 0x80047445
+ TIOCREMOTE = 0x80047469
+ TIOCSBRK = 0x2000747b
+ TIOCSCTTY = 0x20007461
+ TIOCSDTR = 0x20007479
+ TIOCSETA = 0x802c7414
+ TIOCSETAF = 0x802c7416
+ TIOCSETAW = 0x802c7415
+ TIOCSETD = 0x8004741b
+ TIOCSFLAGS = 0x8004745c
+ TIOCSIG = 0x2000745f
+ TIOCSLINED = 0x80207443
+ TIOCSPGRP = 0x80047476
+ TIOCSQSIZE = 0x80047480
+ TIOCSSIZE = 0x80087467
+ TIOCSTART = 0x2000746e
+ TIOCSTAT = 0x80047465
+ TIOCSTI = 0x80017472
+ TIOCSTOP = 0x2000746f
+ TIOCSWINSZ = 0x80087467
+ TIOCUCNTL = 0x80047466
+ TIOCXMTFRAME = 0x80047444
+ TOSTOP = 0x400000
+ VDISCARD = 0xf
+ VDSUSP = 0xb
+ VEOF = 0x0
+ VEOL = 0x1
+ VEOL2 = 0x2
+ VERASE = 0x3
+ VINTR = 0x8
+ VKILL = 0x5
+ VLNEXT = 0xe
+ VMIN = 0x10
+ VQUIT = 0x9
+ VREPRINT = 0x6
+ VSTART = 0xc
+ VSTATUS = 0x12
+ VSTOP = 0xd
+ VSUSP = 0xa
+ VTIME = 0x11
+ VWERASE = 0x4
+ WALL = 0x8
+ WALLSIG = 0x8
+ WALTSIG = 0x4
+ WCLONE = 0x4
+ WCOREFLAG = 0x80
+ WNOHANG = 0x1
+ WNOWAIT = 0x10000
+ WNOZOMBIE = 0x20000
+ WOPTSCHECKED = 0x40000
+ WSTOPPED = 0x7f
+ WUNTRACED = 0x2
+)
+
+// Errors
+const (
+ E2BIG = Errno(0x7)
+ EACCES = Errno(0xd)
+ EADDRINUSE = Errno(0x30)
+ EADDRNOTAVAIL = Errno(0x31)
+ EAFNOSUPPORT = Errno(0x2f)
+ EAGAIN = Errno(0x23)
+ EALREADY = Errno(0x25)
+ EAUTH = Errno(0x50)
+ EBADF = Errno(0x9)
+ EBADMSG = Errno(0x58)
+ EBADRPC = Errno(0x48)
+ EBUSY = Errno(0x10)
+ ECANCELED = Errno(0x57)
+ ECHILD = Errno(0xa)
+ ECONNABORTED = Errno(0x35)
+ ECONNREFUSED = Errno(0x3d)
+ ECONNRESET = Errno(0x36)
+ EDEADLK = Errno(0xb)
+ EDESTADDRREQ = Errno(0x27)
+ EDOM = Errno(0x21)
+ EDQUOT = Errno(0x45)
+ EEXIST = Errno(0x11)
+ EFAULT = Errno(0xe)
+ EFBIG = Errno(0x1b)
+ EFTYPE = Errno(0x4f)
+ EHOSTDOWN = Errno(0x40)
+ EHOSTUNREACH = Errno(0x41)
+ EIDRM = Errno(0x52)
+ EILSEQ = Errno(0x55)
+ EINPROGRESS = Errno(0x24)
+ EINTR = Errno(0x4)
+ EINVAL = Errno(0x16)
+ EIO = Errno(0x5)
+ EISCONN = Errno(0x38)
+ EISDIR = Errno(0x15)
+ ELAST = Errno(0x60)
+ ELOOP = Errno(0x3e)
+ EMFILE = Errno(0x18)
+ EMLINK = Errno(0x1f)
+ EMSGSIZE = Errno(0x28)
+ EMULTIHOP = Errno(0x5e)
+ ENAMETOOLONG = Errno(0x3f)
+ ENEEDAUTH = Errno(0x51)
+ ENETDOWN = Errno(0x32)
+ ENETRESET = Errno(0x34)
+ ENETUNREACH = Errno(0x33)
+ ENFILE = Errno(0x17)
+ ENOATTR = Errno(0x5d)
+ ENOBUFS = Errno(0x37)
+ ENODATA = Errno(0x59)
+ ENODEV = Errno(0x13)
+ ENOENT = Errno(0x2)
+ ENOEXEC = Errno(0x8)
+ ENOLCK = Errno(0x4d)
+ ENOLINK = Errno(0x5f)
+ ENOMEM = Errno(0xc)
+ ENOMSG = Errno(0x53)
+ ENOPROTOOPT = Errno(0x2a)
+ ENOSPC = Errno(0x1c)
+ ENOSR = Errno(0x5a)
+ ENOSTR = Errno(0x5b)
+ ENOSYS = Errno(0x4e)
+ ENOTBLK = Errno(0xf)
+ ENOTCONN = Errno(0x39)
+ ENOTDIR = Errno(0x14)
+ ENOTEMPTY = Errno(0x42)
+ ENOTSOCK = Errno(0x26)
+ ENOTSUP = Errno(0x56)
+ ENOTTY = Errno(0x19)
+ ENXIO = Errno(0x6)
+ EOPNOTSUPP = Errno(0x2d)
+ EOVERFLOW = Errno(0x54)
+ EPERM = Errno(0x1)
+ EPFNOSUPPORT = Errno(0x2e)
+ EPIPE = Errno(0x20)
+ EPROCLIM = Errno(0x43)
+ EPROCUNAVAIL = Errno(0x4c)
+ EPROGMISMATCH = Errno(0x4b)
+ EPROGUNAVAIL = Errno(0x4a)
+ EPROTO = Errno(0x60)
+ EPROTONOSUPPORT = Errno(0x2b)
+ EPROTOTYPE = Errno(0x29)
+ ERANGE = Errno(0x22)
+ EREMOTE = Errno(0x47)
+ EROFS = Errno(0x1e)
+ ERPCMISMATCH = Errno(0x49)
+ ESHUTDOWN = Errno(0x3a)
+ ESOCKTNOSUPPORT = Errno(0x2c)
+ ESPIPE = Errno(0x1d)
+ ESRCH = Errno(0x3)
+ ESTALE = Errno(0x46)
+ ETIME = Errno(0x5c)
+ ETIMEDOUT = Errno(0x3c)
+ ETOOMANYREFS = Errno(0x3b)
+ ETXTBSY = Errno(0x1a)
+ EUSERS = Errno(0x44)
+ EWOULDBLOCK = Errno(0x23)
+ EXDEV = Errno(0x12)
+)
+
+// Signals
+const (
+ SIGABRT = Signal(0x6)
+ SIGALRM = Signal(0xe)
+ SIGBUS = Signal(0xa)
+ SIGCHLD = Signal(0x14)
+ SIGCONT = Signal(0x13)
+ SIGEMT = Signal(0x7)
+ SIGFPE = Signal(0x8)
+ SIGHUP = Signal(0x1)
+ SIGILL = Signal(0x4)
+ SIGINFO = Signal(0x1d)
+ SIGINT = Signal(0x2)
+ SIGIO = Signal(0x17)
+ SIGIOT = Signal(0x6)
+ SIGKILL = Signal(0x9)
+ SIGPIPE = Signal(0xd)
+ SIGPROF = Signal(0x1b)
+ SIGPWR = Signal(0x20)
+ SIGQUIT = Signal(0x3)
+ SIGSEGV = Signal(0xb)
+ SIGSTOP = Signal(0x11)
+ SIGSYS = Signal(0xc)
+ SIGTERM = Signal(0xf)
+ SIGTRAP = Signal(0x5)
+ SIGTSTP = Signal(0x12)
+ SIGTTIN = Signal(0x15)
+ SIGTTOU = Signal(0x16)
+ SIGURG = Signal(0x10)
+ SIGUSR1 = Signal(0x1e)
+ SIGUSR2 = Signal(0x1f)
+ SIGVTALRM = Signal(0x1a)
+ SIGWINCH = Signal(0x1c)
+ SIGXCPU = Signal(0x18)
+ SIGXFSZ = Signal(0x19)
+)
+
+// Error table
+var errors = [...]string{
+ 1: "operation not permitted",
+ 2: "no such file or directory",
+ 3: "no such process",
+ 4: "interrupted system call",
+ 5: "input/output error",
+ 6: "device not configured",
+ 7: "argument list too long",
+ 8: "exec format error",
+ 9: "bad file descriptor",
+ 10: "no child processes",
+ 11: "resource deadlock avoided",
+ 12: "cannot allocate memory",
+ 13: "permission denied",
+ 14: "bad address",
+ 15: "block device required",
+ 16: "device busy",
+ 17: "file exists",
+ 18: "cross-device link",
+ 19: "operation not supported by device",
+ 20: "not a directory",
+ 21: "is a directory",
+ 22: "invalid argument",
+ 23: "too many open files in system",
+ 24: "too many open files",
+ 25: "inappropriate ioctl for device",
+ 26: "text file busy",
+ 27: "file too large",
+ 28: "no space left on device",
+ 29: "illegal seek",
+ 30: "read-only file system",
+ 31: "too many links",
+ 32: "broken pipe",
+ 33: "numerical argument out of domain",
+ 34: "result too large or too small",
+ 35: "resource temporarily unavailable",
+ 36: "operation now in progress",
+ 37: "operation already in progress",
+ 38: "socket operation on non-socket",
+ 39: "destination address required",
+ 40: "message too long",
+ 41: "protocol wrong type for socket",
+ 42: "protocol option not available",
+ 43: "protocol not supported",
+ 44: "socket type not supported",
+ 45: "operation not supported",
+ 46: "protocol family not supported",
+ 47: "address family not supported by protocol family",
+ 48: "address already in use",
+ 49: "can't assign requested address",
+ 50: "network is down",
+ 51: "network is unreachable",
+ 52: "network dropped connection on reset",
+ 53: "software caused connection abort",
+ 54: "connection reset by peer",
+ 55: "no buffer space available",
+ 56: "socket is already connected",
+ 57: "socket is not connected",
+ 58: "can't send after socket shutdown",
+ 59: "too many references: can't splice",
+ 60: "connection timed out",
+ 61: "connection refused",
+ 62: "too many levels of symbolic links",
+ 63: "file name too long",
+ 64: "host is down",
+ 65: "no route to host",
+ 66: "directory not empty",
+ 67: "too many processes",
+ 68: "too many users",
+ 69: "disc quota exceeded",
+ 70: "stale NFS file handle",
+ 71: "too many levels of remote in path",
+ 72: "RPC struct is bad",
+ 73: "RPC version wrong",
+ 74: "RPC prog. not avail",
+ 75: "program version wrong",
+ 76: "bad procedure for program",
+ 77: "no locks available",
+ 78: "function not implemented",
+ 79: "inappropriate file type or format",
+ 80: "authentication error",
+ 81: "need authenticator",
+ 82: "identifier removed",
+ 83: "no message of desired type",
+ 84: "value too large to be stored in data type",
+ 85: "illegal byte sequence",
+ 86: "not supported",
+ 87: "operation Canceled",
+ 88: "bad or Corrupt message",
+ 89: "no message available",
+ 90: "no STREAM resources",
+ 91: "not a STREAM",
+ 92: "STREAM ioctl timeout",
+ 93: "attribute not found",
+ 94: "multihop attempted",
+ 95: "link has been severed",
+ 96: "protocol error",
+}
+
+// Signal table
+var signals = [...]string{
+ 1: "hangup",
+ 2: "interrupt",
+ 3: "quit",
+ 4: "illegal instruction",
+ 5: "trace/BPT trap",
+ 6: "abort trap",
+ 7: "EMT trap",
+ 8: "floating point exception",
+ 9: "killed",
+ 10: "bus error",
+ 11: "segmentation fault",
+ 12: "bad system call",
+ 13: "broken pipe",
+ 14: "alarm clock",
+ 15: "terminated",
+ 16: "urgent I/O condition",
+ 17: "stopped (signal)",
+ 18: "stopped",
+ 19: "continued",
+ 20: "child exited",
+ 21: "stopped (tty input)",
+ 22: "stopped (tty output)",
+ 23: "I/O possible",
+ 24: "cputime limit exceeded",
+ 25: "filesize limit exceeded",
+ 26: "virtual timer expired",
+ 27: "profiling timer expired",
+ 28: "window size changes",
+ 29: "information request",
+ 30: "user defined signal 1",
+ 31: "user defined signal 2",
+ 32: "power fail/restart",
+}
diff --git a/src/pkg/syscall/zerrors_openbsd_386.go b/src/pkg/syscall/zerrors_openbsd_386.go
index 11074d5f5..5330d15b1 100644
--- a/src/pkg/syscall/zerrors_openbsd_386.go
+++ b/src/pkg/syscall/zerrors_openbsd_386.go
@@ -45,6 +45,29 @@ const (
ARPHRD_FRELAY = 0xf
ARPHRD_IEEE1394 = 0x18
ARPHRD_IEEE802 = 0x6
+ B0 = 0x0
+ B110 = 0x6e
+ B115200 = 0x1c200
+ B1200 = 0x4b0
+ B134 = 0x86
+ B14400 = 0x3840
+ B150 = 0x96
+ B1800 = 0x708
+ B19200 = 0x4b00
+ B200 = 0xc8
+ B230400 = 0x38400
+ B2400 = 0x960
+ B28800 = 0x7080
+ B300 = 0x12c
+ B38400 = 0x9600
+ B4800 = 0x12c0
+ B50 = 0x32
+ B57600 = 0xe100
+ B600 = 0x258
+ B7200 = 0x1c20
+ B75 = 0x4b
+ B76800 = 0x12c00
+ B9600 = 0x2580
BIOCFLUSH = 0x20004268
BIOCGBLEN = 0x40044266
BIOCGDIRFILT = 0x4004427c
@@ -116,12 +139,28 @@ const (
BPF_TXA = 0x80
BPF_W = 0x0
BPF_X = 0x8
+ BRKINT = 0x2
+ CFLUSH = 0xf
+ CLOCAL = 0x8000
+ CREAD = 0x800
+ CS5 = 0x0
+ CS6 = 0x100
+ CS7 = 0x200
+ CS8 = 0x300
+ CSIZE = 0x300
+ CSTART = 0x11
+ CSTATUS = 0xff
+ CSTOP = 0x13
+ CSTOPB = 0x400
+ CSUSP = 0x1a
CTL_MAXNAME = 0xc
CTL_NET = 0x4
+ DIOCOSFPFLUSH = 0x2000444e
DLT_ARCNET = 0x7
DLT_ATM_RFC1483 = 0xb
DLT_AX25 = 0x3
DLT_CHAOS = 0x5
+ DLT_C_HDLC = 0x68
DLT_EN10MB = 0x1
DLT_EN3MB = 0x2
DLT_ENC = 0xd
@@ -137,6 +176,7 @@ const (
DLT_PPP = 0x9
DLT_PPP_BSDOS = 0x10
DLT_PPP_ETHER = 0x33
+ DLT_PPP_SERIAL = 0x32
DLT_PRONET = 0x4
DLT_RAW = 0xe
DLT_SLIP = 0x8
@@ -156,10 +196,6 @@ const (
ECHOKE = 0x1
ECHONL = 0x10
ECHOPRT = 0x20
- EFER_LMA = 0x400
- EFER_LME = 0x100
- EFER_NXE = 0x800
- EFER_SCE = 0x1
EMT_TAGOVF = 0x1
EMUL_ENABLED = 0x1
EMUL_NATIVE = 0x2
@@ -385,6 +421,7 @@ const (
EXTPROC = 0x800
FD_CLOEXEC = 0x1
FD_SETSIZE = 0x400
+ FLUSHO = 0x800000
F_DUPFD = 0x0
F_DUPFD_CLOEXEC = 0xa
F_GETFD = 0x1
@@ -400,6 +437,12 @@ const (
F_SETOWN = 0x6
F_UNLCK = 0x2
F_WRLCK = 0x3
+ HUPCL = 0x4000
+ ICANON = 0x100
+ ICRNL = 0x100
+ IEXTEN = 0x400
+ IFAN_ARRIVAL = 0x0
+ IFAN_DEPARTURE = 0x1
IFA_ROUTE = 0x1
IFF_ALLMULTI = 0x200
IFF_BROADCAST = 0x2
@@ -642,6 +685,12 @@ const (
IFT_X25MLP = 0x79
IFT_X25PLE = 0x28
IFT_XETHER = 0x1a
+ IGNBRK = 0x1
+ IGNCR = 0x80
+ IGNPAR = 0x4
+ IMAXBEL = 0x2000
+ INLCR = 0x40
+ INPCK = 0x10
IN_CLASSA_HOST = 0xffffff
IN_CLASSA_MAX = 0x80
IN_CLASSA_NET = 0xff000000
@@ -762,6 +811,7 @@ const (
IP_ESP_TRANS_LEVEL = 0x15
IP_HDRINCL = 0x2
IP_IPCOMP_LEVEL = 0x1d
+ IP_IPSECFLOWINFO = 0x24
IP_IPSEC_LOCAL_AUTH = 0x1b
IP_IPSEC_LOCAL_CRED = 0x19
IP_IPSEC_LOCAL_ID = 0x17
@@ -796,17 +846,23 @@ const (
IP_RTABLE = 0x1021
IP_TOS = 0x3
IP_TTL = 0x4
+ ISIG = 0x80
+ ISTRIP = 0x20
+ IXANY = 0x800
+ IXOFF = 0x400
+ IXON = 0x200
+ LCNT_OVERLOAD_FLUSH = 0x6
LOCK_EX = 0x2
LOCK_NB = 0x4
LOCK_SH = 0x1
LOCK_UN = 0x8
- MAP_ET_KVAGUARD = 0x10
MSG_BCAST = 0x100
MSG_CTRUNC = 0x20
MSG_DONTROUTE = 0x4
MSG_DONTWAIT = 0x80
MSG_EOR = 0x8
MSG_MCAST = 0x200
+ MSG_NOSIGNAL = 0x400
MSG_OOB = 0x1
MSG_PEEK = 0x2
MSG_TRUNC = 0x10
@@ -818,6 +874,7 @@ const (
NET_RT_MAXID = 0x6
NET_RT_STATS = 0x4
NET_RT_TABLE = 0x5
+ NOFLSH = 0x80000000
NOTE_ATTRIB = 0x8
NOTE_CHILD = 0x4
NOTE_DELETE = 0x1
@@ -836,6 +893,12 @@ const (
NOTE_TRACKERR = 0x2
NOTE_TRUNCATE = 0x80
NOTE_WRITE = 0x2
+ OCRNL = 0x10
+ ONLCR = 0x2
+ ONLRET = 0x80
+ ONOCR = 0x40
+ ONOEOT = 0x8
+ OPOST = 0x1
O_ACCMODE = 0x3
O_APPEND = 0x8
O_ASYNC = 0x40
@@ -857,6 +920,12 @@ const (
O_SYNC = 0x80
O_TRUNC = 0x400
O_WRONLY = 0x1
+ PARENB = 0x1000
+ PARMRK = 0x8
+ PARODD = 0x2000
+ PENDIN = 0x20000000
+ PF_FLUSH = 0x1
+ PT_MASK = 0x3ff000
RLIMIT_CORE = 0x4
RLIMIT_CPU = 0x0
RLIMIT_DATA = 0x2
@@ -893,10 +962,9 @@ const (
RTF_CLONING = 0x100
RTF_DONE = 0x40
RTF_DYNAMIC = 0x10
- RTF_FMASK = 0x8f808
+ RTF_FMASK = 0xf808
RTF_GATEWAY = 0x2
RTF_HOST = 0x4
- RTF_JUMBO = 0x80000
RTF_LLINFO = 0x400
RTF_MASK = 0x80
RTF_MODIFIED = 0x20
@@ -942,7 +1010,6 @@ const (
RUSAGE_CHILDREN = -0x1
RUSAGE_SELF = 0x0
RUSAGE_THREAD = 0x1
- SCM_CREDS = 0x2
SCM_RIGHTS = 0x1
SCM_TIMESTAMP = 0x4
SHUT_RD = 0x0
@@ -996,7 +1063,6 @@ const (
SIOCGETPFSYNC = 0xc02069f8
SIOCGETSGCNT = 0xc0147534
SIOCGETVIFCNT = 0xc0147533
- SIOCGETVLANPRIO = 0xc0206990
SIOCGHIWAT = 0x40047301
SIOCGIFADDR = 0xc0206921
SIOCGIFASYNCMAP = 0xc020697c
@@ -1034,7 +1100,6 @@ const (
SIOCSETLABEL = 0x80206999
SIOCSETPFLOW = 0x802069fd
SIOCSETPFSYNC = 0x802069f7
- SIOCSETVLANPRIO = 0x8020698f
SIOCSHIWAT = 0x80047300
SIOCSIFADDR = 0x8020690c
SIOCSIFASYNCMAP = 0x8020697d
@@ -1073,7 +1138,6 @@ const (
SO_DEBUG = 0x1
SO_DONTROUTE = 0x10
SO_ERROR = 0x1007
- SO_JUMBO = 0x400
SO_KEEPALIVE = 0x8
SO_LINGER = 0x80
SO_NETPROC = 0x1020
@@ -1092,6 +1156,9 @@ const (
SO_TIMESTAMP = 0x800
SO_TYPE = 0x1008
SO_USELOOPBACK = 0x40
+ TCIFLUSH = 0x1
+ TCIOFLUSH = 0x3
+ TCOFLUSH = 0x2
TCP_MAXBURST = 0x4
TCP_MAXSEG = 0x2
TCP_MAXWIN = 0xffff
@@ -1102,6 +1169,7 @@ const (
TCP_NODELAY = 0x1
TCP_NSTATES = 0xb
TCP_SACK_ENABLE = 0x8
+ TCSAFLUSH = 0x2
TIOCCBRK = 0x2000747a
TIOCCDTR = 0x20007478
TIOCCONS = 0x80047462
@@ -1167,6 +1235,25 @@ const (
TIOCSTSTAMP = 0x8008745a
TIOCSWINSZ = 0x80087467
TIOCUCNTL = 0x80047466
+ TOSTOP = 0x400000
+ VDISCARD = 0xf
+ VDSUSP = 0xb
+ VEOF = 0x0
+ VEOL = 0x1
+ VEOL2 = 0x2
+ VERASE = 0x3
+ VINTR = 0x8
+ VKILL = 0x5
+ VLNEXT = 0xe
+ VMIN = 0x10
+ VQUIT = 0x9
+ VREPRINT = 0x6
+ VSTART = 0xc
+ VSTATUS = 0x12
+ VSTOP = 0xd
+ VSUSP = 0xa
+ VTIME = 0x11
+ VWERASE = 0x4
WALTSIG = 0x4
WCONTINUED = 0x8
WCOREFLAG = 0x80
diff --git a/src/pkg/syscall/zerrors_openbsd_amd64.go b/src/pkg/syscall/zerrors_openbsd_amd64.go
index 91ea06553..5a7edd341 100644
--- a/src/pkg/syscall/zerrors_openbsd_amd64.go
+++ b/src/pkg/syscall/zerrors_openbsd_amd64.go
@@ -45,6 +45,29 @@ const (
ARPHRD_FRELAY = 0xf
ARPHRD_IEEE1394 = 0x18
ARPHRD_IEEE802 = 0x6
+ B0 = 0x0
+ B110 = 0x6e
+ B115200 = 0x1c200
+ B1200 = 0x4b0
+ B134 = 0x86
+ B14400 = 0x3840
+ B150 = 0x96
+ B1800 = 0x708
+ B19200 = 0x4b00
+ B200 = 0xc8
+ B230400 = 0x38400
+ B2400 = 0x960
+ B28800 = 0x7080
+ B300 = 0x12c
+ B38400 = 0x9600
+ B4800 = 0x12c0
+ B50 = 0x32
+ B57600 = 0xe100
+ B600 = 0x258
+ B7200 = 0x1c20
+ B75 = 0x4b
+ B76800 = 0x12c00
+ B9600 = 0x2580
BIOCFLUSH = 0x20004268
BIOCGBLEN = 0x40044266
BIOCGDIRFILT = 0x4004427c
@@ -116,12 +139,30 @@ const (
BPF_TXA = 0x80
BPF_W = 0x0
BPF_X = 0x8
+ BRKINT = 0x2
+ CCR0_FLUSH = 0x10
+ CFLUSH = 0xf
+ CLOCAL = 0x8000
+ CPUID_CFLUSH = 0x80000
+ CREAD = 0x800
+ CS5 = 0x0
+ CS6 = 0x100
+ CS7 = 0x200
+ CS8 = 0x300
+ CSIZE = 0x300
+ CSTART = 0x11
+ CSTATUS = 0xff
+ CSTOP = 0x13
+ CSTOPB = 0x400
+ CSUSP = 0x1a
CTL_MAXNAME = 0xc
CTL_NET = 0x4
+ DIOCOSFPFLUSH = 0x2000444e
DLT_ARCNET = 0x7
DLT_ATM_RFC1483 = 0xb
DLT_AX25 = 0x3
DLT_CHAOS = 0x5
+ DLT_C_HDLC = 0x68
DLT_EN10MB = 0x1
DLT_EN3MB = 0x2
DLT_ENC = 0xd
@@ -137,6 +178,7 @@ const (
DLT_PPP = 0x9
DLT_PPP_BSDOS = 0x10
DLT_PPP_ETHER = 0x33
+ DLT_PPP_SERIAL = 0x32
DLT_PRONET = 0x4
DLT_RAW = 0xe
DLT_SLIP = 0x8
@@ -385,6 +427,7 @@ const (
EXTPROC = 0x800
FD_CLOEXEC = 0x1
FD_SETSIZE = 0x400
+ FLUSHO = 0x800000
F_DUPFD = 0x0
F_DUPFD_CLOEXEC = 0xa
F_GETFD = 0x1
@@ -400,6 +443,12 @@ const (
F_SETOWN = 0x6
F_UNLCK = 0x2
F_WRLCK = 0x3
+ HUPCL = 0x4000
+ ICANON = 0x100
+ ICRNL = 0x100
+ IEXTEN = 0x400
+ IFAN_ARRIVAL = 0x0
+ IFAN_DEPARTURE = 0x1
IFA_ROUTE = 0x1
IFF_ALLMULTI = 0x200
IFF_BROADCAST = 0x2
@@ -642,6 +691,12 @@ const (
IFT_X25MLP = 0x79
IFT_X25PLE = 0x28
IFT_XETHER = 0x1a
+ IGNBRK = 0x1
+ IGNCR = 0x80
+ IGNPAR = 0x4
+ IMAXBEL = 0x2000
+ INLCR = 0x40
+ INPCK = 0x10
IN_CLASSA_HOST = 0xffffff
IN_CLASSA_MAX = 0x80
IN_CLASSA_NET = 0xff000000
@@ -762,6 +817,7 @@ const (
IP_ESP_TRANS_LEVEL = 0x15
IP_HDRINCL = 0x2
IP_IPCOMP_LEVEL = 0x1d
+ IP_IPSECFLOWINFO = 0x24
IP_IPSEC_LOCAL_AUTH = 0x1b
IP_IPSEC_LOCAL_CRED = 0x19
IP_IPSEC_LOCAL_ID = 0x17
@@ -796,17 +852,23 @@ const (
IP_RTABLE = 0x1021
IP_TOS = 0x3
IP_TTL = 0x4
+ ISIG = 0x80
+ ISTRIP = 0x20
+ IXANY = 0x800
+ IXOFF = 0x400
+ IXON = 0x200
+ LCNT_OVERLOAD_FLUSH = 0x6
LOCK_EX = 0x2
LOCK_NB = 0x4
LOCK_SH = 0x1
LOCK_UN = 0x8
- MAP_ET_KVAGUARD = 0x10
MSG_BCAST = 0x100
MSG_CTRUNC = 0x20
MSG_DONTROUTE = 0x4
MSG_DONTWAIT = 0x80
MSG_EOR = 0x8
MSG_MCAST = 0x200
+ MSG_NOSIGNAL = 0x400
MSG_OOB = 0x1
MSG_PEEK = 0x2
MSG_TRUNC = 0x10
@@ -818,6 +880,7 @@ const (
NET_RT_MAXID = 0x6
NET_RT_STATS = 0x4
NET_RT_TABLE = 0x5
+ NOFLSH = 0x80000000
NOTE_ATTRIB = 0x8
NOTE_CHILD = 0x4
NOTE_DELETE = 0x1
@@ -836,6 +899,12 @@ const (
NOTE_TRACKERR = 0x2
NOTE_TRUNCATE = 0x80
NOTE_WRITE = 0x2
+ OCRNL = 0x10
+ ONLCR = 0x2
+ ONLRET = 0x80
+ ONOCR = 0x40
+ ONOEOT = 0x8
+ OPOST = 0x1
O_ACCMODE = 0x3
O_APPEND = 0x8
O_ASYNC = 0x40
@@ -857,6 +926,12 @@ const (
O_SYNC = 0x80
O_TRUNC = 0x400
O_WRONLY = 0x1
+ PARENB = 0x1000
+ PARMRK = 0x8
+ PARODD = 0x2000
+ PENDIN = 0x20000000
+ PF_FLUSH = 0x1
+ PMC5_PIPELINE_FLUSH = 0x15
RLIMIT_CORE = 0x4
RLIMIT_CPU = 0x0
RLIMIT_DATA = 0x2
@@ -893,10 +968,9 @@ const (
RTF_CLONING = 0x100
RTF_DONE = 0x40
RTF_DYNAMIC = 0x10
- RTF_FMASK = 0x8f808
+ RTF_FMASK = 0xf808
RTF_GATEWAY = 0x2
RTF_HOST = 0x4
- RTF_JUMBO = 0x80000
RTF_LLINFO = 0x400
RTF_MASK = 0x80
RTF_MODIFIED = 0x20
@@ -942,7 +1016,6 @@ const (
RUSAGE_CHILDREN = -0x1
RUSAGE_SELF = 0x0
RUSAGE_THREAD = 0x1
- SCM_CREDS = 0x2
SCM_RIGHTS = 0x1
SCM_TIMESTAMP = 0x4
SHUT_RD = 0x0
@@ -996,7 +1069,6 @@ const (
SIOCGETPFSYNC = 0xc02069f8
SIOCGETSGCNT = 0xc0207534
SIOCGETVIFCNT = 0xc0287533
- SIOCGETVLANPRIO = 0xc0206990
SIOCGHIWAT = 0x40047301
SIOCGIFADDR = 0xc0206921
SIOCGIFASYNCMAP = 0xc020697c
@@ -1034,7 +1106,6 @@ const (
SIOCSETLABEL = 0x80206999
SIOCSETPFLOW = 0x802069fd
SIOCSETPFSYNC = 0x802069f7
- SIOCSETVLANPRIO = 0x8020698f
SIOCSHIWAT = 0x80047300
SIOCSIFADDR = 0x8020690c
SIOCSIFASYNCMAP = 0x8020697d
@@ -1073,7 +1144,6 @@ const (
SO_DEBUG = 0x1
SO_DONTROUTE = 0x10
SO_ERROR = 0x1007
- SO_JUMBO = 0x400
SO_KEEPALIVE = 0x8
SO_LINGER = 0x80
SO_NETPROC = 0x1020
@@ -1092,6 +1162,9 @@ const (
SO_TIMESTAMP = 0x800
SO_TYPE = 0x1008
SO_USELOOPBACK = 0x40
+ TCIFLUSH = 0x1
+ TCIOFLUSH = 0x3
+ TCOFLUSH = 0x2
TCP_MAXBURST = 0x4
TCP_MAXSEG = 0x2
TCP_MAXWIN = 0xffff
@@ -1102,6 +1175,7 @@ const (
TCP_NODELAY = 0x1
TCP_NSTATES = 0xb
TCP_SACK_ENABLE = 0x8
+ TCSAFLUSH = 0x2
TIOCCBRK = 0x2000747a
TIOCCDTR = 0x20007478
TIOCCONS = 0x80047462
@@ -1167,6 +1241,25 @@ const (
TIOCSTSTAMP = 0x8008745a
TIOCSWINSZ = 0x80087467
TIOCUCNTL = 0x80047466
+ TOSTOP = 0x400000
+ VDISCARD = 0xf
+ VDSUSP = 0xb
+ VEOF = 0x0
+ VEOL = 0x1
+ VEOL2 = 0x2
+ VERASE = 0x3
+ VINTR = 0x8
+ VKILL = 0x5
+ VLNEXT = 0xe
+ VMIN = 0x10
+ VQUIT = 0x9
+ VREPRINT = 0x6
+ VSTART = 0xc
+ VSTATUS = 0x12
+ VSTOP = 0xd
+ VSUSP = 0xa
+ VTIME = 0x11
+ VWERASE = 0x4
WALTSIG = 0x4
WCONTINUED = 0x8
WCOREFLAG = 0x80
diff --git a/src/pkg/syscall/zerrors_plan9_386.go b/src/pkg/syscall/zerrors_plan9_386.go
index e7c993c0f..ede3d6a32 100644
--- a/src/pkg/syscall/zerrors_plan9_386.go
+++ b/src/pkg/syscall/zerrors_plan9_386.go
@@ -4,8 +4,6 @@
package syscall
-import "errors"
-
// Constants
const (
// Invented values to support what package os expects.
@@ -24,19 +22,27 @@ const (
S_IFREG = 0x8000
S_IFLNK = 0xa000
S_IFSOCK = 0xc000
-
- SIGINT = Signal(0x2)
- SIGKILL = Signal(0x9)
)
// Errors
var (
- EINVAL = errors.New("bad arg in system call")
- ENOTDIR = errors.New("not a directory")
- ENOENT = errors.New("file does not exist")
- EEXIST = errors.New("file already exists")
- EIO = errors.New("i/o error")
- ENAMETOOLONG = errors.New("file name too long")
- EPERM = errors.New("permission denied")
- EPLAN9 = errors.New("not supported by plan 9")
+ EINVAL = NewError("bad arg in system call")
+ ENOTDIR = NewError("not a directory")
+ EISDIR = NewError("file is a directory")
+ ENOENT = NewError("file does not exist")
+ EEXIST = NewError("file already exists")
+ EMFILE = NewError("no free file descriptors")
+ EIO = NewError("i/o error")
+ ENAMETOOLONG = NewError("file name too long")
+ EINTR = NewError("interrupted")
+ EPERM = NewError("permission denied")
+ EBUSY = NewError("no free devices")
+ ETIMEDOUT = NewError("connection timed out")
+ EPLAN9 = NewError("not supported by plan 9")
+
+ // The following errors do not correspond to any
+ // Plan 9 system messages. Invented to support
+ // what package os and others expect.
+ EACCES = NewError("access permission denied")
+ EAFNOSUPPORT = NewError("address family not supported by protocol")
)
diff --git a/src/pkg/syscall/zerrors_plan9_amd64.go b/src/pkg/syscall/zerrors_plan9_amd64.go
new file mode 100644
index 000000000..ede3d6a32
--- /dev/null
+++ b/src/pkg/syscall/zerrors_plan9_amd64.go
@@ -0,0 +1,48 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syscall
+
+// Constants
+const (
+ // Invented values to support what package os expects.
+ O_CREAT = 0x02000
+ O_APPEND = 0x00400
+ O_NOCTTY = 0x00000
+ O_NONBLOCK = 0x00000
+ O_SYNC = 0x00000
+ O_ASYNC = 0x00000
+
+ S_IFMT = 0x1f000
+ S_IFIFO = 0x1000
+ S_IFCHR = 0x2000
+ S_IFDIR = 0x4000
+ S_IFBLK = 0x6000
+ S_IFREG = 0x8000
+ S_IFLNK = 0xa000
+ S_IFSOCK = 0xc000
+)
+
+// Errors
+var (
+ EINVAL = NewError("bad arg in system call")
+ ENOTDIR = NewError("not a directory")
+ EISDIR = NewError("file is a directory")
+ ENOENT = NewError("file does not exist")
+ EEXIST = NewError("file already exists")
+ EMFILE = NewError("no free file descriptors")
+ EIO = NewError("i/o error")
+ ENAMETOOLONG = NewError("file name too long")
+ EINTR = NewError("interrupted")
+ EPERM = NewError("permission denied")
+ EBUSY = NewError("no free devices")
+ ETIMEDOUT = NewError("connection timed out")
+ EPLAN9 = NewError("not supported by plan 9")
+
+ // The following errors do not correspond to any
+ // Plan 9 system messages. Invented to support
+ // what package os and others expect.
+ EACCES = NewError("access permission denied")
+ EAFNOSUPPORT = NewError("address family not supported by protocol")
+)
diff --git a/src/pkg/syscall/zsyscall_darwin_386.go b/src/pkg/syscall/zsyscall_darwin_386.go
index b952ac47c..2f99b7f6c 100644
--- a/src/pkg/syscall/zsyscall_darwin_386.go
+++ b/src/pkg/syscall/zsyscall_darwin_386.go
@@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) {
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
if e1 != 0 {
err = e1
@@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func utimes(path string, timeval *[2]Timeval) (err error) {
- _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0)
if e1 != 0 {
err = e1
}
@@ -286,7 +291,12 @@ func kill(pid int, signum int, posix int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Access(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -306,7 +316,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -316,7 +331,12 @@ func Chdir(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -326,7 +346,12 @@ func Chflags(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chmod(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -336,7 +361,12 @@ func Chmod(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -346,7 +376,12 @@ func Chown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chroot(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -387,7 +422,17 @@ func Dup2(from int, to int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Exchangedata(path1 string, path2 string, options int) (err error) {
- _, _, e1 := Syscall(SYS_EXCHANGEDATA, uintptr(unsafe.Pointer(StringBytePtr(path1))), uintptr(unsafe.Pointer(StringBytePtr(path2))), uintptr(options))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path1)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(path2)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_EXCHANGEDATA, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(options))
if e1 != 0 {
err = e1
}
@@ -414,7 +459,12 @@ func Fchdir(fd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -675,7 +725,12 @@ func Kqueue() (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lchown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -685,7 +740,17 @@ func Lchown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Link(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -705,7 +770,12 @@ func Listen(s int, backlog int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lstat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -715,7 +785,12 @@ func Lstat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdir(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -725,7 +800,12 @@ func Mkdir(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkfifo(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -735,7 +815,12 @@ func Mkfifo(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknod(path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
if e1 != 0 {
err = e1
}
@@ -745,7 +830,12 @@ func Mknod(path string, mode uint32, dev int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Open(path string, mode int, perm uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
fd = int(r0)
if e1 != 0 {
err = e1
@@ -756,7 +846,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Pathconf(path string, name int) (val int, err error) {
- r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0)
val = int(r0)
if e1 != 0 {
err = e1
@@ -800,7 +895,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Read(fd int, p []byte) (n int, err error) {
+func read(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -818,13 +913,18 @@ func Read(fd int, p []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Readlink(path string, buf []byte) (n int, err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
+ _p1 = unsafe.Pointer(&buf[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
n = int(r0)
if e1 != 0 {
err = e1
@@ -835,7 +935,17 @@ func Readlink(path string, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rename(from string, to string) (err error) {
- _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(from)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(to)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -845,7 +955,12 @@ func Rename(from string, to string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Revoke(path string) (err error) {
- _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -855,7 +970,12 @@ func Revoke(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rmdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -916,7 +1036,12 @@ func Setgid(gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setlogin(name string) (err error) {
- _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1017,7 +1142,12 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1027,7 +1157,12 @@ func Stat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Statfs(path string, stat *Statfs_t) (err error) {
- _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1037,7 +1172,17 @@ func Statfs(path string, stat *Statfs_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Symlink(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -1057,7 +1202,12 @@ func Sync() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Truncate(path string, length int64) (err error) {
- _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), uintptr(length>>32))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), uintptr(length>>32))
if e1 != 0 {
err = e1
}
@@ -1075,7 +1225,12 @@ func Umask(newmask int) (oldmask int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Undelete(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1085,7 +1240,12 @@ func Undelete(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlink(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1095,7 +1255,12 @@ func Unlink(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unmount(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -1104,7 +1269,7 @@ func Unmount(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Write(fd int, p []byte) (n int, err error) {
+func write(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -1142,7 +1307,7 @@ func munmap(addr uintptr, length uintptr) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func read(fd int, buf *byte, nbuf int) (n int, err error) {
+func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
@@ -1153,7 +1318,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func write(fd int, buf *byte, nbuf int) (n int, err error) {
+func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_darwin_amd64.go b/src/pkg/syscall/zsyscall_darwin_amd64.go
index 104ee5513..0f08df4c5 100644
--- a/src/pkg/syscall/zsyscall_darwin_amd64.go
+++ b/src/pkg/syscall/zsyscall_darwin_amd64.go
@@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) {
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
if e1 != 0 {
err = e1
@@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func utimes(path string, timeval *[2]Timeval) (err error) {
- _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0)
if e1 != 0 {
err = e1
}
@@ -286,7 +291,12 @@ func kill(pid int, signum int, posix int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Access(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -306,7 +316,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -316,7 +331,12 @@ func Chdir(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -326,7 +346,12 @@ func Chflags(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chmod(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -336,7 +361,12 @@ func Chmod(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -346,7 +376,12 @@ func Chown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chroot(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -387,7 +422,17 @@ func Dup2(from int, to int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Exchangedata(path1 string, path2 string, options int) (err error) {
- _, _, e1 := Syscall(SYS_EXCHANGEDATA, uintptr(unsafe.Pointer(StringBytePtr(path1))), uintptr(unsafe.Pointer(StringBytePtr(path2))), uintptr(options))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path1)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(path2)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_EXCHANGEDATA, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(options))
if e1 != 0 {
err = e1
}
@@ -414,7 +459,12 @@ func Fchdir(fd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -675,7 +725,12 @@ func Kqueue() (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lchown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -685,7 +740,17 @@ func Lchown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Link(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -705,7 +770,12 @@ func Listen(s int, backlog int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lstat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -715,7 +785,12 @@ func Lstat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdir(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -725,7 +800,12 @@ func Mkdir(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkfifo(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -735,7 +815,12 @@ func Mkfifo(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknod(path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
if e1 != 0 {
err = e1
}
@@ -745,7 +830,12 @@ func Mknod(path string, mode uint32, dev int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Open(path string, mode int, perm uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
fd = int(r0)
if e1 != 0 {
err = e1
@@ -756,7 +846,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Pathconf(path string, name int) (val int, err error) {
- r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0)
val = int(r0)
if e1 != 0 {
err = e1
@@ -800,7 +895,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Read(fd int, p []byte) (n int, err error) {
+func read(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -818,13 +913,18 @@ func Read(fd int, p []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Readlink(path string, buf []byte) (n int, err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
+ _p1 = unsafe.Pointer(&buf[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
n = int(r0)
if e1 != 0 {
err = e1
@@ -835,7 +935,17 @@ func Readlink(path string, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rename(from string, to string) (err error) {
- _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(from)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(to)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -845,7 +955,12 @@ func Rename(from string, to string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Revoke(path string) (err error) {
- _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -855,7 +970,12 @@ func Revoke(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rmdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -916,7 +1036,12 @@ func Setgid(gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setlogin(name string) (err error) {
- _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1017,7 +1142,12 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1027,7 +1157,12 @@ func Stat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Statfs(path string, stat *Statfs_t) (err error) {
- _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1037,7 +1172,17 @@ func Statfs(path string, stat *Statfs_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Symlink(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -1057,7 +1202,12 @@ func Sync() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Truncate(path string, length int64) (err error) {
- _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), 0)
if e1 != 0 {
err = e1
}
@@ -1075,7 +1225,12 @@ func Umask(newmask int) (oldmask int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Undelete(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1085,7 +1240,12 @@ func Undelete(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlink(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1095,7 +1255,12 @@ func Unlink(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unmount(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -1104,7 +1269,7 @@ func Unmount(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Write(fd int, p []byte) (n int, err error) {
+func write(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -1142,7 +1307,7 @@ func munmap(addr uintptr, length uintptr) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func read(fd int, buf *byte, nbuf int) (n int, err error) {
+func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
@@ -1153,7 +1318,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func write(fd int, buf *byte, nbuf int) (n int, err error) {
+func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_freebsd_386.go b/src/pkg/syscall/zsyscall_freebsd_386.go
index 455575467..5ca0bcb16 100644
--- a/src/pkg/syscall/zsyscall_freebsd_386.go
+++ b/src/pkg/syscall/zsyscall_freebsd_386.go
@@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) {
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
if e1 != 0 {
err = e1
@@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func utimes(path string, timeval *[2]Timeval) (err error) {
- _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0)
if e1 != 0 {
err = e1
}
@@ -266,7 +271,12 @@ func pipe() (r int, w int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Access(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -286,7 +296,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -296,7 +311,12 @@ func Chdir(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -306,7 +326,12 @@ func Chflags(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chmod(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -316,7 +341,12 @@ func Chmod(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -326,7 +356,12 @@ func Chown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chroot(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -384,7 +419,12 @@ func Fchdir(fd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -665,7 +705,12 @@ func Kqueue() (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lchown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -675,7 +720,17 @@ func Lchown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Link(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -695,7 +750,12 @@ func Listen(s int, backlog int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lstat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -705,7 +765,12 @@ func Lstat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdir(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -715,7 +780,12 @@ func Mkdir(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkfifo(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -725,7 +795,12 @@ func Mkfifo(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknod(path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
if e1 != 0 {
err = e1
}
@@ -745,7 +820,12 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Open(path string, mode int, perm uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
fd = int(r0)
if e1 != 0 {
err = e1
@@ -756,7 +836,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Pathconf(path string, name int) (val int, err error) {
- r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0)
val = int(r0)
if e1 != 0 {
err = e1
@@ -800,7 +885,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Read(fd int, p []byte) (n int, err error) {
+func read(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -818,13 +903,18 @@ func Read(fd int, p []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Readlink(path string, buf []byte) (n int, err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
+ _p1 = unsafe.Pointer(&buf[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
n = int(r0)
if e1 != 0 {
err = e1
@@ -835,7 +925,17 @@ func Readlink(path string, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rename(from string, to string) (err error) {
- _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(from)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(to)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -845,7 +945,12 @@ func Rename(from string, to string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Revoke(path string) (err error) {
- _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -855,7 +960,12 @@ func Revoke(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rmdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -916,7 +1026,12 @@ func Setgid(gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setlogin(name string) (err error) {
- _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1007,7 +1122,12 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1017,7 +1137,12 @@ func Stat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Statfs(path string, stat *Statfs_t) (err error) {
- _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1027,7 +1152,17 @@ func Statfs(path string, stat *Statfs_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Symlink(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -1047,7 +1182,12 @@ func Sync() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Truncate(path string, length int64) (err error) {
- _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), uintptr(length>>32))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), uintptr(length>>32))
if e1 != 0 {
err = e1
}
@@ -1065,7 +1205,12 @@ func Umask(newmask int) (oldmask int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Undelete(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1075,7 +1220,12 @@ func Undelete(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlink(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1085,7 +1235,12 @@ func Unlink(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unmount(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -1094,7 +1249,7 @@ func Unmount(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Write(fd int, p []byte) (n int, err error) {
+func write(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -1132,7 +1287,7 @@ func munmap(addr uintptr, length uintptr) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func read(fd int, buf *byte, nbuf int) (n int, err error) {
+func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
@@ -1143,7 +1298,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func write(fd int, buf *byte, nbuf int) (n int, err error) {
+func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_freebsd_amd64.go b/src/pkg/syscall/zsyscall_freebsd_amd64.go
index 33b71b9eb..260677473 100644
--- a/src/pkg/syscall/zsyscall_freebsd_amd64.go
+++ b/src/pkg/syscall/zsyscall_freebsd_amd64.go
@@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) {
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
if e1 != 0 {
err = e1
@@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func utimes(path string, timeval *[2]Timeval) (err error) {
- _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0)
if e1 != 0 {
err = e1
}
@@ -266,7 +271,12 @@ func pipe() (r int, w int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Access(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -286,7 +296,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -296,7 +311,12 @@ func Chdir(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -306,7 +326,12 @@ func Chflags(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chmod(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -316,7 +341,12 @@ func Chmod(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -326,7 +356,12 @@ func Chown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chroot(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -384,7 +419,12 @@ func Fchdir(fd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -665,7 +705,12 @@ func Kqueue() (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lchown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -675,7 +720,17 @@ func Lchown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Link(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -695,7 +750,12 @@ func Listen(s int, backlog int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lstat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -705,7 +765,12 @@ func Lstat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdir(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -715,7 +780,12 @@ func Mkdir(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkfifo(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -725,7 +795,12 @@ func Mkfifo(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknod(path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
if e1 != 0 {
err = e1
}
@@ -745,7 +820,12 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Open(path string, mode int, perm uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
fd = int(r0)
if e1 != 0 {
err = e1
@@ -756,7 +836,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Pathconf(path string, name int) (val int, err error) {
- r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0)
val = int(r0)
if e1 != 0 {
err = e1
@@ -800,7 +885,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Read(fd int, p []byte) (n int, err error) {
+func read(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -818,13 +903,18 @@ func Read(fd int, p []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Readlink(path string, buf []byte) (n int, err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
+ _p1 = unsafe.Pointer(&buf[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
n = int(r0)
if e1 != 0 {
err = e1
@@ -835,7 +925,17 @@ func Readlink(path string, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rename(from string, to string) (err error) {
- _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(from)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(to)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -845,7 +945,12 @@ func Rename(from string, to string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Revoke(path string) (err error) {
- _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -855,7 +960,12 @@ func Revoke(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rmdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -916,7 +1026,12 @@ func Setgid(gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setlogin(name string) (err error) {
- _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1007,7 +1122,12 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1017,7 +1137,12 @@ func Stat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Statfs(path string, stat *Statfs_t) (err error) {
- _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1027,7 +1152,17 @@ func Statfs(path string, stat *Statfs_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Symlink(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -1047,7 +1182,12 @@ func Sync() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Truncate(path string, length int64) (err error) {
- _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), 0)
if e1 != 0 {
err = e1
}
@@ -1065,7 +1205,12 @@ func Umask(newmask int) (oldmask int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Undelete(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1075,7 +1220,12 @@ func Undelete(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlink(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1085,7 +1235,12 @@ func Unlink(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unmount(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -1094,7 +1249,7 @@ func Unmount(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Write(fd int, p []byte) (n int, err error) {
+func write(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -1132,7 +1287,7 @@ func munmap(addr uintptr, length uintptr) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func read(fd int, buf *byte, nbuf int) (n int, err error) {
+func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
@@ -1143,7 +1298,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func write(fd int, buf *byte, nbuf int) (n int, err error) {
+func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_freebsd_arm.go b/src/pkg/syscall/zsyscall_freebsd_arm.go
new file mode 100644
index 000000000..925b83fbb
--- /dev/null
+++ b/src/pkg/syscall/zsyscall_freebsd_arm.go
@@ -0,0 +1,1308 @@
+// mksyscall.pl -l32 syscall_bsd.go syscall_freebsd.go syscall_freebsd_386.go
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package syscall
+
+import "unsafe"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getgroups(ngid int, gid *_Gid_t) (n int, err error) {
+ r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setgroups(ngid int, gid *_Gid_t) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error) {
+ r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0)
+ wpid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
+ r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func bind(s int, addr uintptr, addrlen _Socklen) (err error) {
+ _, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func connect(s int, addr uintptr, addrlen _Socklen) (err error) {
+ _, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socket(domain int, typ int, proto int) (fd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) {
+ _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (err error) {
+ _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Shutdown(s int, how int) (err error) {
+ _, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(s), uintptr(how), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
+ _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
+ r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendmsg(s int, msg *Msghdr, flags int) (err error) {
+ _, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, err error) {
+ r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
+ var _p0 unsafe.Pointer
+ if len(mib) > 0 {
+ _p0 = unsafe.Pointer(&mib[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimes(path string, timeval *[2]Timeval) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func futimes(fd int, timeval *[2]Timeval) (err error) {
+ _, _, e1 := Syscall(SYS_FUTIMES, uintptr(fd), uintptr(unsafe.Pointer(timeval)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntl(fd int, cmd int, arg int) (val int, err error) {
+ r0, _, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
+ val = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe() (r int, w int, err error) {
+ r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
+ r = int(r0)
+ w = int(r1)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Access(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
+ _, _, e1 := Syscall(SYS_ADJTIME, uintptr(unsafe.Pointer(delta)), uintptr(unsafe.Pointer(olddelta)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chflags(path string, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chmod(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chown(path string, uid int, gid int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chroot(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (err error) {
+ _, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(fd int) (nfd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0)
+ nfd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup2(from int, to int) (err error) {
+ _, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Exit(code int) {
+ Syscall(SYS_EXIT, uintptr(code), 0, 0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchdir(fd int) (err error) {
+ _, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchflags(path string, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchmod(fd int, mode uint32) (err error) {
+ _, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchown(fd int, uid int, gid int) (err error) {
+ _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Flock(fd int, how int) (err error) {
+ _, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fpathconf(fd int, name int) (val int, err error) {
+ r0, _, e1 := Syscall(SYS_FPATHCONF, uintptr(fd), uintptr(name), 0)
+ val = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, stat *Stat_t) (err error) {
+ _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstatfs(fd int, stat *Statfs_t) (err error) {
+ _, _, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fsync(fd int) (err error) {
+ _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Ftruncate(fd int, length int64) (err error) {
+ _, _, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), uintptr(length>>32))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_GETDIRENTRIES, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0)
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getdtablesize() (size int) {
+ r0, _, _ := Syscall(SYS_GETDTABLESIZE, 0, 0, 0)
+ size = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getegid() (egid int) {
+ r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
+ egid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Geteuid() (uid int) {
+ r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
+ uid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), uintptr(len(buf)), uintptr(flags))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getgid() (gid int) {
+ r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
+ gid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgid(pid int) (pgid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
+ pgid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgrp() (pgrp int) {
+ r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
+ pgrp = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpid() (pid int) {
+ r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
+ pid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getppid() (ppid int) {
+ r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
+ ppid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpriority(which int, who int) (prio int, err error) {
+ r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)
+ prio = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrlimit(which int, lim *Rlimit) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrusage(who int, rusage *Rusage) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getsid(pid int) (sid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
+ sid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Gettimeofday(tv *Timeval) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getuid() (uid int) {
+ r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
+ uid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Issetugid() (tainted bool) {
+ r0, _, _ := Syscall(SYS_ISSETUGID, 0, 0, 0)
+ tainted = bool(r0 != 0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kill(pid int, signum Signal) (err error) {
+ _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kqueue() (fd int, err error) {
+ r0, _, e1 := Syscall(SYS_KQUEUE, 0, 0, 0)
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lchown(path string, uid int, gid int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Link(path string, link string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listen(s int, backlog int) (err error) {
+ _, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(backlog), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lstat(path string, stat *Stat_t) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkdir(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkfifo(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mknod(path string, mode uint32, dev int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Nanosleep(time *Timespec, leftover *Timespec) (err error) {
+ _, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Open(path string, mode int, perm uint32) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pathconf(path string, name int) (val int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0)
+ val = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pread(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Readlink(path string, buf []byte) (n int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(buf) > 0 {
+ _p1 = unsafe.Pointer(&buf[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Rename(from string, to string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(from)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(to)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Revoke(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Rmdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
+ r0, r1, e1 := Syscall6(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(offset>>32), uintptr(whence), 0, 0)
+ newoffset = int64(int64(r1)<<32 | int64(r0))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (err error) {
+ _, _, e1 := Syscall6(SYS_SELECT, uintptr(n), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setegid(egid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETEGID, uintptr(egid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Seteuid(euid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setgid(gid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setlogin(name string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpgid(pid int, pgid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpriority(which int, who int, prio int) (err error) {
+ _, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setregid(rgid int, egid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setreuid(ruid int, euid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setrlimit(which int, lim *Rlimit) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setsid() (pid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
+ pid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Settimeofday(tp *Timeval) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setuid(uid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Stat(path string, stat *Stat_t) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Statfs(path string, stat *Statfs_t) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Symlink(path string, link string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Sync() (err error) {
+ _, _, e1 := Syscall(SYS_SYNC, 0, 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Truncate(path string, length int64) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), uintptr(length>>32))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Umask(newmask int) (oldmask int) {
+ r0, _, _ := Syscall(SYS_UMASK, uintptr(newmask), 0, 0)
+ oldmask = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Undelete(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unlink(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unmount(path string, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) {
+ r0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), uintptr(pos), uintptr(pos>>32), 0, 0)
+ ret = uintptr(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func munmap(addr uintptr, length uintptr) (err error) {
+ _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func read(fd int, p []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func write(fd int, p []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
+ r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
+ r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
diff --git a/src/pkg/syscall/zsyscall_linux_386.go b/src/pkg/syscall/zsyscall_linux_386.go
index 1752fe462..3f0470c98 100644
--- a/src/pkg/syscall/zsyscall_linux_386.go
+++ b/src/pkg/syscall/zsyscall_linux_386.go
@@ -8,7 +8,12 @@ import "unsafe"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func open(path string, mode int, perm uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
fd = int(r0)
if e1 != 0 {
err = e1
@@ -19,7 +24,12 @@ func open(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
- r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), uintptr(mode), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0)
fd = int(r0)
if e1 != 0 {
err = e1
@@ -39,8 +49,38 @@ func pipe(p *[2]_C_int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func pipe2(p *[2]_C_int, flags int) (err error) {
+ _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func utimes(path string, times *[2]Timeval) (err error) {
- _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(times)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimensat(dirfd int, path string, times *[2]Timespec) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMENSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)))
if e1 != 0 {
err = e1
}
@@ -98,7 +138,12 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) {
- _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(StringBytePtr(arg))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(arg)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -108,7 +153,22 @@ func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func mount(source string, target string, fstype string, flags uintptr, data *byte) (err error) {
- _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(StringBytePtr(source))), uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(unsafe.Pointer(StringBytePtr(fstype))), uintptr(flags), uintptr(unsafe.Pointer(data)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(source)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(target)
+ if err != nil {
+ return
+ }
+ var _p2 *byte
+ _p2, err = BytePtrFromString(fstype)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(unsafe.Pointer(_p2)), uintptr(flags), uintptr(unsafe.Pointer(data)), 0)
if e1 != 0 {
err = e1
}
@@ -118,7 +178,12 @@ func mount(source string, target string, fstype string, flags uintptr, data *byt
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Access(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -128,7 +193,12 @@ func Access(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Acct(path string) (err error) {
- _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -149,7 +219,12 @@ func Adjtimex(buf *Timex) (state int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -159,7 +234,12 @@ func Chdir(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chmod(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -169,7 +249,12 @@ func Chmod(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chroot(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -189,7 +274,12 @@ func Close(fd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Creat(path string, mode uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
fd = int(r0)
if e1 != 0 {
err = e1
@@ -277,7 +367,12 @@ func Exit(code int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
- _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
if e1 != 0 {
err = e1
}
@@ -317,7 +412,12 @@ func Fchmod(fd int, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
- _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
if e1 != 0 {
err = e1
}
@@ -327,7 +427,12 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) {
- _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -429,16 +534,6 @@ func Getppid() (ppid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Getrlimit(resource int, rlim *Rlimit) (err error) {
- _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Getrusage(who int, rusage *Rusage) (err error) {
_, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
if e1 != 0 {
@@ -457,8 +552,40 @@ func Gettid() (tid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(attr)
+ if err != nil {
+ return
+ }
+ var _p2 unsafe.Pointer
+ if len(dest) > 0 {
+ _p2 = unsafe.Pointer(&dest[0])
+ } else {
+ _p2 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0)
+ sz = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) {
- r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(StringBytePtr(pathname))), uintptr(mask))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(pathname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mask))
watchdesc = int(r0)
if e1 != 0 {
err = e1
@@ -529,7 +656,39 @@ func Klogctl(typ int, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Link(oldpath string, newpath string) (err error) {
- _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(oldpath)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(newpath)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listxattr(path string, dest []byte) (sz int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(dest) > 0 {
+ _p1 = unsafe.Pointer(&dest[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)))
+ sz = int(r0)
if e1 != 0 {
err = e1
}
@@ -539,7 +698,12 @@ func Link(oldpath string, newpath string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdir(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -549,7 +713,12 @@ func Mkdir(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdirat(dirfd int, path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode))
if e1 != 0 {
err = e1
}
@@ -559,7 +728,12 @@ func Mkdirat(dirfd int, path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknod(path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
if e1 != 0 {
err = e1
}
@@ -569,7 +743,12 @@ func Mknod(path string, mode uint32, dev int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0)
if e1 != 0 {
err = e1
}
@@ -599,7 +778,17 @@ func Pause() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func PivotRoot(newroot string, putold string) (err error) {
- _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(StringBytePtr(newroot))), uintptr(unsafe.Pointer(StringBytePtr(putold))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(newroot)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(putold)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -608,7 +797,17 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Read(fd int, p []byte) (n int, err error) {
+func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
+ _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func read(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -626,13 +825,18 @@ func Read(fd int, p []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Readlink(path string, buf []byte) (n int, err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
+ _p1 = unsafe.Pointer(&buf[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
n = int(r0)
if e1 != 0 {
err = e1
@@ -642,8 +846,38 @@ func Readlink(path string, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Removexattr(path string, attr string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(attr)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Rename(oldpath string, newpath string) (err error) {
- _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(oldpath)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(newpath)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -653,7 +887,17 @@ func Rename(oldpath string, newpath string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) {
- _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(newdirfd), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(oldpath)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(newpath)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -663,7 +907,12 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err e
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rmdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -714,8 +963,9 @@ func Setpgid(pid int, pgid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setrlimit(resource int, rlim *Rlimit) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+func Setsid() (pid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
+ pid = int(r0)
if e1 != 0 {
err = e1
}
@@ -724,9 +974,8 @@ func Setrlimit(resource int, rlim *Rlimit) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setsid() (pid int, err error) {
- r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
- pid = int(r0)
+func Settimeofday(tv *Timeval) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -735,8 +984,8 @@ func Setsid() (pid int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Settimeofday(tv *Timeval) (err error) {
- _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+func Setuid(uid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
if e1 != 0 {
err = e1
}
@@ -745,8 +994,24 @@ func Settimeofday(tv *Timeval) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setuid(uid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
+func Setxattr(path string, attr string, data []byte, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(attr)
+ if err != nil {
+ return
+ }
+ var _p2 unsafe.Pointer
+ if len(data) > 0 {
+ _p2 = unsafe.Pointer(&data[0])
+ } else {
+ _p2 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := Syscall6(SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -756,7 +1021,17 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Symlink(oldpath string, newpath string) (err error) {
- _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(oldpath)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(newpath)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -833,7 +1108,12 @@ func Uname(buf *Utsname) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlink(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -843,7 +1123,12 @@ func Unlink(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlinkat(dirfd int, path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), 0)
if e1 != 0 {
err = e1
}
@@ -853,7 +1138,12 @@ func Unlinkat(dirfd int, path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unmount(target string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(target)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -883,7 +1173,12 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Utime(path string, buf *Utimbuf) (err error) {
- _, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(buf)), 0)
if e1 != 0 {
err = e1
}
@@ -892,7 +1187,7 @@ func Utime(path string, buf *Utimbuf) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Write(fd int, p []byte) (n int, err error) {
+func write(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -919,7 +1214,7 @@ func exitThread(code int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func read(fd int, p *byte, np int) (n int, err error) {
+func readlen(fd int, p *byte, np int) (n int, err error) {
r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np))
n = int(r0)
if e1 != 0 {
@@ -930,7 +1225,7 @@ func read(fd int, p *byte, np int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func write(fd int, p *byte, np int) (n int, err error) {
+func writelen(fd int, p *byte, np int) (n int, err error) {
r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np))
n = int(r0)
if e1 != 0 {
@@ -1036,7 +1331,12 @@ func Munlockall() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_CHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN32, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -1128,7 +1428,12 @@ func Iopl(level int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lchown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_LCHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN32, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -1138,7 +1443,12 @@ func Lchown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lstat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1181,7 +1491,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
r0, _, e1 := Syscall6(SYS_SENDFILE64, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0)
written = int(r0)
if e1 != 0 {
@@ -1274,7 +1584,12 @@ func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n i
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1294,7 +1609,12 @@ func SyncFileRange(fd int, off int64, n int64, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Truncate(path string, length int64) (err error) {
- _, _, e1 := Syscall(SYS_TRUNCATE64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), uintptr(length>>32))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_TRUNCATE64, uintptr(unsafe.Pointer(_p0)), uintptr(length), uintptr(length>>32))
if e1 != 0 {
err = e1
}
@@ -1346,6 +1666,26 @@ func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func getrlimit(resource int, rlim *rlimit32) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setrlimit(resource int, rlim *rlimit32) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Gettimeofday(tv *Timeval) (err error) {
_, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_linux_amd64.go b/src/pkg/syscall/zsyscall_linux_amd64.go
index d27346f7f..43f24e773 100644
--- a/src/pkg/syscall/zsyscall_linux_amd64.go
+++ b/src/pkg/syscall/zsyscall_linux_amd64.go
@@ -8,7 +8,12 @@ import "unsafe"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func open(path string, mode int, perm uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
fd = int(r0)
if e1 != 0 {
err = e1
@@ -19,7 +24,12 @@ func open(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
- r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), uintptr(mode), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0)
fd = int(r0)
if e1 != 0 {
err = e1
@@ -39,8 +49,38 @@ func pipe(p *[2]_C_int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func pipe2(p *[2]_C_int, flags int) (err error) {
+ _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func utimes(path string, times *[2]Timeval) (err error) {
- _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(times)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimensat(dirfd int, path string, times *[2]Timespec) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMENSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)))
if e1 != 0 {
err = e1
}
@@ -98,7 +138,12 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) {
- _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(StringBytePtr(arg))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(arg)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -108,7 +153,22 @@ func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func mount(source string, target string, fstype string, flags uintptr, data *byte) (err error) {
- _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(StringBytePtr(source))), uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(unsafe.Pointer(StringBytePtr(fstype))), uintptr(flags), uintptr(unsafe.Pointer(data)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(source)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(target)
+ if err != nil {
+ return
+ }
+ var _p2 *byte
+ _p2, err = BytePtrFromString(fstype)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(unsafe.Pointer(_p2)), uintptr(flags), uintptr(unsafe.Pointer(data)), 0)
if e1 != 0 {
err = e1
}
@@ -118,7 +178,12 @@ func mount(source string, target string, fstype string, flags uintptr, data *byt
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Access(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -128,7 +193,12 @@ func Access(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Acct(path string) (err error) {
- _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -149,7 +219,12 @@ func Adjtimex(buf *Timex) (state int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -159,7 +234,12 @@ func Chdir(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chmod(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -169,7 +249,12 @@ func Chmod(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chroot(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -189,7 +274,12 @@ func Close(fd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Creat(path string, mode uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
fd = int(r0)
if e1 != 0 {
err = e1
@@ -277,7 +367,12 @@ func Exit(code int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
- _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
if e1 != 0 {
err = e1
}
@@ -317,7 +412,12 @@ func Fchmod(fd int, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
- _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
if e1 != 0 {
err = e1
}
@@ -327,7 +427,12 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) {
- _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -429,16 +534,6 @@ func Getppid() (ppid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Getrlimit(resource int, rlim *Rlimit) (err error) {
- _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Getrusage(who int, rusage *Rusage) (err error) {
_, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
if e1 != 0 {
@@ -457,8 +552,40 @@ func Gettid() (tid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(attr)
+ if err != nil {
+ return
+ }
+ var _p2 unsafe.Pointer
+ if len(dest) > 0 {
+ _p2 = unsafe.Pointer(&dest[0])
+ } else {
+ _p2 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0)
+ sz = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) {
- r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(StringBytePtr(pathname))), uintptr(mask))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(pathname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mask))
watchdesc = int(r0)
if e1 != 0 {
err = e1
@@ -529,7 +656,39 @@ func Klogctl(typ int, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Link(oldpath string, newpath string) (err error) {
- _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(oldpath)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(newpath)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listxattr(path string, dest []byte) (sz int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(dest) > 0 {
+ _p1 = unsafe.Pointer(&dest[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)))
+ sz = int(r0)
if e1 != 0 {
err = e1
}
@@ -539,7 +698,12 @@ func Link(oldpath string, newpath string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdir(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -549,7 +713,12 @@ func Mkdir(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdirat(dirfd int, path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode))
if e1 != 0 {
err = e1
}
@@ -559,7 +728,12 @@ func Mkdirat(dirfd int, path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknod(path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
if e1 != 0 {
err = e1
}
@@ -569,7 +743,12 @@ func Mknod(path string, mode uint32, dev int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0)
if e1 != 0 {
err = e1
}
@@ -599,7 +778,17 @@ func Pause() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func PivotRoot(newroot string, putold string) (err error) {
- _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(StringBytePtr(newroot))), uintptr(unsafe.Pointer(StringBytePtr(putold))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(newroot)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(putold)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -608,7 +797,17 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Read(fd int, p []byte) (n int, err error) {
+func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
+ _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func read(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -626,13 +825,18 @@ func Read(fd int, p []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Readlink(path string, buf []byte) (n int, err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
+ _p1 = unsafe.Pointer(&buf[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
n = int(r0)
if e1 != 0 {
err = e1
@@ -642,8 +846,38 @@ func Readlink(path string, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Removexattr(path string, attr string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(attr)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Rename(oldpath string, newpath string) (err error) {
- _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(oldpath)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(newpath)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -653,7 +887,17 @@ func Rename(oldpath string, newpath string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) {
- _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(newdirfd), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(oldpath)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(newpath)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -663,7 +907,12 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err e
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rmdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -714,8 +963,9 @@ func Setpgid(pid int, pgid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setrlimit(resource int, rlim *Rlimit) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+func Setsid() (pid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
+ pid = int(r0)
if e1 != 0 {
err = e1
}
@@ -724,9 +974,8 @@ func Setrlimit(resource int, rlim *Rlimit) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setsid() (pid int, err error) {
- r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
- pid = int(r0)
+func Settimeofday(tv *Timeval) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -735,8 +984,8 @@ func Setsid() (pid int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Settimeofday(tv *Timeval) (err error) {
- _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+func Setuid(uid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
if e1 != 0 {
err = e1
}
@@ -745,8 +994,24 @@ func Settimeofday(tv *Timeval) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setuid(uid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
+func Setxattr(path string, attr string, data []byte, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(attr)
+ if err != nil {
+ return
+ }
+ var _p2 unsafe.Pointer
+ if len(data) > 0 {
+ _p2 = unsafe.Pointer(&data[0])
+ } else {
+ _p2 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := Syscall6(SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -756,7 +1021,17 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Symlink(oldpath string, newpath string) (err error) {
- _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(oldpath)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(newpath)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -833,7 +1108,12 @@ func Uname(buf *Utsname) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlink(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -843,7 +1123,12 @@ func Unlink(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlinkat(dirfd int, path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), 0)
if e1 != 0 {
err = e1
}
@@ -853,7 +1138,12 @@ func Unlinkat(dirfd int, path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unmount(target string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(target)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -883,7 +1173,12 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Utime(path string, buf *Utimbuf) (err error) {
- _, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(buf)), 0)
if e1 != 0 {
err = e1
}
@@ -892,7 +1187,7 @@ func Utime(path string, buf *Utimbuf) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Write(fd int, p []byte) (n int, err error) {
+func write(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -919,7 +1214,7 @@ func exitThread(code int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func read(fd int, p *byte, np int) (n int, err error) {
+func readlen(fd int, p *byte, np int) (n int, err error) {
r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np))
n = int(r0)
if e1 != 0 {
@@ -930,7 +1225,7 @@ func read(fd int, p *byte, np int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func write(fd int, p *byte, np int) (n int, err error) {
+func writelen(fd int, p *byte, np int) (n int, err error) {
r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np))
n = int(r0)
if e1 != 0 {
@@ -1036,7 +1331,12 @@ func Munlockall() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -1109,6 +1409,16 @@ func Getgid() (gid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Getrlimit(resource int, rlim *Rlimit) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Getuid() (uid int) {
r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
uid = int(r0)
@@ -1138,7 +1448,12 @@ func Iopl(level int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lchown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -1158,7 +1473,12 @@ func Listen(s int, n int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lstat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1223,7 +1543,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
r0, _, e1 := Syscall6(SYS_SENDFILE, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0)
written = int(r0)
if e1 != 0 {
@@ -1294,6 +1614,16 @@ func Setresuid(ruid int, euid int, suid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Setrlimit(resource int, rlim *Rlimit) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Setreuid(ruid int, euid int) (err error) {
_, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
if e1 != 0 {
@@ -1326,7 +1656,12 @@ func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n i
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1336,7 +1671,12 @@ func Stat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Statfs(path string, buf *Statfs_t) (err error) {
- _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(buf)), 0)
if e1 != 0 {
err = e1
}
@@ -1356,7 +1696,12 @@ func SyncFileRange(fd int, off int64, n int64, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Truncate(path string, length int64) (err error) {
- _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), 0)
if e1 != 0 {
err = e1
}
@@ -1376,6 +1721,17 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) {
+ r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0)
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func bind(s int, addr uintptr, addrlen _Socklen) (err error) {
_, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen))
if e1 != 0 {
@@ -1448,7 +1804,7 @@ func socket(domain int, typ int, proto int) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) {
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
if e1 != 0 {
err = e1
diff --git a/src/pkg/syscall/zsyscall_linux_arm.go b/src/pkg/syscall/zsyscall_linux_arm.go
index b1a59a69e..804821eb3 100644
--- a/src/pkg/syscall/zsyscall_linux_arm.go
+++ b/src/pkg/syscall/zsyscall_linux_arm.go
@@ -8,7 +8,12 @@ import "unsafe"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func open(path string, mode int, perm uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
fd = int(r0)
if e1 != 0 {
err = e1
@@ -19,7 +24,12 @@ func open(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
- r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), uintptr(mode), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0)
fd = int(r0)
if e1 != 0 {
err = e1
@@ -39,8 +49,38 @@ func pipe(p *[2]_C_int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func pipe2(p *[2]_C_int, flags int) (err error) {
+ _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func utimes(path string, times *[2]Timeval) (err error) {
- _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(times)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimensat(dirfd int, path string, times *[2]Timespec) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMENSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)))
if e1 != 0 {
err = e1
}
@@ -98,7 +138,12 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) {
- _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(StringBytePtr(arg))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(arg)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -108,7 +153,22 @@ func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func mount(source string, target string, fstype string, flags uintptr, data *byte) (err error) {
- _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(StringBytePtr(source))), uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(unsafe.Pointer(StringBytePtr(fstype))), uintptr(flags), uintptr(unsafe.Pointer(data)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(source)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(target)
+ if err != nil {
+ return
+ }
+ var _p2 *byte
+ _p2, err = BytePtrFromString(fstype)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(unsafe.Pointer(_p2)), uintptr(flags), uintptr(unsafe.Pointer(data)), 0)
if e1 != 0 {
err = e1
}
@@ -118,7 +178,12 @@ func mount(source string, target string, fstype string, flags uintptr, data *byt
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Access(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -128,7 +193,12 @@ func Access(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Acct(path string) (err error) {
- _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -149,7 +219,12 @@ func Adjtimex(buf *Timex) (state int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -159,7 +234,12 @@ func Chdir(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chmod(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -169,7 +249,12 @@ func Chmod(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chroot(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -189,7 +274,12 @@ func Close(fd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Creat(path string, mode uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
fd = int(r0)
if e1 != 0 {
err = e1
@@ -277,7 +367,12 @@ func Exit(code int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
- _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
if e1 != 0 {
err = e1
}
@@ -317,7 +412,12 @@ func Fchmod(fd int, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
- _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
if e1 != 0 {
err = e1
}
@@ -327,7 +427,12 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) {
- _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -429,16 +534,6 @@ func Getppid() (ppid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Getrlimit(resource int, rlim *Rlimit) (err error) {
- _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Getrusage(who int, rusage *Rusage) (err error) {
_, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
if e1 != 0 {
@@ -457,8 +552,40 @@ func Gettid() (tid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(attr)
+ if err != nil {
+ return
+ }
+ var _p2 unsafe.Pointer
+ if len(dest) > 0 {
+ _p2 = unsafe.Pointer(&dest[0])
+ } else {
+ _p2 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0)
+ sz = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) {
- r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(StringBytePtr(pathname))), uintptr(mask))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(pathname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mask))
watchdesc = int(r0)
if e1 != 0 {
err = e1
@@ -529,7 +656,39 @@ func Klogctl(typ int, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Link(oldpath string, newpath string) (err error) {
- _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(oldpath)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(newpath)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listxattr(path string, dest []byte) (sz int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(dest) > 0 {
+ _p1 = unsafe.Pointer(&dest[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)))
+ sz = int(r0)
if e1 != 0 {
err = e1
}
@@ -539,7 +698,12 @@ func Link(oldpath string, newpath string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdir(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -549,7 +713,12 @@ func Mkdir(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdirat(dirfd int, path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode))
if e1 != 0 {
err = e1
}
@@ -559,7 +728,12 @@ func Mkdirat(dirfd int, path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknod(path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
if e1 != 0 {
err = e1
}
@@ -569,7 +743,12 @@ func Mknod(path string, mode uint32, dev int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0)
if e1 != 0 {
err = e1
}
@@ -599,7 +778,17 @@ func Pause() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func PivotRoot(newroot string, putold string) (err error) {
- _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(StringBytePtr(newroot))), uintptr(unsafe.Pointer(StringBytePtr(putold))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(newroot)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(putold)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -608,7 +797,17 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Read(fd int, p []byte) (n int, err error) {
+func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
+ _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func read(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -626,13 +825,18 @@ func Read(fd int, p []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Readlink(path string, buf []byte) (n int, err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
+ _p1 = unsafe.Pointer(&buf[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
n = int(r0)
if e1 != 0 {
err = e1
@@ -642,8 +846,38 @@ func Readlink(path string, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Removexattr(path string, attr string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(attr)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Rename(oldpath string, newpath string) (err error) {
- _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(oldpath)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(newpath)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -653,7 +887,17 @@ func Rename(oldpath string, newpath string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) {
- _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(newdirfd), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(oldpath)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(newpath)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -663,7 +907,12 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err e
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rmdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -714,8 +963,9 @@ func Setpgid(pid int, pgid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setrlimit(resource int, rlim *Rlimit) (err error) {
- _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+func Setsid() (pid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
+ pid = int(r0)
if e1 != 0 {
err = e1
}
@@ -724,9 +974,8 @@ func Setrlimit(resource int, rlim *Rlimit) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setsid() (pid int, err error) {
- r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
- pid = int(r0)
+func Settimeofday(tv *Timeval) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -735,8 +984,8 @@ func Setsid() (pid int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Settimeofday(tv *Timeval) (err error) {
- _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+func Setuid(uid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
if e1 != 0 {
err = e1
}
@@ -745,8 +994,24 @@ func Settimeofday(tv *Timeval) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Setuid(uid int) (err error) {
- _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
+func Setxattr(path string, attr string, data []byte, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(attr)
+ if err != nil {
+ return
+ }
+ var _p2 unsafe.Pointer
+ if len(data) > 0 {
+ _p2 = unsafe.Pointer(&data[0])
+ } else {
+ _p2 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := Syscall6(SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -756,7 +1021,17 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Symlink(oldpath string, newpath string) (err error) {
- _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(oldpath)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(newpath)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -833,7 +1108,12 @@ func Uname(buf *Utsname) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlink(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -843,7 +1123,12 @@ func Unlink(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlinkat(dirfd int, path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), 0)
if e1 != 0 {
err = e1
}
@@ -853,7 +1138,12 @@ func Unlinkat(dirfd int, path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unmount(target string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(StringBytePtr(target))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(target)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -883,7 +1173,12 @@ func Ustat(dev int, ubuf *Ustat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Utime(path string, buf *Utimbuf) (err error) {
- _, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(buf)), 0)
if e1 != 0 {
err = e1
}
@@ -892,7 +1187,7 @@ func Utime(path string, buf *Utimbuf) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Write(fd int, p []byte) (n int, err error) {
+func write(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -919,7 +1214,7 @@ func exitThread(code int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func read(fd int, p *byte, np int) (n int, err error) {
+func readlen(fd int, p *byte, np int) (n int, err error) {
r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np))
n = int(r0)
if e1 != 0 {
@@ -930,7 +1225,7 @@ func read(fd int, p *byte, np int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func write(fd int, p *byte, np int) (n int, err error) {
+func writelen(fd int, p *byte, np int) (n int, err error) {
r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np))
n = int(r0)
if e1 != 0 {
@@ -1046,6 +1341,17 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) {
+ r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0)
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func bind(s int, addr uintptr, addrlen _Socklen) (err error) {
_, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen))
if e1 != 0 {
@@ -1171,7 +1477,7 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (err err
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func socketpair(domain int, typ int, flags int, fd *[2]int) (err error) {
+func socketpair(domain int, typ int, flags int, fd *[2]int32) (err error) {
_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(flags), uintptr(unsafe.Pointer(fd)), 0, 0)
if e1 != 0 {
err = e1
@@ -1203,7 +1509,12 @@ func sendmsg(s int, msg *Msghdr, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_CHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN32, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -1232,16 +1543,6 @@ func Fstat(fd int, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Fstatfs(fd int, buf *Statfs_t) (err error) {
- _, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(buf)), 0)
- if e1 != 0 {
- err = e1
- }
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func Getegid() (egid int) {
r0, _, _ := RawSyscall(SYS_GETEGID32, 0, 0, 0)
egid = int(r0)
@@ -1275,7 +1576,12 @@ func Getuid() (uid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lchown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_LCHOWN32, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN32, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -1295,7 +1601,12 @@ func Listen(s int, n int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lstat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1304,7 +1615,7 @@ func Lstat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
r0, _, e1 := Syscall6(SYS_SENDFILE64, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0)
written = int(r0)
if e1 != 0 {
@@ -1418,17 +1729,12 @@ func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n i
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
- if e1 != 0 {
- err = e1
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
}
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func Statfs(path string, buf *Statfs_t) (err error) {
- _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0)
+ _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1493,7 +1799,12 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Truncate(path string, length int64) (err error) {
- _, _, e1 := Syscall6(SYS_TRUNCATE64, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, uintptr(length), uintptr(length>>32), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_TRUNCATE64, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length), uintptr(length>>32), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1520,3 +1831,23 @@ func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset
}
return
}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getrlimit(resource int, rlim *rlimit32) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setrlimit(resource int, rlim *rlimit32) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
diff --git a/src/pkg/syscall/zsyscall_netbsd_386.go b/src/pkg/syscall/zsyscall_netbsd_386.go
index 6155a1687..10ac072db 100644
--- a/src/pkg/syscall/zsyscall_netbsd_386.go
+++ b/src/pkg/syscall/zsyscall_netbsd_386.go
@@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) {
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
if e1 != 0 {
err = e1
@@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func utimes(path string, timeval *[2]Timeval) (err error) {
- _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0)
if e1 != 0 {
err = e1
}
@@ -253,8 +258,10 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func pipe2(p *[2]_C_int, flags _C_int) (err error) {
- _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+func pipe() (fd1 int, fd2 int, err error) {
+ r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
+ fd1 = int(r0)
+ fd2 = int(r1)
if e1 != 0 {
err = e1
}
@@ -281,7 +288,12 @@ func getdents(fd int, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Access(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -301,7 +313,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -311,7 +328,12 @@ func Chdir(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -321,7 +343,12 @@ func Chflags(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chmod(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -331,7 +358,12 @@ func Chmod(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -341,7 +373,12 @@ func Chown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chroot(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -399,7 +436,12 @@ func Fchdir(fd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -606,7 +648,7 @@ func Issetugid() (tainted bool) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Kill(pid int, signum int) (err error) {
+func Kill(pid int, signum Signal) (err error) {
_, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0)
if e1 != 0 {
err = e1
@@ -628,7 +670,12 @@ func Kqueue() (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lchown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -638,7 +685,17 @@ func Lchown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Link(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -658,7 +715,12 @@ func Listen(s int, backlog int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lstat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -668,7 +730,12 @@ func Lstat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdir(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -678,7 +745,12 @@ func Mkdir(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkfifo(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -688,7 +760,12 @@ func Mkfifo(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknod(path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
if e1 != 0 {
err = e1
}
@@ -708,7 +785,12 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Open(path string, mode int, perm uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
fd = int(r0)
if e1 != 0 {
err = e1
@@ -719,7 +801,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Pathconf(path string, name int) (val int, err error) {
- r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0)
val = int(r0)
if e1 != 0 {
err = e1
@@ -763,7 +850,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Read(fd int, p []byte) (n int, err error) {
+func read(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -781,13 +868,18 @@ func Read(fd int, p []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Readlink(path string, buf []byte) (n int, err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
+ _p1 = unsafe.Pointer(&buf[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
n = int(r0)
if e1 != 0 {
err = e1
@@ -798,7 +890,17 @@ func Readlink(path string, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rename(from string, to string) (err error) {
- _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(from)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(to)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -808,7 +910,12 @@ func Rename(from string, to string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Revoke(path string) (err error) {
- _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -818,7 +925,12 @@ func Revoke(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rmdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -960,7 +1072,12 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -970,7 +1087,17 @@ func Stat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Symlink(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -990,7 +1117,12 @@ func Sync() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Truncate(path string, length int64) (err error) {
- _, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, uintptr(length), uintptr(length>>32), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length), uintptr(length>>32), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1008,7 +1140,12 @@ func Umask(newmask int) (oldmask int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlink(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1018,7 +1155,12 @@ func Unlink(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unmount(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -1027,7 +1169,7 @@ func Unmount(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Write(fd int, p []byte) (n int, err error) {
+func write(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -1065,7 +1207,7 @@ func munmap(addr uintptr, length uintptr) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func read(fd int, buf *byte, nbuf int) (n int, err error) {
+func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
@@ -1076,7 +1218,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func write(fd int, buf *byte, nbuf int) (n int, err error) {
+func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_netbsd_amd64.go b/src/pkg/syscall/zsyscall_netbsd_amd64.go
index d68cd86d8..f10dc0bf4 100644
--- a/src/pkg/syscall/zsyscall_netbsd_amd64.go
+++ b/src/pkg/syscall/zsyscall_netbsd_amd64.go
@@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) {
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
if e1 != 0 {
err = e1
@@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func utimes(path string, timeval *[2]Timeval) (err error) {
- _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0)
if e1 != 0 {
err = e1
}
@@ -253,8 +258,10 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func pipe2(p *[2]_C_int, flags _C_int) (err error) {
- _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+func pipe() (fd1 int, fd2 int, err error) {
+ r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
+ fd1 = int(r0)
+ fd2 = int(r1)
if e1 != 0 {
err = e1
}
@@ -281,7 +288,12 @@ func getdents(fd int, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Access(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -301,7 +313,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -311,7 +328,12 @@ func Chdir(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -321,7 +343,12 @@ func Chflags(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chmod(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -331,7 +358,12 @@ func Chmod(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -341,7 +373,12 @@ func Chown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chroot(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -399,7 +436,12 @@ func Fchdir(fd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -606,7 +648,7 @@ func Issetugid() (tainted bool) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Kill(pid int, signum int) (err error) {
+func Kill(pid int, signum Signal) (err error) {
_, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0)
if e1 != 0 {
err = e1
@@ -628,7 +670,12 @@ func Kqueue() (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lchown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -638,7 +685,17 @@ func Lchown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Link(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -658,7 +715,12 @@ func Listen(s int, backlog int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lstat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -668,7 +730,12 @@ func Lstat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdir(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -678,7 +745,12 @@ func Mkdir(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkfifo(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -688,7 +760,12 @@ func Mkfifo(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknod(path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
if e1 != 0 {
err = e1
}
@@ -708,7 +785,12 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Open(path string, mode int, perm uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
fd = int(r0)
if e1 != 0 {
err = e1
@@ -719,7 +801,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Pathconf(path string, name int) (val int, err error) {
- r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0)
val = int(r0)
if e1 != 0 {
err = e1
@@ -763,7 +850,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Read(fd int, p []byte) (n int, err error) {
+func read(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -781,13 +868,18 @@ func Read(fd int, p []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Readlink(path string, buf []byte) (n int, err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
+ _p1 = unsafe.Pointer(&buf[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
n = int(r0)
if e1 != 0 {
err = e1
@@ -798,7 +890,17 @@ func Readlink(path string, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rename(from string, to string) (err error) {
- _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(from)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(to)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -808,7 +910,12 @@ func Rename(from string, to string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Revoke(path string) (err error) {
- _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -818,7 +925,12 @@ func Revoke(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rmdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -960,7 +1072,12 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -970,7 +1087,17 @@ func Stat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Symlink(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -990,7 +1117,12 @@ func Sync() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Truncate(path string, length int64) (err error) {
- _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, uintptr(length))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length))
if e1 != 0 {
err = e1
}
@@ -1008,7 +1140,12 @@ func Umask(newmask int) (oldmask int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlink(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1018,7 +1155,12 @@ func Unlink(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unmount(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -1027,7 +1169,7 @@ func Unmount(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Write(fd int, p []byte) (n int, err error) {
+func write(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -1065,7 +1207,7 @@ func munmap(addr uintptr, length uintptr) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func read(fd int, buf *byte, nbuf int) (n int, err error) {
+func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
@@ -1076,7 +1218,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func write(fd int, buf *byte, nbuf int) (n int, err error) {
+func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_netbsd_arm.go b/src/pkg/syscall/zsyscall_netbsd_arm.go
new file mode 100644
index 000000000..763a906e5
--- /dev/null
+++ b/src/pkg/syscall/zsyscall_netbsd_arm.go
@@ -0,0 +1,1228 @@
+// mksyscall.pl -l32 -netbsd syscall_bsd.go syscall_netbsd.go syscall_netbsd_arm.go
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package syscall
+
+import "unsafe"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getgroups(ngid int, gid *_Gid_t) (n int, err error) {
+ r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setgroups(ngid int, gid *_Gid_t) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error) {
+ r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0)
+ wpid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
+ r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func bind(s int, addr uintptr, addrlen _Socklen) (err error) {
+ _, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func connect(s int, addr uintptr, addrlen _Socklen) (err error) {
+ _, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socket(domain int, typ int, proto int) (fd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) {
+ _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (err error) {
+ _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Shutdown(s int, how int) (err error) {
+ _, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(s), uintptr(how), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
+ _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
+ r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendmsg(s int, msg *Msghdr, flags int) (err error) {
+ _, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, err error) {
+ r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
+ var _p0 unsafe.Pointer
+ if len(mib) > 0 {
+ _p0 = unsafe.Pointer(&mib[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimes(path string, timeval *[2]Timeval) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func futimes(fd int, timeval *[2]Timeval) (err error) {
+ _, _, e1 := Syscall(SYS_FUTIMES, uintptr(fd), uintptr(unsafe.Pointer(timeval)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntl(fd int, cmd int, arg int) (val int, err error) {
+ r0, _, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
+ val = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe() (fd1 int, fd2 int, err error) {
+ r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0)
+ fd1 = int(r0)
+ fd2 = int(r1)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getdents(fd int, buf []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_GETDENTS, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Access(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
+ _, _, e1 := Syscall(SYS_ADJTIME, uintptr(unsafe.Pointer(delta)), uintptr(unsafe.Pointer(olddelta)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chflags(path string, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chmod(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chown(path string, uid int, gid int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chroot(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (err error) {
+ _, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(fd int) (nfd int, err error) {
+ r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0)
+ nfd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup2(from int, to int) (err error) {
+ _, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Exit(code int) {
+ Syscall(SYS_EXIT, uintptr(code), 0, 0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchdir(fd int) (err error) {
+ _, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchflags(path string, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchmod(fd int, mode uint32) (err error) {
+ _, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchown(fd int, uid int, gid int) (err error) {
+ _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Flock(fd int, how int) (err error) {
+ _, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fpathconf(fd int, name int) (val int, err error) {
+ r0, _, e1 := Syscall(SYS_FPATHCONF, uintptr(fd), uintptr(name), 0)
+ val = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, stat *Stat_t) (err error) {
+ _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fsync(fd int) (err error) {
+ _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Ftruncate(fd int, length int64) (err error) {
+ _, _, e1 := Syscall6(SYS_FTRUNCATE, uintptr(fd), 0, uintptr(length), uintptr(length>>32), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getegid() (egid int) {
+ r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
+ egid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Geteuid() (uid int) {
+ r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
+ uid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getgid() (gid int) {
+ r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
+ gid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgid(pid int) (pgid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
+ pgid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgrp() (pgrp int) {
+ r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0)
+ pgrp = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpid() (pid int) {
+ r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
+ pid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getppid() (ppid int) {
+ r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
+ ppid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpriority(which int, who int) (prio int, err error) {
+ r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)
+ prio = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrlimit(which int, lim *Rlimit) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrusage(who int, rusage *Rusage) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getsid(pid int) (sid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
+ sid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Gettimeofday(tv *Timeval) (err error) {
+ _, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getuid() (uid int) {
+ r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
+ uid = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Issetugid() (tainted bool) {
+ r0, _, _ := Syscall(SYS_ISSETUGID, 0, 0, 0)
+ tainted = bool(r0 != 0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kill(pid int, signum Signal) (err error) {
+ _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kqueue() (fd int, err error) {
+ r0, _, e1 := Syscall(SYS_KQUEUE, 0, 0, 0)
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lchown(path string, uid int, gid int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Link(path string, link string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listen(s int, backlog int) (err error) {
+ _, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(backlog), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lstat(path string, stat *Stat_t) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkdir(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkfifo(path string, mode uint32) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mknod(path string, mode uint32, dev int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Nanosleep(time *Timespec, leftover *Timespec) (err error) {
+ _, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Open(path string, mode int, perm uint32) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
+ fd = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pathconf(path string, name int) (val int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0)
+ val = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pread(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), uintptr(offset>>32))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), uintptr(offset>>32))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func read(fd int, p []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Readlink(path string, buf []byte) (n int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(buf) > 0 {
+ _p1 = unsafe.Pointer(&buf[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Rename(from string, to string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(from)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(to)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Revoke(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Rmdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
+ r0, r1, e1 := Syscall6(SYS_LSEEK, uintptr(fd), 0, uintptr(offset), uintptr(offset>>32), uintptr(whence), 0)
+ newoffset = int64(int64(r1)<<32 | int64(r0))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (err error) {
+ _, _, e1 := Syscall6(SYS_SELECT, uintptr(n), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setegid(egid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETEGID, uintptr(egid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Seteuid(euid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setgid(gid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpgid(pid int, pgid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpriority(which int, who int, prio int) (err error) {
+ _, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio))
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setregid(rgid int, egid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setreuid(ruid int, euid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setrlimit(which int, lim *Rlimit) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setsid() (pid int, err error) {
+ r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
+ pid = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Settimeofday(tp *Timeval) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setuid(uid int) (err error) {
+ _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Stat(path string, stat *Stat_t) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Symlink(path string, link string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Sync() (err error) {
+ _, _, e1 := Syscall(SYS_SYNC, 0, 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Truncate(path string, length int64) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length), uintptr(length>>32), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Umask(newmask int) (oldmask int) {
+ r0, _, _ := Syscall(SYS_UMASK, uintptr(newmask), 0, 0)
+ oldmask = int(r0)
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unlink(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unmount(path string, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func write(fd int, p []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) {
+ r0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), 0, uintptr(pos), uintptr(pos>>32), 0)
+ ret = uintptr(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func munmap(addr uintptr, length uintptr) (err error) {
+ _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
+ r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
+ r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
+ n = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
diff --git a/src/pkg/syscall/zsyscall_openbsd_386.go b/src/pkg/syscall/zsyscall_openbsd_386.go
index c88492329..60e907bd2 100644
--- a/src/pkg/syscall/zsyscall_openbsd_386.go
+++ b/src/pkg/syscall/zsyscall_openbsd_386.go
@@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) {
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
if e1 != 0 {
err = e1
@@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func utimes(path string, timeval *[2]Timeval) (err error) {
- _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0)
if e1 != 0 {
err = e1
}
@@ -264,7 +269,12 @@ func pipe(p *[2]_C_int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Access(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -284,7 +294,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -294,7 +309,12 @@ func Chdir(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -304,7 +324,12 @@ func Chflags(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chmod(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -314,7 +339,12 @@ func Chmod(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -324,7 +354,12 @@ func Chown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chroot(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -382,7 +417,12 @@ func Fchdir(fd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -655,7 +695,12 @@ func Kqueue() (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lchown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -665,7 +710,17 @@ func Lchown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Link(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -685,7 +740,12 @@ func Listen(s int, backlog int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lstat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -695,7 +755,12 @@ func Lstat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdir(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -705,7 +770,12 @@ func Mkdir(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkfifo(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -715,7 +785,12 @@ func Mkfifo(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknod(path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
if e1 != 0 {
err = e1
}
@@ -735,7 +810,12 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Open(path string, mode int, perm uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
fd = int(r0)
if e1 != 0 {
err = e1
@@ -746,7 +826,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Pathconf(path string, name int) (val int, err error) {
- r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0)
val = int(r0)
if e1 != 0 {
err = e1
@@ -790,7 +875,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Read(fd int, p []byte) (n int, err error) {
+func read(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -808,13 +893,18 @@ func Read(fd int, p []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Readlink(path string, buf []byte) (n int, err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
+ _p1 = unsafe.Pointer(&buf[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
n = int(r0)
if e1 != 0 {
err = e1
@@ -825,7 +915,17 @@ func Readlink(path string, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rename(from string, to string) (err error) {
- _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(from)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(to)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -835,7 +935,12 @@ func Rename(from string, to string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Revoke(path string) (err error) {
- _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -845,7 +950,12 @@ func Revoke(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rmdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -906,7 +1016,12 @@ func Setgid(gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setlogin(name string) (err error) {
- _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -997,7 +1112,12 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1007,7 +1127,12 @@ func Stat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Statfs(path string, stat *Statfs_t) (err error) {
- _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1017,7 +1142,17 @@ func Statfs(path string, stat *Statfs_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Symlink(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -1037,7 +1172,12 @@ func Sync() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Truncate(path string, length int64) (err error) {
- _, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, uintptr(length), uintptr(length>>32), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length), uintptr(length>>32), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1055,7 +1195,12 @@ func Umask(newmask int) (oldmask int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlink(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1065,7 +1210,12 @@ func Unlink(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unmount(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -1074,7 +1224,7 @@ func Unmount(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Write(fd int, p []byte) (n int, err error) {
+func write(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -1112,7 +1262,7 @@ func munmap(addr uintptr, length uintptr) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func read(fd int, buf *byte, nbuf int) (n int, err error) {
+func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
@@ -1123,7 +1273,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func write(fd int, buf *byte, nbuf int) (n int, err error) {
+func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_openbsd_amd64.go b/src/pkg/syscall/zsyscall_openbsd_amd64.go
index 7775646c4..1403dd7e7 100644
--- a/src/pkg/syscall/zsyscall_openbsd_amd64.go
+++ b/src/pkg/syscall/zsyscall_openbsd_amd64.go
@@ -131,7 +131,7 @@ func Shutdown(s int, how int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func socketpair(domain int, typ int, proto int, fd *[2]int) (err error) {
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
if e1 != 0 {
err = e1
@@ -223,7 +223,12 @@ func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr)
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func utimes(path string, timeval *[2]Timeval) (err error) {
- _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(timeval)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0)
if e1 != 0 {
err = e1
}
@@ -264,7 +269,12 @@ func pipe(p *[2]_C_int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Access(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -284,7 +294,12 @@ func Adjtime(delta *Timeval, olddelta *Timeval) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -294,7 +309,12 @@ func Chdir(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -304,7 +324,12 @@ func Chflags(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chmod(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -314,7 +339,12 @@ func Chmod(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -324,7 +354,12 @@ func Chown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chroot(path string) (err error) {
- _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -382,7 +417,12 @@ func Fchdir(fd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fchflags(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -655,7 +695,12 @@ func Kqueue() (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lchown(path string, uid int, gid int) (err error) {
- _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
if e1 != 0 {
err = e1
}
@@ -665,7 +710,17 @@ func Lchown(path string, uid int, gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Link(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -685,7 +740,12 @@ func Listen(s int, backlog int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Lstat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -695,7 +755,12 @@ func Lstat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkdir(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -705,7 +770,12 @@ func Mkdir(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mkfifo(path string, mode uint32) (err error) {
- _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
if e1 != 0 {
err = e1
}
@@ -715,7 +785,12 @@ func Mkfifo(path string, mode uint32) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mknod(path string, mode uint32, dev int) (err error) {
- _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev))
if e1 != 0 {
err = e1
}
@@ -735,7 +810,12 @@ func Nanosleep(time *Timespec, leftover *Timespec) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Open(path string, mode int, perm uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
fd = int(r0)
if e1 != 0 {
err = e1
@@ -746,7 +826,12 @@ func Open(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Pathconf(path string, name int) (val int, err error) {
- r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0)
val = int(r0)
if e1 != 0 {
err = e1
@@ -790,7 +875,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Read(fd int, p []byte) (n int, err error) {
+func read(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -808,13 +893,18 @@ func Read(fd int, p []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Readlink(path string, buf []byte) (n int, err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(buf) > 0 {
- _p0 = unsafe.Pointer(&buf[0])
+ _p1 = unsafe.Pointer(&buf[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(buf)))
+ r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)))
n = int(r0)
if e1 != 0 {
err = e1
@@ -825,7 +915,17 @@ func Readlink(path string, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rename(from string, to string) (err error) {
- _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(from)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(to)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -835,7 +935,12 @@ func Rename(from string, to string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Revoke(path string) (err error) {
- _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -845,7 +950,12 @@ func Revoke(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Rmdir(path string) (err error) {
- _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -906,7 +1016,12 @@ func Setgid(gid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Setlogin(name string) (err error) {
- _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -997,7 +1112,12 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, stat *Stat_t) (err error) {
- _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1007,7 +1127,12 @@ func Stat(path string, stat *Stat_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Statfs(path string, stat *Statfs_t) (err error) {
- _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {
err = e1
}
@@ -1017,7 +1142,17 @@ func Statfs(path string, stat *Statfs_t) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Symlink(path string, link string) (err error) {
- _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(link)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
if e1 != 0 {
err = e1
}
@@ -1037,7 +1172,12 @@ func Sync() (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Truncate(path string, length int64) (err error) {
- _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, uintptr(length))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length))
if e1 != 0 {
err = e1
}
@@ -1055,7 +1195,12 @@ func Umask(newmask int) (oldmask int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unlink(path string) (err error) {
- _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0)
if e1 != 0 {
err = e1
}
@@ -1065,7 +1210,12 @@ func Unlink(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Unmount(path string, flags int) (err error) {
- _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
@@ -1074,7 +1224,7 @@ func Unmount(path string, flags int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func Write(fd int, p []byte) (n int, err error) {
+func write(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
@@ -1112,7 +1262,7 @@ func munmap(addr uintptr, length uintptr) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func read(fd int, buf *byte, nbuf int) (n int, err error) {
+func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
@@ -1123,7 +1273,7 @@ func read(fd int, buf *byte, nbuf int) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func write(fd int, buf *byte, nbuf int) (n int, err error) {
+func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
if e1 != 0 {
diff --git a/src/pkg/syscall/zsyscall_plan9_386.go b/src/pkg/syscall/zsyscall_plan9_386.go
index 913c28fa2..5ffa0e439 100644
--- a/src/pkg/syscall/zsyscall_plan9_386.go
+++ b/src/pkg/syscall/zsyscall_plan9_386.go
@@ -7,13 +7,6 @@ import "unsafe"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func exits(msg *byte) {
- Syscall(SYS_EXITS, uintptr(unsafe.Pointer(msg)), 0, 0)
- return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func fd2path(fd int, buf []byte) (err error) {
var _p0 unsafe.Pointer
if len(buf) > 0 {
@@ -69,7 +62,12 @@ func Dup(oldfd int, newfd int) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Open(path string, mode int) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
fd = int(r0)
if int(r0) == -1 {
err = e1
@@ -80,7 +78,12 @@ func Open(path string, mode int) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Create(path string, mode int, perm uint32) (fd int, err error) {
- r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
fd = int(r0)
if int(r0) == -1 {
err = e1
@@ -91,7 +94,12 @@ func Create(path string, mode int, perm uint32) (fd int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Remove(path string) (err error) {
- r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
if int(r0) == -1 {
err = e1
}
@@ -145,7 +153,12 @@ func Close(fd int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err error) {
- r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
if int(r0) == -1 {
err = e1
}
@@ -155,7 +168,17 @@ func Chdir(path string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Bind(name string, old string, flag int) (err error) {
- r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(StringBytePtr(name))), uintptr(unsafe.Pointer(StringBytePtr(old))), uintptr(flag))
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(old)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
if int(r0) == -1 {
err = e1
}
@@ -165,7 +188,17 @@ func Bind(name string, old string, flag int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mount(fd int, afd int, old string, flag int, aname string) (err error) {
- r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(StringBytePtr(old))), uintptr(flag), uintptr(unsafe.Pointer(StringBytePtr(aname))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(old)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(aname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
if int(r0) == -1 {
err = e1
}
@@ -175,13 +208,18 @@ func Mount(fd int, afd int, old string, flag int, aname string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, edir []byte) (n int, err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(edir) > 0 {
- _p0 = unsafe.Pointer(&edir[0])
+ _p1 = unsafe.Pointer(&edir[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(edir)))
+ r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
n = int(r0)
if int(r0) == -1 {
err = e1
@@ -209,13 +247,18 @@ func Fstat(fd int, edir []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Wstat(path string, edir []byte) (err error) {
- var _p0 unsafe.Pointer
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
if len(edir) > 0 {
- _p0 = unsafe.Pointer(&edir[0])
+ _p1 = unsafe.Pointer(&edir[0])
} else {
- _p0 = unsafe.Pointer(&_zero)
+ _p1 = unsafe.Pointer(&_zero)
}
- r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(edir)))
+ r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
if int(r0) == -1 {
err = e1
}
diff --git a/src/pkg/syscall/zsyscall_plan9_amd64.go b/src/pkg/syscall/zsyscall_plan9_amd64.go
new file mode 100644
index 000000000..c64533f49
--- /dev/null
+++ b/src/pkg/syscall/zsyscall_plan9_amd64.go
@@ -0,0 +1,282 @@
+// mksyscall.pl -l32 -plan9 syscall_plan9.go syscall_plan9_amd64.go
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package syscall
+
+import "unsafe"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fd2path(fd int, buf []byte) (err error) {
+ var _p0 unsafe.Pointer
+ if len(buf) > 0 {
+ _p0 = unsafe.Pointer(&buf[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe(p *[2]_C_int) (err error) {
+ r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func await(s []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(s) > 0 {
+ _p0 = unsafe.Pointer(&s[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(oldfd int, newfd int) (fd int, err error) {
+ r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Open(path string, mode int) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Create(path string, mode int, perm uint32) (fd int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
+ fd = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Remove(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pread(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(p) > 0 {
+ _p0 = unsafe.Pointer(&p[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (err error) {
+ r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chdir(path string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Bind(name string, old string, flag int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(old)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mount(fd int, afd int, old string, flag int, aname string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(old)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(aname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Stat(path string, edir []byte) (n int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(edir) > 0 {
+ _p1 = unsafe.Pointer(&edir[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, edir []byte) (n int, err error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
+ n = int(r0)
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Wstat(path string, edir []byte) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(edir) > 0 {
+ _p1 = unsafe.Pointer(&edir[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fwstat(fd int, edir []byte) (err error) {
+ var _p0 unsafe.Pointer
+ if len(edir) > 0 {
+ _p0 = unsafe.Pointer(&edir[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
+ if int32(r0) == -1 {
+ err = e1
+ }
+ return
+}
diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go
index 3022ce2e9..e5c48488b 100644
--- a/src/pkg/syscall/zsyscall_windows_386.go
+++ b/src/pkg/syscall/zsyscall_windows_386.go
@@ -49,6 +49,7 @@ var (
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
procCancelIo = modkernel32.NewProc("CancelIo")
+ procCancelIoEx = modkernel32.NewProc("CancelIoEx")
procCreateProcessW = modkernel32.NewProc("CreateProcessW")
procOpenProcess = modkernel32.NewProc("OpenProcess")
procTerminateProcess = modkernel32.NewProc("TerminateProcess")
@@ -103,11 +104,16 @@ var (
procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW")
procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW")
procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW")
+ procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
+ procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
+ procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
+ procReadConsoleW = modkernel32.NewProc("ReadConsoleW")
procWSAStartup = modws2_32.NewProc("WSAStartup")
procWSACleanup = modws2_32.NewProc("WSACleanup")
procWSAIoctl = modws2_32.NewProc("WSAIoctl")
procsocket = modws2_32.NewProc("socket")
procsetsockopt = modws2_32.NewProc("setsockopt")
+ procgetsockopt = modws2_32.NewProc("getsockopt")
procbind = modws2_32.NewProc("bind")
procconnect = modws2_32.NewProc("connect")
procgetsockname = modws2_32.NewProc("getsockname")
@@ -127,6 +133,8 @@ var (
procgetprotobyname = modws2_32.NewProc("getprotobyname")
procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W")
procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree")
+ procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW")
+ procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW")
procGetIfEntry = modiphlpapi.NewProc("GetIfEntry")
procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo")
procTranslateNameW = modsecur32.NewProc("TranslateNameW")
@@ -142,7 +150,6 @@ var (
procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
- procgetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
)
func GetLastError() (lasterr error) {
@@ -154,7 +161,12 @@ func GetLastError() (lasterr error) {
}
func LoadLibrary(libname string) (handle Handle, err error) {
- r0, _, e1 := Syscall(procLoadLibraryW.Addr(), 1, uintptr(unsafe.Pointer(StringToUTF16Ptr(libname))), 0, 0)
+ var _p0 *uint16
+ _p0, err = UTF16PtrFromString(libname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(procLoadLibraryW.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0)
handle = Handle(r0)
if handle == 0 {
if e1 != 0 {
@@ -168,7 +180,7 @@ func LoadLibrary(libname string) (handle Handle, err error) {
func FreeLibrary(handle Handle) (err error) {
r1, _, e1 := Syscall(procFreeLibrary.Addr(), 1, uintptr(handle), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -179,7 +191,12 @@ func FreeLibrary(handle Handle) (err error) {
}
func GetProcAddress(module Handle, procname string) (proc uintptr, err error) {
- r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(procname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(_p0)), 0)
proc = uintptr(r0)
if proc == 0 {
if e1 != 0 {
@@ -245,7 +262,7 @@ func ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (
_p0 = &buf[0]
}
r1, _, e1 := Syscall6(procReadFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -261,7 +278,7 @@ func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped)
_p0 = &buf[0]
}
r1, _, e1 := Syscall6(procWriteFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -286,7 +303,7 @@ func SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence
func CloseHandle(handle Handle) (err error) {
r1, _, e1 := Syscall(procCloseHandle.Addr(), 1, uintptr(handle), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -324,7 +341,7 @@ func findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err erro
func findNextFile1(handle Handle, data *win32finddata1) (err error) {
r1, _, e1 := Syscall(procFindNextFileW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -336,7 +353,7 @@ func findNextFile1(handle Handle, data *win32finddata1) (err error) {
func FindClose(handle Handle) (err error) {
r1, _, e1 := Syscall(procFindClose.Addr(), 1, uintptr(handle), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -348,7 +365,7 @@ func FindClose(handle Handle) (err error) {
func GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (err error) {
r1, _, e1 := Syscall(procGetFileInformationByHandle.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -373,7 +390,7 @@ func GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, err error) {
func SetCurrentDirectory(path *uint16) (err error) {
r1, _, e1 := Syscall(procSetCurrentDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -385,7 +402,7 @@ func SetCurrentDirectory(path *uint16) (err error) {
func CreateDirectory(path *uint16, sa *SecurityAttributes) (err error) {
r1, _, e1 := Syscall(procCreateDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(sa)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -397,7 +414,7 @@ func CreateDirectory(path *uint16, sa *SecurityAttributes) (err error) {
func RemoveDirectory(path *uint16) (err error) {
r1, _, e1 := Syscall(procRemoveDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -409,7 +426,7 @@ func RemoveDirectory(path *uint16) (err error) {
func DeleteFile(path *uint16) (err error) {
r1, _, e1 := Syscall(procDeleteFileW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -421,7 +438,7 @@ func DeleteFile(path *uint16) (err error) {
func MoveFile(from *uint16, to *uint16) (err error) {
r1, _, e1 := Syscall(procMoveFileW.Addr(), 2, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -433,7 +450,7 @@ func MoveFile(from *uint16, to *uint16) (err error) {
func GetComputerName(buf *uint16, n *uint32) (err error) {
r1, _, e1 := Syscall(procGetComputerNameW.Addr(), 2, uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -445,7 +462,7 @@ func GetComputerName(buf *uint16, n *uint32) (err error) {
func SetEndOfFile(handle Handle) (err error) {
r1, _, e1 := Syscall(procSetEndOfFile.Addr(), 1, uintptr(handle), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -488,7 +505,7 @@ func CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, thre
func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (err error) {
r1, _, e1 := Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -500,7 +517,7 @@ func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overla
func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlapped *Overlapped) (err error) {
r1, _, e1 := Syscall6(procPostQueuedCompletionStatus.Addr(), 4, uintptr(cphandle), uintptr(qty), uintptr(key), uintptr(unsafe.Pointer(overlapped)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -512,7 +529,19 @@ func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlap
func CancelIo(s Handle) (err error) {
r1, _, e1 := Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func CancelIoEx(s Handle, o *Overlapped) (err error) {
+ r1, _, e1 := Syscall(procCancelIoEx.Addr(), 2, uintptr(s), uintptr(unsafe.Pointer(o)), 0)
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -530,7 +559,7 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA
_p0 = 0
}
r1, _, e1 := Syscall12(procCreateProcessW.Addr(), 10, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -561,7 +590,7 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err
func TerminateProcess(handle Handle, exitcode uint32) (err error) {
r1, _, e1 := Syscall(procTerminateProcess.Addr(), 2, uintptr(handle), uintptr(exitcode), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -573,7 +602,7 @@ func TerminateProcess(handle Handle, exitcode uint32) (err error) {
func GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) {
r1, _, e1 := Syscall(procGetExitCodeProcess.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -585,7 +614,7 @@ func GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) {
func GetStartupInfo(startupInfo *StartupInfo) (err error) {
r1, _, e1 := Syscall(procGetStartupInfoW.Addr(), 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -610,7 +639,7 @@ func GetCurrentProcess() (pseudoHandle Handle, err error) {
func GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error) {
r1, _, e1 := Syscall6(procGetProcessTimes.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(creationTime)), uintptr(unsafe.Pointer(exitTime)), uintptr(unsafe.Pointer(kernelTime)), uintptr(unsafe.Pointer(userTime)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -628,7 +657,7 @@ func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetP
_p0 = 0
}
r1, _, e1 := Syscall9(procDuplicateHandle.Addr(), 7, uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -666,7 +695,7 @@ func GetTempPath(buflen uint32, buf *uint16) (n uint32, err error) {
func CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes, size uint32) (err error) {
r1, _, e1 := Syscall6(procCreatePipe.Addr(), 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -691,7 +720,7 @@ func GetFileType(filehandle Handle) (n uint32, err error) {
func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (err error) {
r1, _, e1 := Syscall6(procCryptAcquireContextW.Addr(), 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -703,7 +732,7 @@ func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16
func CryptReleaseContext(provhandle Handle, flags uint32) (err error) {
r1, _, e1 := Syscall(procCryptReleaseContext.Addr(), 2, uintptr(provhandle), uintptr(flags), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -715,7 +744,7 @@ func CryptReleaseContext(provhandle Handle, flags uint32) (err error) {
func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) {
r1, _, e1 := Syscall(procCryptGenRandom.Addr(), 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf)))
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -740,7 +769,7 @@ func GetEnvironmentStrings() (envs *uint16, err error) {
func FreeEnvironmentStrings(envs *uint16) (err error) {
r1, _, e1 := Syscall(procFreeEnvironmentStringsW.Addr(), 1, uintptr(unsafe.Pointer(envs)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -765,7 +794,7 @@ func GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32
func SetEnvironmentVariable(name *uint16, value *uint16) (err error) {
r1, _, e1 := Syscall(procSetEnvironmentVariableW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -777,7 +806,7 @@ func SetEnvironmentVariable(name *uint16, value *uint16) (err error) {
func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) {
r1, _, e1 := Syscall6(procSetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -802,7 +831,7 @@ func GetFileAttributes(name *uint16) (attrs uint32, err error) {
func SetFileAttributes(name *uint16, attrs uint32) (err error) {
r1, _, e1 := Syscall(procSetFileAttributesW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(attrs), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -814,7 +843,7 @@ func SetFileAttributes(name *uint16, attrs uint32) (err error) {
func GetFileAttributesEx(name *uint16, level uint32, info *byte) (err error) {
r1, _, e1 := Syscall(procGetFileAttributesExW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(level), uintptr(unsafe.Pointer(info)))
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -858,7 +887,7 @@ func LocalFree(hmem Handle) (handle Handle, err error) {
func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) {
r1, _, e1 := Syscall(procSetHandleInformation.Addr(), 3, uintptr(handle), uintptr(mask), uintptr(flags))
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -870,7 +899,7 @@ func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error)
func FlushFileBuffers(handle Handle) (err error) {
r1, _, e1 := Syscall(procFlushFileBuffers.Addr(), 1, uintptr(handle), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -947,7 +976,7 @@ func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow ui
func UnmapViewOfFile(addr uintptr) (err error) {
r1, _, e1 := Syscall(procUnmapViewOfFile.Addr(), 1, uintptr(addr), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -959,7 +988,7 @@ func UnmapViewOfFile(addr uintptr) (err error) {
func FlushViewOfFile(addr uintptr, length uintptr) (err error) {
r1, _, e1 := Syscall(procFlushViewOfFile.Addr(), 2, uintptr(addr), uintptr(length), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -971,7 +1000,7 @@ func FlushViewOfFile(addr uintptr, length uintptr) (err error) {
func VirtualLock(addr uintptr, length uintptr) (err error) {
r1, _, e1 := Syscall(procVirtualLock.Addr(), 2, uintptr(addr), uintptr(length), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -983,7 +1012,7 @@ func VirtualLock(addr uintptr, length uintptr) (err error) {
func VirtualUnlock(addr uintptr, length uintptr) (err error) {
r1, _, e1 := Syscall(procVirtualUnlock.Addr(), 2, uintptr(addr), uintptr(length), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -995,7 +1024,7 @@ func VirtualUnlock(addr uintptr, length uintptr) (err error) {
func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (err error) {
r1, _, e1 := Syscall9(procTransmitFile.Addr(), 7, uintptr(s), uintptr(handle), uintptr(bytesToWrite), uintptr(bytsPerSend), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transmitFileBuf)), uintptr(flags), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1013,7 +1042,7 @@ func ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree
_p0 = 0
}
r1, _, e1 := Syscall9(procReadDirectoryChangesW.Addr(), 8, uintptr(handle), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(_p0), uintptr(mask), uintptr(unsafe.Pointer(retlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1064,7 +1093,7 @@ func CertEnumCertificatesInStore(store Handle, prevContext *CertContext) (contex
func CertAddCertificateContextToStore(store Handle, certContext *CertContext, addDisposition uint32, storeContext **CertContext) (err error) {
r1, _, e1 := Syscall6(procCertAddCertificateContextToStore.Addr(), 4, uintptr(store), uintptr(unsafe.Pointer(certContext)), uintptr(addDisposition), uintptr(unsafe.Pointer(storeContext)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1076,7 +1105,7 @@ func CertAddCertificateContextToStore(store Handle, certContext *CertContext, ad
func CertCloseStore(store Handle, flags uint32) (err error) {
r1, _, e1 := Syscall(procCertCloseStore.Addr(), 2, uintptr(store), uintptr(flags), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1088,7 +1117,7 @@ func CertCloseStore(store Handle, flags uint32) (err error) {
func CertGetCertificateChain(engine Handle, leaf *CertContext, time *Filetime, additionalStore Handle, para *CertChainPara, flags uint32, reserved uintptr, chainCtx **CertChainContext) (err error) {
r1, _, e1 := Syscall9(procCertGetCertificateChain.Addr(), 8, uintptr(engine), uintptr(unsafe.Pointer(leaf)), uintptr(unsafe.Pointer(time)), uintptr(additionalStore), uintptr(unsafe.Pointer(para)), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(chainCtx)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1118,7 +1147,7 @@ func CertCreateCertificateContext(certEncodingType uint32, certEncoded *byte, en
func CertFreeCertificateContext(ctx *CertContext) (err error) {
r1, _, e1 := Syscall(procCertFreeCertificateContext.Addr(), 1, uintptr(unsafe.Pointer(ctx)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1130,7 +1159,7 @@ func CertFreeCertificateContext(ctx *CertContext) (err error) {
func CertVerifyCertificateChainPolicy(policyOID uintptr, chain *CertChainContext, para *CertChainPolicyPara, status *CertChainPolicyStatus) (err error) {
r1, _, e1 := Syscall6(procCertVerifyCertificateChainPolicy.Addr(), 4, uintptr(policyOID), uintptr(unsafe.Pointer(chain)), uintptr(unsafe.Pointer(para)), uintptr(unsafe.Pointer(status)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1180,6 +1209,48 @@ func RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32
return
}
+func getCurrentProcessId() (pid uint32) {
+ r0, _, _ := Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0)
+ pid = uint32(r0)
+ return
+}
+
+func GetConsoleMode(console Handle, mode *uint32) (err error) {
+ r1, _, e1 := Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) {
+ r1, _, e1 := Syscall6(procWriteConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(towrite), uintptr(unsafe.Pointer(written)), uintptr(unsafe.Pointer(reserved)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) {
+ r1, _, e1 := Syscall6(procReadConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read)), uintptr(unsafe.Pointer(inputControl)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
func WSAStartup(verreq uint32, data *WSAData) (sockerr error) {
r0, _, _ := Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0)
if r0 != 0 {
@@ -1190,7 +1261,7 @@ func WSAStartup(verreq uint32, data *WSAData) (sockerr error) {
func WSACleanup() (err error) {
r1, _, e1 := Syscall(procWSACleanup.Addr(), 0, 0, 0, 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1202,7 +1273,7 @@ func WSACleanup() (err error) {
func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) {
r1, _, e1 := Syscall9(procWSAIoctl.Addr(), 9, uintptr(s), uintptr(iocc), uintptr(unsafe.Pointer(inbuf)), uintptr(cbif), uintptr(unsafe.Pointer(outbuf)), uintptr(cbob), uintptr(unsafe.Pointer(cbbr)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1227,7 +1298,19 @@ func socket(af int32, typ int32, protocol int32) (handle Handle, err error) {
func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) {
r1, _, e1 := Syscall6(procsetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen), 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func Getsockopt(s Handle, level int32, optname int32, optval *byte, optlen *int32) (err error) {
+ r1, _, e1 := Syscall6(procgetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(unsafe.Pointer(optlen)), 0)
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1239,7 +1322,7 @@ func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32
func bind(s Handle, name uintptr, namelen int32) (err error) {
r1, _, e1 := Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1251,7 +1334,7 @@ func bind(s Handle, name uintptr, namelen int32) (err error) {
func connect(s Handle, name uintptr, namelen int32) (err error) {
r1, _, e1 := Syscall(procconnect.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1263,7 +1346,7 @@ func connect(s Handle, name uintptr, namelen int32) (err error) {
func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) {
r1, _, e1 := Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1275,7 +1358,7 @@ func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) {
func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) {
r1, _, e1 := Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1287,7 +1370,7 @@ func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) {
func listen(s Handle, backlog int32) (err error) {
r1, _, e1 := Syscall(proclisten.Addr(), 2, uintptr(s), uintptr(backlog), 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1299,7 +1382,7 @@ func listen(s Handle, backlog int32) (err error) {
func shutdown(s Handle, how int32) (err error) {
r1, _, e1 := Syscall(procshutdown.Addr(), 2, uintptr(s), uintptr(how), 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1311,7 +1394,7 @@ func shutdown(s Handle, how int32) (err error) {
func Closesocket(s Handle) (err error) {
r1, _, e1 := Syscall(procclosesocket.Addr(), 1, uintptr(s), 0, 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1323,7 +1406,7 @@ func Closesocket(s Handle) (err error) {
func AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (err error) {
r1, _, e1 := Syscall9(procAcceptEx.Addr(), 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1340,7 +1423,7 @@ func GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen
func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) {
r1, _, e1 := Syscall9(procWSARecv.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1352,7 +1435,7 @@ func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32
func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (err error) {
r1, _, e1 := Syscall9(procWSASend.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1364,7 +1447,7 @@ func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32,
func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (err error) {
r1, _, e1 := Syscall9(procWSARecvFrom.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1376,7 +1459,7 @@ func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *ui
func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (err error) {
r1, _, e1 := Syscall9(procWSASendTo.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(to)), uintptr(tolen), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1387,7 +1470,12 @@ func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32
}
func GetHostByName(name string) (h *Hostent, err error) {
- r0, _, e1 := Syscall(procgethostbyname.Addr(), 1, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(procgethostbyname.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0)
h = (*Hostent)(unsafe.Pointer(r0))
if h == nil {
if e1 != 0 {
@@ -1400,7 +1488,17 @@ func GetHostByName(name string) (h *Hostent, err error) {
}
func GetServByName(name string, proto string) (s *Servent, err error) {
- r0, _, e1 := Syscall(procgetservbyname.Addr(), 2, uintptr(unsafe.Pointer(StringBytePtr(name))), uintptr(unsafe.Pointer(StringBytePtr(proto))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(proto)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(procgetservbyname.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
s = (*Servent)(unsafe.Pointer(r0))
if s == nil {
if e1 != 0 {
@@ -1419,7 +1517,12 @@ func Ntohs(netshort uint16) (u uint16) {
}
func GetProtoByName(name string) (p *Protoent, err error) {
- r0, _, e1 := Syscall(procgetprotobyname.Addr(), 1, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(procgetprotobyname.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0)
p = (*Protoent)(unsafe.Pointer(r0))
if p == nil {
if e1 != 0 {
@@ -1432,7 +1535,12 @@ func GetProtoByName(name string) (p *Protoent, err error) {
}
func DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status error) {
- r0, _, _ := Syscall6(procDnsQuery_W.Addr(), 6, uintptr(unsafe.Pointer(StringToUTF16Ptr(name))), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr)))
+ var _p0 *uint16
+ _p0, status = UTF16PtrFromString(name)
+ if status != nil {
+ return
+ }
+ r0, _, _ := Syscall6(procDnsQuery_W.Addr(), 6, uintptr(unsafe.Pointer(_p0)), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr)))
if r0 != 0 {
status = Errno(r0)
}
@@ -1444,6 +1552,19 @@ func DnsRecordListFree(rl *DNSRecord, freetype uint32) {
return
}
+func GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) {
+ r0, _, _ := Syscall6(procGetAddrInfoW.Addr(), 4, uintptr(unsafe.Pointer(nodename)), uintptr(unsafe.Pointer(servicename)), uintptr(unsafe.Pointer(hints)), uintptr(unsafe.Pointer(result)), 0, 0)
+ if r0 != 0 {
+ sockerr = Errno(r0)
+ }
+ return
+}
+
+func FreeAddrInfoW(addrinfo *AddrinfoW) {
+ Syscall(procFreeAddrInfoW.Addr(), 1, uintptr(unsafe.Pointer(addrinfo)), 0, 0)
+ return
+}
+
func GetIfEntry(pIfRow *MibIfRow) (errcode error) {
r0, _, _ := Syscall(procGetIfEntry.Addr(), 1, uintptr(unsafe.Pointer(pIfRow)), 0, 0)
if r0 != 0 {
@@ -1462,7 +1583,7 @@ func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) {
func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) {
r1, _, e1 := Syscall6(procTranslateNameW.Addr(), 5, uintptr(unsafe.Pointer(accName)), uintptr(accNameFormat), uintptr(desiredNameFormat), uintptr(unsafe.Pointer(translatedName)), uintptr(unsafe.Pointer(nSize)), 0)
- if int(r1)&0xff == 0 {
+ if r1&0xff == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1474,7 +1595,7 @@ func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint
func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err error) {
r1, _, e1 := Syscall(procGetUserNameExW.Addr(), 3, uintptr(nameFormat), uintptr(unsafe.Pointer(nameBuffre)), uintptr(unsafe.Pointer(nSize)))
- if int(r1)&0xff == 0 {
+ if r1&0xff == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1502,7 +1623,7 @@ func NetApiBufferFree(buf *byte) (neterr error) {
func LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) {
r1, _, e1 := Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1514,7 +1635,7 @@ func LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint3
func LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) {
r1, _, e1 := Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1526,7 +1647,7 @@ func LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen
func ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) {
r1, _, e1 := Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(stringSid)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1538,7 +1659,7 @@ func ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) {
func ConvertStringSidToSid(stringSid *uint16, sid **SID) (err error) {
r1, _, e1 := Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(stringSid)), uintptr(unsafe.Pointer(sid)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1556,7 +1677,7 @@ func GetLengthSid(sid *SID) (len uint32) {
func CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) {
r1, _, e1 := Syscall(procCopySid.Addr(), 3, uintptr(destSidLen), uintptr(unsafe.Pointer(destSid)), uintptr(unsafe.Pointer(srcSid)))
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1568,7 +1689,7 @@ func CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) {
func OpenProcessToken(h Handle, access uint32, token *Token) (err error) {
r1, _, e1 := Syscall(procOpenProcessToken.Addr(), 3, uintptr(h), uintptr(access), uintptr(unsafe.Pointer(token)))
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1580,7 +1701,7 @@ func OpenProcessToken(h Handle, access uint32, token *Token) (err error) {
func GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32, returnedLen *uint32) (err error) {
r1, _, e1 := Syscall6(procGetTokenInformation.Addr(), 5, uintptr(t), uintptr(infoClass), uintptr(unsafe.Pointer(info)), uintptr(infoLen), uintptr(unsafe.Pointer(returnedLen)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1592,7 +1713,7 @@ func GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32,
func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) {
r1, _, e1 := Syscall(procGetUserProfileDirectoryW.Addr(), 3, uintptr(t), uintptr(unsafe.Pointer(dir)), uintptr(unsafe.Pointer(dirLen)))
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1601,9 +1722,3 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) {
}
return
}
-
-func getCurrentProcessId() (pid uint32) {
- r0, _, _ := Syscall(procgetCurrentProcessId.Addr(), 0, 0, 0, 0)
- pid = uint32(r0)
- return
-}
diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go
index ea0000c32..465b509ae 100644
--- a/src/pkg/syscall/zsyscall_windows_amd64.go
+++ b/src/pkg/syscall/zsyscall_windows_amd64.go
@@ -49,6 +49,7 @@ var (
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
procCancelIo = modkernel32.NewProc("CancelIo")
+ procCancelIoEx = modkernel32.NewProc("CancelIoEx")
procCreateProcessW = modkernel32.NewProc("CreateProcessW")
procOpenProcess = modkernel32.NewProc("OpenProcess")
procTerminateProcess = modkernel32.NewProc("TerminateProcess")
@@ -103,11 +104,16 @@ var (
procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW")
procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW")
procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW")
+ procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
+ procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
+ procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
+ procReadConsoleW = modkernel32.NewProc("ReadConsoleW")
procWSAStartup = modws2_32.NewProc("WSAStartup")
procWSACleanup = modws2_32.NewProc("WSACleanup")
procWSAIoctl = modws2_32.NewProc("WSAIoctl")
procsocket = modws2_32.NewProc("socket")
procsetsockopt = modws2_32.NewProc("setsockopt")
+ procgetsockopt = modws2_32.NewProc("getsockopt")
procbind = modws2_32.NewProc("bind")
procconnect = modws2_32.NewProc("connect")
procgetsockname = modws2_32.NewProc("getsockname")
@@ -127,6 +133,8 @@ var (
procgetprotobyname = modws2_32.NewProc("getprotobyname")
procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W")
procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree")
+ procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW")
+ procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW")
procGetIfEntry = modiphlpapi.NewProc("GetIfEntry")
procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo")
procTranslateNameW = modsecur32.NewProc("TranslateNameW")
@@ -142,7 +150,6 @@ var (
procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
- procgetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
)
func GetLastError() (lasterr error) {
@@ -154,7 +161,12 @@ func GetLastError() (lasterr error) {
}
func LoadLibrary(libname string) (handle Handle, err error) {
- r0, _, e1 := Syscall(procLoadLibraryW.Addr(), 1, uintptr(unsafe.Pointer(StringToUTF16Ptr(libname))), 0, 0)
+ var _p0 *uint16
+ _p0, err = UTF16PtrFromString(libname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(procLoadLibraryW.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0)
handle = Handle(r0)
if handle == 0 {
if e1 != 0 {
@@ -168,7 +180,7 @@ func LoadLibrary(libname string) (handle Handle, err error) {
func FreeLibrary(handle Handle) (err error) {
r1, _, e1 := Syscall(procFreeLibrary.Addr(), 1, uintptr(handle), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -179,7 +191,12 @@ func FreeLibrary(handle Handle) (err error) {
}
func GetProcAddress(module Handle, procname string) (proc uintptr, err error) {
- r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(procname)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(_p0)), 0)
proc = uintptr(r0)
if proc == 0 {
if e1 != 0 {
@@ -245,7 +262,7 @@ func ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (
_p0 = &buf[0]
}
r1, _, e1 := Syscall6(procReadFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -261,7 +278,7 @@ func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped)
_p0 = &buf[0]
}
r1, _, e1 := Syscall6(procWriteFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -286,7 +303,7 @@ func SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence
func CloseHandle(handle Handle) (err error) {
r1, _, e1 := Syscall(procCloseHandle.Addr(), 1, uintptr(handle), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -324,7 +341,7 @@ func findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err erro
func findNextFile1(handle Handle, data *win32finddata1) (err error) {
r1, _, e1 := Syscall(procFindNextFileW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -336,7 +353,7 @@ func findNextFile1(handle Handle, data *win32finddata1) (err error) {
func FindClose(handle Handle) (err error) {
r1, _, e1 := Syscall(procFindClose.Addr(), 1, uintptr(handle), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -348,7 +365,7 @@ func FindClose(handle Handle) (err error) {
func GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (err error) {
r1, _, e1 := Syscall(procGetFileInformationByHandle.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -373,7 +390,7 @@ func GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, err error) {
func SetCurrentDirectory(path *uint16) (err error) {
r1, _, e1 := Syscall(procSetCurrentDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -385,7 +402,7 @@ func SetCurrentDirectory(path *uint16) (err error) {
func CreateDirectory(path *uint16, sa *SecurityAttributes) (err error) {
r1, _, e1 := Syscall(procCreateDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(sa)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -397,7 +414,7 @@ func CreateDirectory(path *uint16, sa *SecurityAttributes) (err error) {
func RemoveDirectory(path *uint16) (err error) {
r1, _, e1 := Syscall(procRemoveDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -409,7 +426,7 @@ func RemoveDirectory(path *uint16) (err error) {
func DeleteFile(path *uint16) (err error) {
r1, _, e1 := Syscall(procDeleteFileW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -421,7 +438,7 @@ func DeleteFile(path *uint16) (err error) {
func MoveFile(from *uint16, to *uint16) (err error) {
r1, _, e1 := Syscall(procMoveFileW.Addr(), 2, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -433,7 +450,7 @@ func MoveFile(from *uint16, to *uint16) (err error) {
func GetComputerName(buf *uint16, n *uint32) (err error) {
r1, _, e1 := Syscall(procGetComputerNameW.Addr(), 2, uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -445,7 +462,7 @@ func GetComputerName(buf *uint16, n *uint32) (err error) {
func SetEndOfFile(handle Handle) (err error) {
r1, _, e1 := Syscall(procSetEndOfFile.Addr(), 1, uintptr(handle), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -488,7 +505,7 @@ func CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, thre
func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (err error) {
r1, _, e1 := Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -500,7 +517,7 @@ func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overla
func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlapped *Overlapped) (err error) {
r1, _, e1 := Syscall6(procPostQueuedCompletionStatus.Addr(), 4, uintptr(cphandle), uintptr(qty), uintptr(key), uintptr(unsafe.Pointer(overlapped)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -512,7 +529,19 @@ func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlap
func CancelIo(s Handle) (err error) {
r1, _, e1 := Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func CancelIoEx(s Handle, o *Overlapped) (err error) {
+ r1, _, e1 := Syscall(procCancelIoEx.Addr(), 2, uintptr(s), uintptr(unsafe.Pointer(o)), 0)
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -530,7 +559,7 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA
_p0 = 0
}
r1, _, e1 := Syscall12(procCreateProcessW.Addr(), 10, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -561,7 +590,7 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err
func TerminateProcess(handle Handle, exitcode uint32) (err error) {
r1, _, e1 := Syscall(procTerminateProcess.Addr(), 2, uintptr(handle), uintptr(exitcode), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -573,7 +602,7 @@ func TerminateProcess(handle Handle, exitcode uint32) (err error) {
func GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) {
r1, _, e1 := Syscall(procGetExitCodeProcess.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -585,7 +614,7 @@ func GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) {
func GetStartupInfo(startupInfo *StartupInfo) (err error) {
r1, _, e1 := Syscall(procGetStartupInfoW.Addr(), 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -610,7 +639,7 @@ func GetCurrentProcess() (pseudoHandle Handle, err error) {
func GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error) {
r1, _, e1 := Syscall6(procGetProcessTimes.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(creationTime)), uintptr(unsafe.Pointer(exitTime)), uintptr(unsafe.Pointer(kernelTime)), uintptr(unsafe.Pointer(userTime)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -628,7 +657,7 @@ func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetP
_p0 = 0
}
r1, _, e1 := Syscall9(procDuplicateHandle.Addr(), 7, uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -666,7 +695,7 @@ func GetTempPath(buflen uint32, buf *uint16) (n uint32, err error) {
func CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes, size uint32) (err error) {
r1, _, e1 := Syscall6(procCreatePipe.Addr(), 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -691,7 +720,7 @@ func GetFileType(filehandle Handle) (n uint32, err error) {
func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (err error) {
r1, _, e1 := Syscall6(procCryptAcquireContextW.Addr(), 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -703,7 +732,7 @@ func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16
func CryptReleaseContext(provhandle Handle, flags uint32) (err error) {
r1, _, e1 := Syscall(procCryptReleaseContext.Addr(), 2, uintptr(provhandle), uintptr(flags), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -715,7 +744,7 @@ func CryptReleaseContext(provhandle Handle, flags uint32) (err error) {
func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) {
r1, _, e1 := Syscall(procCryptGenRandom.Addr(), 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf)))
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -740,7 +769,7 @@ func GetEnvironmentStrings() (envs *uint16, err error) {
func FreeEnvironmentStrings(envs *uint16) (err error) {
r1, _, e1 := Syscall(procFreeEnvironmentStringsW.Addr(), 1, uintptr(unsafe.Pointer(envs)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -765,7 +794,7 @@ func GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32
func SetEnvironmentVariable(name *uint16, value *uint16) (err error) {
r1, _, e1 := Syscall(procSetEnvironmentVariableW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -777,7 +806,7 @@ func SetEnvironmentVariable(name *uint16, value *uint16) (err error) {
func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) {
r1, _, e1 := Syscall6(procSetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -802,7 +831,7 @@ func GetFileAttributes(name *uint16) (attrs uint32, err error) {
func SetFileAttributes(name *uint16, attrs uint32) (err error) {
r1, _, e1 := Syscall(procSetFileAttributesW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(attrs), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -814,7 +843,7 @@ func SetFileAttributes(name *uint16, attrs uint32) (err error) {
func GetFileAttributesEx(name *uint16, level uint32, info *byte) (err error) {
r1, _, e1 := Syscall(procGetFileAttributesExW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(level), uintptr(unsafe.Pointer(info)))
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -858,7 +887,7 @@ func LocalFree(hmem Handle) (handle Handle, err error) {
func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) {
r1, _, e1 := Syscall(procSetHandleInformation.Addr(), 3, uintptr(handle), uintptr(mask), uintptr(flags))
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -870,7 +899,7 @@ func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error)
func FlushFileBuffers(handle Handle) (err error) {
r1, _, e1 := Syscall(procFlushFileBuffers.Addr(), 1, uintptr(handle), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -947,7 +976,7 @@ func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow ui
func UnmapViewOfFile(addr uintptr) (err error) {
r1, _, e1 := Syscall(procUnmapViewOfFile.Addr(), 1, uintptr(addr), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -959,7 +988,7 @@ func UnmapViewOfFile(addr uintptr) (err error) {
func FlushViewOfFile(addr uintptr, length uintptr) (err error) {
r1, _, e1 := Syscall(procFlushViewOfFile.Addr(), 2, uintptr(addr), uintptr(length), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -971,7 +1000,7 @@ func FlushViewOfFile(addr uintptr, length uintptr) (err error) {
func VirtualLock(addr uintptr, length uintptr) (err error) {
r1, _, e1 := Syscall(procVirtualLock.Addr(), 2, uintptr(addr), uintptr(length), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -983,7 +1012,7 @@ func VirtualLock(addr uintptr, length uintptr) (err error) {
func VirtualUnlock(addr uintptr, length uintptr) (err error) {
r1, _, e1 := Syscall(procVirtualUnlock.Addr(), 2, uintptr(addr), uintptr(length), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -995,7 +1024,7 @@ func VirtualUnlock(addr uintptr, length uintptr) (err error) {
func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (err error) {
r1, _, e1 := Syscall9(procTransmitFile.Addr(), 7, uintptr(s), uintptr(handle), uintptr(bytesToWrite), uintptr(bytsPerSend), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transmitFileBuf)), uintptr(flags), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1013,7 +1042,7 @@ func ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree
_p0 = 0
}
r1, _, e1 := Syscall9(procReadDirectoryChangesW.Addr(), 8, uintptr(handle), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(_p0), uintptr(mask), uintptr(unsafe.Pointer(retlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1064,7 +1093,7 @@ func CertEnumCertificatesInStore(store Handle, prevContext *CertContext) (contex
func CertAddCertificateContextToStore(store Handle, certContext *CertContext, addDisposition uint32, storeContext **CertContext) (err error) {
r1, _, e1 := Syscall6(procCertAddCertificateContextToStore.Addr(), 4, uintptr(store), uintptr(unsafe.Pointer(certContext)), uintptr(addDisposition), uintptr(unsafe.Pointer(storeContext)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1076,7 +1105,7 @@ func CertAddCertificateContextToStore(store Handle, certContext *CertContext, ad
func CertCloseStore(store Handle, flags uint32) (err error) {
r1, _, e1 := Syscall(procCertCloseStore.Addr(), 2, uintptr(store), uintptr(flags), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1088,7 +1117,7 @@ func CertCloseStore(store Handle, flags uint32) (err error) {
func CertGetCertificateChain(engine Handle, leaf *CertContext, time *Filetime, additionalStore Handle, para *CertChainPara, flags uint32, reserved uintptr, chainCtx **CertChainContext) (err error) {
r1, _, e1 := Syscall9(procCertGetCertificateChain.Addr(), 8, uintptr(engine), uintptr(unsafe.Pointer(leaf)), uintptr(unsafe.Pointer(time)), uintptr(additionalStore), uintptr(unsafe.Pointer(para)), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(chainCtx)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1118,7 +1147,7 @@ func CertCreateCertificateContext(certEncodingType uint32, certEncoded *byte, en
func CertFreeCertificateContext(ctx *CertContext) (err error) {
r1, _, e1 := Syscall(procCertFreeCertificateContext.Addr(), 1, uintptr(unsafe.Pointer(ctx)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1130,7 +1159,7 @@ func CertFreeCertificateContext(ctx *CertContext) (err error) {
func CertVerifyCertificateChainPolicy(policyOID uintptr, chain *CertChainContext, para *CertChainPolicyPara, status *CertChainPolicyStatus) (err error) {
r1, _, e1 := Syscall6(procCertVerifyCertificateChainPolicy.Addr(), 4, uintptr(policyOID), uintptr(unsafe.Pointer(chain)), uintptr(unsafe.Pointer(para)), uintptr(unsafe.Pointer(status)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1180,6 +1209,48 @@ func RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32
return
}
+func getCurrentProcessId() (pid uint32) {
+ r0, _, _ := Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0)
+ pid = uint32(r0)
+ return
+}
+
+func GetConsoleMode(console Handle, mode *uint32) (err error) {
+ r1, _, e1 := Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) {
+ r1, _, e1 := Syscall6(procWriteConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(towrite), uintptr(unsafe.Pointer(written)), uintptr(unsafe.Pointer(reserved)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) {
+ r1, _, e1 := Syscall6(procReadConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read)), uintptr(unsafe.Pointer(inputControl)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
func WSAStartup(verreq uint32, data *WSAData) (sockerr error) {
r0, _, _ := Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0)
if r0 != 0 {
@@ -1190,7 +1261,7 @@ func WSAStartup(verreq uint32, data *WSAData) (sockerr error) {
func WSACleanup() (err error) {
r1, _, e1 := Syscall(procWSACleanup.Addr(), 0, 0, 0, 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1202,7 +1273,7 @@ func WSACleanup() (err error) {
func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) {
r1, _, e1 := Syscall9(procWSAIoctl.Addr(), 9, uintptr(s), uintptr(iocc), uintptr(unsafe.Pointer(inbuf)), uintptr(cbif), uintptr(unsafe.Pointer(outbuf)), uintptr(cbob), uintptr(unsafe.Pointer(cbbr)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1227,7 +1298,19 @@ func socket(af int32, typ int32, protocol int32) (handle Handle, err error) {
func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) {
r1, _, e1 := Syscall6(procsetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen), 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func Getsockopt(s Handle, level int32, optname int32, optval *byte, optlen *int32) (err error) {
+ r1, _, e1 := Syscall6(procgetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(unsafe.Pointer(optlen)), 0)
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1239,7 +1322,7 @@ func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32
func bind(s Handle, name uintptr, namelen int32) (err error) {
r1, _, e1 := Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1251,7 +1334,7 @@ func bind(s Handle, name uintptr, namelen int32) (err error) {
func connect(s Handle, name uintptr, namelen int32) (err error) {
r1, _, e1 := Syscall(procconnect.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1263,7 +1346,7 @@ func connect(s Handle, name uintptr, namelen int32) (err error) {
func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) {
r1, _, e1 := Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1275,7 +1358,7 @@ func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) {
func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) {
r1, _, e1 := Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1287,7 +1370,7 @@ func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) {
func listen(s Handle, backlog int32) (err error) {
r1, _, e1 := Syscall(proclisten.Addr(), 2, uintptr(s), uintptr(backlog), 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1299,7 +1382,7 @@ func listen(s Handle, backlog int32) (err error) {
func shutdown(s Handle, how int32) (err error) {
r1, _, e1 := Syscall(procshutdown.Addr(), 2, uintptr(s), uintptr(how), 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1311,7 +1394,7 @@ func shutdown(s Handle, how int32) (err error) {
func Closesocket(s Handle) (err error) {
r1, _, e1 := Syscall(procclosesocket.Addr(), 1, uintptr(s), 0, 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1323,7 +1406,7 @@ func Closesocket(s Handle) (err error) {
func AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (err error) {
r1, _, e1 := Syscall9(procAcceptEx.Addr(), 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1340,7 +1423,7 @@ func GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen
func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) {
r1, _, e1 := Syscall9(procWSARecv.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1352,7 +1435,7 @@ func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32
func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (err error) {
r1, _, e1 := Syscall9(procWSASend.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1364,7 +1447,7 @@ func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32,
func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (err error) {
r1, _, e1 := Syscall9(procWSARecvFrom.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1376,7 +1459,7 @@ func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *ui
func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (err error) {
r1, _, e1 := Syscall9(procWSASendTo.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(to)), uintptr(tolen), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
- if int(r1) == -1 {
+ if r1 == socket_error {
if e1 != 0 {
err = error(e1)
} else {
@@ -1387,7 +1470,12 @@ func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32
}
func GetHostByName(name string) (h *Hostent, err error) {
- r0, _, e1 := Syscall(procgethostbyname.Addr(), 1, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(procgethostbyname.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0)
h = (*Hostent)(unsafe.Pointer(r0))
if h == nil {
if e1 != 0 {
@@ -1400,7 +1488,17 @@ func GetHostByName(name string) (h *Hostent, err error) {
}
func GetServByName(name string, proto string) (s *Servent, err error) {
- r0, _, e1 := Syscall(procgetservbyname.Addr(), 2, uintptr(unsafe.Pointer(StringBytePtr(name))), uintptr(unsafe.Pointer(StringBytePtr(proto))), 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(proto)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(procgetservbyname.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
s = (*Servent)(unsafe.Pointer(r0))
if s == nil {
if e1 != 0 {
@@ -1419,7 +1517,12 @@ func Ntohs(netshort uint16) (u uint16) {
}
func GetProtoByName(name string) (p *Protoent, err error) {
- r0, _, e1 := Syscall(procgetprotobyname.Addr(), 1, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0)
+ var _p0 *byte
+ _p0, err = BytePtrFromString(name)
+ if err != nil {
+ return
+ }
+ r0, _, e1 := Syscall(procgetprotobyname.Addr(), 1, uintptr(unsafe.Pointer(_p0)), 0, 0)
p = (*Protoent)(unsafe.Pointer(r0))
if p == nil {
if e1 != 0 {
@@ -1432,7 +1535,12 @@ func GetProtoByName(name string) (p *Protoent, err error) {
}
func DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status error) {
- r0, _, _ := Syscall6(procDnsQuery_W.Addr(), 6, uintptr(unsafe.Pointer(StringToUTF16Ptr(name))), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr)))
+ var _p0 *uint16
+ _p0, status = UTF16PtrFromString(name)
+ if status != nil {
+ return
+ }
+ r0, _, _ := Syscall6(procDnsQuery_W.Addr(), 6, uintptr(unsafe.Pointer(_p0)), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr)))
if r0 != 0 {
status = Errno(r0)
}
@@ -1444,6 +1552,19 @@ func DnsRecordListFree(rl *DNSRecord, freetype uint32) {
return
}
+func GetAddrInfoW(nodename *uint16, servicename *uint16, hints *AddrinfoW, result **AddrinfoW) (sockerr error) {
+ r0, _, _ := Syscall6(procGetAddrInfoW.Addr(), 4, uintptr(unsafe.Pointer(nodename)), uintptr(unsafe.Pointer(servicename)), uintptr(unsafe.Pointer(hints)), uintptr(unsafe.Pointer(result)), 0, 0)
+ if r0 != 0 {
+ sockerr = Errno(r0)
+ }
+ return
+}
+
+func FreeAddrInfoW(addrinfo *AddrinfoW) {
+ Syscall(procFreeAddrInfoW.Addr(), 1, uintptr(unsafe.Pointer(addrinfo)), 0, 0)
+ return
+}
+
func GetIfEntry(pIfRow *MibIfRow) (errcode error) {
r0, _, _ := Syscall(procGetIfEntry.Addr(), 1, uintptr(unsafe.Pointer(pIfRow)), 0, 0)
if r0 != 0 {
@@ -1462,7 +1583,7 @@ func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode error) {
func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) {
r1, _, e1 := Syscall6(procTranslateNameW.Addr(), 5, uintptr(unsafe.Pointer(accName)), uintptr(accNameFormat), uintptr(desiredNameFormat), uintptr(unsafe.Pointer(translatedName)), uintptr(unsafe.Pointer(nSize)), 0)
- if int(r1)&0xff == 0 {
+ if r1&0xff == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1474,7 +1595,7 @@ func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint
func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err error) {
r1, _, e1 := Syscall(procGetUserNameExW.Addr(), 3, uintptr(nameFormat), uintptr(unsafe.Pointer(nameBuffre)), uintptr(unsafe.Pointer(nSize)))
- if int(r1)&0xff == 0 {
+ if r1&0xff == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1502,7 +1623,7 @@ func NetApiBufferFree(buf *byte) (neterr error) {
func LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) {
r1, _, e1 := Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1514,7 +1635,7 @@ func LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint3
func LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) {
r1, _, e1 := Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1526,7 +1647,7 @@ func LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen
func ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) {
r1, _, e1 := Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(stringSid)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1538,7 +1659,7 @@ func ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) {
func ConvertStringSidToSid(stringSid *uint16, sid **SID) (err error) {
r1, _, e1 := Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(stringSid)), uintptr(unsafe.Pointer(sid)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1556,7 +1677,7 @@ func GetLengthSid(sid *SID) (len uint32) {
func CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) {
r1, _, e1 := Syscall(procCopySid.Addr(), 3, uintptr(destSidLen), uintptr(unsafe.Pointer(destSid)), uintptr(unsafe.Pointer(srcSid)))
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1568,7 +1689,7 @@ func CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) {
func OpenProcessToken(h Handle, access uint32, token *Token) (err error) {
r1, _, e1 := Syscall(procOpenProcessToken.Addr(), 3, uintptr(h), uintptr(access), uintptr(unsafe.Pointer(token)))
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1580,7 +1701,7 @@ func OpenProcessToken(h Handle, access uint32, token *Token) (err error) {
func GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32, returnedLen *uint32) (err error) {
r1, _, e1 := Syscall6(procGetTokenInformation.Addr(), 5, uintptr(t), uintptr(infoClass), uintptr(unsafe.Pointer(info)), uintptr(infoLen), uintptr(unsafe.Pointer(returnedLen)), 0)
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1592,7 +1713,7 @@ func GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32,
func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) {
r1, _, e1 := Syscall(procGetUserProfileDirectoryW.Addr(), 3, uintptr(t), uintptr(unsafe.Pointer(dir)), uintptr(unsafe.Pointer(dirLen)))
- if int(r1) == 0 {
+ if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
@@ -1601,9 +1722,3 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) {
}
return
}
-
-func getCurrentProcessId() (pid uint32) {
- r0, _, _ := Syscall(procgetCurrentProcessId.Addr(), 0, 0, 0, 0)
- pid = uint32(r0)
- return
-}
diff --git a/src/pkg/syscall/zsysctl_openbsd.go b/src/pkg/syscall/zsysctl_openbsd.go
index fb1342290..a5914f3f0 100644
--- a/src/pkg/syscall/zsysctl_openbsd.go
+++ b/src/pkg/syscall/zsysctl_openbsd.go
@@ -64,6 +64,7 @@ var sysctlMib = []mibentry{
{"kern.maxlocksperuid", []_C_int{1, 70}},
{"kern.maxpartitions", []_C_int{1, 23}},
{"kern.maxproc", []_C_int{1, 6}},
+ {"kern.maxthread", []_C_int{1, 25}},
{"kern.maxvnodes", []_C_int{1, 5}},
{"kern.mbstat", []_C_int{1, 59}},
{"kern.msgbuf", []_C_int{1, 48}},
@@ -75,6 +76,7 @@ var sysctlMib = []mibentry{
{"kern.nosuidcoredump", []_C_int{1, 32}},
{"kern.nprocs", []_C_int{1, 47}},
{"kern.nselcoll", []_C_int{1, 43}},
+ {"kern.nthreads", []_C_int{1, 26}},
{"kern.numvnodes", []_C_int{1, 58}},
{"kern.osrelease", []_C_int{1, 2}},
{"kern.osrevision", []_C_int{1, 3}},
diff --git a/src/pkg/syscall/zsysnum_freebsd_386.go b/src/pkg/syscall/zsysnum_freebsd_386.go
index e986abac5..5a2bfd119 100644
--- a/src/pkg/syscall/zsysnum_freebsd_386.go
+++ b/src/pkg/syscall/zsysnum_freebsd_386.go
@@ -1,4 +1,4 @@
-// mksysnum_freebsd.pl
+// mksysnum_freebsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
package syscall
diff --git a/src/pkg/syscall/zsysnum_freebsd_amd64.go b/src/pkg/syscall/zsysnum_freebsd_amd64.go
index e986abac5..5a2bfd119 100644
--- a/src/pkg/syscall/zsysnum_freebsd_amd64.go
+++ b/src/pkg/syscall/zsysnum_freebsd_amd64.go
@@ -1,4 +1,4 @@
-// mksysnum_freebsd.pl
+// mksysnum_freebsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
package syscall
diff --git a/src/pkg/syscall/zsysnum_freebsd_arm.go b/src/pkg/syscall/zsysnum_freebsd_arm.go
new file mode 100644
index 000000000..5a2bfd119
--- /dev/null
+++ b/src/pkg/syscall/zsysnum_freebsd_arm.go
@@ -0,0 +1,340 @@
+// mksysnum_freebsd.pl
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const (
+ // SYS_NOSYS = 0; // { int nosys(void); } syscall nosys_args int
+ SYS_EXIT = 1 // { void sys_exit(int rval); } exit \
+ SYS_FORK = 2 // { int fork(void); }
+ SYS_READ = 3 // { ssize_t read(int fd, void *buf, \
+ SYS_WRITE = 4 // { ssize_t write(int fd, const void *buf, \
+ SYS_OPEN = 5 // { int open(char *path, int flags, int mode); }
+ SYS_CLOSE = 6 // { int close(int fd); }
+ SYS_WAIT4 = 7 // { int wait4(int pid, int *status, \
+ SYS_LINK = 9 // { int link(char *path, char *link); }
+ SYS_UNLINK = 10 // { int unlink(char *path); }
+ SYS_CHDIR = 12 // { int chdir(char *path); }
+ SYS_FCHDIR = 13 // { int fchdir(int fd); }
+ SYS_MKNOD = 14 // { int mknod(char *path, int mode, int dev); }
+ SYS_CHMOD = 15 // { int chmod(char *path, int mode); }
+ SYS_CHOWN = 16 // { int chown(char *path, int uid, int gid); }
+ SYS_OBREAK = 17 // { int obreak(char *nsize); } break \
+ SYS_GETPID = 20 // { pid_t getpid(void); }
+ SYS_MOUNT = 21 // { int mount(char *type, char *path, \
+ SYS_UNMOUNT = 22 // { int unmount(char *path, int flags); }
+ SYS_SETUID = 23 // { int setuid(uid_t uid); }
+ SYS_GETUID = 24 // { uid_t getuid(void); }
+ SYS_GETEUID = 25 // { uid_t geteuid(void); }
+ SYS_PTRACE = 26 // { int ptrace(int req, pid_t pid, \
+ SYS_RECVMSG = 27 // { int recvmsg(int s, struct msghdr *msg, \
+ SYS_SENDMSG = 28 // { int sendmsg(int s, struct msghdr *msg, \
+ SYS_RECVFROM = 29 // { int recvfrom(int s, caddr_t buf, \
+ SYS_ACCEPT = 30 // { int accept(int s, \
+ SYS_GETPEERNAME = 31 // { int getpeername(int fdes, \
+ SYS_GETSOCKNAME = 32 // { int getsockname(int fdes, \
+ SYS_ACCESS = 33 // { int access(char *path, int amode); }
+ SYS_CHFLAGS = 34 // { int chflags(char *path, int flags); }
+ SYS_FCHFLAGS = 35 // { int fchflags(int fd, int flags); }
+ SYS_SYNC = 36 // { int sync(void); }
+ SYS_KILL = 37 // { int kill(int pid, int signum); }
+ SYS_GETPPID = 39 // { pid_t getppid(void); }
+ SYS_DUP = 41 // { int dup(u_int fd); }
+ SYS_PIPE = 42 // { int pipe(void); }
+ SYS_GETEGID = 43 // { gid_t getegid(void); }
+ SYS_PROFIL = 44 // { int profil(caddr_t samples, size_t size, \
+ SYS_KTRACE = 45 // { int ktrace(const char *fname, int ops, \
+ SYS_GETGID = 47 // { gid_t getgid(void); }
+ SYS_GETLOGIN = 49 // { int getlogin(char *namebuf, u_int \
+ SYS_SETLOGIN = 50 // { int setlogin(char *namebuf); }
+ SYS_ACCT = 51 // { int acct(char *path); }
+ SYS_SIGALTSTACK = 53 // { int sigaltstack(stack_t *ss, \
+ SYS_IOCTL = 54 // { int ioctl(int fd, u_long com, \
+ SYS_REBOOT = 55 // { int reboot(int opt); }
+ SYS_REVOKE = 56 // { int revoke(char *path); }
+ SYS_SYMLINK = 57 // { int symlink(char *path, char *link); }
+ SYS_READLINK = 58 // { ssize_t readlink(char *path, char *buf, \
+ SYS_EXECVE = 59 // { int execve(char *fname, char **argv, \
+ SYS_UMASK = 60 // { int umask(int newmask); } umask umask_args \
+ SYS_CHROOT = 61 // { int chroot(char *path); }
+ SYS_MSYNC = 65 // { int msync(void *addr, size_t len, \
+ SYS_VFORK = 66 // { int vfork(void); }
+ SYS_SBRK = 69 // { int sbrk(int incr); }
+ SYS_SSTK = 70 // { int sstk(int incr); }
+ SYS_OVADVISE = 72 // { int ovadvise(int anom); } vadvise \
+ SYS_MUNMAP = 73 // { int munmap(void *addr, size_t len); }
+ SYS_MPROTECT = 74 // { int mprotect(const void *addr, size_t len, \
+ SYS_MADVISE = 75 // { int madvise(void *addr, size_t len, \
+ SYS_MINCORE = 78 // { int mincore(const void *addr, size_t len, \
+ SYS_GETGROUPS = 79 // { int getgroups(u_int gidsetsize, \
+ SYS_SETGROUPS = 80 // { int setgroups(u_int gidsetsize, \
+ SYS_GETPGRP = 81 // { int getpgrp(void); }
+ SYS_SETPGID = 82 // { int setpgid(int pid, int pgid); }
+ SYS_SETITIMER = 83 // { int setitimer(u_int which, struct \
+ SYS_SWAPON = 85 // { int swapon(char *name); }
+ SYS_GETITIMER = 86 // { int getitimer(u_int which, \
+ SYS_GETDTABLESIZE = 89 // { int getdtablesize(void); }
+ SYS_DUP2 = 90 // { int dup2(u_int from, u_int to); }
+ SYS_FCNTL = 92 // { int fcntl(int fd, int cmd, long arg); }
+ SYS_SELECT = 93 // { int select(int nd, fd_set *in, fd_set *ou, \
+ SYS_FSYNC = 95 // { int fsync(int fd); }
+ SYS_SETPRIORITY = 96 // { int setpriority(int which, int who, \
+ SYS_SOCKET = 97 // { int socket(int domain, int type, \
+ SYS_CONNECT = 98 // { int connect(int s, caddr_t name, \
+ SYS_GETPRIORITY = 100 // { int getpriority(int which, int who); }
+ SYS_BIND = 104 // { int bind(int s, caddr_t name, \
+ SYS_SETSOCKOPT = 105 // { int setsockopt(int s, int level, int name, \
+ SYS_LISTEN = 106 // { int listen(int s, int backlog); }
+ SYS_GETTIMEOFDAY = 116 // { int gettimeofday(struct timeval *tp, \
+ SYS_GETRUSAGE = 117 // { int getrusage(int who, \
+ SYS_GETSOCKOPT = 118 // { int getsockopt(int s, int level, int name, \
+ SYS_READV = 120 // { int readv(int fd, struct iovec *iovp, \
+ SYS_WRITEV = 121 // { int writev(int fd, struct iovec *iovp, \
+ SYS_SETTIMEOFDAY = 122 // { int settimeofday(struct timeval *tv, \
+ SYS_FCHOWN = 123 // { int fchown(int fd, int uid, int gid); }
+ SYS_FCHMOD = 124 // { int fchmod(int fd, int mode); }
+ SYS_SETREUID = 126 // { int setreuid(int ruid, int euid); }
+ SYS_SETREGID = 127 // { int setregid(int rgid, int egid); }
+ SYS_RENAME = 128 // { int rename(char *from, char *to); }
+ SYS_FLOCK = 131 // { int flock(int fd, int how); }
+ SYS_MKFIFO = 132 // { int mkfifo(char *path, int mode); }
+ SYS_SENDTO = 133 // { int sendto(int s, caddr_t buf, size_t len, \
+ SYS_SHUTDOWN = 134 // { int shutdown(int s, int how); }
+ SYS_SOCKETPAIR = 135 // { int socketpair(int domain, int type, \
+ SYS_MKDIR = 136 // { int mkdir(char *path, int mode); }
+ SYS_RMDIR = 137 // { int rmdir(char *path); }
+ SYS_UTIMES = 138 // { int utimes(char *path, \
+ SYS_ADJTIME = 140 // { int adjtime(struct timeval *delta, \
+ SYS_SETSID = 147 // { int setsid(void); }
+ SYS_QUOTACTL = 148 // { int quotactl(char *path, int cmd, int uid, \
+ SYS_LGETFH = 160 // { int lgetfh(char *fname, \
+ SYS_GETFH = 161 // { int getfh(char *fname, \
+ SYS_SYSARCH = 165 // { int sysarch(int op, char *parms); }
+ SYS_RTPRIO = 166 // { int rtprio(int function, pid_t pid, \
+ SYS_FREEBSD6_PREAD = 173 // { ssize_t freebsd6_pread(int fd, void *buf, \
+ SYS_FREEBSD6_PWRITE = 174 // { ssize_t freebsd6_pwrite(int fd, \
+ SYS_SETFIB = 175 // { int setfib(int fibnum); }
+ SYS_NTP_ADJTIME = 176 // { int ntp_adjtime(struct timex *tp); }
+ SYS_SETGID = 181 // { int setgid(gid_t gid); }
+ SYS_SETEGID = 182 // { int setegid(gid_t egid); }
+ SYS_SETEUID = 183 // { int seteuid(uid_t euid); }
+ SYS_STAT = 188 // { int stat(char *path, struct stat *ub); }
+ SYS_FSTAT = 189 // { int fstat(int fd, struct stat *sb); }
+ SYS_LSTAT = 190 // { int lstat(char *path, struct stat *ub); }
+ SYS_PATHCONF = 191 // { int pathconf(char *path, int name); }
+ SYS_FPATHCONF = 192 // { int fpathconf(int fd, int name); }
+ SYS_GETRLIMIT = 194 // { int getrlimit(u_int which, \
+ SYS_SETRLIMIT = 195 // { int setrlimit(u_int which, \
+ SYS_GETDIRENTRIES = 196 // { int getdirentries(int fd, char *buf, \
+ SYS_FREEBSD6_MMAP = 197 // { caddr_t freebsd6_mmap(caddr_t addr, \
+ SYS_FREEBSD6_LSEEK = 199 // { off_t freebsd6_lseek(int fd, int pad, \
+ SYS_FREEBSD6_TRUNCATE = 200 // { int freebsd6_truncate(char *path, int pad, \
+ SYS_FREEBSD6_FTRUNCATE = 201 // { int freebsd6_ftruncate(int fd, int pad, \
+ SYS___SYSCTL = 202 // { int __sysctl(int *name, u_int namelen, \
+ SYS_MLOCK = 203 // { int mlock(const void *addr, size_t len); }
+ SYS_MUNLOCK = 204 // { int munlock(const void *addr, size_t len); }
+ SYS_UNDELETE = 205 // { int undelete(char *path); }
+ SYS_FUTIMES = 206 // { int futimes(int fd, struct timeval *tptr); }
+ SYS_GETPGID = 207 // { int getpgid(pid_t pid); }
+ SYS_POLL = 209 // { int poll(struct pollfd *fds, u_int nfds, \
+ SYS_CLOCK_GETTIME = 232 // { int clock_gettime(clockid_t clock_id, \
+ SYS_CLOCK_SETTIME = 233 // { int clock_settime( \
+ SYS_CLOCK_GETRES = 234 // { int clock_getres(clockid_t clock_id, \
+ SYS_KTIMER_CREATE = 235 // { int ktimer_create(clockid_t clock_id, \
+ SYS_KTIMER_DELETE = 236 // { int ktimer_delete(int timerid); }
+ SYS_KTIMER_SETTIME = 237 // { int ktimer_settime(int timerid, int flags, \
+ SYS_KTIMER_GETTIME = 238 // { int ktimer_gettime(int timerid, struct \
+ SYS_KTIMER_GETOVERRUN = 239 // { int ktimer_getoverrun(int timerid); }
+ SYS_NANOSLEEP = 240 // { int nanosleep(const struct timespec *rqtp, \
+ SYS_FFCLOCK_GETCOUNTER = 241 // { int ffclock_getcounter(ffcounter *ffcount); }
+ SYS_FFCLOCK_SETESTIMATE = 242 // { int ffclock_setestimate( \
+ SYS_FFCLOCK_GETESTIMATE = 243 // { int ffclock_getestimate( \
+ SYS_NTP_GETTIME = 248 // { int ntp_gettime(struct ntptimeval *ntvp); }
+ SYS_MINHERIT = 250 // { int minherit(void *addr, size_t len, \
+ SYS_RFORK = 251 // { int rfork(int flags); }
+ SYS_OPENBSD_POLL = 252 // { int openbsd_poll(struct pollfd *fds, \
+ SYS_ISSETUGID = 253 // { int issetugid(void); }
+ SYS_LCHOWN = 254 // { int lchown(char *path, int uid, int gid); }
+ SYS_GETDENTS = 272 // { int getdents(int fd, char *buf, \
+ SYS_LCHMOD = 274 // { int lchmod(char *path, mode_t mode); }
+ SYS_LUTIMES = 276 // { int lutimes(char *path, \
+ SYS_NSTAT = 278 // { int nstat(char *path, struct nstat *ub); }
+ SYS_NFSTAT = 279 // { int nfstat(int fd, struct nstat *sb); }
+ SYS_NLSTAT = 280 // { int nlstat(char *path, struct nstat *ub); }
+ SYS_PREADV = 289 // { ssize_t preadv(int fd, struct iovec *iovp, \
+ SYS_PWRITEV = 290 // { ssize_t pwritev(int fd, struct iovec *iovp, \
+ SYS_FHOPEN = 298 // { int fhopen(const struct fhandle *u_fhp, \
+ SYS_FHSTAT = 299 // { int fhstat(const struct fhandle *u_fhp, \
+ SYS_MODNEXT = 300 // { int modnext(int modid); }
+ SYS_MODSTAT = 301 // { int modstat(int modid, \
+ SYS_MODFNEXT = 302 // { int modfnext(int modid); }
+ SYS_MODFIND = 303 // { int modfind(const char *name); }
+ SYS_KLDLOAD = 304 // { int kldload(const char *file); }
+ SYS_KLDUNLOAD = 305 // { int kldunload(int fileid); }
+ SYS_KLDFIND = 306 // { int kldfind(const char *file); }
+ SYS_KLDNEXT = 307 // { int kldnext(int fileid); }
+ SYS_KLDSTAT = 308 // { int kldstat(int fileid, struct \
+ SYS_KLDFIRSTMOD = 309 // { int kldfirstmod(int fileid); }
+ SYS_GETSID = 310 // { int getsid(pid_t pid); }
+ SYS_SETRESUID = 311 // { int setresuid(uid_t ruid, uid_t euid, \
+ SYS_SETRESGID = 312 // { int setresgid(gid_t rgid, gid_t egid, \
+ SYS_YIELD = 321 // { int yield(void); }
+ SYS_MLOCKALL = 324 // { int mlockall(int how); }
+ SYS_MUNLOCKALL = 325 // { int munlockall(void); }
+ SYS___GETCWD = 326 // { int __getcwd(u_char *buf, u_int buflen); }
+ SYS_SCHED_SETPARAM = 327 // { int sched_setparam (pid_t pid, \
+ SYS_SCHED_GETPARAM = 328 // { int sched_getparam (pid_t pid, struct \
+ SYS_SCHED_SETSCHEDULER = 329 // { int sched_setscheduler (pid_t pid, int \
+ SYS_SCHED_GETSCHEDULER = 330 // { int sched_getscheduler (pid_t pid); }
+ SYS_SCHED_YIELD = 331 // { int sched_yield (void); }
+ SYS_SCHED_GET_PRIORITY_MAX = 332 // { int sched_get_priority_max (int policy); }
+ SYS_SCHED_GET_PRIORITY_MIN = 333 // { int sched_get_priority_min (int policy); }
+ SYS_SCHED_RR_GET_INTERVAL = 334 // { int sched_rr_get_interval (pid_t pid, \
+ SYS_UTRACE = 335 // { int utrace(const void *addr, size_t len); }
+ SYS_KLDSYM = 337 // { int kldsym(int fileid, int cmd, \
+ SYS_JAIL = 338 // { int jail(struct jail *jail); }
+ SYS_SIGPROCMASK = 340 // { int sigprocmask(int how, \
+ SYS_SIGSUSPEND = 341 // { int sigsuspend(const sigset_t *sigmask); }
+ SYS_SIGPENDING = 343 // { int sigpending(sigset_t *set); }
+ SYS_SIGTIMEDWAIT = 345 // { int sigtimedwait(const sigset_t *set, \
+ SYS_SIGWAITINFO = 346 // { int sigwaitinfo(const sigset_t *set, \
+ SYS___ACL_GET_FILE = 347 // { int __acl_get_file(const char *path, \
+ SYS___ACL_SET_FILE = 348 // { int __acl_set_file(const char *path, \
+ SYS___ACL_GET_FD = 349 // { int __acl_get_fd(int filedes, \
+ SYS___ACL_SET_FD = 350 // { int __acl_set_fd(int filedes, \
+ SYS___ACL_DELETE_FILE = 351 // { int __acl_delete_file(const char *path, \
+ SYS___ACL_DELETE_FD = 352 // { int __acl_delete_fd(int filedes, \
+ SYS___ACL_ACLCHECK_FILE = 353 // { int __acl_aclcheck_file(const char *path, \
+ SYS___ACL_ACLCHECK_FD = 354 // { int __acl_aclcheck_fd(int filedes, \
+ SYS_EXTATTRCTL = 355 // { int extattrctl(const char *path, int cmd, \
+ SYS_EXTATTR_SET_FILE = 356 // { int extattr_set_file( \
+ SYS_EXTATTR_GET_FILE = 357 // { ssize_t extattr_get_file( \
+ SYS_EXTATTR_DELETE_FILE = 358 // { int extattr_delete_file(const char *path, \
+ SYS_GETRESUID = 360 // { int getresuid(uid_t *ruid, uid_t *euid, \
+ SYS_GETRESGID = 361 // { int getresgid(gid_t *rgid, gid_t *egid, \
+ SYS_KQUEUE = 362 // { int kqueue(void); }
+ SYS_KEVENT = 363 // { int kevent(int fd, \
+ SYS_EXTATTR_SET_FD = 371 // { int extattr_set_fd(int fd, \
+ SYS_EXTATTR_GET_FD = 372 // { ssize_t extattr_get_fd(int fd, \
+ SYS_EXTATTR_DELETE_FD = 373 // { int extattr_delete_fd(int fd, \
+ SYS___SETUGID = 374 // { int __setugid(int flag); }
+ SYS_EACCESS = 376 // { int eaccess(char *path, int amode); }
+ SYS_NMOUNT = 378 // { int nmount(struct iovec *iovp, \
+ SYS___MAC_GET_PROC = 384 // { int __mac_get_proc(struct mac *mac_p); }
+ SYS___MAC_SET_PROC = 385 // { int __mac_set_proc(struct mac *mac_p); }
+ SYS___MAC_GET_FD = 386 // { int __mac_get_fd(int fd, \
+ SYS___MAC_GET_FILE = 387 // { int __mac_get_file(const char *path_p, \
+ SYS___MAC_SET_FD = 388 // { int __mac_set_fd(int fd, \
+ SYS___MAC_SET_FILE = 389 // { int __mac_set_file(const char *path_p, \
+ SYS_KENV = 390 // { int kenv(int what, const char *name, \
+ SYS_LCHFLAGS = 391 // { int lchflags(const char *path, int flags); }
+ SYS_UUIDGEN = 392 // { int uuidgen(struct uuid *store, \
+ SYS_SENDFILE = 393 // { int sendfile(int fd, int s, off_t offset, \
+ SYS_MAC_SYSCALL = 394 // { int mac_syscall(const char *policy, \
+ SYS_GETFSSTAT = 395 // { int getfsstat(struct statfs *buf, \
+ SYS_STATFS = 396 // { int statfs(char *path, \
+ SYS_FSTATFS = 397 // { int fstatfs(int fd, struct statfs *buf); }
+ SYS_FHSTATFS = 398 // { int fhstatfs(const struct fhandle *u_fhp, \
+ SYS___MAC_GET_PID = 409 // { int __mac_get_pid(pid_t pid, \
+ SYS___MAC_GET_LINK = 410 // { int __mac_get_link(const char *path_p, \
+ SYS___MAC_SET_LINK = 411 // { int __mac_set_link(const char *path_p, \
+ SYS_EXTATTR_SET_LINK = 412 // { int extattr_set_link( \
+ SYS_EXTATTR_GET_LINK = 413 // { ssize_t extattr_get_link( \
+ SYS_EXTATTR_DELETE_LINK = 414 // { int extattr_delete_link( \
+ SYS___MAC_EXECVE = 415 // { int __mac_execve(char *fname, char **argv, \
+ SYS_SIGACTION = 416 // { int sigaction(int sig, \
+ SYS_SIGRETURN = 417 // { int sigreturn( \
+ SYS_GETCONTEXT = 421 // { int getcontext(struct __ucontext *ucp); }
+ SYS_SETCONTEXT = 422 // { int setcontext( \
+ SYS_SWAPCONTEXT = 423 // { int swapcontext(struct __ucontext *oucp, \
+ SYS_SWAPOFF = 424 // { int swapoff(const char *name); }
+ SYS___ACL_GET_LINK = 425 // { int __acl_get_link(const char *path, \
+ SYS___ACL_SET_LINK = 426 // { int __acl_set_link(const char *path, \
+ SYS___ACL_DELETE_LINK = 427 // { int __acl_delete_link(const char *path, \
+ SYS___ACL_ACLCHECK_LINK = 428 // { int __acl_aclcheck_link(const char *path, \
+ SYS_SIGWAIT = 429 // { int sigwait(const sigset_t *set, \
+ SYS_THR_CREATE = 430 // { int thr_create(ucontext_t *ctx, long *id, \
+ SYS_THR_EXIT = 431 // { void thr_exit(long *state); }
+ SYS_THR_SELF = 432 // { int thr_self(long *id); }
+ SYS_THR_KILL = 433 // { int thr_kill(long id, int sig); }
+ SYS__UMTX_LOCK = 434 // { int _umtx_lock(struct umtx *umtx); }
+ SYS__UMTX_UNLOCK = 435 // { int _umtx_unlock(struct umtx *umtx); }
+ SYS_JAIL_ATTACH = 436 // { int jail_attach(int jid); }
+ SYS_EXTATTR_LIST_FD = 437 // { ssize_t extattr_list_fd(int fd, \
+ SYS_EXTATTR_LIST_FILE = 438 // { ssize_t extattr_list_file( \
+ SYS_EXTATTR_LIST_LINK = 439 // { ssize_t extattr_list_link( \
+ SYS_THR_SUSPEND = 442 // { int thr_suspend( \
+ SYS_THR_WAKE = 443 // { int thr_wake(long id); }
+ SYS_KLDUNLOADF = 444 // { int kldunloadf(int fileid, int flags); }
+ SYS_AUDIT = 445 // { int audit(const void *record, \
+ SYS_AUDITON = 446 // { int auditon(int cmd, void *data, \
+ SYS_GETAUID = 447 // { int getauid(uid_t *auid); }
+ SYS_SETAUID = 448 // { int setauid(uid_t *auid); }
+ SYS_GETAUDIT = 449 // { int getaudit(struct auditinfo *auditinfo); }
+ SYS_SETAUDIT = 450 // { int setaudit(struct auditinfo *auditinfo); }
+ SYS_GETAUDIT_ADDR = 451 // { int getaudit_addr( \
+ SYS_SETAUDIT_ADDR = 452 // { int setaudit_addr( \
+ SYS_AUDITCTL = 453 // { int auditctl(char *path); }
+ SYS__UMTX_OP = 454 // { int _umtx_op(void *obj, int op, \
+ SYS_THR_NEW = 455 // { int thr_new(struct thr_param *param, \
+ SYS_SIGQUEUE = 456 // { int sigqueue(pid_t pid, int signum, void *value); }
+ SYS_ABORT2 = 463 // { int abort2(const char *why, int nargs, void **args); }
+ SYS_THR_SET_NAME = 464 // { int thr_set_name(long id, const char *name); }
+ SYS_RTPRIO_THREAD = 466 // { int rtprio_thread(int function, \
+ SYS_SCTP_PEELOFF = 471 // { int sctp_peeloff(int sd, uint32_t name); }
+ SYS_SCTP_GENERIC_SENDMSG = 472 // { int sctp_generic_sendmsg(int sd, caddr_t msg, int mlen, \
+ SYS_SCTP_GENERIC_SENDMSG_IOV = 473 // { int sctp_generic_sendmsg_iov(int sd, struct iovec *iov, int iovlen, \
+ SYS_SCTP_GENERIC_RECVMSG = 474 // { int sctp_generic_recvmsg(int sd, struct iovec *iov, int iovlen, \
+ SYS_PREAD = 475 // { ssize_t pread(int fd, void *buf, \
+ SYS_PWRITE = 476 // { ssize_t pwrite(int fd, const void *buf, \
+ SYS_MMAP = 477 // { caddr_t mmap(caddr_t addr, size_t len, \
+ SYS_LSEEK = 478 // { off_t lseek(int fd, off_t offset, \
+ SYS_TRUNCATE = 479 // { int truncate(char *path, off_t length); }
+ SYS_FTRUNCATE = 480 // { int ftruncate(int fd, off_t length); }
+ SYS_THR_KILL2 = 481 // { int thr_kill2(pid_t pid, long id, int sig); }
+ SYS_SHM_OPEN = 482 // { int shm_open(const char *path, int flags, \
+ SYS_SHM_UNLINK = 483 // { int shm_unlink(const char *path); }
+ SYS_CPUSET = 484 // { int cpuset(cpusetid_t *setid); }
+ SYS_CPUSET_SETID = 485 // { int cpuset_setid(cpuwhich_t which, id_t id, \
+ SYS_CPUSET_GETID = 486 // { int cpuset_getid(cpulevel_t level, \
+ SYS_CPUSET_GETAFFINITY = 487 // { int cpuset_getaffinity(cpulevel_t level, \
+ SYS_CPUSET_SETAFFINITY = 488 // { int cpuset_setaffinity(cpulevel_t level, \
+ SYS_FACCESSAT = 489 // { int faccessat(int fd, char *path, int amode, \
+ SYS_FCHMODAT = 490 // { int fchmodat(int fd, char *path, mode_t mode, \
+ SYS_FCHOWNAT = 491 // { int fchownat(int fd, char *path, uid_t uid, \
+ SYS_FEXECVE = 492 // { int fexecve(int fd, char **argv, \
+ SYS_FSTATAT = 493 // { int fstatat(int fd, char *path, \
+ SYS_FUTIMESAT = 494 // { int futimesat(int fd, char *path, \
+ SYS_LINKAT = 495 // { int linkat(int fd1, char *path1, int fd2, \
+ SYS_MKDIRAT = 496 // { int mkdirat(int fd, char *path, mode_t mode); }
+ SYS_MKFIFOAT = 497 // { int mkfifoat(int fd, char *path, mode_t mode); }
+ SYS_MKNODAT = 498 // { int mknodat(int fd, char *path, mode_t mode, \
+ SYS_OPENAT = 499 // { int openat(int fd, char *path, int flag, \
+ SYS_READLINKAT = 500 // { int readlinkat(int fd, char *path, char *buf, \
+ SYS_RENAMEAT = 501 // { int renameat(int oldfd, char *old, int newfd, \
+ SYS_SYMLINKAT = 502 // { int symlinkat(char *path1, int fd, \
+ SYS_UNLINKAT = 503 // { int unlinkat(int fd, char *path, int flag); }
+ SYS_POSIX_OPENPT = 504 // { int posix_openpt(int flags); }
+ SYS_JAIL_GET = 506 // { int jail_get(struct iovec *iovp, \
+ SYS_JAIL_SET = 507 // { int jail_set(struct iovec *iovp, \
+ SYS_JAIL_REMOVE = 508 // { int jail_remove(int jid); }
+ SYS_CLOSEFROM = 509 // { int closefrom(int lowfd); }
+ SYS_LPATHCONF = 513 // { int lpathconf(char *path, int name); }
+ SYS_CAP_NEW = 514 // { int cap_new(int fd, u_int64_t rights); }
+ SYS_CAP_GETRIGHTS = 515 // { int cap_getrights(int fd, \
+ SYS_CAP_ENTER = 516 // { int cap_enter(void); }
+ SYS_CAP_GETMODE = 517 // { int cap_getmode(u_int *modep); }
+ SYS_PDFORK = 518 // { int pdfork(int *fdp, int flags); }
+ SYS_PDKILL = 519 // { int pdkill(int fd, int signum); }
+ SYS_PDGETPID = 520 // { int pdgetpid(int fd, pid_t *pidp); }
+ SYS_PSELECT = 522 // { int pselect(int nd, fd_set *in, \
+ SYS_GETLOGINCLASS = 523 // { int getloginclass(char *namebuf, \
+ SYS_SETLOGINCLASS = 524 // { int setloginclass(const char *namebuf); }
+ SYS_RCTL_GET_RACCT = 525 // { int rctl_get_racct(const void *inbufp, \
+ SYS_RCTL_GET_RULES = 526 // { int rctl_get_rules(const void *inbufp, \
+ SYS_RCTL_GET_LIMITS = 527 // { int rctl_get_limits(const void *inbufp, \
+ SYS_RCTL_ADD_RULE = 528 // { int rctl_add_rule(const void *inbufp, \
+ SYS_RCTL_REMOVE_RULE = 529 // { int rctl_remove_rule(const void *inbufp, \
+ SYS_POSIX_FALLOCATE = 530 // { int posix_fallocate(int fd, \
+ SYS_POSIX_FADVISE = 531 // { int posix_fadvise(int fd, off_t offset, \
+)
diff --git a/src/pkg/syscall/zsysnum_linux_arm.go b/src/pkg/syscall/zsysnum_linux_arm.go
index 7f5d9498c..7068e4e21 100644
--- a/src/pkg/syscall/zsysnum_linux_arm.go
+++ b/src/pkg/syscall/zsysnum_linux_arm.go
@@ -1,4 +1,4 @@
-// mksysnum_linux.pl
+// mksysnum_linux.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
package syscall
diff --git a/src/pkg/syscall/zsysnum_netbsd_386.go b/src/pkg/syscall/zsysnum_netbsd_386.go
index e9a0bcfb1..c57096523 100644
--- a/src/pkg/syscall/zsysnum_netbsd_386.go
+++ b/src/pkg/syscall/zsysnum_netbsd_386.go
@@ -1,4 +1,4 @@
-// mksysnum_netbsd.pl
+// mksysnum_netbsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
package syscall
@@ -163,12 +163,6 @@ const (
SYS__LWP_SETNAME = 323 // { int|sys||_lwp_setname(lwpid_t target, const char *name); }
SYS__LWP_GETNAME = 324 // { int|sys||_lwp_getname(lwpid_t target, char *name, size_t len); }
SYS__LWP_CTL = 325 // { int|sys||_lwp_ctl(int features, struct lwpctl **address); }
- SYS_SA_REGISTER = 330 // { int|sys||sa_register(sa_upcall_t new, sa_upcall_t *old, int flags, ssize_t stackinfo_offset); }
- SYS_SA_STACKS = 331 // { int|sys||sa_stacks(int num, stack_t *stacks); }
- SYS_SA_ENABLE = 332 // { int|sys||sa_enable(void); }
- SYS_SA_SETCONCURRENCY = 333 // { int|sys||sa_setconcurrency(int concurrency); }
- SYS_SA_YIELD = 334 // { int|sys||sa_yield(void); }
- SYS_SA_PREEMPT = 335 // { int|sys||sa_preempt(int sa_id); }
SYS___SIGACTION_SIGTRAMP = 340 // { int|sys||__sigaction_sigtramp(int signum, const struct sigaction *nsa, struct sigaction *osa, const void *tramp, int vers); }
SYS_PMC_GET_INFO = 341 // { int|sys||pmc_get_info(int ctr, int op, void *args); }
SYS_PMC_CONTROL = 342 // { int|sys||pmc_control(int ctr, int op, void *args); }
@@ -250,7 +244,6 @@ const (
SYS_WAIT4 = 449 // { int|sys|50|wait4(pid_t pid, int *status, int options, struct rusage *rusage); }
SYS_MKNOD = 450 // { int|sys|50|mknod(const char *path, mode_t mode, dev_t dev); }
SYS_FHSTAT = 451 // { int|sys|50|fhstat(const void *fhp, size_t fh_size, struct stat *sb); }
- SYS_QUOTACTL = 452 // { int|sys|50|quotactl(const char *path, struct plistref *pref); }
SYS_PIPE2 = 453 // { int|sys||pipe2(int *fildes, int flags); }
SYS_DUP3 = 454 // { int|sys||dup3(int from, int to, int flags); }
SYS_KQUEUE1 = 455 // { int|sys||kqueue1(int flags); }
@@ -271,4 +264,8 @@ const (
SYS_SYMLINKAT = 470 // { int|sys||symlinkat(const char *path1, int fd, const char *path2); }
SYS_UNLINKAT = 471 // { int|sys||unlinkat(int fd, const char *path, int flag); }
SYS_FUTIMENS = 472 // { int|sys||futimens(int fd, const struct timespec *tptr); }
+ SYS___QUOTACTL = 473 // { int|sys||__quotactl(const char *path, struct quotactl_args *args); }
+ SYS_POSIX_SPAWN = 474 // { int|sys||posix_spawn(pid_t *pid, const char *path, const struct posix_spawn_file_actions *file_actions, const struct posix_spawnattr *attrp, char *const *argv, char *const *envp); }
+ SYS_RECVMMSG = 475 // { int|sys||recvmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags, struct timespec *timeout); }
+ SYS_SENDMMSG = 476 // { int|sys||sendmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags); }
)
diff --git a/src/pkg/syscall/zsysnum_netbsd_amd64.go b/src/pkg/syscall/zsysnum_netbsd_amd64.go
index e9a0bcfb1..c57096523 100644
--- a/src/pkg/syscall/zsysnum_netbsd_amd64.go
+++ b/src/pkg/syscall/zsysnum_netbsd_amd64.go
@@ -1,4 +1,4 @@
-// mksysnum_netbsd.pl
+// mksysnum_netbsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
package syscall
@@ -163,12 +163,6 @@ const (
SYS__LWP_SETNAME = 323 // { int|sys||_lwp_setname(lwpid_t target, const char *name); }
SYS__LWP_GETNAME = 324 // { int|sys||_lwp_getname(lwpid_t target, char *name, size_t len); }
SYS__LWP_CTL = 325 // { int|sys||_lwp_ctl(int features, struct lwpctl **address); }
- SYS_SA_REGISTER = 330 // { int|sys||sa_register(sa_upcall_t new, sa_upcall_t *old, int flags, ssize_t stackinfo_offset); }
- SYS_SA_STACKS = 331 // { int|sys||sa_stacks(int num, stack_t *stacks); }
- SYS_SA_ENABLE = 332 // { int|sys||sa_enable(void); }
- SYS_SA_SETCONCURRENCY = 333 // { int|sys||sa_setconcurrency(int concurrency); }
- SYS_SA_YIELD = 334 // { int|sys||sa_yield(void); }
- SYS_SA_PREEMPT = 335 // { int|sys||sa_preempt(int sa_id); }
SYS___SIGACTION_SIGTRAMP = 340 // { int|sys||__sigaction_sigtramp(int signum, const struct sigaction *nsa, struct sigaction *osa, const void *tramp, int vers); }
SYS_PMC_GET_INFO = 341 // { int|sys||pmc_get_info(int ctr, int op, void *args); }
SYS_PMC_CONTROL = 342 // { int|sys||pmc_control(int ctr, int op, void *args); }
@@ -250,7 +244,6 @@ const (
SYS_WAIT4 = 449 // { int|sys|50|wait4(pid_t pid, int *status, int options, struct rusage *rusage); }
SYS_MKNOD = 450 // { int|sys|50|mknod(const char *path, mode_t mode, dev_t dev); }
SYS_FHSTAT = 451 // { int|sys|50|fhstat(const void *fhp, size_t fh_size, struct stat *sb); }
- SYS_QUOTACTL = 452 // { int|sys|50|quotactl(const char *path, struct plistref *pref); }
SYS_PIPE2 = 453 // { int|sys||pipe2(int *fildes, int flags); }
SYS_DUP3 = 454 // { int|sys||dup3(int from, int to, int flags); }
SYS_KQUEUE1 = 455 // { int|sys||kqueue1(int flags); }
@@ -271,4 +264,8 @@ const (
SYS_SYMLINKAT = 470 // { int|sys||symlinkat(const char *path1, int fd, const char *path2); }
SYS_UNLINKAT = 471 // { int|sys||unlinkat(int fd, const char *path, int flag); }
SYS_FUTIMENS = 472 // { int|sys||futimens(int fd, const struct timespec *tptr); }
+ SYS___QUOTACTL = 473 // { int|sys||__quotactl(const char *path, struct quotactl_args *args); }
+ SYS_POSIX_SPAWN = 474 // { int|sys||posix_spawn(pid_t *pid, const char *path, const struct posix_spawn_file_actions *file_actions, const struct posix_spawnattr *attrp, char *const *argv, char *const *envp); }
+ SYS_RECVMMSG = 475 // { int|sys||recvmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags, struct timespec *timeout); }
+ SYS_SENDMMSG = 476 // { int|sys||sendmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags); }
)
diff --git a/src/pkg/syscall/zsysnum_netbsd_arm.go b/src/pkg/syscall/zsysnum_netbsd_arm.go
new file mode 100644
index 000000000..c57096523
--- /dev/null
+++ b/src/pkg/syscall/zsysnum_netbsd_arm.go
@@ -0,0 +1,271 @@
+// mksysnum_netbsd.pl
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const (
+ SYS_EXIT = 1 // { void|sys||exit(int rval); }
+ SYS_FORK = 2 // { int|sys||fork(void); }
+ SYS_READ = 3 // { ssize_t|sys||read(int fd, void *buf, size_t nbyte); }
+ SYS_WRITE = 4 // { ssize_t|sys||write(int fd, const void *buf, size_t nbyte); }
+ SYS_OPEN = 5 // { int|sys||open(const char *path, int flags, ... mode_t mode); }
+ SYS_CLOSE = 6 // { int|sys||close(int fd); }
+ SYS_LINK = 9 // { int|sys||link(const char *path, const char *link); }
+ SYS_UNLINK = 10 // { int|sys||unlink(const char *path); }
+ SYS_CHDIR = 12 // { int|sys||chdir(const char *path); }
+ SYS_FCHDIR = 13 // { int|sys||fchdir(int fd); }
+ SYS_CHMOD = 15 // { int|sys||chmod(const char *path, mode_t mode); }
+ SYS_CHOWN = 16 // { int|sys||chown(const char *path, uid_t uid, gid_t gid); }
+ SYS_BREAK = 17 // { int|sys||obreak(char *nsize); }
+ SYS_GETPID = 20 // { pid_t|sys||getpid_with_ppid(void); }
+ SYS_UNMOUNT = 22 // { int|sys||unmount(const char *path, int flags); }
+ SYS_SETUID = 23 // { int|sys||setuid(uid_t uid); }
+ SYS_GETUID = 24 // { uid_t|sys||getuid_with_euid(void); }
+ SYS_GETEUID = 25 // { uid_t|sys||geteuid(void); }
+ SYS_PTRACE = 26 // { int|sys||ptrace(int req, pid_t pid, void *addr, int data); }
+ SYS_RECVMSG = 27 // { ssize_t|sys||recvmsg(int s, struct msghdr *msg, int flags); }
+ SYS_SENDMSG = 28 // { ssize_t|sys||sendmsg(int s, const struct msghdr *msg, int flags); }
+ SYS_RECVFROM = 29 // { ssize_t|sys||recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlenaddr); }
+ SYS_ACCEPT = 30 // { int|sys||accept(int s, struct sockaddr *name, socklen_t *anamelen); }
+ SYS_GETPEERNAME = 31 // { int|sys||getpeername(int fdes, struct sockaddr *asa, socklen_t *alen); }
+ SYS_GETSOCKNAME = 32 // { int|sys||getsockname(int fdes, struct sockaddr *asa, socklen_t *alen); }
+ SYS_ACCESS = 33 // { int|sys||access(const char *path, int flags); }
+ SYS_CHFLAGS = 34 // { int|sys||chflags(const char *path, u_long flags); }
+ SYS_FCHFLAGS = 35 // { int|sys||fchflags(int fd, u_long flags); }
+ SYS_SYNC = 36 // { void|sys||sync(void); }
+ SYS_KILL = 37 // { int|sys||kill(pid_t pid, int signum); }
+ SYS_GETPPID = 39 // { pid_t|sys||getppid(void); }
+ SYS_DUP = 41 // { int|sys||dup(int fd); }
+ SYS_PIPE = 42 // { int|sys||pipe(void); }
+ SYS_GETEGID = 43 // { gid_t|sys||getegid(void); }
+ SYS_PROFIL = 44 // { int|sys||profil(char *samples, size_t size, u_long offset, u_int scale); }
+ SYS_KTRACE = 45 // { int|sys||ktrace(const char *fname, int ops, int facs, pid_t pid); }
+ SYS_GETGID = 47 // { gid_t|sys||getgid_with_egid(void); }
+ SYS___GETLOGIN = 49 // { int|sys||__getlogin(char *namebuf, size_t namelen); }
+ SYS___SETLOGIN = 50 // { int|sys||__setlogin(const char *namebuf); }
+ SYS_ACCT = 51 // { int|sys||acct(const char *path); }
+ SYS_IOCTL = 54 // { int|sys||ioctl(int fd, u_long com, ... void *data); }
+ SYS_REVOKE = 56 // { int|sys||revoke(const char *path); }
+ SYS_SYMLINK = 57 // { int|sys||symlink(const char *path, const char *link); }
+ SYS_READLINK = 58 // { ssize_t|sys||readlink(const char *path, char *buf, size_t count); }
+ SYS_EXECVE = 59 // { int|sys||execve(const char *path, char * const *argp, char * const *envp); }
+ SYS_UMASK = 60 // { mode_t|sys||umask(mode_t newmask); }
+ SYS_CHROOT = 61 // { int|sys||chroot(const char *path); }
+ SYS_VFORK = 66 // { int|sys||vfork(void); }
+ SYS_SBRK = 69 // { int|sys||sbrk(intptr_t incr); }
+ SYS_SSTK = 70 // { int|sys||sstk(int incr); }
+ SYS_VADVISE = 72 // { int|sys||ovadvise(int anom); }
+ SYS_MUNMAP = 73 // { int|sys||munmap(void *addr, size_t len); }
+ SYS_MPROTECT = 74 // { int|sys||mprotect(void *addr, size_t len, int prot); }
+ SYS_MADVISE = 75 // { int|sys||madvise(void *addr, size_t len, int behav); }
+ SYS_MINCORE = 78 // { int|sys||mincore(void *addr, size_t len, char *vec); }
+ SYS_GETGROUPS = 79 // { int|sys||getgroups(int gidsetsize, gid_t *gidset); }
+ SYS_SETGROUPS = 80 // { int|sys||setgroups(int gidsetsize, const gid_t *gidset); }
+ SYS_GETPGRP = 81 // { int|sys||getpgrp(void); }
+ SYS_SETPGID = 82 // { int|sys||setpgid(pid_t pid, pid_t pgid); }
+ SYS_DUP2 = 90 // { int|sys||dup2(int from, int to); }
+ SYS_FCNTL = 92 // { int|sys||fcntl(int fd, int cmd, ... void *arg); }
+ SYS_FSYNC = 95 // { int|sys||fsync(int fd); }
+ SYS_SETPRIORITY = 96 // { int|sys||setpriority(int which, id_t who, int prio); }
+ SYS_CONNECT = 98 // { int|sys||connect(int s, const struct sockaddr *name, socklen_t namelen); }
+ SYS_GETPRIORITY = 100 // { int|sys||getpriority(int which, id_t who); }
+ SYS_BIND = 104 // { int|sys||bind(int s, const struct sockaddr *name, socklen_t namelen); }
+ SYS_SETSOCKOPT = 105 // { int|sys||setsockopt(int s, int level, int name, const void *val, socklen_t valsize); }
+ SYS_LISTEN = 106 // { int|sys||listen(int s, int backlog); }
+ SYS_GETSOCKOPT = 118 // { int|sys||getsockopt(int s, int level, int name, void *val, socklen_t *avalsize); }
+ SYS_READV = 120 // { ssize_t|sys||readv(int fd, const struct iovec *iovp, int iovcnt); }
+ SYS_WRITEV = 121 // { ssize_t|sys||writev(int fd, const struct iovec *iovp, int iovcnt); }
+ SYS_FCHOWN = 123 // { int|sys||fchown(int fd, uid_t uid, gid_t gid); }
+ SYS_FCHMOD = 124 // { int|sys||fchmod(int fd, mode_t mode); }
+ SYS_SETREUID = 126 // { int|sys||setreuid(uid_t ruid, uid_t euid); }
+ SYS_SETREGID = 127 // { int|sys||setregid(gid_t rgid, gid_t egid); }
+ SYS_RENAME = 128 // { int|sys||rename(const char *from, const char *to); }
+ SYS_FLOCK = 131 // { int|sys||flock(int fd, int how); }
+ SYS_MKFIFO = 132 // { int|sys||mkfifo(const char *path, mode_t mode); }
+ SYS_SENDTO = 133 // { ssize_t|sys||sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen); }
+ SYS_SHUTDOWN = 134 // { int|sys||shutdown(int s, int how); }
+ SYS_SOCKETPAIR = 135 // { int|sys||socketpair(int domain, int type, int protocol, int *rsv); }
+ SYS_MKDIR = 136 // { int|sys||mkdir(const char *path, mode_t mode); }
+ SYS_RMDIR = 137 // { int|sys||rmdir(const char *path); }
+ SYS_SETSID = 147 // { int|sys||setsid(void); }
+ SYS_SYSARCH = 165 // { int|sys||sysarch(int op, void *parms); }
+ SYS_PREAD = 173 // { ssize_t|sys||pread(int fd, void *buf, size_t nbyte, int PAD, off_t offset); }
+ SYS_PWRITE = 174 // { ssize_t|sys||pwrite(int fd, const void *buf, size_t nbyte, int PAD, off_t offset); }
+ SYS_NTP_ADJTIME = 176 // { int|sys||ntp_adjtime(struct timex *tp); }
+ SYS_SETGID = 181 // { int|sys||setgid(gid_t gid); }
+ SYS_SETEGID = 182 // { int|sys||setegid(gid_t egid); }
+ SYS_SETEUID = 183 // { int|sys||seteuid(uid_t euid); }
+ SYS_PATHCONF = 191 // { long|sys||pathconf(const char *path, int name); }
+ SYS_FPATHCONF = 192 // { long|sys||fpathconf(int fd, int name); }
+ SYS_GETRLIMIT = 194 // { int|sys||getrlimit(int which, struct rlimit *rlp); }
+ SYS_SETRLIMIT = 195 // { int|sys||setrlimit(int which, const struct rlimit *rlp); }
+ SYS_MMAP = 197 // { void *|sys||mmap(void *addr, size_t len, int prot, int flags, int fd, long PAD, off_t pos); }
+ SYS_LSEEK = 199 // { off_t|sys||lseek(int fd, int PAD, off_t offset, int whence); }
+ SYS_TRUNCATE = 200 // { int|sys||truncate(const char *path, int PAD, off_t length); }
+ SYS_FTRUNCATE = 201 // { int|sys||ftruncate(int fd, int PAD, off_t length); }
+ SYS___SYSCTL = 202 // { int|sys||__sysctl(const int *name, u_int namelen, void *old, size_t *oldlenp, const void *new, size_t newlen); }
+ SYS_MLOCK = 203 // { int|sys||mlock(const void *addr, size_t len); }
+ SYS_MUNLOCK = 204 // { int|sys||munlock(const void *addr, size_t len); }
+ SYS_UNDELETE = 205 // { int|sys||undelete(const char *path); }
+ SYS_GETPGID = 207 // { pid_t|sys||getpgid(pid_t pid); }
+ SYS_REBOOT = 208 // { int|sys||reboot(int opt, char *bootstr); }
+ SYS_POLL = 209 // { int|sys||poll(struct pollfd *fds, u_int nfds, int timeout); }
+ SYS_SEMGET = 221 // { int|sys||semget(key_t key, int nsems, int semflg); }
+ SYS_SEMOP = 222 // { int|sys||semop(int semid, struct sembuf *sops, size_t nsops); }
+ SYS_SEMCONFIG = 223 // { int|sys||semconfig(int flag); }
+ SYS_MSGGET = 225 // { int|sys||msgget(key_t key, int msgflg); }
+ SYS_MSGSND = 226 // { int|sys||msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); }
+ SYS_MSGRCV = 227 // { ssize_t|sys||msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); }
+ SYS_SHMAT = 228 // { void *|sys||shmat(int shmid, const void *shmaddr, int shmflg); }
+ SYS_SHMDT = 230 // { int|sys||shmdt(const void *shmaddr); }
+ SYS_SHMGET = 231 // { int|sys||shmget(key_t key, size_t size, int shmflg); }
+ SYS_TIMER_CREATE = 235 // { int|sys||timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid); }
+ SYS_TIMER_DELETE = 236 // { int|sys||timer_delete(timer_t timerid); }
+ SYS_TIMER_GETOVERRUN = 239 // { int|sys||timer_getoverrun(timer_t timerid); }
+ SYS_FDATASYNC = 241 // { int|sys||fdatasync(int fd); }
+ SYS_MLOCKALL = 242 // { int|sys||mlockall(int flags); }
+ SYS_MUNLOCKALL = 243 // { int|sys||munlockall(void); }
+ SYS_SIGQUEUEINFO = 245 // { int|sys||sigqueueinfo(pid_t pid, const siginfo_t *info); }
+ SYS_MODCTL = 246 // { int|sys||modctl(int cmd, void *arg); }
+ SYS___POSIX_RENAME = 270 // { int|sys||__posix_rename(const char *from, const char *to); }
+ SYS_SWAPCTL = 271 // { int|sys||swapctl(int cmd, void *arg, int misc); }
+ SYS_MINHERIT = 273 // { int|sys||minherit(void *addr, size_t len, int inherit); }
+ SYS_LCHMOD = 274 // { int|sys||lchmod(const char *path, mode_t mode); }
+ SYS_LCHOWN = 275 // { int|sys||lchown(const char *path, uid_t uid, gid_t gid); }
+ SYS___POSIX_CHOWN = 283 // { int|sys||__posix_chown(const char *path, uid_t uid, gid_t gid); }
+ SYS___POSIX_FCHOWN = 284 // { int|sys||__posix_fchown(int fd, uid_t uid, gid_t gid); }
+ SYS___POSIX_LCHOWN = 285 // { int|sys||__posix_lchown(const char *path, uid_t uid, gid_t gid); }
+ SYS_GETSID = 286 // { pid_t|sys||getsid(pid_t pid); }
+ SYS___CLONE = 287 // { pid_t|sys||__clone(int flags, void *stack); }
+ SYS_FKTRACE = 288 // { int|sys||fktrace(int fd, int ops, int facs, pid_t pid); }
+ SYS_PREADV = 289 // { ssize_t|sys||preadv(int fd, const struct iovec *iovp, int iovcnt, int PAD, off_t offset); }
+ SYS_PWRITEV = 290 // { ssize_t|sys||pwritev(int fd, const struct iovec *iovp, int iovcnt, int PAD, off_t offset); }
+ SYS___GETCWD = 296 // { int|sys||__getcwd(char *bufp, size_t length); }
+ SYS_FCHROOT = 297 // { int|sys||fchroot(int fd); }
+ SYS_LCHFLAGS = 304 // { int|sys||lchflags(const char *path, u_long flags); }
+ SYS_ISSETUGID = 305 // { int|sys||issetugid(void); }
+ SYS_UTRACE = 306 // { int|sys||utrace(const char *label, void *addr, size_t len); }
+ SYS_GETCONTEXT = 307 // { int|sys||getcontext(struct __ucontext *ucp); }
+ SYS_SETCONTEXT = 308 // { int|sys||setcontext(const struct __ucontext *ucp); }
+ SYS__LWP_CREATE = 309 // { int|sys||_lwp_create(const struct __ucontext *ucp, u_long flags, lwpid_t *new_lwp); }
+ SYS__LWP_EXIT = 310 // { int|sys||_lwp_exit(void); }
+ SYS__LWP_SELF = 311 // { lwpid_t|sys||_lwp_self(void); }
+ SYS__LWP_WAIT = 312 // { int|sys||_lwp_wait(lwpid_t wait_for, lwpid_t *departed); }
+ SYS__LWP_SUSPEND = 313 // { int|sys||_lwp_suspend(lwpid_t target); }
+ SYS__LWP_CONTINUE = 314 // { int|sys||_lwp_continue(lwpid_t target); }
+ SYS__LWP_WAKEUP = 315 // { int|sys||_lwp_wakeup(lwpid_t target); }
+ SYS__LWP_GETPRIVATE = 316 // { void *|sys||_lwp_getprivate(void); }
+ SYS__LWP_SETPRIVATE = 317 // { void|sys||_lwp_setprivate(void *ptr); }
+ SYS__LWP_KILL = 318 // { int|sys||_lwp_kill(lwpid_t target, int signo); }
+ SYS__LWP_DETACH = 319 // { int|sys||_lwp_detach(lwpid_t target); }
+ SYS__LWP_UNPARK = 321 // { int|sys||_lwp_unpark(lwpid_t target, const void *hint); }
+ SYS__LWP_UNPARK_ALL = 322 // { ssize_t|sys||_lwp_unpark_all(const lwpid_t *targets, size_t ntargets, const void *hint); }
+ SYS__LWP_SETNAME = 323 // { int|sys||_lwp_setname(lwpid_t target, const char *name); }
+ SYS__LWP_GETNAME = 324 // { int|sys||_lwp_getname(lwpid_t target, char *name, size_t len); }
+ SYS__LWP_CTL = 325 // { int|sys||_lwp_ctl(int features, struct lwpctl **address); }
+ SYS___SIGACTION_SIGTRAMP = 340 // { int|sys||__sigaction_sigtramp(int signum, const struct sigaction *nsa, struct sigaction *osa, const void *tramp, int vers); }
+ SYS_PMC_GET_INFO = 341 // { int|sys||pmc_get_info(int ctr, int op, void *args); }
+ SYS_PMC_CONTROL = 342 // { int|sys||pmc_control(int ctr, int op, void *args); }
+ SYS_RASCTL = 343 // { int|sys||rasctl(void *addr, size_t len, int op); }
+ SYS_KQUEUE = 344 // { int|sys||kqueue(void); }
+ SYS__SCHED_SETPARAM = 346 // { int|sys||_sched_setparam(pid_t pid, lwpid_t lid, int policy, const struct sched_param *params); }
+ SYS__SCHED_GETPARAM = 347 // { int|sys||_sched_getparam(pid_t pid, lwpid_t lid, int *policy, struct sched_param *params); }
+ SYS__SCHED_SETAFFINITY = 348 // { int|sys||_sched_setaffinity(pid_t pid, lwpid_t lid, size_t size, const cpuset_t *cpuset); }
+ SYS__SCHED_GETAFFINITY = 349 // { int|sys||_sched_getaffinity(pid_t pid, lwpid_t lid, size_t size, cpuset_t *cpuset); }
+ SYS_SCHED_YIELD = 350 // { int|sys||sched_yield(void); }
+ SYS_FSYNC_RANGE = 354 // { int|sys||fsync_range(int fd, int flags, off_t start, off_t length); }
+ SYS_UUIDGEN = 355 // { int|sys||uuidgen(struct uuid *store, int count); }
+ SYS_GETVFSSTAT = 356 // { int|sys||getvfsstat(struct statvfs *buf, size_t bufsize, int flags); }
+ SYS_STATVFS1 = 357 // { int|sys||statvfs1(const char *path, struct statvfs *buf, int flags); }
+ SYS_FSTATVFS1 = 358 // { int|sys||fstatvfs1(int fd, struct statvfs *buf, int flags); }
+ SYS_EXTATTRCTL = 360 // { int|sys||extattrctl(const char *path, int cmd, const char *filename, int attrnamespace, const char *attrname); }
+ SYS_EXTATTR_SET_FILE = 361 // { int|sys||extattr_set_file(const char *path, int attrnamespace, const char *attrname, const void *data, size_t nbytes); }
+ SYS_EXTATTR_GET_FILE = 362 // { ssize_t|sys||extattr_get_file(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); }
+ SYS_EXTATTR_DELETE_FILE = 363 // { int|sys||extattr_delete_file(const char *path, int attrnamespace, const char *attrname); }
+ SYS_EXTATTR_SET_FD = 364 // { int|sys||extattr_set_fd(int fd, int attrnamespace, const char *attrname, const void *data, size_t nbytes); }
+ SYS_EXTATTR_GET_FD = 365 // { ssize_t|sys||extattr_get_fd(int fd, int attrnamespace, const char *attrname, void *data, size_t nbytes); }
+ SYS_EXTATTR_DELETE_FD = 366 // { int|sys||extattr_delete_fd(int fd, int attrnamespace, const char *attrname); }
+ SYS_EXTATTR_SET_LINK = 367 // { int|sys||extattr_set_link(const char *path, int attrnamespace, const char *attrname, const void *data, size_t nbytes); }
+ SYS_EXTATTR_GET_LINK = 368 // { ssize_t|sys||extattr_get_link(const char *path, int attrnamespace, const char *attrname, void *data, size_t nbytes); }
+ SYS_EXTATTR_DELETE_LINK = 369 // { int|sys||extattr_delete_link(const char *path, int attrnamespace, const char *attrname); }
+ SYS_EXTATTR_LIST_FD = 370 // { ssize_t|sys||extattr_list_fd(int fd, int attrnamespace, void *data, size_t nbytes); }
+ SYS_EXTATTR_LIST_FILE = 371 // { ssize_t|sys||extattr_list_file(const char *path, int attrnamespace, void *data, size_t nbytes); }
+ SYS_EXTATTR_LIST_LINK = 372 // { ssize_t|sys||extattr_list_link(const char *path, int attrnamespace, void *data, size_t nbytes); }
+ SYS_SETXATTR = 375 // { int|sys||setxattr(const char *path, const char *name, const void *value, size_t size, int flags); }
+ SYS_LSETXATTR = 376 // { int|sys||lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags); }
+ SYS_FSETXATTR = 377 // { int|sys||fsetxattr(int fd, const char *name, const void *value, size_t size, int flags); }
+ SYS_GETXATTR = 378 // { int|sys||getxattr(const char *path, const char *name, void *value, size_t size); }
+ SYS_LGETXATTR = 379 // { int|sys||lgetxattr(const char *path, const char *name, void *value, size_t size); }
+ SYS_FGETXATTR = 380 // { int|sys||fgetxattr(int fd, const char *name, void *value, size_t size); }
+ SYS_LISTXATTR = 381 // { int|sys||listxattr(const char *path, char *list, size_t size); }
+ SYS_LLISTXATTR = 382 // { int|sys||llistxattr(const char *path, char *list, size_t size); }
+ SYS_FLISTXATTR = 383 // { int|sys||flistxattr(int fd, char *list, size_t size); }
+ SYS_REMOVEXATTR = 384 // { int|sys||removexattr(const char *path, const char *name); }
+ SYS_LREMOVEXATTR = 385 // { int|sys||lremovexattr(const char *path, const char *name); }
+ SYS_FREMOVEXATTR = 386 // { int|sys||fremovexattr(int fd, const char *name); }
+ SYS_GETDENTS = 390 // { int|sys|30|getdents(int fd, char *buf, size_t count); }
+ SYS_SOCKET = 394 // { int|sys|30|socket(int domain, int type, int protocol); }
+ SYS_GETFH = 395 // { int|sys|30|getfh(const char *fname, void *fhp, size_t *fh_size); }
+ SYS_MOUNT = 410 // { int|sys|50|mount(const char *type, const char *path, int flags, void *data, size_t data_len); }
+ SYS_MREMAP = 411 // { void *|sys||mremap(void *old_address, size_t old_size, void *new_address, size_t new_size, int flags); }
+ SYS_PSET_CREATE = 412 // { int|sys||pset_create(psetid_t *psid); }
+ SYS_PSET_DESTROY = 413 // { int|sys||pset_destroy(psetid_t psid); }
+ SYS_PSET_ASSIGN = 414 // { int|sys||pset_assign(psetid_t psid, cpuid_t cpuid, psetid_t *opsid); }
+ SYS__PSET_BIND = 415 // { int|sys||_pset_bind(idtype_t idtype, id_t first_id, id_t second_id, psetid_t psid, psetid_t *opsid); }
+ SYS_POSIX_FADVISE = 416 // { int|sys|50|posix_fadvise(int fd, int PAD, off_t offset, off_t len, int advice); }
+ SYS_SELECT = 417 // { int|sys|50|select(int nd, fd_set *in, fd_set *ou, fd_set *ex, struct timeval *tv); }
+ SYS_GETTIMEOFDAY = 418 // { int|sys|50|gettimeofday(struct timeval *tp, void *tzp); }
+ SYS_SETTIMEOFDAY = 419 // { int|sys|50|settimeofday(const struct timeval *tv, const void *tzp); }
+ SYS_UTIMES = 420 // { int|sys|50|utimes(const char *path, const struct timeval *tptr); }
+ SYS_ADJTIME = 421 // { int|sys|50|adjtime(const struct timeval *delta, struct timeval *olddelta); }
+ SYS_FUTIMES = 423 // { int|sys|50|futimes(int fd, const struct timeval *tptr); }
+ SYS_LUTIMES = 424 // { int|sys|50|lutimes(const char *path, const struct timeval *tptr); }
+ SYS_SETITIMER = 425 // { int|sys|50|setitimer(int which, const struct itimerval *itv, struct itimerval *oitv); }
+ SYS_GETITIMER = 426 // { int|sys|50|getitimer(int which, struct itimerval *itv); }
+ SYS_CLOCK_GETTIME = 427 // { int|sys|50|clock_gettime(clockid_t clock_id, struct timespec *tp); }
+ SYS_CLOCK_SETTIME = 428 // { int|sys|50|clock_settime(clockid_t clock_id, const struct timespec *tp); }
+ SYS_CLOCK_GETRES = 429 // { int|sys|50|clock_getres(clockid_t clock_id, struct timespec *tp); }
+ SYS_NANOSLEEP = 430 // { int|sys|50|nanosleep(const struct timespec *rqtp, struct timespec *rmtp); }
+ SYS___SIGTIMEDWAIT = 431 // { int|sys|50|__sigtimedwait(const sigset_t *set, siginfo_t *info, struct timespec *timeout); }
+ SYS__LWP_PARK = 434 // { int|sys|50|_lwp_park(const struct timespec *ts, lwpid_t unpark, const void *hint, const void *unparkhint); }
+ SYS_KEVENT = 435 // { int|sys|50|kevent(int fd, const struct kevent *changelist, size_t nchanges, struct kevent *eventlist, size_t nevents, const struct timespec *timeout); }
+ SYS_PSELECT = 436 // { int|sys|50|pselect(int nd, fd_set *in, fd_set *ou, fd_set *ex, const struct timespec *ts, const sigset_t *mask); }
+ SYS_POLLTS = 437 // { int|sys|50|pollts(struct pollfd *fds, u_int nfds, const struct timespec *ts, const sigset_t *mask); }
+ SYS_STAT = 439 // { int|sys|50|stat(const char *path, struct stat *ub); }
+ SYS_FSTAT = 440 // { int|sys|50|fstat(int fd, struct stat *sb); }
+ SYS_LSTAT = 441 // { int|sys|50|lstat(const char *path, struct stat *ub); }
+ SYS___SEMCTL = 442 // { int|sys|50|__semctl(int semid, int semnum, int cmd, ... union __semun *arg); }
+ SYS_SHMCTL = 443 // { int|sys|50|shmctl(int shmid, int cmd, struct shmid_ds *buf); }
+ SYS_MSGCTL = 444 // { int|sys|50|msgctl(int msqid, int cmd, struct msqid_ds *buf); }
+ SYS_GETRUSAGE = 445 // { int|sys|50|getrusage(int who, struct rusage *rusage); }
+ SYS_TIMER_SETTIME = 446 // { int|sys|50|timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue); }
+ SYS_TIMER_GETTIME = 447 // { int|sys|50|timer_gettime(timer_t timerid, struct itimerspec *value); }
+ SYS_NTP_GETTIME = 448 // { int|sys|50|ntp_gettime(struct ntptimeval *ntvp); }
+ SYS_WAIT4 = 449 // { int|sys|50|wait4(pid_t pid, int *status, int options, struct rusage *rusage); }
+ SYS_MKNOD = 450 // { int|sys|50|mknod(const char *path, mode_t mode, dev_t dev); }
+ SYS_FHSTAT = 451 // { int|sys|50|fhstat(const void *fhp, size_t fh_size, struct stat *sb); }
+ SYS_PIPE2 = 453 // { int|sys||pipe2(int *fildes, int flags); }
+ SYS_DUP3 = 454 // { int|sys||dup3(int from, int to, int flags); }
+ SYS_KQUEUE1 = 455 // { int|sys||kqueue1(int flags); }
+ SYS_PACCEPT = 456 // { int|sys||paccept(int s, struct sockaddr *name, socklen_t *anamelen, const sigset_t *mask, int flags); }
+ SYS_LINKAT = 457 // { int|sys||linkat(int fd1, const char *name1, int fd2, const char *name2, int flags); }
+ SYS_RENAMEAT = 458 // { int|sys||renameat(int fromfd, const char *from, int tofd, const char *to); }
+ SYS_MKFIFOAT = 459 // { int|sys||mkfifoat(int fd, const char *path, mode_t mode); }
+ SYS_MKNODAT = 460 // { int|sys||mknodat(int fd, const char *path, mode_t mode, uint32_t dev); }
+ SYS_MKDIRAT = 461 // { int|sys||mkdirat(int fd, const char *path, mode_t mode); }
+ SYS_FACCESSAT = 462 // { int|sys||faccessat(int fd, const char *path, int amode, int flag); }
+ SYS_FCHMODAT = 463 // { int|sys||fchmodat(int fd, const char *path, mode_t mode, int flag); }
+ SYS_FCHOWNAT = 464 // { int|sys||fchownat(int fd, const char *path, uid_t owner, gid_t group, int flag); }
+ SYS_FEXECVE = 465 // { int|sys||fexecve(int fd, char * const *argp, char * const *envp); }
+ SYS_FSTATAT = 466 // { int|sys||fstatat(int fd, const char *path, struct stat *buf, int flag); }
+ SYS_UTIMENSAT = 467 // { int|sys||utimensat(int fd, const char *path, const struct timespec *tptr, int flag); }
+ SYS_OPENAT = 468 // { int|sys||openat(int fd, const char *path, int oflags, ... mode_t mode); }
+ SYS_READLINKAT = 469 // { int|sys||readlinkat(int fd, const char *path, char *buf, size_t bufsize); }
+ SYS_SYMLINKAT = 470 // { int|sys||symlinkat(const char *path1, int fd, const char *path2); }
+ SYS_UNLINKAT = 471 // { int|sys||unlinkat(int fd, const char *path, int flag); }
+ SYS_FUTIMENS = 472 // { int|sys||futimens(int fd, const struct timespec *tptr); }
+ SYS___QUOTACTL = 473 // { int|sys||__quotactl(const char *path, struct quotactl_args *args); }
+ SYS_POSIX_SPAWN = 474 // { int|sys||posix_spawn(pid_t *pid, const char *path, const struct posix_spawn_file_actions *file_actions, const struct posix_spawnattr *attrp, char *const *argv, char *const *envp); }
+ SYS_RECVMMSG = 475 // { int|sys||recvmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags, struct timespec *timeout); }
+ SYS_SENDMMSG = 476 // { int|sys||sendmmsg(int s, struct mmsghdr *mmsg, unsigned int vlen, unsigned int flags); }
+)
diff --git a/src/pkg/syscall/zsysnum_openbsd_386.go b/src/pkg/syscall/zsysnum_openbsd_386.go
index 1d97fe0db..82c98b90e 100644
--- a/src/pkg/syscall/zsysnum_openbsd_386.go
+++ b/src/pkg/syscall/zsysnum_openbsd_386.go
@@ -1,4 +1,4 @@
-// mksysnum_openbsd.pl
+// mksysnum_openbsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
package syscall
@@ -11,6 +11,7 @@ const (
SYS_OPEN = 5 // { int sys_open(const char *path, \
SYS_CLOSE = 6 // { int sys_close(int fd); }
SYS_WAIT4 = 7 // { pid_t sys_wait4(pid_t pid, int *status, int options, \
+ SYS___TFORK = 8 // { int sys___tfork(const struct __tfork *param, \
SYS_LINK = 9 // { int sys_link(const char *path, const char *link); }
SYS_UNLINK = 10 // { int sys_unlink(const char *path); }
SYS_CHDIR = 12 // { int sys_chdir(const char *path); }
@@ -19,6 +20,7 @@ const (
SYS_CHMOD = 15 // { int sys_chmod(const char *path, mode_t mode); }
SYS_CHOWN = 16 // { int sys_chown(const char *path, uid_t uid, \
SYS_OBREAK = 17 // { int sys_obreak(char *nsize); } break
+ SYS_GETDTABLECOUNT = 18 // { int sys_getdtablecount(void); }
SYS_GETPID = 20 // { pid_t sys_getpid(void); }
SYS_MOUNT = 21 // { int sys_mount(const char *type, const char *path, \
SYS_UNMOUNT = 22 // { int sys_unmount(const char *path, int flags); }
@@ -111,10 +113,6 @@ const (
SYS_SETGID = 181 // { int sys_setgid(gid_t gid); }
SYS_SETEGID = 182 // { int sys_setegid(gid_t egid); }
SYS_SETEUID = 183 // { int sys_seteuid(uid_t euid); }
- LFS_BMAPV = 184 // { int lfs_bmapv(fsid_t *fsidp, \
- LFS_MARKV = 185 // { int lfs_markv(fsid_t *fsidp, \
- LFS_SEGCLEAN = 186 // { int lfs_segclean(fsid_t *fsidp, u_long segment); }
- LFS_SEGWAIT = 187 // { int lfs_segwait(fsid_t *fsidp, struct timeval *tv); }
SYS_PATHCONF = 191 // { long sys_pathconf(const char *path, int name); }
SYS_FPATHCONF = 192 // { long sys_fpathconf(int fd, int name); }
SYS_SWAPCTL = 193 // { int sys_swapctl(int cmd, const void *arg, int misc); }
@@ -129,7 +127,6 @@ const (
SYS_MUNLOCK = 204 // { int sys_munlock(const void *addr, size_t len); }
SYS_FUTIMES = 206 // { int sys_futimes(int fd, \
SYS_GETPGID = 207 // { pid_t sys_getpgid(pid_t pid); }
- SYS_NNPFSPIOCTL = 208 // { int sys_nnpfspioctl(int operation, char *a_pathP, \
SYS_SEMGET = 221 // { int sys_semget(key_t key, int nsems, int semflg); }
SYS_MSGGET = 225 // { int sys_msgget(key_t key, int msgflg); }
SYS_MSGSND = 226 // { int sys_msgsnd(int msqid, const void *msgp, size_t msgsz, \
@@ -141,7 +138,6 @@ const (
SYS_CLOCK_GETRES = 234 // { int sys_clock_getres(clockid_t clock_id, \
SYS_NANOSLEEP = 240 // { int sys_nanosleep(const struct timespec *rqtp, \
SYS_MINHERIT = 250 // { int sys_minherit(void *addr, size_t len, \
- SYS_RFORK = 251 // { int sys_rfork(int flags); }
SYS_POLL = 252 // { int sys_poll(struct pollfd *fds, \
SYS_ISSETUGID = 253 // { int sys_issetugid(void); }
SYS_LCHOWN = 254 // { int sys_lchown(const char *path, uid_t uid, gid_t gid); }
@@ -201,7 +197,6 @@ const (
SYS_UNLINKAT = 325 // { int sys_unlinkat(int fd, const char *path, \
SYS_UTIMENSAT = 326 // { int sys_utimensat(int fd, const char *path, \
SYS_FUTIMENS = 327 // { int sys_futimens(int fd, \
- SYS___TFORK = 328 // { int sys___tfork(struct __tfork *param); }
SYS___SET_TCB = 329 // { void sys___set_tcb(void *tcb); }
SYS___GET_TCB = 330 // { void *sys___get_tcb(void); }
)
diff --git a/src/pkg/syscall/zsysnum_openbsd_amd64.go b/src/pkg/syscall/zsysnum_openbsd_amd64.go
index 1d97fe0db..82c98b90e 100644
--- a/src/pkg/syscall/zsysnum_openbsd_amd64.go
+++ b/src/pkg/syscall/zsysnum_openbsd_amd64.go
@@ -1,4 +1,4 @@
-// mksysnum_openbsd.pl
+// mksysnum_openbsd.pl
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
package syscall
@@ -11,6 +11,7 @@ const (
SYS_OPEN = 5 // { int sys_open(const char *path, \
SYS_CLOSE = 6 // { int sys_close(int fd); }
SYS_WAIT4 = 7 // { pid_t sys_wait4(pid_t pid, int *status, int options, \
+ SYS___TFORK = 8 // { int sys___tfork(const struct __tfork *param, \
SYS_LINK = 9 // { int sys_link(const char *path, const char *link); }
SYS_UNLINK = 10 // { int sys_unlink(const char *path); }
SYS_CHDIR = 12 // { int sys_chdir(const char *path); }
@@ -19,6 +20,7 @@ const (
SYS_CHMOD = 15 // { int sys_chmod(const char *path, mode_t mode); }
SYS_CHOWN = 16 // { int sys_chown(const char *path, uid_t uid, \
SYS_OBREAK = 17 // { int sys_obreak(char *nsize); } break
+ SYS_GETDTABLECOUNT = 18 // { int sys_getdtablecount(void); }
SYS_GETPID = 20 // { pid_t sys_getpid(void); }
SYS_MOUNT = 21 // { int sys_mount(const char *type, const char *path, \
SYS_UNMOUNT = 22 // { int sys_unmount(const char *path, int flags); }
@@ -111,10 +113,6 @@ const (
SYS_SETGID = 181 // { int sys_setgid(gid_t gid); }
SYS_SETEGID = 182 // { int sys_setegid(gid_t egid); }
SYS_SETEUID = 183 // { int sys_seteuid(uid_t euid); }
- LFS_BMAPV = 184 // { int lfs_bmapv(fsid_t *fsidp, \
- LFS_MARKV = 185 // { int lfs_markv(fsid_t *fsidp, \
- LFS_SEGCLEAN = 186 // { int lfs_segclean(fsid_t *fsidp, u_long segment); }
- LFS_SEGWAIT = 187 // { int lfs_segwait(fsid_t *fsidp, struct timeval *tv); }
SYS_PATHCONF = 191 // { long sys_pathconf(const char *path, int name); }
SYS_FPATHCONF = 192 // { long sys_fpathconf(int fd, int name); }
SYS_SWAPCTL = 193 // { int sys_swapctl(int cmd, const void *arg, int misc); }
@@ -129,7 +127,6 @@ const (
SYS_MUNLOCK = 204 // { int sys_munlock(const void *addr, size_t len); }
SYS_FUTIMES = 206 // { int sys_futimes(int fd, \
SYS_GETPGID = 207 // { pid_t sys_getpgid(pid_t pid); }
- SYS_NNPFSPIOCTL = 208 // { int sys_nnpfspioctl(int operation, char *a_pathP, \
SYS_SEMGET = 221 // { int sys_semget(key_t key, int nsems, int semflg); }
SYS_MSGGET = 225 // { int sys_msgget(key_t key, int msgflg); }
SYS_MSGSND = 226 // { int sys_msgsnd(int msqid, const void *msgp, size_t msgsz, \
@@ -141,7 +138,6 @@ const (
SYS_CLOCK_GETRES = 234 // { int sys_clock_getres(clockid_t clock_id, \
SYS_NANOSLEEP = 240 // { int sys_nanosleep(const struct timespec *rqtp, \
SYS_MINHERIT = 250 // { int sys_minherit(void *addr, size_t len, \
- SYS_RFORK = 251 // { int sys_rfork(int flags); }
SYS_POLL = 252 // { int sys_poll(struct pollfd *fds, \
SYS_ISSETUGID = 253 // { int sys_issetugid(void); }
SYS_LCHOWN = 254 // { int sys_lchown(const char *path, uid_t uid, gid_t gid); }
@@ -201,7 +197,6 @@ const (
SYS_UNLINKAT = 325 // { int sys_unlinkat(int fd, const char *path, \
SYS_UTIMENSAT = 326 // { int sys_utimensat(int fd, const char *path, \
SYS_FUTIMENS = 327 // { int sys_futimens(int fd, \
- SYS___TFORK = 328 // { int sys___tfork(struct __tfork *param); }
SYS___SET_TCB = 329 // { void sys___set_tcb(void *tcb); }
SYS___GET_TCB = 330 // { void *sys___get_tcb(void); }
)
diff --git a/src/pkg/syscall/zsysnum_plan9_amd64.go b/src/pkg/syscall/zsysnum_plan9_amd64.go
new file mode 100644
index 000000000..c03864647
--- /dev/null
+++ b/src/pkg/syscall/zsysnum_plan9_amd64.go
@@ -0,0 +1,48 @@
+// mksysnum_plan9.sh /media/sys/src/libc/9syscall/sys.h
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const (
+ SYS_SYSR1 = 0
+ SYS_BIND = 2
+ SYS_CHDIR = 3
+ SYS_CLOSE = 4
+ SYS_DUP = 5
+ SYS_ALARM = 6
+ SYS_EXEC = 7
+ SYS_EXITS = 8
+ SYS_FAUTH = 10
+ SYS_SEGBRK = 12
+ SYS_OPEN = 14
+ SYS_OSEEK = 16
+ SYS_SLEEP = 17
+ SYS_RFORK = 19
+ SYS_PIPE = 21
+ SYS_CREATE = 22
+ SYS_FD2PATH = 23
+ SYS_BRK_ = 24
+ SYS_REMOVE = 25
+ SYS_NOTIFY = 28
+ SYS_NOTED = 29
+ SYS_SEGATTACH = 30
+ SYS_SEGDETACH = 31
+ SYS_SEGFREE = 32
+ SYS_SEGFLUSH = 33
+ SYS_RENDEZVOUS = 34
+ SYS_UNMOUNT = 35
+ SYS_SEMACQUIRE = 37
+ SYS_SEMRELEASE = 38
+ SYS_SEEK = 39
+ SYS_FVERSION = 40
+ SYS_ERRSTR = 41
+ SYS_STAT = 42
+ SYS_FSTAT = 43
+ SYS_WSTAT = 44
+ SYS_FWSTAT = 45
+ SYS_MOUNT = 46
+ SYS_AWAIT = 47
+ SYS_PREAD = 50
+ SYS_PWRITE = 51
+ SYS_NANOTIME = 60
+)
diff --git a/src/pkg/syscall/ztypes_darwin_386.go b/src/pkg/syscall/ztypes_darwin_386.go
index 566b02195..71346fbc1 100644
--- a/src/pkg/syscall/ztypes_darwin_386.go
+++ b/src/pkg/syscall/ztypes_darwin_386.go
@@ -226,6 +226,12 @@ type Cmsghdr struct {
Type int32
}
+type Inet4Pktinfo struct {
+ Ifindex uint32
+ Spec_dst [4]byte /* in_addr */
+ Addr [4]byte /* in_addr */
+}
+
type Inet6Pktinfo struct {
Addr [16]byte /* in6_addr */
Ifindex uint32
@@ -242,6 +248,7 @@ const (
SizeofIPv6Mreq = 0x14
SizeofMsghdr = 0x1c
SizeofCmsghdr = 0xc
+ SizeofInet4Pktinfo = 0xc
SizeofInet6Pktinfo = 0x14
)
@@ -416,3 +423,13 @@ type BpfHdr struct {
Hdrlen uint16
Pad_cgo_0 [2]byte
}
+
+type Termios struct {
+ Iflag uint32
+ Oflag uint32
+ Cflag uint32
+ Lflag uint32
+ Cc [20]uint8
+ Ispeed uint32
+ Ospeed uint32
+}
diff --git a/src/pkg/syscall/ztypes_darwin_amd64.go b/src/pkg/syscall/ztypes_darwin_amd64.go
index 2c6531158..f0809fe4a 100644
--- a/src/pkg/syscall/ztypes_darwin_amd64.go
+++ b/src/pkg/syscall/ztypes_darwin_amd64.go
@@ -234,6 +234,12 @@ type Cmsghdr struct {
Type int32
}
+type Inet4Pktinfo struct {
+ Ifindex uint32
+ Spec_dst [4]byte /* in_addr */
+ Addr [4]byte /* in_addr */
+}
+
type Inet6Pktinfo struct {
Addr [16]byte /* in6_addr */
Ifindex uint32
@@ -250,6 +256,7 @@ const (
SizeofIPv6Mreq = 0x14
SizeofMsghdr = 0x30
SizeofCmsghdr = 0xc
+ SizeofInet4Pktinfo = 0xc
SizeofInet6Pktinfo = 0x14
)
@@ -425,3 +432,14 @@ type BpfHdr struct {
Hdrlen uint16
Pad_cgo_0 [2]byte
}
+
+type Termios struct {
+ Iflag uint64
+ Oflag uint64
+ Cflag uint64
+ Lflag uint64
+ Cc [20]uint8
+ Pad_cgo_0 [4]byte
+ Ispeed uint64
+ Ospeed uint64
+}
diff --git a/src/pkg/syscall/ztypes_freebsd_386.go b/src/pkg/syscall/ztypes_freebsd_386.go
index 4149948c5..89de58e53 100644
--- a/src/pkg/syscall/ztypes_freebsd_386.go
+++ b/src/pkg/syscall/ztypes_freebsd_386.go
@@ -271,12 +271,13 @@ type FdSet struct {
}
const (
- SizeofIfMsghdr = 0x60
- SizeofIfData = 0x50
- SizeofIfaMsghdr = 0x14
- SizeofIfmaMsghdr = 0x10
- SizeofRtMsghdr = 0x5c
- SizeofRtMetrics = 0x38
+ SizeofIfMsghdr = 0x60
+ SizeofIfData = 0x50
+ SizeofIfaMsghdr = 0x14
+ SizeofIfmaMsghdr = 0x10
+ SizeofIfAnnounceMsghdr = 0x18
+ SizeofRtMsghdr = 0x5c
+ SizeofRtMetrics = 0x38
)
type IfMsghdr struct {
@@ -339,6 +340,15 @@ type IfmaMsghdr struct {
Pad_cgo_0 [2]byte
}
+type IfAnnounceMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Index uint16
+ Name [16]int8
+ What uint16
+}
+
type RtMsghdr struct {
Msglen uint16
Version uint8
diff --git a/src/pkg/syscall/ztypes_freebsd_amd64.go b/src/pkg/syscall/ztypes_freebsd_amd64.go
index 80c12f607..d5e87682d 100644
--- a/src/pkg/syscall/ztypes_freebsd_amd64.go
+++ b/src/pkg/syscall/ztypes_freebsd_amd64.go
@@ -273,12 +273,13 @@ type FdSet struct {
}
const (
- SizeofIfMsghdr = 0xa8
- SizeofIfData = 0x98
- SizeofIfaMsghdr = 0x14
- SizeofIfmaMsghdr = 0x10
- SizeofRtMsghdr = 0x98
- SizeofRtMetrics = 0x70
+ SizeofIfMsghdr = 0xa8
+ SizeofIfData = 0x98
+ SizeofIfaMsghdr = 0x14
+ SizeofIfmaMsghdr = 0x10
+ SizeofIfAnnounceMsghdr = 0x18
+ SizeofRtMsghdr = 0x98
+ SizeofRtMetrics = 0x70
)
type IfMsghdr struct {
@@ -341,6 +342,15 @@ type IfmaMsghdr struct {
Pad_cgo_0 [2]byte
}
+type IfAnnounceMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Index uint16
+ Name [16]int8
+ What uint16
+}
+
type RtMsghdr struct {
Msglen uint16
Version uint8
diff --git a/src/pkg/syscall/ztypes_freebsd_arm.go b/src/pkg/syscall/ztypes_freebsd_arm.go
new file mode 100644
index 000000000..4f67a39e6
--- /dev/null
+++ b/src/pkg/syscall/ztypes_freebsd_arm.go
@@ -0,0 +1,436 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_freebsd.go
+
+package syscall
+
+const (
+ sizeofPtr = 0x4
+ sizeofShort = 0x2
+ sizeofInt = 0x4
+ sizeofLong = 0x4
+ sizeofLongLong = 0x8
+)
+
+type (
+ _C_short int16
+ _C_int int32
+ _C_long int32
+ _C_long_long int64
+)
+
+type Timespec struct {
+ Sec int64
+ Nsec int32
+}
+
+type Timeval struct {
+ Sec int64
+ Usec int32
+}
+
+type Rusage struct {
+ Utime Timeval
+ Stime Timeval
+ Maxrss int32
+ Ixrss int32
+ Idrss int32
+ Isrss int32
+ Minflt int32
+ Majflt int32
+ Nswap int32
+ Inblock int32
+ Oublock int32
+ Msgsnd int32
+ Msgrcv int32
+ Nsignals int32
+ Nvcsw int32
+ Nivcsw int32
+}
+
+type Rlimit struct {
+ Cur int64
+ Max int64
+}
+
+type _Gid_t uint32
+
+const (
+ O_CLOEXEC = 0 // not supported
+)
+
+const (
+ S_IFMT = 0xf000
+ S_IFIFO = 0x1000
+ S_IFCHR = 0x2000
+ S_IFDIR = 0x4000
+ S_IFBLK = 0x6000
+ S_IFREG = 0x8000
+ S_IFLNK = 0xa000
+ S_IFSOCK = 0xc000
+ S_ISUID = 0x800
+ S_ISGID = 0x400
+ S_ISVTX = 0x200
+ S_IRUSR = 0x100
+ S_IWUSR = 0x80
+ S_IXUSR = 0x40
+)
+
+type Stat_t struct {
+ Dev uint32
+ Ino uint32
+ Mode uint16
+ Nlink uint16
+ Uid uint32
+ Gid uint32
+ Rdev uint32
+ Atimespec Timespec
+ Mtimespec Timespec
+ Ctimespec Timespec
+ Size int64
+ Blocks int64
+ Blksize uint32
+ Flags uint32
+ Gen uint32
+ Lspare int32
+ Birthtimespec Timespec
+ Pad_cgo_0 [4]byte
+}
+
+type Statfs_t struct {
+ Version uint32
+ Type uint32
+ Flags uint64
+ Bsize uint64
+ Iosize uint64
+ Blocks uint64
+ Bfree uint64
+ Bavail int64
+ Files uint64
+ Ffree int64
+ Syncwrites uint64
+ Asyncwrites uint64
+ Syncreads uint64
+ Asyncreads uint64
+ Spare [10]uint64
+ Namemax uint32
+ Owner uint32
+ Fsid Fsid
+ Charspare [80]int8
+ Fstypename [16]int8
+ Mntfromname [88]int8
+ Mntonname [88]int8
+}
+
+type Flock_t struct {
+ Start int64
+ Len int64
+ Pid int32
+ Type int16
+ Whence int16
+ Sysid int32
+}
+
+type Dirent struct {
+ Fileno uint32
+ Reclen uint16
+ Type uint8
+ Namlen uint8
+ Name [256]int8
+}
+
+type Fsid struct {
+ Val [2]int32
+}
+
+type RawSockaddrInet4 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Addr [4]byte /* in_addr */
+ Zero [8]int8
+}
+
+type RawSockaddrInet6 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type RawSockaddrUnix struct {
+ Len uint8
+ Family uint8
+ Path [104]int8
+ Pad_cgo_0 [2]byte
+}
+
+type RawSockaddrDatalink struct {
+ Len uint8
+ Family uint8
+ Index uint16
+ Type uint8
+ Nlen uint8
+ Alen uint8
+ Slen uint8
+ Data [46]int8
+ Pad_cgo_0 [2]byte
+}
+
+type RawSockaddr struct {
+ Len uint8
+ Family uint8
+ Data [14]int8
+}
+
+type RawSockaddrAny struct {
+ Addr RawSockaddr
+ Pad [92]int8
+}
+
+type _Socklen uint32
+
+type Linger struct {
+ Onoff int32
+ Linger int32
+}
+
+type Iovec struct {
+ Base *byte
+ Len uint32
+}
+
+type IPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type IPMreqn struct {
+ Multiaddr [4]byte /* in_addr */
+ Address [4]byte /* in_addr */
+ Ifindex int32
+}
+
+type IPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+type Msghdr struct {
+ Name *byte
+ Namelen uint32
+ Iov *Iovec
+ Iovlen int32
+ Control *byte
+ Controllen uint32
+ Flags int32
+}
+
+type Cmsghdr struct {
+ Len uint32
+ Level int32
+ Type int32
+}
+
+type Inet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex uint32
+}
+
+const (
+ SizeofSockaddrInet4 = 0x10
+ SizeofSockaddrInet6 = 0x1c
+ SizeofSockaddrAny = 0x6c
+ SizeofSockaddrUnix = 0x6c
+ SizeofSockaddrDatalink = 0x38
+ SizeofLinger = 0x8
+ SizeofIPMreq = 0x8
+ SizeofIPMreqn = 0xc
+ SizeofIPv6Mreq = 0x14
+ SizeofMsghdr = 0x1c
+ SizeofCmsghdr = 0xc
+ SizeofInet6Pktinfo = 0x14
+)
+
+const (
+ PTRACE_TRACEME = 0x0
+ PTRACE_CONT = 0x7
+ PTRACE_KILL = 0x8
+)
+
+type Kevent_t struct {
+ Ident uint32
+ Filter int16
+ Flags uint16
+ Fflags uint32
+ Data int32
+ Udata *byte
+}
+
+type FdSet struct {
+ X__fds_bits [32]uint32
+}
+
+const (
+ SizeofIfMsghdr = 0x68
+ SizeofIfData = 0x58
+ SizeofIfaMsghdr = 0x14
+ SizeofIfmaMsghdr = 0x10
+ SizeofIfAnnounceMsghdr = 0x18
+ SizeofRtMsghdr = 0x5c
+ SizeofRtMetrics = 0x38
+)
+
+type IfMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Data IfData
+}
+
+type IfData struct {
+ Type uint8
+ Physical uint8
+ Addrlen uint8
+ Hdrlen uint8
+ Link_state uint8
+ Spare_char1 uint8 //Vhid uint8
+ Spare_char2 uint8
+ Datalen uint8
+ Mtu uint32
+ Metric uint32
+ Baudrate uint32
+ Ipackets uint32
+ Ierrors uint32
+ Opackets uint32
+ Oerrors uint32
+ Collisions uint32
+ Ibytes uint32
+ Obytes uint32
+ Imcasts uint32
+ Omcasts uint32
+ Iqdrops uint32
+ Noproto uint32
+ Hwassist uint32
+ Epoch int64
+ Lastchange Timeval
+}
+
+type IfaMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Metric int32
+}
+
+type IfmaMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Index uint16
+ Pad_cgo_0 [2]byte
+}
+
+type IfAnnounceMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Index uint16
+ Name [16]int8
+ What uint16
+}
+
+type RtMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Flags int32
+ Addrs int32
+ Pid int32
+ Seq int32
+ Errno int32
+ Fmask int32
+ Inits uint32
+ Rmx RtMetrics
+}
+
+type RtMetrics struct {
+ Locks uint32
+ Mtu uint32
+ Hopcount uint32
+ Expire uint32
+ Recvpipe uint32
+ Sendpipe uint32
+ Ssthresh uint32
+ Rtt uint32
+ Rttvar uint32
+ Pksent uint32
+ Weight uint32
+ Filler [3]uint32
+}
+
+const (
+ SizeofBpfVersion = 0x4
+ SizeofBpfStat = 0x8
+ SizeofBpfZbuf = 0xc
+ SizeofBpfProgram = 0x8
+ SizeofBpfInsn = 0x8
+ SizeofBpfHdr = 0x18
+ SizeofBpfZbufHeader = 0x20
+)
+
+type BpfVersion struct {
+ Major uint16
+ Minor uint16
+}
+
+type BpfStat struct {
+ Recv uint32
+ Drop uint32
+}
+
+type BpfZbuf struct {
+ Bufa *byte
+ Bufb *byte
+ Buflen uint32
+}
+
+type BpfProgram struct {
+ Len uint32
+ Insns *BpfInsn
+}
+
+type BpfInsn struct {
+ Code uint16
+ Jt uint8
+ Jf uint8
+ K uint32
+}
+
+type BpfHdr struct {
+ Tstamp Timeval
+ Caplen uint32
+ Datalen uint32
+ Hdrlen uint16
+ Pad_cgo_0 [2]byte
+}
+
+type BpfZbufHeader struct {
+ Kernel_gen uint32
+ Kernel_len uint32
+ User_gen uint32
+ X_bzh_pad [5]uint32
+}
diff --git a/src/pkg/syscall/ztypes_linux_386.go b/src/pkg/syscall/ztypes_linux_386.go
index fcead2f97..e1c30b649 100644
--- a/src/pkg/syscall/ztypes_linux_386.go
+++ b/src/pkg/syscall/ztypes_linux_386.go
@@ -231,7 +231,7 @@ type Cmsghdr struct {
Len uint32
Level int32
Type int32
- X__cmsg_data [0]byte
+ X__cmsg_data [0]uint8
}
type Inet4Pktinfo struct {
@@ -251,6 +251,40 @@ type Ucred struct {
Gid uint32
}
+type TCPInfo struct {
+ State uint8
+ Ca_state uint8
+ Retransmits uint8
+ Probes uint8
+ Backoff uint8
+ Options uint8
+ Pad_cgo_0 [2]byte
+ Rto uint32
+ Ato uint32
+ Snd_mss uint32
+ Rcv_mss uint32
+ Unacked uint32
+ Sacked uint32
+ Lost uint32
+ Retrans uint32
+ Fackets uint32
+ Last_data_sent uint32
+ Last_ack_sent uint32
+ Last_data_recv uint32
+ Last_ack_recv uint32
+ Pmtu uint32
+ Rcv_ssthresh uint32
+ Rtt uint32
+ Rttvar uint32
+ Snd_ssthresh uint32
+ Snd_cwnd uint32
+ Advmss uint32
+ Reordering uint32
+ Rcv_rtt uint32
+ Rcv_space uint32
+ Total_retrans uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -267,84 +301,101 @@ const (
SizeofInet4Pktinfo = 0xc
SizeofInet6Pktinfo = 0x14
SizeofUcred = 0xc
+ SizeofTCPInfo = 0x68
)
const (
- IFA_UNSPEC = 0x0
- IFA_ADDRESS = 0x1
- IFA_LOCAL = 0x2
- IFA_LABEL = 0x3
- IFA_BROADCAST = 0x4
- IFA_ANYCAST = 0x5
- IFA_CACHEINFO = 0x6
- IFA_MULTICAST = 0x7
- IFLA_UNSPEC = 0x0
- IFLA_ADDRESS = 0x1
- IFLA_BROADCAST = 0x2
- IFLA_IFNAME = 0x3
- IFLA_MTU = 0x4
- IFLA_LINK = 0x5
- IFLA_QDISC = 0x6
- IFLA_STATS = 0x7
- IFLA_COST = 0x8
- IFLA_PRIORITY = 0x9
- IFLA_MASTER = 0xa
- IFLA_WIRELESS = 0xb
- IFLA_PROTINFO = 0xc
- IFLA_TXQLEN = 0xd
- IFLA_MAP = 0xe
- IFLA_WEIGHT = 0xf
- IFLA_OPERSTATE = 0x10
- IFLA_LINKMODE = 0x11
- IFLA_LINKINFO = 0x12
- IFLA_NET_NS_PID = 0x13
- IFLA_IFALIAS = 0x14
- IFLA_MAX = 0x1c
- RT_SCOPE_UNIVERSE = 0x0
- RT_SCOPE_SITE = 0xc8
- RT_SCOPE_LINK = 0xfd
- RT_SCOPE_HOST = 0xfe
- RT_SCOPE_NOWHERE = 0xff
- RT_TABLE_UNSPEC = 0x0
- RT_TABLE_COMPAT = 0xfc
- RT_TABLE_DEFAULT = 0xfd
- RT_TABLE_MAIN = 0xfe
- RT_TABLE_LOCAL = 0xff
- RT_TABLE_MAX = 0xffffffff
- RTA_UNSPEC = 0x0
- RTA_DST = 0x1
- RTA_SRC = 0x2
- RTA_IIF = 0x3
- RTA_OIF = 0x4
- RTA_GATEWAY = 0x5
- RTA_PRIORITY = 0x6
- RTA_PREFSRC = 0x7
- RTA_METRICS = 0x8
- RTA_MULTIPATH = 0x9
- RTA_FLOW = 0xb
- RTA_CACHEINFO = 0xc
- RTA_TABLE = 0xf
- RTN_UNSPEC = 0x0
- RTN_UNICAST = 0x1
- RTN_LOCAL = 0x2
- RTN_BROADCAST = 0x3
- RTN_ANYCAST = 0x4
- RTN_MULTICAST = 0x5
- RTN_BLACKHOLE = 0x6
- RTN_UNREACHABLE = 0x7
- RTN_PROHIBIT = 0x8
- RTN_THROW = 0x9
- RTN_NAT = 0xa
- RTN_XRESOLVE = 0xb
- SizeofNlMsghdr = 0x10
- SizeofNlMsgerr = 0x14
- SizeofRtGenmsg = 0x1
- SizeofNlAttr = 0x4
- SizeofRtAttr = 0x4
- SizeofIfInfomsg = 0x10
- SizeofIfAddrmsg = 0x8
- SizeofRtMsg = 0xc
- SizeofRtNexthop = 0x8
+ IFA_UNSPEC = 0x0
+ IFA_ADDRESS = 0x1
+ IFA_LOCAL = 0x2
+ IFA_LABEL = 0x3
+ IFA_BROADCAST = 0x4
+ IFA_ANYCAST = 0x5
+ IFA_CACHEINFO = 0x6
+ IFA_MULTICAST = 0x7
+ IFLA_UNSPEC = 0x0
+ IFLA_ADDRESS = 0x1
+ IFLA_BROADCAST = 0x2
+ IFLA_IFNAME = 0x3
+ IFLA_MTU = 0x4
+ IFLA_LINK = 0x5
+ IFLA_QDISC = 0x6
+ IFLA_STATS = 0x7
+ IFLA_COST = 0x8
+ IFLA_PRIORITY = 0x9
+ IFLA_MASTER = 0xa
+ IFLA_WIRELESS = 0xb
+ IFLA_PROTINFO = 0xc
+ IFLA_TXQLEN = 0xd
+ IFLA_MAP = 0xe
+ IFLA_WEIGHT = 0xf
+ IFLA_OPERSTATE = 0x10
+ IFLA_LINKMODE = 0x11
+ IFLA_LINKINFO = 0x12
+ IFLA_NET_NS_PID = 0x13
+ IFLA_IFALIAS = 0x14
+ IFLA_MAX = 0x1d
+ RT_SCOPE_UNIVERSE = 0x0
+ RT_SCOPE_SITE = 0xc8
+ RT_SCOPE_LINK = 0xfd
+ RT_SCOPE_HOST = 0xfe
+ RT_SCOPE_NOWHERE = 0xff
+ RT_TABLE_UNSPEC = 0x0
+ RT_TABLE_COMPAT = 0xfc
+ RT_TABLE_DEFAULT = 0xfd
+ RT_TABLE_MAIN = 0xfe
+ RT_TABLE_LOCAL = 0xff
+ RT_TABLE_MAX = 0xffffffff
+ RTA_UNSPEC = 0x0
+ RTA_DST = 0x1
+ RTA_SRC = 0x2
+ RTA_IIF = 0x3
+ RTA_OIF = 0x4
+ RTA_GATEWAY = 0x5
+ RTA_PRIORITY = 0x6
+ RTA_PREFSRC = 0x7
+ RTA_METRICS = 0x8
+ RTA_MULTIPATH = 0x9
+ RTA_FLOW = 0xb
+ RTA_CACHEINFO = 0xc
+ RTA_TABLE = 0xf
+ RTN_UNSPEC = 0x0
+ RTN_UNICAST = 0x1
+ RTN_LOCAL = 0x2
+ RTN_BROADCAST = 0x3
+ RTN_ANYCAST = 0x4
+ RTN_MULTICAST = 0x5
+ RTN_BLACKHOLE = 0x6
+ RTN_UNREACHABLE = 0x7
+ RTN_PROHIBIT = 0x8
+ RTN_THROW = 0x9
+ RTN_NAT = 0xa
+ RTN_XRESOLVE = 0xb
+ RTNLGRP_NONE = 0x0
+ RTNLGRP_LINK = 0x1
+ RTNLGRP_NOTIFY = 0x2
+ RTNLGRP_NEIGH = 0x3
+ RTNLGRP_TC = 0x4
+ RTNLGRP_IPV4_IFADDR = 0x5
+ RTNLGRP_IPV4_MROUTE = 0x6
+ RTNLGRP_IPV4_ROUTE = 0x7
+ RTNLGRP_IPV4_RULE = 0x8
+ RTNLGRP_IPV6_IFADDR = 0x9
+ RTNLGRP_IPV6_MROUTE = 0xa
+ RTNLGRP_IPV6_ROUTE = 0xb
+ RTNLGRP_IPV6_IFINFO = 0xc
+ RTNLGRP_IPV6_PREFIX = 0x12
+ RTNLGRP_IPV6_RULE = 0x13
+ RTNLGRP_ND_USEROPT = 0x14
+ SizeofNlMsghdr = 0x10
+ SizeofNlMsgerr = 0x14
+ SizeofRtGenmsg = 0x1
+ SizeofNlAttr = 0x4
+ SizeofRtAttr = 0x4
+ SizeofIfInfomsg = 0x10
+ SizeofIfAddrmsg = 0x8
+ SizeofRtMsg = 0xc
+ SizeofRtNexthop = 0x8
)
type NlMsghdr struct {
@@ -433,7 +484,7 @@ type InotifyEvent struct {
Mask uint32
Cookie uint32
Len uint32
- Name [0]byte
+ Name [0]uint8
}
const SizeofInotifyEvent = 0x10
@@ -501,6 +552,10 @@ type EpollEvent struct {
Pad int32
}
+const (
+ _AT_FDCWD = -0x64
+)
+
type Termios struct {
Iflag uint32
Oflag uint32
diff --git a/src/pkg/syscall/ztypes_linux_amd64.go b/src/pkg/syscall/ztypes_linux_amd64.go
index d5583a7c2..5800c3c6c 100644
--- a/src/pkg/syscall/ztypes_linux_amd64.go
+++ b/src/pkg/syscall/ztypes_linux_amd64.go
@@ -233,7 +233,7 @@ type Cmsghdr struct {
Len uint64
Level int32
Type int32
- X__cmsg_data [0]byte
+ X__cmsg_data [0]uint8
}
type Inet4Pktinfo struct {
@@ -253,6 +253,40 @@ type Ucred struct {
Gid uint32
}
+type TCPInfo struct {
+ State uint8
+ Ca_state uint8
+ Retransmits uint8
+ Probes uint8
+ Backoff uint8
+ Options uint8
+ Pad_cgo_0 [2]byte
+ Rto uint32
+ Ato uint32
+ Snd_mss uint32
+ Rcv_mss uint32
+ Unacked uint32
+ Sacked uint32
+ Lost uint32
+ Retrans uint32
+ Fackets uint32
+ Last_data_sent uint32
+ Last_ack_sent uint32
+ Last_data_recv uint32
+ Last_ack_recv uint32
+ Pmtu uint32
+ Rcv_ssthresh uint32
+ Rtt uint32
+ Rttvar uint32
+ Snd_ssthresh uint32
+ Snd_cwnd uint32
+ Advmss uint32
+ Reordering uint32
+ Rcv_rtt uint32
+ Rcv_space uint32
+ Total_retrans uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -269,84 +303,101 @@ const (
SizeofInet4Pktinfo = 0xc
SizeofInet6Pktinfo = 0x14
SizeofUcred = 0xc
+ SizeofTCPInfo = 0x68
)
const (
- IFA_UNSPEC = 0x0
- IFA_ADDRESS = 0x1
- IFA_LOCAL = 0x2
- IFA_LABEL = 0x3
- IFA_BROADCAST = 0x4
- IFA_ANYCAST = 0x5
- IFA_CACHEINFO = 0x6
- IFA_MULTICAST = 0x7
- IFLA_UNSPEC = 0x0
- IFLA_ADDRESS = 0x1
- IFLA_BROADCAST = 0x2
- IFLA_IFNAME = 0x3
- IFLA_MTU = 0x4
- IFLA_LINK = 0x5
- IFLA_QDISC = 0x6
- IFLA_STATS = 0x7
- IFLA_COST = 0x8
- IFLA_PRIORITY = 0x9
- IFLA_MASTER = 0xa
- IFLA_WIRELESS = 0xb
- IFLA_PROTINFO = 0xc
- IFLA_TXQLEN = 0xd
- IFLA_MAP = 0xe
- IFLA_WEIGHT = 0xf
- IFLA_OPERSTATE = 0x10
- IFLA_LINKMODE = 0x11
- IFLA_LINKINFO = 0x12
- IFLA_NET_NS_PID = 0x13
- IFLA_IFALIAS = 0x14
- IFLA_MAX = 0x1c
- RT_SCOPE_UNIVERSE = 0x0
- RT_SCOPE_SITE = 0xc8
- RT_SCOPE_LINK = 0xfd
- RT_SCOPE_HOST = 0xfe
- RT_SCOPE_NOWHERE = 0xff
- RT_TABLE_UNSPEC = 0x0
- RT_TABLE_COMPAT = 0xfc
- RT_TABLE_DEFAULT = 0xfd
- RT_TABLE_MAIN = 0xfe
- RT_TABLE_LOCAL = 0xff
- RT_TABLE_MAX = 0xffffffff
- RTA_UNSPEC = 0x0
- RTA_DST = 0x1
- RTA_SRC = 0x2
- RTA_IIF = 0x3
- RTA_OIF = 0x4
- RTA_GATEWAY = 0x5
- RTA_PRIORITY = 0x6
- RTA_PREFSRC = 0x7
- RTA_METRICS = 0x8
- RTA_MULTIPATH = 0x9
- RTA_FLOW = 0xb
- RTA_CACHEINFO = 0xc
- RTA_TABLE = 0xf
- RTN_UNSPEC = 0x0
- RTN_UNICAST = 0x1
- RTN_LOCAL = 0x2
- RTN_BROADCAST = 0x3
- RTN_ANYCAST = 0x4
- RTN_MULTICAST = 0x5
- RTN_BLACKHOLE = 0x6
- RTN_UNREACHABLE = 0x7
- RTN_PROHIBIT = 0x8
- RTN_THROW = 0x9
- RTN_NAT = 0xa
- RTN_XRESOLVE = 0xb
- SizeofNlMsghdr = 0x10
- SizeofNlMsgerr = 0x14
- SizeofRtGenmsg = 0x1
- SizeofNlAttr = 0x4
- SizeofRtAttr = 0x4
- SizeofIfInfomsg = 0x10
- SizeofIfAddrmsg = 0x8
- SizeofRtMsg = 0xc
- SizeofRtNexthop = 0x8
+ IFA_UNSPEC = 0x0
+ IFA_ADDRESS = 0x1
+ IFA_LOCAL = 0x2
+ IFA_LABEL = 0x3
+ IFA_BROADCAST = 0x4
+ IFA_ANYCAST = 0x5
+ IFA_CACHEINFO = 0x6
+ IFA_MULTICAST = 0x7
+ IFLA_UNSPEC = 0x0
+ IFLA_ADDRESS = 0x1
+ IFLA_BROADCAST = 0x2
+ IFLA_IFNAME = 0x3
+ IFLA_MTU = 0x4
+ IFLA_LINK = 0x5
+ IFLA_QDISC = 0x6
+ IFLA_STATS = 0x7
+ IFLA_COST = 0x8
+ IFLA_PRIORITY = 0x9
+ IFLA_MASTER = 0xa
+ IFLA_WIRELESS = 0xb
+ IFLA_PROTINFO = 0xc
+ IFLA_TXQLEN = 0xd
+ IFLA_MAP = 0xe
+ IFLA_WEIGHT = 0xf
+ IFLA_OPERSTATE = 0x10
+ IFLA_LINKMODE = 0x11
+ IFLA_LINKINFO = 0x12
+ IFLA_NET_NS_PID = 0x13
+ IFLA_IFALIAS = 0x14
+ IFLA_MAX = 0x1d
+ RT_SCOPE_UNIVERSE = 0x0
+ RT_SCOPE_SITE = 0xc8
+ RT_SCOPE_LINK = 0xfd
+ RT_SCOPE_HOST = 0xfe
+ RT_SCOPE_NOWHERE = 0xff
+ RT_TABLE_UNSPEC = 0x0
+ RT_TABLE_COMPAT = 0xfc
+ RT_TABLE_DEFAULT = 0xfd
+ RT_TABLE_MAIN = 0xfe
+ RT_TABLE_LOCAL = 0xff
+ RT_TABLE_MAX = 0xffffffff
+ RTA_UNSPEC = 0x0
+ RTA_DST = 0x1
+ RTA_SRC = 0x2
+ RTA_IIF = 0x3
+ RTA_OIF = 0x4
+ RTA_GATEWAY = 0x5
+ RTA_PRIORITY = 0x6
+ RTA_PREFSRC = 0x7
+ RTA_METRICS = 0x8
+ RTA_MULTIPATH = 0x9
+ RTA_FLOW = 0xb
+ RTA_CACHEINFO = 0xc
+ RTA_TABLE = 0xf
+ RTN_UNSPEC = 0x0
+ RTN_UNICAST = 0x1
+ RTN_LOCAL = 0x2
+ RTN_BROADCAST = 0x3
+ RTN_ANYCAST = 0x4
+ RTN_MULTICAST = 0x5
+ RTN_BLACKHOLE = 0x6
+ RTN_UNREACHABLE = 0x7
+ RTN_PROHIBIT = 0x8
+ RTN_THROW = 0x9
+ RTN_NAT = 0xa
+ RTN_XRESOLVE = 0xb
+ RTNLGRP_NONE = 0x0
+ RTNLGRP_LINK = 0x1
+ RTNLGRP_NOTIFY = 0x2
+ RTNLGRP_NEIGH = 0x3
+ RTNLGRP_TC = 0x4
+ RTNLGRP_IPV4_IFADDR = 0x5
+ RTNLGRP_IPV4_MROUTE = 0x6
+ RTNLGRP_IPV4_ROUTE = 0x7
+ RTNLGRP_IPV4_RULE = 0x8
+ RTNLGRP_IPV6_IFADDR = 0x9
+ RTNLGRP_IPV6_MROUTE = 0xa
+ RTNLGRP_IPV6_ROUTE = 0xb
+ RTNLGRP_IPV6_IFINFO = 0xc
+ RTNLGRP_IPV6_PREFIX = 0x12
+ RTNLGRP_IPV6_RULE = 0x13
+ RTNLGRP_ND_USEROPT = 0x14
+ SizeofNlMsghdr = 0x10
+ SizeofNlMsgerr = 0x14
+ SizeofRtGenmsg = 0x1
+ SizeofNlAttr = 0x4
+ SizeofRtAttr = 0x4
+ SizeofIfInfomsg = 0x10
+ SizeofIfAddrmsg = 0x8
+ SizeofRtMsg = 0xc
+ SizeofRtNexthop = 0x8
)
type NlMsghdr struct {
@@ -435,7 +486,7 @@ type InotifyEvent struct {
Mask uint32
Cookie uint32
Len uint32
- Name [0]byte
+ Name [0]uint8
}
const SizeofInotifyEvent = 0x10
@@ -517,6 +568,10 @@ type EpollEvent struct {
Pad int32
}
+const (
+ _AT_FDCWD = -0x64
+)
+
type Termios struct {
Iflag uint32
Oflag uint32
diff --git a/src/pkg/syscall/ztypes_linux_arm.go b/src/pkg/syscall/ztypes_linux_arm.go
index cd680c3ad..4a81d340c 100644
--- a/src/pkg/syscall/ztypes_linux_arm.go
+++ b/src/pkg/syscall/ztypes_linux_arm.go
@@ -253,6 +253,40 @@ type Ucred struct {
Gid uint32
}
+type TCPInfo struct {
+ State uint8
+ Ca_state uint8
+ Retransmits uint8
+ Probes uint8
+ Backoff uint8
+ Options uint8
+ Pad_cgo_0 [2]byte
+ Rto uint32
+ Ato uint32
+ Snd_mss uint32
+ Rcv_mss uint32
+ Unacked uint32
+ Sacked uint32
+ Lost uint32
+ Retrans uint32
+ Fackets uint32
+ Last_data_sent uint32
+ Last_ack_sent uint32
+ Last_data_recv uint32
+ Last_ack_recv uint32
+ Pmtu uint32
+ Rcv_ssthresh uint32
+ Rtt uint32
+ Rttvar uint32
+ Snd_ssthresh uint32
+ Snd_cwnd uint32
+ Advmss uint32
+ Reordering uint32
+ Rcv_rtt uint32
+ Rcv_space uint32
+ Total_retrans uint32
+}
+
const (
SizeofSockaddrInet4 = 0x10
SizeofSockaddrInet6 = 0x1c
@@ -269,84 +303,101 @@ const (
SizeofInet4Pktinfo = 0xc
SizeofInet6Pktinfo = 0x14
SizeofUcred = 0xc
+ SizeofTCPInfo = 0x68
)
const (
- IFA_UNSPEC = 0x0
- IFA_ADDRESS = 0x1
- IFA_LOCAL = 0x2
- IFA_LABEL = 0x3
- IFA_BROADCAST = 0x4
- IFA_ANYCAST = 0x5
- IFA_CACHEINFO = 0x6
- IFA_MULTICAST = 0x7
- IFLA_UNSPEC = 0x0
- IFLA_ADDRESS = 0x1
- IFLA_BROADCAST = 0x2
- IFLA_IFNAME = 0x3
- IFLA_MTU = 0x4
- IFLA_LINK = 0x5
- IFLA_QDISC = 0x6
- IFLA_STATS = 0x7
- IFLA_COST = 0x8
- IFLA_PRIORITY = 0x9
- IFLA_MASTER = 0xa
- IFLA_WIRELESS = 0xb
- IFLA_PROTINFO = 0xc
- IFLA_TXQLEN = 0xd
- IFLA_MAP = 0xe
- IFLA_WEIGHT = 0xf
- IFLA_OPERSTATE = 0x10
- IFLA_LINKMODE = 0x11
- IFLA_LINKINFO = 0x12
- IFLA_NET_NS_PID = 0x13
- IFLA_IFALIAS = 0x14
- IFLA_MAX = 0x1c
- RT_SCOPE_UNIVERSE = 0x0
- RT_SCOPE_SITE = 0xc8
- RT_SCOPE_LINK = 0xfd
- RT_SCOPE_HOST = 0xfe
- RT_SCOPE_NOWHERE = 0xff
- RT_TABLE_UNSPEC = 0x0
- RT_TABLE_COMPAT = 0xfc
- RT_TABLE_DEFAULT = 0xfd
- RT_TABLE_MAIN = 0xfe
- RT_TABLE_LOCAL = 0xff
- RT_TABLE_MAX = 0xffffffff
- RTA_UNSPEC = 0x0
- RTA_DST = 0x1
- RTA_SRC = 0x2
- RTA_IIF = 0x3
- RTA_OIF = 0x4
- RTA_GATEWAY = 0x5
- RTA_PRIORITY = 0x6
- RTA_PREFSRC = 0x7
- RTA_METRICS = 0x8
- RTA_MULTIPATH = 0x9
- RTA_FLOW = 0xb
- RTA_CACHEINFO = 0xc
- RTA_TABLE = 0xf
- RTN_UNSPEC = 0x0
- RTN_UNICAST = 0x1
- RTN_LOCAL = 0x2
- RTN_BROADCAST = 0x3
- RTN_ANYCAST = 0x4
- RTN_MULTICAST = 0x5
- RTN_BLACKHOLE = 0x6
- RTN_UNREACHABLE = 0x7
- RTN_PROHIBIT = 0x8
- RTN_THROW = 0x9
- RTN_NAT = 0xa
- RTN_XRESOLVE = 0xb
- SizeofNlMsghdr = 0x10
- SizeofNlMsgerr = 0x14
- SizeofRtGenmsg = 0x1
- SizeofNlAttr = 0x4
- SizeofRtAttr = 0x4
- SizeofIfInfomsg = 0x10
- SizeofIfAddrmsg = 0x8
- SizeofRtMsg = 0xc
- SizeofRtNexthop = 0x8
+ IFA_UNSPEC = 0x0
+ IFA_ADDRESS = 0x1
+ IFA_LOCAL = 0x2
+ IFA_LABEL = 0x3
+ IFA_BROADCAST = 0x4
+ IFA_ANYCAST = 0x5
+ IFA_CACHEINFO = 0x6
+ IFA_MULTICAST = 0x7
+ IFLA_UNSPEC = 0x0
+ IFLA_ADDRESS = 0x1
+ IFLA_BROADCAST = 0x2
+ IFLA_IFNAME = 0x3
+ IFLA_MTU = 0x4
+ IFLA_LINK = 0x5
+ IFLA_QDISC = 0x6
+ IFLA_STATS = 0x7
+ IFLA_COST = 0x8
+ IFLA_PRIORITY = 0x9
+ IFLA_MASTER = 0xa
+ IFLA_WIRELESS = 0xb
+ IFLA_PROTINFO = 0xc
+ IFLA_TXQLEN = 0xd
+ IFLA_MAP = 0xe
+ IFLA_WEIGHT = 0xf
+ IFLA_OPERSTATE = 0x10
+ IFLA_LINKMODE = 0x11
+ IFLA_LINKINFO = 0x12
+ IFLA_NET_NS_PID = 0x13
+ IFLA_IFALIAS = 0x14
+ IFLA_MAX = 0x1d
+ RT_SCOPE_UNIVERSE = 0x0
+ RT_SCOPE_SITE = 0xc8
+ RT_SCOPE_LINK = 0xfd
+ RT_SCOPE_HOST = 0xfe
+ RT_SCOPE_NOWHERE = 0xff
+ RT_TABLE_UNSPEC = 0x0
+ RT_TABLE_COMPAT = 0xfc
+ RT_TABLE_DEFAULT = 0xfd
+ RT_TABLE_MAIN = 0xfe
+ RT_TABLE_LOCAL = 0xff
+ RT_TABLE_MAX = 0xffffffff
+ RTA_UNSPEC = 0x0
+ RTA_DST = 0x1
+ RTA_SRC = 0x2
+ RTA_IIF = 0x3
+ RTA_OIF = 0x4
+ RTA_GATEWAY = 0x5
+ RTA_PRIORITY = 0x6
+ RTA_PREFSRC = 0x7
+ RTA_METRICS = 0x8
+ RTA_MULTIPATH = 0x9
+ RTA_FLOW = 0xb
+ RTA_CACHEINFO = 0xc
+ RTA_TABLE = 0xf
+ RTN_UNSPEC = 0x0
+ RTN_UNICAST = 0x1
+ RTN_LOCAL = 0x2
+ RTN_BROADCAST = 0x3
+ RTN_ANYCAST = 0x4
+ RTN_MULTICAST = 0x5
+ RTN_BLACKHOLE = 0x6
+ RTN_UNREACHABLE = 0x7
+ RTN_PROHIBIT = 0x8
+ RTN_THROW = 0x9
+ RTN_NAT = 0xa
+ RTN_XRESOLVE = 0xb
+ RTNLGRP_NONE = 0x0
+ RTNLGRP_LINK = 0x1
+ RTNLGRP_NOTIFY = 0x2
+ RTNLGRP_NEIGH = 0x3
+ RTNLGRP_TC = 0x4
+ RTNLGRP_IPV4_IFADDR = 0x5
+ RTNLGRP_IPV4_MROUTE = 0x6
+ RTNLGRP_IPV4_ROUTE = 0x7
+ RTNLGRP_IPV4_RULE = 0x8
+ RTNLGRP_IPV6_IFADDR = 0x9
+ RTNLGRP_IPV6_MROUTE = 0xa
+ RTNLGRP_IPV6_ROUTE = 0xb
+ RTNLGRP_IPV6_IFINFO = 0xc
+ RTNLGRP_IPV6_PREFIX = 0x12
+ RTNLGRP_IPV6_RULE = 0x13
+ RTNLGRP_ND_USEROPT = 0x14
+ SizeofNlMsghdr = 0x10
+ SizeofNlMsgerr = 0x14
+ SizeofRtGenmsg = 0x1
+ SizeofNlAttr = 0x4
+ SizeofRtAttr = 0x4
+ SizeofIfInfomsg = 0x10
+ SizeofIfAddrmsg = 0x8
+ SizeofRtMsg = 0xc
+ SizeofRtNexthop = 0x8
)
type NlMsghdr struct {
@@ -488,6 +539,10 @@ type EpollEvent struct {
Pad int32
}
+const (
+ _AT_FDCWD = -0x64
+)
+
type Termios struct {
Iflag uint32
Oflag uint32
diff --git a/src/pkg/syscall/ztypes_netbsd_386.go b/src/pkg/syscall/ztypes_netbsd_386.go
index ef9f33aeb..dd9cf221a 100644
--- a/src/pkg/syscall/ztypes_netbsd_386.go
+++ b/src/pkg/syscall/ztypes_netbsd_386.go
@@ -19,12 +19,12 @@ type (
)
type Timespec struct {
- Sec int32
+ Sec int64
Nsec int32
}
type Timeval struct {
- Sec int32
+ Sec int64
Usec int32
}
@@ -54,69 +54,27 @@ type Rlimit struct {
type _Gid_t uint32
-const (
- S_IFMT = 0xf000
- S_IFIFO = 0x1000
- S_IFCHR = 0x2000
- S_IFDIR = 0x4000
- S_IFBLK = 0x6000
- S_IFREG = 0x8000
- S_IFLNK = 0xa000
- S_IFSOCK = 0xc000
- S_ISUID = 0x800
- S_ISGID = 0x400
- S_ISVTX = 0x200
- S_IRUSR = 0x100
- S_IWUSR = 0x80
- S_IXUSR = 0x40
-)
-
type Stat_t struct {
- Dev int32
- Ino uint32
- Mode uint32
- Nlink uint32
- Uid uint32
- Gid uint32
- Rdev int32
- Lspare0 int32
- Atim Timespec
- Mtim Timespec
- Ctim Timespec
- Size int64
- Blocks int64
- Blksize uint32
- Flags uint32
- Gen uint32
- Lspare1 int32
- X__st_birthtim Timespec
- Qspare [2]int64
-}
-
-type Statfs_t struct {
- F_flags uint32
- F_bsize uint32
- F_iosize uint32
- F_blocks uint64
- F_bfree uint64
- F_bavail int64
- F_files uint64
- F_ffree uint64
- F_favail int64
- F_syncwrites uint64
- F_syncreads uint64
- F_asyncwrites uint64
- F_asyncreads uint64
- F_fsid Fsid
- F_namemax uint32
- F_owner uint32
- F_ctime uint32
- F_spare [3]uint32
- F_fstypename [16]int8
- F_mntonname [90]int8
- F_mntfromname [90]int8
- Mount_info [160]byte
-}
+ Dev uint64
+ Mode uint32
+ Ino uint64
+ Nlink uint32
+ Uid uint32
+ Gid uint32
+ Rdev uint64
+ Atimespec Timespec
+ Mtimespec Timespec
+ Ctimespec Timespec
+ Birthtimespec Timespec
+ Size int64
+ Blocks int64
+ Blksize uint32
+ Flags uint32
+ Gen uint32
+ Spare [2]uint32
+}
+
+type Statfs_t [0]byte
type Flock_t struct {
Start int64
@@ -127,15 +85,16 @@ type Flock_t struct {
}
type Dirent struct {
- Fileno uint64
- Reclen uint16
- Namlen uint16
- Type uint8
- Name [512]int8
+ Fileno uint64
+ Reclen uint16
+ Namlen uint16
+ Type uint8
+ Name [512]int8
+ Pad_cgo_0 [3]byte
}
type Fsid struct {
- Val [2]int32
+ X__fsid_val [2]int32
}
type RawSockaddrInet4 struct {
@@ -169,7 +128,7 @@ type RawSockaddrDatalink struct {
Nlen uint8
Alen uint8
Slen uint8
- Data [24]int8
+ Data [12]int8
}
type RawSockaddr struct {
@@ -209,7 +168,7 @@ type Msghdr struct {
Name *byte
Namelen uint32
Iov *Iovec
- Iovlen uint32
+ Iovlen int32
Control *byte
Controllen uint32
Flags int32
@@ -231,7 +190,7 @@ const (
SizeofSockaddrInet6 = 0x1c
SizeofSockaddrAny = 0x6c
SizeofSockaddrUnix = 0x6a
- SizeofSockaddrDatalink = 0x20
+ SizeofSockaddrDatalink = 0x14
SizeofLinger = 0x8
SizeofIPMreq = 0x8
SizeofIPv6Mreq = 0x14
@@ -248,123 +207,116 @@ const (
type Kevent_t struct {
Ident uint32
- Filter int16
- Flags uint16
+ Filter uint32
+ Flags uint32
Fflags uint32
- Data int32
- Udata *byte
+ Data int64
+ Udata int32
}
type FdSet struct {
- Bits [32]int32
+ Bits [8]uint32
}
const (
- SizeofIfMsghdr = 0xe8
- SizeofIfData = 0xd0
- SizeofIfaMsghdr = 0x18
- SizeofRtMsghdr = 0x58
- SizeofRtMetrics = 0x30
+ SizeofIfMsghdr = 0x98
+ SizeofIfData = 0x84
+ SizeofIfaMsghdr = 0x18
+ SizeofIfAnnounceMsghdr = 0x18
+ SizeofRtMsghdr = 0x78
+ SizeofRtMetrics = 0x50
)
type IfMsghdr struct {
- Msglen uint16
- Version uint8
- Type uint8
- Hdrlen uint16
- Index uint16
- Tableid uint16
- Pad1 uint8
- Pad2 uint8
- Addrs int32
- Flags int32
- Xflags int32
- Data IfData
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Data IfData
+ Pad_cgo_1 [4]byte
}
type IfData struct {
- Type uint8
- Addrlen uint8
- Hdrlen uint8
- Link_state uint8
- Mtu uint32
- Metric uint32
- Pad uint32
- Baudrate uint64
- Ipackets uint64
- Ierrors uint64
- Opackets uint64
- Oerrors uint64
- Collisions uint64
- Ibytes uint64
- Obytes uint64
- Imcasts uint64
- Omcasts uint64
- Iqdrops uint64
- Noproto uint64
- Capabilities uint32
- Lastchange Timeval
- Mclpool [7]Mclpool
+ Type uint8
+ Addrlen uint8
+ Hdrlen uint8
+ Pad_cgo_0 [1]byte
+ Link_state int32
+ Mtu uint64
+ Metric uint64
+ Baudrate uint64
+ Ipackets uint64
+ Ierrors uint64
+ Opackets uint64
+ Oerrors uint64
+ Collisions uint64
+ Ibytes uint64
+ Obytes uint64
+ Imcasts uint64
+ Omcasts uint64
+ Iqdrops uint64
+ Noproto uint64
+ Lastchange Timespec
}
type IfaMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Metric int32
+ Index uint16
+ Pad_cgo_0 [6]byte
+}
+
+type IfAnnounceMsghdr struct {
Msglen uint16
Version uint8
Type uint8
- Hdrlen uint16
Index uint16
- Tableid uint16
- Pad1 uint8
- Pad2 uint8
- Addrs int32
- Flags int32
- Metric int32
+ Name [16]int8
+ What uint16
}
type RtMsghdr struct {
- Msglen uint16
- Version uint8
- Type uint8
- Hdrlen uint16
- Index uint16
- Tableid uint16
- Priority uint8
- Mpls uint8
- Addrs int32
- Flags int32
- Fmask int32
- Pid int32
- Seq int32
- Errno int32
- Inits uint32
- Rmx RtMetrics
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Flags int32
+ Addrs int32
+ Pid int32
+ Seq int32
+ Errno int32
+ Use int32
+ Inits int32
+ Pad_cgo_1 [4]byte
+ Rmx RtMetrics
}
type RtMetrics struct {
- Pksent uint64
- Locks uint32
- Mtu uint32
- Expire uint32
- Refcnt uint32
- Hopcount uint32
- Recvpipe uint32
- Sendpipe uint32
- Ssthresh uint32
- Rtt uint32
- Rttvar uint32
-}
-
-type Mclpool struct {
- Grown uint32
- Alive uint16
- Hwm uint16
- Cwm uint16
- Lwm uint16
+ Locks uint64
+ Mtu uint64
+ Hopcount uint64
+ Recvpipe uint64
+ Sendpipe uint64
+ Ssthresh uint64
+ Rtt uint64
+ Rttvar uint64
+ Expire int64
+ Pksent int64
}
+type Mclpool [0]byte
+
const (
SizeofBpfVersion = 0x4
- SizeofBpfStat = 0x8
+ SizeofBpfStat = 0x80
SizeofBpfProgram = 0x8
SizeofBpfInsn = 0x8
SizeofBpfHdr = 0x14
@@ -376,8 +328,10 @@ type BpfVersion struct {
}
type BpfStat struct {
- Recv uint32
- Drop uint32
+ Recv uint64
+ Drop uint64
+ Capt uint64
+ Padding [13]uint64
}
type BpfProgram struct {
@@ -401,6 +355,19 @@ type BpfHdr struct {
}
type BpfTimeval struct {
- Sec uint32
- Usec uint32
+ Sec int32
+ Usec int32
+}
+
+type Sysctlnode struct {
+ Flags uint32
+ Num int32
+ Name [32]int8
+ Ver uint32
+ X__rsvd uint32
+ Un [16]byte
+ X_sysctl_size [8]byte
+ X_sysctl_func [8]byte
+ X_sysctl_parent [8]byte
+ X_sysctl_desc [8]byte
}
diff --git a/src/pkg/syscall/ztypes_netbsd_amd64.go b/src/pkg/syscall/ztypes_netbsd_amd64.go
index 3979d54de..89d1ef816 100644
--- a/src/pkg/syscall/ztypes_netbsd_amd64.go
+++ b/src/pkg/syscall/ztypes_netbsd_amd64.go
@@ -19,14 +19,14 @@ type (
)
type Timespec struct {
- Sec int32
- Pad_cgo_0 [4]byte
- Nsec int64
+ Sec int64
+ Nsec int64
}
type Timeval struct {
- Sec int64
- Usec int64
+ Sec int64
+ Usec int32
+ Pad_cgo_0 [4]byte
}
type Rusage struct {
@@ -55,71 +55,30 @@ type Rlimit struct {
type _Gid_t uint32
-const (
- S_IFMT = 0xf000
- S_IFIFO = 0x1000
- S_IFCHR = 0x2000
- S_IFDIR = 0x4000
- S_IFBLK = 0x6000
- S_IFREG = 0x8000
- S_IFLNK = 0xa000
- S_IFSOCK = 0xc000
- S_ISUID = 0x800
- S_ISGID = 0x400
- S_ISVTX = 0x200
- S_IRUSR = 0x100
- S_IWUSR = 0x80
- S_IXUSR = 0x40
-)
-
type Stat_t struct {
- Dev int32
- Ino uint32
- Mode uint32
- Nlink uint32
- Uid uint32
- Gid uint32
- Rdev int32
- Lspare0 int32
- Atim Timespec
- Mtim Timespec
- Ctim Timespec
- Size int64
- Blocks int64
- Blksize uint32
- Flags uint32
- Gen uint32
- Lspare1 int32
- X__st_birthtim Timespec
- Qspare [2]int64
-}
-
-type Statfs_t struct {
- F_flags uint32
- F_bsize uint32
- F_iosize uint32
+ Dev uint64
+ Mode uint32
Pad_cgo_0 [4]byte
- F_blocks uint64
- F_bfree uint64
- F_bavail int64
- F_files uint64
- F_ffree uint64
- F_favail int64
- F_syncwrites uint64
- F_syncreads uint64
- F_asyncwrites uint64
- F_asyncreads uint64
- F_fsid Fsid
- F_namemax uint32
- F_owner uint32
- F_ctime uint32
- F_spare [3]uint32
- F_fstypename [16]int8
- F_mntonname [90]int8
- F_mntfromname [90]int8
+ Ino uint64
+ Nlink uint32
+ Uid uint32
+ Gid uint32
Pad_cgo_1 [4]byte
- Mount_info [160]byte
-}
+ Rdev uint64
+ Atimespec Timespec
+ Mtimespec Timespec
+ Ctimespec Timespec
+ Birthtimespec Timespec
+ Size int64
+ Blocks int64
+ Blksize uint32
+ Flags uint32
+ Gen uint32
+ Spare [2]uint32
+ Pad_cgo_2 [4]byte
+}
+
+type Statfs_t [0]byte
type Flock_t struct {
Start int64
@@ -130,15 +89,16 @@ type Flock_t struct {
}
type Dirent struct {
- Fileno uint64
- Reclen uint16
- Namlen uint16
- Type uint8
- Name [512]int8
+ Fileno uint64
+ Reclen uint16
+ Namlen uint16
+ Type uint8
+ Name [512]int8
+ Pad_cgo_0 [3]byte
}
type Fsid struct {
- Val [2]int32
+ X__fsid_val [2]int32
}
type RawSockaddrInet4 struct {
@@ -172,7 +132,7 @@ type RawSockaddrDatalink struct {
Nlen uint8
Alen uint8
Slen uint8
- Data [24]int8
+ Data [12]int8
}
type RawSockaddr struct {
@@ -213,7 +173,7 @@ type Msghdr struct {
Namelen uint32
Pad_cgo_0 [4]byte
Iov *Iovec
- Iovlen uint32
+ Iovlen int32
Pad_cgo_1 [4]byte
Control *byte
Controllen uint32
@@ -236,7 +196,7 @@ const (
SizeofSockaddrInet6 = 0x1c
SizeofSockaddrAny = 0x6c
SizeofSockaddrUnix = 0x6a
- SizeofSockaddrDatalink = 0x20
+ SizeofSockaddrDatalink = 0x14
SizeofLinger = 0x8
SizeofIPMreq = 0x8
SizeofIPv6Mreq = 0x14
@@ -252,49 +212,47 @@ const (
)
type Kevent_t struct {
- Ident uint64
- Filter int16
- Flags uint16
- Fflags uint32
- Data int32
- Udata *byte
+ Ident uint64
+ Filter uint32
+ Flags uint32
+ Fflags uint32
+ Pad_cgo_0 [4]byte
+ Data int64
+ Udata int64
}
type FdSet struct {
- Bits [32]int32
+ Bits [8]uint32
}
const (
- SizeofIfMsghdr = 0xf0
- SizeofIfData = 0xd8
- SizeofIfaMsghdr = 0x18
- SizeofRtMsghdr = 0x58
- SizeofRtMetrics = 0x30
+ SizeofIfMsghdr = 0x98
+ SizeofIfData = 0x88
+ SizeofIfaMsghdr = 0x18
+ SizeofIfAnnounceMsghdr = 0x18
+ SizeofRtMsghdr = 0x78
+ SizeofRtMetrics = 0x50
)
type IfMsghdr struct {
- Msglen uint16
- Version uint8
- Type uint8
- Hdrlen uint16
- Index uint16
- Tableid uint16
- Pad1 uint8
- Pad2 uint8
- Addrs int32
- Flags int32
- Xflags int32
- Data IfData
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Data IfData
}
type IfData struct {
Type uint8
Addrlen uint8
Hdrlen uint8
- Link_state uint8
- Mtu uint32
- Metric uint32
- Pad uint32
+ Pad_cgo_0 [1]byte
+ Link_state int32
+ Mtu uint64
+ Metric uint64
Baudrate uint64
Ipackets uint64
Ierrors uint64
@@ -307,72 +265,67 @@ type IfData struct {
Omcasts uint64
Iqdrops uint64
Noproto uint64
- Lastchange Timeval
- Mclpool [7]Mclpool
- Pad_cgo_0 [4]byte
+ Lastchange Timespec
}
type IfaMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Metric int32
+ Index uint16
+ Pad_cgo_0 [6]byte
+}
+
+type IfAnnounceMsghdr struct {
Msglen uint16
Version uint8
Type uint8
- Hdrlen uint16
Index uint16
- Tableid uint16
- Pad1 uint8
- Pad2 uint8
- Addrs int32
- Flags int32
- Metric int32
+ Name [16]int8
+ What uint16
}
type RtMsghdr struct {
- Msglen uint16
- Version uint8
- Type uint8
- Hdrlen uint16
- Index uint16
- Tableid uint16
- Priority uint8
- Mpls uint8
- Addrs int32
- Flags int32
- Fmask int32
- Pid int32
- Seq int32
- Errno int32
- Inits uint32
- Rmx RtMetrics
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Flags int32
+ Addrs int32
+ Pid int32
+ Seq int32
+ Errno int32
+ Use int32
+ Inits int32
+ Pad_cgo_1 [4]byte
+ Rmx RtMetrics
}
type RtMetrics struct {
- Pksent uint64
- Locks uint32
- Mtu uint32
- Expire uint32
- Refcnt uint32
- Hopcount uint32
- Recvpipe uint32
- Sendpipe uint32
- Ssthresh uint32
- Rtt uint32
- Rttvar uint32
-}
-
-type Mclpool struct {
- Grown uint32
- Alive uint16
- Hwm uint16
- Cwm uint16
- Lwm uint16
+ Locks uint64
+ Mtu uint64
+ Hopcount uint64
+ Recvpipe uint64
+ Sendpipe uint64
+ Ssthresh uint64
+ Rtt uint64
+ Rttvar uint64
+ Expire int64
+ Pksent int64
}
+type Mclpool [0]byte
+
const (
SizeofBpfVersion = 0x4
- SizeofBpfStat = 0x8
+ SizeofBpfStat = 0x80
SizeofBpfProgram = 0x10
SizeofBpfInsn = 0x8
- SizeofBpfHdr = 0x14
+ SizeofBpfHdr = 0x20
)
type BpfVersion struct {
@@ -381,8 +334,10 @@ type BpfVersion struct {
}
type BpfStat struct {
- Recv uint32
- Drop uint32
+ Recv uint64
+ Drop uint64
+ Capt uint64
+ Padding [13]uint64
}
type BpfProgram struct {
@@ -403,10 +358,23 @@ type BpfHdr struct {
Caplen uint32
Datalen uint32
Hdrlen uint16
- Pad_cgo_0 [2]byte
+ Pad_cgo_0 [6]byte
}
type BpfTimeval struct {
- Sec uint32
- Usec uint32
+ Sec int64
+ Usec int64
+}
+
+type Sysctlnode struct {
+ Flags uint32
+ Num int32
+ Name [32]int8
+ Ver uint32
+ X__rsvd uint32
+ Un [16]byte
+ X_sysctl_size [8]byte
+ X_sysctl_func [8]byte
+ X_sysctl_parent [8]byte
+ X_sysctl_desc [8]byte
}
diff --git a/src/pkg/syscall/ztypes_netbsd_arm.go b/src/pkg/syscall/ztypes_netbsd_arm.go
new file mode 100644
index 000000000..dd9cf221a
--- /dev/null
+++ b/src/pkg/syscall/ztypes_netbsd_arm.go
@@ -0,0 +1,373 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_netbsd.go
+
+package syscall
+
+const (
+ sizeofPtr = 0x4
+ sizeofShort = 0x2
+ sizeofInt = 0x4
+ sizeofLong = 0x4
+ sizeofLongLong = 0x8
+)
+
+type (
+ _C_short int16
+ _C_int int32
+ _C_long int32
+ _C_long_long int64
+)
+
+type Timespec struct {
+ Sec int64
+ Nsec int32
+}
+
+type Timeval struct {
+ Sec int64
+ Usec int32
+}
+
+type Rusage struct {
+ Utime Timeval
+ Stime Timeval
+ Maxrss int32
+ Ixrss int32
+ Idrss int32
+ Isrss int32
+ Minflt int32
+ Majflt int32
+ Nswap int32
+ Inblock int32
+ Oublock int32
+ Msgsnd int32
+ Msgrcv int32
+ Nsignals int32
+ Nvcsw int32
+ Nivcsw int32
+}
+
+type Rlimit struct {
+ Cur uint64
+ Max uint64
+}
+
+type _Gid_t uint32
+
+type Stat_t struct {
+ Dev uint64
+ Mode uint32
+ Ino uint64
+ Nlink uint32
+ Uid uint32
+ Gid uint32
+ Rdev uint64
+ Atimespec Timespec
+ Mtimespec Timespec
+ Ctimespec Timespec
+ Birthtimespec Timespec
+ Size int64
+ Blocks int64
+ Blksize uint32
+ Flags uint32
+ Gen uint32
+ Spare [2]uint32
+}
+
+type Statfs_t [0]byte
+
+type Flock_t struct {
+ Start int64
+ Len int64
+ Pid int32
+ Type int16
+ Whence int16
+}
+
+type Dirent struct {
+ Fileno uint64
+ Reclen uint16
+ Namlen uint16
+ Type uint8
+ Name [512]int8
+ Pad_cgo_0 [3]byte
+}
+
+type Fsid struct {
+ X__fsid_val [2]int32
+}
+
+type RawSockaddrInet4 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Addr [4]byte /* in_addr */
+ Zero [8]int8
+}
+
+type RawSockaddrInet6 struct {
+ Len uint8
+ Family uint8
+ Port uint16
+ Flowinfo uint32
+ Addr [16]byte /* in6_addr */
+ Scope_id uint32
+}
+
+type RawSockaddrUnix struct {
+ Len uint8
+ Family uint8
+ Path [104]int8
+}
+
+type RawSockaddrDatalink struct {
+ Len uint8
+ Family uint8
+ Index uint16
+ Type uint8
+ Nlen uint8
+ Alen uint8
+ Slen uint8
+ Data [12]int8
+}
+
+type RawSockaddr struct {
+ Len uint8
+ Family uint8
+ Data [14]int8
+}
+
+type RawSockaddrAny struct {
+ Addr RawSockaddr
+ Pad [92]int8
+}
+
+type _Socklen uint32
+
+type Linger struct {
+ Onoff int32
+ Linger int32
+}
+
+type Iovec struct {
+ Base *byte
+ Len uint32
+}
+
+type IPMreq struct {
+ Multiaddr [4]byte /* in_addr */
+ Interface [4]byte /* in_addr */
+}
+
+type IPv6Mreq struct {
+ Multiaddr [16]byte /* in6_addr */
+ Interface uint32
+}
+
+type Msghdr struct {
+ Name *byte
+ Namelen uint32
+ Iov *Iovec
+ Iovlen int32
+ Control *byte
+ Controllen uint32
+ Flags int32
+}
+
+type Cmsghdr struct {
+ Len uint32
+ Level int32
+ Type int32
+}
+
+type Inet6Pktinfo struct {
+ Addr [16]byte /* in6_addr */
+ Ifindex uint32
+}
+
+const (
+ SizeofSockaddrInet4 = 0x10
+ SizeofSockaddrInet6 = 0x1c
+ SizeofSockaddrAny = 0x6c
+ SizeofSockaddrUnix = 0x6a
+ SizeofSockaddrDatalink = 0x14
+ SizeofLinger = 0x8
+ SizeofIPMreq = 0x8
+ SizeofIPv6Mreq = 0x14
+ SizeofMsghdr = 0x1c
+ SizeofCmsghdr = 0xc
+ SizeofInet6Pktinfo = 0x14
+)
+
+const (
+ PTRACE_TRACEME = 0x0
+ PTRACE_CONT = 0x7
+ PTRACE_KILL = 0x8
+)
+
+type Kevent_t struct {
+ Ident uint32
+ Filter uint32
+ Flags uint32
+ Fflags uint32
+ Data int64
+ Udata int32
+}
+
+type FdSet struct {
+ Bits [8]uint32
+}
+
+const (
+ SizeofIfMsghdr = 0x98
+ SizeofIfData = 0x84
+ SizeofIfaMsghdr = 0x18
+ SizeofIfAnnounceMsghdr = 0x18
+ SizeofRtMsghdr = 0x78
+ SizeofRtMetrics = 0x50
+)
+
+type IfMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Data IfData
+ Pad_cgo_1 [4]byte
+}
+
+type IfData struct {
+ Type uint8
+ Addrlen uint8
+ Hdrlen uint8
+ Pad_cgo_0 [1]byte
+ Link_state int32
+ Mtu uint64
+ Metric uint64
+ Baudrate uint64
+ Ipackets uint64
+ Ierrors uint64
+ Opackets uint64
+ Oerrors uint64
+ Collisions uint64
+ Ibytes uint64
+ Obytes uint64
+ Imcasts uint64
+ Omcasts uint64
+ Iqdrops uint64
+ Noproto uint64
+ Lastchange Timespec
+}
+
+type IfaMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Addrs int32
+ Flags int32
+ Metric int32
+ Index uint16
+ Pad_cgo_0 [6]byte
+}
+
+type IfAnnounceMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Index uint16
+ Name [16]int8
+ What uint16
+}
+
+type RtMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Index uint16
+ Pad_cgo_0 [2]byte
+ Flags int32
+ Addrs int32
+ Pid int32
+ Seq int32
+ Errno int32
+ Use int32
+ Inits int32
+ Pad_cgo_1 [4]byte
+ Rmx RtMetrics
+}
+
+type RtMetrics struct {
+ Locks uint64
+ Mtu uint64
+ Hopcount uint64
+ Recvpipe uint64
+ Sendpipe uint64
+ Ssthresh uint64
+ Rtt uint64
+ Rttvar uint64
+ Expire int64
+ Pksent int64
+}
+
+type Mclpool [0]byte
+
+const (
+ SizeofBpfVersion = 0x4
+ SizeofBpfStat = 0x80
+ SizeofBpfProgram = 0x8
+ SizeofBpfInsn = 0x8
+ SizeofBpfHdr = 0x14
+)
+
+type BpfVersion struct {
+ Major uint16
+ Minor uint16
+}
+
+type BpfStat struct {
+ Recv uint64
+ Drop uint64
+ Capt uint64
+ Padding [13]uint64
+}
+
+type BpfProgram struct {
+ Len uint32
+ Insns *BpfInsn
+}
+
+type BpfInsn struct {
+ Code uint16
+ Jt uint8
+ Jf uint8
+ K uint32
+}
+
+type BpfHdr struct {
+ Tstamp BpfTimeval
+ Caplen uint32
+ Datalen uint32
+ Hdrlen uint16
+ Pad_cgo_0 [2]byte
+}
+
+type BpfTimeval struct {
+ Sec int32
+ Usec int32
+}
+
+type Sysctlnode struct {
+ Flags uint32
+ Num int32
+ Name [32]int8
+ Ver uint32
+ X__rsvd uint32
+ Un [16]byte
+ X_sysctl_size [8]byte
+ X_sysctl_func [8]byte
+ X_sysctl_parent [8]byte
+ X_sysctl_desc [8]byte
+}
diff --git a/src/pkg/syscall/ztypes_openbsd_386.go b/src/pkg/syscall/ztypes_openbsd_386.go
index 6c21e39b0..528e92c85 100644
--- a/src/pkg/syscall/ztypes_openbsd_386.go
+++ b/src/pkg/syscall/ztypes_openbsd_386.go
@@ -260,11 +260,12 @@ type FdSet struct {
}
const (
- SizeofIfMsghdr = 0xe4
- SizeofIfData = 0xcc
- SizeofIfaMsghdr = 0x18
- SizeofRtMsghdr = 0x58
- SizeofRtMetrics = 0x30
+ SizeofIfMsghdr = 0xe8
+ SizeofIfData = 0xd0
+ SizeofIfaMsghdr = 0x18
+ SizeofIfAnnounceMsghdr = 0x1a
+ SizeofRtMsghdr = 0x58
+ SizeofRtMetrics = 0x30
)
type IfMsghdr struct {
@@ -283,27 +284,28 @@ type IfMsghdr struct {
}
type IfData struct {
- Type uint8
- Addrlen uint8
- Hdrlen uint8
- Link_state uint8
- Mtu uint32
- Metric uint32
- Pad uint32
- Baudrate uint64
- Ipackets uint64
- Ierrors uint64
- Opackets uint64
- Oerrors uint64
- Collisions uint64
- Ibytes uint64
- Obytes uint64
- Imcasts uint64
- Omcasts uint64
- Iqdrops uint64
- Noproto uint64
- Lastchange Timeval
- Mclpool [7]Mclpool
+ Type uint8
+ Addrlen uint8
+ Hdrlen uint8
+ Link_state uint8
+ Mtu uint32
+ Metric uint32
+ Pad uint32
+ Baudrate uint64
+ Ipackets uint64
+ Ierrors uint64
+ Opackets uint64
+ Oerrors uint64
+ Collisions uint64
+ Ibytes uint64
+ Obytes uint64
+ Imcasts uint64
+ Omcasts uint64
+ Iqdrops uint64
+ Noproto uint64
+ Capabilities uint32
+ Lastchange Timeval
+ Mclpool [7]Mclpool
}
type IfaMsghdr struct {
@@ -320,6 +322,16 @@ type IfaMsghdr struct {
Metric int32
}
+type IfAnnounceMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Hdrlen uint16
+ Index uint16
+ What uint16
+ Name [16]int8
+}
+
type RtMsghdr struct {
Msglen uint16
Version uint8
diff --git a/src/pkg/syscall/ztypes_openbsd_amd64.go b/src/pkg/syscall/ztypes_openbsd_amd64.go
index 4d01578fc..4b9393054 100644
--- a/src/pkg/syscall/ztypes_openbsd_amd64.go
+++ b/src/pkg/syscall/ztypes_openbsd_amd64.go
@@ -265,11 +265,12 @@ type FdSet struct {
}
const (
- SizeofIfMsghdr = 0xf0
- SizeofIfData = 0xd8
- SizeofIfaMsghdr = 0x18
- SizeofRtMsghdr = 0x58
- SizeofRtMetrics = 0x30
+ SizeofIfMsghdr = 0xf8
+ SizeofIfData = 0xe0
+ SizeofIfaMsghdr = 0x18
+ SizeofIfAnnounceMsghdr = 0x1a
+ SizeofRtMsghdr = 0x58
+ SizeofRtMetrics = 0x30
)
type IfMsghdr struct {
@@ -288,28 +289,30 @@ type IfMsghdr struct {
}
type IfData struct {
- Type uint8
- Addrlen uint8
- Hdrlen uint8
- Link_state uint8
- Mtu uint32
- Metric uint32
- Pad uint32
- Baudrate uint64
- Ipackets uint64
- Ierrors uint64
- Opackets uint64
- Oerrors uint64
- Collisions uint64
- Ibytes uint64
- Obytes uint64
- Imcasts uint64
- Omcasts uint64
- Iqdrops uint64
- Noproto uint64
- Lastchange Timeval
- Mclpool [7]Mclpool
- Pad_cgo_0 [4]byte
+ Type uint8
+ Addrlen uint8
+ Hdrlen uint8
+ Link_state uint8
+ Mtu uint32
+ Metric uint32
+ Pad uint32
+ Baudrate uint64
+ Ipackets uint64
+ Ierrors uint64
+ Opackets uint64
+ Oerrors uint64
+ Collisions uint64
+ Ibytes uint64
+ Obytes uint64
+ Imcasts uint64
+ Omcasts uint64
+ Iqdrops uint64
+ Noproto uint64
+ Capabilities uint32
+ Pad_cgo_0 [4]byte
+ Lastchange Timeval
+ Mclpool [7]Mclpool
+ Pad_cgo_1 [4]byte
}
type IfaMsghdr struct {
@@ -326,6 +329,16 @@ type IfaMsghdr struct {
Metric int32
}
+type IfAnnounceMsghdr struct {
+ Msglen uint16
+ Version uint8
+ Type uint8
+ Hdrlen uint16
+ Index uint16
+ What uint16
+ Name [16]int8
+}
+
type RtMsghdr struct {
Msglen uint16
Version uint8
diff --git a/src/pkg/syscall/ztypes_plan9_amd64.go b/src/pkg/syscall/ztypes_plan9_amd64.go
new file mode 100644
index 000000000..3e3a8d1f3
--- /dev/null
+++ b/src/pkg/syscall/ztypes_plan9_amd64.go
@@ -0,0 +1,75 @@
+// godefs -gsyscall -f -m32 types_plan9.c
+
+// MACHINE GENERATED - DO NOT EDIT.
+
+package syscall
+
+// Constants
+const (
+ O_RDONLY = 0
+ O_WRONLY = 0x1
+ O_RDWR = 0x2
+ O_TRUNC = 0x10
+ O_CLOEXEC = 0x20
+ O_EXCL = 0x1000
+ STATMAX = 0xffff
+ ERRMAX = 0x80
+ MORDER = 0x3
+ MREPL = 0
+ MBEFORE = 0x1
+ MAFTER = 0x2
+ MCREATE = 0x4
+ MCACHE = 0x10
+ MMASK = 0x17
+ RFNAMEG = 0x1
+ RFENVG = 0x2
+ RFFDG = 0x4
+ RFNOTEG = 0x8
+ RFPROC = 0x10
+ RFMEM = 0x20
+ RFNOWAIT = 0x40
+ RFCNAMEG = 0x400
+ RFCENVG = 0x800
+ RFCFDG = 0x1000
+ RFREND = 0x2000
+ RFNOMNT = 0x4000
+ QTDIR = 0x80
+ QTAPPEND = 0x40
+ QTEXCL = 0x20
+ QTMOUNT = 0x10
+ QTAUTH = 0x8
+ QTTMP = 0x4
+ QTFILE = 0
+ DMDIR = 0x80000000
+ DMAPPEND = 0x40000000
+ DMEXCL = 0x20000000
+ DMMOUNT = 0x10000000
+ DMAUTH = 0x8000000
+ DMTMP = 0x4000000
+ DMREAD = 0x4
+ DMWRITE = 0x2
+ DMEXEC = 0x1
+ STATFIXLEN = 0x31
+)
+
+// Types
+
+type _C_int int32
+
+type Prof struct {
+ Pp *[0]byte /* sPlink */
+ Next *[0]byte /* sPlink */
+ Last *[0]byte /* sPlink */
+ First *[0]byte /* sPlink */
+ Pid uint32
+ What uint32
+}
+
+type Tos struct {
+ Prof Prof
+ Cyclefreq uint64
+ Kcycles int64
+ Pcycles int64
+ Pid uint32
+ Clock uint32
+}
diff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go
index b2dc12e1a..a2006f803 100644
--- a/src/pkg/syscall/ztypes_windows.go
+++ b/src/pkg/syscall/ztypes_windows.go
@@ -20,6 +20,7 @@ const (
ERROR_ENVVAR_NOT_FOUND Errno = 203
ERROR_OPERATION_ABORTED Errno = 995
ERROR_IO_PENDING Errno = 997
+ ERROR_NOT_FOUND Errno = 1168
)
const (
@@ -146,6 +147,7 @@ const (
WAIT_OBJECT_0 = 0x00000000
WAIT_FAILED = 0xFFFFFFFF
+ CREATE_NEW_PROCESS_GROUP = 0x00000200
CREATE_UNICODE_ENVIRONMENT = 0x00000400
PROCESS_QUERY_INFORMATION = 0x00000400
@@ -162,6 +164,9 @@ const (
FILE_MAP_WRITE = 0x02
FILE_MAP_READ = 0x04
FILE_MAP_EXECUTE = 0x20
+
+ CTRL_C_EVENT = 0
+ CTRL_BREAK_EVENT = 1
)
const (
@@ -372,11 +377,9 @@ func copyFindData(dst *Win32finddata, src *win32finddata1) {
dst.Reserved0 = src.Reserved0
dst.Reserved1 = src.Reserved1
- // The src is 1 element shorter than dst. Zero that last one.
+ // The src is 1 element bigger than dst, but it must be NUL.
copy(dst.FileName[:], src.FileName[:])
- dst.FileName[len(dst.FileName)-1] = 0
copy(dst.AlternateFileName[:], src.AlternateFileName[:])
- src.AlternateFileName[len(dst.AlternateFileName)-1] = 0
}
type ByHandleFileInformation struct {
@@ -493,15 +496,22 @@ const (
IPPROTO_TCP = 6
IPPROTO_UDP = 17
- SOL_SOCKET = 0xffff
- SO_REUSEADDR = 4
- SO_KEEPALIVE = 8
- SO_DONTROUTE = 16
- SO_BROADCAST = 32
- SO_LINGER = 128
- SO_RCVBUF = 0x1002
- SO_SNDBUF = 0x1001
- SO_UPDATE_ACCEPT_CONTEXT = 0x700b
+ SOL_SOCKET = 0xffff
+ SO_REUSEADDR = 4
+ SO_KEEPALIVE = 8
+ SO_DONTROUTE = 16
+ SO_BROADCAST = 32
+ SO_LINGER = 128
+ SO_RCVBUF = 0x1002
+ SO_SNDBUF = 0x1001
+ SO_UPDATE_ACCEPT_CONTEXT = 0x700b
+ SO_UPDATE_CONNECT_CONTEXT = 0x7010
+
+ IOC_OUT = 0x40000000
+ IOC_IN = 0x80000000
+ IOC_INOUT = IOC_IN | IOC_OUT
+ IOC_WS2 = 0x08000000
+ SIO_GET_EXTENSION_FUNCTION_POINTER = IOC_INOUT | IOC_WS2 | 6
// cf. http://support.microsoft.com/default.aspx?scid=kb;en-us;257460
@@ -921,3 +931,34 @@ const (
REG_DWORD = REG_DWORD_LITTLE_ENDIAN
REG_QWORD = REG_QWORD_LITTLE_ENDIAN
)
+
+type AddrinfoW struct {
+ Flags int32
+ Family int32
+ Socktype int32
+ Protocol int32
+ Addrlen uintptr
+ Canonname *uint16
+ Addr uintptr
+ Next *AddrinfoW
+}
+
+const (
+ AI_PASSIVE = 1
+ AI_CANONNAME = 2
+ AI_NUMERICHOST = 4
+)
+
+type GUID struct {
+ Data1 uint32
+ Data2 uint16
+ Data3 uint16
+ Data4 [8]byte
+}
+
+var WSAID_CONNECTEX = GUID{
+ 0x25a207b9,
+ 0xddf3,
+ 0x4660,
+ [8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e},
+}
diff --git a/src/pkg/testing/allocs.go b/src/pkg/testing/allocs.go
new file mode 100644
index 000000000..d142a330b
--- /dev/null
+++ b/src/pkg/testing/allocs.go
@@ -0,0 +1,41 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing
+
+import (
+ "runtime"
+)
+
+// AllocsPerRun returns the average number of allocations during calls to f.
+//
+// To compute the number of allocations, the function will first be run once as
+// a warm-up. The average number of allocations over the specified number of
+// runs will then be measured and returned.
+//
+// AllocsPerRun sets GOMAXPROCS to 1 during its measurement and will restore
+// it before returning.
+func AllocsPerRun(runs int, f func()) (avg float64) {
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
+
+ // Warm up the function
+ f()
+
+ // Measure the starting statistics
+ var memstats runtime.MemStats
+ runtime.ReadMemStats(&memstats)
+ mallocs := 0 - memstats.Mallocs
+
+ // Run the function the specified number of times
+ for i := 0; i < runs; i++ {
+ f()
+ }
+
+ // Read the final statistics
+ runtime.ReadMemStats(&memstats)
+ mallocs += memstats.Mallocs
+
+ // Average the mallocs over the runs (not counting the warm-up)
+ return float64(mallocs) / float64(runs)
+}
diff --git a/src/pkg/testing/benchmark.go b/src/pkg/testing/benchmark.go
index 41290594e..25fb2d619 100644
--- a/src/pkg/testing/benchmark.go
+++ b/src/pkg/testing/benchmark.go
@@ -9,11 +9,19 @@ import (
"fmt"
"os"
"runtime"
+ "sync"
"time"
)
var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run")
-var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")
+var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark")
+var benchmarkMemory = flag.Bool("test.benchmem", false, "print memory allocations for benchmarks")
+
+// Global lock to ensure only one benchmark runs at a time.
+var benchmarkLock sync.Mutex
+
+// Used for every benchmark for measuring memory.
+var memStats runtime.MemStats
// An internal type but exported because it is cross-package; part of the implementation
// of the "go test" command.
@@ -26,11 +34,18 @@ type InternalBenchmark struct {
// timing and to specify the number of iterations to run.
type B struct {
common
- N int
- benchmark InternalBenchmark
- bytes int64
- timerOn bool
- result BenchmarkResult
+ N int
+ benchmark InternalBenchmark
+ bytes int64
+ timerOn bool
+ showAllocResult bool
+ result BenchmarkResult
+ // The initial states of memStats.Mallocs and memStats.TotalAlloc.
+ startAllocs uint64
+ startBytes uint64
+ // The net total of this test after being run.
+ netAllocs uint64
+ netBytes uint64
}
// StartTimer starts timing a test. This function is called automatically
@@ -38,6 +53,9 @@ type B struct {
// a call to StopTimer.
func (b *B) StartTimer() {
if !b.timerOn {
+ runtime.ReadMemStats(&memStats)
+ b.startAllocs = memStats.Mallocs
+ b.startBytes = memStats.TotalAlloc
b.start = time.Now()
b.timerOn = true
}
@@ -49,6 +67,9 @@ func (b *B) StartTimer() {
func (b *B) StopTimer() {
if b.timerOn {
b.duration += time.Now().Sub(b.start)
+ runtime.ReadMemStats(&memStats)
+ b.netAllocs += memStats.Mallocs - b.startAllocs
+ b.netBytes += memStats.TotalAlloc - b.startBytes
b.timerOn = false
}
}
@@ -57,15 +78,27 @@ func (b *B) StopTimer() {
// It does not affect whether the timer is running.
func (b *B) ResetTimer() {
if b.timerOn {
+ runtime.ReadMemStats(&memStats)
+ b.startAllocs = memStats.Mallocs
+ b.startBytes = memStats.TotalAlloc
b.start = time.Now()
}
b.duration = 0
+ b.netAllocs = 0
+ b.netBytes = 0
}
// SetBytes records the number of bytes processed in a single operation.
// If this is called, the benchmark will report ns/op and MB/s.
func (b *B) SetBytes(n int64) { b.bytes = n }
+// ReportAllocs enables malloc statistics for this benchmark.
+// It is equivalent to setting -test.benchmem, but it only affects the
+// benchmark function that calls ReportAllocs.
+func (b *B) ReportAllocs() {
+ b.showAllocResult = true
+}
+
func (b *B) nsPerOp() int64 {
if b.N <= 0 {
return 0
@@ -75,6 +108,8 @@ func (b *B) nsPerOp() int64 {
// runN runs a single benchmark for the specified number of iterations.
func (b *B) runN(n int) {
+ benchmarkLock.Lock()
+ defer benchmarkLock.Unlock()
// Try to get a comparable environment for each run
// by clearing garbage from previous runs.
runtime.GC()
@@ -151,7 +186,7 @@ func (b *B) launch() {
b.runN(n)
// Run the benchmark for at least the specified amount of time.
- d := time.Duration(*benchTime * float64(time.Second))
+ d := *benchTime
for !b.failed && b.duration < d && n < 1e9 {
last := n
// Predict iterations/sec.
@@ -168,14 +203,16 @@ func (b *B) launch() {
n = roundUp(n)
b.runN(n)
}
- b.result = BenchmarkResult{b.N, b.duration, b.bytes}
+ b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes}
}
// The results of a benchmark run.
type BenchmarkResult struct {
- N int // The number of iterations.
- T time.Duration // The total time taken.
- Bytes int64 // Bytes processed in one iteration.
+ N int // The number of iterations.
+ T time.Duration // The total time taken.
+ Bytes int64 // Bytes processed in one iteration.
+ MemAllocs uint64 // The total number of memory allocations.
+ MemBytes uint64 // The total number of bytes allocated.
}
func (r BenchmarkResult) NsPerOp() int64 {
@@ -192,6 +229,20 @@ func (r BenchmarkResult) mbPerSec() float64 {
return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds()
}
+func (r BenchmarkResult) AllocsPerOp() int64 {
+ if r.N <= 0 {
+ return 0
+ }
+ return int64(r.MemAllocs) / int64(r.N)
+}
+
+func (r BenchmarkResult) AllocedBytesPerOp() int64 {
+ if r.N <= 0 {
+ return 0
+ }
+ return int64(r.MemBytes) / int64(r.N)
+}
+
func (r BenchmarkResult) String() string {
mbs := r.mbPerSec()
mb := ""
@@ -212,6 +263,11 @@ func (r BenchmarkResult) String() string {
return fmt.Sprintf("%8d\t%s%s", r.N, ns, mb)
}
+func (r BenchmarkResult) MemString() string {
+ return fmt.Sprintf("%8d B/op\t%8d allocs/op",
+ r.AllocedBytesPerOp(), r.AllocsPerOp())
+}
+
// An internal function but exported because it is cross-package; part of the implementation
// of the "go test" command.
func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) {
@@ -249,7 +305,11 @@ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [
fmt.Printf("--- FAIL: %s\n%s", benchName, b.output)
continue
}
- fmt.Printf("%v\n", r)
+ results := r.String()
+ if *benchmarkMemory || b.showAllocResult {
+ results += "\t" + r.MemString()
+ }
+ fmt.Println(results)
// Unlike with tests, we ignore the -chatty flag and always print output for
// benchmarks since the output generation time will skew the results.
if len(b.output) > 0 {
diff --git a/src/pkg/testing/example.go b/src/pkg/testing/example.go
index 671c79876..828c2d3ed 100644
--- a/src/pkg/testing/example.go
+++ b/src/pkg/testing/example.go
@@ -24,8 +24,6 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
var eg InternalExample
- stdout, stderr := os.Stdout, os.Stderr
-
for _, eg = range examples {
matched, err := matchString(*match, eg.Name)
if err != nil {
@@ -35,48 +33,68 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
if !matched {
continue
}
- if *chatty {
- fmt.Printf("=== RUN: %s\n", eg.Name)
+ if !runExample(eg) {
+ ok = false
}
+ }
+
+ return
+}
+
+func runExample(eg InternalExample) (ok bool) {
+ if *chatty {
+ fmt.Printf("=== RUN: %s\n", eg.Name)
+ }
- // capture stdout and stderr
- r, w, err := os.Pipe()
+ // Capture stdout.
+ stdout := os.Stdout
+ r, w, err := os.Pipe()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ os.Stdout = w
+ outC := make(chan string)
+ go func() {
+ buf := new(bytes.Buffer)
+ _, err := io.Copy(buf, r)
+ r.Close()
if err != nil {
- fmt.Fprintln(os.Stderr, err)
+ fmt.Fprintf(os.Stderr, "testing: copying pipe: %v\n", err)
os.Exit(1)
}
- os.Stdout, os.Stderr = w, w
- outC := make(chan string)
- go func() {
- buf := new(bytes.Buffer)
- _, err := io.Copy(buf, r)
- if err != nil {
- fmt.Fprintf(stderr, "testing: copying pipe: %v\n", err)
- os.Exit(1)
- }
- outC <- buf.String()
- }()
+ outC <- buf.String()
+ }()
+
+ start := time.Now()
+ ok = true
- // run example
- t0 := time.Now()
- eg.F()
- dt := time.Now().Sub(t0)
+ // Clean up in a deferred call so we can recover if the example panics.
+ defer func() {
+ d := time.Now().Sub(start)
- // close pipe, restore stdout/stderr, get output
+ // Close pipe, restore stdout, get output.
w.Close()
- os.Stdout, os.Stderr = stdout, stderr
+ os.Stdout = stdout
out := <-outC
- // report any errors
- tstr := fmt.Sprintf("(%.2f seconds)", dt.Seconds())
- if g, e := strings.TrimSpace(out), strings.TrimSpace(eg.Output); g != e {
- fmt.Printf("--- FAIL: %s %s\ngot:\n%s\nwant:\n%s\n",
- eg.Name, tstr, g, e)
+ var fail string
+ err := recover()
+ if g, e := strings.TrimSpace(out), strings.TrimSpace(eg.Output); g != e && err == nil {
+ fail = fmt.Sprintf("got:\n%s\nwant:\n%s\n", g, e)
+ }
+ if fail != "" || err != nil {
+ fmt.Printf("--- FAIL: %s (%v)\n%s", eg.Name, d, fail)
ok = false
} else if *chatty {
- fmt.Printf("--- PASS: %s %s\n", eg.Name, tstr)
+ fmt.Printf("--- PASS: %s (%v)\n", eg.Name, d)
}
- }
+ if err != nil {
+ panic(err)
+ }
+ }()
+ // Run example.
+ eg.F()
return
}
diff --git a/src/pkg/testing/testing.go b/src/pkg/testing/testing.go
index f59ce8ed6..86cd46c29 100644
--- a/src/pkg/testing/testing.go
+++ b/src/pkg/testing/testing.go
@@ -10,10 +10,21 @@
// [a-z]) and serves to identify the test routine.
// These TestXxx routines should be declared within the package they are testing.
//
+// Tests and benchmarks may be skipped if not applicable like this:
+// func TestTimeConsuming(t *testing.T) {
+// if testing.Short() {
+// t.Skip("skipping test in short mode.")
+// }
+// ...
+// }
+//
// Functions of the form
// func BenchmarkXxx(*testing.B)
// are considered benchmarks, and are executed by the "go test" command when
-// the -test.bench flag is provided.
+// the -test.bench flag is provided. Benchmarks are run sequentially.
+//
+// For a description of the testing flags, see
+// http://golang.org/cmd/go/#Description_of_testing_flags.
//
// A sample benchmark function looks like this:
// func BenchmarkHello(b *testing.B) {
@@ -22,26 +33,26 @@
// }
// }
//
+// The benchmark function must run the target code b.N times.
// The benchmark package will vary b.N until the benchmark function lasts
// long enough to be timed reliably. The output
-// testing.BenchmarkHello 10000000 282 ns/op
+// BenchmarkHello 10000000 282 ns/op
// means that the loop ran 10000000 times at a speed of 282 ns per loop.
//
// If a benchmark needs some expensive setup before running, the timer
-// may be stopped:
+// may be reset:
// func BenchmarkBigLen(b *testing.B) {
-// b.StopTimer()
// big := NewBig()
-// b.StartTimer()
+// b.ResetTimer()
// for i := 0; i < b.N; i++ {
// big.Len()
// }
// }
//
// The package also runs and verifies example code. Example functions may
-// include a concluding comment that begins with "Output:" and is compared with
-// the standard output of the function when the tests are run, as in these
-// examples of an example:
+// include a concluding line comment that begins with "Output:" and is compared with
+// the standard output of the function when the tests are run. (The comparison
+// ignores leading and trailing space.) These are examples of an example:
//
// func ExampleHello() {
// fmt.Println("hello")
@@ -79,6 +90,7 @@
package testing
import (
+ "bytes"
"flag"
"fmt"
"os"
@@ -86,6 +98,7 @@ import (
"runtime/pprof"
"strconv"
"strings"
+ "sync"
"time"
)
@@ -98,14 +111,16 @@ var (
short = flag.Bool("test.short", false, "run smaller test suite to save time")
// Report as tests are run; default is silent for success.
- chatty = flag.Bool("test.v", false, "verbose: print additional output")
- match = flag.String("test.run", "", "regular expression to select tests and examples to run")
- memProfile = flag.String("test.memprofile", "", "write a memory profile to the named file after execution")
- memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate")
- cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
- timeout = flag.Duration("test.timeout", 0, "if positive, sets an aggregate time limit for all tests")
- cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
- parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maximum test parallelism")
+ chatty = flag.Bool("test.v", false, "verbose: print additional output")
+ match = flag.String("test.run", "", "regular expression to select tests and examples to run")
+ memProfile = flag.String("test.memprofile", "", "write a memory profile to the named file after execution")
+ memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate")
+ cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
+ blockProfile = flag.String("test.blockprofile", "", "write a goroutine blocking profile to the named file after execution")
+ blockProfileRate = flag.Int("test.blockprofilerate", 1, "if >= 0, calls runtime.SetBlockProfileRate()")
+ timeout = flag.Duration("test.timeout", 0, "if positive, sets an aggregate time limit for all tests")
+ cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
+ parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maximum test parallelism")
haveExamples bool // are there examples?
@@ -115,8 +130,11 @@ var (
// common holds the elements common between T and B and
// captures common methods such as Errorf.
type common struct {
- output []byte // Output generated by test or benchmark.
- failed bool // Test or benchmark has failed.
+ mu sync.RWMutex // guards output and failed
+ output []byte // Output generated by test or benchmark.
+ failed bool // Test or benchmark has failed.
+ skipped bool // Test of benchmark has been skipped.
+
start time.Time // Time test or benchmark started
duration time.Duration
self interface{} // To be sent on signal channel when done.
@@ -128,37 +146,43 @@ func Short() bool {
return *short
}
-// decorate inserts the final newline if needed and indentation tabs for formatting.
-// If addFileLine is true, it also prefixes the string with the file and line of the call site.
-func decorate(s string, addFileLine bool) string {
- if addFileLine {
- _, file, line, ok := runtime.Caller(3) // decorate + log + public function.
- if ok {
- // Truncate file name at last file name separator.
- if index := strings.LastIndex(file, "/"); index >= 0 {
- file = file[index+1:]
- } else if index = strings.LastIndex(file, "\\"); index >= 0 {
- file = file[index+1:]
- }
- } else {
- file = "???"
- line = 1
+// Verbose reports whether the -test.v flag is set.
+func Verbose() bool {
+ return *chatty
+}
+
+// decorate prefixes the string with the file and line of the call site
+// and inserts the final newline if needed and indentation tabs for formatting.
+func decorate(s string) string {
+ _, file, line, ok := runtime.Caller(3) // decorate + log + public function.
+ if ok {
+ // Truncate file name at last file name separator.
+ if index := strings.LastIndex(file, "/"); index >= 0 {
+ file = file[index+1:]
+ } else if index = strings.LastIndex(file, "\\"); index >= 0 {
+ file = file[index+1:]
}
- s = fmt.Sprintf("%s:%d: %s", file, line, s)
+ } else {
+ file = "???"
+ line = 1
}
- s = "\t" + s // Every line is indented at least one tab.
- n := len(s)
- if n > 0 && s[n-1] != '\n' {
- s += "\n"
- n++
+ buf := new(bytes.Buffer)
+ // Every line is indented at least one tab.
+ buf.WriteByte('\t')
+ fmt.Fprintf(buf, "%s:%d: ", file, line)
+ lines := strings.Split(s, "\n")
+ if l := len(lines); l > 1 && lines[l-1] == "" {
+ lines = lines[:l-1]
}
- for i := 0; i < n-1; i++ { // -1 to avoid final newline
- if s[i] == '\n' {
+ for i, line := range lines {
+ if i > 0 {
// Second and subsequent lines are indented an extra tab.
- return s[0:i+1] + "\t" + decorate(s[i+1:n], false)
+ buf.WriteString("\n\t\t")
}
+ buf.WriteString(line)
}
- return s
+ buf.WriteByte('\n')
+ return buf.String()
}
// T is a type passed to Test functions to manage test state and support formatted test logs.
@@ -170,13 +194,25 @@ type T struct {
}
// Fail marks the function as having failed but continues execution.
-func (c *common) Fail() { c.failed = true }
+func (c *common) Fail() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.failed = true
+}
-// Failed returns whether the function has failed.
-func (c *common) Failed() bool { return c.failed }
+// Failed reports whether the function has failed.
+func (c *common) Failed() bool {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ return c.failed
+}
// FailNow marks the function as having failed and stops its execution.
// Execution will continue at the next test or benchmark.
+// FailNow must be called from the goroutine running the
+// test or benchmark function, not from other goroutines
+// created during the test. Calling FailNow does not stop
+// those other goroutines.
func (c *common) FailNow() {
c.Fail()
@@ -204,43 +240,80 @@ func (c *common) FailNow() {
// log generates the output. It's always at the same stack depth.
func (c *common) log(s string) {
- c.output = append(c.output, decorate(s, true)...)
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.output = append(c.output, decorate(s)...)
}
-// Log formats its arguments using default formatting, analogous to Println(),
+// Log formats its arguments using default formatting, analogous to Println,
// and records the text in the error log.
func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) }
-// Logf formats its arguments according to the format, analogous to Printf(),
+// Logf formats its arguments according to the format, analogous to Printf,
// and records the text in the error log.
func (c *common) Logf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) }
-// Error is equivalent to Log() followed by Fail().
+// Error is equivalent to Log followed by Fail.
func (c *common) Error(args ...interface{}) {
c.log(fmt.Sprintln(args...))
c.Fail()
}
-// Errorf is equivalent to Logf() followed by Fail().
+// Errorf is equivalent to Logf followed by Fail.
func (c *common) Errorf(format string, args ...interface{}) {
c.log(fmt.Sprintf(format, args...))
c.Fail()
}
-// Fatal is equivalent to Log() followed by FailNow().
+// Fatal is equivalent to Log followed by FailNow.
func (c *common) Fatal(args ...interface{}) {
c.log(fmt.Sprintln(args...))
c.FailNow()
}
-// Fatalf is equivalent to Logf() followed by FailNow().
+// Fatalf is equivalent to Logf followed by FailNow.
func (c *common) Fatalf(format string, args ...interface{}) {
c.log(fmt.Sprintf(format, args...))
c.FailNow()
}
-// Parallel signals that this test is to be run in parallel with (and only with)
-// other parallel tests in this CPU group.
+// Skip is equivalent to Log followed by SkipNow.
+func (c *common) Skip(args ...interface{}) {
+ c.log(fmt.Sprintln(args...))
+ c.SkipNow()
+}
+
+// Skipf is equivalent to Logf followed by SkipNow.
+func (c *common) Skipf(format string, args ...interface{}) {
+ c.log(fmt.Sprintf(format, args...))
+ c.SkipNow()
+}
+
+// SkipNow marks the test as having been skipped and stops its execution.
+// Execution will continue at the next test or benchmark. See also FailNow.
+// SkipNow must be called from the goroutine running the test, not from
+// other goroutines created during the test. Calling SkipNow does not stop
+// those other goroutines.
+func (c *common) SkipNow() {
+ c.skip()
+ runtime.Goexit()
+}
+
+func (c *common) skip() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.skipped = true
+}
+
+// Skipped reports whether the test was skipped.
+func (c *common) Skipped() bool {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ return c.skipped
+}
+
+// Parallel signals that this test is to be run in parallel with (and only with)
+// other parallel tests.
func (t *T) Parallel() {
t.signal <- (*T)(nil) // Release main testing loop
<-t.startParallel // Wait for serial tests to finish
@@ -257,7 +330,7 @@ func tRunner(t *T, test *InternalTest) {
t.start = time.Now()
// When this goroutine is done, either because test.F(t)
- // returned normally or because a test failure triggered
+ // returned normally or because a test failure triggered
// a call to runtime.Goexit, record the duration and send
// a signal saying that the test is done.
defer func() {
@@ -297,10 +370,14 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest,
func (t *T) report() {
tstr := fmt.Sprintf("(%.2f seconds)", t.duration.Seconds())
format := "--- %s: %s %s\n%s"
- if t.failed {
+ if t.Failed() {
fmt.Printf(format, "FAIL", t.name, tstr, t.output)
} else if *chatty {
- fmt.Printf(format, "PASS", t.name, tstr, t.output)
+ if t.Skipped() {
+ fmt.Printf(format, "SKIP", t.name, tstr, t.output)
+ } else {
+ fmt.Printf(format, "PASS", t.name, tstr, t.output)
+ }
}
}
@@ -356,7 +433,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT
continue
}
t.report()
- ok = ok && !out.failed
+ ok = ok && !out.Failed()
}
running := 0
@@ -369,7 +446,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT
}
t := (<-collector).(*T)
t.report()
- ok = ok && !t.failed
+ ok = ok && !t.Failed()
running--
}
}
@@ -394,7 +471,9 @@ func before() {
}
// Could save f so after can call f.Close; not worth the effort.
}
-
+ if *blockProfile != "" && *blockProfileRate >= 0 {
+ runtime.SetBlockProfileRate(*blockProfileRate)
+ }
}
// after runs after all testing.
@@ -413,6 +492,17 @@ func after() {
}
f.Close()
}
+ if *blockProfile != "" && *blockProfileRate >= 0 {
+ f, err := os.Create(*blockProfile)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s", err)
+ return
+ }
+ if err = pprof.Lookup("block").WriteTo(f, 0); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", *blockProfile, err)
+ }
+ f.Close()
+ }
}
var timer *time.Timer
diff --git a/src/pkg/text/scanner/scanner.go b/src/pkg/text/scanner/scanner.go
index 565650edf..e0d86e343 100644
--- a/src/pkg/text/scanner/scanner.go
+++ b/src/pkg/text/scanner/scanner.go
@@ -5,7 +5,8 @@
// Package scanner provides a scanner and tokenizer for UTF-8-encoded text.
// It takes an io.Reader providing the source, which then can be tokenized
// through repeated calls to the Scan function. For compatibility with
-// existing tools, the NUL character is not allowed.
+// existing tools, the NUL character is not allowed. If the first character
+// in the source is a UTF-8 encoded byte order mark (BOM), it is discarded.
//
// By default, a Scanner skips white space and Go comments and recognizes all
// literals as defined by the Go language specification. It may be
@@ -208,11 +209,6 @@ func (s *Scanner) Init(src io.Reader) *Scanner {
return s
}
-// TODO(gri): The code for next() and the internal scanner state could benefit
-// from a rethink. While next() is optimized for the common ASCII
-// case, the "corrections" needed for proper position tracking undo
-// some of the attempts for fast-path optimization.
-
// next reads and returns the next Unicode character. It is designed such
// that only a minimal amount of work needs to be done in the common ASCII
// case (one test to check for both ASCII and end-of-buffer, and one test
@@ -316,7 +312,11 @@ func (s *Scanner) Next() rune {
// character of the source.
func (s *Scanner) Peek() rune {
if s.ch < 0 {
+ // this code is only run for the very first character
s.ch = s.next()
+ if s.ch == '\uFEFF' {
+ s.ch = s.next() // ignore BOM
+ }
}
return s.ch
}
@@ -389,15 +389,20 @@ func (s *Scanner) scanNumber(ch rune) (rune, rune) {
if ch == 'x' || ch == 'X' {
// hexadecimal int
ch = s.next()
+ hasMantissa := false
for digitVal(ch) < 16 {
ch = s.next()
+ hasMantissa = true
+ }
+ if !hasMantissa {
+ s.error("illegal hexadecimal number")
}
} else {
// octal int or float
- seenDecimalDigit := false
+ has8or9 := false
for isDecimal(ch) {
if ch > '7' {
- seenDecimalDigit = true
+ has8or9 = true
}
ch = s.next()
}
@@ -408,7 +413,7 @@ func (s *Scanner) scanNumber(ch rune) (rune, rune) {
return Float, ch
}
// octal int
- if seenDecimalDigit {
+ if has8or9 {
s.error("illegal octal number")
}
}
diff --git a/src/pkg/text/scanner/scanner_test.go b/src/pkg/text/scanner/scanner_test.go
index bb3adb55a..496eed4a3 100644
--- a/src/pkg/text/scanner/scanner_test.go
+++ b/src/pkg/text/scanner/scanner_test.go
@@ -358,8 +358,10 @@ func TestScanSelectedMask(t *testing.T) {
}
func TestScanNext(t *testing.T) {
- s := new(Scanner).Init(bytes.NewBufferString("if a == bcd /* comment */ {\n\ta += c\n} // line comment ending in eof"))
- checkTok(t, s, 1, s.Scan(), Ident, "if")
+ const BOM = '\uFEFF'
+ BOMs := string(BOM)
+ s := new(Scanner).Init(bytes.NewBufferString(BOMs + "if a == bcd /* com" + BOMs + "ment */ {\n\ta += c\n}" + BOMs + "// line comment ending in eof"))
+ checkTok(t, s, 1, s.Scan(), Ident, "if") // the first BOM is ignored
checkTok(t, s, 1, s.Scan(), Ident, "a")
checkTok(t, s, 1, s.Scan(), '=', "=")
checkTok(t, s, 0, s.Next(), '=', "")
@@ -372,6 +374,7 @@ func TestScanNext(t *testing.T) {
checkTok(t, s, 0, s.Next(), '=', "")
checkTok(t, s, 2, s.Scan(), Ident, "c")
checkTok(t, s, 3, s.Scan(), '}', "}")
+ checkTok(t, s, 3, s.Scan(), BOM, BOMs)
checkTok(t, s, 3, s.Scan(), -1, "")
if s.ErrorCount != 0 {
t.Errorf("%d errors", s.ErrorCount)
@@ -446,6 +449,9 @@ func TestError(t *testing.T) {
testError(t, `"\'"`, "1:3", "illegal char escape", String)
testError(t, `01238`, "1:6", "illegal octal number", Int)
+ testError(t, `01238123`, "1:9", "illegal octal number", Int)
+ testError(t, `0x`, "1:3", "illegal hexadecimal number", Int)
+ testError(t, `0xg`, "1:3", "illegal hexadecimal number", Int)
testError(t, `'aa'`, "1:4", "illegal char literal", Char)
testError(t, `'`, "1:2", "literal not terminated", Char)
diff --git a/src/pkg/text/tabwriter/tabwriter.go b/src/pkg/text/tabwriter/tabwriter.go
index ce84600d6..722ac8d87 100644
--- a/src/pkg/text/tabwriter/tabwriter.go
+++ b/src/pkg/text/tabwriter/tabwriter.go
@@ -547,7 +547,7 @@ func (b *Writer) Write(buf []byte) (n int, err error) {
}
// NewWriter allocates and initializes a new tabwriter.Writer.
-// The parameters are the same as for the the Init function.
+// The parameters are the same as for the Init function.
//
func NewWriter(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Writer {
return new(Writer).Init(output, minwidth, tabwidth, padding, padchar, flags)
diff --git a/src/pkg/text/template/doc.go b/src/pkg/text/template/doc.go
index aa50ab97f..2da339ce8 100644
--- a/src/pkg/text/template/doc.go
+++ b/src/pkg/text/template/doc.go
@@ -63,16 +63,16 @@ data, defined in detail below.
otherwise, T1 is executed. Dot is unaffected.
{{range pipeline}} T1 {{end}}
- The value of the pipeline must be an array, slice, or map. If
- the value of the pipeline has length zero, nothing is output;
+ The value of the pipeline must be an array, slice, map, or channel.
+ If the value of the pipeline has length zero, nothing is output;
otherwise, dot is set to the successive elements of the array,
slice, or map and T1 is executed. If the value is a map and the
keys are of basic type with a defined order ("comparable"), the
elements will be visited in sorted key order.
{{range pipeline}} T1 {{else}} T0 {{end}}
- The value of the pipeline must be an array, slice, or map. If
- the value of the pipeline has length zero, dot is unaffected and
+ The value of the pipeline must be an array, slice, map, or channel.
+ If the value of the pipeline has length zero, dot is unaffected and
T0 is executed; otherwise, dot is set to the successive elements
of the array, slice, or map and T1 is executed.
@@ -100,6 +100,7 @@ An argument is a simple value, denoted by one of the following.
- A boolean, string, character, integer, floating-point, imaginary
or complex constant in Go syntax. These behave like Go's untyped
constants, although raw strings may not span newlines.
+ - The keyword nil, representing an untyped Go nil.
- The character '.' (period):
.
The result is the value of dot.
@@ -147,6 +148,10 @@ An argument is a simple value, denoted by one of the following.
The result is the value of invoking the function, fun(). The return
types and values behave as in methods. Functions and function
names are described below.
+ - A parenthesized instance of one the above, for grouping. The result
+ may be accessed by a field or map key invocation.
+ print (.F1 arg1) (.F2 arg2)
+ (.StructValuedMethod "arg").Field
Arguments may evaluate to any type; if they are pointers the implementation
automatically indirects to the base type when required.
@@ -198,7 +203,7 @@ If a "range" action initializes a variable, the variable is set to the
successive elements of the iteration. Also, a "range" may declare two
variables, separated by a comma:
- $index, $element := pipeline
+ range $index, $element := pipeline
in which case $index and $element are set to the successive values of the
array/slice index or map key and element, respectively. Note that if there is
@@ -227,6 +232,8 @@ All produce the quoted word "output":
{{"output" | printf "%q"}}
A function call whose final argument comes from the previous
command.
+ {{printf "%q" (print "out" "put")}}
+ A parenthesized argument.
{{"put" | printf "%s%s" "out" | printf "%q"}}
A more elaborate call.
{{"output" | printf "%s" | printf "%q"}}
diff --git a/src/pkg/text/template/example_test.go b/src/pkg/text/template/example_test.go
index ad49514a8..de1d51851 100644
--- a/src/pkg/text/template/example_test.go
+++ b/src/pkg/text/template/example_test.go
@@ -47,25 +47,25 @@ Josie
// Output:
// Dear Aunt Mildred,
- //
+ //
// It was a pleasure to see you at the wedding.
// Thank you for the lovely bone china tea set.
- //
+ //
// Best wishes,
// Josie
- //
+ //
// Dear Uncle John,
- //
+ //
// It is a shame you couldn't make it to the wedding.
// Thank you for the lovely moleskin pants.
- //
+ //
// Best wishes,
// Josie
- //
+ //
// Dear Cousin Rodney,
- //
+ //
// It is a shame you couldn't make it to the wedding.
- //
+ //
// Best wishes,
// Josie
}
diff --git a/src/pkg/text/template/exec.go b/src/pkg/text/template/exec.go
index aba21ce28..b9c03d8f0 100644
--- a/src/pkg/text/template/exec.go
+++ b/src/pkg/text/template/exec.go
@@ -20,7 +20,7 @@ import (
type state struct {
tmpl *Template
wr io.Writer
- line int // line number for errors
+ node parse.Node // current node, for errors
vars []variable // push-down stack of variable values.
}
@@ -63,17 +63,32 @@ func (s *state) varValue(name string) reflect.Value {
var zero reflect.Value
+// at marks the state to be on node n, for error reporting.
+func (s *state) at(node parse.Node) {
+ s.node = node
+}
+
+// doublePercent returns the string with %'s replaced by %%, if necessary,
+// so it can be used safely inside a Printf format string.
+func doublePercent(str string) string {
+ if strings.Contains(str, "%") {
+ str = strings.Replace(str, "%", "%%", -1)
+ }
+ return str
+}
+
// errorf formats the error and terminates processing.
func (s *state) errorf(format string, args ...interface{}) {
- format = fmt.Sprintf("template: %s:%d: %s", s.tmpl.Name(), s.line, format)
+ name := doublePercent(s.tmpl.Name())
+ if s.node == nil {
+ format = fmt.Sprintf("template: %s: %s", name, format)
+ } else {
+ location, context := s.tmpl.ErrorContext(s.node)
+ format = fmt.Sprintf("template: %s: executing %q at <%s>: %s", location, name, doublePercent(context), format)
+ }
panic(fmt.Errorf(format, args...))
}
-// error terminates processing.
-func (s *state) error(err error) {
- s.errorf("%s", err)
-}
-
// errRecover is the handler that turns panics into returns from the top
// level of Parse.
func errRecover(errp *error) {
@@ -108,7 +123,6 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
state := &state{
tmpl: t,
wr: wr,
- line: 1,
vars: []variable{{"$", value}},
}
if t.Tree == nil || t.Root == nil {
@@ -120,38 +134,34 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
// Walk functions step through the major pieces of the template structure,
// generating output as they go.
-func (s *state) walk(dot reflect.Value, n parse.Node) {
- switch n := n.(type) {
+func (s *state) walk(dot reflect.Value, node parse.Node) {
+ s.at(node)
+ switch node := node.(type) {
case *parse.ActionNode:
- s.line = n.Line
// Do not pop variables so they persist until next end.
// Also, if the action declares variables, don't print the result.
- val := s.evalPipeline(dot, n.Pipe)
- if len(n.Pipe.Decl) == 0 {
- s.printValue(n, val)
+ val := s.evalPipeline(dot, node.Pipe)
+ if len(node.Pipe.Decl) == 0 {
+ s.printValue(node, val)
}
case *parse.IfNode:
- s.line = n.Line
- s.walkIfOrWith(parse.NodeIf, dot, n.Pipe, n.List, n.ElseList)
+ s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
case *parse.ListNode:
- for _, node := range n.Nodes {
+ for _, node := range node.Nodes {
s.walk(dot, node)
}
case *parse.RangeNode:
- s.line = n.Line
- s.walkRange(dot, n)
+ s.walkRange(dot, node)
case *parse.TemplateNode:
- s.line = n.Line
- s.walkTemplate(dot, n)
+ s.walkTemplate(dot, node)
case *parse.TextNode:
- if _, err := s.wr.Write(n.Text); err != nil {
- s.error(err)
+ if _, err := s.wr.Write(node.Text); err != nil {
+ s.errorf("%s", err)
}
case *parse.WithNode:
- s.line = n.Line
- s.walkIfOrWith(parse.NodeWith, dot, n.Pipe, n.List, n.ElseList)
+ s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList)
default:
- s.errorf("unknown node: %s", n)
+ s.errorf("unknown node: %s", node)
}
}
@@ -206,6 +216,7 @@ func isTrue(val reflect.Value) (truth, ok bool) {
}
func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
+ s.at(r)
defer s.pop(s.mark())
val, _ := indirect(s.evalPipeline(dot, r.Pipe))
// mark top of stack before any variables in the body are pushed.
@@ -266,6 +277,7 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
}
func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) {
+ s.at(t)
tmpl := s.tmpl.tmpl[t.Name]
if tmpl == nil {
s.errorf("template %q not defined", t.Name)
@@ -291,6 +303,7 @@ func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value ref
if pipe == nil {
return
}
+ s.at(pipe)
for _, cmd := range pipe.Cmds {
value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg.
// If the object has type interface{}, dig down one level to the thing inside.
@@ -315,18 +328,26 @@ func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final ref
switch n := firstWord.(type) {
case *parse.FieldNode:
return s.evalFieldNode(dot, n, cmd.Args, final)
+ case *parse.ChainNode:
+ return s.evalChainNode(dot, n, cmd.Args, final)
case *parse.IdentifierNode:
// Must be a function.
- return s.evalFunction(dot, n.Ident, cmd.Args, final)
+ return s.evalFunction(dot, n, cmd, cmd.Args, final)
+ case *parse.PipeNode:
+ // Parenthesized pipeline. The arguments are all inside the pipeline; final is ignored.
+ return s.evalPipeline(dot, n)
case *parse.VariableNode:
return s.evalVariableNode(dot, n, cmd.Args, final)
}
+ s.at(firstWord)
s.notAFunction(cmd.Args, final)
switch word := firstWord.(type) {
case *parse.BoolNode:
return reflect.ValueOf(word.True)
case *parse.DotNode:
return dot
+ case *parse.NilNode:
+ s.errorf("nil is not a command")
case *parse.NumberNode:
return s.idealConstant(word)
case *parse.StringNode:
@@ -344,6 +365,7 @@ func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value {
// These are ideal constants but we don't know the type
// and we have no context. (If it was a method argument,
// we'd know what we need.) The syntax guides us to some extent.
+ s.at(constant)
switch {
case constant.IsComplex:
return reflect.ValueOf(constant.Complex128) // incontrovertible.
@@ -362,43 +384,57 @@ func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value {
}
func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value {
- return s.evalFieldChain(dot, dot, field.Ident, args, final)
+ s.at(field)
+ return s.evalFieldChain(dot, dot, field, field.Ident, args, final)
+}
+
+func (s *state) evalChainNode(dot reflect.Value, chain *parse.ChainNode, args []parse.Node, final reflect.Value) reflect.Value {
+ s.at(chain)
+ // (pipe).Field1.Field2 has pipe as .Node, fields as .Field. Eval the pipeline, then the fields.
+ pipe := s.evalArg(dot, nil, chain.Node)
+ if len(chain.Field) == 0 {
+ s.errorf("internal error: no fields in evalChainNode")
+ }
+ return s.evalFieldChain(dot, pipe, chain, chain.Field, args, final)
}
-func (s *state) evalVariableNode(dot reflect.Value, v *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value {
+func (s *state) evalVariableNode(dot reflect.Value, variable *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value {
// $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields.
- value := s.varValue(v.Ident[0])
- if len(v.Ident) == 1 {
+ s.at(variable)
+ value := s.varValue(variable.Ident[0])
+ if len(variable.Ident) == 1 {
s.notAFunction(args, final)
return value
}
- return s.evalFieldChain(dot, value, v.Ident[1:], args, final)
+ return s.evalFieldChain(dot, value, variable, variable.Ident[1:], args, final)
}
// evalFieldChain evaluates .X.Y.Z possibly followed by arguments.
// dot is the environment in which to evaluate arguments, while
// receiver is the value being walked along the chain.
-func (s *state) evalFieldChain(dot, receiver reflect.Value, ident []string, args []parse.Node, final reflect.Value) reflect.Value {
+func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ident []string, args []parse.Node, final reflect.Value) reflect.Value {
n := len(ident)
for i := 0; i < n-1; i++ {
- receiver = s.evalField(dot, ident[i], nil, zero, receiver)
+ receiver = s.evalField(dot, ident[i], node, nil, zero, receiver)
}
// Now if it's a method, it gets the arguments.
- return s.evalField(dot, ident[n-1], args, final, receiver)
+ return s.evalField(dot, ident[n-1], node, args, final, receiver)
}
-func (s *state) evalFunction(dot reflect.Value, name string, args []parse.Node, final reflect.Value) reflect.Value {
+func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value {
+ s.at(node)
+ name := node.Ident
function, ok := findFunction(name, s.tmpl)
if !ok {
s.errorf("%q is not a defined function", name)
}
- return s.evalCall(dot, function, name, args, final)
+ return s.evalCall(dot, function, cmd, name, args, final)
}
// evalField evaluates an expression like (.Field) or (.Field arg1 arg2).
// The 'final' argument represents the return value from the preceding
// value of the pipeline, if any.
-func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node, final, receiver reflect.Value) reflect.Value {
+func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value {
if !receiver.IsValid() {
return zero
}
@@ -411,26 +447,31 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node
ptr = ptr.Addr()
}
if method := ptr.MethodByName(fieldName); method.IsValid() {
- return s.evalCall(dot, method, fieldName, args, final)
+ return s.evalCall(dot, method, node, fieldName, args, final)
}
hasArgs := len(args) > 1 || final.IsValid()
- // It's not a method; is it a field of a struct?
+ // It's not a method; must be a field of a struct or an element of a map. The receiver must not be nil.
receiver, isNil := indirect(receiver)
- if receiver.Kind() == reflect.Struct {
+ if isNil {
+ s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
+ }
+ switch receiver.Kind() {
+ case reflect.Struct:
tField, ok := receiver.Type().FieldByName(fieldName)
if ok {
field := receiver.FieldByIndex(tField.Index)
- if tField.PkgPath == "" { // field is exported
- // If it's a function, we must call it.
- if hasArgs {
- s.errorf("%s has arguments but cannot be invoked as function", fieldName)
- }
- return field
+ if tField.PkgPath != "" { // field is unexported
+ s.errorf("%s is an unexported field of struct type %s", fieldName, typ)
+ }
+ // If it's a function, we must call it.
+ if hasArgs {
+ s.errorf("%s has arguments but cannot be invoked as function", fieldName)
}
+ return field
}
- }
- // If it's a map, attempt to use the field name as a key.
- if receiver.Kind() == reflect.Map {
+ s.errorf("%s is not a field of struct type %s", fieldName, typ)
+ case reflect.Map:
+ // If it's a map, attempt to use the field name as a key.
nameVal := reflect.ValueOf(fieldName)
if nameVal.Type().AssignableTo(receiver.Type().Key()) {
if hasArgs {
@@ -439,9 +480,6 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node
return receiver.MapIndex(nameVal)
}
}
- if isNil {
- s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
- }
s.errorf("can't evaluate field %s in type %s", fieldName, typ)
panic("not reached")
}
@@ -454,7 +492,7 @@ var (
// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
// it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0]
// as the function itself.
-func (s *state) evalCall(dot, fun reflect.Value, name string, args []parse.Node, final reflect.Value) reflect.Value {
+func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value {
if args != nil {
args = args[1:] // Zeroth arg is function name/node; not passed to function.
}
@@ -473,7 +511,8 @@ func (s *state) evalCall(dot, fun reflect.Value, name string, args []parse.Node,
s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args))
}
if !goodFunc(typ) {
- s.errorf("can't handle multiple results from method/function %q", name)
+ // TODO: This could still be a confusing error; maybe goodFunc should provide info.
+ s.errorf("can't call method/function %q with %d results", name, typ.NumOut())
}
// Build the arg list.
argv := make([]reflect.Value, numIn)
@@ -500,24 +539,31 @@ func (s *state) evalCall(dot, fun reflect.Value, name string, args []parse.Node,
result := fun.Call(argv)
// If we have an error that is not nil, stop execution and return that error to the caller.
if len(result) == 2 && !result[1].IsNil() {
+ s.at(node)
s.errorf("error calling %s: %s", name, result[1].Interface().(error))
}
return result[0]
}
+// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.
+func canBeNil(typ reflect.Type) bool {
+ switch typ.Kind() {
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+ return true
+ }
+ return false
+}
+
// validateType guarantees that the value is valid and assignable to the type.
func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value {
if !value.IsValid() {
- switch typ.Kind() {
- case reflect.Interface, reflect.Ptr, reflect.Chan, reflect.Map, reflect.Slice, reflect.Func:
+ if typ == nil || canBeNil(typ) {
// An untyped nil interface{}. Accept as a proper nil value.
- // TODO: Can we delete the other types in this list? Should we?
- value = reflect.Zero(typ)
- default:
- s.errorf("invalid value; expected %s", typ)
+ return reflect.Zero(typ)
}
+ s.errorf("invalid value; expected %s", typ)
}
- if !value.Type().AssignableTo(typ) {
+ if typ != nil && !value.Type().AssignableTo(typ) {
if value.Kind() == reflect.Interface && !value.IsNil() {
value = value.Elem()
if value.Type().AssignableTo(typ) {
@@ -542,13 +588,21 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
}
func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) reflect.Value {
+ s.at(n)
switch arg := n.(type) {
case *parse.DotNode:
return s.validateType(dot, typ)
+ case *parse.NilNode:
+ if canBeNil(typ) {
+ return reflect.Zero(typ)
+ }
+ s.errorf("cannot assign nil to %s", typ)
case *parse.FieldNode:
return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, zero), typ)
case *parse.VariableNode:
return s.validateType(s.evalVariableNode(dot, arg, nil, zero), typ)
+ case *parse.PipeNode:
+ return s.validateType(s.evalPipeline(dot, arg), typ)
}
switch typ.Kind() {
case reflect.Bool:
@@ -573,6 +627,7 @@ func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) refle
}
func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value {
+ s.at(n)
if n, ok := n.(*parse.BoolNode); ok {
value := reflect.New(typ).Elem()
value.SetBool(n.True)
@@ -583,6 +638,7 @@ func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value {
}
func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value {
+ s.at(n)
if n, ok := n.(*parse.StringNode); ok {
value := reflect.New(typ).Elem()
value.SetString(n.Text)
@@ -593,6 +649,7 @@ func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value {
}
func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value {
+ s.at(n)
if n, ok := n.(*parse.NumberNode); ok && n.IsInt {
value := reflect.New(typ).Elem()
value.SetInt(n.Int64)
@@ -603,6 +660,7 @@ func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value {
}
func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Value {
+ s.at(n)
if n, ok := n.(*parse.NumberNode); ok && n.IsUint {
value := reflect.New(typ).Elem()
value.SetUint(n.Uint64)
@@ -613,6 +671,7 @@ func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Valu
}
func (s *state) evalFloat(typ reflect.Type, n parse.Node) reflect.Value {
+ s.at(n)
if n, ok := n.(*parse.NumberNode); ok && n.IsFloat {
value := reflect.New(typ).Elem()
value.SetFloat(n.Float64)
@@ -633,6 +692,7 @@ func (s *state) evalComplex(typ reflect.Type, n parse.Node) reflect.Value {
}
func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Value {
+ s.at(n)
switch n := n.(type) {
case *parse.BoolNode:
return reflect.ValueOf(n.True)
@@ -641,13 +701,18 @@ func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Valu
case *parse.FieldNode:
return s.evalFieldNode(dot, n, nil, zero)
case *parse.IdentifierNode:
- return s.evalFunction(dot, n.Ident, nil, zero)
+ return s.evalFunction(dot, n, n, nil, zero)
+ case *parse.NilNode:
+ // NilNode is handled in evalArg, the only place that calls here.
+ s.errorf("evalEmptyInterface: nil (can't happen)")
case *parse.NumberNode:
return s.idealConstant(n)
case *parse.StringNode:
return reflect.ValueOf(n.Text)
case *parse.VariableNode:
return s.evalVariableNode(dot, n, nil, zero)
+ case *parse.PipeNode:
+ return s.evalPipeline(dot, n)
}
s.errorf("can't handle assignment of %s to empty interface argument", n)
panic("not reached")
@@ -671,6 +736,7 @@ func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
// printValue writes the textual representation of the value to the output of
// the template.
func (s *state) printValue(n parse.Node, v reflect.Value) {
+ s.at(n)
if v.Kind() == reflect.Ptr {
v, _ = indirect(v) // fmt.Fprint handles nil.
}
diff --git a/src/pkg/text/template/exec_test.go b/src/pkg/text/template/exec_test.go
index f4ae50f0e..683e9ac76 100644
--- a/src/pkg/text/template/exec_test.go
+++ b/src/pkg/text/template/exec_test.go
@@ -63,8 +63,11 @@ type T struct {
BinaryFunc func(string, string) string
VariadicFunc func(...string) string
VariadicFuncInt func(int, ...string) string
+ NilOKFunc func(*int) bool
// Template to test evaluation of templates.
Tmpl *Template
+ // Unexported field; cannot be accessed by template.
+ unexported int
}
type U struct {
@@ -125,6 +128,7 @@ var tVal = &T{
BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) },
VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") },
+ NilOKFunc: func(s *int) bool { return s == nil },
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
}
@@ -220,6 +224,7 @@ var execTests = []execTest{
// Trivial cases.
{"empty", "", "", nil, true},
{"text", "some text", "some text", nil, true},
+ {"nil action", "{{nil}}", "", nil, false},
// Ideal constants.
{"ideal int", "{{typeOf 3}}", "int", 0, true},
@@ -228,10 +233,12 @@ var execTests = []execTest{
{"ideal complex", "{{typeOf 1i}}", "complex128", 0, true},
{"ideal int", "{{typeOf " + bigInt + "}}", "int", 0, true},
{"ideal too big", "{{typeOf " + bigUint + "}}", "", 0, false},
+ {"ideal nil without type", "{{nil}}", "", 0, false},
// Fields of structs.
{".X", "-{{.X}}-", "-x-", tVal, true},
{".U.V", "-{{.U.V}}-", "-v-", tVal, true},
+ {".unexported", "{{.unexported}}", "", tVal, false},
// Fields on maps.
{"map .one", "{{.MSI.one}}", "1", tVal, true},
@@ -292,7 +299,8 @@ var execTests = []execTest{
{".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true},
{".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true},
{".Method2(.U16, $x)", "{{if $x := .X}}-{{.Method2 .U16 $x}}{{end}}-", "-Method2: 16 x-", tVal, true},
- {".Method3(nil)", "-{{.Method3 .MXI.unset}}-", "-Method3: <nil>-", tVal, true},
+ {".Method3(nil constant)", "-{{.Method3 nil}}-", "-Method3: <nil>-", tVal, true},
+ {".Method3(nil value)", "-{{.Method3 .MXI.unset}}-", "-Method3: <nil>-", tVal, true},
{"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true},
{"method on chained var",
"{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}",
@@ -303,6 +311,8 @@ var execTests = []execTest{
{"chained method on variable",
"{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}",
"true", tVal, true},
+ {".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true},
+ {".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true},
// Function call builtin.
{".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true},
@@ -321,14 +331,25 @@ var execTests = []execTest{
{".VariadicFuncBad0", "{{call .VariadicFunc 3}}", "", tVal, false},
{".VariadicFuncIntBad0", "{{call .VariadicFuncInt}}", "", tVal, false},
{".VariadicFuncIntBad`", "{{call .VariadicFuncInt `x`}}", "", tVal, false},
+ {".VariadicFuncNilBad", "{{call .VariadicFunc nil}}", "", tVal, false},
// Pipelines.
{"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true},
{"pipeline func", "-{{call .VariadicFunc `llo` | call .VariadicFunc `he` }}-", "-<he+<llo>>-", tVal, true},
+ // Parenthesized expressions
+ {"parens in pipeline", "{{printf `%d %d %d` (1) (2 | add 3) (add 4 (add 5 6))}}", "1 5 15", tVal, true},
+
+ // Parenthesized expressions with field accesses
+ {"parens: $ in paren", "{{($).X}}", "x", tVal, true},
+ {"parens: $.GetU in paren", "{{($.GetU).V}}", "v", tVal, true},
+ {"parens: $ in paren in pipe", "{{($ | echo).X}}", "x", tVal, true},
+ {"parens: spaces and args", `{{(makemap "up" "down" "left" "right").left}}`, "right", tVal, true},
+
// If.
{"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true},
{"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true},
+ {"if nil", "{{if nil}}TRUE{{end}}", "", tVal, false},
{"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
{"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true},
{"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true},
@@ -348,7 +369,8 @@ var execTests = []execTest{
// Print etc.
{"print", `{{print "hello, print"}}`, "hello, print", tVal, true},
- {"print", `{{print 1 2 3}}`, "1 2 3", tVal, true},
+ {"print 123", `{{print 1 2 3}}`, "1 2 3", tVal, true},
+ {"print nil", `{{print nil}}`, "<nil>", tVal, true},
{"println", `{{println 1 2 3}}`, "1 2 3\n", tVal, true},
{"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true},
{"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true},
@@ -387,7 +409,8 @@ var execTests = []execTest{
{"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false},
{"map[one]", "{{index .MSI `one`}}", "1", tVal, true},
{"map[two]", "{{index .MSI `two`}}", "2", tVal, true},
- {"map[NO]", "{{index .MSI `XXX`}}", "", tVal, true},
+ {"map[NO]", "{{index .MSI `XXX`}}", "0", tVal, true},
+ {"map[nil]", "{{index .MSI nil}}", "0", tVal, true},
{"map[WRONG]", "{{index .MSI 10}}", "", tVal, false},
{"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true},
@@ -474,6 +497,8 @@ var execTests = []execTest{
// Pipelined arg was not being type-checked.
{"bug8a", "{{3|oneArg}}", "", tVal, false},
{"bug8b", "{{4|dddArg 3}}", "", tVal, false},
+ // A bug was introduced that broke map lookups for lower-case names.
+ {"bug9", "{{.cause}}", "neglect", map[string]string{"cause": "neglect"}, true},
}
func zeroArgs() string {
@@ -508,6 +533,29 @@ func vfunc(V, *V) string {
return "vfunc"
}
+func add(args ...int) int {
+ sum := 0
+ for _, x := range args {
+ sum += x
+ }
+ return sum
+}
+
+func echo(arg interface{}) interface{} {
+ return arg
+}
+
+func makemap(arg ...string) map[string]string {
+ if len(arg)%2 != 0 {
+ panic("bad makemap")
+ }
+ m := make(map[string]string)
+ for i := 0; i < len(arg); i += 2 {
+ m[arg[i]] = arg[i+1]
+ }
+ return m
+}
+
func stringer(s fmt.Stringer) string {
return s.String()
}
@@ -515,8 +563,11 @@ func stringer(s fmt.Stringer) string {
func testExecute(execTests []execTest, template *Template, t *testing.T) {
b := new(bytes.Buffer)
funcs := FuncMap{
+ "add": add,
"count": count,
"dddArg": dddArg,
+ "echo": echo,
+ "makemap": makemap,
"oneArg": oneArg,
"typeOf": typeOf,
"vfunc": vfunc,
@@ -624,6 +675,32 @@ func TestExecuteError(t *testing.T) {
}
}
+const execErrorText = `line 1
+line 2
+line 3
+{{template "one" .}}
+{{define "one"}}{{template "two" .}}{{end}}
+{{define "two"}}{{template "three" .}}{{end}}
+{{define "three"}}{{index "hi" $}}{{end}}`
+
+// Check that an error from a nested template contains all the relevant information.
+func TestExecError(t *testing.T) {
+ tmpl, err := New("top").Parse(execErrorText)
+ if err != nil {
+ t.Fatal("parse error:", err)
+ }
+ var b bytes.Buffer
+ err = tmpl.Execute(&b, 5) // 5 is out of range indexing "hi"
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ const want = `template: top:7:20: executing "three" at <index "hi" $>: error calling index: index out of range: 5`
+ got := err.Error()
+ if got != want {
+ t.Errorf("expected\n%q\ngot\n%q", want, got)
+ }
+}
+
func TestJSEscaping(t *testing.T) {
testCases := []struct {
in, exp string
@@ -734,3 +811,8 @@ func TestTree(t *testing.T) {
t.Errorf("expected %q got %q", expect, result)
}
}
+
+func TestExecuteOnNewTemplate(t *testing.T) {
+ // This is issue 3872.
+ _ = New("Name").Templates()
+}
diff --git a/src/pkg/text/template/funcs.go b/src/pkg/text/template/funcs.go
index 8fbf0ef50..31549dc45 100644
--- a/src/pkg/text/template/funcs.go
+++ b/src/pkg/text/template/funcs.go
@@ -54,7 +54,7 @@ func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
panic("value for " + name + " not a function")
}
if !goodFunc(v.Type()) {
- panic(fmt.Errorf("can't handle multiple results from method/function %q", name))
+ panic(fmt.Errorf("can't install method/function %q with %d results", name, v.Type().NumOut()))
}
out[name] = v
}
@@ -107,7 +107,7 @@ func index(item interface{}, indices ...interface{}) (interface{}, error) {
return nil, fmt.Errorf("index of nil pointer")
}
switch v.Kind() {
- case reflect.Array, reflect.Slice:
+ case reflect.Array, reflect.Slice, reflect.String:
var x int64
switch index.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@@ -122,16 +122,19 @@ func index(item interface{}, indices ...interface{}) (interface{}, error) {
}
v = v.Index(int(x))
case reflect.Map:
+ if !index.IsValid() {
+ index = reflect.Zero(v.Type().Key())
+ }
if !index.Type().AssignableTo(v.Type().Key()) {
return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
}
if x := v.MapIndex(index); x.IsValid() {
v = x
} else {
- v = reflect.Zero(v.Type().Key())
+ v = reflect.Zero(v.Type().Elem())
}
default:
- return nil, fmt.Errorf("can't index item of type %s", index.Type())
+ return nil, fmt.Errorf("can't index item of type %s", v.Type())
}
}
return v.Interface(), nil
@@ -154,7 +157,7 @@ func length(item interface{}) (int, error) {
// Function invocation
-// call returns the result of evaluating the the first argument as a function.
+// call returns the result of evaluating the first argument as a function.
// The function must return 1 result, or 2 results, the second of which is an error.
func call(fn interface{}, args ...interface{}) (interface{}, error) {
v := reflect.ValueOf(fn)
@@ -187,10 +190,13 @@ func call(fn interface{}, args ...interface{}) (interface{}, error) {
} else {
argType = dddType
}
+ if !value.IsValid() && canBeNil(argType) {
+ value = reflect.Zero(argType)
+ }
if !value.Type().AssignableTo(argType) {
return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
}
- argv[i] = reflect.ValueOf(arg)
+ argv[i] = value
}
result := v.Call(argv)
if len(result) == 2 {
diff --git a/src/pkg/text/template/parse/lex.go b/src/pkg/text/template/parse/lex.go
index 7705c0b88..23c0cf079 100644
--- a/src/pkg/text/template/parse/lex.go
+++ b/src/pkg/text/template/parse/lex.go
@@ -13,8 +13,9 @@ import (
// item represents a token or text string returned from the scanner.
type item struct {
- typ itemType
- val string
+ typ itemType // The type of this item.
+ pos Pos // The starting position, in bytes, of this item in the input string.
+ val string // The value of this item.
}
func (i item) String() string {
@@ -42,65 +43,32 @@ const (
itemComplex // complex constant (1+2i); imaginary is just a number
itemColonEquals // colon-equals (':=') introducing a declaration
itemEOF
- itemField // alphanumeric identifier, starting with '.', possibly chained ('.x.y')
- itemIdentifier // alphanumeric identifier
+ itemField // alphanumeric identifier starting with '.'
+ itemIdentifier // alphanumeric identifier not starting with '.'
itemLeftDelim // left action delimiter
+ itemLeftParen // '(' inside action
itemNumber // simple number, including imaginary
itemPipe // pipe symbol
itemRawString // raw quoted string (includes quotes)
itemRightDelim // right action delimiter
+ itemRightParen // ')' inside action
+ itemSpace // run of spaces separating arguments
itemString // quoted string (includes quotes)
itemText // plain text
- itemVariable // variable starting with '$', such as '$' or '$1' or '$hello'.
+ itemVariable // variable starting with '$', such as '$' or '$1' or '$hello'
// Keywords appear after all the rest.
itemKeyword // used only to delimit the keywords
- itemDot // the cursor, spelled '.'.
+ itemDot // the cursor, spelled '.'
itemDefine // define keyword
itemElse // else keyword
itemEnd // end keyword
itemIf // if keyword
+ itemNil // the untyped nil constant, easiest to treat as a keyword
itemRange // range keyword
itemTemplate // template keyword
itemWith // with keyword
)
-// Make the types prettyprint.
-var itemName = map[itemType]string{
- itemError: "error",
- itemBool: "bool",
- itemChar: "char",
- itemCharConstant: "charconst",
- itemComplex: "complex",
- itemColonEquals: ":=",
- itemEOF: "EOF",
- itemField: "field",
- itemIdentifier: "identifier",
- itemLeftDelim: "left delim",
- itemNumber: "number",
- itemPipe: "pipe",
- itemRawString: "raw string",
- itemRightDelim: "right delim",
- itemString: "string",
- itemVariable: "variable",
- // keywords
- itemDot: ".",
- itemDefine: "define",
- itemElse: "else",
- itemIf: "if",
- itemEnd: "end",
- itemRange: "range",
- itemTemplate: "template",
- itemWith: "with",
-}
-
-func (i itemType) String() string {
- s := itemName[i]
- if s == "" {
- return fmt.Sprintf("item%d", int(i))
- }
- return s
-}
-
var key = map[string]itemType{
".": itemDot,
"define": itemDefine,
@@ -108,6 +76,7 @@ var key = map[string]itemType{
"end": itemEnd,
"if": itemIf,
"range": itemRange,
+ "nil": itemNil,
"template": itemTemplate,
"with": itemWith,
}
@@ -119,24 +88,27 @@ type stateFn func(*lexer) stateFn
// lexer holds the state of the scanner.
type lexer struct {
- name string // the name of the input; used only for error reports.
- input string // the string being scanned.
- leftDelim string // start of action.
- rightDelim string // end of action.
- state stateFn // the next lexing function to enter.
- pos int // current position in the input.
- start int // start position of this item.
- width int // width of last rune read from input.
- items chan item // channel of scanned items.
+ name string // the name of the input; used only for error reports
+ input string // the string being scanned
+ leftDelim string // start of action
+ rightDelim string // end of action
+ state stateFn // the next lexing function to enter
+ pos Pos // current position in the input
+ start Pos // start position of this item
+ width Pos // width of last rune read from input
+ lastPos Pos // position of most recent item returned by nextItem
+ items chan item // channel of scanned items
+ parenDepth int // nesting depth of ( ) exprs
}
// next returns the next rune in the input.
-func (l *lexer) next() (r rune) {
- if l.pos >= len(l.input) {
+func (l *lexer) next() rune {
+ if int(l.pos) >= len(l.input) {
l.width = 0
return eof
}
- r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
+ r, w := utf8.DecodeRuneInString(l.input[l.pos:])
+ l.width = Pos(w)
l.pos += l.width
return r
}
@@ -155,7 +127,7 @@ func (l *lexer) backup() {
// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
- l.items <- item{t, l.input[l.start:l.pos]}
+ l.items <- item{t, l.start, l.input[l.start:l.pos]}
l.start = l.pos
}
@@ -180,30 +152,25 @@ func (l *lexer) acceptRun(valid string) {
l.backup()
}
-// lineNumber reports which line we're on. Doing it this way
+// lineNumber reports which line we're on, based on the position of
+// the previous item returned by nextItem. Doing it this way
// means we don't have to worry about peek double counting.
func (l *lexer) lineNumber() int {
- return 1 + strings.Count(l.input[:l.pos], "\n")
+ return 1 + strings.Count(l.input[:l.lastPos], "\n")
}
-// error returns an error token and terminates the scan by passing
+// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
- l.items <- item{itemError, fmt.Sprintf(format, args...)}
+ l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
return nil
}
// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
- for {
- select {
- case item := <-l.items:
- return item
- default:
- l.state = l.state(l)
- }
- }
- panic("not reached")
+ item := <-l.items
+ l.lastPos = item.pos
+ return item
}
// lex creates a new scanner for the input string.
@@ -219,12 +186,19 @@ func lex(name, input, left, right string) *lexer {
input: input,
leftDelim: left,
rightDelim: right,
- state: lexText,
- items: make(chan item, 2), // Two items of buffering is sufficient for all state functions
+ items: make(chan item),
}
+ go l.run()
return l
}
+// run runs the state machine for the lexer.
+func (l *lexer) run() {
+ for l.state = lexText; l.state != nil; {
+ l.state = l.state(l)
+ }
+}
+
// state functions
const (
@@ -257,28 +231,30 @@ func lexText(l *lexer) stateFn {
// lexLeftDelim scans the left delimiter, which is known to be present.
func lexLeftDelim(l *lexer) stateFn {
- if strings.HasPrefix(l.input[l.pos:], l.leftDelim+leftComment) {
+ l.pos += Pos(len(l.leftDelim))
+ if strings.HasPrefix(l.input[l.pos:], leftComment) {
return lexComment
}
- l.pos += len(l.leftDelim)
l.emit(itemLeftDelim)
+ l.parenDepth = 0
return lexInsideAction
}
// lexComment scans a comment. The left comment marker is known to be present.
func lexComment(l *lexer) stateFn {
+ l.pos += Pos(len(leftComment))
i := strings.Index(l.input[l.pos:], rightComment+l.rightDelim)
if i < 0 {
return l.errorf("unclosed comment")
}
- l.pos += i + len(rightComment) + len(l.rightDelim)
+ l.pos += Pos(i + len(rightComment) + len(l.rightDelim))
l.ignore()
return lexText
}
// lexRightDelim scans the right delimiter, which is known to be present.
func lexRightDelim(l *lexer) stateFn {
- l.pos += len(l.rightDelim)
+ l.pos += Pos(len(l.rightDelim))
l.emit(itemRightDelim)
return lexText
}
@@ -286,16 +262,19 @@ func lexRightDelim(l *lexer) stateFn {
// lexInsideAction scans the elements inside action delimiters.
func lexInsideAction(l *lexer) stateFn {
// Either number, quoted string, or identifier.
- // Spaces separate and are ignored.
+ // Spaces separate arguments; runs of spaces turn into itemSpace.
// Pipe symbols separate and are emitted.
if strings.HasPrefix(l.input[l.pos:], l.rightDelim) {
- return lexRightDelim
+ if l.parenDepth == 0 {
+ return lexRightDelim
+ }
+ return l.errorf("unclosed left paren")
}
switch r := l.next(); {
- case r == eof || r == '\n':
+ case r == eof || isEndOfLine(r):
return l.errorf("unclosed action")
case isSpace(r):
- l.ignore()
+ return lexSpace
case r == ':':
if l.next() != '=' {
return l.errorf("expected :=")
@@ -308,15 +287,15 @@ func lexInsideAction(l *lexer) stateFn {
case r == '`':
return lexRawQuote
case r == '$':
- return lexIdentifier
+ return lexVariable
case r == '\'':
return lexChar
case r == '.':
// special look-ahead for ".field" so we don't break l.backup().
- if l.pos < len(l.input) {
+ if l.pos < Pos(len(l.input)) {
r := l.input[l.pos]
if r < '0' || '9' < r {
- return lexIdentifier // itemDot comes from the keyword table.
+ return lexField
}
}
fallthrough // '.' can start a number.
@@ -326,6 +305,17 @@ func lexInsideAction(l *lexer) stateFn {
case isAlphaNumeric(r):
l.backup()
return lexIdentifier
+ case r == '(':
+ l.emit(itemLeftParen)
+ l.parenDepth++
+ return lexInsideAction
+ case r == ')':
+ l.emit(itemRightParen)
+ l.parenDepth--
+ if l.parenDepth < 0 {
+ return l.errorf("unexpected right paren %#U", r)
+ }
+ return lexInsideAction
case r <= unicode.MaxASCII && unicode.IsPrint(r):
l.emit(itemChar)
return lexInsideAction
@@ -335,28 +325,34 @@ func lexInsideAction(l *lexer) stateFn {
return lexInsideAction
}
-// lexIdentifier scans an alphanumeric or field.
+// lexSpace scans a run of space characters.
+// One space has already been seen.
+func lexSpace(l *lexer) stateFn {
+ for isSpace(l.peek()) {
+ l.next()
+ }
+ l.emit(itemSpace)
+ return lexInsideAction
+}
+
+// lexIdentifier scans an alphanumeric.
func lexIdentifier(l *lexer) stateFn {
Loop:
for {
switch r := l.next(); {
case isAlphaNumeric(r):
// absorb.
- case r == '.' && (l.input[l.start] == '.' || l.input[l.start] == '$'):
- // field chaining; absorb into one token.
default:
l.backup()
word := l.input[l.start:l.pos]
if !l.atTerminator() {
- return l.errorf("unexpected character %+U", r)
+ return l.errorf("bad character %#U", r)
}
switch {
case key[word] > itemKeyword:
l.emit(key[word])
case word[0] == '.':
l.emit(itemField)
- case word[0] == '$':
- l.emit(itemVariable)
case word == "true", word == "false":
l.emit(itemBool)
default:
@@ -368,17 +364,59 @@ Loop:
return lexInsideAction
}
+// lexField scans a field: .Alphanumeric.
+// The . has been scanned.
+func lexField(l *lexer) stateFn {
+ return lexFieldOrVariable(l, itemField)
+}
+
+// lexVariable scans a Variable: $Alphanumeric.
+// The $ has been scanned.
+func lexVariable(l *lexer) stateFn {
+ if l.atTerminator() { // Nothing interesting follows -> "$".
+ l.emit(itemVariable)
+ return lexInsideAction
+ }
+ return lexFieldOrVariable(l, itemVariable)
+}
+
+// lexVariable scans a field or variable: [.$]Alphanumeric.
+// The . or $ has been scanned.
+func lexFieldOrVariable(l *lexer, typ itemType) stateFn {
+ if l.atTerminator() { // Nothing interesting follows -> "." or "$".
+ if typ == itemVariable {
+ l.emit(itemVariable)
+ } else {
+ l.emit(itemDot)
+ }
+ return lexInsideAction
+ }
+ var r rune
+ for {
+ r = l.next()
+ if !isAlphaNumeric(r) {
+ l.backup()
+ break
+ }
+ }
+ if !l.atTerminator() {
+ return l.errorf("bad character %#U", r)
+ }
+ l.emit(typ)
+ return lexInsideAction
+}
+
// atTerminator reports whether the input is at valid termination character to
-// appear after an identifier. Mostly to catch cases like "$x+2" not being
-// acceptable without a space, in case we decide one day to implement
-// arithmetic.
+// appear after an identifier. Breaks .X.Y into two pieces. Also catches cases
+// like "$x+2" not being acceptable without a space, in case we decide one
+// day to implement arithmetic.
func (l *lexer) atTerminator() bool {
r := l.peek()
- if isSpace(r) {
+ if isSpace(r) || isEndOfLine(r) {
return true
}
switch r {
- case eof, ',', '|', ':':
+ case eof, '.', ',', '|', ':', ')', '(':
return true
}
// Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will
@@ -391,7 +429,7 @@ func (l *lexer) atTerminator() bool {
}
// lexChar scans a character constant. The initial quote is already
-// scanned. Syntax checking is done by the parse.
+// scanned. Syntax checking is done by the parser.
func lexChar(l *lexer) stateFn {
Loop:
for {
@@ -411,7 +449,7 @@ Loop:
return lexInsideAction
}
-// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This
+// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This
// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
// and "089" - but when it's wrong the input is invalid and the parser (via
// strconv) will notice.
@@ -420,7 +458,7 @@ func lexNumber(l *lexer) stateFn {
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
}
if sign := l.peek(); sign == '+' || sign == '-' {
- // Complex: 1+2i. No spaces, must end in 'i'.
+ // Complex: 1+2i. No spaces, must end in 'i'.
if !l.scanNumber() || l.input[l.pos-1] != 'i' {
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
}
@@ -494,11 +532,12 @@ Loop:
// isSpace reports whether r is a space character.
func isSpace(r rune) bool {
- switch r {
- case ' ', '\t', '\n', '\r':
- return true
- }
- return false
+ return r == ' ' || r == '\t'
+}
+
+// isEndOfLine reports whether r is an end-of-line character.
+func isEndOfLine(r rune) bool {
+ return r == '\r' || r == '\n'
}
// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
diff --git a/src/pkg/text/template/parse/lex_test.go b/src/pkg/text/template/parse/lex_test.go
index 6ee1b4701..d2264c991 100644
--- a/src/pkg/text/template/parse/lex_test.go
+++ b/src/pkg/text/template/parse/lex_test.go
@@ -5,10 +5,52 @@
package parse
import (
- "reflect"
+ "fmt"
"testing"
)
+// Make the types prettyprint.
+var itemName = map[itemType]string{
+ itemError: "error",
+ itemBool: "bool",
+ itemChar: "char",
+ itemCharConstant: "charconst",
+ itemComplex: "complex",
+ itemColonEquals: ":=",
+ itemEOF: "EOF",
+ itemField: "field",
+ itemIdentifier: "identifier",
+ itemLeftDelim: "left delim",
+ itemLeftParen: "(",
+ itemNumber: "number",
+ itemPipe: "pipe",
+ itemRawString: "raw string",
+ itemRightDelim: "right delim",
+ itemRightParen: ")",
+ itemSpace: "space",
+ itemString: "string",
+ itemVariable: "variable",
+
+ // keywords
+ itemDot: ".",
+ itemDefine: "define",
+ itemElse: "else",
+ itemIf: "if",
+ itemEnd: "end",
+ itemNil: "nil",
+ itemRange: "range",
+ itemTemplate: "template",
+ itemWith: "with",
+}
+
+func (i itemType) String() string {
+ s := itemName[i]
+ if s == "" {
+ return fmt.Sprintf("item%d", int(i))
+ }
+ return s
+}
+
type lexTest struct {
name string
input string
@@ -16,173 +58,265 @@ type lexTest struct {
}
var (
- tEOF = item{itemEOF, ""}
- tLeft = item{itemLeftDelim, "{{"}
- tRight = item{itemRightDelim, "}}"}
- tRange = item{itemRange, "range"}
- tPipe = item{itemPipe, "|"}
- tFor = item{itemIdentifier, "for"}
- tQuote = item{itemString, `"abc \n\t\" "`}
+ tEOF = item{itemEOF, 0, ""}
+ tFor = item{itemIdentifier, 0, "for"}
+ tLeft = item{itemLeftDelim, 0, "{{"}
+ tLpar = item{itemLeftParen, 0, "("}
+ tPipe = item{itemPipe, 0, "|"}
+ tQuote = item{itemString, 0, `"abc \n\t\" "`}
+ tRange = item{itemRange, 0, "range"}
+ tRight = item{itemRightDelim, 0, "}}"}
+ tRpar = item{itemRightParen, 0, ")"}
+ tSpace = item{itemSpace, 0, " "}
raw = "`" + `abc\n\t\" ` + "`"
- tRawQuote = item{itemRawString, raw}
+ tRawQuote = item{itemRawString, 0, raw}
)
var lexTests = []lexTest{
{"empty", "", []item{tEOF}},
- {"spaces", " \t\n", []item{{itemText, " \t\n"}, tEOF}},
- {"text", `now is the time`, []item{{itemText, "now is the time"}, tEOF}},
+ {"spaces", " \t\n", []item{{itemText, 0, " \t\n"}, tEOF}},
+ {"text", `now is the time`, []item{{itemText, 0, "now is the time"}, tEOF}},
{"text with comment", "hello-{{/* this is a comment */}}-world", []item{
- {itemText, "hello-"},
- {itemText, "-world"},
+ {itemText, 0, "hello-"},
+ {itemText, 0, "-world"},
tEOF,
}},
- {"punctuation", "{{,@%}}", []item{
+ {"punctuation", "{{,@% }}", []item{
tLeft,
- {itemChar, ","},
- {itemChar, "@"},
- {itemChar, "%"},
+ {itemChar, 0, ","},
+ {itemChar, 0, "@"},
+ {itemChar, 0, "%"},
+ tSpace,
+ tRight,
+ tEOF,
+ }},
+ {"parens", "{{((3))}}", []item{
+ tLeft,
+ tLpar,
+ tLpar,
+ {itemNumber, 0, "3"},
+ tRpar,
+ tRpar,
tRight,
tEOF,
}},
{"empty action", `{{}}`, []item{tLeft, tRight, tEOF}},
- {"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}},
+ {"for", `{{for}}`, []item{tLeft, tFor, tRight, tEOF}},
{"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
{"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}},
{"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{
tLeft,
- {itemNumber, "1"},
- {itemNumber, "02"},
- {itemNumber, "0x14"},
- {itemNumber, "-7.2i"},
- {itemNumber, "1e3"},
- {itemNumber, "+1.2e-4"},
- {itemNumber, "4.2i"},
- {itemComplex, "1+2i"},
+ {itemNumber, 0, "1"},
+ tSpace,
+ {itemNumber, 0, "02"},
+ tSpace,
+ {itemNumber, 0, "0x14"},
+ tSpace,
+ {itemNumber, 0, "-7.2i"},
+ tSpace,
+ {itemNumber, 0, "1e3"},
+ tSpace,
+ {itemNumber, 0, "+1.2e-4"},
+ tSpace,
+ {itemNumber, 0, "4.2i"},
+ tSpace,
+ {itemComplex, 0, "1+2i"},
tRight,
tEOF,
}},
{"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{
tLeft,
- {itemCharConstant, `'a'`},
- {itemCharConstant, `'\n'`},
- {itemCharConstant, `'\''`},
- {itemCharConstant, `'\\'`},
- {itemCharConstant, `'\u00FF'`},
- {itemCharConstant, `'\xFF'`},
- {itemCharConstant, `'本'`},
+ {itemCharConstant, 0, `'a'`},
+ tSpace,
+ {itemCharConstant, 0, `'\n'`},
+ tSpace,
+ {itemCharConstant, 0, `'\''`},
+ tSpace,
+ {itemCharConstant, 0, `'\\'`},
+ tSpace,
+ {itemCharConstant, 0, `'\u00FF'`},
+ tSpace,
+ {itemCharConstant, 0, `'\xFF'`},
+ tSpace,
+ {itemCharConstant, 0, `'本'`},
tRight,
tEOF,
}},
{"bools", "{{true false}}", []item{
tLeft,
- {itemBool, "true"},
- {itemBool, "false"},
+ {itemBool, 0, "true"},
+ tSpace,
+ {itemBool, 0, "false"},
tRight,
tEOF,
}},
{"dot", "{{.}}", []item{
tLeft,
- {itemDot, "."},
+ {itemDot, 0, "."},
tRight,
tEOF,
}},
- {"dots", "{{.x . .2 .x.y}}", []item{
+ {"nil", "{{nil}}", []item{
tLeft,
- {itemField, ".x"},
- {itemDot, "."},
- {itemNumber, ".2"},
- {itemField, ".x.y"},
+ {itemNil, 0, "nil"},
+ tRight,
+ tEOF,
+ }},
+ {"dots", "{{.x . .2 .x.y.z}}", []item{
+ tLeft,
+ {itemField, 0, ".x"},
+ tSpace,
+ {itemDot, 0, "."},
+ tSpace,
+ {itemNumber, 0, ".2"},
+ tSpace,
+ {itemField, 0, ".x"},
+ {itemField, 0, ".y"},
+ {itemField, 0, ".z"},
tRight,
tEOF,
}},
{"keywords", "{{range if else end with}}", []item{
tLeft,
- {itemRange, "range"},
- {itemIf, "if"},
- {itemElse, "else"},
- {itemEnd, "end"},
- {itemWith, "with"},
+ {itemRange, 0, "range"},
+ tSpace,
+ {itemIf, 0, "if"},
+ tSpace,
+ {itemElse, 0, "else"},
+ tSpace,
+ {itemEnd, 0, "end"},
+ tSpace,
+ {itemWith, 0, "with"},
tRight,
tEOF,
}},
{"variables", "{{$c := printf $ $hello $23 $ $var.Field .Method}}", []item{
tLeft,
- {itemVariable, "$c"},
- {itemColonEquals, ":="},
- {itemIdentifier, "printf"},
- {itemVariable, "$"},
- {itemVariable, "$hello"},
- {itemVariable, "$23"},
- {itemVariable, "$"},
- {itemVariable, "$var.Field"},
- {itemField, ".Method"},
+ {itemVariable, 0, "$c"},
+ tSpace,
+ {itemColonEquals, 0, ":="},
+ tSpace,
+ {itemIdentifier, 0, "printf"},
+ tSpace,
+ {itemVariable, 0, "$"},
+ tSpace,
+ {itemVariable, 0, "$hello"},
+ tSpace,
+ {itemVariable, 0, "$23"},
+ tSpace,
+ {itemVariable, 0, "$"},
+ tSpace,
+ {itemVariable, 0, "$var"},
+ {itemField, 0, ".Field"},
+ tSpace,
+ {itemField, 0, ".Method"},
+ tRight,
+ tEOF,
+ }},
+ {"variable invocation", "{{$x 23}}", []item{
+ tLeft,
+ {itemVariable, 0, "$x"},
+ tSpace,
+ {itemNumber, 0, "23"},
tRight,
tEOF,
}},
{"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{
- {itemText, "intro "},
+ {itemText, 0, "intro "},
tLeft,
- {itemIdentifier, "echo"},
- {itemIdentifier, "hi"},
- {itemNumber, "1.2"},
+ {itemIdentifier, 0, "echo"},
+ tSpace,
+ {itemIdentifier, 0, "hi"},
+ tSpace,
+ {itemNumber, 0, "1.2"},
+ tSpace,
tPipe,
- {itemIdentifier, "noargs"},
+ {itemIdentifier, 0, "noargs"},
tPipe,
- {itemIdentifier, "args"},
- {itemNumber, "1"},
- {itemString, `"hi"`},
+ {itemIdentifier, 0, "args"},
+ tSpace,
+ {itemNumber, 0, "1"},
+ tSpace,
+ {itemString, 0, `"hi"`},
tRight,
- {itemText, " outro"},
+ {itemText, 0, " outro"},
tEOF,
}},
{"declaration", "{{$v := 3}}", []item{
tLeft,
- {itemVariable, "$v"},
- {itemColonEquals, ":="},
- {itemNumber, "3"},
+ {itemVariable, 0, "$v"},
+ tSpace,
+ {itemColonEquals, 0, ":="},
+ tSpace,
+ {itemNumber, 0, "3"},
tRight,
tEOF,
}},
{"2 declarations", "{{$v , $w := 3}}", []item{
tLeft,
- {itemVariable, "$v"},
- {itemChar, ","},
- {itemVariable, "$w"},
- {itemColonEquals, ":="},
- {itemNumber, "3"},
+ {itemVariable, 0, "$v"},
+ tSpace,
+ {itemChar, 0, ","},
+ tSpace,
+ {itemVariable, 0, "$w"},
+ tSpace,
+ {itemColonEquals, 0, ":="},
+ tSpace,
+ {itemNumber, 0, "3"},
+ tRight,
+ tEOF,
+ }},
+ {"field of parenthesized expression", "{{(.X).Y}}", []item{
+ tLeft,
+ tLpar,
+ {itemField, 0, ".X"},
+ tRpar,
+ {itemField, 0, ".Y"},
tRight,
tEOF,
}},
// errors
{"badchar", "#{{\x01}}", []item{
- {itemText, "#"},
+ {itemText, 0, "#"},
tLeft,
- {itemError, "unrecognized character in action: U+0001"},
+ {itemError, 0, "unrecognized character in action: U+0001"},
}},
{"unclosed action", "{{\n}}", []item{
tLeft,
- {itemError, "unclosed action"},
+ {itemError, 0, "unclosed action"},
}},
{"EOF in action", "{{range", []item{
tLeft,
tRange,
- {itemError, "unclosed action"},
+ {itemError, 0, "unclosed action"},
}},
{"unclosed quote", "{{\"\n\"}}", []item{
tLeft,
- {itemError, "unterminated quoted string"},
+ {itemError, 0, "unterminated quoted string"},
}},
{"unclosed raw quote", "{{`xx\n`}}", []item{
tLeft,
- {itemError, "unterminated raw quoted string"},
+ {itemError, 0, "unterminated raw quoted string"},
}},
{"unclosed char constant", "{{'\n}}", []item{
tLeft,
- {itemError, "unterminated character constant"},
+ {itemError, 0, "unterminated character constant"},
}},
{"bad number", "{{3k}}", []item{
tLeft,
- {itemError, `bad number syntax: "3k"`},
+ {itemError, 0, `bad number syntax: "3k"`},
+ }},
+ {"unclosed paren", "{{(3}}", []item{
+ tLeft,
+ tLpar,
+ {itemNumber, 0, "3"},
+ {itemError, 0, `unclosed left paren`},
+ }},
+ {"extra right paren", "{{3)}}", []item{
+ tLeft,
+ {itemNumber, 0, "3"},
+ tRpar,
+ {itemError, 0, `unexpected right paren U+0029 ')'`},
}},
// Fixed bugs
@@ -198,6 +332,10 @@ var lexTests = []lexTest{
tRight,
tEOF,
}},
+ {"text with bad comment", "hello-{{/*/}}-world", []item{
+ {itemText, 0, "hello-"},
+ {itemError, 0, `unclosed comment`},
+ }},
}
// collect gathers the emitted items into a slice.
@@ -213,11 +351,29 @@ func collect(t *lexTest, left, right string) (items []item) {
return
}
+func equal(i1, i2 []item, checkPos bool) bool {
+ if len(i1) != len(i2) {
+ return false
+ }
+ for k := range i1 {
+ if i1[k].typ != i2[k].typ {
+ return false
+ }
+ if i1[k].val != i2[k].val {
+ return false
+ }
+ if checkPos && i1[k].pos != i2[k].pos {
+ return false
+ }
+ }
+ return true
+}
+
func TestLex(t *testing.T) {
for _, test := range lexTests {
items := collect(&test, "", "")
- if !reflect.DeepEqual(items, test.items) {
- t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
+ if !equal(items, test.items, false) {
+ t.Errorf("%s: got\n\t%+v\nexpected\n\t%v", test.name, items, test.items)
}
}
}
@@ -226,32 +382,74 @@ func TestLex(t *testing.T) {
var lexDelimTests = []lexTest{
{"punctuation", "$$,@%{{}}@@", []item{
tLeftDelim,
- {itemChar, ","},
- {itemChar, "@"},
- {itemChar, "%"},
- {itemChar, "{"},
- {itemChar, "{"},
- {itemChar, "}"},
- {itemChar, "}"},
+ {itemChar, 0, ","},
+ {itemChar, 0, "@"},
+ {itemChar, 0, "%"},
+ {itemChar, 0, "{"},
+ {itemChar, 0, "{"},
+ {itemChar, 0, "}"},
+ {itemChar, 0, "}"},
tRightDelim,
tEOF,
}},
{"empty action", `$$@@`, []item{tLeftDelim, tRightDelim, tEOF}},
- {"for", `$$for @@`, []item{tLeftDelim, tFor, tRightDelim, tEOF}},
+ {"for", `$$for@@`, []item{tLeftDelim, tFor, tRightDelim, tEOF}},
{"quote", `$$"abc \n\t\" "@@`, []item{tLeftDelim, tQuote, tRightDelim, tEOF}},
{"raw quote", "$$" + raw + "@@", []item{tLeftDelim, tRawQuote, tRightDelim, tEOF}},
}
var (
- tLeftDelim = item{itemLeftDelim, "$$"}
- tRightDelim = item{itemRightDelim, "@@"}
+ tLeftDelim = item{itemLeftDelim, 0, "$$"}
+ tRightDelim = item{itemRightDelim, 0, "@@"}
)
func TestDelims(t *testing.T) {
for _, test := range lexDelimTests {
items := collect(&test, "$$", "@@")
- if !reflect.DeepEqual(items, test.items) {
+ if !equal(items, test.items, false) {
+ t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
+ }
+ }
+}
+
+var lexPosTests = []lexTest{
+ {"empty", "", []item{tEOF}},
+ {"punctuation", "{{,@%#}}", []item{
+ {itemLeftDelim, 0, "{{"},
+ {itemChar, 2, ","},
+ {itemChar, 3, "@"},
+ {itemChar, 4, "%"},
+ {itemChar, 5, "#"},
+ {itemRightDelim, 6, "}}"},
+ {itemEOF, 8, ""},
+ }},
+ {"sample", "0123{{hello}}xyz", []item{
+ {itemText, 0, "0123"},
+ {itemLeftDelim, 4, "{{"},
+ {itemIdentifier, 6, "hello"},
+ {itemRightDelim, 11, "}}"},
+ {itemText, 13, "xyz"},
+ {itemEOF, 16, ""},
+ }},
+}
+
+// The other tests don't check position, to make the test cases easier to construct.
+// This one does.
+func TestPos(t *testing.T) {
+ for _, test := range lexPosTests {
+ items := collect(&test, "", "")
+ if !equal(items, test.items, true) {
t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
+ if len(items) == len(test.items) {
+ // Detailed print; avoid item.String() to expose the position value.
+ for i := range items {
+ if !equal(items[i:i+1], test.items[i:i+1], true) {
+ i1 := items[i]
+ i2 := test.items[i]
+ t.Errorf("\t#%d: got {%v %d %q} expected {%v %d %q}", i, i1.typ, i1.pos, i1.val, i2.typ, i2.pos, i2.val)
+ }
+ }
+ }
}
}
}
diff --git a/src/pkg/text/template/parse/node.go b/src/pkg/text/template/parse/node.go
index db645624c..9d0d09eb5 100644
--- a/src/pkg/text/template/parse/node.go
+++ b/src/pkg/text/template/parse/node.go
@@ -13,7 +13,9 @@ import (
"strings"
)
-// A node is an element in the parse tree. The interface is trivial.
+// A Node is an element in the parse tree. The interface is trivial.
+// The interface contains an unexported method so that only
+// types local to this package can satisfy it.
type Node interface {
Type() NodeType
String() string
@@ -21,11 +23,27 @@ type Node interface {
// To avoid type assertions, some XxxNodes also have specialized
// CopyXxx methods that return *XxxNode.
Copy() Node
+ Position() Pos // byte position of start of node in full original input string
+ // Make sure only functions in this package can create Nodes.
+ unexported()
}
// NodeType identifies the type of a parse tree node.
type NodeType int
+// Pos represents a byte position in the original input text from which
+// this template was parsed.
+type Pos int
+
+func (p Pos) Position() Pos {
+ return p
+}
+
+// unexported keeps Node implementations local to the package.
+// All implementations embed Pos, so this takes care of it.
+func (Pos) unexported() {
+}
+
// Type returns itself and provides an easy default implementation
// for embedding in a Node. Embedded in all non-trivial Nodes.
func (t NodeType) Type() NodeType {
@@ -34,8 +52,9 @@ func (t NodeType) Type() NodeType {
const (
NodeText NodeType = iota // Plain text.
- NodeAction // A simple action such as field evaluation.
+ NodeAction // A non-control action such as a field evaluation.
NodeBool // A boolean constant.
+ NodeChain // A sequence of field accesses.
NodeCommand // An element of a pipeline.
NodeDot // The cursor, dot.
nodeElse // An else action. Not added to tree.
@@ -44,6 +63,7 @@ const (
NodeIdentifier // An identifier; always a function name.
NodeIf // An if action.
NodeList // A list of Nodes.
+ NodeNil // An untyped nil constant.
NodeNumber // A numerical constant.
NodePipe // A pipeline of commands.
NodeRange // A range action.
@@ -58,11 +78,12 @@ const (
// ListNode holds a sequence of nodes.
type ListNode struct {
NodeType
+ Pos
Nodes []Node // The element nodes in lexical order.
}
-func newList() *ListNode {
- return &ListNode{NodeType: NodeList}
+func newList(pos Pos) *ListNode {
+ return &ListNode{NodeType: NodeList, Pos: pos}
}
func (l *ListNode) append(n Node) {
@@ -81,7 +102,7 @@ func (l *ListNode) CopyList() *ListNode {
if l == nil {
return l
}
- n := newList()
+ n := newList(l.Pos)
for _, elem := range l.Nodes {
n.append(elem.Copy())
}
@@ -95,11 +116,12 @@ func (l *ListNode) Copy() Node {
// TextNode holds plain text.
type TextNode struct {
NodeType
+ Pos
Text []byte // The text; may span newlines.
}
-func newText(text string) *TextNode {
- return &TextNode{NodeType: NodeText, Text: []byte(text)}
+func newText(pos Pos, text string) *TextNode {
+ return &TextNode{NodeType: NodeText, Pos: pos, Text: []byte(text)}
}
func (t *TextNode) String() string {
@@ -113,13 +135,14 @@ func (t *TextNode) Copy() Node {
// PipeNode holds a pipeline with optional declaration
type PipeNode struct {
NodeType
- Line int // The line number in the input.
+ Pos
+ Line int // The line number in the input (deprecated; kept for compatibility)
Decl []*VariableNode // Variable declarations in lexical order.
Cmds []*CommandNode // The commands in lexical order.
}
-func newPipeline(line int, decl []*VariableNode) *PipeNode {
- return &PipeNode{NodeType: NodePipe, Line: line, Decl: decl}
+func newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode {
+ return &PipeNode{NodeType: NodePipe, Pos: pos, Line: line, Decl: decl}
}
func (p *PipeNode) append(command *CommandNode) {
@@ -154,7 +177,7 @@ func (p *PipeNode) CopyPipe() *PipeNode {
for _, d := range p.Decl {
decl = append(decl, d.Copy().(*VariableNode))
}
- n := newPipeline(p.Line, decl)
+ n := newPipeline(p.Pos, p.Line, decl)
for _, c := range p.Cmds {
n.append(c.Copy().(*CommandNode))
}
@@ -167,15 +190,16 @@ func (p *PipeNode) Copy() Node {
// ActionNode holds an action (something bounded by delimiters).
// Control actions have their own nodes; ActionNode represents simple
-// ones such as field evaluations.
+// ones such as field evaluations and parenthesized pipelines.
type ActionNode struct {
NodeType
- Line int // The line number in the input.
+ Pos
+ Line int // The line number in the input (deprecated; kept for compatibility)
Pipe *PipeNode // The pipeline in the action.
}
-func newAction(line int, pipe *PipeNode) *ActionNode {
- return &ActionNode{NodeType: NodeAction, Line: line, Pipe: pipe}
+func newAction(pos Pos, line int, pipe *PipeNode) *ActionNode {
+ return &ActionNode{NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe}
}
func (a *ActionNode) String() string {
@@ -184,18 +208,19 @@ func (a *ActionNode) String() string {
}
func (a *ActionNode) Copy() Node {
- return newAction(a.Line, a.Pipe.CopyPipe())
+ return newAction(a.Pos, a.Line, a.Pipe.CopyPipe())
}
// CommandNode holds a command (a pipeline inside an evaluating action).
type CommandNode struct {
NodeType
+ Pos
Args []Node // Arguments in lexical order: Identifier, field, or constant.
}
-func newCommand() *CommandNode {
- return &CommandNode{NodeType: NodeCommand}
+func newCommand(pos Pos) *CommandNode {
+ return &CommandNode{NodeType: NodeCommand, Pos: pos}
}
func (c *CommandNode) append(arg Node) {
@@ -208,6 +233,10 @@ func (c *CommandNode) String() string {
if i > 0 {
s += " "
}
+ if arg, ok := arg.(*PipeNode); ok {
+ s += "(" + arg.String() + ")"
+ continue
+ }
s += arg.String()
}
return s
@@ -217,7 +246,7 @@ func (c *CommandNode) Copy() Node {
if c == nil {
return c
}
- n := newCommand()
+ n := newCommand(c.Pos)
for _, c := range c.Args {
n.append(c.Copy())
}
@@ -227,6 +256,7 @@ func (c *CommandNode) Copy() Node {
// IdentifierNode holds an identifier.
type IdentifierNode struct {
NodeType
+ Pos
Ident string // The identifier's name.
}
@@ -235,23 +265,32 @@ func NewIdentifier(ident string) *IdentifierNode {
return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident}
}
+// SetPos sets the position. NewIdentifier is a public method so we can't modify its signature.
+// Chained for convenience.
+// TODO: fix one day?
+func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode {
+ i.Pos = pos
+ return i
+}
+
func (i *IdentifierNode) String() string {
return i.Ident
}
func (i *IdentifierNode) Copy() Node {
- return NewIdentifier(i.Ident)
+ return NewIdentifier(i.Ident).SetPos(i.Pos)
}
-// VariableNode holds a list of variable names. The dollar sign is
-// part of the name.
+// VariableNode holds a list of variable names, possibly with chained field
+// accesses. The dollar sign is part of the (first) name.
type VariableNode struct {
NodeType
- Ident []string // Variable names in lexical order.
+ Pos
+ Ident []string // Variable name and fields in lexical order.
}
-func newVariable(ident string) *VariableNode {
- return &VariableNode{NodeType: NodeVariable, Ident: strings.Split(ident, ".")}
+func newVariable(pos Pos, ident string) *VariableNode {
+ return &VariableNode{NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")}
}
func (v *VariableNode) String() string {
@@ -266,14 +305,16 @@ func (v *VariableNode) String() string {
}
func (v *VariableNode) Copy() Node {
- return &VariableNode{NodeType: NodeVariable, Ident: append([]string{}, v.Ident...)}
+ return &VariableNode{NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)}
}
-// DotNode holds the special identifier '.'. It is represented by a nil pointer.
-type DotNode bool
+// DotNode holds the special identifier '.'.
+type DotNode struct {
+ Pos
+}
-func newDot() *DotNode {
- return nil
+func newDot(pos Pos) *DotNode {
+ return &DotNode{Pos: pos}
}
func (d *DotNode) Type() NodeType {
@@ -285,7 +326,28 @@ func (d *DotNode) String() string {
}
func (d *DotNode) Copy() Node {
- return newDot()
+ return newDot(d.Pos)
+}
+
+// NilNode holds the special identifier 'nil' representing an untyped nil constant.
+type NilNode struct {
+ Pos
+}
+
+func newNil(pos Pos) *NilNode {
+ return &NilNode{Pos: pos}
+}
+
+func (n *NilNode) Type() NodeType {
+ return NodeNil
+}
+
+func (n *NilNode) String() string {
+ return "nil"
+}
+
+func (n *NilNode) Copy() Node {
+ return newNil(n.Pos)
}
// FieldNode holds a field (identifier starting with '.').
@@ -293,11 +355,12 @@ func (d *DotNode) Copy() Node {
// The period is dropped from each ident.
type FieldNode struct {
NodeType
+ Pos
Ident []string // The identifiers in lexical order.
}
-func newField(ident string) *FieldNode {
- return &FieldNode{NodeType: NodeField, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period
+func newField(pos Pos, ident string) *FieldNode {
+ return &FieldNode{NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period
}
func (f *FieldNode) String() string {
@@ -309,17 +372,59 @@ func (f *FieldNode) String() string {
}
func (f *FieldNode) Copy() Node {
- return &FieldNode{NodeType: NodeField, Ident: append([]string{}, f.Ident...)}
+ return &FieldNode{NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)}
+}
+
+// ChainNode holds a term followed by a chain of field accesses (identifier starting with '.').
+// The names may be chained ('.x.y').
+// The periods are dropped from each ident.
+type ChainNode struct {
+ NodeType
+ Pos
+ Node Node
+ Field []string // The identifiers in lexical order.
+}
+
+func newChain(pos Pos, node Node) *ChainNode {
+ return &ChainNode{NodeType: NodeChain, Pos: pos, Node: node}
+}
+
+// Add adds the named field (which should start with a period) to the end of the chain.
+func (c *ChainNode) Add(field string) {
+ if len(field) == 0 || field[0] != '.' {
+ panic("no dot in field")
+ }
+ field = field[1:] // Remove leading dot.
+ if field == "" {
+ panic("empty field")
+ }
+ c.Field = append(c.Field, field)
+}
+
+func (c *ChainNode) String() string {
+ s := c.Node.String()
+ if _, ok := c.Node.(*PipeNode); ok {
+ s = "(" + s + ")"
+ }
+ for _, field := range c.Field {
+ s += "." + field
+ }
+ return s
+}
+
+func (c *ChainNode) Copy() Node {
+ return &ChainNode{NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)}
}
// BoolNode holds a boolean constant.
type BoolNode struct {
NodeType
+ Pos
True bool // The value of the boolean constant.
}
-func newBool(true bool) *BoolNode {
- return &BoolNode{NodeType: NodeBool, True: true}
+func newBool(pos Pos, true bool) *BoolNode {
+ return &BoolNode{NodeType: NodeBool, Pos: pos, True: true}
}
func (b *BoolNode) String() string {
@@ -330,7 +435,7 @@ func (b *BoolNode) String() string {
}
func (b *BoolNode) Copy() Node {
- return newBool(b.True)
+ return newBool(b.Pos, b.True)
}
// NumberNode holds a number: signed or unsigned integer, float, or complex.
@@ -338,6 +443,7 @@ func (b *BoolNode) Copy() Node {
// This simulates in a small amount of code the behavior of Go's ideal constants.
type NumberNode struct {
NodeType
+ Pos
IsInt bool // Number has an integral value.
IsUint bool // Number has an unsigned integral value.
IsFloat bool // Number has a floating-point value.
@@ -349,8 +455,8 @@ type NumberNode struct {
Text string // The original textual representation from the input.
}
-func newNumber(text string, typ itemType) (*NumberNode, error) {
- n := &NumberNode{NodeType: NodeNumber, Text: text}
+func newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) {
+ n := &NumberNode{NodeType: NodeNumber, Pos: pos, Text: text}
switch typ {
case itemCharConstant:
rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0])
@@ -460,12 +566,13 @@ func (n *NumberNode) Copy() Node {
// StringNode holds a string constant. The value has been "unquoted".
type StringNode struct {
NodeType
+ Pos
Quoted string // The original text of the string, with quotes.
Text string // The string, after quote processing.
}
-func newString(orig, text string) *StringNode {
- return &StringNode{NodeType: NodeString, Quoted: orig, Text: text}
+func newString(pos Pos, orig, text string) *StringNode {
+ return &StringNode{NodeType: NodeString, Pos: pos, Quoted: orig, Text: text}
}
func (s *StringNode) String() string {
@@ -473,15 +580,17 @@ func (s *StringNode) String() string {
}
func (s *StringNode) Copy() Node {
- return newString(s.Quoted, s.Text)
+ return newString(s.Pos, s.Quoted, s.Text)
}
-// endNode represents an {{end}} action. It is represented by a nil pointer.
+// endNode represents an {{end}} action.
// It does not appear in the final parse tree.
-type endNode bool
+type endNode struct {
+ Pos
+}
-func newEnd() *endNode {
- return nil
+func newEnd(pos Pos) *endNode {
+ return &endNode{Pos: pos}
}
func (e *endNode) Type() NodeType {
@@ -493,17 +602,18 @@ func (e *endNode) String() string {
}
func (e *endNode) Copy() Node {
- return newEnd()
+ return newEnd(e.Pos)
}
// elseNode represents an {{else}} action. Does not appear in the final tree.
type elseNode struct {
NodeType
- Line int // The line number in the input.
+ Pos
+ Line int // The line number in the input (deprecated; kept for compatibility)
}
-func newElse(line int) *elseNode {
- return &elseNode{NodeType: nodeElse, Line: line}
+func newElse(pos Pos, line int) *elseNode {
+ return &elseNode{NodeType: nodeElse, Pos: pos, Line: line}
}
func (e *elseNode) Type() NodeType {
@@ -515,13 +625,14 @@ func (e *elseNode) String() string {
}
func (e *elseNode) Copy() Node {
- return newElse(e.Line)
+ return newElse(e.Pos, e.Line)
}
// BranchNode is the common representation of if, range, and with.
type BranchNode struct {
NodeType
- Line int // The line number in the input.
+ Pos
+ Line int // The line number in the input (deprecated; kept for compatibility)
Pipe *PipeNode // The pipeline to be evaluated.
List *ListNode // What to execute if the value is non-empty.
ElseList *ListNode // What to execute if the value is empty (nil if absent).
@@ -550,12 +661,12 @@ type IfNode struct {
BranchNode
}
-func newIf(line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
- return &IfNode{BranchNode{NodeType: NodeIf, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
+func newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
+ return &IfNode{BranchNode{NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
func (i *IfNode) Copy() Node {
- return newIf(i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
+ return newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
}
// RangeNode represents a {{range}} action and its commands.
@@ -563,12 +674,12 @@ type RangeNode struct {
BranchNode
}
-func newRange(line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
- return &RangeNode{BranchNode{NodeType: NodeRange, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
+func newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
+ return &RangeNode{BranchNode{NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
func (r *RangeNode) Copy() Node {
- return newRange(r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
+ return newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
}
// WithNode represents a {{with}} action and its commands.
@@ -576,24 +687,25 @@ type WithNode struct {
BranchNode
}
-func newWith(line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
- return &WithNode{BranchNode{NodeType: NodeWith, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
+func newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
+ return &WithNode{BranchNode{NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
}
func (w *WithNode) Copy() Node {
- return newWith(w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList())
+ return newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList())
}
// TemplateNode represents a {{template}} action.
type TemplateNode struct {
NodeType
- Line int // The line number in the input.
+ Pos
+ Line int // The line number in the input (deprecated; kept for compatibility)
Name string // The name of the template (unquoted).
Pipe *PipeNode // The command to evaluate as dot for the template.
}
-func newTemplate(line int, name string, pipe *PipeNode) *TemplateNode {
- return &TemplateNode{NodeType: NodeTemplate, Line: line, Name: name, Pipe: pipe}
+func newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode {
+ return &TemplateNode{NodeType: NodeTemplate, Line: line, Pos: pos, Name: name, Pipe: pipe}
}
func (t *TemplateNode) String() string {
@@ -604,5 +716,5 @@ func (t *TemplateNode) String() string {
}
func (t *TemplateNode) Copy() Node {
- return newTemplate(t.Line, t.Name, t.Pipe.CopyPipe())
+ return newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe())
}
diff --git a/src/pkg/text/template/parse/parse.go b/src/pkg/text/template/parse/parse.go
index c0087b278..c0fb9274a 100644
--- a/src/pkg/text/template/parse/parse.go
+++ b/src/pkg/text/template/parse/parse.go
@@ -13,17 +13,20 @@ import (
"fmt"
"runtime"
"strconv"
+ "strings"
"unicode"
)
// Tree is the representation of a single parsed template.
type Tree struct {
- Name string // name of the template represented by the tree.
- Root *ListNode // top-level root of the tree.
+ Name string // name of the template represented by the tree.
+ ParseName string // name of the top-level template during parsing, for error messages.
+ Root *ListNode // top-level root of the tree.
+ text string // text parsed to create the template (or its parent)
// Parsing only; cleared after parse.
funcs []map[string]interface{}
lex *lexer
- token [2]item // two-token lookahead for parser.
+ token [3]item // three-token lookahead for parser.
peekCount int
vars []string // variables defined at the moment.
}
@@ -34,7 +37,9 @@ type Tree struct {
// empty map is returned with the error.
func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) {
treeSet = make(map[string]*Tree)
- _, err = New(name).Parse(text, leftDelim, rightDelim, treeSet, funcs...)
+ t := New(name)
+ t.text = text
+ _, err = t.Parse(text, leftDelim, rightDelim, treeSet, funcs...)
return
}
@@ -53,12 +58,21 @@ func (t *Tree) backup() {
t.peekCount++
}
-// backup2 backs the input stream up two tokens
+// backup2 backs the input stream up two tokens.
+// The zeroth token is already there.
func (t *Tree) backup2(t1 item) {
t.token[1] = t1
t.peekCount = 2
}
+// backup3 backs the input stream up three tokens
+// The zeroth token is already there.
+func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back.
+ t.token[1] = t1
+ t.token[2] = t2
+ t.peekCount = 3
+}
+
// peek returns but does not consume the next token.
func (t *Tree) peek() item {
if t.peekCount > 0 {
@@ -69,6 +83,29 @@ func (t *Tree) peek() item {
return t.token[0]
}
+// nextNonSpace returns the next non-space token.
+func (t *Tree) nextNonSpace() (token item) {
+ for {
+ token = t.next()
+ if token.typ != itemSpace {
+ break
+ }
+ }
+ return token
+}
+
+// peekNonSpace returns but does not consume the next non-space token.
+func (t *Tree) peekNonSpace() (token item) {
+ for {
+ token = t.next()
+ if token.typ != itemSpace {
+ break
+ }
+ }
+ t.backup()
+ return token
+}
+
// Parsing.
// New allocates a new parse tree with the given name.
@@ -79,10 +116,29 @@ func New(name string, funcs ...map[string]interface{}) *Tree {
}
}
+// ErrorContext returns a textual representation of the location of the node in the input text.
+func (t *Tree) ErrorContext(n Node) (location, context string) {
+ pos := int(n.Position())
+ text := t.text[:pos]
+ byteNum := strings.LastIndex(text, "\n")
+ if byteNum == -1 {
+ byteNum = pos // On first line.
+ } else {
+ byteNum++ // After the newline.
+ byteNum = pos - byteNum
+ }
+ lineNum := 1 + strings.Count(text, "\n")
+ context = n.String()
+ if len(context) > 20 {
+ context = fmt.Sprintf("%.20s...", context)
+ }
+ return fmt.Sprintf("%s:%d:%d", t.ParseName, lineNum, byteNum), context
+}
+
// errorf formats the error and terminates processing.
func (t *Tree) errorf(format string, args ...interface{}) {
t.Root = nil
- format = fmt.Sprintf("template: %s:%d: %s", t.Name, t.lex.lineNumber(), format)
+ format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format)
panic(fmt.Errorf(format, args...))
}
@@ -93,18 +149,18 @@ func (t *Tree) error(err error) {
// expect consumes the next token and guarantees it has the required type.
func (t *Tree) expect(expected itemType, context string) item {
- token := t.next()
+ token := t.nextNonSpace()
if token.typ != expected {
- t.errorf("expected %s in %s; got %s", expected, context, token)
+ t.unexpected(token, context)
}
return token
}
-// expectEither consumes the next token and guarantees it has one of the required types.
+// expectOneOf consumes the next token and guarantees it has one of the required types.
func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
- token := t.next()
+ token := t.nextNonSpace()
if token.typ != expected1 && token.typ != expected2 {
- t.errorf("expected %s or %s in %s; got %s", expected1, expected2, context, token)
+ t.unexpected(token, context)
}
return token
}
@@ -169,9 +225,11 @@ func (t *Tree) atEOF() bool {
// the template for execution. If either action delimiter string is empty, the
// default ("{{" or "}}") is used. Embedded template definitions are added to
// the treeSet map.
-func (t *Tree) Parse(s, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
+func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
defer t.recover(&err)
- t.startParse(funcs, lex(t.Name, s, leftDelim, rightDelim))
+ t.ParseName = t.Name
+ t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim))
+ t.text = text
t.parse(treeSet)
t.add(treeSet)
t.stopParse()
@@ -219,12 +277,14 @@ func IsEmptyTree(n Node) bool {
// as itemList except it also parses {{define}} actions.
// It runs to EOF.
func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {
- t.Root = newList()
+ t.Root = newList(t.peek().pos)
for t.peek().typ != itemEOF {
if t.peek().typ == itemLeftDelim {
delim := t.next()
- if t.next().typ == itemDefine {
+ if t.nextNonSpace().typ == itemDefine {
newT := New("definition") // name will be updated once we know it.
+ newT.text = t.text
+ newT.ParseName = t.ParseName
newT.startParse(t.funcs, t.lex)
newT.parseDefinition(treeSet)
continue
@@ -257,16 +317,16 @@ func (t *Tree) parseDefinition(treeSet map[string]*Tree) {
if end.Type() != nodeEnd {
t.errorf("unexpected %s in %s", end, context)
}
- t.stopParse()
t.add(treeSet)
+ t.stopParse()
}
// itemList:
// textOrAction*
// Terminates at {{end}} or {{else}}, returned separately.
func (t *Tree) itemList() (list *ListNode, next Node) {
- list = newList()
- for t.peek().typ != itemEOF {
+ list = newList(t.peekNonSpace().pos)
+ for t.peekNonSpace().typ != itemEOF {
n := t.textOrAction()
switch n.Type() {
case nodeEnd, nodeElse:
@@ -281,9 +341,9 @@ func (t *Tree) itemList() (list *ListNode, next Node) {
// textOrAction:
// text | action
func (t *Tree) textOrAction() Node {
- switch token := t.next(); token.typ {
+ switch token := t.nextNonSpace(); token.typ {
case itemText:
- return newText(token.val)
+ return newText(token.pos, token.val)
case itemLeftDelim:
return t.action()
default:
@@ -298,7 +358,7 @@ func (t *Tree) textOrAction() Node {
// Left delim is past. Now get actions.
// First word could be a keyword such as range.
func (t *Tree) action() (n Node) {
- switch token := t.next(); token.typ {
+ switch token := t.nextNonSpace(); token.typ {
case itemElse:
return t.elseControl()
case itemEnd:
@@ -314,24 +374,26 @@ func (t *Tree) action() (n Node) {
}
t.backup()
// Do not pop variables; they persist until "end".
- return newAction(t.lex.lineNumber(), t.pipeline("command"))
+ return newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command"))
}
// Pipeline:
-// field or command
-// pipeline "|" pipeline
+// declarations? command ('|' command)*
func (t *Tree) pipeline(context string) (pipe *PipeNode) {
var decl []*VariableNode
+ pos := t.peekNonSpace().pos
// Are there declarations?
for {
- if v := t.peek(); v.typ == itemVariable {
+ if v := t.peekNonSpace(); v.typ == itemVariable {
t.next()
- if next := t.peek(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") {
- t.next()
- variable := newVariable(v.val)
- if len(variable.Ident) != 1 {
- t.errorf("illegal variable in declaration: %s", v.val)
- }
+ // Since space is a token, we need 3-token look-ahead here in the worst case:
+ // in "$x foo" we need to read "foo" (as opposed to ":=") to know that $x is an
+ // argument variable rather than a declaration. So remember the token
+ // adjacent to the variable so we can push it back if necessary.
+ tokenAfterVariable := t.peek()
+ if next := t.peekNonSpace(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") {
+ t.nextNonSpace()
+ variable := newVariable(v.pos, v.val)
decl = append(decl, variable)
t.vars = append(t.vars, v.val)
if next.typ == itemChar && next.val == "," {
@@ -340,22 +402,27 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) {
}
t.errorf("too many declarations in %s", context)
}
+ } else if tokenAfterVariable.typ == itemSpace {
+ t.backup3(v, tokenAfterVariable)
} else {
t.backup2(v)
}
}
break
}
- pipe = newPipeline(t.lex.lineNumber(), decl)
+ pipe = newPipeline(pos, t.lex.lineNumber(), decl)
for {
- switch token := t.next(); token.typ {
- case itemRightDelim:
+ switch token := t.nextNonSpace(); token.typ {
+ case itemRightDelim, itemRightParen:
if len(pipe.Cmds) == 0 {
t.errorf("missing value for %s", context)
}
+ if token.typ == itemRightParen {
+ t.backup()
+ }
return
case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
- itemVariable, itemNumber, itemRawString, itemString:
+ itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen:
t.backup()
pipe.append(t.command())
default:
@@ -365,9 +432,9 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) {
return
}
-func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list, elseList *ListNode) {
- lineNum = t.lex.lineNumber()
+func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
defer t.popVars(len(t.vars))
+ line = t.lex.lineNumber()
pipe = t.pipeline(context)
var next Node
list, next = t.itemList()
@@ -380,7 +447,7 @@ func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list,
}
elseList = elseList
}
- return lineNum, pipe, list, elseList
+ return pipe.Position(), line, pipe, list, elseList
}
// If:
@@ -411,16 +478,14 @@ func (t *Tree) withControl() Node {
// {{end}}
// End keyword is past.
func (t *Tree) endControl() Node {
- t.expect(itemRightDelim, "end")
- return newEnd()
+ return newEnd(t.expect(itemRightDelim, "end").pos)
}
// Else:
// {{else}}
// Else keyword is past.
func (t *Tree) elseControl() Node {
- t.expect(itemRightDelim, "else")
- return newElse(t.lex.lineNumber())
+ return newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber())
}
// Template:
@@ -429,7 +494,8 @@ func (t *Tree) elseControl() Node {
// to a string.
func (t *Tree) templateControl() Node {
var name string
- switch token := t.next(); token.typ {
+ token := t.nextNonSpace()
+ switch token.typ {
case itemString, itemRawString:
s, err := strconv.Unquote(token.val)
if err != nil {
@@ -440,57 +506,38 @@ func (t *Tree) templateControl() Node {
t.unexpected(token, "template invocation")
}
var pipe *PipeNode
- if t.next().typ != itemRightDelim {
+ if t.nextNonSpace().typ != itemRightDelim {
t.backup()
// Do not pop variables; they persist until "end".
pipe = t.pipeline("template")
}
- return newTemplate(t.lex.lineNumber(), name, pipe)
+ return newTemplate(token.pos, t.lex.lineNumber(), name, pipe)
}
// command:
+// operand (space operand)*
// space-separated arguments up to a pipeline character or right delimiter.
// we consume the pipe character but leave the right delim to terminate the action.
func (t *Tree) command() *CommandNode {
- cmd := newCommand()
-Loop:
+ cmd := newCommand(t.peekNonSpace().pos)
for {
+ t.peekNonSpace() // skip leading spaces.
+ operand := t.operand()
+ if operand != nil {
+ cmd.append(operand)
+ }
switch token := t.next(); token.typ {
- case itemRightDelim:
- t.backup()
- break Loop
- case itemPipe:
- break Loop
+ case itemSpace:
+ continue
case itemError:
t.errorf("%s", token.val)
- case itemIdentifier:
- if !t.hasFunction(token.val) {
- t.errorf("function %q not defined", token.val)
- }
- cmd.append(NewIdentifier(token.val))
- case itemDot:
- cmd.append(newDot())
- case itemVariable:
- cmd.append(t.useVar(token.val))
- case itemField:
- cmd.append(newField(token.val))
- case itemBool:
- cmd.append(newBool(token.val == "true"))
- case itemCharConstant, itemComplex, itemNumber:
- number, err := newNumber(token.val, token.typ)
- if err != nil {
- t.error(err)
- }
- cmd.append(number)
- case itemString, itemRawString:
- s, err := strconv.Unquote(token.val)
- if err != nil {
- t.error(err)
- }
- cmd.append(newString(token.val, s))
+ case itemRightDelim, itemRightParen:
+ t.backup()
+ case itemPipe:
default:
- t.unexpected(token, "command")
+ t.errorf("unexpected %s in operand; missing space?", token)
}
+ break
}
if len(cmd.Args) == 0 {
t.errorf("empty command")
@@ -498,6 +545,88 @@ Loop:
return cmd
}
+// operand:
+// term .Field*
+// An operand is a space-separated component of a command,
+// a term possibly followed by field accesses.
+// A nil return means the next item is not an operand.
+func (t *Tree) operand() Node {
+ node := t.term()
+ if node == nil {
+ return nil
+ }
+ if t.peek().typ == itemField {
+ chain := newChain(t.peek().pos, node)
+ for t.peek().typ == itemField {
+ chain.Add(t.next().val)
+ }
+ // Compatibility with original API: If the term is of type NodeField
+ // or NodeVariable, just put more fields on the original.
+ // Otherwise, keep the Chain node.
+ // TODO: Switch to Chains always when we can.
+ switch node.Type() {
+ case NodeField:
+ node = newField(chain.Position(), chain.String())
+ case NodeVariable:
+ node = newVariable(chain.Position(), chain.String())
+ default:
+ node = chain
+ }
+ }
+ return node
+}
+
+// term:
+// literal (number, string, nil, boolean)
+// function (identifier)
+// .
+// .Field
+// $
+// '(' pipeline ')'
+// A term is a simple "expression".
+// A nil return means the next item is not a term.
+func (t *Tree) term() Node {
+ switch token := t.nextNonSpace(); token.typ {
+ case itemError:
+ t.errorf("%s", token.val)
+ case itemIdentifier:
+ if !t.hasFunction(token.val) {
+ t.errorf("function %q not defined", token.val)
+ }
+ return NewIdentifier(token.val).SetPos(token.pos)
+ case itemDot:
+ return newDot(token.pos)
+ case itemNil:
+ return newNil(token.pos)
+ case itemVariable:
+ return t.useVar(token.pos, token.val)
+ case itemField:
+ return newField(token.pos, token.val)
+ case itemBool:
+ return newBool(token.pos, token.val == "true")
+ case itemCharConstant, itemComplex, itemNumber:
+ number, err := newNumber(token.pos, token.val, token.typ)
+ if err != nil {
+ t.error(err)
+ }
+ return number
+ case itemLeftParen:
+ pipe := t.pipeline("parenthesized pipeline")
+ if token := t.next(); token.typ != itemRightParen {
+ t.errorf("unclosed right paren: unexpected %s", token)
+ }
+ return pipe
+ case itemString, itemRawString:
+ s, err := strconv.Unquote(token.val)
+ if err != nil {
+ t.error(err)
+ }
+ return newString(token.pos, token.val, s)
+ }
+ t.backup()
+ return nil
+}
+
// hasFunction reports if a function name exists in the Tree's maps.
func (t *Tree) hasFunction(name string) bool {
for _, funcMap := range t.funcs {
@@ -518,8 +647,8 @@ func (t *Tree) popVars(n int) {
// useVar returns a node for a variable reference. It errors if the
// variable is not defined.
-func (t *Tree) useVar(name string) Node {
- v := newVariable(name)
+func (t *Tree) useVar(pos Pos, name string) Node {
+ v := newVariable(pos, name)
for _, varName := range t.vars {
if varName == v.Ident[0] {
return v
diff --git a/src/pkg/text/template/parse/parse_test.go b/src/pkg/text/template/parse/parse_test.go
index b2e788238..695c76ebf 100644
--- a/src/pkg/text/template/parse/parse_test.go
+++ b/src/pkg/text/template/parse/parse_test.go
@@ -7,10 +7,11 @@ package parse
import (
"flag"
"fmt"
+ "strings"
"testing"
)
-var debug = flag.Bool("debug", false, "show the errors produced by the tests")
+var debug = flag.Bool("debug", false, "show the errors produced by the main tests")
type numberTest struct {
text string
@@ -84,7 +85,7 @@ func TestNumberParse(t *testing.T) {
typ = itemComplex
}
}
- n, err := newNumber(test.text, typ)
+ n, err := newNumber(0, test.text, typ)
ok := test.isInt || test.isUint || test.isFloat || test.isComplex
if ok && err != nil {
t.Errorf("unexpected error for %q: %s", test.text, err)
@@ -185,6 +186,10 @@ var parseTests = []parseTest{
`{{.X | .Y}}`},
{"pipeline with decl", "{{$x := .X|.Y}}", noError,
`{{$x := .X | .Y}}`},
+ {"nested pipeline", "{{.X (.Y .Z) (.A | .B .C) (.E)}}", noError,
+ `{{.X (.Y .Z) (.A | .B .C) (.E)}}`},
+ {"field applied to parentheses", "{{(.Y .Z).Field}}", noError,
+ `{{(.Y .Z).Field}}`},
{"simple if", "{{if .X}}hello{{end}}", noError,
`{{if .X}}"hello"{{end}}`},
{"if with else", "{{if .X}}true{{else}}false{{end}}", noError,
@@ -205,8 +210,8 @@ var parseTests = []parseTest{
`{{range $x := .SI}}{{.}}{{end}}`},
{"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError,
`{{range $x, $y := .SI}}{{.}}{{end}}`},
- {"constants", "{{range .SI 1 -3.2i true false 'a'}}{{end}}", noError,
- `{{range .SI 1 -3.2i true false 'a'}}{{end}}`},
+ {"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError,
+ `{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`},
{"template", "{{template `x`}}", noError,
`{{template "x"}}`},
{"template with arg", "{{template `x` .Y}}", noError,
@@ -230,6 +235,9 @@ var parseTests = []parseTest{
{"invalid punctuation", "{{printf 3, 4}}", hasError, ""},
{"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""},
{"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""},
+ {"dot applied to parentheses", "{{printf (printf .).}}", hasError, ""},
+ {"adjacent args", "{{printf 3`x`}}", hasError, ""},
+ {"adjacent args with .", "{{printf `x`.}}", hasError, ""},
// Equals (and other chars) do not assignments make (yet).
{"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"},
{"bug0b", "{{$x = 1}}{{$x}}", hasError, ""},
@@ -316,3 +324,74 @@ func TestIsEmpty(t *testing.T) {
}
}
}
+
+// All failures, and the result is a string that must appear in the error message.
+var errorTests = []parseTest{
+ // Check line numbers are accurate.
+ {"unclosed1",
+ "line1\n{{",
+ hasError, `unclosed1:2: unexpected unclosed action in command`},
+ {"unclosed2",
+ "line1\n{{define `x`}}line2\n{{",
+ hasError, `unclosed2:3: unexpected unclosed action in command`},
+ // Specific errors.
+ {"function",
+ "{{foo}}",
+ hasError, `function "foo" not defined`},
+ {"comment",
+ "{{/*}}",
+ hasError, `unclosed comment`},
+ {"lparen",
+ "{{.X (1 2 3}}",
+ hasError, `unclosed left paren`},
+ {"rparen",
+ "{{.X 1 2 3)}}",
+ hasError, `unexpected ")"`},
+ {"space",
+ "{{`x`3}}",
+ hasError, `missing space?`},
+ {"idchar",
+ "{{a#}}",
+ hasError, `'#'`},
+ {"charconst",
+ "{{'a}}",
+ hasError, `unterminated character constant`},
+ {"stringconst",
+ `{{"a}}`,
+ hasError, `unterminated quoted string`},
+ {"rawstringconst",
+ "{{`a}}",
+ hasError, `unterminated raw quoted string`},
+ {"number",
+ "{{0xi}}",
+ hasError, `number syntax`},
+ {"multidefine",
+ "{{define `a`}}a{{end}}{{define `a`}}b{{end}}",
+ hasError, `multiple definition of template`},
+ {"eof",
+ "{{range .X}}",
+ hasError, `unexpected EOF`},
+ {"variable",
+ // Declare $x so it's defined, to avoid that error, and then check we don't parse a declaration.
+ "{{$x := 23}}{{with $x.y := 3}}{{$x 23}}{{end}}",
+ hasError, `unexpected ":="`},
+ {"multidecl",
+ "{{$a,$b,$c := 23}}",
+ hasError, `too many declarations`},
+ {"undefvar",
+ "{{$a}}",
+ hasError, `undefined variable`},
+}
+
+func TestErrors(t *testing.T) {
+ for _, test := range errorTests {
+ _, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree))
+ if err == nil {
+ t.Errorf("%q: expected error", test.name)
+ continue
+ }
+ if !strings.Contains(err.Error(), test.result) {
+ t.Errorf("%q: error %q does not contain %q", test.name, err, test.result)
+ }
+ }
+}
diff --git a/src/pkg/text/template/template.go b/src/pkg/text/template/template.go
index 82fc9e5e3..a2b9062ad 100644
--- a/src/pkg/text/template/template.go
+++ b/src/pkg/text/template/template.go
@@ -117,6 +117,9 @@ func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error
// Templates returns a slice of the templates associated with t, including t
// itself.
func (t *Template) Templates() []*Template {
+ if t.common == nil {
+ return nil
+ }
// Return a slice so we don't expose the map.
m := make([]*Template, 0, len(t.tmpl))
for _, v := range t.tmpl {
diff --git a/src/pkg/time/example_test.go b/src/pkg/time/example_test.go
index 944cc789c..8928caaba 100644
--- a/src/pkg/time/example_test.go
+++ b/src/pkg/time/example_test.go
@@ -56,3 +56,100 @@ func ExampleDate() {
fmt.Printf("Go launched at %s\n", t.Local())
// Output: Go launched at 2009-11-10 15:00:00 -0800 PST
}
+
+func ExampleTime_Format() {
+ const format = "Jan 2, 2006 at 3:04pm (MST)"
+ t := time.Date(2009, time.November, 10, 15, 0, 0, 0, time.Local)
+ fmt.Println(t.Format(format))
+ fmt.Println(t.UTC().Format(format))
+ // Output:
+ // Nov 10, 2009 at 3:00pm (PST)
+ // Nov 10, 2009 at 11:00pm (UTC)
+}
+
+func ExampleParse() {
+ const longForm = "Jan 2, 2006 at 3:04pm (MST)"
+ t, _ := time.Parse(longForm, "Feb 3, 2013 at 7:54pm (PST)")
+ fmt.Println(t)
+
+ // Note: without explicit zone, returns time in UTC.
+ const shortForm = "2006-Jan-02"
+ t, _ = time.Parse(shortForm, "2013-Feb-03")
+ fmt.Println(t)
+
+ // Output:
+ // 2013-02-03 19:54:00 -0800 PST
+ // 2013-02-03 00:00:00 +0000 UTC
+}
+
+func ExampleParseInLocation() {
+ loc, _ := time.LoadLocation("Europe/Berlin")
+
+ const longForm = "Jan 2, 2006 at 3:04pm (MST)"
+ t, _ := time.ParseInLocation(longForm, "Jul 9, 2012 at 5:02am (CEST)", loc)
+ fmt.Println(t)
+
+ // Note: without explicit zone, returns time in given location.
+ const shortForm = "2006-Jan-02"
+ t, _ = time.ParseInLocation(shortForm, "2012-Jul-09", loc)
+ fmt.Println(t)
+
+ // Output:
+ // 2012-07-09 05:02:00 +0200 CEST
+ // 2012-07-09 00:00:00 +0200 CEST
+}
+
+func ExampleTime_Round() {
+ t := time.Date(0, 0, 0, 12, 15, 30, 918273645, time.UTC)
+ round := []time.Duration{
+ time.Nanosecond,
+ time.Microsecond,
+ time.Millisecond,
+ time.Second,
+ 2 * time.Second,
+ time.Minute,
+ 10 * time.Minute,
+ time.Hour,
+ }
+
+ for _, d := range round {
+ fmt.Printf("t.Round(%6s) = %s\n", d, t.Round(d).Format("15:04:05.999999999"))
+ }
+ // Output:
+ // t.Round( 1ns) = 12:15:30.918273645
+ // t.Round( 1us) = 12:15:30.918274
+ // t.Round( 1ms) = 12:15:30.918
+ // t.Round( 1s) = 12:15:31
+ // t.Round( 2s) = 12:15:30
+ // t.Round( 1m0s) = 12:16:00
+ // t.Round( 10m0s) = 12:20:00
+ // t.Round(1h0m0s) = 12:00:00
+}
+
+func ExampleTime_Truncate() {
+ t, _ := time.Parse("2006 Jan 02 15:04:05", "2012 Dec 07 12:15:30.918273645")
+ trunc := []time.Duration{
+ time.Nanosecond,
+ time.Microsecond,
+ time.Millisecond,
+ time.Second,
+ 2 * time.Second,
+ time.Minute,
+ 10 * time.Minute,
+ time.Hour,
+ }
+
+ for _, d := range trunc {
+ fmt.Printf("t.Truncate(%6s) = %s\n", d, t.Truncate(d).Format("15:04:05.999999999"))
+ }
+
+ // Output:
+ // t.Truncate( 1ns) = 12:15:30.918273645
+ // t.Truncate( 1us) = 12:15:30.918273
+ // t.Truncate( 1ms) = 12:15:30.918
+ // t.Truncate( 1s) = 12:15:30
+ // t.Truncate( 2s) = 12:15:30
+ // t.Truncate( 1m0s) = 12:15:00
+ // t.Truncate( 10m0s) = 12:10:00
+ // t.Truncate(1h0m0s) = 12:00:00
+}
diff --git a/src/pkg/time/export_test.go b/src/pkg/time/export_test.go
new file mode 100644
index 000000000..130ca8f7e
--- /dev/null
+++ b/src/pkg/time/export_test.go
@@ -0,0 +1,19 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package time
+
+import (
+ "sync"
+)
+
+func ResetLocalOnceForTest() {
+ localOnce = sync.Once{}
+ localLoc = Location{}
+}
+
+func ForceUSPacificForTesting() {
+ ResetLocalOnceForTest()
+ localOnce.Do(initTestingZone)
+}
diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go
index efca3a926..817c79a80 100644
--- a/src/pkg/time/format.go
+++ b/src/pkg/time/format.go
@@ -9,7 +9,7 @@ import "errors"
// These are predefined layouts for use in Time.Format.
// The standard time used in the layouts is:
// Mon Jan 2 15:04:05 MST 2006
-// which is Unix time 1136243045. Since MST is GMT-0700,
+// which is Unix time 1136239445. Since MST is GMT-0700,
// the standard time can be thought of as
// 01/02 03:04:05PM '06 -0700
// To define your own format, write down what the standard time would look
@@ -57,63 +57,74 @@ const (
)
const (
- stdLongMonth = "January"
- stdMonth = "Jan"
- stdNumMonth = "1"
- stdZeroMonth = "01"
- stdLongWeekDay = "Monday"
- stdWeekDay = "Mon"
- stdDay = "2"
- stdUnderDay = "_2"
- stdZeroDay = "02"
- stdHour = "15"
- stdHour12 = "3"
- stdZeroHour12 = "03"
- stdMinute = "4"
- stdZeroMinute = "04"
- stdSecond = "5"
- stdZeroSecond = "05"
- stdLongYear = "2006"
- stdYear = "06"
- stdPM = "PM"
- stdpm = "pm"
- stdTZ = "MST"
- stdISO8601TZ = "Z0700" // prints Z for UTC
- stdISO8601ColonTZ = "Z07:00" // prints Z for UTC
- stdNumTZ = "-0700" // always numeric
- stdNumShortTZ = "-07" // always numeric
- stdNumColonTZ = "-07:00" // always numeric
+ _ = iota
+ stdLongMonth = iota + stdNeedDate // "January"
+ stdMonth // "Jan"
+ stdNumMonth // "1"
+ stdZeroMonth // "01"
+ stdLongWeekDay // "Monday"
+ stdWeekDay // "Mon"
+ stdDay // "2"
+ stdUnderDay // "_2"
+ stdZeroDay // "02"
+ stdHour = iota + stdNeedClock // "15"
+ stdHour12 // "3"
+ stdZeroHour12 // "03"
+ stdMinute // "4"
+ stdZeroMinute // "04"
+ stdSecond // "5"
+ stdZeroSecond // "05"
+ stdLongYear = iota + stdNeedDate // "2006"
+ stdYear // "06"
+ stdPM = iota + stdNeedClock // "PM"
+ stdpm // "pm"
+ stdTZ = iota // "MST"
+ stdISO8601TZ // "Z0700" // prints Z for UTC
+ stdISO8601ColonTZ // "Z07:00" // prints Z for UTC
+ stdNumTZ // "-0700" // always numeric
+ stdNumShortTZ // "-07" // always numeric
+ stdNumColonTZ // "-07:00" // always numeric
+ stdFracSecond0 // ".0", ".00", ... , trailing zeros included
+ stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted
+
+ stdNeedDate = 1 << 8 // need month, day, year
+ stdNeedClock = 2 << 8 // need hour, minute, second
+ stdArgShift = 16 // extra argument in high bits, above low stdArgShift
+ stdMask = 1<<stdArgShift - 1 // mask out argument
)
+// std0x records the std values for "01", "02", ..., "06".
+var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}
+
// nextStdChunk finds the first occurrence of a std string in
// layout and returns the text before, the std string, and the text after.
-func nextStdChunk(layout string) (prefix, std, suffix string) {
+func nextStdChunk(layout string) (prefix string, std int, suffix string) {
for i := 0; i < len(layout); i++ {
- switch layout[i] {
+ switch c := int(layout[i]); c {
case 'J': // January, Jan
- if len(layout) >= i+7 && layout[i:i+7] == stdLongMonth {
- return layout[0:i], stdLongMonth, layout[i+7:]
- }
- if len(layout) >= i+3 && layout[i:i+3] == stdMonth {
+ if len(layout) >= i+3 && layout[i:i+3] == "Jan" {
+ if len(layout) >= i+7 && layout[i:i+7] == "January" {
+ return layout[0:i], stdLongMonth, layout[i+7:]
+ }
return layout[0:i], stdMonth, layout[i+3:]
}
case 'M': // Monday, Mon, MST
- if len(layout) >= i+6 && layout[i:i+6] == stdLongWeekDay {
- return layout[0:i], stdLongWeekDay, layout[i+6:]
- }
if len(layout) >= i+3 {
- if layout[i:i+3] == stdWeekDay {
+ if layout[i:i+3] == "Mon" {
+ if len(layout) >= i+6 && layout[i:i+6] == "Monday" {
+ return layout[0:i], stdLongWeekDay, layout[i+6:]
+ }
return layout[0:i], stdWeekDay, layout[i+3:]
}
- if layout[i:i+3] == stdTZ {
+ if layout[i:i+3] == "MST" {
return layout[0:i], stdTZ, layout[i+3:]
}
}
case '0': // 01, 02, 03, 04, 05, 06
if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' {
- return layout[0:i], layout[i : i+2], layout[i+2:]
+ return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:]
}
case '1': // 15, 1
@@ -123,7 +134,7 @@ func nextStdChunk(layout string) (prefix, std, suffix string) {
return layout[0:i], stdNumMonth, layout[i+1:]
case '2': // 2006, 2
- if len(layout) >= i+4 && layout[i:i+4] == stdLongYear {
+ if len(layout) >= i+4 && layout[i:i+4] == "2006" {
return layout[0:i], stdLongYear, layout[i+4:]
}
return layout[0:i], stdDay, layout[i+1:]
@@ -133,35 +144,41 @@ func nextStdChunk(layout string) (prefix, std, suffix string) {
return layout[0:i], stdUnderDay, layout[i+2:]
}
- case '3', '4', '5': // 3, 4, 5
- return layout[0:i], layout[i : i+1], layout[i+1:]
+ case '3':
+ return layout[0:i], stdHour12, layout[i+1:]
+
+ case '4':
+ return layout[0:i], stdMinute, layout[i+1:]
+
+ case '5':
+ return layout[0:i], stdSecond, layout[i+1:]
case 'P': // PM
if len(layout) >= i+2 && layout[i+1] == 'M' {
- return layout[0:i], layout[i : i+2], layout[i+2:]
+ return layout[0:i], stdPM, layout[i+2:]
}
case 'p': // pm
if len(layout) >= i+2 && layout[i+1] == 'm' {
- return layout[0:i], layout[i : i+2], layout[i+2:]
+ return layout[0:i], stdpm, layout[i+2:]
}
case '-': // -0700, -07:00, -07
- if len(layout) >= i+5 && layout[i:i+5] == stdNumTZ {
- return layout[0:i], layout[i : i+5], layout[i+5:]
+ if len(layout) >= i+5 && layout[i:i+5] == "-0700" {
+ return layout[0:i], stdNumTZ, layout[i+5:]
}
- if len(layout) >= i+6 && layout[i:i+6] == stdNumColonTZ {
- return layout[0:i], layout[i : i+6], layout[i+6:]
+ if len(layout) >= i+6 && layout[i:i+6] == "-07:00" {
+ return layout[0:i], stdNumColonTZ, layout[i+6:]
}
- if len(layout) >= i+3 && layout[i:i+3] == stdNumShortTZ {
- return layout[0:i], layout[i : i+3], layout[i+3:]
+ if len(layout) >= i+3 && layout[i:i+3] == "-07" {
+ return layout[0:i], stdNumShortTZ, layout[i+3:]
}
case 'Z': // Z0700, Z07:00
- if len(layout) >= i+5 && layout[i:i+5] == stdISO8601TZ {
- return layout[0:i], layout[i : i+5], layout[i+5:]
+ if len(layout) >= i+5 && layout[i:i+5] == "Z0700" {
+ return layout[0:i], stdISO8601TZ, layout[i+5:]
}
- if len(layout) >= i+6 && layout[i:i+6] == stdISO8601ColonTZ {
- return layout[0:i], layout[i : i+6], layout[i+6:]
+ if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" {
+ return layout[0:i], stdISO8601ColonTZ, layout[i+6:]
}
case '.': // .000 or .999 - repeated digits for fractional seconds.
if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') {
@@ -172,12 +189,17 @@ func nextStdChunk(layout string) (prefix, std, suffix string) {
}
// String of digits must end here - only fractional second is all digits.
if !isDigit(layout, j) {
- return layout[0:i], layout[i:j], layout[j:]
+ std := stdFracSecond0
+ if layout[i+1] == '9' {
+ std = stdFracSecond9
+ }
+ std |= (j - (i + 1)) << stdArgShift
+ return layout[0:i], std, layout[j:]
}
}
}
}
- return layout, "", ""
+ return layout, 0, ""
}
var longDayNames = []string{
@@ -259,27 +281,36 @@ func lookup(tab []string, val string) (int, string, error) {
return -1, val, errBad
}
+// appendUint appends the decimal form of x to b and returns the result.
+// If x is a single-digit number and pad != 0, appendUint inserts the pad byte
+// before the digit.
// Duplicates functionality in strconv, but avoids dependency.
-func itoa(x int) string {
+func appendUint(b []byte, x uint, pad byte) []byte {
+ if x < 10 {
+ if pad != 0 {
+ b = append(b, pad)
+ }
+ return append(b, byte('0'+x))
+ }
+ if x < 100 {
+ b = append(b, byte('0'+x/10))
+ b = append(b, byte('0'+x%10))
+ return b
+ }
+
var buf [32]byte
n := len(buf)
if x == 0 {
- return "0"
- }
- u := uint(x)
- if x < 0 {
- u = -u
- }
- for u > 0 {
- n--
- buf[n] = byte(u%10 + '0')
- u /= 10
+ return append(b, '0')
}
- if x < 0 {
+ for x >= 10 {
n--
- buf[n] = '-'
+ buf[n] = byte(x%10 + '0')
+ x /= 10
}
- return string(buf[n:])
+ n--
+ buf[n] = byte(x + '0')
+ return append(b, buf[n:]...)
}
// Never printed, just needs to be non-nil for return by atoi.
@@ -292,7 +323,8 @@ func atoi(s string) (x int, err error) {
neg = true
s = s[1:]
}
- x, rem, err := leadingInt(s)
+ q, rem, err := leadingInt(s)
+ x = int(q)
if err != nil || rem != "" {
return 0, atoiError
}
@@ -302,37 +334,30 @@ func atoi(s string) (x int, err error) {
return x, nil
}
-func pad(i int, padding string) string {
- s := itoa(i)
- if i < 10 {
- s = padding + s
+// formatNano appends a fractional second, as nanoseconds, to b
+// and returns the result.
+func formatNano(b []byte, nanosec uint, n int, trim bool) []byte {
+ u := nanosec
+ var buf [9]byte
+ for start := len(buf); start > 0; {
+ start--
+ buf[start] = byte(u%10 + '0')
+ u /= 10
}
- return s
-}
-
-func zeroPad(i int) string { return pad(i, "0") }
-// formatNano formats a fractional second, as nanoseconds.
-func formatNano(nanosec, n int, trim bool) string {
- // User might give us bad data. Make sure it's positive and in range.
- // They'll get nonsense output but it will have the right format.
- s := itoa(int(uint(nanosec) % 1e9))
- // Zero pad left without fmt.
- if len(s) < 9 {
- s = "000000000"[:9-len(s)] + s
- }
if n > 9 {
n = 9
}
if trim {
- for n > 0 && s[n-1] == '0' {
+ for n > 0 && buf[n-1] == '0' {
n--
}
if n == 0 {
- return ""
+ return b
}
}
- return "." + s[:n]
+ b = append(b, '.')
+ return append(b, buf[:n]...)
}
// String returns the time formatted using the format string
@@ -341,16 +366,6 @@ func (t Time) String() string {
return t.Format("2006-01-02 15:04:05.999999999 -0700 MST")
}
-type buffer []byte
-
-func (b *buffer) WriteString(s string) {
- *b = append(*b, s...)
-}
-
-func (b *buffer) String() string {
- return string([]byte(*b))
-}
-
// Format returns a textual representation of the time value formatted
// according to layout. The layout defines the format by showing the
// representation of the standard time,
@@ -361,161 +376,172 @@ func (b *buffer) String() string {
// definition of the standard time, see the documentation for ANSIC.
func (t Time) Format(layout string) string {
var (
+ name, offset, abs = t.locabs()
+
year int = -1
month Month
day int
hour int = -1
min int
sec int
- b buffer
+
+ b []byte
+ buf [64]byte
)
+ max := len(layout) + 10
+ if max <= len(buf) {
+ b = buf[:0]
+ } else {
+ b = make([]byte, 0, max)
+ }
// Each iteration generates one std value.
- for {
+ for layout != "" {
prefix, std, suffix := nextStdChunk(layout)
- b.WriteString(prefix)
- if std == "" {
+ if prefix != "" {
+ b = append(b, prefix...)
+ }
+ if std == 0 {
break
}
+ layout = suffix
// Compute year, month, day if needed.
- if year < 0 {
- // Jan 01 02 2006
- if a, z := std[0], std[len(std)-1]; a == 'J' || a == 'j' || z == '1' || z == '2' || z == '6' {
- year, month, day = t.Date()
- }
+ if year < 0 && std&stdNeedDate != 0 {
+ year, month, day, _ = absDate(abs, true)
}
// Compute hour, minute, second if needed.
- if hour < 0 {
- // 03 04 05 15 pm
- if z := std[len(std)-1]; z == '3' || z == '4' || z == '5' || z == 'm' || z == 'M' {
- hour, min, sec = t.Clock()
- }
+ if hour < 0 && std&stdNeedClock != 0 {
+ hour, min, sec = absClock(abs)
}
- var p string
- switch std {
+ switch std & stdMask {
case stdYear:
- p = zeroPad(year % 100)
+ y := year
+ if y < 0 {
+ y = -y
+ }
+ b = appendUint(b, uint(y%100), '0')
case stdLongYear:
// Pad year to at least 4 digits.
- p = itoa(year)
+ y := year
switch {
case year <= -1000:
- // ok
+ b = append(b, '-')
+ y = -y
case year <= -100:
- p = p[:1] + "0" + p[1:]
+ b = append(b, "-0"...)
+ y = -y
case year <= -10:
- p = p[:1] + "00" + p[1:]
+ b = append(b, "-00"...)
+ y = -y
case year < 0:
- p = p[:1] + "000" + p[1:]
+ b = append(b, "-000"...)
+ y = -y
case year < 10:
- p = "000" + p
+ b = append(b, "000"...)
case year < 100:
- p = "00" + p
+ b = append(b, "00"...)
case year < 1000:
- p = "0" + p
+ b = append(b, '0')
}
+ b = appendUint(b, uint(y), 0)
case stdMonth:
- p = month.String()[:3]
+ b = append(b, month.String()[:3]...)
case stdLongMonth:
- p = month.String()
+ m := month.String()
+ b = append(b, m...)
case stdNumMonth:
- p = itoa(int(month))
+ b = appendUint(b, uint(month), 0)
case stdZeroMonth:
- p = zeroPad(int(month))
+ b = appendUint(b, uint(month), '0')
case stdWeekDay:
- p = t.Weekday().String()[:3]
+ b = append(b, absWeekday(abs).String()[:3]...)
case stdLongWeekDay:
- p = t.Weekday().String()
+ s := absWeekday(abs).String()
+ b = append(b, s...)
case stdDay:
- p = itoa(day)
+ b = appendUint(b, uint(day), 0)
case stdUnderDay:
- p = pad(day, " ")
+ b = appendUint(b, uint(day), ' ')
case stdZeroDay:
- p = zeroPad(day)
+ b = appendUint(b, uint(day), '0')
case stdHour:
- p = zeroPad(hour)
+ b = appendUint(b, uint(hour), '0')
case stdHour12:
// Noon is 12PM, midnight is 12AM.
hr := hour % 12
if hr == 0 {
hr = 12
}
- p = itoa(hr)
+ b = appendUint(b, uint(hr), 0)
case stdZeroHour12:
// Noon is 12PM, midnight is 12AM.
hr := hour % 12
if hr == 0 {
hr = 12
}
- p = zeroPad(hr)
+ b = appendUint(b, uint(hr), '0')
case stdMinute:
- p = itoa(min)
+ b = appendUint(b, uint(min), 0)
case stdZeroMinute:
- p = zeroPad(min)
+ b = appendUint(b, uint(min), '0')
case stdSecond:
- p = itoa(sec)
+ b = appendUint(b, uint(sec), 0)
case stdZeroSecond:
- p = zeroPad(sec)
+ b = appendUint(b, uint(sec), '0')
case stdPM:
if hour >= 12 {
- p = "PM"
+ b = append(b, "PM"...)
} else {
- p = "AM"
+ b = append(b, "AM"...)
}
case stdpm:
if hour >= 12 {
- p = "pm"
+ b = append(b, "pm"...)
} else {
- p = "am"
+ b = append(b, "am"...)
}
case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumColonTZ:
// Ugly special case. We cheat and take the "Z" variants
// to mean "the time zone as formatted for ISO 8601".
- _, offset := t.Zone()
- if offset == 0 && std[0] == 'Z' {
- p = "Z"
+ if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ) {
+ b = append(b, 'Z')
break
}
zone := offset / 60 // convert to minutes
if zone < 0 {
- p = "-"
+ b = append(b, '-')
zone = -zone
} else {
- p = "+"
+ b = append(b, '+')
}
- p += zeroPad(zone / 60)
+ b = appendUint(b, uint(zone/60), '0')
if std == stdISO8601ColonTZ || std == stdNumColonTZ {
- p += ":"
+ b = append(b, ':')
}
- p += zeroPad(zone % 60)
+ b = appendUint(b, uint(zone%60), '0')
case stdTZ:
- name, offset := t.Zone()
if name != "" {
- p = name
- } else {
- // No time zone known for this time, but we must print one.
- // Use the -0700 format.
- zone := offset / 60 // convert to minutes
- if zone < 0 {
- p = "-"
- zone = -zone
- } else {
- p = "+"
- }
- p += zeroPad(zone / 60)
- p += zeroPad(zone % 60)
+ b = append(b, name...)
+ break
}
- default:
- if len(std) >= 2 && (std[0:2] == ".0" || std[0:2] == ".9") {
- p = formatNano(t.Nanosecond(), len(std)-1, std[1] == '9')
+ // No time zone known for this time, but we must print one.
+ // Use the -0700 format.
+ zone := offset / 60 // convert to minutes
+ if zone < 0 {
+ b = append(b, '-')
+ zone = -zone
+ } else {
+ b = append(b, '+')
}
+ b = appendUint(b, uint(zone/60), '0')
+ b = appendUint(b, uint(zone%60), '0')
+ case stdFracSecond0, stdFracSecond9:
+ b = formatNano(b, uint(t.Nanosecond()), std>>stdArgShift, std&stdMask == stdFracSecond9)
}
- b.WriteString(p)
- layout = suffix
}
- return b.String()
+ return string(b)
}
var errBad = errors.New("bad value for field") // placeholder not passed to user
@@ -585,14 +611,14 @@ func skip(value, prefix string) (string, error) {
for len(prefix) > 0 {
if prefix[0] == ' ' {
if len(value) > 0 && value[0] != ' ' {
- return "", errBad
+ return value, errBad
}
prefix = cutspace(prefix)
value = cutspace(value)
continue
}
if len(value) == 0 || value[0] != prefix[0] {
- return "", errBad
+ return value, errBad
}
prefix = prefix[1:]
value = value[1:]
@@ -611,10 +637,41 @@ func skip(value, prefix string) (string, error) {
//
// Elements omitted from the value are assumed to be zero or, when
// zero is impossible, one, so parsing "3:04pm" returns the time
-// corresponding to Jan 1, year 0, 15:04:00 UTC.
+// corresponding to Jan 1, year 0, 15:04:00 UTC (note that because the year is
+// 0, this time is before the zero Time).
// Years must be in the range 0000..9999. The day of the week is checked
// for syntax but it is otherwise ignored.
+//
+// In the absence of a time zone indicator, Parse returns a time in UTC.
+//
+// When parsing a time with a zone offset like -0700, if the offset corresponds
+// to a time zone used by the current location (Local), then Parse uses that
+// location and zone in the returned time. Otherwise it records the time as
+// being in a fabricated location with time fixed at the given zone offset.
+//
+// When parsing a time with a zone abbreviation like MST, if the zone abbreviation
+// has a defined offset in the current location, then that offset is used.
+// The zone abbreviation "UTC" is recognized as UTC regardless of location.
+// If the zone abbreviation is unknown, Parse records the time as being
+// in a fabricated location with the given zone abbreviation and a zero offset.
+// This choice means that such a time can be parse and reformatted with the
+// same layout losslessly, but the exact instant used in the representation will
+// differ by the actual zone offset. To avoid such problems, prefer time layouts
+// that use a numeric zone offset, or use ParseInLocation.
func Parse(layout, value string) (Time, error) {
+ return parse(layout, value, UTC, Local)
+}
+
+// ParseInLocation is like Parse but differs in two important ways.
+// First, in the absence of time zone information, Parse interprets a time as UTC;
+// ParseInLocation interprets the time as in the given location.
+// Second, when given a zone offset or abbreviation, Parse tries to match it
+// against the Local location; ParseInLocation uses the given location.
+func ParseInLocation(layout, value string, loc *Location) (Time, error) {
+ return parse(layout, value, loc, loc)
+}
+
+func parse(layout, value string, defaultLocation, local *Location) (Time, error) {
alayout, avalue := layout, value
rangeErrString := "" // set if a value is out of range
amSet := false // do we need to subtract 12 from the hour for midnight?
@@ -638,11 +695,12 @@ func Parse(layout, value string) (Time, error) {
for {
var err error
prefix, std, suffix := nextStdChunk(layout)
+ stdstr := layout[len(prefix) : len(layout)-len(suffix)]
value, err = skip(value, prefix)
if err != nil {
return Time{}, &ParseError{alayout, avalue, prefix, value, ""}
}
- if len(std) == 0 {
+ if std == 0 {
if len(value) != 0 {
return Time{}, &ParseError{alayout, avalue, "", value, ": extra text: " + value}
}
@@ -650,7 +708,7 @@ func Parse(layout, value string) (Time, error) {
}
layout = suffix
var p string
- switch std {
+ switch std & stdMask {
case stdYear:
if len(value) < 2 {
err = errBad
@@ -716,7 +774,8 @@ func Parse(layout, value string) (Time, error) {
// fractional second in the format?
if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) {
_, std, _ := nextStdChunk(layout)
- if len(std) > 0 && std[0] == '.' && isDigit(std, 1) {
+ std &= stdMask
+ if std == stdFracSecond0 || std == stdFracSecond9 {
// Fractional second in the layout; proceed normally
break
}
@@ -756,7 +815,7 @@ func Parse(layout, value string) (Time, error) {
err = errBad
}
case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ:
- if std[0] == 'Z' && len(value) >= 1 && value[0] == 'Z' {
+ if (std == stdISO8601TZ || std == stdISO8601ColonTZ) && len(value) >= 1 && value[0] == 'Z' {
value = value[1:]
z = UTC
break
@@ -824,21 +883,37 @@ func Parse(layout, value string) (Time, error) {
}
// It's a valid format.
zoneName = p
- default:
- if len(value) < len(std) {
+
+ case stdFracSecond0:
+ // stdFracSecond0 requires the exact number of digits as specified in
+ // the layout.
+ ndigit := 1 + (std >> stdArgShift)
+ if len(value) < ndigit {
err = errBad
break
}
- if len(std) >= 2 && std[0:2] == ".0" {
- nsec, rangeErrString, err = parseNanoseconds(value, len(std))
- value = value[len(std):]
+ nsec, rangeErrString, err = parseNanoseconds(value, ndigit)
+ value = value[ndigit:]
+
+ case stdFracSecond9:
+ if len(value) < 2 || value[0] != '.' || value[1] < '0' || '9' < value[1] {
+ // Fractional second omitted.
+ break
+ }
+ // Take any number of digits, even more than asked for,
+ // because it is what the stdSecond case would do.
+ i := 0
+ for i < 9 && i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' {
+ i++
}
+ nsec, rangeErrString, err = parseNanoseconds(value, 1+i)
+ value = value[1+i:]
}
if rangeErrString != "" {
- return Time{}, &ParseError{alayout, avalue, std, value, ": " + rangeErrString + " out of range"}
+ return Time{}, &ParseError{alayout, avalue, stdstr, value, ": " + rangeErrString + " out of range"}
}
if err != nil {
- return Time{}, &ParseError{alayout, avalue, std, value, ""}
+ return Time{}, &ParseError{alayout, avalue, stdstr, value, ""}
}
}
if pmSet && hour < 12 {
@@ -847,20 +922,19 @@ func Parse(layout, value string) (Time, error) {
hour = 0
}
- // TODO: be more aggressive checking day?
if z != nil {
return Date(year, Month(month), day, hour, min, sec, nsec, z), nil
}
- t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
if zoneOffset != -1 {
+ t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
t.sec -= int64(zoneOffset)
// Look for local zone with the given offset.
// If that zone was in effect at the given time, use it.
- name, offset, _, _, _ := Local.lookup(t.sec + internalToUnix)
+ name, offset, _, _, _ := local.lookup(t.sec + internalToUnix)
if offset == zoneOffset && (zoneName == "" || name == zoneName) {
- t.loc = Local
+ t.loc = local
return t, nil
}
@@ -870,16 +944,14 @@ func Parse(layout, value string) (Time, error) {
}
if zoneName != "" {
+ t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
// Look for local zone with the given offset.
// If that zone was in effect at the given time, use it.
- offset, _, ok := Local.lookupName(zoneName)
+ offset, _, ok := local.lookupName(zoneName, t.sec+internalToUnix)
if ok {
- name, off, _, _, _ := Local.lookup(t.sec + internalToUnix - int64(offset))
- if name == zoneName && off == offset {
- t.sec -= int64(offset)
- t.loc = Local
- return t, nil
- }
+ t.sec -= int64(offset)
+ t.loc = local
+ return t, nil
}
// Otherwise, create fake zone with unknown offset.
@@ -887,8 +959,8 @@ func Parse(layout, value string) (Time, error) {
return t, nil
}
- // Otherwise, fall back to UTC.
- return t, nil
+ // Otherwise, fall back to default.
+ return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil
}
func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
@@ -896,8 +968,7 @@ func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string,
err = errBad
return
}
- ns, err = atoi(value[1:nbytes])
- if err != nil {
+ if ns, err = atoi(value[1:nbytes]); err != nil {
return
}
if ns < 0 || 1e9 <= ns {
@@ -917,18 +988,18 @@ func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string,
var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
// leadingInt consumes the leading [0-9]* from s.
-func leadingInt(s string) (x int, rem string, err error) {
+func leadingInt(s string) (x int64, rem string, err error) {
i := 0
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
- if x >= (1<<31-10)/10 {
+ if x >= (1<<63-10)/10 {
// overflow
return 0, "", errLeadingInt
}
- x = x*10 + int(c) - '0'
+ x = x*10 + int64(c) - '0'
}
return x, s[i:], nil
}
@@ -973,7 +1044,7 @@ func ParseDuration(s string) (Duration, error) {
for s != "" {
g := float64(0) // this element of the sequence
- var x int
+ var x int64
var err error
// The next character must be [0-9.]
diff --git a/src/pkg/time/internal_test.go b/src/pkg/time/internal_test.go
index b753896d7..918a9f33b 100644
--- a/src/pkg/time/internal_test.go
+++ b/src/pkg/time/internal_test.go
@@ -6,7 +6,7 @@ package time
func init() {
// force US/Pacific for time zone tests
- localOnce.Do(initTestingZone)
+ ForceUSPacificForTesting()
}
var Interrupt = interrupt
diff --git a/src/pkg/time/sleep.go b/src/pkg/time/sleep.go
index 27820b0ea..591fa27b0 100644
--- a/src/pkg/time/sleep.go
+++ b/src/pkg/time/sleep.go
@@ -18,10 +18,25 @@ type runtimeTimer struct {
i int32
when int64
period int64
- f func(int64, interface{})
+ f func(int64, interface{}) // NOTE: must not be closure
arg interface{}
}
+// when is a helper function for setting the 'when' field of a runtimeTimer.
+// It returns what the time will be, in nanoseconds, Duration d in the future.
+// If d is negative, it is ignored. If the returned value would be less than
+// zero because of an overflow, MaxInt64 is returned.
+func when(d Duration) int64 {
+ if d <= 0 {
+ return nano()
+ }
+ t := nano() + int64(d)
+ if t < 0 {
+ t = 1<<63 - 1 // math.MaxInt64
+ }
+ return t
+}
+
func startTimer(*runtimeTimer)
func stopTimer(*runtimeTimer) bool
@@ -35,8 +50,10 @@ type Timer struct {
// Stop prevents the Timer from firing.
// It returns true if the call stops the timer, false if the timer has already
-// expired or stopped.
-func (t *Timer) Stop() (ok bool) {
+// expired or been stopped.
+// Stop does not close the channel, to prevent a read from the channel succeeding
+// incorrectly.
+func (t *Timer) Stop() bool {
return stopTimer(&t.r)
}
@@ -47,7 +64,7 @@ func NewTimer(d Duration) *Timer {
t := &Timer{
C: c,
r: runtimeTimer{
- when: nano() + int64(d),
+ when: when(d),
f: sendTime,
arg: c,
},
@@ -56,6 +73,17 @@ func NewTimer(d Duration) *Timer {
return t
}
+// Reset changes the timer to expire after duration d.
+// It returns true if the timer had been active, false if the timer had
+// expired or been stopped.
+func (t *Timer) Reset(d Duration) bool {
+ w := when(d)
+ active := stopTimer(&t.r)
+ t.r.when = w
+ startTimer(&t.r)
+ return active
+}
+
func sendTime(now int64, c interface{}) {
// Non-blocking send of time on c.
// Used in NewTimer, it cannot block anyway (buffer).
@@ -81,7 +109,7 @@ func After(d Duration) <-chan Time {
func AfterFunc(d Duration, f func()) *Timer {
t := &Timer{
r: runtimeTimer{
- when: nano() + int64(d),
+ when: when(d),
f: goFunc,
arg: f,
},
diff --git a/src/pkg/time/sleep_test.go b/src/pkg/time/sleep_test.go
index e05773df6..9908e220f 100644
--- a/src/pkg/time/sleep_test.go
+++ b/src/pkg/time/sleep_test.go
@@ -54,9 +54,10 @@ func TestAfterStress(t *testing.T) {
go func() {
for atomic.LoadUint32(&stop) == 0 {
runtime.GC()
- // Need to yield, because otherwise
- // the main goroutine will never set the stop flag.
- runtime.Gosched()
+ // Yield so that the OS can wake up the timer thread,
+ // so that it can generate channel sends for the main goroutine,
+ // which will eventually set stop = 1 for us.
+ Sleep(Nanosecond)
}
}()
c := Tick(1)
@@ -245,3 +246,70 @@ func TestSleepZeroDeadlock(t *testing.T) {
}
<-c
}
+
+func testReset(d Duration) error {
+ t0 := NewTimer(2 * d)
+ Sleep(d)
+ if t0.Reset(3*d) != true {
+ return errors.New("resetting unfired timer returned false")
+ }
+ Sleep(2 * d)
+ select {
+ case <-t0.C:
+ return errors.New("timer fired early")
+ default:
+ }
+ Sleep(2 * d)
+ select {
+ case <-t0.C:
+ default:
+ return errors.New("reset timer did not fire")
+ }
+
+ if t0.Reset(50*Millisecond) != false {
+ return errors.New("resetting expired timer returned true")
+ }
+ return nil
+}
+
+func TestReset(t *testing.T) {
+ // We try to run this test with increasingly larger multiples
+ // until one works so slow, loaded hardware isn't as flaky,
+ // but without slowing down fast machines unnecessarily.
+ const unit = 25 * Millisecond
+ tries := []Duration{
+ 1 * unit,
+ 3 * unit,
+ 7 * unit,
+ 15 * unit,
+ }
+ var err error
+ for _, d := range tries {
+ err = testReset(d)
+ if err == nil {
+ t.Logf("passed using duration %v", d)
+ return
+ }
+ }
+ t.Error(err)
+}
+
+// Test that sleeping for an interval so large it overflows does not
+// result in a short sleep duration.
+func TestOverflowSleep(t *testing.T) {
+ const timeout = 25 * Millisecond
+ const big = Duration(int64(1<<63 - 1))
+ select {
+ case <-After(big):
+ t.Fatalf("big timeout fired")
+ case <-After(timeout):
+ // OK
+ }
+ const neg = Duration(-1 << 63)
+ select {
+ case <-After(neg):
+ // OK
+ case <-After(timeout):
+ t.Fatalf("negative timeout didn't fire")
+ }
+}
diff --git a/src/pkg/time/tick.go b/src/pkg/time/tick.go
index 8c6b9bc3b..b92c339c0 100644
--- a/src/pkg/time/tick.go
+++ b/src/pkg/time/tick.go
@@ -6,7 +6,7 @@ package time
import "errors"
-// A Ticker holds a synchronous channel that delivers `ticks' of a clock
+// A Ticker holds a channel that delivers `ticks' of a clock
// at intervals.
type Ticker struct {
C <-chan Time // The channel on which the ticks are delivered.
@@ -39,6 +39,8 @@ func NewTicker(d Duration) *Ticker {
}
// Stop turns off a ticker. After Stop, no more ticks will be sent.
+// Stop does not close the channel, to prevent a read from the channel succeeding
+// incorrectly.
func (t *Ticker) Stop() {
stopTimer(&t.r)
}
diff --git a/src/pkg/time/time.go b/src/pkg/time/time.go
index 2461dac06..d291672af 100644
--- a/src/pkg/time/time.go
+++ b/src/pkg/time/time.go
@@ -241,10 +241,10 @@ func (t Time) IsZero() bool {
// It is called when computing a presentation property like Month or Hour.
func (t Time) abs() uint64 {
l := t.loc
- if l == nil {
- l = &utcLoc
+ // Avoid function calls when possible.
+ if l == nil || l == &localLoc {
+ l = l.get()
}
- // Avoid function call if we hit the local time cache.
sec := t.sec + internalToUnix
if l != &utcLoc {
if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
@@ -257,6 +257,30 @@ func (t Time) abs() uint64 {
return uint64(sec + (unixToInternal + internalToAbsolute))
}
+// locabs is a combination of the Zone and abs methods,
+// extracting both return values from a single zone lookup.
+func (t Time) locabs() (name string, offset int, abs uint64) {
+ l := t.loc
+ if l == nil || l == &localLoc {
+ l = l.get()
+ }
+ // Avoid function call if we hit the local time cache.
+ sec := t.sec + internalToUnix
+ if l != &utcLoc {
+ if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
+ name = l.cacheZone.name
+ offset = l.cacheZone.offset
+ } else {
+ name, offset, _, _, _ = l.lookup(sec)
+ }
+ sec += int64(offset)
+ } else {
+ name = "UTC"
+ }
+ abs = uint64(sec + (unixToInternal + internalToAbsolute))
+ return
+}
+
// Date returns the year, month, and day in which t occurs.
func (t Time) Date() (year int, month Month, day int) {
year, month, day, _ = t.date(true)
@@ -283,8 +307,13 @@ func (t Time) Day() int {
// Weekday returns the day of the week specified by t.
func (t Time) Weekday() Weekday {
+ return absWeekday(t.abs())
+}
+
+// absWeekday is like Weekday but operates on an absolute time.
+func absWeekday(abs uint64) Weekday {
// January 1 of the absolute year, like January 1 of 2001, was a Monday.
- sec := (t.abs() + uint64(Monday)*secondsPerDay) % secondsPerWeek
+ sec := (abs + uint64(Monday)*secondsPerDay) % secondsPerWeek
return Weekday(int(sec) / secondsPerDay)
}
@@ -349,7 +378,12 @@ func (t Time) ISOWeek() (year, week int) {
// Clock returns the hour, minute, and second within the day specified by t.
func (t Time) Clock() (hour, min, sec int) {
- sec = int(t.abs() % secondsPerDay)
+ return absClock(t.abs())
+}
+
+// absClock is like clock but operates on an absolute time.
+func absClock(abs uint64) (hour, min, sec int) {
+ sec = int(abs % secondsPerDay)
hour = sec / secondsPerHour
sec -= hour * secondsPerHour
min = sec / secondsPerMinute
@@ -378,6 +412,13 @@ func (t Time) Nanosecond() int {
return int(t.nsec)
}
+// YearDay returns the day of the year specified by t, in the range [1,365] for non-leap years,
+// and [1,366] in leap years.
+func (t Time) YearDay() int {
+ _, _, _, yday := t.date(false)
+ return yday + 1
+}
+
// A Duration represents the elapsed time between two instants
// as an int64 nanosecond count. The representation limits the
// largest representable duration to approximately 290 years.
@@ -607,11 +648,16 @@ const (
days1970To2001 = 31*365 + 8
)
-// date computes the year and, only when full=true,
+// date computes the year, day of year, and when full=true,
// the month and day in which t occurs.
func (t Time) date(full bool) (year int, month Month, day int, yday int) {
+ return absDate(t.abs(), full)
+}
+
+// absDate is like date but operates on an absolute time.
+func absDate(abs uint64, full bool) (year int, month Month, day int, yday int) {
// Split into time and day.
- d := t.abs() / secondsPerDay
+ d := abs / secondsPerDay
// Account for 400 year cycles.
n := d / daysPer400Years
@@ -987,3 +1033,116 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T
return Time{unix + unixToInternal, int32(nsec), loc}
}
+
+// Truncate returns the result of rounding t down to a multiple of d (since the zero time).
+// If d <= 0, Truncate returns t unchanged.
+func (t Time) Truncate(d Duration) Time {
+ if d <= 0 {
+ return t
+ }
+ _, r := div(t, d)
+ return t.Add(-r)
+}
+
+// Round returns the result of rounding t to the nearest multiple of d (since the zero time).
+// The rounding behavior for halfway values is to round up.
+// If d <= 0, Round returns t unchanged.
+func (t Time) Round(d Duration) Time {
+ if d <= 0 {
+ return t
+ }
+ _, r := div(t, d)
+ if r+r < d {
+ return t.Add(-r)
+ }
+ return t.Add(d - r)
+}
+
+// div divides t by d and returns the quotient parity and remainder.
+// We don't use the quotient parity anymore (round half up instead of round to even)
+// but it's still here in case we change our minds.
+func div(t Time, d Duration) (qmod2 int, r Duration) {
+ neg := false
+ if t.sec < 0 {
+ // Operate on absolute value.
+ neg = true
+ t.sec = -t.sec
+ t.nsec = -t.nsec
+ if t.nsec < 0 {
+ t.nsec += 1e9
+ t.sec-- // t.sec >= 1 before the -- so safe
+ }
+ }
+
+ switch {
+ // Special case: 2d divides 1 second.
+ case d < Second && Second%(d+d) == 0:
+ qmod2 = int(t.nsec/int32(d)) & 1
+ r = Duration(t.nsec % int32(d))
+
+ // Special case: d is a multiple of 1 second.
+ case d%Second == 0:
+ d1 := int64(d / Second)
+ qmod2 = int(t.sec/d1) & 1
+ r = Duration(t.sec%d1)*Second + Duration(t.nsec)
+
+ // General case.
+ // This could be faster if more cleverness were applied,
+ // but it's really only here to avoid special case restrictions in the API.
+ // No one will care about these cases.
+ default:
+ // Compute nanoseconds as 128-bit number.
+ sec := uint64(t.sec)
+ tmp := (sec >> 32) * 1e9
+ u1 := tmp >> 32
+ u0 := tmp << 32
+ tmp = uint64(sec&0xFFFFFFFF) * 1e9
+ u0x, u0 := u0, u0+tmp
+ if u0 < u0x {
+ u1++
+ }
+ u0x, u0 = u0, u0+uint64(t.nsec)
+ if u0 < u0x {
+ u1++
+ }
+
+ // Compute remainder by subtracting r<<k for decreasing k.
+ // Quotient parity is whether we subtract on last round.
+ d1 := uint64(d)
+ for d1>>63 != 1 {
+ d1 <<= 1
+ }
+ d0 := uint64(0)
+ for {
+ qmod2 = 0
+ if u1 > d1 || u1 == d1 && u0 >= d0 {
+ // subtract
+ qmod2 = 1
+ u0x, u0 = u0, u0-d0
+ if u0 > u0x {
+ u1--
+ }
+ u1 -= d1
+ }
+ if d1 == 0 && d0 == uint64(d) {
+ break
+ }
+ d0 >>= 1
+ d0 |= (d1 & 1) << 63
+ d1 >>= 1
+ }
+ r = Duration(u0)
+ }
+
+ if neg && r != 0 {
+ // If input was negative and not an exact multiple of d, we computed q, r such that
+ // q*d + r = -t
+ // But the right answers are given by -(q-1), d-r:
+ // q*d + r = -t
+ // -q*d - r = t
+ // -(q-1)*d + (d - r) = t
+ qmod2 ^= 1
+ r = d - r
+ }
+ return
+}
diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go
index 28047804e..4b268f73d 100644
--- a/src/pkg/time/time_test.go
+++ b/src/pkg/time/time_test.go
@@ -9,6 +9,7 @@ import (
"encoding/gob"
"encoding/json"
"fmt"
+ "math/big"
"math/rand"
"strconv"
"strings"
@@ -192,6 +193,184 @@ func TestNanosecondsToUTCAndBack(t *testing.T) {
}
}
+// The time routines provide no way to get absolute time
+// (seconds since zero), but we need it to compute the right
+// answer for bizarre roundings like "to the nearest 3 ns".
+// Compute as t - year1 = (t - 1970) + (1970 - 2001) + (2001 - 1).
+// t - 1970 is returned by Unix and Nanosecond.
+// 1970 - 2001 is -(31*365+8)*86400 = -978307200 seconds.
+// 2001 - 1 is 2000*365.2425*86400 = 63113904000 seconds.
+const unixToZero = -978307200 + 63113904000
+
+// abs returns the absolute time stored in t, as seconds and nanoseconds.
+func abs(t Time) (sec, nsec int64) {
+ unix := t.Unix()
+ nano := t.Nanosecond()
+ return unix + unixToZero, int64(nano)
+}
+
+// absString returns abs as a decimal string.
+func absString(t Time) string {
+ sec, nsec := abs(t)
+ if sec < 0 {
+ sec = -sec
+ nsec = -nsec
+ if nsec < 0 {
+ nsec += 1e9
+ sec--
+ }
+ return fmt.Sprintf("-%d%09d", sec, nsec)
+ }
+ return fmt.Sprintf("%d%09d", sec, nsec)
+}
+
+var truncateRoundTests = []struct {
+ t Time
+ d Duration
+}{
+ {Date(-1, January, 1, 12, 15, 30, 5e8, UTC), 3},
+ {Date(-1, January, 1, 12, 15, 31, 5e8, UTC), 3},
+ {Date(2012, January, 1, 12, 15, 30, 5e8, UTC), Second},
+ {Date(2012, January, 1, 12, 15, 31, 5e8, UTC), Second},
+}
+
+func TestTruncateRound(t *testing.T) {
+ var (
+ bsec = new(big.Int)
+ bnsec = new(big.Int)
+ bd = new(big.Int)
+ bt = new(big.Int)
+ br = new(big.Int)
+ bq = new(big.Int)
+ b1e9 = new(big.Int)
+ )
+
+ b1e9.SetInt64(1e9)
+
+ testOne := func(ti, tns, di int64) bool {
+ t0 := Unix(ti, int64(tns)).UTC()
+ d := Duration(di)
+ if d < 0 {
+ d = -d
+ }
+ if d <= 0 {
+ d = 1
+ }
+
+ // Compute bt = absolute nanoseconds.
+ sec, nsec := abs(t0)
+ bsec.SetInt64(sec)
+ bnsec.SetInt64(nsec)
+ bt.Mul(bsec, b1e9)
+ bt.Add(bt, bnsec)
+
+ // Compute quotient and remainder mod d.
+ bd.SetInt64(int64(d))
+ bq.DivMod(bt, bd, br)
+
+ // To truncate, subtract remainder.
+ // br is < d, so it fits in an int64.
+ r := br.Int64()
+ t1 := t0.Add(-Duration(r))
+
+ // Check that time.Truncate works.
+ if trunc := t0.Truncate(d); trunc != t1 {
+ t.Errorf("Time.Truncate(%s, %s) = %s, want %s\n"+
+ "%v trunc %v =\n%v want\n%v",
+ t0.Format(RFC3339Nano), d, trunc, t1.Format(RFC3339Nano),
+ absString(t0), int64(d), absString(trunc), absString(t1))
+ return false
+ }
+
+ // To round, add d back if remainder r > d/2 or r == exactly d/2.
+ // The commented out code would round half to even instead of up,
+ // but that makes it time-zone dependent, which is a bit strange.
+ if r > int64(d)/2 || r+r == int64(d) /*&& bq.Bit(0) == 1*/ {
+ t1 = t1.Add(Duration(d))
+ }
+
+ // Check that time.Round works.
+ if rnd := t0.Round(d); rnd != t1 {
+ t.Errorf("Time.Round(%s, %s) = %s, want %s\n"+
+ "%v round %v =\n%v want\n%v",
+ t0.Format(RFC3339Nano), d, rnd, t1.Format(RFC3339Nano),
+ absString(t0), int64(d), absString(rnd), absString(t1))
+ return false
+ }
+ return true
+ }
+
+ // manual test cases
+ for _, tt := range truncateRoundTests {
+ testOne(tt.t.Unix(), int64(tt.t.Nanosecond()), int64(tt.d))
+ }
+
+ // exhaustive near 0
+ for i := 0; i < 100; i++ {
+ for j := 1; j < 100; j++ {
+ testOne(unixToZero, int64(i), int64(j))
+ testOne(unixToZero, -int64(i), int64(j))
+ if t.Failed() {
+ return
+ }
+ }
+ }
+
+ if t.Failed() {
+ return
+ }
+
+ // randomly generated test cases
+ cfg := &quick.Config{MaxCount: 100000}
+ if testing.Short() {
+ cfg.MaxCount = 1000
+ }
+
+ // divisors of Second
+ f1 := func(ti int64, tns int32, logdi int32) bool {
+ d := Duration(1)
+ a, b := uint(logdi%9), (logdi>>16)%9
+ d <<= a
+ for i := 0; i < int(b); i++ {
+ d *= 5
+ }
+ return testOne(ti, int64(tns), int64(d))
+ }
+ quick.Check(f1, cfg)
+
+ // multiples of Second
+ f2 := func(ti int64, tns int32, di int32) bool {
+ d := Duration(di) * Second
+ if d < 0 {
+ d = -d
+ }
+ return testOne(ti, int64(tns), int64(d))
+ }
+ quick.Check(f2, cfg)
+
+ // halfway cases
+ f3 := func(tns, di int64) bool {
+ di &= 0xfffffffe
+ if di == 0 {
+ di = 2
+ }
+ tns -= tns % di
+ if tns < 0 {
+ tns += di / 2
+ } else {
+ tns -= di / 2
+ }
+ return testOne(0, tns, di)
+ }
+ quick.Check(f3, cfg)
+
+ // full generality
+ f4 := func(ti int64, tns int32, di int64) bool {
+ return testOne(ti, int64(tns), di)
+ }
+ quick.Check(f4, cfg)
+}
+
type TimeFormatTest struct {
time Time
formattedValue string
@@ -289,7 +468,7 @@ type ParseTest struct {
value string
hasTZ bool // contains a time zone
hasWD bool // contains a weekday
- yearSign int // sign of year
+ yearSign int // sign of year, -1 indicates the year is not present in the format
fracDigits int // number of digits of fractional second
}
@@ -299,6 +478,7 @@ var parseTests = []ParseTest{
{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0},
{"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST", true, true, 1, 0},
{"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST", true, true, 1, 0},
+ {"RFC1123", RFC1123, "Thu, 04 Feb 2010 22:00:57 PDT", true, true, 1, 0},
{"RFC1123Z", RFC1123Z, "Thu, 04 Feb 2010 21:00:57 -0800", true, true, 1, 0},
{"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00", true, false, 1, 0},
{"custom: \"2006-01-02 15:04:05-07\"", "2006-01-02 15:04:05-07", "2010-02-04 21:00:57-08", true, false, 1, 0},
@@ -324,6 +504,23 @@ var parseTests = []ParseTest{
// Leading zeros in other places should not be taken as fractional seconds.
{"zero1", "2006.01.02.15.04.05.0", "2010.02.04.21.00.57.0", false, false, 1, 1},
{"zero2", "2006.01.02.15.04.05.00", "2010.02.04.21.00.57.01", false, false, 1, 2},
+
+ // Accept any number of fractional second digits (including none) for .999...
+ // In Go 1, .999... was completely ignored in the format, meaning the first two
+ // cases would succeed, but the next four would not. Go 1.1 accepts all six.
+ {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
+ {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
+ {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
+ {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
+ {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
+ {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
+
+ // issue 4502.
+ {"", StampNano, "Feb 4 21:00:57.012345678", false, false, -1, 9},
+ {"", "Jan _2 15:04:05.999", "Feb 4 21:00:57.012300000", false, false, -1, 4},
+ {"", "Jan _2 15:04:05.999", "Feb 4 21:00:57.012345678", false, false, -1, 9},
+ {"", "Jan _2 15:04:05.999999999", "Feb 4 21:00:57.0123", false, false, -1, 4},
+ {"", "Jan _2 15:04:05.999999999", "Feb 4 21:00:57.012345678", false, false, -1, 9},
}
func TestParse(t *testing.T) {
@@ -337,6 +534,42 @@ func TestParse(t *testing.T) {
}
}
+func TestParseInSydney(t *testing.T) {
+ loc, err := LoadLocation("Australia/Sydney")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Check that Parse (and ParseInLocation) understand
+ // that Feb EST and Aug EST are different time zones in Sydney
+ // even though both are called EST.
+ t1, err := ParseInLocation("Jan 02 2006 MST", "Feb 01 2013 EST", loc)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t2 := Date(2013, February, 1, 00, 00, 00, 0, loc)
+ if t1 != t2 {
+ t.Fatalf("ParseInLocation(Feb 01 2013 EST, Sydney) = %v, want %v", t1, t2)
+ }
+ _, offset := t1.Zone()
+ if offset != 11*60*60 {
+ t.Fatalf("ParseInLocation(Feb 01 2013 EST, Sydney).Zone = _, %d, want _, %d", offset, 11*60*60)
+ }
+
+ t1, err = ParseInLocation("Jan 02 2006 MST", "Aug 01 2013 EST", loc)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t2 = Date(2013, August, 1, 00, 00, 00, 0, loc)
+ if t1 != t2 {
+ t.Fatalf("ParseInLocation(Aug 01 2013 EST, Sydney) = %v, want %v", t1, t2)
+ }
+ _, offset = t1.Zone()
+ if offset != 10*60*60 {
+ t.Fatalf("ParseInLocation(Aug 01 2013 EST, Sydney).Zone = _, %d, want _, %d", offset, 10*60*60)
+ }
+}
+
var rubyTests = []ParseTest{
{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0},
// Ignore the time zone in the test. If it parses, it'll be OK.
@@ -359,7 +592,7 @@ func TestRubyParse(t *testing.T) {
func checkTime(time Time, test *ParseTest, t *testing.T) {
// The time should be Thu Feb 4 21:00:57 PST 2010
- if test.yearSign*time.Year() != 2010 {
+ if test.yearSign >= 0 && test.yearSign*time.Year() != 2010 {
t.Errorf("%s: bad year: %d not %d", test.name, time.Year(), 2010)
}
if time.Month() != February {
@@ -440,6 +673,14 @@ var parseErrorTests = []ParseErrorTest{
{"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59x01 2010", "cannot parse"},
{"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.xxx 2010", "cannot parse"},
{"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.-123 2010", "fractional second out of range"},
+ // issue 4502. StampNano requires exactly 9 digits of precision.
+ {StampNano, "Dec 7 11:22:01.000000", `cannot parse ".000000" as ".000000000"`},
+ {StampNano, "Dec 7 11:22:01.0000000000", "extra text: 0"},
+ // issue 4493. Helpful errors.
+ {RFC3339, "2006-01-02T15:04:05Z07:00", `parsing time "2006-01-02T15:04:05Z07:00": extra text: 07:00`},
+ {RFC3339, "2006-01-02T15:04_abc", `parsing time "2006-01-02T15:04_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as ":"`},
+ {RFC3339, "2006-01-02T15:04:05_abc", `parsing time "2006-01-02T15:04:05_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as "Z07:00"`},
+ {RFC3339, "2006-01-02T15:04:05Z_abc", `parsing time "2006-01-02T15:04:05Z_abc": extra text: _abc`},
}
func TestParseErrors(t *testing.T) {
@@ -601,6 +842,103 @@ func TestISOWeek(t *testing.T) {
}
}
+type YearDayTest struct {
+ year, month, day int
+ yday int
+}
+
+// Test YearDay in several different scenarios
+// and corner cases
+var yearDayTests = []YearDayTest{
+ // Non-leap-year tests
+ {2007, 1, 1, 1},
+ {2007, 1, 15, 15},
+ {2007, 2, 1, 32},
+ {2007, 2, 15, 46},
+ {2007, 3, 1, 60},
+ {2007, 3, 15, 74},
+ {2007, 4, 1, 91},
+ {2007, 12, 31, 365},
+
+ // Leap-year tests
+ {2008, 1, 1, 1},
+ {2008, 1, 15, 15},
+ {2008, 2, 1, 32},
+ {2008, 2, 15, 46},
+ {2008, 3, 1, 61},
+ {2008, 3, 15, 75},
+ {2008, 4, 1, 92},
+ {2008, 12, 31, 366},
+
+ // Looks like leap-year (but isn't) tests
+ {1900, 1, 1, 1},
+ {1900, 1, 15, 15},
+ {1900, 2, 1, 32},
+ {1900, 2, 15, 46},
+ {1900, 3, 1, 60},
+ {1900, 3, 15, 74},
+ {1900, 4, 1, 91},
+ {1900, 12, 31, 365},
+
+ // Year one tests (non-leap)
+ {1, 1, 1, 1},
+ {1, 1, 15, 15},
+ {1, 2, 1, 32},
+ {1, 2, 15, 46},
+ {1, 3, 1, 60},
+ {1, 3, 15, 74},
+ {1, 4, 1, 91},
+ {1, 12, 31, 365},
+
+ // Year minus one tests (non-leap)
+ {-1, 1, 1, 1},
+ {-1, 1, 15, 15},
+ {-1, 2, 1, 32},
+ {-1, 2, 15, 46},
+ {-1, 3, 1, 60},
+ {-1, 3, 15, 74},
+ {-1, 4, 1, 91},
+ {-1, 12, 31, 365},
+
+ // 400 BC tests (leap-year)
+ {-400, 1, 1, 1},
+ {-400, 1, 15, 15},
+ {-400, 2, 1, 32},
+ {-400, 2, 15, 46},
+ {-400, 3, 1, 61},
+ {-400, 3, 15, 75},
+ {-400, 4, 1, 92},
+ {-400, 12, 31, 366},
+
+ // Special Cases
+
+ // Gregorian calendar change (no effect)
+ {1582, 10, 4, 277},
+ {1582, 10, 15, 288},
+}
+
+// Check to see if YearDay is location sensitive
+var yearDayLocations = []*Location{
+ FixedZone("UTC-8", -8*60*60),
+ FixedZone("UTC-4", -4*60*60),
+ UTC,
+ FixedZone("UTC+4", 4*60*60),
+ FixedZone("UTC+8", 8*60*60),
+}
+
+func TestYearDay(t *testing.T) {
+ for _, loc := range yearDayLocations {
+ for _, ydt := range yearDayTests {
+ dt := Date(ydt.year, Month(ydt.month), ydt.day, 0, 0, 0, 0, loc)
+ yday := dt.YearDay()
+ if yday != ydt.yday {
+ t.Errorf("got %d, expected %d for %d-%02d-%02d in %v",
+ yday, ydt.yday, ydt.year, ydt.month, ydt.day, loc)
+ }
+ }
+ }
+}
+
var durationTests = []struct {
str string
d Duration
@@ -741,7 +1079,7 @@ var gobTests = []Time{
Date(0, 1, 2, 3, 4, 5, 6, UTC),
Date(7, 8, 9, 10, 11, 12, 13, FixedZone("", 0)),
Unix(81985467080890095, 0x76543210), // Time.sec: 0x0123456789ABCDEF
- Time{}, // nil location
+ {}, // nil location
Date(1, 2, 3, 4, 5, 6, 7, FixedZone("", 32767*60)),
Date(1, 2, 3, 4, 5, 6, 7, FixedZone("", -32768*60)),
}
@@ -805,7 +1143,7 @@ var jsonTests = []struct {
time Time
json string
}{
- {Date(9999, 4, 12, 23, 20, 50, .52*1e9, UTC), `"9999-04-12T23:20:50.52Z"`},
+ {Date(9999, 4, 12, 23, 20, 50, 520*1e6, UTC), `"9999-04-12T23:20:50.52Z"`},
{Date(1996, 12, 19, 16, 39, 57, 0, Local), `"1996-12-19T16:39:57-08:00"`},
{Date(0, 1, 1, 0, 0, 0, 1, FixedZone("", 1*60)), `"0000-01-01T00:00:00.000000001+00:01"`},
}
@@ -892,6 +1230,8 @@ var parseDurationTests = []struct {
{"-2m3.4s", true, -(2*Minute + 3*Second + 400*Millisecond)},
{"1h2m3s4ms5us6ns", true, 1*Hour + 2*Minute + 3*Second + 4*Millisecond + 5*Microsecond + 6*Nanosecond},
{"39h9m14.425s", true, 39*Hour + 9*Minute + 14*Second + 425*Millisecond},
+ // large value
+ {"52763797000ns", true, 52763797000 * Nanosecond},
// errors
{"", false, 0},
@@ -928,16 +1268,86 @@ func TestParseDurationRoundTrip(t *testing.T) {
}
}
+// golang.org/issue/4622
+func TestLocationRace(t *testing.T) {
+ ResetLocalOnceForTest() // reset the Once to trigger the race
+
+ c := make(chan string, 1)
+ go func() {
+ c <- Now().String()
+ }()
+ Now().String()
+ <-c
+ Sleep(100 * Millisecond)
+
+ // Back to Los Angeles for subsequent tests:
+ ForceUSPacificForTesting()
+}
+
+var (
+ t Time
+ u int64
+)
+
+var mallocTest = []struct {
+ count int
+ desc string
+ fn func()
+}{
+ {0, `time.Now()`, func() { t = Now() }},
+ {0, `time.Now().UnixNano()`, func() { u = Now().UnixNano() }},
+}
+
+func TestCountMallocs(t *testing.T) {
+ for _, mt := range mallocTest {
+ allocs := int(testing.AllocsPerRun(100, mt.fn))
+ if allocs > mt.count {
+ t.Errorf("%s: %d allocs, want %d", mt.desc, allocs, mt.count)
+ }
+ }
+}
+
+func TestLoadFixed(t *testing.T) {
+ // Issue 4064: handle locations without any zone transitions.
+ loc, err := LoadLocation("Etc/GMT+1")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // The tzdata name Etc/GMT+1 uses "east is negative",
+ // but Go and most other systems use "east is positive".
+ // So GMT+1 corresponds to -3600 in the Go zone, not +3600.
+ name, offset := Now().In(loc).Zone()
+ if name != "GMT+1" || offset != -1*60*60 {
+ t.Errorf("Now().In(loc).Zone() = %q, %d, want %q, %d", name, offset, "GMT+1", -1*60*60)
+ }
+}
+
func BenchmarkNow(b *testing.B) {
for i := 0; i < b.N; i++ {
- Now()
+ t = Now()
+ }
+}
+
+func BenchmarkNowUnixNano(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ u = Now().UnixNano()
}
}
func BenchmarkFormat(b *testing.B) {
- time := Unix(1265346057, 0)
+ t := Unix(1265346057, 0)
+ for i := 0; i < b.N; i++ {
+ t.Format("Mon Jan 2 15:04:05 2006")
+ }
+}
+
+func BenchmarkFormatNow(b *testing.B) {
+ // Like BenchmarkFormat, but easier, because the time zone
+ // lookup cache is optimized for the present.
+ t := Now()
for i := 0; i < b.N; i++ {
- time.Format("Mon Jan 2 15:04:05 2006")
+ t.Format("Mon Jan 2 15:04:05 2006")
}
}
diff --git a/src/pkg/time/zoneinfo.go b/src/pkg/time/zoneinfo.go
index 3c5774404..c44477f47 100644
--- a/src/pkg/time/zoneinfo.go
+++ b/src/pkg/time/zoneinfo.go
@@ -123,35 +123,58 @@ func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start
// Not using sort.Search to avoid dependencies.
tx := l.tx
end = 1<<63 - 1
- for len(tx) > 1 {
- m := len(tx) / 2
+ lo := 0
+ hi := len(tx)
+ for hi-lo > 1 {
+ m := lo + (hi-lo)/2
lim := tx[m].when
if sec < lim {
end = lim
- tx = tx[0:m]
+ hi = m
} else {
- tx = tx[m:]
+ lo = m
}
}
- zone := &l.zone[tx[0].index]
+ zone := &l.zone[tx[lo].index]
name = zone.name
offset = zone.offset
isDST = zone.isDST
- start = tx[0].when
+ start = tx[lo].when
// end = maintained during the search
return
}
// lookupName returns information about the time zone with
-// the given name (such as "EST").
-func (l *Location) lookupName(name string) (offset int, isDST bool, ok bool) {
+// the given name (such as "EST") at the given pseudo-Unix time
+// (what the given time of day would be in UTC).
+func (l *Location) lookupName(name string, unix int64) (offset int, isDST bool, ok bool) {
l = l.get()
+
+ // First try for a zone with the right name that was actually
+ // in effect at the given time. (In Sydney, Australia, both standard
+ // and daylight-savings time are abbreviated "EST". Using the
+ // offset helps us pick the right one for the given time.
+ // It's not perfect: during the backward transition we might pick
+ // either one.)
+ for i := range l.zone {
+ zone := &l.zone[i]
+ if zone.name == name {
+ nam, offset, isDST, _, _ := l.lookup(unix - int64(zone.offset))
+ if nam == zone.name {
+ return offset, isDST, true
+ }
+ }
+ }
+
+ // Otherwise fall back to an ordinary name match.
for i := range l.zone {
zone := &l.zone[i]
if zone.name == name {
return zone.offset, zone.isDST, true
}
}
+
+ // Otherwise, give up.
return
}
diff --git a/src/pkg/time/zoneinfo_read.go b/src/pkg/time/zoneinfo_read.go
index ebb4205a9..4519c9962 100644
--- a/src/pkg/time/zoneinfo_read.go
+++ b/src/pkg/time/zoneinfo_read.go
@@ -141,7 +141,7 @@ func loadZoneData(bytes []byte) (l *Location, err error) {
if n, ok = zonedata.big4(); !ok {
return nil, badData
}
- zone[i].offset = int(n)
+ zone[i].offset = int(int32(n))
var b byte
if b, ok = zonedata.byte(); !ok {
return nil, badData
@@ -174,7 +174,13 @@ func loadZoneData(bytes []byte) (l *Location, err error) {
}
}
- // Commited to succeed.
+ if len(tx) == 0 {
+ // Build fake transition to cover all time.
+ // This happens in fixed locations like "Etc/GMT0".
+ tx = append(tx, zoneTrans{when: -1 << 63, index: 0})
+ }
+
+ // Committed to succeed.
l = &Location{zone: zone, tx: tx}
// Fill in the cache with information about right now,
@@ -284,7 +290,7 @@ func loadZoneZip(zipfile, name string) (l *Location, err error) {
// 42 off[4]
// 46 name[namelen]
// 46+namelen+xlen+fclen - next header
- //
+ //
if get4(buf) != zcheader {
break
}
diff --git a/src/pkg/time/zoneinfo_windows.go b/src/pkg/time/zoneinfo_windows.go
index d596fab93..a8d3dcbfe 100644
--- a/src/pkg/time/zoneinfo_windows.go
+++ b/src/pkg/time/zoneinfo_windows.go
@@ -15,9 +15,9 @@ import (
// BUG(brainman,rsc): On Windows, the operating system does not provide complete
// time zone information.
// The implementation assumes that this year's rules for daylight savings
-// time apply to all previous and future years as well.
+// time apply to all previous and future years as well.
// Also, time zone abbreviations are unavailable. The implementation constructs
-// them using the capital letters from a longer time zone description.
+// them using the capital letters from a longer time zone description.
// abbrev returns the abbreviation to use for the given zone name.
func abbrev(name []uint16) string {
diff --git a/src/pkg/unicode/digit.go b/src/pkg/unicode/digit.go
index 4800bd6ea..53171b396 100644
--- a/src/pkg/unicode/digit.go
+++ b/src/pkg/unicode/digit.go
@@ -9,5 +9,5 @@ func IsDigit(r rune) bool {
if r <= MaxLatin1 {
return '0' <= r && r <= '9'
}
- return Is(Digit, r)
+ return isExcludingLatin(Digit, r)
}
diff --git a/src/pkg/unicode/graphic.go b/src/pkg/unicode/graphic.go
index 0de90ebd8..5b995fcd0 100644
--- a/src/pkg/unicode/graphic.go
+++ b/src/pkg/unicode/graphic.go
@@ -6,15 +6,17 @@ package unicode
// Bit masks for each code point under U+0100, for fast lookup.
const (
- pC = 1 << iota // a control character.
- pP // a punctuation character.
- pN // a numeral.
- pS // a symbolic character.
- pZ // a spacing character.
- pLu // an upper-case letter.
- pLl // a lower-case letter.
- pp // a printable character according to Go's definition.
- pg = pp | pZ // a graphical character according to the Unicode definition.
+ pC = 1 << iota // a control character.
+ pP // a punctuation character.
+ pN // a numeral.
+ pS // a symbolic character.
+ pZ // a spacing character.
+ pLu // an upper-case letter.
+ pLl // a lower-case letter.
+ pp // a printable character according to Go's definition.
+ pg = pp | pZ // a graphical character according to the Unicode definition.
+ pLo = pLl | pLu // a letter that is neither upper nor lower case.
+ pLmask = pLo
)
// GraphicRanges defines the set of graphic characters according to Unicode.
@@ -76,15 +78,15 @@ func IsControl(r rune) bool {
// IsLetter reports whether the rune is a letter (category L).
func IsLetter(r rune) bool {
if uint32(r) <= MaxLatin1 {
- return properties[uint8(r)]&(pLu|pLl) != 0
+ return properties[uint8(r)]&(pLmask) != 0
}
- return Is(Letter, r)
+ return isExcludingLatin(Letter, r)
}
// IsMark reports whether the rune is a mark character (category M).
func IsMark(r rune) bool {
// There are no mark characters in Latin-1.
- return Is(Mark, r)
+ return isExcludingLatin(Mark, r)
}
// IsNumber reports whether the rune is a number (category N).
@@ -92,7 +94,7 @@ func IsNumber(r rune) bool {
if uint32(r) <= MaxLatin1 {
return properties[uint8(r)]&pN != 0
}
- return Is(Number, r)
+ return isExcludingLatin(Number, r)
}
// IsPunct reports whether the rune is a Unicode punctuation character
@@ -119,7 +121,7 @@ func IsSpace(r rune) bool {
}
return false
}
- return Is(White_Space, r)
+ return isExcludingLatin(White_Space, r)
}
// IsSymbol reports whether the rune is a symbolic character.
@@ -127,5 +129,5 @@ func IsSymbol(r rune) bool {
if uint32(r) <= MaxLatin1 {
return properties[uint8(r)]&pS != 0
}
- return Is(Symbol, r)
+ return isExcludingLatin(Symbol, r)
}
diff --git a/src/pkg/unicode/letter.go b/src/pkg/unicode/letter.go
index be484553d..8d56363df 100644
--- a/src/pkg/unicode/letter.go
+++ b/src/pkg/unicode/letter.go
@@ -19,8 +19,9 @@ const (
// The two slices must be in sorted order and non-overlapping.
// Also, R32 should contain only values >= 0x10000 (1<<16).
type RangeTable struct {
- R16 []Range16
- R32 []Range32
+ R16 []Range16
+ R32 []Range32
+ LatinOffset int // number of entries in R16 with Hi <= MaxLatin1
}
// Range16 represents of a range of 16-bit Unicode code points. The range runs from Lo to Hi
@@ -80,14 +81,31 @@ const (
UpperLower = MaxRune + 1 // (Cannot be a valid delta.)
)
-// is16 uses binary search to test whether rune is in the specified slice of 16-bit ranges.
+// linearMax is the maximum size table for linear search for non-Latin1 rune.
+// Derived by running 'go test -calibrate'.
+const linearMax = 18
+
+// is16 reports whether r is in the sorted slice of 16-bit ranges.
func is16(ranges []Range16, r uint16) bool {
+ if len(ranges) <= linearMax || r <= MaxLatin1 {
+ for i := range ranges {
+ range_ := &ranges[i]
+ if r < range_.Lo {
+ return false
+ }
+ if r <= range_.Hi {
+ return (r-range_.Lo)%range_.Stride == 0
+ }
+ }
+ return false
+ }
+
// binary search over ranges
lo := 0
hi := len(ranges)
for lo < hi {
m := lo + (hi-lo)/2
- range_ := ranges[m]
+ range_ := &ranges[m]
if range_.Lo <= r && r <= range_.Hi {
return (r-range_.Lo)%range_.Stride == 0
}
@@ -100,8 +118,21 @@ func is16(ranges []Range16, r uint16) bool {
return false
}
-// is32 uses binary search to test whether rune is in the specified slice of 32-bit ranges.
+// is32 reports whether r is in the sorted slice of 32-bit ranges.
func is32(ranges []Range32, r uint32) bool {
+ if len(ranges) <= linearMax {
+ for i := range ranges {
+ range_ := &ranges[i]
+ if r < range_.Lo {
+ return false
+ }
+ if r <= range_.Hi {
+ return (r-range_.Lo)%range_.Stride == 0
+ }
+ }
+ return false
+ }
+
// binary search over ranges
lo := 0
hi := len(ranges)
@@ -122,21 +153,6 @@ func is32(ranges []Range32, r uint32) bool {
// Is tests whether rune is in the specified table of ranges.
func Is(rangeTab *RangeTable, r rune) bool {
- // common case: rune is ASCII or Latin-1.
- if uint32(r) <= MaxLatin1 {
- // Only need to check R16, since R32 is always >= 1<<16.
- r16 := uint16(r)
- for _, r := range rangeTab.R16 {
- if r16 > r.Hi {
- continue
- }
- if r16 < r.Lo {
- return false
- }
- return (r16-r.Lo)%r.Stride == 0
- }
- return false
- }
r16 := rangeTab.R16
if len(r16) > 0 && r <= rune(r16[len(r16)-1].Hi) {
return is16(r16, uint16(r))
@@ -148,22 +164,34 @@ func Is(rangeTab *RangeTable, r rune) bool {
return false
}
+func isExcludingLatin(rangeTab *RangeTable, r rune) bool {
+ r16 := rangeTab.R16
+ if off := rangeTab.LatinOffset; len(r16) > off && r <= rune(r16[len(r16)-1].Hi) {
+ return is16(r16[off:], uint16(r))
+ }
+ r32 := rangeTab.R32
+ if len(r32) > 0 && r >= rune(r32[0].Lo) {
+ return is32(r32, uint32(r))
+ }
+ return false
+}
+
// IsUpper reports whether the rune is an upper case letter.
func IsUpper(r rune) bool {
// See comment in IsGraphic.
if uint32(r) <= MaxLatin1 {
- return properties[uint8(r)]&pLu != 0
+ return properties[uint8(r)]&pLmask == pLu
}
- return Is(Upper, r)
+ return isExcludingLatin(Upper, r)
}
// IsLower reports whether the rune is a lower case letter.
func IsLower(r rune) bool {
// See comment in IsGraphic.
if uint32(r) <= MaxLatin1 {
- return properties[uint8(r)]&pLl != 0
+ return properties[uint8(r)]&pLmask == pLl
}
- return Is(Lower, r)
+ return isExcludingLatin(Lower, r)
}
// IsTitle reports whether the rune is a title case letter.
@@ -171,7 +199,7 @@ func IsTitle(r rune) bool {
if r <= MaxLatin1 {
return false
}
- return Is(Title, r)
+ return isExcludingLatin(Title, r)
}
// to maps the rune using the specified case mapping.
@@ -288,7 +316,7 @@ type foldPair struct {
// SimpleFold iterates over Unicode code points equivalent under
// the Unicode-defined simple case folding. Among the code points
// equivalent to rune (including rune itself), SimpleFold returns the
-// smallest rune >= r if one exists, or else the smallest rune >= 0.
+// smallest rune >= r if one exists, or else the smallest rune >= 0.
//
// For example:
// SimpleFold('A') = 'a'
diff --git a/src/pkg/unicode/letter_test.go b/src/pkg/unicode/letter_test.go
index 2d8056218..e4d5572a0 100644
--- a/src/pkg/unicode/letter_test.go
+++ b/src/pkg/unicode/letter_test.go
@@ -5,6 +5,10 @@
package unicode_test
import (
+ "flag"
+ "fmt"
+ "runtime"
+ "sort"
"testing"
. "unicode"
)
@@ -427,3 +431,117 @@ func TestSimpleFold(t *testing.T) {
}
}
}
+
+// Running 'go test -calibrate' runs the calibration to find a plausible
+// cutoff point for linear search of a range list vs. binary search.
+// We create a fake table and then time how long it takes to do a
+// sequence of searches within that table, for all possible inputs
+// relative to the ranges (something before all, in each, between each, after all).
+// This assumes that all possible runes are equally likely.
+// In practice most runes are ASCII so this is a conservative estimate
+// of an effective cutoff value. In practice we could probably set it higher
+// than what this function recommends.
+
+var calibrate = flag.Bool("calibrate", false, "compute crossover for linear vs. binary search")
+
+func TestCalibrate(t *testing.T) {
+ if !*calibrate {
+ return
+ }
+
+ if runtime.GOARCH == "amd64" {
+ fmt.Printf("warning: running calibration on %s\n", runtime.GOARCH)
+ }
+
+ // Find the point where binary search wins by more than 10%.
+ // The 10% bias gives linear search an edge when they're close,
+ // because on predominantly ASCII inputs linear search is even
+ // better than our benchmarks measure.
+ n := sort.Search(64, func(n int) bool {
+ tab := fakeTable(n)
+ blinear := func(b *testing.B) {
+ tab := tab
+ max := n*5 + 20
+ for i := 0; i < b.N; i++ {
+ for j := 0; j <= max; j++ {
+ linear(tab, uint16(j))
+ }
+ }
+ }
+ bbinary := func(b *testing.B) {
+ tab := tab
+ max := n*5 + 20
+ for i := 0; i < b.N; i++ {
+ for j := 0; j <= max; j++ {
+ binary(tab, uint16(j))
+ }
+ }
+ }
+ bmlinear := testing.Benchmark(blinear)
+ bmbinary := testing.Benchmark(bbinary)
+ fmt.Printf("n=%d: linear=%d binary=%d\n", n, bmlinear.NsPerOp(), bmbinary.NsPerOp())
+ return bmlinear.NsPerOp()*100 > bmbinary.NsPerOp()*110
+ })
+ fmt.Printf("calibration: linear cutoff = %d\n", n)
+}
+
+func fakeTable(n int) []Range16 {
+ var r16 []Range16
+ for i := 0; i < n; i++ {
+ r16 = append(r16, Range16{uint16(i*5 + 10), uint16(i*5 + 12), 1})
+ }
+ return r16
+}
+
+func linear(ranges []Range16, r uint16) bool {
+ for i := range ranges {
+ range_ := &ranges[i]
+ if r < range_.Lo {
+ return false
+ }
+ if r <= range_.Hi {
+ return (r-range_.Lo)%range_.Stride == 0
+ }
+ }
+ return false
+}
+
+func binary(ranges []Range16, r uint16) bool {
+ // binary search over ranges
+ lo := 0
+ hi := len(ranges)
+ for lo < hi {
+ m := lo + (hi-lo)/2
+ range_ := &ranges[m]
+ if range_.Lo <= r && r <= range_.Hi {
+ return (r-range_.Lo)%range_.Stride == 0
+ }
+ if r < range_.Lo {
+ hi = m
+ } else {
+ lo = m + 1
+ }
+ }
+ return false
+}
+
+func TestLatinOffset(t *testing.T) {
+ var maps = []map[string]*RangeTable{
+ Categories,
+ FoldCategory,
+ FoldScript,
+ Properties,
+ Scripts,
+ }
+ for _, m := range maps {
+ for name, tab := range m {
+ i := 0
+ for i < len(tab.R16) && tab.R16[i].Hi <= MaxLatin1 {
+ i++
+ }
+ if tab.LatinOffset != i {
+ t.Errorf("%s: LatinOffset=%d, want %d", name, tab.LatinOffset, i)
+ }
+ }
+ }
+}
diff --git a/src/pkg/unicode/maketables.go b/src/pkg/unicode/maketables.go
index fcd14fc73..53d8b967e 100644
--- a/src/pkg/unicode/maketables.go
+++ b/src/pkg/unicode/maketables.go
@@ -13,7 +13,6 @@ import (
"bufio"
"flag"
"fmt"
- "io"
"log"
"net/http"
"os"
@@ -41,7 +40,7 @@ func main() {
var dataURL = flag.String("data", "", "full URL for UnicodeData.txt; defaults to --url/UnicodeData.txt")
var casefoldingURL = flag.String("casefolding", "", "full URL for CaseFolding.txt; defaults to --url/CaseFolding.txt")
var url = flag.String("url",
- "http://www.unicode.org/Public/6.0.0/ucd/",
+ "http://www.unicode.org/Public/6.2.0/ucd/",
"URL of Unicode database directory")
var tablelist = flag.String("tables",
"all",
@@ -321,16 +320,11 @@ func loadChars() {
flag.Set("data", *url+"UnicodeData.txt")
}
input := open(*dataURL)
+ defer input.close()
+ scanner := bufio.NewScanner(input)
var first rune = 0
- for {
- line, err := input.ReadString('\n')
- if err != nil {
- if err == io.EOF {
- break
- }
- logger.Fatal(err)
- }
- switch parseCategory(line[0 : len(line)-1]) {
+ for scanner.Scan() {
+ switch parseCategory(scanner.Text()) {
case SNormal:
if first != 0 {
logger.Fatalf("bad state normal at %U", lastChar)
@@ -351,7 +345,9 @@ func loadChars() {
first = 0
}
}
- input.close()
+ if scanner.Err() != nil {
+ logger.Fatal(scanner.Err())
+ }
}
func loadCasefold() {
@@ -359,15 +355,11 @@ func loadCasefold() {
flag.Set("casefolding", *url+"CaseFolding.txt")
}
input := open(*casefoldingURL)
- for {
- line, err := input.ReadString('\n')
- if err != nil {
- if err == io.EOF {
- break
- }
- logger.Fatal(err)
- }
- if line[0] == '#' {
+ defer input.close()
+ scanner := bufio.NewScanner(input)
+ for scanner.Scan() {
+ line := scanner.Text()
+ if len(line) == 0 || line[0] == '#' || len(strings.TrimSpace(line)) == 0 {
continue
}
field := strings.Split(line, "; ")
@@ -389,7 +381,9 @@ func loadCasefold() {
}
chars[p1].foldCase = rune(p2)
}
- input.close()
+ if scanner.Err() != nil {
+ logger.Fatal(scanner.Err())
+ }
}
const progHeader = `// Generated by running
@@ -503,6 +497,7 @@ const format = "\t\t{0x%04x, 0x%04x, %d},\n"
func dumpRange(header string, inCategory Op) {
fmt.Print(header)
next := rune(0)
+ latinOffset := 0
fmt.Print("\tR16: []Range16{\n")
// one Range for each iteration
count := &range16Count
@@ -546,11 +541,17 @@ func dumpRange(header string, inCategory Op) {
break
}
}
+ if uint32(hi) <= unicode.MaxLatin1 {
+ latinOffset++
+ }
size, count = printRange(uint32(lo), uint32(hi), uint32(stride), size, count)
// next range: start looking where this range ends
next = hi + 1
}
fmt.Print("\t},\n")
+ if latinOffset > 0 {
+ fmt.Printf("\tLatinOffset: %d,\n", latinOffset)
+ }
fmt.Print("}\n\n")
}
@@ -703,15 +704,12 @@ func printScriptOrProperty(doProps bool) {
return
}
input := open(*url + file)
- for {
- line, err := input.ReadString('\n')
- if err != nil {
- if err == io.EOF {
- break
- }
- logger.Fatal(err)
- }
- parseScript(line[0:len(line)-1], table)
+ scanner := bufio.NewScanner(input)
+ for scanner.Scan() {
+ parseScript(scanner.Text(), table)
+ }
+ if scanner.Err() != nil {
+ logger.Fatal(scanner.Err())
}
input.close()
@@ -760,14 +758,17 @@ func printScriptOrProperty(doProps bool) {
}
ndecl++
fmt.Printf("var _%s = &RangeTable {\n", name)
- fmt.Print("\tR16: []Range16{\n")
ranges := foldAdjacent(table[name])
+ fmt.Print("\tR16: []Range16{\n")
size := 16
count := &range16Count
for _, s := range ranges {
size, count = printRange(s.Lo, s.Hi, s.Stride, size, count)
}
fmt.Print("\t},\n")
+ if off := findLatinOffset(ranges); off > 0 {
+ fmt.Printf("\tLatinOffset: %d,\n", off)
+ }
fmt.Print("}\n\n")
}
decl.Sort()
@@ -779,6 +780,14 @@ func printScriptOrProperty(doProps bool) {
fmt.Print(")\n\n")
}
+func findLatinOffset(ranges []unicode.Range32) int {
+ i := 0
+ for i < len(ranges) && ranges[i].Hi <= unicode.MaxLatin1 {
+ i++
+ }
+ return i
+}
+
const (
CaseUpper = 1 << iota
CaseLower
@@ -1022,6 +1031,8 @@ func printLatinProperties() {
property = "0"
case "Ll":
property = "pLl | pp"
+ case "Lo":
+ property = "pLo | pp"
case "Lu":
property = "pLu | pp"
case "Nd", "No":
diff --git a/src/pkg/unicode/script_test.go b/src/pkg/unicode/script_test.go
index 1c5b80142..395cc71a0 100644
--- a/src/pkg/unicode/script_test.go
+++ b/src/pkg/unicode/script_test.go
@@ -14,7 +14,7 @@ type T struct {
script string
}
-// Hand-chosen tests from Unicode 5.1.0 & 6.0..0, mostly to discover when new
+// Hand-chosen tests from Unicode 5.1.0, 6.0.0 and 6.2.0 mostly to discover when new
// scripts and categories arise.
var inTest = []T{
{0x06e2, "Arabic"},
@@ -31,6 +31,7 @@ var inTest = []T{
{0x11011, "Brahmi"},
{0x156d, "Canadian_Aboriginal"},
{0x102a9, "Carian"},
+ {0x11111, "Chakma"},
{0xaa4d, "Cham"},
{0x13c2, "Cherokee"},
{0x0020, "Common"},
@@ -76,6 +77,9 @@ var inTest = []T{
{0x0d42, "Malayalam"},
{0x0843, "Mandaic"},
{0xabd0, "Meetei_Mayek"},
+ {0x1099f, "Meroitic_Hieroglyphs"},
+ {0x109a0, "Meroitic_Cursive"},
+ {0x16f00, "Miao"},
{0x1822, "Mongolian"},
{0x104c, "Myanmar"},
{0x19c3, "New_Tai_Lue"},
@@ -94,8 +98,10 @@ var inTest = []T{
{0x16c0, "Runic"},
{0x081d, "Samaritan"},
{0xa892, "Saurashtra"},
+ {0x111a0, "Sharada"},
{0x10463, "Shavian"},
{0x0dbd, "Sinhala"},
+ {0x110d0, "Sora_Sompeng"},
{0x1ba3, "Sundanese"},
{0xa803, "Syloti_Nagri"},
{0x070f, "Syriac"},
@@ -104,6 +110,7 @@ var inTest = []T{
{0x1972, "Tai_Le"},
{0x1a62, "Tai_Tham"},
{0xaadc, "Tai_Viet"},
+ {0x116c9, "Takri"},
{0x0bbf, "Tamil"},
{0x0c55, "Telugu"},
{0x07a7, "Thaana"},
@@ -121,7 +128,7 @@ var outTest = []T{ // not really worth being thorough
var inCategoryTest = []T{
{0x0081, "Cc"},
- {0x17b4, "Cf"},
+ {0x200B, "Cf"},
{0xf0000, "Co"},
{0xdb80, "Cs"},
{0x0236, "Ll"},
diff --git a/src/pkg/unicode/tables.go b/src/pkg/unicode/tables.go
index ebd169b09..36b5a3115 100644
--- a/src/pkg/unicode/tables.go
+++ b/src/pkg/unicode/tables.go
@@ -1,11 +1,11 @@
// Generated by running
-// maketables --tables=all --data=http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/6.0.0/ucd/CaseFolding.txt
+// maketables --tables=all --data=http://www.unicode.org/Public/6.2.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/6.2.0/ucd/CaseFolding.txt
// DO NOT EDIT
package unicode
// Version is the Unicode edition from which the tables are derived.
-const Version = "6.0.0"
+const Version = "6.2.0"
// Categories is the set of Unicode category tables.
var Categories = map[string]*RangeTable{
@@ -52,9 +52,8 @@ var _C = &RangeTable{
{0x0001, 0x001f, 1},
{0x007f, 0x009f, 1},
{0x00ad, 0x0600, 1363},
- {0x0601, 0x0603, 1},
+ {0x0601, 0x0604, 1},
{0x06dd, 0x070f, 50},
- {0x17b4, 0x17b5, 1},
{0x200b, 0x200f, 1},
{0x202a, 0x202e, 1},
{0x2060, 0x2064, 1},
@@ -71,6 +70,7 @@ var _C = &RangeTable{
{0xf0000, 0xffffd, 1},
{0x100000, 0x10fffd, 1},
},
+ LatinOffset: 2,
}
var _Cc = &RangeTable{
@@ -78,14 +78,14 @@ var _Cc = &RangeTable{
{0x0001, 0x001f, 1},
{0x007f, 0x009f, 1},
},
+ LatinOffset: 2,
}
var _Cf = &RangeTable{
R16: []Range16{
{0x00ad, 0x0600, 1363},
- {0x0601, 0x0603, 1},
+ {0x0601, 0x0604, 1},
{0x06dd, 0x070f, 50},
- {0x17b4, 0x17b5, 1},
{0x200b, 0x200f, 1},
{0x202a, 0x202e, 1},
{0x2060, 0x2064, 1},
@@ -162,6 +162,8 @@ var _L = &RangeTable{
{0x081a, 0x0824, 10},
{0x0828, 0x0840, 24},
{0x0841, 0x0858, 1},
+ {0x08a0, 0x08a2, 2},
+ {0x08a3, 0x08ac, 1},
{0x0904, 0x0939, 1},
{0x093d, 0x0950, 19},
{0x0958, 0x0961, 1},
@@ -259,8 +261,9 @@ var _L = &RangeTable{
{0x0ebd, 0x0ec0, 3},
{0x0ec1, 0x0ec4, 1},
{0x0ec6, 0x0edc, 22},
- {0x0edd, 0x0f00, 35},
- {0x0f40, 0x0f47, 1},
+ {0x0edd, 0x0edf, 1},
+ {0x0f00, 0x0f40, 64},
+ {0x0f41, 0x0f47, 1},
{0x0f49, 0x0f6c, 1},
{0x0f88, 0x0f8c, 1},
{0x1000, 0x102a, 1},
@@ -273,9 +276,9 @@ var _L = &RangeTable{
{0x1075, 0x1081, 1},
{0x108e, 0x10a0, 18},
{0x10a1, 0x10c5, 1},
+ {0x10c7, 0x10cd, 6},
{0x10d0, 0x10fa, 1},
- {0x10fc, 0x1100, 4},
- {0x1101, 0x1248, 1},
+ {0x10fc, 0x1248, 1},
{0x124a, 0x124d, 1},
{0x1250, 0x1256, 1},
{0x1258, 0x125a, 2},
@@ -321,12 +324,13 @@ var _L = &RangeTable{
{0x1b45, 0x1b4b, 1},
{0x1b83, 0x1ba0, 1},
{0x1bae, 0x1baf, 1},
- {0x1bc0, 0x1be5, 1},
+ {0x1bba, 0x1be5, 1},
{0x1c00, 0x1c23, 1},
{0x1c4d, 0x1c4f, 1},
{0x1c5a, 0x1c7d, 1},
{0x1ce9, 0x1cec, 1},
{0x1cee, 0x1cf1, 1},
+ {0x1cf5, 0x1cf6, 1},
{0x1d00, 0x1dbf, 1},
{0x1e00, 0x1f15, 1},
{0x1f18, 0x1f1d, 1},
@@ -362,8 +366,10 @@ var _L = &RangeTable{
{0x2c30, 0x2c5e, 1},
{0x2c60, 0x2ce4, 1},
{0x2ceb, 0x2cee, 1},
+ {0x2cf2, 0x2cf3, 1},
{0x2d00, 0x2d25, 1},
- {0x2d30, 0x2d65, 1},
+ {0x2d27, 0x2d2d, 6},
+ {0x2d30, 0x2d67, 1},
{0x2d6f, 0x2d80, 17},
{0x2d81, 0x2d96, 1},
{0x2da0, 0x2da6, 1},
@@ -387,7 +393,7 @@ var _L = &RangeTable{
{0x31a0, 0x31ba, 1},
{0x31f0, 0x31ff, 1},
{0x3400, 0x4db5, 1},
- {0x4e00, 0x9fcb, 1},
+ {0x4e00, 0x9fcc, 1},
{0xa000, 0xa48c, 1},
{0xa4d0, 0xa4fd, 1},
{0xa500, 0xa60c, 1},
@@ -399,9 +405,9 @@ var _L = &RangeTable{
{0xa717, 0xa71f, 1},
{0xa722, 0xa788, 1},
{0xa78b, 0xa78e, 1},
- {0xa790, 0xa791, 1},
- {0xa7a0, 0xa7a9, 1},
- {0xa7fa, 0xa801, 1},
+ {0xa790, 0xa793, 1},
+ {0xa7a0, 0xa7aa, 1},
+ {0xa7f8, 0xa801, 1},
{0xa803, 0xa805, 1},
{0xa807, 0xa80a, 1},
{0xa80c, 0xa822, 1},
@@ -425,6 +431,8 @@ var _L = &RangeTable{
{0xaaba, 0xaabd, 1},
{0xaac0, 0xaac2, 2},
{0xaadb, 0xaadd, 1},
+ {0xaae0, 0xaaea, 1},
+ {0xaaf2, 0xaaf4, 1},
{0xab01, 0xab06, 1},
{0xab09, 0xab0e, 1},
{0xab11, 0xab16, 1},
@@ -434,8 +442,7 @@ var _L = &RangeTable{
{0xac00, 0xd7a3, 1},
{0xd7b0, 0xd7c6, 1},
{0xd7cb, 0xd7fb, 1},
- {0xf900, 0xfa2d, 1},
- {0xfa30, 0xfa6d, 1},
+ {0xf900, 0xfa6d, 1},
{0xfa70, 0xfad9, 1},
{0xfb00, 0xfb06, 1},
{0xfb13, 0xfb17, 1},
@@ -486,6 +493,8 @@ var _L = &RangeTable{
{0x10840, 0x10855, 1},
{0x10900, 0x10915, 1},
{0x10920, 0x10939, 1},
+ {0x10980, 0x109b7, 1},
+ {0x109be, 0x109bf, 1},
{0x10a00, 0x10a10, 16},
{0x10a11, 0x10a13, 1},
{0x10a15, 0x10a17, 1},
@@ -497,9 +506,17 @@ var _L = &RangeTable{
{0x10c00, 0x10c48, 1},
{0x11003, 0x11037, 1},
{0x11083, 0x110af, 1},
+ {0x110d0, 0x110e8, 1},
+ {0x11103, 0x11126, 1},
+ {0x11183, 0x111b2, 1},
+ {0x111c1, 0x111c4, 1},
+ {0x11680, 0x116aa, 1},
{0x12000, 0x1236e, 1},
{0x13000, 0x1342e, 1},
{0x16800, 0x16a38, 1},
+ {0x16f00, 0x16f44, 1},
+ {0x16f50, 0x16f93, 67},
+ {0x16f94, 0x16f9f, 1},
{0x1b000, 0x1b001, 1},
{0x1d400, 0x1d454, 1},
{0x1d456, 0x1d49c, 1},
@@ -531,18 +548,42 @@ var _L = &RangeTable{
{0x1d78a, 0x1d7a8, 1},
{0x1d7aa, 0x1d7c2, 1},
{0x1d7c4, 0x1d7cb, 1},
+ {0x1ee00, 0x1ee03, 1},
+ {0x1ee05, 0x1ee1f, 1},
+ {0x1ee21, 0x1ee22, 1},
+ {0x1ee24, 0x1ee27, 3},
+ {0x1ee29, 0x1ee32, 1},
+ {0x1ee34, 0x1ee37, 1},
+ {0x1ee39, 0x1ee3b, 2},
+ {0x1ee42, 0x1ee47, 5},
+ {0x1ee49, 0x1ee4d, 2},
+ {0x1ee4e, 0x1ee4f, 1},
+ {0x1ee51, 0x1ee52, 1},
+ {0x1ee54, 0x1ee57, 3},
+ {0x1ee59, 0x1ee61, 2},
+ {0x1ee62, 0x1ee64, 2},
+ {0x1ee67, 0x1ee6a, 1},
+ {0x1ee6c, 0x1ee72, 1},
+ {0x1ee74, 0x1ee77, 1},
+ {0x1ee79, 0x1ee7c, 1},
+ {0x1ee7e, 0x1ee80, 2},
+ {0x1ee81, 0x1ee89, 1},
+ {0x1ee8b, 0x1ee9b, 1},
+ {0x1eea1, 0x1eea3, 1},
+ {0x1eea5, 0x1eea9, 1},
+ {0x1eeab, 0x1eebb, 1},
{0x20000, 0x2a6d6, 1},
{0x2a700, 0x2b734, 1},
{0x2b740, 0x2b81d, 1},
{0x2f800, 0x2fa1d, 1},
},
+ LatinOffset: 6,
}
var _Ll = &RangeTable{
R16: []Range16{
{0x0061, 0x007a, 1},
- {0x00aa, 0x00b5, 11},
- {0x00ba, 0x00df, 37},
+ {0x00b5, 0x00df, 42},
{0x00e0, 0x00f6, 1},
{0x00f8, 0x00ff, 1},
{0x0101, 0x0137, 2},
@@ -593,7 +634,7 @@ var _Ll = &RangeTable{
{0x04cf, 0x0527, 2},
{0x0561, 0x0587, 1},
{0x1d00, 0x1d2b, 1},
- {0x1d62, 0x1d77, 1},
+ {0x1d6b, 0x1d77, 1},
{0x1d79, 0x1d9a, 1},
{0x1e01, 0x1e95, 2},
{0x1e96, 0x1e9d, 1},
@@ -630,11 +671,12 @@ var _Ll = &RangeTable{
{0x2c66, 0x2c6c, 2},
{0x2c71, 0x2c73, 2},
{0x2c74, 0x2c76, 2},
- {0x2c77, 0x2c7c, 1},
+ {0x2c77, 0x2c7b, 1},
{0x2c81, 0x2ce3, 2},
{0x2ce4, 0x2cec, 8},
- {0x2cee, 0x2d00, 18},
- {0x2d01, 0x2d25, 1},
+ {0x2cee, 0x2cf3, 5},
+ {0x2d00, 0x2d25, 1},
+ {0x2d27, 0x2d2d, 6},
{0xa641, 0xa66d, 2},
{0xa681, 0xa697, 2},
{0xa723, 0xa72f, 2},
@@ -644,8 +686,8 @@ var _Ll = &RangeTable{
{0xa77a, 0xa77c, 2},
{0xa77f, 0xa787, 2},
{0xa78c, 0xa78e, 2},
- {0xa791, 0xa7a1, 16},
- {0xa7a3, 0xa7a9, 2},
+ {0xa791, 0xa793, 2},
+ {0xa7a1, 0xa7a9, 2},
{0xa7fa, 0xfb00, 21254},
{0xfb01, 0xfb06, 1},
{0xfb13, 0xfb17, 1},
@@ -682,6 +724,7 @@ var _Ll = &RangeTable{
{0x1d7c4, 0x1d7c9, 1},
{0x1d7cb, 0x1d7cb, 1},
},
+ LatinOffset: 4,
}
var _Lm = &RangeTable{
@@ -701,14 +744,15 @@ var _Lm = &RangeTable{
{0x17d7, 0x1843, 108},
{0x1aa7, 0x1c78, 465},
{0x1c79, 0x1c7d, 1},
- {0x1d2c, 0x1d61, 1},
+ {0x1d2c, 0x1d6a, 1},
{0x1d78, 0x1d9b, 35},
{0x1d9c, 0x1dbf, 1},
{0x2071, 0x207f, 14},
{0x2090, 0x209c, 1},
- {0x2c7d, 0x2d6f, 242},
- {0x2e2f, 0x3005, 470},
- {0x3031, 0x3035, 1},
+ {0x2c7c, 0x2c7d, 1},
+ {0x2d6f, 0x2e2f, 192},
+ {0x3005, 0x3031, 44},
+ {0x3032, 0x3035, 1},
{0x303b, 0x309d, 98},
{0x309e, 0x30fc, 94},
{0x30fd, 0x30fe, 1},
@@ -717,14 +761,20 @@ var _Lm = &RangeTable{
{0xa60c, 0xa67f, 115},
{0xa717, 0xa71f, 1},
{0xa770, 0xa788, 24},
+ {0xa7f8, 0xa7f9, 1},
{0xa9cf, 0xaa70, 161},
- {0xaadd, 0xff70, 21651},
+ {0xaadd, 0xaaf3, 22},
+ {0xaaf4, 0xff70, 21628},
{0xff9e, 0xff9f, 1},
},
+ R32: []Range32{
+ {0x16f93, 0x16f9f, 1},
+ },
}
var _Lo = &RangeTable{
R16: []Range16{
+ {0x00aa, 0x00ba, 16},
{0x01bb, 0x01c0, 5},
{0x01c1, 0x01c3, 1},
{0x0294, 0x05d0, 828},
@@ -744,6 +794,8 @@ var _Lo = &RangeTable{
{0x07cb, 0x07ea, 1},
{0x0800, 0x0815, 1},
{0x0840, 0x0858, 1},
+ {0x08a0, 0x08a2, 2},
+ {0x08a3, 0x08ac, 1},
{0x0904, 0x0939, 1},
{0x093d, 0x0950, 19},
{0x0958, 0x0961, 1},
@@ -840,7 +892,7 @@ var _Lo = &RangeTable{
{0x0eb2, 0x0eb3, 1},
{0x0ebd, 0x0ec0, 3},
{0x0ec1, 0x0ec4, 1},
- {0x0edc, 0x0edd, 1},
+ {0x0edc, 0x0edf, 1},
{0x0f00, 0x0f40, 64},
{0x0f41, 0x0f47, 1},
{0x0f49, 0x0f6c, 1},
@@ -855,7 +907,7 @@ var _Lo = &RangeTable{
{0x1075, 0x1081, 1},
{0x108e, 0x10d0, 66},
{0x10d1, 0x10fa, 1},
- {0x1100, 0x1248, 1},
+ {0x10fd, 0x1248, 1},
{0x124a, 0x124d, 1},
{0x1250, 0x1256, 1},
{0x1258, 0x125a, 2},
@@ -901,14 +953,15 @@ var _Lo = &RangeTable{
{0x1b45, 0x1b4b, 1},
{0x1b83, 0x1ba0, 1},
{0x1bae, 0x1baf, 1},
- {0x1bc0, 0x1be5, 1},
+ {0x1bba, 0x1be5, 1},
{0x1c00, 0x1c23, 1},
{0x1c4d, 0x1c4f, 1},
{0x1c5a, 0x1c77, 1},
{0x1ce9, 0x1cec, 1},
{0x1cee, 0x1cf1, 1},
+ {0x1cf5, 0x1cf6, 1},
{0x2135, 0x2138, 1},
- {0x2d30, 0x2d65, 1},
+ {0x2d30, 0x2d67, 1},
{0x2d80, 0x2d96, 1},
{0x2da0, 0x2da6, 1},
{0x2da8, 0x2dae, 1},
@@ -928,7 +981,7 @@ var _Lo = &RangeTable{
{0x31a0, 0x31ba, 1},
{0x31f0, 0x31ff, 1},
{0x3400, 0x4db5, 1},
- {0x4e00, 0x9fcb, 1},
+ {0x4e00, 0x9fcc, 1},
{0xa000, 0xa014, 1},
{0xa016, 0xa48c, 1},
{0xa4d0, 0xa4f7, 1},
@@ -961,7 +1014,9 @@ var _Lo = &RangeTable{
{0xaaba, 0xaabd, 1},
{0xaac0, 0xaac2, 2},
{0xaadb, 0xaadc, 1},
- {0xab01, 0xab06, 1},
+ {0xaae0, 0xaaea, 1},
+ {0xaaf2, 0xab01, 15},
+ {0xab02, 0xab06, 1},
{0xab09, 0xab0e, 1},
{0xab11, 0xab16, 1},
{0xab20, 0xab26, 1},
@@ -970,8 +1025,7 @@ var _Lo = &RangeTable{
{0xac00, 0xd7a3, 1},
{0xd7b0, 0xd7c6, 1},
{0xd7cb, 0xd7fb, 1},
- {0xf900, 0xfa2d, 1},
- {0xfa30, 0xfa6d, 1},
+ {0xf900, 0xfa6d, 1},
{0xfa70, 0xfad9, 1},
{0xfb1d, 0xfb1f, 2},
{0xfb20, 0xfb28, 1},
@@ -1020,6 +1074,8 @@ var _Lo = &RangeTable{
{0x10840, 0x10855, 1},
{0x10900, 0x10915, 1},
{0x10920, 0x10939, 1},
+ {0x10980, 0x109b7, 1},
+ {0x109be, 0x109bf, 1},
{0x10a00, 0x10a10, 16},
{0x10a11, 0x10a13, 1},
{0x10a15, 0x10a17, 1},
@@ -1031,15 +1087,47 @@ var _Lo = &RangeTable{
{0x10c00, 0x10c48, 1},
{0x11003, 0x11037, 1},
{0x11083, 0x110af, 1},
+ {0x110d0, 0x110e8, 1},
+ {0x11103, 0x11126, 1},
+ {0x11183, 0x111b2, 1},
+ {0x111c1, 0x111c4, 1},
+ {0x11680, 0x116aa, 1},
{0x12000, 0x1236e, 1},
{0x13000, 0x1342e, 1},
{0x16800, 0x16a38, 1},
- {0x1b000, 0x1b001, 1},
+ {0x16f00, 0x16f44, 1},
+ {0x16f50, 0x1b000, 16560},
+ {0x1b001, 0x1ee00, 15871},
+ {0x1ee01, 0x1ee03, 1},
+ {0x1ee05, 0x1ee1f, 1},
+ {0x1ee21, 0x1ee22, 1},
+ {0x1ee24, 0x1ee27, 3},
+ {0x1ee29, 0x1ee32, 1},
+ {0x1ee34, 0x1ee37, 1},
+ {0x1ee39, 0x1ee3b, 2},
+ {0x1ee42, 0x1ee47, 5},
+ {0x1ee49, 0x1ee4d, 2},
+ {0x1ee4e, 0x1ee4f, 1},
+ {0x1ee51, 0x1ee52, 1},
+ {0x1ee54, 0x1ee57, 3},
+ {0x1ee59, 0x1ee61, 2},
+ {0x1ee62, 0x1ee64, 2},
+ {0x1ee67, 0x1ee6a, 1},
+ {0x1ee6c, 0x1ee72, 1},
+ {0x1ee74, 0x1ee77, 1},
+ {0x1ee79, 0x1ee7c, 1},
+ {0x1ee7e, 0x1ee80, 2},
+ {0x1ee81, 0x1ee89, 1},
+ {0x1ee8b, 0x1ee9b, 1},
+ {0x1eea1, 0x1eea3, 1},
+ {0x1eea5, 0x1eea9, 1},
+ {0x1eeab, 0x1eebb, 1},
{0x20000, 0x2a6d6, 1},
{0x2a700, 0x2b734, 1},
{0x2b740, 0x2b81d, 1},
{0x2f800, 0x2fa1d, 1},
},
+ LatinOffset: 1,
}
var _Lt = &RangeTable{
@@ -1109,6 +1197,7 @@ var _Lu = &RangeTable{
{0x04d0, 0x0526, 2},
{0x0531, 0x0556, 1},
{0x10a0, 0x10c5, 1},
+ {0x10c7, 0x10cd, 6},
{0x1e00, 0x1e94, 2},
{0x1e9e, 0x1efe, 2},
{0x1f08, 0x1f0f, 1},
@@ -1142,15 +1231,16 @@ var _Lu = &RangeTable{
{0x2c7e, 0x2c80, 1},
{0x2c82, 0x2ce2, 2},
{0x2ceb, 0x2ced, 2},
- {0xa640, 0xa66c, 2},
+ {0x2cf2, 0xa640, 31054},
+ {0xa642, 0xa66c, 2},
{0xa680, 0xa696, 2},
{0xa722, 0xa72e, 2},
{0xa732, 0xa76e, 2},
{0xa779, 0xa77d, 2},
{0xa77e, 0xa786, 2},
{0xa78b, 0xa78d, 2},
- {0xa790, 0xa7a0, 16},
- {0xa7a2, 0xa7a8, 2},
+ {0xa790, 0xa792, 2},
+ {0xa7a0, 0xa7aa, 2},
{0xff21, 0xff3a, 1},
},
R32: []Range32{
@@ -1186,6 +1276,7 @@ var _Lu = &RangeTable{
{0x1d790, 0x1d7a8, 1},
{0x1d7ca, 0x1d7ca, 1},
},
+ LatinOffset: 3,
}
var _M = &RangeTable{
@@ -1212,6 +1303,7 @@ var _M = &RangeTable{
{0x0825, 0x0827, 1},
{0x0829, 0x082d, 1},
{0x0859, 0x085b, 1},
+ {0x08e4, 0x08fe, 1},
{0x0900, 0x0903, 1},
{0x093a, 0x093c, 1},
{0x093e, 0x094f, 1},
@@ -1302,7 +1394,7 @@ var _M = &RangeTable{
{0x1732, 0x1734, 1},
{0x1752, 0x1753, 1},
{0x1772, 0x1773, 1},
- {0x17b6, 0x17d3, 1},
+ {0x17b4, 0x17d3, 1},
{0x17dd, 0x180b, 46},
{0x180c, 0x180d, 1},
{0x18a9, 0x1920, 119},
@@ -1318,12 +1410,13 @@ var _M = &RangeTable{
{0x1b34, 0x1b44, 1},
{0x1b6b, 0x1b73, 1},
{0x1b80, 0x1b82, 1},
- {0x1ba1, 0x1baa, 1},
+ {0x1ba1, 0x1bad, 1},
{0x1be6, 0x1bf3, 1},
{0x1c24, 0x1c37, 1},
{0x1cd0, 0x1cd2, 1},
{0x1cd4, 0x1ce8, 1},
{0x1ced, 0x1cf2, 5},
+ {0x1cf3, 0x1cf4, 1},
{0x1dc0, 0x1de6, 1},
{0x1dfc, 0x1dff, 1},
{0x20d0, 0x20f0, 1},
@@ -1333,11 +1426,11 @@ var _M = &RangeTable{
{0x302a, 0x302f, 1},
{0x3099, 0x309a, 1},
{0xa66f, 0xa672, 1},
- {0xa67c, 0xa67d, 1},
- {0xa6f0, 0xa6f1, 1},
- {0xa802, 0xa806, 4},
- {0xa80b, 0xa823, 24},
- {0xa824, 0xa827, 1},
+ {0xa674, 0xa67d, 1},
+ {0xa69f, 0xa6f0, 81},
+ {0xa6f1, 0xa802, 273},
+ {0xa806, 0xa80b, 5},
+ {0xa823, 0xa827, 1},
{0xa880, 0xa881, 1},
{0xa8b4, 0xa8c4, 1},
{0xa8e0, 0xa8f1, 1},
@@ -1352,8 +1445,10 @@ var _M = &RangeTable{
{0xaab3, 0xaab4, 1},
{0xaab7, 0xaab8, 1},
{0xaabe, 0xaabf, 1},
- {0xaac1, 0xabe3, 290},
- {0xabe4, 0xabea, 1},
+ {0xaac1, 0xaaeb, 42},
+ {0xaaec, 0xaaef, 1},
+ {0xaaf5, 0xaaf6, 1},
+ {0xabe3, 0xabea, 1},
{0xabec, 0xabed, 1},
{0xfb1e, 0xfe00, 738},
{0xfe01, 0xfe0f, 1},
@@ -1370,6 +1465,13 @@ var _M = &RangeTable{
{0x11038, 0x11046, 1},
{0x11080, 0x11082, 1},
{0x110b0, 0x110ba, 1},
+ {0x11100, 0x11102, 1},
+ {0x11127, 0x11134, 1},
+ {0x11180, 0x11182, 1},
+ {0x111b3, 0x111c0, 1},
+ {0x116ab, 0x116b7, 1},
+ {0x16f51, 0x16f7e, 1},
+ {0x16f8f, 0x16f92, 1},
{0x1d165, 0x1d169, 1},
{0x1d16d, 0x1d172, 1},
{0x1d17b, 0x1d182, 1},
@@ -1454,17 +1556,19 @@ var _Mc = &RangeTable{
{0x1b43, 0x1b44, 1},
{0x1b82, 0x1ba1, 31},
{0x1ba6, 0x1ba7, 1},
- {0x1baa, 0x1be7, 61},
+ {0x1baa, 0x1bac, 2},
+ {0x1bad, 0x1be7, 58},
{0x1bea, 0x1bec, 1},
{0x1bee, 0x1bf2, 4},
{0x1bf3, 0x1c24, 49},
{0x1c25, 0x1c2b, 1},
{0x1c34, 0x1c35, 1},
{0x1ce1, 0x1cf2, 17},
- {0xa823, 0xa824, 1},
- {0xa827, 0xa880, 89},
- {0xa881, 0xa8b4, 51},
- {0xa8b5, 0xa8c3, 1},
+ {0x1cf3, 0x302e, 4923},
+ {0x302f, 0xa823, 30708},
+ {0xa824, 0xa827, 3},
+ {0xa880, 0xa881, 1},
+ {0xa8b4, 0xa8c3, 1},
{0xa952, 0xa953, 1},
{0xa983, 0xa9b4, 49},
{0xa9b5, 0xa9ba, 5},
@@ -1473,6 +1577,8 @@ var _Mc = &RangeTable{
{0xaa2f, 0xaa30, 1},
{0xaa33, 0xaa34, 1},
{0xaa4d, 0xaa7b, 46},
+ {0xaaeb, 0xaaee, 3},
+ {0xaaef, 0xaaf5, 6},
{0xabe3, 0xabe4, 1},
{0xabe6, 0xabe7, 1},
{0xabe9, 0xabea, 1},
@@ -1483,6 +1589,12 @@ var _Mc = &RangeTable{
{0x11002, 0x11082, 128},
{0x110b0, 0x110b2, 1},
{0x110b7, 0x110b8, 1},
+ {0x1112c, 0x11182, 86},
+ {0x111b3, 0x111b5, 1},
+ {0x111bf, 0x111c0, 1},
+ {0x116ac, 0x116ae, 2},
+ {0x116af, 0x116b6, 7},
+ {0x16f51, 0x16f7e, 1},
{0x1d165, 0x1d166, 1},
{0x1d16d, 0x1d172, 1},
},
@@ -1521,6 +1633,7 @@ var _Mn = &RangeTable{
{0x0825, 0x0827, 1},
{0x0829, 0x082d, 1},
{0x0859, 0x085b, 1},
+ {0x08e4, 0x08fe, 1},
{0x0900, 0x0902, 1},
{0x093a, 0x093c, 2},
{0x0941, 0x0948, 1},
@@ -1592,6 +1705,7 @@ var _Mn = &RangeTable{
{0x1732, 0x1734, 1},
{0x1752, 0x1753, 1},
{0x1772, 0x1773, 1},
+ {0x17b4, 0x17b5, 1},
{0x17b7, 0x17bd, 1},
{0x17c6, 0x17c9, 3},
{0x17ca, 0x17d3, 1},
@@ -1617,16 +1731,17 @@ var _Mn = &RangeTable{
{0x1b80, 0x1b81, 1},
{0x1ba2, 0x1ba5, 1},
{0x1ba8, 0x1ba9, 1},
- {0x1be6, 0x1be8, 2},
- {0x1be9, 0x1bed, 4},
- {0x1bef, 0x1bf1, 1},
+ {0x1bab, 0x1be6, 59},
+ {0x1be8, 0x1be9, 1},
+ {0x1bed, 0x1bef, 2},
+ {0x1bf0, 0x1bf1, 1},
{0x1c2c, 0x1c33, 1},
{0x1c36, 0x1c37, 1},
{0x1cd0, 0x1cd2, 1},
{0x1cd4, 0x1ce0, 1},
{0x1ce2, 0x1ce8, 1},
- {0x1ced, 0x1dc0, 211},
- {0x1dc1, 0x1de6, 1},
+ {0x1ced, 0x1cf4, 7},
+ {0x1dc0, 0x1de6, 1},
{0x1dfc, 0x1dff, 1},
{0x20d0, 0x20dc, 1},
{0x20e1, 0x20e5, 4},
@@ -1634,10 +1749,11 @@ var _Mn = &RangeTable{
{0x2cef, 0x2cf1, 1},
{0x2d7f, 0x2de0, 97},
{0x2de1, 0x2dff, 1},
- {0x302a, 0x302f, 1},
+ {0x302a, 0x302d, 1},
{0x3099, 0x309a, 1},
- {0xa66f, 0xa67c, 13},
- {0xa67d, 0xa6f0, 115},
+ {0xa66f, 0xa674, 5},
+ {0xa675, 0xa67d, 1},
+ {0xa69f, 0xa6f0, 81},
{0xa6f1, 0xa802, 273},
{0xa806, 0xa80b, 5},
{0xa825, 0xa826, 1},
@@ -1657,10 +1773,11 @@ var _Mn = &RangeTable{
{0xaab3, 0xaab4, 1},
{0xaab7, 0xaab8, 1},
{0xaabe, 0xaabf, 1},
- {0xaac1, 0xabe5, 292},
- {0xabe8, 0xabed, 5},
- {0xfb1e, 0xfe00, 738},
- {0xfe01, 0xfe0f, 1},
+ {0xaac1, 0xaaec, 43},
+ {0xaaed, 0xaaf6, 9},
+ {0xabe5, 0xabe8, 3},
+ {0xabed, 0xfb1e, 20273},
+ {0xfe00, 0xfe0f, 1},
{0xfe20, 0xfe26, 1},
},
R32: []Range32{
@@ -1674,6 +1791,15 @@ var _Mn = &RangeTable{
{0x11080, 0x11081, 1},
{0x110b3, 0x110b6, 1},
{0x110b9, 0x110ba, 1},
+ {0x11100, 0x11102, 1},
+ {0x11127, 0x1112b, 1},
+ {0x1112d, 0x11134, 1},
+ {0x11180, 0x11181, 1},
+ {0x111b6, 0x111be, 1},
+ {0x116ab, 0x116ad, 2},
+ {0x116b0, 0x116b5, 1},
+ {0x116b7, 0x16f8f, 22744},
+ {0x16f90, 0x16f92, 1},
{0x1d167, 0x1d169, 1},
{0x1d17b, 0x1d182, 1},
{0x1d185, 0x1d18b, 1},
@@ -1735,6 +1861,7 @@ var _N = &RangeTable{
{0x3038, 0x303a, 1},
{0x3192, 0x3195, 1},
{0x3220, 0x3229, 1},
+ {0x3248, 0x324f, 1},
{0x3251, 0x325f, 1},
{0x3280, 0x3289, 1},
{0x32b1, 0x32bf, 1},
@@ -1764,11 +1891,16 @@ var _N = &RangeTable{
{0x10b78, 0x10b7f, 1},
{0x10e60, 0x10e7e, 1},
{0x11052, 0x1106f, 1},
+ {0x110f0, 0x110f9, 1},
+ {0x11136, 0x1113f, 1},
+ {0x111d0, 0x111d9, 1},
+ {0x116c0, 0x116c9, 1},
{0x12400, 0x12462, 1},
{0x1d360, 0x1d371, 1},
{0x1d7ce, 0x1d7ff, 1},
{0x1f100, 0x1f10a, 1},
},
+ LatinOffset: 4,
}
var _Nd = &RangeTable{
@@ -1812,8 +1944,13 @@ var _Nd = &RangeTable{
R32: []Range32{
{0x104a0, 0x104a9, 1},
{0x11066, 0x1106f, 1},
+ {0x110f0, 0x110f9, 1},
+ {0x11136, 0x1113f, 1},
+ {0x111d0, 0x111d9, 1},
+ {0x116c0, 0x116c9, 1},
{0x1d7ce, 0x1d7ff, 1},
},
+ LatinOffset: 1,
}
var _Nl = &RangeTable{
@@ -1858,6 +1995,7 @@ var _No = &RangeTable{
{0x2cfd, 0x3192, 1173},
{0x3193, 0x3195, 1},
{0x3220, 0x3229, 1},
+ {0x3248, 0x324f, 1},
{0x3251, 0x325f, 1},
{0x3280, 0x3289, 1},
{0x32b1, 0x32bf, 1},
@@ -1879,6 +2017,7 @@ var _No = &RangeTable{
{0x1d360, 0x1d371, 1},
{0x1f100, 0x1f10a, 1},
},
+ LatinOffset: 3,
}
var _P = &RangeTable{
@@ -1891,7 +2030,8 @@ var _P = &RangeTable{
{0x005b, 0x005d, 1},
{0x005f, 0x007b, 28},
{0x007d, 0x00a1, 36},
- {0x00ab, 0x00b7, 12},
+ {0x00a7, 0x00ab, 4},
+ {0x00b6, 0x00b7, 1},
{0x00bb, 0x00bf, 4},
{0x037e, 0x0387, 9},
{0x055a, 0x055f, 1},
@@ -1910,16 +2050,18 @@ var _P = &RangeTable{
{0x0830, 0x083e, 1},
{0x085e, 0x0964, 262},
{0x0965, 0x0970, 11},
- {0x0df4, 0x0e4f, 91},
- {0x0e5a, 0x0e5b, 1},
- {0x0f04, 0x0f12, 1},
- {0x0f3a, 0x0f3d, 1},
+ {0x0af0, 0x0df4, 772},
+ {0x0e4f, 0x0e5a, 11},
+ {0x0e5b, 0x0f04, 169},
+ {0x0f05, 0x0f12, 1},
+ {0x0f14, 0x0f3a, 38},
+ {0x0f3b, 0x0f3d, 1},
{0x0f85, 0x0fd0, 75},
{0x0fd1, 0x0fd4, 1},
{0x0fd9, 0x0fda, 1},
{0x104a, 0x104f, 1},
- {0x10fb, 0x1361, 614},
- {0x1362, 0x1368, 1},
+ {0x10fb, 0x1360, 613},
+ {0x1361, 0x1368, 1},
{0x1400, 0x166d, 621},
{0x166e, 0x169b, 45},
{0x169c, 0x16eb, 79},
@@ -1936,6 +2078,7 @@ var _P = &RangeTable{
{0x1bfc, 0x1bff, 1},
{0x1c3b, 0x1c3f, 1},
{0x1c7e, 0x1c7f, 1},
+ {0x1cc0, 0x1cc7, 1},
{0x1cd3, 0x2010, 829},
{0x2011, 0x2027, 1},
{0x2030, 0x2043, 1},
@@ -1954,7 +2097,7 @@ var _P = &RangeTable{
{0x2cfe, 0x2cff, 1},
{0x2d70, 0x2e00, 144},
{0x2e01, 0x2e2e, 1},
- {0x2e30, 0x2e31, 1},
+ {0x2e30, 0x2e3b, 1},
{0x3001, 0x3003, 1},
{0x3008, 0x3011, 1},
{0x3014, 0x301f, 1},
@@ -1973,6 +2116,7 @@ var _P = &RangeTable{
{0xa9de, 0xa9df, 1},
{0xaa5c, 0xaa5f, 1},
{0xaade, 0xaadf, 1},
+ {0xaaf0, 0xaaf1, 1},
{0xabeb, 0xfd3e, 20819},
{0xfd3f, 0xfe10, 209},
{0xfe11, 0xfe19, 1},
@@ -1991,7 +2135,7 @@ var _P = &RangeTable{
{0xff60, 0xff65, 1},
},
R32: []Range32{
- {0x10100, 0x10101, 1},
+ {0x10100, 0x10102, 1},
{0x1039f, 0x103d0, 49},
{0x10857, 0x1091f, 200},
{0x1093f, 0x10a50, 273},
@@ -2001,8 +2145,11 @@ var _P = &RangeTable{
{0x11047, 0x1104d, 1},
{0x110bb, 0x110bc, 1},
{0x110be, 0x110c1, 1},
+ {0x11140, 0x11143, 1},
+ {0x111c5, 0x111c8, 1},
{0x12470, 0x12473, 1},
},
+ LatinOffset: 11,
}
var _Pc = &RangeTable{
@@ -2022,6 +2169,7 @@ var _Pd = &RangeTable{
{0x1806, 0x2010, 2058},
{0x2011, 0x2015, 1},
{0x2e17, 0x2e1a, 3},
+ {0x2e3a, 0x2e3b, 1},
{0x301c, 0x3030, 20},
{0x30a0, 0xfe31, 52625},
{0xfe32, 0xfe58, 38},
@@ -2053,6 +2201,7 @@ var _Pe = &RangeTable{
{0xff09, 0xff3d, 52},
{0xff5d, 0xff63, 3},
},
+ LatinOffset: 1,
}
var _Pf = &RangeTable{
@@ -2084,7 +2233,8 @@ var _Po = &RangeTable{
{0x002f, 0x003a, 11},
{0x003b, 0x003f, 4},
{0x0040, 0x005c, 28},
- {0x00a1, 0x00b7, 22},
+ {0x00a1, 0x00a7, 6},
+ {0x00b6, 0x00b7, 1},
{0x00bf, 0x037e, 703},
{0x0387, 0x055a, 467},
{0x055b, 0x055f, 1},
@@ -2102,15 +2252,16 @@ var _Po = &RangeTable{
{0x0830, 0x083e, 1},
{0x085e, 0x0964, 262},
{0x0965, 0x0970, 11},
- {0x0df4, 0x0e4f, 91},
- {0x0e5a, 0x0e5b, 1},
- {0x0f04, 0x0f12, 1},
- {0x0f85, 0x0fd0, 75},
- {0x0fd1, 0x0fd4, 1},
+ {0x0af0, 0x0df4, 772},
+ {0x0e4f, 0x0e5a, 11},
+ {0x0e5b, 0x0f04, 169},
+ {0x0f05, 0x0f12, 1},
+ {0x0f14, 0x0f85, 113},
+ {0x0fd0, 0x0fd4, 1},
{0x0fd9, 0x0fda, 1},
{0x104a, 0x104f, 1},
- {0x10fb, 0x1361, 614},
- {0x1362, 0x1368, 1},
+ {0x10fb, 0x1360, 613},
+ {0x1361, 0x1368, 1},
{0x166d, 0x166e, 1},
{0x16eb, 0x16ed, 1},
{0x1735, 0x1736, 1},
@@ -2126,6 +2277,7 @@ var _Po = &RangeTable{
{0x1bfc, 0x1bff, 1},
{0x1c3b, 0x1c3f, 1},
{0x1c7e, 0x1c7f, 1},
+ {0x1cc0, 0x1cc7, 1},
{0x1cd3, 0x2016, 835},
{0x2017, 0x2020, 9},
{0x2021, 0x2027, 1},
@@ -2146,7 +2298,7 @@ var _Po = &RangeTable{
{0x2e1b, 0x2e1e, 3},
{0x2e1f, 0x2e2a, 11},
{0x2e2b, 0x2e2e, 1},
- {0x2e30, 0x2e31, 1},
+ {0x2e30, 0x2e39, 1},
{0x3001, 0x3003, 1},
{0x303d, 0x30fb, 190},
{0xa4fe, 0xa4ff, 1},
@@ -2162,6 +2314,7 @@ var _Po = &RangeTable{
{0xa9de, 0xa9df, 1},
{0xaa5c, 0xaa5f, 1},
{0xaade, 0xaadf, 1},
+ {0xaaf0, 0xaaf1, 1},
{0xabeb, 0xfe10, 21029},
{0xfe11, 0xfe16, 1},
{0xfe19, 0xfe30, 23},
@@ -2183,17 +2336,21 @@ var _Po = &RangeTable{
},
R32: []Range32{
{0x10100, 0x10100, 1},
- {0x10101, 0x1039f, 670},
- {0x103d0, 0x10857, 1159},
- {0x1091f, 0x1093f, 32},
- {0x10a50, 0x10a58, 1},
+ {0x10101, 0x10102, 1},
+ {0x1039f, 0x103d0, 49},
+ {0x10857, 0x1091f, 200},
+ {0x1093f, 0x10a50, 273},
+ {0x10a51, 0x10a58, 1},
{0x10a7f, 0x10b39, 186},
{0x10b3a, 0x10b3f, 1},
{0x11047, 0x1104d, 1},
{0x110bb, 0x110bc, 1},
{0x110be, 0x110c1, 1},
+ {0x11140, 0x11143, 1},
+ {0x111c5, 0x111c8, 1},
{0x12470, 0x12473, 1},
},
+ LatinOffset: 8,
}
var _Ps = &RangeTable{
@@ -2222,6 +2379,7 @@ var _Ps = &RangeTable{
{0xff5b, 0xff5f, 4},
{0xff62, 0xff62, 1},
},
+ LatinOffset: 1,
}
var _S = &RangeTable{
@@ -2230,10 +2388,11 @@ var _S = &RangeTable{
{0x003c, 0x003e, 1},
{0x005e, 0x0060, 2},
{0x007c, 0x007e, 2},
- {0x00a2, 0x00a9, 1},
+ {0x00a2, 0x00a6, 1},
+ {0x00a8, 0x00a9, 1},
{0x00ac, 0x00ae, 2},
{0x00af, 0x00b1, 1},
- {0x00b4, 0x00b8, 2},
+ {0x00b4, 0x00b8, 4},
{0x00d7, 0x00f7, 32},
{0x02c2, 0x02c5, 1},
{0x02d2, 0x02df, 1},
@@ -2242,8 +2401,8 @@ var _S = &RangeTable{
{0x02f0, 0x02ff, 1},
{0x0375, 0x0384, 15},
{0x0385, 0x03f6, 113},
- {0x0482, 0x0606, 388},
- {0x0607, 0x0608, 1},
+ {0x0482, 0x058f, 269},
+ {0x0606, 0x0608, 1},
{0x060b, 0x060e, 3},
{0x060f, 0x06de, 207},
{0x06e9, 0x06fd, 20},
@@ -2255,7 +2414,8 @@ var _S = &RangeTable{
{0x0c7f, 0x0d79, 250},
{0x0e3f, 0x0f01, 194},
{0x0f02, 0x0f03, 1},
- {0x0f13, 0x0f17, 1},
+ {0x0f13, 0x0f15, 2},
+ {0x0f16, 0x0f17, 1},
{0x0f1a, 0x0f1f, 1},
{0x0f34, 0x0f38, 2},
{0x0fbe, 0x0fc5, 1},
@@ -2263,8 +2423,7 @@ var _S = &RangeTable{
{0x0fce, 0x0fcf, 1},
{0x0fd5, 0x0fd8, 1},
{0x109e, 0x109f, 1},
- {0x1360, 0x1390, 48},
- {0x1391, 0x1399, 1},
+ {0x1390, 0x1399, 1},
{0x17db, 0x1940, 357},
{0x19de, 0x19ff, 1},
{0x1b61, 0x1b6a, 1},
@@ -2278,7 +2437,7 @@ var _S = &RangeTable{
{0x2044, 0x2052, 14},
{0x207a, 0x207c, 1},
{0x208a, 0x208c, 1},
- {0x20a0, 0x20b9, 1},
+ {0x20a0, 0x20ba, 1},
{0x2100, 0x2101, 1},
{0x2103, 0x2106, 1},
{0x2108, 0x2109, 1},
@@ -2299,9 +2458,7 @@ var _S = &RangeTable{
{0x2500, 0x26ff, 1},
{0x2701, 0x2767, 1},
{0x2794, 0x27c4, 1},
- {0x27c7, 0x27ca, 1},
- {0x27cc, 0x27ce, 2},
- {0x27cf, 0x27e5, 1},
+ {0x27c7, 0x27e5, 1},
{0x27f0, 0x2982, 1},
{0x2999, 0x29d7, 1},
{0x29dc, 0x29fb, 1},
@@ -2321,8 +2478,9 @@ var _S = &RangeTable{
{0x3196, 0x319f, 1},
{0x31c0, 0x31e3, 1},
{0x3200, 0x321e, 1},
- {0x322a, 0x3250, 1},
- {0x3260, 0x327f, 1},
+ {0x322a, 0x3247, 1},
+ {0x3250, 0x3260, 16},
+ {0x3261, 0x327f, 1},
{0x328a, 0x32b0, 1},
{0x32c0, 0x32fe, 1},
{0x3300, 0x33ff, 1},
@@ -2349,8 +2507,7 @@ var _S = &RangeTable{
{0xfffc, 0xfffd, 1},
},
R32: []Range32{
- {0x10102, 0x10137, 53},
- {0x10138, 0x1013f, 1},
+ {0x10137, 0x1013f, 1},
{0x10179, 0x10189, 1},
{0x10190, 0x1019b, 1},
{0x101d0, 0x101fc, 1},
@@ -2369,6 +2526,7 @@ var _S = &RangeTable{
{0x1d735, 0x1d74f, 26},
{0x1d76f, 0x1d789, 26},
{0x1d7a9, 0x1d7c3, 26},
+ {0x1eef0, 0x1eef1, 1},
{0x1f000, 0x1f02b, 1},
{0x1f030, 0x1f093, 1},
{0x1f0a0, 0x1f0ae, 1},
@@ -2376,7 +2534,7 @@ var _S = &RangeTable{
{0x1f0c1, 0x1f0cf, 1},
{0x1f0d1, 0x1f0df, 1},
{0x1f110, 0x1f12e, 1},
- {0x1f130, 0x1f169, 1},
+ {0x1f130, 0x1f16b, 1},
{0x1f170, 0x1f19a, 1},
{0x1f1e6, 0x1f202, 1},
{0x1f210, 0x1f23a, 1},
@@ -2394,37 +2552,32 @@ var _S = &RangeTable{
{0x1f443, 0x1f4f7, 1},
{0x1f4f9, 0x1f4fc, 1},
{0x1f500, 0x1f53d, 1},
+ {0x1f540, 0x1f543, 1},
{0x1f550, 0x1f567, 1},
- {0x1f5fb, 0x1f5ff, 1},
- {0x1f601, 0x1f610, 1},
- {0x1f612, 0x1f614, 1},
- {0x1f616, 0x1f61c, 2},
- {0x1f61d, 0x1f61e, 1},
- {0x1f620, 0x1f625, 1},
- {0x1f628, 0x1f62b, 1},
- {0x1f62d, 0x1f630, 3},
- {0x1f631, 0x1f633, 1},
- {0x1f635, 0x1f640, 1},
+ {0x1f5fb, 0x1f640, 1},
{0x1f645, 0x1f64f, 1},
{0x1f680, 0x1f6c5, 1},
{0x1f700, 0x1f773, 1},
},
+ LatinOffset: 10,
}
var _Sc = &RangeTable{
R16: []Range16{
{0x0024, 0x00a2, 126},
{0x00a3, 0x00a5, 1},
- {0x060b, 0x09f2, 999},
- {0x09f3, 0x09fb, 8},
- {0x0af1, 0x0bf9, 264},
- {0x0e3f, 0x17db, 2460},
- {0x20a0, 0x20b9, 1},
+ {0x058f, 0x060b, 124},
+ {0x09f2, 0x09f3, 1},
+ {0x09fb, 0x0af1, 246},
+ {0x0bf9, 0x0e3f, 582},
+ {0x17db, 0x20a0, 2245},
+ {0x20a1, 0x20ba, 1},
{0xa838, 0xfdfc, 21956},
{0xfe69, 0xff04, 155},
{0xffe0, 0xffe1, 1},
{0xffe5, 0xffe6, 1},
},
+ LatinOffset: 2,
}
var _Sk = &RangeTable{
@@ -2452,6 +2605,7 @@ var _Sk = &RangeTable{
{0xff3e, 0xff40, 2},
{0xffe3, 0xffe3, 1},
},
+ LatinOffset: 3,
}
var _Sm = &RangeTable{
@@ -2485,9 +2639,7 @@ var _Sm = &RangeTable{
{0x25f8, 0x25ff, 1},
{0x266f, 0x27c0, 337},
{0x27c1, 0x27c4, 1},
- {0x27c7, 0x27ca, 1},
- {0x27cc, 0x27ce, 2},
- {0x27cf, 0x27e5, 1},
+ {0x27c7, 0x27e5, 1},
{0x27f0, 0x27ff, 1},
{0x2900, 0x2982, 1},
{0x2999, 0x29d7, 1},
@@ -2509,14 +2661,15 @@ var _Sm = &RangeTable{
{0x1d735, 0x1d74f, 26},
{0x1d76f, 0x1d789, 26},
{0x1d7a9, 0x1d7c3, 26},
+ {0x1eef0, 0x1eef1, 1},
},
+ LatinOffset: 5,
}
var _So = &RangeTable{
R16: []Range16{
- {0x00a6, 0x00a7, 1},
- {0x00a9, 0x00ae, 5},
- {0x00b0, 0x00b6, 6},
+ {0x00a6, 0x00a9, 3},
+ {0x00ae, 0x00b0, 2},
{0x0482, 0x060e, 396},
{0x060f, 0x06de, 207},
{0x06e9, 0x06fd, 20},
@@ -2526,7 +2679,8 @@ var _So = &RangeTable{
{0x0bfa, 0x0c7f, 133},
{0x0d79, 0x0f01, 392},
{0x0f02, 0x0f03, 1},
- {0x0f13, 0x0f17, 1},
+ {0x0f13, 0x0f15, 2},
+ {0x0f16, 0x0f17, 1},
{0x0f1a, 0x0f1f, 1},
{0x0f34, 0x0f38, 2},
{0x0fbe, 0x0fc5, 1},
@@ -2534,8 +2688,7 @@ var _So = &RangeTable{
{0x0fce, 0x0fcf, 1},
{0x0fd5, 0x0fd8, 1},
{0x109e, 0x109f, 1},
- {0x1360, 0x1390, 48},
- {0x1391, 0x1399, 1},
+ {0x1390, 0x1399, 1},
{0x1940, 0x19de, 158},
{0x19df, 0x19ff, 1},
{0x1b61, 0x1b6a, 1},
@@ -2594,8 +2747,9 @@ var _So = &RangeTable{
{0x3196, 0x319f, 1},
{0x31c0, 0x31e3, 1},
{0x3200, 0x321e, 1},
- {0x322a, 0x3250, 1},
- {0x3260, 0x327f, 1},
+ {0x322a, 0x3247, 1},
+ {0x3250, 0x3260, 16},
+ {0x3261, 0x327f, 1},
{0x328a, 0x32b0, 1},
{0x32c0, 0x32fe, 1},
{0x3300, 0x33ff, 1},
@@ -2611,8 +2765,8 @@ var _So = &RangeTable{
{0xfffd, 0xfffd, 1},
},
R32: []Range32{
- {0x10102, 0x10102, 1},
- {0x10137, 0x1013f, 1},
+ {0x10137, 0x10137, 1},
+ {0x10138, 0x1013f, 1},
{0x10179, 0x10189, 1},
{0x10190, 0x1019b, 1},
{0x101d0, 0x101fc, 1},
@@ -2633,7 +2787,7 @@ var _So = &RangeTable{
{0x1f0c1, 0x1f0cf, 1},
{0x1f0d1, 0x1f0df, 1},
{0x1f110, 0x1f12e, 1},
- {0x1f130, 0x1f169, 1},
+ {0x1f130, 0x1f16b, 1},
{0x1f170, 0x1f19a, 1},
{0x1f1e6, 0x1f202, 1},
{0x1f210, 0x1f23a, 1},
@@ -2651,21 +2805,14 @@ var _So = &RangeTable{
{0x1f443, 0x1f4f7, 1},
{0x1f4f9, 0x1f4fc, 1},
{0x1f500, 0x1f53d, 1},
+ {0x1f540, 0x1f543, 1},
{0x1f550, 0x1f567, 1},
- {0x1f5fb, 0x1f5ff, 1},
- {0x1f601, 0x1f610, 1},
- {0x1f612, 0x1f614, 1},
- {0x1f616, 0x1f61c, 2},
- {0x1f61d, 0x1f61e, 1},
- {0x1f620, 0x1f625, 1},
- {0x1f628, 0x1f62b, 1},
- {0x1f62d, 0x1f630, 3},
- {0x1f631, 0x1f633, 1},
- {0x1f635, 0x1f640, 1},
+ {0x1f5fb, 0x1f640, 1},
{0x1f645, 0x1f64f, 1},
{0x1f680, 0x1f6c5, 1},
{0x1f700, 0x1f773, 1},
},
+ LatinOffset: 2,
}
var _Z = &RangeTable{
@@ -2677,6 +2824,7 @@ var _Z = &RangeTable{
{0x202f, 0x205f, 48},
{0x3000, 0x3000, 1},
},
+ LatinOffset: 1,
}
var _Zl = &RangeTable{
@@ -2699,6 +2847,7 @@ var _Zs = &RangeTable{
{0x202f, 0x205f, 48},
{0x3000, 0x3000, 1},
},
+ LatinOffset: 1,
}
// These variables have type *RangeTable.
@@ -2753,7 +2902,7 @@ var (
)
// Generated by running
-// maketables --scripts=all --url=http://www.unicode.org/Public/6.0.0/ucd/
+// maketables --scripts=all --url=http://www.unicode.org/Public/6.2.0/ucd/
// DO NOT EDIT
// Scripts is the set of Unicode script tables.
@@ -2772,6 +2921,7 @@ var Scripts = map[string]*RangeTable{
"Buhid": Buhid,
"Canadian_Aboriginal": Canadian_Aboriginal,
"Carian": Carian,
+ "Chakma": Chakma,
"Cham": Cham,
"Cherokee": Cherokee,
"Common": Common,
@@ -2816,6 +2966,9 @@ var Scripts = map[string]*RangeTable{
"Malayalam": Malayalam,
"Mandaic": Mandaic,
"Meetei_Mayek": Meetei_Mayek,
+ "Meroitic_Cursive": Meroitic_Cursive,
+ "Meroitic_Hieroglyphs": Meroitic_Hieroglyphs,
+ "Miao": Miao,
"Mongolian": Mongolian,
"Myanmar": Myanmar,
"New_Tai_Lue": New_Tai_Lue,
@@ -2834,8 +2987,10 @@ var Scripts = map[string]*RangeTable{
"Runic": Runic,
"Samaritan": Samaritan,
"Saurashtra": Saurashtra,
+ "Sharada": Sharada,
"Shavian": Shavian,
"Sinhala": Sinhala,
+ "Sora_Sompeng": Sora_Sompeng,
"Sundanese": Sundanese,
"Syloti_Nagri": Syloti_Nagri,
"Syriac": Syriac,
@@ -2844,6 +2999,7 @@ var Scripts = map[string]*RangeTable{
"Tai_Le": Tai_Le,
"Tai_Tham": Tai_Tham,
"Tai_Viet": Tai_Viet,
+ "Takri": Takri,
"Tamil": Tamil,
"Telugu": Telugu,
"Thaana": Thaana,
@@ -2857,17 +3013,20 @@ var Scripts = map[string]*RangeTable{
var _Arabic = &RangeTable{
R16: []Range16{
- {0x0600, 0x0603, 1},
+ {0x0600, 0x0604, 1},
{0x0606, 0x060b, 1},
{0x060d, 0x061a, 1},
{0x061e, 0x061e, 1},
{0x0620, 0x063f, 1},
{0x0641, 0x064a, 1},
- {0x0656, 0x065e, 1},
+ {0x0656, 0x065f, 1},
{0x066a, 0x066f, 1},
{0x0671, 0x06dc, 1},
{0x06de, 0x06ff, 1},
{0x0750, 0x077f, 1},
+ {0x08a0, 0x08a0, 1},
+ {0x08a2, 0x08ac, 1},
+ {0x08e4, 0x08fe, 1},
{0xfb50, 0xfbc1, 1},
{0xfbd3, 0xfd3d, 1},
{0xfd50, 0xfd8f, 1},
@@ -2878,6 +3037,40 @@ var _Arabic = &RangeTable{
},
R32: []Range32{
{0x10e60, 0x10e7e, 1},
+ {0x1ee00, 0x1ee03, 1},
+ {0x1ee05, 0x1ee1f, 1},
+ {0x1ee21, 0x1ee22, 1},
+ {0x1ee24, 0x1ee24, 1},
+ {0x1ee27, 0x1ee27, 1},
+ {0x1ee29, 0x1ee32, 1},
+ {0x1ee34, 0x1ee37, 1},
+ {0x1ee39, 0x1ee39, 1},
+ {0x1ee3b, 0x1ee3b, 1},
+ {0x1ee42, 0x1ee42, 1},
+ {0x1ee47, 0x1ee47, 1},
+ {0x1ee49, 0x1ee49, 1},
+ {0x1ee4b, 0x1ee4b, 1},
+ {0x1ee4d, 0x1ee4f, 1},
+ {0x1ee51, 0x1ee52, 1},
+ {0x1ee54, 0x1ee54, 1},
+ {0x1ee57, 0x1ee57, 1},
+ {0x1ee59, 0x1ee59, 1},
+ {0x1ee5b, 0x1ee5b, 1},
+ {0x1ee5d, 0x1ee5d, 1},
+ {0x1ee5f, 0x1ee5f, 1},
+ {0x1ee61, 0x1ee62, 1},
+ {0x1ee64, 0x1ee64, 1},
+ {0x1ee67, 0x1ee6a, 1},
+ {0x1ee6c, 0x1ee72, 1},
+ {0x1ee74, 0x1ee77, 1},
+ {0x1ee79, 0x1ee7c, 1},
+ {0x1ee7e, 0x1ee7e, 1},
+ {0x1ee80, 0x1ee89, 1},
+ {0x1ee8b, 0x1ee9b, 1},
+ {0x1eea1, 0x1eea3, 1},
+ {0x1eea5, 0x1eea9, 1},
+ {0x1eeab, 0x1eebb, 1},
+ {0x1eef0, 0x1eef1, 1},
},
}
@@ -2887,6 +3080,7 @@ var _Armenian = &RangeTable{
{0x0559, 0x055f, 1},
{0x0561, 0x0587, 1},
{0x058a, 0x058a, 1},
+ {0x058f, 0x058f, 1},
{0xfb13, 0xfb17, 1},
},
}
@@ -2990,6 +3184,14 @@ var _Carian = &RangeTable{
},
}
+var _Chakma = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x11100, 0x11134, 1},
+ {0x11136, 0x11143, 1},
+ },
+}
+
var _Cham = &RangeTable{
R16: []Range16{
{0xaa00, 0xaa36, 1},
@@ -3029,7 +3231,6 @@ var _Common = &RangeTable{
{0x0660, 0x0669, 1},
{0x06dd, 0x06dd, 1},
{0x0964, 0x0965, 1},
- {0x0970, 0x0970, 1},
{0x0e3f, 0x0e3f, 1},
{0x0fd5, 0x0fd8, 1},
{0x10fb, 0x10fb, 1},
@@ -3040,13 +3241,14 @@ var _Common = &RangeTable{
{0x1cd3, 0x1cd3, 1},
{0x1ce1, 0x1ce1, 1},
{0x1ce9, 0x1cec, 1},
- {0x1cee, 0x1cf2, 1},
+ {0x1cee, 0x1cf3, 1},
+ {0x1cf5, 0x1cf6, 1},
{0x2000, 0x200b, 1},
{0x200e, 0x2064, 1},
{0x206a, 0x2070, 1},
{0x2074, 0x207e, 1},
{0x2080, 0x208e, 1},
- {0x20a0, 0x20b9, 1},
+ {0x20a0, 0x20ba, 1},
{0x2100, 0x2125, 1},
{0x2127, 0x2129, 1},
{0x212c, 0x2131, 1},
@@ -3057,12 +3259,10 @@ var _Common = &RangeTable{
{0x2400, 0x2426, 1},
{0x2440, 0x244a, 1},
{0x2460, 0x26ff, 1},
- {0x2701, 0x27ca, 1},
- {0x27cc, 0x27cc, 1},
- {0x27ce, 0x27ff, 1},
+ {0x2701, 0x27ff, 1},
{0x2900, 0x2b4c, 1},
{0x2b50, 0x2b59, 1},
- {0x2e00, 0x2e31, 1},
+ {0x2e00, 0x2e3b, 1},
{0x2ff0, 0x2ffb, 1},
{0x3000, 0x3004, 1},
{0x3006, 0x3006, 1},
@@ -3141,7 +3341,7 @@ var _Common = &RangeTable{
{0x1f0d1, 0x1f0df, 1},
{0x1f100, 0x1f10a, 1},
{0x1f110, 0x1f12e, 1},
- {0x1f130, 0x1f169, 1},
+ {0x1f130, 0x1f16b, 1},
{0x1f170, 0x1f19a, 1},
{0x1f1e6, 0x1f1ff, 1},
{0x1f201, 0x1f202, 1},
@@ -3160,31 +3360,22 @@ var _Common = &RangeTable{
{0x1f442, 0x1f4f7, 1},
{0x1f4f9, 0x1f4fc, 1},
{0x1f500, 0x1f53d, 1},
+ {0x1f540, 0x1f543, 1},
{0x1f550, 0x1f567, 1},
- {0x1f5fb, 0x1f5ff, 1},
- {0x1f601, 0x1f610, 1},
- {0x1f612, 0x1f614, 1},
- {0x1f616, 0x1f616, 1},
- {0x1f618, 0x1f618, 1},
- {0x1f61a, 0x1f61a, 1},
- {0x1f61c, 0x1f61e, 1},
- {0x1f620, 0x1f625, 1},
- {0x1f628, 0x1f62b, 1},
- {0x1f62d, 0x1f62d, 1},
- {0x1f630, 0x1f633, 1},
- {0x1f635, 0x1f640, 1},
+ {0x1f5fb, 0x1f640, 1},
{0x1f645, 0x1f64f, 1},
{0x1f680, 0x1f6c5, 1},
{0x1f700, 0x1f773, 1},
{0xe0001, 0xe0001, 1},
{0xe0020, 0xe007f, 1},
},
+ LatinOffset: 7,
}
var _Coptic = &RangeTable{
R16: []Range16{
{0x03e2, 0x03ef, 1},
- {0x2c80, 0x2cf1, 1},
+ {0x2c80, 0x2cf3, 1},
{0x2cf9, 0x2cff, 1},
},
}
@@ -3217,8 +3408,8 @@ var _Cyrillic = &RangeTable{
{0x1d2b, 0x1d2b, 1},
{0x1d78, 0x1d78, 1},
{0x2de0, 0x2dff, 1},
- {0xa640, 0xa673, 1},
- {0xa67c, 0xa697, 1},
+ {0xa640, 0xa697, 1},
+ {0xa69f, 0xa69f, 1},
},
}
@@ -3233,8 +3424,7 @@ var _Devanagari = &RangeTable{
R16: []Range16{
{0x0900, 0x0950, 1},
{0x0953, 0x0963, 1},
- {0x0966, 0x096f, 1},
- {0x0971, 0x0977, 1},
+ {0x0966, 0x0977, 1},
{0x0979, 0x097f, 1},
{0xa8e0, 0xa8fb, 1},
},
@@ -3287,9 +3477,13 @@ var _Ethiopic = &RangeTable{
var _Georgian = &RangeTable{
R16: []Range16{
{0x10a0, 0x10c5, 1},
+ {0x10c7, 0x10c7, 1},
+ {0x10cd, 0x10cd, 1},
{0x10d0, 0x10fa, 1},
- {0x10fc, 0x10fc, 1},
+ {0x10fc, 0x10ff, 1},
{0x2d00, 0x2d25, 1},
+ {0x2d27, 0x2d27, 1},
+ {0x2d2d, 0x2d2d, 1},
},
}
@@ -3361,8 +3555,7 @@ var _Gujarati = &RangeTable{
{0x0acb, 0x0acd, 1},
{0x0ad0, 0x0ad0, 1},
{0x0ae0, 0x0ae3, 1},
- {0x0ae6, 0x0aef, 1},
- {0x0af1, 0x0af1, 1},
+ {0x0ae6, 0x0af1, 1},
},
}
@@ -3397,9 +3590,8 @@ var _Han = &RangeTable{
{0x3021, 0x3029, 1},
{0x3038, 0x303b, 1},
{0x3400, 0x4db5, 1},
- {0x4e00, 0x9fcb, 1},
- {0xf900, 0xfa2d, 1},
- {0xfa30, 0xfa6d, 1},
+ {0x4e00, 0x9fcc, 1},
+ {0xf900, 0xfa6d, 1},
{0xfa70, 0xfad9, 1},
},
R32: []Range32{
@@ -3473,13 +3665,13 @@ var _Inherited = &RangeTable{
{0x0300, 0x036f, 1},
{0x0485, 0x0486, 1},
{0x064b, 0x0655, 1},
- {0x065f, 0x065f, 1},
{0x0670, 0x0670, 1},
{0x0951, 0x0952, 1},
{0x1cd0, 0x1cd2, 1},
{0x1cd4, 0x1ce0, 1},
{0x1ce2, 0x1ce8, 1},
{0x1ced, 0x1ced, 1},
+ {0x1cf4, 0x1cf4, 1},
{0x1dc0, 0x1de6, 1},
{0x1dfc, 0x1dff, 1},
{0x200c, 0x200d, 1},
@@ -3612,7 +3804,7 @@ var _Lao = &RangeTable{
{0x0ec6, 0x0ec6, 1},
{0x0ec8, 0x0ecd, 1},
{0x0ed0, 0x0ed9, 1},
- {0x0edc, 0x0edd, 1},
+ {0x0edc, 0x0edf, 1},
},
}
@@ -3642,13 +3834,14 @@ var _Latin = &RangeTable{
{0x2c60, 0x2c7f, 1},
{0xa722, 0xa787, 1},
{0xa78b, 0xa78e, 1},
- {0xa790, 0xa791, 1},
- {0xa7a0, 0xa7a9, 1},
- {0xa7fa, 0xa7ff, 1},
+ {0xa790, 0xa793, 1},
+ {0xa7a0, 0xa7aa, 1},
+ {0xa7f8, 0xa7ff, 1},
{0xfb00, 0xfb06, 1},
{0xff21, 0xff3a, 1},
{0xff41, 0xff5a, 1},
},
+ LatinOffset: 6,
}
var _Lepcha = &RangeTable{
@@ -3728,11 +3921,36 @@ var _Mandaic = &RangeTable{
var _Meetei_Mayek = &RangeTable{
R16: []Range16{
+ {0xaae0, 0xaaf6, 1},
{0xabc0, 0xabed, 1},
{0xabf0, 0xabf9, 1},
},
}
+var _Meroitic_Cursive = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x109a0, 0x109b7, 1},
+ {0x109be, 0x109bf, 1},
+ },
+}
+
+var _Meroitic_Hieroglyphs = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x10980, 0x1099f, 1},
+ },
+}
+
+var _Miao = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x16f00, 0x16f44, 1},
+ {0x16f50, 0x16f7e, 1},
+ {0x16f8f, 0x16f9f, 1},
+ },
+}
+
var _Mongolian = &RangeTable{
R16: []Range16{
{0x1800, 0x1801, 1},
@@ -3877,6 +4095,14 @@ var _Saurashtra = &RangeTable{
},
}
+var _Sharada = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x11180, 0x111c8, 1},
+ {0x111d0, 0x111d9, 1},
+ },
+}
+
var _Shavian = &RangeTable{
R16: []Range16{},
R32: []Range32{
@@ -3900,10 +4126,18 @@ var _Sinhala = &RangeTable{
},
}
+var _Sora_Sompeng = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x110d0, 0x110e8, 1},
+ {0x110f0, 0x110f9, 1},
+ },
+}
+
var _Sundanese = &RangeTable{
R16: []Range16{
- {0x1b80, 0x1baa, 1},
- {0x1bae, 0x1bb9, 1},
+ {0x1b80, 0x1bbf, 1},
+ {0x1cc0, 0x1cc7, 1},
},
}
@@ -3960,6 +4194,14 @@ var _Tai_Viet = &RangeTable{
},
}
+var _Takri = &RangeTable{
+ R16: []Range16{},
+ R32: []Range32{
+ {0x11680, 0x116b7, 1},
+ {0x116c0, 0x116c9, 1},
+ },
+}
+
var _Tamil = &RangeTable{
R16: []Range16{
{0x0b82, 0x0b83, 1},
@@ -4027,7 +4269,7 @@ var _Tibetan = &RangeTable{
var _Tifinagh = &RangeTable{
R16: []Range16{
- {0x2d30, 0x2d65, 1},
+ {0x2d30, 0x2d67, 1},
{0x2d6f, 0x2d70, 1},
{0x2d7f, 0x2d7f, 1},
},
@@ -4070,6 +4312,7 @@ var (
Buhid = _Buhid // Buhid is the set of Unicode characters in script Buhid.
Canadian_Aboriginal = _Canadian_Aboriginal // Canadian_Aboriginal is the set of Unicode characters in script Canadian_Aboriginal.
Carian = _Carian // Carian is the set of Unicode characters in script Carian.
+ Chakma = _Chakma // Chakma is the set of Unicode characters in script Chakma.
Cham = _Cham // Cham is the set of Unicode characters in script Cham.
Cherokee = _Cherokee // Cherokee is the set of Unicode characters in script Cherokee.
Common = _Common // Common is the set of Unicode characters in script Common.
@@ -4114,6 +4357,9 @@ var (
Malayalam = _Malayalam // Malayalam is the set of Unicode characters in script Malayalam.
Mandaic = _Mandaic // Mandaic is the set of Unicode characters in script Mandaic.
Meetei_Mayek = _Meetei_Mayek // Meetei_Mayek is the set of Unicode characters in script Meetei_Mayek.
+ Meroitic_Cursive = _Meroitic_Cursive // Meroitic_Cursive is the set of Unicode characters in script Meroitic_Cursive.
+ Meroitic_Hieroglyphs = _Meroitic_Hieroglyphs // Meroitic_Hieroglyphs is the set of Unicode characters in script Meroitic_Hieroglyphs.
+ Miao = _Miao // Miao is the set of Unicode characters in script Miao.
Mongolian = _Mongolian // Mongolian is the set of Unicode characters in script Mongolian.
Myanmar = _Myanmar // Myanmar is the set of Unicode characters in script Myanmar.
New_Tai_Lue = _New_Tai_Lue // New_Tai_Lue is the set of Unicode characters in script New_Tai_Lue.
@@ -4132,8 +4378,10 @@ var (
Runic = _Runic // Runic is the set of Unicode characters in script Runic.
Samaritan = _Samaritan // Samaritan is the set of Unicode characters in script Samaritan.
Saurashtra = _Saurashtra // Saurashtra is the set of Unicode characters in script Saurashtra.
+ Sharada = _Sharada // Sharada is the set of Unicode characters in script Sharada.
Shavian = _Shavian // Shavian is the set of Unicode characters in script Shavian.
Sinhala = _Sinhala // Sinhala is the set of Unicode characters in script Sinhala.
+ Sora_Sompeng = _Sora_Sompeng // Sora_Sompeng is the set of Unicode characters in script Sora_Sompeng.
Sundanese = _Sundanese // Sundanese is the set of Unicode characters in script Sundanese.
Syloti_Nagri = _Syloti_Nagri // Syloti_Nagri is the set of Unicode characters in script Syloti_Nagri.
Syriac = _Syriac // Syriac is the set of Unicode characters in script Syriac.
@@ -4142,6 +4390,7 @@ var (
Tai_Le = _Tai_Le // Tai_Le is the set of Unicode characters in script Tai_Le.
Tai_Tham = _Tai_Tham // Tai_Tham is the set of Unicode characters in script Tai_Tham.
Tai_Viet = _Tai_Viet // Tai_Viet is the set of Unicode characters in script Tai_Viet.
+ Takri = _Takri // Takri is the set of Unicode characters in script Takri.
Tamil = _Tamil // Tamil is the set of Unicode characters in script Tamil.
Telugu = _Telugu // Telugu is the set of Unicode characters in script Telugu.
Thaana = _Thaana // Thaana is the set of Unicode characters in script Thaana.
@@ -4154,7 +4403,7 @@ var (
)
// Generated by running
-// maketables --props=all --url=http://www.unicode.org/Public/6.0.0/ucd/
+// maketables --props=all --url=http://www.unicode.org/Public/6.2.0/ucd/
// DO NOT EDIT
// Properties is the set of Unicode property tables.
@@ -4199,6 +4448,7 @@ var _ASCII_Hex_Digit = &RangeTable{
{0x0041, 0x0046, 1},
{0x0061, 0x0066, 1},
},
+ LatinOffset: 3,
}
var _Bidi_Control = &RangeTable{
@@ -4222,6 +4472,7 @@ var _Dash = &RangeTable{
{0x2212, 0x2212, 1},
{0x2e17, 0x2e17, 1},
{0x2e1a, 0x2e1a, 1},
+ {0x2e3a, 0x2e3b, 1},
{0x301c, 0x301c, 1},
{0x3030, 0x3030, 1},
{0x30a0, 0x30a0, 1},
@@ -4230,6 +4481,7 @@ var _Dash = &RangeTable{
{0xfe63, 0xfe63, 1},
{0xff0d, 0xff0d, 1},
},
+ LatinOffset: 1,
}
var _Deprecated = &RangeTable{
@@ -4278,6 +4530,7 @@ var _Diacritic = &RangeTable{
{0x07a6, 0x07b0, 1},
{0x07eb, 0x07f5, 1},
{0x0818, 0x0819, 1},
+ {0x08e4, 0x08fe, 1},
{0x093c, 0x093c, 1},
{0x094d, 0x094d, 1},
{0x0951, 0x0954, 1},
@@ -4320,11 +4573,12 @@ var _Diacritic = &RangeTable{
{0x1b34, 0x1b34, 1},
{0x1b44, 0x1b44, 1},
{0x1b6b, 0x1b73, 1},
- {0x1baa, 0x1baa, 1},
+ {0x1baa, 0x1bab, 1},
{0x1c36, 0x1c37, 1},
{0x1c78, 0x1c7d, 1},
{0x1cd0, 0x1ce8, 1},
{0x1ced, 0x1ced, 1},
+ {0x1cf4, 0x1cf4, 1},
{0x1d2c, 0x1d6a, 1},
{0x1dc4, 0x1dcf, 1},
{0x1dfd, 0x1dff, 1},
@@ -4345,6 +4599,7 @@ var _Diacritic = &RangeTable{
{0xa6f0, 0xa6f1, 1},
{0xa717, 0xa721, 1},
{0xa788, 0xa788, 1},
+ {0xa7f8, 0xa7f9, 1},
{0xa8c4, 0xa8c4, 1},
{0xa8e0, 0xa8f1, 1},
{0xa92b, 0xa92e, 1},
@@ -4353,6 +4608,7 @@ var _Diacritic = &RangeTable{
{0xa9c0, 0xa9c0, 1},
{0xaa7b, 0xaa7b, 1},
{0xaabf, 0xaac2, 1},
+ {0xaaf6, 0xaaf6, 1},
{0xabec, 0xabed, 1},
{0xfb1e, 0xfb1e, 1},
{0xfe20, 0xfe26, 1},
@@ -4364,12 +4620,17 @@ var _Diacritic = &RangeTable{
},
R32: []Range32{
{0x110b9, 0x110ba, 1},
+ {0x11133, 0x11134, 1},
+ {0x111c0, 0x111c0, 1},
+ {0x116b6, 0x116b7, 1},
+ {0x16f8f, 0x16f9f, 1},
{0x1d167, 0x1d169, 1},
{0x1d16d, 0x1d172, 1},
{0x1d17b, 0x1d182, 1},
{0x1d185, 0x1d18b, 1},
{0x1d1aa, 0x1d1ad, 1},
},
+ LatinOffset: 6,
}
var _Extender = &RangeTable{
@@ -4380,6 +4641,7 @@ var _Extender = &RangeTable{
{0x07fa, 0x07fa, 1},
{0x0e46, 0x0e46, 1},
{0x0ec6, 0x0ec6, 1},
+ {0x180a, 0x180a, 1},
{0x1843, 0x1843, 1},
{0x1aa7, 0x1aa7, 1},
{0x1c36, 0x1c36, 1},
@@ -4393,8 +4655,10 @@ var _Extender = &RangeTable{
{0xa9cf, 0xa9cf, 1},
{0xaa70, 0xaa70, 1},
{0xaadd, 0xaadd, 1},
+ {0xaaf3, 0xaaf4, 1},
{0xff70, 0xff70, 1},
},
+ LatinOffset: 1,
}
var _Hex_Digit = &RangeTable{
@@ -4406,6 +4670,7 @@ var _Hex_Digit = &RangeTable{
{0xff21, 0xff26, 1},
{0xff41, 0xff46, 1},
},
+ LatinOffset: 3,
}
var _Hyphen = &RangeTable{
@@ -4421,6 +4686,7 @@ var _Hyphen = &RangeTable{
{0xff0d, 0xff0d, 1},
{0xff65, 0xff65, 1},
},
+ LatinOffset: 2,
}
var _IDS_Binary_Operator = &RangeTable{
@@ -4442,9 +4708,8 @@ var _Ideographic = &RangeTable{
{0x3021, 0x3029, 1},
{0x3038, 0x303a, 1},
{0x3400, 0x4db5, 1},
- {0x4e00, 0x9fcb, 1},
- {0xf900, 0xfa2d, 1},
- {0xfa30, 0xfa6d, 1},
+ {0x4e00, 0x9fcc, 1},
+ {0xf900, 0xfa6d, 1},
{0xfa70, 0xfad9, 1},
},
R32: []Range32{
@@ -4519,6 +4784,8 @@ var _Other_Alphabetic = &RangeTable{
{0x081b, 0x0823, 1},
{0x0825, 0x0827, 1},
{0x0829, 0x082c, 1},
+ {0x08e4, 0x08e9, 1},
+ {0x08f0, 0x08fe, 1},
{0x0900, 0x0903, 1},
{0x093a, 0x093b, 1},
{0x093e, 0x094c, 1},
@@ -4615,11 +4882,14 @@ var _Other_Alphabetic = &RangeTable{
{0x1b35, 0x1b43, 1},
{0x1b80, 0x1b82, 1},
{0x1ba1, 0x1ba9, 1},
+ {0x1bac, 0x1bad, 1},
{0x1be7, 0x1bf1, 1},
{0x1c24, 0x1c35, 1},
- {0x1cf2, 0x1cf2, 1},
+ {0x1cf2, 0x1cf3, 1},
{0x24b6, 0x24e9, 1},
{0x2de0, 0x2dff, 1},
+ {0xa674, 0xa67b, 1},
+ {0xa69f, 0xa69f, 1},
{0xa823, 0xa827, 1},
{0xa880, 0xa881, 1},
{0xa8b4, 0xa8c3, 1},
@@ -4634,6 +4904,8 @@ var _Other_Alphabetic = &RangeTable{
{0xaab2, 0xaab4, 1},
{0xaab7, 0xaab8, 1},
{0xaabe, 0xaabe, 1},
+ {0xaaeb, 0xaaef, 1},
+ {0xaaf5, 0xaaf5, 1},
{0xabe3, 0xabea, 1},
{0xfb1e, 0xfb1e, 1},
},
@@ -4645,6 +4917,12 @@ var _Other_Alphabetic = &RangeTable{
{0x11038, 0x11045, 1},
{0x11082, 0x11082, 1},
{0x110b0, 0x110b8, 1},
+ {0x11100, 0x11102, 1},
+ {0x11127, 0x11132, 1},
+ {0x11180, 0x11182, 1},
+ {0x111b3, 0x111bf, 1},
+ {0x116ab, 0x116b5, 1},
+ {0x16f51, 0x16f7e, 1},
},
}
@@ -4652,6 +4930,7 @@ var _Other_Default_Ignorable_Code_Point = &RangeTable{
R16: []Range16{
{0x034f, 0x034f, 1},
{0x115f, 0x1160, 1},
+ {0x17b4, 0x17b5, 1},
{0x2065, 0x2069, 1},
{0x3164, 0x3164, 1},
{0xffa0, 0xffa0, 1},
@@ -4680,6 +4959,7 @@ var _Other_Grapheme_Extend = &RangeTable{
{0x0dcf, 0x0dcf, 1},
{0x0ddf, 0x0ddf, 1},
{0x200c, 0x200d, 1},
+ {0x302e, 0x302f, 1},
{0xff9e, 0xff9f, 1},
},
R32: []Range32{
@@ -4695,6 +4975,7 @@ var _Other_ID_Continue = &RangeTable{
{0x1369, 0x1371, 1},
{0x19da, 0x19da, 1},
},
+ LatinOffset: 1,
}
var _Other_ID_Start = &RangeTable{
@@ -4707,20 +4988,26 @@ var _Other_ID_Start = &RangeTable{
var _Other_Lowercase = &RangeTable{
R16: []Range16{
+ {0x00aa, 0x00aa, 1},
+ {0x00ba, 0x00ba, 1},
{0x02b0, 0x02b8, 1},
{0x02c0, 0x02c1, 1},
{0x02e0, 0x02e4, 1},
{0x0345, 0x0345, 1},
{0x037a, 0x037a, 1},
- {0x1d2c, 0x1d61, 1},
+ {0x1d2c, 0x1d6a, 1},
{0x1d78, 0x1d78, 1},
{0x1d9b, 0x1dbf, 1},
- {0x2090, 0x2094, 1},
+ {0x2071, 0x2071, 1},
+ {0x207f, 0x207f, 1},
+ {0x2090, 0x209c, 1},
{0x2170, 0x217f, 1},
{0x24d0, 0x24e9, 1},
- {0x2c7d, 0x2c7d, 1},
+ {0x2c7c, 0x2c7d, 1},
{0xa770, 0xa770, 1},
+ {0xa7f8, 0xa7f9, 1},
},
+ LatinOffset: 2,
}
var _Other_Math = &RangeTable{
@@ -4827,7 +5114,41 @@ var _Other_Math = &RangeTable{
{0x1d7aa, 0x1d7c2, 1},
{0x1d7c4, 0x1d7cb, 1},
{0x1d7ce, 0x1d7ff, 1},
- },
+ {0x1ee00, 0x1ee03, 1},
+ {0x1ee05, 0x1ee1f, 1},
+ {0x1ee21, 0x1ee22, 1},
+ {0x1ee24, 0x1ee24, 1},
+ {0x1ee27, 0x1ee27, 1},
+ {0x1ee29, 0x1ee32, 1},
+ {0x1ee34, 0x1ee37, 1},
+ {0x1ee39, 0x1ee39, 1},
+ {0x1ee3b, 0x1ee3b, 1},
+ {0x1ee42, 0x1ee42, 1},
+ {0x1ee47, 0x1ee47, 1},
+ {0x1ee49, 0x1ee49, 1},
+ {0x1ee4b, 0x1ee4b, 1},
+ {0x1ee4d, 0x1ee4f, 1},
+ {0x1ee51, 0x1ee52, 1},
+ {0x1ee54, 0x1ee54, 1},
+ {0x1ee57, 0x1ee57, 1},
+ {0x1ee59, 0x1ee59, 1},
+ {0x1ee5b, 0x1ee5b, 1},
+ {0x1ee5d, 0x1ee5d, 1},
+ {0x1ee5f, 0x1ee5f, 1},
+ {0x1ee61, 0x1ee62, 1},
+ {0x1ee64, 0x1ee64, 1},
+ {0x1ee67, 0x1ee6a, 1},
+ {0x1ee6c, 0x1ee72, 1},
+ {0x1ee74, 0x1ee77, 1},
+ {0x1ee79, 0x1ee7c, 1},
+ {0x1ee7e, 0x1ee7e, 1},
+ {0x1ee80, 0x1ee89, 1},
+ {0x1ee8b, 0x1ee9b, 1},
+ {0x1eea1, 0x1eea3, 1},
+ {0x1eea5, 0x1eea9, 1},
+ {0x1eeab, 0x1eebb, 1},
+ },
+ LatinOffset: 1,
}
var _Other_Uppercase = &RangeTable{
@@ -4868,6 +5189,7 @@ var _Pattern_Syntax = &RangeTable{
{0xfd3e, 0xfd3f, 1},
{0xfe45, 0xfe46, 1},
},
+ LatinOffset: 15,
}
var _Pattern_White_Space = &RangeTable{
@@ -4878,6 +5200,7 @@ var _Pattern_White_Space = &RangeTable{
{0x200e, 0x200f, 1},
{0x2028, 0x2029, 1},
},
+ LatinOffset: 3,
}
var _Quotation_Mark = &RangeTable{
@@ -4895,6 +5218,7 @@ var _Quotation_Mark = &RangeTable{
{0xff07, 0xff07, 1},
{0xff62, 0xff63, 1},
},
+ LatinOffset: 4,
}
var _Radical = &RangeTable{
@@ -4944,6 +5268,7 @@ var _STerm = &RangeTable{
{0xa92f, 0xa92f, 1},
{0xa9c8, 0xa9c9, 1},
{0xaa5d, 0xaa5f, 1},
+ {0xaaf0, 0xaaf1, 1},
{0xabeb, 0xabeb, 1},
{0xfe52, 0xfe52, 1},
{0xfe56, 0xfe57, 1},
@@ -4956,7 +5281,10 @@ var _STerm = &RangeTable{
{0x10a56, 0x10a57, 1},
{0x11047, 0x11048, 1},
{0x110be, 0x110c1, 1},
+ {0x11141, 0x11143, 1},
+ {0x111c5, 0x111c6, 1},
},
+ LatinOffset: 3,
}
var _Soft_Dotted = &RangeTable{
@@ -4995,6 +5323,7 @@ var _Soft_Dotted = &RangeTable{
{0x1d65e, 0x1d65f, 1},
{0x1d692, 0x1d693, 1},
},
+ LatinOffset: 1,
}
var _Terminal_Punctuation = &RangeTable{
@@ -5048,6 +5377,7 @@ var _Terminal_Punctuation = &RangeTable{
{0xa9c7, 0xa9c9, 1},
{0xaa5d, 0xaa5f, 1},
{0xaadf, 0xaadf, 1},
+ {0xaaf0, 0xaaf1, 1},
{0xabeb, 0xabeb, 1},
{0xfe50, 0xfe52, 1},
{0xfe54, 0xfe57, 1},
@@ -5067,14 +5397,17 @@ var _Terminal_Punctuation = &RangeTable{
{0x10b3a, 0x10b3f, 1},
{0x11047, 0x1104d, 1},
{0x110be, 0x110c1, 1},
+ {0x11141, 0x11143, 1},
+ {0x111c5, 0x111c6, 1},
{0x12470, 0x12473, 1},
},
+ LatinOffset: 5,
}
var _Unified_Ideograph = &RangeTable{
R16: []Range16{
{0x3400, 0x4db5, 1},
- {0x4e00, 0x9fcb, 1},
+ {0x4e00, 0x9fcc, 1},
{0xfa0e, 0xfa0f, 1},
{0xfa11, 0xfa11, 1},
{0xfa13, 0xfa14, 1},
@@ -5114,6 +5447,7 @@ var _White_Space = &RangeTable{
{0x205f, 0x205f, 1},
{0x3000, 0x3000, 1},
},
+ LatinOffset: 4,
}
// These variables have type *RangeTable.
@@ -5153,7 +5487,7 @@ var (
)
// Generated by running
-// maketables --data=http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/6.0.0/ucd/CaseFolding.txt
+// maketables --data=http://www.unicode.org/Public/6.2.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/6.2.0/ucd/CaseFolding.txt
// DO NOT EDIT
// CaseRanges is the table describing case mappings for all letters with
@@ -5254,6 +5588,7 @@ var _CaseRanges = []CaseRange{
{0x0260, 0x0260, d{-205, 0, -205}},
{0x0263, 0x0263, d{-207, 0, -207}},
{0x0265, 0x0265, d{42280, 0, 42280}},
+ {0x0266, 0x0266, d{42308, 0, 42308}},
{0x0268, 0x0268, d{-209, 0, -209}},
{0x0269, 0x0269, d{-211, 0, -211}},
{0x026B, 0x026B, d{10743, 0, 10743}},
@@ -5315,6 +5650,8 @@ var _CaseRanges = []CaseRange{
{0x0531, 0x0556, d{0, 48, 0}},
{0x0561, 0x0586, d{-48, 0, -48}},
{0x10A0, 0x10C5, d{0, 7264, 0}},
+ {0x10C7, 0x10C7, d{0, 7264, 0}},
+ {0x10CD, 0x10CD, d{0, 7264, 0}},
{0x1D79, 0x1D79, d{35332, 0, 35332}},
{0x1D7D, 0x1D7D, d{3814, 0, 3814}},
{0x1E00, 0x1E95, d{UpperLower, UpperLower, UpperLower}},
@@ -5402,7 +5739,10 @@ var _CaseRanges = []CaseRange{
{0x2C7E, 0x2C7F, d{0, -10815, 0}},
{0x2C80, 0x2CE3, d{UpperLower, UpperLower, UpperLower}},
{0x2CEB, 0x2CEE, d{UpperLower, UpperLower, UpperLower}},
+ {0x2CF2, 0x2CF3, d{UpperLower, UpperLower, UpperLower}},
{0x2D00, 0x2D25, d{-7264, 0, -7264}},
+ {0x2D27, 0x2D27, d{-7264, 0, -7264}},
+ {0x2D2D, 0x2D2D, d{-7264, 0, -7264}},
{0xA640, 0xA66D, d{UpperLower, UpperLower, UpperLower}},
{0xA680, 0xA697, d{UpperLower, UpperLower, UpperLower}},
{0xA722, 0xA72F, d{UpperLower, UpperLower, UpperLower}},
@@ -5412,8 +5752,9 @@ var _CaseRanges = []CaseRange{
{0xA77E, 0xA787, d{UpperLower, UpperLower, UpperLower}},
{0xA78B, 0xA78C, d{UpperLower, UpperLower, UpperLower}},
{0xA78D, 0xA78D, d{0, -42280, 0}},
- {0xA790, 0xA791, d{UpperLower, UpperLower, UpperLower}},
+ {0xA790, 0xA793, d{UpperLower, UpperLower, UpperLower}},
{0xA7A0, 0xA7A9, d{UpperLower, UpperLower, UpperLower}},
+ {0xA7AA, 0xA7AA, d{0, -42308, 0}},
{0xFF21, 0xFF3A, d{0, 32, 0}},
{0xFF41, 0xFF5A, d{-32, 0, -32}},
{0x10400, 0x10427, d{0, 40, 0}},
@@ -5587,10 +5928,10 @@ var properties = [MaxLatin1 + 1]uint8{
0xA4: pS | pp, // '¤'
0xA5: pS | pp, // '¥'
0xA6: pS | pp, // '¦'
- 0xA7: pS | pp, // '§'
+ 0xA7: pP | pp, // '§'
0xA8: pS | pp, // '¨'
0xA9: pS | pp, // '©'
- 0xAA: pLl | pp, // 'ª'
+ 0xAA: pLo | pp, // 'ª'
0xAB: pP | pp, // '«'
0xAC: pS | pp, // '¬'
0xAD: 0, // '\u00ad'
@@ -5602,11 +5943,11 @@ var properties = [MaxLatin1 + 1]uint8{
0xB3: pN | pp, // '³'
0xB4: pS | pp, // '´'
0xB5: pLl | pp, // 'µ'
- 0xB6: pS | pp, // '¶'
+ 0xB6: pP | pp, // '¶'
0xB7: pP | pp, // '·'
0xB8: pS | pp, // '¸'
0xB9: pN | pp, // '¹'
- 0xBA: pLl | pp, // 'º'
+ 0xBA: pLo | pp, // 'º'
0xBB: pP | pp, // '»'
0xBC: pN | pp, // '¼'
0xBD: pN | pp, // '½'
@@ -5844,6 +6185,7 @@ var foldLl = &RangeTable{
{0x04d0, 0x0526, 2},
{0x0531, 0x0556, 1},
{0x10a0, 0x10c5, 1},
+ {0x10c7, 0x10cd, 6},
{0x1e00, 0x1e94, 2},
{0x1e9e, 0x1efe, 2},
{0x1f08, 0x1f0f, 1},
@@ -5873,20 +6215,22 @@ var foldLl = &RangeTable{
{0x2c7e, 0x2c80, 1},
{0x2c82, 0x2ce2, 2},
{0x2ceb, 0x2ced, 2},
- {0xa640, 0xa66c, 2},
+ {0x2cf2, 0xa640, 31054},
+ {0xa642, 0xa66c, 2},
{0xa680, 0xa696, 2},
{0xa722, 0xa72e, 2},
{0xa732, 0xa76e, 2},
{0xa779, 0xa77d, 2},
{0xa77e, 0xa786, 2},
{0xa78b, 0xa78d, 2},
- {0xa790, 0xa7a0, 16},
- {0xa7a2, 0xa7a8, 2},
+ {0xa790, 0xa792, 2},
+ {0xa7a0, 0xa7aa, 2},
{0xff21, 0xff3a, 1},
},
R32: []Range32{
{0x10400, 0x10427, 1},
},
+ LatinOffset: 3,
}
var foldLt = &RangeTable{
@@ -5941,11 +6285,12 @@ var foldLu = &RangeTable{
{0x0256, 0x0257, 1},
{0x0259, 0x025b, 2},
{0x0260, 0x0263, 3},
- {0x0265, 0x0268, 3},
- {0x0269, 0x026b, 2},
- {0x026f, 0x0271, 2},
- {0x0272, 0x0275, 3},
- {0x027d, 0x0283, 3},
+ {0x0265, 0x0266, 1},
+ {0x0268, 0x0269, 1},
+ {0x026b, 0x026f, 4},
+ {0x0271, 0x0272, 1},
+ {0x0275, 0x027d, 8},
+ {0x0280, 0x0283, 3},
{0x0288, 0x028c, 1},
{0x0292, 0x0345, 179},
{0x0371, 0x0373, 2},
@@ -5987,7 +6332,9 @@ var foldLu = &RangeTable{
{0x2c73, 0x2c76, 3},
{0x2c81, 0x2ce3, 2},
{0x2cec, 0x2cee, 2},
- {0x2d00, 0x2d25, 1},
+ {0x2cf3, 0x2d00, 13},
+ {0x2d01, 0x2d25, 1},
+ {0x2d27, 0x2d2d, 6},
{0xa641, 0xa66d, 2},
{0xa681, 0xa697, 2},
{0xa723, 0xa72f, 2},
@@ -5995,12 +6342,14 @@ var foldLu = &RangeTable{
{0xa77a, 0xa77c, 2},
{0xa77f, 0xa787, 2},
{0xa78c, 0xa791, 5},
- {0xa7a1, 0xa7a9, 2},
+ {0xa793, 0xa7a1, 14},
+ {0xa7a3, 0xa7a9, 2},
{0xff41, 0xff5a, 1},
},
R32: []Range32{
{0x10428, 0x1044f, 1},
},
+ LatinOffset: 4,
}
var foldM = &RangeTable{
@@ -6023,7 +6372,7 @@ var foldMn = &RangeTable{
// If there is no entry for a script name, there are no such points.
var FoldScript = map[string]*RangeTable{}
-// Range entries: 3391 16-bit, 659 32-bit, 4050 total.
-// Range bytes: 20346 16-bit, 7908 32-bit, 28254 total.
+// Range entries: 3462 16-bit, 832 32-bit, 4294 total.
+// Range bytes: 20772 16-bit, 9984 32-bit, 30756 total.
// Fold orbit bytes: 63 pairs, 252 bytes
diff --git a/src/pkg/unicode/utf8/example_test.go b/src/pkg/unicode/utf8/example_test.go
new file mode 100644
index 000000000..fe2037336
--- /dev/null
+++ b/src/pkg/unicode/utf8/example_test.go
@@ -0,0 +1,192 @@
+package utf8_test
+
+import (
+ "fmt"
+ "unicode/utf8"
+)
+
+func ExampleDecodeLastRune() {
+ b := []byte("Hello, 世界")
+
+ for len(b) > 0 {
+ r, size := utf8.DecodeLastRune(b)
+ fmt.Printf("%c %v\n", r, size)
+
+ b = b[:len(b)-size]
+ }
+ // Output:
+ // 界 3
+ // 世 3
+ // 1
+ // , 1
+ // o 1
+ // l 1
+ // l 1
+ // e 1
+ // H 1
+}
+
+func ExampleDecodeLastRuneInString() {
+ str := "Hello, 世界"
+
+ for len(str) > 0 {
+ r, size := utf8.DecodeLastRuneInString(str)
+ fmt.Printf("%c %v\n", r, size)
+
+ str = str[:len(str)-size]
+ }
+ // Output:
+ // 界 3
+ // 世 3
+ // 1
+ // , 1
+ // o 1
+ // l 1
+ // l 1
+ // e 1
+ // H 1
+
+}
+
+func ExampleDecodeRune() {
+ b := []byte("Hello, 世界")
+
+ for len(b) > 0 {
+ r, size := utf8.DecodeRune(b)
+ fmt.Printf("%c %v\n", r, size)
+
+ b = b[size:]
+ }
+ // Output:
+ // H 1
+ // e 1
+ // l 1
+ // l 1
+ // o 1
+ // , 1
+ // 1
+ // 世 3
+ // 界 3
+}
+
+func ExampleDecodeRuneInString() {
+ str := "Hello, 世界"
+
+ for len(str) > 0 {
+ r, size := utf8.DecodeRuneInString(str)
+ fmt.Printf("%c %v\n", r, size)
+
+ str = str[size:]
+ }
+ // Output:
+ // H 1
+ // e 1
+ // l 1
+ // l 1
+ // o 1
+ // , 1
+ // 1
+ // 世 3
+ // 界 3
+}
+
+func ExampleEncodeRune() {
+ r := '世'
+ buf := make([]byte, 3)
+
+ n := utf8.EncodeRune(buf, r)
+
+ fmt.Println(buf)
+ fmt.Println(n)
+ // Output:
+ // [228 184 150]
+ // 3
+}
+
+func ExampleFullRune() {
+ buf := []byte{228, 184, 150} // 世
+ fmt.Println(utf8.FullRune(buf))
+ fmt.Println(utf8.FullRune(buf[:2]))
+ // Output:
+ // true
+ // false
+}
+
+func ExampleFullRuneInString() {
+ str := "世"
+ fmt.Println(utf8.FullRuneInString(str))
+ fmt.Println(utf8.FullRuneInString(str[:2]))
+ // Output:
+ // true
+ // false
+}
+
+func ExampleRuneCount() {
+ buf := []byte("Hello, 世界")
+ fmt.Println("bytes =", len(buf))
+ fmt.Println("runes =", utf8.RuneCount(buf))
+ // Output:
+ // bytes = 13
+ // runes = 9
+}
+
+func ExampleRuneCountInString() {
+ str := "Hello, 世界"
+ fmt.Println("bytes =", len(str))
+ fmt.Println("runes =", utf8.RuneCountInString(str))
+ // Output:
+ // bytes = 13
+ // runes = 9
+}
+
+func ExampleRuneLen() {
+ fmt.Println(utf8.RuneLen('a'))
+ fmt.Println(utf8.RuneLen('界'))
+ // Output:
+ // 1
+ // 3
+}
+
+func ExampleRuneStart() {
+ buf := []byte("a界")
+ fmt.Println(utf8.RuneStart(buf[0]))
+ fmt.Println(utf8.RuneStart(buf[1]))
+ fmt.Println(utf8.RuneStart(buf[2]))
+ // Output:
+ // true
+ // true
+ // false
+}
+
+func ExampleValid() {
+ valid := []byte("Hello, 世界")
+ invalid := []byte{0xff, 0xfe, 0xfd}
+
+ fmt.Println(utf8.Valid(valid))
+ fmt.Println(utf8.Valid(invalid))
+ // Output:
+ // true
+ // false
+}
+
+func ExampleValidRune() {
+ valid := 'a'
+ invalid := rune(0xfffffff)
+
+ fmt.Println(utf8.ValidRune(valid))
+ fmt.Println(utf8.ValidRune(invalid))
+ // Output:
+ // true
+ // false
+}
+
+func ExampleValidString() {
+ valid := "Hello, 世界"
+ invalid := string([]byte{0xff, 0xfe, 0xfd})
+
+ fmt.Println(utf8.ValidString(valid))
+ fmt.Println(utf8.ValidString(invalid))
+ // Output:
+ // true
+ // false
+}
diff --git a/src/pkg/unicode/utf8/utf8.go b/src/pkg/unicode/utf8/utf8.go
index 57ea19e96..93d0be5e0 100644
--- a/src/pkg/unicode/utf8/utf8.go
+++ b/src/pkg/unicode/utf8/utf8.go
@@ -18,6 +18,12 @@ const (
UTFMax = 4 // maximum number of bytes of a UTF-8 encoded Unicode character.
)
+// Code points in the surrogate range are not valid for UTF-8.
+const (
+ surrogateMin = 0xD800
+ surrogateMax = 0xDFFF
+)
+
const (
t1 = 0x00 // 0000 0000
tx = 0x80 // 1000 0000
@@ -34,7 +40,6 @@ const (
rune1Max = 1<<7 - 1
rune2Max = 1<<11 - 1
rune3Max = 1<<16 - 1
- rune4Max = 1<<21 - 1
)
func decodeRuneInternal(p []byte) (r rune, size int, short bool) {
@@ -87,6 +92,9 @@ func decodeRuneInternal(p []byte) (r rune, size int, short bool) {
if r <= rune2Max {
return RuneError, 1, false
}
+ if surrogateMin <= r && r <= surrogateMax {
+ return RuneError, 1, false
+ }
return r, 3, false
}
@@ -102,7 +110,7 @@ func decodeRuneInternal(p []byte) (r rune, size int, short bool) {
// 4-byte, 21-bit sequence?
if c0 < t5 {
r = rune(c0&mask4)<<18 | rune(c1&maskx)<<12 | rune(c2&maskx)<<6 | rune(c3&maskx)
- if r <= rune3Max {
+ if r <= rune3Max || MaxRune < r {
return RuneError, 1, false
}
return r, 4, false
@@ -162,6 +170,9 @@ func decodeRuneInStringInternal(s string) (r rune, size int, short bool) {
if r <= rune2Max {
return RuneError, 1, false
}
+ if surrogateMin <= r && r <= surrogateMax {
+ return RuneError, 1, false
+ }
return r, 3, false
}
@@ -177,7 +188,7 @@ func decodeRuneInStringInternal(s string) (r rune, size int, short bool) {
// 4-byte, 21-bit sequence?
if c0 < t5 {
r = rune(c0&mask4)<<18 | rune(c1&maskx)<<12 | rune(c2&maskx)<<6 | rune(c3&maskx)
- if r <= rune3Max {
+ if r <= rune3Max || MaxRune < r {
return RuneError, 1, false
}
return r, 4, false
@@ -202,6 +213,9 @@ func FullRuneInString(s string) bool {
// DecodeRune unpacks the first UTF-8 encoding in p and returns the rune and its width in bytes.
// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
+// An encoding is invalid if it is incorrect UTF-8, encodes a rune that is
+// out of range, or is not the shortest possible UTF-8 encoding for the
+// value. No other validation is performed.
func DecodeRune(p []byte) (r rune, size int) {
r, size, _ = decodeRuneInternal(p)
return
@@ -209,6 +223,9 @@ func DecodeRune(p []byte) (r rune, size int) {
// DecodeRuneInString is like DecodeRune but its input is a string.
// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
+// An encoding is invalid if it is incorrect UTF-8, encodes a rune that is
+// out of range, or is not the shortest possible UTF-8 encoding for the
+// value. No other validation is performed.
func DecodeRuneInString(s string) (r rune, size int) {
r, size, _ = decodeRuneInStringInternal(s)
return
@@ -216,6 +233,9 @@ func DecodeRuneInString(s string) (r rune, size int) {
// DecodeLastRune unpacks the last UTF-8 encoding in p and returns the rune and its width in bytes.
// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
+// An encoding is invalid if it is incorrect UTF-8, encodes a rune that is
+// out of range, or is not the shortest possible UTF-8 encoding for the
+// value. No other validation is performed.
func DecodeLastRune(p []byte) (r rune, size int) {
end := len(p)
if end == 0 {
@@ -250,6 +270,9 @@ func DecodeLastRune(p []byte) (r rune, size int) {
// DecodeLastRuneInString is like DecodeLastRune but its input is a string.
// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
+// An encoding is invalid if it is incorrect UTF-8, encodes a rune that is
+// out of range, or is not the shortest possible UTF-8 encoding for the
+// value. No other validation is performed.
func DecodeLastRuneInString(s string) (r rune, size int) {
end := len(s)
if end == 0 {
@@ -283,15 +306,20 @@ func DecodeLastRuneInString(s string) (r rune, size int) {
}
// RuneLen returns the number of bytes required to encode the rune.
+// It returns -1 if the rune is not a valid value to encode in UTF-8.
func RuneLen(r rune) int {
switch {
+ case r < 0:
+ return -1
case r <= rune1Max:
return 1
case r <= rune2Max:
return 2
+ case surrogateMin <= r && r <= surrogateMax:
+ return -1
case r <= rune3Max:
return 3
- case r <= rune4Max:
+ case r <= MaxRune:
return 4
}
return -1
@@ -316,6 +344,10 @@ func EncodeRune(p []byte, r rune) int {
r = RuneError
}
+ if surrogateMin <= r && r <= surrogateMax {
+ r = RuneError
+ }
+
if uint32(r) <= rune3Max {
p[0] = t3 | byte(r>>12)
p[1] = tx | byte(r>>6)&maskx
@@ -368,7 +400,7 @@ func Valid(p []byte) bool {
} else {
_, size := DecodeRune(p[i:])
if size == 1 {
- // All valid runes of size of 1 (those
+ // All valid runes of size 1 (those
// below RuneSelf) were handled above.
// This must be a RuneError.
return false
@@ -395,3 +427,17 @@ func ValidString(s string) bool {
}
return true
}
+
+// ValidRune reports whether r can be legally encoded as UTF-8.
+// Code points that are out of range or a surrogate half are illegal.
+func ValidRune(r rune) bool {
+ switch {
+ case r < 0:
+ return false
+ case surrogateMin <= r && r <= surrogateMax:
+ return false
+ case r > MaxRune:
+ return false
+ }
+ return true
+}
diff --git a/src/pkg/unicode/utf8/utf8_test.go b/src/pkg/unicode/utf8/utf8_test.go
index 4f73c8fb8..c516871c9 100644
--- a/src/pkg/unicode/utf8/utf8_test.go
+++ b/src/pkg/unicode/utf8/utf8_test.go
@@ -56,6 +56,8 @@ var utf8map = []Utf8Map{
{0x07ff, "\xdf\xbf"},
{0x0800, "\xe0\xa0\x80"},
{0x0801, "\xe0\xa0\x81"},
+ {0xd7ff, "\xed\x9f\xbf"}, // last code point before surrogate half.
+ {0xe000, "\xee\x80\x80"}, // first code point after surrogate half.
{0xfffe, "\xef\xbf\xbe"},
{0xffff, "\xef\xbf\xbf"},
{0x10000, "\xf0\x90\x80\x80"},
@@ -65,6 +67,11 @@ var utf8map = []Utf8Map{
{0xFFFD, "\xef\xbf\xbd"},
}
+var surrogateMap = []Utf8Map{
+ {0xd800, "\xed\xa0\x80"}, // surrogate min decodes to (RuneError, 1)
+ {0xdfff, "\xed\xbf\xbf"}, // surrogate max decodes to (RuneError, 1)
+}
+
var testStrings = []string{
"",
"abcd",
@@ -75,8 +82,7 @@ var testStrings = []string{
}
func TestFullRune(t *testing.T) {
- for i := 0; i < len(utf8map); i++ {
- m := utf8map[i]
+ for _, m := range utf8map {
b := []byte(m.str)
if !FullRune(b) {
t.Errorf("FullRune(%q) (%U) = false, want true", b, m.r)
@@ -97,8 +103,7 @@ func TestFullRune(t *testing.T) {
}
func TestEncodeRune(t *testing.T) {
- for i := 0; i < len(utf8map); i++ {
- m := utf8map[i]
+ for _, m := range utf8map {
b := []byte(m.str)
var buf [10]byte
n := EncodeRune(buf[0:], m.r)
@@ -110,8 +115,7 @@ func TestEncodeRune(t *testing.T) {
}
func TestDecodeRune(t *testing.T) {
- for i := 0; i < len(utf8map); i++ {
- m := utf8map[i]
+ for _, m := range utf8map {
b := []byte(m.str)
r, size := DecodeRune(b)
if r != m.r || size != len(b) {
@@ -168,6 +172,21 @@ func TestDecodeRune(t *testing.T) {
}
}
+func TestDecodeSurrogateRune(t *testing.T) {
+ for _, m := range surrogateMap {
+ b := []byte(m.str)
+ r, size := DecodeRune(b)
+ if r != RuneError || size != 1 {
+ t.Errorf("DecodeRune(%q) = %x, %d want %x, %d", b, r, size, RuneError, 1)
+ }
+ s := m.str
+ r, size = DecodeRuneInString(s)
+ if r != RuneError || size != 1 {
+ t.Errorf("DecodeRune(%q) = %x, %d want %x, %d", b, r, size, RuneError, 1)
+ }
+ }
+}
+
// Check that DecodeRune and DecodeLastRune correspond to
// the equivalent range loop.
func TestSequencing(t *testing.T) {
@@ -284,8 +303,7 @@ var runecounttests = []RuneCountTest{
}
func TestRuneCount(t *testing.T) {
- for i := 0; i < len(runecounttests); i++ {
- tt := runecounttests[i]
+ for _, tt := range runecounttests {
if out := RuneCountInString(tt.in); out != tt.out {
t.Errorf("RuneCountInString(%q) = %d, want %d", tt.in, out, tt.out)
}
@@ -295,6 +313,32 @@ func TestRuneCount(t *testing.T) {
}
}
+type RuneLenTest struct {
+ r rune
+ size int
+}
+
+var runelentests = []RuneLenTest{
+ {0, 1},
+ {'e', 1},
+ {'é', 2},
+ {'☺', 3},
+ {RuneError, 3},
+ {MaxRune, 4},
+ {0xD800, -1},
+ {0xDFFF, -1},
+ {MaxRune + 1, -1},
+ {-1, -1},
+}
+
+func TestRuneLen(t *testing.T) {
+ for _, tt := range runelentests {
+ if size := RuneLen(tt.r); size != tt.size {
+ t.Errorf("RuneLen(%#U) = %d, want %d", tt.r, size, tt.size)
+ }
+ }
+}
+
type ValidTest struct {
in string
out bool
@@ -311,15 +355,50 @@ var validTests = []ValidTest{
{string([]byte{66, 250}), false},
{string([]byte{66, 250, 67}), false},
{"a\uFFFDb", true},
+ {string("\xF4\x8F\xBF\xBF"), true}, // U+10FFFF
+ {string("\xF4\x90\x80\x80"), false}, // U+10FFFF+1; out of range
+ {string("\xF7\xBF\xBF\xBF"), false}, // 0x1FFFFF; out of range
+ {string("\xFB\xBF\xBF\xBF\xBF"), false}, // 0x3FFFFFF; out of range
+ {string("\xc0\x80"), false}, // U+0000 encoded in two bytes: incorrect
+ {string("\xed\xa0\x80"), false}, // U+D800 high surrogate (sic)
+ {string("\xed\xbf\xbf"), false}, // U+DFFF low surrogate (sic)
}
func TestValid(t *testing.T) {
- for i, tt := range validTests {
+ for _, tt := range validTests {
if Valid([]byte(tt.in)) != tt.out {
- t.Errorf("%d. Valid(%q) = %v; want %v", i, tt.in, !tt.out, tt.out)
+ t.Errorf("Valid(%q) = %v; want %v", tt.in, !tt.out, tt.out)
}
if ValidString(tt.in) != tt.out {
- t.Errorf("%d. ValidString(%q) = %v; want %v", i, tt.in, !tt.out, tt.out)
+ t.Errorf("ValidString(%q) = %v; want %v", tt.in, !tt.out, tt.out)
+ }
+ }
+}
+
+type ValidRuneTest struct {
+ r rune
+ ok bool
+}
+
+var validrunetests = []ValidRuneTest{
+ {0, true},
+ {'e', true},
+ {'é', true},
+ {'☺', true},
+ {RuneError, true},
+ {MaxRune, true},
+ {0xD7FF, true},
+ {0xD800, false},
+ {0xDFFF, false},
+ {0xE000, true},
+ {MaxRune + 1, false},
+ {-1, false},
+}
+
+func TestValidRune(t *testing.T) {
+ for _, tt := range validrunetests {
+ if ok := ValidRune(tt.r); ok != tt.ok {
+ t.Errorf("ValidRune(%#U) = %t, want %t", tt.r, ok, tt.ok)
}
}
}
diff --git a/src/pkg/unsafe/unsafe.go b/src/pkg/unsafe/unsafe.go
index 024969b42..83b2e1405 100644
--- a/src/pkg/unsafe/unsafe.go
+++ b/src/pkg/unsafe/unsafe.go
@@ -11,7 +11,7 @@ package unsafe
// part of the unsafe package. It represents the type of an arbitrary Go expression.
type ArbitraryType int
-// Pointer represents a pointer to an arbitrary type. There are three special operations
+// Pointer represents a pointer to an arbitrary type. There are four special operations
// available for type Pointer that are not available for other types.
// 1) A pointer value of any type can be converted to a Pointer.
// 2) A Pointer can be converted to a pointer value of any type.
@@ -32,6 +32,6 @@ func Sizeof(v ArbitraryType) uintptr
func Offsetof(v ArbitraryType) uintptr
// Alignof returns the alignment of the value v. It is the maximum value m such
-// that the address of a variable with the type of v will always always be zero mod m.
+// that the address of a variable with the type of v will always be zero mod m.
// If v is of the form structValue.field, it returns the alignment of field f within struct object obj.
func Alignof(v ArbitraryType) uintptr